Branch data Line data Source code
1 : : // SPDX-License-Identifier: BUSL-1.1
2 : : pragma solidity ^0.8.10;
3 : :
4 : : import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
5 : : import {Address} from '../../../dependencies/openzeppelin/contracts/Address.sol';
6 : : import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
7 : : import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
8 : : import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
9 : : import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
10 : : import {IAToken} from '../../../interfaces/IAToken.sol';
11 : : import {IPriceOracleSentinel} from '../../../interfaces/IPriceOracleSentinel.sol';
12 : : import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
13 : : import {IAccessControl} from '../../../dependencies/openzeppelin/contracts/IAccessControl.sol';
14 : : import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
15 : : import {UserConfiguration} from '../configuration/UserConfiguration.sol';
16 : : import {EModeConfiguration} from '../configuration/EModeConfiguration.sol';
17 : : import {Errors} from '../helpers/Errors.sol';
18 : : import {WadRayMath} from '../math/WadRayMath.sol';
19 : : import {PercentageMath} from '../math/PercentageMath.sol';
20 : : import {DataTypes} from '../types/DataTypes.sol';
21 : : import {ReserveLogic} from './ReserveLogic.sol';
22 : : import {GenericLogic} from './GenericLogic.sol';
23 : : import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
24 : : import {IncentivizedERC20} from '../../tokenization/base/IncentivizedERC20.sol';
25 : :
26 : : /**
27 : : * @title ReserveLogic library
28 : : * @author Aave
29 : : * @notice Implements functions to validate the different actions of the protocol
30 : : */
31 : : library ValidationLogic {
32 : : using ReserveLogic for DataTypes.ReserveData;
33 : : using WadRayMath for uint256;
34 : : using PercentageMath for uint256;
35 : : using SafeCast for uint256;
36 : : using GPv2SafeERC20 for IERC20;
37 : : using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
38 : : using UserConfiguration for DataTypes.UserConfigurationMap;
39 : : using Address for address;
40 : :
41 : : // Factor to apply to "only-variable-debt" liquidity rate to get threshold for rebalancing, expressed in bps
42 : : // A value of 0.9e4 results in 90%
43 : : uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 0.9e4;
44 : :
45 : : // Minimum health factor allowed under any circumstance
46 : : // A value of 0.95e18 results in 0.95
47 : : uint256 public constant MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 0.95e18;
48 : :
49 : : /**
50 : : * @dev Minimum health factor to consider a user position healthy
51 : : * A value of 1e18 results in 1
52 : : */
53 : : uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18;
54 : :
55 : : /**
56 : : * @dev Role identifier for the role allowed to supply isolated reserves as collateral
57 : : */
58 : : bytes32 public constant ISOLATED_COLLATERAL_SUPPLIER_ROLE =
59 : : keccak256('ISOLATED_COLLATERAL_SUPPLIER');
60 : :
61 : : /**
62 : : * @notice Validates a supply action.
63 : : * @param reserveCache The cached data of the reserve
64 : : * @param amount The amount to be supplied
65 : : */
66 : : function validateSupply(
67 : : DataTypes.ReserveCache memory reserveCache,
68 : : DataTypes.ReserveData storage reserve,
69 : : uint256 amount,
70 : : address onBehalfOf
71 : : ) internal view {
72 : 44531 : require(amount != 0, Errors.INVALID_AMOUNT);
73 : :
74 : 44530 : (bool isActive, bool isFrozen, , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
75 : 44530 : require(isActive, Errors.RESERVE_INACTIVE);
76 : 44529 : require(!isPaused, Errors.RESERVE_PAUSED);
77 : 44528 : require(!isFrozen, Errors.RESERVE_FROZEN);
78 : 44527 : require(onBehalfOf != reserveCache.aTokenAddress, Errors.SUPPLY_TO_ATOKEN);
79 : :
80 : 44526 : uint256 supplyCap = reserveCache.reserveConfiguration.getSupplyCap();
81 : 44526 : require(
82 : : supplyCap == 0 ||
83 : : ((IAToken(reserveCache.aTokenAddress).scaledTotalSupply() +
84 : : uint256(reserve.accruedToTreasury)).rayMul(reserveCache.nextLiquidityIndex) + amount) <=
85 : : supplyCap * (10 ** reserveCache.reserveConfiguration.getDecimals()),
86 : : Errors.SUPPLY_CAP_EXCEEDED
87 : : );
88 : : }
89 : :
90 : : /**
91 : : * @notice Validates a withdraw action.
92 : : * @param reserveCache The cached data of the reserve
93 : : * @param amount The amount to be withdrawn
94 : : * @param userBalance The balance of the user
95 : : */
96 : : function validateWithdraw(
97 : : DataTypes.ReserveCache memory reserveCache,
98 : : uint256 amount,
99 : : uint256 userBalance
100 : : ) internal pure {
101 : 2044 : require(amount != 0, Errors.INVALID_AMOUNT);
102 : 2043 : require(amount <= userBalance, Errors.NOT_ENOUGH_AVAILABLE_USER_BALANCE);
103 : :
104 : 2042 : (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
105 : 2042 : require(isActive, Errors.RESERVE_INACTIVE);
106 : 2041 : require(!isPaused, Errors.RESERVE_PAUSED);
107 : : }
108 : :
109 : : struct ValidateBorrowLocalVars {
110 : : uint256 currentLtv;
111 : : uint256 collateralNeededInBaseCurrency;
112 : : uint256 userCollateralInBaseCurrency;
113 : : uint256 userDebtInBaseCurrency;
114 : : uint256 availableLiquidity;
115 : : uint256 healthFactor;
116 : : uint256 totalDebt;
117 : : uint256 totalSupplyVariableDebt;
118 : : uint256 reserveDecimals;
119 : : uint256 borrowCap;
120 : : uint256 amountInBaseCurrency;
121 : : uint256 assetUnit;
122 : : address siloedBorrowingAddress;
123 : : bool isActive;
124 : : bool isFrozen;
125 : : bool isPaused;
126 : : bool borrowingEnabled;
127 : : bool siloedBorrowingEnabled;
128 : : }
129 : :
130 : : /**
131 : : * @notice Validates a borrow action.
132 : : * @param reservesData The state of all the reserves
133 : : * @param reservesList The addresses of all the active reserves
134 : : * @param eModeCategories The configuration of all the efficiency mode categories
135 : : * @param params Additional params needed for the validation
136 : : */
137 : : function validateBorrow(
138 : : mapping(address => DataTypes.ReserveData) storage reservesData,
139 : : mapping(uint256 => address) storage reservesList,
140 : : mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
141 : : DataTypes.ValidateBorrowParams memory params
142 : : ) internal view {
143 : 16110 : require(params.amount != 0, Errors.INVALID_AMOUNT);
144 : :
145 : 16109 : ValidateBorrowLocalVars memory vars;
146 : :
147 : 16109 : (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.isPaused) = params
148 : : .reserveCache
149 : : .reserveConfiguration
150 : : .getFlags();
151 : :
152 : 16109 : require(vars.isActive, Errors.RESERVE_INACTIVE);
153 : 16108 : require(!vars.isPaused, Errors.RESERVE_PAUSED);
154 : 16107 : require(!vars.isFrozen, Errors.RESERVE_FROZEN);
155 : 16106 : require(vars.borrowingEnabled, Errors.BORROWING_NOT_ENABLED);
156 : 15106 : require(
157 : : !params.reserveCache.reserveConfiguration.getIsVirtualAccActive() ||
158 : : IERC20(params.reserveCache.aTokenAddress).totalSupply() >= params.amount,
159 : : Errors.INVALID_AMOUNT
160 : : );
161 : :
162 : 15105 : require(
163 : : params.priceOracleSentinel == address(0) ||
164 : : IPriceOracleSentinel(params.priceOracleSentinel).isBorrowAllowed(),
165 : : Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
166 : : );
167 : :
168 : : //validate interest rate mode
169 : 15104 : require(
170 : : params.interestRateMode == DataTypes.InterestRateMode.VARIABLE,
171 : : Errors.INVALID_INTEREST_RATE_MODE_SELECTED
172 : : );
173 : :
174 : 15102 : vars.reserveDecimals = params.reserveCache.reserveConfiguration.getDecimals();
175 : 15102 : vars.borrowCap = params.reserveCache.reserveConfiguration.getBorrowCap();
176 : : unchecked {
177 : 15102 : vars.assetUnit = 10 ** vars.reserveDecimals;
178 : : }
179 : :
180 : 15102 : if (vars.borrowCap != 0) {
181 : 7 : vars.totalSupplyVariableDebt = params.reserveCache.currScaledVariableDebt.rayMul(
182 : : params.reserveCache.nextVariableBorrowIndex
183 : : );
184 : :
185 : 7 : vars.totalDebt = vars.totalSupplyVariableDebt + params.amount;
186 : :
187 : : unchecked {
188 : 7 : require(vars.totalDebt <= vars.borrowCap * vars.assetUnit, Errors.BORROW_CAP_EXCEEDED);
189 : : }
190 : : }
191 : :
192 : 15099 : if (params.isolationModeActive) {
193 : : // check that the asset being borrowed is borrowable in isolation mode AND
194 : : // the total exposure is no bigger than the collateral debt ceiling
195 : 11 : require(
196 : : params.reserveCache.reserveConfiguration.getBorrowableInIsolation(),
197 : : Errors.ASSET_NOT_BORROWABLE_IN_ISOLATION
198 : : );
199 : :
200 : 10 : require(
201 : : reservesData[params.isolationModeCollateralAddress].isolationModeTotalDebt +
202 : : (params.amount /
203 : : 10 ** (vars.reserveDecimals - ReserveConfiguration.DEBT_CEILING_DECIMALS))
204 : : .toUint128() <=
205 : : params.isolationModeDebtCeiling,
206 : : Errors.DEBT_CEILING_EXCEEDED
207 : : );
208 : : }
209 : :
210 : 15096 : if (params.userEModeCategory != 0) {
211 : 4006 : require(
212 : : EModeConfiguration.isReserveEnabledOnBitmap(
213 : : eModeCategories[params.userEModeCategory].borrowableBitmap,
214 : : reservesData[params.asset].id
215 : : ),
216 : : Errors.NOT_BORROWABLE_IN_EMODE
217 : : );
218 : : }
219 : :
220 : 15095 : (
221 : : vars.userCollateralInBaseCurrency,
222 : : vars.userDebtInBaseCurrency,
223 : : vars.currentLtv,
224 : : ,
225 : : vars.healthFactor,
226 : :
227 : : ) = GenericLogic.calculateUserAccountData(
228 : : reservesData,
229 : : reservesList,
230 : : eModeCategories,
231 : : DataTypes.CalculateUserAccountDataParams({
232 : : userConfig: params.userConfig,
233 : : reservesCount: params.reservesCount,
234 : : user: params.userAddress,
235 : : oracle: params.oracle,
236 : : userEModeCategory: params.userEModeCategory
237 : : })
238 : : );
239 : :
240 : 15095 : require(vars.userCollateralInBaseCurrency != 0, Errors.COLLATERAL_BALANCE_IS_ZERO);
241 : 15094 : require(vars.currentLtv != 0, Errors.LTV_VALIDATION_FAILED);
242 : :
243 : 15094 : require(
244 : : vars.healthFactor > HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
245 : : Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
246 : : );
247 : :
248 : 15093 : vars.amountInBaseCurrency =
249 : : IPriceOracleGetter(params.oracle).getAssetPrice(params.asset) *
250 : : params.amount;
251 : : unchecked {
252 : 15093 : vars.amountInBaseCurrency /= vars.assetUnit;
253 : : }
254 : :
255 : : //add the current already borrowed amount to the amount requested to calculate the total collateral needed.
256 : 15093 : vars.collateralNeededInBaseCurrency = (vars.userDebtInBaseCurrency + vars.amountInBaseCurrency)
257 : : .percentDiv(vars.currentLtv); //LTV is calculated in percentage
258 : :
259 : 15093 : require(
260 : : vars.collateralNeededInBaseCurrency <= vars.userCollateralInBaseCurrency,
261 : : Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW
262 : : );
263 : :
264 : 15092 : if (params.userConfig.isBorrowingAny()) {
265 : 7 : (vars.siloedBorrowingEnabled, vars.siloedBorrowingAddress) = params
266 : : .userConfig
267 : : .getSiloedBorrowingState(reservesData, reservesList);
268 : :
269 : 7 : if (vars.siloedBorrowingEnabled) {
270 : 1 : require(vars.siloedBorrowingAddress == params.asset, Errors.SILOED_BORROWING_VIOLATION);
271 : : } else {
272 : 6 : require(
273 : : !params.reserveCache.reserveConfiguration.getSiloedBorrowing(),
274 : : Errors.SILOED_BORROWING_VIOLATION
275 : : );
276 : : }
277 : : }
278 : : }
279 : :
280 : : /**
281 : : * @notice Validates a repay action.
282 : : * @param reserveCache The cached data of the reserve
283 : : * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1)
284 : : * @param onBehalfOf The address of the user msg.sender is repaying for
285 : : * @param debt The borrow balance of the user
286 : : */
287 : : function validateRepay(
288 : : DataTypes.ReserveCache memory reserveCache,
289 : : uint256 amountSent,
290 : : DataTypes.InterestRateMode interestRateMode,
291 : : address onBehalfOf,
292 : : uint256 debt
293 : : ) internal view {
294 : 5030 : require(amountSent != 0, Errors.INVALID_AMOUNT);
295 : 5029 : require(
296 : : interestRateMode == DataTypes.InterestRateMode.VARIABLE,
297 : : Errors.INVALID_INTEREST_RATE_MODE_SELECTED
298 : : );
299 : 5028 : require(
300 : : amountSent != type(uint256).max || msg.sender == onBehalfOf,
301 : : Errors.NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF
302 : : );
303 : :
304 : 5027 : (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
305 : 5027 : require(isActive, Errors.RESERVE_INACTIVE);
306 : 5026 : require(!isPaused, Errors.RESERVE_PAUSED);
307 : :
308 : 5025 : require(debt != 0, Errors.NO_DEBT_OF_SELECTED_TYPE);
309 : : }
310 : :
311 : : /**
312 : : * @notice Validates the action of setting an asset as collateral.
313 : : * @param reserveCache The cached data of the reserve
314 : : * @param userBalance The balance of the user
315 : : */
316 : : function validateSetUseReserveAsCollateral(
317 : : DataTypes.ReserveCache memory reserveCache,
318 : : uint256 userBalance
319 : : ) internal pure {
320 : 43 : require(userBalance != 0, Errors.UNDERLYING_BALANCE_ZERO);
321 : :
322 : 41 : (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
323 : 41 : require(isActive, Errors.RESERVE_INACTIVE);
324 : 39 : require(!isPaused, Errors.RESERVE_PAUSED);
325 : : }
326 : :
327 : : /**
328 : : * @notice Validates a flashloan action.
329 : : * @param reservesData The state of all the reserves
330 : : * @param assets The assets being flash-borrowed
331 : : * @param amounts The amounts for each asset being borrowed
332 : : */
333 : : function validateFlashloan(
334 : : mapping(address => DataTypes.ReserveData) storage reservesData,
335 : : address[] memory assets,
336 : : uint256[] memory amounts
337 : : ) internal view {
338 : 1011 : require(assets.length == amounts.length, Errors.INCONSISTENT_FLASHLOAN_PARAMS);
339 : 1011 : for (uint256 i = 0; i < assets.length; i++) {
340 : 1012 : for (uint256 j = i + 1; j < assets.length; j++) {
341 : 1001 : require(assets[i] != assets[j], Errors.INCONSISTENT_FLASHLOAN_PARAMS);
342 : : }
343 : 12 : validateFlashloanSimple(reservesData[assets[i]], amounts[i]);
344 : : }
345 : : }
346 : :
347 : : /**
348 : : * @notice Validates a flashloan action.
349 : : * @param reserve The state of the reserve
350 : : */
351 : : function validateFlashloanSimple(
352 : : DataTypes.ReserveData storage reserve,
353 : : uint256 amount
354 : : ) internal view {
355 : 23 : DataTypes.ReserveConfigurationMap memory configuration = reserve.configuration;
356 : 23 : require(!configuration.getPaused(), Errors.RESERVE_PAUSED);
357 : 22 : require(configuration.getActive(), Errors.RESERVE_INACTIVE);
358 : 21 : require(configuration.getFlashLoanEnabled(), Errors.FLASHLOAN_DISABLED);
359 : 20 : require(
360 : : !configuration.getIsVirtualAccActive() ||
361 : : IERC20(reserve.aTokenAddress).totalSupply() >= amount,
362 : : Errors.INVALID_AMOUNT
363 : : );
364 : : }
365 : :
366 : : struct ValidateLiquidationCallLocalVars {
367 : : bool collateralReserveActive;
368 : : bool collateralReservePaused;
369 : : bool principalReserveActive;
370 : : bool principalReservePaused;
371 : : bool isCollateralEnabled;
372 : : }
373 : :
374 : : /**
375 : : * @notice Validates the liquidation action.
376 : : * @param userConfig The user configuration mapping
377 : : * @param collateralReserve The reserve data of the collateral
378 : : * @param debtReserve The reserve data of the debt
379 : : * @param params Additional parameters needed for the validation
380 : : */
381 : : function validateLiquidationCall(
382 : : DataTypes.UserConfigurationMap storage userConfig,
383 : : DataTypes.ReserveData storage collateralReserve,
384 : : DataTypes.ReserveData storage debtReserve,
385 : : DataTypes.ValidateLiquidationCallParams memory params
386 : : ) internal view {
387 : 14501 : ValidateLiquidationCallLocalVars memory vars;
388 : :
389 : 14501 : (vars.collateralReserveActive, , , vars.collateralReservePaused) = collateralReserve
390 : : .configuration
391 : : .getFlags();
392 : :
393 : 14501 : (vars.principalReserveActive, , , vars.principalReservePaused) = params
394 : : .debtReserveCache
395 : : .reserveConfiguration
396 : : .getFlags();
397 : :
398 : 14501 : require(vars.collateralReserveActive && vars.principalReserveActive, Errors.RESERVE_INACTIVE);
399 : 14500 : require(!vars.collateralReservePaused && !vars.principalReservePaused, Errors.RESERVE_PAUSED);
400 : :
401 : 14499 : require(
402 : : params.priceOracleSentinel == address(0) ||
403 : : params.healthFactor < MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD ||
404 : : IPriceOracleSentinel(params.priceOracleSentinel).isLiquidationAllowed(),
405 : : Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
406 : : );
407 : :
408 : 14498 : require(
409 : : collateralReserve.liquidationGracePeriodUntil < uint40(block.timestamp) &&
410 : : debtReserve.liquidationGracePeriodUntil < uint40(block.timestamp),
411 : : Errors.LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED
412 : : );
413 : :
414 : 8014 : require(
415 : : params.healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
416 : : Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD
417 : : );
418 : :
419 : 8013 : vars.isCollateralEnabled =
420 : : collateralReserve.configuration.getLiquidationThreshold() != 0 &&
421 : : userConfig.isUsingAsCollateral(collateralReserve.id);
422 : :
423 : : //if collateral isn't enabled as collateral by user, it cannot be liquidated
424 : 8013 : require(vars.isCollateralEnabled, Errors.COLLATERAL_CANNOT_BE_LIQUIDATED);
425 : 8012 : require(params.totalDebt != 0, Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER);
426 : : }
427 : :
428 : : /**
429 : : * @notice Validates the health factor of a user.
430 : : * @param reservesData The state of all the reserves
431 : : * @param reservesList The addresses of all the active reserves
432 : : * @param eModeCategories The configuration of all the efficiency mode categories
433 : : * @param userConfig The state of the user for the specific reserve
434 : : * @param user The user to validate health factor of
435 : : * @param userEModeCategory The users active efficiency mode category
436 : : * @param reservesCount The number of available reserves
437 : : * @param oracle The price oracle
438 : : */
439 : : function validateHealthFactor(
440 : : mapping(address => DataTypes.ReserveData) storage reservesData,
441 : : mapping(uint256 => address) storage reservesList,
442 : : mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
443 : : DataTypes.UserConfigurationMap memory userConfig,
444 : : address user,
445 : : uint8 userEModeCategory,
446 : : uint256 reservesCount,
447 : : address oracle
448 : : ) internal view returns (uint256, bool) {
449 : 8637 : (, , , , uint256 healthFactor, bool hasZeroLtvCollateral) = GenericLogic
450 : : .calculateUserAccountData(
451 : : reservesData,
452 : : reservesList,
453 : : eModeCategories,
454 : : DataTypes.CalculateUserAccountDataParams({
455 : : userConfig: userConfig,
456 : : reservesCount: reservesCount,
457 : : user: user,
458 : : oracle: oracle,
459 : : userEModeCategory: userEModeCategory
460 : : })
461 : : );
462 : :
463 : 8637 : require(
464 : : healthFactor >= HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
465 : : Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
466 : : );
467 : :
468 : 7631 : return (healthFactor, hasZeroLtvCollateral);
469 : : }
470 : :
471 : : /**
472 : : * @notice Validates the health factor of a user and the ltv of the asset being withdrawn.
473 : : * @param reservesData The state of all the reserves
474 : : * @param reservesList The addresses of all the active reserves
475 : : * @param eModeCategories The configuration of all the efficiency mode categories
476 : : * @param userConfig The state of the user for the specific reserve
477 : : * @param asset The asset for which the ltv will be validated
478 : : * @param from The user from which the aTokens are being transferred
479 : : * @param reservesCount The number of available reserves
480 : : * @param oracle The price oracle
481 : : * @param userEModeCategory The users active efficiency mode category
482 : : */
483 : : function validateHFAndLtv(
484 : : mapping(address => DataTypes.ReserveData) storage reservesData,
485 : : mapping(uint256 => address) storage reservesList,
486 : : mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
487 : : DataTypes.UserConfigurationMap memory userConfig,
488 : : address asset,
489 : : address from,
490 : : uint256 reservesCount,
491 : : address oracle,
492 : : uint8 userEModeCategory
493 : : ) internal view {
494 : 23 : DataTypes.ReserveData memory reserve = reservesData[asset];
495 : :
496 : 23 : (, bool hasZeroLtvCollateral) = validateHealthFactor(
497 : : reservesData,
498 : : reservesList,
499 : : eModeCategories,
500 : : userConfig,
501 : : from,
502 : : userEModeCategory,
503 : : reservesCount,
504 : : oracle
505 : : );
506 : :
507 : 19 : require(
508 : : !hasZeroLtvCollateral || reserve.configuration.getLtv() == 0,
509 : : Errors.LTV_VALIDATION_FAILED
510 : : );
511 : : }
512 : :
513 : : /**
514 : : * @notice Validates a transfer action.
515 : : * @param reserve The reserve object
516 : : */
517 : : function validateTransfer(DataTypes.ReserveData storage reserve) internal view {
518 : 17042 : require(!reserve.configuration.getPaused(), Errors.RESERVE_PAUSED);
519 : : }
520 : :
521 : : /**
522 : : * @notice Validates a drop reserve action.
523 : : * @param reservesList The addresses of all the active reserves
524 : : * @param reserve The reserve object
525 : : * @param asset The address of the reserve's underlying asset
526 : : */
527 : : function validateDropReserve(
528 : : mapping(uint256 => address) storage reservesList,
529 : : DataTypes.ReserveData storage reserve,
530 : : address asset
531 : : ) internal view {
532 : 9 : require(asset != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
533 : 8 : require(reserve.id != 0 || reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
534 : 7 : require(
535 : : IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0,
536 : : Errors.VARIABLE_DEBT_SUPPLY_NOT_ZERO
537 : : );
538 : 6 : require(
539 : : IERC20(reserve.aTokenAddress).totalSupply() == 0 && reserve.accruedToTreasury == 0,
540 : : Errors.UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO
541 : : );
542 : : }
543 : :
544 : : /**
545 : : * @notice Validates the action of setting efficiency mode.
546 : : * @param eModeCategories a mapping storing configurations for all efficiency mode categories
547 : : * @param userConfig the user configuration
548 : : * @param reservesCount The total number of valid reserves
549 : : * @param categoryId The id of the category
550 : : */
551 : : function validateSetUserEMode(
552 : : mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
553 : : DataTypes.UserConfigurationMap memory userConfig,
554 : : uint256 reservesCount,
555 : : uint8 categoryId
556 : : ) internal view {
557 : 9617 : DataTypes.EModeCategory storage eModeCategory = eModeCategories[categoryId];
558 : : // category is invalid if the liq threshold is not set
559 : 9617 : require(
560 : : categoryId == 0 || eModeCategory.liquidationThreshold != 0,
561 : : Errors.INCONSISTENT_EMODE_CATEGORY
562 : : );
563 : :
564 : : // eMode can always be enabled if the user hasn't supplied anything
565 : 9616 : if (userConfig.isEmpty()) {
566 : 6611 : return;
567 : : }
568 : :
569 : : // if user is trying to set another category than default we require that
570 : : // either the user is not borrowing, or it's borrowing assets of categoryId
571 : 3005 : if (categoryId != 0) {
572 : : unchecked {
573 : 3002 : for (uint256 i = 0; i < reservesCount; i++) {
574 : 8006 : if (userConfig.isBorrowing(i)) {
575 : 3002 : require(
576 : : EModeConfiguration.isReserveEnabledOnBitmap(eModeCategory.borrowableBitmap, i),
577 : : Errors.NOT_BORROWABLE_IN_EMODE
578 : : );
579 : : }
580 : : }
581 : : }
582 : : }
583 : : }
584 : :
585 : : /**
586 : : * @notice Validates the action of activating the asset as collateral.
587 : : * @dev Only possible if the asset has non-zero LTV and the user is not in isolation mode
588 : : * @param reservesData The state of all the reserves
589 : : * @param reservesList The addresses of all the active reserves
590 : : * @param userConfig the user configuration
591 : : * @param reserveConfig The reserve configuration
592 : : * @return True if the asset can be activated as collateral, false otherwise
593 : : */
594 : : function validateUseAsCollateral(
595 : : mapping(address => DataTypes.ReserveData) storage reservesData,
596 : : mapping(uint256 => address) storage reservesList,
597 : : DataTypes.UserConfigurationMap storage userConfig,
598 : : DataTypes.ReserveConfigurationMap memory reserveConfig
599 : : ) internal view returns (bool) {
600 : 59411 : if (reserveConfig.getLtv() == 0) {
601 : 3 : return false;
602 : : }
603 : 59408 : if (!userConfig.isUsingAsCollateralAny()) {
604 : 58324 : return true;
605 : : }
606 : 1084 : (bool isolationModeActive, , ) = userConfig.getIsolationModeState(reservesData, reservesList);
607 : :
608 : 1084 : return (!isolationModeActive && reserveConfig.getDebtCeiling() == 0);
609 : : }
610 : :
611 : : /**
612 : : * @notice Validates if an asset should be automatically activated as collateral in the following actions: supply,
613 : : * transfer, mint unbacked, and liquidate
614 : : * @dev This is used to ensure that isolated assets are not enabled as collateral automatically
615 : : * @param reservesData The state of all the reserves
616 : : * @param reservesList The addresses of all the active reserves
617 : : * @param userConfig the user configuration
618 : : * @param reserveConfig The reserve configuration
619 : : * @return True if the asset can be activated as collateral, false otherwise
620 : : */
621 : : function validateAutomaticUseAsCollateral(
622 : : mapping(address => DataTypes.ReserveData) storage reservesData,
623 : : mapping(uint256 => address) storage reservesList,
624 : : DataTypes.UserConfigurationMap storage userConfig,
625 : : DataTypes.ReserveConfigurationMap memory reserveConfig,
626 : : address aTokenAddress
627 : : ) internal view returns (bool) {
628 : 59404 : if (reserveConfig.getDebtCeiling() != 0) {
629 : : // ensures only the ISOLATED_COLLATERAL_SUPPLIER_ROLE can enable collateral as side-effect of an action
630 : 12 : IPoolAddressesProvider addressesProvider = IncentivizedERC20(aTokenAddress)
631 : : .POOL()
632 : : .ADDRESSES_PROVIDER();
633 : : if (
634 : 12 : !IAccessControl(addressesProvider.getACLManager()).hasRole(
635 : : ISOLATED_COLLATERAL_SUPPLIER_ROLE,
636 : : msg.sender
637 : : )
638 : 12 : ) return false;
639 : : }
640 : 59392 : return validateUseAsCollateral(reservesData, reservesList, userConfig, reserveConfig);
641 : : }
642 : : }
|