Conceptos básicos de CPI en Anchor
8 ejemplos para empezar con las CPI de Anchor: 5 básicos y 3 intermedios.
Prerrequisitos
- anchor-lang 0.32.1 y anchor-spl 0.32.1 en el
Cargo.tomldel programa - Comprensión de Manejadores de Instrucciones
- Cuentas de programa para System, Token o destinos personalizados en la struct de cuentas
Ejemplos básicos
1. CpiContext::new para transferencia de SOL
La CPI del programa System mueve lamports entre cuentas.
use anchor_lang::system_program::{self, Transfer};
system_program::transfer(
CpiContext::new(
ctx.accounts.system_program.to_account_info(),
Transfer {
from: ctx.accounts.payer.to_account_info(),
to: ctx.accounts.recipient.to_account_info(),
},
),
lamports,
)?;- La cuenta del programa es el primer argumento de CpiContext::new
- La struct interna enumera las metadatos de cuenta para la CPI
- Usa
?para propagar ProgramError
2. CPI de transferencia de token SPL
anchor-spl envuelve la invocación del programa de token.
use anchor_spl::token::{self, Transfer};
token::transfer(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Transfer {
from: ctx.accounts.from_ata.to_account_info(),
to: ctx.accounts.to_ata.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
},
),
amount,
)?;- La autoridad debe firmar o ser una PDA con
with_signer - Las cuentas de token deben ser mut
- Incluye
Program<Token>en las cuentas
3. with_signer para autoridad PDA
Las PDAs firman las CPI a través de porciones de semillas.
let seeds = &[b"vault".as_ref(), &[ctx.bumps.vault]];
let signer = &[&seeds[..]];
token::transfer(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
accounts,
signer,
),
amount,
)?;- Las semillas deben coincidir con las restricciones de la PDA
- Usa
ctx.bumpscuando esté disponible - La cuenta de autoridad es la PDA
4. CpiContext con signer
Añade semillas a un contexto existente con .with_signer.
let cpi_ctx = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
transfer_accounts,
);
token::transfer(cpi_ctx.with_signer(signer), amount)?;- Útil cuando las semillas del firmante se calculan condicionalmente
- Misma semántica que
new_with_signer - Prefiere un estilo por base de código
5. Incluir cuentas de programa
Fija los IDs de programa de CPI en la struct de cuentas.
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,- Previene ataques de sustitución de programas
- Requerido para los ayudantes de CPI de Anchor
- Usa
Interfacepara Token-2022
Ejemplos intermedios
6. Módulo CPI declare_program!
Anchor 0.32 genera clientes CPI a partir de IDLs externas.
// lib.rs
declare_program!(other_program);
pub fn call_other(ctx: Context<CallOther>) -> Result<()> {
other_program::cpi::do_something(
CpiContext::new(
ctx.accounts.other_program.to_account_info(),
other_program::cpi::accounts::DoSomething { /* ... */ },
),
)?;
Ok(())
}- Coloca el JSON de IDL en el espacio de trabajo para
declare_program! - Módulo generado bajo
cpi:: - Structs de cuentas seguras de tipo para CPI
Relacionado: CPI a Programas Personalizados
7. Cuentas restantes de CpiContext
Reenvía cuentas adicionales al destino de la CPI.
let mut cpi_ctx = CpiContext::new(program, accounts);
cpi_ctx = cpi_ctx.with_remaining_accounts(ctx.remaining_accounts.to_vec());
other_program::cpi::advanced(cpi_ctx)?;- El programa de destino debe esperar extras
- Valida antes de reenviar
- El orden debe coincidir con el llamado
Relacionado: Pasar Cuentas Restantes
8. Propagación de errores
Los fallos de CPI se devuelven como ProgramError en el llamador.
token::transfer(cpi_ctx, amount)
.map_err(|_| MyError::TransferFailed)?;- Mapea errores opacos a
#[error_code]personalizados - Registra antes de devolver para depuración
- Simula transacciones para ver errores internos
Versiones de la pila: Esta página se escribió para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0 y LiteSVM 0.6.x.