diff --git a/contracts/deployments/ATokensAndRatesHelper.sol b/contracts/deployments/ATokensAndRatesHelper.sol index 2cac535b..15a5e81a 100644 --- a/contracts/deployments/ATokensAndRatesHelper.sol +++ b/contracts/deployments/ATokensAndRatesHelper.sol @@ -31,6 +31,8 @@ contract ATokensAndRatesHelper is Ownable { uint256 liquidationThreshold; uint256 liquidationBonus; uint256 reserveFactor; + uint256 borrowCap; + uint256 supplyCap; bool stableBorrowingEnabled; bool borrowingEnabled; } @@ -77,9 +79,11 @@ contract ATokensAndRatesHelper is Ownable { if (inputParams[i].borrowingEnabled) { configurator.enableBorrowingOnReserve( inputParams[i].asset, + 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 7554f2a8..fea5560c 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -127,6 +127,20 @@ interface ILendingPoolConfigurator { **/ event ReserveFactorChanged(address indexed asset, uint256 factor); + /** + * @dev Emitted when the borrow cap of a reserve is updated + * @param asset The address of the underlying asset of the reserve + * @param borrowCap The new borrow cap + **/ + 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 56f7c96e..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 @@ -80,18 +81,28 @@ contract AaveProtocolDataProvider { bool isFrozen ) { - DataTypes.ReserveConfigurationMap memory configuration = + DataTypes.ReserveConfigurationMap memory configuration = ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getConfiguration(asset); - (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor) = configuration - .getParamsMemory(); + (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor) = + configuration.getParamsMemory(); - (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled) = configuration - .getFlagsMemory(); + (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled) = + configuration.getFlagsMemory(); usageAsCollateralEnabled = liquidationThreshold > 0; } + function getReserveCaps(address asset) + external + view + returns (uint256 borrowCap, uint256 supplyCap) { + + (borrowCap, supplyCap) = ILendingPool(ADDRESSES_PROVIDER.getLendingPool()) + .getConfiguration(asset) + .getCapsMemory(); + } + function getReserveData(address asset) external view diff --git a/contracts/misc/UiPoolDataProvider.sol b/contracts/misc/UiPoolDataProvider.sol index 63dd7a1c..2a1b6bae 100644 --- a/contracts/misc/UiPoolDataProvider.sol +++ b/contracts/misc/UiPoolDataProvider.sol @@ -114,6 +114,10 @@ contract UiPoolDataProvider is IUiPoolDataProvider { reserveData.decimals, 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 34f06d1c..5ea96464 100644 --- a/contracts/misc/interfaces/IUiPoolDataProvider.sol +++ b/contracts/misc/interfaces/IUiPoolDataProvider.sol @@ -15,6 +15,8 @@ interface IUiPoolDataProvider { uint256 reserveLiquidationThreshold; uint256 reserveLiquidationBonus; uint256 reserveFactor; + uint256 borrowCap; + uint256 supplyCap; bool usageAsCollateralEnabled; bool borrowingEnabled; bool stableBorrowRateEnabled; diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 49451d92..4b8662c7 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -151,7 +151,8 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); - bytes memory encodedCall = abi.encodeWithSelector( + bytes memory encodedCall = + abi.encodeWithSelector( IInitializableAToken.initialize.selector, cachedPool, input.treasury, @@ -163,11 +164,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur input.params ); - _upgradeTokenImplementation( - reserveData.aTokenAddress, - input.implementation, - encodedCall - ); + _upgradeTokenImplementation(reserveData.aTokenAddress, input.implementation, encodedCall); emit ATokenUpgraded(input.asset, reserveData.aTokenAddress, input.implementation); } @@ -179,10 +176,11 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur ILendingPool cachedPool = pool; DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); - + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); - bytes memory encodedCall = abi.encodeWithSelector( + bytes memory encodedCall = + abi.encodeWithSelector( IInitializableDebtToken.initialize.selector, cachedPool, input.asset, @@ -209,17 +207,15 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur /** * @dev Updates the variable debt token implementation for the asset **/ - function updateVariableDebtToken(UpdateDebtTokenInput calldata input) - external - onlyPoolAdmin - { + function updateVariableDebtToken(UpdateDebtTokenInput calldata input) external onlyPoolAdmin { ILendingPool cachedPool = pool; DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); - bytes memory encodedCall = abi.encodeWithSelector( + bytes memory encodedCall = + abi.encodeWithSelector( IInitializableDebtToken.initialize.selector, cachedPool, input.asset, @@ -248,17 +244,20 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur * @param asset The address of the underlying asset of the reserve * @param stableBorrowRateEnabled True if stable borrow rate needs to be enabled by default on this reserve **/ - function enableBorrowingOnReserve(address asset, bool stableBorrowRateEnabled) - external - onlyPoolAdmin - { + function enableBorrowingOnReserve( + address asset, + uint256 borrowCap, + bool stableBorrowRateEnabled + ) external onlyPoolAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setBorrowingEnabled(true); + currentConfig.setBorrowCap(borrowCap); currentConfig.setStableRateBorrowingEnabled(stableBorrowRateEnabled); pool.setConfiguration(asset, currentConfig.data); + emit BorrowCapChanged(asset, borrowCap); emit BorrowingEnabledOnReserve(asset, stableBorrowRateEnabled); } @@ -430,6 +429,36 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur emit ReserveFactorChanged(asset, reserveFactor); } + /** + * @dev Updates the borrow cap of a reserve + * @param asset The address of the underlying asset of the reserve + * @param borrowCap The new borrow of the reserve + **/ + function setBorrowCap(address asset, uint256 borrowCap) external onlyPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setBorrowCap(borrowCap); + + pool.setConfiguration(asset, currentConfig.data); + + 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 5649a580..567ee93b 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -19,6 +19,8 @@ library ReserveConfiguration { uint256 constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore uint256 constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore uint256 constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant BORROW_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant SUPPLY_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 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; @@ -29,12 +31,16 @@ library ReserveConfiguration { uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58; 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 = 116; uint256 constant MAX_VALID_LTV = 65535; uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; uint256 constant MAX_VALID_LIQUIDATION_BONUS = 65535; uint256 constant MAX_VALID_DECIMALS = 255; uint256 constant MAX_VALID_RESERVE_FACTOR = 65535; + uint256 constant MAX_VALID_BORROW_CAP = 68719476735; + uint256 constant MAX_VALID_SUPPLY_CAP = 68719476735; /** * @dev Sets the Loan to Value of the reserve @@ -264,6 +270,60 @@ library ReserveConfiguration { return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; } + /** + * @dev Sets the borrow cap of the reserve + * @param self The reserve configuration + * @param borrowCap The borrow cap + **/ + function setBorrowCap(DataTypes.ReserveConfigurationMap memory self, uint256 borrowCap) + internal + pure + { + require(borrowCap <= MAX_VALID_BORROW_CAP, Errors.RC_INVALID_BORROW_CAP); + + self.data = (self.data & BORROW_CAP_MASK) | (borrowCap << BORROW_CAP_START_BIT_POSITION); + } + + /** + * @dev Gets the borrow cap of the reserve + * @param self The reserve configuration + * @return The borrow cap + **/ + function getBorrowCap(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (uint256) + { + 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 @@ -290,9 +350,9 @@ library ReserveConfiguration { } /** - * @dev Gets the configuration paramters of the reserve + * @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, the reserve decimals + * @return The state params representing ltv, liquidation threshold, liquidation bonus, reserve decimals, reserve factor **/ function getParams(DataTypes.ReserveConfigurationMap storage self) internal @@ -316,10 +376,28 @@ library ReserveConfiguration { ); } + /** + * @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 ( + (dataLocal & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION, + (dataLocal & ~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, the reserve decimals + * @return The state params representing ltv, liquidation threshold, liquidation bonus, reserve decimals, reserve factor **/ function getParamsMemory(DataTypes.ReserveConfigurationMap memory self) internal @@ -341,6 +419,22 @@ library ReserveConfiguration { ); } + /** + * @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 + ); + } + /** * @dev Gets the configuration flags of the reserve from a memory object * @param self The reserve configuration @@ -363,4 +457,30 @@ library ReserveConfiguration { (self.data & ~STABLE_BORROWING_MASK) != 0 ); } + + /** + * @dev Gets the supply cap of the reserve from a memory objet + * @param self The reserve configuration + * @return The supply cap + **/ + function getSupplyCapMemory(DataTypes.ReserveConfigurationMap memory self) + internal + pure + returns (uint256) + { + return (self.data & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION; + } + + /** + * @dev Gets the borrow cap of the reserve from a memory object + * @param self The reserve configuration + * @return The borrow cap + **/ + function getBorrowCapMemory(DataTypes.ReserveConfigurationMap memory self) + internal + pure + returns (uint256) + { + return (self.data & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION; + } } diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 9729379d..34e3521f 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -102,6 +102,10 @@ library Errors { string public constant LP_NOT_CONTRACT = '78'; string public constant SDT_STABLE_DEBT_OVERFLOW = '79'; 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/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 32f2de4d..0c0b7028 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -14,6 +14,8 @@ import {UserConfiguration} from '../configuration/UserConfiguration.sol'; import {Errors} from '../helpers/Errors.sol'; import {Helpers} from '../helpers/Helpers.sol'; import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; +import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; +import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; import {DataTypes} from '../types/DataTypes.sol'; /** @@ -39,14 +41,22 @@ library ValidationLogic { * @param amount The amount to be deposited */ function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) internal view { - (bool isActive, bool isFrozen, , ) = reserve.configuration.getFlags(); + DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration; + (bool isActive, bool isFrozen, , ) = reserveConfiguration.getFlagsMemory(); + (, , , uint256 reserveDecimals, ) = reserveConfiguration.getParamsMemory(); + uint256 supplyCap = reserveConfiguration.getSupplyCapMemory(); require(amount != 0, Errors.VL_INVALID_AMOUNT); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isFrozen, Errors.VL_RESERVE_FROZEN); + require( + supplyCap == 0 || + IERC20(reserve.aTokenAddress).totalSupply().add(amount).div(10**reserveDecimals) < + supplyCap, + Errors.VL_SUPPLY_CAP_EXCEEDED + ); } - /** * @dev Validates a withdraw action * @param reserve The reserve object @@ -57,7 +67,7 @@ library ValidationLogic { DataTypes.ReserveData storage reserve, uint256 amount, uint256 userBalance - ) internal view { + ) external view { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); @@ -73,6 +83,10 @@ library ValidationLogic { uint256 userBorrowBalanceETH; uint256 availableLiquidity; uint256 healthFactor; + uint256 totalSupplyStableDebt; + uint256 totalSupplyVariableDebt; + uint256 reserveDecimals; + uint256 borrowCap; bool isActive; bool isFrozen; bool borrowingEnabled; @@ -110,9 +124,15 @@ library ValidationLogic { ) internal view { ValidateBorrowLocalVars memory vars; - (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled) = reserve - .configuration - .getFlags(); + DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration; + (, , , vars.reserveDecimals, ) = reserveConfiguration.getParamsMemory(); + + ( + vars.isActive, + vars.isFrozen, + vars.borrowingEnabled, + vars.stableRateBorrowingEnabled + ) = reserveConfiguration.getFlagsMemory(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!vars.isFrozen, Errors.VL_RESERVE_FROZEN); @@ -127,6 +147,19 @@ library ValidationLogic { Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED ); + vars.totalSupplyStableDebt = IERC20(reserve.stableDebtTokenAddress).totalSupply(); + vars.borrowCap = reserveConfiguration.getBorrowCapMemory(); + vars.totalSupplyVariableDebt = IERC20(reserve.variableDebtTokenAddress).totalSupply(); + + require( + vars.borrowCap == 0 || + vars.totalSupplyStableDebt.add(vars.totalSupplyVariableDebt).add(amount).div( + 10**vars.reserveDecimals + ) < + vars.borrowCap, + Errors.VL_BORROW_CAP_EXCEEDED + ); + ( vars.userCollateralBalanceETH, vars.userBorrowBalanceETH, @@ -204,7 +237,7 @@ library ValidationLogic { address onBehalfOf, uint256 stableDebt, uint256 variableDebt - ) internal view { + ) external view { bool isActive = reserve.configuration.getActive(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); @@ -313,9 +346,7 @@ library ValidationLogic { * @dev Validates the action of setting an asset as collateral * @param reserve The state of the reserve that the user is enabling or disabling as collateral */ - function validateSetUseReserveAsCollateral( - DataTypes.ReserveData storage reserve - ) external view { + function validateSetUseReserveAsCollateral(DataTypes.ReserveData storage reserve) external view { uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); @@ -326,7 +357,7 @@ library ValidationLogic { * @param assets The assets being flashborrowed * @param amounts The amounts for each asset being borrowed **/ - function validateFlashloan(address[] memory assets, uint256[] memory amounts) internal pure { + function validateFlashloan(address[] memory assets, uint256[] memory amounts) external pure { require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS); } diff --git a/contracts/protocol/libraries/types/DataTypes.sol b/contracts/protocol/libraries/types/DataTypes.sol index a19e5efc..5c620eaa 100644 --- a/contracts/protocol/libraries/types/DataTypes.sol +++ b/contracts/protocol/libraries/types/DataTypes.sol @@ -38,6 +38,8 @@ library DataTypes { //bit 59: stable rate borrowing enabled //bit 60-63: reserved //bit 64-79: reserve factor + //bit 80-115 borrow cap, borrowCap == 0 => disabled + //bit 116-152 supply cap, supplyCap == 0 => disabled uint256 data; } diff --git a/helpers/constants.ts b/helpers/constants.ts index 95e8922b..f2568ee0 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -15,6 +15,8 @@ export const oneEther = new BigNumber(Math.pow(10, 18)); export const oneRay = new BigNumber(Math.pow(10, 27)); export const MAX_UINT_AMOUNT = '115792089237316195423570985008687907853269984665640564039457584007913129639935'; +export const MAX_BORROW_CAP = '68719476735'; +export const MAX_SUPPLY_CAP = '68719476735'; export const ONE_YEAR = '31536000'; export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; export const ONE_ADDRESS = '0x0000000000000000000000000000000000000001'; diff --git a/helpers/init-helpers.ts b/helpers/init-helpers.ts index aa209583..678125a1 100644 --- a/helpers/init-helpers.ts +++ b/helpers/init-helpers.ts @@ -284,6 +284,8 @@ export const configureReservesByHelper = async ( liquidationThreshold: BigNumberish; liquidationBonus: BigNumberish; reserveFactor: BigNumberish; + borrowCap: BigNumberish; + supplyCap: BigNumberish; stableBorrowingEnabled: boolean; borrowingEnabled: boolean; }[] = []; @@ -295,6 +297,8 @@ export const configureReservesByHelper = async ( liquidationBonus, liquidationThreshold, reserveFactor, + borrowCap, + supplyCap, stableBorrowRateEnabled, borrowingEnabled, }, @@ -326,9 +330,11 @@ export const configureReservesByHelper = async ( inputParams.push({ asset: tokenAddress, baseLTV: baseLTVAsCollateral, - liquidationThreshold: liquidationThreshold, - liquidationBonus: liquidationBonus, - reserveFactor: reserveFactor, + liquidationThreshold, + liquidationBonus, + reserveFactor, + borrowCap, + supplyCap, stableBorrowingEnabled: stableBorrowRateEnabled, borrowingEnabled: borrowingEnabled, }); diff --git a/helpers/types.ts b/helpers/types.ts index b5cc62c8..ad6dbced 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -177,6 +177,10 @@ export enum ProtocolErrors { RC_INVALID_DECIMALS = '70', RC_INVALID_RESERVE_FACTOR = '71', LPAPR_INVALID_ADDRESSES_PROVIDER_ID = '72', + VL_BORROW_CAP_EXCEEDED = '81', + RC_INVALID_BORROW_CAP = '82', + VL_SUPPLY_CAP_EXCEEDED = '83', + RC_INVALID_SUPPLY_CAP = '84', // old @@ -356,6 +360,7 @@ export enum TokenContractId { export interface IReserveParams extends IReserveBorrowParams, IReserveCollateralParams { aTokenImpl: eContractid; reserveFactor: string; + supplyCap: string; strategy: IInterestRateStrategyParams; } @@ -379,6 +384,7 @@ export interface IReserveBorrowParams { borrowingEnabled: boolean; stableBorrowRateEnabled: boolean; reserveDecimals: string; + borrowCap: string; } export interface IReserveCollateralParams { diff --git a/markets/aave/reservesConfigs.ts b/markets/aave/reservesConfigs.ts index a29e16d7..f44e4dc8 100644 --- a/markets/aave/reservesConfigs.ts +++ b/markets/aave/reservesConfigs.ts @@ -1,6 +1,6 @@ import { eContractid, IReserveParams } from '../../helpers/types'; -import { +import { rateStrategyStableOne, rateStrategyStableTwo, rateStrategyStableThree, @@ -21,7 +21,9 @@ export const strategyBUSD: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyDAI: IReserveParams = { @@ -33,7 +35,9 @@ export const strategyDAI: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategySUSD: IReserveParams = { @@ -45,7 +49,9 @@ export const strategySUSD: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyTUSD: IReserveParams = { @@ -57,7 +63,9 @@ export const strategyTUSD: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyUSDC: IReserveParams = { @@ -69,7 +77,9 @@ export const strategyUSDC: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '6', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyUSDT: IReserveParams = { @@ -81,7 +91,9 @@ export const strategyUSDT: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '6', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyAAVE: IReserveParams = { @@ -93,7 +105,9 @@ export const strategyAAVE: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '0' + reserveFactor: '0', + borrowCap: '0', + supplyCap: '0', }; export const strategyBAT: IReserveParams = { @@ -105,7 +119,9 @@ export const strategyBAT: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyENJ: IReserveParams = { @@ -117,7 +133,9 @@ export const strategyENJ: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyWETH: IReserveParams = { @@ -129,7 +147,9 @@ export const strategyWETH: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyKNC: IReserveParams = { @@ -141,7 +161,9 @@ export const strategyKNC: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyLINK: IReserveParams = { @@ -153,7 +175,9 @@ export const strategyLINK: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyMANA: IReserveParams = { @@ -165,7 +189,9 @@ export const strategyMANA: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '3500' + reserveFactor: '3500', + borrowCap: '0', + supplyCap: '0', }; export const strategyMKR: IReserveParams = { @@ -177,7 +203,9 @@ export const strategyMKR: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyREN: IReserveParams = { @@ -189,7 +217,9 @@ export const strategyREN: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategySNX: IReserveParams = { @@ -201,7 +231,9 @@ export const strategySNX: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '3500' + reserveFactor: '3500', + borrowCap: '0', + supplyCap: '0', }; // Invalid borrow rates in params currently, replaced with snx params @@ -214,7 +246,9 @@ export const strategyUNI: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.DelegationAwareAToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyWBTC: IReserveParams = { @@ -226,7 +260,9 @@ export const strategyWBTC: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '8', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyYFI: IReserveParams = { @@ -238,7 +274,9 @@ export const strategyYFI: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyZRX: IReserveParams = { @@ -250,7 +288,9 @@ export const strategyZRX: IReserveParams = { stableBorrowRateEnabled: true, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyXSUSHI: IReserveParams = { @@ -263,4 +303,6 @@ export const strategyXSUSHI: IReserveParams = { reserveDecimals: '18', aTokenImpl: eContractid.AToken, reserveFactor: '3500', -}; \ No newline at end of file + borrowCap: '0', + supplyCap: '0', +}; diff --git a/markets/amm/reservesConfigs.ts b/markets/amm/reservesConfigs.ts index c92aa42f..d59f9ad6 100644 --- a/markets/amm/reservesConfigs.ts +++ b/markets/amm/reservesConfigs.ts @@ -1,10 +1,5 @@ -import { eContractid, IReserveParams} from '../../helpers/types'; -import { - rateStrategyAmmBase, - rateStrategyStable, - rateStrategyBaseOne, -} from './rateStrategies'; - +import { eContractid, IReserveParams } from '../../helpers/types'; +import { rateStrategyAmmBase, rateStrategyStable, rateStrategyBaseOne } from './rateStrategies'; export const strategyWETH: IReserveParams = { strategy: rateStrategyBaseOne, @@ -15,7 +10,9 @@ export const strategyWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyWBTC: IReserveParams = { @@ -27,7 +24,9 @@ export const strategyWBTC: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '8', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyDAI: IReserveParams = { @@ -39,7 +38,9 @@ export const strategyDAI: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyUSDC: IReserveParams = { @@ -51,7 +52,9 @@ export const strategyUSDC: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '6', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyUSDT: IReserveParams = { @@ -63,7 +66,9 @@ export const strategyUSDT: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '6', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyDAIWETH: IReserveParams = { @@ -75,7 +80,9 @@ export const strategyDAIWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyWBTCWETH: IReserveParams = { @@ -87,7 +94,9 @@ export const strategyWBTCWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1500' + reserveFactor: '1500', + borrowCap: '0', + supplyCap: '0', }; export const strategyAAVEWETH: IReserveParams = { @@ -99,7 +108,9 @@ export const strategyAAVEWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '500' + reserveFactor: '500', + borrowCap: '0', + supplyCap: '0', }; export const strategyBATWETH: IReserveParams = { @@ -111,7 +122,9 @@ export const strategyBATWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1500' + reserveFactor: '1500', + borrowCap: '0', + supplyCap: '0', }; export const strategyDAIUSDC: IReserveParams = { @@ -123,7 +136,9 @@ export const strategyDAIUSDC: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyCRVWETH: IReserveParams = { @@ -135,7 +150,9 @@ export const strategyCRVWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1500' + reserveFactor: '1500', + borrowCap: '0', + supplyCap: '0', }; export const strategyLINKWETH: IReserveParams = { @@ -147,7 +164,9 @@ export const strategyLINKWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1500' + reserveFactor: '1500', + borrowCap: '0', + supplyCap: '0', }; export const strategyMKRWETH: IReserveParams = { @@ -159,7 +178,9 @@ export const strategyMKRWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1500' + reserveFactor: '1500', + borrowCap: '0', + supplyCap: '0', }; export const strategyRENWETH: IReserveParams = { @@ -171,7 +192,9 @@ export const strategyRENWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1500' + reserveFactor: '1500', + borrowCap: '0', + supplyCap: '0', }; export const strategySNXWETH: IReserveParams = { @@ -183,7 +206,9 @@ export const strategySNXWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '2000' + reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyUNIWETH: IReserveParams = { @@ -195,7 +220,9 @@ export const strategyUNIWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1500' + reserveFactor: '1500', + borrowCap: '0', + supplyCap: '0', }; export const strategyUSDCWETH: IReserveParams = { @@ -207,7 +234,9 @@ export const strategyUSDCWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1000' + reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyWBTCUSDC: IReserveParams = { @@ -219,7 +248,9 @@ export const strategyWBTCUSDC: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1500' + reserveFactor: '1500', + borrowCap: '0', + supplyCap: '0', }; export const strategyYFIWETH: IReserveParams = { @@ -231,7 +262,9 @@ export const strategyYFIWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1500' + reserveFactor: '1500', + borrowCap: '0', + supplyCap: '0', }; export const strategyBALWETH: IReserveParams = { @@ -243,5 +276,7 @@ export const strategyBALWETH: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, - reserveFactor: '1500' -} \ No newline at end of file + reserveFactor: '1500', + borrowCap: '0', + supplyCap: '0', +}; diff --git a/markets/matic/reservesConfigs.ts b/markets/matic/reservesConfigs.ts index e9dd26d4..cbeba1a6 100644 --- a/markets/matic/reservesConfigs.ts +++ b/markets/matic/reservesConfigs.ts @@ -20,6 +20,8 @@ export const strategyDAI: IReserveParams = { reserveDecimals: '18', aTokenImpl: eContractid.AToken, reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyUSDC: IReserveParams = { @@ -32,6 +34,8 @@ export const strategyUSDC: IReserveParams = { reserveDecimals: '6', aTokenImpl: eContractid.AToken, reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyUSDT: IReserveParams = { @@ -44,6 +48,8 @@ export const strategyUSDT: IReserveParams = { reserveDecimals: '6', aTokenImpl: eContractid.AToken, reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyWETH: IReserveParams = { @@ -56,6 +62,8 @@ export const strategyWETH: IReserveParams = { reserveDecimals: '18', aTokenImpl: eContractid.AToken, reserveFactor: '1000', + borrowCap: '0', + supplyCap: '0', }; export const strategyWBTC: IReserveParams = { @@ -68,6 +76,8 @@ export const strategyWBTC: IReserveParams = { reserveDecimals: '8', aTokenImpl: eContractid.AToken, reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyMATIC: IReserveParams = { @@ -79,6 +89,8 @@ export const strategyMATIC: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, + borrowCap: '0', + supplyCap: '0', reserveFactor: '2000', }; @@ -91,5 +103,7 @@ export const strategyAAVE: IReserveParams = { stableBorrowRateEnabled: false, reserveDecimals: '18', aTokenImpl: eContractid.AToken, + borrowCap: '0', + supplyCap: '0', reserveFactor: '0', }; diff --git a/package.json b/package.json index 1caff7c1..cf887ed4 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,8 @@ "Andrey Kozlov ", "David Racero ", "Pol Sendra ", - "David Truong " + "David Truong ", + "Hadrien Charlanes " ], "license": "AGPLv3", "dependencies": { diff --git a/test-suites/test-aave/borrow-cap.spec.ts b/test-suites/test-aave/borrow-cap.spec.ts new file mode 100644 index 00000000..15dd0a4d --- /dev/null +++ b/test-suites/test-aave/borrow-cap.spec.ts @@ -0,0 +1,354 @@ +import { TestEnv, makeSuite } from './helpers/make-suite'; +import { + APPROVAL_AMOUNT_LENDING_POOL, + MAX_UINT_AMOUNT, + RAY, + MAX_BORROW_CAP, + MAX_SUPPLY_CAP, +} from '../../helpers/constants'; +import { ProtocolErrors } from '../../helpers/types'; +import { MintableERC20, WETH9, WETH9Mocked } from '../../types'; +import { parseEther } from '@ethersproject/units'; +import { BigNumber } from '@ethersproject/bignumber'; + +const { expect } = require('chai'); + +makeSuite('Borrow Cap', (testEnv: TestEnv) => { + const { VL_BORROW_CAP_EXCEEDED, RC_INVALID_BORROW_CAP } = ProtocolErrors; + + const unitParse = async (token: WETH9Mocked | MintableERC20, nb: string) => + BigNumber.from(nb).mul(BigNumber.from('10').pow((await token.decimals()) - 3)); + it('Reserves should initially have borrow cap disabled (borrowCap = 0)', async () => { + const { + configurator, + weth, + pool, + dai, + usdc, + deployer, + helpersContract, + users: [user1], + } = testEnv; + + const mintedAmount = parseEther('1000000000'); + // minting for main user + await dai.mint(mintedAmount); + await weth.mint(mintedAmount); + await usdc.mint(mintedAmount); + // minting for lp user + await dai.connect(user1.signer).mint(mintedAmount); + await weth.connect(user1.signer).mint(mintedAmount); + await usdc.connect(user1.signer).mint(mintedAmount); + + await dai.approve(pool.address, MAX_UINT_AMOUNT); + await weth.approve(pool.address, MAX_UINT_AMOUNT); + await usdc.approve(pool.address, MAX_UINT_AMOUNT); + await dai.connect(user1.signer).approve(pool.address, MAX_UINT_AMOUNT); + await weth.connect(user1.signer).approve(pool.address, MAX_UINT_AMOUNT); + await usdc.connect(user1.signer).approve(pool.address, MAX_UINT_AMOUNT); + + let usdcBorrowCap = (await helpersContract.getReserveCaps(usdc.address)).borrowCap; + let daiBorrowCap = (await helpersContract.getReserveCaps(dai.address)).borrowCap; + + expect(usdcBorrowCap).to.be.equal('0'); + expect(daiBorrowCap).to.be.equal('0'); + }); + it('Should be able to borrow 10 Dai stable, 10 USDC variable', async () => { + const { + configurator, + weth, + pool, + dai, + usdc, + deployer, + helpersContract, + users: [user1], + } = testEnv; + + const suppliedAmount = 1000; + const precisionSuppliedAmount = (suppliedAmount * 1000).toString(); + + const borrowedAmount = 10; + const precisionBorrowedAmount = (borrowedAmount * 1000).toString(); + + // deposit collateral + await pool.deposit( + weth.address, + await unitParse(weth, precisionSuppliedAmount), + deployer.address, + 0 + ); + // user 1 deposit more dai and usdc to be able to borrow + await pool + .connect(user1.signer) + .deposit(dai.address, await unitParse(dai, precisionSuppliedAmount), user1.address, 0); + + await pool + .connect(user1.signer) + .deposit(usdc.address, await unitParse(usdc, precisionSuppliedAmount), user1.address, 0); + + // borrow + await pool.borrow( + usdc.address, + await unitParse(usdc, precisionBorrowedAmount), + 2, + 0, + deployer.address + ); + + await pool.borrow( + dai.address, + await unitParse(dai, precisionBorrowedAmount), + 1, + 0, + deployer.address + ); + }); + it('Sets the borrow cap for Weth and DAI to 10 Units', async () => { + const { + configurator, + weth, + pool, + dai, + usdc, + deployer, + helpersContract, + users: [user1], + } = testEnv; + + await configurator.setBorrowCap(usdc.address, 10); + await configurator.setBorrowCap(dai.address, 10); + + const usdcBorrowCap = (await helpersContract.getReserveCaps(usdc.address)).borrowCap; + const daiBorrowCap = (await helpersContract.getReserveCaps(dai.address)).borrowCap; + + expect(usdcBorrowCap).to.be.equal(10); + expect(daiBorrowCap).to.be.equal(10); + }); + it('should fail to borrow any dai or usdc, stable or variable', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const borrowedAmount = 10; + const precisionBorrowedAmount = (borrowedAmount * 1000).toString(); + + await expect( + pool.borrow( + usdc.address, + await unitParse(usdc, precisionBorrowedAmount), + 2, + 0, + deployer.address + ) + ).to.be.revertedWith(VL_BORROW_CAP_EXCEEDED); + + await expect( + pool.borrow( + dai.address, + await unitParse(dai, precisionBorrowedAmount), + 2, + 0, + deployer.address + ) + ).to.be.revertedWith(VL_BORROW_CAP_EXCEEDED); + }); + it('Should fail to set the borrow cap for usdc and DAI to max cap + 1 Units', async () => { + const { configurator, usdc, pool, dai, deployer, helpersContract } = testEnv; + const newCap = Number(MAX_BORROW_CAP) + 1; + + await expect(configurator.setBorrowCap(usdc.address, newCap)).to.be.revertedWith( + RC_INVALID_BORROW_CAP + ); + await expect(configurator.setBorrowCap(dai.address, newCap)).to.be.revertedWith( + RC_INVALID_BORROW_CAP + ); + }); + it('Sets the borrow cap for usdc and DAI to 120 Units', async () => { + const { configurator, usdc, pool, dai, deployer, helpersContract } = testEnv; + const newCap = '120'; + + await configurator.setBorrowCap(usdc.address, newCap); + await configurator.setBorrowCap(dai.address, newCap); + + const usdcBorrowCap = (await helpersContract.getReserveCaps(usdc.address)).borrowCap; + const daiBorrowCap = (await helpersContract.getReserveCaps(dai.address)).borrowCap; + + expect(usdcBorrowCap).to.be.equal(newCap); + expect(daiBorrowCap).to.be.equal(newCap); + }); + it('Should succeed to borrow 10 stable dai and 10 variable usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const borrowedAmount = 10; + const precisionBorrowedAmount = (borrowedAmount * 1000).toString(); + await pool.borrow( + usdc.address, + await unitParse(usdc, precisionBorrowedAmount), + 2, + 0, + deployer.address + ); + + await pool.borrow( + dai.address, + await unitParse(dai, precisionBorrowedAmount), + 1, + 0, + deployer.address + ); + }); + it('should fail to borrow 100 variable dai and 100 stable usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const borrowedAmount = 100; + const precisionBorrowedAmount = (borrowedAmount * 1000).toString(); + + await expect( + pool.borrow( + usdc.address, + await unitParse(usdc, precisionBorrowedAmount), + 1, + 0, + deployer.address + ) + ).to.be.revertedWith(VL_BORROW_CAP_EXCEEDED); + + await expect( + pool.borrow( + dai.address, + await unitParse(dai, precisionBorrowedAmount), + 2, + 0, + deployer.address + ) + ).to.be.revertedWith(VL_BORROW_CAP_EXCEEDED); + }); + it('Should succeed to borrow 99 variable dai and 99 stable usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const borrowedAmount = 99; + const precisionBorrowedAmount = (borrowedAmount * 1000).toString(); + await pool.borrow( + usdc.address, + await unitParse(usdc, precisionBorrowedAmount), + 2, + 0, + deployer.address + ); + + await pool.borrow( + dai.address, + await unitParse(dai, precisionBorrowedAmount), + 1, + 0, + deployer.address + ); + }); + it('Raises the borrow cap for usdc and DAI to 1000 Units', async () => { + const { configurator, usdc, pool, dai, deployer, helpersContract } = testEnv; + const newCap = '1000'; + let usdcBorrowCap = (await helpersContract.getReserveCaps(usdc.address)).borrowCap; + let daiBorrowCap = (await helpersContract.getReserveCaps(dai.address)).borrowCap; + + await configurator.setBorrowCap(usdc.address, newCap); + await configurator.setBorrowCap(dai.address, newCap); + + usdcBorrowCap = (await helpersContract.getReserveCaps(usdc.address)).borrowCap; + daiBorrowCap = (await helpersContract.getReserveCaps(dai.address)).borrowCap; + + expect(usdcBorrowCap).to.be.equal(newCap); + expect(daiBorrowCap).to.be.equal(newCap); + }); + it('should succeed to borrow 100 variable dai and 100 stable usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const borrowedAmount = 100; + const precisionBorrowedAmount = (borrowedAmount * 1000).toString(); + + await pool.borrow( + usdc.address, + await unitParse(usdc, precisionBorrowedAmount), + 1, + 0, + deployer.address + ); + + await pool.borrow( + dai.address, + await unitParse(dai, precisionBorrowedAmount), + 2, + 0, + deployer.address + ); + }); + it('Lowers the borrow cap for usdc and DAI to 200 Units', async () => { + const { configurator, usdc, pool, dai, deployer, helpersContract } = testEnv; + const newCap = '200'; + let usdcBorrowCap = (await helpersContract.getReserveCaps(usdc.address)).borrowCap; + let daiBorrowCap = (await helpersContract.getReserveCaps(dai.address)).borrowCap; + + await configurator.setBorrowCap(usdc.address, newCap); + await configurator.setBorrowCap(dai.address, newCap); + + usdcBorrowCap = (await helpersContract.getReserveCaps(usdc.address)).borrowCap; + daiBorrowCap = (await helpersContract.getReserveCaps(dai.address)).borrowCap; + + expect(usdcBorrowCap).to.be.equal(newCap); + expect(daiBorrowCap).to.be.equal(newCap); + }); + it('should fail to borrow 100 variable dai and 100 stable usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const borrowedAmount = 100; + const precisionBorrowedAmount = (borrowedAmount * 1000).toString(); + + await expect( + pool.borrow( + usdc.address, + await unitParse(usdc, precisionBorrowedAmount), + 1, + 0, + deployer.address + ) + ).to.be.revertedWith(VL_BORROW_CAP_EXCEEDED); + + await expect( + pool.borrow( + dai.address, + await unitParse(dai, precisionBorrowedAmount), + 2, + 0, + deployer.address + ) + ).to.be.revertedWith(VL_BORROW_CAP_EXCEEDED); + }); + it('Raises the borrow cap for usdc and DAI to max cap Units', async () => { + const { configurator, usdc, pool, dai, deployer, helpersContract } = testEnv; + const newCap = MAX_BORROW_CAP; + let usdcBorrowCap = (await helpersContract.getReserveCaps(usdc.address)).borrowCap; + let daiBorrowCap = (await helpersContract.getReserveCaps(dai.address)).borrowCap; + + await configurator.setBorrowCap(usdc.address, newCap); + await configurator.setBorrowCap(dai.address, newCap); + + usdcBorrowCap = (await helpersContract.getReserveCaps(usdc.address)).borrowCap; + daiBorrowCap = (await helpersContract.getReserveCaps(dai.address)).borrowCap; + + expect(usdcBorrowCap).to.be.equal(newCap); + expect(daiBorrowCap).to.be.equal(newCap); + }); + it('should succeed to borrow 100 variable dai and 100 stable usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const borrowedAmount = 100; + const precisionBorrowedAmount = (borrowedAmount * 1000).toString(); + + await pool.borrow( + usdc.address, + await unitParse(usdc, precisionBorrowedAmount), + 1, + 0, + deployer.address + ); + + await pool.borrow( + dai.address, + await unitParse(dai, precisionBorrowedAmount), + 2, + 0, + deployer.address + ); + }); +}); diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index c94dcdf8..d92779b5 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -1,8 +1,14 @@ import { TestEnv, makeSuite } from './helpers/make-suite'; -import { APPROVAL_AMOUNT_LENDING_POOL, RAY } from '../../helpers/constants'; +import { + APPROVAL_AMOUNT_LENDING_POOL, + MAX_UINT_AMOUNT, + RAY, + MAX_BORROW_CAP, +} from '../../helpers/constants'; import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; import { ProtocolErrors } from '../../helpers/types'; import { strategyWETH } from '../../markets/aave/reservesConfigs'; +import { BigNumber } from '@ethersproject/bignumber'; const { expect } = require('chai'); @@ -15,6 +21,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { RC_INVALID_LIQ_BONUS, RC_INVALID_DECIMALS, RC_INVALID_RESERVE_FACTOR, + RC_INVALID_BORROW_CAP, + RC_INVALID_SUPPLY_CAP, } = ProtocolErrors; it('Reverts trying to set an invalid reserve factor', async () => { @@ -73,6 +81,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isActive, isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); @@ -83,6 +92,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); it('Unfreezes the ETH reserve', async () => { @@ -100,6 +111,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isActive, isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); @@ -110,6 +122,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); it('Check the onlyAaveAdmin on freezeReserve ', async () => { @@ -142,6 +156,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isActive, isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); expect(borrowingEnabled).to.be.equal(false); expect(isActive).to.be.equal(true); @@ -152,11 +167,13 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); it('Activates the ETH reserve for borrowing', async () => { const { configurator, weth, helpersContract } = testEnv; - await configurator.enableBorrowingOnReserve(weth.address, true); + await configurator.enableBorrowingOnReserve(weth.address, '0', true); const { variableBorrowIndex } = await helpersContract.getReserveData(weth.address); const { @@ -170,6 +187,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isActive, isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); @@ -180,6 +198,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); expect(variableBorrowIndex.toString()).to.be.equal(RAY); }); @@ -195,7 +215,7 @@ 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, true), + configurator.connect(users[2].signer).enableBorrowingOnReserve(weth.address, '0', true), CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); @@ -215,6 +235,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isActive, isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); @@ -225,6 +246,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(liquidationBonus).to.be.equal(0); expect(stableBorrowRateEnabled).to.be.equal(true); expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); it('Activates the ETH reserve as collateral', async () => { @@ -242,6 +265,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isActive, isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); @@ -252,6 +276,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); it('Check the onlyAaveAdmin on configureReserveAsCollateral ', async () => { @@ -278,6 +304,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isActive, isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); @@ -288,6 +315,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); expect(stableBorrowRateEnabled).to.be.equal(false); expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); it('Enables stable borrow rate on the ETH reserve', async () => { @@ -304,6 +333,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isActive, isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); @@ -314,6 +344,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); expect(stableBorrowRateEnabled).to.be.equal(true); expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); it('Check the onlyAaveAdmin on disableReserveStableRate', async () => { @@ -332,6 +364,29 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); + it('Check the onlyAaveAdmin on setReserveFactor', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).setReserveFactor(weth.address, '1000'), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + + it('Check the onlyAaveAdmin on setBorrowCap', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).setBorrowCap(weth.address, '3000000000'), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + it('Check the onlyAaveAdmin on setSupplyCap', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).setSupplyCap(weth.address, '3000000000'), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + it('Changes the reserve factor of WETH', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.setReserveFactor(weth.address, '1000'); @@ -346,6 +401,51 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isActive, isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + 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(strategyWETH.stableBorrowRateEnabled); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + expect(reserveFactor).to.be.equal(1000); + }); + + it('Check that borrowCap cannot be set to value that exceeds the MAX_BORROW_CAP', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.setBorrowCap(weth.address, BigNumber.from(MAX_BORROW_CAP).add(1)), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(RC_INVALID_BORROW_CAP); + }); + it('Check that supplyCap cannot be set to value that exceeds the MAX_SUPPLY_CAP', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.setSupplyCap(weth.address, BigNumber.from(MAX_BORROW_CAP).add(1)), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(RC_INVALID_SUPPLY_CAP); + }); + + it('Changes the borrow Cap of WETH', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.setBorrowCap(weth.address, '3000000'); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); @@ -356,14 +456,37 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); expect(reserveFactor).to.be.equal(1000); + expect(borrowCap).to.be.equal('3000000'); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Check the onlyLendingPoolManager on setReserveFactor', async () => { - const { configurator, users, weth } = testEnv; - await expect( - configurator.connect(users[2].signer).setReserveFactor(weth.address, '2000'), - CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + it('Changes the supply Cap of WETH', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.setSupplyCap(weth.address, '3000000'); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + 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(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(1000); + expect(borrowCap).to.be.equal('3000000'); + expect(supplyCap).to.be.equal('3000000'); }); it('Reverts when trying to disable the DAI reserve with liquidity on it', async () => { diff --git a/test-suites/test-aave/supply-cap.spec.ts b/test-suites/test-aave/supply-cap.spec.ts new file mode 100644 index 00000000..6c213682 --- /dev/null +++ b/test-suites/test-aave/supply-cap.spec.ts @@ -0,0 +1,299 @@ +import { TestEnv, makeSuite } from './helpers/make-suite'; +import { + APPROVAL_AMOUNT_LENDING_POOL, + MAX_UINT_AMOUNT, + RAY, + MAX_SUPPLY_CAP, +} from '../../helpers/constants'; +import { ProtocolErrors } from '../../helpers/types'; +import { MintableERC20, WETH9, WETH9Mocked } from '../../types'; +import { parseEther } from '@ethersproject/units'; +import { BigNumber } from '@ethersproject/bignumber'; + +const { expect } = require('chai'); + +makeSuite('supply Cap', (testEnv: TestEnv) => { + const { VL_SUPPLY_CAP_EXCEEDED, RC_INVALID_SUPPLY_CAP } = ProtocolErrors; + + const unitParse = async (token: WETH9Mocked | MintableERC20, nb: string) => + BigNumber.from(nb).mul(BigNumber.from('10').pow((await token.decimals()) - 3)); + + it('Reserves should initially have supply cap disabled (supplyCap = 0)', async () => { + const { + configurator, + weth, + pool, + dai, + usdc, + deployer, + helpersContract, + users: [user1], + } = testEnv; + + const mintedAmount = parseEther('1000000000'); + await dai.mint(mintedAmount); + await weth.mint(mintedAmount); + await usdc.mint(mintedAmount); + + await dai.approve(pool.address, MAX_UINT_AMOUNT); + await weth.approve(pool.address, MAX_UINT_AMOUNT); + await usdc.approve(pool.address, MAX_UINT_AMOUNT); + + let usdcSupplyCap = (await helpersContract.getReserveCaps(usdc.address)).supplyCap; + let daiSupplyCap = (await helpersContract.getReserveCaps(dai.address)).supplyCap; + + expect(usdcSupplyCap).to.be.equal('0'); + expect(daiSupplyCap).to.be.equal('0'); + }); + it('Should be able to deposit 1000 Dai, 1000 USDC and 1000 Weth', async () => { + const { + configurator, + weth, + pool, + dai, + usdc, + deployer, + helpersContract, + users: [user1], + } = testEnv; + + const suppliedAmount = 1000; + const precisionSuppliedAmount = (suppliedAmount * 1000).toString(); + + await pool.deposit( + usdc.address, + await unitParse(usdc, precisionSuppliedAmount), + deployer.address, + 0 + ); + + await pool.deposit( + dai.address, + await unitParse(dai, precisionSuppliedAmount), + deployer.address, + 0 + ); + await pool.deposit( + weth.address, + await unitParse(weth, precisionSuppliedAmount), + deployer.address, + 0 + ); + }); + it('Sets the supply cap for Weth and DAI to 1000 Unit', async () => { + const { + configurator, + weth, + pool, + dai, + usdc, + deployer, + helpersContract, + users: [user1], + } = testEnv; + + const newCap = '1000'; + + await configurator.setSupplyCap(usdc.address, newCap); + await configurator.setSupplyCap(dai.address, newCap); + + const usdcSupplyCap = (await helpersContract.getReserveCaps(usdc.address)).supplyCap; + const daiSupplyCap = (await helpersContract.getReserveCaps(dai.address)).supplyCap; + + expect(usdcSupplyCap).to.be.equal(newCap); + expect(daiSupplyCap).to.be.equal(newCap); + }); + it('should fail to supply any dai or usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const suppliedAmount = 10; + const precisionSuppliedAmount = (suppliedAmount * 1000).toString(); + + await expect( + pool.deposit( + usdc.address, + await unitParse(usdc, precisionSuppliedAmount), + deployer.address, + 0 + ) + ).to.be.revertedWith(VL_SUPPLY_CAP_EXCEEDED); + + await expect( + pool.deposit(dai.address, await unitParse(dai, precisionSuppliedAmount), deployer.address, 0) + ).to.be.revertedWith(VL_SUPPLY_CAP_EXCEEDED); + }); + it('Should fail to set the supply cap for usdc and DAI to max cap + 1 Units', async () => { + const { configurator, usdc, pool, dai, deployer, helpersContract } = testEnv; + const newCap = Number(MAX_SUPPLY_CAP) + 1; + + await expect(configurator.setSupplyCap(usdc.address, newCap)).to.be.revertedWith( + RC_INVALID_SUPPLY_CAP + ); + await expect(configurator.setSupplyCap(dai.address, newCap)).to.be.revertedWith( + RC_INVALID_SUPPLY_CAP + ); + }); + it('Sets the supply cap for usdc and DAI to 1110 Units', async () => { + const { configurator, usdc, pool, dai, deployer, helpersContract } = testEnv; + const newCap = '1110'; + + await configurator.setSupplyCap(usdc.address, newCap); + await configurator.setSupplyCap(dai.address, newCap); + + const usdcSupplyCap = (await helpersContract.getReserveCaps(usdc.address)).supplyCap; + const daiSupplyCap = (await helpersContract.getReserveCaps(dai.address)).supplyCap; + + expect(usdcSupplyCap).to.be.equal(newCap); + expect(daiSupplyCap).to.be.equal(newCap); + }); + it('Should succeed to supply 10 dai and 10 usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const suppliedAmount = 10; + const precisionSuppliedAmount = (suppliedAmount * 1000).toString(); + await pool.deposit( + usdc.address, + await unitParse(usdc, precisionSuppliedAmount), + deployer.address, + 0 + ); + + await pool.deposit( + dai.address, + await unitParse(dai, precisionSuppliedAmount), + deployer.address, + 0 + ); + }); + it('should fail to supply 100 dai and 100 usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const suppliedAmount = 100; + const precisionSuppliedAmount = (suppliedAmount * 1000).toString(); + + await expect( + pool.deposit( + usdc.address, + await unitParse(usdc, precisionSuppliedAmount), + deployer.address, + 0 + ) + ).to.be.revertedWith(VL_SUPPLY_CAP_EXCEEDED); + + await expect( + pool.deposit(dai.address, await unitParse(dai, precisionSuppliedAmount), deployer.address, 0) + ).to.be.revertedWith(VL_SUPPLY_CAP_EXCEEDED); + }); + it('Should succeed to supply 99 dai and 99 usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const suppliedAmount = 99; + const precisionSuppliedAmount = (suppliedAmount * 1000).toString(); + await pool.deposit( + usdc.address, + await unitParse(usdc, precisionSuppliedAmount), + deployer.address, + 0 + ); + + await pool.deposit( + dai.address, + await unitParse(dai, precisionSuppliedAmount), + deployer.address, + 0 + ); + }); + it('Raises the supply cap for usdc and DAI to 2000 Units', async () => { + const { configurator, usdc, pool, dai, deployer, helpersContract } = testEnv; + const newCap = '2000'; + + await configurator.setSupplyCap(usdc.address, newCap); + await configurator.setSupplyCap(dai.address, newCap); + + const usdcSupplyCap = (await helpersContract.getReserveCaps(usdc.address)).supplyCap; + const daiSupplyCap = (await helpersContract.getReserveCaps(dai.address)).supplyCap; + + expect(usdcSupplyCap).to.be.equal(newCap); + expect(daiSupplyCap).to.be.equal(newCap); + }); + it('should succeed to supply 100 dai and 100 usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const suppliedAmount = 100; + const precisionSuppliedAmount = (suppliedAmount * 1000).toString(); + await pool.deposit( + usdc.address, + await unitParse(usdc, precisionSuppliedAmount), + deployer.address, + 0 + ); + + await pool.deposit( + dai.address, + await unitParse(dai, precisionSuppliedAmount), + deployer.address, + 0 + ); + }); + it('Lowers the supply cap for usdc and DAI to 1200 Units', async () => { + const { configurator, usdc, pool, dai, deployer, helpersContract } = testEnv; + const newCap = '1200'; + let usdcSupplyCap = (await helpersContract.getReserveCaps(usdc.address)).supplyCap; + let daiSupplyCap = (await helpersContract.getReserveCaps(dai.address)).supplyCap; + + await configurator.setSupplyCap(usdc.address, newCap); + await configurator.setSupplyCap(dai.address, newCap); + + usdcSupplyCap = (await helpersContract.getReserveCaps(usdc.address)).supplyCap; + daiSupplyCap = (await helpersContract.getReserveCaps(dai.address)).supplyCap; + + expect(usdcSupplyCap).to.be.equal(newCap); + expect(daiSupplyCap).to.be.equal(newCap); + }); + it('should fail to supply 100 dai and 100 usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const suppliedAmount = 100; + const precisionSuppliedAmount = (suppliedAmount * 1000).toString(); + + await expect( + pool.deposit( + usdc.address, + await unitParse(usdc, precisionSuppliedAmount), + deployer.address, + 0 + ) + ).to.be.revertedWith(VL_SUPPLY_CAP_EXCEEDED); + + await expect( + pool.deposit(dai.address, await unitParse(dai, precisionSuppliedAmount), deployer.address, 0) + ).to.be.revertedWith(VL_SUPPLY_CAP_EXCEEDED); + }); + it('Raises the supply cap for usdc and DAI to max cap Units', async () => { + const { configurator, usdc, pool, dai, deployer, helpersContract } = testEnv; + const newCap = MAX_SUPPLY_CAP; + let usdcSupplyCap = (await helpersContract.getReserveCaps(usdc.address)).supplyCap; + let daiSupplyCap = (await helpersContract.getReserveCaps(dai.address)).supplyCap; + + await configurator.setSupplyCap(usdc.address, newCap); + await configurator.setSupplyCap(dai.address, newCap); + + usdcSupplyCap = (await helpersContract.getReserveCaps(usdc.address)).supplyCap; + daiSupplyCap = (await helpersContract.getReserveCaps(dai.address)).supplyCap; + + expect(usdcSupplyCap).to.be.equal(newCap); + expect(daiSupplyCap).to.be.equal(newCap); + }); + it('should succeed to supply 100 dai and 100 usdc', async () => { + const { usdc, pool, dai, deployer, helpersContract } = testEnv; + const suppliedAmount = 100; + const precisionSuppliedAmount = (suppliedAmount * 1000).toString(); + await pool.deposit( + usdc.address, + await unitParse(usdc, precisionSuppliedAmount), + deployer.address, + 0 + ); + + await pool.deposit( + dai.address, + await unitParse(dai, precisionSuppliedAmount), + deployer.address, + 0 + ); + }); +}); diff --git a/test-suites/test-amm/configurator.spec.ts b/test-suites/test-amm/configurator.spec.ts index e4e3f4fa..d54abb78 100644 --- a/test-suites/test-amm/configurator.spec.ts +++ b/test-suites/test-amm/configurator.spec.ts @@ -1,5 +1,5 @@ import { TestEnv, makeSuite } from './helpers/make-suite'; -import { APPROVAL_AMOUNT_LENDING_POOL, RAY } from '../../helpers/constants'; +import { APPROVAL_AMOUNT_LENDING_POOL, MAX_BORROW_CAP, RAY } from '../../helpers/constants'; import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; import { ProtocolErrors } from '../../helpers/types'; import { strategyWETH } from '../../markets/amm/reservesConfigs'; @@ -156,7 +156,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { it('Activates the ETH reserve for borrowing', async () => { const { configurator, weth, helpersContract } = testEnv; - await configurator.enableBorrowingOnReserve(weth.address, true); + await configurator.enableBorrowingOnReserve(weth.address, MAX_BORROW_CAP, true); const { variableBorrowIndex } = await helpersContract.getReserveData(weth.address); const { @@ -178,9 +178,8 @@ 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 +194,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, 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 +251,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); });