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 da Raydium fornece crates de wrapper CPI Anchor que fazem o site de 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, consulte 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 do Cargo

A chave de dependência deve corresponder exatamente ao [package] name do repo de destino, hífens inclusos. O 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 um rev = "<commit>" específico se você precisar de uma compilação reproduzível. Isso é recomendado depois que você passar do protótipo, pois uma mudança de layout de conta upstream em master quebrará sua compilação sem aviso prévio. O sinalizador de feature cpi faz as crates compilarem apenas para a superfície CPI (structs de conta + invocadores) em vez do programa completo, para que seu binário permaneça pequeno. anchor-lang / anchor-spl devem corresponder ao que a crate de destino fixa:
Pegue ambas as crates da mesma linha do Anchor. Ambos os branches master fixam =1.0.2, então um programa pode fazer CPI em CPMM e CLMM de uma única crate. Misturar linhas quebra a compilação: por exemplo, raydium-cp-swap em master com raydium-clmm fixado em um rev anterior à sua atualização do Anchor 1.0. O Cargo teria que vincular duas cópias incompatíveis dos traits do Anchor em um binário. Se você estiver preso em um par misto, divida o programa em dois, ou abandone a crate CPI tipada para um lado e codifique manualmente essa instrução (o padrão mostrado para AMM v4 funciona para qualquer programa).
Anchor 1.0 mudou duas coisas que cada site de chamada CPI toca. Se você está movendo uma integração funcional 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, RequestBuilder::instructions() do anchor-client 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 da nova crate spl-associated-token-account-interface. get_associated_token_address e ID ainda são alcançáveis na raiz da 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 que ::address e ::program são módulos da crate interface; spl_associated_token_account::address::… não resolve.
Para exemplos CPI funcionais que conectam as structs de conta de ponta a ponta, consulte raydium-io/raydium-cpi-example (cobre AMM v4, CPMM e CLMM). Seu branch mais recente é anchor-0.31.0 — ainda não há branch Anchor 1.x, então trate esse repo como a referência para fiação de struct de conta, não para os pinos de versão que esta página exige.

O padrão geral de CPI do Anchor

Esta seção percorre o CPMM de ponta a ponta como exemplo prático: struct Accounts, CpiContext, cpi::<ix>(). O CLMM segue a mesma forma, com uma lista de contas diferente e um requisito de remaining-accounts. O LaunchLab segue a mesma mecânica, mas sua lista de contas inclui várias contas sem equivalente no CPMM/CLMM (global_config, platform_config, event_authority, program), então trate-o como o mesmo padrão, não a mesma forma. Consulte a seção específica de cada programa em vez de assumir que a lista de contas deste passo a passo se transfere diretamente.

Construção da lista de contas

Toda CPI do Raydium requer uma 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 do próprio Raydium, já que seu cliente gerado pelo IDL as endereça por nome, não por posição:
A maioria das contas do lado do Raydium são UncheckedAccount porque o programa chamado (Raydium) é responsável pela validação. Seu programa chamador valida estritamente apenas as contas que você possui, como ATAs de usuário e seus próprios PDAs. O comentário de documentação /// CHECK: suprime o aviso do Anchor sobre verificações ausentes. A única exceção do lado do Raydium é o próprio cpmm_program: é o programa sendo invocado, não uma conta de dados que o Raydium valida internamente, então é tipado como Program<T> e recebe a verificação automática de endereço do Anchor em vez de um /// CHECK: manual. Esta forma, principalmente UncheckedAccount, onde o 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 um deles puder ser um mint Token-2022, adicione um campo token_program_2022: Program<'info, anchor_spl::token_2022::Token2022> e passe-o como input_token_program/output_token_program desse lado na chamada CPI abaixo em vez de token_program.

Construindo a chamada CPI

O Anchor gera um helper por instrução, junto com uma struct de contas CPI (cpi::accounts::Swap, com alias CpmmSwap abaixo). Diferentemente de sua própria struct MyProxySwap acima, os nomes e a ordem dos campos desta são fixos pelo IDL do próprio raydium-cp-swap e devem corresponder exatamente:
cpi::swap_base_input é gerado a partir do IDL; sua lista de argumentos espelha a lista de argumentos da instrução do 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 ao Farm v6 é não confirmado; consulte sua seção.

Signer seeds (CPI assinada por PDA)

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

Remaining accounts

Algumas instruções do Raydium aceitam remaining accounts, uma lista de comprimento variável anexada após as contas fixas. Os helpers CPI do Anchor não fazem verificação de tipo para remaining accounts; passe-os via .with_remaining_accounts(...):
A ordem sempre importa, já que o programa receptor itera remaining accounts na ordem em que você os passa. Duas ordenações confirmadas:
  • CLMM SwapV2: tick arrays, ordenadas direcionalmente.
  • Farm v6: pares (reward_vault, user_reward_ata), mas apenas a partir do segundo fluxo de recompensa em diante; consulte Farm v6 para ver o que a decodificação de 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 remaining-accounts para tick arrays. O módulo #[program] do crate é nomeado raydium_clmm, que também é seu caminho use do Rust.
