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 Solady SafeTransferLib calls that assume the token has code. SafeTransferLib.safeTransfer/safeTransferFrom/safeApprove deliberately skip the EXTCODESIZE check that OpenZeppelin's SafeERC20 performs, so a call to an EOA or a self-destructed/not-yet-deployed token address
$ npx -y skills add omermaksutii/RugProof --skill solady-safetransferlib-no-contract-check --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/solady-safetransferlib-no-contract-checkContext preview
The summary Claude sees to decide when to auto-load this skill.
Detect Solady SafeTransferLib calls that assume the token has code. SafeTransferLib.safeTransfer/safeTransferFrom/safeApprove deliberately skip the EXTCODESIZE check that OpenZeppelin's SafeERC20 performs, so a call to an EOA or a self-destructed/not-yet-deployed token address
name: solady-safetransferlib-no-contract-check description: Detect Solady SafeTransferLib calls that assume the token has code. SafeTransferLib.safeTransfer/safeTransferFrom/safeApprove deliberately skip the EXTCODESIZE check that OpenZeppelin's SafeERC20 performs, so a call to an EOA or a self-destructed/not-yet-deployed token address returns success with no transfer. Activate whenever code imports solady SafeTransferLib, calls safeTransfer/safeTransferFrom on a user-supplied or upgradeable token address, or routes arbitrary tokens.
Trigger on any of:
using SafeTransferLib for address;
function rescue(address token, address to, uint256 amt) external onlyOwner {
token.safeTransfer(to, amt); // ← if `token` has no code, this SUCCEEDS silently
}Solady's `safeTransfer` reverts only if the call itself reverts OR returns a non-truthy bool. A call to an address with no code returns success and empty returndata, which Solady treats as a passing transfer. **Signal:** SafeTransferLib used on a token address that is never verified to contain code (`token.code.length > 0`).
function deposit(address token, uint256 amt) external {
token.safeTransferFrom(msg.sender, address(this), amt); // no-op if token is an EOA
shares[msg.sender][token] += amt; // credited anyway
}Attacker passes an EOA they control as `token`; the "transfer" no-ops but `shares` is credited, minting unbacked accounting balance. **Signal:** `safeTransferFrom` result drives state without a balance-delta check and the token address is attacker-controlled.
address predicted = _computeAddress(salt); predicted.safeTransfer(to, amt); // token not deployed yet → silent success
**Signal:** transfer to an address computed/predicted before the contract is known to be deployed.
| Pattern | Severity | Notes | |---|---|---| | User-supplied token credited without balance-delta check | **High** | Mints unbacked balance / drains pool | | safeTransfer to address with no code in fund-moving path | **High** | Funds "sent" but never move | | Rescue/admin path only (trusted token) | **Medium** | Centralization-bounded, op error | | Hardcoded, audited token address | **Info** | Code presence is implied constant |
1. **Explicit code check** — `require(token.code.length != 0, "no token");` before the first transfer. 2. **Balance-delta accounting** — measure `IERC20(token).balanceOf(address(this))` before/after and credit the delta, never the requested `amount`. 3. **Token allowlist** — only accept registry-approved tokens whose code presence is established at registration. 4. **Use OZ SafeERC20** if you specifically want the built-in `isContract`/`functionCall` revert-on-no-code behavior and can afford the extra gas.
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 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…
Detect reentrancy in Uniswap V4 hooks via the PoolManager unlock/lock callback. V4 uses a singleton PoolManager with transient lock state; all pool mutations…