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 →

Quando CPI é a ferramenta certa

Um programa customizado faz sentido quando a troca precisa acontecer atomicamente com outras mudanças de estado on-chain que apenas seu programa pode fazer. Casos comuns:
  • Programas de escrow / ordem limitada — o usuário deposita um mint no seu escrow, seu programa monitora uma condição de preço, e quando dispara, seu programa atomicamente faz swap através do Raydium e credita a conta do usuário.
  • Proxies agregadores — uma única instrução que roteia um swap através do Raydium + um ou mais outros DEXes, com todos os hops sob uma única verificação de slippage de propriedade do seu programa.
  • Vaults com auto-compounding — deposite tokens LP ou stake de farm no seu vault, o vault colhe recompensas em um cronograma, re-fornece liquidez, emite tokens de participação.
  • Vaults de estratégia — posições LP alavancadas que rebalanceiam fazendo swap através de CLMM; liquidadores que fecham posições e fazem swap de colateral em uma transação.
  • Plataformas de lançamento de tokens com vesting customizado — seu programa mantém tokens em vesting e libera em um pool Raydium em um cronograma.
Se você apenas quer enviar um swap de código off-chain, CPI é excessivo — use o SDK. CPI justifica sua complexidade apenas quando atomicidade com seu próprio estado é o requisito.

Padrões de composição

Padrão 1: Proxy fino

Seu programa expõe uma única instrução que valida alguma política (ex: pares de mint na whitelist, desconto de taxa para usuários verificados) e depois encaminha para o Raydium.
O estado vive nas ATAs do usuário. Seu programa não possui tokens. Pegada de confiança mínima.

Padrão 2: Escrow

Seu programa possui um PDA que mantém o mint de entrada do usuário. No disparo, o PDA assina um CPI para o Raydium fazer swap de seu próprio saldo.
Detalhe crítico: o PDA assina via CpiContext::new_with_signer. Veja Sementes de signatário PDA.

Padrão 3: Multi-hop composto

Seu programa emite múltiplos CPIs em uma instrução, aplicando um único limite de slippage em todos eles. As instruções de swap do Raydium cada uma têm seu próprio minimum_amount_out, mas você define aqueles para 0 (ou um piso muito solto) e aplica um mínimo final rigoroso você mesmo após o último hop.
Isso lhe dá um portão de reversão único para toda a rota. Use este padrão apenas quando você confia que cada hop é seguro em relação a slippage; caso contrário, deixe cada hop aplicar seu próprio mínimo.

Padrão 4: Vault / estratégia

Seu programa mantém tokens LP ou stake de farm em um PDA. Um keeper (ou o usuário) chama compound(), que:
  1. Colhe recompensas do farm.
  2. Faz swap de recompensas por tokens de pool (CPI em CPMM ou CLMM).
  3. Deposita os rendimentos de volta no LP (outro CPI).
  4. Faz stake do novo LP (outro CPI).
Tudo em uma transação para que o NAV do vault se mova atomicamente. O orçamento de compute é tipicamente 600k–1M CU; tabelas de lookup de endereço são obrigatórias.

Construção da lista de contas

A struct Accounts do programa chamador espelha a ordem de contas do programa Raydium, mas a maioria das contas do lado Raydium são UncheckedAccount porque o Raydium as valida por si mesmo. Você apenas adiciona restrições em contas que você possui:
A assimetria — validação rigorosa em suas contas, UncheckedAccount nas do Raydium — não é preguiça. O receptor valida as suas; validar duas vezes no chamador apenas queima CU e corre o risco de ficar fora de sincronização quando o Raydium envia um novo campo de layout de struct.

A chamada CPI em si

Sementes de signatário PDA

O CPI só tem sucesso se o PDA passado como authority corresponder à derivação que o chamador afirma. Os dois devem concordar em:
  1. A sequência de bytes de semente (aqui [b"escrow", user.key().as_ref()]).
  2. O bump.
  3. O ID do programa chamador (seu programa, não o do Raydium).
Note o que o PDA tem que corresponder. O slot authority do CPMM é seu próprio PDA de vault — uma conta fixa de todo o programa que ele deriva e assina com ele mesmo, e que seu programa nem controla nem substitui. A conta com a qual suas sementes PDA têm que se alinhar é payer: a verificação acontece dentro do helper transfer_from_user_to_pool_vault do próprio CPMM, que requer que a conta passada como payer seja a proprietária de input_token_account. Bug comum: passar user como payer enquanto escrow_input_ata é de propriedade do PDA de escrow. O programa SPL Token rejeita com owner mismatch. Sempre faça payer ser o proprietário da ATA — e assine por ela com new_with_signer quando esse proprietário é um PDA.

Contas restantes

Várias instruções do Raydium levam uma lista de comprimento variável de contas anexadas após as fixas — contas restantes.
  • CLMM SwapV2: 1–8 contas TickArrayState para os arrays de tick que o swap pode atravessar, na direção do swap.
  • Farm v6 Deposit / Harvest / Withdraw: pares (reward_vault, user_reward_ata), um par por slot de recompensa ativo.
  • Mints de transfer-hook Token-2022: o programa de transfer-hook mais quaisquer contas que o hook precise.
