AToken
Last updated
Firstly, it is important to note the inheritance chain of the AToken contract:
AToken inherits VersionedInitializable, ScaledBalanceTokenBase, EIP712Base and IAToken.
Abstract helper contract to implement initializer functions.
Inspired by the OpenZeppelin Initializable contract.
This contract is used to assist with initialization. It is modified from the Initializable contract of OpenZeppelin, and the revision version number is introduced. When the version number becomes larger, it can be initialized again.
The revision number is defined on AToken.sol, which is obtained via getRevision
. However, when the modifier initializer
executes, it calls getRevision
as defined in AToken.sol, due to override.
Both revision values and getRevision
are defined on contracts inheriting VersionedInitializable; this keeps VersionedInitializable modular as it does not need to be altered for each inheritance.
ScaledBalanceTokenBase inherits MintableIncentivizedERC20, which in turn inherits IncentivizedERC20.
IncentivizedERC20 is similar to the standard ERC-20, with two differences.
balanceOf is modified with struct UserState
:
balance saved in UserState is the actual balance, and additionalData is the liquidity index.
_transfer will apply incentives via IAaveIncentivesController
It is also interesting to note that transferFrom incorporates both approve and transfer.
Abstract contract MintableIncentivizedERC20 inherits IncentivizedERC20, and implements only two functions:
_mint
_burn
These two functions will update the balance in _totalSupply
and UserState
, and then call IAaveIncentivesController.
Abstract contract ScaledBalanceTokenBase inherits MintableIncentivizedERC20 and implements three functions:
_mintScaled
_burnScaled
_transfer
WIP
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
:
override(IncentivizedERC20, IERC20)
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
This calls balanceOf as declared in IncentivizedERC20.sol.
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 of a user.
getReserveNormalizedIncome
getReserveNormalizedIncome
returns the latest liquidity index, as currentLiquidityIndex
.
Example:
user deposits 1000 DAI, when Index = 1.1
scaledBalance = 909
_userState[account].balance
= 909
Execution flow
AToken::mint
-> ScaledBalanceTokenBase::_mintScaled
-> MintableIncentivizedERC20::_mint
amountScaled
, the amount to mint is scaled against the liquidity index in _mintScaled
.
_mint
increments the user's balance by amountScaled
.
user's balance held in _userState[account].balance
.
Here is a more complete execution flow chart spanning the relevant portions across different contracts.