ETH Price: $3,093.69 (-0.45%)
Gas: 5 Gwei

Contract

0x21E354Da5D929A4da55428F6bfd71eD9Ffd5001F
 

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Redeem189525062024-01-07 3:17:11119 days ago1704597431IN
0x21E354Da...9Ffd5001F
0 ETH0.002466528.01095862
Remove Collatera...189489512024-01-06 15:08:59119 days ago1704553739IN
0x21E354Da...9Ffd5001F
0 ETH0.0027092830.55715974
Liquidate189369662024-01-04 22:36:35121 days ago1704407795IN
0x21E354Da...9Ffd5001F
0 ETH0.0034603118.82119541
Redeem189090492024-01-01 0:31:59125 days ago1704069119IN
0x21E354Da...9Ffd5001F
0 ETH0.0012246411.64611294
Redeem189058372023-12-31 13:44:11125 days ago1704030251IN
0x21E354Da...9Ffd5001F
0 ETH0.001211913.76300777
Redeem189044652023-12-31 9:07:35125 days ago1704013655IN
0x21E354Da...9Ffd5001F
0 ETH0.0010378312.4657693
Redeem188956512023-12-30 3:23:59127 days ago1703906639IN
0x21E354Da...9Ffd5001F
0 ETH0.0012376214.86541444
Liquidate188930542023-12-29 18:36:35127 days ago1703874995IN
0x21E354Da...9Ffd5001F
0 ETH0.0050849725.47644267
Deposit188887452023-12-29 4:04:59128 days ago1703822699IN
0x21E354Da...9Ffd5001F
0 ETH0.0017994320.14301319
Deposit188886072023-12-29 3:37:23128 days ago1703821043IN
0x21E354Da...9Ffd5001F
0 ETH0.001554218.57736963
Update Exchange ...188885942023-12-29 3:34:47128 days ago1703820887IN
0x21E354Da...9Ffd5001F
0 ETH0.0019618121.32543076
Repay Asset188831682023-12-28 9:18:23128 days ago1703755103IN
0x21E354Da...9Ffd5001F
0 ETH0.0026733235.3371515
Redeem188721922023-12-26 20:17:47130 days ago1703621867IN
0x21E354Da...9Ffd5001F
0 ETH0.0015636317.86909005
Redeem188635192023-12-25 15:05:47131 days ago1703516747IN
0x21E354Da...9Ffd5001F
0 ETH0.0023720626.86700886
Redeem188632312023-12-25 14:07:35131 days ago1703513255IN
0x21E354Da...9Ffd5001F
0 ETH0.0032343236.63342886
Redeem188610722023-12-25 6:47:59131 days ago1703486879IN
0x21E354Da...9Ffd5001F
0 ETH0.0015577117.67584248
Deposit188608612023-12-25 6:05:35132 days ago1703484335IN
0x21E354Da...9Ffd5001F
0 ETH0.0014011512.59662073
Liquidate188603522023-12-25 4:22:59132 days ago1703478179IN
0x21E354Da...9Ffd5001F
0 ETH0.0027585515.00424194
Redeem188351862023-12-21 15:38:59135 days ago1703173139IN
0x21E354Da...9Ffd5001F
0 ETH0.0044983350.95008714
Redeem188349312023-12-21 14:47:23135 days ago1703170043IN
0x21E354Da...9Ffd5001F
0 ETH0.0038059443.49401397
Remove Collatera...187901242023-12-15 7:47:35141 days ago1702626455IN
0x21E354Da...9Ffd5001F
0 ETH0.0042554642.37458748
Remove Collatera...186915672023-12-01 12:28:59155 days ago1701433739IN
0x21E354Da...9Ffd5001F
0 ETH0.0045948643.67201672
Remove Collatera...186915622023-12-01 12:27:59155 days ago1701433679IN
0x21E354Da...9Ffd5001F
0 ETH0.0032997542.94933705
Repay Asset186915582023-12-01 12:27:11155 days ago1701433631IN
0x21E354Da...9Ffd5001F
0 ETH0.0033922341.67256429
Remove Collatera...186848322023-11-30 13:51:23156 days ago1701352283IN
0x21E354Da...9Ffd5001F
0 ETH0.0064636838
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
160299342022-11-23 3:11:59529 days ago1669173119  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FraxlendPair

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 100000 runs

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

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ========================== 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, bytes memory _customConfigData)
        FraxlendPairCore(_configData, _immutables, _customConfigData)
        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) {
        return symbolOfContract;
    }

    function decimals() public view override(ERC20, IERC20Metadata) returns (uint8) {
        return decimalsOfContract;
    }

    // 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
    // ============================================================================================

    event SetMaxOracleDelay(uint256 _oldDelay, uint256 _newDelay);

    function setMaxOracleDelay(uint256 _newDelay) external {
        if (msg.sender != TIME_LOCK_ADDRESS) revert OnlyTimeLock();
        emit SetMaxOracleDelay(maxOracleDelay, _newDelay);
        maxOracleDelay = _newDelay;
    }

    /// @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 3 of 23 : FraxlendPairConstants.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.17;

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ===================== 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 = 158_247_046; // 0.5% annual yield 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 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);
    error OracleStale(address _oracle);
}

File 4 of 23 : FraxlendPairCore.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.17;

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ========================= 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/IRateCalculatorV2.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;

    function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch) {
        _major = 2;
        _minor = 0;
        _patch = 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;
    uint256 public maxOracleDelay;

    // LTV Settings
    uint256 public immutable maxLTV;

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

    // Interest Rate Calculator Contract
    IRateCalculatorV2 public immutable rateContract; // For complex rate calculations

    // 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 Metadata
    string internal nameOfContract;
    string internal symbolOfContract;
    uint8 internal immutable decimalsOfContract;

    // 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 {
        uint32 lastBlock;
        uint32 feeToProtocolRate; // Fee amount 1e5 precision
        uint64 lastTimestamp;
        uint64 ratePerSec;
        uint64 fullUtilizationRate;
    }

    /// @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)
    constructor(bytes memory _configData, bytes memory _immutables, bytes memory _customConfigData) {
        // 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,
                uint64 _fullUtilizationRate
            ) = abi.decode(_configData, (address, address, address, address, uint256, address, uint64));

            // Pair Settings
            assetContract = IERC20(_asset);
            collateralContract = IERC20(_collateral);
            currentRateInfo.feeToProtocolRate = DEFAULT_PROTOCOL_FEE;
            currentRateInfo.fullUtilizationRate = _fullUtilizationRate;

            // 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 = IRateCalculatorV2(_rateContract);
        }

        {
            (
                string memory _nameOfContract,
                string memory _symbolOfContract,
                uint8 _decimalsOfContract,
                uint256 _maxLTV,
                uint256 _liquidationFee,
                uint256 _maturityDate,
                uint256 _penaltyRate,
                address[] memory _approvedBorrowers,
                address[] memory _approvedLenders,
                uint256 _maxOracleDelay
            ) = abi.decode(
                    _customConfigData,
                    (string, string, uint8, uint256, uint256, uint256, uint256, address[], address[], uint256)
                );

            // ERC20 Metadata
            nameOfContract = _nameOfContract;
            symbolOfContract = _symbolOfContract;
            decimalsOfContract = _decimalsOfContract;

            //Liquiation Fee Settings
            cleanLiquidationFee = _liquidationFee;
            dirtyLiquidationFee = (_liquidationFee * 90000) / LIQ_PRECISION; // 90% of clean fee

            // Oracle freshness setting
            maxOracleDelay = _maxOracleDelay;

            // Grab these in preparation for checks
            bool _isBorrowerWhitelistActive = _approvedBorrowers.length > 0;
            bool _isLenderWhitelistActive = _approvedLenders.length > 0;

            // If loan under-collateralized then enforce borrower whitelist, set maxLTV
            if (_maxLTV >= LTV_PRECISION && !_isBorrowerWhitelistActive) revert BorrowerWhitelistRequired();
            maxLTV = _maxLTV;

            // Maturity Date & Penalty Interest Rate (per Sec)
            maturityDate = _maturityDate;
            penaltyRate = _penaltyRate;

            // Set approved borrowers whitelist
            borrowerWhitelistActive = _isBorrowerWhitelistActive;
            // Set approved lenders whitelist active
            lenderWhitelistActive = _isLenderWhitelistActive;

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

            // 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 = uint32(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 {
                (_newRate, _currentRateInfo.fullUtilizationRate) = IRateCalculatorV2(rateContract).getNewRate(
                    _deltaTime,
                    _utilizationRate,
                    _currentRateInfo.fullUtilizationRate
                );
            }

            // 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 = uint32(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);
        uint256 _maxOracleDelay = maxOracleDelay;
        if (oracleMultiply != address(0)) {
            (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(oracleMultiply).latestRoundData();
            if (_answer <= 0) {
                revert OracleLTEZero(oracleMultiply);
            }
            if (block.timestamp - _updatedAt > _maxOracleDelay) {
                revert OracleStale(oracleMultiply);
            }
            _price = _price * uint256(_answer);
        }

        if (oracleDivide != address(0)) {
            (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(oracleDivide).latestRoundData();
            if (_answer <= 0) {
                revert OracleLTEZero(oracleDivide);
            }
            if (block.timestamp - _updatedAt > _maxOracleDelay) {
                revert OracleStale(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 5 of 23 : VaultAccount.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.17;

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 6 of 23 : SafeERC20.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.17;

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 7 of 23 : IFraxlendWhitelist.sol
// SPDX-License-Identifier: ISC
pragma solidity >=0.8.17;

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 8 of 23 : IERC4626.sol
// SPDX-License-Identifier: ISC
pragma solidity >=0.8.17;

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 9 of 23 : IRateCalculator.sol
// SPDX-License-Identifier: ISC
pragma solidity >=0.8.17;

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 10 of 23 : ISwapper.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17;

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

File 11 of 23 : 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 12 of 23 : 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 13 of 23 : 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 14 of 23 : 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);

  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 15 of 23 : IRateCalculatorV2.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.17;

interface IRateCalculatorV2 {
    function name() external view returns (string memory);

    function version() external view returns (uint256, uint256, uint256);

    function getNewRate(uint256 _deltaTime, uint256 _utilization, uint64 _maxInterest)
        external
        view
        returns (uint64 _newRatePerSec, uint64 _newMaxInterest);
}

File 16 of 23 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (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 Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

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

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

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        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 17 of 23 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 `from` to `to`.
     *
     * 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 18 of 23 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (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 Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        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 19 of 23 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (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 uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

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

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

    /**
     * @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
     *
     * _Available since v4.2._
     */
    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 uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(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
     *
     * _Available since v2.5._
     */
    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 uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

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

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(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
     *
     * _Available since v4.2._
     */
    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 uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

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

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(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
     *
     * _Available since v2.5._
     */
    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 uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

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

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(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
     *
     * _Available since v2.5._
     */
    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 uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(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
     *
     * _Available since v2.5._
     */
    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
     *
     * _Available since v2.5._
     */
    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.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136) {
        require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits");
        return int136(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 int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120) {
        require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits");
        return int120(value);
    }

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

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

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

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

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

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72) {
        require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits");
        return int72(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 int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56) {
        require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits");
        return int56(value);
    }

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

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40) {
        require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits");
        return int40(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 int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24) {
        require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits");
        return int24(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.
     *
     * _Available since v3.0._
     */
    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 20 of 23 : 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 21 of 23 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.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));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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 22 of 23 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 23 of 23 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 24 of 23 : 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;
    }
}

Settings
{
  "metadata": {
    "bytecodeHash": "none"
  },
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 100000
  },
  "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":"bytes","name":"_customConfigData","type":"bytes"}],"stateMutability":"nonpayable","type":"constructor"},{"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":[{"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":"address","name":"_oracle","type":"address"}],"name":"OracleStale","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":"uint256","name":"_oldDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newDelay","type":"uint256"}],"name":"SetMaxOracleDelay","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":"uint32","name":"lastBlock","type":"uint32"},{"internalType":"uint32","name":"feeToProtocolRate","type":"uint32"},{"internalType":"uint64","name":"lastTimestamp","type":"uint64"},{"internalType":"uint64","name":"ratePerSec","type":"uint64"},{"internalType":"uint64","name":"fullUtilizationRate","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","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":[],"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":"maxOracleDelay","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 IRateCalculatorV2","name":"","type":"address"}],"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":"uint256","name":"_newDelay","type":"uint256"}],"name":"setMaxOracleDelay","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":"uint256","name":"_major","type":"uint256"},{"internalType":"uint256","name":"_minor","type":"uint256"},{"internalType":"uint256","name":"_patch","type":"uint256"}],"stateMutability":"pure","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"}]

