Table of contents
Table of Content
Introduction
Storage
Memory
Stack
CallData
Summary
Introduction
In Solidity, every variable declared and used within a contract has a data location. The EVM(Ethereum Virtual Machine) allows us to store[data] variables in these four locations:
Storage
Memory
Stack
Calldata
Every declared variable is stored somewhere in these four data locations. However, where a variable is stored depends on its type and scope(where it is declared).
Storage
Storage is a global memory available to all functions and variables within a contract. Any variable(or data) stored in storage is essentially stored on the blockchain (i.e. every node within the Ethereum environment), The variable can be modified but its location can't be changed.
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract Storage {
// state variable stored in storage
uint[2] stateArray = [uint(1), 2];
// access stateArray from storage
function StorageExample() public view returns(uint) {
return stateArray[0]; // 1
}
}
In the preceding code, the global stateArray
variable is declared in the contract's global scope, which means it's stored in Storage and can accessed by any function within the contract.
Memory
Memory is a type of memory that can only be used within the scope of function. Variables declared within a function are only saved to memory temporarily—EVM deletes their location from memory after the function executes.
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract Memory {
function MemoryExample() public pure returns(uint) {
// stored in Memory
uint[2] memory localArray = [uint(2), 4];
return (localArray[0]); // 2
}
}
In the preceding code, the localArray
is declared and stored locally in the MemoryExample
function via memory
keyword.
Stack
The Stack is also a temporary storage like memory. It can only be accessed by a function during execution. To store a variable on the Stack you simply declare it in a function without a keyword.
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract Stack {
function StackExample() public pure returns(uint) {
// stored in Stack
uint x = 4;
return (x); // 4
}
}
It is good practice to minimize the number of variables stored on the Stack because of its limited size(It has a maximum size of 1024 elements and contains words of 256 bits) to avoid stack overflow.
Calldata
According to the solidity documentation:
Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory.
Calldata is simply where all incoming data to a function is stored, including arguments(which are the only variables stored in Calldata). It is a temporary memory location that depends on a function execution—Just like Memory and Stack. However, it is a non-modifiable memory location, which means variables(i.e. function arguments) stored here can't be modified.
Example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract CallData {
function calldataExample(string calldata x) external pure returns(string memory) {
// value passed to the x argument
return x;
}
}
A function that uses calldata must use the extenal
visibility keyword(as seen in the preceding code). Using calldata
instead of memory
for function arguments is more gas efficient.
Summary
In solidity, where a variable is stored is determined by two factors:
The location where the variable is declared(i.e global contract scope or function scope)
The data type of the variable(i.e. value or reference).
Note, the data location of a variable is important not only for the persistence of data but also for the semantics of assignment— i.e. the data location and type of a variable affects how the assignment operator(i.e. =
) operates on it. Learn more about data location and assignment behavior from the solidity docs.