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.
And the shared config:
And one optional per-creator account:
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 LP total. It is not equal to the LP mint’s supply: it is exactly 100 base units higher, because the 100 locked units are counted here but never minted. All LP-share math (deposit, withdraw) divides bylp_supply, so use this field and do not substitute the mint’s on-chain supply.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.protocol_fees_token{0,1}also receives the protocol’s share of the creator fee when a creator fee is collected, so it grows outside of swaps — seeproducts/cpmm/fees.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 byCollectCreatorFeeorCollectCreatorFeePermissionless. Both paths zero the full counters, but since the 2026-09-19 creator-fee-share upgrade only part of the balance leaves the pool: the protocol’s share is added toprotocol_fees_token_{0,1}and the remainder is transferred to the creator. The permissionless path fixes the recipients topool_creator’s canonical ATAs.PoolStateitself did not change — there is no separate counter for the shared amount.
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 or the destination-constrained CollectCreatorFeePermissionless, and both paths hand a share of the accrued balance to the protocol on the way out — at creator_fee_share_rate, or at the rate on a CreatorFeeShare PDA when one exists for that (creator, amm_config) pair. 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 by either the CPMM admin or a dedicated permission-PDA creator authority. Since the 2026-09 upgrade, ClosePermissionPda accepts the same two signers; before it was admin-only. End users do not interact with this account directly — it is plumbing for cross-program flows. See security/admin-and-multisig for the role boundary and reference/program-addresses for canonical addresses.
CreatorFeeShare
An optional account that overrides the protocol’s share of the creator fee for one (pool creator, AmmConfig) pair. Added by the 2026-09-19 creator-fee-share upgrade.
- It is optional, but the account is never optional in the instruction.
CollectCreatorFeeandCollectCreatorFeePermissionlessboth declarecreator_fee_sharewith the seed constraint above and take it on every call. The program then checks whether the account is empty or foreign-owned; if so it falls back toAmmConfig.creator_fee_share_rate. So a client must always derive and pass the address, whether or not the account exists. share_rateis capped atFEE_RATE_DENOMINATOR_VALUE(1_000_000) at creation, and again when the split runs.1_000_000routes the entire creator fee to the protocol;0routes none of it.- Created and closed by the admin or a dedicated authority through
CreateCreatorFeeShare/CloseCreatorFeeShare. Closing it returns the rent to the signer and falls the pair back to the config default; the pool creator is not a signer on either path. - It is keyed on the creator, not the pool. One account governs every pool that creator has on that
AmmConfig. A creator with pools on two fee tiers needs two accounts to be covered on both.
products/cpmm/fees.
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 one exception is a per-mint registry: if a SupportMintAssociated PDA exists at seed [b"support_mint", mint], the mint is admitted regardless of its extension set. That PDA is created and removed by the admin (or a dedicated support-mint authority) through CreateSupportMintAssociated / CloseSupportMintAssociated, so onboarding a specific mint no longer needs a program upgrade.
Changed in 2026-09. CPMM previously also carried a hardcoded four-address
MINT_WHITELIST that short-circuited the extension check. That array was removed; the registry PDA is now the only bypass. Any mint that was relying on the hardcoded list needs a SupportMintAssociated PDA before a new pool can be created for it — existing pools are unaffected, because the check runs only at pool creation.programs/cp-swap/src/utils/token.rs and can change with future program upgrades. See reference/token-2022-support for the cross-program matrix.
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.
8 + 16 + 16), so the array alone is 4,000 bytes; ObservationState::LEN is exactly 4,075 bytes (8 + 1 + 2 + 32 + 4,000 + 8 × 4).
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
CPMM pools and their PDAs are never closed.
Permission, SupportMintAssociated and CreatorFeeShare are the exceptions — they are standalone admin-managed records, not pool state, and each has an explicit close instruction. Even at zero liquidity the 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.

