PDAs in Anchor
Program Derived Addresses have no private key; your program signs for them with seeds and bump via invoke_signed or CPI with_signer.
Recipe
#[account(
seeds = [b"escrow", maker.key().as_ref()],
bump,
)]
pub escrow: Account<'info, Escrow>,When to reach for this: Program must custody assets or store canonical state at a deterministic address.
Working Example
#[derive(Accounts)]
pub struct InitEscrow<'info> {
#[account(
init,
payer = maker,
space = 8 + Escrow::INIT_SPACE,
seeds = [b"escrow", maker.key().as_ref(), escrow_id.to_le_bytes().as_ref()],
bump,
)]
pub escrow: Account<'info, Escrow>,
#[account(mut)]
pub maker: Signer<'info>,
pub system_program: Program<'info, System>,
}What this demonstrates:
- Seeds + program id determine PDA address
bumpfinds canonical off-curve bump- Store bump in account for cheaper later checks
- PDAs cannot be Signer type
Deep Dive
Derivation
Pubkey::find_program_address(seeds, program_id) returns (address, bump). Anchor wraps this in constraints.
Program Signing
Only the program that owns the PDA seeds can sign unless using external program id in seeds (advanced).
Gotchas
- Off-curve bump not stored - Extra CU each CPI.. Fix: Persist bump in state.
- Seed length limits - Max seed data constraints.. Fix: Keep seeds compact.
- Using system-owned PDA pattern wrong - Assign to program after create.. Fix: Use Anchor init on PDA.
- Client/server seed mismatch - Transaction fails.. Fix: Share seed builder in SDK.
- PDA as Signer<'info> - Will not compile / wrong model.. Fix: Use Account with seeds constraint.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Keypair-owned accounts | External signing needed | Program custody |
| seeds::program for external PDAs | CPI to foreign program PDAs | Own program state |
FAQs
What Anchor version is assumed?
0.32.1 throughout this section.
Can a PDA be a Signer in accounts struct?
No. Use seeds constraints and CPI signing.
Where is bump stored?
In your account struct field, set at initialization.
How do clients derive PDAs?
Use @solana/kit 7.0.0 with matching seed bytes.
What program id is used for PDAs?
Your Anchor program's declare_id address.
Do seeds include bump?
Not in seeds array; bump is separate parameter to find_program_address.
How to debug PDA failures?
Compare logged keys; verify seeds and program id client-side.
Are PDAs rent-exempt?
Yes when holding data; fund with payer on init.
Can one PDA sign multiple CPIs in one ix?
Yes with same signer seeds for each CPI.
What to read next?
See Related links for deeper pdas topics.
Related
- Signing with PDAs - CPI signing
- Seeds & Bump Constraints - validation
- PDA Basics - native model
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.