---
name: solana
description: "Use when working on Solana software, including one or more of: Solana client code using TypeScript, Rust libraries that use Solana crates, Anchor programs, Quasar programs, LiteSVM tests, including Rust program files, TypeScript tests, and Anchor.toml or Quasar.toml configuration. Designed to create minimal, reusable code without unnecessary duplication."
---

<!--
  Mirror of https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/SKILL.md hosted by kit.superteam.codes.
  Relative links were rewritten to GitHub URLs pinned to the mirrored commit —
  companion files are not hosted here; the upstream repo is the source of truth.
-->


# Coding Guidelines

Apply these rules to ensure code quality, maintainability, and adherence to project standards.

## Fight for Truth

Don't write things that aren't currently true - anywhere. Chat, code comments, variable names, PR titles, READMEs, commit messages.

- Documentation and comments that do not match the code are considered untrue.
- Variable names that do not match the purpose of the variable are considered untrue. For example, a struct called `InitializeMarket` is not true because a struct cannot 'initialize a market' - structs do not do things, only functions can do things.
- Temporary workarounds that aren't labelled as such are lying through omission - there is an issue you aren't telling the next programmer about. Mark them with a `TODO` comment with a link to a git issue (if it exists) and telling the next programmer when they can delete the workaround.
- If unsure of something, say so. Bluffing is lying.
- **Ambiguity is a soft lie:** if a phrase could be read two ways and only one is true, it's misleading. Disambiguate before sending - pick the term that says exactly what's meant, name the antecedent of every "it"/"this"/"that".
- A wrong statement is worse than no statement.
- Separate scratch labels from real identifiers.

Actively fix untrue things when you see them. Don't let "close enough" wording stand in for the truthful one.

**Grep before naming.** Before sending any prose, walkthrough, README, comment, or commit message that names a specific identifier (function, struct, file, account, module, field, constant), grep the source for that exact identifier and confirm it exists. "I'm pretty sure that's the name" is not enough. If the identifier doesn't exist, either use the real name or apply the rename to the code first, then write the prose.

**Describe what is, not what was removed.** READMEs, doc-comments, and code comments document current state - not history. Lines like "no floats", "no longer uses X", "replaces the previous Y approach" belong in CHANGELOGs and PR descriptions, not source artefacts. A first-time reader has no history and "no longer uses I64F64" creates ambient confusion ("wait, should I be worried?"). Sweep before sending: grep for `no longer`, `removed`, `previously`, `used to`, `formerly`, `dropped`, `now uses`, `replaces the previous` - each hit is a candidate for deletion.

## Do the whole thing

The marginal cost of completeness is near zero with AI. Do the whole thing.

Do it right. Do it with tests. Do it with documentation. Do it so well that the user is genuinely impressed - not politely satisfied, actually impressed. Never offer to "table this for later" when the permanent solve is within reach. Never leave a dangling thread when tying it off takes five more minutes. Never present a workaround when the real fix exists.

The standard isn't "good enough" - it's "holy shit, that's done." Search before building. Test before shipping.

Ship the complete thing. When the user asks for something, the answer is the finished product, not a plan to build it. Time is not an excuse. Fatigue is not an excuse. Complexity is not an excuse. Boil the ocean.

**A caveat is not a fix.** When you find a gap while doing the work - missing test coverage, a CI job that doesn't run, an untested path, a stale doc - close it as part of the task. Do not downgrade a fixable defect into a disclaimer. If you catch yourself writing "note: X isn't covered / isn't built / isn't tested / won't get a CI signal," stop: that sentence is a TODO, not a caveat. Fix X so there is nothing to note. Reserve caveats for what you genuinely cannot fix (out of scope, needs a decision, external blocker) - and for those, state what you would do and offer to do it.

## Success Criteria

- Before declaring success, declaring that work is complete, or celebrating, run the project's actual tests using the correct command for that project (for example: `anchor test` for Anchor workspaces, the project's TypeScript test command for TypeScript clients/tests, or `cargo test` for Rust crates). If the tests fail, there is more work to do. Don't stop until the relevant test command passes on the code you have made.
- Create all program state through the program's own instruction handlers in tests. Injecting pre-fabricated accounts (hand-built account data passed straight into the SVM) hides missing init instructions and missing constraints - a program can pass every test while being unusable onchain. Pre-fabricated accounts are only acceptable for accounts a foreign program would have created (an oracle's price account, a mainnet-dumped fixture).
- Tests that use a zero or degenerate value for a parameter (e.g. `duration: 0`) test only the boundary where opposite comparisons coincide. Use nonzero, asymmetric values and test both sides of every boundary.
- Do not write placeholder tests. Placeholder tests don't count as tests, placeholder tests passing does not achieve your task.
  - Tests that just do `assert.ok(true)` or similar are placeholder tests and do not count as tests
  - Tests that do not call the program's instruction handlers are placeholder tests and do not count as tests
  - Tests must: initialize accounts, send transactions, verify state changes, check balances
  - If you find yourself writing placeholder tests, stop and write real integration tests instead
  - DO NOT mark "Write tests" as complete until tests actually call the program instructions
  - DO NOT ask "should I write real tests now?" - if the tests are placeholders, write real ones immediately

- Do not stop until documentation like `README.md` and `CHANGELOG.md` are also updated with your changes. If you have made a feature, and it is not documented in the README or changelog, there is more work to do and you must continue working.

- When summarizing your work, show the work items you have achieved with this symbol '✅' and if there is any more work to do, add a '❌' for each remaining work item.

## Documentation Sources

Use these official documentation sources:

