Os padrões avançados de PDA que separaram engenheiros juniores de séniores em Anchor: canonical bump, seed collisions, cross-program PDAs e armadilhas que a documentação não menciona.
Disley Souza
14 de maio de 2026
PDAs (Program Derived Addresses) são o conceito que mais divide engenheiros Solana. Todo mundo entende a definição básica. Poucos entendem as implicações de bump, seed design e o que acontece quando você usa PDAs de forma errada. Este artigo cobre o que a documentação oficial não explica — porque aprendi na marra.
PDAs são endereços gerados deterministicamente a partir de seeds + program ID. Não têm private key — nenhuma carteira os controla. Apenas o programa que os criou pode assinar transações por eles. Isso parece simples até você encontrar os casos de borda.
// O básico — seed simples com string e pubkey
#[derive(Accounts)]
pub struct CreateUserAccount<'info> {
#[account(
init,
payer = user,
space = 8 + UserAccount::INIT_SPACE,
seeds = [b"user", user.key().as_ref()],
bump,
)]
pub user_account: Account<'info, UserAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}Quando você usa `bump` sem especificar o valor, Anchor encontra o "canonical bump" — o maior valor de bump (começando de 255) que produz um endereço fora da curva ed25519. Isso é caro: `find_program_address` pode tentar até 256 combinações.
Chamar `find_program_address` a cada instrução que acessa uma PDA custa compute units desnecessários. Armazene o canonical bump na conta e use `create_program_address` nas instruções subsequentes.
#[account]
pub struct UserAccount {
pub owner: Pubkey,
pub bump: u8, // Armazenar o canonical bump aqui
// outros campos...
}
// Na instrução de criação — bump é encontrado e armazenado
pub fn create_user(ctx: Context<CreateUser>) -> Result<()> {
let account = &mut ctx.accounts.user_account;
account.owner = ctx.accounts.user.key();
account.bump = ctx.bumps.user_account; // Anchor popula isso automaticamente
Ok(())
}
// Em instruções subsequentes — usar o bump armazenado
#[derive(Accounts)]
pub struct UpdateUser<'info> {
#[account(
mut,
seeds = [b"user", user.key().as_ref()],
bump = user_account.bump, // Usa bump armazenado — sem find_program_address
)]
pub user_account: Account<'info, UserAccount>,
pub user: Signer<'info>,
}Seeds são concatenados antes de derivar o endereço. Seeds `[b"ab", b"c"]` e `[b"a", b"bc"]` produzem o mesmo endereço. Isso cria uma vulnerabilidade de seed collision onde um atacante pode derivar um endereço que você não esperava.
// VULNERÁVEL — seed collision possível
seeds = [user.key().as_ref(), b"config"]
// Um atacante com chave pública que começa com os bytes de "config" pode criar ambiguidade
// SEGURO — separadores explícitos ou seeds com tamanho fixo
seeds = [b"config", b"v1", user.key().as_ref()]
// Pubkeys têm 32 bytes fixos — não podem ser confundidas com stringsSeu programa pode invocar outro programa passando uma PDA como signer — mas apenas se a PDA foi derivada do seu program ID. Você não pode assinar por PDAs de outros programas. Isso parece óbvio mas tem implicações em arquiteturas compostas.
// Assinando por uma PDA via invoke_signed
let signer_seeds: &[&[&[u8]]] = &[&[
b"vault",
ctx.accounts.user.key().as_ref(),
&[ctx.accounts.vault.bump],
]];
// A PDA assina a instrução CPI
invoke_signed(
&transfer_instruction,
&[
ctx.accounts.vault.to_account_info(),
ctx.accounts.destination.to_account_info(),
ctx.accounts.token_program.to_account_info(),
],
signer_seeds, // Apenas funciona se vault foi derivada deste program ID
)?;O caso de uso mais elegante de PDAs é como escrow determinístico: uma vault que só o programa controla, cujo endereço qualquer um pode derivar independentemente, sem precisar de estado externo. É a base de protocolos de swap, staking e liquidez.
PDAs são o conceito que mais confunde engenheiros vindos de Ethereum — não há `msg.sender`, não há estado de quem está chamando baked-in. Mas quando você entende PDAs profundamente, a arquitetura de programas Solana fica elegante: todo estado é uma conta derivada deterministicamente, toda autoridade é verificável on-chain sem oracle externo.
Próximo passo
Auditoria de segurança, arquitetura de sistemas blockchain, integração Solana — trabalhamos com equipes que não aceitam o suficiente.