Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.raydium.io/llms.txt

Use this file to discover all available pages before exploring further.

Stable AMM shares the pool-side account structure of AMM v4 (AmmInfo, vaults, authority) and additionally requires a ModelDataInfo account that stores the lookup table. This page covers both.

Inventory

A Stable AMM pool binds to exactly one OpenBook market. The full inventory mirrors AMM v4 closely:
CategoryAccountOwnerRole
PoolAmmInfoStable programPool state, references to vaults, OpenBook, and model-data account.
Poolamm_authorityStable programProgram-owned PDA that signs vault moves. Shared across all Stable AMM pools.
Poolamm_open_ordersOpenBookThe pool’s OpenBook OpenOrders account.
Poolamm_target_ordersStable programPool-side grid for limit orders.
Poolpool_coin_token_accountSPL TokenPool’s coin-side vault.
Poolpool_pc_token_accountSPL TokenPool’s pc-side vault.
Poollp_mintSPL TokenFungible LP mint.
Modelmodel_data_accountStable programThe lookup table: 50,000 × DataElement.
Marketserum_marketOpenBookOpenBook market.
Marketserum_bids, serum_asksOpenBookBid/ask queues.
Marketserum_event_queueOpenBookEvent queue.
Marketserum_coin_vault, serum_pc_vaultSPL TokenOpenBook market-level vaults.
Marketserum_vault_signerOpenBookMarket-level vault signer.

AmmInfo

Root state account. Layout is nearly identical to AMM v4 — pool params, decimals, fees, vault/mint references — with one addition: a model_data_key field pointing to the lookup table.
// raydium-stable/program/src/state.rs (abridged)
pub struct AmmInfo {
    pub account_type: u64,              // = 0 (AmmAccount)
    pub status: u64,                    // bitmask: swap/deposit/withdraw/crank enabled
    pub nonce: u64,                     // bump for amm_authority
    pub order_num: u64,
    pub depth: u64,
    pub coin_decimals: u64,
    pub pc_decimals: u64,
    pub state: u64,                     // state machine (IdleState, etc.)
    pub reset_flag: u64,
    pub min_size: u64,
    pub vol_max_cut_ratio: u64,
    pub amount_wave: u64,
    pub coin_lot_size: u64,             // mirrors OpenBook
    pub pc_lot_size: u64,
    pub min_price_multiplier: u64,
    pub max_price_multiplier: u64,
    pub sys_decimal_value: u64,
    pub abort_trade_factor: u64,
    pub price_tick_multiplier: u64,
    pub price_tick: u64,
    
    pub fees: Fees,                     // see below
    pub out_put: OutPutData,            // PnL, swaps, punish amounts
    
    pub coin_vault: Pubkey,
    pub pc_vault: Pubkey,
    pub coin_mint: Pubkey,
    pub pc_mint: Pubkey,
    pub lp_mint: Pubkey,
    pub model_data_key: Pubkey,         // ← THE LOOKUP TABLE
    pub open_orders: Pubkey,            // OpenBook OpenOrders
    pub serum_market: Pubkey,
    pub serum_program: Pubkey,
    pub target_orders: Pubkey,
    pub amm_admin: Pubkey,              // admin key
    pub client_order_id: u64,
    pub lp_amount: u64,                 // LP supply
    pub lp_net: u64,                    // LP value metric
    pub padding: [u64; 61],
}

pub struct Fees {
    pub min_separate_numerator: u64,
    pub min_separate_denominator: u64,
    pub trade_fee_numerator: u64,       // 25
    pub trade_fee_denominator: u64,     // 10_000 → 0.25%
    pub pnl_numerator: u64,             // 12
    pub pnl_denominator: u64,           // 100 → 12% of fee = 0.03% of volume
    pub swap_fee_numerator: u64,        // 25
    pub swap_fee_denominator: u64,      // 10_000
}

