diff --git a/solana-anchor-skill/LICENSE b/solana-anchor-skill/LICENSE new file mode 100644 index 0000000..5c05be1 --- /dev/null +++ b/solana-anchor-skill/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Solana Anchor Skill contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/solana-anchor-skill/README.md b/solana-anchor-skill/README.md new file mode 100644 index 0000000..c37f473 --- /dev/null +++ b/solana-anchor-skill/README.md @@ -0,0 +1,181 @@ +# Solana Anchor Skill + +A kit-style skill for scaffolding Solana Anchor projects, recovering missing Anchor CLI setup, and generating comprehensive runnable tests for Anchor programs. + +This skill focuses on a common builder pain: Anchor projects are easy to initialize, but real test coverage is often inconsistent, under-specified, or blocked by local toolchain issues. The skill routes agents through preflight checks, transparent CLI recovery, test harness detection, IDL/source-driven test generation, and coverage-gap reporting. + +## What It Does + +- Scaffolds Anchor projects after checking the local CLI is available. +- Documents an approval-based AVM recovery path when `anchor` is missing. +- Detects existing test setup before generating files. +- Generates TypeScript/Mocha tests with `@coral-xyz/anchor` and Chai when TypeScript is detected. +- Generates Rust integration tests when `solana-program-test` or LiteSVM is detected. +- Defaults to `anchor test` against a local validator. +- Routes to Surfpool only when external mainnet-forked state is required. +- Reports tested instructions, tested constraints, edge cases, and coverage gaps. + +### Execution Flow + +```text +┌──────────────────────────────┐ +│ Developer request │ +│ scaffold, test, or recover │ +└───────────────┬──────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ Skill preflight │ +│ inspect repo + Anchor setup │ +└───────────────┬──────────────┘ + │ + ┌─────────┴─────────┐ + │ │ + ▼ ▼ +┌──────────────┐ ┌──────────────────────────┐ +│ anchor found │ │ anchor missing │ +│ continue │ │ request AVM recovery │ +└──────┬───────┘ └────────────┬─────────────┘ + │ │ + │ ▼ + │ ┌────────────────────────┐ + │ │ Install/activate Anchor │ + │ │ cargo install avm │ + │ │ avm install/use latest │ + │ │ hydrate PATH │ + │ └───────────┬────────────┘ + │ │ + └──────────────┬───────────┘ + ▼ +┌──────────────────────────────┐ +│ Scaffolding or test planning │ +│ read Anchor.toml, IDL, tests │ +└───────────────┬──────────────┘ + │ + ┌─────────┴──────────┐ + │ │ + ▼ ▼ +┌────────────────┐ ┌──────────────────────┐ +│ TypeScript test │ │ Rust test setup │ +│ Mocha + Chai │ │ program-test/LiteSVM │ +└────────┬────────┘ └──────────┬───────────┘ + │ │ + └───────────┬───────────┘ + ▼ +┌──────────────────────────────┐ +│ Generate runnable tests │ +│ happy paths, failures, PDAs │ +│ numeric and authority edges │ +└───────────────┬──────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ Run local validator by default│ +│ Surfpool only for live state │ +└───────────────┬──────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ Coverage-gap report │ +│ tested vs blocked/manual work │ +└──────────────────────────────┘ +``` + +## Repository Structure + +```text +solana-anchor-skill/ +├── README.md +├── LICENSE +├── install.sh +├── agents/ +│ └── openai.yaml +├── commands/ +│ ├── anchor-generate-tests.md +│ ├── anchor-scaffold.md +│ └── anchor-test.md +└── skill/ + ├── SKILL.md + ├── cli-installation.md + ├── anti-patterns.md + ├── program-patterns.md + ├── resources.md + ├── rust-tests.md + ├── scaffolding.md + ├── testing-anchor.md + └── typescript-tests.md +``` + +## Installation + +Clone the repo and run the installer: + +```bash +git clone https://github.com/manueldezman/solana-anchor-skill.git +cd solana-anchor-skill +./install.sh +``` + +By default, the installer copies `skill/` to: + +```text +~/.codex/skills/solana-anchor +``` + +Install for Claude-compatible skill directories: + +```bash +./install.sh --target claude +``` + +Install non-interactively: + +```bash +./install.sh -y +``` + +Choose a custom destination: + +```bash +./install.sh --dest /path/to/skills/solana-anchor +``` + +## Usage Examples + +The skill can be triggered naturally by Anchor-related requests; users do not have to type `Use $solana-anchor`. Explicit invocation still works when they want to force this skill. + +```text +Create a new Anchor project named escrow. +``` + +```text +Generate tests for this Anchor program. Detect whether it uses TypeScript or Rust tests first. +``` + +```text +anchor command not found. Fix my Anchor CLI setup, then run anchor init. +``` + +```text +Debug PDA seeds in this Anchor instruction. +``` + +```text +Add failure-path tests for wrong signer, wrong PDA seeds, duplicate init, and numeric overflow. +``` + +## Testing Expectations + +Generated tests should be runnable and should match the target project: + +- TypeScript projects use `@coral-xyz/anchor`, Mocha, and Chai. +- Rust test projects use the existing `solana-program-test` or LiteSVM pattern. +- Anchor `1.1.2` scaffolds Rust/LiteSVM tests by default, so new-project test generation should inspect the generated layout before assuming TypeScript. +- Local validator is the default for validator-backed tests; LiteSVM scaffolds can use `skip_local_validator = true`. +- Surfpool is reserved for programs that need forked external state. + +Every test-generation task should end with a coverage-gap table so the developer can see what was generated and what still needs manual fixtures or live-state setup. + +## License + +MIT. See [LICENSE](LICENSE). diff --git a/solana-anchor-skill/SUBMISSION.md b/solana-anchor-skill/SUBMISSION.md new file mode 100644 index 0000000..02a06c7 --- /dev/null +++ b/solana-anchor-skill/SUBMISSION.md @@ -0,0 +1,34 @@ +# Solana Anchor Skill Submission + +Canonical repo: https://github.com/manueldezman/solana-anchor-skill + +## What it does + +This skill extends Codex with a production-oriented Solana Anchor workflow: + +- Create new Anchor projects with preflight checks and transparent CLI recovery. +- Install or repair Anchor CLI/AVM setup without hidden automation. +- Build, review, and debug Anchor programs, accounts, constraints, PDAs, seeds, and bumps. +- Detect an existing Anchor test harness before generating tests. +- Generate runnable TypeScript/Mocha or Rust integration tests for IDL instructions, constraints, PDAs, authority checks, and numeric boundaries. +- Default to local `anchor test`, with Surfpool documented as an opt-in path for live external state. + +## Validation + +- `install.sh` passes shell syntax validation. +- Skill routing and trigger metadata were reviewed with `rg`. +- A disposable Anchor fixture was created and `anchor test` completed successfully. +- Toolchain and scaffold findings were converted into reusable anti-pattern guidance rather than incident-log notes. + +## Structure + +The submission follows the kit-style layout: + +- `README.md` +- `LICENSE` +- `install.sh` +- `agents/openai.yaml` +- `commands/` +- `skill/SKILL.md` +- focused skill docs under `skill/` + diff --git a/solana-anchor-skill/agents/openai.yaml b/solana-anchor-skill/agents/openai.yaml new file mode 100644 index 0000000..4589d2d --- /dev/null +++ b/solana-anchor-skill/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Solana Anchor" + short_description: "Scaffold, debug, and test Anchor programs" + default_prompt: "Use $solana-anchor to scaffold Anchor projects, install or recover Anchor CLI setup, build/debug programs, or generate runnable tests." + +policy: + allow_implicit_invocation: true diff --git a/solana-anchor-skill/commands/anchor-generate-tests.md b/solana-anchor-skill/commands/anchor-generate-tests.md new file mode 100644 index 0000000..1bfc927 --- /dev/null +++ b/solana-anchor-skill/commands/anchor-generate-tests.md @@ -0,0 +1,20 @@ +# /anchor-generate-tests + +Generate runnable Anchor program tests that match the target project's existing test harness. + +## Procedure + +1. Read `skill/testing-anchor.md`. +2. Inspect `Anchor.toml`, package manifests, Rust manifests, existing tests, and IDL. +3. Select TypeScript or Rust based on detected project style. +4. Read `skill/typescript-tests.md` or `skill/rust-tests.md`. +5. Generate runnable tests for IDL instructions, constraints, PDAs, authority checks, and numeric boundaries. +6. Run the project test command when feasible. +7. Finish with the coverage-gap report table. + +## Expected Output + +- Detected framework and why. +- Test files created or updated. +- Test command run and result. +- Coverage-gap table. diff --git a/solana-anchor-skill/commands/anchor-scaffold.md b/solana-anchor-skill/commands/anchor-scaffold.md new file mode 100644 index 0000000..8f98b74 --- /dev/null +++ b/solana-anchor-skill/commands/anchor-scaffold.md @@ -0,0 +1,19 @@ +# /anchor-scaffold + +Scaffold a new Anchor project with transparent CLI preflight and recovery. + +## Procedure + +1. Read `skill/SKILL.md`. +2. Read `skill/scaffolding.md` and `skill/cli-installation.md`. +3. Run `anchor --version`. +4. If Anchor is missing, ask before AVM install/recovery commands. +5. Run `anchor init ` only after the CLI is available. +6. Inspect generated files and report the created project structure. + +## Expected Output + +- Anchor version used. +- Project path created. +- Any installation commands run or skipped. +- Next recommended test command. diff --git a/solana-anchor-skill/commands/anchor-test.md b/solana-anchor-skill/commands/anchor-test.md new file mode 100644 index 0000000..deb723e --- /dev/null +++ b/solana-anchor-skill/commands/anchor-test.md @@ -0,0 +1,18 @@ +# /anchor-test + +Run or configure Anchor tests with the correct local validator or Surfpool environment. + +## Procedure + +1. Read `skill/testing-anchor.md`. +2. Default to `anchor test`. +3. Inspect whether the program reads external mainnet state. +4. Use Surfpool only when external state cannot be faked locally. +5. Report the environment choice and any accounts that require forked state. + +## Expected Output + +- Chosen test environment. +- Command to run. +- External state requirements, if any. +- Test result or blocker. diff --git a/solana-anchor-skill/install.sh b/solana-anchor-skill/install.sh new file mode 100755 index 0000000..e5de7c9 --- /dev/null +++ b/solana-anchor-skill/install.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOURCE_DIR="$SCRIPT_DIR/skill" +DEFAULT_CODEX_DEST="$HOME/.codex/skills/solana-anchor" +DEFAULT_CLAUDE_DEST="$HOME/.claude/skills/solana-anchor" + +TARGET="codex" +DEST="" +YES=false + +print_help() { + cat <<'USAGE' +Solana Anchor Skill installer + +Usage: + ./install.sh [options] + +Options: + --target codex Install to ~/.codex/skills/solana-anchor (default) + --target claude Install to ~/.claude/skills/solana-anchor + --dest PATH Install to a custom skill directory + -y, --yes Skip confirmation prompt + -h, --help Show help +USAGE +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --target) + if [[ $# -lt 2 || "${2:-}" == --* ]]; then + echo "--target requires a value: codex or claude" >&2 + exit 1 + fi + TARGET="${2:-}" + shift 2 + ;; + --dest) + if [[ $# -lt 2 || "${2:-}" == --* ]]; then + echo "--dest requires a path" >&2 + exit 1 + fi + DEST="${2:-}" + shift 2 + ;; + -y|--yes) + YES=true + shift + ;; + -h|--help) + print_help + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + print_help + exit 1 + ;; + esac +done + +if [[ ! -d "$SOURCE_DIR" ]]; then + echo "Cannot find skill payload at $SOURCE_DIR" >&2 + exit 1 +fi + +if [[ -z "$DEST" ]]; then + case "$TARGET" in + codex) + DEST="$DEFAULT_CODEX_DEST" + ;; + claude) + DEST="$DEFAULT_CLAUDE_DEST" + ;; + *) + echo "Unsupported target: $TARGET" >&2 + echo "Use --target codex or --target claude." >&2 + exit 1 + ;; + esac +fi + +echo "Solana Anchor Skill installer" +echo "" +echo "Source: $SOURCE_DIR" +echo "Destination: $DEST" +echo "" + +if [[ "$YES" != true ]]; then + read -r -p "Install skill to this destination? [Y/n] " reply + if [[ "$reply" =~ ^[Nn]$ ]]; then + echo "Installation cancelled." + exit 0 + fi +fi + +if [[ -d "$DEST" ]]; then + backup="$DEST.backup.$(date +%Y%m%d%H%M%S)" + echo "Existing installation found. Moving it to $backup" + mv "$DEST" "$backup" +fi + +mkdir -p "$(dirname "$DEST")" +mkdir -p "$DEST" +cp -R "$SOURCE_DIR"/. "$DEST"/ + +echo "" +echo "Installed solana-anchor skill to:" +echo " $DEST" +echo "" +echo "Try:" +echo ' Generate tests for this Anchor program. Detect the existing Anchor test setup first.' diff --git a/solana-anchor-skill/skill/SKILL.md b/solana-anchor-skill/skill/SKILL.md new file mode 100644 index 0000000..5e594e7 --- /dev/null +++ b/solana-anchor-skill/skill/SKILL.md @@ -0,0 +1,55 @@ +--- +name: solana-anchor +description: Use this skill for Solana Anchor work including anchor init, scaffold Anchor project, scaffold Anchor app, create Solana Anchor program, install Anchor CLI, anchor command not found, install AVM, fix Anchor setup, write Anchor instruction, review Anchor account, debug Anchor constraint, debug PDA seeds, bump issues, anchor test, test Anchor program, generate Anchor tests, TypeScript Anchor tests, Rust Anchor tests, LiteSVM, solana-program-test, and Surfpool test environments. +user-invocable: true +--- + +# Solana Anchor Skill + +Use this skill for Anchor program scaffolding, CLI setup recovery, account/PDA design, and production-grade test generation. + +## What This Skill Is For + +- Initialize new Anchor projects after checking the host has a working CLI. +- Recover missing Anchor CLI installations through transparent AVM-based steps. +- Design and review Anchor accounts, instructions, constraints, and PDA flows. +- Detect an existing Anchor project's test setup before generating tests. +- Generate runnable `.ts` or `.rs` tests that match the detected framework. +- Wire local validator tests by default, with Surfpool as an opt-in path for external mainnet state. + +## Operating Procedure + +1. Inspect the target repo before deciding what to generate. +2. For new projects, read [scaffolding.md](scaffolding.md) and [cli-installation.md](cli-installation.md). +3. For program design, account sizing, or PDA work, read [program-patterns.md](program-patterns.md). +4. For tests, read [testing-anchor.md](testing-anchor.md), then load either [typescript-tests.md](typescript-tests.md) or [rust-tests.md](rust-tests.md) based on the detected harness. +5. Finish test-generation tasks with a coverage-gap report. + +## Default Decisions + +- Do not assume Anchor's default TypeScript harness. Detect what the project already uses. +- Prefer `anchor test` against a local validator unless the program depends on external state that cannot be mocked locally. +- Use Surfpool only when live/forked accounts are necessary, such as oracle or protocol accounts. +- Keep generated tests runnable and idiomatic for the existing project. +- Ask before running install commands that download dependencies or alter the host toolchain. + +## Progressive Disclosure + +| Task | Read | +|------|------| +| New project scaffold | [scaffolding.md](scaffolding.md), [cli-installation.md](cli-installation.md) | +| Missing Anchor CLI | [cli-installation.md](cli-installation.md) | +| Account/PDA design | [program-patterns.md](program-patterns.md) | +| Generate tests | [testing-anchor.md](testing-anchor.md) | +| TypeScript/Mocha tests | [typescript-tests.md](typescript-tests.md) | +| Rust integration tests | [rust-tests.md](rust-tests.md) | +| Guardrails and anti-patterns | [anti-patterns.md](anti-patterns.md) | +| Links and ecosystem references | [resources.md](resources.md) | + +## Delivery Expectations + +- Name the detected Anchor version, test harness, and validator mode when relevant. +- Keep changes consistent with the project's existing account derivation, helper, and assertion style. +- Generate real test files, not pseudocode or TODO-only skeletons. +- Explain any generated test that cannot be made fully runnable without external state, fixtures, or missing IDL details. +- Report which instructions, constraints, PDA assertions, numeric boundaries, and authority checks were covered. diff --git a/solana-anchor-skill/skill/anti-patterns.md b/solana-anchor-skill/skill/anti-patterns.md new file mode 100644 index 0000000..81380f0 --- /dev/null +++ b/solana-anchor-skill/skill/anti-patterns.md @@ -0,0 +1,30 @@ +# Anti-Patterns + +Use this file when reviewing generated plans, scaffolds, tests, or install recovery steps. + +## Toolchain And Setup + +- Do not install or modify Anchor, AVM, Solana CLI, PATH, or shell profiles without user approval. +- Do not claim recovery succeeded until `anchor --version`, `avm --version`, and `solana --version` pass in the active shell. +- Do not assume AVM shims are healthy. If `anchor --version` hangs, test the versioned binary under `~/.avm/bin/anchor-`. +- Do not treat a quiet installer as failed too quickly. Solana CLI and AVM installs can be silent for several minutes. + +## Scaffolding + +- Do not assume new Anchor projects use TypeScript tests. Anchor `1.1.2` scaffolds Rust/LiteSVM by default. +- Do not infer test language from habit. Inspect `Anchor.toml`, `package.json`, `Cargo.toml`, and existing tests first. +- Do not overwrite generated account, instruction, or test structure without reading the generated project. + +## Testing + +- Do not run raw `cargo test` first when LiteSVM tests include `target/deploy/.so`; run `anchor test` or `anchor build` first. +- Do not use Surfpool by default. Use it only when external mainnet state cannot be faked locally. +- Do not generate pseudocode tests. Generate runnable `.ts` or `.rs` files that match the detected harness. +- Do not claim full coverage when external accounts, stale IDLs, missing fixtures, or unknown constraints block a generated test. + +## Program Logic + +- Do not duplicate PDA seed logic in many files when a local helper already exists. +- Do not trust client-side authority checks; assert authority on-chain and test failure paths. +- Do not mutate accounts without confirming `mut` constraints and account lifecycle behavior. +- Do not change account layout without checking discriminator, dynamic field space, rent, and migration assumptions. diff --git a/solana-anchor-skill/skill/cli-installation.md b/solana-anchor-skill/skill/cli-installation.md new file mode 100644 index 0000000..69bfeb2 --- /dev/null +++ b/solana-anchor-skill/skill/cli-installation.md @@ -0,0 +1,91 @@ +# Anchor CLI Installation And Recovery + +Use this file when `anchor --version` fails, Anchor CLI is missing, or a scaffold cannot proceed because the host is not configured. + +## Safety Rules + +- Never install or modify the host toolchain silently. +- Explain the commands before running them. +- Ask for approval before commands that download dependencies, install binaries, or alter PATH/symlinks. +- If Rust or Cargo is missing, stop and report that Rust must be installed before AVM can be built. + +## Detection + +Run: + +```bash +anchor --version +``` + +Interpretation: + +- Exit code `0` with a version string: continue to scaffold/build/test. +- Command not found or exit code `127`: Anchor CLI is absent; offer AVM recovery. +- Other failures: report stderr and inspect PATH, shell profile, and `~/.avm/bin`. + +## AVM Recovery Flow + +Use the official Anchor Version Manager flow. Warn the user that the first command can take several minutes because it builds AVM from source: + +```bash +cargo install --git https://github.com/solana-foundation/anchor avm --force +avm install latest +avm use latest +anchor --version +``` + +If the current shell does not see newly installed binaries, add these paths for the active command session: + +```bash +export PATH="$HOME/.cargo/bin:$HOME/.avm/bin:$PATH" +``` + +Then retry: + +```bash +anchor --version +``` + +## Solana Toolchain Reality Check + +For full `anchor test` validation, `solana` must be available too: + +```bash +solana --version +``` + +If `solana` is missing, use the official Solana installer instructions from `https://solana.com/docs/intro/installation`, but do not assume the all-in-one installer is non-interactive. On Debian/Ubuntu hosts it may attempt `sudo` for system packages before continuing. If `sudo` cannot prompt for a password in the current shell, report that clearly and switch to the smallest user-approved install path available for the task. + +Official individual Solana CLI install command: + +```bash +sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)" +``` + +Production install notes: + +- The official Solana quick installer may update Rust before installing Solana CLI. +- It may attempt apt package installation through `sudo`; non-interactive shells can fail if a password prompt is required. +- The individual Solana CLI installer can sit silently at "downloading stable installer" for several minutes and still complete successfully. Wait before declaring it stalled. +- A successful Solana CLI install may add `export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"` to `~/.profile`; hydrate PATH manually in the current shell before verifying. +- `cargo install --git https://github.com/solana-foundation/anchor avm --force` can spend a long time compiling. Keep progress updates honest and wait for completion before judging the install. +- AVM's generated `anchor` shim may hang while the versioned binary works. Verify `$HOME/.avm/bin/anchor- --version`; if it works, repair PATH-visible symlinks or report the shim issue. +- After any interrupted install, re-run `anchor --version`, `solana --version`, and `avm --version` before assuming anything was installed. + +## Messaging Guidance + +Use concise progress updates: + +- "Anchor CLI was not found. I can install AVM and activate the latest stable Anchor version." +- "AVM is installed; now building and selecting the Anchor CLI version." +- "Anchor is available; continuing with the scaffold." + +## Failure Handling + +- If `cargo install` fails because Rust is missing, direct the user to install Rust first. +- If the official Solana installer fails because `sudo` cannot prompt, report that host package installation is blocked and ask whether to continue with user-space Anchor-only recovery. +- If Solana CLI is still missing after Anchor installs, do not claim `anchor test` is fully validated; report that local-validator testing remains blocked. +- If AVM compilation stalls or is interrupted, verify whether `avm` exists before continuing. +- If `anchor --version` hangs through the AVM shim, test the versioned binary directly and report or repair the symlink target. +- If `avm install latest` fails, retry with the version required by the project if `Anchor.toml` or `Cargo.toml` indicates one. +- If PATH hydration fails, report the exact binary paths checked and the command output. diff --git a/solana-anchor-skill/skill/program-patterns.md b/solana-anchor-skill/skill/program-patterns.md new file mode 100644 index 0000000..bce6190 --- /dev/null +++ b/solana-anchor-skill/skill/program-patterns.md @@ -0,0 +1,42 @@ +# Anchor Program Patterns + +Use this file for account design, PDA derivation, constraints, and program review. + +## Account Modeling + +Always check: + +- 8-byte Anchor discriminator. +- fixed-width scalar sizes. +- 4-byte prefixes for `String` and `Vec`. +- 1-byte tag for `Option`. +- max lengths for dynamic fields. +- whether `realloc` changes rent or compatibility assumptions. + +Prefer the existing project style for space constants, such as `INIT_SPACE`, `MAX_SIZE`, or manual `space = 8 + ...`. + +## PDA Design + +- Use durable seeds and keep seed order identical across program and client/tests. +- Mirror byte encoding exactly: string literals, pubkey buffers, and little-endian integers must match on both sides. +- Store bumps when the project already does or when the PDA signs CPIs in multiple places. +- Add PDA derivation assertions in generated tests whenever `seeds` and `bump` constraints are present. + +## Constraint Review + +Prefer Anchor constraints when they express the rule cleanly: + +- `mut` for mutated accounts. +- `Signer` for transaction signers. +- `has_one` for stored authority relationships. +- `seeds` and `bump` for PDA identity. +- `init`, `init_if_needed`, `close`, and `realloc` for lifecycle behavior. +- `constraint = ... @ ErrorCode::...` for business rules. + +## Common Bugs + +- Client and program derive a PDA with different seed bytes. +- Account data is changed without `mut`. +- A signer is implied by business logic but not declared in the context. +- Duplicate initialization tests are missing for `init` accounts. +- Authority checks rely on client behavior instead of on-chain constraints. diff --git a/solana-anchor-skill/skill/resources.md b/solana-anchor-skill/skill/resources.md new file mode 100644 index 0000000..418e468 --- /dev/null +++ b/solana-anchor-skill/skill/resources.md @@ -0,0 +1,35 @@ +# Resources + +Use these resources when current ecosystem details matter or when a project depends on a specific tool version. + +## Primary Documentation + +- Anchor framework: `https://www.anchor-lang.com/` +- Anchor GitHub and AVM: `https://github.com/solana-foundation/anchor` +- Solana installation: `https://solana.com/docs/intro/installation` +- Solana program testing: `https://solana.com/docs/programs/testing` +- Solana local validator: `https://solana.com/docs/references/cli/solana-test-validator` +- LiteSVM: `https://github.com/LiteSVM/litesvm` +- Surfpool: `https://github.com/txtx/surfpool` + +## Version Checks + +Check local versions before applying version-specific advice: + +```bash +anchor --version +solana --version +cargo --version +node --version +``` + +## Useful Files In Anchor Projects + +- `Anchor.toml` +- `Cargo.toml` +- `programs/*/Cargo.toml` +- `programs/*/src/lib.rs` +- `tests/*` +- `target/idl/*.json` +- `package.json` +- `.mocharc*` diff --git a/solana-anchor-skill/skill/rust-tests.md b/solana-anchor-skill/skill/rust-tests.md new file mode 100644 index 0000000..3deb8e7 --- /dev/null +++ b/solana-anchor-skill/skill/rust-tests.md @@ -0,0 +1,105 @@ +# Rust Anchor Tests + +Use this file when the project already uses Rust integration tests with `solana-program-test` or LiteSVM. + +## Detection + +- Use `solana-program-test` patterns when `Cargo.toml` includes `solana-program-test` or existing tests use `ProgramTest`. +- Use LiteSVM patterns when `Cargo.toml` includes `litesvm` or existing tests instantiate `LiteSVM`. +- Match existing helper modules, account serialization helpers, and transaction wrappers. + +## solana-program-test Shape + +Generate runnable Rust integration tests that follow the project imports. A minimal pattern is: + +```rust +use anchor_lang::{InstructionData, ToAccountMetas}; +use solana_program_test::*; +use solana_sdk::{ + instruction::Instruction, + signature::{Keypair, Signer}, + transaction::Transaction, +}; + +#[tokio::test] +async fn initializes_state() { + let program_id = program_name::id(); + let mut program_test = ProgramTest::new( + "program_name", + program_id, + processor!(program_name::entry), + ); + + let (mut banks_client, payer, recent_blockhash) = program_test.start().await; + let authority = Keypair::new(); + + let (state, bump) = Pubkey::find_program_address( + &[b"state", authority.pubkey().as_ref()], + &program_id, + ); + + let ix = Instruction { + program_id, + accounts: program_name::accounts::Initialize { + state, + authority: authority.pubkey(), + system_program: solana_sdk::system_program::ID, + } + .to_account_metas(None), + data: program_name::instruction::Initialize {}.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&payer.pubkey()), + &[&payer, &authority], + recent_blockhash, + ); + + banks_client.process_transaction(tx).await.unwrap(); + + let account = banks_client.get_account(state).await.unwrap().unwrap(); + let data = program_name::State::try_deserialize(&mut account.data.as_ref()).unwrap(); + assert_eq!(data.authority, authority.pubkey()); + assert_eq!(data.bump, bump); +} +``` + +Replace all program, account, and instruction names with the real crate exports. + +## LiteSVM Shape + +When LiteSVM is detected, use the existing project pattern. The generated test must: + +- register the program with the target program ID. +- fund required signers. +- build the instruction from Anchor-generated `instruction` and `accounts` modules. +- submit the transaction through LiteSVM. +- fetch and deserialize account state. + +Anchor `1.1.2` scaffold note: + +- generated tests are placed at `programs//tests/*.rs`. +- generated LiteSVM tests load the SBF program with `include_bytes!(concat!(env!("CARGO_TARGET_TMPDIR"), "/../deploy/.so"))`. +- run `anchor test` before raw `cargo test` so the deploy artifact exists. +- `Anchor.toml` may set `skip_local_validator = true` because LiteSVM does not need a local validator process. + +## Failure Assertions + +Generate negative tests for: + +- signer mismatch. +- wrong PDA seeds. +- missing initialized account. +- duplicate initialization. +- authority mismatch. +- numeric overflow or underflow. + +Assert concrete transaction errors when the project already has helpers for decoding Anchor errors. Otherwise assert the transaction fails and include a comment naming the expected Anchor error or constraint. + +## Rust Test Hygiene + +- Keep setup helpers in the test module when used by several tests. +- Avoid a separate framework migration unless the user asks for it. +- Match the repo's async runtime and Solana crate versions. +- Use `cargo test` or the repo's documented command when Anchor is not required for the Rust tests. diff --git a/solana-anchor-skill/skill/scaffolding.md b/solana-anchor-skill/skill/scaffolding.md new file mode 100644 index 0000000..eb91ff8 --- /dev/null +++ b/solana-anchor-skill/skill/scaffolding.md @@ -0,0 +1,42 @@ +# Anchor Scaffolding + +Use this file when the user asks to create or initialize an Anchor project. + +## Preflight + +Before running `anchor init`: + +- Check whether the requested destination already exists. +- Run `anchor --version` or inspect an existing `Anchor.toml` to determine the target Anchor version. +- If `anchor` is missing, read [cli-installation.md](cli-installation.md) and follow the approval-based recovery workflow. +- Confirm Rust/Cargo and Solana CLI availability when the project will immediately build or test. + +## Scaffold Flow + +1. Run `anchor init ` only after preflight succeeds. +2. Inspect the generated `Anchor.toml`, `programs/*/src/lib.rs`, `tests/*`, `package.json`, and `Cargo.toml`. +3. If the user asked for a specific program shape, update the generated program with minimal, idiomatic Anchor code. +4. Keep the generated test harness unless the user explicitly requests another language. +5. Run `anchor build` or `anchor test` only when dependencies are available and the user has approved any missing install steps. + +## New Project Defaults + +- Do not assume new Anchor projects use TypeScript. Anchor `1.1.2` scaffolds a Rust/LiteSVM test layout by default, with no `package.json`. +- After `anchor init`, inspect `Anchor.toml` and generated tests before choosing the next test workflow. +- Add reusable PDA derivation helpers in tests when the program uses PDAs. +- Keep account sizing constants near account structs. +- Use custom errors for business rules that are not captured by Anchor constraints. + +## Anchor 1.1 Scaffold Shape + +When Anchor generates a Rust/LiteSVM scaffold: + +- `Anchor.toml` includes `skip_local_validator = true`. +- `[scripts] test = "cargo test"`. +- generated tests live under `programs//tests/*.rs`. +- generated Rust tests use LiteSVM and include the deployed program bytes from `target/deploy/.so`. +- raw `cargo test` can fail before `anchor build` or `anchor test` creates the `.so`; run `anchor test` for the first full validation. + +## Handoff After Scaffolding + +After project creation, route follow-up test generation through [testing-anchor.md](testing-anchor.md). Do not generate tests from assumptions made before reading the actual program and IDL. diff --git a/solana-anchor-skill/skill/testing-anchor.md b/solana-anchor-skill/skill/testing-anchor.md new file mode 100644 index 0000000..136aae1 --- /dev/null +++ b/solana-anchor-skill/skill/testing-anchor.md @@ -0,0 +1,72 @@ +# Anchor Program Testing + +Use this file when generating or repairing tests for an Anchor program. + +## Detection First + +Inspect before generating: + +- `Anchor.toml` for scripts, cluster config, program names, and test command. +- `package.json`, `.mocharc*`, `tsconfig.json`, `tests/*.ts`, and `tests/**/*.ts` for TypeScript/Mocha. +- root `Cargo.toml`, `programs/*/Cargo.toml`, `tests/*.rs`, `dev-dependencies`, `solana-program-test`, and `litesvm` for Rust tests. +- `target/idl/*.json` if present, but treat source code as authoritative if IDL is stale. + +Decision: + +- If TypeScript tests already exist, extend TypeScript/Mocha and read [typescript-tests.md](typescript-tests.md). +- If Rust integration tests already exist, extend Rust and read [rust-tests.md](rust-tests.md). +- If both exist, choose the style with more existing coverage and report that choice. +- If neither exists, inspect the Anchor version and generated layout before choosing. Anchor `1.1.2` scaffolds Rust/LiteSVM tests by default, so prefer Rust for that layout. Use TypeScript only when the project has Node/Mocha wiring or the user requests it. + +## Coverage To Generate + +For every instruction exposed by the IDL: + +- one happy-path test using valid accounts and signers. +- wrong signer or missing authority failure when signer or authority fields exist. +- wrong PDA seed failure when `seeds` constraints exist. +- account-not-initialized failure for required existing accounts. +- account-already-initialized failure for `init` flows. +- numeric boundary tests for integer args and account numeric fields. +- PDA derivation assertion for expected address and bump. + +## Environment Selection + +Default for existing projects: + +```bash +anchor test +``` + +For Anchor `1.1.2` Rust/LiteSVM scaffolds, use `anchor test` for the first full run because it builds `target/deploy/.so` before invoking the configured test script. Running `cargo test` first can fail when tests call `include_bytes!(concat!(env!("CARGO_TARGET_TMPDIR"), "/../deploy/.so"))`. + +Use Surfpool only when the program reads accounts that cannot be realistically faked locally: + +- oracle price feeds. +- other protocols' live program accounts. +- mainnet-only account state needed for instruction execution. + +When Surfpool is needed, document which accounts require forked state and keep the local-validator tests for all instructions that can be isolated. + +## IDL-Driven Generation Workflow + +1. Parse `target/idl/.json` or build the IDL if safe to do so. +2. Map each IDL instruction to its account list, args, and expected context. +3. Cross-check source `#[derive(Accounts)]` contexts for constraints that IDL does not fully express. +4. Generate helper setup for common signers, PDAs, and initialized accounts. +5. Write runnable tests in the detected framework. +6. Run the project's test command when feasible. +7. Report coverage gaps. + +## Coverage Gap Report + +End with a short table: + +| Area | Covered | Gaps | +|------|---------|------| +| Instructions | list tested instructions | list missing or blocked instructions | +| Constraints | signer/PDA/init/authority checks | constraints not inferable from IDL/source | +| Edge cases | numeric boundaries and duplicate init | boundaries needing domain-specific fixtures | +| Environment | local validator or Surfpool | external accounts or live state not available | + +Do not claim full coverage when tests require missing fixtures, unknown external accounts, or stale IDLs. diff --git a/solana-anchor-skill/skill/typescript-tests.md b/solana-anchor-skill/skill/typescript-tests.md new file mode 100644 index 0000000..4927b81 --- /dev/null +++ b/solana-anchor-skill/skill/typescript-tests.md @@ -0,0 +1,93 @@ +# TypeScript Anchor Tests + +Use this file when the detected project uses TypeScript/Mocha tests. + +## Imports + +Use the project's existing imports when present. Otherwise prefer: + +```ts +import * as anchor from "@coral-xyz/anchor"; +import { Program } from "@coral-xyz/anchor"; +import { expect } from "chai"; +import { PublicKey, Keypair, SystemProgram } from "@solana/web3.js"; +``` + +## Test Shape + +Generate runnable Mocha tests: + +```ts +describe("program_name", () => { + const provider = anchor.AnchorProvider.env(); + anchor.setProvider(provider); + + const program = anchor.workspace.ProgramName as Program; + const authority = provider.wallet; + + it("initializes state", async () => { + const [state, bump] = PublicKey.findProgramAddressSync( + [Buffer.from("state"), authority.publicKey.toBuffer()], + program.programId, + ); + + await program.methods + .initialize() + .accounts({ + state, + authority: authority.publicKey, + systemProgram: SystemProgram.programId, + }) + .rpc(); + + const account = await program.account.state.fetch(state); + expect(account.authority.toBase58()).to.equal(authority.publicKey.toBase58()); + expect(account.bump).to.equal(bump); + }); +}); +``` + +Replace `ProgramName`, `ProgramName` type imports, account names, seeds, and instruction methods with the actual project values. + +## Failure Assertions + +Use the project's existing helper if present. Otherwise use a local helper: + +```ts +async function expectAnchorError(action: Promise, expected: string) { + try { + await action; + expect.fail("expected transaction to fail"); + } catch (err) { + const message = String(err); + expect(message).to.contain(expected); + } +} +``` + +Generate concrete failure tests by passing invalid accounts or signers: + +- wrong signer: create a new `Keypair`, airdrop if needed, and sign with it where the instruction expects the authority. +- wrong PDA: derive the account with one seed byte changed and expect a seeds constraint failure. +- uninitialized account: pass a fresh keypair public key where an existing Anchor account is required. +- already initialized: call the same `init` instruction twice with the same PDA. + +## Numeric Boundaries + +For integer args or account numeric fields: + +- test zero when meaningful. +- test max allowed value if the program documents or enforces one. +- test overflow or underflow attempts when arithmetic exists. +- assert Anchor custom errors when the program exposes them. + +Use `new anchor.BN(...)` for `u64`, `i64`, `u128`, and `i128` values. + +## PDA Assertions + +When a context has `seeds` and `bump`, assert both: + +- derived PDA equals the address passed to the instruction. +- fetched bump equals the expected bump when the account stores it. + +Do not duplicate PDA derivation logic inline across many tests; create helper functions once the same PDA appears in more than one test.