example-fork-detection
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on…
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.
Claude Code
$ npx -y skills add omermaksutii/RugProof --skill vyper-specific --agent claude-code
Codex
$ npx skills add omermaksutii/RugProof --skill vyper-specific -a codex -y
Cursor
$ npx skills add omermaksutii/RugProof --skill vyper-specific -a cursor -y
Gemini CLI
$ npx skills add omermaksutii/RugProof --skill vyper-specific -a gemini-cli -y
GitHub Copilot
$ npx skills add omermaksutii/RugProof --skill vyper-specific -a github-copilot -y
OpenCode
$ npx skills add omermaksutii/RugProof --skill vyper-specific -a opencode -y
Windsurf
$ npx skills add omermaksutii/RugProof --skill vyper-specific -a windsurf -y
name: vyper-specific description: 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.
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.
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(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)
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.
`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.
Same EIP-6780 caveats apply ([[selfdestruct-eip6780]]). Vyper code that relies on selfdestruct deleting code is broken post-Cancun on mainnet (March 2024).
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.
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.
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).
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` 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.
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.
| Pattern | Severity | |---|---| | Vyper 0.2.15/0.2.16/0.3.0 with `@nonreentrant` | **Critical** *(known-broken)* | | `raw_call` to user-supplied target | **High** | | `send` (2300 gas) to dynamic recipient on L2 | **High** | | `selfdestruct` for state cleanup post-Cancun | **High** | | `create_forwarder_to` without per-clone init guard | **High** | | Decimal scaling errors in price math | **High** | | Storage layout in Vyper proxy without explicit reservation | **High** | | `default_value` semantics misuse | **Medium** | | `assert` vs `raise` inconsistency | **Low** |
Rugproof your code before someone else does. 🌐 Live site: omermaksutii.github.io/RugProof 📦 Latest: v1.0.0 — 45 commands · 23 agents · 45 skills · 13 MCP servers · tested, offline-first, with rule packs, a benchmark, non-EVM coverage, and post-deploy
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on…
Detect unsafe assumptions about Solady's gas-optimized ERC20/ERC2612 permit and DN404 metadata. Solady's ERC20 uses custom…
Detect front-runnable ownership initialization in Solady Ownable / OwnableRoles. Solady's `_initializeOwner` is a guarded…
Detect Solady SafeTransferLib calls that assume the token has code. SafeTransferLib.safeTransfer/safeTransferFrom/safeApprove…
Detect Uniswap V4 hooks that fail to settle currency deltas with the PoolManager. Every credit/debit a hook creates…
Detect Uniswap V4 hooks whose address-encoded permission flags don't match the callbacks the hook actually implements. In V4 the…
Use for authorized security assessment of REST, GraphQL, WebSocket, or SOAP APIs, including discovery, authentication,…
Extract and decrypt Windows DPAPI-protected secrets (Credential Manager, browser logins/cookies, Wi-Fi credentials, KeePass keys)…
Clean and finalize authorized natural-language text intended for readers by auditing suspicious invisible Unicode and rewriting…