fix: changed conditions of the exposure ceiling

This commit is contained in:
The3D 2021-06-28 16:41:35 +02:00
parent 715635def4
commit 435c34fefd
3 changed files with 38 additions and 35 deletions

View File

@ -289,7 +289,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
if (useAsCollateral) {
emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
} else {
ValidationLogic.validateWithdrawCollateral(
ValidationLogic.validateHFAndExposureCap(
asset,
msg.sender,
_reserves,
@ -497,7 +497,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
totalDebtETH,
ltv,
currentLiquidationThreshold,
healthFactor
healthFactor,
) = GenericLogic.getUserAccountData(
user,
_reserves,
@ -629,7 +629,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
if (fromConfig.isUsingAsCollateral(reserveId)) {
if (fromConfig.isBorrowingAny()) {
ValidationLogic.validateWithdrawCollateral(
ValidationLogic.validateHFAndExposureCap(
asset,
from,
_reserves,
@ -877,7 +877,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
if (userConfig.isUsingAsCollateral(reserve.id)) {
if (userConfig.isBorrowingAny()) {
ValidationLogic.validateWithdrawCollateral(
ValidationLogic.validateHFAndExposureCap(
asset,
msg.sender,
_reserves,

View File

@ -34,6 +34,7 @@ library GenericLogic {
uint256 userBalance;
uint256 userBalanceETH;
uint256 userDebt;
uint256 userStableDebt;
uint256 userDebtETH;
uint256 decimals;
uint256 ltv;
@ -43,6 +44,7 @@ library GenericLogic {
uint256 totalCollateralInETH;
uint256 totalDebtInETH;
uint256 avgLtv;
uint256 avgUncappedLtv;
uint256 avgLiquidationThreshold;
uint256 reservesLength;
uint256 normalizedIncome;
@ -65,7 +67,7 @@ library GenericLogic {
* @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
* @return The total collateral and total debt of the user in ETH, the avg ltv, liquidation threshold, the HF and the uncapped avg ltv (without exposure ceiling)
**/
function calculateUserAccountData(
address user,
@ -82,13 +84,14 @@ library GenericLogic {
uint256,
uint256,
uint256,
uint256,
uint256
)
{
CalculateUserAccountDataVars memory vars;
if (userConfig.isEmpty()) {
return (0, 0, 0, 0, uint256(-1));
return (0, 0, 0, 0, uint256(-1), 0);
}
for (vars.i = 0; vars.i < reservesCount; vars.i++) {
if (!userConfig.isUsingAsCollateralOrBorrowing(vars.i)) {
@ -130,14 +133,18 @@ library GenericLogic {
vars.exposureCapCrossed =
vars.exposureCap != 0 &&
vars.aTokenSupply.div(10**vars.decimals) > vars.exposureCap;
vars.avgLtv = vars.avgLtv.add(vars.exposureCapCrossed ? 0 : vars.userBalanceETH.mul(vars.ltv));
vars.avgLtv = vars.avgLtv.add(
vars.exposureCapCrossed ? 0 : vars.userBalanceETH.mul(vars.ltv)
);
vars.avgUncappedLtv = vars.avgUncappedLtv.add(vars.userBalanceETH.mul(vars.ltv));
vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add(
vars.userBalanceETH.mul(vars.liquidationThreshold)
);
}
if (userConfig.isBorrowing(vars.i)) {
vars.userStableDebt = IERC20(currentReserve.stableDebtTokenAddress).balanceOf(user);
vars.userDebt = IScaledBalanceToken(currentReserve.variableDebtTokenAddress)
.scaledBalanceOf(user);
@ -145,16 +152,16 @@ library GenericLogic {
vars.normalizedDebt = currentReserve.getNormalizedDebt();
vars.userDebt = vars.userDebt.rayMul(vars.normalizedDebt);
}
vars.userDebt = vars.userDebt.add(
IERC20(currentReserve.stableDebtTokenAddress).balanceOf(user)
);
vars.userDebt = vars.userDebt.add(vars.userStableDebt);
vars.userDebtETH = vars.assetPrice.mul(vars.userDebt).div(vars.assetUnit);
vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH);
}
}
vars.avgLtv = vars.totalCollateralInETH > 0 ? vars.avgLtv.div(vars.totalCollateralInETH) : 0;
vars.avgUncappedLtv = vars.totalCollateralInETH > 0
? vars.avgUncappedLtv.div(vars.totalCollateralInETH)
: 0;
vars.avgLiquidationThreshold = vars.totalCollateralInETH > 0
? vars.avgLiquidationThreshold.div(vars.totalCollateralInETH)
: 0;
@ -169,7 +176,8 @@ library GenericLogic {
vars.totalDebtInETH,
vars.avgLtv,
vars.avgLiquidationThreshold,
vars.healthFactor
vars.healthFactor,
vars.avgUncappedLtv
);
}
@ -239,6 +247,7 @@ library GenericLogic {
uint256,
uint256,
uint256,
uint256,
uint256
)
{

View File

@ -185,7 +185,7 @@ library ValidationLogic {
vars.userBorrowBalanceETH,
vars.currentLtv,
vars.currentLiquidationThreshold,
vars.healthFactor
vars.healthFactor,
) = GenericLogic.calculateUserAccountData(
userAddress,
reservesData,
@ -263,7 +263,7 @@ library ValidationLogic {
address onBehalfOf,
uint256 stableDebt,
uint256 variableDebt
) internal view {
) external view {
(bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory();
require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
require(!isPaused, Errors.VL_RESERVE_PAUSED);
@ -469,7 +469,7 @@ library ValidationLogic {
return (uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), Errors.VL_RESERVE_PAUSED);
}
(, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
(, , , , vars.healthFactor, ) = GenericLogic.calculateUserAccountData(
user,
reservesData,
userConfig,
@ -508,7 +508,7 @@ library ValidationLogic {
}
/**
* @dev Validates the health factor of a user
* @dev Validates the health factor of a user and the exposure cap for the asset being withdrawn
* @param from The user from which the aTokens are being transferred
* @param reservesData The state of all the reserves
* @param userConfig The state of the user for the specific reserve
@ -516,7 +516,7 @@ library ValidationLogic {
* @param reservesCount The number of available reserves
* @param oracle The price oracle
*/
function validateWithdrawCollateral(
function validateHFAndExposureCap(
address collateral,
address from,
mapping(address => DataTypes.ReserveData) storage reservesData,
@ -526,7 +526,7 @@ library ValidationLogic {
address oracle
) external view {
DataTypes.ReserveData memory reserve = reservesData[collateral];
(, , uint256 ltv, uint256 liquidationThreshold, uint256 healthFactor) =
(, , uint256 ltv, uint256 liquidationThreshold, uint256 healthFactor, uint256 uncappedLtv) =
GenericLogic.calculateUserAccountData(
from,
reservesData,
@ -536,28 +536,22 @@ library ValidationLogic {
oracle
);
uint256 exposureCap = reserve.configuration.getExposureCapMemory();
uint256 totalSupplyAtoken = IERC20(reserve.aTokenAddress).totalSupply();
(, , , uint256 reserveDecimals, ) = reserve.configuration.getParamsMemory();
require(
healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
);
// exposureCap == 0 means no cap => can withdraw
// ltv > liquidationThreshold means that there is enough collateral margin => can withdraw
// ltv == 0 means all current collaterals have exceeded the exposure cap => can withdraw
// last means that for this asset the cap is not yet exceeded => can withdraw
// else this means the user is trying to withdraw a collateral that has exceeded the exposure cap, and that it
// as other collaterals available to withdraw: he must withdraw from other collateral reserves first
require(
exposureCap == 0 ||
ltv > liquidationThreshold ||
ltv == 0 ||
totalSupplyAtoken.div(10**reserveDecimals) < exposureCap,
Errors.VL_COLLATERAL_EXPOSURE_CAP_EXCEEDED
);
uint256 exposureCap = reserve.configuration.getExposureCapMemory();
if (exposureCap != 0) {
if (ltv < uncappedLtv) {
uint256 totalSupplyAtoken = IERC20(reserve.aTokenAddress).totalSupply();
(, , , uint256 reserveDecimals, ) = reserve.configuration.getParamsMemory();
bool isAssetCapped = totalSupplyAtoken.div(10**reserveDecimals) >= exposureCap;
require(isAssetCapped, Errors.VL_COLLATERAL_EXPOSURE_CAP_EXCEEDED);
}
}
}
/**