2021-01-11 16:40:25 +00:00
|
|
|
// SPDX-License-Identifier: agpl-3.0
|
|
|
|
pragma solidity 0.6.12;
|
|
|
|
pragma experimental ABIEncoderV2;
|
|
|
|
|
|
|
|
import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol';
|
|
|
|
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
|
|
|
|
import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol';
|
|
|
|
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
|
|
|
import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
|
|
|
|
import {Helpers} from '../protocol/libraries/helpers/Helpers.sol';
|
|
|
|
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
|
|
|
|
import {IAToken} from '../interfaces/IAToken.sol';
|
|
|
|
import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @title UniswapLiquiditySwapAdapter
|
|
|
|
* @notice Uniswap V2 Adapter to swap liquidity.
|
|
|
|
* @author Aave
|
|
|
|
**/
|
|
|
|
contract FlashLiquidationAdapter is BaseUniswapAdapter {
|
|
|
|
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
|
|
|
|
uint256 internal constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000;
|
|
|
|
|
|
|
|
struct LiquidationParams {
|
|
|
|
address collateralAsset;
|
2021-01-29 17:09:06 +00:00
|
|
|
address borrowedAsset;
|
2021-01-11 16:40:25 +00:00
|
|
|
address user;
|
|
|
|
uint256 debtToCover;
|
|
|
|
bool useEthPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct LiquidationCallLocalVars {
|
2021-01-29 17:09:06 +00:00
|
|
|
uint256 initFlashBorrowedBalance;
|
|
|
|
uint256 diffFlashBorrowedBalance;
|
2021-01-22 09:59:47 +00:00
|
|
|
uint256 initCollateralBalance;
|
|
|
|
uint256 diffCollateralBalance;
|
|
|
|
uint256 flashLoanDebt;
|
|
|
|
uint256 soldAmount;
|
|
|
|
uint256 remainingTokens;
|
2021-02-01 12:24:07 +00:00
|
|
|
uint256 borrowedAssetLeftovers;
|
2021-01-11 16:40:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
ILendingPoolAddressesProvider addressesProvider,
|
|
|
|
IUniswapV2Router02 uniswapRouter,
|
|
|
|
address wethAddress
|
|
|
|
) public BaseUniswapAdapter(addressesProvider, uniswapRouter, wethAddress) {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Liquidate a non-healthy position collateral-wise, with a Health Factor below 1, using Flash Loan and Uniswap to repay flash loan premium.
|
|
|
|
* - The caller (liquidator) with a flash loan covers `debtToCover` amount of debt of the user getting liquidated, and receives
|
|
|
|
* a proportionally amount of the `collateralAsset` plus a bonus to cover market risk minus the flash loan premium.
|
|
|
|
* @param assets Address of asset to be swapped
|
|
|
|
* @param amounts Amount of the asset to be swapped
|
|
|
|
* @param premiums Fee of the flash loan
|
|
|
|
* @param initiator Address of the caller
|
|
|
|
* @param params Additional variadic field to include extra params. Expected parameters:
|
|
|
|
* address collateralAsset The collateral asset to release and will be exchanged to pay the flash loan premium
|
2021-01-29 17:09:06 +00:00
|
|
|
* address borrowedAsset The asset that must be covered
|
2021-01-11 16:40:25 +00:00
|
|
|
* address user The user address with a Health Factor below 1
|
|
|
|
* uint256 debtToCover The amount of debt to cover
|
2021-01-29 17:09:06 +00:00
|
|
|
* bool useEthPath Use WETH as connector path between the collateralAsset and borrowedAsset at Uniswap
|
2021-01-11 16:40:25 +00:00
|
|
|
*/
|
|
|
|
function executeOperation(
|
|
|
|
address[] calldata assets,
|
|
|
|
uint256[] calldata amounts,
|
|
|
|
uint256[] calldata premiums,
|
|
|
|
address initiator,
|
|
|
|
bytes calldata params
|
|
|
|
) external override returns (bool) {
|
|
|
|
require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL');
|
|
|
|
|
|
|
|
LiquidationParams memory decodedParams = _decodeParams(params);
|
|
|
|
|
2021-01-29 17:09:06 +00:00
|
|
|
require(assets.length == 1 && assets[0] == decodedParams.borrowedAsset, 'INCONSISTENT_PARAMS');
|
2021-01-11 16:40:25 +00:00
|
|
|
|
|
|
|
_liquidateAndSwap(
|
|
|
|
decodedParams.collateralAsset,
|
2021-01-29 17:09:06 +00:00
|
|
|
decodedParams.borrowedAsset,
|
2021-01-11 16:40:25 +00:00
|
|
|
decodedParams.user,
|
|
|
|
decodedParams.debtToCover,
|
|
|
|
decodedParams.useEthPath,
|
|
|
|
amounts[0],
|
|
|
|
premiums[0],
|
|
|
|
initiator
|
|
|
|
);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev
|
|
|
|
* @param collateralAsset The collateral asset to release and will be exchanged to pay the flash loan premium
|
2021-01-29 17:09:06 +00:00
|
|
|
* @param borrowedAsset The asset that must be covered
|
2021-01-11 16:40:25 +00:00
|
|
|
* @param user The user address with a Health Factor below 1
|
|
|
|
* @param debtToCover The amount of debt to coverage, can be max(-1) to liquidate all possible debt
|
|
|
|
* @param useEthPath true if the swap needs to occur using ETH in the routing, false otherwise
|
2021-01-29 17:09:06 +00:00
|
|
|
* @param flashBorrowedAmount Amount of asset requested at the flash loan to liquidate the user position
|
2021-01-11 16:40:25 +00:00
|
|
|
* @param premium Fee of the requested flash loan
|
|
|
|
* @param initiator Address of the caller
|
|
|
|
*/
|
|
|
|
function _liquidateAndSwap(
|
|
|
|
address collateralAsset,
|
2021-01-29 17:09:06 +00:00
|
|
|
address borrowedAsset,
|
2021-01-11 16:40:25 +00:00
|
|
|
address user,
|
|
|
|
uint256 debtToCover,
|
|
|
|
bool useEthPath,
|
2021-02-01 12:24:07 +00:00
|
|
|
uint256 flashBorrowedAmount,
|
2021-01-11 16:40:25 +00:00
|
|
|
uint256 premium,
|
|
|
|
address initiator
|
|
|
|
) internal {
|
|
|
|
LiquidationCallLocalVars memory vars;
|
2021-01-22 09:59:47 +00:00
|
|
|
vars.initCollateralBalance = IERC20(collateralAsset).balanceOf(address(this));
|
2021-01-29 17:09:06 +00:00
|
|
|
if (collateralAsset != borrowedAsset) {
|
|
|
|
vars.initFlashBorrowedBalance = IERC20(borrowedAsset).balanceOf(address(this));
|
2021-02-01 12:24:07 +00:00
|
|
|
|
|
|
|
// Track leftover balance to rescue funds in case of external transfers into this contract
|
|
|
|
vars.borrowedAssetLeftovers = vars.initFlashBorrowedBalance.sub(flashBorrowedAmount);
|
2021-01-29 17:09:06 +00:00
|
|
|
}
|
2021-02-01 12:24:07 +00:00
|
|
|
vars.flashLoanDebt = flashBorrowedAmount.add(premium);
|
2021-01-11 16:40:25 +00:00
|
|
|
|
2021-01-22 09:59:47 +00:00
|
|
|
// Approve LendingPool to use debt token for liquidation
|
2021-01-29 17:09:06 +00:00
|
|
|
IERC20(borrowedAsset).approve(address(LENDING_POOL), debtToCover);
|
2021-01-15 15:48:54 +00:00
|
|
|
|
2021-01-11 16:40:25 +00:00
|
|
|
// Liquidate the user position and release the underlying collateral
|
2021-01-29 17:09:06 +00:00
|
|
|
LENDING_POOL.liquidationCall(collateralAsset, borrowedAsset, user, debtToCover, false);
|
2021-01-11 16:40:25 +00:00
|
|
|
|
2021-01-22 09:59:47 +00:00
|
|
|
// Discover the liquidated tokens
|
2021-01-29 17:09:06 +00:00
|
|
|
uint256 collateralBalanceAfter = IERC20(collateralAsset).balanceOf(address(this));
|
|
|
|
|
2021-02-01 12:24:07 +00:00
|
|
|
// Track only collateral released, not current asset balance of the contract
|
2021-01-29 17:09:06 +00:00
|
|
|
vars.diffCollateralBalance = collateralBalanceAfter.sub(vars.initCollateralBalance);
|
|
|
|
|
|
|
|
if (collateralAsset != borrowedAsset) {
|
2021-02-01 12:24:07 +00:00
|
|
|
// Discover flash loan balance after the liquidation
|
2021-01-29 17:09:06 +00:00
|
|
|
uint256 flashBorrowedAssetAfter = IERC20(borrowedAsset).balanceOf(address(this));
|
|
|
|
|
2021-02-01 12:24:07 +00:00
|
|
|
// Use only flash loan borrowed assets, not current asset balance of the contract
|
|
|
|
vars.diffFlashBorrowedBalance = flashBorrowedAssetAfter.sub(vars.borrowedAssetLeftovers);
|
|
|
|
|
2021-01-29 17:09:06 +00:00
|
|
|
// Swap released collateral into the debt asset, to repay the flash loan
|
|
|
|
vars.soldAmount = _swapTokensForExactTokens(
|
|
|
|
collateralAsset,
|
|
|
|
borrowedAsset,
|
|
|
|
vars.diffCollateralBalance,
|
|
|
|
vars.flashLoanDebt.sub(vars.diffFlashBorrowedBalance),
|
|
|
|
useEthPath
|
|
|
|
);
|
|
|
|
vars.remainingTokens = vars.diffCollateralBalance.sub(vars.soldAmount);
|
|
|
|
} else {
|
|
|
|
vars.remainingTokens = vars.diffCollateralBalance.sub(premium);
|
|
|
|
}
|
2021-01-11 16:40:25 +00:00
|
|
|
|
2021-01-22 09:59:47 +00:00
|
|
|
// Allow repay of flash loan
|
2021-01-29 17:09:06 +00:00
|
|
|
IERC20(borrowedAsset).approve(address(LENDING_POOL), vars.flashLoanDebt);
|
2021-01-15 15:48:54 +00:00
|
|
|
|
|
|
|
// Transfer remaining tokens to initiator
|
2021-01-22 09:59:47 +00:00
|
|
|
if (vars.remainingTokens > 0) {
|
|
|
|
IERC20(collateralAsset).transfer(initiator, vars.remainingTokens);
|
2021-01-11 16:40:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Decodes the information encoded in the flash loan params
|
|
|
|
* @param params Additional variadic field to include extra params. Expected parameters:
|
|
|
|
* address collateralAsset The collateral asset to claim
|
2021-01-29 17:09:06 +00:00
|
|
|
* address borrowedAsset The asset that must be covered and will be exchanged to pay the flash loan premium
|
2021-01-11 16:40:25 +00:00
|
|
|
* address user The user address with a Health Factor below 1
|
|
|
|
* uint256 debtToCover The amount of debt to cover
|
2021-01-29 17:09:06 +00:00
|
|
|
* bool useEthPath Use WETH as connector path between the collateralAsset and borrowedAsset at Uniswap
|
2021-01-11 16:40:25 +00:00
|
|
|
* @return LiquidationParams struct containing decoded params
|
|
|
|
*/
|
|
|
|
function _decodeParams(bytes memory params) internal pure returns (LiquidationParams memory) {
|
|
|
|
(
|
|
|
|
address collateralAsset,
|
2021-01-29 17:09:06 +00:00
|
|
|
address borrowedAsset,
|
2021-01-11 16:40:25 +00:00
|
|
|
address user,
|
|
|
|
uint256 debtToCover,
|
|
|
|
bool useEthPath
|
|
|
|
) = abi.decode(params, (address, address, address, uint256, bool));
|
|
|
|
|
2021-01-29 17:09:06 +00:00
|
|
|
return LiquidationParams(collateralAsset, borrowedAsset, user, debtToCover, useEthPath);
|
2021-01-11 16:40:25 +00:00
|
|
|
}
|
|
|
|
}
|