Section 3 of 18

Build
+15 Lynx

Interest Rate Model

What You Are Building

The interest rate model determines how expensive it is to borrow and how much suppliers earn. Compound V2 uses the Jump Rate Model: a piecewise linear function with a "kink" that causes rates to spike when utilization gets too high. This section builds both the abstract InterestRateModel interface and the concrete JumpRateModel implementation.

This contract is standalone. It does not inherit from the math library because it uses raw uint256 arithmetic with explicit 1e18 scaling. The CToken calls it externally to get the current rates.

Utilization Rate: The Key Input

Every interest rate calculation starts with utilization, which measures what fraction of the pool is currently lent out:

utilizationRate = borrows / (cash + borrows - reserves)

cash is the underlying tokens sitting in the CToken contract (available to withdraw or borrow). borrows is the total amount currently lent out. reserves is the protocol's accumulated revenue. The denominator cash + borrows - reserves is the total capital that suppliers have deposited. If borrows is 0, utilization is 0.

A market with 800,000 DAI borrowed from a pool of 1,000,000 DAI total has 80% utilization. This single number drives the entire rate curve.

Why the Kink Exists

A simple linear rate model (borrow rate increases proportionally with utilization) has a problem: at 100% utilization, suppliers cannot withdraw. Every token is lent out. The protocol has become illiquid.

The kink solves this by creating two rate regimes. Below the kink (typically 80%), rates rise gently. This is the "normal" operating range. Above the kink, rates spike sharply. A borrower at 90% utilization might pay 20% APR instead of the 5% they would pay at 70%.

This spike creates a powerful feedback loop. High rates discourage new borrowing and incentivize repayment. High rates also attract new deposits from yield-seeking suppliers. Both forces push utilization back below the kink.

The result is a self-regulating market. The kink acts as a soft ceiling that the market naturally gravitates toward, ensuring there is always some liquidity available for withdrawals.

The Rate Formulas

Below the kink:

borrowRate = baseRate + multiplier * utilization

Above the kink:

borrowRate = baseRate + multiplier * kink + jumpMultiplier * (utilization - kink)

The two segments connect at the kink point. Below it, the multiplier controls slope. Above it, the jumpMultiplier (which is much steeper) takes over. Real Compound markets use parameters like: baseRate = 2% APR, multiplier = 20% APR, jumpMultiplier = 200% APR, kink = 80%.

Supply rate is derived from the borrow rate:

supplyRate = borrowRate * (1 - reserveFactor) * utilizationRate

Suppliers earn less than borrowers pay for two reasons. Only the borrowed fraction generates interest (multiplied by utilization). And the protocol takes a cut (the reserve factor, typically 10%). At 80% utilization with a 10% reserve factor, suppliers earn borrowRate * 0.9 * 0.8 = 72% of the borrow rate.

Per-Block Rates

All rates are stored and returned per block, not per year. The constructor takes annual rates and divides by blocksPerYear (2,628,000 for ~12 second blocks). This means baseRatePerBlock is a very small number, around 7.6e9 for a 2% annual rate.

Working per-block avoids large intermediate values. When accrueInterest() calculates borrowRate * blockDelta, both values are small, keeping the product well within uint256 range even over thousands of blocks.

Your Task

Build both the InterestRateModel abstract contract and the JumpRateModel implementation.

  1. Define the InterestRateModel abstract contract with the isInterestRateModel constant and two virtual functions: getBorrowRate and getSupplyRate
  2. Implement the JumpRateModel constructor that converts annual rates to per-block rates
  3. Implement utilizationRate: return borrows * 1e18 / (cash + borrows - reserves), or 0 if borrows is 0
  4. Implement getBorrowRate: compute utilization, then apply the piecewise formula. If util <= kink, use the normal slope. Otherwise, compute the normal rate at the kink plus the jump rate for the excess utilization
  5. Implement getSupplyRate: compute oneMinusReserveFactor = 1e18 - reserveFactorMantissa, get the borrow rate via this.getBorrowRate(...), multiply borrow rate by oneMinusReserveFactor (divided by 1e18) to get the pool's rate, then multiply by utilization (divided by 1e18) to get the per-supplier rate

Your Code

Solution.sol
Solidity
Loading editor...

Requirements

Write your implementation, then click Run Tests. Tests execute on the server.