ETH Price: $2,865.88 (-9.47%)
Gas: 10 Gwei

Contract

0x7D5ba536ab244aAA1EA42aB88428847F25E3E676
 

Multichain Info

Transaction Hash
Method
Block
From
To
Value
Withdraw186422222023-11-24 14:40:59223 days ago1700836859IN
KyberSwap: Elastic LM v1
0 ETH0.0035307330.44105766
Exit186376022023-11-23 23:08:11224 days ago1700780891IN
KyberSwap: Elastic LM v1
0 ETH0.0055745125.50808956
Exit186354772023-11-23 15:58:47224 days ago1700755127IN
KyberSwap: Elastic LM v1
0 ETH0.0013465838.47613776
Exit186354762023-11-23 15:58:35224 days ago1700755115IN
KyberSwap: Elastic LM v1
0 ETH0.011686738.81105387
Withdraw186315582023-11-23 2:49:47225 days ago1700707787IN
KyberSwap: Elastic LM v1
0 ETH0.0035399130.52016684
Exit186315532023-11-23 2:48:47225 days ago1700707727IN
KyberSwap: Elastic LM v1
0 ETH0.0091249831.71391062
Withdraw186307892023-11-23 0:14:47225 days ago1700698487IN
KyberSwap: Elastic LM v1
0 ETH0.0029906529.23242098
Exit186307812023-11-23 0:13:11225 days ago1700698391IN
KyberSwap: Elastic LM v1
0 ETH0.0047221531.8298893
Harvest Multiple...186307572023-11-23 0:08:23225 days ago1700698103IN
KyberSwap: Elastic LM v1
0 ETH0.0071354232.6317622
Exit186305782023-11-22 23:32:35225 days ago1700695955IN
KyberSwap: Elastic LM v1
0 ETH0.005682535.24820918
Harvest Multiple...186305772023-11-22 23:32:23225 days ago1700695943IN
KyberSwap: Elastic LM v1
0 ETH0.0087624435.19110134
Harvest Multiple...186305722023-11-22 23:31:23225 days ago1700695883IN
KyberSwap: Elastic LM v1
0 ETH0.0064030934.73317814
Harvest Multiple...186305372023-11-22 23:24:11225 days ago1700695451IN
KyberSwap: Elastic LM v1
0 ETH0.0099584445.46406571
Harvest Multiple...186305372023-11-22 23:24:11225 days ago1700695451IN
KyberSwap: Elastic LM v1
0 ETH0.0099584445.46406571
Harvest Multiple...186305352023-11-22 23:23:47225 days ago1700695427IN
KyberSwap: Elastic LM v1
0 ETH0.011606149.14925746
Harvest Multiple...186265232023-11-22 9:55:59225 days ago1700646959IN
KyberSwap: Elastic LM v1
0 ETH0.0042467823.83758544
Harvest Multiple...186243442023-11-22 2:35:35226 days ago1700620535IN
KyberSwap: Elastic LM v1
0 ETH0.0051985929.18018629
Harvest Multiple...186208302023-11-21 14:48:11226 days ago1700578091IN
KyberSwap: Elastic LM v1
0 ETH0.0055861631.35563919
Claim Fee186208282023-11-21 14:47:47226 days ago1700578067IN
KyberSwap: Elastic LM v1
0 ETH0.0083942833.07636722
Harvest Multiple...186183352023-11-21 6:24:11227 days ago1700547851IN
KyberSwap: Elastic LM v1
0 ETH0.0051133127.66346361
Harvest Multiple...186156802023-11-20 21:28:47227 days ago1700515727IN
KyberSwap: Elastic LM v1
0 ETH0.0071384339.04194988
Deposit And Join186132652023-11-20 13:21:35227 days ago1700486495IN
KyberSwap: Elastic LM v1
0 ETH0.0110391223.75174201
Join186125132023-11-20 10:50:47227 days ago1700477447IN
KyberSwap: Elastic LM v1
0 ETH0.003927121.31668574
Harvest Multiple...186125072023-11-20 10:49:35227 days ago1700477375IN
KyberSwap: Elastic LM v1
0 ETH0.003963121.44075263
Remove Liquidity186100722023-11-20 2:38:47228 days ago1700447927IN
KyberSwap: Elastic LM v1
0 ETH0.0098884919.63564298
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
KyberSwapElasticLM

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 17 : KyberSwapElasticLM.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

import {KSMath} from '../libraries/KSMath.sol';
import {IKyberSwapElasticLM} from '../interfaces/liquidityMining/IKyberSwapElasticLM.sol';
import {IKSElasticLMHelper} from '../interfaces/liquidityMining/IKSElasticLMHelper.sol';
import {IBasePositionManager} from '../interfaces/liquidityMining/IBasePositionManager.sol';
import {IPoolStorage} from '../interfaces/liquidityMining/IPoolStorage.sol';
import {KSAdmin} from './base/KSAdmin.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import {ReentrancyGuard} from '@openzeppelin/contracts/security/ReentrancyGuard.sol';

