sdk-api/rust-cpi covers the low-level mechanics of invoking each Raydium program. This page is the higher-level companion: why you would compose Raydium into your own program, which pattern fits your use case, and the full glue you need end-to-end.When CPI is the right tool
A custom program makes sense when the trade needs to happen atomically with other on-chain state changes that only your program can make. Common cases:- Escrow / limit-order programs — user deposits a mint into your escrow, your program watches for a price condition, and when it triggers, your program atomically swaps through Raydium and credits the user’s account.
- Aggregator proxies — a single instruction that routes a swap through Raydium + one or more other DEXes, with all hops under a single slippage check owned by your program.
- Auto-compounding vaults — deposit LP or farm stake into your vault, vault harvests rewards on a schedule, re-supplies liquidity, issues share tokens.
- Strategy vaults — leveraged LP positions that rebalance by swapping through CLMM; liquidators that close positions and swap collateral in one transaction.
- Token-launch platforms with custom vesting — your program holds vesting tokens and releases into a Raydium pool on a schedule.
Composition patterns
Pattern 1: Thin proxy
Your program exposes a single instruction that validates some policy (e.g. whitelisted mint pairs, fee discount for verified users) and then forwards to Raydium.Pattern 2: Escrow
Your program owns a PDA that holds the user’s input mint. On trigger, the PDA signs a CPI to Raydium to swap its own balance.CpiContext::new_with_signer. See Signer seeds.
Pattern 3: Composed multi-hop
Your program issues multiple CPIs in one instruction, enforcing a single slippage bound across all of them. The Raydium swap instructions each have their ownminimum_amount_out, but you set those to 0 (or a very loose floor) and enforce a strict final minimum yourself after the last hop.
Pattern 4: Vault / strategy
Your program holds LP tokens or farm stake in a PDA. A keeper (or the user) callscompound(), which:
- Harvests rewards from the farm.
- Swaps rewards for pool tokens (CPI into CPMM or CLMM).
- Deposits the proceeds back into the LP (another CPI).
- Stakes the new LP (another CPI).
Account list construction
The calling program’sAccounts struct mirrors the Raydium program’s account order, but most Raydium-side accounts are UncheckedAccount because Raydium validates them itself. You only add constraints on accounts you own:
UncheckedAccount on Raydium’s — is not laziness. The receiver validates its own; double-validating at the caller just burns CU and risks going out of sync when Raydium ships a new struct layout field.
The CPI call itself
PDA signer seeds
The CPI succeeds only if the PDA passed asauthority matches the derivation the caller claims. The two must agree on:
- The seed byte sequence (here
[b"escrow", user.key().as_ref()]). - The bump.
- The calling program ID (your program, not Raydium’s).
authority signature covers the transaction and that the input ATA is owned by that authority. The validation happens in anchor_spl::token::transfer: the ATA’s authority field must equal the signer.
Common bug: passing user as the authority (and transferring from escrow_input_ata that is owned by the escrow PDA). The SPL Token program rejects with owner mismatch. Always make the authority field match the ATA owner.
Remaining accounts
Several Raydium instructions take a variable-length list of accounts appended after the fixed ones — remaining accounts.- CLMM
SwapV2: 1–8TickArrayStateaccounts for the tick arrays the swap may traverse, in swap direction. - Farm v6
Deposit/Harvest/Withdraw:(reward_vault, user_reward_ata)pairs, one pair per live reward slot. - Token-2022 transfer-hook mints: the transfer-hook program plus any accounts the hook needs.
Compute budget for composed calls
A CPI costs ~1,500 CU for the call frame itself; the callee’s own CU use stacks on top. Rough budget per Raydium CPI:| Call | CU (SPL Token) | CU (Token-2022) |
|---|---|---|
| CPMM swap_base_input | ~150,000 | ~200,000 |
| CLMM swap_v2 (single tick array) | ~180,000 | ~230,000 |
| CLMM swap_v2 (crosses 2 ticks) | ~220,000 | ~270,000 |
| Farm v6 deposit | ~120,000 | ~150,000 |
| Farm v6 harvest (per reward slot) | +30,000 | +40,000 |
| AMM v4 swap_base_in | ~140,000 | n/a |
harvest → swap A → swap B → deposit LP → stake LP easily hits 700k CU.
Always set an explicit ComputeBudgetProgram::set_compute_unit_limit:
Error propagation
Raydium’s programs return Anchor errors with stable error codes. Your calling program sees them asErr(ProgramError::Custom(code)). Bubble through by default:
sdk-api/anchor-idl); new codes append at the end, existing codes never change meaning.
Full worked example: limit-order escrow
Flow:open_order— user depositsamount_inofinput_mintinto escrow PDA; record targetmin_amount_outand expiry.execute_order— anyone (keeper) calls with the current pool accounts. Program checks the current quote ≥min_amount_out, then CPIs Raydium swap and keeps the output in escrow.claim— user withdraws the output mint from escrow.
Testing
Pulling Raydium programs into a local validator for integration tests (fromAnchor.toml):
anchor test fetches them from mainnet at startup. See sdk-api/rust-cpi.
Pitfalls specific to composition
Reentrancy
Solana has no true reentrancy — a CPI can’t call back into the originating program in the same invocation. But you can still build yourself into a logical reentrancy: a CPI that reads your state, then your code reads it again assuming the CPI didn’t change it. For Raydium, the CPIs don’t touch your state, so this is less a concern than e.g. flash-loan contexts. But if you compose Raydium with a lending protocol, be aware.Account mutability drift
If your program passes an account asmut but Raydium expects it read-only (or vice versa), the runtime rejects the invocation with InvalidAccountData. Always check Raydium’s instruction’s expected mutability in the IDL; anchor_cp_swap::cpi::accounts::Swap enforces it via its field types.
Token-2022 program field
Input and output mints may be under different token programs — one SPL Token, one Token-2022. The CPI has separateinput_token_program and output_token_program fields for this reason. Always check each mint’s owner field and route the correct program into each slot.
Versioned transactions
A composed tx that does 2+ Raydium CPIs plus an ATA creation rarely fits in a legacy (v0-without-LUT) transaction. Use V0 with address lookup tables; pull Raydium’s public LUTs viaraydium.getRaydiumLutAddresses().
Pointers
sdk-api/rust-cpi— low-level CPI mechanics.integration-guides/priority-fee-tuning— sizing compute budget.products/cpmm/code-demos,products/clmm/code-demos,products/farm-staking/code-demos— per-product CPI snippets.

