In the previous post, we covered arrays and structs in Solidity, which are essential building blocks for data organization in smart contracts. In this post, we’ll continue with our simple bank setup example and explore memory, calldata, and storage, key concepts for understanding how data is handled in Solidity.
EVM and Data Storage
The Ethereum Virtual Machine (EVM) provides several places to store and access data, including:
- Memory
- Calldata
- Storage
- Code
- Logs
- Stack
For this blog, we’ll focus on memory, calldata, and storage, the most commonly used in Solidity.
Memory and Calldata
Both memory and calldata store data temporarily, meaning the variables are only available during the execution of a function call. Once the function finishes executing, these variables disappear.
Let’s break this down with an example using our bank setup.
function createUserAccount(uint256 _balance, string memory _name) public {
userAccounts.push(Username(_balance, _name));
}
Here, _name is a memory variable. It exists only for the duration of the function call and cannot be accessed again once the function finishes executing. For example, after calling createUserAccount, you won’t be able to retrieve _namebecause it was stored in memory temporarily.
Now, let’s replace memory with calldata:
function createUserAccount(uint256 _balance, string calldata _name) public {
userAccounts.push(Username(_balance, _name));
}
When you deploy this code and execute the function, it will work exactly the same as the memory version. Both memoryand calldata indicate temporary variables, but there’s one key difference.
Memory vs. Calldata
The main difference is:
- Memory variables can be modified.
- Calldata variables cannot be modified.
Let’s see this in action.
Using a memory variable:
function createUserAccount(uint256 _balance, string memory _name) public {
_name = "pwnmachine"; // Modifying the memory variable
userAccounts.push(Username(_balance, _name));
}
This code will compile successfully because memory variables can be changed within the function.
Now, try the same with a calldata variable:
function createUserAccount(uint256 _balance, string calldata _name) public {
_name = "pwnmachine"; // Attempting to modify the calldata variable
userAccounts.push(Username(_balance, _name));
}
This will throw the following error:

Since calldata variables are read-only, you cannot modify them within a function. This makes calldata ideal for parameters that are passed into a function but shouldn’t be changed.
Storage: Permanent Data
Unlike memory and calldata, storage variables are permanent. They persist across function calls and are stored on the blockchain.
For example, in our contract, the balance variable is a storage variable:
uint public balance;
This is a permanent variable. You can always click on balance in Remix IDE to retrieve its current value because it is stored in the contract’s state.
What Happens if We Use storage in a Function?
If we try to replace memory or calldata with storage in a function parameter, Solidity will throw an error. For example:
function createUserAccount(uint256 _balance, string storage _name) public {
userAccounts.push(Username(_balance, _name));
}
This code will not compile because function parameters are inherently temporary, and storage is only for permanent variables.

Variables declared inside a function are temporary, meaning they only exist during the function’s execution. Since storage is for permanent variables, it cannot be used for function parameters.
Strings and Structs Need Memory or Calldata
In Solidity, data structures like arrays, mappings, and structs must use the memory or calldata keyword when passed as function parameters.
For example:
- Strings are arrays of bytes, so they require
memoryorcalldata. - Structs and arrays need to explicitly define their temporary nature using the same keywords.
Here’s our working function with memory and calldata:
function createUserAccount(uint256 _balance, string memory _name) public {
userAccounts.push(Username(_balance, _name));
}
function createUserAccountWithCalldata(uint256 _balance, string calldata _name) public {
userAccounts.push(Username(_balance, _name));
}
Both functions work, but use calldata if you don’t need to modify the input variable.
Conclusion
In this post, we explored how memory, calldata, and storage are used in Solidity. Here’s a quick recap:
- Memory: Temporary and modifiable.
- Calldata: Temporary but read-only.
- Storage: Permanent and modifiable.
These concepts are important for efficient and secure smart contract development. Understanding where and how to store data can significantly impact the contract’s performance and behavior.
Stay tuned for the next post, where we’ll lead about mappings and explore how they simplify key-value pair data management in Solidity!