Program Derived Addresses
Understanding PDAs on Solana
Program Derived Addresses (PDAs)
PDAs are one of Solana's most powerful features. They allow programs to deterministically derive addresses and sign transactions without a private key.
How PDAs Work
PDA = findProgramAddress([seed1, seed2, ...], programId)
= SHA256(seed1 + seed2 + ... + programId + bump) (mod curve order)
The key insight: PDAs are addresses that fall off the Ed25519 curve, meaning no private key exists for them. Only the program itself can sign for its PDAs.
Common PDA Patterns
// User-specific data account
let (pda, bump) = Pubkey::find_program_address(
&[b"user_data", user.key().as_ref()],
program_id
);
// Global state (singleton)
let (pda, bump) = Pubkey::find_program_address(
&[b"global_config"],
program_id
);
// Escrow between two parties
let (pda, bump) = Pubkey::find_program_address(
&[b"escrow", maker.key().as_ref(), taker.key().as_ref()],
program_id
);
Always store the bump seed in your account data. Recomputing it on every call wastes compute units.
PDAs as Signers
When a program uses invoke_signed, it provides the seeds + bump to prove it "owns" the PDA:
invoke_signed(
&transfer_ix,
&[pda_account, destination, system_program],
&[&[b"vault", &[bump_seed]]], // signer seeds
)?;
This is how programs can transfer SOL and tokens from PDA-owned accounts โ no private key needed.