ETH Price: $3,079.04 (-0.02%)
Gas: 14 Gwei

Contract

0x9A103aB4FE2De5db16338B16FD7550D21d7b8DB6
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Add LP And Stake199101962024-05-20 9:18:594 hrs ago1716196739IN
0x9A103aB4...21d7b8DB6
0 ETH0.00394245.20400005
Unstake And Remo...199101862024-05-20 9:16:594 hrs ago1716196619IN
0x9A103aB4...21d7b8DB6
0 ETH0.002175955.383633
Bond199099492024-05-20 8:28:595 hrs ago1716193739IN
0x9A103aB4...21d7b8DB6
0 ETH0.001277982.85819021
Unstake And Remo...198995902024-05-18 21:43:5940 hrs ago1716068639IN
0x9A103aB4...21d7b8DB6
0 ETH0.001966983.12923388
Unstake And Remo...198995562024-05-18 21:37:1140 hrs ago1716068231IN
0x9A103aB4...21d7b8DB6
0 ETH0.001319633.12805233
Unstake And Remo...198995022024-05-18 21:26:2340 hrs ago1716067583IN
0x9A103aB4...21d7b8DB6
0 ETH0.003769353.9015058
Bond198993642024-05-18 20:58:4741 hrs ago1716065927IN
0x9A103aB4...21d7b8DB6
0 ETH0.001463813.03090213
Add LP And Stake198983182024-05-18 17:28:1144 hrs ago1716053291IN
0x9A103aB4...21d7b8DB6
0 ETH0.001333683.14614441
Bond198975582024-05-18 14:54:4747 hrs ago1716044087IN
0x9A103aB4...21d7b8DB6
0 ETH0.000725784.36369292
Bond198956312024-05-18 8:26:352 days ago1716020795IN
0x9A103aB4...21d7b8DB6
0 ETH0.000721664.3395299
Add LP And Stake198954032024-05-18 7:39:592 days ago1716017999IN
0x9A103aB4...21d7b8DB6
0 ETH0.002223434.2991681
Bond198953872024-05-18 7:36:472 days ago1716017807IN
0x9A103aB4...21d7b8DB6
0 ETH0.000779793.89228981
Bond198909512024-05-17 16:42:112 days ago1715964131IN
0x9A103aB4...21d7b8DB6
0 ETH0.001375648.27146192
Bond198903382024-05-17 14:38:232 days ago1715956703IN
0x9A103aB4...21d7b8DB6
0 ETH0.003456848.26613705
Bond198889602024-05-17 10:01:233 days ago1715940083IN
0x9A103aB4...21d7b8DB6
0 ETH0.003965669
Unstake And Remo...198882542024-05-17 7:38:113 days ago1715931491IN
0x9A103aB4...21d7b8DB6
0 ETH0.004064787.30713292
Bond198873952024-05-17 4:44:593 days ago1715921099IN
0x9A103aB4...21d7b8DB6
0 ETH0.001415453.24525878
Unstake And Remo...198868072024-05-17 2:46:473 days ago1715914007IN
0x9A103aB4...21d7b8DB6
0 ETH0.003527274.50508459
Add LP And Stake198861492024-05-17 0:34:353 days ago1715906075IN
0x9A103aB4...21d7b8DB6
0 ETH0.001453213.29718702
Bond198861332024-05-17 0:31:233 days ago1715905883IN
0x9A103aB4...21d7b8DB6
0 ETH0.001939413.45505357
Unstake And Remo...198856292024-05-16 22:50:233 days ago1715899823IN
0x9A103aB4...21d7b8DB6
0 ETH0.002530363.40864985
Add LP And Stake198852162024-05-16 21:27:233 days ago1715894843IN
0x9A103aB4...21d7b8DB6
0.0160096 ETH0.001999573.75010504
Bond198852122024-05-16 21:26:353 days ago1715894795IN
0x9A103aB4...21d7b8DB6
0 ETH0.000751664.09914635
Unstake And Remo...198830182024-05-16 14:05:234 days ago1715868323IN
0x9A103aB4...21d7b8DB6
0 ETH0.002790656.11095757
Unstake And Remo...198828842024-05-16 13:37:474 days ago1715866667IN
0x9A103aB4...21d7b8DB6
0 ETH0.003094146.91761871
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To Value
198852162024-05-16 21:27:233 days ago1715894843
0x9A103aB4...21d7b8DB6
0.0160096 ETH
198642722024-05-13 23:07:476 days ago1715641667
0x9A103aB4...21d7b8DB6
0.33746246 ETH
198535592024-05-12 11:11:478 days ago1715512307
0x9A103aB4...21d7b8DB6
1.94056133 ETH
198526242024-05-12 8:03:118 days ago1715500991
0x9A103aB4...21d7b8DB6
0.45 ETH
198514592024-05-12 4:08:238 days ago1715486903
0x9A103aB4...21d7b8DB6
15 ETH
198513872024-05-12 3:53:478 days ago1715486027
0x9A103aB4...21d7b8DB6
17.05700619 ETH
198312362024-05-09 8:16:5911 days ago1715242619
0x9A103aB4...21d7b8DB6
0.22867555 ETH
197319062024-04-25 10:52:4725 days ago1714042367
0x9A103aB4...21d7b8DB6
0.00093185 ETH
197319062024-04-25 10:52:4725 days ago1714042367
0x9A103aB4...21d7b8DB6
0.00093185 ETH
197319062024-04-25 10:52:4725 days ago1714042367
0x9A103aB4...21d7b8DB6
0.02 ETH
197275172024-04-24 20:09:1125 days ago1713989351
0x9A103aB4...21d7b8DB6
0.20156438 ETH
197189422024-04-23 15:19:3526 days ago1713885575
0x9A103aB4...21d7b8DB6
0.01302561 ETH
196891682024-04-19 11:24:4731 days ago1713525887
0x9A103aB4...21d7b8DB6
1.02560241 ETH
196810592024-04-18 8:09:3532 days ago1713427775
0x9A103aB4...21d7b8DB6
11 ETH
196678852024-04-16 11:56:4734 days ago1713268607
0x9A103aB4...21d7b8DB6
2 ETH
196677292024-04-16 11:25:1134 days ago1713266711
0x9A103aB4...21d7b8DB6
1 ETH
196620542024-04-15 16:18:3534 days ago1713197915
0x9A103aB4...21d7b8DB6
0.07341743 ETH
196593132024-04-15 7:05:2335 days ago1713164723
0x9A103aB4...21d7b8DB6
0.06748226 ETH
196573002024-04-15 0:20:1135 days ago1713140411
0x9A103aB4...21d7b8DB6
0.1650002 ETH
196457322024-04-13 9:22:3537 days ago1713000155
0x9A103aB4...21d7b8DB6
0.13750244 ETH
196244142024-04-10 9:43:3540 days ago1712742215
0x9A103aB4...21d7b8DB6
0.24553075 ETH
195749432024-04-03 11:27:5947 days ago1712143679
0x9A103aB4...21d7b8DB6
0.12144069 ETH
195723932024-04-03 2:53:2347 days ago1712112803
0x9A103aB4...21d7b8DB6
0.09011555 ETH
195721342024-04-03 2:01:1147 days ago1712109671
0x9A103aB4...21d7b8DB6
1.20208733 ETH
195647162024-04-02 1:02:2348 days ago1712019743
0x9A103aB4...21d7b8DB6
1 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
IndexUtils

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 25 : IndexUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@uniswap/v3-core/contracts/libraries/FixedPoint96.sol';
import '@uniswap/v3-periphery/contracts/interfaces/IPeripheryImmutableState.sol';
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import './interfaces/IDecentralizedIndex.sol';
import './interfaces/IERC20Metadata.sol';
import './interfaces/IStakingPoolToken.sol';
import './interfaces/ITokenRewards.sol';
import './interfaces/IUniswapV2Factory.sol';
import './interfaces/IUniswapV3Pool.sol';
import './interfaces/IUniswapV2Router02.sol';
import './interfaces/IWETH.sol';
import './Zapper.sol';

