Address Details
contract

0x20468E8ACe661d7CD948077972f6DD15EB302280

Contract Name
AAutoRepay
Creator
0xb44a99–08bd5b at 0x9bc423–f29c29
Balance
0 CELO ( )
Locked CELO Balance
0.00 CELO
Voting CELO Balance
0.00 CELO
Pending Unlocked Gold
0.00 CELO
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
9684176
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
AAutoRepay




Optimization enabled
true
Compiler version
v0.6.12+commit.27d51765




Optimization runs
200
EVM Version
istanbul




Verified at
2022-09-30T05:59:13.453512Z

contracts/AutoRepay.sol

// Sources flattened with hardhat v2.6.4 https://hardhat.org

// File @openzeppelin/contracts/utils/EnumerableSet.sol@v3.1.0

// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

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

    struct Set {
        // Storage of set values
        bytes32[] _values;

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

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

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

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

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

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

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

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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


    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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


// File contracts/protocol/libraries/helpers/Errors.sol

pragma solidity 0.6.12;

/**
 * @title Errors library
 * @author Aave
 * @notice Defines the error messages emitted by the different contracts of the Aave protocol
 * @dev Error messages prefix glossary:
 *  - VL = ValidationLogic
 *  - MATH = Math libraries
 *  - CT = Common errors between tokens (AToken, VariableDebtToken and StableDebtToken)
 *  - AT = AToken
 *  - SDT = StableDebtToken
 *  - VDT = VariableDebtToken
 *  - LP = LendingPool
 *  - LPAPR = LendingPoolAddressesProviderRegistry
 *  - LPC = LendingPoolConfiguration
 *  - RL = ReserveLogic
 *  - LPCM = LendingPoolCollateralManager
 *  - P = Pausable
 */
library Errors {
  //common errors
  string public constant CALLER_NOT_POOL_ADMIN = '33'; // 'The caller must be the pool admin'
  string public constant BORROW_ALLOWANCE_NOT_ENOUGH = '59'; // User borrows on behalf, but allowance are too small

  //contract specific errors
  string public constant VL_INVALID_AMOUNT = '1'; // 'Amount must be greater than 0'
  string public constant VL_NO_ACTIVE_RESERVE = '2'; // 'Action requires an active reserve'
  string public constant VL_RESERVE_FROZEN = '3'; // 'Action cannot be performed because the reserve is frozen'
  string public constant VL_CURRENT_AVAILABLE_LIQUIDITY_NOT_ENOUGH = '4'; // 'The current liquidity is not enough'
  string public constant VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE = '5'; // 'User cannot withdraw more than the available balance'
  string public constant VL_TRANSFER_NOT_ALLOWED = '6'; // 'Transfer cannot be allowed.'
  string public constant VL_BORROWING_NOT_ENABLED = '7'; // 'Borrowing is not enabled'
  string public constant VL_INVALID_INTEREST_RATE_MODE_SELECTED = '8'; // 'Invalid interest rate mode selected'
  string public constant VL_COLLATERAL_BALANCE_IS_0 = '9'; // 'The collateral balance is 0'
  string public constant VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '10'; // 'Health factor is lesser than the liquidation threshold'
  string public constant VL_COLLATERAL_CANNOT_COVER_NEW_BORROW = '11'; // 'There is not enough collateral to cover a new borrow'
  string public constant VL_STABLE_BORROWING_NOT_ENABLED = '12'; // stable borrowing not enabled
  string public constant VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY = '13'; // collateral is (mostly) the same currency that is being borrowed
  string public constant VL_AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE = '14'; // 'The requested amount is greater than the max loan size in stable rate mode
  string public constant VL_NO_DEBT_OF_SELECTED_TYPE = '15'; // 'for repayment of stable debt, the user needs to have stable debt, otherwise, he needs to have variable debt'
  string public constant VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '16'; // 'To repay on behalf of an user an explicit amount to repay is needed'
  string public constant VL_NO_STABLE_RATE_LOAN_IN_RESERVE = '17'; // 'User does not have a stable rate loan in progress on this reserve'
  string public constant VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE = '18'; // 'User does not have a variable rate loan in progress on this reserve'
  string public constant VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0 = '19'; // 'The underlying balance needs to be greater than 0'
  string public constant VL_DEPOSIT_ALREADY_IN_USE = '20'; // 'User deposit is already being used as collateral'
  string public constant LP_NOT_ENOUGH_STABLE_BORROW_BALANCE = '21'; // 'User does not have any stable rate loan for this reserve'
  string public constant LP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '22'; // 'Interest rate rebalance conditions were not met'
  string public constant LP_LIQUIDATION_CALL_FAILED = '23'; // 'Liquidation call failed'
  string public constant LP_NOT_ENOUGH_LIQUIDITY_TO_BORROW = '24'; // 'There is not enough liquidity available to borrow'
  string public constant LP_REQUESTED_AMOUNT_TOO_SMALL = '25'; // 'The requested amount is too small for a FlashLoan.'
  string public constant LP_INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent'
  string public constant LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The caller of the function is not the lending pool configurator'
  string public constant LP_INCONSISTENT_FLASHLOAN_PARAMS = '28';
  string public constant CT_CALLER_MUST_BE_LENDING_POOL = '29'; // 'The caller of this function must be a lending pool'
  string public constant CT_CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself'
  string public constant CT_TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero'
  string public constant RL_RESERVE_ALREADY_INITIALIZED = '32'; // 'Reserve has already been initialized'
  string public constant LPC_RESERVE_LIQUIDITY_NOT_0 = '34'; // 'The liquidity of the reserve needs to be 0'
  string public constant LPC_INVALID_ATOKEN_POOL_ADDRESS = '35'; // 'The liquidity of the reserve needs to be 0'
  string public constant LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS = '36'; // 'The liquidity of the reserve needs to be 0'
  string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS = '37'; // 'The liquidity of the reserve needs to be 0'
  string public constant LPC_INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '38'; // 'The liquidity of the reserve needs to be 0'
  string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '39'; // 'The liquidity of the reserve needs to be 0'
  string public constant LPC_INVALID_ADDRESSES_PROVIDER_ID = '40'; // 'The liquidity of the reserve needs to be 0'
  string public constant LPC_INVALID_CONFIGURATION = '75'; // 'Invalid risk parameters for the reserve'
  string public constant LPC_CALLER_NOT_EMERGENCY_ADMIN = '76'; // 'The caller must be the emergency admin'
  string public constant LPAPR_PROVIDER_NOT_REGISTERED = '41'; // 'Provider is not registered'
  string public constant LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '42'; // 'Health factor is not below the threshold'
  string public constant LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED = '43'; // 'The collateral chosen cannot be liquidated'
  string public constant LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '44'; // 'User did not borrow the specified currency'
  string public constant LPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE = '45'; // "There isn't enough liquidity available to liquidate"
  string public constant LPCM_NO_ERRORS = '46'; // 'No errors'
  string public constant LP_INVALID_FLASHLOAN_MODE = '47'; //Invalid flashloan mode selected
  string public constant MATH_MULTIPLICATION_OVERFLOW = '48';
  string public constant MATH_ADDITION_OVERFLOW = '49';
  string public constant MATH_DIVISION_BY_ZERO = '50';
  string public constant RL_LIQUIDITY_INDEX_OVERFLOW = '51'; //  Liquidity index overflows uint128
  string public constant RL_VARIABLE_BORROW_INDEX_OVERFLOW = '52'; //  Variable borrow index overflows uint128
  string public constant RL_LIQUIDITY_RATE_OVERFLOW = '53'; //  Liquidity rate overflows uint128
  string public constant RL_VARIABLE_BORROW_RATE_OVERFLOW = '54'; //  Variable borrow rate overflows uint128
  string public constant RL_STABLE_BORROW_RATE_OVERFLOW = '55'; //  Stable borrow rate overflows uint128
  string public constant CT_INVALID_MINT_AMOUNT = '56'; //invalid amount to mint
  string public constant LP_FAILED_REPAY_WITH_COLLATERAL = '57';
  string public constant CT_INVALID_BURN_AMOUNT = '58'; //invalid amount to burn
  string public constant LP_FAILED_COLLATERAL_SWAP = '60';
  string public constant LP_INVALID_EQUAL_ASSETS_TO_SWAP = '61';
  string public constant LP_REENTRANCY_NOT_ALLOWED = '62';
  string public constant LP_CALLER_MUST_BE_AN_ATOKEN = '63';
  string public constant LP_IS_PAUSED = '64'; // 'Pool is paused'
  string public constant LP_NO_MORE_RESERVES_ALLOWED = '65';
  string public constant LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN = '66';
  string public constant RC_INVALID_LTV = '67';
  string public constant RC_INVALID_LIQ_THRESHOLD = '68';
  string public constant RC_INVALID_LIQ_BONUS = '69';
  string public constant RC_INVALID_DECIMALS = '70';
  string public constant RC_INVALID_RESERVE_FACTOR = '71';
  string public constant LPAPR_INVALID_ADDRESSES_PROVIDER_ID = '72';
  string public constant VL_INCONSISTENT_FLASHLOAN_PARAMS = '73';
  string public constant LP_INCONSISTENT_PARAMS_LENGTH = '74';
  string public constant UL_INVALID_INDEX = '77';
  string public constant LP_NOT_CONTRACT = '78';
  string public constant SDT_STABLE_DEBT_OVERFLOW = '79';
  string public constant SDT_BURN_EXCEEDS_BALANCE = '80';

  enum CollateralManagerErrors {
    NO_ERROR,
    NO_COLLATERAL_AVAILABLE,
    COLLATERAL_CANNOT_BE_LIQUIDATED,
    CURRRENCY_NOT_BORROWED,
    HEALTH_FACTOR_ABOVE_THRESHOLD,
    NOT_ENOUGH_LIQUIDITY,
    NO_ACTIVE_RESERVE,
    HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD,
    INVALID_EQUAL_ASSETS_TO_SWAP,
    FROZEN_RESERVE
  }
}


// File contracts/protocol/libraries/math/PercentageMath.sol

pragma solidity 0.6.12;

/**
 * @title PercentageMath library
 * @author Aave
 * @notice Provides functions to perform percentage calculations
 * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR
 * @dev Operations are rounded half up
 **/

library PercentageMath {
  uint256 constant PERCENTAGE_FACTOR = 1e4; //percentage plus two decimals
  uint256 constant HALF_PERCENT = PERCENTAGE_FACTOR / 2;

  /**
   * @dev Executes a percentage multiplication
   * @param value The value of which the percentage needs to be calculated
   * @param percentage The percentage of the value to be calculated
   * @return The percentage of value
   **/
  function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256) {
    if (value == 0 || percentage == 0) {
      return 0;
    }

    require(
      value <= (type(uint256).max - HALF_PERCENT) / percentage,
      Errors.MATH_MULTIPLICATION_OVERFLOW
    );

    return (value * percentage + HALF_PERCENT) / PERCENTAGE_FACTOR;
  }

  /**
   * @dev Executes a percentage division
   * @param value The value of which the percentage needs to be calculated
   * @param percentage The percentage of the value to be calculated
   * @return The value divided the percentage
   **/
  function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256) {
    require(percentage != 0, Errors.MATH_DIVISION_BY_ZERO);
    uint256 halfPercentage = percentage / 2;

    require(
      value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR,
      Errors.MATH_MULTIPLICATION_OVERFLOW
    );

    return (value * PERCENTAGE_FACTOR + halfPercentage) / percentage;
  }
}


// File contracts/dependencies/openzeppelin/contracts/SafeMath.sol

pragma solidity 0.6.12;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
  /**
   * @dev Returns the addition of two unsigned integers, reverting on
   * overflow.
   *
   * Counterpart to Solidity's `+` operator.
   *
   * Requirements:
   * - Addition cannot overflow.
   */
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a, 'SafeMath: addition overflow');

    return c;
  }

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

  /**
   * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
   * overflow (when the result is negative).
   *
   * Counterpart to Solidity's `-` operator.
   *
   * Requirements:
   * - Subtraction cannot overflow.
   */
  function sub(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    require(b <= a, errorMessage);
    uint256 c = a - b;

    return c;
  }

  /**
   * @dev Returns the multiplication of two unsigned integers, reverting on
   * overflow.
   *
   * Counterpart to Solidity's `*` operator.
   *
   * Requirements:
   * - Multiplication cannot overflow.
   */
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
    if (a == 0) {
      return 0;
    }

    uint256 c = a * b;
    require(c / a == b, 'SafeMath: multiplication overflow');

    return c;
  }

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

  /**
   * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
   * division by zero. The result is rounded towards zero.
   *
   * Counterpart to Solidity's `/` operator. Note: this function uses a
   * `revert` opcode (which leaves remaining gas untouched) while Solidity
   * uses an invalid opcode to revert (consuming all remaining gas).
   *
   * Requirements:
   * - The divisor cannot be zero.
   */
  function div(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    // Solidity only automatically asserts when dividing by 0
    require(b > 0, errorMessage);
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold

    return c;
  }

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

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


// File contracts/dependencies/openzeppelin/contracts/IERC20.sol

pragma solidity 0.6.12;

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

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

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

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

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

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

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

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


// File contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol

pragma solidity 0.6.12;

interface IERC20Detailed is IERC20 {
  function name() external view returns (string memory);

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

  function decimals() external view returns (uint8);
}


// File contracts/dependencies/openzeppelin/contracts/Address.sol

pragma solidity 0.6.12;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
  /**
   * @dev Returns true if `account` is a contract.
   *
   * [IMPORTANT]
   * ====
   * It is unsafe to assume that an address for which this function returns
   * false is an externally-owned account (EOA) and not a contract.
   *
   * Among others, `isContract` will return false for the following
   * types of addresses:
   *
   *  - an externally-owned account
   *  - a contract in construction
   *  - an address where a contract will be created
   *  - an address where a contract lived, but was destroyed
   * ====
   */
  function isContract(address account) internal view returns (bool) {
    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
    // for accounts without code, i.e. `keccak256('')`
    bytes32 codehash;
    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
    // solhint-disable-next-line no-inline-assembly
    assembly {
      codehash := extcodehash(account)
    }
    return (codehash != accountHash && codehash != 0x0);
  }

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


// File contracts/dependencies/openzeppelin/contracts/SafeERC20.sol


pragma solidity 0.6.12;



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

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

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

  function safeApprove(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    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 callOptionalReturn(IERC20 token, bytes memory data) private {
    require(address(token).isContract(), 'SafeERC20: call to non-contract');

    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = address(token).call(data);
    require(success, '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');
    }
  }
}


// File contracts/dependencies/openzeppelin/contracts/Context.sol

pragma solidity 0.6.12;

/*
 * @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 GSN 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 payable) {
    return msg.sender;
  }

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


// File contracts/dependencies/openzeppelin/contracts/Ownable.sol


pragma solidity ^0.6.0;

/**
 * @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.
 */
contract Ownable is Context {
  address private _owner;

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

  /**
   * @dev Initializes the contract setting the deployer as the initial owner.
   */
  constructor() internal {
    address msgSender = _msgSender();
    _owner = msgSender;
    emit OwnershipTransferred(address(0), msgSender);
  }

  /**
   * @dev Returns the address of the current owner.
   */
  function owner() public view 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;
  }
}


// File contracts/interfaces/ILendingPoolAddressesProvider.sol

pragma solidity 0.6.12;

/**
 * @title LendingPoolAddressesProvider contract
 * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
 * - Acting also as factory of proxies and admin of those, so with right to change its implementations
 * - Owned by the Aave Governance
 * @author Aave
 **/
