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.
PDAs (program-derived addresses) and CPIs (cross-program invocation) are the two primitives that make Raydium possible. PDAs let a program “own” deterministic addresses without private keys — that’s how pool authorities and vaults work. CPIs let one program call another — that’s how Raydium swaps tokens via the SPL Token program and how integrators compose Raydium into their own flows. Both are worth understanding before reading Raydium’s source.
PDAs: addresses without keys
A Program-Derived Address is a public key that:- Is not on the ed25519 curve (no private key exists for it).
- Is derived deterministically from a program ID and a set of seeds.
- Can be signed for by only the derivation program, via
invoke_signed.
Derivation
A PDA is computed by hashing the program ID with the seeds, then finding a “bump” byte that forces the result off-curve. The first bump (typically starting from 255 and decrementing) that produces an off-curve address wins; this is the canonical bump.u64 values as little-endian bytes. Raydium’s convention is a human-readable prefix followed by unique identifiers.
Raydium PDA patterns
Common PDAs in Raydium’s programs:| PDA | Seeds | Program |
|---|---|---|
| AMM authority (AMM v4) | [b"amm authority"] + bump | AMM v4 |
| Pool state (CPMM) | [b"pool", amm_config, mint_a, mint_b] | CPMM |
| Pool vault (CPMM) | [b"pool_vault", pool, mint] | CPMM |
| Authority (CPMM) | [b"vault_and_lp_mint_auth_seed"] | CPMM |
| Pool state (CLMM) | [b"pool", amm_config, mint_0, mint_1] | CLMM |
| Tick array (CLMM) | [b"tick_array", pool, start_tick_index] | CLMM |
| Observation (CLMM) | [b"observation", pool] | CLMM |
| Personal position (CLMM) | [b"position", position_nft_mint] | CLMM |
| Farm state (Farm v6) | [b"pool_farm_state", farm_id] | Farm v6 |
| User ledger (Farm v6) | [b"user_ledger", farm, user] | Farm v6 |
Canonical bump
Although there can in principle be multiple bumps producing off-curve addresses, Raydium’s programs always use the canonical bump (found by decrementing from 255). This is stored in the PDA’s account data so subsequent transactions can pass it in and skip the (expensive) derivation loop:CPIs: calling other programs
Cross-Program Invocation lets a program invoke another program’s instructions inline within a single transaction. Raydium uses CPIs extensively:- Swap instructions call SPL Token program to move tokens.
- CLMM calls Metaplex to mint the position NFT.
- Pool creation calls System Program to allocate accounts.
- Farm v6 calls SPL Token to transfer rewards.
integration-guides/cpi-integration.
invoke vs invoke_signed
The Solana runtime offers two CPI primitives:invoke: call another program; the called program inherits the outer transaction’s signers.invoke_signed: call another program on behalf of a PDA; the runtime verifies the PDA’s seeds and authorizes the signature.
invoke_signed is the magic that lets programs hold authority over accounts without managing private keys.
Example: Raydium transferring from a pool vault
A pool vault is a Token Account whose authority is a PDA of the pool program. To transfer tokens out during a swap, the pool program must sign as that PDA:invoke_signed is called by the CPMM program, verifies that vault_and_lp_mint_auth_seed + bump derives to pool_authority’s address when hashed with the CPMM program ID, and permits the authority signature on the token transfer. No private key involved.
Example: integrator calling Raydium CPMM
An integrator program (e.g., an escrow) can invoke Raydium’sswap_base_input via CPI:
integration-guides/cpi-integration for the full escrow example.
CPI depth limit
Solana caps CPI depth at 4 levels. A transaction’s top-level instruction counts as depth 0; each CPI invocation increments depth. Practical implication: Raydium’s own swap already uses 1-2 levels of CPI (Raydium → SPL Token). An integrator calling Raydium uses 2. If that integrator is called by another integrator, it’s 3. The 4th level is the limit. Most compositions stay under this easily, but deep nesting (aggregator → router → Raydium → hook) can hit it. Design flat rather than deep.Remaining accounts
When a Raydium instruction needs a variable number of accounts (e.g., CLMM swap crossing an unknown number of tick arrays), the extra accounts are passed as remaining accounts — appended to the fixed-account list, interpreted by position. CPMM’sSwapV2 uses remaining accounts for transfer-hook programs’ extra required accounts. Clients fetch the needed accounts and append them:
PDA pitfalls
Wrong seeds → wrong address
A bug where seeds are in the wrong order, wrong encoding, or include/exclude an extra byte silently produces a different PDA. The transaction fails ambiguously (the program tries to read an account that doesn’t exist). Always unit-test seed derivation against known golden values.Not storing bump
If you re-derive the bump on every transaction, you pay compute for the derivation loop. Store the canonical bump in the PDA’s data and read it from there.Confusing canonical vs non-canonical bump
Non-canonical bumps (if anyone finds one that yields off-curve) are allowed byinvoke_signed but rejected by Raydium’s programs via assert_eq!(bump, canonical_bump). If someone tries to claim a PDA with a non-canonical bump, the tx fails.
Passing a PDA as signer when you’re not the owning program
Only the program whose ID is in the PDA’s derivation caninvoke_signed with its seeds. If you try, the runtime rejects.
CPI pitfalls
Forgetting to forward remaining_accounts
If your outer instruction passes transfer-hook accounts in remaining_accounts but the CPI into Raydium doesn’t forward them, Raydium fails because it can’t find the hook accounts. Always include with_remaining_accounts in CPIs that need them.
Writable flags mismatch
An account that the outer instruction marks writable must also be writable in the CPI call if the called program intends to write it. Mismatch → runtime rejection.Not accounting for rent
CPI to a program that creates an account (e.g., ATA creation) requires the payer to have enough SOL for rent. Failed rent checks appear as obscure errors.Worked example: computing Raydium CPMM PDAs
getPoolInfoFromRpc({ poolId }) — it derives the associated PDAs without a round-trip.
Pointers
solana-fundamentals/account-model— how PDAs fit in the account model.solana-fundamentals/programs-and-anchor— Anchor’s helpers for declaring PDAs.integration-guides/cpi-integration— building integrations that CPI into Raydium.sdk-api/rust-cpi— Raydium’s Rust CPI types.