contract IndexUtils is Context, Zapper {
  using SafeERC20 for IERC20;

  constructor(
    address _v2Router,
    IV3TwapUtilities _v3TwapUtilities
  ) Zapper(_v2Router, _v3TwapUtilities) {}

  function bond(
    IDecentralizedIndex _indexFund,
    address _token,
    uint256 _amount,
    uint256 _amountMintMin
  ) external {
    if (_indexFund.indexType() == IDecentralizedIndex.IndexType.WEIGHTED) {
      IDecentralizedIndex.IndexAssetInfo[] memory _assets = _indexFund
        .getAllAssets();
      uint256[] memory _balsBefore = new uint256[](_assets.length);

      uint256 _tokenCurSupply = IERC20(_token).balanceOf(address(_indexFund));
      uint256 _tokenAmtSupplyRatioX96 = _indexFund.totalSupply() == 0
        ? FixedPoint96.Q96
        : (_amount * FixedPoint96.Q96) / _tokenCurSupply;
      for (uint256 _i; _i < _assets.length; _i++) {
        uint256 _amountNeeded = _indexFund.totalSupply() == 0
          ? _indexFund.getInitialAmount(_token, _amount, _assets[_i].token)
          : (IERC20(_assets[_i].token).balanceOf(address(_indexFund)) *
            _tokenAmtSupplyRatioX96) / FixedPoint96.Q96;
        _balsBefore[_i] = IERC20(_assets[_i].token).balanceOf(address(this));
        IERC20(_assets[_i].token).safeTransferFrom(
          _msgSender(),
          address(this),
          _amountNeeded
        );
        IERC20(_assets[_i].token).safeIncreaseAllowance(
          address(_indexFund),
          _amountNeeded
        );
      }
      uint256 _idxBalBefore = IERC20(_indexFund).balanceOf(address(this));
      _indexFund.bond(_token, _amount, _amountMintMin);
      IERC20(_indexFund).safeTransfer(
        _msgSender(),
        IERC20(_indexFund).balanceOf(address(this)) - _idxBalBefore
      );

      // refund any excess tokens to user we didn't use to bond
      for (uint256 _i; _i < _assets.length; _i++) {
        _checkAndRefundERC20(_msgSender(), _assets[_i].token, _balsBefore[_i]);
      }
    } else {
      require(
        _indexFund.indexType() == IDecentralizedIndex.IndexType.UNWEIGHTED,
        'UW'
      );
      IERC20(_token).safeTransferFrom(_msgSender(), address(this), _amount);
      IERC20(_token).safeIncreaseAllowance(address(_indexFund), _amount);
      uint256 _idxBalBefore = IERC20(_indexFund).balanceOf(address(this));
      _indexFund.bond(_token, _amount, _amountMintMin);
      IERC20(_indexFund).safeTransfer(
        _msgSender(),
        IERC20(_indexFund).balanceOf(address(this)) - _idxBalBefore
      );
    }
  }

  function bondWeightedFromNative(
    IDecentralizedIndex _indexFund,
    uint256 _assetIdx,
    uint256 _amountTokensForAssetIdx,
    uint256 _amountMintMin,
    uint256 _amountPairedLpTokenMin,
    uint256 _slippage, // 1 == 0.1%, 10 == 1%, 1000 == 100%
    uint256 _deadline,
    bool _stakeAsWell
  ) external payable {
    require(msg.value > 0, 'NATIVE');
    uint256 _ethBalBefore = address(this).balance - msg.value;
    IDecentralizedIndex.IndexAssetInfo[] memory _assets = _indexFund
      .getAllAssets();
    (
      uint256[] memory _balancesBefore,
      uint256[] memory _amountsReceived
    ) = _swapNativeForTokensWeightedV2(
        _indexFund,
        _stakeAsWell ? msg.value / 2 : msg.value,
        _assets,
        _assetIdx,
        _amountTokensForAssetIdx
      );

    // allowance for _assetIdx is increased in _bondToRecipient below,
    // we just need to increase allowance for any other index tokens here first
    for (uint256 _i; _i < _assets.length; _i++) {
      if (_i == _assetIdx) {
        continue;
      }
      IERC20(_assets[_i].token).safeIncreaseAllowance(
        address(_indexFund),
        _amountsReceived[_i]
      );
    }
    uint256 _idxTokensGained = _bondToRecipient(
      _indexFund,
      _assets[_assetIdx].token,
      _amountsReceived[_assetIdx],
      _amountMintMin,
      _stakeAsWell ? address(this) : _msgSender()
    );

    if (_stakeAsWell) {
      _zapIndexTokensAndNative(
        _msgSender(),
        _indexFund,
        _idxTokensGained,
        msg.value / 2,
        _amountPairedLpTokenMin,
        _slippage,
        _deadline
      );
    }

    // refund any excess tokens to user we didn't use to bond
    for (uint256 _i; _i < _assets.length; _i++) {
      _checkAndRefundERC20(
        _msgSender(),
        _assets[_i].token,
        _balancesBefore[_i]
      );
    }

    // refund excess ETH
    if (address(this).balance > _ethBalBefore) {
      (bool _s, ) = payable(_msgSender()).call{
        value: address(this).balance - _ethBalBefore
      }('');
      require(_s, 'ETHREFUND');
    }
  }

  // function bondUnweightedFromNative(
  //   IDecentralizedIndex _indexFund,
  //   uint256 _poolIdx,
  //   uint256 _amountPairedLpTokenMin,
  //   uint256 _slippage, // 1 == 0.1%, 10 == 1%, 1000 == 100%
  //   uint256 _deadline,
  //   bool _stakeAsWell
  // ) external payable {
  //   require(msg.value > 0, 'NATIVE');
  //   uint256 _wethBalBefore = IERC20(WETH).balanceOf(address(this));
  //   IWETH(WETH).deposit{ value: _stakeAsWell ? msg.value / 2 : msg.value }();
  //   uint256 _wethToBond = IERC20(WETH).balanceOf(address(this)) -
  //     _wethBalBefore;
  //   uint256 _idxTokensGained = _bondUnweightedFromWrappedNative(
  //     _indexFund,
  //     _stakeAsWell ? address(this) : _msgSender(),
  //     _poolIdx,
  //     _wethToBond,
  //     _slippage
  //   );

  //   if (_stakeAsWell) {
  //     _zapIndexTokensAndNative(
  //       _msgSender(),
  //       _indexFund,
  //       _idxTokensGained,
  //       msg.value / 2,
  //       _amountPairedLpTokenMin,
  //       _slippage,
  //       _deadline
  //     );
  //   }
  // }

  function addLPAndStake(
    IDecentralizedIndex _indexFund,
    uint256 _amountIdxTokens,
    address _pairedLpTokenProvided,
    uint256 _amtPairedLpTokenProvided,
    uint256 _amountPairedLpTokenMin,
    uint256 _slippage,
    uint256 _deadline
  ) external payable {
    address _v2Pool = IUniswapV2Factory(IUniswapV2Router02(V2_ROUTER).factory())
      .getPair(address(_indexFund), _indexFund.PAIRED_LP_TOKEN());
    uint256 _idxTokensBefore = IERC20(address(_indexFund)).balanceOf(
      address(this)
    );
    uint256 _pairedLpTokenBefore = IERC20(_indexFund.PAIRED_LP_TOKEN())
      .balanceOf(address(this));
    uint256 _ethBefore = address(this).balance - msg.value;
    uint256 _v2PoolBefore = IERC20(_v2Pool).balanceOf(address(this));
    IERC20(address(_indexFund)).safeTransferFrom(
      _msgSender(),
      address(this),
      _amountIdxTokens
    );
    if (_pairedLpTokenProvided == address(0)) {
      require(msg.value > 0, 'NEEDETH');
      _amtPairedLpTokenProvided = msg.value;
    } else {
      IERC20(_pairedLpTokenProvided).safeTransferFrom(
        _msgSender(),
        address(this),
        _amtPairedLpTokenProvided
      );
    }
    if (_pairedLpTokenProvided != _indexFund.PAIRED_LP_TOKEN()) {
      _zap(
        _pairedLpTokenProvided,
        _indexFund.PAIRED_LP_TOKEN(),
        _amtPairedLpTokenProvided,
        _amountPairedLpTokenMin
      );
    }

    IERC20(_indexFund.PAIRED_LP_TOKEN()).safeIncreaseAllowance(
      address(_indexFund),
      IERC20(_indexFund.PAIRED_LP_TOKEN()).balanceOf(address(this)) -
        _pairedLpTokenBefore
    );
    _indexFund.addLiquidityV2(
      IERC20(address(_indexFund)).balanceOf(address(this)) - _idxTokensBefore,
      IERC20(_indexFund.PAIRED_LP_TOKEN()).balanceOf(address(this)) -
        _pairedLpTokenBefore,
      _slippage,
      _deadline
    );

    IERC20(_v2Pool).safeIncreaseAllowance(
      _indexFund.lpStakingPool(),
      IERC20(_v2Pool).balanceOf(address(this)) - _v2PoolBefore
    );
    IStakingPoolToken(_indexFund.lpStakingPool()).stake(
      _msgSender(),
      IERC20(_v2Pool).balanceOf(address(this)) - _v2PoolBefore
    );

    // refunds if needed for index tokens and pairedLpToken
    if (address(this).balance > _ethBefore) {
      (bool _s, ) = payable(_msgSender()).call{
        value: address(this).balance - _ethBefore
      }('');
      require(_s && address(this).balance >= _ethBefore, 'TOOMUCH');
    }
    _checkAndRefundERC20(_msgSender(), address(_indexFund), _idxTokensBefore);
    _checkAndRefundERC20(
      _msgSender(),
      _indexFund.PAIRED_LP_TOKEN(),
      _pairedLpTokenBefore
    );
  }

  function unstakeAndRemoveLP(
    IDecentralizedIndex _indexFund,
    uint256 _amountStakedTokens,
    uint256 _minLPTokens,
    uint256 _minPairedLpToken,
    uint256 _deadline
  ) external {
    address _stakingPool = _indexFund.lpStakingPool();
    address _pairedLpToken = _indexFund.PAIRED_LP_TOKEN();
    uint256 _stakingBalBefore = IERC20(_stakingPool).balanceOf(address(this));
    uint256 _pairedLpTokenBefore = IERC20(_pairedLpToken).balanceOf(
      address(this)
    );
    IERC20(_stakingPool).safeTransferFrom(
      _msgSender(),
      address(this),
      _amountStakedTokens
    );
    uint256 _indexBalBefore = _unstakeAndRemoveLP(
      _indexFund,
      _stakingPool,
      IERC20(_stakingPool).balanceOf(address(this)) - _stakingBalBefore,
      _minLPTokens,
      _minPairedLpToken,
      _deadline
    );
    if (
      IERC20(address(_indexFund)).balanceOf(address(this)) > _indexBalBefore
    ) {
      IERC20(address(_indexFund)).safeTransfer(
        _msgSender(),
        IERC20(address(_indexFund)).balanceOf(address(this)) - _indexBalBefore
      );
    }
    if (
      IERC20(_pairedLpToken).balanceOf(address(this)) > _pairedLpTokenBefore
    ) {
      IERC20(_pairedLpToken).safeTransfer(
        _msgSender(),
        IERC20(_pairedLpToken).balanceOf(address(this)) - _pairedLpTokenBefore
      );
    }
  }

  function claimRewardsMulti(address[] memory _rewards) external {
    for (uint256 _i; _i < _rewards.length; _i++) {
      ITokenRewards(_rewards[_i]).claimReward(_msgSender());
    }
  }

  function _swapNativeForTokensWeightedV2(
    IDecentralizedIndex _indexFund,
    uint256 _amountNative,
    IDecentralizedIndex.IndexAssetInfo[] memory _assets,
    uint256 _poolIdx,
    uint256 _amountForPoolIdx
  ) internal returns (uint256[] memory, uint256[] memory) {
    uint256[] memory _amountBefore = new uint256[](_assets.length);
    uint256[] memory _amountReceived = new uint256[](_assets.length);
    uint256 _tokenCurSupply = IERC20(_assets[_poolIdx].token).balanceOf(
      address(_indexFund)
    );
    uint256 _tokenAmtSupplyRatioX96 = _indexFund.totalSupply() == 0
      ? FixedPoint96.Q96
      : (_amountForPoolIdx * FixedPoint96.Q96) / _tokenCurSupply;
    uint256 _nativeLeft = _amountNative;
    for (uint256 _i; _i < _assets.length; _i++) {
      (_nativeLeft, _amountBefore[_i], _amountReceived[_i]) = _swapForIdxToken(
        _indexFund,
        _assets[_poolIdx].token,
        _amountForPoolIdx,
        _assets[_i].token,
        _tokenAmtSupplyRatioX96,
        _nativeLeft
      );
    }
    return (_amountBefore, _amountReceived);
  }

  function _swapForIdxToken(
    IDecentralizedIndex _indexFund,
    address _initToken,
    uint256 _initTokenAmount,
    address _outToken,
    uint256 _tokenAmtSupplyRatioX96,
    uint256 _nativeLeft
  )
    internal
    returns (
      uint256 _newNativeLeft,
      uint256 _amountBefore,
      uint256 _amountReceived
    )
  {
    uint256 _nativeBefore = address(this).balance;
    _amountBefore = IERC20(_outToken).balanceOf(address(this));
    uint256 _amountOut = _indexFund.totalSupply() == 0
      ? _indexFund.getInitialAmount(_initToken, _initTokenAmount, _outToken)
      : (IERC20(_outToken).balanceOf(address(_indexFund)) *
        _tokenAmtSupplyRatioX96) / FixedPoint96.Q96;
    address[] memory _path = new address[](2);
    _path[0] = IUniswapV2Router02(V2_ROUTER).WETH();
    _path[1] = _outToken;
    IUniswapV2Router02(V2_ROUTER).swapETHForExactTokens{ value: _nativeLeft }(
      _amountOut,
      _path,
      address(this),
      block.timestamp
    );
    _newNativeLeft = _nativeLeft - (_nativeBefore - address(this).balance);
    _amountReceived =
      IERC20(_outToken).balanceOf(address(this)) -
      _amountBefore;
  }

  // function _bondUnweightedFromWrappedNative(
  //   IDecentralizedIndex _indexFund,
  //   address _recipient,
  //   uint256 _poolIdx,
  //   uint256 _wethToBond,
  //   uint256 _slippage // 1 == 0.1%, 10 == 1%, 1000 == 100%
  // ) internal returns (uint256) {
  //   IDecentralizedIndex.IndexAssetInfo[] memory _assets = _indexFund
  //     .getAllAssets();

  //   uint256 _bondTokensGained;
  //   if (_assets[_poolIdx].token == WETH) {
  //     _bondTokensGained = _wethToBond;
  //   } else {
  //     uint256 _poolPriceX96 = V3_TWAP_UTILS.priceX96FromSqrtPriceX96(
  //       V3_TWAP_UTILS.sqrtPriceX96FromPoolAndInterval(_assets[_poolIdx].c1)
  //     );
  //     address _token0 = WETH < address(_indexFund) ? WETH : address(_indexFund);
  //     uint256 _amountOut = _token0 == WETH
  //       ? (_poolPriceX96 * _wethToBond) / FixedPoint96.Q96
  //       : (_wethToBond * FixedPoint96.Q96) / _poolPriceX96;

  //     IERC20(WETH).safeIncreaseAllowance(V3_ROUTER, _wethToBond);
  //     _bondTokensGained = ISwapRouter(V3_ROUTER).exactInputSingle(
  //       ISwapRouter.ExactInputSingleParams({
  //         tokenIn: WETH,
  //         tokenOut: _assets[_poolIdx].token,
  //         fee: IUniswapV3Pool(_assets[_poolIdx].c1).fee(),
  //         recipient: address(this),
  //         deadline: block.timestamp,
  //         amountIn: _wethToBond,
  //         amountOutMinimum: (_amountOut * (1000 - _slippage)) / 1000,
  //         sqrtPriceLimitX96: 0
  //       })
  //     );
  //   }
  //   return
  //     _bondToRecipient(
  //       _indexFund,
  //       _assets[_poolIdx].token,
  //       _bondTokensGained,
  //       0,
  //       _recipient
  //     );
  // }

  function _unstakeAndRemoveLP(
    IDecentralizedIndex _indexFund,
    address _stakingPool,
    uint256 _unstakeAmount,
    uint256 _minLPTokens,
    uint256 _minPairedLpTokens,
    uint256 _deadline
  ) internal returns (uint256 _fundTokensBefore) {
    address _pairedLpToken = _indexFund.PAIRED_LP_TOKEN();
    address _v2Pool = IUniswapV2Factory(IUniswapV2Router02(V2_ROUTER).factory())
      .getPair(address(_indexFund), _pairedLpToken);
    uint256 _v2TokensBefore = IERC20(_v2Pool).balanceOf(address(this));
    IStakingPoolToken(_stakingPool).unstake(_unstakeAmount);

    _fundTokensBefore = _indexFund.balanceOf(address(this));
    IERC20(_v2Pool).safeIncreaseAllowance(
      address(_indexFund),
      IERC20(_v2Pool).balanceOf(address(this)) - _v2TokensBefore
    );
    _indexFund.removeLiquidityV2(
      IERC20(_v2Pool).balanceOf(address(this)) - _v2TokensBefore,
      _minLPTokens,
      _minPairedLpTokens,
      _deadline
    );
  }

  function _bondToRecipient(
    IDecentralizedIndex _indexFund,
    address _indexToken,
    uint256 _bondTokens,
    uint256 _amountMintMin,
    address _recipient
  ) internal returns (uint256) {
    uint256 _idxTokensBefore = IERC20(address(_indexFund)).balanceOf(
      address(this)
    );
    IERC20(_indexToken).safeIncreaseAllowance(address(_indexFund), _bondTokens);
    _indexFund.bond(_indexToken, _bondTokens, _amountMintMin);
    uint256 _idxTokensGained = IERC20(address(_indexFund)).balanceOf(
      address(this)
    ) - _idxTokensBefore;
    if (_recipient != address(this)) {
      IERC20(address(_indexFund)).safeTransfer(_recipient, _idxTokensGained);
    }
    return _idxTokensGained;
  }

  function _zapIndexTokensAndNative(
    address _user,
    IDecentralizedIndex _indexFund,
    uint256 _amountTokens,
    uint256 _amountETH,
    uint256 _amtPairedLpTokenMin,
    uint256 _slippage,
    uint256 _deadline
  ) internal {
    address _pairedLpToken = _indexFund.PAIRED_LP_TOKEN();
    uint256 _tokensBefore = IERC20(address(_indexFund)).balanceOf(
      address(this)
    ) - _amountTokens;
    uint256 _pairedLpTokenBefore = IERC20(_pairedLpToken).balanceOf(
      address(this)
    );
    address _stakingPool = _indexFund.lpStakingPool();

    _zap(address(0), _pairedLpToken, _amountETH, _amtPairedLpTokenMin);

    address _v2Pool = IUniswapV2Factory(IUniswapV2Router02(V2_ROUTER).factory())
      .getPair(address(_indexFund), _pairedLpToken);
    uint256 _lpTokensBefore = IERC20(_v2Pool).balanceOf(address(this));
    IERC20(_pairedLpToken).safeIncreaseAllowance(
      address(_indexFund),
      IERC20(_pairedLpToken).balanceOf(address(this)) - _pairedLpTokenBefore
    );
    _indexFund.addLiquidityV2(
      _amountTokens,
      IERC20(_pairedLpToken).balanceOf(address(this)) - _pairedLpTokenBefore,
      _slippage,
      _deadline
    );
    IERC20(_v2Pool).safeIncreaseAllowance(
      _stakingPool,
      IERC20(_v2Pool).balanceOf(address(this)) - _lpTokensBefore
    );
    IStakingPoolToken(_stakingPool).stake(
      _user,
      IERC20(_v2Pool).balanceOf(address(this)) - _lpTokensBefore
    );

    // check & refund excess tokens from LPing as needed
    if (IERC20(address(_indexFund)).balanceOf(address(this)) > _tokensBefore) {
      IERC20(address(_indexFund)).safeTransfer(
        _user,
        IERC20(address(_indexFund)).balanceOf(address(this)) - _tokensBefore
      );
    }
    if (
      IERC20(_pairedLpToken).balanceOf(address(this)) > _pairedLpTokenBefore
    ) {
      IERC20(_pairedLpToken).safeTransfer(
        _user,
        IERC20(_pairedLpToken).balanceOf(address(this)) - _pairedLpTokenBefore
      );
    }
  }

  function _checkAndRefundERC20(
    address _user,
    address _asset,
    uint256 _beforeBal
  ) internal {
    uint256 _curBal = IERC20(_asset).balanceOf(address(this));
    if (_curBal > _beforeBal) {
      IERC20(_asset).safeTransfer(_user, _curBal - _beforeBal);
    }
  }
}

File 2 of 25 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 3 of 25 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

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

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

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

File 4 of 25 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 5 of 25 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
     * 0 before setting it to a non-zero value.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 6 of 25 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

File 7 of 25 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 8 of 25 : IUniswapV3SwapCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
    /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
    /// @dev In the implementation you must pay the pool tokens owed for the swap.
    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
    /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
    /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
    /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
    function uniswapV3SwapCallback(
        int256 amount0Delta,
        int256 amount1Delta,
        bytes calldata data
    ) external;
}

File 9 of 25 : FixedPoint96.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.4.0;

/// @title FixedPoint96
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
/// @dev Used in SqrtPriceMath.sol
library FixedPoint96 {
    uint8 internal constant RESOLUTION = 96;
    uint256 internal constant Q96 = 0x1000000000000000000000000;
}

File 10 of 25 : IPeripheryImmutableState.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Immutable state
/// @notice Functions that return immutable state of the router
interface IPeripheryImmutableState {
    /// @return Returns the address of the Uniswap V3 factory
    function factory() external view returns (address);

    /// @return Returns the address of WETH9
    function WETH9() external view returns (address);
}

File 11 of 25 : ISwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

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

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

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

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

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

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

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

File 13 of 25 : ICurvePool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface ICurvePool {
  function coins(uint256 _idx) external returns (address);

  function exchange(
    int128 i,
    int128 j,
    uint256 dx,
    uint256 minDy,
    address receiver
  ) external returns (uint256);
}

