Field Note
Understanding Program Derived Addresses
One of the first puzzles in Solana is deciding where to store state. You cannot keep balances inside a smart contract the way you might on other chains. Instead, your program mutates accounts. But if the user is supposed to own an account, who owns an account that belongs to the program itself? The answer is a program derived address, or PDA.
The problem PDAs solve
Imagine a note-taking program. Every user needs a note account. If the user creates the note account themselves, they can delete it or transfer ownership. The program cannot stop them. What you want is an account that the program owns on behalf of the user, governed by rules written into the program.
A PDA is an address that looks like a normal public key but has no corresponding private key. That means no external wallet can sign for it. Only the program that derived it can authorize instructions that mutate it, because the runtime lets that program "sign" with the PDA for the duration of a call.
How derivation works
A PDA is found by hashing a set of seeds plus the program ID. The seeds are usually strings, public keys, counters, or other public data. The runtime tries a small extra byte called the bump to make sure the result does not fall on the ed25519 curve, which is where normal keypairs live. A PDA is therefore provably off-curve and has no private key.
Because the same seeds and program ID always produce the same address, you can compute a PDA ahead of time. This is useful for finding a user’s account, a token account, or a configuration account without storing a mapping somewhere else.
A simple example in code
Here is a Rust-like illustration. In practice you would use the Anchor or Solana Program Library helpers, but the logic is the same.
// Seeds: the literal "note", plus the user's public key
let seeds = &[b"note", user_pubkey.as_ref(), &[bump]];
let (expected_pda, bump) = Pubkey::find_program_address(seeds, program_id);
The program later passes the same seeds and bump to an instruction. The runtime verifies that the provided account matches the derived address and that the program is allowed to sign for it. If the seeds do not match, the instruction fails.
Why this matters
PDAs let programs keep deterministic, tamper-resistant state. They are used for token accounts, escrow accounts, metadata records, authority accounts, and game inventories. Whenever you see an account that is owned by a program but represents a user, it is probably a PDA.
Common mistakes
- Using the wrong bump seed or recomputing the bump inside an instruction instead of passing it in.
- Putting sensitive data in seeds, because seeds are public and visible on-chain.
- Assuming a PDA is automatically safe. The program still has to validate ownership and constraints before writing.
Next
Try tracing a PDA on the explorer. Find a token account owned by the Token Program and look at its address and mint. Then read the Token Program source to see how it derives associated token accounts from the wallet and mint seeds.