diff --git a/contracts/fees/TokenDistributor.sol b/contracts/fees/TokenDistributor.sol index a36a707c..6591ddc6 100644 --- a/contracts/fees/TokenDistributor.sol +++ b/contracts/fees/TokenDistributor.sol @@ -9,6 +9,7 @@ import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '../libraries/openzeppelin-upgradeability/VersionedInitializable.sol'; import '../interfaces/IExchangeAdapter.sol'; import '../libraries/UniversalERC20.sol'; +import {PercentageMath} from '../libraries/PercentageMath.sol'; /// @title TokenDistributor /// @author Aave @@ -22,6 +23,7 @@ import '../libraries/UniversalERC20.sol'; /// and burn it (sending to address(0) the tokenToBurn) contract TokenDistributor is ReentrancyGuard, VersionedInitializable { using SafeMath for uint256; + using PercentageMath for uint256; using UniversalERC20 for IERC20; struct Distribution { @@ -126,10 +128,9 @@ contract TokenDistributor is ReentrancyGuard, VersionedInitializable { public { for (uint256 i = 0; i < _tokens.length; i++) { - uint256 _amountToDistribute = _tokens[i] - .universalBalanceOf(address(this)) - .mul(_percentages[i]) - .div(100); + uint256 _amountToDistribute = _tokens[i].universalBalanceOf(address(this)).percentMul( + _percentages[i] + ); if (_amountToDistribute <= 0) { continue; diff --git a/contracts/lendingpool/LendingPoolLiquidationManager.sol b/contracts/lendingpool/LendingPoolLiquidationManager.sol index c54aee6f..e6b4e50a 100644 --- a/contracts/lendingpool/LendingPoolLiquidationManager.sol +++ b/contracts/lendingpool/LendingPoolLiquidationManager.sol @@ -20,6 +20,7 @@ import '../libraries/UserLogic.sol'; import '../libraries/ReserveLogic.sol'; import '../libraries/UniversalERC20.sol'; import '../libraries/ReserveConfiguration.sol'; +import {PercentageMath} from '../libraries/PercentageMath.sol'; /** * @title LendingPoolLiquidationManager contract @@ -30,6 +31,7 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl using UniversalERC20 for IERC20; using SafeMath for uint256; using WadRayMath for uint256; + using PercentageMath for uint256; using Address for address; using ReserveLogic for ReserveLogic.ReserveData; using UserLogic for UserLogic.UserReserveData; @@ -43,7 +45,7 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl address[] public reservesList; - uint256 constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 50; + uint256 constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000; /** * @dev emitted when a borrower is liquidated @@ -177,11 +179,9 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl } //all clear - calculate the max principal amount that can be liquidated - vars.maxPrincipalAmountToLiquidate = vars - .userStableDebt - .add(vars.userVariableDebt) - .mul(LIQUIDATION_CLOSE_FACTOR_PERCENT) - .div(100); + vars.maxPrincipalAmountToLiquidate = vars.userStableDebt.add(vars.userVariableDebt).percentMul( + LIQUIDATION_CLOSE_FACTOR_PERCENT + ); vars.actualAmountToLiquidate = _purchaseAmount > vars.maxPrincipalAmountToLiquidate ? vars.maxPrincipalAmountToLiquidate @@ -331,8 +331,7 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl .mul(_purchaseAmount) .mul(10**vars.collateralDecimals) .div(vars.collateralPrice.mul(10**vars.principalDecimals)) - .mul(vars.liquidationBonus) - .div(100); + .percentMul(vars.liquidationBonus); if (vars.maxAmountCollateralToLiquidate > _userCollateralBalance) { collateralAmount = _userCollateralBalance; @@ -341,8 +340,7 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl .mul(collateralAmount) .mul(10**vars.principalDecimals) .div(vars.principalCurrencyPrice.mul(10**vars.collateralDecimals)) - .mul(100) - .div(vars.liquidationBonus); + .percentDiv(vars.liquidationBonus); } else { collateralAmount = vars.maxAmountCollateralToLiquidate; principalAmountNeeded = _purchaseAmount; diff --git a/contracts/libraries/GenericLogic.sol b/contracts/libraries/GenericLogic.sol index e019d466..927670bd 100644 --- a/contracts/libraries/GenericLogic.sol +++ b/contracts/libraries/GenericLogic.sol @@ -8,7 +8,7 @@ import {ReserveLogic} from './ReserveLogic.sol'; import {ReserveConfiguration} from './ReserveConfiguration.sol'; import {UserLogic} from './UserLogic.sol'; import {WadRayMath} from './WadRayMath.sol'; - +import {PercentageMath} from './PercentageMath.sol'; import '../interfaces/IPriceOracleGetter.sol'; import {IFeeProvider} from '../interfaces/IFeeProvider.sol'; import '@nomiclabs/buidler/console.sol'; @@ -23,6 +23,7 @@ library GenericLogic { using UserLogic for UserLogic.UserReserveData; using SafeMath for uint256; using WadRayMath for uint256; + using PercentageMath for uint256; using ReserveConfiguration for ReserveConfiguration.Map; uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18; @@ -237,7 +238,7 @@ library GenericLogic { ) internal view returns (uint256) { if (borrowBalanceETH == 0) return uint256(-1); - return (collateralBalanceETH.mul(liquidationThreshold).div(100)).wadDiv(borrowBalanceETH); + return (collateralBalanceETH.percentMul(liquidationThreshold)).wadDiv(borrowBalanceETH); } /** @@ -254,7 +255,7 @@ library GenericLogic { uint256 borrowBalanceETH, uint256 ltv ) external view returns (uint256) { - uint256 availableBorrowsETH = collateralBalanceETH.mul(ltv).div(100); //ltv is in percentage + uint256 availableBorrowsETH = collateralBalanceETH.percentMul(ltv); //ltv is in percentage if (availableBorrowsETH < borrowBalanceETH) { return 0; diff --git a/contracts/libraries/PercentageMath.sol b/contracts/libraries/PercentageMath.sol new file mode 100644 index 00000000..846fb518 --- /dev/null +++ b/contracts/libraries/PercentageMath.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.6.8; + +import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol'; + +/** + * @title PercentageMath library + * @author Aave + * @notice Provides functions to calculate percentages. + * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR + * @dev Operations are rounded half up + **/ + +library PercentageMath { + using SafeMath for uint256; + + uint256 constant PERCENTAGE_FACTOR = 1e4; //percentage plus two decimals + uint256 constant HALF_PERCENT = PERCENTAGE_FACTOR / 2; + + /** + * @dev executes a percentage multiplication + * @param _value the value of which the percentage needs to be calculated + * @param _percentage the percentage of the value to be calculated + * @return the _percentage of _value + **/ + function percentMul(uint256 _value, uint256 _percentage) internal pure returns (uint256) { + return HALF_PERCENT.add(_value.mul(_percentage)).div(PERCENTAGE_FACTOR); + } + + /** + * @dev executes a percentage division + * @param _value the value of which the percentage needs to be calculated + * @param _percentage the percentage of the value to be calculated + * @return the _value divided the _percentage + **/ + function percentDiv(uint256 _value, uint256 _percentage) internal pure returns (uint256) { + uint256 halfPercentage = _percentage / 2; + + return halfPercentage.add(_value.mul(PERCENTAGE_FACTOR)).div(_percentage); + } +} diff --git a/contracts/libraries/ValidationLogic.sol b/contracts/libraries/ValidationLogic.sol index 8e4a6a12..8f299ca6 100644 --- a/contracts/libraries/ValidationLogic.sol +++ b/contracts/libraries/ValidationLogic.sol @@ -8,6 +8,7 @@ import {ReserveLogic} from './ReserveLogic.sol'; import {UserLogic} from './UserLogic.sol'; import {GenericLogic} from './GenericLogic.sol'; import {WadRayMath} from './WadRayMath.sol'; +import {PercentageMath} from './PercentageMath.sol'; import {UniversalERC20} from './UniversalERC20.sol'; import {ReserveConfiguration} from './ReserveConfiguration.sol'; import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; @@ -24,6 +25,7 @@ library ValidationLogic { using UserLogic for UserLogic.UserReserveData; using SafeMath for uint256; using WadRayMath for uint256; + using PercentageMath for uint256; using UniversalERC20 for IERC20; using ReserveConfiguration for ReserveConfiguration.Map; @@ -161,7 +163,7 @@ library ValidationLogic { require(vars.healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, '8'); //add the current already borrowed amount to the amount requested to calculate the total collateral needed. - vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(_amountInETH).mul(100).div( + vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(_amountInETH).percentDiv( vars.currentLtv ); //LTV is calculated in percentage @@ -193,7 +195,7 @@ library ValidationLogic { //calculate the max available loan size in stable rate mode as a percentage of the //available liquidity - uint256 maxLoanSizeStable = vars.availableLiquidity.mul(_maxStableLoanPercent).div(100); + uint256 maxLoanSizeStable = vars.availableLiquidity.percentMul(_maxStableLoanPercent); require(_amount <= maxLoanSizeStable, '13'); } diff --git a/deployed-contracts.json b/deployed-contracts.json index d3fe5f3d..e6b20b9a 100644 --- a/deployed-contracts.json +++ b/deployed-contracts.json @@ -438,4 +438,4 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } } -} +} \ No newline at end of file diff --git a/helpers/constants.ts b/helpers/constants.ts index f5be1fe9..efeaca36 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -170,9 +170,9 @@ export const getReservesConfigByPool = (pool: AavePools): iMultiPoolsAssets const userGlobalDataAfter = await pool.getUserAccountData(borrower.address); expect(userGlobalDataAfter.currentLiquidationThreshold).to.be.bignumber.equal( - '80', + '8000', 'Invalid liquidation threshold' ); diff --git a/test/liquidation-underlying.spec.ts b/test/liquidation-underlying.spec.ts index be3e0401..5a62d680 100644 --- a/test/liquidation-underlying.spec.ts +++ b/test/liquidation-underlying.spec.ts @@ -92,7 +92,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset', const userGlobalDataAfter: any = await pool.getUserAccountData(borrower.address); expect(userGlobalDataAfter.currentLiquidationThreshold.toString()).to.be.bignumber.equal( - '80', + '8000', 'Invalid liquidation threshold' ); }); @@ -425,7 +425,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset', .div( new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) ) - .times(100) + .times(10000) .div(liquidationBonus.toString()) .decimalPlaces(0, BigNumber.ROUND_DOWN);