From d137c9c5e4b0809bc31e5edf2243998ac1eb0bbe Mon Sep 17 00:00:00 2001 From: eboado Date: Thu, 13 Oct 2022 13:24:49 +0200 Subject: [PATCH 1/6] Fix on ReserveLogic for #315. - Changed condition for when indexes should be updated on _updateIndexes(). - Added condition to not do any update on updateState() if time has not passed since the previous update. - Moved update location of reserve.lastUpdateTimestamp. --- .../protocol/libraries/logic/ReserveLogic.sol | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 2b5b2cf4b..17e58c551 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -108,29 +108,38 @@ library ReserveLogic { * @param reserve the reserve object **/ function updateState(DataTypes.ReserveData storage reserve) internal { - uint256 scaledVariableDebt = - IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply(); - uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; - uint256 previousLiquidityIndex = reserve.liquidityIndex; - uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; - - (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = - _updateIndexes( + //solium-disable-next-line + if (reserve.lastUpdateTimestamp != uint40(block.timestamp)) { + uint256 scaledVariableDebt = + IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply(); + uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; + uint256 previousLiquidityIndex = reserve.liquidityIndex; + uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; + uint256 avgStableRate = + IStableDebtToken(reserve.stableDebtTokenAddress).getAverageStableRate(); + + (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = + _updateIndexes( + reserve, + scaledVariableDebt, + previousLiquidityIndex, + previousVariableBorrowIndex, + lastUpdatedTimestamp, + avgStableRate + ); + + _mintToTreasury( reserve, scaledVariableDebt, - previousLiquidityIndex, previousVariableBorrowIndex, + newLiquidityIndex, + newVariableBorrowIndex, lastUpdatedTimestamp ); - _mintToTreasury( - reserve, - scaledVariableDebt, - previousVariableBorrowIndex, - newLiquidityIndex, - newVariableBorrowIndex, - lastUpdatedTimestamp - ); + //solium-disable-next-line + reserve.lastUpdateTimestamp = uint40(block.timestamp); + } } /** @@ -336,15 +345,17 @@ library ReserveLogic { uint256 scaledVariableDebt, uint256 liquidityIndex, uint256 variableBorrowIndex, - uint40 timestamp + uint40 timestamp, + uint256 avgStableRate ) internal returns (uint256, uint256) { uint256 currentLiquidityRate = reserve.currentLiquidityRate; + uint256 currentVariableBorrowRate = reserve.currentVariableBorrowRate; uint256 newLiquidityIndex = liquidityIndex; uint256 newVariableBorrowIndex = variableBorrowIndex; - //only cumulating if there is any income being produced - if (currentLiquidityRate > 0) { + // Indexes should be updated only if there is any debt component getting accrued + if ((scaledVariableDebt != 0 && currentVariableBorrowRate != 0) || avgStableRate != 0) { uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp); newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex); @@ -366,8 +377,6 @@ library ReserveLogic { } } - //solium-disable-next-line - reserve.lastUpdateTimestamp = uint40(block.timestamp); return (newLiquidityIndex, newVariableBorrowIndex); } } From 6f90b233ebd22d08e9c49ed415790ad1a94f3544 Mon Sep 17 00:00:00 2001 From: eboado Date: Thu, 13 Oct 2022 14:51:16 +0200 Subject: [PATCH 2/6] Invert time condition on updateState() --- .../protocol/libraries/logic/ReserveLogic.sol | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 17e58c551..a04b72f49 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -108,38 +108,39 @@ library ReserveLogic { * @param reserve the reserve object **/ function updateState(DataTypes.ReserveData storage reserve) internal { + // If time didn't pass since last stored timestamp, skip state update //solium-disable-next-line - if (reserve.lastUpdateTimestamp != uint40(block.timestamp)) { - uint256 scaledVariableDebt = - IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply(); - uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; - uint256 previousLiquidityIndex = reserve.liquidityIndex; - uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; - uint256 avgStableRate = - IStableDebtToken(reserve.stableDebtTokenAddress).getAverageStableRate(); - - (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = - _updateIndexes( - reserve, - scaledVariableDebt, - previousLiquidityIndex, - previousVariableBorrowIndex, - lastUpdatedTimestamp, - avgStableRate - ); + if (reserve.lastUpdateTimestamp == uint40(block.timestamp)) { + return; + } + + uint256 scaledVariableDebt = + IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply(); + uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; + uint256 previousLiquidityIndex = reserve.liquidityIndex; + uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; + uint256 avgStableRate = IStableDebtToken(reserve.stableDebtTokenAddress).getAverageStableRate(); - _mintToTreasury( + (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = + _updateIndexes( reserve, scaledVariableDebt, + previousLiquidityIndex, previousVariableBorrowIndex, - newLiquidityIndex, - newVariableBorrowIndex, - lastUpdatedTimestamp + lastUpdatedTimestamp, + avgStableRate ); + _mintToTreasury( + reserve, + scaledVariableDebt, + previousVariableBorrowIndex, + newLiquidityIndex, + newVariableBorrowIndex, + lastUpdatedTimestamp + ); - //solium-disable-next-line - reserve.lastUpdateTimestamp = uint40(block.timestamp); - } + //solium-disable-next-line + reserve.lastUpdateTimestamp = uint40(block.timestamp); } /** From 7e8e084c168c8426bc94676a2a6f7af62cae6fcc Mon Sep 17 00:00:00 2001 From: eboado Date: Fri, 14 Oct 2022 10:15:25 +0200 Subject: [PATCH 3/6] Simplified _updateIndexes() --- .../protocol/libraries/logic/ReserveLogic.sol | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index a04b72f49..109ac2a32 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -119,7 +119,6 @@ library ReserveLogic { uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; uint256 previousLiquidityIndex = reserve.liquidityIndex; uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; - uint256 avgStableRate = IStableDebtToken(reserve.stableDebtTokenAddress).getAverageStableRate(); (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = _updateIndexes( @@ -127,8 +126,7 @@ library ReserveLogic { scaledVariableDebt, previousLiquidityIndex, previousVariableBorrowIndex, - lastUpdatedTimestamp, - avgStableRate + lastUpdatedTimestamp ); _mintToTreasury( reserve, @@ -346,36 +344,34 @@ library ReserveLogic { uint256 scaledVariableDebt, uint256 liquidityIndex, uint256 variableBorrowIndex, - uint40 timestamp, - uint256 avgStableRate + uint40 timestamp ) internal returns (uint256, uint256) { - uint256 currentLiquidityRate = reserve.currentLiquidityRate; - uint256 currentVariableBorrowRate = reserve.currentVariableBorrowRate; - uint256 newLiquidityIndex = liquidityIndex; uint256 newVariableBorrowIndex = variableBorrowIndex; + uint256 currentVariableBorrowRate = reserve.currentVariableBorrowRate; + uint256 currentLiquidityRate = reserve.currentLiquidityRate; - // Indexes should be updated only if there is any debt component getting accrued - if ((scaledVariableDebt != 0 && currentVariableBorrowRate != 0) || avgStableRate != 0) { + // Only cumulating on the supply side if there is any income being produced + // The case of Reserve Factor 100% is not a problem (currentLiquidityRate == 0), + // as liquidity index should not be updated + if (currentLiquidityRate != 0) { uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp); newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex); require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); - reserve.liquidityIndex = uint128(newLiquidityIndex); + } - //as the liquidity rate might come only from stable rate loans, we need to ensure - //that there is actual variable debt before accumulating - if (scaledVariableDebt != 0) { - uint256 cumulatedVariableBorrowInterest = - MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp); - newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex); - require( - newVariableBorrowIndex <= type(uint128).max, - Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW - ); - reserve.variableBorrowIndex = uint128(newVariableBorrowIndex); - } + // Variable borrow side only gets updated if there is any accrual of variable debt + if (scaledVariableDebt != 0) { + uint256 cumulatedVariableBorrowInterest = + MathUtils.calculateCompoundedInterest(currentVariableBorrowRate, timestamp); + newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex); + require( + newVariableBorrowIndex <= type(uint128).max, + Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW + ); + reserve.variableBorrowIndex = uint128(newVariableBorrowIndex); } return (newLiquidityIndex, newVariableBorrowIndex); From 761ea29084bb36dc88e45caf76c5b007b0199a56 Mon Sep 17 00:00:00 2001 From: eboado Date: Fri, 14 Oct 2022 10:25:12 +0200 Subject: [PATCH 4/6] Fix on _updateIndexes() --- contracts/protocol/libraries/logic/ReserveLogic.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 109ac2a32..182ca4f78 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -348,7 +348,6 @@ library ReserveLogic { ) internal returns (uint256, uint256) { uint256 newLiquidityIndex = liquidityIndex; uint256 newVariableBorrowIndex = variableBorrowIndex; - uint256 currentVariableBorrowRate = reserve.currentVariableBorrowRate; uint256 currentLiquidityRate = reserve.currentLiquidityRate; // Only cumulating on the supply side if there is any income being produced @@ -365,7 +364,7 @@ library ReserveLogic { // Variable borrow side only gets updated if there is any accrual of variable debt if (scaledVariableDebt != 0) { uint256 cumulatedVariableBorrowInterest = - MathUtils.calculateCompoundedInterest(currentVariableBorrowRate, timestamp); + MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp); newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex); require( newVariableBorrowIndex <= type(uint128).max, From bf82b9f5287860786ca50c8ccfcb0ff5f6ddef87 Mon Sep 17 00:00:00 2001 From: eboado Date: Mon, 17 Oct 2022 14:45:54 +0200 Subject: [PATCH 5/6] - Added tests for RF 100% fix. - Misc changes on ReserveLogic to reduce diff with previous version --- .../protocol/libraries/logic/ReserveLogic.sol | 13 +- package.json | 1 + test-suites/test-aave/reserve-factor.spec.ts | 236 ++++++++++++++++++ 3 files changed, 244 insertions(+), 6 deletions(-) create mode 100644 test-suites/test-aave/reserve-factor.spec.ts diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 182ca4f78..ac1335aa0 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -128,6 +128,7 @@ library ReserveLogic { previousVariableBorrowIndex, lastUpdatedTimestamp ); + _mintToTreasury( reserve, scaledVariableDebt, @@ -136,9 +137,6 @@ library ReserveLogic { newVariableBorrowIndex, lastUpdatedTimestamp ); - - //solium-disable-next-line - reserve.lastUpdateTimestamp = uint40(block.timestamp); } /** @@ -346,14 +344,15 @@ library ReserveLogic { uint256 variableBorrowIndex, uint40 timestamp ) internal returns (uint256, uint256) { + uint256 currentLiquidityRate = reserve.currentLiquidityRate; + uint256 newLiquidityIndex = liquidityIndex; uint256 newVariableBorrowIndex = variableBorrowIndex; - uint256 currentLiquidityRate = reserve.currentLiquidityRate; // Only cumulating on the supply side if there is any income being produced // The case of Reserve Factor 100% is not a problem (currentLiquidityRate == 0), // as liquidity index should not be updated - if (currentLiquidityRate != 0) { + if (currentLiquidityRate > 0) { uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp); newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex); @@ -362,7 +361,7 @@ library ReserveLogic { } // Variable borrow side only gets updated if there is any accrual of variable debt - if (scaledVariableDebt != 0) { + if (scaledVariableDebt > 0) { uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp); newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex); @@ -373,6 +372,8 @@ library ReserveLogic { reserve.variableBorrowIndex = uint128(newVariableBorrowIndex); } + //solium-disable-next-line + reserve.lastUpdateTimestamp = uint40(block.timestamp); return (newLiquidityIndex, newVariableBorrowIndex); } } diff --git a/package.json b/package.json index 5489c7f0e..a95cc6fbd 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "test-amm": "npm run compile && TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-amm/*.spec.ts", "test-amm-scenarios": "npm run compile && TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-amm/__setup.spec.ts test-suites/test-amm/scenario.spec.ts", "test-scenarios": "npm run compile && npx hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/scenario.spec.ts", + "test-reserve-factor": "npm run compile && npx hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/reserve-factor.spec.ts", "test-subgraph:scenarios": "npm run compile && hardhat --network hardhatevm_docker test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/subgraph-scenarios.spec.ts", "test:main:check-list": "npm run compile && FORK=main TS_NODE_TRANSPILE_ONLY=1 hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/mainnet/check-list.spec.ts", "dev:coverage": "buidler compile --force && buidler coverage --network coverage", diff --git a/test-suites/test-aave/reserve-factor.spec.ts b/test-suites/test-aave/reserve-factor.spec.ts new file mode 100644 index 000000000..adfbecd40 --- /dev/null +++ b/test-suites/test-aave/reserve-factor.spec.ts @@ -0,0 +1,236 @@ +import BigNumber from 'bignumber.js'; + +import { DRE, increaseTime } from '../../helpers/misc-utils'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { makeSuite } from './helpers/make-suite'; +import { RateMode } from '../../helpers/types'; +import { ConfigNames, getTreasuryAddress, loadPoolConfig } from '../../helpers/configuration'; + +const chai = require('chai'); + +const { expect } = chai; + +makeSuite('LendingPool Reserve Factor 100%. Only variable borrowings', (testEnv) => { + before('Before LendingPool Reserve Factor accrual: set config', () => { + BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN }); + }); + + after('After LendingPool Reserve Factor accrual: reset config', () => { + BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP }); + }); + + it('Deposits WETH, borrows DAI', async () => { + const { dai, weth, users, pool, oracle } = testEnv; + const depositor = users[0]; + const borrower = users[1]; + + // mints DAI to depositor + await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '2000')); + + // approve protocol to access depositor wallet + await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + // user 1 deposits 1000 DAI + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool + .connect(depositor.signer) + .deposit(dai.address, amountDAItoDeposit, depositor.address, '0'); + // user 2 deposits 1 ETH + const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + + // mints WETH to borrower + await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000')); + + // approve protocol to access the borrower wallet + await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool + .connect(borrower.signer) + .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + + //user 2 borrows + + const userGlobalData = await pool.getUserAccountData(borrower.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + + const amountDAIToBorrow = await convertToCurrencyDecimals( + dai.address, + new BigNumber(userGlobalData.availableBorrowsETH.toString()) + .div(daiPrice.toString()) + .multipliedBy(0.95) + .toFixed(0) + ); + + await pool + .connect(borrower.signer) + .borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0', borrower.address); + }); + + it('Change RF of DAI to 100%', async () => { + const { configurator, dai } = testEnv; + await configurator.setReserveFactor(dai.address, '10000'); + }); + + it('Validate that variable borrow index accrue, liquidity index not, and the Collector receives aTokens after interest accrues', async () => { + const { dai, users, pool, aDai } = testEnv; + const depositor = users[0]; + + const collectorAddress = await getTreasuryAddress(loadPoolConfig(ConfigNames.Aave)); + + const collectorADAIBalanceBefore = await aDai.scaledBalanceOf(collectorAddress); + + const reserveDataBefore = await pool.getReserveData(dai.address); + + await increaseTime(10000); + + // Deposit to "settle" the liquidity index accrual from pre-RF increase to 100% + await pool + .connect(depositor.signer) + .deposit( + dai.address, + await convertToCurrencyDecimals(dai.address, '1'), + depositor.address, + '0' + ); + + const reserveDataAfter1 = await pool.getReserveData(dai.address); + const collectorADAIBalanceAfter1 = await aDai.balanceOf(collectorAddress); + + expect(reserveDataAfter1.variableBorrowIndex).to.be.gt(reserveDataBefore.variableBorrowIndex); + expect(collectorADAIBalanceAfter1).to.be.gt(collectorADAIBalanceBefore); + expect(reserveDataAfter1.liquidityIndex).to.be.gt(reserveDataBefore.liquidityIndex); + + await increaseTime(10000); + + // "Clean" update, that should not increase the liquidity index, only variable borrow + await pool + .connect(depositor.signer) + .deposit( + dai.address, + await convertToCurrencyDecimals(dai.address, '1'), + depositor.address, + '0' + ); + + const reserveDataAfter2 = await pool.getReserveData(dai.address); + const collectorADAIBalanceAfter2 = await aDai.balanceOf(collectorAddress); + + expect(reserveDataAfter2.variableBorrowIndex).to.be.gt(reserveDataAfter1.variableBorrowIndex); + expect(collectorADAIBalanceAfter2).to.be.gt(collectorADAIBalanceAfter1); + expect(reserveDataAfter2.liquidityIndex).to.be.eq(reserveDataAfter1.liquidityIndex); + }); +}); + +makeSuite('LendingPool Reserve Factor 100%. Only stable borrowings', (testEnv) => { + before('Before LendingPool Reserve Factor accrual: set config', () => { + BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN }); + }); + + after('After LendingPool Reserve Factor accrual: reset config', () => { + BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP }); + }); + + it('Deposits WETH, borrows DAI', async () => { + const { dai, weth, users, pool, oracle } = testEnv; + const depositor = users[0]; + const borrower = users[1]; + + // mints DAI to depositor + await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '2000')); + + // approve protocol to access depositor wallet + await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + // user 1 deposits 1000 DAI + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool + .connect(depositor.signer) + .deposit(dai.address, amountDAItoDeposit, depositor.address, '0'); + // user 2 deposits 1 ETH + const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + + // mints WETH to borrower + await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000')); + + // approve protocol to access the borrower wallet + await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool + .connect(borrower.signer) + .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + + //user 2 borrows + + const userGlobalData = await pool.getUserAccountData(borrower.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + + const amountDAIToBorrow = await convertToCurrencyDecimals( + dai.address, + new BigNumber(userGlobalData.availableBorrowsETH.toString()) + .div(daiPrice.toString()) + .multipliedBy(0.95) + .toFixed(0) + ); + + await pool + .connect(borrower.signer) + .borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0', borrower.address); + }); + + it('Change RF of DAI to 100%', async () => { + const { configurator, dai } = testEnv; + await configurator.setReserveFactor(dai.address, '10000'); + }); + + it('Validate that neither variable borrow index nor liquidity index increase, but the Collector receives aTokens after interest accrues', async () => { + const { dai, users, pool, aDai } = testEnv; + const depositor = users[0]; + + const collectorAddress = await getTreasuryAddress(loadPoolConfig(ConfigNames.Aave)); + + const collectorADAIBalanceBefore = await aDai.scaledBalanceOf(collectorAddress); + + const reserveDataBefore = await pool.getReserveData(dai.address); + + await increaseTime(10000); + + // Deposit to "settle" the liquidity index accrual from pre-RF increase to 100% + await pool + .connect(depositor.signer) + .deposit( + dai.address, + await convertToCurrencyDecimals(dai.address, '1'), + depositor.address, + '0' + ); + + const reserveDataAfter1 = await pool.getReserveData(dai.address); + const collectorADAIBalanceAfter1 = await aDai.balanceOf(collectorAddress); + + expect(reserveDataAfter1.variableBorrowIndex).to.be.eq(reserveDataBefore.variableBorrowIndex); + expect(collectorADAIBalanceAfter1).to.be.gt(collectorADAIBalanceBefore); + expect(reserveDataAfter1.liquidityIndex).to.be.gt(reserveDataBefore.liquidityIndex); + + await increaseTime(10000); + + // "Clean" update, that should not increase the liquidity index, only variable borrow + await pool + .connect(depositor.signer) + .deposit( + dai.address, + await convertToCurrencyDecimals(dai.address, '1'), + depositor.address, + '0' + ); + + const reserveDataAfter2 = await pool.getReserveData(dai.address); + const collectorADAIBalanceAfter2 = await aDai.balanceOf(collectorAddress); + + expect(reserveDataAfter2.variableBorrowIndex).to.be.eq(reserveDataAfter1.variableBorrowIndex); + expect(collectorADAIBalanceAfter2).to.be.gt(collectorADAIBalanceAfter1); + expect(reserveDataAfter2.liquidityIndex).to.be.eq(reserveDataAfter1.liquidityIndex); + }); +}); From b88fd82188215c88b6a18d6f2a46f2879df505a7 Mon Sep 17 00:00:00 2001 From: eboado Date: Mon, 17 Oct 2022 17:04:24 +0200 Subject: [PATCH 6/6] Simplified reserve-factor tests --- test-suites/test-aave/reserve-factor.spec.ts | 154 +++++++------------ 1 file changed, 55 insertions(+), 99 deletions(-) diff --git a/test-suites/test-aave/reserve-factor.spec.ts b/test-suites/test-aave/reserve-factor.spec.ts index adfbecd40..e706ace9d 100644 --- a/test-suites/test-aave/reserve-factor.spec.ts +++ b/test-suites/test-aave/reserve-factor.spec.ts @@ -3,7 +3,7 @@ import BigNumber from 'bignumber.js'; import { DRE, increaseTime } from '../../helpers/misc-utils'; import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants'; import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; -import { makeSuite } from './helpers/make-suite'; +import { makeSuite, TestEnv } from './helpers/make-suite'; import { RateMode } from '../../helpers/types'; import { ConfigNames, getTreasuryAddress, loadPoolConfig } from '../../helpers/configuration'; @@ -11,70 +11,73 @@ const chai = require('chai'); const { expect } = chai; -makeSuite('LendingPool Reserve Factor 100%. Only variable borrowings', (testEnv) => { - before('Before LendingPool Reserve Factor accrual: set config', () => { - BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN }); - }); +// Setup function to have 1 user with DAI deposits, and another user with WETH collateral +// and DAI borrowings at an indicated borrowing mode +const setupPositions = async (testEnv: TestEnv, borrowingMode: RateMode) => { + const { dai, weth, users, pool, oracle } = testEnv; + const depositor = users[0]; + const borrower = users[1]; - after('After LendingPool Reserve Factor accrual: reset config', () => { - BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP }); - }); + // mints DAI to depositor + await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '2000')); - it('Deposits WETH, borrows DAI', async () => { - const { dai, weth, users, pool, oracle } = testEnv; - const depositor = users[0]; - const borrower = users[1]; + // approve protocol to access depositor wallet + await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); - // mints DAI to depositor - await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '2000')); + // user 1 deposits 1000 DAI + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); - // approve protocol to access depositor wallet - await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool + .connect(depositor.signer) + .deposit(dai.address, amountDAItoDeposit, depositor.address, '0'); + // user 2 deposits 1 ETH + const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); - // user 1 deposits 1000 DAI - const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + // mints WETH to borrower + await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000')); - await pool - .connect(depositor.signer) - .deposit(dai.address, amountDAItoDeposit, depositor.address, '0'); - // user 2 deposits 1 ETH - const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + // approve protocol to access the borrower wallet + await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); - // mints WETH to borrower - await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000')); + await pool + .connect(borrower.signer) + .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); - // approve protocol to access the borrower wallet - await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + //user 2 borrows - await pool - .connect(borrower.signer) - .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + const userGlobalData = await pool.getUserAccountData(borrower.address); + const daiPrice = await oracle.getAssetPrice(dai.address); - //user 2 borrows + const amountDAIToBorrow = await convertToCurrencyDecimals( + dai.address, + new BigNumber(userGlobalData.availableBorrowsETH.toString()) + .div(daiPrice.toString()) + .multipliedBy(0.95) + .toFixed(0) + ); - const userGlobalData = await pool.getUserAccountData(borrower.address); - const daiPrice = await oracle.getAssetPrice(dai.address); + await pool + .connect(borrower.signer) + .borrow(dai.address, amountDAIToBorrow, borrowingMode, '0', borrower.address); +}; - const amountDAIToBorrow = await convertToCurrencyDecimals( - dai.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) - .div(daiPrice.toString()) - .multipliedBy(0.95) - .toFixed(0) - ); +makeSuite('LendingPool Reserve Factor 100%. Only variable borrowings', (testEnv) => { + before('Before LendingPool Reserve Factor accrual: set config', () => { + BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN }); + }); - await pool - .connect(borrower.signer) - .borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0', borrower.address); + after('After LendingPool Reserve Factor accrual: reset config', () => { + BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP }); }); - it('Change RF of DAI to 100%', async () => { - const { configurator, dai } = testEnv; + it('Validates that variable borrow index accrue, liquidity index not, and the Collector receives aTokens after interest accrues', async () => { + const { configurator, dai, users, pool, aDai } = testEnv; + + await setupPositions(testEnv, RateMode.Variable); + + // Set the RF to 100% await configurator.setReserveFactor(dai.address, '10000'); - }); - it('Validate that variable borrow index accrue, liquidity index not, and the Collector receives aTokens after interest accrues', async () => { - const { dai, users, pool, aDai } = testEnv; const depositor = users[0]; const collectorAddress = await getTreasuryAddress(loadPoolConfig(ConfigNames.Aave)); @@ -132,61 +135,14 @@ makeSuite('LendingPool Reserve Factor 100%. Only stable borrowings', (testEnv) = BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP }); }); - it('Deposits WETH, borrows DAI', async () => { - const { dai, weth, users, pool, oracle } = testEnv; - const depositor = users[0]; - const borrower = users[1]; - - // mints DAI to depositor - await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '2000')); - - // approve protocol to access depositor wallet - await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); - - // user 1 deposits 1000 DAI - const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); - - await pool - .connect(depositor.signer) - .deposit(dai.address, amountDAItoDeposit, depositor.address, '0'); - // user 2 deposits 1 ETH - const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); - - // mints WETH to borrower - await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000')); + it('Validates that neither variable borrow index nor liquidity index increase, but the Collector receives aTokens after interest accrues', async () => { + const { configurator, dai, users, pool, aDai } = testEnv; - // approve protocol to access the borrower wallet - await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await setupPositions(testEnv, RateMode.Stable); - await pool - .connect(borrower.signer) - .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); - - //user 2 borrows - - const userGlobalData = await pool.getUserAccountData(borrower.address); - const daiPrice = await oracle.getAssetPrice(dai.address); - - const amountDAIToBorrow = await convertToCurrencyDecimals( - dai.address, - new BigNumber(userGlobalData.availableBorrowsETH.toString()) - .div(daiPrice.toString()) - .multipliedBy(0.95) - .toFixed(0) - ); - - await pool - .connect(borrower.signer) - .borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0', borrower.address); - }); - - it('Change RF of DAI to 100%', async () => { - const { configurator, dai } = testEnv; + // Set the RF to 100% await configurator.setReserveFactor(dai.address, '10000'); - }); - it('Validate that neither variable borrow index nor liquidity index increase, but the Collector receives aTokens after interest accrues', async () => { - const { dai, users, pool, aDai } = testEnv; const depositor = users[0]; const collectorAddress = await getTreasuryAddress(loadPoolConfig(ConfigNames.Aave));