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 storage-layout issues in upgradeable contracts — slot collisions, slot reuse, packing changes, missing gap. Activate when reviewing UUPS/Transparent proxies, OZ Upgradeable contracts, diamonds (EIP-2535), libraries with structs, or any contract using assembly to read
$ npx -y skills add omermaksutii/RugProof --skill storage-layout --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/storage-layoutContext preview
The summary Claude sees to decide when to auto-load this skill.
Detect storage-layout issues in upgradeable contracts — slot collisions, slot reuse, packing changes, missing gap. Activate when reviewing UUPS/Transparent proxies, OZ Upgradeable contracts, diamonds (EIP-2535), libraries with structs, or any contract using assembly to read
name: storage-layout description: Detect storage-layout issues in upgradeable contracts — slot collisions, slot reuse, packing changes, missing gap. Activate when reviewing UUPS/Transparent proxies, OZ Upgradeable contracts, diamonds (EIP-2535), libraries with structs, or any contract using assembly to read storage slots.
contract BaseUpgradeable {
uint256 public foo;
// no uint256[50] __gap; ← can't append fields to derived contracts safely
}OZ convention: each base contract reserves `__gap` for future fields.
Implementation defines `address public owner` in slot 0; proxy uses slot 0 for its admin. Hello, hijacked proxy. Use EIP-1967 slots (`bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)`).
// V1: contract X is A, B, C // V2: contract X is A, C, B ← B's vars now at C's old slots
Even one append to a parent breaks all descendants.
Solidity packs adjacent same-bit-width fields. Inserting a `bool` between two `uint256` adds a slot. Verify with `forge inspect <Contract> storage`.
`uint8` → `uint256` changes packing.
assembly { sstore(0x0, value) } // ← collides with the consumer's slot 0Use namespaced storage (`keccak256("myapp.storage.foo")`).
Two facets using the same `bytes32 STORAGE_POSITION`. Always derive from a unique namespace string.
Modern best-practice: use `@custom:storage-location erc7201:my.namespace`.
| Pattern | Severity | |---|---| | Proxy/impl slot 0 collision | **Critical** | | Slot reuse after upgrade (data corruption) | **Critical** | | Struct reorder in upgrade | **High** | | Inheritance reorder | **High** | | Missing `__gap` in upgradeable base | **High** | | Diamond storage collision | **High** | | Library writing to fixed low slots | **High** | | ERC-7201 missing on new namespaced module | **Medium** | | Variable rename only (same slot) | **Info** |
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…