File 14 of 25 : IDecentralizedIndex.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

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

interface IDecentralizedIndex is IERC20 {
  enum IndexType {
    WEIGHTED,
    UNWEIGHTED
  }

  // all fees: 1 == 0.01%, 10 == 0.1%, 100 == 1%
  struct Fees {
    uint256 burn;
    uint256 bond;
    uint256 debond;
    uint256 buy;
    uint256 sell;
    uint256 partner;
  }

  struct IndexAssetInfo {
    address token;
    uint256 weighting;
    uint256 basePriceUSDX96;
    address c1; // arbitrary contract/address field we can use for an index
    uint256 q1; // arbitrary quantity/number field we can use for an index
  }

  event Create(address indexed newIdx, address indexed wallet);
  event Bond(
    address indexed wallet,
    address indexed token,
    uint256 amountTokensBonded,
    uint256 amountTokensMinted
  );
  event Debond(address indexed wallet, uint256 amountDebonded);
  event AddLiquidity(
    address indexed wallet,
    uint256 amountTokens,
    uint256 amountDAI
  );
  event RemoveLiquidity(address indexed wallet, uint256 amountLiquidity);

  function BOND_FEE() external view returns (uint256);

  function DEBOND_FEE() external view returns (uint256);

  function FLASH_FEE() external view returns (uint256);

  function PAIRED_LP_TOKEN() external view returns (address);

  function indexType() external view returns (IndexType);

  function created() external view returns (uint256);

  function lpStakingPool() external view returns (address);

  function lpRewardsToken() external view returns (address);

  function partner() external view returns (address);

  function getIdxPriceUSDX96() external view returns (uint256, uint256);

  function isAsset(address token) external view returns (bool);

  function getAllAssets() external view returns (IndexAssetInfo[] memory);

  function getInitialAmount(
    address sToken,
    uint256 sAmount,
    address tToken
  ) external view returns (uint256);

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

  function processPreSwapFeesAndSwap() external;

  function bond(address token, uint256 amount, uint256 amountMintMin) external;

  function debond(
    uint256 amount,
    address[] memory token,
    uint8[] memory percentage
  ) external;

  function addLiquidityV2(
    uint256 idxTokens,
    uint256 daiTokens,
    uint256 slippage,
    uint256 deadline
  ) external;

  function removeLiquidityV2(
    uint256 lpTokens,
    uint256 minTokens,
    uint256 minDAI,
    uint256 deadline
  ) external;

  function flash(
    address recipient,
    address token,
    uint256 amount,
    bytes calldata data
  ) external;
}

File 15 of 25 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IERC20Metadata {
  function decimals() external view returns (uint8);
}

File 16 of 25 : IERC4626.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IERC4626 {
  function deposit(
    uint256 yETHAmount,
    address receiver
  ) external returns (uint256 styETHAmount);

  function withdraw(
    uint256 styETHAmount,
    address receiver
  ) external returns (uint256 yETHAmount);
}

File 17 of 25 : IStakingPoolToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IStakingPoolToken {
  event Stake(address indexed executor, address indexed user, uint256 amount);

  event Unstake(address indexed user, uint256 amount);

  function indexFund() external view returns (address);

  function stakingToken() external view returns (address);

  function poolRewards() external view returns (address);

  function stakeUserRestriction() external view returns (address);

  function stake(address user, uint256 amount) external;

  function unstake(uint256 amount) external;
}

File 18 of 25 : ITokenRewards.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface ITokenRewards {
  event AddShares(address indexed wallet, uint256 amount);

  event RemoveShares(address indexed wallet, uint256 amount);

  event ClaimReward(address indexed wallet);

  event DistributeReward(address indexed wallet, uint256 amount);

  event DepositRewards(address indexed wallet, uint256 amount);

  function totalShares() external view returns (uint256);

  function totalStakers() external view returns (uint256);

  function rewardsToken() external view returns (address);

  function trackingToken() external view returns (address);

  function depositFromPairedLpToken(
    uint256 amount,
    uint256 slippageOverride
  ) external;

  function depositRewards(uint256 amount) external;

  function claimReward(address wallet) external;

  function setShares(
    address wallet,
    uint256 amount,
    bool sharesRemoving
  ) external;
}

File 19 of 25 : IUniswapV2Factory.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

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

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

