Estado Propiedad del Programa
Los programas de Solana son ejecutables sin estado. Todo el estado duradero reside en cuentas propiedad del programa - direcciones separadas cuyo campo owner es tu ID de programa.
Receta
#[account]
pub struct Escrow {
pub maker: Pubkey,
pub amount: u64,
}
#[account(
init,
payer = maker,
space = 8 + Escrow::INIT_SPACE,
seeds = [b"escrow", maker.key().as_ref()],
bump,
)]
pub escrow: Account<'info, Escrow>,Cuándo usar esto:
- Diseñar cualquier protocolo que almacene datos de configuración de usuario o globales.
- Reemplazar patrones EVM
mapping(address => Data). - Separar el estado "caliente" por usuario de las cuentas de configuración globales.
- Habilitar transacciones paralelas en cuentas de usuario no superpuestas.
Ejemplo Funcional
use anchor_lang::prelude::*;
declare_id!("Escrow1111111111111111111111111111111111111");
#[program]
pub mod escrow_program {
use super::*;
pub fn initialize(ctx: Context<Initialize>, amount: u64) -> Result<()> {
let escrow = &mut ctx.accounts.escrow;
escrow.maker = ctx.accounts.maker.key();
escrow.amount = amount;
Ok(())
}
pub fn release(ctx: Context<Release>) -> Result<()> {
let escrow = &ctx.accounts.escrow;
require!(escrow.amount > 0, EscrowError::Empty);
// transferir lamports vía CPI...
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(
init,
payer = maker,
space = 8 + Escrow::INIT_SPACE,
seeds = [b"escrow", maker.key().as_ref()],
bump,
)]
pub escrow: Account<'info, Escrow>,
#[account(mut)]
pub maker: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
#[derive(InitSpace)]
pub struct Escrow {
pub maker: Pubkey,
pub amount: u64,
}Lo que esto demuestra:
- El estado de la cuenta de depósito en garantía es una cuenta propiedad de
escrow_program. - Las semillas de PDA vinculan la cuenta a un creador específico.
- El bytecode del programa nunca almacena
amount- solo la cuenta de depósito en garantía lo hace.
Análisis Profundo
Patrones de Estado
| Patrón | Semillas | Uso |
|---|---|---|
| PDA por usuario | [b"user", pubkey] | Perfiles, posiciones |
| Configuración global | [b"config"] | Ajustes de administrador |
| Bóveda por token | [b"vault", mint] | Custodia de tokens |
Por Qué Programas Sin Estado
- El mismo código de programa sirve a usuarios ilimitados a través de cuentas separadas.
- Ejecución paralela cuando las transacciones tocan cuentas diferentes.
- Las actualizaciones reemplazan el código sin migrar el almacenamiento incrustado.
Errores Comunes
- Singleton global para todos los usuarios: Contención y límites de tamaño. Solución: Fragmentar el estado en PDAs.
- Almacenar estado solo en los datos de la instrucción: No es duradero. Solución: Persistir en cuentas.
- Falta de almacenamiento del bump de la PDA: Costo de recálculo y problemas de canonicalidad de semillas. Solución: Almacenar el bump en la cuenta o usar
seeds::programconbump. - Desajuste del propietario al inicializar: Otro programa podría suplantar. Solución:
initde Anchor establece el propietario automáticamente. - No cerrar cuentas obsoletas: Rent bloqueado para siempre. Solución: Restricción
closecuando finaliza el ciclo de vida.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| PDA por entidad | Estado mutable específico del usuario | Banderas globales pequeñas |
| Cuenta de configuración única | Configuración de todo el protocolo | Datos de usuario con escrituras frecuentes |
| Cuenta sin copia (Zero-copy) | Datos de cuenta grandes | Estructuras pequeñas |
| Compresión de estado | Emisiones / reclamaciones masivas | Registros actualizados frecuentemente |
Preguntas Frecuentes
¿Dónde almacena mi programa el saldo de un usuario?
En una cuenta propiedad del programa (a menudo una PDA), no dentro del binario del programa.
¿Pueden los programas leer las cuentas de estado de otros programas?
Sí, si se pasan en la transacción y se conoce la deserialización. La escritura todavía requiere la lógica del programa propietario.
¿Cuántas cuentas puede poseer un programa?
Direcciones distintas ilimitadas. Cada una es una cuenta separada en la cadena.
¿Qué es el anti-patrón de cuenta global?
Una cuenta que contiene todos los registros de usuario. Causa límites de tamaño, explosiones de CU y fallos de paralelización.
¿Las PDAs cuentan como estado propiedad del programa?
Sí. Las PDAs son cuentas propiedad del programa que las deriva.
¿Cómo afecta la actualización al estado?
Las cuentas de estado persisten a través de las actualizaciones del programa. Solo cambia el bytecode.
¿Puedo migrar el estado a un nuevo diseño?
Sí, a través de realloc, nuevas cuentas o versionado de deserialización en los manejadores de instrucciones.
¿Quién paga para crear cuentas de estado?
El payer en init - el usuario o el protocolo que subsidia.
¿Es propiedad del programa lo mismo que propiedad del usuario?
El programa posee la cuenta; el usuario la controla mediante comprobaciones de firma en las instrucciones que la mutan.
¿Cómo encuentran los indexadores todas las cuentas de un programa?
RPC getProgramAccounts con filtros memcmp en los discriminadores.
¿Pueden las cuentas de estado contener SOL?
Sí - campo lamports. Común en patrones de depósito en garantía y bóvedas.
¿Qué es InitSpace en Anchor 0.32.1?
Macro de derivación que calcula el tamaño serializado de Borsh para el atributo space.
Relacionado
- Cuentas del Sistema vs. Cuentas de Programa - tipos de cuenta
- Cuentas de Datos y Diseño - serialización
- Creación y Cierre de Cuentas - ciclo de vida
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.