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
|
|
|
|
2020-11-23 10:28:57 +00:00
|
|
|
import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
|
|
|
|
import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.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';
|
2020-11-23 10:28:57 +00:00
|
|
|
import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
|
2020-11-24 13:53:34 +00:00
|
|
|
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 {
|
2020-11-24 13:53:34 +00:00
|
|
|
using ReserveLogic for DataTypes.ReserveData;
|
2020-07-13 08:54:08 +00:00
|
|
|
using SafeMath for uint256;
|
|
|
|
using WadRayMath for uint256;
|
2020-07-27 11:47:48 +00:00
|
|
|
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
|
|
|
|
2020-09-03 13:46:45 +00:00
|
|
|
uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether;
|
2020-07-13 08:54:08 +00:00
|
|
|
|
|
|
|
struct balanceDecreaseAllowedLocalVars {
|
|
|
|
uint256 decimals;
|
2020-09-24 16:20:17 +00:00
|
|
|
uint256 liquidationThreshold;
|
2020-11-25 17:33:49 +00:00
|
|
|
uint256 totalCollateralInETH;
|
|
|
|
uint256 totalDebtInETH;
|
2020-09-24 16:20:17 +00:00
|
|
|
uint256 avgLiquidationThreshold;
|
2020-11-25 17:33:49 +00:00
|
|
|
uint256 amountToDecreaseInETH;
|
2020-10-27 15:57:05 +00:00
|
|
|
uint256 collateralBalanceAfterDecrease;
|
2020-07-13 08:54:08 +00:00
|
|
|
uint256 liquidationThresholdAfterDecrease;
|
|
|
|
uint256 healthFactorAfterDecrease;
|
|
|
|
bool reserveUsageAsCollateralEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-11-25 17:33:49 +00:00
|
|
|
* @dev Checks if a specific balance decrease is allowed
|
2020-07-23 15:18:06 +00:00
|
|
|
* (i.e. doesn't bring the user borrow position health factor under HEALTH_FACTOR_LIQUIDATION_THRESHOLD)
|
2020-11-25 17:33:49 +00:00
|
|
|
* @param asset The address of the underlying asset of the reserve
|
|
|
|
* @param user The address of the user
|
|
|
|
* @param amount The amount to decrease
|
|
|
|
* @param reservesData The data of all the reserves
|
|
|
|
* @param userConfig The user configuration
|
|
|
|
* @param reserves The list of all the active reserves
|
|
|
|
* @param oracle The address of the oracle contract
|
2020-07-13 08:54:08 +00:00
|
|
|
* @return true if the decrease of the balance is allowed
|
|
|
|
**/
|
|
|
|
function balanceDecreaseAllowed(
|
2020-08-21 12:58:30 +00:00
|
|
|
address asset,
|
|
|
|
address user,
|
|
|
|
uint256 amount,
|
2020-11-24 13:53:34 +00:00
|
|
|
mapping(address => DataTypes.ReserveData) storage reservesData,
|
2020-11-24 15:17:27 +00:00
|
|
|
DataTypes.UserConfigurationMap calldata 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
|
|
|
) external view returns (bool) {
|
2020-09-25 08:57:51 +00:00
|
|
|
if (!userConfig.isBorrowingAny() || !userConfig.isUsingAsCollateral(reservesData[asset].id)) {
|
2020-08-05 10:40:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
2021-01-27 14:43:34 +00:00
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
balanceDecreaseAllowedLocalVars memory vars;
|
|
|
|
|
2021-04-23 15:32:07 +00:00
|
|
|
(, vars.liquidationThreshold, , vars.decimals, , ) = reservesData[asset]
|
2020-10-15 13:41:56 +00:00
|
|
|
.configuration
|
|
|
|
.getParams();
|
2020-09-24 16:20:17 +00:00
|
|
|
|
|
|
|
if (vars.liquidationThreshold == 0) {
|
2021-01-27 14:43:34 +00:00
|
|
|
return true;
|
2020-06-20 23:40:03 +00:00
|
|
|
}
|
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
(
|
2020-11-25 17:33:49 +00:00
|
|
|
vars.totalCollateralInETH,
|
|
|
|
vars.totalDebtInETH,
|
2020-07-13 08:54:08 +00:00
|
|
|
,
|
2020-09-24 16:20:17 +00:00
|
|
|
vars.avgLiquidationThreshold,
|
2020-06-20 23:40:03 +00:00
|
|
|
|
2020-10-06 13:51:48 +00:00
|
|
|
) = calculateUserAccountData(user, reservesData, userConfig, reserves, reservesCount, oracle);
|
2020-06-20 23:40:03 +00:00
|
|
|
|
2020-11-25 17:33:49 +00:00
|
|
|
if (vars.totalDebtInETH == 0) {
|
|
|
|
return true;
|
2020-07-13 08:54:08 +00:00
|
|
|
}
|
2020-06-20 23:40:03 +00:00
|
|
|
|
2020-11-25 17:33:49 +00:00
|
|
|
vars.amountToDecreaseInETH = IPriceOracleGetter(oracle).getAssetPrice(asset).mul(amount).div(
|
2020-07-23 15:18:06 +00:00
|
|
|
10**vars.decimals
|
2020-07-13 08:54:08 +00:00
|
|
|
);
|
2020-06-20 23:40:03 +00:00
|
|
|
|
2020-11-25 17:33:49 +00:00
|
|
|
vars.collateralBalanceAfterDecrease = vars.totalCollateralInETH.sub(vars.amountToDecreaseInETH);
|
2020-06-20 23:40:03 +00:00
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
//if there is a borrow, there can't be 0 collateral
|
2020-10-27 15:57:05 +00:00
|
|
|
if (vars.collateralBalanceAfterDecrease == 0) {
|
2020-07-13 08:54:08 +00:00
|
|
|
return false;
|
2020-06-20 23:40:03 +00:00
|
|
|
}
|
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
vars.liquidationThresholdAfterDecrease = vars
|
2020-11-25 17:33:49 +00:00
|
|
|
.totalCollateralInETH
|
2020-09-24 16:20:17 +00:00
|
|
|
.mul(vars.avgLiquidationThreshold)
|
2020-11-25 17:33:49 +00:00
|
|
|
.sub(vars.amountToDecreaseInETH.mul(vars.liquidationThreshold))
|
2020-10-27 16:49:35 +00:00
|
|
|
.div(vars.collateralBalanceAfterDecrease);
|
2020-07-13 08:54:08 +00:00
|
|
|
|
2020-11-23 10:28:57 +00:00
|
|
|
uint256 healthFactorAfterDecrease =
|
|
|
|
calculateHealthFactorFromBalances(
|
|
|
|
vars.collateralBalanceAfterDecrease,
|
2020-11-25 17:33:49 +00:00
|
|
|
vars.totalDebtInETH,
|
2020-11-23 10:28:57 +00:00
|
|
|
vars.liquidationThresholdAfterDecrease
|
|
|
|
);
|
2020-07-13 08:54:08 +00:00
|
|
|
|
2020-10-27 15:57:05 +00:00
|
|
|
return healthFactorAfterDecrease >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
|
2020-07-13 08:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct CalculateUserAccountDataVars {
|
|
|
|
uint256 reserveUnitPrice;
|
|
|
|
uint256 tokenUnit;
|
|
|
|
uint256 compoundedLiquidityBalance;
|
|
|
|
uint256 compoundedBorrowBalance;
|
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;
|
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,
|
2020-11-24 13:53:34 +00:00
|
|
|
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];
|
2020-11-24 13:53:34 +00:00
|
|
|
DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];
|
2020-07-13 08:54:08 +00:00
|
|
|
|
2021-04-23 15:32:07 +00:00
|
|
|
(vars.ltv, vars.liquidationThreshold, , vars.decimals, , ) = currentReserve
|
2020-07-23 15:18:06 +00:00
|
|
|
.configuration
|
|
|
|
.getParams();
|
|
|
|
|
|
|
|
vars.tokenUnit = 10**vars.decimals;
|
2020-08-21 12:58:30 +00:00
|
|
|
vars.reserveUnitPrice = IPriceOracleGetter(oracle).getAssetPrice(vars.currentReserveAddress);
|
2020-08-05 22:46:22 +00:00
|
|
|
|
2020-09-25 08:55:58 +00:00
|
|
|
if (vars.liquidationThreshold != 0 && userConfig.isUsingAsCollateral(vars.i)) {
|
2020-08-21 12:58:30 +00:00
|
|
|
vars.compoundedLiquidityBalance = IERC20(currentReserve.aTokenAddress).balanceOf(user);
|
2020-07-13 08:54:08 +00:00
|
|
|
|
2020-11-23 10:28:57 +00:00
|
|
|
uint256 liquidityBalanceETH =
|
|
|
|
vars.reserveUnitPrice.mul(vars.compoundedLiquidityBalance).div(vars.tokenUnit);
|
2020-06-20 23:40:03 +00:00
|
|
|
|
2020-11-25 17:33:49 +00:00
|
|
|
vars.totalCollateralInETH = vars.totalCollateralInETH.add(liquidityBalanceETH);
|
2020-07-23 15:18:06 +00:00
|
|
|
|
2020-08-05 22:46:22 +00:00
|
|
|
vars.avgLtv = vars.avgLtv.add(liquidityBalanceETH.mul(vars.ltv));
|
|
|
|
vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add(
|
|
|
|
liquidityBalanceETH.mul(vars.liquidationThreshold)
|
|
|
|
);
|
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)) {
|
2020-08-05 22:46:22 +00:00
|
|
|
vars.compoundedBorrowBalance = IERC20(currentReserve.stableDebtTokenAddress).balanceOf(
|
2020-08-21 12:58:30 +00:00
|
|
|
user
|
2020-08-05 22:46:22 +00:00
|
|
|
);
|
|
|
|
vars.compoundedBorrowBalance = vars.compoundedBorrowBalance.add(
|
2020-08-21 12:58:30 +00:00
|
|
|
IERC20(currentReserve.variableDebtTokenAddress).balanceOf(user)
|
2020-08-05 22:46:22 +00:00
|
|
|
);
|
|
|
|
|
2020-11-25 17:33:49 +00:00
|
|
|
vars.totalDebtInETH = vars.totalDebtInETH.add(
|
2020-07-13 08:54:08 +00:00
|
|
|
vars.reserveUnitPrice.mul(vars.compoundedBorrowBalance).div(vars.tokenUnit)
|
2020-06-20 23:40:03 +00:00
|
|
|
);
|
2020-07-13 08:54:08 +00:00
|
|
|
}
|
2020-06-20 23:40:03 +00:00
|
|
|
}
|
|
|
|
|
2021-01-27 14:43:34 +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) {
|
2021-01-27 14:43:34 +00:00
|
|
|
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
|
|
|
}
|