Liquidation Bot

Parallel Liquidation Bot Technical Designs

Liquidation happens when a borrower’s health factor goes below 1 and their collateral cannot adequately cover their loan to debt value. Liquidations are necessary to reduce risks to the protocol.

Liquidation Bot Architecture

This is a micro-service-based architecture. The orange parts are the producers and the green part is the consumer. As a message bus, Redis connects all the services. Each micro-service can run as a process or a thread. Each service can have its scanning interval to configure different latencies.

We call the accounts whose HF is below 1 as shortfall (underwater) accounts, and these accounts are also the target to be liquidated by the liquidation bot.

Borrower Scanner

The scanner will do these things:

  1. Before liquidating, we need to know all the borrowers' accounts that can be liquidated. We scan the Borrow Event by using FilterLogs JSON-RPC to get all the accounts that have completed borrow operations. These are the potential accounts for liquidation.

  2. Afterward, we store the borrowers into Redis SET.

    ["BorrowerAddress1", "BorrowerAddress2"]

Shortfall Scanner

This scanner will scan all the borrowers and checks the status for each. If the account health factor is below 1, they are marked as a shortfall account and then put into shortfall SET.

["ShortfallAddress1", "ShortfallAddress2"]

Offer Scanner

This scanner is only designed for NFT liquidation, its goal is to get the offers from multiple platforms and then store them in Redis in real-time, which can be read in time for the liquidator to liquidate the corresponding NFT. So, when this scanner is running, it will get the offers from each platform, through the offer data structure is different for each platform, we will stringify the data and store it in Redis.

"OpenseaOffer_MAYC" => "actual_data_info"

Liquidator

The liquidator will get the shortfall accounts from Redis, and for each account, it will try to generate its liquidation task, which is divided into two categories, one is ERC20 and the other is ERC721 (NFT). We have HealthFactor(HF) and ERC721HealthFactor(ERC721HF) to determine whether a certain category of a token can be liquidated or not. For HF < 1, we can try to liquidate ERC20, but if we want to liquidate NFT, the protocol also require ERC721HF < 1 as well.

For ERC20 liquidation:

  • Check the account position for both supplies and borrow using getUserReserveData ABI. Choose a liquidation parameter by using a simple strategy.

  • And then call the liquidateERC20 method in liquidator contract, thus we can liquidate ERC20 by flashloan.

For NFT liquidation:

  • If we find an NFT that can be liquidated, we need to find a valid offer from Redis to match it. Because when the bot constructs the liquidation parameters, the bot needs to give an offer parameter so that the NFTFlashloanLiquidator can complete the offer and get the corresponding offered token to repay the assets of flashloan. So the liquidation bot needs to make sure that this offer can be successfully executed.

  • And then call the liquidateERC721 method in the liquidator contract, thus we can liquidate ERC721 by flashloan.

After the service gets the shortfall accounts from Redis SET, the liquidator will liquidate these shortfall accounts immediately. It will check which asset type (if it includes only ERC20 or both ERC20 and ERC721) can be liquidated by HealthFactor and Erc721HealthFactor. Call the corresponding external liquidation function on the ERC20 liquidation contract or the ERC721 liquidation contract.

How to choose the best offer (bid order)?

For NFT, there are two types of offers: collection offer and token offer. collection offer means that it will receive any token of the collection, the id is not a constraint, whereas the token offer is the exact opposite, it constrains the offer must receive the collection with the specified token id, otherwise, it cannot be completed.

For both types of offers, we choose the offer with the highest price to liquidate according to a greedy strategy. However, the actual profit of our final liquidation transaction cannot be judged by the price alone, because each platform's offer has a different fee strategy, and the liquidator contract also has operational fees which need to be considered.

Specifically, when we consider choosing an offer, we need to consider the fee for

  • The offer its own contained fee.

  • The flash loan fee for the liquidator contract to operate.

  • The swap fee for the Uniswap token exchange, also for the liquidator contract to operate.

  • The liquidation protocol fee that configurated in Parallel.

At last, we choose the offer with the highest price - fees value for the liquidation.

Profit description of NFT Flashloan liquidation

Once the bot finds a profitable offer to liquidate, we will use the flashloan contract to liquidate the NFT, and the profit from the liquidation will eventually be transferred to the owner's address of the flashloan contract. For instance, to liquidate an NFT with USDC, the specific liquidation process can be described as follows.

NOTE: The caller is an executor of the liquidator contract, this address doesn’t hold any asset, so we need to flashloan the corresponding asset to do liquidating.

  1. use Flashloan to borrow the required USDC assets

  2. use the hold USDC assets to call Pool.liquidationERC721 for liquidation

  3. sell the liquidated NFT by completing the offer, and getting the assets provided by the offer

  4. swap the offer traded assets (e.g. WETH) to USDC via UNISWAP

  5. use USDC to repay the flashloan in step 1

  6. complete liquidation