- **Anchor**: https://www.anchor-lang.com/docs
- **LiteSVM**: https://www.anchor-lang.com/docs/testing/litesvm
- **Anchor Error Codes**: https://github.com/otter-sec/anchor/blob/master/lang/error/src/lib.rs
- **Quasar**: https://quasar-lang.com/docs
- **Solana Kite**: https://solanakite.org
- **Solana Kit**: https://solanakit.com
- **Agave (Solana CLI)**: https://docs.anza.xyz/ (Anza makes the Solana CLI and Agave).
- **Arcium** (if used): https://docs.arcium.com/developers
- **Quicknode Solana Program Examples**: https://github.com/quicknode/solana-program-examples
- **Anatoly Yakovenko's GitHub**: https://github.com/aeyakovenko in particular the 'Percolator' perps project repositories.

## Terminology

- Remember this is Solana not Ethereum. Ethereum is not relevant to any documentation you write. Do not assume people know or care about Ethereum.
  - Don't tell me about 'smart contracts' or 'protocols' (use 'programs' instead)
  - Don't tell me about 'gas' (use 'transaction fees' instead)
  - There are no 'mempools'.
  - Do not tell me about other things that are not relevant to Solana.

- Token program terminology:
  - Use 'Token Extensions Program' or 'Token extensions' for the newer token program (not 'Token 2022' which is just a code name)
  - Use 'Classic Token Program' for the older token program
  - Use 'Token' rather than 'SPL Token' unless you are specifically discussing the distinction between the native token (SOL) and all other tokens (SPL Tokens)

