Firmar con PDAs
Las PDAs firman las CPIs a través de "seed slices" pasados a CpiContext::new_with_signer. Las semillas deben coincidir con las restricciones de PDA utilizadas en la inicialización.
Receta
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,
)?;Cuándo usar esto: Una PDA es la autoridad sobre cuentas de tokens o posee SOL que se está transfiriendo.
Ejemplo de Trabajo
pub fn vault_withdraw(ctx: Context<VaultWithdraw>, amount: u64) -> Result<()> {
let bump = ctx.bumps.vault;
let authority_key = ctx.accounts.vault.authority;
let seeds: &[&[u8]] = &[b"vault", authority_key.as_ref(), &[bump]];
let signer_seeds = &[seeds];
token::transfer(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
token::Transfer {
from: ctx.accounts.vault_ata.to_account_info(),
to: ctx.accounts.user_ata.to_account_info(),
authority: ctx.accounts.vault.to_account_info(),
},
signer_seeds,
),
amount,
)?;
Ok(())
}Lo que esto demuestra:
- Las semillas del firmante son "slices" anidados
&[&[&[u8]]] - El "bump" debe ser canónico
- La información de la cuenta de la PDA es la metadato de autoridad
- Usa
ctx.bumpscuando las semillas coinciden con el nombre del campo
Análisis Profundo
invoke_signed vs CPI de Anchor
Los ayudantes de CPI de Anchor aceptan with_signer para la misma semántica que invoke_signed nativo.
PDAs Multi-semilla
Incluye cada elemento de semilla en el mismo orden utilizado en #[account(seeds = [...])].
Errores Comunes
- "Bump" incorrecto en el array de firmantes - La firma de la CPI falla. Solución: Usa el "bump" almacenado o
ctx.bumps. - Componente de semilla faltante - Desajuste de la dirección derivada. Solución: Copia las semillas de la restricción exactamente.
- Metadato de autoridad no es una cuenta PDA - El programa de tokens rechaza. Solución: Pasa la PDA de la bóveda como autoridad.
- Reutilización incorrecta del buffer de semillas - Problemas de tiempo de vida/préstamo. Solución: Construye el array de semillas por llamada a CPI.
- La PDA firma sin restricción en la cuenta - El atacante sustituye la PDA. Solución: Valida las semillas en la cadena.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Delegar a un firmante humano | El usuario paga las tarifas directamente | Custodia del programa |
| Patrones de PDA Multi-firma | Autoridad compartida | PDA de semilla única |
Preguntas Frecuentes
¿Qué versión de Anchor se asume?
0.32.1 en toda esta sección.
¿Puede una PDA ser un Signer en la estructura de cuentas?
No. Usa restricciones de semillas y firma de CPI.
¿Dónde se almacena el bump?
En el campo de la estructura de tu cuenta, establecido en la inicialización.
¿Cómo derivan los clientes las PDAs?
Usa @solana/kit 7.0.0 con bytes de semilla coincidentes.
¿Qué ID de programa se usa para las PDAs?
La dirección declare_id de tu programa Anchor.
¿Las semillas incluyen el bump?
No en el array de semillas; el "bump" es un parámetro separado para find_program_address.
¿Cómo depurar fallos de PDA?
Compara las claves registradas; verifica las semillas y el ID del programa en el lado del cliente.
¿Las PDAs están exentas de alquiler?
Sí, cuando contienen datos; fináncialas con el pagador en la inicialización.
¿Puede una PDA firmar múltiples CPIs en una ix?
Sí, con las mismas semillas de firmante para cada CPI.
¿Qué leer a continuación?
Consulta los enlaces relacionados para temas más profundos sobre la firma de PDAs.
Relacionados
- CPIs Firmadas - Patrones de CPI
- Autoridades de PDA - Mints y bóvedas
- Almacenamiento de Bumps - Bump canónico
Versiones de Stack: 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.