Storage Layout Versioning
Detects storage layout changes during proxy upgrades that could corrupt existing state, including reordered variables and changed types.
Storage Layout Versioning
Overview
The storage layout versioning detector identifies changes in storage variable ordering or types between implementation versions of an upgradeable proxy contract. When a proxy upgrades its implementation, the new code must use the same storage layout as the old code for all existing variables. Inserting a new variable before existing ones, removing a variable, or changing a variable’s type shifts all subsequent slots, causing the new code to read old data from wrong slots.
Why This Is an Issue
Storage layout corruption during upgrades silently breaks contract state. A uint256 balance at slot 3 in version 1 becomes address admin at slot 3 in version 2 if a variable was inserted before it. The balance value is now interpreted as an address, and the admin address is read from what used to be the next variable. This corruption is difficult to detect after the fact and can lock funds permanently.
How to Resolve
// Version 1
contract ImplementationV1 {
address public owner; // slot 0
uint256 public totalSupply; // slot 1
uint256 __gap[48]; // Reserve 48 slots for future variables
}
// Version 2 — CORRECT: new variable uses gap space
contract ImplementationV2 {
address public owner; // slot 0 — unchanged
uint256 public totalSupply; // slot 1 — unchanged
address public treasury; // slot 2 — uses first gap slot
uint256 __gap[47]; // Gap shrinks by 1
}
Detection Methodology
- Storage slot extraction: Extracts all storage access patterns from both old and new implementation bytecode.
- Slot mapping comparison: Compares the type and purpose of each slot between versions.
- Insertion detection: Flags cases where new
SSTOREoperations appear at slots that were used differently in the previous version. - Gap analysis: Checks for the presence of storage gap variables and validates they are maintained across versions.
Limitations
False positives: Fresh deployments with no predecessor version may be flagged against default comparison baselines. False negatives: Subtle type changes (e.g., uint256 to int256) at the same slot may not be detected from bytecode alone.
Related Detectors
- Storage Collision — detects proxy-implementation storage conflicts
- UUPS Initialization — detects uninitialized proxy implementations