Skip to content
LogoLogo

Liquidations

When an account's equity can no longer cover its maintenance margin, the program lets anyone reduce its risk. Liquidation on Flint is permissionless, partial and on-chain: a keeper closes just enough of the account to bring it back above maintenance, at prices the program bounds, and is paid a share of a fee for doing it.

When an account is liquidatable

liquidatable  ⇔  net_equity < MM

measured at forced-action fair prices — the oracle's unbanded fair price, not the conservative risk-on prices used to open positions. net_equity and MM are the same totals described in Collateral & margin: weighted collateral, debt, weighted perp PnL (including accrued funding and unsettled fees), and the maintenance margin of every borrow and perp position.

The API flags it as meets_maintenance_margin = false, a positive deficit, or a health_ratio below 1.

Resting limit orders reserve initial margin only, so they never make an account liquidatable. Before that, once an account falls below initial margin, anyone may call force_cancel_perp_orders to take its margin-reserving orders off one contract's book; its reduce-only orders stay. See Orders.

Who liquidates

Anyone. There is no privileged liquidator: a keeper with its own trader account calls the instruction, and the program checks every condition itself. Flint runs keepers that watch account health and liquidate; any third party can do the same. In a perp close the liquidator takes on no position — it is paid in fee ticks only.

Closing perp positions

liquidate_perp closes part of one position on one contract. It first cancels every order the liquidatee rests on that contract, reduce-only ones included. The liquidatee is then the taker: the close crosses the makers' quotes and resting orders exactly like an order, and the makers take the other side. The liquidator must pass every enabled maker quoting slot except the liquidatee's, so a close can't skip live quotes; owners of resting orders are optional.

Sizing. The program sizes the close itself, level by level, until the account's maintenance deficit plus a 1% buffer is covered, so the account lands just above maintenance rather than on the line. The close is then bounded per call by the contract's:

TermListing defaultEffect
min_liquidation_notional_usd$1,000A smaller close is lifted to it; a smaller position is closed whole
max_liquidation_notional_usd$100,000Large positions unwind in tranches across several calls
max_liquidation_band0.1%Every fill must land within mark × (1 ± band)

The band is the liquidatee's protection against a bad choice of makers: a close that cannot fill inside it does not fill.

Charges. The liquidatee pays the normal taker fee on the close, plus a liquidation_fee on the closed notional:

ChargeListing defaultPaid to
Taker feeContract's taker_feeMaker rebate share to the makers, the rest to the protocol
Liquidation fee0.05% of notional (cap 5%)liquidator_share (default 50%) to the liquidator, the rest to the protocol

The liquidation fee is capped at the equity the account has left, so the fee can never itself create bad debt.

Guarantees. The instruction reverts unless the account's maintenance surplus is no worse after the close than before, and every maker filled still passes its own margin check.

Repaying borrows

Spot debt — including debt created by settling perp losses — is reduced with liquidate_spot, one (borrow, collateral) pair per call. The liquidator repays part of the borrow and seizes collateral worth 1 + liquidation_bonus per dollar repaid, where the bonus is set per seized asset. The pair and size are chosen to clear the maintenance deficit plus the same 1% buffer, and the liquidator must itself stay above initial margin.

Backstop

Some accounts fall too far, too fast for small closes to help. Once

net_equity ≤ MM × backstop_mm_ratio      (default ratio: 0.5)

or equity is outright negative, any account may call backstop and take over the same percentage of every spot and perp position of the distressed account at once. The takeover:

  • must not reduce the distressed account's maintenance surplus,
  • must leave the backstopper above initial margin, and
  • must take everything, or leave the distressed account with at least $500 of net equity.

The distressed account's resting orders stay on the book.

Backstopping is voluntary. It replaces a dedicated backstop fund with last-resort takers who judge the account worth absorbing.

Bad debt

If an account's debts outlive all of its collateral, what remains is bad debt.

  • Spot debt is written off by socialize_loss (Flint admin, only for an account with strictly negative unweighted equity and at most $1 of collateral left). The insurance fund's balance of the same asset is drawn first, up to the configured fraction per call; any remainder lowers the market's deposit share price, spreading the loss across that market's depositors.
  • Perp winners are paid from each contract's settle pool, which holds the losses losers have actually paid in. A gain is settled only as far as the pool reaches; the rest stays owed and is paid as further losses arrive.

The insurance fund is funded by a configurable share of collected fees (see Fees); RiskService.GetInsuranceFund reports its balances.

Worked example

Continuing the margin example: long 50 SOL-PERP from $150, USDC balance 992.50 after fees, devnet parameters (mmf = 5%, USDC deposit_weight = 0.9, taker fee 0.10%, liquidation fee 0.05%, liquidator_share = 50%, min close $1,000). The liquidation price was about $137.81.

SOL marks at $137.

USDC balance + PnL  = 992.50 − 50 × 13            = 342.50
net_equity          = 342.50 × 0.9                = 308.25
MM                  = 0.05 × 50 × 137             = 342.50
deficit             = 342.50 − 308.25             = 34.25
target (+1%)        ≈ 34.59

Each SOL closed at the mark releases 0.05 × 137 = 6.85 of MM and costs 0.15% in fees (0.2055, or 0.185 of equity after the 0.9 weight), a net gain of about 6.67 per SOL. Clearing the target needs about 5.2 SOL ($711) — below the $1,000 floor, so the close is lifted to it: 7.289 SOL, $998.59 of notional.

taker fee           = 0.10% × 998.59   ≈ 1.00   (0.50 to makers, 0.50 protocol)
liquidation fee     = 0.05% × 998.59   ≈ 0.50   (0.25 liquidator, 0.25 protocol)

after the close (filled at the mark):
remaining position  = 42.711 SOL
net_equity          = (342.50 − 1.50) × 0.9      ≈ 306.90
MM                  = 0.05 × 42.711 × 137        ≈ 292.57
health ratio        ≈ 1.05

The account is back above maintenance and keeps the rest of its position. Fills worse than the mark (up to the 0.1% band) cost a little more equity per SOL and would size the close slightly larger.

Avoiding liquidation

  • Watch health, not price. Every position, borrow and funding accrual feeds one number. SubscribeAccount streams it; aim to stay well above a health_ratio of 1.
  • Hold settlement-asset cash. Perp losses settle in USDC. Without USDC behind them they become USDC debt with its own, much larger margin requirement (see the warning).
  • Size with free collateral to spare. Opening at exactly initial margin leaves only the imf − mmf band before liquidation.
  • Reduce early. Below initial margin you can still close: a reduce-only fill only has to clear maintenance.
  • Expect to lose resting orders under stress. Below initial margin anyone can force-cancel your margin-reserving orders, and a liquidation cancels all of them on the contract it closes.
  • Mind funding. A large adverse funding rate drains equity hour after hour, capped by the contract's maximum rate.
  • Use the simulators. RiskService.SimulateAccountRisk applies price shocks to your account; PreviewOrder shows the margin an order leaves you with.