Dynamic Interest Rate Model
Overview
The Dynamic Interest Rate Model (DIRM) is a sophisticated interest rate mechanism designed to efficiently balance supply and demand within Curvance lending markets. It builds upon traditional "jump rate" models while introducing dynamic elements that automatically respond to changing market conditions.
Core Components
Interest Rate Structure
The model uses two primary interest rate components:
Base Interest Rate: Applied linearly as utilization increases from 0% to the vertex point, which increases linearly until a certain utilization threshold (
vertexStartingPoint
) is reached.Vertex Interest Rate: Applied when utilization exceeds the vertex point, multiplied by the Vertex Multiplier, which adjusts more steeply based on liquidity utilization.
Vertex Starting Point: The utilization threshold where the model switches from base to vertex rate.
Dynamic Vertex Multiplier
The heart of the model is the Vertex Multiplier - a dynamic coefficient that adjusts based on market conditions:
Default Value: Starts at 1.0 (WAD).
Maximum Value: Capped at vertexMultiplierMax.
Storage Format: Packed with timestamp data in a single storage slot to minimize gas costs.

Data Flow
Market Utilization → Calculated from borrows / (underlyingHeld + borrows - reserves).
Utilization → Drives interest rate calculations through base and vertex formulas.
Interest Rates → Applied to borrowers and distributed to lenders (minus protocol fees).
Vertex Multiplier → Adjusted based on sustained market utilization patterns.
Decay Mechanism → Continuously reduces elevated multiplier values over time.
State Machine
The Vertex Multiplier operates as a state machine with the following transitions:
States and Transitions
Initialization State
Starting point: vertexMultiplier = WAD (1.0).
Next update timestamp set.
Adjustment States
Based on utilization relative to thresholds:
Below decreaseThresholdMax: Maximum negative adjustment + decay.
Below vertex but above decreaseThresholdMax: Scaled negative adjustment + decay.
Above vertex but below increaseThreshold: Only decay applied.
Above increaseThreshold: Positive adjustment + decay.
Transition Conditions
Transitions only occur when current
block.timestamp ≥ updateTimestamp
.Rate update only possible when properly linked to an eToken.
Decay Mechanism
The decay feature introduces a downward sloping characteristic:
When the multiplier is elevated, a constant negative velocity is applied.
This occurs regardless of positive/negative acceleration from utilization.
If the multiplier is elevated due to high utilization, it naturally decays over time to prevent interest rates from remaining too high indefinitely.
Similarly, if utilization is low and the multiplier has been reduced, it decays back upward over time to prevent excessively low rates.
Creates natural incentives for borrowers while protecting against liquidity crunches.