contract KyberSwapElasticLM is IKyberSwapElasticLM, ReentrancyGuard, KSAdmin {
  using EnumerableSet for EnumerableSet.UintSet;
  using SafeERC20 for IERC20Metadata;
  using KSMath for uint256;

  IERC721 public immutable nft;
  IKSElasticLMHelper private helper;
  address public immutable weth;

  uint256 internal constant PRECISION = 1e12;

  uint256 public poolLength;

  // pId => Pool info
  mapping(uint256 => LMPoolInfo) public pools;

  // nftId => Position info
  mapping(uint256 => PositionInfo) public positions;

  // nftId => pId => Stake info
  mapping(uint256 => mapping(uint256 => StakeInfo)) public stakes;

  // nftId => list of joined pools
  mapping(uint256 => EnumerableSet.UintSet) internal joinedPools;

  // user address => set of nft id which user already deposit into LM contract
  mapping(address => EnumerableSet.UintSet) private depositNFTs;

  mapping(uint256 => bool) public isEmergencyWithdrawnNFT;

  bool public emergencyEnabled;
  bool public specialFeatureEnabled;

  modifier checkLength(uint256 a, uint256 b) {
    require(a == b, 'invalid length');
    _;
  }

  modifier isSpecialFeaturesEnabled() {
    require(specialFeatureEnabled, 'special feature disabled');
    _;
  }

  constructor(IERC721 _nft, IKSElasticLMHelper _helper) {
    nft = _nft;
    helper = _helper;
    weth = IBasePositionManager(address(_nft)).WETH();
  }

  /**
   * EXTERNAL FUNCTIONS *************************
   */

  /**
   * @dev receive native reward token
   */
  receive() external payable {}

  /**
   * @dev Set emergencyEnabled flag to true
   */
  function emergencyEnable() public isAdmin {
    require(!emergencyEnabled, 'Invalid value');
    emergencyEnabled = true;
    emit EmergencyEnabled();
  }

  /**
   * @dev Set specialFeatureEnabled flag to true or false
   */
  function updateSpecialFeatureEnabled(bool enableOrDisable) public isAdmin {
    specialFeatureEnabled = enableOrDisable;
    emit UpdateSpecialFeatureEnabled(enableOrDisable);
  }

  function updateHelper(IKSElasticLMHelper _helper) external isAdmin {
    helper = _helper;

    emit LMHelperUpdated(_helper);
  }

  /// @inheritdoc IKyberSwapElasticLM
  function addPool(
    address poolAddress,
    uint32 startTime,
    uint32 endTime,
    address[] calldata rewardTokens,
    uint256[] calldata rewardAmounts,
    uint256 feeTarget
  ) external override isOperator checkLength(rewardTokens.length, rewardAmounts.length) {
    require(startTime >= _getBlockTime() && endTime > startTime, 'addPool: invalid times');
    uint256 pId = poolLength; // save gas
    LMPoolInfo storage pool = pools[pId];

    pool.poolAddress = poolAddress;
    pool.startTime = startTime;
    pool.endTime = endTime;
    pool.totalSecondsClaimed = 0;
    pool.feeTarget = feeTarget;

    for (uint256 i = 0; i < rewardTokens.length; i++) {
      pool.rewards.push(RewardData(rewardTokens[i], rewardAmounts[i]));
    }
    poolLength++;
    emit AddPool(pId, poolAddress, startTime, endTime, feeTarget);
  }

  /// @inheritdoc IKyberSwapElasticLM
  function renewPool(
    uint256 pId,
    uint32 startTime,
    uint32 endTime,
    uint256[] calldata rewardAmounts,
    uint256 feeTarget
  ) external override isOperator {
    LMPoolInfo storage pool = pools[pId];

    // check if pool has not started or already ended
    require(
      pool.startTime > _getBlockTime() || pool.endTime < _getBlockTime(),
      'renew: invalid pool state'
    );
    require(pool.rewards.length == rewardAmounts.length, 'renew: invalid length');
    // check input startTime and endTime
    require(startTime > _getBlockTime() && endTime > startTime, 'renew: invalid times');
    // check pool has stakes
    require(pool.numStakes == 0, 'renew: pool has stakes');

    pool.startTime = startTime;
    pool.endTime = endTime;
    pool.totalSecondsClaimed = 0;
    pool.feeTarget = feeTarget;

    for (uint256 i = 0; i < rewardAmounts.length; ++i) {
      pool.rewards[i].rewardUnclaimed = rewardAmounts[i];
    }
    emit RenewPool(pId, startTime, endTime, feeTarget);
  }

  /// @inheritdoc IKyberSwapElasticLM
  function deposit(uint256[] calldata nftIds) external override nonReentrant {
    _depositAndJoin(0, nftIds, false);
  }

  /// @inheritdoc IKyberSwapElasticLM
  function depositAndJoin(
    uint256 pId,
    uint256[] calldata nftIds
  ) external override isSpecialFeaturesEnabled nonReentrant {
    _depositAndJoin(pId, nftIds, true);
  }

  /// @inheritdoc IKyberSwapElasticLM
  function withdraw(uint256[] calldata nftIds) external override nonReentrant {
    address sender = msg.sender;
    for (uint256 i = 0; i < nftIds.length; ++i) {
      require(positions[nftIds[i]].owner == sender, 'withdraw: not owner');
      require(joinedPools[nftIds[i]].length() == 0, 'withdraw: not exited yet');
      delete positions[nftIds[i]];
      require(depositNFTs[sender].remove(nftIds[i]));
      nft.transferFrom(address(this), sender, nftIds[i]);
      emit Withdraw(sender, nftIds[i]);
    }
  }

  /// @inheritdoc IKyberSwapElasticLM
  function emergencyWithdraw(uint256[] calldata nftIds) external override nonReentrant {
    address sender = msg.sender;
    // save gas
    bool _emergencyEnabled = emergencyEnabled;

    for (uint256 i = 0; i < nftIds.length; ++i) {
      require(positions[nftIds[i]].owner == sender, 'withdraw: not owner');

      isEmergencyWithdrawnNFT[nftIds[i]] = true;
      uint256[] memory values = joinedPools[nftIds[i]].values();
      for (uint256 j = 0; j < values.length; ++j) {
        uint256 poolId = values[j];
        unchecked {
          pools[poolId].numStakes--;
        }
        delete stakes[nftIds[i]][poolId];
      }
      delete positions[nftIds[i]];

      if (!_emergencyEnabled) {
        require(depositNFTs[sender].remove(nftIds[i]));
        for (uint256 j = 0; j < values.length; ++j) {
          uint256 poolId = values[j];
          require(joinedPools[nftIds[i]].remove(poolId));
        }
      }

      nft.transferFrom(address(this), sender, nftIds[i]);
      emit EmergencyWithdraw(sender, nftIds[i]);
    }
  }

  /// @inheritdoc IKyberSwapElasticLM
  function emergencyWithdrawForOwner(
    address[] calldata rewards,
    uint256[] calldata amounts
  ) external override isAdmin checkLength(rewards.length, amounts.length) {
    for (uint256 i = 0; i < rewards.length; ++i) {
      _transferReward(rewards[i], msg.sender, amounts[i]);
      emit EmergencyWithdrawForOwner(rewards[i], amounts[i]);
    }
  }

  /// @inheritdoc IKyberSwapElasticLM
  function join(
    uint256 pId,
    uint256[] calldata nftIds,
    uint256[] calldata liqs
  ) external override nonReentrant checkLength(nftIds.length, liqs.length) {
    require(poolLength > pId, 'Pool not exists');
    LMPoolInfo storage pool = pools[pId];
    require(pool.startTime <= _getBlockTime() && _getBlockTime() < pool.endTime, 'Invalid time');
    for (uint256 i = 0; i < nftIds.length; ++i) {
      require(positions[nftIds[i]].owner == msg.sender, 'Not owner');
      positions[nftIds[i]].liquidity = helper.getLiq(address(nft), nftIds[i]);
      StakeInfo storage stake = stakes[nftIds[i]][pId];
      if (stake.liquidity == 0) {
        _join(nftIds[i], pId, liqs[i], pool);
      } else {
        _sync(nftIds[i], pId, liqs[i], pool);
      }
    }
  }

  /// @inheritdoc IKyberSwapElasticLM
  function exit(
    uint256 pId,
    uint256[] calldata nftIds,
    uint256[] calldata liqs
  ) external override nonReentrant checkLength(nftIds.length, liqs.length) {
    require(poolLength > pId, 'Pool not exists');
    for (uint256 i = 0; i < nftIds.length; ++i) {
      _exit(nftIds[i], pId, liqs[i], true);
    }
  }

  /// @inheritdoc IKyberSwapElasticLM
  function harvestMultiplePools(
    uint256[] calldata nftIds,
    bytes[] calldata datas
  ) external override nonReentrant checkLength(nftIds.length, datas.length) {
    for (uint256 i; i < nftIds.length; ++i) {
      require(positions[nftIds[i]].owner == msg.sender, 'harvest: not owner');
      HarvestData memory data = abi.decode(datas[i], (HarvestData));
      for (uint256 j; j < data.pIds.length; ++j) {
        _harvest(nftIds[i], data.pIds[j]);
      }
    }
  }

  /// @inheritdoc IKyberSwapElasticLM
  function removeLiquidity(
    uint256 nftId,
    uint128 liquidity,
    uint256 amount0Min,
    uint256 amount1Min,
    uint256 deadline,
    bool isReceiveNative,
    bool[2] calldata claimFeeAndRewards
  ) external override nonReentrant isSpecialFeaturesEnabled {
    require(_getBlockTime() <= deadline, 'removeLiquidity: expired');
    require(positions[nftId].owner == msg.sender, 'removeLiquidity: not owner');

    uint256 posLiquidity = helper.getLiq(address(nft), nftId);
    require(liquidity > 0 && liquidity <= posLiquidity, 'removeLiquidity: invalid liquidity');

    posLiquidity -= liquidity;
    positions[nftId].liquidity = posLiquidity;

    uint256[] memory poolIds = joinedPools[nftId].values();
    for (uint256 i; i < poolIds.length; ) {
      uint256 stakedLiquidity = stakes[nftId][poolIds[i]].liquidity;
      uint256 deltaLiq = stakedLiquidity > posLiquidity ? stakedLiquidity - posLiquidity : 0;

      if (deltaLiq > 0) _exit(nftId, poolIds[i], deltaLiq, claimFeeAndRewards[1]);

      unchecked {
        ++i;
      }
    }

    (address token0, address token1) = helper.getPair(address(nft), nftId);
    _removeLiquidity(nftId, liquidity, deadline);
    if (claimFeeAndRewards[0]) _claimFee(nftId, deadline, false);
    _transferTokens(token0, token1, amount0Min, amount1Min, msg.sender, isReceiveNative);
  }

  /// @inheritdoc IKyberSwapElasticLM
  function claimFee(
    uint256[] calldata nftIds,
    uint256 amount0Min,
    uint256 amount1Min,
    address poolAddress,
    bool isReceiveNative,
    uint256 deadline
  ) external override nonReentrant isSpecialFeaturesEnabled {
    require(_getBlockTime() <= deadline, 'claimFee: expired');

    uint256 length = nftIds.length;
    (address token0, address token1) = (
      address(IPoolStorage(poolAddress).token0()),
      address(IPoolStorage(poolAddress).token1())
    );

    for (uint256 i; i < length; ) {
      require(positions[nftIds[i]].owner == msg.sender, 'claimFee: not owner');

      (address nftToken0, address nftToken1) = helper.getPair(address(nft), nftIds[i]);
      require(nftToken0 == token0 && nftToken1 == token1, 'claimFee: token pair not match');

      _claimFee(nftIds[i], deadline, true);

      unchecked {
        ++i;
      }
    }

    _transferTokens(token0, token1, amount0Min, amount1Min, msg.sender, isReceiveNative);
  }

  /// @inheritdoc IKyberSwapElasticLM
  function getJoinedPools(
    uint256 nftId
  ) external view override returns (uint256[] memory poolIds) {
    uint256 length = joinedPools[nftId].length();
    poolIds = new uint256[](length);
    for (uint256 i = 0; i < length; ++i) {
      poolIds[i] = joinedPools[nftId].at(i);
    }
  }

  /// @inheritdoc IKyberSwapElasticLM
  function getJoinedPoolsInRange(
    uint256 nftId,
    uint256 fromIndex,
    uint256 toIndex
  ) external view returns (uint256[] memory poolIds) {
    require(fromIndex <= toIndex, 'fromIndex > toIndex');
    require(toIndex < joinedPools[nftId].length(), 'toIndex >= length');
    poolIds = new uint256[](toIndex - fromIndex + 1);
    for (uint256 index = fromIndex; index <= toIndex; ++index) {
      poolIds[index - fromIndex] = joinedPools[nftId].at(index);
    }
  }

  /// @inheritdoc IKyberSwapElasticLM
  function getUserInfo(
    uint256 nftId,
    uint256 pId
  )
    external
    view
    override
    returns (uint256 liquidity, uint256[] memory rewardPending, uint256[] memory rewardLast)
  {
    LMPoolInfo storage pool = pools[pId];
    StakeInfo storage stake = stakes[nftId][pId];

    require(stake.liquidity > 0, 'getUserInfo: not joined yet');

    rewardPending = new uint256[](pool.rewards.length);
    rewardLast = new uint256[](pool.rewards.length);

    RewardCalculationData memory data = getRewardCalculationData(nftId, pId);
    for (uint256 i = 0; i < pool.rewards.length; ++i) {
      uint256 rewardHarvest = _calculateRewardHarvest(
        stake.liquidity,
        pool.rewards[i].rewardUnclaimed,
        data.totalSecondsUnclaimed,
        data.secondsPerLiquidity
      );
      uint256 rewardCollected = _calculateRewardCollected(
        stake.rewardHarvested[i] + rewardHarvest,
        data.vestingVolume,
        stake.rewardLast[i]
      );
      rewardPending[i] = stake.rewardPending[i] + rewardCollected;
      rewardLast[i] = stake.rewardLast[i];
    }
    liquidity = stake.liquidity;
  }

  /// @inheritdoc IKyberSwapElasticLM
  function getPoolInfo(
    uint256 pId
  )
    external
    view
    override
    returns (
      address poolAddress,
      uint32 startTime,
      uint32 endTime,
      uint256 totalSecondsClaimed,
      uint256 feeTarget,
      uint256 numStakes,
      //index reward => reward data
      address[] memory rewardTokens,
      uint256[] memory rewardUnclaimeds
    )
  {
    LMPoolInfo storage pool = pools[pId];

    poolAddress = pool.poolAddress;
    startTime = pool.startTime;
    endTime = pool.endTime;
    totalSecondsClaimed = pool.totalSecondsClaimed;
    feeTarget = pool.feeTarget;
    numStakes = pool.numStakes;

    uint256 length = pool.rewards.length;
    rewardTokens = new address[](length);
    rewardUnclaimeds = new uint256[](length);
    for (uint256 i = 0; i < length; ++i) {
      rewardTokens[i] = pool.rewards[i].rewardToken;
      rewardUnclaimeds[i] = pool.rewards[i].rewardUnclaimed;
    }
  }

  /// @inheritdoc IKyberSwapElasticLM
  function getDepositedNFTs(address user) external view returns (uint256[] memory listNFTs) {
    listNFTs = depositNFTs[user].values();
  }

  /**
   * INTERNAL FUNCTIONS *************************
   */

  /**
   * @dev Deposit NFTs to the LM contract, and join farming pool if needed
   * @param pId pool id to join farm, if isJoining = true
   * @param nftIds list of NFT ids to deposit
   * @param isJoining whether to join farm with pId
   */
  function _depositAndJoin(uint256 pId, uint256[] memory nftIds, bool isJoining) internal {
    require(!emergencyEnabled, 'Not allowed to deposit');

    if (isJoining) {
      // verify if pool's state is valid
      require(poolLength > pId, 'Pool not exists');
      uint32 _blockTime = _getBlockTime();
      require(
        pools[pId].startTime <= _blockTime && _blockTime < pools[pId].endTime,
        'Invalid time'
      );
    }

    address sender = msg.sender;

    for (uint256 i = 0; i < nftIds.length; ) {
      // if the nft has used emergency withdraw before, not allow to re-deposit
      require(!isEmergencyWithdrawnNFT[nftIds[i]], 'Not allowed to deposit');
      // and nft to the list and deposit nft to the LM contract
      require(depositNFTs[sender].add(nftIds[i]));
      nft.transferFrom(sender, address(this), nftIds[i]);
      emit Deposit(sender, nftIds[i]);
      // update position data
      positions[nftIds[i]].owner = sender;
      uint128 liquidity = helper.getLiq(address(nft), nftIds[i]);
      positions[nftIds[i]].liquidity = liquidity;
      // join full liquidity to the farm if joining is enabled
      if (isJoining) {
        _join(nftIds[i], pId, liquidity, pools[pId]);
      }
      unchecked {
        ++i;
      }
    }
  }

  /**
   * @dev join pool first time
   * @param nftId NFT id to join
   * @param pId pool id to join
   * @param liq liquidity amount to join
   * @param pool LM pool
   */
  function _join(uint256 nftId, uint256 pId, uint256 liq, LMPoolInfo storage pool) internal {
    PositionInfo storage position = positions[nftId];
    StakeInfo storage stake = stakes[nftId][pId];
    require(helper.checkPool(pool.poolAddress, address(nft), nftId), 'join: invalid pool');
    require(liq != 0 && liq <= position.liquidity, 'join: invalid liq');

    stake.secondsPerLiquidityLast = helper.getActiveTime(pool.poolAddress, address(nft), nftId);
    stake.rewardLast = new uint256[](pool.rewards.length);
    stake.rewardPending = new uint256[](pool.rewards.length);
    stake.rewardHarvested = new uint256[](pool.rewards.length);
    if (pool.feeTarget != 0) {
      stake.feeFirst = helper.getSignedFee(address(nft), nftId);
    }
    stake.liquidity = liq;
    pool.numStakes++;

    require(joinedPools[nftId].add(pId), 'Fail to add joinedPools');

    emit Join(nftId, pId, liq);
  }

  /**
   * @dev Increase liquidity in pool
   * @param nftId NFT id to sync
   * @param pId pool id to sync
   * @param liq liquidity amount to increase
   * @param pool LM pool
   */
  function _sync(uint256 nftId, uint256 pId, uint256 liq, LMPoolInfo storage pool) internal {
    PositionInfo storage position = positions[nftId];
    StakeInfo storage stake = stakes[nftId][pId];

    require(liq != 0 && liq + stake.liquidity <= position.liquidity, 'sync: invalid liq');

    RewardCalculationData memory data = getRewardCalculationData(nftId, pId);

    for (uint256 i = 0; i < pool.rewards.length; ++i) {
      uint256 rewardHarvest = _calculateRewardHarvest(
        stake.liquidity,
        pool.rewards[i].rewardUnclaimed,
        data.totalSecondsUnclaimed,
        data.secondsPerLiquidity
      );

      if (rewardHarvest != 0) {
        stake.rewardHarvested[i] += rewardHarvest;
        pool.rewards[i].rewardUnclaimed -= rewardHarvest;
      }

      uint256 rewardCollected = _calculateRewardCollected(
        stake.rewardHarvested[i],
        data.vestingVolume,
        stake.rewardLast[i]
      );

      if (rewardCollected != 0) {
        stake.rewardLast[i] += rewardCollected;
        stake.rewardPending[i] += rewardCollected;
      }
    }

    pool.totalSecondsClaimed += data.secondsClaim;
    stake.secondsPerLiquidityLast = data.secondsPerLiquidityNow;
    stake.feeFirst = _calculateFeeFirstAfterJoin(
      stake.feeFirst,
      data.feeNow,
      pool.feeTarget,
      stake.liquidity,
      liq,
      nftId
    );
    stake.liquidity += liq;
    emit SyncLiq(nftId, pId, liq);
  }

  /**
   * @dev Exit pool
   * @param nftId NFT id to exit
   * @param pId pool id to exit
   * @param liq liquidity amount to exit
   * @param claimReward transfer reward or not
   */
  function _exit(uint256 nftId, uint256 pId, uint256 liq, bool claimReward) internal {
    LMPoolInfo storage pool = pools[pId];
    address pOwner = positions[nftId].owner;
    StakeInfo storage stake = stakes[nftId][pId];

    require(
      pOwner == msg.sender || (_getBlockTime() > pool.endTime && operators[msg.sender]),
      'exit: not owner or pool not ended'
    );

    uint256 liquidityOld = stake.liquidity;
    require(liq != 0 && liq <= liquidityOld, 'exit: invalid liq');

    uint256 liquidityNew = liquidityOld - liq;
    RewardCalculationData memory data = getRewardCalculationData(nftId, pId);

    pool.totalSecondsClaimed += data.secondsClaim;
    stake.secondsPerLiquidityLast = data.secondsPerLiquidityNow;
    stake.liquidity = liquidityNew;
    for (uint256 i = 0; i < pool.rewards.length; ++i) {
      uint256 rewardHarvest = _calculateRewardHarvest(
        liquidityOld,
        pool.rewards[i].rewardUnclaimed,
        data.totalSecondsUnclaimed,
        data.secondsPerLiquidity
      );

      if (rewardHarvest != 0) {
        stake.rewardHarvested[i] += rewardHarvest;
        pool.rewards[i].rewardUnclaimed -= rewardHarvest;
      }

      uint256 rewardCollected = _calculateRewardCollected(
        stake.rewardHarvested[i],
        data.vestingVolume,
        stake.rewardLast[i]
      );

      uint256 rewardPending = stake.rewardPending[i];
      if (rewardCollected != 0) {
        stake.rewardLast[i] += rewardCollected;
        rewardPending += rewardCollected;
      }

      if (rewardPending != 0) {
        if (claimReward) {
          stake.rewardPending[i] = 0;
          _transferReward(pool.rewards[i].rewardToken, pOwner, rewardPending);
          emit Harvest(nftId, pOwner, pool.rewards[i].rewardToken, rewardPending);
        } else {
          stake.rewardPending[i] = rewardPending;
        }
      }
    }
    if (liquidityNew == 0) {
      delete stakes[nftId][pId];
      pool.numStakes--;

      require(joinedPools[nftId].remove(pId), 'Fail to remove joinedPools');
    }
    emit Exit(msg.sender, nftId, pId, liq);
  }

  /**
   * @dev Harvest reward
   * @param nftId NFT id to harvest
   * @param pId pool id to harvest
   */
  function _harvest(uint256 nftId, uint256 pId) internal {
    require(poolLength > pId, 'Pool not exists');
    LMPoolInfo storage pool = pools[pId];
    address pOwner = positions[nftId].owner;
    StakeInfo storage stake = stakes[nftId][pId];

    require(stake.liquidity > 0, 'harvest: not joined yet');

    RewardCalculationData memory data = getRewardCalculationData(nftId, pId);

    pool.totalSecondsClaimed += data.secondsClaim;
    stake.secondsPerLiquidityLast = data.secondsPerLiquidityNow;
    for (uint256 i = 0; i < pool.rewards.length; ++i) {
      uint256 rewardHarvest = _calculateRewardHarvest(
        stake.liquidity,
        pool.rewards[i].rewardUnclaimed,
        data.totalSecondsUnclaimed,
        data.secondsPerLiquidity
      );

      if (rewardHarvest != 0) {
        stake.rewardHarvested[i] += rewardHarvest;
        pool.rewards[i].rewardUnclaimed -= rewardHarvest;
      }

      uint256 rewardCollected = _calculateRewardCollected(
        stake.rewardHarvested[i],
        data.vestingVolume,
        stake.rewardLast[i]
      );

      uint256 rewardPending = stake.rewardPending[i] + rewardCollected;
      if (rewardPending != 0) {
        if (rewardCollected != 0) {
          stake.rewardLast[i] += rewardCollected;
        }
        stake.rewardPending[i] = 0;
        _transferReward(pool.rewards[i].rewardToken, pOwner, rewardPending);
        emit Harvest(nftId, pOwner, pool.rewards[i].rewardToken, rewardPending);
      }
    }
  }

  /**
   * @dev transfer reward
   */
  function _transferReward(address _token, address _account, uint256 _amount) internal {
    if (_token == address(0)) {
      (bool success, ) = payable(_account).call{value: _amount}('');
      require(success, 'transfer reward token failed');
    } else {
      IERC20Metadata(_token).safeTransfer(_account, _amount);
    }
  }

  /// @dev remove liquidiy of nft from posManager
  /// @param nftId nft's id
  /// @param liquidity liquidity amount to remove
  /// @param deadline removeLiquidity deadline
  function _removeLiquidity(uint256 nftId, uint128 liquidity, uint256 deadline) internal {
    IBasePositionManager.RemoveLiquidityParams memory removeLiq = IBasePositionManager
      .RemoveLiquidityParams({
        tokenId: nftId,
        liquidity: liquidity,
        amount0Min: 0,
        amount1Min: 0,
        deadline: deadline
      });

    IBasePositionManager(address(nft)).removeLiquidity(removeLiq);
  }

  /// @dev claim fee of nft from posManager
  /// @param nftId nft's id
  /// @param deadline claimFee deadline
  /// @param syncFee is need to sync new fee or not
  function _claimFee(uint256 nftId, uint256 deadline, bool syncFee) internal {
    if (syncFee) {
      IBasePositionManager(address(nft)).syncFeeGrowth(nftId);
    }

    IBasePositionManager.BurnRTokenParams memory burnRToken = IBasePositionManager
      .BurnRTokenParams({tokenId: nftId, amount0Min: 0, amount1Min: 0, deadline: deadline});

    IBasePositionManager(address(nft)).burnRTokens(burnRToken);
  }

  /// @dev transfer tokens from removeLiquidity (and burnRToken if any) to receiver
  /// @param token0 address of token0
  /// @param token1 address of token1
  /// @param amount0Min minimum amount of token0 should receive
  /// @param amount1Min minimum amount of token1 should receive
  /// @param receiver receiver of tokens
  /// @param isReceiveNative should unwrap wrapped native or not
  function _transferTokens(
    address token0,
    address token1,
    uint256 amount0Min,
    uint256 amount1Min,
    address receiver,
    bool isReceiveNative
  ) internal {
    IBasePositionManager posManager = IBasePositionManager(address(nft));

    if (isReceiveNative) {
      // expect to receive in native token
      if (weth == token0) {
        // receive in native for token0
        posManager.unwrapWeth(amount0Min, receiver);
        posManager.transferAllTokens(token1, amount1Min, receiver);
        return;
      }
      if (weth == token1) {
        // receive in native for token1
        posManager.transferAllTokens(token0, amount0Min, receiver);
        posManager.unwrapWeth(amount1Min, receiver);
        return;
      }
    }

    posManager.transferAllTokens(token0, amount0Min, receiver);
    posManager.transferAllTokens(token1, amount1Min, receiver);
  }

  /**
   * HELPER MATH FUNCTIONS *************************
   */
  function getRewardCalculationData(
    uint256 nftId,
    uint256 pId
  ) public view override returns (RewardCalculationData memory data) {
    LMPoolInfo storage pool = pools[pId];
    StakeInfo storage stake = stakes[nftId][pId];

    data.secondsPerLiquidityNow = helper.getActiveTime(pool.poolAddress, address(nft), nftId);
    data.feeNow = helper.getSignedFeePool(pool.poolAddress, address(nft), nftId);
    data.vestingVolume = _calculateVestingVolume(data.feeNow, stake.feeFirst, pool.feeTarget);
    data.totalSecondsUnclaimed = _calculateSecondsUnclaimed(
      pool.startTime,
      pool.endTime,
      pool.totalSecondsClaimed
    );
    unchecked {
      data.secondsPerLiquidity = data.secondsPerLiquidityNow - stake.secondsPerLiquidityLast;
    }
    data.secondsClaim = stake.liquidity * data.secondsPerLiquidity;
  }

  /**
   * @dev feeFirst = (liq * max(feeNow - feeTarget, feeFirst) + liqAdd * feeNow) / liqNew
   */
  function _calculateFeeFirstAfterJoin(
    int256 feeFirst,
    int256 feeNow,
    uint256 feeTarget,
    uint256 liquidity,
    uint256 liquidityAdd,
    uint256 nftId
  ) internal view returns (int256) {
    if (feeTarget == 0) {
      return 0;
    }
    int256 feeFirstCurrent = feeNow - int256(feeTarget) < feeFirst
      ? feeFirst
      : feeNow - int256(feeTarget);
    int256 numerator = int256(liquidity) *
      feeFirstCurrent +
      int256(liquidityAdd) *
      helper.getSignedFee(address(nft), nftId);
    int256 denominator = int256(liquidity + liquidityAdd);
    return numerator / denominator;
  }

  /**
   * @dev vesting = min((feeNow - feeFirst) / feeTarget, 1)
   */
  function _calculateVestingVolume(
    int256 feeNow,
    int256 feeFirst,
    uint256 feeTarget
  ) internal pure returns (uint256) {
    if (feeTarget == 0) {
      return PRECISION;
    }
    uint256 feeInside = uint256(feeNow - feeFirst);
    return KSMath.min((feeInside * PRECISION) / feeTarget, PRECISION);
  }

  /**
   * @dev secondsUnclaimed = (max(currentTime, endTime) - startTime) - secondsClaimed
   */
  function _calculateSecondsUnclaimed(
    uint256 startTime,
    uint256 endTime,
    uint256 totalSecondsClaimed
  ) internal view returns (uint256) {
    uint256 totalSeconds = KSMath.max(_getBlockTime(), endTime) - startTime;
    uint256 totalSecondsScaled = totalSeconds * (1 << 96);
    return totalSecondsScaled > totalSecondsClaimed ? totalSecondsScaled - totalSecondsClaimed : 0;
  }

  /**
   * @dev rewardHarvested = L * rewardRate * secondsPerLiquidity
   */
  function _calculateRewardHarvest(
    uint256 liquidity,
    uint256 rewardUnclaimed,
    uint256 totalSecondsUnclaimed,
    uint256 secondsPerLiquidity
  ) internal pure returns (uint256) {
    return (liquidity * rewardUnclaimed * secondsPerLiquidity) / totalSecondsUnclaimed;
  }

  /**
   * @dev rewardCollected = Max(rewardHarvested * vestingVolume - rewardLast, 0);
   */
  function _calculateRewardCollected(
    uint256 rewardHarvested,
    uint256 vestingVolume,
    uint256 rewardLast
  ) internal pure returns (uint256) {
    uint256 rewardNow = (rewardHarvested * vestingVolume) / PRECISION;
    return rewardNow > rewardLast ? rewardNow - rewardLast : 0;
  }

  function _getBlockTime() internal view virtual returns (uint32) {
    return uint32(block.timestamp);
  }
}

File 2 of 17 : KSMath.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

library KSMath {
  function max(uint256 a, uint256 b) internal pure returns (uint256) {
    return a >= b ? a : b;
  }

  function min(uint256 a, uint256 b) internal pure returns (uint256) {
    return a >= b ? b : a;
  }
}

File 3 of 17 : IKyberSwapElasticLM.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IKyberSwapElasticLMEvents} from './IKyberSwapElasticLMEvents.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';

interface IKyberSwapElasticLM is IKyberSwapElasticLMEvents {
  struct RewardData {
    address rewardToken;
    uint256 rewardUnclaimed;
  }

  struct LMPoolInfo {
    address poolAddress;
    uint32 startTime;
    uint32 endTime;
    uint256 totalSecondsClaimed; // scaled by (1 << 96)
    RewardData[] rewards;
    uint256 feeTarget;
    uint256 numStakes;
  }

  struct PositionInfo {
    address owner;
    uint256 liquidity;
  }

  struct StakeInfo {
    uint128 secondsPerLiquidityLast;
    uint256[] rewardLast;
    uint256[] rewardPending;
    uint256[] rewardHarvested;
    int256 feeFirst;
    uint256 liquidity;
  }

  // input data in harvestMultiplePools function
  struct HarvestData {
    uint256[] pIds;
  }

  // avoid stack too deep error
  struct RewardCalculationData {
    uint128 secondsPerLiquidityNow;
    int256 feeNow;
    uint256 vestingVolume;
    uint256 totalSecondsUnclaimed;
    uint256 secondsPerLiquidity;
    uint256 secondsClaim; // scaled by (1 << 96)
  }

  /**
   * @dev Add new pool to LM
   * @param poolAddr pool address
   * @param startTime start time of liquidity mining
   * @param endTime end time of liquidity mining
   * @param rewardTokens reward token list for pool
   * @param rewardAmounts reward amount of list token
   * @param feeTarget fee target for pool
   *
   */
  function addPool(
    address poolAddr,
    uint32 startTime,
    uint32 endTime,
    address[] calldata rewardTokens,
    uint256[] calldata rewardAmounts,
    uint256 feeTarget
  ) external;

  /**
   * @dev Renew a pool to start another LM program
   * @param pId pool id to update
   * @param startTime start time of liquidity mining
   * @param endTime end time of liquidity mining
   * @param rewardAmounts reward amount of list token
   * @param feeTarget fee target for pool
   *
   */
  function renewPool(
    uint256 pId,
    uint32 startTime,
    uint32 endTime,
    uint256[] calldata rewardAmounts,
    uint256 feeTarget
  ) external;

  /**
   * @dev Deposit NFT
   * @param nftIds list nft id
   *
   */
  function deposit(uint256[] calldata nftIds) external;

  /**
   * @dev Deposit NFTs into the pool and join farms if applicable
   * @param pId pool id to join farm
   * @param nftIds List of NFT ids from BasePositionManager, should match with the pId
   *
   */
  function depositAndJoin(uint256 pId, uint256[] calldata nftIds) external;

  /**
   * @dev Withdraw NFT, must exit all pool before call.
   * @param nftIds list nft id
   *
   */
  function withdraw(uint256[] calldata nftIds) external;

  /**
   * @dev Join pools
   * @param pId pool id to join
   * @param nftIds nfts to join
   * @param liqs list liquidity value to join each nft
   *
   */
  function join(uint256 pId, uint256[] calldata nftIds, uint256[] calldata liqs) external;

  /**
   * @dev Exit from pools
   * @param pId pool ids to exit
   * @param nftIds list nfts id
   * @param liqs list liquidity value to exit from each nft
   *
   */
  function exit(uint256 pId, uint256[] calldata nftIds, uint256[] calldata liqs) external;