How to calculate the profit?

liquidation_profit = liquidation_earnings - liquidation_costs
  1. liquidation earnings

    • LiquidationBonus: configurated by parallel protocol, take 5% as an example

      • It also means liquidation incentives, specifically, the liquidator is able to take the collateralized NFT at a discount price.

    • Fund provided by the offer: This May be higher or lower than the NFT value(floor price). In this case, we assume that the fund is 105 % * NFT_Value, and we can get a 5% reward.

  2. liquidation costs

    • Flashloan fee: about 0.09 % per-time

      • if we flashloan USDC to repay the loan, we need to charge the flashloan fee

    • Swap fee: about 0.3 % per-time, the average will consume 3 * 0.3% = 0.9%

      • if the liquidation asset(such as USDC) supports flashloan, we only need to swap once.

      • but if the liquidation asset(such as USDT and APE) does not support flashloan, we need to swap more times.

    • LiquidationProtocolFee: configurated by Parallel protocol, take 10% as an example

      • if LiquidationBonus is 5% and LiquidationProtocolFee is 10%, we end up paying 5% * 10% = 0.5% protocol fee.

    • Offer fee: determined by the order itself, the offer may be from many different marketplaces, and different marketplaces have different strategies. In this case, we assume that the fee is 5%.

Thus, in this case

liquidation profit = liquidation earnings - liquidation costs
= (5 % + 5%) - (0.09% + 0.9% + 0.5% + 5%)
= 3.51 %

Eventually, these profits will be transferred to the owner’s address of the ERC721LiquidatorContract.

ERC20 Liquidation Contract

FlashloanLiquidator contract will borrow a FlashLoan to get the liquidation fund and repay the loan to acquire the incentive rewards.

A simple process to liquidate ERC20 with flashloan:

These are the following components:

  • Flashloan Pool - Currently we are using AAVE V2 as the FlashLoan Pool.

  • Parallel Pool - The pool contract of Parallel protocol.

  • UniswapV2 - Swap flashloan asset to debt asset or collateral asset to flashLoan asset.

Contract Interfaces

liquidateERC20

function liquidateERC20(LiquidateERC20Params memory params) external;

Call Params

NameTypeDescription

params

LiquidateERC20Params

this struct contains some liquidation related parameters. struct LiquidateERC20Params { address collateralAsset; address debtAsset; address user; uint256 debtToCover; address flashLoanAsset; uint256 flashLoanAmount; SwapParams flashLoanToDebt; SwapParams collateralToFlashLoan; } struct SwapParams { bool isSwapV3; address router; bytes v3Path; address[] v2Path; }

ERC721 Liquidation Contract

Since the NFT can’t be easily sold, it may not be possible to use FlashLoan's liquidation funds. Let's take the following strategy:

  1. If the NFT to be liquidated has a bid order in other NFT markets, such as Parallel, Opensea, Looksrare, etc, the NFTFlashloanLiquidator contract will borrow the asset from FlashLoan to liquidate the NFT, and then match the bid order. After obtaining the funds, repay the Flash loan.

  2. If the NFT to be liquidated does not have a suitable bid order, liquidate the NFT with the liquidation bot's own funds and make a sell order to Parallel protocol or Opensea, etc.

A simple process to liquidate NFT with flashloan and NFT bid:

Compared with ERC20 flashloan liquidation, it has the following additional components:

  • NFT Market Adaptor - A library of liquidator contract that integrates methods for interacting with various NFT markets.

For UNI-V3-POS, NFTFlashloanLiquidator contract will remove all liquidity of its position and collect fee after the NFT has been liquidated, And then swap the token0 and token1 to WETH if necessary.

In the end, If there are any remaining offer tokens or flash loan assets, they will be transferred to the bot owner as profits.

Supported NFT Marketplaces

Currently, we can get NFT bid orders which contain token bids and collection bids from the following markets.

MarketToken BidCollection Bid

Opensea

supported

not supported

Looksrare

supported

supported

Parallel

supported

supported

CryptoPunksMarket

supported

not supported

NFTX

supported

supported

Sudo

supported

supported

For CryptoPunks, all its bid orders are stored in its own contract, So we can directly interact with the contract to get bid orders and sell the NFT.

Contract interfaces

liquidateERC721

function liquidateERC721( bytes calldata _fullParamsBytes, bytes calldata _liquidateParamsBytes) external;

Call Params

NameTypeDescription

_fullParamsBytes

bytes

encoded complete parameters that contains the market type and bid order and liquidation parameters and flashloan parameters

_liquidateParamsBytes

bytes

encoded liquidation parameters

Last updated