InterestRate
Title: InterestRate
Author: Modified from Compound (https://github.com/compound-finance/compound-protocol/blob/master/contracts/JumpRateModelV2.sol)
Stateless utility library implementing the kinked (Aave/Compound-style) interest rate model.
No own ERC-7201 storage. The consumer module holds any storage that references this library. All rates are ray-scaled (1e27 = 100%).
Constants
RAY
Ray scaling factor (1e27 = 100%).
uint256 internal constant RAY = 1e27;Functions
getBorrowRate
Computes the annualized borrow rate given current utilization.
Uses a two-slope (kinked) model:
- Below kink:
baseRate + utilization * slope1 / kink - At or above kink:
baseRate + slope1 + (utilization - kink) * slope2 / (RAY - kink)Utilization is capped at RAY (100%) on entry; values above RAY are physically impossible and would cause division-by-zero whenkink == RAY(remainingRange == 0). When kink == RAY, capping util to RAY meansutil <= kinkis always true, so the second slope never applies and the denominator is never zero.
function getBorrowRate(Config memory config, uint256 util) internal pure returns (uint256 borrowRate);Parameters
| Name | Type | Description |
|---|---|---|
| config | Config | Rate model parameters. |
| util | uint256 | Borrow/supply ratio, ray-scaled (1e27 = 100%). Values above RAY are capped to RAY. |
Returns
| Name | Type | Description |
|---|---|---|
| borrowRate | uint256 | Annualized borrow rate, ray-scaled. |
getSupplyRate
Computes the annualized supply rate given utilization and reserve factor.
Formula: borrowRate * utilization * (RAY - reserveFactor) / RAY / RAY.
Uses two sequential full-precision mulDiv steps to avoid intermediate overflow
without requiring 512-bit arithmetic.
function getSupplyRate(Config memory config, uint256 util, uint256 reserveFactor)
internal
pure
returns (uint256 supplyRate);Parameters
| Name | Type | Description |
|---|---|---|
| config | Config | Rate model parameters. |
| util | uint256 | Borrow/supply ratio, ray-scaled. |
| reserveFactor | uint256 | Fraction of borrow interest kept as protocol reserves, ray-scaled. Must be <= RAY (i.e., at most 100% of interest goes to reserves). |
Returns
| Name | Type | Description |
|---|---|---|
| supplyRate | uint256 | Annualized supply rate, ray-scaled. |
utilization
Computes borrow utilization as totalBorrows / totalSupply, ray-scaled.
Returns 0 if totalSupply is 0 to avoid division by zero.
Overflow note (L-4): totalBorrows * RAY overflows uint256 when
totalBorrows > type(uint256).max / 1e27 ≈ 1.16e50. This is an
astronomically large token amount (far beyond any real protocol's TVL),
but callers should ensure totalBorrows is within this bound.
If overflow-safe math is required, use mulDiv(totalBorrows, RAY, totalSupply).
function utilization(uint256 totalBorrows, uint256 totalSupply) internal pure returns (uint256 util);Parameters
| Name | Type | Description |
|---|---|---|
| totalBorrows | uint256 | Total outstanding borrows denominated in the asset. |
| totalSupply | uint256 | Total supplied assets (may include idle + allocated). |
Returns
| Name | Type | Description |
|---|---|---|
| util | uint256 | Utilization ratio, ray-scaled (1e27 = 100%). |
validateConfig
Validates that a Config is well-formed; reverts with InvalidConfig otherwise.
Checks:
- kink must be <= RAY (utilization is always in [0, RAY])
- kink must be > 0 (otherwise the first-slope formula divides by zero)
Note on overflow (L-3): baseRate, slope1, and slope2 are not bounded here.
The two-slope formula can produce a borrowRate larger than RAY for extreme configs
(e.g. slope2 >> RAY). If borrowRate > type(uint256).max / RAY, the intermediate
product in _rayMul silently overflows. Consumers MUST ensure that
baseRate + slope1 + slope2 <= type(uint256).max / RAY (~1.16e50) for safety.
Practical lending protocols use rates well below this bound (e.g., all fields <= 10*RAY).
function validateConfig(Config memory config) internal pure;Parameters
| Name | Type | Description |
|---|---|---|
| config | Config | The Config to validate. |
_rayMul
Multiplies two ray-scaled values and divides by RAY, truncating (floor). Intermediate product can reach up to (1e27)^2 = 1e54 < 2^256, so no overflow.
function _rayMul(uint256 a, uint256 b) private pure returns (uint256);Parameters
| Name | Type | Description |
|---|---|---|
| a | uint256 | |
| b | uint256 |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
Errors
InvalidConfig
Reverts when a Config is structurally invalid (e.g., kink > RAY).
error InvalidConfig();Structs
Config
Configuration for the kinked interest rate model.
struct Config {
/// @dev Annualized borrow rate at 0% utilization, ray-scaled (1e27 = 100%).
uint256 baseRate;
/// @dev Slope of the rate curve below the kink, ray-scaled.
uint256 slope1;
/// @dev Slope of the rate curve above the kink, ray-scaled.
uint256 slope2;
/// @dev Utilization breakpoint where the slope changes, ray-scaled (e.g., 0.8e27 = 80%).
uint256 kink;
}Properties
| Name | Type | Description |
|---|---|---|
| baseRate | uint256 | |
| slope1 | uint256 | |
| slope2 | uint256 | |
| kink | uint256 |