ETH Price: $2,257.86 (-0.98%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions found.

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer Ownersh...151849442022-07-21 8:48:431294 days ago1658393323
0x31BEE1fB...f3deA2140
0 ETH
Harvest151808212022-07-20 17:24:141294 days ago1658337854
0x31BEE1fB...f3deA2140
0 ETH
Deposit BB151798312022-07-20 13:40:321294 days ago1658324432
0x31BEE1fB...f3deA2140
0 ETH
Harvest151760202022-07-19 23:22:571295 days ago1658272977
0x31BEE1fB...f3deA2140
0 ETH
Harvest151760202022-07-19 23:22:571295 days ago1658272977
0x31BEE1fB...f3deA2140
0 ETH
Deposit BB151742412022-07-19 16:55:151295 days ago1658249715
0x31BEE1fB...f3deA2140
0 ETH
Harvest151725102022-07-19 10:22:351296 days ago1658226155
0x31BEE1fB...f3deA2140
0 ETH
Withdraw BB151583732022-07-17 5:43:171298 days ago1658036597
0x31BEE1fB...f3deA2140
0 ETH
Harvest151562652022-07-16 21:53:391298 days ago1658008419
0x31BEE1fB...f3deA2140
0 ETH
Harvest151530472022-07-16 9:53:341299 days ago1657965214
0x31BEE1fB...f3deA2140
0 ETH
Deposit BB151529522022-07-16 9:30:451299 days ago1657963845
0x31BEE1fB...f3deA2140
0 ETH
Harvest151449502022-07-15 3:51:301300 days ago1657857090
0x31BEE1fB...f3deA2140
0 ETH
Withdraw BB151321052022-07-13 4:19:191302 days ago1657685959
0x31BEE1fB...f3deA2140
0 ETH
Harvest151111292022-07-09 22:39:501305 days ago1657406390
0x31BEE1fB...f3deA2140
0 ETH
Deposit AA151012632022-07-08 10:04:491307 days ago1657274689
0x31BEE1fB...f3deA2140
0 ETH
Harvest150934582022-07-07 5:13:131308 days ago1657170793
0x31BEE1fB...f3deA2140
0 ETH
Harvest150878162022-07-06 8:14:461309 days ago1657095286
0x31BEE1fB...f3deA2140
0 ETH
Deposit BB150872572022-07-06 6:12:211309 days ago1657087941
0x31BEE1fB...f3deA2140
0 ETH
Harvest150846562022-07-05 20:14:181309 days ago1657052058
0x31BEE1fB...f3deA2140
0 ETH
Deposit BB150838242022-07-05 17:18:231309 days ago1657041503
0x31BEE1fB...f3deA2140
0 ETH
Harvest150509842022-06-30 15:08:041314 days ago1656601684
0x31BEE1fB...f3deA2140
0 ETH
Deposit BB150500092022-06-30 10:55:131314 days ago1656586513
0x31BEE1fB...f3deA2140
0 ETH
Harvest150307892022-06-26 20:37:011318 days ago1656275821
0x31BEE1fB...f3deA2140
0 ETH
Harvest150161012022-06-24 2:37:151321 days ago1656038235
0x31BEE1fB...f3deA2140
0 ETH
Harvest150002382022-06-21 3:33:001324 days ago1655782380
0x31BEE1fB...f3deA2140
0 ETH
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
IdleCDO

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 90 runs

Other Settings:
default evmVersion
File 1 of 21 : IdleCDO.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;

import '@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol';
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

import "./interfaces/IIdleCDOStrategy.sol";
import "./interfaces/IERC20Detailed.sol";
import "./interfaces/IIdleCDOTrancheRewards.sol";
import "./interfaces/IStakedAave.sol";

import "./GuardedLaunchUpgradable.sol";
import "./IdleCDOTranche.sol";
import "./IdleCDOStorage.sol";

/// @title A perpetual tranche implementation
/// @author Idle Labs Inc.
/// @notice More info and high level overview in the README
/// @dev The contract is upgradable, to add storage slots, create IdleCDOStorageVX and inherit from IdleCDOStorage, then update the definitaion below
contract IdleCDO is PausableUpgradeable, GuardedLaunchUpgradable, IdleCDOStorage {
  using SafeERC20Upgradeable for IERC20Detailed;

  // ERROR MESSAGES:
  // 0 = is 0
  // 1 = already initialized
  // 2 = Contract limit reached
  // 3 = Tranche withdraw not allowed (Paused or in shutdown)
  // 4 = Default, wait shutdown
  // 5 = Amount too low
  // 6 = Not authorized
  // 7 = Amount too high
  // 8 = Same block

  // Used to prevent initialization of the implementation contract
  /// @custom:oz-upgrades-unsafe-allow constructor
  constructor() {
    token = address(1);
  }

  // ###################
  // Initializer
  // ###################

  /// @notice can only be called once
  /// @dev Initialize the upgradable contract
  /// @param _limit contract value limit, can be 0
  /// @param _guardedToken underlying token
  /// @param _governanceFund address where funds will be sent in case of emergency
  /// @param _owner guardian address (can pause, unpause and call emergencyShutdown)
  /// @param _rebalancer rebalancer address
  /// @param _strategy strategy address
  /// @param _trancheAPRSplitRatio trancheAPRSplitRatio value
  /// @param _trancheIdealWeightRatio trancheIdealWeightRatio value
  /// @param _incentiveTokens array of addresses for incentive tokens
  function initialize(
    uint256 _limit, address _guardedToken, address _governanceFund, address _owner, // GuardedLaunch args
    address _rebalancer,
    address _strategy,
    uint256 _trancheAPRSplitRatio, // for AA tranches, so eg 10000 means 10% interest to AA and 90% BB
    uint256 _trancheIdealWeightRatio, // for AA tranches, so eg 10000 means 10% of tranches are AA and 90% BB
    address[] memory _incentiveTokens
  ) external initializer {
    uint256 _idealRange = FULL_ALLOC / 10;
    require(token == address(0), '1');
    require(_rebalancer != address(0) && _strategy != address(0) && _guardedToken != address(0), "0");
    require( _trancheAPRSplitRatio <= FULL_ALLOC, '7');
    require(_trancheIdealWeightRatio <= (FULL_ALLOC - _idealRange), '7');
    require(_trancheIdealWeightRatio >= _idealRange, '5');
    // Initialize contracts
    PausableUpgradeable.__Pausable_init();
    // check for _governanceFund and _owner != address(0) are inside GuardedLaunchUpgradable
    GuardedLaunchUpgradable.__GuardedLaunch_init(_limit, _governanceFund, _owner);
    // Deploy Tranches tokens
    address _strategyToken = IIdleCDOStrategy(_strategy).strategyToken();
    // get strategy token symbol (eg. idleDAI)
    string memory _symbol = IERC20Detailed(_strategyToken).symbol();
    // create tranche tokens (concat strategy token symbol in the name and symbol of the tranche tokens)
    AATranche = address(new IdleCDOTranche(_concat(string("IdleCDO AA Tranche - "), _symbol), _concat(string("AA_"), _symbol)));
    BBTranche = address(new IdleCDOTranche(_concat(string("IdleCDO BB Tranche - "), _symbol), _concat(string("BB_"), _symbol)));
    // Set CDO params
    token = _guardedToken;
    strategy = _strategy;
    strategyToken = _strategyToken;
    rebalancer = _rebalancer;
    trancheAPRSplitRatio = _trancheAPRSplitRatio;
    trancheIdealWeightRatio = _trancheIdealWeightRatio;
    idealRange = _idealRange; // trancheIdealWeightRatio ± 10%
    uint256 _oneToken = 10**(IERC20Detailed(_guardedToken).decimals());
    oneToken = _oneToken;
    uniswapRouterV2 = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    incentiveTokens = _incentiveTokens;
    priceAA = _oneToken;
    priceBB = _oneToken;
    unlentPerc = 2000; // 2%
    // # blocks, after an harvest, during which harvested rewards gets progressively unlocked
    releaseBlocksPeriod = 1500; // about 1/4 of a day
    // Set flags
    allowAAWithdraw = true;
    allowBBWithdraw = true;
    revertIfTooLow = true;
    // skipDefaultCheck = false is the default value
    // Set allowance for strategy
    _allowUnlimitedSpend(_guardedToken, _strategy);
    _allowUnlimitedSpend(strategyToken, _strategy);
    // Save current strategy price
    lastStrategyPrice = _strategyPrice();
    // Fee params
    fee = 10000; // 10% performance fee
    feeReceiver = address(0xFb3bD022D5DAcF95eE28a6B07825D4Ff9C5b3814); // treasury multisig
    guardian = _owner;
    // feeSplit = 0; // default all to feeReceiver as default
    // StkAAVE unwrapping is active
    isStkAAVEActive = true;
  }

  // ###############
  // Public methods
  // ###############

  /// @notice pausable
  /// @dev msg.sender should approve this contract first to spend `_amount` of `token`
  /// @param _amount amount of `token` to deposit
  /// @return AA tranche tokens minted
  function depositAA(uint256 _amount) external returns (uint256) {
    return _deposit(_amount, AATranche);
  }

  /// @notice pausable in _deposit
  /// @dev msg.sender should approve this contract first to spend `_amount` of `token`
  /// @param _amount amount of `token` to deposit
  /// @return BB tranche tokens minted
  function depositBB(uint256 _amount) external returns (uint256) {
    return _deposit(_amount, BBTranche);
  }

  /// @notice pausable in _deposit
  /// @param _amount amount of AA tranche tokens to burn
  /// @return underlying tokens redeemed
  function withdrawAA(uint256 _amount) external returns (uint256) {
    require(!paused() || allowAAWithdraw, '3');
    return _withdraw(_amount, AATranche);
  }

  /// @notice pausable
  /// @param _amount amount of BB tranche tokens to burn
  /// @return underlying tokens redeemed
  function withdrawBB(uint256 _amount) external returns (uint256) {
    require(!paused() || allowBBWithdraw, '3');
    return _withdraw(_amount, BBTranche);
  }

  // ###############
  // Views
  // ###############

  /// @param _tranche tranche address
  /// @return tranche price
  function tranchePrice(address _tranche) external view returns (uint256) {
    return _tranchePrice(_tranche);
  }

  /// @notice calculates the current total value locked (in `token` terms)
  /// @dev unclaimed rewards (gov tokens) are not counted.
  /// NOTE: `unclaimedFees` are not included in the contract value
  /// NOTE2: fees that *will* be taken (in the next _updateAccounting call) are counted
  function getContractValue() public override view returns (uint256) {
    address _strategyToken = strategyToken;
    uint256 strategyTokenDecimals = IERC20Detailed(_strategyToken).decimals();
    // TVL is the sum of unlent balance in the contract + the balance in lending - the reduction for harvested rewards - unclaimedFees
    // the balance in lending is the value of the interest bearing assets (strategyTokens) in this contract
    // TVL = (strategyTokens * strategy token price) + unlent balance - lockedRewards - unclaimedFees
    return (_contractTokenBalance(_strategyToken) * _strategyPrice() / (10**(strategyTokenDecimals))) +
            _contractTokenBalance(token) -
            _lockedRewards() -
            unclaimedFees;
  }

  /// @param _tranche tranche address
  /// @return apr at ideal ratio (trancheIdealWeightRatio) between AA and BB
  function getIdealApr(address _tranche) external view returns (uint256) {
    return _getApr(_tranche, trancheIdealWeightRatio);
  }

  /// @param _tranche tranche address
  /// @return actual apr given current ratio between AA and BB tranches
  function getApr(address _tranche) external view returns (uint256) {
    return _getApr(_tranche, getCurrentAARatio());
  }

  /// @notice calculates the current AA tranches ratio
  /// @dev _virtualBalance is used to have a more accurate/recent value for the AA ratio
  /// because it calculates the balance after splitting the accrued interest since the
  /// last depositXX/withdrawXX/harvest
  /// @return AA tranches ratio (in underlying value) considering all interest
  function getCurrentAARatio() public view returns (uint256) {
    uint256 AABal = _virtualBalance(AATranche);
    uint256 contractVal = AABal + _virtualBalance(BBTranche);
    if (contractVal == 0) {
      return 0;
    }
    // Current AA tranche split ratio = AABal * FULL_ALLOC / (AABal + BBBal)
    return AABal * FULL_ALLOC / contractVal;
  }

  /// @notice calculates the current tranches price considering the interest that is yet to be splitted
  /// ie the interest generated since the last update of priceAA and priceBB (done on depositXX/withdrawXX/harvest)
  /// useful for showing updated gains on frontends
  /// @dev this should always be >= of _tranchePrice(_tranche)
  /// @param _tranche address of the requested tranche
  /// @return _virtualPrice tranche price considering all interest
  function virtualPrice(address _tranche) public view returns (uint256 _virtualPrice) {
    // get both NAVs, because we need the total NAV anyway
    uint256 _lastNAVAA = lastNAVAA;
    uint256 _lastNAVBB = lastNAVBB;

    (_virtualPrice, ) = _virtualPricesAux(
      _tranche,
      getContractValue(), // nav
      _lastNAVAA + _lastNAVBB, // lastNAV
      _tranche == AATranche ? _lastNAVAA : _lastNAVBB, // lastTrancheNAV
      trancheAPRSplitRatio
    );
  }

  /// @notice returns an array of tokens used to incentive tranches via IIdleCDOTrancheRewards
  /// @return array with addresses of incentiveTokens (can be empty)
  function getIncentiveTokens() external view returns (address[] memory) {
    return incentiveTokens;
  }

  // ###############
  // Internal
  // ###############

  /// @notice method used to deposit `token` and mint tranche tokens
  /// Ideally users should deposit right after an `harvest` call to maximize profit
  /// @dev this contract must be approved to spend at least _amount of `token` before calling this method
  /// automatically reverts on lending provider default (_strategyPrice decreased)
  /// @param _amount amount of underlyings (`token`) to deposit
  /// @param _tranche tranche address
  /// @return _minted number of tranche tokens minted
  function _deposit(uint256 _amount, address _tranche) internal whenNotPaused returns (uint256 _minted) {
    if (_amount == 0) {
      return _minted;
    }
    // check that we are not depositing more than the contract available limit
    _guarded(_amount);
    // set _lastCallerBlock hash
    _updateCallerBlock();
    // check if _strategyPrice decreased
    _checkDefault();
    // interest accrued since last depositXX/withdrawXX/harvest is splitted between AA and BB
    // according to trancheAPRSplitRatio. NAVs of AA and BB are updated and tranche
    // prices adjusted accordingly
    _updateAccounting();
    // get underlyings from sender
    IERC20Detailed(token).safeTransferFrom(msg.sender, address(this), _amount);
    // mint tranche tokens according to the current tranche price
    _minted = _mintShares(_amount, msg.sender, _tranche);
  }

  /// @notice this method is called on depositXX/withdrawXX/harvest and
  /// updates the accounting of the contract and effectively splits the yield between the
  /// AA and BB tranches
  /// @dev this method:
  /// - update tranche prices (priceAA and priceBB)
  /// - update net asset value for both tranches (lastNAVAA and lastNAVBB)
  /// - update fee accounting (unclaimedFees)
  function _updateAccounting() internal {
    uint256 _lastNAVAA = lastNAVAA;
    uint256 _lastNAVBB = lastNAVBB;
    uint256 _lastNAV = _lastNAVAA + _lastNAVBB;
    uint256 nav = getContractValue();
    uint256 _aprSplitRatio = trancheAPRSplitRatio;

    // If gain is > 0, then collect some fees in `unclaimedFees`
    if (nav > _lastNAV) {
      unclaimedFees += (nav - _lastNAV) * fee / FULL_ALLOC;
    }

    (uint256 _priceAA, uint256 _totalAAGain) = _virtualPricesAux(AATranche, nav, _lastNAV, _lastNAVAA, _aprSplitRatio);
    (uint256 _priceBB, uint256 _totalBBGain) = _virtualPricesAux(BBTranche, nav, _lastNAV, _lastNAVBB, _aprSplitRatio);

    lastNAVAA += _totalAAGain;
    lastNAVBB += _totalBBGain;
    priceAA = _priceAA;
    priceBB = _priceBB;
  }

  /// @notice calculates the NAV for a tranche considering the interest that is yet to be splitted
  /// @param _tranche address of the requested tranche
  /// @return net asset value, in underlying tokens, for _tranche considering all nav
  function _virtualBalance(address _tranche) internal view returns (uint256) {
    // balance is: tranche supply * virtual tranche price
    return IdleCDOTranche(_tranche).totalSupply() * virtualPrice(_tranche) / ONE_TRANCHE_TOKEN;
  }

  /// @notice calculates the current tranches price considering the interest that is yet to be splitted and the
  /// total gain for a specific tranche
  /// @param _tranche address of the requested tranche
  /// @param _nav current NAV
  /// @param _lastNAV last saved NAV
  /// @param _lastTrancheNAV last saved tranche NAV
  /// @param _trancheAPRSplitRatio APR split ratio for AA tranche
  /// @return _virtualPrice tranche price considering all interest
  /// @return _totalTrancheGain tranche gain since last update
  function _virtualPricesAux(
    address _tranche,
    uint256 _nav,
    uint256 _lastNAV,
    uint256 _lastTrancheNAV,
    uint256 _trancheAPRSplitRatio
  ) internal view returns (uint256 _virtualPrice, uint256 _totalTrancheGain) {
    // If there is no gain return the current price
    if (_nav <= _lastNAV) {
      return (_tranchePrice(_tranche), 0);
    }

    // Check if there are tranche holders
    uint256 trancheSupply = IdleCDOTranche(_tranche).totalSupply();
    if (_lastNAV == 0 || trancheSupply == 0) {
      return (oneToken, 0);
    }
    // In order to correctly split the interest generated between AA and BB tranche holders
    // (according to the trancheAPRSplitRatio) we need to know how much interest we gained
    // since the last price update (during a depositXX/withdrawXX/harvest)
    // To do that we need to get the current value of the assets in this contract
    // and the last saved one (always during a depositXX/withdrawXX/harvest)

    // Calculate the total gain
    uint256 totalGain = _nav - _lastNAV;
    // Remove performance fee
    totalGain -= totalGain * fee / FULL_ALLOC;

    address _AATranche = AATranche;
    bool _isAATranche = _tranche == _AATranche;
    // Get the supply of the other tranche and
    // if it's 0 then give all gain to the current `_tranche` holders
    if (IdleCDOTranche(_isAATranche ? BBTranche : _AATranche).totalSupply() == 0) {
      _totalTrancheGain = totalGain;
    } else {
      // Split the net gain, with precision loss favoring the AA tranche.
      uint256 totalBBGain = totalGain * (FULL_ALLOC - _trancheAPRSplitRatio) / FULL_ALLOC;
      // The new NAV for the tranche is old NAV + total gain for the tranche
      _totalTrancheGain = _isAATranche ? (totalGain - totalBBGain) : totalBBGain;
    }
    // Split the new NAV (_lastTrancheNAV + _totalTrancheGain) per tranche token
    _virtualPrice = (_lastTrancheNAV + _totalTrancheGain) * ONE_TRANCHE_TOKEN / trancheSupply;
  }

  /// @notice mint tranche tokens and updates tranche last NAV
  /// @param _amount, in underlyings, to convert in tranche tokens
  /// @param _to receiver address of the newly minted tranche tokens
  /// @param _tranche tranche address
  /// @return _minted number of tranche tokens minted
  function _mintShares(uint256 _amount, address _to, address _tranche) internal returns (uint256 _minted) {
    // calculate # of tranche token to mint based on current tranche price: _amount / tranchePrice
    _minted = _amount * ONE_TRANCHE_TOKEN / _tranchePrice(_tranche);
    IdleCDOTranche(_tranche).mint(_to, _minted);
    // update NAV with the _amount of underlyings added
    if (_tranche == AATranche) {
      lastNAVAA += _amount;
    } else {
      lastNAVBB += _amount;
    }
  }

  /// @notice convert fees (`unclaimedFees`) in AA tranche tokens
  /// Tranche tokens are then automatically staked in the relative IdleCDOTrancheRewards contact if present
  /// @dev this will be called only during harvests
  function _depositFees() internal {
    uint256 _amount = unclaimedFees;
    if (_amount > 0) {
      address stakingRewards = AAStaking;
      bool isStakingRewardsActive = stakingRewards != address(0);
      address _feeReceiver = feeReceiver;
      address _referral = referral;
      uint256 _referralAmount;
      if (_referral != address(0)) {
        // If the contract has a referral, then we give the referral a share of the fees (in AA tranche tokens)
        _referralAmount = _amount * feeSplit / FULL_ALLOC;
        _mintShares(_referralAmount, _referral, AATranche);
      }

      // mint tranches tokens to this contract
      uint256 _minted = _mintShares(_amount - _referralAmount,
        isStakingRewardsActive ? address(this) : _feeReceiver,
        // Mint AA tranche tokens as fees
        AATranche
      );
      // reset unclaimedFees counter
      unclaimedFees = 0;

      // auto stake fees in staking contract for feeReceiver
      if (isStakingRewardsActive) {
        IIdleCDOTrancheRewards(stakingRewards).stakeFor(_feeReceiver, _minted);
      }
    }
  }

  /// @notice It allows users to burn their tranche token and redeem their principal + interest back
  /// @dev automatically reverts on lending provider default (_strategyPrice decreased).
  /// @param _amount in tranche tokens
  /// @param _tranche tranche address
  /// @return toRedeem number of underlyings redeemed
  function _withdraw(uint256 _amount, address _tranche) internal nonReentrant returns (uint256 toRedeem) {
    // check if a deposit is made in the same block from the same user
    _checkSameTx();
    // check if _strategyPrice decreased
    _checkDefault();
    // accrue interest to tranches and updates tranche prices
    _updateAccounting();
    // redeem all user balance if 0 is passed as _amount
    if (_amount == 0) {
      _amount = IERC20Detailed(_tranche).balanceOf(msg.sender);
    }
    require(_amount > 0, '0');
    address _token = token;
    // get current available unlent balance
    uint256 balanceUnderlying = _contractTokenBalance(_token);
    // Calculate the amount to redeem
    toRedeem = _amount * _tranchePrice(_tranche) / ONE_TRANCHE_TOKEN;
    if (toRedeem > balanceUnderlying) {
      // if the unlent balance is not enough we try to redeem what's missing directly from the strategy
      // and then add it to the current unlent balance
      // NOTE: A difference of up to 100 wei due to rounding is tolerated
      toRedeem = _liquidate(toRedeem - balanceUnderlying, revertIfTooLow) + balanceUnderlying;
    }
    // burn tranche token
    IdleCDOTranche(_tranche).burn(msg.sender, _amount);
    // send underlying to msg.sender
    IERC20Detailed(_token).safeTransfer(msg.sender, toRedeem);

    // update NAV with the _amount of underlyings removed
    if (_tranche == AATranche) {
      lastNAVAA -= toRedeem;
    } else {
      lastNAVBB -= toRedeem;
    }
  }

  /// @dev check if _strategyPrice is decreased since last update and updates last saved strategy price
  function _checkDefault() internal {
    uint256 currPrice = _strategyPrice();
    if (!skipDefaultCheck) {
      require(lastStrategyPrice <= currPrice, "4");
    }
    lastStrategyPrice = currPrice;
  }

  /// @return strategy price, in underlyings
  function _strategyPrice() internal view returns (uint256) {
    return IIdleCDOStrategy(strategy).price();
  }

  /// @dev this should liquidate at least _amount of `token` from the lending provider or revertIfNeeded
  /// @param _amount in underlying tokens
  /// @param _revertIfNeeded flag whether to revert or not if the redeemed amount is not enough
  /// @return _redeemedTokens number of underlyings redeemed
  function _liquidate(uint256 _amount, bool _revertIfNeeded) internal returns (uint256 _redeemedTokens) {
    _redeemedTokens = IIdleCDOStrategy(strategy).redeemUnderlying(_amount);
    if (_revertIfNeeded) {
      // keep 100 wei as margin for rounding errors
      require(_redeemedTokens + 100 >= _amount, '5');
    }
    if (_redeemedTokens > _amount) {
      _redeemedTokens = _amount;
    }
  }

  /// @notice sends rewards to the tranche rewards staking contracts
  /// @dev this method is called only during harvests
  function _updateIncentives() internal {
    // Read state variables only once to save gas
    uint256 _trancheIdealWeightRatio = trancheIdealWeightRatio;
    uint256 _trancheAPRSplitRatio = trancheAPRSplitRatio;
    uint256 _idealRange = idealRange;
    address _BBStaking = BBStaking;
    address _AAStaking = AAStaking;
    bool _isBBStakingActive = _BBStaking != address(0);
    bool _isAAStakingActive = _AAStaking != address(0);

    uint256 currAARatio;
    if (_isBBStakingActive && _isAAStakingActive) {
      currAARatio = getCurrentAARatio();
    }

    // Check if BB tranches should be rewarded (if AA ratio is too high)
    if (_isBBStakingActive && (!_isAAStakingActive || (currAARatio > (_trancheIdealWeightRatio + _idealRange)))) {
      // give more rewards to BB holders, ie send some rewards to BB Staking contract
      return _depositIncentiveToken(_BBStaking, FULL_ALLOC);
    }
    // Check if AA tranches should be rewarded (id AA ratio is too low)
    if (_isAAStakingActive && (!_isBBStakingActive || (currAARatio < (_trancheIdealWeightRatio - _idealRange)))) {
      // give more rewards to AA holders, ie send some rewards to AA Staking contract
      return _depositIncentiveToken(_AAStaking, FULL_ALLOC);
    }

    // Split rewards according to trancheAPRSplitRatio in case the ratio between
    // AA and BB is already ideal
    if (_isAAStakingActive) {
      // NOTE: the order is important here, first there must be the deposit for AA rewards,
      // if staking contract for AA is present
      _depositIncentiveToken(_AAStaking, _trancheAPRSplitRatio);
    }

    if (_isBBStakingActive) {
      // NOTE: here we should use FULL_ALLOC directly and not (FULL_ALLOC - _trancheAPRSplitRatio)
      // because contract balance for incentive tokens is fetched at each _depositIncentiveToken
      // and the balance for AA is already transferred
      _depositIncentiveToken(_BBStaking, FULL_ALLOC);
    }
  }

  /// @notice sends requested ratio of reward to a specific IdleCDOTrancheRewards contract
  /// @param _stakingContract address which will receive incentive Rewards
  /// @param _ratio ratio of the incentive token balance to send
  function _depositIncentiveToken(address _stakingContract, uint256 _ratio) internal {
    address[] memory _incentiveTokens = incentiveTokens;
    for (uint256 i = 0; i < _incentiveTokens.length; i++) {
      address _incentiveToken = _incentiveTokens[i];
      // calculates the requested _ratio of the current contract balance of
      // _incentiveToken to be sent to the IdleCDOTrancheRewards contract
      uint256 _reward = _contractTokenBalance(_incentiveToken) * _ratio / FULL_ALLOC;
      if (_reward > 0) {
        // call depositReward to actually let the IdleCDOTrancheRewards get the reward
        IIdleCDOTrancheRewards(_stakingContract).depositReward(_incentiveToken, _reward);
      }
    }
  }

  /// @notice method used to sell `_rewardToken` for `_token` on uniswap
  /// @param _rewardToken address of the token to sell
  /// @param _path uniswap path for the trade
  /// @param _amount of `_rewardToken` to sell
  /// @param _minAmount min amount of `_token` to buy
  /// @return _amount of _rewardToken sold
  /// @return _amount received for the sell
  function _sellReward(address _rewardToken, address[] memory _path, uint256 _amount, uint256 _minAmount)
    internal
    returns (uint256, uint256) {
    // If 0 is passed as sell amount, we get the whole contract balance
    if (_amount == 0) {
      _amount = _contractTokenBalance(_rewardToken);
    }
    if (_amount == 0) {
      return (0, 0);
    }

    IUniswapV2Router02 _uniRouter = uniswapRouterV2;
    // approve the uniswap router to spend our reward
    IERC20Detailed(_rewardToken).safeIncreaseAllowance(address(_uniRouter), _amount);
    // do the trade with all `_rewardToken` in this contract
    uint256[] memory _amounts = _uniRouter.swapExactTokensForTokens(
      _amount,
      _minAmount,
      _path,
      address(this),
      block.timestamp + 1
    );
    // return the amount swapped and the amount received
    return (_amounts[0], _amounts[_amounts.length - 1]);
  }

  /// @notice method used to sell all sellable rewards for `_token` on uniswap
  /// @param _strategy IIdleCDOStrategy stategy instance
  /// @param _sellAmounts array with amounts of rewards to sell
  /// @param _minAmount array with amounts of _token buy for each reward sold. (should have the same length as _sellAmounts)
  /// @param _skipReward array of flags for skipping the market sell of specific rewards (should have the same length as _sellAmounts)
  /// @return _soldAmounts array with amounts of rewards actually sold
  /// @return _swappedAmounts array with amounts of _token actually bought
  /// @return _totSold total rewards sold in `_token`
  function _sellAllRewards(IIdleCDOStrategy _strategy, uint256[] memory _sellAmounts, uint256[] memory _minAmount, bool[] memory _skipReward)
    internal
    returns (uint256[] memory _soldAmounts, uint256[] memory _swappedAmounts, uint256 _totSold) {
    // Fetch state variables once to save gas
    address[] memory _incentiveTokens = incentiveTokens;
    // get all rewards addresses
    address[] memory _rewards = _strategy.getRewardTokens();
    address _rewardToken;
    // Prepare path for uniswap trade
    address[] memory _path = new address[](3);
    // _path[0] will be the reward token to sell
    _path[1] = weth;
    _path[2] = token;
    // Initialize the return array, containing the amounts received after swapping reward tokens
    _soldAmounts = new uint256[](_rewards.length);
    _swappedAmounts = new uint256[](_rewards.length);
    // loop through all reward tokens
    for (uint256 i = 0; i < _rewards.length; i++) {
      _rewardToken = _rewards[i];
      // check if it should be sold or not
      if (_skipReward[i] || _includesAddress(_incentiveTokens, _rewardToken)) { continue; }
      // do not sell stkAAVE but only AAVE if present
      if (_rewardToken == stkAave) {
        _rewardToken = AAVE;
      }
      // set token to sell in the uniswap path
      _path[0] = _rewardToken;
      // Market sell _rewardToken in this contract for _token
      (_soldAmounts[i], _swappedAmounts[i]) = _sellReward(_rewardToken, _path, _sellAmounts[i], _minAmount[i]);
      _totSold += _swappedAmounts[i];
    }
  }

  /// @param _tranche tranche address
  /// @return last saved tranche price, in underlyings
  function _tranchePrice(address _tranche) internal view returns (uint256) {
    if (IdleCDOTranche(_tranche).totalSupply() == 0) {
      return oneToken;
    }
    return _tranche == AATranche ? priceAA : priceBB;
  }

  /// @notice returns the current apr for a tranche based on trancheAPRSplitRatio and the provided AA ratio
  /// @dev the apr for a tranche can be higher than the strategy apr
  /// @param _tranche tranche token address
  /// @param _AATrancheSplitRatio AA split ratio used for calculations
  /// @return apr for the specific tranche
  function _getApr(address _tranche, uint256 _AATrancheSplitRatio) internal view returns (uint256) {
    uint256 stratApr = IIdleCDOStrategy(strategy).getApr();
    uint256 _trancheAPRSplitRatio = trancheAPRSplitRatio;
    bool isAATranche = _tranche == AATranche;
    if (_AATrancheSplitRatio == 0) {
      // if there are no AA tranches, apr for AA is 0 (all apr to BB and it will be equal to stratApr)
      return isAATranche ? 0 : stratApr;
    }
    return isAATranche ?
      // AA apr is: stratApr * AAaprSplitRatio / AASplitRatio
      stratApr * _trancheAPRSplitRatio / _AATrancheSplitRatio :
      // BB apr is: stratApr * BBaprSplitRatio / BBSplitRatio -> where
      // BBaprSplitRatio is: (FULL_ALLOC - _trancheAPRSplitRatio) and
      // BBSplitRatio is: (FULL_ALLOC - _AATrancheSplitRatio)
      stratApr * (FULL_ALLOC - _trancheAPRSplitRatio) / (FULL_ALLOC - _AATrancheSplitRatio);
  }

  /// @return _locked amount of harvested rewards that are still not available to be redeemed
  function _lockedRewards() internal view returns (uint256 _locked) {
    uint256 _releaseBlocksPeriod = releaseBlocksPeriod;
    uint256 _blocksSinceLastHarvest = block.number - latestHarvestBlock;
    uint256 _harvestedRewards = harvestedRewards;

    // NOTE: _harvestedRewards is never set to 0, but rather to 1 to save some gas
    if (_harvestedRewards > 1 && _blocksSinceLastHarvest < _releaseBlocksPeriod) {
      // progressively release harvested rewards
      _locked = _harvestedRewards * (_releaseBlocksPeriod - _blocksSinceLastHarvest) / _releaseBlocksPeriod;
    }
  }

  /// @notice used to start the cooldown for unstaking stkAAVE and claiming AAVE rewards (for the contract itself)
  function _claimStkAave() internal {
    if (!isStkAAVEActive) {
      return;
    }

    IStakedAave _stkAave = IStakedAave(stkAave);
    uint256 _stakersCooldown = _stkAave.stakersCooldowns(address(this));
    // If there is a pending cooldown:
    if (_stakersCooldown > 0) {
      uint256 _cooldownEnd = _stakersCooldown + _stkAave.COOLDOWN_SECONDS();
      // If it is over
      if (_cooldownEnd < block.timestamp) {
        // If the unstake window is active
        if (block.timestamp - _cooldownEnd <= _stkAave.UNSTAKE_WINDOW()) {
          // redeem stkAave AND begin new cooldown
          _stkAave.redeem(address(this), type(uint256).max);
        }
      } else {
        // If it is not over, do nothing
        return;
      }
    }

    // Pull new stkAAVE rewards
    IIdleCDOStrategy(strategy).pullStkAAVE();

    // If there's no pending cooldown or we just redeem the prev locked rewards,
    // then begin a new cooldown
    if (_stkAave.balanceOf(address(this)) > 0) {
      // start a new cooldown
      _stkAave.cooldown();
    }
  }

  // ###################
  // Protected
  // ###################

  /// @notice This method is used to lend user funds in the lending provider through the IIdleCDOStrategy and update tranches incentives.
  /// The method:
  /// - redeems rewards (if any) from the lending provider
  /// - converts the rewards NOT present in the `incentiveTokens` array, in underlyings through uniswap v2
  /// - calls _updateAccounting to update the accounting of the system with the new underlyings received
  /// - it then convert fees in tranche tokens and stake tranche tokens in the IdleCDOTrancheRewards if any
  /// - sends the correct amount of `incentiveTokens` to the each of the IdleCDOTrancheRewards contracts
  /// - Finally it deposits the (initial unlent balance + the underlyings get from uniswap - fees) in the
  ///   lending provider through the IIdleCDOStrategy `deposit` call
  /// The method will be called by an external, whitelisted, keeper bot which will call the method sistematically (eg once a day)
  /// @dev can be called only by the rebalancer or the owner
  /// @param _skipFlags array of flags, [0] = skip reward redemption, [1] = skip incentives update, [2] = skip fee deposit, [3] = skip all
  /// @param _skipReward array of flags for skipping the market sell of specific rewards. Length should be equal to the `IIdleCDOStrategy(strategy).getRewardTokens()` array
  /// @param _minAmount array of min amounts for uniswap trades. Lenght should be equal to the _skipReward array
  /// @param _sellAmounts array of amounts (of reward tokens) to sell on uniswap. Lenght should be equal to the _minAmount array
  /// if a sellAmount is 0 the whole contract balance for that token is swapped
  /// @param _extraData bytes to be passed to the redeemRewards call
  /// @return _res array of arrays with the following elements:
  ///   [0] _soldAmounts array with amounts of rewards actually sold
  ///   [1] _swappedAmounts array with amounts of _token actually bought
  ///   [2] _redeemedRewards array with amounts of rewards redeemed
  function harvest(
    // _skipFlags[0] _skipRedeem,
    // _skipFlags[1] _skipIncentivesUpdate,
    // _skipFlags[2] _skipFeeDeposit,
    // _skipFlags[3] _skipRedeem && _skipIncentivesUpdate && _skipFeeDeposit,
    bool[] calldata _skipFlags,
    bool[] calldata _skipReward,
    uint256[] calldata _minAmount,
    uint256[] calldata _sellAmounts,
    bytes calldata _extraData
  ) external
    returns (uint256[][] memory _res) {
    _checkOnlyOwnerOrRebalancer();
    // initalize the returned array (elements will be [_soldAmounts, _swappedAmounts, _redeemedRewards])
    _res = new uint256[][](3);
    // Fetch state variable once to save gas
    IIdleCDOStrategy _strategy = IIdleCDOStrategy(strategy);
    // Check whether to redeem rewards from strategy or not
    if (!_skipFlags[3]) {
      uint256 _totSold;

      if (!_skipFlags[0]) {
        // Redeem all rewards associated with the strategy
        _res[2] = _strategy.redeemRewards(_extraData);
        // Redeem unlocked AAVE if any and start a new cooldown for stkAAVE
        _claimStkAave();
        // Sell rewards
        (_res[0], _res[1], _totSold) = _sellAllRewards(_strategy, _sellAmounts, _minAmount, _skipReward);
      }
      // update last saved harvest block number
      latestHarvestBlock = block.number;
      // update harvested rewards value (avoid setting it to 0 to save some gas)
      harvestedRewards = _totSold == 0 ? 1 : _totSold;

      // split converted rewards if any and update tranche prices
      // NOTE: harvested rewards won't be counted directly but released over time
      _updateAccounting();

      if (!_skipFlags[2]) {
        // Get fees in the form of totalSupply diluition
        _depositFees();
      }

      if (!_skipFlags[1]) {
        // Update tranche incentives distribution and send rewards to staking contracts
        _updateIncentives();
      }
    }

    // Deposit the remaining balance in the lending provider and 
    // keep some unlent balance for cheap redeems and as reserve of last resort
    uint256 underlyingBal = _contractTokenBalance(token);
    uint256 idealUnlent = getContractValue() * unlentPerc / FULL_ALLOC;
    if (underlyingBal > idealUnlent) {
      // Put unlent balance at work in the lending provider
      _strategy.deposit(underlyingBal - idealUnlent);
    }
  }

  /// @notice method used to redeem underlyings from the lending provider
  /// @dev can be called only by the rebalancer or the owner
  /// @param _amount in underlyings to liquidate from lending provider
  /// @param _revertIfNeeded flag to revert if amount liquidated is too low
  /// @return liquidated amount in underlyings
  function liquidate(uint256 _amount, bool _revertIfNeeded) external returns (uint256) {
    _checkOnlyOwnerOrRebalancer();
    return _liquidate(_amount, _revertIfNeeded);
  }

  // ###################
  // onlyOwner
  // ###################

  /// @param _allowed flag to allow AA withdraws
  function setAllowAAWithdraw(bool _allowed) external {
    _checkOnlyOwner();
    allowAAWithdraw = _allowed;
  }

  /// @param _allowed flag to allow BB withdraws
  function setAllowBBWithdraw(bool _allowed) external {
    _checkOnlyOwner();
    allowBBWithdraw = _allowed;
  }

  /// @param _allowed flag to enable the 'default' check (whether _strategyPrice decreased or not)
  function setSkipDefaultCheck(bool _allowed) external {
    _checkOnlyOwner();
    skipDefaultCheck = _allowed;
  }

  /// @param _allowed flag to enable the check if redeemed amount during liquidations is enough
  function setRevertIfTooLow(bool _allowed) external {
    _checkOnlyOwner();
    revertIfTooLow = _allowed;
  }

  /// @notice updates the strategy used (potentially changing the lending protocol used)
  /// @dev it's REQUIRED to liquidate / redeem everything from the lending provider before changing strategy
  /// if the leding provider of the new strategy is different from the current one
  /// it's also REQUIRED to transfer out any incentive tokens accrued if those are changed from the current ones
  /// if the lending provider is changed
  /// @param _strategy new strategy address
  /// @param _incentiveTokens array of incentive tokens addresses
  function setStrategy(address _strategy, address[] memory _incentiveTokens) external {
    _checkOnlyOwner();

    require(_strategy != address(0), '0');
    IERC20Detailed _token = IERC20Detailed(token);
    // revoke allowance for the current strategy
    address _currStrategy = strategy;
    _removeAllowance(address(_token), _currStrategy);
    _removeAllowance(strategyToken, _currStrategy);
    // Updated strategy variables
    strategy = _strategy;
    // Update incentive tokens
    incentiveTokens = _incentiveTokens;
    // Update strategyToken
    address _newStrategyToken = IIdleCDOStrategy(_strategy).strategyToken();
    strategyToken = _newStrategyToken;
    // Approve underlyingToken
    _allowUnlimitedSpend(address(_token), _strategy);
    // Approve the new strategy to transfer strategyToken out from this contract
    _allowUnlimitedSpend(_newStrategyToken, _strategy);
    // Update last strategy price
    lastStrategyPrice = _strategyPrice();
  }

  /// @param _rebalancer new rebalancer address
  function setRebalancer(address _rebalancer) external {
    _checkOnlyOwner();
    require((rebalancer = _rebalancer) != address(0), '0');
  }

  /// @param _feeReceiver new fee receiver address
  function setFeeReceiver(address _feeReceiver) external {
    _checkOnlyOwner();
    require((feeReceiver = _feeReceiver) != address(0), '0');
  }

  /// @notice set new referral address
  /// @dev can be called only by the owner
  /// @param _referral new referral address (can be address(0))
  function setReferral(address _referral) external {
    _checkOnlyOwner();
    referral = _referral;
  }

  /// @param _guardian new guardian (pauser) address
  function setGuardian(address _guardian) external {
    _checkOnlyOwner();
    require((guardian = _guardian) != address(0), '0');
  }

  /// @param _fee new fee
  function setFee(uint256 _fee) external {
    _checkOnlyOwner();
    require((fee = _fee) <= MAX_FEE, '7');
  }

  /// @notice set fee split between feeReceiver and referral (if any). If referral is not set, fee goes to feeReceiver.
  /// @dev can be called only by the owner
  /// @param _feeSplit new fee split
  function setFeeSplit(uint256 _feeSplit) external {
    _checkOnlyOwner();
    require((feeSplit = _feeSplit) <= FULL_ALLOC, '8');
  }

  /// @param _unlentPerc new unlent percentage
  function setUnlentPerc(uint256 _unlentPerc) external {
    _checkOnlyOwner();
    require((unlentPerc = _unlentPerc) <= FULL_ALLOC, '7');
  }

  /// @param _releaseBlocksPeriod new # of blocks after an harvest during which
  /// harvested rewards gets progressively redistriburted to users
  function setReleaseBlocksPeriod(uint256 _releaseBlocksPeriod) external {
    _checkOnlyOwner();
    releaseBlocksPeriod = _releaseBlocksPeriod;
  }

  /// @param _isStkAAVEActive whether the contract receive stkAAVE or not
  function setIsStkAAVEActive(bool _isStkAAVEActive) external {
    _checkOnlyOwner();
    isStkAAVEActive = _isStkAAVEActive;
  }

  /// @param _idealRange new ideal range
  function setIdealRange(uint256 _idealRange) external {
    _checkOnlyOwner();
    require((idealRange = _idealRange) <= FULL_ALLOC, '7');
  }

  /// @param _trancheAPRSplitRatio new apr split ratio
  function setTrancheAPRSplitRatio(uint256 _trancheAPRSplitRatio) external {
    _checkOnlyOwner();
    require((trancheAPRSplitRatio = _trancheAPRSplitRatio) <= FULL_ALLOC, '7');
  }

  /// @param _trancheIdealWeightRatio new ideal weight ratio (for incentives)
  function setTrancheIdealWeightRatio(uint256 _trancheIdealWeightRatio) external {
    _checkOnlyOwner();
    require((_trancheIdealWeightRatio) >= idealRange, '5');
    require((trancheIdealWeightRatio = _trancheIdealWeightRatio) <= (FULL_ALLOC - idealRange), '7');
  }

  /// @dev it's REQUIRED to transfer out any incentive tokens accrued before
  /// @param _incentiveTokens array with new incentive tokens
  function setIncentiveTokens(address[] memory _incentiveTokens) external {
    _checkOnlyOwner();
    incentiveTokens = _incentiveTokens;
  }

  /// @notice Set tranche Rewards contract addresses (for tranches incentivization)
  /// @param _AAStaking IdleCDOTrancheRewards contract address for AA tranches
  /// @param _BBStaking IdleCDOTrancheRewards contract address for BB tranches
  function setStakingRewards(address _AAStaking, address _BBStaking) external {
    _checkOnlyOwner();
    // Read state variable once
    address _AATranche = AATranche;
    address _BBTranche = BBTranche;
    address[] memory _incentiveTokens = incentiveTokens;
    address _currAAStaking = AAStaking;
    address _currBBStaking = BBStaking;
    bool _isAAStakingActive = _currAAStaking != address(0);
    bool _isBBStakingActive = _currBBStaking != address(0);
    address _incentiveToken;
    // Remove allowance for incentive tokens for current staking contracts
    for (uint256 i = 0; i < _incentiveTokens.length; i++) {
      _incentiveToken = _incentiveTokens[i];
      if (_isAAStakingActive) {
        _removeAllowance(_incentiveToken, _currAAStaking);
      }
      if (_isBBStakingActive) {
        _removeAllowance(_incentiveToken, _currBBStaking);
      }
    }
    // Remove allowace for tranche tokens (used for staking fees)
    if (_isAAStakingActive && _AATranche != address(0)) {
      _removeAllowance(_AATranche, _currAAStaking);
    }
    if (_isBBStakingActive && _BBTranche != address(0)) {
      _removeAllowance(_BBTranche, _currBBStaking);
    }

    // Update staking contract addresses
    AAStaking = _AAStaking;
    BBStaking = _BBStaking;

    _isAAStakingActive = _AAStaking != address(0);
    _isBBStakingActive = _BBStaking != address(0);

    // Increase allowance for incentiveTokens
    for (uint256 i = 0; i < _incentiveTokens.length; i++) {
      _incentiveToken = _incentiveTokens[i];
      // Approve each staking contract to spend each incentiveToken on beahlf of this contract
      if (_isAAStakingActive) {
        _allowUnlimitedSpend(_incentiveToken, _AAStaking);
      }
      if (_isBBStakingActive) {
        _allowUnlimitedSpend(_incentiveToken, _BBStaking);
      }
    }

    // Increase allowance for tranche tokens (used for staking fees)
    if (_isAAStakingActive && _AATranche != address(0)) {
      _allowUnlimitedSpend(_AATranche, _AAStaking);
    }
    if (_isBBStakingActive && _BBTranche != address(0)) {
      _allowUnlimitedSpend(_BBTranche, _BBStaking);
    }
  }

  /// @notice pause deposits and redeems for all classes of tranches
  /// @dev can be called by both the owner and the guardian
  function emergencyShutdown() external {
    _checkOnlyOwnerOrGuardian();
    // prevent deposits
    _pause();
    // prevent withdraws
    allowAAWithdraw = false;
    allowBBWithdraw = false;
    // Allow deposits/withdraws (once selectively re-enabled, eg for AA holders)
    // without checking for lending protocol default
    skipDefaultCheck = true;
    revertIfTooLow = true;
  }

  /// @notice Pauses deposits and redeems
  /// @dev can be called by both the owner and the guardian
  function pause() external  {
    _checkOnlyOwnerOrGuardian();
    _pause();
  }

  /// @notice Unpauses deposits and redeems
  /// @dev can be called by both the owner and the guardian
  function unpause() external {
    _checkOnlyOwnerOrGuardian();
    _unpause();
  }

  // ###################
  // Helpers
  // ###################

  /// @dev Check that the msg.sender is the either the owner or the guardian
  function _checkOnlyOwnerOrGuardian() internal view {
    require(msg.sender == guardian || msg.sender == owner(), "6");
  }

  /// @dev Check that the msg.sender is the either the owner or the rebalancer
  function _checkOnlyOwnerOrRebalancer() internal view {
    require(msg.sender == rebalancer || msg.sender == owner(), "6");
  }

  /// @notice returns the current balance of this contract for a specific token
  /// @param _token token address
  /// @return balance of `_token` for this contract
  function _contractTokenBalance(address _token) internal view returns (uint256) {
    return IERC20Detailed(_token).balanceOf(address(this));
  }

  /// @dev Set allowance for _token to 0 for _spender
  /// @param _token token address
  /// @param _spender spender address
  function _removeAllowance(address _token, address _spender) internal {
    IERC20Detailed(_token).safeApprove(_spender, 0);
  }

  /// @dev Set allowance for _token to unlimited for _spender
  /// @param _token token address
  /// @param _spender spender address
  function _allowUnlimitedSpend(address _token, address _spender) internal {
    IERC20Detailed(_token).safeIncreaseAllowance(_spender, type(uint256).max);
  }

  /// @dev Set last caller and block.number hash. This should be called at the beginning of the first function to protect
  function _updateCallerBlock() internal {
    _lastCallerBlock = keccak256(abi.encodePacked(tx.origin, block.number));
  }

  /// @dev Check that the second function is not called in the same tx from the same tx.origin
  function _checkSameTx() internal view {
    require(keccak256(abi.encodePacked(tx.origin, block.number)) != _lastCallerBlock, "8");
  }

  /// @dev this method is only used to check whether a token is an incentive tokens or not
  /// in the harvest call. The maximum number of element in the array will be a small number (eg at most 3-5)
  /// @param _array array of addresses to search for an element
  /// @param _val address of an element to find
  /// @return flag if the _token is an incentive token or not
  function _includesAddress(address[] memory _array, address _val) internal pure returns (bool) {
    for (uint256 i = 0; i < _array.length; i++) {
      if (_array[i] == _val) {
        return true;
      }
    }
    // explicit return to fix linter
    return false;
  }

  /// @notice concat 2 strings in a single one
  /// @param a first string
  /// @param b second string
  /// @return new string with a and b concatenated
  function _concat(string memory a, string memory b) internal pure returns (string memory) {
    return string(abi.encodePacked(a, b));
  }
}

pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal initializer {
        __Context_init_unchained();
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal initializer {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../../../utils/AddressUpgradeable.sol";

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

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

    function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

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

    function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

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

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.10;

interface IIdleCDOStrategy {
  function strategyToken() external view returns(address);
  function token() external view returns(address);
  function tokenDecimals() external view returns(uint256);
  function oneToken() external view returns(uint256);
  function redeemRewards(bytes calldata _extraData) external returns(uint256[] memory);
  function pullStkAAVE() external returns(uint256);
  function price() external view returns(uint256);
  function getRewardTokens() external view returns(address[] memory);
  function deposit(uint256 _amount) external returns(uint256);
  // _amount in `strategyToken`
  function redeem(uint256 _amount) external returns(uint256);
  // _amount in `token`
  function redeemUnderlying(uint256 _amount) external returns(uint256);
  function getApr() external view returns(uint256);
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.10;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

interface IERC20Detailed is IERC20Upgradeable {
  function name() external view returns(string memory);
  function symbol() external view returns(string memory);
  function decimals() external view returns(uint256);
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.10;

interface IIdleCDOTrancheRewards {
  function stake(uint256 _amount) external;
  function stakeFor(address _user, uint256 _amount) external;
  function unstake(uint256 _amount) external;
  function depositReward(address _reward, uint256 _amount) external;
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.10;

interface IStakedAave {
  function COOLDOWN_SECONDS() external view returns (uint256);
  function UNSTAKE_WINDOW() external view returns (uint256);
  function redeem(address to, uint256 amount) external;
  function transfer(address to, uint256 amount) external returns (bool);
  function cooldown() external;
  function balanceOf(address) external view returns (uint256);
  function stakersCooldowns(address) external view returns (uint256);
}

//SPDX-License-Identifier: Apache 2.0
pragma solidity 0.8.10;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/// @notice This abstract contract is used to add an updatable limit on the total value locked
/// that the contract can have. It also have an emergency method that allows the owner to pull
/// funds into predefined recovery address
/// @dev Inherit this contract and add the _guarded method to the child contract
abstract contract GuardedLaunchUpgradable is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable {
  using SafeERC20Upgradeable for IERC20Upgradeable;

  // ERROR MESSAGES:
  // 0 = is 0
  // 1 = already initialized
  // 2 = Contract limit reached

  // TVL limit in underlying value
  uint256 public limit;
  // recovery address
  address public governanceRecoveryFund;

  /// @param _limit TVL limit. (0 means unlimited)
  /// @param _governanceRecoveryFund recovery address
  /// @param _owner owner address
  function __GuardedLaunch_init(uint256 _limit, address _governanceRecoveryFund, address _owner) internal {
    require(_governanceRecoveryFund != address(0), '0');
    require(_owner != address(0), '0');
    // Initialize inherited contracts
    OwnableUpgradeable.__Ownable_init();
    ReentrancyGuardUpgradeable.__ReentrancyGuard_init();
    // Initialize state variables
    limit = _limit;
    governanceRecoveryFund = _governanceRecoveryFund;
    // Transfer ownership
    transferOwnership(_owner);
  }

  /// @notice this check should be called inside the child contract on deposits to check that the
  /// TVL didn't exceed a threshold
  /// @param _amount new amount to deposit
  function _guarded(uint256 _amount) internal view {
    uint256 _limit = limit;
    if (_limit == 0) {
      return;
    }
    require(getContractValue() + _amount <= _limit, '2');
  }

  /// @dev Check that the second function is not called in the same tx from the same tx.origin
  function _checkOnlyOwner() internal view {
    require(owner() == msg.sender, '6');
  }

  /// @notice abstract method, should return the TVL in underlyings
  function getContractValue() public virtual view returns (uint256);

  /// @notice set contract TVL limit
  /// @param _limit limit in underlying value, 0 means no limit
  function _setLimit(uint256 _limit) external {
    _checkOnlyOwner();
    limit = _limit;
  }

  /// @notice Emergency method, tokens gets transferred to the governanceRecoveryFund address
  /// @param _token address of the token to transfer
  /// @param _value amount to transfer
  function transferToken(address _token, uint256 _value) external {
    _checkOnlyOwner();
    IERC20Upgradeable(_token).safeTransfer(governanceRecoveryFund, _value);
  }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/// @dev ERC20 representing a tranche token
contract IdleCDOTranche is ERC20 {
  // allowed minter address
  address public minter;

  /// @param _name tranche name
  /// @param _symbol tranche symbol
  constructor(
    string memory _name, // eg. IdleDAI
    string memory _symbol // eg. IDLEDAI
  ) ERC20(_name, _symbol) {
    // minter is msg.sender which is IdleCDO (in initialize)
    minter = msg.sender;
  }

  /// @param account that should receive the tranche tokens
  /// @param amount of tranche tokens to mint
  function mint(address account, uint256 amount) external {
    require(msg.sender == minter, 'TRANCHE:!AUTH');
    _mint(account, amount);
  }

  /// @param account that should have the tranche tokens burned
  /// @param amount of tranche tokens to burn
  function burn(address account, uint256 amount) external {
    require(msg.sender == minter, 'TRANCHE:!AUTH');
    _burn(account, amount);
  }
}

File 11 of 21 : IdleCDOStorage.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.10;

import '@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol';

contract IdleCDOStorage {
  // constant to represent 100%
  uint256 public constant FULL_ALLOC = 100000;
  // max fee, relative to FULL_ALLOC
  uint256 public constant MAX_FEE = 20000;
  // one token
  uint256 public constant ONE_TRANCHE_TOKEN = 10**18;
  // variable used to save the last tx.origin and block.number
  bytes32 internal _lastCallerBlock;
  // variable used to save the block of the latest harvest
  uint256 internal latestHarvestBlock;
  // WETH address
  address public weth;
  // tokens used to incentivize the idle tranche ideal ratio
  address[] public incentiveTokens;
  // underlying token (eg DAI)
  address public token;
  // address that can only pause/unpause the contract in case of emergency
  address public guardian;
  // one `token` (eg for DAI 10**18)
  uint256 public oneToken;
  // address that can call the 'harvest' method and lend pool assets
  address public rebalancer;
  // address of the uniswap v2 router
  IUniswapV2Router02 internal uniswapRouterV2;

  // Flag for allowing AA withdraws
  bool public allowAAWithdraw;
  // Flag for allowing BB withdraws
  bool public allowBBWithdraw;
  // Flag for allowing to enable reverting in case the strategy gives back less
  // amount than the requested one
  bool public revertIfTooLow;
  // Flag to enable the `Default Check` (related to the emergency shutdown)
  bool public skipDefaultCheck;

  // address of the strategy used to lend funds
  address public strategy;
  // address of the strategy token which represent the position in the lending provider
  address public strategyToken;
  // address of AA Tranche token contract
  address public AATranche;
  // address of BB Tranche token contract
  address public BBTranche;
  // address of AA Staking reward token contract
  address public AAStaking;
  // address of BB Staking reward token contract
  address public BBStaking;

  // Apr split ratio for AA tranches
  // (relative to FULL_ALLOC so 50% => 50000 => 50% of the interest to tranche AA)
  uint256 public trancheAPRSplitRatio; //
  // Ideal tranche split ratio in `token` value
  // (relative to FULL_ALLOC so 50% => 50000 means 50% of tranches (in value) should be AA)
  uint256 public trancheIdealWeightRatio;
  // Price for minting AA tranche, in underlyings
  uint256 public priceAA;
  // Price for minting BB tranche, in underlyings
  uint256 public priceBB;
  // last saved net asset value (in `token`) for AA tranches
  uint256 public lastNAVAA;
  // last saved net asset value (in `token`) for BB tranches
  uint256 public lastNAVBB;
  // last saved lending provider price
  uint256 public lastStrategyPrice;
  // Keeps track of unclaimed fees for feeReceiver
  uint256 public unclaimedFees;
  // Keeps an unlent balance both for cheap redeem and as 'insurance of last resort'
  uint256 public unlentPerc;

  // Fee amount (relative to FULL_ALLOC)
  uint256 public fee;
  // address of the fee receiver
  address public feeReceiver;

  // trancheIdealWeightRatio ± idealRanges, used in updateIncentives
  uint256 public idealRange;
  // period, in blocks, for progressively releasing harvested rewards to users
  uint256 public releaseBlocksPeriod;
  // amount of rewards sold in the last harvest (in `token`)
  uint256 internal harvestedRewards;
  // stkAave address
  address internal constant stkAave = address(0x4da27a545c0c5B758a6BA100e3a049001de870f5);
  // aave address
  address internal constant AAVE = address(0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9);
  // if the cdo receive stkAAVE
  bool internal isStkAAVEActive;
  // referral address of the strategy developer
  address public referral;
  // amount of fee for feeReceiver. Max is FULL_ALLOC
  uint256 public feeSplit;
}

pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/*
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal initializer {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal initializer {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
    uint256[50] private __gap;
}

File 14 of 21 : Initializable.sol
// SPDX-License-Identifier: MIT

// solhint-disable-next-line compiler-version
pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @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 {UpgradeableProxy-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;
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

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

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

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

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

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

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal initializer {
        __Context_init_unchained();
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal initializer {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

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

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

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

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

    uint256 private _status;

    function __ReentrancyGuard_init() internal initializer {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal initializer {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20.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 guidelines: functions revert instead
 * of 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 {
    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 defaut value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All three 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 returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual 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
     * overloaded;
     *
     * 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 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");
        _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");
        _approve(_msgSender(), spender, currentAllowance - subtractedValue);

        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is 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");
        _balances[sender] = senderBalance - amount;
        _balances[recipient] += amount;

        emit Transfer(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:
     *
     * - `to` 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);
    }

    /**
     * @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");
        _balances[account] = accountBalance - amount;
        _totalSupply -= amount;

        emit Transfer(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 to 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 { }
}

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

// 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) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"AAStaking","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"AATranche","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BBStaking","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BBTranche","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FULL_ALLOC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE_TRANCHE_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"name":"_setLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"allowAAWithdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowBBWithdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"depositAA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"depositBB","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeSplit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tranche","type":"address"}],"name":"getApr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getContractValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentAARatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tranche","type":"address"}],"name":"getIdealApr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getIncentiveTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governanceRecoveryFund","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool[]","name":"_skipFlags","type":"bool[]"},{"internalType":"bool[]","name":"_skipReward","type":"bool[]"},{"internalType":"uint256[]","name":"_minAmount","type":"uint256[]"},{"internalType":"uint256[]","name":"_sellAmounts","type":"uint256[]"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"harvest","outputs":[{"internalType":"uint256[][]","name":"_res","type":"uint256[][]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"idealRange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"incentiveTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_limit","type":"uint256"},{"internalType":"address","name":"_guardedToken","type":"address"},{"internalType":"address","name":"_governanceFund","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_rebalancer","type":"address"},{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_trancheAPRSplitRatio","type":"uint256"},{"internalType":"uint256","name":"_trancheIdealWeightRatio","type":"uint256"},{"internalType":"address[]","name":"_incentiveTokens","type":"address[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastNAVAA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastNAVBB","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastStrategyPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"limit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_revertIfNeeded","type":"bool"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"oneToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceAA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceBB","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebalancer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referral","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"releaseBlocksPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revertIfTooLow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setAllowAAWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setAllowBBWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeReceiver","type":"address"}],"name":"setFeeReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_feeSplit","type":"uint256"}],"name":"setFeeSplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_guardian","type":"address"}],"name":"setGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_idealRange","type":"uint256"}],"name":"setIdealRange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_incentiveTokens","type":"address[]"}],"name":"setIncentiveTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isStkAAVEActive","type":"bool"}],"name":"setIsStkAAVEActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rebalancer","type":"address"}],"name":"setRebalancer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_referral","type":"address"}],"name":"setReferral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_releaseBlocksPeriod","type":"uint256"}],"name":"setReleaseBlocksPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setRevertIfTooLow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setSkipDefaultCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_AAStaking","type":"address"},{"internalType":"address","name":"_BBStaking","type":"address"}],"name":"setStakingRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"address[]","name":"_incentiveTokens","type":"address[]"}],"name":"setStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_trancheAPRSplitRatio","type":"uint256"}],"name":"setTrancheAPRSplitRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_trancheIdealWeightRatio","type":"uint256"}],"name":"setTrancheIdealWeightRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_unlentPerc","type":"uint256"}],"name":"setUnlentPerc","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"skipDefaultCheck","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategyToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trancheAPRSplitRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trancheIdealWeightRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tranche","type":"address"}],"name":"tranchePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unclaimedFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unlentPerc","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tranche","type":"address"}],"name":"virtualPrice","outputs":[{"internalType":"uint256","name":"_virtualPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawAA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawBB","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b5060cf80546001600160a01b0319166001179055615fc580620000346000396000f3fe60806040523480156200001157600080fd5b5060043610620004415760003560e01c80638456cb591162000239578063c9aba356116200013f578063e70875ad11620000c2578063e70875ad1462000967578063eb120bf4146200097e578063eb67acb51462000992578063edd636fb14620009a9578063efdcd97414620009b3578063f22ff9f414620009ca578063f2fde38b14620009e1578063fc0c546a14620009f8578063fdf4c0dc1462000a0c578063fe81a0241462000a1657600080fd5b8063c9aba35614620008a2578063cc11580514620008b2578063cd962a0614620008c9578063d5357d8914620008e0578063dc82697c1462000906578063ddca3f431462000910578063dfd5b1c9146200091a578063e07eace61462000925578063e14488e4146200093c578063e3e39368146200095357600080fd5b80639d3ef4b211620001c85780639d3ef4b214620007eb5780639e5914da1462000800578063a219d2181462000817578063a27eccc1146200082e578063a3d0bd481462000838578063a4d66daf146200084f578063a8c62e761462000859578063b3f00674146200086d578063b450dfce1462000881578063bc063e1a146200089857600080fd5b80638456cb5914620007435780638a0dac4a146200074d5780638da5cb5b14620007645780638f5aa090146200076e5780639290d427146200078557806392b29be3146200079c57806394929dc114620007a657806399abe5e814620007bd5780639af6485414620007d457600080fd5b80633f4ba83a116200034b57806362adade511620002ce57806362adade514620006965780636373ea6914620006a057806369fe0e2d14620006aa5780636cfd155314620006c15780636f47d99b14620006d8578063715018a614620006ed578063734d828714620006f7578063747efea114620007015780637692ee0a1462000715578063812e43fa146200072c57600080fd5b80633f4ba83a14620005e05780633fc8cef314620005ea578063400f7d3214620005fe578063408f3a30146200060857806344966ba0146200061f57806344ca67ec1462000633578063452a9320146200064a57806346840590146200065e57806355c7070114620006755780635c975abb146200068a57600080fd5b80631ed4fd9711620003d45780631ed4fd9714620005205780632047782a146200054657806325cdd860146200055057806325f66adf146200055a5780632758db0c146200056457806329811ee5146200057b5780632a3e4dc214620005925780633403c2fc14620005a9578063387af1bd14620005b35780633b28d53714620005c757600080fd5b806301d22ccd1462000446578063085a2b7814620004725780630986950b146200048b5780631072cbea14620004a457806312361ac214620004bb5780631441a5a914620004c557806315c44b8b14620004de57806318dc719514620004f25780631cf7a8ff1462000509575b600080fd5b60d2546200045a906001600160a01b031681565b60405162000469919062004401565b60405180910390f35b6200047c60da5481565b60405190815260200162000469565b620004a26200049c3660046200442b565b62000a20565b005b620004a2620004b536600462004469565b62000c71565b6200047c60dc5481565b60e8546200045a9061010090046001600160a01b031681565b60d9546200045a906001600160a01b031681565b620004a26200050336600462004585565b62000c9b565b620004a26200051a3660046200464a565b6200126e565b60d3546200053590600160b81b900460ff1681565b604051901515815260200162000469565b6200047c60e55481565b6200047c620012a6565b6200047c60db5481565b6200047c6200057536600462004673565b6200131f565b6200047c6200058c3660046200469b565b62001340565b6200045a620005a33660046200464a565b62001357565b620004a262001382565b60ca546200045a906001600160a01b031681565b620005d1620013af565b60405162000469919062004701565b620004a262001413565b60cd546200045a906001600160a01b031681565b6200047c60dd5481565b620004a2620006193660046200464a565b62001429565b60d6546200045a906001600160a01b031681565b620004a2620006443660046200464a565b62001438565b60d0546200045a906001600160a01b031681565b620004a26200066f36600462004716565b620014a0565b60d3546200053590600160a01b900460ff1681565b60335460ff1662000535565b6200047c60de5481565b6200047c60e95481565b620004a2620006bb3660046200464a565b620014c8565b620004a2620006d23660046200469b565b620014fc565b60d3546200053590600160b01b900460ff1681565b620004a262001543565b6200047c60e15481565b60d5546200045a906001600160a01b031681565b620004a26200072636600462004736565b620015b0565b620004a26200073d3660046200464a565b620015cf565b620004a262001604565b620004a26200075e3660046200469b565b62001618565b6200045a6200165f565b620004a26200077f36600462004716565b6200166e565b6200047c620007963660046200469b565b62001696565b6200047c60e25481565b620004a2620007b73660046200464a565b620016ec565b6200047c620007ce3660046200464a565b620016fb565b6200047c620007e53660046200464a565b62001758565b60d3546200053590600160a81b900460ff1681565b620004a2620008113660046200469b565b620017b5565b6200047c620008283660046200469b565b620017e7565b6200047c60d15481565b620004a2620008493660046200464a565b620017f4565b6200047c60c95481565b60d4546200045a906001600160a01b031681565b60e4546200045a906001600160a01b031681565b6200047c620008923660046200464a565b62001829565b6200047c614e2081565b6200047c670de0b6b3a764000081565b620004a2620008c336600462004716565b62001845565b620004a2620008da3660046200464a565b6200186d565b620008f7620008f136600462004800565b620018a2565b60405162000469919062004900565b6200047c62001c7e565b6200047c60e35481565b6200047c620186a081565b620004a26200093636600462004716565b62001d77565b6200047c6200094d3660046200469b565b62001d9f565b60d7546200045a906001600160a01b031681565b6200047c620009783660046200464a565b62001daf565b60d8546200045a906001600160a01b031681565b620004a2620009a336600462004716565b62001dcb565b6200047c60e65481565b620004a2620009c43660046200469b565b62001de8565b620004a2620009db3660046200498e565b62001e2f565b620004a2620009f23660046200469b565b62001f7a565b60cf546200045a906001600160a01b031681565b6200047c60e05481565b6200047c60df5481565b62000a2a62002060565b60d65460d75460ce8054604080516020808402820181019092528281526001600160a01b0395861695909416936000939092909183018282801562000a9957602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162000a7a575b505060d85460d9549495506001600160a01b039081169416925050508115158115156000805b865181101562000b235786818151811062000ade5762000ade620049e3565b60200260200101519150831562000afb5762000afb828762002094565b821562000b0e5762000b0e828662002094565b8062000b1a8162004a0f565b91505062000abf565b5082801562000b3a57506001600160a01b03881615155b1562000b4c5762000b4c888662002094565b81801562000b6257506001600160a01b03871615155b1562000b745762000b74878562002094565b60d880546001600160a01b03808d166001600160a01b0319928316811790935560d98054918d1691909216811790915590151593501515915060005b865181101562000c145786818151811062000bcf5762000bcf620049e3565b60200260200101519150831562000bec5762000bec828c620020ab565b821562000bff5762000bff828b620020ab565b8062000c0b8162004a0f565b91505062000bb0565b5082801562000c2b57506001600160a01b03881615155b1562000c3d5762000c3d888b620020ab565b81801562000c5357506001600160a01b03871615155b1562000c655762000c65878a620020ab565b50505050505050505050565b62000c7b62002060565b60ca5462000c97906001600160a01b03848116911683620020c3565b5050565b600054610100900460ff168062000cb5575060005460ff16155b62000cdd5760405162461bcd60e51b815260040162000cd49062004a2d565b60405180910390fd5b600054610100900460ff1615801562000d00576000805461ffff19166101011790555b600062000d12600a620186a062004a7b565b60cf549091506001600160a01b03161562000d545760405162461bcd60e51b81526020600482015260016024820152603160f81b604482015260640162000cd4565b6001600160a01b0387161580159062000d7557506001600160a01b03861615155b801562000d8a57506001600160a01b038a1615155b62000da95760405162461bcd60e51b815260040162000cd49062004a9e565b620186a085111562000dcf5760405162461bcd60e51b815260040162000cd49062004ab9565b62000dde81620186a062004ad4565b84111562000e005760405162461bcd60e51b815260040162000cd49062004ab9565b8084101562000e235760405162461bcd60e51b815260040162000cd49062004aee565b62000e2d62002122565b62000e3a8b8a8a620021a7565b6000866001600160a01b031663747efea16040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000e7b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000ea1919062004b09565b90506000816001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000ee4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000f0e919081019062004b58565b905062000f4960405180604001604052806015815260200174024b23632a1a2279020a0902a3930b731b43290169605d1b8152508262002238565b62000f706040518060400160405280600381526020016241415f60e81b8152508362002238565b60405162000f7e9062004372565b62000f8b92919062004c1b565b604051809103906000f08015801562000fa8573d6000803e3d6000fd5b5060d680546001600160a01b0319166001600160a01b0392909216919091179055604080518082019091526015815274024b23632a1a227902121102a3930b731b43290169605d1b602082015262001001908262002238565b620010286040518060400160405280600381526020016242425f60e81b8152508362002238565b604051620010369062004372565b6200104392919062004c1b565b604051809103906000f08015801562001060573d6000803e3d6000fd5b5060d780546001600160a01b03199081166001600160a01b039384161790915560cf805482168f841690811790915560d4805483168c851617905560d58054831686851617905560d28054909216928c1692909217905560da88905560db87905560e58490556040805163313ce56760e01b815290516000929163313ce5679160048083019260209291908290030181865afa15801562001105573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200112b919062004c4d565b6200113890600a62004d64565b60d181905560d380546001600160a01b0319908116737a250d5630b4cf539739df2c5dacb4c659f2488d1790915560cd805490911673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc217905586519091506200119d9060ce90602089019062004380565b5060dc81905560dd8190556107d060e2556105dc60e65560d3805462ffffff60a01b19166201010160a01b179055620011d78d8a620020ab565b60d554620011ef906001600160a01b03168a620020ab565b620011f962002266565b60e055505061271060e355505060e4805473fb3bd022d5dacf95ee28a6b07825d4ff9c5b38146001600160a01b03199182161790915560d080549091166001600160a01b03891617905560e8805460ff19166001179055801562000c65576000805461ff001916905550505050505050505050565b6200127862002060565b620186a08160e28190551115620012a35760405162461bcd60e51b815260040162000cd49062004ab9565b50565b60d6546000908190620012c2906001600160a01b0316620022dc565b60d754909150600090620012df906001600160a01b0316620022dc565b620012eb908362004d72565b905080620012fc5760009250505090565b806200130c620186a08462004d8d565b62001318919062004a7b565b9250505090565b60006200132b6200236f565b620013378383620023c2565b90505b92915050565b60006200133a8262001351620012a6565b6200247d565b60ce81815481106200136857600080fd5b6000918252602090912001546001600160a01b0316905081565b6200138c62002598565b62001396620025b7565b60d3805463ffffffff60a01b191661010160b01b179055565b606060ce8054806020026020016040519081016040528092919081815260200182805480156200140957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311620013ea575b5050505050905090565b6200141d62002598565b620014276200262c565b565b6200143362002060565b60c955565b6200144262002060565b60e554811015620014675760405162461bcd60e51b815260040162000cd49062004aee565b60e5546200147990620186a062004ad4565b8160db8190551115620012a35760405162461bcd60e51b815260040162000cd49062004ab9565b620014aa62002060565b60d38054911515600160b81b0260ff60b81b19909216919091179055565b620014d262002060565b614e208160e38190551115620012a35760405162461bcd60e51b815260040162000cd49062004ab9565b6200150662002060565b60d280546001600160a01b0319166001600160a01b038316908117909155620012a35760405162461bcd60e51b815260040162000cd49062004a9e565b336200154e6200165f565b6001600160a01b031614620015775760405162461bcd60e51b815260040162000cd49062004daf565b6065546040516000916001600160a01b03169060008051602062005f70833981519152908390a3606580546001600160a01b0319169055565b620015ba62002060565b805162000c979060ce90602084019062004380565b620015d962002060565b620186a08160e58190551115620012a35760405162461bcd60e51b815260040162000cd49062004ab9565b6200160e62002598565b62001427620025b7565b6200162262002060565b60d080546001600160a01b0319166001600160a01b038316908117909155620012a35760405162461bcd60e51b815260040162000cd49062004a9e565b6065546001600160a01b031690565b6200167862002060565b60d38054911515600160a81b0260ff60a81b19909216919091179055565b60de5460df5460009190620016e384620016af62001c7e565b620016bb848662004d72565b60d6546001600160a01b03898116911614620016d85784620016da565b855b60da54620026a9565b50949350505050565b620016f662002060565b60e655565b60006200170a60335460ff1690565b158062001720575060d354600160a81b900460ff165b6200173f5760405162461bcd60e51b815260040162000cd49062004de4565b60d7546200133a9083906001600160a01b0316620028bb565b60006200176760335460ff1690565b15806200177d575060d354600160a01b900460ff165b6200179c5760405162461bcd60e51b815260040162000cd49062004de4565b60d6546200133a9083906001600160a01b0316620028bb565b620017bf62002060565b60e880546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b60006200133a8262002b31565b620017fe62002060565b620186a08160da8190551115620012a35760405162461bcd60e51b815260040162000cd49062004ab9565b60d6546000906200133a9083906001600160a01b031662002bcc565b6200184f62002060565b60d38054911515600160a01b0260ff60a01b19909216919091179055565b6200187762002060565b620186a08160e98190551115620012a35760405162461bcd60e51b815260040162000cd49062004dff565b6060620018ae6200236f565b6040805160038082526080820190925290816020015b6060815260200190600190039081620018c457505060d4549091506001600160a01b03168b8b6003818110620018fe57620018fe620049e3565b905060200201602081019062001915919062004716565b62001b995760008c8c6000818110620019325762001932620049e3565b905060200201602081019062001949919062004716565b62001afd576040516323b1c78360e21b81526001600160a01b03831690638ec71e0c906200197e908890889060040162004e1a565b6000604051808303816000875af11580156200199e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620019c8919081019062004e49565b83600281518110620019de57620019de620049e3565b6020026020010181905250620019f362002c57565b62001ab682888880806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508b8b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508e8e8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525062002f9f92505050565b8560008151811062001acc5762001acc620049e3565b602002602001018660018151811062001ae95762001ae9620049e3565b602002602001018295508390528390525050505b4360cc55801562001b0f578062001b12565b60015b60e75562001b1f62003358565b8c8c600281811062001b355762001b35620049e3565b905060200201602081019062001b4c919062004716565b62001b5b5762001b5b6200345d565b8c8c600181811062001b715762001b71620049e3565b905060200201602081019062001b88919062004716565b62001b975762001b9762003581565b505b60cf5460009062001bb3906001600160a01b031662003665565b90506000620186a060e25462001bc862001c7e565b62001bd4919062004d8d565b62001be0919062004a7b565b90508082111562001c6d576001600160a01b03831663b6b55f2562001c06838562004ad4565b6040518263ffffffff1660e01b815260040162001c2591815260200190565b6020604051808303816000875af115801562001c45573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001c6b919062004c4d565b505b5050509a9950505050505050505050565b60d5546040805163313ce56760e01b815290516000926001600160a01b0316918391839163313ce5679160048083019260209291908290030181865afa15801562001ccd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001cf3919062004c4d565b905060e15462001d02620036da565b60cf5462001d19906001600160a01b031662003665565b62001d2684600a62004d64565b62001d3062002266565b62001d3b8762003665565b62001d47919062004d8d565b62001d53919062004a7b565b62001d5f919062004d72565b62001d6b919062004ad4565b62001318919062004ad4565b62001d8162002060565b60d38054911515600160b01b0260ff60b01b19909216919091179055565b60006200133a8260db546200247d565b60d7546000906200133a9083906001600160a01b031662002bcc565b62001dd562002060565b60e8805460ff1916911515919091179055565b62001df262002060565b60e480546001600160a01b0319166001600160a01b038316908117909155620012a35760405162461bcd60e51b815260040162000cd49062004a9e565b62001e3962002060565b6001600160a01b03821662001e625760405162461bcd60e51b815260040162000cd49062004a9e565b60cf5460d4546001600160a01b03918216911662001e81828262002094565b60d55462001e99906001600160a01b03168262002094565b60d480546001600160a01b0319166001600160a01b038616179055825162001ec99060ce90602086019062004380565b506000846001600160a01b031663747efea16040518163ffffffff1660e01b8152600401602060405180830381865afa15801562001f0b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001f31919062004b09565b60d580546001600160a01b0319166001600160a01b038316179055905062001f5a8386620020ab565b62001f668186620020ab565b62001f7062002266565b60e0555050505050565b3362001f856200165f565b6001600160a01b03161462001fae5760405162461bcd60e51b815260040162000cd49062004daf565b6001600160a01b038116620020155760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840162000cd4565b6065546040516001600160a01b0380841692169060008051602062005f7083398151915290600090a3606580546001600160a01b0319166001600160a01b0392909216919091179055565b336200206b6200165f565b6001600160a01b031614620014275760405162461bcd60e51b815260040162000cd49062004ed7565b62000c976001600160a01b0383168260006200373c565b62000c976001600160a01b0383168260001962003849565b6200211d8363a9059cbb60e01b8484604051602401620020e592919062004ef2565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152620038f7565b505050565b600054610100900460ff16806200213c575060005460ff16155b6200215b5760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff161580156200217e576000805461ffff19166101011790555b62002188620039d0565b6200219262003a40565b8015620012a3576000805461ff001916905550565b6001600160a01b038216620021d05760405162461bcd60e51b815260040162000cd49062004a9e565b6001600160a01b038116620021f95760405162461bcd60e51b815260040162000cd49062004a9e565b6200220362003abb565b6200220d62003b2b565b60c983905560ca80546001600160a01b0319166001600160a01b0384161790556200211d8162001f7a565b606082826040516020016200224f92919062004f0b565b604051602081830303815290604052905092915050565b60d4546040805163501ad8ff60e11b815290516000926001600160a01b03169163a035b1fe9160048083019260209291908290030181865afa158015620022b1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620022d7919062004c4d565b905090565b6000670de0b6b3a7640000620022f28362001696565b836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562002331573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002357919062004c4d565b62002363919062004d8d565b6200133a919062004a7b565b60d2546001600160a01b0316331480620023a357506200238e6200165f565b6001600160a01b0316336001600160a01b0316145b620014275760405162461bcd60e51b815260040162000cd49062004ed7565b60d45460405163852a12e360e01b8152600481018490526000916001600160a01b03169063852a12e3906024016020604051808303816000875af11580156200240f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002435919062004c4d565b905081156200246d57826200244c82606462004d72565b10156200246d5760405162461bcd60e51b815260040162000cd49062004aee565b828111156200133a575090919050565b60008060d460009054906101000a90046001600160a01b03166001600160a01b031663845bc8046040518163ffffffff1660e01b8152600401602060405180830381865afa158015620024d4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620024fa919062004c4d565b60da5460d654919250906001600160a01b038681169116148462002533578062002525578262002528565b60005b93505050506200133a565b8062002575576200254885620186a062004ad4565b6200255783620186a062004ad4565b62002563908562004d8d565b6200256f919062004a7b565b6200258e565b8462002582838562004d8d565b6200258e919062004a7b565b9695505050505050565b60d0546001600160a01b0316331480620023a357506200238e6200165f565b60335460ff1615620025dd5760405162461bcd60e51b815260040162000cd49062004f3e565b6033805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258620026133390565b60405162002622919062004401565b60405180910390a1565b60335460ff16620026775760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015260640162000cd4565b6033805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa3362002613565b600080848611620026cb57620026bf8762002b31565b600091509150620028b1565b6000876001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200270c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002732919062004c4d565b905085158062002740575080155b156200275557505060d15490506000620028b1565b600062002763878962004ad4565b9050620186a060e3548262002779919062004d8d565b62002785919062004a7b565b62002791908262004ad4565b60d6549091506001600160a01b03908116908a16811480620027b45781620027c1565b60d7546001600160a01b03165b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620027ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002825919062004c4d565b62002833578294506200287c565b6000620186a062002845898262004ad4565b62002851908662004d8d565b6200285d919062004a7b565b9050816200286c578062002878565b62002878818562004ad4565b9550505b83670de0b6b3a764000062002892878b62004d72565b6200289e919062004d8d565b620028aa919062004a7b565b9550505050505b9550959350505050565b600060026097541415620029125760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640162000cd4565b60026097556200292162003b91565b6200292b62003be0565b6200293562003358565b82620029b0576040516370a0823160e01b81526001600160a01b038316906370a08231906200296990339060040162004401565b602060405180830381865afa15801562002987573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620029ad919062004c4d565b92505b60008311620029d35760405162461bcd60e51b815260040162000cd49062004a9e565b60cf546001600160a01b03166000620029ec8262003665565b9050670de0b6b3a764000062002a028562002b31565b62002a0e908762004d8d565b62002a1a919062004a7b565b92508083111562002a58578062002a4962002a36828662004ad4565b60d354600160b01b900460ff16620023c2565b62002a55919062004d72565b92505b604051632770a7eb60e21b81526001600160a01b03851690639dc29fac9062002a88903390899060040162004ef2565b600060405180830381600087803b15801562002aa357600080fd5b505af115801562002ab8573d6000803e3d6000fd5b5062002ad3925050506001600160a01b0383163385620020c3565b60d6546001600160a01b038581169116141562002b0a578260de600082825462002afe919062004ad4565b9091555062002b249050565b8260df600082825462002b1e919062004ad4565b90915550505b5050600160975592915050565b6000816001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562002b72573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002b98919062004c4d565b62002ba557505060d15490565b60d6546001600160a01b0383811691161462002bc45760dd546200133a565b505060dc5490565b600062002bdb60335460ff1690565b1562002bfb5760405162461bcd60e51b815260040162000cd49062004f3e565b8262002c07576200133a565b62002c128362003c3e565b62002c1c62003c97565b62002c2662003be0565b62002c3062003358565b60cf5462002c4a906001600160a01b031633308662003cc9565b6200133783338462003d03565b60e85460ff1662002c6457565b60405163091030c360e01b8152734da27a545c0c5b758a6ba100e3a049001de870f590600090829063091030c39062002ca290309060040162004401565b602060405180830381865afa15801562002cc0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002ce6919062004c4d565b9050801562002e4d576000826001600160a01b03166372b49d636040518163ffffffff1660e01b8152600401602060405180830381865afa15801562002d30573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002d56919062004c4d565b62002d62908362004d72565b9050428110156200211d57826001600160a01b031663359c4a966040518163ffffffff1660e01b8152600401602060405180830381865afa15801562002dac573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002dd2919062004c4d565b62002dde824262004ad4565b1162002e4b576040516301e9a69560e41b81526001600160a01b03841690631e9a69509062002e169030906000199060040162004ef2565b600060405180830381600087803b15801562002e3157600080fd5b505af115801562002e46573d6000803e3d6000fd5b505050505b505b60d460009054906101000a90046001600160a01b03166001600160a01b031663cdfbe9c56040518163ffffffff1660e01b81526004016020604051808303816000875af115801562002ea3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002ec9919062004c4d565b506040516370a0823160e01b81526000906001600160a01b038416906370a082319062002efb90309060040162004401565b602060405180830381865afa15801562002f19573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002f3f919062004c4d565b111562000c9757816001600160a01b031663787a08a66040518163ffffffff1660e01b8152600401600060405180830381600087803b15801562002f8257600080fd5b505af115801562002f97573d6000803e3d6000fd5b505050505050565b60608060008060ce80548060200260200160405190810160405280929190818152602001828054801562002ffd57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162002fde575b505050505090506000886001600160a01b031663c4f59f9b6040518163ffffffff1660e01b8152600401600060405180830381865afa15801562003045573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526200306f919081019062004f68565b60408051600380825260808201909252919250600091829160208201606080368337505060cd5482519293506001600160a01b0316918391506001908110620030bc57620030bc620049e3565b6001600160a01b03928316602091820292909201015260cf54825191169082906002908110620030f057620030f0620049e3565b60200260200101906001600160a01b031690816001600160a01b03168152505082516001600160401b038111156200312c576200312c62004498565b60405190808252806020026020018201604052801562003156578160200160208202803683370190505b50965082516001600160401b0381111562003175576200317562004498565b6040519080825280602002602001820160405280156200319f578160200160208202803683370190505b50955060005b83518110156200334957838181518110620031c457620031c4620049e3565b60200260200101519250888181518110620031e357620031e3620049e3565b602002602001015180620031fe5750620031fe858462003df1565b156200320a5762003334565b6001600160a01b038316734da27a545c0c5b758a6ba100e3a049001de870f514156200324857737fc66500c84a76ad7e9c93437bfc5ac33e2ddae992505b82826000815181106200325f576200325f620049e3565b60200260200101906001600160a01b031690816001600160a01b031681525050620032c583838d84815181106200329a576200329a620049e3565b60200260200101518d8581518110620032b757620032b7620049e3565b602002602001015162003e60565b898381518110620032da57620032da620049e3565b60200260200101898481518110620032f657620032f6620049e3565b60200260200101828152508281525050508681815181106200331c576200331c620049e3565b60200260200101518662003331919062004d72565b95505b80620033408162004a0f565b915050620031a5565b50505050509450945094915050565b60de5460df5460006200336c828462004d72565b905060006200337a62001c7e565b60da5490915082821115620033ce5760e354620186a0906200339d858562004ad4565b620033a9919062004d8d565b620033b5919062004a7b565b60e16000828254620033c8919062004d72565b90915550505b60d6546000908190620033ee906001600160a01b031685878a87620026a9565b60d7549193509150600090819062003413906001600160a01b031687898b89620026a9565b915091508260de60008282546200342b919062004d72565b925050819055508060df600082825462003446919062004d72565b90915550505060dc929092555060dd555050505050565b60e1548015620012a35760d85460e45460e8546001600160a01b03928316928315159281169161010090041660008115620034d257620186a060e95487620034a6919062004d8d565b620034b2919062004a7b565b60d654909150620034d090829084906001600160a01b031662003d03565b505b600062003505620034e4838962004ad4565b86620034f15785620034f3565b305b60d6546001600160a01b031662003d03565b600060e1559050841562003578576040516305dc812160e31b81526001600160a01b03871690632ee409089062003543908790859060040162004ef2565b600060405180830381600087803b1580156200355e57600080fd5b505af115801562003573573d6000803e3d6000fd5b505050505b50505050505050565b60db5460da5460e55460d95460d8546001600160a01b0391821691168115801590821515906000908390620035b35750815b15620035c657620035c3620012a6565b90505b828015620035e75750811580620035e75750620035e4868962004d72565b81115b156200360657620035fc85620186a062003f91565b5050505050505050565b81801562003627575082158062003627575062003624868962004ad4565b81105b156200363c57620035fc84620186a062003f91565b81156200364f576200364f848862003f91565b8215620035fc57620035fc85620186a062003f91565b6040516370a0823160e01b81526000906001600160a01b038316906370a08231906200369690309060040162004401565b602060405180830381865afa158015620036b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200133a919062004c4d565b60e65460cc54600091908290620036f2904362004ad4565b60e7549091506001811180156200370857508282105b156200373657826200371b838262004ad4565b62003727908362004d8d565b62003733919062004a7b565b93505b50505090565b801580620037ba5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa15801562003792573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620037b8919062004c4d565b155b620038275760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606482015260840162000cd4565b6200211d8363095ea7b360e01b8484604051602401620020e592919062004ef2565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa1580156200389b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620038c1919062004c4d565b620038cd919062004d72565b9050620038f18463095ea7b360e01b8584604051602401620020e592919062004ef2565b50505050565b60006200394e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316620040d09092919063ffffffff16565b8051909150156200211d57808060200190518101906200396f919062005001565b6200211d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840162000cd4565b600054610100900460ff1680620039ea575060005460ff16155b62003a095760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff1615801562002192576000805461ffff19166101011790558015620012a3576000805461ff001916905550565b600054610100900460ff168062003a5a575060005460ff16155b62003a795760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff1615801562003a9c576000805461ffff19166101011790555b6033805460ff191690558015620012a3576000805461ff001916905550565b600054610100900460ff168062003ad5575060005460ff16155b62003af45760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff1615801562003b17576000805461ffff19166101011790555b62003b21620039d0565b62002192620040e9565b600054610100900460ff168062003b45575060005460ff16155b62003b645760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff1615801562003b87576000805461ffff19166101011790555b620021926200418c565b60cb54324360405160200162003ba992919062005021565b604051602081830303815290604052805190602001201415620014275760405162461bcd60e51b815260040162000cd49062004dff565b600062003bec62002266565b60d354909150600160b81b900460ff1662003c39578060e054111562003c395760405162461bcd60e51b81526020600482015260016024820152600d60fa1b604482015260640162000cd4565b60e055565b60c9548062003c4b575050565b808262003c5762001c7e565b62003c63919062004d72565b111562000c975760405162461bcd60e51b81526020600482015260016024820152601960f91b604482015260640162000cd4565b324360405160200162003cac92919062005021565b60408051601f19818403018152919052805160209091012060cb55565b6040516001600160a01b0380851660248301528316604482015260648101829052620038f19085906323b872dd60e01b90608401620020e5565b600062003d108262002b31565b62003d24670de0b6b3a76400008662004d8d565b62003d30919062004a7b565b6040516340c10f1960e01b81529091506001600160a01b038316906340c10f199062003d63908690859060040162004ef2565b600060405180830381600087803b15801562003d7e57600080fd5b505af115801562003d93573d6000803e3d6000fd5b505060d6546001600160a01b0385811691161415915062003dd09050578360de600082825462003dc4919062004d72565b9091555062003dea9050565b8360df600082825462003de4919062004d72565b90915550505b9392505050565b6000805b835181101562003e5657826001600160a01b031684828151811062003e1e5762003e1e620049e3565b60200260200101516001600160a01b0316141562003e415760019150506200133a565b8062003e4d8162004a0f565b91505062003df5565b5060009392505050565b6000808362003e775762003e748662003665565b93505b8362003e895750600090508062003f88565b60d3546001600160a01b039081169062003ea7908816828762003849565b60006001600160a01b0382166338ed173987878a3062003ec942600162004d72565b6040518663ffffffff1660e01b815260040162003eeb95949392919062005043565b6000604051808303816000875af115801562003f0b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262003f35919081019062004e49565b90508060008151811062003f4d5762003f4d620049e3565b6020026020010151816001835162003f66919062004ad4565b8151811062003f795762003f79620049e3565b60200260200101519350935050505b94509492505050565b600060ce80548060200260200160405190810160405280929190818152602001828054801562003feb57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162003fcc575b5050505050905060005b8151811015620038f1576000828281518110620040165762004016620049e3565b602002602001015190506000620186a085620040328462003665565b6200403e919062004d8d565b6200404a919062004a7b565b90508015620040b857604051637db4e28f60e01b81526001600160a01b03871690637db4e28f9062004083908590859060040162004ef2565b600060405180830381600087803b1580156200409e57600080fd5b505af1158015620040b3573d6000803e3d6000fd5b505050505b50508080620040c79062004a0f565b91505062003ff5565b6060620040e1848460008562004202565b949350505050565b600054610100900460ff168062004103575060005460ff16155b620041225760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff1615801562004145576000805461ffff19166101011790555b606580546001600160a01b03191633908117909155604051819060009060008051602062005f70833981519152908290a3508015620012a3576000805461ff001916905550565b600054610100900460ff1680620041a6575060005460ff16155b620041c55760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff16158015620041e8576000805461ffff19166101011790555b60016097558015620012a3576000805461ff001916905550565b606082471015620042655760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000cd4565b843b620042b55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000cd4565b600080866001600160a01b03168587604051620042d3919062005081565b60006040518083038185875af1925050503d806000811462004312576040519150601f19603f3d011682016040523d82523d6000602084013e62004317565b606091505b50915091506200432982828662004334565b979650505050505050565b606083156200434557508162003dea565b825115620043565782518084602001fd5b8160405162461bcd60e51b815260040162000cd491906200509f565b610ebb80620050b583390190565b828054828255906000526020600020908101928215620043d8579160200282015b82811115620043d857825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190620043a1565b50620043e6929150620043ea565b5090565b5b80821115620043e65760008155600101620043eb565b6001600160a01b0391909116815260200190565b6001600160a01b0381168114620012a357600080fd5b600080604083850312156200443f57600080fd5b82356200444c8162004415565b915060208301356200445e8162004415565b809150509250929050565b600080604083850312156200447d57600080fd5b82356200448a8162004415565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715620044d957620044d962004498565b604052919050565b60006001600160401b03821115620044fd57620044fd62004498565b5060051b60200190565b600082601f8301126200451957600080fd5b81356020620045326200452c83620044e1565b620044ae565b82815260059290921b840181019181810190868411156200455257600080fd5b8286015b848110156200457a5780356200456c8162004415565b835291830191830162004556565b509695505050505050565b60008060008060008060008060006101208a8c031215620045a557600080fd5b8935985060208a0135620045b98162004415565b975060408a0135620045cb8162004415565b965060608a0135620045dd8162004415565b955060808a0135620045ef8162004415565b945060a08a0135620046018162004415565b935060c08a0135925060e08a013591506101008a01356001600160401b038111156200462c57600080fd5b6200463a8c828d0162004507565b9150509295985092959850929598565b6000602082840312156200465d57600080fd5b5035919050565b8015158114620012a357600080fd5b600080604083850312156200468757600080fd5b8235915060208301356200445e8162004664565b600060208284031215620046ae57600080fd5b813562003dea8162004415565b600081518084526020808501945080840160005b83811015620046f65781516001600160a01b031687529582019590820190600101620046cf565b509495945050505050565b602081526000620013376020830184620046bb565b6000602082840312156200472957600080fd5b813562003dea8162004664565b6000602082840312156200474957600080fd5b81356001600160401b038111156200476057600080fd5b620040e18482850162004507565b60008083601f8401126200478157600080fd5b5081356001600160401b038111156200479957600080fd5b6020830191508360208260051b8501011115620047b557600080fd5b9250929050565b60008083601f840112620047cf57600080fd5b5081356001600160401b03811115620047e757600080fd5b602083019150836020828501011115620047b557600080fd5b60008060008060008060008060008060a08b8d0312156200482057600080fd5b8a356001600160401b03808211156200483857600080fd5b620048468e838f016200476e565b909c509a5060208d01359150808211156200486057600080fd5b6200486e8e838f016200476e565b909a50985060408d01359150808211156200488857600080fd5b620048968e838f016200476e565b909850965060608d0135915080821115620048b057600080fd5b620048be8e838f016200476e565b909650945060808d0135915080821115620048d857600080fd5b50620048e78d828e01620047bc565b915080935050809150509295989b9194979a5092959850565b6000602080830181845280855180835260408601915060408160051b87010192508387016000805b838110156200498057888603603f19018552825180518088529088019088880190845b81811015620049695783518352928a0192918a01916001016200494b565b509097505050938601939186019160010162004928565b509398975050505050505050565b60008060408385031215620049a257600080fd5b8235620049af8162004415565b915060208301356001600160401b03811115620049cb57600080fd5b620049d98582860162004507565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060001982141562004a265762004a26620049f9565b5060010190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b60008262004a9957634e487b7160e01b600052601260045260246000fd5b500490565b6020808252600190820152600360fc1b604082015260600190565b6020808252600190820152603760f81b604082015260600190565b60008282101562004ae95762004ae9620049f9565b500390565b6020808252600190820152603560f81b604082015260600190565b60006020828403121562004b1c57600080fd5b815162003dea8162004415565b60005b8381101562004b4657818101518382015260200162004b2c565b83811115620038f15750506000910152565b60006020828403121562004b6b57600080fd5b81516001600160401b038082111562004b8357600080fd5b818401915084601f83011262004b9857600080fd5b81518181111562004bad5762004bad62004498565b62004bc2601f8201601f1916602001620044ae565b915080825285602082850101111562004bda57600080fd5b620016e381602084016020860162004b29565b6000815180845262004c0781602086016020860162004b29565b601f01601f19169290920160200192915050565b60408152600062004c30604083018562004bed565b828103602084015262004c44818562004bed565b95945050505050565b60006020828403121562004c6057600080fd5b5051919050565b600181815b8085111562004ca857816000190482111562004c8c5762004c8c620049f9565b8085161562004c9a57918102915b93841c939080029062004c6c565b509250929050565b60008262004cc1575060016200133a565b8162004cd0575060006200133a565b816001811462004ce9576002811462004cf45762004d14565b60019150506200133a565b60ff84111562004d085762004d08620049f9565b50506001821b6200133a565b5060208310610133831016604e8410600b841016171562004d39575081810a6200133a565b62004d45838362004c67565b806000190482111562004d5c5762004d5c620049f9565b029392505050565b600062001337838362004cb0565b6000821982111562004d885762004d88620049f9565b500190565b600081600019048311821515161562004daa5762004daa620049f9565b500290565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252600190820152603360f81b604082015260600190565b6020808252600190820152600760fb1b604082015260600190565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b6000602080838503121562004e5d57600080fd5b82516001600160401b0381111562004e7457600080fd5b8301601f8101851362004e8657600080fd5b805162004e976200452c82620044e1565b81815260059190911b8201830190838101908783111562004eb757600080fd5b928401925b82841015620043295783518252928401929084019062004ebc565b6020808252600190820152601b60f91b604082015260600190565b6001600160a01b03929092168252602082015260400190565b6000835162004f1f81846020880162004b29565b83519083019062004f3581836020880162004b29565b01949350505050565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b6000602080838503121562004f7c57600080fd5b82516001600160401b0381111562004f9357600080fd5b8301601f8101851362004fa557600080fd5b805162004fb66200452c82620044e1565b81815260059190911b8201830190838101908783111562004fd657600080fd5b928401925b828410156200432957835162004ff18162004415565b8252928401929084019062004fdb565b6000602082840312156200501457600080fd5b815162003dea8162004664565b60609290921b6bffffffffffffffffffffffff19168252601482015260340190565b85815284602082015260a0604082015260006200506460a0830186620046bb565b6001600160a01b0394909416606083015250608001529392505050565b600082516200509581846020870162004b29565b9190910192915050565b60208152600062001337602083018462004bed56fe60806040523480156200001157600080fd5b5060405162000ebb38038062000ebb8339810160408190526200003491620001f4565b8151829082906200004d90600390602085019062000081565b5080516200006390600490602084019062000081565b5050600580546001600160a01b03191633179055506200029b915050565b8280546200008f906200025e565b90600052602060002090601f016020900481019282620000b35760008555620000fe565b82601f10620000ce57805160ff1916838001178555620000fe565b82800160010185558215620000fe579182015b82811115620000fe578251825591602001919060010190620000e1565b506200010c92915062000110565b5090565b5b808211156200010c576000815560010162000111565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200014f57600080fd5b81516001600160401b03808211156200016c576200016c62000127565b604051601f8301601f19908116603f0116810190828211818310171562000197576200019762000127565b81604052838152602092508683858801011115620001b457600080fd5b600091505b83821015620001d85785820183015181830184015290820190620001b9565b83821115620001ea5760008385830101525b9695505050505050565b600080604083850312156200020857600080fd5b82516001600160401b03808211156200022057600080fd5b6200022e868387016200013d565b935060208501519150808211156200024557600080fd5b5062000254858286016200013d565b9150509250929050565b600181811c908216806200027357607f821691505b602082108114156200029557634e487b7160e01b600052602260045260246000fd5b50919050565b610c1080620002ab6000396000f3fe608060405234801561001057600080fd5b50600436106100ca5760003560e01c806340c10f191161007c57806340c10f191461018257806370a082311461019757806395d89b41146101c05780639dc29fac146101c8578063a457c2d7146101db578063a9059cbb146101ee578063dd62ed3e1461020157600080fd5b806306fdde03146100cf57806307546172146100ed578063095ea7b31461011857806318160ddd1461013b57806323b872dd1461014d578063313ce56714610160578063395093511461016f575b600080fd5b6100d761023a565b6040516100e491906109e7565b60405180910390f35b600554610100906001600160a01b031681565b6040516001600160a01b0390911681526020016100e4565b61012b610126366004610a58565b6102cc565b60405190151581526020016100e4565b6002545b6040519081526020016100e4565b61012b61015b366004610a82565b6102e2565b604051601281526020016100e4565b61012b61017d366004610a58565b610398565b610195610190366004610a58565b6103cf565b005b61013f6101a5366004610abe565b6001600160a01b031660009081526020819052604090205490565b6100d7610407565b6101956101d6366004610a58565b610416565b61012b6101e9366004610a58565b61044a565b61012b6101fc366004610a58565b6104e5565b61013f61020f366004610ae0565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60606003805461024990610b13565b80601f016020809104026020016040519081016040528092919081815260200182805461027590610b13565b80156102c25780601f10610297576101008083540402835291602001916102c2565b820191906000526020600020905b8154815290600101906020018083116102a557829003601f168201915b5050505050905090565b60006102d93384846104f2565b50600192915050565b60006102ef848484610617565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156103795760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b61038d85336103888685610b64565b6104f2565b506001949350505050565b3360008181526001602090815260408083206001600160a01b038716845290915281205490916102d9918590610388908690610b7b565b6005546001600160a01b031633146103f95760405162461bcd60e51b815260040161037090610b93565b61040382826107dd565b5050565b60606004805461024990610b13565b6005546001600160a01b031633146104405760405162461bcd60e51b815260040161037090610b93565b61040382826108aa565b3360009081526001602090815260408083206001600160a01b0386168452909152812054828110156104cc5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610370565b6104db33856103888685610b64565b5060019392505050565b60006102d9338484610617565b6001600160a01b0383166105545760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610370565b6001600160a01b0382166105b55760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610370565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b03831661067b5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610370565b6001600160a01b0382166106dd5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610370565b6001600160a01b038316600090815260208190526040902054818110156107555760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610370565b61075f8282610b64565b6001600160a01b038086166000908152602081905260408082209390935590851681529081208054849290610795908490610b7b565b92505081905550826001600160a01b0316846001600160a01b0316600080516020610bbb833981519152846040516107cf91815260200190565b60405180910390a350505050565b6001600160a01b0382166108335760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610370565b80600260008282546108459190610b7b565b90915550506001600160a01b03821660009081526020819052604081208054839290610872908490610b7b565b90915550506040518181526001600160a01b03831690600090600080516020610bbb8339815191529060200160405180910390a35050565b6001600160a01b03821661090a5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610370565b6001600160a01b0382166000908152602081905260409020548181101561097e5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610370565b6109888282610b64565b6001600160a01b038416600090815260208190526040812091909155600280548492906109b6908490610b64565b90915550506040518281526000906001600160a01b03851690600080516020610bbb8339815191529060200161060a565b600060208083528351808285015260005b81811015610a14578581018301518582016040015282016109f8565b81811115610a26576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b0381168114610a5357600080fd5b919050565b60008060408385031215610a6b57600080fd5b610a7483610a3c565b946020939093013593505050565b600080600060608486031215610a9757600080fd5b610aa084610a3c565b9250610aae60208501610a3c565b9150604084013590509250925092565b600060208284031215610ad057600080fd5b610ad982610a3c565b9392505050565b60008060408385031215610af357600080fd5b610afc83610a3c565b9150610b0a60208401610a3c565b90509250929050565b600181811c90821680610b2757607f821691505b60208210811415610b4857634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600082821015610b7657610b76610b4e565b500390565b60008219821115610b8e57610b8e610b4e565b500190565b6020808252600d908201526c0a8a4829c86908a744282aaa89609b1b60408201526060019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212200e6d35ef1ebff3fb23df47e3bb81d32388bde501b266ddc8ce3aaf280dd89ce464736f6c634300080a00338be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0a2646970667358221220cff232f965e34c6431035c3f70f7e0b6011ba39f963ef823f16365a840daf58964736f6c634300080a0033

Deployed Bytecode

0x60806040523480156200001157600080fd5b5060043610620004415760003560e01c80638456cb591162000239578063c9aba356116200013f578063e70875ad11620000c2578063e70875ad1462000967578063eb120bf4146200097e578063eb67acb51462000992578063edd636fb14620009a9578063efdcd97414620009b3578063f22ff9f414620009ca578063f2fde38b14620009e1578063fc0c546a14620009f8578063fdf4c0dc1462000a0c578063fe81a0241462000a1657600080fd5b8063c9aba35614620008a2578063cc11580514620008b2578063cd962a0614620008c9578063d5357d8914620008e0578063dc82697c1462000906578063ddca3f431462000910578063dfd5b1c9146200091a578063e07eace61462000925578063e14488e4146200093c578063e3e39368146200095357600080fd5b80639d3ef4b211620001c85780639d3ef4b214620007eb5780639e5914da1462000800578063a219d2181462000817578063a27eccc1146200082e578063a3d0bd481462000838578063a4d66daf146200084f578063a8c62e761462000859578063b3f00674146200086d578063b450dfce1462000881578063bc063e1a146200089857600080fd5b80638456cb5914620007435780638a0dac4a146200074d5780638da5cb5b14620007645780638f5aa090146200076e5780639290d427146200078557806392b29be3146200079c57806394929dc114620007a657806399abe5e814620007bd5780639af6485414620007d457600080fd5b80633f4ba83a116200034b57806362adade511620002ce57806362adade514620006965780636373ea6914620006a057806369fe0e2d14620006aa5780636cfd155314620006c15780636f47d99b14620006d8578063715018a614620006ed578063734d828714620006f7578063747efea114620007015780637692ee0a1462000715578063812e43fa146200072c57600080fd5b80633f4ba83a14620005e05780633fc8cef314620005ea578063400f7d3214620005fe578063408f3a30146200060857806344966ba0146200061f57806344ca67ec1462000633578063452a9320146200064a57806346840590146200065e57806355c7070114620006755780635c975abb146200068a57600080fd5b80631ed4fd9711620003d45780631ed4fd9714620005205780632047782a146200054657806325cdd860146200055057806325f66adf146200055a5780632758db0c146200056457806329811ee5146200057b5780632a3e4dc214620005925780633403c2fc14620005a9578063387af1bd14620005b35780633b28d53714620005c757600080fd5b806301d22ccd1462000446578063085a2b7814620004725780630986950b146200048b5780631072cbea14620004a457806312361ac214620004bb5780631441a5a914620004c557806315c44b8b14620004de57806318dc719514620004f25780631cf7a8ff1462000509575b600080fd5b60d2546200045a906001600160a01b031681565b60405162000469919062004401565b60405180910390f35b6200047c60da5481565b60405190815260200162000469565b620004a26200049c3660046200442b565b62000a20565b005b620004a2620004b536600462004469565b62000c71565b6200047c60dc5481565b60e8546200045a9061010090046001600160a01b031681565b60d9546200045a906001600160a01b031681565b620004a26200050336600462004585565b62000c9b565b620004a26200051a3660046200464a565b6200126e565b60d3546200053590600160b81b900460ff1681565b604051901515815260200162000469565b6200047c60e55481565b6200047c620012a6565b6200047c60db5481565b6200047c6200057536600462004673565b6200131f565b6200047c6200058c3660046200469b565b62001340565b6200045a620005a33660046200464a565b62001357565b620004a262001382565b60ca546200045a906001600160a01b031681565b620005d1620013af565b60405162000469919062004701565b620004a262001413565b60cd546200045a906001600160a01b031681565b6200047c60dd5481565b620004a2620006193660046200464a565b62001429565b60d6546200045a906001600160a01b031681565b620004a2620006443660046200464a565b62001438565b60d0546200045a906001600160a01b031681565b620004a26200066f36600462004716565b620014a0565b60d3546200053590600160a01b900460ff1681565b60335460ff1662000535565b6200047c60de5481565b6200047c60e95481565b620004a2620006bb3660046200464a565b620014c8565b620004a2620006d23660046200469b565b620014fc565b60d3546200053590600160b01b900460ff1681565b620004a262001543565b6200047c60e15481565b60d5546200045a906001600160a01b031681565b620004a26200072636600462004736565b620015b0565b620004a26200073d3660046200464a565b620015cf565b620004a262001604565b620004a26200075e3660046200469b565b62001618565b6200045a6200165f565b620004a26200077f36600462004716565b6200166e565b6200047c620007963660046200469b565b62001696565b6200047c60e25481565b620004a2620007b73660046200464a565b620016ec565b6200047c620007ce3660046200464a565b620016fb565b6200047c620007e53660046200464a565b62001758565b60d3546200053590600160a81b900460ff1681565b620004a2620008113660046200469b565b620017b5565b6200047c620008283660046200469b565b620017e7565b6200047c60d15481565b620004a2620008493660046200464a565b620017f4565b6200047c60c95481565b60d4546200045a906001600160a01b031681565b60e4546200045a906001600160a01b031681565b6200047c620008923660046200464a565b62001829565b6200047c614e2081565b6200047c670de0b6b3a764000081565b620004a2620008c336600462004716565b62001845565b620004a2620008da3660046200464a565b6200186d565b620008f7620008f136600462004800565b620018a2565b60405162000469919062004900565b6200047c62001c7e565b6200047c60e35481565b6200047c620186a081565b620004a26200093636600462004716565b62001d77565b6200047c6200094d3660046200469b565b62001d9f565b60d7546200045a906001600160a01b031681565b6200047c620009783660046200464a565b62001daf565b60d8546200045a906001600160a01b031681565b620004a2620009a336600462004716565b62001dcb565b6200047c60e65481565b620004a2620009c43660046200469b565b62001de8565b620004a2620009db3660046200498e565b62001e2f565b620004a2620009f23660046200469b565b62001f7a565b60cf546200045a906001600160a01b031681565b6200047c60e05481565b6200047c60df5481565b62000a2a62002060565b60d65460d75460ce8054604080516020808402820181019092528281526001600160a01b0395861695909416936000939092909183018282801562000a9957602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162000a7a575b505060d85460d9549495506001600160a01b039081169416925050508115158115156000805b865181101562000b235786818151811062000ade5762000ade620049e3565b60200260200101519150831562000afb5762000afb828762002094565b821562000b0e5762000b0e828662002094565b8062000b1a8162004a0f565b91505062000abf565b5082801562000b3a57506001600160a01b03881615155b1562000b4c5762000b4c888662002094565b81801562000b6257506001600160a01b03871615155b1562000b745762000b74878562002094565b60d880546001600160a01b03808d166001600160a01b0319928316811790935560d98054918d1691909216811790915590151593501515915060005b865181101562000c145786818151811062000bcf5762000bcf620049e3565b60200260200101519150831562000bec5762000bec828c620020ab565b821562000bff5762000bff828b620020ab565b8062000c0b8162004a0f565b91505062000bb0565b5082801562000c2b57506001600160a01b03881615155b1562000c3d5762000c3d888b620020ab565b81801562000c5357506001600160a01b03871615155b1562000c655762000c65878a620020ab565b50505050505050505050565b62000c7b62002060565b60ca5462000c97906001600160a01b03848116911683620020c3565b5050565b600054610100900460ff168062000cb5575060005460ff16155b62000cdd5760405162461bcd60e51b815260040162000cd49062004a2d565b60405180910390fd5b600054610100900460ff1615801562000d00576000805461ffff19166101011790555b600062000d12600a620186a062004a7b565b60cf549091506001600160a01b03161562000d545760405162461bcd60e51b81526020600482015260016024820152603160f81b604482015260640162000cd4565b6001600160a01b0387161580159062000d7557506001600160a01b03861615155b801562000d8a57506001600160a01b038a1615155b62000da95760405162461bcd60e51b815260040162000cd49062004a9e565b620186a085111562000dcf5760405162461bcd60e51b815260040162000cd49062004ab9565b62000dde81620186a062004ad4565b84111562000e005760405162461bcd60e51b815260040162000cd49062004ab9565b8084101562000e235760405162461bcd60e51b815260040162000cd49062004aee565b62000e2d62002122565b62000e3a8b8a8a620021a7565b6000866001600160a01b031663747efea16040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000e7b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000ea1919062004b09565b90506000816001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000ee4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000f0e919081019062004b58565b905062000f4960405180604001604052806015815260200174024b23632a1a2279020a0902a3930b731b43290169605d1b8152508262002238565b62000f706040518060400160405280600381526020016241415f60e81b8152508362002238565b60405162000f7e9062004372565b62000f8b92919062004c1b565b604051809103906000f08015801562000fa8573d6000803e3d6000fd5b5060d680546001600160a01b0319166001600160a01b0392909216919091179055604080518082019091526015815274024b23632a1a227902121102a3930b731b43290169605d1b602082015262001001908262002238565b620010286040518060400160405280600381526020016242425f60e81b8152508362002238565b604051620010369062004372565b6200104392919062004c1b565b604051809103906000f08015801562001060573d6000803e3d6000fd5b5060d780546001600160a01b03199081166001600160a01b039384161790915560cf805482168f841690811790915560d4805483168c851617905560d58054831686851617905560d28054909216928c1692909217905560da88905560db87905560e58490556040805163313ce56760e01b815290516000929163313ce5679160048083019260209291908290030181865afa15801562001105573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200112b919062004c4d565b6200113890600a62004d64565b60d181905560d380546001600160a01b0319908116737a250d5630b4cf539739df2c5dacb4c659f2488d1790915560cd805490911673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc217905586519091506200119d9060ce90602089019062004380565b5060dc81905560dd8190556107d060e2556105dc60e65560d3805462ffffff60a01b19166201010160a01b179055620011d78d8a620020ab565b60d554620011ef906001600160a01b03168a620020ab565b620011f962002266565b60e055505061271060e355505060e4805473fb3bd022d5dacf95ee28a6b07825d4ff9c5b38146001600160a01b03199182161790915560d080549091166001600160a01b03891617905560e8805460ff19166001179055801562000c65576000805461ff001916905550505050505050505050565b6200127862002060565b620186a08160e28190551115620012a35760405162461bcd60e51b815260040162000cd49062004ab9565b50565b60d6546000908190620012c2906001600160a01b0316620022dc565b60d754909150600090620012df906001600160a01b0316620022dc565b620012eb908362004d72565b905080620012fc5760009250505090565b806200130c620186a08462004d8d565b62001318919062004a7b565b9250505090565b60006200132b6200236f565b620013378383620023c2565b90505b92915050565b60006200133a8262001351620012a6565b6200247d565b60ce81815481106200136857600080fd5b6000918252602090912001546001600160a01b0316905081565b6200138c62002598565b62001396620025b7565b60d3805463ffffffff60a01b191661010160b01b179055565b606060ce8054806020026020016040519081016040528092919081815260200182805480156200140957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311620013ea575b5050505050905090565b6200141d62002598565b620014276200262c565b565b6200143362002060565b60c955565b6200144262002060565b60e554811015620014675760405162461bcd60e51b815260040162000cd49062004aee565b60e5546200147990620186a062004ad4565b8160db8190551115620012a35760405162461bcd60e51b815260040162000cd49062004ab9565b620014aa62002060565b60d38054911515600160b81b0260ff60b81b19909216919091179055565b620014d262002060565b614e208160e38190551115620012a35760405162461bcd60e51b815260040162000cd49062004ab9565b6200150662002060565b60d280546001600160a01b0319166001600160a01b038316908117909155620012a35760405162461bcd60e51b815260040162000cd49062004a9e565b336200154e6200165f565b6001600160a01b031614620015775760405162461bcd60e51b815260040162000cd49062004daf565b6065546040516000916001600160a01b03169060008051602062005f70833981519152908390a3606580546001600160a01b0319169055565b620015ba62002060565b805162000c979060ce90602084019062004380565b620015d962002060565b620186a08160e58190551115620012a35760405162461bcd60e51b815260040162000cd49062004ab9565b6200160e62002598565b62001427620025b7565b6200162262002060565b60d080546001600160a01b0319166001600160a01b038316908117909155620012a35760405162461bcd60e51b815260040162000cd49062004a9e565b6065546001600160a01b031690565b6200167862002060565b60d38054911515600160a81b0260ff60a81b19909216919091179055565b60de5460df5460009190620016e384620016af62001c7e565b620016bb848662004d72565b60d6546001600160a01b03898116911614620016d85784620016da565b855b60da54620026a9565b50949350505050565b620016f662002060565b60e655565b60006200170a60335460ff1690565b158062001720575060d354600160a81b900460ff165b6200173f5760405162461bcd60e51b815260040162000cd49062004de4565b60d7546200133a9083906001600160a01b0316620028bb565b60006200176760335460ff1690565b15806200177d575060d354600160a01b900460ff165b6200179c5760405162461bcd60e51b815260040162000cd49062004de4565b60d6546200133a9083906001600160a01b0316620028bb565b620017bf62002060565b60e880546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b60006200133a8262002b31565b620017fe62002060565b620186a08160da8190551115620012a35760405162461bcd60e51b815260040162000cd49062004ab9565b60d6546000906200133a9083906001600160a01b031662002bcc565b6200184f62002060565b60d38054911515600160a01b0260ff60a01b19909216919091179055565b6200187762002060565b620186a08160e98190551115620012a35760405162461bcd60e51b815260040162000cd49062004dff565b6060620018ae6200236f565b6040805160038082526080820190925290816020015b6060815260200190600190039081620018c457505060d4549091506001600160a01b03168b8b6003818110620018fe57620018fe620049e3565b905060200201602081019062001915919062004716565b62001b995760008c8c6000818110620019325762001932620049e3565b905060200201602081019062001949919062004716565b62001afd576040516323b1c78360e21b81526001600160a01b03831690638ec71e0c906200197e908890889060040162004e1a565b6000604051808303816000875af11580156200199e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620019c8919081019062004e49565b83600281518110620019de57620019de620049e3565b6020026020010181905250620019f362002c57565b62001ab682888880806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508b8b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508e8e8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525062002f9f92505050565b8560008151811062001acc5762001acc620049e3565b602002602001018660018151811062001ae95762001ae9620049e3565b602002602001018295508390528390525050505b4360cc55801562001b0f578062001b12565b60015b60e75562001b1f62003358565b8c8c600281811062001b355762001b35620049e3565b905060200201602081019062001b4c919062004716565b62001b5b5762001b5b6200345d565b8c8c600181811062001b715762001b71620049e3565b905060200201602081019062001b88919062004716565b62001b975762001b9762003581565b505b60cf5460009062001bb3906001600160a01b031662003665565b90506000620186a060e25462001bc862001c7e565b62001bd4919062004d8d565b62001be0919062004a7b565b90508082111562001c6d576001600160a01b03831663b6b55f2562001c06838562004ad4565b6040518263ffffffff1660e01b815260040162001c2591815260200190565b6020604051808303816000875af115801562001c45573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001c6b919062004c4d565b505b5050509a9950505050505050505050565b60d5546040805163313ce56760e01b815290516000926001600160a01b0316918391839163313ce5679160048083019260209291908290030181865afa15801562001ccd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001cf3919062004c4d565b905060e15462001d02620036da565b60cf5462001d19906001600160a01b031662003665565b62001d2684600a62004d64565b62001d3062002266565b62001d3b8762003665565b62001d47919062004d8d565b62001d53919062004a7b565b62001d5f919062004d72565b62001d6b919062004ad4565b62001318919062004ad4565b62001d8162002060565b60d38054911515600160b01b0260ff60b01b19909216919091179055565b60006200133a8260db546200247d565b60d7546000906200133a9083906001600160a01b031662002bcc565b62001dd562002060565b60e8805460ff1916911515919091179055565b62001df262002060565b60e480546001600160a01b0319166001600160a01b038316908117909155620012a35760405162461bcd60e51b815260040162000cd49062004a9e565b62001e3962002060565b6001600160a01b03821662001e625760405162461bcd60e51b815260040162000cd49062004a9e565b60cf5460d4546001600160a01b03918216911662001e81828262002094565b60d55462001e99906001600160a01b03168262002094565b60d480546001600160a01b0319166001600160a01b038616179055825162001ec99060ce90602086019062004380565b506000846001600160a01b031663747efea16040518163ffffffff1660e01b8152600401602060405180830381865afa15801562001f0b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001f31919062004b09565b60d580546001600160a01b0319166001600160a01b038316179055905062001f5a8386620020ab565b62001f668186620020ab565b62001f7062002266565b60e0555050505050565b3362001f856200165f565b6001600160a01b03161462001fae5760405162461bcd60e51b815260040162000cd49062004daf565b6001600160a01b038116620020155760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840162000cd4565b6065546040516001600160a01b0380841692169060008051602062005f7083398151915290600090a3606580546001600160a01b0319166001600160a01b0392909216919091179055565b336200206b6200165f565b6001600160a01b031614620014275760405162461bcd60e51b815260040162000cd49062004ed7565b62000c976001600160a01b0383168260006200373c565b62000c976001600160a01b0383168260001962003849565b6200211d8363a9059cbb60e01b8484604051602401620020e592919062004ef2565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152620038f7565b505050565b600054610100900460ff16806200213c575060005460ff16155b6200215b5760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff161580156200217e576000805461ffff19166101011790555b62002188620039d0565b6200219262003a40565b8015620012a3576000805461ff001916905550565b6001600160a01b038216620021d05760405162461bcd60e51b815260040162000cd49062004a9e565b6001600160a01b038116620021f95760405162461bcd60e51b815260040162000cd49062004a9e565b6200220362003abb565b6200220d62003b2b565b60c983905560ca80546001600160a01b0319166001600160a01b0384161790556200211d8162001f7a565b606082826040516020016200224f92919062004f0b565b604051602081830303815290604052905092915050565b60d4546040805163501ad8ff60e11b815290516000926001600160a01b03169163a035b1fe9160048083019260209291908290030181865afa158015620022b1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620022d7919062004c4d565b905090565b6000670de0b6b3a7640000620022f28362001696565b836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562002331573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002357919062004c4d565b62002363919062004d8d565b6200133a919062004a7b565b60d2546001600160a01b0316331480620023a357506200238e6200165f565b6001600160a01b0316336001600160a01b0316145b620014275760405162461bcd60e51b815260040162000cd49062004ed7565b60d45460405163852a12e360e01b8152600481018490526000916001600160a01b03169063852a12e3906024016020604051808303816000875af11580156200240f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002435919062004c4d565b905081156200246d57826200244c82606462004d72565b10156200246d5760405162461bcd60e51b815260040162000cd49062004aee565b828111156200133a575090919050565b60008060d460009054906101000a90046001600160a01b03166001600160a01b031663845bc8046040518163ffffffff1660e01b8152600401602060405180830381865afa158015620024d4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620024fa919062004c4d565b60da5460d654919250906001600160a01b038681169116148462002533578062002525578262002528565b60005b93505050506200133a565b8062002575576200254885620186a062004ad4565b6200255783620186a062004ad4565b62002563908562004d8d565b6200256f919062004a7b565b6200258e565b8462002582838562004d8d565b6200258e919062004a7b565b9695505050505050565b60d0546001600160a01b0316331480620023a357506200238e6200165f565b60335460ff1615620025dd5760405162461bcd60e51b815260040162000cd49062004f3e565b6033805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258620026133390565b60405162002622919062004401565b60405180910390a1565b60335460ff16620026775760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015260640162000cd4565b6033805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa3362002613565b600080848611620026cb57620026bf8762002b31565b600091509150620028b1565b6000876001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200270c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002732919062004c4d565b905085158062002740575080155b156200275557505060d15490506000620028b1565b600062002763878962004ad4565b9050620186a060e3548262002779919062004d8d565b62002785919062004a7b565b62002791908262004ad4565b60d6549091506001600160a01b03908116908a16811480620027b45781620027c1565b60d7546001600160a01b03165b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620027ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002825919062004c4d565b62002833578294506200287c565b6000620186a062002845898262004ad4565b62002851908662004d8d565b6200285d919062004a7b565b9050816200286c578062002878565b62002878818562004ad4565b9550505b83670de0b6b3a764000062002892878b62004d72565b6200289e919062004d8d565b620028aa919062004a7b565b9550505050505b9550959350505050565b600060026097541415620029125760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640162000cd4565b60026097556200292162003b91565b6200292b62003be0565b6200293562003358565b82620029b0576040516370a0823160e01b81526001600160a01b038316906370a08231906200296990339060040162004401565b602060405180830381865afa15801562002987573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620029ad919062004c4d565b92505b60008311620029d35760405162461bcd60e51b815260040162000cd49062004a9e565b60cf546001600160a01b03166000620029ec8262003665565b9050670de0b6b3a764000062002a028562002b31565b62002a0e908762004d8d565b62002a1a919062004a7b565b92508083111562002a58578062002a4962002a36828662004ad4565b60d354600160b01b900460ff16620023c2565b62002a55919062004d72565b92505b604051632770a7eb60e21b81526001600160a01b03851690639dc29fac9062002a88903390899060040162004ef2565b600060405180830381600087803b15801562002aa357600080fd5b505af115801562002ab8573d6000803e3d6000fd5b5062002ad3925050506001600160a01b0383163385620020c3565b60d6546001600160a01b038581169116141562002b0a578260de600082825462002afe919062004ad4565b9091555062002b249050565b8260df600082825462002b1e919062004ad4565b90915550505b5050600160975592915050565b6000816001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562002b72573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002b98919062004c4d565b62002ba557505060d15490565b60d6546001600160a01b0383811691161462002bc45760dd546200133a565b505060dc5490565b600062002bdb60335460ff1690565b1562002bfb5760405162461bcd60e51b815260040162000cd49062004f3e565b8262002c07576200133a565b62002c128362003c3e565b62002c1c62003c97565b62002c2662003be0565b62002c3062003358565b60cf5462002c4a906001600160a01b031633308662003cc9565b6200133783338462003d03565b60e85460ff1662002c6457565b60405163091030c360e01b8152734da27a545c0c5b758a6ba100e3a049001de870f590600090829063091030c39062002ca290309060040162004401565b602060405180830381865afa15801562002cc0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002ce6919062004c4d565b9050801562002e4d576000826001600160a01b03166372b49d636040518163ffffffff1660e01b8152600401602060405180830381865afa15801562002d30573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002d56919062004c4d565b62002d62908362004d72565b9050428110156200211d57826001600160a01b031663359c4a966040518163ffffffff1660e01b8152600401602060405180830381865afa15801562002dac573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002dd2919062004c4d565b62002dde824262004ad4565b1162002e4b576040516301e9a69560e41b81526001600160a01b03841690631e9a69509062002e169030906000199060040162004ef2565b600060405180830381600087803b15801562002e3157600080fd5b505af115801562002e46573d6000803e3d6000fd5b505050505b505b60d460009054906101000a90046001600160a01b03166001600160a01b031663cdfbe9c56040518163ffffffff1660e01b81526004016020604051808303816000875af115801562002ea3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002ec9919062004c4d565b506040516370a0823160e01b81526000906001600160a01b038416906370a082319062002efb90309060040162004401565b602060405180830381865afa15801562002f19573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002f3f919062004c4d565b111562000c9757816001600160a01b031663787a08a66040518163ffffffff1660e01b8152600401600060405180830381600087803b15801562002f8257600080fd5b505af115801562002f97573d6000803e3d6000fd5b505050505050565b60608060008060ce80548060200260200160405190810160405280929190818152602001828054801562002ffd57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162002fde575b505050505090506000886001600160a01b031663c4f59f9b6040518163ffffffff1660e01b8152600401600060405180830381865afa15801562003045573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526200306f919081019062004f68565b60408051600380825260808201909252919250600091829160208201606080368337505060cd5482519293506001600160a01b0316918391506001908110620030bc57620030bc620049e3565b6001600160a01b03928316602091820292909201015260cf54825191169082906002908110620030f057620030f0620049e3565b60200260200101906001600160a01b031690816001600160a01b03168152505082516001600160401b038111156200312c576200312c62004498565b60405190808252806020026020018201604052801562003156578160200160208202803683370190505b50965082516001600160401b0381111562003175576200317562004498565b6040519080825280602002602001820160405280156200319f578160200160208202803683370190505b50955060005b83518110156200334957838181518110620031c457620031c4620049e3565b60200260200101519250888181518110620031e357620031e3620049e3565b602002602001015180620031fe5750620031fe858462003df1565b156200320a5762003334565b6001600160a01b038316734da27a545c0c5b758a6ba100e3a049001de870f514156200324857737fc66500c84a76ad7e9c93437bfc5ac33e2ddae992505b82826000815181106200325f576200325f620049e3565b60200260200101906001600160a01b031690816001600160a01b031681525050620032c583838d84815181106200329a576200329a620049e3565b60200260200101518d8581518110620032b757620032b7620049e3565b602002602001015162003e60565b898381518110620032da57620032da620049e3565b60200260200101898481518110620032f657620032f6620049e3565b60200260200101828152508281525050508681815181106200331c576200331c620049e3565b60200260200101518662003331919062004d72565b95505b80620033408162004a0f565b915050620031a5565b50505050509450945094915050565b60de5460df5460006200336c828462004d72565b905060006200337a62001c7e565b60da5490915082821115620033ce5760e354620186a0906200339d858562004ad4565b620033a9919062004d8d565b620033b5919062004a7b565b60e16000828254620033c8919062004d72565b90915550505b60d6546000908190620033ee906001600160a01b031685878a87620026a9565b60d7549193509150600090819062003413906001600160a01b031687898b89620026a9565b915091508260de60008282546200342b919062004d72565b925050819055508060df600082825462003446919062004d72565b90915550505060dc929092555060dd555050505050565b60e1548015620012a35760d85460e45460e8546001600160a01b03928316928315159281169161010090041660008115620034d257620186a060e95487620034a6919062004d8d565b620034b2919062004a7b565b60d654909150620034d090829084906001600160a01b031662003d03565b505b600062003505620034e4838962004ad4565b86620034f15785620034f3565b305b60d6546001600160a01b031662003d03565b600060e1559050841562003578576040516305dc812160e31b81526001600160a01b03871690632ee409089062003543908790859060040162004ef2565b600060405180830381600087803b1580156200355e57600080fd5b505af115801562003573573d6000803e3d6000fd5b505050505b50505050505050565b60db5460da5460e55460d95460d8546001600160a01b0391821691168115801590821515906000908390620035b35750815b15620035c657620035c3620012a6565b90505b828015620035e75750811580620035e75750620035e4868962004d72565b81115b156200360657620035fc85620186a062003f91565b5050505050505050565b81801562003627575082158062003627575062003624868962004ad4565b81105b156200363c57620035fc84620186a062003f91565b81156200364f576200364f848862003f91565b8215620035fc57620035fc85620186a062003f91565b6040516370a0823160e01b81526000906001600160a01b038316906370a08231906200369690309060040162004401565b602060405180830381865afa158015620036b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200133a919062004c4d565b60e65460cc54600091908290620036f2904362004ad4565b60e7549091506001811180156200370857508282105b156200373657826200371b838262004ad4565b62003727908362004d8d565b62003733919062004a7b565b93505b50505090565b801580620037ba5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa15801562003792573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620037b8919062004c4d565b155b620038275760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606482015260840162000cd4565b6200211d8363095ea7b360e01b8484604051602401620020e592919062004ef2565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa1580156200389b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620038c1919062004c4d565b620038cd919062004d72565b9050620038f18463095ea7b360e01b8584604051602401620020e592919062004ef2565b50505050565b60006200394e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316620040d09092919063ffffffff16565b8051909150156200211d57808060200190518101906200396f919062005001565b6200211d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840162000cd4565b600054610100900460ff1680620039ea575060005460ff16155b62003a095760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff1615801562002192576000805461ffff19166101011790558015620012a3576000805461ff001916905550565b600054610100900460ff168062003a5a575060005460ff16155b62003a795760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff1615801562003a9c576000805461ffff19166101011790555b6033805460ff191690558015620012a3576000805461ff001916905550565b600054610100900460ff168062003ad5575060005460ff16155b62003af45760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff1615801562003b17576000805461ffff19166101011790555b62003b21620039d0565b62002192620040e9565b600054610100900460ff168062003b45575060005460ff16155b62003b645760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff1615801562003b87576000805461ffff19166101011790555b620021926200418c565b60cb54324360405160200162003ba992919062005021565b604051602081830303815290604052805190602001201415620014275760405162461bcd60e51b815260040162000cd49062004dff565b600062003bec62002266565b60d354909150600160b81b900460ff1662003c39578060e054111562003c395760405162461bcd60e51b81526020600482015260016024820152600d60fa1b604482015260640162000cd4565b60e055565b60c9548062003c4b575050565b808262003c5762001c7e565b62003c63919062004d72565b111562000c975760405162461bcd60e51b81526020600482015260016024820152601960f91b604482015260640162000cd4565b324360405160200162003cac92919062005021565b60408051601f19818403018152919052805160209091012060cb55565b6040516001600160a01b0380851660248301528316604482015260648101829052620038f19085906323b872dd60e01b90608401620020e5565b600062003d108262002b31565b62003d24670de0b6b3a76400008662004d8d565b62003d30919062004a7b565b6040516340c10f1960e01b81529091506001600160a01b038316906340c10f199062003d63908690859060040162004ef2565b600060405180830381600087803b15801562003d7e57600080fd5b505af115801562003d93573d6000803e3d6000fd5b505060d6546001600160a01b0385811691161415915062003dd09050578360de600082825462003dc4919062004d72565b9091555062003dea9050565b8360df600082825462003de4919062004d72565b90915550505b9392505050565b6000805b835181101562003e5657826001600160a01b031684828151811062003e1e5762003e1e620049e3565b60200260200101516001600160a01b0316141562003e415760019150506200133a565b8062003e4d8162004a0f565b91505062003df5565b5060009392505050565b6000808362003e775762003e748662003665565b93505b8362003e895750600090508062003f88565b60d3546001600160a01b039081169062003ea7908816828762003849565b60006001600160a01b0382166338ed173987878a3062003ec942600162004d72565b6040518663ffffffff1660e01b815260040162003eeb95949392919062005043565b6000604051808303816000875af115801562003f0b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262003f35919081019062004e49565b90508060008151811062003f4d5762003f4d620049e3565b6020026020010151816001835162003f66919062004ad4565b8151811062003f795762003f79620049e3565b60200260200101519350935050505b94509492505050565b600060ce80548060200260200160405190810160405280929190818152602001828054801562003feb57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162003fcc575b5050505050905060005b8151811015620038f1576000828281518110620040165762004016620049e3565b602002602001015190506000620186a085620040328462003665565b6200403e919062004d8d565b6200404a919062004a7b565b90508015620040b857604051637db4e28f60e01b81526001600160a01b03871690637db4e28f9062004083908590859060040162004ef2565b600060405180830381600087803b1580156200409e57600080fd5b505af1158015620040b3573d6000803e3d6000fd5b505050505b50508080620040c79062004a0f565b91505062003ff5565b6060620040e1848460008562004202565b949350505050565b600054610100900460ff168062004103575060005460ff16155b620041225760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff1615801562004145576000805461ffff19166101011790555b606580546001600160a01b03191633908117909155604051819060009060008051602062005f70833981519152908290a3508015620012a3576000805461ff001916905550565b600054610100900460ff1680620041a6575060005460ff16155b620041c55760405162461bcd60e51b815260040162000cd49062004a2d565b600054610100900460ff16158015620041e8576000805461ffff19166101011790555b60016097558015620012a3576000805461ff001916905550565b606082471015620042655760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000cd4565b843b620042b55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000cd4565b600080866001600160a01b03168587604051620042d3919062005081565b60006040518083038185875af1925050503d806000811462004312576040519150601f19603f3d011682016040523d82523d6000602084013e62004317565b606091505b50915091506200432982828662004334565b979650505050505050565b606083156200434557508162003dea565b825115620043565782518084602001fd5b8160405162461bcd60e51b815260040162000cd491906200509f565b610ebb80620050b583390190565b828054828255906000526020600020908101928215620043d8579160200282015b82811115620043d857825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190620043a1565b50620043e6929150620043ea565b5090565b5b80821115620043e65760008155600101620043eb565b6001600160a01b0391909116815260200190565b6001600160a01b0381168114620012a357600080fd5b600080604083850312156200443f57600080fd5b82356200444c8162004415565b915060208301356200445e8162004415565b809150509250929050565b600080604083850312156200447d57600080fd5b82356200448a8162004415565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715620044d957620044d962004498565b604052919050565b60006001600160401b03821115620044fd57620044fd62004498565b5060051b60200190565b600082601f8301126200451957600080fd5b81356020620045326200452c83620044e1565b620044ae565b82815260059290921b840181019181810190868411156200455257600080fd5b8286015b848110156200457a5780356200456c8162004415565b835291830191830162004556565b509695505050505050565b60008060008060008060008060006101208a8c031215620045a557600080fd5b8935985060208a0135620045b98162004415565b975060408a0135620045cb8162004415565b965060608a0135620045dd8162004415565b955060808a0135620045ef8162004415565b945060a08a0135620046018162004415565b935060c08a0135925060e08a013591506101008a01356001600160401b038111156200462c57600080fd5b6200463a8c828d0162004507565b9150509295985092959850929598565b6000602082840312156200465d57600080fd5b5035919050565b8015158114620012a357600080fd5b600080604083850312156200468757600080fd5b8235915060208301356200445e8162004664565b600060208284031215620046ae57600080fd5b813562003dea8162004415565b600081518084526020808501945080840160005b83811015620046f65781516001600160a01b031687529582019590820190600101620046cf565b509495945050505050565b602081526000620013376020830184620046bb565b6000602082840312156200472957600080fd5b813562003dea8162004664565b6000602082840312156200474957600080fd5b81356001600160401b038111156200476057600080fd5b620040e18482850162004507565b60008083601f8401126200478157600080fd5b5081356001600160401b038111156200479957600080fd5b6020830191508360208260051b8501011115620047b557600080fd5b9250929050565b60008083601f840112620047cf57600080fd5b5081356001600160401b03811115620047e757600080fd5b602083019150836020828501011115620047b557600080fd5b60008060008060008060008060008060a08b8d0312156200482057600080fd5b8a356001600160401b03808211156200483857600080fd5b620048468e838f016200476e565b909c509a5060208d01359150808211156200486057600080fd5b6200486e8e838f016200476e565b909a50985060408d01359150808211156200488857600080fd5b620048968e838f016200476e565b909850965060608d0135915080821115620048b057600080fd5b620048be8e838f016200476e565b909650945060808d0135915080821115620048d857600080fd5b50620048e78d828e01620047bc565b915080935050809150509295989b9194979a5092959850565b6000602080830181845280855180835260408601915060408160051b87010192508387016000805b838110156200498057888603603f19018552825180518088529088019088880190845b81811015620049695783518352928a0192918a01916001016200494b565b509097505050938601939186019160010162004928565b509398975050505050505050565b60008060408385031215620049a257600080fd5b8235620049af8162004415565b915060208301356001600160401b03811115620049cb57600080fd5b620049d98582860162004507565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060001982141562004a265762004a26620049f9565b5060010190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b60008262004a9957634e487b7160e01b600052601260045260246000fd5b500490565b6020808252600190820152600360fc1b604082015260600190565b6020808252600190820152603760f81b604082015260600190565b60008282101562004ae95762004ae9620049f9565b500390565b6020808252600190820152603560f81b604082015260600190565b60006020828403121562004b1c57600080fd5b815162003dea8162004415565b60005b8381101562004b4657818101518382015260200162004b2c565b83811115620038f15750506000910152565b60006020828403121562004b6b57600080fd5b81516001600160401b038082111562004b8357600080fd5b818401915084601f83011262004b9857600080fd5b81518181111562004bad5762004bad62004498565b62004bc2601f8201601f1916602001620044ae565b915080825285602082850101111562004bda57600080fd5b620016e381602084016020860162004b29565b6000815180845262004c0781602086016020860162004b29565b601f01601f19169290920160200192915050565b60408152600062004c30604083018562004bed565b828103602084015262004c44818562004bed565b95945050505050565b60006020828403121562004c6057600080fd5b5051919050565b600181815b8085111562004ca857816000190482111562004c8c5762004c8c620049f9565b8085161562004c9a57918102915b93841c939080029062004c6c565b509250929050565b60008262004cc1575060016200133a565b8162004cd0575060006200133a565b816001811462004ce9576002811462004cf45762004d14565b60019150506200133a565b60ff84111562004d085762004d08620049f9565b50506001821b6200133a565b5060208310610133831016604e8410600b841016171562004d39575081810a6200133a565b62004d45838362004c67565b806000190482111562004d5c5762004d5c620049f9565b029392505050565b600062001337838362004cb0565b6000821982111562004d885762004d88620049f9565b500190565b600081600019048311821515161562004daa5762004daa620049f9565b500290565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252600190820152603360f81b604082015260600190565b6020808252600190820152600760fb1b604082015260600190565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b6000602080838503121562004e5d57600080fd5b82516001600160401b0381111562004e7457600080fd5b8301601f8101851362004e8657600080fd5b805162004e976200452c82620044e1565b81815260059190911b8201830190838101908783111562004eb757600080fd5b928401925b82841015620043295783518252928401929084019062004ebc565b6020808252600190820152601b60f91b604082015260600190565b6001600160a01b03929092168252602082015260400190565b6000835162004f1f81846020880162004b29565b83519083019062004f3581836020880162004b29565b01949350505050565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b6000602080838503121562004f7c57600080fd5b82516001600160401b0381111562004f9357600080fd5b8301601f8101851362004fa557600080fd5b805162004fb66200452c82620044e1565b81815260059190911b8201830190838101908783111562004fd657600080fd5b928401925b828410156200432957835162004ff18162004415565b8252928401929084019062004fdb565b6000602082840312156200501457600080fd5b815162003dea8162004664565b60609290921b6bffffffffffffffffffffffff19168252601482015260340190565b85815284602082015260a0604082015260006200506460a0830186620046bb565b6001600160a01b0394909416606083015250608001529392505050565b600082516200509581846020870162004b29565b9190910192915050565b60208152600062001337602083018462004bed56fe60806040523480156200001157600080fd5b5060405162000ebb38038062000ebb8339810160408190526200003491620001f4565b8151829082906200004d90600390602085019062000081565b5080516200006390600490602084019062000081565b5050600580546001600160a01b03191633179055506200029b915050565b8280546200008f906200025e565b90600052602060002090601f016020900481019282620000b35760008555620000fe565b82601f10620000ce57805160ff1916838001178555620000fe565b82800160010185558215620000fe579182015b82811115620000fe578251825591602001919060010190620000e1565b506200010c92915062000110565b5090565b5b808211156200010c576000815560010162000111565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200014f57600080fd5b81516001600160401b03808211156200016c576200016c62000127565b604051601f8301601f19908116603f0116810190828211818310171562000197576200019762000127565b81604052838152602092508683858801011115620001b457600080fd5b600091505b83821015620001d85785820183015181830184015290820190620001b9565b83821115620001ea5760008385830101525b9695505050505050565b600080604083850312156200020857600080fd5b82516001600160401b03808211156200022057600080fd5b6200022e868387016200013d565b935060208501519150808211156200024557600080fd5b5062000254858286016200013d565b9150509250929050565b600181811c908216806200027357607f821691505b602082108114156200029557634e487b7160e01b600052602260045260246000fd5b50919050565b610c1080620002ab6000396000f3fe608060405234801561001057600080fd5b50600436106100ca5760003560e01c806340c10f191161007c57806340c10f191461018257806370a082311461019757806395d89b41146101c05780639dc29fac146101c8578063a457c2d7146101db578063a9059cbb146101ee578063dd62ed3e1461020157600080fd5b806306fdde03146100cf57806307546172146100ed578063095ea7b31461011857806318160ddd1461013b57806323b872dd1461014d578063313ce56714610160578063395093511461016f575b600080fd5b6100d761023a565b6040516100e491906109e7565b60405180910390f35b600554610100906001600160a01b031681565b6040516001600160a01b0390911681526020016100e4565b61012b610126366004610a58565b6102cc565b60405190151581526020016100e4565b6002545b6040519081526020016100e4565b61012b61015b366004610a82565b6102e2565b604051601281526020016100e4565b61012b61017d366004610a58565b610398565b610195610190366004610a58565b6103cf565b005b61013f6101a5366004610abe565b6001600160a01b031660009081526020819052604090205490565b6100d7610407565b6101956101d6366004610a58565b610416565b61012b6101e9366004610a58565b61044a565b61012b6101fc366004610a58565b6104e5565b61013f61020f366004610ae0565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60606003805461024990610b13565b80601f016020809104026020016040519081016040528092919081815260200182805461027590610b13565b80156102c25780601f10610297576101008083540402835291602001916102c2565b820191906000526020600020905b8154815290600101906020018083116102a557829003601f168201915b5050505050905090565b60006102d93384846104f2565b50600192915050565b60006102ef848484610617565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156103795760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b61038d85336103888685610b64565b6104f2565b506001949350505050565b3360008181526001602090815260408083206001600160a01b038716845290915281205490916102d9918590610388908690610b7b565b6005546001600160a01b031633146103f95760405162461bcd60e51b815260040161037090610b93565b61040382826107dd565b5050565b60606004805461024990610b13565b6005546001600160a01b031633146104405760405162461bcd60e51b815260040161037090610b93565b61040382826108aa565b3360009081526001602090815260408083206001600160a01b0386168452909152812054828110156104cc5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610370565b6104db33856103888685610b64565b5060019392505050565b60006102d9338484610617565b6001600160a01b0383166105545760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610370565b6001600160a01b0382166105b55760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610370565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b03831661067b5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610370565b6001600160a01b0382166106dd5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610370565b6001600160a01b038316600090815260208190526040902054818110156107555760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610370565b61075f8282610b64565b6001600160a01b038086166000908152602081905260408082209390935590851681529081208054849290610795908490610b7b565b92505081905550826001600160a01b0316846001600160a01b0316600080516020610bbb833981519152846040516107cf91815260200190565b60405180910390a350505050565b6001600160a01b0382166108335760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610370565b80600260008282546108459190610b7b565b90915550506001600160a01b03821660009081526020819052604081208054839290610872908490610b7b565b90915550506040518181526001600160a01b03831690600090600080516020610bbb8339815191529060200160405180910390a35050565b6001600160a01b03821661090a5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610370565b6001600160a01b0382166000908152602081905260409020548181101561097e5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610370565b6109888282610b64565b6001600160a01b038416600090815260208190526040812091909155600280548492906109b6908490610b64565b90915550506040518281526000906001600160a01b03851690600080516020610bbb8339815191529060200161060a565b600060208083528351808285015260005b81811015610a14578581018301518582016040015282016109f8565b81811115610a26576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b0381168114610a5357600080fd5b919050565b60008060408385031215610a6b57600080fd5b610a7483610a3c565b946020939093013593505050565b600080600060608486031215610a9757600080fd5b610aa084610a3c565b9250610aae60208501610a3c565b9150604084013590509250925092565b600060208284031215610ad057600080fd5b610ad982610a3c565b9392505050565b60008060408385031215610af357600080fd5b610afc83610a3c565b9150610b0a60208401610a3c565b90509250929050565b600181811c90821680610b2757607f821691505b60208210811415610b4857634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600082821015610b7657610b76610b4e565b500390565b60008219821115610b8e57610b8e610b4e565b500190565b6020808252600d908201526c0a8a4829c86908a744282aaa89609b1b60408201526060019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212200e6d35ef1ebff3fb23df47e3bb81d32388bde501b266ddc8ce3aaf280dd89ce464736f6c634300080a00338be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0a2646970667358221220cff232f965e34c6431035c3f70f7e0b6011ba39f963ef823f16365a840daf58964736f6c634300080a0033

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

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.