Skip to main content
Perps run on Phoenix (Ellipsis Labs’ perpetuals exchange). Vanna has no perps engine of its own. The margin account is its own Phoenix trader, and every step is a margin_execute call, health-checked after it. Sources: programs/vanna_validator/src/phoenix.rs, programs/vanna_oracle/src/phoenix.rs, vanna-credit-backend/lib/solana/{phoenix,perps}.ts.

Programs and accounts

The whole Phoenix trader account counts as one collateral line on the margin.

Lifecycle

Phoenix market orders are immediate-or-cancel and fill against the book inside the transaction. There is no keeper and no pending order.

Validator rules

The leverage cap is set per market in the market book (list_market). On the hosted fork, SOL, ETH and BTC are listed at 5x, and TSLA and META at 3x.

Valuation

The oracle values the trader account as:
Index and USDC prices carry the same freshness and TWAP checks as every other price. For borrowing, the oracle counts collateral and the losses of open positions, but not unrealized gains. The venue leaves the margin’s active list once nothing is open.

Liquidation

A Phoenix position can’t change hands, so public_liquidate takes venue calls first (calls: Vec<VenueCall>), run in liquidation mode:
  • Reduce-only closes priced within a band of the fresh index: 5% divided by the market’s leverage cap, at most 3%.
  • A withdrawal of the trader’s collateral up to what Phoenix’s withdraw budget pays now. It lands in the margin’s phUSD vault. phUSD is a registered collateral-only asset priced at USDC, so the liquidator can then seize it in the same instruction.
While anyone’s Phoenix withdrawal is queued, the withdrawal is refused, but the closes and the margin’s other tokens can still be liquidated. With at most 4 open positions, closing all of them, withdrawing and settling fits one transaction with a lookup table.

Known limits

  • A stock perp’s index (TSLA, META) goes stale while its market is closed, and liquidation closes need a fresh index. Those markets have lower leverage caps for that reason.
  • After a deposit into Phoenix, Phoenix refuses withdrawals for a short period. An unhealthy owner can’t deposit again, so the delay can’t be repeated.
  • On mainnet, Phoenix grants deposit, withdraw and order capabilities to onboarded traders itself. On the fork, the app’s /api/perps/phoenix-access route stands in for that onboarding.