init & init_if_needed
init creates a new account in the same instruction. init_if_needed creates only when missing, which introduces reinitialization risk if constraints are weak.
Recipe
#[account(
init,
payer = payer,
space = 8 + MyAccount::INIT_SPACE,
seeds = [b"vault", user.key().as_ref()],
bump
)]
pub vault: Account<'info, Vault>,When to reach for this: An instruction must allocate program-owned state for the first time.
Working Example
#[derive(Accounts)]
pub struct CreateProfile<'info> {
#[account(
init,
payer = user,
space = 8 + Profile::INIT_SPACE,
seeds = [b"profile", user.key().as_ref()],
bump
)]
pub profile: Account<'info, Profile>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
// init_if_needed: only when idempotent and safe
#[account(
init_if_needed,
payer = payer,
space = 8 + Stats::INIT_SPACE,
seeds = [b"stats"],
bump,
)]
pub stats: Account<'info, Stats>,What this demonstrates:
initfails if account already existspayerfunds rent-exempt lamportsspaceincludes 8-byte discriminatorinit_if_neededcan skip creation when account exists
Deep Dive
init Requirements
- Payer must be mutable signer with SOL.
- System program required for allocate/assign.
- Space must fit serialized account size.
init_if_needed Risk
If existing account data is not validated, attacker passes uninitialized garbage that passes weak checks. Prefer init or validate discriminator and authority on every path.
Gotchas
- init without space - Compile error or wrong rent.. Fix: Use
INIT_SPACEorspace = 8 + .... - init_if_needed on user-owned data - Reinit attacks possible.. Fix: Use
initor strictconstrainton existing state. - Forgot system_program - Account creation CPI fails.. Fix: Include
Program<System>. - Payer not mut - Rent transfer fails.. Fix: Mark payer
#[account(mut)]. - Double init same seeds - Second call fails; plan idempotent flow.. Fix: Use
init_if_neededonly with strong guards.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Separate create instruction | Clear lifecycle | One-shot setup UX |
| CPI to system program manually | Native programs | Anchor init macros |
| init_if_needed + constraint | Idempotent onboarding | High-security vaults |
FAQs
What Anchor version does this site use?
0.32.1 for anchor-lang, Anchor CLI, and examples in this section.
Do I need Solana CLI alongside Anchor?
Yes. Solana CLI 3.0.10 handles keypairs, airdrops, and solana program inspection.
Where does the IDL live after build?
target/idl/<program>.json in your workspace.
Can I mix UncheckedAccount with Signer?
Yes, but every unchecked field needs explicit constraints or handler checks.
How do I test without devnet?
Use anchor test with Surfpool 0.12.0 or LiteSVM 0.6.x in CI.
What is the 8-byte prefix on account data?
Anchor account discriminator; do not strip it when sizing space.
Should I commit generated IDL?
Yes, or publish on-chain IDL so clients have a canonical source.
How do I debug constraint failures?
Run with logs; Anchor prints constraint name and account index.
Does Anchor work on Agave 4.1.1?
Yes. This stack targets Agave validators with Solana CLI 3.0.10.
What should I read next in this section?
See sibling articles linked in Related for deeper init topics.
Related
- Space & Rent - sizing
- Seeds & Bump Constraints - PDA init
- Account Initialization Patterns - design patterns
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.