- Onchain / offchain (one word, no hyphen)
  - Always write 'onchain' and 'offchain' as single, unhyphenated words - like 'online' and 'offline'.
  - Never write 'on-chain' or 'off-chain'. The hyphenated forms are wrong.
  - Apply the same rule to related terms: 'crosschain' (not 'cross-chain'), etc.
  - Sources:
    - [Solana Foundation style guide](https://solana.com/docs/references/terminology)
    - [US Government usage](https://www.sec.gov/files/rules/interp/2026/33-11412.pdf)
    - [Cat (catmcgee) will make fun of you if you write 'on-chain'](https://x.com/catmcgee/status/2028153588715761825)

- Token amount units: a **major unit** is the human-scale denomination - dollar, pound, yen, SOL. A **minor unit** is the smallest denomination, the raw integer programs operate on - cent, penny, sen, lamport. Use these terms; do not say 'base units'.

- Some tools in Solana unfortunately use the same word 'instructions' for both the input and the functions. To avoid confusion, use 'instruction handlers' for the functions that handle instructions, and 'instructions' for the input to those functions.

- Name handlers by one rule: **if the domain has a word for it, use the domain's word; if the handler only stamps out a container, it is `initialize_`.** Domain verbs are what make a program read like finance, and they come in pairs: `make_offer()`/`take_offer()`, `open_position()`/`close_position()`, `place_order()`/`cancel_order()`, plus `borrow`, `repay`, `liquidate`, `contribute`, `refund`. Setup has no domain meaning, so it is always `initialize_x()`, naming what it initializes: `initialize_market()`, `initialize_pool()`, `initialize_config()`, never `create_pool()`, `init_reserve()`, or a bare `initialize()`. The test is whether the handler moves tokens or takes a market action, or only writes parameters into a new account. A third family, `add_x()`, extends a container that already exists: `add_liquidity()`, `add_outcome()`, `add_asset()`.

- A **key pair** is two keys: a private key that signs, and a public key. The **public key is the address**; the pair is not an address, is not an account, and is never "at" one, and the private key is never shown or named as if it were public. An address made from a key pair is always on the Ed25519 curve, whoever generated the pair and whatever they did with the private key afterwards; only a PDA, or a built-in address such as the System Program's or a sysvar's, is drawn off it. So never write "a keypair address", "the keypair's account", "an off-curve keypair", or a caption such as `A CLIENT KEYPAIR`; write "a public key the client generated", "the account's address", or name the account. Never write a bare "key" for either half: say "private key" where it signs, is held or can be lost ("the market owner's private key signed the price", "a single private key on one person's laptop"), and "public key" where it is stored, recorded or checked ("her public key is stored as the market's `admin`"). Where the point is who signs rather than the key, "signer" or "signature" is often shorter ("a single trusted signer", "requiring the operator's signature"). "Key" in the database sense (a key/value store, a tree's sort key) is a different word; say so where the two could be confused.

- Say **PDA** for an address and **account** for the data at it. They are not synonyms: a program derives the offer PDA and then writes the offer account. Use PDA where the subject is derivation, seeds, authority, ownership, or signing, and account where the subject is fields, balances, rent, creation, or closure. Introduce a program's state as an account that lives at a PDA ("the **market** account lives at a PDA with seeds `"market"` and the two mints, and holds...", or "the **reserve** account (PDA seeds: ...)"), never as "the market is a PDA holding...". Don't write "PDA account": it names both halves at once and blurs them back together. Name the account by its role and add "at a PDA" only when the address model matters. The reverse slip is as common: a vault's authority is the offer PDA, not "the offer account", because authority is signing, and signing is the PDA's.

## Do not use

- Do not use 'Solana Labs' documentation. The company has been replaced by Anza.

- Do not use 'Coral XYZ' documentation or packages. Coral used to maintain Anchor, but Anchor is now maintained by the Solana Foundation (solana.org). The TypeScript package has been [moved from @coral-xyz/anchor to @anchor-lang/core](https://www.anchor-lang.com/docs/updates/release-notes/1-0-0).

- Do not use any documentaton or tools from Project Serum, which collapsed many years ago.

- Do not use yarn. Yarn has no reason to exist and only adds unnecessary dependencies and is not commonly used for new JS/TS projects in 2026. Replace Yarn with npm everywhere you see it. Use npm for new projects as it does not require additional dependencies. Keep using pnpm if the project already uses pnpm.

- Do not use **Switchboard** - it is shutting down. Every Switchboard implementation was deprecated in September 2026 and support ended on 25 September 2026. Use Pyth for price feeds instead, and migrate any existing Switchboard feed reads.

- Do not use **Clockwork** - this product is dead. For scheduled instruction handler invocation, use [TukTuk](https://github.com/helium/tuktuk/tree/main/typescript-examples) instead.

- Do not use [https://github.com/solana-developers/program-examples]. As of June 2026 these examples are out of date, going back to Anchor 0.26 in 2022, use a bunch of deprecated tools, have security failures and broken tests, and have been this way for more than a year.

## Library versions

Use the latest stable Anchor, Rust, TypeScript, Solana Kit, and Kite you can. If a bug occurs, favor updating rather than rolling back.

## Project Documentation

Every project must have a `README.md` file in the project root that includes:

- **Purpose**: Why the project exists and what problem it solves
- **Major Concepts**: Key architectural concepts, important PDAs, state structures, and program logic
- **Testing**: How to run the tests (e.g., `anchor test`)
- **Setup**: Any prerequisites or setup steps needed to work with the project
- **Usage**: Basic usage examples or deployment instructions if applicable

Keep the README focused and practical. Avoid generic boilerplate - write documentation that would actually help someone understand and work with this specific project.

### Documentation style

- **No numbered headings.** Headings are words only - no `## 1. Overview` or `### 3.6 Liquidation`. Numbered headings break when a section is inserted or removed.
- **A heading states what the reader gets, and works read alone.** It is close to a summary of its section, because a table of contents lists every heading out of context. Name the outcome, not the mechanism behind it: "Seeds Make Data Findable", not "Seeds Make Addresses Derivable". Keep specific actors out: "A User's Public Key Is Their Address", not "Alice's Public Key Is Her Address". A narrative walkthrough is the exception, where the headings are the story's steps and name who acts.
- **A section lives where its subject belongs, not where it was first needed.** If a section would sit equally well under a different heading, it is under the wrong one: rent is a property of every account, so it belongs with accounts, even if program-derived vaults are where its lifecycle first mattered. Teach each concept once, in full, where it belongs; elsewhere give it a clause and a pointer, never a second explanation. When a section turns out to be two things, split it and send each half home.
- **A named character has a gender; a role does not.** Alice, Bob, Carol, Dave, Maria and Victor keep their pronouns. A maker, a taker, an admin, a manager or a borrower named only by role is they: "the maker funds their offer account", "the admin picks the winning outcome, but they cannot touch the pro-rata math", never "her offer account" or "she cannot touch". A role sentence that sits next to a named character is still a role sentence: "Maria is the fund manager" takes she, and "the manager is included in the threat model, and the program treats them as such" takes they. The reverse also misleads: a gendered pronoun for a named character, placed right after a role noun, reads as the role's pronoun. "Every health evaluation errs against the borrower. His NVDAx is not sold" makes the borrower a he. When the nearest noun is a role, use the character's name: "Bob's NVDAx is not sold".
- **A summary section states what the reader now knows**, not what the example's characters did - "a user's account holds their balance of SOL", not "Alice's holds her balance" - even when the section that taught it was a story.
- **Integrate per-instruction reference into lifecycle prose.** Walk through the program's flow and inline each handler's mechanics (signers, accounts, token movements, errors) at the point it's first called. Don't keep a separate flat "Instruction Reference" section.
- **Emphasis is meaningful.** Bold canonical terms on first use, plain everywhere after, like a textbook, and bold the claim a passage turns on. Italicize the titles of books and other works (*TCP/IP Illustrated*). Never use either as decoration.
- **Quote a word you are talking about as a word.** When a sentence is about a word rather than the thing it names (what it means, what something is called, what it is short for, where the name comes from), put the word in double quotes: '"Physical" here means the asset itself changes hands', 'creating an option is called "writing" it', 'short for "underlying asset"'. Unmarked, the word reads as ordinary prose ("Physical here means" reads as an adjective that lost its noun), and italics read as emphasis. This is different from introducing a term, which is bold at its definition: "this is **physical settlement**" defines the term, '"physical" means' discusses the word.
- **No ASCII art, no Mermaid diagrams, no markdown tables.** Use headings, nested bullet lists, or prose. Tables don't render well on chat surfaces. When a project calls for real figures (a book, a docs site), draw hand-authored SVG account diagrams per [DIAGRAMS.md](https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/DIAGRAMS.md) instead.
#### Plain language

Every rule below was written after an audit found the failure many times over in one manuscript, and the manuscripts were written with care. The shared cause is that each of these shapes sounds like writing: it has cadence, or a term of art, or the air of a conclusion, and so neither the writer nor a reviewer stops on it. The test for all of them is the same: after reading the sentence alone, can the reader say what it claims, in values, actors and events? If not, it is one of the shapes below, and the fix is the plain sentence it was standing in front of.

- **Say the mechanism; never announce, point at, or abstract it.** Name the thing, the actor and the event, in the plainest words that stay accurate. The shapes that fail:
  - *Announcing a point instead of making it.* "What matters here is the unit", "the point is", "the whole reason", "this is the real lesson", "the lesson stands", "that is exactly the point", "it matters more than it looks", "it is worth being clear about". Delete the announcement and keep what followed it; the content is always the next sentence.
  - *Inventing a category to fill.* "The defense is a margin", "the fix is mechanical", "the pattern to copy is the price injection", "the payoff is an invariant", "the order is what makes it safe", "the trick is", "the catch is", "the mechanism is". "The" points at a defense or pattern the reader was never given. State it with an actor and a verb: "Handle this with a margin", "Copy the price injection: make the price feed a fixture and move it deliberately", "the handler prices the shares before it takes the USDC in, so a deposit never counts toward its own price". "The answer is" stays when the sentence before asked the question; "the cost is" stays when the cost was just stated.
  - *Pointing at a rule or a passage instead of stating it.* "The same rule protects the AMM", "the exit rule", "a rule to apply to your own programs", "you've seen this before", "an old friend", "the familiar shape", "more on that later", "the chapter puts a number on it", "the limits are covered below". Each sends the reader off to find something, and a label given to a principle becomes a coined term the moment another section cites it. State the content where it is used ("every participant must be able to get their assets out of every state"), and make every reference name both the place and the thing: "the accumulation factor in the *Lending and Borrowing* chapter", never "the familiar shape". A forward reference that withholds the fact fails the same way: "that fee compensates them for impermanent loss" beats "the chapter puts a number on it".
  - *A pointer as the subject.* "This is how it is enforced" names nothing; "disabling the mint authority is how it is enforced" does. "Offchain the question has to be answered" hides who answers; "the code that reads the chain has to answer it" names them. A sentence leads with its actor and lets it act.
  - *Restating a concrete passage in the abstract.* After a paragraph that showed Bob's swap leaving the pool at 459 ACME and 1,225 USDC and Carol then paying $2.78, "The second trader's numbers follow from what the first trader's swap did to the pool" names no number, no handler and no field; it says only that a relation exists, which the reader just watched. The tell is an abstract noun as subject ("the numbers", "the result", "the outcome", "the design") joined to the sentences before it by "follows from", "is a consequence of", "depends on", "reflects", "is shaped by". Delete it, or state the relation with the values: "Bob's buy raised the price from $2.25 to $2.67, so Carol pays more per ACME than he did."
  - *Shrinking a mechanism to sound profound.* "The only check that enforces the all-or-nothing promise is an integer comparison", "fifteen lines of integer math doing a central bank's job", "all of it is one comparison". These restate the paragraph and ask the reader to be impressed by how small the code is. Cut them; if the size of the code is a fact the reader needs, give it plainly.
  - *Defining by abstract category.* An account is "some data, a Rust struct, with an address", never "the unit of state". A definition that would fit equally well in a document about a different subject is too vague. Prefer the short common word: "the deployed program", not "the deployed bytecode".
- **No machine cadence: no maxims, epigrams, fragments, rhetorical questions, or straw-man contrasts.** A reader hears these before the content, and clocks them as generated.
  - *The em-dash and the phrases that read the same way* are banned outright: "load-bearing", "delve", "tapestry", "intricate", "multifaceted", "holistic", "myriad", "seamless", "robust", "pivotal", "crucial", "elevate", "testament to", "deep dive", "paradigm shift", "game-changer", "unlock the power", "realm of", "landscape of", "in today's", "it is worth noting", "underscores the". Use a regular dash or rewrite; each phrase is a gesture, so name the thing and say what it does ("every number in the fund is computed from the feed", not "the feed is load-bearing").
  - *The paragraph-closing maxim.* A paragraph makes its point in plain sentences, then closes with a short quotable restatement: "A wall that has never been pushed on is a hypothesis", "Conservation, the escrow edition", "One transaction's dust is worth nothing; a million transactions' dust is worth taking", "None of these tiers makes a venue's losses smaller. They name, before the loss happens, which users pay for it." Delete the maxim; if it held a fact the paragraph lacked, state the fact in a plain sentence.
  - *The epigram as definition.* "A market maker's structural loss: the trades that find you when your quote is wrong are the trades you did not want" is two mirrored halves the reader solves like a riddle, and once solved it names no actor and no event. "Balances are custody, state is truth" and "the counter is the truth; the vault is just custody" are the same shape. Write who does what and what follows: "The loss a market maker takes when a trader who knows the price has moved fills the quote before it is updated." This applies most strictly to glossary entries, summary bullets and section openers, which are read with no paragraph around them to decode the line.
  - *The fragment used as a beat.* "Not Alice, not the program's author.", "His bot fires.", "There is no idle-cash mode, no 'the manager will get to it.'" Fold it into the sentence before, or cut it.
  - *The rhetorical question as a transition.* "Is this greedy?", "And what if the film festival burns down?", "A perpetual never expires, so what stops its price drifting?" Each is a sentence that delays the answer by its own length. Write the statement: "Keeping the dust in the pool is the only policy nobody can exploit", "An Open event can also be voided", "A perpetual never expires, so a venue charges funding". A question a reader would actually ask, answered at once, is different and stays.
  - *"X is not Y; it is Z" against a straw man.* The construction corrects a misreading, so it works only when the reader really would assume Y: "a mint is not itself a token: it is the account a token comes from". One audit found 48 in 50,000 words, a third negating something nobody thought ("this is not stinginess; it is the only stable policy", "quoting the oracle with no spread is a configuration mistake, not a strategy"). Drop the negation and assert Z.
- **Plain words over elevated, figurative, or empty ones.** If a plainer word carries the same meaning, it is the right one, and finding the fancier one used consistently across a document is evidence of a repeated mistake, not of a house style.
  - *The elevated register.* "The program interrogates the price" was "checks" in all five places; "further clusters are stood up from time to time" is "other clusters are started now and then"; "dissects Mango Markets" is "covers"; "the arithmetic is the smallest in this book" rates where "the arithmetic lives in `contract_math.rs`" states.
  - *Metaphor, idiom and slang where a literal phrase of the same length exists.* "A dial worth turning" (a parameter worth varying), "a single whale" (a single large backer), "config foot-guns" (dangerous configurations), "zombie positions" (positions nobody liquidates), "cannot be rugged", "by fat-finger", "after the whistle" (after the deadline), "comes off the top" (is deducted first), "pays for itself", "every lamport comes home", "the hard parts are at the edges rather than in the middle", "dollars outside the banking system they were born into", "the crowded side pays rent" (in a book where rent is lamports), "return 'not applicable'" (do not apply), "the arithmetic is a straight line against the clock" (the amount released grows in proportion to the seconds elapsed), "the ladder has three rungs" (there are three levels of assurance). A metaphor drags in connotations the writer did not choose, and the test is to restate it plainly: if you cannot, it carried no mechanism. Money words are the worst case, because a reader of financial writing takes them at their word: "a slot count would buy nothing here and cost a guess" reads as a claim about money and meant "counting slots would have no advantage here, and would be inaccurate"; "conviction pays three to one" in a market with proportional payouts was "winnings are proportional to stake". Keep buys, costs, pays, earns, cheap, price, trade and bet for assets that actually move. Two things are not mannered and stay: the teaching analogy that carries the mechanism and is then unpacked (a mint is a factory and the mint authority is its boss), and the term of art the field itself uses (a deep pool, seeding a pool, adverse selection, the rails).
  - *Intensifiers and fillers.* "Simply", "exactly", "precisely", "real", "the whole", "the very", "deliberately", "forever", "at production scale", and "quietly" or "silently" as intensifiers ("quietly rewritten": the value changed, so say it changed). Each is a word the sentence reads the same without. "Honest" and "honestly" are never a synonym for accurate, stated or plain ("the honest way to make it", "honest bookkeeping": twenty in one manuscript, none about honesty). "The discipline" and "hygiene" as names for a rule ("the staleness check was hygiene") name a category where the rule is shorter.
  - *Words with a plain equivalent.* "Utilize", "commence", "prior to", "subsequent to", "in order to", "in the event that", "whilst", "thus", "hence", "therefore", "approximately", "numerous", "sufficient", and the Latin ("pro rata", "per se", "de facto", "ad hoc", "vice versa", "e.g.", "i.e.", "etc."): each has an English word the reader does not have to translate. "Worked example" is "example". Words that only look bureaucratic but are exact in this domain stay: a transaction executes, a supplier earns yield, a program presents seeds.
  - *Never "fail open" or "fail closed".* An open circuit has stopped conducting and an open door lets everything through, so readers split on which way each points. Say what the program does: "a stale price is refused", "a missing signer check lets anyone through".
  - *No "X-based".* "Pull-based refunds", "share-based accounting", "time-based expiry" name a category and leave the mechanism to the reader: "each backer calls `refund()` themselves", "deposits are recorded as shares of the pool". The one exception is naming "property-based testing" once so a reader can find those libraries.
  - *Say where the tokens went, not that they are "locked".* "The handler moves 1,000 USDC from Bob's token account into the quote vault" is the same length as "the handler locks 1,000 USDC" and cannot be read as a mutex. "Total value locked" and a locked minimum liquidity are established terms and stay.
- **Name things by their real names, and name the members, not the category.** The reader must be able to find every word in the program's source, its error messages, or a standard finance or Solana reference.
  - *The coined label that sounds like jargon.* "Gate" for a check, "exit" for a handler that pays out, "guard" for a check, "promise" for what a program does, "truth" for a recorded field, "custody" for a vault, "the terms sheet" for a struct, "the machinery" for a mechanism, "the rent economics" for who gets the lamports, "by construction" for "because this code does X". Once one section uses the label the next reuses it, until it looks like house style, and the reader decodes it every time. Name the handler, the field, the check or the error: "fails with `FundraiserNotEnded` before the deadline", not "fails on the close gates". Real terms keep their meaning: Solana's feature gates, a trader's exit price, a Merkle proof.
  - *The category standing for its members.* "The systems around the program choose", "infrastructure", "components", "tooling", "services", "large onchain fund products", "a production consumer": say "the liquidator bot, the crank on a schedule, the backend that marks an order paid", or name the venue. If you cannot list the members, you do not yet know what you are claiming.
  - *A part stated as the whole.* "The manager is the threat model" (included in it), "the operator's signature is the oracle" (acts as), "a stale price is the attack" (one way to exploit it), "the venue's entire risk surface is the custody ledger" (what is left after the list before it). Say the relation that is true. Keep "is" where the identity is literal: the perps pool is the counterparty to every position.
  - *Say what a feed carries.* Write "price feed", "funding rate feed", or whatever the account publishes, never a bare "feed"; leave code identifiers such as `mock-price-feed` as they shipped.
  - *Never write "money"; say what it is.* "Money" names no asset and no amount: say the stake, the collateral, the deposit, the fee, the vault's USDC, or "assets" where no single one is meant. The option terms "in the money" and "money market", and everyday speech about people ("put money on", "get my money back"), are exact and stay.
  - *A test checks; it does not prove, and it checks only what it asserts.* "`test_close_fundraiser_sweeps_direct_donations_to_maker` proves close is where strays are swept" overclaims and hides the scenario; "checks that `close_fundraiser()` pays those tokens to the maker" states it. Before writing what a test checks, read its assertions: a test that asserts only `is_err()` does not check an error code, a test that deposits one fixed value does not check "repeated round trips", a test that asserts balances cannot tell a skipped transfer from one of zero, and a test whose stakes are Alice 100 and Bob 300 is not "the exact story above" when the chapter had them the other way round. Either say what the test asserts, or add the assertion to the test. Keep "proof" for Merkle and zero-knowledge proofs and for a formal proof a person wrote in a proof assistant; a Kani harness model-checks, whatever its `proof_` name says.
  - *Define a symbol wherever a reader can arrive at it.* A glossary entry, a summary bullet, a heading and a figure caption are each read on their own, so "an empty reserve makes K zero" means nothing to someone who opened the glossary at "degenerate value". Say what the symbol is in words the first time it appears in that passage.
  - *Write "ID" in capitals in prose* ("a program ID", "distinct market IDs"); only code identifiers (`program_id`) and the `id.json` filename keep the lowercase form.
- **Teach each thing once, where it belongs; everywhere else, a clause and a pointer.** A section lives in the chapter its subject belongs to, not where it was first needed, and a mechanism is explained in full in exactly one place.
  - *Across a document.* One manuscript taught its interest accumulation factor in full in three chapters, each with a What You Learnt bullet, and its share-inflation attack with the same numbers in four. Decide the owner (the chapter whose subject it is) and reduce every other telling to one clause with the owner's name: "the accumulation factor the *Lending and Borrowing* chapter builds".
  - *Within a chapter.* A program chapter's walkthrough, math, invariants, failure modes and summary each re-derived the same point, so a haircut was explained five times and a double-claim defense four. The walkthrough teaches at the step where it happens; the later sections name the mechanism and add only what is new there (the formula's rounding, the invariant's wording, the test's name).
  - *A summary lists what this chapter added*, in general terms, never an earlier chapter's bullet or the story's events: "a user's account holds their balance of SOL", not "Alice's holds her balance", and not the book-wide invariant another chapter owns.
  - *A caption or figure annotation never restates the paragraph beside it*; it carries the one thing the drawing cannot say.
  - *Two sentences that say one thing, forty lines apart, are one sentence.* "A fee above 100% is nonsense" verbatim in two chapters, the same handler's two effects in the same words in a step and again in the failure modes, the same fact in a boxout and the line above it: keep the first and point.
- **Do not rate, preview, or narrate the document; talk to the reader about the subject.**
  - *Rating.* "Two design details are worth stealing", "a guarantee worth printing", "the boundary case worth studying closely", "by now you know the rule", "makes the trust arrangement unusually visible", "those two amounts are the account's most deliberate decision", "the plainest case of a rule that covers this whole page". Cut the rating and keep the content: "Two design details:".
  - *Previewing.* "The sections below cover X, Y and Z", "The rules below push every test to an edge", "The specification follows", "The table lists the extensions that matter": the headings and the table already say it. Delete.
  - *Narrating the document.* "Each section is short on purpose", "that is why it comes first", "we previously mentioned", "this chapter is a tour of that computer", "the chapter walks through". If a sentence is addressed to the author, editor or reviewer rather than the reader, delete it; a chapter explains or builds, and a reader is not on a tour.
  - *Telling the reader to look.* "Note what is pledged", "Notice who did nothing", "Watch K in the story below", "Read this carefully": each is one word longer than the sentence that says the thing. Telling them to watch something in the world ("watch a market maker during a fast market and their quotes vanish") is different and stays.
- **Sentence mechanics the reader should never have to re-parse.**
  - *Lead with the subject and use a plain connective.* "Another concern:" not "The question has a second half"; "A restart works the same way" not "The same discipline covers the restart". Inverted or agentless openings make the reader hold the clause until the subject arrives.
  - *No garden paths or zeugmas.* "A flat fee makes payments too small to bother with worth sending", "admins lose private keys or interest", "which Solana cluster software the Solana SDK uses", "PDA addresses" (address twice), "Burning does the reverse" after a sentence about the mint authority (so the authority seems to burn). Each has to be read twice. Split or reorder until it reads once.
  - *If the prose is already a list, set it as one.* The sentence spells its bullets out as words ("First, ... Second, ..."), announces a count and runs the items together, or strings parallel items on semicolons. A rhetorical triple and real narrative stay in prose.
  - *Prefer plain conditionals to biconditionals.* "An asset is approved if it has an `ApprovedAsset` account, and not approved if it does not", never "approved exactly when its account exists".
  - *Write a rule as an instruction.* "Give each transition its own instruction handler", "Give a pause two levels, and let only the second stop withdrawals". Written as a description ("One instruction handler per transition"), the reader cannot tell the example from the advice. A sentence about what a specific program does stays a description.
  - *Give the answer for each case before the principle behind them.* Lead with the cases and what goes wrong with each choice; state the principle underneath in a line. A section that only states the principle has handed the reader homework.
  - *No sentence past about forty-five words.* Every one that long was a list glued into a paragraph or two claims joined by a colon; split at the punctuation already there.

#### Content

- **Do not print a number that a scheduled change will invalidate.** "Tower BFT finalizes 32 slots back, about 12.8 seconds" is true today and wrong the week Alpenglow ships. Write what stays true and where the number comes from: waiting for finality costs latency, and how much is set by the consensus protocol. The same goes for compute limits, fee levels, and account size caps. Where a current number genuinely helps, name its source and date it, so a reader can tell a measurement from a constant.
- **Write for the reader's job.** A program developer needs to know a halt can stop the chain for hours and what that does to a slot count; the procedure for agreeing a restart slot and waiting on 80% of stake is a validator operator's work and does not belong in their document. Before including a mechanism, ask whether the reader will ever act on it.
- **A message a human reads names the consequence, not the condition that tripped.** This covers Anchor `#[msg]` strings, log lines, and CLI output. "Price feed was last updated before the most recent cluster restart" states what the check found and leaves the operator to work out why anyone cares; "Price feed is stale: it predates the last cluster restart" leads with the problem and keeps the cause. Match the voice of the errors beside it: a set of `#[msg]` strings that all state conditions should not acquire one that narrates the program's action.

## Writing About Financial Software

These apply to READMEs, docs, blog posts, and PR descriptions for finance-related projects (AMMs, escrows, lending, leasing, CLOBs, prediction markets, stablecoins).

- **"Non-custodial" is a loaded word.** If the program locks funds in vaults during its lifecycle (every escrow, lending, AMM, leasing program does), don't claim "non-custodial" - it contradicts itself. What you usually mean is "no admin override, the rules are the deployed bytecode". Say that directly, or just describe the custody arrangement (program-owned vault, PDA signers, no admin escape hatch).
- **Upgrade authority is normal on Solana** - programs are usually upgradable so authors can ship security fixes. Don't apologise for it or treat it as disqualifying for "trustless" claims. Trust in the author/multisig is baseline; "trustless" means the documented rules can't be bypassed, not "bytecode frozen forever".
- **"Token" not "mint" in economic prose, and never the two as synonyms.** A mint is the onchain account that defines a type of token and controls its supply; a token is the asset. A mint is a token's factory, not a token, and holds no balance of anything - which is the test when a sentence is ambiguous: if it can be held, spent, or transferred it is a token, and if it can be created from or checked against it is the mint. So "one mint defines USDC", never "USDC is one mint"; "every distinct token has its own mint account", never "is a distinct mint account". In economic descriptions ("post token A as collateral, borrow token B"), say "token A" and "token B". Reserve "mint account" for technical descriptions of what gets passed to instructions.
- **Example assets are USDC and real-world assets**: stocks like NVDAx, TSLAx, and SPCXx. Never meme tokens - they date a document and make it read as speculation rather than finance.
- **Tokens are fungible by default - don't say so.** Don't write "fungible token" or sentences explaining that tokens are fungible. The reader knows. Only qualify when contrasting ("non-fungible token" / NFT). Same rule as not explaining what an integer is.
- **Don't be fascinated with "tokenization" - a tokenized asset is just an asset.** Drop the word "tokenized" from economic prose. A "basket of tokenized assets" is a "basket of assets"; "tokenized stocks like TSLAx and NVDAx" are "stocks like TSLAx and NVDAx". The fact that an asset is represented by a token onchain is the baseline assumption of everything here, not a notable property. Only mention tokenization when the act of representing an offchain asset onchain is itself the subject (e.g. explaining how an issuer mints a token backed by a real-world asset).
- **An invariant is an "invariant", never a "property".** "Property" also names an account's fields and a struct's members, so "property test", "the conservation property", and "pins the property" each read two ways, and one manuscript used the word both ways in the same chapter. Say "invariant test", "the conservation invariant", "pins the invariant"; where the thing is a rule one handler enforces rather than a condition that holds across every state change, say "rule". "Property" stays for a characteristic ("a PDA has three properties", "a good crank has three properties"). Name the testing libraries' own term, "property-based testing", at most once, so a reader can find those libraries.
- **One name per role/concept, enforced everywhere.** Pick a single term for each party (lessor/lessee, maker/taker, long/short, borrower/lender) and use ONLY that term throughout. Mixing terminology mid-document is how readers lose track of who owes what to whom.
- **Don't conflate "long the collateral" with "long the trade".** Anyone who posts collateral wants it to hold value (otherwise margin call), so every borrower is long their collateral. The directional bet is on the _borrowed_ asset, separately. Be precise about which "long" you mean.
- **Be careful with the word "securities".** It's a legal term. SOL is not a security. Asset-leasing is not "securities lending" even when the mechanics are analogous. Prefer "asset lending", "token lending", or "directional token lending" - and ask before picking one.
- **"Drain" is the attacker's word.** A vault is drained by an exploit: a rounding drain, an attacker draining the pool. A handler that legitimately moves a vault's whole balance out on its way to closing it *empties* it or *pays it out*: "both exits empty the vault", "the claim pays out the vault's full balance", never "both exits drain the vault". One word for both makes the program's own lifecycle read as an incident.
- **Closing an account returns its rent; say so.** Write "closes the option account and returns its rent to the writer", never "closes the option account to the writer". The second is Anchor's `close = writer` read aloud, and it tells the reader nothing about where the lamports go.
- **Spell out two-asset flows with concrete examples.** "Posts collateral and takes delivery of borrowed tokens" reads circular. "Posts USDC as collateral, borrows NVDAx" makes the asymmetry obvious. Don't make the reader infer that mints A and B are different things.
- **Name the instruction handlers in lifecycle prose.** When walking through "what the user does" (open position, close position, liquidate), name the actual handler (`take_lease`, `return_lease`, `liquidate`). Plain-English mechanics without handler names leave the reader unable to connect the narrative to the code.
- **A flaw in the program you are writing about gets fixed, not written up.** If the program is open to an attack or can wedge itself (a donation inflating share prices, an order book that fills with no eviction handler), fix the program and open a pull request with a test that proves the fix, then describe the fixed behaviour. A paragraph warning the reader about the flaw is the prose form of a caveat that should have been a fix. The details are in [SUMMARIZING-PROGRAMS.md](https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/SUMMARIZING-PROGRAMS.md) under _Fix a flawed program - don't document the flaw_.
- **When you explain or summarize a program** - a README, walkthrough, video script, or answer - follow [SUMMARIZING-PROGRAMS.md](https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/SUMMARIZING-PROGRAMS.md): persona-driven casts, real assets, correct incentives, and step-by-step account state-transition + token-movement ledgers.

## General Coding Guidelines

### You are a deletionist

Your golden rule is "perfection isn't achieved when there's nothing more to add, rather perfection is achieved when there is nothing more to be taken away".

Remove:

- Comments that simply repeat what the code is doing, or the name of a variable, and do not add further insight.
- Repeated code that should be turned into a named function.
- Unused imports, unused constants, unused files, and comments that no longer apply.

Before deleting "stale" scaffolding, confirm it is actually dead: grep the CI workflows (`.github/workflows/`) and package scripts for references. Test files and package.json scripts that look like leftovers are sometimes exactly what CI runs.
- Doc-comments whose first line just paraphrases the identifier. `/// Pool authority PDA.` above `pub pool_authority` is noise. Either explain something the name doesn't (seed derivation, mutability rationale, type-choice reason, an invariant the reader can't see from the type) or delete the line.

Don't remove existing comments unless they are no longer useful or accurate.

### Communication Style

- Do not make disclaimers about being a "complete project" or state what works
- It is expected that work is complete and functional - no need to state this explicitly
- Avoid phrases like "This is a complete implementation" or "All features are working"
- Just deliver the work without meta-commentary about its completeness

### Config files: leave a comment explaining WHY

When you change a configuration value, or pin a version in any config file (`Anchor.toml`, `Cargo.toml`, `package.json`, CI workflows, `.gitignore`, `rust-toolchain.toml`), leave a comment explaining _why_. The next reader needs the rationale, not just the value.

- **Pinned versions:** what breaks without the pin? when can it be unpinned?
- **Non-default timeouts / limits:** why this number?
- **Removed sections:** what was it doing? why was it removed?
- **`.gitignore` exceptions:** why is this file tracked despite the rule?
- **Workarounds:** what's the proper fix? when can this be replaced? (mark with `TODO`)

Example:

```toml
# Pinned: 0.8.7 conflicts with litesvm's dep tree.
# Unpin when litesvm upgrades its ahash requirement.
ahash = "=0.8.6"
```

When you remove a section, only add why to the git commit, so the file is free of information that does not apply to its existing state.

### Working with Generated or Unfamiliar Code

**CRITICAL - Verify Before Use:**

- Before calling ANY function whose signature you don't know with certainty, read the actual source code/type definitions first
- NEVER guess or assume what parameters a function accepts based on what seems logical
- Don't invent convenience parameters that don't exist
- Generated code, third-party libraries, and unfamiliar codebases often have different APIs than you expect
- Common mistake: Assuming a function accepts high-level parameters → WRONG. Check the actual signature in the source files first

### Variable Naming

Ensure good variable naming. Rather than add comments to explain what things are, give them useful names.

**Don't do this:**

```typescript
// Foo
const shlerg = getFoo();
```

**Do this instead:**

```typescript
const foo = getFoo();
```

**Naming conventions:**

- Arrays should be plurals (`shoes`), items within arrays should be the singular (`shoes.forEach((shoe) => {...})`)
- Functions should be verby, like `calculateFoo` or `getBar`
- Avoid abbreviations, use full words (e.g., use `context` rather than `ctx`). Never use `e` for something thrown, use `thrownObject`, never use `v` when you mean `value`. There is almost no case where a single character variable is a good idea outside math (eg `p` and `q` for cryptography).
- Name a transaction some variant of `transaction`. Name instructions some variant of `instruction`. Name signatures some variant of `signature`. Do not confuse them - eg if the type looks like an instruction, you should not call it a 'transaction' because that is deceptive.
- A name must make sense inside the thing that holds it, and one word means one thing per program. An account struct `OptionContract` with a field `contracts` reads as a contract that contains contracts, because "contract" names both the account and a unit inside it. Pick one meaning and rename the other.
- Store the quantities the program uses, not the factors it multiplies back together. If every handler computes `contracts * underlying_per_contract`, the account should store `underlying_amount`. Fields copied from traditional finance's conventions (contract sizes, lots, per-unit prices) are the usual source: keep one only if the program acts on it separately, for example by exercising or transferring part of an option.

You can still add comments for additional context, just be careful to avoid comments that are explaining things that would be better conveyed by good variable naming.

### Code Quality

- Avoid 'magic numbers'. Make numbers either have a good variable name, a comment explaining why they are that value, or a reference to the URL you got the value from. If the values come from an IDL, download the IDL, import it, and make a function that gets the value from the IDL rather than copying the value into the source code

This is a magic number. Don't do this:

```ts
const FINALIZE_EVENT_DISCRIMINATOR = new Uint8Array([
  27, 75, 117, 221, 191, 213, 253, 249,
]);
```

Instead do this:

```ts
const FINALIZE_EVENT_DISCRIMINATOR = getEventDiscriminator(
  arciumIdl,
  "FinalizeComputationEvent",
);
```

- The code you are making is for production. You shouldn't have comments like `// In production we'd do this differently` or `**Implementation incomplete** - Needs program config handling and proper PDA derivations` or `**WORK IN PROGRESS**` in the final code you produce, or functions that return placeholder data. Instead: do the fucking work.

## Language-Specific Guidelines

The rules above apply to every file in the project. In addition, read the file that matches the language you are editing:

- **TypeScript** (Solana Kit clients, Solana Kit tests, browser code, anything `.ts`): see [TYPESCRIPT.md](https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/TYPESCRIPT.md)
- **Rust - any Solana program/crate** (financial math, checked arithmetic, project structure, cargo hygiene): see [RUST.md](https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/RUST.md)
- **Rust - Anchor** (`.rs` files using `anchor_lang`, LiteSVM tests): see [ANCHOR.md](https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/ANCHOR.md), plus RUST.md for the shared rules
- **Rust - Anchor 2.0.0-rc.1** (porting a 1.x program, or writing v2): see [ANCHOR-V2.md](https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/ANCHOR-V2.md), plus ANCHOR.md and RUST.md
- **Rust - Quasar** (`.rs` files using `quasar_lang`/`quasar_spl`/`quasar_test`): see [QUASAR.md](https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/QUASAR.md), plus RUST.md for the shared rules

If a task touches more than one, read each.

For setting up a Solana toolchain in CI or a fresh remote container (Agave, platform-tools behind TLS-intercepting proxies, the Quasar CLI, building Anchor projects without the anchor CLI): see [ENVIRONMENT.md](https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/ENVIRONMENT.md).

When you draw diagrams of Solana accounts (SVG figures for books, docs sites, or presentations): see [DIAGRAMS.md](https://github.com/quiknode-labs/solana-anchor-claude-skill/blob/a440e16f752139031840d606e1363637404e5d01/skills/solana/DIAGRAMS.md).

## Git commits

Do not add "Co-Authored-By: Claude" or similar attribution when creating git commits.

Use the Linux/Git style `scope: description` for commit titles. [Do not use 'Conventional commits'](https://sumnerevans.com/posts/software-engineering/stop-using-conventional-commits/).

## Acknowledgment

- Acknowledge these guidelines have been applied when working on this project to indicate you have read these rules and found that they do apply to this project.
