Conceptos básicos de programas nativos
10 ejemplos para empezar con los Programas Nativos: 7 básicos y 3 intermedios.
Prerrequisitos
- Solana CLI 3.0.10 con
cargo build-sbf. - Rust 1.91.1 y un crate de programa con dependencia
solana-program. - Familiaridad con Conceptos básicos de Rust para Solana.
Ejemplos básicos
1. Cargo.toml mínimo
Los programas nativos tienen como destino SBF con el tipo de crate cdylib.
[lib]
crate-type = ["cdylib"]
[dependencies]
solana-program = "2.2"
borsh = "1.5"cdylibproduce el.sodesplegable.- Fija
solana-programpara que coincida con el SDK de Agave 4.1.1. - Mantén bajo el recuento de dependencias.
2. Esqueleto de lib.rs
La raíz del crate conecta módulos y el punto de entrada.
#![no_std]
extern crate alloc;
mod error;
mod instruction;
mod processor;
use solana_program::entrypoint;
entrypoint!(processor::process_instruction);- Divide el despacho, el estado y los errores en módulos.
- Solo un
entrypoint!por crate. - Exporta la enumeración de instrucciones en
instruction.rs.
3. Enumeración de instrucciones
Etiqueta las instrucciones con una enumeración Borsh.
use borsh::{BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize)]
pub enum HelloInstruction {
Greet { name: String },
}- El primer byte es el índice de variante por defecto.
- Mantén las variantes estables entre actualizaciones.
- Coincide exactamente con la serialización del cliente.
Relacionado: Serialización/Deserialización de datos de instrucciones - análisis
4. Coincidencia de despacho
Dirige las instrucciones analizadas a los manejadores.
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
data: &[u8],
) -> ProgramResult {
let ix = HelloInstruction::deserialize(&mut &data[..])?;
match ix {
HelloInstruction::Greet { name } => greet(accounts, name),
}
}- Deserializa una vez en el nivel superior.
- Pasa
program_ida los manejadores que derivan PDAs. - Usa
?para la propagación de errores.
Relacionado: El Punto de Entrada y el Despacho - patrones
5. Iterador de cuentas
Consume cuentas en un orden fijo.
let iter = &mut accounts.iter();
let payer = next_account_info(iter)?;
let state = next_account_info(iter)?;- El orden debe coincidir con la lista
AccountMetadel cliente. - Devuelve
NotEnoughAccountKeyssi el iterador está vacío. - Documenta el orden en el README/IDL del programa.
6. Módulo de error personalizado
Centraliza los errores del dominio.
#[repr(u32)]
pub enum MyError { BadName = 0 }
impl From<MyError> for ProgramError {
fn from(e: MyError) -> Self { ProgramError::Custom(e as u32) }
}- Mapea a
ProgramError::Custompara los clientes. - Evita rangos de Anchor superpuestos si se mezclan herramientas.
- Exporta códigos para clientes de @solana/kit 7.0.0.
7. Estructura de estado con LEN
Define la constante de tamaño de la cuenta.
#[derive(BorshSerialize, BorshDeserialize)]
pub struct State { pub counter: u64 }
impl State {
pub const LEN: usize = 8;
}- Usa la constante al llamar a
create_account. - Verifica la longitud en cada acceso.
InitSpacede Anchor es el equivalente a la macro.
Ejemplos intermedios
8. Crear cuenta a través de CPI
El Programa del Sistema asigna espacio y asigna propietario.
invoke(
&system_instruction::create_account(
payer.key, state.key, rent, State::LEN as u64, program_id,
),
&[payer.clone(), state.clone(), system_program.clone()],
)?;- El pagador debe firmar y ser escribible.
- La exención de alquiler se calcula con la sysvar
Rent. - Después de la CPI, serializa el estado inicial.
Relacionado: Creación de Cuentas mediante CPI - flujo completo
9. Leer y actualizar estado
Toma prestados datos, muta, serializa de nuevo.
let mut state = State::deserialize(&mut &account.data.borrow()[..])?;
state.counter = state.counter.saturating_add(1);
state.serialize(&mut &mut account.data.borrow_mut()[..])?;- Libera los préstamos antes de que la CPI toque la misma cuenta.
- Usa matemáticas comprobadas/saturadas.
- El propietario debe ser tu programa.
Relacionado: Lectura y Escritura de Datos de Cuentas - patrones
10. Emitir una línea de registro
Depura con msg! durante el desarrollo.
solana_program::msg!("greet: {}", name);- Visible en exploradores y simulación.
- Cuesta CUs - elimínalo en rutas críticas si es necesario.
- Prefiere errores para fallos orientados al usuario.
Relacionado: Emisión de Registros - guía de registro
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.