Skip to content

4. Accounts and state

Consensus gives validators one accepted history. What does that history contain? Primarily, changes to accounts. Account design is the center of Solana application architecture.

An account is a typed box only because your program says so

Section titled “An account is a typed box only because your program says so”

At runtime, account data is bytes. A program and its client agree that those bytes represent a profile, market, order, or configuration.

Conceptually, an account contains:

struct Account {
lamports: u64,
data: Vec<u8>,
owner: Pubkey,
executable: bool,
rent_epoch: u64,
}

lamports: how much native balance is attached?

Section titled “lamports: how much native balance is attached?”

Lamports can represent ordinary SOL holdings and fund the minimum balance associated with storing account data.

The owning program interprets these bytes according to a serialization format and version.

The runtime enforces owner-based rules. This owner is a program address, not necessarily the person who controls an application action.

Executable accounts represent programs rather than ordinary mutable application records.

rent_epoch: what storage-accounting metadata exists?

Section titled “rent_epoch: what storage-accounting metadata exists?”

Treat this as a runtime field. Do not build application logic on assumptions that are not guaranteed by current documentation.

Suppose a program stores one profile per wallet.

Profile PDA
version: 1
authority: Alice public key
display_name: "Alice"
created_slot: 123456

The PDA is derived from ['profile', Alice public key]. Alice can find the profile without a registry, and the program can verify the derivation.

To update the profile safely, the program checks:

  1. the profile address matches the expected PDA;
  2. the profile is owned by the Profile Program;
  3. the bytes represent the expected account type and version;
  4. the stored authority equals Alice’s signer address;
  5. the new name fits allocated space;
  6. the profile is writable.

Missing one check may allow account substitution or unauthorized updates.

  • A system account commonly holds SOL and may correspond to a wallet public key.
  • A data account stores application state.
  • A program account contains executable code.
  • A sysvar exposes selected cluster information to programs.
  • A mint account describes a token.
  • A token account records a balance for one mint.
  • A stake account stores staking state.

The word “account” therefore does not mean “user login.” It means an addressable piece of on-chain state.

TermThe question it answers
Owner programWhich program may modify base account data?
AuthorityWhich address does the application trust for an action?
SignerWhich required private-key authorization is present?
Fee payerWhich signer pays the transaction fee?
WritableMay this transaction modify the account?

On-chain data is not an unlimited JSON document. Allocate space for fixed fields and bounded variable fields.

space = discriminator + version + fixed fields + bounded content

Avoid unbounded vectors and strings. If data grows indefinitely, consider pages, one PDA per record, bounded queues, or off-chain content with an on-chain integrity reference.

Fetch the current minimum balance for the exact account size rather than hard-coding it.

A durable account should identify its type and version. A simple layout might be:

[discriminator][version][authority][application fields]

Deployed data can outlive one program release. Plan how version 1 becomes version 2:

  • retain backward-compatible readers;
  • add bounded optional fields;
  • reallocate with correct authority and funding;
  • or create a new account and migrate safely.

The runtime locks writable accounts during transaction execution.

Alice update -> writes AliceProfile
Bob update -> writes BobProfile

These operations do not conflict. If every update also writes one global counter, that counter becomes a shared lock and reduces concurrency.

A useful design rule is:

Keep shared configuration read-only on common paths, and partition mutable state when application invariants allow it.

Partitioning is not free. It can complicate queries and atomic cross-record operations. Design from actual read/write patterns, not from a slogan.

An application may allow unused accounts to be closed and remaining lamports returned. A safe close instruction validates the authority and destination, invalidates the old state, and prevents unsafe reuse.

Cleanup is not an afterthought. An unauthorized close path can be as damaging as an unauthorized transfer.

Before trusting an account, ask:

  • Is the address expected?
  • Is the owner program expected?
  • Is the account the expected type and version?
  • Is the PDA derivation correct?
  • Is the required authority the actual signer?
  • Are mint and token authority fields correct?
  • Is writability necessary?
  • Could two inputs point to the same account unexpectedly?
  • Is the account initialized, active, and large enough?

Draw a box for a Profile Program and two boxes for AliceProfile and BobProfile. Put logic in the program box and state in the profile boxes. Then explain why the two updates can run concurrently.

Next, learn how a transaction carries those account boxes into program execution in Transactions and runtime.