  /**
   * @dev Claim rewards for a list of pools for a list of nft positions
   * @param nftIds List of NFT ids to harvest
   * @param datas List of pool ids to harvest for each nftId, encoded into bytes
   */
  function harvestMultiplePools(uint256[] calldata nftIds, bytes[] calldata datas) external;

  /**
   * @dev remove liquidity from elastic for a list of nft position, also update on farm
   * @param nftId to remove
   * @param liquidity liquidity amount to remove from nft
   * @param amount0Min expected min amount of token0 should receive
   * @param amount1Min expected min amount of token1 should receive
   * @param deadline deadline of this tx
   * @param isReceiveNative should unwrap native or not
   * @param claimFeeAndRewards also claim LP Fee and farm rewards
   */
  function removeLiquidity(
    uint256 nftId,
    uint128 liquidity,
    uint256 amount0Min,
    uint256 amount1Min,
    uint256 deadline,
    bool isReceiveNative,
    bool[2] calldata claimFeeAndRewards
  ) external;

  /**
   * @dev Claim fee from elastic for a list of nft positions
   * @param nftIds List of NFT ids to claim
   * @param amount0Min expected min amount of token0 should receive
   * @param amount1Min expected min amount of token1 should receive
   * @param poolAddress address of Elastic pool of those nfts
   * @param isReceiveNative should unwrap native or not
   * @param deadline deadline of this tx
   */
  function claimFee(
    uint256[] calldata nftIds,
    uint256 amount0Min,
    uint256 amount1Min,
    address poolAddress,
    bool isReceiveNative,
    uint256 deadline
  ) external;

  /**
   * @dev Operator only. Call to withdraw all reward from list pools.
   * @param rewards list reward address erc20 token
   * @param amounts amount to withdraw
   *
   */
  function emergencyWithdrawForOwner(
    address[] calldata rewards,
    uint256[] calldata amounts
  ) external;

  /**
   * @dev Withdraw NFT, can call any time, reward will be reset. Must enable this func by operator
   * @param pIds list pool to withdraw
   *
   */
  function emergencyWithdraw(uint256[] calldata pIds) external;

  /**
   * @dev get list of pool that this nft joined
   * @param nftId to get
   */
  function getJoinedPools(uint256 nftId) external view returns (uint256[] memory poolIds);

  /**
   * @dev get list of pool that this nft joined, only in a specific range
   * @param nftId to get
   * @param fromIndex index from
   * @param toIndex index to
   */
  function getJoinedPoolsInRange(
    uint256 nftId,
    uint256 fromIndex,
    uint256 toIndex
  ) external view returns (uint256[] memory poolIds);

  /**
   * @dev get user's info (staked info) of a nft in a pool
   * @param nftId to get
   * @param pId to get
   */
  function getUserInfo(
    uint256 nftId,
    uint256 pId
  )
    external
    view
    returns (uint256 liquidity, uint256[] memory rewardPending, uint256[] memory rewardLast);

  /**
   * @dev get pool info
   * @param pId to get
   */
  function getPoolInfo(
    uint256 pId
  )
    external
    view
    returns (
      address poolAddress,
      uint32 startTime,
      uint32 endTime,
      uint256 totalSecondsClaimed,
      uint256 feeTarget,
      uint256 numStakes,
      //index reward => reward data
      address[] memory rewardTokens,
      uint256[] memory rewardUnclaimeds
    );

  /**
   * @dev get list of deposited nfts of an address
   * @param user address of user to get
   */
  function getDepositedNFTs(address user) external view returns (uint256[] memory listNFTs);

  function nft() external view returns (IERC721);

  function poolLength() external view returns (uint256);

  function getRewardCalculationData(
    uint256 nftId,
    uint256 pId
  ) external view returns (RewardCalculationData memory data);
}

File 4 of 17 : IKSElasticLMHelper.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IKSElasticLMHelper {
  function checkPool(
    address pAddress,
    address nftContract,
    uint256 nftId
  ) external view returns (bool);

  /// @dev use virtual to be overrided to mock data for fuzz tests
  function getActiveTime(
    address pAddr,
    address nftContract,
    uint256 nftId
  ) external view returns (uint128);

  function getSignedFee(address nftContract, uint256 nftId) external view returns (int256);

  function getSignedFeePool(
    address poolAddress,
    address nftContract,
    uint256 nftId
  ) external view returns (int256);

  function getLiq(address nftContract, uint256 nftId) external view returns (uint128);

  function getPair(address nftContract, uint256 nftId) external view returns (address, address);
}

File 5 of 17 : IBasePositionManager.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

interface IBasePositionManager {
  struct Position {
    // the nonce for permits
    uint96 nonce;
    // the address that is approved for spending this token
    address operator;
    // the ID of the pool with which this token is connected
    uint80 poolId;
    // the tick range of the position
    int24 tickLower;
    int24 tickUpper;
    // the liquidity of the position
    uint128 liquidity;
    // the current rToken that the position owed
    uint256 rTokenOwed;
    // fee growth per unit of liquidity as of the last update to liquidity
    uint256 feeGrowthInsideLast;
  }

  struct PoolInfo {
    address token0;
    uint16 fee;
    address token1;
  }

  struct MintParams {
    address token0;
    address token1;
    uint24 fee;
    int24 tickLower;
    int24 tickUpper;
    int24[2] ticksPrevious;
    uint256 amount0Desired;
    uint256 amount1Desired;
    uint256 amount0Min;
    uint256 amount1Min;
    address recipient;
    uint256 deadline;
  }

  struct IncreaseLiquidityParams {
    uint256 tokenId;
    uint256 amount0Desired;
    uint256 amount1Desired;
    uint256 amount0Min;
    uint256 amount1Min;
    uint256 deadline;
  }

  struct RemoveLiquidityParams {
    uint256 tokenId;
    uint128 liquidity;
    uint256 amount0Min;
    uint256 amount1Min;
    uint256 deadline;
  }

  struct BurnRTokenParams {
    uint256 tokenId;
    uint256 amount0Min;
    uint256 amount1Min;
    uint256 deadline;
  }

  function positions(
    uint256 tokenId
  ) external view returns (Position memory pos, PoolInfo memory info);

  function addressToPoolId(address pool) external view returns (uint80);

  function WETH() external view returns (address);

  function mint(
    MintParams calldata params
  )
    external
    payable
    returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);

  function addLiquidity(
    IncreaseLiquidityParams calldata params
  )
    external
    payable
    returns (uint128 liquidity, uint256 amount0, uint256 amount1, uint256 additionalRTokenOwed);

  function removeLiquidity(
    RemoveLiquidityParams calldata params
  ) external returns (uint256 amount0, uint256 amount1, uint256 additionalRTokenOwed);

  function syncFeeGrowth(uint256 tokenId) external returns (uint256 additionalRTokenOwed);

  function burnRTokens(
    BurnRTokenParams calldata params
  ) external returns (uint256 rTokenQty, uint256 amount0, uint256 amount1);

  function transferAllTokens(address token, uint256 minAmount, address recipient) external payable;

  function unwrapWeth(uint256 minAmount, address recipient) external payable;
}

File 6 of 17 : IPoolStorage.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity >=0.8.0;

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

import {IFactory} from './IFactory.sol';

interface IPoolStorage {
  struct PoolData {
    uint160 sqrtP;
    int24 nearestCurrentTick;
    int24 currentTick;
    bool locked;
    uint128 baseL;
    uint128 reinvestL;
    uint128 reinvestLLast;
    uint256 feeGrowthGlobal;
    uint128 secondsPerLiquidityGlobal;
    uint32 secondsPerLiquidityUpdateTime;
  }

  // data stored for each initialized individual tick
  struct TickData {
    // gross liquidity of all positions in tick
    uint128 liquidityGross;
    // liquidity quantity to be added | removed when tick is crossed up | down
    int128 liquidityNet;
    // fee growth per unit of liquidity on the other side of this tick (relative to current tick)
    // only has relative meaning, not absolute — the value depends on when the tick is initialized
    uint256 feeGrowthOutside;
    // seconds spent on the other side of this tick (relative to current tick)
    // only has relative meaning, not absolute — the value depends on when the tick is initialized
    uint128 secondsPerLiquidityOutside;
  }

  /// @notice The contract that deployed the pool, which must adhere to the IFactory interface
  /// @return The contract address
  function factory() external view returns (IFactory);

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

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

  /// @notice The fee to be charged for a swap in basis points
  /// @return The swap fee in basis points
  function swapFeeBps() external view returns (uint16);

  /// @notice The pool tick distance
  /// @dev Ticks can only be initialized and used at multiples of this value
  /// It remains an int24 to avoid casting even though it is >= 1.
  /// e.g: a tickDistance of 5 means ticks can be initialized every 5th tick, i.e., ..., -10, -5, 0, 5, 10, ...
  /// @return The tick distance
  function tickDistance() external view returns (int24);

  /// @notice Maximum gross liquidity that an initialized tick can have
  /// @dev This is to prevent overflow the pool's active base liquidity (uint128)
  /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
  /// @return The max amount of liquidity per tick
  function maxTickLiquidity() external view returns (uint128);

  /// @notice Look up information about a specific tick in the pool
  /// @param tick The tick to look up
  /// @return liquidityGross total liquidity amount from positions that uses this tick as a lower or upper tick
  /// liquidityNet how much liquidity changes when the pool tick crosses above the tick
  /// feeGrowthOutside the fee growth on the other side of the tick relative to the current tick
  /// secondsPerLiquidityOutside the seconds spent on the other side of the tick relative to the current tick
  function ticks(int24 tick)
    external
    view
    returns (
      uint128 liquidityGross,
      int128 liquidityNet,
      uint256 feeGrowthOutside,
      uint128 secondsPerLiquidityOutside
    );

  /// @notice Returns the previous and next initialized ticks of a specific tick
  /// @dev If specified tick is uninitialized, the returned values are zero.
  /// @param tick The tick to look up
  function initializedTicks(int24 tick) external view returns (int24 previous, int24 next);

  /// @notice Returns the information about a position by the position's key
  /// @return liquidity the liquidity quantity of the position
  /// @return feeGrowthInsideLast fee growth inside the tick range as of the last mint / burn action performed
  function getPositions(
    address owner,
    int24 tickLower,
    int24 tickUpper
  ) external view returns (uint128 liquidity, uint256 feeGrowthInsideLast);

  /// @notice Fetches the pool's prices, ticks and lock status
  /// @return sqrtP sqrt of current price: sqrt(token1/token0)
  /// @return currentTick pool's current tick
  /// @return nearestCurrentTick pool's nearest initialized tick that is <= currentTick
  /// @return locked true if pool is locked, false otherwise
  function getPoolState()
    external
    view
    returns (
      uint160 sqrtP,
      int24 currentTick,
      int24 nearestCurrentTick,
      bool locked
    );

  /// @notice Fetches the pool's liquidity values
  /// @return baseL pool's base liquidity without reinvest liqudity
  /// @return reinvestL the liquidity is reinvested into the pool
  /// @return reinvestLLast last cached value of reinvestL, used for calculating reinvestment token qty
  function getLiquidityState()
    external
    view
    returns (
      uint128 baseL,
      uint128 reinvestL,
      uint128 reinvestLLast
    );

  /// @return feeGrowthGlobal All-time fee growth per unit of liquidity of the pool
  function getFeeGrowthGlobal() external view returns (uint256);

  /// @return secondsPerLiquidityGlobal All-time seconds per unit of liquidity of the pool
  /// @return lastUpdateTime The timestamp in which secondsPerLiquidityGlobal was last updated
  function getSecondsPerLiquidityData()
    external
    view
    returns (uint128 secondsPerLiquidityGlobal, uint32 lastUpdateTime);

  /// @notice Calculates and returns the active time per unit of liquidity until current block.timestamp
  /// @param tickLower The lower tick (of a position)
  /// @param tickUpper The upper tick (of a position)
  /// @return secondsPerLiquidityInside active time (multiplied by 2^96)
  /// between the 2 ticks, per unit of liquidity.
  function getSecondsPerLiquidityInside(int24 tickLower, int24 tickUpper)
    external
    view
    returns (uint128 secondsPerLiquidityInside);
}

File 7 of 17 : KSAdmin.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

abstract contract KSAdmin {
  address public admin;
  mapping(address => bool) public operators; // address => bool

  event TransferAdmin(address indexed admin);
  event UpdateOperator(address indexed user, bool grantOrRevoke);

  modifier isAdmin() {
    require(msg.sender == admin, 'forbidden');
    _;
  }

  modifier isOperator() {
    require(operators[msg.sender], 'forbidden');
    _;
  }

  constructor() {
    admin = msg.sender;
    operators[msg.sender] = true;
  }

  function transferAdmin(address _admin) external virtual isAdmin {
    require(_admin != address(0), 'forbidden');

    admin = _admin;

    emit TransferAdmin(_admin);
  }

  function updateOperator(address user, bool grantOrRevoke) external isAdmin {
    operators[user] = grantOrRevoke;

    emit UpdateOperator(user, grantOrRevoke);
  }
}

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

pragma solidity ^0.8.0;

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

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

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

pragma solidity ^0.8.0;

import "../IERC20.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;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    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));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    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");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @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");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

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

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastvalue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly {
            result := store
        }

        return result;
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 13 of 17 : IKyberSwapElasticLMEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IKSElasticLMHelper} from './IKSElasticLMHelper.sol';

interface IKyberSwapElasticLMEvents {
  event AddPool(
    uint256 indexed pId,
    address poolAddress,
    uint32 startTime,
    uint32 endTime,
    uint256 feeTarget
  );

  event RenewPool(uint256 indexed pid, uint32 startTime, uint32 endTime, uint256 feeTarget);

  event Deposit(address sender, uint256 indexed nftId);

  event Withdraw(address sender, uint256 indexed nftId);

  event Join(uint256 indexed nftId, uint256 indexed pId, uint256 indexed liq);

  event Exit(address to, uint256 indexed nftId, uint256 indexed pId, uint256 indexed liq);

  event SyncLiq(uint256 indexed nftId, uint256 indexed pId, uint256 indexed liq);

  event Harvest(uint256 indexed nftId, address to, address reward, uint256 indexed amount);

  event EmergencyEnabled();

  event UpdateSpecialFeatureEnabled(bool enableOrDisable);

  event EmergencyWithdrawForOwner(address reward, uint256 indexed amount);

  event EmergencyWithdraw(address sender, uint256 indexed nftId);

  event LMHelperUpdated(IKSElasticLMHelper helper);
}

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

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 16 of 17 : IFactory.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity >=0.8.0;

/// @title KyberSwap v2 factory
/// @notice Deploys KyberSwap v2 pools and manages control over government fees
interface IFactory {
  /// @notice Emitted when a pool is created
  /// @param token0 First pool token by address sort order
  /// @param token1 Second pool token by address sort order
  /// @param swapFeeBps Fee to be collected upon every swap in the pool, in basis points
  /// @param tickDistance Minimum number of ticks between initialized ticks
  /// @param pool The address of the created pool
  event PoolCreated(
    address indexed token0,
    address indexed token1,
    uint16 indexed swapFeeBps,
    int24 tickDistance,
    address pool
  );

  /// @notice Emitted when a new fee is enabled for pool creation via the factory
  /// @param swapFeeBps Fee to be collected upon every swap in the pool, in basis points
  /// @param tickDistance Minimum number of ticks between initialized ticks for pools created with the given fee
  event SwapFeeEnabled(uint16 indexed swapFeeBps, int24 indexed tickDistance);

  /// @notice Emitted when vesting period changes
  /// @param vestingPeriod The maximum time duration for which LP fees
  /// are proportionally burnt upon LP removals
  event VestingPeriodUpdated(uint32 vestingPeriod);

  /// @notice Emitted when configMaster changes
  /// @param oldConfigMaster configMaster before the update
  /// @param newConfigMaster configMaster after the update
  event ConfigMasterUpdated(address oldConfigMaster, address newConfigMaster);

  /// @notice Emitted when fee configuration changes
  /// @param feeTo Recipient of government fees
  /// @param governmentFeeBps Fee amount, in basis points,
  /// to be collected out of the fee charged for a pool swap
  event FeeConfigurationUpdated(address feeTo, uint16 governmentFeeBps);

  /// @notice Emitted when whitelist feature is enabled
  event WhitelistEnabled();

  /// @notice Emitted when whitelist feature is disabled
  event WhitelistDisabled();

  /// @notice Returns the maximum time duration for which LP fees
  /// are proportionally burnt upon LP removals
  function vestingPeriod() external view returns (uint32);

  /// @notice Returns the tick distance for a specified fee.
  /// @dev Once added, cannot be updated or removed.
  /// @param swapFeeBps Swap fee, in basis points.
  /// @return The tick distance. Returns 0 if fee has not been added.
  function feeAmountTickDistance(uint16 swapFeeBps) external view returns (int24);

  /// @notice Returns the address which can update the fee configuration
  function configMaster() external view returns (address);

  /// @notice Returns the keccak256 hash of the Pool creation code
  /// This is used for pre-computation of pool addresses
  function poolInitHash() external view returns (bytes32);

  /// @notice Fetches the recipient of government fees
  /// and current government fee charged in basis points
  function feeConfiguration() external view returns (address _feeTo, uint16 _governmentFeeBps);

  /// @notice Returns the status of whitelisting feature of NFT managers
  /// If true, anyone can mint liquidity tokens
  /// Otherwise, only whitelisted NFT manager(s) are allowed to mint liquidity tokens
  function whitelistDisabled() external view returns (bool);

  //// @notice Returns all whitelisted NFT managers
  /// If the whitelisting feature is turned on,
  /// only whitelisted NFT manager(s) are allowed to mint liquidity tokens
  function getWhitelistedNFTManagers() external view returns (address[] memory);

  /// @notice Checks if sender is a whitelisted NFT manager
  /// If the whitelisting feature is turned on,
  /// only whitelisted NFT manager(s) are allowed to mint liquidity tokens
  /// @param sender address to be checked
  /// @return true if sender is a whistelisted NFT manager, false otherwise
  function isWhitelistedNFTManager(address sender) external view returns (bool);

  /// @notice Returns the pool address for a given pair of tokens and a swap fee
  /// @dev Token order does not matter
  /// @param tokenA Contract address of either token0 or token1
  /// @param tokenB Contract address of the other token
  /// @param swapFeeBps Fee to be collected upon every swap in the pool, in basis points
  /// @return pool The pool address. Returns null address if it does not exist
  function getPool(
    address tokenA,
    address tokenB,
    uint16 swapFeeBps
  ) external view returns (address pool);

  /// @notice Fetch parameters to be used for pool creation
  /// @dev Called by the pool constructor to fetch the parameters of the pool
  /// @return factory The factory address
  /// @return token0 First pool token by address sort order
  /// @return token1 Second pool token by address sort order
  /// @return swapFeeBps Fee to be collected upon every swap in the pool, in basis points
  /// @return tickDistance Minimum number of ticks between initialized ticks
  function parameters()
    external
    view
    returns (
      address factory,
      address token0,
      address token1,
      uint16 swapFeeBps,
      int24 tickDistance
    );

  /// @notice Creates a pool for the given two tokens and fee
  /// @param tokenA One of the two tokens in the desired pool
  /// @param tokenB The other of the two tokens in the desired pool
  /// @param swapFeeBps Desired swap fee for the pool, in basis points
  /// @dev Token order does not matter. tickDistance is determined from the fee.
  /// Call will revert under any of these conditions:
  ///     1) pool already exists
  ///     2) invalid swap fee
  ///     3) invalid token arguments
  /// @return pool The address of the newly created pool
  function createPool(
    address tokenA,
    address tokenB,
    uint16 swapFeeBps
  ) external returns (address pool);

