ETH Price: $3,393.73 (-3.57%)
Gas: 8 Gwei

Contract

0x3a25B9aB8c07FfEFEe614531C75905E810d8A239
 

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Remove Collatera...191011462024-01-27 23:22:11142 days ago1706397731IN
0x3a25B9aB...810d8A239
0 ETH0.00110389.33630136
Repay Asset191011282024-01-27 23:18:35142 days ago1706397515IN
0x3a25B9aB...810d8A239
0 ETH0.000820479.64211912
Borrow Asset191010472024-01-27 23:01:59142 days ago1706396519IN
0x3a25B9aB...810d8A239
0 ETH0.001764019.58372295
Repay Asset190721802024-01-23 21:56:59146 days ago1706047019IN
0x3a25B9aB...810d8A239
0 ETH0.0012113713.47569728
Remove Collatera...187218942023-12-05 18:21:35195 days ago1701800495IN
0x3a25B9aB...810d8A239
0 ETH0.0100033784.60299663
Repay Asset187218912023-12-05 18:20:59195 days ago1701800459IN
0x3a25B9aB...810d8A239
0 ETH0.0074931988.04651271
Borrow Asset186385722023-11-24 2:24:11207 days ago1700792651IN
0x3a25B9aB...810d8A239
0 ETH0.0054866721.1279548
Liquidate184062412023-10-22 13:49:35239 days ago1697982575IN
0x3a25B9aB...810d8A239
0 ETH0.001893427.85132044
Liquidate184060862023-10-22 13:17:59239 days ago1697980679IN
0x3a25B9aB...810d8A239
0 ETH0.000412817
Add Collateral181448892023-09-15 23:33:23276 days ago1694820803IN
0x3a25B9aB...810d8A239
0 ETH0.0010339211.59876609
Remove Collatera...180159552023-08-28 21:55:35294 days ago1693259735IN
0x3a25B9aB...810d8A239
0 ETH0.0044814437.91310373
Borrow Asset179742442023-08-23 1:51:11300 days ago1692755471IN
0x3a25B9aB...810d8A239
0 ETH0.0039890517.90634906
Borrow Asset179047692023-08-13 8:32:59310 days ago1691915579IN
0x3a25B9aB...810d8A239
0 ETH0.0007833212.4874926
Borrow Asset179047692023-08-13 8:32:59310 days ago1691915579IN
0x3a25B9aB...810d8A239
0 ETH0.0037313712.4874926
Remove Collatera...176919682023-07-14 13:22:11339 days ago1689340931IN
0x3a25B9aB...810d8A239
0 ETH0.0030580931.75100431
Repay Asset176919642023-07-14 13:21:23339 days ago1689340883IN
0x3a25B9aB...810d8A239
0 ETH0.0025740430.24982534
Borrow Asset176411652023-07-07 9:54:23347 days ago1688723663IN
0x3a25B9aB...810d8A239
0 ETH0.0061438123.93616155
Redeem175593592023-06-25 22:09:11358 days ago1687730951IN
0x3a25B9aB...810d8A239
0 ETH0.0012157714
Deposit175593492023-06-25 22:07:11358 days ago1687730831IN
0x3a25B9aB...810d8A239
0 ETH0.0015094913
Borrow Asset175135792023-06-19 11:44:23365 days ago1687175063IN
0x3a25B9aB...810d8A239
0 ETH0.0041230116.06556825
Borrow Asset174215322023-06-06 12:53:47377 days ago1686056027IN
0x3a25B9aB...810d8A239
0 ETH0.0071299324.90023135
Remove Collatera...174159882023-06-05 18:08:11378 days ago1685988491IN
0x3a25B9aB...810d8A239
0 ETH0.0054465356.54919757
Remove Collatera...174159652023-06-05 18:03:23378 days ago1685988203IN
0x3a25B9aB...810d8A239
0 ETH0.0062738663.11541919
Repay Asset174159632023-06-05 18:02:59378 days ago1685988179IN
0x3a25B9aB...810d8A239
0 ETH0.0055029564.68815078
Repay Asset174158582023-06-05 17:41:47378 days ago1685986907IN
0x3a25B9aB...810d8A239
0 ETH0.0041410546.06646745
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
167805402023-03-08 2:21:23468 days ago1678242083  Contract Creation0 ETH
Loading...
Loading

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

Contract Name:
FraxlendPair

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 1660 runs

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

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ========================== 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 "./FraxlendPairConstants.sol";
import "./FraxlendPairCore.sol";
import "./libraries/VaultAccount.sol";
import "./interfaces/ISwapper.sol";

