Contents

Gas Fee Comparison Among Difference Keyword

Abstract

Gas optimization is extremely important to minimize the cost of deployment and gas fees for the end user. This article demonstrates a series of testings to compare the gas fee of varying keyword.

The unit testing code released at GithubGithub.

Evaluation Setting

The hardhat gas reporter plugin is used in this research for gas estimation.

1
npm i hardhat-gas-reporter --save-dev

The configuration of hardhat-gas-reporter is listed as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
require("@nomicfoundation/hardhat-toolbox");
require("hardhat-gas-reporter");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.18",
  gasReporter: {
    enabled: true,
    outputFile: "GasReporter.txt",
    noColors: true,
    token: "ETH"
  },
};

Option: Estimate the gas fee in USD

Here is way to transfer gas fee to USD currency in realtime, we get the currency information from coinmarketcap.

Add dotenv to your environment.

1
npm i dotenv --save-dev

Add dotenv and hardhat-gas-reporter to hardhat.config.js.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
require("@nomicfoundation/hardhat-toolbox");
require('dotenv').config()
require("hardhat-gas-reporter");

const COINMACKETCAP_API_KEY = process.env.COINMACKETCAP_API_KEY

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.18",
  gasReporter: {
    enabled: true,
    outputFile: "GasReporter.txt",
    noColors: true,
    currency: "USD", 
    coinmarketcap: COINMACKETCAP_API_KEY,
    token: "ETH"
  },
};

Create a .env file in root folder, add following string.

The {Your CoinMacketCap API KEY} is

1
COINMACKETCAP_API_KEY = {Your CoinMacketCap API KEY}

The {Your CoinMacketCap API KEY} can be obtained by registering an account at https://pro.coinmarketcap.com/ .

Constant, Immutable, Variable comparison

We defind three value using Constant, Immutable, Variable keyword.

For each keyword value, addVarC(), addVarI(), and addVarV() are used to read the corresponding keyword value and assign it to the a variable.

We list code as follow.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Constant {
    uint256 public constant varC = 1000;
    uint public immutable varI = 1000;
    uint public varV = 1000;

    constructor() {}

    function addVarC() external{
        varV = varC;
    }

    function addVarI() external {
        varV = varI;
    }

    function addVarV() external {
        varV = varV;
    }
}

In solidity, the MethodID order of the functions will effect the gas fee. The later the sort will consume more. Each position will have an extra 22 gas.

Here is the MethodID order of the functions in this example:

1
2
3
addVarC(): 0x75abe8c5
addVarI(): 0x81685e40
AddVarV(): 0xdb49f15c

Therefore, we run the unit testing, the result of gas fee are list following:

MethodGas FeeNet Gas FeeSave(Compare to varibale)
addVarC234002340066
addVarI434222340066
AddVarV23510234660

Due to the limitation of hardhat, I can’t achieve the cost of single read.

Here are the pure reading result in remix ide:

KeyworkNet Gas FeeSave(Compare to varibale)
constant1612100 (≈93%)
immutable1612100 (≈93%)
variable22610

Conclusion

  • In practical, The variable definitions should be avoided as much as possible;
  • For constants that do not need to be modified, it is recommended to use const to define them, which is the best in terms of functionality and gas.

Calldata, Memory comparison

This section compares Calldata and Memory keywords.

We write same data to variables modified in calldata and memory respectively.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract CalldataAndMemory {
    struct Person {
        uint16 age;
        string name;
        string wish;
    }

    Person role_1;
    Person role_2;

    function writeByCalldata(Person calldata role_1_) external {
        role_1 = role_1_;
    }

    function writeByMemory(Person memory role_2_) external {
        role_2 = role_2_;
    }
}

Here is the result of gas fee:

Gas fee comparision of calldata and memory.

MethodGas FeeNet Gas FeeSave(Compare to writeByCalldata)
writeByCalldata91372913720
writeByMemory9216492142770 (≈0.8%)

Conclusion

  • It is recommended to use calldata for variable writing in preference.