ETH Price: $3,486.00 (+5.30%)

Token

FraxlendV1 - Frax Share/Frax - Variable Time-Weighted Interest Rate - 5 (FraxlendV1 - FXS/FRAX)
 

Overview

Max Total Supply

1,204,605.678462557298970926 FraxlendV1 - FXS/FRAX

Holders

79 (0.00%)

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Filtered by Token Holder
Frax Finance: FraxlendV1 - FXS/FRAX Token
Balance
17,261.498288116372548001 FraxlendV1 - FXS/FRAX

Value
$0.00
0xdbe88dbac39263c47629ebba02b3ef4cf0752a72
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x794F6B13...6910207Ff
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
FraxlendPair

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 725 runs

Other Settings:
default evmVersion
File 1 of 21 : FraxlendPair.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.16;

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ========================== FraxlendPair ============================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance

// Primary Author
// Drake Evans: https://github.com/DrakeEvans

// Reviewers
// Dennis: https://github.com/denett
// Sam Kazemian: https://github.com/samkazemian
// Travis Moore: https://github.com/FortisFortuna
// Jack Corddry: https://github.com/corddry
// Rich Gee: https://github.com/zer0blockchain

// ====================================================================

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "./FraxlendPairConstants.sol";
import "./FraxlendPairCore.sol";
import "./libraries/VaultAccount.sol";
import "./libraries/SafeERC20.sol";
import "./interfaces/IERC4626.sol";
import "./interfaces/IFraxlendWhitelist.sol";
import "./interfaces/IRateCalculator.sol";
import "./interfaces/ISwapper.sol";

contract FraxlendPair is IERC20Metadata, FraxlendPairCore {
    using VaultAccountingLibrary for VaultAccount;
    using SafeERC20 for IERC20;

    constructor(
        bytes memory _configData,
        bytes memory _immutables,
        uint256 _maxLTV,
        uint256 _liquidationFee,
        uint256 _maturityDate,
        uint256 _penaltyRate,
        bool _isBorrowerWhitelistActive,
        bool _isLenderWhitelistActive
    )
        FraxlendPairCore(
            _configData,
            _immutables,
            _maxLTV,
            _liquidationFee,
            _maturityDate,
            _penaltyRate,
            _isBorrowerWhitelistActive,
            _isLenderWhitelistActive
        )
        ERC20("", "")
        Ownable()
        Pausable()
    {}

    // ============================================================================================
    // ERC20 Metadata
    // ============================================================================================

    function name() public view override(ERC20, IERC20Metadata) returns (string memory) {
        return nameOfContract;
    }

    function symbol() public view override(ERC20, IERC20Metadata) returns (string memory) {
        // prettier-ignore
        // solhint-disable-next-line max-line-length
        return string(abi.encodePacked("FraxlendV1 - ", collateralContract.safeSymbol(), "/", assetContract.safeSymbol()));
    }

    function decimals() public pure override(ERC20, IERC20Metadata) returns (uint8) {
        return 18;
    }

    // totalSupply for fToken ERC20 compatibility
    function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
        return totalAsset.shares;
    }

    // ============================================================================================
    // Functions: Helpers
    // ============================================================================================

    function asset() external view returns (address) {
        return address(assetContract);
    }

    function getConstants()
        external
        pure
        returns (
            uint256 _LTV_PRECISION,
            uint256 _LIQ_PRECISION,
            uint256 _UTIL_PREC,
            uint256 _FEE_PRECISION,
            uint256 _EXCHANGE_PRECISION,
            uint64 _DEFAULT_INT,
            uint16 _DEFAULT_PROTOCOL_FEE,
            uint256 _MAX_PROTOCOL_FEE
        )
    {
        _LTV_PRECISION = LTV_PRECISION;
        _LIQ_PRECISION = LIQ_PRECISION;
        _UTIL_PREC = UTIL_PREC;
        _FEE_PRECISION = FEE_PRECISION;
        _EXCHANGE_PRECISION = EXCHANGE_PRECISION;
        _DEFAULT_INT = DEFAULT_INT;
        _DEFAULT_PROTOCOL_FEE = DEFAULT_PROTOCOL_FEE;
        _MAX_PROTOCOL_FEE = MAX_PROTOCOL_FEE;
    }

    /// @notice The ```getImmutableAddressBool``` function gets all the address and bool configs
    /// @return _assetContract Address of asset
    /// @return _collateralContract Address of collateral
    /// @return _oracleMultiply Address of oracle numerator
    /// @return _oracleDivide Address of oracle denominator
    /// @return _rateContract Address of rate contract
    /// @return _DEPLOYER_CONTRACT Address of deployer contract
    /// @return _COMPTROLLER_ADDRESS Address of comptroller
    /// @return _FRAXLEND_WHITELIST Address of whitelist
    /// @return _borrowerWhitelistActive Boolean is borrower whitelist active
    /// @return _lenderWhitelistActive Boolean is lender whitelist active
    function getImmutableAddressBool()
        external
        view
        returns (
            address _assetContract,
            address _collateralContract,
            address _oracleMultiply,
            address _oracleDivide,
            address _rateContract,
            address _DEPLOYER_CONTRACT,
            address _COMPTROLLER_ADDRESS,
            address _FRAXLEND_WHITELIST,
            bool _borrowerWhitelistActive,
            bool _lenderWhitelistActive
        )
    {
        _assetContract = address(assetContract);
        _collateralContract = address(collateralContract);
        _oracleMultiply = oracleMultiply;
        _oracleDivide = oracleDivide;
        _rateContract = address(rateContract);
        _DEPLOYER_CONTRACT = DEPLOYER_ADDRESS;
        _COMPTROLLER_ADDRESS = COMPTROLLER_ADDRESS;
        _FRAXLEND_WHITELIST = FRAXLEND_WHITELIST_ADDRESS;
        _borrowerWhitelistActive = borrowerWhitelistActive;
        _lenderWhitelistActive = lenderWhitelistActive;
    }

    /// @notice The ```getImmutableUint256``` function gets all uint256 config values
    /// @return _oracleNormalization Oracle normalization factor
    /// @return _maxLTV Maximum LTV
    /// @return _cleanLiquidationFee Clean Liquidation Fee
    /// @return _maturityDate Maturity Date
    /// @return _penaltyRate Penalty Rate
    function getImmutableUint256()
        external
        view
        returns (
            uint256 _oracleNormalization,
            uint256 _maxLTV,
            uint256 _cleanLiquidationFee,
            uint256 _maturityDate,
            uint256 _penaltyRate
        )
    {
        _oracleNormalization = oracleNormalization;
        _maxLTV = maxLTV;
        _cleanLiquidationFee = cleanLiquidationFee;
        _maturityDate = maturityDate;
        _penaltyRate = penaltyRate;
    }

    /// @notice The ```getUserSnapshot``` function gets user level accounting data
    /// @param _address The user address
    /// @return _userAssetShares The user fToken balance
    /// @return _userBorrowShares The user borrow shares
    /// @return _userCollateralBalance The user collateral balance
    function getUserSnapshot(address _address)
        external
        view
        returns (
            uint256 _userAssetShares,
            uint256 _userBorrowShares,
            uint256 _userCollateralBalance
        )
    {
        _userAssetShares = balanceOf(_address);
        _userBorrowShares = userBorrowShares[_address];
        _userCollateralBalance = userCollateralBalance[_address];
    }

    /// @notice The ```getPairAccounting``` function gets all pair level accounting numbers
    /// @return _totalAssetAmount Total assets deposited and interest accrued, total claims
    /// @return _totalAssetShares Total fTokens
    /// @return _totalBorrowAmount Total borrows
    /// @return _totalBorrowShares Total borrow shares
    /// @return _totalCollateral Total collateral
    function getPairAccounting()
        external
        view
        returns (
            uint128 _totalAssetAmount,
            uint128 _totalAssetShares,
            uint128 _totalBorrowAmount,
            uint128 _totalBorrowShares,
            uint256 _totalCollateral
        )
    {
        VaultAccount memory _totalAsset = totalAsset;
        _totalAssetAmount = _totalAsset.amount;
        _totalAssetShares = _totalAsset.shares;

        VaultAccount memory _totalBorrow = totalBorrow;
        _totalBorrowAmount = _totalBorrow.amount;
        _totalBorrowShares = _totalBorrow.shares;
        _totalCollateral = totalCollateral;
    }

    /// @notice The ```toBorrowShares``` function converts a given amount of borrow debt into the number of shares
    /// @param _amount Amount of borrow
    /// @param _roundUp Whether to roundup during division
    function toBorrowShares(uint256 _amount, bool _roundUp) external view returns (uint256) {
        return totalBorrow.toShares(_amount, _roundUp);
    }

    /// @notice The ```toBorrowAmount``` function converts a given amount of borrow debt into the number of shares
    /// @param _shares Shares of borrow
    /// @param _roundUp Whether to roundup during division
    /// @return The amount of asset
    function toBorrowAmount(uint256 _shares, bool _roundUp) external view returns (uint256) {
        return totalBorrow.toAmount(_shares, _roundUp);
    }

    /// @notice The ```toAssetAmount``` function converts a given number of shares to an asset amount
    /// @param _shares Shares of asset (fToken)
    /// @param _roundUp Whether to round up after division
    /// @return The amount of asset
    function toAssetAmount(uint256 _shares, bool _roundUp) external view returns (uint256) {
        return totalAsset.toAmount(_shares, _roundUp);
    }

    /// @notice The ```toAssetShares``` function converts a given asset amount to a number of asset shares (fTokens)
    /// @param _amount The amount of asset
    /// @param _roundUp Whether to round up after division
    /// @return The number of shares (fTokens)
    function toAssetShares(uint256 _amount, bool _roundUp) external view returns (uint256) {
        return totalAsset.toShares(_amount, _roundUp);
    }

    // ============================================================================================
    // Functions: Configuration
    // ============================================================================================
    /// @notice The ```SetTimeLock``` event fires when the TIME_LOCK_ADDRESS is set
    /// @param _oldAddress The original address
    /// @param _newAddress The new address
    event SetTimeLock(address _oldAddress, address _newAddress);

    /// @notice The ```setTimeLock``` function sets the TIME_LOCK address
    /// @param _newAddress the new time lock address
    function setTimeLock(address _newAddress) external {
        if (msg.sender != TIME_LOCK_ADDRESS) revert OnlyTimeLock();
        emit SetTimeLock(TIME_LOCK_ADDRESS, _newAddress);
        TIME_LOCK_ADDRESS = _newAddress;
    }

    /// @notice The ```ChangeFee``` event first when the fee is changed
    /// @param _newFee The new fee
    event ChangeFee(uint32 _newFee);

    /// @notice The ```changeFee``` function changes the protocol fee, max 50%
    /// @param _newFee The new fee
    function changeFee(uint32 _newFee) external whenNotPaused {
        if (msg.sender != TIME_LOCK_ADDRESS) revert OnlyTimeLock();
        if (_newFee > MAX_PROTOCOL_FEE) {
            revert BadProtocolFee();
        }
        _addInterest();
        currentRateInfo.feeToProtocolRate = _newFee;
        emit ChangeFee(_newFee);
    }

    /// @notice The ```WithdrawFees``` event fires when the fees are withdrawn
    /// @param _shares Number of _shares (fTokens) redeemed
    /// @param _recipient To whom the assets were sent
    /// @param _amountToTransfer The amount of fees redeemed
    event WithdrawFees(uint128 _shares, address _recipient, uint256 _amountToTransfer);

    /// @notice The ```withdrawFees``` function withdraws fees accumulated
    /// @param _shares Number of fTokens to redeem
    /// @param _recipient Address to send the assets
    /// @return _amountToTransfer Amount of assets sent to recipient
    function withdrawFees(uint128 _shares, address _recipient) external onlyOwner returns (uint256 _amountToTransfer) {
        // Grab some data from state to save gas
        VaultAccount memory _totalAsset = totalAsset;
        VaultAccount memory _totalBorrow = totalBorrow;

        // Take all available if 0 value passed
        if (_shares == 0) _shares = uint128(balanceOf(address(this)));

        // We must calculate this before we subtract from _totalAsset or invoke _burn
        _amountToTransfer = _totalAsset.toAmount(_shares, true);

        // Check for sufficient withdraw liquidity
        uint256 _assetsAvailable = _totalAssetAvailable(_totalAsset, _totalBorrow);
        if (_assetsAvailable < _amountToTransfer) {
            revert InsufficientAssetsInContract(_assetsAvailable, _amountToTransfer);
        }

        // Effects: bookkeeping
        _totalAsset.amount -= uint128(_amountToTransfer);
        _totalAsset.shares -= _shares;

        // Effects: write to states
        // NOTE: will revert if _shares > balanceOf(address(this))
        _burn(address(this), _shares);
        totalAsset = _totalAsset;

        // Interactions
        assetContract.safeTransfer(_recipient, _amountToTransfer);
        emit WithdrawFees(_shares, _recipient, _amountToTransfer);
    }

    /// @notice The ```SetSwapper``` event fires whenever a swapper is black or whitelisted
    /// @param _swapper The swapper address
    /// @param _approval The approval
    event SetSwapper(address _swapper, bool _approval);

    /// @notice The ```setSwapper``` function is called to black or whitelist a given swapper address
    /// @dev
    /// @param _swapper The swapper address
    /// @param _approval The approval
    function setSwapper(address _swapper, bool _approval) external onlyOwner {
        swappers[_swapper] = _approval;
        emit SetSwapper(_swapper, _approval);
    }

    /// @notice The ```SetApprovedLender``` event fires when a lender is black or whitelisted
    /// @param _address The address
    /// @param _approval The approval
    event SetApprovedLender(address indexed _address, bool _approval);

    /// @notice The ```setApprovedLenders``` function sets a given set of addresses to the whitelist
    /// @dev Cannot black list self
    /// @param _lenders The addresses who's status will be set
    /// @param _approval The approval status
    function setApprovedLenders(address[] calldata _lenders, bool _approval) external approvedLender(msg.sender) {
        for (uint256 i = 0; i < _lenders.length; i++) {
            // Do not set when _approval == false and _lender == msg.sender
            if (_approval || _lenders[i] != msg.sender) {
                approvedLenders[_lenders[i]] = _approval;
                emit SetApprovedLender(_lenders[i], _approval);
            }
        }
    }

    /// @notice The ```SetApprovedBorrower``` event fires when a borrower is black or whitelisted
    /// @param _address The address
    /// @param _approval The approval
    event SetApprovedBorrower(address indexed _address, bool _approval);

    /// @notice The ```setApprovedBorrowers``` function sets a given array of addresses to the whitelist
    /// @dev Cannot black list self
    /// @param _borrowers The addresses who's status will be set
    /// @param _approval The approval status
    function setApprovedBorrowers(address[] calldata _borrowers, bool _approval) external approvedBorrower {
        for (uint256 i = 0; i < _borrowers.length; i++) {
            // Do not set when _approval == false and _borrower == msg.sender
            if (_approval || _borrowers[i] != msg.sender) {
                approvedBorrowers[_borrowers[i]] = _approval;
                emit SetApprovedBorrower(_borrowers[i], _approval);
            }
        }
    }

    function pause() external {
        if (
            msg.sender != CIRCUIT_BREAKER_ADDRESS &&
            msg.sender != COMPTROLLER_ADDRESS &&
            msg.sender != owner() &&
            msg.sender != DEPLOYER_ADDRESS
        ) {
            revert ProtocolOrOwnerOnly();
        }
        _addInterest(); // accrue any interest prior to pausing as it won't accrue during pause
        _pause();
    }

    function unpause() external {
        if (msg.sender != COMPTROLLER_ADDRESS && msg.sender != owner()) {
            revert ProtocolOrOwnerOnly();
        }
        // Resets the lastTimestamp which has the effect of no interest accruing over the pause period
        _addInterest();
        _unpause();
    }
}

File 2 of 21 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 3 of 21 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

File 4 of 21 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

File 5 of 21 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

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

  function version() external view returns (uint256);

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}

File 6 of 21 : FraxlendPairConstants.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.16;

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ===================== FraxlendPairConstants ========================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance

// Primary Author
// Drake Evans: https://github.com/DrakeEvans

// Reviewers
// Dennis: https://github.com/denett
// Sam Kazemian: https://github.com/samkazemian
// Travis Moore: https://github.com/FortisFortuna
// Jack Corddry: https://github.com/corddry
// Rich Gee: https://github.com/zer0blockchain

// ====================================================================

abstract contract FraxlendPairConstants {
    // ============================================================================================
    // Constants
    // ============================================================================================

    // Precision settings
    uint256 internal constant LTV_PRECISION = 1e5; // 5 decimals
    uint256 internal constant LIQ_PRECISION = 1e5;
    uint256 internal constant UTIL_PREC = 1e5;
    uint256 internal constant FEE_PRECISION = 1e5;
    uint256 internal constant EXCHANGE_PRECISION = 1e18;

    // Default Interest Rate (if borrows = 0)
    uint64 internal constant DEFAULT_INT = 158049988; // 0.5% annual rate 1e18 precision

    // Protocol Fee
    uint16 internal constant DEFAULT_PROTOCOL_FEE = 0; // 1e5 precision
    uint256 internal constant MAX_PROTOCOL_FEE = 5e4; // 50% 1e5 precision

    error Insolvent(uint256 _borrow, uint256 _collateral, uint256 _exchangeRate);
    error BorrowerSolvent();
    error OnlyApprovedBorrowers();
    error OnlyApprovedLenders();
    error PastMaturity();
    error ProtocolOrOwnerOnly();
    error OracleLTEZero(address _oracle);
    error InsufficientAssetsInContract(uint256 _assets, uint256 _request);
    error NotOnWhitelist(address _address);
    error NotDeployer();
    error NameEmpty();
    error AlreadyInitialized();
    error SlippageTooHigh(uint256 _minOut, uint256 _actual);
    error BadSwapper();
    error InvalidPath(address _expected, address _actual);
    error BadProtocolFee();
    error BorrowerWhitelistRequired();
    error OnlyTimeLock();
    error PriceTooLarge();
    error PastDeadline(uint256 _blockTimestamp, uint256 _deadline);
}

File 7 of 21 : FraxlendPairCore.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.16;

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ========================= FraxlendPairCore =========================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance

// Primary Author
// Drake Evans: https://github.com/DrakeEvans

// Reviewers
// Dennis: https://github.com/denett
// Sam Kazemian: https://github.com/samkazemian
// Travis Moore: https://github.com/FortisFortuna
// Jack Corddry: https://github.com/corddry
// Rich Gee: https://github.com/zer0blockchain

// ====================================================================

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "./FraxlendPairConstants.sol";
import "./libraries/VaultAccount.sol";
import "./libraries/SafeERC20.sol";
import "./interfaces/IERC4626.sol";
import "./interfaces/IFraxlendWhitelist.sol";
import "./interfaces/IRateCalculator.sol";
import "./interfaces/ISwapper.sol";

