Skip to content
Logo

How Flint perps work

Flint perps are linear perpetual futures on Solana. The margin program stores markets, trader accounts, maker quotes and resting orders. It matches orders and enforces margin, funding, settlement and liquidation rules. Flint's off-chain indexer, gRPC gateway, keepers and trading app read state and prepare or submit transactions. The program validates those transactions on-chain.

This page explains the main account and market rules. See margin, funding, liquidations, fees and units.

Account and market structure

          ┌──────────────── one trader account (cross-margined) ───────────────┐
          │                                                                     │
          │  spot balances            perp positions                            │
          │  ─────────────            ──────────────                            │
          │  USDC   +1,000  ◄──────── PnL, funding and fees of every perp       │
          │  SOL    +10               settle into its settlement asset          │
          │  …                        SOL-USDC-PERP  long 50                    │
          │                           BTC-USDC-PERP  short 0.2                  │
          │                                                                     │
          │        account health:  net equity  vs  IM / MM                    │
          └─────────────────────────────────────────────────────────────────────┘

These rules apply across the markets:

  1. Your account is a balance sheet. Deposits of any listed asset are collateral. A perp position adds market risk (its margin) and routes its PnL, funding and fees into a spot balance of the contract's settlement asset. Health is assessed across the account; positions do not have separate isolated margin.
  2. The oracle determines the mark. A contract's mark is the oracle price of the underlying, placed on the contract's tick grid. Your unrealized PnL, margin and liquidation are all measured against it. Book prices determine execution and contribute to funding.
  3. Liquidity rests on-chain. Each contract has a set of market makers whose quotes live on-chain, and an order book where any trader can rest limit orders. An order crosses that liquidity in one transaction. Its kind determines whether an unfilled remainder rests, is dropped, or causes the order to revert.
  4. Each trade instruction is atomic. Fills, fees and margin checks execute in the same instruction. An instruction error reverts all of its state changes.

Markets

A market is a linear perp: a contract whose PnL is size × Δprice, paid in a spot asset called its PnL currency (settlement asset). On the current listings that asset is USDC, so SOL-USDC-PERP is quoted in USDC per SOL and settles in USDC.

Each contract fixes, at listing:

FieldWhat it sets
underlying_oracle_idThe oracle entry the mark is derived from
pnl_currencyThe spot asset PnL, fees and funding settle in
lot_scaleSize granularity: one lot is 10^-lot_scale of the underlying
tick_sizePrice granularity: PnL-currency atoms per tick
imf / mmfInitial / maintenance margin fraction of notional
unpl_weightHaircut applied to positive unrealized PnL
taker_fee / maker_rebate_shareThe trading fee and the maker's share of it
max_open_interest_lotsCap on long-side lots outstanding
max_position_lotsCap on one trader's net size either way, resting orders counted as filled
liquidation termsFee, liquidator share, price band, per-close notional bounds
funding parametersHow the funding rate is measured and capped

taker_fee and maker_rebate_share can never change after listing. Margin fractions and most funding parameters change only through a 24-hour timelock (RISK_UPDATE_DELAY_SECS), so a position is never re-margined without notice.

Read live values with PerpMarketService.ListPerpMarkets (grid, margin rates, open-interest cap) and AccountService.GetTradingFees. See Units for how the grid maps to human prices.

Mark, index and the oracle

The program reads prices from an on-chain oracle cache that keepers fill from Pyth and Chainlink feeds. Each asset is registered as a single-source or quorum entry under a risk tier, which sets how fresh a feed must be, how far sources may disagree, and how uncertain a price may get before the program stops trusting it.

From each oracle entry the program materializes two kinds of price:

PriceUsed forShape
Risk-on (collateral credit / liability cover)Opening risk, borrowing, withdrawingThe fair price shaded against you by a confidence band: collateral valued low, debt and perp notional valued high
Forced-action fairMarks, liquidations, backstopsThe unbanded fair price, usable for a bounded time after the feed goes quiet

The index is the underlying's fair price per base unit. The mark is that same price rounded to the nearest tick on the contract's grid. The PnL currency is taken at par with USD for the mark, so the mark moves only with the underlying; a stablecoin depeg shows up in how your collateral is valued instead.

