2020-09-08 18:06:28 +00:00
import { TestEnv , makeSuite } from './helpers/make-suite' ;
import { APPROVAL_AMOUNT_LENDING_POOL , oneEther } from '../helpers/constants' ;
import { ethers } from 'ethers' ;
import BigNumber from 'bignumber.js' ;
import {
calcExpectedVariableDebtTokenBalance ,
calcExpectedStableDebtTokenBalance ,
} from './helpers/utils/calculations' ;
import { getContractsData } from './helpers/actions' ;
import { waitForTx } from './__setup.spec' ;
2020-09-09 19:24:20 +00:00
import { timeLatest , BRE , increaseTime } from '../helpers/misc-utils' ;
2020-09-09 19:26:52 +00:00
import { ProtocolErrors } from '../helpers/types' ;
2020-09-08 18:06:28 +00:00
import { convertToCurrencyDecimals } from '../helpers/contracts-helpers' ;
2020-09-09 19:26:52 +00:00
import { expectRepayWithCollateralEvent } from './repay-with-collateral.spec' ;
2020-09-08 18:06:28 +00:00
const { expect } = require ( 'chai' ) ;
const { parseUnits , parseEther } = ethers . utils ;
makeSuite ( 'LendingPool. repayWithCollateral() with liquidator' , ( testEnv : TestEnv ) = > {
2020-09-14 11:57:40 +00:00
const { INVALID_HF , COLLATERAL_CANNOT_BE_LIQUIDATED , IS_PAUSED } = ProtocolErrors ;
2020-09-08 18:06:28 +00:00
it ( 'User 1 provides some liquidity for others to borrow' , async ( ) = > {
2020-09-10 19:40:26 +00:00
const { pool , weth , dai , usdc , deployer } = testEnv ;
2020-09-08 18:06:28 +00:00
await weth . mint ( parseEther ( '200' ) ) ;
await weth . approve ( pool . address , parseEther ( '200' ) ) ;
2020-09-10 19:40:26 +00:00
await pool . deposit ( weth . address , parseEther ( '200' ) , deployer . address , 0 ) ;
2020-09-08 18:06:28 +00:00
await dai . mint ( parseEther ( '20000' ) ) ;
await dai . approve ( pool . address , parseEther ( '20000' ) ) ;
2020-09-10 19:40:26 +00:00
await pool . deposit ( dai . address , parseEther ( '20000' ) , deployer . address , 0 ) ;
2020-09-08 18:06:28 +00:00
await usdc . mint ( parseEther ( '20000' ) ) ;
await usdc . approve ( pool . address , parseEther ( '20000' ) ) ;
2020-09-10 19:40:26 +00:00
await pool . deposit ( usdc . address , parseEther ( '20000' ) , deployer . address , 0 ) ;
2020-09-08 18:06:28 +00:00
} ) ;
it ( 'User 5 liquidate User 3 collateral, all his variable debt and part of the stable' , async ( ) = > {
const { pool , weth , usdc , users , mockSwapAdapter , oracle } = testEnv ;
const user = users [ 2 ] ;
const liquidator = users [ 4 ] ;
const amountToDeposit = parseEther ( '20' ) ;
const amountToBorrow = parseUnits ( '40' , 6 ) ;
await weth . connect ( user . signer ) . mint ( amountToDeposit ) ;
await weth . connect ( user . signer ) . approve ( pool . address , amountToDeposit ) ;
2020-09-10 19:40:26 +00:00
await pool . connect ( user . signer ) . deposit ( weth . address , amountToDeposit , user . address , '0' ) ;
2020-09-08 18:06:28 +00:00
const usdcPrice = await oracle . getAssetPrice ( usdc . address ) ;
2020-09-11 09:25:42 +00:00
await pool . connect ( user . signer ) . borrow ( usdc . address , amountToBorrow , 2 , 0 , user . address ) ;
2020-09-08 18:06:28 +00:00
2020-09-11 09:25:42 +00:00
await pool . connect ( user . signer ) . borrow ( usdc . address , amountToBorrow , 1 , 0 , user . address ) ;
2020-09-08 18:06:28 +00:00
const { userData : wethUserDataBefore } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const {
reserveData : usdcReserveDataBefore ,
userData : usdcUserDataBefore ,
} = await getContractsData ( usdc . address , user . address , testEnv ) ;
// Set HF below 1
await oracle . setAssetPrice (
usdc . address ,
new BigNumber ( usdcPrice . toString ( ) ) . multipliedBy ( 60 ) . toFixed ( 0 )
) ;
const userGlobalDataPrior = await pool . getUserAccountData ( user . address ) ;
expect ( userGlobalDataPrior . healthFactor . toString ( ) ) . to . be . bignumber . lt ( oneEther , INVALID_HF ) ;
const amountToRepay = parseUnits ( '80' , 6 ) ;
await mockSwapAdapter . setAmountToReturn ( amountToRepay ) ;
const txReceipt = await waitForTx (
await pool
. connect ( liquidator . signer )
. repayWithCollateral (
weth . address ,
usdc . address ,
user . address ,
amountToRepay ,
mockSwapAdapter . address ,
'0x'
)
) ;
const repayWithCollateralTimestamp = await timeLatest ( ) ;
const { userData : wethUserDataAfter } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const { userData : usdcUserDataAfter } = await getContractsData (
usdc . address ,
user . address ,
testEnv
) ;
const collateralPrice = await oracle . getAssetPrice ( weth . address ) ;
const principalPrice = await oracle . getAssetPrice ( usdc . address ) ;
const collateralDecimals = (
await pool . getReserveConfigurationData ( weth . address )
) . decimals . toString ( ) ;
const principalDecimals = (
await pool . getReserveConfigurationData ( usdc . address )
) . decimals . toString ( ) ;
const expectedCollateralLiquidated = new BigNumber ( principalPrice . toString ( ) )
. times ( new BigNumber ( amountToRepay . toString ( ) ) . times ( 105 ) )
. times ( new BigNumber ( 10 ) . pow ( collateralDecimals ) )
. div (
new BigNumber ( collateralPrice . toString ( ) ) . times ( new BigNumber ( 10 ) . pow ( principalDecimals ) )
)
. div ( 100 )
. decimalPlaces ( 0 , BigNumber . ROUND_DOWN ) ;
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance (
usdcReserveDataBefore ,
usdcUserDataBefore ,
new BigNumber ( repayWithCollateralTimestamp )
) . minus ( usdcUserDataBefore . currentVariableDebt ) ;
const expectedStableDebtIncrease = calcExpectedStableDebtTokenBalance (
usdcUserDataBefore ,
new BigNumber ( repayWithCollateralTimestamp )
) . minus ( usdcUserDataBefore . currentStableDebt ) ;
expect ( usdcUserDataAfter . currentVariableDebt ) . to . be . bignumber . equal (
new BigNumber ( usdcUserDataBefore . currentVariableDebt )
. minus ( amountToRepay . toString ( ) )
. plus ( expectedVariableDebtIncrease )
. gte ( 0 )
? new BigNumber ( usdcUserDataBefore . currentVariableDebt )
. minus ( amountToRepay . toString ( ) )
. plus ( expectedVariableDebtIncrease )
. toString ( )
: '0' ,
'INVALID_VARIABLE_DEBT_POSITION'
) ;
const stableDebtRepaid = new BigNumber ( usdcUserDataBefore . currentVariableDebt )
. minus ( amountToRepay . toString ( ) )
. plus ( expectedVariableDebtIncrease )
. abs ( ) ;
expect ( usdcUserDataAfter . currentStableDebt ) . to . be . bignumber . equal (
new BigNumber ( usdcUserDataBefore . currentStableDebt )
. minus ( stableDebtRepaid )
. plus ( expectedStableDebtIncrease )
. gte ( 0 )
? new BigNumber ( usdcUserDataBefore . currentStableDebt )
. minus ( stableDebtRepaid )
. plus ( expectedStableDebtIncrease )
. toString ( )
: '0' ,
'INVALID_STABLE_DEBT_POSITION'
) ;
expect ( wethUserDataAfter . currentATokenBalance ) . to . be . bignumber . equal (
new BigNumber ( wethUserDataBefore . currentATokenBalance ) . minus (
expectedCollateralLiquidated . toString ( )
) ,
'INVALID_COLLATERAL_POSITION'
) ;
const eventsEmitted = txReceipt . events || [ ] ;
expectRepayWithCollateralEvent (
eventsEmitted ,
pool . address ,
weth . address ,
usdc . address ,
user . address
) ;
// Resets USDC Price
await oracle . setAssetPrice ( usdc . address , usdcPrice ) ;
} ) ;
it ( 'User 3 deposits WETH and borrows USDC at Variable' , async ( ) = > {
const { pool , weth , usdc , users , oracle } = testEnv ;
const user = users [ 2 ] ;
const amountToDeposit = parseEther ( '10' ) ;
await weth . connect ( user . signer ) . mint ( amountToDeposit ) ;
await weth . connect ( user . signer ) . approve ( pool . address , APPROVAL_AMOUNT_LENDING_POOL ) ;
2020-09-10 19:40:26 +00:00
await pool . connect ( user . signer ) . deposit ( weth . address , amountToDeposit , user . address , '0' ) ;
2020-09-08 18:06:28 +00:00
const userGlobalData = await pool . getUserAccountData ( user . address ) ;
const usdcPrice = await oracle . getAssetPrice ( usdc . address ) ;
const amountUSDCToBorrow = await convertToCurrencyDecimals (
usdc . address ,
new BigNumber ( userGlobalData . availableBorrowsETH . toString ( ) )
. div ( usdcPrice . toString ( ) )
. multipliedBy ( 0.95 )
. toFixed ( 0 )
) ;
2020-09-11 09:25:42 +00:00
await pool . connect ( user . signer ) . borrow ( usdc . address , amountUSDCToBorrow , 2 , 0 , user . address ) ;
2020-09-08 18:06:28 +00:00
} ) ;
it ( 'User 5 liquidates half the USDC loan of User 3 by swapping his WETH collateral' , async ( ) = > {
const { pool , weth , usdc , users , mockSwapAdapter , oracle } = testEnv ;
const user = users [ 2 ] ;
const liquidator = users [ 4 ] ;
// Sets USDC Price higher to decrease health factor below 1
const usdcPrice = await oracle . getAssetPrice ( usdc . address ) ;
await oracle . setAssetPrice (
usdc . address ,
new BigNumber ( usdcPrice . toString ( ) ) . multipliedBy ( 1.15 ) . toFixed ( 0 )
) ;
const userGlobalData = await pool . getUserAccountData ( user . address ) ;
expect ( userGlobalData . healthFactor . toString ( ) ) . to . be . bignumber . lt ( oneEther , INVALID_HF ) ;
const { userData : wethUserDataBefore } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const {
reserveData : usdcReserveDataBefore ,
userData : usdcUserDataBefore ,
} = await getContractsData ( usdc . address , user . address , testEnv ) ;
const amountToRepay = usdcReserveDataBefore . totalBorrowsVariable . dividedBy ( 2 ) . toFixed ( 0 ) ;
await mockSwapAdapter . setAmountToReturn ( amountToRepay ) ;
await waitForTx (
await pool
. connect ( liquidator . signer )
. repayWithCollateral (
weth . address ,
usdc . address ,
user . address ,
amountToRepay ,
mockSwapAdapter . address ,
'0x'
)
) ;
const repayWithCollateralTimestamp = await timeLatest ( ) ;
const { userData : wethUserDataAfter } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const { userData : usdcUserDataAfter } = await getContractsData (
usdc . address ,
user . address ,
testEnv
) ;
const collateralPrice = await oracle . getAssetPrice ( weth . address ) ;
const principalPrice = await oracle . getAssetPrice ( usdc . address ) ;
const collateralDecimals = (
await pool . getReserveConfigurationData ( weth . address )
) . decimals . toString ( ) ;
const principalDecimals = (
await pool . getReserveConfigurationData ( usdc . address )
) . decimals . toString ( ) ;
const expectedCollateralLiquidated = new BigNumber ( principalPrice . toString ( ) )
. times ( new BigNumber ( amountToRepay . toString ( ) ) . times ( 105 ) )
. times ( new BigNumber ( 10 ) . pow ( collateralDecimals ) )
. div (
new BigNumber ( collateralPrice . toString ( ) ) . times ( new BigNumber ( 10 ) . pow ( principalDecimals ) )
)
. div ( 100 )
. decimalPlaces ( 0 , BigNumber . ROUND_DOWN ) ;
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance (
usdcReserveDataBefore ,
usdcUserDataBefore ,
new BigNumber ( repayWithCollateralTimestamp )
) . minus ( usdcUserDataBefore . currentVariableDebt ) ;
expect ( usdcUserDataAfter . currentVariableDebt ) . to . be . bignumber . almostEqual (
new BigNumber ( usdcUserDataBefore . currentVariableDebt )
. minus ( amountToRepay . toString ( ) )
. plus ( expectedVariableDebtIncrease )
. toString ( ) ,
'INVALID_DEBT_POSITION'
) ;
expect ( wethUserDataAfter . currentATokenBalance ) . to . be . bignumber . equal (
new BigNumber ( wethUserDataBefore . currentATokenBalance ) . minus (
expectedCollateralLiquidated . toString ( )
) ,
'INVALID_COLLATERAL_POSITION'
) ;
2020-09-09 13:43:02 +00:00
expect ( wethUserDataAfter . usageAsCollateralEnabled ) . to . be . true ;
2020-09-08 18:06:28 +00:00
// Resets USDC Price
await oracle . setAssetPrice ( usdc . address , usdcPrice ) ;
} ) ;
it ( 'Revert expected. User 5 tries to liquidate an User 3 collateral a currency he havent borrow' , async ( ) = > {
const { pool , weth , dai , users , oracle , mockSwapAdapter , usdc } = testEnv ;
const user = users [ 2 ] ;
const liquidator = users [ 4 ] ;
const amountToRepay = parseUnits ( '10' , 6 ) ;
// Sets USDC Price higher to decrease health factor below 1
const usdcPrice = await oracle . getAssetPrice ( usdc . address ) ;
await oracle . setAssetPrice (
usdc . address ,
new BigNumber ( usdcPrice . toString ( ) ) . multipliedBy ( 6.4 ) . toFixed ( 0 )
) ;
const userGlobalData = await pool . getUserAccountData ( user . address ) ;
expect ( userGlobalData . healthFactor . toString ( ) ) . to . be . bignumber . lt ( oneEther , INVALID_HF ) ;
await expect (
pool
. connect ( liquidator . signer )
. repayWithCollateral (
weth . address ,
dai . address ,
user . address ,
amountToRepay ,
mockSwapAdapter . address ,
'0x'
)
2020-09-09 19:12:36 +00:00
) . to . be . revertedWith ( '40' ) ;
2020-09-08 18:06:28 +00:00
await oracle . setAssetPrice ( usdc . address , usdcPrice ) ;
} ) ;
2020-09-09 12:47:33 +00:00
it ( 'User 5 liquidates all the USDC loan of User 3 by swapping his WETH collateral' , async ( ) = > {
const { pool , weth , usdc , users , mockSwapAdapter , oracle } = testEnv ;
const user = users [ 2 ] ;
const liquidator = users [ 4 ] ;
// Sets USDC Price higher to decrease health factor below 1
const usdcPrice = await oracle . getAssetPrice ( usdc . address ) ;
await oracle . setAssetPrice (
usdc . address ,
new BigNumber ( usdcPrice . toString ( ) ) . multipliedBy ( 1.35 ) . toFixed ( 0 )
) ;
const userGlobalData = await pool . getUserAccountData ( user . address ) ;
expect ( userGlobalData . healthFactor . toString ( ) ) . to . be . bignumber . lt ( oneEther , INVALID_HF ) ;
const { userData : wethUserDataBefore } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const {
reserveData : usdcReserveDataBefore ,
userData : usdcUserDataBefore ,
} = await getContractsData ( usdc . address , user . address , testEnv ) ;
const amountToRepay = usdcReserveDataBefore . totalBorrowsVariable . toFixed ( 0 ) ;
await mockSwapAdapter . setAmountToReturn ( amountToRepay ) ;
await waitForTx (
await pool
. connect ( liquidator . signer )
. repayWithCollateral (
weth . address ,
usdc . address ,
user . address ,
amountToRepay ,
mockSwapAdapter . address ,
'0x'
)
) ;
const repayWithCollateralTimestamp = await timeLatest ( ) ;
const { userData : wethUserDataAfter } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const { userData : usdcUserDataAfter } = await getContractsData (
usdc . address ,
user . address ,
testEnv
) ;
const collateralPrice = await oracle . getAssetPrice ( weth . address ) ;
const principalPrice = await oracle . getAssetPrice ( usdc . address ) ;
const collateralDecimals = (
await pool . getReserveConfigurationData ( weth . address )
) . decimals . toString ( ) ;
const principalDecimals = (
await pool . getReserveConfigurationData ( usdc . address )
) . decimals . toString ( ) ;
const expectedCollateralLiquidated = new BigNumber ( principalPrice . toString ( ) )
. times ( new BigNumber ( amountToRepay . toString ( ) ) . times ( 105 ) )
. times ( new BigNumber ( 10 ) . pow ( collateralDecimals ) )
. div (
new BigNumber ( collateralPrice . toString ( ) ) . times ( new BigNumber ( 10 ) . pow ( principalDecimals ) )
)
. div ( 100 )
. decimalPlaces ( 0 , BigNumber . ROUND_DOWN ) ;
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance (
usdcReserveDataBefore ,
usdcUserDataBefore ,
new BigNumber ( repayWithCollateralTimestamp )
) . minus ( usdcUserDataBefore . currentVariableDebt ) ;
expect ( usdcUserDataAfter . currentVariableDebt ) . to . be . bignumber . almostEqual (
new BigNumber ( usdcUserDataBefore . currentVariableDebt )
. minus ( amountToRepay . toString ( ) )
. plus ( expectedVariableDebtIncrease )
. toString ( ) ,
'INVALID_DEBT_POSITION'
) ;
expect ( wethUserDataAfter . currentATokenBalance ) . to . be . bignumber . equal (
new BigNumber ( wethUserDataBefore . currentATokenBalance ) . minus (
expectedCollateralLiquidated . toString ( )
) ,
'INVALID_COLLATERAL_POSITION'
) ;
// Resets USDC Price
await oracle . setAssetPrice ( usdc . address , usdcPrice ) ;
} ) ;
2020-09-08 18:06:28 +00:00
it ( 'User 2 deposit WETH and borrows DAI at Variable' , async ( ) = > {
const { pool , weth , dai , users , oracle } = testEnv ;
const user = users [ 1 ] ;
2020-09-09 19:24:20 +00:00
const amountToDeposit = ethers . utils . parseEther ( '2' ) ;
2020-09-08 18:06:28 +00:00
await weth . connect ( user . signer ) . mint ( amountToDeposit ) ;
await weth . connect ( user . signer ) . approve ( pool . address , APPROVAL_AMOUNT_LENDING_POOL ) ;
2020-09-10 19:40:26 +00:00
await pool . connect ( user . signer ) . deposit ( weth . address , amountToDeposit , user . address , '0' ) ;
2020-09-08 18:06:28 +00:00
const userGlobalData = await pool . getUserAccountData ( user . address ) ;
const daiPrice = await oracle . getAssetPrice ( dai . address ) ;
const amountDAIToBorrow = await convertToCurrencyDecimals (
dai . address ,
new BigNumber ( userGlobalData . availableBorrowsETH . toString ( ) )
. div ( daiPrice . toString ( ) )
2020-09-09 19:24:20 +00:00
. multipliedBy ( 0.9 )
2020-09-08 18:06:28 +00:00
. toFixed ( 0 )
) ;
2020-09-11 09:25:42 +00:00
await pool . connect ( user . signer ) . borrow ( dai . address , amountDAIToBorrow , 2 , 0 , user . address ) ;
2020-09-08 18:06:28 +00:00
} ) ;
2020-09-09 12:47:33 +00:00
it ( 'It is not possible to do reentrancy on repayWithCollateral()' , async ( ) = > {
const { pool , weth , dai , users , mockSwapAdapter , oracle } = testEnv ;
const user = users [ 1 ] ;
const liquidator = users [ 4 ] ;
// Sets DAI Price higher to decrease health factor below 1
const daiPrice = await oracle . getAssetPrice ( dai . address ) ;
await oracle . setAssetPrice (
dai . address ,
new BigNumber ( daiPrice . toString ( ) ) . multipliedBy ( 1.4 ) . toFixed ( 0 )
) ;
const { reserveData : daiReserveDataBefore } = await getContractsData (
dai . address ,
user . address ,
testEnv
) ;
const amountToRepay = daiReserveDataBefore . totalBorrowsVariable . toString ( ) ;
await waitForTx ( await mockSwapAdapter . setTryReentrancy ( true ) ) ;
await mockSwapAdapter . setAmountToReturn ( amountToRepay ) ;
await expect (
pool
. connect ( liquidator . signer )
. repayWithCollateral (
weth . address ,
dai . address ,
user . address ,
amountToRepay ,
mockSwapAdapter . address ,
'0x'
)
2020-09-09 19:12:36 +00:00
) . to . be . revertedWith ( '53' ) ;
2020-09-09 12:47:33 +00:00
// Resets DAI Price
await oracle . setAssetPrice ( dai . address , daiPrice ) ;
// Resets mock
await waitForTx ( await mockSwapAdapter . setTryReentrancy ( false ) ) ;
} ) ;
2020-09-08 18:06:28 +00:00
it ( 'User 5 tries to liquidate User 2 DAI Variable loan using his WETH collateral, with good HF' , async ( ) = > {
const { pool , weth , dai , users , mockSwapAdapter } = testEnv ;
const user = users [ 1 ] ;
const liquidator = users [ 4 ] ;
const { reserveData : daiReserveDataBefore } = await getContractsData (
dai . address ,
user . address ,
testEnv
) ;
// First half
const amountToRepay = daiReserveDataBefore . totalBorrowsVariable . dividedBy ( 2 ) . toString ( ) ;
await mockSwapAdapter . setAmountToReturn ( amountToRepay ) ;
await expect (
pool
. connect ( liquidator . signer )
. repayWithCollateral (
weth . address ,
dai . address ,
user . address ,
amountToRepay ,
mockSwapAdapter . address ,
'0x'
)
2020-09-09 19:12:36 +00:00
) . to . be . revertedWith ( '38' ) ;
2020-09-08 18:06:28 +00:00
} ) ;
it ( 'User 5 liquidates User 2 DAI Variable loan using his WETH collateral, half the amount' , async ( ) = > {
const { pool , weth , dai , users , mockSwapAdapter , oracle } = testEnv ;
const user = users [ 1 ] ;
const liquidator = users [ 4 ] ;
// Sets DAI Price higher to decrease health factor below 1
const daiPrice = await oracle . getAssetPrice ( dai . address ) ;
await oracle . setAssetPrice (
dai . address ,
new BigNumber ( daiPrice . toString ( ) ) . multipliedBy ( 1.4 ) . toFixed ( 0 )
) ;
const userGlobalData = await pool . getUserAccountData ( user . address ) ;
expect ( userGlobalData . healthFactor . toString ( ) ) . to . be . bignumber . lt ( oneEther , INVALID_HF ) ;
const { userData : wethUserDataBefore } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const { reserveData : daiReserveDataBefore , userData : daiUserDataBefore } = await getContractsData (
dai . address ,
user . address ,
testEnv
) ;
// First half
2020-09-09 19:24:20 +00:00
const amountToRepay = daiReserveDataBefore . totalBorrowsVariable . multipliedBy ( 0.6 ) . toString ( ) ;
2020-09-08 18:06:28 +00:00
await mockSwapAdapter . setAmountToReturn ( amountToRepay ) ;
await waitForTx (
await pool
. connect ( liquidator . signer )
. repayWithCollateral (
weth . address ,
dai . address ,
user . address ,
amountToRepay ,
mockSwapAdapter . address ,
'0x'
)
) ;
const repayWithCollateralTimestamp = await timeLatest ( ) ;
const { userData : wethUserDataAfter } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const { userData : daiUserDataAfter } = await getContractsData ( dai . address , user . address , testEnv ) ;
const collateralPrice = await oracle . getAssetPrice ( weth . address ) ;
const principalPrice = await oracle . getAssetPrice ( dai . address ) ;
const collateralDecimals = (
await pool . getReserveConfigurationData ( weth . address )
) . decimals . toString ( ) ;
const principalDecimals = (
await pool . getReserveConfigurationData ( dai . address )
) . decimals . toString ( ) ;
const expectedCollateralLiquidated = new BigNumber ( principalPrice . toString ( ) )
. times ( new BigNumber ( amountToRepay . toString ( ) ) . times ( 105 ) )
. times ( new BigNumber ( 10 ) . pow ( collateralDecimals ) )
. div (
new BigNumber ( collateralPrice . toString ( ) ) . times ( new BigNumber ( 10 ) . pow ( principalDecimals ) )
)
. div ( 100 )
. decimalPlaces ( 0 , BigNumber . ROUND_DOWN ) ;
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance (
daiReserveDataBefore ,
daiUserDataBefore ,
new BigNumber ( repayWithCollateralTimestamp )
) . minus ( daiUserDataBefore . currentVariableDebt ) ;
expect ( daiUserDataAfter . currentVariableDebt ) . to . be . bignumber . almostEqual (
new BigNumber ( daiUserDataBefore . currentVariableDebt )
. minus ( amountToRepay . toString ( ) )
. plus ( expectedVariableDebtIncrease )
. toString ( )
) ;
expect ( wethUserDataAfter . currentATokenBalance ) . to . be . bignumber . equal (
new BigNumber ( wethUserDataBefore . currentATokenBalance ) . minus (
expectedCollateralLiquidated . toString ( )
)
) ;
2020-09-09 13:43:02 +00:00
expect ( wethUserDataAfter . usageAsCollateralEnabled ) . to . be . true ;
2020-09-08 18:06:28 +00:00
// Resets DAI price
await oracle . setAssetPrice ( dai . address , daiPrice ) ;
} ) ;
it ( 'User 2 tries to repay remaining DAI Variable loan using his WETH collateral' , async ( ) = > {
const { pool , weth , dai , users , mockSwapAdapter , oracle } = testEnv ;
const user = users [ 1 ] ;
const { userData : wethUserDataBefore } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const { reserveData : daiReserveDataBefore , userData : daiUserDataBefore } = await getContractsData (
dai . address ,
user . address ,
testEnv
) ;
2020-09-09 19:24:20 +00:00
await increaseTime ( 1000 ) ;
2020-09-08 18:06:28 +00:00
// Repay the remaining DAI
const amountToRepay = daiReserveDataBefore . totalBorrowsVariable . toString ( ) ;
await mockSwapAdapter . setAmountToReturn ( amountToRepay ) ;
2020-09-09 19:24:20 +00:00
const receipt = await waitForTx (
2020-09-08 18:06:28 +00:00
await pool
. connect ( user . signer )
. repayWithCollateral (
weth . address ,
dai . address ,
user . address ,
amountToRepay ,
mockSwapAdapter . address ,
'0x'
)
) ;
2020-09-09 19:24:20 +00:00
const repayWithCollateralTimestamp = ( await BRE . ethers . provider . getBlock ( receipt . blockNumber ) )
. timestamp ;
2020-09-08 18:06:28 +00:00
const { userData : wethUserDataAfter } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const { userData : daiUserDataAfter } = await getContractsData ( dai . address , user . address , testEnv ) ;
const collateralPrice = await oracle . getAssetPrice ( weth . address ) ;
const principalPrice = await oracle . getAssetPrice ( dai . address ) ;
const collateralDecimals = (
await pool . getReserveConfigurationData ( weth . address )
) . decimals . toString ( ) ;
const principalDecimals = (
await pool . getReserveConfigurationData ( dai . address )
) . decimals . toString ( ) ;
const expectedCollateralLiquidated = new BigNumber ( principalPrice . toString ( ) )
. times ( new BigNumber ( amountToRepay . toString ( ) ) . times ( 105 ) )
. times ( new BigNumber ( 10 ) . pow ( collateralDecimals ) )
. div (
new BigNumber ( collateralPrice . toString ( ) ) . times ( new BigNumber ( 10 ) . pow ( principalDecimals ) )
)
. div ( 100 )
. decimalPlaces ( 0 , BigNumber . ROUND_DOWN ) ;
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance (
daiReserveDataBefore ,
daiUserDataBefore ,
new BigNumber ( repayWithCollateralTimestamp )
) . minus ( daiUserDataBefore . currentVariableDebt ) ;
expect ( daiUserDataAfter . currentVariableDebt ) . to . be . bignumber . almostEqual (
new BigNumber ( daiUserDataBefore . currentVariableDebt )
. minus ( amountToRepay . toString ( ) )
. plus ( expectedVariableDebtIncrease )
. toString ( )
) ;
2020-09-09 19:24:20 +00:00
expect (
2020-09-08 18:06:28 +00:00
new BigNumber ( wethUserDataBefore . currentATokenBalance ) . minus (
expectedCollateralLiquidated . toString ( )
)
2020-09-09 19:24:20 +00:00
) . to . be . bignumber . equal ( wethUserDataAfter . currentATokenBalance ) ;
2020-09-08 18:06:28 +00:00
} ) ;
2020-09-09 13:43:02 +00:00
it ( 'Liquidator tries to repay 4 user a bigger amount that what can be swapped of a particular collateral, repaying only the maximum allowed by that collateral' , async ( ) = > {
const { pool , weth , dai , usdc , users , mockSwapAdapter , oracle } = testEnv ;
2020-09-08 18:06:28 +00:00
const user = users [ 3 ] ;
const liquidator = users [ 5 ] ;
const amountToDepositWeth = parseEther ( '0.1' ) ;
const amountToDepositDAI = parseEther ( '500' ) ;
2020-09-09 13:43:02 +00:00
const amountToBorrowVariable = parseUnits ( '80' , '6' ) ;
2020-09-08 18:06:28 +00:00
await weth . connect ( user . signer ) . mint ( amountToDepositWeth ) ;
await dai . connect ( user . signer ) . mint ( amountToDepositDAI ) ;
await weth . connect ( user . signer ) . approve ( pool . address , APPROVAL_AMOUNT_LENDING_POOL ) ;
await dai . connect ( user . signer ) . approve ( pool . address , APPROVAL_AMOUNT_LENDING_POOL ) ;
2020-09-10 19:40:26 +00:00
await pool . connect ( user . signer ) . deposit ( weth . address , amountToDepositWeth , user . address , '0' ) ;
await pool . connect ( user . signer ) . deposit ( dai . address , amountToDepositDAI , user . address , '0' ) ;
2020-09-08 18:06:28 +00:00
2020-09-14 17:59:00 +00:00
await pool
. connect ( user . signer )
. borrow ( usdc . address , amountToBorrowVariable , 2 , 0 , user . address ) ;
2020-09-08 18:06:28 +00:00
2020-09-09 13:43:02 +00:00
const amountToRepay = amountToBorrowVariable ;
2020-09-08 18:06:28 +00:00
const { userData : wethUserDataBefore } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
2020-09-09 13:43:02 +00:00
const {
reserveData : usdcReserveDataBefore ,
userData : usdcUserDataBefore ,
} = await getContractsData ( usdc . address , user . address , testEnv ) ;
2020-09-08 18:06:28 +00:00
// Set HF below 1
2020-09-09 13:43:02 +00:00
const daiPrice = await oracle . getAssetPrice ( dai . address ) ;
2020-09-08 18:06:28 +00:00
await oracle . setAssetPrice (
2020-09-09 13:43:02 +00:00
dai . address ,
new BigNumber ( daiPrice . toString ( ) ) . multipliedBy ( 0.1 ) . toFixed ( 0 )
2020-09-08 18:06:28 +00:00
) ;
const userGlobalDataPrior = await pool . getUserAccountData ( user . address ) ;
expect ( userGlobalDataPrior . healthFactor . toString ( ) ) . to . be . bignumber . lt ( oneEther , INVALID_HF ) ;
2020-09-09 13:43:02 +00:00
// Execute liquidation
2020-09-08 18:06:28 +00:00
await mockSwapAdapter . setAmountToReturn ( amountToRepay ) ;
await waitForTx (
await pool
. connect ( liquidator . signer )
. repayWithCollateral (
weth . address ,
2020-09-09 13:43:02 +00:00
usdc . address ,
2020-09-08 18:06:28 +00:00
user . address ,
amountToRepay ,
mockSwapAdapter . address ,
'0x'
)
) ;
const repayWithCollateralTimestamp = await timeLatest ( ) ;
const { userData : wethUserDataAfter } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
2020-09-09 13:43:02 +00:00
const { userData : usdcUserDataAfter } = await getContractsData (
usdc . address ,
user . address ,
testEnv
) ;
2020-09-08 18:06:28 +00:00
const collateralPrice = await oracle . getAssetPrice ( weth . address ) ;
2020-09-09 13:43:02 +00:00
const principalPrice = await oracle . getAssetPrice ( usdc . address ) ;
2020-09-08 18:06:28 +00:00
const collateralConfig = await pool . getReserveConfigurationData ( weth . address ) ;
const collateralDecimals = collateralConfig . decimals . toString ( ) ;
const principalDecimals = (
2020-09-09 13:43:02 +00:00
await pool . getReserveConfigurationData ( usdc . address )
2020-09-08 18:06:28 +00:00
) . decimals . toString ( ) ;
const collateralLiquidationBonus = collateralConfig . liquidationBonus . toString ( ) ;
const expectedDebtCovered = new BigNumber ( collateralPrice . toString ( ) )
. times ( new BigNumber ( wethUserDataBefore . currentATokenBalance . toString ( ) ) )
. times ( new BigNumber ( 10 ) . pow ( principalDecimals ) )
. div (
new BigNumber ( principalPrice . toString ( ) ) . times ( new BigNumber ( 10 ) . pow ( collateralDecimals ) )
)
. div ( new BigNumber ( collateralLiquidationBonus ) . div ( 10000 ) . toString ( ) )
. decimalPlaces ( 0 , BigNumber . ROUND_DOWN ) ;
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance (
2020-09-09 13:43:02 +00:00
usdcReserveDataBefore ,
usdcUserDataBefore ,
2020-09-08 18:06:28 +00:00
new BigNumber ( repayWithCollateralTimestamp )
2020-09-09 13:43:02 +00:00
) . minus ( usdcUserDataBefore . currentVariableDebt ) ;
2020-09-08 18:06:28 +00:00
2020-09-09 13:43:02 +00:00
expect ( usdcUserDataAfter . currentVariableDebt ) . to . be . bignumber . equal (
new BigNumber ( usdcUserDataBefore . currentVariableDebt )
2020-09-08 18:06:28 +00:00
. minus ( expectedDebtCovered . toString ( ) )
. plus ( expectedVariableDebtIncrease ) ,
'INVALID_VARIABLE_DEBT_POSITION'
) ;
2020-09-09 13:43:02 +00:00
expect ( wethUserDataAfter . usageAsCollateralEnabled ) . to . be . false ;
2020-09-08 18:06:28 +00:00
expect ( wethUserDataAfter . currentATokenBalance ) . to . be . bignumber . equal ( 0 ) ;
2020-09-09 13:43:02 +00:00
// Resets DAI Price
await oracle . setAssetPrice ( dai . address , daiPrice ) ;
2020-09-08 18:06:28 +00:00
} ) ;
2020-09-10 11:05:26 +00:00
it ( 'User 5 deposits WETH and DAI, then borrows USDC at Variable, then disables WETH as collateral' , async ( ) = > {
const { pool , weth , dai , usdc , users } = testEnv ;
const user = users [ 4 ] ;
const amountWETHToDeposit = parseEther ( '10' ) ;
const amountDAIToDeposit = parseEther ( '60' ) ;
const amountToBorrow = parseUnits ( '65' , 6 ) ;
await weth . connect ( user . signer ) . mint ( amountWETHToDeposit ) ;
await weth . connect ( user . signer ) . approve ( pool . address , APPROVAL_AMOUNT_LENDING_POOL ) ;
2020-09-10 19:40:26 +00:00
await pool . connect ( user . signer ) . deposit ( weth . address , amountWETHToDeposit , user . address , '0' ) ;
2020-09-10 11:05:26 +00:00
await dai . connect ( user . signer ) . mint ( amountDAIToDeposit ) ;
await dai . connect ( user . signer ) . approve ( pool . address , APPROVAL_AMOUNT_LENDING_POOL ) ;
2020-09-10 19:40:26 +00:00
await pool . connect ( user . signer ) . deposit ( dai . address , amountDAIToDeposit , user . address , '0' ) ;
2020-09-10 11:05:26 +00:00
2020-09-11 09:25:42 +00:00
await pool . connect ( user . signer ) . borrow ( usdc . address , amountToBorrow , 2 , 0 , user . address ) ;
2020-09-10 11:05:26 +00:00
} ) ;
it ( 'Liquidator tries to liquidates User 5 USDC loan by swapping his WETH collateral, should revert due WETH collateral disabled' , async ( ) = > {
const { pool , weth , usdc , users , mockSwapAdapter , oracle } = testEnv ;
const user = users [ 4 ] ;
const liquidator = users [ 5 ] ;
const amountToRepay = parseUnits ( '65' , 6 ) ;
// User 5 Disable WETH as collateral
await pool . connect ( user . signer ) . setUserUseReserveAsCollateral ( weth . address , false ) ;
const { userData : wethUserDataBefore } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const {
reserveData : usdcReserveDataBefore ,
userData : usdcUserDataBefore ,
} = await getContractsData ( usdc . address , user . address , testEnv ) ;
expect ( wethUserDataBefore . usageAsCollateralEnabled ) . to . be . false ;
// Liquidator should NOT be able to liquidate himself with WETH, even if is disabled
await mockSwapAdapter . setAmountToReturn ( amountToRepay ) ;
await expect (
pool
. connect ( liquidator . signer )
. repayWithCollateral (
weth . address ,
usdc . address ,
user . address ,
amountToRepay ,
mockSwapAdapter . address ,
'0x'
)
) . to . be . revertedWith ( COLLATERAL_CANNOT_BE_LIQUIDATED ) ;
const repayWithCollateralTimestamp = await timeLatest ( ) ;
const { userData : wethUserDataAfter } = await getContractsData (
weth . address ,
user . address ,
testEnv
) ;
const { userData : usdcUserDataAfter } = await getContractsData (
usdc . address ,
user . address ,
testEnv
) ;
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance (
usdcReserveDataBefore ,
usdcUserDataBefore ,
new BigNumber ( repayWithCollateralTimestamp )
) . minus ( usdcUserDataBefore . currentVariableDebt ) ;
expect ( usdcUserDataAfter . currentVariableDebt ) . to . be . bignumber . almostEqual (
new BigNumber ( usdcUserDataBefore . currentVariableDebt )
. plus ( expectedVariableDebtIncrease )
. toString ( ) ,
'INVALID_DEBT_POSITION'
) ;
expect ( wethUserDataAfter . usageAsCollateralEnabled ) . to . be . false ;
} ) ;
2020-09-08 18:06:28 +00:00
} ) ;