Skip to main content
Esta página foi traduzida automaticamente por IA. A versão em inglês é a fonte oficial.Ver versão em inglês →
CPI (“cross-program invocation”) é o mecanismo pelo qual um programa Solana chama outro. A maioria dos programas Raydium fornece crates wrapper CPI Anchor que fazem o local da chamada parecer uma chamada de função tipada, com structs de conta que têm nomes de campo validados e helpers cpi::<ix>(). Esta página documenta o padrão geral uma vez, depois as diferenças por programa. Para TypeScript executável, veja a página code-demos de cada capítulo de produto.

Qual padrão se aplica a qual programa

Se você está integrando CPMM, CLMM ou LaunchLab, leia o padrão geral primeiro, depois pule para a seção do seu programa para a lista de contas e quaisquer diferenças. Farm v6 e AMM v4 são diferentes o suficiente para justificar a leitura de suas seções de forma independente.

Dependências Cargo

A chave de dependência deve corresponder exatamente ao [package] name do repo de destino, hífens inclusos. Cargo não trata raydium_cp_swap como equivalente a raydium-cp-swap ao resolver uma dependência git.
branch = "master" rastreia a fonte publicada mais recente; fixe em um rev = "<commit>" específico se você precisar de uma compilação reproduzível. Isso é recomendado uma vez que você passar do prototipagem, já que uma mudança de layout de conta upstream em master quebrará sua compilação sem aviso. O sinalizador de feature cpi faz os crates compilarem apenas para a superfície CPI (structs de conta + invocadores) em vez do programa completo, então seu binário permanece pequeno. anchor-lang / anchor-spl devem corresponder ao que o crate de destino fixa, e a partir de 2026-09 os dois crates públicos Raydium não concordam:
Você não pode depender de ambos os crates de um programa agora. Cada um fixa Anchor com =, então Cargo teria que vincular duas cópias incompatíveis dos traits do Anchor em um binário, e a compilação falha. Se seu programa faz CPI em CPMM e CLMM, você tem que dividir em dois programas, ou descartar o crate CPI tipado para um deles e codificar manualmente essa instrução (o padrão mostrado para AMM v4 funciona para qualquer programa). Verifique novamente ambos os arquivos Cargo.toml antes de começar — espera-se que isso seja resolvido quando CLMM se mover para Anchor 1.x.
Anchor 1.0 mudou duas coisas que cada local de chamada CPI toca. Se você está movendo uma integração funcionando de 0.3x:
  • CpiContext::new recebe um Pubkey, não um AccountInfo. CpiContext::new(ctx.accounts.cpmm_program.to_account_info(), accts) se torna CpiContext::new(*ctx.accounts.cpmm_program.key, accts). O mesmo para new_with_signer. O campo struct agora é program_id: Pubkey.
  • Context tem uma lifetime, não quatro. Context<'_, '_, 'info, 'info, MyProxySwap<'info>> se torna Context<'info, MyProxySwap<'info>>.
No lado do cliente, anchor-client’s RequestBuilder::instructions() agora retorna Vec<Instruction> em vez de Result<Vec<Instruction>> (remova o ?), e CommitmentConfig saiu de solana-sdk — pegue de anchor_client em vez disso. spl-associated-token-account 8.0 re-exporta seus helpers do novo crate spl-associated-token-account-interface. get_associated_token_address e ID ainda são alcançáveis na raiz do crate (spl_associated_token_account::{get_associated_token_address, ID}), mas os helpers de endereço estão deprecados lá — prefira depender de spl-associated-token-account-interface diretamente e importar spl_associated_token_account_interface::address::get_associated_token_address e spl_associated_token_account_interface::program::ID. Note ::address e ::program são módulos do crate interface; spl_associated_token_account::address::… não resolve.
Para exemplos CPI funcionais que conectam os structs de conta de ponta a ponta, veja raydium-io/raydium-cpi-example (cobre AMM v4, CPMM e CLMM). Seu branch mais recente é anchor-0.31.0 — não há branch Anchor 1.x ainda, então trate esse repo como a referência para fiação de struct de conta, não para os pins de versão que esta página exige.

O padrão geral de CPI Anchor

Esta seção percorre CPMM de ponta a ponta como o exemplo prático: struct Accounts, CpiContext, cpi::<ix>(). CLMM segue a forma idêntica, com uma lista de contas diferente e um requisito de contas restantes. LaunchLab segue a mesma mecânica, mas sua lista de contas carrega várias contas sem equivalente CPMM/CLMM (global_config, platform_config, event_authority, program), então trate como o mesmo padrão, não a mesma forma. Veja a seção própria de cada programa em vez de assumir que a lista de contas deste passo a passo se transfere diretamente.

Construção de lista de contas

Todo CPI Raydium requer um struct Accounts no programa chamador. Seus campos são quaisquer contas que sua instrução precisa, com validadores no nível do campo; sua ordem de declaração não precisa corresponder à ordem de contas da instrução própria de Raydium, já que seu próprio cliente gerado por IDL as endereça por nome, não por posição:
A maioria das contas do lado Raydium são UncheckedAccount porque o chamado (Raydium) possui a validação. Seu programa chamador apenas valida estritamente contas que você possui, como ATAs de usuário e seus próprios PDAs. O comentário de doc /// CHECK: suprime o aviso do Anchor sobre verificações ausentes. A exceção do lado Raydium é o próprio cpmm_program: é o programa sendo invocado em vez de uma conta de dados que Raydium valida internamente, então é tipado Program<T> e recebe a verificação de endereço automática do Anchor em vez de um /// CHECK: manual. Esta forma principalmente UncheckedAccount, onde Raydium valida suas próprias contas, é a mesma para CLMM e LaunchLab. Este exemplo assume que ambos os mints são Token SPL clássico; se qualquer lado puder ser um mint Token-2022, adicione um campo token_program_2022: Program<'info, anchor_spl::token_2022::Token2022> e passe como input_token_program/output_token_program desse lado na chamada CPI abaixo em vez de token_program.

Construindo a chamada CPI

Anchor gera um helper por instrução, junto com um struct de contas CPI (cpi::accounts::Swap, aliasado CpmmSwap abaixo). Diferentemente do seu próprio struct MyProxySwap acima, os nomes e ordem dos campos deste são fixos pelo próprio IDL de raydium-cp-swap e têm que corresponder exatamente:
cpi::swap_base_input é gerado do IDL; sua lista de argumentos espelha a lista de argumentos da instrução Anchor. Todo programa Raydium baseado em Anchor confirmado (CPMM, CLMM, LaunchLab) gera seus helpers cpi::<ix>() da mesma forma, com o nome da função correspondendo ao nome da instrução em snake_case. Se isso se estende a Farm v6 é não confirmado; veja sua seção.

Seeds de assinante (CPI assinado por PDA)

Quando seu programa assina o CPI em nome de um PDA (comum para cofres, depósitos, etc.), use CpiContext::new_with_signer:
As seeds de assinante devem corresponder à derivação do PDA. Para qualquer conta passada como authority (ou papel de assinante similar), o runtime Solana verifica que o PDA assina via essas seeds.

Contas restantes

Algumas instruções Raydium recebem contas restantes, uma lista de comprimento variável anexada após as contas fixas. Os helpers CPI do Anchor não verificam de tipo contas restantes; passe via .with_remaining_accounts(...):
A ordem sempre importa, já que o programa receptor itera contas restantes na ordem que você as passa. Duas ordenações confirmadas:
  • CLMM SwapV2: arrays de tick, ordenados direcionalmente.
  • Farm v6: pares (reward_vault, user_reward_ata), mas apenas do segundo fluxo de recompensa em diante; veja Farm v6 para o que decodificar uma transação real mostra.

Aplicando o padrão: CLMM

SwapV2 segue o padrão geral acima com uma lista de contas diferente e um requisito de contas restantes para arrays de tick. O módulo #[program] do crate é nomeado raydium_clmm, que também é seu caminho Rust use.
O struct de contas CPI é nomeado SwapSingleV2, não SwapV2. SwapV2 é o nome da instrução on-chain.
Calcule a lista de arrays de tick da mesma forma que o SDK faz, via uma cotação contra o estado atual do pool, em vez de adivinhar uma contagem fixa; um swap que ultrapassa os arrays que você passou reverte com TickArrayNotFound (veja products/clmm/instructions para a tabela de contas completa e lista de erros). Passe-os na direção do percurso de preço: primeiro array na direção do swap primeiro.

Aplicando o padrão: LaunchLab

LaunchLab é baseado em Anchor e IDL-publicado: raydium_launchpad/raydium_launchpad.json no repo público raydium-idl. O identificador de metadados internos desse IDL é raydium_launchpad, um nome técnico para o programa subjacente, não um nome alternativo para o produto. Diferentemente de CPMM e CLMM, porém, a própria fonte do programa não está disponível publicamente (veja reference/program-addresses). Não há dependência git = "..." para apontar Cargo, e nenhuma fonte para confirmar qual seria o caminho Rust use de um crate real. Gere bindings do IDL publicado usando a macro declare_program! do Anchor. Salve o JSON do IDL como idls/raydium_launchpad.json em seu crate (Cargo procura um diretório idls/ relativo a CARGO_MANIFEST_DIR), depois declare_program!(raydium_launchpad); gera structs raydium_launchpad::cpi::accounts::<Ix> e funções cpi::<ix>() direto do IDL, nenhuma fonte de programa necessária. O nome do struct de contas gerado é sempre o nome da instrução em PascalCase (buy_exact_in → BuyExactIn), e os nomes dos campos correspondem aos nomes de contas do IDL exatamente, a mesma lista de contas já usada em MyProxyBuy abaixo. A forma CPI segue o padrão geral. A lista de contas e argumentos abaixo vêm da instrução buy_exact_in do IDL on-chain, não de products/launchlab/instructions.mdx:
Pós-graduação, o programa de destino é CPMM ou AMM v4 dependendo de pool_state.migrate_type, que products/launchlab/accounts.mdx diz ser definido no tempo de Initialize. Sua lista de contas CPI tem que estar preparada para qualquer um, ou você precisa ler migrate_type de PoolState primeiro e ramificar.

Propagação de erro

Cada programa Raydium baseado em Anchor retorna seu próprio enum de erro; Anchor os envolve, então seu programa chamador os vê como Err(ProgramError::Custom(code)). Para lidar com erros específicos:
Troque o tipo de erro relevante para o programa que você está chamando (raydium_clmm::error::ErrorCode para CLMM, e assim por diante). Os números de código de erro são estáveis por política de IDL (sdk-api/anchor-idl), então você pode testar contra códigos específicos comparando contra o valor numérico. Tabelas de erro completas: CPMM, CLMM, AMM v4, Farm v6 e LaunchLab.

Orçamento de computação em CPIs compostos

Cada frame CPI tem overhead, e o próprio consumo de CU do chamado se acumula no topo do seu, então uma transação que chama Raydium de dentro do seu programa precisa de um orçamento de computação explícito em vez de confiar no padrão de 200k CU.
Medido, não estimado. Um swap_base_input de CPMM na mainnet consome ~23.000 CU no próprio programa CPMM — amostrado em 2026-09-09 em oito swaps ao vivo em um pool de alto volume (22.721–23.052), lido da linha de log Program CPMMoo8… consumed N of M compute units. Para comparação: swap AMM v4 ~26.000; CLMM swap ~41.000; CLMM swap_v2 ~48.000 (43.838–52.887), aumentando com cada cruzamento de tick.Uma revisão anterior desta página relatava ~47.700 CU para um CPI de proxy-swap. Essa figura era a transação inteira (computeUnitsConsumed), que inclui seu próprio programa, o frame CPI e qualquer configuração de ATA — não o custo do chamado. Ambos são úteis, mas não são o mesmo número, então compare como com como. Meça sua própria transação em vez de orçar fora de qualquer um.
CPIs de CLMM e LaunchLab custam mais (CLMM em particular percorre arrays de tick adicionais via remaining_accounts, adicionando CU por array), mas apenas a figura de CPMM acima é um valor medido. Sempre defina um limite explícito ComputeBudgetProgram::set_compute_unit_limit(...) dimensionado a partir de sua própria medição, não um número copiado da documentação, já que o limite padrão de 200k CU se esgotará silenciosamente e os custos por instrução mudam conforme os programas são atualizados.

AMM v4: construção manual de instrução

AMM v4 é anterior ao Anchor e não tem crate CPI, tornando-o o único programa neste doc que não segue o padrão geral acima. Construa a Instruction manualmente:
Veja products/amm-v4/code-demos para a lista de contas completa.

Farm v6