/// @title FraxlendPairCore
/// @author Drake Evans (Frax Finance) https://github.com/drakeevans
/// @notice  An abstract contract which contains the core logic and storage for the FraxlendPair
abstract contract FraxlendPairCore is FraxlendPairConstants, ERC20, Ownable, Pausable, ReentrancyGuard {
    using VaultAccountingLibrary for VaultAccount;
    using SafeERC20 for IERC20;
    using SafeCast for uint256;

    string public version = "1.0.0";

    // ============================================================================================
    // Settings set by constructor() & initialize()
    // ============================================================================================

    // Asset and collateral contracts
    IERC20 internal immutable assetContract;
    IERC20 public immutable collateralContract;

    // Oracle wrapper contract and oracleData
    address public immutable oracleMultiply;
    address public immutable oracleDivide;
    uint256 public immutable oracleNormalization;

    // LTV Settings
    uint256 public immutable maxLTV;

    // Liquidation Fee
    uint256 public immutable cleanLiquidationFee;
    uint256 public immutable dirtyLiquidationFee;

    // Interest Rate Calculator Contract
    IRateCalculator public immutable rateContract; // For complex rate calculations
    bytes public rateInitCallData; // Optional extra data from init function to be passed to rate calculator

    // Swapper
    mapping(address => bool) public swappers; // approved swapper addresses

    // Deployer
    address public immutable DEPLOYER_ADDRESS;

    // Admin contracts
    address public immutable CIRCUIT_BREAKER_ADDRESS;
    address public immutable COMPTROLLER_ADDRESS;
    address public TIME_LOCK_ADDRESS;

    // Dependencies
    address public immutable FRAXLEND_WHITELIST_ADDRESS;

    // ERC20 token name, accessible via name()
    string internal nameOfContract;

    // Maturity Date & Penalty Interest Rate (per Sec)
    uint256 public immutable maturityDate;
    uint256 public immutable penaltyRate;

    // ============================================================================================
    // Storage
    // ============================================================================================

    /// @notice Stores information about the current interest rate
    /// @dev struct is packed to reduce SLOADs. feeToProtocolRate is 1e5 precision, ratePerSec is 1e18 precision
    CurrentRateInfo public currentRateInfo;
    struct CurrentRateInfo {
        uint64 lastBlock;
        uint64 feeToProtocolRate; // Fee amount 1e5 precision
        uint64 lastTimestamp;
        uint64 ratePerSec;
    }

    /// @notice Stores information about the current exchange rate. Collateral:Asset ratio
    /// @dev Struct packed to save SLOADs. Amount of Collateral Token to buy 1e18 Asset Token
    ExchangeRateInfo public exchangeRateInfo;
    struct ExchangeRateInfo {
        uint32 lastTimestamp;
        uint224 exchangeRate; // collateral:asset ratio. i.e. how much collateral to buy 1e18 asset
    }

    // Contract Level Accounting
    VaultAccount public totalAsset; // amount = total amount of assets, shares = total shares outstanding
    VaultAccount public totalBorrow; // amount = total borrow amount with interest accrued, shares = total shares outstanding
    uint256 public totalCollateral; // total amount of collateral in contract

    // User Level Accounting
    /// @notice Stores the balance of collateral for each user
    mapping(address => uint256) public userCollateralBalance; // amount of collateral each user is backed
    /// @notice Stores the balance of borrow shares for each user
    mapping(address => uint256) public userBorrowShares; // represents the shares held by individuals
    // NOTE: user shares of assets are represented as ERC-20 tokens and accessible via balanceOf()

    // Internal Whitelists
    bool public immutable borrowerWhitelistActive;
    mapping(address => bool) public approvedBorrowers;

    bool public immutable lenderWhitelistActive;
    mapping(address => bool) public approvedLenders;

    // ============================================================================================
    // Initialize
    // ============================================================================================

    /// @notice The ```constructor``` function is called on deployment
    /// @param _configData abi.encode(address _asset, address _collateral, address _oracleMultiply, address _oracleDivide, uint256 _oracleNormalization, address _rateContract, bytes memory _rateInitData)
    /// @param _maxLTV The Maximum Loan-To-Value for a borrower to be considered solvent (1e5 precision)
    /// @param _liquidationFee The fee paid to liquidators given as a % of the repayment (1e5 precision)
    /// @param _maturityDate The maturityDate date of the Pair
    /// @param _penaltyRate The interest rate after maturity date
    /// @param _isBorrowerWhitelistActive Enables borrower whitelist
    /// @param _isLenderWhitelistActive Enables lender whitelist
    constructor(
        bytes memory _configData,
        bytes memory _immutables,
        uint256 _maxLTV,
        uint256 _liquidationFee,
        uint256 _maturityDate,
        uint256 _penaltyRate,
        bool _isBorrowerWhitelistActive,
        bool _isLenderWhitelistActive
    ) {
        // Handle Immutables Configuration
        {
            (
                address _circuitBreaker,
                address _comptrollerAddress,
                address _timeLockAddress,
                address _fraxlendWhitelistAddress
            ) = abi.decode(_immutables, (address, address, address, address));

            // Deployer contract
            DEPLOYER_ADDRESS = msg.sender;
            CIRCUIT_BREAKER_ADDRESS = _circuitBreaker;
            COMPTROLLER_ADDRESS = _comptrollerAddress;
            TIME_LOCK_ADDRESS = _timeLockAddress;
            FRAXLEND_WHITELIST_ADDRESS = _fraxlendWhitelistAddress;
        }

        {
            (
                address _asset,
                address _collateral,
                address _oracleMultiply,
                address _oracleDivide,
                uint256 _oracleNormalization,
                address _rateContract,

            ) = abi.decode(_configData, (address, address, address, address, uint256, address, bytes));

            // Pair Settings
            assetContract = IERC20(_asset);
            collateralContract = IERC20(_collateral);
            currentRateInfo.feeToProtocolRate = DEFAULT_PROTOCOL_FEE;
            cleanLiquidationFee = _liquidationFee;
            dirtyLiquidationFee = (_liquidationFee * 90000) / LIQ_PRECISION; // 90% of clean fee

            if (_maxLTV >= LTV_PRECISION && !_isBorrowerWhitelistActive) revert BorrowerWhitelistRequired();
            maxLTV = _maxLTV;

            // Oracle Settings
            {
                IFraxlendWhitelist _fraxlendWhitelist = IFraxlendWhitelist(FRAXLEND_WHITELIST_ADDRESS);
                // Check that oracles are on the whitelist
                if (_oracleMultiply != address(0) && !_fraxlendWhitelist.oracleContractWhitelist(_oracleMultiply)) {
                    revert NotOnWhitelist(_oracleMultiply);
                }

                if (_oracleDivide != address(0) && !_fraxlendWhitelist.oracleContractWhitelist(_oracleDivide)) {
                    revert NotOnWhitelist(_oracleDivide);
                }

                // Write oracleData to storage
                oracleMultiply = _oracleMultiply;
                oracleDivide = _oracleDivide;
                oracleNormalization = _oracleNormalization;

                // Rate Settings
                if (!_fraxlendWhitelist.rateContractWhitelist(_rateContract)) {
                    revert NotOnWhitelist(_rateContract);
                }
            }

            rateContract = IRateCalculator(_rateContract);
        }

        // Set approved borrowers whitelist
        borrowerWhitelistActive = _isBorrowerWhitelistActive;

        // Set approved lenders whitelist active
        lenderWhitelistActive = _isLenderWhitelistActive;

        // Set maturity date & penalty interest rate
        maturityDate = _maturityDate;
        penaltyRate = _penaltyRate;
    }

    /// @notice The ```initialize``` function is called immediately after deployment
    /// @dev This function can only be called by the deployer
    /// @param _name The name of the contract
    /// @param _approvedBorrowers An array of approved borrower addresses
    /// @param _approvedLenders An array of approved lender addresses
    /// @param _rateInitCallData The configuration data for the Rate Calculator contract
    function initialize(
        string calldata _name,
        address[] calldata _approvedBorrowers,
        address[] calldata _approvedLenders,
        bytes calldata _rateInitCallData
    ) external {
        if (msg.sender != DEPLOYER_ADDRESS) {
            revert NotDeployer();
        }
        if (bytes(_name).length == 0) {
            revert NameEmpty();
        }
        if (bytes(nameOfContract).length != 0) {
            revert AlreadyInitialized();
        }

        // Set name
        nameOfContract = _name;

        // Set approved borrowers
        for (uint256 i = 0; i < _approvedBorrowers.length; ++i) {
            approvedBorrowers[_approvedBorrowers[i]] = true;
        }

        // Set approved lenders
        for (uint256 i = 0; i < _approvedLenders.length; ++i) {
            approvedLenders[_approvedLenders[i]] = true;
        }

        // Reverts if init data is not valid
        IRateCalculator(rateContract).requireValidInitData(_rateInitCallData);

        // Set rate init Data
        rateInitCallData = _rateInitCallData;

        // Instantiate Interest
        _addInterest();

        // Instantiate Exchange Rate
        _updateExchangeRate();
    }

    // ============================================================================================
    // Internal Helpers
    // ============================================================================================

    /// @notice The ```_totalAssetAvailable``` function returns the total balance of Asset Tokens in the contract
    /// @param _totalAsset VaultAccount struct which stores total amount and shares for assets
    /// @param _totalBorrow VaultAccount struct which stores total amount and shares for borrows
    /// @return The balance of Asset Tokens held by contract
    function _totalAssetAvailable(VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow)
        internal
        pure
        returns (uint256)
    {
        return _totalAsset.amount - _totalBorrow.amount;
    }

    /// @notice The ```_isSolvent``` function determines if a given borrower is solvent given an exchange rate
    /// @param _borrower The borrower address to check
    /// @param _exchangeRate The exchange rate, i.e. the amount of collateral to buy 1e18 asset
    /// @return Whether borrower is solvent
    function _isSolvent(address _borrower, uint256 _exchangeRate) internal view returns (bool) {
        if (maxLTV == 0) return true;
        uint256 _borrowerAmount = totalBorrow.toAmount(userBorrowShares[_borrower], true);
        if (_borrowerAmount == 0) return true;
        uint256 _collateralAmount = userCollateralBalance[_borrower];
        if (_collateralAmount == 0) return false;

        uint256 _ltv = (((_borrowerAmount * _exchangeRate) / EXCHANGE_PRECISION) * LTV_PRECISION) / _collateralAmount;
        return _ltv <= maxLTV;
    }

    /// @notice The ```_isPastMaturity``` function determines if the current block timestamp is past the maturityDate date
    /// @return Whether or not the debt is past maturity
    function _isPastMaturity() internal view returns (bool) {
        return maturityDate != 0 && block.timestamp > maturityDate;
    }

    // ============================================================================================
    // Modifiers
    // ============================================================================================

    /// @notice Checks for solvency AFTER executing contract code
    /// @param _borrower The borrower whose solvency we will check
    modifier isSolvent(address _borrower) {
        _;
        if (!_isSolvent(_borrower, exchangeRateInfo.exchangeRate)) {
            revert Insolvent(
                totalBorrow.toAmount(userBorrowShares[_borrower], true),
                userCollateralBalance[_borrower],
                exchangeRateInfo.exchangeRate
            );
        }
    }

    /// @notice Checks if msg.sender is an approved Borrower
    modifier approvedBorrower() {
        if (borrowerWhitelistActive && !approvedBorrowers[msg.sender]) {
            revert OnlyApprovedBorrowers();
        }
        _;
    }

    /// @notice Checks if msg.sender and _receiver are both an approved Lender
    /// @param _receiver An additional receiver address to check
    modifier approvedLender(address _receiver) {
        if (lenderWhitelistActive && (!approvedLenders[msg.sender] || !approvedLenders[_receiver])) {
            revert OnlyApprovedLenders();
        }
        _;
    }

    /// @notice Ensure function is not called when passed maturity
    modifier isNotPastMaturity() {
        if (_isPastMaturity()) {
            revert PastMaturity();
        }
        _;
    }

    // ============================================================================================
    // Functions: Interest Accumulation and Adjustment
    // ============================================================================================

    /// @notice The ```AddInterest``` event is emitted when interest is accrued by borrowers
    /// @param _interestEarned The total interest accrued by all borrowers
    /// @param _rate The interest rate used to calculate accrued interest
    /// @param _deltaTime The time elapsed since last interest accrual
    /// @param _feesAmount The amount of fees paid to protocol
    /// @param _feesShare The amount of shares distributed to protocol
    event AddInterest(
        uint256 _interestEarned,
        uint256 _rate,
        uint256 _deltaTime,
        uint256 _feesAmount,
        uint256 _feesShare
    );

    /// @notice The ```UpdateRate``` event is emitted when the interest rate is updated
    /// @param _ratePerSec The old interest rate (per second)
    /// @param _deltaTime The time elapsed since last update
    /// @param _utilizationRate The utilization of assets in the Pair
    /// @param _newRatePerSec The new interest rate (per second)
    event UpdateRate(uint256 _ratePerSec, uint256 _deltaTime, uint256 _utilizationRate, uint256 _newRatePerSec);

    /// @notice The ```addInterest``` function is a public implementation of _addInterest and allows 3rd parties to trigger interest accrual
    /// @return _interestEarned The amount of interest accrued by all borrowers
    function addInterest()
        external
        nonReentrant
        returns (
            uint256 _interestEarned,
            uint256 _feesAmount,
            uint256 _feesShare,
            uint64 _newRate
        )
    {
        return _addInterest();
    }

    /// @notice The ```_addInterest``` function is invoked prior to every external function and is used to accrue interest and update interest rate
    /// @dev Can only called once per block
    /// @return _interestEarned The amount of interest accrued by all borrowers
    function _addInterest()
        internal
        returns (
            uint256 _interestEarned,
            uint256 _feesAmount,
            uint256 _feesShare,
            uint64 _newRate
        )
    {
        // Add interest only once per block
        CurrentRateInfo memory _currentRateInfo = currentRateInfo;
        if (_currentRateInfo.lastTimestamp == block.timestamp) {
            _newRate = _currentRateInfo.ratePerSec;
            return (_interestEarned, _feesAmount, _feesShare, _newRate);
        }

        // Pull some data from storage to save gas
        VaultAccount memory _totalAsset = totalAsset;
        VaultAccount memory _totalBorrow = totalBorrow;

        // If there are no borrows or contract is paused, no interest accrues and we reset interest rate
        if (_totalBorrow.shares == 0 || paused()) {
            if (!paused()) {
                _currentRateInfo.ratePerSec = DEFAULT_INT;
            }
            _currentRateInfo.lastTimestamp = uint64(block.timestamp);
            _currentRateInfo.lastBlock = uint64(block.number);

            // Effects: write to storage
            currentRateInfo = _currentRateInfo;
        } else {
            // We know totalBorrow.shares > 0
            uint256 _deltaTime = block.timestamp - _currentRateInfo.lastTimestamp;

            // NOTE: Violates Checks-Effects-Interactions pattern
            // Be sure to mark external version NONREENTRANT (even though rateContract is trusted)
            // Calc new rate
            uint256 _utilizationRate = (UTIL_PREC * _totalBorrow.amount) / _totalAsset.amount;
            if (_isPastMaturity()) {
                _newRate = uint64(penaltyRate);
            } else {
                bytes memory _rateData = abi.encode(
                    _currentRateInfo.ratePerSec,
                    _deltaTime,
                    _utilizationRate,
                    block.number - _currentRateInfo.lastBlock
                );
                _newRate = IRateCalculator(rateContract).getNewRate(_rateData, rateInitCallData);
            }

            // Event must be here to use non-mutated values
            emit UpdateRate(_currentRateInfo.ratePerSec, _deltaTime, _utilizationRate, _newRate);

            // Effects: bookkeeping
            _currentRateInfo.ratePerSec = _newRate;
            _currentRateInfo.lastTimestamp = uint64(block.timestamp);
            _currentRateInfo.lastBlock = uint64(block.number);

            // Calculate interest accrued
            _interestEarned = (_deltaTime * _totalBorrow.amount * _currentRateInfo.ratePerSec) / 1e18;

            // Accumulate interest and fees, only if no overflow upon casting
            if (
                _interestEarned + _totalBorrow.amount <= type(uint128).max &&
                _interestEarned + _totalAsset.amount <= type(uint128).max
            ) {
                _totalBorrow.amount += uint128(_interestEarned);
                _totalAsset.amount += uint128(_interestEarned);
                if (_currentRateInfo.feeToProtocolRate > 0) {
                    _feesAmount = (_interestEarned * _currentRateInfo.feeToProtocolRate) / FEE_PRECISION;

                    _feesShare = (_feesAmount * _totalAsset.shares) / (_totalAsset.amount - _feesAmount);

                    // Effects: Give new shares to this contract, effectively diluting lenders an amount equal to the fees
                    // We can safely cast because _feesShare < _feesAmount < interestEarned which is always less than uint128
                    _totalAsset.shares += uint128(_feesShare);

                    // Effects: write to storage
                    _mint(address(this), _feesShare);
                }
                emit AddInterest(_interestEarned, _currentRateInfo.ratePerSec, _deltaTime, _feesAmount, _feesShare);
            }

            // Effects: write to storage
            totalAsset = _totalAsset;
            currentRateInfo = _currentRateInfo;
            totalBorrow = _totalBorrow;
        }
    }

    // ============================================================================================
    // Functions: ExchangeRate
    // ============================================================================================
    /// @notice The ```UpdateExchangeRate``` event is emitted when the Collateral:Asset exchange rate is updated
    /// @param _rate The new rate given as the amount of Collateral Token to buy 1e18 Asset Token
    event UpdateExchangeRate(uint256 _rate);

    /// @notice The ```updateExchangeRate``` function is the external implementation of _updateExchangeRate.
    /// @dev This function is invoked at most once per block as these queries can be expensive
    /// @return _exchangeRate The new exchange rate
    function updateExchangeRate() external nonReentrant returns (uint256 _exchangeRate) {
        _exchangeRate = _updateExchangeRate();
    }

    /// @notice The ```_updateExchangeRate``` function retrieves the latest exchange rate. i.e how much collateral to buy 1e18 asset.
    /// @dev This function is invoked at most once per block as these queries can be expensive
    /// @return _exchangeRate The new exchange rate
    function _updateExchangeRate() internal returns (uint256 _exchangeRate) {
        ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;
        if (_exchangeRateInfo.lastTimestamp == block.timestamp) {
            return _exchangeRate = _exchangeRateInfo.exchangeRate;
        }

        uint256 _price = uint256(1e36);
        if (oracleMultiply != address(0)) {
            (, int256 _answer, , , ) = AggregatorV3Interface(oracleMultiply).latestRoundData();
            if (_answer <= 0) {
                revert OracleLTEZero(oracleMultiply);
            }
            _price = _price * uint256(_answer);
        }

        if (oracleDivide != address(0)) {
            (, int256 _answer, , , ) = AggregatorV3Interface(oracleDivide).latestRoundData();
            if (_answer <= 0) {
                revert OracleLTEZero(oracleDivide);
            }
            _price = _price / uint256(_answer);
        }

        _exchangeRate = _price / oracleNormalization;

        // write to storage, if no overflow
        if (_exchangeRate > type(uint224).max) revert PriceTooLarge();
        _exchangeRateInfo.exchangeRate = uint224(_exchangeRate);
        _exchangeRateInfo.lastTimestamp = uint32(block.timestamp);
        exchangeRateInfo = _exchangeRateInfo;
        emit UpdateExchangeRate(_exchangeRate);
    }

    // ============================================================================================
    // Functions: Lending
    // ============================================================================================

    /// @notice The ```Deposit``` event fires when a user deposits assets to the pair
    /// @param caller the msg.sender
    /// @param owner the account the fTokens are sent to
    /// @param assets the amount of assets deposited
    /// @param shares the number of fTokens minted
    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

    /// @notice The ```_deposit``` function is the internal implementation for lending assets
    /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling function
    /// @param _totalAsset An in memory VaultAccount struct representing the total amounts and shares for the Asset Token
    /// @param _amount The amount of Asset Token to be transferred
    /// @param _shares The amount of Asset Shares (fTokens) to be minted
    /// @param _receiver The address to receive the Asset Shares (fTokens)
    function _deposit(
        VaultAccount memory _totalAsset,
        uint128 _amount,
        uint128 _shares,
        address _receiver
    ) internal {
        // Effects: bookkeeping
        _totalAsset.amount += _amount;
        _totalAsset.shares += _shares;

        // Effects: write back to storage
        _mint(_receiver, _shares);
        totalAsset = _totalAsset;

        // Interactions
        assetContract.safeTransferFrom(msg.sender, address(this), _amount);
        emit Deposit(msg.sender, _receiver, _amount, _shares);
    }

    /// @notice The ```deposit``` function allows a user to Lend Assets by specifying the amount of Asset Tokens to lend
    /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling function
    /// @param _amount The amount of Asset Token to transfer to Pair
    /// @param _receiver The address to receive the Asset Shares (fTokens)
    /// @return _sharesReceived The number of fTokens received for the deposit
    function deposit(uint256 _amount, address _receiver)
        external
        nonReentrant
        isNotPastMaturity
        whenNotPaused
        approvedLender(_receiver)
        returns (uint256 _sharesReceived)
    {
        _addInterest();
        VaultAccount memory _totalAsset = totalAsset;
        _sharesReceived = _totalAsset.toShares(_amount, false);
        _deposit(_totalAsset, _amount.toUint128(), _sharesReceived.toUint128(), _receiver);
    }

    /// @notice The ```Withdraw``` event fires when a user redeems their fTokens for the underlying asset
    /// @param caller the msg.sender
    /// @param receiver The address to which the underlying asset will be transferred to
    /// @param owner The owner of the fTokens
    /// @param assets The assets transferred
    /// @param shares The number of fTokens burned
    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /// @notice The ```_redeem``` function is an internal implementation which allows a Lender to pull their Asset Tokens out of the Pair
    /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling function
    /// @param _totalAsset An in-memory VaultAccount struct which holds the total amount of Asset Tokens and the total number of Asset Shares (fTokens)
    /// @param _amountToReturn The number of Asset Tokens to return
    /// @param _shares The number of Asset Shares (fTokens) to burn
    /// @param _receiver The address to which the Asset Tokens will be transferred
    /// @param _owner The owner of the Asset Shares (fTokens)
    function _redeem(
        VaultAccount memory _totalAsset,
        uint128 _amountToReturn,
        uint128 _shares,
        address _receiver,
        address _owner
    ) internal {
        if (msg.sender != _owner) {
            uint256 allowed = allowance(_owner, msg.sender);
            // NOTE: This will revert on underflow ensuring that allowance > shares
            if (allowed != type(uint256).max) _approve(_owner, msg.sender, allowed - _shares);
        }

        // Check for sufficient withdraw liquidity
        uint256 _assetsAvailable = _totalAssetAvailable(_totalAsset, totalBorrow);
        if (_assetsAvailable < _amountToReturn) {
            revert InsufficientAssetsInContract(_assetsAvailable, _amountToReturn);
        }

        // Effects: bookkeeping
        _totalAsset.amount -= _amountToReturn;
        _totalAsset.shares -= _shares;

        // Effects: write to storage
        totalAsset = _totalAsset;
        _burn(_owner, _shares);

        // Interactions
        assetContract.safeTransfer(_receiver, _amountToReturn);
        emit Withdraw(msg.sender, _receiver, _owner, _amountToReturn, _shares);
    }

    /// @notice The ```redeem``` function allows the caller to redeem their Asset Shares for Asset Tokens
    /// @param _shares The number of Asset Shares (fTokens) to burn for Asset Tokens
    /// @param _receiver The address to which the Asset Tokens will be transferred
    /// @param _owner The owner of the Asset Shares (fTokens)
    /// @return _amountToReturn The amount of Asset Tokens to be transferred
    function redeem(
        uint256 _shares,
        address _receiver,
        address _owner
    ) external nonReentrant returns (uint256 _amountToReturn) {
        _addInterest();
        VaultAccount memory _totalAsset = totalAsset;
        _amountToReturn = _totalAsset.toAmount(_shares, false);
        _redeem(_totalAsset, _amountToReturn.toUint128(), _shares.toUint128(), _receiver, _owner);
    }

    // ============================================================================================
    // Functions: Borrowing
    // ============================================================================================

    /// @notice The ```BorrowAsset``` event is emitted when a borrower increases their position
    /// @param _borrower The borrower whose account was debited
    /// @param _receiver The address to which the Asset Tokens were transferred
    /// @param _borrowAmount The amount of Asset Tokens transferred
    /// @param _sharesAdded The number of Borrow Shares the borrower was debited
    event BorrowAsset(
        address indexed _borrower,
        address indexed _receiver,
        uint256 _borrowAmount,
        uint256 _sharesAdded
    );

    /// @notice The ```_borrowAsset``` function is the internal implementation for borrowing assets
    /// @param _borrowAmount The amount of the Asset Token to borrow
    /// @param _receiver The address to receive the Asset Tokens
    /// @return _sharesAdded The amount of borrow shares the msg.sender will be debited
    function _borrowAsset(uint128 _borrowAmount, address _receiver) internal returns (uint256 _sharesAdded) {
        VaultAccount memory _totalBorrow = totalBorrow;

        // Check available capital
        uint256 _assetsAvailable = _totalAssetAvailable(totalAsset, _totalBorrow);
        if (_assetsAvailable < _borrowAmount) {
            revert InsufficientAssetsInContract(_assetsAvailable, _borrowAmount);
        }

        // Effects: Bookkeeping to add shares & amounts to total Borrow accounting
        _sharesAdded = _totalBorrow.toShares(_borrowAmount, true);
        _totalBorrow.amount += _borrowAmount;
        _totalBorrow.shares += uint128(_sharesAdded);
        // NOTE: we can safely cast here because shares are always less than amount and _borrowAmount is uint128

        // Effects: write back to storage
        totalBorrow = _totalBorrow;
        userBorrowShares[msg.sender] += _sharesAdded;

        // Interactions
        if (_receiver != address(this)) {
            assetContract.safeTransfer(_receiver, _borrowAmount);
        }
        emit BorrowAsset(msg.sender, _receiver, _borrowAmount, _sharesAdded);
    }

    /// @notice The ```borrowAsset``` function allows a user to open/increase a borrow position
    /// @dev Borrower must call ```ERC20.approve``` on the Collateral Token contract if applicable
    /// @param _borrowAmount The amount of Asset Token to borrow
    /// @param _collateralAmount The amount of Collateral Token to transfer to Pair
    /// @param _receiver The address which will receive the Asset Tokens
    /// @return _shares The number of borrow Shares the msg.sender will be debited
    function borrowAsset(
        uint256 _borrowAmount,
        uint256 _collateralAmount,
        address _receiver
    )
        external
        isNotPastMaturity
        whenNotPaused
        nonReentrant
        isSolvent(msg.sender)
        approvedBorrower
        returns (uint256 _shares)
    {
        _addInterest();
        _updateExchangeRate();
        if (_collateralAmount > 0) {
            _addCollateral(msg.sender, _collateralAmount, msg.sender);
        }
        _shares = _borrowAsset(_borrowAmount.toUint128(), _receiver);
    }

    event AddCollateral(address indexed _sender, address indexed _borrower, uint256 _collateralAmount);

    /// @notice The ```_addCollateral``` function is an internal implementation for adding collateral to a borrowers position
    /// @param _sender The source of funds for the new collateral
    /// @param _collateralAmount The amount of Collateral Token to be transferred
    /// @param _borrower The borrower account for which the collateral should be credited
    function _addCollateral(
        address _sender,
        uint256 _collateralAmount,
        address _borrower
    ) internal {
        // Effects: write to state
        userCollateralBalance[_borrower] += _collateralAmount;
        totalCollateral += _collateralAmount;

        // Interactions
        if (_sender != address(this)) {
            collateralContract.safeTransferFrom(_sender, address(this), _collateralAmount);
        }
        emit AddCollateral(_sender, _borrower, _collateralAmount);
    }

    /// @notice The ```addCollateral``` function allows the caller to add Collateral Token to a borrowers position
    /// @dev msg.sender must call ERC20.approve() on the Collateral Token contract prior to invocation
    /// @param _collateralAmount The amount of Collateral Token to be added to borrower's position
    /// @param _borrower The account to be credited
    function addCollateral(uint256 _collateralAmount, address _borrower) external nonReentrant isNotPastMaturity {
        _addInterest();
        _addCollateral(msg.sender, _collateralAmount, _borrower);
    }

    /// @notice The ```RemoveCollateral``` event is emitted when collateral is removed from a borrower's position
    /// @param _sender The account from which funds are transferred
    /// @param _collateralAmount The amount of Collateral Token to be transferred
    /// @param _receiver The address to which Collateral Tokens will be transferred
    event RemoveCollateral(
        address indexed _sender,
        uint256 _collateralAmount,
        address indexed _receiver,
        address indexed _borrower
    );

    /// @notice The ```_removeCollateral``` function is the internal implementation for removing collateral from a borrower's position
    /// @param _collateralAmount The amount of Collateral Token to remove from the borrower's position
    /// @param _receiver The address to receive the Collateral Token transferred
    /// @param _borrower The borrower whose account will be debited the Collateral amount
    function _removeCollateral(
        uint256 _collateralAmount,
        address _receiver,
        address _borrower
    ) internal {
        // Effects: write to state
        // Following line will revert on underflow if _collateralAmount > userCollateralBalance
        userCollateralBalance[_borrower] -= _collateralAmount;
        // Following line will revert on underflow if totalCollateral < _collateralAmount
        totalCollateral -= _collateralAmount;

        // Interactions
        if (_receiver != address(this)) {
            collateralContract.safeTransfer(_receiver, _collateralAmount);
        }
        emit RemoveCollateral(msg.sender, _collateralAmount, _receiver, _borrower);
    }

    /// @notice The ```removeCollateral``` function is used to remove collateral from msg.sender's borrow position
    /// @dev msg.sender must be solvent after invocation or transaction will revert
    /// @param _collateralAmount The amount of Collateral Token to transfer
    /// @param _receiver The address to receive the transferred funds
    function removeCollateral(uint256 _collateralAmount, address _receiver)
        external
        nonReentrant
        isSolvent(msg.sender)
    {
        _addInterest();
        // Note: exchange rate is irrelevant when borrower has no debt shares
        if (userBorrowShares[msg.sender] > 0) {
            _updateExchangeRate();
        }
        _removeCollateral(_collateralAmount, _receiver, msg.sender);
    }

    /// @notice The ```RepayAsset``` event is emitted whenever a debt position is repaid
    /// @param _payer The address paying for the repayment
    /// @param _borrower The borrower whose account will be credited
    /// @param _amountToRepay The amount of Asset token to be transferred
    /// @param _shares The amount of Borrow Shares which will be debited from the borrower after repayment
    event RepayAsset(address indexed _payer, address indexed _borrower, uint256 _amountToRepay, uint256 _shares);

    /// @notice The ```_repayAsset``` function is the internal implementation for repaying a borrow position
    /// @dev The payer must have called ERC20.approve() on the Asset Token contract prior to invocation
    /// @param _totalBorrow An in memory copy of the totalBorrow VaultAccount struct
    /// @param _amountToRepay The amount of Asset Token to transfer
    /// @param _shares The number of Borrow Shares the sender is repaying
    /// @param _payer The address from which funds will be transferred
    /// @param _borrower The borrower account which will be credited
    function _repayAsset(
        VaultAccount memory _totalBorrow,
        uint128 _amountToRepay,
        uint128 _shares,
        address _payer,
        address _borrower
    ) internal {
        // Effects: Bookkeeping
        _totalBorrow.amount -= _amountToRepay;
        _totalBorrow.shares -= _shares;

        // Effects: write to state
        userBorrowShares[_borrower] -= _shares;
        totalBorrow = _totalBorrow;

        // Interactions
        if (_payer != address(this)) {
            assetContract.safeTransferFrom(_payer, address(this), _amountToRepay);
        }
        emit RepayAsset(_payer, _borrower, _amountToRepay, _shares);
    }

    /// @notice The ```repayAsset``` function allows the caller to pay down the debt for a given borrower.
    /// @dev Caller must first invoke ```ERC20.approve()``` for the Asset Token contract
    /// @param _shares The number of Borrow Shares which will be repaid by the call
    /// @param _borrower The account for which the debt will be reduced
    /// @return _amountToRepay The amount of Asset Tokens which were transferred in order to repay the Borrow Shares
    function repayAsset(uint256 _shares, address _borrower) external nonReentrant returns (uint256 _amountToRepay) {
        _addInterest();
        VaultAccount memory _totalBorrow = totalBorrow;
        _amountToRepay = _totalBorrow.toAmount(_shares, true);
        _repayAsset(_totalBorrow, _amountToRepay.toUint128(), _shares.toUint128(), msg.sender, _borrower);
    }

    // ============================================================================================
    // Functions: Liquidations
    // ============================================================================================
    /// @notice The ```Liquidate``` event is emitted when a liquidation occurs
    /// @param _borrower The borrower account for which the liquidation occurred
    /// @param _collateralForLiquidator The amount of Collateral Token transferred to the liquidator
    /// @param _sharesToLiquidate The number of Borrow Shares the liquidator repaid on behalf of the borrower
    /// @param _sharesToAdjust The number of Borrow Shares that were adjusted on liabilities and assets (a writeoff)
    event Liquidate(
        address indexed _borrower,
        uint256 _collateralForLiquidator,
        uint256 _sharesToLiquidate,
        uint256 _amountLiquidatorToRepay,
        uint256 _sharesToAdjust,
        uint256 _amountToAdjust
    );

    /// @notice The ```liquidate``` function allows a third party to repay a borrower's debt if they have become insolvent
    /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling ```Liquidate()```
    /// @param _sharesToLiquidate The number of Borrow Shares repaid by the liquidator
    /// @param _deadline The timestamp after which tx will revert
    /// @param _borrower The account for which the repayment is credited and from whom collateral will be taken
    /// @return _collateralForLiquidator The amount of Collateral Token transferred to the liquidator
    function liquidate(
        uint128 _sharesToLiquidate,
        uint256 _deadline,
        address _borrower
    ) external whenNotPaused nonReentrant approvedLender(msg.sender) returns (uint256 _collateralForLiquidator) {
        if (block.timestamp > _deadline) revert PastDeadline(block.timestamp, _deadline);

        _addInterest();
        uint256 _exchangeRate = _updateExchangeRate();

        if (_isSolvent(_borrower, _exchangeRate)) {
            revert BorrowerSolvent();
        }

        // Read from state
        VaultAccount memory _totalBorrow = totalBorrow;
        uint256 _userCollateralBalance = userCollateralBalance[_borrower];
        uint128 _borrowerShares = userBorrowShares[_borrower].toUint128();

        // Prevent stack-too-deep
        int256 _leftoverCollateral;
        {
            // Checks & Calculations
            // Determine the liquidation amount in collateral units (i.e. how much debt is liquidator going to repay)
            uint256 _liquidationAmountInCollateralUnits = ((_totalBorrow.toAmount(_sharesToLiquidate, false) *
                _exchangeRate) / EXCHANGE_PRECISION);

            // We first optimistically calculate the amount of collateral to give the liquidator based on the higher clean liquidation fee
            // This fee only applies if the liquidator does a full liquidation
            uint256 _optimisticCollateralForLiquidator = (_liquidationAmountInCollateralUnits *
                (LIQ_PRECISION + cleanLiquidationFee)) / LIQ_PRECISION;

            // Because interest accrues every block, _liquidationAmountInCollateralUnits from a few lines up is an ever increasing value
            // This means that leftoverCollateral can occasionally go negative by a few hundred wei (cleanLiqFee premium covers this for liquidator)
            _leftoverCollateral = (_userCollateralBalance.toInt256() - _optimisticCollateralForLiquidator.toInt256());

            // If cleanLiquidation fee results in no leftover collateral, give liquidator all the collateral
            // This will only be true when there liquidator is cleaning out the position
            _collateralForLiquidator = _leftoverCollateral <= 0
                ? _userCollateralBalance
                : (_liquidationAmountInCollateralUnits * (LIQ_PRECISION + dirtyLiquidationFee)) / LIQ_PRECISION;
        }
        // Calculated here for use during repayment, grouped with other calcs before effects start
        uint128 _amountLiquidatorToRepay = (_totalBorrow.toAmount(_sharesToLiquidate, true)).toUint128();

        // Determine if and how much debt to adjust
        uint128 _sharesToAdjust;
        {
            uint128 _amountToAdjust;
            if (_leftoverCollateral <= 0) {
                // Determine if we need to adjust any shares
                _sharesToAdjust = _borrowerShares - _sharesToLiquidate;
                if (_sharesToAdjust > 0) {
                    // Write off bad debt
                    _amountToAdjust = (_totalBorrow.toAmount(_sharesToAdjust, false)).toUint128();

                    // Note: Ensure this memory struct will be passed to _repayAsset for write to state
                    _totalBorrow.amount -= _amountToAdjust;

                    // Effects: write to state
                    totalAsset.amount -= _amountToAdjust;
                }
            }
            emit Liquidate(
                _borrower,
                _collateralForLiquidator,
                _sharesToLiquidate,
                _amountLiquidatorToRepay,
                _sharesToAdjust,
                _amountToAdjust
            );
        }

        // Effects & Interactions
        // NOTE: reverts if _shares > userBorrowShares
        _repayAsset(
            _totalBorrow,
            _amountLiquidatorToRepay,
            _sharesToLiquidate + _sharesToAdjust,
            msg.sender,
            _borrower
        ); // liquidator repays shares on behalf of borrower
        // NOTE: reverts if _collateralForLiquidator > userCollateralBalance
        // Collateral is removed on behalf of borrower and sent to liquidator
        // NOTE: reverts if _collateralForLiquidator > userCollateralBalance
        _removeCollateral(_collateralForLiquidator, msg.sender, _borrower);
    }

    // ============================================================================================
    // Functions: Leverage
    // ============================================================================================

    /// @notice The ```LeveragedPosition``` event is emitted when a borrower takes out a new leveraged position
    /// @param _borrower The account for which the debt is debited
    /// @param _swapperAddress The address of the swapper which conforms the FraxSwap interface
    /// @param _borrowAmount The amount of Asset Token to be borrowed to be borrowed
    /// @param _borrowShares The number of Borrow Shares the borrower is credited
    /// @param _initialCollateralAmount The amount of initial Collateral Tokens supplied by the borrower
    /// @param _amountCollateralOut The amount of Collateral Token which was received for the Asset Tokens
    event LeveragedPosition(
        address indexed _borrower,
        address _swapperAddress,
        uint256 _borrowAmount,
        uint256 _borrowShares,
        uint256 _initialCollateralAmount,
        uint256 _amountCollateralOut
    );

    /// @notice The ```leveragedPosition``` function allows a user to enter a leveraged borrow position with minimal upfront Collateral
    /// @dev Caller must invoke ```ERC20.approve()``` on the Collateral Token contract prior to calling function
    /// @param _swapperAddress The address of the whitelisted swapper to use to swap borrowed Asset Tokens for Collateral Tokens
    /// @param _borrowAmount The amount of Asset Tokens borrowed
    /// @param _initialCollateralAmount The initial amount of Collateral Tokens supplied by the borrower
    /// @param _amountCollateralOutMin The minimum amount of Collateral Tokens to be received in exchange for the borrowed Asset Tokens
    /// @param _path An array containing the addresses of ERC20 tokens to swap.  Adheres to UniV2 style path params.
    /// @return _totalCollateralBalance The total amount of Collateral Tokens added to a users account (initial + swap)
    function leveragedPosition(
        address _swapperAddress,
        uint256 _borrowAmount,
        uint256 _initialCollateralAmount,
        uint256 _amountCollateralOutMin,
        address[] memory _path
    )
        external
        isNotPastMaturity
        nonReentrant
        whenNotPaused
        approvedBorrower
        isSolvent(msg.sender)
        returns (uint256 _totalCollateralBalance)
    {
        _addInterest();
        _updateExchangeRate();

        IERC20 _assetContract = assetContract;
        IERC20 _collateralContract = collateralContract;

        if (!swappers[_swapperAddress]) {
            revert BadSwapper();
        }
        if (_path[0] != address(_assetContract)) {
            revert InvalidPath(address(_assetContract), _path[0]);
        }
        if (_path[_path.length - 1] != address(_collateralContract)) {
            revert InvalidPath(address(_collateralContract), _path[_path.length - 1]);
        }

        // Add initial collateral
        if (_initialCollateralAmount > 0) {
            _addCollateral(msg.sender, _initialCollateralAmount, msg.sender);
        }

        // Debit borrowers account
        // setting recipient to address(this) means no transfer will happen
        uint256 _borrowShares = _borrowAsset(_borrowAmount.toUint128(), address(this));

        // Interactions
        _assetContract.approve(_swapperAddress, _borrowAmount);

        // Even though swappers are trusted, we verify the balance before and after swap
        uint256 _initialCollateralBalance = _collateralContract.balanceOf(address(this));
        ISwapper(_swapperAddress).swapExactTokensForTokens(
            _borrowAmount,
            _amountCollateralOutMin,
            _path,
            address(this),
            block.timestamp
        );
        uint256 _finalCollateralBalance = _collateralContract.balanceOf(address(this));

        // Note: VIOLATES CHECKS-EFFECTS-INTERACTION pattern, make sure function is NONREENTRANT
        // Effects: bookkeeping & write to state
        uint256 _amountCollateralOut = _finalCollateralBalance - _initialCollateralBalance;
        if (_amountCollateralOut < _amountCollateralOutMin) {
            revert SlippageTooHigh(_amountCollateralOutMin, _amountCollateralOut);
        }

        // address(this) as _sender means no transfer occurs as the pair has already received the collateral during swap
        _addCollateral(address(this), _amountCollateralOut, msg.sender);

        _totalCollateralBalance = _initialCollateralAmount + _amountCollateralOut;
        emit LeveragedPosition(
            msg.sender,
            _swapperAddress,
            _borrowAmount,
            _borrowShares,
            _initialCollateralAmount,
            _amountCollateralOut
        );
    }

    /// @notice The ```RepayAssetWithCollateral``` event is emitted whenever ```repayAssetWithCollateral()``` is invoked
    /// @param _borrower The borrower account for which the repayment is taking place
    /// @param _swapperAddress The address of the whitelisted swapper to use for token swaps
    /// @param _collateralToSwap The amount of Collateral Token to swap and use for repayment
    /// @param _amountAssetOut The amount of Asset Token which was repaid
    /// @param _sharesRepaid The number of Borrow Shares which were repaid
    event RepayAssetWithCollateral(
        address indexed _borrower,
        address _swapperAddress,
        uint256 _collateralToSwap,
        uint256 _amountAssetOut,
        uint256 _sharesRepaid
    );

    /// @notice The ```repayAssetWithCollateral``` function allows a borrower to repay their debt using existing collateral in contract
    /// @param _swapperAddress The address of the whitelisted swapper to use for token swaps
    /// @param _collateralToSwap The amount of Collateral Tokens to swap for Asset Tokens
    /// @param _amountAssetOutMin The minimum amount of Asset Tokens to receive during the swap
    /// @param _path An array containing the addresses of ERC20 tokens to swap.  Adheres to UniV2 style path params.
    /// @return _amountAssetOut The amount of Asset Tokens received for the Collateral Tokens, the amount the borrowers account was credited
    function repayAssetWithCollateral(
        address _swapperAddress,
        uint256 _collateralToSwap,
        uint256 _amountAssetOutMin,
        address[] calldata _path
    ) external nonReentrant isSolvent(msg.sender) returns (uint256 _amountAssetOut) {
        _addInterest();
        _updateExchangeRate();

        IERC20 _assetContract = assetContract;
        IERC20 _collateralContract = collateralContract;

        if (!swappers[_swapperAddress]) {
            revert BadSwapper();
        }
        if (_path[0] != address(_collateralContract)) {
            revert InvalidPath(address(_collateralContract), _path[0]);
        }
        if (_path[_path.length - 1] != address(_assetContract)) {
            revert InvalidPath(address(_assetContract), _path[_path.length - 1]);
        }

        // Effects: bookkeeping & write to state
        // Debit users collateral balance in preparation for swap, setting _recipient to address(this) means no transfer occurs
        _removeCollateral(_collateralToSwap, address(this), msg.sender);

        // Interactions
        _collateralContract.approve(_swapperAddress, _collateralToSwap);

        // Even though swappers are trusted, we verify the balance before and after swap
        uint256 _initialAssetBalance = _assetContract.balanceOf(address(this));
        ISwapper(_swapperAddress).swapExactTokensForTokens(
            _collateralToSwap,
            _amountAssetOutMin,
            _path,
            address(this),
            block.timestamp
        );
        uint256 _finalAssetBalance = _assetContract.balanceOf(address(this));

        // Note: VIOLATES CHECKS-EFFECTS-INTERACTION pattern, make sure function is NONREENTRANT
        // Effects: bookkeeping
        _amountAssetOut = _finalAssetBalance - _initialAssetBalance;
        if (_amountAssetOut < _amountAssetOutMin) {
            revert SlippageTooHigh(_amountAssetOutMin, _amountAssetOut);
        }

        VaultAccount memory _totalBorrow = totalBorrow;
        uint256 _sharesToRepay = _totalBorrow.toShares(_amountAssetOut, false);

        // Effects: write to state
        // Note: setting _payer to address(this) means no actual transfer will occur.  Contract already has funds
        _repayAsset(_totalBorrow, _amountAssetOut.toUint128(), _sharesToRepay.toUint128(), address(this), msg.sender);

        emit RepayAssetWithCollateral(msg.sender, _swapperAddress, _collateralToSwap, _amountAssetOut, _sharesToRepay);
    }
}