A mark is valid for 60 seconds (and never longer than the oracle evidence it came from). An order or liquidation against an expired mark is refused, so transactions usually bundle a mark refresh (sync_linear_perp_cache) ahead of the order. A newly listed contract has no mark until a keeper publishes one, and cannot trade until then.

The order book as a trader sees it

Each contract holds liquidity in two accounts:

  • A quoting account with a slot per maker, up to 32 per contract. A maker writes an explicit order list or bands at offsets from its fair price into the slot. The maker refreshes its prices, and quotes expire at slot-based deadlines.
  • An order book (order_book PDA) of resting limit orders that any trader can post: at most one per trader per side and price, at most 16 per trader per contract. Gtc orders have no expiry; Gtd orders expire at an absolute slot.

When you trade, the program builds a virtual book for that order: it merges the quotes and resting orders of the makers your transaction includes, best price first. Matching stops when the size is filled or a price bound prevents another fill. Within one price level, makers share fills pro rata, with no time priority. A quoted level is checked for its full quantity after the position cap is applied. If the maker cannot support it under margin or leverage, that level and deeper quote levels are not offered for this order. A resting order that fails its owner's margin check is skipped; later orders can still fill. Other validation failures can revert the transaction.

MarketDataService supplies the merged view of enabled, unexpired maker quotes. The app combines those quotes with resting orders read directly from the on-chain book. Displayed depth can exceed executable depth because of maker margin, the open-interest cap, price bounds, or transaction account limits. PreviewOrder simulates an immediate order against the makers included in its simulation.

Flint grants a trader quoting permission (MAY_QUOTE); the maker then opens and releases its own slot on each contract. See Maker onboarding and Quoting for the maker side.

Orders

On-chain, place_perp_order takes a size, direction and kind. The kind contains the price bounds and controls any unfilled quantity:

KindPrice fieldsResult
Gtclimit, post_onlyFill within the limit and rest the remainder until cancelled.
Gtdlimit, post_only, expires_at_slotFill within the limit and rest the remainder until its absolute expiry slot.
Iocbound, min_fill_lotsFill at least the minimum and at least one lot within the bounds; drop the remainder.
FokboundFill every requested lot within the bounds, or revert.

For Gtc and Gtd, post_only: true refuses the order if any quantity would cross the makers included in the transaction. An omitted maker does not prevent the order from resting.

For Ioc and Fok, bound is a TakerBound { limit, average }. limit bounds each fill; optional average bounds the volume-weighted average fill price before fees. Prices are positive UInt32 ticks: a long accepts prices at or below its bound; a short accepts prices at or above it. The average bound must be at or inside the individual limit.

ArgumentMeaning
directionLong (buy) or Short (sell)
lotsSize budget, in lots
reduce_onlyLimit fills to the opposing position; a resting remainder reserves no margin and each later fill is limited to the position then held
kindOne of Gtc, Gtd, Ioc, or Fok, with the fields above
client_order_idYour own identifier, echoed on the order's events
book_scanHow many resting orders the crossing may read

A Market order in the app uses Fok, with an individual price limit derived from the mark and your maximum slippage. Limit defaults to Gtc and also offers Gtd, Ioc and Fok. The app sets no average-price bound. Resting orders are changed with modify_perp_order and removed with cancel_all_perp_orders; anyone may prune expired ones or force-cancel those of an account below initial margin. A resting order that is not reduce-only reserves initial margin. Increasing that reserve must also satisfy the account's leverage cap. Stop and take-profit orders are not supported. See Orders.

Accounts and collateral

A trader account is a program-owned PDA seeded by a numeric trader_id its creator picks, with an owner wallet that signs for it. One account holds:

  • up to max_spot_positions spot balances (collateral, lent deposits, borrows), and
  • up to max_linear_perp_positions perp positions,

both set in the program's global config. The owner can authorize delegates — another wallet or a passkey — scoped to trading, so an app can sign orders without the owner key. See Wallets & sessions. The account may also hold itself to a leverage cap stricter than the markets' own; see Collateral & margin.

