Solana fuzz
Claude Code / Codex skill that writes Trident property and invariant fuzz tests for Solana programs (Anchor and native).
npx -y skills add belumume/solana-fuzz --skill solana-fuzzAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when fuzzing a Solana program or writing a Trident fuzz test: property and invariant testing of a program's state transitions, finding runtime logic bugs that happy-path and example-based tests (LiteSVM, Mollusk, Surfpool) miss, hardening an Anchor or native program before a mainnet deploy, or adding regression fuzzing across program versions. Trident is the Solana Foundation supported fuzzer by Ackee Blockchain. Dynamic, dev-loop property testing, not a static security auditor.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
9.5 KB, as published. Nobody here has run it
solana-fuzz: Trident fuzzing and invariants for Solana programs
Turn a Solana program into a property-tested one. This skill writes Trident fuzz tests: it scaffolds the test layout, generates the flow methods that drive your instructions with random inputs, and writes invariant methods that assert your program's state stays correct after every fuzzed transaction sequence. Trident (Ackee Blockchain, Solana Foundation supported) runs many transactions per second against the TridentSVM client and catches the runtime logic errors that happy-path example tests never reach.
Verify before you author. Trident's API changes between minor versions. This skill pins to v0.12.0 (see
metadata.trident-version), the latest stable release; 0.13 is still pre-release (RC) as of this skill's date, so stable is the reproducible choice. Before generating code, confirm the user's installed version and re-read the relevant reference file rather than authoring from memory. If the version differs, check the live source at the URL inmetadata.trident-source.
When to use this skill
- The user wants to fuzz a Solana program, or write a Trident fuzz test.
- The user wants property/invariant checks over a program's state transitions.
- The user wants to find runtime bugs that example-based tests miss, or harden a program before a mainnet deploy.
- The user wants regression fuzzing to catch bugs introduced between versions.
What this skill is NOT
- Not a static security auditor or vulnerability scanner. Route code-audit and exploit-class detection to a dedicated audit skill. This skill is dynamic, dev-loop property testing.
- Not a replacement for example-based tests. It complements LiteSVM/Mollusk/Surfpool.
- Not a transaction-landing, signing, or RPC tool. It runs against TridentSVM, not a live cluster.
The Trident model (v0.12.0)
Anchored to the repo source, not memory:
-
Install the CLI:
cargo install trident-cli --locked. Run a suite from thetrident-tests/directory:trident fuzz run fuzz_0 --with-exit-code. The--with-exit-codeflag is required: without it a failing invariant is silently swallowed in parallel mode and the run still exits 0 (seereferences/invariants.md). -
A fuzz test lives in
trident-tests/fuzz_0/and is made of:test_fuzz.rs,fuzz_accounts.rs,types.rs, andTrident.toml. -
The test is a struct deriving the framework methods and an impl annotated as a flow executor:
use trident_fuzz::fuzzing::*; #[derive(FuzzTestMethods)] struct FuzzTest { trident: Trident, fuzz_accounts: AccountAddresses, } #[flow_executor] impl FuzzTest { fn new() -> Self { Self { trident: Trident::default(), fuzz_accounts: AccountAddresses::default() } } #[init] fn start(&mut self) { /* per-iteration setup */ } #[flow] fn flow1(&mut self) { /* randomly selected each iteration */ } #[end] fn end(&mut self) { /* per-iteration cleanup */ } } fn main() { FuzzTest::fuzz(1000, 100); } -
Drive an instruction: build it, then
self.trident.process_transaction(&[ix], Some("Label")), which returns a result with.is_success()and.get_transaction_timestamp(). Gate invariant checks on.is_success(): a rejected transaction is the expected-failure path, not a violation. -
Random inputs:
self.trident.random_from_range(0..u8::MAX). Fund a signer:self.trident.airdrop(&addr, 10 * LAMPORTS_PER_SOL). Advance the clock:self.trident.forward_in_time(n). -
Insert accounts in
fuzz_accounts:self.fuzz_accounts.author.insert(&mut self.trident, None)for a keypair, or withSome(PdaSeeds { seeds: &[b"seed"], program_id })for a PDA. -
Read typed state:
self.trident.get_account_with_type::<MyAccount>(&addr, 8)(the8is the discriminator byte offset for Anchor accounts).
Invariants (the core value)
The pattern: capture state before, execute, capture state after, assert the expected change in a dedicated invariant method. Handle expected failures explicitly instead of swallowing them.
let before = match self.trident.get_account_with_type::<MyAccount>(&addr, 8) {
Some(v) => v, // 8 = Anchor discriminator; None until the account is initialized
None => return,
};
let res = self.trident.process_transaction(&[ix], Some("op"));
if res.is_success() {
if let Some(after) = self.trident.get_account_with_type::<MyAccount>(&addr, 8) {
assert!(/* domain property over before/after */, "describe the violation");
}
}
Gate the assert on is_success() so a correctly-rejected transaction is not counted
as a violation. The full worked invariant method, the expected-failure handling, and
the mandatory --with-exit-code flag (without it, a failing assert is silently
swallowed in parallel mode) are in references/invariants.md and the runnable
examples/vault/.
Operating procedure (when invoked)
- Detect the program: read
Anchor.tomland the IDL (for a native program, read its instruction enum and entrypoint instead); list the instructions and the accounts each touches. - Scaffold the
trident-tests/fuzz_0/layout (or use the CLI init flow perreferences/setup.md). - Define
fuzz_accounts(signers and PDAs with their real seeds) infuzz_accounts.rs; mirror the program's account types intypes.rs. - Write
#[init]to establish baseline state (create accounts, airdrop, seed PDAs). - Write one
#[flow]per instruction (and per meaningful multi-instruction sequence), driving each with random inputs. - For every state-changing instruction, write an invariant method using the capture-before/after pattern above. Assert the real domain property, not just "it didn't error".
- Run
trident fuzz run fuzz_0 --with-exit-codefromtrident-tests/. The flag is mandatory or invariant failures do not register. Triage a reported failure with the printed master seed (trident fuzz run fuzz_0 <SEED>), fix, re-run green.
Do not author flow or invariant code from memory. Read the relevant reference file first; the API is version-specific.
References (progressive disclosure)
references/setup.md: install, init,Trident.toml, prerequisites, run commandreferences/flows.md:#[init]/#[flow]/#[end], driving instructions, random inputs, time controlreferences/invariants.md: capture-before/after, invariant methods, expected-failure handlingreferences/accounts.md:fuzz_accounts, PDA seeds,get_account_with_type, discriminator offsetreferences/regression.md: comparing program behavior across versions
Worked example
examples/vault/ is the verified, runnable reference: a tiny Anchor program with a
planted balance-invariant bug plus a Trident suite that catches it (red), and the
require! + checked_sub fix that makes it pass (green). It is compiled and fuzzed against the exact
v0.12 API; when in doubt about a call, copy from there.
Verified vs documented
examples/vault/ is the one flow verified end-to-end against the v0.12.0 API (red on the
planted bug, green on the require! + checked_sub fix). The other capabilities -- native
(non-Anchor) programs, multi-instruction sequences, and the across-versions regression flow
in references/regression.md -- are documented patterns, not separately shipped runnable
examples. Generate them from the references for your program and your installed Trident
version, then verify the output the same way the vault was verified: run it, confirm a real
bug goes red and the fix goes green, before relying on it.
Troubleshooting (common failure modes)
- Every flow shows 0 successful invocations: the
fuzz_accountsPDA seeds do not match the program's real seeds. Fix them infuzz_accounts.rs(seereferences/accounts.md). - A run exits 0 when you expected a violation:
--with-exit-codewas omitted, so a failing invariant was swallowed in parallel mode. Always runtrident fuzz run fuzz_0 --with-exit-code. - The build dies with an
edition2024/ MSRV error before your code compiles: a transitive dependency resolved newer than the pinned toolchain supports. Pin it (cargo update -p <crate> --precise <older>) or use a newer platform-tools (anchor build -- --tools-version vX.Y). Seereferences/setup.md. - A native (non-Anchor) account reads as garbage: wrong discriminator offset. Anchor accounts
use offset 8; native accounts have no Anchor discriminator, so pass the program's real
layout offset (often 0). See
references/accounts.md.
Provenance
Trident API in this skill is taken from the v0.12.0 source and docs at
https://github.com/Ackee-Blockchain/trident (examples under
examples/hello_world/trident-tests/ and the invariants-assertions documentation).
Re-verify against that source when the pinned version changes.