From 7e6565fed49802cf9e168772fc38e9d1ff934cc2 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 21 Jun 2021 08:13:20 +0200 Subject: [PATCH 1/3] test: multiple flashoborrow asset, test extensively fees to reserve, fees to protocol --- test-suites/test-aave/flashloan.spec.ts | 226 ++++++++++++++------ test-suites/test-aave/helpers/make-suite.ts | 4 + test-suites/test-amm/flashloan.spec.ts | 4 +- 3 files changed, 168 insertions(+), 66 deletions(-) diff --git a/test-suites/test-aave/flashloan.spec.ts b/test-suites/test-aave/flashloan.spec.ts index 91e162a5..4e2fcb23 100644 --- a/test-suites/test-aave/flashloan.spec.ts +++ b/test-suites/test-aave/flashloan.spec.ts @@ -27,12 +27,24 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { LP_BORROW_ALLOWANCE_NOT_ENOUGH, } = ProtocolErrors; + const TOTAL_PREMIUM = 9; + const PREMIUM_TO_PROTOCOL = 3; + const PREMIUM_TO_LP = TOTAL_PREMIUM - PREMIUM_TO_PROTOCOL; + before(async () => { _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); }); + it('Configurator sets total premium = 9 bps, premium to protocol = 3 bps', async () => { + const { configurator, pool } = testEnv; + await configurator.updateFlashloanPremiumTotal(TOTAL_PREMIUM); + await configurator.updateFlashloanPremiumToProtocol(PREMIUM_TO_PROTOCOL); + + expect(await pool.FLASHLOAN_PREMIUM_TOTAL()).to.be.equal(TOTAL_PREMIUM); + expect(await pool.FLASHLOAN_PREMIUM_TO_PROTOCOL()).to.be.equal(PREMIUM_TO_PROTOCOL); + }); it('Deposits WETH into the reserve', async () => { - const { pool, weth, aave } = testEnv; + const { pool, weth, aave, dai } = testEnv; const userAddress = await pool.signer.getAddress(); const amountToDeposit = ethers.utils.parseEther('1'); @@ -47,44 +59,102 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await aave.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); await pool.deposit(aave.address, amountToDeposit, userAddress, '0'); + await dai.mint(amountToDeposit); + + await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool.deposit(dai.address, amountToDeposit, userAddress, '0'); }); - it('Takes WETH flash loan with mode = 0, returns the funds correctly', async () => { - const { pool, helpersContract, weth, aWETH } = testEnv; + it('Takes WETH + Dai flash loan with mode = 0, returns the funds correctly', async () => { + const { pool, helpersContract, weth, aWETH, dai, aDai } = testEnv; - const flashBorrowedAmount = ethers.utils.parseEther('0.8'); - const fees = new BigNumber(flashBorrowedAmount.mul(9).div(10000).toString()); + const wethFlashBorrowedAmount = ethers.utils.parseEther('0.8'); + const daiFlashBorrowedAmount = ethers.utils.parseEther('0.3'); + const wethTotalFees = new BigNumber( + wethFlashBorrowedAmount.mul(TOTAL_PREMIUM).div(10000).toString() + ); + const wethFeesToProtocol = wethFlashBorrowedAmount.mul(PREMIUM_TO_PROTOCOL).div(10000); + const wethFeesToLp = wethFlashBorrowedAmount.mul(PREMIUM_TO_LP).div(10000); + const daiTotalFees = new BigNumber( + daiFlashBorrowedAmount.mul(TOTAL_PREMIUM).div(10000).toString() + ); + const daiFeesToProtocol = daiFlashBorrowedAmount.mul(PREMIUM_TO_PROTOCOL).div(10000); + const daiFeesToLp = daiFlashBorrowedAmount.mul(PREMIUM_TO_LP).div(10000); - let reserveData = await helpersContract.getReserveData(weth.address); + const wethLiquidityIndexAdded = wethFeesToLp + .mul(ethers.BigNumber.from(10).pow(27)) + .div((await aWETH.totalSupply()).toString()); - const totalLiquidityBefore = new BigNumber(reserveData.availableLiquidity.toString()) - .plus(reserveData.totalStableDebt.toString()) - .plus(reserveData.totalVariableDebt.toString()); + const daiLiquidityIndexAdded = daiFeesToLp + .mul(ethers.BigNumber.from(10).pow(27)) + .div((await aDai.totalSupply()).toString()); + + let wethReserveData = await helpersContract.getReserveData(weth.address); + let daiReserveData = await helpersContract.getReserveData(dai.address); + + const wethLiquidityIndexBefore = wethReserveData.liquidityIndex; + const daiLiquidityIndexBefore = daiReserveData.liquidityIndex; + + const wethTotalLiquidityBefore = new BigNumber(wethReserveData.availableLiquidity.toString()) + .plus(wethReserveData.totalStableDebt.toString()) + .plus(wethReserveData.totalVariableDebt.toString()); + + const daiTotalLiquidityBefore = new BigNumber(daiReserveData.availableLiquidity.toString()) + .plus(daiReserveData.totalStableDebt.toString()) + .plus(daiReserveData.totalVariableDebt.toString()); + + const wethReservesBefore = await aWETH.balanceOf(await aWETH.RESERVE_TREASURY_ADDRESS()); + const daiReservesBefore = await aDai.balanceOf(await aDai.RESERVE_TREASURY_ADDRESS()); await pool.flashLoan( _mockFlashLoanReceiver.address, - [weth.address], - [flashBorrowedAmount], - [0], + [weth.address, dai.address], + [wethFlashBorrowedAmount, daiFlashBorrowedAmount], + [0, 0], _mockFlashLoanReceiver.address, '0x10', '0' ); - await pool.mintToTreasury([weth.address]); + await pool.mintToTreasury([weth.address, dai.address]); - reserveData = await helpersContract.getReserveData(weth.address); + wethReserveData = await helpersContract.getReserveData(weth.address); + daiReserveData = await helpersContract.getReserveData(dai.address); - const currentLiquidityRate = reserveData.liquidityRate; - const currentLiquidityIndex = reserveData.liquidityIndex; + const wethCurrentLiquidityRate = wethReserveData.liquidityRate; + const wethCurrentLiquidityIndex = wethReserveData.liquidityIndex; + const daiCurrentLiquidityRate = daiReserveData.liquidityRate; + const daiCurrentLiquidityIndex = daiReserveData.liquidityIndex; - const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString()) - .plus(reserveData.totalStableDebt.toString()) - .plus(reserveData.totalVariableDebt.toString()); + const wethTotalLiquidityAfter = new BigNumber(wethReserveData.availableLiquidity.toString()) + .plus(wethReserveData.totalStableDebt.toString()) + .plus(wethReserveData.totalVariableDebt.toString()); - expect(totalLiquidityBefore.plus(fees).toString()).to.be.equal(totalLiquidityAfter.toString()); - expect(currentLiquidityRate.toString()).to.be.equal('0'); - expect(currentLiquidityIndex.toString()).to.be.equal('1000720000000000000000000000'); + const daiTotalLiquidityAfter = new BigNumber(daiReserveData.availableLiquidity.toString()) + .plus(daiReserveData.totalStableDebt.toString()) + .plus(daiReserveData.totalVariableDebt.toString()); + + const wethReservesAfter = await aWETH.balanceOf(await aWETH.RESERVE_TREASURY_ADDRESS()); + const daiReservesAfter = await aDai.balanceOf(await aDai.RESERVE_TREASURY_ADDRESS()); + + expect(wethTotalLiquidityBefore.plus(wethTotalFees).toString()).to.be.equal( + wethTotalLiquidityAfter.toString() + ); + expect(wethCurrentLiquidityRate.toString()).to.be.equal('0'); + expect(wethCurrentLiquidityIndex.toString()).to.be.equal( + wethLiquidityIndexBefore.add(wethLiquidityIndexAdded.toString()).toString() + ); + expect(wethReservesAfter).to.be.equal(wethReservesBefore.add(wethFeesToProtocol)); + + expect(daiTotalLiquidityBefore.plus(daiTotalFees).toString()).to.be.equal( + daiTotalLiquidityAfter.toString() + ); + expect(daiCurrentLiquidityRate.toString()).to.be.equal('0'); + expect(daiCurrentLiquidityIndex.toString()).to.be.equal( + daiLiquidityIndexBefore.add(daiLiquidityIndexAdded.toString()).toString() + ); + expect(daiReservesAfter).to.be.equal(daiReservesBefore.add(daiFeesToProtocol)); }); it('Takes an authorized AAVE flash loan with mode = 0, returns the funds correctly', async () => { const { @@ -97,7 +167,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await configurator.authorizeFlashBorrower(authorizedUser.address); const flashBorrowedAmount = ethers.utils.parseEther('0.8'); - const fees = new BigNumber(0); + const totalFees = new BigNumber(0); let reserveData = await helpersContract.getReserveData(aave.address); @@ -119,33 +189,43 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await pool.mintToTreasury([aave.address]); - ethers.utils.parseUnits('10000'); - reserveData = await helpersContract.getReserveData(aave.address); const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString()) .plus(reserveData.totalStableDebt.toString()) .plus(reserveData.totalVariableDebt.toString()); - expect(totalLiquidityBefore.plus(fees).toString()).to.be.equal(totalLiquidityAfter.toString()); + expect(totalLiquidityBefore.plus(totalFees).toString()).to.be.equal( + totalLiquidityAfter.toString() + ); }); it('Takes an ETH flashloan with mode = 0 as big as the available liquidity', async () => { - const { pool, helpersContract, weth } = testEnv; + const { pool, helpersContract, weth, aWETH } = testEnv; let reserveData = await helpersContract.getReserveData(weth.address); - const totalLiquidityBefore = new BigNumber(reserveData.availableLiquidity.toString()) - .plus(reserveData.totalStableDebt.toString()) - .plus(reserveData.totalVariableDebt.toString()); + const totalLiquidityBefore = reserveData.availableLiquidity + .add(reserveData.totalStableDebt) + .add(reserveData.totalVariableDebt); - const flashBorrowedAmount = totalLiquidityBefore.toString(); + const flashBorrowedAmount = totalLiquidityBefore; - const fees = new BigNumber(flashBorrowedAmount).multipliedBy(9).dividedBy(10000).toString(); + const totalFees = new BigNumber(flashBorrowedAmount.mul(TOTAL_PREMIUM).div(10000).toString()); + const feesToProtocol = flashBorrowedAmount.mul(PREMIUM_TO_PROTOCOL).div(10000); + const feesToLp = flashBorrowedAmount.mul(PREMIUM_TO_LP).div(10000); + const liquidityIndexBefore = reserveData.liquidityIndex; + const liquidityIndexAdded = feesToLp + .mul(ethers.BigNumber.from(10).pow(27)) + .div((await aWETH.totalSupply()).toString()) + .mul(liquidityIndexBefore) + .div(ethers.BigNumber.from(10).pow(27)); + + const reservesBefore = await aWETH.balanceOf(await aWETH.RESERVE_TREASURY_ADDRESS()); const txResult = await pool.flashLoan( _mockFlashLoanReceiver.address, [weth.address], - [totalLiquidityBefore.toString()], + [flashBorrowedAmount], [0], _mockFlashLoanReceiver.address, '0x10', @@ -156,22 +236,25 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { reserveData = await helpersContract.getReserveData(weth.address); + const currentLiquidityRate = reserveData.liquidityRate; + const currentLiquidityIndex = reserveData.liquidityIndex; + const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString()) .plus(reserveData.totalStableDebt.toString()) .plus(reserveData.totalVariableDebt.toString()); - const currentLiqudityRate = reserveData.liquidityRate; - const currentLiquidityIndex = reserveData.liquidityIndex; - - const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString()) - .plus(reserveData.totalStableDebt.toString()) - .plus(reserveData.totalVariableDebt.toString()); - - expect(totalLiquidity.toString()).to.be.equal('1001620648000000000'); - expect(currentLiqudityRate.toString()).to.be.equal('0'); - expect(currentLiquidityIndex.toString()).to.be.equal('1001620648000000000000000000'); + const reservesAfter = await aWETH.balanceOf(await aWETH.RESERVE_TREASURY_ADDRESS()); + expect(new BigNumber(totalLiquidityBefore.toString()).plus(totalFees).toString()).to.be.equal( + totalLiquidityAfter.toString() + ); + expect(currentLiquidityRate.toString()).to.be.equal('0'); + expect(currentLiquidityIndex.toString()).to.be.equal( + liquidityIndexBefore.add(liquidityIndexAdded.toString()).toString() + ); + expect( + reservesAfter.sub(feesToProtocol).mul(liquidityIndexBefore).div(currentLiquidityIndex) + ).to.be.equal(reservesBefore); }); - it('Takes WETH flashloan, does not return the funds with mode = 0. (revert expected)', async () => { const { pool, weth, users } = testEnv; const caller = users[1]; @@ -337,46 +420,59 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { }); it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => { - const { usdc, pool, helpersContract, deployer: depositor } = testEnv; + const { usdc, aUsdc, pool, helpersContract, deployer: depositor } = testEnv; await _mockFlashLoanReceiver.setFailExecutionTransfer(false); - const reserveDataBefore = await helpersContract.getReserveData(usdc.address); + const flashBorrowedAmount = await convertToCurrencyDecimals(usdc.address, '500'); + const totalFees = new BigNumber(flashBorrowedAmount.mul(TOTAL_PREMIUM).div(10000).toString()); + const feesToProtocol = flashBorrowedAmount.mul(PREMIUM_TO_PROTOCOL).div(10000); + const feesToLp = flashBorrowedAmount.mul(PREMIUM_TO_LP).div(10000); + const liquidityIndexAdded = feesToLp + .mul(ethers.BigNumber.from(10).pow(27)) + .div((await aUsdc.totalSupply()).toString()); - const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500'); + let reserveData = await helpersContract.getReserveData(usdc.address); + + const liquidityIndexBefore = reserveData.liquidityIndex; + + const totalLiquidityBefore = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + + const reservesBefore = await aUsdc.balanceOf(await aUsdc.RESERVE_TREASURY_ADDRESS()); await pool.flashLoan( _mockFlashLoanReceiver.address, [usdc.address], - [flashloanAmount], + [flashBorrowedAmount], [0], _mockFlashLoanReceiver.address, '0x10', '0' ); - const reserveDataAfter = helpersContract.getReserveData(usdc.address); + await pool.mintToTreasury([usdc.address]); - const reserveData = await helpersContract.getReserveData(usdc.address); - const userData = await helpersContract.getUserReserveData(usdc.address, depositor.address); + reserveData = await helpersContract.getReserveData(usdc.address); - const totalLiquidity = reserveData.availableLiquidity - .add(reserveData.totalStableDebt) - .add(reserveData.totalVariableDebt) - .toString(); - const currentLiqudityRate = reserveData.liquidityRate.toString(); - const currentLiquidityIndex = reserveData.liquidityIndex.toString(); - const currentUserBalance = userData.currentATokenBalance.toString(); + const currentLiquidityRate = reserveData.liquidityRate; + const currentLiquidityIndex = reserveData.liquidityIndex; - const expectedLiquidity = await convertToCurrencyDecimals(usdc.address, '1000.450'); + const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); - expect(totalLiquidity).to.be.equal(expectedLiquidity, 'Invalid total liquidity'); - expect(currentLiqudityRate).to.be.equal('0', 'Invalid liquidity rate'); - expect(currentLiquidityIndex).to.be.equal( - new BigNumber('1.00045').multipliedBy(oneRay).toFixed(), - 'Invalid liquidity index' + const reservesAfter = await aUsdc.balanceOf(await aUsdc.RESERVE_TREASURY_ADDRESS()); + + expect(totalLiquidityBefore.plus(totalFees).toString()).to.be.equal( + totalLiquidityAfter.toString() ); - expect(currentUserBalance.toString()).to.be.equal(expectedLiquidity, 'Invalid user balance'); + expect(currentLiquidityRate.toString()).to.be.equal('0'); + expect(currentLiquidityIndex.toString()).to.be.equal( + liquidityIndexBefore.add(liquidityIndexAdded.toString()).toString() + ); + expect(reservesAfter).to.be.equal(reservesBefore.add(feesToProtocol)); }); it('Takes out a 500 USDC flashloan with mode = 0, does not return the funds. (revert expected)', async () => { diff --git a/test-suites/test-aave/helpers/make-suite.ts b/test-suites/test-aave/helpers/make-suite.ts index 490aeb0b..130635f1 100644 --- a/test-suites/test-aave/helpers/make-suite.ts +++ b/test-suites/test-aave/helpers/make-suite.ts @@ -63,6 +63,7 @@ export interface TestEnv { aWETH: AToken; dai: MintableERC20; aDai: AToken; + aUsdc: AToken; usdc: MintableERC20; aave: MintableERC20; addressesProvider: LendingPoolAddressesProvider; @@ -92,6 +93,7 @@ const testEnv: TestEnv = { aWETH: {} as AToken, dai: {} as MintableERC20, aDai: {} as AToken, + aUsdc: {} as AToken, usdc: {} as MintableERC20, aave: {} as MintableERC20, addressesProvider: {} as LendingPoolAddressesProvider, @@ -138,6 +140,7 @@ export async function initializeMakeSuite() { const allTokens = await testEnv.helpersContract.getAllATokens(); const aDaiAddress = allTokens.find((aToken) => aToken.symbol === 'aDAI')?.tokenAddress; + const aUsdcAddress = allTokens.find((aToken) => aToken.symbol === 'aUSDC')?.tokenAddress; const aWEthAddress = allTokens.find((aToken) => aToken.symbol === 'aWETH')?.tokenAddress; @@ -156,6 +159,7 @@ export async function initializeMakeSuite() { } testEnv.aDai = await getAToken(aDaiAddress); + testEnv.aUsdc = await getAToken(aUsdcAddress); testEnv.aWETH = await getAToken(aWEthAddress); testEnv.dai = await getMintableERC20(daiAddress); diff --git a/test-suites/test-amm/flashloan.spec.ts b/test-suites/test-amm/flashloan.spec.ts index 22e9ade2..c5281a0b 100644 --- a/test-suites/test-amm/flashloan.spec.ts +++ b/test-suites/test-amm/flashloan.spec.ts @@ -47,10 +47,12 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { it('Takes WETH flashloan with mode = 0, returns the funds correctly', async () => { const { pool, helpersContract, weth } = testEnv; + const borrowedAmount = ethers.utils.parseEther('0.8'); + await pool.flashLoan( _mockFlashLoanReceiver.address, [weth.address], - [ethers.utils.parseEther('0.8')], + [borrowedAmount], [0], _mockFlashLoanReceiver.address, '0x10', From 22bbb962619c977c0e2f82b26109075f5ae16775 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Tue, 22 Jun 2021 12:25:13 +0200 Subject: [PATCH 2/3] test: added requirements in update premium functions of configurator --- .../lendingpool/LendingPoolConfigurator.sol | 16 ++++++++++++++++ contracts/protocol/libraries/helpers/Errors.sol | 2 ++ 2 files changed, 18 insertions(+) diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 3c699179..f8b730ee 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -514,6 +514,14 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur override onlyPoolAdmin { + require( + flashloanPremiumTotal < PercentageMath.PERCENTAGE_FACTOR, + Errors.LPC_FLASHLOAN_PREMIUM_INVALID + ); + require( + flashloanPremiumTotal >= _pool.FLASHLOAN_PREMIUM_TO_PROTOCOL(), + Errors.LPC_FLASHLOAN_PREMIUMS_MISMATCH + ); _pool.updateFlashloanPremiums(flashloanPremiumTotal, _pool.FLASHLOAN_PREMIUM_TO_PROTOCOL()); emit FlashloanPremiumTotalUpdated(flashloanPremiumTotal); } @@ -524,6 +532,14 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur override onlyPoolAdmin { + require( + flashloanPremiumToProtocol < PercentageMath.PERCENTAGE_FACTOR, + Errors.LPC_FLASHLOAN_PREMIUM_INVALID + ); + require( + flashloanPremiumToProtocol <= _pool.FLASHLOAN_PREMIUM_TOTAL(), + Errors.LPC_FLASHLOAN_PREMIUMS_MISMATCH + ); _pool.updateFlashloanPremiums(_pool.FLASHLOAN_PREMIUM_TOTAL(), flashloanPremiumToProtocol); emit FlashloanPremiumToProcolUpdated(flashloanPremiumToProtocol); } diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index e0fea7bb..8f576c40 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -113,6 +113,8 @@ library Errors { string public constant RL_STABLE_DEBT_NOT_ZERO = '89'; string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90'; string public constant LP_CALLER_NOT_EOA = '91'; + string public constant LPC_FLASHLOAN_PREMIUMS_MISMATCH = '95'; + string public constant LPC_FLASHLOAN_PREMIUM_INVALID = '96'; enum CollateralManagerErrors { NO_ERROR, From 694eb7731c07001debecd54502bc128a22188a9c Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Tue, 22 Jun 2021 12:40:12 +0200 Subject: [PATCH 3/3] test: configurator tested against update premium functions --- helpers/types.ts | 2 + test-suites/test-aave/configurator.spec.ts | 59 ++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/helpers/types.ts b/helpers/types.ts index 157f34ba..360050e5 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -187,6 +187,8 @@ export enum ProtocolErrors { RL_ATOKEN_SUPPLY_NOT_ZERO = '88', RL_STABLE_DEBT_NOT_ZERO = '89', RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90', + LPC_FLASHLOAN_PREMIUMS_MISMATCH = '95', + LPC_FLASHLOAN_PREMIUM_INVALID = '96', // old diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index 7a49e625..ecbcb0b0 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -26,6 +26,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN, LPC_CALLER_NOT_RISK_OR_POOL_ADMIN, VL_RESERVE_PAUSED, + LPC_FLASHLOAN_PREMIUMS_MISMATCH, + LPC_FLASHLOAN_PREMIUM_INVALID, } = ProtocolErrors; it('Reverts trying to set an invalid reserve factor', async () => { @@ -1303,4 +1305,61 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); + it('Update flash loan premiums: 10 toProtocol, 40 total', async () => { + const { dai, pool, configurator, users } = testEnv; + const newPremiumTotal = 40; + const newPremiumToProtocol = 10; + + await configurator.updateFlashloanPremiumTotal(newPremiumTotal); + await configurator.updateFlashloanPremiumToProtocol(newPremiumToProtocol); + + expect(await pool.FLASHLOAN_PREMIUM_TOTAL()).to.be.eq(newPremiumTotal); + expect(await pool.FLASHLOAN_PREMIUM_TO_PROTOCOL()).to.be.eq(newPremiumToProtocol); + }); + it('Fails to update flahloan premiums with toProtocol > total', async () => { + const { dai, pool, configurator, users } = testEnv; + const newPremiumTotal = 9; + const newPremiumToProtocol = 41; + + await expect(configurator.updateFlashloanPremiumTotal(newPremiumTotal)).to.be.revertedWith( + LPC_FLASHLOAN_PREMIUMS_MISMATCH + ); + await expect( + configurator.updateFlashloanPremiumToProtocol(newPremiumToProtocol) + ).to.be.revertedWith(LPC_FLASHLOAN_PREMIUMS_MISMATCH); + }); + it('Fails to update flahloan premiums > 100%', async () => { + const { dai, pool, configurator, users } = testEnv; + const newPremiumTotal = 10100; + const newPremiumToProtocol = 10100; + + await expect(configurator.updateFlashloanPremiumTotal(newPremiumTotal)).to.be.revertedWith( + LPC_FLASHLOAN_PREMIUM_INVALID + ); + await expect( + configurator.updateFlashloanPremiumToProtocol(newPremiumToProtocol) + ).to.be.revertedWith(LPC_FLASHLOAN_PREMIUM_INVALID); + }); + it('Checks only pool admin can update flashloan premiums', async () => { + const { dai, pool, configurator, users, riskAdmin, emergencyAdmin } = testEnv; + await expect( + configurator.connect(riskAdmin.signer).updateFlashloanPremiumToProtocol(50), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + + await expect( + configurator.connect(riskAdmin.signer).updateFlashloanPremiumTotal(50), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + + await expect( + configurator.connect(emergencyAdmin.signer).updateFlashloanPremiumToProtocol(50), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + + await expect( + configurator.connect(emergencyAdmin.signer).updateFlashloanPremiumTotal(50), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); });