Scaling and ATokens
Last updated
Was this helpful?
Last updated
Was this helpful?
Previously we explained how deposits were scaled against the liquidity index to reflect every user's unique starting point in the timeseries of interest accumulation.
That brings us to the topic of ATokens and the question of how they are minted - on the basis of the scaled value of deposit or otherwise.
Atokens are interest-bearing tokens that represent a user's share in the underlying deposited assets. When users deposit funds into the Aave protocol, they receive Atokens in return, which represent their entitlement to a portion of the pool's reserves.
A depositor's balance of aTokens will increment over time as interest is accrued.
Example:
You deposit 100 DAI and are minted 100 aDAI token.
Over time, your aDAI balance will increment beyond 100, reflecting interest accrued.
When you look to withdraw in full, you will receive more than 100 DAI.
Therefore, the growth of aToken balance reflects the interest earned on deposits over a period.
Now we run into a contradiction. In the earlier on scaling positions, we saw that a user who deposited 1000 DAI when the liquidity index was 1.1, had his deposit scaled to 909.
Does this mean that he is minted 909 aDAI? Yes and no:
user will see that he has 1000 aDAI in his wallet
however, the amount parameter passed into mint
will be the scaledbalance
of 909
To make more sense of this, let's look at how balanceOf
works in the context of aTokens.
balanceOf
Aave implements a modified version of the ERC-20 standard for its aTokens, such that the balance of aTokens increments without any transactions.
Let's examine the implementation for balanceOf
:
Notice the use of override - it serves to override balanceOf
functions declared within its inheritance tree, so that the parent functions do not get called.
A user's aToken balance is the multiplication of two components:
super.balanceOf
POOL.getReserveNormalizedIncome()
In a more digestible form, this basically translates to:
Execution flow:
super.BalanceOf
Each user has a struct, UserState
associated with their address via the mapping _userState
The balance
element within the UserState
stores user's scaled balance
super.balanceOf
returns _userState[account].balance
super.BalanceOf
returns the scaled balance for a user.
POOL.getReserveNormalizedIncome()
You might wonder what calculateLinearInterest
does, we will explain this in a later section. For now you can operate on the understanding that getReserveNormalizedIncome
returns the latest liquidity index, referred to as currentLiquidityIndex
.
So in conclusion,
user deposits 1000 DAI, when Index = 1.1
scaledBalance = 909
_userState[account].balance = 909
The scaledBalance
of a user is fixed, while the currentLiquidityIndex
increments with every state-changing transaction, reflecting accrued interest. Due to this characteristic of the index, users' token balance increases with no action on their part.
Earlier, we stated that instead of the deposit amount, the scaled amount is passed into the mint function. We will understand why from this section.
Execution flow
AToken::mint
-> ScaledBalanceTokenBase::_mintScaled
-> MintableIncentivizedERC20::_mint
The abovementioned scaling can be seen from the first line in _mintScaled
. Subsequently, amountScaled
is passed into _mint
, which increments the user's balance which is captured in _userState[account].balance
.
Here is a more complete execution flow chart spanning the relevant portions across different contracts.