Lesson 17 of 25
Storage vs Memory vs Stack
Understand EVM data locations and how they affect gas costs.
IntermediateThe Three Data Locations
Every variable in Solidity has a data location. Understanding this is critical for both correctness and gas efficiency.
- storage — persistent, on-chain. Expensive (20,000 gas to write a new slot).
- memory — temporary, in-function. Cheap, erased after the call.
- stack — for simple value types inside functions. Very cheap, limited to 1024 slots.
- calldata — immutable external function parameter data. Cheapest for read-only params.
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract DataLocations {
uint256[] public storageArr; // lives in storage
function work(uint256[] calldata input) external {
// calldata: read-only external param (cheapest)
uint256[] memory memArr = new uint256[](input.length);
// memory: temp array, erased after this function ends
for (uint i = 0; i < input.length; i++) {
memArr[i] = input[i] * 2;
}
// Push to storage — the only way to persist changes
for (uint i = 0; i < memArr.length; i++) {
storageArr.push(memArr[i]);
}
}
}
Storage References vs Copies
When you assign a
storage struct to a local variable, you get a reference (pointer). Modifications affect the original. To make a copy, use memory.
Solidity
struct Item { uint256 price; bool available; }
Item[] public items;
function updatePrice(uint256 idx, uint256 newPrice) public {
// Storage reference — modifies items[idx] directly
Item storage item = items[idx];
item.price = newPrice; // ✅ persists on-chain
}
function readItem(uint256 idx) public view returns (Item memory) {
// Memory copy — safe read-only snapshot
Item memory copy = items[idx];
copy.price = 999; // changes copy only, not storage
return copy;
}
Gas Cost Comparison
Understanding gas costs for data locations helps you optimize contracts dramatically.
Solidity
// Gas costs (approximate, EIP-2929):
//
// SSTORE new slot: ~20,000 gas ← very expensive
// SSTORE update slot: ~5,000 gas
// SLOAD cold slot: ~2,100 gas
// SLOAD warm slot: ~100 gas
// MSTORE (memory): ~3 gas
// Stack operation: ~3 gas
// Optimization pattern: cache storage in memory
function sumStorageArray() public view returns (uint256 sum) {
uint256[] memory arr = storageArr; // cache in memory once
for (uint i = 0; i < arr.length; i++) {
sum += arr[i]; // reads from memory, not storage
}
}