File 20 of 25 : IUniswapV2Pair.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IUniswapV2Pair {
  function token0() external view returns (address);

  function token1() external view returns (address);

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

File 21 of 25 : IUniswapV2Router02.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IUniswapV2Router02 {
  function factory() external view returns (address);

  function WETH() external view returns (address);

  function addLiquidity(
    address tokenA,
    address tokenB,
    uint amountADesired,
    uint amountBDesired,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
  ) external returns (uint amountA, uint amountB, uint liquidity);

  function removeLiquidity(
    address tokenA,
    address tokenB,
    uint liquidity,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
  ) external returns (uint amountA, uint amountB);

  function swapETHForExactTokens(
    uint256 amountOut,
    address[] calldata path,
    address to,
    uint256 deadline
  ) external payable returns (uint256[] memory amounts);

  function swapExactTokensForTokensSupportingFeeOnTransferTokens(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external;
}

File 22 of 25 : IUniswapV3Pool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IUniswapV3Pool {
  /// @notice The first of the two tokens of the pool, sorted by address
  /// @return The token contract address
  function token0() external view returns (address);

  /// @notice The second of the two tokens of the pool, sorted by address
  /// @return The token contract address
  function token1() external view returns (address);

  /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
  /// @return The fee
  function fee() external view returns (uint24);

  /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
  /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
  /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
  /// you must call it with secondsAgos = [3600, 0].
  /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
  /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
  /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
  /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
  /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
  /// timestamp
  function observe(
    uint32[] calldata secondsAgos
  )
    external
    view
    returns (
      int56[] memory tickCumulatives,
      uint160[] memory secondsPerLiquidityCumulativeX128s
    );

  /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
  /// when accessed externally.
  /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
  /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
  /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
  /// boundary.
  /// observationIndex The index of the last oracle observation that was written,
  /// observationCardinality The current maximum number of observations stored in the pool,
  /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
  /// feeProtocol The protocol fee for both tokens of the pool.
  /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
  /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
  /// unlocked Whether the pool is currently locked to reentrancy
  function slot0()
    external
    view
    returns (
      uint160 sqrtPriceX96,
      int24 tick,
      uint16 observationIndex,
      uint16 observationCardinality,
      uint16 observationCardinalityNext,
      uint8 feeProtocol,
      bool unlocked
    );
}

File 23 of 25 : IV3TwapUtilities.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IV3TwapUtilities {
  function getV3Pool(
    address v3Factory,
    address token0,
    address token1,
    uint24 poolFee
  ) external view returns (address);

  function getPoolPriceUSDX96(
    address pricePool,
    address nativeStablePool,
    address WETH9
  ) external view returns (uint256);

  function sqrtPriceX96FromPoolAndInterval(
    address pool
  ) external view returns (uint160);

  function priceX96FromSqrtPriceX96(
    uint160 sqrtPriceX96
  ) external pure returns (uint256);
}

File 24 of 25 : IWETH.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IWETH {
  function deposit() external payable;

  function withdraw(uint256 _amount) external;
}

File 25 of 25 : IZapper.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IZapper {
  enum PoolType {
    CURVE,
    V2,
    V3
  }

  struct Pools {
    PoolType poolType; // assume same for both pool1 and pool2
    address pool1;
    address pool2;
  }
}

File 26 of 25 : Zapper.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@uniswap/v3-core/contracts/libraries/FixedPoint96.sol';
import '@uniswap/v3-periphery/contracts/interfaces/IPeripheryImmutableState.sol';
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import './interfaces/ICurvePool.sol';
import './interfaces/IDecentralizedIndex.sol';
import './interfaces/IERC4626.sol';
import './interfaces/IUniswapV2Pair.sol';
import './interfaces/IUniswapV3Pool.sol';
import './interfaces/IUniswapV2Router02.sol';
import './interfaces/IV3TwapUtilities.sol';
import './interfaces/IWETH.sol';
import './interfaces/IZapper.sol';

contract Zapper is IZapper, Context, Ownable {
  using SafeERC20 for IERC20;

  address constant OHM = 0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5;
  address constant STYETH = 0x583019fF0f430721aDa9cfb4fac8F06cA104d0B4;
  address constant YETH = 0x1BED97CBC3c24A4fb5C069C6E311a967386131f7;
  address constant WETH_YETH_POOL = 0x69ACcb968B19a53790f43e57558F5E443A91aF22;
  address constant V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
  address immutable V2_ROUTER;
  address immutable WETH;
  IV3TwapUtilities immutable V3_TWAP_UTILS;

  uint256 _slippage = 30; // 3%

  address public pOHM;

  // token in => token out => swap pool(s)
  mapping(address => mapping(address => Pools)) public zapMap;
  // curve pool => token => idx
  mapping(address => mapping(address => int128)) public curveTokenIdx;

  constructor(address _v2Router, IV3TwapUtilities _v3TwapUtilities) {
    V2_ROUTER = _v2Router;
    V3_TWAP_UTILS = _v3TwapUtilities;
    WETH = IUniswapV2Router02(V2_ROUTER).WETH();

    // WETH/YETH
    _setZapMapFromPoolSingle(
      PoolType.CURVE,
      0x69ACcb968B19a53790f43e57558F5E443A91aF22
    );
    // WETH/DAI
    _setZapMapFromPoolSingle(
      PoolType.V3,
      0x60594a405d53811d3BC4766596EFD80fd545A270
    );
    // WETH/USDC
    _setZapMapFromPoolSingle(
      PoolType.V3,
      0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640
    );
    // WETH/OHM
    _setZapMapFromPoolSingle(
      PoolType.V3,
      0x88051B0eea095007D3bEf21aB287Be961f3d8598
    );
    // USDC/OHM
    _setZapMapFromPoolSingle(
      PoolType.V3,
      0x893f503FaC2Ee1e5B78665db23F9c94017Aae97D
    );
  }

  function _zap(
    address _in,
    address _out,
    uint256 _amountIn,
    uint256 _amountOutMin
  ) internal returns (uint256 _amountOut) {
    if (_in == address(0)) {
      _amountIn = _ethToWETH(_amountIn);
      _in = WETH;
    }
    // handle pOHM separately through pod, modularize later
    bool _isOutPOHM;
    if (pOHM == _out) {
      _isOutPOHM = true;
      _out = OHM;
    }
    // handle yETH and st-yETH special through curve pool, modularize later
    if (_out == YETH || _out == STYETH) {
      require(_in == WETH, 'YETHIN');
      return _wethToYeth(_amountIn, _amountOutMin, _out == STYETH);
    } else if (_in == YETH || _in == STYETH) {
      require(_out == WETH, 'YETHOUT');
      return _styethToWeth(_amountIn, _amountOutMin, _in == YETH);
    }
    Pools memory _poolInfo = zapMap[_in][_out];
    // no pool so just try to swap over one path univ2
    if (_poolInfo.pool1 == address(0)) {
      address[] memory _path = new address[](2);
      _path[0] = _in;
      _path[1] = _out;
      _amountOut = _swapV2(_path, _amountIn, _amountOutMin);
    } else {
      bool _twoHops = _poolInfo.pool2 != address(0);
      if (_poolInfo.poolType == PoolType.CURVE) {
        // curve
        _amountOut = _swapCurve(
          _poolInfo.pool1,
          curveTokenIdx[_poolInfo.pool1][_in],
          curveTokenIdx[_poolInfo.pool1][_out],
          _amountIn,
          _amountOutMin
        );
      } else if (_poolInfo.poolType == PoolType.V2) {
        // univ2
        address _token0 = IUniswapV2Pair(_poolInfo.pool1).token0();
        address[] memory _path = new address[](_twoHops ? 3 : 2);
        _path[0] = _in;
        _path[1] = !_twoHops ? _out : _token0 == _in
          ? IUniswapV2Pair(_poolInfo.pool1).token1()
          : _token0;
        if (_twoHops) {
          _path[2] = _out;
        }
        _amountOut = _swapV2(_path, _amountIn, _amountOutMin);
      } else {
        // univ3
        if (_twoHops) {
          address _t0 = IUniswapV3Pool(_poolInfo.pool1).token0();
          _amountOut = _swapV3Multi(
            _in,
            IUniswapV3Pool(_poolInfo.pool1).fee(),
            _t0 == _in ? IUniswapV3Pool(_poolInfo.pool1).token0() : _t0,
            IUniswapV3Pool(_poolInfo.pool2).fee(),
            _out,
            _amountIn,
            _amountOutMin
          );
        } else {
          _amountOut = _swapV3Single(
            _in,
            IUniswapV3Pool(_poolInfo.pool1).fee(),
            _out,
            _amountIn,
            _amountOutMin
          );
        }
      }
    }
    if (!_isOutPOHM) {
      return _amountOut;
    }
    uint256 _pOHMBefore = IERC20(pOHM).balanceOf(address(this));
    IERC20(OHM).safeIncreaseAllowance(pOHM, _amountOut);
    IDecentralizedIndex(pOHM).bond(OHM, _amountOut, 0);
    return IERC20(pOHM).balanceOf(address(this)) - _pOHMBefore;
  }

  function _ethToWETH(uint256 _amountETH) internal returns (uint256) {
    uint256 _wethBal = IERC20(WETH).balanceOf(address(this));
    IWETH(WETH).deposit{ value: _amountETH }();
    return IERC20(WETH).balanceOf(address(this)) - _wethBal;
  }

  function _swapV3Single(
    address _in,
    uint24 _fee,
    address _out,
    uint256 _amountIn,
    uint256 _amountOutMin
  ) internal returns (uint256 _amountOut) {
    if (_amountOutMin == 0) {
      address _v3Pool = V3_TWAP_UTILS.getV3Pool(
        IPeripheryImmutableState(V3_ROUTER).factory(),
        _in,
        _out,
        _fee
      );
      address _token0 = _in < _out ? _in : _out;
      uint256 _poolPriceX96 = V3_TWAP_UTILS.priceX96FromSqrtPriceX96(
        V3_TWAP_UTILS.sqrtPriceX96FromPoolAndInterval(_v3Pool)
      );
      _amountOutMin = _in == _token0
        ? (_poolPriceX96 * _amountIn) / FixedPoint96.Q96
        : (_amountIn * FixedPoint96.Q96) / _poolPriceX96;
    }

    uint256 _outBefore = IERC20(_out).balanceOf(address(this));
    IERC20(_in).safeIncreaseAllowance(V3_ROUTER, _amountIn);
    ISwapRouter(V3_ROUTER).exactInputSingle(
      ISwapRouter.ExactInputSingleParams({
        tokenIn: _in,
        tokenOut: _out,
        fee: _fee,
        recipient: address(this),
        deadline: block.timestamp,
        amountIn: _amountIn,
        amountOutMinimum: (_amountOutMin * (1000 - _slippage)) / 1000,
        sqrtPriceLimitX96: 0
      })
    );
    return IERC20(_out).balanceOf(address(this)) - _outBefore;
  }

  function _swapV3Multi(
    address _in,
    uint24 _fee1,
    address _in2,
    uint24 _fee2,
    address _out,
    uint256 _amountIn,
    uint256 _amountOutMin
  ) internal returns (uint256) {
    uint256 _outBefore = IERC20(_out).balanceOf(address(this));
    IERC20(_in).safeIncreaseAllowance(V3_ROUTER, _amountIn);
    bytes memory _path = abi.encodePacked(_in, _fee1, _in2, _fee2, _out);
    ISwapRouter(V3_ROUTER).exactInput(
      ISwapRouter.ExactInputParams({
        path: _path,
        recipient: address(this),
        deadline: block.timestamp,
        amountIn: _amountIn,
        amountOutMinimum: _amountOutMin
      })
    );
    return IERC20(_out).balanceOf(address(this)) - _outBefore;
  }

  function _swapV2(
    address[] memory _path,
    uint256 _amountIn,
    uint256 _amountOutMin
  ) internal returns (uint256) {
    address _out = _path.length == 3 ? _path[2] : _path[1];
    uint256 _outBefore = IERC20(_out).balanceOf(address(this));
    IERC20(_path[0]).safeIncreaseAllowance(V2_ROUTER, _amountIn);
    IUniswapV2Router02(V2_ROUTER)
      .swapExactTokensForTokensSupportingFeeOnTransferTokens(
        _amountIn,
        _amountOutMin,
        _path,
        address(this),
        block.timestamp
      );
    return IERC20(_out).balanceOf(address(this)) - _outBefore;
  }

  function _swapCurve(
    address _pool,
    int128 _i,
    int128 _j,
    uint256 _amountIn,
    uint256 _amountOutMin
  ) internal returns (uint256) {
    return
      ICurvePool(_pool).exchange(
        _i,
        _j,
        _amountIn,
        _amountOutMin,
        address(this)
      );
  }

  function _wethToYeth(
    uint256 _ethAmount,
    uint256 _minYethAmount,
    bool _stakeToStyeth
  ) internal returns (uint256 _amountOut) {
    uint256 _boughtYeth = _swapCurve(
      WETH_YETH_POOL,
      0,
      1,
      _ethAmount,
      _minYethAmount
    );
    if (_stakeToStyeth) {
      return IERC4626(STYETH).deposit(_boughtYeth, address(this));
    }
    return _boughtYeth;
  }

  function _styethToWeth(
    uint256 _stYethAmount,
    uint256 _minWethAmount,
    bool _isYethOnly
  ) internal returns (uint256 _amountOut) {
    uint256 _yethAmount;
    if (_isYethOnly) {
      _yethAmount = _stYethAmount;
    } else {
      _yethAmount = IERC4626(STYETH).withdraw(_stYethAmount, address(this));
    }
    return _swapCurve(WETH_YETH_POOL, 1, 0, _yethAmount, _minWethAmount);
  }

  function _setZapMapFromPoolSingle(PoolType _type, address _pool) internal {
    address _t0;
    address _t1;
    if (_type == PoolType.CURVE) {
      _t0 = ICurvePool(_pool).coins(0);
      _t1 = ICurvePool(_pool).coins(1);
      curveTokenIdx[_pool][_t0] = 0;
      curveTokenIdx[_pool][_t1] = 1;
    } else {
      _t0 = IUniswapV3Pool(_pool).token0();
      _t1 = IUniswapV3Pool(_pool).token1();
    }
    Pools memory _poolConf = Pools({
      poolType: _type,
      pool1: _pool,
      pool2: address(0)
    });
    zapMap[_t0][_t1] = _poolConf;
    zapMap[_t1][_t0] = _poolConf;
  }

  function setPOHM(address _pOHM) external onlyOwner {
    pOHM = _pOHM;
  }

  function setSlippage(uint256 _slip) external onlyOwner {
    _slippage = _slip;
  }

  function setZapMap(
    address _in,
    address _out,
    Pools memory _pools
  ) external onlyOwner {
    zapMap[_in][_out] = _pools;
  }

  function setZapMapFromPoolSingle(
    PoolType _type,
    address _pool
  ) external onlyOwner {
    _setZapMapFromPoolSingle(_type, _pool);
  }

  function rescueETH() external onlyOwner {
    (bool _sent, ) = payable(owner()).call{ value: address(this).balance }('');
    require(_sent);
  }

  function rescueERC20(IERC20 _token) external onlyOwner {
    require(_token.balanceOf(address(this)) > 0);
    _token.safeTransfer(owner(), _token.balanceOf(address(this)));
  }

  receive() external payable {}
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_v2Router","type":"address"},{"internalType":"contract IV3TwapUtilities","name":"_v3TwapUtilities","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"contract IDecentralizedIndex","name":"_indexFund","type":"address"},{"internalType":"uint256","name":"_amountIdxTokens","type":"uint256"},{"internalType":"address","name":"_pairedLpTokenProvided","type":"address"},{"internalType":"uint256","name":"_amtPairedLpTokenProvided","type":"uint256"},{"internalType":"uint256","name":"_amountPairedLpTokenMin","type":"uint256"},{"internalType":"uint256","name":"_slippage","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"addLPAndStake","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDecentralizedIndex","name":"_indexFund","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_amountMintMin","type":"uint256"}],"name":"bond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IDecentralizedIndex","name":"_indexFund","type":"address"},{"internalType":"uint256","name":"_assetIdx","type":"uint256"},{"internalType":"uint256","name":"_amountTokensForAssetIdx","type":"uint256"},{"internalType":"uint256","name":"_amountMintMin","type":"uint256"},{"internalType":"uint256","name":"_amountPairedLpTokenMin","type":"uint256"},{"internalType":"uint256","name":"_slippage","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"bool","name":"_stakeAsWell","type":"bool"}],"name":"bondWeightedFromNative","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_rewards","type":"address[]"}],"name":"claimRewardsMulti","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"curveTokenIdx","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pOHM","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"}],"name":"rescueERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rescueETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pOHM","type":"address"}],"name":"setPOHM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_slip","type":"uint256"}],"name":"setSlippage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_in","type":"address"},{"internalType":"address","name":"_out","type":"address"},{"components":[{"internalType":"enum IZapper.PoolType","name":"poolType","type":"uint8"},{"internalType":"address","name":"pool1","type":"address"},{"internalType":"address","name":"pool2","type":"address"}],"internalType":"struct IZapper.Pools","name":"_pools","type":"tuple"}],"name":"setZapMap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum IZapper.PoolType","name":"_type","type":"uint8"},{"internalType":"address","name":"_pool","type":"address"}],"name":"setZapMapFromPoolSingle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IDecentralizedIndex","name":"_indexFund","type":"address"},{"internalType":"uint256","name":"_amountStakedTokens","type":"uint256"},{"internalType":"uint256","name":"_minLPTokens","type":"uint256"},{"internalType":"uint256","name":"_minPairedLpToken","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"unstakeAndRemoveLP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"zapMap","outputs":[{"internalType":"enum IZapper.PoolType","name":"poolType","type":"uint8"},{"internalType":"address","name":"pool1","type":"address"},{"internalType":"address","name":"pool2","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60e0604052601e6001553480156200001657600080fd5b506040516200639f3803806200639f833981016040819052620000399162000551565b8181620000463362000179565b6001600160a01b03808316608081905290821660c052604080516315ab88c960e31b8152905163ad5c4648916004808201926020929091908290030181865afa15801562000098573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000be919062000590565b6001600160a01b031660a052620000eb60007369accb968b19a53790f43e57558f5e443a91af22620001c9565b6200010c60027360594a405d53811d3bc4766596efd80fd545a270620001c9565b6200012d60027388e6a0c2ddd26feeb64f039a2c41296fcb3f5640620001c9565b6200014e60027388051b0eea095007d3bef21ab287be961f3d8598620001c9565b6200016f600273893f503fac2ee1e5b78665db23f9c94017aae97d620001c9565b50505050620005cd565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008080846002811115620001e257620001e2620005b7565b03620003185760405163c661065760e01b8152600060048201526001600160a01b0384169063c6610657906024016020604051808303816000875af115801562000230573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000256919062000590565b60405163c661065760e01b8152600160048201529092506001600160a01b0384169063c6610657906024016020604051808303816000875af1158015620002a1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002c7919062000590565b6001600160a01b038481166000908152600460209081526040808320878516845290915280822080546001600160801b031990811690915592841682529020805490911660011790559050620003e7565b826001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000357573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200037d919062000590565b9150826001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003be573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003e4919062000590565b90505b60006040518060600160405280866002811115620004095762000409620005b7565b81526001600160a01b0380871660208084019190915260006040938401819052878316815260038252838120928716815291905220815181549293508392829060ff19166001836002811115620004645762000464620005b7565b02179055506020828101518254610100600160a81b0319166101006001600160a01b0392831602178355604093840151600193840180546001600160a01b0319169183169190911790558581166000908152600383528481209188168152915291909120825181548493839160ff191690836002811115620004ea57620004ea620005b7565b021790555060208201518154610100600160a81b0319166101006001600160a01b0392831602178255604090920151600190910180546001600160a01b031916919092161790555050505050565b6001600160a01b03811681146200054e57600080fd5b50565b600080604083850312156200056557600080fd5b8251620005728162000538565b6020840151909250620005858162000538565b809150509250929050565b600060208284031215620005a357600080fd5b8151620005b08162000538565b9392505050565b634e487b7160e01b600052602160045260246000fd5b60805160a05160c051615d4e6200065160003960008181614a620152614bd2015260008181612acd01528181612b7001528181612c58015281816142b40152818161432301526143aa015260008181610903015281816122cb01528181613bb901528181614658015281816146c20152818161517501526152610152615d4e6000f3fe6080604052600436106101025760003560e01c8063a1879d9011610095578063d71f11fb11610064578063d71f11fb146102b2578063e42f5343146102d2578063e679daf514610337578063f0fa55a91461034a578063f2fde38b1461036a57600080fd5b8063a1879d9014610232578063ae7eb64614610252578063b2dce9ef14610272578063ccec37161461029257600080fd5b8063715018a6116100d1578063715018a614610185578063816846c81461019a57806384614e1a146101ed5780638da5cb5b1461020057600080fd5b80631fd8ecd91461010e57806320800a001461013057806338e781111461014557806353eedc671461016557600080fd5b3661010957005b600080fd5b34801561011a57600080fd5b5061012e6101293660046154ac565b61038a565b005b34801561013c57600080fd5b5061012e6107cf565b34801561015157600080fd5b5061012e610160366004615504565b61083a565b34801561017157600080fd5b5061012e6101803660046155ab565b610850565b34801561019157600080fd5b5061012e6108eb565b3480156101a657600080fd5b506101d56101b5366004615659565b6004602090815260009283526040808420909152908252902054600f0b81565b604051600f9190910b81526020015b60405180910390f35b61012e6101fb366004615677565b6108ff565b34801561020c57600080fd5b506000546001600160a01b03165b6040516001600160a01b0390911681526020016101e4565b34801561023e57600080fd5b5061012e61024d366004615700565b6113dc565b34801561025e57600080fd5b5060025461021a906001600160a01b031681565b34801561027e57600080fd5b5061012e61028d366004615794565b611483565b34801561029e57600080fd5b5061012e6102ad366004615794565b6114ad565b3480156102be57600080fd5b5061012e6102cd3660046157b1565b6115b8565b3480156102de57600080fd5b506103286102ed366004615659565b60036020908152600092835260408084209091529082529020805460019091015460ff8216916001600160a01b036101009091048116911683565b6040516101e49392919061580d565b61012e61034536600461585b565b611eda565b34801561035657600080fd5b5061012e6103653660046158ce565b612172565b34801561037657600080fd5b5061012e610385366004615794565b61217f565b6000856001600160a01b03166394cc699e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ee91906158e7565b90506000866001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610430573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045491906158e7565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561049e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104c29190615904565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561050c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105309190615904565b90506105476001600160a01b03851633308b6121f5565b6040516370a0823160e01b81523060048201526000906105ce908b90879086906001600160a01b038316906370a0823190602401602060405180830381865afa158015610598573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105bc9190615904565b6105c69190615933565b8b8b8b612260565b6040516370a0823160e01b815230600482015290915081906001600160a01b038c16906370a0823190602401602060405180830381865afa158015610617573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063b9190615904565b11156106ca576106ca336040516370a0823160e01b815230600482015283906001600160a01b038e16906370a0823190602401602060405180830381865afa15801561068b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106af9190615904565b6106b99190615933565b6001600160a01b038d169190612673565b6040516370a0823160e01b815230600482015282906001600160a01b038616906370a0823190602401602060405180830381865afa158015610710573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107349190615904565b11156107c3576107c3336040516370a0823160e01b815230600482015284906001600160a01b038816906370a0823190602401602060405180830381865afa158015610784573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a89190615904565b6107b29190615933565b6001600160a01b0387169190612673565b50505050505050505050565b6107d76126a8565b600080546040516001600160a01b039091169047908381818185875af1925050503d8060008114610824576040519150601f19603f3d011682016040523d82523d6000602084013e610829565b606091505b505090508061083757600080fd5b50565b6108426126a8565b61084c8282612702565b5050565b6108586126a8565b6001600160a01b0380841660009081526003602090815260408083209386168352929052208151815483929190829060ff1916600183600281111561089f5761089f6157f7565b021790555060208201518154610100600160a81b0319166101006001600160a01b0392831602178255604090920151600190910180546001600160a01b03191691909216179055505050565b6108f36126a8565b6108fd6000612a60565b565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa15801561095f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061098391906158e7565b6001600160a01b031663e6a43905898a6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f491906158e7565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015291166024820152604401602060405180830381865afa158015610a3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6391906158e7565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038a16906370a0823190602401602060405180830381865afa158015610aad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad19190615904565b90506000896001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3791906158e7565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015610b7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba19190615904565b90506000610baf3447615933565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038616906370a0823190602401602060405180830381865afa158015610bf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1d9190615904565b9050610c346001600160a01b038d1633308e6121f5565b6001600160a01b038a16610c895760003411610c815760405162461bcd60e51b815260206004820152600760248201526609c8a8a888aa8960cb1b60448201526064015b60405180910390fd5b349850610c9e565b610c9e6001600160a01b038b1633308c6121f5565b8b6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cdc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d0091906158e7565b6001600160a01b03168a6001600160a01b031614610d8757610d858a8d6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e91906158e7565b8b8b612ab0565b505b610ed48c848e6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dee91906158e7565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015610e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e589190615904565b610e629190615933565b8e6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ea0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ec491906158e7565b6001600160a01b031691906134c0565b6040516370a0823160e01b81523060048201526001600160a01b038d169063a9e9c8bc90869083906370a0823190602401602060405180830381865afa158015610f22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f469190615904565b610f509190615933565b858f6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb391906158e7565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015610ff9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101d9190615904565b6110279190615933565b6040516001600160e01b031960e085901b16815260048101929092526024820152604481018a905260648101899052608401600060405180830381600087803b15801561107357600080fd5b505af1158015611087573d6000803e3d6000fd5b505050506111758c6001600160a01b03166394cc699e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f091906158e7565b6040516370a0823160e01b815230600482015283906001600160a01b038916906370a0823190602401602060405180830381865afa158015611136573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115a9190615904565b6111649190615933565b6001600160a01b03881691906134c0565b8b6001600160a01b03166394cc699e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d791906158e7565b6001600160a01b031663adc9772e336040516370a0823160e01b815230600482015284906001600160a01b038a16906370a0823190602401602060405180830381865afa15801561122c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112509190615904565b61125a9190615933565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b1580156112a057600080fd5b505af11580156112b4573d6000803e3d6000fd5b5050505081471115611357576000336112cd8447615933565b604051600081818185875af1925050503d8060008114611309576040519150601f19603f3d011682016040523d82523d6000602084013e61130e565b606091505b5050905080801561131f5750824710155b6113555760405162461bcd60e51b81526020600482015260076024820152660a89e9e9aaa86960cb1b6044820152606401610c78565b505b611362338d8661356d565b6113ce338d6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c891906158e7565b8561356d565b505050505050505050505050565b60005b815181101561084c578181815181106113fa576113fa61594c565b60200260200101516001600160a01b031663d279c1916114173390565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401600060405180830381600087803b15801561145857600080fd5b505af115801561146c573d6000803e3d6000fd5b50505050808061147b90615962565b9150506113df565b61148b6126a8565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6114b56126a8565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156114fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115209190615904565b1161152a57600080fd5b61083761153f6000546001600160a01b031690565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015611583573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a79190615904565b6001600160a01b0384169190612673565b6000846001600160a01b03166353f504476040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161c919061597b565b600181111561162d5761162d6157f7565b03611ca3576000846001600160a01b0316632acada4d6040518163ffffffff1660e01b8152600401600060405180830381865afa158015611672573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261169a919081019061599c565b90506000815167ffffffffffffffff8111156116b8576116b861553b565b6040519080825280602002602001820160405280156116e1578160200160208202803683370190505b506040516370a0823160e01b81526001600160a01b0388811660048301529192506000918716906370a0823190602401602060405180830381865afa15801561172e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117529190615904565b90506000876001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117b89190615904565b156117db57816117cc600160601b88615a80565b6117d69190615a97565b6117e1565b600160601b5b905060005b8451811015611ad9576000896001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561182f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118539190615904565b1561190057600160601b838784815181106118705761187061594c565b6020908102919091010151516040516370a0823160e01b81526001600160a01b038e81166004830152909116906370a0823190602401602060405180830381865afa1580156118c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e79190615904565b6118f19190615a80565b6118fb9190615a97565b6119a8565b896001600160a01b031663e4b549578a8a8986815181106119235761192361594c565b6020908102919091010151516040516001600160e01b031960e086901b1681526001600160a01b0393841660048201526024810192909252919091166044820152606401602060405180830381865afa158015611984573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a89190615904565b90508582815181106119bc576119bc61594c565b6020908102919091010151516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611a0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a319190615904565b858381518110611a4357611a4361594c565b6020908102919091010152611a8b333083898681518110611a6657611a6661594c565b6020026020010151600001516001600160a01b03166121f5909392919063ffffffff16565b611ac68a82888581518110611aa257611aa261594c565b6020026020010151600001516001600160a01b03166134c09092919063ffffffff16565b5080611ad181615962565b9150506117e6565b506040516370a0823160e01b81523060048201526000906001600160a01b038a16906370a0823190602401602060405180830381865afa158015611b21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b459190615904565b60405163b08d033360e01b81529091506001600160a01b038a169063b08d033390611b78908b908b908b90600401615ab9565b600060405180830381600087803b158015611b9257600080fd5b505af1158015611ba6573d6000803e3d6000fd5b50505050611c39611bb43390565b6040516370a0823160e01b815230600482015283906001600160a01b038d16906370a0823190602401602060405180830381865afa158015611bfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1e9190615904565b611c289190615933565b6001600160a01b038c169190612673565b60005b8551811015611c9857611c8633878381518110611c5b57611c5b61594c565b602002602001015160000151878481518110611c7957611c7961594c565b602002602001015161356d565b80611c9081615962565b915050611c3c565b505050505050611ed4565b6001846001600160a01b03166353f504476040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ce3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d07919061597b565b6001811115611d1857611d186157f7565b14611d4a5760405162461bcd60e51b8152602060048201526002602482015261555760f01b6044820152606401610c78565b611d5f6001600160a01b0384163330856121f5565b611d736001600160a01b03841685846134c0565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015611dba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dde9190615904565b60405163b08d033360e01b81529091506001600160a01b0386169063b08d033390611e1190879087908790600401615ab9565b600060405180830381600087803b158015611e2b57600080fd5b505af1158015611e3f573d6000803e3d6000fd5b50505050611ed2611e4d3390565b6040516370a0823160e01b815230600482015283906001600160a01b038916906370a0823190602401602060405180830381865afa158015611e93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb79190615904565b611ec19190615933565b6001600160a01b0388169190612673565b505b50505050565b60003411611f135760405162461bcd60e51b81526020600482015260066024820152654e415449564560d01b6044820152606401610c78565b6000611f1f3447615933565b90506000896001600160a01b0316632acada4d6040518163ffffffff1660e01b8152600401600060405180830381865afa158015611f61573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f89919081019061599c565b9050600080611fb08c86611f9d5734611fa8565b611fa8600234615a97565b858e8e613601565b9150915060005b835181101561200957808c14611ff757611ff78d838381518110611fdd57611fdd61594c565b6020026020010151868481518110611aa257611aa261594c565b8061200181615962565b915050611fb7565b50600061205a8d858e815181106120225761202261594c565b602002602001015160000151848f815181106120405761204061594c565b60200260200101518d8a6120545733613866565b30613866565b9050851561207b5761207b338e83612073600234615a97565b8d8d8d6139f3565b60005b84518110156120cd576120bb3386838151811061209d5761209d61594c565b602002602001015160000151868481518110611c7957611c7961594c565b806120c581615962565b91505061207e565b5084471115612163576000336120e38747615933565b604051600081818185875af1925050503d806000811461211f576040519150601f19603f3d011682016040523d82523d6000602084013e612124565b606091505b50509050806121615760405162461bcd60e51b815260206004820152600960248201526811551214915195539160ba1b6044820152606401610c78565b505b50505050505050505050505050565b61217a6126a8565b600155565b6121876126a8565b6001600160a01b0381166121ec5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610c78565b61083781612a60565b6040516001600160a01b0380851660248301528316604482015260648101829052611ed49085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526141bd565b600080876001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c591906158e7565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015612327573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061234b91906158e7565b60405163e6a4390560e01b81526001600160a01b038b811660048301528481166024830152919091169063e6a4390590604401602060405180830381865afa15801561239b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bf91906158e7565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015612409573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061242d9190615904565b6040516305c2fbcf60e31b8152600481018a90529091506001600160a01b038a1690632e17de7890602401600060405180830381600087803b15801561247257600080fd5b505af1158015612486573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201526001600160a01b038d1692506370a082319150602401602060405180830381865afa1580156124ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f29190615904565b6040516370a0823160e01b8152306004820152909450612581908b9083906001600160a01b038616906370a08231906024015b602060405180830381865afa158015612542573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125669190615904565b6125709190615933565b6001600160a01b03851691906134c0565b6040516370a0823160e01b81523060048201526001600160a01b03808c169163f6823996918491908616906370a0823190602401602060405180830381865afa1580156125d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125f69190615904565b6126009190615933565b6040516001600160e01b031960e084901b1681526004810191909152602481018a90526044810189905260648101889052608401600060405180830381600087803b15801561264e57600080fd5b505af1158015612662573d6000803e3d6000fd5b505050505050509695505050505050565b6040516001600160a01b0383166024820152604481018290526126a390849063a9059cbb60e01b90606401612229565b505050565b6000546001600160a01b031633146108fd5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610c78565b60008080846002811115612718576127186157f7565b0361284f5760405163c661065760e01b8152600060048201526001600160a01b0384169063c6610657906024016020604051808303816000875af1158015612764573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061278891906158e7565b60405163c661065760e01b8152600160048201529092506001600160a01b0384169063c6610657906024016020604051808303816000875af11580156127d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f691906158e7565b6001600160a01b038481166000908152600460209081526040808320878516845290915280822080546fffffffffffffffffffffffffffffffff1990811690915592841682529020805490911660011790559050612918565b826001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa15801561288d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128b191906158e7565b9150826001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291591906158e7565b90505b60006040518060600160405280866002811115612937576129376157f7565b81526001600160a01b0380871660208084019190915260006040938401819052878316815260038252838120928716815291905220815181549293508392829060ff1916600183600281111561298f5761298f6157f7565b02179055506020828101518254610100600160a81b0319166101006001600160a01b0392831602178355604093840151600193840180546001600160a01b0319169183169190911790558581166000908152600383528481209188168152915291909120825181548493839160ff191690836002811115612a1257612a126157f7565b021790555060208201518154610100600160a81b0319166101006001600160a01b0392831602178255604090920151600190910180546001600160a01b031916919092161790555050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006001600160a01b038516612aef57612ac983614292565b92507f000000000000000000000000000000000000000000000000000000000000000094505b6002546000906001600160a01b03808716911603612b2257507364aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5935060015b6001600160a01b038516731bed97cbc3c24a4fb5c069c6e311a967386131f71480612b6957506001600160a01b03851673583019ff0f430721ada9cfb4fac8f06ca104d0b4145b15612c0a577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316866001600160a01b031614612bd85760405162461bcd60e51b81526020600482015260066024820152652ca2aa2424a760d11b6044820152606401610c78565b612c0284846001600160a01b03881673583019ff0f430721ada9cfb4fac8f06ca104d0b414614430565b9150506134b8565b6001600160a01b038616731bed97cbc3c24a4fb5c069c6e311a967386131f71480612c5157506001600160a01b03861673583019ff0f430721ada9cfb4fac8f06ca104d0b4145b15612ceb577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316856001600160a01b031614612cc15760405162461bcd60e51b81526020600482015260076024820152661651551213d55560ca1b6044820152606401610c78565b612c0284846001600160a01b038916731bed97cbc3c24a4fb5c069c6e311a967386131f7146144e2565b6001600160a01b03808716600090815260036020908152604080832093891683529290528181208251606081019093528054919291829060ff166002811115612d3657612d366157f7565b6002811115612d4757612d476157f7565b815281546001600160a01b03610100909104811660208084019190915260019093015481166040909201919091529082015191925016612e1d576040805160028082526060820183526000926020830190803683370190505090508781600081518110612db657612db661594c565b60200260200101906001600160a01b031690816001600160a01b0316815250508681600181518110612dea57612dea61594c565b60200260200101906001600160a01b031690816001600160a01b031681525050612e1581878761459b565b935050613317565b60408101516001600160a01b03161515600082516002811115612e4257612e426157f7565b03612ea757602080830180516001600160a01b038082166000908152600480865260408083208f85168452875280832054955184168352908652808220928d1682529190945290922054612ea09291600f90810b91900b89896147af565b9350613315565b600182516002811115612ebc57612ebc6157f7565b036130cb57600082602001516001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f2991906158e7565b9050600082612f39576002612f3c565b60035b60ff1667ffffffffffffffff811115612f5757612f5761553b565b604051908082528060200260200182016040528015612f80578160200160208202803683370190505b5090508981600081518110612f9757612f9761594c565b60200260200101906001600160a01b031690816001600160a01b031681525050821561304757896001600160a01b0316826001600160a01b031614612fdc5781613049565b83602001516001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561301e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304291906158e7565b613049565b885b8160018151811061305c5761305c61594c565b60200260200101906001600160a01b031690816001600160a01b03168152505082156130b75788816002815181106130965761309661594c565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6130c281898961459b565b95505050613315565b80156132a057600082602001516001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015613115573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061313991906158e7565b90506132988984602001516001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015613181573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131a59190615ada565b8b6001600160a01b0316846001600160a01b0316146131c4578361322a565b85602001516001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015613206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061322a91906158e7565b86604001516001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa15801561326c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132909190615ada565b8c8c8c61483c565b945050613315565b6133128883602001516001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061330a9190615ada565b898989614a54565b93505b505b816133235750506134b8565b6002546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa15801561336c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133909190615904565b6002549091506133bf907364aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5906001600160a01b0316866134c0565b60025460405163b08d033360e01b81526001600160a01b039091169063b08d033390613408907364aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5908890600090600401615ab9565b600060405180830381600087803b15801561342257600080fd5b505af1158015613436573d6000803e3d6000fd5b50506002546040516370a0823160e01b81523060048201528493506001600160a01b0390911691506370a0823190602401602060405180830381865afa158015613484573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134a89190615904565b6134b29190615933565b93505050505b949350505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015613510573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135349190615904565b9050611ed48463095ea7b360e01b8561354d8686615aff565b6040516001600160a01b0390921660248301526044820152606401612229565b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156135b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135d89190615904565b905081811115611ed457611ed4846135f08484615933565b6001600160a01b0386169190612673565b6060806000855167ffffffffffffffff8111156136205761362061553b565b604051908082528060200260200182016040528015613649578160200160208202803683370190505b5090506000865167ffffffffffffffff8111156136685761366861553b565b604051908082528060200260200182016040528015613691578160200160208202803683370190505b50905060008787815181106136a8576136a861594c565b6020908102919091010151516040516370a0823160e01b81526001600160a01b038c81166004830152909116906370a0823190602401602060405180830381865afa1580156136fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061371f9190615904565b905060008a6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613761573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137859190615904565b156137a85781613799600160601b89615a80565b6137a39190615a97565b6137ae565b600160601b5b90508960005b8a51811015613853576138058d8c8c815181106137d3576137d361594c565b6020026020010151600001518b8e85815181106137f2576137f261594c565b6020026020010151600001518787614f6d565b8884815181106138175761381761594c565b602002602001018885815181106138305761383061594c565b60209081029190910101919091525291508061384b81615962565b9150506137b4565b50939b929a509198505050505050505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038816906370a0823190602401602060405180830381865afa1580156138af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138d39190615904565b90506138e96001600160a01b03871688876134c0565b60405163b08d033360e01b81526001600160a01b0388169063b08d03339061391990899089908990600401615ab9565b600060405180830381600087803b15801561393357600080fd5b505af1158015613947573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092508391506001600160a01b038a16906370a0823190602401602060405180830381865afa158015613994573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139b89190615904565b6139c29190615933565b90506001600160a01b03841630146139e8576139e86001600160a01b0389168583612673565b979650505050505050565b6000866001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5791906158e7565b6040516370a0823160e01b815230600482015290915060009087906001600160a01b038a16906370a0823190602401602060405180830381865afa158015613aa3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ac79190615904565b613ad19190615933565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa158015613b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b3f9190615904565b90506000896001600160a01b03166394cc699e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613b81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ba591906158e7565b9050613bb46000858a8a612ab0565b5060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c3991906158e7565b60405163e6a4390560e01b81526001600160a01b038d811660048301528781166024830152919091169063e6a4390590604401602060405180830381865afa158015613c89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cad91906158e7565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015613cf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d1b9190615904565b6040516370a0823160e01b8152306004820152909150613da9908d9086906001600160a01b038a16906370a0823190602401602060405180830381865afa158015613d6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d8e9190615904565b613d989190615933565b6001600160a01b03891691906134c0565b6040516370a0823160e01b81523060048201526001600160a01b03808e169163a9e9c8bc918e9188918b16906370a0823190602401602060405180830381865afa158015613dfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e1f9190615904565b613e299190615933565b6040516001600160e01b031960e085901b16815260048101929092526024820152604481018b9052606481018a9052608401600060405180830381600087803b158015613e7557600080fd5b505af1158015613e89573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152613ec1925085915083906001600160a01b038616906370a0823190602401612525565b826001600160a01b031663adc9772e8e83856001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401613f0d91906001600160a01b0391909116815260200190565b602060405180830381865afa158015613f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f4e9190615904565b613f589190615933565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015613f9e57600080fd5b505af1158015613fb2573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201528792506001600160a01b038f1691506370a0823190602401602060405180830381865afa158015613ffc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140209190615904565b11156140c2576140c28d868e6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161406691906001600160a01b0391909116815260200190565b602060405180830381865afa158015614083573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140a79190615904565b6140b19190615933565b6001600160a01b038f169190612673565b6040516370a0823160e01b815230600482015284906001600160a01b038816906370a0823190602401602060405180830381865afa158015614108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061412c9190615904565b1115612163576040516370a0823160e01b8152306004820152612163908e9086906001600160a01b038a16906370a0823190602401602060405180830381865afa15801561417e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141a29190615904565b6141ac9190615933565b6001600160a01b0389169190612673565b6000614212826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166153849092919063ffffffff16565b90508051600014806142335750808060200190518101906142339190615b12565b6126a35760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610c78565b6040516370a0823160e01b815230600482015260009081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156142fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061431f9190615904565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561437c57600080fd5b505af1158015614390573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201528493507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692506370a082319150602401602060405180830381865afa1580156143fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061441f9190615904565b6144299190615933565b9392505050565b6000806144567369accb968b19a53790f43e57558f5e443a91af226000600188886147af565b905082156134b857604051636e553f6560e01b81526004810182905230602482015273583019ff0f430721ada9cfb4fac8f06ca104d0b490636e553f65906044016020604051808303816000875af11580156144b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144da9190615904565b915050614429565b60008082156144f257508361456f565b604051627b8a6760e11b81526004810186905230602482015273583019ff0f430721ada9cfb4fac8f06ca104d0b49062f714ce906044016020604051808303816000875af1158015614548573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061456c9190615904565b90505b6145927369accb968b19a53790f43e57558f5e443a91af226001600084886147af565b95945050505050565b60008084516003146145c757846001815181106145ba576145ba61594c565b60200260200101516145e3565b846002815181106145da576145da61594c565b60200260200101515b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561462d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146519190615904565b90506146ab7f0000000000000000000000000000000000000000000000000000000000000000868860008151811061468b5761468b61594c565b60200260200101516001600160a01b03166134c09092919063ffffffff16565b604051635c11d79560e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690635c11d795906146ff90889088908b9030904290600401615b73565b600060405180830381600087803b15801561471957600080fd5b505af115801561472d573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201528392506001600160a01b03851691506370a0823190602401602060405180830381865afa158015614777573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061479b9190615904565b6147a59190615933565b9695505050505050565b60405163ddc1f59d60e01b8152600f85810b600483015284900b602482015260448101839052606481018290523060848201526000906001600160a01b0387169063ddc1f59d9060a4016020604051808303816000875af1158015614818573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147a59190615904565b6040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa158015614885573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148a99190615904565b90506148d36001600160a01b038a1673e592427a0aece92de3edee1f18e0157c05861564866134c0565b6040516bffffffffffffffffffffffff1960608b811b821660208401526001600160e81b031960e88c811b821660348601528b831b841660378601528a901b16604b84015287901b16604e82015260009060620160408051601f1981840301815260a08301825280835230602084015242838301526060830188905260808301879052905163c04b8d5960e01b815290925073e592427a0aece92de3edee1f18e0157c058615649163c04b8d599161498e9190600401615bff565b6020604051808303816000875af11580156149ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149d19190615904565b506040516370a0823160e01b815230600482015282906001600160a01b038816906370a0823190602401602060405180830381865afa158015614a18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a3c9190615904565b614a469190615933565b9a9950505050505050505050565b600081600003614d055760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634556bd2073e592427a0aece92de3edee1f18e0157c058615646001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015614ae1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b0591906158e7565b6040516001600160e01b031960e084901b1681526001600160a01b039182166004820152818b166024820152908816604482015262ffffff89166064820152608401602060405180830381865afa158015614b64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b8891906158e7565b90506000856001600160a01b0316886001600160a01b031610614bab5785614bad565b875b604051637fb4f79d60e01b81526001600160a01b0384811660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063d4bf1334908290637fb4f79d90602401602060405180830381865afa158015614c21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c4591906158e7565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015614c89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cad9190615904565b9050816001600160a01b0316896001600160a01b031614614ce65780614cd7600160601b88615a80565b614ce19190615a97565b614cff565b600160601b614cf58783615a80565b614cff9190615a97565b94505050505b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015614d4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614d709190615904565b9050614d9a6001600160a01b03881673e592427a0aece92de3edee1f18e0157c05861564866134c0565b73e592427a0aece92de3edee1f18e0157c058615646001600160a01b031663414bf3896040518061010001604052808a6001600160a01b03168152602001886001600160a01b031681526020018962ffffff168152602001306001600160a01b031681526020014281526020018781526020016103e86001546103e8614e209190615933565b614e2a9089615a80565b614e349190615a97565b815260006020918201526040805160e085811b6001600160e01b031916825284516001600160a01b03908116600484015293850151841660248301529184015162ffffff1660448201526060840151831660648201526080840151608482015260a084015160a482015260c084015160c48201529201511660e4820152610104016020604051808303816000875af1158015614ed4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614ef89190615904565b506040516370a0823160e01b815230600482015281906001600160a01b038716906370a0823190602401602060405180830381865afa158015614f3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614f639190615904565b6139e89190615933565b6040516370a0823160e01b81523060048201526000908190819047906001600160a01b038816906370a0823190602401602060405180830381865afa158015614fba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614fde9190615904565b925060008a6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015615020573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906150449190615904565b156150d4576040516370a0823160e01b81526001600160a01b038c81166004830152600160601b9189918b16906370a0823190602401602060405180830381865afa158015615097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906150bb9190615904565b6150c59190615a80565b6150cf9190615a97565b61514d565b60405163e4b5495760e01b81526001600160a01b038b81166004830152602482018b905289811660448301528c169063e4b5495790606401602060405180830381865afa158015615129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061514d9190615904565b60408051600280825260608201835292935060009290916020830190803683370190505090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa1580156151d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906151f591906158e7565b816000815181106152085761520861594c565b60200260200101906001600160a01b031690816001600160a01b031681525050888160018151811061523c5761523c61594c565b6001600160a01b03928316602091820292909201015260405163fb3bdb4160e01b81527f00000000000000000000000000000000000000000000000000000000000000009091169063fb3bdb419089906152a0908690869030904290600401615c57565b60006040518083038185885af11580156152be573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f191682016040526152e79190810190615c8c565b506152f24784615933565b6152fc9088615933565b6040516370a0823160e01b815230600482015290965085906001600160a01b038b16906370a0823190602401602060405180830381865afa158015615345573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906153699190615904565b6153739190615933565b935050505096509650969350505050565b60606134b8848460008585600080866001600160a01b031685876040516153ab9190615d12565b60006040518083038185875af1925050503d80600081146153e8576040519150601f19603f3d011682016040523d82523d6000602084013e6153ed565b606091505b50915091506139e88783838760608315615468578251600003615461576001600160a01b0385163b6154615760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610c78565b50816134b8565b6134b8838381511561547d5781518083602001fd5b8060405162461bcd60e51b8152600401610c789190615d2e565b6001600160a01b038116811461083757600080fd5b600080600080600060a086880312156154c457600080fd5b85356154cf81615497565b97602087013597506040870135966060810135965060800135945092505050565b8035600381106154ff57600080fd5b919050565b6000806040838503121561551757600080fd5b615520836154f0565b9150602083013561553081615497565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b60405160a0810167ffffffffffffffff811182821017156155745761557461553b565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156155a3576155a361553b565b604052919050565b600080600083850360a08112156155c157600080fd5b84356155cc81615497565b935060208501356155dc81615497565b92506060603f19820112156155f057600080fd5b506040516060810181811067ffffffffffffffff821117156156145761561461553b565b8060405250615625604086016154f0565b8152606085013561563581615497565b6020820152608085013561564881615497565b604082015292959194509192509050565b6000806040838503121561566c57600080fd5b823561552081615497565b600080600080600080600060e0888a03121561569257600080fd5b873561569d81615497565b96506020880135955060408801356156b481615497565b969995985095966060810135965060808101359560a0820135955060c0909101359350915050565b600067ffffffffffffffff8211156156f6576156f661553b565b5060051b60200190565b6000602080838503121561571357600080fd5b823567ffffffffffffffff81111561572a57600080fd5b8301601f8101851361573b57600080fd5b803561574e615749826156dc565b61557a565b81815260059190911b8201830190838101908783111561576d57600080fd5b928401925b828410156139e857833561578581615497565b82529284019290840190615772565b6000602082840312156157a657600080fd5b813561442981615497565b600080600080608085870312156157c757600080fd5b84356157d281615497565b935060208501356157e281615497565b93969395505050506040820135916060013590565b634e487b7160e01b600052602160045260246000fd5b606081016003851061582f57634e487b7160e01b600052602160045260246000fd5b9381526001600160a01b039283166020820152911660409091015290565b801515811461083757600080fd5b600080600080600080600080610100898b03121561587857600080fd5b883561588381615497565b97506020890135965060408901359550606089013594506080890135935060a0890135925060c0890135915060e08901356158bd8161584d565b809150509295985092959890939650565b6000602082840312156158e057600080fd5b5035919050565b6000602082840312156158f957600080fd5b815161442981615497565b60006020828403121561591657600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156159465761594661591d565b92915050565b634e487b7160e01b600052603260045260246000fd5b6000600182016159745761597461591d565b5060010190565b60006020828403121561598d57600080fd5b81516002811061442957600080fd5b600060208083850312156159af57600080fd5b825167ffffffffffffffff8111156159c657600080fd5b8301601f810185136159d757600080fd5b80516159e5615749826156dc565b81815260a09182028301840191848201919088841115615a0457600080fd5b938501935b83851015615a745780858a031215615a215760008081fd5b615a29615551565b8551615a3481615497565b8152858701518782015260408087015190820152606080870151615a5781615497565b908201526080868101519082015283529384019391850191615a09565b50979650505050505050565b80820281158282048414176159465761594661591d565b600082615ab457634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b039390931683526020830191909152604082015260600190565b600060208284031215615aec57600080fd5b815162ffffff8116811461442957600080fd5b808201808211156159465761594661591d565b600060208284031215615b2457600080fd5b81516144298161584d565b600081518084526020808501945080840160005b83811015615b685781516001600160a01b031687529582019590820190600101615b43565b509495945050505050565b85815284602082015260a060408201526000615b9260a0830186615b2f565b6001600160a01b0394909416606083015250608001529392505050565b60005b83811015615bca578181015183820152602001615bb2565b50506000910152565b60008151808452615beb816020860160208601615baf565b601f01601f19169290920160200192915050565b602081526000825160a06020840152615c1b60c0840182615bd3565b905060018060a01b0360208501511660408401526040840151606084015260608401516080840152608084015160a08401528091505092915050565b848152608060208201526000615c706080830186615b2f565b6001600160a01b03949094166040830152506060015292915050565b60006020808385031215615c9f57600080fd5b825167ffffffffffffffff811115615cb657600080fd5b8301601f81018513615cc757600080fd5b8051615cd5615749826156dc565b81815260059190911b82018301908381019087831115615cf457600080fd5b928401925b828410156139e857835182529284019290840190615cf9565b60008251615d24818460208701615baf565b9190910192915050565b6020815260006144296020830184615bd356fea164736f6c6343000813000a0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d000000000000000000000000024ff47d552cb222b265d68c7aeb26e586d5229d