  /// @notice Enables a fee amount with the given tickDistance
  /// @dev Fee amounts may never be removed once enabled
  /// @param swapFeeBps The fee amount to enable, in basis points
  /// @param tickDistance The distance between ticks to be enforced for all pools created with the given fee amount
  function enableSwapFee(uint16 swapFeeBps, int24 tickDistance) external;

  /// @notice Updates the address which can update the fee configuration
  /// @dev Must be called by the current configMaster
  function updateConfigMaster(address) external;

  /// @notice Updates the vesting period
  /// @dev Must be called by the current configMaster
  function updateVestingPeriod(uint32) external;

  /// @notice Updates the address receiving government fees and fee quantity
  /// @dev Only configMaster is able to perform the update
  /// @param feeTo Address to receive government fees collected from pools
  /// @param governmentFeeBps Fee amount, in basis points,
  /// to be collected out of the fee charged for a pool swap
  function updateFeeConfiguration(address feeTo, uint16 governmentFeeBps) external;

  /// @notice Enables the whitelisting feature
  /// @dev Only configMaster is able to perform the update
  function enableWhitelist() external;

  /// @notice Disables the whitelisting feature
  /// @dev Only configMaster is able to perform the update
  function disableWhitelist() external;
}

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

pragma solidity ^0.8.0;