Os helpers CPI do Anchor não verificam tipos de contas restantes. Passe-as através:
A ordem importa. Para CLMM:
Para farm v6 harvest:
Seu programa chamador deve passar as contas restantes que recebe do cliente inalteradas. Não tente filtrá-las ou reordená-las.

Orçamento de compute para chamadas compostas

Um CPI custa ~1.500 CU para o frame de chamada em si; o uso de CU do chamado se acumula no topo. Os valores do chamado abaixo são medidos de transações mainnet ao vivo em pools de alto volume em 2026-09-09, lidos da linha de log Program <id> consumed N of M compute units para a própria invocação do programa Raydium (então incluem seus CPIs internos de programa de token): Adicione ~1.500 para cada frame CPI e a sobrecarga do seu próprio programa no topo. O custo de swap CLMM escala com travessias de tick, então trate seu valor como um piso. Mints Token-2022 adicionam o custo de manipulação de extensão da transferência em si; meça para seus próprios mints em vez de aplicar um multiplicador fixo.
Revisões anteriores desta página carregavam estimativas 5–7× maiores (um swap CPMM de ~150.000 CU, ~180.000 para CLMM). Aquelas nunca foram medidas. Orçamento a partir de sua própria leitura de computeUnitsConsumed, não de um número documentado — e note que uma transação completa custa mais que a instrução Raydium sozinha uma vez que criação de ATA, envolvimento de wSOL e instruções de orçamento de compute são contadas.
Sempre defina um ComputeBudgetProgram::set_compute_unit_limit explícito:
O teto padrão de 200k CU se esgotará silenciosamente muito antes de uma chamada composta ser concluída.

Propagação de erro

Os programas do Raydium retornam erros Anchor com códigos estáveis. Seu programa chamador os vê como Err(ProgramError::Custom(code)). Propague por padrão:
Ou intercepte para códigos específicos:
Note o termo ERROR_CODE_OFFSET: variantes #[error_code] são emitidas começando em 6000, então comparar contra o discriminante de enum nu nunca corresponde. (Não há helper is_err em anchor-lang ou em raydium_cp_swap — revisões anteriores desta página usavam um que não existe.) O mapeamento de código de erro para significado é estável por política de IDL (sdk-api/anchor-idl); novos códigos se anexam ao final, códigos existentes nunca mudam de significado.

Exemplo completo trabalhado: escrow de ordem limitada

Fluxo:
  1. open_order — usuário deposita amount_in de input_mint no PDA de escrow; registra min_amount_out alvo e expiração.
  2. execute_order — qualquer um (keeper) chama com as contas de pool atuais. Programa verifica a cotação atual ≥ min_amount_out, depois CPI swap do Raydium e mantém a saída em escrow.
  3. claim — usuário retira o mint de saída do escrow.
O keeper paga a taxa de transação (recebe uma taxa de keeper em outro lugar — não mostrado). O PDA order assina o CPI como payer, porque possui a ATA de entrada do escrow; ExecuteOrder portanto também precisa de um campo pool_authority: UncheckedAccount<'info> para o próprio PDA de vault do CPMM. Tanto a verificação de slippage do lado Raydium quanto a verificação de delta do próprio escrow aplicam o piso — segurança dupla.

Testes

Puxando programas Raydium para um validador local para testes de integração (de Anchor.toml):
Clone também as contas de estado do pool para que seus testes possam realmente executar swaps; anchor test as busca da mainnet na inicialização. Veja sdk-api/rust-cpi.

Armadilhas específicas de composição

Reentrância

Solana não tem verdadeira reentrância — um CPI não pode chamar de volta para o programa originador na mesma invocação. Mas você ainda pode se construir em uma reentrância lógica: um CPI que lê seu estado, depois seu código o lê novamente assumindo que o CPI não o mudou. Para Raydium, os CPIs não tocam seu estado, então isso é menos uma preocupação do que ex: contextos de flash-loan. Mas se você compor Raydium com um protocolo de empréstimo, esteja ciente.

Deriva de mutabilidade de conta

Se seu programa passa uma conta como mut mas Raydium espera apenas leitura (ou vice versa), o runtime rejeita a invocação com InvalidAccountData. Sempre verifique a mutabilidade esperada da instrução do Raydium no IDL; raydium_cp_swap::cpi::accounts::Swap define a mutabilidade de cada conta para você, a partir dos marcadores #[account(mut)] na própria struct Swap do CPMM — os campos derivados são todos AccountInfo<'info> simples, então é a impl ToAccountMetas derivada, não os tipos de campo, que carrega as flags.

Campo de programa Token-2022

Mints de entrada e saída podem estar sob diferentes programas de token — um SPL Token, um Token-2022. O CPI tem campos input_token_program e output_token_program separados por essa razão. Sempre verifique o campo owner de cada mint e roteia o programa correto em cada slot.

Transações versionadas

Uma tx composta que faz 2+ CPIs Raydium mais uma criação de ATA raramente cabe em uma transação legada (v0-sem-LUT). Use V0 com tabelas de lookup de endereço; puxe LUTs públicas do Raydium via raydium.getRaydiumLutAddresses().

Ponteiros

Fontes: