Previous Module
Event Logs Explorer

💾 Contract Storage: How Data Lives On-Chain

Understand storage slots, state variables, and persistent data

💾 Understanding Contract Storage

Storage is the permanent memory of smart contracts—where state lives between transactions. Understanding how the EVM manages storage is crucial for writing efficient, cost-effective contracts.

🎯 Interactive: Storage Hierarchy Explorer

Explore the 4 data locations in the EVM:

💾

Storage

Permanent

Persistent data stored on-chain between function calls and transactions

Location
Blockchain
Gas Cost
~20,000 gas (initial)
Lifetime
Forever (or until modified)
Use Case
State variables, balances, user data
Access Pattern
Most expensive, but permanent

What Is Contract Storage?

Contract storage is permanent key-value data that persists on the blockchain. Unlike memory (temporary) or calldata (read-only), storage is where your contract's state lives—balances, mappings, arrays, and all state variables.

Key Characteristics:

1
Key-Value Store
2^256 slots, each 32 bytes (256 bits) - mapping keys to values
2
Expensive Operations
SSTORE: 20,000 gas (new), 5,000 gas (update), 2,900 gas (delete refund)
3
Permanent & Global
Accessible across all function calls, survives transactions
4
Default Initialization
All slots default to zero (0x00...00) until written

Why Storage Matters

💰
Gas Costs

Storage is the most expensive operation in smart contracts. Poor optimization can make your contract unusable due to high gas fees.

📦
Variable Packing

Multiple variables can share a single 32-byte slot, reducing gas costs by up to 75% with proper optimization.

🔍
Storage Layout

Understanding how Solidity assigns slots is critical for upgradeable contracts and avoiding storage collisions.

Performance

Reading from storage (SLOAD) costs 2,100 gas vs. 3 gas for memory—caching strategies can save thousands.

Storage vs. Other Data Locations

LocationPersistenceGas CostUse Case
StoragePermanent20,000 gas (new)State variables
MemoryFunction scope~3 gas/wordTemp calculations
CalldataRead-only16 gas/byteFunction args
StackOperation-level~3 gasPrimitives only

Real-World Example

User Profile Contract

contract UserProfile {
  // Storage variables (expensive!)
  mapping(address => string) public names;    // Slot 0
  mapping(address => uint256) public ages;    // Slot 1
  mapping(address => bool) public verified;   // Slot 2
  
  uint256 public totalUsers;                  // Slot 3
  
  function updateProfile(string memory _name, uint256 _age) public {
    // Memory variable (cheap, temporary)
    string memory tempName = _name;
    
    // Writing to storage (20,000 gas for new slot!)
    names[msg.sender] = tempName;
    ages[msg.sender] = _age;
    verified[msg.sender] = true;
    
    totalUsers++;  // Update storage (5,000 gas)
  }
}
💡 Gas Analysis:
  • • First call: ~65,000 gas (3 new mappings + counter)
  • • Update call: ~20,000 gas (updating existing values)
  • • Reading name: ~2,100 gas (SLOAD)
  • • Using memory for temp: Only ~3 gas/word

💡 Key Insight

Storage is like a hard drive for your contract—permanent but slow and expensive. The key to efficient contracts is:

  • Minimize writes: Only store what must persist between transactions
  • Pack variables: Fit multiple variables in one 32-byte slot
  • Cache reads: Load storage once into memory, reuse it
  • Use memory: Temporary calculations should never touch storage