ETH Price: $2,503.19 (+1.39%)

Contract

0x0c64034984293EB44f0aD19bb48E7a8d3aC05e94
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040119447402021-02-28 8:21:151283 days ago1614500475IN
 Create: SigmaIndexPoolV1
0 ETH0.460194390

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SigmaIndexPoolV1

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 7 : SigmaIndexPoolV1.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

/* ========== Internal Inheritance ========== */
import "./BToken.sol";
import "./BMath.sol";

/* ========== Internal Interfaces ========== */
import "../interfaces/IIndexPool.sol";
import "../interfaces/ICompLikeToken.sol";


/************************************************************************************************
Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol

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

Subject to the GPL-3.0 license
*************************************************************************************************/


contract SigmaIndexPoolV1 is BToken, BMath, IIndexPool {

/* ==========  EVENTS  ========== */

  /** @dev Emitted when tokens are swapped. */
  event LOG_SWAP(
    address indexed caller,
    address indexed tokenIn,
    address indexed tokenOut,
    uint256 tokenAmountIn,
    uint256 tokenAmountOut
  );

  /** @dev Emitted when underlying tokens are deposited for pool tokens. */
  event LOG_JOIN(
    address indexed caller,
    address indexed tokenIn,
    uint256 tokenAmountIn
  );

  /** @dev Emitted when pool tokens are burned for underlying. */
  event LOG_EXIT(
    address indexed caller,
    address indexed tokenOut,
    uint256 tokenAmountOut
  );

  /** @dev Emitted when a token's weight updates. */
  event LOG_DENORM_UPDATED(address indexed token, uint256 newDenorm);

  /** @dev Emitted when a token's desired weight is set. */
  event LOG_DESIRED_DENORM_SET(address indexed token, uint256 desiredDenorm);

  /** @dev Emitted when a token is unbound from the pool. */
  event LOG_TOKEN_REMOVED(address token);

  /** @dev Emitted when a token is unbound from the pool. */
  event LOG_TOKEN_ADDED(
    address indexed token,
    uint256 desiredDenorm,
    uint256 minimumBalance
  );

  /** @dev Emitted when a token's minimum balance is updated. */
  event LOG_MINIMUM_BALANCE_UPDATED(address token, uint256 minimumBalance);

  /** @dev Emitted when a token reaches its minimum balance. */
  event LOG_TOKEN_READY(address indexed token);

  /** @dev Emitted when public trades are disabled. */
  event LOG_PUBLIC_SWAP_TOGGLED(bool enabled);

  /** @dev Emitted when the swap fee is updated. */
  event LOG_SWAP_FEE_UPDATED(uint256 swapFee);

/* ==========  Modifiers  ========== */

  modifier _lock_ {
    require(!_mutex, "ERR_REENTRY");
    _mutex = true;
    _;
    _mutex = false;
  }

  modifier _viewlock_() {
    require(!_mutex, "ERR_REENTRY");
    _;
  }

  modifier _control_ {
    require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
    _;
  }

  modifier _public_ {
    require(_publicSwap, "ERR_NOT_PUBLIC");
    _;
  }

/* ==========  Storage  ========== */

  bool internal _mutex;

  // Account with CONTROL role. Able to modify the swap fee,
  // adjust token weights, bind and unbind tokens and lock
  // public swaps & joins.
  address internal _controller;

  // Contract that handles unbound tokens.
  TokenUnbindHandler internal _unbindHandler;

  // True if PUBLIC can call SWAP & JOIN functions
  bool internal _publicSwap;

  // `setSwapFee` requires CONTROL
  uint256 internal _swapFee;

  // Array of underlying tokens in the pool.
  address[] internal _tokens;

  // Internal records of the pool's underlying tokens
  mapping(address => Record) internal _records;

  // Total denormalized weight of the pool.
  uint256 internal _totalWeight;

  // Minimum balances for tokens which have been added without the
  // requisite initial balance.
  mapping(address => uint256) internal _minimumBalances;

  // Recipient for exit fees
  address internal _exitFeeRecipient;

/* ==========  Controls  ========== */

  /**
   * @dev Sets the controller address and the token name & symbol.
   *
   * Note: This saves on storage costs for multi-step pool deployment.
   *
   * @param controller Controller of the pool
   * @param name Name of the pool token
   * @param symbol Symbol of the pool token
   */
  function configure(
    address controller,
    string calldata name,
    string calldata symbol
  ) external override {
    require(_controller == address(0), "ERR_CONFIGURED");
    require(controller != address(0), "ERR_NULL_ADDRESS");
    _controller = controller;
    // default fee is 2.5%
    _swapFee = BONE / 40;
    _initializeToken(name, symbol);
  }

  /**
   * @dev Sets up the initial assets for the pool.
   *
   * Note: `tokenProvider` must have approved the pool to transfer the
   * corresponding `balances` of `tokens`.
   *
   * @param tokens Underlying tokens to initialize the pool with
   * @param balances Initial balances to transfer
   * @param denorms Initial denormalized weights for the tokens
   * @param tokenProvider Address to transfer the balances from
   * @param unbindHandler Address that receives tokens removed from the pool
   * @param exitFeeRecipient Address that receives exit fees
   */
  function initialize(
    address[] calldata tokens,
    uint256[] calldata balances,
    uint96[] calldata denorms,
    address tokenProvider,
    address unbindHandler,
    address exitFeeRecipient
  )
    external
    override
    _control_
  {
    require(_tokens.length == 0, "ERR_INITIALIZED");
    uint256 len = tokens.length;
    require(len >= MIN_BOUND_TOKENS, "ERR_MIN_TOKENS");
    require(len <= MAX_BOUND_TOKENS, "ERR_MAX_TOKENS");
    require(balances.length == len && denorms.length == len, "ERR_ARR_LEN");
    uint256 totalWeight = 0;
    for (uint256 i = 0; i < len; i++) {
      address token = tokens[i];
      uint96 denorm = denorms[i];
      uint256 balance = balances[i];
      require(denorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT");
      require(denorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
      require(balance >= MIN_BALANCE, "ERR_MIN_BALANCE");
      _records[token] = Record({
        bound: true,
        ready: true,
        lastDenormUpdate: uint40(now),
        denorm: denorm,
        desiredDenorm: denorm,
        index: uint8(i),
        balance: balance
      });
      _tokens.push(token);
      totalWeight = badd(totalWeight, denorm);
      _pullUnderlying(token, tokenProvider, balance);
    }
    require(totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT");
    _totalWeight = totalWeight;
    _publicSwap = true;
    emit LOG_PUBLIC_SWAP_TOGGLED(true);
    _mintPoolShare(INIT_POOL_SUPPLY);
    _pushPoolShare(tokenProvider, INIT_POOL_SUPPLY);
    _unbindHandler = TokenUnbindHandler(unbindHandler);
    _exitFeeRecipient = exitFeeRecipient;
  }

  /**
   * @dev Set the swap fee.
   * Note: Swap fee must be between 0.0001% and 10%
   */
  function setSwapFee(uint256 swapFee) external override _control_ {
    require(swapFee >= MIN_FEE && swapFee <= MAX_FEE, "ERR_INVALID_FEE");
    _swapFee = swapFee;
    emit LOG_SWAP_FEE_UPDATED(swapFee);
  }

  /**
   * @dev Delegate a comp-like governance token to an address
   * specified by the controller.
   */
  function delegateCompLikeToken(address token, address delegatee)
    external
    override
    _control_
  {
    ICompLikeToken(token).delegate(delegatee);
  }

  /**
   * @dev Set the exit fee recipient address. Can only be called
   * by the current exit fee recipient.
   */
  function setExitFeeRecipient(address exitFeeRecipient) external override {
    require(msg.sender == _exitFeeRecipient, "ERR_NOT_FEE_RECIPIENT");
    _exitFeeRecipient = exitFeeRecipient;
  }

  /**
   * @dev Toggle public trading for the index pool.
   * This will enable or disable swaps and single-token joins and exits.
   */
  function setPublicSwap(bool enabled) external override _control_ {
    _publicSwap = enabled;
    emit LOG_PUBLIC_SWAP_TOGGLED(enabled);
  }

/* ==========  Token Management Actions  ========== */

  /**
   * @dev Sets the desired weights for the pool tokens, which
   * will be adjusted over time as they are swapped.
   *
   * Note: This does not check for duplicate tokens or that the total
   * of the desired weights is equal to the target total weight (25).
   * Those assumptions should be met in the controller. Further, the
   * provided tokens should only include the tokens which are not set
   * for removal.
   */
  function reweighTokens(
    address[] calldata tokens,
    uint96[] calldata desiredDenorms
  )
    external
    override
    _lock_
    _control_
  {
    require(desiredDenorms.length == tokens.length, "ERR_ARR_LEN");
    for (uint256 i = 0; i < tokens.length; i++)
      _setDesiredDenorm(tokens[i], desiredDenorms[i]);
  }

  /**
   * @dev Update the underlying assets held by the pool and their associated
   * weights. Tokens which are not currently bound will be gradually added
   * as they are swapped in to reach the provided minimum balances, which must
   * be an amount of tokens worth the minimum weight of the total pool value.
   * If a currently bound token is not received in this call, the token's
   * desired weight will be set to 0.
   */
  function reindexTokens(
    address[] calldata tokens,
    uint96[] calldata desiredDenorms,
    uint256[] calldata minimumBalances
  )
    external
    override
    _lock_
    _control_
  {
    require(
      desiredDenorms.length == tokens.length && minimumBalances.length == tokens.length,
      "ERR_ARR_LEN"
    );
    // This size may not be the same as the input size, as it is possible
    // to temporarily exceed the index size while tokens are being phased in
    // or out.
    uint256 tLen = _tokens.length;
    bool[] memory receivedIndices = new bool[](tLen);
    // We need to read token records in two separate loops, so
    // write them to memory to avoid duplicate storage reads.
    Record[] memory records = new Record[](tokens.length);
    // Read all the records from storage and mark which of the existing tokens
    // were represented in the reindex call.
    for (uint256 i = 0; i < tokens.length; i++) {
      records[i] = _records[tokens[i]];
      if (records[i].bound) receivedIndices[records[i].index] = true;
    }
    // If any bound tokens were not sent in this call, set their desired weights to 0.
    for (uint256 i = 0; i < tLen; i++) {
      if (!receivedIndices[i]) {
        _setDesiredDenorm(_tokens[i], 0);
      }
    }
    for (uint256 i = 0; i < tokens.length; i++) {
      address token = tokens[i];
      // If an input weight is less than the minimum weight, use that instead.
      uint96 denorm = desiredDenorms[i];
      if (denorm < MIN_WEIGHT) denorm = uint96(MIN_WEIGHT);
      if (!records[i].bound) {
        // If the token is not bound, bind it.
        _bind(token, minimumBalances[i], denorm);
      } else {
        _setDesiredDenorm(token, denorm);
      }
    }
  }

  /**
   * @dev Updates the minimum balance for an uninitialized token.
   * This becomes useful if a token's external price significantly
   * rises after being bound, since the pool can not send a token
   * out until it reaches the minimum balance.
   */
  function setMinimumBalance(
    address token,
    uint256 minimumBalance
  )
    external
    override
    _control_
  {
    Record storage record = _records[token];
    require(record.bound, "ERR_NOT_BOUND");
    require(!record.ready, "ERR_READY");
    _minimumBalances[token] = minimumBalance;
    emit LOG_MINIMUM_BALANCE_UPDATED(token, minimumBalance);
  }

/* ==========  Liquidity Provider Actions  ========== */

  /**
   * @dev Mint new pool tokens by providing the proportional amount of each
   * underlying token's balance relative to the proportion of pool tokens minted.
   *
   * For any underlying tokens which are not initialized, the caller must provide
   * the proportional share of the minimum balance for the token rather than the
   * actual balance.
   *
   * @param poolAmountOut Amount of pool tokens to mint
   * @param maxAmountsIn Maximum amount of each token to pay in the same
   * order as the pool's _tokens list.
   */
  function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn)
    external
    override
    _lock_
    _public_
  {
    uint256 poolTotal = totalSupply();
    uint256 ratio = bdiv(poolAmountOut, poolTotal);
    require(ratio != 0, "ERR_MATH_APPROX");
    require(maxAmountsIn.length == _tokens.length, "ERR_ARR_LEN");

    for (uint256 i = 0; i < maxAmountsIn.length; i++) {
      address t = _tokens[i];
      (Record memory record, uint256 realBalance) = _getInputToken(t);
      uint256 tokenAmountIn = bmul(ratio, record.balance);
      require(tokenAmountIn != 0, "ERR_MATH_APPROX");
      require(tokenAmountIn <= maxAmountsIn[i], "ERR_LIMIT_IN");
      _updateInputToken(t, record, badd(realBalance, tokenAmountIn));
      emit LOG_JOIN(msg.sender, t, tokenAmountIn);
      _pullUnderlying(t, msg.sender, tokenAmountIn);
    }
    _mintPoolShare(poolAmountOut);
    _pushPoolShare(msg.sender, poolAmountOut);
  }

  /**
   * @dev Pay `tokenAmountIn` of `tokenIn` to mint at least `minPoolAmountOut`
   * pool tokens.
   *
   * The pool implicitly swaps `(1- weightTokenIn) * tokenAmountIn` to the other
   * underlying tokens. Thus a swap fee is charged against the input tokens.
   *
   * @param tokenIn Token to send the pool
   * @param tokenAmountIn Exact amount of `tokenIn` to pay
   * @param minPoolAmountOut Minimum amount of pool tokens to mint
   * @return poolAmountOut - Amount of pool tokens minted
   */
  function joinswapExternAmountIn(
    address tokenIn,
    uint256 tokenAmountIn,
    uint256 minPoolAmountOut
  )
    external
    override
    _lock_
    _public_
    returns (uint256/* poolAmountOut */)
  {
    (Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);

    require(tokenAmountIn != 0, "ERR_ZERO_IN");

    require(
      tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO),
      "ERR_MAX_IN_RATIO"
    );

    uint256 poolAmountOut = calcPoolOutGivenSingleIn(
      inRecord.balance,
      inRecord.denorm,
      _totalSupply,
      _totalWeight,
      tokenAmountIn,
      _swapFee
    );

    require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT");

    _updateInputToken(tokenIn, inRecord, badd(realInBalance, tokenAmountIn));

    emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);

    _mintPoolShare(poolAmountOut);
    _pushPoolShare(msg.sender, poolAmountOut);
    _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);

    return poolAmountOut;
  }

  /**
   * @dev Pay up to `maxAmountIn` of `tokenIn` to mint exactly `poolAmountOut`.
   *
   * The pool implicitly swaps `(1- weightTokenIn) * tokenAmountIn` to the other
   * underlying tokens. Thus a swap fee is charged against the input tokens.
   *
   * @param tokenIn Token to send the pool
   * @param poolAmountOut Exact amount of pool tokens to mint
   * @param maxAmountIn Maximum amount of `tokenIn` to pay
   * @return tokenAmountIn - Amount of `tokenIn` paid
   */
  function joinswapPoolAmountOut(
    address tokenIn,
    uint256 poolAmountOut,
    uint256 maxAmountIn
  )
    external
    override
    _lock_
    _public_
    returns (uint256/* tokenAmountIn */)
  {
    (Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);

    uint256 tokenAmountIn = calcSingleInGivenPoolOut(
      inRecord.balance,
      inRecord.denorm,
      _totalSupply,
      _totalWeight,
      poolAmountOut,
      _swapFee
    );

    require(tokenAmountIn != 0, "ERR_MATH_APPROX");
    require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN");

    require(
      tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO),
      "ERR_MAX_IN_RATIO"
    );

    _updateInputToken(tokenIn, inRecord, badd(realInBalance, tokenAmountIn));

    emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);

    _mintPoolShare(poolAmountOut);
    _pushPoolShare(msg.sender, poolAmountOut);
    _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);

    return tokenAmountIn;
  }

  /**
   * @dev Burns `poolAmountIn` pool tokens in exchange for the amounts of each
   * underlying token's balance proportional to the ratio of tokens burned to
   * total pool supply. The amount of each token transferred to the caller must
   * be greater than or equal to the associated minimum output amount from the
   * `minAmountsOut` array.
   *
   * @param poolAmountIn Exact amount of pool tokens to burn
   * @param minAmountsOut Minimum amount of each token to receive, in the same
   * order as the pool's _tokens list.
   */
  function exitPool(uint256 poolAmountIn, uint256[] calldata minAmountsOut)
    external
    override
    _lock_
  {
    require(minAmountsOut.length == _tokens.length, "ERR_ARR_LEN");
    uint256 poolTotal = totalSupply();
    uint256 exitFee = bmul(poolAmountIn, EXIT_FEE);
    uint256 pAiAfterExitFee = bsub(poolAmountIn, exitFee);
    uint256 ratio = bdiv(pAiAfterExitFee, poolTotal);
    require(ratio != 0, "ERR_MATH_APPROX");

    _pullPoolShare(msg.sender, poolAmountIn);
    _pushPoolShare(_exitFeeRecipient, exitFee);
    _burnPoolShare(pAiAfterExitFee);
    for (uint256 i = 0; i < minAmountsOut.length; i++) {
      address t = _tokens[i];
      Record memory record = _records[t];
      if (record.ready) {
        uint256 tokenAmountOut = bmul(ratio, record.balance);
        require(tokenAmountOut != 0, "ERR_MATH_APPROX");
        require(tokenAmountOut >= minAmountsOut[i], "ERR_LIMIT_OUT");

        _records[t].balance = bsub(record.balance, tokenAmountOut);
        emit LOG_EXIT(msg.sender, t, tokenAmountOut);
        _pushUnderlying(t, msg.sender, tokenAmountOut);
      } else {
        // If the token is not initialized, it can not exit the pool.
        require(minAmountsOut[i] == 0, "ERR_OUT_NOT_READY");
      }
    }
  }

  /**
   * @dev Burns `poolAmountIn` pool tokens in exchange for at least `minAmountOut`
   * of `tokenOut`. Returns the number of tokens sent to the caller.
   *
   * The pool implicitly burns the tokens for all underlying tokens and swaps them
   * to the desired output token. A swap fee is charged against the output tokens.
   *
   * @param tokenOut Token to receive
   * @param poolAmountIn Exact amount of pool tokens to burn
   * @param minAmountOut Minimum amount of `tokenOut` to receive
   * @return tokenAmountOut - Amount of `tokenOut` received
   */
  function exitswapPoolAmountIn(
    address tokenOut,
    uint256 poolAmountIn,
    uint256 minAmountOut
  )
    external
    override
    _lock_
    returns (uint256/* tokenAmountOut */)
  {
    Record memory outRecord = _getOutputToken(tokenOut);

    uint256 tokenAmountOut = calcSingleOutGivenPoolIn(
      outRecord.balance,
      outRecord.denorm,
      _totalSupply,
      _totalWeight,
      poolAmountIn,
      _swapFee
    );

    require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");

    require(
      tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO),
      "ERR_MAX_OUT_RATIO"
    );

    _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
    _records[tokenOut].balance = bsub(outRecord.balance, tokenAmountOut);
    _decreaseDenorm(outRecord, tokenOut);
    uint256 exitFee = bmul(poolAmountIn, EXIT_FEE);

    emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);

    _pullPoolShare(msg.sender, poolAmountIn);
    _burnPoolShare(bsub(poolAmountIn, exitFee));
    _pushPoolShare(_exitFeeRecipient, exitFee);

    return tokenAmountOut;
  }

  /**
   * @dev Burn up to `maxPoolAmountIn` for exactly `tokenAmountOut` of `tokenOut`.
   * Returns the number of pool tokens burned.
   *
   * The pool implicitly burns the tokens for all underlying tokens and swaps them
   * to the desired output token. A swap fee is charged against the output tokens.
   *
   * @param tokenOut Token to receive
   * @param tokenAmountOut Exact amount of `tokenOut` to receive
   * @param maxPoolAmountIn Maximum amount of pool tokens to burn
   * @return poolAmountIn - Amount of pool tokens burned
   */
  function exitswapExternAmountOut(
    address tokenOut,
    uint256 tokenAmountOut,
    uint256 maxPoolAmountIn
  )
    external
    override
    _lock_
    returns (uint256/* poolAmountIn */)
  {
    Record memory outRecord = _getOutputToken(tokenOut);
    require(
      tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO),
      "ERR_MAX_OUT_RATIO"
    );

    uint256 poolAmountIn = calcPoolInGivenSingleOut(
      outRecord.balance,
      outRecord.denorm,
      _totalSupply,
      _totalWeight,
      tokenAmountOut,
      _swapFee
    );

    require(poolAmountIn != 0, "ERR_MATH_APPROX");
    require(poolAmountIn <= maxPoolAmountIn, "ERR_LIMIT_IN");

    _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
    _records[tokenOut].balance = bsub(outRecord.balance, tokenAmountOut);
    _decreaseDenorm(outRecord, tokenOut);

    uint256 exitFee = bmul(poolAmountIn, EXIT_FEE);

    emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);

    _pullPoolShare(msg.sender, poolAmountIn);
    _burnPoolShare(bsub(poolAmountIn, exitFee));
    _pushPoolShare(_exitFeeRecipient, exitFee);

    return poolAmountIn;
  }

/* ==========  Other  ========== */

  /**
   * @dev Absorb any tokens that have been sent to the pool.
   * If the token is not bound, it will be sent to the unbound
   * token handler.
   */
  function gulp(address token) external override _lock_ {
    Record storage record = _records[token];
    uint256 balance = IERC20(token).balanceOf(address(this));
    if (record.bound) {
      if (!record.ready) {
        uint256 minimumBalance = _minimumBalances[token];
        if (balance >= minimumBalance) {
          _minimumBalances[token] = 0;
          record.ready = true;
          emit LOG_TOKEN_READY(token);
          uint256 additionalBalance = bsub(balance, minimumBalance);
          uint256 balRatio = bdiv(additionalBalance, minimumBalance);
          uint96 newDenorm = uint96(badd(MIN_WEIGHT, bmul(MIN_WEIGHT, balRatio)));
          record.denorm = newDenorm;
          record.lastDenormUpdate = uint40(now);
          _totalWeight = badd(_totalWeight, newDenorm);
          emit LOG_DENORM_UPDATED(token, record.denorm);
        }
      }
      _records[token].balance = balance;
    } else {
      _pushUnderlying(token, address(_unbindHandler), balance);
      _unbindHandler.handleUnbindToken(token, balance);
    }
  }

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

  /**
   * @dev Execute a token swap with a specified amount of input
   * tokens and a minimum amount of output tokens.
   *
   * Note: Will revert if `tokenOut` is uninitialized.
   *
   * @param tokenIn Token to swap in
   * @param tokenAmountIn Exact amount of `tokenIn` to swap in
   * @param tokenOut Token to swap out
   * @param minAmountOut Minimum amount of `tokenOut` to receive
   * @param maxPrice Maximum ratio of input to output tokens
   * @return (tokenAmountOut, spotPriceAfter)
   */
  function swapExactAmountIn(
    address tokenIn,
    uint256 tokenAmountIn,
    address tokenOut,
    uint256 minAmountOut,
    uint256 maxPrice
  )
    external
    override
    _lock_
    _public_
    returns (uint256/* tokenAmountOut */, uint256/* spotPriceAfter */)
  {
    (Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);
    Record memory outRecord = _getOutputToken(tokenOut);

    require(
      tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO),
      "ERR_MAX_IN_RATIO"
    );

    uint256 spotPriceBefore = calcSpotPrice(
      inRecord.balance,
      inRecord.denorm,
      outRecord.balance,
      outRecord.denorm,
      _swapFee
    );
    require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE");

    uint256 tokenAmountOut = calcOutGivenIn(
      inRecord.balance,
      inRecord.denorm,
      outRecord.balance,
      outRecord.denorm,
      tokenAmountIn,
      _swapFee
    );

    require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");

    _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
    _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);

    realInBalance = badd(realInBalance, tokenAmountIn);
    _updateInputToken(tokenIn, inRecord, realInBalance);
    if (inRecord.ready) {
      inRecord.balance = realInBalance;
    }
    // Update the in-memory record for the spotPriceAfter calculation,
    // then update the storage record with the local balance.
    outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
    _records[tokenOut].balance = outRecord.balance;
    // If needed, update the output token's weight.
    _decreaseDenorm(outRecord, tokenOut);

    uint256 spotPriceAfter = calcSpotPrice(
      inRecord.balance,
      inRecord.denorm,
      outRecord.balance,
      outRecord.denorm,
      _swapFee
    );

    require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX_2");
    require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE");
    require(
      spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut),
      "ERR_MATH_APPROX"
    );

    emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut);

    return (tokenAmountOut, spotPriceAfter);
  }

  /**
   * @dev Trades at most `maxAmountIn` of `tokenIn` for exactly `tokenAmountOut`
   * of `tokenOut`.
   *
   * Returns the actual input amount and the new spot price after the swap,
   * which can not exceed `maxPrice`.
   *
   * @param tokenIn Token to swap in
   * @param maxAmountIn Maximum amount of `tokenIn` to pay
   * @param tokenOut Token to swap out
   * @param tokenAmountOut Exact amount of `tokenOut` to receive
   * @param maxPrice Maximum ratio of input to output tokens
   * @return (tokenAmountIn, spotPriceAfter)
   */
  function swapExactAmountOut(
    address tokenIn,
    uint256 maxAmountIn,
    address tokenOut,
    uint256 tokenAmountOut,
    uint256 maxPrice
  )
    external
    override
    _lock_
    _public_
    returns (uint256 /* tokenAmountIn */, uint256 /* spotPriceAfter */)
  {
    (Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);
    Record memory outRecord = _getOutputToken(tokenOut);

    require(
      tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO),
      "ERR_MAX_OUT_RATIO"
    );

    uint256 spotPriceBefore = calcSpotPrice(
      inRecord.balance,
      inRecord.denorm,
      outRecord.balance,
      outRecord.denorm,
      _swapFee
    );
    require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE");

    uint256 tokenAmountIn = calcInGivenOut(
      inRecord.balance,
      inRecord.denorm,
      outRecord.balance,
      outRecord.denorm,
      tokenAmountOut,
      _swapFee
    );

    require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN");

    _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
    _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);

    // Update the balance and (if necessary) weight of the input token.
    realInBalance = badd(realInBalance, tokenAmountIn);
    _updateInputToken(tokenIn, inRecord, realInBalance);
    if (inRecord.ready) {
      inRecord.balance = realInBalance;
    }
    // Update the in-memory record for the spotPriceAfter calculation,
    // then update the storage record with the local balance.
    outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
    _records[tokenOut].balance = outRecord.balance;
    // If needed, update the output token's weight.
    _decreaseDenorm(outRecord, tokenOut);

    uint256 spotPriceAfter = calcSpotPrice(
      inRecord.balance,
      inRecord.denorm,
      outRecord.balance,
      outRecord.denorm,
      _swapFee
    );

    require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX");
    require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE");
    require(
      spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut),
      "ERR_MATH_APPROX"
    );

    emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut);

    return (tokenAmountIn, spotPriceAfter);
  }

/* ==========  Config Queries  ========== */
  /**
   * @dev Check if swapping tokens and joining the pool is allowed.
   */
  function isPublicSwap() external view override returns (bool) {
    return _publicSwap;
  }

  function getSwapFee() external view override _viewlock_ returns (uint256/* swapFee */) {
    return _swapFee;
  }

  /**
   * @dev Returns the controller address.
   */
  function getController() external view override returns (address) {
    return _controller;
  }

  /**
   * @dev Returns the exit fee recipient address.
   */
  function getExitFeeRecipient() external view override returns (address) {
    return _exitFeeRecipient;
  }

/* ==========  Token Queries  ========== */

  /**
   * @dev Check if a token is bound to the pool.
   */
  function isBound(address t) external view override returns (bool) {
    return _records[t].bound;
  }

  /**
   * @dev Get the number of tokens bound to the pool.
   */
  function getNumTokens() external view override returns (uint256) {
    return _tokens.length;
  }

  /**
   * @dev Get all bound tokens.
   */
  function getCurrentTokens()
    external
    view
    override
    _viewlock_
    returns (address[] memory tokens)
  {
    tokens = _tokens;
  }

  /**
   * @dev Returns the list of tokens which have a desired weight above 0.
   * Tokens with a desired weight of 0 are set to be phased out of the pool.
   */
  function getCurrentDesiredTokens()
    external
    view
    override
    _viewlock_
    returns (address[] memory tokens)
  {
    address[] memory tempTokens = _tokens;
    tokens = new address[](tempTokens.length);
    uint256 usedIndex = 0;
    for (uint256 i = 0; i < tokens.length; i++) {
      address token = tempTokens[i];
      if (_records[token].desiredDenorm > 0) {
        tokens[usedIndex++] = token;
      }
    }
    assembly { mstore(tokens, usedIndex) }
  }

  /**
   * @dev Returns the denormalized weight of a bound token.
   */
  function getDenormalizedWeight(address token)
    external
    view
    override
    _viewlock_
    returns (uint256/* denorm */)
  {
    require(_records[token].bound, "ERR_NOT_BOUND");
    return _records[token].denorm;
  }

  /**
   * @dev Returns the record for a token bound to the pool.
   */
  function getTokenRecord(address token)
    external
    view
    override
    _viewlock_
    returns (Record memory record)
  {
    record = _records[token];
    require(record.bound, "ERR_NOT_BOUND");
  }

  /**
   * @dev Finds the first token which is both initialized and has a
   * desired weight above 0, then returns the address of that token
   * and the extrapolated value of the pool in terms of that token.
   *
   * The value is extrapolated by multiplying the token's
   * balance by the reciprocal of its normalized weight.
   * @return (token, extrapolatedValue)
   */
  function extrapolatePoolValueFromToken()
    external
    view
    override
    _viewlock_
    returns (address/* token */, uint256/* extrapolatedValue */)
  {
    address token;
    uint256 extrapolatedValue;
    uint256 len = _tokens.length;
    for (uint256 i = 0; i < len; i++) {
      token = _tokens[i];
      Record storage record = _records[token];
      if (record.ready && record.desiredDenorm > 0) {
        extrapolatedValue = bmul(
          record.balance,
          bdiv(_totalWeight, record.denorm)
        );
        break;
      }
    }
    require(extrapolatedValue > 0, "ERR_NONE_READY");
    return (token, extrapolatedValue);
  }

  /**
   * @dev Get the total denormalized weight of the pool.
   */
  function getTotalDenormalizedWeight()
    external
    view
    override
    _viewlock_
    returns (uint256)
  {
    return _totalWeight;
  }

  /**
   * @dev Returns the stored balance of a bound token.
   */
  function getBalance(address token) external view override _viewlock_ returns (uint256) {
    Record storage record = _records[token];
    require(record.bound, "ERR_NOT_BOUND");
    return record.balance;
  }

  /**
   * @dev Get the minimum balance of an uninitialized token.
   * Note: Throws if the token is initialized.
   */
  function getMinimumBalance(address token) external view override _viewlock_ returns (uint256) {
    Record memory record = _records[token];
    require(record.bound, "ERR_NOT_BOUND");
    require(!record.ready, "ERR_READY");
    return _minimumBalances[token];
  }

  /**
   * @dev Returns the balance of a token which is used in price
   * calculations. If the token is initialized, this is the
   * stored balance; if not, this is the minimum balance.
   */
  function getUsedBalance(address token) external view override _viewlock_ returns (uint256) {
    Record memory record = _records[token];
    require(record.bound, "ERR_NOT_BOUND");
    if (!record.ready) {
      return _minimumBalances[token];
    }
    return record.balance;
  }

/* ==========  Price Queries  ========== */
  /**
   * @dev Returns the spot price for `tokenOut` in terms of `tokenIn`.
   */
  function getSpotPrice(address tokenIn, address tokenOut)
    external
    view
    override
    _viewlock_
    returns (uint256)
  {
    (Record memory inRecord,) = _getInputToken(tokenIn);
    Record memory outRecord = _getOutputToken(tokenOut);
    return
      calcSpotPrice(
        inRecord.balance,
        inRecord.denorm,
        outRecord.balance,
        outRecord.denorm,
        _swapFee
      );
  }

/* ==========  Pool Share Internal Functions  ========== */

  function _pullPoolShare(address from, uint256 amount) internal {
    _pull(from, amount);
  }

  function _pushPoolShare(address to, uint256 amount) internal {
    _push(to, amount);
  }

  function _mintPoolShare(uint256 amount) internal {
    _mint(amount);
  }

  function _burnPoolShare(uint256 amount) internal {
    _burn(amount);
  }

/* ==========  Underlying Token Internal Functions  ========== */
  // 'Underlying' token-manipulation functions make external calls but are NOT locked
  // You must `_lock_` or otherwise ensure reentry-safety

  function _pullUnderlying(
    address erc20,
    address from,
    uint256 amount
  ) internal {
    (bool success, bytes memory data) = erc20.call(
      abi.encodeWithSelector(
        IERC20.transferFrom.selector,
        from,
        address(this),
        amount
      )
    );
    require(
      success && (data.length == 0 || abi.decode(data, (bool))),
      "ERR_ERC20_FALSE"
    );
  }

  function _pushUnderlying(
    address erc20,
    address to,
    uint256 amount
  ) internal {
    (bool success, bytes memory data) = erc20.call(
      abi.encodeWithSelector(
        IERC20.transfer.selector,
        to,
        amount
      )
    );
    require(
      success && (data.length == 0 || abi.decode(data, (bool))),
      "ERR_ERC20_FALSE"
    );
  }