pub struct OutPutData {
    pub need_take_pnl_coin: u64,        // accrued protocol fee (coin)
    pub need_take_pnl_pc: u64,          // accrued protocol fee (pc)
    pub total_pnl_pc: u64,
    pub total_pnl_coin: u64,
    pub pool_open_time: u64,
    pub punish_pc_amount: u64,
    pub punish_coin_amount: u64,
    pub orderbook_to_init_time: u64,
    pub swap_coin_in_amount: u128,
    pub swap_pc_out_amount: u128,
    pub swap_pc_in_amount: u128,
    pub swap_coin_out_amount: u128,
    pub swap_pc_fee: u64,
    pub swap_coin_fee: u64,
}
Key integrator-facing fields:
  • model_data_key — the address of the lookup table. Must be passed to every instruction.
  • fees — identical structure to AMM v4. Defaults to 0.25% trade fee, 0.22% LP / 0.03% protocol split.
  • coin_vault, pc_vault — the pools’ vaults.
  • status — bitmask gating swap/deposit/withdraw/crank.
  • out_put.need_take_pnl_* — swept by WithdrawPnl.

ModelDataInfo

The lookup table. A large sparse array of price/quantity points.
// raydium-stable/program/src/state.rs
pub const ELEMENT_SIZE: usize = 50000;

pub struct DataElement {
    pub x: u64,         // table X (e.g., coin amount)
    pub y: u64,         // table Y (e.g., pc amount)
    pub price: u64,     // price at (x, y)
}

pub struct ModelDataInfo {
    pub account_type: u64,              // = 2 (ModleDataAccount)
    pub status: u64,                    // Initialized or Uninitialized
    pub multiplier: u64,                // scale factor for x, y (e.g., 10^6)
    pub valid_data_count: u64,          // how many elements are populated
    pub elements: [DataElement; 50000], // the table itself
}
Lifecycle:
  1. InitModelData creates the account and sets status = Initialized, multiplier = <admin-provided>, valid_data_count = 0.
  2. UpdateModelData (invoked up to 5 times per transaction) populates elements via:
    • Input: array of (index: u64, DataElement) pairs.
    • Writes each to elements[index].
    • Increments valid_data_count if index >= valid_data_count.
  3. Swap/deposit/withdraw call lookup functions that binary-search and interpolate within elements[0..valid_data_count].

DataElement

The atomic entry in the table. Must be sorted (x ascending, y descending, price ascending) for binary search to work.
pub struct DataElement {
    pub x: u64,         // X coordinate (e.g., token_a balance, scaled by multiplier)
    pub y: u64,         // Y coordinate (e.g., token_b balance, scaled by multiplier)
    pub price: u64,     // price (x/y in scaled form, scaled by multiplier)
}
When populating the table, the admin specifies these pre-scaled. The program does not validate sort order on-chain (for speed), so missorting causes incorrect quotes.

Authority and vaults

Same as AMM v4:
  • amm_authority is a single program-wide PDA derived with seed ["amm authority"]. It owns all pool vaults and signs their moves.
  • Vaults are SPL Token accounts whose owner is amm_authority, not ATAs.
Token-2022 is not supported.

Status bitmask

Identical to AMM v4. Controls whether swap/deposit/withdraw/crank are enabled.

Fee and PnL tracking

Same as AMM v4. The out_put struct tracks:
  • need_take_pnl_coin, need_take_pnl_pc — protocol fees accrued but not yet swept. WithdrawPnl moves these out.
  • swap_coin_in_amount, swap_pc_in_amount, etc. — analytics counters.

Account size

The ModelDataInfo is large (~1.2 MB, since 50,000 elements × 24 bytes per element). This is why creating a Stable pool requires explicit rent and account pre-allocation. The Raydium SDK and tools handle this transparently; integrators rarely need to manually allocate.

Deriving accounts from scratch

Like AMM v4, Stable AMM uses seeded keys (not pure PDAs). The canonical pool identity is derived via:
ammId = createWithSeed(
  owner: ammAuthority,
  seed: marketPubkey.toBase58().slice(0, 32),
  programId: STABLE_PROGRAM_ID,
)
Similarly for vaults, LP mint, target orders, etc. In practice, use the SDK or API to fetch pre-computed addresses.

What to read where

Sources: