You might have come across some Solidity tips to improve your code skills in order to save some gas, but, today I want to focus more on how understanding the Ethereum Virtual Machine can effectively save you gas costs on your smart contracts.

Since we are going to dive into Ethereum, I am going to leave here the snippet of its Yellow Paper which specifies the gas costs of the opcodes, and during the article we will be referring to them.



Tip #1: Cold access VS warm access

Gcoldsload: 2100 gas

Gwarmaccess: 100 gas

There we have our first OPCODES, the first one is specifying how much it costs to access a variable for the first time (or cold access) while the second one specifies how much it costs to access the variable a second time and further (warm access). As you can see the difference in price is quite big, so understanding this can make a big difference in the costs of your smart contract’s transactions. Let’s see an example.





Caching the data inside a function in Solidity can result in lower gas usage, even if it needs more lines of code. In this case, it is by switching the location of the array and instead of using it from storage and hence cold accessing it every time in the loop, it stores the array in memory where is cheaper to access it.

Tip #2: Zero vs non-zero values and gas refunds

Gsset = 20,000 gas

Rsclear = {discount on execution price}

Changing a value from 0 to non-zero on the Ethereum blockchain is expensive as we see in the price of Gsset, but changing a value from non-zero to 0, can give you a refund in gas value as per the opcode Rsclear. In order to not take advantage of the refund, it is established that you can only get refunded by up to a maximum of 20% of the total transaction cost.

You can find such a scenario in a very common scenario on blockchain, which is updating the balance of addresses in smart contracts. Let’s see an example of each:





  • In the first example ZeroToNonZero contract, non-zero to non-zero (5,000 gas*) + zero to non-zero (20,000 gas) = 25,000 gas

  • In the second example NonZeroToZero contract, Non-zero to zero (5,000 gas*) + zero to non-zero (20,000 gas) — Refund (4,800 gas) = 21,200 gas

*2,100 (Gcolssload) + 2,900 (Gsreset) = 5,000 gas

Tip #3: Order of state variables matter

The Storage is like a key-value data structure that holds the state variables values of a Solidity smart contract.

You can think of storage as an array which will help to visualize this. Each space in this storage “array” is called a slot and holds 32 bytes (256 bits) of data and each state variable declared in the smart contract will occupy a slot depending on its declaration position and its type.

Not all data types take all the 32 bytes of each slot as there are some data types (bool, uint8, address…) that take less than that.

The trick here is that if two/three or more variables together are 32 bytes or less, solidity’s compiler will try to pack them together in a single slot, but these variables need to be defined next to each other.





Here we are using the data types bool (1 byte), address (20 bytes), and uint256 (32 bytes). So, knowing the size of these variables you can easily understand that in the first example in the TwoSlots contract since we have bool and address together (1 + 20 = 21 bytes, which is less than 32 bytes) they will occupy one slot. On the ThreeSlots contract since bool and uint256 cannot be in the same slot (1 + 32 = 33 bytes, which is bigger than the slot capacity) in total we will be using three slots.

Now, why is this so important?

SLOAD opcode costs 2100 gas and it is used to read from Storage slots, so if you can store the variables in fewer slots, you will end up saving some gas.

Tip #4: uint256 is cheaper than uint8

We have learned in tip #3, that uint256 (256 bits = 32 bytes) occupies by itself a slot and we have learned as well that uint8 is less than 32 bytes. So, while it is kind of straightforward that 8 bits are smaller than 256 bits, how come uint256 is cheaper?

In order to understand that it is important to know that if a variable does not fill itself the whole slot and if this slot is not filled by any other variable, the EVM is going to fill the rest of the remaining bits with “0”s in order to be able to manipulate it.

This “0” addition performed by the EVM will cost gas, meaning that in order to save transaction gas, it is better to use uint256 instead of uint8.

__________________

Hopefully, while finding out about these tips to reduce the gas costs in your smart contracts you have learned as well a bit of how the EVM works.

__________________

Twitter @TheBlockChainer to find more daily updates about Smart Contracts, Web3 Security, Solidity, Auditing smart contracts, and more.

__________________