agentleFS
iktok90-designiktok90-design/ai-smart-contract-auditor/skills/vyper-specific/SKILL.md

vyper-specific

Skill36 starsChanged 5 months agoMITView on GitHub

Detect Vyper-specific bug classes — compiler-version reentrancy bugs, raw_call return-value handling, send/raw_call gas defaults, immutables vs constants, default-bytes scope, msg.sender-based auth corner cases. Activate on any `.vy` file or `pragma version` directive.

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 0 are distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Default revert_on_failure=True will revert the calling tx on a revert, but the *return data* is silently dropped.

    line 37
  2. Very specific:

    Use raw_call(addr, b"", value=amount) with explicit gas bounds.

    line 47
  3. Very specific:

    Use raw_call with explicit max_outsize + check return.

    line 98
  4. Very specific:

    Use OZ ReentrancyGuard-equivalent only after verifying the compiler version.

    line 100
  5. Very specific:

    When analyzing Vyper contracts compiled with v0.4+, the @nonreentrant decorator uses a mutex storage slot.

    line 116

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Vyper-specific detection

Why a separate skill

Vyper has different defaults than Solidity. Some patterns that are safe in Solidity are unsafe in Vyper, and vice versa. Vyper has also had compiler-level reentrancy bugs (most famously the 2023 Curve incident). Audit Vyper with Vyper-aware eyes.

When this applies

  • Any .vy file
  • Any # @version pragma directive
  • Any contract using raw_call, send, selfdestruct, create_forwarder_to
  • LP tokens, AMM pools written in Vyper (Curve and forks)

Detection patterns

Compiler-version reentrancy bug (CRITICAL)

Vyper 0.2.15 / 0.2.16 / 0.3.0 had a broken @nonreentrant lock — the lock was per-key, but the same key was reused across functions, causing locks to fail to engage in some compositions. Caused the Curve / Vyper exploit (July 2023, ~$73M).

Action: any contract pinned to # @version 0.2.15, 0.2.16, or 0.3.0 — treat reentrancy guards as broken. Recommend upgrade to ≥ 0.3.7 (or 0.3.10+ for ongoing support).

# @version 0.2.15        # ← FLAG: known-broken nonreentrant

raw_call ignores return data unless max_outsize set (HIGH)

raw_call(target, b"...")    # ← return data ignored entirely

Default revert_on_failure=True will revert the calling tx on a revert, but the return data is silently dropped. If you need to read the return:

ok: bool = raw_call(target, b"...", max_outsize=32, revert_on_failure=False)

send forwards 2300 gas (HIGH on L2s)

Same problem as Solidity transfer. Smart-wallet recipients can't receive. On L2s with different base costs (Berachain, etc.), the 2300 gas bound is unreliable.

Use raw_call(addr, b"", value=amount) with explicit gas bounds.

default_value= on storage (MEDIUM)

HashMap[address, uint256] defaults to 0 for unset keys. Vyper devs sometimes assume "exists vs not-exists" semantics that don't apply. Use a paired bool flag if existence matters.

selfdestruct semantics (HIGH on pre-Cancun)

Same EIP-6780 caveats apply ([[selfdestruct-eip6780]]). Vyper code that relies on selfdestruct deleting code is broken post-Cancun on mainnet (March 2024).

create_forwarder_to clones share code (HIGH)

EIP-1167 minimal-proxy clones via create_forwarder_to. Same gotcha as Solidity: per-clone init guard required, or anyone can initialize a fresh clone.

Decimal scaling (MEDIUM-HIGH)

Vyper's decimal type is fixed-point with limited range. Mixing uint256 and decimal for price math invites overflow or precision loss. Most modern Vyper code avoids decimal; if present, audit carefully.

external vs internal and visibility (MEDIUM)

Vyper enforces explicit visibility (@external, @internal, @view, @pure). Functions without visibility are syntax errors — but @external + missing access checks are still a foot-gun (same as Solidity).

msg.sender == self patterns (MEDIUM)

Vyper allows msg.sender == self checks — used to gate "callable only from internal call". Easy to typo msg.sender == self.owner vs == self. Check the intent.

assert vs raise (LOW)

assert and raise both revert. assert <cond> reverts if false; raise "reason" always reverts. Mix-up on devs coming from Solidity. Both are safe; just consistency.

Storage layout — Vyper has no __gap convention (HIGH for upgradeable)

Vyper isn't typically used for upgradeable contracts (no equivalent of OZ Upgradeable plugin). If you find a Vyper proxy implementation, audit storage layout very carefully — reserved slots aren't a Vyper convention.

Severity rubric

PatternSeverity
Vyper 0.2.15/0.2.16/0.3.0 with @nonreentrantCritical (known-broken)
raw_call to user-supplied targetHigh
send (2300 gas) to dynamic recipient on L2High
selfdestruct for state cleanup post-CancunHigh
create_forwarder_to without per-clone init guardHigh
Decimal scaling errors in price mathHigh
Storage layout in Vyper proxy without explicit reservationHigh
default_value semantics misuseMedium
assert vs raise inconsistencyLow

Remediation patterns

  • Upgrade to Vyper ≥ 0.3.10 to get fixed @nonreentrant.
  • Use raw_call with explicit max_outsize + check return.
  • Replace send with raw_call(addr, b"", value=amt) and explicit gas budget.
  • Use OZ ReentrancyGuard-equivalent only after verifying the compiler version.
  • For Curve forks specifically: pin compiler, audit the lock implementation against the Curve patch commits.

False-positive notes

  • Vyper test fixtures often use @version 0.2.x for legacy compatibility — verify deployment compiler differs.
  • Curve LP tokens and pools have been re-audited extensively; current Curve deploys use post-fix Vyper.

Related

  • [[reentrancy]] — base reentrancy patterns
  • [[selfdestruct-eip6780]] — EIP-6780 implications
  • [[unchecked-calls]] — raw_call analog of low-level call

Compiler-Specific Notes (v0.4+)

When analyzing Vyper contracts compiled with v0.4+, the @nonreentrant decorator uses a mutex storage slot. Reentrancy guards that rely on storage checks between external calls MUST account for Vyper's 0-indexed mutex storage layout (sload(0) vs sload(keccak256)).

Detection Path: external call → state mutation → nonreentrant check bypass

Verified against Vyper v0.4.0 compiler output for CurveStableSwap pattern.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.