Solidity Internal Storage Layout: Slots, Packed Variables & Transient Storage (EIP-1153)
By NorwegianSpark Editorial — written with AI assistance and reviewed by the NorwegianSpark SA editorial team | Last updated: 2026-04-14
This article contains affiliate links. We may earn a commission at no extra cost to you. Full disclosure
EVM Storage Architecture: 2^256 Slots of 32-Byte Words
In the Ethereum Virtual Machine, persistent contract state is stored in a dedicated, contract-specific key-value storage space containing individual slots. Each slot is exactly 32 bytes (256 bits) wide, initialized to zero ().
Accessing EVM persistent storage is the single most gas-expensive operation in smart contract execution:
- SLOAD (Read 32 bytes): Costs 2,100 gas for a cold slot read (first access in a transaction) and 100 gas for a warm slot read.
- SSTORE (Write 32 bytes): Costs 20,000 gas when setting a previously zero slot to a non-zero value, 2,900 gas when updating an existing non-zero value, and refunds up to a capped maximum when clearing a slot to zero.
Because storage costs dominate smart contract gas consumption, understanding the deterministic storage layout algorithms employed by the Solidity compiler is essential for writing gas-optimized and audit-secure protocols.
Storage variables declared at contract scope are allocated contiguous 32-byte slots starting from slot index , ordered sequentially according to their declaration in the Solidity source code.
Variable Packing Mechanics & Right-to-Left Slot Alignment
When state variables are smaller than 32 bytes (such as uint128, uint64, uint8, address (20 bytes), or bool (1 byte)), the Solidity compiler attempts to pack multiple adjacent variables into a single 32-byte storage slot.
Rules of Variable Packing:
- Contiguous Packing: Multiple variables are packed into the same slot only if they are declared consecutively and their combined byte length does not exceed 32 bytes.
- Little-Endian / Right-Aligned Packing: Within a single 32-byte slot, the first declared variable occupies the lowest-order bytes (the rightmost bits). Subsequent variables are packed to the left.
- Word Boundary Alignment: If adding the next variable would exceed 32 bytes, that variable is not split across slot boundaries; instead, the compiler advances to the next clean 32-byte slot, leaving the remaining bytes in the previous slot empty and wasted.
Consider the following contrast in struct variable ordering:
Unoptimized Layout (Consumes 3 Slots = 96 Bytes):
uint128 a;(Slot 0, 16 bytes used, 16 bytes empty)uint256 b;(Slot 1, cannot fit in Slot 0, occupies full 32 bytes)uint128 c;(Slot 2, occupies 16 bytes)
Optimized Layout (Consumes 2 Slots = 64 Bytes):
uint128 a;(Slot 0, bytes 0..15)uint128 c;(Slot 0, bytes 16..31 - perfectly packed!)uint256 b;(Slot 1, bytes 0..31)
By reordering variables, the contract saves one full SLOAD/SSTORE slot, reducing deployment costs by 20,000 gas and every subsequent state update by thousands of execution gas.
Storage Math: Calculating Locations for Mappings, Dynamic Arrays & Strings
Because dynamic data structures (mappings, dynamic arrays, and strings) have unbounded lengths, they cannot be stored in simple sequential slots without causing slot collision with subsequent state variables.
Solidity resolves this using cryptographic Keccak-256 hash formulas:
- Mappings:
If a mapping mapping(keyType => valueType) public myMap; is declared at slot :
- The slot itself remains completely empty (stores zero).
- The storage slot location for a specific key is calculated as:
where is the key padded to 32 bytes, is the slot index padded to 32 bytes, and represents binary concatenation.
- For nested mappings
mapping(k1 => mapping(k2 => v))at slot :
- Dynamic Arrays:
If a dynamic array uint256[] public myArray; is declared at slot :
- The slot stores the length of the array ().
- The array elements are stored contiguously starting at base location:
- The -th element is located at: .
- Short Strings and Bytes (< 32 bytes):
If a string or bytes is 31 bytes or shorter, Solidity utilizes Short String Optimization: the raw string characters are stored directly in the higher-order bytes of slot , while the lowest byte (byte 31) stores . If the string is bytes, slot stores , and the actual string data is stored at .
Yul Inline Assembly for Direct Bit-Level Storage Manipulation
When reading or writing packed variables in high-performance protocols (such as Uniswap v4 pool managers or Yearn vaults), developers use Yul inline assembly to avoid compiler-generated redundant masking and achieve maximum gas efficiency.
In Yul assembly:
- SLOAD: Loads a 32-byte word from storage: let value := sload(slot).
- SSTORE: Stores a 32-byte word into storage: sstore(slot, value).
- Bit-Shift & Masking: To extract packed variables from a slot, developers use bit-shift right (shr) and bitwise AND (and) operations.
Consider reading a packed uint64 variable located at byte offset 20 (bit offset 160) inside a 32-byte slot: The assembly executes let slotData := sload(0), shifts right by 160 bits (let shifted := shr(160, slotData)), and masks with 0xFFFFFFFFFFFFFFFF (let uint64Val := and(shifted, 0xffffffffffffffff)).
Direct Yul storage manipulation eliminates Solidity's automatic boundary checks and redundant memory allocations, reducing execution gas by up to 30% in tight computational loops.
Transient Storage (EIP-1153): TLOAD, TSTORE & 100-Gas Reentrancy Guards
Introduced in the Ethereum Cancun upgrade (March 2024), EIP-1153 introduced Transient Storage to the EVM via two new opcodes:
TSTORE(Opcode0x5D): Stores a 32-byte word in transient storage for 100 gas.TLOAD(Opcode0x5C): Loads a 32-byte word from transient storage for 100 gas.
Transient storage possesses unique operational characteristics:
- Scope: Behaves exactly like storage (accessible across multiple smart contract calls within the same transaction), but is completely discarded and wiped to zero at the end of the transaction execution.
- No Disk I/O: Because transient data is never written to the Ethereum state trie on disk, it requires zero Merkle trie recomputation, explaining its flat 100 gas cost.
The most transformative application of Transient Storage is the Zero-Overhead Reentrancy Guard.
Historically, OpenZeppelin's ReentrancyGuard used persistent storage (SSTORE), costing ~2,200 to 5,000 gas per protected function call to set a non-reentrant lock and clear it.
With EIP-1153 transient storage, setting and clearing a reentrancy lock costs exactly gas ( gas TSTORE(lockSlot, 1) + gas TSTORE(lockSlot, 0)), a gas reduction that fundamentally optimizes all modern DeFi protocols.
Storage Collision Vulnerabilities in Upgradeable Proxy Contracts
The deterministic slot layout of the EVM introduces severe security hazards when building Upgradeable Proxy Contracts (such as ERC-1967, UUPS, and Transparent Proxies).
In a proxy pattern:
- The Proxy Contract holds all persistent state in its own storage slots.
- The Implementation Contract contains the execution logic, executed via
delegatecall.
Storage Collision occurs when the new implementation contract modifies the variable declaration order, removes an existing variable, or inserts a new variable before existing ones:
- If Implementation V1 has
address ownerat Slot 0 anduint256 balanceat Slot 1. - Implementation V2 inserts
bool isPausedat the top (Slot 0). - When V2 reads
isPaused, it will read the lowest byte of the originalowneraddress. When V2 updatesowner, it overwritesbalanceat Slot 1!
To eliminate storage collisions permanently:
- ERC-7201 Namespaced Storage: Instead of starting variables at Slot 0, contracts allocate isolated storage structs at deterministic hash slots:
- Storage Gap Arrays: Including
uint256[50] private __gap;reserved slots at the end of base contracts to allow future state expansion without shifting downstream child contract storage slots.
## Frequently asked questions
Why does Solidity pack variables from right to left inside a 32-byte slot?
Solidity uses little-endian byte ordering within words, placing the first declared variable in the lowest-order (rightmost) byte positions of the 32-byte EVM storage word.
How much gas does EIP-1153 Transient Storage save compared to persistent storage for reentrancy locks?
Transient storage costs 100 gas for TLOAD and 100 gas for TSTORE (200 gas total per call), compared to 2,200 to 5,000 gas for traditional SSTORE-based reentrancy guards—a 90% to 95% gas saving.
What happens to transient storage variables at the end of a transaction?
All transient storage slots are completely wiped to zero at the end of the transaction and cannot be accessed by future transactions.
Related reading
- Transient storage reentrancy guards vs standard SLOAD guards — See how EIP-1153 eliminates reentrancy vulnerabilities while saving 95% in gas fees.
## Sources
- Layout of State Variables in Storage — Solidity docs
- EIP-1153: Transient storage opcodes — Ethereum Improvement Proposals
No contributor to this article holds a professional cryptography or security credential. Every technical claim above is sourced to primary protocol documentation rather than to personal authority — follow the sources and verify anything you intend to act on.
Not financial advice. Crypto assets are volatile and can lose value. This article describes how protocols work, not what you should buy.
Content on AICryptoCoin is for informational purposes only and does not constitute financial advice. Always do your own research and consult a qualified financial advisor before making investment decisions.
Related Articles
Formal Verification & Invariant Testing: Certora, Echidna & Foundry Fuzzing
17 min
Protocol Deep DivesSmart Contract Upgradeability Patterns: Transparent, UUPS, Diamond & Beacon Proxies
16 min
Protocol Deep DivesSmart Contract Attack Vectors: Reentrancy, Read-Only Reentrancy & Flash Loan Manipulation
18 min