diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index 4d8459ba..e5b92b61 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -120,6 +120,18 @@ interface ILendingPoolConfigurator { **/ event ReserveUnfrozen(address indexed asset); + /** + * @dev Emitted when a reserve is paused + * @param asset The address of the underlying asset of the reserve + **/ + event ReservePaused(address indexed asset); + + /** + * @dev Emitted when a reserve is unpaused + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveUnpaused(address indexed asset); + /** * @dev Emitted when a reserve factor is updated * @param asset The address of the underlying asset of the reserve @@ -221,7 +233,11 @@ interface ILendingPoolConfigurator { * @param borrowCap The borrow cap for this specific asset, in absolute units of tokens * @param stableBorrowRateEnabled True if stable borrow rate needs to be enabled by default on this reserve **/ - function enableBorrowingOnReserve(address asset, uint256 borrowCap, bool stableBorrowRateEnabled) external; + function enableBorrowingOnReserve( + address asset, + uint256 borrowCap, + bool stableBorrowRateEnabled + ) external; /** * @dev Disables borrowing on a reserve @@ -282,6 +298,18 @@ interface ILendingPoolConfigurator { **/ function unfreezeReserve(address asset) external; + /** + * @dev Pauses a reserve. A paused reserve allow now user moves such as deposit, borrow, repay, swap interestrate, liquidate + * @param asset The address of the underlying asset of the reserve + **/ + function pauseReserve(address asset) external; + + /** + * @dev Unpauses a reserve + * @param asset The address of the underlying asset of the reserve + **/ + function unpauseReserve(address asset) external; + /** * @dev Updates the reserve factor of a reserve * @param asset The address of the underlying asset of the reserve diff --git a/contracts/misc/AaveProtocolDataProvider.sol b/contracts/misc/AaveProtocolDataProvider.sol index d07a5f1b..95deb6ea 100644 --- a/contracts/misc/AaveProtocolDataProvider.sol +++ b/contracts/misc/AaveProtocolDataProvider.sol @@ -64,7 +64,7 @@ contract AaveProtocolDataProvider { return aTokens; } - // not returning borrow and supply caps for compatibility + // not returning borrow and supply caps for compatibility, nor pause flag function getReserveConfigurationData(address asset) external view @@ -87,7 +87,7 @@ contract AaveProtocolDataProvider { (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor) = configuration.getParamsMemory(); - (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled) = + (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled, ) = configuration.getFlagsMemory(); usageAsCollateralEnabled = liquidationThreshold > 0; @@ -103,6 +103,13 @@ contract AaveProtocolDataProvider { .getCapsMemory(); } + function getPaused(address asset) external view returns (bool isPaused) { + (, , , , isPaused) = + ILendingPool(ADDRESSES_PROVIDER.getLendingPool()) + .getConfiguration(asset) + .getFlagsMemory(); + } + function getReserveData(address asset) external view diff --git a/contracts/misc/UiPoolDataProvider.sol b/contracts/misc/UiPoolDataProvider.sol index 2a1b6bae..4e4641c3 100644 --- a/contracts/misc/UiPoolDataProvider.sol +++ b/contracts/misc/UiPoolDataProvider.sol @@ -122,7 +122,8 @@ contract UiPoolDataProvider is IUiPoolDataProvider { reserveData.isActive, reserveData.isFrozen, reserveData.borrowingEnabled, - reserveData.stableBorrowRateEnabled + reserveData.stableBorrowRateEnabled, + reserveData.isPaused ) = baseData.configuration.getFlagsMemory(); reserveData.usageAsCollateralEnabled = reserveData.baseLTVasCollateral != 0; ( diff --git a/contracts/misc/WalletBalanceProvider.sol b/contracts/misc/WalletBalanceProvider.sol index 3d4a9288..22a6f55e 100644 --- a/contracts/misc/WalletBalanceProvider.sol +++ b/contracts/misc/WalletBalanceProvider.sol @@ -96,7 +96,7 @@ contract WalletBalanceProvider { DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(reservesWithEth[j]); - (bool isActive, , , ) = configuration.getFlagsMemory(); + (bool isActive, , , , ) = configuration.getFlagsMemory(); if (!isActive) { balances[j] = 0; diff --git a/contracts/misc/interfaces/IUiPoolDataProvider.sol b/contracts/misc/interfaces/IUiPoolDataProvider.sol index 5ea96464..ac7d344a 100644 --- a/contracts/misc/interfaces/IUiPoolDataProvider.sol +++ b/contracts/misc/interfaces/IUiPoolDataProvider.sol @@ -22,6 +22,7 @@ interface IUiPoolDataProvider { bool stableBorrowRateEnabled; bool isActive; bool isFrozen; + bool isPaused; // base data uint128 liquidityIndex; uint128 variableBorrowIndex; diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index f61826f0..d3ef2c26 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -465,7 +465,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ) external override whenNotPaused { FlashLoanLocalVars memory vars; - ValidationLogic.validateFlashloan(assets, amounts); + ValidationLogic.validateFlashloan(assets, amounts, _reserves); address[] memory aTokenAddresses = new address[](assets.length); uint256[] memory premiums = new uint256[](assets.length); @@ -720,6 +720,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ) external override whenNotPaused { require(msg.sender == _reserves[asset].aTokenAddress, Errors.LP_CALLER_MUST_BE_AN_ATOKEN); + ValidationLogic.validateTransfer(_reserves[asset]); + uint256 reserveId = _reserves[asset].id; if (from != to) { diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 5b517ad5..e3cb07af 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -46,6 +46,15 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur _; } + modifier onlyEmergencyOrPoolAdmin { + require( + _addressesProvider.getEmergencyAdmin() == msg.sender || + _addressesProvider.getPoolAdmin() == msg.sender, + Errors.LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN + ); + _; + } + uint256 internal constant CONFIGURATOR_REVISION = 0x1; function getRevision() internal pure override returns (uint256) { @@ -126,6 +135,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur currentConfig.setDecimals(input.underlyingAssetDecimals); currentConfig.setActive(true); + currentConfig.setPaused(false); currentConfig.setFrozen(false); pool.setConfiguration(input.underlyingAsset, currentConfig.data); @@ -380,6 +390,28 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur emit ReserveUnfrozen(asset); } + /// @inheritdoc ILendingPoolConfigurator + function pauseReserve(address asset) external override onlyEmergencyOrPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); + + currentConfig.setPaused(true); + + _pool.setConfiguration(asset, currentConfig.data); + + emit ReservePaused(asset); + } + + /// @inheritdoc ILendingPoolConfigurator + function unpauseReserve(address asset) external override onlyEmergencyOrPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); + + currentConfig.setPaused(false); + + _pool.setConfiguration(asset, currentConfig.data); + + emit ReserveUnpaused(asset); + } + /// @inheritdoc ILendingPoolConfigurator function setReserveFactor(address asset, uint256 reserveFactor) external override onlyPoolAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index 567ee93b..91d8bc50 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -18,6 +18,7 @@ library ReserveConfiguration { uint256 constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore uint256 constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore uint256 constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore + uint256 constant PAUSED_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF; // 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 @@ -30,6 +31,8 @@ library ReserveConfiguration { uint256 constant IS_FROZEN_START_BIT_POSITION = 57; uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58; uint256 constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59; + uint256 constant IS_PAUSED_START_BIT_POSITION = 60; + // bits 61 62 63 unused yet uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64; uint256 constant BORROW_CAP_START_BIT_POSITION = 80; uint256 constant SUPPLY_CAP_START_BIT_POSITION = 116; @@ -187,6 +190,26 @@ library ReserveConfiguration { return (self.data & ~FROZEN_MASK) != 0; } + /** + * @dev Sets the paused state of the reserve + * @param self The reserve configuration + * @param paused The paused state + **/ + function setPaused(DataTypes.ReserveConfigurationMap memory self, bool paused) internal pure { + self.data = + (self.data & PAUSED_MASK) | + (uint256(paused ? 1 : 0) << IS_PAUSED_START_BIT_POSITION); + } + + /** + * @dev Gets the paused state of the reserve + * @param self The reserve configuration + * @return The paused state + **/ + function getPaused(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { + return (self.data & ~PAUSED_MASK) != 0; + } + /** * @dev Enables or disables borrowing on the reserve * @param self The reserve configuration @@ -336,6 +359,7 @@ library ReserveConfiguration { bool, bool, bool, + bool, bool ) { @@ -345,7 +369,8 @@ library ReserveConfiguration { (dataLocal & ~ACTIVE_MASK) != 0, (dataLocal & ~FROZEN_MASK) != 0, (dataLocal & ~BORROWING_MASK) != 0, - (dataLocal & ~STABLE_BORROWING_MASK) != 0 + (dataLocal & ~STABLE_BORROWING_MASK) != 0, + (dataLocal & ~PAUSED_MASK) != 0 ); } @@ -447,6 +472,7 @@ library ReserveConfiguration { bool, bool, bool, + bool, bool ) { @@ -454,7 +480,8 @@ library ReserveConfiguration { (self.data & ~ACTIVE_MASK) != 0, (self.data & ~FROZEN_MASK) != 0, (self.data & ~BORROWING_MASK) != 0, - (self.data & ~STABLE_BORROWING_MASK) != 0 + (self.data & ~STABLE_BORROWING_MASK) != 0, + (self.data & ~PAUSED_MASK) != 0 ); } diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 34e3521f..2b6f015a 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -106,6 +106,8 @@ library Errors { string public constant RC_INVALID_BORROW_CAP = '82'; string public constant VL_SUPPLY_CAP_EXCEEDED = '83'; string public constant RC_INVALID_SUPPLY_CAP = '84'; + string public constant LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85'; + string public constant VL_RESERVE_PAUSED = '86'; enum CollateralManagerErrors { NO_ERROR, @@ -117,6 +119,7 @@ library Errors { NO_ACTIVE_RESERVE, HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD, INVALID_EQUAL_ASSETS_TO_SWAP, - FROZEN_RESERVE + FROZEN_RESERVE, + PAUSED_RESERVE } } diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 0c0b7028..40ef23be 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -42,12 +42,13 @@ library ValidationLogic { */ function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) internal view { DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration; - (bool isActive, bool isFrozen, , ) = reserveConfiguration.getFlagsMemory(); + (bool isActive, bool isFrozen, , , bool isPaused) = 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(!isPaused, Errors.VL_RESERVE_PAUSED); require(!isFrozen, Errors.VL_RESERVE_FROZEN); require( supplyCap == 0 || @@ -71,8 +72,9 @@ library ValidationLogic { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); - (bool isActive, , , ) = reserve.configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isPaused, Errors.VL_RESERVE_PAUSED); } struct ValidateBorrowLocalVars { @@ -89,6 +91,7 @@ library ValidationLogic { uint256 borrowCap; bool isActive; bool isFrozen; + bool isPaused; bool borrowingEnabled; bool stableRateBorrowingEnabled; } @@ -121,7 +124,7 @@ library ValidationLogic { mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle - ) internal view { + ) external view { ValidateBorrowLocalVars memory vars; DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration; @@ -131,10 +134,12 @@ library ValidationLogic { vars.isActive, vars.isFrozen, vars.borrowingEnabled, - vars.stableRateBorrowingEnabled + vars.stableRateBorrowingEnabled, + vars.isPaused ) = reserveConfiguration.getFlagsMemory(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!vars.isPaused, Errors.VL_RESERVE_PAUSED); require(!vars.isFrozen, Errors.VL_RESERVE_FROZEN); require(amount != 0, Errors.VL_INVALID_AMOUNT); @@ -238,9 +243,9 @@ library ValidationLogic { uint256 stableDebt, uint256 variableDebt ) external view { - bool isActive = reserve.configuration.getActive(); - + (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isPaused, Errors.VL_RESERVE_PAUSED); require(amountSent > 0, Errors.VL_INVALID_AMOUNT); @@ -273,9 +278,11 @@ library ValidationLogic { uint256 variableDebt, DataTypes.InterestRateMode currentRateMode ) external view { - (bool isActive, bool isFrozen, , bool stableRateEnabled) = reserve.configuration.getFlags(); + (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = + reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isPaused, Errors.VL_RESERVE_PAUSED); require(!isFrozen, Errors.VL_RESERVE_FROZEN); if (currentRateMode == DataTypes.InterestRateMode.STABLE) { @@ -317,9 +324,10 @@ library ValidationLogic { IERC20 variableDebtToken, address aTokenAddress ) external view { - (bool isActive, , , ) = reserve.configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isPaused, Errors.VL_RESERVE_PAUSED); //if the usage ratio is below 95%, no rebalances are needed uint256 totalDebt = @@ -348,6 +356,9 @@ library ValidationLogic { */ function validateSetUseReserveAsCollateral(DataTypes.ReserveData storage reserve) external view { uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); + bool isPaused = reserve.configuration.getPaused(); + + require(!isPaused, Errors.VL_RESERVE_PAUSED); require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); } @@ -357,7 +368,14 @@ 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) external pure { + function validateFlashloan( + address[] memory assets, + uint256[] memory amounts, + mapping(address => DataTypes.ReserveData) storage reservesData + ) external view { + for (uint256 i = 0; i < assets.length; i++) { + require(!reservesData[assets[i]].configuration.getPaused(), Errors.VL_RESERVE_PAUSED); + } require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS); } @@ -386,6 +404,9 @@ library ValidationLogic { Errors.VL_NO_ACTIVE_RESERVE ); } + if (collateralReserve.configuration.getPaused() || principalReserve.configuration.getPaused()) { + return (uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), Errors.VL_RESERVE_PAUSED); + } if (userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) { return ( @@ -448,4 +469,12 @@ library ValidationLogic { Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD ); } + + /** + * @dev Validates a transfer action + * @param reserve The reserve object + */ + function validateTransfer(DataTypes.ReserveData storage reserve) internal view { + require(!reserve.configuration.getPaused(), Errors.VL_RESERVE_PAUSED); + } } diff --git a/contracts/protocol/libraries/types/DataTypes.sol b/contracts/protocol/libraries/types/DataTypes.sol index 5c620eaa..d6399a58 100644 --- a/contracts/protocol/libraries/types/DataTypes.sol +++ b/contracts/protocol/libraries/types/DataTypes.sol @@ -36,7 +36,8 @@ library DataTypes { //bit 57: reserve is frozen //bit 58: borrowing is enabled //bit 59: stable rate borrowing enabled - //bit 60-63: reserved + //bit 60: asset is paused + //bit 61-63: reserved //bit 64-79: reserve factor //bit 80-115 borrow cap, borrowCap == 0 => disabled //bit 116-152 supply cap, supplyCap == 0 => disabled diff --git a/helpers/types.ts b/helpers/types.ts index ad6dbced..45f7ddcf 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -181,6 +181,8 @@ export enum ProtocolErrors { RC_INVALID_BORROW_CAP = '82', VL_SUPPLY_CAP_EXCEEDED = '83', RC_INVALID_SUPPLY_CAP = '84', + LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85', + VL_RESERVE_PAUSED = '86', // old diff --git a/markets/matic/reservesConfigs.ts b/markets/matic/reservesConfigs.ts index cbeba1a6..36260ef9 100644 --- a/markets/matic/reservesConfigs.ts +++ b/markets/matic/reservesConfigs.ts @@ -92,6 +92,8 @@ export const strategyMATIC: IReserveParams = { borrowCap: '0', supplyCap: '0', reserveFactor: '2000', + borrowCap: '0', + supplyCap: '0', }; export const strategyAAVE: IReserveParams = { diff --git a/package.json b/package.json index e1ebb028..70ed56fa 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "test-scenarios": "npm run compile && npx hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/scenario.spec.ts", "test-subgraph:scenarios": "npm run compile && hardhat --network hardhatevm_docker test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/subgraph-scenarios.spec.ts", "test:main:check-list": "npm run compile && FORK=main TS_NODE_TRANSPILE_ONLY=1 hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/mainnet/check-list.spec.ts", + "test:aave": "hardhat test test-suites/test-aave/__setup.spec.ts", + "test:amm": "hardhat test test-suites/test-amm/__setup.spec.ts", "dev:coverage": "buidler compile --force && buidler coverage --network coverage", "aave:evm:dev:migration": "npm run compile && hardhat aave:dev", "aave:docker:full:migration": "npm run compile && npm run hardhat:docker -- aave:mainnet --skip-registry", diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index d92779b5..e525c348 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -23,6 +23,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { RC_INVALID_RESERVE_FACTOR, RC_INVALID_BORROW_CAP, RC_INVALID_SUPPLY_CAP, + LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN, + VL_RESERVE_PAUSED, } = ProtocolErrors; it('Reverts trying to set an invalid reserve factor', async () => { @@ -65,6 +67,152 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); + it('Pauses the ETH reserve by pool admin', async () => { + const { configurator, weth, helpersContract, addressesProvider, users } = testEnv; + expect(await configurator.signer.getAddress()).to.be.equal( + await addressesProvider.getPoolAdmin() + ); + + await configurator.pauseReserve(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).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(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + + it('Unpauses the ETH reserve by pool admin ', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.unpauseReserve(weth.address); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + 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(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + it('Pauses the ETH reserve by emergency admin', async () => { + const { configurator, weth, helpersContract, addressesProvider, users } = testEnv; + expect(users[1].address).to.be.equal(await addressesProvider.getEmergencyAdmin()); + + await configurator.connect(users[1].signer).pauseReserve(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).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(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + + it('Unpauses the ETH reserve by emergency admin ', async () => { + const { configurator, helpersContract, weth, users } = testEnv; + await configurator.connect(users[1].signer).unpauseReserve(weth.address); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + 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(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + + it('Check the only admin or emergency admin can pauseReserve ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).pauseReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN); + }); + + it('Check the only admin or emergency admin can unpauseReserve ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).unpauseReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN); + }); it('Freezes the ETH reserve', async () => { const { configurator, weth, helpersContract } = testEnv; @@ -82,9 +230,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(true); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -112,9 +262,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -157,9 +309,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(false); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -188,9 +342,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -236,9 +392,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(18); expect(ltv).to.be.equal(0); @@ -266,9 +424,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -305,9 +465,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -334,9 +496,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -402,9 +566,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -446,9 +612,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -475,9 +643,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); diff --git a/test-suites/test-aave/pausable-functions.spec.ts b/test-suites/test-aave/pausable-functions.spec.ts index c95aaf3c..24bedb69 100644 --- a/test-suites/test-aave/pausable-functions.spec.ts +++ b/test-suites/test-aave/pausable-functions.spec.ts @@ -12,17 +12,14 @@ const { expect } = require('chai'); makeSuite('Pausable Pool', (testEnv: TestEnv) => { let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; - const { - LP_IS_PAUSED, - INVALID_FROM_BALANCE_AFTER_TRANSFER, - INVALID_TO_BALANCE_AFTER_TRANSFER, - } = ProtocolErrors; + const { LP_IS_PAUSED, INVALID_FROM_BALANCE_AFTER_TRANSFER, INVALID_TO_BALANCE_AFTER_TRANSFER } = + ProtocolErrors; before(async () => { _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); }); - it('User 0 deposits 1000 DAI. Configurator pauses pool. Transfers to user 1 reverts. Configurator unpauses the network and next transfer succees', async () => { + it('User 0 deposits 1000 DAI. Configurator pauses pool. Transfers to user 1 reverts. Configurator unpauses the network and next transfer succeeds', async () => { const { users, pool, dai, aDai, configurator } = testEnv; const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); diff --git a/test-suites/test-aave/reserve-pause.spec.ts b/test-suites/test-aave/reserve-pause.spec.ts new file mode 100644 index 00000000..22851764 --- /dev/null +++ b/test-suites/test-aave/reserve-pause.spec.ts @@ -0,0 +1,330 @@ +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { parseEther, parseUnits } from 'ethers/lib/utils'; +import { BigNumber } from 'bignumber.js'; +import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; +import { getMockFlashLoanReceiver } from '../../helpers/contracts-getters'; + +const { expect } = require('chai'); + +makeSuite('Pause Reserve', (testEnv: TestEnv) => { + let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; + + const { + VL_RESERVE_PAUSED, + INVALID_FROM_BALANCE_AFTER_TRANSFER, + INVALID_TO_BALANCE_AFTER_TRANSFER, + } = ProtocolErrors; + + before(async () => { + _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); + }); + + it('User 0 deposits 1000 DAI. Configurator pauses pool. Transfers to user 1 reverts. Configurator unpauses the network and next transfer succeeds', async () => { + const { users, pool, dai, aDai, configurator } = testEnv; + + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + // user 0 deposits 1000 DAI + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + + const user0Balance = await aDai.balanceOf(users[0].address); + const user1Balance = await aDai.balanceOf(users[1].address); + + // Configurator pauses the pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + + // User 0 tries the transfer to User 1 + await expect( + aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit) + ).to.revertedWith(VL_RESERVE_PAUSED); + + const pausedFromBalance = await aDai.balanceOf(users[0].address); + const pausedToBalance = await aDai.balanceOf(users[1].address); + + expect(pausedFromBalance).to.be.equal( + user0Balance.toString(), + INVALID_TO_BALANCE_AFTER_TRANSFER + ); + expect(pausedToBalance.toString()).to.be.equal( + user1Balance.toString(), + INVALID_FROM_BALANCE_AFTER_TRANSFER + ); + + // Configurator unpauses the pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + + // User 0 succeeds transfer to User 1 + await aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit); + + const fromBalance = await aDai.balanceOf(users[0].address); + const toBalance = await aDai.balanceOf(users[1].address); + + expect(fromBalance.toString()).to.be.equal( + user0Balance.sub(amountDAItoDeposit), + INVALID_FROM_BALANCE_AFTER_TRANSFER + ); + expect(toBalance.toString()).to.be.equal( + user1Balance.add(amountDAItoDeposit), + INVALID_TO_BALANCE_AFTER_TRANSFER + ); + }); + + it('Deposit', async () => { + const { users, pool, dai, aDai, configurator } = testEnv; + + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + // user 0 deposits 1000 DAI + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + // Configurator pauses the pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + await expect( + pool.connect(users[0].signer).deposit(dai.address, amountDAItoDeposit, users[0].address, '0') + ).to.revertedWith(VL_RESERVE_PAUSED); + + // Configurator unpauses the pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + }); + + it('Withdraw', async () => { + const { users, pool, dai, aDai, configurator } = testEnv; + + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + // user 0 deposits 1000 DAI + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + + // Configurator pauses the pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + + // user tries to burn + await expect( + pool.connect(users[0].signer).withdraw(dai.address, amountDAItoDeposit, users[0].address) + ).to.revertedWith(VL_RESERVE_PAUSED); + + // Configurator unpauses the pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + }); + + it('Borrow', async () => { + const { pool, dai, users, configurator } = testEnv; + + const user = users[1]; + // Pause the pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + + // Try to execute liquidation + await expect( + pool.connect(user.signer).borrow(dai.address, '1', '1', '0', user.address) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause the pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + }); + + it('Repay', async () => { + const { pool, dai, users, configurator } = testEnv; + + const user = users[1]; + // Pause the pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + + // Try to execute liquidation + await expect(pool.connect(user.signer).repay(dai.address, '1', '1', user.address)).revertedWith( + VL_RESERVE_PAUSED + ); + + // Unpause the pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + }); + + it('Flash loan', async () => { + const { dai, pool, weth, users, configurator } = testEnv; + + const caller = users[3]; + + const flashAmount = parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + // Pause pool + await configurator.connect(users[1].signer).pauseReserve(weth.address); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [1], + caller.address, + '0x10', + '0' + ) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).unpauseReserve(weth.address); + }); + + it('Liquidation call', async () => { + const { users, pool, usdc, oracle, weth, configurator, helpersContract } = testEnv; + const depositor = users[3]; + const borrower = users[4]; + + //mints USDC to depositor + await usdc + .connect(depositor.signer) + .mint(await convertToCurrencyDecimals(usdc.address, '1000')); + + //approve protocol to access depositor wallet + await usdc.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + //user 3 deposits 1000 USDC + const amountUSDCtoDeposit = await convertToCurrencyDecimals(usdc.address, '1000'); + + await pool + .connect(depositor.signer) + .deposit(usdc.address, amountUSDCtoDeposit, depositor.address, '0'); + + //user 4 deposits 1 ETH + const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + + //mints WETH to borrower + await weth.connect(borrower.signer).mint(amountETHtoDeposit); + + //approve protocol to access borrower wallet + await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool + .connect(borrower.signer) + .deposit(weth.address, amountETHtoDeposit, borrower.address, '0'); + + //user 4 borrows + const userGlobalData = await pool.getUserAccountData(borrower.address); + + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const amountUSDCToBorrow = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(userGlobalData.availableBorrowsETH.toString()) + .div(usdcPrice.toString()) + .multipliedBy(0.9502) + .toFixed(0) + ); + + await pool + .connect(borrower.signer) + .borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address); + + // Drops HF below 1 + await oracle.setAssetPrice( + usdc.address, + new BigNumber(usdcPrice.toString()).multipliedBy(1.2).toFixed(0) + ); + + //mints dai to the liquidator + await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000')); + await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const userReserveDataBefore = await helpersContract.getUserReserveData( + usdc.address, + borrower.address + ); + + const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString()) + .multipliedBy(0.5) + .toFixed(0); + + // Pause pool + await configurator.connect(users[1].signer).pauseReserve(usdc.address); + + // Do liquidation + await expect( + pool.liquidationCall(weth.address, usdc.address, borrower.address, amountToLiquidate, true) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).unpauseReserve(usdc.address); + }); + + it('SwapBorrowRateMode', async () => { + const { pool, weth, dai, usdc, users, configurator } = testEnv; + const user = users[1]; + const amountWETHToDeposit = parseEther('10'); + const amountDAIToDeposit = parseEther('120'); + const amountToBorrow = parseUnits('65', 6); + + await weth.connect(user.signer).mint(amountWETHToDeposit); + await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0'); + + await dai.connect(user.signer).mint(amountDAIToDeposit); + await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0'); + + await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address); + + // Pause pool + await configurator.connect(users[1].signer).pauseReserve(usdc.address); + + // Try to repay + await expect( + pool.connect(user.signer).swapBorrowRateMode(usdc.address, RateMode.Stable) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).unpauseReserve(usdc.address); + }); + + it('RebalanceStableBorrowRate', async () => { + const { pool, dai, users, configurator } = testEnv; + const user = users[1]; + // Pause pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + + await expect( + pool.connect(user.signer).rebalanceStableBorrowRate(dai.address, user.address) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + }); + + it('setUserUseReserveAsCollateral', async () => { + const { pool, weth, users, configurator } = testEnv; + const user = users[1]; + + const amountWETHToDeposit = parseEther('1'); + await weth.connect(user.signer).mint(amountWETHToDeposit); + await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0'); + + // Pause pool + await configurator.connect(users[1].signer).pauseReserve(weth.address); + + await expect( + pool.connect(user.signer).setUserUseReserveAsCollateral(weth.address, false) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).unpauseReserve(weth.address); + }); +}); diff --git a/test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts b/test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts index 42224c52..e9883776 100644 --- a/test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts +++ b/test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts @@ -187,17 +187,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const collateralDecimals = (await usdc.decimals()).toString(); const principalDecimals = (await dai.decimals()).toString(); - const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountUSDCtoSwap.toString()) - .times( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); + const expectedDaiAmountForUsdc = new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) + .toFixed(0); // Make a deposit for user await usdc.connect(user).mint(amountUSDCtoSwap); @@ -240,7 +235,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ], [false, false] ); - await pool .connect(user) .flashLoan( @@ -309,17 +303,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const collateralDecimals = (await usdc.decimals()).toString(); const principalDecimals = (await dai.decimals()).toString(); - const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountUSDCtoSwap.toString()) - .times( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); + const expectedDaiAmountForUsdc = new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) + .toFixed(0); // Make a deposit for user await usdc.connect(user).mint(amountUSDCtoSwap); @@ -862,17 +851,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const collateralDecimals = (await usdc.decimals()).toString(); const principalDecimals = (await dai.decimals()).toString(); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountUSDCtoSwap.toString()) - .times( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); + const expectedDaiAmount = new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) + .toFixed(0); await mockUniswapRouter.connect(user).setAmountToReturn(usdc.address, expectedDaiAmount); @@ -1484,17 +1468,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const collateralDecimals = (await usdc.decimals()).toString(); const principalDecimals = (await dai.decimals()).toString(); - const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountUSDCtoSwap.toString()) - .times( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); + const expectedDaiAmountForUsdc = new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) + .toFixed(0); // Make a deposit for user await usdc.connect(user).mint(amountUSDCtoSwap); @@ -1592,17 +1571,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const collateralDecimals = (await usdc.decimals()).toString(); const principalDecimals = (await dai.decimals()).toString(); - const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountUSDCtoSwap.toString()) - .times( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); + const expectedDaiAmountForUsdc = new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) + .toFixed(0); // Make a deposit for user await usdc.connect(user).mint(amountUSDCtoSwap); diff --git a/test-suites/test-amm/pausable-functions.spec.ts b/test-suites/test-amm/pausable-functions.spec.ts index 2096b235..f42388a8 100644 --- a/test-suites/test-amm/pausable-functions.spec.ts +++ b/test-suites/test-amm/pausable-functions.spec.ts @@ -22,7 +22,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => { _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); }); - it('User 0 deposits 1000 DAI. Configurator pauses pool. Transfers to user 1 reverts. Configurator unpauses the network and next transfer succees', async () => { + it('User 0 deposits 1000 DAI. Configurator pauses pool. Transfers to user 1 reverts. Configurator unpauses the network and next transfer succeeds', async () => { const { users, pool, dai, aDai, configurator } = testEnv; const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');