APE Price: $1.03 (-1.47%)

Contract Diff Checker

Contract Name:
MarketConfigurator

Contract Source Code:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "openzeppelin-contracts/contracts/access/Ownable2Step.sol";
import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./Constants.sol";
import "../../interfaces/IApeFinance.sol";
import "../../interfaces/IAToken.sol";
import "../../interfaces/IDebtToken.sol";
import "../../interfaces/IPToken.sol";
import "../../libraries/DataTypes.sol";
import "../../libraries/PauseFlags.sol";

contract MarketConfigurator is Ownable2Step, Constants {
    using PauseFlags for DataTypes.MarketConfig;

    /// @notice The ApeFinance contract
    IApeFinance public immutable apeFinance;

    /// @notice The address of the guardian
    address public guardian;

    event GuardianSet(address guardian);
    event MarketListed(
        address market,
        address aToken,
        address debtToken,
        address interestRateModel,
        uint16 reserveFactor,
        bool isPToken
    );
    event MarketDelisted(address market);
    event MarketCollateralFactorSet(address market, uint16 collateralFactor);
    event MarketLiquidationThresholdSet(address market, uint16 liquidationThreshold);
    event MarketLiquidationBonusSet(address market, uint16 liquidationBonus);
    event MarketReserveFactorSet(address market, uint16 reserveFactor);
    event MarketInterestRateModelSet(address market, address interestRateModel);
    event MarketSupplyCapSet(address market, uint256 cap);
    event MarketBorrowCapSet(address market, uint256 cap);
    event MarketPausedSet(address market, string action, bool paused);
    event MarketFrozen(address market, bool state);
    event MarketConfiguredAsPToken(address market);

    constructor(address apeFinance_) {
        apeFinance = IApeFinance(apeFinance_);
    }

    /**
     * @notice Check if the caller is the owner or the guardian.
     */
    modifier onlyOwnerOrGuardian() {
        _checkOwnerOrGuardian();
        _;
    }

    /* ========== VIEW FUNCTIONS ========== */

    /**
     * @notice Get the market configuration of a market.
     * @return The market configuration
     */
    function getMarketConfiguration(address market) public view returns (DataTypes.MarketConfig memory) {
        return apeFinance.getMarketConfiguration(market);
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    /**
     * @notice Set the guardian of market configurator.
     * @param _guardian The address of the guardian
     */
    function setGuardian(address _guardian) external onlyOwner {
        guardian = _guardian;

        emit GuardianSet(guardian);
    }

    /**
     * @notice List a market to ApeFinance.
     * @param market The market to be listed
     * @param aTokenAddress The address of the aToken
     * @param debtTokenAddress The address of the debtToken
     * @param interestRateModelAddress The address of the interest rate model
     * @param reserveFactor The reserve factor of the market
     */
    function listMarket(
        address market,
        address aTokenAddress,
        address debtTokenAddress,
        address interestRateModelAddress,
        uint16 reserveFactor
    ) external onlyOwner {
        _listMarket(market, aTokenAddress, debtTokenAddress, interestRateModelAddress, reserveFactor, false);
    }

    /**
     * @notice List a pToken market to ApeFinance.
     * @param market The market to be listed
     * @param aTokenAddress The address of the aToken
     * @param interestRateModelAddress The address of the interest rate model
     * @param reserveFactor The reserve factor of the market
     */
    function listPTokenMarket(
        address market,
        address aTokenAddress,
        address interestRateModelAddress,
        uint16 reserveFactor
    ) external onlyOwner {
        _listMarket(market, aTokenAddress, address(0), interestRateModelAddress, reserveFactor, true);
    }

    /**
     * @notice Configure a market as collateral.
     * @dev This function is used for the first time to configure a market as collateral.
     * @param market The market to be configured
     * @param collateralFactor The collateral factor of the market
     * @param liquidationThreshold The liquidation threshold of the market
     * @param liquidationBonus The liquidation bonus of the market
     */
    function configureMarketAsCollateral(
        address market,
        uint16 collateralFactor,
        uint16 liquidationThreshold,
        uint16 liquidationBonus
    ) external onlyOwner {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");
        require(
            config.collateralFactor == 0 && config.liquidationThreshold == 0 && config.liquidationBonus == 0,
            "already configured"
        );
        require(collateralFactor > 0 && collateralFactor <= MAX_COLLATERAL_FACTOR, "invalid collateral factor");
        require(
            liquidationThreshold > 0 && liquidationThreshold <= MAX_LIQUIDATION_THRESHOLD
                && liquidationThreshold >= collateralFactor,
            "invalid liquidation threshold"
        );
        require(
            liquidationBonus > MIN_LIQUIDATION_BONUS && liquidationBonus <= MAX_LIQUIDATION_BONUS,
            "invalid liquidation bonus"
        );
        require(
            uint256(liquidationThreshold) * uint256(liquidationBonus) / FACTOR_SCALE
                <= MAX_LIQUIDATION_THRESHOLD_X_BONUS,
            "liquidation threshold * liquidation bonus larger than 100%"
        );

        config.collateralFactor = collateralFactor;
        config.liquidationThreshold = liquidationThreshold;
        config.liquidationBonus = liquidationBonus;
        apeFinance.setMarketConfiguration(market, config);

        emit MarketCollateralFactorSet(market, collateralFactor);
        emit MarketLiquidationThresholdSet(market, liquidationThreshold);
        emit MarketLiquidationBonusSet(market, liquidationBonus);
    }

    /**
     * @notice Adjust the collateral factor of a market.
     * @param market The market to be adjusted
     * @param collateralFactor The new collateral factor of the market
     */
    function adjustMarketCollateralFactor(address market, uint16 collateralFactor) external onlyOwner {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");
        if (collateralFactor > 0) {
            require(collateralFactor <= MAX_COLLATERAL_FACTOR, "invalid collateral factor");
            require(
                collateralFactor <= config.liquidationThreshold, "collateral factor larger than liquidation threshold"
            );
        }

        config.collateralFactor = collateralFactor;
        apeFinance.setMarketConfiguration(market, config);

        emit MarketCollateralFactorSet(market, collateralFactor);
    }

    /**
     * @notice Adjust the reserve factor of a market.
     * @param market The market to be adjusted
     * @param reserveFactor The new reserve factor of the market
     */
    function adjustMarketReserveFactor(address market, uint16 reserveFactor) external onlyOwner {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");
        require(reserveFactor <= MAX_RESERVE_FACTOR, "invalid reserve factor");

        // Accrue interests before changing reserve factor.
        apeFinance.accrueInterest(market);

        config.reserveFactor = reserveFactor;
        apeFinance.setMarketConfiguration(market, config);

        emit MarketReserveFactorSet(market, reserveFactor);
    }

    /**
     * @notice Adjust the liquidation threshold of a market.
     * @param market The market to be adjusted
     * @param liquidationThreshold The new liquidation threshold of the market
     */
    function adjustMarketLiquidationThreshold(address market, uint16 liquidationThreshold) external onlyOwner {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");
        if (liquidationThreshold > 0) {
            require(liquidationThreshold <= MAX_LIQUIDATION_THRESHOLD, "invalid liquidation threshold");
            require(
                liquidationThreshold >= config.collateralFactor, "liquidation threshold smaller than collateral factor"
            );
            require(
                uint256(liquidationThreshold) * uint256(config.liquidationBonus) / FACTOR_SCALE
                    <= MAX_LIQUIDATION_THRESHOLD_X_BONUS,
                "liquidation threshold * liquidation bonus larger than 100%"
            );
        } else {
            require(config.collateralFactor == 0, "collateral factor not zero");
        }

        config.liquidationThreshold = liquidationThreshold;
        apeFinance.setMarketConfiguration(market, config);

        emit MarketLiquidationThresholdSet(market, liquidationThreshold);
    }

    /**
     * @notice Adjust the liquidation bonus of a market.
     * @param market The market to be adjusted
     * @param liquidationBonus The new liquidation bonus of the market
     */
    function adjustMarketLiquidationBonus(address market, uint16 liquidationBonus) external onlyOwner {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");
        if (liquidationBonus > 0) {
            require(
                liquidationBonus > MIN_LIQUIDATION_BONUS && liquidationBonus <= MAX_LIQUIDATION_BONUS,
                "invalid liquidation bonus"
            );
            require(
                uint256(config.liquidationThreshold) * uint256(liquidationBonus) / FACTOR_SCALE
                    <= MAX_LIQUIDATION_THRESHOLD_X_BONUS,
                "liquidation threshold * liquidation bonus larger than 100%"
            );
        } else {
            require(
                config.collateralFactor == 0 && config.liquidationThreshold == 0,
                "collateral factor or liquidation threshold not zero"
            );
        }

        config.liquidationBonus = liquidationBonus;
        apeFinance.setMarketConfiguration(market, config);

        emit MarketLiquidationBonusSet(market, liquidationBonus);
    }

    /**
     * @notice Change the interest rate model of a market.
     * @param market The market to be changed
     * @param interestRateModelAddress The new interest rate model of the market
     */
    function changeMarketInterestRateModel(address market, address interestRateModelAddress) external onlyOwner {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");

        // Accrue interests before changing IRM.
        apeFinance.accrueInterest(market);

        config.interestRateModelAddress = interestRateModelAddress;
        apeFinance.setMarketConfiguration(market, config);

        emit MarketInterestRateModelSet(market, interestRateModelAddress);
    }

    /**
     * @notice Soft delist a market.
     * @dev Soft delisting a market means that the supply and borrow will be paused and the reserve factor will be set to 100%.
     * @param market The market to be soft delisted
     */
    function softDelistMarket(address market) external onlyOwner {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");

        if (!config.isSupplyPaused()) {
            config.setSupplyPaused(true);
            emit MarketPausedSet(market, "supply", true);
        }
        if (!config.isBorrowPaused()) {
            config.setBorrowPaused(true);
            emit MarketPausedSet(market, "borrow", true);
        }
        if (config.reserveFactor != MAX_RESERVE_FACTOR) {
            // Accrue interests before changing reserve factor.
            apeFinance.accrueInterest(market);

            config.reserveFactor = MAX_RESERVE_FACTOR;
            emit MarketReserveFactorSet(market, MAX_RESERVE_FACTOR);
        }
        apeFinance.setMarketConfiguration(market, config);
    }

    /**
     * @notice Hard delist a market.
     * @param market The market to be hard delisted
     */
    function hardDelistMarket(address market) external onlyOwner {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");
        require(config.isSupplyPaused() && config.isBorrowPaused(), "not paused");
        require(config.reserveFactor == MAX_RESERVE_FACTOR, "reserve factor not max");
        require(
            config.collateralFactor == 0 && config.liquidationThreshold == 0,
            "collateral factor or liquidation threshold not zero"
        );

        apeFinance.delistMarket(market);

        emit MarketDelisted(market);
    }

    /**
     * @notice Pause or unpause the transfer of a market's aToken.
     * @param market The market's aToken to be paused or unpaused
     * @param paused Pause or unpause
     */
    function setMarketTransferPaused(address market, bool paused) external onlyOwner {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");

        config.setTransferPaused(paused);
        apeFinance.setMarketConfiguration(market, config);

        emit MarketPausedSet(market, "transfer", paused);
    }

    struct MarketCap {
        address market;
        uint256 cap;
    }

    /**
     * @notice Set the supply cap of a list of markets.
     * @param marketCaps The list of markets and their supply caps
     */
    function setMarketSupplyCaps(MarketCap[] calldata marketCaps) external onlyOwnerOrGuardian {
        uint256 length = marketCaps.length;
        for (uint256 i = 0; i < length;) {
            address market = marketCaps[i].market;
            uint256 cap = marketCaps[i].cap;
            DataTypes.MarketConfig memory config = getMarketConfiguration(market);
            require(config.isListed, "not listed");

            config.supplyCap = cap;
            apeFinance.setMarketConfiguration(market, config);

            emit MarketSupplyCapSet(market, cap);

            unchecked {
                i++;
            }
        }
    }

    /**
     * @notice Set the borrow cap of a list of markets.
     * @param marketCaps The list of markets and their borrow caps
     */
    function setMarketBorrowCaps(MarketCap[] calldata marketCaps) external onlyOwnerOrGuardian {
        uint256 length = marketCaps.length;
        for (uint256 i = 0; i < length;) {
            address market = marketCaps[i].market;
            uint256 cap = marketCaps[i].cap;
            DataTypes.MarketConfig memory config = getMarketConfiguration(market);
            require(config.isListed, "not listed");
            require(!config.isPToken, "cannot set borrow cap for pToken");

            config.borrowCap = cap;
            apeFinance.setMarketConfiguration(market, config);

            emit MarketBorrowCapSet(market, cap);

            unchecked {
                i++;
            }
        }
    }

    /**
     * @notice Pause or unpause the supply of a market.
     * @param market The market to be paused or unpaused
     * @param paused Pause or unpause
     */
    function setMarketSupplyPaused(address market, bool paused) external onlyOwnerOrGuardian {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");

        config.setSupplyPaused(paused);
        apeFinance.setMarketConfiguration(market, config);

        emit MarketPausedSet(market, "supply", paused);
    }

    /**
     * @notice Pause or unpause the borrow of a market.
     * @param market The market to be paused or unpaused
     * @param paused Pause or unpause
     */
    function setMarketBorrowPaused(address market, bool paused) external onlyOwnerOrGuardian {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");
        require(!config.isPToken, "cannot set borrow paused for pToken");

        config.setBorrowPaused(paused);
        apeFinance.setMarketConfiguration(market, config);

        emit MarketPausedSet(market, "borrow", paused);
    }

    /**
     * @notice Configure a market as a pToken.
     * @dev This function can be called when the pToken was accidentally listed by using `listMarket` function.
     * @param market The market to be configured as a pToken
     */
    function configureMarketAsPToken(address market) external onlyOwnerOrGuardian {
        // Simple sanity check to make sure the market is a pToken.
        IPToken(market).getUnderlying();

        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(config.isListed, "not listed");
        require(!config.isPToken, "already a pToken");

        config.isPToken = true;
        config.setBorrowPaused(true);
        // Set the borrow cap to a very small amount (1 Wei) to prevent borrowing.
        config.borrowCap = 1;

        apeFinance.setMarketConfiguration(market, config);

        emit MarketConfiguredAsPToken(market);
    }

    /* ========== INTERNAL FUNCTIONS ========== */

    /**
     * @dev Check if the caller is the owner or guardian.
     */
    function _checkOwnerOrGuardian() internal view {
        require(msg.sender == owner() || msg.sender == guardian, "!authorized");
    }

    /**
     * @dev List a vanilla market or a pToken market. Markets that were delisted can't be listed again.
     * @param market The market to be listed
     * @param aTokenAddress The aToken of the market
     * @param debtTokenAddress The debtToken of the market
     * @param interestRateModelAddress The interest rate model of the market
     * @param reserveFactor The reserve factor of the market
     * @param isPToken Whether the market is a pToken market
     */
    function _listMarket(
        address market,
        address aTokenAddress,
        address debtTokenAddress,
        address interestRateModelAddress,
        uint16 reserveFactor,
        bool isPToken
    ) internal {
        DataTypes.MarketConfig memory config = getMarketConfiguration(market);
        require(!config.isListed, "already listed");
        require(!config.isDelisted, "already delisted");
        require(IAToken(aTokenAddress).asset() == market, "mismatch market");
        if (!isPToken) {
            require(IDebtToken(debtTokenAddress).asset() == market, "mismatch market");
        }
        require(reserveFactor <= MAX_RESERVE_FACTOR, "invalid reserve factor");

        uint8 underlyingDecimals = IERC20Metadata(market).decimals();
        require(underlyingDecimals <= 18, "nonstandard token decimals");

        config.isListed = true;
        config.aTokenAddress = aTokenAddress;
        config.interestRateModelAddress = interestRateModelAddress;
        config.reserveFactor = reserveFactor;
        config.initialExchangeRate = 10 ** underlyingDecimals;
        if (isPToken) {
            config.isPToken = true;
            config.setBorrowPaused(true);
            // Set the borrow cap to a very small amount (1 Wei) to prevent borrowing.
            config.borrowCap = 1;
        } else {
            config.debtTokenAddress = debtTokenAddress;
        }

        apeFinance.listMarket(market, config);

        emit MarketListed(market, aTokenAddress, debtTokenAddress, interestRateModelAddress, reserveFactor, isPToken);
    }
}

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

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

abstract contract Constants {
    uint256 internal constant INITIAL_BORROW_INDEX = 1e18;
    uint256 internal constant INITIAL_EXCHANGE_RATE = 1e18;
    uint256 internal constant FACTOR_SCALE = 10000;

    uint16 internal constant MAX_COLLATERAL_FACTOR = 9000; // 90%
    uint16 internal constant MAX_LIQUIDATION_THRESHOLD = 10000; // 100%
    uint16 internal constant MIN_LIQUIDATION_BONUS = 10000; // 100%
    uint16 internal constant MAX_LIQUIDATION_BONUS = 12500; // 125%
    uint16 internal constant MAX_LIQUIDATION_THRESHOLD_X_BONUS = 10000; // 100%
    uint16 internal constant MAX_RESERVE_FACTOR = 10000; // 100%

    uint8 internal constant LIQUIDITY_CHECK_NORMAL = 0;
    uint8 internal constant LIQUIDITY_CHECK_DEFERRED = 1;
    uint8 internal constant LIQUIDITY_CHECK_DIRTY = 2;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../libraries/DataTypes.sol";

interface IApeFinance {
    /* ========== USER INTERFACES ========== */

    function accrueInterest(address market) external;

    function supply(address from, address to, address market, uint256 amount) external;

    function borrow(address from, address to, address asset, uint256 amount) external;

    function redeem(address from, address to, address asset, uint256 amount) external returns (uint256);

    function repay(address from, address to, address asset, uint256 amount) external returns (uint256);

    function liquidate(
        address liquidator,
        address borrower,
        address marketBorrow,
        address marketCollateral,
        uint256 repayAmount
    ) external returns (uint256, uint256);

    function deferLiquidityCheck(address user, bytes memory data) external;

    function getBorrowBalance(address user, address market) external view returns (uint256);

    function getATokenBalance(address user, address market) external view returns (uint256);

    function getSupplyBalance(address user, address market) external view returns (uint256);

    function isMarketListed(address market) external view returns (bool);

    function getExchangeRate(address market) external view returns (uint256);

    function getTotalSupply(address market) external view returns (uint256);

    function getTotalBorrow(address market) external view returns (uint256);

    function getTotalCash(address market) external view returns (uint256);

    function getTotalReserves(address market) external view returns (uint256);

    function getAccountLiquidity(address user) external view returns (uint256, uint256, uint256);

    function isAllowedExtension(address user, address extension) external view returns (bool);

    function transferAToken(address market, address from, address to, uint256 amount) external;

    function setSubAccountExtension(address primary, uint256 subAccountId, bool allowed) external;

    /* ========== MARKET CONFIGURATOR INTERFACES ========== */

    function getMarketConfiguration(address market) external view returns (DataTypes.MarketConfig memory);

    function listMarket(address market, DataTypes.MarketConfig calldata config) external;

    function delistMarket(address market) external;

    function setMarketConfiguration(address market, DataTypes.MarketConfig calldata config) external;

    /* ========== CREDIT LIMIT MANAGER INTERFACES ========== */

    function getCreditLimit(address user, address market) external view returns (uint256);

    function getUserCreditMarkets(address user) external view returns (address[] memory);

    function isCreditAccount(address user) external view returns (bool);

    function setCreditLimit(address user, address market, uint256 credit) external;

    /* ========== RESERVE MANAGER INTERFACES ========== */

    function absorbToReserves(address market) external;

    function reduceReserves(address market, uint256 aTokenAmount, address recipient) external;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IAToken {
    function mint(address account, uint256 amount) external;

    function burn(address account, uint256 amount) external;

    function seize(address from, address to, uint256 amount) external;

    function asset() external view returns (address);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

interface IDebtToken is IERC20, IERC20Metadata {
    function asset() external view returns (address);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

interface IPToken is IERC20 {
    function getUnderlying() external view returns (address);

    function wrap(uint256 amount) external;

    function unwrap(uint256 amount) external;

    function absorb(address user) external;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

library DataTypes {
    struct UserBorrow {
        uint256 borrowBalance;
        uint256 borrowIndex;
    }

    struct MarketConfig {
        // 1 + 1 + 2 + 2 + 2 + 2 + 1 + 1 = 12
        bool isListed;
        uint8 pauseFlags;
        uint16 collateralFactor;
        uint16 liquidationThreshold;
        uint16 liquidationBonus;
        uint16 reserveFactor;
        bool isPToken;
        bool isDelisted;
        // 20 + 20 + 20 + 32 + 32 + 32
        address aTokenAddress;
        address debtTokenAddress;
        address interestRateModelAddress;
        uint256 supplyCap;
        uint256 borrowCap;
        uint256 initialExchangeRate;
    }

    struct Market {
        MarketConfig config;
        uint40 lastUpdateTimestamp;
        uint256 totalCash;
        uint256 totalBorrow;
        uint256 totalSupply;
        uint256 totalReserves;
        uint256 borrowIndex;
        mapping(address => UserBorrow) userBorrows;
        mapping(address => uint256) userSupplies;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./DataTypes.sol";

library PauseFlags {
    /// @dev Mask for specific actions in the pause flag bit array
    uint8 internal constant PAUSE_SUPPLY_MASK = 0xFE;
    uint8 internal constant PAUSE_BORROW_MASK = 0xFD;
    uint8 internal constant PAUSE_TRANSFER_MASK = 0xFB;

    /// @dev Offsets for specific actions in the pause flag bit array
    uint8 internal constant PAUSE_SUPPLY_OFFSET = 0;
    uint8 internal constant PAUSE_BORROW_OFFSET = 1;
    uint8 internal constant PAUSE_TRANSFER_OFFSET = 2;

    /// @dev Sets the market supply paused.
    function setSupplyPaused(DataTypes.MarketConfig memory self, bool paused) internal pure {
        self.pauseFlags = (self.pauseFlags & PAUSE_SUPPLY_MASK) | (toUInt8(paused) << PAUSE_SUPPLY_OFFSET);
    }

    /// @dev Returns true if the market supply is paused, and false otherwise.
    function isSupplyPaused(DataTypes.MarketConfig memory self) internal pure returns (bool) {
        return toBool(self.pauseFlags & ~PAUSE_SUPPLY_MASK);
    }

    /// @dev Sets the market borrow paused.
    function setBorrowPaused(DataTypes.MarketConfig memory self, bool paused) internal pure {
        self.pauseFlags = (self.pauseFlags & PAUSE_BORROW_MASK) | (toUInt8(paused) << PAUSE_BORROW_OFFSET);
    }

    /// @dev Returns true if the market borrow is paused, and false otherwise.
    function isBorrowPaused(DataTypes.MarketConfig memory self) internal pure returns (bool) {
        return toBool(self.pauseFlags & ~PAUSE_BORROW_MASK);
    }

    /// @dev Sets the market transfer paused.
    function setTransferPaused(DataTypes.MarketConfig memory self, bool paused) internal pure {
        self.pauseFlags = (self.pauseFlags & PAUSE_TRANSFER_MASK) | (toUInt8(paused) << PAUSE_TRANSFER_OFFSET);
    }

    /// @dev Returns true if the market transfer is paused, and false otherwise.
    function isTransferPaused(DataTypes.MarketConfig memory self) internal pure returns (bool) {
        return toBool(self.pauseFlags & ~PAUSE_TRANSFER_MASK);
    }

    /// @dev Casts a boolean to uint8.
    function toUInt8(bool x) internal pure returns (uint8) {
        return x ? 1 : 0;
    }

    /// @dev Casts a uint8 to boolean.
    function toBool(uint8 x) internal pure returns (bool) {
        return x != 0;
    }
}

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

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

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

Please enter a contract address above to load the contract details and source code.

Context size (optional):