- Adapted flashLoan() for partial debt opening.

This commit is contained in:
eboado 2020-08-25 12:55:05 +02:00
parent d86aafda0c
commit 75da6e0fba
4 changed files with 145 additions and 117 deletions

View File

@ -232,6 +232,7 @@ interface ILendingPool {
address receiver, address receiver,
address reserve, address reserve,
uint256 amount, uint256 amount,
uint256 debtType,
bytes calldata params, bytes calldata params,
uint16 referralCode uint16 referralCode
) external; ) external;

View File

@ -154,6 +154,15 @@ contract LendingPool is VersionedInitializable, ILendingPool {
emit Withdraw(asset, msg.sender, amount); emit Withdraw(asset, msg.sender, amount);
} }
struct BorrowLocalVars {
address asset;
address user;
uint256 amount;
uint256 interestRateMode;
bool releaseUnderlying;
uint16 referralCode;
}
/** /**
* @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower * @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
* already deposited enough collateral. * already deposited enough collateral.
@ -167,25 +176,35 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 interestRateMode, uint256 interestRateMode,
uint16 referralCode uint16 referralCode
) external override { ) external override {
ReserveLogic.ReserveData storage reserve = _reserves[asset]; _executeBorrow(
UserConfiguration.Map storage userConfig = _usersConfig[msg.sender]; BorrowLocalVars(asset, msg.sender, amount, interestRateMode, true, referralCode)
);
}
uint256 amountInETH = IPriceOracleGetter(addressesProvider.getPriceOracle()) /**
.getAssetPrice(asset) * @dev Internal function to execute a borrowing action, allowing to transfer or not the underlying
.mul(amount) * @param vars Input struct for the borrowing action, in order to avoid STD errors
.div(10**reserve.configuration.getDecimals()); //price is in ether **/
function _executeBorrow(BorrowLocalVars memory vars) internal {
ReserveLogic.ReserveData storage reserve = _reserves[vars.asset];
UserConfiguration.Map storage userConfig = _usersConfig[vars.user];
address oracle = addressesProvider.getPriceOracle();
uint256 amountInETH = IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div(
10**reserve.configuration.getDecimals()
);
ValidationLogic.validateBorrow( ValidationLogic.validateBorrow(
reserve, reserve,
asset, vars.asset,
amount, vars.amount,
amountInETH, amountInETH,
interestRateMode, vars.interestRateMode,
MAX_STABLE_RATE_BORROW_SIZE_PERCENT, MAX_STABLE_RATE_BORROW_SIZE_PERCENT,
_reserves, _reserves,
_usersConfig[msg.sender], _usersConfig[vars.user],
reservesList, reservesList,
addressesProvider.getPriceOracle() oracle
); );
//caching the current stable borrow rate //caching the current stable borrow rate
@ -193,30 +212,34 @@ contract LendingPool is VersionedInitializable, ILendingPool {
reserve.updateCumulativeIndexesAndTimestamp(); reserve.updateCumulativeIndexesAndTimestamp();
if (ReserveLogic.InterestRateMode(interestRateMode) == ReserveLogic.InterestRateMode.STABLE) { if (
IStableDebtToken(reserve.stableDebtTokenAddress).mint(msg.sender, amount, userStableRate); ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE
) {
IStableDebtToken(reserve.stableDebtTokenAddress).mint(vars.user, vars.amount, userStableRate);
} else { } else {
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(msg.sender, amount); IVariableDebtToken(reserve.variableDebtTokenAddress).mint(vars.user, vars.amount);
} }
reserve.updateInterestRates(asset, 0, amount); reserve.updateInterestRates(vars.asset, 0, vars.amount);
if (!userConfig.isBorrowing(reserve.index)) { if (!userConfig.isBorrowing(reserve.index)) {
userConfig.setBorrowing(reserve.index, true); userConfig.setBorrowing(reserve.index, true);
} }
//if we reached this point, we can transfer //if we reached this point and we need to, we can transfer
IAToken(reserve.aTokenAddress).transferUnderlyingTo(msg.sender, amount); if (vars.releaseUnderlying) {
IAToken(reserve.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
}
emit Borrow( emit Borrow(
asset, vars.asset,
msg.sender, vars.user,
amount, vars.amount,
interestRateMode, vars.interestRateMode,
ReserveLogic.InterestRateMode(interestRateMode) == ReserveLogic.InterestRateMode.STABLE ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE
? userStableRate ? userStableRate
: reserve.currentVariableBorrowRate, : reserve.currentVariableBorrowRate,
referralCode vars.referralCode
); );
} }
@ -249,14 +272,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
paybackAmount = amount; paybackAmount = amount;
} }
ValidationLogic.validateRepay( ValidationLogic.validateRepay(reserve, amount, rateMode, _onBehalfOf, stableDebt, variableDebt);
reserve,
amount,
rateMode,
_onBehalfOf,
stableDebt,
variableDebt
);
reserve.updateCumulativeIndexesAndTimestamp(); reserve.updateCumulativeIndexesAndTimestamp();
@ -316,10 +332,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
reserve.updateInterestRates(asset, 0, 0); reserve.updateInterestRates(asset, 0, 0);
emit Swap( emit Swap(asset, msg.sender);
asset,
msg.sender
);
} }
/** /**
@ -374,10 +387,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
* @param asset the address of the reserve * @param asset the address of the reserve
* @param _useAsCollateral true if the user wants to user the deposit as collateral, false otherwise. * @param _useAsCollateral true if the user wants to user the deposit as collateral, false otherwise.
**/ **/
function setUserUseReserveAsCollateral(address asset, bool _useAsCollateral) function setUserUseReserveAsCollateral(address asset, bool _useAsCollateral) external override {
external
override
{
ReserveLogic.ReserveData storage reserve = _reserves[asset]; ReserveLogic.ReserveData storage reserve = _reserves[asset];
ValidationLogic.validateSetUseReserveAsCollateral( ValidationLogic.validateSetUseReserveAsCollateral(
@ -437,10 +447,14 @@ contract LendingPool is VersionedInitializable, ILendingPool {
} }
} }
struct FlashLoanLocalVars{ struct FlashLoanLocalVars {
uint256 premium; uint256 premium;
uint256 amountPlusPremium; uint256 amountPlusPremium;
uint256 amountPlusPremiumInETH; uint256 amountPlusPremiumInETH;
uint256 receiverBalance;
uint256 receiverAllowance;
uint256 balanceToPull;
uint256 assetPrice;
IFlashLoanReceiver receiver; IFlashLoanReceiver receiver;
address aTokenAddress; address aTokenAddress;
address oracle; address oracle;
@ -451,13 +465,17 @@ contract LendingPool is VersionedInitializable, ILendingPool {
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts * as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
* that must be kept into consideration. For further details please visit https://developers.aave.com * that must be kept into consideration. For further details please visit https://developers.aave.com
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface. * @param receiverAddress The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
* @param asset the address of the principal reserve * @param asset The address of the principal reserve
* @param amount the amount requested for this flashloan * @param amount The amount requested for this flashloan
* @param debtType Type of the debt to open if the flash loan is not returned. 0 -> Don't open any debt, just revert, 1 -> stable, 2 -> variable
* @param params Variadic packed params to pass to the receiver as extra information
* @param referralCode Referral code of the flash loan
**/ **/
function flashLoan( function flashLoan(
address receiverAddress, address receiverAddress,
address asset, address asset,
uint256 amount, uint256 amount,
uint256 debtType,
bytes calldata params, bytes calldata params,
uint16 referralCode uint16 referralCode
) external override { ) external override {
@ -486,49 +504,51 @@ contract LendingPool is VersionedInitializable, ILendingPool {
vars.amountPlusPremium = amount.add(vars.premium); vars.amountPlusPremium = amount.add(vars.premium);
//transfer from the receiver the amount plus the fee //transfer from the receiver the amount plus the fee
try IERC20(asset).transferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusPremium) { try IERC20(asset).transferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusPremium) {
//if the transfer succeeded, the executor has repaid the flashloan. //if the transfer succeeded, the executor has repaid the flashloan.
//the fee is compounded into the reserve //the fee is compounded into the reserve
reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.premium); reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.premium);
//refresh interest rates //refresh interest rates
reserve.updateInterestRates(asset, vars.premium, 0); reserve.updateInterestRates(asset, vars.premium, 0);
emit FlashLoan(receiverAddress, asset, amount, vars.premium, referralCode); emit FlashLoan(receiverAddress, asset, amount, vars.premium, referralCode);
} } catch (bytes memory reason) {
catch(bytes memory reason){ if (debtType == 1 || debtType == 2) {
// If the transfer didn't succeed, the receiver either didn't return the funds, or didn't approve the transfer.
// We will try to pull all the available funds from the receiver and create a debt position with the rest owed
// if it has collateral enough
//if the transfer didn't succeed, the executor either didn't return the funds, or didn't approve the transfer. vars.receiverBalance = IERC20(asset).balanceOf(receiverAddress);
//we check if the caller has enough collateral to open a variable rate loan. If it does, then debt is mint to msg.sender vars.receiverAllowance = IERC20(asset).allowance(receiverAddress, address(this));
vars.oracle = addressesProvider.getPriceOracle(); vars.balanceToPull = (vars.receiverBalance > vars.receiverAllowance)
vars.amountPlusPremiumInETH = IPriceOracleGetter(vars.oracle) ? vars.receiverAllowance
.getAssetPrice(asset) : vars.receiverBalance;
.mul(vars.amountPlusPremium)
.div(10**reserve.configuration.getDecimals()); //price is in ether
ValidationLogic.validateBorrow( if (vars.balanceToPull > 0) {
reserve, IERC20(asset).transferFrom(receiverAddress, vars.aTokenAddress, vars.balanceToPull);
asset, reserve.cumulateToLiquidityIndex(
vars.amountPlusPremium, IERC20(vars.aTokenAddress).totalSupply(),
vars.amountPlusPremiumInETH, (vars.balanceToPull > vars.premium) ? vars.premium : vars.balanceToPull
uint256(ReserveLogic.InterestRateMode.VARIABLE), );
MAX_STABLE_RATE_BORROW_SIZE_PERCENT, reserve.updateInterestRates(
_reserves, asset,
_usersConfig[msg.sender], (vars.balanceToPull > vars.premium) ? vars.premium : vars.balanceToPull,
reservesList, 0
vars.oracle );
); }
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(msg.sender, vars.amountPlusPremium); _executeBorrow(
//refresh interest rate, substracting from the available liquidity the flashloan amount BorrowLocalVars(
reserve.updateInterestRates(asset, 0, amount); asset,
msg.sender,
emit Borrow( vars.amountPlusPremium.sub(vars.balanceToPull),
asset, debtType,
msg.sender, false,
vars.amountPlusPremium, referralCode
uint256(ReserveLogic.InterestRateMode.VARIABLE), )
reserve.currentVariableBorrowRate, );
referralCode } else {
); revert(string(reason));
}
} }
} }

View File

@ -13,6 +13,7 @@
"types-gen": "typechain --target ethers-v5 --outDir ./types './artifacts/*.json'", "types-gen": "typechain --target ethers-v5 --outDir ./types './artifacts/*.json'",
"test": "buidler test", "test": "buidler test",
"test-scenarios": "buidler test test/__setup.spec.ts test/scenario.spec.ts", "test-scenarios": "buidler test test/__setup.spec.ts test/scenario.spec.ts",
"test-flash": "buidler test test/__setup.spec.ts test/flashloan.spec.ts",
"dev:coverage": "buidler coverage", "dev:coverage": "buidler coverage",
"dev:deployment": "buidler dev-deployment", "dev:deployment": "buidler dev-deployment",
"dev:deployExample": "buidler deploy-Example", "dev:deployExample": "buidler deploy-Example",

View File

@ -1,13 +1,17 @@
import { TestEnv, makeSuite } from './helpers/make-suite'; import {TestEnv, makeSuite} from './helpers/make-suite';
import { APPROVAL_AMOUNT_LENDING_POOL, oneRay } from '../helpers/constants'; import {APPROVAL_AMOUNT_LENDING_POOL, oneRay} from '../helpers/constants';
import { convertToCurrencyDecimals, getMockFlashLoanReceiver, getContract } from '../helpers/contracts-helpers'; import {
import { ethers } from 'ethers'; convertToCurrencyDecimals,
import { MockFlashLoanReceiver } from '../types/MockFlashLoanReceiver'; getMockFlashLoanReceiver,
import { ProtocolErrors, eContractid } from '../helpers/types'; getContract,
} from '../helpers/contracts-helpers';
import {ethers} from 'ethers';
import {MockFlashLoanReceiver} from '../types/MockFlashLoanReceiver';
import {ProtocolErrors, eContractid} from '../helpers/types';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { VariableDebtToken } from '../types/VariableDebtToken'; import {VariableDebtToken} from '../types/VariableDebtToken';
const { expect } = require('chai'); const {expect} = require('chai');
makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver;
@ -22,7 +26,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('Deposits ETH into the reserve', async () => { it('Deposits ETH into the reserve', async () => {
const { pool, weth } = testEnv; const {pool, weth} = testEnv;
const amountToDeposit = ethers.utils.parseEther('1'); const amountToDeposit = ethers.utils.parseEther('1');
await weth.mint(amountToDeposit); await weth.mint(amountToDeposit);
@ -33,12 +37,13 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('Takes ETH flashloan, returns the funds correctly', async () => { it('Takes ETH flashloan, returns the funds correctly', async () => {
const { pool, deployer, weth } = testEnv; const {pool, deployer, weth} = testEnv;
await pool.flashLoan( await pool.flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, weth.address,
ethers.utils.parseEther('0.8'), ethers.utils.parseEther('0.8'),
2,
'0x10', '0x10',
'0' '0'
); );
@ -60,13 +65,14 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('Takes an ETH flashloan as big as the available liquidity', async () => { it('Takes an ETH flashloan as big as the available liquidity', async () => {
const { pool, weth } = testEnv; const {pool, weth} = testEnv;
const reserveDataBefore = await pool.getReserveData(weth.address); const reserveDataBefore = await pool.getReserveData(weth.address);
const txResult = await pool.flashLoan( const txResult = await pool.flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, weth.address,
'1000720000000000000', '1000720000000000000',
2,
'0x10', '0x10',
'0' '0'
); );
@ -86,7 +92,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('Takes WETH flashloan, does not return the funds. Caller does not have any collateral (revert expected)', async () => { it('Takes WETH flashloan, does not return the funds. Caller does not have any collateral (revert expected)', async () => {
const { pool, weth, users } = testEnv; const {pool, weth, users} = testEnv;
const caller = users[1]; const caller = users[1];
await _mockFlashLoanReceiver.setFailExecutionTransfer(true); await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
@ -97,6 +103,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, weth.address,
ethers.utils.parseEther('0.8'), ethers.utils.parseEther('0.8'),
2,
'0x10', '0x10',
'0' '0'
) )
@ -104,7 +111,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('Caller deposits 1000 DAI as collateral, Takes WETH flashloan, does not return the funds. A loan for caller is created', async () => { it('Caller deposits 1000 DAI as collateral, Takes WETH flashloan, does not return the funds. A loan for caller is created', async () => {
const { dai, pool, weth, users } = testEnv; const {dai, pool, weth, users} = testEnv;
const caller = users[1]; const caller = users[1];
@ -124,12 +131,16 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, weth.address,
ethers.utils.parseEther('0.8'), ethers.utils.parseEther('0.8'),
2,
'0x10', '0x10',
'0' '0'
); );
const {variableDebtTokenAddress} = await pool.getReserveTokensAddresses(weth.address); const {variableDebtTokenAddress} = await pool.getReserveTokensAddresses(weth.address);
const wethDebtToken = await getContract<VariableDebtToken>(eContractid.VariableDebtToken, variableDebtTokenAddress); const wethDebtToken = await getContract<VariableDebtToken>(
eContractid.VariableDebtToken,
variableDebtTokenAddress
);
const callerDebt = await wethDebtToken.balanceOf(caller.address); const callerDebt = await wethDebtToken.balanceOf(caller.address);
@ -137,13 +148,14 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('tries to take a very small flashloan, which would result in 0 fees (revert expected)', async () => { it('tries to take a very small flashloan, which would result in 0 fees (revert expected)', async () => {
const { pool, weth } = testEnv; const {pool, weth} = testEnv;
await expect( await expect(
pool.flashLoan( pool.flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, weth.address,
'1', //1 wei loan '1', //1 wei loan
2,
'0x10', '0x10',
'0' '0'
) )
@ -151,13 +163,14 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => { it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => {
const { pool, weth } = testEnv; const {pool, weth} = testEnv;
await expect( await expect(
pool.flashLoan( pool.flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, weth.address,
'1004415000000000000', //slightly higher than the available liquidity '1004415000000000000', //slightly higher than the available liquidity
2,
'0x10', '0x10',
'0' '0'
), ),
@ -166,14 +179,15 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('tries to take a flashloan using a non contract address as receiver (revert expected)', async () => { it('tries to take a flashloan using a non contract address as receiver (revert expected)', async () => {
const { pool, deployer, weth } = testEnv; const {pool, deployer, weth} = testEnv;
await expect(pool.flashLoan(deployer.address, weth.address, '1000000000000000000', '0x10', '0')) await expect(
.to.be.reverted; pool.flashLoan(deployer.address, weth.address, '1000000000000000000', 2, '0x10', '0')
).to.be.reverted;
}); });
it('Deposits USDC into the reserve', async () => { it('Deposits USDC into the reserve', async () => {
const { usdc, pool } = testEnv; const {usdc, pool} = testEnv;
await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000')); await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
@ -185,7 +199,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => { it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => {
const { usdc, pool, deployer: depositor } = testEnv; const {usdc, pool, deployer: depositor} = testEnv;
await _mockFlashLoanReceiver.setFailExecutionTransfer(false); await _mockFlashLoanReceiver.setFailExecutionTransfer(false);
@ -195,6 +209,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
usdc.address, usdc.address,
flashloanAmount, flashloanAmount,
2,
'0x10', '0x10',
'0' '0'
); );
@ -210,7 +225,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
const currentLiquidityIndex = reserveData.liquidityIndex.toString(); const currentLiquidityIndex = reserveData.liquidityIndex.toString();
const currentUserBalance = userData.currentATokenBalance.toString(); const currentUserBalance = userData.currentATokenBalance.toString();
const expectedLiquidity = await convertToCurrencyDecimals(usdc.address,'1000.450'); const expectedLiquidity = await convertToCurrencyDecimals(usdc.address, '1000.450');
expect(totalLiquidity).to.be.equal(expectedLiquidity, 'Invalid total liquidity'); expect(totalLiquidity).to.be.equal(expectedLiquidity, 'Invalid total liquidity');
expect(currentLiqudityRate).to.be.equal('0', 'Invalid liquidity rate'); expect(currentLiqudityRate).to.be.equal('0', 'Invalid liquidity rate');
@ -222,7 +237,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('Takes out a 500 USDC flashloan, does not return the funds. Caller does not have any collateral (revert expected)', async () => { it('Takes out a 500 USDC flashloan, does not return the funds. Caller does not have any collateral (revert expected)', async () => {
const { usdc, pool, users } = testEnv; const {usdc, pool, users} = testEnv;
const caller = users[2]; const caller = users[2];
const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500'); const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
@ -232,18 +247,12 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await expect( await expect(
pool pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan( .flashLoan(_mockFlashLoanReceiver.address, usdc.address, flashloanAmount, 2, '0x10', '0')
_mockFlashLoanReceiver.address,
usdc.address,
flashloanAmount,
'0x10',
'0'
)
).to.be.revertedWith(COLLATERAL_BALANCE_IS_0); ).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
}); });
it('Caller deposits 5 ETH as collateral, Takes a USDC flashloan, does not return the funds. A loan for caller is created', async () => { it('Caller deposits 5 ETH as collateral, Takes a USDC flashloan, does not return the funds. A loan for caller is created', async () => {
const { usdc, pool, weth, users } = testEnv; const {usdc, pool, weth, users} = testEnv;
const caller = users[2]; const caller = users[2];
@ -261,16 +270,13 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await pool await pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan( .flashLoan(_mockFlashLoanReceiver.address, usdc.address, flashloanAmount, 2, '0x10', '0');
_mockFlashLoanReceiver.address,
usdc.address,
flashloanAmount,
'0x10',
'0'
);
const {variableDebtTokenAddress} = await pool.getReserveTokensAddresses(usdc.address); const {variableDebtTokenAddress} = await pool.getReserveTokensAddresses(usdc.address);
const usdcDebtToken = await getContract<VariableDebtToken>(eContractid.VariableDebtToken, variableDebtTokenAddress); const usdcDebtToken = await getContract<VariableDebtToken>(
eContractid.VariableDebtToken,
variableDebtTokenAddress
);
const callerDebt = await usdcDebtToken.balanceOf(caller.address); const callerDebt = await usdcDebtToken.balanceOf(caller.address);