A struct de contas CPI é nomeada SwapSingleV2, não SwapV2. SwapV2 é o nome da instrução on-chain.
Calcule a lista de tick-array 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 (consulte products/clmm/instructions para a tabela de contas completa e lista de erros). Passe-os na direção do caminhar de preço: primeiro array na direção do swap primeiro.

Aplicando o padrão: LaunchLab

LaunchLab é baseado em Anchor e publica um IDL: raydium_launchpad/raydium_launchpad.json no repositório público raydium-idl. O identificador de metadados interno 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, o código-fonte do programa não está disponível publicamente (veja reference/program-addresses). Não há uma dependência git = "..." para apontar o Cargo, e nenhum código-fonte para confirmar qual seria o caminho use real do Rust de um crate. Gere bindings a partir do IDL publicado usando a macro declare_program! do Anchor. Salve o JSON do IDL como idls/raydium_launchpad.json no seu crate (Cargo procura por um diretório idls/ relativo a CARGO_MANIFEST_DIR), então declare_program!(raydium_launchpad); gera structs raydium_launchpad::cpi::accounts::<Ix> e funções cpi::<ix>() diretamente do IDL, sem precisar do código-fonte do programa. O nome da struct de contas gerada é sempre o nome da instrução em PascalCase (buy_exact_in → BuyExactIn), e os nomes dos campos correspondem exatamente aos nomes de contas do IDL, a mesma lista de contas já usada em MyProxyBuy abaixo. A forma do 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:
Após a conclusão, o programa alvo é 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 precisa estar preparada para ambos, ou você precisa ler migrate_type de PoolState primeiro e fazer uma ramificação.

Propagação de erros

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:
Substitua pelo tipo de erro relevante do 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 conforme a política do IDL (sdk-api/anchor-idl), então você pode testar contra códigos específicos comparando com 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 de CPI tem overhead, e o próprio consumo de CU do chamado se acumula sobre o 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. Esse número era a transação inteira (computeUnitsConsumed), que inclui o próprio programa do chamador, o frame de 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 com base em um ou outro.
CPIs de CLMM e LaunchLab custam mais (CLMM em particular percorre arrays de tick adicionais via remaining_accounts, adicionando CU por array), mas apenas o número de CPMM acima é um valor medido. Sempre defina uma instrução ComputeBudgetProgram::set_compute_unit_limit(...) explícita dimensionada 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 Instruction

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

Farm v6

Use o SDK do TypeScript se essa for uma opção para sua integração. raydium.farm.deposit(...) (veja products/farm-staking/code-demos) é exercitado por demos reais e não depende de se um crate Anchor existe para este programa.
Farm v6 não oferece caminho de CPI Anchor. Não há um crate raydium_farm_v6 em crates.io, nenhum repositório de código-fonte público, e nenhum IDL on-chain — o programa não possui nem uma conta anchor:idl legada nem uma entrada no programa Program Metadata (veja sdk-api/anchor-idl). Trate-o como um programa não-Anchor e construa suas instruções manualmente, como abaixo.
Se você precisar de CPI Rust mesmo assim, por exemplo compondo a partir de outro programa on-chain, construa a Instruction manualmente, da mesma forma que AMM v4: derive a lista de contas real e os discriminadores de instrução independentemente, por exemplo decodificando os layouts do 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 conta real é um prefixo fixo (token_program, a conta de estado do farm, um PDA de autoridade de vault, o primeiro vault 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 só começa no segundo fluxo de recompensa: o vault e ATA do primeiro fluxo são contas fixas, não adjacentes um ao outro, e não fazem parte de remaining_accounts em absoluto.

Testando um fluxo de CPI

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

Clonando programas em um validador local

Isso funciona por ID de programa independentemente de o código-fonte do programa ser público, então LaunchLab clona da mesma forma que CPMM e CLMM, mesmo que seu código-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 do CPMM valida suas contas amm_config e create_pool_fee contra dados reais na 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ê deseja (busque seu endereço em GET https://api-v3.raydium.io/main/cpmm-config, índice 0 é o tier de 0,25%) e a conta de token receptora de taxa, validada por endereço exato, não criada dinamicamente, então ela já deve existir.
Um pool que seu teste acabou de criar não é permutável no mesmo instante. A instrução initialize do 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 até startTime: 0 (“abrir imediatamente,” conforme o SDK) deixa uma lacuna real de ≥1 segundo antes do pool aceitar swaps. Um teste que cria um pool e faz swap contra ele com zero atraso atingirá NotApproved. Um pequeno await (1–2s) entre a criação do pool e o primeiro swap é suficiente. Isso é específico para testes; um humano executando dois comandos manuais separados normalmente não notaria, já que digitar e inicialização de processo já consomem mais de um segundo.

Referências

Fontes: