Instruction Ordering & Dependencies
Clients compose multiple Anchor instructions in one transaction. Order matters when later instructions depend on accounts created or mutated by earlier ones.
Recipe
1. create_user (init PDA)
2. create_ata (init token account)
3. deposit (transfer tokens)When to reach for this: You design client flows or document integration steps.
Working Example
// Client sends ordered instructions in one transaction
const tx = await program.methods
.createUser()
.accounts({ /* ... */ })
.preInstructions([/* compute budget if needed */])
.rpc();
// Or batch:
// tx = new Transaction().add(ix1, ix2, ix3);// Program: deposit assumes user PDA already initialized
#[account(
seeds = [b"user", owner.key().as_ref()],
bump = user.bump,
constraint = user.initialized @ UserError::NotInitialized,
)]
pub user: Account<'info, UserState>,What this demonstrates:
- Init must precede use in same or prior tx
- Atomic batches succeed or fail together
- Idempotent instructions simplify retries
- Document required ordering in IDL docs
Deep Dive
Dependency Patterns
- Setup + action - Separate create and operate instructions.
- Same-tx init - Multiple instructions; account from ix0 visible in ix1.
- Prefetch - Client reads PDAs off-chain, passes to later ix.
Use explicit errors when prerequisites missing instead of panics.
Gotchas
- Deposit before init - Account missing.. Fix: Return
NotInitializederror. - Assuming cross-tx atomicity - Only single tx is atomic.. Fix: Handle partial completion in UI.
- Blockhash expiry on long batches - Retry with fresh blockhash.. Fix: Keep batches lean.
- Duplicate init in retry - Second tx fails.. Fix: Use idempotent design or check existence client-side.
- Wrong account mutability in batch - Later ix fails.. Fix: Mark accounts mut in all instructions that write.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Single instruction does all | Simpler client | Higher CU per call |
| init_if_needed | Fewer client ix | Weaker safety if misused |
| Versioned txs with ALTs | Many accounts | Simple legacy txs |
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 ordering topics.
Related
- init & init_if_needed - creation
- Transaction Assembly - client txs
- Building Transaction Messages - @solana/kit
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.