Fixed liquidation tests

This commit is contained in:
The3D 2020-07-08 17:26:50 +02:00
parent 395e4aa3a7
commit f1743a5eac
26 changed files with 221 additions and 266 deletions

View File

@ -731,6 +731,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
external external
view view
returns ( returns (
uint256 decimals,
uint256 ltv, uint256 ltv,
uint256 liquidationThreshold, uint256 liquidationThreshold,
uint256 liquidationBonus, uint256 liquidationBonus,
@ -746,6 +747,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.ReserveData storage reserve = reserves[_reserve];
return ( return (
reserve.decimals,
reserve.baseLTVasCollateral, reserve.baseLTVasCollateral,
reserve.liquidationThreshold, reserve.liquidationThreshold,
reserve.liquidationBonus, reserve.liquidationBonus,

View File

@ -10,10 +10,11 @@ import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
import "../configuration/LendingPoolAddressesProvider.sol"; import "../configuration/LendingPoolAddressesProvider.sol";
import "../tokenization/AToken.sol"; import "../tokenization/AToken.sol";
import "../tokenization/interfaces/IStableDebtToken.sol";
import "../tokenization/interfaces/IVariableDebtToken.sol";
import "../libraries/CoreLibrary.sol"; import "../libraries/CoreLibrary.sol";
import "../libraries/WadRayMath.sol"; import "../libraries/WadRayMath.sol";
import "../interfaces/IPriceOracleGetter.sol"; import "../interfaces/IPriceOracleGetter.sol";
import {IFeeProvider} from "../interfaces/IFeeProvider.sol";
import "../libraries/EthAddressLib.sol"; import "../libraries/EthAddressLib.sol";
import "../libraries/GenericLogic.sol"; import "../libraries/GenericLogic.sol";
import "../libraries/UserLogic.sol"; import "../libraries/UserLogic.sol";
@ -120,13 +121,12 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
) external payable returns (uint256, string memory) { ) external payable returns (uint256, string memory) {
CoreLibrary.ReserveData storage principalReserve = reserves[_reserve]; CoreLibrary.ReserveData storage principalReserve = reserves[_reserve];
CoreLibrary.ReserveData storage collateralReserve = reserves[_collateral]; CoreLibrary.ReserveData storage collateralReserve = reserves[_collateral];
CoreLibrary.UserReserveData storage userCollateral = usersReserveData[msg CoreLibrary.UserReserveData storage userCollateral = usersReserveData[_user][_collateral];
.sender][_collateral];
LiquidationCallLocalVars memory vars; LiquidationCallLocalVars memory vars;
(, , , , , vars.healthFactor) = GenericLogic.calculateUserAccountData( (, , , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
msg.sender, _user,
reserves, reserves,
usersReserveData, usersReserveData,
reservesList, reservesList,
@ -140,7 +140,7 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
); );
} }
vars.userCollateralBalance = IERC20(_collateral).balanceOf(_user); vars.userCollateralBalance = IERC20(collateralReserve.aTokenAddress).balanceOf(_user);
//if _user hasn't deposited this specific collateral, nothing can be liquidated //if _user hasn't deposited this specific collateral, nothing can be liquidated
if (vars.userCollateralBalance == 0) { if (vars.userCollateralBalance == 0) {
@ -213,6 +213,8 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
} }
} }
console.log("Balance before liquidation is %s", IERC20(principalReserve.stableDebtTokenAddress).balanceOf(_user));
//TODO Burn debt tokens //TODO Burn debt tokens
if(vars.userVariableDebt >= vars.actualAmountToLiquidate){ if(vars.userVariableDebt >= vars.actualAmountToLiquidate){
IVariableDebtToken(principalReserve.variableDebtTokenAddress).burn(_user, vars.actualAmountToLiquidate); IVariableDebtToken(principalReserve.variableDebtTokenAddress).burn(_user, vars.actualAmountToLiquidate);
@ -222,6 +224,8 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
IStableDebtToken(principalReserve.stableDebtTokenAddress).burn(_user, vars.actualAmountToLiquidate.sub(vars.userVariableDebt)); IStableDebtToken(principalReserve.stableDebtTokenAddress).burn(_user, vars.actualAmountToLiquidate.sub(vars.userVariableDebt));
} }
console.log("Balance after liquidation is %s", IERC20(principalReserve.stableDebtTokenAddress).balanceOf(_user));
vars.collateralAtoken = AToken(collateralReserve.aTokenAddress); vars.collateralAtoken = AToken(collateralReserve.aTokenAddress);
//if liquidator reclaims the aToken, he receives the equivalent atoken amount //if liquidator reclaims the aToken, he receives the equivalent atoken amount
@ -235,9 +239,13 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl
//otherwise receives the underlying asset //otherwise receives the underlying asset
//burn the equivalent amount of atoken //burn the equivalent amount of atoken
vars.collateralAtoken.burnOnLiquidation(_user, vars.maxCollateralToLiquidate); vars.collateralAtoken.burnOnLiquidation(_user, vars.maxCollateralToLiquidate);
console.log("Burned %s collateral tokens, collateral %s",vars.maxCollateralToLiquidate, _collateral);
IERC20(_collateral).universalTransfer(msg.sender, vars.maxCollateralToLiquidate); IERC20(_collateral).universalTransfer(msg.sender, vars.maxCollateralToLiquidate);
} }
console.log("Transferring principal, amount %s",vars.actualAmountToLiquidate);
//transfers the principal currency to the pool //transfers the principal currency to the pool
IERC20(_reserve).universalTransferFromSenderToThis(vars.actualAmountToLiquidate, true); IERC20(_reserve).universalTransferFromSenderToThis(vars.actualAmountToLiquidate, true);

View File

@ -11,6 +11,7 @@ import {WadRayMath} from "./WadRayMath.sol";
import "../interfaces/IPriceOracleGetter.sol"; import "../interfaces/IPriceOracleGetter.sol";
import {IFeeProvider} from "../interfaces/IFeeProvider.sol"; import {IFeeProvider} from "../interfaces/IFeeProvider.sol";
import '@nomiclabs/buidler/console.sol';
/** /**
* @title GenericLogic library * @title GenericLogic library
@ -150,7 +151,7 @@ library GenericLogic {
CalculateUserAccountDataVars memory vars; CalculateUserAccountDataVars memory vars;
for (vars.i = 0; vars.i < _reserves.length; vars.i++) { for (vars.i = 0; vars.i < _reserves.length; vars.i++) {
vars.currentReserveAddress = _reserves[vars.i]; vars.currentReserveAddress = _reserves[vars.i];
CoreLibrary.ReserveData storage currentReserve = _reservesData[vars CoreLibrary.ReserveData storage currentReserve = _reservesData[vars
@ -236,7 +237,10 @@ library GenericLogic {
uint256 borrowBalanceETH, uint256 borrowBalanceETH,
uint256 totalFeesETH, uint256 totalFeesETH,
uint256 liquidationThreshold uint256 liquidationThreshold
) internal pure returns (uint256) { ) internal view returns (uint256) {
console.log("Borrow balance ETH is %s", borrowBalanceETH);
if (borrowBalanceETH == 0) return uint256(-1); if (borrowBalanceETH == 0) return uint256(-1);
return return

View File

@ -4,6 +4,7 @@ pragma solidity ^0.6.8;
import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@nomiclabs/buidler/console.sol';
/** /**
* @title UniversalERC20 library * @title UniversalERC20 library
@ -37,8 +38,13 @@ library UniversalERC20 {
} }
if (isETH(token)) { if (isETH(token)) {
console.log("transfer of ETH, value %s", amount);
console.log("Balance is %s", address(this).balance);
(bool result, ) = payable(to).call{value: amount, gas: DEFAULT_TRANSFER_GAS}(''); (bool result, ) = payable(to).call{value: amount, gas: DEFAULT_TRANSFER_GAS}('');
console.log("Transfer done, result %s", result);
require(result, 'ETH_TRANSFER_FAILED'); require(result, 'ETH_TRANSFER_FAILED');
} else { } else {
token.safeTransfer(to, amount); token.safeTransfer(to, amount);
} }
@ -166,6 +172,6 @@ library UniversalERC20 {
* @return boolean * @return boolean
**/ **/
function isETH(IERC20 token) internal pure returns (bool) { function isETH(IERC20 token) internal pure returns (bool) {
return (address(token) == address(ZERO_ADDRESS) || address(token) == address(ETH_ADDRESS)); return (address(token) == address(ETH_ADDRESS));
} }
} }

