Lesson 7 / 25
Storage, Memory, Calldata and Transient Storage
Choose data locations correctly and understand their costs.
Where data lives
Reference types need a data location. storage is persistent contract state on chain; reading and especially writing it is the most expensive operation (a new non-zero slot costs about 20,000 gas). memory is temporary, exists for the duration of a call and is cleared afterwards. calldata is the read-only input data of an external call, the cheapest location for array and string parameters you do not modify. Assignments behave differently by location: Item storage item = items[i]; creates a reference, so changes write to storage, while Item memory item = items[i]; makes a copy, so changes are lost unless written back, a classic bug. The stack holds value-type local variables (limited to 16 reachable slots, which is the origin of "stack too deep" errors). Storage is organised in 32-byte slots, and adjacent small variables are packed into one slot. Transient storage (EIP-1153, active since the Cancun upgrade in 2024) is cleared at the end of each transaction and is cheap; recent Solidity versions support the transient keyword for value types, useful for reentrancy locks.
Data locations
Storage persists across transactions; memory and calldata exist only during a call.
Storage references versus memory copies
A common bug and its fix.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Inventory {
struct Item {
string sku;
uint128 stock;
uint128 priceWei;
}
Item[] public items;
function add(string calldata sku, uint128 stock, uint128 priceWei) external {
items.push(Item(sku, stock, priceWei)); // calldata string copied into storage
}
// BUG: modifies a memory copy; storage is unchanged
function restockBroken(uint256 index, uint128 amount) external {
Item memory item = items[index];
item.stock += amount;
}
// FIX: a storage reference writes through to contract state
function restock(uint256 index, uint128 amount) external {
Item storage item = items[index];
item.stock += amount;
}
// calldata avoids copying a large array we only read
function totalStock(uint256[] calldata indexes) external view returns (uint256 total) {
for (uint256 i = 0; i < indexes.length; i++) {
total += items[indexes[i]].stock;
}
}
}
// stock and priceWei (uint128 each) pack into a single 32-byte storage slotRead storage once
Each storage read costs gas. In loops or repeated checks, copy a storage value into a local variable once (uint256 bal = balances[user];), work with it, and write the result back once.
Quick check: What does `Item memory item = items[i]; item.stock += 1;` do to contract storage?
- Nothing: it changes a temporary memory copy
- Increments the stored stock
- Deletes the item
- Reverts the transaction
Answer
Nothing: it changes a temporary memory copy — Memory variables are copies; use a storage reference to modify state.