6102c0806040523462000ada5762007a02803803809162000021828562000e83565b833981019060608183031262000ada5780516001600160401b03811162000ada57826200005091830162000ea7565b60208201519091906001600160401b03811162000ada57836200007591830162000ea7565b60408201519093906001600160401b03811162000ada5762000098920162000ea7565b9060405192620000a88462000e67565b6000845260405193620000bb8562000e67565b600085528051906001600160401b038211620009ce5760035490600182811c9216801562000e40575b6020831014620009ad5781601f84931162000dcb575b50602090601f831160011462000d405760009262000d34575b50508160011b916000199060031b1c1916176003555b83516001600160401b038111620009ce57600454600181811c9116801562000d29575b6020821014620009ad57601f811162000cbf575b506020601f821160011462000c3557819293949560009262000c29575b50508160011b916000199060031b1c1916176004555b60055460405190336001600160a01b0382167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a36001600160a81b0319163360ff60a01b1916176005556001600655815160809083018390031262000ada57620002036020830162000f1e565b91620002126040820162000f1e565b620002206060830162000f1e565b916001600160a01b0390620002389060800162000f1e565b1693336101a05260018060a01b03166101c05260018060a01b03166101e05260018060a01b031660018060a01b03196009541617600955816102005260e08380518101031262000ada57620002906020840162000f1e565b6200029e6040850162000f1e565b91620002ad6060860162000f1e565b90620002bc6080870162000f1e565b9260a087015194620002df60e0620002d760c08b0162000f1e565b990162000f33565b6001600160a01b0392831660805290821660a052600c805467ffffffff00000001600160c01b031660c09290921b6001600160c01b03191691909117905582161515908162000bc7575b5062000adf576001600160a01b03821615158062000b64575b62000b43576001600160a01b0390811660c05290811660e0526101009190915260405163f3a51aa160e01b8152908316600482015290602090829060249082905afa90811562000b375760009162000b01575b501562000adf576001600160a01b031661018052805181016101408282031262000ada5760208201516001600160401b03811162000ada57620003e19060208084019185010162000ea7565b60408301516001600160401b03811162000ada57620004099060208085019186010162000ea7565b60608401519060ff8216820362000ada5760808501519260a086015160c08701519360e08801519361010089015160018060401b03811162000ada5762000459906020808b01918c010162000f62565b6101208a015190986001600160401b03821162000ada5761014091602080620004879301918d010162000f62565b990151845190946001600160401b038211620009ce57600a5490600182811c9216801562000acf575b6020831014620009ad5781601f84931162000a6e575b50602090601f8311600114620009f057600092620009e4575b50508160011b916000199060031b1c191617600a555b8051906001600160401b038211620009ce57600b5490600182811c92168015620009c3575b6020831014620009ad5781601f84931162000938575b50602090601f8311600114620008ad57600092620008a1575b50508160011b916000199060031b1c191617600b555b61022052806101405262015f9081818102048114821517156200088b57620186a091020461016052600755835115918551151593620186a08110158062000883575b620008715761012052610240526102605215610280526102a05260005b8151811015620006075762000601906001600160a01b03620005e1828562001025565b511660005260136020526040600020600160ff1982541617905562001015565b620005be565b8260005b815181101562000654576200064e906001600160a01b036200062e828562001025565b511660005260146020526040600020600160ff1982541617905562001015565b6200060b565b6200065e62001086565b505050506200066c6200177b565b50604051615fbd62001a058239608051818181610af901528181610f680152818161104d0152818161115c015281816113a801528181611dd00152818161366901526136bc015260a051818181610eea0152818161154f01528181611e5d01528181611f5101528181612060015281816122b0015281816136e101526157c6015260c0518181816123bc015281816137090152614fc6015260e05181818161373101528181613bba0152614ff1015261010051818181610364015281816115c1015261501f015261012051818181610387015281816104f801526153a90152610140518181816103ad01528181612add0152613a00015261016051818181612c9e01526131fd0152610180518181816106b0015281816137590152614d6e01526101a051818181610cb70152818161283a015261378101526101c051818181611b4d015261275201526101e05181818161047d015281816128760152818161327201526137a901526102005181818161065f01526137d0015261022051816138630152610240518181816103d301528181610bd80152614eec0152610260518181816103f901528181610b9d015261486101526102805181818161071a0152818161199501528181611d2d015281816137f701526139a201526102a051818181612a0c01528181612eab015281816131c0015281816134ad01526138200152615fbd90f35b604051631d74772960e31b8152600490fd5b5083620005a1565b634e487b7160e01b600052601160045260246000fd5b01519050388062000549565b600b60009081527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db99350601f198516905b8181106200091f575090846001959493921062000905575b505050811b01600b556200055f565b015160001960f88460031b161c19169055388080620008f6565b92936020600181928786015181550195019301620008de565b600b6000529091507f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9601f840160051c81019160208510620009a2575b90601f859493920160051c01905b81811062000992575062000530565b6000815584935060010162000983565b909150819062000975565b634e487b7160e01b600052602260045260246000fd5b91607f16916200051a565b634e487b7160e01b600052604160045260246000fd5b015190503880620004df565b600a60009081529350600080516020620079c283398151915291905b601f198416851062000a52576001945083601f1981161062000a38575b505050811b01600a55620004f5565b015160001960f88460031b161c1916905538808062000a29565b8181015183556020948501946001909301929091019062000a0c565b600a600052909150600080516020620079c2833981519152601f840160051c81016020851062000ac7575b90849392915b601f830160051c8201811062000ab7575050620004c6565b6000815585945060010162000a9f565b508062000a99565b91607f1691620004b0565b600080fd5b604051630f19dca360e01b81526001600160a01b039091166004820152602490fd5b62000b28915060203d60201162000b2f575b62000b1f818362000e83565b81019062000f48565b3862000395565b503d62000b13565b6040513d6000823e3d90fd5b604051630f19dca360e01b81526001600160a01b0383166004820152602490fd5b506040516373015f9360e11b81526001600160a01b0383166004820152602081602481885afa90811562000b375760009162000ba3575b501562000342565b62000bc0915060203d60201162000b2f5762000b1f818362000e83565b3862000b9b565b6373015f9360e11b81526001600160a01b03831660048201529050602081602481885afa90811562000b375760009162000c05575b50153862000329565b62000c22915060203d60201162000b2f5762000b1f818362000e83565b3862000bfc565b0151905038806200017d565b60046000908152601f198316967f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b92915b88811062000ca65750836001959697981062000c8c575b505050811b0160045562000193565b015160001960f88460031b161c1916905538808062000c7d565b9192602060018192868501518155019401920162000c66565b60046000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f830160051c8101916020841062000d1e575b601f0160051c01905b81811062000d11575062000160565b6000815560010162000d02565b909150819062000cf9565b90607f16906200014c565b01519050388062000113565b600360009081527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b9350601f198516905b81811062000db2575090846001959493921062000d98575b505050811b0160035562000129565b015160001960f88460031b161c1916905538808062000d89565b9293602060018192878601518155019501930162000d71565b60036000529091507fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f840160051c8101916020851062000e35575b90601f859493920160051c01905b81811062000e255750620000fa565b6000815584935060010162000e16565b909150819062000e08565b91607f1691620000e4565b604081019081106001600160401b03821117620009ce57604052565b602081019081106001600160401b03821117620009ce57604052565b601f909101601f19168101906001600160401b03821190821017620009ce57604052565b919080601f8401121562000ada578251906001600160401b038211620009ce576040519160209162000ee3601f8301601f191684018562000e83565b81845282828701011162000ada5760005b81811062000f0a57508260009394955001015290565b858101830151848201840152820162000ef4565b51906001600160a01b038216820362000ada57565b51906001600160401b038216820362000ada57565b9081602091031262000ada5751801515810362000ada5790565b9080601f8301121562000ada578151906001600160401b038211620009ce578160051b6040519360209362000f9a8584018762000e83565b8552838086019282010192831162000ada578301905b82821062000fbf575050505090565b81516001600160a01b038116810362000ada57815290830190830162000fb0565b818102929181159184041417156200088b57565b811562000fff570490565b634e487b7160e01b600052601260045260246000fd5b60001981146200088b5760010190565b80518210156200103a5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b919082039182116200088b57565b919082018092116200088b57565b6001600160801b0391821690821601919082116200088b57565b60405160009182918291829160a081016001600160401b03811182821017620009ce57604052600c5463ffffffff8116825263ffffffff8160201c16602083015260018060401b038160401c1680604084015260018060401b038260801c169182606085015260c01c608084015242146200171d57506040516200110a8162000e4b565b600e546001600160801b038116825260801c60208201526040516200112f8162000e4b565b600f546001600160801b038116825260801c602082018190521580156200170d575b1562001202575050620011f09060ff60055460a01c1615620011f2575b426001600160401b0381166040838101919091524363ffffffff1680845260208085015160608601516080968701519190921b67ffffffff00000000169092179390921b6fffffffffffffffff00000000000000001692909217921b600160801b600160c01b03169190911760c09190911b6001600160c01b03191617600c55565b565b63096ea88660608201526200116e565b60408301519297509093509062001223906001600160401b03164262001050565b81519096906001600160801b0316620186a08082020481036200088b5784516200125e916001600160801b0390911690620186a00262000ff4565b9361024051801515908162001702575b50156200163f57610260516001600160401b0316945b60018060401b036060840151169060405191825289602083015260408201527fc63977c8e2362a31182dc8e89a52252f9836922738e0abcfc0de6924972eafe5608060018060401b03881692836060820152a16060830152426001600160401b031660408301524363ffffffff1682528251670de0b6b3a7640000906200132f906200131a906001600160801b03168b62000fe0565b60608501516001600160401b03169062000fe0565b84519190049889916001600160801b03906200134e908216846200105e565b1115806200161d575b62001436575b50506020916200141d9160018060801b038151169060018060801b0319948591015160801b1617600e55620013a963ffffffff82511663ffffffff1663ffffffff19600c541617600c55565b602081810151600c805460408086015160608701516080978801519590961b67ffffffff000000001663ffffffff9093169290921791901b6fffffffffffffffff000000000000000016179290931b600160801b600160c01b03169190911760c09190911b6001600160c01b031916179055565b602060018060801b0383511692015160801b1617600f55565b84516001600160801b03838116916200145390839083166200106c565b16865283516001600160801b03916200146e9183166200106c565b16835263ffffffff60208501511680620014d4575b5091600080516020620079e283398151915260a0602095936200141d9560018060401b036060870151166040519283528883015260408201528b60608201528a6080820152a191899193506200135d565b909850620186a092939750620014ea9162000fe0565b0495620015276200150860018060801b036020850151168962000fe0565b835162001520908a906001600160801b031662001050565b9062000ff4565b60208301519096906001600160801b03906200154990828a169083166200106c565b1660208401523015620015d857602092600080516020620079e283398151915260a08b6200141d956200157f8c6002546200105e565b600255306000526000885260406000206200159c8d82546200105e565b90556040518c815260007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8a3093a39395509395505062001483565b60405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606490fd5b5082516001600160801b039062001637908216846200105e565b111562001357565b6101805160808301516040805163cd3181d560e01b8152600481018c9052602481018990526001600160401b0390921660448301529091829060649082906001600160a01b03165afa801562000b3757600091600091620016b1575b506001600160401b031660808401529462001284565b9150506040813d604011620016f9575b81620016d06040938362000e83565b8101031262000ada57620016f26020620016ea8362000f33565b920162000f33565b386200169b565b3d9150620016c1565b90504211386200126e565b5060ff60055460a01c1662001151565b60009650869550859450925050565b51906001600160501b038216820362000ada57565b908160a091031262000ada5762001758816200172c565b91602082015191604081015191620017786080606084015193016200172c565b90565b6040908151916200178c8362000e4b565b600d549263ffffffff8085169485835260201c60208301958187524214620019fd575060075460c0516001600160a01b03906ec097ce7bc90715b34b9f10000000009082168062001941575b50908060e05116928362001862575b5050620017fa9150610100519062000ff4565b946001600160e01b03808711620018515791602093917f4fc1b45960547ee95894b08a284c3c066cf5aca706a7420639c42c3ec2e118a4959388169052421680915263ffffffff1986841b1617600d5551848152a1565b845163057b0e2160e41b8152600490fd5b60a0600494885195868092633fabe5a360e21b82525afa801562001936576000948591620018f8575b506000851315620018dc57620018a2904262001050565b11620018c15750620017fa91620018b99162000ff4565b3880620017e7565b60e05186516338fd7ca960e21b815291166004820152602490fd5b60e05188516322ad99db60e21b81529084166004820152602490fd5b90506200192191945060a03d81116200192e575b62001918818362000e83565b81019062001741565b509592505093386200188b565b503d6200190c565b87513d6000823e3d90fd5b60a0600491885192838092633fabe5a360e21b82525afa801562001936576000918291620019d3575b506000821315620019b7576200198285914262001050565b116200199b57808202918204036200088b5738620017d8565b60c05187516338fd7ca960e21b81529084166004820152602490fd5b60c05188516322ad99db60e21b81529085166004820152602490fd5b9050620019f1915060a03d81116200192e5762001918818362000e83565b5092505090386200196a565b945050505056fe608080604052600436101561001357600080fd5b60003560e01c90816253f73314613b8f5750806302ce728f14613b6057806306fdde0314613aa2578063095ea7b314613a7c5780630d09365c14613a2357806311a2e4bc146139e857806318160ddd146139c75780632165d72f1461398a57806323b872dd14613887578063313ce5671461384957806336fad62d1461368d57806338d52e0f1461363c57806339509351146135dd5780633cc32aba1461349c5780633d417d2d1461342e5780633f2617cb1461337a5780633f4ba83a14613249578063404ffa7a146132205780634732428c146131e55780634962e494146131a85780634ac8eb5f1461318a5780634fd422df1461314357806352f1edcc1461312557806354fd4d50146130fb57806356968f97146130d25780635c975abb146130ac578063657a409c146130785780636e553f6514612e6457806370a0823114612e1d578063715018a614612d9d578063721b0a47146129c95780638142dd53146128db5780638285ef40146128a05780638456cb5914612729578063891682d2146126745780638cad7fbe146126285780638da5cb5b146125f457806394e301e0146125c657806395d14ca81461257057806395d89b41146124385780639a295e73146123e05780639bdff2e61461238f578063a053db6814611c88578063a457c2d714611ba2578063a9059cbb14611b71578063ad0c3bb514611b20578063afa85de614611ad4578063b054898b14611984578063b5af30621461193d578063b68d0a09146118d5578063ba087652146115e4578063c10c92a1146115a9578063c270a54414611573578063c6e1c7c914611522578063c936c624146114d6578063ca2298fe14610e34578063cadac47914610ddb578063cd3b691c14610d47578063cdd72d5214610cdb578063d2a156e014610c8a578063d41ddc9614610bfb578063d59624b414610bc0578063d6b7494f14610b85578063daf33f2a1461090b578063dd62ed3e146108ac578063e5f13b16146106d4578063eee2421914610683578063ef14900d14610632578063f2fde38b1461051b578063f384bd05146104e0578063f9557ccb146104a1578063fbaa8b8514610450578063fbbbf94c146104245763fea10d5d1461034c57600080fd5b3461041f57600060031936011261041f5760a06040517f000000000000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060208201527f000000000000000000000000000000000000000000000000000000000000000060408201527f000000000000000000000000000000000000000000000000000000000000000060608201527f00000000000000000000000000000000000000000000000000000000000000006080820152f35b600080fd5b3461041f57600060031936011261041f576040600d5481519063ffffffff8116825260201c6020820152f35b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461041f57600060031936011261041f57600e54604080516fffffffffffffffffffffffffffffffff8316815260809290921c602083015290f35b0390f35b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461041f57602060031936011261041f57610534613c44565b61053c613e29565b73ffffffffffffffffffffffffffffffffffffffff8091169081156105ae57600554827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600555167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461041f57606060031936011261041f576024356106f0613c8a565b6106f8614eea565b6108825761070461429e565b6107136002600654141561451c565b60026006557f000000000000000000000000000000000000000000000000000000000000000080610869575b61083f576107739161074f614606565b5050505061075b614f6b565b508061082d575b5061076e600435615305565b6154a3565b600d5460201c9061078482336153a6565b15610799576020906001600655604051908152f35b506107ba6107a5614240565b33600052601260205260406000205490615ed8565b33600090815260116020526040908190205490517fed27783c000000000000000000000000000000000000000000000000000000008152600481019290925260248201527bffffffffffffffffffffffffffffffffffffffffffffffffffffffff919091166044820152606490fd5b0390fd5b61083990339033615700565b82610762565b60046040517f85e83bba000000000000000000000000000000000000000000000000000000008152fd5b5033600052601360205260ff604060002054161561073f565b60046040517fb063a8a5000000000000000000000000000000000000000000000000000000008152fd5b3461041f57604060031936011261041f576108c5613c44565b6108cd613c67565b9073ffffffffffffffffffffffffffffffffffffffff8091166000526001602052604060002091166000526020526020604060002054604051908152f35b3461041f57604060031936011261041f57610924613d4a565b61092c613c67565b90610935613e29565b809161093f61426f565b91610948614240565b936fffffffffffffffffffffffffffffffff80921615610b6e575b81811693826109846109758784615ed8565b9782808551169151169061430b565b16868110610b37576020877f14f6e172cd596e9f9c5d24e2d4010daa24f8f65f9274b259b66517b306c617b960608973ffffffffffffffffffffffffffffffffffffffff8a8a8a610a40828c816109e0818d168284511661430b565b168152816109f48d8301958287511661430b565b168452610a01883061433a565b51166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e541617600e55565b51600e805490921660809190911b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000161790556040517fa9059cbb000000000000000000000000000000000000000000000000000000008882015273ffffffffffffffffffffffffffffffffffffffff8216602482015260448101879052610b1d90610af781606481015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282613dd0565b7f0000000000000000000000000000000000000000000000000000000000000000615bbf565b6040519283521685820152836040820152a1604051908152f35b60449087604051917fc5bb6dae00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b503060005260006020528060406000205416610963565b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461041f57604060031936011261041f57610c55610c17613c67565b610c266002600654141561451c565b6002600655610c33614606565b50505050336000526012602052604060002054610c7c575b33906004356157ef565b600d5460201c610c6581336153a6565b15610c71576001600655005b6107ba6107a5614240565b610c84614f6b565b50610c4b565b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461041f57600060031936011261041f5760a0610cf661426f565b6fffffffffffffffffffffffffffffffff806020818451169301511690610d1b614240565b906020818351169201511690601054926040519485526020850152604084015260608301526080820152f35b3461041f57602060031936011261041f5760043573ffffffffffffffffffffffffffffffffffffffff600954163303610db1577f489892ab7e2f839a8630c507bc283ea1a98549bf7a6d8a315ebdf78e45794dae60406007548151908152836020820152a1600755005b60046040517f3b6b86b1000000000000000000000000000000000000000000000000000000008152fd5b3461041f57604060031936011261041f57610df4613c67565b610e036002600654141561451c565b6002600655610e10614eea565b61088257610e2d90610e20614606565b5050505060043533615700565b6001600655005b3461041f57608060031936011261041f57610e4d613c44565b60643567ffffffffffffffff811161041f57610e6d903690600401613cce565b610e7c6002600654141561451c565b6002600655610e89614606565b50505050610e95614f6b565b5073ffffffffffffffffffffffffffffffffffffffff8316600052600860205260ff60406000205416156114ac57801561147d5773ffffffffffffffffffffffffffffffffffffffff610ee7836144fb565b817f0000000000000000000000000000000000000000000000000000000000000000169182911603611425577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82018281116113f657610f50610f4b8285876144eb565b6144fb565b73ffffffffffffffffffffffffffffffffffffffff807f00000000000000000000000000000000000000000000000000000000000000001691160361136f5750610f9d33306024356157ef565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602480359082015290602090829060449082906000905af180156112a457611340575b50604051917f70a0823100000000000000000000000000000000000000000000000000000000835230600484015260208360248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9283156112a45760009361130c575b50906040519182917f38ed17390000000000000000000000000000000000000000000000000000000083526024356004840152604435602484015260a060448401528060a484015260c48301919060005b8181106112d357505050908060009230606483015242608483015203818373ffffffffffffffffffffffffffffffffffffffff88165af180156112a4576112b0575b50604051907f70a0823100000000000000000000000000000000000000000000000000000000825230600483015260208260248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa80156112a457600090611270575b611196925061432d565b906044358210611238576111a8614240565b6111d26111b58483615e1d565b916111bf85615305565b339130916111cc86615305565b916158d1565b73ffffffffffffffffffffffffffffffffffffffff60405192168252602435602083015282604083015260608201527fe947f0f9b6255bdcf76d13d1257d34fbe380e0d5d4daa75e61c783a41e1607ba60803392a2600d5460201c9061078482336153a6565b604482604051907f76baadda000000000000000000000000000000000000000000000000000000008252823560048301526024820152fd5b506020823d60201161129c575b8161128a60209383613dd0565b8101031261041f57611196915161118c565b3d915061127d565b6040513d6000823e3d90fd5b6112cc903d806000833e6112c48183613dd0565b810190615b45565b5082611111565b9193509160208060019273ffffffffffffffffffffffffffffffffffffffff6112fb88613cad565b1681520194019101918493926110cf565b9092506020813d602011611338575b8161132860209383613dd0565b8101031261041f5751918461107e565b3d915061131b565b6113619060203d602011611368575b6113598183613dd0565b810190615b2d565b5083611002565b503d61134f565b611380610f4b6108299285876144eb565b6040517fb0b3262d0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff908116600483015290911660248201529081906044820190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61142e836144fb565b6040517fb0b3262d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015291166024820152604490fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60046040517f1311dc6d000000000000000000000000000000000000000000000000000000008152fd5b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff611504613c44565b166000526013602052602060ff604060002054166040519015158152f35b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461041f57604060031936011261041f5760206115a1611591613cff565b60043561159c614240565b615e53565b604051908152f35b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461041f57606060031936011261041f57600435611600613c67565b611608613c8a565b6116176002600654141561451c565b6002600655611624614606565b5050505061163061426f565b9161163b8484615f36565b9261164e61164885615305565b95615305565b9173ffffffffffffffffffffffffffffffffffffffff918285169485330361185f575b61169e61167c614240565b6fffffffffffffffffffffffffffffffff91829182808751169151169061430b565b1692818a16938481106118145750610af78a96946117887ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db9795858b60209f8288816116f6611750946117de9d610acb9d511661430b565b168152602061170b908201948386511661430b565b93828516905251166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e541617600e55565b817fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e549260801b16911617600e558b169061433a565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008d82015273ffffffffffffffffffffffffffffffffffffffff8616602482015260448101919091529182906064820190565b604080516fffffffffffffffffffffffffffffffff95861681529590941660208601521692339290a46001600655604051908152f35b6040517fc5bb6dae00000000000000000000000000000000000000000000000000000000815260048101919091526fffffffffffffffffffffffffffffffff8b166024820152604490fd5b8560005260016020526040600020336000526020526040600020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81036118a8575b50611671565b6118c86118cf916fffffffffffffffffffffffffffffffff88169061432d565b33836140cb565b886118a2565b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff611903613c44565b1660005260006020526060604060002054601260205260406000205460116020526040600020549060405192835260208301526040820152f35b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff61196b613c44565b1660005260116020526020604060002054604051908152f35b3461041f5761199236613d0e565b917f000000000000000000000000000000000000000000000000000000000000000080611abb575b61083f5760005b8281106119ca57005b6119e29084158581611a8e575b6119e7575b506144be565b6119c1565b7f9c798cce2c4fdbec95a1a6dbba64db726912274dac938f44e36bce9b779cfee873ffffffffffffffffffffffffffffffffffffffff80611a2c610f4b868a8a6144eb565b1660005260209060138252611a708960406000209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b611a7e610f4b868a8a6144eb565b169260405190158152a2856119dc565b50611a9d610f4b8387876144eb565b73ffffffffffffffffffffffffffffffffffffffff163314156119d7565b5033600052601360205260ff60406000205416156119ba565b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff611b02613c44565b166000526014602052602060ff604060002054166040519015158152f35b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461041f57604060031936011261041f57611b97611b8d613c44565b6024359033613eb5565b602060405160018152f35b3461041f57604060031936011261041f57611bbb613c44565b60243590336000526001602052604060002073ffffffffffffffffffffffffffffffffffffffff821660005260205260406000205491808310611c0457611b97920390336140cb565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152fd5b3461041f5760a060031936011261041f57611ca1613c44565b6084359067ffffffffffffffff821161041f573660238301121561041f578160040135611ccd81613e11565b92611cdb6040519485613dd0565b818452602084016024819360051b8301019136831161041f57602401905b82821061237757505050611d0b614eea565b61088257611d1e6002600654141561451c565b6002600655611d2b61429e565b7f00000000000000000000000000000000000000000000000000000000000000008061235e575b61083f57611d5e614606565b50505050611d6a614f6b565b5073ffffffffffffffffffffffffffffffffffffffff8216600052600860205260ff60406000205416156114ac5773ffffffffffffffffffffffffffffffffffffffff611db684615b0c565b51169273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168094036122ec5780517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908181019081116113f657611e4473ffffffffffffffffffffffffffffffffffffffff9184615b19565b511673ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016036122445750604435612232575b611ea0611e99602435615305565b30906154a3565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015260248035908201529094602090829060449082906000905af180156112a457612213575b50604051917f70a0823100000000000000000000000000000000000000000000000000000000835230600484015260208360248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9283156112a4576000936121df575b506040517f38ed17390000000000000000000000000000000000000000000000000000000081526024803560048301526064359082015260a06044820152915160a48301819052829160c483019160005b8181106121b057505050908060009230606483015242608483015203818373ffffffffffffffffffffffffffffffffffffffff88165af180156112a457612195575b50604051907f70a0823100000000000000000000000000000000000000000000000000000000825230600483015260208260248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa80156112a457600090612161575b61209a925061432d565b6064358110612128576120ae338230615700565b6120ba81604435613ea8565b9273ffffffffffffffffffffffffffffffffffffffff6040519316835260243560208401526040830152604435606083015260808201527fb19ca0df3f3a01af950d8e6ad62aeff167cf14c73e98af6c52afef1add5c97ed60a03392a2600d5460201c9061078482336153a6565b604490604051907f76baadda00000000000000000000000000000000000000000000000000000000825260643560048301526024820152fd5b506020823d60201161218d575b8161217b60209383613dd0565b8101031261041f5761209a9151612090565b3d915061216e565b6121a9903d806000833e6112c48183613dd0565b5083612015565b825173ffffffffffffffffffffffffffffffffffffffff16845285945060209384019390920191600101611fd3565b9092506020813d60201161220b575b816121fb60209383613dd0565b8101031261041f57519185611f82565b3d91506121ee565b61222b9060203d602011611368576113598183613dd0565b5084611f06565b61223f3360443533615700565b611e8b565b81519081019081116113f65761227273ffffffffffffffffffffffffffffffffffffffff9161082993615b19565b516040517fb0b3262d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301529290911690911660248201529081906044820190565b73ffffffffffffffffffffffffffffffffffffffff61230b8592615b0c565b516040517fb0b3262d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff938416600482015291169091166024820152604490fd5b5033600052601360205260ff6040600020541615611d52565b6020809161238484613cad565b815201910190611cf9565b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461041f57600060031936011261041f57610100604051620186a08082528060208301528060408301526060820152670de0b6b3a7640000608082015263096ea88660a0820152600060c082015261c35060e0820152f35b3461041f57600060031936011261041f57604051600090600b54600181811c90808316928315612566575b6020938484108114612537578386529081156124f9575060011461249e575b6104dc8461249281880382613dd0565b60405191829182613bde565b600b60009081529294507f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db95b8284106124e657505050816104dc936124929282010193612482565b80548585018701529285019281016124ca565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b8201019150612492816104dc612482565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691612463565b3461041f57600060031936011261041f5760a0600c546040519063ffffffff80821683528160201c16602083015267ffffffffffffffff808260401c1660408401528160801c16606083015260c01c6080820152f35b3461041f57604060031936011261041f5760206115a16125e4613cff565b6004356125ef614240565b615f70565b3461041f57600060031936011261041f57602073ffffffffffffffffffffffffffffffffffffffff60055416604051908152f35b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff612656613c44565b166000526008602052602060ff604060002054166040519015158152f35b3461041f57602060031936011261041f5761268d613c44565b6009549073ffffffffffffffffffffffffffffffffffffffff80831691823303610db1576040805173ffffffffffffffffffffffffffffffffffffffff948516815293821660208501527fffffffffffffffffffffffff0000000000000000000000000000000000000000937f582d6cc2f042c43e00e0dd5c187f575daac294216d2afa075d9e1e27b0a40a949190a116911617600955600080f35b3461041f57600060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff807f0000000000000000000000000000000000000000000000000000000000000000163314159081612873575b81612863575b81612836575b5061280c57612795614606565b505050506127a161429e565b740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60055416176005557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b60046040517f8f5b12a7000000000000000000000000000000000000000000000000000000008152fd5b90507f00000000000000000000000000000000000000000000000000000000000000001633141581612788565b8091506005541633141590612782565b337f000000000000000000000000000000000000000000000000000000000000000082161415915061277c565b3461041f57600060031936011261041f57600f54604080516fffffffffffffffffffffffffffffffff8316815260809290921c602083015290f35b3461041f57602060031936011261041f5760043563ffffffff81169081810361041f5761290661429e565b73ffffffffffffffffffffffffffffffffffffffff600954163303610db15761c350821161299f577f58a58c712558f3d6e20bed57421eb8f73048d881dea9e5bb80efb37c49680d1c9160209161295b614606565b505050507fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff67ffffffff00000000600c5492851b16911617600c55604051908152a1005b60046040517fda0afa57000000000000000000000000000000000000000000000000000000008152fd5b3461041f57606060031936011261041f576129e2613d4a565b6024356129ed613c8a565b906129f661429e565b612a056002600654141561451c565b60026006557f000000000000000000000000000000000000000000000000000000000000000080612d74575b612d4a57804211612d135750612a45614606565b50505050612a51614f6b565b90612a5c82826153a6565b612ce957612a68614240565b9273ffffffffffffffffffffffffffffffffffffffff821693846000526011602052604060002054936012602052612aa4604060002054615305565b6fffffffffffffffffffffffffffffffff95670de0b6b3a7640000612ad588871694612ad08688615f36565b614581565b0490620186a07f000000000000000000000000000000000000000000000000000000000000000081018082116113f657612b10829185614581565b04612b23612b1d84615a5e565b91615a5e565b90600082820392128183128116918313901516176113f65760001280159390612c94575050926111cc87969360209a612bd19794612bd89a979b8c915b612b72612b6d888a615ed8565b615305565b9689600096600093612be5575b5050917f35f432a64bd3767447a456650432406c6cacb885819947a202216eeea6820ecf939160a093604051938452602084015281891660408401528187166060840152166080820152a233946145e2565b33836157ef565b6001600655604051908152f35b60a0949297507f35f432a64bd3767447a456650432406c6cacb885819947a202216eeea6820ecf959391612c189161430b565b96828b818a1680612c33575b50505091938b91939550612b7f565b82945090612c47612b6d612c519383615f36565b948591511661430b565b168b52600e547fffffffffffffffffffffffffffffffff0000000000000000000000000000000084612c858582851661430b565b16911617600e55828b38612c24565b94915094919695927f00000000000000000000000000000000000000000000000000000000000000008601908187116113f65760209a612bd899612bd198612ce06111cc958c9a614581565b049b8c91612b60565b60046040517f75e595fa000000000000000000000000000000000000000000000000000000008152fd5b604490604051907f5ba2a8d50000000000000000000000000000000000000000000000000000000082524260048301526024820152fd5b60046040517f95a584f7000000000000000000000000000000000000000000000000000000008152fd5b5033600052601460205260ff604060002054161580612a31575060ff6040600020541615612a31565b3461041f57600060031936011261041f57612db6613e29565b600073ffffffffffffffffffffffffffffffffffffffff6005547fffffffffffffffffffffffff00000000000000000000000000000000000000008116600555167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff612e4b613c44565b1660005260006020526020604060002054604051908152f35b3461041f57604060031936011261041f57600435612e80613c67565b90612e906002600654141561451c565b6002600655612e9d614eea565b61088257612ea961429e565b7f000000000000000000000000000000000000000000000000000000000000000080613035575b612d4a57602091612edf614606565b50505050612eeb61426f565b907fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d773ffffffffffffffffffffffffffffffffffffffff612f2f6116488686615e1d565b92612fff612f3c87615305565b956fffffffffffffffffffffffffffffffff9081612f5d88828451166145e2565b168152612f85828b83019281612f768c828751166145e2565b168452610a01828c1688614e25565b51817fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e549260801b16911617600e55604051907f23b872dd000000000000000000000000000000000000000000000000000000008a8301523360248301523060448301528616606482015260648152610af781613db4565b604080516fffffffffffffffffffffffffffffffff95861681529590941660208601521692339290a36001600655604051908152f35b5033600052601460205260ff604060002054161580612ed0575073ffffffffffffffffffffffffffffffffffffffff821660005260ff6040600020541615612ed0565b3461041f57600060031936011261041f57602073ffffffffffffffffffffffffffffffffffffffff60095416604051908152f35b3461041f57600060031936011261041f57602060ff60055460a01c166040519015158152f35b3461041f57604060031936011261041f5760206115a16130f0613cff565b60043561159c61426f565b3461041f57600060031936011261041f576060604051600281526000602082015260006040820152f35b3461041f57600060031936011261041f576020600754604051908152f35b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff613171613c44565b1660005260126020526020604060002054604051908152f35b3461041f57600060031936011261041f576020601054604051908152f35b3461041f57600060031936011261041f5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461041f57604060031936011261041f5760206115a161323e613cff565b6004356125ef61426f565b3461041f57600060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000000000000000000000000000000000000000000016331415908161336b575b5061280c576132a9614606565b5050505060055460ff8160a01c161561330d577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff166005557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152fd5b9050600554163314158161329c565b3461041f57604060031936011261041f577fea1eefb4fd58778d7b274fe54045a9feeec8f2847899c2e71126d3a74d486da560406133b6613c44565b73ffffffffffffffffffffffffffffffffffffffff6133d3613cff565b916133dc613e29565b169081600052600860205261341f81846000209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b825191825215156020820152a1005b3461041f57604060031936011261041f576020600435612bd861344f613c67565b61345e6002600654141561451c565b600260065561346b614606565b50505050613477614240565b6134818482615ed8565b9361349461348e86615305565b91615305565b9033926158d1565b3461041f576134aa36613d0e565b917f0000000000000000000000000000000000000000000000000000000000000000806135b4575b612d4a5760005b8281106134e257005b6134f99084158581613587575b6134fe57506144be565b6134d9565b7f3bb51c63bf139c4bc98211d74c51aafae8b743fd3090bee8b6bfe2026678a25073ffffffffffffffffffffffffffffffffffffffff80613543610f4b868a8a6144eb565b1660005260209060148252611a708960406000209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b503373ffffffffffffffffffffffffffffffffffffffff6135ac610f4b8589896144eb565b1614156134ef565b5033600052601460205260ff6040600020541615806134d2575060ff60406000205416156134d2565b3461041f57604060031936011261041f57611b976135f9613c44565b336000526001602052604060002073ffffffffffffffffffffffffffffffffffffffff8216600052602052613635602435604060002054613ea8565b90336140cb565b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461041f57600060031936011261041f5761014060405173ffffffffffffffffffffffffffffffffffffffff807f0000000000000000000000000000000000000000000000000000000000000000168252807f0000000000000000000000000000000000000000000000000000000000000000166020830152807f0000000000000000000000000000000000000000000000000000000000000000166040830152807f0000000000000000000000000000000000000000000000000000000000000000166060830152807f0000000000000000000000000000000000000000000000000000000000000000166080830152807f00000000000000000000000000000000000000000000000000000000000000001660a0830152807f00000000000000000000000000000000000000000000000000000000000000001660c08301527f00000000000000000000000000000000000000000000000000000000000000001660e08201527f000000000000000000000000000000000000000000000000000000000000000015156101008201527f00000000000000000000000000000000000000000000000000000000000000001515610120820152f35b3461041f57600060031936011261041f57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461041f57606060031936011261041f576138a0613c44565b6138a8613c67565b6044359073ffffffffffffffffffffffffffffffffffffffff83166000526001602052604060002033600052602052604060002054927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8403613910575b611b979350613eb5565b82841061392c5761392783611b97950333836140cb565b613906565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b3461041f57600060031936011261041f5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461041f57600060031936011261041f576020600e5460801c604051908152f35b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461041f57600060031936011261041f57613a436002600654141561451c565b6002600655608067ffffffffffffffff613a5b614606565b91600160069594955560405194855260208501526040840152166060820152f35b3461041f57604060031936011261041f57611b97613a98613c44565b60243590336140cb565b3461041f57600060031936011261041f57604051600090600a54600181811c90808316928315613b56575b6020938484108114612537578386529081156124f95750600114613afb576104dc8461249281880382613dd0565b600a60009081529294507fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a85b828410613b4357505050816104dc936124929282010193612482565b8054858501870152928501928101613b27565b91607f1691613acd565b3461041f57600060031936011261041f57613b806002600654141561451c565b60026006556020612bd8614f6b565b3461041f57600060031936011261041f5760209073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b60208082528251818301819052939260005b858110613c30575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201613bf0565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361041f57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361041f57565b6044359073ffffffffffffffffffffffffffffffffffffffff8216820361041f57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361041f57565b9181601f8401121561041f5782359167ffffffffffffffff831161041f576020808501948460051b01011161041f57565b60243590811515820361041f57565b604060031982011261041f576004359067ffffffffffffffff821161041f57613d3991600401613cce565b9091602435801515810361041f5790565b600435906fffffffffffffffffffffffffffffffff8216820361041f57565b6040810190811067ffffffffffffffff821117613d8557604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60a0810190811067ffffffffffffffff821117613d8557604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117613d8557604052565b67ffffffffffffffff8111613d855760051b60200190565b73ffffffffffffffffffffffffffffffffffffffff600554163303613e4a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b919082018092116113f657565b73ffffffffffffffffffffffffffffffffffffffff8091169182156140475716918215613fc357600082815280602052604081205491808310613f3f57604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815220613f34828254613ea8565b9055604051908152a3565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152fd5b73ffffffffffffffffffffffffffffffffffffffff8091169182156141bd57169182156141395760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260018252604060002085600052825280604060002055604051908152a3565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152fd5b6040519061424d82613d69565b600f546fffffffffffffffffffffffffffffffff8116835260801c6020830152565b6040519061427c82613d69565b600e546fffffffffffffffffffffffffffffffff8116835260801c6020830152565b60ff60055460a01c166142ad57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152fd5b6fffffffffffffffffffffffffffffffff91821690821603919082116113f657565b919082039182116113f657565b73ffffffffffffffffffffffffffffffffffffffff16801561443a576000918183528260205260408320548181106143b657817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef926020928587528684520360408620556143aa8160025461432d565b600255604051908152a3565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146113f65760010190565b919081101561147d5760051b0190565b3573ffffffffffffffffffffffffffffffffffffffff8116810361041f5790565b1561452357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b818102929181159184041417156113f657565b811561459e570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b519067ffffffffffffffff8216820361041f57565b9190916fffffffffffffffffffffffffffffffff808094169116019182116113f657565b60009060009060009060009060405160a0810181811067ffffffffffffffff821117613d8557604052600c5463ffffffff8116825263ffffffff8160201c16602083015267ffffffffffffffff8160401c1680604084015267ffffffffffffffff8260801c169182606085015260c01c60808401524214614e16575061468a61426f565b614692614240565b6fffffffffffffffffffffffffffffffff602082015116158015614e07575b156147e557505060ff60055460a01c16156147d6575b67ffffffffffffffff4216604082015261471563ffffffff431680835263ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000600c541617600c55565b61475d63ffffffff6020830151167fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff67ffffffff00000000600c549260201b16911617600c55565b600c54907fffffffffffffffff000000000000000000000000000000000000000000000000608077ffffffffffffffff000000000000000000000000000000006060840151821b1692015160c01b169167ffffffffffffffff6fffffffffffffffff00000000000000004260401b169116171717600c55565b63096ea88660608201526146c7565b919650925061480267ffffffffffffffff6040880151164261432d565b956fffffffffffffffffffffffffffffffff825116620186a08181020481036113f657614848906fffffffffffffffffffffffffffffffff86511690620186a002614594565b93614851614eea565b15614d0a5767ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016945b67ffffffffffffffff6060840151169060405191825289602083015260408201527fc63977c8e2362a31182dc8e89a52252f9836922738e0abcfc0de6924972eafe5608067ffffffffffffffff881692836060820152a1606083015267ffffffffffffffff4216604083015263ffffffff43168252670de0b6b3a76400006149366149216fffffffffffffffffffffffffffffffff8651168b614581565b67ffffffffffffffff60608601511690614581565b0480986fffffffffffffffffffffffffffffffff6149578187511684613ea8565b111580614ce4575b614b98575b5050906020826149c06fffffffffffffffffffffffffffffffff83969551166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e541617600e55565b01516fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e549260801b16911617600e55614a3e63ffffffff82511663ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000600c541617600c55565b614a8563ffffffff84830151167fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff67ffffffff00000000600c549260201b16911617600c55565b604081015190600c5467ffffffffffffffff6fffffffffffffffff00000000000000007fffffffffffffffff000000000000000000000000000000000000000000000000608077ffffffffffffffff000000000000000000000000000000006060870151821b1695015160c01b169460401b169116171717600c55614b546fffffffffffffffffffffffffffffffff8251166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f541617600f55565b01516fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f549260801b16911617600f55565b6fffffffffffffffffffffffffffffffff614bc981841682614bbd82828b51166145e2565b168852828651166145e2565b16835263ffffffff60208501511680614c40575b50917f50225349cc7e3814c4fa5fe6baef7a3c4cac55e92c64b1f4a5d1ba55e65dcc8260a0602096959493879467ffffffffffffffff6060880151166040519283528683015260408201528b60608201528a6080820152a1919293899150614964565b614cba99506fffffffffffffffffffffffffffffffff985060a0836020979695938b614cc8620186a0614c957f50225349cc7e3814c4fa5fe6baef7a3c4cac55e92c64b1f4a5d1ba55e65dcc82978d9a614581565b049e8f614cb4614caa858c8c01511683614581565b91858b511661432d565b90614594565b9d8e168289890151166145e2565b1686860152614cd78c30614e25565b9395969750935050614bdd565b506fffffffffffffffffffffffffffffffff614d038185511684613ea8565b111561495f565b67ffffffffffffffff608083015116604051907fcd3181d5000000000000000000000000000000000000000000000000000000008252896004830152866024830152604482015260408160648173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa80156112a457600091600091614db7575b5067ffffffffffffffff16608084015294614883565b9150506040813d604011614dff575b81614dd360409383613dd0565b8101031261041f5767ffffffffffffffff614df96020614df2846145cd565b93016145cd565b90614da1565b3d9150614dc6565b5060ff60055460a01c166146b1565b60009650869550859450925050565b73ffffffffffffffffffffffffffffffffffffffff16908115614e8c577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602082614e74600094600254613ea8565b60025584845283825260408420613f34828254613ea8565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b7f00000000000000000000000000000000000000000000000000000000000000008015159081614f18575090565b9050421190565b519069ffffffffffffffffffff8216820361041f57565b908160a091031261041f57614f4a81614f1f565b91602082015191604081015191614f68608060608401519301614f1f565b90565b604090815191614f7a83613d69565b600d549263ffffffff8085169485835260201c602083019581875242146152fe575060075473ffffffffffffffffffffffffffffffffffffffff6ec097ce7bc90715b34b9f10000000007f0000000000000000000000000000000000000000000000000000000000000000821680615201575b50907f000000000000000000000000000000000000000000000000000000000000000016806150f3575b5061504491507f000000000000000000000000000000000000000000000000000000000000000090614594565b947bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8087116150ca5791602093917f4fc1b45960547ee95894b08a284c3c066cf5aca706a7420639c42c3ec2e118a495938816905242168091527fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000086841b1617600d5551848152a1565b600485517f57b0e210000000000000000000000000000000000000000000000000000000008152fd5b8551927ffeaf968c00000000000000000000000000000000000000000000000000000000845260a084600481855afa80156151f65760009485916151bf575b50600085131561518f57615146904261432d565b1161515f57506150449161515991614594565b38615017565b6024908651907fe3f5f2a40000000000000000000000000000000000000000000000000000000082526004820152fd5b6024838951907f8ab6676c0000000000000000000000000000000000000000000000000000000082526004820152fd5b90506151e391945060a03d81116151ef575b6151db8183613dd0565b810190614f36565b50959250509338615132565b503d6151d1565b87513d6000823e3d90fd5b8651907ffeaf968c00000000000000000000000000000000000000000000000000000000825260a082600481845afa80156152f35760009283916152cc575b50600083131561529c5761525586914261432d565b1161526c5750808202918204036113f65738614fed565b6024908851907fe3f5f2a40000000000000000000000000000000000000000000000000000000082526004820152fd5b6024828a51907f8ab6676c0000000000000000000000000000000000000000000000000000000082526004820152fd5b90506152e791925060a03d81116151ef576151db8183613dd0565b50939250509138615240565b88513d6000823e3d90fd5b9450505050565b6fffffffffffffffffffffffffffffffff90818111615322571690565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f32382062697473000000000000000000000000000000000000000000000000006064820152fd5b907f000000000000000000000000000000000000000000000000000000000000000091821561549b5773ffffffffffffffffffffffffffffffffffffffff6153ec614240565b9116916154086000928484526012602052604084205490615ed8565b9283156154915782526011602052604082205492831561548957670de0b6b3a76400009161543591614581565b0490620186a09182810292818404149015171561545c57509061545791614594565b111590565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526011600452fd5b505091505090565b5050505050600190565b505050600190565b91906154ad614240565b6fffffffffffffffffffffffffffffffff806154d7816154cb61426f565b5116828551169061430b565b1691818616928381106156b55750805182166156625782915b806154ff8498828551166145e2565b16825261555f81602084019361551a828716838751166145e2565b94828616905251166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f541617600f55565b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f549260801b16911617600f5533600052601260205260406000206155a7828254613ea8565b905573ffffffffffffffffffffffffffffffffffffffff831692823085036155ff575b505060405191825260208201527f01348584ec81ac7acd52b7d66d9ade986dd909f3d513881c190fc31c90527efe60403392a3565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff9092166024830152604482015261565b90610af78160648101610acb565b38826155ca565b60208101918361569d61568561567b8487511684614581565b8486511690614594565b94836156948187511688614581565b91511690614594565b10156154f05791600181018091116113f657916154f0565b6040517fc5bb6dae00000000000000000000000000000000000000000000000000000000815260048101919091526fffffffffffffffffffffffffffffffff87166024820152604490fd5b9160207fa32435755c235de2976ed44a75a2f85cb01faf0c894f639fe0c32bb9455fea8f9173ffffffffffffffffffffffffffffffffffffffff8091169485600052601183526040600020615756868254613ea8565b905561576485601054613ea8565b601055169230840361577a575b604051908152a3565b6157ea6040517f23b872dd0000000000000000000000000000000000000000000000000000000084820152856024820152306044820152826064820152606481526157c481613db4565b7f0000000000000000000000000000000000000000000000000000000000000000615bbf565b615771565b73ffffffffffffffffffffffffffffffffffffffff80931692836000526011602052604060002061582183825461432d565b905561582f8260105461432d565b6010558216918130840361586e575b50506040519081527fbc290bb45104f73cf92115c9603987c3f8fd30c182a13603d8cffa49b5f5995260203392a4565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff909216602483015260448201526158ca906157c48160648101610acb565b388161583e565b93907f9dc1449a0ff0c152e18e8289d865b47acc6e1b76b1ecb239c13d6ee22a9206a792916fffffffffffffffffffffffffffffffff948561591684828a511661430b565b168752602087018661592b868284511661430b565b1681526159a78773ffffffffffffffffffffffffffffffffffffffff809516998a60005260126020526040600020615966838a16825461432d565b905551166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f541617600f55565b51867fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f549260801b16911617600f551693308503615a0a575b50604080516fffffffffffffffffffffffffffffffff928316815292909116602083015290a3565b615a5890604051907f23b872dd0000000000000000000000000000000000000000000000000000000060208301528660248301523060448301528316606482015260648152610af781613db4565b386159e2565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8111615a885790565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e743235360000000000000000000000000000000000000000000000006064820152fd5b80511561147d5760200190565b805182101561147d5760209160051b010190565b9081602091031261041f5751801515810361041f5790565b602090818184031261041f5780519067ffffffffffffffff821161041f57019180601f8401121561041f578251615b7b81613e11565b93615b896040519586613dd0565b818552838086019260051b82010192831161041f578301905b828210615bb0575050505090565b81518152908301908301615ba2565b73ffffffffffffffffffffffffffffffffffffffff9092919216604051615be581613d69565b6020918282527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656483830152803b15615d6d5760008581969282868195519301915af1933d15615d5e573d9467ffffffffffffffff8611615d3157615c8893949560405190615c7a877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160183613dd0565b81528092863d92013e615dcb565b80519081615c9557505050565b8280615ca5938301019101615b2d565b15615cad5750565b608490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b50615c88919293606090615dcb565b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b90919015615dd7575090565b815115615de75750805190602001fd5b610829906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301613bde565b6fffffffffffffffffffffffffffffffff8082511615600014615e3f57505090565b615694614f68938260208501511690614581565b90916fffffffffffffffffffffffffffffffff8083511615600014615e785750505090565b602083959492930190615e9b615e918284511685614581565b8288511690614594565b9584615ebb575b50505050615eac57565b90600181018091116113f65790565b615ece9394508161569491511687614581565b1038808080615ea2565b919060208301926fffffffffffffffffffffffffffffffff8085511615600014615f03575090925050565b908161569481615f26615f1c615f2f9686511688614581565b828a511690614594565b97511687614581565b10615eac57565b60208101906fffffffffffffffffffffffffffffffff908183511615600014615f5f5750505090565b614f68938261569492511690614581565b909160208201916fffffffffffffffffffffffffffffffff8084511615600014615f9b575050505090565b615e9b615e918284989795969851168561458156fea164736f6c6343000811000ac65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a850225349cc7e3814c4fa5fe6baef7a3c4cac55e92c64b1f4a5d1ba55e65dcc8200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000853d955acef822db058eb8505911ed77f175b99e000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027942afe4ecb7f9945168094e0749cac749ac97b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000018500cb1f2fe7a40ebda393383a0b8548a31f26100000000000000000000000000000000000000000000000000000001d79cea2c0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000fd3065c629ee890fd74f43b802c2fea4b7279b8c000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b0000000000000000000000008412ebf45bac1b340bbe8f318b928c466c4e39ca000000000000000000000000118c1462aa28bf2ea304f78f49c3388cfd93234e0000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000124f800000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000036467261786c656e6420496e7465726573742042656172696e67204652415820285374616b6564204672617820457468657229202d203900000000000000000000000000000000000000000000000000000000000000000000000000000000001066465241582873667278455448292d390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608080604052600436101561001357600080fd5b60003560e01c90816253f73314613b8f5750806302ce728f14613b6057806306fdde0314613aa2578063095ea7b314613a7c5780630d09365c14613a2357806311a2e4bc146139e857806318160ddd146139c75780632165d72f1461398a57806323b872dd14613887578063313ce5671461384957806336fad62d1461368d57806338d52e0f1461363c57806339509351146135dd5780633cc32aba1461349c5780633d417d2d1461342e5780633f2617cb1461337a5780633f4ba83a14613249578063404ffa7a146132205780634732428c146131e55780634962e494146131a85780634ac8eb5f1461318a5780634fd422df1461314357806352f1edcc1461312557806354fd4d50146130fb57806356968f97146130d25780635c975abb146130ac578063657a409c146130785780636e553f6514612e6457806370a0823114612e1d578063715018a614612d9d578063721b0a47146129c95780638142dd53146128db5780638285ef40146128a05780638456cb5914612729578063891682d2146126745780638cad7fbe146126285780638da5cb5b146125f457806394e301e0146125c657806395d14ca81461257057806395d89b41146124385780639a295e73146123e05780639bdff2e61461238f578063a053db6814611c88578063a457c2d714611ba2578063a9059cbb14611b71578063ad0c3bb514611b20578063afa85de614611ad4578063b054898b14611984578063b5af30621461193d578063b68d0a09146118d5578063ba087652146115e4578063c10c92a1146115a9578063c270a54414611573578063c6e1c7c914611522578063c936c624146114d6578063ca2298fe14610e34578063cadac47914610ddb578063cd3b691c14610d47578063cdd72d5214610cdb578063d2a156e014610c8a578063d41ddc9614610bfb578063d59624b414610bc0578063d6b7494f14610b85578063daf33f2a1461090b578063dd62ed3e146108ac578063e5f13b16146106d4578063eee2421914610683578063ef14900d14610632578063f2fde38b1461051b578063f384bd05146104e0578063f9557ccb146104a1578063fbaa8b8514610450578063fbbbf94c146104245763fea10d5d1461034c57600080fd5b3461041f57600060031936011261041f5760a06040517f000000000000000000000000000000000000000000000000000000000000000181527f00000000000000000000000000000000000000000000000000000000000124f860208201527f000000000000000000000000000000000000000000000000000000000000271060408201527f000000000000000000000000000000000000000000000000000000000000000060608201527f00000000000000000000000000000000000000000000000000000000000000006080820152f35b600080fd5b3461041f57600060031936011261041f576040600d5481519063ffffffff8116825260201c6020820152f35b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b168152f35b3461041f57600060031936011261041f57600e54604080516fffffffffffffffffffffffffffffffff8316815260809290921c602083015290f35b0390f35b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000124f88152f35b3461041f57602060031936011261041f57610534613c44565b61053c613e29565b73ffffffffffffffffffffffffffffffffffffffff8091169081156105ae57600554827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600555167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000118c1462aa28bf2ea304f78f49c3388cfd93234e168152f35b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000018500cb1f2fe7a40ebda393383a0b8548a31f261168152f35b3461041f57606060031936011261041f576024356106f0613c8a565b6106f8614eea565b6108825761070461429e565b6107136002600654141561451c565b60026006557f000000000000000000000000000000000000000000000000000000000000000080610869575b61083f576107739161074f614606565b5050505061075b614f6b565b508061082d575b5061076e600435615305565b6154a3565b600d5460201c9061078482336153a6565b15610799576020906001600655604051908152f35b506107ba6107a5614240565b33600052601260205260406000205490615ed8565b33600090815260116020526040908190205490517fed27783c000000000000000000000000000000000000000000000000000000008152600481019290925260248201527bffffffffffffffffffffffffffffffffffffffffffffffffffffffff919091166044820152606490fd5b0390fd5b61083990339033615700565b82610762565b60046040517f85e83bba000000000000000000000000000000000000000000000000000000008152fd5b5033600052601360205260ff604060002054161561073f565b60046040517fb063a8a5000000000000000000000000000000000000000000000000000000008152fd5b3461041f57604060031936011261041f576108c5613c44565b6108cd613c67565b9073ffffffffffffffffffffffffffffffffffffffff8091166000526001602052604060002091166000526020526020604060002054604051908152f35b3461041f57604060031936011261041f57610924613d4a565b61092c613c67565b90610935613e29565b809161093f61426f565b91610948614240565b936fffffffffffffffffffffffffffffffff80921615610b6e575b81811693826109846109758784615ed8565b9782808551169151169061430b565b16868110610b37576020877f14f6e172cd596e9f9c5d24e2d4010daa24f8f65f9274b259b66517b306c617b960608973ffffffffffffffffffffffffffffffffffffffff8a8a8a610a40828c816109e0818d168284511661430b565b168152816109f48d8301958287511661430b565b168452610a01883061433a565b51166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e541617600e55565b51600e805490921660809190911b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000161790556040517fa9059cbb000000000000000000000000000000000000000000000000000000008882015273ffffffffffffffffffffffffffffffffffffffff8216602482015260448101879052610b1d90610af781606481015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282613dd0565b7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e615bbf565b6040519283521685820152836040820152a1604051908152f35b60449087604051917fc5bb6dae00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b503060005260006020528060406000205416610963565b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461041f57604060031936011261041f57610c55610c17613c67565b610c266002600654141561451c565b6002600655610c33614606565b50505050336000526012602052604060002054610c7c575b33906004356157ef565b600d5460201c610c6581336153a6565b15610c71576001600655005b6107ba6107a5614240565b610c84614f6b565b50610c4b565b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000038488de975b77dc1b0d4b8569f596f6fd6ca0b92168152f35b3461041f57600060031936011261041f5760a0610cf661426f565b6fffffffffffffffffffffffffffffffff806020818451169301511690610d1b614240565b906020818351169201511690601054926040519485526020850152604084015260608301526080820152f35b3461041f57602060031936011261041f5760043573ffffffffffffffffffffffffffffffffffffffff600954163303610db1577f489892ab7e2f839a8630c507bc283ea1a98549bf7a6d8a315ebdf78e45794dae60406007548151908152836020820152a1600755005b60046040517f3b6b86b1000000000000000000000000000000000000000000000000000000008152fd5b3461041f57604060031936011261041f57610df4613c67565b610e036002600654141561451c565b6002600655610e10614eea565b61088257610e2d90610e20614606565b5050505060043533615700565b6001600655005b3461041f57608060031936011261041f57610e4d613c44565b60643567ffffffffffffffff811161041f57610e6d903690600401613cce565b610e7c6002600654141561451c565b6002600655610e89614606565b50505050610e95614f6b565b5073ffffffffffffffffffffffffffffffffffffffff8316600052600860205260ff60406000205416156114ac57801561147d5773ffffffffffffffffffffffffffffffffffffffff610ee7836144fb565b817f000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f169182911603611425577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82018281116113f657610f50610f4b8285876144eb565b6144fb565b73ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e1691160361136f5750610f9d33306024356157ef565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602480359082015290602090829060449082906000905af180156112a457611340575b50604051917f70a0823100000000000000000000000000000000000000000000000000000000835230600484015260208360248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e165afa9283156112a45760009361130c575b50906040519182917f38ed17390000000000000000000000000000000000000000000000000000000083526024356004840152604435602484015260a060448401528060a484015260c48301919060005b8181106112d357505050908060009230606483015242608483015203818373ffffffffffffffffffffffffffffffffffffffff88165af180156112a4576112b0575b50604051907f70a0823100000000000000000000000000000000000000000000000000000000825230600483015260208260248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e165afa80156112a457600090611270575b611196925061432d565b906044358210611238576111a8614240565b6111d26111b58483615e1d565b916111bf85615305565b339130916111cc86615305565b916158d1565b73ffffffffffffffffffffffffffffffffffffffff60405192168252602435602083015282604083015260608201527fe947f0f9b6255bdcf76d13d1257d34fbe380e0d5d4daa75e61c783a41e1607ba60803392a2600d5460201c9061078482336153a6565b604482604051907f76baadda000000000000000000000000000000000000000000000000000000008252823560048301526024820152fd5b506020823d60201161129c575b8161128a60209383613dd0565b8101031261041f57611196915161118c565b3d915061127d565b6040513d6000823e3d90fd5b6112cc903d806000833e6112c48183613dd0565b810190615b45565b5082611111565b9193509160208060019273ffffffffffffffffffffffffffffffffffffffff6112fb88613cad565b1681520194019101918493926110cf565b9092506020813d602011611338575b8161132860209383613dd0565b8101031261041f5751918461107e565b3d915061131b565b6113619060203d602011611368575b6113598183613dd0565b810190615b2d565b5083611002565b503d61134f565b611380610f4b6108299285876144eb565b6040517fb0b3262d0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e73ffffffffffffffffffffffffffffffffffffffff908116600483015290911660248201529081906044820190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61142e836144fb565b6040517fb0b3262d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015291166024820152604490fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60046040517f1311dc6d000000000000000000000000000000000000000000000000000000008152fd5b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff611504613c44565b166000526013602052602060ff604060002054166040519015158152f35b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f168152f35b3461041f57604060031936011261041f5760206115a1611591613cff565b60043561159c614240565b615e53565b604051908152f35b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000000018152f35b3461041f57606060031936011261041f57600435611600613c67565b611608613c8a565b6116176002600654141561451c565b6002600655611624614606565b5050505061163061426f565b9161163b8484615f36565b9261164e61164885615305565b95615305565b9173ffffffffffffffffffffffffffffffffffffffff918285169485330361185f575b61169e61167c614240565b6fffffffffffffffffffffffffffffffff91829182808751169151169061430b565b1692818a16938481106118145750610af78a96946117887ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db9795858b60209f8288816116f6611750946117de9d610acb9d511661430b565b168152602061170b908201948386511661430b565b93828516905251166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e541617600e55565b817fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e549260801b16911617600e558b169061433a565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008d82015273ffffffffffffffffffffffffffffffffffffffff8616602482015260448101919091529182906064820190565b604080516fffffffffffffffffffffffffffffffff95861681529590941660208601521692339290a46001600655604051908152f35b6040517fc5bb6dae00000000000000000000000000000000000000000000000000000000815260048101919091526fffffffffffffffffffffffffffffffff8b166024820152604490fd5b8560005260016020526040600020336000526020526040600020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81036118a8575b50611671565b6118c86118cf916fffffffffffffffffffffffffffffffff88169061432d565b33836140cb565b886118a2565b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff611903613c44565b1660005260006020526060604060002054601260205260406000205460116020526040600020549060405192835260208301526040820152f35b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff61196b613c44565b1660005260116020526020604060002054604051908152f35b3461041f5761199236613d0e565b917f000000000000000000000000000000000000000000000000000000000000000080611abb575b61083f5760005b8281106119ca57005b6119e29084158581611a8e575b6119e7575b506144be565b6119c1565b7f9c798cce2c4fdbec95a1a6dbba64db726912274dac938f44e36bce9b779cfee873ffffffffffffffffffffffffffffffffffffffff80611a2c610f4b868a8a6144eb565b1660005260209060138252611a708960406000209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b611a7e610f4b868a8a6144eb565b169260405190158152a2856119dc565b50611a9d610f4b8387876144eb565b73ffffffffffffffffffffffffffffffffffffffff163314156119d7565b5033600052601360205260ff60406000205416156119ba565b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff611b02613c44565b166000526014602052602060ff604060002054166040519015158152f35b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000fd3065c629ee890fd74f43b802c2fea4b7279b8c168152f35b3461041f57604060031936011261041f57611b97611b8d613c44565b6024359033613eb5565b602060405160018152f35b3461041f57604060031936011261041f57611bbb613c44565b60243590336000526001602052604060002073ffffffffffffffffffffffffffffffffffffffff821660005260205260406000205491808310611c0457611b97920390336140cb565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152fd5b3461041f5760a060031936011261041f57611ca1613c44565b6084359067ffffffffffffffff821161041f573660238301121561041f578160040135611ccd81613e11565b92611cdb6040519485613dd0565b818452602084016024819360051b8301019136831161041f57602401905b82821061237757505050611d0b614eea565b61088257611d1e6002600654141561451c565b6002600655611d2b61429e565b7f00000000000000000000000000000000000000000000000000000000000000008061235e575b61083f57611d5e614606565b50505050611d6a614f6b565b5073ffffffffffffffffffffffffffffffffffffffff8216600052600860205260ff60406000205416156114ac5773ffffffffffffffffffffffffffffffffffffffff611db684615b0c565b51169273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e168094036122ec5780517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908181019081116113f657611e4473ffffffffffffffffffffffffffffffffffffffff9184615b19565b511673ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f16036122445750604435612232575b611ea0611e99602435615305565b30906154a3565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015260248035908201529094602090829060449082906000905af180156112a457612213575b50604051917f70a0823100000000000000000000000000000000000000000000000000000000835230600484015260208360248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f165afa9283156112a4576000936121df575b506040517f38ed17390000000000000000000000000000000000000000000000000000000081526024803560048301526064359082015260a06044820152915160a48301819052829160c483019160005b8181106121b057505050908060009230606483015242608483015203818373ffffffffffffffffffffffffffffffffffffffff88165af180156112a457612195575b50604051907f70a0823100000000000000000000000000000000000000000000000000000000825230600483015260208260248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f165afa80156112a457600090612161575b61209a925061432d565b6064358110612128576120ae338230615700565b6120ba81604435613ea8565b9273ffffffffffffffffffffffffffffffffffffffff6040519316835260243560208401526040830152604435606083015260808201527fb19ca0df3f3a01af950d8e6ad62aeff167cf14c73e98af6c52afef1add5c97ed60a03392a2600d5460201c9061078482336153a6565b604490604051907f76baadda00000000000000000000000000000000000000000000000000000000825260643560048301526024820152fd5b506020823d60201161218d575b8161217b60209383613dd0565b8101031261041f5761209a9151612090565b3d915061216e565b6121a9903d806000833e6112c48183613dd0565b5083612015565b825173ffffffffffffffffffffffffffffffffffffffff16845285945060209384019390920191600101611fd3565b9092506020813d60201161220b575b816121fb60209383613dd0565b8101031261041f57519185611f82565b3d91506121ee565b61222b9060203d602011611368576113598183613dd0565b5084611f06565b61223f3360443533615700565b611e8b565b81519081019081116113f65761227273ffffffffffffffffffffffffffffffffffffffff9161082993615b19565b516040517fb0b3262d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f811660048301529290911690911660248201529081906044820190565b73ffffffffffffffffffffffffffffffffffffffff61230b8592615b0c565b516040517fb0b3262d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff938416600482015291169091166024820152604490fd5b5033600052601360205260ff6040600020541615611d52565b6020809161238484613cad565b815201910190611cf9565b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461041f57600060031936011261041f57610100604051620186a08082528060208301528060408301526060820152670de0b6b3a7640000608082015263096ea88660a0820152600060c082015261c35060e0820152f35b3461041f57600060031936011261041f57604051600090600b54600181811c90808316928315612566575b6020938484108114612537578386529081156124f9575060011461249e575b6104dc8461249281880382613dd0565b60405191829182613bde565b600b60009081529294507f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db95b8284106124e657505050816104dc936124929282010193612482565b80548585018701529285019281016124ca565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b8201019150612492816104dc612482565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691612463565b3461041f57600060031936011261041f5760a0600c546040519063ffffffff80821683528160201c16602083015267ffffffffffffffff808260401c1660408401528160801c16606083015260c01c6080820152f35b3461041f57604060031936011261041f5760206115a16125e4613cff565b6004356125ef614240565b615f70565b3461041f57600060031936011261041f57602073ffffffffffffffffffffffffffffffffffffffff60055416604051908152f35b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff612656613c44565b166000526008602052602060ff604060002054166040519015158152f35b3461041f57602060031936011261041f5761268d613c44565b6009549073ffffffffffffffffffffffffffffffffffffffff80831691823303610db1576040805173ffffffffffffffffffffffffffffffffffffffff948516815293821660208501527fffffffffffffffffffffffff0000000000000000000000000000000000000000937f582d6cc2f042c43e00e0dd5c187f575daac294216d2afa075d9e1e27b0a40a949190a116911617600955600080f35b3461041f57600060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000fd3065c629ee890fd74f43b802c2fea4b7279b8c163314159081612873575b81612863575b81612836575b5061280c57612795614606565b505050506127a161429e565b740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60055416176005557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b60046040517f8f5b12a7000000000000000000000000000000000000000000000000000000008152fd5b90507f00000000000000000000000038488de975b77dc1b0d4b8569f596f6fd6ca0b921633141581612788565b8091506005541633141590612782565b337f000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b82161415915061277c565b3461041f57600060031936011261041f57600f54604080516fffffffffffffffffffffffffffffffff8316815260809290921c602083015290f35b3461041f57602060031936011261041f5760043563ffffffff81169081810361041f5761290661429e565b73ffffffffffffffffffffffffffffffffffffffff600954163303610db15761c350821161299f577f58a58c712558f3d6e20bed57421eb8f73048d881dea9e5bb80efb37c49680d1c9160209161295b614606565b505050507fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff67ffffffff00000000600c5492851b16911617600c55604051908152a1005b60046040517fda0afa57000000000000000000000000000000000000000000000000000000008152fd5b3461041f57606060031936011261041f576129e2613d4a565b6024356129ed613c8a565b906129f661429e565b612a056002600654141561451c565b60026006557f000000000000000000000000000000000000000000000000000000000000000080612d74575b612d4a57804211612d135750612a45614606565b50505050612a51614f6b565b90612a5c82826153a6565b612ce957612a68614240565b9273ffffffffffffffffffffffffffffffffffffffff821693846000526011602052604060002054936012602052612aa4604060002054615305565b6fffffffffffffffffffffffffffffffff95670de0b6b3a7640000612ad588871694612ad08688615f36565b614581565b0490620186a07f000000000000000000000000000000000000000000000000000000000000271081018082116113f657612b10829185614581565b04612b23612b1d84615a5e565b91615a5e565b90600082820392128183128116918313901516176113f65760001280159390612c94575050926111cc87969360209a612bd19794612bd89a979b8c915b612b72612b6d888a615ed8565b615305565b9689600096600093612be5575b5050917f35f432a64bd3767447a456650432406c6cacb885819947a202216eeea6820ecf939160a093604051938452602084015281891660408401528187166060840152166080820152a233946145e2565b33836157ef565b6001600655604051908152f35b60a0949297507f35f432a64bd3767447a456650432406c6cacb885819947a202216eeea6820ecf959391612c189161430b565b96828b818a1680612c33575b50505091938b91939550612b7f565b82945090612c47612b6d612c519383615f36565b948591511661430b565b168b52600e547fffffffffffffffffffffffffffffffff0000000000000000000000000000000084612c858582851661430b565b16911617600e55828b38612c24565b94915094919695927f00000000000000000000000000000000000000000000000000000000000023288601908187116113f65760209a612bd899612bd198612ce06111cc958c9a614581565b049b8c91612b60565b60046040517f75e595fa000000000000000000000000000000000000000000000000000000008152fd5b604490604051907f5ba2a8d50000000000000000000000000000000000000000000000000000000082524260048301526024820152fd5b60046040517f95a584f7000000000000000000000000000000000000000000000000000000008152fd5b5033600052601460205260ff604060002054161580612a31575060ff6040600020541615612a31565b3461041f57600060031936011261041f57612db6613e29565b600073ffffffffffffffffffffffffffffffffffffffff6005547fffffffffffffffffffffffff00000000000000000000000000000000000000008116600555167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff612e4b613c44565b1660005260006020526020604060002054604051908152f35b3461041f57604060031936011261041f57600435612e80613c67565b90612e906002600654141561451c565b6002600655612e9d614eea565b61088257612ea961429e565b7f000000000000000000000000000000000000000000000000000000000000000080613035575b612d4a57602091612edf614606565b50505050612eeb61426f565b907fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d773ffffffffffffffffffffffffffffffffffffffff612f2f6116488686615e1d565b92612fff612f3c87615305565b956fffffffffffffffffffffffffffffffff9081612f5d88828451166145e2565b168152612f85828b83019281612f768c828751166145e2565b168452610a01828c1688614e25565b51817fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e549260801b16911617600e55604051907f23b872dd000000000000000000000000000000000000000000000000000000008a8301523360248301523060448301528616606482015260648152610af781613db4565b604080516fffffffffffffffffffffffffffffffff95861681529590941660208601521692339290a36001600655604051908152f35b5033600052601460205260ff604060002054161580612ed0575073ffffffffffffffffffffffffffffffffffffffff821660005260ff6040600020541615612ed0565b3461041f57600060031936011261041f57602073ffffffffffffffffffffffffffffffffffffffff60095416604051908152f35b3461041f57600060031936011261041f57602060ff60055460a01c166040519015158152f35b3461041f57604060031936011261041f5760206115a16130f0613cff565b60043561159c61426f565b3461041f57600060031936011261041f576060604051600281526000602082015260006040820152f35b3461041f57600060031936011261041f576020600754604051908152f35b3461041f57602060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff613171613c44565b1660005260126020526020604060002054604051908152f35b3461041f57600060031936011261041f576020601054604051908152f35b3461041f57600060031936011261041f5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000023288152f35b3461041f57604060031936011261041f5760206115a161323e613cff565b6004356125ef61426f565b3461041f57600060031936011261041f5773ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b16331415908161336b575b5061280c576132a9614606565b5050505060055460ff8160a01c161561330d577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff166005557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152fd5b9050600554163314158161329c565b3461041f57604060031936011261041f577fea1eefb4fd58778d7b274fe54045a9feeec8f2847899c2e71126d3a74d486da560406133b6613c44565b73ffffffffffffffffffffffffffffffffffffffff6133d3613cff565b916133dc613e29565b169081600052600860205261341f81846000209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b825191825215156020820152a1005b3461041f57604060031936011261041f576020600435612bd861344f613c67565b61345e6002600654141561451c565b600260065561346b614606565b50505050613477614240565b6134818482615ed8565b9361349461348e86615305565b91615305565b9033926158d1565b3461041f576134aa36613d0e565b917f0000000000000000000000000000000000000000000000000000000000000000806135b4575b612d4a5760005b8281106134e257005b6134f99084158581613587575b6134fe57506144be565b6134d9565b7f3bb51c63bf139c4bc98211d74c51aafae8b743fd3090bee8b6bfe2026678a25073ffffffffffffffffffffffffffffffffffffffff80613543610f4b868a8a6144eb565b1660005260209060148252611a708960406000209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b503373ffffffffffffffffffffffffffffffffffffffff6135ac610f4b8589896144eb565b1614156134ef565b5033600052601460205260ff6040600020541615806134d2575060ff60406000205416156134d2565b3461041f57604060031936011261041f57611b976135f9613c44565b336000526001602052604060002073ffffffffffffffffffffffffffffffffffffffff8216600052602052613635602435604060002054613ea8565b90336140cb565b3461041f57600060031936011261041f57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e168152f35b3461041f57600060031936011261041f5761014060405173ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e168252807f000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f166020830152807f0000000000000000000000000000000000000000000000000000000000000000166040830152807f00000000000000000000000027942afe4ecb7f9945168094e0749cac749ac97b166060830152807f00000000000000000000000018500cb1f2fe7a40ebda393383a0b8548a31f261166080830152807f00000000000000000000000038488de975b77dc1b0d4b8569f596f6fd6ca0b921660a0830152807f000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b1660c08301527f000000000000000000000000118c1462aa28bf2ea304f78f49c3388cfd93234e1660e08201527f000000000000000000000000000000000000000000000000000000000000000015156101008201527f00000000000000000000000000000000000000000000000000000000000000001515610120820152f35b3461041f57600060031936011261041f57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000012168152f35b3461041f57606060031936011261041f576138a0613c44565b6138a8613c67565b6044359073ffffffffffffffffffffffffffffffffffffffff83166000526001602052604060002033600052602052604060002054927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8403613910575b611b979350613eb5565b82841061392c5761392783611b97950333836140cb565b613906565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b3461041f57600060031936011261041f5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461041f57600060031936011261041f576020600e5460801c604051908152f35b3461041f57600060031936011261041f5760206040517f00000000000000000000000000000000000000000000000000000000000027108152f35b3461041f57600060031936011261041f57613a436002600654141561451c565b6002600655608067ffffffffffffffff613a5b614606565b91600160069594955560405194855260208501526040840152166060820152f35b3461041f57604060031936011261041f57611b97613a98613c44565b60243590336140cb565b3461041f57600060031936011261041f57604051600090600a54600181811c90808316928315613b56575b6020938484108114612537578386529081156124f95750600114613afb576104dc8461249281880382613dd0565b600a60009081529294507fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a85b828410613b4357505050816104dc936124929282010193612482565b8054858501870152928501928101613b27565b91607f1691613acd565b3461041f57600060031936011261041f57613b806002600654141561451c565b60026006556020612bd8614f6b565b3461041f57600060031936011261041f5760209073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000027942afe4ecb7f9945168094e0749cac749ac97b168152f35b60208082528251818301819052939260005b858110613c30575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201613bf0565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361041f57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361041f57565b6044359073ffffffffffffffffffffffffffffffffffffffff8216820361041f57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361041f57565b9181601f8401121561041f5782359167ffffffffffffffff831161041f576020808501948460051b01011161041f57565b60243590811515820361041f57565b604060031982011261041f576004359067ffffffffffffffff821161041f57613d3991600401613cce565b9091602435801515810361041f5790565b600435906fffffffffffffffffffffffffffffffff8216820361041f57565b6040810190811067ffffffffffffffff821117613d8557604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60a0810190811067ffffffffffffffff821117613d8557604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117613d8557604052565b67ffffffffffffffff8111613d855760051b60200190565b73ffffffffffffffffffffffffffffffffffffffff600554163303613e4a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b919082018092116113f657565b73ffffffffffffffffffffffffffffffffffffffff8091169182156140475716918215613fc357600082815280602052604081205491808310613f3f57604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815220613f34828254613ea8565b9055604051908152a3565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152fd5b73ffffffffffffffffffffffffffffffffffffffff8091169182156141bd57169182156141395760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260018252604060002085600052825280604060002055604051908152a3565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152fd5b6040519061424d82613d69565b600f546fffffffffffffffffffffffffffffffff8116835260801c6020830152565b6040519061427c82613d69565b600e546fffffffffffffffffffffffffffffffff8116835260801c6020830152565b60ff60055460a01c166142ad57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152fd5b6fffffffffffffffffffffffffffffffff91821690821603919082116113f657565b919082039182116113f657565b73ffffffffffffffffffffffffffffffffffffffff16801561443a576000918183528260205260408320548181106143b657817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef926020928587528684520360408620556143aa8160025461432d565b600255604051908152a3565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146113f65760010190565b919081101561147d5760051b0190565b3573ffffffffffffffffffffffffffffffffffffffff8116810361041f5790565b1561452357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b818102929181159184041417156113f657565b811561459e570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b519067ffffffffffffffff8216820361041f57565b9190916fffffffffffffffffffffffffffffffff808094169116019182116113f657565b60009060009060009060009060405160a0810181811067ffffffffffffffff821117613d8557604052600c5463ffffffff8116825263ffffffff8160201c16602083015267ffffffffffffffff8160401c1680604084015267ffffffffffffffff8260801c169182606085015260c01c60808401524214614e16575061468a61426f565b614692614240565b6fffffffffffffffffffffffffffffffff602082015116158015614e07575b156147e557505060ff60055460a01c16156147d6575b67ffffffffffffffff4216604082015261471563ffffffff431680835263ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000600c541617600c55565b61475d63ffffffff6020830151167fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff67ffffffff00000000600c549260201b16911617600c55565b600c54907fffffffffffffffff000000000000000000000000000000000000000000000000608077ffffffffffffffff000000000000000000000000000000006060840151821b1692015160c01b169167ffffffffffffffff6fffffffffffffffff00000000000000004260401b169116171717600c55565b63096ea88660608201526146c7565b919650925061480267ffffffffffffffff6040880151164261432d565b956fffffffffffffffffffffffffffffffff825116620186a08181020481036113f657614848906fffffffffffffffffffffffffffffffff86511690620186a002614594565b93614851614eea565b15614d0a5767ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016945b67ffffffffffffffff6060840151169060405191825289602083015260408201527fc63977c8e2362a31182dc8e89a52252f9836922738e0abcfc0de6924972eafe5608067ffffffffffffffff881692836060820152a1606083015267ffffffffffffffff4216604083015263ffffffff43168252670de0b6b3a76400006149366149216fffffffffffffffffffffffffffffffff8651168b614581565b67ffffffffffffffff60608601511690614581565b0480986fffffffffffffffffffffffffffffffff6149578187511684613ea8565b111580614ce4575b614b98575b5050906020826149c06fffffffffffffffffffffffffffffffff83969551166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e541617600e55565b01516fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff00000000000000000000000000000000600e549260801b16911617600e55614a3e63ffffffff82511663ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000600c541617600c55565b614a8563ffffffff84830151167fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff67ffffffff00000000600c549260201b16911617600c55565b604081015190600c5467ffffffffffffffff6fffffffffffffffff00000000000000007fffffffffffffffff000000000000000000000000000000000000000000000000608077ffffffffffffffff000000000000000000000000000000006060870151821b1695015160c01b169460401b169116171717600c55614b546fffffffffffffffffffffffffffffffff8251166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f541617600f55565b01516fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f549260801b16911617600f55565b6fffffffffffffffffffffffffffffffff614bc981841682614bbd82828b51166145e2565b168852828651166145e2565b16835263ffffffff60208501511680614c40575b50917f50225349cc7e3814c4fa5fe6baef7a3c4cac55e92c64b1f4a5d1ba55e65dcc8260a0602096959493879467ffffffffffffffff6060880151166040519283528683015260408201528b60608201528a6080820152a1919293899150614964565b614cba99506fffffffffffffffffffffffffffffffff985060a0836020979695938b614cc8620186a0614c957f50225349cc7e3814c4fa5fe6baef7a3c4cac55e92c64b1f4a5d1ba55e65dcc82978d9a614581565b049e8f614cb4614caa858c8c01511683614581565b91858b511661432d565b90614594565b9d8e168289890151166145e2565b1686860152614cd78c30614e25565b9395969750935050614bdd565b506fffffffffffffffffffffffffffffffff614d038185511684613ea8565b111561495f565b67ffffffffffffffff608083015116604051907fcd3181d5000000000000000000000000000000000000000000000000000000008252896004830152866024830152604482015260408160648173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000018500cb1f2fe7a40ebda393383a0b8548a31f261165afa80156112a457600091600091614db7575b5067ffffffffffffffff16608084015294614883565b9150506040813d604011614dff575b81614dd360409383613dd0565b8101031261041f5767ffffffffffffffff614df96020614df2846145cd565b93016145cd565b90614da1565b3d9150614dc6565b5060ff60055460a01c166146b1565b60009650869550859450925050565b73ffffffffffffffffffffffffffffffffffffffff16908115614e8c577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602082614e74600094600254613ea8565b60025584845283825260408420613f34828254613ea8565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b7f00000000000000000000000000000000000000000000000000000000000000008015159081614f18575090565b9050421190565b519069ffffffffffffffffffff8216820361041f57565b908160a091031261041f57614f4a81614f1f565b91602082015191604081015191614f68608060608401519301614f1f565b90565b604090815191614f7a83613d69565b600d549263ffffffff8085169485835260201c602083019581875242146152fe575060075473ffffffffffffffffffffffffffffffffffffffff6ec097ce7bc90715b34b9f10000000007f0000000000000000000000000000000000000000000000000000000000000000821680615201575b50907f00000000000000000000000027942afe4ecb7f9945168094e0749cac749ac97b16806150f3575b5061504491507f000000000000000000000000000000000000000000000000000000000000000190614594565b947bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8087116150ca5791602093917f4fc1b45960547ee95894b08a284c3c066cf5aca706a7420639c42c3ec2e118a495938816905242168091527fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000086841b1617600d5551848152a1565b600485517f57b0e210000000000000000000000000000000000000000000000000000000008152fd5b8551927ffeaf968c00000000000000000000000000000000000000000000000000000000845260a084600481855afa80156151f65760009485916151bf575b50600085131561518f57615146904261432d565b1161515f57506150449161515991614594565b38615017565b6024908651907fe3f5f2a40000000000000000000000000000000000000000000000000000000082526004820152fd5b6024838951907f8ab6676c0000000000000000000000000000000000000000000000000000000082526004820152fd5b90506151e391945060a03d81116151ef575b6151db8183613dd0565b810190614f36565b50959250509338615132565b503d6151d1565b87513d6000823e3d90fd5b8651907ffeaf968c00000000000000000000000000000000000000000000000000000000825260a082600481845afa80156152f35760009283916152cc575b50600083131561529c5761525586914261432d565b1161526c5750808202918204036113f65738614fed565b6024908851907fe3f5f2a40000000000000000000000000000000000000000000000000000000082526004820152fd5b6024828a51907f8ab6676c0000000000000000000000000000000000000000000000000000000082526004820152fd5b90506152e791925060a03d81116151ef576151db8183613dd0565b50939250509138615240565b88513d6000823e3d90fd5b9450505050565b6fffffffffffffffffffffffffffffffff90818111615322571690565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f32382062697473000000000000000000000000000000000000000000000000006064820152fd5b907f00000000000000000000000000000000000000000000000000000000000124f891821561549b5773ffffffffffffffffffffffffffffffffffffffff6153ec614240565b9116916154086000928484526012602052604084205490615ed8565b9283156154915782526011602052604082205492831561548957670de0b6b3a76400009161543591614581565b0490620186a09182810292818404149015171561545c57509061545791614594565b111590565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526011600452fd5b505091505090565b5050505050600190565b505050600190565b91906154ad614240565b6fffffffffffffffffffffffffffffffff806154d7816154cb61426f565b5116828551169061430b565b1691818616928381106156b55750805182166156625782915b806154ff8498828551166145e2565b16825261555f81602084019361551a828716838751166145e2565b94828616905251166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f541617600f55565b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f549260801b16911617600f5533600052601260205260406000206155a7828254613ea8565b905573ffffffffffffffffffffffffffffffffffffffff831692823085036155ff575b505060405191825260208201527f01348584ec81ac7acd52b7d66d9ade986dd909f3d513881c190fc31c90527efe60403392a3565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff9092166024830152604482015261565b90610af78160648101610acb565b38826155ca565b60208101918361569d61568561567b8487511684614581565b8486511690614594565b94836156948187511688614581565b91511690614594565b10156154f05791600181018091116113f657916154f0565b6040517fc5bb6dae00000000000000000000000000000000000000000000000000000000815260048101919091526fffffffffffffffffffffffffffffffff87166024820152604490fd5b9160207fa32435755c235de2976ed44a75a2f85cb01faf0c894f639fe0c32bb9455fea8f9173ffffffffffffffffffffffffffffffffffffffff8091169485600052601183526040600020615756868254613ea8565b905561576485601054613ea8565b601055169230840361577a575b604051908152a3565b6157ea6040517f23b872dd0000000000000000000000000000000000000000000000000000000084820152856024820152306044820152826064820152606481526157c481613db4565b7f000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f615bbf565b615771565b73ffffffffffffffffffffffffffffffffffffffff80931692836000526011602052604060002061582183825461432d565b905561582f8260105461432d565b6010558216918130840361586e575b50506040519081527fbc290bb45104f73cf92115c9603987c3f8fd30c182a13603d8cffa49b5f5995260203392a4565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff909216602483015260448201526158ca906157c48160648101610acb565b388161583e565b93907f9dc1449a0ff0c152e18e8289d865b47acc6e1b76b1ecb239c13d6ee22a9206a792916fffffffffffffffffffffffffffffffff948561591684828a511661430b565b168752602087018661592b868284511661430b565b1681526159a78773ffffffffffffffffffffffffffffffffffffffff809516998a60005260126020526040600020615966838a16825461432d565b905551166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f541617600f55565b51867fffffffffffffffffffffffffffffffff00000000000000000000000000000000600f549260801b16911617600f551693308503615a0a575b50604080516fffffffffffffffffffffffffffffffff928316815292909116602083015290a3565b615a5890604051907f23b872dd0000000000000000000000000000000000000000000000000000000060208301528660248301523060448301528316606482015260648152610af781613db4565b386159e2565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8111615a885790565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e743235360000000000000000000000000000000000000000000000006064820152fd5b80511561147d5760200190565b805182101561147d5760209160051b010190565b9081602091031261041f5751801515810361041f5790565b602090818184031261041f5780519067ffffffffffffffff821161041f57019180601f8401121561041f578251615b7b81613e11565b93615b896040519586613dd0565b818552838086019260051b82010192831161041f578301905b828210615bb0575050505090565b81518152908301908301615ba2565b73ffffffffffffffffffffffffffffffffffffffff9092919216604051615be581613d69565b6020918282527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656483830152803b15615d6d5760008581969282868195519301915af1933d15615d5e573d9467ffffffffffffffff8611615d3157615c8893949560405190615c7a877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160183613dd0565b81528092863d92013e615dcb565b80519081615c9557505050565b8280615ca5938301019101615b2d565b15615cad5750565b608490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b50615c88919293606090615dcb565b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b90919015615dd7575090565b815115615de75750805190602001fd5b610829906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301613bde565b6fffffffffffffffffffffffffffffffff8082511615600014615e3f57505090565b615694614f68938260208501511690614581565b90916fffffffffffffffffffffffffffffffff8083511615600014615e785750505090565b602083959492930190615e9b615e918284511685614581565b8288511690614594565b9584615ebb575b50505050615eac57565b90600181018091116113f65790565b615ece9394508161569491511687614581565b1038808080615ea2565b919060208301926fffffffffffffffffffffffffffffffff8085511615600014615f03575090925050565b908161569481615f26615f1c615f2f9686511688614581565b828a511690614594565b97511687614581565b10615eac57565b60208101906fffffffffffffffffffffffffffffffff908183511615600014615f5f5750505090565b614f68938261569492511690614581565b909160208201916fffffffffffffffffffffffffffffffff8084511615600014615f9b575050505090565b615e9b615e918284989795969851168561458156fea164736f6c6343000811000a

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000853d955acef822db058eb8505911ed77f175b99e000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027942afe4ecb7f9945168094e0749cac749ac97b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000018500cb1f2fe7a40ebda393383a0b8548a31f26100000000000000000000000000000000000000000000000000000001d79cea2c0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000fd3065c629ee890fd74f43b802c2fea4b7279b8c000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b0000000000000000000000008412ebf45bac1b340bbe8f318b928c466c4e39ca000000000000000000000000118c1462aa28bf2ea304f78f49c3388cfd93234e0000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000124f800000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000036467261786c656e6420496e7465726573742042656172696e67204652415820285374616b6564204672617820457468657229202d203900000000000000000000000000000000000000000000000000000000000000000000000000000000001066465241582873667278455448292d390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _configData (bytes): 0x000000000000000000000000853d955acef822db058eb8505911ed77f175b99e000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027942afe4ecb7f9945168094e0749cac749ac97b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000018500cb1f2fe7a40ebda393383a0b8548a31f26100000000000000000000000000000000000000000000000000000001d79cea2c
Arg [1] : _immutables (bytes): 0x000000000000000000000000fd3065c629ee890fd74f43b802c2fea4b7279b8c000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b0000000000000000000000008412ebf45bac1b340bbe8f318b928c466c4e39ca000000000000000000000000118c1462aa28bf2ea304f78f49c3388cfd93234e
Arg [2] : _customConfigData (bytes): 0x000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000124f800000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000036467261786c656e6420496e7465726573742042656172696e67204652415820285374616b6564204672617820457468657229202d203900000000000000000000000000000000000000000000000000000000000000000000000000000000001066465241582873667278455448292d390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

