aave-protocol-v2/test/flashloan.spec.ts

280 lines
9.6 KiB
TypeScript
Raw Normal View History

2020-08-23 23:22:14 +00:00
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';
2020-06-20 23:40:03 +00:00
import BigNumber from 'bignumber.js';
2020-08-23 23:22:14 +00:00
import { VariableDebtToken } from '../types/VariableDebtToken';
2020-08-23 23:22:14 +00:00
const { expect } = require('chai');
2020-06-20 23:40:03 +00:00
makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver;
const {
TRANSFER_AMOUNT_EXCEEDS_BALANCE,
TOO_SMALL_FLASH_LOAN,
2020-08-23 23:22:14 +00:00
COLLATERAL_BALANCE_IS_0,
} = ProtocolErrors;
before(async () => {
_mockFlashLoanReceiver = await getMockFlashLoanReceiver();
});
2020-06-20 23:40:03 +00:00
it('Deposits ETH into the reserve', async () => {
2020-08-23 23:22:14 +00:00
const { pool, weth } = testEnv;
2020-06-20 23:40:03 +00:00
const amountToDeposit = ethers.utils.parseEther('1');
2020-08-14 17:05:31 +00:00
await weth.mint(amountToDeposit);
await weth.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
await pool.deposit(weth.address, amountToDeposit, '0');
});
2020-06-20 23:40:03 +00:00
it('Takes ETH flashloan, returns the funds correctly', async () => {
2020-08-23 23:22:14 +00:00
const { pool, deployer, weth } = testEnv;
await pool.flashLoan(
_mockFlashLoanReceiver.address,
weth.address,
2020-06-20 23:40:03 +00:00
ethers.utils.parseEther('0.8'),
2020-08-23 23:22:14 +00:00
'0x10',
'0'
);
2020-06-20 23:40:03 +00:00
ethers.utils.parseUnits('10000');
2020-08-19 12:23:41 +00:00
const reserveData = await pool.getReserveData(weth.address);
const currentLiquidityRate = reserveData.liquidityRate;
const currentLiquidityIndex = reserveData.liquidityIndex;
2020-08-19 12:23:41 +00:00
const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString())
.plus(reserveData.totalBorrowsStable.toString())
.plus(reserveData.totalBorrowsVariable.toString());
2020-06-27 02:13:32 +00:00
expect(totalLiquidity.toString()).to.be.equal('1000720000000000000');
2020-06-27 02:13:32 +00:00
expect(currentLiquidityRate.toString()).to.be.equal('0');
expect(currentLiquidityIndex.toString()).to.be.equal('1000720000000000000000000000');
});
2020-06-20 23:40:03 +00:00
it('Takes an ETH flashloan as big as the available liquidity', async () => {
2020-08-23 23:22:14 +00:00
const { pool, weth } = testEnv;
2020-08-20 10:21:42 +00:00
const reserveDataBefore = await pool.getReserveData(weth.address);
const txResult = await pool.flashLoan(
_mockFlashLoanReceiver.address,
weth.address,
2020-08-20 10:21:42 +00:00
'1000720000000000000',
2020-08-23 23:22:14 +00:00
'0x10',
'0'
);
2020-08-19 12:23:41 +00:00
const reserveData = await pool.getReserveData(weth.address);
const currentLiqudityRate = reserveData.liquidityRate;
const currentLiquidityIndex = reserveData.liquidityIndex;
2020-08-19 12:23:41 +00:00
const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString())
.plus(reserveData.totalBorrowsStable.toString())
.plus(reserveData.totalBorrowsVariable.toString());
2020-06-27 02:13:32 +00:00
2020-08-20 10:21:42 +00:00
expect(totalLiquidity.toString()).to.be.equal('1001620648000000000');
2020-06-27 02:13:32 +00:00
expect(currentLiqudityRate.toString()).to.be.equal('0');
2020-08-20 10:21:42 +00:00
expect(currentLiquidityIndex.toString()).to.be.equal('1001620648000000000000000000');
});
2020-08-23 23:22:14 +00:00
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
.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);
2020-08-23 23:22:14 +00:00
const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000');
await pool.connect(caller.signer).deposit(dai.address, amountToDeposit, '0');
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
2020-08-23 23:22:14 +00:00
await pool
.connect(caller.signer)
.flashLoan(
_mockFlashLoanReceiver.address,
weth.address,
2020-06-20 23:40:03 +00:00
ethers.utils.parseEther('0.8'),
2020-08-23 23:22:14 +00:00
'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');
});
2020-06-20 23:40:03 +00:00
it('tries to take a very small flashloan, which would result in 0 fees (revert expected)', async () => {
2020-08-23 23:22:14 +00:00
const { pool, weth } = testEnv;
await expect(
pool.flashLoan(
_mockFlashLoanReceiver.address,
weth.address,
2020-06-20 23:40:03 +00:00
'1', //1 wei loan
2020-08-23 23:22:14 +00:00
'0x10',
'0'
2020-06-27 02:13:32 +00:00
)
).to.be.revertedWith(TOO_SMALL_FLASH_LOAN);
});
2020-06-20 23:40:03 +00:00
it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => {
2020-08-23 23:22:14 +00:00
const { pool, weth } = testEnv;
await expect(
pool.flashLoan(
_mockFlashLoanReceiver.address,
weth.address,
2020-06-20 23:40:03 +00:00
'1004415000000000000', //slightly higher than the available liquidity
2020-08-23 23:22:14 +00:00
'0x10',
'0'
),
TRANSFER_AMOUNT_EXCEEDS_BALANCE
).to.be.revertedWith(TRANSFER_AMOUNT_EXCEEDS_BALANCE);
});
2020-06-20 23:40:03 +00:00
it('tries to take a flashloan using a non contract address as receiver (revert expected)', async () => {
2020-08-23 23:22:14 +00:00
const { pool, deployer, weth } = testEnv;
2020-08-23 23:22:14 +00:00
await expect(pool.flashLoan(deployer.address, weth.address, '1000000000000000000', '0x10', '0'))
.to.be.reverted;
});
2020-08-23 23:22:14 +00:00
it('Deposits USDC into the reserve', async () => {
const { usdc, pool } = testEnv;
2020-08-23 23:22:14 +00:00
await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
2020-08-23 23:22:14 +00:00
await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
2020-08-23 23:22:14 +00:00
const amountToDeposit = await convertToCurrencyDecimals(usdc.address, '1000');
2020-08-23 23:22:14 +00:00
await pool.deposit(usdc.address, amountToDeposit, '0');
});
2020-08-23 23:22:14 +00:00
it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => {
const { usdc, pool, deployer: depositor } = testEnv;
await _mockFlashLoanReceiver.setFailExecutionTransfer(false);
2020-08-23 23:22:14 +00:00
const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
await pool.flashLoan(
_mockFlashLoanReceiver.address,
2020-08-23 23:22:14 +00:00
usdc.address,
flashloanAmount,
'0x10',
'0'
);
2020-08-23 23:22:14 +00:00
const reserveData = await pool.getReserveData(usdc.address);
const userData = await pool.getUserReserveData(usdc.address, depositor.address);
2020-06-20 23:40:03 +00:00
const totalLiquidity = reserveData.availableLiquidity
.add(reserveData.totalBorrowsStable)
.add(reserveData.totalBorrowsVariable)
.toString();
const currentLiqudityRate = reserveData.liquidityRate.toString();
const currentLiquidityIndex = reserveData.liquidityIndex.toString();
const currentUserBalance = userData.currentATokenBalance.toString();
2020-08-23 23:22:14 +00:00
const expectedLiquidity = await convertToCurrencyDecimals(usdc.address,'1000.450');
2020-06-20 23:40:03 +00:00
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(),
2020-06-20 23:40:03 +00:00
'Invalid liquidity index'
);
2020-06-20 23:40:03 +00:00
expect(currentUserBalance.toString()).to.be.equal(expectedLiquidity, 'Invalid user balance');
});
2020-08-23 23:22:14 +00:00
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(
2020-08-23 23:22:14 +00:00
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,
2020-08-23 23:22:14 +00:00
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');
});
});