Solidity Storage Slots Cut Smart Contract Gas Costs 40%

Blockchain
Date:October 13, 2026
Topic:
Solidity Storage Slots Cut Smart Contract Gas Costs 40%
⏱ 3 min read

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.

solidity
// Bad layout - 3 slots
contract BadLayout {
    uint128 a; // slot 0, 16 bytes
    uint256 b; // slot 1, 32 bytes
    uint128 c; // slot 2, 16 bytes (wasted 16 bytes in slot 0)
}

// Good layout - 2 slots
contract GoodLayout {
    uint128 a; // slot 0, 16 bytes
    uint128 c; // slot 0, 16 bytes (packed with a)
    uint256 b; // slot 1, 32 bytes
}

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.

LayoutSlots UsedFirst Write GasSubsequent Write Gas
Bad (scattered)360,00015,000
Good (packed)240,00010,000
Optimal (tight)120,0005,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.

💡
TipUse `forge inspect <Contract> storage-layout` or `hardhat storage` to visualize slot assignments before deploying. Catch layout bugs in CI, not on mainnet.

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.

solidity
// Storage layout verification in Foundry test
function testStorageLayout() public {
    vm.startPrank(deployer);
    GoodLayout good = new GoodLayout();
    BadLayout bad = new BadLayout();
    
    // Verify slot assignments
    assertEq(vm.load(address(good), bytes32(0)), 0); // slot 0: a + c
    assertEq(vm.load(address(good), bytes32(1)), 0); // slot 1: b
    assertEq(vm.load(address(bad), bytes32(0)), 0);  // slot 0: a
    assertEq(vm.load(address(bad), bytes32(1)), 0);  // slot 1: b
    assertEq(vm.load(address(bad), bytes32(2)), 0);  // slot 2: c
}

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.

solidity
assembly {
    // Pack two uint128 into one slot manually
    let packed := or(shl(128, val1), val2)
    sstore(slot, packed)
    
    // Unpack
    val1 := shr(128, sload(slot))
    val2 := and(sload(slot), sub(exp(2, 128), 1))
}

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.

⚠️
WarningStorage gaps only work if you never remove them. A gap removed in v2 shifts all subsequent variables. Treat gaps as permanent reserved space.

✦

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.

Share𝕏 Twitterin LinkedInin Whatsapp