Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

How to Implement Dynamic Interest Rate Models

A technical guide to building algorithmic interest rate models that adjust based on pool utilization. Includes Solidity code for Jump Rate and kinked models.
Chainscore © 2026
introduction
DEVELOPER TUTORIAL

How to Implement Dynamic Interest Rate Models

A practical guide to coding and deploying algorithmic interest rate mechanisms for DeFi lending protocols.

Dynamic interest rate models are the core financial engines of decentralized lending protocols like Aave and Compound. Unlike traditional fixed rates, these algorithms automatically adjust borrowing and lending rates based on real-time pool utilization—the ratio of borrowed assets to supplied assets. This creates a self-regulating market: high demand for loans increases rates to incentivize more deposits, while low demand lowers rates to encourage borrowing. Implementing one requires a smart contract that calculates rates using a mathematical formula, typically a piecewise linear or kinked curve, updated with each interaction.

The most common model is the jump rate model or kinked model, exemplified by Compound's JumpRateModelV2. Its logic defines a utilizationRate (borrows / (cash + borrows)). Below an optimal kink (e.g., 80% utilization), rates increase slowly via a low multiplierPerBlock. Above the kink, a much higher jumpMultiplierPerBlock applies, sharply increasing rates to urgently attract liquidity. Your contract must implement a getBorrowRate and getSupplyRate function. Here's a simplified snippet:

solidity
function getBorrowRate(uint cash, uint borrows) public view returns (uint) {
    uint util = utilizationRate(cash, borrows);
    if (util <= kink) {
        return util * multiplierPerBlock / 1e18;
    } else {
        uint normalRate = kink * multiplierPerBlock / 1e18;
        uint excessUtil = util - kink;
        return normalRate + (excessUtil * jumpMultiplierPerBlock / 1e18);
    }
}

For production, you must integrate the model with a lending market. The market contract calls your model's rate functions during critical actions: mint (deposit), borrow, repay, and redeem (withdraw). Rates are usually expressed as an annual percentage yield (APY) but calculated per second or per block. Use ray math (27 decimals) for precision, as Aave does, to avoid rounding errors. Key parameters—baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, and kink—should be settable by governance. Thoroughly test the model's behavior at edge cases: 0% utilization, 100% utilization, and exactly at the kink, using a framework like Foundry or Hardhat.

Advanced models introduce greater complexity. Aave V3 uses a variable debt token model where the rate is stored and compounded continuously on-chain. Other approaches include PID controllers that target a specific utilization ratio or benchmark rates tied to external oracles like the Secured Overnight Financing Rate (SOFR). When designing your model, audit for interest rate manipulation: a whale could temporarily drain liquidity to spike rates, then deposit to earn abnormal yields. Implement rate change speed limits or time-weighted averages to mitigate this. Always verify calculations against reference implementations from established protocols.

To deploy, start with a forked version of a proven model from Compound or Aave. Use blockchain explorers like Etherscan to examine live deployments (e.g., Aave's USDC pool on Ethereum mainnet at 0x...). The final step is governance integration, allowing a DAO to adjust parameters in response to market conditions. A well-tuned dynamic model balances lender yield, borrower cost, and protocol solvency, forming the bedrock of a resilient DeFi money market.

prerequisites
FOUNDATIONAL KNOWLEDGE

Prerequisites

Before implementing a dynamic interest rate model, you need a solid understanding of the underlying DeFi primitives and development tools.

A dynamic interest rate model is a core component of lending protocols like Aave and Compound. To build one, you must first understand the key financial concepts it governs: utilization rate, supply/demand dynamics, and APY/APR calculations. The utilization rate, defined as Total Borrows / Total Liquidity, is the primary input that determines the borrowing cost. You'll also need familiarity with smart contract security patterns, as these models handle user funds and must be resistant to manipulation and oracle attacks.

Your development environment should be set up with essential tools. This includes Hardhat or Foundry for smart contract development and testing, a Node.js environment for scripting, and a wallet like MetaMask for deployment. You will interact with price oracles, such as Chainlink, to fetch asset prices for calculating collateral ratios. Knowledge of Solidity 0.8.x is required, with a focus on safe math libraries and understanding of integer precision when dealing with interest rate calculations, which often use RAY or WAD units for decimals.

Finally, analyze existing implementations. Study the interest rate models in Compound's White Paper and the verified source code for Aave's DefaultReserveInterestRateStrategy on Etherscan. These models, like the jump-rate model and kinked-rate model, provide a blueprint for how rates change at different utilization thresholds. Understanding their parameters—the base rate, multiplier, jump multiplier, and optimal utilization rate—is crucial for designing your own. This foundational knowledge ensures you can create a model that is both economically sound and technically robust.

key-concepts-text
CORE CONCEPTS

How to Implement Dynamic Interest Rate Models

Dynamic interest rate models are the algorithmic engines that determine borrowing costs and lending yields in DeFi protocols. This guide explains the core concepts of utilization and rate curves, and provides a practical implementation.

At the heart of any lending protocol like Aave or Compound lies the interest rate model. Unlike a static rate, a dynamic model algorithmically adjusts rates based on real-time supply and demand within a liquidity pool. The primary input for this calculation is the utilization rate (U), defined as U = Total Borrows / (Total Supply + Total Borrows). When utilization is low, capital is abundant, so rates are low to encourage borrowing. As utilization approaches 100%, capital becomes scarce, and the model sharply increases rates to incentivize repayments and more deposits.

The relationship between utilization and the resulting interest rate is defined by a rate curve. The most common model is a piecewise linear function with distinct slopes for different utilization regimes. A typical implementation uses a kink—a specific utilization point (e.g., 80%) where the slope of the curve changes. Below the kink, rates increase gradually with a slope1. Above the kink, rates spike dramatically with a much steeper slope2 to protect protocol liquidity. The base rate, or rateAtZeroUtilization, is the minimum rate when no funds are borrowed.

Here is a simplified Solidity implementation of a kinked rate model, similar to Compound's JumpRateModelV2. The core function getBorrowRate calculates the rate based on current utilization.

solidity
function getBorrowRate(uint256 cash, uint256 borrows) public view returns (uint256) {
    uint256 util = utilizationRate(cash, borrows);
    if (util <= kink) {
        return rateAtZeroUtilization + (util * slope1) / 1e18;
    } else {
        uint256 normalRate = rateAtZeroUtilization + (kink * slope1) / 1e18;
        uint256 excessUtil = util - kink;
        return normalRate + (excessUtil * slope2) / 1e18;
    }
}
function utilizationRate(uint256 cash, uint256 borrows) public pure returns (uint256) {
    if (borrows == 0) return 0;
    return (borrows * 1e18) / (cash + borrows); // Returns an 18-decimal scaled value
}

When implementing a model, you must carefully parameterize the curve. For a stablecoin pool, a common configuration might set the kink at 80%, slope1 to 4% per 100% utilization, and slope2 to 75% per 100% utilization. These parameters are governance-controlled in live protocols. The model's responsiveness is critical: a slope that is too flat fails to protect reserves during high demand, while a slope that is too steep can make borrowing prohibitively expensive and stifle protocol usage. Always test models under extreme market simulations.

Beyond the kinked model, more complex curves exist. Dynamic rate models can incorporate external oracle data like the Federal Funds Rate or protocol-specific metrics like reserve factors. Some newer models use a smooth, continuous curve (e.g., a sigmoid function) to avoid abrupt changes at the kink. The choice depends on the asset's volatility and the desired user experience. The model must be upgradeable to allow for parameter tuning based on real-world usage data without requiring a full migration of user funds.

To integrate this model into a lending protocol, you must call getBorrowRate during every liquidity operation—deposit, withdraw, borrow, and repay. The calculated borrow rate, along with a predetermined reserve factor, is used to derive the supply rate for lenders: Supply Rate = Borrow Rate * Utilization * (1 - Reserve Factor). This ensures lenders are compensated from the protocol's revenue. Always verify your implementation with formal verification tools and extensive forking tests on mainnet state to simulate real economic conditions.

common-model-types
IMPLEMENTATION PATTERNS

Common Dynamic Interest Rate Models

Dynamic models adjust borrowing and lending rates algorithmically based on real-time market conditions, primarily utilization rate. Here are the most widely implemented types.

05

Time-Based Decay Model

Incorporates the duration of a loan into the interest rate calculation. Borrowers may pay a premium for longer-term certainty, or rates may decay over time to reward long-term lenders.

  • Mechanism: Adds a time component f(t) to the core rate formula.
  • Use Case: Protocols offering fixed-term loans or aiming to shape the maturity profile of debt.
  • Complexity: Requires tracking loan inception time, increasing on-chain storage costs.
06

Implementation Checklist

Key steps and considerations when deploying a dynamic model on-chain.

  • Audit Math: Ensure rate calculations cannot overflow/underflow and are gas-efficient.
  • Parameter Governance: Decide how baseRate, slopes, and kink are set/updated (admin, governance, adaptive).
  • Integration Hook: The model must expose a getBorrowRate() or getSupplyRate() function for the core lending contract.
  • Testing: Simulate extreme utilization scenarios (0%, 100%) and rapid swings.
  • Reference: Study verified implementations from Aave, Compound, and Euler.
MODEL ARCHITECTURES

Interest Rate Model Comparison

A comparison of common interest rate model designs used in DeFi lending protocols.

Feature / MetricLinear ModelJump Rate ModelKinked Model

Core Formula

rate = baseRate + (utilization * multiplier)

rate = baseRate + (utilization * multiplier) + jumpMultiplier

Two distinct slopes with a kink at optimal utilization

Implementation Complexity

Low

Medium

Medium

Interest Rate Volatility

Low

High near jump point

Controlled

Optimal Utilization Target

Typically 80-90%

Configurable, e.g., 80%

Interest Rate at 100% Utilization

Capped by slope

Can spike to >1000% APY

Capped by second, steeper slope

Used by

Early Compound v1

Compound v2, Aave V2

Aave V1, Iron Bank

Gas Cost for getBorrowRate()

~2,500 gas

~3,800 gas

~3,200 gas

Primary Use Case

Simple, predictable rates

Strong utilization defense

Balanced efficiency and safety

implement-jump-rate-model
DYNAMIC INTEREST RATES

Step 1: Implementing a Jump Rate Model

A jump rate model adjusts borrowing costs based on utilization, with a sharp rate increase after a specified threshold to protect liquidity.

A jump rate model is a piecewise function that defines borrowing interest rates based on a pool's utilization rate (U). The core principle is to maintain protocol stability: low, stable rates encourage borrowing when capital is abundant, while a sharp, non-linear increase (the "jump") strongly disincentivizes borrowing when utilization is high and liquidity is scarce. This model is foundational in protocols like Compound v2 and Aave v2, where it acts as a primary defense against liquidity crises by making it prohibitively expensive to borrow the last portions of available funds.

The model is defined by several key parameters: the optimal utilization rate (U_optimal), the base rate (R_base), the rate at optimal utilization (R_slope1), and the jump rate slope (R_slope2). When utilization is below the optimal threshold (U < U_optimal), the rate increases gradually along R_slope1. Once utilization exceeds this threshold, the rate increases much more steeply according to R_slope2. The formula is: if U <= U_optimal: rate = R_base + (U / U_optimal) * R_slope1 and if U > U_optimal: rate = R_base + R_slope1 + ((U - U_optimal) / (1 - U_optimal)) * R_slope2.

Implementing this in Solidity requires careful fixed-point math. You typically use a ray (27 decimals) or wad (18 decimals) for precision. The calculation must be gas-efficient and safe from overflow. Here's a simplified conceptual structure:

solidity
function calculateBorrowRate(uint256 cash, uint256 borrows) public view returns (uint256) {
    uint256 total = cash + borrows;
    if (total == 0) return baseRate;
    uint256 utilization = (borrows * 1e18) / total; // Wad

    if (utilization <= optimalUtilization) {
        return baseRate + (utilization * slope1) / optimalUtilization;
    } else {
        uint256 excessUtilization = utilization - optimalUtilization;
        uint256 normalSlope = (optimalUtilization * slope1) / optimalUtilization; // Simplifies to slope1
        return baseRate + normalSlope + (excessUtilization * slope2) / (1e18 - optimalUtilization);
    }
}

Note: This example uses 1e18 for wad scaling; a production implementation would use a library like PRBMath or Solmate's FixedPointMathLib.

Parameter selection is critical for security and efficiency. The optimalUtilization is often set between 80-90%, balancing capital efficiency with a safety buffer. R_slope1 is typically modest (e.g., 0-10% APY range below optimal), while R_slope2 is aggressive, often designed to push rates towards 100%+ APY when utilization approaches 100%. These values are usually set by governance and can be updated via timelock contracts. You must also integrate this rate model with a liquidation engine and reserve factor calculations to complete the monetary policy.

Before deploying, thoroughly test the model using property-based testing with Foundry or Hardhat. Key tests should verify: rate continuity at the U_optimal boundary, monotonic increase, prevention of overflow/underflow, and integration with your lending contract's accrueInterest function. Simulate edge cases like 0% and 100% utilization. The jump rate model is a powerful tool, but its parameters must be calibrated to the specific risk profile and market dynamics of the assets in your protocol.

implement-kinked-model
TUTORIAL

Step 2: Implementing a Kinked Model (Aave v2)

This guide details the process of deploying a custom kinked interest rate model for a reserve on the Aave v2 protocol, moving from theory to a live implementation.

A kinked interest rate model introduces a kink point in the utilization curve, creating two distinct slopes for borrowing rates. Below the kink (e.g., 80% utilization), rates increase gradually to encourage borrowing. Above the kink, rates increase sharply to disincentivize further borrowing and protect liquidity. This model is defined by several parameters: the optimalUtilizationRate (the kink point), baseVariableBorrowRate, variableRateSlope1 (slope below kink), and variableRateSlope2 (slope above kink). You must also define the model's address and the optimalStableToTotalDebtRatio for stable rate calculations.

Implementation begins with writing and testing the Solidity contract. While you can write a model from scratch, Aave provides verified base contracts like DefaultReserveInterestRateStrategy as a reference. Your custom contract must inherit from and implement the IReserveInterestRateStrategy interface. The core logic resides in the calculateInterestRates function, which takes the reserve's data (current liquidity, total stable debt, total variable debt) and returns new rates. Use the established kinked formula: currentVariableBorrowRate = baseVariableBorrowRate + (utilization * slope), where the slope changes based on whether utilization is above or below the optimalUtilizationRate.

Before mainnet deployment, rigorous testing is non-negotiable. Use a forked mainnet environment (with tools like Foundry or Hardhat) to simulate interactions with the actual Aave v2 protocol contracts. Write tests that verify rate calculations at key utilization points: at 0%, at the kink, above the kink, and at 100%. Ensure the model integrates correctly with the LendingPool and that interest accrual updates as expected. Test edge cases like zero liquidity or zero debt. Aave's test suite for interest rate strategies is a valuable reference for understanding required behavior.

Deployment is a two-step process managed by the Aave governance system. First, deploy your audited interest rate strategy contract to the Ethereum mainnet. Second, submit a governance proposal to call the setReserveInterestRateStrategyAddress function on the LendingPoolConfigurator contract, linking your new contract address to the specific reserve (e.g., USDC). This proposal will specify the reserve asset and the new strategy address. Once the proposal passes and is executed, the Aave protocol will begin using your kinked model for all future interest rate calculations on that reserve, dynamically adjusting rates based on real-time utilization.

testing-and-parameterization
DYNAMIC MODELS

Step 3: Testing and Parameter Tuning

This section covers the critical process of validating and calibrating a dynamic interest rate model using simulations and testnets before mainnet deployment.

After implementing the core logic for your dynamic interest rate model, rigorous testing is essential. This phase moves beyond unit tests to assess the model's behavior under realistic, volatile market conditions. You should create a comprehensive test suite that simulates various scenarios: - Sudden liquidity influxes and withdrawals - Extreme price volatility of the underlying collateral - Periods of sustained high or low utilization - Manipulation attempts like flash loan attacks. Tools like Foundry's forge or Hardhat allow you to script these scenarios, logging key metrics such as borrow rate, supply rate, and reserve factors over time.

Parameter tuning is an iterative process of adjusting the model's constants—like the optimal utilization ratio, base rate, and slope coefficients—to achieve desired economic outcomes. For a model like JumpRateModelV2, you might start with initial parameters (e.g., baseRatePerYear=0.02, multiplierPerYear=0.20, jumpMultiplierPerYear=3.00, kink=0.80) and observe the resulting rate curve. The goal is to balance lender yield attractiveness with protocol safety, ensuring rates rise sharply enough above the kink to disincentivize over-borrowing without being prohibitively expensive.

Deploy the model to a testnet (like Sepolia or Goerli) and integrate it with a fork of a live lending protocol, such as Aave or Compound, using tools like Tenderly or Foundry's cheatcodes. This allows you to test interactions with real-world token contracts and user behavior patterns. Monitor the model's response to actual testnet transactions and compare the simulated APY with your projections. Pay close attention to gas costs for interest rate calculations, as these are incurred on every block and must remain efficient.

Finally, establish clear key performance indicators (KPIs) for model success. These typically include: - Stability of the utilization ratio around the target kink - Predictable and non-volatile rate changes for users - Sufficient protocol revenue from interest rate spreads - Resilience to oracle price feed latency. Document the rationale for your final parameter set and the test results. This documentation is crucial for audits and for informing users and stakeholders about the model's designed economic behavior and safety parameters.

DYNAMIC INTEREST RATE MODELS

Frequently Asked Questions

Common questions and troubleshooting for developers implementing dynamic interest rate models in DeFi protocols.

A dynamic interest rate model is a smart contract algorithm that automatically adjusts borrowing and lending rates based on real-time supply and demand for an asset in a liquidity pool. Unlike fixed-rate models, it uses a utilization ratio—the proportion of total deposits that are currently borrowed—as its primary input.

Key Mechanics:

  • Utilization Ratio (U): U = Total Borrows / Total Supply.
  • Rate Calculation: The model defines separate functions for the borrow rate and the supply rate (borrow rate * U * (1 - reserve factor)).
  • Kink Model: Many models, like Compound's Jump Rate model, use a 'kink' point (e.g., 90% utilization). Below the kink, rates increase slowly; above it, they increase sharply to incentivize repayments and more deposits.

The goal is to algorithmically manage capital efficiency and pool solvency without manual intervention.

security-considerations
DYNAMIC INTEREST RATE MODELS

Security Considerations and Common Pitfalls

Implementing dynamic interest rate models requires careful attention to security, economic stability, and code integrity to prevent exploits and protocol insolvency.

Dynamic interest rate models are core to DeFi lending protocols like Aave and Compound, algorithmically adjusting borrowing costs based on pool utilization. The primary security risk is oracle manipulation, where an attacker artificially inflates or deflates the reported utilization rate to borrow at unfairly low rates or trigger unnecessary liquidations. Always use a decentralized, time-weighted average price (TWAP) oracle for utilization data, and implement circuit breakers that halt rate updates during extreme volatility. The model's getBorrowRate and getSupplyRate functions must be gas-efficient and non-reentrant to prevent denial-of-service attacks during high network congestion.

A common pitfall is creating models that are economically unstable. A model that adjusts rates too slowly can lead to prolonged periods of under-collateralization during a market crash, as seen in early versions of some protocols. Conversely, a model that reacts too aggressively can cause rate volatility that drives away users. Implement rate change limits (e.g., a maximum increase per block) and thorough simulations using historical market data. The model should be calibrated to maintain a target utilization rate (often ~80-90%) where rates rise sharply to incentivize repayments and new supply, acting as a natural circuit breaker.

The smart contract implementation must guard against precision loss and integer overflow. Interest calculations typically use a ray or wad (27 or 18 decimal) fixed-point math library, as used by MakerDAO and Aave. Never use floating-point numbers. Ensure the interestAccumulator (the factor by which debt scales) updates correctly with each block. A critical bug here can permanently break the protocol's solvency. All math operations should use SafeMath libraries or Solidity 0.8.x's built-in overflow checks. Audit the interaction between the rate model and the core lending contract, especially for any rounding that could favor attackers.

Upgradability introduces significant risk. If the rate model is upgradeable via a proxy, a malicious or buggy update can instantly destabilize the protocol. Use a timelock controller for all upgrades, giving users time to exit. Consider making the model immutable after thorough auditing, or implement a dual-stage update where a new model is first attached in a 'shadow' mode for simulation before becoming active. Prominent exploits, like the bZx attack, have involved manipulated price oracles affecting rate calculations, underscoring the need for defense-in-depth.

Finally, comprehensive testing is non-negotiable. This includes: - Unit tests for all math functions across edge cases. - Fork tests using mainnet state to simulate real market conditions. - Invariant tests (e.g., with Foundry) to assert that total assets always equal total liabilities plus equity. - Scenario analysis for 'black swan' events and rapid utilization swings. Open-source your model and audits to build trust. A secure implementation balances algorithmic efficiency with robust, paranoid security practices at every data input and calculation step.

conclusion
IMPLEMENTATION SUMMARY

Conclusion and Next Steps

You have explored the core components for building dynamic interest rate models, from foundational concepts to practical Solidity code.

Implementing a dynamic interest rate model is a critical step in designing efficient DeFi lending protocols like Aave or Compound. The core principle is to algorithmically adjust borrowing costs based on real-time pool utilization, creating a self-regulating system for capital efficiency and protocol security. Your model should incorporate key parameters: the optimal utilization rate (U_optimal), base rate (R_0), and slope parameters (R_slope1, R_slope2) that define the kinked or linear response curve. Properly tuning these parameters is essential for balancing lender yields, borrower costs, and reserve stability.

The provided Solidity code examples demonstrate a functional KinkedRateModel contract. Key implementation details include using a utilization state variable, calculating rates with safe math operations, and exposing a getBorrowRate function for integration with a main lending contract. Remember to thoroughly test your model's behavior under edge cases—such as 0% utilization, 100% utilization, and the exact kink point—using a framework like Foundry or Hardhat. Simulations should validate that rates increase sharply past the kink to strongly disincentivize over-utilization and protect liquidity.

For production deployment, consider advanced enhancements. These include making parameters upgradeable via governance or a timelock controller, incorporating a smoothing factor to prevent rate volatility, or creating a model that reacts to external oracle data like ETH staking yields. Always subject your contract to professional audits and consider starting with conservative parameters on a testnet. The next step is to integrate your IRateModel interface with a lending pool's core logic, which will call getBorrowRate() and getSupplyRate() to update interest accruals for all users.

How to Implement Dynamic Interest Rate Models in DeFi | ChainScore Guides