interface ILendingPoolAddressesProvider {
  event MarketIdSet(string newMarketId);
  event LendingPoolUpdated(address indexed newAddress);
  event ConfigurationAdminUpdated(address indexed newAddress);
  event EmergencyAdminUpdated(address indexed newAddress);
  event LendingPoolConfiguratorUpdated(address indexed newAddress);
  event LendingPoolCollateralManagerUpdated(address indexed newAddress);
  event PriceOracleUpdated(address indexed newAddress);
  event LendingRateOracleUpdated(address indexed newAddress);
  event ProxyCreated(bytes32 id, address indexed newAddress);
  event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy);

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

  function setMarketId(string calldata marketId) external;

  function setAddress(bytes32 id, address newAddress) external;

  function setAddressAsProxy(bytes32 id, address impl) external;

  function getAddress(bytes32 id) external view returns (address);

  function getLendingPool() external view returns (address);

  function setLendingPoolImpl(address pool) external;

  function getLendingPoolConfigurator() external view returns (address);

  function setLendingPoolConfiguratorImpl(address configurator) external;

  function getLendingPoolCollateralManager() external view returns (address);

  function setLendingPoolCollateralManager(address manager) external;

  function getPoolAdmin() external view returns (address);

  function setPoolAdmin(address admin) external;

  function getEmergencyAdmin() external view returns (address);

  function setEmergencyAdmin(address admin) external;

  function getPriceOracle() external view returns (address);

  function setPriceOracle(address priceOracle) external;

  function getLendingRateOracle() external view returns (address);

  function setLendingRateOracle(address lendingRateOracle) external;
}


// File contracts/protocol/libraries/types/DataTypes.sol

pragma solidity 0.6.12;

library DataTypes {
  // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
  struct ReserveData {
    //stores the reserve configuration
    ReserveConfigurationMap configuration;
    //the liquidity index. Expressed in ray
    uint128 liquidityIndex;
    //variable borrow index. Expressed in ray
    uint128 variableBorrowIndex;
    //the current supply rate. Expressed in ray
    uint128 currentLiquidityRate;
    //the current variable borrow rate. Expressed in ray
    uint128 currentVariableBorrowRate;
    //the current stable borrow rate. Expressed in ray
    uint128 currentStableBorrowRate;
    uint40 lastUpdateTimestamp;
    //tokens addresses
    address aTokenAddress;
    address stableDebtTokenAddress;
    address variableDebtTokenAddress;
    //address of the interest rate strategy
    address interestRateStrategyAddress;
    //the id of the reserve. Represents the position in the list of the active reserves
    uint8 id;
  }

  struct ReserveConfigurationMap {
    //bit 0-15: LTV
    //bit 16-31: Liq. threshold
    //bit 32-47: Liq. bonus
    //bit 48-55: Decimals
    //bit 56: Reserve is active
    //bit 57: reserve is frozen
    //bit 58: borrowing is enabled
    //bit 59: stable rate borrowing enabled
    //bit 60-63: reserved
    //bit 64-79: reserve factor
    uint256 data;
  }

  struct UserConfigurationMap {
    uint256 data;
  }

  enum InterestRateMode {NONE, STABLE, VARIABLE}
}


// File contracts/interfaces/IUniswapV2Router02.sol

pragma solidity 0.6.12;

interface IUniswapV2Router02 {
  function swapExactTokensForTokens(
    uint256 amountIn,
    uint256 amountOutMin,
    address[] calldata path,
    address to,
    uint256 deadline
  ) external returns (uint256[] memory amounts);

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

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

  function getAmountsOut(uint256 amountIn, address[] calldata path)
    external
    view
    returns (uint256[] memory amounts);

  function getAmountsIn(uint256 amountOut, address[] calldata path)
    external
    view
    returns (uint256[] memory amounts);
}


// File contracts/interfaces/IPriceOracleGetter.sol

pragma solidity 0.6.12;

/**
 * @title IPriceOracleGetter interface
 * @notice Interface for the Aave price oracle.
 **/

interface IPriceOracleGetter {
  /**
   * @dev returns the asset price in ETH
   * @param asset the address of the asset
   * @return the ETH price of the asset
   **/
  function getAssetPrice(address asset) external view returns (uint256);
}


// File contracts/interfaces/IERC20WithPermit.sol

pragma solidity 0.6.12;

interface IERC20WithPermit is IERC20 {
  function permit(
    address owner,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;
}


// File contracts/interfaces/ILendingPool.sol

pragma solidity 0.6.12;


interface ILendingPool {
  /**
   * @dev Emitted on deposit()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The address initiating the deposit
   * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens
   * @param amount The amount deposited
   * @param referral The referral code used
   **/
  event Deposit(
    address indexed reserve,
    address user,
    address indexed onBehalfOf,
    uint256 amount,
    uint16 indexed referral
  );

  /**
   * @dev Emitted on withdraw()
   * @param reserve The address of the underlyng asset being withdrawn
   * @param user The address initiating the withdrawal, owner of aTokens
   * @param to Address that will receive the underlying
   * @param amount The amount to be withdrawn
   **/
  event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);

  /**
   * @dev Emitted on borrow() and flashLoan() when debt needs to be opened
   * @param reserve The address of the underlying asset being borrowed
   * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
   * initiator of the transaction on flashLoan()
   * @param onBehalfOf The address that will be getting the debt
   * @param amount The amount borrowed out
   * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable
   * @param borrowRate The numeric rate at which the user has borrowed
   * @param referral The referral code used
   **/
  event Borrow(
    address indexed reserve,
    address user,
    address indexed onBehalfOf,
    uint256 amount,
    uint256 borrowRateMode,
    uint256 borrowRate,
    uint16 indexed referral
  );

  /**
   * @dev Emitted on repay()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The beneficiary of the repayment, getting his debt reduced
   * @param repayer The address of the user initiating the repay(), providing the funds
   * @param amount The amount repaid
   **/
  event Repay(
    address indexed reserve,
    address indexed user,
    address indexed repayer,
    uint256 amount
  );

  /**
   * @dev Emitted on swapBorrowRateMode()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The address of the user swapping his rate mode
   * @param rateMode The rate mode that the user wants to swap to
   **/
  event Swap(address indexed reserve, address indexed user, uint256 rateMode);

  /**
   * @dev Emitted on setUserUseReserveAsCollateral()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The address of the user enabling the usage as collateral
   **/
  event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);

  /**
   * @dev Emitted on setUserUseReserveAsCollateral()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The address of the user enabling the usage as collateral
   **/
  event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);

  /**
   * @dev Emitted on rebalanceStableBorrowRate()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The address of the user for which the rebalance has been executed
   **/
  event RebalanceStableBorrowRate(address indexed reserve, address indexed user);

  /**
   * @dev Emitted on flashLoan()
   * @param target The address of the flash loan receiver contract
   * @param initiator The address initiating the flash loan
   * @param asset The address of the asset being flash borrowed
   * @param amount The amount flash borrowed
   * @param premium The fee flash borrowed
   * @param referralCode The referral code used
   **/
  event FlashLoan(
    address indexed target,
    address indexed initiator,
    address indexed asset,
    uint256 amount,
    uint256 premium,
    uint16 referralCode
  );

  /**
   * @dev Emitted when the pause is triggered.
   */
  event Paused();

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

  /**
   * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via
   * LendingPoolCollateral manager using a DELEGATECALL
   * This allows to have the events in the generated ABI for LendingPool.
   * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
   * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
   * @param user The address of the borrower getting liquidated
   * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
   * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator
   * @param liquidator The address of the liquidator
   * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
   * to receive the underlying collateral asset directly
   **/
  event LiquidationCall(
    address indexed collateralAsset,
    address indexed debtAsset,
    address indexed user,
    uint256 debtToCover,
    uint256 liquidatedCollateralAmount,
    address liquidator,
    bool receiveAToken
  );

  /**
   * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared
   * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal,
   * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it
   * gets added to the LendingPool ABI
   * @param reserve The address of the underlying asset of the reserve
   * @param liquidityRate The new liquidity rate
   * @param stableBorrowRate The new stable borrow rate
   * @param variableBorrowRate The new variable borrow rate
   * @param liquidityIndex The new liquidity index
   * @param variableBorrowIndex The new variable borrow index
   **/
  event ReserveDataUpdated(
    address indexed reserve,
    uint256 liquidityRate,
    uint256 stableBorrowRate,
    uint256 variableBorrowRate,
    uint256 liquidityIndex,
    uint256 variableBorrowIndex
  );

  /**
   * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
   * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
   * @param asset The address of the underlying asset to deposit
   * @param amount The amount to be deposited
   * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
   *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
   *   is a different wallet
   * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
   *   0 if the action is executed directly by the user, without any middle-man
   **/
  function deposit(
    address asset,
    uint256 amount,
    address onBehalfOf,
    uint16 referralCode
  ) external;

  /**
   * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
   * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
   * @param asset The address of the underlying asset to withdraw
   * @param amount The underlying amount to be withdrawn
   *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
   * @param to Address that will receive the underlying, same as msg.sender if the user
   *   wants to receive it on his own wallet, or a different address if the beneficiary is a
   *   different wallet
   * @return The final amount withdrawn
   **/
  function withdraw(
    address asset,
    uint256 amount,
    address to
  ) external returns (uint256);

  /**
   * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
   * already deposited enough collateral, or he was given enough allowance by a credit delegator on the
   * corresponding debt token (StableDebtToken or VariableDebtToken)
   * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
   *   and 100 stable/variable debt tokens, depending on the `interestRateMode`
   * @param asset The address of the underlying asset to borrow
   * @param amount The amount to be borrowed
   * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
   * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
   *   0 if the action is executed directly by the user, without any middle-man
   * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself
   * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
   * if he has been given credit delegation allowance
   **/
  function borrow(
    address asset,
    uint256 amount,
    uint256 interestRateMode,
    uint16 referralCode,
    address onBehalfOf
  ) external;

  /**
   * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
   * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
   * @param asset The address of the borrowed underlying asset previously borrowed
   * @param amount The amount to repay
   * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
   * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
   * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
   * user calling the function if he wants to reduce/remove his own debt, or the address of any other
   * other borrower whose debt should be removed
   * @return The final amount repaid
   **/
  function repay(
    address asset,
    uint256 amount,
    uint256 rateMode,
    address onBehalfOf
  ) external returns (uint256);

  /**
   * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
   * @param asset The address of the underlying asset borrowed
   * @param rateMode The rate mode that the user wants to swap to
   **/
  function swapBorrowRateMode(address asset, uint256 rateMode) external;

  /**
   * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
   * - Users can be rebalanced if the following conditions are satisfied:
   *     1. Usage ratio is above 95%
   *     2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
   *        borrowed at a stable rate and depositors are not earning enough
   * @param asset The address of the underlying asset borrowed
   * @param user The address of the user to be rebalanced
   **/
  function rebalanceStableBorrowRate(address asset, address user) external;

  /**
   * @dev Allows depositors to enable/disable a specific deposited asset as collateral
   * @param asset The address of the underlying asset deposited
   * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise
   **/
  function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;

  /**
   * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
   * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
   *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
   * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
   * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
   * @param user The address of the borrower getting liquidated
   * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
   * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
   * to receive the underlying collateral asset directly
   **/
  function liquidationCall(
    address collateralAsset,
    address debtAsset,
    address user,
    uint256 debtToCover,
    bool receiveAToken
  ) external;

  /**
   * @dev Allows smartcontracts to access the liquidity of the pool within one transaction,
   * as long as the amount taken plus a fee is returned.
   * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration.
   * For further details please visit https://developers.aave.com
   * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface
   * @param assets The addresses of the assets being flash-borrowed
   * @param amounts The amounts amounts being flash-borrowed
   * @param modes Types of the debt to open if the flash loan is not returned:
   *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
   *   1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
   *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
   * @param onBehalfOf The address  that will receive the debt in the case of using on `modes` 1 or 2
   * @param params Variadic packed params to pass to the receiver as extra information
   * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
   *   0 if the action is executed directly by the user, without any middle-man
   **/
  function flashLoan(
    address receiverAddress,
    address[] calldata assets,
    uint256[] calldata amounts,
    uint256[] calldata modes,
    address onBehalfOf,
    bytes calldata params,
    uint16 referralCode
  ) external;

  /**
   * @dev Returns the user account data across all the reserves
   * @param user The address of the user
   * @return totalCollateralETH the total collateral in ETH of the user
   * @return totalDebtETH the total debt in ETH of the user
   * @return availableBorrowsETH the borrowing power left of the user
   * @return currentLiquidationThreshold the liquidation threshold of the user
   * @return ltv the loan to value of the user
   * @return healthFactor the current health factor of the user
   **/
  function getUserAccountData(address user)
    external
    view
    returns (
      uint256 totalCollateralETH,
      uint256 totalDebtETH,
      uint256 availableBorrowsETH,
      uint256 currentLiquidationThreshold,
      uint256 ltv,
      uint256 healthFactor
    );

  function initReserve(
    address reserve,
    address aTokenAddress,
    address stableDebtAddress,
    address variableDebtAddress,
    address interestRateStrategyAddress
  ) external;

  function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress)
    external;

  function setConfiguration(address reserve, uint256 configuration) external;

  /**
   * @dev Returns the configuration of the reserve
   * @param asset The address of the underlying asset of the reserve
   * @return The configuration of the reserve
   **/
  function getConfiguration(address asset)
    external
    view
    returns (DataTypes.ReserveConfigurationMap memory);

  /**
   * @dev Returns the configuration of the user across all the reserves
   * @param user The user address
   * @return The configuration of the user
   **/
  function getUserConfiguration(address user)
    external
    view
    returns (DataTypes.UserConfigurationMap memory);

  /**
   * @dev Returns the normalized income normalized income of the reserve
   * @param asset The address of the underlying asset of the reserve
   * @return The reserve's normalized income
   */
  function getReserveNormalizedIncome(address asset) external view returns (uint256);

  /**
   * @dev Returns the normalized variable debt per unit of asset
   * @param asset The address of the underlying asset of the reserve
   * @return The reserve normalized variable debt
   */
  function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);

  /**
   * @dev Returns the state and configuration of the reserve
   * @param asset The address of the underlying asset of the reserve
   * @return The state of the reserve
   **/
  function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);

  function finalizeTransfer(
    address asset,
    address from,
    address to,
    uint256 amount,
    uint256 balanceFromAfter,
    uint256 balanceToBefore
  ) external;

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

  function getAddressesProvider() external view returns (ILendingPoolAddressesProvider);

  function setPause(bool val) external;

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


// File contracts/flashloan/interfaces/IFlashLoanReceiver.sol

pragma solidity 0.6.12;


/**
 * @title IFlashLoanReceiver interface
 * @notice Interface for the Aave fee IFlashLoanReceiver.
 * @author Aave
 * @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract
 **/
interface IFlashLoanReceiver {
  function executeOperation(
    address[] calldata assets,
    uint256[] calldata amounts,
    uint256[] calldata premiums,
    address initiator,
    bytes calldata params
  ) external returns (bool);

  function ADDRESSES_PROVIDER() external view returns (ILendingPoolAddressesProvider);

  function LENDING_POOL() external view returns (ILendingPool);
}


// File contracts/flashloan/base/FlashLoanReceiverBase.sol

pragma solidity 0.6.12;






abstract contract FlashLoanReceiverBase is IFlashLoanReceiver {
  using SafeERC20 for IERC20;
  using SafeMath for uint256;

  ILendingPoolAddressesProvider public immutable override ADDRESSES_PROVIDER;
  ILendingPool public immutable override LENDING_POOL;

  constructor(ILendingPoolAddressesProvider provider) public {
    ADDRESSES_PROVIDER = provider;
    LENDING_POOL = ILendingPool(provider.getLendingPool());
  }
}


// File contracts/adapters/interfaces/IBaseUniswapAdapter.sol

pragma solidity 0.6.12;


