2020-09-07 13:48:46 +00:00
import BigNumber from 'bignumber.js' ;
2020-06-20 23:40:03 +00:00
import { TestEnv , makeSuite } from './helpers/make-suite' ;
2020-08-13 11:06:23 +00:00
import { APPROVAL_AMOUNT_LENDING_POOL , oneRay } from '../helpers/constants' ;
2020-08-25 10:55:05 +00:00
import {
convertToCurrencyDecimals ,
getMockFlashLoanReceiver ,
getContract ,
} from '../helpers/contracts-helpers' ;
2020-06-20 23:40:03 +00:00
import { ethers } from 'ethers' ;
import { MockFlashLoanReceiver } from '../types/MockFlashLoanReceiver' ;
2020-08-25 10:55:05 +00:00
import { ProtocolErrors , eContractid } from '../helpers/types' ;
import { VariableDebtToken } from '../types/VariableDebtToken' ;
2020-09-03 16:14:39 +00:00
import { StableDebtToken } from '../types/StableDebtToken' ;
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 ;
const {
2020-08-23 23:22:14 +00:00
COLLATERAL_BALANCE_IS_0 ,
2020-09-03 08:33:15 +00:00
REQUESTED_AMOUNT_TOO_SMALL ,
2020-09-03 14:29:14 +00:00
TRANSFER_AMOUNT_EXCEEDS_BALANCE ,
2020-09-04 15:10:32 +00:00
INVALID_FLASHLOAN_MODE ,
2020-09-09 19:26:52 +00:00
SAFEERC20_LOWLEVEL_CALL ,
2020-09-14 11:57:40 +00:00
IS_PAUSED ,
2020-10-12 13:19:27 +00:00
INVALID_FLASH_LOAN_EXECUTOR_RETURN ,
2020-06-12 13:41:52 +00:00
} = ProtocolErrors ;
before ( async ( ) = > {
_mockFlashLoanReceiver = await getMockFlashLoanReceiver ( ) ;
} ) ;
2020-09-07 13:48:46 +00:00
it ( 'Deposits WETH into the reserve' , async ( ) = > {
2020-08-13 11:06:23 +00:00
const { pool , weth } = testEnv ;
2020-09-09 10:47:27 +00:00
const userAddress = await pool . signer . getAddress ( ) ;
2020-06-20 23:40:03 +00:00
const amountToDeposit = ethers . utils . parseEther ( '1' ) ;
2020-06-12 13:41:52 +00:00
2020-08-14 17:05:31 +00:00
await weth . mint ( amountToDeposit ) ;
await weth . approve ( pool . address , APPROVAL_AMOUNT_LENDING_POOL ) ;
2020-09-09 10:47:27 +00:00
await pool . deposit ( weth . address , amountToDeposit , userAddress , '0' ) ;
2020-06-12 13:41:52 +00:00
} ) ;
2020-09-03 13:53:18 +00:00
it ( 'Takes WETH flashloan with mode = 0, returns the funds correctly' , async ( ) = > {
2020-10-12 18:07:17 +00:00
const { pool , helpersContract , weth } = testEnv ;
2020-06-12 13:41:52 +00:00
await pool . flashLoan (
_mockFlashLoanReceiver . address ,
2020-08-13 11:06:23 +00:00
weth . address ,
2020-06-20 23:40:03 +00:00
ethers . utils . parseEther ( '0.8' ) ,
2020-09-03 13:17:46 +00:00
0 ,
2020-08-23 23:22:14 +00:00
'0x10' ,
'0'
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
2020-10-12 18:07:17 +00:00
const reserveData = await helpersContract . getReserveData ( weth . address ) ;
2020-06-12 13:41:52 +00:00
const currentLiquidityRate = reserveData . liquidityRate ;
const currentLiquidityIndex = reserveData . liquidityIndex ;
2020-08-19 12:23:41 +00:00
const totalLiquidity = new BigNumber ( reserveData . availableLiquidity . toString ( ) )
2020-09-14 13:13:30 +00:00
. plus ( reserveData . totalStableDebt . toString ( ) )
. plus ( reserveData . totalVariableDebt . toString ( ) ) ;
2020-06-27 02:13:32 +00:00
2020-08-19 15:56:51 +00:00
expect ( totalLiquidity . toString ( ) ) . to . be . equal ( '1000720000000000000' ) ;
2020-06-27 02:13:32 +00:00
expect ( currentLiquidityRate . toString ( ) ) . to . be . equal ( '0' ) ;
2020-08-19 15:56:51 +00:00
expect ( currentLiquidityIndex . toString ( ) ) . to . be . equal ( '1000720000000000000000000000' ) ;
2020-06-12 13:41:52 +00:00
} ) ;
2020-09-03 13:53:18 +00:00
it ( 'Takes an ETH flashloan with mode = 0 as big as the available liquidity' , async ( ) = > {
2020-10-12 18:07:17 +00:00
const { pool , helpersContract , weth } = testEnv ;
2020-06-12 13:41:52 +00:00
2020-10-12 18:07:17 +00:00
const reserveDataBefore = await helpersContract . getReserveData ( weth . address ) ;
2020-06-12 13:41:52 +00:00
const txResult = await pool . flashLoan (
_mockFlashLoanReceiver . address ,
2020-08-13 11:06:23 +00:00
weth . address ,
2020-08-20 10:21:42 +00:00
'1000720000000000000' ,
2020-09-03 13:17:46 +00:00
0 ,
2020-08-23 23:22:14 +00:00
'0x10' ,
'0'
2020-06-12 13:41:52 +00:00
) ;
2020-10-12 18:07:17 +00:00
const reserveData = await helpersContract . getReserveData ( weth . address ) ;
2020-06-12 13:41:52 +00:00
const currentLiqudityRate = reserveData . liquidityRate ;
const currentLiquidityIndex = reserveData . liquidityIndex ;
2020-08-19 12:23:41 +00:00
const totalLiquidity = new BigNumber ( reserveData . availableLiquidity . toString ( ) )
2020-09-14 13:13:30 +00:00
. plus ( reserveData . totalStableDebt . toString ( ) )
. plus ( reserveData . totalVariableDebt . 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-06-12 13:41:52 +00:00
} ) ;
2020-09-03 13:53:18 +00:00
it ( 'Takes WETH flashloan, does not return the funds with mode = 0. (revert expected)' , async ( ) = > {
2020-08-25 10:55:05 +00:00
const { pool , weth , users } = testEnv ;
2020-08-23 23:22:14 +00:00
const caller = users [ 1 ] ;
await _mockFlashLoanReceiver . setFailExecutionTransfer ( true ) ;
2020-06-12 13:41:52 +00:00
2020-08-23 23:22:14 +00:00
await expect (
pool
. connect ( caller . signer )
. flashLoan (
_mockFlashLoanReceiver . address ,
weth . address ,
ethers . utils . parseEther ( '0.8' ) ,
2020-09-03 13:41:36 +00:00
0 ,
2020-08-23 23:22:14 +00:00
'0x10' ,
'0'
)
2020-10-15 15:20:22 +00:00
) . to . be . revertedWith ( SAFEERC20_LOWLEVEL_CALL ) ;
2020-08-23 23:22:14 +00:00
} ) ;
2020-06-12 13:41:52 +00:00
2020-10-07 14:20:32 +00:00
it ( 'Takes WETH flashloan, simulating a receiver as EOA (revert expected)' , async ( ) = > {
const { pool , weth , users } = testEnv ;
const caller = users [ 1 ] ;
await _mockFlashLoanReceiver . setFailExecutionTransfer ( true ) ;
2020-10-12 13:19:27 +00:00
await _mockFlashLoanReceiver . setSimulateEOA ( true ) ;
2020-10-07 14:20:32 +00:00
await expect (
pool
. connect ( caller . signer )
. flashLoan (
_mockFlashLoanReceiver . address ,
weth . address ,
ethers . utils . parseEther ( '0.8' ) ,
0 ,
'0x10' ,
'0'
)
) . to . be . revertedWith ( INVALID_FLASH_LOAN_EXECUTOR_RETURN ) ;
} ) ;
2020-09-03 14:29:14 +00:00
it ( 'Takes a WETH flashloan with an invalid mode. (revert expected)' , async ( ) = > {
const { pool , weth , users } = testEnv ;
const caller = users [ 1 ] ;
2020-10-12 13:19:27 +00:00
await _mockFlashLoanReceiver . setSimulateEOA ( false ) ;
2020-06-12 13:41:52 +00:00
await _mockFlashLoanReceiver . setFailExecutionTransfer ( true ) ;
await expect (
2020-09-03 14:29:14 +00:00
pool
. connect ( caller . signer )
. flashLoan (
_mockFlashLoanReceiver . address ,
weth . address ,
ethers . utils . parseEther ( '0.8' ) ,
4 ,
'0x10' ,
'0'
)
) . to . be . revertedWith ( INVALID_FLASHLOAN_MODE ) ;
} ) ;
2020-09-03 13:53:18 +00:00
it ( 'Caller deposits 1000 DAI as collateral, Takes WETH flashloan with mode = 2, does not return the funds. A variable loan for caller is created' , async ( ) = > {
2020-10-12 18:07:17 +00:00
const { dai , pool , weth , users , helpersContract } = testEnv ;
2020-08-23 23:22:14 +00:00
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-06-12 13:41:52 +00:00
2020-08-23 23:22:14 +00:00
const amountToDeposit = await convertToCurrencyDecimals ( dai . address , '1000' ) ;
2020-09-09 10:47:27 +00:00
await pool . connect ( caller . signer ) . deposit ( dai . address , amountToDeposit , caller . address , '0' ) ;
2020-06-12 13:41:52 +00:00
await _mockFlashLoanReceiver . setFailExecutionTransfer ( true ) ;
2020-08-23 23:22:14 +00:00
await pool
. connect ( caller . signer )
. flashLoan (
2020-06-12 13:41:52 +00:00
_mockFlashLoanReceiver . address ,
2020-08-13 11:06:23 +00:00
weth . address ,
2020-06-20 23:40:03 +00:00
ethers . utils . parseEther ( '0.8' ) ,
2020-08-25 10:55:05 +00:00
2 ,
2020-08-23 23:22:14 +00:00
'0x10' ,
'0'
) ;
2020-10-12 18:07:17 +00:00
const { variableDebtTokenAddress } = await helpersContract . getReserveTokensAddresses (
weth . address
) ;
2020-08-23 23:22:14 +00:00
2020-08-25 10:55:05 +00:00
const wethDebtToken = await getContract < VariableDebtToken > (
eContractid . VariableDebtToken ,
variableDebtTokenAddress
) ;
2020-08-23 23:22:14 +00:00
const callerDebt = await wethDebtToken . balanceOf ( caller . address ) ;
expect ( callerDebt . toString ( ) ) . to . be . equal ( '800720000000000000' , 'Invalid user debt' ) ;
2020-06-12 13:41:52 +00:00
} ) ;
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-13 11:06:23 +00:00
const { pool , weth } = testEnv ;
2020-06-12 13:41:52 +00:00
await expect (
pool . flashLoan (
_mockFlashLoanReceiver . address ,
2020-08-13 11:06:23 +00:00
weth . address ,
2020-06-20 23:40:03 +00:00
'1' , //1 wei loan
2020-08-25 10:55:05 +00:00
2 ,
2020-08-23 23:22:14 +00:00
'0x10' ,
'0'
2020-06-27 02:13:32 +00:00
)
2020-09-03 08:33:15 +00:00
) . to . be . revertedWith ( REQUESTED_AMOUNT_TOO_SMALL ) ;
2020-06-12 13:41:52 +00:00
} ) ;
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-13 11:06:23 +00:00
const { pool , weth } = testEnv ;
2020-06-12 13:41:52 +00:00
await expect (
pool . flashLoan (
_mockFlashLoanReceiver . address ,
2020-08-13 11:06:23 +00:00
weth . address ,
2020-06-20 23:40:03 +00:00
'1004415000000000000' , //slightly higher than the available liquidity
2020-08-25 10:55:05 +00:00
2 ,
2020-08-23 23:22:14 +00:00
'0x10' ,
'0'
2020-06-12 13:41:52 +00:00
) ,
2020-08-23 16:38:34 +00:00
TRANSFER_AMOUNT_EXCEEDS_BALANCE
2020-09-04 15:10:32 +00:00
) . to . be . revertedWith ( SAFEERC20_LOWLEVEL_CALL ) ;
2020-06-12 13:41:52 +00:00
} ) ;
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-13 11:06:23 +00:00
const { pool , deployer , weth } = testEnv ;
2020-06-12 13:41:52 +00:00
2020-08-25 10:55:05 +00:00
await expect (
pool . flashLoan ( deployer . address , weth . address , '1000000000000000000' , 2 , '0x10' , '0' )
) . to . be . reverted ;
2020-06-12 13:41:52 +00:00
} ) ;
2020-08-23 23:22:14 +00:00
it ( 'Deposits USDC into the reserve' , async ( ) = > {
2020-08-25 10:55:05 +00:00
const { usdc , pool } = testEnv ;
2020-09-09 10:47:27 +00:00
const userAddress = await pool . signer . getAddress ( ) ;
2020-06-12 13:41:52 +00:00
2020-08-23 23:22:14 +00:00
await usdc . mint ( await convertToCurrencyDecimals ( usdc . address , '1000' ) ) ;
2020-06-12 13:41:52 +00:00
2020-08-23 23:22:14 +00:00
await usdc . approve ( pool . address , APPROVAL_AMOUNT_LENDING_POOL ) ;
2020-06-12 13:41:52 +00:00
2020-08-23 23:22:14 +00:00
const amountToDeposit = await convertToCurrencyDecimals ( usdc . address , '1000' ) ;
2020-06-12 13:41:52 +00:00
2020-09-09 10:47:27 +00:00
await pool . deposit ( usdc . address , amountToDeposit , userAddress , '0' ) ;
2020-06-12 13:41:52 +00:00
} ) ;
2020-08-23 23:22:14 +00:00
it ( 'Takes out a 500 USDC flashloan, returns the funds correctly' , async ( ) = > {
2020-10-12 18:07:17 +00:00
const { usdc , pool , helpersContract , deployer : depositor } = testEnv ;
2020-06-12 13:41:52 +00:00
await _mockFlashLoanReceiver . setFailExecutionTransfer ( false ) ;
2020-08-23 23:22:14 +00:00
const flashloanAmount = await convertToCurrencyDecimals ( usdc . address , '500' ) ;
2020-06-12 13:41:52 +00:00
await pool . flashLoan (
_mockFlashLoanReceiver . address ,
2020-08-23 23:22:14 +00:00
usdc . address ,
flashloanAmount ,
2020-09-03 13:17:46 +00:00
0 ,
2020-08-23 23:22:14 +00:00
'0x10' ,
'0'
2020-06-12 13:41:52 +00:00
) ;
2020-10-12 18:07:17 +00:00
const reserveData = await helpersContract . getReserveData ( usdc . address ) ;
const userData = await helpersContract . getUserReserveData ( usdc . address , depositor . address ) ;
2020-06-12 13:41:52 +00:00
2020-06-20 23:40:03 +00:00
const totalLiquidity = reserveData . availableLiquidity
2020-09-14 13:13:30 +00:00
. add ( reserveData . totalStableDebt )
. add ( reserveData . totalVariableDebt )
2020-06-20 23:40:03 +00:00
. 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-08-25 10:55:05 +00:00
const expectedLiquidity = await convertToCurrencyDecimals ( usdc . address , '1000.450' ) ;
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-08-19 15:56:51 +00:00
new BigNumber ( '1.00045' ) . multipliedBy ( oneRay ) . toFixed ( ) ,
2020-06-20 23:40:03 +00:00
'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
} ) ;
2020-09-03 16:14:39 +00:00
it ( 'Takes out a 500 USDC flashloan with mode = 0, does not return the funds. (revert expected)' , async ( ) = > {
2020-08-25 10:55:05 +00:00
const { usdc , pool , users } = testEnv ;
2020-08-23 23:22:14 +00:00
const caller = users [ 2 ] ;
const flashloanAmount = await convertToCurrencyDecimals ( usdc . address , '500' ) ;
2020-06-12 13:41:52 +00:00
await _mockFlashLoanReceiver . setFailExecutionTransfer ( true ) ;
await expect (
2020-08-23 23:22:14 +00:00
pool
. connect ( caller . signer )
2020-08-25 10:55:05 +00:00
. flashLoan ( _mockFlashLoanReceiver . address , usdc . address , flashloanAmount , 2 , '0x10' , '0' )
2020-08-23 23:22:14 +00:00
) . to . be . revertedWith ( COLLATERAL_BALANCE_IS_0 ) ;
} ) ;
2020-09-03 16:14:39 +00:00
it ( 'Caller deposits 5 WETH as collateral, Takes a USDC flashloan with mode = 2, does not return the funds. A loan for caller is created' , async ( ) = > {
2020-10-12 18:07:17 +00:00
const { usdc , pool , weth , users , helpersContract } = testEnv ;
2020-08-23 23:22:14 +00:00
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' ) ;
2020-09-09 10:47:27 +00:00
await pool . connect ( caller . signer ) . deposit ( weth . address , amountToDeposit , caller . address , '0' ) ;
2020-08-23 23:22:14 +00:00
await _mockFlashLoanReceiver . setFailExecutionTransfer ( true ) ;
2020-08-25 10:55:05 +00:00
2020-08-23 23:22:14 +00:00
const flashloanAmount = await convertToCurrencyDecimals ( usdc . address , '500' ) ;
await pool
. connect ( caller . signer )
2020-08-25 10:55:05 +00:00
. flashLoan ( _mockFlashLoanReceiver . address , usdc . address , flashloanAmount , 2 , '0x10' , '0' ) ;
2020-10-12 18:07:17 +00:00
const { variableDebtTokenAddress } = await helpersContract . getReserveTokensAddresses (
usdc . address
) ;
2020-08-23 23:22:14 +00:00
2020-08-25 10:55:05 +00:00
const usdcDebtToken = await getContract < VariableDebtToken > (
eContractid . VariableDebtToken ,
variableDebtTokenAddress
) ;
2020-08-23 23:22:14 +00:00
const callerDebt = await usdcDebtToken . balanceOf ( caller . address ) ;
expect ( callerDebt . toString ( ) ) . to . be . equal ( '500450000' , 'Invalid user debt' ) ;
2020-08-25 12:50:07 +00:00
} ) ;
2020-09-03 16:14:39 +00:00
it ( 'Caller deposits 1000 DAI as collateral, Takes a WETH flashloan with mode = 0, does not approve the transfer of the funds' , async ( ) = > {
2020-08-25 12:50:07 +00:00
const { dai , pool , weth , users } = testEnv ;
const caller = users [ 3 ] ;
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' ) ;
2020-09-09 10:47:27 +00:00
await pool . connect ( caller . signer ) . deposit ( dai . address , amountToDeposit , caller . address , '0' ) ;
2020-08-25 12:50:07 +00:00
const flashAmount = ethers . utils . parseEther ( '0.8' ) ;
await _mockFlashLoanReceiver . setFailExecutionTransfer ( false ) ;
await _mockFlashLoanReceiver . setAmountToApprove ( flashAmount . div ( 2 ) ) ;
await expect (
pool
. connect ( caller . signer )
. flashLoan ( _mockFlashLoanReceiver . address , weth . address , flashAmount , 0 , '0x10' , '0' )
2020-10-15 15:20:22 +00:00
) . to . be . revertedWith ( SAFEERC20_LOWLEVEL_CALL ) ;
2020-08-25 12:50:07 +00:00
} ) ;
2020-09-03 16:14:39 +00:00
it ( 'Caller takes a WETH flashloan with mode = 1' , async ( ) = > {
2020-10-12 18:07:17 +00:00
const { dai , pool , weth , users , helpersContract } = testEnv ;
2020-09-03 16:14:39 +00:00
const caller = users [ 3 ] ;
const flashAmount = ethers . utils . parseEther ( '0.8' ) ;
await _mockFlashLoanReceiver . setFailExecutionTransfer ( true ) ;
await pool
2020-09-09 19:26:52 +00:00
. connect ( caller . signer )
. flashLoan ( _mockFlashLoanReceiver . address , weth . address , flashAmount , 1 , '0x10' , '0' ) ;
2020-09-03 16:14:39 +00:00
2020-10-12 18:07:17 +00:00
const { stableDebtTokenAddress } = await helpersContract . getReserveTokensAddresses ( weth . address ) ;
2020-09-03 16:14:39 +00:00
const wethDebtToken = await getContract < StableDebtToken > (
eContractid . VariableDebtToken ,
stableDebtTokenAddress
) ;
const callerDebt = await wethDebtToken . balanceOf ( caller . address ) ;
expect ( callerDebt . toString ( ) ) . to . be . equal ( '800720000000000000' , 'Invalid user debt' ) ;
2020-06-12 13:41:52 +00:00
} ) ;
} ) ;