diff --git a/contracts/deployments/ATokensAndRatesHelper.sol b/contracts/deployments/ATokensAndRatesHelper.sol index ee8f6697..2240a514 100644 --- a/contracts/deployments/ATokensAndRatesHelper.sol +++ b/contracts/deployments/ATokensAndRatesHelper.sol @@ -32,6 +32,7 @@ contract ATokensAndRatesHelper is Ownable { uint256 liquidationBonus; uint256 reserveFactor; uint256 borrowCap; + uint256 supplyCap; bool stableBorrowingEnabled; } @@ -79,6 +80,7 @@ contract ATokensAndRatesHelper is Ownable { inputParams[i].borrowCap, inputParams[i].stableBorrowingEnabled ); + configurator.setSupplyCap(inputParams[i].asset, inputParams[i].supplyCap); configurator.setReserveFactor(inputParams[i].asset, inputParams[i].reserveFactor); } } diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index 2dcd8259..fea5560c 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -134,6 +134,13 @@ interface ILendingPoolConfigurator { **/ event BorrowCapChanged(address indexed asset, uint256 borrowCap); + /** + * @dev Emitted when the supply cap of a reserve is updated + * @param asset The address of the underlying asset of the reserve + * @param supplyCap The new supply cap + **/ + event SupplyCapChanged(address indexed asset, uint256 supplyCap); + /** * @dev Emitted when the reserve decimals are updated * @param asset The address of the underlying asset of the reserve diff --git a/contracts/misc/AaveProtocolDataProvider.sol b/contracts/misc/AaveProtocolDataProvider.sol index 78e26b95..d07a5f1b 100644 --- a/contracts/misc/AaveProtocolDataProvider.sol +++ b/contracts/misc/AaveProtocolDataProvider.sol @@ -64,6 +64,7 @@ contract AaveProtocolDataProvider { return aTokens; } + // not returning borrow and supply caps for compatibility function getReserveConfigurationData(address asset) external view @@ -83,7 +84,7 @@ contract AaveProtocolDataProvider { DataTypes.ReserveConfigurationMap memory configuration = ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getConfiguration(asset); - (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor, ) = + (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor) = configuration.getParamsMemory(); (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled) = @@ -92,14 +93,14 @@ contract AaveProtocolDataProvider { usageAsCollateralEnabled = liquidationThreshold > 0; } - function getReserveBorrowCap(address asset) + function getReserveCaps(address asset) external view - returns (uint256 borrowCap) { + returns (uint256 borrowCap, uint256 supplyCap) { - (, , , , , borrowCap) = ILendingPool(ADDRESSES_PROVIDER.getLendingPool()) + (borrowCap, supplyCap) = ILendingPool(ADDRESSES_PROVIDER.getLendingPool()) .getConfiguration(asset) - .getParamsMemory(); + .getCapsMemory(); } function getReserveData(address asset) diff --git a/contracts/misc/UiPoolDataProvider.sol b/contracts/misc/UiPoolDataProvider.sol index b59da60f..9643385a 100644 --- a/contracts/misc/UiPoolDataProvider.sol +++ b/contracts/misc/UiPoolDataProvider.sol @@ -104,9 +104,12 @@ contract UiPoolDataProvider is IUiPoolDataProvider { reserveData.reserveLiquidationThreshold, reserveData.reserveLiquidationBonus, reserveData.decimals, - reserveData.reserveFactor, - reserveData.borrowCap + reserveData.reserveFactor ) = baseData.configuration.getParamsMemory(); + ( + reserveData.borrowCap, + reserveData.supplyCap + ) = baseData.configuration.getCapsMemory(); ( reserveData.isActive, reserveData.isFrozen, diff --git a/contracts/misc/interfaces/IUiPoolDataProvider.sol b/contracts/misc/interfaces/IUiPoolDataProvider.sol index a93c0a43..06979a14 100644 --- a/contracts/misc/interfaces/IUiPoolDataProvider.sol +++ b/contracts/misc/interfaces/IUiPoolDataProvider.sol @@ -15,6 +15,7 @@ interface IUiPoolDataProvider { uint256 reserveLiquidationBonus; uint256 reserveFactor; uint256 borrowCap; + uint256 supplyCap; bool usageAsCollateralEnabled; bool borrowingEnabled; bool stableBorrowRateEnabled; diff --git a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol index 53e8fc52..80692726 100644 --- a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol +++ b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol @@ -286,7 +286,7 @@ contract LendingPoolCollateralManager is vars.collateralPrice = oracle.getAssetPrice(collateralAsset); vars.debtAssetPrice = oracle.getAssetPrice(debtAsset); - (, , vars.liquidationBonus, vars.collateralDecimals, , ) = collateralReserve + (, , vars.liquidationBonus, vars.collateralDecimals, ) = collateralReserve .configuration .getParams(); vars.debtAssetDecimals = debtReserve.configuration.getDecimals(); diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 66100e1e..85cd32cf 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -149,7 +149,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); - (, , , uint256 decimals, , ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); bytes memory encodedCall = abi.encodeWithSelector( IInitializableAToken.initialize.selector, @@ -180,7 +180,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); - (, , , uint256 decimals, , ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); bytes memory encodedCall = abi.encodeWithSelector( IInitializableDebtToken.initialize.selector, @@ -217,7 +217,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); - (, , , uint256 decimals, , ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); bytes memory encodedCall = abi.encodeWithSelector( IInitializableDebtToken.initialize.selector, @@ -446,6 +446,21 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur emit BorrowCapChanged(asset, borrowCap); } + /** + * @dev Updates the supply cap of a reserve + * @param asset The address of the underlying asset of the reserve + * @param supplyCap The new supply of the reserve + **/ + function setSupplyCap(address asset, uint256 supplyCap) external onlyPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setSupplyCap(supplyCap); + + pool.setConfiguration(asset, currentConfig.data); + + emit SupplyCapChanged(asset, supplyCap); + } + /** * @dev Sets the interest rate strategy of a reserve * @param asset The address of the underlying asset of the reserve diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index 51fabbf9..e849e321 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -20,6 +20,7 @@ library ReserveConfiguration { uint256 constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore uint256 constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant BORROW_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant SUPPLY_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFF; // prettier-ignore /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed uint256 constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; @@ -31,6 +32,7 @@ library ReserveConfiguration { uint256 constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59; uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64; uint256 constant BORROW_CAP_START_BIT_POSITION = 80; + uint256 constant SUPPLY_CAP_START_BIT_POSITION = 128; uint256 constant MAX_VALID_LTV = 65535; uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; @@ -38,6 +40,7 @@ library ReserveConfiguration { uint256 constant MAX_VALID_DECIMALS = 255; uint256 constant MAX_VALID_RESERVE_FACTOR = 65535; uint256 constant MAX_VALID_BORROW_CAP = 281474976710655; + uint256 constant MAX_VALID_SUPPLY_CAP = 281474976710655; /** * @dev Sets the Loan to Value of the reserve @@ -296,6 +299,35 @@ library ReserveConfiguration { return (self.data & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION; } + /** + * @dev Sets the supply cap of the reserve + * @param self The reserve configuration + * @param supplyCap The supply cap + **/ + function setSupplyCap(DataTypes.ReserveConfigurationMap memory self, uint256 supplyCap) + internal + pure + { + require(supplyCap <= MAX_VALID_SUPPLY_CAP, Errors.RC_INVALID_SUPPLY_CAP); + + self.data = + (self.data & SUPPLY_CAP_MASK) | + (supplyCap << SUPPLY_CAP_START_BIT_POSITION); + } + + /** + * @dev Gets the supply cap of the reserve + * @param self The reserve configuration + * @return The supply cap + **/ + function getSupplyCap(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (uint256) + { + return (self.data & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION; + } + /** * @dev Gets the configuration flags of the reserve * @param self The reserve configuration @@ -324,7 +356,7 @@ library ReserveConfiguration { /** * @dev Gets the configuration paramters of the reserve from storage * @param self The reserve configuration - * @return The state params representing ltv, liquidation threshold, liquidation bonus, reserve decimals, reserve factor and borrow cap. + * @return The state params representing ltv, liquidation threshold, liquidation bonus, reserve decimals, reserve factor **/ function getParams(DataTypes.ReserveConfigurationMap storage self) internal @@ -334,7 +366,6 @@ library ReserveConfiguration { uint256, uint256, uint256, - uint256, uint256 ) { @@ -345,15 +376,35 @@ library ReserveConfiguration { (dataLocal & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, (dataLocal & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, (dataLocal & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, - (dataLocal & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION, - (self.data & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION + (dataLocal & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION + ); + } + + /** + * @dev Gets the caps paramters of the reserve from storage + * @param self The reserve configuration + * @return The state params representing borrow cap and supply cap. + **/ + function getCaps(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns ( + uint256, + uint256 + ) + { + uint256 dataLocal = self.data; + + return ( + (self.data & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION, + (self.data & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION ); } /** * @dev Gets the configuration paramters of the reserve from a memory object * @param self The reserve configuration - * @return The state params representing ltv, liquidation threshold, liquidation bonus, reserve decimals, reserve factor, borrow cap + * @return The state params representing ltv, liquidation threshold, liquidation bonus, reserve decimals, reserve factor **/ function getParamsMemory(DataTypes.ReserveConfigurationMap memory self) internal @@ -363,7 +414,6 @@ library ReserveConfiguration { uint256, uint256, uint256, - uint256, uint256 ) { @@ -372,8 +422,26 @@ library ReserveConfiguration { (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, - (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION, - (self.data & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION + (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION + ); + } + + /** + * @dev Gets the caps paramters of the reserve from a memory object + * @param self The reserve configuration + * @return The state params borrow cap and supply cap + **/ + function getCapsMemory(DataTypes.ReserveConfigurationMap memory self) + internal + pure + returns ( + uint256, + uint256 + ) + { + return ( + (self.data & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION, + (self.data & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION ); } diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 6ee6cced..8ae23f7b 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -105,6 +105,8 @@ library Errors { string public constant SDT_BURN_EXCEEDS_BALANCE = '80'; string public constant VL_BORROW_CAP_EXCEEDED = '81'; string public constant RC_INVALID_BORROW_CAP = '82'; + string public constant VL_SUPPLY_CAP_EXCEEDED = '83'; + string public constant RC_INVALID_SUPPLY_CAP = '84'; enum CollateralManagerErrors { NO_ERROR, diff --git a/contracts/protocol/libraries/logic/GenericLogic.sol b/contracts/protocol/libraries/logic/GenericLogic.sol index 9f457a17..d4081dda 100644 --- a/contracts/protocol/libraries/logic/GenericLogic.sol +++ b/contracts/protocol/libraries/logic/GenericLogic.sol @@ -68,7 +68,7 @@ library GenericLogic { balanceDecreaseAllowedLocalVars memory vars; - (, vars.liquidationThreshold, , vars.decimals, , ) = reservesData[asset] + (, vars.liquidationThreshold, , vars.decimals, ) = reservesData[asset] .configuration .getParams(); @@ -178,7 +178,7 @@ library GenericLogic { vars.currentReserveAddress = reserves[vars.i]; DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress]; - (vars.ltv, vars.liquidationThreshold, , vars.decimals, , ) = currentReserve + (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve .configuration .getParams(); diff --git a/test-suites/test-amm/configurator.spec.ts b/test-suites/test-amm/configurator.spec.ts index 4884d070..4ba20620 100644 --- a/test-suites/test-amm/configurator.spec.ts +++ b/test-suites/test-amm/configurator.spec.ts @@ -178,7 +178,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); - expect(stableBorrowRateEnabled).to.be.equal(true/*strategyWETH.stableBorrowRateEnabled*/); + expect(stableBorrowRateEnabled).to.be.equal(true /*strategyWETH.stableBorrowRateEnabled*/); expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); expect(variableBorrowIndex.toString()).to.be.equal(RAY); @@ -195,7 +195,9 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { it('Check the onlyAaveAdmin on enableBorrowingOnReserve ', async () => { const { configurator, users, weth } = testEnv; await expect( - configurator.connect(users[2].signer).enableBorrowingOnReserve(weth.address, MAX_BORROW_CAP, true), + configurator + .connect(users[2].signer) + .enableBorrowingOnReserve(weth.address, MAX_BORROW_CAP, true), CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); @@ -250,7 +252,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); - expect(stableBorrowRateEnabled).to.be.equal(true/*strategyWETH.stableBorrowRateEnabled*/); + expect(stableBorrowRateEnabled).to.be.equal(true /*strategyWETH.stableBorrowRateEnabled*/); expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); });