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

View File

@ -34,6 +34,7 @@ library GenericLogic {
uint256 userBalance; uint256 userBalance;
uint256 userBalanceETH; uint256 userBalanceETH;
uint256 userDebt; uint256 userDebt;
uint256 userStableDebt;
uint256 userDebtETH; uint256 userDebtETH;
uint256 decimals; uint256 decimals;
uint256 ltv; uint256 ltv;
@ -43,6 +44,7 @@ library GenericLogic {
uint256 totalCollateralInETH; uint256 totalCollateralInETH;
uint256 totalDebtInETH; uint256 totalDebtInETH;
uint256 avgLtv; uint256 avgLtv;
uint256 avgUncappedLtv;
uint256 avgLiquidationThreshold; uint256 avgLiquidationThreshold;
uint256 reservesLength; uint256 reservesLength;
uint256 normalizedIncome; uint256 normalizedIncome;
@ -65,7 +67,7 @@ library GenericLogic {
* @param userConfig The configuration of the user * @param userConfig The configuration of the user
* @param reserves The list of the available reserves * @param reserves The list of the available reserves
* @param oracle The price oracle address * @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( function calculateUserAccountData(
address user, address user,
@ -82,13 +84,14 @@ library GenericLogic {
uint256, uint256,
uint256, uint256,
uint256, uint256,
uint256,
uint256 uint256
) )
{ {
CalculateUserAccountDataVars memory vars; CalculateUserAccountDataVars memory vars;
if (userConfig.isEmpty()) { 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++) { for (vars.i = 0; vars.i < reservesCount; vars.i++) {
if (!userConfig.isUsingAsCollateralOrBorrowing(vars.i)) { if (!userConfig.isUsingAsCollateralOrBorrowing(vars.i)) {
@ -131,13 +134,17 @@ library GenericLogic {
vars.exposureCap != 0 && vars.exposureCap != 0 &&
vars.aTokenSupply.div(10**vars.decimals) > vars.exposureCap; 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.avgLiquidationThreshold = vars.avgLiquidationThreshold.add(
vars.userBalanceETH.mul(vars.liquidationThreshold) vars.userBalanceETH.mul(vars.liquidationThreshold)
); );
} }
if (userConfig.isBorrowing(vars.i)) { if (userConfig.isBorrowing(vars.i)) {
vars.userStableDebt = IERC20(currentReserve.stableDebtTokenAddress).balanceOf(user);
vars.userDebt = IScaledBalanceToken(currentReserve.variableDebtTokenAddress) vars.userDebt = IScaledBalanceToken(currentReserve.variableDebtTokenAddress)
.scaledBalanceOf(user); .scaledBalanceOf(user);
@ -145,16 +152,16 @@ library GenericLogic {
vars.normalizedDebt = currentReserve.getNormalizedDebt(); vars.normalizedDebt = currentReserve.getNormalizedDebt();
vars.userDebt = vars.userDebt.rayMul(vars.normalizedDebt); vars.userDebt = vars.userDebt.rayMul(vars.normalizedDebt);
} }
vars.userDebt = vars.userDebt.add(vars.userStableDebt);
vars.userDebt = vars.userDebt.add(
IERC20(currentReserve.stableDebtTokenAddress).balanceOf(user)
);
vars.userDebtETH = vars.assetPrice.mul(vars.userDebt).div(vars.assetUnit); vars.userDebtETH = vars.assetPrice.mul(vars.userDebt).div(vars.assetUnit);
vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH); vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH);
} }
} }
vars.avgLtv = vars.totalCollateralInETH > 0 ? vars.avgLtv.div(vars.totalCollateralInETH) : 0; 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 = vars.totalCollateralInETH > 0
? vars.avgLiquidationThreshold.div(vars.totalCollateralInETH) ? vars.avgLiquidationThreshold.div(vars.totalCollateralInETH)
: 0; : 0;
@ -169,7 +176,8 @@ library GenericLogic {
vars.totalDebtInETH, vars.totalDebtInETH,
vars.avgLtv, vars.avgLtv,
vars.avgLiquidationThreshold, vars.avgLiquidationThreshold,
vars.healthFactor vars.healthFactor,
vars.avgUncappedLtv
); );
} }
@ -239,6 +247,7 @@ library GenericLogic {
uint256, uint256,
uint256, uint256,
uint256, uint256,
uint256,
uint256 uint256
) )
{ {

View File

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