mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Use diff balances instead of liquidation logic for flash liquidations
This commit is contained in:
parent
f05550fc04
commit
55f14c1af9
|
@ -30,20 +30,11 @@ contract FlashLiquidationAdapter is BaseUniswapAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LiquidationCallLocalVars {
|
struct LiquidationCallLocalVars {
|
||||||
uint256 userCollateralBalance;
|
uint256 initCollateralBalance;
|
||||||
uint256 userStableDebt;
|
uint256 diffCollateralBalance;
|
||||||
uint256 userVariableDebt;
|
uint256 flashLoanDebt;
|
||||||
uint256 maxLiquidatableDebt;
|
uint256 soldAmount;
|
||||||
uint256 actualDebtToLiquidate;
|
uint256 remainingTokens;
|
||||||
uint256 maxAmountCollateralToLiquidate;
|
|
||||||
uint256 maxCollateralToLiquidate;
|
|
||||||
uint256 debtAmountNeeded;
|
|
||||||
uint256 collateralPrice;
|
|
||||||
uint256 debtAssetPrice;
|
|
||||||
uint256 liquidationBonus;
|
|
||||||
uint256 collateralDecimals;
|
|
||||||
uint256 debtAssetDecimals;
|
|
||||||
IAToken collateralAtoken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -115,63 +106,38 @@ contract FlashLiquidationAdapter is BaseUniswapAdapter {
|
||||||
uint256 premium,
|
uint256 premium,
|
||||||
address initiator
|
address initiator
|
||||||
) internal {
|
) internal {
|
||||||
DataTypes.ReserveData memory collateralReserve = LENDING_POOL.getReserveData(collateralAsset);
|
|
||||||
DataTypes.ReserveData memory debtReserve = LENDING_POOL.getReserveData(debtAsset);
|
|
||||||
LiquidationCallLocalVars memory vars;
|
LiquidationCallLocalVars memory vars;
|
||||||
|
vars.initCollateralBalance = IERC20(collateralAsset).balanceOf(address(this));
|
||||||
|
vars.flashLoanDebt = coverAmount.add(premium);
|
||||||
|
|
||||||
(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebtMemory(
|
// Approve LendingPool to use debt token for liquidation
|
||||||
user,
|
|
||||||
debtReserve
|
|
||||||
);
|
|
||||||
vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress);
|
|
||||||
vars.maxLiquidatableDebt = vars.userStableDebt.add(vars.userVariableDebt).percentMul(
|
|
||||||
LIQUIDATION_CLOSE_FACTOR_PERCENT
|
|
||||||
);
|
|
||||||
|
|
||||||
vars.userCollateralBalance = vars.collateralAtoken.balanceOf(user);
|
|
||||||
vars.actualDebtToLiquidate = debtToCover > vars.maxLiquidatableDebt
|
|
||||||
? vars.maxLiquidatableDebt
|
|
||||||
: debtToCover;
|
|
||||||
|
|
||||||
(
|
|
||||||
vars.maxCollateralToLiquidate,
|
|
||||||
vars.debtAmountNeeded
|
|
||||||
) = _calculateAvailableCollateralToLiquidate(
|
|
||||||
collateralReserve,
|
|
||||||
debtReserve,
|
|
||||||
collateralAsset,
|
|
||||||
debtAsset,
|
|
||||||
vars.actualDebtToLiquidate,
|
|
||||||
vars.userCollateralBalance
|
|
||||||
);
|
|
||||||
|
|
||||||
require(coverAmount >= vars.debtAmountNeeded, 'FLASH_COVER_NOT_ENOUGH');
|
|
||||||
|
|
||||||
uint256 flashLoanDebt = coverAmount.add(premium);
|
|
||||||
|
|
||||||
IERC20(debtAsset).approve(address(LENDING_POOL), debtToCover);
|
IERC20(debtAsset).approve(address(LENDING_POOL), debtToCover);
|
||||||
|
|
||||||
// Liquidate the user position and release the underlying collateral
|
// Liquidate the user position and release the underlying collateral
|
||||||
LENDING_POOL.liquidationCall(collateralAsset, debtAsset, user, debtToCover, false);
|
LENDING_POOL.liquidationCall(collateralAsset, debtAsset, user, debtToCover, false);
|
||||||
|
|
||||||
|
// Discover the liquidated tokens
|
||||||
|
vars.diffCollateralBalance = IERC20(collateralAsset).balanceOf(address(this)).sub(
|
||||||
|
vars.initCollateralBalance
|
||||||
|
);
|
||||||
|
|
||||||
// Swap released collateral into the debt asset, to repay the flash loan
|
// Swap released collateral into the debt asset, to repay the flash loan
|
||||||
uint256 soldAmount =
|
vars.soldAmount = _swapTokensForExactTokens(
|
||||||
_swapTokensForExactTokens(
|
collateralAsset,
|
||||||
collateralAsset,
|
debtAsset,
|
||||||
debtAsset,
|
vars.diffCollateralBalance,
|
||||||
vars.maxCollateralToLiquidate,
|
vars.flashLoanDebt,
|
||||||
flashLoanDebt,
|
useEthPath
|
||||||
useEthPath
|
);
|
||||||
);
|
|
||||||
|
|
||||||
// Repay flash loan
|
// Allow repay of flash loan
|
||||||
IERC20(debtAsset).approve(address(LENDING_POOL), flashLoanDebt);
|
IERC20(debtAsset).approve(address(LENDING_POOL), vars.flashLoanDebt);
|
||||||
|
|
||||||
uint256 remainingTokens = vars.maxCollateralToLiquidate.sub(soldAmount);
|
vars.remainingTokens = vars.diffCollateralBalance.sub(vars.soldAmount);
|
||||||
|
|
||||||
// Transfer remaining tokens to initiator
|
// Transfer remaining tokens to initiator
|
||||||
if (remainingTokens > 0) {
|
if (vars.remainingTokens > 0) {
|
||||||
IERC20(collateralAsset).transfer(initiator, remainingTokens);
|
IERC20(collateralAsset).transfer(initiator, vars.remainingTokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,64 +162,4 @@ contract FlashLiquidationAdapter is BaseUniswapAdapter {
|
||||||
|
|
||||||
return LiquidationParams(collateralAsset, debtAsset, user, debtToCover, useEthPath);
|
return LiquidationParams(collateralAsset, debtAsset, user, debtToCover, useEthPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Calculates how much of a specific collateral can be liquidated, given
|
|
||||||
* a certain amount of debt asset.
|
|
||||||
* - This function needs to be called after all the checks to validate the liquidation have been performed,
|
|
||||||
* otherwise it might fail.
|
|
||||||
* @param collateralReserve The data of the collateral reserve
|
|
||||||
* @param debtReserve The data of the debt reserve
|
|
||||||
* @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
|
|
||||||
* @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
|
|
||||||
* @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
|
|
||||||
* @param userCollateralBalance The collateral balance for the specific `collateralAsset` of the user being liquidated
|
|
||||||
* @return collateralAmount: The maximum amount that is possible to liquidate given all the liquidation constraints
|
|
||||||
* (user balance, close factor)
|
|
||||||
* debtAmountNeeded: The amount to repay with the liquidation
|
|
||||||
**/
|
|
||||||
function _calculateAvailableCollateralToLiquidate(
|
|
||||||
DataTypes.ReserveData memory collateralReserve,
|
|
||||||
DataTypes.ReserveData memory debtReserve,
|
|
||||||
address collateralAsset,
|
|
||||||
address debtAsset,
|
|
||||||
uint256 debtToCover,
|
|
||||||
uint256 userCollateralBalance
|
|
||||||
) internal view returns (uint256, uint256) {
|
|
||||||
uint256 collateralAmount = 0;
|
|
||||||
uint256 debtAmountNeeded = 0;
|
|
||||||
|
|
||||||
LiquidationCallLocalVars memory vars;
|
|
||||||
|
|
||||||
vars.collateralPrice = ORACLE.getAssetPrice(collateralAsset);
|
|
||||||
vars.debtAssetPrice = ORACLE.getAssetPrice(debtAsset);
|
|
||||||
|
|
||||||
(, , vars.liquidationBonus, vars.collateralDecimals, ) = collateralReserve
|
|
||||||
.configuration
|
|
||||||
.getParamsMemory();
|
|
||||||
(, , , vars.debtAssetDecimals, ) = debtReserve.configuration.getParamsMemory();
|
|
||||||
|
|
||||||
// This is the maximum possible amount of the selected collateral that can be liquidated, given the
|
|
||||||
// max amount of liquidatable debt
|
|
||||||
vars.maxAmountCollateralToLiquidate = vars
|
|
||||||
.debtAssetPrice
|
|
||||||
.mul(debtToCover)
|
|
||||||
.mul(10**vars.collateralDecimals)
|
|
||||||
.percentMul(vars.liquidationBonus)
|
|
||||||
.div(vars.collateralPrice.mul(10**vars.debtAssetDecimals));
|
|
||||||
|
|
||||||
if (vars.maxAmountCollateralToLiquidate > userCollateralBalance) {
|
|
||||||
collateralAmount = userCollateralBalance;
|
|
||||||
debtAmountNeeded = vars
|
|
||||||
.collateralPrice
|
|
||||||
.mul(collateralAmount)
|
|
||||||
.mul(10**vars.debtAssetDecimals)
|
|
||||||
.div(vars.debtAssetPrice.mul(10**vars.collateralDecimals))
|
|
||||||
.percentDiv(vars.liquidationBonus);
|
|
||||||
} else {
|
|
||||||
collateralAmount = vars.maxAmountCollateralToLiquidate;
|
|
||||||
debtAmountNeeded = debtToCover;
|
|
||||||
}
|
|
||||||
return (collateralAmount, debtAmountNeeded);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,13 @@ const { expect } = require('chai');
|
||||||
makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
let mockUniswapRouter: MockUniswapV2Router02;
|
let mockUniswapRouter: MockUniswapV2Router02;
|
||||||
let evmSnapshotId: string;
|
let evmSnapshotId: string;
|
||||||
|
const { INVALID_HF, LP_LIQUIDATION_CALL_FAILED } = ProtocolErrors;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
mockUniswapRouter = await getMockUniswapRouter();
|
mockUniswapRouter = await getMockUniswapRouter();
|
||||||
});
|
});
|
||||||
|
|
||||||
const depositAndHFBelowOne = async () => {
|
const depositAndHFBelowOne = async () => {
|
||||||
const { INVALID_HF } = ProtocolErrors;
|
|
||||||
const { dai, weth, users, pool, oracle } = testEnv;
|
const { dai, weth, users, pool, oracle } = testEnv;
|
||||||
const depositor = users[0];
|
const depositor = users[0];
|
||||||
const borrower = users[1];
|
const borrower = users[1];
|
||||||
|
@ -643,7 +643,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
params,
|
params,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
).to.be.revertedWith('FLASH_COVER_NOT_ENOUGH');
|
).to.be.revertedWith(LP_LIQUIDATION_CALL_FAILED);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Revert if requested multiple assets', async () => {
|
it('Revert if requested multiple assets', async () => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user