Non-Auction Liquidations

Step 1: Check if a Position is Liquidatable:

See: Monitoring Position Health.

Step 2: Executing Liquidations

You have two choices of functions to call when executing a liquidation:

liquidate() - Used for maximum liquidations.

Use when:

  • You want to liquidate the maximum allowed amount.

  • You don't know the exact optimal amount.

function liquidate(
    address[] calldata accounts,
    address collateralToken
) external nonReentrant;

liquidateExact() - Used for exact amount liquidations.

Use when:

  • You want to repay an exact amount of debt.

function liquidateExact(
    uint256[] calldata debtAmounts,
    address[] calldata accounts,
    address collateralToken
) external nonReentrant;

Token Flows

What the Liquidator Pays:

Liquidator → Debt Token (underlying) → Borrowable cToken
Amount: result.debtRepaid

What the Liquidator Receives:

Collateral cToken → seize() → Liquidator
Amount: result.liquidatedShares (in cToken shares)

Examples

Executing maximum liquidations

  // Prepare sorted accounts
  const accounts = [
      "0x1111111111111111111111111111111111111111",
      "0x2222222222222222222222222222222222222222",
      "0x3333333333333333333333333333333333333333"
  ];
    // Check liquidation amounts
  const debtAmounts = [0, 0, 0];

  const action = {
      collateralToken: collateralCToken,
      debtToken: debtCToken,
      numAccounts: 3,
      liquidateExact: false,
      liquidatedShares: 0,
      debtRepaid: 0,
      badDebt: 0
  };

  // Approve total debt to repay
  const approveTx = await underlyingDebt.approve(debtCToken, result.debtRepaid);
  await approveTx.wait();

  // Execute batch liquidation
  const liquidateTx = await borrowableCUSDC.liquidate(
      accounts,
      collateralCToken
  );
  await liquidateTx.wait();

  console.log("All three accounts liquidated in one transaction");

Executing Exact Amount Liquidation

  // Liquidate exactly 500 USDC, and store into an array.
  
  const accounts = ["<borrowerAddress>"];
  const debtAmounts = [ethers.utils.parseUnits("500", 6)];
  
  // Get underlying debt token
  const underlyingDebtAddress = await borrowableCUSDC.asset();
  const underlyingDebt = new ethers.Contract(
      underlyingDebtAddress,
      ["function approve(address,uint256) returns (bool)"], // abi
      signer
  );

  // Approve tokens
  const approveTx = await underlyingDebt.approve(debtCToken, debtAmounts[0]);
  await approveTx.wait();
  
  // Execute exact liquidation
  const liquidateTx = await borrowableCUSDC.liquidateExact(
      debtAmounts,
      accounts,
      collateralCToken
  );
  await liquidateTx.wait();

  console.log("Liquidated a single account for an exact amount");

Liquidation Tips

Finding Liquidation Opportunities:

  • Monitor price feeds for significant collateral price drops or debt price increases.

  • Track accounts with debt close to their maxDebt limit (high-leverage positions).

  • Focus on volatile collateral assets during market downturns.

  • Set up event listeners for Borrow events to catch new leveraged positions early.

Using Smart Contracts vs Scripts:

Smart contracts are strongly recommended over off-chain scripts for executing liquidations:

  • Atomic profitability checks - Contract can calculate profit on-chain and revert if unprofitable, saving you from wasting gas on bad liquidations.

  • Flash loan integration - Borrow capital, liquidate, and repay all in one atomic transaction with zero upfront capital.

  • Composability - Can integrate with DEX swaps to instantly convert seized collateral to stablecoins.

A typical smart contract liquidator pattern: check profitability → revert if not profitable → execute liquidation → swap collateral → ensure profit threshold met. This guarantees you never execute an unprofitable liquidation.

Last updated