freecoding.school100% FREE · NO SIGNUP
The EVM CoreISSUE #16 of 48

storage slots · packing · refunds

SoliaVSThe Reentrancy Reaper
Solia saysSolidity packs adjacent state variables into the same 32-byte storage slot when they fit, turning two writes into one and halving the cold-init gas.

Storage costs 22,100 gas to initialize a slot (20,000 init + 2,100 cold access). The biggest optimization is variable packing: declare adjacent state variables that fit in 32 bytes (two uint128, four uint64, etc.) and Solidity packs them into one slot — paying the cold-init cost once instead of per variable.

Packing is positional: variables are packed in declaration order, and a new variable starts a new slot if it does not fit in what is left. Mappings and dynamic arrays always start a fresh slot — their data lives at keccak256(slot ‖ key) for mappings and keccak256(slot) + index for arrays. The demo compares packed vs unpacked init costs.

Power-ups you unlock

The Reentrancy Reaper attacks — common mistakes

Boss battleCompare gas to initialize two separate uint256 fields versus two uint128 fields packed into one slot.

Example code

<!doctype html><html><head><meta charset="utf-8"></head>
<body style="background:#06040d;color:#e6e0ff;font-family:monospace;padding:20px"><pre id="o"></pre>
<script>
const SSTORE_COLD_INIT = 22100;        // 20000 init + 2100 cold access
// unpacked: two uint256 → two slots, two cold inits
const unpacked = 2 * SSTORE_COLD_INIT;
// packed: two uint128 → one slot, one cold init
const packed = SSTORE_COLD_INIT;
const savings = unpacked - packed;
document.getElementById('o').textContent = [
  'gas to initialize two adjacent state vars:',
  '  uint256 a; uint256 b;  → ' + unpacked.toLocaleString() + ' gas (two slots)',
  '  uint128 a; uint128 b;  → ' + packed.toLocaleString() + ' gas (one slot, packed)',
  '  savings:               ' + savings.toLocaleString() + ' gas (' + Math.round(100*savings/unpacked) + '% off)',
  '',
  'slot-index rules:',
  '  mapping(K=>V)  v;  →  data at  keccak256(key ‖ slot)',
  '  T[] arr;          →  data at  keccak256(slot) + index',
  '  mappings & dynamic arrays always start a fresh slot'
].join('\n');
</script></body></html>
▶ Open the interactive comic issue
‹ Memory Expansion · Quadratic CostPrecompiles · Ecrecover · Modexp · Pairings ›