Client-Side PDA Derivation
Clients must derive the same PDAs as on-chain seeds constraints. Use identical seed bytes, order, and program ID.
Recipe
import { getProgramDerivedAddress } from "@solana/kit";
const [pda] = await getProgramDerivedAddress({
programAddress: programId,
seeds: ["escrow", addressEncoder.encode(maker), u64ToLeBytes(escrowId)],
});When to reach for this: Building transactions that pass PDA accounts to Anchor instructions.
Working Example
import { address, getProgramDerivedAddress, getBytesEncoder } from "@solana/kit";
const programId = address("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
const [escrowPda] = await getProgramDerivedAddress({
programAddress: programId,
seeds: [
new TextEncoder().encode("escrow"),
getBytesEncoder().encode(makerAddress),
new Uint8Array(new BigUint64Array([BigInt(escrowId)]).buffer),
],
});
// Pass escrowPda in accounts map matching IDL name "escrow"What this demonstrates:
- Seed strings must match
b"escrow"bytes - Pubkeys encoded as 32 raw bytes
- Integers use same endianness as Rust
to_le_bytes - Program ID is the Anchor program address
Deep Dive
@solana/kit 7.0.0
Use kit helpers for PDA derivation and address encoding. Share seed builders between program (Rust constants) and client (TS module) via codegen or documented constants.
Testing
Assert client PDA matches anchor test fixtures for every seed variant.
Gotchas
- UTF-8 string vs b-string mismatch - Different seeds.. Fix: Use exact ASCII seed literals.
- Big-endian integer seeds - Rust uses le on-chain.. Fix: Match
to_le_bytes. - Wrong program ID in client - Derives wrong address.. Fix: Read from IDL metadata or Anchor.toml.
- Optional bump passed incorrectly - Usually omit; program validates.. Fix: Let on-chain constraint verify bump.
- Base58 vs bytes confusion - Encoding errors.. Fix: Use kit address types.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Anchor TS client .pda helpers | Legacy @coral-xyz/anchor | Greenfield @solana/kit |
| Codama-generated PDAs | From IDL seeds metadata | Hand-rolled seed math |
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 client pda topics.
Related
- PDAs in Anchor - on-chain seeds
- Generating TS Clients - client codegen
- PDA Derivation - native derivation
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.