StableDebtToken

https://docs.aave.com/developers/v/2.0/the-core-protocol/debt-tokens

We will examine stableDebtToken contract in this section. Note that debt tokens cannot be transferred and therefore do not have their related functions.

getSupplyData

This function is called in .cache, and we will explain each component.

.cache(...)
(
  reserveCache.currPrincipalStableDebt,
  reserveCache.currTotalStableDebt,
  reserveCache.currAvgStableBorrowRate,
  reserveCache.stableDebtLastUpdateTimestamp
) = IStableDebtToken(reserveCache.stableDebtTokenAddress).getSupplyData();

super.totalSupply()

  • Inherited from IncentivizedERC20.sol

  • Essentially a getter function for the internal storage variable _totalSupply

On _totalSupply:

  • Reflects total stable debt, accounting for interest accrued from inception till _totalSupplyTimestamp.

  • Does not account for interest accrued from _totalSupplyTimestamp to now.

_totalSupply

Represents total stable debt, accounting for interest accrued since inception till _totalSupplyTimestamp.

_totalSupply is incremented on mint, decremented on burn.

Example: a stable borrow is taken some time after _totalSupplyTimestamp

  • _totalSupply += stableBorrowAmount + unbooked interest

_totalSupply is incremented to account for the incoming borrow as well as the interest accrued in the period since _totalSupplyTimestamp.

Assigned to currPrincipalStableDebt, in .cache

  • _totalSupply is declared in IncentivizedERC20

  • _totalSupplyTimestamp is declared in StableDebtToken

_calcTotalSupply(avgRate)

Calculates total stable debt, accounting for interest accrued to date.

  • avgRate is _avgStableRate

  • principalSupply is _totalSupply

  • calculates interest compounded from _totalSupplyTimestamp till now (block.timestamp)

  • _totalSupplyTimestamp: Timestamp of the last update of _totalSupply

    • updated in mint & burn

Assigned to currTotalStableDebt, in .cache

Why do we calculate interest from _totalSupplyTimestamp?

Every time mint or burn is called, _totalSupply is updated such that it accounts for the interest accrued since previous update till now, as well as the mint/burn amount.

For example, at the start of mint

From this, we can see that _totalSupply is updated with the interest accrual from previous timestamp till now.

Since this occurs on each function call that would modify _totalSupply, the definition of principalStableDebt is to distinguish from accounted interest and floating, unaccounted interest.

_avgStableRate

  • internal storage variable

  • weighted average rate, calculated across all stable borrows

Simply put, assume there are 3 stable borrows at differing times:

  • 1: 100 DAI at 1%

  • 2: 200 DAI at 2%

  • 3: 300 DAI at 3%

weighted average rate = (100 * 1%) + (200 * 2%) + (300 * 3%) / (100 + 200 + 300) = 2.3%

_avgStableRate = 2.3%

ReserveCache contains the following:

  • currPrincipalStableDebt => super.totalSupply()

  • currTotalStableDebt => _calcTotalSupply(avgRate)

  • currAvgStableBorrowRate => _avgStableRate

  • stableDebtLastUpdateTimestamp => _totalSupplyTimestamp

Also

  • nextAvgStableBorrowRate = currTotalStableDebt

  • nexTotalStableDebt = currAvgStableBorrowRate

balanceOf

The balance for any address is calculated to account for interest accrued since the last interaction.

  • each user's stable rate is stored at _user[account].additionalData

  • _timestamps[account] stores the timestamp of their last interaction

totalSupply()

Declared on StableDebtToken.sol.

_calcTotalSupply(_avgStableRate)

  • super.TotalSupply() returns _totalSupply; accounts for interest from inception till _totalSupplyTimestamp.

  • _calcTotalSupply will compound this with the recently accrued interest, from _totalSupplyTimestamp till now.

Therefore, totalSupply returns the total stable debt, accounting for all interest to date.

mint

Let's examine mint, from the pretext that is has been called via executeBorrow.

