A two-line refactor can drop the check that guards a vault.
Some of Solana's largest losses came down to one account nobody checked: Wormhole (~$320M), Cashio (~$48M), Crema (~$8.8M). In Anchor, a check that was written is one line in a struct, and a later pull request can delete it without touching any logic. It compiles and the tests still pass. This page catches that case: a check that was there, and a change that removes it.
#[account(
mut,
seeds = [b"vault", depositor.key().as_ref()],
bump,- has_one = depositor, )]- pub vault: Account<'info, Vault>,+ pub vault: UncheckedAccount<'info>,
2 account checks removed
- Anchor no longer checks that
vaultis aVaultthis program owns. vaultno longer has to belong to the signingdepositor.
Whether that is exploitable depends on what else pins the account (here the seeds still do). That is the question a reviewer has to answer, and solinv puts it in front of them.
Check yours
Paste a pull request, or the address of a deployed program to see what each upgrade changed. It runs in your browser; nothing to install.
What this page does, and what needs solinv installed
This page reads what a program declares. Installed, solinv also runs the program and tries the attack each check exists to stop.
| On this page | With solinv installed |
|---|---|
| One public pull request at a time, when you paste it | Every pull request, private repositories too: the build fails when a check is removed, with a note on the line |
| Code already pushed to GitHub, or deployed | Your local branch, before you push |
| Reads the declarations and key checks at the top of the handler. A check inside a function the handler calls looks removed here | Runs the program locally and tries what each check prevents: skipping a signer, passing a fake owner or type, forging a PDA, swapping in another user's account. You learn whether the check is really enforced, wherever it lives |
| Deployed Anchor programs with an on-chain IDL | Any Anchor program from its source, plus a map of the instructions that rely on hand-written checks |
| A shared quota of lookups | No limit |
Fail the pull request that removes a check
Add this to a workflow. No Solana toolchain and no build of your program.
steps:
- uses: actions/checkout@v7
- uses: psyto/solinv@v0
Or run it locally
cargo install --git https://github.com/psyto/solinv --tag v0.1.9 solinv-cli
solinv diff --base-src old/src --head-src programs/my_program/src
solinv idl target/idl/my_program.json --src programs/my_program/src
To fuzz the checks, start from your IDL: solinv writes most of the spec from it.