Any listed spot asset can be deposited as collateral. Each one counts at a deposit_weight haircut, and may also be lent to earn interest or borrowed against. See Collateral & margin.

The lifecycle of a trade

  1. Fund. Create a trader account and deposit collateral. The deposit lands as idle ("isolated") balance in that asset.
  2. Price. Keepers keep the oracle cache published and each contract's mark and funding fresh.
  3. Order. You (or your delegate) send place_perp_order with the maker accounts to cross. The app selects them from the quoting account and order book.
  4. Match. The program checks the mark is current, merges the makers' quotes and resting orders, walks them up to your limit and size, prices each trade's fee, and applies the fill to your position and each maker's. A Gtc or Gtd remainder rests on the book; with post_only, any immediate fill is refused. An Ioc remainder is dropped, and Fok requires the full size to fill.
  5. Check. Every account touched must pass margin: initial margin (and the account's own leverage cap, if set) if the fill added exposure or reserve, maintenance if it only reduced it. The open-interest cap, position size cap and position-count limits are enforced. Any failure reverts the whole order.
  6. Hold. The position marks to the oracle and accrues funding continuously. Health includes unrealized PnL, accrued funding and unsettled fees at all times.
  7. Settle. Closing realizes PnL onto the position as unsettled ticks. A permissionless settle_realised_pnl moves realized PnL, funding and fees into your settlement-asset balance — gains as far as losers have paid in, losses from idle cash first and then by borrowing the shortfall.
  8. Withdraw. Withdrawals spend idle balance and must leave the account above initial margin.

If health falls below maintenance at any step, anyone may liquidate the account.

On-chain vs off-chain

On-chain (the program)Off-chain (Flint services)
Markets, grids, risk and fee parametersgRPC gateway: market data, accounts, risk, funding, history
Oracle cache and price validationIndexer mirroring program accounts in near real time
Makers' quotes, resting orders and the matching engineOrder preview by transaction simulation
Trader accounts, positions, balancesHistorical fills, candles and account history
Margin checks, funding index, feesOptional fee-payer sponsorship for app orders
Liquidation, backstop, settlementKeepers that crank prices, funding, settlement and liquidations

The upkeep instructions listed here are permissionless: anyone can refresh a mark, settle funding, settle PnL, prune expired orders, force-cancel eligible orders, or liquidate. The program validates each action. API responses include DataProvenance with commitment, slot and observation time. Use on-chain state to resolve differences with API data.

Glossary

TermMeaning
Trader accountYour cross-margined account on the program, keyed by trader_id
CollateralAny deposited spot balance; counts at its deposit_weight
PnL currencyThe spot asset a contract settles in (USDC on current listings)
LotThe contract's size unit: 10^-lot_scale of the underlying
TickThe contract's price unit: tick_size atoms of the PnL currency per lot
IndexThe underlying's oracle fair price per base unit
MarkThe index on the contract's tick grid; what PnL and margin use
Notional`
IM / MMInitial / maintenance margin: notional × imf / mmf, plus borrow margins
Net equityWeighted collateral − debt + weighted PnL, in USD
Free collateralNet equity − IM, floored at zero
Health ratioNet equity ÷ MM; below 1 is liquidatable
Taker / makerThe order that crosses / the quote or resting order it crosses against
IOCImmediate-or-cancel: fill now, drop the rest
FOKFill-or-kill: fill the full requested size within the price bounds, or revert
GTC / GTDGood-till-cancelled / good-till-slot-expiry resting orders
Resting orderA Gtc or Gtd remainder waiting on the contract's order book
Post-onlypost_only: true on Gtc or Gtd; the order must rest whole and is refused if it would cross an included maker
FundingPeriodic transfer between longs and shorts that ties the book to the index
Impact bid / askAverage price to fill a fixed notional into each side of the book
Open interestLong-side lots outstanding on a contract
KeeperAny bot calling the permissionless crank, settle and liquidate instructions
BackstopVoluntary takeover of a deeply underwater account by another account
Settle poolPer-contract pool of losses paid in, from which winners are paid