mint is called via the interface IStableDebtToken, reserve.currentStableBorrowRate is passed as a param.

  • variable is cached to avoid unnecessary calls to storage: currentStableRate

_calculateBalanceIncrease

calculates the increase in balance due to compounding interest, for a specific user, since the previous

Update _totalSupply

vars.previousSupply = totalSupply();
vars.currentAvgStableRate = _avgStableRate;
vars.nextSupply = _totalSupply = vars.previousSupply + amount;
  • _totalSupply is updated to be previousSupply + amount

  • previousSupply reflects total stable debt and recently accrued interest as explained in totalSupply

  • hence, _totalSupply is incremented to account for both unbooked interest and incoming borrow.

Calculate nextStableRate

reserve.currentStableBorrowRate is passed as rate.

vars.currentStableRate = _userState[onBehalfOf].additionalData;
vars.nextStableRate = (vars.currentStableRate.rayMul(currentBalance.wadToRay()) + vars.amountInRay.rayMul(rate)).rayDiv((currentBalance + amount).wadToRay());

_userState[onBehalfOf].additionalData = vars.nextStableRate.toUint128();
  • From now till the next future interaction, interest will compound at the nextStableRate.

  • This is reflected in balanceOf, in _calculateBalanceIncrease section.

Calculate updated average stable rate

// Calculates the updated average stable rate
vars.currentAvgStableRate = _avgStableRate = (
  (vars.currentAvgStableRate.rayMul(vars.previousSupply.wadToRay()) +  rate.rayMul(vars.amountInRay)).rayDiv(vars.nextSupply.wadToRay())
).toUint128();

_mint

  • increments user's balance by amount

  • makes a call to _incentivesController, should it be defined

The maximum 128-bit integer, 2128−12^{128}− 1is a number that is not usefully written out in words or in all of its 39 digits: 340282366920938463463374607431768211455

Here it is in words:

three hundred forty undecillion, two hundred eighty-two decillion, three hundred sixty-six nonillion, nine hundred twenty octillion, nine hundred thirty-eight septillion, four hundred sixty-three sextillion, four hundred sixty-three quintillion, three hundred seventy-four quadrillion, six hundred seven trillion, four hundred thirty-one billion, seven hundred sixty-eight million, two hundred eleven thousand, four hundred fifty-five

Return variables

  • nextSupply

  • currentAvgStableRate

Visual Aid

burn

This function is typically called through repay, when the user wishes to repay all or some of his stable debt.

Get variables

_calculateBalanceIncrease

  • currentBalance accounts for latest interest, updated to block.timestamp

  • balanceIncrease: increase in interest, acrrued between last update and now

previousSupply = totalSupply()

  • get total supply of stable debt

  • calculated via _calcTotalSupply(_avgStableRate)

get user's stable rate: _userState[from].additionalData

Decrement avgStable rate accordingly

if (totalSupply <= amount):

A discrepancy arises, such that there is no debt to repay; this is possible because total debt accrues seperately from each user's individual debt.

  • if there is discrepancy, simply reset total supply & avgStableRate to 0.

  • user's debt, which he try repaying is the remainding global debt.

Else:

  • update _totalSupply = previousSupply - amount [store in local variable nextSupply]

Discrepancy:

  • Similar to before, there might be a discrepancy arising due to global rate and user's rate being tracking independently.

  • To identify if such a discrepancy exists: if userRate * userBalance > avgRate * totalSupply, reset both totalSupply and avgStableRate.

  • note that totalSupply has been updated, less the amount to burn

  • Otherwise, update avgRate as per nextAvgStableRate calculation.

nextAvgStableRate: [(avgRate * totalSupply) - (userRate * userBalance)] / (totalSupply - amount)

Update user info

if amount == user's updated balance:

  • reset user's stabel rate

  • reset user's lastUpdatedTimestamp -> no more debt. repaid in full. else:

  • just update user's lastUpdatedTimestamp

Global _totalSupplyTimestamp is updated as well

_mint or _burn

Depends if accrued interest > user input

  • if(balanceIncrease > amount): mint the difference

  • else: burn the difference

Last updated