PDA Basics
10 examples to get you started with PDAs - 7 basic and 3 intermediate.
Prerequisites
- Understand Pubkeys.
- Solana CLI 3.0.10 for
solana addresshelpers off-chain.
Basic Examples
1. Derive off-chain
Clients find PDA + bump before building transactions.
use solana_program::pubkey::Pubkey;
let program_id = Pubkey::new_unique();
let (pda, bump) = Pubkey::find_program_address(&[b"vault"], &program_id);find_program_addresstries bumps 255..0.- PDA is off-curve - no private key exists.
- Same seeds + program id always yield same address.
2. PDA as account key
Transactions reference the PDA pubkey like any account.
// AccountMeta::new(pda, false) - PDA is not a wallet signer- Wallets sign; PDAs sign only via program
invoke_signed. - PDAs can own token accounts and data accounts.
- Store bump in account data for cheaper re-derivation.
3. Seed slices
Seeds are byte strings - pubkeys, strings, numbers.
let seeds: &[&[u8]] = &[b"escrow", buyer.as_ref(), seller.as_ref()];- Max seed length rules apply per seed.
- Use constant prefixes to namespace PDAs.
- Document seed order for clients.
4. Program id in derivation
Changing program id changes PDA address.
Pubkey::find_program_address(seeds, program_id);- Upgrade deployments keep program id.
- Cross-program PDAs use callee program id.
- Never hardcode PDAs - always derive.
5. PDA cannot sign tx
End users never hold PDA private keys.
// Only the owning program can sign for PDA via invoke_signed- Security model: program logic is authority.
- Compromised program = compromised PDAs.
- Keep program upgrade authority secure.
6. On-curve vs off-curve
PDAs are intentionally off ed25519 curve.
// Off-chain: Pubkey::create_program_address validates bumpcreate_program_addressfails if on-curve.find_program_addresssearches valid bump.- Explorers still show PDA as normal address.
7. Single program authority
Only the deriving program can sign for PDA.
invoke_signed(&ix, accounts, &[&[b"vault", &[bump]]])?;- Seeds must match derivation.
- Wrong program id cannot sign.
- CPI passes signer seeds to runtime.
Intermediate Examples
8. Store canonical bump
Save bump at init to avoid search on-chain.
pub struct Vault { pub bump: u8, pub total: u64 }- Verify
seeds[bump] == stored_bumpon each ix. - Prevents bump hunting griefing.
- See Canonical Bumps page.
Related: Canonical Bumps - bump storage
9. PDA-owned token account
Token accounts can use PDA as authority.
// seeds: [b"token", mint.key.as_ref(), owner.as_ref()]- Program signs token transfers with
invoke_signed. - Validate mint and owner in seeds.
- Common vault pattern.
Related: PDAs as Authorities - vaults
10. Collision awareness
Different seed sets should not collide semantically.
const PREFIX_VAULT: &[u8] = b"vault";
const PREFIX_ESCROW: &[u8] = b"escrow";- Use distinct static prefixes.
- Include pubkeys for per-user PDAs.
- Document seed grammar.
Related: Seed Design & Collisions - design rules
Stack versions: This page was written for 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, and LiteSVM 0.6.x.