Watching positions
Every short is aShortPosition account at a program-derived address (["short", market, owner]). Fetch all
of them per market with getProgramAccounts filtered on the account
discriminator and the market field, then compute each position’s health:
funding_index and index_twap live on the Market account, so one
market fetch prices every position in it. Re-check after every oracle push
(subscribe to the market account or the IndexPushed event) since index
moves, not time, are what push positions underwater.
Executing a liquidation
The liquidator repays the position’s full debt from their own wallet (the tokens return to the market’s treasury reserve), so you need synth inventory first. Buy it from the AMM in the same transaction or hold a standing balance.Economics
Your gross margin per liquidation is the bonus (liq_bonus_bps, for example
5% of debt value) minus:
- AMM slippage and fee when acquiring the synth to repay with. For large positions this is the dominant cost; check pool depth against position size before committing.
- Transaction fees (negligible on Solana).
- Inventory risk if you pre-hold synth between opportunities.
Running the funding crank
accrueFunding() is permissionless and takes only the market account. It
updates the mark EMA and applies the clamped funding rate to the global
funding index. It rejects calls more frequent than
min_crank_interval_secs with CrankTooSoon.
Liquidation and the crank both require the market to be
Live and not frozen,
and liquidation requires a nonzero index. During a
circuit breaker freeze, positions cannot be
liquidated; expect a queue of eligible positions when the market unfreezes,
and price the reopening race accordingly.
