Skip to main content
CPI (“cross-program invocation”) is the mechanism by which one Solana program calls another. Most of Raydium’s programs ship Anchor CPI wrapper crates that make the call site look like a typed function call, with account structs that have validated field names and cpi::<ix>() helpers. This page documents the general pattern once, then the per-program differences. For runnable TypeScript, see the code-demos page of each product chapter.

Which pattern applies to which program

If you’re integrating CPMM, CLMM, or LaunchLab, read the general pattern first, then jump to your program’s section for the account list and any differences. Farm v6 and AMM v4 are different enough to warrant reading their sections standalone.

Cargo dependencies

The dependency key must match the target repo’s [package] name exactly, hyphens included. Cargo does not treat raydium_cp_swap as equivalent to raydium-cp-swap when resolving a git dependency.
branch = "master" tracks the latest published source; pin to a specific rev = "<commit>" if you need a reproducible build. This is recommended once you’re past prototyping, since an upstream account-layout change on master will break your build with no warning otherwise. The cpi feature flag makes the crates compile to just the CPI surface (account structs + invokers) rather than the full program, so your binary stays small. anchor-lang / anchor-spl must match what the target crate pins:
Take both crates from the same Anchor line. Both upgrade branches pin =1.0.2, so one program can CPI into CPMM and CLMM from a single crate. Mixing lines breaks the build: for example, raydium-cp-swap on chore/upgrade-anchor with raydium-clmm on master. Cargo would have to link two incompatible copies of Anchor’s traits into one binary. If you are stuck on a mixed pair, split the program in two, or drop the typed CPI crate for one side and hand-encode that instruction (the pattern shown for AMM v4 works for any program). Re-check both Cargo.toml files before starting, since the branches will move to master eventually.
Anchor 1.0 changed two things every CPI call site touches. If you are moving a working integration off 0.3x:
  • CpiContext::new takes a Pubkey, not an AccountInfo. CpiContext::new(ctx.accounts.cpmm_program.to_account_info(), accts) becomes CpiContext::new(*ctx.accounts.cpmm_program.key, accts). Same for new_with_signer. The struct field is now program_id: Pubkey.
  • Context has one lifetime, not four. Context<'_, '_, 'info, 'info, MyProxySwap<'info>> becomes Context<'info, MyProxySwap<'info>>.
On the client side, anchor-client’s RequestBuilder::instructions() now returns Vec<Instruction> rather than Result<Vec<Instruction>> (drop the ?), and CommitmentConfig moved out of solana-sdk — take it from anchor_client instead. spl-associated-token-account 8.0 re-exports its helpers from the new spl-associated-token-account-interface crate. get_associated_token_address and ID are still reachable at the crate root (spl_associated_token_account::{get_associated_token_address, ID}), but the address helpers are deprecated there — prefer depending on spl-associated-token-account-interface directly and importing spl_associated_token_account_interface::address::get_associated_token_address and spl_associated_token_account_interface::program::ID. Note ::address and ::program are modules of the interface crate; spl_associated_token_account::address::… does not resolve.
For working CPI examples that wire up the account structs end-to-end, see raydium-io/raydium-cpi-example (covers AMM v4, CPMM, and CLMM). Its newest branch is anchor-0.31.0 — there is no Anchor 1.x branch yet, so treat that repo as the reference for account-struct wiring, not for the version pins this page mandates.

The general Anchor CPI pattern

This section walks through CPMM end-to-end as the worked example: Accounts struct, CpiContext, cpi::<ix>(). CLMM follows the identical shape, with a different account list and a remaining-accounts requirement. LaunchLab follows the same mechanics but its account list carries several accounts with no CPMM/CLMM equivalent (global_config, platform_config, event_authority, program), so treat it as the same pattern, not the same shape. See each program’s own section rather than assuming this walkthrough’s account list transfers directly.

Account list construction

Every Raydium CPI requires an Accounts struct in the calling program. Its fields are whatever accounts your instruction needs, with field-level validators; their declaration order doesn’t have to match Raydium’s own instruction account order, since your own IDL-generated client addresses them by name, not position:
Most of the Raydium-side accounts are UncheckedAccount because the callee (Raydium) owns the validation. Your calling program only strictly validates accounts you own, such as user ATAs and your own PDAs. The /// CHECK: doc-comment suppresses Anchor’s warning about missing checks. The one Raydium-side exception is cpmm_program itself: it’s the program being invoked rather than a data account Raydium validates internally, so it’s typed Program<T> and gets Anchor’s automatic address check instead of a manual /// CHECK:. This mostly-UncheckedAccount shape, where Raydium validates its own accounts, is the same for CLMM and LaunchLab. This example assumes both mints are classic SPL Token; if either side can be a Token-2022 mint, add a token_program_2022: Program<'info, anchor_spl::token_2022::Token2022> field and pass it as that side’s input_token_program/output_token_program in the CPI call below instead of token_program.