-----Encoded View---------------
34 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000200
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [4] : 000000000000000000000000853d955acef822db058eb8505911ed77f175b99e
Arg [5] : 000000000000000000000000ac3e018457b222d93114458476f3e3416abbe38f
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 00000000000000000000000027942afe4ecb7f9945168094e0749cac749ac97b
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [9] : 00000000000000000000000018500cb1f2fe7a40ebda393383a0b8548a31f261
Arg [10] : 00000000000000000000000000000000000000000000000000000001d79cea2c
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [12] : 000000000000000000000000fd3065c629ee890fd74f43b802c2fea4b7279b8c
Arg [13] : 000000000000000000000000168200cf227d4543302686124ac28ae0eaf2ca0b
Arg [14] : 0000000000000000000000008412ebf45bac1b340bbe8f318b928c466c4e39ca
Arg [15] : 000000000000000000000000118c1462aa28bf2ea304f78f49c3388cfd93234e
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000220
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [18] : 00000000000000000000000000000000000000000000000000000000000001a0
Arg [19] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [20] : 00000000000000000000000000000000000000000000000000000000000124f8
Arg [21] : 0000000000000000000000000000000000000000000000000000000000002710
Arg [22] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [23] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [24] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [25] : 0000000000000000000000000000000000000000000000000000000000000200
Arg [26] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [27] : 0000000000000000000000000000000000000000000000000000000000000036
Arg [28] : 467261786c656e6420496e7465726573742042656172696e6720465241582028
Arg [29] : 5374616b6564204672617820457468657229202d203900000000000000000000
Arg [30] : 0000000000000000000000000000000000000000000000000000000000000010
Arg [31] : 66465241582873667278455448292d3900000000000000000000000000000000
Arg [32] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [33] : 0000000000000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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