ETH Price: $2,444.69 (+1.80%)

Contract Diff Checker

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

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

Context size (optional):