File 8 of 21 : VaultAccount.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.16;

struct VaultAccount {
    uint128 amount; // Total amount, analogous to market cap
    uint128 shares; // Total shares, analogous to shares outstanding
}

/// @title VaultAccount Library
/// @author Drake Evans (Frax Finance) github.com/drakeevans, modified from work by @Boring_Crypto github.com/boring_crypto
/// @notice Provides a library for use with the VaultAccount struct, provides convenient math implementations
/// @dev Uses uint128 to save on storage
library VaultAccountingLibrary {
    /// @notice Calculates the shares value in relationship to `amount` and `total`
    /// @dev Given an amount, return the appropriate number of shares
    function toShares(
        VaultAccount memory total,
        uint256 amount,
        bool roundUp
    ) internal pure returns (uint256 shares) {
        if (total.amount == 0) {
            shares = amount;
        } else {
            shares = (amount * total.shares) / total.amount;
            if (roundUp && (shares * total.amount) / total.shares < amount) {
                shares = shares + 1;
            }
        }
    }

    /// @notice Calculates the amount value in relationship to `shares` and `total`
    /// @dev Given a number of shares, returns the appropriate amount
    function toAmount(
        VaultAccount memory total,
        uint256 shares,
        bool roundUp
    ) internal pure returns (uint256 amount) {
        if (total.shares == 0) {
            amount = shares;
        } else {
            amount = (shares * total.amount) / total.shares;
            if (roundUp && (amount * total.shares) / total.amount < shares) {
                amount = amount + 1;
            }
        }
    }
}

File 9 of 21 : SafeERC20.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.16;

