10 Simple Ways to Save Gas with Solidity

xtremetom
Coinmonks
5 min readSep 6, 2022

--

Below is a list of just a few simple ways you can save gas in solidity contracts. The full list is seemingly endless, but all boils down to finding the most efficient way to perform an action with the minimum number of operations.

Table of contents

  1. State data vs bytecode data
  2. The power of do-while vs for loops
  3. i++ vs ++i
  4. Ignore safe maths — unchecked{}
  5. Sticking to preferred data types
  6. Calldata vs memory
  7. Use memory to cache read variables
  8. Efficient variable management
  9. Save gas on the first write
  10. Calling struct data

1. State data vs bytecode data

By storing _b as a constant it is stored as immutable data in the contract’s bytecode. The same goes for our magic number 1000. _a on the other hand is stored in the contract’s state and requires a SLOAD operation to read it .

2. The power of do-while vs for loops

Loops are pretty standard in almost every coding language and that familiarity can make us complacent.

The standard for loop will check the condition before executing the statement. A do while will execute the statement at least once before and then check the conditions, allowing for gas savings.

Word of warning: Only use a do-while when you are certain the code should execute at least once and remember to handle your conditions to prevent an infinite loop and gas out.

For information on how do-while loops differ from while loops, check out this article:

3. i++ vs ++i

I know, I know, its a little crazy looking but the numbers dont lie — but why is it cheaper?

i++ increments the value of i by storing the original value in memory, incrementing it, then storing the resulting value into temporary memory and returning the original value. Upon that return the new value of i is updated. All in all it takes 4 operations.

++i increments the value of i and returns that value, which takes 2 operations.

4. Ignore safe maths — unchecked{}

From Solidity docs:

https://docs.soliditylang.org/en/v0.8.0/control-structures.html#checked-or-unchecked-arithmetic

Prior to Solidity 0.8.0, arithmetic operations would always wrap in case of under- or overflow leading to widespread use of libraries that introduce additional checks.

Since Solidity 0.8.0, all arithmetic operations revert on over- and underflow by default, thus making the use of these libraries unnecessary.

To obtain the previous behaviour, an unchecked block can be used:

Which means you can save gas by employing the unchecked block to avoid unnecessary checks on your ++i.

In the example code above it is safe to do this because we know ++i will not overflow 2²⁵⁶, both of our loops will stop processing when i == 200

5. Sticking to preferred data types

EVM (Ethereum Virtual Machine) is driven by 256 bit (32 bytes) variables. This means that in our loop above, the uint16 variable is converted to a uint256 before it is used, this conversion costs gas.

As you can see, we save gas by using uint256 for the variable i.

6. Calldata vs memory

From Solidity docs:

https://docs.soliditylang.org/en/v0.5.11/types.html?highlight=memory#data-location

Calldata is only valid for parameters of external contract functions and is required for this type of parameter. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory.

Its is suggested to try and use calldata where possible. It avoids unnecessary copies, hence the lower gas price and ensures the data is immutable.

Worth noting that since solidity 0.6.9 usage of memory and calldata is not limited by the visibility of the function.

For more details about the differences between storage, memory and calldata checkout this article:

7. Use memory to cache read variables

Accessing a cold storage variable (first read) costs 2100 gas and 100 gas there after — warm storage access

You can save gas by caching the storage variable and reading the cached data instead of the storage data. This also works when reading data from a storage array.

In the above example we are storing _arr in memory of SaveGas2 > loop(). All reads are then called from our new arr.

This is a great gas saving method as long as the storage array is not too big. The data has to be copied to memory, so big arrays will be inefficient.

8. Efficient variable management

If you plan on reusing a variable like solution and you know it will not inherit value from the previous loop, you can save gas by defining the variable outside of the loop — compare loops in SaveGas and SaveGas2.

Additionally, if you know there is no risk of overflow you can save more gas by nesting your solution calculation within the unchecked block.

9. Save gas on the first write

Changing a storage value to non-zero from a zero value will cost 20000 gas and all non-zero nth writes will cost 5000 gas. We can save the first user to call the function some gas by defining a non-zero value for _data.

10. Calling struct data

EVM stores data sequentially, trying to pack everything into 256 bit slots (32 bytes). In the example above, the struct data is arranged so x and y will occupy a single 256 bit memory slot and z will occupy its own slot — nice and tidy.

When it comes to reading our values we can save gas by minimizing how many memory slots we have to read and or cache.

SaveGas > getUserZ() is storing the relevant MyData struct in memory — making a copy of both memory slots and then reading the relevant data.

SaveGas2 > getUserZ() is reading data from the single memory slot containing z.

Where to find me

I can normally be found in the Cool Cats discord channel
https://discord.gg/WhBAAHnSz4

Or on Twitter
https://twitter.com/xtremetom

New to trading? Try crypto trading bots or copy trading

--

--

xtremetom
Coinmonks

Im a Found of Cool Cats, general Web 3 consultant with 20 yrs experience as a builder, marketer and company owner in Web 2.