Solana program examples in three flavors per example: anchor/, native/, pinocchio/ (a few have asm/). Path convention: <category>/<example-name>/<framework>.
- Not a pnpm workspace, on purpose. Every example has its own
package.jsonandpnpm-lock.yamlso it can be copied out and run standalone. Never introduce cross-example imports or shared JS helpers. Align dependency versions withpnpm sync-package-jsonfrom the root. - Rust is one workspace. Most program crates are members of the root
Cargo.toml. Crates that can't be members are listed in.github/.workspace-ignore(CI enforces one or the other).tokens/token-2022/transfer-hook/block-list/pinocchioandgames/world-cup/pinocchiohave their own workspaces. - Toolchain pins:
rust-toolchain.toml,.nvmrc,packageManagerin the rootpackage.json,anchor_version/solana_versionin everyAnchor.toml.
| Framework | Build + test |
|---|---|
| anchor | anchor test in the project ([scripts] test in Anchor.toml runs mocha) |
| native / pinocchio | pnpm build-and-test (cargo build-sbf into tests/fixtures/, then pnpm test) |
| asm | same script shape; programs assemble with sbpf (rev pinned in .github/actions/setup/action.yml) |
| world-cup | just setup && just build && just test — excluded from the pinocchio workflow by design |
Rust integration tests live in program/tests/*.rs (litesvm) and run with cargo test --manifest-path=./program/Cargo.toml; CI runs them only when program/Cargo.toml exists.
- Runner: mocha 11 via tsx (
mocha --import=tsx …). Never ts-mocha, ts-node, jest, ornode:testimports —node:testsuites under mocha exit 0 even when failing. - Runtime: LiteSVM. Non-anchor tests use
@solana/kit+litesvm1.x (pattern:basics/hello-solana/native/tests/index.test.ts, full versiontokens/create-token/pinocchio/tests/test.ts). Anchor tests use@anchor-lang/core+anchor-litesvm+litesvm0.8 with@solana/web3.js— deliberate, anchor's JS client is web3.js-based until it moves to kit. - litesvm never throws on a failed transaction: assert
result instanceof FailedTransactionMetadata. Sending the same bytes twice needssvm.expireBlockhash()in between. - anchor-litesvm pins its own old litesvm; every anchor project carries
pnpm.overrides { "litesvm": "^0.8.0" }. Without it, failed transactions passinstanceofchecks silently. - Tests must assert real post-state (account bytes, lamport deltas) and fail loudly — verify by breaking an assertion once.
- TS/MD/JSON:
pnpm format/pnpm run checkat the root (prettier,@solana/prettier-config-solana). Rust:cargo fmtat the root (sharedrustfmt.toml); clippy runs with-D warningsin CI. - Workflows discover projects by directory name (
anchor,native,pinocchio,asm)..github/.ghaignorelists CI-skipped projects — every entry needs a comment with the real reason; verify a reason still holds before trusting it. - Per-project CI:
pnpm install --frozen-lockfile(commit lockfiles),tsc --noEmitwhen atsconfig.jsonexists (keepskipLibCheck), build, test.
- Resolver-2 feature unification: a crate must declare every feature-gated dependency it uses itself (e.g.
solana-addresswithcurve25519/decode). Whole-workspace builds mask missing features that per-crate CI builds expose. anchor keys syncrewritesdeclare_id!and strips Anchor.toml comments. Don't run it casually;basics/cross-program-invocation/anchorhas committed keypairs with a drift guard — never resync it.- Fixtures under
tests/fixtures/are gitignored and built on demand; Metaplextoken_metadata.sois dumped from mainnet by each project'sprepare.mjspostinstall. The metadata natives build Metaplex instructions by hand inmpl_util.rs— there is no mpl crate dependency; keep it that way. - Old runtimes (bankrun) and old harnesses were removed deliberately; don't reintroduce them from upstream examples or old tutorials.