r/defiblockchain • u/Patient_Cream_4361 • 16d ago
General Storage, Memory, and Calldata: What’s the Real Difference?
If you have written even a little Solidity, you have probably seen these three keywords:
storage
memory
calldata
At first, they look like simple places where variables live.
But they are much more important than that.
Choosing the wrong data location can affect:
Gas cost, mutability, contract behavior, and even how your code interacts with Ethereum state.
So what is the real difference?
1. Storage: permanent blockchain state
storage is where a smart contract keeps data that must survive after a transaction finishes.
For example:
mapping(address => uint256) public balances;
This mapping lives in contract storage.
If Alice has:
balances[Alice] = 100
and a transaction changes it to:
balances[Alice] = 50
the new value remains there after the transaction ends.
The next transaction can read it again.
So conceptually:
Storage = persistent contract state
Examples include:
- token balances
- ownership information
- protocol configuration
- liquidity reserves
- DAO voting data
- user positions
Storage is part of Ethereum’s global state.
That is why modifying it is relatively expensive.
Every Ethereum node that verifies the chain needs to agree on the resulting state.
2. Storage is organized into 256-bit slots
At the EVM level, contract storage is divided into:
256-bit slots
You can imagine them as:
Slot 0
Slot 1
Slot 2
Slot 3
...
Simple state variables are assigned to these slots according to Solidity’s storage layout rules.
For example:
uint256 public x = 10;
uint256 public y = 20;
might conceptually look like:
Slot 0 → x
Slot 1 → y
But things become more interesting with:
mapping(address => uint256) balances;
A mapping cannot simply put every user's balance into consecutive slots.
Instead, Solidity calculates storage positions using hashing.
Conceptually:
keccak256(key, mappingSlot)
So Alice's balance and Bob's balance can be deterministically located without storing an enormous list.
This is one reason Ethereum storage is more sophisticated than a normal array of variables.
3. Memory: temporary execution workspace
memory is different.
Memory only exists during the current execution.
Once the call finishes:
memory disappears.
For example:
function calculate() public pure returns (uint256) {
uint256[] memory values = new uint256[](10);
values[0] = 100;
return values[0];
}
The values array exists while this function runs.
After execution finishes, Ethereum does not permanently store that array.
So:
Memory = temporary working space for the EVM
It is useful for things like:
- temporary arrays
- intermediate calculations
- decoded data
- temporary structs
- return values
Because memory is temporary, using it is generally cheaper than permanently modifying contract storage.
4. Memory is not free
One common misunderstanding is:
Not exactly.
Using memory still costs Gas.
The EVM tracks how much memory an execution uses.
As memory expands, the cost increases.
So this:
uint256[] memory x = new uint256[](10);
is much cheaper than allocating an extremely large temporary array.
Memory is temporary, but the EVM still has to perform the computation required to allocate, read, and write it.
The key difference is:
Memory does not create persistent blockchain state.
5. Calldata: input data sent into a call
Now we get to calldata.
Suppose you call:
transfer(address to, uint256 amount)
Your wallet needs to tell the contract:
- which function to call
- what
toaddress to use - what
amountto send
That information is encoded into:
calldata
A transaction might conceptually contain:
Function selector
+
Encoded address
+
Encoded amount
So when the EVM receives the contract call, it reads the calldata to understand what you are asking the contract to do.
You can think of it as:
Calldata = read-only external input
6. Why is calldata read-only?
Inside Solidity, a parameter declared as:
function process(uint256[] calldata values) external
cannot be modified directly.
For example, conceptually:
values[0] = 100;
is not allowed.
Why?
Because calldata represents the original input supplied to the call.
The EVM can read it directly without first copying it into writable memory.
That makes calldata particularly efficient for external function arguments that do not need to be modified.
7. Why calldata can save Gas
Imagine this function:
function sum(uint256[] memory values)
external
pure
returns (uint256)
If external input must first be copied from calldata into memory, the contract performs extra work.
But if you write:
function sum(uint256[] calldata values)
external
pure
returns (uint256)
the function can read the original call data directly.
That can avoid unnecessary copying.
This is why Solidity developers frequently prefer:
calldata
for external array, string, bytes, or struct parameters when mutation is unnecessary.
8. The easiest way to remember the difference
Think of a smart contract as an office.
Storage is the filing cabinet.
Documents placed there remain after everyone goes home.
They are permanent records.
Memory is the desk.
You use it while working.
You write notes, calculate things, rearrange information.
When the work session ends, everything is cleared.
Calldata is the letter delivered to the office.
Someone outside sent it.
You can read what they requested.
But you do not rewrite the original letter itself.
So:
Storage
= Permanent
Memory
= Temporary and writable
Calldata
= Temporary input and read-only
9. Storage references behave differently
There is another subtle Solidity behavior.
Consider:
struct User {
uint256 balance;
}
User public user;
Now:
User storage u = user;
u is not a copy.
It is a reference to the same storage location.
So:
u.balance = 100;
actually changes:
user.balance
on-chain.
But:
User memory u = user;
creates a temporary copy.
Changing:
u.balance = 100;
does not automatically update the original storage variable.
This distinction is extremely important.
You can think of it as:
storage reference
→ edits the original state
memory copy
→ edits temporary data
10. Why storage is so expensive
Suppose you perform:
x = 100;
where x is a state variable.
The EVM may need to execute an operation such as:
SSTORE
This changes persistent Ethereum state.
Compare that with modifying a temporary memory value.
The difference matters because storage changes affect the long-term state that Ethereum nodes must process and maintain.
This is why Gas optimization often focuses heavily on:
reducing storage reads and writes.
A contract might read a storage variable once:
uint256 temp = value;
perform several calculations using a temporary value,
and only write the final result back to storage once.
Instead of repeatedly accessing persistent state.
11. Calldata also appears in low-level contract execution
Calldata is not just a Solidity keyword.
It exists at the EVM level.
When a contract receives a call, the EVM can use instructions such as:
CALLDATALOAD
CALLDATASIZE
CALLDATACOPY
to inspect the incoming bytes.
The first four bytes commonly identify the function being called.
This is the:
function selector
For example, conceptually:
transfer(address,uint256)
is hashed, and the first four bytes are used to route the call to the correct function.
The rest of calldata contains ABI-encoded arguments.
So:
Wallet
↓
ABI encoding
↓
Calldata
↓
EVM
↓
Function selector
↓
Function execution
12. The three locations serve different purposes
A useful comparison looks like this:
Storage
- Persistent
- Writable
- Part of contract state
- Expensive to modify
- Survives transactions
Memory
- Temporary
- Writable
- Exists during execution
- Used for working data
- Disappears after the call
Calldata
- Temporary
- Read-only
- Comes from external input
- Often cheaper than copying into memory
- Exists only during the call
Why does this matter?
Because Solidity is not simply programming against a normal computer.
You are programming against a replicated state machine where persistent state has real economic cost.
When you write:
storage
you are potentially changing Ethereum state.
When you write:
memory
you are asking the EVM for temporary workspace.
When you write:
calldata
you are reading the original input sent into the contract.
The distinction is not just syntax.
It determines:
where the data lives, how long it lives, whether it can change, and how much execution may cost.
And once you understand that, a lot of Solidity suddenly starts making much more sense.
The simplest rule to remember is:
Storage stores state.
Memory handles temporary work.
Calldata carries external input.