Kamino and Marginfi Liquidation Bots on Solana: How They Work
A Solana liquidation bot for Kamino and Marginfi: how to monitor health factors, race other bots to underwater positions, and land the transaction first.
Liquidations on Solana money markets settle in a single slot, and the bot that lands its transaction first takes the collateral discount. That's the whole game on Kamino and Marginfi: watch every borrower's health, and the moment one crosses into insolvency, be the account that repays their debt and seizes their collateral at a bonus. Miss by one slot and someone else already did it.
This corner of Solana bot development gets far less attention than sniping or arbitrage, which is exactly why it's interesting. The competition is thinner, the mechanics are well-documented on-chain, and the profit per successful liquidation is often larger than a good arb. But the engineering is unforgiving — you're indexing thousands of positions in real time and racing other bots to a target that appears without warning.
What a liquidation actually is
Both Kamino Lend and Marginfi are over-collateralized lending protocols. A user deposits SOL, mSOL, USDC, whatever, and borrows against it. The protocol tracks a health factor — roughly, the risk-adjusted value of collateral divided by the risk-adjusted value of debt. When that ratio drops below 1.0 (the exact threshold depends on each asset's liquidation LTV), the position is eligible for liquidation.
A liquidator repays some or all of the borrower's debt and, in exchange, claims an equivalent chunk of collateral plus a liquidation bonus (also called the liquidation penalty from the borrower's side). On Kamino this bonus is typically 5–10% depending on the reserve; on Marginfi the liquidator fee plus insurance fee lands in a similar range. So if you repay $10,000 of USDC debt against a position collateralized in SOL, you might receive ~$10,500–$11,000 of SOL. That spread, minus your swap slippage and priority fees, is your profit.
The catch: you have to source the repayment asset, and you end up holding the seized collateral. Both need to be handled atomically or near-atomically, or price moves against you before you can flatten.
The monitoring problem
You can't poll. Fetching every obligation account (Kamino) or marginfi account (Marginfi) via getProgramAccounts on a loop is slow, rate-limited, and stale by the time you parse it. The real approach is to build a local index and keep it warm.
The pattern most serious liquidation bots use:
- Snapshot once. On startup, pull all obligation/marginfi accounts with a filtered
getProgramAccountscall (usedataSliceandmemcmpfilters to cut payload). Decode them into a local map keyed by account pubkey. - Subscribe to changes. Open account or program subscriptions and apply deltas as they land. Better: consume a Geyser/Yellowstone gRPC stream so you get account writes at the same time the validator does, not after a WebSocket round-trip.
- Recompute health on price ticks. A position's health doesn't only change when the account is written — it changes when the oracle price moves. Kamino and Marginfi both use Pyth and Switchboard feeds. You need to re-evaluate every position that references a feed whenever that feed updates, because a SOL price drop can push hundreds of positions underwater simultaneously without any of their accounts being touched.
That third point is where naive bots fail. They watch accounts, miss the oracle-driven cascade, and show up late to the exact liquidations that matter — the ones triggered by a sharp move, which is when the biggest positions go underwater at once.
Keeping a Geyser feed and a local health engine in sync is real infrastructure work, and it's the same backbone you'd build for a wallet-tracking or MEV system. If you don't already run this, our Solana infra and data services exist for exactly this layer, and the same feed powers a real-time wallet tracker.
A minimal health check
The math you run on every position, per reserve, looks like this:
weighted_collateral = Σ (deposit_amount_i * price_i * liquidation_ltv_i)
weighted_debt = Σ (borrow_amount_j * price_j / debt_weight_j)
health = weighted_collateral / weighted_debt
# eligible when health < 1.0
if health < 1.0:
max_repay = weighted_debt * close_factor # Kamino caps partial repay
enqueue_liquidation(account, reserve, max_repay)
The close_factor matters: neither protocol lets you always repay 100% in one shot. Kamino applies a close factor so you liquidate a fraction of the debt per call when the position isn't deeply underwater, which means a single fat position can support several liquidation transactions from competing bots. Plan for partial fills.
Winning the race
Detecting the liquidation is half the job. Landing it is the other half, and it's the same latency problem every competitive Solana bot fights.
- Transaction construction has to be pre-warmed. You should already have the repay-and-liquidate instruction template built, with ALTs (address lookup tables) resolved, before the position goes underwater. When health crosses the line you're filling in amounts and signing, not assembling from scratch.
- You need the repayment asset ready or flash-sourced. Some bots hold inventory of common debt assets (USDC, SOL) so they can repay instantly. More sophisticated ones wrap the liquidation in a flash loan — borrow the repay asset, liquidate, swap the seized collateral back, repay the flash loan, all in one transaction. If you're already building atomic multi-leg transactions for MEV and arbitrage, the liquidation leg drops into the same bundle structure cleanly.
- Priority and inclusion decide the winner. Two bots detect the same underwater position in the same slot. The one whose transaction the leader includes first wins; the loser's transaction fails because the position's already liquidated (or partially, changing the amounts). This is where Jito bundles, priority fees, and validator connectivity separate winners from losers.
On that last point: the difference between landing and getting dropped often comes down to how your transactions reach the leader. It's worth understanding stake-weighted QoS and how it prioritizes transactions, because a liquidation bot sending through a normal RPC is competing at a structural disadvantage. Related, if your liquidation transactions are silently vanishing under load, the cause is frequently QUIC connection throttling and transaction drops rather than anything wrong with your logic. And for the detection side, feeding your health engine from Jito's ShredStream low-latency shred feed buys you a head start on seeing state changes before they're confirmed.
Gotchas that cost real money
Oracle staleness reverts. Both protocols reject liquidations when the price feed is stale (Pyth publish time too old). During volatile moves, feeds sometimes lag — your liquidation is valid by your local math but reverts on-chain because the oracle account hasn't been crank-updated. Some bots crank the oracle themselves in the same transaction.
You're now holding illiquid collateral. Seizing $11k of some thin LST is only profit if you can sell it without eating the bonus in slippage. Price the exit before you liquidate, not after. Thinly-traded collateral is a trap.
Failed transactions still cost fees. In a hot race you'll land maybe 1 in 5 attempts against strong competition. Your priority fees on the 4 losers come straight out of the winner's margin. Track your land rate obsessively; if it's below ~20% against a given position type, you're subsidizing everyone else.
Simulation lies under contention. A transaction that simulates clean can revert on-chain because the position changed between your simulation slot and inclusion. Build for revert, size priority fees against expected profit, and never assume simulation success means landing success.
The teams that make money here treat liquidation as a latency-and-inventory problem, not a smart-contract problem. The contracts are the easy part. The hard part is the same infrastructure edge that wins MEV and arbitrage and sniper races — fast state, pre-built transactions, and privileged transaction routing.
If you want a Kamino/Marginfi liquidation bot built on infrastructure that actually lands transactions, talk to us about a custom MEV and arbitrage build.
Need a bot like this built?
We design, build and run trading bots on Solana, Hyperliquid and Polymarket.
Start a projectMore from the blog
Public vs Paid RPC: How to Run a Fair Benchmark
A fair comparison of public and paid RPC endpoints must account for quotas, workload, freshness and support guarantees.
Read articleRPC Benchmark Percentiles Explained: p50, p95 and p99
Why averages hide the latency spikes that break trading bots, wallets and production blockchain applications.
Read articleHyperliquid REST vs WebSocket Benchmark: What to Measure
A benchmark plan for Hyperliquid market data that separates request latency from streaming freshness and recovery.
Read article