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