Overview — Writing Programs & Anchor
Every part so far has taught you the runtime from the outside in — the clock that orders transactions,
the account model that externalizes state, the Sealevel scheduler that runs
non-conflicting transactions in parallel. If you followed the rust-playbook, you also built a miniature of
all three by hand: solmini gave you the mental model in running code — stateless programs, typed accounts,
declared footprints. This part is where you stop modeling the runtime and start authoring against it.
That distinction matters, because the throughline of this whole book — how do you build a single global state machine that runs at hardware speed without falling apart? — has, at the program author’s altitude, a sharp and slightly alarming answer. A Solana program is code with no state that receives a raw list of accounts. Correctness therefore reduces to two humble jobs: parse the bytes you were handed, and check every account by hand. Miss one check and an attacker substitutes a malicious account. This part is the story of doing those two jobs correctly — first the dangerous native way, then with Anchor, the framework that exists to make the checking routine instead of heroic.
From the capstone bridge to real code
Section titled “From the capstone bridge to real code”If you did the rust-playbook, Day 30’s Anchor bridge
already gave you the vocabulary of a real program — you saw the annotations, and you were told which
solmini piece each one stands for:
declare_id! → the program's address (solmini: Program::id) #[program] → the stateless code module (solmini: Program::process handlers) #[derive(Accounts)] → the declared footprint (solmini: Transaction.accounts / AccountMeta) #[account] → a typed data account (solmini: CounterState in account.data) Context<T> → the accounts this call gets (solmini: accounts: &mut [Account]) CPI → one program calling another (solmini: process calling process) PDA → an account the program owns (solmini: an Account whose owner is the program)The bridge was recognition at a distance: “this annotation stands for that runtime idea.” This part is
the fill-in. We take each of those words and show you exactly what it compiles to, why it is there, and
what goes wrong when it is missing. By the end you will not read #[account(mut)] as a mysterious spell —
you will read it as “feed the scheduler is_writable: true for this account,” because you will have written
the version that has no spell at all.
The tension this part resolves
Section titled “The tension this part resolves”There is one recurring tension underneath every page here, and it is worth naming up front so you can watch it play out:
The native model is completely explicit and completely dangerous. Anchor trades some of that explicitness for declarative safety — and you must still know what it is doing underneath, because the danger did not disappear, it moved into a macro.
We resolve that tension by teaching the dangerous version first. You will hand-parse an instruction, and
you will hand-write the ownership, signer, and key checks that a real program lives or dies by. Only then do
we introduce Anchor — so that when a constraint like has_one or #[account(mut)] saves you, you can see
the exact hand-written check it replaced. A tool whose magic you cannot un-magic is a liability; Anchor’s
whole value is that its magic is legible.
native program Anchor program ────────────── ────────────── entrypoint! + a giant #[program] fn per instruction process_instruction + #[derive(Accounts)] struct
manual borsh/byte parse Context<T> deserializes for you manual owner check #[account(...)] constraint manual signer check Signer<'info> manual key/PDA check seeds = [...], bump ───────────────────── ───────────────────── nothing hidden, the checks are DECLARED, nothing enforced the framework ENFORCES themRoadmap for this part
Section titled “Roadmap for this part”Read these in order — each page depends on the one before it. The “Depends on” column points back to the earlier idea each page assumes you already hold.
| # | Page | What it teaches | Depends on |
|---|---|---|---|
| 2 | The Native Program: entrypoint and process_instruction | The real shape of a program with no framework: entrypoint!, the single process_instruction, and the program_id / accounts / instruction_data arguments | Stateless programs, SBF bytecode & programs |
| 3 | Parsing Accounts and the Checks You Must Do by Hand | Why a raw account list is dangerous, and the owner / signer / key / discriminator checks every native program must perform itself | Everything is an account, Instruction & account flags |
| 4 | Anchor: #[program], #[derive(Accounts)], and Constraints | How Anchor turns the manual checks from page 3 into declarative constraints, and what its macros generate underneath | The native structure (2) and the manual checks (3) it replaces |
| 5 | Program-Derived Addresses and Cross-Program Invocation | PDAs as program-owned accounts at deterministic addresses; CPI as one program calling another with invoke / invoke_signed | Program-derived addresses, CPI & PDAs in the runtime, declared footprints |
| 6 | The IDL, Testing, and the Localnet Loop | The IDL as the program’s typed interface, and the solana-test-validator + anchor test loop that lets you iterate safely | Everything above — a full program to test |
| 900 | Revision — Writing Programs & Anchor | The part recapped as one worked mental model, with self-check questions | The whole part |
The dependency order is not arbitrary: you cannot appreciate what Anchor removes (page 4) until you have felt the sharp edges it removes it for (pages 2 and 3), and PDAs and CPI (page 5) only make sense once you hold both the account model and the constraint system. Testing (page 6) needs an actual program to run.
The thread
Section titled “The thread”The book’s question, asked at the program author’s desk, becomes: how do you write code for a machine that runs your transaction in parallel with thousands of others and trusts you to have declared everything you touch? The answer this part builds toward is that the program’s safety is not the runtime’s job — it is yours — the runtime only enforces that a program touches accounts it was handed and owns. Whether those are the right accounts is a question only your checks (or Anchor’s constraints) can answer. That is why this part sits exactly where it does: after you understand the runtime’s guarantees (accounts, Sealevel) and before you trust yourself to write something that holds other people’s money. The native model gives you the runtime’s raw truth; Anchor gives you a disciplined way to not get robbed by it.
Check your understanding
Section titled “Check your understanding”- This part is framed as the “practical continuation” of the rust-playbook’s
solmini/Anchor capstone. What didsolminigive you, and what does this part add on top of it? - Restate the part’s throughline in one sentence: what does a Solana program actually receive, and why does that reduce correctness to two specific jobs?
- The part deliberately teaches the native model before Anchor. Why that order — what would you fail to appreciate if you learned Anchor first?
- Name the recurring tension this part resolves, and state Anchor’s side of the trade it makes.
- Using the roadmap, pick any one content page and name the earlier idea it depends on and the capstone
annotation (
declare_id!,#[account], CPI, PDA, …) it fills in.
Show answers
solminigave you the runtime’s mental model in running code: stateless programs, typed accounts in external buffers, and footprints declared up front so a scheduler can parallelize. This part adds the authoring layer — actually writing programs against that model: the native structure, the manual security checks, Anchor’s constraints, PDAs/CPI, and testing.- A Solana program is stateless code that receives a raw list of accounts (plus an opaque instruction-data blob). Because it holds no state and the runtime hands it accounts positionally, correctness reduces to (a) parsing the bytes it was given and (b) checking every account by hand — the right key, owner, signer, and type.
- Because Anchor’s value is that it replaces specific hand-written checks. If you never wrote those checks,
a constraint like
has_oneor#[account(mut)]looks like arbitrary magic; having done the checks by hand, you read each constraint as the exact manual check it stands in for — so you know what breaks if it is missing. - The tension: the native model is fully explicit but dangerous, while Anchor trades explicitness for declarative safety — and the danger doesn’t vanish, it moves into a macro you must still understand. Anchor’s side of the trade: give up some visible, hand-rolled control in exchange for checks that are declared and framework-enforced, so they are hard to forget.
- Example: PDAs and CPI depends on the account model and
declared footprints, and fills in the capstone’s CPI and PDA
annotations — CPI being one stateless
processcalling another, PDA being an account the program owns at a deterministic address. (Any roadmap row’s answer is acceptable.)