Configuration Parameters
The model is governed by several parameters that define its behavior:
Base Parameters:
baseInterestRate: The slope of the linear portion.
vertexInterestRate: The slope of the exponential portion.
vertexStartingPoint: The utilization point where vertex takes effect.
Adjustment Controls:
adjustmentRate: Time between multiplier updates (seconds).
adjustmentVelocity: Maximum rate of multiplier change per update.
vertexMultiplierMax: Maximum allowed value for multiplier.
Threshold Parameters:
increaseThreshold: Point above vertex where multiplier increases.
increaseThresholdMax: Point where multiplier increase reaches maximum.
decreaseThreshold: Point below vertex where multiplier decreases.
decreaseThresholdMax: Point where multiplier decrease reaches maximum.
decayRate: Rate at which elevated multipliers naturally decrease.
Design Benefits
Responsive to Market Conditions:
High utilization leads to increased rates, attracting lenders.
Sustained high rates encourage borrowers to repay.
Self-Balancing:
Creates a feedback loop that stabilizes market liquidity.
Prevents liquidity crunches through predictive rate adjustments.
Growth Incentives:
Decay mechanism helps maintain competitive rates during normal operations.
Creates naturally decreasing interest rates in stable markets.
Gas Optimization:
Uses bit-packed storage for multiplier and timestamp.
Efficient math calculations for model computation.
Practical Example
If a market experiences sustained high utilization:
Interest rates will gradually increase as the Vertex Multiplier rises.
This attracts new lenders while encouraging borrowers to repay.
As utilization decreases, rates begin to fall (but not immediately due to the multiplier).
The decay mechanism ensures rates will eventually normalize even without full utilization correction.
This creates a more stable, responsive system compared to fixed-rate models while protecting the protocol from potential liquidity crises.
Math Equations
Utilization Rate Calculation
function utilizationRate(...) {
if (borrows == 0) {
return 0;
uint256 utilRate = (borrows * WAD) /
(underlyingHeld + borrows - reserves);
// If reserves end up growing too much and cause util > 100%,
// cap it to 100%.
return utilRate > WAD ? WAD : utilRate;
}
Base Interest Rate Calculation
function _getBaseInterestRate(uint256 util) internal view returns (uint256) {
return (util * ratesConfig.baseInterestRate) / WAD;
}
Vertex Interest Rate Calculation
function _getVertexInterestRate(uint256 util) internal view returns (uint256) {
return (util * ratesConfig.vertexInterestRate * vertexMultiplier()) / WAD;
}
Final Borrow Interest Rate Calculation
function getBorrowRate(...) {
if (util <= vertexPoint) {
return _getBaseInterestRate(util);
}
return (_getVertexInterestRate(util - vertexPoint) +
_getBaseInterestRate(vertexPoint));
}
Vertex Multiplier Adjustment (Above Vertex)
Where:
function _getPositiveCFactorResult(...) {
uint256 cFactor = ((current - start) * WAD) / (end - start);
cFactor = WAD_SQUARED + (cFactor * adjustmentVelocity);
return ((multiplier * cFactor) / WAD_SQUARED) - decay;
}
Vertex Multiplier Adjustment (Below Vertex)
function _getNegativeCFactorResult(...) {
newMultiplier = ((currentMultiplier * WAD) / (WAD + config.adjustmentVelocity)) - decay;
return newMultiplier < WAD ? WAD : newMultiplier;
}
User Interaction Functions
getPredictedBorrowRatePerYear()
Contract: DynamicInterestRateModel
Description: Calculates the current borrow rate per year, with updated vertex multiplier applied.
Function signature:
function getPredictedBorrowRatePerYear(
uint256 underlyingHeld,
uint256 borrows,
uint256 reserves
) external view returns (uint256)
uint256
underlyingHeld
The amount of underlying assets held in the market.
uint256
borrows
The amount of borrows in the market.
uint256
reserves
The amount of reserves in the market.
Return data:
uint256
The borrow rate percentage per year, in WAD
getSupplyRatePerYear()
Contract: DynamicInterestRateModel
Description: Calculates the current supply rate per year. This function converts the per-compound supply rate to an annual rate.
Function signature:
function getSupplyRatePerYear(
uint256 underlyingHeld,
uint256 borrows,
uint256 reserves,
uint256 interestFee
) external view returns (uint256)
uint256
underlyingHeld
The amount of underlying assets held in the market.
uint256
borrows
The amount of borrows in the market.
uint256
reserves
The amount of reserves in the market.
uint256
interestFee
The current interest rate reserve factor for the market.
Return data:
uint256
The supply rate percentage per year, in WAD.
currentRatesData()
Contract: DynamicInterestRateModel
Description: Returns the unpacked values from _currentRates storage variable, providing the current vertex multiplier and next update timestamp.
Function signature:
function currentRatesData() external view returns (uint256, uint256)
Return data:
uint256
The current Vertex Multiplier, in WAD.
uint256
The timestamp for the next vertex multiplier update, in unix time.
utilizationRate()
Contract: DynamicInterestRateModel
Description: Calculates the utilization rate of the market using the formula: borrows / (underlyingHeld + borrows - reserves).
Function signature:
function utilizationRate(
uint256 underlyingHeld,
uint256 borrows,
uint256 reserves
) public pure returns (uint256)
uint256
underlyingHeld
The amount of underlying assets held in the market.
uint256
borrows
The amount of borrows in the market.
uint256
reserves
The amount of reserves in the market.
Return data:
uint256
The utilization rate between [0, WAD].
getPredictedBorrowRate()
Contract: DynamicInterestRateModel
Description: Calculates the current borrow rate per compound, with updated vertex multiplier applied. This provides a prediction of what the borrow rate will be after the next vertex multiplier update.
Function signature:
function getPredictedBorrowRate(
uint256 underlyingHeld,
uint256 borrows,
uint256 reserves
) public view returns (uint256)
uint256
underlyingHeld
The amount of underlying assets held in the market.
uint256
borrows
The amount of borrows in the market.
uint256
reserves
The amount of reserves in the market.
Return data:
uint256
The borrow rate percentage per compound, in WAD.
getBorrowRate()
Contract: DynamicInterestRateModel
Description: Calculates the current borrow rate, per compound, based on the current market conditions.
Function signature:
function getBorrowRate(
uint256 underlyingHeld,
uint256 borrows,
uint256 reserves
) public view returns (uint256)
uint256
underlyingHeld
The amount of underlying assets held in the market.
uint256
borrows
The amount of borrows in the market.
uint256
reserves
The amount of reserves in the market.
Return data:
uint256
The borrow rate percentage, per compound, in WAD.
getSupplyRate()
Contract: DynamicInterestRateModel
Description: Calculates the current supply rate, per compound, based on the borrow rate and interest fee.
Function signature:
function getSupplyRate(
uint256 underlyingHeld,
uint256 borrows,
uint256 reserves,
uint256 interestFee
) public view returns (uint256)
uint256
underlyingHeld
The amount of underlying assets held in the market.
uint256
borrows
The amount of borrows in the market.
uint256
reserves
The amount of reserves in the market.
uint256
interestFee
The current interest rate reserve factor for the market.
Return data:
uint256
The supply rate percentage, per compound, in WAD
vertexMultiplier()
Contract: DynamicInterestRateModel
Description: Returns the multiplier applied to the vertex interest rate, which is used to dynamically adjust interest rates based on market conditions.
Function signature:
function vertexMultiplier() public view returns (uint256)
Return data:
uint256
The multiplier applied to the vertex interest rate, in WAD.
updateTimestamp()
Contract: DynamicInterestRateModel
Description: Returns the next timestamp when the vertex multiplier will be updated.
Function signature:
function updateTimestamp() public view returns (uint256)
Return data:
uint256
The next timestamp when vertexMultiplier will be updated, in unix time.
Last updated