interface IBaseUniswapAdapter {
  event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount);

  struct PermitSignature {
    uint256 amount;
    uint256 deadline;
    uint8 v;
    bytes32 r;
    bytes32 s;
  }

  struct AmountCalc {
    uint256 calculatedAmount;
    uint256 relativePrice;
    uint256 amountInUsd;
    uint256 amountOutUsd;
    address[] path;
  }

  function WETH_ADDRESS() external returns (address);

  function MAX_SLIPPAGE_PERCENT() external returns (uint256);

  function FLASHLOAN_PREMIUM_TOTAL() external returns (uint256);

  function USD_ADDRESS() external returns (address);

  function ORACLE() external returns (IPriceOracleGetter);

  function UNISWAP_ROUTER() external returns (IUniswapV2Router02);

  /**
   * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices
   * @param amountIn Amount of reserveIn
   * @param reserveIn Address of the asset to be swap from
   * @param reserveOut Address of the asset to be swap to
   * @return uint256 Amount out of the reserveOut
   * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals)
   * @return uint256 In amount of reserveIn value denominated in USD (8 decimals)
   * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals)
   * @return address[] The exchange path
   */
  function getAmountsOut(
    uint256 amountIn,
    address reserveIn,
    address reserveOut
  )
    external
    view
    returns (
      uint256,
      uint256,
      uint256,
      uint256,
      address[] memory
    );

  /**
   * @dev Returns the minimum input asset amount required to buy the given output asset amount and the prices
   * @param amountOut Amount of reserveOut
   * @param reserveIn Address of the asset to be swap from
   * @param reserveOut Address of the asset to be swap to
   * @return uint256 Amount in of the reserveIn
   * @return uint256 The price of in amount denominated in the reserveOut currency (18 decimals)
   * @return uint256 In amount of reserveIn value denominated in USD (8 decimals)
   * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals)
   * @return address[] The exchange path
   */
  function getAmountsIn(
    uint256 amountOut,
    address reserveIn,
    address reserveOut
  )
    external
    view
    returns (
      uint256,
      uint256,
      uint256,
      uint256,
      address[] memory
    );
}


// File contracts/adapters/BaseUniswapAdapter.sol

pragma solidity 0.6.12;













/**
 * @title BaseUniswapAdapter
 * @notice Implements the logic for performing assets swaps in Uniswap V2
 * @author Aave
 **/
abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapter, Ownable {
  using SafeMath for uint256;
  using PercentageMath for uint256;
  using SafeERC20 for IERC20;

  // Max slippage percent allowed
  uint256 public constant override MAX_SLIPPAGE_PERCENT = 3000; // 30%
  // FLash Loan fee set in lending pool
  uint256 public constant override FLASHLOAN_PREMIUM_TOTAL = 9;
  // USD oracle asset address
  address public constant override USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96;

  address public immutable override WETH_ADDRESS;
  IPriceOracleGetter public immutable override ORACLE;
  IUniswapV2Router02 public immutable override UNISWAP_ROUTER;

  constructor(
    ILendingPoolAddressesProvider addressesProvider,
    IUniswapV2Router02 uniswapRouter,
    address wethAddress
  ) public FlashLoanReceiverBase(addressesProvider) {
    ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle());
    UNISWAP_ROUTER = uniswapRouter;
    WETH_ADDRESS = wethAddress;
  }

  /**
   * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices
   * @param amountIn Amount of reserveIn
   * @param reserveIn Address of the asset to be swap from
   * @param reserveOut Address of the asset to be swap to
   * @return uint256 Amount out of the reserveOut
   * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals)
   * @return uint256 In amount of reserveIn value denominated in USD (8 decimals)
   * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals)
   */
  function getAmountsOut(
    uint256 amountIn,
    address reserveIn,
    address reserveOut
  )
    external
    view
    override
    returns (
      uint256,
      uint256,
      uint256,
      uint256,
      address[] memory
    )
  {
    AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn);

    return (
      results.calculatedAmount,
      results.relativePrice,
      results.amountInUsd,
      results.amountOutUsd,
      results.path
    );
  }

  /**
   * @dev Returns the minimum input asset amount required to buy the given output asset amount and the prices
   * @param amountOut Amount of reserveOut
   * @param reserveIn Address of the asset to be swap from
   * @param reserveOut Address of the asset to be swap to
   * @return uint256 Amount in of the reserveIn
   * @return uint256 The price of in amount denominated in the reserveOut currency (18 decimals)
   * @return uint256 In amount of reserveIn value denominated in USD (8 decimals)
   * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals)
   */
  function getAmountsIn(
    uint256 amountOut,
    address reserveIn,
    address reserveOut
  )
    external
    view
    override
    returns (
      uint256,
      uint256,
      uint256,
      uint256,
      address[] memory
    )
  {
    AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut);

    return (
      results.calculatedAmount,
      results.relativePrice,
      results.amountInUsd,
      results.amountOutUsd,
      results.path
    );
  }

  /**
   * @dev Swaps an exact `amountToSwap` of an asset to another
   * @param assetToSwapFromPrice Origin asset to get price
   * @param assetToSwapToPrice Destination asset to get pricce
   * @param assetToSwapFrom Origin asset
   * @param assetToSwapTo Destination asset
   * @param amountToSwap Exact amount of `assetToSwapFrom` to be swapped
   * @param minAmountOut the min amount of `assetToSwapTo` to be received from the swap
   * @param aTokenExist is a token exist in path
   * @return the amount received from the swap
   */
  function _swapExactTokensForTokens(
    address assetToSwapFromPrice,
    address assetToSwapToPrice,
    address assetToSwapFrom,
    address assetToSwapTo,
    uint256 amountToSwap,
    uint256 minAmountOut,
    bool useEthPath,
    bool aTokenExist
  ) internal returns (uint256) {
    uint256 fromAssetDecimals = _getDecimals(assetToSwapFromPrice);
    uint256 toAssetDecimals = _getDecimals(assetToSwapToPrice);

    uint256 fromAssetPrice = _getPrice(assetToSwapFromPrice);
    uint256 toAssetPrice = _getPrice(assetToSwapToPrice);

    uint256 expectedMinAmountOut = amountToSwap
      .mul(fromAssetPrice.mul(10**toAssetDecimals))
      .div(toAssetPrice.mul(10**fromAssetDecimals))
      .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT));

    require(expectedMinAmountOut < minAmountOut, 'minAmountOut exceed max slippage');

    // Approves the transfer for the swap. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix.
    IERC20(assetToSwapFrom).safeApprove(address(UNISWAP_ROUTER), 0);
    IERC20(assetToSwapFrom).safeApprove(address(UNISWAP_ROUTER), amountToSwap);

    address[] memory path;
    if (useEthPath) {
      path = new address[](3);
      path[0] = assetToSwapFrom;
      path[1] = WETH_ADDRESS;
      path[2] = assetToSwapTo;
    } else {
      path = new address[](2);
      path[0] = assetToSwapFrom;
      path[1] = assetToSwapTo;
    }

    if (aTokenExist) {
      uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(address(this));

      UNISWAP_ROUTER.swapExactTokensForTokensSupportingFeeOnTransferTokens(
        amountToSwap,
        minAmountOut,
        path,
        address(this),
        block.timestamp
      );

      uint256 swappedAmount = IERC20(path[path.length - 1]).balanceOf(address(this)) -
        balanceBefore;

      emit Swapped(assetToSwapFrom, assetToSwapTo, amountToSwap, swappedAmount);

      return swappedAmount;
    } else {
      uint256[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens(
        amountToSwap,
        minAmountOut,
        path,
        address(this),
        block.timestamp
      );

      emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[amounts.length - 1]);

      return amounts[amounts.length - 1];
    }
  }

  /**
   * @dev Receive an exact amount `amountToReceive` of `assetToSwapTo` tokens for as few `assetToSwapFrom` tokens as
   * possible.
   * @param assetToSwapFromPrice Origin asset to get price
   * @param assetToSwapToPrice Destination asset to get pricce
   * @param assetToSwapFrom Origin asset
   * @param assetToSwapTo Destination asset
   * @param maxAmountToSwap Max amount of `assetToSwapFrom` allowed to be swapped
   * @param amountToReceive Exact amount of `assetToSwapTo` to receive
   * @return the amount swapped
   */
  function _swapTokensForExactTokens(
    address assetToSwapFromPrice,
    address assetToSwapToPrice,
    address assetToSwapFrom,
    address assetToSwapTo,
    uint256 maxAmountToSwap,
    uint256 amountToReceive,
    bool useEthPath
  ) internal returns (uint256) {
    uint256 fromAssetDecimals = _getDecimals(assetToSwapFromPrice);
    uint256 toAssetDecimals = _getDecimals(assetToSwapToPrice);

    uint256 fromAssetPrice = _getPrice(assetToSwapFromPrice);
    uint256 toAssetPrice = _getPrice(assetToSwapToPrice);

    uint256 expectedMaxAmountToSwap = amountToReceive
      .mul(toAssetPrice.mul(10**fromAssetDecimals))
      .div(fromAssetPrice.mul(10**toAssetDecimals))
      .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT));

    require(maxAmountToSwap < expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage');

    // Approves the transfer for the swap. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix.
    IERC20(assetToSwapFrom).safeApprove(address(UNISWAP_ROUTER), 0);
    IERC20(assetToSwapFrom).safeApprove(address(UNISWAP_ROUTER), maxAmountToSwap);

    address[] memory path;
    if (useEthPath) {
      path = new address[](3);
      path[0] = assetToSwapFrom;
      path[1] = WETH_ADDRESS;
      path[2] = assetToSwapTo;
    } else {
      path = new address[](2);
      path[0] = assetToSwapFrom;
      path[1] = assetToSwapTo;
    }

    uint256[] memory amounts = UNISWAP_ROUTER.swapTokensForExactTokens(
      amountToReceive,
      maxAmountToSwap,
      path,
      address(this),
      block.timestamp
    );

    emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[amounts.length - 1]);

    return amounts[0];
  }

  /**
   * @dev Get the price of the asset from the oracle denominated in eth
   * @param asset address
   * @return eth price for the asset
   */
  function _getPrice(address asset) internal view returns (uint256) {
    return ORACLE.getAssetPrice(asset);
  }

  /**
   * @dev Get the decimals of an asset
   * @return number of decimals of the asset
   */
  function _getDecimals(address asset) internal view returns (uint256) {
    return IERC20Detailed(asset).decimals();
  }

  /**
   * @dev Get the aToken associated to the asset
   * @return address of the aToken
   */
  function _getReserveData(address asset) internal view returns (DataTypes.ReserveData memory) {
    return LENDING_POOL.getReserveData(asset);
  }

  /**
   * @dev Pull the ATokens from the user
   * @param reserve address of the asset
   * @param reserveAToken address of the aToken of the reserve
   * @param user address
   * @param amount of tokens to be transferred to the contract
   * @param permitSignature struct containing the permit signature
   */
  function _pullAToken(
    address reserve,
    address reserveAToken,
    address user,
    uint256 amount,
    PermitSignature memory permitSignature
  ) internal {
    _transferATokenToContractAddress(reserveAToken, user, amount, permitSignature);

    // withdraw reserve
    LENDING_POOL.withdraw(reserve, amount, address(this));
  }

  /**
   * @dev Transfer user ATokens to contract address
   * @param reserveAToken address of the aToken of the reserve
   * @param user address
   * @param amount of tokens to be transferred to the contract
   * @param permitSignature struct containing the permit signature
   */
  function _transferATokenToContractAddress(
    address reserveAToken,
    address user,
    uint256 amount,
    PermitSignature memory permitSignature
  ) internal {
    if (_usePermit(permitSignature)) {
      IERC20WithPermit(reserveAToken).permit(
        user,
        address(this),
        permitSignature.amount,
        permitSignature.deadline,
        permitSignature.v,
        permitSignature.r,
        permitSignature.s
      );
    }

    // transfer from user to adapter
    IERC20(reserveAToken).safeTransferFrom(user, address(this), amount);
  }

  /**
   * @dev Tells if the permit method should be called by inspecting if there is a valid signature.
   * If signature params are set to 0, then permit won't be called.
   * @param signature struct containing the permit signature
   * @return whether or not permit should be called
   */
  function _usePermit(PermitSignature memory signature) internal pure returns (bool) {
    return
      !(uint256(signature.deadline) == uint256(signature.v) && uint256(signature.deadline) == 0);
  }

  /**
   * @dev Calculates the value denominated in USD
   * @param reserve Address of the reserve
   * @param amount Amount of the reserve
   * @param decimals Decimals of the reserve
   * @return whether or not permit should be called
   */
  function _calcUsdValue(
    address reserve,
    uint256 amount,
    uint256 decimals
  ) internal view returns (uint256) {
    uint256 ethUsdPrice = _getPrice(USD_ADDRESS);
    uint256 reservePrice = _getPrice(reserve);

    return amount.mul(reservePrice).div(10**decimals).mul(ethUsdPrice).div(10**18);
  }

  /**
   * @dev Given an input asset amount, returns the maximum output amount of the other asset
   * @param reserveIn Address of the asset to be swap from
   * @param reserveOut Address of the asset to be swap to
   * @param amountIn Amount of reserveIn
   * @return Struct containing the following information:
   *   uint256 Amount out of the reserveOut
   *   uint256 The price of out amount denominated in the reserveIn currency (18 decimals)
   *   uint256 In amount of reserveIn value denominated in USD (8 decimals)
   *   uint256 Out amount of reserveOut value denominated in USD (8 decimals)
   */
  function _getAmountsOutData(
    address reserveIn,
    address reserveOut,
    uint256 amountIn
  ) internal view returns (AmountCalc memory) {
    // Subtract flash loan fee
    uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000));

    if (reserveIn == reserveOut) {
      uint256 reserveDecimals = _getDecimals(reserveIn);
      address[] memory path = new address[](1);
      path[0] = reserveIn;

      return
        AmountCalc(
          finalAmountIn,
          finalAmountIn.mul(10**18).div(amountIn),
          _calcUsdValue(reserveIn, amountIn, reserveDecimals),
          _calcUsdValue(reserveIn, finalAmountIn, reserveDecimals),
          path
        );
    }

    address[] memory simplePath = new address[](2);
    simplePath[0] = reserveIn;
    simplePath[1] = reserveOut;

    uint256[] memory amountsWithoutWeth;
    uint256[] memory amountsWithWeth;

    address[] memory pathWithWeth = new address[](3);
    if (reserveIn != WETH_ADDRESS && reserveOut != WETH_ADDRESS) {
      pathWithWeth[0] = reserveIn;
      pathWithWeth[1] = WETH_ADDRESS;
      pathWithWeth[2] = reserveOut;

      try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth) returns (
        uint256[] memory resultsWithWeth
      ) {
        amountsWithWeth = resultsWithWeth;
      } catch {
        amountsWithWeth = new uint256[](3);
      }
    } else {
      amountsWithWeth = new uint256[](3);
    }

    uint256 bestAmountOut;
    try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, simplePath) returns (
      uint256[] memory resultAmounts
    ) {
      amountsWithoutWeth = resultAmounts;

      bestAmountOut = (amountsWithWeth[2] > amountsWithoutWeth[1])
        ? amountsWithWeth[2]
        : amountsWithoutWeth[1];
    } catch {
      amountsWithoutWeth = new uint256[](2);
      bestAmountOut = amountsWithWeth[2];
    }

    uint256 reserveInDecimals = _getDecimals(reserveIn);
    uint256 reserveOutDecimals = _getDecimals(reserveOut);

    uint256 outPerInPrice = finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div(
      bestAmountOut.mul(10**reserveInDecimals)
    );

    return
      AmountCalc(
        bestAmountOut,
        outPerInPrice,
        _calcUsdValue(reserveIn, amountIn, reserveInDecimals),
        _calcUsdValue(reserveOut, bestAmountOut, reserveOutDecimals),
        (bestAmountOut == 0) ? new address[](2) : (bestAmountOut == amountsWithoutWeth[1])
          ? simplePath
          : pathWithWeth
      );
  }

  /**
   * @dev Returns the minimum input asset amount required to buy the given output asset amount
   * @param reserveIn Address of the asset to be swap from
   * @param reserveOut Address of the asset to be swap to
   * @param amountOut Amount of reserveOut
   * @return Struct containing the following information:
   *   uint256 Amount in of the reserveIn
   *   uint256 The price of in amount denominated in the reserveOut currency (18 decimals)
   *   uint256 In amount of reserveIn value denominated in USD (8 decimals)
   *   uint256 Out amount of reserveOut value denominated in USD (8 decimals)
   */
  function _getAmountsInData(
    address reserveIn,
    address reserveOut,
    uint256 amountOut
  ) internal view returns (AmountCalc memory) {
    if (reserveIn == reserveOut) {
      // Add flash loan fee
      uint256 amountIn = amountOut.add(amountOut.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000));
      uint256 reserveDecimals = _getDecimals(reserveIn);
      address[] memory path = new address[](1);
      path[0] = reserveIn;

      return
        AmountCalc(
          amountIn,
          amountOut.mul(10**18).div(amountIn),
          _calcUsdValue(reserveIn, amountIn, reserveDecimals),
          _calcUsdValue(reserveIn, amountOut, reserveDecimals),
          path
        );
    }

    (uint256[] memory amounts, address[] memory path) = _getAmountsInAndPath(
      reserveIn,
      reserveOut,
      amountOut
    );

    // Add flash loan fee
    uint256 finalAmountIn = amounts[0].add(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000));

    uint256 reserveInDecimals = _getDecimals(reserveIn);
    uint256 reserveOutDecimals = _getDecimals(reserveOut);

    uint256 inPerOutPrice = amountOut.mul(10**18).mul(10**reserveInDecimals).div(
      finalAmountIn.mul(10**reserveOutDecimals)
    );

    return
      AmountCalc(
        finalAmountIn,
        inPerOutPrice,
        _calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals),
        _calcUsdValue(reserveOut, amountOut, reserveOutDecimals),
        path
      );
  }

  /**
   * @dev Calculates the input asset amount required to buy the given output asset amount
   * @param reserveIn Address of the asset to be swap from
   * @param reserveOut Address of the asset to be swap to
   * @param amountOut Amount of reserveOut
   * @return uint256[] amounts Array containing the amountIn and amountOut for a swap
   */
  function _getAmountsInAndPath(
    address reserveIn,
    address reserveOut,
    uint256 amountOut
  ) internal view returns (uint256[] memory, address[] memory) {
    address[] memory simplePath = new address[](2);
    simplePath[0] = reserveIn;
    simplePath[1] = reserveOut;

    uint256[] memory amountsWithoutWeth;
    uint256[] memory amountsWithWeth;
    address[] memory pathWithWeth = new address[](3);

    if (reserveIn != WETH_ADDRESS && reserveOut != WETH_ADDRESS) {
      pathWithWeth[0] = reserveIn;
      pathWithWeth[1] = WETH_ADDRESS;
      pathWithWeth[2] = reserveOut;

      try UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth) returns (
        uint256[] memory resultsWithWeth
      ) {
        amountsWithWeth = resultsWithWeth;
      } catch {
        amountsWithWeth = new uint256[](3);
      }
    } else {
      amountsWithWeth = new uint256[](3);
    }

    try UNISWAP_ROUTER.getAmountsIn(amountOut, simplePath) returns (
      uint256[] memory resultAmounts
    ) {
      amountsWithoutWeth = resultAmounts;

      return
        (amountsWithWeth[0] < amountsWithoutWeth[0] && amountsWithWeth[0] != 0)
          ? (amountsWithWeth, pathWithWeth)
          : (amountsWithoutWeth, simplePath);
    } catch {
      return (amountsWithWeth, pathWithWeth);
    }
  }

  /**
   * @dev Calculates the input asset amount required to buy the given output asset amount
   * @param reserveIn Address of the asset to be swap from
   * @param reserveOut Address of the asset to be swap to
   * @param amountOut Amount of reserveOut
   * @return uint256[] amounts Array containing the amountIn and amountOut for a swap
   */
  function _getAmountsIn(
    address reserveIn,
    address reserveOut,
    uint256 amountOut,
    bool useEthPath
  ) internal view returns (uint256[] memory) {
    address[] memory path;

    if (useEthPath) {
      path = new address[](3);
      path[0] = reserveIn;
      path[1] = WETH_ADDRESS;
      path[2] = reserveOut;
    } else {
      path = new address[](2);
      path[0] = reserveIn;
      path[1] = reserveOut;
    }

    return UNISWAP_ROUTER.getAmountsIn(amountOut, path);
  }

  /**
   * @dev Emergency rescue for token stucked on this contract, as failsafe mechanism
   * - Funds should never remain in this contract more time than during transactions
   * - Only callable by the owner
   **/
  function rescueTokens(IERC20 token) external onlyOwner {
    token.transfer(owner(), token.balanceOf(address(this)));
  }
}


