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.
Program ID and PDA seeds for CPMM are listed canonically in
reference/program-addresses. This page focuses on what each account is for and the invariants it maintains, not the hardcoded addresses.The six accounts of a CPMM pool
Every CPMM pool is fully described by six program-derived addresses (PDAs) under the CPMM program, plus one sharedAmmConfig account it references. Once you have the two mints, you can derive everything deterministically without touching the network.
| Account | Seed(s) | Owner | Purpose |
|---|---|---|---|
authority | "vault_and_lp_mint_auth_seed" | CPMM | The signer for every vault move and every LP mint/burn. Shared across all CPMM pools. |
poolState | "pool", ammConfig, token0Mint, token1Mint or any signer-provided random keypair | CPMM | The pool’s state struct — mint pair, vault balances, LP supply, fee accrual, observation pointer. The CPMM Initialize instruction accepts either the canonical PDA derived from the four seeds or an arbitrary keypair signed by the creator. The random-keypair path exists to defeat a front-running attack where an adversary watches mempool and races to occupy the canonical PDA before the legitimate creator. |
lpMint | "pool_lp_mint", poolState | SPL Token | The pool’s LP token. Supply = total LP outstanding. Mint authority = the CPMM authority PDA. |
vault0 | "pool_vault", poolState, token0Mint | SPL Token / Token-2022 | Holds the pool’s balance of token0. Owned by the authority PDA. |
vault1 | "pool_vault", poolState, token1Mint | SPL Token / Token-2022 | Holds the pool’s balance of token1. Owned by the authority PDA. |
observation | "observation", poolState | CPMM | Ring buffer of price samples used for the TWAP. Written on every swap. |
| Account | Seed(s) | Owner | Purpose |
|---|---|---|---|
ammConfig | "amm_config", index: u16 | CPMM | Holds the trade/protocol/fund/creator fee rates and admin keys. One per “fee tier”. Poolstate binds to one at creation and cannot change later. |
Deriving a pool from nothing but two mints
Pool ID is not always the canonical PDA.
Initialize accepts an arbitrary signer keypair as pool_state in addition to the PDA above. If the passed account does not match the canonical PDA, the program requires it to be a signer — i.e., the creator passes a fresh keypair that they sign with. This is the front-run defence: any third party racing to grab the canonical PDA can be sidestepped by the legitimate creator using a random keypair instead. The downstream PDAs (lpMint, vault0, vault1, observation) are still derived from poolState.key(), so they remain unique to whichever address was used. When you index pools, always discover the pool ID from the on-chain state (e.g., PoolState accounts under the CPMM program), not by deriving the canonical PDA — the latter will miss random-keypair pools.Account layouts
The full Rust definitions live in theraydium-cp-swap source. The fields below are the ones you will read from an integration.
PoolState
lp_supply— the pool’s internal mirror of the LP mint’s total supply. Use it for LP-share math; the value should match the mint’s on-chain supply, but reading it fromPoolStateavoids an extra account fetch.protocol_fees_token{0,1},fund_fees_token{0,1}— accrued fees not yet swept. These do not affect swap pricing; they sit in the vaults untilCollectProtocolFee/CollectFundFeeis called.status— a bitmask controlling whetherSwap,Deposit,Withdraware allowed. Updated by the admin viaUpdatePoolStatus. The SDK checks this before building a transaction; if you are CPI-ing directly, check it yourself.token0_program/token1_program— the token program to CPI into for each vault. One can be classic SPL Token and the other Token-2022; they are independent.open_time— a Unix timestamp. Swaps before this time fail. Deposits are permitted beforeopen_timeso the pool can be seeded.creator_fee_on/enable_creator_fee— together control whether the optional creator fee is active for this pool and which side of the swap it is collected from.enable_creator_fee == falsezeroes the creator-fee path entirely. When enabled,creator_fee_onselects:0= take fee from whichever token is the swap input (BothToken);1= take fee fromtoken_0only (skip ontoken_1 → token_0swaps);2= take fee fromtoken_1only. Set at pool creation viaInitializeWithPermission; cannot change later.creator_fees_token_{0,1}— accrued creator fees, swept byCollectCreatorFee.
AmmConfig
trade_fee_rateandcreator_fee_rateare fractions of volume, both denominated in units of1/1_000_000.2500means 0.25% of the trade volume.protocol_fee_rateandfund_fee_rateare fractions of the trade fee (not of volume), in the same1/1_000_000denominator. The creator fee is not a fraction of the trade fee — it is its own independent rate. Full arithmetic is inproducts/cpmm/fees.indexis au16, so the seed hash uses 2 bytes big-endian. Off-by-one on the byte order is a common integration bug.AmmConfigis immutable at pool level. A pool points at oneAmmConfigat creation and never switches. Fee changes propagate because the pool reads the config each swap — but the pool cannot be moved between fee tiers.
creator_fee_rate) lives on AmmConfig and is shared across the fee tier. Whether a particular pool actually charges it (enable_creator_fee) and which side of the swap it lands on (creator_fee_on) live on PoolState. The creator fee is independent of the trade fee — it is its own rate, accrued to its own counters (creator_fees_token_{0,1}), and never reduces the LP / protocol / fund shares of the trade fee. Sweep is via CollectCreatorFee. See products/cpmm/fees for the full mechanics.
Permission
A small access-control account used by InitializeWithPermission. The CPMM program supports a permissioned pool-creation path so that other programs (e.g. LaunchLab when graduating a token to CPMM) can prove they are entitled to create a pool against a given AmmConfig.
CreatePermissionPda and revoked via ClosePermissionPda. End users do not interact with this account directly — it is plumbing for cross-program flows.
Vaults and Token-2022
vault0 and vault1 are owned by the CPMM authority PDA, and their token-program owner (token_program) is either SPL Token or Token-2022, determined at pool creation by the mint’s program. The pool handles the two cases transparently — you pass the right token-program ID for each side in the Swap / Deposit / Withdraw instruction accounts.
CPMM enforces a strict extension allow-list at pool creation (is_supported_mint in utils/token.rs). A Token-2022 mint can be used in a CPMM pool only if every extension it carries is on this list:
TransferFeeConfig. Applied by the mint on every transfer. The pool is on the receiving side forSwapBaseInputdeposits and the sending side for withdrawals. The program computes the net amount landing in the vault and sets the curve accordingly. Seealgorithms/token-2022-transfer-fees.MetadataPointerandTokenMetadata. Standard on-mint metadata. No effect on swap math.InterestBearingConfig. The mint’s UI amount accrues interest. The vault stores raw amounts; the curve operates on raw amounts only. UIs that show APR should call the Token-2022 helpers to render the UI amount.ScaledUiAmount. UI-display scaling extension. Same treatment asInterestBearingConfig— the curve uses raw amounts.
PermanentDelegate, TransferHook, DefaultAccountState, NonTransferable, ConfidentialTransfer, Group/GroupMember, MintCloseAuthority, etc. — causes Initialize to reject with NotSupportMint. The exception is a small hard-coded mint whitelist in the program (a handful of specific pubkeys) that bypasses the extension check; it is used to onboard specific mints case-by-case.
The vetted-extension list and the mint whitelist live in the CP-Swap source under programs/cp-swap/src/utils/token.rs and can change with future program upgrades.
Observation
The observation account is a ring buffer ofObservationState entries, each storing a block_timestamp and a cumulative price. On every swap the program appends a new observation if enough time has passed since the last one. TWAPs are computed by reading two observations and dividing Δcumulative / Δtime.
ObservationState PDA is around 4,100 bytes after the surrounding fields and discriminator.
Two consumer rules:
- Do not use a single observation as a price. It is a cumulative, not a spot price. Use two of them to compute a TWAP.
- Pick observations at least one block apart. Swaps within the same block may not produce a new observation; reading back-to-back can return the same record.
products/clmm/accounts.
Account lifecycle
| Event | Accounts created | Accounts destroyed |
|---|---|---|
Initialize | poolState, lpMint, vault0, vault1, observation | — |
Deposit | — (may create user LP ATA) | — |
Withdraw | — | — |
Swap | — (may create user destination ATA) | — |
CollectProtocolFee | — | — |
CollectFundFee | — | — |
UpdatePoolStatus | — | — |
poolState remains. This is deliberate: re-seeding the same pool later preserves its historical observation buffer and its PDA derivation remains stable.
What to read where
- Instruction account lists (which of the above are writable/signer for each instruction):
products/cpmm/instructions. - Fee-accrual semantics:
products/cpmm/fees. - Swap math / observation update rule:
products/cpmm/math. - Canonical seeds / program IDs:
reference/program-addresses.