import "@openzeppelin/contracts/interfaces/IERC20.sol";
import { SafeERC20 as OZSafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

// solhint-disable avoid-low-level-calls
// solhint-disable max-line-length

/// @title SafeERC20 provides helper functions for safe transfers as well as safe metadata access
/// @author Library originally written by @Boring_Crypto github.com/boring_crypto, modified by Drake Evans (Frax Finance) github.com/drakeevans
/// @dev original: https://github.com/boringcrypto/BoringSolidity/blob/fed25c5d43cb7ce20764cd0b838e21a02ea162e9/contracts/libraries/BoringERC20.sol
library SafeERC20 {
    bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol()
    bytes4 private constant SIG_NAME = 0x06fdde03; // name()
    bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()

    function returnDataToString(bytes memory data) internal pure returns (string memory) {
        if (data.length >= 64) {
            return abi.decode(data, (string));
        } else if (data.length == 32) {
            uint8 i = 0;
            while (i < 32 && data[i] != 0) {
                i++;
            }
            bytes memory bytesArray = new bytes(i);
            for (i = 0; i < 32 && data[i] != 0; i++) {
                bytesArray[i] = data[i];
            }
            return string(bytesArray);
        } else {
            return "???";
        }
    }

    /// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.
    /// @param token The address of the ERC-20 token contract.
    /// @return (string) Token symbol.
    function safeSymbol(IERC20 token) internal view returns (string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL));
        return success ? returnDataToString(data) : "???";
    }

    /// @notice Provides a safe ERC20.name version which returns '???' as fallback string.
    /// @param token The address of the ERC-20 token contract.
    /// @return (string) Token name.
    function safeName(IERC20 token) internal view returns (string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME));
        return success ? returnDataToString(data) : "???";
    }

    /// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.
    /// @param token The address of the ERC-20 token contract.
    /// @return (uint8) Token decimals.
    function safeDecimals(IERC20 token) internal view returns (uint8) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS));
        return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
    }

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        OZSafeERC20.safeTransfer(token, to, value);
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        OZSafeERC20.safeTransferFrom(token, from, to, value);
    }
}

File 10 of 21 : IERC4626.sol
// SPDX-License-Identifier: ISC
pragma solidity >=0.8.16;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    function asset() external view returns (address);

    function convertToAssets(uint256 shares) external view returns (uint256);

    function convertToShares(uint256 assets) external view returns (uint256);

    function maxDeposit(address) external view returns (uint256);

    function maxMint(address) external view returns (uint256);

    function maxRedeem(address owner) external view returns (uint256);

    function maxWithdraw(address owner) external view returns (uint256);

    function previewDeposit(uint256 assets) external view returns (uint256);

    function previewMint(uint256 shares) external view returns (uint256);

    function previewRedeem(uint256 shares) external view returns (uint256);

    function previewWithdraw(uint256 assets) external view returns (uint256);

    function totalAssets() external view returns (uint256);

    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);
}

File 11 of 21 : IFraxlendWhitelist.sol
// SPDX-License-Identifier: ISC
pragma solidity >=0.8.16;

interface IFraxlendWhitelist {
    function fraxlendDeployerWhitelist(address) external view returns (bool);

    function oracleContractWhitelist(address) external view returns (bool);

    function owner() external view returns (address);

    function rateContractWhitelist(address) external view returns (bool);

    function renounceOwnership() external;

    function setFraxlendDeployerWhitelist(address[] calldata _addresses, bool _bool) external;

    function setOracleContractWhitelist(address[] calldata _addresses, bool _bool) external;

    function setRateContractWhitelist(address[] calldata _addresses, bool _bool) external;

    function transferOwnership(address newOwner) external;
}

File 12 of 21 : IRateCalculator.sol
// SPDX-License-Identifier: ISC
pragma solidity >=0.8.16;

interface IRateCalculator {
    function name() external pure returns (string memory);

    function requireValidInitData(bytes calldata _initData) external pure;

    function getConstants() external pure returns (bytes memory _calldata);

    function getNewRate(bytes calldata _data, bytes calldata _initData) external pure returns (uint64 _newRatePerSec);
}

File 13 of 21 : ISwapper.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.16;

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

File 14 of 21 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

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

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

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

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

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

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

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

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

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

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

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

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

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

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

        return true;
    }

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

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

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

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

File 15 of 21 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

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

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

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

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

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

