Curvance
  • Protocol Overview
    • Click Less, Earn More
    • Protocol Architecture
    • Asset Types
    • Liquidity Markets
      • Borrowing
      • Liquidations
      • Interest Rates
      • Oracles
      • Collateral Caps
      • Bad Debt Socialization
    • Application Specific Sequencing
    • New Age Liquidity Mining
      • Protocols
    • How Are New Assets Integrated
    • Plugin System
    • Universal Account Balance
    • Token Approval Management
    • Lending Risks
  • Security
    • Security and Audits
  • Miscellaneous
    • RPCs and Testnet Stability
    • Glossary
    • TL;DR
      • Customer Types and Benefits
    • Brand Assets
    • Weblinks
    • Disclaimer
    • Frequently Asked Questions
  • Developer Docs
    • Overview
    • Quick Start Guides
      • Atlas Fastlane Auctions (coming soon)
      • Plugin Integration
        • List of Delegable Actions
      • Loans & Collateral
        • Lend Assets
        • Deposit into pTokens
        • Withdraw Loans
        • Withdraw pTokens
      • Borrowing & Repayment
        • Borrow
        • Repaying Debt
      • Leverage
        • Leveraging
        • Deleveraging
    • Lending Protocol
      • Market Manager
      • Position Tokens (pToken)
      • Earn Tokens (eTokens)
      • Dynamic Interest Rate Model
      • Universal Balance
    • Position Management
      • Leverage
      • Deleverage / Fold
    • Dynamic Liquidation Engine (DLE)
      • Orderflow Auction System
      • Bad Debt Socialization
    • Plugin & Delegation System
      • Transfer Lock Mechanism
      • Delegable Actions
    • Cross-Chain Functionality
      • Messaging Hub
      • Fee Manager
      • Reward Manager
    • Auxiliary Functionality
Powered by GitBook
On this page
  • Overview
  • Core Components
  • Data Flow
  • State Machine
  • Configuration Parameters
  • Design Benefits
  • Practical Example
  • Math Equations
  • User Interaction Functions
Export as PDF
  1. Developer Docs
  2. Lending Protocol

Dynamic Interest Rate Model

PreviousEarn Tokens (eTokens)NextUniversal Balance

Last updated 26 days ago

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

  1. Market Utilization → Calculated from borrows / (underlyingHeld + borrows - reserves).

  2. Utilization → Drives interest rate calculations through base and vertex formulas.

  3. Interest Rates → Applied to borrowers and distributed to lenders (minus protocol fees).

  4. Vertex Multiplier → Adjusted based on sustained market utilization patterns.

  5. 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

  1. Responsive to Market Conditions:

    1. High utilization leads to increased rates, attracting lenders.

    2. Sustained high rates encourage borrowers to repay.

  2. Self-Balancing:

    1. Creates a feedback loop that stabilizes market liquidity.

    2. Prevents liquidity crunches through predictive rate adjustments.

  3. Growth Incentives:

    1. Decay mechanism helps maintain competitive rates during normal operations.

    2. Creates naturally decreasing interest rates in stable markets.

  4. Gas Optimization:

    1. Uses bit-packed storage for multiplier and timestamp.

    2. Efficient math calculations for model computation.

Practical Example

If a market experiences sustained high utilization:

  1. Interest rates will gradually increase as the Vertex Multiplier rises.

  2. This attracts new lenders while encouraging borrowers to repay.

  3. As utilization decreases, rates begin to fall (but not immediately due to the multiplier).

  4. 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

  1. Utilization Rate Calculation

UtilizationRate=Borrows/(UnderlyingHeld+Borrows−Reserves)Utilization Rate = Borrows / (Underlying Held + Borrows - Reserves)UtilizationRate=Borrows/(UnderlyingHeld+Borrows−Reserves)
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;
}

  1. Base Interest Rate Calculation

BaseInterestRate=(Utilization∗BaseRate)/WADBase Interest Rate = (Utilization * Base Rate) / WADBaseInterestRate=(Utilization∗BaseRate)/WAD
function _getBaseInterestRate(uint256 util) internal view returns (uint256) {
    return (util * ratesConfig.baseInterestRate) / WAD;
}

  1. Vertex Interest Rate Calculation

VertexInterestRate=(Utilization−VertexStart)∗(VertexRate)∗(Multiplier)/WAD2Vertex Interest Rate = (Utilization - Vertex Start) * (Vertex Rate) * (Multiplier) / WAD^2VertexInterestRate=(Utilization−VertexStart)∗(VertexRate)∗(Multiplier)/WAD2
function _getVertexInterestRate(uint256 util) internal view returns (uint256) {
    return (util * ratesConfig.vertexInterestRate * vertexMultiplier()) / WAD;
}

  1. Final Borrow Interest Rate Calculation

BorrowRate=BaseInterestRate+VertexInterestRateBorrow Rate = Base Interest Rate + Vertex Interest RateBorrowRate=BaseInterestRate+VertexInterestRate
function getBorrowRate(...) {
    if (util <= vertexPoint) {
        return _getBaseInterestRate(util);
    }
    return (_getVertexInterestRate(util - vertexPoint) +
         _getBaseInterestRate(vertexPoint));
}

  1. Vertex Multiplier Adjustment (Above Vertex)

NewMultiplier=(CurrentMultiplier∗(WAD2+CFactor∗CFactor∗AdjustmentVelocity)/WAD2)−DecayNew Multiplier = (Current Multiplier * (WAD^2 + CFactor * CFactor * Adjustment Velocity) / WAD^2) - DecayNewMultiplier=(CurrentMultiplier∗(WAD2+CFactor∗CFactor∗AdjustmentVelocity)/WAD2)−Decay

Where:

CFactor=(Utilization−IncreaseThreshold)/(IncreaseThresholdMax−IncreaseThreshold)CFactor = (Utilization - Increase Threshold) / (Increase Threshold Max - Increase Threshold)CFactor=(Utilization−IncreaseThreshold)/(IncreaseThresholdMax−IncreaseThreshold)
function _getPositiveCFactorResult(...) {

uint256 cFactor = ((current - start) * WAD) / (end - start);
cFactor = WAD_SQUARED + (cFactor * adjustmentVelocity);
return ((multiplier * cFactor) / WAD_SQUARED) - decay;

}

  1. Vertex Multiplier Adjustment (Below Vertex)

NewMultiplier=((CurrentMultiplier∗WAD)/(WAD+AdjustmentVelocity))−DecayNew Multiplier = ((Current Multiplier * WAD) / (WAD + Adjustment Velocity)) - DecayNewMultiplier=((CurrentMultiplier∗WAD)/(WAD+AdjustmentVelocity))−Decay
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)
Type
Name
Description

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:

Type
Description

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)
Type
Name
Description

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:

Type
Description

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:

Type
Description

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)
Type
Name
Description

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:

Type
Description

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)
Type
Name
Description

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:

Type
Description

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)
Type
Name
Description

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:

Type
Description

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)
Type
Name
Description

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:

Type
Description

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:

Type
Description

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:

Type
Description

uint256

The next timestamp when vertexMultiplier will be updated, in unix time.