Key Structures
Reference
All structs used by Phoenix are defined in the phoenix-v1 crate.
Market Structure
The market account is structured as a MarketHeader
followed by a FIFOMarket
.
/*
*****************
Top-level structs
*****************
*/
pub struct MarketHeader {
pub discriminant: u64,
pub status: u64,
/// The size params of the market.
pub market_params: MarketSizeParams,
/// The specification of the base token of the market.
pub base_params: TokenParams,
/// The lot size of the base token of the market, in base atoms.
base_lot_size: u64,
/// The specification of the quote token of the market.
pub quote_params: TokenParams,
/// The lot size of the quote token of the market, in quote atoms.
quote_lot_size: u64,
/// The number of quote atoms per base unit per tick in the market.
tick_size_in_quote_atoms_per_base_unit: u64,
/// The Pubkey of the market authority.
pub authority: Pubkey,
/// The fee recipient of the market
pub fee_recipient: Pubkey,
/// The sequence number of the market.
pub market_sequence_number: u64,
/// The authority's successor. This key must sign the `ClaimAuthority`
/// instruction to become the new market authority
successor: Pubkey,
pub raw_base_units_per_base_unit: u32.
_padding1: u32,
_padding2: [u64; 32],
}
pub struct FIFOMarket<
const BIDS_SIZE: usize,
const ASKS_SIZE: usize,
const NUM_SEATS: usize,
> {
/// Padding
pub _padding: [u64; 32],
/// Number of base lots in a base unit. For example, if the base lot
/// size (in base units) is 0.001 SOL, then base_lots_per_base_unit is
/// 1000.
pub base_lots_per_base_unit: u64,
/// Tick size in quote lots per base unit. For example, if the tick size
/// (in quote units per base units) is 0.01 USDC per SOL and the quote
/// lot size (in quote units) is 0.001 USDC, then
/// tick_size_in_quote_lots_per_base_unit is 10.
pub tick_size_in_quote_lots_per_base_unit: u64,
/// The sequence number of the next event.
order_sequence_number: u64,
/// There are no maker fees. Taker fees are charged on the quote lots
/// transacted in the trade, in basis points.
pub taker_fee_bps: u64,
/// Amount of fees collected from the market in its lifetime, in quote
/// lots.
collected_quote_lot_fees: u64,
/// Amount of unclaimed fees accrued to the market, in quote lots.
unclaimed_quote_lot_fees: u64,
/// Red-black tree representing the bids in the order book.
pub bids: RedBlackTree<FIFOOrderId, FIFORestingOrder, BIDS_SIZE>,
/// Red-black tree representing the asks in the order book.
pub asks: RedBlackTree<FIFOOrderId, FIFORestingOrder, ASKS_SIZE>,
/// Red-black tree representing the authorized makers in the market.
pub traders: RedBlackTree<Pubkey, TraderState, NUM_SEATS>,
}
/*
*************
Inner structs
*************
*/
pub struct MarketSizeParams {
pub bids_size: u64,
pub asks_size: u64,
pub num_seats: u64,
}
pub struct TokenParams {
/// Number of decimals for the token (e.g. 9 for SOL, 6 for USDC).
pub decimals: u32,
/// Bump used for generating the PDA for the market's token vault.
pub vault_bump: u32,
/// Pubkey of the token mint.
pub mint_key: Pubkey,
/// Pubkey of the token vault.
pub vault_key: Pubkey,
}
pub struct FIFOOrderId {
/// The price of the order, in ticks. Each market has a designated
/// tick size (some number of quote lots per base unit) that is used to
/// convert the price to ticks. For example, if the tick size is 0.01,
/// then a price of 1.23 is converted to 123 ticks.
///
/// If the quote lot size is 0.001, this means that there is a spacing of
/// 10 quote lots in between each tick.
pub price_in_ticks: u64,
/// This is the unique identifier of the order, which is used to
/// determine the side of the order.
/// It is derived from the sequence number of the market.
///
/// If the order is a bid, the sequence number will have its bits
/// inverted, and if it is an ask,the sequence number will be used as is.
///
/// The way to identify the side of the order is to check the leading bit
/// of `order_id`. A leading bit of 0 indicates an ask, and a leading bit
/// of 1 indicates a bid.
pub order_sequence_number: u64,
}
pub struct FIFORestingOrder {
/// The index of the trader who placed the resting order
pub trader_index: u64,
/// Number of base lots quoted
pub num_base_lots: BaseLots,
/// The last slot for which the order is valid. If the current slot time
/// is greater than this value, the order will be cancelled on match.
///
/// If this value is set to 0, it will be ignored
pub last_valid_slot: u64,
/// The last unix timestamp in seconds for which the order is valid. If
/// the current unix timestamp is greater than this value, the order
/// will be cancelled on match.
///
/// If this value is set to 0, it will be ignored
pub last_valid_unix_timestamp_in_seconds: u64,
}
The MarketHeader
struct has a fixed size. To deserialize the entire market, first deserialize the MarketHeader
to read its params
. That will give you the information to deserialize the FIFOMarket
struct that follows.
// Read data from the market account
let market_account_data = client.get_account_data(market_key).await.unwrap();
// MarketHeader is fixed size; split the market account bytes into header bytes
// and market bytes
let (header_bytes, market_bytes) =
market_account_data.split_at(size_of::<MarketHeader>());
// Deserialize the header
let header = MarketHeader::try_from_slice(header_bytes).unwrap();
// Use the size params from the header to deserialize the market
let market = load_with_dispatch(&header.market_params, market_bytes).unwrap().inner;
You can find working sample code to load markets in the developer SDK.
Matching Logic
Limit orders are placed on the book and held in the bids
and asks
fields of the Market
. Orders are matched by price-time priority: orders are executed based on best price, and if multiple orders are at the same price, the order with the earliest time will be matched first.
Last updated