// File contracts/adapters/AutoRepay.sol

pragma solidity 0.6.12;






contract AAutoRepay is BaseUniswapAdapter {
  using SafeERC20 for IERC20;
  using EnumerableSet for EnumerableSet.AddressSet;
  struct RepayParams {
    address user;
    address collateralAsset;
    address caller;
    uint256 collateralAmount;
    uint256 rateMode;
    PermitSignature permitSignature;
    bool useEthPath;
    bool useATokenAsFrom;
    bool useATokenAsTo;
  }

  struct UserInfo {
    uint256 minHealthFactor;
    uint256 maxHealthFactor;
  }

  EnumerableSet.AddressSet private _whitelistedAddresses;

  mapping(address => UserInfo) public userInfos;

  uint256 public constant FEE = 10;
  uint256 public constant FEE_DECIMALS = 10000;

  constructor(
    ILendingPoolAddressesProvider addressesProvider,
    IUniswapV2Router02 uniswapRouter,
    address wethAddress
  ) public BaseUniswapAdapter(addressesProvider, uniswapRouter, wethAddress) {}

  function whitelistAddress(address userAddress) public onlyOwner returns (bool) {
    return _whitelistedAddresses.add(userAddress);
  }

  function removeFromWhitelist(address userAddress) public onlyOwner returns (bool) {
    return _whitelistedAddresses.remove(userAddress);
  }

  function userIsWhitelisted(address userAddress) public view returns (bool) {
    return _whitelistedAddresses.contains(userAddress);
  }

  function getWitelistedAddresses() public view returns (address[] memory) {
    uint256 length = _whitelistedAddresses.length();
    address[] memory addresses = new address[](length);
    for (uint256 i = 0; i < length; i++) {
      addresses[i] = _whitelistedAddresses.at(i);
    }
    return addresses;
  }

  function setMinMaxHealthFactor(uint256 minHealthFactor, uint256 maxHealthFactor) public {
    require(
      maxHealthFactor >= minHealthFactor,
      'maxHealthFactor should be more or equal than minHealthFactor'
    );
    userInfos[msg.sender] = UserInfo({
      minHealthFactor: minHealthFactor,
      maxHealthFactor: maxHealthFactor
    });
  }

  function _checkMinHealthFactor(address user) internal view {
    (, , , , , uint256 healthFactor) = LENDING_POOL.getUserAccountData(user);
    require(
      healthFactor < userInfos[user].minHealthFactor,
      'User health factor must be less than minHealthFactor for user'
    );
  }

  function _checkHealthFactorInRange(address user) internal view {
    (, , , , , uint256 healthFactor) = LENDING_POOL.getUserAccountData(user);
    require(
      healthFactor >= userInfos[user].minHealthFactor &&
        healthFactor <= userInfos[user].maxHealthFactor,
      'User health factor must be in range {from minHealthFactor to maxHealthFactor}'
    );
  }

  /**
   * @dev Uses the received funds from the flash loan to repay a debt on the protocol on behalf of the user. Then pulls
   * the collateral from the user and swaps it to the debt asset to repay the flash loan.
   * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset, swap it
   * and repay the flash loan.
   * Supports only one asset on the flash loan.
   * @param assets Address of debt asset
   * @param amounts Amount of the debt to be repaid
   * @param premiums Fee of the flash loan
   * @param params Additional variadic field to include extra params. Expected parameters:
   *   address collateralAsset Address of the reserve to be swapped
   *   uint256 collateralAmount Amount of reserve to be swapped
   *   uint256 rateMode Rate modes of the debt to be repaid
   *   uint256 permitAmount Amount for the permit signature
   *   uint256 deadline Deadline for the permit signature
   *   uint8 v V param for the permit signature
   *   bytes32 r R param for the permit signature
   *   bytes32 s S param for the permit signature
   */
  function executeOperation(
    address[] calldata assets,
    uint256[] calldata amounts,
    uint256[] calldata premiums,
    address,
    bytes calldata params
  ) external override returns (bool) {
    require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL');

    RepayParams memory decodedParams = _decodeParams(params);

    // Repay debt. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix.
    IERC20(assets[0]).safeApprove(address(LENDING_POOL), 0);
    IERC20(assets[0]).safeApprove(address(LENDING_POOL), amounts[0]);
    uint256 repaidAmount = IERC20(assets[0]).balanceOf(address(this));
    LENDING_POOL.repay(assets[0], amounts[0], decodedParams.rateMode, decodedParams.user);
    repaidAmount = repaidAmount.sub(IERC20(assets[0]).balanceOf(address(this)));

    uint256 maxCollateralToSwap = decodedParams.collateralAmount;
    if (repaidAmount < amounts[0]) {
      maxCollateralToSwap = maxCollateralToSwap.mul(repaidAmount).div(amounts[0]);
    }

    _doSwapAndPullWithFee(
      [decodedParams.user, decodedParams.collateralAsset, assets[0], decodedParams.caller],
      [repaidAmount, maxCollateralToSwap, premiums[0]],
      decodedParams.permitSignature,
      [decodedParams.useEthPath, decodedParams.useATokenAsFrom, decodedParams.useATokenAsTo]
    );

    // Repay flashloan. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix.
    IERC20(assets[0]).safeApprove(address(LENDING_POOL), 0);
    IERC20(assets[0]).safeApprove(address(LENDING_POOL), amounts[0].add(premiums[0]));

    return true;
  }

  function increaseHealthFactor(
    address[3] memory addressParams, // user, collateralAsset, debtAsset,
    uint256[3] memory uintParams, // collateralAmount, debtRepayAmount, debtRateMode,
    PermitSignature calldata permitSignature,
    bool[4] memory boolParams // useEthPath, useATokenAsFrom, useATokenAsTo, useFlashloan
  ) public {
    require(userIsWhitelisted(msg.sender), "Caller is not whitelisted");
    _checkMinHealthFactor(addressParams[0]);
    if (boolParams[3]) {
      bytes memory params = abi.encode(
        [addressParams[0], addressParams[1], msg.sender],
        uintParams[0],
        uintParams[2],
        permitSignature.amount,
        permitSignature.deadline,
        permitSignature.v,
        permitSignature.r,
        permitSignature.s,
        [boolParams[0], boolParams[1], boolParams[2]]
      );
      address[] memory assets = new address[](1);
      assets[0] = addressParams[2];
      uint256[] memory amounts = new uint256[](1);
      amounts[0] = uintParams[1];
      uint256[] memory modes = new uint256[](1);
      modes[0] = 0;
      LENDING_POOL.flashLoan(address(this), assets, amounts, modes, addressParams[0], params, 0);
    } else {
      DataTypes.ReserveData memory debtReserveData = _getReserveData(addressParams[2]);
      uint256 amountToRepay;
      {
        address debtToken = DataTypes.InterestRateMode(uintParams[2]) ==
          DataTypes.InterestRateMode.STABLE
          ? debtReserveData.stableDebtTokenAddress
          : debtReserveData.variableDebtTokenAddress;
        uint256 currentDebt = IERC20(debtToken).balanceOf(addressParams[0]);
        amountToRepay = uintParams[1] <= currentDebt ? uintParams[1] : currentDebt;
      }
      uint256 maxCollateralToSwap = uintParams[0];
      if (amountToRepay < uintParams[1]) {
        maxCollateralToSwap = maxCollateralToSwap.mul(amountToRepay).div(uintParams[1]);
      }
      _doSwapAndPullWithFee(
        [addressParams[0], addressParams[1], addressParams[2], msg.sender],
        [amountToRepay, maxCollateralToSwap, 0],
        permitSignature,
        [boolParams[0], boolParams[1], boolParams[2]]
      );

      // Repay debt. Approves 0 first to comply with tokens that implement the anti frontrunning approval fix
      IERC20(addressParams[2]).safeApprove(address(LENDING_POOL), 0);
      IERC20(addressParams[2]).safeApprove(address(LENDING_POOL), amountToRepay);
      LENDING_POOL.repay(addressParams[2], amountToRepay, uintParams[2], addressParams[0]);
    }
    _checkHealthFactorInRange(addressParams[0]);
  }

  function _doSwapAndPullWithFee(
    address[4] memory addressParams, // user, collateralAsset, debtAsset, caller
    uint256[3] memory uintParams, // amountToRepay, maxCollateralToSwap, premium
    PermitSignature memory permitSignature,
    bool[3] memory boolParams // useEthPath, useATokenAsFrom, useATokenASTo
  ) internal {
    address collateralATokenAddress = _getReserveData(addressParams[1]).aTokenAddress;
    address debtATokenAddress = _getReserveData(addressParams[2]).aTokenAddress;
    if (addressParams[1] != addressParams[2]) {
      uint256 amounts0 = _getAmountsIn(
        boolParams[1] ? collateralATokenAddress : addressParams[1],
        boolParams[2] ? debtATokenAddress : addressParams[2],
        uintParams[0].add(uintParams[2]),
        boolParams[0]
      )[0];
      require(amounts0 <= uintParams[1], 'slippage too high');
      uint256 feeAmount = amounts0.mul(FEE).div(FEE_DECIMALS);

      if (boolParams[1]) {
        // Transfer aTokens from user to contract address
        _transferATokenToContractAddress(
          collateralATokenAddress,
          addressParams[0],
          amounts0.add(feeAmount),
          permitSignature
        );
        LENDING_POOL.withdraw(addressParams[1], feeAmount, addressParams[3]);
      } else {
        // Pull aTokens from user
        _pullAToken(
          addressParams[1],
          collateralATokenAddress,
          addressParams[0],
          amounts0.add(feeAmount),
          permitSignature
        );
        IERC20(addressParams[1]).safeTransfer(addressParams[3], feeAmount);
      }

      // Swap collateral asset to the debt asset
      _swapTokensForExactTokens(
        addressParams[1],
        addressParams[2],
        boolParams[1] ? collateralATokenAddress : addressParams[1],
        boolParams[2] ? debtATokenAddress : addressParams[2],
        amounts0,
        uintParams[0].add(uintParams[2]),
        boolParams[0]
      );

      if (boolParams[2]) {
        // withdraw debt AToken
        LENDING_POOL.withdraw(
          addressParams[2],
          IERC20(debtATokenAddress).balanceOf(address(this)),
          address(this)
        );
      }
    } else {
      uint256 feeAmount = uintParams[0].mul(FEE).div(FEE_DECIMALS);
      // Pull aTokens from user
      _pullAToken(
        addressParams[1],
        collateralATokenAddress,
        addressParams[0],
        uintParams[0].add(uintParams[2]).add(feeAmount),
        permitSignature
      );
      IERC20(addressParams[1]).safeTransfer(addressParams[3], feeAmount);
    }
  }

  function _decodeParams(bytes memory params) internal pure returns (RepayParams memory) {
    (
      address[3] memory addressParams, // user, collateralAsset, caller,
      uint256 collateralAmount,
      uint256 rateMode,
      uint256 permitAmount,
      uint256 deadline,
      uint8 v,
      bytes32 r,
      bytes32 s,
      bool[3] memory boolParams // useEthPath, useATokenAsFrom, useATokenAsTo
    ) = abi.decode(
        params,
        (address[3], uint256, uint256, uint256, uint256, uint8, bytes32, bytes32, bool[3])
      );

    return
      RepayParams(
        addressParams[0],
        addressParams[1],
        addressParams[2],
        collateralAmount,
        rateMode,
        PermitSignature(permitAmount, deadline, v, r, s),
        boolParams[0],
        boolParams[1],
        boolParams[2]
      );
  }
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"addressesProvider","internalType":"contract ILendingPoolAddressesProvider"},{"type":"address","name":"uniswapRouter","internalType":"contract IUniswapV2Router02"},{"type":"address","name":"wethAddress","internalType":"address"}]},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Swapped","inputs":[{"type":"address","name":"fromAsset","internalType":"address","indexed":false},{"type":"address","name":"toAsset","internalType":"address","indexed":false},{"type":"uint256","name":"fromAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"receivedAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ILendingPoolAddressesProvider"}],"name":"ADDRESSES_PROVIDER","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"FEE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"FEE_DECIMALS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"FLASHLOAN_PREMIUM_TOTAL","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ILendingPool"}],"name":"LENDING_POOL","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_SLIPPAGE_PERCENT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IPriceOracleGetter"}],"name":"ORACLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IUniswapV2Router02"}],"name":"UNISWAP_ROUTER","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"USD_ADDRESS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"WETH_ADDRESS","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"executeOperation","inputs":[{"type":"address[]","name":"assets","internalType":"address[]"},{"type":"uint256[]","name":"amounts","internalType":"uint256[]"},{"type":"uint256[]","name":"premiums","internalType":"uint256[]"},{"type":"address","name":"","internalType":"address"},{"type":"bytes","name":"params","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"address[]","name":"","internalType":"address[]"}],"name":"getAmountsIn","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"address","name":"reserveIn","internalType":"address"},{"type":"address","name":"reserveOut","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"address[]","name":"","internalType":"address[]"}],"name":"getAmountsOut","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address","name":"reserveIn","internalType":"address"},{"type":"address","name":"reserveOut","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getWitelistedAddresses","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"increaseHealthFactor","inputs":[{"type":"address[3]","name":"addressParams","internalType":"address[3]"},{"type":"uint256[3]","name":"uintParams","internalType":"uint256[3]"},{"type":"tuple","name":"permitSignature","internalType":"struct IBaseUniswapAdapter.PermitSignature","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"bool[4]","name":"boolParams","internalType":"bool[4]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"removeFromWhitelist","inputs":[{"type":"address","name":"userAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rescueTokens","inputs":[{"type":"address","name":"token","internalType":"contract IERC20"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMinMaxHealthFactor","inputs":[{"type":"uint256","name":"minHealthFactor","internalType":"uint256"},{"type":"uint256","name":"maxHealthFactor","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"minHealthFactor","internalType":"uint256"},{"type":"uint256","name":"maxHealthFactor","internalType":"uint256"}],"name":"userInfos","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"userIsWhitelisted","inputs":[{"type":"address","name":"userAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"whitelistAddress","inputs":[{"type":"address","name":"userAddress","internalType":"address"}]}]
              

Contract Creation Code

0x6101206040523480156200001257600080fd5b5060405162004998380380620049988339810160408190526200003591620001fd565b82828282806001600160a01b03166080816001600160a01b031660601b81525050806001600160a01b0316630261bf8b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200009057600080fd5b505afa158015620000a5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000cb9190620001d7565b60601b6001600160601b03191660a052506000620000e8620001d3565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350826001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b1580156200016c57600080fd5b505afa15801562000181573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001a79190620001d7565b6001600160601b0319606091821b811660e05292811b8316610100521b1660c052506200026992505050565b3390565b600060208284031215620001e9578081fd5b8151620001f68162000250565b9392505050565b60008060006060848603121562000212578182fd5b83516200021f8162000250565b6020850151909350620002328162000250565b6040850151909250620002458162000250565b809150509250925092565b6001600160a01b03811681146200026657600080fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c61463b6200035d600039806111c55280611faa528061209e528061262b52806128d6528061290b5280612a9f52806130da52806131cb5250806109dc528061332f5250806104395280611e845280611ec15280611f2b528061251852806129895280612fb45280612ff1528061305b52508061065952806108ab52806108ea528061092c5280610be85280610c735280610ccb5280610dae528061107052806110a35280611135528061130852806113ed528061162252806117625280611a06528061278552508061045d525061463b6000f3fe608060405234801561001057600080fd5b50600436106101575760003560e01c80638c39745d116100c3578063c57981b51161007c578063c57981b5146102a2578063ccf288c6146102aa578063cdf58cd6146102b2578063d8264920146102c5578063ee76103e146102cd578063f2fde38b146102e057610157565b80638c39745d1461023e5780638da5cb5b14610253578063920f5c841461025b5780639d1211bf1461026e578063b4dcfc7714610276578063baf7fa991461027e57610157565b806338013f021161011557806338013f02146101c757806341566585146101cf57806343b0215f146101ef578063637bcd2a14610210578063715018a6146102235780638ab1d6811461022b57610157565b8062ae3bf81461015c578063040141e5146101715780630542975c1461018f578063074b2e4314610197578063114778a4146101ac57806332e4b286146101bf575b600080fd5b61016f61016a3660046137a5565b6102f3565b005b610179610437565b6040516101869190613e1b565b60405180910390f35b61017961045b565b61019f61047f565b60405161018691906144bb565b61016f6101ba3660046137c1565b610484565b61019f6109d4565b6101796109da565b6101e26101dd3660046137a5565b6109fe565b6040516101869190614053565b6102026101fd3660046137a5565b610a46565b6040516101869291906144dd565b6101e261021e3660046137a5565b610a5f565b61016f610a6c565b6101e26102393660046137a5565b610aeb565b610246610b2d565b6040516101869190614040565b610179610bcc565b6101e2610269366004613938565b610bdb565b61017961111b565b610179611133565b61029161028c366004613c52565b611157565b604051610186959493929190614527565b61019f61119d565b61019f6111a2565b6102916102c0366004613c52565b6111a8565b6101796111c3565b61016f6102db366004613c93565b6111e7565b61016f6102ee3660046137a5565b611234565b6102fb6112ea565b6000546001600160a01b039081169116146103315760405162461bcd60e51b8152600401610328906142ca565b60405180910390fd5b806001600160a01b031663a9059cbb610348610bcc565b6040516370a0823160e01b81526001600160a01b038516906370a0823190610374903090600401613e1b565b60206040518083038186803b15801561038c57600080fd5b505afa1580156103a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103c49190613c3a565b6040518363ffffffff1660e01b81526004016103e1929190613f55565b602060405180830381600087803b1580156103fb57600080fd5b505af115801561040f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104339190613aca565b5050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b600981565b61048d33610a5f565b6104a95760405162461bcd60e51b81526004016103289061420f565b83516104b4906112ee565b6060810151156106dd57604080516060808201835286516001600160a01b0390811683526020808901519091168184015233838501528651878501519294909291873591908801359061050c90898801908a01613cfd565b8860600135896080013560405180606001604052808b60006004811061052e57fe5b60209081029190910151151582528c8101511515828201526040808e0151151592810192909252905161056a9a99989796959493929101613fbc565b60408051601f198184030181526001808452838301909252925060609190602080830190803683370190505090508560026020020151816000815181106105ad57fe5b6001600160a01b0392909216602092830291909101909101526040805160018082528183019092526060918160200160208202803683370190505090508560016020020151816000815181106105ff57fe5b602090810291909101015260408051600180825281830190925260609181602001602082028036833701905050905060008160008151811061063d57fe5b6020908102919091010152875160405163ab9c4b5d60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169163ab9c4b5d916106a2913091889188918891908c90600090600401613ed7565b600060405180830381600087803b1580156106bc57600080fd5b505af11580156106d0573d6000803e3d6000fd5b50505050505050506109c3565b6106e5613462565b6106f68560025b60200201516113ce565b905060008060018660026020020151600281111561071057fe5b600281111561071b57fe5b1461072b57826101200151610732565b8261010001515b87516040516370a0823160e01b81529192506000916001600160a01b038416916370a08231916107659190600401613e1b565b60206040518083038186803b15801561077d57600080fd5b505afa158015610791573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b59190613c3a565b905080876001602002015111156107cc57806107d2565b60208701515b8751602089015191945092508310159050610803576020860151610800906107fa8385611473565b906114b4565b90505b6108a660405180608001604052808960006003811061081e57fe5b602090810291909101516001600160a01b0390811683528b8201518116838301526040808d015190911681840152336060938401528051928301815286835290820185905260009082015261087836899003890189613ae6565b604080516060810182528951151581526020808b0151151590820152898201511515918101919091526114f6565b6108e57f000000000000000000000000000000000000000000000000000000000000000060008960025b60200201516001600160a01b031691906118ed565b6109127f0000000000000000000000000000000000000000000000000000000000000000838960026108d0565b604080880151878201518951925163573ade8160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169363573ade819361096c939092889290600401613f91565b602060405180830381600087803b15801561098657600080fd5b505af115801561099a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109be9190613c3a565b505050505b83516109ce906119ec565b50505050565b610bb881565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000610a086112ea565b6000546001600160a01b03908116911614610a355760405162461bcd60e51b8152600401610328906142ca565b610a40600183611af4565b92915050565b6003602052600090815260409020805460019091015482565b6000610a40600183611b09565b610a746112ea565b6000546001600160a01b03908116911614610aa15760405162461bcd60e51b8152600401610328906142ca565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000610af56112ea565b6000546001600160a01b03908116911614610b225760405162461bcd60e51b8152600401610328906142ca565b610a40600183611b1e565b60606000610b3b6001611b33565b905060608167ffffffffffffffff81118015610b5657600080fd5b50604051908082528060200260200182016040528015610b80578160200160208202803683370190505b50905060005b82811015610bc557610b99600182611b3e565b828281518110610ba557fe5b6001600160a01b0390921660209283029190910190910152600101610b86565b5091505090565b6000546001600160a01b031690565b6000336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610c255760405162461bcd60e51b815260040161032890614126565b610c2d6134cd565b610c6c84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b4a92505050565b9050610cc67f000000000000000000000000000000000000000000000000000000000000000060008d8d6000818110610ca157fe5b9050602002016020810190610cb691906137a5565b6001600160a01b031691906118ed565b610d0b7f00000000000000000000000000000000000000000000000000000000000000008a8a6000818110610cf757fe5b905060200201358d8d6000818110610ca157fe5b60008b8b6000818110610d1a57fe5b9050602002016020810190610d2f91906137a5565b6001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610d5a9190613e1b565b60206040518083038186803b158015610d7257600080fd5b505afa158015610d86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610daa9190613c3a565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663573ade818d8d6000818110610de857fe5b9050602002016020810190610dfd91906137a5565b8c8c6000818110610e0a57fe5b90506020020135856080015186600001516040518563ffffffff1660e01b8152600401610e3a9493929190613f91565b602060405180830381600087803b158015610e5457600080fd5b505af1158015610e68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8c9190613c3a565b50610f348c8c6000818110610e9d57fe5b9050602002016020810190610eb291906137a5565b6001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610edd9190613e1b565b60206040518083038186803b158015610ef557600080fd5b505afa158015610f09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2d9190613c3a565b8290611c91565b60608301519091508a8a600081610f4757fe5b90506020020135821015610f8257610f7f8b8b6000818110610f6557fe5b905060200201356107fa848461147390919063ffffffff16565b90505b6040805160808101825284516001600160a01b0390811682526020808701519091169082015261106b9181018f8f600081610fb957fe5b9050602002016020810190610fce91906137a5565b6001600160a01b03166001600160a01b0316815260200185604001516001600160a01b03166001600160a01b031681525060405180606001604052808581526020018481526020018c8c600081811061102357fe5b905060200201358152508560a0015160405180606001604052808860c001511515151581526020018860e00151151515158152602001886101000151151515158152506114f6565b61109e7f000000000000000000000000000000000000000000000000000000000000000060008f8f6000818110610ca157fe5b6111087f00000000000000000000000000000000000000000000000000000000000000006110fb8b8b60008181106110d257fe5b905060200201358e8e60008181106110e657fe5b90506020020135611cd390919063ffffffff16565b8f8f6000818110610ca157fe5b5060019c9b505050505050505050505050565b7310f7fc1f91ba351f9c629c5947ad69bd03c05b9681565b7f000000000000000000000000000000000000000000000000000000000000000081565b600080600080606061116761353a565b61117288888b611cf8565b8051602082015160408301516060840151608090940151929d919c509a509198509650945050505050565b600a81565b61271081565b60008060008060606111b861353a565b61117288888b6122ad565b7f000000000000000000000000000000000000000000000000000000000000000081565b818110156112075760405162461bcd60e51b81526004016103289061435c565b60408051808201825292835260208084019283523360009081526003909152209151825551600190910155565b61123c6112ea565b6000546001600160a01b039081169116146112695760405162461bcd60e51b8152600401610328906142ca565b6001600160a01b03811661128f5760405162461bcd60e51b81526004016103289061415d565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b604051632fe4a15f60e21b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063bf92857c9061133d908590600401613e1b565b60c06040518083038186803b15801561135557600080fd5b505afa158015611369573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061138d9190613cb4565b6001600160a01b0388166000908152600360205260409020549096508610945061043393505050505760405162461bcd60e51b8152600401610328906142ff565b6113d6613462565b6040516335ea6a7560e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906335ea6a7590611422908590600401613e1b565b6101806040518083038186803b15801561143b57600080fd5b505afa15801561144f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a409190613b3f565b60008261148257506000610a40565b8282028284828161148f57fe5b04146114ad5760405162461bcd60e51b815260040161032890614289565b9392505050565b60006114ad83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612487565b60006115038560016106ec565b60e00151905060006115168660026106ec565b60e00151604087015160208801519192506001600160a01b0391821691161461188557600061158a8460016020020151611554576020880151611556565b835b604086015161156957604089015161156b565b835b6040890151611583908a60005b602002015190611cd3565b87516124be565b60008151811061159657fe5b60200260200101519050856001600381106115ad57fe5b60200201518111156115d15760405162461bcd60e51b8152600401610328906143b9565b60006115e46127106107fa84600a611473565b6020860151909150156116b85787516116099085906116038585611cd3565b896126c0565b60208801516060890151604051631a4ca37b60e21b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316926369328dec9261166092869190600401613f6e565b602060405180830381600087803b15801561167a57600080fd5b505af115801561168e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b29190613c3a565b506116f9565b602088015188516116d6919086906116d08686611cd3565b8a612762565b60608801516116f990828a60015b60200201516001600160a01b03169190612810565b60208089015160408a01519187015161174d929061171b5760208b015161171d565b865b60408901516117305760408c0151611732565b865b866117468d600260200201518e6000611578565b8b5161282f565b5060408501511561187e576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166369328dec89600260200201516040516370a0823160e01b81526001600160a01b038716906370a08231906117bb903090600401613e1b565b60206040518083038186803b1580156117d357600080fd5b505afa1580156117e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180b9190613c3a565b306040518463ffffffff1660e01b815260040161182a93929190613f6e565b602060405180830381600087803b15801561184457600080fd5b505af1158015611858573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187c9190613c3a565b505b50506118e5565b845160009061189d90612710906107fa90600a611473565b6020880151885160408901519293506118d1928691906118cb9086906118c5908d6000611578565b90611cd3565b89612762565b60608701516118e390828960016116e4565b505b505050505050565b8015806119755750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906119239030908690600401613e2f565b60206040518083038186803b15801561193b57600080fd5b505afa15801561194f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119739190613c3a565b155b6119915760405162461bcd60e51b81526004016103289061442e565b6119e78363095ea7b360e01b84846040516024016119b0929190613f55565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612bc5565b505050565b604051632fe4a15f60e21b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063bf92857c90611a3b908590600401613e1b565b60c06040518083038186803b158015611a5357600080fd5b505afa158015611a67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8b9190613cb4565b6001600160a01b0388166000908152600360205260409020549096508610801595509350611ad89250505057506001600160a01b0382166000908152600360205260409020600101548111155b6104335760405162461bcd60e51b8152600401610328906140b3565b60006114ad836001600160a01b038416612caa565b60006114ad836001600160a01b038416612cf4565b60006114ad836001600160a01b038416612d0c565b6000610a4082612dd2565b60006114ad8383612dd6565b611b526134cd565b611b5a613569565b6000806000806000806000611b6d613569565b8a806020019051810190611b819190613868565b9850985098509850985098509850985098506040518061012001604052808a600060038110611bac57fe5b60200201516001600160a01b031681526020018a600160038110611bcc57fe5b60200201516001600160a01b031681526020018a600260038110611bec57fe5b60200201516001600160a01b031681526020018981526020018881526020016040518060a001604052808981526020018881526020018760ff16815260200186815260200185815250815260200182600060038110611c4757fe5b60200201511515815260200182600160038110611c6057fe5b60200201511515815260200182600260038110611c7957fe5b6020020151151590529b9a5050505050505050505050565b60006114ad83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612e1b565b6000828201838110156114ad5760405162461bcd60e51b8152600401610328906141a3565b611d0061353a565b6000611d1d611d166127106107fa866009611473565b8490611c91565b9050836001600160a01b0316856001600160a01b03161415611de9576000611d4486612e47565b60408051600180825281830190925291925060609190602080830190803683370190505090508681600081518110611d7857fe5b6001600160a01b039092166020928302919091018201526040805160a08101909152848152908101611db6876107fa87670de0b6b3a7640000611473565b8152602001611dc6898886612ec3565b8152602001611dd6898686612ec3565b81526020018281525093505050506114ad565b60408051600280825260608083018452926020830190803683370190505090508581600081518110611e1757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600181518110611e4557fe5b6001600160a01b0392909216602092830291909101820152604080516003808252608082019092526060928392839291820183803683370190505090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316896001600160a01b031614158015611ef657507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316886001600160a01b031614155b15612062578881600081518110611f0957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000000000000000000000000000000000000000000081600181518110611f5757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508781600281518110611f8557fe5b6001600160a01b03928316602091820292909201015260405163d06ca61f60e01b81527f00000000000000000000000000000000000000000000000000000000000000009091169063d06ca61f90611fe390889085906004016144c4565b60006040518083038186803b158015611ffb57600080fd5b505afa92505050801561203057506040513d6000823e601f3d908101601f1916820160405261202d9190810190613a35565b60015b61205a5760408051600380825260808201909252906020820160608036833701905050915061205d565b91505b612084565b6040805160038082526080820190925290602082016060803683370190505091505b60405163d06ca61f60e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d06ca61f906120d590899089906004016144c4565b60006040518083038186803b1580156120ed57600080fd5b505afa92505050801561212257506040513d6000823e601f3d908101601f1916820160405261211f9190810190613a35565b60015b6121625760408051600280825260608201835290916020830190803683370190505093508260028151811061215357fe5b602002602001015190506121c8565b8094508460018151811061217257fe5b60200260200101518460028151811061218757fe5b6020026020010151116121ae57846001815181106121a157fe5b60200260200101516121c4565b836002815181106121bb57fe5b60200260200101515b9150505b60006121d38b612e47565b905060006121e08b612e47565b905060006122156121f585600a86900a611473565b6107fa600a85900a61220f8d670de0b6b3a7640000611473565b90611473565b90506040518060a001604052808581526020018281526020016122398f8e87612ec3565b81526020016122498e8786612ec3565b8152602001851561227c578860018151811061226157fe5b602002602001015186146122755786612277565b895b61229a565b60408051600280825260608201835290916020830190803683375050505b90529d9c50505050505050505050505050565b6122b561353a565b826001600160a01b0316846001600160a01b0316141561238b5760006122ec6122e56127106107fa866009611473565b8490611cd3565b905060006122f986612e47565b6040805160018082528183019092529192506060919060208083019080368337019050509050868160008151811061232d57fe5b6001600160a01b039092166020928302919091018201526040805160a0810190915284815290810161236b856107fa89670de0b6b3a7640000611473565b815260200161237b898686612ec3565b8152602001611dd6898886612ec3565b606080612399868686612f12565b9150915060006123f36123d06127106107fa6009876000815181106123ba57fe5b602002602001015161147390919063ffffffff16565b846000815181106123dd57fe5b6020026020010151611cd390919063ffffffff16565b9050600061240088612e47565b9050600061240d88612e47565b9050600061243c61242285600a85900a611473565b6107fa600a86900a61220f8c670de0b6b3a7640000611473565b90506040518060a001604052808581526020018281526020016124608c8787612ec3565b81526020016124708b8b86612ec3565b815260200195909552509298975050505050505050565b600081836124a85760405162461bcd60e51b8152600401610328919061405e565b5060008385816124b457fe5b0495945050505050565b60608082156125975760408051600380825260808201909252906020820160608036833701905050905085816000815181106124f657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f00000000000000000000000000000000000000000000000000000000000000008160018151811061254457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050848160028151811061257257fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612614565b604080516002808252606082018352909160208301908036833701905050905085816000815181106125c557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505084816001815181106125f357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6040516307c0329d60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631f00ca749061266290879085906004016144c4565b60006040518083038186803b15801561267a57600080fd5b505afa15801561268e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526126b69190810190613a35565b9695505050505050565b6126c9816132cf565b1561274d57836001600160a01b031663d505accf8430846000015185602001518660400151876060015188608001516040518863ffffffff1660e01b815260040161271a9796959493929190613e96565b600060405180830381600087803b15801561273457600080fd5b505af1158015612748573d6000803e3d6000fd5b505050505b6109ce6001600160a01b0385168430856132f4565b61276e848484846126c0565b604051631a4ca37b60e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906369328dec906127be90889086903090600401613f6e565b602060405180830381600087803b1580156127d857600080fd5b505af11580156127ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e59190613c3a565b6119e78363a9059cbb60e01b84846040516024016119b0929190613f55565b60008061283b89612e47565b9050600061284889612e47565b905060006128558b613315565b905060006128628b613315565b905060006128a6612877612710610bb8611cd3565b6128a061288886600a89900a611473565b6107fa61289987600a8c900a611473565b8d90611473565b906133b4565b90508089106128c75760405162461bcd60e51b815260040161032890614246565b6128fc6001600160a01b038c167f000000000000000000000000000000000000000000000000000000000000000060006118ed565b6129306001600160a01b038c167f00000000000000000000000000000000000000000000000000000000000000008b6118ed565b60608715612a08576040805160038082526080820190925290602082016060803683370190505090508b8160008151811061296757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f0000000000000000000000000000000000000000000000000000000000000000816001815181106129b557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508a816002815181106129e357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612a85565b60408051600280825260608201835290916020830190803683370190505090508b81600081518110612a3657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508a81600181518110612a6457fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b604051634401edf760e11b81526060906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638803dbee90612adc908d908f908790309042906004016144eb565b600060405180830381600087803b158015612af657600080fd5b505af1158015612b0a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612b329190810190613a35565b90507fa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb8d8d83600081518110612b6457fe5b602002602001015184600186510381518110612b7c57fe5b6020026020010151604051612b949493929190613e6d565b60405180910390a180600081518110612ba957fe5b6020026020010151975050505050505050979650505050505050565b612bd7826001600160a01b0316613426565b612bf35760405162461bcd60e51b815260040161032890614484565b60006060836001600160a01b031683604051612c0f9190613dff565b6000604051808303816000865af19150503d8060008114612c4c576040519150601f19603f3d011682016040523d82523d6000602084013e612c51565b606091505b509150915081612c735760405162461bcd60e51b8152600401610328906141da565b8051156109ce5780806020019051810190612c8e9190613aca565b6109ce5760405162461bcd60e51b8152600401610328906143e4565b6000612cb68383612cf4565b612cec57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610a40565b506000610a40565b60009081526001919091016020526040902054151590565b60008181526001830160205260408120548015612dc85783546000198083019190810190600090879083908110612d3f57fe5b9060005260206000200154905080876000018481548110612d5c57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080612d8c57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610a40565b6000915050610a40565b5490565b81546000908210612df95760405162461bcd60e51b815260040161032890614071565b826000018281548110612e0857fe5b9060005260206000200154905092915050565b60008184841115612e3f5760405162461bcd60e51b8152600401610328919061405e565b505050900390565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015612e8257600080fd5b505afa158015612e96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eba9190613d19565b60ff1692915050565b600080612ee37310f7fc1f91ba351f9c629c5947ad69bd03c05b96613315565b90506000612ef086613315565b90506126b6670de0b6b3a76400006107fa8461220f600a89900a838b88611473565b6040805160028082526060828101909352829182918160200160208202803683370190505090508581600081518110612f4757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600181518110612f7557fe5b6001600160a01b0392909216602092830291909101820152604080516003808252608082019092526060928392839291820183803683370190505090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316896001600160a01b03161415801561302657507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316886001600160a01b031614155b1561319257888160008151811061303957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f00000000000000000000000000000000000000000000000000000000000000008160018151811061308757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505087816002815181106130b557fe5b6001600160a01b0392831660209182029290920101526040516307c0329d60e21b81527f000000000000000000000000000000000000000000000000000000000000000090911690631f00ca7490613113908a9085906004016144c4565b60006040518083038186803b15801561312b57600080fd5b505afa92505050801561316057506040513d6000823e601f3d908101601f1916820160405261315d9190810190613a35565b60015b61318a5760408051600380825260808201909252906020820160608036833701905050915061318d565b91505b6131b4565b6040805160038082526080820190925290602082016060803683370190505091505b6040516307c0329d60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631f00ca7490613202908a9088906004016144c4565b60006040518083038186803b15801561321a57600080fd5b505afa92505050801561324f57506040513d6000823e601f3d908101601f1916820160405261324c9190810190613a35565b60015b6132605790945092506132c7915050565b8093508360008151811061327057fe5b60200260200101518360008151811061328557fe5b60200260200101511080156132af5750826000815181106132a257fe5b6020026020010151600014155b6132ba5783856132bd565b82825b9650965050505050505b935093915050565b6000816040015160ff1682602001511480156132ed57506020820151155b1592915050565b6109ce846323b872dd60e01b8585856040516024016119b093929190613e49565b60405163b3596f0760e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b3596f0790613364908590600401613e1b565b60206040518083038186803b15801561337c57600080fd5b505afa158015613390573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a409190613c3a565b60008215806133c1575081155b156133ce57506000610a40565b8161138819816133da57fe5b0483111560405180604001604052806002815260200161068760f31b815250906134175760405162461bcd60e51b8152600401610328919061405e565b50506127109102611388010490565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061345a57508115155b949350505050565b604051806101800160405280613476613587565b815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e082018190526101008201819052610120820181905261014082018190526101609091015290565b60405180610120016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200161351f61359a565b81526000602082018190526040820181905260609091015290565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001606081525090565b60405180606001604052806003906020820280368337509192915050565b6040518060200160405280600081525090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b8051610a40816145d0565b60008083601f8401126135e4578182fd5b50813567ffffffffffffffff8111156135fb578182fd5b602083019150836020808302850101111561361557600080fd5b9250929050565b600082601f83011261362c578081fd5b613636606061455d565b905080828460608501111561364a57600080fd5b60005b6003811015613676578151613661816145e8565b8352602092830192919091019060010161364d565b50505092915050565b600082601f83011261368f578081fd5b613699608061455d565b90508082846080850111156136ad57600080fd5b60005b60048110156136765781356136c4816145e8565b835260209283019291909101906001016136b0565b600082601f8301126136e9578081fd5b6136f3606061455d565b905080828460608501111561370757600080fd5b60005b600381101561367657813583526020928301929091019060010161370a565b600060a0828403121561373a578081fd5b50919050565b600060208284031215613751578081fd5b61375b602061455d565b9151825250919050565b80516fffffffffffffffffffffffffffffffff81168114610a4057600080fd5b805164ffffffffff81168114610a4057600080fd5b8051610a40816145f6565b6000602082840312156137b6578081fd5b81356114ad816145d0565b6000806000806101e085870312156137d7578283fd5b85601f8601126137e5578283fd5b6137ef606061455d565b80866060880189811115613801578687fd5b865b600381101561382c578235613817816145d0565b85526020948501949290920191600101613803565b5082975061383a8a826136d9565b96505050505061384d8660c08701613729565b915061385d86610160870161367f565b905092959194509250565b60008060008060008060008060006101a08a8c031215613886578687fd5b8a601f8b0112613894578687fd5b61389e606061455d565b808b60608d018e8111156138b0578a8bfd5b8a5b60038110156138db5782516138c6816145d0565b855260209485019492909201916001016138b2565b50829c5080519b505050505060808a0151965060a08a0151955060c08a015194506139098b60e08c0161379a565b93506101008a015192506101208a015191506139298b6101408c0161361c565b90509295985092959850929598565b600080600080600080600080600060a08a8c031215613955578283fd5b893567ffffffffffffffff8082111561396c578485fd5b6139788d838e016135d3565b909b50995060208c0135915080821115613990578485fd5b61399c8d838e016135d3565b909950975060408c01359150808211156139b4578485fd5b6139c08d838e016135d3565b909750955060608c013591506139d5826145d0565b90935060808b013590808211156139ea578384fd5b818c0191508c601f8301126139fd578384fd5b813581811115613a0b578485fd5b8d6020828501011115613a1c578485fd5b6020830194508093505050509295985092959850929598565b60006020808385031215613a47578182fd5b825167ffffffffffffffff811115613a5d578283fd5b8301601f81018513613a6d578283fd5b8051613a80613a7b82614584565b61455d565b8181528381019083850185840285018601891015613a9c578687fd5b8694505b83851015613abe578051835260019490940193918501918501613aa0565b50979650505050505050565b600060208284031215613adb578081fd5b81516114ad816145e8565b600060a08284031215613af7578081fd5b613b0160a061455d565b82358152602083013560208201526040830135613b1d816145f6565b6040820152606083810135908201526080928301359281019290925250919050565b6000610180808385031215613b52578182fd5b613b5b8161455d565b9050613b678484613740565b8152613b768460208501613765565b6020820152613b888460408501613765565b6040820152613b9a8460608501613765565b6060820152613bac8460808501613765565b6080820152613bbe8460a08501613765565b60a0820152613bd08460c08501613785565b60c0820152613be28460e085016135c8565b60e0820152610100613bf6858286016135c8565b90820152610120613c09858583016135c8565b90820152610140613c1c858583016135c8565b90820152610160613c2f8585830161379a565b908201529392505050565b600060208284031215613c4b578081fd5b5051919050565b600080600060608486031215613c66578081fd5b833592506020840135613c78816145d0565b91506040840135613c88816145d0565b809150509250925092565b60008060408385031215613ca5578182fd5b50508035926020909101359150565b60008060008060008060c08789031215613ccc578384fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600060208284031215613d0e578081fd5b81356114ad816145f6565b600060208284031215613d2a578081fd5b81516114ad816145f6565b6000815180845260208085019450808401835b83811015613d6d5781516001600160a01b031687529582019590820190600101613d48565b509495945050505050565b8060005b60038110156109ce5781511515845260209384019390910190600101613d7c565b6000815180845260208085019450808401835b83811015613d6d57815187529582019590820190600101613db0565b60008151808452613de48160208601602086016145a4565b601f01601f19169290920160200192915050565b60ff169052565b60008251613e118184602087016145a4565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b600060018060a01b03808a16835260e06020840152613ef960e084018a613d35565b8381036040850152613f0b818a613d9d565b90508381036060850152613f1f8189613d9d565b9050818716608085015283810360a0850152613f3b8187613dcc565b9250505061ffff831660c083015298975050505050505050565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b03948516815260208101939093526040830191909152909116606082015260800190565b6101a08101818b60005b6003811015613fee5781516001600160a01b0316835260209283019290910190600101613fc6565b5050508960608301528860808301528760a08301528660c083015261401660e0830187613df8565b8461010083015283610120830152614032610140830184613d78565b9a9950505050505050505050565b6000602082526114ad6020830184613d35565b901515815260200190565b6000602082526114ad6020830184613dcc565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252604d908201527f55736572206865616c746820666163746f72206d75737420626520696e20726160408201527f6e6765207b66726f6d206d696e4865616c7468466163746f7220746f206d617860608201526c4865616c7468466163746f727d60981b608082015260a00190565b6020808252601b908201527f43414c4c45525f4d5553545f42455f4c454e44494e475f504f4f4c0000000000604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b60208082526019908201527f43616c6c6572206973206e6f742077686974656c697374656400000000000000604082015260600190565b60208082526023908201527f6d6178416d6f756e74546f5377617020657863656564206d617820736c69707060408201526261676560e81b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252603d908201527f55736572206865616c746820666163746f72206d757374206265206c6573732060408201527f7468616e206d696e4865616c7468466163746f7220666f722075736572000000606082015260800190565b6020808252603c908201527f6d61784865616c7468466163746f722073686f756c64206265206d6f7265206f60408201527f7220657175616c207468616e206d696e4865616c7468466163746f7200000000606082015260800190565b6020808252601190820152700e6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606082015260800190565b6020808252601f908201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604082015260600190565b90815260200190565b60008382526040602083015261345a6040830184613d35565b918252602082015260400190565b600086825285602083015260a0604083015261450a60a0830186613d35565b6001600160a01b0394909416606083015250608001529392505050565b600086825285602083015284604083015283606083015260a0608083015261455260a0830184613d35565b979650505050505050565b60405181810167ffffffffffffffff8111828210171561457c57600080fd5b604052919050565b600067ffffffffffffffff82111561459a578081fd5b5060209081020190565b60005b838110156145bf5781810151838201526020016145a7565b838111156109ce5750506000910152565b6001600160a01b03811681146145e557600080fd5b50565b80151581146145e557600080fd5b60ff811681146145e557600080fdfea26469706673582212206e171dbd97a0dfd95a13d5c2427474b050d5e0eb09da6c6aeef678abe0c46d4864736f6c634300060c0033000000000000000000000000b3072f5f0d5e8b9036aec29f37bab70e86ea0018000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f96121000000000000000000000000f194afdf50b03e69bd7d057c1aa9e10c9954e4c9

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101575760003560e01c80638c39745d116100c3578063c57981b51161007c578063c57981b5146102a2578063ccf288c6146102aa578063cdf58cd6146102b2578063d8264920146102c5578063ee76103e146102cd578063f2fde38b146102e057610157565b80638c39745d1461023e5780638da5cb5b14610253578063920f5c841461025b5780639d1211bf1461026e578063b4dcfc7714610276578063baf7fa991461027e57610157565b806338013f021161011557806338013f02146101c757806341566585146101cf57806343b0215f146101ef578063637bcd2a14610210578063715018a6146102235780638ab1d6811461022b57610157565b8062ae3bf81461015c578063040141e5146101715780630542975c1461018f578063074b2e4314610197578063114778a4146101ac57806332e4b286146101bf575b600080fd5b61016f61016a3660046137a5565b6102f3565b005b610179610437565b6040516101869190613e1b565b60405180910390f35b61017961045b565b61019f61047f565b60405161018691906144bb565b61016f6101ba3660046137c1565b610484565b61019f6109d4565b6101796109da565b6101e26101dd3660046137a5565b6109fe565b6040516101869190614053565b6102026101fd3660046137a5565b610a46565b6040516101869291906144dd565b6101e261021e3660046137a5565b610a5f565b61016f610a6c565b6101e26102393660046137a5565b610aeb565b610246610b2d565b6040516101869190614040565b610179610bcc565b6101e2610269366004613938565b610bdb565b61017961111b565b610179611133565b61029161028c366004613c52565b611157565b604051610186959493929190614527565b61019f61119d565b61019f6111a2565b6102916102c0366004613c52565b6111a8565b6101796111c3565b61016f6102db366004613c93565b6111e7565b61016f6102ee3660046137a5565b611234565b6102fb6112ea565b6000546001600160a01b039081169116146103315760405162461bcd60e51b8152600401610328906142ca565b60405180910390fd5b806001600160a01b031663a9059cbb610348610bcc565b6040516370a0823160e01b81526001600160a01b038516906370a0823190610374903090600401613e1b565b60206040518083038186803b15801561038c57600080fd5b505afa1580156103a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103c49190613c3a565b6040518363ffffffff1660e01b81526004016103e1929190613f55565b602060405180830381600087803b1580156103fb57600080fd5b505af115801561040f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104339190613aca565b5050565b7f000000000000000000000000f194afdf50b03e69bd7d057c1aa9e10c9954e4c981565b7f000000000000000000000000b3072f5f0d5e8b9036aec29f37bab70e86ea001881565b600981565b61048d33610a5f565b6104a95760405162461bcd60e51b81526004016103289061420f565b83516104b4906112ee565b6060810151156106dd57604080516060808201835286516001600160a01b0390811683526020808901519091168184015233838501528651878501519294909291873591908801359061050c90898801908a01613cfd565b8860600135896080013560405180606001604052808b60006004811061052e57fe5b60209081029190910151151582528c8101511515828201526040808e0151151592810192909252905161056a9a99989796959493929101613fbc565b60408051601f198184030181526001808452838301909252925060609190602080830190803683370190505090508560026020020151816000815181106105ad57fe5b6001600160a01b0392909216602092830291909101909101526040805160018082528183019092526060918160200160208202803683370190505090508560016020020151816000815181106105ff57fe5b602090810291909101015260408051600180825281830190925260609181602001602082028036833701905050905060008160008151811061063d57fe5b6020908102919091010152875160405163ab9c4b5d60e01b81527f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e886001600160a01b03169163ab9c4b5d916106a2913091889188918891908c90600090600401613ed7565b600060405180830381600087803b1580156106bc57600080fd5b505af11580156106d0573d6000803e3d6000fd5b50505050505050506109c3565b6106e5613462565b6106f68560025b60200201516113ce565b905060008060018660026020020151600281111561071057fe5b600281111561071b57fe5b1461072b57826101200151610732565b8261010001515b87516040516370a0823160e01b81529192506000916001600160a01b038416916370a08231916107659190600401613e1b565b60206040518083038186803b15801561077d57600080fd5b505afa158015610791573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b59190613c3a565b905080876001602002015111156107cc57806107d2565b60208701515b8751602089015191945092508310159050610803576020860151610800906107fa8385611473565b906114b4565b90505b6108a660405180608001604052808960006003811061081e57fe5b602090810291909101516001600160a01b0390811683528b8201518116838301526040808d015190911681840152336060938401528051928301815286835290820185905260009082015261087836899003890189613ae6565b604080516060810182528951151581526020808b0151151590820152898201511515918101919091526114f6565b6108e57f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e8860008960025b60200201516001600160a01b031691906118ed565b6109127f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e88838960026108d0565b604080880151878201518951925163573ade8160e01b81527f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e886001600160a01b03169363573ade819361096c939092889290600401613f91565b602060405180830381600087803b15801561098657600080fd5b505af115801561099a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109be9190613c3a565b505050505b83516109ce906119ec565b50505050565b610bb881565b7f00000000000000000000000088a4a87ef224d8b1f463708d0cd62b17de593dad81565b6000610a086112ea565b6000546001600160a01b03908116911614610a355760405162461bcd60e51b8152600401610328906142ca565b610a40600183611af4565b92915050565b6003602052600090815260409020805460019091015482565b6000610a40600183611b09565b610a746112ea565b6000546001600160a01b03908116911614610aa15760405162461bcd60e51b8152600401610328906142ca565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000610af56112ea565b6000546001600160a01b03908116911614610b225760405162461bcd60e51b8152600401610328906142ca565b610a40600183611b1e565b60606000610b3b6001611b33565b905060608167ffffffffffffffff81118015610b5657600080fd5b50604051908082528060200260200182016040528015610b80578160200160208202803683370190505b50905060005b82811015610bc557610b99600182611b3e565b828281518110610ba557fe5b6001600160a01b0390921660209283029190910190910152600101610b86565b5091505090565b6000546001600160a01b031690565b6000336001600160a01b037f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e881614610c255760405162461bcd60e51b815260040161032890614126565b610c2d6134cd565b610c6c84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b4a92505050565b9050610cc67f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e8860008d8d6000818110610ca157fe5b9050602002016020810190610cb691906137a5565b6001600160a01b031691906118ed565b610d0b7f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e888a8a6000818110610cf757fe5b905060200201358d8d6000818110610ca157fe5b60008b8b6000818110610d1a57fe5b9050602002016020810190610d2f91906137a5565b6001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610d5a9190613e1b565b60206040518083038186803b158015610d7257600080fd5b505afa158015610d86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610daa9190613c3a565b90507f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e886001600160a01b031663573ade818d8d6000818110610de857fe5b9050602002016020810190610dfd91906137a5565b8c8c6000818110610e0a57fe5b90506020020135856080015186600001516040518563ffffffff1660e01b8152600401610e3a9493929190613f91565b602060405180830381600087803b158015610e5457600080fd5b505af1158015610e68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8c9190613c3a565b50610f348c8c6000818110610e9d57fe5b9050602002016020810190610eb291906137a5565b6001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610edd9190613e1b565b60206040518083038186803b158015610ef557600080fd5b505afa158015610f09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2d9190613c3a565b8290611c91565b60608301519091508a8a600081610f4757fe5b90506020020135821015610f8257610f7f8b8b6000818110610f6557fe5b905060200201356107fa848461147390919063ffffffff16565b90505b6040805160808101825284516001600160a01b0390811682526020808701519091169082015261106b9181018f8f600081610fb957fe5b9050602002016020810190610fce91906137a5565b6001600160a01b03166001600160a01b0316815260200185604001516001600160a01b03166001600160a01b031681525060405180606001604052808581526020018481526020018c8c600081811061102357fe5b905060200201358152508560a0015160405180606001604052808860c001511515151581526020018860e00151151515158152602001886101000151151515158152506114f6565b61109e7f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e8860008f8f6000818110610ca157fe5b6111087f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e886110fb8b8b60008181106110d257fe5b905060200201358e8e60008181106110e657fe5b90506020020135611cd390919063ffffffff16565b8f8f6000818110610ca157fe5b5060019c9b505050505050505050505050565b7310f7fc1f91ba351f9c629c5947ad69bd03c05b9681565b7f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e8881565b600080600080606061116761353a565b61117288888b611cf8565b8051602082015160408301516060840151608090940151929d919c509a509198509650945050505050565b600a81565b61271081565b60008060008060606111b861353a565b61117288888b6122ad565b7f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f9612181565b818110156112075760405162461bcd60e51b81526004016103289061435c565b60408051808201825292835260208084019283523360009081526003909152209151825551600190910155565b61123c6112ea565b6000546001600160a01b039081169116146112695760405162461bcd60e51b8152600401610328906142ca565b6001600160a01b03811661128f5760405162461bcd60e51b81526004016103289061415d565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b604051632fe4a15f60e21b81526000906001600160a01b037f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e88169063bf92857c9061133d908590600401613e1b565b60c06040518083038186803b15801561135557600080fd5b505afa158015611369573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061138d9190613cb4565b6001600160a01b0388166000908152600360205260409020549096508610945061043393505050505760405162461bcd60e51b8152600401610328906142ff565b6113d6613462565b6040516335ea6a7560e01b81526001600160a01b037f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e8816906335ea6a7590611422908590600401613e1b565b6101806040518083038186803b15801561143b57600080fd5b505afa15801561144f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a409190613b3f565b60008261148257506000610a40565b8282028284828161148f57fe5b04146114ad5760405162461bcd60e51b815260040161032890614289565b9392505050565b60006114ad83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612487565b60006115038560016106ec565b60e00151905060006115168660026106ec565b60e00151604087015160208801519192506001600160a01b0391821691161461188557600061158a8460016020020151611554576020880151611556565b835b604086015161156957604089015161156b565b835b6040890151611583908a60005b602002015190611cd3565b87516124be565b60008151811061159657fe5b60200260200101519050856001600381106115ad57fe5b60200201518111156115d15760405162461bcd60e51b8152600401610328906143b9565b60006115e46127106107fa84600a611473565b6020860151909150156116b85787516116099085906116038585611cd3565b896126c0565b60208801516060890151604051631a4ca37b60e21b81527f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e886001600160a01b0316926369328dec9261166092869190600401613f6e565b602060405180830381600087803b15801561167a57600080fd5b505af115801561168e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b29190613c3a565b506116f9565b602088015188516116d6919086906116d08686611cd3565b8a612762565b60608801516116f990828a60015b60200201516001600160a01b03169190612810565b60208089015160408a01519187015161174d929061171b5760208b015161171d565b865b60408901516117305760408c0151611732565b865b866117468d600260200201518e6000611578565b8b5161282f565b5060408501511561187e576001600160a01b037f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e88166369328dec89600260200201516040516370a0823160e01b81526001600160a01b038716906370a08231906117bb903090600401613e1b565b60206040518083038186803b1580156117d357600080fd5b505afa1580156117e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180b9190613c3a565b306040518463ffffffff1660e01b815260040161182a93929190613f6e565b602060405180830381600087803b15801561184457600080fd5b505af1158015611858573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187c9190613c3a565b505b50506118e5565b845160009061189d90612710906107fa90600a611473565b6020880151885160408901519293506118d1928691906118cb9086906118c5908d6000611578565b90611cd3565b89612762565b60608701516118e390828960016116e4565b505b505050505050565b8015806119755750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906119239030908690600401613e2f565b60206040518083038186803b15801561193b57600080fd5b505afa15801561194f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119739190613c3a565b155b6119915760405162461bcd60e51b81526004016103289061442e565b6119e78363095ea7b360e01b84846040516024016119b0929190613f55565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612bc5565b505050565b604051632fe4a15f60e21b81526000906001600160a01b037f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e88169063bf92857c90611a3b908590600401613e1b565b60c06040518083038186803b158015611a5357600080fd5b505afa158015611a67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8b9190613cb4565b6001600160a01b0388166000908152600360205260409020549096508610801595509350611ad89250505057506001600160a01b0382166000908152600360205260409020600101548111155b6104335760405162461bcd60e51b8152600401610328906140b3565b60006114ad836001600160a01b038416612caa565b60006114ad836001600160a01b038416612cf4565b60006114ad836001600160a01b038416612d0c565b6000610a4082612dd2565b60006114ad8383612dd6565b611b526134cd565b611b5a613569565b6000806000806000806000611b6d613569565b8a806020019051810190611b819190613868565b9850985098509850985098509850985098506040518061012001604052808a600060038110611bac57fe5b60200201516001600160a01b031681526020018a600160038110611bcc57fe5b60200201516001600160a01b031681526020018a600260038110611bec57fe5b60200201516001600160a01b031681526020018981526020018881526020016040518060a001604052808981526020018881526020018760ff16815260200186815260200185815250815260200182600060038110611c4757fe5b60200201511515815260200182600160038110611c6057fe5b60200201511515815260200182600260038110611c7957fe5b6020020151151590529b9a5050505050505050505050565b60006114ad83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612e1b565b6000828201838110156114ad5760405162461bcd60e51b8152600401610328906141a3565b611d0061353a565b6000611d1d611d166127106107fa866009611473565b8490611c91565b9050836001600160a01b0316856001600160a01b03161415611de9576000611d4486612e47565b60408051600180825281830190925291925060609190602080830190803683370190505090508681600081518110611d7857fe5b6001600160a01b039092166020928302919091018201526040805160a08101909152848152908101611db6876107fa87670de0b6b3a7640000611473565b8152602001611dc6898886612ec3565b8152602001611dd6898686612ec3565b81526020018281525093505050506114ad565b60408051600280825260608083018452926020830190803683370190505090508581600081518110611e1757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600181518110611e4557fe5b6001600160a01b0392909216602092830291909101820152604080516003808252608082019092526060928392839291820183803683370190505090507f000000000000000000000000f194afdf50b03e69bd7d057c1aa9e10c9954e4c96001600160a01b0316896001600160a01b031614158015611ef657507f000000000000000000000000f194afdf50b03e69bd7d057c1aa9e10c9954e4c96001600160a01b0316886001600160a01b031614155b15612062578881600081518110611f0957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000f194afdf50b03e69bd7d057c1aa9e10c9954e4c981600181518110611f5757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508781600281518110611f8557fe5b6001600160a01b03928316602091820292909201015260405163d06ca61f60e01b81527f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961219091169063d06ca61f90611fe390889085906004016144c4565b60006040518083038186803b158015611ffb57600080fd5b505afa92505050801561203057506040513d6000823e601f3d908101601f1916820160405261202d9190810190613a35565b60015b61205a5760408051600380825260808201909252906020820160608036833701905050915061205d565b91505b612084565b6040805160038082526080820190925290602082016060803683370190505091505b60405163d06ca61f60e01b81526000906001600160a01b037f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f96121169063d06ca61f906120d590899089906004016144c4565b60006040518083038186803b1580156120ed57600080fd5b505afa92505050801561212257506040513d6000823e601f3d908101601f1916820160405261211f9190810190613a35565b60015b6121625760408051600280825260608201835290916020830190803683370190505093508260028151811061215357fe5b602002602001015190506121c8565b8094508460018151811061217257fe5b60200260200101518460028151811061218757fe5b6020026020010151116121ae57846001815181106121a157fe5b60200260200101516121c4565b836002815181106121bb57fe5b60200260200101515b9150505b60006121d38b612e47565b905060006121e08b612e47565b905060006122156121f585600a86900a611473565b6107fa600a85900a61220f8d670de0b6b3a7640000611473565b90611473565b90506040518060a001604052808581526020018281526020016122398f8e87612ec3565b81526020016122498e8786612ec3565b8152602001851561227c578860018151811061226157fe5b602002602001015186146122755786612277565b895b61229a565b60408051600280825260608201835290916020830190803683375050505b90529d9c50505050505050505050505050565b6122b561353a565b826001600160a01b0316846001600160a01b0316141561238b5760006122ec6122e56127106107fa866009611473565b8490611cd3565b905060006122f986612e47565b6040805160018082528183019092529192506060919060208083019080368337019050509050868160008151811061232d57fe5b6001600160a01b039092166020928302919091018201526040805160a0810190915284815290810161236b856107fa89670de0b6b3a7640000611473565b815260200161237b898686612ec3565b8152602001611dd6898886612ec3565b606080612399868686612f12565b9150915060006123f36123d06127106107fa6009876000815181106123ba57fe5b602002602001015161147390919063ffffffff16565b846000815181106123dd57fe5b6020026020010151611cd390919063ffffffff16565b9050600061240088612e47565b9050600061240d88612e47565b9050600061243c61242285600a85900a611473565b6107fa600a86900a61220f8c670de0b6b3a7640000611473565b90506040518060a001604052808581526020018281526020016124608c8787612ec3565b81526020016124708b8b86612ec3565b815260200195909552509298975050505050505050565b600081836124a85760405162461bcd60e51b8152600401610328919061405e565b5060008385816124b457fe5b0495945050505050565b60608082156125975760408051600380825260808201909252906020820160608036833701905050905085816000815181106124f657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000f194afdf50b03e69bd7d057c1aa9e10c9954e4c98160018151811061254457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050848160028151811061257257fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612614565b604080516002808252606082018352909160208301908036833701905050905085816000815181106125c557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505084816001815181106125f357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6040516307c0329d60e21b81526001600160a01b037f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961211690631f00ca749061266290879085906004016144c4565b60006040518083038186803b15801561267a57600080fd5b505afa15801561268e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526126b69190810190613a35565b9695505050505050565b6126c9816132cf565b1561274d57836001600160a01b031663d505accf8430846000015185602001518660400151876060015188608001516040518863ffffffff1660e01b815260040161271a9796959493929190613e96565b600060405180830381600087803b15801561273457600080fd5b505af1158015612748573d6000803e3d6000fd5b505050505b6109ce6001600160a01b0385168430856132f4565b61276e848484846126c0565b604051631a4ca37b60e21b81526001600160a01b037f00000000000000000000000058ad305f1ece49ca55ade0d5ccc90114c3902e8816906369328dec906127be90889086903090600401613f6e565b602060405180830381600087803b1580156127d857600080fd5b505af11580156127ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e59190613c3a565b6119e78363a9059cbb60e01b84846040516024016119b0929190613f55565b60008061283b89612e47565b9050600061284889612e47565b905060006128558b613315565b905060006128628b613315565b905060006128a6612877612710610bb8611cd3565b6128a061288886600a89900a611473565b6107fa61289987600a8c900a611473565b8d90611473565b906133b4565b90508089106128c75760405162461bcd60e51b815260040161032890614246565b6128fc6001600160a01b038c167f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f9612160006118ed565b6129306001600160a01b038c167f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961218b6118ed565b60608715612a08576040805160038082526080820190925290602082016060803683370190505090508b8160008151811061296757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000f194afdf50b03e69bd7d057c1aa9e10c9954e4c9816001815181106129b557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508a816002815181106129e357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612a85565b60408051600280825260608201835290916020830190803683370190505090508b81600081518110612a3657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508a81600181518110612a6457fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b604051634401edf760e11b81526060906001600160a01b037f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961211690638803dbee90612adc908d908f908790309042906004016144eb565b600060405180830381600087803b158015612af657600080fd5b505af1158015612b0a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612b329190810190613a35565b90507fa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb8d8d83600081518110612b6457fe5b602002602001015184600186510381518110612b7c57fe5b6020026020010151604051612b949493929190613e6d565b60405180910390a180600081518110612ba957fe5b6020026020010151975050505050505050979650505050505050565b612bd7826001600160a01b0316613426565b612bf35760405162461bcd60e51b815260040161032890614484565b60006060836001600160a01b031683604051612c0f9190613dff565b6000604051808303816000865af19150503d8060008114612c4c576040519150601f19603f3d011682016040523d82523d6000602084013e612c51565b606091505b509150915081612c735760405162461bcd60e51b8152600401610328906141da565b8051156109ce5780806020019051810190612c8e9190613aca565b6109ce5760405162461bcd60e51b8152600401610328906143e4565b6000612cb68383612cf4565b612cec57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610a40565b506000610a40565b60009081526001919091016020526040902054151590565b60008181526001830160205260408120548015612dc85783546000198083019190810190600090879083908110612d3f57fe5b9060005260206000200154905080876000018481548110612d5c57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080612d8c57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610a40565b6000915050610a40565b5490565b81546000908210612df95760405162461bcd60e51b815260040161032890614071565b826000018281548110612e0857fe5b9060005260206000200154905092915050565b60008184841115612e3f5760405162461bcd60e51b8152600401610328919061405e565b505050900390565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015612e8257600080fd5b505afa158015612e96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eba9190613d19565b60ff1692915050565b600080612ee37310f7fc1f91ba351f9c629c5947ad69bd03c05b96613315565b90506000612ef086613315565b90506126b6670de0b6b3a76400006107fa8461220f600a89900a838b88611473565b6040805160028082526060828101909352829182918160200160208202803683370190505090508581600081518110612f4757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600181518110612f7557fe5b6001600160a01b0392909216602092830291909101820152604080516003808252608082019092526060928392839291820183803683370190505090507f000000000000000000000000f194afdf50b03e69bd7d057c1aa9e10c9954e4c96001600160a01b0316896001600160a01b03161415801561302657507f000000000000000000000000f194afdf50b03e69bd7d057c1aa9e10c9954e4c96001600160a01b0316886001600160a01b031614155b1561319257888160008151811061303957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000f194afdf50b03e69bd7d057c1aa9e10c9954e4c98160018151811061308757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505087816002815181106130b557fe5b6001600160a01b0392831660209182029290920101526040516307c0329d60e21b81527f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f9612190911690631f00ca7490613113908a9085906004016144c4565b60006040518083038186803b15801561312b57600080fd5b505afa92505050801561316057506040513d6000823e601f3d908101601f1916820160405261315d9190810190613a35565b60015b61318a5760408051600380825260808201909252906020820160608036833701905050915061318d565b91505b6131b4565b6040805160038082526080820190925290602082016060803683370190505091505b6040516307c0329d60e21b81526001600160a01b037f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961211690631f00ca7490613202908a9088906004016144c4565b60006040518083038186803b15801561321a57600080fd5b505afa92505050801561324f57506040513d6000823e601f3d908101601f1916820160405261324c9190810190613a35565b60015b6132605790945092506132c7915050565b8093508360008151811061327057fe5b60200260200101518360008151811061328557fe5b60200260200101511080156132af5750826000815181106132a257fe5b6020026020010151600014155b6132ba5783856132bd565b82825b9650965050505050505b935093915050565b6000816040015160ff1682602001511480156132ed57506020820151155b1592915050565b6109ce846323b872dd60e01b8585856040516024016119b093929190613e49565b60405163b3596f0760e01b81526000906001600160a01b037f00000000000000000000000088a4a87ef224d8b1f463708d0cd62b17de593dad169063b3596f0790613364908590600401613e1b565b60206040518083038186803b15801561337c57600080fd5b505afa158015613390573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a409190613c3a565b60008215806133c1575081155b156133ce57506000610a40565b8161138819816133da57fe5b0483111560405180604001604052806002815260200161068760f31b815250906134175760405162461bcd60e51b8152600401610328919061405e565b50506127109102611388010490565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061345a57508115155b949350505050565b604051806101800160405280613476613587565b815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e082018190526101008201819052610120820181905261014082018190526101609091015290565b60405180610120016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200161351f61359a565b81526000602082018190526040820181905260609091015290565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001606081525090565b60405180606001604052806003906020820280368337509192915050565b6040518060200160405280600081525090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b8051610a40816145d0565b60008083601f8401126135e4578182fd5b50813567ffffffffffffffff8111156135fb578182fd5b602083019150836020808302850101111561361557600080fd5b9250929050565b600082601f83011261362c578081fd5b613636606061455d565b905080828460608501111561364a57600080fd5b60005b6003811015613676578151613661816145e8565b8352602092830192919091019060010161364d565b50505092915050565b600082601f83011261368f578081fd5b613699608061455d565b90508082846080850111156136ad57600080fd5b60005b60048110156136765781356136c4816145e8565b835260209283019291909101906001016136b0565b600082601f8301126136e9578081fd5b6136f3606061455d565b905080828460608501111561370757600080fd5b60005b600381101561367657813583526020928301929091019060010161370a565b600060a0828403121561373a578081fd5b50919050565b600060208284031215613751578081fd5b61375b602061455d565b9151825250919050565b80516fffffffffffffffffffffffffffffffff81168114610a4057600080fd5b805164ffffffffff81168114610a4057600080fd5b8051610a40816145f6565b6000602082840312156137b6578081fd5b81356114ad816145d0565b6000806000806101e085870312156137d7578283fd5b85601f8601126137e5578283fd5b6137ef606061455d565b80866060880189811115613801578687fd5b865b600381101561382c578235613817816145d0565b85526020948501949290920191600101613803565b5082975061383a8a826136d9565b96505050505061384d8660c08701613729565b915061385d86610160870161367f565b905092959194509250565b60008060008060008060008060006101a08a8c031215613886578687fd5b8a601f8b0112613894578687fd5b61389e606061455d565b808b60608d018e8111156138b0578a8bfd5b8a5b60038110156138db5782516138c6816145d0565b855260209485019492909201916001016138b2565b50829c5080519b505050505060808a0151965060a08a0151955060c08a015194506139098b60e08c0161379a565b93506101008a015192506101208a015191506139298b6101408c0161361c565b90509295985092959850929598565b600080600080600080600080600060a08a8c031215613955578283fd5b893567ffffffffffffffff8082111561396c578485fd5b6139788d838e016135d3565b909b50995060208c0135915080821115613990578485fd5b61399c8d838e016135d3565b909950975060408c01359150808211156139b4578485fd5b6139c08d838e016135d3565b909750955060608c013591506139d5826145d0565b90935060808b013590808211156139ea578384fd5b818c0191508c601f8301126139fd578384fd5b813581811115613a0b578485fd5b8d6020828501011115613a1c578485fd5b6020830194508093505050509295985092959850929598565b60006020808385031215613a47578182fd5b825167ffffffffffffffff811115613a5d578283fd5b8301601f81018513613a6d578283fd5b8051613a80613a7b82614584565b61455d565b8181528381019083850185840285018601891015613a9c578687fd5b8694505b83851015613abe578051835260019490940193918501918501613aa0565b50979650505050505050565b600060208284031215613adb578081fd5b81516114ad816145e8565b600060a08284031215613af7578081fd5b613b0160a061455d565b82358152602083013560208201526040830135613b1d816145f6565b6040820152606083810135908201526080928301359281019290925250919050565b6000610180808385031215613b52578182fd5b613b5b8161455d565b9050613b678484613740565b8152613b768460208501613765565b6020820152613b888460408501613765565b6040820152613b9a8460608501613765565b6060820152613bac8460808501613765565b6080820152613bbe8460a08501613765565b60a0820152613bd08460c08501613785565b60c0820152613be28460e085016135c8565b60e0820152610100613bf6858286016135c8565b90820152610120613c09858583016135c8565b90820152610140613c1c858583016135c8565b90820152610160613c2f8585830161379a565b908201529392505050565b600060208284031215613c4b578081fd5b5051919050565b600080600060608486031215613c66578081fd5b833592506020840135613c78816145d0565b91506040840135613c88816145d0565b809150509250925092565b60008060408385031215613ca5578182fd5b50508035926020909101359150565b60008060008060008060c08789031215613ccc578384fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600060208284031215613d0e578081fd5b81356114ad816145f6565b600060208284031215613d2a578081fd5b81516114ad816145f6565b6000815180845260208085019450808401835b83811015613d6d5781516001600160a01b031687529582019590820190600101613d48565b509495945050505050565b8060005b60038110156109ce5781511515845260209384019390910190600101613d7c565b6000815180845260208085019450808401835b83811015613d6d57815187529582019590820190600101613db0565b60008151808452613de48160208601602086016145a4565b601f01601f19169290920160200192915050565b60ff169052565b60008251613e118184602087016145a4565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b600060018060a01b03808a16835260e06020840152613ef960e084018a613d35565b8381036040850152613f0b818a613d9d565b90508381036060850152613f1f8189613d9d565b9050818716608085015283810360a0850152613f3b8187613dcc565b9250505061ffff831660c083015298975050505050505050565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b03948516815260208101939093526040830191909152909116606082015260800190565b6101a08101818b60005b6003811015613fee5781516001600160a01b0316835260209283019290910190600101613fc6565b5050508960608301528860808301528760a08301528660c083015261401660e0830187613df8565b8461010083015283610120830152614032610140830184613d78565b9a9950505050505050505050565b6000602082526114ad6020830184613d35565b901515815260200190565b6000602082526114ad6020830184613dcc565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252604d908201527f55736572206865616c746820666163746f72206d75737420626520696e20726160408201527f6e6765207b66726f6d206d696e4865616c7468466163746f7220746f206d617860608201526c4865616c7468466163746f727d60981b608082015260a00190565b6020808252601b908201527f43414c4c45525f4d5553545f42455f4c454e44494e475f504f4f4c0000000000604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b60208082526019908201527f43616c6c6572206973206e6f742077686974656c697374656400000000000000604082015260600190565b60208082526023908201527f6d6178416d6f756e74546f5377617020657863656564206d617820736c69707060408201526261676560e81b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252603d908201527f55736572206865616c746820666163746f72206d757374206265206c6573732060408201527f7468616e206d696e4865616c7468466163746f7220666f722075736572000000606082015260800190565b6020808252603c908201527f6d61784865616c7468466163746f722073686f756c64206265206d6f7265206f60408201527f7220657175616c207468616e206d696e4865616c7468466163746f7200000000606082015260800190565b6020808252601190820152700e6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606082015260800190565b6020808252601f908201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604082015260600190565b90815260200190565b60008382526040602083015261345a6040830184613d35565b918252602082015260400190565b600086825285602083015260a0604083015261450a60a0830186613d35565b6001600160a01b0394909416606083015250608001529392505050565b600086825285602083015284604083015283606083015260a0608083015261455260a0830184613d35565b979650505050505050565b60405181810167ffffffffffffffff8111828210171561457c57600080fd5b604052919050565b600067ffffffffffffffff82111561459a578081fd5b5060209081020190565b60005b838110156145bf5781810151838201526020016145a7565b838111156109ce5750506000910152565b6001600160a01b03811681146145e557600080fd5b50565b80151581146145e557600080fd5b60ff811681146145e557600080fdfea26469706673582212206e171dbd97a0dfd95a13d5c2427474b050d5e0eb09da6c6aeef678abe0c46d4864736f6c634300060c0033