Deployed Bytecode

0x6080604052600436106101025760003560e01c8063a1879d9011610095578063d71f11fb11610064578063d71f11fb146102b2578063e42f5343146102d2578063e679daf514610337578063f0fa55a91461034a578063f2fde38b1461036a57600080fd5b8063a1879d9014610232578063ae7eb64614610252578063b2dce9ef14610272578063ccec37161461029257600080fd5b8063715018a6116100d1578063715018a614610185578063816846c81461019a57806384614e1a146101ed5780638da5cb5b1461020057600080fd5b80631fd8ecd91461010e57806320800a001461013057806338e781111461014557806353eedc671461016557600080fd5b3661010957005b600080fd5b34801561011a57600080fd5b5061012e6101293660046154ac565b61038a565b005b34801561013c57600080fd5b5061012e6107cf565b34801561015157600080fd5b5061012e610160366004615504565b61083a565b34801561017157600080fd5b5061012e6101803660046155ab565b610850565b34801561019157600080fd5b5061012e6108eb565b3480156101a657600080fd5b506101d56101b5366004615659565b6004602090815260009283526040808420909152908252902054600f0b81565b604051600f9190910b81526020015b60405180910390f35b61012e6101fb366004615677565b6108ff565b34801561020c57600080fd5b506000546001600160a01b03165b6040516001600160a01b0390911681526020016101e4565b34801561023e57600080fd5b5061012e61024d366004615700565b6113dc565b34801561025e57600080fd5b5060025461021a906001600160a01b031681565b34801561027e57600080fd5b5061012e61028d366004615794565b611483565b34801561029e57600080fd5b5061012e6102ad366004615794565b6114ad565b3480156102be57600080fd5b5061012e6102cd3660046157b1565b6115b8565b3480156102de57600080fd5b506103286102ed366004615659565b60036020908152600092835260408084209091529082529020805460019091015460ff8216916001600160a01b036101009091048116911683565b6040516101e49392919061580d565b61012e61034536600461585b565b611eda565b34801561035657600080fd5b5061012e6103653660046158ce565b612172565b34801561037657600080fd5b5061012e610385366004615794565b61217f565b6000856001600160a01b03166394cc699e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ee91906158e7565b90506000866001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610430573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045491906158e7565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561049e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104c29190615904565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561050c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105309190615904565b90506105476001600160a01b03851633308b6121f5565b6040516370a0823160e01b81523060048201526000906105ce908b90879086906001600160a01b038316906370a0823190602401602060405180830381865afa158015610598573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105bc9190615904565b6105c69190615933565b8b8b8b612260565b6040516370a0823160e01b815230600482015290915081906001600160a01b038c16906370a0823190602401602060405180830381865afa158015610617573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063b9190615904565b11156106ca576106ca336040516370a0823160e01b815230600482015283906001600160a01b038e16906370a0823190602401602060405180830381865afa15801561068b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106af9190615904565b6106b99190615933565b6001600160a01b038d169190612673565b6040516370a0823160e01b815230600482015282906001600160a01b038616906370a0823190602401602060405180830381865afa158015610710573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107349190615904565b11156107c3576107c3336040516370a0823160e01b815230600482015284906001600160a01b038816906370a0823190602401602060405180830381865afa158015610784573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a89190615904565b6107b29190615933565b6001600160a01b0387169190612673565b50505050505050505050565b6107d76126a8565b600080546040516001600160a01b039091169047908381818185875af1925050503d8060008114610824576040519150601f19603f3d011682016040523d82523d6000602084013e610829565b606091505b505090508061083757600080fd5b50565b6108426126a8565b61084c8282612702565b5050565b6108586126a8565b6001600160a01b0380841660009081526003602090815260408083209386168352929052208151815483929190829060ff1916600183600281111561089f5761089f6157f7565b021790555060208201518154610100600160a81b0319166101006001600160a01b0392831602178255604090920151600190910180546001600160a01b03191691909216179055505050565b6108f36126a8565b6108fd6000612a60565b565b60007f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa15801561095f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061098391906158e7565b6001600160a01b031663e6a43905898a6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f491906158e7565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015291166024820152604401602060405180830381865afa158015610a3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6391906158e7565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038a16906370a0823190602401602060405180830381865afa158015610aad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad19190615904565b90506000896001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3791906158e7565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015610b7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba19190615904565b90506000610baf3447615933565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038616906370a0823190602401602060405180830381865afa158015610bf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1d9190615904565b9050610c346001600160a01b038d1633308e6121f5565b6001600160a01b038a16610c895760003411610c815760405162461bcd60e51b815260206004820152600760248201526609c8a8a888aa8960cb1b60448201526064015b60405180910390fd5b349850610c9e565b610c9e6001600160a01b038b1633308c6121f5565b8b6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cdc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d0091906158e7565b6001600160a01b03168a6001600160a01b031614610d8757610d858a8d6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e91906158e7565b8b8b612ab0565b505b610ed48c848e6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dee91906158e7565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015610e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e589190615904565b610e629190615933565b8e6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ea0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ec491906158e7565b6001600160a01b031691906134c0565b6040516370a0823160e01b81523060048201526001600160a01b038d169063a9e9c8bc90869083906370a0823190602401602060405180830381865afa158015610f22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f469190615904565b610f509190615933565b858f6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb391906158e7565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015610ff9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101d9190615904565b6110279190615933565b6040516001600160e01b031960e085901b16815260048101929092526024820152604481018a905260648101899052608401600060405180830381600087803b15801561107357600080fd5b505af1158015611087573d6000803e3d6000fd5b505050506111758c6001600160a01b03166394cc699e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f091906158e7565b6040516370a0823160e01b815230600482015283906001600160a01b038916906370a0823190602401602060405180830381865afa158015611136573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115a9190615904565b6111649190615933565b6001600160a01b03881691906134c0565b8b6001600160a01b03166394cc699e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d791906158e7565b6001600160a01b031663adc9772e336040516370a0823160e01b815230600482015284906001600160a01b038a16906370a0823190602401602060405180830381865afa15801561122c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112509190615904565b61125a9190615933565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b1580156112a057600080fd5b505af11580156112b4573d6000803e3d6000fd5b5050505081471115611357576000336112cd8447615933565b604051600081818185875af1925050503d8060008114611309576040519150601f19603f3d011682016040523d82523d6000602084013e61130e565b606091505b5050905080801561131f5750824710155b6113555760405162461bcd60e51b81526020600482015260076024820152660a89e9e9aaa86960cb1b6044820152606401610c78565b505b611362338d8661356d565b6113ce338d6001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c891906158e7565b8561356d565b505050505050505050505050565b60005b815181101561084c578181815181106113fa576113fa61594c565b60200260200101516001600160a01b031663d279c1916114173390565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401600060405180830381600087803b15801561145857600080fd5b505af115801561146c573d6000803e3d6000fd5b50505050808061147b90615962565b9150506113df565b61148b6126a8565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6114b56126a8565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156114fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115209190615904565b1161152a57600080fd5b61083761153f6000546001600160a01b031690565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015611583573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a79190615904565b6001600160a01b0384169190612673565b6000846001600160a01b03166353f504476040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161c919061597b565b600181111561162d5761162d6157f7565b03611ca3576000846001600160a01b0316632acada4d6040518163ffffffff1660e01b8152600401600060405180830381865afa158015611672573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261169a919081019061599c565b90506000815167ffffffffffffffff8111156116b8576116b861553b565b6040519080825280602002602001820160405280156116e1578160200160208202803683370190505b506040516370a0823160e01b81526001600160a01b0388811660048301529192506000918716906370a0823190602401602060405180830381865afa15801561172e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117529190615904565b90506000876001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117b89190615904565b156117db57816117cc600160601b88615a80565b6117d69190615a97565b6117e1565b600160601b5b905060005b8451811015611ad9576000896001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561182f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118539190615904565b1561190057600160601b838784815181106118705761187061594c565b6020908102919091010151516040516370a0823160e01b81526001600160a01b038e81166004830152909116906370a0823190602401602060405180830381865afa1580156118c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e79190615904565b6118f19190615a80565b6118fb9190615a97565b6119a8565b896001600160a01b031663e4b549578a8a8986815181106119235761192361594c565b6020908102919091010151516040516001600160e01b031960e086901b1681526001600160a01b0393841660048201526024810192909252919091166044820152606401602060405180830381865afa158015611984573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a89190615904565b90508582815181106119bc576119bc61594c565b6020908102919091010151516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611a0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a319190615904565b858381518110611a4357611a4361594c565b6020908102919091010152611a8b333083898681518110611a6657611a6661594c565b6020026020010151600001516001600160a01b03166121f5909392919063ffffffff16565b611ac68a82888581518110611aa257611aa261594c565b6020026020010151600001516001600160a01b03166134c09092919063ffffffff16565b5080611ad181615962565b9150506117e6565b506040516370a0823160e01b81523060048201526000906001600160a01b038a16906370a0823190602401602060405180830381865afa158015611b21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b459190615904565b60405163b08d033360e01b81529091506001600160a01b038a169063b08d033390611b78908b908b908b90600401615ab9565b600060405180830381600087803b158015611b9257600080fd5b505af1158015611ba6573d6000803e3d6000fd5b50505050611c39611bb43390565b6040516370a0823160e01b815230600482015283906001600160a01b038d16906370a0823190602401602060405180830381865afa158015611bfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1e9190615904565b611c289190615933565b6001600160a01b038c169190612673565b60005b8551811015611c9857611c8633878381518110611c5b57611c5b61594c565b602002602001015160000151878481518110611c7957611c7961594c565b602002602001015161356d565b80611c9081615962565b915050611c3c565b505050505050611ed4565b6001846001600160a01b03166353f504476040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ce3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d07919061597b565b6001811115611d1857611d186157f7565b14611d4a5760405162461bcd60e51b8152602060048201526002602482015261555760f01b6044820152606401610c78565b611d5f6001600160a01b0384163330856121f5565b611d736001600160a01b03841685846134c0565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015611dba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dde9190615904565b60405163b08d033360e01b81529091506001600160a01b0386169063b08d033390611e1190879087908790600401615ab9565b600060405180830381600087803b158015611e2b57600080fd5b505af1158015611e3f573d6000803e3d6000fd5b50505050611ed2611e4d3390565b6040516370a0823160e01b815230600482015283906001600160a01b038916906370a0823190602401602060405180830381865afa158015611e93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb79190615904565b611ec19190615933565b6001600160a01b0388169190612673565b505b50505050565b60003411611f135760405162461bcd60e51b81526020600482015260066024820152654e415449564560d01b6044820152606401610c78565b6000611f1f3447615933565b90506000896001600160a01b0316632acada4d6040518163ffffffff1660e01b8152600401600060405180830381865afa158015611f61573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f89919081019061599c565b9050600080611fb08c86611f9d5734611fa8565b611fa8600234615a97565b858e8e613601565b9150915060005b835181101561200957808c14611ff757611ff78d838381518110611fdd57611fdd61594c565b6020026020010151868481518110611aa257611aa261594c565b8061200181615962565b915050611fb7565b50600061205a8d858e815181106120225761202261594c565b602002602001015160000151848f815181106120405761204061594c565b60200260200101518d8a6120545733613866565b30613866565b9050851561207b5761207b338e83612073600234615a97565b8d8d8d6139f3565b60005b84518110156120cd576120bb3386838151811061209d5761209d61594c565b602002602001015160000151868481518110611c7957611c7961594c565b806120c581615962565b91505061207e565b5084471115612163576000336120e38747615933565b604051600081818185875af1925050503d806000811461211f576040519150601f19603f3d011682016040523d82523d6000602084013e612124565b606091505b50509050806121615760405162461bcd60e51b815260206004820152600960248201526811551214915195539160ba1b6044820152606401610c78565b505b50505050505050505050505050565b61217a6126a8565b600155565b6121876126a8565b6001600160a01b0381166121ec5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610c78565b61083781612a60565b6040516001600160a01b0380851660248301528316604482015260648101829052611ed49085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526141bd565b600080876001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c591906158e7565b905060007f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015612327573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061234b91906158e7565b60405163e6a4390560e01b81526001600160a01b038b811660048301528481166024830152919091169063e6a4390590604401602060405180830381865afa15801561239b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bf91906158e7565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015612409573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061242d9190615904565b6040516305c2fbcf60e31b8152600481018a90529091506001600160a01b038a1690632e17de7890602401600060405180830381600087803b15801561247257600080fd5b505af1158015612486573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201526001600160a01b038d1692506370a082319150602401602060405180830381865afa1580156124ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f29190615904565b6040516370a0823160e01b8152306004820152909450612581908b9083906001600160a01b038616906370a08231906024015b602060405180830381865afa158015612542573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125669190615904565b6125709190615933565b6001600160a01b03851691906134c0565b6040516370a0823160e01b81523060048201526001600160a01b03808c169163f6823996918491908616906370a0823190602401602060405180830381865afa1580156125d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125f69190615904565b6126009190615933565b6040516001600160e01b031960e084901b1681526004810191909152602481018a90526044810189905260648101889052608401600060405180830381600087803b15801561264e57600080fd5b505af1158015612662573d6000803e3d6000fd5b505050505050509695505050505050565b6040516001600160a01b0383166024820152604481018290526126a390849063a9059cbb60e01b90606401612229565b505050565b6000546001600160a01b031633146108fd5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610c78565b60008080846002811115612718576127186157f7565b0361284f5760405163c661065760e01b8152600060048201526001600160a01b0384169063c6610657906024016020604051808303816000875af1158015612764573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061278891906158e7565b60405163c661065760e01b8152600160048201529092506001600160a01b0384169063c6610657906024016020604051808303816000875af11580156127d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f691906158e7565b6001600160a01b038481166000908152600460209081526040808320878516845290915280822080546fffffffffffffffffffffffffffffffff1990811690915592841682529020805490911660011790559050612918565b826001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa15801561288d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128b191906158e7565b9150826001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291591906158e7565b90505b60006040518060600160405280866002811115612937576129376157f7565b81526001600160a01b0380871660208084019190915260006040938401819052878316815260038252838120928716815291905220815181549293508392829060ff1916600183600281111561298f5761298f6157f7565b02179055506020828101518254610100600160a81b0319166101006001600160a01b0392831602178355604093840151600193840180546001600160a01b0319169183169190911790558581166000908152600383528481209188168152915291909120825181548493839160ff191690836002811115612a1257612a126157f7565b021790555060208201518154610100600160a81b0319166101006001600160a01b0392831602178255604090920151600190910180546001600160a01b031916919092161790555050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006001600160a01b038516612aef57612ac983614292565b92507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc294505b6002546000906001600160a01b03808716911603612b2257507364aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5935060015b6001600160a01b038516731bed97cbc3c24a4fb5c069c6e311a967386131f71480612b6957506001600160a01b03851673583019ff0f430721ada9cfb4fac8f06ca104d0b4145b15612c0a577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316866001600160a01b031614612bd85760405162461bcd60e51b81526020600482015260066024820152652ca2aa2424a760d11b6044820152606401610c78565b612c0284846001600160a01b03881673583019ff0f430721ada9cfb4fac8f06ca104d0b414614430565b9150506134b8565b6001600160a01b038616731bed97cbc3c24a4fb5c069c6e311a967386131f71480612c5157506001600160a01b03861673583019ff0f430721ada9cfb4fac8f06ca104d0b4145b15612ceb577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316856001600160a01b031614612cc15760405162461bcd60e51b81526020600482015260076024820152661651551213d55560ca1b6044820152606401610c78565b612c0284846001600160a01b038916731bed97cbc3c24a4fb5c069c6e311a967386131f7146144e2565b6001600160a01b03808716600090815260036020908152604080832093891683529290528181208251606081019093528054919291829060ff166002811115612d3657612d366157f7565b6002811115612d4757612d476157f7565b815281546001600160a01b03610100909104811660208084019190915260019093015481166040909201919091529082015191925016612e1d576040805160028082526060820183526000926020830190803683370190505090508781600081518110612db657612db661594c565b60200260200101906001600160a01b031690816001600160a01b0316815250508681600181518110612dea57612dea61594c565b60200260200101906001600160a01b031690816001600160a01b031681525050612e1581878761459b565b935050613317565b60408101516001600160a01b03161515600082516002811115612e4257612e426157f7565b03612ea757602080830180516001600160a01b038082166000908152600480865260408083208f85168452875280832054955184168352908652808220928d1682529190945290922054612ea09291600f90810b91900b89896147af565b9350613315565b600182516002811115612ebc57612ebc6157f7565b036130cb57600082602001516001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f2991906158e7565b9050600082612f39576002612f3c565b60035b60ff1667ffffffffffffffff811115612f5757612f5761553b565b604051908082528060200260200182016040528015612f80578160200160208202803683370190505b5090508981600081518110612f9757612f9761594c565b60200260200101906001600160a01b031690816001600160a01b031681525050821561304757896001600160a01b0316826001600160a01b031614612fdc5781613049565b83602001516001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561301e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304291906158e7565b613049565b885b8160018151811061305c5761305c61594c565b60200260200101906001600160a01b031690816001600160a01b03168152505082156130b75788816002815181106130965761309661594c565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6130c281898961459b565b95505050613315565b80156132a057600082602001516001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015613115573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061313991906158e7565b90506132988984602001516001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015613181573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131a59190615ada565b8b6001600160a01b0316846001600160a01b0316146131c4578361322a565b85602001516001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015613206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061322a91906158e7565b86604001516001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa15801561326c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132909190615ada565b8c8c8c61483c565b945050613315565b6133128883602001516001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061330a9190615ada565b898989614a54565b93505b505b816133235750506134b8565b6002546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa15801561336c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133909190615904565b6002549091506133bf907364aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5906001600160a01b0316866134c0565b60025460405163b08d033360e01b81526001600160a01b039091169063b08d033390613408907364aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5908890600090600401615ab9565b600060405180830381600087803b15801561342257600080fd5b505af1158015613436573d6000803e3d6000fd5b50506002546040516370a0823160e01b81523060048201528493506001600160a01b0390911691506370a0823190602401602060405180830381865afa158015613484573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134a89190615904565b6134b29190615933565b93505050505b949350505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015613510573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135349190615904565b9050611ed48463095ea7b360e01b8561354d8686615aff565b6040516001600160a01b0390921660248301526044820152606401612229565b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156135b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135d89190615904565b905081811115611ed457611ed4846135f08484615933565b6001600160a01b0386169190612673565b6060806000855167ffffffffffffffff8111156136205761362061553b565b604051908082528060200260200182016040528015613649578160200160208202803683370190505b5090506000865167ffffffffffffffff8111156136685761366861553b565b604051908082528060200260200182016040528015613691578160200160208202803683370190505b50905060008787815181106136a8576136a861594c565b6020908102919091010151516040516370a0823160e01b81526001600160a01b038c81166004830152909116906370a0823190602401602060405180830381865afa1580156136fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061371f9190615904565b905060008a6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613761573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137859190615904565b156137a85781613799600160601b89615a80565b6137a39190615a97565b6137ae565b600160601b5b90508960005b8a51811015613853576138058d8c8c815181106137d3576137d361594c565b6020026020010151600001518b8e85815181106137f2576137f261594c565b6020026020010151600001518787614f6d565b8884815181106138175761381761594c565b602002602001018885815181106138305761383061594c565b60209081029190910101919091525291508061384b81615962565b9150506137b4565b50939b929a509198505050505050505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038816906370a0823190602401602060405180830381865afa1580156138af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138d39190615904565b90506138e96001600160a01b03871688876134c0565b60405163b08d033360e01b81526001600160a01b0388169063b08d03339061391990899089908990600401615ab9565b600060405180830381600087803b15801561393357600080fd5b505af1158015613947573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092508391506001600160a01b038a16906370a0823190602401602060405180830381865afa158015613994573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139b89190615904565b6139c29190615933565b90506001600160a01b03841630146139e8576139e86001600160a01b0389168583612673565b979650505050505050565b6000866001600160a01b0316634f4ce61d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5791906158e7565b6040516370a0823160e01b815230600482015290915060009087906001600160a01b038a16906370a0823190602401602060405180830381865afa158015613aa3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ac79190615904565b613ad19190615933565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa158015613b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b3f9190615904565b90506000896001600160a01b03166394cc699e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613b81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ba591906158e7565b9050613bb46000858a8a612ab0565b5060007f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c3991906158e7565b60405163e6a4390560e01b81526001600160a01b038d811660048301528781166024830152919091169063e6a4390590604401602060405180830381865afa158015613c89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cad91906158e7565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015613cf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d1b9190615904565b6040516370a0823160e01b8152306004820152909150613da9908d9086906001600160a01b038a16906370a0823190602401602060405180830381865afa158015613d6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d8e9190615904565b613d989190615933565b6001600160a01b03891691906134c0565b6040516370a0823160e01b81523060048201526001600160a01b03808e169163a9e9c8bc918e9188918b16906370a0823190602401602060405180830381865afa158015613dfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e1f9190615904565b613e299190615933565b6040516001600160e01b031960e085901b16815260048101929092526024820152604481018b9052606481018a9052608401600060405180830381600087803b158015613e7557600080fd5b505af1158015613e89573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152613ec1925085915083906001600160a01b038616906370a0823190602401612525565b826001600160a01b031663adc9772e8e83856001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401613f0d91906001600160a01b0391909116815260200190565b602060405180830381865afa158015613f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f4e9190615904565b613f589190615933565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015613f9e57600080fd5b505af1158015613fb2573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201528792506001600160a01b038f1691506370a0823190602401602060405180830381865afa158015613ffc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140209190615904565b11156140c2576140c28d868e6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161406691906001600160a01b0391909116815260200190565b602060405180830381865afa158015614083573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140a79190615904565b6140b19190615933565b6001600160a01b038f169190612673565b6040516370a0823160e01b815230600482015284906001600160a01b038816906370a0823190602401602060405180830381865afa158015614108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061412c9190615904565b1115612163576040516370a0823160e01b8152306004820152612163908e9086906001600160a01b038a16906370a0823190602401602060405180830381865afa15801561417e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141a29190615904565b6141ac9190615933565b6001600160a01b0389169190612673565b6000614212826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166153849092919063ffffffff16565b90508051600014806142335750808060200190518101906142339190615b12565b6126a35760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610c78565b6040516370a0823160e01b815230600482015260009081906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa1580156142fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061431f9190615904565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561437c57600080fd5b505af1158015614390573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201528493507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031692506370a082319150602401602060405180830381865afa1580156143fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061441f9190615904565b6144299190615933565b9392505050565b6000806144567369accb968b19a53790f43e57558f5e443a91af226000600188886147af565b905082156134b857604051636e553f6560e01b81526004810182905230602482015273583019ff0f430721ada9cfb4fac8f06ca104d0b490636e553f65906044016020604051808303816000875af11580156144b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144da9190615904565b915050614429565b60008082156144f257508361456f565b604051627b8a6760e11b81526004810186905230602482015273583019ff0f430721ada9cfb4fac8f06ca104d0b49062f714ce906044016020604051808303816000875af1158015614548573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061456c9190615904565b90505b6145927369accb968b19a53790f43e57558f5e443a91af226001600084886147af565b95945050505050565b60008084516003146145c757846001815181106145ba576145ba61594c565b60200260200101516145e3565b846002815181106145da576145da61594c565b60200260200101515b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561462d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146519190615904565b90506146ab7f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d868860008151811061468b5761468b61594c565b60200260200101516001600160a01b03166134c09092919063ffffffff16565b604051635c11d79560e01b81526001600160a01b037f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d1690635c11d795906146ff90889088908b9030904290600401615b73565b600060405180830381600087803b15801561471957600080fd5b505af115801561472d573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201528392506001600160a01b03851691506370a0823190602401602060405180830381865afa158015614777573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061479b9190615904565b6147a59190615933565b9695505050505050565b60405163ddc1f59d60e01b8152600f85810b600483015284900b602482015260448101839052606481018290523060848201526000906001600160a01b0387169063ddc1f59d9060a4016020604051808303816000875af1158015614818573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147a59190615904565b6040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa158015614885573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148a99190615904565b90506148d36001600160a01b038a1673e592427a0aece92de3edee1f18e0157c05861564866134c0565b6040516bffffffffffffffffffffffff1960608b811b821660208401526001600160e81b031960e88c811b821660348601528b831b841660378601528a901b16604b84015287901b16604e82015260009060620160408051601f1981840301815260a08301825280835230602084015242838301526060830188905260808301879052905163c04b8d5960e01b815290925073e592427a0aece92de3edee1f18e0157c058615649163c04b8d599161498e9190600401615bff565b6020604051808303816000875af11580156149ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149d19190615904565b506040516370a0823160e01b815230600482015282906001600160a01b038816906370a0823190602401602060405180830381865afa158015614a18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a3c9190615904565b614a469190615933565b9a9950505050505050505050565b600081600003614d055760007f000000000000000000000000024ff47d552cb222b265d68c7aeb26e586d5229d6001600160a01b0316634556bd2073e592427a0aece92de3edee1f18e0157c058615646001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015614ae1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b0591906158e7565b6040516001600160e01b031960e084901b1681526001600160a01b039182166004820152818b166024820152908816604482015262ffffff89166064820152608401602060405180830381865afa158015614b64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b8891906158e7565b90506000856001600160a01b0316886001600160a01b031610614bab5785614bad565b875b604051637fb4f79d60e01b81526001600160a01b0384811660048301529192506000917f000000000000000000000000024ff47d552cb222b265d68c7aeb26e586d5229d169063d4bf1334908290637fb4f79d90602401602060405180830381865afa158015614c21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c4591906158e7565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015614c89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cad9190615904565b9050816001600160a01b0316896001600160a01b031614614ce65780614cd7600160601b88615a80565b614ce19190615a97565b614cff565b600160601b614cf58783615a80565b614cff9190615a97565b94505050505b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015614d4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614d709190615904565b9050614d9a6001600160a01b03881673e592427a0aece92de3edee1f18e0157c05861564866134c0565b73e592427a0aece92de3edee1f18e0157c058615646001600160a01b031663414bf3896040518061010001604052808a6001600160a01b03168152602001886001600160a01b031681526020018962ffffff168152602001306001600160a01b031681526020014281526020018781526020016103e86001546103e8614e209190615933565b614e2a9089615a80565b614e349190615a97565b815260006020918201526040805160e085811b6001600160e01b031916825284516001600160a01b03908116600484015293850151841660248301529184015162ffffff1660448201526060840151831660648201526080840151608482015260a084015160a482015260c084015160c48201529201511660e4820152610104016020604051808303816000875af1158015614ed4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614ef89190615904565b506040516370a0823160e01b815230600482015281906001600160a01b038716906370a0823190602401602060405180830381865afa158015614f3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614f639190615904565b6139e89190615933565b6040516370a0823160e01b81523060048201526000908190819047906001600160a01b038816906370a0823190602401602060405180830381865afa158015614fba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614fde9190615904565b925060008a6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015615020573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906150449190615904565b156150d4576040516370a0823160e01b81526001600160a01b038c81166004830152600160601b9189918b16906370a0823190602401602060405180830381865afa158015615097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906150bb9190615904565b6150c59190615a80565b6150cf9190615a97565b61514d565b60405163e4b5495760e01b81526001600160a01b038b81166004830152602482018b905289811660448301528c169063e4b5495790606401602060405180830381865afa158015615129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061514d9190615904565b60408051600280825260608201835292935060009290916020830190803683370190505090507f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa1580156151d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906151f591906158e7565b816000815181106152085761520861594c565b60200260200101906001600160a01b031690816001600160a01b031681525050888160018151811061523c5761523c61594c565b6001600160a01b03928316602091820292909201015260405163fb3bdb4160e01b81527f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d9091169063fb3bdb419089906152a0908690869030904290600401615c57565b60006040518083038185885af11580156152be573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f191682016040526152e79190810190615c8c565b506152f24784615933565b6152fc9088615933565b6040516370a0823160e01b815230600482015290965085906001600160a01b038b16906370a0823190602401602060405180830381865afa158015615345573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906153699190615904565b6153739190615933565b935050505096509650969350505050565b60606134b8848460008585600080866001600160a01b031685876040516153ab9190615d12565b60006040518083038185875af1925050503d80600081146153e8576040519150601f19603f3d011682016040523d82523d6000602084013e6153ed565b606091505b50915091506139e88783838760608315615468578251600003615461576001600160a01b0385163b6154615760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610c78565b50816134b8565b6134b8838381511561547d5781518083602001fd5b8060405162461bcd60e51b8152600401610c789190615d2e565b6001600160a01b038116811461083757600080fd5b600080600080600060a086880312156154c457600080fd5b85356154cf81615497565b97602087013597506040870135966060810135965060800135945092505050565b8035600381106154ff57600080fd5b919050565b6000806040838503121561551757600080fd5b615520836154f0565b9150602083013561553081615497565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b60405160a0810167ffffffffffffffff811182821017156155745761557461553b565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156155a3576155a361553b565b604052919050565b600080600083850360a08112156155c157600080fd5b84356155cc81615497565b935060208501356155dc81615497565b92506060603f19820112156155f057600080fd5b506040516060810181811067ffffffffffffffff821117156156145761561461553b565b8060405250615625604086016154f0565b8152606085013561563581615497565b6020820152608085013561564881615497565b604082015292959194509192509050565b6000806040838503121561566c57600080fd5b823561552081615497565b600080600080600080600060e0888a03121561569257600080fd5b873561569d81615497565b96506020880135955060408801356156b481615497565b969995985095966060810135965060808101359560a0820135955060c0909101359350915050565b600067ffffffffffffffff8211156156f6576156f661553b565b5060051b60200190565b6000602080838503121561571357600080fd5b823567ffffffffffffffff81111561572a57600080fd5b8301601f8101851361573b57600080fd5b803561574e615749826156dc565b61557a565b81815260059190911b8201830190838101908783111561576d57600080fd5b928401925b828410156139e857833561578581615497565b82529284019290840190615772565b6000602082840312156157a657600080fd5b813561442981615497565b600080600080608085870312156157c757600080fd5b84356157d281615497565b935060208501356157e281615497565b93969395505050506040820135916060013590565b634e487b7160e01b600052602160045260246000fd5b606081016003851061582f57634e487b7160e01b600052602160045260246000fd5b9381526001600160a01b039283166020820152911660409091015290565b801515811461083757600080fd5b600080600080600080600080610100898b03121561587857600080fd5b883561588381615497565b97506020890135965060408901359550606089013594506080890135935060a0890135925060c0890135915060e08901356158bd8161584d565b809150509295985092959890939650565b6000602082840312156158e057600080fd5b5035919050565b6000602082840312156158f957600080fd5b815161442981615497565b60006020828403121561591657600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156159465761594661591d565b92915050565b634e487b7160e01b600052603260045260246000fd5b6000600182016159745761597461591d565b5060010190565b60006020828403121561598d57600080fd5b81516002811061442957600080fd5b600060208083850312156159af57600080fd5b825167ffffffffffffffff8111156159c657600080fd5b8301601f810185136159d757600080fd5b80516159e5615749826156dc565b81815260a09182028301840191848201919088841115615a0457600080fd5b938501935b83851015615a745780858a031215615a215760008081fd5b615a29615551565b8551615a3481615497565b8152858701518782015260408087015190820152606080870151615a5781615497565b908201526080868101519082015283529384019391850191615a09565b50979650505050505050565b80820281158282048414176159465761594661591d565b600082615ab457634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b039390931683526020830191909152604082015260600190565b600060208284031215615aec57600080fd5b815162ffffff8116811461442957600080fd5b808201808211156159465761594661591d565b600060208284031215615b2457600080fd5b81516144298161584d565b600081518084526020808501945080840160005b83811015615b685781516001600160a01b031687529582019590820190600101615b43565b509495945050505050565b85815284602082015260a060408201526000615b9260a0830186615b2f565b6001600160a01b0394909416606083015250608001529392505050565b60005b83811015615bca578181015183820152602001615bb2565b50506000910152565b60008151808452615beb816020860160208601615baf565b601f01601f19169290920160200192915050565b602081526000825160a06020840152615c1b60c0840182615bd3565b905060018060a01b0360208501511660408401526040840151606084015260608401516080840152608084015160a08401528091505092915050565b848152608060208201526000615c706080830186615b2f565b6001600160a01b03949094166040830152506060015292915050565b60006020808385031215615c9f57600080fd5b825167ffffffffffffffff811115615cb657600080fd5b8301601f81018513615cc757600080fd5b8051615cd5615749826156dc565b81815260059190911b82018301908381019087831115615cf457600080fd5b928401925b828410156139e857835182529284019290840190615cf9565b60008251615d24818460208701615baf565b9190910192915050565b6020815260006144296020830184615bd356fea164736f6c6343000813000a

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

0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d000000000000000000000000024ff47d552cb222b265d68c7aeb26e586d5229d

-----Decoded View---------------
Arg [0] : _v2Router (address): 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
Arg [1] : _v3TwapUtilities (address): 0x024ff47D552cB222b265D68C7aeB26E586D5229D

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d
Arg [1] : 000000000000000000000000024ff47d552cb222b265d68c7aeb26e586d5229d


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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