#[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
#[account] macro adds an 8-byte discriminator at the start, so when calculating space:
InitSpace Derive Macro
For easier space calculation, use theInitSpace derive macro:
Account Initialization
To create and initialize a new account, use theinit constraint:
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
payerandspaceparameters - Requires
system_programin the accounts struct
Initialization with PDA
For PDA (Program Derived Address) accounts:Reading Account Data
Access account data through theContext:
Updating Account Data
To modify account data, mark the account asmut (mutable):
Account Constraints
Use constraints to enforce security and business logic:has_one Constraint
Verifies that an account field matches a provided account:my_account.authority == authority.key().
constraint Constraint
Custom validation logic:Closing Accounts
Reclaim SOL rent by closing accounts with theclose constraint:
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 withrealloc:
Complete Example
Here’s a complete counter program demonstrating state management:init_if_needed
For accounts that might already exist, useinit_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
- Use InitSpace: Leverage
#[derive(InitSpace)]for automatic space calculation - Validate ownership: Always use
has_oneor other constraints to verify account relationships - Check arithmetic: Use checked math operations to prevent overflows
- Close unused accounts: Reclaim rent by closing accounts when done
- Use PDAs for deterministic addresses: Prefer PDAs over keypair-based accounts
- Minimize account size: Only store necessary data to reduce rent costs