Conceptos básicos de CPI
10 ejemplos para empezar con las CPI: 7 básicos y 3 intermedios.
Prerrequisitos
- Familiaridad con el punto de entrada del programa nativo.
- Orden de metadatos de cuenta de Trabajar con Pubkeys.
Ejemplos básicos
1. invoke simple
Llama a la transferencia del Programa del Sistema desde tu programa.
use solana_program::program::invoke;
use solana_program::system_instruction;
invoke(
&system_instruction::transfer(from.key, to.key, lamports),
&[from.clone(), to.clone()],
)?;- El ID del programa llamado (
callee) está incrustado en laInstruction. - Las cuentas deben coincidir con las expectativas del programa llamado.
- Devuelve
?para propagar errores de CPI.
2. Estructura Instruction
Construye una Instruction con el ID del programa, las cuentas y los datos.
use solana_program::instruction::{AccountMeta, Instruction};
let ix = Instruction {
program_id: token_program.key(),
accounts: vec![/* AccountMeta */],
data: vec![/* opcode */],
};- El diseño de los datos debe coincidir con el programa llamado.
- Prefiere crates auxiliares (
spl-token) para la codificación. - ID de programa incorrecto =
IncorrectProgramId.
3. Clonar AccountInfo
invoke necesita referencias AccountInfo propias en el slice.
&[payer.clone(), vault.clone(), system_program.clone()]cloneenAccountInfoes una clonación de referencia barata.- El orden coincide con el orden de
AccountMeta. - Incluye las cuentas del programa como de solo lectura (
readonly) a menos que se requiera escritura.
4. Propagación de errores de CPI
Una CPI fallida hace fallar tu instrucción.
invoke(&ix, accounts).map_err(|e| {
solana_program::msg!("cpi failed");
e
})?;- Toda la transacción se revierte ante un error de CPI.
- Mapea errores si necesitas códigos personalizados.
- Los registros pueden incluir errores del programa llamado.
5. Cuenta de programa en CPI
Pasa la cuenta del programa de tokens/sistema como de solo lectura.
let system_program = next_account_info(iter)?;
if system_program.key != &system_program::ID {
return Err(ProgramError::IncorrectProgramId);
}- Valida los IDs de programa antes de
invoke. - Las cuentas ejecutables son cuentas de programa.
- Nunca pases un ID de programa incorrecto.
6. Requisitos de firmante (signer)
La CPI no puede añadir firmas que las billeteras no proporcionaron.
if !payer.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}- Excepción: PDAs a través de
invoke_signed. - El débito de un usuario requiere la firma del usuario.
- El débito de un PDA requiere las semillas del programa.
7. CPI de solo lectura
Algunas CPI solo leen el estado del programa llamado.
// ej. lectura de oráculo - aún pasa las cuentas- El indicador
writabledebe serfalsesi no se está mutando. - Metadatos sobrescritos (
over-writable) son hallazgos de auditoría. - Sigue la documentación de la interfaz del programa llamado.
Ejemplos intermedios
8. invoke_signed para PDA
El programa firma la CPI en nombre del PDA.
invoke_signed(&ix, accounts, &[&[b"vault", &[bump]]])?;- Las semillas deben coincidir con la derivación del PDA.
- Requerido para PDAs de autoridad de token.
- Ver la página
invokevsinvoke_signed.
Relacionado: invoke vs invoke_signed
9. Transferencia de tokens SPL con CPI
Transfiere tokens con el programa de tokens.
use spl_token::instruction as token_ix;
let ix = token_ix::transfer(
token_program.key,
source.key,
dest.key,
authority.key,
&[],
amount,
)?;
invoke(&ix, &[source.clone(), dest.clone(), authority.clone(), token_program.clone()])?;- La autoridad debe firmar o ser un PDA con
invoke_signed. - Valida la coincidencia de
mintentre las cuentas. - Usa matemáticas de cantidad verificadas (
checked amount math) antes de la CPI.
Relacionado: CPI a SPL Token
10. Conciencia de la profundidad de la CPI
Las CPI pueden llamar a programas que a su vez hacen CPI; profundidad máxima 4.
// Tu programa -> Token -> ... presupuesto de profundidad- Las cadenas profundas consumen CUs (Unidades de Computación).
- Diseña gráficos de CPI planos siempre que sea posible.
- Ver Profundidad y Límites de CPI.
Relacionado: Profundidad y Límites de CPI
Versiones de la pila: Esta página fue escrita 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.