mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Updated flashloans V2
This commit is contained in:
parent
e04d1881a1
commit
928770d9d5
|
@ -8,7 +8,7 @@ usePlugin('buidler-typechain');
|
|||
usePlugin('solidity-coverage');
|
||||
usePlugin('@nomiclabs/buidler-waffle');
|
||||
usePlugin('@nomiclabs/buidler-etherscan');
|
||||
usePlugin('buidler-gas-reporter');
|
||||
//usePlugin('buidler-gas-reporter');
|
||||
|
||||
const DEFAULT_BLOCK_GAS_LIMIT = 10000000;
|
||||
const DEFAULT_GAS_PRICE = 10;
|
||||
|
|
|
@ -63,7 +63,7 @@ interface ILendingPool {
|
|||
* @param reserve the address of the reserve
|
||||
* @param user the address of the user executing the swap
|
||||
**/
|
||||
event Swap(address indexed reserve, address indexed user, uint256 timestamp);
|
||||
event Swap(address indexed reserve, address indexed user);
|
||||
|
||||
/**
|
||||
* @dev emitted when a user enables a reserve as collateral
|
||||
|
@ -91,12 +91,14 @@ interface ILendingPool {
|
|||
* @param reserve the address of the reserve
|
||||
* @param amount the amount requested
|
||||
* @param totalFee the total fee on the amount
|
||||
* @param referralCode the referral code of the caller
|
||||
**/
|
||||
event FlashLoan(
|
||||
address indexed target,
|
||||
address indexed reserve,
|
||||
uint256 amount,
|
||||
uint256 totalFee
|
||||
uint256 totalFee,
|
||||
uint16 referralCode
|
||||
);
|
||||
/**
|
||||
* @dev these events are not emitted directly by the LendingPool
|
||||
|
@ -105,21 +107,6 @@ interface ILendingPool {
|
|||
* This allows to have the events in the generated ABI for LendingPool.
|
||||
**/
|
||||
|
||||
/**
|
||||
* @dev emitted when a borrow fee is liquidated
|
||||
* @param collateral the address of the collateral being liquidated
|
||||
* @param reserve the address of the reserve
|
||||
* @param user the address of the user being liquidated
|
||||
* @param feeLiquidated the total fee liquidated
|
||||
* @param liquidatedCollateralForFee the amount of collateral received by the protocol in exchange for the fee
|
||||
**/
|
||||
event OriginationFeeLiquidated(
|
||||
address indexed collateral,
|
||||
address indexed reserve,
|
||||
address indexed user,
|
||||
uint256 feeLiquidated,
|
||||
uint256 liquidatedCollateralForFee
|
||||
);
|
||||
/**
|
||||
* @dev emitted when a borrower is liquidated
|
||||
* @param collateral the address of the collateral being liquidated
|
||||
|
@ -238,12 +225,15 @@ interface ILendingPool {
|
|||
* @param receiver The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
|
||||
* @param reserve the address of the principal reserve
|
||||
* @param amount the amount requested for this flashloan
|
||||
* @param params a bytes array to be sent to the flashloan executor
|
||||
* @param referralCode the referral code of the caller
|
||||
**/
|
||||
function flashLoan(
|
||||
address receiver,
|
||||
address reserve,
|
||||
uint256 amount,
|
||||
bytes calldata params
|
||||
bytes calldata params,
|
||||
uint16 referralCode
|
||||
) external;
|
||||
|
||||
/**
|
||||
|
|
|
@ -108,7 +108,6 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
|||
//transfer to the aToken contract
|
||||
IERC20(asset).safeTransferFrom(msg.sender, address(aToken), amount);
|
||||
|
||||
//solium-disable-next-line
|
||||
emit Deposit(asset, msg.sender, amount, referralCode);
|
||||
}
|
||||
|
||||
|
@ -152,7 +151,6 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
|||
|
||||
aToken.burn(msg.sender, msg.sender, amountToWithdraw);
|
||||
|
||||
//solium-disable-next-line
|
||||
emit Withdraw(asset, msg.sender, amount);
|
||||
}
|
||||
|
||||
|
@ -320,9 +318,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
|||
|
||||
emit Swap(
|
||||
asset,
|
||||
msg.sender,
|
||||
//solium-disable-next-line
|
||||
block.timestamp
|
||||
msg.sender
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -441,6 +437,15 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
|||
}
|
||||
}
|
||||
|
||||
struct FlashLoanLocalVars{
|
||||
uint256 amountFee;
|
||||
uint256 amountPlusFee;
|
||||
uint256 amountPlusFeeInETH;
|
||||
IFlashLoanReceiver receiver;
|
||||
address aTokenAddress;
|
||||
address oracle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev allows smartcontracts to access the liquidity of the pool within one transaction,
|
||||
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
|
||||
|
@ -453,40 +458,77 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
|||
address receiverAddress,
|
||||
address asset,
|
||||
uint256 amount,
|
||||
bytes calldata params
|
||||
bytes calldata params,
|
||||
uint16 referralCode
|
||||
) external override {
|
||||
ReserveLogic.ReserveData storage reserve = _reserves[asset];
|
||||
FlashLoanLocalVars memory vars;
|
||||
|
||||
address aTokenAddress = reserve.aTokenAddress;
|
||||
vars.aTokenAddress = reserve.aTokenAddress;
|
||||
|
||||
//calculate amount fee
|
||||
uint256 amountFee = amount.mul(FLASHLOAN_FEE_TOTAL).div(10000);
|
||||
vars.amountFee = amount.mul(FLASHLOAN_FEE_TOTAL).div(10000);
|
||||
|
||||
require(amountFee > 0, 'The requested amount is too small for a FlashLoan.');
|
||||
require(vars.amountFee > 0, 'The requested amount is too small for a FlashLoan.');
|
||||
|
||||
//get the FlashLoanReceiver instance
|
||||
IFlashLoanReceiver receiver = IFlashLoanReceiver(receiverAddress);
|
||||
vars.receiver = IFlashLoanReceiver(receiverAddress);
|
||||
|
||||
//transfer funds to the receiver
|
||||
IAToken(aTokenAddress).transferUnderlyingTo(receiverAddress, amount);
|
||||
IAToken(vars.aTokenAddress).transferUnderlyingTo(receiverAddress, amount);
|
||||
|
||||
//execute action of the receiver
|
||||
receiver.executeOperation(asset, amount, amountFee, params);
|
||||
vars.receiver.executeOperation(asset, amount, vars.amountFee, params);
|
||||
|
||||
//transfer from the receiver the amount plus the fee
|
||||
IERC20(asset).safeTransferFrom(receiverAddress, aTokenAddress, amount.add(amountFee));
|
||||
|
||||
//compounding the cumulated interest
|
||||
//compounding the cumulated interest
|
||||
reserve.updateCumulativeIndexesAndTimestamp();
|
||||
|
||||
//compounding the received fee into the reserve
|
||||
reserve.cumulateToLiquidityIndex(IERC20(aTokenAddress).totalSupply(), amountFee);
|
||||
vars.amountPlusFee = amount.add(vars.amountFee);
|
||||
|
||||
//refresh interest rates
|
||||
reserve.updateInterestRates(asset, amountFee, 0);
|
||||
//transfer from the receiver the amount plus the fee
|
||||
try IERC20(asset).transferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusFee) {
|
||||
//if the transfer succeeded, the executor has repaid the flashloans.
|
||||
//the fee is compounded into the reserve
|
||||
reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.amountFee);
|
||||
//refresh interest rates
|
||||
reserve.updateInterestRates(asset, vars.amountFee, 0);
|
||||
emit FlashLoan(receiverAddress, asset, amount, vars.amountFee, referralCode);
|
||||
}
|
||||
catch(bytes memory reason){
|
||||
|
||||
//solium-disable-next-line
|
||||
emit FlashLoan(receiverAddress, asset, amount, amountFee);
|
||||
//if the transfer didn't succeed, the executor either didn't return the funds, or didn't approve the transfer.
|
||||
//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.oracle = addressesProvider.getPriceOracle();
|
||||
vars.amountPlusFeeInETH = IPriceOracleGetter(vars.oracle)
|
||||
.getAssetPrice(asset)
|
||||
.mul(vars.amountPlusFee)
|
||||
.div(10**reserve.configuration.getDecimals()); //price is in ether
|
||||
|
||||
ValidationLogic.validateBorrow(
|
||||
reserve,
|
||||
asset,
|
||||
vars.amountPlusFee,
|
||||
vars.amountPlusFeeInETH,
|
||||
uint256(ReserveLogic.InterestRateMode.VARIABLE),
|
||||
MAX_STABLE_RATE_BORROW_SIZE_PERCENT,
|
||||
_reserves,
|
||||
_usersConfig[msg.sender],
|
||||
reservesList,
|
||||
vars.oracle
|
||||
);
|
||||
|
||||
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(msg.sender, vars.amountPlusFee);
|
||||
//refresh interest rates
|
||||
reserve.updateInterestRates(asset, vars.amountFee, 0);
|
||||
emit Borrow(
|
||||
asset,
|
||||
msg.sender,
|
||||
vars.amountPlusFee,
|
||||
uint256(ReserveLogic.InterestRateMode.VARIABLE),
|
||||
reserve.currentVariableBorrowRate,
|
||||
referralCode
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -62,10 +62,6 @@ library ValidationLogic {
|
|||
) external view {
|
||||
require(amount > 0, 'Amount must be greater than 0');
|
||||
|
||||
uint256 currentAvailableLiquidity = IERC20(reserveAddress).balanceOf(address(aTokenAddress));
|
||||
|
||||
require(currentAvailableLiquidity >= amount, '4');
|
||||
|
||||
require(amount <= userBalance, 'User cannot withdraw more than the available balance');
|
||||
|
||||
require(
|
||||
|
@ -150,11 +146,6 @@ library ValidationLogic {
|
|||
'Invalid interest rate mode selected'
|
||||
);
|
||||
|
||||
//check that the amount is available in the reserve
|
||||
vars.availableLiquidity = IERC20(reserveAddress).balanceOf(address(reserve.aTokenAddress));
|
||||
|
||||
require(vars.availableLiquidity >= amount, '7');
|
||||
|
||||
(
|
||||
vars.userCollateralBalanceETH,
|
||||
vars.userBorrowBalanceETH,
|
||||
|
|
|
@ -63,6 +63,7 @@ export enum ProtocolErrors {
|
|||
INVALID_HF = 'Invalid health factor',
|
||||
USER_DID_NOT_BORROW_SPECIFIED = 'User did not borrow the specified currency',
|
||||
THE_COLLATERAL_CHOSEN_CANNOT_BE_LIQUIDATED = 'The collateral chosen cannot be liquidated',
|
||||
COLLATERAL_BALANCE_IS_0 = 'The collateral balance is 0'
|
||||
}
|
||||
|
||||
export type tEthereumAddress = string;
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import {TestEnv, makeSuite} from './helpers/make-suite';
|
||||
import {APPROVAL_AMOUNT_LENDING_POOL, oneRay} from '../helpers/constants';
|
||||
import {convertToCurrencyDecimals, getMockFlashLoanReceiver} from '../helpers/contracts-helpers';
|
||||
import {ethers} from 'ethers';
|
||||
import {MockFlashLoanReceiver} from '../types/MockFlashLoanReceiver';
|
||||
import {ProtocolErrors} from '../helpers/types';
|
||||
import { TestEnv, makeSuite } from './helpers/make-suite';
|
||||
import { APPROVAL_AMOUNT_LENDING_POOL, oneRay } from '../helpers/constants';
|
||||
import { convertToCurrencyDecimals, getMockFlashLoanReceiver, 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 { VariableDebtToken } from '../types/VariableDebtToken';
|
||||
|
||||
const {expect} = require('chai');
|
||||
const { expect } = require('chai');
|
||||
|
||||
makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||
let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver;
|
||||
const {
|
||||
TRANSFER_AMOUNT_EXCEEDS_BALANCE,
|
||||
TOO_SMALL_FLASH_LOAN,
|
||||
COLLATERAL_BALANCE_IS_0,
|
||||
} = ProtocolErrors;
|
||||
|
||||
before(async () => {
|
||||
|
@ -20,7 +22,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
});
|
||||
|
||||
it('Deposits ETH into the reserve', async () => {
|
||||
const {pool, weth} = testEnv;
|
||||
const { pool, weth } = testEnv;
|
||||
const amountToDeposit = ethers.utils.parseEther('1');
|
||||
|
||||
await weth.mint(amountToDeposit);
|
||||
|
@ -31,13 +33,14 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
});
|
||||
|
||||
it('Takes ETH flashloan, returns the funds correctly', async () => {
|
||||
const {pool, deployer, weth} = testEnv;
|
||||
const { pool, deployer, weth } = testEnv;
|
||||
|
||||
await pool.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
ethers.utils.parseEther('0.8'),
|
||||
'0x10'
|
||||
'0x10',
|
||||
'0'
|
||||
);
|
||||
|
||||
ethers.utils.parseUnits('10000');
|
||||
|
@ -57,17 +60,15 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
});
|
||||
|
||||
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);
|
||||
|
||||
console.log("Total liquidity is ", reserveDataBefore.availableLiquidity.toString());
|
||||
|
||||
const txResult = await pool.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
'1000720000000000000',
|
||||
'0x10'
|
||||
'0x10',
|
||||
'0'
|
||||
);
|
||||
|
||||
const reserveData = await pool.getReserveData(weth.address);
|
||||
|
@ -84,83 +85,122 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
expect(currentLiquidityIndex.toString()).to.be.equal('1001620648000000000000000000');
|
||||
});
|
||||
|
||||
it('Takes WETH flashloan, does not return the funds (revert expected)', async () => {
|
||||
const {pool, deployer, weth} = testEnv;
|
||||
|
||||
// move funds to the MockFlashLoanReceiver contract to pay the fee
|
||||
|
||||
it('Takes WETH flashloan, does not return the funds. Caller does not have any collateral (revert expected)', async () => {
|
||||
const { pool, weth, users } = testEnv;
|
||||
const caller = users[1];
|
||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
||||
|
||||
await expect(
|
||||
pool.flashLoan(
|
||||
pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
ethers.utils.parseEther('0.8'),
|
||||
'0x10',
|
||||
'0'
|
||||
)
|
||||
).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
|
||||
});
|
||||
|
||||
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 caller = users[1];
|
||||
|
||||
await dai.connect(caller.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
||||
|
||||
await dai.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
||||
|
||||
await pool.connect(caller.signer).deposit(dai.address, amountToDeposit, '0');
|
||||
|
||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
||||
|
||||
await pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
ethers.utils.parseEther('0.8'),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith(TRANSFER_AMOUNT_EXCEEDS_BALANCE);
|
||||
'0x10',
|
||||
'0'
|
||||
);
|
||||
const {variableDebtTokenAddress} = await pool.getReserveTokensAddresses(weth.address);
|
||||
|
||||
const wethDebtToken = await getContract<VariableDebtToken>(eContractid.VariableDebtToken, variableDebtTokenAddress);
|
||||
|
||||
const callerDebt = await wethDebtToken.balanceOf(caller.address);
|
||||
|
||||
expect(callerDebt.toString()).to.be.equal('800720000000000000', 'Invalid user debt');
|
||||
});
|
||||
|
||||
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(
|
||||
pool.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
'1', //1 wei loan
|
||||
'0x10'
|
||||
'0x10',
|
||||
'0'
|
||||
)
|
||||
).to.be.revertedWith(TOO_SMALL_FLASH_LOAN);
|
||||
});
|
||||
|
||||
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(
|
||||
pool.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
weth.address,
|
||||
'1004415000000000000', //slightly higher than the available liquidity
|
||||
'0x10'
|
||||
'0x10',
|
||||
'0'
|
||||
),
|
||||
TRANSFER_AMOUNT_EXCEEDS_BALANCE
|
||||
).to.be.revertedWith(TRANSFER_AMOUNT_EXCEEDS_BALANCE);
|
||||
});
|
||||
|
||||
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')).to
|
||||
.be.reverted;
|
||||
await expect(pool.flashLoan(deployer.address, weth.address, '1000000000000000000', '0x10', '0'))
|
||||
.to.be.reverted;
|
||||
});
|
||||
|
||||
it('Deposits DAI into the reserve', async () => {
|
||||
const {dai, pool} = testEnv;
|
||||
it('Deposits USDC into the reserve', async () => {
|
||||
const { usdc, pool } = testEnv;
|
||||
|
||||
await dai.mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
||||
await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
|
||||
|
||||
await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
||||
const amountToDeposit = await convertToCurrencyDecimals(usdc.address, '1000');
|
||||
|
||||
await pool.deposit(dai.address, amountToDeposit, '0');
|
||||
await pool.deposit(usdc.address, amountToDeposit, '0');
|
||||
});
|
||||
|
||||
it('Takes out a 500 DAI flashloan, returns the funds correctly', async () => {
|
||||
const {dai, pool, deployer: depositor} = testEnv;
|
||||
it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => {
|
||||
const { usdc, pool, deployer: depositor } = testEnv;
|
||||
|
||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(false);
|
||||
|
||||
const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
|
||||
|
||||
await pool.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
dai.address,
|
||||
ethers.utils.parseEther('500'),
|
||||
'0x10'
|
||||
usdc.address,
|
||||
flashloanAmount,
|
||||
'0x10',
|
||||
'0'
|
||||
);
|
||||
|
||||
const reserveData = await pool.getReserveData(dai.address);
|
||||
const userData = await pool.getUserReserveData(dai.address, depositor.address);
|
||||
const reserveData = await pool.getReserveData(usdc.address);
|
||||
const userData = await pool.getUserReserveData(usdc.address, depositor.address);
|
||||
|
||||
const totalLiquidity = reserveData.availableLiquidity
|
||||
.add(reserveData.totalBorrowsStable)
|
||||
|
@ -170,7 +210,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
const currentLiquidityIndex = reserveData.liquidityIndex.toString();
|
||||
const currentUserBalance = userData.currentATokenBalance.toString();
|
||||
|
||||
const expectedLiquidity = ethers.utils.parseEther('1000.450');
|
||||
const expectedLiquidity = await convertToCurrencyDecimals(usdc.address,'1000.450');
|
||||
|
||||
expect(totalLiquidity).to.be.equal(expectedLiquidity, 'Invalid total liquidity');
|
||||
expect(currentLiqudityRate).to.be.equal('0', 'Invalid liquidity rate');
|
||||
|
@ -181,19 +221,59 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
expect(currentUserBalance.toString()).to.be.equal(expectedLiquidity, 'Invalid user balance');
|
||||
});
|
||||
|
||||
it('Takes out a 500 DAI flashloan, does not return the funds (revert expected)', async () => {
|
||||
const {dai, pool} = testEnv;
|
||||
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 caller = users[2];
|
||||
|
||||
const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
|
||||
|
||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
||||
|
||||
await expect(
|
||||
pool.flashLoan(
|
||||
pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
usdc.address,
|
||||
flashloanAmount,
|
||||
'0x10',
|
||||
'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 () => {
|
||||
const { usdc, pool, weth, users } = testEnv;
|
||||
|
||||
const caller = users[2];
|
||||
|
||||
await weth.connect(caller.signer).mint(await convertToCurrencyDecimals(weth.address, '5'));
|
||||
|
||||
await weth.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
const amountToDeposit = await convertToCurrencyDecimals(weth.address, '5');
|
||||
|
||||
await pool.connect(caller.signer).deposit(weth.address, amountToDeposit, '0');
|
||||
|
||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
||||
|
||||
const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
|
||||
|
||||
await pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
dai.address,
|
||||
ethers.utils.parseEther('500'),
|
||||
'0x10'
|
||||
),
|
||||
TRANSFER_AMOUNT_EXCEEDS_BALANCE
|
||||
).to.be.revertedWith(TRANSFER_AMOUNT_EXCEEDS_BALANCE);
|
||||
usdc.address,
|
||||
flashloanAmount,
|
||||
'0x10',
|
||||
'0'
|
||||
);
|
||||
const {variableDebtTokenAddress} = await pool.getReserveTokensAddresses(usdc.address);
|
||||
|
||||
const usdcDebtToken = await getContract<VariableDebtToken>(eContractid.VariableDebtToken, variableDebtTokenAddress);
|
||||
|
||||
const callerDebt = await usdcDebtToken.balanceOf(caller.address);
|
||||
|
||||
expect(callerDebt.toString()).to.be.equal('500450000', 'Invalid user debt');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user