File 16 of 21 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract 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() {
        _transferOwnership(_msgSender());
    }

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

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

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(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");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 17 of 21 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such 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.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 18 of 21 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

File 19 of 21 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";

File 20 of 21 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.sol";

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

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

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

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

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

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

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

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

File 21 of 21 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

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

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

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

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

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

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

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

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"bytes","name":"_configData","type":"bytes"},{"internalType":"bytes","name":"_immutables","type":"bytes"},{"internalType":"uint256","name":"_maxLTV","type":"uint256"},{"internalType":"uint256","name":"_liquidationFee","type":"uint256"},{"internalType":"uint256","name":"_maturityDate","type":"uint256"},{"internalType":"uint256","name":"_penaltyRate","type":"uint256"},{"internalType":"bool","name":"_isBorrowerWhitelistActive","type":"bool"},{"internalType":"bool","name":"_isLenderWhitelistActive","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BadProtocolFee","type":"error"},{"inputs":[],"name":"BadSwapper","type":"error"},{"inputs":[],"name":"BorrowerSolvent","type":"error"},{"inputs":[],"name":"BorrowerWhitelistRequired","type":"error"},{"inputs":[{"internalType":"uint256","name":"_borrow","type":"uint256"},{"internalType":"uint256","name":"_collateral","type":"uint256"},{"internalType":"uint256","name":"_exchangeRate","type":"uint256"}],"name":"Insolvent","type":"error"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"uint256","name":"_request","type":"uint256"}],"name":"InsufficientAssetsInContract","type":"error"},{"inputs":[{"internalType":"address","name":"_expected","type":"address"},{"internalType":"address","name":"_actual","type":"address"}],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"NameEmpty","type":"error"},{"inputs":[],"name":"NotDeployer","type":"error"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"NotOnWhitelist","type":"error"},{"inputs":[],"name":"OnlyApprovedBorrowers","type":"error"},{"inputs":[],"name":"OnlyApprovedLenders","type":"error"},{"inputs":[],"name":"OnlyTimeLock","type":"error"},{"inputs":[{"internalType":"address","name":"_oracle","type":"address"}],"name":"OracleLTEZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"_blockTimestamp","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"PastDeadline","type":"error"},{"inputs":[],"name":"PastMaturity","type":"error"},{"inputs":[],"name":"PriceTooLarge","type":"error"},{"inputs":[],"name":"ProtocolOrOwnerOnly","type":"error"},{"inputs":[{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_actual","type":"uint256"}],"name":"SlippageTooHigh","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_sender","type":"address"},{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_collateralAmount","type":"uint256"}],"name":"AddCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_interestEarned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_rate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_deltaTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_feesAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_feesShare","type":"uint256"}],"name":"AddInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":true,"internalType":"address","name":"_receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"_borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_sharesAdded","type":"uint256"}],"name":"BorrowAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"_newFee","type":"uint32"}],"name":"ChangeFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"address","name":"_swapperAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_borrowShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_initialCollateralAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountCollateralOut","type":"uint256"}],"name":"LeveragedPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_collateralForLiquidator","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_sharesToLiquidate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountLiquidatorToRepay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_sharesToAdjust","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountToAdjust","type":"uint256"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"_receiver","type":"address"},{"indexed":true,"internalType":"address","name":"_borrower","type":"address"}],"name":"RemoveCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_payer","type":"address"},{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountToRepay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"RepayAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"address","name":"_swapperAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_collateralToSwap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountAssetOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_sharesRepaid","type":"uint256"}],"name":"RepayAssetWithCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_address","type":"address"},{"indexed":false,"internalType":"bool","name":"_approval","type":"bool"}],"name":"SetApprovedBorrower","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_address","type":"address"},{"indexed":false,"internalType":"bool","name":"_approval","type":"bool"}],"name":"SetApprovedLender","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_swapper","type":"address"},{"indexed":false,"internalType":"bool","name":"_approval","type":"bool"}],"name":"SetSwapper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"_newAddress","type":"address"}],"name":"SetTimeLock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_rate","type":"uint256"}],"name":"UpdateExchangeRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_ratePerSec","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_deltaTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_utilizationRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newRatePerSec","type":"uint256"}],"name":"UpdateRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"_shares","type":"uint128"},{"indexed":false,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountToTransfer","type":"uint256"}],"name":"WithdrawFees","type":"event"},{"inputs":[],"name":"CIRCUIT_BREAKER_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COMPTROLLER_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPLOYER_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FRAXLEND_WHITELIST_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TIME_LOCK_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"address","name":"_borrower","type":"address"}],"name":"addCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addInterest","outputs":[{"internalType":"uint256","name":"_interestEarned","type":"uint256"},{"internalType":"uint256","name":"_feesAmount","type":"uint256"},{"internalType":"uint256","name":"_feesShare","type":"uint256"},{"internalType":"uint64","name":"_newRate","type":"uint64"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedBorrowers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedLenders","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_borrowAmount","type":"uint256"},{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"borrowAsset","outputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowerWhitelistActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_newFee","type":"uint32"}],"name":"changeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cleanLiquidationFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralContract","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRateInfo","outputs":[{"internalType":"uint64","name":"lastBlock","type":"uint64"},{"internalType":"uint64","name":"feeToProtocolRate","type":"uint64"},{"internalType":"uint64","name":"lastTimestamp","type":"uint64"},{"internalType":"uint64","name":"ratePerSec","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"_sharesReceived","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dirtyLiquidationFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exchangeRateInfo","outputs":[{"internalType":"uint32","name":"lastTimestamp","type":"uint32"},{"internalType":"uint224","name":"exchangeRate","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConstants","outputs":[{"internalType":"uint256","name":"_LTV_PRECISION","type":"uint256"},{"internalType":"uint256","name":"_LIQ_PRECISION","type":"uint256"},{"internalType":"uint256","name":"_UTIL_PREC","type":"uint256"},{"internalType":"uint256","name":"_FEE_PRECISION","type":"uint256"},{"internalType":"uint256","name":"_EXCHANGE_PRECISION","type":"uint256"},{"internalType":"uint64","name":"_DEFAULT_INT","type":"uint64"},{"internalType":"uint16","name":"_DEFAULT_PROTOCOL_FEE","type":"uint16"},{"internalType":"uint256","name":"_MAX_PROTOCOL_FEE","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getImmutableAddressBool","outputs":[{"internalType":"address","name":"_assetContract","type":"address"},{"internalType":"address","name":"_collateralContract","type":"address"},{"internalType":"address","name":"_oracleMultiply","type":"address"},{"internalType":"address","name":"_oracleDivide","type":"address"},{"internalType":"address","name":"_rateContract","type":"address"},{"internalType":"address","name":"_DEPLOYER_CONTRACT","type":"address"},{"internalType":"address","name":"_COMPTROLLER_ADDRESS","type":"address"},{"internalType":"address","name":"_FRAXLEND_WHITELIST","type":"address"},{"internalType":"bool","name":"_borrowerWhitelistActive","type":"bool"},{"internalType":"bool","name":"_lenderWhitelistActive","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getImmutableUint256","outputs":[{"internalType":"uint256","name":"_oracleNormalization","type":"uint256"},{"internalType":"uint256","name":"_maxLTV","type":"uint256"},{"internalType":"uint256","name":"_cleanLiquidationFee","type":"uint256"},{"internalType":"uint256","name":"_maturityDate","type":"uint256"},{"internalType":"uint256","name":"_penaltyRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPairAccounting","outputs":[{"internalType":"uint128","name":"_totalAssetAmount","type":"uint128"},{"internalType":"uint128","name":"_totalAssetShares","type":"uint128"},{"internalType":"uint128","name":"_totalBorrowAmount","type":"uint128"},{"internalType":"uint128","name":"_totalBorrowShares","type":"uint128"},{"internalType":"uint256","name":"_totalCollateral","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"getUserSnapshot","outputs":[{"internalType":"uint256","name":"_userAssetShares","type":"uint256"},{"internalType":"uint256","name":"_userBorrowShares","type":"uint256"},{"internalType":"uint256","name":"_userCollateralBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"address[]","name":"_approvedBorrowers","type":"address[]"},{"internalType":"address[]","name":"_approvedLenders","type":"address[]"},{"internalType":"bytes","name":"_rateInitCallData","type":"bytes"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lenderWhitelistActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_swapperAddress","type":"address"},{"internalType":"uint256","name":"_borrowAmount","type":"uint256"},{"internalType":"uint256","name":"_initialCollateralAmount","type":"uint256"},{"internalType":"uint256","name":"_amountCollateralOutMin","type":"uint256"},{"internalType":"address[]","name":"_path","type":"address[]"}],"name":"leveragedPosition","outputs":[{"internalType":"uint256","name":"_totalCollateralBalance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"_sharesToLiquidate","type":"uint128"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"address","name":"_borrower","type":"address"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"_collateralForLiquidator","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maturityDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLTV","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleDivide","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleMultiply","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleNormalization","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"penaltyRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rateContract","outputs":[{"internalType":"contract IRateCalculator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rateInitCallData","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"_amountToReturn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"removeCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_borrower","type":"address"}],"name":"repayAsset","outputs":[{"internalType":"uint256","name":"_amountToRepay","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapperAddress","type":"address"},{"internalType":"uint256","name":"_collateralToSwap","type":"uint256"},{"internalType":"uint256","name":"_amountAssetOutMin","type":"uint256"},{"internalType":"address[]","name":"_path","type":"address[]"}],"name":"repayAssetWithCollateral","outputs":[{"internalType":"uint256","name":"_amountAssetOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_borrowers","type":"address[]"},{"internalType":"bool","name":"_approval","type":"bool"}],"name":"setApprovedBorrowers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_lenders","type":"address[]"},{"internalType":"bool","name":"_approval","type":"bool"}],"name":"setApprovedLenders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"},{"internalType":"bool","name":"_approval","type":"bool"}],"name":"setSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAddress","type":"address"}],"name":"setTimeLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"swappers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"}],"name":"toAssetAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"}],"name":"toAssetShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"}],"name":"toBorrowAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"}],"name":"toBorrowShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAsset","outputs":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrow","outputs":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateExchangeRate","outputs":[{"internalType":"uint256","name":"_exchangeRate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userBorrowShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userCollateralBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"_shares","type":"uint128"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawFees","outputs":[{"internalType":"uint256","name":"_amountToTransfer","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

6102a08060405234620006695762006b46803803809162000021828562000a51565b8339810161010082820312620006695781516001600160401b0381116200066957816200005091840162000a75565b602083015190916001600160401b03821162000669576200007391840162000a75565b604083015192606081015160808201519360a083015195620000a660e06200009e60c0870162000aec565b950162000aec565b94604051620000b58162000a35565b6000815260405190620000c88262000a35565b600082528051906001600160401b03821162000935578190620000ed60035462000afa565b601f8111620009e2575b50602090601f831160011462000957576000926200094b575b50508160011b916000199060031b1c1916176003555b8051906001600160401b038211620009355781906200014760045462000afa565b601f8111620008d6575b50602090601f831160011462000847576000926200083b575b50508160011b916000199060031b1c1916176004555b6005543360018060a01b0382167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e06000604051a36001600160a81b0319163360ff60a01b1916176005556001600655600754620001dd9062000afa565b601f8111620007f9575b50600a640312e302e360dc1b016007556080818051810103126200066957620002136020820162000b50565b90620002226040820162000b50565b6200023e6080620002366060850162000b50565b930162000b50565b336101a0526001600160a01b039384166101c0529083166101e052600a80546001600160a01b031916928416929092179091551661020052815182019060e0838303126200066957620002946020840162000b50565b90620002a36040850162000b50565b94620002b26060860162000b50565b95620002c16080870162000b50565b9360a087015195620002d660c0890162000b50565b60e0890151909890916001600160401b03831162000669576200030192602080920192010162000a75565b506001600160a01b039081166080521660a052600c8054600160401b600160801b031916905561014081905280151560001982900462015f901116620007e35762015f9090620186a091829102046101605281101580620007da575b620007c85761012052610200516001600160a01b039081169490811615158062000747575b62000725576001600160a01b038216151580620006a4575b62000683576001600160a01b0390811660c05290811660e0526101009190915260405163f3a51aa160e01b815291166004820181905291602090829060249082905afa908115620006775760009162000630575b501562000618576101805261026052610280526102205261024052604051615fe062000b668239608051818181610a1201528181610bd401528181611979015281816120f2015281816124b301528181612c1a01528181614a9401528181614d0801528181614fd4015261544f015260a051818181610a370152818161194b015281816122650152818161246f01528181614dd901528181614e9a0152615492015260c051818181610a5f01528181611b3f0152614757015260e05181818161060f01528181610a8701526147830152610100518181816121f30152818161305f01526147b4015261012051818181612f7e015281816130820152614b4a01526101405181818161086c015281816130a801526150df015261016051818181611097015261529a015261018051818181610aaf01528181612e6301528181613b0d01526144f201526101a051818181610ad601528181611758015281816129930152613aac01526101c0518181816117030152611d3701526101e051818181610b0401528181610f5d015281816117940152612ff5015261020051818181610b3a0152612ea8015261022051818181612abe015281816130ce0152614679015261024051818181612afa015281816130f401526140e50152610260518181816108ca01528181610b6101528181611db101528181612d8c01526153c2015261028051818181610b8a01528181610ced015281816110d301528181611384015261153e0152615fe090f35b60249060405190630f19dca360e01b82526004820152fd5b90506020813d6020116200066e575b816200064e6020938362000a51565b810103126200066957620006629062000aec565b38620003ee565b600080fd5b3d91506200063f565b6040513d6000823e3d90fd5b604051630f19dca360e01b81526001600160a01b0383166004820152602490fd5b506040516373015f9360e11b81526001600160a01b0383166004820152602081602481895afa9081156200067757600091620006e3575b50156200039a565b90506020813d6020116200071c575b81620007016020938362000a51565b810103126200066957620007159062000aec565b38620006db565b3d9150620006f2565b604051630f19dca360e01b81526001600160a01b039091166004820152602490fd5b506040516373015f9360e11b81526001600160a01b0382166004820152602081602481895afa908115620006775760009162000786575b501562000382565b90506020813d602011620007bf575b81620007a46020938362000a51565b810103126200066957620007b89062000aec565b386200077e565b3d915062000795565b604051631d74772960e31b8152600490fd5b5085156200035d565b634e487b7160e01b600052601160045260246000fd5b60076000526200083490601f0160051c7fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889081019062000b37565b38620001e7565b0151905038806200016a565b6004600090815293507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b91905b601f1984168510620008ba576001945083601f19811610620008a0575b505050811b0160045562000180565b015160001960f88460031b161c1916905538808062000891565b8181015183556020948501946001909301929091019062000874565b600460005262000923907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f850160051c810191602086106200092a575b601f0160051c019062000b37565b3862000151565b909150819062000915565b634e487b7160e01b600052604160045260246000fd5b01519050388062000110565b600360009081527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b9350601f198516905b818110620009c95750908460019594939210620009af575b505050811b0160035562000126565b015160001960f88460031b161c19169055388080620009a0565b9293602060018192878601518155019501930162000988565b600360005262000a2e907fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f850160051c810191602086106200092a57601f0160051c019062000b37565b38620000f7565b602081019081106001600160401b038211176200093557604052565b601f909101601f19168101906001600160401b038211908210176200093557604052565b919080601f8401121562000669578251906001600160401b03821162000935576040519160209162000ab1601f8301601f191684018562000a51565b818452828287010111620006695760005b81811062000ad857508260009394955001015290565b858101830151848201840152820162000ac2565b519081151582036200066957565b90600182811c9216801562000b2c575b602083101462000b1657565b634e487b7160e01b600052602260045260246000fd5b91607f169162000b0a565b81811062000b43575050565b6000815560010162000b37565b51906001600160a01b0382168203620006695756fe60806040526004361015610013575b600080fd5b60003560e01c806253f733146105da57806302ce728f146105d157806306fdde03146105c8578063095ea7b3146105bf5780630d09365c146105b657806311a2e4bc146105ad57806318160ddd146105a45780632165d72f1461059b57806323b872dd14610592578063313ce5671461058957806336fad62d1461058057806338d52e0f14610577578063395093511461056e5780633cc32aba146105655780633d417d2d1461055c5780633f2617cb146105535780633f4ba83a1461054a578063404ffa7a146105415780634732428c146105385780634962e4941461052f5780634ac8eb5f146105265780634fd422df1461051d57806354fd4d501461051457806356968f971461050b5780635c975abb14610502578063657a409c146104f95780636e553f65146104f057806370a08231146104e7578063715018a6146104de578063721b0a47146104d55780638142dd53146104cc5780638285ef40146104c35780638456cb59146104ba578063891682d2146104b15780638cad7fbe146104a85780638da5cb5b1461049f57806394e301e01461049657806395d14ca81461048d57806395d89b411461048457806399024fc11461047b5780639a295e73146104725780639bdff2e614610469578063a053db6814610460578063a457c2d714610457578063a9059cbb1461044e578063ad0c3bb514610445578063afa85de61461043c578063b054898b14610433578063b5af30621461042a578063b68d0a0914610421578063ba08765214610418578063c10c92a11461040f578063c270a54414610406578063c6e1c7c9146103fd578063c7be9786146103f4578063c936c624146103eb578063ca2298fe146103e2578063cadac479146103d9578063cdd72d52146103d0578063d2a156e0146103c7578063d41ddc96146103be578063d59624b4146103b5578063d6b7494f146103ac578063daf33f2a146103a3578063dd62ed3e1461039a578063e5f13b1614610391578063eee2421914610388578063ef14900d1461037f578063f2fde38b14610376578063f384bd051461036d578063f9557ccb14610364578063fbaa8b851461035b578063fbbbf94c146103525763fea10d5d1461034a57600080fd5b61000e613046565b5061000e613019565b5061000e612fd4565b5061000e612fa1565b5061000e612f65565b5061000e612ecc565b5061000e612e87565b5061000e612e42565b5061000e612d37565b5061000e612cd1565b5061000e612b1d565b5061000e612ae1565b5061000e612aa5565b5061000e6129b7565b5061000e612972565b5061000e61290e565b5061000e61289e565b5061000e6123b4565b5061000e612370565b5061000e612289565b5061000e612244565b5061000e612216565b5061000e6121da565b5061000e611f8a565b5061000e611f2a565b5061000e611eeb565b5061000e611d9f565b5061000e611d5b565b5061000e611d16565b5061000e611ceb565b5061000e611c37565b5061000e611b8a565b5061000e611b1e565b5061000e611ac5565b5061000e611a3a565b5061000e611931565b5061000e6118ea565b5061000e6118bc565b5061000e611894565b5061000e611850565b5061000e6117be565b5061000e6116e6565b5061000e6116b3565b5061000e6115bb565b5061000e6114ef565b5061000e611475565b5061000e611436565b5061000e611330565b5061000e611308565b5061000e6112e1565b5061000e6112ae565b5061000e611206565b5061000e611117565b5061000e6110f8565b5061000e6110ba565b5061000e61107e565b5061000e611043565b5061000e610f40565b5061000e610ea7565b5061000e610e34565b5061000e610cd9565b5061000e610bf8565b5061000e610bb3565b5061000e6109ef565b5061000e6109d2565b5061000e6108ef565b5061000e6108b1565b5061000e61088f565b5061000e610853565b5061000e6107f9565b5061000e6107c3565b5061000e6106cc565b5061000e610633565b5061000e6105ee565b600091031261000e57565b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e57600036600319011261000e5761065460026006541415613c7f565b60026006556020610663614718565b6001600655604051908152f35b60005b8381106106835750506000910152565b8181015183820152602001610673565b906020916106ac81518092818552858086019101610670565b601f01601f1916010190565b9060206106c9928181520190610693565b90565b503461000e576000806003193601126107af576040519080600b546106f081611156565b80855291600191808316908115610785575060011461072a575b6107268561071a818703826111e4565b604051918291826106b8565b0390f35b9250600b83527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db95b82841061076d57505050810160200161071a8261072661070a565b80546020858701810191909152909301928101610752565b8695506107269693506020925061071a94915060ff191682840152151560051b820101929361070a565b80fd5b6001600160a01b0381160361000e57565b503461000e57604036600319011261000e576107ee6004356107e4816107b2565b60243590336133d3565b602060405160018152f35b503461000e57600036600319011261000e5761081a60026006541415613c7f565b6002600655608067ffffffffffffffff610832613edd565b91600160069594955560405194855260208501526040840152166060820152f35b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461000e57600036600319011261000e576020600e5460801c604051908152f35b503461000e57600036600319011261000e5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b503461000e57606036600319011261000e5760043561090d816107b2565b602435610919816107b2565b604435906001600160a01b0383166000526001602052610950336040600020906001600160a01b0316600052602052604060002090565b549260018401610971575b61096593506132af565b60405160018152602090f35b82841061098d5761098883610965950333836133d3565b61095b565b60405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b503461000e57600036600319011261000e57602060405160128152f35b503461000e57600036600319011261000e576101406040516001600160a01b03807f0000000000000000000000000000000000000000000000000000000000000000168252807f0000000000000000000000000000000000000000000000000000000000000000166020830152807f0000000000000000000000000000000000000000000000000000000000000000166040830152807f0000000000000000000000000000000000000000000000000000000000000000166060830152807f00000000000000000000000000000000000000000000000000000000000000001660808301527f00000000000000000000000000000000000000000000000000000000000000001660a0820152610b3060c082017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169052565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660e08201527f000000000000000000000000000000000000000000000000000000000000000015156101008201527f00000000000000000000000000000000000000000000000000000000000000001515610120820152f35b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e57604036600319011261000e576107ee600435610c19816107b2565b336000526001602052610c43816040600020906001600160a01b0316600052602052604060002090565b54906024358201809211610c58575b336133d3565b610c606131af565b610c52565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b8015150361000e57565b604060031982011261000e576004359067ffffffffffffffff821161000e57610ccb91600401610c65565b90916024356106c981610c96565b503461000e57610ce836610ca0565b9190917f000000000000000000000000000000000000000000000000000000000000000080610e0b575b610df957600092835b818110610d285784604051f35b610d3d90838415610dd2575b610d42576136e7565b610d1b565b610d4d81848761372b565b35610d57816107b2565b6001600160a01b03809116875260146020527f3bb51c63bf139c4bc98211d74c51aafae8b743fd3090bee8b6bfe2026678a250604091610da587848b209060ff801983541691151516179055565b610db084878a61372b565b3592610dbb846107b2565b51871515815292169180602081015b0390a26136e7565b50610dde81848761372b565b35610de8816107b2565b6001600160a01b0316331415610d34565b6040516395a584f760e01b8152600490fd5b5033600052601460205260ff604060002054161580610d12575060ff6040600020541615610d12565b503461000e57604036600319011261000e576020600435610663602435610e5a816107b2565b610e6960026006541415613c7f565b6002600655610e76613edd565b50505050610e826134e8565b610e8c8482615f30565b93610e9f610e9986614adf565b91614adf565b903392614edf565b503461000e57604036600319011261000e577fea1eefb4fd58778d7b274fe54045a9feeec8f2847899c2e71126d3a74d486da56040600435610ee8816107b2565b60243590610ef582610c96565b6001600160a01b0390610f0d8260055416331461311a565b1690816000526009602052610f3181846000209060ff801983541691151516179055565b825191825215156020820152a1005b503461000e57600036600319011261000e576001600160a01b03807f0000000000000000000000000000000000000000000000000000000000000000163314159081611034575b5061102257610f94613edd565b5050505060055460ff8160a01c1615610fdd5760ff60a01b19166005557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606490fd5b604051638f5b12a760e01b8152600490fd5b90506005541633141538610f87565b503461000e57604036600319011261000e57602061107660243561106681610c96565b60043561107161350e565b615f9c565b604051908152f35b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461000e57600036600319011261000e5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b503461000e57600036600319011261000e576020601054604051908152f35b503461000e57602036600319011261000e576001600160a01b0360043561113d816107b2565b1660005260126020526020604060002054604051908152f35b90600182811c92168015611186575b602083101461117057565b634e487b7160e01b600052602260045260246000fd5b91607f1691611165565b50634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff8211176111c357604052565b6111cb611190565b604052565b67ffffffffffffffff81116111c357604052565b90601f8019910116810190811067ffffffffffffffff8211176111c357604052565b503461000e576000806003193601126107af57604051908060075461122a81611156565b808552916001918083169081156107855750600114611253576107268561071a818703826111e4565b9250600783527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6885b82841061129657505050810160200161071a8261072661070a565b8054602085870181019190915290930192810161127b565b503461000e57604036600319011261000e5760206110766024356112d181610c96565b6004356112dc61350e565b615ec3565b503461000e57600036600319011261000e57602060ff60055460a01c166040519015158152f35b503461000e57600036600319011261000e5760206001600160a01b03600a5416604051908152f35b503461000e5760408060031936011261000e5760243590611350826107b2565b61135f60026006541415613c7f565b600260065561136c614677565b6114265761138260ff60055460a01c1615613534565b7f0000000000000000000000000000000000000000000000000000000000000000806113e5575b6113d5576113bc610726926004356149b5565b906113c76001600655565b519081529081906020820190565b516395a584f760e01b8152600490fd5b5033600052601460205260ff81600020541615806113a957506001600160a01b03821660005261142161141d8260002060ff90541690565b1590565b6113a9565b5163b063a8a560e01b8152600490fd5b503461000e57602036600319011261000e576001600160a01b0360043561145c816107b2565b1660005260006020526020604060002054604051908152f35b503461000e576000806003193601126107af576005546001600160a01b038116906114a133831461311a565b6001600160a01b03191660055581604051917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08284a3f35b600435906001600160801b038216820361000e57565b503461000e57606036600319011261000e576115096114d9565b60443590611516826107b2565b61152860ff60055460a01c1615613534565b61153760026006541415613c7f565b60026006557f000000000000000000000000000000000000000000000000000000000000000080611592575b610df957610726916115789160243590615025565b6115826001600655565b6040519081529081906020820190565b5033600052601460205260ff604060002054161580611563575060406000205460ff1615611563565b503461000e57602036600319011261000e5760043563ffffffff811680820361000e576115f060ff60055460a01c1615613534565b600a546001600160a01b031633036116a15761c350811161168f577f58a58c712558f3d6e20bed57421eb8f73048d881dea9e5bb80efb37c49680d1c9161167361168a9261163c613edd565b505050506fffffffffffffffff0000000000000000600c549160401b16906fffffffffffffffff0000000000000000191617600c55565b60405163ffffffff90911681529081906020820190565b0390a1005b60405163da0afa5760e01b8152600490fd5b604051633b6b86b160e01b8152600490fd5b503461000e57600036600319011261000e57600f54604080516001600160801b038316815260809290921c602083015290f35b503461000e57600036600319011261000e576001600160a01b03807f0000000000000000000000000000000000000000000000000000000000000000163314159081611791575b81611781575b81611754575b5061102257611746613edd565b5050505061175261374d565b005b90507f00000000000000000000000000000000000000000000000000000000000000001633141538611739565b8091506005541633141590611733565b337f000000000000000000000000000000000000000000000000000000000000000082161415915061172d565b503461000e57602036600319011261000e576004356117dc816107b2565b600a54906001600160a01b03908183168033036116a157604080516001600160a01b03928316815291831660208301527f582d6cc2f042c43e00e0dd5c187f575daac294216d2afa075d9e1e27b0a40a9491a173ffffffffffffffffffffffffffffffffffffffff19909216911617600a55005b503461000e57602036600319011261000e576001600160a01b03600435611876816107b2565b166000526009602052602060ff604060002054166040519015158152f35b503461000e57600036600319011261000e5760206001600160a01b0360055416604051908152f35b503461000e57604036600319011261000e5760206110766024356118df81610c96565b6004356110716134e8565b503461000e57600036600319011261000e576080600c546040519067ffffffffffffffff8082168352808260401c16602084015281841c16604083015260c01c6060820152f35b503461000e57600036600319011261000e5761072661196f7f00000000000000000000000000000000000000000000000000000000000000006159f8565b61071a602e61199d7f00000000000000000000000000000000000000000000000000000000000000006159f8565b926040519384917f467261786c656e645631202d200000000000000000000000000000000000000060208401526119de815180926020602d87019101610670565b8201602f60f81b602d8201526119fd8251809360208785019101610670565b0103600e8101845201826111e4565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020838186019501011161000e57565b503461000e57608036600319011261000e5767ffffffffffffffff60043581811161000e57611a6d903690600401611a0c565b9060243583811161000e57611a86903690600401610c65565b9060443585811161000e57611a9f903690600401610c65565b92909160643596871161000e57611abd611752973690600401611a0c565b969095613a9c565b503461000e57600036600319011261000e57610100604051620186a08082528060208301528060408301526060820152670de0b6b3a7640000608082015263096ba6c460a0820152600060c082015261c35060e0820152f35b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b60209067ffffffffffffffff8111611b7d575b60051b0190565b611b85611190565b611b76565b503461000e5760a036600319011261000e57600435611ba8816107b2565b6084359067ffffffffffffffff821161000e573660238301121561000e578160040135611bd481611b63565b92611be260405194856111e4565b81845260209160248386019160051b8301019136831161000e57602401905b828210611c1e57610726611582876064356044356024358a61538b565b8380918335611c2c816107b2565b815201910190611c01565b503461000e57604036600319011261000e57600435611c55816107b2565b60243590336000526001602052611c83816040600020906001600160a01b0316600052602052604060002090565b5491808310611c9857610965920390336133d3565b60405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608490fd5b503461000e57604036600319011261000e576107ee600435611d0c816107b2565b60243590336132af565b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e57602036600319011261000e576001600160a01b03600435611d81816107b2565b166000526014602052602060ff604060002054166040519015158152f35b503461000e57611dae36610ca0565b907f000000000000000000000000000000000000000000000000000000000000000080611ed2575b611ec057600092835b828110611dec5784604051f35b611e0190848515611e99575b611e06576136e7565b611ddf565b6001600160a01b03611e1982868661372b565b35611e23816107b2565b16865260136020526040611e45868289209060ff801983541691151516179055565b7f9c798cce2c4fdbec95a1a6dbba64db726912274dac938f44e36bce9b779cfee8611e88611e7c611e7785898961372b565b613743565b6001600160a01b031690565b915187151581528060208101610dca565b50611ea581858561372b565b35611eaf816107b2565b6001600160a01b0316331415611df8565b6040516342f41ddd60e11b8152600490fd5b5033600052601360205260ff6040600020541615611dd6565b503461000e57602036600319011261000e576001600160a01b03600435611f11816107b2565b1660005260116020526020604060002054604051908152f35b503461000e57602036600319011261000e576001600160a01b03600435611f50816107b2565b1660005260006020526060604060002054601260205260406000205460116020526040600020549060405192835260208301526040820152f35b503461000e57606036600319011261000e57600435602435611fab816107b2565b604435611fb7816107b2565b611fc660026006541415613c7f565b6002600655611fd3613edd565b50505050611fdf61350e565b91611fea8484615f6b565b92611ffd611ff785614adf565b95614adf565b916001600160a01b03918285169485330361216d575b61202461201e6134e8565b83613c61565b916001600160801b0390818a16938481106121405750926120ef7ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db9593612116936120e7876120996107269f9b998c61208761208c92516001600160801b031690565b613580565b6001600160801b03168252565b6120c38c6120b6602084019161208783516001600160801b031690565b6001600160801b03169052565b6001600160801b038151169060206001600160801b031991015160801b1617600e55565b8916906135b5565b827f0000000000000000000000000000000000000000000000000000000000000000615c08565b604080516001600160801b0395861681529590941660208601521692339290a46115826001600655565b6040516362ddb6d760e11b815260048101919091526001600160801b038b166024820152604490fd5b0390fd5b6121a63361218e836001600160a01b03166000526001602052604060002090565b906001600160a01b0316600052602052604060002090565b54600181016121b6575b50612013565b6121cd6121d4916001600160801b038816906135a8565b33836133d3565b386121b0565b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461000e57604036600319011261000e57602061107660243561223981610c96565b6004356112dc6134e8565b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e576000806003193601126107af5760405190806008546122ad81611156565b8085529160019180831690811561234657506001146122eb575b610726856122d7818703826111e4565b604051918291602083526020830190610693565b9250600883527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee35b82841061232e5750505081016020016122d7826107266122c7565b80546020858701810191909152909301928101612313565b869550610726969350602092506122d794915060ff191682840152151560051b82010192936122c7565b503461000e57602036600319011261000e576001600160a01b03600435612396816107b2565b166000526013602052602060ff604060002054166040519015158152f35b503461000e57608036600319011261000e57600480356123d3816107b2565b602435916044359160643567ffffffffffffffff811161000e576123fa9036908401610c65565b61240c60026006979397541415613c7f565b6002600655612419613edd565b50505050612425614718565b5061245061141d612449856001600160a01b03166000526009602052604060002090565b5460ff1690565b61288d57612461611e77828861371a565b6001600160a01b03918291827f000000000000000000000000000000000000000000000000000000000000000016938491160361287b57806124b0611e776124a98594613599565b838c61372b565b827f0000000000000000000000000000000000000000000000000000000000000000169384911603612831579161252c6125f79492888a956124f333308b614e1e565b8960409d8e94855163095ea7b360e01b81528c818060209a8b93888b8401602090939291936001600160a01b0360408201951681520152565b03816000809e5af18015612824575b6127f7575b508551998c6370a0823160e01b97888d52898d8c81806125738d309083019190916001600160a01b036020820193169052565b03915afa9c8d156127ea575b8c9d6127b1575b50928998979694926125b48d979593889451998a98899788956338ed173960e01b875242943094880161590b565b0393165af180156127a4575b612782575b508b51908152308a820190815290938491829081906020015b03915afa928315612775575b92612748575b50506135a8565b938085106127255750837fe947f0f9b6255bdcf76d13d1257d34fbe380e0d5d4daa75e61c783a41e1607ba9161268561262e6134e8565b61265861263b8583615e31565b9161264586614adf565b3391309161265286614adf565b91614edf565b8851938493339785909493926060926001600160a01b036080840197168352602083015260408201520152565b0390a2600d5460201c61269b61141d8233614b48565b6126ae5761072683856113c76001600655565b6121699192506126d56126bf6134e8565b3360009081526012602052604090205490615f30565b936126f3336001600160a01b03166000526011602052604060002090565b549051633b49de0f60e21b815293840194855260208501526001600160e01b0316604084015290918291606090910190565b8551633b5d56ed60e11b8152808501918252602082018690529081906040010390fd5b6127679250803d1061276e575b61275f81836111e4565b810190615817565b38806125f0565b503d612755565b61277d6139a3565b6125ea565b61279d903d8088833e61279581836111e4565b810190615826565b50386125c5565b6127ac6139a3565b6125c0565b8c9694919d50926125b487936127d88d9c9b9a98968d803d1061276e5761275f81836111e4565b9f939698509350509294969798612586565b6127f26139a3565b61257f565b61281690883d8a1161281d575b61280e81836111e4565b810190615802565b5038612540565b503d612804565b61282c6139a3565b61253b565b8661216961284d611e778c856128478997613599565b9161372b565b6040805163b0b3262d60e01b81526001600160a01b0395861694810194855294909116602084015283920190565b8561216961284d611e7786948c61371a565b604051631311dc6d60e01b81528490fd5b503461000e57604036600319011261000e576024356128bc816107b2565b6128cb60026006541415613c7f565b60026006556128d8614677565b6128fc576128f5906128e8613edd565b5050505060043533614d5c565b6001600655005b60405163b063a8a560e01b8152600490fd5b503461000e57600036600319011261000e5760a061292a61350e565b6001600160801b038060208184511693015116906129466134e8565b906020818351169201511690601054926040519485526020850152604084015260608301526080820152f35b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e57604036600319011261000e57612a166024356129d8816107b2565b6129e760026006541415613c7f565b60026006556129f4613edd565b50505050336000526012602052604060002054612a97575b3390600435614e1e565b600d5460201c612a2961141d8233614b48565b612a37576117526001600655565b612a426126bf6134e8565b612169612a62336001600160a01b03166000526011602052604060002090565b54604051633b49de0f60e21b8152600481019390935260248301526001600160e01b0390921660448201529081906064820190565b612a9f614718565b50612a0c565b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461000e57604036600319011261000e57612b376114d9565b602435612b43816107b2565b612b596001600160a01b0360055416331461311a565b81612b6261350e565b612b6a6134e8565b936001600160801b0380911615612c9c575b80831690612b94612b8d8385615f30565b9684613c61565b868110612c7b57610726877f14f6e172cd596e9f9c5d24e2d4010daa24f8f65f9274b259b66517b306c617b98888612c13896120c38a612bf1612be48c8a1661208786516001600160801b031690565b6001600160801b03168452565b612c0d602084016120b68761208783516001600160801b031690565b306135b5565b612c3e84837f0000000000000000000000000000000000000000000000000000000000000000615c08565b604080516001600160801b039290921682526001600160a01b03929092166020820152908101839052606090a16040519081529081906020820190565b6040516362ddb6d760e11b8152600481019190915260248101879052604490fd5b9150612ccb612cbe306001600160a01b03166000526000602052604060002090565b546001600160801b031690565b91612b7c565b503461000e57604036600319011261000e576020612d2e600435612cf4816107b2565b6001600160a01b0360243591612d09836107b2565b16600052600183526040600020906001600160a01b0316600052602052604060002090565b54604051908152f35b503461000e57606036600319011261000e57602435604435612d58816107b2565b612d60614677565b6128fc57612d7660ff60055460a01c1615613534565b612d8560026006541415613c7f565b60026006557f000000000000000000000000000000000000000000000000000000000000000080612e29575b611ec057612de591612dc1613edd565b50505050612dcd614718565b5080612e17575b50612de0600435614adf565b614c15565b600d5460201c90612df961141d8333614b48565b612e0b57610726906115826001600655565b50612a426126bf6134e8565b612e2390339033614d5c565b38612dd4565b5033600052601360205260ff6040600020541615612db1565b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e57602036600319011261000e57600435612eea816107b2565b6001600160a01b03612f018160055416331461311a565b811615612f115761175290613165565b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461000e57600036600319011261000e57600e54604080516001600160801b038316815260809290921c602083015290f35b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e57600036600319011261000e576040600d5481519063ffffffff8116825260201c6020820152f35b503461000e57600036600319011261000e5760a06040517f000000000000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060208201527f000000000000000000000000000000000000000000000000000000000000000060408201527f000000000000000000000000000000000000000000000000000000000000000060608201527f00000000000000000000000000000000000000000000000000000000000000006080820152f35b1561312157565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b600554906001600160a01b0380911691826001600160a01b0319821617600555167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e06000604051a3565b50634e487b7160e01b600052601160045260246000fd5b90620186a09182018092116131d757565b6131df6131af565b565b90600182018092116131d757565b919082018092116131d757565b1561320357565b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b1561325b57565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b91906001600160a01b03908184169283156133805761335e827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9461337b9416966132fb8815156131fc565b6133448461331c836001600160a01b03166000526000602052604060002090565b5461332982821015613254565b03916001600160a01b03166000526000602052604060002090565b556001600160a01b03166000526000602052604060002090565b6133698282546131ef565b90556040519081529081906020820190565b0390a3565b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b906001600160a01b039182811692831561349757821693841561344757806134367f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259461218e61337b956001600160a01b03166000526001602052604060002090565b556040519081529081906020820190565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b60405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b604051906134f5826111a7565b600f546001600160801b038116835260801c6020830152565b6040519061351b826111a7565b600e546001600160801b038116835260801c6020830152565b1561353b57565b60405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606490fd5b6001600160801b0391821690821603919082116131d757565b6000198101919082116131d757565b919082039182116131d757565b6001600160a01b038116908115613698576135e3816001600160a01b03166000526000602052604060002090565b5483811061364857837fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9261363360009661337b9403916001600160a01b03166000526000602052604060002090565b55611582613643826002546135a8565b600255565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608490fd5b60019060001981146136f7570190565b6136ff6131af565b0190565b50634e487b7160e01b600052603260045260246000fd5b90156137235790565b6106c9613703565b919081101561373b5760051b0190565b611b85613703565b356106c9816107b2565b7401000000000000000000000000000000000000000060ff60a01b1960055461377c60ff8260a01c1615613534565b16176005557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1565b601f81116137b9575050565b600090600b82527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9906020601f850160051c83019410613814575b601f0160051c01915b82811061380957505050565b8181556001016137fd565b90925082906137f4565b601f811161382a575050565b600090600882527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3906020601f850160051c83019410613885575b601f0160051c01915b82811061387a57505050565b81815560010161386e565b9092508290613865565b919067ffffffffffffffff811161396e575b6138b5816138b0600b54611156565b6137ad565b6000601f82116001146138ef578192936000926138e4575b50508160011b916000199060031b1c191617600b55565b0135905038806138cd565b600b600052601f198216937f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db991805b868110613956575083600195961061393c575b505050811b01600b55565b0135600019600384901b60f8161c19169055388080613931565b9092602060018192868601358155019401910161391e565b613976611190565b6138a1565b90918060409360208452816020850152848401376000828201840152601f01601f1916010190565b506040513d6000823e3d90fd5b919067ffffffffffffffff8111613a8f575b6139d6816139d1600854611156565b61381e565b6000601f8211600114613a1057819293600092613a05575b50508160011b916000199060031b1c191617600855565b0135905038806139ee565b6008600052601f198216937ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee391805b868110613a775750836001959610613a5d575b505050811b01600855565b0135600019600384901b60f8161c19169055388080613a52565b90926020600181928686013581550194019101613a3f565b613a97611190565b6139c2565b9596956001600160a01b039593917f000000000000000000000000000000000000000000000000000000000000000087163303613c4f578015613c3d57613ae4600b54611156565b613c2c57613af19161388f565b60005b818110613bf65750505060005b818110613bae575050507f00000000000000000000000000000000000000000000000000000000000000001691823b1561000e5760405163453f31d560e01b8152613b719360009082908180613b5b88886004840161397b565b03915afa8015613ba1575b613b88575b506139b0565b613b79613edd565b50505050613b85614718565b50565b80613b95613b9b926111d0565b806105e3565b38613b6b565b613ba96139a3565b613b66565b80613bec613bdf613bc6611e77613bf195878961372b565b6001600160a01b03166000526014602052604060002090565b805460ff19166001179055565b6136e7565b613b01565b80613bec613bdf613c0e611e77613c2795878961372b565b6001600160a01b03166000526013602052604060002090565b613af4565b60405162dc149f60e41b8152600490fd5b604051631ff3ed9d60e01b8152600490fd5b604051638b906c9760e01b8152600490fd5b5190516001600160801b0391613c7b918316908316613580565b1690565b15613c8657565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b60405167ffffffffffffffff91906080810183811182821017613d16575b60405260608193600c54908082168452808260401c1660208501528160801c16604084015260c01c910152565b613d1e611190565b613ce9565b7da7c5ac471b4784230fcf80dc33721d53cddd6e04c059210385c67dfe32a08111600116613d54575b620186a00290565b613d5c6131af565b613d4c565b710154484932d2e725a5bbca17a3aba173d3d58111600116613d92575b6ec097ce7bc90715b34b9f10000000000290565b613d9a6131af565b613d7e565b8060001904821181151516613db2570290565b613dba6131af565b0290565b8115613dc8570490565b634e487b7160e01b600052601260045260246000fd5b9081602091031261000e575167ffffffffffffffff8116810361000e5790565b90613e1190604083526040830190610693565b81810360209283015260085460009291613e2a82611156565b80825291600190818116908115613ea55750600114613e4b575b5050505090565b9293509060086000527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee392846000945b838610613e915750505050010138808080613e44565b805485870183015294019385908201613e7b565b60ff191685840152505090151560051b0101905038808080613e44565b9190916001600160801b03808094169116019182116131d757565b60009060009081809181613eef613ccb565b9060408201613f06815167ffffffffffffffff1690565b67ffffffffffffffff92908316421461457557613f2161350e565b613f296134e8565b906001600160801b039260209380613f4a868601516001600160801b031690565b16158015614566575b156140305750505050506131df9291613f92613fa492613f7c61141d60055460ff9060a01c1690565b614021575b82421667ffffffffffffffff169052565b4316829067ffffffffffffffff169052565b67ffffffffffffffff815116906020810151916fffffffffffffffff00000000000000007fffffffffffffffff000000000000000000000000000000000000000000000000606077ffffffffffffffff00000000000000000000000000000000604086015160801b1694015160c01b169360401b16171717600c55565b63096ba6c46060860152613f81565b909194849c508094995096959296516140509067ffffffffffffffff1690565b67ffffffffffffffff1661406490426135a8565b98899487895161407a906001600160801b031690565b6001600160801b031661408c90613d23565b88516001600160801b03166001600160801b03166140a991613dbe565b94876140b3614677565b61417695901561442e57505050507fc63977c8e2362a31182dc8e89a52252f9836922738e0abcfc0de6924972eafe5857f0000000000000000000000000000000000000000000000000000000000000000169b8c955b61415560608c0197614123895167ffffffffffffffff1690565b926040519485948592936060929594608085019667ffffffffffffffff80941686526020860152604085015216910152565b0390a167ffffffffffffffff8b16845284421667ffffffffffffffff169052565b67ffffffffffffffff4384161686526141ea6141dc6141b46141ae6141a28b516001600160801b031690565b6001600160801b031690565b87613d9f565b6141d66141c9865167ffffffffffffffff1690565b67ffffffffffffffff1690565b90613d9f565b670de0b6b3a7640000900490565b9b8c938261420b6142056141a28c516001600160801b031690565b876131ef565b11158061440e575b614272575b5050505050506131df9291613fa461424e926001600160801b038151169060206001600160801b031991015160801b1617600e55565b6001600160801b038151169060206001600160801b031991015160801b1617600f55565b6142c46142b76142956142a761429a8d888b16938491516001600160801b031690565b613ec2565b6001600160801b03168d52565b89516001600160801b0316613ec2565b6001600160801b03168852565b8782015167ffffffffffffffff161680614367575b505050927f50225349cc7e3814c4fa5fe6baef7a3c4cac55e92c64b1f4a5d1ba55e65dcc826131df969593613fa4936143588c8e61432261424e9a5167ffffffffffffffff1690565b9460405195869586919267ffffffffffffffff608094979695929760a08501988552166020840152604083015260608201520152565b0390a1928294958b9238614218565b909361437c9296939c50809897959b50613d9f565b620186a09004998a928201998a5161439a906001600160801b031690565b6001600160801b03166143ad9085613d9f565b83516143c39086906001600160801b03166135a8565b6143cc91613dbe565b9a8b96871681516143e3906001600160801b031690565b906143ed91613ec2565b6001600160801b03169052614402863061459b565b939596928192956142d9565b50826144276142056141a28a516001600160801b031690565b1115614213565b806144e6926144cc866144be61448e6144886141c961447960607fc63977c8e2362a31182dc8e89a52252f9836922738e0abcfc0de6924972eafe59d015167ffffffffffffffff1690565b945167ffffffffffffffff1690565b436135a8565b8d6040519586948886019094939260609267ffffffffffffffff6080840197168352602083015260408201520152565b03601f1981018352826111e4565b60405180948192631b54c1a360e01b835260048301613dfe565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa928315614559575b9261452c575b50509b8c95614109565b61454b9250803d10614552575b61454381836111e4565b810190613dde565b8f80614522565b503d614539565b6145616139a3565b61451c565b5060055460a01c60ff16613f53565b96505050909450614594925060609150015167ffffffffffffffff1690565b8192829190565b6001600160a01b0381169182156146325760207fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91614600600094600254838101809111614625575b6002556001600160a01b03166000526000602052604060002090565b805490828201809211614618575b55604051908152a3565b6146206131af565b61460e565b61462d6131af565b6145e4565b60405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606490fd5b7f000000000000000000000000000000000000000000000000000000000000000080151590816146a5575090565b9050421190565b604051906146b9826111a7565b816020600d5463ffffffff81168352811c910152565b519069ffffffffffffffffffff8216820361000e57565b908160a091031261000e576146fa816146cf565b916020820151916040810151916106c96080606084015193016146cf565b6147206146ac565b9061472f825163ffffffff1690565b63ffffffff9081164214614990576001600160a01b036ec097ce7bc90715b34b9f10000000007f000000000000000000000000000000000000000000000000000000000000000082811680614918575b50507f0000000000000000000000000000000000000000000000000000000000000000918216918261486d575b506147d991507f000000000000000000000000000000000000000000000000000000000000000090613dbe565b926001600160e01b0380851161485b5784166001600160e01b0316602082015261482d9161480f904216829063ffffffff169052565b63ffffffff81511690602063ffffffff1991015160201b1617600d55565b6040518281527f4fc1b45960547ee95894b08a284c3c066cf5aca706a7420639c42c3ec2e118a490602090a1565b60405163057b0e2160e41b8152600490fd5b60a060049360405194858092633fabe5a360e21b82525afa92831561490b575b6000936148d6575b5060008313156148b357506147d9916148ad91613dbe565b386147ac565b6040516322ad99db60e21b81526001600160a01b03919091166004820152602490fd5b6148f891935060a03d8111614904575b6148f081836111e4565b8101906146e6565b50505090509138614895565b503d6148e6565b6149136139a3565b61488d565b604051633fabe5a360e21b8152925060a090839060049082905afa918215614983575b60009261495e575b5060008213156148b3575061495790613d61565b388061477f565b61497791925060a03d8111614904576148f081836111e4565b50505090509038614943565b61498b6139a3565b61493b565b50906149a960206106c99201516001600160e01b031690565b6001600160e01b031690565b91906149bf613edd565b505050506149cb61350e565b907fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d76001600160a01b03614a08614a028786615e31565b96614adf565b92614ab8614a1588614adf565b956001600160801b039081614a2d8882845116613ec2565b168152614a7482602083019281614a478c82875116613ec2565b168452614a56828c168861459b565b51166001600160801b03166001600160801b0319600e541617600e55565b5181600e54916001600160801b03199060801b16911617600e55851630337f0000000000000000000000000000000000000000000000000000000000000000615dce565b604080516001600160801b039586168152959094166020860152169233928190810161337b565b6001600160801b0390818111614af3571690565b60405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608490fd5b7f0000000000000000000000000000000000000000000000000000000000000000918215614c0d57614b9b6001600160a01b03614b836134e8565b93169283600052601260205260406000205490615f30565b918215614c04576000526011602052604060002054908115614bfb57614bcd670de0b6b3a764000091614be994613d9f565b04620186a0908060001904821181151516614bee575b02613dbe565b111590565b614bf66131af565b614be3565b50505050600090565b50505050600190565b505050600190565b9190614c1f6134e8565b92614c3184614c2c61350e565b613c61565b916001600160801b039485831693848110614d3357508061424e614c5886614c8c94615e67565b97614c71612be48761429586516001600160801b031690565b6120b660208401918a1661429583516001600160801b031690565b336000908152601260205260409020614ca68682546131ef565b90556001600160a01b03811692308403614d02575b5050604080516001600160801b0390921682526020820185905233917f01348584ec81ac7acd52b7d66d9ade986dd909f3d513881c190fc31c90527efe918190810161337b565b614d2c917f0000000000000000000000000000000000000000000000000000000000000000615c08565b3880614cbb565b6040516362ddb6d760e11b815260048101919091526001600160801b0384166024820152604490fd5b9060207fa32435755c235de2976ed44a75a2f85cb01faf0c894f639fe0c32bb9455fea8f916001600160a01b038095169485600052601183526040600020805490838201809211614e11575b55601054828101809111614e04575b60105584169381308603614dd1575b5050604051908152a3565b614dfd9130907f0000000000000000000000000000000000000000000000000000000000000000615dce565b3881614dc6565b614e0c6131af565b614db7565b614e196131af565b614da8565b6001600160a01b03809316928360005260116020526040600020805490838203918211614ed2575b55601054828103908111614ec5575b60105582169181308403614e94575b50506040519081527fbc290bb45104f73cf92115c9603987c3f8fd30c182a13603d8cffa49b5f5995260203392a4565b614ebe917f0000000000000000000000000000000000000000000000000000000000000000615c08565b3881614e64565b614ecd6131af565b614e55565b614eda6131af565b614e46565b909361337b7f9dc1449a0ff0c152e18e8289d865b47acc6e1b76b1ecb239c13d6ee22a9206a793614f8a6001600160801b039485614f208a82845116613580565b1681526020810186614f358582845116613580565b169052614f55856001600160a01b03166000526012602052604060002090565b8054908785168203918211614fff575b556001600160801b038151169060206001600160801b031991015160801b1617600f55565b6001600160a01b039384871696308803614fc9575b50506040519384931696839060209093929360408301946001600160801b03809216845216910152565b614ff89189169030907f0000000000000000000000000000000000000000000000000000000000000000615dce565b3880614f9f565b6150076131af565b614f65565b818103929160001380158285131691841216176131d757565b9291908042116152d65750615038613edd565b50505050615044614718565b9061504f8282614b48565b6152c457836151c2826131df9461510397610e9f61506b6134e8565b92615089856001600160a01b03166000526011602052604060002090565b54600061512461510c6150d86141dc6150be6150b88c6001600160a01b03166000526012602052604060002090565b54614adf565b966150d36001600160801b03809b168c615f6b565b613d9f565b9e8f6141d67f00000000000000000000000000000000000000000000000000000000000000006131c6565b620186a0900490565b61511e615118856152f6565b916152f6565b9061500c565b1380159c9061528c5750965b8786819d6151486151438886168a615f30565b614adf565b968860009687936151c9575b5050604080519485526001600160801b038087166020870152808a169186019190915280871660608601529091166080840152506001600160a01b0316907f35f432a64bd3767447a456650432406c6cacb885819947a202216eeea6820ecf908060a081015b0390a2613ec2565b3390614e1e565b7f35f432a64bd3767447a456650432406c6cacb885819947a202216eeea6820ecf94929750926151ba91615205886001600160a01b0396613580565b9889168061521a575b50915092829450615154565b6120879250816152336151436152439361525095615f6b565b938491516001600160801b031690565b6001600160801b03168c52565b61528561526982612087600e546001600160801b031690565b6001600160801b03166001600160801b0319600e541617600e55565b8a3861520e565b6152be9150615103906141d67f00000000000000000000000000000000000000000000000000000000000000006131c6565b96615130565b604051633af2cafd60e11b8152600490fd5b604051635ba2a8d560e01b81524260048201526024810191909152604490fd5b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116153205790565b60405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e743235360000000000000000000000000000000000000000000000006064820152608490fd5b909193615396614677565b6128fc576153a960026006541415613c7f565b60026006556153c060ff60055460a01c1615613534565b7f0000000000000000000000000000000000000000000000000000000000000000806157b1575b611ec0576153f3613edd565b505050506153ff614718565b5061542361141d612449846001600160a01b03166000526009602052604060002090565b61579f57615440615433826157ca565b516001600160a01b031690565b926001600160a01b03948594857f0000000000000000000000000000000000000000000000000000000000000000169687911603615792578461548f6154336154898651613599565b866157e0565b817f00000000000000000000000000000000000000000000000000000000000000001696879116036157515787615741575b6154ca83614adf565b306154d491614c15565b9360409788519263095ea7b360e01b84526020906004998a918387808d8c8783019161551592602090939291936001600160a01b0360408201951681520152565b0381600080995af16155d297908015615734575b615717575b508c898c8251986370a0823160e01b94858b528c898c80615561308d83019190916001600160a01b036020820193169052565b03818b5afa9b8c1561570a575b8b9c6156cd575b50978593819795938c80948d9c6155a86125de9f9d9b51988997889687946338ed173960e01b86524293309387016158a0565b0393165af180156156c0575b6156a6575b5051908152309281019283529485928391829160200190565b9080821061568357509061564d7fb19ca0df3f3a01af950d8e6ad62aeff167cf14c73e98af6c52afef1add5c97ed939261560d338230614d5c565b615617818a6131ef565b988851948594339886919260809396959491966001600160a01b0360a08501981684526020840152604083015260608201520152565b0390a2600d5460201c61566361141d8233614b48565b615674575050506106c96001600655565b612169906126d56126bf6134e8565b8651633b5d56ed60e11b8152808701918252602082018390529081906040010390fd5b6156b9903d808b833e61279581836111e4565b50386155b9565b6156c86139a3565b6155b4565b899795938b9a929d508c80946156f68d9c9a98946125de9f803d1061276e5761275f81836111e4565b9f9450945050939597995093959799615575565b6157126139a3565b61556e565b61572d90853d871161281d5761280e81836111e4565b503861552e565b61573c6139a3565b615529565b61574c338933614d5c565b6154c1565b85615769615433866157638151613599565b906157e0565b60405163b0b3262d60e01b81526001600160a01b03928316600482015291166024820152604490fd5b85615769615433856157ca565b604051631311dc6d60e01b8152600490fd5b5033600052601360205260ff60406000205416156153e7565b6020908051156157d8570190565b6136ff613703565b60209181518110156157f5575b60051b010190565b6157fd613703565b6157ed565b9081602091031261000e57516106c981610c96565b9081602091031261000e575190565b602090818184031261000e5780519067ffffffffffffffff821161000e57019180601f8401121561000e57825161585c81611b63565b9361586a60405195866111e4565b818552838086019260051b82010192831161000e578301905b828210615891575050505090565b81518152908301908301615883565b9291909594939560a084019084526020918285015260a0604085015282518091528160c0850193019160005b8281106158ee5750505050906001600160a01b03608092951660608201520152565b83516001600160a01b0316855293810193928101926001016158cc565b9380919796959760a086019086526020938487015260a060408701525260c08401929160005b8281106159535750505050906001600160a01b03608092951660608201520152565b9091929382806001926001600160a01b03883561596f816107b2565b16815201950193929101615931565b60209067ffffffffffffffff811161599c575b601f01601f19160190565b6159a4611190565b615991565b3d156159d4573d906159ba8261597e565b916159c860405193846111e4565b82523d6000602084013e565b606090565b604051906159e6826111a7565b60038252623f3f3f60e81b6020830152565b600080916040516001600160a01b0360208201916395d89b4160e01b835260048152615a23816111a7565b5192165afa615a306159a9565b9015615a3f576106c990615b0a565b506106c96159d9565b906020918051821015615a5a57010190565b615a62613703565b010190565b60ff6001911660ff81146136f7570190565b90615a838261597e565b615a9060405191826111e4565b8281528092615aa1601f199161597e565b0190602036910137565b60208183031261000e5780519067ffffffffffffffff821161000e570181601f8201121561000e578051615ade8161597e565b92615aec60405194856111e4565b8184526020828401011161000e576106c99160208085019101610670565b805160408110615b285750806020806106c993518301019101615aab565b602092908303615bfd576000805b60ff81168581109081615bdb575b5015615b5857615b5390615a67565b615b36565b92615b6660ff809516615a79565b92825b85811687811080615bbc575b15615bb1579081615ba6615b9d615b8f615bac9588615a48565b516001600160f81b03191690565b871a9188615a48565b53615a67565b615b69565b505094505050905090565b506001600160f81b0319615bd3615b8f8387615a48565b161515615b75565b6001600160f81b03199150615bf490615b8f9087615a48565b16151538615b44565b9150506106c96159d9565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526131df91615c446064836111e4565b615cbb565b15615c5057565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608490fd5b6001600160a01b03169060405190615cd2826111a7565b6020928383527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656484840152803b15615d495760008281928287615d249796519301915af1615d1e6159a9565b90615d8e565b80519081615d3157505050565b826131df93615d44938301019101615802565b615c49565b60405162461bcd60e51b815260048101859052601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b90919015615d9a575090565b815115615daa5750805190602001fd5b60405162461bcd60e51b815260206004820152908190612169906024830190610693565b90926131df93604051936323b872dd60e01b60208601526001600160a01b03809216602486015216604484015260648301526064825260a0820182811067ffffffffffffffff821117615e24575b604052615cbb565b615e2c611190565b615e1c565b6001600160801b038082511615600014615e4a57505090565b615e5e6106c9938260208501511690613d9f565b91511690613dbe565b91906001600160801b038084511615600014615e8257509150565b615eb2906020850190615e5e81615ea9615e9f8286511688613d9f565b828a511690613dbe565b97511687613d9f565b10615eb957565b906106c9906131e1565b90916001600160801b038083511615600014615edf5750505090565b602083959492930190615f02615ef88284511685613d9f565b8288511690613dbe565b9584615f13575b50505050615eb957565b615f2693945081615e5e91511687613d9f565b1038808080615f09565b919060208301926001600160801b038085511615600014615f52575090925050565b9081615e5e81615ea9615e9f615eb29686511688613d9f565b60208101906001600160801b03908183511615600014615f8b5750505090565b6106c99382615e5e92511690613d9f565b909160208201916001600160801b038084511615600014615fbe575050505090565b615f02615ef882849897959698511685613d9f56fea164736f6c6343000810000a0000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000124f8000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000853d955acef822db058eb8505911ed77f175b99e000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841900000000000000000000000000000000000000000000000000000002540be4000000000000000000000000003da1bf0be175b7caa38d67a6e78371947d2f51f700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000fd3065c629ee890fd74f43b802c2fea4b7279b8c000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b0000000000000000000000008412ebf45bac1b340bbe8f318b928c466c4e39ca000000000000000000000000118c1462aa28bf2ea304f78f49c3388cfd93234e

Deployed Bytecode

0x60806040526004361015610013575b600080fd5b60003560e01c806253f733146105da57806302ce728f146105d157806306fdde03146105c8578063095ea7b3146105bf5780630d09365c146105b657806311a2e4bc146105ad57806318160ddd146105a45780632165d72f1461059b57806323b872dd14610592578063313ce5671461058957806336fad62d1461058057806338d52e0f14610577578063395093511461056e5780633cc32aba146105655780633d417d2d1461055c5780633f2617cb146105535780633f4ba83a1461054a578063404ffa7a146105415780634732428c146105385780634962e4941461052f5780634ac8eb5f146105265780634fd422df1461051d57806354fd4d501461051457806356968f971461050b5780635c975abb14610502578063657a409c146104f95780636e553f65146104f057806370a08231146104e7578063715018a6146104de578063721b0a47146104d55780638142dd53146104cc5780638285ef40146104c35780638456cb59146104ba578063891682d2146104b15780638cad7fbe146104a85780638da5cb5b1461049f57806394e301e01461049657806395d14ca81461048d57806395d89b411461048457806399024fc11461047b5780639a295e73146104725780639bdff2e614610469578063a053db6814610460578063a457c2d714610457578063a9059cbb1461044e578063ad0c3bb514610445578063afa85de61461043c578063b054898b14610433578063b5af30621461042a578063b68d0a0914610421578063ba08765214610418578063c10c92a11461040f578063c270a54414610406578063c6e1c7c9146103fd578063c7be9786146103f4578063c936c624146103eb578063ca2298fe146103e2578063cadac479146103d9578063cdd72d52146103d0578063d2a156e0146103c7578063d41ddc96146103be578063d59624b4146103b5578063d6b7494f146103ac578063daf33f2a146103a3578063dd62ed3e1461039a578063e5f13b1614610391578063eee2421914610388578063ef14900d1461037f578063f2fde38b14610376578063f384bd051461036d578063f9557ccb14610364578063fbaa8b851461035b578063fbbbf94c146103525763fea10d5d1461034a57600080fd5b61000e613046565b5061000e613019565b5061000e612fd4565b5061000e612fa1565b5061000e612f65565b5061000e612ecc565b5061000e612e87565b5061000e612e42565b5061000e612d37565b5061000e612cd1565b5061000e612b1d565b5061000e612ae1565b5061000e612aa5565b5061000e6129b7565b5061000e612972565b5061000e61290e565b5061000e61289e565b5061000e6123b4565b5061000e612370565b5061000e612289565b5061000e612244565b5061000e612216565b5061000e6121da565b5061000e611f8a565b5061000e611f2a565b5061000e611eeb565b5061000e611d9f565b5061000e611d5b565b5061000e611d16565b5061000e611ceb565b5061000e611c37565b5061000e611b8a565b5061000e611b1e565b5061000e611ac5565b5061000e611a3a565b5061000e611931565b5061000e6118ea565b5061000e6118bc565b5061000e611894565b5061000e611850565b5061000e6117be565b5061000e6116e6565b5061000e6116b3565b5061000e6115bb565b5061000e6114ef565b5061000e611475565b5061000e611436565b5061000e611330565b5061000e611308565b5061000e6112e1565b5061000e6112ae565b5061000e611206565b5061000e611117565b5061000e6110f8565b5061000e6110ba565b5061000e61107e565b5061000e611043565b5061000e610f40565b5061000e610ea7565b5061000e610e34565b5061000e610cd9565b5061000e610bf8565b5061000e610bb3565b5061000e6109ef565b5061000e6109d2565b5061000e6108ef565b5061000e6108b1565b5061000e61088f565b5061000e610853565b5061000e6107f9565b5061000e6107c3565b5061000e6106cc565b5061000e610633565b5061000e6105ee565b600091031261000e57565b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419168152f35b503461000e57600036600319011261000e5761065460026006541415613c7f565b60026006556020610663614718565b6001600655604051908152f35b60005b8381106106835750506000910152565b8181015183820152602001610673565b906020916106ac81518092818552858086019101610670565b601f01601f1916010190565b9060206106c9928181520190610693565b90565b503461000e576000806003193601126107af576040519080600b546106f081611156565b80855291600191808316908115610785575060011461072a575b6107268561071a818703826111e4565b604051918291826106b8565b0390f35b9250600b83527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db95b82841061076d57505050810160200161071a8261072661070a565b80546020858701810191909152909301928101610752565b8695506107269693506020925061071a94915060ff191682840152151560051b820101929361070a565b80fd5b6001600160a01b0381160361000e57565b503461000e57604036600319011261000e576107ee6004356107e4816107b2565b60243590336133d3565b602060405160018152f35b503461000e57600036600319011261000e5761081a60026006541415613c7f565b6002600655608067ffffffffffffffff610832613edd565b91600160069594955560405194855260208501526040840152166060820152f35b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000027108152f35b503461000e57600036600319011261000e576020600e5460801c604051908152f35b503461000e57600036600319011261000e5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b503461000e57606036600319011261000e5760043561090d816107b2565b602435610919816107b2565b604435906001600160a01b0383166000526001602052610950336040600020906001600160a01b0316600052602052604060002090565b549260018401610971575b61096593506132af565b60405160018152602090f35b82841061098d5761098883610965950333836133d3565b61095b565b60405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b503461000e57600036600319011261000e57602060405160128152f35b503461000e57600036600319011261000e576101406040516001600160a01b03807f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e168252807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2166020830152807f0000000000000000000000000000000000000000000000000000000000000000166040830152807f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419166060830152807f0000000000000000000000003da1bf0be175b7caa38d67a6e78371947d2f51f71660808301527f0000000000000000000000005d6e79bcf90140585ce88c7119b7e43caaa670441660a0820152610b3060c082017f000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b6001600160a01b03169052565b6001600160a01b037f000000000000000000000000118c1462aa28bf2ea304f78f49c3388cfd93234e1660e08201527f000000000000000000000000000000000000000000000000000000000000000015156101008201527f00000000000000000000000000000000000000000000000000000000000000001515610120820152f35b503461000e57600036600319011261000e5760206040516001600160a01b037f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e168152f35b503461000e57604036600319011261000e576107ee600435610c19816107b2565b336000526001602052610c43816040600020906001600160a01b0316600052602052604060002090565b54906024358201809211610c58575b336133d3565b610c606131af565b610c52565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b8015150361000e57565b604060031982011261000e576004359067ffffffffffffffff821161000e57610ccb91600401610c65565b90916024356106c981610c96565b503461000e57610ce836610ca0565b9190917f000000000000000000000000000000000000000000000000000000000000000080610e0b575b610df957600092835b818110610d285784604051f35b610d3d90838415610dd2575b610d42576136e7565b610d1b565b610d4d81848761372b565b35610d57816107b2565b6001600160a01b03809116875260146020527f3bb51c63bf139c4bc98211d74c51aafae8b743fd3090bee8b6bfe2026678a250604091610da587848b209060ff801983541691151516179055565b610db084878a61372b565b3592610dbb846107b2565b51871515815292169180602081015b0390a26136e7565b50610dde81848761372b565b35610de8816107b2565b6001600160a01b0316331415610d34565b6040516395a584f760e01b8152600490fd5b5033600052601460205260ff604060002054161580610d12575060ff6040600020541615610d12565b503461000e57604036600319011261000e576020600435610663602435610e5a816107b2565b610e6960026006541415613c7f565b6002600655610e76613edd565b50505050610e826134e8565b610e8c8482615f30565b93610e9f610e9986614adf565b91614adf565b903392614edf565b503461000e57604036600319011261000e577fea1eefb4fd58778d7b274fe54045a9feeec8f2847899c2e71126d3a74d486da56040600435610ee8816107b2565b60243590610ef582610c96565b6001600160a01b0390610f0d8260055416331461311a565b1690816000526009602052610f3181846000209060ff801983541691151516179055565b825191825215156020820152a1005b503461000e57600036600319011261000e576001600160a01b03807f000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b163314159081611034575b5061102257610f94613edd565b5050505060055460ff8160a01c1615610fdd5760ff60a01b19166005557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606490fd5b604051638f5b12a760e01b8152600490fd5b90506005541633141538610f87565b503461000e57604036600319011261000e57602061107660243561106681610c96565b60043561107161350e565b615f9c565b604051908152f35b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000023288152f35b503461000e57600036600319011261000e5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b503461000e57600036600319011261000e576020601054604051908152f35b503461000e57602036600319011261000e576001600160a01b0360043561113d816107b2565b1660005260126020526020604060002054604051908152f35b90600182811c92168015611186575b602083101461117057565b634e487b7160e01b600052602260045260246000fd5b91607f1691611165565b50634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff8211176111c357604052565b6111cb611190565b604052565b67ffffffffffffffff81116111c357604052565b90601f8019910116810190811067ffffffffffffffff8211176111c357604052565b503461000e576000806003193601126107af57604051908060075461122a81611156565b808552916001918083169081156107855750600114611253576107268561071a818703826111e4565b9250600783527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6885b82841061129657505050810160200161071a8261072661070a565b8054602085870181019190915290930192810161127b565b503461000e57604036600319011261000e5760206110766024356112d181610c96565b6004356112dc61350e565b615ec3565b503461000e57600036600319011261000e57602060ff60055460a01c166040519015158152f35b503461000e57600036600319011261000e5760206001600160a01b03600a5416604051908152f35b503461000e5760408060031936011261000e5760243590611350826107b2565b61135f60026006541415613c7f565b600260065561136c614677565b6114265761138260ff60055460a01c1615613534565b7f0000000000000000000000000000000000000000000000000000000000000000806113e5575b6113d5576113bc610726926004356149b5565b906113c76001600655565b519081529081906020820190565b516395a584f760e01b8152600490fd5b5033600052601460205260ff81600020541615806113a957506001600160a01b03821660005261142161141d8260002060ff90541690565b1590565b6113a9565b5163b063a8a560e01b8152600490fd5b503461000e57602036600319011261000e576001600160a01b0360043561145c816107b2565b1660005260006020526020604060002054604051908152f35b503461000e576000806003193601126107af576005546001600160a01b038116906114a133831461311a565b6001600160a01b03191660055581604051917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08284a3f35b600435906001600160801b038216820361000e57565b503461000e57606036600319011261000e576115096114d9565b60443590611516826107b2565b61152860ff60055460a01c1615613534565b61153760026006541415613c7f565b60026006557f000000000000000000000000000000000000000000000000000000000000000080611592575b610df957610726916115789160243590615025565b6115826001600655565b6040519081529081906020820190565b5033600052601460205260ff604060002054161580611563575060406000205460ff1615611563565b503461000e57602036600319011261000e5760043563ffffffff811680820361000e576115f060ff60055460a01c1615613534565b600a546001600160a01b031633036116a15761c350811161168f577f58a58c712558f3d6e20bed57421eb8f73048d881dea9e5bb80efb37c49680d1c9161167361168a9261163c613edd565b505050506fffffffffffffffff0000000000000000600c549160401b16906fffffffffffffffff0000000000000000191617600c55565b60405163ffffffff90911681529081906020820190565b0390a1005b60405163da0afa5760e01b8152600490fd5b604051633b6b86b160e01b8152600490fd5b503461000e57600036600319011261000e57600f54604080516001600160801b038316815260809290921c602083015290f35b503461000e57600036600319011261000e576001600160a01b03807f000000000000000000000000fd3065c629ee890fd74f43b802c2fea4b7279b8c163314159081611791575b81611781575b81611754575b5061102257611746613edd565b5050505061175261374d565b005b90507f0000000000000000000000005d6e79bcf90140585ce88c7119b7e43caaa670441633141538611739565b8091506005541633141590611733565b337f000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b82161415915061172d565b503461000e57602036600319011261000e576004356117dc816107b2565b600a54906001600160a01b03908183168033036116a157604080516001600160a01b03928316815291831660208301527f582d6cc2f042c43e00e0dd5c187f575daac294216d2afa075d9e1e27b0a40a9491a173ffffffffffffffffffffffffffffffffffffffff19909216911617600a55005b503461000e57602036600319011261000e576001600160a01b03600435611876816107b2565b166000526009602052602060ff604060002054166040519015158152f35b503461000e57600036600319011261000e5760206001600160a01b0360055416604051908152f35b503461000e57604036600319011261000e5760206110766024356118df81610c96565b6004356110716134e8565b503461000e57600036600319011261000e576080600c546040519067ffffffffffffffff8082168352808260401c16602084015281841c16604083015260c01c6060820152f35b503461000e57600036600319011261000e5761072661196f7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26159f8565b61071a602e61199d7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e6159f8565b926040519384917f467261786c656e645631202d200000000000000000000000000000000000000060208401526119de815180926020602d87019101610670565b8201602f60f81b602d8201526119fd8251809360208785019101610670565b0103600e8101845201826111e4565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020838186019501011161000e57565b503461000e57608036600319011261000e5767ffffffffffffffff60043581811161000e57611a6d903690600401611a0c565b9060243583811161000e57611a86903690600401610c65565b9060443585811161000e57611a9f903690600401610c65565b92909160643596871161000e57611abd611752973690600401611a0c565b969095613a9c565b503461000e57600036600319011261000e57610100604051620186a08082528060208301528060408301526060820152670de0b6b3a7640000608082015263096ba6c460a0820152600060c082015261c35060e0820152f35b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b60209067ffffffffffffffff8111611b7d575b60051b0190565b611b85611190565b611b76565b503461000e5760a036600319011261000e57600435611ba8816107b2565b6084359067ffffffffffffffff821161000e573660238301121561000e578160040135611bd481611b63565b92611be260405194856111e4565b81845260209160248386019160051b8301019136831161000e57602401905b828210611c1e57610726611582876064356044356024358a61538b565b8380918335611c2c816107b2565b815201910190611c01565b503461000e57604036600319011261000e57600435611c55816107b2565b60243590336000526001602052611c83816040600020906001600160a01b0316600052602052604060002090565b5491808310611c9857610965920390336133d3565b60405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608490fd5b503461000e57604036600319011261000e576107ee600435611d0c816107b2565b60243590336132af565b503461000e57600036600319011261000e5760206040516001600160a01b037f000000000000000000000000fd3065c629ee890fd74f43b802c2fea4b7279b8c168152f35b503461000e57602036600319011261000e576001600160a01b03600435611d81816107b2565b166000526014602052602060ff604060002054166040519015158152f35b503461000e57611dae36610ca0565b907f000000000000000000000000000000000000000000000000000000000000000080611ed2575b611ec057600092835b828110611dec5784604051f35b611e0190848515611e99575b611e06576136e7565b611ddf565b6001600160a01b03611e1982868661372b565b35611e23816107b2565b16865260136020526040611e45868289209060ff801983541691151516179055565b7f9c798cce2c4fdbec95a1a6dbba64db726912274dac938f44e36bce9b779cfee8611e88611e7c611e7785898961372b565b613743565b6001600160a01b031690565b915187151581528060208101610dca565b50611ea581858561372b565b35611eaf816107b2565b6001600160a01b0316331415611df8565b6040516342f41ddd60e11b8152600490fd5b5033600052601360205260ff6040600020541615611dd6565b503461000e57602036600319011261000e576001600160a01b03600435611f11816107b2565b1660005260116020526020604060002054604051908152f35b503461000e57602036600319011261000e576001600160a01b03600435611f50816107b2565b1660005260006020526060604060002054601260205260406000205460116020526040600020549060405192835260208301526040820152f35b503461000e57606036600319011261000e57600435602435611fab816107b2565b604435611fb7816107b2565b611fc660026006541415613c7f565b6002600655611fd3613edd565b50505050611fdf61350e565b91611fea8484615f6b565b92611ffd611ff785614adf565b95614adf565b916001600160a01b03918285169485330361216d575b61202461201e6134e8565b83613c61565b916001600160801b0390818a16938481106121405750926120ef7ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db9593612116936120e7876120996107269f9b998c61208761208c92516001600160801b031690565b613580565b6001600160801b03168252565b6120c38c6120b6602084019161208783516001600160801b031690565b6001600160801b03169052565b6001600160801b038151169060206001600160801b031991015160801b1617600e55565b8916906135b5565b827f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e615c08565b604080516001600160801b0395861681529590941660208601521692339290a46115826001600655565b6040516362ddb6d760e11b815260048101919091526001600160801b038b166024820152604490fd5b0390fd5b6121a63361218e836001600160a01b03166000526001602052604060002090565b906001600160a01b0316600052602052604060002090565b54600181016121b6575b50612013565b6121cd6121d4916001600160801b038816906135a8565b33836133d3565b386121b0565b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000002540be4008152f35b503461000e57604036600319011261000e57602061107660243561223981610c96565b6004356112dc6134e8565b503461000e57600036600319011261000e5760206040516001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168152f35b503461000e576000806003193601126107af5760405190806008546122ad81611156565b8085529160019180831690811561234657506001146122eb575b610726856122d7818703826111e4565b604051918291602083526020830190610693565b9250600883527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee35b82841061232e5750505081016020016122d7826107266122c7565b80546020858701810191909152909301928101612313565b869550610726969350602092506122d794915060ff191682840152151560051b82010192936122c7565b503461000e57602036600319011261000e576001600160a01b03600435612396816107b2565b166000526013602052602060ff604060002054166040519015158152f35b503461000e57608036600319011261000e57600480356123d3816107b2565b602435916044359160643567ffffffffffffffff811161000e576123fa9036908401610c65565b61240c60026006979397541415613c7f565b6002600655612419613edd565b50505050612425614718565b5061245061141d612449856001600160a01b03166000526009602052604060002090565b5460ff1690565b61288d57612461611e77828861371a565b6001600160a01b03918291827f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216938491160361287b57806124b0611e776124a98594613599565b838c61372b565b827f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e169384911603612831579161252c6125f79492888a956124f333308b614e1e565b8960409d8e94855163095ea7b360e01b81528c818060209a8b93888b8401602090939291936001600160a01b0360408201951681520152565b03816000809e5af18015612824575b6127f7575b508551998c6370a0823160e01b97888d52898d8c81806125738d309083019190916001600160a01b036020820193169052565b03915afa9c8d156127ea575b8c9d6127b1575b50928998979694926125b48d979593889451998a98899788956338ed173960e01b875242943094880161590b565b0393165af180156127a4575b612782575b508b51908152308a820190815290938491829081906020015b03915afa928315612775575b92612748575b50506135a8565b938085106127255750837fe947f0f9b6255bdcf76d13d1257d34fbe380e0d5d4daa75e61c783a41e1607ba9161268561262e6134e8565b61265861263b8583615e31565b9161264586614adf565b3391309161265286614adf565b91614edf565b8851938493339785909493926060926001600160a01b036080840197168352602083015260408201520152565b0390a2600d5460201c61269b61141d8233614b48565b6126ae5761072683856113c76001600655565b6121699192506126d56126bf6134e8565b3360009081526012602052604090205490615f30565b936126f3336001600160a01b03166000526011602052604060002090565b549051633b49de0f60e21b815293840194855260208501526001600160e01b0316604084015290918291606090910190565b8551633b5d56ed60e11b8152808501918252602082018690529081906040010390fd5b6127679250803d1061276e575b61275f81836111e4565b810190615817565b38806125f0565b503d612755565b61277d6139a3565b6125ea565b61279d903d8088833e61279581836111e4565b810190615826565b50386125c5565b6127ac6139a3565b6125c0565b8c9694919d50926125b487936127d88d9c9b9a98968d803d1061276e5761275f81836111e4565b9f939698509350509294969798612586565b6127f26139a3565b61257f565b61281690883d8a1161281d575b61280e81836111e4565b810190615802565b5038612540565b503d612804565b61282c6139a3565b61253b565b8661216961284d611e778c856128478997613599565b9161372b565b6040805163b0b3262d60e01b81526001600160a01b0395861694810194855294909116602084015283920190565b8561216961284d611e7786948c61371a565b604051631311dc6d60e01b81528490fd5b503461000e57604036600319011261000e576024356128bc816107b2565b6128cb60026006541415613c7f565b60026006556128d8614677565b6128fc576128f5906128e8613edd565b5050505060043533614d5c565b6001600655005b60405163b063a8a560e01b8152600490fd5b503461000e57600036600319011261000e5760a061292a61350e565b6001600160801b038060208184511693015116906129466134e8565b906020818351169201511690601054926040519485526020850152604084015260608301526080820152f35b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000005d6e79bcf90140585ce88c7119b7e43caaa67044168152f35b503461000e57604036600319011261000e57612a166024356129d8816107b2565b6129e760026006541415613c7f565b60026006556129f4613edd565b50505050336000526012602052604060002054612a97575b3390600435614e1e565b600d5460201c612a2961141d8233614b48565b612a37576117526001600655565b612a426126bf6134e8565b612169612a62336001600160a01b03166000526011602052604060002090565b54604051633b49de0f60e21b8152600481019390935260248301526001600160e01b0390921660448201529081906064820190565b612a9f614718565b50612a0c565b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461000e57604036600319011261000e57612b376114d9565b602435612b43816107b2565b612b596001600160a01b0360055416331461311a565b81612b6261350e565b612b6a6134e8565b936001600160801b0380911615612c9c575b80831690612b94612b8d8385615f30565b9684613c61565b868110612c7b57610726877f14f6e172cd596e9f9c5d24e2d4010daa24f8f65f9274b259b66517b306c617b98888612c13896120c38a612bf1612be48c8a1661208786516001600160801b031690565b6001600160801b03168452565b612c0d602084016120b68761208783516001600160801b031690565b306135b5565b612c3e84837f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e615c08565b604080516001600160801b039290921682526001600160a01b03929092166020820152908101839052606090a16040519081529081906020820190565b6040516362ddb6d760e11b8152600481019190915260248101879052604490fd5b9150612ccb612cbe306001600160a01b03166000526000602052604060002090565b546001600160801b031690565b91612b7c565b503461000e57604036600319011261000e576020612d2e600435612cf4816107b2565b6001600160a01b0360243591612d09836107b2565b16600052600183526040600020906001600160a01b0316600052602052604060002090565b54604051908152f35b503461000e57606036600319011261000e57602435604435612d58816107b2565b612d60614677565b6128fc57612d7660ff60055460a01c1615613534565b612d8560026006541415613c7f565b60026006557f000000000000000000000000000000000000000000000000000000000000000080612e29575b611ec057612de591612dc1613edd565b50505050612dcd614718565b5080612e17575b50612de0600435614adf565b614c15565b600d5460201c90612df961141d8333614b48565b612e0b57610726906115826001600655565b50612a426126bf6134e8565b612e2390339033614d5c565b38612dd4565b5033600052601360205260ff6040600020541615612db1565b503461000e57600036600319011261000e5760206040516001600160a01b037f0000000000000000000000003da1bf0be175b7caa38d67a6e78371947d2f51f7168152f35b503461000e57600036600319011261000e5760206040516001600160a01b037f000000000000000000000000118c1462aa28bf2ea304f78f49c3388cfd93234e168152f35b503461000e57602036600319011261000e57600435612eea816107b2565b6001600160a01b03612f018160055416331461311a565b811615612f115761175290613165565b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b503461000e57600036600319011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000124f88152f35b503461000e57600036600319011261000e57600e54604080516001600160801b038316815260809290921c602083015290f35b503461000e57600036600319011261000e5760206040516001600160a01b037f000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b168152f35b503461000e57600036600319011261000e576040600d5481519063ffffffff8116825260201c6020820152f35b503461000e57600036600319011261000e5760a06040517f00000000000000000000000000000000000000000000000000000002540be40081527f00000000000000000000000000000000000000000000000000000000000124f860208201527f000000000000000000000000000000000000000000000000000000000000271060408201527f000000000000000000000000000000000000000000000000000000000000000060608201527f00000000000000000000000000000000000000000000000000000000000000006080820152f35b1561312157565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b600554906001600160a01b0380911691826001600160a01b0319821617600555167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e06000604051a3565b50634e487b7160e01b600052601160045260246000fd5b90620186a09182018092116131d757565b6131df6131af565b565b90600182018092116131d757565b919082018092116131d757565b1561320357565b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b1561325b57565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b91906001600160a01b03908184169283156133805761335e827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9461337b9416966132fb8815156131fc565b6133448461331c836001600160a01b03166000526000602052604060002090565b5461332982821015613254565b03916001600160a01b03166000526000602052604060002090565b556001600160a01b03166000526000602052604060002090565b6133698282546131ef565b90556040519081529081906020820190565b0390a3565b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b906001600160a01b039182811692831561349757821693841561344757806134367f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259461218e61337b956001600160a01b03166000526001602052604060002090565b556040519081529081906020820190565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b60405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b604051906134f5826111a7565b600f546001600160801b038116835260801c6020830152565b6040519061351b826111a7565b600e546001600160801b038116835260801c6020830152565b1561353b57565b60405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606490fd5b6001600160801b0391821690821603919082116131d757565b6000198101919082116131d757565b919082039182116131d757565b6001600160a01b038116908115613698576135e3816001600160a01b03166000526000602052604060002090565b5483811061364857837fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9261363360009661337b9403916001600160a01b03166000526000602052604060002090565b55611582613643826002546135a8565b600255565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608490fd5b60019060001981146136f7570190565b6136ff6131af565b0190565b50634e487b7160e01b600052603260045260246000fd5b90156137235790565b6106c9613703565b919081101561373b5760051b0190565b611b85613703565b356106c9816107b2565b7401000000000000000000000000000000000000000060ff60a01b1960055461377c60ff8260a01c1615613534565b16176005557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1565b601f81116137b9575050565b600090600b82527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9906020601f850160051c83019410613814575b601f0160051c01915b82811061380957505050565b8181556001016137fd565b90925082906137f4565b601f811161382a575050565b600090600882527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3906020601f850160051c83019410613885575b601f0160051c01915b82811061387a57505050565b81815560010161386e565b9092508290613865565b919067ffffffffffffffff811161396e575b6138b5816138b0600b54611156565b6137ad565b6000601f82116001146138ef578192936000926138e4575b50508160011b916000199060031b1c191617600b55565b0135905038806138cd565b600b600052601f198216937f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db991805b868110613956575083600195961061393c575b505050811b01600b55565b0135600019600384901b60f8161c19169055388080613931565b9092602060018192868601358155019401910161391e565b613976611190565b6138a1565b90918060409360208452816020850152848401376000828201840152601f01601f1916010190565b506040513d6000823e3d90fd5b919067ffffffffffffffff8111613a8f575b6139d6816139d1600854611156565b61381e565b6000601f8211600114613a1057819293600092613a05575b50508160011b916000199060031b1c191617600855565b0135905038806139ee565b6008600052601f198216937ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee391805b868110613a775750836001959610613a5d575b505050811b01600855565b0135600019600384901b60f8161c19169055388080613a52565b90926020600181928686013581550194019101613a3f565b613a97611190565b6139c2565b9596956001600160a01b039593917f0000000000000000000000005d6e79bcf90140585ce88c7119b7e43caaa6704487163303613c4f578015613c3d57613ae4600b54611156565b613c2c57613af19161388f565b60005b818110613bf65750505060005b818110613bae575050507f0000000000000000000000003da1bf0be175b7caa38d67a6e78371947d2f51f71691823b1561000e5760405163453f31d560e01b8152613b719360009082908180613b5b88886004840161397b565b03915afa8015613ba1575b613b88575b506139b0565b613b79613edd565b50505050613b85614718565b50565b80613b95613b9b926111d0565b806105e3565b38613b6b565b613ba96139a3565b613b66565b80613bec613bdf613bc6611e77613bf195878961372b565b6001600160a01b03166000526014602052604060002090565b805460ff19166001179055565b6136e7565b613b01565b80613bec613bdf613c0e611e77613c2795878961372b565b6001600160a01b03166000526013602052604060002090565b613af4565b60405162dc149f60e41b8152600490fd5b604051631ff3ed9d60e01b8152600490fd5b604051638b906c9760e01b8152600490fd5b5190516001600160801b0391613c7b918316908316613580565b1690565b15613c8657565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b60405167ffffffffffffffff91906080810183811182821017613d16575b60405260608193600c54908082168452808260401c1660208501528160801c16604084015260c01c910152565b613d1e611190565b613ce9565b7da7c5ac471b4784230fcf80dc33721d53cddd6e04c059210385c67dfe32a08111600116613d54575b620186a00290565b613d5c6131af565b613d4c565b710154484932d2e725a5bbca17a3aba173d3d58111600116613d92575b6ec097ce7bc90715b34b9f10000000000290565b613d9a6131af565b613d7e565b8060001904821181151516613db2570290565b613dba6131af565b0290565b8115613dc8570490565b634e487b7160e01b600052601260045260246000fd5b9081602091031261000e575167ffffffffffffffff8116810361000e5790565b90613e1190604083526040830190610693565b81810360209283015260085460009291613e2a82611156565b80825291600190818116908115613ea55750600114613e4b575b5050505090565b9293509060086000527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee392846000945b838610613e915750505050010138808080613e44565b805485870183015294019385908201613e7b565b60ff191685840152505090151560051b0101905038808080613e44565b9190916001600160801b03808094169116019182116131d757565b60009060009081809181613eef613ccb565b9060408201613f06815167ffffffffffffffff1690565b67ffffffffffffffff92908316421461457557613f2161350e565b613f296134e8565b906001600160801b039260209380613f4a868601516001600160801b031690565b16158015614566575b156140305750505050506131df9291613f92613fa492613f7c61141d60055460ff9060a01c1690565b614021575b82421667ffffffffffffffff169052565b4316829067ffffffffffffffff169052565b67ffffffffffffffff815116906020810151916fffffffffffffffff00000000000000007fffffffffffffffff000000000000000000000000000000000000000000000000606077ffffffffffffffff00000000000000000000000000000000604086015160801b1694015160c01b169360401b16171717600c55565b63096ba6c46060860152613f81565b909194849c508094995096959296516140509067ffffffffffffffff1690565b67ffffffffffffffff1661406490426135a8565b98899487895161407a906001600160801b031690565b6001600160801b031661408c90613d23565b88516001600160801b03166001600160801b03166140a991613dbe565b94876140b3614677565b61417695901561442e57505050507fc63977c8e2362a31182dc8e89a52252f9836922738e0abcfc0de6924972eafe5857f0000000000000000000000000000000000000000000000000000000000000000169b8c955b61415560608c0197614123895167ffffffffffffffff1690565b926040519485948592936060929594608085019667ffffffffffffffff80941686526020860152604085015216910152565b0390a167ffffffffffffffff8b16845284421667ffffffffffffffff169052565b67ffffffffffffffff4384161686526141ea6141dc6141b46141ae6141a28b516001600160801b031690565b6001600160801b031690565b87613d9f565b6141d66141c9865167ffffffffffffffff1690565b67ffffffffffffffff1690565b90613d9f565b670de0b6b3a7640000900490565b9b8c938261420b6142056141a28c516001600160801b031690565b876131ef565b11158061440e575b614272575b5050505050506131df9291613fa461424e926001600160801b038151169060206001600160801b031991015160801b1617600e55565b6001600160801b038151169060206001600160801b031991015160801b1617600f55565b6142c46142b76142956142a761429a8d888b16938491516001600160801b031690565b613ec2565b6001600160801b03168d52565b89516001600160801b0316613ec2565b6001600160801b03168852565b8782015167ffffffffffffffff161680614367575b505050927f50225349cc7e3814c4fa5fe6baef7a3c4cac55e92c64b1f4a5d1ba55e65dcc826131df969593613fa4936143588c8e61432261424e9a5167ffffffffffffffff1690565b9460405195869586919267ffffffffffffffff608094979695929760a08501988552166020840152604083015260608201520152565b0390a1928294958b9238614218565b909361437c9296939c50809897959b50613d9f565b620186a09004998a928201998a5161439a906001600160801b031690565b6001600160801b03166143ad9085613d9f565b83516143c39086906001600160801b03166135a8565b6143cc91613dbe565b9a8b96871681516143e3906001600160801b031690565b906143ed91613ec2565b6001600160801b03169052614402863061459b565b939596928192956142d9565b50826144276142056141a28a516001600160801b031690565b1115614213565b806144e6926144cc866144be61448e6144886141c961447960607fc63977c8e2362a31182dc8e89a52252f9836922738e0abcfc0de6924972eafe59d015167ffffffffffffffff1690565b945167ffffffffffffffff1690565b436135a8565b8d6040519586948886019094939260609267ffffffffffffffff6080840197168352602083015260408201520152565b03601f1981018352826111e4565b60405180948192631b54c1a360e01b835260048301613dfe565b03816001600160a01b037f0000000000000000000000003da1bf0be175b7caa38d67a6e78371947d2f51f7165afa928315614559575b9261452c575b50509b8c95614109565b61454b9250803d10614552575b61454381836111e4565b810190613dde565b8f80614522565b503d614539565b6145616139a3565b61451c565b5060055460a01c60ff16613f53565b96505050909450614594925060609150015167ffffffffffffffff1690565b8192829190565b6001600160a01b0381169182156146325760207fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91614600600094600254838101809111614625575b6002556001600160a01b03166000526000602052604060002090565b805490828201809211614618575b55604051908152a3565b6146206131af565b61460e565b61462d6131af565b6145e4565b60405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606490fd5b7f000000000000000000000000000000000000000000000000000000000000000080151590816146a5575090565b9050421190565b604051906146b9826111a7565b816020600d5463ffffffff81168352811c910152565b519069ffffffffffffffffffff8216820361000e57565b908160a091031261000e576146fa816146cf565b916020820151916040810151916106c96080606084015193016146cf565b6147206146ac565b9061472f825163ffffffff1690565b63ffffffff9081164214614990576001600160a01b036ec097ce7bc90715b34b9f10000000007f000000000000000000000000000000000000000000000000000000000000000082811680614918575b50507f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419918216918261486d575b506147d991507f00000000000000000000000000000000000000000000000000000002540be40090613dbe565b926001600160e01b0380851161485b5784166001600160e01b0316602082015261482d9161480f904216829063ffffffff169052565b63ffffffff81511690602063ffffffff1991015160201b1617600d55565b6040518281527f4fc1b45960547ee95894b08a284c3c066cf5aca706a7420639c42c3ec2e118a490602090a1565b60405163057b0e2160e41b8152600490fd5b60a060049360405194858092633fabe5a360e21b82525afa92831561490b575b6000936148d6575b5060008313156148b357506147d9916148ad91613dbe565b386147ac565b6040516322ad99db60e21b81526001600160a01b03919091166004820152602490fd5b6148f891935060a03d8111614904575b6148f081836111e4565b8101906146e6565b50505090509138614895565b503d6148e6565b6149136139a3565b61488d565b604051633fabe5a360e21b8152925060a090839060049082905afa918215614983575b60009261495e575b5060008213156148b3575061495790613d61565b388061477f565b61497791925060a03d8111614904576148f081836111e4565b50505090509038614943565b61498b6139a3565b61493b565b50906149a960206106c99201516001600160e01b031690565b6001600160e01b031690565b91906149bf613edd565b505050506149cb61350e565b907fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d76001600160a01b03614a08614a028786615e31565b96614adf565b92614ab8614a1588614adf565b956001600160801b039081614a2d8882845116613ec2565b168152614a7482602083019281614a478c82875116613ec2565b168452614a56828c168861459b565b51166001600160801b03166001600160801b0319600e541617600e55565b5181600e54916001600160801b03199060801b16911617600e55851630337f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e615dce565b604080516001600160801b039586168152959094166020860152169233928190810161337b565b6001600160801b0390818111614af3571690565b60405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608490fd5b7f00000000000000000000000000000000000000000000000000000000000124f8918215614c0d57614b9b6001600160a01b03614b836134e8565b93169283600052601260205260406000205490615f30565b918215614c04576000526011602052604060002054908115614bfb57614bcd670de0b6b3a764000091614be994613d9f565b04620186a0908060001904821181151516614bee575b02613dbe565b111590565b614bf66131af565b614be3565b50505050600090565b50505050600190565b505050600190565b9190614c1f6134e8565b92614c3184614c2c61350e565b613c61565b916001600160801b039485831693848110614d3357508061424e614c5886614c8c94615e67565b97614c71612be48761429586516001600160801b031690565b6120b660208401918a1661429583516001600160801b031690565b336000908152601260205260409020614ca68682546131ef565b90556001600160a01b03811692308403614d02575b5050604080516001600160801b0390921682526020820185905233917f01348584ec81ac7acd52b7d66d9ade986dd909f3d513881c190fc31c90527efe918190810161337b565b614d2c917f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e615c08565b3880614cbb565b6040516362ddb6d760e11b815260048101919091526001600160801b0384166024820152604490fd5b9060207fa32435755c235de2976ed44a75a2f85cb01faf0c894f639fe0c32bb9455fea8f916001600160a01b038095169485600052601183526040600020805490838201809211614e11575b55601054828101809111614e04575b60105584169381308603614dd1575b5050604051908152a3565b614dfd9130907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2615dce565b3881614dc6565b614e0c6131af565b614db7565b614e196131af565b614da8565b6001600160a01b03809316928360005260116020526040600020805490838203918211614ed2575b55601054828103908111614ec5575b60105582169181308403614e94575b50506040519081527fbc290bb45104f73cf92115c9603987c3f8fd30c182a13603d8cffa49b5f5995260203392a4565b614ebe917f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2615c08565b3881614e64565b614ecd6131af565b614e55565b614eda6131af565b614e46565b909361337b7f9dc1449a0ff0c152e18e8289d865b47acc6e1b76b1ecb239c13d6ee22a9206a793614f8a6001600160801b039485614f208a82845116613580565b1681526020810186614f358582845116613580565b169052614f55856001600160a01b03166000526012602052604060002090565b8054908785168203918211614fff575b556001600160801b038151169060206001600160801b031991015160801b1617600f55565b6001600160a01b039384871696308803614fc9575b50506040519384931696839060209093929360408301946001600160801b03809216845216910152565b614ff89189169030907f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e615dce565b3880614f9f565b6150076131af565b614f65565b818103929160001380158285131691841216176131d757565b9291908042116152d65750615038613edd565b50505050615044614718565b9061504f8282614b48565b6152c457836151c2826131df9461510397610e9f61506b6134e8565b92615089856001600160a01b03166000526011602052604060002090565b54600061512461510c6150d86141dc6150be6150b88c6001600160a01b03166000526012602052604060002090565b54614adf565b966150d36001600160801b03809b168c615f6b565b613d9f565b9e8f6141d67f00000000000000000000000000000000000000000000000000000000000027106131c6565b620186a0900490565b61511e615118856152f6565b916152f6565b9061500c565b1380159c9061528c5750965b8786819d6151486151438886168a615f30565b614adf565b968860009687936151c9575b5050604080519485526001600160801b038087166020870152808a169186019190915280871660608601529091166080840152506001600160a01b0316907f35f432a64bd3767447a456650432406c6cacb885819947a202216eeea6820ecf908060a081015b0390a2613ec2565b3390614e1e565b7f35f432a64bd3767447a456650432406c6cacb885819947a202216eeea6820ecf94929750926151ba91615205886001600160a01b0396613580565b9889168061521a575b50915092829450615154565b6120879250816152336151436152439361525095615f6b565b938491516001600160801b031690565b6001600160801b03168c52565b61528561526982612087600e546001600160801b031690565b6001600160801b03166001600160801b0319600e541617600e55565b8a3861520e565b6152be9150615103906141d67f00000000000000000000000000000000000000000000000000000000000023286131c6565b96615130565b604051633af2cafd60e11b8152600490fd5b604051635ba2a8d560e01b81524260048201526024810191909152604490fd5b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116153205790565b60405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e743235360000000000000000000000000000000000000000000000006064820152608490fd5b909193615396614677565b6128fc576153a960026006541415613c7f565b60026006556153c060ff60055460a01c1615613534565b7f0000000000000000000000000000000000000000000000000000000000000000806157b1575b611ec0576153f3613edd565b505050506153ff614718565b5061542361141d612449846001600160a01b03166000526009602052604060002090565b61579f57615440615433826157ca565b516001600160a01b031690565b926001600160a01b03948594857f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e169687911603615792578461548f6154336154898651613599565b866157e0565b817f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21696879116036157515787615741575b6154ca83614adf565b306154d491614c15565b9360409788519263095ea7b360e01b84526020906004998a918387808d8c8783019161551592602090939291936001600160a01b0360408201951681520152565b0381600080995af16155d297908015615734575b615717575b508c898c8251986370a0823160e01b94858b528c898c80615561308d83019190916001600160a01b036020820193169052565b03818b5afa9b8c1561570a575b8b9c6156cd575b50978593819795938c80948d9c6155a86125de9f9d9b51988997889687946338ed173960e01b86524293309387016158a0565b0393165af180156156c0575b6156a6575b5051908152309281019283529485928391829160200190565b9080821061568357509061564d7fb19ca0df3f3a01af950d8e6ad62aeff167cf14c73e98af6c52afef1add5c97ed939261560d338230614d5c565b615617818a6131ef565b988851948594339886919260809396959491966001600160a01b0360a08501981684526020840152604083015260608201520152565b0390a2600d5460201c61566361141d8233614b48565b615674575050506106c96001600655565b612169906126d56126bf6134e8565b8651633b5d56ed60e11b8152808701918252602082018390529081906040010390fd5b6156b9903d808b833e61279581836111e4565b50386155b9565b6156c86139a3565b6155b4565b899795938b9a929d508c80946156f68d9c9a98946125de9f803d1061276e5761275f81836111e4565b9f9450945050939597995093959799615575565b6157126139a3565b61556e565b61572d90853d871161281d5761280e81836111e4565b503861552e565b61573c6139a3565b615529565b61574c338933614d5c565b6154c1565b85615769615433866157638151613599565b906157e0565b60405163b0b3262d60e01b81526001600160a01b03928316600482015291166024820152604490fd5b85615769615433856157ca565b604051631311dc6d60e01b8152600490fd5b5033600052601360205260ff60406000205416156153e7565b6020908051156157d8570190565b6136ff613703565b60209181518110156157f5575b60051b010190565b6157fd613703565b6157ed565b9081602091031261000e57516106c981610c96565b9081602091031261000e575190565b602090818184031261000e5780519067ffffffffffffffff821161000e57019180601f8401121561000e57825161585c81611b63565b9361586a60405195866111e4565b818552838086019260051b82010192831161000e578301905b828210615891575050505090565b81518152908301908301615883565b9291909594939560a084019084526020918285015260a0604085015282518091528160c0850193019160005b8281106158ee5750505050906001600160a01b03608092951660608201520152565b83516001600160a01b0316855293810193928101926001016158cc565b9380919796959760a086019086526020938487015260a060408701525260c08401929160005b8281106159535750505050906001600160a01b03608092951660608201520152565b9091929382806001926001600160a01b03883561596f816107b2565b16815201950193929101615931565b60209067ffffffffffffffff811161599c575b601f01601f19160190565b6159a4611190565b615991565b3d156159d4573d906159ba8261597e565b916159c860405193846111e4565b82523d6000602084013e565b606090565b604051906159e6826111a7565b60038252623f3f3f60e81b6020830152565b600080916040516001600160a01b0360208201916395d89b4160e01b835260048152615a23816111a7565b5192165afa615a306159a9565b9015615a3f576106c990615b0a565b506106c96159d9565b906020918051821015615a5a57010190565b615a62613703565b010190565b60ff6001911660ff81146136f7570190565b90615a838261597e565b615a9060405191826111e4565b8281528092615aa1601f199161597e565b0190602036910137565b60208183031261000e5780519067ffffffffffffffff821161000e570181601f8201121561000e578051615ade8161597e565b92615aec60405194856111e4565b8184526020828401011161000e576106c99160208085019101610670565b805160408110615b285750806020806106c993518301019101615aab565b602092908303615bfd576000805b60ff81168581109081615bdb575b5015615b5857615b5390615a67565b615b36565b92615b6660ff809516615a79565b92825b85811687811080615bbc575b15615bb1579081615ba6615b9d615b8f615bac9588615a48565b516001600160f81b03191690565b871a9188615a48565b53615a67565b615b69565b505094505050905090565b506001600160f81b0319615bd3615b8f8387615a48565b161515615b75565b6001600160f81b03199150615bf490615b8f9087615a48565b16151538615b44565b9150506106c96159d9565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526131df91615c446064836111e4565b615cbb565b15615c5057565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608490fd5b6001600160a01b03169060405190615cd2826111a7565b6020928383527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656484840152803b15615d495760008281928287615d249796519301915af1615d1e6159a9565b90615d8e565b80519081615d3157505050565b826131df93615d44938301019101615802565b615c49565b60405162461bcd60e51b815260048101859052601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b90919015615d9a575090565b815115615daa5750805190602001fd5b60405162461bcd60e51b815260206004820152908190612169906024830190610693565b90926131df93604051936323b872dd60e01b60208601526001600160a01b03809216602486015216604484015260648301526064825260a0820182811067ffffffffffffffff821117615e24575b604052615cbb565b615e2c611190565b615e1c565b6001600160801b038082511615600014615e4a57505090565b615e5e6106c9938260208501511690613d9f565b91511690613dbe565b91906001600160801b038084511615600014615e8257509150565b615eb2906020850190615e5e81615ea9615e9f8286511688613d9f565b828a511690613dbe565b97511687613d9f565b10615eb957565b906106c9906131e1565b90916001600160801b038083511615600014615edf5750505090565b602083959492930190615f02615ef88284511685613d9f565b8288511690613dbe565b9584615f13575b50505050615eb957565b615f2693945081615e5e91511687613d9f565b1038808080615f09565b919060208301926001600160801b038085511615600014615f52575090925050565b9081615e5e81615ea9615e9f615eb29686511688613d9f565b60208101906001600160801b03908183511615600014615f8b5750505090565b6106c99382615e5e92511690613d9f565b909160208201916001600160801b038084511615600014615fbe575050505090565b615f02615ef882849897959698511685613d9f56fea164736f6c6343000810000a

Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.