Building the CPI call

Anchor generates one helper per instruction, together with a CPI-accounts struct (cpi::accounts::Swap, aliased CpmmSwap below). Unlike your own MyProxySwap struct above, this one’s field names and order are fixed by raydium-cp-swap’s own IDL and have to match exactly:
cpi::swap_base_input is generated from the IDL; its argument list mirrors the Anchor instruction’s argument list. Every confirmed Anchor-based Raydium program (CPMM, CLMM, LaunchLab) generates its cpi::<ix>() helpers the same way, with the function name matching the instruction name in snake_case. Whether this extends to Farm v6 is unconfirmed; see its section.

Signer seeds (PDA-signed CPI)

When your program signs the CPI on behalf of a PDA (common for vaults, escrows, etc.), use CpiContext::new_with_signer:
The signer seeds must match the PDA’s derivation. For any account passed as authority (or similar signer role), the Solana runtime checks that the PDA signs via these seeds.

Remaining accounts

Some Raydium instructions take remaining accounts, a variable-length list appended after the fixed accounts. Anchor’s CPI helpers do not type-check remaining accounts; pass them via .with_remaining_accounts(...):
Order always matters, since the receiver program iterates remaining accounts in the order you pass them. Two confirmed orderings:
  • CLMM SwapV2: tick arrays, ordered directionally.
  • Farm v6: (reward_vault, user_reward_ata) pairs, but only from the second reward stream onward; see Farm v6 for what decoding a real transaction shows.

Applying the pattern: CLMM

SwapV2 follows the general pattern above with a different account list and a remaining-accounts requirement for tick arrays. The crate’s #[program] module is named raydium_clmm, which is also its Rust use path.
The CPI accounts struct is named SwapSingleV2, not SwapV2. SwapV2 is the on-chain instruction name.
Compute the tick-array list the same way the SDK does, via a quote against current pool state, rather than guessing a fixed count; a swap that outruns the arrays you passed reverts with TickArrayNotFound (see products/clmm/instructions for the full account table and error list). Pass them in the direction of the price walk: first array in swap direction first.

Applying the pattern: LaunchLab

LaunchLab is Anchor-based and IDL-published: raydium_launchpad/raydium_launchpad.json in the public raydium-idl repo. That IDL’s internal metadata identifier is raydium_launchpad, a technical name for the underlying program, not an alternate name for the product. Unlike CPMM and CLMM, though, the program’s own source is not publicly available (see reference/program-addresses). There’s no git = "..." dependency to point Cargo at, and no source to confirm what a real crate’s Rust use-path would be. Generate bindings from the published IDL using Anchor’s declare_program! macro. Save the IDL JSON as idls/raydium_launchpad.json in your crate (Cargo looks for an idls/ directory relative to CARGO_MANIFEST_DIR), then declare_program!(raydium_launchpad); generates raydium_launchpad::cpi::accounts::<Ix> structs and cpi::<ix>() functions straight from the IDL, no program source required. The generated accounts-struct name is always the instruction name in PascalCase (buy_exact_in → BuyExactIn), and field names match the IDL’s account names exactly, the same account list already used in MyProxyBuy below. The CPI shape follows the general pattern. The account list and arguments below come from the on-chain IDL’s buy_exact_in instruction, not from products/launchlab/instructions.mdx:
Post-graduation, the target program is CPMM or AMM v4 depending on pool_state.migrate_type, which products/launchlab/accounts.mdx says is set at Initialize time. Your CPI account list has to be prepared for either, or you need to read migrate_type off PoolState first and branch.

Error propagation

Each Anchor-based Raydium program returns its own error enum; Anchor wraps them, so your calling program sees them as Err(ProgramError::Custom(code)). To handle specific errors:
Swap in the relevant error type for the program you’re calling (raydium_clmm::error::ErrorCode for CLMM, and so on). Error code numbers are stable per the IDL policy (sdk-api/anchor-idl), so you can test against specific codes by comparing against the numeric value. Full error tables: CPMM, CLMM, AMM v4, Farm v6, and LaunchLab.

Compute budget in composed CPIs

