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(