diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 49451d923..f02dafeb3 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -46,6 +46,14 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur _; } + modifier onlyPoolOrEmergencyAdmin { + require( + addressesProvider.getPoolAdmin() == msg.sender || addressesProvider.getEmergencyAdmin() == msg.sender, + Errors.LPC_NOT_POOL_OR_EMERGENCY_ADMIN + ); + _; + } + uint256 internal constant CONFIGURATOR_REVISION = 0x1; function getRevision() internal pure override returns (uint256) { @@ -391,7 +399,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur * but allows repayments, liquidations, rate rebalances and withdrawals * @param asset The address of the underlying asset of the reserve **/ - function freezeReserve(address asset) external onlyPoolAdmin { + function freezeReserve(address asset) external onlyPoolOrEmergencyAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setFrozen(true); @@ -405,7 +413,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur * @dev Unfreezes a reserve * @param asset The address of the underlying asset of the reserve **/ - function unfreezeReserve(address asset) external onlyPoolAdmin { + function unfreezeReserve(address asset) external onlyPoolOrEmergencyAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setFrozen(false); diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 8756d797f..465af8806 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -103,6 +103,7 @@ library Errors { string public constant LP_NOT_CONTRACT = '78'; string public constant SDT_STABLE_DEBT_OVERFLOW = '79'; string public constant SDT_BURN_EXCEEDS_BALANCE = '80'; + string public constant LPC_NOT_POOL_OR_EMERGENCY_ADMIN = '81'; enum CollateralManagerErrors { NO_ERROR, diff --git a/helpers/types.ts b/helpers/types.ts index 71597f9c3..390930f24 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -193,6 +193,7 @@ export enum ProtocolErrors { RC_INVALID_DECIMALS = '70', RC_INVALID_RESERVE_FACTOR = '71', LPAPR_INVALID_ADDRESSES_PROVIDER_ID = '72', + LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '81', // old diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index c94dcdf87..7d3f8e453 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -15,6 +15,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { RC_INVALID_LIQ_BONUS, RC_INVALID_DECIMALS, RC_INVALID_RESERVE_FACTOR, + LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN } = ProtocolErrors; it('Reverts trying to set an invalid reserve factor', async () => { @@ -58,7 +59,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); - it('Freezes the ETH reserve', async () => { + it('Freezes the ETH reserve using the pool admin', async () => { const { configurator, weth, helpersContract } = testEnv; await configurator.freezeReserve(weth.address); @@ -85,7 +86,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); }); - it('Unfreezes the ETH reserve', async () => { + it('Unfreezes the ETH reserve using the pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.unfreezeReserve(weth.address); @@ -112,20 +113,74 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); }); - it('Check the onlyAaveAdmin on freezeReserve ', async () => { + it('Freezes the ETH reserve using the emergency admin', async () => { + const { configurator, weth, helpersContract, users } = testEnv; + + await configurator.connect(users[1].signer).freezeReserve(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + 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); + }); + + it('Unfreezes the ETH reserve using the emergency admin', async () => { + const { configurator, helpersContract, weth, users } = testEnv; + await configurator.connect(users[1].signer).unfreezeReserve(weth.address); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + }); + + it('Check the onlyPoolOrEmergencyAdmin 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); + LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN); }); - it('Check the onlyAaveAdmin on unfreezeReserve ', async () => { + it('Check the onlyPoolOrEmergencyAdmin 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); + LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN); }); it('Deactivates the ETH reserve for borrowing', async () => { diff --git a/test-suites/test-amm/configurator.spec.ts b/test-suites/test-amm/configurator.spec.ts index e4e3f4fab..1fc529088 100644 --- a/test-suites/test-amm/configurator.spec.ts +++ b/test-suites/test-amm/configurator.spec.ts @@ -15,6 +15,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { RC_INVALID_LIQ_BONUS, RC_INVALID_DECIMALS, RC_INVALID_RESERVE_FACTOR, + LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN } = ProtocolErrors; it('Reverts trying to set an invalid reserve factor', async () => { @@ -58,7 +59,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); - it('Freezes the ETH reserve', async () => { + it('Freezes the ETH reserve using the pool admin', async () => { const { configurator, weth, helpersContract } = testEnv; await configurator.freezeReserve(weth.address); @@ -85,7 +86,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); }); - it('Unfreezes the ETH reserve', async () => { + it('Unfreezes the ETH reserve using the pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.unfreezeReserve(weth.address); @@ -112,20 +113,75 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); }); - it('Check the onlyAaveAdmin on freezeReserve ', async () => { + it('Freezes the ETH reserve using the emergency admin', async () => { + const { configurator, weth, helpersContract, users } = testEnv; + + await configurator.connect(users[1].signer).freezeReserve(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + 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); + }); + + it('Unfreezes the ETH reserve using the emergency admin', async () => { + const { configurator, helpersContract, weth, users } = testEnv; + await configurator.connect(users[1].signer).unfreezeReserve(weth.address); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + }); + + + it('Check the onlyPoolOrEmergencyAdmin 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); + LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN); }); - it('Check the onlyAaveAdmin on unfreezeReserve ', async () => { + it('Check the onlyPoolOrEmergencyAdmin 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); + LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN); }); it('Deactivates the ETH reserve for borrowing', async () => { @@ -188,8 +244,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { 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); + LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_POOL_OR_EMERGENCY_ADMIN); }); it('Check the onlyAaveAdmin on enableBorrowingOnReserve ', async () => {