Contract Name:
ERC4626OracleAdapter
Contract Source Code:
// SPDX-License-Identifier: BSUL-1.1
pragma solidity =0.7.6;
import "../../../interfaces/IERC4626.sol";
import "../../../interfaces/IERC20.sol";
import "../../math/SafeUint256.sol";
import "./ChainlinkAdapter.sol";
contract ERC4626OracleAdapter is ChainlinkAdapter {
int256 immutable ASSET_PRECISION;
using SafeInt256 for int256;
using SafeUint256 for uint256;
constructor (
AggregatorV2V3Interface baseToUSDOracle_,
AggregatorV2V3Interface quoteToUSDOracle_,
bool invertBase_,
bool invertQuote_,
string memory description_,
AggregatorV2V3Interface sequencerUptimeOracle_
) ChainlinkAdapter(
baseToUSDOracle_, quoteToUSDOracle_, invertBase_, invertQuote_, description_, sequencerUptimeOracle_
) {
uint256 assetDecimals = IERC20(IERC4626(address(quoteToUSDOracle_)).asset()).decimals();
require(assetDecimals <= 18);
ASSET_PRECISION = int256(10 ** assetDecimals);
}
function _getQuoteRate() internal view override returns (int256 quoteRate) {
// quoteToUSDDecimals will be equal to 1 share of the ERC4626 token.
// ASSET_PRECISION is equal to 1 asset token. This is returned as: ASSET/SHARE
IERC4626 oracle = IERC4626(address(quoteToUSDOracle));
quoteRate = invertQuote ?
oracle.convertToShares(uint256(ASSET_PRECISION)).toInt()
.mul(ASSET_PRECISION).div(quoteToUSDDecimals) :
oracle.convertToAssets(uint256(quoteToUSDDecimals)).toInt()
.mul(quoteToUSDDecimals).div(ASSET_PRECISION);
}
}
pragma solidity >=0.7.6;
interface IERC4626 {
event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed caller,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is "managed" by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the "per-user" price-per-share, and instead should reflect the
* "average-user's" price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the "per-user" price-per-share, and instead should reflect the
* "average-user's" price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(
uint256 assets,
address receiver,
address owner
) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
function decimals() external view returns (uint8);
/**
* @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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);
/**
* @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);
}
// SPDX-License-Identifier: BSUL-1.1
pragma solidity =0.7.6;
import {Constants} from "../global/Constants.sol";
library SafeUint256 {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0);
return a / b;
}
function max(uint256 x, uint256 y) internal pure returns (uint256) {
return x > y ? x : y;
}
function min(uint256 x, uint256 y) internal pure returns (uint256) {
return x < y ? x : y;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0);
return a % b;
}
function divInRatePrecision(uint256 x, uint256 y) internal pure returns (uint256) {
return div(mul(x, uint256(Constants.RATE_PRECISION)), y);
}
function mulInRatePrecision(uint256 x, uint256 y) internal pure returns (uint256) {
return div(mul(x, y), uint256(Constants.RATE_PRECISION));
}
function divInScalarPrecision(uint256 x, uint256 y) internal pure returns (uint256) {
return div(mul(x, Constants.SCALAR_PRECISION), y);
}
function mulInScalarPrecision(uint256 x, uint256 y) internal pure returns (uint256) {
return div(mul(x, y), Constants.SCALAR_PRECISION);
}
function toUint8(uint256 x) internal pure returns (uint8) {
require(x <= type(uint8).max);
return uint8(x);
}
function toUint32(uint256 x) internal pure returns (uint32) {
require(x <= type(uint32).max);
return uint32(x);
}
function toUint40(uint256 x) internal pure returns (uint40) {
require(x <= type(uint40).max);
return uint40(x);
}
function toUint48(uint256 x) internal pure returns (uint48) {
require(x <= type(uint48).max);
return uint48(x);
}
function toUint56(uint256 x) internal pure returns (uint56) {
require(x <= type(uint56).max);
return uint56(x);
}
function toUint72(uint256 x) internal pure returns (uint72) {
require(x <= type(uint72).max);
return uint72(x);
}
function toUint80(uint256 x) internal pure returns (uint80) {
require(x <= type(uint80).max);
return uint80(x);
}
function toUint88(uint256 x) internal pure returns (uint88) {
require(x <= type(uint88).max);
return uint88(x);
}
function toUint104(uint256 x) internal pure returns (uint104) {
require(x <= type(uint104).max);
return uint104(x);
}
function toUint112(uint256 x) internal pure returns (uint112) {
require(x <= type(uint112).max);
return uint112(x);
}
function toUint128(uint256 x) internal pure returns (uint128) {
require(x <= type(uint128).max);
return uint128(x);
}
function toInt(uint256 x) internal pure returns (int256) {
require (x <= uint256(type(int256).max)); // dev: toInt overflow
return int256(x);
}
}
// SPDX-License-Identifier: BSUL-1.1
pragma solidity =0.7.6;
import "../../math/SafeInt256.sol";
import "../../../interfaces/chainlink/AggregatorV2V3Interface.sol";
contract ChainlinkAdapter is AggregatorV2V3Interface {
using SafeInt256 for int256;
uint8 public override constant decimals = 18;
uint256 public override constant version = 1;
int256 public constant rateDecimals = 10**18;
string public override description;
// Grace period after a sequencer downtime has occurred
uint256 public constant SEQUENCER_UPTIME_GRACE_PERIOD = 1 hours;
AggregatorV2V3Interface public immutable baseToUSDOracle;
int256 public immutable baseToUSDDecimals;
AggregatorV2V3Interface public immutable quoteToUSDOracle;
int256 public immutable quoteToUSDDecimals;
bool public immutable invertBase;
bool public immutable invertQuote;
AggregatorV2V3Interface public immutable sequencerUptimeOracle;
constructor (
AggregatorV2V3Interface baseToUSDOracle_,
AggregatorV2V3Interface quoteToUSDOracle_,
bool invertBase_,
bool invertQuote_,
string memory description_,
AggregatorV2V3Interface sequencerUptimeOracle_
) {
description = description_;
baseToUSDOracle = baseToUSDOracle_;
quoteToUSDOracle = quoteToUSDOracle_;
uint8 _baseDecimals = baseToUSDOracle_.decimals();
uint8 _quoteDecimals = quoteToUSDOracle_.decimals();
require(_baseDecimals <= 18);
require(_quoteDecimals <= 18);
baseToUSDDecimals = int256(10**_baseDecimals);
quoteToUSDDecimals = int256(10**_quoteDecimals);
invertBase = invertBase_;
invertQuote = invertQuote_;
sequencerUptimeOracle = sequencerUptimeOracle_;
}
function _checkSequencer() private view {
// See: https://docs.chain.link/data-feeds/l2-sequencer-feeds/
if (address(sequencerUptimeOracle) != address(0)) {
(
/*uint80 roundID*/,
int256 answer,
uint256 startedAt,
/*uint256 updatedAt*/,
/*uint80 answeredInRound*/
) = sequencerUptimeOracle.latestRoundData();
require(answer == 0, "Sequencer Down");
require(SEQUENCER_UPTIME_GRACE_PERIOD < block.timestamp - startedAt, "Sequencer Grace Period");
}
}
function _getQuoteRate() internal view virtual returns (int256 quoteRate) {
(
/* roundId */,
quoteRate,
/* uint256 startedAt */,
/* updatedAt */,
/* answeredInRound */
) = quoteToUSDOracle.latestRoundData();
require(quoteRate > 0, "Chainlink Rate Error");
if (invertQuote) quoteRate = (quoteToUSDDecimals * quoteToUSDDecimals) / quoteRate;
}
function _calculateBaseToQuote() internal view returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
) {
_checkSequencer();
int256 baseToUSD;
(
roundId,
baseToUSD,
startedAt,
updatedAt,
answeredInRound
) = baseToUSDOracle.latestRoundData();
require(baseToUSD > 0, "Chainlink Rate Error");
// Overflow and div by zero not possible
if (invertBase) baseToUSD = (baseToUSDDecimals * baseToUSDDecimals) / baseToUSD;
int256 quoteToUSD = _getQuoteRate();
// To convert from USDC/USD (base) and ETH/USD (quote) to USDC/ETH we do:
// (USDC/USD * quoteDecimals * 1e18) / (ETH/USD * baseDecimals)
answer = baseToUSD
.mul(quoteToUSDDecimals)
.mul(rateDecimals)
.div(quoteToUSD)
.div(baseToUSDDecimals);
}
function latestRoundData() external view override returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
) {
return _calculateBaseToQuote();
}
function latestAnswer() external view override returns (int256 answer) {
(/* */, answer, /* */, /* */, /* */) = _calculateBaseToQuote();
}
function latestTimestamp() external view override returns (uint256 updatedAt) {
(/* */, /* */, /* */, updatedAt, /* */) = _calculateBaseToQuote();
}
function latestRound() external view override returns (uint256 roundId) {
(roundId, /* */, /* */, /* */, /* */) = _calculateBaseToQuote();
}
function getRoundData(uint80 /* _roundId */) external view override returns (
uint80 /* roundId */,
int256 /* answer */,
uint256 /* startedAt */,
uint256 /* updatedAt */,
uint80 /* answeredInRound */
) {
revert();
}
function getAnswer(uint256 /* roundId */) external view override returns (int256) { revert(); }
function getTimestamp(uint256 /* roundId */) external view override returns (uint256) { revert(); }
}
// SPDX-License-Identifier: BSUL-1.1
pragma solidity >=0.7.6;
/// @title All shared constants for the Notional system should be declared here.
library Constants {
uint8 internal constant CETH_DECIMAL_PLACES = 8;
// Token precision used for all internal balances, TokenHandler library ensures that we
// limit the dust amount caused by precision mismatches
int256 internal constant INTERNAL_TOKEN_PRECISION = 1e8;
uint256 internal constant INCENTIVE_ACCUMULATION_PRECISION = 1e18;
// ETH will be initialized as the first currency
uint256 internal constant ETH_CURRENCY_ID = 1;
uint8 internal constant ETH_DECIMAL_PLACES = 18;
int256 internal constant ETH_DECIMALS = 1e18;
address internal constant ETH_ADDRESS = address(0);
// Used to prevent overflow when converting decimal places to decimal precision values via
// 10**decimalPlaces. This is a safe value for int256 and uint256 variables. We apply this
// constraint when storing decimal places in governance.
uint256 internal constant MAX_DECIMAL_PLACES = 36;
// Address of the account where fees are collected
address internal constant FEE_RESERVE = 0x0000000000000000000000000000000000000FEE;
// Address of the account where settlement funds are collected, this is only
// used for off chain event tracking.
address internal constant SETTLEMENT_RESERVE = 0x00000000000000000000000000000000000005e7;
// Most significant bit
bytes32 internal constant MSB =
0x8000000000000000000000000000000000000000000000000000000000000000;
// Each bit set in this mask marks where an active market should be in the bitmap
// if the first bit refers to the reference time. Used to detect idiosyncratic
// fcash in the nToken accounts
bytes32 internal constant ACTIVE_MARKETS_MASK = (
MSB >> ( 90 - 1) | // 3 month
MSB >> (105 - 1) | // 6 month
MSB >> (135 - 1) | // 1 year
MSB >> (147 - 1) | // 2 year
MSB >> (183 - 1) | // 5 year
MSB >> (211 - 1) | // 10 year
MSB >> (251 - 1) // 20 year
);
// Basis for percentages
int256 internal constant PERCENTAGE_DECIMALS = 100;
// Min Buffer Scale and Buffer Scale are used in ExchangeRate to increase the maximum
// possible buffer values at the higher end of the uint8 range.
int256 internal constant MIN_BUFFER_SCALE = 150;
int256 internal constant BUFFER_SCALE = 10;
// Max number of traded markets, also used as the maximum number of assets in a portfolio array
uint256 internal constant MAX_TRADED_MARKET_INDEX = 7;
// Max number of fCash assets in a bitmap, this is based on the gas costs of calculating free collateral
// for a bitmap portfolio
uint256 internal constant MAX_BITMAP_ASSETS = 20;
uint256 internal constant FIVE_MINUTES = 300;
// Internal date representations, note we use a 6/30/360 week/month/year convention here
uint256 internal constant DAY = 86400;
// We use six day weeks to ensure that all time references divide evenly
uint256 internal constant WEEK = DAY * 6;
uint256 internal constant MONTH = WEEK * 5;
uint256 internal constant QUARTER = MONTH * 3;
uint256 internal constant YEAR = QUARTER * 4;
// These constants are used in DateTime.sol
uint256 internal constant DAYS_IN_WEEK = 6;
uint256 internal constant DAYS_IN_MONTH = 30;
uint256 internal constant DAYS_IN_QUARTER = 90;
// Offsets for each time chunk denominated in days
uint256 internal constant MAX_DAY_OFFSET = 90;
uint256 internal constant MAX_WEEK_OFFSET = 360;
uint256 internal constant MAX_MONTH_OFFSET = 2160;
uint256 internal constant MAX_QUARTER_OFFSET = 7650;
// Offsets for each time chunk denominated in bits
uint256 internal constant WEEK_BIT_OFFSET = 90;
uint256 internal constant MONTH_BIT_OFFSET = 135;
uint256 internal constant QUARTER_BIT_OFFSET = 195;
// Number of decimal places that rates are stored in, equals 100%
int256 internal constant RATE_PRECISION = 1e9;
// Used for prime cash scalars
uint256 internal constant SCALAR_PRECISION = 1e18;
// Used in prime rate lib
int256 internal constant DOUBLE_SCALAR_PRECISION = 1e36;
// One basis point in RATE_PRECISION terms
uint256 internal constant BASIS_POINT = uint256(RATE_PRECISION / 10000);
// Used to when calculating the amount to deleverage of a market when minting nTokens
uint256 internal constant DELEVERAGE_BUFFER = 300 * BASIS_POINT;
// Used for scaling cash group factors
uint256 internal constant FIVE_BASIS_POINTS = 5 * BASIS_POINT;
// Used for residual purchase incentive and cash withholding buffer
uint256 internal constant TEN_BASIS_POINTS = 10 * BASIS_POINT;
// Used for max oracle rate
uint256 internal constant FIFTEEN_BASIS_POINTS = 15 * BASIS_POINT;
// Used in max rate calculations
uint256 internal constant MAX_LOWER_INCREMENT = 150;
uint256 internal constant MAX_LOWER_INCREMENT_VALUE = 150 * 25 * BASIS_POINT;
uint256 internal constant TWENTY_FIVE_BASIS_POINTS = 25 * BASIS_POINT;
uint256 internal constant ONE_HUNDRED_FIFTY_BASIS_POINTS = 150 * BASIS_POINT;
// This is the ABDK64x64 representation of RATE_PRECISION
// RATE_PRECISION_64x64 = ABDKMath64x64.fromUint(RATE_PRECISION)
int128 internal constant RATE_PRECISION_64x64 = 0x3b9aca000000000000000000;
uint8 internal constant FCASH_ASSET_TYPE = 1;
// Liquidity token asset types are 1 + marketIndex (where marketIndex is 1-indexed)
uint8 internal constant MIN_LIQUIDITY_TOKEN_INDEX = 2;
uint8 internal constant MAX_LIQUIDITY_TOKEN_INDEX = 8;
uint8 internal constant VAULT_SHARE_ASSET_TYPE = 9;
uint8 internal constant VAULT_DEBT_ASSET_TYPE = 10;
uint8 internal constant VAULT_CASH_ASSET_TYPE = 11;
// Used for tracking legacy nToken assets
uint8 internal constant LEGACY_NTOKEN_ASSET_TYPE = 12;
// Account context flags
bytes1 internal constant HAS_ASSET_DEBT = 0x01;
bytes1 internal constant HAS_CASH_DEBT = 0x02;
bytes2 internal constant ACTIVE_IN_PORTFOLIO = 0x8000;
bytes2 internal constant ACTIVE_IN_BALANCES = 0x4000;
bytes2 internal constant UNMASK_FLAGS = 0x3FFF;
uint16 internal constant MAX_CURRENCIES = uint16(UNMASK_FLAGS);
// Equal to 100% of all deposit amounts for nToken liquidity across fCash markets.
int256 internal constant DEPOSIT_PERCENT_BASIS = 1e8;
// nToken Parameters: there are offsets in the nTokenParameters bytes6 variable returned
// in nTokenHandler. Each constant represents a position in the byte array.
uint8 internal constant LIQUIDATION_HAIRCUT_PERCENTAGE = 0;
uint8 internal constant CASH_WITHHOLDING_BUFFER = 1;
uint8 internal constant RESIDUAL_PURCHASE_TIME_BUFFER = 2;
uint8 internal constant PV_HAIRCUT_PERCENTAGE = 3;
uint8 internal constant RESIDUAL_PURCHASE_INCENTIVE = 4;
uint8 internal constant MAX_MINT_DEVIATION_LIMIT = 5;
// Liquidation parameters
// Default percentage of collateral that a liquidator is allowed to liquidate, will be higher if the account
// requires more collateral to be liquidated
int256 internal constant DEFAULT_LIQUIDATION_PORTION = 40;
// Percentage of local liquidity token cash claim delivered to the liquidator for liquidating liquidity tokens
int256 internal constant TOKEN_REPO_INCENTIVE_PERCENT = 30;
// Pause Router liquidation enabled states
bytes1 internal constant LOCAL_CURRENCY_ENABLED = 0x01;
bytes1 internal constant COLLATERAL_CURRENCY_ENABLED = 0x02;
bytes1 internal constant LOCAL_FCASH_ENABLED = 0x04;
bytes1 internal constant CROSS_CURRENCY_FCASH_ENABLED = 0x08;
// Requires vault accounts to enter a position for a minimum of 1 min
// to mitigate strange behavior where accounts may enter and exit using
// flash loans or other MEV type behavior.
uint256 internal constant VAULT_ACCOUNT_MIN_TIME = 1 minutes;
// Placeholder constant to mark the variable rate prime cash maturity
uint40 internal constant PRIME_CASH_VAULT_MATURITY = type(uint40).max;
// This represents the maximum percent change allowed before and after
// a rebalancing. 100_000 represents a 0.01% change
// as a result of rebalancing. We should expect to never lose value as
// a result of rebalancing, but some rounding errors may exist as a result
// of redemption and deposit.
int256 internal constant REBALANCING_UNDERLYING_DELTA_PERCENT = 100_000;
// Ensures that the minimum total underlying held by the contract continues
// to accrue interest so that money market oracle rates are properly updated
// between rebalancing. With a minimum rebalancing cool down time of 6 hours
// we would be able to detect at least 1 unit of accrual at 8 decimal precision
// at an interest rate of 2.8 basis points (0.0288%) with 0.05e8 minimum balance
// held in a given token.
//
// MIN_ACCRUAL * (86400 / REBALANCING_COOL_DOWN_HOURS)
// MINIMUM_INTEREST_RATE = ---------------------------------------------------
// MINIMUM_UNDERLYING_BALANCE
int256 internal constant MIN_TOTAL_UNDERLYING_VALUE = 0.05e8;
}
// SPDX-License-Identifier: BSUL-1.1
pragma solidity =0.7.6;
import {Constants} from "../global/Constants.sol";
library SafeInt256 {
int256 private constant _INT256_MIN = type(int256).min;
/// @dev Returns the multiplication of two signed integers, reverting on
/// overflow.
/// Counterpart to Solidity's `*` operator.
/// Requirements:
/// - Multiplication cannot overflow.
function mul(int256 a, int256 b) internal pure returns (int256 c) {
c = a * b;
if (a == -1) require (b == 0 || c / b == a);
else require (a == 0 || c / a == b);
}
/// @dev Returns the integer division of two signed integers. Reverts on
/// division by zero. The result is rounded towards zero.
/// Counterpart to Solidity's `/` operator. Note: this function uses a
/// `revert` opcode (which leaves remaining gas untouched) while Solidity
/// uses an invalid opcode to revert (consuming all remaining gas).
/// Requirements:
/// - The divisor cannot be zero.
function div(int256 a, int256 b) internal pure returns (int256 c) {
require(!(b == -1 && a == _INT256_MIN)); // dev: int256 div overflow
// NOTE: solidity will automatically revert on divide by zero
c = a / b;
}
function sub(int256 x, int256 y) internal pure returns (int256 z) {
// taken from uniswap v3
require((z = x - y) <= x == (y >= 0));
}
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
function neg(int256 x) internal pure returns (int256 y) {
return mul(-1, x);
}
function abs(int256 x) internal pure returns (int256) {
if (x < 0) return neg(x);
else return x;
}
function subNoNeg(int256 x, int256 y) internal pure returns (int256 z) {
z = sub(x, y);
require(z >= 0); // dev: int256 sub to negative
return z;
}
/// @dev Calculates x * RATE_PRECISION / y while checking overflows
function divInRatePrecision(int256 x, int256 y) internal pure returns (int256) {
return div(mul(x, Constants.RATE_PRECISION), y);
}
/// @dev Calculates x * y / RATE_PRECISION while checking overflows
function mulInRatePrecision(int256 x, int256 y) internal pure returns (int256) {
return div(mul(x, y), Constants.RATE_PRECISION);
}
function toUint(int256 x) internal pure returns (uint256) {
require(x >= 0);
return uint256(x);
}
function toInt(uint256 x) internal pure returns (int256) {
require (x <= uint256(type(int256).max)); // dev: toInt overflow
return int256(x);
}
function toInt80(int256 x) internal pure returns (int80) {
require (int256(type(int80).min) <= x && x <= int256(type(int80).max)); // dev: toInt overflow
return int80(x);
}
function toInt88(int256 x) internal pure returns (int88) {
require (int256(type(int88).min) <= x && x <= int256(type(int88).max)); // dev: toInt overflow
return int88(x);
}
function toInt128(int256 x) internal pure returns (int128) {
require (int256(type(int128).min) <= x && x <= int256(type(int128).max)); // dev: toInt overflow
return int128(x);
}
function max(int256 x, int256 y) internal pure returns (int256) {
return x > y ? x : y;
}
function min(int256 x, int256 y) internal pure returns (int256) {
return x < y ? x : y;
}
/// @notice Returns the net change in negative signed values, used for
/// determining the (positive) amount of debt change
function negChange(int256 start, int256 end) internal pure returns (int256) {
// No change in these two scenarios
if (start == end || (start >= 0 && end >= 0)) return 0;
if (start <= 0 && 0 < end) {
// Negative portion has been eliminated so the net change on the
// negative side is start (i.e. a reduction in the negative balance)
return start;
} else if (end <= 0 && 0 < start) {
// Entire negative portion has been created so the net change on the
// negative side is -end (i.e. an increase in the negative balance)
return neg(end);
} else if (start <= 0 && end <= 0) {
// There is some net change in the negative amounts.
// If start < end then this is negative, debt has been reduced
// If end < start then this is positive, debt has been increased
return sub(start, end);
}
// Should never get to this point
revert();
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;
import "./AggregatorInterface.sol";
import "./AggregatorV3Interface.sol";
interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
{
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;
interface AggregatorInterface {
function latestAnswer() external view returns (int256);
function latestTimestamp() external view returns (uint256);
function latestRound() external view returns (uint256);
function getAnswer(uint256 roundId) external view returns (int256);
function getTimestamp(uint256 roundId) external view returns (uint256);
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
// getRoundData and latestRoundData should both raise "No data present"
// if they do not have data to report, instead of returning unset values
// which could be misinterpreted as actual reported values.
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}