/* ==========  Token Management Internal Functions  ========== */

  /**
   * @dev Bind a token by address without actually depositing a balance.
   * The token will be unable to be swapped out until it reaches the minimum balance.
   * Note: Token must not already be bound.
   * Note: `minimumBalance` should represent an amount of the token which is worth
   * the portion of the current pool value represented by the minimum weight.
   * @param token Address of the token to bind
   * @param minimumBalance minimum balance to reach before the token can be swapped out
   * @param desiredDenorm Desired weight for the token.
   */
  function _bind(
    address token,
    uint256 minimumBalance,
    uint96 desiredDenorm
  ) internal {
    require(!_records[token].bound, "ERR_IS_BOUND");

    require(desiredDenorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT");
    require(desiredDenorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
    require(minimumBalance >= MIN_BALANCE, "ERR_MIN_BALANCE");

    _records[token] = Record({
      bound: true,
      ready: false,
      lastDenormUpdate: 0,
      denorm: 0,
      desiredDenorm: desiredDenorm,
      index: uint8(_tokens.length),
      balance: 0
    });
    _tokens.push(token);
    _minimumBalances[token] = minimumBalance;
    emit LOG_TOKEN_ADDED(token, desiredDenorm, minimumBalance);
  }

  /**
   * @dev Remove a token from the pool.
   * Replaces the address in the tokens array with the last address,
   * then removes it from the array.
   * Note: This should only be called after the total weight has been adjusted.
   * Note: Must be called in a function with:
   * - _lock_ modifier to prevent reentrance
   * - requirement that the token is bound
   */
  function _unbind(address token) internal {
    Record memory record = _records[token];
    uint256 tokenBalance = record.balance;

    // Swap the token-to-unbind with the last token,
    // then delete the last token
    uint256 index = record.index;
    uint256 last = _tokens.length - 1;
    // Only swap the token with the last token if it is not
    // already at the end of the array.
    if (index != last) {
      _tokens[index] = _tokens[last];
      _records[_tokens[index]].index = uint8(index);
    }
    _tokens.pop();
    _records[token] = Record({
      bound: false,
      ready: false,
      lastDenormUpdate: 0,
      denorm: 0,
      desiredDenorm: 0,
      index: 0,
      balance: 0
    });
    // transfer any remaining tokens out
    _pushUnderlying(token, address(_unbindHandler), tokenBalance);
    _unbindHandler.handleUnbindToken(token, tokenBalance);
    emit LOG_TOKEN_REMOVED(token);
  }

  function _setDesiredDenorm(address token, uint96 desiredDenorm) internal {
    Record storage record = _records[token];
    require(record.bound, "ERR_NOT_BOUND");
    // If the desired weight is 0, this will trigger a gradual unbinding of the token.
    // Therefore the weight only needs to be above the minimum weight if it isn't 0.
    require(
      desiredDenorm >= MIN_WEIGHT || desiredDenorm == 0,
      "ERR_MIN_WEIGHT"
    );
    require(desiredDenorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
    record.desiredDenorm = desiredDenorm;
    emit LOG_DESIRED_DENORM_SET(token, desiredDenorm);
  }

  function _increaseDenorm(Record memory record, address token) internal {
    // If the weight does not need to increase or the token is not
    // initialized, don't do anything.
    if (
      record.denorm >= record.desiredDenorm ||
      !record.ready ||
      now - record.lastDenormUpdate < WEIGHT_UPDATE_DELAY
    ) return;
    uint96 oldWeight = record.denorm;
    uint96 denorm = record.desiredDenorm;
    uint256 maxDiff = bmul(oldWeight, WEIGHT_CHANGE_PCT);
    uint256 diff = bsub(denorm, oldWeight);
    if (diff > maxDiff) {
      denorm = uint96(badd(oldWeight, maxDiff));
      diff = maxDiff;
    }
    _totalWeight = badd(_totalWeight, diff);
    require(_totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT");
    // Update the in-memory denorm value for spot-price computations.
    record.denorm = denorm;
    // Update the storage record
    _records[token].denorm = denorm;
    _records[token].lastDenormUpdate = uint40(now);
    emit LOG_DENORM_UPDATED(token, denorm);
  }

  function _decreaseDenorm(Record memory record, address token) internal {
    // If the weight does not need to decrease, don't do anything.
    if (
      record.denorm <= record.desiredDenorm ||
      !record.ready ||
      now - record.lastDenormUpdate < WEIGHT_UPDATE_DELAY
    ) return;
    uint96 oldWeight = record.denorm;
    uint96 denorm = record.desiredDenorm;
    uint256 maxDiff = bmul(oldWeight, WEIGHT_CHANGE_PCT);
    uint256 diff = bsub(oldWeight, denorm);
    if (diff > maxDiff) {
      denorm = uint96(bsub(oldWeight, maxDiff));
      diff = maxDiff;
    }
    if (denorm <= MIN_WEIGHT) {
      denorm = 0;
      _totalWeight = bsub(_totalWeight, denorm);
      // Because this is removing the token from the pool, the
      // in-memory denorm value is irrelevant, as it is only used
      // to calculate the new spot price, but the spot price calc
      // will throw if it is passed 0 for the denorm.
      _unbind(token);
    } else {
      _totalWeight = bsub(_totalWeight, diff);
      // Update the in-memory denorm value for spot-price computations.
      record.denorm = denorm;
      // Update the stored denorm value
      _records[token].denorm = denorm;
      _records[token].lastDenormUpdate = uint40(now);
      emit LOG_DENORM_UPDATED(token, denorm);
    }
  }

  /**
   * @dev Handles weight changes and initialization of an
   * input token.
   *
   * If the token is not initialized and the new balance is
   * still below the minimum, this will not do anything.
   *
   * If the token is not initialized but the new balance will
   * bring the token above the minimum balance, this will
   * mark the token as initialized, remove the minimum
   * balance and set the weight to the minimum weight plus
   * 1%.
   *
   *
   * @param token Address of the input token
   * @param record Token record with minimums applied to the balance
   * and weight if the token was uninitialized.
   */
  function _updateInputToken(
    address token,
    Record memory record,
    uint256 realBalance
  )
    internal
  {
    if (!record.ready) {
      // Check if the minimum balance has been reached
      if (realBalance >= record.balance) {
        // Remove the minimum balance record
        _minimumBalances[token] = 0;
        // Mark the token as initialized
        _records[token].ready = true;
        record.ready = true;
        emit LOG_TOKEN_READY(token);
        // Set the initial denorm value to the minimum weight times one plus
        // the ratio of the increase in balance over the minimum to the minimum
        // balance.
        // weight = (1 + ((bal - min_bal) / min_bal)) * min_weight
        uint256 additionalBalance = bsub(realBalance, record.balance);
        uint256 balRatio = bdiv(additionalBalance, record.balance);
        record.denorm = uint96(badd(MIN_WEIGHT, bmul(MIN_WEIGHT, balRatio)));
        _records[token].denorm = record.denorm;
        _records[token].lastDenormUpdate = uint40(now);
        _totalWeight = badd(_totalWeight, record.denorm);
        emit LOG_DENORM_UPDATED(token, record.denorm);
      } else {
        uint256 realToMinRatio = bdiv(
          bsub(record.balance, realBalance),
          record.balance
        );
        uint256 weightPremium = bmul(MIN_WEIGHT / 10, realToMinRatio);
        record.denorm = uint96(badd(MIN_WEIGHT, weightPremium));
      }
      // If the token is still not ready, do not adjust the weight.
    } else {
      // If the token is already initialized, update the weight (if any adjustment
      // is needed).
      _increaseDenorm(record, token);
    }
    // Regardless of whether the token is initialized, store the actual new balance.
    _records[token].balance = realBalance;
  }

/* ==========  Token Query Internal Functions  ========== */

  /**
   * @dev Get the record for a token which is being swapped in.
   * The token must be bound to the pool. If the token is not
   * initialized (meaning it does not have the minimum balance)
   * this function will return the actual balance of the token
   * which the pool holds, but set the record's balance and weight
   * to the token's minimum balance and the pool's minimum weight.
   * This allows the token swap to be priced correctly even if the
   * pool does not own any of the tokens.
   */
  function _getInputToken(address token)
    internal
    view
    returns (Record memory record, uint256 realBalance)
  {
    record = _records[token];
    require(record.bound, "ERR_NOT_BOUND");

    realBalance = record.balance;
    // If the input token is not initialized, we use the minimum
    // initial weight and minimum initial balance instead of the
    // real values for price and output calculations.
    if (!record.ready) {
      record.balance = _minimumBalances[token];
      uint256 realToMinRatio = bdiv(
        bsub(record.balance, realBalance),
        record.balance
      );
      uint256 weightPremium = bmul(MIN_WEIGHT / 10, realToMinRatio);
      record.denorm = uint96(badd(MIN_WEIGHT, weightPremium));
    }
  }

  function _getOutputToken(address token)
    internal
    view
    returns (Record memory record)
  {
    record = _records[token];
    require(record.bound, "ERR_NOT_BOUND");
    // Tokens which have not reached their minimum balance can not be
    // swapped out.
    require(record.ready, "ERR_OUT_NOT_READY");
  }
}


interface TokenUnbindHandler {
  /**
   * @dev Receive `amount` of `token` from the pool.
   */
  function handleUnbindToken(address token, uint256 amount) external;
}

File 2 of 7 : BConst.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;


/************************************************************************************************
Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BConst.sol

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

Subject to the GPL-3.0 license
*************************************************************************************************/


contract BConst {
  uint256 public constant VERSION_NUMBER = 1;

/* ---  Weight Updates  --- */

  // Minimum time passed between each weight update for a token.
  uint256 internal constant WEIGHT_UPDATE_DELAY = 1 hours;

  // Maximum percent by which a weight can adjust at a time
  // relative to the current weight.
  // The number of iterations needed to move from weight A to weight B is the floor of:
  // (A > B): (ln(A) - ln(B)) / ln(1.01)
  // (B > A): (ln(A) - ln(B)) / ln(0.99)
  uint256 internal constant WEIGHT_CHANGE_PCT = BONE/100;

  uint256 internal constant BONE = 10**18;

  uint256 internal constant MIN_BOUND_TOKENS = 2;
  uint256 internal constant MAX_BOUND_TOKENS = 10;

  // Minimum swap fee.
  uint256 internal constant MIN_FEE = BONE / 10**6;
  // Maximum swap or exit fee.
  uint256 internal constant MAX_FEE = BONE / 10;
  // Actual exit fee.
  uint256 internal constant EXIT_FEE = 5e15;

  // Default total of all desired weights. Can differ by up to BONE.
  uint256 internal constant DEFAULT_TOTAL_WEIGHT = BONE * 25;
  // Minimum weight for any token (1/100).
  uint256 internal constant MIN_WEIGHT = BONE / 4;
  uint256 internal constant MAX_WEIGHT = BONE * 25;
  // Maximum total weight.
  uint256 internal constant MAX_TOTAL_WEIGHT = BONE * 26;
  // Minimum balance for a token (only applied at initialization)
  uint256 internal constant MIN_BALANCE = BONE / 10**12;
  // Initial pool tokens
  uint256 internal constant INIT_POOL_SUPPLY = BONE * 100;

  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;

  // Maximum ratio of input tokens to balance for swaps.
  uint256 internal constant MAX_IN_RATIO = BONE / 2;
  // Maximum ratio of output tokens to balance for swaps.
  uint256 internal constant MAX_OUT_RATIO = (BONE / 3) + 1 wei;
}

File 3 of 7 : BMath.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;

import "./BNum.sol";


/************************************************************************************************
Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BMath.sol

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

Subject to the GPL-3.0 license
*************************************************************************************************/


contract BMath is BConst, BNum {
  /**********************************************************************************************
    // calcSpotPrice                                                                             //
    // sP = spotPrice                                                                            //
    // bI = tokenBalanceIn                ( bI / wI )         1                                  //
    // bO = tokenBalanceOut         sP =  -----------  *  ----------                             //
    // wI = tokenWeightIn                 ( bO / wO )     ( 1 - sF )                             //
    // wO = tokenWeightOut                                                                       //
    // sF = swapFee                                                                              //
    **********************************************************************************************/
  function calcSpotPrice(
    uint256 tokenBalanceIn,
    uint256 tokenWeightIn,
    uint256 tokenBalanceOut,
    uint256 tokenWeightOut,
    uint256 swapFee
  ) internal pure returns (uint256 spotPrice) {
    uint256 numer = bdiv(tokenBalanceIn, tokenWeightIn);
    uint256 denom = bdiv(tokenBalanceOut, tokenWeightOut);
    uint256 ratio = bdiv(numer, denom);
    uint256 scale = bdiv(BONE, bsub(BONE, swapFee));
    return (spotPrice = bmul(ratio, scale));
  }

  /**********************************************************************************************
    // calcOutGivenIn                                                                            //
    // aO = tokenAmountOut                                                                       //
    // bO = tokenBalanceOut                                                                      //
    // bI = tokenBalanceIn              /      /            bI             \    (wI / wO) \      //
    // aI = tokenAmountIn    aO = bO * |  1 - | --------------------------  | ^            |     //
    // wI = tokenWeightIn               \      \ ( bI + ( aI * ( 1 - sF )) /              /      //
    // wO = tokenWeightOut                                                                       //
    // sF = swapFee                                                                              //
    **********************************************************************************************/
  function calcOutGivenIn(
    uint256 tokenBalanceIn,
    uint256 tokenWeightIn,
    uint256 tokenBalanceOut,
    uint256 tokenWeightOut,
    uint256 tokenAmountIn,
    uint256 swapFee
  ) internal pure returns (uint256 tokenAmountOut) {
    uint256 weightRatio = bdiv(tokenWeightIn, tokenWeightOut);
    uint256 adjustedIn = bsub(BONE, swapFee);
    adjustedIn = bmul(tokenAmountIn, adjustedIn);
    uint256 y = bdiv(tokenBalanceIn, badd(tokenBalanceIn, adjustedIn));
    uint256 foo = bpow(y, weightRatio);
    uint256 bar = bsub(BONE, foo);
    tokenAmountOut = bmul(tokenBalanceOut, bar);
    return tokenAmountOut;
  }

  /**********************************************************************************************
    // calcInGivenOut                                                                            //
    // aI = tokenAmountIn                                                                        //
    // bO = tokenBalanceOut               /  /     bO      \    (wO / wI)      \                 //
    // bI = tokenBalanceIn          bI * |  | ------------  | ^            - 1  |                //
    // aO = tokenAmountOut    aI =        \  \ ( bO - aO ) /                   /                 //
    // wI = tokenWeightIn           --------------------------------------------                 //
    // wO = tokenWeightOut                          ( 1 - sF )                                   //
    // sF = swapFee                                                                              //
    **********************************************************************************************/
  function calcInGivenOut(
    uint256 tokenBalanceIn,
    uint256 tokenWeightIn,
    uint256 tokenBalanceOut,
    uint256 tokenWeightOut,
    uint256 tokenAmountOut,
    uint256 swapFee
  ) internal pure returns (uint256 tokenAmountIn) {
    uint256 weightRatio = bdiv(tokenWeightOut, tokenWeightIn);
    uint256 diff = bsub(tokenBalanceOut, tokenAmountOut);
    uint256 y = bdiv(tokenBalanceOut, diff);
    uint256 foo = bpow(y, weightRatio);
    foo = bsub(foo, BONE);
    tokenAmountIn = bsub(BONE, swapFee);
    tokenAmountIn = bdiv(bmul(tokenBalanceIn, foo), tokenAmountIn);
    return tokenAmountIn;
  }

  /**********************************************************************************************
    // calcPoolOutGivenSingleIn                                                                  //
    // pAo = poolAmountOut         /                                              \              //
    // tAi = tokenAmountIn        ///      /     //    wI \      \\       \     wI \             //
    // wI = tokenWeightIn        //| tAi *| 1 - || 1 - --  | * sF || + tBi \    --  \            //
    // tW = totalWeight     pAo=||  \      \     \\    tW /      //         | ^ tW   | * pS - pS //
    // tBi = tokenBalanceIn      \\  ------------------------------------- /        /            //
    // pS = poolSupply            \\                    tBi               /        /             //
    // sF = swapFee                \                                              /              //
    **********************************************************************************************/
  function calcPoolOutGivenSingleIn(
    uint256 tokenBalanceIn,
    uint256 tokenWeightIn,
    uint256 poolSupply,
    uint256 totalWeight,
    uint256 tokenAmountIn,
    uint256 swapFee
  ) internal pure returns (uint256 poolAmountOut) {
    // Charge the trading fee for the proportion of tokenAi
    ///  which is implicitly traded to the other pool tokens.
    // That proportion is (1- weightTokenIn)
    // tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee);
    uint256 normalizedWeight = bdiv(tokenWeightIn, totalWeight);
    uint256 zaz = bmul(bsub(BONE, normalizedWeight), swapFee);
    uint256 tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz));

    uint256 newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee);
    uint256 tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn);

    // uint newPoolSupply = (ratioTi ^ weightTi) * poolSupply;
    uint256 poolRatio = bpow(tokenInRatio, normalizedWeight);
    uint256 newPoolSupply = bmul(poolRatio, poolSupply);
    poolAmountOut = bsub(newPoolSupply, poolSupply);
    return poolAmountOut;
  }

  /**********************************************************************************************
    // calcSingleInGivenPoolOut                                                                  //
    // tAi = tokenAmountIn              //(pS + pAo)\     /    1    \\                           //
    // pS = poolSupply                 || ---------  | ^ | --------- || * bI - bI                //
    // pAo = poolAmountOut              \\    pS    /     \(wI / tW)//                           //
    // bI = balanceIn          tAi =  --------------------------------------------               //
    // wI = weightIn                              /      wI  \                                   //
    // tW = totalWeight                          |  1 - ----  |  * sF                            //
    // sF = swapFee                               \      tW  /                                   //
    **********************************************************************************************/
  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;
  }

  /**********************************************************************************************
    // calcSingleOutGivenPoolIn                                                                  //
    // tAo = tokenAmountOut            /      /                                             \\   //
    // bO = tokenBalanceOut           /      // pS - (pAi * (1 - eF)) \     /    1    \      \\  //
    // pAi = poolAmountIn            | bO - || ----------------------- | ^ | --------- | * b0 || //
    // ps = poolSupply                \      \\          pS           /     \(wO / tW)/      //  //
    // wI = tokenWeightIn      tAo =   \      \                                             //   //
    // tW = totalWeight                    /     /      wO \       \                             //
    // sF = swapFee                    *  | 1 - |  1 - ---- | * sF  |                            //
    // eF = exitFee                        \     \      tW /       /                             //
    **********************************************************************************************/
  function calcSingleOutGivenPoolIn(
    uint256 tokenBalanceOut,
    uint256 tokenWeightOut,
    uint256 poolSupply,
    uint256 totalWeight,
    uint256 poolAmountIn,
    uint256 swapFee
  ) internal pure returns (uint256 tokenAmountOut) {
    uint256 normalizedWeight = bdiv(tokenWeightOut, totalWeight);
    // charge exit fee on the pool token side
    // pAiAfterExitFee = pAi*(1-exitFee)
    uint256 poolAmountInAfterExitFee = bmul(poolAmountIn, bsub(BONE, EXIT_FEE));
    uint256 newPoolSupply = bsub(poolSupply, poolAmountInAfterExitFee);
    uint256 poolRatio = bdiv(newPoolSupply, poolSupply);

    // newBalTo = poolRatio^(1/weightTo) * balTo;
    uint256 tokenOutRatio = bpow(poolRatio, bdiv(BONE, normalizedWeight));
    uint256 newTokenBalanceOut = bmul(tokenOutRatio, tokenBalanceOut);

    uint256 tokenAmountOutBeforeSwapFee = bsub(
      tokenBalanceOut,
      newTokenBalanceOut
    );

    // charge swap fee on the output token side
    //uint tAo = tAoBeforeSwapFee * (1 - (1-weightTo) * swapFee)
    uint256 zaz = bmul(bsub(BONE, normalizedWeight), swapFee);
    tokenAmountOut = bmul(tokenAmountOutBeforeSwapFee, bsub(BONE, zaz));
    return tokenAmountOut;
  }

  /**********************************************************************************************
    // calcPoolInGivenSingleOut                                                                  //
    // pAi = poolAmountIn               // /               tAo             \\     / wO \     \   //
    // bO = tokenBalanceOut            // | bO - -------------------------- |\   | ---- |     \  //
    // tAo = tokenAmountOut      pS - ||   \     1 - ((1 - (tO / tW)) * sF)/  | ^ \ tW /  * pS | //
    // ps = poolSupply                 \\ -----------------------------------/                /  //
    // wO = tokenWeightOut  pAi =       \\               bO                 /                /   //
    // tW = totalWeight           -------------------------------------------------------------  //
    // sF = swapFee                                        ( 1 - eF )                            //
    // eF = exitFee                                                                              //
    **********************************************************************************************/
  function calcPoolInGivenSingleOut(
    uint256 tokenBalanceOut,
    uint256 tokenWeightOut,
    uint256 poolSupply,
    uint256 totalWeight,
    uint256 tokenAmountOut,
    uint256 swapFee
  ) internal pure returns (uint256 poolAmountIn) {
    // charge swap fee on the output token side
    uint256 normalizedWeight = bdiv(tokenWeightOut, totalWeight);
    //uint tAoBeforeSwapFee = tAo / (1 - (1-weightTo) * swapFee) ;
    uint256 zoo = bsub(BONE, normalizedWeight);
    uint256 zar = bmul(zoo, swapFee);
    uint256 tokenAmountOutBeforeSwapFee = bdiv(tokenAmountOut, bsub(BONE, zar));

    uint256 newTokenBalanceOut = bsub(
      tokenBalanceOut,
      tokenAmountOutBeforeSwapFee
    );
    uint256 tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut);

    //uint newPoolSupply = (ratioTo ^ weightTo) * poolSupply;
    uint256 poolRatio = bpow(tokenOutRatio, normalizedWeight);
    uint256 newPoolSupply = bmul(poolRatio, poolSupply);
    uint256 poolAmountInAfterExitFee = bsub(poolSupply, newPoolSupply);

    // charge exit fee on the pool token side
    // pAi = pAiAfterExitFee/(1-exitFee)
    poolAmountIn = bdiv(poolAmountInAfterExitFee, bsub(BONE, EXIT_FEE));
    return poolAmountIn;
  }
}

File 4 of 7 : BNum.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;

import "./BConst.sol";


/************************************************************************************************
Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol

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

Subject to the GPL-3.0 license
*************************************************************************************************/


