Firmar con PDAs
Las PDAs firman las CPIs a través de slices de semillas pasados a CpiContext::new_with_signer. Las semillas deben coincidir con las restricciones de PDA utilizadas en la inicialización.
Busca en todas las páginas de la documentación
Las PDAs firman las CPIs a través de slices de semillas pasados a CpiContext::new_with_signer. Las semillas deben coincidir con las restricciones de PDA utilizadas en la inicialización.
let seeds: &[&[u8]] = &[b"vault", &[ctx.bumps.vault]];
let signer: &[&[&[u8]]] = &[seeds];
token_interface::transfer_checked(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
accounts,
signer,
),
amount,
decimals,
)?;invoke_signed y new_with_signer ambos toman &[&[&[u8]]]: el slice exterior contiene un grupo de semillas por PDA, y cada grupo es la lista de semillas para esa PDA terminando en su byte de bump.
Cuándo usar esto: Una PDA es la autoridad sobre cuentas de tokens o posee SOL que se está transfiriendo.
use anchor_lang::prelude::*;
use anchor_spl::token_interface::{
self, Mint, TokenAccount, TokenInterface, TransferChecked,
};
#[account]
pub struct Vault {
pub authority: Pubkey,
pub bump: u8,
}
#[derive(Accounts)]
pub struct VaultWithdraw<'info> {
#[account(seeds = [b"vault", vault.authority.as_ref()], bump = vault.bump)]
pub vault: Account<'info, Vault>,
#[account(mut, token::mint = mint, token::authority = vault)]
pub vault_ata: InterfaceAccount<'info, TokenAccount>,
#[account(mut, token::mint = mint)]
pub user_ata: InterfaceAccount<'info, TokenAccount>,
pub mint: InterfaceAccount<'info, Mint>,
pub token_program: Interface<'info, TokenInterface>,
}
pub fn vault_withdraw(ctx: Context<VaultWithdraw>, amount: u64) -> Result<()> {
// Vincula la Pubkey a una variable local primero. `ctx.accounts.vault.authority` es
// una Pubkey por valor; llamar a `.as_ref()` en una temporal dentro del array de semillas
// es un error de préstamo E0716.
let authority_key = ctx.accounts.vault.authority;
let bump = ctx.accounts.vault.bump;
let seeds: &[&[u8]] = &[b"vault", authority_key.as_ref(), &[bump]];
let signer_seeds: &[&[&[u8]]] = &[seeds];
token_interface::transfer_checked(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
TransferChecked {
from: ctx.accounts.vault_ata.to_account_info(),
mint: ctx.accounts.mint.to_account_info(),
to: ctx.accounts.user_ata.to_account_info(),
authority: ctx.accounts.vault.to_account_info(),
},
signer_seeds,
),
amount,
ctx.accounts.mint.decimals,
)?;
Ok(())
}Lo que esto demuestra:
&[&[&[u8]]] - un grupo interno por PDAPubkey a un let antes de .as_ref() en el array de semillastransfer_checked / TransferChecked - anchor_spl::token::{transfer, Transfer} están obsoletos, y el spl_token::instruction::transfer que construyen rechaza directamente el ID del programa Token-2022invoke_signed vs CPI de AnchorLos ayudantes de CPI de Anchor aceptan with_signer para la misma semántica que invoke_signed nativo.
Incluye cada elemento de semilla en el mismo orden utilizado en #[account(seeds = [...])].
ctx.bumps.| 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 |
0.32.1 en toda esta sección.
No. Usa restricciones de semillas y firma de CPI.
En el campo de la estructura de tu cuenta, establecido en la inicialización.
Usa @solana/kit 7.0.0 con bytes de semilla coincidentes.
La dirección declare_id de tu programa Anchor.
No en el array de semillas; el bump es un parámetro separado para find_program_address.
Compara las claves registradas; verifica las semillas y el ID del programa en el lado del cliente.
Sí, cuando contienen datos; fináncialas con el pagador en la inicialización.
Sí, con las mismas semillas de firmante para cada CPI.
Consulta los enlaces relacionados para temas más profundos sobre la firma de PDAs.
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.
Revisado por Chris St. John·Última actualización: 16 jul 2026