AAVE Source Code Analysis: The Interest Rate Model

Summary
The interest-rate model and risk control are the core of a lending protocol. In AAVE, rate updates can be divided into three parts.

The interest-rate model and risk control are the core of any lending protocol. In AAVE, interest updates can be divided into three parts:

  1. deposit interest
  2. variable borrow interest
  3. stable borrow interest

As for risk control, I discuss that separately in a later article.

The ReserveData Struct

ReserveData is the core data structure behind AAVE’s interest-rate mechanics. It is defined in contracts/protocol/libraries/types/DataTypes.sol:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
  struct ReserveData {
    // stores the reserve configuration
    // multiple config values packed into a uint256 using bitmasks
    ReserveConfigurationMap configuration;
    // the liquidity index. Expressed in ray
    // deposit-side interest index: exchange rate between aToken and token
    uint128 liquidityIndex;
    // variable borrow index. Expressed in ray
    // variable-rate borrowing index
    uint128 variableBorrowIndex;
    // the current supply rate. Expressed in ray
    // weighted average of variable and stable borrowing
    uint128 currentLiquidityRate;
    // the current variable borrow rate. Expressed in ray
    // variable borrow rate
    uint128 currentVariableBorrowRate;
    // the current stable borrow rate. Expressed in ray
    // stable borrow rate
    uint128 currentStableBorrowRate;
    uint40 lastUpdateTimestamp;
    // tokens addresses
    address aTokenAddress;
    address stableDebtTokenAddress;
    address variableDebtTokenAddress;
    // address of the interest rate strategy
    address interestRateStrategyAddress;
    // the id of the reserve. Represents the position in the list of the active reserves
    uint8 id;
  }

ReserveData is the most important structure in the lending relationship. Every asset that can be borrowed or supplied in the market has a corresponding ReserveData entry that records its key rate information.

The variables most directly related to interest are:

  1. liquidityIndex: deposit interest index
  2. variableBorrowIndex: variable-rate borrowing index
  3. currentLiquidityRate: supply-side liquidity rate, used to update liquidityIndex
  4. currentStableBorrowRate: stable borrowing rate
  5. currentVariableBorrowRate: variable borrowing rate, used to update variableBorrowIndex

Two-Slope Interest Rates

At the core, interest rates are driven by supply and demand. Whenever available liquidity changes, AAVE adjusts rates automatically. In both AAVE and Compound, the variable used to express this supply-demand relationship is utilization. The definition is:

1
2
Utilisation = total borrows / (total deposits + total borrows)
total borrows = variable borrows + stable borrows

The rate curve looks like this:

The chart shows the behavior clearly:

  1. When utilization is below the optimal utilization level, rate = base rate + utilization * slope1
  2. When utilization exceeds the optimal level, rate = base rate + slope1 + (utilization - optimal utilization) * slope2

Once utilization moves past the optimal point, rates rise very quickly. That pushes borrowers to repay sooner, otherwise they risk being liquidated.

liquidityIndex

This is the deposit-side index that describes the token/aToken exchange relationship.

It is mainly used in:

  1. Deposits

    The user’s tokens are transferred into the protocol and aTokens are minted to the user.

    The conversion is:

1
aToken amount = token amount / liquidityIndex
  1. Withdrawals

    The user’s aTokens are transferred back and burned, and the corresponding amount of underlying tokens is returned.

1
token amount = aToken amount * liquidityIndex

Because liquidityIndex keeps increasing over time, these two formulas are enough to derive deposit interest earned by the user.

variableBorrowIndex

  1. Variable-rate borrowing

    When a user borrows, the system mints debt tokens to track the principal plus time-based interest accrual.

1
varDebtToken amount = token amount / variableBorrowIndex
  1. Variable-rate repayment

    When the user repays, the amount owed is calculated as:

1
token amount = varDebtToken amount * variableBorrowIndex
  1. Liquidation

    Liquidation is one of the most important parts of risk control, and I cover it separately in the risk-control article.

Stable Borrowing

Stable borrowing is conceptually similar to variable borrowing, but with some important differences. Because stable borrowing usage is relatively low, I will skip a detailed analysis of it here.

Interest Rate Calculation Flow

The main logic lives in contracts/protocol/lendingpool/DefaultReserveInterestRateStrategy.sol, in the function calculateInterestRates.

The rough flow is:

  1. Total borrows = variable borrows + stable borrows
  2. Utilization = total borrows / (total deposits + total borrows)
  3. Calculate the variable and stable borrow rates. Both use a two-slope piecewise-linear formula. a. variable rate = base variable rate + utilization * variable slope1, plus slope2 if utilization is in the second segment b. stable rate = base stable rate + utilization * stable slope1
  4. Weighted average borrow rate = (variable borrows * variable rate + stable borrows * average stable borrow rate) / total borrows The average stable borrow rate is an input parameter and is not calculated here.
  5. currentLiquidityRate = (weighted average borrow rate / total borrows) * utilization * (1 - reserve factor)

You can see that currentLiquidityRate is the most complex variable in the chain. Its purpose is to drive the calculation of the deposit-side rate, i.e. liquidityIndex.

For the code-level analysis of these formulas, see the separate article on AAVE interest-rate code.

AAVE series:

  1. Introduction to the AAVE lending protocol
  2. Overall AAVE code architecture
  3. AAVE interest-rate model
  4. AAVE interest-rate code walkthrough
  5. AAVE liquidation
  6. AAVE flash loans
  7. How AAVE decouples modules
  8. AAVE proxy pattern
  9. AAVE testing and deployment