/**
 * @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
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 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://diligence.consensys.net/posts/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.5.11/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 functionCall(target, data, "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");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason 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 {
            // 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IERC721","name":"_nft","type":"address"},{"internalType":"contract IKSElasticLMHelper","name":"_helper","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"pId","type":"uint256"},{"indexed":false,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":false,"internalType":"uint32","name":"startTime","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"endTime","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"feeTarget","type":"uint256"}],"name":"AddPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[],"name":"EmergencyEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"reward","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdrawForOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"pId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"liq","type":"uint256"}],"name":"Exit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nftId","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"reward","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Harvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nftId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"pId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"liq","type":"uint256"}],"name":"Join","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IKSElasticLMHelper","name":"helper","type":"address"}],"name":"LMHelperUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"startTime","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"endTime","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"feeTarget","type":"uint256"}],"name":"RenewPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nftId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"pId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"liq","type":"uint256"}],"name":"SyncLiq","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"TransferAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"grantOrRevoke","type":"bool"}],"name":"UpdateOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enableOrDisable","type":"bool"}],"name":"UpdateSpecialFeatureEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardAmounts","type":"uint256[]"},{"internalType":"uint256","name":"feeTarget","type":"uint256"}],"name":"addPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"bool","name":"isReceiveNative","type":"bool"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"claimFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pId","type":"uint256"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"depositAndJoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyEnable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"rewards","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"emergencyWithdrawForOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pId","type":"uint256"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"uint256[]","name":"liqs","type":"uint256[]"}],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getDepositedNFTs","outputs":[{"internalType":"uint256[]","name":"listNFTs","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"getJoinedPools","outputs":[{"internalType":"uint256[]","name":"poolIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"uint256","name":"fromIndex","type":"uint256"},{"internalType":"uint256","name":"toIndex","type":"uint256"}],"name":"getJoinedPoolsInRange","outputs":[{"internalType":"uint256[]","name":"poolIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pId","type":"uint256"}],"name":"getPoolInfo","outputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"},{"internalType":"uint256","name":"totalSecondsClaimed","type":"uint256"},{"internalType":"uint256","name":"feeTarget","type":"uint256"},{"internalType":"uint256","name":"numStakes","type":"uint256"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardUnclaimeds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"uint256","name":"pId","type":"uint256"}],"name":"getRewardCalculationData","outputs":[{"components":[{"internalType":"uint128","name":"secondsPerLiquidityNow","type":"uint128"},{"internalType":"int256","name":"feeNow","type":"int256"},{"internalType":"uint256","name":"vestingVolume","type":"uint256"},{"internalType":"uint256","name":"totalSecondsUnclaimed","type":"uint256"},{"internalType":"uint256","name":"secondsPerLiquidity","type":"uint256"},{"internalType":"uint256","name":"secondsClaim","type":"uint256"}],"internalType":"struct IKyberSwapElasticLM.RewardCalculationData","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"uint256","name":"pId","type":"uint256"}],"name":"getUserInfo","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256[]","name":"rewardPending","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardLast","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"bytes[]","name":"datas","type":"bytes[]"}],"name":"harvestMultiplePools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"isEmergencyWithdrawnNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pId","type":"uint256"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"uint256[]","name":"liqs","type":"uint256[]"}],"name":"join","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nft","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"operators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pools","outputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"},{"internalType":"uint256","name":"totalSecondsClaimed","type":"uint256"},{"internalType":"uint256","name":"feeTarget","type":"uint256"},{"internalType":"uint256","name":"numStakes","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"positions","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"isReceiveNative","type":"bool"},{"internalType":"bool[2]","name":"claimFeeAndRewards","type":"bool[2]"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pId","type":"uint256"},{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"},{"internalType":"uint256[]","name":"rewardAmounts","type":"uint256[]"},{"internalType":"uint256","name":"feeTarget","type":"uint256"}],"name":"renewPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"specialFeatureEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakes","outputs":[{"internalType":"uint128","name":"secondsPerLiquidityLast","type":"uint128"},{"internalType":"int256","name":"feeFirst","type":"int256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"transferAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IKSElasticLMHelper","name":"_helper","type":"address"}],"name":"updateHelper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"grantOrRevoke","type":"bool"}],"name":"updateOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enableOrDisable","type":"bool"}],"name":"updateSpecialFeatureEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60c06040523480156200001157600080fd5b5060405162005f7738038062005f77833981016040819052620000349162000125565b600160008181558154336001600160a01b031991821681178455825260026020908152604092839020805460ff19169094179093556001600160a01b038581166080819052600380549093169186169190911790915581516315ab88c960e31b81529151909263ad5c46489260048082019391829003018186803b158015620000bc57600080fd5b505afa158015620000d1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000f7919062000164565b6001600160a01b031660a052506200018b9050565b6001600160a01b03811681146200012257600080fd5b50565b600080604083850312156200013957600080fd5b825162000146816200010c565b602084015190925062000159816200010c565b809150509250929050565b6000602082840312156200017757600080fd5b815162000184816200010c565b9392505050565b60805160a051615d3a6200023d600039600081816102fa01528181614681015261478501526000818161038001528181610faf01528181611d2501528181612128015281816123d2015281816125fd01528181612a0301528181612e1801528181612eca01528181613355015281816134900152818161366001528181613c3301528181613d8a015281816144f5015281816145c901528181614650015281816149820152614b100152615d3a6000f3fe6080604052600436106101fd5760003560e01c806375829def1161010d578063b36b26c5116100a0578063c7ca58701161006f578063c7ca58701461074b578063d1941b061461076b578063dc168a921461078b578063e7762a0c146107ba578063f851a4401461082f57600080fd5b8063b36b26c5146106bc578063b63ff916146106dc578063b82562751461070c578063bb5ae7751461072c57600080fd5b806399fbab88116100dc57806399fbab881461052f578063ac4afa381461058e578063affe737914610631578063b221e5fd1461064657600080fd5b806375829def146104af5780638bcc4166146104cf57806391fb2f15146104ef578063983d95ce1461050f57600080fd5b806347ccca021161019057806361657b121161015f57806361657b121461040f57806366b5a6641461042f578063683f3eda1461044f5780636d44a3b21461046f578063705b5bef1461048f57600080fd5b806347ccca021461036e57806349b35168146103a257806350523cea146103cf578063598b8e71146103ef57600080fd5b80632f380b35116101cc5780632f380b35146102b45780633fc8cef3146102e857806341e817ab14610334578063430ccd9b1461035457600080fd5b8063078ded5d14610209578063081e3eda1461022b57806313e7c9d814610254578063180f80361461029457600080fd5b3661020457005b600080fd5b34801561021557600080fd5b5061022961022436600461515c565b61084f565b005b34801561023757600080fd5b5061024160045481565b6040519081526020015b60405180910390f35b34801561026057600080fd5b5061028461026f3660046151e7565b60026020526000908152604090205460ff1681565b604051901515815260200161024b565b3480156102a057600080fd5b506102296102af366004615204565b610b0b565b3480156102c057600080fd5b506102d46102cf36600461526f565b610c8d565b60405161024b9897969594939291906152c3565b3480156102f457600080fd5b5061031c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161024b565b34801561034057600080fd5b5061022961034f366004615363565b610e37565b34801561036057600080fd5b50600b546102849060ff1681565b34801561037a57600080fd5b5061031c7f000000000000000000000000000000000000000000000000000000000000000081565b3480156103ae57600080fd5b506103c26103bd3660046153dc565b61118b565b60405161024b9190615408565b3480156103db57600080fd5b506102296103ea366004615429565b6112e3565b3480156103fb57600080fd5b5061022961040a366004615446565b611361565b34801561041b57600080fd5b5061022961042a366004615204565b6113d3565b34801561043b57600080fd5b5061022961044a366004615363565b611507565b34801561045b57600080fd5b5061022961046a366004615487565b6115d8565b34801561047b57600080fd5b5061022961048a3660046154d2565b611674565b34801561049b57600080fd5b506103c26104aa36600461526f565b6116fd565b3480156104bb57600080fd5b506102296104ca3660046151e7565b6117b5565b3480156104db57600080fd5b506102296104ea3660046151e7565b61184f565b3480156104fb57600080fd5b5061022961050a36600461550b565b6118c7565b34801561051b57600080fd5b5061022961052a366004615446565b611b60565b34801561053b57600080fd5b5061056f61054a36600461526f565b600660205260009081526040902080546001909101546001600160a01b039091169082565b604080516001600160a01b03909316835260208301919091520161024b565b34801561059a57600080fd5b506105f06105a936600461526f565b60056020526000908152604090208054600182015460038301546004909301546001600160a01b0383169363ffffffff600160a01b8504811694600160c01b900416929186565b604080516001600160a01b0397909716875263ffffffff958616602088015293909416928501929092526060840152608083015260a082015260c00161024b565b34801561063d57600080fd5b50610229611e30565b34801561065257600080fd5b506106976106613660046155b5565b60076020908152600092835260408084209091529082529020805460048201546005909201546001600160801b03909116919083565b604080516001600160801b03909416845260208401929092529082015260600161024b565b3480156106c857600080fd5b506103c26106d73660046151e7565b611ed5565b3480156106e857600080fd5b506102846106f736600461526f565b600a6020526000908152604090205460ff1681565b34801561071857600080fd5b506102296107273660046155d7565b611eff565b34801561073857600080fd5b50600b5461028490610100900460ff1681565b34801561075757600080fd5b50610229610766366004615672565b6122ad565b34801561077757600080fd5b50610229610786366004615446565b6126d6565b34801561079757600080fd5b506107ab6107a63660046155b5565b612b10565b60405161024b939291906156ea565b3480156107c657600080fd5b506107da6107d53660046155b5565b612d81565b60405161024b9190600060c0820190506001600160801b0383511682526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015292915050565b34801561083b57600080fd5b5060015461031c906001600160a01b031681565b3360009081526002602052604090205460ff166108875760405162461bcd60e51b815260040161087e90615715565b60405180910390fd5b6000868152600560205260409020805463ffffffff428116600160a01b9092041611806108c45750805463ffffffff428116600160c01b90920416105b6109105760405162461bcd60e51b815260206004820152601960248201527f72656e65773a20696e76616c696420706f6f6c20737461746500000000000000604482015260640161087e565b6002810154831461095b5760405162461bcd60e51b81526020600482015260156024820152740e4cadccaee7440d2dcecc2d8d2c840d8cadccee8d605b1b604482015260640161087e565b4263ffffffff168663ffffffff1611801561098157508563ffffffff168563ffffffff16115b6109c45760405162461bcd60e51b815260206004820152601460248201527372656e65773a20696e76616c69642074696d657360601b604482015260640161087e565b600481015415610a0f5760405162461bcd60e51b815260206004820152601660248201527572656e65773a20706f6f6c20686173207374616b657360501b604482015260640161087e565b805463ffffffff868116600160c01b0263ffffffff60c01b19918916600160a01b029190911667ffffffffffffffff60a01b1990921691909117178155600060018201819055600382018390555b83811015610ab757848482818110610a7757610a77615738565b90506020020135826002018281548110610a9357610a93615738565b6000918252602090912060016002909202010155610ab081615764565b9050610a5d565b506040805163ffffffff80891682528716602082015290810183905287907feda7bcc7e20158ff9fa87a4200758e927bbb3c8823e648403bdd6053b96dd0ad9060600160405180910390a250505050505050565b60026000541415610b2e5760405162461bcd60e51b815260040161087e9061577f565b60026000558281808214610b545760405162461bcd60e51b815260040161087e906157b6565b60005b85811015610c7f573360066000898985818110610b7657610b76615738565b60209081029290920135835250810191909152604001600020546001600160a01b031614610bdb5760405162461bcd60e51b81526020600482015260126024820152713430b93b32b9ba1d103737ba1037bbb732b960711b604482015260640161087e565b6000858583818110610bef57610bef615738565b9050602002810190610c0191906157de565b810190610c0e9190615892565b905060005b815151811015610c6c57610c5c898985818110610c3257610c32615738565b9050602002013583600001518381518110610c4f57610c4f615738565b6020026020010151612fc9565b610c6581615764565b9050610c13565b505080610c7890615764565b9050610b57565b505060016000555050505050565b6000818152600560205260409020805460018201546003830154600484015460028501546001600160a01b0385169563ffffffff600160a01b8704811696600160c01b900416949392916060918291806001600160401b03811115610cf457610cf4615824565b604051908082528060200260200182016040528015610d1d578160200160208202803683370190505b509350806001600160401b03811115610d3857610d38615824565b604051908082528060200260200182016040528015610d61578160200160208202803683370190505b50925060005b81811015610e2957826002018181548110610d8457610d84615738565b600091825260209091206002909102015485516001600160a01b0390911690869083908110610db557610db5615738565b60200260200101906001600160a01b031690816001600160a01b031681525050826002018181548110610dea57610dea615738565b906000526020600020906002020160010154848281518110610e0e57610e0e615738565b6020908102919091010152610e2281615764565b9050610d67565b505050919395975091939597565b60026000541415610e5a5760405162461bcd60e51b815260040161087e9061577f565b60026000558281808214610e805760405162461bcd60e51b815260040161087e906157b6565b8660045411610ea15760405162461bcd60e51b815260040161087e90615966565b6000878152600560205260409020805463ffffffff428116600160a01b9092041611801590610ee357508054600160c01b900463ffffffff164263ffffffff16105b610f1e5760405162461bcd60e51b815260206004820152600c60248201526b496e76616c69642074696d6560a01b604482015260640161087e565b60005b8681101561117b5733600660008a8a85818110610f4057610f40615738565b60209081029290920135835250810191909152604001600020546001600160a01b031614610f9c5760405162461bcd60e51b81526020600482015260096024820152682737ba1037bbb732b960b91b604482015260640161087e565b6003546001600160a01b0316635c6d96a97f00000000000000000000000000000000000000000000000000000000000000008a8a85818110610fe057610fe0615738565b6040516001600160e01b031960e087901b1681526001600160a01b039094166004850152602002919091013560248301525060440160206040518083038186803b15801561102d57600080fd5b505afa158015611041573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611065919061598f565b6001600160801b0316600660008a8a8581811061108457611084615738565b905060200201358152602001908152602001600020600101819055506000600760008a8a858181106110b8576110b8615738565b90506020020135815260200190815260200160002060008b8152602001908152602001600020905080600501546000141561112e5761112989898481811061110257611102615738565b905060200201358b89898681811061111c5761111c615738565b9050602002013586613303565b61116a565b61116a89898481811061114357611143615738565b905060200201358b89898681811061115d5761115d615738565b90506020020135866137a9565b5061117481615764565b9050610f21565b5050600160005550505050505050565b6060818311156111d35760405162461bcd60e51b81526020600482015260136024820152720cce4deda92dcc8caf0407c40e8de92dcc8caf606b1b604482015260640161087e565b60008481526008602052604090206111ea90613a36565b821061122c5760405162461bcd60e51b81526020600482015260116024820152700e8de92dcc8caf0407c7a40d8cadccee8d607b1b604482015260640161087e565b61123683836159ac565b6112419060016159c3565b6001600160401b0381111561125857611258615824565b604051908082528060200260200182016040528015611281578160200160208202803683370190505b509050825b8281116112db5760008581526008602052604090206112a59082613a40565b826112b086846159ac565b815181106112c0576112c0615738565b60209081029190910101526112d481615764565b9050611286565b509392505050565b6001546001600160a01b0316331461130d5760405162461bcd60e51b815260040161087e90615715565b600b80548215156101000261ff00199091161790556040517f294aa6bf970997b8c55a069e787eae16138f63ad315362696337d14ef42281ea9061135690831515815260200190565b60405180910390a150565b600260005414156113845760405162461bcd60e51b815260040161087e9061577f565b60026000819055506113ca600083838080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509250613a53915050565b50506001600055565b6001546001600160a01b031633146113fd5760405162461bcd60e51b815260040161087e90615715565b828180821461141e5760405162461bcd60e51b815260040161087e906157b6565b60005b858110156114fe5761147287878381811061143e5761143e615738565b905060200201602081019061145391906151e7565b3387878581811061146657611466615738565b90506020020135613eda565b84848281811061148457611484615738565b905060200201357f4042b7789d1f6c4d5a798b447cfb918e82b64dd68b81d78ddc4dd1944ba5c51c8888848181106114be576114be615738565b90506020020160208101906114d391906151e7565b6040516001600160a01b03909116815260200160405180910390a26114f781615764565b9050611421565b50505050505050565b6002600054141561152a5760405162461bcd60e51b815260040161087e9061577f565b600260005582818082146115505760405162461bcd60e51b815260040161087e906157b6565b86600454116115715760405162461bcd60e51b815260040161087e90615966565b60005b858110156115c9576115b987878381811061159157611591615738565b90506020020135898787858181106115ab576115ab615738565b905060200201356001613faa565b6115c281615764565b9050611574565b50506001600055505050505050565b600b54610100900460ff166115ff5760405162461bcd60e51b815260040161087e906159db565b600260005414156116225760405162461bcd60e51b815260040161087e9061577f565b600260008190555061166a8383838080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525060019250613a53915050565b5050600160005550565b6001546001600160a01b0316331461169e5760405162461bcd60e51b815260040161087e90615715565b6001600160a01b038216600081815260026020908152604091829020805460ff191685151590811790915591519182527f2ee52be9d342458b3d25e07faada7ff9bc06723b4aa24edb6321ac1316b8a9dd910160405180910390a25050565b60008181526008602052604081206060919061171890613a36565b9050806001600160401b0381111561173257611732615824565b60405190808252806020026020018201604052801561175b578160200160208202803683370190505b50915060005b818110156117ae5760008481526008602052604090206117819082613a40565b83828151811061179357611793615738565b60209081029190910101526117a781615764565b9050611761565b5050919050565b6001546001600160a01b031633146117df5760405162461bcd60e51b815260040161087e90615715565b6001600160a01b0381166118055760405162461bcd60e51b815260040161087e90615715565b600180546001600160a01b0319166001600160a01b0383169081179091556040517fda7b0a7bc965abdec8a1a995575a891838264c2968e14bd456c5391827b7aa3090600090a250565b6001546001600160a01b031633146118795760405162461bcd60e51b815260040161087e90615715565b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527f7b9adf13852d5b3e153f2a442b9aa3a4570da8f9b9046c60410b17e8034fe33c90602001611356565b3360009081526002602052604090205460ff166118f65760405162461bcd60e51b815260040161087e90615715565b83828082146119175760405162461bcd60e51b815260040161087e906157b6565b4263ffffffff168963ffffffff161015801561193e57508863ffffffff168863ffffffff16115b6119835760405162461bcd60e51b8152602060048201526016602482015275616464506f6f6c3a20696e76616c69642074696d657360501b604482015260640161087e565b6000600454905060006005600083815260200190815260200160002090508b8160000160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508a8160000160146101000a81548163ffffffff021916908363ffffffff160217905550898160000160186101000a81548163ffffffff021916908363ffffffff1602179055506000816001018190555084816003018190555060005b88811015611ae3578160020160405180604001604052808c8c85818110611a4f57611a4f615738565b9050602002016020810190611a6491906151e7565b6001600160a01b031681526020018a8a85818110611a8457611a84615738565b60209081029290920135909252835460018082018655600095865294829020845160029092020180546001600160a01b0319166001600160a01b0390921691909117815592015191909201555080611adb81615764565b915050611a26565b5060048054906000611af483615764565b9091555050604080516001600160a01b038e16815263ffffffff8d811660208301528c168183015260608101879052905183917f251199ba7b3e7a12b779b3f606eaf369cecc0eac8bcbacaadcf08079e8c5940f919081900360800190a2505050505050505050505050565b60026000541415611b835760405162461bcd60e51b815260040161087e9061577f565b6002600090815533905b82811015611e2557816001600160a01b031660066000868685818110611bb557611bb5615738565b60209081029290920135835250810191909152604001600020546001600160a01b031614611c1b5760405162461bcd60e51b81526020600482015260136024820152723bb4ba34323930bb9d103737ba1037bbb732b960691b604482015260640161087e565b611c4e60086000868685818110611c3457611c34615738565b905060200201358152602001908152602001600020613a36565b15611c9b5760405162461bcd60e51b815260206004820152601860248201527f77697468647261773a206e6f7420657869746564207965740000000000000000604482015260640161087e565b60066000858584818110611cb157611cb1615738565b60209081029290920135835250810191909152604001600090812080546001600160a01b031916815560010155611d1a848483818110611cf357611cf3615738565b6001600160a01b0386166000908152600960209081526040909120939102013590506144c0565b611d2357600080fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166323b872dd3084878786818110611d6657611d66615738565b905060200201356040518463ffffffff1660e01b8152600401611d8b93929190615a12565b600060405180830381600087803b158015611da557600080fd5b505af1158015611db9573d6000803e3d6000fd5b50505050838382818110611dcf57611dcf615738565b6040516001600160a01b038616815260209182029390930135927f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436492500160405180910390a2611e1e81615764565b9050611b8d565b505060016000555050565b6001546001600160a01b03163314611e5a5760405162461bcd60e51b815260040161087e90615715565b600b5460ff1615611e9d5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642076616c756560981b604482015260640161087e565b600b805460ff191660011790556040517f1d8c7af3ae0426053ea40080543aa1cbe5bea5cdb7a55888b4cca96eea0493e490600090a1565b6001600160a01b0381166000908152600960205260409020606090611ef9906144cc565b92915050565b60026000541415611f225760405162461bcd60e51b815260040161087e9061577f565b6002600055600b54610100900460ff16611f4e5760405162461bcd60e51b815260040161087e906159db565b804263ffffffff161115611f985760405162461bcd60e51b815260206004820152601160248201527018db185a5b5199594e88195e1c1a5c9959607a1b604482015260640161087e565b6000878790509050600080856001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b158015611fdc57600080fd5b505afa158015611ff0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120149190615a36565b866001600160a01b031663d21220a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561204d57600080fd5b505afa158015612061573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120859190615a36565b9150915060005b8381101561228d5733600660008d8d858181106120ab576120ab615738565b60209081029290920135835250810191909152604001600020546001600160a01b0316146121115760405162461bcd60e51b815260206004820152601360248201527231b630b4b6a332b29d103737ba1037bbb732b960691b604482015260640161087e565b60035460009081906001600160a01b0316626349fb7f00000000000000000000000000000000000000000000000000000000000000008f8f8781811061215957612159615738565b6040516001600160e01b031960e087901b1681526001600160a01b0390941660048501526020029190910135602483015250604401604080518083038186803b1580156121a557600080fd5b505afa1580156121b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121dd9190615a53565b91509150846001600160a01b0316826001600160a01b03161480156122135750836001600160a01b0316816001600160a01b0316145b61225f5760405162461bcd60e51b815260206004820152601e60248201527f636c61696d4665653a20746f6b656e2070616972206e6f74206d617463680000604482015260640161087e565b6122838d8d8581811061227457612274615738565b905060200201358860016144d9565b505060010161208c565b5061229c82828a8a338a61464e565b505060016000555050505050505050565b600260005414156122d05760405162461bcd60e51b815260040161087e9061577f565b6002600055600b54610100900460ff166122fc5760405162461bcd60e51b815260040161087e906159db565b824263ffffffff1611156123525760405162461bcd60e51b815260206004820152601860248201527f72656d6f76654c69717569646974793a20657870697265640000000000000000604482015260640161087e565b6000878152600660205260409020546001600160a01b031633146123b85760405162461bcd60e51b815260206004820152601a60248201527f72656d6f76654c69717569646974793a206e6f74206f776e6572000000000000604482015260640161087e565b600354604051635c6d96a960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018a90526000921690635c6d96a99060440160206040518083038186803b15801561242557600080fd5b505afa158015612439573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061245d919061598f565b6001600160801b031690506000876001600160801b031611801561248a575080876001600160801b031611155b6124e15760405162461bcd60e51b815260206004820152602260248201527f72656d6f76654c69717569646974793a20696e76616c6964206c697175696469604482015261747960f01b606482015260840161087e565b6124f46001600160801b038816826159ac565b6000898152600660209081526040808320600101849055600890915281209192509061251f906144cc565b905060005b81518110156125e35760008a81526007602052604081208351829085908590811061255157612551615738565b60200260200101518152602001908152602001600020600501549050600084821161257d576000612587565b61258785836159ac565b905080156125d9576125d98c8585815181106125a5576125a5615738565b602002602001015183896001600281106125c1576125c1615738565b6020020160208101906125d49190615429565b613faa565b5050600101612524565b50600354604051626349fb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018c90526000928392911690626349fb90604401604080518083038186803b15801561265157600080fd5b505afa158015612665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126899190615a53565b915091506126988b8b89614918565b6126a56020860186615429565b156126b6576126b68b8860006144d9565b6126c482828b8b338b61464e565b50506001600055505050505050505050565b600260005414156126f95760405162461bcd60e51b815260040161087e9061577f565b60026000908155600b54339160ff909116905b83811015612b0457826001600160a01b03166006600087878581811061273457612734615738565b60209081029290920135835250810191909152604001600020546001600160a01b03161461279a5760405162461bcd60e51b81526020600482015260136024820152723bb4ba34323930bb9d103737ba1037bbb732b960691b604482015260640161087e565b6001600a60008787858181106127b2576127b2615738565b90506020020135815260200190815260200160002060006101000a81548160ff0219169083151502179055506000612813600860008888868181106127f9576127f9615738565b9050602002013581526020019081526020016000206144cc565b905060005b81518110156128f057600082828151811061283557612835615738565b60209081029190910181015160008181526005909252604082206004018054600019019055915060079089898781811061287157612871615738565b602090810292909201358352508181019290925260409081016000908120848252909252812080546001600160801b0319168155906128b36001830182615077565b6128c1600283016000615077565b6128cf600383016000615077565b50600060048201819055600590910155506128e981615764565b9050612818565b506006600087878581811061290757612907615738565b60209081029290920135835250810191909152604001600090812080546001600160a01b03191681556001015582612a015761297586868481811061294e5761294e615738565b6001600160a01b0388166000908152600960209081526040909120939102013590506144c0565b61297e57600080fd5b60005b81518110156129ff57600082828151811061299e5761299e615738565b602002602001015190506129e581600860008b8b898181106129c2576129c2615738565b9050602002013581526020019081526020016000206144c090919063ffffffff16565b6129ee57600080fd5b506129f881615764565b9050612981565b505b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166323b872dd3086898987818110612a4457612a44615738565b905060200201356040518463ffffffff1660e01b8152600401612a6993929190615a12565b600060405180830381600087803b158015612a8357600080fd5b505af1158015612a97573d6000803e3d6000fd5b50505050858583818110612aad57612aad615738565b6040516001600160a01b038816815260209182029390930135927f5fafa99d0643513820be26656b45130b01e1c03062e1266bf36f88cbd3bd969592500160405180910390a250612afd81615764565b905061270c565b50506001600055505050565b600081815260056020818152604080842086855260078352818520868652909252832091820154606092839291612b895760405162461bcd60e51b815260206004820152601b60248201527f67657455736572496e666f3a206e6f74206a6f696e6564207965740000000000604482015260640161087e565b60028201546001600160401b03811115612ba557612ba5615824565b604051908082528060200260200182016040528015612bce578160200160208202803683370190505b5060028301549094506001600160401b03811115612bee57612bee615824565b604051908082528060200260200182016040528015612c17578160200160208202803683370190505b5092506000612c268888612d81565b905060005b6002840154811015612d6f576000612c778460050154866002018481548110612c5657612c56615738565b906000526020600020906002020160010154856060015186608001516149b9565b90506000612cd382866003018581548110612c9457612c94615738565b9060005260206000200154612ca991906159c3565b8560400151876001018681548110612cc357612cc3615738565b90600052602060002001546149e4565b905080856002018481548110612ceb57612ceb615738565b9060005260206000200154612d0091906159c3565b888481518110612d1257612d12615738565b602002602001018181525050846001018381548110612d3357612d33615738565b9060005260206000200154878481518110612d5057612d50615738565b602002602001018181525050505080612d6890615764565b9050612c2b565b50816005015495505050509250925092565b612dc36040518060c0016040528060006001600160801b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000828152600560209081526040808320868452600783528184208685529092529182902060035482549351632106297960e11b8152929391926001600160a01b039182169263420c52f292612e42929116907f0000000000000000000000000000000000000000000000000000000000000000908a90600401615a12565b60206040518083038186803b158015612e5a57600080fd5b505afa158015612e6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e92919061598f565b6001600160801b0316835260035482546040516339bb866b60e11b81526001600160a01b03928316926373770cd692612ef4929116907f0000000000000000000000000000000000000000000000000000000000000000908a90600401615a12565b60206040518083038186803b158015612f0c57600080fd5b505afa158015612f20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f449190615a82565b6020840181905260048201546003840154612f60929190614a1b565b604084015281546001830154612f8e9163ffffffff600160a01b8204811692600160c01b9092041690614a64565b6060840152805483516001600160801b03918216900316608084018190526005820154612fbb9190615a9b565b60a084015250909392505050565b8060045411612fea5760405162461bcd60e51b815260040161087e90615966565b60008181526005602081815260408084208685526006835281852054600784528286208787529093529320918201546001600160a01b0390911691906130725760405162461bcd60e51b815260206004820152601760248201527f686172766573743a206e6f74206a6f696e656420796574000000000000000000604482015260640161087e565b600061307e8686612d81565b90508060a0015184600101600082825461309891906159c3565b9091555050805182546001600160801b0319166001600160801b0390911617825560005b60028501548110156114fe5760006130e78460050154876002018481548110612c5657612c56615738565b90508015613161578084600301838154811061310557613105615738565b90600052602060002001600082825461311e91906159c3565b925050819055508086600201838154811061313b5761313b615738565b9060005260206000209060020201600101600082825461315b91906159ac565b90915550505b60006131a085600301848154811061317b5761317b615738565b90600052602060002001548560400151876001018681548110612cc357612cc3615738565b90506000818660020185815481106131ba576131ba615738565b90600052602060002001546131cf91906159c3565b905080156132ef57811561321257818660010185815481106131f3576131f3615738565b90600052602060002001600082825461320c91906159c3565b90915550505b600086600201858154811061322957613229615738565b906000526020600020018190555061327088600201858154811061324f5761324f615738565b60009182526020909120600290910201546001600160a01b03168883613eda565b808a7f1ec92956b13869de207e232e60c65e35826c5e12b7fdc6d70b083b5a4fd26703898b60020188815481106132a9576132a9615738565b60009182526020909120600290910201546040516132e692916001600160a01b0316906001600160a01b0392831681529116602082015260400190565b60405180910390a35b505050806132fc90615764565b90506130bc565b600084815260066020908152604080832060078352818420878552909252918290206003548454935163d7aee3bd60e01b8152929391926001600160a01b039182169263d7aee3bd9261337f929116907f0000000000000000000000000000000000000000000000000000000000000000908b90600401615a12565b60206040518083038186803b15801561339757600080fd5b505afa1580156133ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133cf9190615aba565b6134105760405162461bcd60e51b81526020600482015260126024820152711a9bda5b8e881a5b9d985b1a59081c1bdbdb60721b604482015260640161087e565b8315801590613423575081600101548411155b6134635760405162461bcd60e51b81526020600482015260116024820152706a6f696e3a20696e76616c6964206c697160781b604482015260640161087e565b6003548354604051632106297960e11b81526001600160a01b039283169263420c52f2926134ba929116907f0000000000000000000000000000000000000000000000000000000000000000908b90600401615a12565b60206040518083038186803b1580156134d257600080fd5b505afa1580156134e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061350a919061598f565b81546001600160801b0319166001600160801b039190911617815560028301546001600160401b0381111561354157613541615824565b60405190808252806020026020018201604052801561356a578160200160208202803683370190505b508051613581916001840191602090910190615098565b5060028301546001600160401b0381111561359e5761359e615824565b6040519080825280602002602001820160405280156135c7578160200160208202803683370190505b5080516135de916002840191602090910190615098565b5060028301546001600160401b038111156135fb576135fb615824565b604051908082528060200260200182016040528015613624578160200160208202803683370190505b50805161363b916003840191602090910190615098565b506003830154156136f05760035460405163528ee47960e11b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018990529091169063a51dc8f29060440160206040518083038186803b1580156136b257600080fd5b505afa1580156136c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136ea9190615a82565b60048201555b6005810184905560048301805490600061370983615764565b909155505060008681526008602052604090206137269086614ab8565b6137725760405162461bcd60e51b815260206004820152601760248201527f4661696c20746f20616464206a6f696e6564506f6f6c73000000000000000000604482015260640161087e565b8385877f5d48292d83151281e6ea967de9fd35716c713dc76c11beb42d78ed5110270f7360405160405180910390a4505050505050565b60008481526006602090815260408083206007835281842087855290925290912083158015906137eb5750600182015460058201546137e890866159c3565b11155b61382b5760405162461bcd60e51b815260206004820152601160248201527073796e633a20696e76616c6964206c697160781b604482015260640161087e565b60006138378787612d81565b905060005b60028501548110156139815760006138678460050154876002018481548110612c5657612c56615738565b905080156138e1578084600301838154811061388557613885615738565b90600052602060002001600082825461389e91906159c3565b92505081905550808660020183815481106138bb576138bb615738565b906000526020600020906002020160010160008282546138db91906159ac565b90915550505b60006138fb85600301848154811061317b5761317b615738565b9050801561396e578085600101848154811061391957613919615738565b90600052602060002001600082825461393291906159c3565b925050819055508085600201848154811061394f5761394f615738565b90600052602060002001600082825461396891906159c3565b90915550505b50508061397a90615764565b905061383c565b508060a0015184600101600082825461399a91906159c3565b9091555050805182546001600160801b0319166001600160801b0390911617825560048201546020820151600386015460058501546139dd93929190898c614ac4565b8260040181905550848260050160008282546139f991906159c3565b90915550506040518590879089907ff31cd5db84a4ab16cd339c510d5a60c63ef5818c538aa67046f5bc0dca794cd390600090a450505050505050565b6000611ef9825490565b6000613a4c8383614be6565b9392505050565b600b5460ff1615613a9f5760405162461bcd60e51b8152602060048201526016602482015275139bdd08185b1b1bddd959081d1bc819195c1bdcda5d60521b604482015260640161087e565b8015613b51578260045411613ac65760405162461bcd60e51b815260040161087e90615966565b600083815260056020526040902054429063ffffffff808316600160a01b9092041611801590613b14575060008481526005602052604090205463ffffffff600160c01b9091048116908216105b613b4f5760405162461bcd60e51b815260206004820152600c60248201526b496e76616c69642074696d6560a01b604482015260640161087e565b505b3360005b8351811015613ed357600a6000858381518110613b7457613b74615738565b60209081029190910181015182528101919091526040016000205460ff1615613bd85760405162461bcd60e51b8152602060048201526016602482015275139bdd08185b1b1bddd959081d1bc819195c1bdcda5d60521b604482015260640161087e565b613c28848281518110613bed57613bed615738565b602002602001015160096000856001600160a01b03166001600160a01b03168152602001908152602001600020614ab890919063ffffffff16565b613c3157600080fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166323b872dd8330878581518110613c7457613c74615738565b60200260200101516040518463ffffffff1660e01b8152600401613c9a93929190615a12565b600060405180830381600087803b158015613cb457600080fd5b505af1158015613cc8573d6000803e3d6000fd5b50505050838181518110613cde57613cde615738565b60200260200101517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c83604051613d2491906001600160a01b0391909116815260200190565b60405180910390a28160066000868481518110613d4357613d43615738565b6020908102919091018101518252810191909152604001600090812080546001600160a01b0319166001600160a01b03938416179055600354865191921690635c6d96a9907f000000000000000000000000000000000000000000000000000000000000000090889086908110613dbc57613dbc615738565b60200260200101516040518363ffffffff1660e01b8152600401613df59291906001600160a01b03929092168252602082015260400190565b60206040518083038186803b158015613e0d57600080fd5b505afa158015613e21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e45919061598f565b9050806001600160801b031660066000878581518110613e6757613e67615738565b60200260200101518152602001908152602001600020600101819055508315613eca57613eca858381518110613e9f57613e9f615738565b602002602001015187836001600160801b0316600560008b8152602001908152602001600020613303565b50600101613b55565b5050505050565b6001600160a01b038316613f91576000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613f35576040519150601f19603f3d011682016040523d82523d6000602084013e613f3a565b606091505b5050905080613f8b5760405162461bcd60e51b815260206004820152601c60248201527f7472616e736665722072657761726420746f6b656e206661696c656400000000604482015260640161087e565b50505050565b613fa56001600160a01b0384168383614c10565b505050565b600083815260056020908152604080832087845260068352818420546007845282852088865290935292206001600160a01b03909116903382148061401c57508254600160c01b900463ffffffff164263ffffffff1611801561401c57503360009081526002602052604090205460ff165b6140725760405162461bcd60e51b815260206004820152602160248201527f657869743a206e6f74206f776e6572206f7220706f6f6c206e6f7420656e64656044820152601960fa1b606482015260840161087e565b600581015485158015906140865750808611155b6140c65760405162461bcd60e51b8152602060048201526011602482015270657869743a20696e76616c6964206c697160781b604482015260640161087e565b60006140d287836159ac565b905060006140e08a8a612d81565b90508060a001518660010160008282546140fa91906159c3565b9091555050805184546001600160801b0319166001600160801b039091161784556005840182905560005b600287015481101561439957600061414c85896002018481548110612c5657612c56615738565b905080156141c6578086600301838154811061416a5761416a615738565b90600052602060002001600082825461418391906159c3565b92505081905550808860020183815481106141a0576141a0615738565b906000526020600020906002020160010160008282546141c091906159ac565b90915550505b60006142058760030184815481106141e0576141e0615738565b90600052602060002001548560400151896001018681548110612cc357612cc3615738565b9050600087600201848154811061421e5761421e615738565b9060005260206000200154905081600014614275578188600101858154811061424957614249615738565b90600052602060002001600082825461426291906159c3565b90915550614272905082826159c3565b90505b8015614385578a1561436257600088600201858154811061429857614298615738565b90600052602060002001819055506142df8a60020185815481106142be576142be615738565b60009182526020909120600290910201546001600160a01b03168a83613eda565b808e7f1ec92956b13869de207e232e60c65e35826c5e12b7fdc6d70b083b5a4fd267038b8d600201888154811061431857614318615738565b600091825260209091206002909102015460405161435592916001600160a01b0316906001600160a01b0392831681529116602082015260400190565b60405180910390a3614385565b8088600201858154811061437857614378615738565b6000918252602090912001555b5050508061439290615764565b9050614125565b508161447b5760008a81526007602090815260408083208c8452909152812080546001600160801b0319168155906143d46001830182615077565b6143e2600283016000615077565b6143f0600383016000615077565b5060006004828101829055600590920181905590870180549161441283615ad7565b909155505060008a815260086020526040902061442f908a6144c0565b61447b5760405162461bcd60e51b815260206004820152601a60248201527f4661696c20746f2072656d6f7665206a6f696e6564506f6f6c73000000000000604482015260640161087e565b60405133815288908a908c907f275029c7b988945c03ac5499c0d532fce79ce36efab42e1b3f180a62001cad2c9060200160405180910390a450505050505050505050565b6000613a4c8383614c62565b60606000613a4c83614d55565b801561457b57604051630c479e6560e21b8152600481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063311e799490602401602060405180830381600087803b15801561454157600080fd5b505af1158015614555573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145799190615a82565b505b60408051608081018252848152600060208201818152828401918252606083018681529351637686c6e960e11b815283516004820152905160248201529051604482015291516064830152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ed0d8dd2906084015b606060405180830381600087803b15801561461657600080fd5b505af115801561462a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114fe9190615aee565b7f0000000000000000000000000000000000000000000000000000000000000000811561484a57866001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614156147795760405163bac37ef760e01b8152600481018690526001600160a01b03848116602483015282169063bac37ef790604401600060405180830381600087803b1580156146f957600080fd5b505af115801561470d573d6000803e3d6000fd5b505060405163bf1316c160e01b81526001600160a01b038416925063bf1316c1915061474190899088908890600401615b1c565b600060405180830381600087803b15801561475b57600080fd5b505af115801561476f573d6000803e3d6000fd5b5050505050614910565b856001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316141561484a5760405163bf1316c160e01b81526001600160a01b0382169063bf1316c1906147e3908a9089908890600401615b1c565b600060405180830381600087803b1580156147fd57600080fd5b505af1158015614811573d6000803e3d6000fd5b505060405163bac37ef760e01b8152600481018790526001600160a01b0386811660248301528416925063bac37ef79150604401614741565b60405163bf1316c160e01b81526001600160a01b0382169063bf1316c19061487a908a9089908890600401615b1c565b600060405180830381600087803b15801561489457600080fd5b505af11580156148a8573d6000803e3d6000fd5b505060405163bf1316c160e01b81526001600160a01b038416925063bf1316c191506148dc90899088908890600401615b1c565b600060405180830381600087803b1580156148f657600080fd5b505af115801561490a573d6000803e3d6000fd5b50505050505b505050505050565b6040805160a0810182528481526001600160801b03848116602083019081526000838501818152606085019182526080850187815295516398e04d7760e01b81528551600482015292519093166024830152915160448201529051606482015291516084830152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906398e04d779060a4016145fc565b600082826149c78688615a9b565b6149d19190615a9b565b6149db9190615b55565b95945050505050565b60008064e8d4a510006149f78587615a9b565b614a019190615b55565b9050828111614a115760006149db565b6149db83826159ac565b600081614a2e575064e8d4a51000613a4c565b6000614a3a8486615b69565b90506149db83614a4f64e8d4a5100084615a9b565b614a599190615b55565b64e8d4a51000614db1565b60008084614a784263ffffffff1686614dc7565b614a8291906159ac565b90506000614a9482600160601b615a9b565b9050838111614aa4576000614aae565b614aae84826159ac565b9695505050505050565b6000613a4c8383614dde565b600084614ad357506000614aae565b600087614ae08789615b69565b12614af457614aef8688615b69565b614af6565b875b60035460405163528ee47960e11b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018790529293506000929091169063a51dc8f29060440160206040518083038186803b158015614b6857600080fd5b505afa158015614b7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614ba09190615a82565b614baa9086615ba8565b614bb48388615ba8565b614bbe9190615c2d565b90506000614bcc86886159c3565b9050614bd88183615c6e565b9a9950505050505050505050565b6000826000018281548110614bfd57614bfd615738565b9060005260206000200154905092915050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613fa5908490614e2d565b60008181526001830160205260408120548015614d4b576000614c866001836159ac565b8554909150600090614c9a906001906159ac565b9050818114614cff576000866000018281548110614cba57614cba615738565b9060005260206000200154905080876000018481548110614cdd57614cdd615738565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614d1057614d10615c9c565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050611ef9565b6000915050611ef9565b606081600001805480602002602001604051908101604052809291908181526020018280548015614da557602002820191906000526020600020905b815481526020019060010190808311614d91575b50505050509050919050565b600081831015614dc15782613a4c565b50919050565b600081831015614dd75781613a4c565b5090919050565b6000818152600183016020526040812054614e2557508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611ef9565b506000611ef9565b6000614e82826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614eff9092919063ffffffff16565b805190915015613fa55780806020019051810190614ea09190615aba565b613fa55760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161087e565b6060614f0e8484600085614f16565b949350505050565b606082471015614f775760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161087e565b843b614fc55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161087e565b600080866001600160a01b03168587604051614fe19190615cde565b60006040518083038185875af1925050503d806000811461501e576040519150601f19603f3d011682016040523d82523d6000602084013e615023565b606091505b509150915061503382828661503e565b979650505050505050565b6060831561504d575081613a4c565b82511561505d5782518084602001fd5b8160405162461bcd60e51b815260040161087e9190615cfa565b508054600082559060005260206000209081019061509591906150e3565b50565b8280548282559060005260206000209081019282156150d3579160200282015b828111156150d35782518255916020019190600101906150b8565b506150df9291506150e3565b5090565b5b808211156150df57600081556001016150e4565b803563ffffffff8116811461510c57600080fd5b919050565b60008083601f84011261512357600080fd5b5081356001600160401b0381111561513a57600080fd5b6020830191508360208260051b850101111561515557600080fd5b9250929050565b60008060008060008060a0878903121561517557600080fd5b86359550615185602088016150f8565b9450615193604088016150f8565b935060608701356001600160401b038111156151ae57600080fd5b6151ba89828a01615111565b979a9699509497949695608090950135949350505050565b6001600160a01b038116811461509557600080fd5b6000602082840312156151f957600080fd5b8135613a4c816151d2565b6000806000806040858703121561521a57600080fd5b84356001600160401b038082111561523157600080fd5b61523d88838901615111565b9096509450602087013591508082111561525657600080fd5b5061526387828801615111565b95989497509550505050565b60006020828403121561528157600080fd5b5035919050565b600081518084526020808501945080840160005b838110156152b85781518752958201959082019060010161529c565b509495945050505050565b6001600160a01b03898116825263ffffffff8981166020808501919091529089166040840152606083018890526080830187905260a0830186905261010060c08401819052855190840181905260009261012085019287810192855b8181101561533d57845184168652948201949382019360010161531f565b505050505082810360e08401526153548185615288565b9b9a5050505050505050505050565b60008060008060006060868803121561537b57600080fd5b8535945060208601356001600160401b038082111561539957600080fd5b6153a589838a01615111565b909650945060408801359150808211156153be57600080fd5b506153cb88828901615111565b969995985093965092949392505050565b6000806000606084860312156153f157600080fd5b505081359360208301359350604090920135919050565b602081526000613a4c6020830184615288565b801515811461509557600080fd5b60006020828403121561543b57600080fd5b8135613a4c8161541b565b6000806020838503121561545957600080fd5b82356001600160401b0381111561546f57600080fd5b61547b85828601615111565b90969095509350505050565b60008060006040848603121561549c57600080fd5b8335925060208401356001600160401b038111156154b957600080fd5b6154c586828701615111565b9497909650939450505050565b600080604083850312156154e557600080fd5b82356154f0816151d2565b915060208301356155008161541b565b809150509250929050565b60008060008060008060008060c0898b03121561552757600080fd5b8835615532816151d2565b975061554060208a016150f8565b965061554e60408a016150f8565b955060608901356001600160401b038082111561556a57600080fd5b6155768c838d01615111565b909750955060808b013591508082111561558f57600080fd5b5061559c8b828c01615111565b999c989b50969995989497949560a00135949350505050565b600080604083850312156155c857600080fd5b50508035926020909101359150565b600080600080600080600060c0888a0312156155f257600080fd5b87356001600160401b0381111561560857600080fd5b6156148a828b01615111565b90985096505060208801359450604088013593506060880135615636816151d2565b925060808801356156468161541b565b8092505060a0880135905092959891949750929550565b6001600160801b038116811461509557600080fd5b600080600080600080600061010080898b03121561568f57600080fd5b8835975060208901356156a18161565d565b965060408901359550606089013594506080890135935060a08901356156c68161541b565b92508881018a10156156d757600080fd5b5060c08801905092959891949750929550565b8381526060602082015260006157036060830185615288565b8281036040840152614aae8185615288565b6020808252600990820152683337b93134b23232b760b91b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006000198214156157785761577861574e565b5060010190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020808252600e908201526d0d2dcecc2d8d2c840d8cadccee8d60931b604082015260600190565b6000808335601e198436030181126157f557600080fd5b8301803591506001600160401b0382111561580f57600080fd5b60200191503681900382131561515557600080fd5b634e487b7160e01b600052604160045260246000fd5b604051602081016001600160401b038111828210171561585c5761585c615824565b60405290565b604051601f8201601f191681016001600160401b038111828210171561588a5761588a615824565b604052919050565b600060208083850312156158a557600080fd5b82356001600160401b03808211156158bc57600080fd5b81850191508282870312156158d057600080fd5b6158d861583a565b8235828111156158e757600080fd5b80840193505086601f8401126158fc57600080fd5b82358281111561590e5761590e615824565b8060051b925061591f858401615862565b818152928401850192858101908985111561593957600080fd5b948601945b848610156159575785358252948601949086019061593e565b83525090979650505050505050565b6020808252600f908201526e506f6f6c206e6f742065786973747360881b604082015260600190565b6000602082840312156159a157600080fd5b8151613a4c8161565d565b6000828210156159be576159be61574e565b500390565b600082198211156159d6576159d661574e565b500190565b60208082526018908201527f7370656369616c20666561747572652064697361626c65640000000000000000604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600060208284031215615a4857600080fd5b8151613a4c816151d2565b60008060408385031215615a6657600080fd5b8251615a71816151d2565b6020840151909250615500816151d2565b600060208284031215615a9457600080fd5b5051919050565b6000816000190483118215151615615ab557615ab561574e565b500290565b600060208284031215615acc57600080fd5b8151613a4c8161541b565b600081615ae657615ae661574e565b506000190190565b600080600060608486031215615b0357600080fd5b8351925060208401519150604084015190509250925092565b6001600160a01b0393841681526020810192909252909116604082015260600190565b634e487b7160e01b600052601260045260246000fd5b600082615b6457615b64615b3f565b500490565b60008083128015600160ff1b850184121615615b8757615b8761574e565b6001600160ff1b0384018313811615615ba257615ba261574e565b50500390565b60006001600160ff1b0381841382841380821686840486111615615bce57615bce61574e565b600160ff1b6000871282811687830589121615615bed57615bed61574e565b60008712925087820587128484161615615c0957615c0961574e565b87850587128184161615615c1f57615c1f61574e565b505050929093029392505050565b600080821280156001600160ff1b0384900385131615615c4f57615c4f61574e565b600160ff1b8390038412811615615c6857615c6861574e565b50500190565b600082615c7d57615c7d615b3f565b600160ff1b821460001984141615615c9757615c9761574e565b500590565b634e487b7160e01b600052603160045260246000fd5b60005b83811015615ccd578181015183820152602001615cb5565b83811115613f8b5750506000910152565b60008251615cf0818460208701615cb2565b9190910192915050565b6020815260008251806020840152615d19816040850160208701615cb2565b601f01601f1916919091016040019291505056fea164736f6c6343000809000a000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a48000000000000000000000000035be3f4fd8239a35a7f120756d4d69e5c5e10870

Deployed Bytecode

0x6080604052600436106101fd5760003560e01c806375829def1161010d578063b36b26c5116100a0578063c7ca58701161006f578063c7ca58701461074b578063d1941b061461076b578063dc168a921461078b578063e7762a0c146107ba578063f851a4401461082f57600080fd5b8063b36b26c5146106bc578063b63ff916146106dc578063b82562751461070c578063bb5ae7751461072c57600080fd5b806399fbab88116100dc57806399fbab881461052f578063ac4afa381461058e578063affe737914610631578063b221e5fd1461064657600080fd5b806375829def146104af5780638bcc4166146104cf57806391fb2f15146104ef578063983d95ce1461050f57600080fd5b806347ccca021161019057806361657b121161015f57806361657b121461040f57806366b5a6641461042f578063683f3eda1461044f5780636d44a3b21461046f578063705b5bef1461048f57600080fd5b806347ccca021461036e57806349b35168146103a257806350523cea146103cf578063598b8e71146103ef57600080fd5b80632f380b35116101cc5780632f380b35146102b45780633fc8cef3146102e857806341e817ab14610334578063430ccd9b1461035457600080fd5b8063078ded5d14610209578063081e3eda1461022b57806313e7c9d814610254578063180f80361461029457600080fd5b3661020457005b600080fd5b34801561021557600080fd5b5061022961022436600461515c565b61084f565b005b34801561023757600080fd5b5061024160045481565b6040519081526020015b60405180910390f35b34801561026057600080fd5b5061028461026f3660046151e7565b60026020526000908152604090205460ff1681565b604051901515815260200161024b565b3480156102a057600080fd5b506102296102af366004615204565b610b0b565b3480156102c057600080fd5b506102d46102cf36600461526f565b610c8d565b60405161024b9897969594939291906152c3565b3480156102f457600080fd5b5061031c7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b03909116815260200161024b565b34801561034057600080fd5b5061022961034f366004615363565b610e37565b34801561036057600080fd5b50600b546102849060ff1681565b34801561037a57600080fd5b5061031c7f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a48081565b3480156103ae57600080fd5b506103c26103bd3660046153dc565b61118b565b60405161024b9190615408565b3480156103db57600080fd5b506102296103ea366004615429565b6112e3565b3480156103fb57600080fd5b5061022961040a366004615446565b611361565b34801561041b57600080fd5b5061022961042a366004615204565b6113d3565b34801561043b57600080fd5b5061022961044a366004615363565b611507565b34801561045b57600080fd5b5061022961046a366004615487565b6115d8565b34801561047b57600080fd5b5061022961048a3660046154d2565b611674565b34801561049b57600080fd5b506103c26104aa36600461526f565b6116fd565b3480156104bb57600080fd5b506102296104ca3660046151e7565b6117b5565b3480156104db57600080fd5b506102296104ea3660046151e7565b61184f565b3480156104fb57600080fd5b5061022961050a36600461550b565b6118c7565b34801561051b57600080fd5b5061022961052a366004615446565b611b60565b34801561053b57600080fd5b5061056f61054a36600461526f565b600660205260009081526040902080546001909101546001600160a01b039091169082565b604080516001600160a01b03909316835260208301919091520161024b565b34801561059a57600080fd5b506105f06105a936600461526f565b60056020526000908152604090208054600182015460038301546004909301546001600160a01b0383169363ffffffff600160a01b8504811694600160c01b900416929186565b604080516001600160a01b0397909716875263ffffffff958616602088015293909416928501929092526060840152608083015260a082015260c00161024b565b34801561063d57600080fd5b50610229611e30565b34801561065257600080fd5b506106976106613660046155b5565b60076020908152600092835260408084209091529082529020805460048201546005909201546001600160801b03909116919083565b604080516001600160801b03909416845260208401929092529082015260600161024b565b3480156106c857600080fd5b506103c26106d73660046151e7565b611ed5565b3480156106e857600080fd5b506102846106f736600461526f565b600a6020526000908152604090205460ff1681565b34801561071857600080fd5b506102296107273660046155d7565b611eff565b34801561073857600080fd5b50600b5461028490610100900460ff1681565b34801561075757600080fd5b50610229610766366004615672565b6122ad565b34801561077757600080fd5b50610229610786366004615446565b6126d6565b34801561079757600080fd5b506107ab6107a63660046155b5565b612b10565b60405161024b939291906156ea565b3480156107c657600080fd5b506107da6107d53660046155b5565b612d81565b60405161024b9190600060c0820190506001600160801b0383511682526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015292915050565b34801561083b57600080fd5b5060015461031c906001600160a01b031681565b3360009081526002602052604090205460ff166108875760405162461bcd60e51b815260040161087e90615715565b60405180910390fd5b6000868152600560205260409020805463ffffffff428116600160a01b9092041611806108c45750805463ffffffff428116600160c01b90920416105b6109105760405162461bcd60e51b815260206004820152601960248201527f72656e65773a20696e76616c696420706f6f6c20737461746500000000000000604482015260640161087e565b6002810154831461095b5760405162461bcd60e51b81526020600482015260156024820152740e4cadccaee7440d2dcecc2d8d2c840d8cadccee8d605b1b604482015260640161087e565b4263ffffffff168663ffffffff1611801561098157508563ffffffff168563ffffffff16115b6109c45760405162461bcd60e51b815260206004820152601460248201527372656e65773a20696e76616c69642074696d657360601b604482015260640161087e565b600481015415610a0f5760405162461bcd60e51b815260206004820152601660248201527572656e65773a20706f6f6c20686173207374616b657360501b604482015260640161087e565b805463ffffffff868116600160c01b0263ffffffff60c01b19918916600160a01b029190911667ffffffffffffffff60a01b1990921691909117178155600060018201819055600382018390555b83811015610ab757848482818110610a7757610a77615738565b90506020020135826002018281548110610a9357610a93615738565b6000918252602090912060016002909202010155610ab081615764565b9050610a5d565b506040805163ffffffff80891682528716602082015290810183905287907feda7bcc7e20158ff9fa87a4200758e927bbb3c8823e648403bdd6053b96dd0ad9060600160405180910390a250505050505050565b60026000541415610b2e5760405162461bcd60e51b815260040161087e9061577f565b60026000558281808214610b545760405162461bcd60e51b815260040161087e906157b6565b60005b85811015610c7f573360066000898985818110610b7657610b76615738565b60209081029290920135835250810191909152604001600020546001600160a01b031614610bdb5760405162461bcd60e51b81526020600482015260126024820152713430b93b32b9ba1d103737ba1037bbb732b960711b604482015260640161087e565b6000858583818110610bef57610bef615738565b9050602002810190610c0191906157de565b810190610c0e9190615892565b905060005b815151811015610c6c57610c5c898985818110610c3257610c32615738565b9050602002013583600001518381518110610c4f57610c4f615738565b6020026020010151612fc9565b610c6581615764565b9050610c13565b505080610c7890615764565b9050610b57565b505060016000555050505050565b6000818152600560205260409020805460018201546003830154600484015460028501546001600160a01b0385169563ffffffff600160a01b8704811696600160c01b900416949392916060918291806001600160401b03811115610cf457610cf4615824565b604051908082528060200260200182016040528015610d1d578160200160208202803683370190505b509350806001600160401b03811115610d3857610d38615824565b604051908082528060200260200182016040528015610d61578160200160208202803683370190505b50925060005b81811015610e2957826002018181548110610d8457610d84615738565b600091825260209091206002909102015485516001600160a01b0390911690869083908110610db557610db5615738565b60200260200101906001600160a01b031690816001600160a01b031681525050826002018181548110610dea57610dea615738565b906000526020600020906002020160010154848281518110610e0e57610e0e615738565b6020908102919091010152610e2281615764565b9050610d67565b505050919395975091939597565b60026000541415610e5a5760405162461bcd60e51b815260040161087e9061577f565b60026000558281808214610e805760405162461bcd60e51b815260040161087e906157b6565b8660045411610ea15760405162461bcd60e51b815260040161087e90615966565b6000878152600560205260409020805463ffffffff428116600160a01b9092041611801590610ee357508054600160c01b900463ffffffff164263ffffffff16105b610f1e5760405162461bcd60e51b815260206004820152600c60248201526b496e76616c69642074696d6560a01b604482015260640161087e565b60005b8681101561117b5733600660008a8a85818110610f4057610f40615738565b60209081029290920135835250810191909152604001600020546001600160a01b031614610f9c5760405162461bcd60e51b81526020600482015260096024820152682737ba1037bbb732b960b91b604482015260640161087e565b6003546001600160a01b0316635c6d96a97f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a4808a8a85818110610fe057610fe0615738565b6040516001600160e01b031960e087901b1681526001600160a01b039094166004850152602002919091013560248301525060440160206040518083038186803b15801561102d57600080fd5b505afa158015611041573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611065919061598f565b6001600160801b0316600660008a8a8581811061108457611084615738565b905060200201358152602001908152602001600020600101819055506000600760008a8a858181106110b8576110b8615738565b90506020020135815260200190815260200160002060008b8152602001908152602001600020905080600501546000141561112e5761112989898481811061110257611102615738565b905060200201358b89898681811061111c5761111c615738565b9050602002013586613303565b61116a565b61116a89898481811061114357611143615738565b905060200201358b89898681811061115d5761115d615738565b90506020020135866137a9565b5061117481615764565b9050610f21565b5050600160005550505050505050565b6060818311156111d35760405162461bcd60e51b81526020600482015260136024820152720cce4deda92dcc8caf0407c40e8de92dcc8caf606b1b604482015260640161087e565b60008481526008602052604090206111ea90613a36565b821061122c5760405162461bcd60e51b81526020600482015260116024820152700e8de92dcc8caf0407c7a40d8cadccee8d607b1b604482015260640161087e565b61123683836159ac565b6112419060016159c3565b6001600160401b0381111561125857611258615824565b604051908082528060200260200182016040528015611281578160200160208202803683370190505b509050825b8281116112db5760008581526008602052604090206112a59082613a40565b826112b086846159ac565b815181106112c0576112c0615738565b60209081029190910101526112d481615764565b9050611286565b509392505050565b6001546001600160a01b0316331461130d5760405162461bcd60e51b815260040161087e90615715565b600b80548215156101000261ff00199091161790556040517f294aa6bf970997b8c55a069e787eae16138f63ad315362696337d14ef42281ea9061135690831515815260200190565b60405180910390a150565b600260005414156113845760405162461bcd60e51b815260040161087e9061577f565b60026000819055506113ca600083838080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509250613a53915050565b50506001600055565b6001546001600160a01b031633146113fd5760405162461bcd60e51b815260040161087e90615715565b828180821461141e5760405162461bcd60e51b815260040161087e906157b6565b60005b858110156114fe5761147287878381811061143e5761143e615738565b905060200201602081019061145391906151e7565b3387878581811061146657611466615738565b90506020020135613eda565b84848281811061148457611484615738565b905060200201357f4042b7789d1f6c4d5a798b447cfb918e82b64dd68b81d78ddc4dd1944ba5c51c8888848181106114be576114be615738565b90506020020160208101906114d391906151e7565b6040516001600160a01b03909116815260200160405180910390a26114f781615764565b9050611421565b50505050505050565b6002600054141561152a5760405162461bcd60e51b815260040161087e9061577f565b600260005582818082146115505760405162461bcd60e51b815260040161087e906157b6565b86600454116115715760405162461bcd60e51b815260040161087e90615966565b60005b858110156115c9576115b987878381811061159157611591615738565b90506020020135898787858181106115ab576115ab615738565b905060200201356001613faa565b6115c281615764565b9050611574565b50506001600055505050505050565b600b54610100900460ff166115ff5760405162461bcd60e51b815260040161087e906159db565b600260005414156116225760405162461bcd60e51b815260040161087e9061577f565b600260008190555061166a8383838080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525060019250613a53915050565b5050600160005550565b6001546001600160a01b0316331461169e5760405162461bcd60e51b815260040161087e90615715565b6001600160a01b038216600081815260026020908152604091829020805460ff191685151590811790915591519182527f2ee52be9d342458b3d25e07faada7ff9bc06723b4aa24edb6321ac1316b8a9dd910160405180910390a25050565b60008181526008602052604081206060919061171890613a36565b9050806001600160401b0381111561173257611732615824565b60405190808252806020026020018201604052801561175b578160200160208202803683370190505b50915060005b818110156117ae5760008481526008602052604090206117819082613a40565b83828151811061179357611793615738565b60209081029190910101526117a781615764565b9050611761565b5050919050565b6001546001600160a01b031633146117df5760405162461bcd60e51b815260040161087e90615715565b6001600160a01b0381166118055760405162461bcd60e51b815260040161087e90615715565b600180546001600160a01b0319166001600160a01b0383169081179091556040517fda7b0a7bc965abdec8a1a995575a891838264c2968e14bd456c5391827b7aa3090600090a250565b6001546001600160a01b031633146118795760405162461bcd60e51b815260040161087e90615715565b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527f7b9adf13852d5b3e153f2a442b9aa3a4570da8f9b9046c60410b17e8034fe33c90602001611356565b3360009081526002602052604090205460ff166118f65760405162461bcd60e51b815260040161087e90615715565b83828082146119175760405162461bcd60e51b815260040161087e906157b6565b4263ffffffff168963ffffffff161015801561193e57508863ffffffff168863ffffffff16115b6119835760405162461bcd60e51b8152602060048201526016602482015275616464506f6f6c3a20696e76616c69642074696d657360501b604482015260640161087e565b6000600454905060006005600083815260200190815260200160002090508b8160000160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508a8160000160146101000a81548163ffffffff021916908363ffffffff160217905550898160000160186101000a81548163ffffffff021916908363ffffffff1602179055506000816001018190555084816003018190555060005b88811015611ae3578160020160405180604001604052808c8c85818110611a4f57611a4f615738565b9050602002016020810190611a6491906151e7565b6001600160a01b031681526020018a8a85818110611a8457611a84615738565b60209081029290920135909252835460018082018655600095865294829020845160029092020180546001600160a01b0319166001600160a01b0390921691909117815592015191909201555080611adb81615764565b915050611a26565b5060048054906000611af483615764565b9091555050604080516001600160a01b038e16815263ffffffff8d811660208301528c168183015260608101879052905183917f251199ba7b3e7a12b779b3f606eaf369cecc0eac8bcbacaadcf08079e8c5940f919081900360800190a2505050505050505050505050565b60026000541415611b835760405162461bcd60e51b815260040161087e9061577f565b6002600090815533905b82811015611e2557816001600160a01b031660066000868685818110611bb557611bb5615738565b60209081029290920135835250810191909152604001600020546001600160a01b031614611c1b5760405162461bcd60e51b81526020600482015260136024820152723bb4ba34323930bb9d103737ba1037bbb732b960691b604482015260640161087e565b611c4e60086000868685818110611c3457611c34615738565b905060200201358152602001908152602001600020613a36565b15611c9b5760405162461bcd60e51b815260206004820152601860248201527f77697468647261773a206e6f7420657869746564207965740000000000000000604482015260640161087e565b60066000858584818110611cb157611cb1615738565b60209081029290920135835250810191909152604001600090812080546001600160a01b031916815560010155611d1a848483818110611cf357611cf3615738565b6001600160a01b0386166000908152600960209081526040909120939102013590506144c0565b611d2357600080fd5b7f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a4806001600160a01b03166323b872dd3084878786818110611d6657611d66615738565b905060200201356040518463ffffffff1660e01b8152600401611d8b93929190615a12565b600060405180830381600087803b158015611da557600080fd5b505af1158015611db9573d6000803e3d6000fd5b50505050838382818110611dcf57611dcf615738565b6040516001600160a01b038616815260209182029390930135927f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436492500160405180910390a2611e1e81615764565b9050611b8d565b505060016000555050565b6001546001600160a01b03163314611e5a5760405162461bcd60e51b815260040161087e90615715565b600b5460ff1615611e9d5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642076616c756560981b604482015260640161087e565b600b805460ff191660011790556040517f1d8c7af3ae0426053ea40080543aa1cbe5bea5cdb7a55888b4cca96eea0493e490600090a1565b6001600160a01b0381166000908152600960205260409020606090611ef9906144cc565b92915050565b60026000541415611f225760405162461bcd60e51b815260040161087e9061577f565b6002600055600b54610100900460ff16611f4e5760405162461bcd60e51b815260040161087e906159db565b804263ffffffff161115611f985760405162461bcd60e51b815260206004820152601160248201527018db185a5b5199594e88195e1c1a5c9959607a1b604482015260640161087e565b6000878790509050600080856001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b158015611fdc57600080fd5b505afa158015611ff0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120149190615a36565b866001600160a01b031663d21220a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561204d57600080fd5b505afa158015612061573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120859190615a36565b9150915060005b8381101561228d5733600660008d8d858181106120ab576120ab615738565b60209081029290920135835250810191909152604001600020546001600160a01b0316146121115760405162461bcd60e51b815260206004820152601360248201527231b630b4b6a332b29d103737ba1037bbb732b960691b604482015260640161087e565b60035460009081906001600160a01b0316626349fb7f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a4808f8f8781811061215957612159615738565b6040516001600160e01b031960e087901b1681526001600160a01b0390941660048501526020029190910135602483015250604401604080518083038186803b1580156121a557600080fd5b505afa1580156121b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121dd9190615a53565b91509150846001600160a01b0316826001600160a01b03161480156122135750836001600160a01b0316816001600160a01b0316145b61225f5760405162461bcd60e51b815260206004820152601e60248201527f636c61696d4665653a20746f6b656e2070616972206e6f74206d617463680000604482015260640161087e565b6122838d8d8581811061227457612274615738565b905060200201358860016144d9565b505060010161208c565b5061229c82828a8a338a61464e565b505060016000555050505050505050565b600260005414156122d05760405162461bcd60e51b815260040161087e9061577f565b6002600055600b54610100900460ff166122fc5760405162461bcd60e51b815260040161087e906159db565b824263ffffffff1611156123525760405162461bcd60e51b815260206004820152601860248201527f72656d6f76654c69717569646974793a20657870697265640000000000000000604482015260640161087e565b6000878152600660205260409020546001600160a01b031633146123b85760405162461bcd60e51b815260206004820152601a60248201527f72656d6f76654c69717569646974793a206e6f74206f776e6572000000000000604482015260640161087e565b600354604051635c6d96a960e01b81526001600160a01b037f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a48081166004830152602482018a90526000921690635c6d96a99060440160206040518083038186803b15801561242557600080fd5b505afa158015612439573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061245d919061598f565b6001600160801b031690506000876001600160801b031611801561248a575080876001600160801b031611155b6124e15760405162461bcd60e51b815260206004820152602260248201527f72656d6f76654c69717569646974793a20696e76616c6964206c697175696469604482015261747960f01b606482015260840161087e565b6124f46001600160801b038816826159ac565b6000898152600660209081526040808320600101849055600890915281209192509061251f906144cc565b905060005b81518110156125e35760008a81526007602052604081208351829085908590811061255157612551615738565b60200260200101518152602001908152602001600020600501549050600084821161257d576000612587565b61258785836159ac565b905080156125d9576125d98c8585815181106125a5576125a5615738565b602002602001015183896001600281106125c1576125c1615738565b6020020160208101906125d49190615429565b613faa565b5050600101612524565b50600354604051626349fb60e01b81526001600160a01b037f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a48081166004830152602482018c90526000928392911690626349fb90604401604080518083038186803b15801561265157600080fd5b505afa158015612665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126899190615a53565b915091506126988b8b89614918565b6126a56020860186615429565b156126b6576126b68b8860006144d9565b6126c482828b8b338b61464e565b50506001600055505050505050505050565b600260005414156126f95760405162461bcd60e51b815260040161087e9061577f565b60026000908155600b54339160ff909116905b83811015612b0457826001600160a01b03166006600087878581811061273457612734615738565b60209081029290920135835250810191909152604001600020546001600160a01b03161461279a5760405162461bcd60e51b81526020600482015260136024820152723bb4ba34323930bb9d103737ba1037bbb732b960691b604482015260640161087e565b6001600a60008787858181106127b2576127b2615738565b90506020020135815260200190815260200160002060006101000a81548160ff0219169083151502179055506000612813600860008888868181106127f9576127f9615738565b9050602002013581526020019081526020016000206144cc565b905060005b81518110156128f057600082828151811061283557612835615738565b60209081029190910181015160008181526005909252604082206004018054600019019055915060079089898781811061287157612871615738565b602090810292909201358352508181019290925260409081016000908120848252909252812080546001600160801b0319168155906128b36001830182615077565b6128c1600283016000615077565b6128cf600383016000615077565b50600060048201819055600590910155506128e981615764565b9050612818565b506006600087878581811061290757612907615738565b60209081029290920135835250810191909152604001600090812080546001600160a01b03191681556001015582612a015761297586868481811061294e5761294e615738565b6001600160a01b0388166000908152600960209081526040909120939102013590506144c0565b61297e57600080fd5b60005b81518110156129ff57600082828151811061299e5761299e615738565b602002602001015190506129e581600860008b8b898181106129c2576129c2615738565b9050602002013581526020019081526020016000206144c090919063ffffffff16565b6129ee57600080fd5b506129f881615764565b9050612981565b505b7f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a4806001600160a01b03166323b872dd3086898987818110612a4457612a44615738565b905060200201356040518463ffffffff1660e01b8152600401612a6993929190615a12565b600060405180830381600087803b158015612a8357600080fd5b505af1158015612a97573d6000803e3d6000fd5b50505050858583818110612aad57612aad615738565b6040516001600160a01b038816815260209182029390930135927f5fafa99d0643513820be26656b45130b01e1c03062e1266bf36f88cbd3bd969592500160405180910390a250612afd81615764565b905061270c565b50506001600055505050565b600081815260056020818152604080842086855260078352818520868652909252832091820154606092839291612b895760405162461bcd60e51b815260206004820152601b60248201527f67657455736572496e666f3a206e6f74206a6f696e6564207965740000000000604482015260640161087e565b60028201546001600160401b03811115612ba557612ba5615824565b604051908082528060200260200182016040528015612bce578160200160208202803683370190505b5060028301549094506001600160401b03811115612bee57612bee615824565b604051908082528060200260200182016040528015612c17578160200160208202803683370190505b5092506000612c268888612d81565b905060005b6002840154811015612d6f576000612c778460050154866002018481548110612c5657612c56615738565b906000526020600020906002020160010154856060015186608001516149b9565b90506000612cd382866003018581548110612c9457612c94615738565b9060005260206000200154612ca991906159c3565b8560400151876001018681548110612cc357612cc3615738565b90600052602060002001546149e4565b905080856002018481548110612ceb57612ceb615738565b9060005260206000200154612d0091906159c3565b888481518110612d1257612d12615738565b602002602001018181525050846001018381548110612d3357612d33615738565b9060005260206000200154878481518110612d5057612d50615738565b602002602001018181525050505080612d6890615764565b9050612c2b565b50816005015495505050509250925092565b612dc36040518060c0016040528060006001600160801b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000828152600560209081526040808320868452600783528184208685529092529182902060035482549351632106297960e11b8152929391926001600160a01b039182169263420c52f292612e42929116907f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a480908a90600401615a12565b60206040518083038186803b158015612e5a57600080fd5b505afa158015612e6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e92919061598f565b6001600160801b0316835260035482546040516339bb866b60e11b81526001600160a01b03928316926373770cd692612ef4929116907f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a480908a90600401615a12565b60206040518083038186803b158015612f0c57600080fd5b505afa158015612f20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f449190615a82565b6020840181905260048201546003840154612f60929190614a1b565b604084015281546001830154612f8e9163ffffffff600160a01b8204811692600160c01b9092041690614a64565b6060840152805483516001600160801b03918216900316608084018190526005820154612fbb9190615a9b565b60a084015250909392505050565b8060045411612fea5760405162461bcd60e51b815260040161087e90615966565b60008181526005602081815260408084208685526006835281852054600784528286208787529093529320918201546001600160a01b0390911691906130725760405162461bcd60e51b815260206004820152601760248201527f686172766573743a206e6f74206a6f696e656420796574000000000000000000604482015260640161087e565b600061307e8686612d81565b90508060a0015184600101600082825461309891906159c3565b9091555050805182546001600160801b0319166001600160801b0390911617825560005b60028501548110156114fe5760006130e78460050154876002018481548110612c5657612c56615738565b90508015613161578084600301838154811061310557613105615738565b90600052602060002001600082825461311e91906159c3565b925050819055508086600201838154811061313b5761313b615738565b9060005260206000209060020201600101600082825461315b91906159ac565b90915550505b60006131a085600301848154811061317b5761317b615738565b90600052602060002001548560400151876001018681548110612cc357612cc3615738565b90506000818660020185815481106131ba576131ba615738565b90600052602060002001546131cf91906159c3565b905080156132ef57811561321257818660010185815481106131f3576131f3615738565b90600052602060002001600082825461320c91906159c3565b90915550505b600086600201858154811061322957613229615738565b906000526020600020018190555061327088600201858154811061324f5761324f615738565b60009182526020909120600290910201546001600160a01b03168883613eda565b808a7f1ec92956b13869de207e232e60c65e35826c5e12b7fdc6d70b083b5a4fd26703898b60020188815481106132a9576132a9615738565b60009182526020909120600290910201546040516132e692916001600160a01b0316906001600160a01b0392831681529116602082015260400190565b60405180910390a35b505050806132fc90615764565b90506130bc565b600084815260066020908152604080832060078352818420878552909252918290206003548454935163d7aee3bd60e01b8152929391926001600160a01b039182169263d7aee3bd9261337f929116907f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a480908b90600401615a12565b60206040518083038186803b15801561339757600080fd5b505afa1580156133ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133cf9190615aba565b6134105760405162461bcd60e51b81526020600482015260126024820152711a9bda5b8e881a5b9d985b1a59081c1bdbdb60721b604482015260640161087e565b8315801590613423575081600101548411155b6134635760405162461bcd60e51b81526020600482015260116024820152706a6f696e3a20696e76616c6964206c697160781b604482015260640161087e565b6003548354604051632106297960e11b81526001600160a01b039283169263420c52f2926134ba929116907f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a480908b90600401615a12565b60206040518083038186803b1580156134d257600080fd5b505afa1580156134e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061350a919061598f565b81546001600160801b0319166001600160801b039190911617815560028301546001600160401b0381111561354157613541615824565b60405190808252806020026020018201604052801561356a578160200160208202803683370190505b508051613581916001840191602090910190615098565b5060028301546001600160401b0381111561359e5761359e615824565b6040519080825280602002602001820160405280156135c7578160200160208202803683370190505b5080516135de916002840191602090910190615098565b5060028301546001600160401b038111156135fb576135fb615824565b604051908082528060200260200182016040528015613624578160200160208202803683370190505b50805161363b916003840191602090910190615098565b506003830154156136f05760035460405163528ee47960e11b81526001600160a01b037f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a48081166004830152602482018990529091169063a51dc8f29060440160206040518083038186803b1580156136b257600080fd5b505afa1580156136c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136ea9190615a82565b60048201555b6005810184905560048301805490600061370983615764565b909155505060008681526008602052604090206137269086614ab8565b6137725760405162461bcd60e51b815260206004820152601760248201527f4661696c20746f20616464206a6f696e6564506f6f6c73000000000000000000604482015260640161087e565b8385877f5d48292d83151281e6ea967de9fd35716c713dc76c11beb42d78ed5110270f7360405160405180910390a4505050505050565b60008481526006602090815260408083206007835281842087855290925290912083158015906137eb5750600182015460058201546137e890866159c3565b11155b61382b5760405162461bcd60e51b815260206004820152601160248201527073796e633a20696e76616c6964206c697160781b604482015260640161087e565b60006138378787612d81565b905060005b60028501548110156139815760006138678460050154876002018481548110612c5657612c56615738565b905080156138e1578084600301838154811061388557613885615738565b90600052602060002001600082825461389e91906159c3565b92505081905550808660020183815481106138bb576138bb615738565b906000526020600020906002020160010160008282546138db91906159ac565b90915550505b60006138fb85600301848154811061317b5761317b615738565b9050801561396e578085600101848154811061391957613919615738565b90600052602060002001600082825461393291906159c3565b925050819055508085600201848154811061394f5761394f615738565b90600052602060002001600082825461396891906159c3565b90915550505b50508061397a90615764565b905061383c565b508060a0015184600101600082825461399a91906159c3565b9091555050805182546001600160801b0319166001600160801b0390911617825560048201546020820151600386015460058501546139dd93929190898c614ac4565b8260040181905550848260050160008282546139f991906159c3565b90915550506040518590879089907ff31cd5db84a4ab16cd339c510d5a60c63ef5818c538aa67046f5bc0dca794cd390600090a450505050505050565b6000611ef9825490565b6000613a4c8383614be6565b9392505050565b600b5460ff1615613a9f5760405162461bcd60e51b8152602060048201526016602482015275139bdd08185b1b1bddd959081d1bc819195c1bdcda5d60521b604482015260640161087e565b8015613b51578260045411613ac65760405162461bcd60e51b815260040161087e90615966565b600083815260056020526040902054429063ffffffff808316600160a01b9092041611801590613b14575060008481526005602052604090205463ffffffff600160c01b9091048116908216105b613b4f5760405162461bcd60e51b815260206004820152600c60248201526b496e76616c69642074696d6560a01b604482015260640161087e565b505b3360005b8351811015613ed357600a6000858381518110613b7457613b74615738565b60209081029190910181015182528101919091526040016000205460ff1615613bd85760405162461bcd60e51b8152602060048201526016602482015275139bdd08185b1b1bddd959081d1bc819195c1bdcda5d60521b604482015260640161087e565b613c28848281518110613bed57613bed615738565b602002602001015160096000856001600160a01b03166001600160a01b03168152602001908152602001600020614ab890919063ffffffff16565b613c3157600080fd5b7f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a4806001600160a01b03166323b872dd8330878581518110613c7457613c74615738565b60200260200101516040518463ffffffff1660e01b8152600401613c9a93929190615a12565b600060405180830381600087803b158015613cb457600080fd5b505af1158015613cc8573d6000803e3d6000fd5b50505050838181518110613cde57613cde615738565b60200260200101517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c83604051613d2491906001600160a01b0391909116815260200190565b60405180910390a28160066000868481518110613d4357613d43615738565b6020908102919091018101518252810191909152604001600090812080546001600160a01b0319166001600160a01b03938416179055600354865191921690635c6d96a9907f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a48090889086908110613dbc57613dbc615738565b60200260200101516040518363ffffffff1660e01b8152600401613df59291906001600160a01b03929092168252602082015260400190565b60206040518083038186803b158015613e0d57600080fd5b505afa158015613e21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e45919061598f565b9050806001600160801b031660066000878581518110613e6757613e67615738565b60200260200101518152602001908152602001600020600101819055508315613eca57613eca858381518110613e9f57613e9f615738565b602002602001015187836001600160801b0316600560008b8152602001908152602001600020613303565b50600101613b55565b5050505050565b6001600160a01b038316613f91576000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613f35576040519150601f19603f3d011682016040523d82523d6000602084013e613f3a565b606091505b5050905080613f8b5760405162461bcd60e51b815260206004820152601c60248201527f7472616e736665722072657761726420746f6b656e206661696c656400000000604482015260640161087e565b50505050565b613fa56001600160a01b0384168383614c10565b505050565b600083815260056020908152604080832087845260068352818420546007845282852088865290935292206001600160a01b03909116903382148061401c57508254600160c01b900463ffffffff164263ffffffff1611801561401c57503360009081526002602052604090205460ff165b6140725760405162461bcd60e51b815260206004820152602160248201527f657869743a206e6f74206f776e6572206f7220706f6f6c206e6f7420656e64656044820152601960fa1b606482015260840161087e565b600581015485158015906140865750808611155b6140c65760405162461bcd60e51b8152602060048201526011602482015270657869743a20696e76616c6964206c697160781b604482015260640161087e565b60006140d287836159ac565b905060006140e08a8a612d81565b90508060a001518660010160008282546140fa91906159c3565b9091555050805184546001600160801b0319166001600160801b039091161784556005840182905560005b600287015481101561439957600061414c85896002018481548110612c5657612c56615738565b905080156141c6578086600301838154811061416a5761416a615738565b90600052602060002001600082825461418391906159c3565b92505081905550808860020183815481106141a0576141a0615738565b906000526020600020906002020160010160008282546141c091906159ac565b90915550505b60006142058760030184815481106141e0576141e0615738565b90600052602060002001548560400151896001018681548110612cc357612cc3615738565b9050600087600201848154811061421e5761421e615738565b9060005260206000200154905081600014614275578188600101858154811061424957614249615738565b90600052602060002001600082825461426291906159c3565b90915550614272905082826159c3565b90505b8015614385578a1561436257600088600201858154811061429857614298615738565b90600052602060002001819055506142df8a60020185815481106142be576142be615738565b60009182526020909120600290910201546001600160a01b03168a83613eda565b808e7f1ec92956b13869de207e232e60c65e35826c5e12b7fdc6d70b083b5a4fd267038b8d600201888154811061431857614318615738565b600091825260209091206002909102015460405161435592916001600160a01b0316906001600160a01b0392831681529116602082015260400190565b60405180910390a3614385565b8088600201858154811061437857614378615738565b6000918252602090912001555b5050508061439290615764565b9050614125565b508161447b5760008a81526007602090815260408083208c8452909152812080546001600160801b0319168155906143d46001830182615077565b6143e2600283016000615077565b6143f0600383016000615077565b5060006004828101829055600590920181905590870180549161441283615ad7565b909155505060008a815260086020526040902061442f908a6144c0565b61447b5760405162461bcd60e51b815260206004820152601a60248201527f4661696c20746f2072656d6f7665206a6f696e6564506f6f6c73000000000000604482015260640161087e565b60405133815288908a908c907f275029c7b988945c03ac5499c0d532fce79ce36efab42e1b3f180a62001cad2c9060200160405180910390a450505050505050505050565b6000613a4c8383614c62565b60606000613a4c83614d55565b801561457b57604051630c479e6560e21b8152600481018490527f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a4806001600160a01b03169063311e799490602401602060405180830381600087803b15801561454157600080fd5b505af1158015614555573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145799190615a82565b505b60408051608081018252848152600060208201818152828401918252606083018681529351637686c6e960e11b815283516004820152905160248201529051604482015291516064830152907f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a4806001600160a01b03169063ed0d8dd2906084015b606060405180830381600087803b15801561461657600080fd5b505af115801561462a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114fe9190615aee565b7f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a480811561484a57866001600160a01b03167f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031614156147795760405163bac37ef760e01b8152600481018690526001600160a01b03848116602483015282169063bac37ef790604401600060405180830381600087803b1580156146f957600080fd5b505af115801561470d573d6000803e3d6000fd5b505060405163bf1316c160e01b81526001600160a01b038416925063bf1316c1915061474190899088908890600401615b1c565b600060405180830381600087803b15801561475b57600080fd5b505af115801561476f573d6000803e3d6000fd5b5050505050614910565b856001600160a01b03167f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316141561484a5760405163bf1316c160e01b81526001600160a01b0382169063bf1316c1906147e3908a9089908890600401615b1c565b600060405180830381600087803b1580156147fd57600080fd5b505af1158015614811573d6000803e3d6000fd5b505060405163bac37ef760e01b8152600481018790526001600160a01b0386811660248301528416925063bac37ef79150604401614741565b60405163bf1316c160e01b81526001600160a01b0382169063bf1316c19061487a908a9089908890600401615b1c565b600060405180830381600087803b15801561489457600080fd5b505af11580156148a8573d6000803e3d6000fd5b505060405163bf1316c160e01b81526001600160a01b038416925063bf1316c191506148dc90899088908890600401615b1c565b600060405180830381600087803b1580156148f657600080fd5b505af115801561490a573d6000803e3d6000fd5b50505050505b505050505050565b6040805160a0810182528481526001600160801b03848116602083019081526000838501818152606085019182526080850187815295516398e04d7760e01b81528551600482015292519093166024830152915160448201529051606482015291516084830152907f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a4806001600160a01b0316906398e04d779060a4016145fc565b600082826149c78688615a9b565b6149d19190615a9b565b6149db9190615b55565b95945050505050565b60008064e8d4a510006149f78587615a9b565b614a019190615b55565b9050828111614a115760006149db565b6149db83826159ac565b600081614a2e575064e8d4a51000613a4c565b6000614a3a8486615b69565b90506149db83614a4f64e8d4a5100084615a9b565b614a599190615b55565b64e8d4a51000614db1565b60008084614a784263ffffffff1686614dc7565b614a8291906159ac565b90506000614a9482600160601b615a9b565b9050838111614aa4576000614aae565b614aae84826159ac565b9695505050505050565b6000613a4c8383614dde565b600084614ad357506000614aae565b600087614ae08789615b69565b12614af457614aef8688615b69565b614af6565b875b60035460405163528ee47960e11b81526001600160a01b037f000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a48081166004830152602482018790529293506000929091169063a51dc8f29060440160206040518083038186803b158015614b6857600080fd5b505afa158015614b7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614ba09190615a82565b614baa9086615ba8565b614bb48388615ba8565b614bbe9190615c2d565b90506000614bcc86886159c3565b9050614bd88183615c6e565b9a9950505050505050505050565b6000826000018281548110614bfd57614bfd615738565b9060005260206000200154905092915050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613fa5908490614e2d565b60008181526001830160205260408120548015614d4b576000614c866001836159ac565b8554909150600090614c9a906001906159ac565b9050818114614cff576000866000018281548110614cba57614cba615738565b9060005260206000200154905080876000018481548110614cdd57614cdd615738565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614d1057614d10615c9c565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050611ef9565b6000915050611ef9565b606081600001805480602002602001604051908101604052809291908181526020018280548015614da557602002820191906000526020600020905b815481526020019060010190808311614d91575b50505050509050919050565b600081831015614dc15782613a4c565b50919050565b600081831015614dd75781613a4c565b5090919050565b6000818152600183016020526040812054614e2557508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611ef9565b506000611ef9565b6000614e82826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614eff9092919063ffffffff16565b805190915015613fa55780806020019051810190614ea09190615aba565b613fa55760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161087e565b6060614f0e8484600085614f16565b949350505050565b606082471015614f775760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161087e565b843b614fc55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161087e565b600080866001600160a01b03168587604051614fe19190615cde565b60006040518083038185875af1925050503d806000811461501e576040519150601f19603f3d011682016040523d82523d6000602084013e615023565b606091505b509150915061503382828661503e565b979650505050505050565b6060831561504d575081613a4c565b82511561505d5782518084602001fd5b8160405162461bcd60e51b815260040161087e9190615cfa565b508054600082559060005260206000209081019061509591906150e3565b50565b8280548282559060005260206000209081019282156150d3579160200282015b828111156150d35782518255916020019190600101906150b8565b506150df9291506150e3565b5090565b5b808211156150df57600081556001016150e4565b803563ffffffff8116811461510c57600080fd5b919050565b60008083601f84011261512357600080fd5b5081356001600160401b0381111561513a57600080fd5b6020830191508360208260051b850101111561515557600080fd5b9250929050565b60008060008060008060a0878903121561517557600080fd5b86359550615185602088016150f8565b9450615193604088016150f8565b935060608701356001600160401b038111156151ae57600080fd5b6151ba89828a01615111565b979a9699509497949695608090950135949350505050565b6001600160a01b038116811461509557600080fd5b6000602082840312156151f957600080fd5b8135613a4c816151d2565b6000806000806040858703121561521a57600080fd5b84356001600160401b038082111561523157600080fd5b61523d88838901615111565b9096509450602087013591508082111561525657600080fd5b5061526387828801615111565b95989497509550505050565b60006020828403121561528157600080fd5b5035919050565b600081518084526020808501945080840160005b838110156152b85781518752958201959082019060010161529c565b509495945050505050565b6001600160a01b03898116825263ffffffff8981166020808501919091529089166040840152606083018890526080830187905260a0830186905261010060c08401819052855190840181905260009261012085019287810192855b8181101561533d57845184168652948201949382019360010161531f565b505050505082810360e08401526153548185615288565b9b9a5050505050505050505050565b60008060008060006060868803121561537b57600080fd5b8535945060208601356001600160401b038082111561539957600080fd5b6153a589838a01615111565b909650945060408801359150808211156153be57600080fd5b506153cb88828901615111565b969995985093965092949392505050565b6000806000606084860312156153f157600080fd5b505081359360208301359350604090920135919050565b602081526000613a4c6020830184615288565b801515811461509557600080fd5b60006020828403121561543b57600080fd5b8135613a4c8161541b565b6000806020838503121561545957600080fd5b82356001600160401b0381111561546f57600080fd5b61547b85828601615111565b90969095509350505050565b60008060006040848603121561549c57600080fd5b8335925060208401356001600160401b038111156154b957600080fd5b6154c586828701615111565b9497909650939450505050565b600080604083850312156154e557600080fd5b82356154f0816151d2565b915060208301356155008161541b565b809150509250929050565b60008060008060008060008060c0898b03121561552757600080fd5b8835615532816151d2565b975061554060208a016150f8565b965061554e60408a016150f8565b955060608901356001600160401b038082111561556a57600080fd5b6155768c838d01615111565b909750955060808b013591508082111561558f57600080fd5b5061559c8b828c01615111565b999c989b50969995989497949560a00135949350505050565b600080604083850312156155c857600080fd5b50508035926020909101359150565b600080600080600080600060c0888a0312156155f257600080fd5b87356001600160401b0381111561560857600080fd5b6156148a828b01615111565b90985096505060208801359450604088013593506060880135615636816151d2565b925060808801356156468161541b565b8092505060a0880135905092959891949750929550565b6001600160801b038116811461509557600080fd5b600080600080600080600061010080898b03121561568f57600080fd5b8835975060208901356156a18161565d565b965060408901359550606089013594506080890135935060a08901356156c68161541b565b92508881018a10156156d757600080fd5b5060c08801905092959891949750929550565b8381526060602082015260006157036060830185615288565b8281036040840152614aae8185615288565b6020808252600990820152683337b93134b23232b760b91b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006000198214156157785761577861574e565b5060010190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020808252600e908201526d0d2dcecc2d8d2c840d8cadccee8d60931b604082015260600190565b6000808335601e198436030181126157f557600080fd5b8301803591506001600160401b0382111561580f57600080fd5b60200191503681900382131561515557600080fd5b634e487b7160e01b600052604160045260246000fd5b604051602081016001600160401b038111828210171561585c5761585c615824565b60405290565b604051601f8201601f191681016001600160401b038111828210171561588a5761588a615824565b604052919050565b600060208083850312156158a557600080fd5b82356001600160401b03808211156158bc57600080fd5b81850191508282870312156158d057600080fd5b6158d861583a565b8235828111156158e757600080fd5b80840193505086601f8401126158fc57600080fd5b82358281111561590e5761590e615824565b8060051b925061591f858401615862565b818152928401850192858101908985111561593957600080fd5b948601945b848610156159575785358252948601949086019061593e565b83525090979650505050505050565b6020808252600f908201526e506f6f6c206e6f742065786973747360881b604082015260600190565b6000602082840312156159a157600080fd5b8151613a4c8161565d565b6000828210156159be576159be61574e565b500390565b600082198211156159d6576159d661574e565b500190565b60208082526018908201527f7370656369616c20666561747572652064697361626c65640000000000000000604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600060208284031215615a4857600080fd5b8151613a4c816151d2565b60008060408385031215615a6657600080fd5b8251615a71816151d2565b6020840151909250615500816151d2565b600060208284031215615a9457600080fd5b5051919050565b6000816000190483118215151615615ab557615ab561574e565b500290565b600060208284031215615acc57600080fd5b8151613a4c8161541b565b600081615ae657615ae661574e565b506000190190565b600080600060608486031215615b0357600080fd5b8351925060208401519150604084015190509250925092565b6001600160a01b0393841681526020810192909252909116604082015260600190565b634e487b7160e01b600052601260045260246000fd5b600082615b6457615b64615b3f565b500490565b60008083128015600160ff1b850184121615615b8757615b8761574e565b6001600160ff1b0384018313811615615ba257615ba261574e565b50500390565b60006001600160ff1b0381841382841380821686840486111615615bce57615bce61574e565b600160ff1b6000871282811687830589121615615bed57615bed61574e565b60008712925087820587128484161615615c0957615c0961574e565b87850587128184161615615c1f57615c1f61574e565b505050929093029392505050565b600080821280156001600160ff1b0384900385131615615c4f57615c4f61574e565b600160ff1b8390038412811615615c6857615c6861574e565b50500190565b600082615c7d57615c7d615b3f565b600160ff1b821460001984141615615c9757615c9761574e565b500590565b634e487b7160e01b600052603160045260246000fd5b60005b83811015615ccd578181015183820152602001615cb5565b83811115613f8b5750506000910152565b60008251615cf0818460208701615cb2565b9190910192915050565b6020815260008251806020840152615d19816040850160208701615cb2565b601f01601f1916919091016040019291505056fea164736f6c6343000809000a

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

000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a48000000000000000000000000035be3f4fd8239a35a7f120756d4d69e5c5e10870

-----Decoded View---------------
Arg [0] : _nft (address): 0xe222fBE074A436145b255442D919E4E3A6c6a480
Arg [1] : _helper (address): 0x35BE3F4fd8239A35a7F120756D4D69e5C5e10870

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000e222fbe074a436145b255442d919e4e3a6c6a480
Arg [1] : 00000000000000000000000035be3f4fd8239a35a7f120756d4d69e5c5e10870


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
Chain Token Portfolio % Price Amount Value
BSC33.12%$0.410074,249.8978$1,742.76
ARB9.59%$0.4091771,233.8507$504.86
ARB8.15%$3,041.650.141$428.96
ARB3.21%$0.594317284.4387$169.05
ARB1.04%$0.465794117.9732$54.95
ARB0.36%$3,354.80.00558657$18.74
ARB0.20%$110.4808$10.48
OP18.27%$1.28749.9093$961.2
OP0.50%$3,353.090.00784124$26.29
OP0.07%$0.4090428.4704$3.46
MATIC15.84%$3,353.090.2485$833.31
MATIC2.38%$0.409042306.1352$125.22
MATIC<0.01%$1.510.0716$0.1081
BASE3.67%$0.409042471.676$192.94
ETH2.48%$0.407104320.9995$130.68
ETH0.53%$3,355.740.00823924$27.65
ETH0.51%$2,956.80.00913038$27
LINEA0.08%$0.40971710.3684$4.25
Loading...
Loading
[ Download: CSV Export  ]

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