Skip to content

Everything Is an Account

The part overview made a promise: Solana’s answer to “how do you build a single global state machine that runs at hardware speed” starts with an unusual data model. This page pays that promise off by defining the one and only shape state can take. Get this struct into your bones and the rest of the part — stateless programs, PDAs, the contrast with Ethereum — is just consequences.

Here is the claim, stated as bluntly as it deserves: the entire state of the Solana network is a single flat map from a 32-byte address to an account record. Not a tree of contracts. Not per-contract private storage. One dictionary. Every balance you own, every NFT, every program’s bytecode, every DeFi position on the chain is a value in that one map, looked up by its key. If you understand that map and the five fields of the record it stores, you understand where all of Solana’s state lives.

Most people arrive from a mental model where a smart contract is a little world with its own private storage inside it. Throw that away for a moment. In Solana there is exactly one global structure, and conceptually it is this:

Solana world state = Map<Pubkey, Account>
┌───────────────────────────── the one global map ─────────────────────────────┐
│ key (32-byte pubkey) value (account record) │
├──────────────────────────────────────────────────────────────────────────────┤
│ 9WzD…alice ──────────────► { lamports, owner, data, executable, rent } │ a wallet
│ 4Nd8…counter ──────────────► { lamports, owner, data, executable, rent } │ program state
│ Tokenkeg… ──────────────► { lamports, owner, data, executable, rent } │ a program (code)
│ Sysvar…clock ──────────────► { lamports, owner, data, executable, rent } │ runtime data
└──────────────────────────────────────────────────────────────────────────────┘

The key is a Pubkey: 32 bytes, usually rendered as a base58 string like 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM. Real keys are ed25519 public keys, but the map doesn’t care where the 32 bytes came from — it just uses them as a lookup index. There is no nesting. There is no contract-private storage hidden away where the runtime can’t see it. If a piece of state exists on Solana, it is a top-level entry in this map with its own address, and anyone can look it up.

That flatness is not an aesthetic choice — it is the load-bearing decision of the whole architecture. A transaction can name every account it will touch by address, up front, precisely because there is nothing to name but top-level map entries. Hold that thought; the next page and the parallel scheduler cash it in.

So state is a map, and the value is an account. What is in an account? On real Solana, the record has five fields. Learn them one at a time — each one exists to answer a specific question the runtime keeps asking.

pub struct Account {
pub lamports: u64, // how much native currency does this account hold?
pub owner: Pubkey, // which program is allowed to change this account?
pub data: Vec<u8>, // the account's raw contents — meaning is up to the owner
pub executable: bool, // is `data` a loadable program, or plain state?
pub rent_epoch: u64, // rent bookkeeping (see "Introduce rent", below)
}

A lamport is the smallest unit of SOL, Solana’s native currency: 1 SOL = 1,000,000,000 lamports (10⁹), named after Leslie Lamport. The lamports field is a plain u64 counting how many this account holds. It is the only value the runtime understands natively; everything else about an account’s meaning is delegated to a program. When you “send someone SOL,” you are decrementing one account’s lamports and incrementing another’s. As you’ll see, lamports also do double duty as the account’s rent deposit.

This is the field that surprises newcomers, and it is the most important one on the page. owner is the Pubkey of a program, and it encodes a single, ruthless rule enforced by the runtime itself:

Only the owner program may change an account’s data or debit its lamports.

Not the person who “created” the account. Not whoever holds a matching private key. The owner program. Anyone may credit an account (send it lamports), and anyone may read any account’s data — the map is public — but mutation is gated. This is Solana’s entire access-control model in one field: capability, not identity. We will build a whole page around what this enables (Stateless Programs Act on Accounts), but the seed is here: ownership is a program id, and the runtime polices it on every write.

data is a raw, variable-length byte buffer. The runtime treats it as opaque — a bag of bytes with no structure it cares about. Meaning is entirely the owner program’s business: one program stores a serialized counter here, another a token balance, another an order book. This is what “typed account data” means — the type lives in the owning program’s head, not in the runtime. The runtime guards who may write the bytes (via owner); it takes no opinion on what the bytes mean.

A bool. If true, the runtime treats this account’s data as a loadable program — its bytes are BPF/SBF bytecode the runtime can execute — and freezes it: an executable account’s data can no longer be changed. This is the field that lets “code is just an account” be literally true. A program on Solana is an account with executable = true, and calling that program means naming its address in a transaction. State accounts have executable = false.

A u64 used by the rent system to track when an account was last charged. In today’s Solana every live account is required to be rent-exempt (see below), so this field is largely a historical artifact — but it is still part of the record, and knowing it exists keeps the struct honest.

The solmini Account: the three fields that matter

Section titled “The solmini Account: the three fields that matter”

The book’s companion crate, solmini, models the account with just the three fields that carry the core idea, and deliberately drops the other two:

/// An account: a balance, an arbitrary data buffer, and the program that owns it.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Account {
pub lamports: u64, // native balance (Solana's smallest unit)
pub data: Vec<u8>, // an opaque buffer a program interprets however it likes
pub owner: Pubkey, // the program id allowed to mutate this account's data
}

The teaching model keeps lamports, data, and owner because those three are the account model: balance, contents, and the access-control rule over them. It omits the other two on purpose:

  • executable is omitted because solmini doesn’t load bytecode. Instead of storing programs as accounts and executing their data, it keeps programs in a small in-memory registry. There is no need to flag an account as “code” because code never lives in an account here.
  • rent_epoch is omitted because solmini doesn’t charge rent — it is an in-memory model of a few transactions, not a validator holding gigabytes of state for months. The economics that rent_epoch tracks only matter when accounts must persist and pay for the memory they occupy.

