FTM Price: $0.99 (-3.27%)
Gas: 45 GWei

Contract

0xC1d8804fF5DF18058e9b804890053e3d686c7c79
 

Overview

FTM Balance

Fantom LogoFantom LogoFantom Logo0 FTM

FTM Value

$0.00

Sponsored

Transaction Hash
Method
Block
From
To
Value
0x60806040254550852021-12-19 21:39:37830 days ago1639949977IN
 Create: ContractReader
0 FTM1.34530151312.9079

Latest 1 internal transaction

Parent Txn Hash Block From To Value
254550852021-12-19 21:39:37830 days ago1639949977  Contract Creation0 FTM
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ContractReader

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 150 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 15 : ContractReader.sol
// SPDX-License-Identifier: ISC
/**
* By using this software, you understand, acknowledge and accept that Tetu
* and/or the underlying software are provided “as is” and “as available”
* basis and without warranties or representations of any kind either expressed
* or implied. Any use of this open source software released under the ISC
* Internet Systems Consortium license is done at your own risk to the fullest
* extent permissible pursuant to applicable law any and all liability as well
* as all warranties, including any fitness for a particular purpose with respect
* to Tetu and/or the underlying software and the use thereof are disclaimed.
*/

pragma solidity 0.8.4;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "../base/governance/Controllable.sol";
import "../base/interface/IBookkeeper.sol";
import "../base/interface/ISmartVault.sol";
import "../base/interface/IStrategy.sol";
import "../infrastructure/price/IPriceCalculator.sol";

/// @title View data reader for using on website UI and other integrations
/// @author belbix
contract ContractReader is Initializable, Controllable {
  using SafeMath for uint256;

  string public constant VERSION = "1.0.4";
  uint256 constant public PRECISION = 1e18;
  mapping(bytes32 => address) internal tools;

  function initialize(address _controller, address _calculator) external initializer {
    Controllable.initializeControllable(_controller);
    tools[keccak256(abi.encodePacked("calculator"))] = _calculator;
  }

  event ToolAddressUpdated(address newValue);

  struct VaultInfo {
    address addr;
    string name;
    uint256 created;
    bool active;
    uint256 tvl;
    uint256 tvlUsdc;
    uint256 decimals;
    address underlying;
    address[] rewardTokens;
    uint256[] rewardTokensBal;
    uint256[] rewardTokensBalUsdc;
    uint256 duration;
    uint256[] rewardsApr;
    uint256 ppfsApr;
    uint256 users;

    // strategy
    address strategy;
    uint256 strategyCreated;
    IStrategy.Platform platform;
    address[] assets;
    address[] strategyRewards;
    bool strategyOnPause;
    uint256 earned;
  }

  struct VaultInfoLight {
    address addr;
    uint256 created;
    bool active;
    uint256 tvl;
    uint256 tvlUsdc;
    address underlying;
    address[] rewardTokens;
    uint256[] rewardsApr;
    uint256 ppfsApr;
    IStrategy.Platform platform;
    address[] assets;
    uint256 earned;
  }

  struct UserInfo {
    address wallet;
    address vault;
    uint256 underlyingBalance;
    uint256 underlyingBalanceUsdc;
    uint256 depositedUnderlying;
    uint256 depositedUnderlyingUsdc;
    uint256 depositedShare;
    address[] rewardTokens;
    uint256[] rewards;
    uint256[] rewardsUsdc;
    uint256[] rewardsBoost;
    uint256[] rewardsBoostUsdc;
  }

  struct UserInfoLight {
    uint256 depositedUnderlying;
    uint256 depositedUnderlyingUsdc;
    uint256 depositedShare;
  }

  struct VaultWithUserInfo {
    VaultInfo vault;
    UserInfo user;
  }

  struct VaultWithUserInfoLight {
    VaultInfoLight vault;
    UserInfoLight user;
  }

  // **************************************************************
  // HEAVY QUERIES
  //***************************************************************

  function vaultInfo(address vault) public view returns (VaultInfo memory) {
    address strategy = ISmartVault(vault).strategy();
    VaultInfo memory v = VaultInfo(
      vault,
      vaultName(vault),
      vaultCreated(vault),
      vaultActive(vault),
      vaultTvl(vault),
      vaultTvlUsdc(vault),
      vaultDecimals(vault),
      vaultUnderlying(vault),
      vaultRewardTokens(vault),
      vaultRewardTokensBal(vault),
      vaultRewardTokensBalUsdc(vault),
      vaultDuration(vault),
      vaultRewardsApr(vault),
      vaultPpfsApr(vault),
      vaultUsers(vault),
      strategy,
      strategyCreated(strategy),
      strategyPlatform(strategy),
      strategyAssets(strategy),
      strategyRewardTokens(strategy),
      strategyPausedInvesting(strategy),
      strategyEarned(strategy)
    );

    return v;
  }

  function vaultInfoLight(address vault) public view returns (VaultInfoLight memory) {
    address strategy = ISmartVault(vault).strategy();
    VaultInfoLight memory v = VaultInfoLight(
      vault,
      vaultCreated(vault),
      vaultActive(vault),
      vaultTvl(vault),
      vaultTvlUsdc(vault),
      vaultUnderlying(vault),
      vaultRewardTokens(vault),
      vaultRewardsApr(vault),
      vaultPpfsApr(vault),
      strategyPlatform(strategy),
      strategyAssets(strategy),
      strategyEarned(strategy)
    );

    return v;
  }

  function vaultInfos(address[] memory _vaults)
  external view returns (VaultInfo[] memory){
    VaultInfo[] memory result = new VaultInfo[](_vaults.length);
    for (uint256 i = 0; i < _vaults.length; i++) {
      result[i] = vaultInfo(_vaults[i]);
    }
    return result;
  }

  function vaultInfosLight(address[] memory _vaults)
  external view returns (VaultInfoLight[] memory){
    VaultInfoLight[] memory result = new VaultInfoLight[](_vaults.length);
    for (uint256 i = 0; i < _vaults.length; i++) {
      result[i] = vaultInfoLight(_vaults[i]);
    }
    return result;
  }

  function userInfo(address _user, address _vault) public view returns (UserInfo memory) {
    address[] memory rewardTokens = ISmartVault(_vault).rewardTokens();
    uint256[] memory rewardsEarned = new uint256[](rewardTokens.length);
    for (uint256 i = 0; i < rewardTokens.length; i++) {
      rewardsEarned[i] = ISmartVault(_vault).earned(rewardTokens[i], _user);
    }
    return UserInfo(
      _user,
      _vault,
      userUnderlyingBalance(_user, _vault),
      userUnderlyingBalanceUsdc(_user, _vault),
      userDepositedUnderlying(_user, _vault),
      userDepositedUnderlyingUsdc(_user, _vault),
      userDepositedShare(_user, _vault),
      rewardTokens,
      userRewards(_user, _vault),
      userRewardsUsdc(_user, _vault),
      userRewardsBoost(_user, _vault),
      userRewardsBoostUsdc(_user, _vault)
    );
  }

  function userInfoLight(address _user, address _vault) public view returns (UserInfoLight memory) {
    return UserInfoLight(
      userDepositedUnderlying(_user, _vault),
      userDepositedUnderlyingUsdc(_user, _vault),
      userDepositedShare(_user, _vault)
    );
  }

  function userInfosLight(address _user, address[] memory _vaults)
  external view returns (UserInfoLight[] memory) {
    UserInfoLight[] memory result = new UserInfoLight[](_vaults.length);
    for (uint256 i = 0; i < _vaults.length; i++) {
      result[i] = userInfoLight(_user, _vaults[i]);
    }
    return result;
  }


  function vaultWithUserInfos(address _user, address[] memory _vaults)
  external view returns (VaultWithUserInfo[] memory){
    VaultWithUserInfo[] memory result = new VaultWithUserInfo[](_vaults.length);
    for (uint256 i = 0; i < _vaults.length; i++) {
      result[i] = VaultWithUserInfo(
        vaultInfo(_vaults[i]),
        userInfo(_user, _vaults[i])
      );
    }
    return result;
  }

  function vaultWithUserInfosLight(address _user, address[] memory _vaults)
  external view returns (VaultWithUserInfoLight[] memory){
    VaultWithUserInfoLight[] memory result = new VaultWithUserInfoLight[](_vaults.length);
    for (uint256 i = 0; i < _vaults.length; i++) {
      result[i] = VaultWithUserInfoLight(
        vaultInfoLight(_vaults[i]),
        userInfoLight(_user, _vaults[i])
      );
    }
    return result;
  }

  function vaultWithUserInfoPages(address _user, uint256 page, uint256 pageSize)
  external view returns (VaultWithUserInfo[] memory){

    uint256 size = vaults().length;
    require(size > 0, "empty vaults");

    uint256 totalPages = size / pageSize;
    if (totalPages * pageSize < size) {
      totalPages++;
    }

    if (page > totalPages) {
      page = totalPages;
    }

    uint256 start = Math.min(page * pageSize, size - 1);
    uint256 end = Math.min((start + pageSize), size);
    VaultWithUserInfo[] memory result = new VaultWithUserInfo[](end - start);
    for (uint256 i = start; i < end; i++) {
      result[i - start] = VaultWithUserInfo(
        vaultInfo(vaults()[i]),
        userInfo(_user, vaults()[i])
      );
    }
    return result;
  }

  function vaultWithUserInfoPagesLight(address _user, uint256 page, uint256 pageSize)
  external view returns (VaultWithUserInfoLight[] memory){

    uint256 size = vaults().length;
    require(size > 0, "empty vaults");

    uint256 totalPages = size / pageSize;
    if (totalPages * pageSize < size) {
      totalPages++;
    }

    if (page > totalPages) {
      page = totalPages;
    }

    uint256 start = Math.min(page * pageSize, size - 1);
    uint256 end = Math.min((start + pageSize), size);
    VaultWithUserInfoLight[] memory result = new VaultWithUserInfoLight[](end - start);
    for (uint256 i = start; i < end; i++) {
      result[i - start] = VaultWithUserInfoLight(
        vaultInfoLight(vaults()[i]),
        userInfoLight(_user, vaults()[i])
      );
    }
    return result;
  }

  function tetuTokenValues() external view returns (uint256[] memory){
    uint256 price = getPrice(IController(controller()).rewardToken());
    uint256 mCap = ERC20(IController(controller()).rewardToken()).totalSupply()
    .mul(price).div(1e18);

    uint256[] memory result = new uint256[](2);
    result[0] = price;
    result[1] = mCap;
    return result;
  }

  function totalTvlUsdc(address[] memory _vaults) external view returns (uint256) {
    uint256 result = 0;
    for (uint256 i = 0; i < _vaults.length; i++) {
      result += vaultTvlUsdc(_vaults[i]);
    }
    return result;
  }

  function totalTetuBoughBack(address[] memory _vaults) external view returns (uint256) {
    uint256 result = 0;
    for (uint256 i = 0; i < _vaults.length; i++) {
      result += strategyEarned(ISmartVault(_vaults[i]).strategy());
    }
    return result;
  }

  function totalTetuBoughBack2(address[] memory _strategies) external view returns (uint256) {
    uint256 result = 0;
    for (uint256 i = 0; i < _strategies.length; i++) {
      result += strategyEarned(_strategies[i]);
    }
    return result;
  }

  function totalUsers(address[] memory _vaults) external view returns (uint256) {
    uint256 result = 0;
    for (uint256 i = 0; i < _vaults.length; i++) {
      result += vaultUsers(_vaults[i]);
    }
    return result;
  }

  function totalUsersForAllVaults() external view returns (uint256) {
    address[] memory _vaults = vaults();
    uint256 result = 0;
    for (uint256 i = 0; i < _vaults.length; i++) {
      result += vaultUsers(_vaults[i]);
    }
    return result;
  }

  // ********************** FIELDS ***********************

  // no decimals
  function vaultUsers(address _vault) public view returns (uint256){
    return IBookkeeper(bookkeeper()).vaultUsersQuantity(_vault);
  }

  function vaultName(address _vault) public view returns (string memory){
    return ERC20(_vault).name();
  }

  function vaultPlatform(address _vault) public view returns (IStrategy.Platform){
    return IStrategy(ISmartVault(_vault).strategy()).platform();
  }

  // no decimals
  function vaultCreated(address _vault) public view returns (uint256){
    return Controllable(_vault).created();
  }

  function vaultActive(address _vault) public view returns (bool){
    return ISmartVault(_vault).active();
  }

  // normalized precision
  function vaultTvl(address _vault) public view returns (uint256){
    return normalizePrecision(ISmartVault(_vault).underlyingBalanceWithInvestment(), vaultDecimals(_vault));
  }

  // normalized precision
  function vaultTvlUsdc(address _vault) public view returns (uint256){
    uint256 underlyingPrice = getPrice(vaultUnderlying(_vault));
    return vaultTvl(_vault).mul(underlyingPrice).div(PRECISION);
  }

  function vaultDecimals(address _vault) public view returns (uint256){
    return uint256(ERC20(_vault).decimals());
  }

  function vaultUnderlying(address _vault) public view returns (address){
    return ISmartVault(_vault).underlying();
  }

  // no decimals
  function vaultDuration(address _vault) public view returns (uint256){
    return ISmartVault(_vault).duration();
  }

  function vaultRewardTokens(address _vault) public view returns (address[] memory){
    return ISmartVault(_vault).rewardTokens();
  }

  // normalized precision
  function vaultRewardTokensBal(address _vault) public view returns (uint256[] memory){
    uint256[] memory result = new uint256[](vaultRewardTokens(_vault).length);
    for (uint256 i = 0; i < vaultRewardTokens(_vault).length; i++) {
      address rt = vaultRewardTokens(_vault)[i];
      result[i] = normalizePrecision(ERC20(rt).balanceOf(_vault), ERC20(rt).decimals());
    }
    return result;
  }

  // normalized precision
  function vaultRewardTokensBalUsdc(address _vault) public view returns (uint256[] memory){
    uint256[] memory result = new uint256[](vaultRewardTokens(_vault).length);
    for (uint256 i = 0; i < vaultRewardTokens(_vault).length; i++) {
      address rt = vaultRewardTokens(_vault)[i];
      uint256 rtPrice = getPrice(rt);
      uint256 bal = ERC20(rt).balanceOf(_vault).mul(rtPrice).div(PRECISION);
      result[i] = normalizePrecision(bal, ERC20(rt).decimals());
    }
    return result;
  }

  // normalized precision
  function vaultRewardsApr(address _vault) public view returns (uint256[] memory){
    ISmartVault vault = ISmartVault(_vault);
    uint256[] memory result = new uint256[](vault.rewardTokens().length);
    for (uint256 i = 0; i < vault.rewardTokens().length; i++) {
      result[i] = computeRewardApr(_vault, vault.rewardTokens()[i]);
    }
    return result;
  }

  // normalized precision
  function computeRewardApr(address _vault, address rt) public view returns (uint256) {
    uint256 periodFinish = ISmartVault(_vault).periodFinishForToken(rt);
    // already normalized precision
    uint256 tvlUsd = vaultTvlUsdc(_vault);
    uint256 rtPrice = getPrice(rt);

    uint256 rewardsForFullPeriod = ISmartVault(_vault).rewardRateForToken(rt)
    .mul(ISmartVault(_vault).duration());

    // keep precision numbers
    if (tvlUsd != 0 && rewardsForFullPeriod != 0 && periodFinish > block.timestamp) {
      uint256 currentPeriod = periodFinish.sub(block.timestamp);
      uint256 periodRatio = currentPeriod.mul(PRECISION).div(ISmartVault(_vault).duration());

      uint256 rtBalanceUsd = rewardsForFullPeriod
      .mul(periodRatio)
      .mul(rtPrice)
      .div(1e36);

      // amounts should have the same decimals
      rtBalanceUsd = normalizePrecision(rtBalanceUsd, ERC20(rt).decimals());

      return computeApr(tvlUsd, rtBalanceUsd, currentPeriod);
    } else {
      return 0;
    }
  }

  // https://www.investopedia.com/terms/a/apr.asp
  // TVL and rewards should be in the same currency and with the same decimals
  function computeApr(uint256 tvl, uint256 rewards, uint256 duration) public pure returns (uint256) {
    uint256 rewardsPerTvlRatio = rewards.mul(PRECISION).div(tvl).mul(PRECISION);
    return rewardsPerTvlRatio.mul(PRECISION).div(duration.mul(PRECISION).div(1 days))
    .mul(uint256(365)).mul(uint256(100)).div(PRECISION);
  }

  // normalized precision
  function vaultPpfs(address _vault) public view returns (uint256){
    return normalizePrecision(ISmartVault(_vault).getPricePerFullShare(), vaultDecimals(_vault));
  }

  // normalized precision
  function vaultPpfsApr(address _vault) public view returns (uint256){
    return normalizePrecision(computePpfsApr(
        ISmartVault(_vault).getPricePerFullShare(),
        10 ** vaultDecimals(_vault),
        block.timestamp,
        vaultCreated(_vault)
      ), vaultDecimals(_vault));
  }

  // it is an experimental metric and shows very volatile value
  // normalized precision
  function vaultPpfsLastApr(address _vault) external view returns (uint256){
    IBookkeeper.PpfsChange memory lastPpfsChange = IBookkeeper(bookkeeper()).lastPpfsChange(_vault);
    // skip fresh vault
    if (lastPpfsChange.time == 0) {
      return 0;
    }
    return normalizePrecision(computePpfsApr(
        lastPpfsChange.value,
        lastPpfsChange.oldValue,
        lastPpfsChange.time,
        lastPpfsChange.oldTime
      ), vaultDecimals(_vault));
  }

  function computePpfsApr(uint256 ppfs, uint256 startPpfs, uint256 curTime, uint256 startTime)
  internal pure returns (uint256) {
    if (ppfs <= startPpfs) {
      return 0;
    }
    uint256 ppfsChange = ppfs.sub(startPpfs);
    uint256 timeChange = Math.max(curTime.sub(startTime), 1);
    return ppfsChange.mul(PRECISION).div(timeChange)
    .mul(uint256(1 days * 365)).mul(uint256(100)).div(PRECISION);
  }

  // no decimals
  function strategyCreated(address _strategy) public view returns (uint256){
    return Controllable(_strategy).created();
  }

  function strategyPlatform(address _strategy) public view returns (IStrategy.Platform){
    return IStrategy(_strategy).platform();
  }

  function strategyAssets(address _strategy) public view returns (address[] memory){
    return IStrategy(_strategy).assets();
  }

  function strategyRewardTokens(address _strategy) public view returns (address[] memory){
    return IStrategy(_strategy).rewardTokens();
  }

  function strategyPausedInvesting(address _strategy) public view returns (bool){
    return IStrategy(_strategy).pausedInvesting();
  }

  // normalized precision
  function strategyEarned(address _strategy) public view returns (uint256){
    address targetToken = IController(controller()).rewardToken();
    return normalizePrecision(
      IBookkeeper(bookkeeper()).targetTokenEarned(_strategy),
      ERC20(targetToken).decimals()
    );
  }

  // normalized precision
  function userUnderlyingBalance(address _user, address _vault) public view returns (uint256) {
    return normalizePrecision(IERC20(vaultUnderlying(_vault)).balanceOf(_user), vaultDecimals(_vault));
  }

  // normalized precision
  function userUnderlyingBalanceUsdc(address _user, address _vault) public view returns (uint256) {
    uint256 underlyingPrice = getPrice(vaultUnderlying(_vault));
    return userUnderlyingBalance(_user, _vault).mul(underlyingPrice).div(PRECISION);
  }

  // normalized precision
  function userDepositedUnderlying(address _user, address _vault) public view returns (uint256) {
    return normalizePrecision(
      ISmartVault(_vault).underlyingBalanceWithInvestmentForHolder(_user),
      vaultDecimals(_vault)
    );
  }

  function userDepositedUnderlyingUsdc(address _user, address _vault)
  public view returns (uint256) {
    uint256 underlyingPrice = getPrice(vaultUnderlying(_vault));
    return userDepositedUnderlying(_user, _vault).mul(underlyingPrice).div(PRECISION);
  }

  // normalized precision
  function userDepositedShare(address _user, address _vault) public view returns (uint256) {
    return normalizePrecision(IERC20(_vault).balanceOf(_user), vaultDecimals(_vault));
  }

  // normalized precision
  function userRewards(address _user, address _vault) public view returns (uint256[] memory) {
    address[] memory rewardTokens = ISmartVault(_vault).rewardTokens();
    uint256[] memory rewards = new uint256[](rewardTokens.length);
    for (uint256 i = 0; i < rewardTokens.length; i++) {
      rewards[i] = normalizePrecision(
        ISmartVault(_vault).earned(rewardTokens[i], _user),
        ERC20(rewardTokens[i]).decimals()
      );
    }
    return rewards;
  }

  // normalized precision
  function userRewardsBoost(address _user, address _vault) public view returns (uint256[] memory) {
    address[] memory rewardTokens = ISmartVault(_vault).rewardTokens();
    uint256[] memory rewards = new uint256[](rewardTokens.length);
    for (uint256 i = 0; i < rewardTokens.length; i++) {
      rewards[i] = normalizePrecision(
        ISmartVault(_vault).earnedWithBoost(rewardTokens[i], _user),
        ERC20(rewardTokens[i]).decimals()
      );
    }
    return rewards;
  }

  // normalized precision
  function userRewardsUsdc(address _user, address _vault) public view returns (uint256[] memory) {
    address[] memory rewardTokens = ISmartVault(_vault).rewardTokens();
    uint256[] memory rewards = new uint256[](rewardTokens.length);
    for (uint256 i = 0; i < rewardTokens.length; i++) {
      uint256 price = getPrice(rewardTokens[i]);
      rewards[i] = normalizePrecision(
        ISmartVault(_vault).earned(rewardTokens[i], _user).mul(price).div(PRECISION),
        ERC20(rewardTokens[i]).decimals()
      );
    }
    return rewards;
  }

  // normalized precision
  function userRewardsBoostUsdc(address _user, address _vault) public view returns (uint256[] memory) {
    address[] memory rewardTokens = ISmartVault(_vault).rewardTokens();
    uint256[] memory rewards = new uint256[](rewardTokens.length);
    for (uint256 i = 0; i < rewardTokens.length; i++) {
      uint256 price = getPrice(rewardTokens[i]);
      rewards[i] = normalizePrecision(
        ISmartVault(_vault).earnedWithBoost(rewardTokens[i], _user).mul(price).div(PRECISION),
        ERC20(rewardTokens[i]).decimals()
      );
    }
    return rewards;
  }

  function vaults() public view returns (address[] memory){
    return IBookkeeper(bookkeeper()).vaults();
  }

  function vaultsLength() public view returns (uint256){
    return IBookkeeper(bookkeeper()).vaults().length;
  }

  function strategies() public view returns (address[] memory){
    return IBookkeeper(bookkeeper()).strategies();
  }

  function strategiesLength() public view returns (uint256){
    return IBookkeeper(bookkeeper()).strategies().length;
  }

  function priceCalculator() public view returns (address) {
    return tools[keccak256(abi.encodePacked("calculator"))];
  }

  function bookkeeper() public view returns (address) {
    return IController(controller()).bookkeeper();
  }

  // normalized precision
  //noinspection NoReturn
  function getPrice(address _token) public view returns (uint256) {
    //slither-disable-next-line unused-return,variable-scope,uninitialized-local
    try IPriceCalculator(priceCalculator()).getPriceWithDefaultOutput(_token) returns (uint256 price){
      return price;
    } catch {
      return 0;
    }
  }

  function normalizePrecision(uint256 amount, uint256 decimals) internal pure returns (uint256){
    return amount.mul(PRECISION).div(10 ** decimals);
  }

  // *********** GOVERNANCE ACTIONS *****************

  function setPriceCalculator(address newValue) external onlyControllerOrGovernance {
    tools[keccak256(abi.encodePacked("calculator"))] = newValue;
    emit ToolAddressUpdated(newValue);
  }

}

