aave-protocol-v2/contracts/protocol/libraries/logic/GenericLogic.sol

199 lines
6.9 KiB
Solidity
Raw Normal View History

2020-06-20 23:40:03 +00:00
// SPDX-License-Identifier: agpl-3.0
2020-11-20 10:45:20 +00:00
pragma solidity 0.6.12;
2020-08-05 10:40:24 +00:00
pragma experimental ABIEncoderV2;
2020-06-20 23:40:03 +00:00
import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
2020-07-13 08:54:08 +00:00
import {ReserveLogic} from './ReserveLogic.sol';
2020-08-20 07:51:21 +00:00
import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
import {UserConfiguration} from '../configuration/UserConfiguration.sol';
import {WadRayMath} from '../math/WadRayMath.sol';
import {PercentageMath} from '../math/PercentageMath.sol';
import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
import {DataTypes} from '../types/DataTypes.sol';
2020-06-20 23:40:03 +00:00
2020-06-27 02:13:32 +00:00
/**
2020-07-13 08:54:08 +00:00
* @title GenericLogic library
* @author Aave
2020-11-25 17:33:49 +00:00
* @title Implements protocol-level logic to calculate and validate the state of a user
2020-07-13 08:54:08 +00:00
*/
2020-06-20 23:40:03 +00:00
library GenericLogic {
using ReserveLogic for DataTypes.ReserveData;
2020-07-13 08:54:08 +00:00
using SafeMath for uint256;
using WadRayMath for uint256;
using PercentageMath for uint256;
2020-11-24 15:17:27 +00:00
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
using UserConfiguration for DataTypes.UserConfigurationMap;
2020-07-13 08:54:08 +00:00
uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether;
2020-07-13 08:54:08 +00:00
struct CalculateUserAccountDataVars {
2021-04-27 15:21:36 +00:00
uint256 assetPrice;
uint256 assetUnit;
uint256 userBalance;
uint256 userBalanceETH;
uint256 userDebt;
uint256 userDebtETH;
2020-07-23 15:18:06 +00:00
uint256 decimals;
uint256 ltv;
2020-07-13 08:54:08 +00:00
uint256 liquidationThreshold;
uint256 i;
uint256 healthFactor;
2020-11-25 17:33:49 +00:00
uint256 totalCollateralInETH;
uint256 totalDebtInETH;
2020-07-23 15:18:06 +00:00
uint256 avgLtv;
uint256 avgLiquidationThreshold;
uint256 reservesLength;
uint256 normalizedIncome;
uint256 normalizedDebt;
2020-07-13 08:54:08 +00:00
bool healthFactorBelowThreshold;
address currentReserveAddress;
bool usageAsCollateralEnabled;
bool userUsesReserveAsCollateral;
}
/**
2020-11-25 17:33:49 +00:00
* @dev Calculates the user data across the reserves.
2020-07-13 08:54:08 +00:00
* this includes the total liquidity/collateral/borrow balances in ETH,
* the average Loan To Value, the average Liquidation Ratio, and the Health factor.
2020-11-25 17:33:49 +00:00
* @param user The address of the user
* @param reservesData Data of all the reserves
* @param userConfig The configuration of the user
* @param reserves The list of the available reserves
* @param oracle The price oracle address
* @return The total collateral and total debt of the user in ETH, the avg ltv, liquidation threshold and the HF
2020-07-13 08:54:08 +00:00
**/
function calculateUserAccountData(
2020-08-21 12:58:30 +00:00
address user,
mapping(address => DataTypes.ReserveData) storage reservesData,
2020-11-24 15:17:27 +00:00
DataTypes.UserConfigurationMap memory userConfig,
2020-10-06 13:51:48 +00:00
mapping(uint256 => address) storage reserves,
uint256 reservesCount,
2020-08-21 12:58:30 +00:00
address oracle
2020-07-13 08:54:08 +00:00
)
2020-08-05 10:40:24 +00:00
internal
2020-07-13 08:54:08 +00:00
view
returns (
uint256,
uint256,
uint256,
uint256,
uint256
)
{
CalculateUserAccountDataVars memory vars;
2020-08-21 12:58:30 +00:00
if (userConfig.isEmpty()) {
2020-08-05 22:46:22 +00:00
return (0, 0, 0, 0, uint256(-1));
}
2020-10-06 13:51:48 +00:00
for (vars.i = 0; vars.i < reservesCount; vars.i++) {
2020-08-21 12:58:30 +00:00
if (!userConfig.isUsingAsCollateralOrBorrowing(vars.i)) {
2020-08-05 10:40:24 +00:00
continue;
}
2020-08-21 12:58:30 +00:00
vars.currentReserveAddress = reserves[vars.i];
DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];
2020-07-13 08:54:08 +00:00
2020-10-12 12:37:53 +00:00
(vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve
2020-07-23 15:18:06 +00:00
.configuration
.getParams();
2021-04-27 15:21:36 +00:00
vars.assetUnit = 10**vars.decimals;
vars.assetPrice = IPriceOracleGetter(oracle).getAssetPrice(vars.currentReserveAddress);
2020-08-05 22:46:22 +00:00
if (vars.liquidationThreshold != 0 && userConfig.isUsingAsCollateral(vars.i)) {
2021-04-27 15:21:36 +00:00
vars.userBalance = IScaledBalanceToken(currentReserve.aTokenAddress).scaledBalanceOf(user);
if (vars.userBalance > 0) {
vars.normalizedIncome = currentReserve.getNormalizedIncome();
2021-04-27 15:21:36 +00:00
vars.userBalance = vars.userBalance.rayMul(vars.normalizedIncome);
}
2020-07-13 08:54:08 +00:00
2021-04-27 15:21:36 +00:00
vars.userBalanceETH = vars.assetPrice.mul(vars.userBalance).div(vars.assetUnit);
2020-06-20 23:40:03 +00:00
2021-04-27 15:21:36 +00:00
vars.totalCollateralInETH = vars.totalCollateralInETH.add(vars.userBalanceETH);
2020-07-23 15:18:06 +00:00
2021-04-27 15:21:36 +00:00
vars.avgLtv = vars.avgLtv.add(vars.userBalanceETH.mul(vars.ltv));
2020-08-05 22:46:22 +00:00
vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add(
2021-04-27 15:21:36 +00:00
vars.userBalanceETH.mul(vars.liquidationThreshold)
2020-08-05 22:46:22 +00:00
);
2020-07-13 08:54:08 +00:00
}
2020-06-20 23:40:03 +00:00
2020-08-21 12:58:30 +00:00
if (userConfig.isBorrowing(vars.i)) {
2021-04-27 15:21:36 +00:00
vars.userDebt = IScaledBalanceToken(currentReserve.variableDebtTokenAddress)
.scaledBalanceOf(user);
if (vars.userDebt > 0) {
vars.normalizedDebt = currentReserve.getNormalizedDebt();
2021-04-27 15:21:36 +00:00
vars.userDebt = vars.userDebt.rayMul(vars.normalizedDebt);
}
2021-04-27 15:21:36 +00:00
vars.userDebt = vars.userDebt.add(
IERC20(currentReserve.stableDebtTokenAddress).balanceOf(user)
2020-06-20 23:40:03 +00:00
);
2021-04-27 15:21:36 +00:00
vars.userDebtETH = vars.assetPrice.mul(vars.userDebt).div(vars.assetUnit);
vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH);
2020-07-13 08:54:08 +00:00
}
2020-06-20 23:40:03 +00:00
}
vars.avgLtv = vars.totalCollateralInETH > 0 ? vars.avgLtv.div(vars.totalCollateralInETH) : 0;
2020-11-25 17:33:49 +00:00
vars.avgLiquidationThreshold = vars.totalCollateralInETH > 0
? vars.avgLiquidationThreshold.div(vars.totalCollateralInETH)
2020-07-13 08:54:08 +00:00
: 0;
vars.healthFactor = calculateHealthFactorFromBalances(
2020-11-25 17:33:49 +00:00
vars.totalCollateralInETH,
vars.totalDebtInETH,
2020-07-23 15:18:06 +00:00
vars.avgLiquidationThreshold
2020-07-13 08:54:08 +00:00
);
return (
2020-11-25 17:33:49 +00:00
vars.totalCollateralInETH,
vars.totalDebtInETH,
2020-07-23 15:18:06 +00:00
vars.avgLtv,
vars.avgLiquidationThreshold,
2020-07-13 08:54:08 +00:00
vars.healthFactor
);
}
/**
2020-11-25 17:33:49 +00:00
* @dev Calculates the health factor from the corresponding balances
* @param totalCollateralInETH The total collateral in ETH
* @param totalDebtInETH The total debt in ETH
* @param liquidationThreshold The avg liquidation threshold
* @return The health factor calculated from the balances provided
2020-07-13 08:54:08 +00:00
**/
function calculateHealthFactorFromBalances(
2020-11-25 17:33:49 +00:00
uint256 totalCollateralInETH,
uint256 totalDebtInETH,
2020-07-13 08:54:08 +00:00
uint256 liquidationThreshold
2020-08-21 12:58:30 +00:00
) internal pure returns (uint256) {
2020-11-25 17:33:49 +00:00
if (totalDebtInETH == 0) return uint256(-1);
2020-07-13 08:54:08 +00:00
2020-11-25 17:33:49 +00:00
return (totalCollateralInETH.percentMul(liquidationThreshold)).wadDiv(totalDebtInETH);
2020-07-13 08:54:08 +00:00
}
2020-09-21 20:08:44 +00:00
2020-09-25 08:57:51 +00:00
/**
2020-11-25 17:33:49 +00:00
* @dev Calculates the equivalent amount in ETH that an user can borrow, depending on the available collateral and the
* average Loan To Value
* @param totalCollateralInETH The total collateral in ETH
* @param totalDebtInETH The total borrow balance
* @param ltv The average loan to value
2020-09-21 20:08:44 +00:00
* @return the amount available to borrow in ETH for the user
**/
function calculateAvailableBorrowsETH(
2020-11-25 17:33:49 +00:00
uint256 totalCollateralInETH,
uint256 totalDebtInETH,
2020-09-21 20:08:44 +00:00
uint256 ltv
) internal pure returns (uint256) {
uint256 availableBorrowsETH = totalCollateralInETH.percentMul(ltv);
2020-09-21 20:08:44 +00:00
2020-11-25 17:33:49 +00:00
if (availableBorrowsETH < totalDebtInETH) {
2020-09-21 20:08:44 +00:00
return 0;
}
2020-11-25 17:33:49 +00:00
availableBorrowsETH = availableBorrowsETH.sub(totalDebtInETH);
2020-09-21 20:08:44 +00:00
return availableBorrowsETH;
}
2020-06-20 23:40:03 +00:00
}