mirror of
				https://github.com/Instadapp/aave-protocol-v2.git
				synced 2024-07-29 21:47:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			185 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
| // 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;
 | |
|     address borrowedAsset;
 | |
|     address user;
 | |
|     uint256 debtToCover;
 | |
|     bool useEthPath;
 | |
|   }
 | |
| 
 | |
|   struct LiquidationCallLocalVars {
 | |
|     uint256 initFlashBorrowedBalance;
 | |
|     uint256 diffFlashBorrowedBalance;
 | |
|     uint256 initCollateralBalance;
 | |
|     uint256 diffCollateralBalance;
 | |
|     uint256 flashLoanDebt;
 | |
|     uint256 soldAmount;
 | |
|     uint256 remainingTokens;
 | |
|     uint256 borrowedAssetLeftovers;
 | |
|   }
 | |
| 
 | |
|   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
 | |
|    *   address borrowedAsset The asset that must be covered
 | |
|    *   address user The user address with a Health Factor below 1
 | |
|    *   uint256 debtToCover The amount of debt to cover
 | |
|    *   bool useEthPath Use WETH as connector path between the collateralAsset and borrowedAsset at Uniswap
 | |
|    */
 | |
|   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);
 | |
| 
 | |
|     require(assets.length == 1 && assets[0] == decodedParams.borrowedAsset, 'INCONSISTENT_PARAMS');
 | |
| 
 | |
|     _liquidateAndSwap(
 | |
|       decodedParams.collateralAsset,
 | |
|       decodedParams.borrowedAsset,
 | |
|       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
 | |
|    * @param borrowedAsset The asset that must be covered
 | |
|    * @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
 | |
|    * @param flashBorrowedAmount Amount of asset requested at the flash loan to liquidate the user position
 | |
|    * @param premium Fee of the requested flash loan
 | |
|    * @param initiator Address of the caller
 | |
|    */
 | |
|   function _liquidateAndSwap(
 | |
|     address collateralAsset,
 | |
|     address borrowedAsset,
 | |
|     address user,
 | |
|     uint256 debtToCover,
 | |
|     bool useEthPath,
 | |
|     uint256 flashBorrowedAmount,
 | |
|     uint256 premium,
 | |
|     address initiator
 | |
|   ) internal {
 | |
|     LiquidationCallLocalVars memory vars;
 | |
|     vars.initCollateralBalance = IERC20(collateralAsset).balanceOf(address(this));
 | |
|     if (collateralAsset != borrowedAsset) {
 | |
|       vars.initFlashBorrowedBalance = IERC20(borrowedAsset).balanceOf(address(this));
 | |
| 
 | |
|       // Track leftover balance to rescue funds in case of external transfers into this contract
 | |
|       vars.borrowedAssetLeftovers = vars.initFlashBorrowedBalance.sub(flashBorrowedAmount);
 | |
|     }
 | |
|     vars.flashLoanDebt = flashBorrowedAmount.add(premium);
 | |
| 
 | |
|     // Approve LendingPool to use debt token for liquidation
 | |
|     IERC20(borrowedAsset).approve(address(LENDING_POOL), debtToCover);
 | |
| 
 | |
|     // Liquidate the user position and release the underlying collateral
 | |
|     LENDING_POOL.liquidationCall(collateralAsset, borrowedAsset, user, debtToCover, false);
 | |
| 
 | |
|     // Discover the liquidated tokens
 | |
|     uint256 collateralBalanceAfter = IERC20(collateralAsset).balanceOf(address(this));
 | |
| 
 | |
|     // Track only collateral released, not current asset balance of the contract
 | |
|     vars.diffCollateralBalance = collateralBalanceAfter.sub(vars.initCollateralBalance);
 | |
| 
 | |
|     if (collateralAsset != borrowedAsset) {
 | |
|       // Discover flash loan balance after the liquidation
 | |
|       uint256 flashBorrowedAssetAfter = IERC20(borrowedAsset).balanceOf(address(this));
 | |
| 
 | |
|       // Use only flash loan borrowed assets, not current asset balance of the contract
 | |
|       vars.diffFlashBorrowedBalance = flashBorrowedAssetAfter.sub(vars.borrowedAssetLeftovers);
 | |
| 
 | |
|       // 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);
 | |
|     }
 | |
| 
 | |
|     // Allow repay of flash loan
 | |
|     IERC20(borrowedAsset).approve(address(LENDING_POOL), vars.flashLoanDebt);
 | |
| 
 | |
|     // Transfer remaining tokens to initiator
 | |
|     if (vars.remainingTokens > 0) {
 | |
|       IERC20(collateralAsset).transfer(initiator, vars.remainingTokens);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @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
 | |
|    *   address borrowedAsset The asset that must be covered and will be exchanged to pay the flash loan premium
 | |
|    *   address user The user address with a Health Factor below 1
 | |
|    *   uint256 debtToCover The amount of debt to cover
 | |
|    *   bool useEthPath Use WETH as connector path between the collateralAsset and borrowedAsset at Uniswap
 | |
|    * @return LiquidationParams struct containing decoded params
 | |
|    */
 | |
|   function _decodeParams(bytes memory params) internal pure returns (LiquidationParams memory) {
 | |
|     (
 | |
|       address collateralAsset,
 | |
|       address borrowedAsset,
 | |
|       address user,
 | |
|       uint256 debtToCover,
 | |
|       bool useEthPath
 | |
|     ) = abi.decode(params, (address, address, address, uint256, bool));
 | |
| 
 | |
|     return LiquidationParams(collateralAsset, borrowedAsset, user, debtToCover, useEthPath);
 | |
|   }
 | |
| }
 | 