File 2 of 15 : Initializable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }
}

File 3 of 15 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

File 4 of 15 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

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

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

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

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

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

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 5 of 15 : Math.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a / b + (a % b == 0 ? 0 : 1);
    }
}

File 6 of 15 : Controllable.sol
// SPDX-License-Identifier: ISC
/**
* By using this software, you understand, acknowledge and accept that Tetu
* and/or the underlying software are provided “as is” and “as available”
* basis and without warranties or representations of any kind either expressed
* or implied. Any use of this open source software released under the ISC
* Internet Systems Consortium license is done at your own risk to the fullest
* extent permissible pursuant to applicable law any and all liability as well
* as all warranties, including any fitness for a particular purpose with respect
* to Tetu and/or the underlying software and the use thereof are disclaimed.
*/

pragma solidity 0.8.4;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../interface/IController.sol";
import "../interface/IControllable.sol";

/// @title Implement basic functionality for any contract that require strict control
/// @dev Can be used with upgradeable pattern.
///      Require call initializeControllable() in any case.
/// @author belbix
abstract contract Controllable is Initializable, IControllable {
  bytes32 internal constant _CONTROLLER_SLOT = 0x5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3617;
  bytes32 internal constant _CREATED_SLOT = 0x6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8a;

  /// @notice Controller address changed
  event UpdateController(address oldValue, address newValue);

  constructor() {
    assert(_CONTROLLER_SLOT == bytes32(uint256(keccak256("eip1967.controllable.controller")) - 1));
    assert(_CREATED_SLOT == bytes32(uint256(keccak256("eip1967.controllable.created")) - 1));
  }

  /// @notice Initialize contract after setup it as proxy implementation
  ///         Save block.timestamp in the "created" variable
  /// @dev Use it only once after first logic setup
  /// @param _controller Controller address
  function initializeControllable(address _controller) public initializer {
    setController(_controller);
    setCreated(block.timestamp);
  }

  function isController(address _adr) public override view returns (bool) {
    return _adr == controller();
  }

  /// @notice Return true is given address is setup as governance in Controller
  /// @param _adr Address for check
  /// @return true if given address is governance
  function isGovernance(address _adr) public override view returns (bool) {
    return IController(controller()).governance() == _adr;
  }

  // ************ MODIFIERS **********************

  /// @dev Allow operation only for Controller
  modifier onlyController() {
    require(controller() == msg.sender, "not controller");
    _;
  }

  /// @dev Allow operation only for Controller or Governance
  modifier onlyControllerOrGovernance() {
    require(isController(msg.sender) || isGovernance(msg.sender), "not controller or gov");
    _;
  }

  /// @dev Only smart contracts will be affected by this modifier
  ///      If it is a contract it should be whitelisted
  modifier onlyAllowedUsers() {
    require(IController(controller()).isAllowedUser(msg.sender), "not allowed");
    _;
  }

  /// @dev Only Reward Distributor allowed. Governance is Reward Distributor by default.
  modifier onlyRewardDistribution() {
    require(IController(controller()).isRewardDistributor(msg.sender), "only distr");
    _;
  }

  // ************* SETTERS/GETTERS *******************

  /// @notice Return controller address saved in the contract slot
  /// @return adr Controller address
  function controller() public view returns (address adr) {
    bytes32 slot = _CONTROLLER_SLOT;
    assembly {
      adr := sload(slot)
    }
  }

  /// @dev Set a controller address to contract slot
  /// @param _newController Controller address
  function setController(address _newController) internal {
    require(_newController != address(0), "zero address");
    emit UpdateController(controller(), _newController);
    bytes32 slot = _CONTROLLER_SLOT;
    assembly {
      sstore(slot, _newController)
    }
  }

  /// @notice Return creation timestamp
  /// @return ts Creation timestamp
  function created() external view returns (uint256 ts) {
    bytes32 slot = _CREATED_SLOT;
    assembly {
      ts := sload(slot)
    }
  }

  /// @dev Filled only once when contract initialized
  /// @param _created block.timestamp
  function setCreated(uint256 _created) private {
    bytes32 slot = _CREATED_SLOT;
    assembly {
      sstore(slot, _created)
    }
  }

}

File 7 of 15 : IBookkeeper.sol
// SPDX-License-Identifier: ISC
/**
* By using this software, you understand, acknowledge and accept that Tetu
* and/or the underlying software are provided “as is” and “as available”
* basis and without warranties or representations of any kind either expressed
* or implied. Any use of this open source software released under the ISC
* Internet Systems Consortium license is done at your own risk to the fullest
* extent permissible pursuant to applicable law any and all liability as well
* as all warranties, including any fitness for a particular purpose with respect
* to Tetu and/or the underlying software and the use thereof are disclaimed.
*/

pragma solidity 0.8.4;

interface IBookkeeper {

  struct PpfsChange {
    address vault;
    uint256 block;
    uint256 time;
    uint256 value;
    uint256 oldBlock;
    uint256 oldTime;
    uint256 oldValue;
  }

  struct HardWork {
    address strategy;
    uint256 block;
    uint256 time;
    uint256 targetTokenAmount;
  }

  function addVault(address _vault) external;

  function addStrategy(address _strategy) external;

  function registerStrategyEarned(uint256 _targetTokenAmount) external;

  function registerFundKeeperEarned(address _token, uint256 _fundTokenAmount) external;

  function registerUserAction(address _user, uint256 _amount, bool _deposit) external;

  function registerVaultTransfer(address from, address to, uint256 amount) external;

  function registerUserEarned(address _user, address _vault, address _rt, uint256 _amount) external;

  function registerPpfsChange(address vault, uint256 value) external;

  function registerRewardDistribution(address vault, address token, uint256 amount) external;

  function vaults() external view returns (address[] memory);

  function vaultsLength() external view returns (uint256);

  function strategies() external view returns (address[] memory);

  function strategiesLength() external view returns (uint256);

  function lastPpfsChange(address vault) external view returns (PpfsChange memory);

  /// @notice Return total earned TETU tokens for strategy
  /// @dev Should be incremented after strategy rewards distribution
  /// @param strategy Strategy address
  /// @return Earned TETU tokens
  function targetTokenEarned(address strategy) external view returns (uint256);

  /// @notice Return share(xToken) balance of given user
  /// @dev Should be calculated for each xToken transfer
  /// @param vault Vault address
  /// @param user User address
  /// @return User share (xToken) balance
  function vaultUsersBalances(address vault, address user) external view returns (uint256);

  /// @notice Return earned token amount for given token and user
  /// @dev Fills when user claim rewards
  /// @param user User address
  /// @param vault Vault address
  /// @param token Token address
  /// @return User's earned tokens amount
  function userEarned(address user, address vault, address token) external view returns (uint256);

