mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
feat: added ability to liquidate the full loan
This commit is contained in:
parent
5a33536b5f
commit
96cb9b3d50
|
@ -18,9 +18,10 @@ import {PercentageMath} from '../libraries/math/PercentageMath.sol';
|
|||
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {Errors} from '../libraries/helpers/Errors.sol';
|
||||
import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol';
|
||||
import {GenericLogic} from '../libraries/logic/GenericLogic.sol';
|
||||
import {DataTypes} from '../libraries/types/DataTypes.sol';
|
||||
import {LendingPoolStorage} from './LendingPoolStorage.sol';
|
||||
|
||||
import "hardhat/console.sol";
|
||||
/**
|
||||
* @title LendingPoolCollateralManager contract
|
||||
* @author Aave
|
||||
|
@ -39,7 +40,10 @@ contract LendingPoolCollateralManager is
|
|||
using PercentageMath for uint256;
|
||||
using ReserveLogic for DataTypes.ReserveCache;
|
||||
|
||||
uint256 internal constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000;
|
||||
uint256 public constant STD_LIQUIDATION_CLOSE_FACTOR = 5000;
|
||||
uint256 public constant MAX_LIQUIDATION_CLOSE_FACTOR = 10000;
|
||||
|
||||
uint256 public constant CLOSE_FACTOR_HF_THRESHOLD = 0.95 * 1e18;
|
||||
|
||||
struct LiquidationCallLocalVars {
|
||||
uint256 userCollateralBalance;
|
||||
|
@ -54,6 +58,7 @@ contract LendingPoolCollateralManager is
|
|||
uint256 debtAmountNeeded;
|
||||
uint256 healthFactor;
|
||||
uint256 liquidatorPreviousATokenBalance;
|
||||
uint256 closeFactor;
|
||||
IAToken collateralAtoken;
|
||||
IPriceOracleGetter oracle;
|
||||
bool isCollateralEnabled;
|
||||
|
@ -96,14 +101,10 @@ contract LendingPoolCollateralManager is
|
|||
|
||||
LiquidationCallLocalVars memory vars;
|
||||
|
||||
|
||||
(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve);
|
||||
vars.oracle = IPriceOracleGetter(_addressesProvider.getPriceOracle());
|
||||
vars.oracle = IPriceOracleGetter(_addressesProvider.getPriceOracle());
|
||||
|
||||
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall(
|
||||
collateralReserve,
|
||||
debtReserveCache,
|
||||
vars.userStableDebt.add(vars.userVariableDebt),
|
||||
(, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
|
||||
user,
|
||||
_reserves,
|
||||
userConfig,
|
||||
|
@ -112,6 +113,14 @@ contract LendingPoolCollateralManager is
|
|||
address(vars.oracle)
|
||||
);
|
||||
|
||||
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall(
|
||||
collateralReserve,
|
||||
debtReserveCache,
|
||||
vars.userStableDebt.add(vars.userVariableDebt),
|
||||
userConfig,
|
||||
vars.healthFactor
|
||||
);
|
||||
|
||||
if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) {
|
||||
return (vars.errorCode, vars.errorMsg);
|
||||
}
|
||||
|
@ -120,14 +129,24 @@ contract LendingPoolCollateralManager is
|
|||
|
||||
vars.userCollateralBalance = vars.collateralAtoken.balanceOf(user);
|
||||
|
||||
vars.closeFactor = vars.healthFactor > CLOSE_FACTOR_HF_THRESHOLD
|
||||
? STD_LIQUIDATION_CLOSE_FACTOR
|
||||
: MAX_LIQUIDATION_CLOSE_FACTOR;
|
||||
|
||||
vars.maxLiquidatableDebt = vars.userStableDebt.add(vars.userVariableDebt).percentMul(
|
||||
LIQUIDATION_CLOSE_FACTOR_PERCENT
|
||||
vars.closeFactor
|
||||
);
|
||||
|
||||
console.log("close factor is ", vars.closeFactor);
|
||||
|
||||
|
||||
vars.actualDebtToLiquidate = debtToCover > vars.maxLiquidatableDebt
|
||||
? vars.maxLiquidatableDebt
|
||||
: debtToCover;
|
||||
|
||||
console.log("actual debt to liquidate: ", vars.actualDebtToLiquidate);
|
||||
console.log("Debt to cover", debtToCover);
|
||||
|
||||
(
|
||||
vars.maxCollateralToLiquidate,
|
||||
vars.debtAmountNeeded
|
||||
|
@ -163,25 +182,30 @@ contract LendingPoolCollateralManager is
|
|||
}
|
||||
|
||||
debtReserve.updateState(debtReserveCache);
|
||||
console.log("Updated debt reserve state");
|
||||
|
||||
if (vars.userVariableDebt >= vars.actualDebtToLiquidate) {
|
||||
console.log("Burning variable debt");
|
||||
IVariableDebtToken(debtReserveCache.variableDebtTokenAddress).burn(
|
||||
user,
|
||||
vars.actualDebtToLiquidate,
|
||||
debtReserveCache.nextVariableBorrowIndex
|
||||
);
|
||||
console.log("variable debt burned");
|
||||
debtReserveCache.refreshDebt(0, 0, 0, vars.actualDebtToLiquidate);
|
||||
debtReserve.updateInterestRates(debtReserveCache, debtAsset, vars.actualDebtToLiquidate, 0);
|
||||
} else {
|
||||
// If the user doesn't have variable debt, no need to try to burn variable debt tokens
|
||||
if (vars.userVariableDebt > 0) {
|
||||
console.log("Burning variable debt");
|
||||
IVariableDebtToken(debtReserveCache.variableDebtTokenAddress).burn(
|
||||
user,
|
||||
vars.userVariableDebt,
|
||||
debtReserveCache.nextVariableBorrowIndex
|
||||
);
|
||||
}
|
||||
IStableDebtToken(debtReserveCache.stableDebtTokenAddress).burn(
|
||||
console.log("variable debt burned, burning stable");
|
||||
IStableDebtToken(debtReserveCache.stableDebtTokenAddress).burn(
|
||||
user,
|
||||
vars.actualDebtToLiquidate.sub(vars.userVariableDebt)
|
||||
);
|
||||
|
@ -191,6 +215,7 @@ contract LendingPoolCollateralManager is
|
|||
0,
|
||||
vars.userVariableDebt
|
||||
);
|
||||
console.log("stable debt burned");
|
||||
|
||||
debtReserve.updateInterestRates(debtReserveCache, debtAsset, vars.actualDebtToLiquidate, 0);
|
||||
}
|
||||
|
@ -230,6 +255,7 @@ contract LendingPoolCollateralManager is
|
|||
emit ReserveUsedAsCollateralDisabled(collateralAsset, user);
|
||||
}
|
||||
|
||||
console.log("transferring debt");
|
||||
// Transfers the debt asset being repaid to the aToken, where the liquidity is kept
|
||||
IERC20(debtAsset).safeTransferFrom(
|
||||
msg.sender,
|
||||
|
@ -237,6 +263,8 @@ contract LendingPoolCollateralManager is
|
|||
vars.actualDebtToLiquidate
|
||||
);
|
||||
|
||||
console.log("debt transferred");
|
||||
|
||||
emit LiquidationCall(
|
||||
collateralAsset,
|
||||
debtAsset,
|
||||
|
|
|
@ -417,7 +417,6 @@ library ValidationLogic {
|
|||
}
|
||||
|
||||
struct ValidateLiquidationCallLocalVars {
|
||||
uint256 healthFactor;
|
||||
bool collateralReserveActive;
|
||||
bool collateralReservePaused;
|
||||
bool principalReserveActive;
|
||||
|
@ -431,23 +430,15 @@ library ValidationLogic {
|
|||
* @param principalReserveCache The cached reserve data of the principal
|
||||
* @param userConfig The user configuration
|
||||
* @param totalDebt Total debt balance of the user
|
||||
* @param user The address of the user being liquidated
|
||||
* @param reservesData The mapping of the reserves data
|
||||
* @param userConfig The user configuration mapping
|
||||
* @param reserves The list of the reserves
|
||||
* @param reservesCount The number of reserves in the list
|
||||
* @param oracle The address of the price oracle
|
||||
* @param healthFactor The health factor of the loan
|
||||
**/
|
||||
function validateLiquidationCall(
|
||||
DataTypes.ReserveData storage collateralReserve,
|
||||
DataTypes.ReserveCache memory principalReserveCache,
|
||||
uint256 totalDebt,
|
||||
address user,
|
||||
mapping(address => DataTypes.ReserveData) storage reservesData,
|
||||
DataTypes.UserConfigurationMap storage userConfig,
|
||||
mapping(uint256 => address) storage reserves,
|
||||
uint256 reservesCount,
|
||||
address oracle
|
||||
DataTypes.UserConfigurationMap memory userConfig,
|
||||
uint256 healthFactor
|
||||
) internal view returns (uint256, string memory) {
|
||||
ValidateLiquidationCallLocalVars memory vars;
|
||||
|
||||
|
@ -468,17 +459,8 @@ library ValidationLogic {
|
|||
if (vars.collateralReservePaused || vars.principalReservePaused) {
|
||||
return (uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), Errors.VL_RESERVE_PAUSED);
|
||||
}
|
||||
|
||||
(, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
|
||||
user,
|
||||
reservesData,
|
||||
userConfig,
|
||||
reserves,
|
||||
reservesCount,
|
||||
oracle
|
||||
);
|
||||
|
||||
if (vars.healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) {
|
||||
|
||||
if (healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
|
||||
Errors.LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD
|
||||
|
|
|
@ -627,12 +627,9 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
|
||||
const liquidator = users[3];
|
||||
const borrower = users[1];
|
||||
const liquidatorWethBalanceBefore = await weth.balanceOf(liquidator.address);
|
||||
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||
const daiReserveDataBefore = await helpersContract.getReserveData(dai.address);
|
||||
const ethReserveDataBefore = await helpersContract.getReserveData(weth.address);
|
||||
const userReserveDataBefore = await getUserData(
|
||||
pool,
|
||||
helpersContract,
|
||||
|
@ -646,7 +643,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(dai.address)
|
||||
).decimals.toString();
|
||||
const amountToLiquidate = userReserveDataBefore.currentStableDebt.div(2).toFixed(0);
|
||||
const amountToLiquidate = userReserveDataBefore.currentStableDebt.toFixed(0);
|
||||
const extraAmount = new BigNumber(amountToLiquidate).times('1.15').toFixed(0);
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
|
@ -660,10 +657,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const flashLoanDebt = new BigNumber(amountToLiquidate.toString())
|
||||
.multipliedBy(1.0009)
|
||||
.toFixed(0);
|
||||
|
||||
// Set how much ETH will be sold and swapped for DAI at Uniswap mock
|
||||
await (
|
||||
await mockUniswapRouter.setAmountToSwap(
|
||||
|
|
Loading…
Reference in New Issue
Block a user