Each CPI frame has overhead, and the callee’s own CU consumption stacks on top of yours, so a transaction that calls into Raydium from inside your program needs an explicit compute budget rather than relying on the 200k CU default.
Measured, not estimated. A CPMM swap_base_input on mainnet consumes ~23,000 CU in the CPMM program itself — sampled 2026-09-09 across eight live swaps on a high-volume pool (22,721–23,052), read from the Program CPMMoo8… consumed N of M compute units log line. For comparison: AMM v4 swap ~26,000; CLMM swap ~41,000; CLMM swap_v2 ~48,000 (43,838–52,887), rising with each tick crossing.An earlier revision of this page reported ~47,700 CU for a proxy-swap CPI. That figure was the whole transaction (computeUnitsConsumed), which includes the caller’s own program, the CPI frame and any ATA setup — not the callee’s cost. Both are useful, but they are not the same number, so compare like with like. Measure your own transaction rather than budgeting off either.
CLMM and LaunchLab CPIs cost more (CLMM in particular walks additional tick arrays via remaining_accounts, adding CU per array), but only the CPMM figure above is a measured value. Always set an explicit ComputeBudgetProgram::set_compute_unit_limit(...) instruction sized from your own measurement, not a number copied from documentation, since the default 200k CU limit will silently exhaust and per-instruction costs shift as programs are upgraded.

AMM v4: manual Instruction construction

AMM v4 predates Anchor and has no CPI crate, making it the one program in this doc that doesn’t follow the general pattern above. Build the Instruction by hand:
See products/amm-v4/code-demos for the full account list.

Farm v6

Use the TS SDK if that’s an option for your integration. raydium.farm.deposit(...) (see products/farm-staking/code-demos) is exercised by real demos and doesn’t depend on whether a Rust Anchor crate exists for this program.
Farm v6 offers no Anchor CPI path. There is no raydium_farm_v6 crate on crates.io, no public source repository, and no on-chain IDL — the program has neither a legacy anchor:idl account nor an entry in the Program Metadata program (see sdk-api/anchor-idl). Treat it as a non-Anchor program and build its instructions by hand, as below.
If you need Rust CPI regardless, for example composing from another on-chain program, build the Instruction by hand, the same way as AMM v4: derive the real account list and instruction discriminators independently, for example by decoding the SDK’s TypeScript layouts (raydium-sdk-V2’s farm module), decoding real transactions directly (see below), or dumping and disassembling the deployed program. For the zero-argument instruction shape consistent with a harvest or claim call, the real account order is a fixed prefix (token_program, the farm’s state account, a vault-authority PDA, that PDA’s first reward vault, a second PDA, the caller, and the caller’s ATA for that first reward mint), followed by (reward_vault_i, user_reward_ata_i) pairs in remaining_accounts for every reward stream after the first. The pairing convention is real, but it only starts at the second reward stream: the first stream’s vault and ATA are fixed accounts, not adjacent to each other, and not part of remaining_accounts at all.

Testing a CPI flow

Local dev requires the Raydium programs to be available in your test validator. Three options:
  1. anchor test with program clone. Pulls deployed mainnet bytecode into your local validator; see Cloning programs into a local validator below for the Anchor.toml config and two things that trip up pool-creation tests specifically.
  2. Devnet. Raydium deploys most programs to devnet, but at different program IDs than mainnet for every program (CPMM, CLMM, AMM v4, Stable AMM, and LaunchLab each have a distinct devnet address; see the Devnet table in reference/program-addresses). Farm v3/v5/v6 aren’t reliably published on devnet; the live API (https://api-v3-devnet.raydium.io/main/info) has the current picture. If you use raydium_clmm’s bundled DEVNET_PROGRAM_ID constants (or the equivalent for other crates), don’t assume a mainnet ID also works on devnet. Run anchor test --provider.cluster devnet to hit live code once you have the right addresses.
  3. Local deploy. Clone the Raydium repos (CPMM, CLMM; LaunchLab’s source isn’t available for this option) and anchor deploy to a local validator. Adds test cycle overhead but lets you modify the callee for debugging.
Run with anchor test, or anchor build first and anchor test --skip-build after if you’re iterating on the test file without changing the program.

Cloning programs into a local validator

This works by program ID regardless of whether the program’s source is public, so LaunchLab clones the same way CPMM and CLMM do even though its source isn’t available. reference/program-addresses is the source of truth for every address here.
Cloning the program isn’t enough if your test also creates a pool (rather than swapping against one that already exists). CPMM’s initialize instruction validates its amm_config and create_pool_fee accounts against real on-chain data, so you need to clone those too, or initialize fails outright. For CPMM specifically: clone the fee-tier AmmConfig you want (fetch its address from GET https://api-v3.raydium.io/main/cpmm-config, index 0 is the 0.25% tier) and the fee-receiver token account, validated by exact address, not created on the fly, so it has to already exist.
A pool your test just created isn’t swappable in the same instant. CPMM’s initialize silently overrides a requested open_time that isn’t strictly in the future (if open_time <= block_timestamp { open_time = block_timestamp + 1 }), so even startTime: 0 (“open immediately,” per the SDK) leaves a real ≥1-second gap before the pool accepts swaps. A test that creates a pool and swaps against it with zero delay will hit NotApproved. A short await (1–2s) between pool creation and the first swap is enough. This is specific to testing; a human running two separate manual commands wouldn’t normally notice, since typing and process startup already eat more than a second.

Pointers

Sources: