iktok90-design/ai-smart-contract-auditor/solana-anchor
Claude Code and agent skill by iktok90-design/ai-smart-contract-auditor
- community
Checking that the SKILL.md file is still in the repository.
Detect bug classes specific to Solana / Anchor (Rust) programs — missing signer checks, missing account owner checks, account-confusion / type-cosplay without discriminator validation, unchecked AccountInfo, non-canonical PDA seeds/bumps & seed collisions, missing has_one/constraint, CPI to unverified programs, close-account lamport-drain & revival, sysvar spoofing, and arbitrary-account substitution. Activate on any `.rs` file with `use anchor_lang`, `#[program]`, `#[derive(Accounts)]`, `#[account]`, `Signer<'info>`, or `AccountInfo`.
Install
npx vskill@latest install iktok90-design/ai-smart-contract-auditor/solana-anchorSKILL.md
Solana / Anchor (Rust) detection
When this applies
- Any
.rsfile importinganchor_lang(use anchor_lang::prelude::*) - Macros
#[program],#[derive(Accounts)],#[account],#[instruction(...)] - Account wrappers
Signer<'info>,Account<'info, T>,AccountInfo<'info>,UncheckedAccount,Program<'info, T>,Sysvar<'info, T> - PDA derivation:
seeds = [...],bump,find_program_address,create_program_address - CPI:
CpiContext,invoke,invoke_signed,*_cpi
Solana has no implicit caller. Every authority, ownership, and identity invariant must be asserted explicitly in the account struct or handler.
Detection patterns
Missing signer check (CRITICAL)
Signal: an "authority"/"owner"/"admin" account typed AccountInfo/UncheckedAccount instead of Signer<'info>, or a handler reading ctx.accounts.x without checking x.is_signer. Anyone passes the real authority's pubkey without their signature.
Missing owner check / arbitrary account substitution (CRITICAL)
Signal: deserializing from a raw AccountInfo/UncheckedAccount without verifying account.owner == program_id, or using Account<T> for data but not constraining which account. An attacker passes a fake account they own with crafted bytes. Account<'info, T> checks owner+discriminator; raw AccountInfo checks nothing.
Type cosplay / missing discriminator validation (HIGH)
Signal: try_from_slice / try_deserialize_unchecked on raw data, bypassing the Anchor 8-byte discriminator. A different account type with the same layout (e.g. Config passed where User is expected) is accepted. Use Account<'info, T> so Anchor enforces the discriminator.
Non-canonical PDA bump / seed collision (HIGH)
Signal: create_program_address with a caller-supplied bump (multiple valid PDAs → forgeable accounts), bumps not stored/re-checked against the canonical one, or seeds that omit a discriminator so ["vault", x] collides with ["vault", x] of another domain. Use find_program_address (canonical bump) or Anchor seeds+bump and persist bump.
Missing has_one / constraint linking accounts (HIGH)
Signal: a Signer that's never bound to the data account it should control — no #[account(has_one = authority)] or #[account(constraint = config.authority == authority.key())]. Any signer updates any config. Same for mint, owner, vault relationships.
CPI to unverified program (HIGH)
Signal: CpiContext::new whose program account isn't constrained to the expected Program<'info, Token> / known program id, or invoke/invoke_signed to a caller-supplied program. Attacker passes a malicious program at the token_program slot. Type it Program<'info, Token> or assert the key.
close-account lamport drain / revival (HIGH)
Signal: manual lamport zeroing without wiping the discriminator/data, or #[account(close = ...)] whose data the attacker can re-fund in the same tx (revival attack) before it's GC'd. Use Anchor close = recipient (it zeroes the discriminator) and don't reuse closed accounts in the same instruction.
Sysvar / clock spoofing (MEDIUM-HIGH)
Signal: Clock/Rent/Instructions sysvars typed as AccountInfo/UncheckedAccount rather than Sysvar<'info, Clock> or fetched via Clock::get(). An attacker substitutes a fake sysvar account with controlled time/data.
Unchecked AccountInfo used for value flow (MEDIUM)
Signal: UncheckedAccount/AccountInfo (or a /// CHECK: with no real validation) that's subsequently used as a transfer destination, authority, or data source. The CHECK comment silences Anchor but doesn't add a constraint.
Severity rubric
| Pattern | Severity | Notes | |
|---|---|---|---|
Authority as AccountInfo, no signer proof | Critical | Impersonate any authority | |
| Deserialize raw account, no owner check | Critical | Fake-account substitution | |
| Type cosplay (no discriminator) | High | Wrong account type accepted | |
| Caller-supplied / non-canonical PDA bump or seed collision | High | Forge PDA-owned accounts | |
Missing has_one/constraint linking signer↔data | High | Any signer mutates any record | |
| CPI to unconstrained program account | High | Malicious program at slot | |
| Manual close / revivable closed account | High | Lamport drain / reuse | |
Sysvar passed as AccountInfo | Medium | Spoofed clock/rent | |
UncheckedAccount used for value flow | Medium | Depends on later checks |
Remediation patterns
- Type authorities as `Signer<'info>` and assert their relationship with
#[account(has_one = authority)]orconstraint =. - Use `Account<'info, T>` (not raw
AccountInfo) so Anchor enforcesowner == program_idAND the 8-byte discriminator; nevertry_from_slice/try_deserialize_uncheckedon untrusted accounts. - Derive PDAs canonically — Anchor
seeds = [...]+bump(orfind_program_address), store the bump, include a domain discriminant in seeds to prevent collisions. - Constrain CPI program accounts to
Program<'info, Token>/known ids; validate any account passed toinvoke/invoke_signed. - Close with `#[account(close = recipient)]` so the discriminator is zeroed; don't reuse a closed account within the instruction.
- Fetch sysvars via `Clock::get()` or
Sysvar<'info, Clock>, never a raw account. - Resolve every `/// CHECK:` with an actual key/owner constraint, not just a comment.
False-positive notes
- An
AccountInfo/UncheckedAccountwith a real#[account(address = KNOWN_PUBKEY)]or in-handler key assertion is validated — don't flag it as unchecked. Account<'info, T>already enforces owner + discriminator; type-cosplay and owner-check findings apply to RAWAccountInfo/try_from_slicepaths only.- A
Signerthat legitimately needs no data binding (e.g. a fee payer that only pays rent) doesn't needhas_one. init/init_if_neededaccounts set their own owner to the program — owner-check findings don't apply at init.
Related
- [[access-control]] — signer, owner, and has_one constraints are Solana's entire auth model
- [[signature-replay]] — Solana txs need recent-blockhash + nonce discipline; instruction-level replay across PDAs
PDA Validation & Account Constraints
Added program-derived address (PDA) validation for Anchor programs. The skill now detects:
- Missing `seeds` constraint on PDA accounts initialized via CPI
- Mismatched bump seeds between
initand subsequentmutconstraints - Unchecked account discriminators in cross-program invocations
- Missing `has_one` constraints on associated token accounts
Detection Example
The skill verifies Anchor IDL account constraints against actual on-chain validation and flags mismatches that could allow fake PDA injection attacks.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/solana-anchor/SKILL.md
Popularity
- Repository stars
- 37
- Repository forks
- 5
- 7-day trend score
- 18
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/iktok90-design/ai-smart-contract-auditor/solana-anchor)