Skip to content

Commit

Permalink
feat: add gho partial liquidator
Browse files Browse the repository at this point in the history
  • Loading branch information
doomsower committed Sep 2, 2024
1 parent c12f870 commit f110565
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 50 deletions.
51 changes: 51 additions & 0 deletions contracts/AaveLiquidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024
pragma solidity ^0.8.10;

import {AbstractLiquidator, LiquidationResult, IntermediateData} from "./AbstractLiquidator.sol";

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";

import {AaveFLTaker} from "./AaveFLTaker.sol";

contract AaveLiquidator is AbstractLiquidator {
using SafeERC20 for IERC20;

address public immutable aavePool;
address public immutable aaveFLTaker;

modifier onlyAave() {
if (msg.sender != aavePool) revert("Caller not Aave pool");
_;
}

constructor(address _router, address _plb, address _aavePool, address _aaveFLTaker)
AbstractLiquidator(_router, _plb)
{
aavePool = _aavePool;
aaveFLTaker = _aaveFLTaker;
}

function _takeFlashLoan(address underlying, uint256 amount, bytes memory data) internal virtual override {
AaveFLTaker(aaveFLTaker).takeFlashLoan(underlying, amount, data);
}

function executeOperation(
address[] memory assets,
uint256[] memory amounts,
uint256[] memory premiums,
address initiator,
bytes calldata params
) external onlyAave returns (bool) {
if (initiator != aaveFLTaker) revert("Flash loan initiator is not FLTaker");

IntermediateData memory intData = abi.decode(params, (IntermediateData));

_processFlashLoan(assets[0], amounts[0], premiums[0], intData);

IERC20(assets[0]).forceApprove(aavePool, amounts[0] + premiums[0]);
return true;
}
}
62 changes: 13 additions & 49 deletions contracts/Liquidator.sol → contracts/AbstractLiquidator.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024
pragma solidity ^0.8.17;
pragma solidity ^0.8.10;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";

