Scaling and ATokens
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
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.
User deposits 100 DAI, receives 100 aDAI in return.
ATokens are redeemable 1:1 for their underlying.
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.
You can see your AToken balance increase in real-time directly in your wallet.
Now we run into a contradiction. In the earlier example 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 thescaledbalance
of 909
To make more sense of this, let's look at how balanceOf
works in the context of aTokens.
AToken: balanceOf
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
super.BalanceOf
Each user has a struct,
UserState
associated with their address via the mapping_userState
The
balance
element within theUserState
stores user's scaled balancesuper.balanceOf
returns_userState[account].balance
super.BalanceOf
returns the scaled balance for a user.
POOL.getReserveNormalizedIncome()
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
AToken: Incrementation
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.
You can see your AToken balance increase in real-time directly in your wallet.
AToken: mint
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
.
Execution flow: Visual Aid
Here is a more complete execution flow chart spanning the relevant portions across different contracts.
Last updated