Skip to content
Back to skills

Storage Layout

ASecurity

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.

  • 36 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 4, 2026
blockchain

Security analysis

A100/100

Scanned October 4, 2026

npx -y skills add iktok90-design/ai-smart-contract-auditor --skill storage-layout --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Storage Layout?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Storage Layout
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/iktok90-design-storage-layout/badge)](https://www.skillsdirectory.com/skills/iktok90-design-storage-layout)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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.
---

# Storage layout detection

## When this applies

- UUPS / Transparent proxy upgrades
- OpenZeppelin `*Upgradeable` contracts
- 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)
```solidity
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)
```solidity
// 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.

### 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)
```solidity
assembly { sstore(0x0, value) }   // ← collides with the consumer's slot 0
```
Use 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> storageLayout` for 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 __gap` at 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 `immutable` variable 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)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…