Skip to main content
State management in Anchor programs involves creating, reading, updating, and closing accounts that store your program’s data. Anchor provides powerful abstractions through the #[account] macro and account constraints to make state management safe and ergonomic.

The #[account] Macro

The #[account] macro is applied to structs to create custom account types for your program. It automatically implements several traits:
  • Owner: Sets the account owner to the program ID (from declare_id!)
  • AccountSerialize and AccountDeserialize: Handles serialization
  • Discriminator: Adds an 8-byte discriminator to distinguish account types

Basic Account Definition

The #[account] macro adds an 8-byte discriminator at the start, so when calculating space:

InitSpace Derive Macro

For easier space calculation, use the InitSpace derive macro:

Account Initialization

To create and initialize a new account, use the init constraint:
The init constraint:
  • Creates the account via CPI to the system program
  • Allocates the specified space
  • Assigns ownership to your program
  • Sets the account discriminator
  • Requires payer and space parameters
  • Requires system_program in the accounts struct

Initialization with PDA

For PDA (Program Derived Address) accounts:

Reading Account Data

Access account data through the Context:

Updating Account Data

To modify account data, mark the account as mut (mutable):
Anchor automatically serializes the account data back to the account when the instruction completes successfully.

Account Constraints

Use constraints to enforce security and business logic:

has_one Constraint

Verifies that an account field matches a provided account:
This checks that my_account.authority == authority.key().

constraint Constraint

Custom validation logic:

Closing Accounts

Reclaim SOL rent by closing accounts with the close constraint:
The close constraint:
  • Transfers all lamports to the specified account
  • Zeroes out the account data
  • Marks the account for garbage collection

Reallocating Accounts

Increase or decrease account size with realloc:

Complete Example

Here’s a complete counter program demonstrating state management:

init_if_needed

For accounts that might already exist, use init_if_needed:
Be careful with init_if_needed as it can introduce security vulnerabilities if not used properly. Always validate the account state after initialization.

Best Practices

  1. Use InitSpace: Leverage #[derive(InitSpace)] for automatic space calculation
  2. Validate ownership: Always use has_one or other constraints to verify account relationships
  3. Check arithmetic: Use checked math operations to prevent overflows
  4. Close unused accounts: Reclaim rent by closing accounts when done
  5. Use PDAs for deterministic addresses: Prefer PDAs over keypair-based accounts
  6. Minimize account size: Only store necessary data to reduce rent costs