Join, compete & win! Prize pool of $100,000
Search
Generic filters

Gas Optimizations in Solidity – Top Tips

Tweaking your code can be extremely helpful to reduce gas fees, making your project more profitable. If you’ve ever used smart contracts on blockchains such as Ethereum, you know how painful exorbitant fees can be. Even when you use other chains such as BNB Chain, which offers low fees, transactions can add up. Thus, knowing how to implement gas optimizations in Solidity will benefit your project immensely. Also, performing minor tweaks to your code can reduce gas fees significantly. Moreover, since Solidity applies to all EVM-compatible blockchains, we will focus on gas optimizations in Solidity herein. However, if you prefer building on Solana, you can apply similar tweaks using Rust. 

In this article, we’ll take a closer look at a relatively simple “for” loop. The first version of our function containing the loop will not be optimized at all. Then, we will create three variations of that function. Functionality-wise, they will all provide the same results. However, each of them will include more gas optimizations in Solidity than the previous one. As such, you’ll be able to see that pretty simple tweaks can make a huge difference, at least over time. Nonetheless, you’ll have a chance to take these tips and apply them to your future projects. This is an important aspect since you can’t avoid gas fees completely; either you or your users must cover them. Even when using the best Web3 backend platform, Moralis, you should still apply gas optimizations in Solidity to save on fees.

What is Solidity?

Are you not sure what Solidity is? If that’s the case, use the “Solidity” link stated earlier. However, knowing that it is an object-oriented programming language should be enough to help you understand this article. Moreover, Solidity is used for writing smart contracts on Ethereum and other EVM-compatible chains. Hence, if you want to become a blockchain developer who writes smart contracts, it is a coding language that you ought to get familiar with. Fortunately, it is by no means a must. With tools such as Remix and OpenZeppelin on your side, you can get far with knowing the very basics of Solidity. 

Furthermore, when using the pinnacle of the current Web3 tech stack, Moralis, plus your JavaScript proficiency and ability to use the most popular Web3 wallet, MetaMask, will get you quite far. With Moralis (a.k.a. Firebase for crypto), you get to create phenomenal dapps (decentralized applications) across multiple chains fast. So, if you are eager to start building, create your free Moralis account now.    

Gas Optimizations in Solidity – Example Tweaks

We believe that you will get the most out of this article if we take on a smart contract example. As such, we will focus on a particular function inside our smart contract. Moreover, we will be using Remix to deploy our smart contract. We will start with basic tweaks and then transition to more advanced gas optimizations in Solidity. Nonetheless, note that the “gas_optimization.sol” smart contract, which is the result of these example tweaks, is available on GitHub. It contains all variations of the function containing our “for” loop.

Our Smart Contract Example

Before we start applying any tweaks, let’s take a look at the first version of our smart contract example. Like normal practice dictates, it begins with the following “pragma” line at the top:

pragma solidity 0.8.7;

However, the actual contract begins with this line of code:

