diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index e5b92b61..36d0f206 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -203,6 +203,18 @@ interface ILendingPoolConfigurator { address indexed implementation ); + /** + * @dev Emitted when a new risk admin is registered + * @param admin the newly registered admin + **/ + event RiskAdminRegistered(address indexed admin); + + /** + * @dev Emitted when a risk admin is unregistered + * @param admin the unregistered admin + **/ + event RiskAdminUnregistered(address indexed admin); + /** * @dev Initializes reserves in batch * @param input The array of reserves initialization parameters @@ -344,4 +356,22 @@ interface ILendingPoolConfigurator { * @param supplyCap The new supply of the reserve **/ function setSupplyCap(address asset, uint256 supplyCap) external; + + /** + * @dev Registers a new admin with rights on risk related configurations + * @param admin The address of the admin to register + **/ + function registerRiskAdmin(address admin) external; + + /** + * @dev Unegisters a risk admin + * @param admin The address of the admin to unregister + **/ + function unregisterRiskAdmin(address admin) external; + + /** + * @dev Returns wether an address in a risk admin or not + * @param admin The address of the potential admin + **/ + function isRiskAdmin(address admin) external view returns (bool); } diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index e3cb07af..c1abf2b0 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -33,6 +33,8 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur ILendingPoolAddressesProvider internal _addressesProvider; ILendingPool internal _pool; + mapping(address => bool) private _riskAdmins; + modifier onlyPoolAdmin { require(_addressesProvider.getPoolAdmin() == msg.sender, Errors.CALLER_NOT_POOL_ADMIN); _; @@ -55,6 +57,14 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur _; } + modifier onlyRiskOrPoolAdmins { + require( + _riskAdmins[msg.sender] || _addressesProvider.getPoolAdmin() == msg.sender, + Errors.LPC_CALLER_NOT_RISK_OR_POOL_ADMIN + ); + _; + } + uint256 internal constant CONFIGURATOR_REVISION = 0x1; function getRevision() internal pure override returns (uint256) { @@ -254,7 +264,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur address asset, uint256 borrowCap, bool stableBorrowRateEnabled - ) external override onlyPoolAdmin { + ) external override onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setBorrowingEnabled(true); @@ -268,7 +278,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } /// @inheritdoc ILendingPoolConfigurator - function disableBorrowingOnReserve(address asset) external override onlyPoolAdmin { + function disableBorrowingOnReserve(address asset) external override onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setBorrowingEnabled(false); @@ -283,7 +293,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus - ) external override onlyPoolAdmin { + ) external override onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); //validation of the parameters: the LTV can @@ -323,7 +333,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } /// @inheritdoc ILendingPoolConfigurator - function enableReserveStableRate(address asset) external override onlyPoolAdmin { + function enableReserveStableRate(address asset) external override onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setStableRateBorrowingEnabled(true); @@ -334,7 +344,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } /// @inheritdoc ILendingPoolConfigurator - function disableReserveStableRate(address asset) external override onlyPoolAdmin { + function disableReserveStableRate(address asset) external override onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setStableRateBorrowingEnabled(false); @@ -369,7 +379,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } /// @inheritdoc ILendingPoolConfigurator - function freezeReserve(address asset) external override onlyPoolAdmin { + function freezeReserve(address asset) external override onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setFrozen(true); @@ -380,7 +390,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } /// @inheritdoc ILendingPoolConfigurator - function unfreezeReserve(address asset) external override onlyPoolAdmin { + function unfreezeReserve(address asset) external override onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setFrozen(false); @@ -413,7 +423,11 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } /// @inheritdoc ILendingPoolConfigurator - function setReserveFactor(address asset, uint256 reserveFactor) external override onlyPoolAdmin { + function setReserveFactor(address asset, uint256 reserveFactor) + external + override + onlyRiskOrPoolAdmins + { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setReserveFactor(reserveFactor); @@ -424,7 +438,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } ///@inheritdoc ILendingPoolConfigurator - function setBorrowCap(address asset, uint256 borrowCap) external override onlyPoolAdmin { + function setBorrowCap(address asset, uint256 borrowCap) external override onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setBorrowCap(borrowCap); @@ -435,7 +449,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } ///@inheritdoc ILendingPoolConfigurator - function setSupplyCap(address asset, uint256 supplyCap) external override onlyPoolAdmin { + function setSupplyCap(address asset, uint256 supplyCap) external override onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setSupplyCap(supplyCap); @@ -445,15 +459,11 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur emit SupplyCapChanged(asset, supplyCap); } - /** - * @dev Sets the interest rate strategy of a reserve - * @param asset The address of the underlying asset of the reserve - * @param rateStrategyAddress The new address of the interest strategy contract - **/ + ///@inheritdoc ILendingPoolConfigurator function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress) external override - onlyPoolAdmin + onlyRiskOrPoolAdmins { _pool.setReserveInterestRateStrategyAddress(asset, rateStrategyAddress); emit ReserveInterestRateStrategyChanged(asset, rateStrategyAddress); @@ -464,6 +474,23 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur _pool.setPause(val); } + /// @inheritdoc ILendingPoolConfigurator + function registerRiskAdmin(address admin) external override onlyPoolAdmin { + _riskAdmins[admin] = true; + emit RiskAdminRegistered(admin); + } + + /// @inheritdoc ILendingPoolConfigurator + function unregisterRiskAdmin(address admin) external override onlyPoolAdmin { + _riskAdmins[admin] = false; + emit RiskAdminUnregistered(admin); + } + + /// @inheritdoc ILendingPoolConfigurator + function isRiskAdmin(address admin) external view override onlyPoolAdmin returns (bool) { + return _riskAdmins[admin]; + } + function _initTokenWithProxy(address implementation, bytes memory initParams) internal returns (address) diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 2b6f015a..8a2e7653 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -108,6 +108,7 @@ library Errors { 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'; + string public constant LPC_CALLER_NOT_RISK_OR_POOL_ADMIN = '87'; enum CollateralManagerErrors { NO_ERROR, diff --git a/helpers/types.ts b/helpers/types.ts index 45f7ddcf..0e0ac375 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -183,6 +183,7 @@ export enum ProtocolErrors { RC_INVALID_SUPPLY_CAP = '84', LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85', VL_RESERVE_PAUSED = '86', + LPC_CALLER_NOT_RISK_OR_POOL_ADMIN = '87', // old diff --git a/markets/matic/reservesConfigs.ts b/markets/matic/reservesConfigs.ts index 36260ef9..cbeba1a6 100644 --- a/markets/matic/reservesConfigs.ts +++ b/markets/matic/reservesConfigs.ts @@ -92,8 +92,6 @@ export const strategyMATIC: IReserveParams = { borrowCap: '0', supplyCap: '0', reserveFactor: '2000', - borrowCap: '0', - supplyCap: '0', }; export const strategyAAVE: IReserveParams = { diff --git a/test-suites/test-aave/__setup.spec.ts b/test-suites/test-aave/__setup.spec.ts index c004d10f..dd8ed168 100644 --- a/test-suites/test-aave/__setup.spec.ts +++ b/test-suites/test-aave/__setup.spec.ts @@ -128,6 +128,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const lendingPoolConfiguratorProxy = await getLendingPoolConfiguratorProxy( await addressesProvider.getLendingPoolConfigurator() ); + await waitForTx(await lendingPoolConfiguratorProxy.registerRiskAdmin(addressList[3])); await insertContractAddressInDb( eContractid.LendingPoolConfigurator, lendingPoolConfiguratorProxy.address diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index e525c348..5dc4884c 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -24,6 +24,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { RC_INVALID_BORROW_CAP, RC_INVALID_SUPPLY_CAP, LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN, + LPC_CALLER_NOT_RISK_OR_POOL_ADMIN, VL_RESERVE_PAUSED, } = ProtocolErrors; @@ -134,10 +135,9 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { 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 { configurator, weth, helpersContract, addressesProvider, users, emergencyAdmin } = + testEnv; + await configurator.connect(emergencyAdmin.signer).pauseReserve(weth.address); const { decimals, ltv, @@ -167,8 +167,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { }); 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 { configurator, helpersContract, weth, users, emergencyAdmin } = testEnv; + await configurator.connect(emergencyAdmin.signer).unpauseReserve(weth.address); const { decimals, @@ -199,22 +199,22 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { }); it('Check the only admin or emergency admin can pauseReserve ', async () => { - const { configurator, users, weth } = testEnv; + const { configurator, users, weth, riskAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).pauseReserve(weth.address), + configurator.connect(riskAdmin.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; + const { configurator, users, weth, riskAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).unpauseReserve(weth.address), + configurator.connect(riskAdmin.signer).unpauseReserve(weth.address), CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN); }); - it('Freezes the ETH reserve', async () => { + it('Freezes the ETH reserve by pool Admin', async () => { const { configurator, weth, helpersContract } = testEnv; await configurator.freezeReserve(weth.address); @@ -246,7 +246,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Unfreezes the ETH reserve', async () => { + it('Unfreezes the ETH reserve by Pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.unfreezeReserve(weth.address); @@ -277,24 +277,87 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(borrowCap).to.be.equal(strategyWETH.borrowCap); expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); + it('Freezes the ETH reserve by Risk Admin', async () => { + const { configurator, weth, helpersContract, riskAdmin } = testEnv; - it('Check the onlyAaveAdmin on freezeReserve ', async () => { - const { configurator, users, weth } = testEnv; - await expect( - configurator.connect(users[2].signer).freezeReserve(weth.address), - CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + await configurator.connect(riskAdmin.signer).freezeReserve(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(true); + 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 onlyAaveAdmin on unfreezeReserve ', async () => { - const { configurator, users, weth } = testEnv; - await expect( - configurator.connect(users[2].signer).unfreezeReserve(weth.address), - CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + it('Unfreezes the ETH reserve by Risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).unfreezeReserve(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('Deactivates the ETH reserve for borrowing', async () => { + it('Check the onlyRiskOrPoolAdmins on freezeReserve ', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; + await expect( + configurator.connect(emergencyAdmin.signer).freezeReserve(weth.address), + LPC_CALLER_NOT_RISK_OR_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); + }); + + it('Check the onlyRiskOrPoolAdmins on unfreezeReserve ', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; + await expect( + configurator.connect(emergencyAdmin.signer).unfreezeReserve(weth.address), + LPC_CALLER_NOT_RISK_OR_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); + }); + + it('Deactivates the ETH reserve for borrowing via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.disableBorrowingOnReserve(weth.address); const { @@ -325,7 +388,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Activates the ETH reserve for borrowing', async () => { + it('Activates the ETH reserve for borrowing via pool admin', async () => { const { configurator, weth, helpersContract } = testEnv; await configurator.enableBorrowingOnReserve(weth.address, '0', true); const { variableBorrowIndex } = await helpersContract.getReserveData(weth.address); @@ -360,23 +423,92 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(variableBorrowIndex.toString()).to.be.equal(RAY); }); - it('Check the onlyAaveAdmin on disableBorrowingOnReserve ', async () => { - const { configurator, users, weth } = testEnv; - await expect( - configurator.connect(users[2].signer).disableBorrowingOnReserve(weth.address), - CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + it('Deactivates the ETH reserve for borrowing via risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).disableBorrowingOnReserve(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(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); + 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 onlyAaveAdmin on enableBorrowingOnReserve ', async () => { - const { configurator, users, weth } = testEnv; - await expect( - configurator.connect(users[2].signer).enableBorrowingOnReserve(weth.address, '0', true), - CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + it('Activates the ETH reserve for borrowing via risk admin', async () => { + const { configurator, weth, helpersContract, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).enableBorrowingOnReserve(weth.address, '0', true); + const { variableBorrowIndex } = await helpersContract.getReserveData(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); + + expect(variableBorrowIndex.toString()).to.be.equal(RAY); }); - it('Deactivates the ETH reserve as collateral', async () => { + it('Check the onlyAaveAdmin or Risk admin on disableBorrowingOnReserve ', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; + + await expect( + configurator.connect(emergencyAdmin.signer).disableBorrowingOnReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); + }); + + it('Check the onlyAaveAdmin or Risk admin on enableBorrowingOnReserve ', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; + await expect( + configurator + .connect(emergencyAdmin.signer) + .enableBorrowingOnReserve(weth.address, MAX_BORROW_CAP, true), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); + }); + + it('Deactivates the ETH reserve as collateral via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.configureReserveAsCollateral(weth.address, 0, 0, 0); @@ -408,7 +540,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Activates the ETH reserve as collateral', async () => { + it('Activates the ETH reserve as collateral via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.configureReserveAsCollateral(weth.address, '8000', '8250', '10500'); @@ -439,18 +571,85 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(borrowCap).to.be.equal(strategyWETH.borrowCap); expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); + it('Deactivates the ETH reserve as collateral via risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator + .connect(riskAdmin.signer) + .configureReserveAsCollateral(weth.address, 0, 0, 0); - it('Check the onlyAaveAdmin on configureReserveAsCollateral ', async () => { - const { configurator, users, weth } = testEnv; - await expect( - configurator - .connect(users[2].signer) - .configureReserveAsCollateral(weth.address, '7500', '8000', '10500'), - CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + 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(18); + expect(ltv).to.be.equal(0); + expect(liquidationThreshold).to.be.equal(0); + 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('Disable stable borrow rate on the ETH reserve', async () => { + it('Activates the ETH reserve as collateral via risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator + .connect(riskAdmin.signer) + .configureReserveAsCollateral(weth.address, '8000', '8250', '10500'); + + 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 onlyRiskOrPoolAdmin on configureReserveAsCollateral ', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; + await expect( + configurator + .connect(emergencyAdmin.signer) + .configureReserveAsCollateral(weth.address, '7500', '8000', '10500'), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); + }); + + it('Disable stable borrow rate on the ETH reserve via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.disableReserveStableRate(weth.address); const { @@ -481,7 +680,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Enables stable borrow rate on the ETH reserve', async () => { + it('Enables stable borrow rate on the ETH reserve via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.enableReserveStableRate(weth.address); const { @@ -511,47 +710,108 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(borrowCap).to.be.equal(strategyWETH.borrowCap); expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); + it('Disable stable borrow rate on the ETH reserve risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).disableReserveStableRate(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); - it('Check the onlyAaveAdmin on disableReserveStableRate', async () => { - const { configurator, users, weth } = testEnv; - await expect( - configurator.connect(users[2].signer).disableReserveStableRate(weth.address), - CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + 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(false); + 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 enableReserveStableRate', async () => { - const { configurator, users, weth } = testEnv; - await expect( - configurator.connect(users[2].signer).enableReserveStableRate(weth.address), - CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + it('Enables stable borrow rate on the ETH reserve risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).enableReserveStableRate(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(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 setReserveFactor', async () => { - const { configurator, users, weth } = testEnv; + it('Check the onlyRiskOrPoolAdmin on disableReserveStableRate', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).setReserveFactor(weth.address, '1000'), + configurator.connect(emergencyAdmin.signer).disableReserveStableRate(weth.address), CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); }); - it('Check the onlyAaveAdmin on setBorrowCap', async () => { - const { configurator, users, weth } = testEnv; + it('Check the onlyRiskOrPoolAdmin on enableReserveStableRate', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).setBorrowCap(weth.address, '3000000000'), + configurator.connect(emergencyAdmin.signer).enableReserveStableRate(weth.address), 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); + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); }); - it('Changes the reserve factor of WETH', async () => { + it('Check the onlyRiskOrPoolAdmin on setReserveFactor', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; + await expect( + configurator.connect(emergencyAdmin.signer).setReserveFactor(weth.address, '1000'), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); + }); + + it('Check the onlyRiskOrPoolAdmin on setBorrowCap', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; + await expect( + configurator.connect(emergencyAdmin.signer).setBorrowCap(weth.address, '3000000000'), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); + }); + it('Check the onlyRiskOrPoolAdmin on setSupplyCap', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; + await expect( + configurator.connect(emergencyAdmin.signer).setSupplyCap(weth.address, '3000000000'), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); + }); + + it('Changes the reserve factor of WETH via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.setReserveFactor(weth.address, '1000'); const { @@ -581,6 +841,36 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); expect(reserveFactor).to.be.equal(1000); }); + it('Changes the reserve factor of WETH risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).setReserveFactor(weth.address, '1000'); + 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(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; @@ -597,7 +887,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { ).to.be.revertedWith(RC_INVALID_SUPPLY_CAP); }); - it('Changes the borrow Cap of WETH', async () => { + it('Changes the borrow Cap of WETH via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.setBorrowCap(weth.address, '3000000'); const { @@ -627,8 +917,38 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(borrowCap).to.be.equal('3000000'); expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); + it('Changes the borrow Cap of WETH risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).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); + const isPaused = await helpersContract.getPaused(weth.address); - it('Changes the supply Cap of WETH', async () => { + 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(1000); + expect(borrowCap).to.be.equal('3000000'); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + + it('Changes the supply Cap of WETH via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.setSupplyCap(weth.address, '3000000'); const { @@ -658,6 +978,36 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(borrowCap).to.be.equal('3000000'); expect(supplyCap).to.be.equal('3000000'); }); + it('Changes the supply Cap of WETH via risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).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); + 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(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 () => { const { dai, pool, configurator } = testEnv; @@ -676,4 +1026,23 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { LPC_RESERVE_LIQUIDITY_NOT_0 ).to.be.revertedWith(LPC_RESERVE_LIQUIDITY_NOT_0); }); + it('Register a new risk Admin', async () => { + const { dai, pool, configurator, users, riskAdmin } = testEnv; + await configurator.registerRiskAdmin(users[3].address); + + const isRiskAdminRegistered = await configurator.isRiskAdmin(riskAdmin.address); + const isNewRegistered = await configurator.isRiskAdmin(users[3].address); + expect(isNewRegistered).to.be.true; + expect(isRiskAdminRegistered).to.be.true; + }); + it('Unregister a risk Admins', async () => { + const { dai, pool, configurator, users, riskAdmin } = testEnv; + await configurator.unregisterRiskAdmin(users[3].address); + await configurator.unregisterRiskAdmin(riskAdmin.address); + + const isRiskAdminRegistered = await configurator.isRiskAdmin(riskAdmin.address); + const isNewRegistered = await configurator.isRiskAdmin(users[3].address); + expect(isNewRegistered).to.be.false; + expect(isRiskAdminRegistered).to.be.false; + }); }); diff --git a/test-suites/test-aave/helpers/make-suite.ts b/test-suites/test-aave/helpers/make-suite.ts index e5421031..490aeb0b 100644 --- a/test-suites/test-aave/helpers/make-suite.ts +++ b/test-suites/test-aave/helpers/make-suite.ts @@ -51,6 +51,9 @@ export interface SignerWithAddress { } export interface TestEnv { deployer: SignerWithAddress; + poolAdmin: SignerWithAddress; + emergencyAdmin: SignerWithAddress; + riskAdmin: SignerWithAddress; users: SignerWithAddress[]; pool: LendingPool; configurator: LendingPoolConfigurator; @@ -77,6 +80,9 @@ const setBuidlerevmSnapshotId = (id: string) => { const testEnv: TestEnv = { deployer: {} as SignerWithAddress, + poolAdmin: {} as SignerWithAddress, + emergencyAdmin: {} as SignerWithAddress, + riskAdmin: {} as SignerWithAddress, users: [] as SignerWithAddress[], pool: {} as LendingPool, configurator: {} as LendingPoolConfigurator, @@ -110,6 +116,9 @@ export async function initializeMakeSuite() { }); } testEnv.deployer = deployer; + testEnv.poolAdmin = deployer; + testEnv.emergencyAdmin = testEnv.users[1]; + testEnv.riskAdmin = testEnv.users[2]; testEnv.pool = await getLendingPool(); testEnv.configurator = await getLendingPoolConfiguratorProxy(); diff --git a/test-suites/test-amm/configurator.spec.ts b/test-suites/test-amm/configurator.spec.ts index d54abb78..6370ce1c 100644 --- a/test-suites/test-amm/configurator.spec.ts +++ b/test-suites/test-amm/configurator.spec.ts @@ -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, MAX_BORROW_CAP, true); + await configurator.enableBorrowingOnReserve(weth.address, '0', true); const { variableBorrowIndex } = await helpersContract.getReserveData(weth.address); const {