Every SSTORE operation burns 20,000 gas when writing to a fresh slot and 5,000 gas when updating an existing one. A single poorly packed struct can cost users hundreds of dollars per transaction on mainnet. Understanding how the EVM maps variables to 32-byte storage slots is the highest-leverage optimization most Solidity developers ignore.
How the EVM Lays Out Storage
The EVM treats storage as a key-value store where each key is a 32-byte slot. State variables are assigned slots sequentially in declaration order. Values smaller than 32 bytes get packed together until the next variable would overflow the slot. This packing happens at compile time, not runtime, so the layout is deterministic and inspectable.
Real Gas Impact Measurement
Deploy both contracts and call a function that writes to all three variables. The bad layout triggers three SSTORE operations on fresh slots (60,000 gas). The good layout triggers two (40,000 gas). That's a 33% reduction before any other optimizations. On a contract with dozens of state variables, savings compound to 40% or more.
| Layout | Slots Used | First Write Gas | Subsequent Write Gas |
|---|---|---|---|
| Bad (scattered) | 3 | 60,000 | 15,000 |
| Good (packed) | 2 | 40,000 | 10,000 |
| Optimal (tight) | 1 | 20,000 | 5,000 |
Packing Rules You Can Exploit
Elementary types pack greedily: uint8 through uint128, int8 through int128, bool, address, and bytes1 through bytes31 all share slots. Fixed-size arrays and structs start new slots. Dynamic arrays and mappings always occupy a new slot and use keccak256 for indexing. Order your variables from smallest to largest within logical groups to maximize packing density.
Inheritance and Upgradeable Contracts
Inheritance linearizes parent contracts C3 style. Parents' state variables occupy slots before children's. In upgradeable proxies (ERC-1967, UUPS), the implementation contract's storage layout must remain immutable. Adding variables in the middle shifts all subsequent slots, corrupting data. Always append new variables at the end and never reorder existing ones.
Advanced: Manual Slot Management
For maximum control, use assembly to write directly to specific slots. This enables patterns like transient storage (EIP-1153) via tstore/tload, or packing multiple mappings into a single slot using bitwise operations. The Diamond Standard (ERC-2535) uses explicit slot allocation to avoid collisions across facets.
Audit Your Existing Contracts
Run `forge inspect` on every deployed contract. Identify contracts with more than 10 storage slots. Reorder variables to pack tightly. For upgradeable contracts, plan storage gaps (empty uint256 arrays) in v1 to reserve slots for future variables without breaking layout.
✦
Open your largest contract in the IDE. Run the storage layout inspector. Find three variables you can reorder to save a slot. Deploy the optimized version to a testnet, measure the gas delta, then schedule the mainnet upgrade. The 40% savings compound across every user interaction for the lifetime of the contract.