contract Gas_Test{

Inside our contract, we first define a couple of state variables stored on the blockchain: 

    uint[] public arrayFunds;
    uint public totalFunds;

Next, we use a constructor to populate the “arrayFunds” variable:

   constructor() {
        arrayFunds = [1,2,3,4,5,6,7,8,9,10,11,12,13];
    }

As you can see above, the “arrayFunds” variable is an array with a set of numbers. Moreover, then we have the “optionA” function:

    function optionA() external {
        for (uint i =0; i < arrayFunds.length; i++){
            totalFunds = totalFunds + arrayFunds[i];
        }
    }

The above function will be our main focus. In its current form, it’s spending a lot on fees, which means it requires gas optimizations in Solidity. This is where we will be applying different tweaks and, in turn, implementing gas optimizations. Moreover, you can see that our example function is pretty simple. It takes the value of the addition of all the elements of the above array, and it is also populating the other state variable, “totalFunds”, with that addition.

Also, you should note that there are many optimizing examples available online. However, most of the tweaks online cover the optimizer in the compiler, which eventually is usually taken care of. Though the above function (posted by Chanlink’s team) and its tweaks that follow focus on best practices. The latter must be appropriately implemented manually. As such, you should really keep this example at the back of your mind as you create smart contracts. 

Basic Gas Optimizations in Solidity 

Before we apply the first stage of optimizations, let’s look at why the above function results in exorbitant gas fees. The primary reason for this is the fact that the “optionA” function is reading and writing directly to the blockchain in each iteration of the loop. 

Moreover, “opcodes” (the machine language) in charge of taking action for the Ethereum blockchain or other EVM-compatible blockchains can be quite expensive. As such, we want to avoid performing it as much as possible. Hence, we can optimize the above function by caching our variable to a memory variable (“_totalFunds”). Then, use the memory variable inside the loop:

    function optionB() external {
        uint _totalFunds;
        for (uint i =0; i < arrayFunds.length; i++){
            _totalFunds = _totalFunds + arrayFunds[i];
        }
        totalFunds = _totalFunds;
    }

Just by this simple tweak, we can save a lot on fees during the execution of our “for” loop. However, even though we are not writing to the blockchain in the loop, we are still reading from the blockchain for each iteration. Thus, this is a clear indication that we can take gas optimizations in Solidity even further. This takes us to the “optionC()” function:

   function optionC() external {
        uint _totalFunds;
        uint[] memory _arrayFunds = arrayFunds;
        for (uint i =0; i < _arrayFunds.length; i++){
            _totalFunds = _totalFunds + _arrayFunds[i];
        }
        totalFunds = _totalFunds;
    }

By adding another memory variable, we are now caching both of our state variables (“arrayFunds” and “totalFunds”) to memory variables (“_arrayFunds” and “_totalFunds”). With the above memory array, we are also not reading from the blockchain for each of the loop’s iterations. As such, we only read from the blockchain once before initializing the loop. Then, we execute our function with the copy of the array, which is in memory. Finally, we just populate our variable as we did in “optionB”.

Advanced Gas Optimizations in Solidity

In the above examples, we went from “optionA” to “optionC” via “optionB”. As a result, we obtained a function that is properly optimized for lower gas fees. By taking both the reading and the writing within the “for” loop iterations off the chain, we made quite a difference. However, we wanted to take things even further. Hence, we came up with one of the esoteric gas optimizations in Solidity. We will present this nifty trick to you below.

Utilizing the SafeMath Library 

The optimization that we will implement is related to the SafeMath library, which used to be quite popular. The purpose behind that library came due to a particular past flaw in Solidity. In the past, Solidity didn’t revert to variable overflow. Keep in mind that in Solidity, every variable defined as “uint” or “integer” has a certain amount of values that it can hold. Whenever you tried to store more or store a figure that was greater than that upper limit in Solidity’s previous version, it didn’t return an error. Instead, it gave an incorrect value. Because of this, SafeMath was developed to solve that issue. However, starting from the Solidity version 0.8, this flaw was fixed. As such, Solidity was able to revert on overflows, which also eliminated the need for SafeMath. Though, this made the arithmetic more expensive in terms of gas.

So, let’s now focus our attention on “i++” used in all of our function variations presented so far. In this kind of addition of the variable “i”, we use the protected arithmetic of Solidity, also known as “checked arithmetic”. As far as the gas fees go, it would be cheaper to use unchecked arithmetic. Fortunately, we can do this confidently because it would be quite difficult for our variable “i” to overflow. The latter is the “uint256” variable, which has a pretty high limit. Moreover, we know that no array will be as long as that limit.     

Using the “Unchecked Arithmetic” Trick 

As part of this advanced gas optimization in Solidity, we’ll add a helper function. The latter will help us use the unchecked arithmetic trick:

    function unsafe_inc(uint x) private pure returns (uint) {
        unchecked { return x + 1; }
    }

As far as the execution goes, we will create a new function, called “optionD”:

  function optionD() external {
        uint _totalFunds;
        uint[] memory _arrayFunds = arrayFunds;
        for (uint i =0; i < _arrayFunds.length; i = unsafe_inc(i)){
            _totalFunds = _totalFunds + _arrayFunds[i];
        }
        totalFunds = _totalFunds;
    }

By looking at the above function, you can see that it follows “optionC” to the point except for the “i++” part. This is where we used the above-defined “unsafe_inc” helper function instead. That way, we do the same things – increase “i” by one but using the unchecked arithmetic trick. 

Gas Optimizations in Solidity – Results

Let’s now check the gas fees for each of the function options covered above. For that purpose, we will use Remix and the “Injected Web3” environment to deploy our smart contract on BNB Chain:

Next, we get to execute each of the four functions and compare the gas fees. In case you forgot, these are the functions included in the “gas_optimization.sol” smart contract:

  • optionA – Includes on-chain reading and writing throughout all of the loop’s iterations.
  • optionB – Includes on-chain reading throughout all of the loop’s iterations but off-chain writing.
  • optionC – Includes off-chain reading and writing throughout all of the loop’s iterations.
  • optionD – Includes off-chain reading and writing throughout all of the loop’s iterations in combination with the unchecked arithmetic.

Moreover, if you are using Remix to execute the same steps as we do, use the image below to assist you. That way, you’ll be able to execute the functions one by one. So, just expand the details below “Deployed Contracts”:

Finally, here are the results:

Looking at the image above, you can see that each tweak applied to the function reduced the gas fee. Also, notice that the basic gas optimizations in Solidity made quite an impact. On the other hand, the advanced gas optimization may have resulted in a smaller gas reduction. However, it could make a noticeable difference over time. 

Top Tips of Gas Optimizations in Solidity – Video Explanation

We hope that this presentation impressed you enough to memorize the following important guideline: 

The most significant gas optimizations in Solidity can be done by avoiding on-chain writing and reading within loops. 

In addition, if you prefer video tutorials, below is also a video version of the above presentation. To watch the basic gas optimizations in Solidity, start at 01:23. Moreover, for details on our advanced optimization, jump over to 04:20. Nonetheless, to see the results, hop over to 08:07. 

Gas Optimizations in Solidity – Top Tips – Summary

Being mindful of potential gas optimizations in Solidity pays off. As such, make sure to go over your code before deploying your smart contracts. Ask yourself, “are all of my loops writing and reading off-chain?”. If your answer is “no”, use the guidelines provided herein to apply the necessary tweaks. However, in case you are new to the blockchain world, make sure to get the basics under your belt first. 

Hence, you can continue your free crypto education with the help of the Moralis YouTube channel and the Moralis blog. There you’ll find a ton of articles and example projects that will help you progress fast. For instance, some of the latest topics show you how to build a 2D Web3 game, how to create a GameFi game, how to get into Web3 in 2022, how to use Web3 Firebase authentication, how to do blockchain game transactions, how to create a Web3 music platform, how to do gasless metaverse interactions, and much more. 

On the other hand, you may be eager to become a Web3 developer sooner rather than later. If so, you ought to consider enrolling in Moralis Academy. By doing so, you will get access to high-quality blockchain development courses. Among them is the Ethereum Smart Contract Programming 101 course, which will help you understand Solidity much better. However, an even deeper value lies in the community and expert mentorship that awaits you on the other side.    

March 22, 2022
Become a Moralis Mage
Get the latest information about Moralis Web3 in your email
Search
Generic filters
Related Articles
Become a Web3 Expert
Subscribe to our newsletter and get our E-book Web3 Speed run for FREE