Use o SDK TS se essa for uma opção para sua integração. raydium.farm.deposit(...) (veja products/farm-staking/code-demos) é exercido por demos reais e não depende de se um crate Anchor existe para este programa.
Farm v6 não oferece caminho CPI Anchor. Não há crate raydium_farm_v6 em crates.io, nenhum repositório de fonte pública, e nenhum IDL on-chain — o programa não tem nem uma conta anchor:idl legada nem uma entrada no programa Program Metadata (veja sdk-api/anchor-idl). Trate como um programa não-Anchor e construa suas instruções manualmente, como abaixo.
Se você precisar de Rust CPI mesmo assim, por exemplo compondo de outro programa on-chain, construa a Instruction manualmente, da mesma forma que AMM v4: derive a lista de contas real e discriminadores de instrução independentemente, por exemplo decodificando os layouts TypeScript do SDK (raydium-sdk-V2’s farm module), decodificando transações reais diretamente (veja abaixo), ou despejando e desassemblando o programa implantado. Para a forma de instrução de argumento zero consistente com uma chamada de harvest ou claim, a ordem de contas real é um prefixo fixo (token_program, a conta de estado do farm, um PDA de autoridade de cofre, o primeiro cofre de recompensa desse PDA, um segundo PDA, o chamador, e o ATA do chamador para esse primeiro mint de recompensa), seguido por pares (reward_vault_i, user_reward_ata_i) em remaining_accounts para cada fluxo de recompensa após o primeiro. A convenção de emparelhamento é real, mas apenas começa no segundo fluxo de recompensa: o cofre e ATA do primeiro fluxo são contas fixas, não adjacentes um ao outro, e não fazem parte de remaining_accounts em tudo.

Testando um fluxo CPI

Dev local requer que os programas Raydium estejam disponíveis em seu validador de teste. Três opções:
  1. anchor test com clonagem de programa. Puxa bytecode implantado na mainnet para seu validador local; veja Clonando programas em um validador local abaixo para a configuração Anchor.toml e duas coisas que enganam testes de criação de pool especificamente.
  2. Devnet. Raydium implanta a maioria dos programas em devnet, mas em IDs de programa diferentes da mainnet para cada programa (CPMM, CLMM, AMM v4, Stable AMM e LaunchLab cada um têm um endereço devnet distinto; veja a tabela Devnet em reference/program-addresses). Farm v3/v5/v6 não são confiávelmente publicados em devnet; a API ao vivo (https://api-v3-devnet.raydium.io/main/info) tem o quadro atual. Se você usar constantes DEVNET_PROGRAM_ID agrupadas de raydium_clmm (ou o equivalente para outros crates), não assuma que um ID de mainnet também funciona em devnet. Execute anchor test --provider.cluster devnet para atingir código ao vivo uma vez que você tenha os endereços corretos.
  3. Deploy local. Clone os repos Raydium (CPMM, CLMM; a fonte de LaunchLab não está disponível para esta opção) e anchor deploy para um validador local. Adiciona overhead de ciclo de teste, mas permite modificar o chamado para depuração.
Execute com anchor test, ou anchor build primeiro e anchor test --skip-build depois se você estiver iterando no arquivo de teste sem alterar o programa.

Clonando programas em um validador local

Isso funciona por ID de programa independentemente de se a fonte do programa é pública, então LaunchLab clona da mesma forma que CPMM e CLMM mesmo que sua fonte não esteja disponível. reference/program-addresses é a fonte de verdade para cada endereço aqui.
Clonar o programa não é suficiente se seu teste também cria um pool (em vez de fazer swap contra um que já existe). A instrução initialize de CPMM valida suas contas amm_config e create_pool_fee contra dados reais on-chain, então você precisa clonar essas também, ou initialize falha completamente. Para CPMM especificamente: clone o AmmConfig de tier de taxa que você quer (busque seu endereço de GET https://api-v3.raydium.io/main/cpmm-config, índice 0 é o tier de 0,25%) e a conta de token receptor de taxa, validada por endereço exato, não criada na hora, então tem que já existir.
Um pool que seu teste acabou de criar não é permutável no mesmo instante. O initialize de CPMM silenciosamente sobrescreve um open_time solicitado que não está estritamente no futuro (if open_time <= block_timestamp { open_time = block_timestamp + 1 }), então mesmo startTime: 0 (“abrir imediatamente,” por o SDK) deixa um intervalo real ≥1-segundo antes do pool aceitar swaps. Um teste que cria um pool e faz swap contra ele com zero atraso vai atingir NotApproved. Um curto await (1–2s) entre criação de pool e o primeiro swap é suficiente. Isso é específico de teste; um humano executando dois comandos manuais separados normalmente não notaria, já que digitação e inicialização de processo já comem mais de um segundo.

Ponteiros

Fontes: