storage-layout
Skill36 starsChanged 5 months agoMITView on GitHub
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.
In this repository44 other files it gives its agents.
- access-controlSkill
- approval-issuesSkill
- caching-and-incrementalSkill
- centralization-riskSkill
- confidence-scoringSkill
- cosmwasmSkill
- cross-chain-messagingSkill
- cross-contract-stateSkill
- delegatecall-risksSkill
- diamond-eip2535Skill
- dos-vectorsSkill
- erc1271-contract-signaturesSkill
- erc4337-account-abstractionSkill
- erc4626-inflationSkill
- false-positive-feedback-loopSkill
- fee-on-transferSkill
- flash-loan-attacksSkill
- initializationSkill
- inline-assemblySkill
- integer-issuesSkill
- intents-erc7683Skill
- known-good-comparisonSkill
- liquidation-cascadeSkill
- mev-frontrunningSkill
- mev-pbsSkill
- multi-pass-self-critiqueSkill
- oracle-manipulationSkill
- oracle-redundancySkill
- permit2-patternsSkill
- pragma-and-addressesSkill
- progress-and-streamingSkill
- reentrancySkill
- restaking-eigenlayerSkill
- selfdestruct-eip6780Skill
- signature-malleabilitySkill
- signature-replaySkill
- solana-anchorSkill
- stylus-rustSkill
- token-compatibilitySkill
- tx-context-misuseSkill
- unchecked-callsSkill
- ve-lock-governanceSkill
- vyper-specificSkill
- zk-verifier-bugsSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 2 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 30
Use EIP-1967 slots (bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)).
- Very specific: line 49
Use namespaced storage (keccak256("myapp.storage.foo")).
- Very specific: line 73
Run forge inspect <Contract> storageLayout for V1 and V2.
- Distinctive: line 76
Use EIP-1967 / EIP-7201 for namespaced storage in libraries and diamonds.
- Distinctive: line 77
Never modify or remove existing storage slots — only append.
≈ “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.
Files
Storage layout detection
When this applies
- UUPS / Transparent proxy upgrades
- OpenZeppelin
*Upgradeablecontracts - EIP-2535 Diamonds with shared storage
- Libraries that read storage via assembly
- Any change that modifies inheritance order or struct layout in an upgradeable contract
- Initializers that depend on storage being zero
Detection patterns
Missing __gap (HIGH)
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.
Slot collision between proxy and implementation (CRITICAL)
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)).
Inheritance reorder breaks layout (HIGH)
// V1: contract X is A, B, C
// V2: contract X is A, C, B ← B's vars now at C's old slotsEven one append to a parent breaks all descendants.
Struct field reorder/insert (HIGH)
Solidity packs adjacent same-bit-width fields. Inserting a bool between two uint256 adds a slot. Verify with forge inspect <Contract> storage.
Type widening (HIGH)
uint8 → uint256 changes packing.
Library uses fixed assembly slot (MEDIUM-HIGH)
assembly { sstore(0x0, value) } // ← collides with the consumer's slot 0Use namespaced storage (keccak256("myapp.storage.foo")).
Diamond storage namespace collision (HIGH)
Two facets using the same bytes32 STORAGE_POSITION. Always derive from a unique namespace string.
ERC-7201 namespaced storage missing on new code (MEDIUM)
Modern best-practice: use @custom:storage-location erc7201:my.namespace.
Severity rubric
| 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 |
Remediation patterns
- Run
forge inspect <Contract> storageLayoutfor V1 and V2; diff JSON. Any non-append change = abort. - OpenZeppelin Upgrades Plugin:
validateUpgrade()catches most layout breaks at build time. - Each upgradeable base contract reserves
uint256[N] private __gapat the end (size by convention). - Use EIP-1967 / EIP-7201 for namespaced storage in libraries and diamonds.
- Never modify or remove existing storage slots — only append.
False-positive notes
- Adding a constant or
immutablevariable doesn't change storage layout (lives in bytecode, not storage). - Pure function additions don't affect layout.
- Internal helper changes within a function don't affect layout.
Related
- [[initialization]] — re-init after upgrade
- [[delegatecall-risks]] — proxy mechanics
- [[upgrade-safety]] (command/skill bridge)
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
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.

