Solidity: calldata, memory, and storage


There are three types of variables in Solidity:

  • Global variables
    • Provide information about the blockchain
    • For example, block.number, block.timestamp, or msg.sender
  • State variables
    • Declared outside a function
    • Stored on the blockchain
    • Also called "storage"
  • Local variables
    • Declared inside a function
    • Not stored on the blockchain, stored in memory instead
    • Erased between function calls


Data Types

There are two types of data:

  • Value types: YourContract, address, bool, uint256, int256, enum, and bytes32 (fixed-size byte arrays)
  • Reference types: array, mapping, and struct

It's worth noting that bytes and string are dynamically-sized byte arrays and are considered reference types. However, byte (bytes1), bytes2, ..., bytes32 are value types since they're fixed-size byte arrays.


Data Location

When using a reference type, you must explicitly provide the data location where the type is stored. There are three data locations:

  • storage is where the state variables are stored, and its lifetime is the lifetime of the contract
  • memory means variables are temporary and erased between external function calls
  • calldata behaves mostly like memory, but is immutable

For reference types in function arguments, you must declare them as memory or calldata.

If possible, use calldata as the data location because it avoids copying, reduces gas usage, and ensures that the data cannot be modified. Arrays and structs with calldata data location can also be returned from functions, but it is not possible to allocate such types.


It is also a best practice to use external if you expect that the function will only ever be called externally, and use public if you need to call the function internally. The difference between both is that public function arguments are copied to memory, while in external functions, arguments are read directly from calldata, which is cheaper than memory allocation. external functions are sometimes more efficient when they receive large arrays.


Assignment Behaviour

  • Assignments between storage and memory (or from calldata) always create an independent copy.
  • Assignments from memory to memory only create references.
  • Assignments from storage to a local storage variable also only assign a reference.
  • All other assignments to storage always copy.