import {IPartialLiquidator, IntermediateData, LiquidationResult} from "./interfaces/IPartialLiquidator.sol";
import {IRouterV3, RouterResult} from "./interfaces/IRouterV3.sol";
import {ILiquidator, LiquidationResult} from "./interfaces/ILiquidator.sol";
import {IPartialLiquidationBotV3} from "@gearbox-protocol/bots-v3/contracts/interfaces/IPartialLiquidationBotV3.sol";
import {ICreditAccountV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditAccountV3.sol";
import {
Expand All @@ -22,53 +22,26 @@ import {ICreditFacadeV3Multicall} from "@gearbox-protocol/core-v3/contracts/inte
import {CreditLogic} from "@gearbox-protocol/core-v3/contracts/libraries/CreditLogic.sol";
import {MultiCall, MultiCallOps} from "@gearbox-protocol/core-v2/contracts/libraries/MultiCall.sol";
import {IUpdatablePriceFeed} from "@gearbox-protocol/core-v2/contracts/interfaces/IPriceFeed.sol";
import {AaveFLTaker} from "./AaveFLTaker.sol";
import {IAavePoolFlashLoan} from "./interfaces/IAavePoolFlashLoan.sol";

struct IntermediateData {
bool preview;
address creditManager;
address creditAccount;
address creditFacade;
address assetOut;
uint256 amountOut;
IPartialLiquidationBotV3.PriceUpdate[] priceUpdates;
MultiCall[] conversionCalls;
address[] connectors;
uint256 slippage;
address conversionAccount;
uint256 initialUnderlyingBalance;
}

uint256 constant PERCENTAGE_FACTOR = 10000;

contract Liquidator is Ownable {
abstract contract AbstractLiquidator is Ownable, IPartialLiquidator {
using SafeERC20 for IERC20;
using MultiCallOps for MultiCall[];

event SetRouter(address indexed newRouter);
event SetPartialLiquidationBot(address indexed partialLiquidationBot);

address public immutable aavePool;
address public immutable aaveFLTaker;

address public router;
address public partialLiquidationBot;

mapping(address => address) public cmToCA;

bytes private _liqResultTemp;

modifier onlyAave() {
if (msg.sender != aavePool) revert("Caller not Aave pool");
_;
}

constructor(address _router, address _plb, address _aavePool, address _aaveFLTaker) {
constructor(address _router, address _plb) {
router = _router;
partialLiquidationBot = _plb;
aavePool = _aavePool;
aaveFLTaker = _aaveFLTaker;
}

function registerCM(address creditManager) external onlyOwner {
Expand All @@ -86,17 +59,9 @@ contract Liquidator is Ownable {
IERC20(token).safeTransfer(to, amount);
}

function executeOperation(
address[] memory assets,
uint256[] memory amounts,
uint256[] memory premiums,
address initiator,
bytes calldata params
) external onlyAave returns (bool) {
if (initiator != aaveFLTaker) revert("Flash loan initiator is not FLTaker");

IntermediateData memory intData = abi.decode(params, (IntermediateData));

function _processFlashLoan(address asset, uint256 amount, uint256 premium, IntermediateData memory intData)
internal
{
if (intData.preview) {
LiquidationResult memory liqResult = _previewPartialLiquidationInt(
intData.creditManager,
Expand All @@ -123,13 +88,10 @@ contract Liquidator is Ownable {
_performConversion(intData.creditFacade, intData.conversionAccount, intData.conversionCalls);

require(
intData.initialUnderlyingBalance + amounts[0] + premiums[0] < IERC20(assets[0]).balanceOf(address(this)),
intData.initialUnderlyingBalance + amount + premium < IERC20(asset).balanceOf(address(this)),
"Liquidation was not profitable"
);
}

IERC20(assets[0]).forceApprove(aavePool, amounts[0] + premiums[0]);
return true;
}

function _partialLiquidateInt(
Expand Down Expand Up @@ -177,7 +139,7 @@ contract Liquidator is Ownable {

intData.initialUnderlyingBalance = IERC20(underlying).balanceOf(address(this));

AaveFLTaker(aaveFLTaker).takeFlashLoan(underlying, flashLoanAmount, abi.encode(intData));
_takeFlashLoan(underlying, flashLoanAmount, abi.encode(intData));
}

function previewPartialLiquidation(
Expand Down Expand Up @@ -208,11 +170,13 @@ contract Liquidator is Ownable {

address underlying = ICreditManagerV3(creditManager).underlying();

AaveFLTaker(aaveFLTaker).takeFlashLoan(underlying, flashLoanAmount, abi.encode(intData));
_takeFlashLoan(underlying, flashLoanAmount, abi.encode(intData));

return abi.decode(_liqResultTemp, (LiquidationResult));
}

function _takeFlashLoan(address underlying, uint256 amount, bytes memory data) internal virtual;

function _previewPartialLiquidationInt(
address creditManager,
address creditAccount,
Expand Down Expand Up @@ -269,7 +233,7 @@ contract Liquidator is Ownable {
function getOptimalLiquidation(
address creditAccount,
uint256 hfOptimal,
IPartialLiquidationBotV3.PriceUpdate[] calldata priceUpdates
IPartialLiquidationBotV3.PriceUpdate[] memory priceUpdates
)
external
returns (
Expand Down
35 changes: 35 additions & 0 deletions contracts/GhoFMTaker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
pragma solidity ^0.8.17;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IGhoFlashMinter} from "./interfaces/IGhoFlashMinter.sol";

contract GhoFMTaker is Ownable {
event SetAllowedFMReceiver(address indexed consumer, bool status);

error CallerNotAllowedReceiverException();

address public immutable ghoFlashMinter;
address public immutable gho;

mapping(address => bool) public allowedFMReceiver;

modifier onlyAllowedFMReceiver() {
if (!allowedFMReceiver[msg.sender]) revert CallerNotAllowedReceiverException();
_;
}

constructor(address _ghoFlashMinter, address _gho) {
ghoFlashMinter = _ghoFlashMinter;
gho = _gho;
}

function takeFlashMint(uint256 amount, bytes memory data) external onlyAllowedFMReceiver {
IGhoFlashMinter(ghoFlashMinter).flashLoan(msg.sender, gho, amount, data);
}

function setAllowedFMReceiver(address receiver, bool status) external onlyOwner {
if (allowedFMReceiver[receiver] == status) return;
allowedFMReceiver[receiver] = status;
emit SetAllowedFMReceiver(receiver, status);
}
}
54 changes: 54 additions & 0 deletions contracts/GhoLiquidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024
pragma solidity ^0.8.10;

import {AbstractLiquidator, LiquidationResult, IntermediateData} from "./AbstractLiquidator.sol";

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";

import {GhoFMTaker} from "./GhoFMTaker.sol";

contract GhoLiquidator is AbstractLiquidator {
using SafeERC20 for IERC20;

bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");

address public immutable ghoFlashMinter;
address public immutable ghoFMTaker;
address public immutable gho;

modifier onlyGhoFlashMinter() {
if (msg.sender != ghoFlashMinter) revert("Caller not GHO flash minter");
_;
}

constructor(address _router, address _plb, address _ghoFlashMinter, address _ghoFMTaker, address _gho)
AbstractLiquidator(_router, _plb)
{
ghoFlashMinter = _ghoFlashMinter;
ghoFMTaker = _ghoFMTaker;
gho = _gho;
}

function _takeFlashLoan(address, uint256 amount, bytes memory data) internal virtual override {
GhoFMTaker(ghoFMTaker).takeFlashMint(amount, data);
}

function onFlashLoan(address initiator, address token, uint256 amount, uint256 fee, bytes calldata data)
external
onlyGhoFlashMinter
returns (bytes32)
{
if (initiator != ghoFMTaker) revert("Flash loan initiator is not FMTaker");

IntermediateData memory intData = abi.decode(data, (IntermediateData));

_processFlashLoan(token, amount, fee, intData);

IERC20(token).forceApprove(ghoFlashMinter, amount + fee);

return CALLBACK_SUCCESS;
}
}
8 changes: 8 additions & 0 deletions contracts/interfaces/IGhoFlashMinter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024
pragma solidity ^0.8.10;

interface IGhoFlashMinter {
function flashLoan(address receiver, address token, uint256 amount, bytes calldata data) external returns (bool);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,22 @@ struct LiquidationResult {
uint256 amountOut;
}

interface ILiquidator {
struct IntermediateData {
bool preview;
address creditManager;
address creditAccount;
address creditFacade;
address assetOut;
uint256 amountOut;
IPartialLiquidationBotV3.PriceUpdate[] priceUpdates;
MultiCall[] conversionCalls;
address[] connectors;
uint256 slippage;
address conversionAccount;
uint256 initialUnderlyingBalance;
}

interface IPartialLiquidator {
function router() external view returns (address);
function partialLiquidationBot() external view returns (address);
function cmToCA(address cm) external view returns (address);
Expand Down

0 comments on commit f110565

Please sign in to comment.