/// @title FraxlendPair
/// @author Drake Evans (Frax Finance) https://github.com/drakeevans
/// @notice  The FraxlendPair is a lending pair that allows users to engage in lending and borrowing activities
contract FraxlendPair is IERC20Metadata, FraxlendPairCore {
    using VaultAccountingLibrary for VaultAccount;
    using SafeERC20 for IERC20;
    using SafeCast for uint256;

    /// @param _configData abi.encode(address _asset, address _collateral, address _oracle, uint32 _maxOracleDeviation, address _rateContract, uint64 _fullUtilizationRate, uint256 _maxLTV, uint256 _cleanLiquidationFee, uint256 _dirtyLiquidationFee, uint256 _protocolLiquidationFee)
    /// @param _immutables abi.encode(address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress)
    /// @param _customConfigData abi.encode(string memory _nameOfContract, string memory _symbolOfContract, uint8 _decimalsOfContract)
    constructor(
        bytes memory _configData,
        bytes memory _immutables,
        bytes memory _customConfigData
    ) FraxlendPairCore(_configData, _immutables, _customConfigData) {}

    // ============================================================================================
    // 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,
            uint256 _DEVIATION_PRECISION,
            uint256 _RATE_PRECISION,
            uint256 _MAX_PROTOCOL_FEE
        )
    {
        _LTV_PRECISION = LTV_PRECISION;
        _LIQ_PRECISION = LIQ_PRECISION;
        _UTIL_PREC = UTIL_PREC;
        _FEE_PRECISION = FEE_PRECISION;
        _EXCHANGE_PRECISION = EXCHANGE_PRECISION;
        _DEVIATION_PRECISION = DEVIATION_PRECISION;
        _RATE_PRECISION = RATE_PRECISION;
        _MAX_PROTOCOL_FEE = MAX_PROTOCOL_FEE;
    }

    /// @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, VaultAccount memory _totalBorrow) = previewAddInterest();
        _totalAssetAmount = _totalAsset.amount;
        _totalAssetShares = _totalAsset.shares;
        _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
    /// @param _previewInterest Whether to simulate interest accrual
    /// @return _shares The number of shares
    function toBorrowShares(
        uint256 _amount,
        bool _roundUp,
        bool _previewInterest
    ) external view returns (uint256 _shares) {
        if (_previewInterest) {
            (, , , , , VaultAccount memory _totalBorrow) = previewAddInterest();
            _shares = _totalBorrow.toShares(_amount, _roundUp);
        } else {
            _shares = 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
    /// @param _previewInterest Whether to simulate interest accrual
    /// @return _amount The amount of asset
    function toBorrowAmount(
        uint256 _shares,
        bool _roundUp,
        bool _previewInterest
    ) external view returns (uint256 _amount) {
        if (_previewInterest) {
            (, , , , , VaultAccount memory _totalBorrow) = previewAddInterest();
            _amount = _totalBorrow.toAmount(_shares, _roundUp);
        } else {
            _amount = 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
    /// @param _previewInterest Whether to preview interest accrual before calculation
    /// @return _amount The amount of asset
    function toAssetAmount(
        uint256 _shares,
        bool _roundUp,
        bool _previewInterest
    ) public view returns (uint256 _amount) {
        if (_previewInterest) {
            (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();
            _amount = _totalAsset.toAmount(_shares, _roundUp);
        } else {
            _amount = 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
    /// @param _previewInterest Whether to preview interest accrual before calculation
    /// @return _shares The number of shares (fTokens)
    function toAssetShares(
        uint256 _amount,
        bool _roundUp,
        bool _previewInterest
    ) public view returns (uint256 _shares) {
        if (_previewInterest) {
            (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();
            _shares = _totalAsset.toShares(_amount, _roundUp);
        } else {
            _shares = totalAsset.toShares(_amount, _roundUp);
        }
    }

    function convertToAssets(uint256 _shares) external view returns (uint256 _assets) {
        _assets = toAssetAmount(_shares, false, true);
    }

    function convertToShares(uint256 _assets) external view returns (uint256 _shares) {
        _shares = toAssetShares(_assets, false, true);
    }

    function pricePerShare() external view returns (uint256 _amount) {
        _amount = toAssetAmount(1e18, false, true);
    }

    function totalAssets() external view returns (uint256) {
        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();
        return _totalAsset.amount;
    }

    function maxDeposit(address _receiver) public view returns (uint256 _maxAssets) {
        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();
        _maxAssets = _totalAsset.amount < depositLimit ? depositLimit : depositLimit - _totalAsset.amount;
    }

    function maxMint(address _receiver) external view returns (uint256 _maxShares) {
        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();
        uint256 _maxDeposit = _totalAsset.amount < depositLimit ? depositLimit : depositLimit - _totalAsset.amount;
        _maxShares = _totalAsset.toShares(_maxDeposit, false);
    }

    function maxWithdraw(address _owner) external view returns (uint256 _maxAssets) {
        if (isWithdrawPaused) return 0;
        (
            ,
            ,
            uint256 _feesShare,
            ,
            VaultAccount memory _totalAsset,
            VaultAccount memory _totalBorrow
        ) = previewAddInterest();
        // Get the owner balance and include the fees share if owner is this contract
        uint256 _ownerBalance = _owner == address(this) ? balanceOf(_owner) + _feesShare : balanceOf(_owner);

        // Return the lower of total assets in contract or total assets available to _owner
        uint256 _totalAssetsAvailable = _totalAssetAvailable(_totalAsset, _totalBorrow);
        uint256 _totalUserWithdraw = _totalAsset.toAmount(_ownerBalance, false);
        _maxAssets = _totalAssetsAvailable < _totalUserWithdraw ? _totalAssetsAvailable : _totalUserWithdraw;
    }

    function maxRedeem(address _owner) external view returns (uint256 _maxShares) {
        if (isWithdrawPaused) return 0;
        (
            ,
            ,
            uint256 _feesShare,
            ,
            VaultAccount memory _totalAsset,
            VaultAccount memory _totalBorrow
        ) = previewAddInterest();

        // Calculate the total shares available
        uint256 _totalAssetsAvailable = _totalAssetAvailable(_totalAsset, _totalBorrow);
        uint256 _totalSharesAvailable = _totalAsset.toShares(_totalAssetsAvailable, false);

        // Get the owner balance and include the fees share if owner is this contract
        uint256 _ownerBalance = _owner == address(this) ? balanceOf(_owner) + _feesShare : balanceOf(_owner);
        _maxShares = _totalSharesAvailable < _ownerBalance ? _totalSharesAvailable : _ownerBalance;
    }

    // ============================================================================================
    // Functions: Configuration
    // ============================================================================================

    bool public isOracleSetterRevoked;

    /// @notice The ```RevokeOracleSetter``` event is emitted when the oracle setter is revoked
    event RevokeOracleInfoSetter();

    /// @notice The ```revokeOracleSetter``` function revokes the oracle setter
    function revokeOracleInfoSetter() external {
        _requireTimelock();
        isOracleSetterRevoked = true;
        emit RevokeOracleInfoSetter();
    }

    /// @notice The ```SetOracleInfo``` event is emitted when the oracle info (address and max deviation) is set
    /// @param oldOracle The old oracle address
    /// @param oldMaxOracleDeviation The old max oracle deviation
    /// @param newOracle The new oracle address
    /// @param newMaxOracleDeviation The new max oracle deviation
    event SetOracleInfo(
        address oldOracle,
        uint32 oldMaxOracleDeviation,
        address newOracle,
        uint32 newMaxOracleDeviation
    );

    /// @notice The ```setOracleInfo``` function sets the oracle data
    /// @param _newOracle The new oracle address
    /// @param _newMaxOracleDeviation The new max oracle deviation
    function setOracle(address _newOracle, uint32 _newMaxOracleDeviation) external {
        _requireTimelock();
        if (isOracleSetterRevoked) revert SetterRevoked();
        ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;
        emit SetOracleInfo(
            _exchangeRateInfo.oracle,
            _exchangeRateInfo.maxOracleDeviation,
            _newOracle,
            _newMaxOracleDeviation
        );
        _exchangeRateInfo.oracle = _newOracle;
        _exchangeRateInfo.maxOracleDeviation = _newMaxOracleDeviation;
        exchangeRateInfo = _exchangeRateInfo;
    }

    bool public isMaxLTVSetterRevoked;

    /// @notice The ```RevokeMaxLTVSetter``` event is emitted when the max LTV setter is revoked
    event RevokeMaxLTVSetter();

    /// @notice The ```revokeMaxLTVSetter``` function revokes the max LTV setter
    function revokeMaxLTVSetter() external {
        _requireTimelock();
        isMaxLTVSetterRevoked = true;
        emit RevokeMaxLTVSetter();
    }

    /// @notice The ```SetMaxLTV``` event is emitted when the max LTV is set
    /// @param oldMaxLTV The old max LTV
    /// @param newMaxLTV The new max LTV
    event SetMaxLTV(uint256 oldMaxLTV, uint256 newMaxLTV);

    /// @notice The ```setMaxLTV``` function sets the max LTV
    /// @param _newMaxLTV The new max LTV
    function setMaxLTV(uint256 _newMaxLTV) external {
        _requireTimelock();
        if (isMaxLTVSetterRevoked) revert SetterRevoked();
        emit SetMaxLTV(maxLTV, _newMaxLTV);
        maxLTV = _newMaxLTV;
    }

    bool public isRateContractSetterRevoked;

    /// @notice The ```RevokeRateContractSetter``` event is emitted when the rate contract setter is revoked
    event RevokeRateContractSetter();

    /// @notice The ```revokeRateContractSetter``` function revokes the rate contract setter
    function revokeRateContractSetter() external {
        _requireTimelock();
        isRateContractSetterRevoked = true;
        emit RevokeRateContractSetter();
    }

    /// @notice The ```SetRateContract``` event is emitted when the rate contract is set
    /// @param oldRateContract The old rate contract
    /// @param newRateContract The new rate contract
    event SetRateContract(address oldRateContract, address newRateContract);

    /// @notice The ```setRateContract``` function sets the rate contract address
    /// @param _newRateContract The new rate contract address
    function setRateContract(address _newRateContract) external {
        _requireTimelock();
        if (isRateContractSetterRevoked) revert SetterRevoked();
        emit SetRateContract(address(rateContract), _newRateContract);
        rateContract = IRateCalculatorV2(_newRateContract);
    }

    bool public isLiquidationFeeSetterRevoked;

    /// @notice The ```RevokeLiquidationFeeSetter``` event is emitted when the liquidation fee setter is revoked
    event RevokeLiquidationFeeSetter();

    /// @notice The ```revokeLiquidationFeeSetter``` function revokes the liquidation fee setter
    function revokeLiquidationFeeSetter() external {
        _requireTimelock();
        isLiquidationFeeSetterRevoked = true;
        emit RevokeLiquidationFeeSetter();
    }

    /// @notice The ```SetLiquidationFees``` event is emitted when the liquidation fees are set
    /// @param oldCleanLiquidationFee The old clean liquidation fee
    /// @param oldDirtyLiquidationFee The old dirty liquidation fee
    /// @param oldProtocolLiquidationFee The old protocol liquidation fee
    /// @param newCleanLiquidationFee The new clean liquidation fee
    /// @param newDirtyLiquidationFee The new dirty liquidation fee
    /// @param newProtocolLiquidationFee The new protocol liquidation fee
    event SetLiquidationFees(
        uint256 oldCleanLiquidationFee,
        uint256 oldDirtyLiquidationFee,
        uint256 oldProtocolLiquidationFee,
        uint256 newCleanLiquidationFee,
        uint256 newDirtyLiquidationFee,
        uint256 newProtocolLiquidationFee
    );

    /// @notice The ```setLiquidationFees``` function sets the liquidation fees
    /// @param _newCleanLiquidationFee The new clean liquidation fee
    /// @param _newDirtyLiquidationFee The new dirty liquidation fee
    function setLiquidationFees(
        uint256 _newCleanLiquidationFee,
        uint256 _newDirtyLiquidationFee,
        uint256 _newProtocolLiquidationFee
    ) external {
        _requireTimelock();
        if (isLiquidationFeeSetterRevoked) revert SetterRevoked();
        emit SetLiquidationFees(
            cleanLiquidationFee,
            dirtyLiquidationFee,
            protocolLiquidationFee,
            _newCleanLiquidationFee,
            _newDirtyLiquidationFee,
            _newProtocolLiquidationFee
        );
        cleanLiquidationFee = _newCleanLiquidationFee;
        dirtyLiquidationFee = _newDirtyLiquidationFee;
        protocolLiquidationFee = _newProtocolLiquidationFee;
    }

    /// @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 {
        _requireTimelock();
        if (isInterestPaused) revert InterestPaused();
        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, uint256 collateralAmount);

    /// @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) {
        if (_recipient == address(0)) revert InvalidReceiver();

        // Grab some data from state to save gas
        VaultAccount memory _totalAsset = totalAsset;

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

        _approve(address(this), msg.sender, _shares);
        _redeem(_totalAsset, _amountToTransfer.toUint128(), _shares, _recipient, address(this));
        uint256 _collateralAmount = userCollateralBalance[address(this)];
        _removeCollateral(_collateralAmount, _recipient, address(this));
        emit WithdrawFees(_shares, _recipient, _amountToTransfer, _collateralAmount);
    }

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

    // ============================================================================================
    // Functions: Access Control
    // ============================================================================================

    /// @notice The ```pause``` function is called to pause all contract functionality
    function pause() external {
        _requireProtocolOrOwner();
        if (!isBorrowAccessControlRevoked) _setBorrowLimit(0);
        if (!isDepositAccessControlRevoked) _setDepositLimit(0);
        if (!isRepayAccessControlRevoked) _pauseRepay(true);
        if (!isWithdrawAccessControlRevoked) _pauseWithdraw(true);
        if (!isLiquidateAccessControlRevoked) _pauseLiquidate(true);
        if (!isInterestAccessControlRevoked) {
            _addInterest();
            _pauseInterest(true);
        }
    }

    /// @notice The ```unpause``` function is called to unpause all contract functionality
    function unpause() external {
        _requireTimelockOrOwner();
        if (!isBorrowAccessControlRevoked) _setBorrowLimit(type(uint256).max);
        if (!isDepositAccessControlRevoked) _setDepositLimit(type(uint256).max);
        if (!isRepayAccessControlRevoked) _pauseRepay(false);
        if (!isWithdrawAccessControlRevoked) _pauseWithdraw(false);
        if (!isLiquidateAccessControlRevoked) _pauseLiquidate(false);
        if (!isInterestAccessControlRevoked) {
            _addInterest();
            _pauseInterest(false);
        }
    }

    /// @notice The ```pauseBorrow``` function sets borrow limit to 0
    function pauseBorrow() external {
        _requireProtocolOrOwner();
        if (isBorrowAccessControlRevoked) revert AccessControlRevoked();
        _setBorrowLimit(0);
    }

    /// @notice The ```setBorrowLimit``` function sets the borrow limit
    /// @param _limit The new borrow limit
    function setBorrowLimit(uint256 _limit) external {
        _requireTimelockOrOwner();
        if (isBorrowAccessControlRevoked) revert AccessControlRevoked();
        _setBorrowLimit(_limit);
    }

    /// @notice The ```revokeBorrowLimitAccessControl``` function revokes borrow limit access control
    /// @param _borrowLimit The new borrow limit
    function revokeBorrowLimitAccessControl(uint256 _borrowLimit) external {
        _requireTimelock();
        _revokeBorrowAccessControl(_borrowLimit);
    }

    /// @notice The ```pauseDeposit``` function pauses deposit functionality
    function pauseDeposit() external {
        _requireProtocolOrOwner();
        if (isDepositAccessControlRevoked) revert AccessControlRevoked();
        _setDepositLimit(0);
    }

    /// @notice The ```setDepositLimit``` function sets the deposit limit
    /// @param _limit The new deposit limit
    function setDepositLimit(uint256 _limit) external {
        _requireTimelockOrOwner();
        if (isDepositAccessControlRevoked) revert AccessControlRevoked();
        _setDepositLimit(_limit);
    }

    /// @notice The ```revokeDepositLimitAccessControl``` function revokes deposit limit access control
    /// @param _depositLimit The new deposit limit
    function revokeDepositLimitAccessControl(uint256 _depositLimit) external {
        _requireTimelock();
        _revokeDepositAccessControl(_depositLimit);
    }

    /// @notice The ```pauseRepay``` function pauses repay functionality
    /// @param _isPaused The new pause state
    function pauseRepay(bool _isPaused) external {
        if (_isPaused) {
            _requireProtocolOrOwner();
        } else {
            _requireTimelockOrOwner();
        }
        if (isRepayAccessControlRevoked) revert AccessControlRevoked();
        _pauseRepay(_isPaused);
    }

    /// @notice The ```revokeRepayAccessControl``` function revokes repay access control
    function revokeRepayAccessControl() external {
        _requireTimelock();
        _revokeRepayAccessControl();
    }

    /// @notice The ```pauseWithdraw``` function pauses withdraw functionality
    /// @param _isPaused The new pause state
    function pauseWithdraw(bool _isPaused) external {
        if (_isPaused) {
            _requireProtocolOrOwner();
        } else {
            _requireTimelockOrOwner();
        }
        if (isWithdrawAccessControlRevoked) revert AccessControlRevoked();
        _pauseWithdraw(_isPaused);
    }

    /// @notice The ```revokeWithdrawAccessControl``` function revokes withdraw access control
    function revokeWithdrawAccessControl() external {
        _requireTimelock();
        _revokeWithdrawAccessControl();
    }

    /// @notice The ```pauseLiquidate``` function pauses liquidate functionality
    /// @param _isPaused The new pause state
    function pauseLiquidate(bool _isPaused) external {
        if (_isPaused) {
            _requireProtocolOrOwner();
        } else {
            _requireTimelockOrOwner();
        }
        if (isLiquidateAccessControlRevoked) revert AccessControlRevoked();
        _pauseLiquidate(_isPaused);
    }

    /// @notice The ```revokeLiquidateAccessControl``` function revokes liquidate access control
    function revokeLiquidateAccessControl() external {
        _requireTimelock();
        _revokeLiquidateAccessControl();
    }

    /// @notice The ```pauseInterest``` function pauses interest functionality
    /// @param _isPaused The new pause state
    function pauseInterest(bool _isPaused) external {
        if (_isPaused) {
            _requireProtocolOrOwner();
        } else {
            _requireTimelockOrOwner();
        }
        if (isInterestAccessControlRevoked) revert AccessControlRevoked();
        // Resets the lastTimestamp which has the effect of no interest accruing over the pause period
        _addInterest();
        _pauseInterest(_isPaused);
    }

    /// @notice The ```revokeInterestAccessControl``` function revokes interest access control
    function revokeInterestAccessControl() external {
        _requireTimelock();
        _revokeInterestAccessControl();
    }
}

File 2 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 3 of 23 : Ownable2Step.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.0;

import "./Ownable.sol";

/**
 * @dev Contract module which provides 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} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() external {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }
}

File 4 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 5 of 23 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (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() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

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

File 6 of 23 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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.openzeppelin.com/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;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _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;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _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;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _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 7 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 8 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 9 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 10 of 23 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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 11 of 23 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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 12 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;
    }
}

File 13 of 23 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

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 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int128(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int64(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int32(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int16(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int8(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
    }

    /**
     * @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 14 of 23 : FraxlendPairAccessControl.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.18;

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

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

// Reviewers
// Dennis: https://github.com/denett

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

import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "./Timelock2Step.sol";
import "./FraxlendPairAccessControlErrors.sol";

/// @title FraxlendPairAccessControl
/// @author Drake Evans (Frax Finance) https://github.com/drakeevans
/// @notice  An abstract contract which contains the access control logic for FraxlendPair
abstract contract FraxlendPairAccessControl is Timelock2Step, Ownable2Step, FraxlendPairAccessControlErrors {
    // Deployer
    address public immutable DEPLOYER_ADDRESS;

    // Admin contracts
    address public circuitBreakerAddress;

    // access control
    uint256 public borrowLimit = type(uint256).max;
    bool public isBorrowAccessControlRevoked;

    uint256 public depositLimit = type(uint256).max;
    bool public isDepositAccessControlRevoked;

    bool public isRepayPaused;
    bool public isRepayAccessControlRevoked;

    bool public isWithdrawPaused;
    bool public isWithdrawAccessControlRevoked;

    bool public isLiquidatePaused;
    bool public isLiquidateAccessControlRevoked;

    bool public isInterestPaused;
    bool public isInterestAccessControlRevoked;

    /// @param _immutables abi.encode(address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress)
    constructor(bytes memory _immutables) Timelock2Step() Ownable() {
        // Handle Immutables Configuration
        (address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress) = abi.decode(
            _immutables,
            (address, address, address)
        );
        _setTimelock(_timelockAddress);
        _transferOwnership(_comptrollerAddress);

        // Deployer contract
        DEPLOYER_ADDRESS = msg.sender;
        circuitBreakerAddress = _circuitBreakerAddress;
    }

    // ============================================================================================
    // Functions: Access Control
    // ============================================================================================

    function _requireProtocolOrOwner() internal view {
        if (
            msg.sender != circuitBreakerAddress &&
            msg.sender != owner() &&
            msg.sender != DEPLOYER_ADDRESS &&
            msg.sender != timelockAddress
        ) {
            revert OnlyProtocolOrOwner();
        }
    }

    function _requireTimelockOrOwner() internal view {
        if (msg.sender != owner() && msg.sender != timelockAddress) {
            revert OnlyTimelockOrOwner();
        }
    }

    /// @notice The ```RevokeBorrowAccessControl``` event is emitted when access to borrow limit is revoked
    /// @param borrowLimit The final permanent borrow limit
    event RevokeBorrowAccessControl(uint256 borrowLimit);

    function _revokeBorrowAccessControl(uint256 _borrowLimit) internal {
        isBorrowAccessControlRevoked = true;
        borrowLimit = _borrowLimit;
        emit RevokeBorrowAccessControl(_borrowLimit);
    }

    /// @notice The ```SetBorrowLimit``` event is emitted when the borrow limit is set
    /// @param limit The new borrow limit
    event SetBorrowLimit(uint256 limit);

    function _setBorrowLimit(uint256 _limit) internal {
        borrowLimit = _limit;
        emit SetBorrowLimit(_limit);
    }

    /// @notice The ```RevokeDepositAccessControl``` event is emitted when access to deposit limit is revoked
    /// @param depositLimit The final permanent deposit limit
    event RevokeDepositAccessControl(uint256 depositLimit);

    function _revokeDepositAccessControl(uint256 _depositLimit) internal {
        isDepositAccessControlRevoked = true;
        depositLimit = _depositLimit;
        emit RevokeDepositAccessControl(_depositLimit);
    }

    /// @notice The ```SetDepositLimit``` event is emitted when the deposit limit is set
    /// @param limit The new deposit limit
    event SetDepositLimit(uint256 limit);

    function _setDepositLimit(uint256 _limit) internal {
        depositLimit = _limit;
        emit SetDepositLimit(_limit);
    }

    /// @notice The ```RevokeRepayAccessControl``` event is emitted when repay access control is revoked
    event RevokeRepayAccessControl();

    function _revokeRepayAccessControl() internal {
        isRepayAccessControlRevoked = true;
        emit RevokeRepayAccessControl();
    }

    /// @notice The ```PauseRepay``` event is emitted when repay is paused or unpaused
    /// @param isPaused The new paused state
    event PauseRepay(bool isPaused);

    function _pauseRepay(bool _isPaused) internal {
        isRepayPaused = _isPaused;
        emit PauseRepay(_isPaused);
    }

    /// @notice The ```RevokeWithdrawAccessControl``` event is emitted when withdraw access control is revoked
    event RevokeWithdrawAccessControl();

    function _revokeWithdrawAccessControl() internal {
        isWithdrawAccessControlRevoked = true;
        emit RevokeWithdrawAccessControl();
    }

    /// @notice The ```PauseWithdraw``` event is emitted when withdraw is paused or unpaused
    /// @param isPaused The new paused state
    event PauseWithdraw(bool isPaused);

    function _pauseWithdraw(bool _isPaused) internal {
        isWithdrawPaused = _isPaused;
        emit PauseWithdraw(_isPaused);
    }

    /// @notice The ```RevokeLiquidateAccessControl``` event is emitted when liquidate access control is revoked
    event RevokeLiquidateAccessControl();

    function _revokeLiquidateAccessControl() internal {
        isLiquidateAccessControlRevoked = true;
        emit RevokeLiquidateAccessControl();
    }

    /// @notice The ```PauseLiquidate``` event is emitted when liquidate is paused or unpaused
    /// @param isPaused The new paused state
    event PauseLiquidate(bool isPaused);

    function _pauseLiquidate(bool _isPaused) internal {
        isLiquidatePaused = _isPaused;
        emit PauseLiquidate(_isPaused);
    }

    /// @notice The ```RevokeInterestAccessControl``` event is emitted when interest access control is revoked
    event RevokeInterestAccessControl();

    function _revokeInterestAccessControl() internal {
        isInterestAccessControlRevoked = true;
        emit RevokeInterestAccessControl();
    }

    /// @notice The ```PauseInterest``` event is emitted when interest is paused or unpaused
    /// @param isPaused The new paused state
    event PauseInterest(bool isPaused);

    function _pauseInterest(bool _isPaused) internal {
        isInterestPaused = _isPaused;
        emit PauseInterest(_isPaused);
    }

    /// @notice The ```SetCircuitBreaker``` event is emitted when the circuit breaker address is set
    /// @param oldCircuitBreaker The old circuit breaker address
    /// @param newCircuitBreaker The new circuit breaker address
    event SetCircuitBreaker(address oldCircuitBreaker, address newCircuitBreaker);

    /// @notice The ```_setCircuitBreaker``` function is called to set the circuit breaker address
    /// @param _newCircuitBreaker The new circuit breaker address
    function _setCircuitBreaker(address _newCircuitBreaker) internal {
        address oldCircuitBreaker = circuitBreakerAddress;
        circuitBreakerAddress = _newCircuitBreaker;
        emit SetCircuitBreaker(oldCircuitBreaker, _newCircuitBreaker);
    }

    /// @notice The ```setCircuitBreaker``` function is called to set the circuit breaker address
    /// @param _newCircuitBreaker The new circuit breaker address
    function setCircuitBreaker(address _newCircuitBreaker) external virtual {
        _requireTimelock();
        _setCircuitBreaker(_newCircuitBreaker);
    }
}

File 15 of 23 : FraxlendPairAccessControlErrors.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.18;

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

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

// Reviewers
// Dennis: https://github.com/denett

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

/// @title FraxlendPairAccessControlErrors
/// @author Drake Evans (Frax Finance) https://github.com/drakeevans
/// @notice  An abstract contract which contains the errors for the Access Control contract
abstract contract FraxlendPairAccessControlErrors {
    error OnlyProtocolOrOwner();
    error OnlyTimelockOrOwner();
    error ExceedsBorrowLimit();
    error AccessControlRevoked();
    error RepayPaused();
    error ExceedsDepositLimit();
    error WithdrawPaused();
    error LiquidatePaused();
    error InterestPaused();
}

File 16 of 23 : FraxlendPairConstants.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.18;

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

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

/// @title FraxlendPairConstants
/// @author Drake Evans (Frax Finance) https://github.com/drakeevans
/// @notice  An abstract contract which contains the errors and constants for the FraxlendPair contract
abstract contract FraxlendPairConstants {
    // ============================================================================================
    // Constants
    // ============================================================================================

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

    // Protocol Fee
    uint256 public constant MAX_PROTOCOL_FEE = 5e4; // 50% 1e5 precision

    error Insolvent(uint256 _borrow, uint256 _collateral, uint256 _exchangeRate);
    error BorrowerSolvent();
    error InsufficientAssetsInContract(uint256 _assets, uint256 _request);
    error SlippageTooHigh(uint256 _minOut, uint256 _actual);
    error BadSwapper();
    error InvalidPath(address _expected, address _actual);
    error BadProtocolFee();
    error PastDeadline(uint256 _blockTimestamp, uint256 _deadline);
    error SetterRevoked();
    error ExceedsMaxOracleDeviation();
    error InvalidReceiver();
}

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

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ========================= 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/utils/math/SafeCast.sol";
import "./FraxlendPairAccessControl.sol";
import "./FraxlendPairConstants.sol";
import "./libraries/VaultAccount.sol";
import "./libraries/SafeERC20.sol";
import "./interfaces/IDualOracle.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 FraxlendPairAccessControl, FraxlendPairConstants, ERC20, 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 = 3;
        _minor = 0;
        _patch = 0;
    }

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

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

    // LTV Settings
    /// @notice The maximum LTV allowed for this pair
    /// @dev 1e5 precision
    uint256 public maxLTV;

    // Liquidation Fees
    /// @notice The liquidation fee, given as a % of repayment amount, when all collateral is consumed in liquidation
    /// @dev 1e5 precision
    uint256 public cleanLiquidationFee;
    /// @notice The liquidation fee, given as % of repayment amount, when some collateral remains for borrower
    /// @dev 1e5 precision
    uint256 public dirtyLiquidationFee;
    /// @notice The portion of the liquidation fee given to protocol
    /// @dev 1e5 precision
    uint256 public protocolLiquidationFee;

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

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

    // ERC20 Metadata
    string internal nameOfContract;
    string internal symbolOfContract;
    uint8 internal immutable decimalsOfContract;

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

    /// @notice Stores information about the current interest rate
    /// @dev struct is packed to reduce SLOADs. feeToProtocolRate is 1e5 precision, ratePerSec & fullUtilizationRate 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 {
        address oracle;
        uint32 maxOracleDeviation; // % of larger number, 1e5 precision
        uint184 lastTimestamp;
        uint256 lowExchangeRate;
        uint256 highExchangeRate;
    }

    // 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()

    // ============================================================================================
    // Constructor
    // ============================================================================================

    /// @notice The ```constructor``` function is called on deployment
    /// @param _configData abi.encode(address _asset, address _collateral, address _oracle, uint32 _maxOracleDeviation, address _rateContract, uint64 _fullUtilizationRate, uint256 _maxLTV, uint256 _cleanLiquidationFee, uint256 _dirtyLiquidationFee, uint256 _protocolLiquidationFee)
    /// @param _immutables abi.encode(address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress)
    /// @param _customConfigData abi.encode(string memory _nameOfContract, string memory _symbolOfContract, uint8 _decimalsOfContract)
    constructor(
        bytes memory _configData,
        bytes memory _immutables,
        bytes memory _customConfigData
    ) FraxlendPairAccessControl(_immutables) ERC20("", "") {
        {
            (
                address _asset,
                address _collateral,
                address _oracle,
                uint32 _maxOracleDeviation,
                address _rateContract,
                uint64 _fullUtilizationRate,
                uint256 _maxLTV,
                uint256 _liquidationFee,
                uint256 _protocolLiquidationFee
            ) = abi.decode(
                    _configData,
                    (address, address, address, uint32, address, uint64, uint256, uint256, uint256)
                );

            // Pair Settings
            assetContract = IERC20(_asset);
            collateralContract = IERC20(_collateral);

            currentRateInfo.feeToProtocolRate = 0;
            currentRateInfo.fullUtilizationRate = _fullUtilizationRate;
            currentRateInfo.lastTimestamp = uint64(block.timestamp - 1);
            currentRateInfo.lastBlock = uint32(block.number - 1);

            exchangeRateInfo.oracle = _oracle;
            exchangeRateInfo.maxOracleDeviation = _maxOracleDeviation;

            rateContract = IRateCalculatorV2(_rateContract);

            //Liquidation Fee Settings
            cleanLiquidationFee = _liquidationFee;
            dirtyLiquidationFee = (_liquidationFee * 90_000) / LIQ_PRECISION; // 90% of clean fee
            protocolLiquidationFee = _protocolLiquidationFee;

            // set maxLTV
            maxLTV = _maxLTV;
        }

        {
            (string memory _nameOfContract, string memory _symbolOfContract, uint8 _decimalsOfContract) = abi.decode(
                _customConfigData,
                (string, string, uint8)
            );

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

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

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

    /// @notice Checks for solvency AFTER executing contract code
    /// @param _borrower The borrower whose solvency we will check
    modifier isSolvent(address _borrower) {
        _;
        ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;

        if (!_isSolvent(_borrower, exchangeRateInfo.highExchangeRate)) {
            revert Insolvent(
                totalBorrow.toAmount(userBorrowShares[_borrower], true),
                userCollateralBalance[_borrower],
                exchangeRateInfo.highExchangeRate
            );
        }
    }

    // ============================================================================================
    // 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 feesAmount The amount of fees paid to protocol
    /// @param feesShare The amount of shares distributed to protocol
    event AddInterest(uint256 interestEarned, uint256 rate, uint256 feesAmount, uint256 feesShare);

    /// @notice The ```UpdateRate``` event is emitted when the interest rate is updated
    /// @param oldRatePerSec The old interest rate (per second)
    /// @param oldFullUtilizationRate The old full utilization rate
    /// @param newRatePerSec The new interest rate (per second)
    /// @param newFullUtilizationRate The new full utilization rate
    event UpdateRate(
        uint256 oldRatePerSec,
        uint256 oldFullUtilizationRate,
        uint256 newRatePerSec,
        uint256 newFullUtilizationRate
    );

    /// @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
    /// @return _feesAmount The amount of fees paid to protocol
    /// @return _feesShare The amount of shares distributed to protocol
    /// @return _currentRateInfo The new rate info struct
    /// @return _totalAsset The new total asset struct
    /// @return _totalBorrow The new total borrow struct
    function addInterest(
        bool _returnAccounting
    )
        external
        nonReentrant
        returns (
            uint256 _interestEarned,
            uint256 _feesAmount,
            uint256 _feesShare,
            CurrentRateInfo memory _currentRateInfo,
            VaultAccount memory _totalAsset,
            VaultAccount memory _totalBorrow
        )
    {
        (, _interestEarned, _feesAmount, _feesShare, _currentRateInfo) = _addInterest();
        if (_returnAccounting) {
            _totalAsset = totalAsset;
            _totalBorrow = totalBorrow;
        }
    }

    /// @notice The ```previewAddInterest``` function
    /// @return _interestEarned The amount of interest accrued by all borrowers
    /// @return _feesAmount The amount of fees paid to protocol
    /// @return _feesShare The amount of shares distributed to protocol
    /// @return _newCurrentRateInfo The new rate info struct
    /// @return _totalAsset The new total asset struct
    /// @return _totalBorrow The new total borrow struct
    function previewAddInterest()
        public
        view
        returns (
            uint256 _interestEarned,
            uint256 _feesAmount,
            uint256 _feesShare,
            CurrentRateInfo memory _newCurrentRateInfo,
            VaultAccount memory _totalAsset,
            VaultAccount memory _totalBorrow
        )
    {
        _newCurrentRateInfo = currentRateInfo;
        // Write return values
        InterestCalculationResults memory _results = _calculateInterest(_newCurrentRateInfo);

        if (_results.isInterestUpdated) {
            _interestEarned = _results.interestEarned;
            _feesAmount = _results.feesAmount;
            _feesShare = _results.feesShare;

            _newCurrentRateInfo.ratePerSec = _results.newRate;
            _newCurrentRateInfo.fullUtilizationRate = _results.newFullUtilizationRate;

            _totalAsset = _results.totalAsset;
            _totalBorrow = _results.totalBorrow;
        } else {
            _totalAsset = totalAsset;
            _totalBorrow = totalBorrow;
        }
    }

    struct InterestCalculationResults {
        bool isInterestUpdated;
        uint64 newRate;
        uint64 newFullUtilizationRate;
        uint256 interestEarned;
        uint256 feesAmount;
        uint256 feesShare;
        VaultAccount totalAsset;
        VaultAccount totalBorrow;
    }

    /// @notice The ```_calculateInterest``` function calculates the interest to be accrued and the new interest rate info
    /// @param _currentRateInfo The current rate info
    /// @return _results The results of the interest calculation
    function _calculateInterest(
        CurrentRateInfo memory _currentRateInfo
    ) internal view returns (InterestCalculationResults memory _results) {
        // Short circuit if interest already calculated this block OR if interest is paused
        if (_currentRateInfo.lastTimestamp != block.timestamp && !isInterestPaused) {
            // Indicate that interest is updated and calculated
            _results.isInterestUpdated = true;

            // Write return values and use these to save gas
            _results.totalAsset = totalAsset;
            _results.totalBorrow = totalBorrow;

            // Time elapsed since last interest update
            uint256 _deltaTime = block.timestamp - _currentRateInfo.lastTimestamp;

            // Get the utilization rate
            uint256 _utilizationRate = _results.totalAsset.amount == 0
                ? 0
                : (UTIL_PREC * _results.totalBorrow.amount) / _results.totalAsset.amount;

            // Request new interest rate and full utilization rate from the rate calculator
            (_results.newRate, _results.newFullUtilizationRate) = IRateCalculatorV2(rateContract).getNewRate(
                _deltaTime,
                _utilizationRate,
                _currentRateInfo.fullUtilizationRate
            );

            // Calculate interest accrued
            _results.interestEarned = (_deltaTime * _results.totalBorrow.amount * _results.newRate) / RATE_PRECISION;

            // Accrue interest (if any) and fees iff no overflow
            if (
                _results.interestEarned > 0 &&
                _results.interestEarned + _results.totalBorrow.amount <= type(uint128).max &&
                _results.interestEarned + _results.totalAsset.amount <= type(uint128).max
            ) {
                // Increment totalBorrow and totalAsset by interestEarned
                _results.totalBorrow.amount += uint128(_results.interestEarned);
                _results.totalAsset.amount += uint128(_results.interestEarned);
                if (_currentRateInfo.feeToProtocolRate > 0) {
                    _results.feesAmount =
                        (_results.interestEarned * _currentRateInfo.feeToProtocolRate) /
                        FEE_PRECISION;

                    _results.feesShare =
                        (_results.feesAmount * _results.totalAsset.shares) /
                        (_results.totalAsset.amount - _results.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
                    _results.totalAsset.shares += uint128(_results.feesShare);
                }
            }
        }
    }

    /// @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 _isInterestUpdated True if interest was calculated
    /// @return _interestEarned The amount of interest accrued by all borrowers
    /// @return _feesAmount The amount of fees paid to protocol
    /// @return _feesShare The amount of shares distributed to protocol
    /// @return _currentRateInfo The new rate info struct
    function _addInterest()
        internal
        returns (
            bool _isInterestUpdated,
            uint256 _interestEarned,
            uint256 _feesAmount,
            uint256 _feesShare,
            CurrentRateInfo memory _currentRateInfo
        )
    {
        // Pull from storage and set default return values
        _currentRateInfo = currentRateInfo;

        // Calc interest
        InterestCalculationResults memory _results = _calculateInterest(_currentRateInfo);

        // Write return values only if interest was updated and calculated
        if (_results.isInterestUpdated) {
            _isInterestUpdated = _results.isInterestUpdated;
            _interestEarned = _results.interestEarned;
            _feesAmount = _results.feesAmount;
            _feesShare = _results.feesShare;

            // emit here so that we have access to the old values
            emit UpdateRate(
                _currentRateInfo.ratePerSec,
                _currentRateInfo.fullUtilizationRate,
                _results.newRate,
                _results.newFullUtilizationRate
            );
            emit AddInterest(_interestEarned, _results.newRate, _feesAmount, _feesShare);

            // overwrite original values
            _currentRateInfo.ratePerSec = _results.newRate;
            _currentRateInfo.fullUtilizationRate = _results.newFullUtilizationRate;
            _currentRateInfo.lastTimestamp = uint64(block.timestamp);
            _currentRateInfo.lastBlock = uint32(block.number);

            // Effects: write to state
            currentRateInfo = _currentRateInfo;
            totalAsset = _results.totalAsset;
            totalBorrow = _results.totalBorrow;
            if (_feesShare > 0) _mint(address(this), _feesShare);
        }
    }

    // ============================================================================================
    // Functions: ExchangeRate
    // ============================================================================================

    /// @notice The ```UpdateExchangeRate``` event is emitted when the Collateral:Asset exchange rate is updated
    /// @param lowExchangeRate The low exchange rate
    /// @param highExchangeRate The high exchange rate
    event UpdateExchangeRate(uint256 lowExchangeRate, uint256 highExchangeRate);

    /// @notice The ```WarnOracleData``` event is emitted when one of the oracles has stale or otherwise problematic data
    /// @param oracle The oracle address
    event WarnOracleData(address oracle);

    /// @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 _isBorrowAllowed True if deviation is within bounds
    /// @return _lowExchangeRate The low exchange rate
    /// @return _highExchangeRate The high exchange rate
    function updateExchangeRate()
        external
        nonReentrant
        returns (bool _isBorrowAllowed, uint256 _lowExchangeRate, uint256 _highExchangeRate)
    {
        return _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 _isBorrowAllowed True if deviation is within bounds
    /// @return _lowExchangeRate The low exchange rate
    /// @return _highExchangeRate The high exchange rate

    function _updateExchangeRate()
        internal
        returns (bool _isBorrowAllowed, uint256 _lowExchangeRate, uint256 _highExchangeRate)
    {
        // Pull from storage to save gas and set default return values
        ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;

        // Short circuit if already updated this block
        if (_exchangeRateInfo.lastTimestamp != block.timestamp) {
            // Get the latest exchange rate from the dual oracle
            bool _oneOracleBad;
            (_oneOracleBad, _lowExchangeRate, _highExchangeRate) = IDualOracle(_exchangeRateInfo.oracle).getPrices();

            // If one oracle is bad data, emit an event for off-chain monitoring
            if (_oneOracleBad) emit WarnOracleData(_exchangeRateInfo.oracle);

            // Effects: Bookkeeping and write to storage
            _exchangeRateInfo.lastTimestamp = uint184(block.timestamp);
            _exchangeRateInfo.lowExchangeRate = _lowExchangeRate;
            _exchangeRateInfo.highExchangeRate = _highExchangeRate;
            exchangeRateInfo = _exchangeRateInfo;
            emit UpdateExchangeRate(_lowExchangeRate, _highExchangeRate);
        } else {
            // Use default return values if already updated this block
            _lowExchangeRate = _exchangeRateInfo.lowExchangeRate;
            _highExchangeRate = _exchangeRateInfo.highExchangeRate;
        }

        uint256 _deviation = (DEVIATION_PRECISION *
            (_exchangeRateInfo.highExchangeRate - _exchangeRateInfo.lowExchangeRate)) /
            _exchangeRateInfo.highExchangeRate;
        if (_deviation <= _exchangeRateInfo.maxOracleDeviation) {
            _isBorrowAllowed = true;
        }
    }

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

    function previewDeposit(uint256 _assets) external view returns (uint256 _sharesReceived) {
        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();
        _sharesReceived = _totalAsset.toShares(_assets, false);
    }

    /// @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 returns (uint256 _sharesReceived) {
        if (_receiver == address(0)) revert InvalidReceiver();

        // Accrue interest if necessary
        _addInterest();

        // Pull from storage to save gas
        VaultAccount memory _totalAsset = totalAsset;

        // Check if this deposit will violate the deposit limit
        if (depositLimit < _totalAsset.amount + _amount) revert ExceedsDepositLimit();

        // Calculate the number of fTokens to mint
        _sharesReceived = _totalAsset.toShares(_amount, false);

        // Execute the deposit effects
        _deposit(_totalAsset, _amount.toUint128(), _sharesReceived.toUint128(), _receiver);
    }

    function previewMint(uint256 _shares) external view returns (uint256 _amount) {
        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();
        _amount = _totalAsset.toAmount(_shares, false);
    }

    function mint(uint256 _shares, address _receiver) external nonReentrant returns (uint256 _amount) {
        if (_receiver == address(0)) revert InvalidReceiver();

        // Accrue interest if necessary
        _addInterest();

        // Pull from storage to save gas
        VaultAccount memory _totalAsset = totalAsset;

        // Calculate the number of assets to transfer based on the shares to mint
        _amount = _totalAsset.toAmount(_shares, false);

        // Check if this deposit will violate the deposit limit
        if (depositLimit < _totalAsset.amount + _amount) revert ExceedsDepositLimit();

        // Execute the deposit effects
        _deposit(_totalAsset, _amount.toUint128(), _shares.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 {
        // Check for sufficient allowance/approval if necessary
        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 (not strictly necessary because balance will underflow)
        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);
    }

    function previewRedeem(uint256 _shares) external view returns (uint256 _assets) {
        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();
        _assets = _totalAsset.toAmount(_shares, false);
    }

    /// @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) {
        if (_receiver == address(0)) revert InvalidReceiver();

        // Check if withdraw is paused and revert if necessary
        if (isWithdrawPaused) revert WithdrawPaused();

        // Accrue interest if necessary
        _addInterest();

        // Pull from storage to save gas
        VaultAccount memory _totalAsset = totalAsset;

        // Calculate the number of assets to transfer based on the shares to burn
        _amountToReturn = _totalAsset.toAmount(_shares, false);

        // Execute the withdraw effects
        _redeem(_totalAsset, _amountToReturn.toUint128(), _shares.toUint128(), _receiver, _owner);
    }

    /// @notice The ```previewWithdraw``` function returns the number of Asset Shares (fTokens) that would be burned for a given amount of Asset Tokens
    /// @param _amount The amount of Asset Tokens to be withdrawn
    /// @return _sharesToBurn The number of shares that would be burned
    function previewWithdraw(uint256 _amount) external view returns (uint256 _sharesToBurn) {
        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();
        _sharesToBurn = _totalAsset.toShares(_amount, true);
    }

    /// @notice The ```withdraw``` function allows the caller to withdraw their Asset Tokens for a given amount of fTokens
    /// @param _amount The amount to withdraw
    /// @param _receiver The address to which the Asset Tokens will be transferred
    /// @param _owner The owner of the Asset Shares (fTokens)
    /// @return _sharesToBurn The number of shares (fTokens) that were burned
    function withdraw(
        uint256 _amount,
        address _receiver,
        address _owner
    ) external nonReentrant returns (uint256 _sharesToBurn) {
        if (_receiver == address(0)) revert InvalidReceiver();

        // Check if withdraw is paused and revert if necessary
        if (isWithdrawPaused) revert WithdrawPaused();

        // Accrue interest if necessary
        _addInterest();

        // Pull from storage to save gas
        VaultAccount memory _totalAsset = totalAsset;

        // Calculate the number of shares to burn based on the amount to withdraw
        _sharesToBurn = _totalAsset.toShares(_amount, true);

        // Execute the withdraw effects
        _redeem(_totalAsset, _amount.toUint128(), _sharesToBurn.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) {
        // Get borrow accounting from storage to save gas
        VaultAccount memory _totalBorrow = totalBorrow;

        // Check available capital (not strictly necessary because balance will underflow, but better revert message)
        uint256 _assetsAvailable = _totalAssetAvailable(totalAsset, _totalBorrow);
        if (_assetsAvailable < _borrowAmount) {
            revert InsufficientAssetsInContract(_assetsAvailable, _borrowAmount);
        }

        // Calculate the number of shares to add based on the amount to borrow
        _sharesAdded = _totalBorrow.toShares(_borrowAmount, true);

        // Effects: Bookkeeping to add shares & amounts to total Borrow accounting
        _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 nonReentrant isSolvent(msg.sender) returns (uint256 _shares) {
        if (_receiver == address(0)) revert InvalidReceiver();

        // Accrue interest if necessary
        _addInterest();

        // Check if borrow will violate the borrow limit and revert if necessary
        if (borrowLimit < totalBorrow.amount + _borrowAmount) revert ExceedsBorrowLimit();

        // Update _exchangeRate and check if borrow is allowed based on deviation
        (bool _isBorrowAllowed, , ) = _updateExchangeRate();
        if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation();

        // Only add collateral if necessary
        if (_collateralAmount > 0) {
            _addCollateral(msg.sender, _collateralAmount, msg.sender);
        }

        // Effects: Call internal borrow function
        _shares = _borrowAsset(_borrowAmount.toUint128(), _receiver);
    }

    /// @notice The ```AddCollateral``` event is emitted when a borrower adds collateral to their position
    /// @param sender The source of funds for the new collateral
    /// @param borrower The borrower account for which the collateral should be credited
    /// @param collateralAmount The amount of Collateral Token to be transferred
    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 {
        if (_borrower == address(0)) revert InvalidReceiver();

        _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
        // NOTE: Following line will revert on underflow if _collateralAmount > userCollateralBalance
        userCollateralBalance[_borrower] -= _collateralAmount;
        // NOTE: 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) {
        if (_receiver == address(0)) revert InvalidReceiver();

        _addInterest();
        // Note: exchange rate is irrelevant when borrower has no debt shares
        if (userBorrowShares[msg.sender] > 0) {
            (bool _isBorrowAllowed, , ) = _updateExchangeRate();
            if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation();
        }
        _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) {
        if (_borrower == address(0)) revert InvalidReceiver();

        // Check if repay is paused revert if necessary
        if (isRepayPaused) revert RepayPaused();

        // Accrue interest if necessary
        _addInterest();

        // Calculate amount to repay based on shares
        VaultAccount memory _totalBorrow = totalBorrow;
        _amountToRepay = _totalBorrow.toAmount(_shares, true);

        // Execute repayment effects
        _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 _feesAmount,
        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 nonReentrant returns (uint256 _collateralForLiquidator) {
        if (_borrower == address(0)) revert InvalidReceiver();

        // Check if liquidate is paused revert if necessary
        if (isLiquidatePaused) revert LiquidatePaused();

        // Ensure deadline has not passed
        if (block.timestamp > _deadline) revert PastDeadline(block.timestamp, _deadline);

        // accrue interest if necessary
        _addInterest();

        // Update exchange rate and use the lower rate for liquidations
        (, uint256 _exchangeRate, ) = _updateExchangeRate();

        // Check if borrower is solvent, revert if they are
        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;
        uint256 _feesAmount;
        {
            // Checks & Calculations
            // Determine the liquidation amount in collateral units (i.e. how much debt liquidator is 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;

            if (protocolLiquidationFee > 0) {
                _feesAmount = (protocolLiquidationFee * _collateralForLiquidator) / LIQ_PRECISION;
                _collateralForLiquidator = _collateralForLiquidator - _feesAmount;
            }
        }

        // 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 = 0;
        {
            uint128 _amountToAdjust = 0;
            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,
                _feesAmount,
                _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);
        // Adjust bookkeeping only (decreases collateral held by borrower)
        _removeCollateral(_feesAmount, address(this), _borrower);
        // Adjusts bookkeeping only (increases collateral held by protocol)
        _addCollateral(address(this), _feesAmount, address(this));
    }

    // ============================================================================================
    // 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 nonReentrant isSolvent(msg.sender) returns (uint256 _totalCollateralBalance) {
        // Accrue interest if necessary
        _addInterest();

        // Update exchange rate and check if borrow is allowed, revert if not
        {
            (bool _isBorrowAllowed, , ) = _updateExchangeRate();
            if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation();
        }

        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) {
        // Accrue interest if necessary
        _addInterest();

        // Update exchange rate and check if borrow is allowed, revert if not
        (bool _isBorrowAllowed, , ) = _updateExchangeRate();
        if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation();

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

interface IDualOracle {
    function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh);

    function decimals() external view returns (uint8);

    function oracleType() external view returns (uint256);

    function name() external view returns (string memory);
}

File 19 of 23 : IRateCalculatorV2.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.18;

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

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

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

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 22 of 23 : VaultAccount.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.18;

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

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

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

// Reviewers
// Dennis: https://github.com/denett

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

/// @title FraxlendPairCore
/// @author Drake Evans (Frax Finance) https://github.com/drakeevans
/// @dev Inspired by the OpenZeppelin's Ownable2Step contract
/// @notice  An abstract contract which contains 2-step transfer and renounce logic for a timelock address
abstract contract Timelock2Step {
    /// @notice The pending timelock address
    address public pendingTimelockAddress;

    /// @notice The current timelock address
    address public timelockAddress;

    constructor() {
        timelockAddress = msg.sender;
    }

    /// @notice Emitted when timelock is transferred
    error OnlyTimelock();

    /// @notice Emitted when pending timelock is transferred
    error OnlyPendingTimelock();

    /// @notice The ```TimelockTransferStarted``` event is emitted when the timelock transfer is initiated
    /// @param previousTimelock The address of the previous timelock
    /// @param newTimelock The address of the new timelock
    event TimelockTransferStarted(address indexed previousTimelock, address indexed newTimelock);

    /// @notice The ```TimelockTransferred``` event is emitted when the timelock transfer is completed
    /// @param previousTimelock The address of the previous timelock
    /// @param newTimelock The address of the new timelock
    event TimelockTransferred(address indexed previousTimelock, address indexed newTimelock);

    /// @notice The ```_isSenderTimelock``` function checks if msg.sender is current timelock address
    /// @return Whether or not msg.sender is current timelock address
    function _isSenderTimelock() internal view returns (bool) {
        return msg.sender == timelockAddress;
    }

    /// @notice The ```_requireTimelock``` function reverts if msg.sender is not current timelock address
    function _requireTimelock() internal view {
        if (msg.sender != timelockAddress) revert OnlyTimelock();
    }

    /// @notice The ```_isSenderPendingTimelock``` function checks if msg.sender is pending timelock address
    /// @return Whether or not msg.sender is pending timelock address
    function _isSenderPendingTimelock() internal view returns (bool) {
        return msg.sender == pendingTimelockAddress;
    }

    /// @notice The ```_requirePendingTimelock``` function reverts if msg.sender is not pending timelock address
    function _requirePendingTimelock() internal view {
        if (msg.sender != pendingTimelockAddress) revert OnlyPendingTimelock();
    }

    /// @notice The ```_transferTimelock``` function initiates the timelock transfer
    /// @dev This function is to be implemented by a public function
    /// @param _newTimelock The address of the nominated (pending) timelock
    function _transferTimelock(address _newTimelock) internal {
        pendingTimelockAddress = _newTimelock;
        emit TimelockTransferStarted(timelockAddress, _newTimelock);
    }

    /// @notice The ```_acceptTransferTimelock``` function completes the timelock transfer
    /// @dev This function is to be implemented by a public function
    function _acceptTransferTimelock() internal {
        pendingTimelockAddress = address(0);
        _setTimelock(msg.sender);
    }

    /// @notice The ```_setTimelock``` function sets the timelock address
    /// @dev This function is to be implemented by a public function
    /// @param _newTimelock The address of the new timelock
    function _setTimelock(address _newTimelock) internal {
        emit TimelockTransferred(timelockAddress, _newTimelock);
        timelockAddress = _newTimelock;
    }

    /// @notice The ```transferTimelock``` function initiates the timelock transfer
    /// @dev Must be called by the current timelock
    /// @param _newTimelock The address of the nominated (pending) timelock
    function transferTimelock(address _newTimelock) external virtual {
        _requireTimelock();
        _transferTimelock(_newTimelock);
    }

    /// @notice The ```acceptTransferTimelock``` function completes the timelock transfer
    /// @dev Must be called by the pending timelock
    function acceptTransferTimelock() external virtual {
        _requirePendingTimelock();
        _acceptTransferTimelock();
    }

    /// @notice The ```renounceTimelock``` function renounces the timelock after setting pending timelock to current timelock
    /// @dev Pending timelock must be set to current timelock before renouncing, creating a 2-step renounce process
    function renounceTimelock() external virtual {
        _requireTimelock();
        _requirePendingTimelock();
        _transferTimelock(address(0));
        _setTimelock(address(0));
    }
}

Settings
{
  "metadata": {
    "bytecodeHash": "none"
  },
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 1660
  },
  "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":"AccessControlRevoked","type":"error"},{"inputs":[],"name":"BadProtocolFee","type":"error"},{"inputs":[],"name":"BadSwapper","type":"error"},{"inputs":[],"name":"BorrowerSolvent","type":"error"},{"inputs":[],"name":"ExceedsBorrowLimit","type":"error"},{"inputs":[],"name":"ExceedsDepositLimit","type":"error"},{"inputs":[],"name":"ExceedsMaxOracleDeviation","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":[],"name":"InterestPaused","type":"error"},{"inputs":[{"internalType":"address","name":"_expected","type":"address"},{"internalType":"address","name":"_actual","type":"address"}],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidReceiver","type":"error"},{"inputs":[],"name":"LiquidatePaused","type":"error"},{"inputs":[],"name":"OnlyPendingTimelock","type":"error"},{"inputs":[],"name":"OnlyProtocolOrOwner","type":"error"},{"inputs":[],"name":"OnlyTimelock","type":"error"},{"inputs":[],"name":"OnlyTimelockOrOwner","type":"error"},{"inputs":[{"internalType":"uint256","name":"_blockTimestamp","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"PastDeadline","type":"error"},{"inputs":[],"name":"RepayPaused","type":"error"},{"inputs":[],"name":"SetterRevoked","type":"error"},{"inputs":[{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_actual","type":"uint256"}],"name":"SlippageTooHigh","type":"error"},{"inputs":[],"name":"WithdrawPaused","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":"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":"_feesAmount","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":"OwnershipTransferStarted","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":"bool","name":"isPaused","type":"bool"}],"name":"PauseInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"PauseLiquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"PauseRepay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"PauseWithdraw","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":false,"internalType":"uint256","name":"borrowLimit","type":"uint256"}],"name":"RevokeBorrowAccessControl","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"depositLimit","type":"uint256"}],"name":"RevokeDepositAccessControl","type":"event"},{"anonymous":false,"inputs":[],"name":"RevokeInterestAccessControl","type":"event"},{"anonymous":false,"inputs":[],"name":"RevokeLiquidateAccessControl","type":"event"},{"anonymous":false,"inputs":[],"name":"RevokeLiquidationFeeSetter","type":"event"},{"anonymous":false,"inputs":[],"name":"RevokeMaxLTVSetter","type":"event"},{"anonymous":false,"inputs":[],"name":"RevokeOracleInfoSetter","type":"event"},{"anonymous":false,"inputs":[],"name":"RevokeRateContractSetter","type":"event"},{"anonymous":false,"inputs":[],"name":"RevokeRepayAccessControl","type":"event"},{"anonymous":false,"inputs":[],"name":"RevokeWithdrawAccessControl","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"limit","type":"uint256"}],"name":"SetBorrowLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldCircuitBreaker","type":"address"},{"indexed":false,"internalType":"address","name":"newCircuitBreaker","type":"address"}],"name":"SetCircuitBreaker","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"limit","type":"uint256"}],"name":"SetDepositLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldCleanLiquidationFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldDirtyLiquidationFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldProtocolLiquidationFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCleanLiquidationFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDirtyLiquidationFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newProtocolLiquidationFee","type":"uint256"}],"name":"SetLiquidationFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMaxLTV","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMaxLTV","type":"uint256"}],"name":"SetMaxLTV","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOracle","type":"address"},{"indexed":false,"internalType":"uint32","name":"oldMaxOracleDeviation","type":"uint32"},{"indexed":false,"internalType":"address","name":"newOracle","type":"address"},{"indexed":false,"internalType":"uint32","name":"newMaxOracleDeviation","type":"uint32"}],"name":"SetOracleInfo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldRateContract","type":"address"},{"indexed":false,"internalType":"address","name":"newRateContract","type":"address"}],"name":"SetRateContract","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":true,"internalType":"address","name":"previousTimelock","type":"address"},{"indexed":true,"internalType":"address","name":"newTimelock","type":"address"}],"name":"TimelockTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousTimelock","type":"address"},{"indexed":true,"internalType":"address","name":"newTimelock","type":"address"}],"name":"TimelockTransferred","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":"uint256","name":"lowExchangeRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"highExchangeRate","type":"uint256"}],"name":"UpdateExchangeRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldRatePerSec","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldFullUtilizationRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newRatePerSec","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFullUtilizationRate","type":"uint256"}],"name":"UpdateRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oracle","type":"address"}],"name":"WarnOracleData","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"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"WithdrawFees","type":"event"},{"inputs":[],"name":"DEPLOYER_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEVIATION_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXCHANGE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQ_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LTV_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PROTOCOL_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RATE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UTIL_PREC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptTransferTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"address","name":"_borrower","type":"address"}],"name":"addCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_returnAccounting","type":"bool"}],"name":"addInterest","outputs":[{"internalType":"uint256","name":"_interestEarned","type":"uint256"},{"internalType":"uint256","name":"_feesAmount","type":"uint256"},{"internalType":"uint256","name":"_feesShare","type":"uint256"},{"components":[{"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"}],"internalType":"struct FraxlendPairCore.CurrentRateInfo","name":"_currentRateInfo","type":"tuple"},{"components":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"internalType":"struct VaultAccount","name":"_totalAsset","type":"tuple"},{"components":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"internalType":"struct VaultAccount","name":"_totalBorrow","type":"tuple"}],"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":[],"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":"borrowLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_newFee","type":"uint32"}],"name":"changeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"circuitBreakerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"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":"depositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dirtyLiquidationFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exchangeRateInfo","outputs":[{"internalType":"address","name":"oracle","type":"address"},{"internalType":"uint32","name":"maxOracleDeviation","type":"uint32"},{"internalType":"uint184","name":"lastTimestamp","type":"uint184"},{"internalType":"uint256","name":"lowExchangeRate","type":"uint256"},{"internalType":"uint256","name":"highExchangeRate","type":"uint256"}],"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":"uint256","name":"_DEVIATION_PRECISION","type":"uint256"},{"internalType":"uint256","name":"_RATE_PRECISION","type":"uint256"},{"internalType":"uint256","name":"_MAX_PROTOCOL_FEE","type":"uint256"}],"stateMutability":"pure","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":"isBorrowAccessControlRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isDepositAccessControlRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isInterestAccessControlRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isInterestPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isLiquidateAccessControlRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isLiquidatePaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isLiquidationFeeSetterRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMaxLTVSetterRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOracleSetterRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isRateContractSetterRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isRepayAccessControlRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isRepayPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isWithdrawAccessControlRevoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isWithdrawPaused","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":[{"internalType":"address","name":"_receiver","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"_maxAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLTV","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_receiver","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"_maxShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"_maxShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"_maxAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"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":"pauseBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isPaused","type":"bool"}],"name":"pauseInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isPaused","type":"bool"}],"name":"pauseLiquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isPaused","type":"bool"}],"name":"pauseRepay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isPaused","type":"bool"}],"name":"pauseWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingTimelockAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"previewAddInterest","outputs":[{"internalType":"uint256","name":"_interestEarned","type":"uint256"},{"internalType":"uint256","name":"_feesAmount","type":"uint256"},{"internalType":"uint256","name":"_feesShare","type":"uint256"},{"components":[{"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"}],"internalType":"struct FraxlendPairCore.CurrentRateInfo","name":"_newCurrentRateInfo","type":"tuple"},{"components":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"internalType":"struct VaultAccount","name":"_totalAsset","type":"tuple"},{"components":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"internalType":"struct VaultAccount","name":"_totalBorrow","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"_sharesReceived","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"_sharesToBurn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pricePerShare","outputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolLiquidationFee","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":[],"name":"renounceTimelock","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":"uint256","name":"_borrowLimit","type":"uint256"}],"name":"revokeBorrowLimitAccessControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositLimit","type":"uint256"}],"name":"revokeDepositLimitAccessControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeInterestAccessControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeLiquidateAccessControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeLiquidationFeeSetter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeMaxLTVSetter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeOracleInfoSetter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeRateContractSetter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeRepayAccessControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeWithdrawAccessControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"name":"setBorrowLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newCircuitBreaker","type":"address"}],"name":"setCircuitBreaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"name":"setDepositLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newCleanLiquidationFee","type":"uint256"},{"internalType":"uint256","name":"_newDirtyLiquidationFee","type":"uint256"},{"internalType":"uint256","name":"_newProtocolLiquidationFee","type":"uint256"}],"name":"setLiquidationFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMaxLTV","type":"uint256"}],"name":"setMaxLTV","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOracle","type":"address"},{"internalType":"uint32","name":"_newMaxOracleDeviation","type":"uint32"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newRateContract","type":"address"}],"name":"setRateContract","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":"","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":[],"name":"timelockAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"},{"internalType":"bool","name":"_previewInterest","type":"bool"}],"name":"toAssetAmount","outputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"},{"internalType":"bool","name":"_previewInterest","type":"bool"}],"name":"toAssetShares","outputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"},{"internalType":"bool","name":"_previewInterest","type":"bool"}],"name":"toBorrowAmount","outputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"},{"internalType":"bool","name":"_previewInterest","type":"bool"}],"name":"toBorrowShares","outputs":[{"internalType":"uint256","name":"_shares","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":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[{"internalType":"address","name":"_newTimelock","type":"address"}],"name":"transferTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateExchangeRate","outputs":[{"internalType":"bool","name":"_isBorrowAllowed","type":"bool"},{"internalType":"uint256","name":"_lowExchangeRate","type":"uint256"},{"internalType":"uint256","name":"_highExchangeRate","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":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"_sharesToBurn","type":"uint256"}],"stateMutability":"nonpayable","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"}]

Deployed Bytecode

0x6080604052600436101561001257600080fd5b60003560e01c8062348d1814613d4e57806301e1d11414613d1c57806302ce728f14613cdf5780630475260e14613c8a57806306fdde0314613bcc57806307a2d13a14613b9f5780630880b2f014613b4c57806308a0c37514613aec578063090f3f5014613ac5578063095ea7b314613a9f5780630a28a47714613a725780630a67918c14613a4f5780630b4aecf014613a2c5780630c70661d14613a06578063115a334c146139e057806311a2e4bc146139c257806318160ddd146139a15780631bc23cf9146139175780631c2591d3146138df5780631c6c95971461384357806323b872dd146137855780632b3ba6811461154c578063313ce5671461374757806334680fe51461372157806338d52e0f146136dd578063390308641461368e578063395093511461363c5780633d417d2d1461359f5780633f2617cb1461351a5780633f4ba83a1461334b578063402d267d146132f9578063450140951461328f57806345757e4a146132695780634732428c1461324b578063492924271461322457806349eb7af61461091d5780634ac8eb5f146132065780634b4b418e146131805780634bc66f32146131595780634c18a4fb146130cb5780634c4179951461307a5780634cdad506146117c65780634f8b4ae714612fe95780634fd422df14612fb057806354fd4d5014612f8657806364e51d5d14612f1357806367800b5f14612eed57806369026e8814612e985780636b96668f14612e0f5780636cd3cc771461091d5780636e553f6514612d8757806370a0823114612d4d578063715018a614612ce9578063721b0a471461297f57806379ba5097146128ab5780637d37bdd7146128735780637d63fbc21461284d5780637ec4b571146128105780637ecefa6e1461279f5780638142dd53146126985780638285ef401461266657806382beee89146125ef5780638456cb5914612403578063858f1e6814612365578063886c033a1461230c5780638cad7fbe146122cd5780638da5cb5b146122a65780638f791f8b1461212157806393f46f64146120e457806394bf804d1461203157806395d14ca814611fdb57806395d89b4114611eda57806399530b0614611e605780639a295e7314611e0c5780639bc6ab8614611de65780639fe34bdc1461091d578063a053db68146118bb578063a457c2d7146117fc578063a9059cbb146117cb578063b3d7f6b9146117c6578063b460af941461175a578063b5af306214611720578063b68d0a09146116ba578063b78294dd1461091d578063b7db54f514611694578063b8ca3b8314611677578063ba087652146115cd578063bbb09624146115a8578063bdc8144b14611551578063c0a7e8921461154c578063c58e4df614611526578063c63d75b6146114ca578063c6e1c7c914611486578063c6e6f59214610725578063ca2298fe14610f33578063cacf3b5814610f04578063cadac47914610eb3578063cdd72d5214610e52578063ce96cb7714610e2f578063d2a156e014610deb578063d41ddc9614610d41578063d905777e14610d1e578063daf33f2a14610c20578063dd62ed3e14610bce578063e1e9277514610b81578063e203641714610b5e578063e30c397814610b37578063e4b0007014610ae6578063e551d11d14610ac8578063e5f13b1614610922578063e63a391f1461091d578063e7a33174146108c6578063e8596f7214610871578063eafecffa14610853578063ebd462cb1461079f578063ecf7085814610781578063eee242191461075a578063ef8b30f714610725578063f211c390146106ff578063f2fde38b14610695578063f384bd0514610677578063f6ccaad414610611578063f9557ccb146105db5763fbbbf94c1461056f57600080fd5b346105d65760003660031901126105d65760a060185476ffffffffffffffffffffffffffffffffffffffffffffff60195416601a54601b549163ffffffff604051946001600160a01b0381168652861c166020850152604084015260608301526080820152f35b600080fd5b346105d65760003660031901126105d657601c54604080516001600160801b038316815260809290921c602083015290f35b0390f35b346105d65760003660031901126105d65761062a615bc1565b6001600160a01b0319806000541660005560015490336001600160a01b0383167f31b6c5a04b069b6ec1b3cef44c4e7c1eadd721349cda9823d0b1877b3551cdc6600080a3163317600155005b346105d65760003660031901126105d6576020600f54604051908152f35b346105d65760203660031901126105d6576106ae613df0565b6106b661403d565b6001600160a01b0380911690816001600160a01b03196003541617600355600254167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700600080a3005b346105d65760003660031901126105d657602060ff60085460381c166040519015158152f35b346105d65760203660031901126105d65760206107526107436147b7565b50935050505060043590615e45565b604051908152f35b346105d65760003660031901126105d65760206001600160a01b0360135416604051908152f35b346105d65760003660031901126105d6576020600754604051908152f35b346105d65760203660031901126105d6576107b8613e46565b8015610846576107c66145db565b6008549060ff8260201c1661081c577fc56dd3e14f5af3a74c61b7cdf855a3d8ab4401c78c0622a4d312de8a8f8736a29160209115159063ff0000008260181b169063ff000000191617600855604051908152a1005b60046040517f1ada47b8000000000000000000000000000000000000000000000000000000008152fd5b61084e61467b565b6107c6565b346105d65760003660031901126105d6576020601254604051908152f35b346105d65760003660031901126105d65761088a6145db565b60ff6006541661081c5760006005557fbf1ce7fb3a8e648b70ea830f99b52f7ea31554186d29763280751f42e77f6386602060405160008152a1005b346105d65760203660031901126105d6576004356108e261467b565b60ff6006541661081c576020817fbf1ce7fb3a8e648b70ea830f99b52f7ea31554186d29763280751f42e77f638692600555604051908152a1005b613f4c565b346105d65760603660031901126105d657600435602435610941613e1c565b9061094a614716565b6001600160a01b03821615610a9e57610961614c0a565b505050505060055461097e846001600160801b03601d5416614095565b11610a745761098b614ed1565b505015610a63576109ab92816109a692610a51575b5061455d565b61564f565b6109b3614500565b50601b54906109c282336155ab565b156109d7576020906001600e55604051908152f35b506109f76109e36143b2565b336000526020805260406000205490615f10565b33600052601f602052610a4d604060002054926040519384937fed27783c000000000000000000000000000000000000000000000000000000008552600485016040919493926060820195825260208201520152565b0390fd5b610a5d90339033615798565b846109a0565b600460405163345513d960e01b8152fd5b60046040517f97ba4de3000000000000000000000000000000000000000000000000000000008152fd5b60046040517f1e4ec46b000000000000000000000000000000000000000000000000000000008152fd5b346105d65760003660031901126105d6576020600554604051908152f35b346105d65760003660031901126105d657610aff615b83565b6201000062ff00001960215416176021557f70328969870b42d0fc62ce5946530c50a466c5ad21af004a9f2f3a8eda9a4a0c600080a1005b346105d65760003660031901126105d65760206001600160a01b0360035416604051908152f35b346105d65760003660031901126105d657602060ff600854166040519015158152f35b346105d65760003660031901126105d657610b9a615b83565b600160ff1960215416176021557faa96740f913149dce2173396218295b4c082e86fabacc37ac8d45305239d26f3600080a1005b346105d65760403660031901126105d657610be7613df0565b610bef613e06565b906001600160a01b03809116600052600a602052604060002091166000526020526020604060002054604051908152f35b346105d65760403660031901126105d657610c39613f6a565b610c41613e06565b90610c4a61403d565b806001600160a01b038316928315610a9e57602093610c6761438c565b926001600160801b0380911615610d06575b917faf48306b6b4f0ba30d00f05b21559d8d29934142980a553d8a014780c6c7e4529391610cce60809487169383610cb18683615f10565b98610cbd873330614258565b3092610cc88b61455d565b90615299565b30600052601f8752610ce860406000205492309084615861565b604051928352868301528460408301526060820152a1604051908152f35b30600090815260098752604090205481169450610c79565b346105d65760203660031901126105d6576020610752610d3c613df0565b614481565b346105d65760403660031901126105d657610d5a613e06565b610d62614716565b6001600160a01b03811615610a9e57610d79614c0a565b50505050503360005260208052604060002054610dcc575b610d9f903390600435615861565b610da7614500565b50601b54610db581336155ab565b15610dc1576001600e55005b6109f76109e36143b2565b610dd4614ed1565b5050610d9157600460405163345513d960e01b8152fd5b346105d65760003660031901126105d65760206040516001600160a01b037f0000000000000000000000007ab788d0483551428f2291232477f1818952998c168152f35b346105d65760203660031901126105d6576020610752610e4d613df0565b6143e5565b346105d65760003660031901126105d65760a0610e6d6147b7565b9350935050506001600160801b03908160208185511694015116916020818351169201511690601e54926040519485526020850152604084015260608301526080820152f35b346105d65760403660031901126105d657610ecc613e06565b610ed4614716565b6001600160a01b03811615610a9e57610efd90610eef614c0a565b505050505060043533615798565b6001600e55005b346105d65760003660031901126105d65761060d610f206147b7565b9260409694969291925196879687613e81565b346105d65760803660031901126105d657610f4c613df0565b60643567ffffffffffffffff8082116105d657366023830112156105d6578160040135116105d657366024826004013560051b830101116105d657610f8f614716565b610f97614c0a565b5050505050610fa4614ed1565b505015610a63576001600160a01b038216600052601460205260ff604060002054161561145c57806004013515611446576001600160a01b03610fe960248301615b6f565b817f0000000000000000000000004d224452801aced8b2f0aebe155379bb5d59438116918291160361141157600482013560001981019081116113fb5761104061103b82856004013560248701615b5f565b615b6f565b6001600160a01b03807f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e1691160361139257506110803330602435615861565b60405163095ea7b360e01b81526001600160a01b0384166004820152602480359082015290602090829060449082906000905af180156112ca57611359575b50604051906370a0823160e01b82523060048301526020826024816001600160a01b037f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e165afa9182156112ca57600092611325575b5060405180916338ed173960e01b82526024356004830152604435602483015260a06044830152806004013560a483015260c4820190602481019060005b816004013581106112f95750505090806000923060648301524260848301520381836001600160a01b0388165af180156112ca576112d6575b50604051906370a0823160e01b82523060048301526020826024816001600160a01b037f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e165afa80156112ca57600090611296575b6111eb92506143d8565b906044358210611277576111fd6143b2565b61122761120a8483615e45565b916112148561455d565b339130916112218661455d565b91615910565b6001600160a01b0360405192168252602435602083015282604083015260608201527fe947f0f9b6255bdcf76d13d1257d34fbe380e0d5d4daa75e61c783a41e1607ba60803392a26109b3614500565b60448260405190633b5d56ed60e11b8252823560048301526024820152fd5b506020823d6020116112c2575b816112b060209383613fce565b810103126105d6576111eb91516111e1565b3d91506112a3565b6040513d6000823e3d90fd5b6112f2903d806000833e6112ea8183613fce565b810190615ae5565b508261118c565b919350916020806001926001600160a01b0361131488613e32565b168152019401910191849392611153565b9091506020813d602011611351575b8161134160209383613fce565b810103126105d657519083611115565b3d9150611334565b6020813d60201161138a575b8161137260209383613fce565b810103126105d65761138390614ec4565b50826110bf565b3d9150611365565b6113ab61103b610a4d9285602481600401359101615b5f565b60405163b0b3262d60e01b81527f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e6001600160a01b03908116600483015290911660248201529081906044820190565b634e487b7160e01b600052601160045260246000fd5b61141d60248301615b6f565b60405163b0b3262d60e01b81526001600160a01b03928316600482015291166024820152604490fd5b634e487b7160e01b600052603260045260246000fd5b60046040517f1311dc6d000000000000000000000000000000000000000000000000000000008152fd5b346105d65760003660031901126105d65760206040516001600160a01b037f0000000000000000000000004d224452801aced8b2f0aebe155379bb5d594381168152f35b346105d65760203660031901126105d6576114e3613df0565b5060206107526114f16147b7565b5093505050506001600160801b0381511660075490818110600014611517575090615e45565b611520916143d8565b90615e45565b346105d65760003660031901126105d657602060ff60085460281c166040519015158152f35b613f29565b346105d65760203660031901126105d65760043561156d61467b565b60ff6008541661081c576020817f854df3eb95564502c8bc871ebdd15310ee26270f955f6c6bd8cea68e75045bc092600755604051908152a1005b346105d65760003660031901126105d657602060085460ff60405191831c1615158152f35b346105d6576115db36614008565b91906115e5614716565b6001600160a01b03811615610a9e5760ff60085460181c1661164d5760209261164091611610614c0a565b505050505061161d61438c565b6116278582615edf565b9461163a6116348761455d565b9161455d565b91615299565b6001600e55604051908152f35b60046040517fe0a39803000000000000000000000000000000000000000000000000000000008152fd5b346105d65760003660031901126105d657602060405161c3508152f35b346105d65760003660031901126105d657602060ff60215460081c166040519015158152f35b346105d65760203660031901126105d6576001600160a01b036116db613df0565b1660005260096020526040600020546020805260406000205490601f60205261060d604060002054604051938493846040919493926060820195825260208201520152565b346105d65760203660031901126105d6576001600160a01b03611741613df0565b16600052601f6020526020604060002054604051908152f35b346105d65761176836614008565b9190611772614716565b6001600160a01b03811615610a9e5760ff60085460181c1661164d576020926116409161179d614c0a565b50505050506117aa61438c565b6117bd6117b78683615ddb565b9561455d565b61163a8661455d565b613b9f565b346105d65760403660031901126105d6576117f16117e7613df0565b60243590336140a2565b602060405160018152f35b346105d65760403660031901126105d657611815613df0565b6024359033600052600a60205260406000206001600160a01b03821660005260205260406000205491808310611851576117f192039033614258565b608460405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152fd5b346105d65760a03660031901126105d6576118d4613df0565b6084359067ffffffffffffffff82116105d657366023830112156105d657816004013561190081613ff0565b9261190e6040519485613fce565b818452602084016024819360051b830101913683116105d657602401905b828210611dce5750505061193e614716565b611946614c0a565b5050505050611953614ed1565b505015610a63576001600160a01b038216600052601460205260ff604060002054161561145c576001600160a01b0361198b84615ac4565b5116926001600160a01b037f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e16809403611d8f578051600019908181019081116113fb576119e16001600160a01b039184615ad1565b51166001600160a01b037f0000000000000000000000004d224452801aced8b2f0aebe155379bb5d5943811603611d1a5750604435611d08575b611a30611a2960243561455d565b309061564f565b60405163095ea7b360e01b81526001600160a01b038516600482015260248035908201529094602090829060449082906000905af180156112ca57611ccf575b50604051916370a0823160e01b83523060048401526020836024816001600160a01b037f0000000000000000000000004d224452801aced8b2f0aebe155379bb5d594381165afa9283156112ca57600093611c9b575b506040516338ed173960e01b81526024803560048301526064359082015260a06044820152915160a48301819052829160c483019160005b818110611c795750505090806000923060648301524260848301520381836001600160a01b0388165af180156112ca57611c5e575b50604051906370a0823160e01b82523060048301526020826024816001600160a01b037f0000000000000000000000004d224452801aced8b2f0aebe155379bb5d594381165afa80156112ca57600090611c2a575b611b9292506143d8565b6064358110611c0a57611ba6338230615798565b611bb281604435614095565b926001600160a01b036040519316835260243560208401526040830152604435606083015260808201527fb19ca0df3f3a01af950d8e6ad62aeff167cf14c73e98af6c52afef1add5c97ed60a03392a26109b3614500565b60449060405190633b5d56ed60e11b825260643560048301526024820152fd5b506020823d602011611c56575b81611c4460209383613fce565b810103126105d657611b929151611b88565b3d9150611c37565b611c72903d806000833e6112ea8183613fce565b5083611b33565b82516001600160a01b0316845285945060209384019390920191600101611afe565b9092506020813d602011611cc7575b81611cb760209383613fce565b810103126105d657519185611ac6565b3d9150611caa565b6020813d602011611d00575b81611ce860209383613fce565b810103126105d657611cf990614ec4565b5084611a70565b3d9150611cdb565b611d153360443533615798565b611a1b565b81519081019081116113fb57611d3b6001600160a01b0391610a4d93615ad1565b5160405163b0b3262d60e01b81526001600160a01b037f0000000000000000000000004d224452801aced8b2f0aebe155379bb5d594381811660048301529290911690911660248201529081906044820190565b6001600160a01b03611da18592615ac4565b5160405163b0b3262d60e01b81526001600160a01b03938416600482015291169091166024820152604490fd5b60208091611ddb84613e32565b81520191019061192c565b346105d65760003660031901126105d657602060ff60215460181c166040519015158152f35b346105d65760003660031901126105d657610100604051620186a0808252806020830152806040830152806060830152670de0b6b3a76400009081608084015260a083015260c082015261c35060e0820152f35b346105d65760003660031901126105d657611e796147b7565b509350505050602081016001600160801b03918282511615600014611eae575050506020670de0b6b3a7640000604051908152f35b51670de0b6b3a7640000908316818102918204036113fb57602092611ed59251169061487a565b610752565b346105d65760003660031901126105d657604051600090601654600181811c90808316928315611fd1575b6020938484108114611fbb57838652908115611f9b5750600114611f40575b61060d84611f3481880382613fce565b60405191829182613da7565b601660009081529294507fd833147d7dc355ba459fc788f669e58cfaf9dc25ddcd0702e87d69c7b51242895b828410611f88575050508161060d93611f349282010193611f24565b8054858501870152928501928101611f6c565b60ff1916858501525050151560051b8201019150611f348161060d611f24565b634e487b7160e01b600052602260045260246000fd5b91607f1691611f05565b346105d65760003660031901126105d65760a06017546040519063ffffffff80821683528160201c16602083015267ffffffffffffffff808260401c1660408401528160801c16606083015260c01c6080820152f35b346105d65760403660031901126105d65760043561204d613e06565b612055614716565b6001600160a01b03811615610a9e5761206c614c0a565b505050505061207961438c565b916120848184615edf565b9160075461209c846001600160801b03875116614095565b116120ba57602093611640926120b46116348661455d565b91615173565b60046040517f2ab4a214000000000000000000000000000000000000000000000000000000008152fd5b346105d65760206120f436613e55565b1561211057610752916121056147b7565b945050505050615e72565b611ed59161211c6143b2565b615e72565b346105d65760403660031901126105d65761213a613df0565b63ffffffff60243581811692918382036105d657612156615b83565b60ff6021541661227c576080937f78ba1c32ac8ea4b3d51133dd0b6f5d8f98e23797aade6afc381ea317d5d4f28b856121db93612191614500565b966001600160a01b0390818951169260208a015116604051938452602084015216938460408301526060820152a16001600160a01b03166001600160a01b03196018541617601855565b7fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff77ffffffff00000000000000000000000000000000000000006018549260a01b1691161760185576ffffffffffffffffffffffffffffffffffffffffffffff6040820151167fffffffffffffffffff000000000000000000000000000000000000000000000060195416176019556060810151601a550151601b55600080f35b60046040517f8c34a9b8000000000000000000000000000000000000000000000000000000008152fd5b346105d65760003660031901126105d65760206001600160a01b0360025416604051908152f35b346105d65760203660031901126105d6576001600160a01b036122ee613df0565b166000526014602052602060ff604060002054166040519015158152f35b346105d65760203660031901126105d6577f3ff713beec3d10b4dfe28953471682eab1f857ba2fdb6367366252381888a750602060043561234b615b83565b600160ff19600854161760085580600755604051908152a1005b346105d65760203660031901126105d65761237e613e46565b80156123f65761238c6145db565b60ff60085460401c1661081c5760207fdea8bb46eee4300a7d2de86939c245f568dc5994576194cbfb69969e010dcb67916123c5614c0a565b5050505050151560085467ff000000000000001967ff000000000000008360381b16911617600855604051908152a1005b6123fe61467b565b61238c565b346105d65760003660031901126105d65761241c6145db565b60ff8060065416156125ba575b600890815481811615612585575b818160101c1615612548575b508154818160201c1615612507575b508154818160301c16156124c2575b50815460401c161561246f57005b612477614c0a565b505050505067010000000000000067ff00000000000000198254161790557fdea8bb46eee4300a7d2de86939c245f568dc5994576194cbfb69969e010dcb67602060405160018152a1005b650100000000009065ff000000000019161782557f28bc4f9e24da61e7ba3aa697dfaefd0167093d2425c00b6190a7d3152ee6dfaa602060405160018152a182612461565b63010000009063ff00000019161782557fc56dd3e14f5af3a74c61b7cdf855a3d8ab4401c78c0622a4d312de8a8f8736a2602060405160018152a182612452565b6101009061ff0019161782557f34a71a12fa81891b738d910d4d44ffabeeb12f8bc026844db237ea8bf8ebe8be602060405160018152a182612443565b60006007557f854df3eb95564502c8bc871ebdd15310ee26270f955f6c6bd8cea68e75045bc0602060405160008152a1612437565b60006005557fbf1ce7fb3a8e648b70ea830f99b52f7ea31554186d29763280751f42e77f6386602060405160008152a1612429565b346105d65760203660031901126105d6577f4cb8c9e37efb94c6cdbd2a80fe36cee1957b5584d1a1986fa2bae115180af59a612629613df0565b612631615b83565b600480546001600160a01b039283166001600160a01b03198216811790925560408051939091168352602083019190915290a1005b346105d65760003660031901126105d657601d54604080516001600160801b038316815260809290921c602083015290f35b346105d65760203660031901126105d65760043563ffffffff8116908181036105d6576126c3615b83565b60ff60085460381c166127755761c350821161274b577f58a58c712558f3d6e20bed57421eb8f73048d881dea9e5bb80efb37c49680d1c91602091612706614c0a565b50505050507fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff67ffffffff0000000060175492851b16911617601755604051908152a1005b60046040517fda0afa57000000000000000000000000000000000000000000000000000000008152fd5b60046040517fa02a2bcd000000000000000000000000000000000000000000000000000000008152fd5b346105d65760003660031901126105d6576127b8615b83565b66010000000000007fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff60085416176008557f60c2acdf5b421891c8cc7302420292f2680f0e835fc76dd15f35a7bb0dd5cbc8600080a1005b346105d657602061282036613e55565b1561283c57610752916128316147b7565b945050505050615f4b565b611ed5916128486143b2565b615f4b565b346105d65760003660031901126105d657602060ff60215460101c166040519015158152f35b346105d657602061288336613e55565b1561289f57610752916128946147b7565b509350505050615f4b565b611ed59161284861438c565b346105d65760003660031901126105d6576003546001600160a01b033381831603612915576001600160a01b03198092166003556002549133908316176002553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b608460405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e657200000000000000000000000000000000000000000000006064820152fd5b346105d65760603660031901126105d657612998613f6a565b602435906129a4613e1c565b6129ac614716565b6001600160a01b038116928315610a9e5760ff60085460281c16612cbf57804211612c8857506129da614c0a565b50505050506129e7614ed1565b5090506129f481836155ab565b612c5e57612a006143b2565b9184600052601f6020526040600020549260208052612a2360406000205461455d565b926000936001600160801b0396670de0b6b3a7640000612a4f89831694612a4a8688615edf565b614867565b049860105497620186a0988901808a116113fb57612a6e8a918d614867565b04612a81612a7b83615a30565b91615a30565b90600082820392128183128116918313901516176113fb5760001280159190612c3e5760209b505b809960125480612bfc575b50505086959293612b5d979486938b6116409c9d612b43958d612ae2612add612b4b9d8c615f10565b61455d565b98600097600093612b65575b509160c093917f821de4e13fff1938b3806eb2859b6a5d55111f00dcf286f8a793584228ff36f895936040519485526020850152828b166040850152606084015281881660808401521660a0820152a26148af565b903392615910565b612b56813387615861565b3083615861565b309030615798565b7f821de4e13fff1938b3806eb2859b6a5d55111f00dcf286f8a793584228ff36f8959391985091612b998860c096946146d2565b98838d818c1680612bb3575b505050919395509193612aee565b82955090612bc7612add612bd19383615edf565b95869151166146d2565b168d52601c546001600160801b031985612bed868285166146d2565b16911617601c55838d38612ba5565b6116409b50829a5093612b4393612c2b8b9a9793612c23612b5d9d9a96612b4b9c99614867565b04809d6143d8565b9c50935093968296508195989950612ab4565b506011548901808a116113fb57612c588a9160209d614867565b04612aa9565b60046040517f75e595fa000000000000000000000000000000000000000000000000000000008152fd5b604490604051907f5ba2a8d50000000000000000000000000000000000000000000000000000000082524260048301526024820152fd5b60046040517f6d2c35dc000000000000000000000000000000000000000000000000000000008152fd5b346105d65760003660031901126105d657612d0261403d565b60006001600160a01b036001600160a01b03198060035416600355600254908116600255167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346105d65760203660031901126105d6576001600160a01b03612d6e613df0565b1660005260096020526020604060002054604051908152f35b346105d65760403660031901126105d657600435612da3613e06565b90612dac614716565b6001600160a01b03821615610a9e57612dc3614c0a565b5050505050612dd061438c565b600754612de7836001600160801b03845116614095565b116120ba5760209281612e06612e008561164095615e45565b9461455d565b6120b48561455d565b346105d65760203660031901126105d657612e28613df0565b612e30615b83565b60ff60215460101c1661227c57601354604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917faeae842c8b3cd009fbb602e1ed072dc1aec69750e431ceae97f7543b466cd04c9190a116911617601355600080f35b346105d65760003660031901126105d657612eb16145db565b60ff6008541661081c5760006007557f854df3eb95564502c8bc871ebdd15310ee26270f955f6c6bd8cea68e75045bc0602060405160008152a1005b346105d65760003660031901126105d657602060ff60085460181c166040519015158152f35b346105d65760003660031901126105d657612f2c615b83565b680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff60085416176008557f16c0a933c76f28f1abdcef88bcea1650397c5f4bb4bf491a0d451a65cae016b6600080a1005b346105d65760003660031901126105d6576060604051600381526000602082015260006040820152f35b346105d65760203660031901126105d6576001600160a01b03612fd1613df0565b16600052602080526020604060002054604051908152f35b346105d65760003660031901126105d657613002615b83565b61300a615bc1565b6001600160a01b0319806000541660005560015460006001600160a01b03821681817f162998b90abc2507f3953aa797827b03a14c42dbd9a35f09feaf02e0d592773a8280a37f31b6c5a04b069b6ec1b3cef44c4e7c1eadd721349cda9823d0b1877b3551cdc68280a316600155005b346105d65760003660031901126105d657613093615b83565b6201000062ff00001960085416176008557f269ac55859865c2ff127a862e95c81ce7e3b9b13582036d3df419df5c07ec8b4600080a1005b346105d65760203660031901126105d6576130e4613e46565b801561314c576130f26145db565b6008549060ff8260301c1661081c577f28bc4f9e24da61e7ba3aa697dfaefd0167093d2425c00b6190a7d3152ee6dfaa9160209115159065ff00000000008260281b169065ff0000000000191617600855604051908152a1005b61315461467b565b6130f2565b346105d65760003660031901126105d65760206001600160a01b0360015416604051908152f35b346105d65760203660031901126105d657613199613e46565b80156131f9576131a76145db565b6008549060ff8260101c1661081c577f34a71a12fa81891b738d910d4d44ffabeeb12f8bc026844db237ea8bf8ebe8be9160209115159061ff008260081b169061ff00191617600855604051908152a1005b61320161467b565b6131a7565b346105d65760003660031901126105d6576020601e54604051908152f35b346105d65760003660031901126105d65760206001600160a01b0360045416604051908152f35b346105d65760003660031901126105d6576020601154604051908152f35b346105d65760003660031901126105d657602060ff60085460401c166040519015158152f35b346105d65760203660031901126105d6576132a8613df0565b6132b0615b83565b6001600160a01b0380911690816001600160a01b03196000541617600055600154167f162998b90abc2507f3953aa797827b03a14c42dbd9a35f09feaf02e0d592773a600080a3005b346105d65760203660031901126105d657613312613df0565b5060206001600160801b036133256147b7565b505160075495169350505050818110156133425750604051908152f35b611ed5916143d8565b346105d65760003660031901126105d65761336461467b565b60ff8060065416156134e4575b6008908154818116156134ae575b818160101c1615613476575b508154818160201c161561343c575b508154818160301c1615613400575b50815460401c16156133b757005b6133bf614c0a565b505050505067ff000000000000001981541690557fdea8bb46eee4300a7d2de86939c245f568dc5994576194cbfb69969e010dcb67602060405160008152a1005b65ff0000000000191682557f28bc4f9e24da61e7ba3aa697dfaefd0167093d2425c00b6190a7d3152ee6dfaa602060405160008152a1826133a9565b63ff000000191682557fc56dd3e14f5af3a74c61b7cdf855a3d8ab4401c78c0622a4d312de8a8f8736a2602060405160008152a18261339a565b61ff00191682557f34a71a12fa81891b738d910d4d44ffabeeb12f8bc026844db237ea8bf8ebe8be602060405160008152a18261338b565b7f854df3eb95564502c8bc871ebdd15310ee26270f955f6c6bd8cea68e75045bc0602060001980600755604051908152a161337f565b7fbf1ce7fb3a8e648b70ea830f99b52f7ea31554186d29763280751f42e77f6386602060001980600555604051908152a1613371565b346105d65760403660031901126105d657613533613df0565b602435908115158092036105d6577fea1eefb4fd58778d7b274fe54045a9feeec8f2847899c2e71126d3a74d486da5916001600160a01b0360409261357661403d565b16908160005260146020528260002060ff1981541660ff831617905582519182526020820152a1005b346105d65760403660031901126105d6576004356135bb613e06565b906135c4614716565b6001600160a01b03821615610a9e5760ff60085460081c16613612576116406020926135ee614c0a565b50505050506135fb6143b2565b6136058482615f10565b93612b436116348661455d565b60046040517f3cc383d2000000000000000000000000000000000000000000000000000000008152fd5b346105d65760403660031901126105d6576117f1613658613df0565b33600052600a60205260406000206001600160a01b038216600052602052613687602435604060002054614095565b9033614258565b346105d65760003660031901126105d6576136a7615b83565b61010061ff001960215416176021557f0af6d9d6ea0e3f0cdb71562ce1fce30aa597445ea04f5b25a939cfe0a252171c600080a1005b346105d65760003660031901126105d65760206040516001600160a01b037f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e168152f35b346105d65760003660031901126105d657602060ff60085460301c166040519015158152f35b346105d65760003660031901126105d657602060405160ff7f0000000000000000000000000000000000000000000000000000000000000012168152f35b346105d65760603660031901126105d65761379e613df0565b6137a6613e06565b604435906001600160a01b038316600052600a6020526040600020336000526020526040600020549260001984036137e3575b6117f193506140a2565b8284106137ff576137fa836117f195033383614258565b6137d9565b606460405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b346105d65760203660031901126105d65761385c613e46565b6138646146eb565b5060405161387181613f80565b600081526000602082015261060d6040519161388c83613f80565b600083526000602084015261389f614716565b916138a8614c0a565b9296935090966138c4575b6001600e5560405196879687613e81565b925090506138d061438c565b906138d96143b2565b926138b3565b346105d65760206138ef36613e55565b1561390b57610752916139006147b7565b509350505050615e72565b611ed59161211c61438c565b346105d65760603660031901126105d657604435602435600435613939615b83565b60ff60215460181c1661227c577fc9aa62b60be8f25ac9f285edbb80bde64199b3c53e1da1027058551d32695fca60c060105460115460125490604051928352602083015260408201528360608201528460808201528560a0820152a1601055601155601255005b346105d65760003660031901126105d6576020601c5460801c604051908152f35b346105d65760003660031901126105d6576020601054604051908152f35b346105d65760003660031901126105d657602060ff60085460081c166040519015158152f35b346105d65760003660031901126105d657602060ff60085460101c166040519015158152f35b346105d65760003660031901126105d657602060ff602154166040519015158152f35b346105d65760003660031901126105d657602060ff600654166040519015158152f35b346105d65760203660031901126105d6576020610752613a906147b7565b50935050505060043590615ddb565b346105d65760403660031901126105d6576117f1613abb613df0565b6024359033614258565b346105d65760003660031901126105d65760206001600160a01b0360005416604051908152f35b346105d65760203660031901126105d657600435613b08615b83565b60ff60215460081c1661227c577fe796e9ae748449310fcd1cc6718aab236c9b8d2e0e04dacb232ba564d5b338cc6040600f548151908152836020820152a1600f55005b346105d65760003660031901126105d657613b65615b83565b630100000063ff0000001960215416176021557f1cd8398e5a04411acbddcb6451a57b51c242322c538947cea5e4a1a506700b87600080a1005b346105d65760203660031901126105d6576020610752613bbd6147b7565b50935050505060043590615edf565b346105d65760003660031901126105d657604051600090601554600181811c90808316928315613c80575b6020938484108114611fbb57838652908115611f9b5750600114613c255761060d84611f3481880382613fce565b601560009081529294507f55f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec4755b828410613c6d575050508161060d93611f349282010193611f24565b8054858501870152928501928101613c51565b91607f1691613bf7565b346105d65760003660031901126105d657613ca3615b83565b64010000000064ff000000001960085416176008557fb949af551d0c88280e648f9205b986bb5f1d899c425498238655ee37617c0c39600080a1005b346105d65760003660031901126105d657613cf8614716565b6060613d02614ed1565b906001600e55604051921515835260208301526040820152f35b346105d65760003660031901126105d65760206001600160801b03613d3f6147b7565b50516040519516855250505050f35b346105d65760203660031901126105d6577fee4b3f9e70b2c6499288c7b5fbef140756009cf8839be64c473b1c7cb6d616c46020600435613d8d615b83565b600160ff19600654161760065580600555604051908152a1005b6020808252825181830181905290939260005b828110613ddc57505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501613dba565b600435906001600160a01b03821682036105d657565b602435906001600160a01b03821682036105d657565b604435906001600160a01b03821682036105d657565b35906001600160a01b03821682036105d657565b6004359081151582036105d657565b60609060031901126105d6576004359060243580151581036105d6579060443580151581036105d65790565b9194613f0a9197969461014094613f279761018086019a86526020860152604085015263ffffffff8082511660608601526020820151166080850152608067ffffffffffffffff918260408201511660a08701528260608201511660c087015201511660e0840152610100830190602090816001600160801b0391828151168552015116910152565b0190602090816001600160801b0391828151168552015116910152565b565b346105d65760003660031901126105d6576020604051670de0b6b3a76400008152f35b346105d65760003660031901126105d6576020604051620186a08152f35b600435906001600160801b03821682036105d657565b6040810190811067ffffffffffffffff821117613f9c57604052565b634e487b7160e01b600052604160045260246000fd5b60a0810190811067ffffffffffffffff821117613f9c57604052565b90601f8019910116810190811067ffffffffffffffff821117613f9c57604052565b67ffffffffffffffff8111613f9c5760051b60200190565b60609060031901126105d657600435906001600160a01b039060243582811681036105d6579160443590811681036105d65790565b6001600160a01b0360025416330361405157565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b919082018092116113fb57565b6001600160a01b038091169182156141ee5716918215614184576000828152600960205260408120549180831061411a57604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef95876020965260098652038282205586815220818154019055604051908152a3565b608460405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152fd5b6001600160a01b0380911691821561432357169182156142b95760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259183600052600a8252604060002085600052825280604060002055604051908152a3565b608460405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152fd5b6040519061439982613f80565b601c546001600160801b038116835260801c6020830152565b604051906143bf82613f80565b601d546001600160801b038116835260801c6020830152565b919082039182116113fb57565b60ff60085460181c1661447b576144566001600160a01b036144056147b7565b9593509691935050169060003083146000146144675750614433916000526009602052604060002054614095565b905b61444f6001600160801b03918280875116915116906146d2565b1692615edf565b80821015614462575090565b905090565b905060409181526009602052205490614435565b50600090565b60ff60085460181c1661447b576144c96001600160a01b036144a16147b7565b929694509250506144c26001600160801b03918280855116915116906146d2565b1690615e45565b92169060003083036144ed5750614456916000526009602052604060002054614095565b9050604091815260096020522054614456565b6040519061450d82613fb2565b6018546001600160a01b038116835260a01c63ffffffff16602083015260195476ffffffffffffffffffffffffffffffffffffffffffffff166040830152601a546060830152601b546080830152565b6001600160801b0390818111614571571690565b608460405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f32382062697473000000000000000000000000000000000000000000000000006064820152fd5b6001600160a01b038060045416331415908161466b575b8161463e575b8161462f575b5061460557565b60046040517f1d1e647b000000000000000000000000000000000000000000000000000000008152fd5b905060015416331415386145fe565b337f0000000000000000000000007ab788d0483551428f2291232477f1818952998c8216141591506145f8565b80915060025416331415906145f2565b6001600160a01b03806002541633141590816146c3575b5061469957565b60046040517f6f545269000000000000000000000000000000000000000000000000000000008152fd5b90506001541633141538614692565b6001600160801b0391821690821603919082116113fb57565b604051906146f882613fb2565b60006080838281528260208201528260408201528260608201520152565b6002600e5414614727576002600e55565b606460405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b6040519061477882613fb2565b81608060175463ffffffff80821684528160201c16602084015267ffffffffffffffff808260401c16604085015281831c16606084015260c01c910152565b600090819081806147c66146eb565b508060206040516147d681613f80565b828152015260206040516147e981613f80565b82815201526147f661476b565b93614800856148ca565b80519094901561484c5750505060608201519260808301519260a08101519267ffffffffffffffff806020840151166060850152604083015116608084015260e060c083015192015190565b9290935061485b94919461438c565b906148646143b2565b90565b818102929181159184041417156113fb57565b8115614884570490565b634e487b7160e01b600052601260045260246000fd5b519067ffffffffffffffff821682036105d657565b9190916001600160801b03808094169116019182116113fb57565b60408051929167ffffffffffffffff9190610100850183811186821017613f9c57825260009182865260208087019284845282880190858252606089019386855260808a019587875260a08b0198888a5260c08c0194835161492b81613f80565b8a81528a88820152865260e08d0193805161494581613f80565b8b81528b8982015285528d818701908482511642141580614bfa575b614976575b5050505050505050505050505050565b61499b91600186925261498761438c565b8a526149916143b2565b88525116426143d8565b916001600160801b039b8c8951511615600014614bbd57805b836001600160a01b03601354169160648860808d01511691835194859384927fcd3181d50000000000000000000000000000000000000000000000000000000084528b6004850152602484015260448301525afa918215614bb25780948193614b4d575b50505092848093614a4393614a4c97670de0b6b3a764000099971690521684528c8751511690614867565b91511690614867565b04808652878115159182614b33575b505080614b1a575b614a73575b808080808080614966565b63ffffffff91849188614a8f81895116925192828451166148af565b1690528786511688614aa6865192828451166148af565b1690520151169283614ab9575b80614a68565b614b0b94614af5614ae7620186a0614ad68a98614afb9651614867565b048084528787875101511690614867565b9186855151169051906143d8565b9061487a565b80965251019316828451166148af565b16905238808080808080614ab3565b5086614b2c8651828651511690614095565b1115614a63565b81614b449293508451511690614095565b11158738614a5b565b919450915083813d8111614bab575b614b668183613fce565b81010312614ba85750670de0b6b3a764000094928483614a439382614b998e614b92614a4c9b9961489a565b940161489a565b94979950509381959750614a18565b80fd5b503d614b5c565b8451903d90823e3d90fd5b8c87515116620186a09080820291820403614be657614be1908e8b5151169061487a565b6149b4565b602482634e487b7160e01b81526011600452fd5b5060ff60085460381c1615614961565b600090600090600090600090614c1e6146eb565b50614c2761476b565b90614c31826148ca565b8051151580614c3e575050565b935095509250925092606081015192608092838301519360a084015193606084019067ffffffffffffffff80835116928487018281511692602095868601828151169189604097888a019486865116918a519485528c8501528984015260608301527fc63977c8e2362a31182dc8e89a52252f9836922738e0abcfc0de6924972eafe591a18d838251168751918252898201528d87820152898d60608301527f2b5229f33f1d24d5baab718e1e25d0d86195a9b6d786c2c0868edfb21a460e2591a151938285169052519181831690524216838901524363ffffffff1680895285890151861b67ffffffff00000000169160c01b7fffffffffffffffff000000000000000000000000000000000000000000000000169342901b6fffffffffffffffff00000000000000001691171790851b77ffffffffffffffff0000000000000000000000000000000016171760175560c081015191806001600160801b039384815116614dc3906001600160801b03166001600160801b0319601c541617601c55565b015191601c54846001600160801b03198095881b16911617601c5560e0015183815116614e06906001600160801b03166001600160801b0319601d541617601d55565b0151601d54931b16911617601d5582614e1b57565b613f2783305b6001600160a01b0316908115614e80577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602082614e63600094600b54614095565b600b558484526009825260408420818154019055604051908152a3565b606460405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b519081151582036105d657565b600090600090614edf614500565b906040918281019376ffffffffffffffffffffffffffffffffffffffffffffff938486511642141560001461515e5760046001600160a01b03966060888651168451938480927fbd9a548b0000000000000000000000000000000000000000000000000000000082525afa908115615151578498859386936150da575b5090614fbf83927fc1f41e029acf5127d111625602160c4cee3e1a4d38e691e50544d1f7c68b77be9695949a859c6150a8575b42168093528460608a01528360808a01528851166001600160a01b03166001600160a01b03196018541617601855565b60208701517fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff77ffffffff00000000000000000000000000000000000000006018549260a01b169116176018557fffffffffffffffffff0000000000000000000000000000000000000000000000601954161760195581601a5580601b5582519182526020820152a15b6080820161505d81516060850151906143d8565b91620186a09280840293840403615094575061508263ffffffff92602092519061487a565b92015116101561508e57565b60019350565b80634e487b7160e01b602492526011600452fd5b7ffc131c36b7e444dacda44901fd43641dcdcfdc43fe9e2601b3c1dd87061db9e56020838c51168951908152a1614f8f565b9950915091506060883d8211615149575b816150f860609383613fce565b8101031261514557907fc1f41e029acf5127d111625602160c4cee3e1a4d38e691e50544d1f7c68b77be929161512d89614ec4565b60208a01519984015190999394509190614fbf614f5c565b8380fd5b3d91506150eb565b50505051903d90823e3d90fd5b50606082015160808301519095509350615049565b7fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79192936152716001600160a01b03926001600160801b0390816151ba88828451166148af565b168152615201826020830192816151d48c828751166148af565b1684526151e3828c1688614e21565b51166001600160801b03166001600160801b0319601c541617601c55565b51816001600160801b0319601c549260801b16911617601c55604051906323b872dd60e01b6020830152336024830152306044830152861660648201526064815261524b81613fb2565b7f000000000000000000000000853d955acef822db058eb8505911ed77f175b99e615bff565b604080516001600160801b03958616815295909416602086015216923392819081015b0390a3565b9091926152d66001600160a01b039283871696873303615559575b506152bd6143b2565b6001600160801b039283918280855116915116906146d2565b1691808616928381106155175750806152f287828551166146d2565b168252602091615331828483019261530d8b838651166146d2565b93828516905251166001600160801b03166001600160801b0319601c541617601c55565b816001600160801b0319601c549260801b16911617601c5586169187156154ad57876000526009825260409283600020548181106154445761524b8461540d7ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db9998979560008e7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef61543f9a8961541b9a63a9059cbb60e01b9a85875260098452038c86205580600b5403600b558b51908152a386519485938401528860248401602090939291936001600160a01b0360408201951681520152565b03601f198101835282613fce565b516001600160801b0395861681529590941660208601521692339281906040820190565b0390a4565b60848486519062461bcd60e51b82526004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152fd5b6084826040519062461bcd60e51b82526004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152fd5b6040517fc5bb6dae00000000000000000000000000000000000000000000000000000000815260048101919091526001600160801b0387166024820152604490fd5b87600052600a602052604060002033600052602052604060002054906000198203615585575b506152b4565b61559c6155a4926001600160801b038a16906143d8565b903390614258565b388061557f565b90600f54918215615647576001600160a01b036155c66143b2565b9116916155e160009284845260208052604084205490615f10565b92831561563d578252601f602052604082205492831561563557670de0b6b3a76400009161560e91614867565b0490620186a0918281029281840414901517156150945750906156309161487a565b111590565b505091505090565b5050505050600190565b505050600190565b91906156596143b2565b6001600160801b03908161567b8161566f61438c565b511682845116906146d2565b1691808616928381106155175750806156a16156978585615ddb565b97828551166148af565b1682526156e08160208401936156bc828a16838751166148af565b94828616905251166001600160801b03166001600160801b0319601d541617601d55565b6001600160801b0319601d549260801b16911617601d553360005260208052604060002061570f858254614095565b90556001600160a01b038216918130840361575b575b50506040519081528360208201527f01348584ec81ac7acd52b7d66d9ade986dd909f3d513881c190fc31c90527efe60403392a3565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448201526157919061524b816064810161540d565b3881615725565b9160207fa32435755c235de2976ed44a75a2f85cb01faf0c894f639fe0c32bb9455fea8f916001600160a01b038091169485600052601f835260406000206157e1868254614095565b90556157ef85601e54614095565b601e551692308403615805575b604051908152a3565b61585c6040516323b872dd60e01b848201528560248201523060448201528260648201526064815261583681613fb2565b7f0000000000000000000000004d224452801aced8b2f0aebe155379bb5d594381615bff565b6157fc565b6001600160a01b038093169283600052601f60205260406000206158868382546143d8565b905561589482601e546143d8565b601e55821691813084036158d3575b50506040519081527fbc290bb45104f73cf92115c9603987c3f8fd30c182a13603d8cffa49b5f5995260203392a4565b60405163a9059cbb60e01b60208201526001600160a01b039092166024830152604482015261590990615836816064810161540d565b38816158a3565b93907f9dc1449a0ff0c152e18e8289d865b47acc6e1b76b1ecb239c13d6ee22a9206a792916001600160801b03948561594c84828a51166146d2565b168752602087018661596186828451166146d2565b1681526159ae876001600160a01b03809516998a60005260208052604060002061598e838a1682546143d8565b905551166001600160801b03166001600160801b0319601d541617601d55565b51866001600160801b0319601d549260801b16911617601d5516933085036159f5575b50604080516001600160801b03928316815292909116602083015281908101615294565b615a2a90604051906323b872dd60e01b6020830152866024830152306044830152831660648201526064815261524b81613fb2565b386159d1565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8111615a5a5790565b608460405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e743235360000000000000000000000000000000000000000000000006064820152fd5b8051156114465760200190565b80518210156114465760209160051b010190565b60209081818403126105d65780519067ffffffffffffffff82116105d657019180601f840112156105d6578251615b1b81613ff0565b93615b296040519586613fce565b818552838086019260051b8201019283116105d6578301905b828210615b50575050505090565b81518152908301908301615b42565b91908110156114465760051b0190565b356001600160a01b03811681036105d65790565b6001600160a01b03600154163303615b9757565b60046040517f1c0be90a000000000000000000000000000000000000000000000000000000008152fd5b6001600160a01b03600054163303615bd557565b60046040517ff5c49e64000000000000000000000000000000000000000000000000000000008152fd5b6001600160a01b031690604051615c1581613f80565b6020928382527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564848301526000808486829651910182855af13d15615d41573d9167ffffffffffffffff8311615d2d5790615c9093929160405192615c8388601f19601f8401160185613fce565b83523d868885013e615d4b565b90815180615c9f575b50505050565b82849181010312614ba8575081615cb69101614ec4565b15615cc357808080615c99565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b602485634e487b7160e01b81526041600452fd5b90615c9092916060915b91929015615dac5750815115615d5f575090565b3b15615d685790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b825190915015615dbf5750805190602001fd5b610a4d9060405191829162461bcd60e51b835260048301613da7565b91906001600160801b038084511615600014615df657509150565b615e2f906020850190615e2681615e1d615e138286511688614867565b828a51169061487a565b97511687614867565b9151169061487a565b10615e3657565b90600181018091116113fb5790565b6001600160801b038082511615600014615e5e57505090565b615e26614864938260208501511690614867565b90916001600160801b038083511615600014615e8e5750505090565b602083959492930190615eb1615ea78284511685614867565b828851169061487a565b9584615ec2575b50505050615e3657565b615ed593945081615e2691511687614867565b1038808080615eb8565b60208101906001600160801b03908183511615600014615eff5750505090565b6148649382615e2692511690614867565b919060208301926001600160801b038085511615600014615f32575090925050565b9081615e2681615e1d615e13615e2f9686511688614867565b909160208201916001600160801b038084511615600014615f6d575050505090565b615eb1615ea78284989795969851168561486756fea164736f6c6343000812000a

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.