That is exactly the right kind of omission for a first-principles model: keep the fields that change how you think, drop the fields that only matter for production bookkeeping.

Here is the payoff of “everything is an account”: your wallet is not special. A wallet is simply an account with some lamports and empty data, owned by the System program (the built-in program at a well-known address, 11111111111111111111111111111111). solmini spells this out directly:

/// A plain System-owned wallet holding `lamports` and no data.
pub fn wallet(lamports: u64) -> Self {
Account::new(lamports, Pubkey::default()) // default (all-zero) key stands in for the System program
}

Read the fields back: data is empty because a wallet stores no program state — it only holds a balance. owner is the System program because the System program is the one thing allowed to move a plain account’s lamports and to hand the account off (reassign its owner) when you want a program to start managing it. This is why “creating an account” on Solana is a System-program instruction: only the System program can mint a fresh entry into the map and set its initial owner.

The flat map has to live somewhere, and that somewhere is expensive: every validator keeps the account map in memory (largely RAM) so it can execute transactions at hardware speed. An account you create is not free storage on someone else’s disk — it is a row that thousands of validators must each hold, forever, until it is deleted. Solana makes that cost explicit through rent.

The rule today is simple: an account must be rent-exempt or it does not survive. Rent-exempt means the account holds at least a minimum lamport balance, set so that roughly two years’ worth of rent is prepaid per byte it occupies. Fund an account to that threshold and it lives indefinitely; the deposit is fully refundable when you close the account and reclaim the space. Fail to, and the account is subject to being purged — swept out of the map to reclaim the memory. There is also a hard ceiling: a single account’s data cannot exceed 10 MiB. State is finite and priced, by design.

Under the hood — why rent has to exist at all

Section titled “Under the hood — why rent has to exist at all”

Rent is not a money grab; it is the direct consequence of “state is a flat map held in validator memory.” Without it, the map would grow monotonically forever — every abandoned test account, every dust balance, every dead program would sit in every validator’s RAM permanently, and the cost of running a validator would climb without bound until only a handful of data-center operators could keep up. That centralization is exactly the failure the book keeps circling: a system that runs fast today but falls apart as its state grows. Rent bounds the state. Making the deposit refundable (rather than a burned fee) means the economics push you to close accounts you no longer need, actively returning memory to the network. Rent is how “one global state machine at hardware speed” stays sustainable, not just fast on day one.

  • Why does it exist? A blockchain needs one agreed-upon global state. Solana models that state as a single flat Pubkey → Account map so that every piece of state has a public, top-level address the runtime can see and reason about — no state is hidden inside a contract.
  • What problem does it solve? It makes a transaction’s footprint knowable in advance. Because every account is externally addressable, a transaction can list exactly which accounts it reads and writes before it runs — the precondition for parallel execution, which is Solana’s whole throughput bet.
  • What are the trade-offs? Flat, public, external state buys you addressability and parallelism, but it pushes work onto programs: a program is handed raw account buffers and must validate each one itself (see Stateless Programs). It also forces the rent economics — storage is finite, priced, and capped at 10 MiB per account.
  • When should I avoid it? You don’t avoid it on Solana — it is the substrate. But the lesson to avoid elsewhere is assuming state must be co-located with code; when you need order-of-operations known ahead of time (as any parallel scheduler does), external addressable state is the pattern, and inline private storage is the thing to drop.
  • What breaks if I remove it? Collapse account state back inside contracts and you lose up-front footprint declaration — the runtime can no longer tell which transactions conflict without executing them, so it must run everything serially. Remove the flat model and you remove Solana’s parallelism at the root.
  1. Describe Solana’s entire world state in one sentence, and say what the key and the value of that structure are.
  2. Name the five fields of a real Solana Account and, in a few words each, say what each one is for.
  3. The owner field encodes an access-control rule. State the rule precisely: who may change an account’s data or debit its lamports, and who may credit or read it?
  4. The solmini Account keeps lamports, data, and owner but omits executable and rent_epoch. Why is each omission justified for a teaching model?
  5. Why must accounts be rent-exempt, and what would go wrong for the network — not just the user — if rent did not exist? Include the per-account size cap in your answer.
Show answers
  1. Solana’s world state is a single flat map from a 32-byte Pubkey to an Account record — the key is the account’s address (a 32-byte public key, base58-rendered), and the value is the account record (lamports, owner, data, executable, rent_epoch). There is no nesting and no contract-private storage.
  2. lamports — the native balance (count of SOL’s smallest unit) the runtime understands directly. owner — the program id permitted to mutate this account; the access-control rule. data — an opaque byte buffer whose meaning is up to the owner program. executable — whether data is a loadable program (code) rather than plain state; executable accounts are frozen. rent_epoch — rent bookkeeping that tracks when the account was last charged.
  3. Only the owner program may change the account’s data or debit its lamports, and the runtime enforces this on every write. Anyone may credit an account (send it lamports) and anyone may read any account, since the map is public. It is capability-based (a program id), not identity-based.
  4. executable is omitted because solmini never loads bytecode — it keeps programs in an in-memory registry rather than as accounts, so no account is ever “code.” rent_epoch is omitted because solmini is an in-memory model of a handful of transactions and never charges rent; the memory economics rent_epoch tracks only matter for validators persisting state over time. Both fields affect production bookkeeping, not how you think about the model.
  5. Every validator holds the account map in memory, so each account consumes real, permanent RAM across the network. Rent-exemption requires an account to prepay (as a refundable deposit) for the space it occupies, which bounds state growth. Without rent, the map would grow forever — every abandoned or dust account would sit in every validator’s memory permanently, driving up the cost of running a validator until only a few operators could, centralizing the network. The data of a single account is also hard-capped at 10 MiB, so no one account can consume unbounded memory.