Creación y Cierre de Cuentas
La creación de cuentas asigna espacio, designa un propietario y financia la exención de alquiler. El cierre recupera lamports. realloc aumenta o reduce los datos en el lugar.
Receta
// Crear
#[account(init, payer = payer, space = 8 + MyData::INIT_SPACE)]
pub data: Account<'info, MyData>,
// Ampliar
#[account(mut, realloc = new_size, realloc::payer = payer, realloc::zero = false)]
pub data: Account<'info, MyData>,
// Cerrar
#[account(mut, close = receiver)]
pub data: Account<'info, MyData>,Cuándo usar esto:
- Flujos de incorporación de usuarios que crean PDAs o cuentas de configuración
- Instrucciones de migración que amplían el tamaño de la cuenta
- Incentivos de limpieza que devuelven el alquiler a los usuarios
- Cuentas de bóveda de recuperación al cerrar el protocolo
Ejemplo de Trabajo
use anchor_lang::prelude::*;
#[program]
pub mod lifecycle {
use super::*;
pub fn create(ctx: Context<Create>, label: String) -> Result<()> {
ctx.accounts.data.authority = ctx.accounts.payer.key();
ctx.accounts.data.label = label;
Ok(())
}
pub fn expand(ctx: Context<Expand>, extra: u32) -> Result<()> {
ctx.accounts.data.extra = extra;
Ok(())
}
pub fn close_account(ctx: Context<CloseData>) -> Result<()> {
Ok(())
}
}
#[account]
#[derive(InitSpace)]
pub struct MyData {
pub authority: Pubkey,
#[max_len(20)]
pub label: String,
pub extra: u32,
}
#[derive(Accounts)]
pub struct Create<'info> {
#[account(init, payer = payer, space = 8 + MyData::INIT_SPACE)]
pub data: Account<'info, MyData>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Expand<'info> {
#[account(
mut,
realloc = 8 + MyData::INIT_SPACE,
realloc::payer = payer,
realloc::zero = false,
)]
pub data: Account<'info, MyData>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct CloseData<'info> {
#[account(mut, close = receiver, has_one = authority)]
pub data: Account<'info, MyData>,
pub authority: Signer<'info>,
#[account(mut)]
pub receiver: SystemAccount<'info>,
}Lo que esto demuestra:
- CPIs
inital Programa del Sistema para asignación + transferencia de alquiler reallocpuede requerir lamports adicionales delrealloc::payerclosepone los datos a cero y envía todos los lamports areceiver
Análisis Profundo
Pasos de Creación (Tiempo de Ejecución)
- El pagador firma y proporciona lamports.
- El Programa del Sistema asigna
spacebytes. - El propietario se establece en el programa que llama.
- El programa escribe los datos iniciales.
Seguridad del Cierre
- Anchor
closeestablece los lamports a 0 y asigna la propiedad al Programa del Sistema. - Siempre verifique la autoridad antes de cerrar para evitar ataques de griefing.
- No se pueden cerrar cuentas que aún contengan fondos inesperados (añadir restricciones).
Trampas Comunes
initsinsystem_program: fallo de compilación/tiempo de ejecución. Solución: incluir siempreProgram<'info, System>.- La reducción de
reallocpierde datos: trunca bytes. Solución: migrar datos antes de reducir o solo aumentar. - Cierre de cuentas referenciadas en otros lugares: los indexadores posteriores fallan. Solución: emitir un evento y documentar el ciclo de vida.
- Olvido de
muten el pagador pararealloc: financiación insuficiente. Solución: marcar el pagador comomutcuandoreallocpueda costar lamports. - Cierre de cuentas de tokens con saldo: SPL requiere que el saldo esté vacío primero. Solución: transferir tokens y luego cerrar a través de CPI de SPL.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
init_if_needed | Incorporación idempotente | Seguridad estricta de creación única |
| CPI Manual del Sistema | Programas nativos sin Anchor | Proyectos Anchor (usar macros) |
| Migración de cuentas nuevas | Cambios de diseño importantes | Adiciones menores de campos |
realloc | Crecimiento modesto del tamaño | Cambios grandes en conjuntos de datos (nuevo PDA) |
Preguntas Frecuentes
¿Qué hace `init` internamente?
CPI al Programa del Sistema create_account con espacio, propietario y lamports del pagador.
¿Quién puede cerrar una cuenta?
Tu programa a través de la restricción close, que normalmente requiere la firma de la autoridad.
¿Adónde van los lamports recuperados?
A la cuenta especificada en close = receiver.
¿Cuándo se necesita `realloc`?
Cuando los datos de la cuenta crecen más allá del space asignado originalmente.
¿Cuesta `realloc` alquiler adicional?
Sí, si un tamaño mayor requiere un mínimo de exención de alquiler más alto. El pagador financia la diferencia.
¿Puedo cerrar un PDA?
Sí. El programa firma si es necesario; los lamports van al receptor.
¿Qué es `init_if_needed`?
Crea la cuenta solo si no existe; útil para patrones similares a ATAs con cuidado.
¿Se pueden revivir las cuentas cerradas?
La misma dirección se puede reinicializar si se recrea con init; trátela como una cuenta nueva.
¿Cómo cierro varias cuentas a la vez?
Múltiples restricciones close en una instrucción; ten en cuenta los límites de CU.
¿El cierre emite un evento?
No automáticamente. Añade emit! para los indexadores.
¿Pueden los usuarios cerrar cuentas sin el programa?
Las cuentas vacías propiedad del sistema se pueden vaciar; las propiedad del programa requieren una instrucción del programa.
¿Qué es `realloc::zero`?
Si los nuevos bytes se ponen a cero. false preserva los datos existentes durante el crecimiento.
Relacionado
- Alquiler y Exención de Alquiler - importes de financiación
- Cuentas de Datos y Diseño - planificación del espacio
- Estado Propiedad del Programa - creación de PDAs
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.