Skip to content

Commit c0bd6b9

Browse files
TepNiksakulstra
authored andcommitted
Fixed a bug with rates after a flashloan
fix: adjust tests
1 parent 468e5dc commit c0bd6b9

5 files changed

Lines changed: 246 additions & 17 deletions

File tree

docs/3.4/Aave-v3.4-features.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ Solc was upgraded from `8.20` to `8.27`
184184
- `PoolLogic` contract:
185185
- The `PoolLogic` contract was extended with two methods `executeSyncIndexesState` & `executeSyncRatesState`, which simply replicate what was previously on the Pool itself. This is done to free some code-space on the pool itself.
186186
- `FlashLoanLogic` library:
187-
- Now all fees from flash-loans are sent to the `RESERVE_TREASURY_ADDRESS`.
187+
- Now all fees from flash-loans are accrued to the `RESERVE_TREASURY_ADDRESS`.
188188
- `MathUtils` library:
189189
- The `calculateCompoundedInterest` function was improved to be more accurate and gas efficient without changing the formula.
190190

snapshots/Pool.Operations.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"borrow: first borrow->borrowingEnabled": "244049",
33
"borrow: recurrent borrow": "216250",
4-
"flashLoan: flash loan for one asset": "143666",
4+
"flashLoan: flash loan for one asset": "162275",
55
"flashLoan: flash loan for one asset and borrow": "265661",
6-
"flashLoan: flash loan for two assets": "234930",
6+
"flashLoan: flash loan for two assets": "257324",
77
"flashLoan: flash loan for two assets and borrow": "458805",
8-
"flashLoanSimple: simple flash loan": "122132",
8+
"flashLoanSimple: simple flash loan": "138173",
99
"liquidationCall: deficit on liquidated asset": "354595",
1010
"liquidationCall: deficit on liquidated asset + other asset": "443653",
1111
"liquidationCall: full liquidation": "354595",

src/contracts/protocol/libraries/logic/FlashLoanLogic.sol

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,18 +218,29 @@ library FlashLoanLogic {
218218
DataTypes.ReserveData storage reserve,
219219
DataTypes.FlashLoanRepaymentParams memory params
220220
) internal {
221-
reserve.virtualUnderlyingBalance += params.amount.toUint128();
221+
uint256 amountPlusPremium = params.amount + params.totalPremium;
222+
223+
DataTypes.ReserveCache memory reserveCache = reserve.cache();
224+
reserve.updateState(reserveCache);
225+
226+
reserve.accruedToTreasury += params
227+
.totalPremium
228+
.rayDiv(reserveCache.nextLiquidityIndex)
229+
.toUint128();
230+
231+
reserve.updateInterestRatesAndVirtualBalance(
232+
reserveCache,
233+
params.asset,
234+
amountPlusPremium,
235+
0,
236+
params.interestRateStrategyAddress
237+
);
222238

223239
IERC20(params.asset).safeTransferFrom(
224240
params.receiverAddress,
225-
reserve.aTokenAddress,
226-
params.amount
241+
reserveCache.aTokenAddress,
242+
amountPlusPremium
227243
);
228-
if (params.totalPremium != 0) {
229-
address treasury = IAToken(reserve.aTokenAddress).RESERVE_TREASURY_ADDRESS();
230-
231-
IERC20(params.asset).safeTransferFrom(params.receiverAddress, treasury, params.totalPremium);
232-
}
233244

234245
emit IPool.FlashLoan(
235246
params.receiverAddress,
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.10;
3+
4+
import {IERC20} from '../../src/contracts/dependencies/openzeppelin/contracts/IERC20.sol';
5+
import {IPoolAddressesProvider} from '../../src/contracts/interfaces/IPoolAddressesProvider.sol';
6+
import {IPool} from '../../src/contracts/interfaces/IPool.sol';
7+
import {FlashLoanSimpleReceiverBase} from '../../src/contracts/misc/flashloan/base/FlashLoanSimpleReceiverBase.sol';
8+
import {MintableERC20} from '../../src/contracts/mocks/tokens/MintableERC20.sol';
9+
10+
contract MockFlashLoanBorrowInsideFlashLoan is FlashLoanSimpleReceiverBase {
11+
constructor(IPoolAddressesProvider provider) FlashLoanSimpleReceiverBase(provider) {}
12+
13+
function executeOperation(
14+
address asset,
15+
uint256 amount,
16+
uint256 premium,
17+
address /* initiator */,
18+
bytes calldata /* params */
19+
) external returns (bool) {
20+
IERC20(asset).approve(msg.sender, amount * 2 + premium);
21+
22+
uint256 underlyingBalance = IPool(msg.sender).getVirtualUnderlyingBalance(asset);
23+
24+
IPool(msg.sender).borrow({
25+
asset: asset,
26+
amount: (underlyingBalance * 9) / 10,
27+
interestRateMode: 2,
28+
referralCode: 0,
29+
onBehalfOf: address(this)
30+
});
31+
32+
return true;
33+
}
34+
35+
function executeOperation(
36+
address[] memory assets,
37+
uint256[] memory amounts,
38+
uint256[] memory premiums,
39+
address /* initiator */,
40+
bytes calldata /* params */
41+
) public returns (bool) {
42+
for (uint256 i = 0; i < assets.length; i++) {
43+
IERC20(assets[i]).approve(msg.sender, amounts[i] * 2 + premiums[i]);
44+
45+
uint256 underlyingBalance = IPool(msg.sender).getVirtualUnderlyingBalance(assets[i]);
46+
47+
IPool(msg.sender).borrow({
48+
asset: assets[i],
49+
amount: (underlyingBalance * 9) / 10,
50+
interestRateMode: 2,
51+
referralCode: 0,
52+
onBehalfOf: address(this)
53+
});
54+
}
55+
56+
return true;
57+
}
58+
}

tests/protocol/pool/Pool.FlashLoans.t.sol

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import {MockFlashLoanReceiver} from '../../../src/contracts/mocks/flashloan/Mock
1414
import {MockFlashLoanSimpleReceiver} from '../../../src/contracts/mocks/flashloan/MockSimpleFlashLoanReceiver.sol';
1515
import {IPoolAddressesProvider} from '../../../src/contracts/interfaces/IPoolAddressesProvider.sol';
1616
import {IPool} from '../../../src/contracts/interfaces/IPool.sol';
17+
import {IReserveInterestRateStrategy} from '../../../src/contracts/interfaces/IReserveInterestRateStrategy.sol';
1718
import {DataTypes} from '../../../src/contracts/protocol/libraries/types/DataTypes.sol';
1819
import {IERC20} from '../../../src/contracts/dependencies/openzeppelin/contracts/IERC20.sol';
1920
import {MockFlashLoanATokenReceiver} from '../../mocks/MockFlashLoanATokenReceiver.sol';
21+
import {MockFlashLoanBorrowInsideFlashLoan} from '../../mocks/MockFlashLoanBorrowInsideFlashLoan.sol';
2022
import {TestnetProcedures, TestReserveConfig} from '../../utils/TestnetProcedures.sol';
2123

2224
contract PoolFlashLoansTests is TestnetProcedures {
@@ -329,6 +331,8 @@ contract PoolFlashLoansTests is TestnetProcedures {
329331
uint256 virtualUnderlyingBalanceBefore = contracts.poolProxy.getVirtualUnderlyingBalance(
330332
tokenList.usdx
331333
);
334+
uint256 totalFee = contracts.poolProxy.FLASHLOAN_PREMIUM_TOTAL();
335+
uint256 amount = 10e6;
332336

333337
vm.prank(poolAdmin);
334338
TestnetERC20(tokenList.usdx).transferOwnership(address(mockFlashSimpleReceiver));
@@ -337,15 +341,18 @@ contract PoolFlashLoansTests is TestnetProcedures {
337341
contracts.poolProxy.flashLoanSimple(
338342
address(mockFlashSimpleReceiver),
339343
tokenList.usdx,
340-
10e6,
344+
amount,
341345
'0x',
342346
0
343347
);
344348

345349
uint256 virtualUnderlyingBalanceAfter = contracts.poolProxy.getVirtualUnderlyingBalance(
346350
tokenList.usdx
347351
);
348-
assertEq(virtualUnderlyingBalanceBefore, virtualUnderlyingBalanceAfter);
352+
assertEq(
353+
virtualUnderlyingBalanceBefore + (amount * totalFee) / 1e4,
354+
virtualUnderlyingBalanceAfter
355+
);
349356
}
350357

351358
function test_flashloan_simple_2() public {
@@ -382,6 +389,7 @@ contract PoolFlashLoansTests is TestnetProcedures {
382389
uint256 virtualUnderlyingBalanceBefore = contracts.poolProxy.getVirtualUnderlyingBalance(
383390
assets[0]
384391
);
392+
uint256 totalFee = contracts.poolProxy.FLASHLOAN_PREMIUM_TOTAL();
385393

386394
vm.prank(alice);
387395
contracts.poolProxy.flashLoan(
@@ -397,7 +405,10 @@ contract PoolFlashLoansTests is TestnetProcedures {
397405
uint256 virtualUnderlyingBalanceAfter = contracts.poolProxy.getVirtualUnderlyingBalance(
398406
assets[0]
399407
);
400-
assertEq(virtualUnderlyingBalanceBefore, virtualUnderlyingBalanceAfter);
408+
assertEq(
409+
virtualUnderlyingBalanceBefore + (amounts[0] * totalFee) / 1e4,
410+
virtualUnderlyingBalanceAfter
411+
);
401412
}
402413

403414
function test_flashloan_multiple() public {
@@ -414,6 +425,7 @@ contract PoolFlashLoansTests is TestnetProcedures {
414425
uint256 virtualUnderlyingBalanceBefore1 = contracts.poolProxy.getVirtualUnderlyingBalance(
415426
assets[1]
416427
);
428+
uint256 totalFee = contracts.poolProxy.FLASHLOAN_PREMIUM_TOTAL();
417429

418430
vm.prank(alice);
419431
contracts.poolProxy.flashLoan(
@@ -433,8 +445,14 @@ contract PoolFlashLoansTests is TestnetProcedures {
433445
assets[1]
434446
);
435447

436-
assertEq(virtualUnderlyingBalanceBefore0, virtualUnderlyingBalanceAfter0);
437-
assertEq(virtualUnderlyingBalanceBefore1, virtualUnderlyingBalanceAfter1);
448+
assertEq(
449+
virtualUnderlyingBalanceBefore0 + (amounts[0] * totalFee) / 1e4,
450+
virtualUnderlyingBalanceAfter0
451+
);
452+
assertEq(
453+
virtualUnderlyingBalanceBefore1 + (amounts[1] * totalFee) / 1e4,
454+
virtualUnderlyingBalanceAfter1
455+
);
438456
}
439457

440458
function test_flashloan_borrow() public {
@@ -460,6 +478,148 @@ contract PoolFlashLoansTests is TestnetProcedures {
460478
);
461479
}
462480

481+
function test_flashloan_simple_borrow_inside_flashloan_and_check_rate_after() public {
482+
MockFlashLoanBorrowInsideFlashLoan receiver = new MockFlashLoanBorrowInsideFlashLoan(
483+
contracts.poolAddressesProvider
484+
);
485+
486+
address asset = tokenList.usdx;
487+
uint256 underlyingBalance = contracts.poolProxy.getVirtualUnderlyingBalance(asset);
488+
489+
vm.startPrank(carol);
490+
contracts.poolProxy.borrow({
491+
asset: asset,
492+
amount: underlyingBalance / 5,
493+
interestRateMode: 2,
494+
referralCode: 0,
495+
onBehalfOf: carol
496+
});
497+
IERC20(contracts.poolProxy.getReserveAToken(asset)).transfer(
498+
address(receiver),
499+
underlyingBalance / 2
500+
);
501+
vm.stopPrank();
502+
503+
underlyingBalance = contracts.poolProxy.getVirtualUnderlyingBalance(asset);
504+
uint256 amount = (underlyingBalance * 9) / 10;
505+
506+
deal(asset, address(receiver), amount * 2);
507+
508+
DataTypes.ReserveDataLegacy memory reserveData = contracts.poolProxy.getReserveData(asset);
509+
assertGt(reserveData.currentLiquidityRate, 0);
510+
assertGt(reserveData.currentVariableBorrowRate, 0);
511+
512+
contracts.poolProxy.flashLoanSimple({
513+
receiverAddress: address(receiver),
514+
asset: asset,
515+
amount: amount,
516+
params: '',
517+
referralCode: 0
518+
});
519+
520+
reserveData = contracts.poolProxy.getReserveData(asset);
521+
522+
(uint256 nextLiquidityRate, uint256 nextVariableRate) = contracts
523+
.defaultInterestRateStrategy
524+
.calculateInterestRates(
525+
DataTypes.CalculateInterestRatesParams({
526+
unbacked: contracts.poolProxy.getReserveDeficit(asset),
527+
liquidityAdded: 0,
528+
liquidityTaken: 0,
529+
totalDebt: IERC20(contracts.poolProxy.getReserveVariableDebtToken(asset)).totalSupply(),
530+
reserveFactor: reserveData.configuration.getReserveFactor(),
531+
reserve: asset,
532+
usingVirtualBalance: true,
533+
virtualUnderlyingBalance: contracts.poolProxy.getVirtualUnderlyingBalance(asset)
534+
})
535+
);
536+
assertEq(reserveData.currentLiquidityRate, nextLiquidityRate);
537+
assertEq(reserveData.currentVariableBorrowRate, nextVariableRate);
538+
}
539+
540+
function test_flashloan_borrow_inside_flashloan_and_check_rate_after() public {
541+
MockFlashLoanBorrowInsideFlashLoan receiver = new MockFlashLoanBorrowInsideFlashLoan(
542+
contracts.poolAddressesProvider
543+
);
544+
545+
address[] memory assets = new address[](2);
546+
uint256[] memory underlyingBalances = new uint256[](2);
547+
uint256[] memory amounts = new uint256[](2);
548+
uint256[] memory interestRateModes = new uint256[](2);
549+
550+
assets[0] = tokenList.usdx;
551+
assets[1] = tokenList.wbtc;
552+
553+
interestRateModes[0] = 0;
554+
interestRateModes[1] = 0;
555+
556+
for (uint256 i = 0; i < assets.length; ++i) {
557+
underlyingBalances[i] = contracts.poolProxy.getVirtualUnderlyingBalance(assets[i]);
558+
559+
vm.startPrank(carol);
560+
contracts.poolProxy.borrow({
561+
asset: assets[i],
562+
amount: underlyingBalances[i] / 5,
563+
interestRateMode: 2,
564+
referralCode: 0,
565+
onBehalfOf: carol
566+
});
567+
568+
IERC20(contracts.poolProxy.getReserveAToken(assets[i])).transfer(
569+
address(receiver),
570+
underlyingBalances[i] / 2
571+
);
572+
vm.stopPrank();
573+
574+
underlyingBalances[i] = contracts.poolProxy.getVirtualUnderlyingBalance(assets[i]);
575+
576+
amounts[i] = (underlyingBalances[i] * 9) / 10;
577+
578+
deal(assets[i], address(receiver), amounts[i] * 2);
579+
580+
DataTypes.ReserveDataLegacy memory reserveData = contracts.poolProxy.getReserveData(
581+
assets[i]
582+
);
583+
584+
assertGt(reserveData.currentLiquidityRate, 0);
585+
assertGt(reserveData.currentVariableBorrowRate, 0);
586+
}
587+
588+
contracts.poolProxy.flashLoan({
589+
receiverAddress: address(receiver),
590+
assets: assets,
591+
amounts: amounts,
592+
interestRateModes: interestRateModes,
593+
onBehalfOf: address(receiver),
594+
params: '',
595+
referralCode: 0
596+
});
597+
598+
for (uint256 i = 0; i < assets.length; ++i) {
599+
DataTypes.ReserveDataLegacy memory reserveData = contracts.poolProxy.getReserveData(
600+
assets[i]
601+
);
602+
603+
(uint256 nextLiquidityRate, uint256 nextVariableRate) = contracts
604+
.defaultInterestRateStrategy
605+
.calculateInterestRates(
606+
DataTypes.CalculateInterestRatesParams({
607+
unbacked: contracts.poolProxy.getReserveDeficit(assets[0]),
608+
liquidityAdded: 0,
609+
liquidityTaken: 0,
610+
totalDebt: IERC20(contracts.poolProxy.getReserveVariableDebtToken(assets[0]))
611+
.totalSupply(),
612+
reserveFactor: reserveData.configuration.getReserveFactor(),
613+
reserve: assets[0],
614+
usingVirtualBalance: true,
615+
virtualUnderlyingBalance: contracts.poolProxy.getVirtualUnderlyingBalance(assets[0])
616+
})
617+
);
618+
assertEq(reserveData.currentLiquidityRate, nextLiquidityRate);
619+
assertEq(reserveData.currentVariableBorrowRate, nextVariableRate);
620+
}
621+
}
622+
463623
function test_revert_flashloan_borrow_stable() public {
464624
(
465625
address[] memory assets,

0 commit comments

Comments
 (0)