Contract Name:
InterestRate
Contract Source Code:
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IYieldOracle } from "./interfaces/IYieldOracle.sol";
import { WadRayMath } from "./libraries/math/WadRayMath.sol";
// forgefmt: disable-start
struct IlkData {
// Word 1
uint96 adjustedProfitMargin; // 27 decimals
uint96 minimumKinkRate; // 27 decimals
// Word 2
uint16 reserveFactor; // 4 decimals
uint96 adjustedBaseRate; // 27 decimals
uint96 minimumBaseRate; // 27 decimals
uint16 optimalUtilizationRate; // 4 decimals
uint16 distributionFactor; // 4 decimals
// Word 3
uint96 adjustedAboveKinkSlope; // 27 decimals
uint96 minimumAboveKinkSlope; // 27 decimals
}
// Word 1
//
// 256 240 216 192 96 0
// | | | | min_kink_rate | adj_profit_margin |
//
uint256 constant ADJUSTED_PROFIT_MARGIN_MASK = 0x0000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
uint256 constant MINIMUM_KINK_RATE_MASK = 0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000;
// Word 2
//
// 256 240 224 208 112 16 0
// | __ | | | min_base_rate | adj_base_rate | |
// ^ ^ ^
// ^ opt_util reserve_factor
// distribution_factor
uint256 constant RESERVE_FACTOR_MASK = 0x000000000000000000000000000000000000000000000000000000000000FFFF;
uint256 constant ADJUSTED_BASE_RATE_MASK = 0x000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF0000;
uint256 constant MINIMUM_BASE_RATE_MASK = 0x000000000000FFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000;
uint256 constant OPTIMAL_UTILIZATION_MASK = 0x00000000FFFF0000000000000000000000000000000000000000000000000000;
uint256 constant DISTRIBUTION_FACTOR_MASK = 0x0000FFFF00000000000000000000000000000000000000000000000000000000;
// Word 3
// 256 240 216 192 96 0
// | | | | min_above_kink_slope | adj_above_kink_slope |
//
uint256 constant ADJUSTED_ABOVE_KINK_SLOPE_MASK = 0x0000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
uint256 constant MINIMUM_ABOVE_KINK_SLOPE_MASK = 0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000;
// forgefmt: disable-end
// Word 1
uint8 constant ADJUSTED_PROFIT_MARGIN_SHIFT = 0;
uint8 constant MINIMUM_KINK_RATE_SHIFT = 96;
// Word 2
uint8 constant RESERVE_FACTOR_SHIFT = 0;
uint8 constant ADJUSTED_BASE_RATE_SHIFT = 16;
uint8 constant MINIMUM_BASE_RATE_SHIFT = 16 + 96;
uint8 constant OPTIMAL_UTILIZATION_SHIFT = 16 + 96 + 96;
uint8 constant DISTRIBUTION_FACTOR_SHIFT = 16 + 96 + 96 + 16;
// Word 3
uint8 constant ADJUSTED_ABOVE_KINK_SLOPE_SHIFT = 0;
uint8 constant MINIMUM_ABOVE_KINK_SLOPE_SHIFT = 96;
uint48 constant SECONDS_IN_A_YEAR = 31_536_000;
/**
* @notice An external contract that provides the APY for each collateral type.
* A modular design here allows for updating of the parameters at a later date
* without upgrading the core protocol.
*
* @dev Each collateral has its own interest rate model, and every operation on
* the `IonPool` (lend, withdraw, borrow, repay) will alter the interest rate
* for all collaterals. Therefore, before every operation, the previous interest
* rate must be accrued. Ion determines the interest rate for each collateral
* based on various collateral-specific parameters which must be stored
* on-chain. However, to iterate through all these parameters as contract
* storage on every operation introduces an immense gas overhead, especially as
* more collaterals are listed on Ion. Therefore, this contract is heavily
* optimized to reduce storage reads at the unfortunate cost of code-complexity.
*
* @custom:security-contact [email protected]
*/
contract InterestRate {
using WadRayMath for *;
error CollateralIndexOutOfBounds();
error DistributionFactorsDoNotSumToOne(uint256 sum);
error TotalDebtsLength(uint256 COLLATERAL_COUNT, uint256 totalIlkDebtsLength);
error InvalidMinimumKinkRate(uint256 minimumKinkRate, uint256 minimumBaseRate);
error InvalidIlkDataListLength(uint256 length);
error InvalidOptimalUtilizationRate(uint256 optimalUtilizationRate);
error InvalidReserveFactor(uint256 reserveFactor);
error InvalidYieldOracleAddress();
uint256 private constant MAX_ILKS = 8;
/**
* @dev Packed collateral configs
*/
uint256 private immutable ILKCONFIG_0A;
uint256 private immutable ILKCONFIG_0B;
uint256 private immutable ILKCONFIG_0C;
uint256 private immutable ILKCONFIG_1A;
uint256 private immutable ILKCONFIG_1B;
uint256 private immutable ILKCONFIG_1C;
uint256 private immutable ILKCONFIG_2A;
uint256 private immutable ILKCONFIG_2B;
uint256 private immutable ILKCONFIG_2C;
uint256 private immutable ILKCONFIG_3A;
uint256 private immutable ILKCONFIG_3B;
uint256 private immutable ILKCONFIG_3C;
uint256 private immutable ILKCONFIG_4A;
uint256 private immutable ILKCONFIG_4B;
uint256 private immutable ILKCONFIG_4C;
uint256 private immutable ILKCONFIG_5A;
uint256 private immutable ILKCONFIG_5B;
uint256 private immutable ILKCONFIG_5C;
uint256 private immutable ILKCONFIG_6A;
uint256 private immutable ILKCONFIG_6B;
uint256 private immutable ILKCONFIG_6C;
uint256 private immutable ILKCONFIG_7A;
uint256 private immutable ILKCONFIG_7B;
uint256 private immutable ILKCONFIG_7C;
uint256 public immutable COLLATERAL_COUNT;
IYieldOracle public immutable YIELD_ORACLE;
/**
* @notice Creates a new `InterestRate` instance.
* @param ilkDataList List of ilk configs.
* @param _yieldOracle Address of the Yield oracle.
*/
constructor(IlkData[] memory ilkDataList, IYieldOracle _yieldOracle) {
if (address(_yieldOracle) == address(0)) revert InvalidYieldOracleAddress();
if (ilkDataList.length > MAX_ILKS) revert InvalidIlkDataListLength(ilkDataList.length);
COLLATERAL_COUNT = ilkDataList.length;
YIELD_ORACLE = _yieldOracle;
uint256 distributionFactorSum = 0;
for (uint256 i = 0; i < COLLATERAL_COUNT;) {
distributionFactorSum += ilkDataList[i].distributionFactor;
if (ilkDataList[i].minimumKinkRate < ilkDataList[i].minimumBaseRate) {
revert InvalidMinimumKinkRate(ilkDataList[i].minimumKinkRate, ilkDataList[i].minimumBaseRate);
}
if (ilkDataList[i].optimalUtilizationRate == 0) {
revert InvalidOptimalUtilizationRate(ilkDataList[i].optimalUtilizationRate);
}
if (ilkDataList[i].reserveFactor > 1e4) {
revert InvalidReserveFactor(ilkDataList[i].reserveFactor);
}
// forgefmt: disable-next-line
unchecked { ++i; }
}
if (distributionFactorSum != 1e4) revert DistributionFactorsDoNotSumToOne(distributionFactorSum);
(ILKCONFIG_0A, ILKCONFIG_0B, ILKCONFIG_0C) = _packCollateralConfig(ilkDataList, 0);
(ILKCONFIG_1A, ILKCONFIG_1B, ILKCONFIG_1C) = _packCollateralConfig(ilkDataList, 1);
(ILKCONFIG_2A, ILKCONFIG_2B, ILKCONFIG_2C) = _packCollateralConfig(ilkDataList, 2);
(ILKCONFIG_3A, ILKCONFIG_3B, ILKCONFIG_3C) = _packCollateralConfig(ilkDataList, 3);
(ILKCONFIG_4A, ILKCONFIG_4B, ILKCONFIG_4C) = _packCollateralConfig(ilkDataList, 4);
(ILKCONFIG_5A, ILKCONFIG_5B, ILKCONFIG_5C) = _packCollateralConfig(ilkDataList, 5);
(ILKCONFIG_6A, ILKCONFIG_6B, ILKCONFIG_6C) = _packCollateralConfig(ilkDataList, 6);
(ILKCONFIG_7A, ILKCONFIG_7B, ILKCONFIG_7C) = _packCollateralConfig(ilkDataList, 7);
}
/**
* @notice Helper function to pack the collateral configs into 3 words. This
* function is only called during construction.
* @param ilkDataList The list of ilk configs.
* @param index The ilkIndex to pack.
* @return packedConfig_a
* @return packedConfig_b
* @return packedConfig_c
*/
function _packCollateralConfig(
IlkData[] memory ilkDataList,
uint256 index
)
private
view
returns (uint256 packedConfig_a, uint256 packedConfig_b, uint256 packedConfig_c)
{
if (index >= COLLATERAL_COUNT) return (0, 0, 0);
IlkData memory ilkData = ilkDataList[index];
packedConfig_a = (
uint256(ilkData.adjustedProfitMargin) << ADJUSTED_PROFIT_MARGIN_SHIFT
| uint256(ilkData.minimumKinkRate) << MINIMUM_KINK_RATE_SHIFT
);
packedConfig_b = (
uint256(ilkData.reserveFactor) << RESERVE_FACTOR_SHIFT
| uint256(ilkData.adjustedBaseRate) << ADJUSTED_BASE_RATE_SHIFT
| uint256(ilkData.minimumBaseRate) << MINIMUM_BASE_RATE_SHIFT
| uint256(ilkData.optimalUtilizationRate) << OPTIMAL_UTILIZATION_SHIFT
| uint256(ilkData.distributionFactor) << DISTRIBUTION_FACTOR_SHIFT
);
packedConfig_c = (
uint256(ilkData.adjustedAboveKinkSlope) << ADJUSTED_ABOVE_KINK_SLOPE_SHIFT
| uint256(ilkData.minimumAboveKinkSlope) << MINIMUM_ABOVE_KINK_SLOPE_SHIFT
);
}
/**
* @notice Helper function to unpack the collateral configs from the 3
* words.
* @param index The ilkIndex to unpack.
* @return ilkData The unpacked collateral config.
*/
function unpackCollateralConfig(uint256 index) external view returns (IlkData memory ilkData) {
return _unpackCollateralConfig(index);
}
function _unpackCollateralConfig(uint256 index) internal view returns (IlkData memory ilkData) {
if (index > COLLATERAL_COUNT - 1) revert CollateralIndexOutOfBounds();
uint256 packedConfig_a;
uint256 packedConfig_b;
uint256 packedConfig_c;
if (index == 0) {
packedConfig_a = ILKCONFIG_0A;
packedConfig_b = ILKCONFIG_0B;
packedConfig_c = ILKCONFIG_0C;
} else if (index == 1) {
packedConfig_a = ILKCONFIG_1A;
packedConfig_b = ILKCONFIG_1B;
packedConfig_c = ILKCONFIG_1C;
} else if (index == 2) {
packedConfig_a = ILKCONFIG_2A;
packedConfig_b = ILKCONFIG_2B;
packedConfig_c = ILKCONFIG_2C;
} else if (index == 3) {
packedConfig_a = ILKCONFIG_3A;
packedConfig_b = ILKCONFIG_3B;
packedConfig_c = ILKCONFIG_3C;
} else if (index == 4) {
packedConfig_a = ILKCONFIG_4A;
packedConfig_b = ILKCONFIG_4B;
packedConfig_c = ILKCONFIG_4C;
} else if (index == 5) {
packedConfig_a = ILKCONFIG_5A;
packedConfig_b = ILKCONFIG_5B;
packedConfig_c = ILKCONFIG_5C;
} else if (index == 6) {
packedConfig_a = ILKCONFIG_6A;
packedConfig_b = ILKCONFIG_6B;
packedConfig_c = ILKCONFIG_6C;
} else if (index == 7) {
packedConfig_a = ILKCONFIG_7A;
packedConfig_b = ILKCONFIG_7B;
packedConfig_c = ILKCONFIG_7C;
}
uint96 adjustedProfitMargin =
uint96((packedConfig_a & ADJUSTED_PROFIT_MARGIN_MASK) >> ADJUSTED_PROFIT_MARGIN_SHIFT);
uint96 minimumKinkRate = uint96((packedConfig_a & MINIMUM_KINK_RATE_MASK) >> MINIMUM_KINK_RATE_SHIFT);
uint16 reserveFactor = uint16((packedConfig_b & RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_SHIFT);
uint96 adjustedBaseRate = uint96((packedConfig_b & ADJUSTED_BASE_RATE_MASK) >> ADJUSTED_BASE_RATE_SHIFT);
uint96 minimumBaseRate = uint96((packedConfig_b & MINIMUM_BASE_RATE_MASK) >> MINIMUM_BASE_RATE_SHIFT);
uint16 optimalUtilizationRate = uint16((packedConfig_b & OPTIMAL_UTILIZATION_MASK) >> OPTIMAL_UTILIZATION_SHIFT);
uint16 distributionFactor = uint16((packedConfig_b & DISTRIBUTION_FACTOR_MASK) >> DISTRIBUTION_FACTOR_SHIFT);
uint96 adjustedAboveKinkSlope =
uint96((packedConfig_c & ADJUSTED_ABOVE_KINK_SLOPE_MASK) >> ADJUSTED_ABOVE_KINK_SLOPE_SHIFT);
uint96 minimumAboveKinkSlope =
uint96((packedConfig_c & MINIMUM_ABOVE_KINK_SLOPE_MASK) >> MINIMUM_ABOVE_KINK_SLOPE_SHIFT);
ilkData = IlkData({
adjustedProfitMargin: adjustedProfitMargin,
minimumKinkRate: minimumKinkRate,
reserveFactor: reserveFactor,
adjustedBaseRate: adjustedBaseRate,
minimumBaseRate: minimumBaseRate,
optimalUtilizationRate: optimalUtilizationRate,
distributionFactor: distributionFactor,
adjustedAboveKinkSlope: adjustedAboveKinkSlope,
minimumAboveKinkSlope: minimumAboveKinkSlope
});
}
/**
* @notice Calculates the interest rate for a given collateral.
* @param ilkIndex Index of the collateral.
* @param totalIlkDebt Total debt of the collateral. [RAD]
* @param totalEthSupply Total eth supply of the system. [WAD]
* @return The borrow rate for the collateral. [RAY]
* @return The reserve factor for the collateral. [RAY]
*/
function calculateInterestRate(
uint256 ilkIndex,
uint256 totalIlkDebt,
uint256 totalEthSupply
)
external
view
returns (uint256, uint256)
{
IlkData memory ilkData = _unpackCollateralConfig(ilkIndex);
uint256 optimalUtilizationRateRay = ilkData.optimalUtilizationRate.scaleUpToRay(4);
uint256 collateralApyRayInSeconds = YIELD_ORACLE.apys(ilkIndex).scaleUpToRay(8) / SECONDS_IN_A_YEAR;
uint256 distributionFactor = ilkData.distributionFactor;
// The only time the distribution factor will be set to 0 is when a
// market has been sunset. In this case, we want to prevent division by
// 0, but we also want to prevent the borrow rate from skyrocketing. So
// we will return a reasonable borrow rate of kink utilization on the
// minimum curve.
if (distributionFactor == 0) {
return (ilkData.minimumKinkRate, ilkData.reserveFactor.scaleUpToRay(4));
}
// [RAD] / [WAD] = [RAY]
uint256 utilizationRate =
totalEthSupply == 0 ? 0 : totalIlkDebt / (totalEthSupply.wadMulDown(distributionFactor.scaleUpToWad(4)));
// Avoid stack too deep
uint256 adjustedBelowKinkSlope;
{
uint256 slopeNumerator;
unchecked {
slopeNumerator = collateralApyRayInSeconds - ilkData.adjustedProfitMargin - ilkData.adjustedBaseRate;
}
// Underflow occurred
// If underflow occurred, then the Apy was too low or the profitMargin was too high and
// we would want to switch to minimum borrow rate. Set slopeNumerator to zero such
// that adjusted borrow rate is below the minimum borrow rate.
if (slopeNumerator > collateralApyRayInSeconds) {
slopeNumerator = 0;
}
adjustedBelowKinkSlope = slopeNumerator.rayDivDown(optimalUtilizationRateRay);
}
uint256 minimumBelowKinkSlope =
(ilkData.minimumKinkRate - ilkData.minimumBaseRate).rayDivDown(optimalUtilizationRateRay);
// Below kink
if (utilizationRate < optimalUtilizationRateRay) {
uint256 adjustedBorrowRate = adjustedBelowKinkSlope.rayMulDown(utilizationRate) + ilkData.adjustedBaseRate;
uint256 minimumBorrowRate = minimumBelowKinkSlope.rayMulDown(utilizationRate) + ilkData.minimumBaseRate;
if (adjustedBorrowRate < minimumBorrowRate) {
return (minimumBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));
} else {
return (adjustedBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));
}
}
// Above kink
else {
// For the above kink calculation, we will use the below kink slope
// for all utilization up until the kink. From that point on we will
// use the above kink slope.
uint256 excessUtil = utilizationRate - optimalUtilizationRateRay;
uint256 adjustedNormalRate =
adjustedBelowKinkSlope.rayMulDown(optimalUtilizationRateRay) + ilkData.adjustedBaseRate;
uint256 minimumNormalRate =
minimumBelowKinkSlope.rayMulDown(optimalUtilizationRateRay) + ilkData.minimumBaseRate;
// [WAD] * [RAY] / [WAD] = [RAY]
uint256 adjustedBorrowRate = ilkData.adjustedAboveKinkSlope.rayMulDown(excessUtil) + adjustedNormalRate;
uint256 minimumBorrowRate = ilkData.minimumAboveKinkSlope.rayMulDown(excessUtil) + minimumNormalRate;
if (adjustedBorrowRate < minimumBorrowRate) {
return (minimumBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));
} else {
return (adjustedBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
interface IYieldOracle {
function apys(uint256 ilkIndex) external view returns (uint32);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
uint256 constant WAD = 1e18;
uint256 constant RAY = 1e27;
uint256 constant RAD = 1e45;
/**
* @title WadRayMath
*
* @notice This library provides mul/div[up/down] functionality for WAD, RAY and
* RAD with phantom overflow protection as well as scale[up/down] functionality
* for WAD, RAY and RAD.
*
* @custom:security-contact [email protected]
*/
library WadRayMath {
using Math for uint256;
error NotScalingUp(uint256 from, uint256 to);
error NotScalingDown(uint256 from, uint256 to);
/**
* @notice Multiplies two WAD numbers and returns the result as a WAD
* rounding the result down.
* @param a Multiplicand.
* @param b Multiplier.
*/
function wadMulDown(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(b, WAD);
}
/**
* @notice Multiplies two WAD numbers and returns the result as a WAD
* rounding the result up.
* @param a Multiplicand.
* @param b Multiplier.
*/
function wadMulUp(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(b, WAD, Math.Rounding.Ceil);
}
/**
* @notice Divides two WAD numbers and returns the result as a WAD rounding
* the result down.
* @param a Dividend.
* @param b Divisor.
*/
function wadDivDown(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(WAD, b);
}
/**
* @notice Divides two WAD numbers and returns the result as a WAD rounding
* the result up.
* @param a Dividend.
* @param b Divisor.
*/
function wadDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(WAD, b, Math.Rounding.Ceil);
}
/**
* @notice Multiplies two RAY numbers and returns the result as a RAY
* rounding the result down.
* @param a Multiplicand
* @param b Multiplier
*/
function rayMulDown(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(b, RAY);
}
/**
* @notice Multiplies two RAY numbers and returns the result as a RAY
* rounding the result up.
* @param a Multiplicand
* @param b Multiplier
*/
function rayMulUp(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(b, RAY, Math.Rounding.Ceil);
}
/**
* @notice Divides two RAY numbers and returns the result as a RAY
* rounding the result down.
* @param a Dividend
* @param b Divisor
*/
function rayDivDown(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(RAY, b);
}
/**
* @notice Divides two RAY numbers and returns the result as a RAY
* rounding the result up.
* @param a Dividend
* @param b Divisor
*/
function rayDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(RAY, b, Math.Rounding.Ceil);
}
/**
* @notice Multiplies two RAD numbers and returns the result as a RAD
* rounding the result down.
* @param a Multiplicand
* @param b Multiplier
*/
function radMulDown(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(b, RAD);
}
/**
* @notice Multiplies two RAD numbers and returns the result as a RAD
* rounding the result up.
* @param a Multiplicand
* @param b Multiplier
*/
function radMulUp(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(b, RAD, Math.Rounding.Ceil);
}
/**
* @notice Divides two RAD numbers and returns the result as a RAD rounding
* the result down.
* @param a Dividend
* @param b Divisor
*/
function radDivDown(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(RAD, b);
}
/**
* @notice Divides two RAD numbers and returns the result as a RAD rounding
* the result up.
* @param a Dividend
* @param b Divisor
*/
function radDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
return a.mulDiv(RAD, b, Math.Rounding.Ceil);
}
// --- Scalers ---
/**
* @notice Scales a value up from WAD. NOTE: The `scale` value must be
* less than 18.
* @param value to scale up.
* @param scale of the returned value.
*/
function scaleUpToWad(uint256 value, uint256 scale) internal pure returns (uint256) {
return scaleUp(value, scale, 18);
}
/**
* @notice Scales a value up from RAY. NOTE: The `scale` value must be
* less than 27.
* @param value to scale up.
* @param scale of the returned value.
*/
function scaleUpToRay(uint256 value, uint256 scale) internal pure returns (uint256) {
return scaleUp(value, scale, 27);
}
/**
* @notice Scales a value up from RAD. NOTE: The `scale` value must be
* less than 45.
* @param value to scale up.
* @param scale of the returned value.
*/
function scaleUpToRad(uint256 value, uint256 scale) internal pure returns (uint256) {
return scaleUp(value, scale, 45);
}
/**
* @notice Scales a value down to WAD. NOTE: The `scale` value must be
* greater than 18.
* @param value to scale down.
* @param scale of the returned value.
*/
function scaleDownToWad(uint256 value, uint256 scale) internal pure returns (uint256) {
return scaleDown(value, scale, 18);
}
/**
* @notice Scales a value down to RAY. NOTE: The `scale` value must be
* greater than 27.
* @param value to scale down.
* @param scale of the returned value.
*/
function scaleDownToRay(uint256 value, uint256 scale) internal pure returns (uint256) {
return scaleDown(value, scale, 27);
}
/**
* @notice Scales a value down to RAD. NOTE: The `scale` value must be
* greater than 45.
* @param value to scale down.
* @param scale of the returned value.
*/
function scaleDownToRad(uint256 value, uint256 scale) internal pure returns (uint256) {
return scaleDown(value, scale, 45);
}
/**
* @notice Scales a value up from one fixed-point precision to another.
* @param value to scale up.
* @param from Precision to scale from.
* @param to Precision to scale to.
*/
function scaleUp(uint256 value, uint256 from, uint256 to) internal pure returns (uint256) {
if (from >= to) revert NotScalingUp(from, to);
return value * (10 ** (to - from));
}
/**
* @notice Scales a value down from one fixed-point precision to another.
* @param value to scale down.
* @param from Precision to scale from.
* @param to Precision to scale to.
*/
function scaleDown(uint256 value, uint256 from, uint256 to) internal pure returns (uint256) {
if (from <= to) revert NotScalingDown(from, to);
return value / (10 ** (from - to));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}