What the hack is Memory and Storage in Solidity?

One of the most confusing concepts in solidity is how to effectively use memory and storage.

This article aims to provide a basic understanding of memory and storage and also demonstrate it’s effective usage with some examples.

If you are new to solidity, please read the below articles first:

Getting Started with ethereum blockchain development: part-1

Getting Started with ethereum blockchain development: Part-2

Before we move ahead, let’s understand the different kind of storage that solidity has:

Storage: The persistent memory that every account has is called storage. Storage is a key-value store where keys and values are both 32 bytes.

Memory: Memory is a byte-array, which hold the data in it until the execution of the function. Memory starts with zero-size and can be expanded in 32-byte chunks by simply accessing or storing memory at indices greater than its current size. 
To save gas, it is always recommended to shrink it’s size whenever possible.

Stack: It is used to hold small local variables. It costs same as memory, but can only hold a limited amount of values.

Gas Consumption while consuming space

This section of the article assumes that reader has a basic understanding of gas consumption in ethereum, if not refer below link.👇

What is gas in ethereum?

Everything you do on ethereum blockchain will cost you some gas. Let’s try to compare gas consumption in storage and memory.

Storage Gas consumption: Storage gas cost is easy to understand:

  • It costs 20,000 gas when a value is set to non-zero from zero
  • It costs 5,000 gas when writing to existing storage or setting a value to zero.
  • 5,000 gas is refunded when a non-zero value is set to zero.

Gas savings from packing storage is still possible, such as fitting 2 uint128 values within a single key, instead of using 2 keys.

Memory Gas consumption: The gas cost of memory expansion is defined in the Yellow Paper as follows:

Gas consumption in memory expansion

G_memory is 3, a is the number of 256-bit chunk words allocated.

For a =512 chunks, Gas consumption will be (3 * 512 + (512 * 512)/512) = 2560 units

Note: Cost of memory usage is not very significant as compared to storage. It’s a good practice to always use memory to perform intermediate calculations and store results in storage.

Where are variables stored?

For each type of variable, there exists default storage location::

  • State variables are always in storage.
  • Function arguments are always in memory.
  • Local variables of struct, array or mapping type reference are stored in storage by default.
  • Local variables of a value type (uint, int etc) are stored in the stack.

Let’s deep dive more into this and understand one more important concept around memory and storage !!!

Contract description:

  • Contract variable: Mapping to store key-value pair as user Id and User type.
    mapping(uint => User) users;
  • Methods: Followings are methods in the contract:

I. addUser: It takes uint id, uint balance as the argument and adds a user to mapping.

II. updateBalance: It takes uint id, uint balance as the argument and updates balance of the user.

III. getBalance: It takes uint id as an argument and returns the balance of the user.

I. Create a user by calling addUser method with arguments (id = 1 and balance = 10)

II. Update balance of user by calling updateBalance method with arguments(id = 1 and balance = 20)

  • Expected Output: Get the balance of user by calling getBalance method with an argument (id = 1). What do you think is the expected value? Have a guess? Is it 20 or 10 and why?🤔

Output is 10 not 20. 😯

Explanation: In solidity, when storage type variable is assigned to memory type it creates a new copy of data. If any update is done on copied data, it will not reflect in the original copy.

Can you now guess, what change you need to do in updateBalance method to make it work? 🤔

Change line 17 in the contract. Changing storage location from memory to storage will not create a new copy of data, rather it will create a pointer to storage. Hence any change in user object will be reflected in the storage itself.

User storage user = users[id];

Bingo!!! You now have a good understanding of memory and storage in the solidity 🙌

In the next few articles. I will explain about EVM internals and Merkel Pertrica trees.

Happy Coding 😇