Back to glossary

Storage in Solidity

Table of Contents

What is storage?

In the Ethereum Virtual Machine (EVM), storage refers to the persistent, contract-level key-value store used to hold state variables across contract calls and transactions. It is one of the three primary data locations in Solidity, alongside memory (temporary, function-level) and calldata (read-only input data).

At the EVM level, a fourth data location called transient storage was introduced in EIP-1153. It allows for temporary, transaction-scoped storage that is cleared when a transaction completes. As of Solidity v0.8.30, transient storage is accessible via the transient keyword for value types (e.g., uint256, bool). Support for reference types (like arrays, mappings, and structs) is not yet available but is expected in future compiler releases. Until then, low-level inline assembly remains the primary way to interact with transient storage.

Unlike memory or calldata, storage persists across transactions and is not automatically cleared. All data written to storage becomes part of the contract’s state and is stored on-chain. While this data is durable, it is modifiable and can be cleared using operations like delete or by setting its values to zero. 

Incorrect handling of storage references, especially with structs, arrays, or within functions that use storage pointers, can result in unintended overwrites or shared references, leading to subtle and difficult-to-detect bugs.

Purpose of storage

The primary function of storage is to persist contract state between function calls and transactions. State variables declared at the contract level (e.g., uint public counter, mapping(address => uint) balances) are placed in storage by default, making it the backbone for maintaining long-term data such as balances, configuration, and ownership. 

Key features of storage

  • Persistence: Data stored is maintained permanently across blockchain transactions and contract executions.

  • High Gas costs: Writing data into storage incurs significantly higher gas fees compared to memory or calldata due to its permanence in the blockchain’s state. Writing to a new storage slot costs 20,000 gas, though clearing a slot refunds 15,000 gas. Because these costs scale with the number of slots written or updated, minimizing unnecessary storage is a key optimization strategy in contract design.

  • Modifiable: Stored data can be updated or deleted, but every modification incurs gas costs proportional to the complexity and size of the data involved.

  • Structured as slots: EVM storage is organized as a linear array of 2²⁵⁶ slots, each 32 bytes in size. Simple state variables are assigned to sequential slots starting from slot 0. Complex data structures like mappings, arrays, and structs, rely on deterministic slot calculations (via keccak256 hashing) to store and retrieve nested or dynamic values. In upgradeable smart contracts using proxy patterns, developers must carefully manage storage layout to avoid slot collisions. Misaligned layouts can result in overwritten variables or corrupted state.

  • Per-contract scope: Storage is isolated per smart contract. Contracts cannot access another contract’s storage unless explicitly exposed via an interface. However, when using delegatecall (including libraries compiled with delegatecall), the code executes in the context of the calling contract’s storage layout. It can read from and write to the caller’s storage layout, a powerful but potentially dangerous mechanism. If storage layouts are misaligned, severe bugs or vulnerabilities can be introduced, especially in upgradeable proxy patterns.

Storage use cases

Storage finds extensive use across various smart contract scenarios:

  • Token balances: Tracking balances of ERC-20 tokens across user accounts using mappings like mapping(address => uint256).
  • Ownership records: Maintaining details of NFT ownership (e.g., ERC-721, ERC-1155), where each token ID maps to an owner address.
  • Governance voting: Maintaining voting results and participant records in decentralized autonomous organizations (DAOs), including vote weights, proposals, and execution results.
  • Contract settings: Storing protocol-wide configuration such as admin roles, interest rate models, pausing mechanisms, and quorum thresholds.

Memory vs. other data locations

Here’s a quick comparison of EVM data locations:

Property Storage Memory Calldata Transient storage
Modifiability Mutable Mutable Immutable (read-only) Mutable
Lifespan Persistent (on-chain) Temporary (during a message call) Temporary (during a message call) Temporary (during a transaction)
Gas cost High Moderate Low Low to moderate
Primary use Long-term contract state In-function computation Function arguments for external calls Temporary cross-functional data

Solidity code example of storage

contract StorageExample {
    uint256 public value;                // stored at slot 0
    mapping(address => uint256) public balances; // storage slot determined by keccak256(abi.encode(key, slot))
    
    function store(uint256 _val) external {
        value = _val; // Writes to storage slot 0
    }

    function updateBalance(address _user, uint256 _amount) external {
        balances[_user] = _amount; // Writes to slot: keccak256(abi.encode(_user, uint256(1)))
    }
}


In the example above:

  • value is stored at slot 0.
  • Each balances[_user] is stored at keccak256(abi.encode(_user, 1)), where 1 is the slot assigned to the balances mapping declaration. Slot 1 does not directly store data but serves as the base slot for the mapping. Solidity uses this “root” slot to compute unique keys for each mapping entry via a hash formula, ensuring deterministic and collision-free storage layout. This layout is critical when analyzing storage structure to avoid storage collisions in upgradeable contracts or when accessing storage directly via assembly.

Summary

Storage is a persistent, modifiable, contract-level data location in the EVM. It is used to maintain a long-term state across transactions, such as balances, configurations, and ownership records. While it comes with high gas costs, its permanence and determinism are foundational to contract behavior and user trust.

Cyfrin Updraft can help you master how and when to use storage to build reliable, stateful, upgrade-safe smart contracts. 

Related Terms

No items found.