Free architect grade utility

Solidity Storage Layout Visualizer

Plan how Solidity packs your state variables into 32 byte slots, spot wasted bytes that cost gas, and compute the keccak256 derived value slot for any mapping key. Built for architects designing upgradeable contracts and gas optimisations.

No loginSolidity rulesMapping calculator
Storage slot packing visualThree storage slots showing packed and unpacked variables with byte boundaries.Slot 0 (32 bytes)address owner (20 bytes)buint64 userCountSlot 1 (32 bytes)uint256 totalSupply (32 bytes, takes the whole slot)Slot 2 (32 bytes)mapping (length stored here, values at keccak256(key . slot))

Step 1 — define your contract's state variables in declaration order

Step 2 — storage slot layout

Slot 0

3 variables packed3 bytes unused (consider reorder)
owneraddressaddress packed at byte 0
isPausedboolbool packed at byte 20
userCountuint64uint64 packed at byte 21

Slot 1

totalSupplyuint256uint256 packed at byte 0

Slot 2

balancesmappingmapping (slot reserved; values at keccak256(key . 2))

Slot 3

namestringstring (length stored here, data at keccak256(3))

Mapping value slot calculator

For mapping at slot N, the value slot for key K is keccak256(abi.encode(K, N)).

Value slot

0x06916d8435461b818de333b2efa8afb999951e4b20801faa2fa60303f498c7ee

About this tool

What this visualizer answers

The Solidity Storage Layout Visualizer shows how the compiler will pack your contract's state variables into 32 byte storage slots. Use it to plan upgradeable contract layouts, find gas wasted on poorly ordered variables, and derive the storage slot for any mapping key without running a forge or hardhat command.

The tool models the standard Solidity packing rules: variables fill slots in declaration order, fit into the current slot if there is room, and start a new slot if not. Dynamic types (string, bytes, mappings, dynamic arrays) reserve a full slot for length or hash anchor and place actual data elsewhere via keccak256.

How to use it

Add each state variable in the order your contract declares them. Pick the type from the dropdown. As you build the list, the slot table updates to show which variables share slots and which slots have unused bytes. The unused byte indicator surfaces packing opportunities.

For mapping value slots, use the calculator at the bottom. Enter the slot where the mapping is declared and the key in 32 byte hex form. The tool returns the keccak256 derived value slot, the same one your contract reads or writes when accessing mapping[key].

How storage packing works

EVM storage is a key value store of 32 byte slots. The Solidity compiler assigns each state variable a slot starting from 0, in declaration order. Static variables smaller than 32 bytes pack into the current slot if they fit, otherwise start a new one. Dynamic variables always start a new slot and store data elsewhere using keccak256 derivations.

Mapping values live at keccak256(abi.encode(key, slot)). Dynamic array items live at keccak256(slot) plus the index. Strings and bytes use a length flag in the slot and store the data at keccak256(slot) when long enough to overflow the slot itself.

Where storage layout most often surprises engineers

The first surprise is that ordering matters. A contract with three uint128 variables uses two slots if the first follows a uint256, but only two if all three are declared consecutively. Reorder when reviewing the visualizer output and watch the slot count drop.

The second surprise is upgradeable contracts. Adding a variable in the middle of an inheritance chain shifts every subsequent variable into a different slot. Existing on chain data continues to live at the original slot, so the new code reads garbage. OpenZeppelin's __gap pattern reserves trailing slots so additions are safe.

When this tool is the right one and when it is not

Use this visualizer for design and review work: planning a new contract's state, auditing a fork before changes, teaching storage layout in a workshop, or sanity checking what slot a mapping value lives at when reading raw storage with cast or eth_getStorageAt.

For the source of truth on a deployed contract, run forge inspect ContractName storageLayout in Foundry, or compile with Hardhat and inspect the artifact. Those commands account for inheritance, libraries, and any compiler version specific behaviour that this planning tool does not model.

Designing an upgradeable system?

Storage layout is one of the most common reasons upgradeable contracts go wrong in production. Bring the design for a focused review.

Book a smart contract review

Frequently asked questions

Why does storage layout matter?
Each storage slot in the EVM is 32 bytes and costs gas to read or write. Solidity packs multiple smaller variables into one slot when they declare consecutively. Bad ordering leaves slots half empty and adds slot reads, which costs real gas at scale. Storage layout also matters for upgradeable contracts because adding or reordering variables changes which slot existing data lives in.
How does Solidity decide what to pack?
Solidity packs variables in declaration order. If the next variable fits in the remaining bytes of the current slot, it joins. Otherwise it starts a new slot. Dynamic types (string, bytes, mappings, dynamic arrays) always start a new slot and reserve the full 32 bytes because their data lives elsewhere in storage.
How is the storage slot for a mapping value computed?
For a mapping declared at slot N and key K, the value slot is keccak256(abi.encode(K, N)). The tool exposes this in the mapping calculator section. Address keys must be padded to 32 bytes, integer keys are encoded as uint256. This is the same derivation that Solidity uses internally when you read or write mapping[key].
What about nested mappings?
For mapping(K1 => mapping(K2 => V)) declared at slot N, the inner mapping for outer key K1 starts at keccak256(abi.encode(K1, N)). The value for K2 inside that inner mapping is then keccak256(abi.encode(K2, keccak256(abi.encode(K1, N)))). Apply the formula recursively.
How do I save gas with packing?
Order small variables consecutively so they share a slot. uint128 plus uint128 fits in one slot. address (20 bytes) plus bool (1 byte) plus uint64 (8 bytes) also fits. Avoid placing a uint256 between two small variables, since the small ones get pushed into separate slots. Booleans are a particularly common waste, since each occupies a full byte.
What is __gap and why do upgradeable contracts use it?
OpenZeppelin's upgradeable base contracts include uint256[50] private __gap at the end of each contract. The reserved slots let future versions add variables without colliding with downstream contracts that inherit from them. The pattern is essential when shipping UUPS or Transparent proxy contracts.
Does this tool compile real Solidity?
No. The tool models the layout based on the variable list you enter, applying the standard Solidity packing rules. For real compiler output, run forge inspect ContractName storageLayout in Foundry, or hardhat compile with the --storage-layout flag in Hardhat. This tool is for planning and teaching, not the source of truth for a deployed contract.

Related services and reading

From layout to gas optimisation.

Author: Mudassir Khan. Last updated May 9, 2026. Layout rules verified against Solidity 0.8.x packing behaviour.