Storage Layout Versioning Remediation
Overview
Related Detector: Storage Layout Versioning
An upgradeable proxy keeps its storage while swapping implementation code. If a new implementation reorders variables, removes one, inserts one before existing ones, or changes a type, every subsequent slot shifts — and the new code reads old data from the wrong place. The remediation is to treat storage layout as an append-only contract between versions.
Recommended Fix
Before (Vulnerable — inserting a variable shifts all later slots)
// V1
contract VaultV1 { address owner; uint256 total; }
// V2 inserts `paused` before `total` — `total` now reads what `owner` held.
contract VaultV2 { address owner; bool paused; uint256 total; }
After (Fixed — append only, and reserve a gap)
contract VaultV2 {
address owner;
uint256 total;
bool paused; // NEW variables are always appended at the end
uint256[49] private __gap; // reserve slots so future appends don't collide
}
Prefer namespaced storage (ERC-7201) for new upgradeable contracts, which isolates each module’s variables under a hashed slot and removes ordering fragility entirely. Always run a storage-layout diff between versions before upgrading.
Common Mistakes
- Inserting or reordering variables instead of appending, silently corrupting every later slot.
- Changing a variable’s type (e.g.
uint128touint256) so packed neighbours shift. - Removing a
__gapor shrinking it incorrectly when adding variables, reintroducing collisions.