View File

@ -36,7 +36,7 @@ contract AaveProtocolTestHelpers {
address[] memory reserves = pool.getReserves(); address[] memory reserves = pool.getReserves();
TokenData[] memory aTokens = new TokenData[](reserves.length); TokenData[] memory aTokens = new TokenData[](reserves.length);
for (uint256 i = 0; i < reserves.length; i++) { for (uint256 i = 0; i < reserves.length; i++) {
(,,,,address aTokenAddress,,,,,) = pool.getReserveConfigurationData(reserves[i]); (,,,,,address aTokenAddress,,,,,) = pool.getReserveConfigurationData(reserves[i]);
aTokens[i] = TokenData({ aTokens[i] = TokenData({
symbol: AToken(aTokenAddress).symbol(), symbol: AToken(aTokenAddress).symbol(),
tokenAddress: aTokenAddress tokenAddress: aTokenAddress

View File

@ -92,7 +92,7 @@ contract WalletBalanceProvider {
uint256[] memory balances = new uint256[](reserves.length); uint256[] memory balances = new uint256[](reserves.length);
for (uint256 j = 0; j < reserves.length; j++) { for (uint256 j = 0; j < reserves.length; j++) {
(, , , , , , , , bool isActive,) = pool.getReserveConfigurationData(reserves[j]); (, , , , , , , , , bool isActive,) = pool.getReserveConfigurationData(reserves[j]);
if (!isActive) { if (!isActive) {
balances[j] = 0; balances[j] = 0;

6
package-lock.json generated
View File

@ -2162,6 +2162,12 @@
"integrity": "sha512-SubOtaSI2AILWTWe2j0c6i2yFT/f9J6UBjeVGDuwDiPLkF/U5+/eTWUE3sbCZ1KgcPF6UJsDVYbIxaYA097MQA==", "integrity": "sha512-SubOtaSI2AILWTWe2j0c6i2yFT/f9J6UBjeVGDuwDiPLkF/U5+/eTWUE3sbCZ1KgcPF6UJsDVYbIxaYA097MQA==",
"dev": true "dev": true
}, },
"chai-bn": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/chai-bn/-/chai-bn-0.2.1.tgz",
"integrity": "sha512-01jt2gSXAw7UYFPT5K8d7HYjdXj2vyeIuE+0T/34FWzlNcVbs1JkPxRu7rYMfQnJhrHT8Nr6qjSf5ZwwLU2EYg==",
"dev": true
},
"chalk": { "chalk": {
"version": "2.4.2", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",

View File

@ -36,6 +36,7 @@
"buidler-typechain": "0.1.1", "buidler-typechain": "0.1.1",
"chai": "4.2.0", "chai": "4.2.0",
"chai-bignumber": "3.0.0", "chai-bignumber": "3.0.0",
"chai-bn": "^0.2.1",
"ethereum-waffle": "2.5.1", "ethereum-waffle": "2.5.1",
"ethers": "4.0.47", "ethers": "4.0.47",
"husky": "^4.2.5", "husky": "^4.2.5",

View File

@ -270,8 +270,6 @@ const initReserves = async (
] ]
) )
console.log(`Debt tokens for ${assetSymbol}: stable ${stableDebtToken.address} variable ${variableDebtToken.address}`)
if (process.env.POOL === AavePools.secondary) { if (process.env.POOL === AavePools.secondary) {
if (assetSymbol.search("UNI") === -1) { if (assetSymbol.search("UNI") === -1) {
assetSymbol = `Uni${assetSymbol}`; assetSymbol = `Uni${assetSymbol}`;
@ -525,7 +523,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
fallbackOracle.address, fallbackOracle.address,
]); ]);
await waitForTx( await waitForTx(
await addressesProvider.setPriceOracle(chainlinkProxyPriceProvider.address) await addressesProvider.setPriceOracle(fallbackOracle.address)
); );
const lendingRateOracle = await deployLendingRateOracle(); const lendingRateOracle = await deployLendingRateOracle();

View File

@ -175,7 +175,7 @@ makeSuite("AToken: Transfer", (testEnv: TestEnv) => {
const {users, pool, aDai, dai} = testEnv; const {users, pool, aDai, dai} = testEnv;
await pool await pool
.connect(users[1].signer) .connect(users[1].signer)
.repay(MOCK_ETH_ADDRESS, MAX_UINT_AMOUNT, users[1].address, users[1].address, { .repay(MOCK_ETH_ADDRESS, MAX_UINT_AMOUNT, RateMode.Stable, users[1].address, {
value: ethers.utils.parseEther("1"), value: ethers.utils.parseEther("1"),
}); });

View File

@ -326,8 +326,6 @@ export const calcExpectedReserveDataAfterBorrow = (
): ReserveData => { ): ReserveData => {
const expectedReserveData = <ReserveData>{}; const expectedReserveData = <ReserveData>{};
console.log('Computing borrow, amountBorrowed: ', amountBorrowed, ' Rate mode: ', borrowRateMode);
expectedReserveData.address = reserveDataBeforeAction.address; expectedReserveData.address = reserveDataBeforeAction.address;
const amountBorrowedBN = new BigNumber(amountBorrowed); const amountBorrowedBN = new BigNumber(amountBorrowed);
@ -419,8 +417,7 @@ export const calcExpectedReserveDataAfterRepay = (
txTimestamp: BigNumber, txTimestamp: BigNumber,
currentTimestamp: BigNumber currentTimestamp: BigNumber
): ReserveData => { ): ReserveData => {
console.log('Computing repay, amount repaid: ', amountRepaid, ' Rate mode: ', borrowRateMode);
const expectedReserveData: ReserveData = <ReserveData>{}; const expectedReserveData: ReserveData = <ReserveData>{};
expectedReserveData.address = reserveDataBeforeAction.address; expectedReserveData.address = reserveDataBeforeAction.address;
@ -766,7 +763,6 @@ export const calcExpectedReserveDataAfterSwapRateMode = (
rateMode: string, rateMode: string,
txTimestamp: BigNumber txTimestamp: BigNumber
): ReserveData => { ): ReserveData => {
console.log('Computing swap, Rate mode: ', rateMode);
const expectedReserveData: ReserveData = <ReserveData>{}; const expectedReserveData: ReserveData = <ReserveData>{};
@ -1246,7 +1242,7 @@ const calcExpectedVariableDebtUserIndex = (
return calcExpectedReserveNormalizedDebt(reserveDataBeforeAction, currentTimestamp); return calcExpectedReserveNormalizedDebt(reserveDataBeforeAction, currentTimestamp);
}; };
const calcExpectedVariableDebtTokenBalance = ( export const calcExpectedVariableDebtTokenBalance = (
reserveDataBeforeAction: ReserveData, reserveDataBeforeAction: ReserveData,
userDataBeforeAction: UserReserveData, userDataBeforeAction: UserReserveData,
currentTimestamp: BigNumber currentTimestamp: BigNumber
@ -1266,7 +1262,7 @@ const calcExpectedVariableDebtTokenBalance = (
.rayToWad(); .rayToWad();
}; };
const calcExpectedStableDebtTokenBalance = ( export const calcExpectedStableDebtTokenBalance = (
userDataBeforeAction: UserReserveData, userDataBeforeAction: UserReserveData,
currentTimestamp: BigNumber currentTimestamp: BigNumber
) => { ) => {

View File

@ -5,8 +5,12 @@ import {APPROVAL_AMOUNT_LENDING_POOL, MOCK_ETH_ADDRESS, oneEther} from '../helpe
import {convertToCurrencyDecimals} from '../helpers/contracts-helpers'; import {convertToCurrencyDecimals} from '../helpers/contracts-helpers';
import {makeSuite} from './helpers/make-suite'; import {makeSuite} from './helpers/make-suite';
import {ProtocolErrors, RateMode} from '../helpers/types'; import {ProtocolErrors, RateMode} from '../helpers/types';
import { calcExpectedVariableDebtTokenBalance } from './helpers/utils/calculations';
import { getUserData, getReserveData } from './helpers/utils/helpers';
const {expect} = require('chai'); const chai = require('chai');
chai.use(require('chai-bignumber')());
const {expect} = chai;
makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => { makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => {
const { const {
@ -42,7 +46,6 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
}); });
//user 2 borrows //user 2 borrows
const userGlobalData = await pool.getUserAccountData(borrower.address); const userGlobalData = await pool.getUserAccountData(borrower.address);
const daiPrice = await oracle.getAssetPrice(dai.address); const daiPrice = await oracle.getAssetPrice(dai.address);
@ -59,7 +62,6 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
.borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0'); .borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0');
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address); const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
console.log('userGlobalDataAfter.healthFactor', userGlobalDataAfter.healthFactor.toString());
expect(userGlobalDataAfter.currentLiquidationThreshold).to.be.bignumber.equal( expect(userGlobalDataAfter.currentLiquidationThreshold).to.be.bignumber.equal(
'80', '80',
@ -78,18 +80,14 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
const daiPrice = await oracle.getAssetPrice(dai.address); const daiPrice = await oracle.getAssetPrice(dai.address);
//halving the price of ETH - means doubling the DAIETH exchange rate
console.log('DAI price before', daiPrice.toString());
await oracle.setAssetPrice( await oracle.setAssetPrice(
dai.address, dai.address,
new BigNumber(daiPrice.toString()).multipliedBy(1.15).toFixed(0) new BigNumber(daiPrice.toString()).multipliedBy(1.15).toFixed(0)
); );
console.log('DAI price after', (await oracle.getAssetPrice(dai.address)).toString());
const userGlobalData = await pool.getUserAccountData(borrower.address); const userGlobalData = await pool.getUserAccountData(borrower.address);
expect(userGlobalData.healthFactor).to.be.bignumber.lt(oneEther.toFixed(0), INVALID_HF); expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
}); });
it('LIQUIDATION - Tries to liquidate a different currency than the loan principal', async () => { it('LIQUIDATION - Tries to liquidate a different currency than the loan principal', async () => {
@ -107,20 +105,17 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
).revertedWith(USER_DID_NOT_BORROW_SPECIFIED); ).revertedWith(USER_DID_NOT_BORROW_SPECIFIED);
}); });
it( it('LIQUIDATION - Tries to liquidate a different collateral than the borrower collateral', async () => {
'LIQUIDATION - Tries to liquidate a different ' + 'collateral than the borrower collateral', const {pool, dai, users} = testEnv;
async () => { const borrower = users[1];
const {pool, dai, users} = testEnv;
const borrower = users[1];
await expect( await expect(
pool.liquidationCall(dai.address, dai.address, borrower.address, oneEther.toString(), true) pool.liquidationCall(dai.address, dai.address, borrower.address, oneEther.toString(), true)
).revertedWith(INVALID_COLLATERAL_TO_LIQUIDATE); ).revertedWith(INVALID_COLLATERAL_TO_LIQUIDATE);
} });
);
it('LIQUIDATION - Liquidates the borrow', async () => { it('LIQUIDATION - Liquidates the borrow', async () => {
const {pool, dai, users, addressesProvider, oracle} = testEnv; const {pool, dai, users, oracle} = testEnv;
const borrower = users[1]; const borrower = users[1];
//mints dai to the caller //mints dai to the caller
@ -130,17 +125,16 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
//approve protocol to access depositor wallet //approve protocol to access depositor wallet
await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
const userReserveDataBefore = await pool.getUserReserveData(dai.address, borrower.address); const daiReserveDataBefore = await getReserveData(pool, dai.address);
const daiReserveDataBefore = await pool.getReserveData(dai.address);
const ethReserveDataBefore = await pool.getReserveData(MOCK_ETH_ADDRESS); const ethReserveDataBefore = await pool.getReserveData(MOCK_ETH_ADDRESS);
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString()) const userReserveDataBefore = await getUserData(pool, dai.address, borrower.address);
.plus(userReserveDataBefore.currentVariableDebt.toString())
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentVariableDebt.toString())
.div(2) .div(2)
.toFixed(0); .toFixed(0);
await pool.liquidationCall( const tx = await pool.liquidationCall(
MOCK_ETH_ADDRESS, MOCK_ETH_ADDRESS,
dai.address, dai.address,
borrower.address, borrower.address,
@ -155,15 +149,15 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
const daiReserveDataAfter = await pool.getReserveData(dai.address); const daiReserveDataAfter = await pool.getReserveData(dai.address);
const ethReserveDataAfter = await pool.getReserveData(MOCK_ETH_ADDRESS); const ethReserveDataAfter = await pool.getReserveData(MOCK_ETH_ADDRESS);
const feeAddress = await addressesProvider.getTokenDistributor();
const feeAddressBalance = await BRE.ethers.provider.getBalance(feeAddress);
const collateralPrice = (await oracle.getAssetPrice(MOCK_ETH_ADDRESS)).toString(); const collateralPrice = (await oracle.getAssetPrice(MOCK_ETH_ADDRESS)).toString();
const principalPrice = (await oracle.getAssetPrice(dai.address)).toString(); const principalPrice = (await oracle.getAssetPrice(dai.address)).toString();
const collateralDecimals = (await pool.getReserveDecimals(MOCK_ETH_ADDRESS)).toString(); const collateralDecimals = (
const principalDecimals = (await pool.getReserveDecimals(dai.address)).toString(); await pool.getReserveConfigurationData(MOCK_ETH_ADDRESS)
).decimals.toString();
const principalDecimals = (
await pool.getReserveConfigurationData(dai.address)
).decimals.toString();
const expectedCollateralLiquidated = new BigNumber(principalPrice) const expectedCollateralLiquidated = new BigNumber(principalPrice)
.times(new BigNumber(amountToLiquidate).times(105)) .times(new BigNumber(amountToLiquidate).times(105))
@ -171,15 +165,26 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
.div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals))) .div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals)))
.decimalPlaces(0, BigNumber.ROUND_DOWN); .decimalPlaces(0, BigNumber.ROUND_DOWN);
expect(userGlobalDataAfter.healthFactor).to.be.bignumber.gt( if(!tx.blockNumber){
expect(false, "Invalid block number");
return;
}
const txTimestamp = new BigNumber((await BRE.ethers.provider.getBlock(tx.blockNumber)).timestamp);
const variableDebtBeforeTx = calcExpectedVariableDebtTokenBalance(
daiReserveDataBefore,
userReserveDataBefore,
txTimestamp
);
expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt(
oneEther.toFixed(0), oneEther.toFixed(0),
'Invalid health factor' 'Invalid health factor'
); );
expect(feeAddressBalance).to.be.bignumber.gt('0');
expect(userReserveDataAfter.currentVariableDebt).to.be.bignumber.almostEqual( expect(userReserveDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
new BigNumber(userReserveDataBefore.currentVariableDebt.toString()) new BigNumber(variableDebtBeforeTx)
.minus(amountToLiquidate) .minus(amountToLiquidate)
.toFixed(0), .toFixed(0),
'Invalid user borrow balance after liquidation' 'Invalid user borrow balance after liquidation'
@ -199,8 +204,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
}); });
it( it(
'User 3 deposits 1000 USDC, user 4 1 ETH,' + 'User 3 deposits 1000 USDC, user 4 1 ETH, user 4 borrows - drops HF, liquidates the borrow',
' user 4 borrows - drops HF, liquidates the borrow',
async () => { async () => {
const {users, pool, usdc, oracle, addressesProvider} = testEnv; const {users, pool, usdc, oracle, addressesProvider} = testEnv;
const depositor = users[3]; const depositor = users[3];
@ -262,7 +266,6 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
const ethReserveDataBefore = await pool.getReserveData(MOCK_ETH_ADDRESS); const ethReserveDataBefore = await pool.getReserveData(MOCK_ETH_ADDRESS);
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString()) const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString())
.plus(userReserveDataBefore.currentStableDebt.toString())
.div(2) .div(2)
.toFixed(0); .toFixed(0);
@ -281,15 +284,15 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
const usdcReserveDataAfter = await pool.getReserveData(usdc.address); const usdcReserveDataAfter = await pool.getReserveData(usdc.address);
const ethReserveDataAfter = await pool.getReserveData(MOCK_ETH_ADDRESS); const ethReserveDataAfter = await pool.getReserveData(MOCK_ETH_ADDRESS);
const feeAddress = await addressesProvider.getTokenDistributor();
const feeAddressBalance = await BRE.ethers.provider.getBalance(feeAddress);
const collateralPrice = (await oracle.getAssetPrice(MOCK_ETH_ADDRESS)).toString(); const collateralPrice = (await oracle.getAssetPrice(MOCK_ETH_ADDRESS)).toString();
const principalPrice = (await oracle.getAssetPrice(usdc.address)).toString(); const principalPrice = (await oracle.getAssetPrice(usdc.address)).toString();
const collateralDecimals = (await pool.getReserveDecimals(MOCK_ETH_ADDRESS)).toString(); const collateralDecimals = (
const principalDecimals = (await pool.getReserveDecimals(usdc.address)).toString(); await pool.getReserveConfigurationData(MOCK_ETH_ADDRESS)
).decimals.toString();
const principalDecimals = (
await pool.getReserveConfigurationData(usdc.address)
).decimals.toString();
const expectedCollateralLiquidated = new BigNumber(principalPrice) const expectedCollateralLiquidated = new BigNumber(principalPrice)
.times(new BigNumber(amountToLiquidate).times(105)) .times(new BigNumber(amountToLiquidate).times(105))
@ -297,21 +300,19 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
.div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals))) .div(new BigNumber(collateralPrice).times(new BigNumber(10).pow(principalDecimals)))
.decimalPlaces(0, BigNumber.ROUND_DOWN); .decimalPlaces(0, BigNumber.ROUND_DOWN);
expect(userGlobalDataAfter.healthFactor).to.be.bignumber.gt( expect(userGlobalDataAfter.healthFactor.toString()).to.be.bignumber.gt(
oneEther.toFixed(0), oneEther.toFixed(0),
'Invalid health factor' 'Invalid health factor'
); );
expect(feeAddressBalance).to.be.bignumber.gt('0'); expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
new BigNumber(userReserveDataBefore.currentStableDebt.toString())
expect(userReserveDataAfter.principalBorrowBalance).to.be.bignumber.almostEqual(
new BigNumber(userReserveDataBefore.currentBorrowBalance.toString())
.minus(amountToLiquidate) .minus(amountToLiquidate)
.toFixed(0), .toFixed(0),
'Invalid user borrow balance after liquidation' 'Invalid user borrow balance after liquidation'
); );
expect(usdcReserveDataAfter.availableLiquidity).to.be.bignumber.almostEqual( expect(usdcReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
new BigNumber(usdcReserveDataBefore.availableLiquidity.toString()) new BigNumber(usdcReserveDataBefore.availableLiquidity.toString())
.plus(amountToLiquidate) .plus(amountToLiquidate)
.toFixed(0), .toFixed(0),

View File

@ -6,6 +6,8 @@ import {convertToCurrencyDecimals} from '../helpers/contracts-helpers';
import {makeSuite} from './helpers/make-suite'; import {makeSuite} from './helpers/make-suite';
import {ProtocolErrors, RateMode} from '../helpers/types'; import {ProtocolErrors, RateMode} from '../helpers/types';
import {borrow} from './helpers/actions'; import {borrow} from './helpers/actions';
import {calcExpectedStableDebtTokenBalance} from './helpers/utils/calculations';
import { getUserData } from './helpers/utils/helpers';
const chai = require('chai'); const chai = require('chai');
chai.use(require('chai-bignumber')()); chai.use(require('chai-bignumber')());
@ -115,34 +117,31 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
}); });
it('LIQUIDATION - Liquidates the borrow', async () => { it('LIQUIDATION - Liquidates the borrow', async () => {
const {dai, users, pool, oracle, addressesProvider} = testEnv; const {dai, users, pool, oracle} = testEnv;
const liquidator = users[3]; const liquidator = users[3];
const borrower = users[1]; const borrower = users[1];
//mints dai to the caller //mints dai to the liquidator
await dai.mint(await convertToCurrencyDecimals(dai.address, '1000')); await dai.connect(liquidator.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
//approve protocol to access depositor wallet //approve protocol to access the liquidator wallet
await dai.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); await dai.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
const userReserveDataBefore: any = await pool.getUserReserveData(dai.address, borrower.address);
const daiReserveDataBefore: any = await pool.getReserveData(dai.address); const daiReserveDataBefore: any = await pool.getReserveData(dai.address);
const ethReserveDataBefore: any = await pool.getReserveData(MOCK_ETH_ADDRESS); const ethReserveDataBefore: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentBorrowBalance) const userReserveDataBefore: any = await getUserData(pool, dai.address, borrower.address);
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt)
.div(2) .div(2)
.toFixed(0); .toFixed(0);
await pool.liquidationCall( const tx = await pool
MOCK_ETH_ADDRESS, .connect(liquidator.signer)
dai.address, .liquidationCall(MOCK_ETH_ADDRESS, dai.address, borrower.address, amountToLiquidate, false);
borrower.address,
amountToLiquidate,
false
);
const userReserveDataAfter: any = await pool.getUserReserveData(dai.address, borrower.address); const userReserveDataAfter: any = await getUserData(pool, dai.address, borrower.address);
const daiReserveDataAfter: any = await pool.getReserveData(dai.address); const daiReserveDataAfter: any = await pool.getReserveData(dai.address);
const ethReserveDataAfter: any = await pool.getReserveData(MOCK_ETH_ADDRESS); const ethReserveDataAfter: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
@ -150,8 +149,12 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
const collateralPrice = await oracle.getAssetPrice(MOCK_ETH_ADDRESS); const collateralPrice = await oracle.getAssetPrice(MOCK_ETH_ADDRESS);
const principalPrice = await oracle.getAssetPrice(dai.address); const principalPrice = await oracle.getAssetPrice(dai.address);
const collateralDecimals = await pool.getReserveDecimals(MOCK_ETH_ADDRESS); const collateralDecimals = (
const principalDecimals = await pool.getReserveDecimals(dai.address); await pool.getReserveConfigurationData(MOCK_ETH_ADDRESS)
).decimals.toString();
const principalDecimals = (
await pool.getReserveConfigurationData(dai.address)
).decimals.toString();
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString()) const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
.times(new BigNumber(amountToLiquidate).times(105)) .times(new BigNumber(amountToLiquidate).times(105))
@ -162,29 +165,24 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
.div(100) .div(100)
.decimalPlaces(0, BigNumber.ROUND_DOWN); .decimalPlaces(0, BigNumber.ROUND_DOWN);
const expectedFeeLiquidated = new BigNumber(principalPrice.toString()) if (!tx.blockNumber) {
.times(new BigNumber(userReserveDataBefore.originationFee).times(105)) expect(false, 'Invalid block number');
.times(new BigNumber(10).pow(collateralDecimals)) return;
.div( }
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) const txTimestamp = new BigNumber(
) (await BRE.ethers.provider.getBlock(tx.blockNumber)).timestamp
.div(100)
.decimalPlaces(0, BigNumber.ROUND_DOWN);
const feeAddress = await addressesProvider.getTokenDistributor();
const feeAddressBalance = await BRE.ethers.provider.getBalance(feeAddress);
expect(userReserveDataAfter.originationFee.toString()).to.be.bignumber.eq(
'0',
'Origination fee should be repaid'
); );
expect(feeAddressBalance).to.be.bignumber.gt('0'); const stableDebtBeforeTx = calcExpectedStableDebtTokenBalance(
userReserveDataBefore,
txTimestamp.plus(2)
);
expect(userReserveDataAfter.principalBorrowBalance.toString()).to.be.bignumber.almostEqual( console.log("debt: ", stableDebtBeforeTx.toFixed(), userReserveDataBefore.currentStableDebt.toFixed(), userReserveDataAfter.currentStableDebt.toString())
new BigNumber(userReserveDataBefore.currentBorrowBalance).minus(amountToLiquidate).toFixed(0),
'Invalid user borrow balance after liquidation' expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
new BigNumber(stableDebtBeforeTx).minus(amountToLiquidate).toFixed(0),
'Invalid user debt after liquidation'
); );
expect(daiReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( expect(daiReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
@ -192,9 +190,10 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
'Invalid principal available liquidity' 'Invalid principal available liquidity'
); );
console.log('eth liquidity: ', daiReserveDataAfter.availableLiquidity.toString());
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
new BigNumber(ethReserveDataBefore.availableLiquidity) new BigNumber(ethReserveDataBefore.availableLiquidity)
.minus(expectedFeeLiquidated)
.minus(expectedCollateralLiquidated) .minus(expectedCollateralLiquidated)
.toFixed(0), .toFixed(0),
'Invalid collateral available liquidity' 'Invalid collateral available liquidity'
@ -208,7 +207,6 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
const borrower = users[4]; const borrower = users[4];
const liquidator = users[5]; const liquidator = users[5];
//mints USDC to depositor //mints USDC to depositor
await usdc await usdc
.connect(depositor.signer) .connect(depositor.signer)
@ -236,32 +234,47 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
const amountUSDCToBorrow = await convertToCurrencyDecimals( const amountUSDCToBorrow = await convertToCurrencyDecimals(
usdc.address, usdc.address,
new BigNumber(userGlobalData.availableBorrowsETH).div(usdcPrice.toString()).multipliedBy(0.95).toFixed(0) new BigNumber(userGlobalData.availableBorrowsETH)
.div(usdcPrice.toString())
.multipliedBy(0.95)
.toFixed(0)
); );
await pool.connect(borrower.signer).borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0'); await pool
.connect(borrower.signer)
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0');
//drops HF below 1 //drops HF below 1
await oracle.setAssetPrice(usdc.address, new BigNumber(usdcPrice.toString()).multipliedBy(1.2).toFixed(0)); await oracle.setAssetPrice(
usdc.address,
new BigNumber(usdcPrice.toString()).multipliedBy(1.2).toFixed(0)
);
//mints dai to the liquidator //mints dai to the liquidator
await usdc.connect(liquidator.signer).mint(await convertToCurrencyDecimals(usdc.address, '1000')); await usdc
.connect(liquidator.signer)
.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
//approve protocol to access depositor wallet //approve protocol to access depositor wallet
await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); await usdc.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
const userReserveDataBefore: any = await pool.getUserReserveData(usdc.address, borrower.address); const userReserveDataBefore: any = await pool.getUserReserveData(
usdc.address,
borrower.address
);
const usdcReserveDataBefore: any = await pool.getReserveData(usdc.address); const usdcReserveDataBefore: any = await pool.getReserveData(usdc.address);
const ethReserveDataBefore: any = await pool.getReserveData(MOCK_ETH_ADDRESS); const ethReserveDataBefore: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentBorrowBalance) const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt)
.div(2) .div(2)
.decimalPlaces(0, BigNumber.ROUND_DOWN) .decimalPlaces(0, BigNumber.ROUND_DOWN)
.toFixed(0); .toFixed(0);
await pool.connect(liquidator.signer).liquidationCall(MOCK_ETH_ADDRESS, usdc.address, borrower.address, amountToLiquidate, false); await pool
.connect(liquidator.signer)
.liquidationCall(MOCK_ETH_ADDRESS, usdc.address, borrower.address, amountToLiquidate, false);
const userReserveDataAfter: any = await pool.getUserReserveData(usdc.address, borrower.address); const userReserveDataAfter: any = await pool.getUserReserveData(usdc.address, borrower.address);
@ -270,27 +283,18 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
const usdcReserveDataAfter: any = await pool.getReserveData(usdc.address); const usdcReserveDataAfter: any = await pool.getReserveData(usdc.address);
const ethReserveDataAfter: any = await pool.getReserveData(MOCK_ETH_ADDRESS); const ethReserveDataAfter: any = await pool.getReserveData(MOCK_ETH_ADDRESS);
const feeAddress = await addressesProvider.getTokenDistributor();
const feeAddressBalance = await BRE.ethers.provider.getBalance(feeAddress);
const collateralPrice = await oracle.getAssetPrice(MOCK_ETH_ADDRESS); const collateralPrice = await oracle.getAssetPrice(MOCK_ETH_ADDRESS);
const principalPrice = await oracle.getAssetPrice(usdc.address); const principalPrice = await oracle.getAssetPrice(usdc.address);
const collateralDecimals = await pool.getReserveDecimals(MOCK_ETH_ADDRESS); const collateralDecimals = (await pool.getReserveConfigurationData(MOCK_ETH_ADDRESS)).decimals.toString();
const principalDecimals = await pool.getReserveDecimals(usdc.address); const principalDecimals = (await pool.getReserveConfigurationData(usdc.address)).decimals.toString();
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString()) const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
.times(new BigNumber(amountToLiquidate).times(105)) .times(new BigNumber(amountToLiquidate).times(105))
.times(new BigNumber(10).pow(collateralDecimals)) .times(new BigNumber(10).pow(collateralDecimals))
.div(new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))) .div(
.div(100) new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
.decimalPlaces(0, BigNumber.ROUND_DOWN); )
const expectedFeeLiquidated = new BigNumber(principalPrice.toString())
.times(new BigNumber(userReserveDataBefore.originationFee).times(105))
.times(new BigNumber(10).pow(collateralDecimals))
.div(new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals)))
.div(100) .div(100)
.decimalPlaces(0, BigNumber.ROUND_DOWN); .decimalPlaces(0, BigNumber.ROUND_DOWN);
@ -299,15 +303,16 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
'Invalid health factor' 'Invalid health factor'
); );
expect(userReserveDataAfter.originationFee.toString()).to.be.bignumber.eq( console.log(
'0', 'Debt: ',
'Origination fee should be repaid' userReserveDataAfter.currentStableDebt.toString(),
new BigNumber(userReserveDataBefore.currentStableDebt.toString())
.minus(amountToLiquidate)
.toFixed(0)
); );
expect(feeAddressBalance.toString()).to.be.bignumber.gt('0'); expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
new BigNumber(userReserveDataBefore.currentStableDebt.toString())
expect(userReserveDataAfter.principalBorrowBalance.toString()).to.be.bignumber.almostEqual(
new BigNumber(userReserveDataBefore.currentBorrowBalance.toString())
.minus(amountToLiquidate) .minus(amountToLiquidate)
.toFixed(0), .toFixed(0),
'Invalid user borrow balance after liquidation' 'Invalid user borrow balance after liquidation'
@ -318,9 +323,16 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
'Invalid principal available liquidity' 'Invalid principal available liquidity'
); );
console.log(
'Debt: ',
usdcReserveDataAfter.availableLiquidity.toString(),
new BigNumber(usdcReserveDataBefore.availableLiquidity.toString())
.plus(amountToLiquidate)
.toFixed(0)
);
expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual( expect(ethReserveDataAfter.availableLiquidity.toString()).to.be.bignumber.almostEqual(
new BigNumber(ethReserveDataBefore.availableLiquidity) new BigNumber(ethReserveDataBefore.availableLiquidity)
.minus(expectedFeeLiquidated)
.minus(expectedCollateralLiquidated) .minus(expectedCollateralLiquidated)
.toFixed(0), .toFixed(0),
'Invalid collateral available liquidity' 'Invalid collateral available liquidity'
@ -328,7 +340,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
}); });
it('User 4 deposits 1000 LEND - drops HF, liquidates the LEND, which results on a lower amount being liquidated', async () => { it('User 4 deposits 1000 LEND - drops HF, liquidates the LEND, which results on a lower amount being liquidated', async () => {
const {lend, usdc, users, pool, oracle, addressesProvider} = testEnv; const {lend, usdc, users, pool, oracle} = testEnv;
const depositor = users[3]; const depositor = users[3];
const borrower = users[4]; const borrower = users[4];
@ -347,20 +359,26 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
const usdcPrice = await oracle.getAssetPrice(usdc.address); const usdcPrice = await oracle.getAssetPrice(usdc.address);
//drops HF below 1 //drops HF below 1
await oracle.setAssetPrice(usdc.address, new BigNumber(usdcPrice.toString()).multipliedBy(1.1).toFixed(0)); await oracle.setAssetPrice(
usdc.address,
new BigNumber(usdcPrice.toString()).multipliedBy(1.12).toFixed(0)
);
//mints usdc to the liquidator //mints usdc to the liquidator
await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000')); await usdc.connect(liquidator.signer).mint(await convertToCurrencyDecimals(usdc.address, '1000'));
//approve protocol to access depositor wallet //approve protocol to access depositor wallet
await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); await usdc.connect(liquidator.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
const userReserveDataBefore: any = await pool.getUserReserveData(usdc.address, borrower.address); const userReserveDataBefore: any = await pool.getUserReserveData(
usdc.address,
borrower.address
);
const usdcReserveDataBefore: any = await pool.getReserveData(usdc.address); const usdcReserveDataBefore: any = await pool.getReserveData(usdc.address);
const lendReserveDataBefore: any = await pool.getReserveData(lend.address); const lendReserveDataBefore: any = await pool.getReserveData(lend.address);
const amountToLiquidate = new BigNumber(userReserveDataBefore.currentBorrowBalance) const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt)
.div(2) .div(2)
.decimalPlaces(0, BigNumber.ROUND_DOWN) .decimalPlaces(0, BigNumber.ROUND_DOWN)
.toFixed(0); .toFixed(0);
@ -368,13 +386,9 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
const collateralPrice = await oracle.getAssetPrice(lend.address); const collateralPrice = await oracle.getAssetPrice(lend.address);
const principalPrice = await oracle.getAssetPrice(usdc.address); const principalPrice = await oracle.getAssetPrice(usdc.address);
await pool.connect(liquidator.signer).liquidationCall( await pool
lend.address, .connect(liquidator.signer)
usdc.address, .liquidationCall(lend.address, usdc.address, borrower.address, amountToLiquidate, false);
borrower.address,
amountToLiquidate,
false
);
const userReserveDataAfter: any = await pool.getUserReserveData(usdc.address, borrower.address); const userReserveDataAfter: any = await pool.getUserReserveData(usdc.address, borrower.address);
@ -383,17 +397,19 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
const usdcReserveDataAfter: any = await pool.getReserveData(usdc.address); const usdcReserveDataAfter: any = await pool.getReserveData(usdc.address);
const lendReserveDataAfter: any = await pool.getReserveData(lend.address); const lendReserveDataAfter: any = await pool.getReserveData(lend.address);
const collateralDecimals = await pool.getReserveDecimals(lend.address); const collateralDecimals = (await pool.getReserveConfigurationData(lend.address)).decimals.toString();
const principalDecimals = await pool.getReserveDecimals(usdc.address); const principalDecimals = (await pool.getReserveConfigurationData(usdc.address)).decimals.toString();
const expectedCollateralLiquidated = oneEther.multipliedBy('1000'); const expectedCollateralLiquidated = oneEther.multipliedBy('1000');
const liquidationBonus = await pool.getReserveLiquidationBonus(lend.address); const liquidationBonus = (await pool.getReserveConfigurationData(lend.address)).liquidationBonus.toString();
const expectedPrincipal = new BigNumber(collateralPrice.toString()) const expectedPrincipal = new BigNumber(collateralPrice.toString())
.times(expectedCollateralLiquidated) .times(expectedCollateralLiquidated)
.times(new BigNumber(10).pow(principalDecimals)) .times(new BigNumber(10).pow(principalDecimals))
.div(new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) .div(
new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
)
.times(100) .times(100)
.div(liquidationBonus.toString()) .div(liquidationBonus.toString())
.decimalPlaces(0, BigNumber.ROUND_DOWN); .decimalPlaces(0, BigNumber.ROUND_DOWN);
@ -403,13 +419,8 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
'Invalid health factor' 'Invalid health factor'
); );
expect(userReserveDataAfter.originationFee.toString()).to.be.bignumber.eq( expect(userReserveDataAfter.currentStableDebt.toString()).to.be.bignumber.almostEqual(
'0', new BigNumber(userReserveDataBefore.currentStableDebt).minus(expectedPrincipal).toFixed(0),
'Origination fee should be repaid'
);
expect(userReserveDataAfter.principalBorrowBalance.toString()).to.be.bignumber.almostEqual(
new BigNumber(userReserveDataBefore.currentBorrowBalance).minus(expectedPrincipal).toFixed(0),
'Invalid user borrow balance after liquidation' 'Invalid user borrow balance after liquidation'
); );

View File

@ -12,7 +12,7 @@ BigNumber.config({DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN});
const scenarioFolder = './test/helpers/scenarios/'; const scenarioFolder = './test/helpers/scenarios/';
const selectedScenarios: string[] = []; const selectedScenarios: string[] = ['borrow-repay-variable.json'];
fs.readdirSync(scenarioFolder).forEach((file) => { fs.readdirSync(scenarioFolder).forEach((file) => {
if (selectedScenarios.length > 0 && !selectedScenarios.includes(file)) return; if (selectedScenarios.length > 0 && !selectedScenarios.includes(file)) return;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -385,6 +385,7 @@ export class LendingPool extends Contract {
getReserveConfigurationData( getReserveConfigurationData(
_reserve: string _reserve: string
): Promise<{ ): Promise<{
decimals: BigNumber;
ltv: BigNumber; ltv: BigNumber;
liquidationThreshold: BigNumber; liquidationThreshold: BigNumber;
liquidationBonus: BigNumber; liquidationBonus: BigNumber;
@ -398,13 +399,14 @@ export class LendingPool extends Contract {
0: BigNumber; 0: BigNumber;
1: BigNumber; 1: BigNumber;
2: BigNumber; 2: BigNumber;
3: string; 3: BigNumber;
4: string; 4: string;
5: boolean; 5: string;
6: boolean; 6: boolean;
7: boolean; 7: boolean;
8: boolean; 8: boolean;
9: boolean; 9: boolean;
10: boolean;
}>; }>;
getReserveData( getReserveData(
@ -650,6 +652,7 @@ export class LendingPool extends Contract {
getReserveConfigurationData( getReserveConfigurationData(
_reserve: string _reserve: string
): Promise<{ ): Promise<{
decimals: BigNumber;
ltv: BigNumber; ltv: BigNumber;
liquidationThreshold: BigNumber; liquidationThreshold: BigNumber;
liquidationBonus: BigNumber; liquidationBonus: BigNumber;
@ -663,13 +666,14 @@ export class LendingPool extends Contract {
0: BigNumber; 0: BigNumber;
1: BigNumber; 1: BigNumber;
2: BigNumber; 2: BigNumber;
3: string; 3: BigNumber;
4: string; 4: string;
5: boolean; 5: string;
6: boolean; 6: boolean;
7: boolean; 7: boolean;
8: boolean; 8: boolean;
9: boolean; 9: boolean;
10: boolean;
}>; }>;
getReserveData( getReserveData(

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -37,7 +37,6 @@ interface LendingPoolLiquidationManagerInterface extends Interface {
_user, _user,
_purchaseAmount, _purchaseAmount,
_liquidatedCollateralAmount, _liquidatedCollateralAmount,
_accruedBorrowInterest,
_liquidator, _liquidator,
_receiveAToken, _receiveAToken,
_timestamp _timestamp
@ -49,25 +48,6 @@ interface LendingPoolLiquidationManagerInterface extends Interface {
null, null,
null, null,
null, null,
null,
null
]): string[];
}>;
OriginationFeeLiquidated: TypedEventDescription<{
encodeTopics([
_collateral,
_reserve,
_user,
_feeLiquidated,
_liquidatedCollateralForFee,
_timestamp
]: [
string | null,
string | null,
string | null,
null,
null,
null null
]): string[]; ]): string[];
}>; }>;
@ -138,20 +118,10 @@ export class LendingPoolLiquidationManager extends Contract {
_user: string | null, _user: string | null,
_purchaseAmount: null, _purchaseAmount: null,
_liquidatedCollateralAmount: null, _liquidatedCollateralAmount: null,
_accruedBorrowInterest: null,
_liquidator: null, _liquidator: null,
_receiveAToken: null, _receiveAToken: null,
_timestamp: null _timestamp: null
): EventFilter; ): EventFilter;
OriginationFeeLiquidated(
_collateral: string | null,
_reserve: string | null,
_user: string | null,
_feeLiquidated: null,
_liquidatedCollateralForFee: null,
_timestamp: null
): EventFilter;
}; };
estimate: { estimate: {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -134,4 +134,4 @@ const _abi = [
]; ];
const _bytecode = const _bytecode =
"0x608060405234801561001057600080fd5b5060405161099e38038061099e8339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055610939806100656000396000f3fe6080604052600436106100385760003560e01c80639e3c930914610083578063b59b28ef1461014f578063f7888aec146102d35761007e565b3661007e5761004633610320565b61007c576040805162461bcd60e51b8152602060048201526002602482015261191960f11b604482015290519081900360640190fd5b005b600080fd5b34801561008f57600080fd5b506100b6600480360360208110156100a657600080fd5b50356001600160a01b031661035c565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100fa5781810151838201526020016100e2565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015610139578181015183820152602001610121565b5050505090500194505050505060405180910390f35b34801561015b57600080fd5b506102836004803603604081101561017257600080fd5b81019060208101813564010000000081111561018d57600080fd5b82018360208201111561019f57600080fd5b803590602001918460208302840111640100000000831117156101c157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561021157600080fd5b82018360208201111561022357600080fd5b8035906020019184602083028401116401000000008311171561024557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506106a9945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156102bf5781810151838201526020016102a7565b505050509050019250505060405180910390f35b3480156102df57600080fd5b5061030e600480360360408110156102f657600080fd5b506001600160a01b0381358116916020013516610841565b60408051918252519081900360200190f35b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061035457508115155b949350505050565b60608060008060009054906101000a90046001600160a01b03166001600160a01b0316630261bf8b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156103ae57600080fd5b505afa1580156103c2573d6000803e3d6000fd5b505050506040513d60208110156103d857600080fd5b505160408051630240bc6b60e21b815290519192506060916001600160a01b03841691630902f1ac916004808301926000929190829003018186803b15801561042057600080fd5b505afa158015610434573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561045d57600080fd5b810190808051604051939291908464010000000082111561047d57600080fd5b90830190602082018581111561049257600080fd5b82518660208202830111640100000000821117156104af57600080fd5b82525081516020918201928201910280838360005b838110156104dc5781810151838201526020016104c4565b5050505090500160405250505090506060815167ffffffffffffffff8111801561050557600080fd5b5060405190808252806020026020018201604052801561052f578160200160208202803683370190505b50905060005b825181101561069d576000846001600160a01b0316633e15014185848151811061055b57fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b031681526020019150506101406040518083038186803b1580156105aa57600080fd5b505afa1580156105be573d6000803e3d6000fd5b505050506040513d6101408110156105d557600080fd5b5061010001519050806106025760008383815181106105f057fe5b60200260200101818152505050610695565b61060a6108eb565b6001600160a01b031684838151811061061f57fe5b60200260200101516001600160a01b03161461066f576106528885848151811061064557fe5b6020026020010151610841565b83838151811061065e57fe5b602002602001018181525050610693565b876001600160a01b03163183838151811061068657fe5b6020026020010181815250505b505b600101610535565b50909350915050915091565b606080825184510267ffffffffffffffff811180156106c757600080fd5b506040519080825280602002602001820160405280156106f1578160200160208202803683370190505b50905060005b84518110156108375760005b845181101561082e57845182026107186108eb565b6001600160a01b031686838151811061072d57fe5b60200260200101516001600160a01b031614156107815786838151811061075057fe5b60200260200101516001600160a01b031631848383018151811061077057fe5b602002602001018181525050610825565b6107a686838151811061079057fe5b60200260200101516001600160a01b0316610320565b6107e7576040805162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22faa27a5a2a760991b604482015290519081900360640190fd5b61080a8784815181106107f657fe5b602002602001015187848151811061064557fe5b848383018151811061081857fe5b6020026020010181815250505b50600101610703565b506001016106f7565b5090505b92915050565b6000610855826001600160a01b0316610320565b156108e357816001600160a01b03166370a08231846040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156108b057600080fd5b505afa1580156108c4573d6000803e3d6000fd5b505050506040513d60208110156108da57600080fd5b5051905061083b565b50600061083b565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9056fea2646970667358221220690f7ecb9ab912f052e8d44a9a4f27007b3b6f0119cdc6270033c06dbc83a4e664736f6c63430006080033"; "0x608060405234801561001057600080fd5b5060405161099e38038061099e8339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055610939806100656000396000f3fe6080604052600436106100385760003560e01c80639e3c930914610083578063b59b28ef1461014f578063f7888aec146102d35761007e565b3661007e5761004633610320565b61007c576040805162461bcd60e51b8152602060048201526002602482015261191960f11b604482015290519081900360640190fd5b005b600080fd5b34801561008f57600080fd5b506100b6600480360360208110156100a657600080fd5b50356001600160a01b031661035c565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100fa5781810151838201526020016100e2565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015610139578181015183820152602001610121565b5050505090500194505050505060405180910390f35b34801561015b57600080fd5b506102836004803603604081101561017257600080fd5b81019060208101813564010000000081111561018d57600080fd5b82018360208201111561019f57600080fd5b803590602001918460208302840111640100000000831117156101c157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561021157600080fd5b82018360208201111561022357600080fd5b8035906020019184602083028401116401000000008311171561024557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506106a9945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156102bf5781810151838201526020016102a7565b505050509050019250505060405180910390f35b3480156102df57600080fd5b5061030e600480360360408110156102f657600080fd5b506001600160a01b0381358116916020013516610841565b60408051918252519081900360200190f35b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061035457508115155b949350505050565b60608060008060009054906101000a90046001600160a01b03166001600160a01b0316630261bf8b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156103ae57600080fd5b505afa1580156103c2573d6000803e3d6000fd5b505050506040513d60208110156103d857600080fd5b505160408051630240bc6b60e21b815290519192506060916001600160a01b03841691630902f1ac916004808301926000929190829003018186803b15801561042057600080fd5b505afa158015610434573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561045d57600080fd5b810190808051604051939291908464010000000082111561047d57600080fd5b90830190602082018581111561049257600080fd5b82518660208202830111640100000000821117156104af57600080fd5b82525081516020918201928201910280838360005b838110156104dc5781810151838201526020016104c4565b5050505090500160405250505090506060815167ffffffffffffffff8111801561050557600080fd5b5060405190808252806020026020018201604052801561052f578160200160208202803683370190505b50905060005b825181101561069d576000846001600160a01b0316633e15014185848151811061055b57fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b031681526020019150506101606040518083038186803b1580156105aa57600080fd5b505afa1580156105be573d6000803e3d6000fd5b505050506040513d6101608110156105d557600080fd5b5061012001519050806106025760008383815181106105f057fe5b60200260200101818152505050610695565b61060a6108eb565b6001600160a01b031684838151811061061f57fe5b60200260200101516001600160a01b03161461066f576106528885848151811061064557fe5b6020026020010151610841565b83838151811061065e57fe5b602002602001018181525050610693565b876001600160a01b03163183838151811061068657fe5b6020026020010181815250505b505b600101610535565b50909350915050915091565b606080825184510267ffffffffffffffff811180156106c757600080fd5b506040519080825280602002602001820160405280156106f1578160200160208202803683370190505b50905060005b84518110156108375760005b845181101561082e57845182026107186108eb565b6001600160a01b031686838151811061072d57fe5b60200260200101516001600160a01b031614156107815786838151811061075057fe5b60200260200101516001600160a01b031631848383018151811061077057fe5b602002602001018181525050610825565b6107a686838151811061079057fe5b60200260200101516001600160a01b0316610320565b6107e7576040805162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22faa27a5a2a760991b604482015290519081900360640190fd5b61080a8784815181106107f657fe5b602002602001015187848151811061064557fe5b848383018151811061081857fe5b6020026020010181815250505b50600101610703565b506001016106f7565b5090505b92915050565b6000610855826001600160a01b0316610320565b156108e357816001600160a01b03166370a08231846040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156108b057600080fd5b505afa1580156108c4573d6000803e3d6000fd5b505050506040513d60208110156108da57600080fd5b5051905061083b565b50600061083b565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9056fea2646970667358221220fa706074cbd7cd86464474ec2c51c52dff3f0f34b17906e75605b4357a7d4dbe64736f6c63430006080033";