In our previous lesson, we explored how to handle errors and saw that using custom errors is more gas-efficient than traditional error strings. This introduced a critical theme in smart contract development: gas optimization. Today, we will delve much deeper into this theme by examining one of its most fundamental aspects: how and where data is stored.
This lesson focuses on the different data locations available in the Ethereum Virtual Machine (EVM): storage, memory, and calldata. For someone with your background in data warehousing and ETL, this concept is highly analogous to managing data across different tiers of a data architecture. Choosing between writing to a permanent database table, processing data in-memory, or reading from a raw input file has significant performance and cost implications. In Solidity, the stakes are just as high, directly impacting the gas fees users pay for every transaction.
By the end of this lesson, you will be able to distinguish between storage, memory, and calldata, enabling you to make conscious decisions that optimize your smart contracts for gas efficiency—a crucial skill for building viable applications for tokenized money.
1. storage: The Permanent Ledger
The most critical and expensive data location in Solidity is storage. Think of this as the contract's permanent database or the tables in your Oracle or Snowflake data warehouse. Any data written to storage is recorded on the blockchain, persists across transactions and function calls, and is accessible to other smart contracts.
- Persistence: Permanent until explicitly changed or deleted.
- Scope: Accessible across the entire contract and externally (if public).
- Cost: Extremely high. Writing a new value to storage (
SSTOREopcode) is one of the most gas-intensive operations in the EVM.
State variables—those declared outside of any function—reside in storage by default.
12 Solidity Gas Optimization Techniques
This article from Alchemy provides excellent context on the cost of storage.
Please read the section Minimize on-chain data. Pay close attention to the gas cost comparison between SSTORE (storage) and memory operations. This highlights why minimizing on-chain data is the first rule of gas optimization.
The following video provides a conceptual overview and a simple demonstration of how state variables are implicitly storage and how we can create a storage pointer inside a function to modify them.
Storage, Memory and Calldata | Solidity 0.8
This video from Smart Contract Programmer clearly introduces the data locations and demonstrates storage.
Watch these two short segments: The definition of storage establishes it as the location for state variables. The storage example shows how declaring a variable as storage inside a function creates a pointer to a state variable, allowing you to modify the contract's persistent state.
The key takeaway is to be highly selective about what you store on-chain. It should only be data that is absolutely essential for the contract's logic and integrity, such as user balances or ownership records in a token contract.
2. memory: The Temporary Workspace
If storage is the permanent database, memory is the contract's RAM or scratchpad. It is a temporary, volatile data location that exists only for the duration of a function's execution. Once the function call ends, the data in memory is wiped clean.
- Persistence: Temporary; erased after function execution.
- Scope: Limited to the function in which it's declared.
- Cost: Low. Far cheaper than
storage.
You use memory for variables that you need to create and manipulate within a function but don't need to persist permanently. This includes creating arrays or structs for intermediate calculations or preparing complex data to be returned from a function. For reference types like arrays, strings, and structs, you must explicitly specify memory when declaring them inside a function.
This video from Dro Orozco offers a great analogy for storage vs. memory.
Watch the section on memory, which compares it to a computer's RAM, contrasting it with the hard drive (storage). This mental model is very effective.
Note that when you pass data from storage to a function that expects a memory variable, a copy is made. Modifying this memory copy will not change the original state variable in storage.
Storage, Memory and Calldata | Solidity 0.8
The "Smart Contract Programmer" video also demonstrates the practical use of memory.
Watch this segment on memory variables. It shows how memory is used for read-only copies of state data, as well as for function inputs, outputs, and creating new temporary arrays.
3. calldata: The Read-Only Input Stream
The third major data location, calldata, is a special, non-modifiable area where a function's arguments are stored. It is only available for parameters of external functions.
- Persistence: Temporary; exists only during function execution.
- Scope: Limited to
externalfunction parameters. - Cost: The cheapest of the three.
Think of calldata as a read-only input feed. When a user calls an external function with arguments, that data lives in calldata. If you declare a parameter as calldata, the function can read directly from this location without having to spend gas to copy it into memory. This makes it the most gas-efficient choice for function parameters that you only need to read.
If you try to modify a calldata variable, the compiler will throw an error, as shown in the image below.

The following reading explains this crucial gas-saving pattern.
12 Solidity Gas Optimization Techniques
The Alchemy article provides a perfect, practical explanation of when and why to use calldata.
Read section 7, calldata vs. memory. It clearly lays out the rule: "Use calldata for external function parameters you only need to read. Use memory only when you need to modify the data within your function."
This short video reinforces the concept, explaining that using calldata avoids an unnecessary data copy, which is how it saves gas.
Storage, Memory and Calldata | Solidity 0.8
The "Smart Contract Programmer" video concludes with a clear explanation of calldata.
Watch the segment on calldata. The key insight here is that passing a calldata variable to another internal function avoids a copy, directly saving gas.
4. Comparison and Summary
Now that we've covered the three main data locations, this table provides a concise summary of their characteristics.

As you can see, storage is for permanent state, memory is for temporary and modifiable data within a function, and calldata is for temporary, read-only function inputs. Choosing the right one is a key part of writing professional, optimized Solidity code.
A Note on Other Locations:
The table also mentions "Transient Storage," a newer feature (EIP-1153) for cheap, temporary storage that persists across function calls within the same transaction. It's a powerful tool for complex interactions but is more advanced. You may also hear about the stack, which the EVM uses automatically to store simple value types (uint, bool, address) within functions. It is extremely cheap, but we do not declare it explicitly. For now, mastering storage, memory, and calldata is the priority.
Conclusion
Understanding data locations in Solidity is fundamental to managing gas costs and writing efficient contracts. Just as in data engineering, where choosing the right storage and processing strategy determines the cost and performance of your system, your choices in Solidity have a direct financial impact on your users.
Here are the key takeaways:
storage: The permanent, on-chain database. It's persistent and very expensive. Use it only for essential state variables.memory: A temporary workspace for data within a function. It's cheaper thanstorageand is used for creating and modifying temporary variables.calldata: A read-only location for external function arguments. It is the cheapest option as it avoids copying data, making it ideal for parameters that are only read.
In our next lesson, we will build on this by learning how to write and apply function modifiers. These are reusable pieces of code that can check conditions before a function executes, such as verifying user permissions. Understanding data locations will be important, as modifiers often need to inspect the arguments passed to a function.