From e2edf016eb8d8f7d360bb5b38d7c058a71dd9ec6 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 26 May 2021 20:11:48 +0200 Subject: [PATCH 01/11] feat: added drop reserve main capability --- contracts/interfaces/ILendingPool.sol | 15 +++-- .../protocol/lendingpool/LendingPool.sol | 56 +++++++++++++++---- .../lendingpool/LendingPoolConfigurator.sol | 5 ++ .../protocol/libraries/helpers/Errors.sol | 3 + .../protocol/libraries/logic/ReserveLogic.sol | 21 ++++++- test-suites/test-aave/__setup.spec.ts | 1 + 6 files changed, 80 insertions(+), 21 deletions(-) diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index 4441d4d4..bcebbde8 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -168,14 +168,11 @@ interface ILendingPool { ); /** - * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest. - * @param reserve the address of the reserve - * @param amountMinted the amount minted to the treasury - **/ - event MintedToTreasury( - address indexed reserve, - uint256 amountMinted - ); + * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest. + * @param reserve the address of the reserve + * @param amountMinted the amount minted to the treasury + **/ + event MintedToTreasury(address indexed reserve, uint256 amountMinted); /** * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. @@ -406,6 +403,8 @@ interface ILendingPool { address interestRateStrategyAddress ) external; + function dropReserve(address reserve) external; + function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress) external; diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 479c9298..9fb2bcfb 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -545,11 +545,11 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function mintToTreasury(address[] calldata reserves) public { for (uint256 i = 0; i < reserves.length; i++) { address reserveAddress = reserves[i]; - + DataTypes.ReserveData storage reserve = _reserves[reserveAddress]; // this cover both inactive reserves and invalid reserves since the flag will be 0 for both - if(!reserve.configuration.getActive()){ + if (!reserve.configuration.getActive()) { continue; } @@ -690,15 +690,29 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } /** - * @dev Returns the list of the initialized reserves + * @dev Returns the list of the initialized reserves, does not contain dropped reserves **/ function getReservesList() external view override returns (address[] memory) { - address[] memory _activeReserves = new address[](_reservesCount); + uint256 reserveListCount = _reservesCount; + uint256 droppedReservesCount = 0; + address[] memory reserves = new address[](reserveListCount); - for (uint256 i = 0; i < _reservesCount; i++) { - _activeReserves[i] = _reservesList[i]; + for (uint256 i = 0; i < reserveListCount; i++) { + if (_reservesList[i] != address(0)) { + reserves[i - droppedReservesCount] = _reservesList[i]; + } else { + droppedReservesCount++; + } } - return _activeReserves; + + if (droppedReservesCount == 0) return reserves; + + address[] memory undroppedReserves = new address[](reserveListCount - droppedReservesCount); + for (uint256 i = 0; i < reserveListCount - droppedReservesCount; i++) { + undroppedReserves[i] = reserves[i]; + } + + return undroppedReserves; } /** @@ -808,6 +822,16 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage _addReserveToList(asset); } + /** + * @dev Drop a reserve + * - Only callable by the LendingPoolConfigurator contract + * @param asset The address of the underlying asset of the reserve + **/ + function dropReserve(address asset) external override onlyLendingPoolConfigurator { + _reserves[asset].dropReserve(); + _removeReserveFromList(asset); + } + /** * @dev Updates the address of the interest rate strategy contract * - Only callable by the LendingPoolConfigurator contract @@ -1084,7 +1108,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage return paybackAmount; } - function _addReserveToList(address asset) internal { + function _addReserveToList(address asset) internal returns (uint8) { uint256 reservesCount = _reservesCount; require(reservesCount < _maxNumberOfReserves, Errors.LP_NO_MORE_RESERVES_ALLOWED); @@ -1092,10 +1116,18 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset; if (!reserveAlreadyAdded) { - _reserves[asset].id = uint8(reservesCount); - _reservesList[reservesCount] = asset; - - _reservesCount = reservesCount + 1; + for (uint8 i = 0; i <= reservesCount; i++) { + if (_reservesList[i] == address(0)) { + _reserves[asset].id = i; + _reservesList[i] = asset; + _reservesCount = reservesCount + 1; + return i; + } + } } } + + function _removeReserveFromList(address asset) internal { + _reservesList[_reserves[asset].id] = address(0); + } } diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 2730937f..c3c8e2e9 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -159,6 +159,11 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur ); } + /// @inheritdoc ILendingPoolConfigurator + function dropReserve(address asset) external onlyPoolAdmin { + _pool.dropReserve(asset); + } + /// @inheritdoc ILendingPoolConfigurator function updateAToken(UpdateATokenInput calldata input) external override onlyPoolAdmin { ILendingPool cachedPool = _pool; diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 8a2e7653..1b984270 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -109,6 +109,9 @@ library Errors { string public constant LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85'; string public constant VL_RESERVE_PAUSED = '86'; string public constant LPC_CALLER_NOT_RISK_OR_POOL_ADMIN = '87'; + string public constant RL_ATOKEN_SUPPLY_NOT_NULL = '88'; + string public constant RL_STABLE_DEBT_NOT_NULL = '89'; + string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_NULL = '90'; enum CollateralManagerErrors { NO_ERROR, diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 2e9850c9..81fe56fc 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -178,6 +178,23 @@ library ReserveLogic { reserve.interestRateStrategyAddress = interestRateStrategyAddress; } + /** + * @dev drop a reserve + * @param reserve The reserve object + **/ + function dropReserve(DataTypes.ReserveData storage reserve) external { + require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_NULL); + require( + IERC20(reserve.stableDebtTokenAddress).totalSupply() == 0, + Errors.RL_STABLE_DEBT_NOT_NULL + ); + require( + IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0, + Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_NULL + ); + reserve.id = type(uint8).max; + } + struct UpdateInterestRatesLocalVars { address stableDebtTokenAddress; uint256 availableLiquidity; @@ -320,7 +337,9 @@ library ReserveLogic { vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor); if (vars.amountToMint != 0) { - reserve.accruedToTreasury = reserve.accruedToTreasury.add(vars.amountToMint.rayDiv(newLiquidityIndex)); + reserve.accruedToTreasury = reserve.accruedToTreasury.add( + vars.amountToMint.rayDiv(newLiquidityIndex) + ); } } diff --git a/test-suites/test-aave/__setup.spec.ts b/test-suites/test-aave/__setup.spec.ts index dd8ed168..2ef2f3a0 100644 --- a/test-suites/test-aave/__setup.spec.ts +++ b/test-suites/test-aave/__setup.spec.ts @@ -265,6 +265,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { ); await configureReservesByHelper(reservesParams, allReservesAddresses, testHelpers, admin); + lendingPoolConfiguratorProxy.dropReserve(mockTokens.KNC.address); const collateralManager = await deployLendingPoolCollateralManager(); await waitForTx( From 776220d705a63c630392f82f2ffb5201c5cfa017 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 27 May 2021 08:02:41 +0200 Subject: [PATCH 02/11] feat: removed user updates on droped reserves --- contracts/protocol/libraries/logic/GenericLogic.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/protocol/libraries/logic/GenericLogic.sol b/contracts/protocol/libraries/logic/GenericLogic.sol index 381fe804..549328a8 100644 --- a/contracts/protocol/libraries/logic/GenericLogic.sol +++ b/contracts/protocol/libraries/logic/GenericLogic.sol @@ -93,6 +93,9 @@ library GenericLogic { } vars.currentReserveAddress = reserves[vars.i]; + + if (vars.currentReserveAddress == address(0)) continue; + DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress]; (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve @@ -132,7 +135,7 @@ library GenericLogic { IERC20(currentReserve.stableDebtTokenAddress).balanceOf(user) ); vars.userDebtETH = vars.assetPrice.mul(vars.userDebt).div(vars.assetUnit); - vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH); + vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH); } } From 52b903cd205914bbb35195c1bde9f8324a256a56 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 27 May 2021 08:12:27 +0200 Subject: [PATCH 03/11] feat: configurator: added reserveDropped event --- .../interfaces/ILendingPoolConfigurator.sol | 16 +++++++++++----- .../lendingpool/LendingPoolConfigurator.sol | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index ee5cdf2f..75b8c47e 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -132,6 +132,12 @@ interface ILendingPoolConfigurator { **/ event ReserveUnpaused(address indexed asset); + /** + * @dev Emitted when a reserve is dropped + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveDropped(address indexed asset); + /** * @dev Emitted when a reserve factor is updated * @param asset The address of the underlying asset of the reserve @@ -203,13 +209,13 @@ interface ILendingPoolConfigurator { address indexed implementation ); - /** + /** * @dev Emitted when a new borrower is authorized (fees = 0) * @param flashBorrower The address of the authorized borrower **/ event FlashBorrowerAuthorized(address indexed flashBorrower); - /** + /** * @dev Emitted when a borrower is unauthorized * @param flashBorrower The address of the unauthorized borrower **/ @@ -386,14 +392,14 @@ interface ILendingPoolConfigurator { * @param admin The address of the potential admin **/ function isRiskAdmin(address admin) external view returns (bool); - - /** + + /** * @dev Authorize a new borrower (fees are 0 for the authorized borrower) * @param flashBorrower The address of the authorized borrower **/ function authorizeFlashBorrower(address flashBorrower) external; - /** + /** * @dev Unauthorize a borrower * @param flashBorrower The address of the unauthorized borrower **/ diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index c3c8e2e9..a7cc2f77 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -162,6 +162,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur /// @inheritdoc ILendingPoolConfigurator function dropReserve(address asset) external onlyPoolAdmin { _pool.dropReserve(asset); + emit ReserveDropped(asset); } /// @inheritdoc ILendingPoolConfigurator From d0841097e6d0f8a9594189755afe275fcf6a970a Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Sun, 6 Jun 2021 09:35:32 +0200 Subject: [PATCH 04/11] fix: drop reserve logic updated --- contracts/protocol/lendingpool/LendingPool.sol | 3 ++- .../protocol/libraries/logic/ReserveLogic.sol | 17 ----------------- .../libraries/logic/ValidationLogic.sol | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 9fb2bcfb..e49a53f1 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -828,8 +828,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage * @param asset The address of the underlying asset of the reserve **/ function dropReserve(address asset) external override onlyLendingPoolConfigurator { - _reserves[asset].dropReserve(); + ValidationLogic.validateDropReserve(_reserves[asset]); _removeReserveFromList(asset); + delete _reserves[asset]; } /** diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 81fe56fc..28e96647 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -178,23 +178,6 @@ library ReserveLogic { reserve.interestRateStrategyAddress = interestRateStrategyAddress; } - /** - * @dev drop a reserve - * @param reserve The reserve object - **/ - function dropReserve(DataTypes.ReserveData storage reserve) external { - require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_NULL); - require( - IERC20(reserve.stableDebtTokenAddress).totalSupply() == 0, - Errors.RL_STABLE_DEBT_NOT_NULL - ); - require( - IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0, - Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_NULL - ); - reserve.id = type(uint8).max; - } - struct UpdateInterestRatesLocalVars { address stableDebtTokenAddress; uint256 availableLiquidity; diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 40ef23be..2f887d7e 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -477,4 +477,20 @@ library ValidationLogic { function validateTransfer(DataTypes.ReserveData storage reserve) internal view { require(!reserve.configuration.getPaused(), Errors.VL_RESERVE_PAUSED); } + + /** + * @dev Validates a drop reserve action + * @param reserve The reserve object + **/ + function validateDropReserve(DataTypes.ReserveData storage reserve) external view { + require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_NULL); + require( + IERC20(reserve.stableDebtTokenAddress).totalSupply() == 0, + Errors.RL_STABLE_DEBT_NOT_NULL + ); + require( + IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0, + Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_NULL + ); + } } From 68b7384146e8c258fa34a699b37574e3eddd4c1b Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 7 Jun 2021 09:24:52 +0200 Subject: [PATCH 05/11] rename: renamed NOT_NULL Errors to NOT_ZERO --- contracts/protocol/libraries/helpers/Errors.sol | 6 +++--- contracts/protocol/libraries/logic/ValidationLogic.sol | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 1b984270..4b0644f5 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -109,9 +109,9 @@ library Errors { string public constant LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85'; string public constant VL_RESERVE_PAUSED = '86'; string public constant LPC_CALLER_NOT_RISK_OR_POOL_ADMIN = '87'; - string public constant RL_ATOKEN_SUPPLY_NOT_NULL = '88'; - string public constant RL_STABLE_DEBT_NOT_NULL = '89'; - string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_NULL = '90'; + string public constant RL_ATOKEN_SUPPLY_NOT_ZERO = '88'; + string public constant RL_STABLE_DEBT_NOT_ZERO = '89'; + string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90'; enum CollateralManagerErrors { NO_ERROR, diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 2f887d7e..3e4419dc 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -483,14 +483,14 @@ library ValidationLogic { * @param reserve The reserve object **/ function validateDropReserve(DataTypes.ReserveData storage reserve) external view { - require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_NULL); + require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_ZERO); require( IERC20(reserve.stableDebtTokenAddress).totalSupply() == 0, - Errors.RL_STABLE_DEBT_NOT_NULL + Errors.RL_STABLE_DEBT_NOT_ZERO ); require( IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0, - Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_NULL + Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO ); } } From 46d14e912434e9b6bc6cbda75cc34c22d698e779 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 7 Jun 2021 09:49:51 +0200 Subject: [PATCH 06/11] refactor: changed the order of requirements in validateDropReserve for easier testing --- contracts/protocol/libraries/logic/ValidationLogic.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 3e4419dc..e57f55fa 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -483,7 +483,6 @@ library ValidationLogic { * @param reserve The reserve object **/ function validateDropReserve(DataTypes.ReserveData storage reserve) external view { - require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_ZERO); require( IERC20(reserve.stableDebtTokenAddress).totalSupply() == 0, Errors.RL_STABLE_DEBT_NOT_ZERO @@ -492,5 +491,6 @@ library ValidationLogic { IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0, Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO ); + require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_ZERO); } } From 648604930e27e86ebe458ea33f3b12b47eab4bd2 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 7 Jun 2021 10:18:37 +0200 Subject: [PATCH 07/11] test: tested drop reserve --- helpers/types.ts | 3 + test-suites/test-aave/drop-reserve.spec.ts | 104 +++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 test-suites/test-aave/drop-reserve.spec.ts diff --git a/helpers/types.ts b/helpers/types.ts index 0e0ac375..157f34ba 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -184,6 +184,9 @@ export enum ProtocolErrors { LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85', VL_RESERVE_PAUSED = '86', LPC_CALLER_NOT_RISK_OR_POOL_ADMIN = '87', + RL_ATOKEN_SUPPLY_NOT_ZERO = '88', + RL_STABLE_DEBT_NOT_ZERO = '89', + RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90', // old diff --git a/test-suites/test-aave/drop-reserve.spec.ts b/test-suites/test-aave/drop-reserve.spec.ts new file mode 100644 index 00000000..9c98686a --- /dev/null +++ b/test-suites/test-aave/drop-reserve.spec.ts @@ -0,0 +1,104 @@ +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; +import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { parseEther, parseUnits } from 'ethers/lib/utils'; +import { BigNumber } from 'bignumber.js'; +import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; +import { getMockFlashLoanReceiver } from '../../helpers/contracts-getters'; +import { domainToUnicode } from 'url'; + +const { expect } = require('chai'); + +makeSuite('Pause Reserve', (testEnv: TestEnv) => { + let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; + + const { + RL_ATOKEN_SUPPLY_NOT_ZERO, + RL_STABLE_DEBT_NOT_ZERO, + RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO, + } = ProtocolErrors; + + before(async () => { + _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); + }); + + it('User 1 deposits Dai, User 2 borrow Dai stable and variable, should fail to drop Dai reserve', async () => { + const { + deployer, + users: [user1], + pool, + dai, + aDai, + weth, + configurator, + } = testEnv; + + const depositedAmount = parseEther('1000'); + const borrowedAmount = parseEther('100'); + console.log((await aDai.totalSupply()).toString()); + // setting reserve factor to 0 to ease tests, no aToken accrued in reserve + await configurator.setReserveFactor(dai.address, 0); + + await dai.mint(depositedAmount); + await dai.approve(pool.address, depositedAmount); + await dai.connect(user1.signer).mint(depositedAmount); + await dai.connect(user1.signer).approve(pool.address, depositedAmount); + + await weth.connect(user1.signer).mint(depositedAmount); + await weth.connect(user1.signer).approve(pool.address, depositedAmount); + + await pool.deposit(dai.address, depositedAmount, deployer.address, 0); + + await expect(configurator.dropReserve(dai.address)).to.be.revertedWith( + RL_ATOKEN_SUPPLY_NOT_ZERO + ); + + await pool.connect(user1.signer).deposit(weth.address, depositedAmount, user1.address, 0); + + await pool.connect(user1.signer).borrow(dai.address, borrowedAmount, 2, 0, user1.address); + await expect(configurator.dropReserve(dai.address)).to.be.revertedWith( + RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO + ); + await pool.connect(user1.signer).borrow(dai.address, borrowedAmount, 1, 0, user1.address); + await expect(configurator.dropReserve(dai.address)).to.be.revertedWith(RL_STABLE_DEBT_NOT_ZERO); + }); + it('User 2 repays debts, drop Dai reserve should fail', async () => { + const { + deployer, + users: [user1], + pool, + dai, + weth, + configurator, + } = testEnv; + await pool.connect(user1.signer).repay(dai.address, MAX_UINT_AMOUNT, 1, user1.address); + await expect(configurator.dropReserve(dai.address)).to.be.revertedWith( + RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO + ); + + await pool.connect(user1.signer).repay(dai.address, MAX_UINT_AMOUNT, 2, user1.address); + await expect(configurator.dropReserve(dai.address)).to.be.revertedWith( + RL_ATOKEN_SUPPLY_NOT_ZERO + ); + }); + it('User 1 withdraw Dai, drop Dai reserve should succeed', async () => { + const { + deployer, + users: [user1], + pool, + dai, + aDai, + weth, + configurator, + } = testEnv; + + await pool.withdraw(dai.address, MAX_UINT_AMOUNT, deployer.address); + console.log((await aDai.totalSupply()).toString()); + await configurator.dropReserve(dai.address); + + const tokens = await pool.getReservesList(); + + expect(tokens.includes(dai.address)).to.be.false; + }); +}); From a0f6199ea36a3e9d4240f0cbc1a9b8d85731c2d5 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 7 Jun 2021 10:26:38 +0200 Subject: [PATCH 08/11] doc: added doc to drop reserve --- contracts/interfaces/ILendingPoolConfigurator.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index 75b8c47e..312aebed 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -404,4 +404,10 @@ interface ILendingPoolConfigurator { * @param flashBorrower The address of the unauthorized borrower **/ function unauthorizeFlashBorrower(address flashBorrower) external; + + /** + * @dev Drops a reserve entirely + * @param asset the address of the reserve to drop + **/ + function dropReserve(address asset) external; } From 58ab73272ea0c6f0957969a4c72c1326e0234cc7 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 7 Jun 2021 13:43:04 +0200 Subject: [PATCH 09/11] test: added testing droped reserve is inactive --- test-suites/test-aave/drop-reserve.spec.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test-suites/test-aave/drop-reserve.spec.ts b/test-suites/test-aave/drop-reserve.spec.ts index 9c98686a..f4e9b68a 100644 --- a/test-suites/test-aave/drop-reserve.spec.ts +++ b/test-suites/test-aave/drop-reserve.spec.ts @@ -36,7 +36,6 @@ makeSuite('Pause Reserve', (testEnv: TestEnv) => { const depositedAmount = parseEther('1000'); const borrowedAmount = parseEther('100'); - console.log((await aDai.totalSupply()).toString()); // setting reserve factor to 0 to ease tests, no aToken accrued in reserve await configurator.setReserveFactor(dai.address, 0); @@ -91,14 +90,18 @@ makeSuite('Pause Reserve', (testEnv: TestEnv) => { aDai, weth, configurator, + helpersContract, } = testEnv; await pool.withdraw(dai.address, MAX_UINT_AMOUNT, deployer.address); - console.log((await aDai.totalSupply()).toString()); await configurator.dropReserve(dai.address); const tokens = await pool.getReservesList(); expect(tokens.includes(dai.address)).to.be.false; + + const { isActive } = await helpersContract.getReserveConfigurationData(dai.address); + + expect(isActive).to.be.false; }); }); From 5bbbc78d0b3e6e7e281dafb7dd7da8013a381edd Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Tue, 8 Jun 2021 18:04:33 +0200 Subject: [PATCH 10/11] fix: test naming --- test-suites/test-aave/drop-reserve.spec.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test-suites/test-aave/drop-reserve.spec.ts b/test-suites/test-aave/drop-reserve.spec.ts index f4e9b68a..d2257f79 100644 --- a/test-suites/test-aave/drop-reserve.spec.ts +++ b/test-suites/test-aave/drop-reserve.spec.ts @@ -10,14 +10,11 @@ import { domainToUnicode } from 'url'; const { expect } = require('chai'); -makeSuite('Pause Reserve', (testEnv: TestEnv) => { +makeSuite('Drop Reserve', (testEnv: TestEnv) => { let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; - const { - RL_ATOKEN_SUPPLY_NOT_ZERO, - RL_STABLE_DEBT_NOT_ZERO, - RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO, - } = ProtocolErrors; + const { RL_ATOKEN_SUPPLY_NOT_ZERO, RL_STABLE_DEBT_NOT_ZERO, RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO } = + ProtocolErrors; before(async () => { _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); From d708ff4971375fd5f606b07ed698fb394985dc83 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Tue, 8 Jun 2021 18:05:26 +0200 Subject: [PATCH 11/11] fix: added override to drop reserve function --- contracts/protocol/lendingpool/LendingPoolConfigurator.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index a7cc2f77..29c725cf 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -160,7 +160,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } /// @inheritdoc ILendingPoolConfigurator - function dropReserve(address asset) external onlyPoolAdmin { + function dropReserve(address asset) external override onlyPoolAdmin { _pool.dropReserve(asset); emit ReserveDropped(asset); }