PDA
PDAs (Program Derived Addresses)¶
PDAs (Program Derived Addresses) are a special type of account address in Solana that are generated deterministically from a program ID and a set of seeds, without requiring a corresponding private key. PDAs are one of the most important concepts in Solana smart contract development, used to create accounts that can be controlled by programs.
Core Features¶
1. Deterministic Generation PDA addresses are deterministically generated using the following formula:
Where: - seeds: A set of byte arrays, which can be strings, public keys, numbers, etc. - program_id: The program's address - bump: A value from 0-255, used to ensure the generated address does not lie on the Ed25519 curve
2. No Private Key PDA addresses are deliberately designed to not lie on the Ed25519 elliptic curve, meaning no corresponding private key exists. Therefore, only the program that derived it can sign on behalf of this address.
3. Program Signing When a program needs to perform operations as a PDA, it can prove ownership by providing the correct seeds and bump, and the runtime will automatically sign for the PDA.
Use Cases¶
1. User Data Storage Creating an independent data account for each user:
let (pda, bump) = Pubkey::find_program_address(
&[
b"user_data",
user_pubkey.as_ref(),
],
program_id
);
2. Token Account Management Creating program-controlled token accounts for asset custody:
let (vault_pda, bump) = Pubkey::find_program_address(
&[
b"vault",
pool_id.as_ref(),
],
program_id
);
3. Authority Management Serving as a program's authorized account to perform operations requiring signatures (such as transfers, minting tokens).
4. Decentralized Application State Storing application-level state data, such as liquidity pools, staking pools, etc.
Generation Methods¶
Finding a PDA
use solana_program::pubkey::Pubkey;
let (pda, bump_seed) = Pubkey::find_program_address(
&[b"my_seed", other_key.as_ref()],
&program_id
);
find_program_address automatically decrements from 255, trying different bump values until it finds an address that is not on the curve. The returned bump_seed should be saved for subsequent use.
Signing with a PDA Within a program, you can use invoke_signed to have the PDA sign:
invoke_signed(
&transfer_instruction,
&[source_account, dest_account, pda_account],
&[&[b"vault", pool_id.as_ref(), &[bump]]], // PDA signer seeds
)?;
Best Practices¶
1. Use Meaningful Seeds Choose seeds that clearly express relationships:
2. Cache the Bump Value Store the bump value in the account data to avoid recalculating it each time:
3. Avoid Collisions Use multiple seed combinations to ensure PDAs for different purposes do not collide:
// User config
&[b"config", user_pubkey.as_ref()]
// User balance
&[b"balance", user_pubkey.as_ref()]
4. Verify PDA Derivation Always verify within your program that the passed-in PDA was correctly derived:
let (expected_pda, bump) = Pubkey::find_program_address(
&[b"vault", pool_id.as_ref()],
ctx.program_id
);
require!(expected_pda == *vault.key, ErrorCode::InvalidPDA);
PDAs in the Anchor Framework¶
Anchor provides simplified PDA operations:
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(
init,
payer = user,
space = 8 + UserData::SIZE,
seeds = [b"user_data", user.key().as_ref()],
bump
)]
pub user_data: Account<'info, UserData>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
Anchor automatically: - Finds the correct bump value - Verifies PDA derivation - Stores the bump in the account
Comparison with Regular Accounts¶
| Feature | PDA | Regular Account |
|---|---|---|
| Private Key | None | Exists |
| Signing Method | Program signing | Private key signing |
| Address Generation | Deterministic | Random |
| Ownership | Program | Private key holder |
| Purpose | Program-controlled assets and data | User-controlled assets |
Related Concepts¶
- CPI (Cross-Program Invocation): PDAs are commonly used in CPI for program signing
- Seeds: Input parameters used to derive PDAs
- Bump Seed: An adjustment value ensuring the PDA is not on the curve
- Associated Token Account: A special type of PDA used for user token accounts