  function lastHardWork(address vault) external view returns (HardWork memory);

  /// @notice Return users quantity for given Vault
  /// @dev Calculation based in Bookkeeper user balances
  /// @param vault Vault address
  /// @return Users quantity
  function vaultUsersQuantity(address vault) external view returns (uint256);

  function fundKeeperEarned(address vault) external view returns (uint256);

  function vaultRewards(address vault, address token, uint256 idx) external view returns (uint256);

  function vaultRewardsLength(address vault, address token) external view returns (uint256);

  function strategyEarnedSnapshots(address strategy, uint256 idx) external view returns (uint256);

  function strategyEarnedSnapshotsTime(address strategy, uint256 idx) external view returns (uint256);

  function strategyEarnedSnapshotsLength(address strategy) external view returns (uint256);
}

File 8 of 15 : ISmartVault.sol
// SPDX-License-Identifier: ISC
/**
* By using this software, you understand, acknowledge and accept that Tetu
* and/or the underlying software are provided “as is” and “as available”
* basis and without warranties or representations of any kind either expressed
* or implied. Any use of this open source software released under the ISC
* Internet Systems Consortium license is done at your own risk to the fullest
* extent permissible pursuant to applicable law any and all liability as well
* as all warranties, including any fitness for a particular purpose with respect
* to Tetu and/or the underlying software and the use thereof are disclaimed.
*/

pragma solidity 0.8.4;

interface ISmartVault {

  function setStrategy(address _strategy) external;

  function changeActivityStatus(bool _active) external;

  function changeProtectionMode(bool _active) external;

  function changePpfsDecreaseAllowed(bool _value) external;

  function setLockPeriod(uint256 _value) external;

  function setLockPenalty(uint256 _value) external;

  function setToInvest(uint256 _value) external;

  function doHardWork() external;

  function rebalance() external;

  function disableLock() external;

  function notifyTargetRewardAmount(address _rewardToken, uint256 reward) external;

  function notifyRewardWithoutPeriodChange(address _rewardToken, uint256 reward) external;

  function deposit(uint256 amount) external;

  function depositAndInvest(uint256 amount) external;

  function depositFor(uint256 amount, address holder) external;

  function withdraw(uint256 numberOfShares) external;

  function exit() external;

  function getAllRewards() external;

  function getReward(address rt) external;

  function underlying() external view returns (address);

  function strategy() external view returns (address);

  function getRewardTokenIndex(address rt) external view returns (uint256);

  function getPricePerFullShare() external view returns (uint256);

  function underlyingUnit() external view returns (uint256);

  function duration() external view returns (uint256);

  function underlyingBalanceInVault() external view returns (uint256);

  function underlyingBalanceWithInvestment() external view returns (uint256);

  function underlyingBalanceWithInvestmentForHolder(address holder) external view returns (uint256);

  function availableToInvestOut() external view returns (uint256);

  function earned(address rt, address account) external view returns (uint256);

  function earnedWithBoost(address rt, address account) external view returns (uint256);

  function rewardPerToken(address rt) external view returns (uint256);

  function lastTimeRewardApplicable(address rt) external view returns (uint256);

  function rewardTokensLength() external view returns (uint256);

  function active() external view returns (bool);

  function rewardTokens() external view returns (address[] memory);

  function periodFinishForToken(address _rt) external view returns (uint256);

  function rewardRateForToken(address _rt) external view returns (uint256);

  function lastUpdateTimeForToken(address _rt) external view returns (uint256);

  function rewardPerTokenStoredForToken(address _rt) external view returns (uint256);

  function userRewardPerTokenPaidForToken(address _rt, address account) external view returns (uint256);

  function rewardsForToken(address _rt, address account) external view returns (uint256);

  function userLastWithdrawTs(address _user) external returns (uint256);

  function userLastDepositTs(address _user) external returns (uint256);

  function userBoostTs(address _user) external returns (uint256);

  function userLockTs(address _user) external returns (uint256);

  function addRewardToken(address rt) external;

  function removeRewardToken(address rt) external;

  function stop() external;

  function ppfsDecreaseAllowed() external view returns (bool);

  function lockPeriod() external view returns (uint256);

  function lockPenalty() external view returns (uint256);

  function toInvest() external view returns (uint256);

  function depositFeeNumerator() external view returns (uint256);

  function lockAllowed() external view returns (bool);

  function protectionMode() external view returns (bool);
}

File 9 of 15 : IStrategy.sol
// SPDX-License-Identifier: ISC
/**
* By using this software, you understand, acknowledge and accept that Tetu
* and/or the underlying software are provided “as is” and “as available”
* basis and without warranties or representations of any kind either expressed
* or implied. Any use of this open source software released under the ISC
* Internet Systems Consortium license is done at your own risk to the fullest
* extent permissible pursuant to applicable law any and all liability as well
* as all warranties, including any fitness for a particular purpose with respect
* to Tetu and/or the underlying software and the use thereof are disclaimed.
*/

pragma solidity 0.8.4;

interface IStrategy {

  enum Platform {
    UNKNOWN, // 0
    TETU, // 1
    QUICK, // 2
    SUSHI, // 3
    WAULT, // 4
    IRON, // 5
    COSMIC, // 6
    CURVE, // 7
    DINO, // 8
    IRON_LEND, // 9
    HERMES, // 10
    CAFE, // 11
    TETU_SWAP, // 12
    SPOOKY, // 13
    AAVE_LEND, //14
    AAVE_MAI_BAL, // 15
    GEIST, //16
    HARVEST, //17
    SCREAM_LEND, //18
    KLIMA //19
  }

  // *************** GOVERNANCE ACTIONS **************
  function STRATEGY_NAME() external view returns (string memory);

  function withdrawAllToVault() external;

  function withdrawToVault(uint256 amount) external;

  function salvage(address recipient, address token, uint256 amount) external;

  function doHardWork() external;

  function investAllUnderlying() external;

  function emergencyExit() external;

  function pauseInvesting() external;

  function continueInvesting() external;

  // **************** VIEWS ***************
  function rewardTokens() external view returns (address[] memory);

  function underlying() external view returns (address);

  function underlyingBalance() external view returns (uint256);

  function rewardPoolBalance() external view returns (uint256);

  function buyBackRatio() external view returns (uint256);

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

  function vault() external view returns (address);

  function investedUnderlyingBalance() external view returns (uint256);

  function platform() external view returns (Platform);

  function assets() external view returns (address[] memory);

  function pausedInvesting() external view returns (bool);

  function readyToClaim() external view returns (uint256[] memory);

  function poolTotalAmount() external view returns (uint256);
}

File 10 of 15 : IPriceCalculator.sol
// SPDX-License-Identifier: ISC
/**
* By using this software, you understand, acknowledge and accept that Tetu
* and/or the underlying software are provided “as is” and “as available”
* basis and without warranties or representations of any kind either expressed
* or implied. Any use of this open source software released under the ISC
* Internet Systems Consortium license is done at your own risk to the fullest
* extent permissible pursuant to applicable law any and all liability as well
* as all warranties, including any fitness for a particular purpose with respect
* to Tetu and/or the underlying software and the use thereof are disclaimed.
*/

pragma solidity 0.8.4;

interface IPriceCalculator {

  function getPrice(address token, address outputToken) external view returns (uint256);

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

  function getLargestPool(address token, address[] memory usedLps) external view returns (address, uint256, address);

  function getPriceFromLp(address lpAddress, address token) external view returns (uint256);

}

File 11 of 15 : 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 12 of 15 : 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 13 of 15 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

File 14 of 15 : IController.sol
// SPDX-License-Identifier: ISC
/**
* By using this software, you understand, acknowledge and accept that Tetu
* and/or the underlying software are provided “as is” and “as available”
* basis and without warranties or representations of any kind either expressed
* or implied. Any use of this open source software released under the ISC
* Internet Systems Consortium license is done at your own risk to the fullest
* extent permissible pursuant to applicable law any and all liability as well
* as all warranties, including any fitness for a particular purpose with respect
* to Tetu and/or the underlying software and the use thereof are disclaimed.
*/

pragma solidity 0.8.4;

interface IController {

  function addVaultsAndStrategies(address[] memory _vaults, address[] memory _strategies) external;

  function addStrategy(address _strategy) external;

  function governance() external view returns (address);

  function dao() external view returns (address);

  function bookkeeper() external view returns (address);

  function feeRewardForwarder() external view returns (address);

  function mintHelper() external view returns (address);

  function rewardToken() external view returns (address);

  function fundToken() external view returns (address);

  function psVault() external view returns (address);

  function fund() external view returns (address);

  function distributor() external view returns (address);

  function announcer() external view returns (address);

  function vaultController() external view returns (address);

  function whiteList(address _target) external view returns (bool);

  function vaults(address _target) external view returns (bool);

  function strategies(address _target) external view returns (bool);

  function psNumerator() external view returns (uint256);

  function psDenominator() external view returns (uint256);

  function fundNumerator() external view returns (uint256);

  function fundDenominator() external view returns (uint256);

  function isAllowedUser(address _adr) external view returns (bool);

  function isDao(address _adr) external view returns (bool);

  function isHardWorker(address _adr) external view returns (bool);

  function isRewardDistributor(address _adr) external view returns (bool);

  function isPoorRewardConsumer(address _adr) external view returns (bool);

  function isValidVault(address _vault) external view returns (bool);

  function isValidStrategy(address _strategy) external view returns (bool);

  // ************ DAO ACTIONS *************
  function setPSNumeratorDenominator(uint256 numerator, uint256 denominator) external;

  function setFundNumeratorDenominator(uint256 numerator, uint256 denominator) external;

  function changeWhiteListStatus(address[] calldata _targets, bool status) external;
}

File 15 of 15 : IControllable.sol
// SPDX-License-Identifier: ISC
/**
* By using this software, you understand, acknowledge and accept that Tetu
* and/or the underlying software are provided “as is” and “as available”
* basis and without warranties or representations of any kind either expressed
* or implied. Any use of this open source software released under the ISC
* Internet Systems Consortium license is done at your own risk to the fullest
* extent permissible pursuant to applicable law any and all liability as well
* as all warranties, including any fitness for a particular purpose with respect
* to Tetu and/or the underlying software and the use thereof are disclaimed.
*/

pragma solidity 0.8.4;

interface IControllable {

  function isController(address _contract) external view returns (bool);

