2020-06-20 23:40:03 +00:00
|
|
|
import {TestEnv, makeSuite} from './helpers/make-suite';
|
|
|
|
import {MOCK_ETH_ADDRESS, APPROVAL_AMOUNT_LENDING_POOL, oneRay} from '../helpers/constants';
|
2020-06-12 13:41:52 +00:00
|
|
|
import {
|
|
|
|
convertToCurrencyDecimals,
|
|
|
|
getMockFlashLoanReceiver,
|
|
|
|
getTokenDistributor,
|
2020-06-20 23:40:03 +00:00
|
|
|
} from '../helpers/contracts-helpers';
|
|
|
|
import {ethers} from 'ethers';
|
|
|
|
import {MockFlashLoanReceiver} from '../types/MockFlashLoanReceiver';
|
|
|
|
import {TokenDistributor} from '../types/TokenDistributor';
|
|
|
|
import {BRE} from '../helpers/misc-utils';
|
|
|
|
import {ProtocolErrors} from '../helpers/types';
|
|
|
|
import BigNumber from 'bignumber.js';
|
2020-06-12 13:41:52 +00:00
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
const {expect} = require('chai');
|
2020-06-12 13:41:52 +00:00
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
2020-06-12 13:41:52 +00:00
|
|
|
let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver;
|
|
|
|
let _tokenDistributor = {} as TokenDistributor;
|
|
|
|
const {
|
|
|
|
INCONSISTENT_PROTOCOL_BALANCE,
|
|
|
|
TOO_SMALL_FLASH_LOAN,
|
|
|
|
NOT_ENOUGH_LIQUIDITY_TO_BORROW,
|
|
|
|
} = ProtocolErrors;
|
|
|
|
|
|
|
|
before(async () => {
|
|
|
|
_mockFlashLoanReceiver = await getMockFlashLoanReceiver();
|
|
|
|
_tokenDistributor = await getTokenDistributor();
|
|
|
|
});
|
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
it('Deposits ETH into the reserve', async () => {
|
2020-06-12 13:41:52 +00:00
|
|
|
const {pool} = testEnv;
|
2020-06-20 23:40:03 +00:00
|
|
|
const amountToDeposit = ethers.utils.parseEther('1');
|
2020-06-12 13:41:52 +00:00
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
await pool.deposit(MOCK_ETH_ADDRESS, amountToDeposit, '0', {
|
2020-06-12 13:41:52 +00:00
|
|
|
value: amountToDeposit,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
it('Takes ETH flashloan, returns the funds correctly', async () => {
|
2020-06-12 13:41:52 +00:00
|
|
|
const {pool, deployer} = testEnv;
|
|
|
|
|
|
|
|
// move funds to the MockFlashLoanReceiver contract to pay the fee
|
|
|
|
await deployer.signer.sendTransaction({
|
2020-06-20 23:40:03 +00:00
|
|
|
value: ethers.utils.parseEther('0.5'),
|
2020-06-12 13:41:52 +00:00
|
|
|
to: _mockFlashLoanReceiver.address,
|
|
|
|
});
|
|
|
|
|
|
|
|
await pool.flashLoan(
|
|
|
|
_mockFlashLoanReceiver.address,
|
|
|
|
MOCK_ETH_ADDRESS,
|
2020-06-20 23:40:03 +00:00
|
|
|
ethers.utils.parseEther('0.8'),
|
|
|
|
'0x10'
|
2020-06-12 13:41:52 +00:00
|
|
|
);
|
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
ethers.utils.parseUnits('10000');
|
2020-06-12 13:41:52 +00:00
|
|
|
|
|
|
|
const reserveData: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
2020-06-20 23:40:03 +00:00
|
|
|
const tokenDistributorBalance = await BRE.ethers.provider.getBalance(_tokenDistributor.address);
|
2020-06-12 13:41:52 +00:00
|
|
|
|
|
|
|
const currentLiquidityRate = reserveData.liquidityRate;
|
|
|
|
const currentLiquidityIndex = reserveData.liquidityIndex;
|
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
const totalLiquidity = new BigNumber(reserveData.availableLiquidity)
|
|
|
|
.plus(reserveData.totalBorrowsStable)
|
|
|
|
.plus(reserveData.totalBorrowsVariable);
|
2020-06-27 02:13:32 +00:00
|
|
|
|
|
|
|
expect(totalLiquidity.toString()).to.be.equal('1000504000000000000');
|
|
|
|
expect(currentLiquidityRate.toString()).to.be.equal('0');
|
|
|
|
expect(currentLiquidityIndex.toString()).to.be.equal('1000504000000000000000000000');
|
|
|
|
expect(tokenDistributorBalance.toString()).to.be.equal('216000000000000');
|
2020-06-12 13:41:52 +00:00
|
|
|
});
|
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
it('Takes an ETH flashloan as big as the available liquidity', async () => {
|
2020-06-12 13:41:52 +00:00
|
|
|
const {pool, deployer} = testEnv;
|
|
|
|
|
|
|
|
// move funds to the MockFlashLoanReceiver contract to pay the fee
|
|
|
|
await deployer.signer.sendTransaction({
|
2020-06-20 23:40:03 +00:00
|
|
|
value: ethers.utils.parseEther('0.5'),
|
2020-06-12 13:41:52 +00:00
|
|
|
to: _mockFlashLoanReceiver.address,
|
|
|
|
});
|
|
|
|
|
|
|
|
const txResult = await pool.flashLoan(
|
|
|
|
_mockFlashLoanReceiver.address,
|
|
|
|
MOCK_ETH_ADDRESS,
|
2020-06-20 23:40:03 +00:00
|
|
|
'1000504000000000000',
|
|
|
|
'0x10'
|
2020-06-12 13:41:52 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
const reserveData: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
|
2020-06-20 23:40:03 +00:00
|
|
|
const tokenDistributorBalance = await BRE.ethers.provider.getBalance(_tokenDistributor.address);
|
2020-06-12 13:41:52 +00:00
|
|
|
|
|
|
|
const currentLiqudityRate = reserveData.liquidityRate;
|
|
|
|
const currentLiquidityIndex = reserveData.liquidityIndex;
|
|
|
|
|
2020-07-13 08:54:08 +00:00
|
|
|
const totalLiquidity = new BigNumber(reserveData.availableLiquidity)
|
|
|
|
.plus(reserveData.totalBorrowsStable)
|
|
|
|
.plus(reserveData.totalBorrowsVariable);
|
2020-06-27 02:13:32 +00:00
|
|
|
|
|
|
|
expect(totalLiquidity.toString()).to.be.equal('1001134317520000000');
|
|
|
|
expect(currentLiqudityRate.toString()).to.be.equal('0');
|
|
|
|
expect(currentLiquidityIndex.toString()).to.be.equal('1001134317520000000000000000');
|
|
|
|
expect(tokenDistributorBalance.toString()).to.be.equal('486136080000000');
|
2020-06-12 13:41:52 +00:00
|
|
|
});
|
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
it('Takes ETH flashloan, does not return the funds (revert expected)', async () => {
|
2020-06-12 13:41:52 +00:00
|
|
|
const {pool, deployer} = testEnv;
|
|
|
|
|
|
|
|
// move funds to the MockFlashLoanReceiver contract to pay the fee
|
|
|
|
await deployer.signer.sendTransaction({
|
2020-06-20 23:40:03 +00:00
|
|
|
value: ethers.utils.parseEther('0.5'),
|
2020-06-12 13:41:52 +00:00
|
|
|
to: _mockFlashLoanReceiver.address,
|
|
|
|
});
|
|
|
|
|
|
|
|
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
pool.flashLoan(
|
|
|
|
_mockFlashLoanReceiver.address,
|
|
|
|
MOCK_ETH_ADDRESS,
|
2020-06-20 23:40:03 +00:00
|
|
|
ethers.utils.parseEther('0.8'),
|
|
|
|
'0x10'
|
2020-06-27 02:13:32 +00:00
|
|
|
)
|
2020-06-12 13:41:52 +00:00
|
|
|
).to.be.revertedWith(INCONSISTENT_PROTOCOL_BALANCE);
|
|
|
|
});
|
|
|
|
|
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-06-12 13:41:52 +00:00
|
|
|
const {pool} = testEnv;
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
pool.flashLoan(
|
|
|
|
_mockFlashLoanReceiver.address,
|
|
|
|
MOCK_ETH_ADDRESS,
|
2020-06-20 23:40:03 +00:00
|
|
|
'1', //1 wei loan
|
|
|
|
'0x10'
|
2020-06-27 02:13:32 +00:00
|
|
|
)
|
2020-06-12 13:41:52 +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-06-12 13:41:52 +00:00
|
|
|
const {pool} = testEnv;
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
pool.flashLoan(
|
|
|
|
_mockFlashLoanReceiver.address,
|
|
|
|
MOCK_ETH_ADDRESS,
|
2020-06-20 23:40:03 +00:00
|
|
|
'1004415000000000000', //slightly higher than the available liquidity
|
|
|
|
'0x10'
|
2020-06-12 13:41:52 +00:00
|
|
|
),
|
|
|
|
NOT_ENOUGH_LIQUIDITY_TO_BORROW
|
|
|
|
).to.be.revertedWith(NOT_ENOUGH_LIQUIDITY_TO_BORROW);
|
|
|
|
});
|
|
|
|
|
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-06-12 13:41:52 +00:00
|
|
|
const {pool, deployer} = testEnv;
|
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
await expect(pool.flashLoan(deployer.address, MOCK_ETH_ADDRESS, '1000000000000000000', '0x10'))
|
|
|
|
.to.be.reverted;
|
2020-06-12 13:41:52 +00:00
|
|
|
});
|
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
it('Deposits DAI into the reserve', async () => {
|
2020-06-27 02:13:32 +00:00
|
|
|
const {dai, pool} = testEnv;
|
2020-06-12 13:41:52 +00:00
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
await dai.mint(await convertToCurrencyDecimals(dai.address, '1000'));
|
2020-06-12 13:41:52 +00:00
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
2020-06-12 13:41:52 +00:00
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000');
|
2020-06-12 13:41:52 +00:00
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
await pool.deposit(dai.address, amountToDeposit, '0');
|
2020-06-12 13:41:52 +00:00
|
|
|
});
|
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
it('Takes out a 500 DAI flashloan, returns the funds correctly', async () => {
|
2020-06-12 13:41:52 +00:00
|
|
|
const {dai, pool, deployer: depositor} = testEnv;
|
|
|
|
|
|
|
|
await _mockFlashLoanReceiver.setFailExecutionTransfer(false);
|
|
|
|
|
|
|
|
await pool.flashLoan(
|
|
|
|
_mockFlashLoanReceiver.address,
|
|
|
|
dai.address,
|
2020-06-20 23:40:03 +00:00
|
|
|
ethers.utils.parseEther('500'),
|
|
|
|
'0x10'
|
2020-06-12 13:41:52 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
const reserveData = await pool.getReserveData(dai.address);
|
2020-06-20 23:40:03 +00:00
|
|
|
const userData = await pool.getUserReserveData(dai.address, depositor.address);
|
2020-06-12 13:41:52 +00:00
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
const totalLiquidity = reserveData.availableLiquidity
|
|
|
|
.add(reserveData.totalBorrowsStable)
|
|
|
|
.add(reserveData.totalBorrowsVariable)
|
|
|
|
.toString();
|
2020-06-12 13:41:52 +00:00
|
|
|
const currentLiqudityRate = reserveData.liquidityRate.toString();
|
|
|
|
const currentLiquidityIndex = reserveData.liquidityIndex.toString();
|
|
|
|
const currentUserBalance = userData.currentATokenBalance.toString();
|
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
const expectedLiquidity = ethers.utils.parseEther('1000.315');
|
2020-06-12 13:41:52 +00:00
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
const tokenDistributorBalance = await dai.balanceOf(_tokenDistributor.address);
|
2020-06-12 13:41:52 +00:00
|
|
|
|
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');
|
2020-06-12 13:41:52 +00:00
|
|
|
expect(currentLiquidityIndex).to.be.equal(
|
2020-06-20 23:40:03 +00:00
|
|
|
new BigNumber('1.000315').multipliedBy(oneRay).toFixed(),
|
|
|
|
'Invalid liquidity index'
|
2020-06-12 13:41:52 +00:00
|
|
|
);
|
2020-06-20 23:40:03 +00:00
|
|
|
expect(currentUserBalance.toString()).to.be.equal(expectedLiquidity, 'Invalid user balance');
|
2020-06-12 13:41:52 +00:00
|
|
|
|
|
|
|
expect(tokenDistributorBalance.toString()).to.be.equal(
|
2020-06-20 23:40:03 +00:00
|
|
|
ethers.utils.parseEther('0.135'),
|
|
|
|
'Invalid token distributor balance'
|
2020-06-12 13:41:52 +00:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-06-20 23:40:03 +00:00
|
|
|
it('Takes out a 500 DAI flashloan, does not return the funds (revert expected)', async () => {
|
2020-06-12 13:41:52 +00:00
|
|
|
const {dai, pool} = testEnv;
|
|
|
|
|
|
|
|
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
pool.flashLoan(
|
|
|
|
_mockFlashLoanReceiver.address,
|
|
|
|
dai.address,
|
2020-06-20 23:40:03 +00:00
|
|
|
ethers.utils.parseEther('500'),
|
|
|
|
'0x10'
|
2020-06-12 13:41:52 +00:00
|
|
|
),
|
|
|
|
INCONSISTENT_PROTOCOL_BALANCE
|
|
|
|
).to.be.revertedWith(INCONSISTENT_PROTOCOL_BALANCE);
|
|
|
|
});
|
|
|
|
});
|