From 07db321b4dc7abedc3150f71acbe32db8e306a84 Mon Sep 17 00:00:00 2001 From: The3D Date: Mon, 21 Sep 2020 19:52:22 +0200 Subject: [PATCH] updated rebalance conditions,tests --- .../IReserveInterestRateStrategy.sol | 6 ++- .../DefaultReserveInterestRateStrategy.sol | 4 ++ contracts/lendingpool/LendingPool.sol | 51 ++++++++++++------- .../scenarios/rebalance-stable-rate.json | 43 +++------------- 4 files changed, 51 insertions(+), 53 deletions(-) diff --git a/contracts/interfaces/IReserveInterestRateStrategy.sol b/contracts/interfaces/IReserveInterestRateStrategy.sol index 48311e70..a69160b3 100644 --- a/contracts/interfaces/IReserveInterestRateStrategy.sol +++ b/contracts/interfaces/IReserveInterestRateStrategy.sol @@ -10,9 +10,13 @@ interface IReserveInterestRateStrategy { /** * @dev returns the base variable borrow rate, in rays */ - function baseVariableBorrowRate() external view returns (uint256); + /** + * @dev returns the maximum variable borrow rate + */ + function getMaxVariableBorrowRate() external view returns (uint256); + /** * @dev calculates the liquidity, stable, and variable rates depending on the current utilization rate * and the base parameters diff --git a/contracts/lendingpool/DefaultReserveInterestRateStrategy.sol b/contracts/lendingpool/DefaultReserveInterestRateStrategy.sol index c2303254..64b90a44 100644 --- a/contracts/lendingpool/DefaultReserveInterestRateStrategy.sol +++ b/contracts/lendingpool/DefaultReserveInterestRateStrategy.sol @@ -89,6 +89,10 @@ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy { return _baseVariableBorrowRate; } + function getMaxVariableBorrowRate() external override view returns (uint256) { + return _baseVariableBorrowRate.add(_variableRateSlope1).add(_variableRateSlope2); + } + /** * @dev calculates the interest rates depending on the available liquidity and the total borrowed. * @param reserve the address of the reserve diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 86c47374..bd651647 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -12,6 +12,7 @@ import {IAToken} from '../tokenization/interfaces/IAToken.sol'; import {Helpers} from '../libraries/helpers/Helpers.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; +import {PercentageMath} from '../libraries/math/PercentageMath.sol'; import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; @@ -26,6 +27,7 @@ import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import {ILendingPool} from '../interfaces/ILendingPool.sol'; import {LendingPoolStorage} from './LendingPoolStorage.sol'; +import {IReserveInterestRateStrategy} from '../interfaces/IReserveInterestRateStrategy.sol'; /** * @title LendingPool contract @@ -36,10 +38,12 @@ import {LendingPoolStorage} from './LendingPoolStorage.sol'; contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage { using SafeMath for uint256; using WadRayMath for uint256; + using PercentageMath for uint256; using SafeERC20 for IERC20; //main configuration parameters - uint256 public constant REBALANCE_DOWN_RATE_DELTA = (1e27) / 5; + uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 4000; + uint256 public constant REBALANCE_UP_USAGE_RATIO_THRESHOLD = 0.95 * 1e27; //usage ratio of 95% uint256 public constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 25; uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9; @@ -354,26 +358,39 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage whenNotPaused(); ReserveLogic.ReserveData storage reserve = _reserves[asset]; - IStableDebtToken stableDebtToken = IStableDebtToken(reserve.stableDebtTokenAddress); + IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress); + IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress); - uint256 stableBorrowBalance = IERC20(address(stableDebtToken)).balanceOf(user); + uint256 stableBorrowBalance = IERC20(stableDebtToken).balanceOf(user); // user must be borrowing on asset at a stable rate require(stableBorrowBalance > 0, Errors.NOT_ENOUGH_STABLE_BORROW_BALANCE); - uint256 rebalanceDownRateThreshold = WadRayMath.ray().add(REBALANCE_DOWN_RATE_DELTA).rayMul( - reserve.currentStableBorrowRate - ); - - //1. user stable borrow rate is below the current liquidity rate. The loan needs to be rebalanced, - //as this situation can be abused (user putting back the borrowed liquidity in the same reserve to earn on it) - //2. user stable rate is above the market avg borrow rate of a certain delta, and utilization rate is low. - //In this case, the user is paying an interest that is too high, and needs to be rescaled down. - - uint256 userStableRate = stableDebtToken.getUserStableRate(user); + //if the utilization rate is below 95%, no rebalances are needed + uint256 totalBorrows = stableDebtToken.totalSupply().add(variableDebtToken.totalSupply()); + uint256 availableLiquidity = IERC20(reserve.aTokenAddress).totalSupply(); + uint256 utilizationRate = totalBorrows == 0 + ? 0 + : totalBorrows.rayDiv(availableLiquidity.add(totalBorrows)); require( - userStableRate < reserve.currentLiquidityRate || userStableRate > rebalanceDownRateThreshold, + utilizationRate >= REBALANCE_UP_USAGE_RATIO_THRESHOLD, + Errors.INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET + ); + + //if the liquidity rate is below REBALANCE_UP_THRESHOLD of the max variable APR at 95% usage, + //then we allow rebalancing of the stable rate positions. + + uint256 currentLiquidityRate = reserve.currentLiquidityRate; + uint256 maxVariableBorrowRate = IReserveInterestRateStrategy( + reserve + .interestRateStrategyAddress + ) + .getMaxVariableBorrowRate(); + + require( + currentLiquidityRate <= + maxVariableBorrowRate.percentMul(REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD), Errors.INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET ); @@ -381,8 +398,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.updateCumulativeIndexesAndTimestamp(); - stableDebtToken.burn(user, stableBorrowBalance); - stableDebtToken.mint(user, stableBorrowBalance, reserve.currentStableBorrowRate); + IStableDebtToken(address(stableDebtToken)).burn(user, stableBorrowBalance); + IStableDebtToken(address(stableDebtToken)).mint(user, stableBorrowBalance, reserve.currentStableBorrowRate); reserve.updateInterestRates(asset, reserve.aTokenAddress, 0, 0); @@ -507,7 +524,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage _flashLiquidationLocked = false; } - struct FlashLoanLocalVars { + struct FlashLoanLocalVars { uint256 premium; uint256 amountPlusPremium; IFlashLoanReceiver receiver; diff --git a/test/helpers/scenarios/rebalance-stable-rate.json b/test/helpers/scenarios/rebalance-stable-rate.json index 1200ff3e..891de2b7 100644 --- a/test/helpers/scenarios/rebalance-stable-rate.json +++ b/test/helpers/scenarios/rebalance-stable-rate.json @@ -19,7 +19,7 @@ ] }, { - "description": "User 0 deposits 1000 DAI, user 1 deposits 1 ETH, borrows 100 DAI at a variable rate, user 0 rebalances user 1 (revert expected)", + "description": "User 0 deposits 1000 DAI, user 1 deposits 5 ETH, borrows 600 DAI at a variable rate, user 0 rebalances user 1 (revert expected)", "actions": [ { "name": "mint", @@ -51,7 +51,7 @@ "name": "mint", "args": { "reserve": "WETH", - "amount": "1", + "amount": "5", "user": "1" }, "expected": "success" @@ -69,7 +69,7 @@ "args": { "reserve": "WETH", - "amount": "1", + "amount": "5", "user": "1" }, "expected": "success" @@ -78,7 +78,7 @@ "name": "borrow", "args": { "reserve": "DAI", - "amount": "100", + "amount": "600", "borrowRateMode": "variable", "user": "1", "timeTravel": "365" @@ -122,7 +122,7 @@ ] }, { - "description": "User 2 deposits ETH and borrows the remaining DAI, causing the stable rates to rise (liquidity rate < user 1 borrow rate). User 0 tries to rebalance user 1 (revert expected)", + "description": "User 2 deposits ETH and borrows the remaining DAI, causing the stable rates to rise (usage ratio = 94%). User 0 tries to rebalance user 1 (revert expected)", "actions": [ { "name": "mint", @@ -155,7 +155,7 @@ "name": "borrow", "args": { "reserve": "DAI", - "amount": "100", + "amount": "340", "borrowRateMode": "variable", "user": "2" }, @@ -174,40 +174,13 @@ ] }, { - "description": "User 2 borrows more DAI, causing the liquidity rate to rise above user 1 stable borrow rate User 0 rebalances user 1", + "description": "User 2 borrows the remaining DAI (usage ratio = 100%). User 0 rebalances user 1", "actions": [ - { - "name": "mint", - "args": { - "reserve": "WETH", - "amount": "3", - "user": "2" - }, - "expected": "success" - }, - { - "name": "approve", - "args": { - "reserve": "WETH", - "user": "2" - }, - "expected": "success" - }, - { - "name": "deposit", - "args": { - "reserve": "WETH", - - "amount": "3", - "user": "2" - }, - "expected": "success" - }, { "name": "borrow", "args": { "reserve": "DAI", - "amount": "700", + "amount": "60", "borrowRateMode": "variable", "user": "2" },