aave-protocol-v2/test/flash-liquidation-with-collateral.spec.ts

917 lines
31 KiB
TypeScript
Raw Normal View History

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';
import {timeLatest, BRE, increaseTime} from '../helpers/misc-utils';
2020-09-09 19:26:52 +00:00
import {ProtocolErrors} from '../helpers/types';
import {convertToCurrencyDecimals} from '../helpers/contracts-helpers';
2020-09-09 19:26:52 +00:00
import {expectRepayWithCollateralEvent} from './repay-with-collateral.spec';
const {expect} = require('chai');
const {parseUnits, parseEther} = ethers.utils;
makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEnv) => {
const {INVALID_HF, COLLATERAL_CANNOT_BE_LIQUIDATED} = ProtocolErrors;
it('User 1 provides some liquidity for others to borrow', async () => {
const {pool, weth, dai, usdc, deployer} = testEnv;
await weth.mint(parseEther('200'));
await weth.approve(pool.address, parseEther('200'));
await pool.deposit(weth.address, parseEther('200'), deployer.address, 0);
await dai.mint(parseEther('20000'));
await dai.approve(pool.address, parseEther('20000'));
await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0);
await usdc.mint(parseEther('20000'));
await usdc.approve(pool.address, parseEther('20000'));
await pool.deposit(usdc.address, parseEther('20000'), deployer.address, 0);
});
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);
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
const usdcPrice = await oracle.getAssetPrice(usdc.address);
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 1, 0);
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);
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
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)
);
await pool.connect(user.signer).borrow(usdc.address, amountUSDCToBorrow, 2, 0);
});
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.totalVariableDebt.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'
);
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
// 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'
)
).to.be.revertedWith('40');
await oracle.setAssetPrice(usdc.address, usdcPrice);
});
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.totalVariableDebt.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);
});
it('User 2 deposit WETH and borrows DAI at Variable', async () => {
const {pool, weth, dai, users, oracle} = testEnv;
const user = users[1];
const amountToDeposit = ethers.utils.parseEther('2');
await weth.connect(user.signer).mint(amountToDeposit);
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
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())
.multipliedBy(0.9)
.toFixed(0)
);
await pool.connect(user.signer).borrow(dai.address, amountDAIToBorrow, 2, 0);
});
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.totalVariableDebt.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'
)
).to.be.revertedWith('53');
// Resets DAI Price
await oracle.setAssetPrice(dai.address, daiPrice);
// Resets mock
await waitForTx(await mockSwapAdapter.setTryReentrancy(false));
});
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.totalVariableDebt.dividedBy(2).toString();
await mockSwapAdapter.setAmountToReturn(amountToRepay);
await expect(
pool
.connect(liquidator.signer)
.repayWithCollateral(
weth.address,
dai.address,
user.address,
amountToRepay,
mockSwapAdapter.address,
'0x'
)
).to.be.revertedWith('38');
});
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
const amountToRepay = daiReserveDataBefore.totalVariableDebt.multipliedBy(0.6).toString();
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()
)
);
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
// 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
);
await increaseTime(1000);
// Repay the remaining DAI
const amountToRepay = daiReserveDataBefore.totalVariableDebt.toString();
await mockSwapAdapter.setAmountToReturn(amountToRepay);
const receipt = await waitForTx(
await pool
.connect(user.signer)
.repayWithCollateral(
weth.address,
dai.address,
user.address,
amountToRepay,
mockSwapAdapter.address,
'0x'
)
);
const repayWithCollateralTimestamp = (await BRE.ethers.provider.getBlock(receipt.blockNumber))
.timestamp;
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(
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
expectedCollateralLiquidated.toString()
)
).to.be.bignumber.equal(wethUserDataAfter.currentATokenBalance);
});
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;
const user = users[3];
const liquidator = users[5];
const amountToDepositWeth = parseEther('0.1');
const amountToDepositDAI = parseEther('500');
const amountToBorrowVariable = parseUnits('80', '6');
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);
await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowVariable, 2, 0);
const amountToRepay = amountToBorrowVariable;
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
const daiPrice = await oracle.getAssetPrice(dai.address);
await oracle.setAssetPrice(
dai.address,
new BigNumber(daiPrice.toString()).multipliedBy(0.1).toFixed(0)
);
const userGlobalDataPrior = await pool.getUserAccountData(user.address);
expect(userGlobalDataPrior.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
// Execute liquidation
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 collateralConfig = await pool.getReserveConfigurationData(weth.address);
const collateralDecimals = collateralConfig.decimals.toString();
const principalDecimals = (
await pool.getReserveConfigurationData(usdc.address)
).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(
usdcReserveDataBefore,
usdcUserDataBefore,
new BigNumber(repayWithCollateralTimestamp)
).minus(usdcUserDataBefore.currentVariableDebt);
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.equal(
new BigNumber(usdcUserDataBefore.currentVariableDebt)
.minus(expectedDebtCovered.toString())
.plus(expectedVariableDebtIncrease),
'INVALID_VARIABLE_DEBT_POSITION'
);
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.false;
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(0);
// Resets DAI Price
await oracle.setAssetPrice(dai.address, daiPrice);
});
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);
await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0');
await dai.connect(user.signer).mint(amountDAIToDeposit);
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
});
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;
});
});