How Fuzzy works
Checking a Solana program has meant separate tools run by a person: confirm the deployed build, run a static scanner, write a fuzz harness, test against real state, then commission an audit. Fuzzy runs the parts a machine can prove as one automated pass. It attacks the program in LiteSVM, a local copy of Solana, keeps only the attacks that work, grades the run, and can send the result to a Verafy jury that imprints a certification on Verafy Devnet.
What a Sniff run does
- Reads the program's Anchor IDL and builds the accounts it expects, funded as if users had deposited.
- Calls every instruction as an honest owner would, to learn which paths work and in what order.
- For a deployed program, replays its recent mainnet calls on cloned copies of the accounts they touched, then attacks those calls too. This works without an IDL.
- Attacks each working path: drops each required signature, re-runs setup as a stranger, swaps in a look-alike account owned by another program, and sends extreme arguments.
- Keeps a finding only if the attack transaction succeeds and moves funds to the attacker or writes the attacker in as an authority. Everything else is a lead: shown, never scored.
- Never sends anything to mainnet or devnet. Fetching a deployed program is a read.
Every step, in detail
1. Ingest
- Reads the program account with
getAccountInfo. For the upgradeable loader it follows the pointer to the program data account, reads the upgrade authority, and strips the 45-byte header. Loader v2 programs are read as is. - Parses the ELF (machine type, text size, SBF instruction count) and hashes it with SHA-256. Every result is tied to that hash.
- Looks for an Anchor IDL on chain, at
createWithSeed(findProgramAddress([], program), "anchor:idl", program), and inflates it. With an IDL, Fuzzy knows every instruction's accounts, signers, PDAs and argument types.
2. Learn
- Builds a world in LiteSVM: the program, an owner, a stranger and an attacker, each funded with 10 SOL, plus the state accounts and PDAs the IDL describes.
- Signers are derived from
sha256("fuzzy:" + seed + ":" + label), so a run with the same seed is the same run, and any finding replays exactly. - Calls every instruction as an honest owner, with typical arguments, to find the paths that complete and the order they need (for example, initialize before deposit before withdraw).
3. Replay
- For a deployed program, fetches up to 60 recent signatures with
getSignaturesForAddressand up to 10 successful calls withgetTransaction. - Clones every account those calls touched, and every other program they invoked, into LiteSVM at the slot they ran.
- Replays each call honestly first. Signature checks are off so the original signers can be stood in for, which is why the next rule exists: in an attack, every remaining signer becomes the attacker, and a finding that relies on a signature the attacker does not hold is thrown out.
- Replay works without an IDL, which is how Fuzzy tests programs that publish none.
4. Attack
From every path that completed, Fuzzy runs each attack class that applies. A class applies when the interface has something it can target, such as a signer, a numeric argument, a state account or an instruction named close.
| Class | The attack | Counts as proof when | Status |
|---|---|---|---|
| Missing signer check | For every instruction that worked when the owner called it, Fuzzy calls it again with each required signature dropped, paid for by an attacker, with any payout redirected to the attacker. | SOL reaches the attacker, or the attacker's key replaces the owner's in program state. | runs today |
| Missing owner check | Fuzzy passes a copy of each state account owned by a different program, with the same bytes, and checks whether the instruction still accepts it. | The forged account is accepted and funds or authority move to the attacker. | runs today |
| Account substitution | Fuzzy signs as the attacker in the owner's place, and swaps in look-alike accounts the attacker controls. | The attacker, signing as themselves, moves the owner's funds or authority. | runs today |
| Reinitialization | After an honest setup, Fuzzy runs initialize again as a stranger on the same account, then tries to withdraw. | The stranger's key is written in as the authority, or the follow-up withdrawal succeeds. | runs today |
| Arithmetic overflow | Fuzzy sends 0, 1, the maximum and maximum minus one for every numeric argument. | A wrapped amount moves funds to the attacker. A panic alone is a lead. | runs today |
| Lamport draining | Every attack above measures every account's SOL before and after; any path that moves SOL to the attacker counts here too. | The attacker's SOL balance goes up. | runs today |
| Compute exhaustion | Fuzzy records the compute each call used and flags calls that hit the limit. | Shown as a lead. It never counts toward the score in this engine version. | runs today |
| Unchecked CPI target | Planned: pass an attacker program in each program slot and watch what it is asked to do. | Not built yet. | planned |
| PDA seed collision | Planned: derive colliding seeds and non-canonical bumps for each PDA in the IDL. | Not built yet. | planned |
| Close, then reuse | Planned: close an account, then refund and reuse it in the same transaction. | Not built yet. | planned |
| Unchecked token owner | In mainnet replay, the attacker signs in the user's place and each of the user's token accounts is swapped, one at a time, for an empty copy the attacker owns. | Tokens land in the attacker-owned copy. It starts empty, so every unit came from someone else. | runs today |
| Price manipulation | Planned: move the pool or oracle a program reads before calling it. | Not built yet. | planned |
5. Prove
- Measures every account's SOL before and after each attack. Transaction fees paid by the attacker are not counted as loss.
- A finding needs the attack transaction to succeed and either SOL to reach an account the attacker controls, or the attacker's key to replace the owner's in program state. The transaction, its logs, the compute it used and the balance changes are kept as evidence.
- Anything else that changed program state without permission, or made the program panic, is a lead: shown, never scored.
6. Grade
The Fuzzy Score, below. A Sniff run has a budget of 600 transactions and 40 seconds; replay gets half of each when there is history.
7. Jury and 8. Imprint
The report is hashed as canonical JSON and sent to a jury of registered Verafy harnesses on Verafy Devnet. The verafy_certification program writes the level only if the ruling is sealed, its contract hash binds this program, bytecode hash, report hash and score, and every seat is an active registered harness, with at least three seats from at least two model families. Certifications
Built on
The Fuzzy Score
The score is the lower of two numbers: Depth, which says how hard Fuzzy tried, and a cap set by the worst proven finding. A shallow run can't score high because it found nothing, and a deep run can't score high with a critical bug.
| Worst proven finding | Meaning | Cap |
|---|---|---|
| Critical | Anyone can steal or permanently lock funds, or seize an authority | 20 |
| High | Funds at risk under realistic conditions, or access control bypassed | 45 |
| Medium | An invariant breaks without direct loss | 70 |
| Low | Hygiene, such as a dependency with a published advisory | 90 |
| None proven | 100 |
| Depth part | Weight | Measured as |
|---|---|---|
| Branch coverage | 40 | Share of the program's branches executed. Not measured by this engine version yet, so it scores 0. |
| Instruction coverage | 20 | Share of instructions driven to both success and failure. |
| Attack classes tried | 20 | Share of the attack classes that apply to this program that Fuzzy actually ran. |
| Real state | 10 | Cloned mainnet accounts 10, synthetic accounts from the IDL 5, empty accounts 0. |
| Effort | 10 | Share of the planned run completed within the time limit. |
Grades: 90 and up is Hardened, 75 Solid, 50 Shaky, 25 Exposed, and below 25 Broken. Scores are versioned as fuzzy-score/1 and tied to one exact build of the program.
Add-ons
A Sniff run costs 100 credits. Add-ons run more checks for more credits. Most need source code, which Fuzzy finds through a verified build: when the deployed bytecode has been rebuilt from a public repository, Fuzzy reads that exact commit. Add-ons are reported beside the Fuzzy Score and never change it.
| Add-on | Credits | What it runs | Needs | Status |
|---|---|---|---|---|
| Verified-source check | +25 | Whether the deployed bytecode was rebuilt from public source, and from which repository and commit. | A deployed mainnet program | available |
| Dependency advisories | +100 | Every crate in the verified commit's Cargo.lock checked against the RustSec advisory database. | A verified build | available |
| Rebuild from source | +500 | Fuzzy clones the verified commit, builds every program in it with cargo-build-sbf, and checks a build matches the deployed bytecode byte for byte. | A verified build | available |
| Static lints | +300 | Trail of Bits' Solana lints (through dylint) and Clippy over the verified source. | A verified build | available |
| Deep Fuzz | +1,000 | Four times the mainnet history and five times the budget of a Sniff run, plus branch coverage: every invocation is traced instruction by instruction in agave-ledger-tool. Required for the Fuzzed level. | Nothing | available |
| Property fuzzing | +1,500 | Random call sequences by several users, with invariants derived from the IDL checked after every transaction: authorities change only when they sign, and SOL leaves state only when its authority signs. | An IDL | available |
| AI design review | +1,000 | Four models from different labs read the source and IDL for design and economic risk; risks two or more raise independently are reported as agreed. Not a human audit. | A verified build | available |
| Kitchen Sink | +4,000 | Everything above in one run, for less than the parts. Required for the Hardened level. | A verified build for the source checks | available |
Rebuild, lints, Deep Fuzz, property fuzzing and the AI design review run on Fuzzy's build machine: an isolated container on Modal with Node 22, the Rust toolchain, the Agave tools and cargo-dylint, because a web request cannot build Rust or run for minutes. Until the build machine is online, the add-on panel says so and nothing is charged. Token balances are part of the proof in every mainnet-replay run, at no extra cost.
Still not automated
- A human audit: Design and economic risks need people who understand what the program is for. The AI design review is a first pass, not a substitute. Helius: A Hitchhiker's Guide to Solana Program Security
What this version does not do yet
- A Sniff run does not measure branch coverage, so it scores 60 at most with mainnet replay and 55 without. Deep Fuzz measures it. Model-planned attacks are next.
- Programs with no IDL and no recent calls Fuzzy can replay get only a shallow run, because Fuzzy can't tell what their instructions are.
- Token-account, CPI and PDA attack classes are listed as not tried when they apply.