calculateUserAccountData
Last updated
Last updated
This function calculates and returns the following user data across all the assets:
totalCollateralInBaseCurrency
totalDebtInBaseCurrency
avgLTV
avgLiquidationThreshold
healthFactor
hasZeroLtvCollateral
userConfig
is emptyIf all the bits in UserConfiguration is set to 0, data == 0
will evaluate to be true. This indicates that the user did not undertake any borrow or supply as collateral action.
returns health factor as type(uint256).max
returns hasZeroLTVCollateral
as false
We need to obtain e-mode specific info such as LTV, liquidationThreshold and eModeAssetPrice.
Get e-mode configuration details, by passing the user's eModeCategory id (params.userEModeCategory
) into the mapping eModeCategories.
This will return the EModeCategory
struct.
params.userEModeCategory
was obtained previously by passing _usersEModeCategory[msg.sender]
The if statement exists purely to check if a priceSource
was defined in EModeCategory
. If no priceSource
was defined, params.oracle
is returned as the oracle address.
Else params.oracle
, is overwritten with the category.priceSource
.
Loops through all the active reserves there are in the protocol. reservesCount
is defined on PoolStorage.sol
For each asset, the following is executed.
If the asset is not being used as either, increment the counter and continue
; skip the remaining block of code and moving to the next reserveIndex
.
require statement performs a boundary check to ensure that reserveIndex
value is within the valid range of [0 - 127]
.
If you are unclear on the bitmap manipulations, please see that section.
Get the asset address of the current iteration by passing the counter into mapping reservesList
reservesList is defined on PoolStorage
If asset address is undefined, increment counter and continue.
Would there be gas savings by checking for zero address first, then followed by isUsingAsCollateralOrBorrow?
Now that we have established that the asset is defined and being used by the user as either collateral or borrowing, let us obtain the following key details:
LTV
liquidationThreshold
decimals
Emode category
This is achieved via getParams
, which utilizes bitmasks to extract the relevant information from the ReserveConfigurationMap; which is a bitmap.
Define the decimal precision of 1 unit if the asset (1 Ether = 10**18
| 1 USDC = 10**6
)
Define the oracle interface
If both the user and the asset are in the same e-mode category, and vars.eModeAssetPrice !=0
, use vars.eModeAssetPrice
Else, default to using the following as the oracle interface:
Setting of oracles is crucial because we will be normalizing all of the user's collateral and debt to a common base currency; likely USD. This will allow us to calculate wallet-level metrics like LTV and liquidation threshold and consequently the user's health factor.
If the asset's liquidation threshold is defined and it is being used by the user as collateral, execute the following.
get user's balance in base CCY and increment totalCollateralInBaseCurrency
totalCollateralInBaseCurrency
will be the sum of collateral across all asset classes, normalized into the base currency.
E.g. get user's total collateral in USD.
Each market has an AaveOracle contract where you can query token prices in the base currency. BaseCCY:
ETH on V2 mainnet/polygon
USD on all other markets)
If the asset's LTV is defined: avgLTV
calculate the user's max debt for each asset
the sum of these across all assets will give us the numerator for the avgLTV calculation
we will divide by totalCollateralInBaseCurrency
at the end
Loan to Value (”LTV”) ratio defines the maximum amount of assets that can be borrowed with a specific collateral.
avgLiquidationThreshold
For each wallet, the Liquidation Threshold is calculate as the weighted average of the Liquidation Thresholds of the collateral assets and their value:
At this stage we simply look to obtain the numerator for the avgLiquidationThreshold calculation. Like avgLTV, the division will be done at the end, once the loop has been completed.
Liquidation threshold is the percentage at which a position is defined as undercollateralised. For example, a Liquidation threshold of 80% means that if the loan value rises above 80% of the collateral, the position is undercollateralised and could be liquidated.
If user is borrowing this asset, calculate its value in base CCY, and increment vars.totalDebtInBaseCurrency
Now that we have traversed across the entire universe of assets and increments the various necessary measures like
totalCollateralInBaseCurrency
totalDebtInBaseCurrency
LTV, Liquidation threshold
We have the prerequisites to calculate a wallet's health factor.
First we obtain avgLtv and avgLiquidationThreshold by dividing them each against totalCollateralInBaseCurrency
.
Remember, we previously only obtained their respective numerators for the weighted calculation, in the while loop.
Then the calculation for health factor:
avgLiquidationThreshold
was obtained by dividing the weighted sum by totalCollateral, therefore this can be expressed as:
collateral * liquidationThreshold => max debt possible for that collateral asset
the ratio of the sum of max debt possible against the totalDebt presently held constitutes the health factor of a wallet
numerator < denominator
<
User's current total debt exceeds his max loan value possible; hence considered undercollateralized.