  function isGovernance(address _contract) external view returns (bool);
}

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

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newValue","type":"address"}],"name":"ToolAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldValue","type":"address"},{"indexed":false,"internalType":"address","name":"newValue","type":"address"}],"name":"UpdateController","type":"event"},{"inputs":[],"name":"PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bookkeeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tvl","type":"uint256"},{"internalType":"uint256","name":"rewards","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"computeApr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"rt","type":"address"}],"name":"computeRewardApr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address","name":"adr","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"created","outputs":[{"internalType":"uint256","name":"ts","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_controller","type":"address"},{"internalType":"address","name":"_calculator","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_controller","type":"address"}],"name":"initializeControllable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_adr","type":"address"}],"name":"isController","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_adr","type":"address"}],"name":"isGovernance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceCalculator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newValue","type":"address"}],"name":"setPriceCalculator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strategies","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategiesLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"strategyAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"strategyCreated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"strategyEarned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"strategyPausedInvesting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"strategyPlatform","outputs":[{"internalType":"enum IStrategy.Platform","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"strategyRewardTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tetuTokenValues","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_vaults","type":"address[]"}],"name":"totalTetuBoughBack","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_strategies","type":"address[]"}],"name":"totalTetuBoughBack2","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_vaults","type":"address[]"}],"name":"totalTvlUsdc","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_vaults","type":"address[]"}],"name":"totalUsers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUsersForAllVaults","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userDepositedShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userDepositedUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userDepositedUnderlyingUsdc","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userInfo","outputs":[{"components":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"underlyingBalance","type":"uint256"},{"internalType":"uint256","name":"underlyingBalanceUsdc","type":"uint256"},{"internalType":"uint256","name":"depositedUnderlying","type":"uint256"},{"internalType":"uint256","name":"depositedUnderlyingUsdc","type":"uint256"},{"internalType":"uint256","name":"depositedShare","type":"uint256"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewards","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardsUsdc","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardsBoost","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardsBoostUsdc","type":"uint256[]"}],"internalType":"struct ContractReader.UserInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userInfoLight","outputs":[{"components":[{"internalType":"uint256","name":"depositedUnderlying","type":"uint256"},{"internalType":"uint256","name":"depositedUnderlyingUsdc","type":"uint256"},{"internalType":"uint256","name":"depositedShare","type":"uint256"}],"internalType":"struct ContractReader.UserInfoLight","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address[]","name":"_vaults","type":"address[]"}],"name":"userInfosLight","outputs":[{"components":[{"internalType":"uint256","name":"depositedUnderlying","type":"uint256"},{"internalType":"uint256","name":"depositedUnderlyingUsdc","type":"uint256"},{"internalType":"uint256","name":"depositedShare","type":"uint256"}],"internalType":"struct ContractReader.UserInfoLight[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userRewards","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userRewardsBoost","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userRewardsBoostUsdc","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userRewardsUsdc","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userUnderlyingBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"name":"userUnderlyingBalanceUsdc","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultCreated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultDecimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"vaultInfo","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"tvl","type":"uint256"},{"internalType":"uint256","name":"tvlUsdc","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardTokensBal","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardTokensBalUsdc","type":"uint256[]"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256[]","name":"rewardsApr","type":"uint256[]"},{"internalType":"uint256","name":"ppfsApr","type":"uint256"},{"internalType":"uint256","name":"users","type":"uint256"},{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"strategyCreated","type":"uint256"},{"internalType":"enum IStrategy.Platform","name":"platform","type":"uint8"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address[]","name":"strategyRewards","type":"address[]"},{"internalType":"bool","name":"strategyOnPause","type":"bool"},{"internalType":"uint256","name":"earned","type":"uint256"}],"internalType":"struct ContractReader.VaultInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"vaultInfoLight","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"tvl","type":"uint256"},{"internalType":"uint256","name":"tvlUsdc","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardsApr","type":"uint256[]"},{"internalType":"uint256","name":"ppfsApr","type":"uint256"},{"internalType":"enum IStrategy.Platform","name":"platform","type":"uint8"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"earned","type":"uint256"}],"internalType":"struct ContractReader.VaultInfoLight","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_vaults","type":"address[]"}],"name":"vaultInfos","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"tvl","type":"uint256"},{"internalType":"uint256","name":"tvlUsdc","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardTokensBal","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardTokensBalUsdc","type":"uint256[]"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256[]","name":"rewardsApr","type":"uint256[]"},{"internalType":"uint256","name":"ppfsApr","type":"uint256"},{"internalType":"uint256","name":"users","type":"uint256"},{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"strategyCreated","type":"uint256"},{"internalType":"enum IStrategy.Platform","name":"platform","type":"uint8"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address[]","name":"strategyRewards","type":"address[]"},{"internalType":"bool","name":"strategyOnPause","type":"bool"},{"internalType":"uint256","name":"earned","type":"uint256"}],"internalType":"struct ContractReader.VaultInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_vaults","type":"address[]"}],"name":"vaultInfosLight","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"tvl","type":"uint256"},{"internalType":"uint256","name":"tvlUsdc","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardsApr","type":"uint256[]"},{"internalType":"uint256","name":"ppfsApr","type":"uint256"},{"internalType":"enum IStrategy.Platform","name":"platform","type":"uint8"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"earned","type":"uint256"}],"internalType":"struct ContractReader.VaultInfoLight[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultPlatform","outputs":[{"internalType":"enum IStrategy.Platform","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultPpfs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultPpfsApr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultPpfsLastApr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultRewardTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultRewardTokensBal","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultRewardTokensBalUsdc","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultRewardsApr","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultTvl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultTvlUsdc","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultUnderlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultUsers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"page","type":"uint256"},{"internalType":"uint256","name":"pageSize","type":"uint256"}],"name":"vaultWithUserInfoPages","outputs":[{"components":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"tvl","type":"uint256"},{"internalType":"uint256","name":"tvlUsdc","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardTokensBal","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardTokensBalUsdc","type":"uint256[]"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256[]","name":"rewardsApr","type":"uint256[]"},{"internalType":"uint256","name":"ppfsApr","type":"uint256"},{"internalType":"uint256","name":"users","type":"uint256"},{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"strategyCreated","type":"uint256"},{"internalType":"enum IStrategy.Platform","name":"platform","type":"uint8"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address[]","name":"strategyRewards","type":"address[]"},{"internalType":"bool","name":"strategyOnPause","type":"bool"},{"internalType":"uint256","name":"earned","type":"uint256"}],"internalType":"struct ContractReader.VaultInfo","name":"vault","type":"tuple"},{"components":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"underlyingBalance","type":"uint256"},{"internalType":"uint256","name":"underlyingBalanceUsdc","type":"uint256"},{"internalType":"uint256","name":"depositedUnderlying","type":"uint256"},{"internalType":"uint256","name":"depositedUnderlyingUsdc","type":"uint256"},{"internalType":"uint256","name":"depositedShare","type":"uint256"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewards","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardsUsdc","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardsBoost","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardsBoostUsdc","type":"uint256[]"}],"internalType":"struct ContractReader.UserInfo","name":"user","type":"tuple"}],"internalType":"struct ContractReader.VaultWithUserInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"page","type":"uint256"},{"internalType":"uint256","name":"pageSize","type":"uint256"}],"name":"vaultWithUserInfoPagesLight","outputs":[{"components":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"tvl","type":"uint256"},{"internalType":"uint256","name":"tvlUsdc","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardsApr","type":"uint256[]"},{"internalType":"uint256","name":"ppfsApr","type":"uint256"},{"internalType":"enum IStrategy.Platform","name":"platform","type":"uint8"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"earned","type":"uint256"}],"internalType":"struct ContractReader.VaultInfoLight","name":"vault","type":"tuple"},{"components":[{"internalType":"uint256","name":"depositedUnderlying","type":"uint256"},{"internalType":"uint256","name":"depositedUnderlyingUsdc","type":"uint256"},{"internalType":"uint256","name":"depositedShare","type":"uint256"}],"internalType":"struct ContractReader.UserInfoLight","name":"user","type":"tuple"}],"internalType":"struct ContractReader.VaultWithUserInfoLight[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address[]","name":"_vaults","type":"address[]"}],"name":"vaultWithUserInfos","outputs":[{"components":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"tvl","type":"uint256"},{"internalType":"uint256","name":"tvlUsdc","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardTokensBal","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardTokensBalUsdc","type":"uint256[]"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256[]","name":"rewardsApr","type":"uint256[]"},{"internalType":"uint256","name":"ppfsApr","type":"uint256"},{"internalType":"uint256","name":"users","type":"uint256"},{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"strategyCreated","type":"uint256"},{"internalType":"enum IStrategy.Platform","name":"platform","type":"uint8"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address[]","name":"strategyRewards","type":"address[]"},{"internalType":"bool","name":"strategyOnPause","type":"bool"},{"internalType":"uint256","name":"earned","type":"uint256"}],"internalType":"struct ContractReader.VaultInfo","name":"vault","type":"tuple"},{"components":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"underlyingBalance","type":"uint256"},{"internalType":"uint256","name":"underlyingBalanceUsdc","type":"uint256"},{"internalType":"uint256","name":"depositedUnderlying","type":"uint256"},{"internalType":"uint256","name":"depositedUnderlyingUsdc","type":"uint256"},{"internalType":"uint256","name":"depositedShare","type":"uint256"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewards","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardsUsdc","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardsBoost","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardsBoostUsdc","type":"uint256[]"}],"internalType":"struct ContractReader.UserInfo","name":"user","type":"tuple"}],"internalType":"struct ContractReader.VaultWithUserInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address[]","name":"_vaults","type":"address[]"}],"name":"vaultWithUserInfosLight","outputs":[{"components":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"tvl","type":"uint256"},{"internalType":"uint256","name":"tvlUsdc","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardsApr","type":"uint256[]"},{"internalType":"uint256","name":"ppfsApr","type":"uint256"},{"internalType":"enum IStrategy.Platform","name":"platform","type":"uint8"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"earned","type":"uint256"}],"internalType":"struct ContractReader.VaultInfoLight","name":"vault","type":"tuple"},{"components":[{"internalType":"uint256","name":"depositedUnderlying","type":"uint256"},{"internalType":"uint256","name":"depositedUnderlyingUsdc","type":"uint256"},{"internalType":"uint256","name":"depositedShare","type":"uint256"}],"internalType":"struct ContractReader.UserInfoLight","name":"user","type":"tuple"}],"internalType":"struct ContractReader.VaultWithUserInfoLight[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaults","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b506200003f60017f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3618620000ec565b7f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c3617146200007c57634e487b7160e01b600052600160045260246000fd5b620000a960017f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8b620000ec565b7f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8a14620000e657634e487b7160e01b600052600160045260246000fd5b62000110565b6000828210156200010b57634e487b7160e01b81526011600452602481fd5b500390565b614cc280620001206000396000f3fe608060405234801561001057600080fd5b50600436106103f15760003560e01c80638b95e33511610215578063d9f9027f11610125578063e527e2c8116100b8578063ee98aa5f11610087578063ee98aa5f14610910578063f2dc7ff514610923578063f4754e0114610669578063f77c479114610936578063ffa1ad741461093e57600080fd5b8063e527e2c8146108cf578063e77fb0d8146108e2578063e945cdcd146108ea578063ebee0406146108fd57600080fd5b8063df0ac912116100f4578063df0ac91214610896578063e2710b89146108a9578063e3d3b66d146108bc578063e410e4a61461087057600080fd5b8063d9f9027f14610848578063db84991d14610850578063dc54c27614610870578063dee1f0e41461088357600080fd5b8063a90f55bf116101a8578063b429afeb11610177578063b429afeb146107dc578063bab6929c146107ef578063be1e27d114610802578063c376dbb314610815578063d0d026331461082857600080fd5b8063a90f55bf14610787578063a980356a146107a7578063aaf5eb68146107ba578063ab1b491c146107c957600080fd5b80639c5d61b4116101e45780639c5d61b41461072e578063a179751b14610741578063a689cbc314610761578063a80a5ce71461077457600080fd5b80638b95e335146106e05780638d852391146106e85780639164359a146106fb57806396ef1b981461071b57600080fd5b80634acd7390116103105780636f8d34f0116102a35780638170bbd5116102725780638170bbd5146106a25780638220ef5b146106aa57806382af54c9146106b2578063849efb71146106ba5780638adab464146106cd57600080fd5b80636f8d34f014610646578063728204e41461066957806372a4aa711461067c5780637e52586c1461068f57600080fd5b8063514b9ec7116102df578063514b9ec7146105e05780636430be8c146106005780636922d7b6146106135780636d5f9b331461062657600080fd5b80634acd7390146105945780634d18e43d146105a75780634e58d37e146105ba5780634f8f0eb0146105cd57600080fd5b8063325a19f1116103885780634624aa44116103575780634624aa441461052a57806347b03bba1461054a578063485cc9551461055f57806349c997ce1461057457600080fd5b8063325a19f1146104d55780633f74bb89146104fc5780634135b6731461050457806341976e091461051757600080fd5b80631184006e116103c45780631184006e1461046f57806319babe8b1461048f57806324d1efcf146104a257806331bd6e2c146104b557600080fd5b806308dc993c146103f65780630bcc41df1461041c5780630f208beb1461042f5780631041cc641461044f575b600080fd5b610409610404366004613f56565b610962565b6040519081526020015b60405180910390f35b61040961042a366004614047565b610ae5565b61044261043d366004613f8e565b610bb3565b6040516104139190614a18565b61046261045d366004613f56565b610e37565b604051610413919061473f565b61048261047d366004613f8e565b610eae565b6040516104139190614975565b61040961049d366004613f56565b6110d8565b6104096104b0366004613f8e565b61110e565b6104c86104c3366004614047565b611141565b6040516104139190614815565b7f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8a54610409565b610409611222565b610409610512366004613f8e565b6112a6565b610409610525366004613f56565b611335565b61053d610538366004613f56565b6113be565b6040516104139190614a2b565b610552611521565b6040516104139190614711565b61057261056d366004613f8e565b6115a0565b005b610587610582366004613fc6565b61168d565b6040516104139190614752565b6104096105a2366004613f56565b611793565b6104096105b5366004613f56565b611806565b6104096105c8366004614047565b6118a2565b6104826105db366004613f8e565b6118fe565b6105f36105ee366004614013565b611b24565b6040516104139190614869565b61040961060e366004613f8e565b611d22565b610572610621366004613f56565b611d53565b610639610634366004613f56565b611e56565b6040516104139190614996565b610659610654366004613f56565b611ecd565b6040519015158152602001610413565b610409610677366004613f56565b611f40565b61040961068a366004613f8e565b611f7b565b61048261069d366004613f8e565b611fb4565b610482612137565b610462612302565b610409612380565b6104096106c8366004613f56565b6123c2565b6106596106db366004613f56565b61240f565b61055261244a565b6104096106f6366004614047565b6124a4565b61070e610709366004613f56565b612500565b6040516104139190614a3e565b610482610729366004613f56565b6126e8565b61040961073c366004613f56565b61291a565b61075461074f366004613f56565b6129e0565b6040516104139190614988565b61075461076f366004613f56565b612a53565b610409610782366004613f56565b612afe565b61079a610795366004613fc6565b612b3c565b60405161041391906148f1565b6104826107b5366004613f8e565b612c54565b610409670de0b6b3a764000081565b6105526107d7366004613f56565b612d9f565b6106596107ea366004613f56565b612e12565b6104096107fd366004613f8e565b612e37565b610409610810366004613f56565b612e62565b610409610823366004613f56565b612ea0565b61083b610836366004614047565b612f1c565b60405161041391906147b4565b610462612ff0565b61086361085e366004613f8e565b613032565b60405161041391906149f7565b61046261087e366004613f56565b613092565b610659610891366004613f56565b6130cd565b61079a6108a4366004614013565b613161565b6105f36108b7366004613fc6565b61332f565b6104096108ca36600461427c565b61343a565b6104096108dd366004614047565b61349b565b6104096134f7565b6105726108f8366004613f56565b613552565b61048261090b366004613f56565b6135f0565b61040961091e366004613f8e565b613763565b610482610931366004613f56565b6139e2565b610552613af6565b610639604051806040016040528060058152602001640c4b8c0b8d60da1b81525081565b60008061096d613af6565b6001600160a01b031663f7c618c16040518163ffffffff1660e01b815260040160206040518083038186803b1580156109a557600080fd5b505afa1580156109b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109dd9190613f72565b9050610ade6109ea611521565b6001600160a01b031663cb618c9d856040518263ffffffff1660e01b8152600401610a159190614711565b60206040518083038186803b158015610a2d57600080fd5b505afa158015610a41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a659190614264565b826001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9e57600080fd5b505afa158015610ab2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad691906142a7565b60ff16613b1b565b9392505050565b600080805b8351811015610bac57610b8e848281518110610b1657634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663a8c62e766040518163ffffffff1660e01b815260040160206040518083038186803b158015610b5657600080fd5b505afa158015610b6a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104049190613f72565b610b989083614aa4565b915080610ba481614c2d565b915050610aea565b5092915050565b610bbb613c91565b6000826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b158015610bf657600080fd5b505afa158015610c0a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c329190810190614079565b9050600081516001600160401b03811115610c5d57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610c86578160200160208202803683370190505b50905060005b8251811015610d7757846001600160a01b031663211dc32d848381518110610cc457634e487b7160e01b600052603260045260246000fd5b6020026020010151886040518363ffffffff1660e01b8152600401610cea929190614725565b60206040518083038186803b158015610d0257600080fd5b505afa158015610d16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3a9190614264565b828281518110610d5a57634e487b7160e01b600052603260045260246000fd5b602090810291909101015280610d6f81614c2d565b915050610c8c565b50604051806101800160405280866001600160a01b03168152602001856001600160a01b03168152602001610dac8787611f7b565b8152602001610dbb8787612e37565b8152602001610dca87876112a6565b8152602001610dd9878761110e565b8152602001610de88787611d22565b8152602001838152602001610dfd8787612c54565b8152602001610e0c8787611fb4565b8152602001610e1b8787610eae565b8152602001610e2a87876118fe565b9052925050505b92915050565b6060816001600160a01b03166371a973056040518163ffffffff1660e01b815260040160006040518083038186803b158015610e7257600080fd5b505afa158015610e86573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e319190810190614079565b60606000826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b158015610eeb57600080fd5b505afa158015610eff573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f279190810190614079565b9050600081516001600160401b03811115610f5257634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610f7b578160200160208202803683370190505b50905060005b82518110156110cf57611092856001600160a01b031663e81bb2d6858481518110610fbc57634e487b7160e01b600052603260045260246000fd5b6020026020010151896040518363ffffffff1660e01b8152600401610fe2929190614725565b60206040518083038186803b158015610ffa57600080fd5b505afa15801561100e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110329190614264565b84838151811061105257634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9e57600080fd5b8282815181106110b257634e487b7160e01b600052603260045260246000fd5b6020908102919091010152806110c781614c2d565b915050610f81565b50949350505050565b6000806110e761052584612d9f565b9050610ade670de0b6b3a76400006111088361110287612afe565b90613b39565b90613b45565b60008061111d61052584612d9f565b9050611139670de0b6b3a76400006111088361110288886112a6565b949350505050565b6060600082516001600160401b0381111561116c57634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156111a557816020015b611192613d04565b81526020019060019003908161118a5790505b50905060005b8351811015610bac576111e48482815181106111d757634e487b7160e01b600052603260045260246000fd5b6020026020010151612500565b82828151811061120457634e487b7160e01b600052603260045260246000fd5b6020026020010181905250808061121a90614c2d565b9150506111ab565b600061122c611521565b6001600160a01b031663d9f9027f6040518163ffffffff1660e01b815260040160006040518083038186803b15801561126457600080fd5b505afa158015611278573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112a09190810190614079565b51905090565b6000610ade826001600160a01b0316638cb1d67f856040518263ffffffff1660e01b81526004016112d79190614711565b60206040518083038186803b1580156112ef57600080fd5b505afa158015611303573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113279190614264565b61133084612ea0565b613b1b565b600061133f61244a565b6001600160a01b0316637539bdb1836040518263ffffffff1660e01b815260040161136a9190614711565b60206040518083038186803b15801561138257600080fd5b505afa9250505080156113b2575060408051601f3d908101601f191682019092526113af91810190614264565b60015b610e3157506000919050565b6113c6613de9565b6000826001600160a01b031663a8c62e766040518163ffffffff1660e01b815260040160206040518083038186803b15801561140157600080fd5b505afa158015611415573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114399190613f72565b90506000604051806101800160405280856001600160a01b0316815260200161146186611f40565b815260200161146f86611ecd565b1515815260200161147f86612afe565b815260200161148d866110d8565b815260200161149b86612d9f565b6001600160a01b031681526020016114b286613092565b81526020016114c0866126e8565b81526020016114ce86611806565b81526020016114dc846129e0565b60138111156114fb57634e487b7160e01b600052602160045260246000fd5b815260200161150984610e37565b815260200161151784610962565b9052949350505050565b600061152b613af6565b6001600160a01b03166347b03bba6040518163ffffffff1660e01b815260040160206040518083038186803b15801561156357600080fd5b505afa158015611577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061159b9190613f72565b905090565b600054610100900460ff16806115b9575060005460ff16155b6115de5760405162461bcd60e51b81526004016115d5906149a9565b60405180910390fd5b600054610100900460ff16158015611600576000805461ffff19166101011790555b61160983613552565b816001600060405160200161162e906931b0b631bab630ba37b960b11b8152600a0190565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055508015611688576000805461ff00191690555b505050565b6060600082516001600160401b038111156116b857634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561170d57816020015b6116fa60405180606001604052806000815260200160008152602001600081525090565b8152602001906001900390816116d65790505b50905060005b835181101561178b5761174d8585838151811061174057634e487b7160e01b600052603260045260246000fd5b6020026020010151613032565b82828151811061176d57634e487b7160e01b600052603260045260246000fd5b6020026020010181905250808061178390614c2d565b915050611713565b509392505050565b6000816001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156117ce57600080fd5b505afa1580156117e2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e319190614264565b6000610e31611327836001600160a01b03166377c7b8fc6040518163ffffffff1660e01b815260040160206040518083038186803b15801561184757600080fd5b505afa15801561185b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187f9190614264565b61188885612ea0565b61189390600a614b1f565b4261189d87611f40565b613b51565b600080805b8351811015610bac576118e08482815181106118d357634e487b7160e01b600052603260045260246000fd5b60200260200101516123c2565b6118ea9083614aa4565b9150806118f681614c2d565b9150506118a7565b60606000826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b15801561193b57600080fd5b505afa15801561194f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119779190810190614079565b9050600081516001600160401b038111156119a257634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156119cb578160200160208202803683370190505b50905060005b82518110156110cf576000611a0c8483815181106119ff57634e487b7160e01b600052603260045260246000fd5b6020026020010151611335565b9050611ae6611ac6670de0b6b3a7640000611108848a6001600160a01b031663e81bb2d68a8981518110611a5057634e487b7160e01b600052603260045260246000fd5b60200260200101518e6040518363ffffffff1660e01b8152600401611a76929190614725565b60206040518083038186803b158015611a8e57600080fd5b505afa158015611aa2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111029190614264565b85848151811061105257634e487b7160e01b600052603260045260246000fd5b838381518110611b0657634e487b7160e01b600052603260045260246000fd5b60209081029190910101525080611b1c81614c2d565b9150506119d1565b60606000611b30612302565b51905060008111611b725760405162461bcd60e51b815260206004820152600c60248201526b656d707479207661756c747360a01b60448201526064016115d5565b6000611b7e8483614abc565b905081611b8b8583614bc7565b1015611b9f5780611b9b81614c2d565b9150505b80851115611bab578094505b6000611bca611bba8688614bc7565b611bc5600186614be6565b613bb6565b90506000611be1611bdb8784614aa4565b85613bb6565b90506000611bef8383614be6565b6001600160401b03811115611c1457634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611c4d57816020015b611c3a613e7d565b815260200190600190039081611c325790505b509050825b82811015611d15576040518060400160405280611c9c611c70612302565b8481518110611c8f57634e487b7160e01b600052603260045260246000fd5b60200260200101516113be565b8152602001611ccc8c611cad612302565b858151811061174057634e487b7160e01b600052603260045260246000fd5b905282611cd98684614be6565b81518110611cf757634e487b7160e01b600052603260045260246000fd5b60200260200101819052508080611d0d90614c2d565b915050611c52565b5098975050505050505050565b6000610ade826001600160a01b03166370a08231856040518263ffffffff1660e01b81526004016112d79190614711565b611d5c33612e12565b80611d6b5750611d6b336130cd565b611daf5760405162461bcd60e51b81526020600482015260156024820152743737ba1031b7b73a3937b63632b91037b91033b7bb60591b60448201526064016115d5565b8060016000604051602001611dd4906931b0b631bab630ba37b960b11b8152600a0190565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f0caadb6ce6adfa207b9ff9c87df2ad8361fe4e2e5c4fb493fba65daa077dc4f481604051611e4b9190614711565b60405180910390a150565b6060816001600160a01b03166306fdde036040518163ffffffff1660e01b815260040160006040518083038186803b158015611e9157600080fd5b505afa158015611ea5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e319190810190614153565b6000816001600160a01b03166302fb0c5e6040518163ffffffff1660e01b815260040160206040518083038186803b158015611f0857600080fd5b505afa158015611f1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e319190614114565b6000816001600160a01b031663325a19f16040518163ffffffff1660e01b815260040160206040518083038186803b1580156117ce57600080fd5b6000610ade611f8983612d9f565b6001600160a01b03166370a08231856040518263ffffffff1660e01b81526004016112d79190614711565b60606000826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b158015611ff157600080fd5b505afa158015612005573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261202d9190810190614079565b9050600081516001600160401b0381111561205857634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612081578160200160208202803683370190505b50905060005b82518110156110cf5760006120b58483815181106119ff57634e487b7160e01b600052603260045260246000fd5b90506120f9611ac6670de0b6b3a7640000611108848a6001600160a01b031663211dc32d8a8981518110611a5057634e487b7160e01b600052603260045260246000fd5b83838151811061211957634e487b7160e01b600052603260045260246000fd5b6020908102919091010152508061212f81614c2d565b915050612087565b606060006121b6612146613af6565b6001600160a01b031663f7c618c16040518163ffffffff1660e01b815260040160206040518083038186803b15801561217e57600080fd5b505afa158015612192573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105259190613f72565b9050600061227a670de0b6b3a7640000611108846121d2613af6565b6001600160a01b031663f7c618c16040518163ffffffff1660e01b815260040160206040518083038186803b15801561220a57600080fd5b505afa15801561221e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122429190613f72565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8e57600080fd5b604080516002808252606082018352929350600092909160208301908036833701905050905082816000815181106122c257634e487b7160e01b600052603260045260246000fd5b60200260200101818152505081816001815181106122f057634e487b7160e01b600052603260045260246000fd5b60209081029190910101529392505050565b606061230c611521565b6001600160a01b0316638220ef5b6040518163ffffffff1660e01b815260040160006040518083038186803b15801561234457600080fd5b505afa158015612358573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261159b9190810190614079565b600061238a611521565b6001600160a01b0316638220ef5b6040518163ffffffff1660e01b815260040160006040518083038186803b15801561126457600080fd5b60006123cc611521565b6001600160a01b031663c14a38e3836040518263ffffffff1660e01b81526004016123f79190614711565b60206040518083038186803b1580156117ce57600080fd5b6000816001600160a01b031663d3df8aa46040518163ffffffff1660e01b815260040160206040518083038186803b158015611f0857600080fd5b600060016000604051602001612470906931b0b631bab630ba37b960b11b8152600a0190565b60408051601f19818403018152918152815160209283012083529082019290925201600020546001600160a01b0316919050565b600080805b8351811015610bac576124e28482815181106124d557634e487b7160e01b600052603260045260246000fd5b60200260200101516110d8565b6124ec9083614aa4565b9150806124f881614c2d565b9150506124a9565b612508613d04565b6000826001600160a01b031663a8c62e766040518163ffffffff1660e01b815260040160206040518083038186803b15801561254357600080fd5b505afa158015612557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257b9190613f72565b90506000604051806102c00160405280856001600160a01b031681526020016125a386611e56565b81526020016125b186611f40565b81526020016125bf86611ecd565b151581526020016125cf86612afe565b81526020016125dd866110d8565b81526020016125eb86612ea0565b81526020016125f986612d9f565b6001600160a01b0316815260200161261086613092565b815260200161261e866139e2565b815260200161262c866135f0565b815260200161263a86611793565b8152602001612648866126e8565b815260200161265686611806565b8152602001612664866123c2565b8152602001836001600160a01b0316815260200161268184611f40565b815260200161268f846129e0565b60138111156126ae57634e487b7160e01b600052602160045260246000fd5b81526020016126bc84610e37565b81526020016126ca84613092565b81526020016126d88461240f565b1515815260200161151784610962565b606060008290506000816001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b15801561272a57600080fd5b505afa15801561273e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526127669190810190614079565b516001600160401b0381111561278c57634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156127b5578160200160208202803683370190505b50905060005b826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b1580156127f457600080fd5b505afa158015612808573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128309190810190614079565b5181101561178b576128dd85846001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b15801561287557600080fd5b505afa158015612889573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128b19190810190614079565b83815181106128d057634e487b7160e01b600052603260045260246000fd5b6020026020010151613763565b8282815181106128fd57634e487b7160e01b600052603260045260246000fd5b60209081029190910101528061291281614c2d565b9150506127bb565b600080612925611521565b6001600160a01b031663400dfebd846040518263ffffffff1660e01b81526004016129509190614711565b60e06040518083038186803b15801561296857600080fd5b505afa15801561297c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129a091906141d9565b90508060400151600014156129b85750600092915050565b610ade6129d782606001518360c0015184604001518560a00151613b51565b61133085612ea0565b6000816001600160a01b0316634bde38c86040518163ffffffff1660e01b815260040160206040518083038186803b158015612a1b57600080fd5b505afa158015612a2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e319190614134565b6000816001600160a01b031663a8c62e766040518163ffffffff1660e01b815260040160206040518083038186803b158015612a8e57600080fd5b505afa158015612aa2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ac69190613f72565b6001600160a01b0316634bde38c86040518163ffffffff1660e01b815260040160206040518083038186803b158015612a1b57600080fd5b6000610e31826001600160a01b0316631bf8e7be6040518163ffffffff1660e01b815260040160206040518083038186803b1580156112ef57600080fd5b6060600082516001600160401b03811115612b6757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612ba057816020015b612b8d613ebe565b815260200190600190039081612b855790505b50905060005b835181101561178b576040518060400160405280612bdd8684815181106111d757634e487b7160e01b600052603260045260246000fd5b8152602001612c1387878581518110612c0657634e487b7160e01b600052603260045260246000fd5b6020026020010151610bb3565b815250828281518110612c3657634e487b7160e01b600052603260045260246000fd5b60200260200101819052508080612c4c90614c2d565b915050612ba6565b60606000826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b158015612c9157600080fd5b505afa158015612ca5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612ccd9190810190614079565b9050600081516001600160401b03811115612cf857634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612d21578160200160208202803683370190505b50905060005b82518110156110cf57612d62856001600160a01b031663211dc32d858481518110610fbc57634e487b7160e01b600052603260045260246000fd5b828281518110612d8257634e487b7160e01b600052603260045260246000fd5b602090810291909101015280612d9781614c2d565b915050612d27565b6000816001600160a01b0316636f307dc36040518163ffffffff1660e01b815260040160206040518083038186803b158015612dda57600080fd5b505afa158015612dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e319190613f72565b6000612e1c613af6565b6001600160a01b0316826001600160a01b0316149050919050565b600080612e4661052584612d9f565b9050611139670de0b6b3a7640000611108836111028888611f7b565b6000610e31826001600160a01b03166377c7b8fc6040518163ffffffff1660e01b815260040160206040518083038186803b1580156112ef57600080fd5b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015612edb57600080fd5b505afa158015612eef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f1391906142a7565b60ff1692915050565b6060600082516001600160401b03811115612f4757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612f8057816020015b612f6d613de9565b815260200190600190039081612f655790505b50905060005b8351811015610bac57612fb2848281518110611c8f57634e487b7160e01b600052603260045260246000fd5b828281518110612fd257634e487b7160e01b600052603260045260246000fd5b60200260200101819052508080612fe890614c2d565b915050612f86565b6060612ffa611521565b6001600160a01b031663d9f9027f6040518163ffffffff1660e01b815260040160006040518083038186803b15801561234457600080fd5b61305660405180606001604052806000815260200160008152602001600081525090565b604051806060016040528061306b85856112a6565b815260200161307a858561110e565b81526020016130898585611d22565b90529392505050565b6060816001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b158015610e7257600080fd5b6000816001600160a01b03166130e1613af6565b6001600160a01b0316635aa6e6756040518163ffffffff1660e01b815260040160206040518083038186803b15801561311957600080fd5b505afa15801561312d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131519190613f72565b6001600160a01b03161492915050565b6060600061316d612302565b519050600081116131af5760405162461bcd60e51b815260206004820152600c60248201526b656d707479207661756c747360a01b60448201526064016115d5565b60006131bb8483614abc565b9050816131c88583614bc7565b10156131dc57806131d881614c2d565b9150505b808511156131e8578094505b60006131f7611bba8688614bc7565b90506000613208611bdb8784614aa4565b905060006132168383614be6565b6001600160401b0381111561323b57634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561327457816020015b613261613ebe565b8152602001906001900390816132595790505b509050825b82811015611d155760405180604001604052806132b6613297612302565b84815181106111d757634e487b7160e01b600052603260045260246000fd5b81526020016132e68c6132c7612302565b8581518110612c0657634e487b7160e01b600052603260045260246000fd5b9052826132f38684614be6565b8151811061331157634e487b7160e01b600052603260045260246000fd5b6020026020010181905250808061332790614c2d565b915050613279565b6060600082516001600160401b0381111561335a57634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561339357816020015b613380613e7d565b8152602001906001900390816133785790505b50905060005b835181101561178b5760405180604001604052806133d0868481518110611c8f57634e487b7160e01b600052603260045260246000fd5b81526020016133f98787858151811061174057634e487b7160e01b600052603260045260246000fd5b81525082828151811061341c57634e487b7160e01b600052603260045260246000fd5b6020026020010181905250808061343290614c2d565b915050613399565b600080613457670de0b6b3a7640000611102876111088884613b39565b9050613492670de0b6b3a7640000611108606461110261016d8161348062015180868c89613b39565b61110889670de0b6b3a7640000613b39565b95945050505050565b600080805b8351811015610bac576134d98482815181106134cc57634e487b7160e01b600052603260045260246000fd5b6020026020010151610962565b6134e39083614aa4565b9150806134ef81614c2d565b9150506134a0565b600080613502612302565b90506000805b8251811015610bac576135348382815181106118d357634e487b7160e01b600052603260045260246000fd5b61353e9083614aa4565b91508061354a81614c2d565b915050613508565b600054610100900460ff168061356b575060005460ff16155b6135875760405162461bcd60e51b81526004016115d5906149a9565b600054610100900460ff161580156135a9576000805461ffff19166101011790555b6135b282613bcc565b6135da427f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8a55565b80156135ec576000805461ff00191690555b5050565b606060006135fd83613092565b516001600160401b0381111561362357634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561364c578160200160208202803683370190505b50905060005b61365b84613092565b51811015610bac57600061366e85613092565b828151811061368d57634e487b7160e01b600052603260045260246000fd5b6020026020010151905060006136a282611335565b905060006136e2670de0b6b3a764000061110884866001600160a01b03166370a082318c6040518263ffffffff1660e01b8152600401611a769190614711565b905061372181846001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9e57600080fd5b85858151811061374157634e487b7160e01b600052603260045260246000fd5b602002602001018181525050505050808061375b90614c2d565b915050613652565b600080836001600160a01b0316633fee85e6846040518263ffffffff1660e01b81526004016137929190614711565b60206040518083038186803b1580156137aa57600080fd5b505afa1580156137be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137e29190614264565b905060006137ef856110d8565b905060006137fc85611335565b905060006138a0876001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561383c57600080fd5b505afa158015613850573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138749190614264565b604051630b711f0760e11b81526001600160a01b038a16906316e23e0e90611a76908b90600401614711565b905082158015906138b057508015155b80156138bb57504284115b156139d55760006138cc8542613c75565b90506000613956896001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561390c57600080fd5b505afa158015613920573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139449190614264565b61110884670de0b6b3a7640000613b39565b9050600061397b6ec097ce7bc90715b34b9f1000000000611108876111028887613b39565b90506139ba818a6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9e57600080fd5b90506139c786828561343a565b975050505050505050610e31565b6000945050505050610e31565b606060006139ef83613092565b516001600160401b03811115613a1557634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015613a3e578160200160208202803683370190505b50905060005b613a4d84613092565b51811015610bac576000613a6085613092565b8281518110613a7f57634e487b7160e01b600052603260045260246000fd5b60200260200101519050613ab8816001600160a01b03166370a08231876040518263ffffffff1660e01b8152600401610a159190614711565b838381518110613ad857634e487b7160e01b600052603260045260246000fd5b60209081029190910101525080613aee81614c2d565b915050613a44565b7f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c36175490565b6000610ade613b2b83600a614b1f565b61110885670de0b6b3a76400005b6000610ade8284614bc7565b6000610ade8284614abc565b6000838511613b6257506000611139565b6000613b6e8686613c75565b90506000613b86613b7f8686613c75565b6001613c81565b9050613bab670de0b6b3a764000061110860646111026301e133808187858a88613b39565b979650505050505050565b6000818310613bc55781610ade565b5090919050565b6001600160a01b038116613c115760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b60448201526064016115d5565b7f19b7d592cea0dfda222f6a4ea5c5a8d018ee9c6b1d0a917483a405de94eb26cd613c3a613af6565b82604051613c49929190614725565b60405180910390a17f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c361755565b6000610ade8284614be6565b600081831015613bc55781610ade565b60405180610180016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160608152602001606081526020016060815260200160608152602001606081525090565b604051806102c0016040528060006001600160a01b03168152602001606081526020016000815260200160001515815260200160008152602001600081526020016000815260200160006001600160a01b031681526020016060815260200160608152602001606081526020016000815260200160608152602001600081526020016000815260200160006001600160a01b031681526020016000815260200160006013811115613dc557634e487b7160e01b600052602160045260246000fd5b81526020016060815260200160608152602001600015158152602001600081525090565b60405180610180016040528060006001600160a01b0316815260200160008152602001600015158152602001600081526020016000815260200160006001600160a01b0316815260200160608152602001606081526020016000815260200160006013811115613e6957634e487b7160e01b600052602160045260246000fd5b815260200160608152602001600081525090565b6040518060400160405280613e90613de9565b8152602001613eb960405180606001604052806000815260200160008152602001600081525090565b905290565b6040518060400160405280613ed1613d04565b8152602001613eb9613c91565b600082601f830112613eee578081fd5b81356020613f03613efe83614a81565b614a51565b80838252828201915082860187848660051b8901011115613f22578586fd5b855b85811015613f49578135613f3781614c74565b84529284019290840190600101613f24565b5090979650505050505050565b600060208284031215613f67578081fd5b8135610ade81614c74565b600060208284031215613f83578081fd5b8151610ade81614c74565b60008060408385031215613fa0578081fd5b8235613fab81614c74565b91506020830135613fbb81614c74565b809150509250929050565b60008060408385031215613fd8578182fd5b8235613fe381614c74565b915060208301356001600160401b03811115613ffd578182fd5b61400985828601613ede565b9150509250929050565b600080600060608486031215614027578081fd5b833561403281614c74565b95602085013595506040909401359392505050565b600060208284031215614058578081fd5b81356001600160401b0381111561406d578182fd5b61113984828501613ede565b6000602080838503121561408b578182fd5b82516001600160401b038111156140a0578283fd5b8301601f810185136140b0578283fd5b80516140be613efe82614a81565b80828252848201915084840188868560051b87010111156140dd578687fd5b8694505b838510156141085780516140f481614c74565b8352600194909401939185019185016140e1565b50979650505050505050565b600060208284031215614125578081fd5b81518015158114610ade578182fd5b600060208284031215614145578081fd5b815160148110610ade578182fd5b600060208284031215614164578081fd5b81516001600160401b038082111561417a578283fd5b818401915084601f83011261418d578283fd5b81518181111561419f5761419f614c5e565b6141b2601f8201601f1916602001614a51565b91508082528560208285010111156141c8578384fd5b6110cf816020840160208601614bfd565b600060e082840312156141ea578081fd5b60405160e081018181106001600160401b038211171561420c5761420c614c5e565b604052825161421a81614c74565b808252506020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c08201528091505092915050565b600060208284031215614275578081fd5b5051919050565b600080600060608486031215614290578081fd5b505081359360208301359350604090920135919050565b6000602082840312156142b8578081fd5b815160ff81168114610ade578182fd5b6000815180845260208085019450808401835b838110156143005781516001600160a01b0316875295820195908201906001016142db565b509495945050505050565b6000815180845260208085019450808401835b838110156143005781518752958201959082019060010161431e565b6014811061435857634e487b7160e01b600052602160045260246000fd5b9052565b60008151808452614374816020860160208601614bfd565b601f01601f19169290920160200192915050565b80516001600160a01b03168252600061018060208301516143b460208601826001600160a01b03169052565b5060408301516040850152606083015160608501526080830151608085015260a083015160a085015260c083015160c085015260e08301518160e08601526143fe828601826142c8565b9150506101008084015185830382870152614419838261430b565b925050506101208084015185830382870152614435838261430b565b925050506101408084015185830382870152614451838261430b565b92505050610160808401518583038287015261446d838261430b565b9695505050505050565b80516001600160a01b0316825260006101806020830151602085015260408301516144a6604086018215159052565b50606083015160608501526080830151608085015260a08301516144d560a08601826001600160a01b03169052565b5060c08301518160c08601526144ed828601826142c8565b91505060e083015184820360e0860152614507828261430b565b915050610100808401518186015250610120808401516145298287018261433a565b5050610140808401518583038287015261454383826142c8565b61016095860151969095019590955250919392505050565b80516001600160a01b0316825260006102c060208301518160208601526145848286018261435c565b9150506040830151604085015260608301516145a4606086018215159052565b506080830151608085015260a083015160a085015260c083015160c085015260e08301516145dd60e08601826001600160a01b03169052565b5061010080840151858303828701526145f683826142c8565b925050506101208084015185830382870152614612838261430b565b92505050610140808401518583038287015261462e838261430b565b925050506101608084015181860152506101808084015185830382870152614656838261430b565b925050506101a08084015181860152506101c08084015181860152506101e08084015161468d828701826001600160a01b03169052565b50506102008381015190850152610220808401516146ad8287018261433a565b505061024080840151858303828701526146c783826142c8565b9250505061026080840151858303828701526146e383826142c8565b92505050610280808401516146fb8287018215159052565b50506102a0928301519390920192909252919050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b602081526000610ade60208301846142c8565b6020808252825182820181905260009190848201906040850190845b818110156147a8576147958385518051825260208082015190830152604090810151910152565b928401926060929092019160010161476e565b50909695505050505050565b6000602080830181845280855180835260408601915060408160051b8701019250838701855b8281101561480857603f198886030184526147f6858351614477565b945092850192908501906001016147da565b5092979650505050505050565b6000602080830181845280855180835260408601915060408160051b8701019250838701855b8281101561480857603f1988860301845261485785835161455b565b9450928501929085019060010161483b565b6000602080830181845280855180835260408601915060408160051b8701019250838701855b8281101561480857603f198886030184528151608081518188526148b582890182614477565b9150508782015191506148de888801838051825260208082015190830152604090810151910152565b955050928501929085019060010161488f565b60006020808301818452808551808352604092508286019150828160051b870101848801865b8381101561496757888303603f190185528151805187855261493b8886018261455b565b91890151858303868b01529190506149538183614388565b968901969450505090860190600101614917565b509098975050505050505050565b602081526000610ade602083018461430b565b60208101610e31828461433a565b602081526000610ade602083018461435c565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b81518152602080830151908201526040808301519082015260608101610e31565b602081526000610ade6020830184614388565b602081526000610ade6020830184614477565b602081526000610ade602083018461455b565b604051601f8201601f191681016001600160401b0381118282101715614a7957614a79614c5e565b604052919050565b60006001600160401b03821115614a9a57614a9a614c5e565b5060051b60200190565b60008219821115614ab757614ab7614c48565b500190565b600082614ad757634e487b7160e01b81526012600452602481fd5b500490565b600181815b80851115614b17578160001904821115614afd57614afd614c48565b80851615614b0a57918102915b93841c9390800290614ae1565b509250929050565b6000610ade8383600082614b3557506001610e31565b81614b4257506000610e31565b8160018114614b585760028114614b6257614b7e565b6001915050610e31565b60ff841115614b7357614b73614c48565b50506001821b610e31565b5060208310610133831016604e8410600b8410161715614ba1575081810a610e31565b614bab8383614adc565b8060001904821115614bbf57614bbf614c48565b029392505050565b6000816000190483118215151615614be157614be1614c48565b500290565b600082821015614bf857614bf8614c48565b500390565b60005b83811015614c18578181015183820152602001614c00565b83811115614c27576000848401525b50505050565b6000600019821415614c4157614c41614c48565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114614c8957600080fd5b5056fea2646970667358221220bc25778b0b2b8da310654fbcc9ecfd812edae3f0312069e9b19db3755989180764736f6c63430008040033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103f15760003560e01c80638b95e33511610215578063d9f9027f11610125578063e527e2c8116100b8578063ee98aa5f11610087578063ee98aa5f14610910578063f2dc7ff514610923578063f4754e0114610669578063f77c479114610936578063ffa1ad741461093e57600080fd5b8063e527e2c8146108cf578063e77fb0d8146108e2578063e945cdcd146108ea578063ebee0406146108fd57600080fd5b8063df0ac912116100f4578063df0ac91214610896578063e2710b89146108a9578063e3d3b66d146108bc578063e410e4a61461087057600080fd5b8063d9f9027f14610848578063db84991d14610850578063dc54c27614610870578063dee1f0e41461088357600080fd5b8063a90f55bf116101a8578063b429afeb11610177578063b429afeb146107dc578063bab6929c146107ef578063be1e27d114610802578063c376dbb314610815578063d0d026331461082857600080fd5b8063a90f55bf14610787578063a980356a146107a7578063aaf5eb68146107ba578063ab1b491c146107c957600080fd5b80639c5d61b4116101e45780639c5d61b41461072e578063a179751b14610741578063a689cbc314610761578063a80a5ce71461077457600080fd5b80638b95e335146106e05780638d852391146106e85780639164359a146106fb57806396ef1b981461071b57600080fd5b80634acd7390116103105780636f8d34f0116102a35780638170bbd5116102725780638170bbd5146106a25780638220ef5b146106aa57806382af54c9146106b2578063849efb71146106ba5780638adab464146106cd57600080fd5b80636f8d34f014610646578063728204e41461066957806372a4aa711461067c5780637e52586c1461068f57600080fd5b8063514b9ec7116102df578063514b9ec7146105e05780636430be8c146106005780636922d7b6146106135780636d5f9b331461062657600080fd5b80634acd7390146105945780634d18e43d146105a75780634e58d37e146105ba5780634f8f0eb0146105cd57600080fd5b8063325a19f1116103885780634624aa44116103575780634624aa441461052a57806347b03bba1461054a578063485cc9551461055f57806349c997ce1461057457600080fd5b8063325a19f1146104d55780633f74bb89146104fc5780634135b6731461050457806341976e091461051757600080fd5b80631184006e116103c45780631184006e1461046f57806319babe8b1461048f57806324d1efcf146104a257806331bd6e2c146104b557600080fd5b806308dc993c146103f65780630bcc41df1461041c5780630f208beb1461042f5780631041cc641461044f575b600080fd5b610409610404366004613f56565b610962565b6040519081526020015b60405180910390f35b61040961042a366004614047565b610ae5565b61044261043d366004613f8e565b610bb3565b6040516104139190614a18565b61046261045d366004613f56565b610e37565b604051610413919061473f565b61048261047d366004613f8e565b610eae565b6040516104139190614975565b61040961049d366004613f56565b6110d8565b6104096104b0366004613f8e565b61110e565b6104c86104c3366004614047565b611141565b6040516104139190614815565b7f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8a54610409565b610409611222565b610409610512366004613f8e565b6112a6565b610409610525366004613f56565b611335565b61053d610538366004613f56565b6113be565b6040516104139190614a2b565b610552611521565b6040516104139190614711565b61057261056d366004613f8e565b6115a0565b005b610587610582366004613fc6565b61168d565b6040516104139190614752565b6104096105a2366004613f56565b611793565b6104096105b5366004613f56565b611806565b6104096105c8366004614047565b6118a2565b6104826105db366004613f8e565b6118fe565b6105f36105ee366004614013565b611b24565b6040516104139190614869565b61040961060e366004613f8e565b611d22565b610572610621366004613f56565b611d53565b610639610634366004613f56565b611e56565b6040516104139190614996565b610659610654366004613f56565b611ecd565b6040519015158152602001610413565b610409610677366004613f56565b611f40565b61040961068a366004613f8e565b611f7b565b61048261069d366004613f8e565b611fb4565b610482612137565b610462612302565b610409612380565b6104096106c8366004613f56565b6123c2565b6106596106db366004613f56565b61240f565b61055261244a565b6104096106f6366004614047565b6124a4565b61070e610709366004613f56565b612500565b6040516104139190614a3e565b610482610729366004613f56565b6126e8565b61040961073c366004613f56565b61291a565b61075461074f366004613f56565b6129e0565b6040516104139190614988565b61075461076f366004613f56565b612a53565b610409610782366004613f56565b612afe565b61079a610795366004613fc6565b612b3c565b60405161041391906148f1565b6104826107b5366004613f8e565b612c54565b610409670de0b6b3a764000081565b6105526107d7366004613f56565b612d9f565b6106596107ea366004613f56565b612e12565b6104096107fd366004613f8e565b612e37565b610409610810366004613f56565b612e62565b610409610823366004613f56565b612ea0565b61083b610836366004614047565b612f1c565b60405161041391906147b4565b610462612ff0565b61086361085e366004613f8e565b613032565b60405161041391906149f7565b61046261087e366004613f56565b613092565b610659610891366004613f56565b6130cd565b61079a6108a4366004614013565b613161565b6105f36108b7366004613fc6565b61332f565b6104096108ca36600461427c565b61343a565b6104096108dd366004614047565b61349b565b6104096134f7565b6105726108f8366004613f56565b613552565b61048261090b366004613f56565b6135f0565b61040961091e366004613f8e565b613763565b610482610931366004613f56565b6139e2565b610552613af6565b610639604051806040016040528060058152602001640c4b8c0b8d60da1b81525081565b60008061096d613af6565b6001600160a01b031663f7c618c16040518163ffffffff1660e01b815260040160206040518083038186803b1580156109a557600080fd5b505afa1580156109b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109dd9190613f72565b9050610ade6109ea611521565b6001600160a01b031663cb618c9d856040518263ffffffff1660e01b8152600401610a159190614711565b60206040518083038186803b158015610a2d57600080fd5b505afa158015610a41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a659190614264565b826001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9e57600080fd5b505afa158015610ab2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad691906142a7565b60ff16613b1b565b9392505050565b600080805b8351811015610bac57610b8e848281518110610b1657634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663a8c62e766040518163ffffffff1660e01b815260040160206040518083038186803b158015610b5657600080fd5b505afa158015610b6a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104049190613f72565b610b989083614aa4565b915080610ba481614c2d565b915050610aea565b5092915050565b610bbb613c91565b6000826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b158015610bf657600080fd5b505afa158015610c0a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c329190810190614079565b9050600081516001600160401b03811115610c5d57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610c86578160200160208202803683370190505b50905060005b8251811015610d7757846001600160a01b031663211dc32d848381518110610cc457634e487b7160e01b600052603260045260246000fd5b6020026020010151886040518363ffffffff1660e01b8152600401610cea929190614725565b60206040518083038186803b158015610d0257600080fd5b505afa158015610d16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3a9190614264565b828281518110610d5a57634e487b7160e01b600052603260045260246000fd5b602090810291909101015280610d6f81614c2d565b915050610c8c565b50604051806101800160405280866001600160a01b03168152602001856001600160a01b03168152602001610dac8787611f7b565b8152602001610dbb8787612e37565b8152602001610dca87876112a6565b8152602001610dd9878761110e565b8152602001610de88787611d22565b8152602001838152602001610dfd8787612c54565b8152602001610e0c8787611fb4565b8152602001610e1b8787610eae565b8152602001610e2a87876118fe565b9052925050505b92915050565b6060816001600160a01b03166371a973056040518163ffffffff1660e01b815260040160006040518083038186803b158015610e7257600080fd5b505afa158015610e86573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e319190810190614079565b60606000826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b158015610eeb57600080fd5b505afa158015610eff573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f279190810190614079565b9050600081516001600160401b03811115610f5257634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610f7b578160200160208202803683370190505b50905060005b82518110156110cf57611092856001600160a01b031663e81bb2d6858481518110610fbc57634e487b7160e01b600052603260045260246000fd5b6020026020010151896040518363ffffffff1660e01b8152600401610fe2929190614725565b60206040518083038186803b158015610ffa57600080fd5b505afa15801561100e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110329190614264565b84838151811061105257634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9e57600080fd5b8282815181106110b257634e487b7160e01b600052603260045260246000fd5b6020908102919091010152806110c781614c2d565b915050610f81565b50949350505050565b6000806110e761052584612d9f565b9050610ade670de0b6b3a76400006111088361110287612afe565b90613b39565b90613b45565b60008061111d61052584612d9f565b9050611139670de0b6b3a76400006111088361110288886112a6565b949350505050565b6060600082516001600160401b0381111561116c57634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156111a557816020015b611192613d04565b81526020019060019003908161118a5790505b50905060005b8351811015610bac576111e48482815181106111d757634e487b7160e01b600052603260045260246000fd5b6020026020010151612500565b82828151811061120457634e487b7160e01b600052603260045260246000fd5b6020026020010181905250808061121a90614c2d565b9150506111ab565b600061122c611521565b6001600160a01b031663d9f9027f6040518163ffffffff1660e01b815260040160006040518083038186803b15801561126457600080fd5b505afa158015611278573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112a09190810190614079565b51905090565b6000610ade826001600160a01b0316638cb1d67f856040518263ffffffff1660e01b81526004016112d79190614711565b60206040518083038186803b1580156112ef57600080fd5b505afa158015611303573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113279190614264565b61133084612ea0565b613b1b565b600061133f61244a565b6001600160a01b0316637539bdb1836040518263ffffffff1660e01b815260040161136a9190614711565b60206040518083038186803b15801561138257600080fd5b505afa9250505080156113b2575060408051601f3d908101601f191682019092526113af91810190614264565b60015b610e3157506000919050565b6113c6613de9565b6000826001600160a01b031663a8c62e766040518163ffffffff1660e01b815260040160206040518083038186803b15801561140157600080fd5b505afa158015611415573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114399190613f72565b90506000604051806101800160405280856001600160a01b0316815260200161146186611f40565b815260200161146f86611ecd565b1515815260200161147f86612afe565b815260200161148d866110d8565b815260200161149b86612d9f565b6001600160a01b031681526020016114b286613092565b81526020016114c0866126e8565b81526020016114ce86611806565b81526020016114dc846129e0565b60138111156114fb57634e487b7160e01b600052602160045260246000fd5b815260200161150984610e37565b815260200161151784610962565b9052949350505050565b600061152b613af6565b6001600160a01b03166347b03bba6040518163ffffffff1660e01b815260040160206040518083038186803b15801561156357600080fd5b505afa158015611577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061159b9190613f72565b905090565b600054610100900460ff16806115b9575060005460ff16155b6115de5760405162461bcd60e51b81526004016115d5906149a9565b60405180910390fd5b600054610100900460ff16158015611600576000805461ffff19166101011790555b61160983613552565b816001600060405160200161162e906931b0b631bab630ba37b960b11b8152600a0190565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055508015611688576000805461ff00191690555b505050565b6060600082516001600160401b038111156116b857634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561170d57816020015b6116fa60405180606001604052806000815260200160008152602001600081525090565b8152602001906001900390816116d65790505b50905060005b835181101561178b5761174d8585838151811061174057634e487b7160e01b600052603260045260246000fd5b6020026020010151613032565b82828151811061176d57634e487b7160e01b600052603260045260246000fd5b6020026020010181905250808061178390614c2d565b915050611713565b509392505050565b6000816001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156117ce57600080fd5b505afa1580156117e2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e319190614264565b6000610e31611327836001600160a01b03166377c7b8fc6040518163ffffffff1660e01b815260040160206040518083038186803b15801561184757600080fd5b505afa15801561185b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187f9190614264565b61188885612ea0565b61189390600a614b1f565b4261189d87611f40565b613b51565b600080805b8351811015610bac576118e08482815181106118d357634e487b7160e01b600052603260045260246000fd5b60200260200101516123c2565b6118ea9083614aa4565b9150806118f681614c2d565b9150506118a7565b60606000826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b15801561193b57600080fd5b505afa15801561194f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119779190810190614079565b9050600081516001600160401b038111156119a257634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156119cb578160200160208202803683370190505b50905060005b82518110156110cf576000611a0c8483815181106119ff57634e487b7160e01b600052603260045260246000fd5b6020026020010151611335565b9050611ae6611ac6670de0b6b3a7640000611108848a6001600160a01b031663e81bb2d68a8981518110611a5057634e487b7160e01b600052603260045260246000fd5b60200260200101518e6040518363ffffffff1660e01b8152600401611a76929190614725565b60206040518083038186803b158015611a8e57600080fd5b505afa158015611aa2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111029190614264565b85848151811061105257634e487b7160e01b600052603260045260246000fd5b838381518110611b0657634e487b7160e01b600052603260045260246000fd5b60209081029190910101525080611b1c81614c2d565b9150506119d1565b60606000611b30612302565b51905060008111611b725760405162461bcd60e51b815260206004820152600c60248201526b656d707479207661756c747360a01b60448201526064016115d5565b6000611b7e8483614abc565b905081611b8b8583614bc7565b1015611b9f5780611b9b81614c2d565b9150505b80851115611bab578094505b6000611bca611bba8688614bc7565b611bc5600186614be6565b613bb6565b90506000611be1611bdb8784614aa4565b85613bb6565b90506000611bef8383614be6565b6001600160401b03811115611c1457634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611c4d57816020015b611c3a613e7d565b815260200190600190039081611c325790505b509050825b82811015611d15576040518060400160405280611c9c611c70612302565b8481518110611c8f57634e487b7160e01b600052603260045260246000fd5b60200260200101516113be565b8152602001611ccc8c611cad612302565b858151811061174057634e487b7160e01b600052603260045260246000fd5b905282611cd98684614be6565b81518110611cf757634e487b7160e01b600052603260045260246000fd5b60200260200101819052508080611d0d90614c2d565b915050611c52565b5098975050505050505050565b6000610ade826001600160a01b03166370a08231856040518263ffffffff1660e01b81526004016112d79190614711565b611d5c33612e12565b80611d6b5750611d6b336130cd565b611daf5760405162461bcd60e51b81526020600482015260156024820152743737ba1031b7b73a3937b63632b91037b91033b7bb60591b60448201526064016115d5565b8060016000604051602001611dd4906931b0b631bab630ba37b960b11b8152600a0190565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f0caadb6ce6adfa207b9ff9c87df2ad8361fe4e2e5c4fb493fba65daa077dc4f481604051611e4b9190614711565b60405180910390a150565b6060816001600160a01b03166306fdde036040518163ffffffff1660e01b815260040160006040518083038186803b158015611e9157600080fd5b505afa158015611ea5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e319190810190614153565b6000816001600160a01b03166302fb0c5e6040518163ffffffff1660e01b815260040160206040518083038186803b158015611f0857600080fd5b505afa158015611f1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e319190614114565b6000816001600160a01b031663325a19f16040518163ffffffff1660e01b815260040160206040518083038186803b1580156117ce57600080fd5b6000610ade611f8983612d9f565b6001600160a01b03166370a08231856040518263ffffffff1660e01b81526004016112d79190614711565b60606000826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b158015611ff157600080fd5b505afa158015612005573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261202d9190810190614079565b9050600081516001600160401b0381111561205857634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612081578160200160208202803683370190505b50905060005b82518110156110cf5760006120b58483815181106119ff57634e487b7160e01b600052603260045260246000fd5b90506120f9611ac6670de0b6b3a7640000611108848a6001600160a01b031663211dc32d8a8981518110611a5057634e487b7160e01b600052603260045260246000fd5b83838151811061211957634e487b7160e01b600052603260045260246000fd5b6020908102919091010152508061212f81614c2d565b915050612087565b606060006121b6612146613af6565b6001600160a01b031663f7c618c16040518163ffffffff1660e01b815260040160206040518083038186803b15801561217e57600080fd5b505afa158015612192573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105259190613f72565b9050600061227a670de0b6b3a7640000611108846121d2613af6565b6001600160a01b031663f7c618c16040518163ffffffff1660e01b815260040160206040518083038186803b15801561220a57600080fd5b505afa15801561221e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122429190613f72565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8e57600080fd5b604080516002808252606082018352929350600092909160208301908036833701905050905082816000815181106122c257634e487b7160e01b600052603260045260246000fd5b60200260200101818152505081816001815181106122f057634e487b7160e01b600052603260045260246000fd5b60209081029190910101529392505050565b606061230c611521565b6001600160a01b0316638220ef5b6040518163ffffffff1660e01b815260040160006040518083038186803b15801561234457600080fd5b505afa158015612358573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261159b9190810190614079565b600061238a611521565b6001600160a01b0316638220ef5b6040518163ffffffff1660e01b815260040160006040518083038186803b15801561126457600080fd5b60006123cc611521565b6001600160a01b031663c14a38e3836040518263ffffffff1660e01b81526004016123f79190614711565b60206040518083038186803b1580156117ce57600080fd5b6000816001600160a01b031663d3df8aa46040518163ffffffff1660e01b815260040160206040518083038186803b158015611f0857600080fd5b600060016000604051602001612470906931b0b631bab630ba37b960b11b8152600a0190565b60408051601f19818403018152918152815160209283012083529082019290925201600020546001600160a01b0316919050565b600080805b8351811015610bac576124e28482815181106124d557634e487b7160e01b600052603260045260246000fd5b60200260200101516110d8565b6124ec9083614aa4565b9150806124f881614c2d565b9150506124a9565b612508613d04565b6000826001600160a01b031663a8c62e766040518163ffffffff1660e01b815260040160206040518083038186803b15801561254357600080fd5b505afa158015612557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257b9190613f72565b90506000604051806102c00160405280856001600160a01b031681526020016125a386611e56565b81526020016125b186611f40565b81526020016125bf86611ecd565b151581526020016125cf86612afe565b81526020016125dd866110d8565b81526020016125eb86612ea0565b81526020016125f986612d9f565b6001600160a01b0316815260200161261086613092565b815260200161261e866139e2565b815260200161262c866135f0565b815260200161263a86611793565b8152602001612648866126e8565b815260200161265686611806565b8152602001612664866123c2565b8152602001836001600160a01b0316815260200161268184611f40565b815260200161268f846129e0565b60138111156126ae57634e487b7160e01b600052602160045260246000fd5b81526020016126bc84610e37565b81526020016126ca84613092565b81526020016126d88461240f565b1515815260200161151784610962565b606060008290506000816001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b15801561272a57600080fd5b505afa15801561273e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526127669190810190614079565b516001600160401b0381111561278c57634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156127b5578160200160208202803683370190505b50905060005b826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b1580156127f457600080fd5b505afa158015612808573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128309190810190614079565b5181101561178b576128dd85846001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b15801561287557600080fd5b505afa158015612889573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128b19190810190614079565b83815181106128d057634e487b7160e01b600052603260045260246000fd5b6020026020010151613763565b8282815181106128fd57634e487b7160e01b600052603260045260246000fd5b60209081029190910101528061291281614c2d565b9150506127bb565b600080612925611521565b6001600160a01b031663400dfebd846040518263ffffffff1660e01b81526004016129509190614711565b60e06040518083038186803b15801561296857600080fd5b505afa15801561297c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129a091906141d9565b90508060400151600014156129b85750600092915050565b610ade6129d782606001518360c0015184604001518560a00151613b51565b61133085612ea0565b6000816001600160a01b0316634bde38c86040518163ffffffff1660e01b815260040160206040518083038186803b158015612a1b57600080fd5b505afa158015612a2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e319190614134565b6000816001600160a01b031663a8c62e766040518163ffffffff1660e01b815260040160206040518083038186803b158015612a8e57600080fd5b505afa158015612aa2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ac69190613f72565b6001600160a01b0316634bde38c86040518163ffffffff1660e01b815260040160206040518083038186803b158015612a1b57600080fd5b6000610e31826001600160a01b0316631bf8e7be6040518163ffffffff1660e01b815260040160206040518083038186803b1580156112ef57600080fd5b6060600082516001600160401b03811115612b6757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612ba057816020015b612b8d613ebe565b815260200190600190039081612b855790505b50905060005b835181101561178b576040518060400160405280612bdd8684815181106111d757634e487b7160e01b600052603260045260246000fd5b8152602001612c1387878581518110612c0657634e487b7160e01b600052603260045260246000fd5b6020026020010151610bb3565b815250828281518110612c3657634e487b7160e01b600052603260045260246000fd5b60200260200101819052508080612c4c90614c2d565b915050612ba6565b60606000826001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b158015612c9157600080fd5b505afa158015612ca5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612ccd9190810190614079565b9050600081516001600160401b03811115612cf857634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612d21578160200160208202803683370190505b50905060005b82518110156110cf57612d62856001600160a01b031663211dc32d858481518110610fbc57634e487b7160e01b600052603260045260246000fd5b828281518110612d8257634e487b7160e01b600052603260045260246000fd5b602090810291909101015280612d9781614c2d565b915050612d27565b6000816001600160a01b0316636f307dc36040518163ffffffff1660e01b815260040160206040518083038186803b158015612dda57600080fd5b505afa158015612dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e319190613f72565b6000612e1c613af6565b6001600160a01b0316826001600160a01b0316149050919050565b600080612e4661052584612d9f565b9050611139670de0b6b3a7640000611108836111028888611f7b565b6000610e31826001600160a01b03166377c7b8fc6040518163ffffffff1660e01b815260040160206040518083038186803b1580156112ef57600080fd5b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015612edb57600080fd5b505afa158015612eef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f1391906142a7565b60ff1692915050565b6060600082516001600160401b03811115612f4757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612f8057816020015b612f6d613de9565b815260200190600190039081612f655790505b50905060005b8351811015610bac57612fb2848281518110611c8f57634e487b7160e01b600052603260045260246000fd5b828281518110612fd257634e487b7160e01b600052603260045260246000fd5b60200260200101819052508080612fe890614c2d565b915050612f86565b6060612ffa611521565b6001600160a01b031663d9f9027f6040518163ffffffff1660e01b815260040160006040518083038186803b15801561234457600080fd5b61305660405180606001604052806000815260200160008152602001600081525090565b604051806060016040528061306b85856112a6565b815260200161307a858561110e565b81526020016130898585611d22565b90529392505050565b6060816001600160a01b031663c2b18aa06040518163ffffffff1660e01b815260040160006040518083038186803b158015610e7257600080fd5b6000816001600160a01b03166130e1613af6565b6001600160a01b0316635aa6e6756040518163ffffffff1660e01b815260040160206040518083038186803b15801561311957600080fd5b505afa15801561312d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131519190613f72565b6001600160a01b03161492915050565b6060600061316d612302565b519050600081116131af5760405162461bcd60e51b815260206004820152600c60248201526b656d707479207661756c747360a01b60448201526064016115d5565b60006131bb8483614abc565b9050816131c88583614bc7565b10156131dc57806131d881614c2d565b9150505b808511156131e8578094505b60006131f7611bba8688614bc7565b90506000613208611bdb8784614aa4565b905060006132168383614be6565b6001600160401b0381111561323b57634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561327457816020015b613261613ebe565b8152602001906001900390816132595790505b509050825b82811015611d155760405180604001604052806132b6613297612302565b84815181106111d757634e487b7160e01b600052603260045260246000fd5b81526020016132e68c6132c7612302565b8581518110612c0657634e487b7160e01b600052603260045260246000fd5b9052826132f38684614be6565b8151811061331157634e487b7160e01b600052603260045260246000fd5b6020026020010181905250808061332790614c2d565b915050613279565b6060600082516001600160401b0381111561335a57634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561339357816020015b613380613e7d565b8152602001906001900390816133785790505b50905060005b835181101561178b5760405180604001604052806133d0868481518110611c8f57634e487b7160e01b600052603260045260246000fd5b81526020016133f98787858151811061174057634e487b7160e01b600052603260045260246000fd5b81525082828151811061341c57634e487b7160e01b600052603260045260246000fd5b6020026020010181905250808061343290614c2d565b915050613399565b600080613457670de0b6b3a7640000611102876111088884613b39565b9050613492670de0b6b3a7640000611108606461110261016d8161348062015180868c89613b39565b61110889670de0b6b3a7640000613b39565b95945050505050565b600080805b8351811015610bac576134d98482815181106134cc57634e487b7160e01b600052603260045260246000fd5b6020026020010151610962565b6134e39083614aa4565b9150806134ef81614c2d565b9150506134a0565b600080613502612302565b90506000805b8251811015610bac576135348382815181106118d357634e487b7160e01b600052603260045260246000fd5b61353e9083614aa4565b91508061354a81614c2d565b915050613508565b600054610100900460ff168061356b575060005460ff16155b6135875760405162461bcd60e51b81526004016115d5906149a9565b600054610100900460ff161580156135a9576000805461ffff19166101011790555b6135b282613bcc565b6135da427f6f55f470bdc9cb5f04223fd822021061668e4dccb43e8727b295106dc9769c8a55565b80156135ec576000805461ff00191690555b5050565b606060006135fd83613092565b516001600160401b0381111561362357634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561364c578160200160208202803683370190505b50905060005b61365b84613092565b51811015610bac57600061366e85613092565b828151811061368d57634e487b7160e01b600052603260045260246000fd5b6020026020010151905060006136a282611335565b905060006136e2670de0b6b3a764000061110884866001600160a01b03166370a082318c6040518263ffffffff1660e01b8152600401611a769190614711565b905061372181846001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9e57600080fd5b85858151811061374157634e487b7160e01b600052603260045260246000fd5b602002602001018181525050505050808061375b90614c2d565b915050613652565b600080836001600160a01b0316633fee85e6846040518263ffffffff1660e01b81526004016137929190614711565b60206040518083038186803b1580156137aa57600080fd5b505afa1580156137be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137e29190614264565b905060006137ef856110d8565b905060006137fc85611335565b905060006138a0876001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561383c57600080fd5b505afa158015613850573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138749190614264565b604051630b711f0760e11b81526001600160a01b038a16906316e23e0e90611a76908b90600401614711565b905082158015906138b057508015155b80156138bb57504284115b156139d55760006138cc8542613c75565b90506000613956896001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561390c57600080fd5b505afa158015613920573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139449190614264565b61110884670de0b6b3a7640000613b39565b9050600061397b6ec097ce7bc90715b34b9f1000000000611108876111028887613b39565b90506139ba818a6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9e57600080fd5b90506139c786828561343a565b975050505050505050610e31565b6000945050505050610e31565b606060006139ef83613092565b516001600160401b03811115613a1557634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015613a3e578160200160208202803683370190505b50905060005b613a4d84613092565b51811015610bac576000613a6085613092565b8281518110613a7f57634e487b7160e01b600052603260045260246000fd5b60200260200101519050613ab8816001600160a01b03166370a08231876040518263ffffffff1660e01b8152600401610a159190614711565b838381518110613ad857634e487b7160e01b600052603260045260246000fd5b60209081029190910101525080613aee81614c2d565b915050613a44565b7f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c36175490565b6000610ade613b2b83600a614b1f565b61110885670de0b6b3a76400005b6000610ade8284614bc7565b6000610ade8284614abc565b6000838511613b6257506000611139565b6000613b6e8686613c75565b90506000613b86613b7f8686613c75565b6001613c81565b9050613bab670de0b6b3a764000061110860646111026301e133808187858a88613b39565b979650505050505050565b6000818310613bc55781610ade565b5090919050565b6001600160a01b038116613c115760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b60448201526064016115d5565b7f19b7d592cea0dfda222f6a4ea5c5a8d018ee9c6b1d0a917483a405de94eb26cd613c3a613af6565b82604051613c49929190614725565b60405180910390a17f5165972ef41194f06c5007493031d0b927c20741adcb74403b954009fd2c361755565b6000610ade8284614be6565b600081831015613bc55781610ade565b60405180610180016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160608152602001606081526020016060815260200160608152602001606081525090565b604051806102c0016040528060006001600160a01b03168152602001606081526020016000815260200160001515815260200160008152602001600081526020016000815260200160006001600160a01b031681526020016060815260200160608152602001606081526020016000815260200160608152602001600081526020016000815260200160006001600160a01b031681526020016000815260200160006013811115613dc557634e487b7160e01b600052602160045260246000fd5b81526020016060815260200160608152602001600015158152602001600081525090565b60405180610180016040528060006001600160a01b0316815260200160008152602001600015158152602001600081526020016000815260200160006001600160a01b0316815260200160608152602001606081526020016000815260200160006013811115613e6957634e487b7160e01b600052602160045260246000fd5b815260200160608152602001600081525090565b6040518060400160405280613e90613de9565b8152602001613eb960405180606001604052806000815260200160008152602001600081525090565b905290565b6040518060400160405280613ed1613d04565b8152602001613eb9613c91565b600082601f830112613eee578081fd5b81356020613f03613efe83614a81565b614a51565b80838252828201915082860187848660051b8901011115613f22578586fd5b855b85811015613f49578135613f3781614c74565b84529284019290840190600101613f24565b5090979650505050505050565b600060208284031215613f67578081fd5b8135610ade81614c74565b600060208284031215613f83578081fd5b8151610ade81614c74565b60008060408385031215613fa0578081fd5b8235613fab81614c74565b91506020830135613fbb81614c74565b809150509250929050565b60008060408385031215613fd8578182fd5b8235613fe381614c74565b915060208301356001600160401b03811115613ffd578182fd5b61400985828601613ede565b9150509250929050565b600080600060608486031215614027578081fd5b833561403281614c74565b95602085013595506040909401359392505050565b600060208284031215614058578081fd5b81356001600160401b0381111561406d578182fd5b61113984828501613ede565b6000602080838503121561408b578182fd5b82516001600160401b038111156140a0578283fd5b8301601f810185136140b0578283fd5b80516140be613efe82614a81565b80828252848201915084840188868560051b87010111156140dd578687fd5b8694505b838510156141085780516140f481614c74565b8352600194909401939185019185016140e1565b50979650505050505050565b600060208284031215614125578081fd5b81518015158114610ade578182fd5b600060208284031215614145578081fd5b815160148110610ade578182fd5b600060208284031215614164578081fd5b81516001600160401b038082111561417a578283fd5b818401915084601f83011261418d578283fd5b81518181111561419f5761419f614c5e565b6141b2601f8201601f1916602001614a51565b91508082528560208285010111156141c8578384fd5b6110cf816020840160208601614bfd565b600060e082840312156141ea578081fd5b60405160e081018181106001600160401b038211171561420c5761420c614c5e565b604052825161421a81614c74565b808252506020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c08201528091505092915050565b600060208284031215614275578081fd5b5051919050565b600080600060608486031215614290578081fd5b505081359360208301359350604090920135919050565b6000602082840312156142b8578081fd5b815160ff81168114610ade578182fd5b6000815180845260208085019450808401835b838110156143005781516001600160a01b0316875295820195908201906001016142db565b509495945050505050565b6000815180845260208085019450808401835b838110156143005781518752958201959082019060010161431e565b6014811061435857634e487b7160e01b600052602160045260246000fd5b9052565b60008151808452614374816020860160208601614bfd565b601f01601f19169290920160200192915050565b80516001600160a01b03168252600061018060208301516143b460208601826001600160a01b03169052565b5060408301516040850152606083015160608501526080830151608085015260a083015160a085015260c083015160c085015260e08301518160e08601526143fe828601826142c8565b9150506101008084015185830382870152614419838261430b565b925050506101208084015185830382870152614435838261430b565b925050506101408084015185830382870152614451838261430b565b92505050610160808401518583038287015261446d838261430b565b9695505050505050565b80516001600160a01b0316825260006101806020830151602085015260408301516144a6604086018215159052565b50606083015160608501526080830151608085015260a08301516144d560a08601826001600160a01b03169052565b5060c08301518160c08601526144ed828601826142c8565b91505060e083015184820360e0860152614507828261430b565b915050610100808401518186015250610120808401516145298287018261433a565b5050610140808401518583038287015261454383826142c8565b61016095860151969095019590955250919392505050565b80516001600160a01b0316825260006102c060208301518160208601526145848286018261435c565b9150506040830151604085015260608301516145a4606086018215159052565b506080830151608085015260a083015160a085015260c083015160c085015260e08301516145dd60e08601826001600160a01b03169052565b5061010080840151858303828701526145f683826142c8565b925050506101208084015185830382870152614612838261430b565b92505050610140808401518583038287015261462e838261430b565b925050506101608084015181860152506101808084015185830382870152614656838261430b565b925050506101a08084015181860152506101c08084015181860152506101e08084015161468d828701826001600160a01b03169052565b50506102008381015190850152610220808401516146ad8287018261433a565b505061024080840151858303828701526146c783826142c8565b9250505061026080840151858303828701526146e383826142c8565b92505050610280808401516146fb8287018215159052565b50506102a0928301519390920192909252919050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b602081526000610ade60208301846142c8565b6020808252825182820181905260009190848201906040850190845b818110156147a8576147958385518051825260208082015190830152604090810151910152565b928401926060929092019160010161476e565b50909695505050505050565b6000602080830181845280855180835260408601915060408160051b8701019250838701855b8281101561480857603f198886030184526147f6858351614477565b945092850192908501906001016147da565b5092979650505050505050565b6000602080830181845280855180835260408601915060408160051b8701019250838701855b8281101561480857603f1988860301845261485785835161455b565b9450928501929085019060010161483b565b6000602080830181845280855180835260408601915060408160051b8701019250838701855b8281101561480857603f198886030184528151608081518188526148b582890182614477565b9150508782015191506148de888801838051825260208082015190830152604090810151910152565b955050928501929085019060010161488f565b60006020808301818452808551808352604092508286019150828160051b870101848801865b8381101561496757888303603f190185528151805187855261493b8886018261455b565b91890151858303868b01529190506149538183614388565b968901969450505090860190600101614917565b509098975050505050505050565b602081526000610ade602083018461430b565b60208101610e31828461433a565b602081526000610ade602083018461435c565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b81518152602080830151908201526040808301519082015260608101610e31565b602081526000610ade6020830184614388565b602081526000610ade6020830184614477565b602081526000610ade602083018461455b565b604051601f8201601f191681016001600160401b0381118282101715614a7957614a79614c5e565b604052919050565b60006001600160401b03821115614a9a57614a9a614c5e565b5060051b60200190565b60008219821115614ab757614ab7614c48565b500190565b600082614ad757634e487b7160e01b81526012600452602481fd5b500490565b600181815b80851115614b17578160001904821115614afd57614afd614c48565b80851615614b0a57918102915b93841c9390800290614ae1565b509250929050565b6000610ade8383600082614b3557506001610e31565b81614b4257506000610e31565b8160018114614b585760028114614b6257614b7e565b6001915050610e31565b60ff841115614b7357614b73614c48565b50506001821b610e31565b5060208310610133831016604e8410600b8410161715614ba1575081810a610e31565b614bab8383614adc565b8060001904821115614bbf57614bbf614c48565b029392505050565b6000816000190483118215151615614be157614be1614c48565b500290565b600082821015614bf857614bf8614c48565b500390565b60005b83811015614c18578181015183820152602001614c00565b83811115614c27576000848401525b50505050565b6000600019821415614c4157614c41614c48565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114614c8957600080fd5b5056fea2646970667358221220bc25778b0b2b8da310654fbcc9ecfd812edae3f0312069e9b19db3755989180764736f6c63430008040033

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

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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