example-fork-detection
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on `<your trigger keywords or…
Detect ignored external-call return values — silent failures from low-level call/delegatecall/staticcall, ignored ERC20 transfer return values, return-data length issues. Activate on `.call`, `.delegatecall`, `.staticcall`, `.send`, `transfer`/`transferFrom` (without SafeERC20),
$ npx -y skills add omermaksutii/RugProof --skill unchecked-calls --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/unchecked-callsContext preview
The summary Claude sees to decide when to auto-load this skill.
Detect ignored external-call return values — silent failures from low-level call/delegatecall/staticcall, ignored ERC20 transfer return values, return-data length issues. Activate on `.call`, `.delegatecall`, `.staticcall`, `.send`, `transfer`/`transferFrom` (without SafeERC20),
name: unchecked-calls description: Detect ignored external-call return values — silent failures from low-level call/delegatecall/staticcall, ignored ERC20 transfer return values, return-data length issues. Activate on `.call`, `.delegatecall`, `.staticcall`, `.send`, `transfer`/`transferFrom` (without SafeERC20), and any function returning `bool` whose return is discarded.
(bool ok,) = target.call(data); // ← ok unused // or worse: target.call(data); // ← Solidity ≥0.5 still allows this with warning
Always `require(ok, "call failed");` unless an intentional best-effort.
USDT and other non-conformant tokens don't return `bool`. Naive call:
IERC20(usdt).transfer(to, amt); // ← reverts on USDT due to ABI mismatch
Use OZ `SafeERC20`'s `safeTransfer` which handles missing return values.
Even compliant ERC20 returning `false` instead of reverting is silently passed:
bool ok = token.transfer(to, amt); // ← ok unused, returns false on failure
try external.call() { /* … */ }
catch { /* silently ignored */ }Without inspecting `catch (bytes memory reason)`, you lose all info; transitioning a critical revert into a silent success is a bug.
(bool ok,) = target.call(data); require(ok);
`ok = true` even if `target` is an EOA with no contract — `call` returns true. Add `target.code.length > 0` check.
If `permit` reverts (because it was already used), the consuming transferFrom continues with a stale allowance. Use `try`/`catch` only to skip the permit reuse case.
Some routers continue on per-call failure. If a treasury sweeps via multicall, a single failed call could drop revenue.
Forwarding 2300 gas is brittle on Berachain/Arbitrum/etc. — see [[dos-vectors]].
| Pattern | Severity | |---|---| | `.call` ignored, leads to silent fund loss | **Critical** | | Non-SafeERC20 with USDT/non-conformant token | **High** | | `target.call` without code-length check | **High** | | Try-catch swallows revert without inspection | **High** | | Multicall fail-skip on revenue path | **High** | | `.transfer` (2300 gas) on L2 / to smart-wallet receivers | **Medium** | | Best-effort fire-and-forget event hook | **Low** *(document intent)* |
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
Repo: omermaksutii/RugProof
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on `<your trigger keywords or…
Detect unsafe assumptions about Solady's gas-optimized ERC20/ERC2612 permit and DN404 metadata. Solady's ERC20 uses custom storage slots, returns bools via…
Detect front-runnable ownership initialization in Solady Ownable / OwnableRoles. Solady's `_initializeOwner` is a guarded one-time setter (it reverts with…
Detect Solady SafeTransferLib calls that assume the token has code. SafeTransferLib.safeTransfer/safeTransferFrom/safeApprove deliberately skip the EXTCODESIZE…
Detect Uniswap V4 hooks that fail to settle currency deltas with the PoolManager. Every credit/debit a hook creates (BeforeSwapDelta, afterSwap hookDelta,…
Detect Uniswap V4 hooks whose address-encoded permission flags don't match the callbacks the hook actually implements. In V4 the hook's permissions live in the…