Assinatura com PDAs
PDAs assinam CPIs por meio de fatias de seeds passadas para CpiContext::new_with_signer. As seeds devem corresponder às constraints de PDA usadas na inicialização.
Busque em todas as páginas da documentação
PDAs assinam CPIs por meio de fatias de seeds passadas para CpiContext::new_with_signer. As seeds devem corresponder às constraints de PDA usadas na inicialização.
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 e new_with_signer ambos aceitam &[&[&[u8]]]: a fatia externa contém um grupo de seeds por PDA, e cada grupo é a lista de seeds para aquela PDA terminando em seu byte de bump.
Quando usar isso: Uma PDA tem autoridade sobre contas de token ou possui SOL sendo transferido.
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 a Pubkey a uma variável local primeiro. `ctx.accounts.vault.authority` é uma
// Pubkey por valor; chamar `.as_ref()` em um temporário dentro do array de seeds
// resulta em um erro de empréstimo 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(())
}O que isso demonstra:
&[&[&[u8]]] - um grupo interno por PDAPubkey a um let antes de .as_ref() no array de seedstransfer_checked / TransferChecked - anchor_spl::token::{transfer, Transfer} estão obsoletos, e o spl_token::instruction::transfer que eles constroem rejeita o ID do programa Token-2022 completamenteOs helpers de CPI do Anchor aceitam with_signer com a mesma semântica do invoke_signed nativo.
Inclua cada elemento de seed na mesma ordem usada em #[account(seeds = [...])].
ctx.bumps.| Alternativa | Use quando | Não use quando |
|---|---|---|
| Delegar a um signer humano | O usuário paga as taxas diretamente | Custódia pelo programa |
| Padrões de PDA com multisig | Autoridade compartilhada | PDA de seed única |
0.32.1 em toda esta seção.
Não. Use constraints de seeds e assinatura em CPI.
No campo da sua struct de conta, definido na inicialização.
Use @solana/kit 7.0.0 com bytes de seed correspondentes.
O endereço declare_id do seu programa Anchor.
Não no array de seeds; o bump é um parâmetro separado para find_program_address.
Compare as chaves logadas; verifique seeds e program id no lado do cliente.
Sim, quando mantêm dados; financie com o pagador na inicialização.
Sim, com as mesmas signer seeds para cada CPI.
Veja os links Relacionados para tópicos mais profundos sobre assinatura de PDA.
Versões da stack: Esta página foi 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 e LiteSVM 0.6.x.
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026