contract BNum is BConst {
  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 5 of 7 : BToken.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;

import "./BNum.sol";


/************************************************************************************************
Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BToken.sol

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

Subject to the GPL-3.0 license
*************************************************************************************************/


// Highly opinionated token implementation
interface IERC20 {
  event Approval(address indexed src, address indexed dst, uint256 amt);
  event Transfer(address indexed src, address indexed dst, uint256 amt);

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

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

  function decimals() external view returns (uint8);

  function totalSupply() external view returns (uint256);

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

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

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

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

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


contract BTokenBase is BNum {
  mapping(address => uint256) internal _balance;
  mapping(address => mapping(address => uint256)) internal _allowance;
  uint256 internal _totalSupply;

  event Approval(address indexed src, address indexed dst, uint256 amt);
  event Transfer(address indexed src, address indexed dst, uint256 amt);

  function _mint(uint256 amt) internal {
    _balance[address(this)] = badd(_balance[address(this)], amt);
    _totalSupply = badd(_totalSupply, amt);
    emit Transfer(address(0), address(this), amt);
  }

  function _burn(uint256 amt) internal {
    require(_balance[address(this)] >= amt, "ERR_INSUFFICIENT_BAL");
    _balance[address(this)] = bsub(_balance[address(this)], amt);
    _totalSupply = bsub(_totalSupply, amt);
    emit Transfer(address(this), address(0), amt);
  }

  function _move(
    address src,
    address dst,
    uint256 amt
  ) internal {
    require(_balance[src] >= amt, "ERR_INSUFFICIENT_BAL");
    _balance[src] = bsub(_balance[src], amt);
    _balance[dst] = badd(_balance[dst], amt);
    emit Transfer(src, dst, amt);
  }

  function _push(address to, uint256 amt) internal {
    _move(address(this), to, amt);
  }

  function _pull(address from, uint256 amt) internal {
    _move(from, address(this), amt);
  }
}


contract BToken is BTokenBase, IERC20 {
  uint8 private constant DECIMALS = 18;
  string private _name;
  string private _symbol;

  function _initializeToken(string memory name, string memory symbol) internal {
    require(
      bytes(_name).length == 0 &&
      bytes(name).length != 0 &&
      bytes(symbol).length != 0,
      "ERR_BTOKEN_INITIALIZED"
    );
    _name = name;
    _symbol = symbol;
  }

  function name()
    external
    override
    view
    returns (string memory)
  {
    return _name;
  }

  function symbol()
    external
    override
    view
    returns (string memory)
  {
    return _symbol;
  }

  function decimals()
    external
    override
    view
    returns (uint8)
  {
    return DECIMALS;
  }

  function allowance(address src, address dst)
    external
    override
    view
    returns (uint256)
  {
    return _allowance[src][dst];
  }

  function balanceOf(address whom) external override view returns (uint256) {
    return _balance[whom];
  }

  function totalSupply() public override view returns (uint256) {
    return _totalSupply;
  }

  function approve(address dst, uint256 amt) external override returns (bool) {
    _allowance[msg.sender][dst] = amt;
    emit Approval(msg.sender, dst, amt);
    return true;
  }

  function increaseApproval(address dst, uint256 amt) external returns (bool) {
    _allowance[msg.sender][dst] = badd(_allowance[msg.sender][dst], amt);
    emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
    return true;
  }

  function decreaseApproval(address dst, uint256 amt) external returns (bool) {
    uint256 oldValue = _allowance[msg.sender][dst];
    if (amt > oldValue) {
      _allowance[msg.sender][dst] = 0;
    } else {
      _allowance[msg.sender][dst] = bsub(oldValue, amt);
    }
    emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
    return true;
  }

  function transfer(address dst, uint256 amt) external override returns (bool) {
    _move(msg.sender, dst, amt);
    return true;
  }

  function transferFrom(
    address src,
    address dst,
    uint256 amt
  ) external override returns (bool) {
    require(
      msg.sender == src || amt <= _allowance[src][msg.sender],
      "ERR_BTOKEN_BAD_CALLER"
    );
    _move(src, dst, amt);
    if (msg.sender != src && _allowance[src][msg.sender] != uint256(-1)) {
      _allowance[src][msg.sender] = bsub(_allowance[src][msg.sender], amt);
      emit Approval(msg.sender, dst, _allowance[src][msg.sender]);
    }
    return true;
  }
}

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


interface IIndexPool {
  /**
   * @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_TOGGLED(bool 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 setSwapFee(uint256 swapFee) external;

  function delegateCompLikeToken(address token, address delegatee) external;

  function setExitFeeRecipient(address) external;

  function setPublicSwap(bool enabled) 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 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 getExitFeeRecipient() external view returns (address);

  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 7 of 7 : ICompLikeToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;


interface ICompLikeToken {
  function delegate(address delegatee) external;
}

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

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":false,"internalType":"uint256","name":"amt","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newDenorm","type":"uint256"}],"name":"LOG_DENORM_UPDATED","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"desiredDenorm","type":"uint256"}],"name":"LOG_DESIRED_DENORM_SET","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmountOut","type":"uint256"}],"name":"LOG_EXIT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmountIn","type":"uint256"}],"name":"LOG_JOIN","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxPoolTokens","type":"uint256"}],"name":"LOG_MAX_TOKENS_UPDATED","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"minimumBalance","type":"uint256"}],"name":"LOG_MINIMUM_BALANCE_UPDATED","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"LOG_PUBLIC_SWAP_TOGGLED","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":true,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmountOut","type":"uint256"}],"name":"LOG_SWAP","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"swapFee","type":"uint256"}],"name":"LOG_SWAP_FEE_UPDATED","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"desiredDenorm","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minimumBalance","type":"uint256"}],"name":"LOG_TOKEN_ADDED","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"LOG_TOKEN_READY","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"LOG_TOKEN_REMOVED","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":false,"internalType":"uint256","name":"amt","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"VERSION_NUMBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"whom","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"name":"configure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"decreaseApproval","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegateCompLikeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolAmountIn","type":"uint256"},{"internalType":"uint256[]","name":"minAmountsOut","type":"uint256[]"}],"name":"exitPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"tokenAmountOut","type":"uint256"},{"internalType":"uint256","name":"maxPoolAmountIn","type":"uint256"}],"name":"exitswapExternAmountOut","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"poolAmountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"exitswapPoolAmountIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"extrapolatePoolValueFromToken","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getController","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentDesiredTokens","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentTokens","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getDenormalizedWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExitFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getMinimumBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"getSpotPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSwapFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getTokenRecord","outputs":[{"components":[{"internalType":"bool","name":"bound","type":"bool"},{"internalType":"bool","name":"ready","type":"bool"},{"internalType":"uint40","name":"lastDenormUpdate","type":"uint40"},{"internalType":"uint96","name":"denorm","type":"uint96"},{"internalType":"uint96","name":"desiredDenorm","type":"uint96"},{"internalType":"uint8","name":"index","type":"uint8"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct IIndexPool.Record","name":"record","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalDenormalizedWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getUsedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"gulp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"increaseApproval","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"balances","type":"uint256[]"},{"internalType":"uint96[]","name":"denorms","type":"uint96[]"},{"internalType":"address","name":"tokenProvider","type":"address"},{"internalType":"address","name":"unbindHandler","type":"address"},{"internalType":"address","name":"exitFeeRecipient","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"t","type":"address"}],"name":"isBound","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPublicSwap","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolAmountOut","type":"uint256"},{"internalType":"uint256[]","name":"maxAmountsIn","type":"uint256[]"}],"name":"joinPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"tokenAmountIn","type":"uint256"},{"internalType":"uint256","name":"minPoolAmountOut","type":"uint256"}],"name":"joinswapExternAmountIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"poolAmountOut","type":"uint256"},{"internalType":"uint256","name":"maxAmountIn","type":"uint256"}],"name":"joinswapPoolAmountOut","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint96[]","name":"desiredDenorms","type":"uint96[]"},{"internalType":"uint256[]","name":"minimumBalances","type":"uint256[]"}],"name":"reindexTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint96[]","name":"desiredDenorms","type":"uint96[]"}],"name":"reweighTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"exitFeeRecipient","type":"address"}],"name":"setExitFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"minimumBalance","type":"uint256"}],"name":"setMinimumBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setPublicSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"swapFee","type":"uint256"}],"name":"setSwapFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"tokenAmountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"name":"swapExactAmountIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"maxAmountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"tokenAmountOut","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"name":"swapExactAmountOut","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50615b8780620000216000396000f3fe608060405234801561001057600080fd5b506004361061028a5760003560e01c80637c5e9ea41161015c578063a9059cbb116100ce578063d73dd62311610087578063d73dd6231461057a578063dd62ed3e1461058d578063f8b2cb4f146105a0578063f8b5db09146105b3578063f9f97c98146105c6578063fde924f7146105d95761028a565b8063a9059cbb14610529578063b02f0b731461053c578063c3f468101461054f578063cc77828d14610562578063cd2ed8fb1461056a578063d4cadf68146105725761028a565b806391bfa2bf1161012057806391bfa2bf146104ca578063936c3477146104dd578063948d8ce6146104e557806395d89b41146104f857806398836f0814610500578063a49c44d7146105165761028a565b80637c5e9ea4146104735780638025e303146104945780638201aa3f1461049c578063865bcccb146104af5780638c28cbe8146104b75761028a565b8063313ce567116102005780635d5e8ce7116101b95780635d5e8ce7146103f45780635db342771461040757806364c7d6611461041a578063661884631461043a5780636d06dfa01461044d57806370a08231146104605761028a565b8063313ce5671461038057806334e199071461039557806346ab38f1146103a857806349b59552146103bb5780634aa4e0b5146103ce5780634f69c0d4146103e15761028a565b806318160ddd1161025257806318160ddd1461031557806319f0f8491461031d57806323b872dd146103325780632b110c74146103455780632f37b624146103585780633018205f1461036b5761028a565b806302c967481461028f578063039209af146102b857806306fdde03146102cd578063095ea7b3146102e257806315e84af914610302575b600080fd5b6102a261029d366004614fb0565b6105e1565b6040516102af9190615a6c565b60405180910390f35b6102c06107bd565b6040516102af91906152f6565b6102d5610924565b6040516102af919061534e565b6102f56102f0366004614f37565b6109ba565b6040516102af9190615343565b6102a2610310366004614e43565b610a13565b6102a2610a9a565b61033061032b366004614eb7565b610aa0565b005b6102f5610340366004614e77565b610b9b565b610330610353366004614fe3565b610ccc565b6102f5610366366004614e28565b61113b565b61037361115d565b6040516102af91906152a5565b610388611171565b6040516102af9190615a83565b6103306103a33660046151e8565b611176565b6102a26103b6366004614fb0565b61121e565b6103306103c93660046151b0565b61139c565b6102a26103dc366004614e28565b611412565b6103306103ef366004615218565b61151f565b6103306104023660046150b1565b611718565b6102a2610415366004614fb0565b6117f4565b61042d610428366004614e28565b61199c565b6040516102af9190615a01565b6102f5610448366004614f37565b611a73565b6102a261045b366004614fb0565b611b3c565b6102a261046e366004614e28565b611cc6565b610486610481366004614f61565b611ce1565b6040516102af929190615a75565b6102a2611fd8565b6104866104aa366004614f61565b611fdd565b6103736122b0565b6103306104c5366004614e28565b6122bf565b6102a26104d8366004614e28565b612586565b6102a261269f565b6102a26104f3366004614e28565b6126cc565b6102d5612756565b6105086127b7565b6040516102af9291906152dd565b610330610524366004614f37565b6128bb565b6102f5610537366004614f37565b6129a3565b61033061054a366004615218565b6129b9565b61033061055d36600461511a565b612c84565b6102c061301d565b6102a26130a2565b6102a26130a8565b6102f5610588366004614f37565b6130d5565b6102a261059b366004614e43565b613149565b6102a26105ae366004614e28565b613174565b6103306105c1366004614e43565b6131dd565b6103306105d4366004614e28565b61326e565b6102f56132ba565b60055460009060ff16156106105760405162461bcd60e51b81526004016106079061564d565b60405180910390fd5b6005805460ff19166001179055610625614cc0565b61062e856132ca565b90506106538160c001516003670de0b6b3a76400008161064a57fe5b0460010161339f565b8411156106725760405162461bcd60e51b815260040161060790615672565b60006106998260c0015183606001516001600160601b0316600254600a5489600754613418565b9050806106b85760405162461bcd60e51b815260040161060790615478565b838111156106d85760405162461bcd60e51b8152600401610607906155f7565b6106e38633876134e2565b6106f18260c00151866135d1565b6001600160a01b038716600090815260096020526040902060010155610717828761360a565b600061072a826611c37937e0800061339f565b9050866001600160a01b0316336001600160a01b03167fe74c91552b64c2e2e7bd255639e004e693bd3e1d01cc33e65610b86afcc1ffed8860405161076f9190615a6c565b60405180910390a3610781338361379c565b61079361078e83836135d1565b6137a6565b600c546107a9906001600160a01b0316826137b2565b506005805460ff1916905595945050505050565b60055460609060ff16156107e35760405162461bcd60e51b81526004016106079061564d565b6060600880548060200260200160405190810160405280929190818152602001828054801561083b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161081d575b50505050509050805167ffffffffffffffff8111801561085a57600080fd5b50604051908082528060200260200182016040528015610884578160200160208202803683370190505b5091506000805b835181101561091d5760008382815181106108a257fe5b6020908102919091018101516001600160a01b03811660009081526009909252604090912054909150600160981b90046001600160601b03161561091457808584806001019550815181106108f357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b5060010161088b565b5082525090565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156109b05780601f10610985576101008083540402835291602001916109b0565b820191906000526020600020905b81548152906001019060200180831161099357829003601f168201915b5050505050905090565b3360008181526001602090815260408083206001600160a01b03871680855292528083208590555191929091600080516020615b3283398151915290610a01908690615a6c565b60405180910390a35060015b92915050565b60055460009060ff1615610a395760405162461bcd60e51b81526004016106079061564d565b610a41614cc0565b610a4a846137bc565b509050610a55614cc0565b610a5e846132ca565b9050610a918260c0015183606001516001600160601b03168360c0015184606001516001600160601b031660075461390a565b95945050505050565b60025490565b60055461010090046001600160a01b031615610ace5760405162461bcd60e51b815260040161060790615836565b6001600160a01b038516610af45760405162461bcd60e51b815260040161060790615931565b60058054610100600160a81b0319166101006001600160a01b038816021790556028670de0b6b3a764000004600755604080516020601f8601819004810282018101909252848152610b9491869086908190840183828082843760009201919091525050604080516020601f8801819004810282018101909252868152925086915085908190840183828082843760009201919091525061396f92505050565b5050505050565b6000336001600160a01b0385161480610bd757506001600160a01b03841660009081526001602090815260408083203384529091529020548211155b610bf35760405162461bcd60e51b8152600401610607906155c8565b610bfe8484846139e6565b336001600160a01b03851614801590610c3c57506001600160a01b038416600090815260016020908152604080832033845290915290205460001914155b15610cc2576001600160a01b0384166000908152600160209081526040808320338452909152902054610c6f90836135d1565b6001600160a01b038581166000908152600160209081526040808320338085529252918290208490559051918616929091600080516020615b3283398151915291610cb991615a6c565b60405180910390a35b5060019392505050565b60055461010090046001600160a01b03163314610cfb5760405162461bcd60e51b815260040161060790615381565b60085415610d1b5760405162461bcd60e51b815260040161060790615575565b876002811015610d3d5760405162461bcd60e51b815260040161060790615984565b600a811115610d5e5760405162461bcd60e51b8152600401610607906153fb565b8681148015610d6c57508481145b610d885760405162461bcd60e51b8152600401610607906157c5565b6000805b828110156110645760008c8c83818110610da257fe5b9050602002016020810190610db79190614e28565b90506000898984818110610dc757fe5b9050602002016020810190610ddc9190615262565b905060008c8c85818110610dec57fe5b9050602002013590506004670de0b6b3a764000081610e0757fe5b04826001600160601b03161015610e305760405162461bcd60e51b815260040161060790615744565b68015af1d78b58c400006001600160601b0383161115610e625760405162461bcd60e51b8152600401610607906154a1565b620f4240811015610e855760405162461bcd60e51b81526004016106079061551e565b6040518060e001604052806001151581526020016001151581526020014264ffffffffff168152602001836001600160601b03168152602001836001600160601b031681526020018560ff1681526020018281525060096000856001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff02191690831515021790555060408201518160000160026101000a81548164ffffffffff021916908364ffffffffff16021790555060608201518160000160076101000a8154816001600160601b0302191690836001600160601b0316021790555060808201518160000160136101000a8154816001600160601b0302191690836001600160601b0316021790555060a082015181600001601f6101000a81548160ff021916908360ff16021790555060c082015181600101559050506008839080600181540180825580915050600190039060005260206000200160009091909190916101000a8154816001600160a01b0302191690836001600160a01b0316021790555061104c85836001600160601b0316613acf565b9450611059838a83613afb565b505050600101610d8c565b50680168d28e3f0028000081111561108e5760405162461bcd60e51b8152600401610607906154c9565b600a8190556006805460ff60a01b1916600160a01b1790556040517f40fc85fbff9305015298ba6fcee88b7e442a64cc803ddb889327680bbd62270a906110d790600190615343565b60405180910390a16110f168056bc75e2d63100000613b26565b6111048568056bc75e2d631000006137b2565b5050600680546001600160a01b039384166001600160a01b031991821617909155600c805492909316911617905550505050505050565b6001600160a01b03811660009081526009602052604090205460ff165b919050565b60055461010090046001600160a01b031690565b601290565b60055461010090046001600160a01b031633146111a55760405162461bcd60e51b815260040161060790615381565b64e8d4a5100081108015906111c2575067016345785d8a00008111155b6111de5760405162461bcd60e51b81526004016106079061595b565b60078190556040517fccfe595973efc7c1f6c29e31974d380470b9431d7770290185b7129419c7e63e90611213908390615a6c565b60405180910390a150565b60055460009060ff16156112445760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055611259614cc0565b611262856132ca565b9050600061128b8260c0015183606001516001600160601b0316600254600a5489600754613b2f565b9050838110156112ad5760405162461bcd60e51b8152600401610607906154f7565b6112c78260c001516003670de0b6b3a76400008161064a57fe5b8111156112e65760405162461bcd60e51b815260040161060790615672565b6112f18633836134e2565b6112ff8260c00151826135d1565b6001600160a01b038716600090815260096020526040902060010155611325828761360a565b6000611338866611c37937e0800061339f565b9050866001600160a01b0316336001600160a01b03167fe74c91552b64c2e2e7bd255639e004e693bd3e1d01cc33e65610b86afcc1ffed8460405161137d9190615a6c565b60405180910390a361138f338761379c565b61079361078e87836135d1565b60055461010090046001600160a01b031633146113cb5760405162461bcd60e51b815260040161060790615381565b6006805460ff60a01b1916600160a01b831515021790556040517f40fc85fbff9305015298ba6fcee88b7e442a64cc803ddb889327680bbd62270a90611213908390615343565b60055460009060ff16156114385760405162461bcd60e51b81526004016106079061564d565b611440614cc0565b506001600160a01b038216600090815260096020908152604091829020825160e081018452815460ff80821615158084526101008304821615159584019590955264ffffffffff62010000830416958301959095526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490931660a08401526001015460c08301526114ec5760405162461bcd60e51b81526004016106079061569d565b80602001516115155750506001600160a01b0381166000908152600b6020526040902054611158565b60c0015192915050565b60055460ff16156115425760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055600654600160a01b900460ff166115785760405162461bcd60e51b8152600401610607906156ef565b6000611582610a9a565b905060006115908583613bfe565b9050806115af5760405162461bcd60e51b815260040161060790615478565b60085483146115d05760405162461bcd60e51b8152600401610607906157c5565b60005b838110156116f3576000600882815481106115ea57fe5b6000918252602090912001546001600160a01b03169050611609614cc0565b6000611614836137bc565b915091506000611628868460c0015161339f565b9050806116475760405162461bcd60e51b815260040161060790615478565b88888681811061165357fe5b905060200201358111156116795760405162461bcd60e51b8152600401610607906155f7565b61168d84846116888585613acf565b613c92565b836001600160a01b0316336001600160a01b03167f63982df10efd8dfaaaa0fcc7f50b2d93b7cba26ccc48adee2873220d485dc39a836040516116d09190615a6c565b60405180910390a36116e3843383613afb565b5050600190920191506115d39050565b506116fd85613b26565b61170733866137b2565b50506005805460ff19169055505050565b60055460ff161561173b5760405162461bcd60e51b81526004016106079061564d565b60058054600160ff19909116179081905561010090046001600160a01b031633146117785760405162461bcd60e51b815260040161060790615381565b8083146117975760405162461bcd60e51b8152600401610607906157c5565b60005b83811015611707576117ec8585838181106117b157fe5b90506020020160208101906117c69190614e28565b8484848181106117d257fe5b90506020020160208101906117e79190615262565b613e72565b60010161179a565b60055460009060ff161561181a5760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055600654600160a01b900460ff166118505760405162461bcd60e51b8152600401610607906156ef565b611858614cc0565b6000611863866137bc565b9150915084600014156118885760405162461bcd60e51b8152600401610607906159dc565b6118a88260c001516002670de0b6b3a7640000816118a257fe5b0461339f565b8511156118c75760405162461bcd60e51b815260040161060790615423565b60006118ee8360c0015184606001516001600160601b0316600254600a548a600754613f8f565b9050848110156119105760405162461bcd60e51b8152600401610607906154f7565b61191f8784611688858a613acf565b866001600160a01b0316336001600160a01b03167f63982df10efd8dfaaaa0fcc7f50b2d93b7cba26ccc48adee2873220d485dc39a886040516119629190615a6c565b60405180910390a361197381613b26565b61197d33826137b2565b611988873388613afb565b6005805460ff191690559695505050505050565b6119a4614cc0565b60055460ff16156119c75760405162461bcd60e51b81526004016106079061564d565b506001600160a01b038116600090815260096020908152604091829020825160e081018452815460ff80821615158084526101008304821615159584019590955264ffffffffff62010000830416958301959095526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490931660a08401526001015460c08301526111585760405162461bcd60e51b81526004016106079061569d565b3360009081526001602090815260408083206001600160a01b038616845290915281205480831115611ac8573360009081526001602090815260408083206001600160a01b0388168452909152812055611af7565b611ad281846135d1565b3360009081526001602090815260408083206001600160a01b03891684529091529020555b3360008181526001602090815260408083206001600160a01b038916808552925291829020549151909291600080516020615b3283398151915291610cb99190615a6c565b60055460009060ff1615611b625760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055600654600160a01b900460ff16611b985760405162461bcd60e51b8152600401610607906156ef565b611ba0614cc0565b6000611bab866137bc565b915091506000611bd68360c0015184606001516001600160601b0316600254600a548a60075461402b565b905080611bf55760405162461bcd60e51b815260040161060790615478565b84811115611c155760405162461bcd60e51b8152600401610607906155f7565b611c2f8360c001516002670de0b6b3a7640000816118a257fe5b811115611c4e5760405162461bcd60e51b815260040161060790615423565b611c5d87846116888585613acf565b866001600160a01b0316336001600160a01b03167f63982df10efd8dfaaaa0fcc7f50b2d93b7cba26ccc48adee2873220d485dc39a83604051611ca09190615a6c565b60405180910390a3611cb186613b26565b611cbb33876137b2565b611988873383613afb565b6001600160a01b031660009081526020819052604090205490565b600554600090819060ff1615611d095760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055600654600160a01b900460ff16611d3f5760405162461bcd60e51b8152600401610607906156ef565b611d47614cc0565b6000611d52896137bc565b91509150611d5e614cc0565b611d67886132ca565b9050611d838160c001516003670de0b6b3a76400008161064a57fe5b871115611da25760405162461bcd60e51b815260040161060790615672565b6000611dd58460c0015185606001516001600160601b03168460c0015185606001516001600160601b031660075461390a565b905086811115611df75760405162461bcd60e51b815260040161060790615717565b6000611e2b8560c0015186606001516001600160601b03168560c0015186606001516001600160601b03168d6007546140c5565b90508a811115611e4d5760405162461bcd60e51b8152600401610607906155f7565b611e588c3383613afb565b611e638a338b6134e2565b611e6d8482613acf565b9350611e7a8c8686613c92565b846020015115611e8c5760c085018490525b611e9a8360c001518a6135d1565b60c084018190526001600160a01b038b16600090815260096020526040902060010155611ec7838b61360a565b6000611efa8660c0015187606001516001600160601b03168660c0015187606001516001600160601b031660075461390a565b905082811015611f1c5760405162461bcd60e51b815260040161060790615478565b88811115611f3c5760405162461bcd60e51b81526004016106079061580d565b611f46828b613bfe565b831115611f655760405162461bcd60e51b815260040161060790615478565b8a6001600160a01b03168d6001600160a01b0316336001600160a01b03167f908fb5ee8f16c6bc9bc3690973819f32a4d4b10188134543c88706e0e1d43378858e604051611fb4929190615a75565b60405180910390a46005805460ff19169055909c909b509950505050505050505050565b600181565b600554600090819060ff16156120055760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055600654600160a01b900460ff1661203b5760405162461bcd60e51b8152600401610607906156ef565b612043614cc0565b600061204e896137bc565b9150915061205a614cc0565b612063886132ca565b905061207f8360c001516002670de0b6b3a7640000816118a257fe5b89111561209e5760405162461bcd60e51b815260040161060790615423565b60006120d18460c0015185606001516001600160601b03168460c0015185606001516001600160601b031660075461390a565b9050868111156120f35760405162461bcd60e51b815260040161060790615717565b60006121278560c0015186606001516001600160601b03168560c0015186606001516001600160601b03168f600754614148565b9050888110156121495760405162461bcd60e51b8152600401610607906154f7565b6121548c338d613afb565b61215f8a33836134e2565b612169848c613acf565b93506121768c8686613c92565b8460200151156121885760c085018490525b6121968360c00151826135d1565b60c084018190526001600160a01b038b166000908152600960205260409020600101556121c3838b61360a565b60006121f68660c0015187606001516001600160601b03168660c0015187606001516001600160601b031660075461390a565b9050828110156122185760405162461bcd60e51b81526004016106079061544d565b888111156122385760405162461bcd60e51b81526004016106079061580d565b6122428c83613bfe565b8311156122615760405162461bcd60e51b815260040161060790615478565b8a6001600160a01b03168d6001600160a01b0316336001600160a01b03167f908fb5ee8f16c6bc9bc3690973819f32a4d4b10188134543c88706e0e1d433788f86604051611fb4929190615a75565b600c546001600160a01b031690565b60055460ff16156122e25760405162461bcd60e51b81526004016106079061564d565b6005805460ff191660011790556001600160a01b03811660008181526009602052604080822090516370a0823160e01b81529092906370a082319061232b9030906004016152a5565b60206040518083038186803b15801561234357600080fd5b505afa158015612357573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061237b9190615200565b825490915060ff16156124fa578154610100900460ff166124d7576001600160a01b0383166000908152600b60205260409020548082106124d5576001600160a01b0384166000818152600b6020526040808220829055855461ff001916610100178655517ff7bb8e57ffdfd9a31e7580ee84f68757f44fb4a8a913f44520d22f2da1c955e59190a2600061241083836135d1565b9050600061241e8284613bfe565b9050600061243d6703782dace9d90000612438818561339f565b613acf565b8654600160381b600160981b031916600160381b6001600160601b0383169081029190911766ffffffffff00001916620100004264ffffffffff1602178855600a5491925061248c9190613acf565b600a5585546040516001600160a01b03891691600080516020615b12833981519152916124c991600160381b90046001600160601b031690615a91565b60405180910390a25050505b505b6001600160a01b0383166000908152600960205260409020600101819055612577565b6006546125129084906001600160a01b0316836134e2565b6006546040516360b8257960e11b81526001600160a01b039091169063c1704af29061254490869085906004016152dd565b600060405180830381600087803b15801561255e57600080fd5b505af1158015612572573d6000803e3d6000fd5b505050505b50506005805460ff1916905550565b60055460009060ff16156125ac5760405162461bcd60e51b81526004016106079061564d565b6125b4614cc0565b506001600160a01b038216600090815260096020908152604091829020825160e081018452815460ff80821615158084526101008304821615159584019590955264ffffffffff62010000830416958301959095526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490931660a08401526001015460c08301526126605760405162461bcd60e51b81526004016106079061569d565b8060200151156126825760405162461bcd60e51b8152600401610607906157ea565b50506001600160a01b03166000908152600b602052604090205490565b60055460009060ff16156126c55760405162461bcd60e51b81526004016106079061564d565b50600a5490565b60055460009060ff16156126f25760405162461bcd60e51b81526004016106079061564d565b6001600160a01b03821660009081526009602052604090205460ff1661272a5760405162461bcd60e51b81526004016106079061569d565b506001600160a01b0316600090815260096020526040902054600160381b90046001600160601b031690565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156109b05780601f10610985576101008083540402835291602001916109b0565b600554600090819060ff16156127df5760405162461bcd60e51b81526004016106079061564d565b6008546000908190815b8181101561289057600881815481106127fe57fe5b60009182526020808320909101546001600160a01b03168083526009909152604090912080549195509060ff61010090910416801561284d57508054600160981b90046001600160601b031615155b15612887576001810154600a54825461287f929161287a91600160381b90046001600160601b0316613bfe565b61339f565b935050612890565b506001016127e9565b50600082116128b15760405162461bcd60e51b8152600401610607906153ad565b5090925090509091565b60055461010090046001600160a01b031633146128ea5760405162461bcd60e51b815260040161060790615381565b6001600160a01b0382166000908152600960205260409020805460ff166129235760405162461bcd60e51b81526004016106079061569d565b8054610100900460ff161561294a5760405162461bcd60e51b8152600401610607906157ea565b6001600160a01b0383166000908152600b602052604090819020839055517e0c7a55677231b335e6dea005fa240ac2aeafbd62f188372a7d66892b722c529061299690859085906152dd565b60405180910390a1505050565b60006129b03384846139e6565b50600192915050565b60055460ff16156129dc5760405162461bcd60e51b81526004016106079061564d565b6005805460ff191660011790556008548114612a0a5760405162461bcd60e51b8152600401610607906157c5565b6000612a14610a9a565b90506000612a29856611c37937e0800061339f565b90506000612a3786836135d1565b90506000612a458285613bfe565b905080612a645760405162461bcd60e51b815260040161060790615478565b612a6e338861379c565b600c54612a84906001600160a01b0316846137b2565b612a8d826137a6565b60005b85811015612c7057600060088281548110612aa757fe5b6000918252602090912001546001600160a01b03169050612ac6614cc0565b506001600160a01b038116600090815260096020908152604091829020825160e081018452815460ff80821615158352610100820481161580159584019590955264ffffffffff62010000830416958301959095526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490931660a08401526001015460c0830152612c34576000612b6a858360c0015161339f565b905080612b895760405162461bcd60e51b815260040161060790615478565b898985818110612b9557fe5b90506020020135811015612bbb5760405162461bcd60e51b8152600401610607906154f7565b612bc98260c00151826135d1565b6001600160a01b0384166000818152600960205260409081902060010192909255905133907fe74c91552b64c2e2e7bd255639e004e693bd3e1d01cc33e65610b86afcc1ffed90612c1b908590615a6c565b60405180910390a3612c2e8333836134e2565b50612c66565b888884818110612c4057fe5b90506020020135600014612c665760405162461bcd60e51b8152600401610607906156c4565b5050600101612a90565b50506005805460ff19169055505050505050565b60055460ff1615612ca75760405162461bcd60e51b81526004016106079061564d565b60058054600160ff19909116179081905561010090046001600160a01b03163314612ce45760405162461bcd60e51b815260040161060790615381565b8285148015612cf257508085145b612d0e5760405162461bcd60e51b8152600401610607906157c5565b60085460608167ffffffffffffffff81118015612d2a57600080fd5b50604051908082528060200260200182016040528015612d54578160200160208202803683370190505b50905060608767ffffffffffffffff81118015612d7057600080fd5b50604051908082528060200260200182016040528015612daa57816020015b612d97614cc0565b815260200190600190039081612d8f5790505b50905060005b88811015612ee557600960008b8b84818110612dc857fe5b9050602002016020810190612ddd9190614e28565b6001600160a01b031681526020808201929092526040908101600020815160e081018352815460ff808216151583526101008204811615159583019590955264ffffffffff62010000820416938201939093526001600160601b03600160381b840481166060830152600160981b8404166080820152600160f81b90920490921660a082015260019091015460c08201528251839083908110612e7c57fe5b6020026020010181905250818181518110612e9357fe5b60200260200101516000015115612edd57600183838381518110612eb357fe5b602002602001015160a0015160ff1681518110612ecc57fe5b911515602092830291909101909101525b600101612db0565b5060005b83811015612f3b57828181518110612efd57fe5b6020026020010151612f3357612f3360088281548110612f1957fe5b60009182526020822001546001600160a01b031690613e72565b600101612ee9565b5060005b888110156130075760008a8a83818110612f5557fe5b9050602002016020810190612f6a9190614e28565b90506000898984818110612f7a57fe5b9050602002016020810190612f8f9190615262565b90506703782dace9d900006001600160601b0382161015612fb557506703782dace9d900005b838381518110612fc157fe5b602002602001015160000151612ff357612fee82898986818110612fe157fe5b90506020020135836141c9565b612ffd565b612ffd8282613e72565b5050600101612f3f565b50506005805460ff191690555050505050505050565b60055460609060ff16156130435760405162461bcd60e51b81526004016106079061564d565b60088054806020026020016040519081016040528092919081815260200182805480156109b057602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161307b575050505050905090565b60085490565b60055460009060ff16156130ce5760405162461bcd60e51b81526004016106079061564d565b5060075490565b3360009081526001602090815260408083206001600160a01b03861684529091528120546131039083613acf565b3360008181526001602090815260408083206001600160a01b03891680855292529182902084905590519092600080516020615b3283398151915291610a019190615a6c565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60055460009060ff161561319a5760405162461bcd60e51b81526004016106079061564d565b6001600160a01b0382166000908152600960205260409020805460ff166131d35760405162461bcd60e51b81526004016106079061569d565b6001015492915050565b60055461010090046001600160a01b0316331461320c5760405162461bcd60e51b815260040161060790615381565b6040516317066a5760e21b81526001600160a01b03831690635c19a95c906132389084906004016152a5565b600060405180830381600087803b15801561325257600080fd5b505af1158015613266573d6000803e3d6000fd5b505050505050565b600c546001600160a01b031633146132985760405162461bcd60e51b81526004016106079061576c565b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b600654600160a01b900460ff1690565b6132d2614cc0565b506001600160a01b038116600090815260096020908152604091829020825160e081018452815460ff80821615158084526101008304821615159584019590955264ffffffffff62010000830416958301959095526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490931660a08401526001015460c083015261337e5760405162461bcd60e51b81526004016106079061569d565b80602001516111585760405162461bcd60e51b8152600401610607906156c4565b60008282028315806133b95750828482816133b657fe5b04145b6133d55760405162461bcd60e51b81526004016106079061579b565b6706f05b59d3b200008101818110156134005760405162461bcd60e51b81526004016106079061579b565b6000670de0b6b3a7640000825b049695505050505050565b6000806134258786613bfe565b9050600061343b670de0b6b3a7640000836135d1565b90506000613449828661339f565b9050600061346887613463670de0b6b3a7640000856135d1565b613bfe565b905060006134768c836135d1565b90506000613484828e613bfe565b9050600061349282886144a9565b905060006134a0828e61339f565b905060006134ae8e836135d1565b90506134cd81613463670de0b6b3a76400006611c37937e080006135d1565b99505050505050505050509695505050505050565b60006060846001600160a01b031663a9059cbb60e01b858560405160240161350b9291906152dd565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516135499190615289565b6000604051808303816000865af19150503d8060008114613586576040519150601f19603f3d011682016040523d82523d6000602084013e61358b565b606091505b50915091508180156135b55750805115806135b55750808060200190518101906135b591906151cc565b610b945760405162461bcd60e51b8152600401610607906158b3565b60008060006135e0858561455c565b9150915080156136025760405162461bcd60e51b8152600401610607906158dc565b509392505050565b81608001516001600160601b031682606001516001600160601b031611158061363557508160200151155b8061364e5750610e10826040015164ffffffffff164203105b1561365857613798565b6060820151608083015160006136816001600160601b0384166064670de0b6b3a76400006118a2565b905060006136a1846001600160601b0316846001600160601b03166135d1565b9050818111156136c4576136be846001600160601b0316836135d1565b92508190505b6703782dace9d900006001600160601b038416116136fe57600a54600093506136ed90846135d1565b600a556136f985614581565b613266565b61370a600a54826135d1565b600a556001600160601b038316606087018190526001600160a01b038616600081815260096020526040908190208054600160381b600160981b031916600160381b9094029390931766ffffffffff00001916620100004264ffffffffff1602179092559051600080516020615b128339815191529061378b908690615a91565b60405180910390a2505050505b5050565b61379882826148c3565b6137af816148ce565b50565b6137988282614977565b6137c4614cc0565b506001600160a01b0381166000908152600960209081526040808320815160e081018352815460ff80821615158084526101008304821615159684019690965264ffffffffff62010000830416948301949094526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490921660a08301526001015460c082015291906138715760405162461bcd60e51b81526004016106079061569d565b5060c08101516020820151613905576001600160a01b0383166000908152600b602052604081205460c084018190526138b8906138ae90846135d1565b8460c00151613bfe565b905060006138dc600a6004670de0b6b3a76400005b04816138d557fe5b048361339f565b90506138f46004670de0b6b3a76400005b0482613acf565b6001600160601b0316606085015250505b915091565b6000806139178787613bfe565b905060006139258686613bfe565b905060006139338383613bfe565b90506000613955670de0b6b3a7640000613463670de0b6b3a7640000896135d1565b9050613961828261339f565b9a9950505050505050505050565b600354600260001961010060018416150201909116041580156139925750815115155b801561399e5750805115155b6139ba5760405162461bcd60e51b81526004016106079061561d565b81516139cd906003906020850190614cfc565b5080516139e1906004906020840190614cfc565b505050565b6001600160a01b038316600090815260208190526040902054811115613a1e5760405162461bcd60e51b815260040161060790615547565b6001600160a01b038316600090815260208190526040902054613a4190826135d1565b6001600160a01b038085166000908152602081905260408082209390935590841681522054613a709082613acf565b6001600160a01b0380841660008181526020819052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90613ac2908590615a6c565b60405180910390a3505050565b600082820183811015613af45760405162461bcd60e51b815260040161060790615907565b9392505050565b60006060846001600160a01b03166323b872dd60e01b85308660405160240161350b939291906152b9565b6137af81614982565b600080613b3c8786613bfe565b90506000613b5d8561287a670de0b6b3a76400006611c37937e080006135d1565b90506000613b6b88836135d1565b90506000613b79828a613bfe565b90506000613b9882613b93670de0b6b3a764000088613bfe565b6144a9565b90506000613ba6828e61339f565b90506000613bb48e836135d1565b90506000613bd3613bcd670de0b6b3a76400008a6135d1565b8b61339f565b9050613beb8261287a670de0b6b3a7640000846135d1565b9f9e505050505050505050505050505050565b600081613c1d5760405162461bcd60e51b81526004016106079061585e565b670de0b6b3a76400008302831580613c455750670de0b6b3a7640000848281613c4257fe5b04145b613c615760405162461bcd60e51b81526004016106079061559e565b60028304810181811015613c875760405162461bcd60e51b81526004016106079061559e565b600084828161340d57fe5b8160200151613e45578160c001518110613dee576001600160a01b0383166000818152600b6020908152604080832083905560098252808320805461ff001916610100179055600191860191909152517ff7bb8e57ffdfd9a31e7580ee84f68757f44fb4a8a913f44520d22f2da1c955e59190a26000613d16828460c001516135d1565b90506000613d28828560c00151613bfe565b9050613d406703782dace9d90000612438818461339f565b6001600160601b03908116606086018181526001600160a01b03881660009081526009602052604090208054600160381b600160981b031916600160381b9093029290921766ffffffffff00001916620100004264ffffffffff160217909155600a549051613daf9216613acf565b600a5560608401516040516001600160a01b03871691600080516020615b1283398151915291613ddf9190615a91565b60405180910390a25050613e40565b6000613e016138ae8460c00151846135d1565b90506000613e1a600a6004670de0b6b3a76400006138cd565b9050613e2f6004670de0b6b3a76400006138ed565b6001600160601b0316606085015250505b613e4f565b613e4f82846149f1565b6001600160a01b0390921660009081526009602052604090206001019190915550565b6001600160a01b0382166000908152600960205260409020805460ff16613eab5760405162461bcd60e51b81526004016106079061569d565b6703782dace9d900006001600160601b038316101580613ed257506001600160601b038216155b613eee5760405162461bcd60e51b815260040161060790615744565b68015af1d78b58c400006001600160601b0383161115613f205760405162461bcd60e51b8152600401610607906154a1565b80546bffffffffffffffffffffffff60981b1916600160981b6001600160601b038416021781556040516001600160a01b038416907fc7ea88f3376e27ce6ebc2025310023327f743a8377d438258c36b166dd8b298390613f82908590615a91565b60405180910390a2505050565b600080613f9c8786613bfe565b90506000613fbb613fb5670de0b6b3a7640000846135d1565b8561339f565b90506000613fd58661287a670de0b6b3a7640000856135d1565b90506000613fe38b83613acf565b90506000613ff1828d613bfe565b90506000613fff82876144a9565b9050600061400d828d61339f565b9050614019818d6135d1565b9e9d5050505050505050505050505050565b6000806140388786613bfe565b905060006140468786613acf565b905060006140548289613bfe565b9050600061406a670de0b6b3a764000085613bfe565b9050600061407883836144a9565b90506000614086828e61339f565b90506000614094828f6135d1565b905060006140ad613bcd670de0b6b3a76400008a6135d1565b9050613beb82613463670de0b6b3a7640000846135d1565b6000806140d28588613bfe565b905060006140e087866135d1565b905060006140ee8883613bfe565b905060006140fc82856144a9565b905061411081670de0b6b3a76400006135d1565b9050614124670de0b6b3a7640000876135d1565b94506141396141338c8361339f565b86613bfe565b9b9a5050505050505050505050565b6000806141558786613bfe565b9050600061416b670de0b6b3a7640000856135d1565b9050614177858261339f565b905060006141898a6134638c85613acf565b9050600061419782856144a9565b905060006141ad670de0b6b3a7640000836135d1565b90506141b98a8261339f565b9c9b505050505050505050505050565b6001600160a01b03831660009081526009602052604090205460ff16156142025760405162461bcd60e51b8152600401610607906153d5565b6703782dace9d900006001600160601b03821610156142335760405162461bcd60e51b815260040161060790615744565b68015af1d78b58c400006001600160601b03821611156142655760405162461bcd60e51b8152600401610607906154a1565b620f42408210156142885760405162461bcd60e51b81526004016106079061551e565b6040518060e00160405280600115158152602001600015158152602001600064ffffffffff16815260200160006001600160601b03168152602001826001600160601b0316815260200160088054905060ff168152602001600081525060096000856001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff02191690831515021790555060408201518160000160026101000a81548164ffffffffff021916908364ffffffffff16021790555060608201518160000160076101000a8154816001600160601b0302191690836001600160601b0316021790555060808201518160000160136101000a8154816001600160601b0302191690836001600160601b0316021790555060a082015181600001601f6101000a81548160ff021916908360ff16021790555060c082015181600101559050506008839080600181540180825580915050600190039060005260206000200160009091909190916101000a8154816001600160a01b0302191690836001600160a01b0316021790555081600b6000856001600160a01b03166001600160a01b0316815260200190815260200160002081905550826001600160a01b03167fb2daf560899f6307b318aecfb57eb2812c488da4a4c1cad2019b482fa63294ed8284604051613f82929190615aa5565b600060018310156144cc5760405162461bcd60e51b815260040161060790615884565b671bc16d674ec7ffff8311156144f45760405162461bcd60e51b8152600401610607906159ac565b60006144ff83614b62565b9050600061450d84836135d1565b905060006145238661451e85614b7d565b614b8b565b905081614534579250610a0d915050565b600061454587846305f5e100614be2565b9050614551828261339f565b979650505050505050565b600080828410614572575050808203600061457a565b505081810360015b9250929050565b614589614cc0565b506001600160a01b038116600090815260096020908152604091829020825160e081018452815460ff808216151583526101008204811615159483019490945264ffffffffff62010000820416948201949094526001600160601b03600160381b850481166060830152600160981b8504166080820152600160f81b90930490911660a0830181905260019091015460c08301819052600854909190600019018082146146e7576008818154811061463d57fe5b600091825260209091200154600880546001600160a01b03909216918490811061466357fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508160096000600885815481106146a357fe5b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff92909216600160f81b026001600160f81b039092169190911790555b60088054806146f257fe5b60008281526020808220600019908401810180546001600160a01b03191690559092019092556040805160e081018252838152808301848152818301858152606083018681526080840187815260a0850188815260c086018981526001600160a01b038f81168b52600990995296909820945185549451935192519151985160ff199095169015151761ff001916610100931515939093029290921766ffffffffff000019166201000064ffffffffff9092169190910217600160381b600160981b031916600160381b6001600160601b0392831602176bffffffffffffffffffffffff60981b1916600160981b9190961602949094176001600160f81b0316600160f81b60ff90951694909402939093178355516001929092019190915560065461482191879116856134e2565b6006546040516360b8257960e11b81526001600160a01b039091169063c1704af29061485390889087906004016152dd565b600060405180830381600087803b15801561486d57600080fd5b505af1158015614881573d6000803e3d6000fd5b505050507f12a8262eb28ee8a8c11e6cf411b3af6ce5bea42abb36e051bf0a65ae602d52ec856040516148b491906152a5565b60405180910390a15050505050565b6137988230836139e6565b306000908152602081905260409020548111156148fd5760405162461bcd60e51b815260040161060790615547565b3060009081526020819052604090205461491790826135d1565b3060009081526020819052604090205560025461493490826135d1565b60025560405160009030907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061496c908590615a6c565b60405180910390a350565b6137983083836139e6565b3060009081526020819052604090205461499c9082613acf565b306000908152602081905260409020556002546149b99082613acf565b60025560405130906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061496c908590615a6c565b81608001516001600160601b031682606001516001600160601b0316101580614a1c57508160200151155b80614a355750610e10826040015164ffffffffff164203105b15614a3f57613798565b606082015160808301516000614a686001600160601b0384166064670de0b6b3a76400006118a2565b90506000614a88836001600160601b0316856001600160601b03166135d1565b905081811115614aab57614aa5846001600160601b031683613acf565b92508190505b614ab7600a5482613acf565b600a819055680168d28e3f002800001015614ae45760405162461bcd60e51b8152600401610607906154c9565b6001600160601b038316606087018190526001600160a01b038616600081815260096020526040908190208054600160381b600160981b031916600160381b9094029390931766ffffffffff00001916620100004264ffffffffff1602179092559051600080516020615b128339815191529061378b908690615a91565b6000670de0b6b3a7640000614b7683614b7d565b0292915050565b670de0b6b3a7640000900490565b60008060028306614ba457670de0b6b3a7640000614ba6565b835b90506002830492505b8215613af457614bbf848561339f565b93506002830615614bd757614bd4818561339f565b90505b600283049250614baf565b6000828180614bf987670de0b6b3a764000061455c565b9092509050670de0b6b3a764000080600060015b888410614cb1576000670de0b6b3a764000082029050600080614c418a614c3c85670de0b6b3a76400006135d1565b61455c565b91509150614c538761287a848c61339f565b9650614c5f8784613bfe565b965086614c6e57505050614cb1565b8715614c78579315935b8015614c82579315935b8415614c9957614c9286886135d1565b9550614ca6565b614ca38688613acf565b95505b505050600101614c0d565b50909998505050505050505050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10614d3d57805160ff1916838001178555614d6a565b82800160010185558215614d6a579182015b82811115614d6a578251825591602001919060010190614d4f565b50614d76929150614d7a565b5090565b5b80821115614d765760008155600101614d7b565b80356001600160a01b0381168114610a0d57600080fd5b60008083601f840112614db7578182fd5b50813567ffffffffffffffff811115614dce578182fd5b602083019150836020808302850101111561457a57600080fd5b60008083601f840112614df9578182fd5b50813567ffffffffffffffff811115614e10578182fd5b60208301915083602082850101111561457a57600080fd5b600060208284031215614e39578081fd5b613af48383614d8f565b60008060408385031215614e55578081fd5b614e5f8484614d8f565b9150614e6e8460208501614d8f565b90509250929050565b600080600060608486031215614e8b578081fd5b8335614e9681615aee565b92506020840135614ea681615aee565b929592945050506040919091013590565b600080600080600060608688031215614ece578081fd5b8535614ed981615aee565b9450602086013567ffffffffffffffff80821115614ef5578283fd5b614f0189838a01614de8565b90965094506040880135915080821115614f19578283fd5b50614f2688828901614de8565b969995985093965092949392505050565b60008060408385031215614f49578182fd5b614f538484614d8f565b946020939093013593505050565b600080600080600060a08688031215614f78578081fd5b614f828787614d8f565b945060208601359350614f988760408801614d8f565b94979396509394606081013594506080013592915050565b600080600060608486031215614fc4578283fd5b614fce8585614d8f565b95602085013595506040909401359392505050565b600080600080600080600080600060c08a8c031215615000578384fd5b893567ffffffffffffffff80821115615017578586fd5b6150238d838e01614da6565b909b50995060208c013591508082111561503b578586fd5b6150478d838e01614da6565b909950975060408c013591508082111561505f578586fd5b5061506c8c828d01614da6565b90965094505060608a013561508081615aee565b925060808a013561509081615aee565b915060a08a01356150a081615aee565b809150509295985092959850929598565b600080600080604085870312156150c6578384fd5b843567ffffffffffffffff808211156150dd578586fd5b6150e988838901614da6565b90965094506020870135915080821115615101578384fd5b5061510e87828801614da6565b95989497509550505050565b60008060008060008060608789031215615132578384fd5b863567ffffffffffffffff80821115615149578586fd5b6151558a838b01614da6565b9098509650602089013591508082111561516d578586fd5b6151798a838b01614da6565b90965094506040890135915080821115615191578384fd5b5061519e89828a01614da6565b979a9699509497509295939492505050565b6000602082840312156151c1578081fd5b8135613af481615b03565b6000602082840312156151dd578081fd5b8151613af481615b03565b6000602082840312156151f9578081fd5b5035919050565b600060208284031215615211578081fd5b5051919050565b60008060006040848603121561522c578081fd5b83359250602084013567ffffffffffffffff811115615249578182fd5b61525586828701614da6565b9497909650939450505050565b600060208284031215615273578081fd5b81356001600160601b0381168114613af4578182fd5b6000825161529b818460208701615abe565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6020808252825182820181905260009190848201906040850190845b818110156153375783516001600160a01b031683529284019291840191600101615312565b50909695505050505050565b901515815260200190565b600060208252825180602084015261536d816040850160208701615abe565b601f01601f19169190910160400192915050565b60208082526012908201527122a9292fa727aa2fa1a7a72a2927a62622a960711b604082015260600190565b6020808252600e908201526d4552525f4e4f4e455f524541445960901b604082015260600190565b6020808252600c908201526b11549497d254d7d093d5539160a21b604082015260600190565b6020808252600e908201526d4552525f4d41585f544f4b454e5360901b604082015260600190565b60208082526010908201526f4552525f4d41585f494e5f524154494f60801b604082015260600190565b60208082526011908201527022a9292fa6a0aa242fa0a8282927ac2f9960791b604082015260600190565b6020808252600f908201526e08aa4a4be9a82a890be82a0a0a49eb608b1b604082015260600190565b6020808252600e908201526d11549497d3505617d5d15251d21560921b604082015260600190565b60208082526014908201527311549497d3505617d513d5105317d5d15251d21560621b604082015260600190565b6020808252600d908201526c11549497d31253525517d3d555609a1b604082015260600190565b6020808252600f908201526e4552525f4d494e5f42414c414e434560881b604082015260600190565b60208082526014908201527311549497d25394d551919250d251539517d0905360621b604082015260600190565b6020808252600f908201526e11549497d253925512505312569151608a1b604082015260600190565b60208082526010908201526f11549497d1125597d25395115493905360821b604082015260600190565b60208082526015908201527422a9292fa12a27a5a2a72fa120a22fa1a0a62622a960591b604082015260600190565b6020808252600c908201526b22a9292fa624a6a4aa2fa4a760a11b604082015260600190565b60208082526016908201527511549497d09513d2d15397d25392551250531256915160521b604082015260600190565b6020808252600b908201526a4552525f5245454e54525960a81b604082015260600190565b6020808252601190820152704552525f4d41585f4f55545f524154494f60781b604082015260600190565b6020808252600d908201526c11549497d393d517d093d55391609a1b604082015260600190565b6020808252601190820152704552525f4f55545f4e4f545f524541445960781b604082015260600190565b6020808252600e908201526d4552525f4e4f545f5055424c494360901b604082015260600190565b6020808252601390820152724552525f4241445f4c494d49545f505249434560681b604082015260600190565b6020808252600e908201526d11549497d3525397d5d15251d21560921b604082015260600190565b60208082526015908201527411549497d393d517d1915157d49150d25412515395605a1b604082015260600190565b60208082526010908201526f4552525f4d554c5f4f564552464c4f5760801b604082015260600190565b6020808252600b908201526a22a9292fa0a9292fa622a760a91b604082015260600190565b6020808252600990820152684552525f524541445960b81b604082015260600190565b6020808252600f908201526e4552525f4c494d49545f505249434560881b604082015260600190565b6020808252600e908201526d11549497d0d3d3919251d554915160921b604082015260600190565b6020808252600c908201526b4552525f4449565f5a45524f60a01b604082015260600190565b6020808252601590820152744552525f42504f575f424153455f544f4f5f4c4f5760581b604082015260600190565b6020808252600f908201526e4552525f45524332305f46414c534560881b604082015260600190565b6020808252601190820152704552525f5355425f554e444552464c4f5760781b604082015260600190565b60208082526010908201526f4552525f4144445f4f564552464c4f5760801b604082015260600190565b60208082526010908201526f4552525f4e554c4c5f4144445245535360801b604082015260600190565b6020808252600f908201526e4552525f494e56414c49445f46454560881b604082015260600190565b6020808252600e908201526d4552525f4d494e5f544f4b454e5360901b604082015260600190565b60208082526016908201527508aa4a4be84a09eaebe8482a68abea89e9ebe90928e960531b604082015260600190565b6020808252600b908201526a22a9292fad22a927afa4a760a91b604082015260600190565b600060e08201905082511515825260208301511515602083015264ffffffffff604084015116604083015260608301516001600160601b038082166060850152806080860151166080850152505060ff60a08401511660a083015260c083015160c083015292915050565b90815260200190565b918252602082015260400190565b60ff91909116815260200190565b6001600160601b0391909116815260200190565b6001600160601b03929092168252602082015260400190565b60005b83811015615ad9578181015183820152602001615ac1565b83811115615ae8576000848401525b50505050565b6001600160a01b03811681146137af57600080fd5b80151581146137af57600080fdfe21b12aed5d425f5675450ffeeae01039085e5323974c3099e1828155d9b51e778c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925a264697066735822122018b2550a0d179afba2d654006db034e06e0e741847346b333f104cd911ac2cd264736f6c634300060c0033

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061028a5760003560e01c80637c5e9ea41161015c578063a9059cbb116100ce578063d73dd62311610087578063d73dd6231461057a578063dd62ed3e1461058d578063f8b2cb4f146105a0578063f8b5db09146105b3578063f9f97c98146105c6578063fde924f7146105d95761028a565b8063a9059cbb14610529578063b02f0b731461053c578063c3f468101461054f578063cc77828d14610562578063cd2ed8fb1461056a578063d4cadf68146105725761028a565b806391bfa2bf1161012057806391bfa2bf146104ca578063936c3477146104dd578063948d8ce6146104e557806395d89b41146104f857806398836f0814610500578063a49c44d7146105165761028a565b80637c5e9ea4146104735780638025e303146104945780638201aa3f1461049c578063865bcccb146104af5780638c28cbe8146104b75761028a565b8063313ce567116102005780635d5e8ce7116101b95780635d5e8ce7146103f45780635db342771461040757806364c7d6611461041a578063661884631461043a5780636d06dfa01461044d57806370a08231146104605761028a565b8063313ce5671461038057806334e199071461039557806346ab38f1146103a857806349b59552146103bb5780634aa4e0b5146103ce5780634f69c0d4146103e15761028a565b806318160ddd1161025257806318160ddd1461031557806319f0f8491461031d57806323b872dd146103325780632b110c74146103455780632f37b624146103585780633018205f1461036b5761028a565b806302c967481461028f578063039209af146102b857806306fdde03146102cd578063095ea7b3146102e257806315e84af914610302575b600080fd5b6102a261029d366004614fb0565b6105e1565b6040516102af9190615a6c565b60405180910390f35b6102c06107bd565b6040516102af91906152f6565b6102d5610924565b6040516102af919061534e565b6102f56102f0366004614f37565b6109ba565b6040516102af9190615343565b6102a2610310366004614e43565b610a13565b6102a2610a9a565b61033061032b366004614eb7565b610aa0565b005b6102f5610340366004614e77565b610b9b565b610330610353366004614fe3565b610ccc565b6102f5610366366004614e28565b61113b565b61037361115d565b6040516102af91906152a5565b610388611171565b6040516102af9190615a83565b6103306103a33660046151e8565b611176565b6102a26103b6366004614fb0565b61121e565b6103306103c93660046151b0565b61139c565b6102a26103dc366004614e28565b611412565b6103306103ef366004615218565b61151f565b6103306104023660046150b1565b611718565b6102a2610415366004614fb0565b6117f4565b61042d610428366004614e28565b61199c565b6040516102af9190615a01565b6102f5610448366004614f37565b611a73565b6102a261045b366004614fb0565b611b3c565b6102a261046e366004614e28565b611cc6565b610486610481366004614f61565b611ce1565b6040516102af929190615a75565b6102a2611fd8565b6104866104aa366004614f61565b611fdd565b6103736122b0565b6103306104c5366004614e28565b6122bf565b6102a26104d8366004614e28565b612586565b6102a261269f565b6102a26104f3366004614e28565b6126cc565b6102d5612756565b6105086127b7565b6040516102af9291906152dd565b610330610524366004614f37565b6128bb565b6102f5610537366004614f37565b6129a3565b61033061054a366004615218565b6129b9565b61033061055d36600461511a565b612c84565b6102c061301d565b6102a26130a2565b6102a26130a8565b6102f5610588366004614f37565b6130d5565b6102a261059b366004614e43565b613149565b6102a26105ae366004614e28565b613174565b6103306105c1366004614e43565b6131dd565b6103306105d4366004614e28565b61326e565b6102f56132ba565b60055460009060ff16156106105760405162461bcd60e51b81526004016106079061564d565b60405180910390fd5b6005805460ff19166001179055610625614cc0565b61062e856132ca565b90506106538160c001516003670de0b6b3a76400008161064a57fe5b0460010161339f565b8411156106725760405162461bcd60e51b815260040161060790615672565b60006106998260c0015183606001516001600160601b0316600254600a5489600754613418565b9050806106b85760405162461bcd60e51b815260040161060790615478565b838111156106d85760405162461bcd60e51b8152600401610607906155f7565b6106e38633876134e2565b6106f18260c00151866135d1565b6001600160a01b038716600090815260096020526040902060010155610717828761360a565b600061072a826611c37937e0800061339f565b9050866001600160a01b0316336001600160a01b03167fe74c91552b64c2e2e7bd255639e004e693bd3e1d01cc33e65610b86afcc1ffed8860405161076f9190615a6c565b60405180910390a3610781338361379c565b61079361078e83836135d1565b6137a6565b600c546107a9906001600160a01b0316826137b2565b506005805460ff1916905595945050505050565b60055460609060ff16156107e35760405162461bcd60e51b81526004016106079061564d565b6060600880548060200260200160405190810160405280929190818152602001828054801561083b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161081d575b50505050509050805167ffffffffffffffff8111801561085a57600080fd5b50604051908082528060200260200182016040528015610884578160200160208202803683370190505b5091506000805b835181101561091d5760008382815181106108a257fe5b6020908102919091018101516001600160a01b03811660009081526009909252604090912054909150600160981b90046001600160601b03161561091457808584806001019550815181106108f357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b5060010161088b565b5082525090565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156109b05780601f10610985576101008083540402835291602001916109b0565b820191906000526020600020905b81548152906001019060200180831161099357829003601f168201915b5050505050905090565b3360008181526001602090815260408083206001600160a01b03871680855292528083208590555191929091600080516020615b3283398151915290610a01908690615a6c565b60405180910390a35060015b92915050565b60055460009060ff1615610a395760405162461bcd60e51b81526004016106079061564d565b610a41614cc0565b610a4a846137bc565b509050610a55614cc0565b610a5e846132ca565b9050610a918260c0015183606001516001600160601b03168360c0015184606001516001600160601b031660075461390a565b95945050505050565b60025490565b60055461010090046001600160a01b031615610ace5760405162461bcd60e51b815260040161060790615836565b6001600160a01b038516610af45760405162461bcd60e51b815260040161060790615931565b60058054610100600160a81b0319166101006001600160a01b038816021790556028670de0b6b3a764000004600755604080516020601f8601819004810282018101909252848152610b9491869086908190840183828082843760009201919091525050604080516020601f8801819004810282018101909252868152925086915085908190840183828082843760009201919091525061396f92505050565b5050505050565b6000336001600160a01b0385161480610bd757506001600160a01b03841660009081526001602090815260408083203384529091529020548211155b610bf35760405162461bcd60e51b8152600401610607906155c8565b610bfe8484846139e6565b336001600160a01b03851614801590610c3c57506001600160a01b038416600090815260016020908152604080832033845290915290205460001914155b15610cc2576001600160a01b0384166000908152600160209081526040808320338452909152902054610c6f90836135d1565b6001600160a01b038581166000908152600160209081526040808320338085529252918290208490559051918616929091600080516020615b3283398151915291610cb991615a6c565b60405180910390a35b5060019392505050565b60055461010090046001600160a01b03163314610cfb5760405162461bcd60e51b815260040161060790615381565b60085415610d1b5760405162461bcd60e51b815260040161060790615575565b876002811015610d3d5760405162461bcd60e51b815260040161060790615984565b600a811115610d5e5760405162461bcd60e51b8152600401610607906153fb565b8681148015610d6c57508481145b610d885760405162461bcd60e51b8152600401610607906157c5565b6000805b828110156110645760008c8c83818110610da257fe5b9050602002016020810190610db79190614e28565b90506000898984818110610dc757fe5b9050602002016020810190610ddc9190615262565b905060008c8c85818110610dec57fe5b9050602002013590506004670de0b6b3a764000081610e0757fe5b04826001600160601b03161015610e305760405162461bcd60e51b815260040161060790615744565b68015af1d78b58c400006001600160601b0383161115610e625760405162461bcd60e51b8152600401610607906154a1565b620f4240811015610e855760405162461bcd60e51b81526004016106079061551e565b6040518060e001604052806001151581526020016001151581526020014264ffffffffff168152602001836001600160601b03168152602001836001600160601b031681526020018560ff1681526020018281525060096000856001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff02191690831515021790555060408201518160000160026101000a81548164ffffffffff021916908364ffffffffff16021790555060608201518160000160076101000a8154816001600160601b0302191690836001600160601b0316021790555060808201518160000160136101000a8154816001600160601b0302191690836001600160601b0316021790555060a082015181600001601f6101000a81548160ff021916908360ff16021790555060c082015181600101559050506008839080600181540180825580915050600190039060005260206000200160009091909190916101000a8154816001600160a01b0302191690836001600160a01b0316021790555061104c85836001600160601b0316613acf565b9450611059838a83613afb565b505050600101610d8c565b50680168d28e3f0028000081111561108e5760405162461bcd60e51b8152600401610607906154c9565b600a8190556006805460ff60a01b1916600160a01b1790556040517f40fc85fbff9305015298ba6fcee88b7e442a64cc803ddb889327680bbd62270a906110d790600190615343565b60405180910390a16110f168056bc75e2d63100000613b26565b6111048568056bc75e2d631000006137b2565b5050600680546001600160a01b039384166001600160a01b031991821617909155600c805492909316911617905550505050505050565b6001600160a01b03811660009081526009602052604090205460ff165b919050565b60055461010090046001600160a01b031690565b601290565b60055461010090046001600160a01b031633146111a55760405162461bcd60e51b815260040161060790615381565b64e8d4a5100081108015906111c2575067016345785d8a00008111155b6111de5760405162461bcd60e51b81526004016106079061595b565b60078190556040517fccfe595973efc7c1f6c29e31974d380470b9431d7770290185b7129419c7e63e90611213908390615a6c565b60405180910390a150565b60055460009060ff16156112445760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055611259614cc0565b611262856132ca565b9050600061128b8260c0015183606001516001600160601b0316600254600a5489600754613b2f565b9050838110156112ad5760405162461bcd60e51b8152600401610607906154f7565b6112c78260c001516003670de0b6b3a76400008161064a57fe5b8111156112e65760405162461bcd60e51b815260040161060790615672565b6112f18633836134e2565b6112ff8260c00151826135d1565b6001600160a01b038716600090815260096020526040902060010155611325828761360a565b6000611338866611c37937e0800061339f565b9050866001600160a01b0316336001600160a01b03167fe74c91552b64c2e2e7bd255639e004e693bd3e1d01cc33e65610b86afcc1ffed8460405161137d9190615a6c565b60405180910390a361138f338761379c565b61079361078e87836135d1565b60055461010090046001600160a01b031633146113cb5760405162461bcd60e51b815260040161060790615381565b6006805460ff60a01b1916600160a01b831515021790556040517f40fc85fbff9305015298ba6fcee88b7e442a64cc803ddb889327680bbd62270a90611213908390615343565b60055460009060ff16156114385760405162461bcd60e51b81526004016106079061564d565b611440614cc0565b506001600160a01b038216600090815260096020908152604091829020825160e081018452815460ff80821615158084526101008304821615159584019590955264ffffffffff62010000830416958301959095526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490931660a08401526001015460c08301526114ec5760405162461bcd60e51b81526004016106079061569d565b80602001516115155750506001600160a01b0381166000908152600b6020526040902054611158565b60c0015192915050565b60055460ff16156115425760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055600654600160a01b900460ff166115785760405162461bcd60e51b8152600401610607906156ef565b6000611582610a9a565b905060006115908583613bfe565b9050806115af5760405162461bcd60e51b815260040161060790615478565b60085483146115d05760405162461bcd60e51b8152600401610607906157c5565b60005b838110156116f3576000600882815481106115ea57fe5b6000918252602090912001546001600160a01b03169050611609614cc0565b6000611614836137bc565b915091506000611628868460c0015161339f565b9050806116475760405162461bcd60e51b815260040161060790615478565b88888681811061165357fe5b905060200201358111156116795760405162461bcd60e51b8152600401610607906155f7565b61168d84846116888585613acf565b613c92565b836001600160a01b0316336001600160a01b03167f63982df10efd8dfaaaa0fcc7f50b2d93b7cba26ccc48adee2873220d485dc39a836040516116d09190615a6c565b60405180910390a36116e3843383613afb565b5050600190920191506115d39050565b506116fd85613b26565b61170733866137b2565b50506005805460ff19169055505050565b60055460ff161561173b5760405162461bcd60e51b81526004016106079061564d565b60058054600160ff19909116179081905561010090046001600160a01b031633146117785760405162461bcd60e51b815260040161060790615381565b8083146117975760405162461bcd60e51b8152600401610607906157c5565b60005b83811015611707576117ec8585838181106117b157fe5b90506020020160208101906117c69190614e28565b8484848181106117d257fe5b90506020020160208101906117e79190615262565b613e72565b60010161179a565b60055460009060ff161561181a5760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055600654600160a01b900460ff166118505760405162461bcd60e51b8152600401610607906156ef565b611858614cc0565b6000611863866137bc565b9150915084600014156118885760405162461bcd60e51b8152600401610607906159dc565b6118a88260c001516002670de0b6b3a7640000816118a257fe5b0461339f565b8511156118c75760405162461bcd60e51b815260040161060790615423565b60006118ee8360c0015184606001516001600160601b0316600254600a548a600754613f8f565b9050848110156119105760405162461bcd60e51b8152600401610607906154f7565b61191f8784611688858a613acf565b866001600160a01b0316336001600160a01b03167f63982df10efd8dfaaaa0fcc7f50b2d93b7cba26ccc48adee2873220d485dc39a886040516119629190615a6c565b60405180910390a361197381613b26565b61197d33826137b2565b611988873388613afb565b6005805460ff191690559695505050505050565b6119a4614cc0565b60055460ff16156119c75760405162461bcd60e51b81526004016106079061564d565b506001600160a01b038116600090815260096020908152604091829020825160e081018452815460ff80821615158084526101008304821615159584019590955264ffffffffff62010000830416958301959095526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490931660a08401526001015460c08301526111585760405162461bcd60e51b81526004016106079061569d565b3360009081526001602090815260408083206001600160a01b038616845290915281205480831115611ac8573360009081526001602090815260408083206001600160a01b0388168452909152812055611af7565b611ad281846135d1565b3360009081526001602090815260408083206001600160a01b03891684529091529020555b3360008181526001602090815260408083206001600160a01b038916808552925291829020549151909291600080516020615b3283398151915291610cb99190615a6c565b60055460009060ff1615611b625760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055600654600160a01b900460ff16611b985760405162461bcd60e51b8152600401610607906156ef565b611ba0614cc0565b6000611bab866137bc565b915091506000611bd68360c0015184606001516001600160601b0316600254600a548a60075461402b565b905080611bf55760405162461bcd60e51b815260040161060790615478565b84811115611c155760405162461bcd60e51b8152600401610607906155f7565b611c2f8360c001516002670de0b6b3a7640000816118a257fe5b811115611c4e5760405162461bcd60e51b815260040161060790615423565b611c5d87846116888585613acf565b866001600160a01b0316336001600160a01b03167f63982df10efd8dfaaaa0fcc7f50b2d93b7cba26ccc48adee2873220d485dc39a83604051611ca09190615a6c565b60405180910390a3611cb186613b26565b611cbb33876137b2565b611988873383613afb565b6001600160a01b031660009081526020819052604090205490565b600554600090819060ff1615611d095760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055600654600160a01b900460ff16611d3f5760405162461bcd60e51b8152600401610607906156ef565b611d47614cc0565b6000611d52896137bc565b91509150611d5e614cc0565b611d67886132ca565b9050611d838160c001516003670de0b6b3a76400008161064a57fe5b871115611da25760405162461bcd60e51b815260040161060790615672565b6000611dd58460c0015185606001516001600160601b03168460c0015185606001516001600160601b031660075461390a565b905086811115611df75760405162461bcd60e51b815260040161060790615717565b6000611e2b8560c0015186606001516001600160601b03168560c0015186606001516001600160601b03168d6007546140c5565b90508a811115611e4d5760405162461bcd60e51b8152600401610607906155f7565b611e588c3383613afb565b611e638a338b6134e2565b611e6d8482613acf565b9350611e7a8c8686613c92565b846020015115611e8c5760c085018490525b611e9a8360c001518a6135d1565b60c084018190526001600160a01b038b16600090815260096020526040902060010155611ec7838b61360a565b6000611efa8660c0015187606001516001600160601b03168660c0015187606001516001600160601b031660075461390a565b905082811015611f1c5760405162461bcd60e51b815260040161060790615478565b88811115611f3c5760405162461bcd60e51b81526004016106079061580d565b611f46828b613bfe565b831115611f655760405162461bcd60e51b815260040161060790615478565b8a6001600160a01b03168d6001600160a01b0316336001600160a01b03167f908fb5ee8f16c6bc9bc3690973819f32a4d4b10188134543c88706e0e1d43378858e604051611fb4929190615a75565b60405180910390a46005805460ff19169055909c909b509950505050505050505050565b600181565b600554600090819060ff16156120055760405162461bcd60e51b81526004016106079061564d565b6005805460ff19166001179055600654600160a01b900460ff1661203b5760405162461bcd60e51b8152600401610607906156ef565b612043614cc0565b600061204e896137bc565b9150915061205a614cc0565b612063886132ca565b905061207f8360c001516002670de0b6b3a7640000816118a257fe5b89111561209e5760405162461bcd60e51b815260040161060790615423565b60006120d18460c0015185606001516001600160601b03168460c0015185606001516001600160601b031660075461390a565b9050868111156120f35760405162461bcd60e51b815260040161060790615717565b60006121278560c0015186606001516001600160601b03168560c0015186606001516001600160601b03168f600754614148565b9050888110156121495760405162461bcd60e51b8152600401610607906154f7565b6121548c338d613afb565b61215f8a33836134e2565b612169848c613acf565b93506121768c8686613c92565b8460200151156121885760c085018490525b6121968360c00151826135d1565b60c084018190526001600160a01b038b166000908152600960205260409020600101556121c3838b61360a565b60006121f68660c0015187606001516001600160601b03168660c0015187606001516001600160601b031660075461390a565b9050828110156122185760405162461bcd60e51b81526004016106079061544d565b888111156122385760405162461bcd60e51b81526004016106079061580d565b6122428c83613bfe565b8311156122615760405162461bcd60e51b815260040161060790615478565b8a6001600160a01b03168d6001600160a01b0316336001600160a01b03167f908fb5ee8f16c6bc9bc3690973819f32a4d4b10188134543c88706e0e1d433788f86604051611fb4929190615a75565b600c546001600160a01b031690565b60055460ff16156122e25760405162461bcd60e51b81526004016106079061564d565b6005805460ff191660011790556001600160a01b03811660008181526009602052604080822090516370a0823160e01b81529092906370a082319061232b9030906004016152a5565b60206040518083038186803b15801561234357600080fd5b505afa158015612357573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061237b9190615200565b825490915060ff16156124fa578154610100900460ff166124d7576001600160a01b0383166000908152600b60205260409020548082106124d5576001600160a01b0384166000818152600b6020526040808220829055855461ff001916610100178655517ff7bb8e57ffdfd9a31e7580ee84f68757f44fb4a8a913f44520d22f2da1c955e59190a2600061241083836135d1565b9050600061241e8284613bfe565b9050600061243d6703782dace9d90000612438818561339f565b613acf565b8654600160381b600160981b031916600160381b6001600160601b0383169081029190911766ffffffffff00001916620100004264ffffffffff1602178855600a5491925061248c9190613acf565b600a5585546040516001600160a01b03891691600080516020615b12833981519152916124c991600160381b90046001600160601b031690615a91565b60405180910390a25050505b505b6001600160a01b0383166000908152600960205260409020600101819055612577565b6006546125129084906001600160a01b0316836134e2565b6006546040516360b8257960e11b81526001600160a01b039091169063c1704af29061254490869085906004016152dd565b600060405180830381600087803b15801561255e57600080fd5b505af1158015612572573d6000803e3d6000fd5b505050505b50506005805460ff1916905550565b60055460009060ff16156125ac5760405162461bcd60e51b81526004016106079061564d565b6125b4614cc0565b506001600160a01b038216600090815260096020908152604091829020825160e081018452815460ff80821615158084526101008304821615159584019590955264ffffffffff62010000830416958301959095526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490931660a08401526001015460c08301526126605760405162461bcd60e51b81526004016106079061569d565b8060200151156126825760405162461bcd60e51b8152600401610607906157ea565b50506001600160a01b03166000908152600b602052604090205490565b60055460009060ff16156126c55760405162461bcd60e51b81526004016106079061564d565b50600a5490565b60055460009060ff16156126f25760405162461bcd60e51b81526004016106079061564d565b6001600160a01b03821660009081526009602052604090205460ff1661272a5760405162461bcd60e51b81526004016106079061569d565b506001600160a01b0316600090815260096020526040902054600160381b90046001600160601b031690565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156109b05780601f10610985576101008083540402835291602001916109b0565b600554600090819060ff16156127df5760405162461bcd60e51b81526004016106079061564d565b6008546000908190815b8181101561289057600881815481106127fe57fe5b60009182526020808320909101546001600160a01b03168083526009909152604090912080549195509060ff61010090910416801561284d57508054600160981b90046001600160601b031615155b15612887576001810154600a54825461287f929161287a91600160381b90046001600160601b0316613bfe565b61339f565b935050612890565b506001016127e9565b50600082116128b15760405162461bcd60e51b8152600401610607906153ad565b5090925090509091565b60055461010090046001600160a01b031633146128ea5760405162461bcd60e51b815260040161060790615381565b6001600160a01b0382166000908152600960205260409020805460ff166129235760405162461bcd60e51b81526004016106079061569d565b8054610100900460ff161561294a5760405162461bcd60e51b8152600401610607906157ea565b6001600160a01b0383166000908152600b602052604090819020839055517e0c7a55677231b335e6dea005fa240ac2aeafbd62f188372a7d66892b722c529061299690859085906152dd565b60405180910390a1505050565b60006129b03384846139e6565b50600192915050565b60055460ff16156129dc5760405162461bcd60e51b81526004016106079061564d565b6005805460ff191660011790556008548114612a0a5760405162461bcd60e51b8152600401610607906157c5565b6000612a14610a9a565b90506000612a29856611c37937e0800061339f565b90506000612a3786836135d1565b90506000612a458285613bfe565b905080612a645760405162461bcd60e51b815260040161060790615478565b612a6e338861379c565b600c54612a84906001600160a01b0316846137b2565b612a8d826137a6565b60005b85811015612c7057600060088281548110612aa757fe5b6000918252602090912001546001600160a01b03169050612ac6614cc0565b506001600160a01b038116600090815260096020908152604091829020825160e081018452815460ff80821615158352610100820481161580159584019590955264ffffffffff62010000830416958301959095526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490931660a08401526001015460c0830152612c34576000612b6a858360c0015161339f565b905080612b895760405162461bcd60e51b815260040161060790615478565b898985818110612b9557fe5b90506020020135811015612bbb5760405162461bcd60e51b8152600401610607906154f7565b612bc98260c00151826135d1565b6001600160a01b0384166000818152600960205260409081902060010192909255905133907fe74c91552b64c2e2e7bd255639e004e693bd3e1d01cc33e65610b86afcc1ffed90612c1b908590615a6c565b60405180910390a3612c2e8333836134e2565b50612c66565b888884818110612c4057fe5b90506020020135600014612c665760405162461bcd60e51b8152600401610607906156c4565b5050600101612a90565b50506005805460ff19169055505050505050565b60055460ff1615612ca75760405162461bcd60e51b81526004016106079061564d565b60058054600160ff19909116179081905561010090046001600160a01b03163314612ce45760405162461bcd60e51b815260040161060790615381565b8285148015612cf257508085145b612d0e5760405162461bcd60e51b8152600401610607906157c5565b60085460608167ffffffffffffffff81118015612d2a57600080fd5b50604051908082528060200260200182016040528015612d54578160200160208202803683370190505b50905060608767ffffffffffffffff81118015612d7057600080fd5b50604051908082528060200260200182016040528015612daa57816020015b612d97614cc0565b815260200190600190039081612d8f5790505b50905060005b88811015612ee557600960008b8b84818110612dc857fe5b9050602002016020810190612ddd9190614e28565b6001600160a01b031681526020808201929092526040908101600020815160e081018352815460ff808216151583526101008204811615159583019590955264ffffffffff62010000820416938201939093526001600160601b03600160381b840481166060830152600160981b8404166080820152600160f81b90920490921660a082015260019091015460c08201528251839083908110612e7c57fe5b6020026020010181905250818181518110612e9357fe5b60200260200101516000015115612edd57600183838381518110612eb357fe5b602002602001015160a0015160ff1681518110612ecc57fe5b911515602092830291909101909101525b600101612db0565b5060005b83811015612f3b57828181518110612efd57fe5b6020026020010151612f3357612f3360088281548110612f1957fe5b60009182526020822001546001600160a01b031690613e72565b600101612ee9565b5060005b888110156130075760008a8a83818110612f5557fe5b9050602002016020810190612f6a9190614e28565b90506000898984818110612f7a57fe5b9050602002016020810190612f8f9190615262565b90506703782dace9d900006001600160601b0382161015612fb557506703782dace9d900005b838381518110612fc157fe5b602002602001015160000151612ff357612fee82898986818110612fe157fe5b90506020020135836141c9565b612ffd565b612ffd8282613e72565b5050600101612f3f565b50506005805460ff191690555050505050505050565b60055460609060ff16156130435760405162461bcd60e51b81526004016106079061564d565b60088054806020026020016040519081016040528092919081815260200182805480156109b057602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161307b575050505050905090565b60085490565b60055460009060ff16156130ce5760405162461bcd60e51b81526004016106079061564d565b5060075490565b3360009081526001602090815260408083206001600160a01b03861684529091528120546131039083613acf565b3360008181526001602090815260408083206001600160a01b03891680855292529182902084905590519092600080516020615b3283398151915291610a019190615a6c565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60055460009060ff161561319a5760405162461bcd60e51b81526004016106079061564d565b6001600160a01b0382166000908152600960205260409020805460ff166131d35760405162461bcd60e51b81526004016106079061569d565b6001015492915050565b60055461010090046001600160a01b0316331461320c5760405162461bcd60e51b815260040161060790615381565b6040516317066a5760e21b81526001600160a01b03831690635c19a95c906132389084906004016152a5565b600060405180830381600087803b15801561325257600080fd5b505af1158015613266573d6000803e3d6000fd5b505050505050565b600c546001600160a01b031633146132985760405162461bcd60e51b81526004016106079061576c565b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b600654600160a01b900460ff1690565b6132d2614cc0565b506001600160a01b038116600090815260096020908152604091829020825160e081018452815460ff80821615158084526101008304821615159584019590955264ffffffffff62010000830416958301959095526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490931660a08401526001015460c083015261337e5760405162461bcd60e51b81526004016106079061569d565b80602001516111585760405162461bcd60e51b8152600401610607906156c4565b60008282028315806133b95750828482816133b657fe5b04145b6133d55760405162461bcd60e51b81526004016106079061579b565b6706f05b59d3b200008101818110156134005760405162461bcd60e51b81526004016106079061579b565b6000670de0b6b3a7640000825b049695505050505050565b6000806134258786613bfe565b9050600061343b670de0b6b3a7640000836135d1565b90506000613449828661339f565b9050600061346887613463670de0b6b3a7640000856135d1565b613bfe565b905060006134768c836135d1565b90506000613484828e613bfe565b9050600061349282886144a9565b905060006134a0828e61339f565b905060006134ae8e836135d1565b90506134cd81613463670de0b6b3a76400006611c37937e080006135d1565b99505050505050505050509695505050505050565b60006060846001600160a01b031663a9059cbb60e01b858560405160240161350b9291906152dd565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516135499190615289565b6000604051808303816000865af19150503d8060008114613586576040519150601f19603f3d011682016040523d82523d6000602084013e61358b565b606091505b50915091508180156135b55750805115806135b55750808060200190518101906135b591906151cc565b610b945760405162461bcd60e51b8152600401610607906158b3565b60008060006135e0858561455c565b9150915080156136025760405162461bcd60e51b8152600401610607906158dc565b509392505050565b81608001516001600160601b031682606001516001600160601b031611158061363557508160200151155b8061364e5750610e10826040015164ffffffffff164203105b1561365857613798565b6060820151608083015160006136816001600160601b0384166064670de0b6b3a76400006118a2565b905060006136a1846001600160601b0316846001600160601b03166135d1565b9050818111156136c4576136be846001600160601b0316836135d1565b92508190505b6703782dace9d900006001600160601b038416116136fe57600a54600093506136ed90846135d1565b600a556136f985614581565b613266565b61370a600a54826135d1565b600a556001600160601b038316606087018190526001600160a01b038616600081815260096020526040908190208054600160381b600160981b031916600160381b9094029390931766ffffffffff00001916620100004264ffffffffff1602179092559051600080516020615b128339815191529061378b908690615a91565b60405180910390a2505050505b5050565b61379882826148c3565b6137af816148ce565b50565b6137988282614977565b6137c4614cc0565b506001600160a01b0381166000908152600960209081526040808320815160e081018352815460ff80821615158084526101008304821615159684019690965264ffffffffff62010000830416948301949094526001600160601b03600160381b820481166060840152600160981b8204166080830152600160f81b900490921660a08301526001015460c082015291906138715760405162461bcd60e51b81526004016106079061569d565b5060c08101516020820151613905576001600160a01b0383166000908152600b602052604081205460c084018190526138b8906138ae90846135d1565b8460c00151613bfe565b905060006138dc600a6004670de0b6b3a76400005b04816138d557fe5b048361339f565b90506138f46004670de0b6b3a76400005b0482613acf565b6001600160601b0316606085015250505b915091565b6000806139178787613bfe565b905060006139258686613bfe565b905060006139338383613bfe565b90506000613955670de0b6b3a7640000613463670de0b6b3a7640000896135d1565b9050613961828261339f565b9a9950505050505050505050565b600354600260001961010060018416150201909116041580156139925750815115155b801561399e5750805115155b6139ba5760405162461bcd60e51b81526004016106079061561d565b81516139cd906003906020850190614cfc565b5080516139e1906004906020840190614cfc565b505050565b6001600160a01b038316600090815260208190526040902054811115613a1e5760405162461bcd60e51b815260040161060790615547565b6001600160a01b038316600090815260208190526040902054613a4190826135d1565b6001600160a01b038085166000908152602081905260408082209390935590841681522054613a709082613acf565b6001600160a01b0380841660008181526020819052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90613ac2908590615a6c565b60405180910390a3505050565b600082820183811015613af45760405162461bcd60e51b815260040161060790615907565b9392505050565b60006060846001600160a01b03166323b872dd60e01b85308660405160240161350b939291906152b9565b6137af81614982565b600080613b3c8786613bfe565b90506000613b5d8561287a670de0b6b3a76400006611c37937e080006135d1565b90506000613b6b88836135d1565b90506000613b79828a613bfe565b90506000613b9882613b93670de0b6b3a764000088613bfe565b6144a9565b90506000613ba6828e61339f565b90506000613bb48e836135d1565b90506000613bd3613bcd670de0b6b3a76400008a6135d1565b8b61339f565b9050613beb8261287a670de0b6b3a7640000846135d1565b9f9e505050505050505050505050505050565b600081613c1d5760405162461bcd60e51b81526004016106079061585e565b670de0b6b3a76400008302831580613c455750670de0b6b3a7640000848281613c4257fe5b04145b613c615760405162461bcd60e51b81526004016106079061559e565b60028304810181811015613c875760405162461bcd60e51b81526004016106079061559e565b600084828161340d57fe5b8160200151613e45578160c001518110613dee576001600160a01b0383166000818152600b6020908152604080832083905560098252808320805461ff001916610100179055600191860191909152517ff7bb8e57ffdfd9a31e7580ee84f68757f44fb4a8a913f44520d22f2da1c955e59190a26000613d16828460c001516135d1565b90506000613d28828560c00151613bfe565b9050613d406703782dace9d90000612438818461339f565b6001600160601b03908116606086018181526001600160a01b03881660009081526009602052604090208054600160381b600160981b031916600160381b9093029290921766ffffffffff00001916620100004264ffffffffff160217909155600a549051613daf9216613acf565b600a5560608401516040516001600160a01b03871691600080516020615b1283398151915291613ddf9190615a91565b60405180910390a25050613e40565b6000613e016138ae8460c00151846135d1565b90506000613e1a600a6004670de0b6b3a76400006138cd565b9050613e2f6004670de0b6b3a76400006138ed565b6001600160601b0316606085015250505b613e4f565b613e4f82846149f1565b6001600160a01b0390921660009081526009602052604090206001019190915550565b6001600160a01b0382166000908152600960205260409020805460ff16613eab5760405162461bcd60e51b81526004016106079061569d565b6703782dace9d900006001600160601b038316101580613ed257506001600160601b038216155b613eee5760405162461bcd60e51b815260040161060790615744565b68015af1d78b58c400006001600160601b0383161115613f205760405162461bcd60e51b8152600401610607906154a1565b80546bffffffffffffffffffffffff60981b1916600160981b6001600160601b038416021781556040516001600160a01b038416907fc7ea88f3376e27ce6ebc2025310023327f743a8377d438258c36b166dd8b298390613f82908590615a91565b60405180910390a2505050565b600080613f9c8786613bfe565b90506000613fbb613fb5670de0b6b3a7640000846135d1565b8561339f565b90506000613fd58661287a670de0b6b3a7640000856135d1565b90506000613fe38b83613acf565b90506000613ff1828d613bfe565b90506000613fff82876144a9565b9050600061400d828d61339f565b9050614019818d6135d1565b9e9d5050505050505050505050505050565b6000806140388786613bfe565b905060006140468786613acf565b905060006140548289613bfe565b9050600061406a670de0b6b3a764000085613bfe565b9050600061407883836144a9565b90506000614086828e61339f565b90506000614094828f6135d1565b905060006140ad613bcd670de0b6b3a76400008a6135d1565b9050613beb82613463670de0b6b3a7640000846135d1565b6000806140d28588613bfe565b905060006140e087866135d1565b905060006140ee8883613bfe565b905060006140fc82856144a9565b905061411081670de0b6b3a76400006135d1565b9050614124670de0b6b3a7640000876135d1565b94506141396141338c8361339f565b86613bfe565b9b9a5050505050505050505050565b6000806141558786613bfe565b9050600061416b670de0b6b3a7640000856135d1565b9050614177858261339f565b905060006141898a6134638c85613acf565b9050600061419782856144a9565b905060006141ad670de0b6b3a7640000836135d1565b90506141b98a8261339f565b9c9b505050505050505050505050565b6001600160a01b03831660009081526009602052604090205460ff16156142025760405162461bcd60e51b8152600401610607906153d5565b6703782dace9d900006001600160601b03821610156142335760405162461bcd60e51b815260040161060790615744565b68015af1d78b58c400006001600160601b03821611156142655760405162461bcd60e51b8152600401610607906154a1565b620f42408210156142885760405162461bcd60e51b81526004016106079061551e565b6040518060e00160405280600115158152602001600015158152602001600064ffffffffff16815260200160006001600160601b03168152602001826001600160601b0316815260200160088054905060ff168152602001600081525060096000856001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff02191690831515021790555060408201518160000160026101000a81548164ffffffffff021916908364ffffffffff16021790555060608201518160000160076101000a8154816001600160601b0302191690836001600160601b0316021790555060808201518160000160136101000a8154816001600160601b0302191690836001600160601b0316021790555060a082015181600001601f6101000a81548160ff021916908360ff16021790555060c082015181600101559050506008839080600181540180825580915050600190039060005260206000200160009091909190916101000a8154816001600160a01b0302191690836001600160a01b0316021790555081600b6000856001600160a01b03166001600160a01b0316815260200190815260200160002081905550826001600160a01b03167fb2daf560899f6307b318aecfb57eb2812c488da4a4c1cad2019b482fa63294ed8284604051613f82929190615aa5565b600060018310156144cc5760405162461bcd60e51b815260040161060790615884565b671bc16d674ec7ffff8311156144f45760405162461bcd60e51b8152600401610607906159ac565b60006144ff83614b62565b9050600061450d84836135d1565b905060006145238661451e85614b7d565b614b8b565b905081614534579250610a0d915050565b600061454587846305f5e100614be2565b9050614551828261339f565b979650505050505050565b600080828410614572575050808203600061457a565b505081810360015b9250929050565b614589614cc0565b506001600160a01b038116600090815260096020908152604091829020825160e081018452815460ff808216151583526101008204811615159483019490945264ffffffffff62010000820416948201949094526001600160601b03600160381b850481166060830152600160981b8504166080820152600160f81b90930490911660a0830181905260019091015460c08301819052600854909190600019018082146146e7576008818154811061463d57fe5b600091825260209091200154600880546001600160a01b03909216918490811061466357fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508160096000600885815481106146a357fe5b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff92909216600160f81b026001600160f81b039092169190911790555b60088054806146f257fe5b60008281526020808220600019908401810180546001600160a01b03191690559092019092556040805160e081018252838152808301848152818301858152606083018681526080840187815260a0850188815260c086018981526001600160a01b038f81168b52600990995296909820945185549451935192519151985160ff199095169015151761ff001916610100931515939093029290921766ffffffffff000019166201000064ffffffffff9092169190910217600160381b600160981b031916600160381b6001600160601b0392831602176bffffffffffffffffffffffff60981b1916600160981b9190961602949094176001600160f81b0316600160f81b60ff90951694909402939093178355516001929092019190915560065461482191879116856134e2565b6006546040516360b8257960e11b81526001600160a01b039091169063c1704af29061485390889087906004016152dd565b600060405180830381600087803b15801561486d57600080fd5b505af1158015614881573d6000803e3d6000fd5b505050507f12a8262eb28ee8a8c11e6cf411b3af6ce5bea42abb36e051bf0a65ae602d52ec856040516148b491906152a5565b60405180910390a15050505050565b6137988230836139e6565b306000908152602081905260409020548111156148fd5760405162461bcd60e51b815260040161060790615547565b3060009081526020819052604090205461491790826135d1565b3060009081526020819052604090205560025461493490826135d1565b60025560405160009030907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061496c908590615a6c565b60405180910390a350565b6137983083836139e6565b3060009081526020819052604090205461499c9082613acf565b306000908152602081905260409020556002546149b99082613acf565b60025560405130906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061496c908590615a6c565b81608001516001600160601b031682606001516001600160601b0316101580614a1c57508160200151155b80614a355750610e10826040015164ffffffffff164203105b15614a3f57613798565b606082015160808301516000614a686001600160601b0384166064670de0b6b3a76400006118a2565b90506000614a88836001600160601b0316856001600160601b03166135d1565b905081811115614aab57614aa5846001600160601b031683613acf565b92508190505b614ab7600a5482613acf565b600a819055680168d28e3f002800001015614ae45760405162461bcd60e51b8152600401610607906154c9565b6001600160601b038316606087018190526001600160a01b038616600081815260096020526040908190208054600160381b600160981b031916600160381b9094029390931766ffffffffff00001916620100004264ffffffffff1602179092559051600080516020615b128339815191529061378b908690615a91565b6000670de0b6b3a7640000614b7683614b7d565b0292915050565b670de0b6b3a7640000900490565b60008060028306614ba457670de0b6b3a7640000614ba6565b835b90506002830492505b8215613af457614bbf848561339f565b93506002830615614bd757614bd4818561339f565b90505b600283049250614baf565b6000828180614bf987670de0b6b3a764000061455c565b9092509050670de0b6b3a764000080600060015b888410614cb1576000670de0b6b3a764000082029050600080614c418a614c3c85670de0b6b3a76400006135d1565b61455c565b91509150614c538761287a848c61339f565b9650614c5f8784613bfe565b965086614c6e57505050614cb1565b8715614c78579315935b8015614c82579315935b8415614c9957614c9286886135d1565b9550614ca6565b614ca38688613acf565b95505b505050600101614c0d565b50909998505050505050505050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10614d3d57805160ff1916838001178555614d6a565b82800160010185558215614d6a579182015b82811115614d6a578251825591602001919060010190614d4f565b50614d76929150614d7a565b5090565b5b80821115614d765760008155600101614d7b565b80356001600160a01b0381168114610a0d57600080fd5b60008083601f840112614db7578182fd5b50813567ffffffffffffffff811115614dce578182fd5b602083019150836020808302850101111561457a57600080fd5b60008083601f840112614df9578182fd5b50813567ffffffffffffffff811115614e10578182fd5b60208301915083602082850101111561457a57600080fd5b600060208284031215614e39578081fd5b613af48383614d8f565b60008060408385031215614e55578081fd5b614e5f8484614d8f565b9150614e6e8460208501614d8f565b90509250929050565b600080600060608486031215614e8b578081fd5b8335614e9681615aee565b92506020840135614ea681615aee565b929592945050506040919091013590565b600080600080600060608688031215614ece578081fd5b8535614ed981615aee565b9450602086013567ffffffffffffffff80821115614ef5578283fd5b614f0189838a01614de8565b90965094506040880135915080821115614f19578283fd5b50614f2688828901614de8565b969995985093965092949392505050565b60008060408385031215614f49578182fd5b614f538484614d8f565b946020939093013593505050565b600080600080600060a08688031215614f78578081fd5b614f828787614d8f565b945060208601359350614f988760408801614d8f565b94979396509394606081013594506080013592915050565b600080600060608486031215614fc4578283fd5b614fce8585614d8f565b95602085013595506040909401359392505050565b600080600080600080600080600060c08a8c031215615000578384fd5b893567ffffffffffffffff80821115615017578586fd5b6150238d838e01614da6565b909b50995060208c013591508082111561503b578586fd5b6150478d838e01614da6565b909950975060408c013591508082111561505f578586fd5b5061506c8c828d01614da6565b90965094505060608a013561508081615aee565b925060808a013561509081615aee565b915060a08a01356150a081615aee565b809150509295985092959850929598565b600080600080604085870312156150c6578384fd5b843567ffffffffffffffff808211156150dd578586fd5b6150e988838901614da6565b90965094506020870135915080821115615101578384fd5b5061510e87828801614da6565b95989497509550505050565b60008060008060008060608789031215615132578384fd5b863567ffffffffffffffff80821115615149578586fd5b6151558a838b01614da6565b9098509650602089013591508082111561516d578586fd5b6151798a838b01614da6565b90965094506040890135915080821115615191578384fd5b5061519e89828a01614da6565b979a9699509497509295939492505050565b6000602082840312156151c1578081fd5b8135613af481615b03565b6000602082840312156151dd578081fd5b8151613af481615b03565b6000602082840312156151f9578081fd5b5035919050565b600060208284031215615211578081fd5b5051919050565b60008060006040848603121561522c578081fd5b83359250602084013567ffffffffffffffff811115615249578182fd5b61525586828701614da6565b9497909650939450505050565b600060208284031215615273578081fd5b81356001600160601b0381168114613af4578182fd5b6000825161529b818460208701615abe565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6020808252825182820181905260009190848201906040850190845b818110156153375783516001600160a01b031683529284019291840191600101615312565b50909695505050505050565b901515815260200190565b600060208252825180602084015261536d816040850160208701615abe565b601f01601f19169190910160400192915050565b60208082526012908201527122a9292fa727aa2fa1a7a72a2927a62622a960711b604082015260600190565b6020808252600e908201526d4552525f4e4f4e455f524541445960901b604082015260600190565b6020808252600c908201526b11549497d254d7d093d5539160a21b604082015260600190565b6020808252600e908201526d4552525f4d41585f544f4b454e5360901b604082015260600190565b60208082526010908201526f4552525f4d41585f494e5f524154494f60801b604082015260600190565b60208082526011908201527022a9292fa6a0aa242fa0a8282927ac2f9960791b604082015260600190565b6020808252600f908201526e08aa4a4be9a82a890be82a0a0a49eb608b1b604082015260600190565b6020808252600e908201526d11549497d3505617d5d15251d21560921b604082015260600190565b60208082526014908201527311549497d3505617d513d5105317d5d15251d21560621b604082015260600190565b6020808252600d908201526c11549497d31253525517d3d555609a1b604082015260600190565b6020808252600f908201526e4552525f4d494e5f42414c414e434560881b604082015260600190565b60208082526014908201527311549497d25394d551919250d251539517d0905360621b604082015260600190565b6020808252600f908201526e11549497d253925512505312569151608a1b604082015260600190565b60208082526010908201526f11549497d1125597d25395115493905360821b604082015260600190565b60208082526015908201527422a9292fa12a27a5a2a72fa120a22fa1a0a62622a960591b604082015260600190565b6020808252600c908201526b22a9292fa624a6a4aa2fa4a760a11b604082015260600190565b60208082526016908201527511549497d09513d2d15397d25392551250531256915160521b604082015260600190565b6020808252600b908201526a4552525f5245454e54525960a81b604082015260600190565b6020808252601190820152704552525f4d41585f4f55545f524154494f60781b604082015260600190565b6020808252600d908201526c11549497d393d517d093d55391609a1b604082015260600190565b6020808252601190820152704552525f4f55545f4e4f545f524541445960781b604082015260600190565b6020808252600e908201526d4552525f4e4f545f5055424c494360901b604082015260600190565b6020808252601390820152724552525f4241445f4c494d49545f505249434560681b604082015260600190565b6020808252600e908201526d11549497d3525397d5d15251d21560921b604082015260600190565b60208082526015908201527411549497d393d517d1915157d49150d25412515395605a1b604082015260600190565b60208082526010908201526f4552525f4d554c5f4f564552464c4f5760801b604082015260600190565b6020808252600b908201526a22a9292fa0a9292fa622a760a91b604082015260600190565b6020808252600990820152684552525f524541445960b81b604082015260600190565b6020808252600f908201526e4552525f4c494d49545f505249434560881b604082015260600190565b6020808252600e908201526d11549497d0d3d3919251d554915160921b604082015260600190565b6020808252600c908201526b4552525f4449565f5a45524f60a01b604082015260600190565b6020808252601590820152744552525f42504f575f424153455f544f4f5f4c4f5760581b604082015260600190565b6020808252600f908201526e4552525f45524332305f46414c534560881b604082015260600190565b6020808252601190820152704552525f5355425f554e444552464c4f5760781b604082015260600190565b60208082526010908201526f4552525f4144445f4f564552464c4f5760801b604082015260600190565b60208082526010908201526f4552525f4e554c4c5f4144445245535360801b604082015260600190565b6020808252600f908201526e4552525f494e56414c49445f46454560881b604082015260600190565b6020808252600e908201526d4552525f4d494e5f544f4b454e5360901b604082015260600190565b60208082526016908201527508aa4a4be84a09eaebe8482a68abea89e9ebe90928e960531b604082015260600190565b6020808252600b908201526a22a9292fad22a927afa4a760a91b604082015260600190565b600060e08201905082511515825260208301511515602083015264ffffffffff604084015116604083015260608301516001600160601b038082166060850152806080860151166080850152505060ff60a08401511660a083015260c083015160c083015292915050565b90815260200190565b918252602082015260400190565b60ff91909116815260200190565b6001600160601b0391909116815260200190565b6001600160601b03929092168252602082015260400190565b60005b83811015615ad9578181015183820152602001615ac1565b83811115615ae8576000848401525b50505050565b6001600160a01b03811681146137af57600080fd5b80151581146137af57600080fdfe21b12aed5d425f5675450ffeeae01039085e5323974c3099e1828155d9b51e778c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925a264697066735822122018b2550a0d179afba2d654006db034e06e0e741847346b333f104cd911ac2cd264736f6c634300060c0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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