Address Details
contract
0xCC321F48CF7bFeFe100D1Ce13585dcfF7627f754
- Contract Name
- AAutoRepay
- Creator
- 0xb44a99–08bd5b at 0xb99c66–95efd1
- 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
- 4 Transactions
- Transfers
- 11 Transfers
- Gas Used
- 1,188,699
- Last Balance Update
- 12049117
Transactions
Token Transfers
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
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-02-21T17:05:55.402964Z
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: agpl-3.0 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; /** * @dev struct RepayParams * * @param user Address of user * @param colalteralAsset Address of asset to be swapped * @param debtAsset Address of debt asset * @param collateralAmount Amount of the collateral to be swapped * @param debtRepayAmount Amount of the debt to be repaid * @param rateMode Rate mode of the debt to be repaid * @param useEthPath Use Eth in swap path * @param useATokenAsFrom Use aToken as from in swap * @param useATokenAsTo Use aToken as to in swap * @param useFlashloan Use flahsloan for increasing health factor */ struct RepayParams { address user; address collateralAsset; address debtAsset; uint256 collateralAmount; uint256 debtRepayAmount; uint256 rateMode; bool useEthPath; bool useATokenAsFrom; bool useATokenAsTo; bool useFlashloan; } 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) external onlyOwner returns (bool) { return _whitelistedAddresses.add(userAddress); } function removeFromWhitelist(address userAddress) external onlyOwner returns (bool) { return _whitelistedAddresses.remove(userAddress); } function isWhitelisted(address userAddress) public view returns (bool) { return _whitelistedAddresses.contains(userAddress); } function getWitelistedAddresses() external 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) external { 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 initiator Address of the flashloan caller * @param params Additional variadic field to include extra params. Expected parameters: * RepayParams repayParams - See {RepayParams} * PermitSignature permitSignature - struct containing the permit signature * address caller - Address of increaseHealthFactor function caller */ function executeOperation( address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, address initiator, bytes calldata params ) external override returns (bool) { require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL'); require(initiator == address(this), 'Only this contract can call flashloan'); ( RepayParams memory repayParams, PermitSignature memory permitSignature, address caller ) = _decodeParams(params); repayParams.debtAsset = assets[0]; repayParams.debtRepayAmount = amounts[0]; // Repay debt. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. { IERC20(repayParams.debtAsset).safeApprove(address(LENDING_POOL), 0); IERC20(repayParams.debtAsset).safeApprove(address(LENDING_POOL), repayParams.debtRepayAmount); uint256 repaidAmount = IERC20(repayParams.debtAsset).balanceOf(address(this)); LENDING_POOL.repay( repayParams.debtAsset, repayParams.debtRepayAmount, repayParams.rateMode, repayParams.user ); repaidAmount = repaidAmount.sub(IERC20(repayParams.debtAsset).balanceOf(address(this))); if (repaidAmount < repayParams.debtRepayAmount) { repayParams.collateralAmount = repayParams.collateralAmount.mul(repaidAmount).div( repayParams.debtRepayAmount ); } repayParams.debtRepayAmount = repaidAmount; } _doSwapAndPullWithFee(repayParams, permitSignature, caller, premiums[0]); // Repay flashloan. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. IERC20(repayParams.debtAsset).safeApprove(address(LENDING_POOL), 0); IERC20(repayParams.debtAsset).safeApprove(address(LENDING_POOL), amounts[0].add(premiums[0])); return true; } /** * @dev whitelisted address(caller) calls this function, repay debt from collateral * for the user, increases user health factor and take 0.1% fee from collateral * * @param repayParams See {RepayParams} * @param permitSignature struct containing the permit signature */ function increaseHealthFactor( RepayParams memory repayParams, PermitSignature calldata permitSignature ) external { require(isWhitelisted(msg.sender), 'Caller is not whitelisted'); _checkMinHealthFactor(repayParams.user); if (repayParams.useFlashloan) { bytes memory params = abi.encode(repayParams, permitSignature, msg.sender); address[] memory assets = new address[](1); assets[0] = repayParams.debtAsset; uint256[] memory amounts = new uint256[](1); amounts[0] = repayParams.debtRepayAmount; uint256[] memory modes = new uint256[](1); modes[0] = 0; LENDING_POOL.flashLoan(address(this), assets, amounts, modes, repayParams.user, params, 0); } else { DataTypes.ReserveData memory debtReserveData = _getReserveData(repayParams.debtAsset); uint256 amountToRepay; { address debtToken = DataTypes.InterestRateMode(repayParams.rateMode) == DataTypes.InterestRateMode.STABLE ? debtReserveData.stableDebtTokenAddress : debtReserveData.variableDebtTokenAddress; uint256 currentDebt = IERC20(debtToken).balanceOf(repayParams.user); amountToRepay = repayParams.debtRepayAmount <= currentDebt ? repayParams.debtRepayAmount : currentDebt; } if (amountToRepay < repayParams.debtRepayAmount) { repayParams.collateralAmount = repayParams.collateralAmount.mul(amountToRepay).div( repayParams.debtRepayAmount ); } repayParams.debtRepayAmount = amountToRepay; _doSwapAndPullWithFee(repayParams, permitSignature, msg.sender, 0); // Repay debt. Approves 0 first to comply with tokens that implement the anti frontrunning approval fix IERC20(repayParams.debtAsset).safeApprove(address(LENDING_POOL), 0); IERC20(repayParams.debtAsset).safeApprove(address(LENDING_POOL), repayParams.debtRepayAmount); LENDING_POOL.repay( repayParams.debtAsset, repayParams.debtRepayAmount, repayParams.rateMode, repayParams.user ); } _checkHealthFactorInRange(repayParams.user); } /** * @dev If the collateral asset is not equal to the debt asset, * then this function pulls tokens from the user, transfers the fee to the whitelisted caller, * and swaps the collateral asset to the debt asset. * Otherwise, if the collateral asset is equal to the debt asset then the function pulls tokens * from the user and transfers the fee to the whitelisted caller. * * @param repayParams See {RepayParams} * @param permitSignature struct containing the permit signature * @param caller address of increaseHealthFactor function caller * @param premium flashloan fee if called inside executeOperation otherwise 0 */ function _doSwapAndPullWithFee( RepayParams memory repayParams, PermitSignature memory permitSignature, address caller, uint256 premium ) internal { address collateralATokenAddress = _getReserveData(repayParams.collateralAsset).aTokenAddress; address debtATokenAddress = _getReserveData(repayParams.debtAsset).aTokenAddress; if (repayParams.collateralAsset != repayParams.debtAsset) { uint256 amounts0 = _getAmountsIn( repayParams.useATokenAsFrom ? collateralATokenAddress : repayParams.collateralAsset, repayParams.useATokenAsTo ? debtATokenAddress : repayParams.debtAsset, repayParams.debtRepayAmount.add(premium), repayParams.useEthPath )[0]; require(amounts0 <= repayParams.collateralAmount, 'slippage too high'); uint256 feeAmount = amounts0.mul(FEE).div(FEE_DECIMALS); _transferATokenToContractAddress( collateralATokenAddress, repayParams.user, amounts0.add(feeAmount), permitSignature ); IERC20(collateralATokenAddress).safeTransfer(caller, feeAmount); if (!repayParams.useATokenAsFrom) { // Pull aTokens from user LENDING_POOL.withdraw(repayParams.collateralAsset, amounts0, address(this)); } // Swap collateral asset to the debt asset _swapTokensForExactTokens( repayParams.collateralAsset, repayParams.debtAsset, repayParams.useATokenAsFrom ? collateralATokenAddress : repayParams.collateralAsset, repayParams.useATokenAsTo ? debtATokenAddress : repayParams.debtAsset, amounts0, repayParams.debtRepayAmount.add(premium), repayParams.useEthPath ); if (repayParams.useATokenAsTo) { // withdraw debt AToken LENDING_POOL.withdraw( repayParams.debtAsset, IERC20(debtATokenAddress).balanceOf(address(this)), address(this) ); } } else { uint256 feeAmount = repayParams.debtRepayAmount.mul(FEE).div(FEE_DECIMALS); uint256 aTokenTransferAmount = repayParams.debtRepayAmount.add(premium).add(feeAmount); _transferATokenToContractAddress( collateralATokenAddress, repayParams.user, aTokenTransferAmount, permitSignature ); LENDING_POOL.withdraw( repayParams.collateralAsset, repayParams.debtRepayAmount.add(premium), address(this) ); IERC20(collateralATokenAddress).safeTransfer( caller, IERC20(collateralATokenAddress).balanceOf(address(this)) ); } } function _decodeParams(bytes memory params) internal pure returns ( RepayParams memory, PermitSignature memory, address ) { (RepayParams memory repayParams, PermitSignature memory permitSignature, address caller) = abi .decode(params, (RepayParams, PermitSignature, address)); return (repayParams, permitSignature, caller); } }
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":"initiator","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":"tuple","name":"repayParams","internalType":"struct AAutoRepay.RepayParams","components":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"collateralAsset","internalType":"address"},{"type":"address","name":"debtAsset","internalType":"address"},{"type":"uint256","name":"collateralAmount","internalType":"uint256"},{"type":"uint256","name":"debtRepayAmount","internalType":"uint256"},{"type":"uint256","name":"rateMode","internalType":"uint256"},{"type":"bool","name":"useEthPath","internalType":"bool"},{"type":"bool","name":"useATokenAsFrom","internalType":"bool"},{"type":"bool","name":"useATokenAsTo","internalType":"bool"},{"type":"bool","name":"useFlashloan","internalType":"bool"}]},{"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":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isWhitelisted","inputs":[{"type":"address","name":"userAddress","internalType":"address"}]},{"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":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"whitelistAddress","inputs":[{"type":"address","name":"userAddress","internalType":"address"}]}]
Contract Creation Code
0x6101206040523480156200001257600080fd5b506040516200473a3803806200473a8339810160408190526200003591620001fd565b82828282806001600160a01b03166080816001600160a01b031660601b81525050806001600160a01b0316630261bf8b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200009057600080fd5b505afa158015620000a5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000cb9190620001d7565b60601b6001600160601b03191660a052506000620000e8620001d3565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350826001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b1580156200016c57600080fd5b505afa15801562000181573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001a79190620001d7565b6001600160601b0319606091821b811660e05292811b8316610100521b1660c052506200026992505050565b3390565b600060208284031215620001e9578081fd5b8151620001f68162000250565b9392505050565b60008060006060848603121562000212578182fd5b83516200021f8162000250565b6020850151909350620002328162000250565b6040850151909250620002458162000250565b809150509250925092565b6001600160a01b03811681146200026657600080fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6143dd6200035d60003980610fe55280611dce5280611ec2528061244f52806126545280612689528061281d5280612e625280612f535250806108d652806130b75250806104395280611ca85280611ce55280611d4f528061233c52806127075280612d3c5280612d795280612de35250806105c7528061079f52806107ca528061080d5280610ae25280610bfb5280610c265280610d145280610e905280610ebb5280610f555280611128528061120d528061144b528061153852806116b9528061192952508061045d52506143dd6000f3fe608060405234801561001057600080fd5b50600436106101575760003560e01c80638c39745d116100c3578063c57981b51161007c578063c57981b5146102a2578063ccf288c6146102aa578063cdf58cd6146102b2578063d8264920146102c5578063ee76103e146102cd578063f2fde38b146102e057610157565b80638c39745d1461023e5780638da5cb5b14610253578063920f5c841461025b5780639d1211bf1461026e578063b4dcfc7714610276578063baf7fa991461027e57610157565b806338013f021161011557806338013f02146101c75780633af32abf146101cf57806341566585146101ef57806343b0215f14610202578063715018a6146102235780638ab1d6811461022b57610157565b8062ae3bf81461015c578063040141e5146101715780630542975c1461018f578063074b2e43146101975780632abe22a2146101ac57806332e4b286146101bf575b600080fd5b61016f61016a366004613461565b6102f3565b005b610179610437565b6040516101869190613b38565b60405180910390f35b61017961045b565b61019f61047f565b604051610186919061425d565b61016f6101ba366004613684565b610484565b61019f6108ce565b6101796108d4565b6101e26101dd366004613461565b6108f8565b6040516101869190613cec565b6101e26101fd366004613461565b61090b565b610215610210366004613461565b61094d565b60405161018692919061427f565b61016f610966565b6101e2610239366004613461565b6109e5565b610246610a27565b6040516101869190613cd9565b610179610ac6565b6101e261026936600461347d565b610ad5565b610179610f3b565b610179610f53565b61029161028c36600461396b565b610f77565b6040516101869594939291906142c9565b61019f610fbd565b61019f610fc2565b6102916102c036600461396b565b610fc8565b610179610fe3565b61016f6102db3660046139ac565b611007565b61016f6102ee366004613461565b611054565b6102fb61110a565b6000546001600160a01b039081169116146103315760405162461bcd60e51b815260040161032890613fa8565b60405180910390fd5b806001600160a01b031663a9059cbb610348610ac6565b6040516370a0823160e01b81526001600160a01b038516906370a0823190610374903090600401613b38565b60206040518083038186803b15801561038c57600080fd5b505afa1580156103a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103c49190613953565b6040518363ffffffff1660e01b81526004016103e1929190613c72565b602060405180830381600087803b1580156103fb57600080fd5b505af115801561040f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610433919061360f565b5050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b600981565b61048d336108f8565b6104a95760405162461bcd60e51b815260040161032890613eed565b81516104b49061110e565b816101200151156106435760608282336040516020016104d693929190614199565b60408051601f1981840301815260018084528383019092529250606091906020808301908036833701905050905083604001518160008151811061051657fe5b6001600160a01b03929092166020928302919091019091015260408051600180825281830190925260609181602001602082028036833701905050905084608001518160008151811061056557fe5b60209081029190910101526040805160018082528183019092526060918160200160208202803683370190505090506000816000815181106105a357fe5b6020908102919091010152855160405163ab9c4b5d60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163ab9c4b5d91610608913091889188918891908c90600090600401613bf4565b600060405180830381600087803b15801561062257600080fd5b505af1158015610636573d6000803e3d6000fd5b50505050505050506108c3565b61064b6131e7565b61065883604001516111ee565b905060008060018560a00151600281111561066f57fe5b600281111561067a57fe5b1461068a57826101200151610691565b8261010001515b85516040516370a0823160e01b81529192506000916001600160a01b038416916370a08231916106c49190600401613b38565b60206040518083038186803b1580156106dc57600080fd5b505afa1580156106f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107149190613953565b90508086608001511115610728578061072e565b85608001515b92505050836080015181101561076957610763846080015161075d83876060015161129390919063ffffffff16565b906112d4565b60608501525b6080840181905261078b846107833686900386018661362b565b336000611316565b60408401516107c5906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006000611810565b61080b7f0000000000000000000000000000000000000000000000000000000000000000856080015186604001516001600160a01b03166118109092919063ffffffff16565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663573ade81856040015186608001518760a0015188600001516040518563ffffffff1660e01b815260040161086d9493929190613cae565b602060405180830381600087803b15801561088757600080fd5b505af115801561089b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108bf9190613953565b5050505b81516104339061190f565b610bb881565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000610905600183611a17565b92915050565b600061091561110a565b6000546001600160a01b039081169116146109425760405162461bcd60e51b815260040161032890613fa8565b610905600183611a2c565b6003602052600090815260409020805460019091015482565b61096e61110a565b6000546001600160a01b0390811691161461099b5760405162461bcd60e51b815260040161032890613fa8565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60006109ef61110a565b6000546001600160a01b03908116911614610a1c5760405162461bcd60e51b815260040161032890613fa8565b610905600183611a41565b60606000610a356001611a56565b905060608167ffffffffffffffff81118015610a5057600080fd5b50604051908082528060200260200182016040528015610a7a578160200160208202803683370190505b50905060005b82811015610abf57610a93600182611a61565b828281518110610a9f57fe5b6001600160a01b0390921660209283029190910190910152600101610a80565b5091505090565b6000546001600160a01b031690565b6000336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610b1f5760405162461bcd60e51b815260040161032890613dbf565b6001600160a01b0384163014610b475760405162461bcd60e51b815260040161032890613e73565b610b4f613252565b610b576132a6565b6000610b9886868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611a6d92505050565b9250925092508c8c6000818110610bab57fe5b9050602002016020810190610bc09190613461565b6001600160a01b031660408401528a8a600081610bd957fe5b60200291909101356080850152506040830151610c21906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006000611810565b610c677f0000000000000000000000000000000000000000000000000000000000000000846080015185604001516001600160a01b03166118109092919063ffffffff16565b600083604001516001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610c999190613b38565b60206040518083038186803b158015610cb157600080fd5b505afa158015610cc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce99190613953565b604080860151608087015160a08801518851935163573ade8160e01b81529495506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169463573ade8194610d4a94939291600401613cae565b602060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9c9190613953565b50610e2784604001516001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610dd09190613b38565b60206040518083038186803b158015610de857600080fd5b505afa158015610dfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e209190613953565b8290611ab5565b90508360800151811015610e5a57610e54846080015161075d83876060015161129390919063ffffffff16565b60608501525b6080840152610e7c8383838c8c600081610e7057fe5b90506020020135611316565b6040830151610eb6906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006000611810565b610f287f0000000000000000000000000000000000000000000000000000000000000000610f138b8b6000818110610eea57fe5b905060200201358e8e6000818110610efe57fe5b90506020020135611af790919063ffffffff16565b60408601516001600160a01b03169190611810565b5060019c9b505050505050505050505050565b7310f7fc1f91ba351f9c629c5947ad69bd03c05b9681565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806000806060610f876132d4565b610f9288888b611b1c565b8051602082015160408301516060840151608090940151929d919c509a509198509650945050505050565b600a81565b61271081565b6000806000806060610fd86132d4565b610f9288888b6120d1565b7f000000000000000000000000000000000000000000000000000000000000000081565b818110156110275760405162461bcd60e51b81526004016103289061403a565b60408051808201825292835260208084019283523360009081526003909152209151825551600190910155565b61105c61110a565b6000546001600160a01b039081169116146110895760405162461bcd60e51b815260040161032890613fa8565b6001600160a01b0381166110af5760405162461bcd60e51b815260040161032890613df6565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b604051632fe4a15f60e21b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063bf92857c9061115d908590600401613b38565b60c06040518083038186803b15801561117557600080fd5b505afa158015611189573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ad91906139cd565b6001600160a01b0388166000908152600360205260409020549096508610945061043393505050505760405162461bcd60e51b815260040161032890613fdd565b6111f66131e7565b6040516335ea6a7560e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906335ea6a7590611242908590600401613b38565b6101806040518083038186803b15801561125b57600080fd5b505afa15801561126f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109059190613858565b6000826112a257506000610905565b828202828482816112af57fe5b04146112cd5760405162461bcd60e51b815260040161032890613f67565b9392505050565b60006112cd83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506122ab565b600061132585602001516111ee565b60e001519050600061133a86604001516111ee565b60e00151905085604001516001600160a01b031686602001516001600160a01b0316146116605760006113aa8760e0015161137957876020015161137b565b835b88610100015161138f578860400151611391565b835b60808a01516113a09088611af7565b8a60c001516122e2565b6000815181106113b657fe5b6020026020010151905086606001518111156113e45760405162461bcd60e51b815260040161032890614097565b60006113f761271061075d84600a611293565b885190915061141290859061140c8585611af7565b8a6124e6565b6114266001600160a01b038516878361258e565b8760e001516114d7576020880151604051631a4ca37b60e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916369328dec91611483919086903090600401613c8b565b602060405180830381600087803b15801561149d57600080fd5b505af11580156114b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d59190613953565b505b61152a886020015189604001518a60e001516114f7578a602001516114f9565b865b8b610100015161150d578b6040015161150f565b865b60808d01518790611520908c611af7565b8e60c001516125ad565b5087610100015115611659577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166369328dec8960400151856001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016115969190613b38565b60206040518083038186803b1580156115ae57600080fd5b505afa1580156115c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e69190613953565b306040518463ffffffff1660e01b815260040161160593929190613c8b565b602060405180830381600087803b15801561161f57600080fd5b505af1158015611633573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116579190613953565b505b5050611808565b600061168061271061075d600a8a6080015161129390919063ffffffff16565b905060006116a58261169f878b60800151611af790919063ffffffff16565b90611af7565b90506116b7848960000151838a6124e6565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166369328dec8960200151611702888c60800151611af790919063ffffffff16565b306040518463ffffffff1660e01b815260040161172193929190613c8b565b602060405180830381600087803b15801561173b57600080fd5b505af115801561174f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117739190613953565b5061180586856001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016117a49190613b38565b60206040518083038186803b1580156117bc57600080fd5b505afa1580156117d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f49190613953565b6001600160a01b038716919061258e565b50505b505050505050565b8015806118985750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906118469030908690600401613b4c565b60206040518083038186803b15801561185e57600080fd5b505afa158015611872573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118969190613953565b155b6118b45760405162461bcd60e51b81526004016103289061410c565b61190a8363095ea7b360e01b84846040516024016118d3929190613c72565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612943565b505050565b604051632fe4a15f60e21b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063bf92857c9061195e908590600401613b38565b60c06040518083038186803b15801561197657600080fd5b505afa15801561198a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ae91906139cd565b6001600160a01b03881660009081526003602052604090205490965086108015955093506119fb9250505057506001600160a01b0382166000908152600360205260409020600101548111155b6104335760405162461bcd60e51b815260040161032890613d4c565b60006112cd836001600160a01b038416612a28565b60006112cd836001600160a01b038416612a40565b60006112cd836001600160a01b038416612a8a565b600061090582612b50565b60006112cd8383612b54565b611a75613252565b611a7d6132a6565b6000611a87613252565b611a8f6132a6565b600086806020019051810190611aa59190613765565b9199909850909650945050505050565b60006112cd83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612b99565b6000828201838110156112cd5760405162461bcd60e51b815260040161032890613e3c565b611b246132d4565b6000611b41611b3a61271061075d866009611293565b8490611ab5565b9050836001600160a01b0316856001600160a01b03161415611c0d576000611b6886612bc5565b60408051600180825281830190925291925060609190602080830190803683370190505090508681600081518110611b9c57fe5b6001600160a01b039092166020928302919091018201526040805160a08101909152848152908101611bda8761075d87670de0b6b3a7640000611293565b8152602001611bea898886612c41565b8152602001611bfa898686612c41565b81526020018281525093505050506112cd565b60408051600280825260608083018452926020830190803683370190505090508581600081518110611c3b57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600181518110611c6957fe5b6001600160a01b0392909216602092830291909101820152604080516003808252608082019092526060928392839291820183803683370190505090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316896001600160a01b031614158015611d1a57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316886001600160a01b031614155b15611e86578881600081518110611d2d57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000000000000000000000000000000000000000000081600181518110611d7b57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508781600281518110611da957fe5b6001600160a01b03928316602091820292909201015260405163d06ca61f60e01b81527f00000000000000000000000000000000000000000000000000000000000000009091169063d06ca61f90611e079088908590600401614266565b60006040518083038186803b158015611e1f57600080fd5b505afa925050508015611e5457506040513d6000823e601f3d908101601f19168201604052611e51919081019061357a565b60015b611e7e57604080516003808252608082019092529060208201606080368337019050509150611e81565b91505b611ea8565b6040805160038082526080820190925290602082016060803683370190505091505b60405163d06ca61f60e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d06ca61f90611ef99089908990600401614266565b60006040518083038186803b158015611f1157600080fd5b505afa925050508015611f4657506040513d6000823e601f3d908101601f19168201604052611f43919081019061357a565b60015b611f8657604080516002808252606082018352909160208301908036833701905050935082600281518110611f7757fe5b60200260200101519050611fec565b80945084600181518110611f9657fe5b602002602001015184600281518110611fab57fe5b602002602001015111611fd25784600181518110611fc557fe5b6020026020010151611fe8565b83600281518110611fdf57fe5b60200260200101515b9150505b6000611ff78b612bc5565b905060006120048b612bc5565b9050600061203961201985600a86900a611293565b61075d600a85900a6120338d670de0b6b3a7640000611293565b90611293565b90506040518060a0016040528085815260200182815260200161205d8f8e87612c41565b815260200161206d8e8786612c41565b815260200185156120a0578860018151811061208557fe5b60200260200101518614612099578661209b565b895b6120be565b60408051600280825260608201835290916020830190803683375050505b90529d9c50505050505050505050505050565b6120d96132d4565b826001600160a01b0316846001600160a01b031614156121af57600061211061210961271061075d866009611293565b8490611af7565b9050600061211d86612bc5565b6040805160018082528183019092529192506060919060208083019080368337019050509050868160008151811061215157fe5b6001600160a01b039092166020928302919091018201526040805160a0810190915284815290810161218f8561075d89670de0b6b3a7640000611293565b815260200161219f898686612c41565b8152602001611bfa898886612c41565b6060806121bd868686612c9a565b9150915060006122176121f461271061075d6009876000815181106121de57fe5b602002602001015161129390919063ffffffff16565b8460008151811061220157fe5b6020026020010151611af790919063ffffffff16565b9050600061222488612bc5565b9050600061223188612bc5565b9050600061226061224685600a85900a611293565b61075d600a86900a6120338c670de0b6b3a7640000611293565b90506040518060a001604052808581526020018281526020016122848c8787612c41565b81526020016122948b8b86612c41565b815260200195909552509298975050505050505050565b600081836122cc5760405162461bcd60e51b81526004016103289190613cf7565b5060008385816122d857fe5b0495945050505050565b60608082156123bb57604080516003808252608082019092529060208201606080368337019050509050858160008151811061231a57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f00000000000000000000000000000000000000000000000000000000000000008160018151811061236857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050848160028151811061239657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612438565b604080516002808252606082018352909160208301908036833701905050905085816000815181106123e957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050848160018151811061241757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6040516307c0329d60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631f00ca74906124869087908590600401614266565b60006040518083038186803b15801561249e57600080fd5b505afa1580156124b2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526124da919081019061357a565b9150505b949350505050565b6124ef81613057565b1561257357836001600160a01b031663d505accf8430846000015185602001518660400151876060015188608001516040518863ffffffff1660e01b81526004016125409796959493929190613bb3565b600060405180830381600087803b15801561255a57600080fd5b505af115801561256e573d6000803e3d6000fd5b505050505b6125886001600160a01b03851684308561307c565b50505050565b61190a8363a9059cbb60e01b84846040516024016118d3929190613c72565b6000806125b989612bc5565b905060006125c689612bc5565b905060006125d38b61309d565b905060006125e08b61309d565b905060006126246125f5612710610bb8611af7565b61261e61260686600a89900a611293565b61075d61261787600a8c900a611293565b8d90611293565b9061313c565b90508089106126455760405162461bcd60e51b815260040161032890613f24565b61267a6001600160a01b038c167f00000000000000000000000000000000000000000000000000000000000000006000611810565b6126ae6001600160a01b038c167f00000000000000000000000000000000000000000000000000000000000000008b611810565b60608715612786576040805160038082526080820190925290602082016060803683370190505090508b816000815181106126e557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f00000000000000000000000000000000000000000000000000000000000000008160018151811061273357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508a8160028151811061276157fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612803565b60408051600280825260608201835290916020830190803683370190505090508b816000815181106127b457fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508a816001815181106127e257fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b604051634401edf760e11b81526060906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638803dbee9061285a908d908f9087903090429060040161428d565b600060405180830381600087803b15801561287457600080fd5b505af1158015612888573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128b0919081019061357a565b90507fa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb8d8d836000815181106128e257fe5b6020026020010151846001865103815181106128fa57fe5b60200260200101516040516129129493929190613b8a565b60405180910390a18060008151811061292757fe5b6020026020010151975050505050505050979650505050505050565b612955826001600160a01b03166131ae565b6129715760405162461bcd60e51b815260040161032890614162565b60006060836001600160a01b03168360405161298d9190613b1c565b6000604051808303816000865af19150503d80600081146129ca576040519150601f19603f3d011682016040523d82523d6000602084013e6129cf565b606091505b5091509150816129f15760405162461bcd60e51b815260040161032890613eb8565b8051156125885780806020019051810190612a0c919061360f565b6125885760405162461bcd60e51b8152600401610328906140c2565b60009081526001919091016020526040902054151590565b6000612a4c8383612a28565b612a8257508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610905565b506000610905565b60008181526001830160205260408120548015612b465783546000198083019190810190600090879083908110612abd57fe5b9060005260206000200154905080876000018481548110612ada57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080612b0a57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610905565b6000915050610905565b5490565b81546000908210612b775760405162461bcd60e51b815260040161032890613d0a565b826000018281548110612b8657fe5b9060005260206000200154905092915050565b60008184841115612bbd5760405162461bcd60e51b81526004016103289190613cf7565b505050900390565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015612c0057600080fd5b505afa158015612c14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c389190613a16565b60ff1692915050565b600080612c617310f7fc1f91ba351f9c629c5947ad69bd03c05b9661309d565b90506000612c6e8661309d565b9050612c90670de0b6b3a764000061075d84612033600a89900a838b88611293565b9695505050505050565b6040805160028082526060828101909352829182918160200160208202803683370190505090508581600081518110612ccf57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600181518110612cfd57fe5b6001600160a01b0392909216602092830291909101820152604080516003808252608082019092526060928392839291820183803683370190505090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316896001600160a01b031614158015612dae57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316886001600160a01b031614155b15612f1a578881600081518110612dc157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000000000000000000000000000000000000000000081600181518110612e0f57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508781600281518110612e3d57fe5b6001600160a01b0392831660209182029290920101526040516307c0329d60e21b81527f000000000000000000000000000000000000000000000000000000000000000090911690631f00ca7490612e9b908a908590600401614266565b60006040518083038186803b158015612eb357600080fd5b505afa925050508015612ee857506040513d6000823e601f3d908101601f19168201604052612ee5919081019061357a565b60015b612f1257604080516003808252608082019092529060208201606080368337019050509150612f15565b91505b612f3c565b6040805160038082526080820190925290602082016060803683370190505091505b6040516307c0329d60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631f00ca7490612f8a908a908890600401614266565b60006040518083038186803b158015612fa257600080fd5b505afa925050508015612fd757506040513d6000823e601f3d908101601f19168201604052612fd4919081019061357a565b60015b612fe857909450925061304f915050565b80935083600081518110612ff857fe5b60200260200101518360008151811061300d57fe5b602002602001015110801561303757508260008151811061302a57fe5b6020026020010151600014155b613042578385613045565b82825b9650965050505050505b935093915050565b6000816040015160ff16826020015114801561307557506020820151155b1592915050565b612588846323b872dd60e01b8585856040516024016118d393929190613b66565b60405163b3596f0760e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b3596f07906130ec908590600401613b38565b60206040518083038186803b15801561310457600080fd5b505afa158015613118573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109059190613953565b6000821580613149575081155b1561315657506000610905565b81611388198161316257fe5b0483111560405180604001604052806002815260200161068760f31b8152509061319f5760405162461bcd60e51b81526004016103289190613cf7565b50506127109102611388010490565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906124de575050151592915050565b6040518061018001604052806131fb613303565b815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e082018190526101008201819052610120820181905261014082018190526101609091015290565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081019190915290565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001606081525090565b6040518060200160405280600081525090565b803561090581614372565b805161090581614372565b60008083601f84011261333d578182fd5b50813567ffffffffffffffff811115613354578182fd5b602083019150836020808302850101111561336e57600080fd5b9250929050565b80356109058161438a565b80516109058161438a565b600060a0828403121561339c578081fd5b50919050565b600060a082840312156133b3578081fd5b6133bd60a06142ff565b9050815181526020820151602082015260408201516133db81614398565b80604083015250606082015160608201526080820151608082015292915050565b60006020828403121561340d578081fd5b61341760206142ff565b9151825250919050565b80516fffffffffffffffffffffffffffffffff8116811461090557600080fd5b805164ffffffffff8116811461090557600080fd5b805161090581614398565b600060208284031215613472578081fd5b81356112cd81614372565b600080600080600080600080600060a08a8c03121561349a578485fd5b893567ffffffffffffffff808211156134b1578687fd5b6134bd8d838e0161332c565b909b50995060208c01359150808211156134d5578687fd5b6134e18d838e0161332c565b909950975060408c01359150808211156134f9578687fd5b6135058d838e0161332c565b909750955060608c0135915061351a82614372565b90935060808b0135908082111561352f578384fd5b818c0191508c601f830112613542578384fd5b813581811115613550578485fd5b8d6020828501011115613561578485fd5b6020830194508093505050509295985092959850929598565b6000602080838503121561358c578182fd5b825167ffffffffffffffff8111156135a2578283fd5b8301601f810185136135b2578283fd5b80516135c56135c082614326565b6142ff565b81815283810190838501858402850186018910156135e1578687fd5b8694505b838510156136035780518352600194909401939185019185016135e5565b50979650505050505050565b600060208284031215613620578081fd5b81516112cd8161438a565b600060a0828403121561363c578081fd5b61364660a06142ff565b8235815260208301356020820152604083013561366281614398565b6040820152606083810135908201526080928301359281019290925250919050565b6000808284036101e0811215613698578283fd5b610140808212156136a7578384fd5b6136b0816142ff565b91506136bc8686613316565b82526136cb8660208701613316565b60208301526136dd8660408701613316565b6040830152606085013560608301526080850135608083015260a085013560a083015261370d8660c08701613375565b60c083015261371f8660e08701613375565b60e083015261010061373387828801613375565b9083015261012061374687878301613375565b818401525081935061375a8682870161338b565b925050509250929050565b600080600083850361020081121561377b578182fd5b6101408082121561378a578283fd5b613793816142ff565b915061379f8787613321565b82526137ae8760208801613321565b60208301526137c08760408801613321565b6040830152606086015160608301526080860151608083015260a086015160a08301526137f08760c08801613380565b60c08301526138028760e08801613380565b60e083015261010061381688828901613380565b9083015261012061382988888301613380565b818401525081945061383d878288016133a2565b9350505061384f856101e08601613321565b90509250925092565b600061018080838503121561386b578182fd5b613874816142ff565b905061388084846133fc565b815261388f8460208501613421565b60208201526138a18460408501613421565b60408201526138b38460608501613421565b60608201526138c58460808501613421565b60808201526138d78460a08501613421565b60a08201526138e98460c08501613441565b60c08201526138fb8460e08501613321565b60e082015261010061390f85828601613321565b9082015261012061392285858301613321565b9082015261014061393585858301613321565b9082015261016061394885858301613456565b908201529392505050565b600060208284031215613964578081fd5b5051919050565b60008060006060848603121561397f578081fd5b83359250602084013561399181614372565b915060408401356139a181614372565b809150509250925092565b600080604083850312156139be578182fd5b50508035926020909101359150565b60008060008060008060c087890312156139e5578384fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600060208284031215613a27578081fd5b81516112cd81614398565b6001600160a01b03169052565b6000815180845260208085019450808401835b83811015613a775781516001600160a01b031687529582019590820190600101613a52565b509495945050505050565b6000815180845260208085019450808401835b83811015613a7757815187529582019590820190600101613a95565b15159052565b60008151808452613acf816020860160208601614346565b601f01601f19169290920160200192915050565b80358252602081013560208301526040810135613aff81614398565b60ff16604083015260608181013590830152608090810135910152565b60008251613b2e818460208701614346565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b600060018060a01b03808a16835260e06020840152613c1660e084018a613a3f565b8381036040850152613c28818a613a82565b90508381036060850152613c3c8189613a82565b9050818716608085015283810360a0850152613c588187613ab7565b9250505061ffff831660c083015298975050505050505050565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b03948516815260208101939093526040830191909152909116606082015260800190565b6000602082526112cd6020830184613a3f565b901515815260200190565b6000602082526112cd6020830184613ab7565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252604d908201527f55736572206865616c746820666163746f72206d75737420626520696e20726160408201527f6e6765207b66726f6d206d696e4865616c7468466163746f7220746f206d617860608201526c4865616c7468466163746f727d60981b608082015260a00190565b6020808252601b908201527f43414c4c45525f4d5553545f42455f4c454e44494e475f504f4f4c0000000000604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b60208082526025908201527f4f6e6c79207468697320636f6e74726163742063616e2063616c6c20666c6173604082015264343637b0b760d91b606082015260800190565b6020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b60208082526019908201527f43616c6c6572206973206e6f742077686974656c697374656400000000000000604082015260600190565b60208082526023908201527f6d6178416d6f756e74546f5377617020657863656564206d617820736c69707060408201526261676560e81b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252603d908201527f55736572206865616c746820666163746f72206d757374206265206c6573732060408201527f7468616e206d696e4865616c7468466163746f7220666f722075736572000000606082015260800190565b6020808252603c908201527f6d61784865616c7468466163746f722073686f756c64206265206d6f7265206f60408201527f7220657175616c207468616e206d696e4865616c7468466163746f7200000000606082015260800190565b6020808252601190820152700e6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606082015260800190565b6020808252601f908201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604082015260600190565b6000610200820190506141ad828651613a32565b60208501516141bf6020840182613a32565b5060408501516141d26040840182613a32565b50606085015160608301526080850151608083015260a085015160a083015260c085015161420360c0840182613ab1565b5060e085015161421660e0840182613ab1565b506101008086015161422a82850182613ab1565b50506101208086015161423f82850182613ab1565b505061424f610140830185613ae3565b6124de6101e0830184613a32565b90815260200190565b6000838252604060208301526124de6040830184613a3f565b918252602082015260400190565b600086825285602083015260a060408301526142ac60a0830186613a3f565b6001600160a01b0394909416606083015250608001529392505050565b600086825285602083015284604083015283606083015260a060808301526142f460a0830184613a3f565b979650505050505050565b60405181810167ffffffffffffffff8111828210171561431e57600080fd5b604052919050565b600067ffffffffffffffff82111561433c578081fd5b5060209081020190565b60005b83811015614361578181015183820152602001614349565b838111156125885750506000910152565b6001600160a01b038116811461438757600080fd5b50565b801515811461438757600080fd5b60ff8116811461438757600080fdfea26469706673582212202a708b9da79ed30b5921351fffb870d3ad0cb25a3063799c94c389cac57b2a0b64736f6c634300060c0033000000000000000000000000d1088091a174d33412a968fa34cb67131188b332000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f96121000000000000000000000000471ece3750da237f93b8e339c536989b8978a438
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101575760003560e01c80638c39745d116100c3578063c57981b51161007c578063c57981b5146102a2578063ccf288c6146102aa578063cdf58cd6146102b2578063d8264920146102c5578063ee76103e146102cd578063f2fde38b146102e057610157565b80638c39745d1461023e5780638da5cb5b14610253578063920f5c841461025b5780639d1211bf1461026e578063b4dcfc7714610276578063baf7fa991461027e57610157565b806338013f021161011557806338013f02146101c75780633af32abf146101cf57806341566585146101ef57806343b0215f14610202578063715018a6146102235780638ab1d6811461022b57610157565b8062ae3bf81461015c578063040141e5146101715780630542975c1461018f578063074b2e43146101975780632abe22a2146101ac57806332e4b286146101bf575b600080fd5b61016f61016a366004613461565b6102f3565b005b610179610437565b6040516101869190613b38565b60405180910390f35b61017961045b565b61019f61047f565b604051610186919061425d565b61016f6101ba366004613684565b610484565b61019f6108ce565b6101796108d4565b6101e26101dd366004613461565b6108f8565b6040516101869190613cec565b6101e26101fd366004613461565b61090b565b610215610210366004613461565b61094d565b60405161018692919061427f565b61016f610966565b6101e2610239366004613461565b6109e5565b610246610a27565b6040516101869190613cd9565b610179610ac6565b6101e261026936600461347d565b610ad5565b610179610f3b565b610179610f53565b61029161028c36600461396b565b610f77565b6040516101869594939291906142c9565b61019f610fbd565b61019f610fc2565b6102916102c036600461396b565b610fc8565b610179610fe3565b61016f6102db3660046139ac565b611007565b61016f6102ee366004613461565b611054565b6102fb61110a565b6000546001600160a01b039081169116146103315760405162461bcd60e51b815260040161032890613fa8565b60405180910390fd5b806001600160a01b031663a9059cbb610348610ac6565b6040516370a0823160e01b81526001600160a01b038516906370a0823190610374903090600401613b38565b60206040518083038186803b15801561038c57600080fd5b505afa1580156103a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103c49190613953565b6040518363ffffffff1660e01b81526004016103e1929190613c72565b602060405180830381600087803b1580156103fb57600080fd5b505af115801561040f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610433919061360f565b5050565b7f000000000000000000000000471ece3750da237f93b8e339c536989b8978a43881565b7f000000000000000000000000d1088091a174d33412a968fa34cb67131188b33281565b600981565b61048d336108f8565b6104a95760405162461bcd60e51b815260040161032890613eed565b81516104b49061110e565b816101200151156106435760608282336040516020016104d693929190614199565b60408051601f1981840301815260018084528383019092529250606091906020808301908036833701905050905083604001518160008151811061051657fe5b6001600160a01b03929092166020928302919091019091015260408051600180825281830190925260609181602001602082028036833701905050905084608001518160008151811061056557fe5b60209081029190910101526040805160018082528183019092526060918160200160208202803683370190505090506000816000815181106105a357fe5b6020908102919091010152855160405163ab9c4b5d60e01b81526001600160a01b037f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f670169163ab9c4b5d91610608913091889188918891908c90600090600401613bf4565b600060405180830381600087803b15801561062257600080fd5b505af1158015610636573d6000803e3d6000fd5b50505050505050506108c3565b61064b6131e7565b61065883604001516111ee565b905060008060018560a00151600281111561066f57fe5b600281111561067a57fe5b1461068a57826101200151610691565b8261010001515b85516040516370a0823160e01b81529192506000916001600160a01b038416916370a08231916106c49190600401613b38565b60206040518083038186803b1580156106dc57600080fd5b505afa1580156106f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107149190613953565b90508086608001511115610728578061072e565b85608001515b92505050836080015181101561076957610763846080015161075d83876060015161129390919063ffffffff16565b906112d4565b60608501525b6080840181905261078b846107833686900386018661362b565b336000611316565b60408401516107c5906001600160a01b03167f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f6706000611810565b61080b7f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f670856080015186604001516001600160a01b03166118109092919063ffffffff16565b7f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f6706001600160a01b031663573ade81856040015186608001518760a0015188600001516040518563ffffffff1660e01b815260040161086d9493929190613cae565b602060405180830381600087803b15801561088757600080fd5b505af115801561089b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108bf9190613953565b5050505b81516104339061190f565b610bb881565b7f000000000000000000000000568547688121aa69bdeb8aeb662c321c5d7b98d081565b6000610905600183611a17565b92915050565b600061091561110a565b6000546001600160a01b039081169116146109425760405162461bcd60e51b815260040161032890613fa8565b610905600183611a2c565b6003602052600090815260409020805460019091015482565b61096e61110a565b6000546001600160a01b0390811691161461099b5760405162461bcd60e51b815260040161032890613fa8565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60006109ef61110a565b6000546001600160a01b03908116911614610a1c5760405162461bcd60e51b815260040161032890613fa8565b610905600183611a41565b60606000610a356001611a56565b905060608167ffffffffffffffff81118015610a5057600080fd5b50604051908082528060200260200182016040528015610a7a578160200160208202803683370190505b50905060005b82811015610abf57610a93600182611a61565b828281518110610a9f57fe5b6001600160a01b0390921660209283029190910190910152600101610a80565b5091505090565b6000546001600160a01b031690565b6000336001600160a01b037f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f6701614610b1f5760405162461bcd60e51b815260040161032890613dbf565b6001600160a01b0384163014610b475760405162461bcd60e51b815260040161032890613e73565b610b4f613252565b610b576132a6565b6000610b9886868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611a6d92505050565b9250925092508c8c6000818110610bab57fe5b9050602002016020810190610bc09190613461565b6001600160a01b031660408401528a8a600081610bd957fe5b60200291909101356080850152506040830151610c21906001600160a01b03167f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f6706000611810565b610c677f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f670846080015185604001516001600160a01b03166118109092919063ffffffff16565b600083604001516001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610c999190613b38565b60206040518083038186803b158015610cb157600080fd5b505afa158015610cc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce99190613953565b604080860151608087015160a08801518851935163573ade8160e01b81529495506001600160a01b037f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f670169463573ade8194610d4a94939291600401613cae565b602060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9c9190613953565b50610e2784604001516001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610dd09190613b38565b60206040518083038186803b158015610de857600080fd5b505afa158015610dfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e209190613953565b8290611ab5565b90508360800151811015610e5a57610e54846080015161075d83876060015161129390919063ffffffff16565b60608501525b6080840152610e7c8383838c8c600081610e7057fe5b90506020020135611316565b6040830151610eb6906001600160a01b03167f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f6706000611810565b610f287f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f670610f138b8b6000818110610eea57fe5b905060200201358e8e6000818110610efe57fe5b90506020020135611af790919063ffffffff16565b60408601516001600160a01b03169190611810565b5060019c9b505050505050505050505050565b7310f7fc1f91ba351f9c629c5947ad69bd03c05b9681565b7f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f67081565b6000806000806060610f876132d4565b610f9288888b611b1c565b8051602082015160408301516060840151608090940151929d919c509a509198509650945050505050565b600a81565b61271081565b6000806000806060610fd86132d4565b610f9288888b6120d1565b7f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f9612181565b818110156110275760405162461bcd60e51b81526004016103289061403a565b60408051808201825292835260208084019283523360009081526003909152209151825551600190910155565b61105c61110a565b6000546001600160a01b039081169116146110895760405162461bcd60e51b815260040161032890613fa8565b6001600160a01b0381166110af5760405162461bcd60e51b815260040161032890613df6565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b604051632fe4a15f60e21b81526000906001600160a01b037f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f670169063bf92857c9061115d908590600401613b38565b60c06040518083038186803b15801561117557600080fd5b505afa158015611189573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ad91906139cd565b6001600160a01b0388166000908152600360205260409020549096508610945061043393505050505760405162461bcd60e51b815260040161032890613fdd565b6111f66131e7565b6040516335ea6a7560e01b81526001600160a01b037f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f67016906335ea6a7590611242908590600401613b38565b6101806040518083038186803b15801561125b57600080fd5b505afa15801561126f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109059190613858565b6000826112a257506000610905565b828202828482816112af57fe5b04146112cd5760405162461bcd60e51b815260040161032890613f67565b9392505050565b60006112cd83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506122ab565b600061132585602001516111ee565b60e001519050600061133a86604001516111ee565b60e00151905085604001516001600160a01b031686602001516001600160a01b0316146116605760006113aa8760e0015161137957876020015161137b565b835b88610100015161138f578860400151611391565b835b60808a01516113a09088611af7565b8a60c001516122e2565b6000815181106113b657fe5b6020026020010151905086606001518111156113e45760405162461bcd60e51b815260040161032890614097565b60006113f761271061075d84600a611293565b885190915061141290859061140c8585611af7565b8a6124e6565b6114266001600160a01b038516878361258e565b8760e001516114d7576020880151604051631a4ca37b60e21b81526001600160a01b037f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f67016916369328dec91611483919086903090600401613c8b565b602060405180830381600087803b15801561149d57600080fd5b505af11580156114b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d59190613953565b505b61152a886020015189604001518a60e001516114f7578a602001516114f9565b865b8b610100015161150d578b6040015161150f565b865b60808d01518790611520908c611af7565b8e60c001516125ad565b5087610100015115611659577f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f6706001600160a01b03166369328dec8960400151856001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016115969190613b38565b60206040518083038186803b1580156115ae57600080fd5b505afa1580156115c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e69190613953565b306040518463ffffffff1660e01b815260040161160593929190613c8b565b602060405180830381600087803b15801561161f57600080fd5b505af1158015611633573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116579190613953565b505b5050611808565b600061168061271061075d600a8a6080015161129390919063ffffffff16565b905060006116a58261169f878b60800151611af790919063ffffffff16565b90611af7565b90506116b7848960000151838a6124e6565b7f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f6706001600160a01b03166369328dec8960200151611702888c60800151611af790919063ffffffff16565b306040518463ffffffff1660e01b815260040161172193929190613c8b565b602060405180830381600087803b15801561173b57600080fd5b505af115801561174f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117739190613953565b5061180586856001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016117a49190613b38565b60206040518083038186803b1580156117bc57600080fd5b505afa1580156117d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f49190613953565b6001600160a01b038716919061258e565b50505b505050505050565b8015806118985750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906118469030908690600401613b4c565b60206040518083038186803b15801561185e57600080fd5b505afa158015611872573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118969190613953565b155b6118b45760405162461bcd60e51b81526004016103289061410c565b61190a8363095ea7b360e01b84846040516024016118d3929190613c72565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612943565b505050565b604051632fe4a15f60e21b81526000906001600160a01b037f000000000000000000000000970b12522ca9b4054807a2c5b736149a5be6f670169063bf92857c9061195e908590600401613b38565b60c06040518083038186803b15801561197657600080fd5b505afa15801561198a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ae91906139cd565b6001600160a01b03881660009081526003602052604090205490965086108015955093506119fb9250505057506001600160a01b0382166000908152600360205260409020600101548111155b6104335760405162461bcd60e51b815260040161032890613d4c565b60006112cd836001600160a01b038416612a28565b60006112cd836001600160a01b038416612a40565b60006112cd836001600160a01b038416612a8a565b600061090582612b50565b60006112cd8383612b54565b611a75613252565b611a7d6132a6565b6000611a87613252565b611a8f6132a6565b600086806020019051810190611aa59190613765565b9199909850909650945050505050565b60006112cd83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612b99565b6000828201838110156112cd5760405162461bcd60e51b815260040161032890613e3c565b611b246132d4565b6000611b41611b3a61271061075d866009611293565b8490611ab5565b9050836001600160a01b0316856001600160a01b03161415611c0d576000611b6886612bc5565b60408051600180825281830190925291925060609190602080830190803683370190505090508681600081518110611b9c57fe5b6001600160a01b039092166020928302919091018201526040805160a08101909152848152908101611bda8761075d87670de0b6b3a7640000611293565b8152602001611bea898886612c41565b8152602001611bfa898686612c41565b81526020018281525093505050506112cd565b60408051600280825260608083018452926020830190803683370190505090508581600081518110611c3b57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600181518110611c6957fe5b6001600160a01b0392909216602092830291909101820152604080516003808252608082019092526060928392839291820183803683370190505090507f000000000000000000000000471ece3750da237f93b8e339c536989b8978a4386001600160a01b0316896001600160a01b031614158015611d1a57507f000000000000000000000000471ece3750da237f93b8e339c536989b8978a4386001600160a01b0316886001600160a01b031614155b15611e86578881600081518110611d2d57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000471ece3750da237f93b8e339c536989b8978a43881600181518110611d7b57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508781600281518110611da957fe5b6001600160a01b03928316602091820292909201015260405163d06ca61f60e01b81527f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961219091169063d06ca61f90611e079088908590600401614266565b60006040518083038186803b158015611e1f57600080fd5b505afa925050508015611e5457506040513d6000823e601f3d908101601f19168201604052611e51919081019061357a565b60015b611e7e57604080516003808252608082019092529060208201606080368337019050509150611e81565b91505b611ea8565b6040805160038082526080820190925290602082016060803683370190505091505b60405163d06ca61f60e01b81526000906001600160a01b037f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f96121169063d06ca61f90611ef99089908990600401614266565b60006040518083038186803b158015611f1157600080fd5b505afa925050508015611f4657506040513d6000823e601f3d908101601f19168201604052611f43919081019061357a565b60015b611f8657604080516002808252606082018352909160208301908036833701905050935082600281518110611f7757fe5b60200260200101519050611fec565b80945084600181518110611f9657fe5b602002602001015184600281518110611fab57fe5b602002602001015111611fd25784600181518110611fc557fe5b6020026020010151611fe8565b83600281518110611fdf57fe5b60200260200101515b9150505b6000611ff78b612bc5565b905060006120048b612bc5565b9050600061203961201985600a86900a611293565b61075d600a85900a6120338d670de0b6b3a7640000611293565b90611293565b90506040518060a0016040528085815260200182815260200161205d8f8e87612c41565b815260200161206d8e8786612c41565b815260200185156120a0578860018151811061208557fe5b60200260200101518614612099578661209b565b895b6120be565b60408051600280825260608201835290916020830190803683375050505b90529d9c50505050505050505050505050565b6120d96132d4565b826001600160a01b0316846001600160a01b031614156121af57600061211061210961271061075d866009611293565b8490611af7565b9050600061211d86612bc5565b6040805160018082528183019092529192506060919060208083019080368337019050509050868160008151811061215157fe5b6001600160a01b039092166020928302919091018201526040805160a0810190915284815290810161218f8561075d89670de0b6b3a7640000611293565b815260200161219f898686612c41565b8152602001611bfa898886612c41565b6060806121bd868686612c9a565b9150915060006122176121f461271061075d6009876000815181106121de57fe5b602002602001015161129390919063ffffffff16565b8460008151811061220157fe5b6020026020010151611af790919063ffffffff16565b9050600061222488612bc5565b9050600061223188612bc5565b9050600061226061224685600a85900a611293565b61075d600a86900a6120338c670de0b6b3a7640000611293565b90506040518060a001604052808581526020018281526020016122848c8787612c41565b81526020016122948b8b86612c41565b815260200195909552509298975050505050505050565b600081836122cc5760405162461bcd60e51b81526004016103289190613cf7565b5060008385816122d857fe5b0495945050505050565b60608082156123bb57604080516003808252608082019092529060208201606080368337019050509050858160008151811061231a57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000471ece3750da237f93b8e339c536989b8978a4388160018151811061236857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050848160028151811061239657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612438565b604080516002808252606082018352909160208301908036833701905050905085816000815181106123e957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050848160018151811061241757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6040516307c0329d60e21b81526001600160a01b037f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961211690631f00ca74906124869087908590600401614266565b60006040518083038186803b15801561249e57600080fd5b505afa1580156124b2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526124da919081019061357a565b9150505b949350505050565b6124ef81613057565b1561257357836001600160a01b031663d505accf8430846000015185602001518660400151876060015188608001516040518863ffffffff1660e01b81526004016125409796959493929190613bb3565b600060405180830381600087803b15801561255a57600080fd5b505af115801561256e573d6000803e3d6000fd5b505050505b6125886001600160a01b03851684308561307c565b50505050565b61190a8363a9059cbb60e01b84846040516024016118d3929190613c72565b6000806125b989612bc5565b905060006125c689612bc5565b905060006125d38b61309d565b905060006125e08b61309d565b905060006126246125f5612710610bb8611af7565b61261e61260686600a89900a611293565b61075d61261787600a8c900a611293565b8d90611293565b9061313c565b90508089106126455760405162461bcd60e51b815260040161032890613f24565b61267a6001600160a01b038c167f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961216000611810565b6126ae6001600160a01b038c167f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961218b611810565b60608715612786576040805160038082526080820190925290602082016060803683370190505090508b816000815181106126e557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000471ece3750da237f93b8e339c536989b8978a4388160018151811061273357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508a8160028151811061276157fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612803565b60408051600280825260608201835290916020830190803683370190505090508b816000815181106127b457fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508a816001815181106127e257fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b604051634401edf760e11b81526060906001600160a01b037f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961211690638803dbee9061285a908d908f9087903090429060040161428d565b600060405180830381600087803b15801561287457600080fd5b505af1158015612888573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128b0919081019061357a565b90507fa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb8d8d836000815181106128e257fe5b6020026020010151846001865103815181106128fa57fe5b60200260200101516040516129129493929190613b8a565b60405180910390a18060008151811061292757fe5b6020026020010151975050505050505050979650505050505050565b612955826001600160a01b03166131ae565b6129715760405162461bcd60e51b815260040161032890614162565b60006060836001600160a01b03168360405161298d9190613b1c565b6000604051808303816000865af19150503d80600081146129ca576040519150601f19603f3d011682016040523d82523d6000602084013e6129cf565b606091505b5091509150816129f15760405162461bcd60e51b815260040161032890613eb8565b8051156125885780806020019051810190612a0c919061360f565b6125885760405162461bcd60e51b8152600401610328906140c2565b60009081526001919091016020526040902054151590565b6000612a4c8383612a28565b612a8257508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610905565b506000610905565b60008181526001830160205260408120548015612b465783546000198083019190810190600090879083908110612abd57fe5b9060005260206000200154905080876000018481548110612ada57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080612b0a57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610905565b6000915050610905565b5490565b81546000908210612b775760405162461bcd60e51b815260040161032890613d0a565b826000018281548110612b8657fe5b9060005260206000200154905092915050565b60008184841115612bbd5760405162461bcd60e51b81526004016103289190613cf7565b505050900390565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015612c0057600080fd5b505afa158015612c14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c389190613a16565b60ff1692915050565b600080612c617310f7fc1f91ba351f9c629c5947ad69bd03c05b9661309d565b90506000612c6e8661309d565b9050612c90670de0b6b3a764000061075d84612033600a89900a838b88611293565b9695505050505050565b6040805160028082526060828101909352829182918160200160208202803683370190505090508581600081518110612ccf57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600181518110612cfd57fe5b6001600160a01b0392909216602092830291909101820152604080516003808252608082019092526060928392839291820183803683370190505090507f000000000000000000000000471ece3750da237f93b8e339c536989b8978a4386001600160a01b0316896001600160a01b031614158015612dae57507f000000000000000000000000471ece3750da237f93b8e339c536989b8978a4386001600160a01b0316886001600160a01b031614155b15612f1a578881600081518110612dc157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000471ece3750da237f93b8e339c536989b8978a43881600181518110612e0f57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508781600281518110612e3d57fe5b6001600160a01b0392831660209182029290920101526040516307c0329d60e21b81527f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f9612190911690631f00ca7490612e9b908a908590600401614266565b60006040518083038186803b158015612eb357600080fd5b505afa925050508015612ee857506040513d6000823e601f3d908101601f19168201604052612ee5919081019061357a565b60015b612f1257604080516003808252608082019092529060208201606080368337019050509150612f15565b91505b612f3c565b6040805160038082526080820190925290602082016060803683370190505091505b6040516307c0329d60e21b81526001600160a01b037f000000000000000000000000e3d8bd6aed4f159bc8000a9cd47cffdb95f961211690631f00ca7490612f8a908a908890600401614266565b60006040518083038186803b158015612fa257600080fd5b505afa925050508015612fd757506040513d6000823e601f3d908101601f19168201604052612fd4919081019061357a565b60015b612fe857909450925061304f915050565b80935083600081518110612ff857fe5b60200260200101518360008151811061300d57fe5b602002602001015110801561303757508260008151811061302a57fe5b6020026020010151600014155b613042578385613045565b82825b9650965050505050505b935093915050565b6000816040015160ff16826020015114801561307557506020820151155b1592915050565b612588846323b872dd60e01b8585856040516024016118d393929190613b66565b60405163b3596f0760e01b81526000906001600160a01b037f000000000000000000000000568547688121aa69bdeb8aeb662c321c5d7b98d0169063b3596f07906130ec908590600401613b38565b60206040518083038186803b15801561310457600080fd5b505afa158015613118573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109059190613953565b6000821580613149575081155b1561315657506000610905565b81611388198161316257fe5b0483111560405180604001604052806002815260200161068760f31b8152509061319f5760405162461bcd60e51b81526004016103289190613cf7565b50506127109102611388010490565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906124de575050151592915050565b6040518061018001604052806131fb613303565b815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e082018190526101008201819052610120820181905261014082018190526101609091015290565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081019190915290565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001606081525090565b6040518060200160405280600081525090565b803561090581614372565b805161090581614372565b60008083601f84011261333d578182fd5b50813567ffffffffffffffff811115613354578182fd5b602083019150836020808302850101111561336e57600080fd5b9250929050565b80356109058161438a565b80516109058161438a565b600060a0828403121561339c578081fd5b50919050565b600060a082840312156133b3578081fd5b6133bd60a06142ff565b9050815181526020820151602082015260408201516133db81614398565b80604083015250606082015160608201526080820151608082015292915050565b60006020828403121561340d578081fd5b61341760206142ff565b9151825250919050565b80516fffffffffffffffffffffffffffffffff8116811461090557600080fd5b805164ffffffffff8116811461090557600080fd5b805161090581614398565b600060208284031215613472578081fd5b81356112cd81614372565b600080600080600080600080600060a08a8c03121561349a578485fd5b893567ffffffffffffffff808211156134b1578687fd5b6134bd8d838e0161332c565b909b50995060208c01359150808211156134d5578687fd5b6134e18d838e0161332c565b909950975060408c01359150808211156134f9578687fd5b6135058d838e0161332c565b909750955060608c0135915061351a82614372565b90935060808b0135908082111561352f578384fd5b818c0191508c601f830112613542578384fd5b813581811115613550578485fd5b8d6020828501011115613561578485fd5b6020830194508093505050509295985092959850929598565b6000602080838503121561358c578182fd5b825167ffffffffffffffff8111156135a2578283fd5b8301601f810185136135b2578283fd5b80516135c56135c082614326565b6142ff565b81815283810190838501858402850186018910156135e1578687fd5b8694505b838510156136035780518352600194909401939185019185016135e5565b50979650505050505050565b600060208284031215613620578081fd5b81516112cd8161438a565b600060a0828403121561363c578081fd5b61364660a06142ff565b8235815260208301356020820152604083013561366281614398565b6040820152606083810135908201526080928301359281019290925250919050565b6000808284036101e0811215613698578283fd5b610140808212156136a7578384fd5b6136b0816142ff565b91506136bc8686613316565b82526136cb8660208701613316565b60208301526136dd8660408701613316565b6040830152606085013560608301526080850135608083015260a085013560a083015261370d8660c08701613375565b60c083015261371f8660e08701613375565b60e083015261010061373387828801613375565b9083015261012061374687878301613375565b818401525081935061375a8682870161338b565b925050509250929050565b600080600083850361020081121561377b578182fd5b6101408082121561378a578283fd5b613793816142ff565b915061379f8787613321565b82526137ae8760208801613321565b60208301526137c08760408801613321565b6040830152606086015160608301526080860151608083015260a086015160a08301526137f08760c08801613380565b60c08301526138028760e08801613380565b60e083015261010061381688828901613380565b9083015261012061382988888301613380565b818401525081945061383d878288016133a2565b9350505061384f856101e08601613321565b90509250925092565b600061018080838503121561386b578182fd5b613874816142ff565b905061388084846133fc565b815261388f8460208501613421565b60208201526138a18460408501613421565b60408201526138b38460608501613421565b60608201526138c58460808501613421565b60808201526138d78460a08501613421565b60a08201526138e98460c08501613441565b60c08201526138fb8460e08501613321565b60e082015261010061390f85828601613321565b9082015261012061392285858301613321565b9082015261014061393585858301613321565b9082015261016061394885858301613456565b908201529392505050565b600060208284031215613964578081fd5b5051919050565b60008060006060848603121561397f578081fd5b83359250602084013561399181614372565b915060408401356139a181614372565b809150509250925092565b600080604083850312156139be578182fd5b50508035926020909101359150565b60008060008060008060c087890312156139e5578384fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b600060208284031215613a27578081fd5b81516112cd81614398565b6001600160a01b03169052565b6000815180845260208085019450808401835b83811015613a775781516001600160a01b031687529582019590820190600101613a52565b509495945050505050565b6000815180845260208085019450808401835b83811015613a7757815187529582019590820190600101613a95565b15159052565b60008151808452613acf816020860160208601614346565b601f01601f19169290920160200192915050565b80358252602081013560208301526040810135613aff81614398565b60ff16604083015260608181013590830152608090810135910152565b60008251613b2e818460208701614346565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b600060018060a01b03808a16835260e06020840152613c1660e084018a613a3f565b8381036040850152613c28818a613a82565b90508381036060850152613c3c8189613a82565b9050818716608085015283810360a0850152613c588187613ab7565b9250505061ffff831660c083015298975050505050505050565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b03948516815260208101939093526040830191909152909116606082015260800190565b6000602082526112cd6020830184613a3f565b901515815260200190565b6000602082526112cd6020830184613ab7565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252604d908201527f55736572206865616c746820666163746f72206d75737420626520696e20726160408201527f6e6765207b66726f6d206d696e4865616c7468466163746f7220746f206d617860608201526c4865616c7468466163746f727d60981b608082015260a00190565b6020808252601b908201527f43414c4c45525f4d5553545f42455f4c454e44494e475f504f4f4c0000000000604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b60208082526025908201527f4f6e6c79207468697320636f6e74726163742063616e2063616c6c20666c6173604082015264343637b0b760d91b606082015260800190565b6020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b60208082526019908201527f43616c6c6572206973206e6f742077686974656c697374656400000000000000604082015260600190565b60208082526023908201527f6d6178416d6f756e74546f5377617020657863656564206d617820736c69707060408201526261676560e81b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252603d908201527f55736572206865616c746820666163746f72206d757374206265206c6573732060408201527f7468616e206d696e4865616c7468466163746f7220666f722075736572000000606082015260800190565b6020808252603c908201527f6d61784865616c7468466163746f722073686f756c64206265206d6f7265206f60408201527f7220657175616c207468616e206d696e4865616c7468466163746f7200000000606082015260800190565b6020808252601190820152700e6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606082015260800190565b6020808252601f908201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604082015260600190565b6000610200820190506141ad828651613a32565b60208501516141bf6020840182613a32565b5060408501516141d26040840182613a32565b50606085015160608301526080850151608083015260a085015160a083015260c085015161420360c0840182613ab1565b5060e085015161421660e0840182613ab1565b506101008086015161422a82850182613ab1565b50506101208086015161423f82850182613ab1565b505061424f610140830185613ae3565b6124de6101e0830184613a32565b90815260200190565b6000838252604060208301526124de6040830184613a3f565b918252602082015260400190565b600086825285602083015260a060408301526142ac60a0830186613a3f565b6001600160a01b0394909416606083015250608001529392505050565b600086825285602083015284604083015283606083015260a060808301526142f460a0830184613a3f565b979650505050505050565b60405181810167ffffffffffffffff8111828210171561431e57600080fd5b604052919050565b600067ffffffffffffffff82111561433c578081fd5b5060209081020190565b60005b83811015614361578181015183820152602001614349565b838111156125885750506000910152565b6001600160a01b038116811461438757600080fd5b50565b801515811461438757600080fd5b60ff8116811461438757600080fdfea26469706673582212202a708b9da79ed30b5921351fffb870d3ad0cb25a3063799c94c389cac57b2a0b64736f6c634300060c0033