James Bachini

3 Tips For Gas Efficient Solidity Smart Contracts

Solidity Gas Saving

Here are 3 tips which could be considered the low hanging fruit of gas efficient solidity smart contracts.

  1. Use Correct Modifiers & Declarations
  2. Plan & Pack State Storage Variables
  3. Optimise For Runtime Cost
James On YouTube

Use Correct Modifiers & Declarations

This is the simplest way to get some small wins with gas optimisation. Go through your contract and define anything that shouldn’t change as constant (or immutable if it is set after declaration).

uint constant x = 42069;

Next we can check the function parameters are set correctly. Anything that doesn’t modify state should be set as view, or pure if it doesn’t read state either.

function loop(uint _x) external pure returns (uint) {
  return _x * 2;
}

When consuming input data such as arrays and strings we need to set the data storage to somewhere. Check to see if changing memory storage to calldata will save gas if you aren’t manipulating that input data.

function doSomething(uint[] calldata _arr) external {
  // calldata is read only
}

Plan & Pack State Storage Variables

Persistent state storage is the most gas expensive part of our contracts. We pay a significant amount to take up the valuable storage capacity of the Ethereum virtual machine. This is where we can save the most through careful planning and packing of our state variables.

Ethereum has a standard storage slot of 32 bytes (256 bits). This is why we use uint256 so widely, because it’s a massive number that fits conveniently into a single storage slot.

Solidity packing variables for gas optimisation

If we don’t need a massive whole number we can actually break that storage slot down and pack multiple values in there. We want to place smaller sized variables next to each other to ensure they are packed efficiently.

uint128 x = 1;
uint128 y = 2;

We can also pack variables efficiently into structs, such as this one below which still only takes up a single slot.

struct User {
  bool verified;
  bool confirmed;
  uint8 age;
  uint32 birthYear;
}

Note that variable types such as uint8 have a maximum value (0-255) due to the limitations of the reduced data storage size.


Optimise For Runtime Cost

When we compile a contract we make compromises based on if we want to optimise for deployment costs or runtime costs. It costs significant amounts to store a smart contract on-chain which is paid for during the deployment transaction. On Ethereum, during peak periods, this can stretch into thousands of dollars. However for contracts where we expect continued and frequent usage it is efficient to optimise for runtime costs which means you will pay more for deployment but users will pay less for transactions.

One simple way to do this is to change the settings in your compiler. For example if you are using Hardhat you can adjust them directly in the hardhat.config.js file.

module.exports = {
  solidity: {
    version: "0.8.9",
    settings: {
      optimizer: {
        enabled: true,
        runs: 1000,
      },
    },
  },
};

In foundry we would set it in the foundry.toml config file:

optimizer = true
optimizer_runs = 1000

And of course in Remix you can do this too by going into the compile tab and editing the advanced configurations settings.

Gas optimisation in Remix

Set the runs to a rough estimate for how many transactions the contract will receive over it’s life time. This doesn’t need to be anywhere near exact but will help inform the solidity compiler as to what you want to optimise for. Setting the runs value to a higher amount will push it towards optimising for runtime costs over deployment costs making tx fees lower for users.



Get The Blockchain Sector Newsletter, binge the YouTube channel and connect with me on Twitter

The Blockchain Sector newsletter goes out a few times a month when there is breaking news or interesting developments to discuss. All the content I produce is free, if you’d like to help please share this content on social media.

Thank you.

James Bachini

Disclaimer: Not a financial advisor, not financial advice. The content I create is to document my journey and for educational and entertainment purposes only. It is not under any circumstances investment advice. I am not an investment or trading professional and am learning myself while still making plenty of mistakes along the way. Any code published is experimental and not production ready to be used for financial transactions. Do your own research and do not play with funds you do not want to lose.