{"id":18952459,"url":"https://github.com/devanshbatham/solidity-gas-optimization-tips","last_synced_at":"2025-04-16T01:33:59.961Z","repository":{"id":38399859,"uuid":"485263050","full_name":"devanshbatham/Solidity-Gas-Optimization-Tips","owner":"devanshbatham","description":"Solidity Gas Optimization Tips","archived":true,"fork":false,"pushed_at":"2023-03-17T07:46:37.000Z","size":21,"stargazers_count":88,"open_issues_count":1,"forks_count":15,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-21T03:31:26.462Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devanshbatham.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-04-25T07:18:23.000Z","updated_at":"2024-12-03T11:08:14.000Z","dependencies_parsed_at":"2022-08-18T07:10:24.067Z","dependency_job_id":null,"html_url":"https://github.com/devanshbatham/Solidity-Gas-Optimization-Tips","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devanshbatham%2FSolidity-Gas-Optimization-Tips","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devanshbatham%2FSolidity-Gas-Optimization-Tips/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devanshbatham%2FSolidity-Gas-Optimization-Tips/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devanshbatham%2FSolidity-Gas-Optimization-Tips/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devanshbatham","download_url":"https://codeload.github.com/devanshbatham/Solidity-Gas-Optimization-Tips/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249182565,"owners_count":21226090,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T13:33:32.322Z","updated_at":"2025-04-16T01:33:59.712Z","avatar_url":"https://github.com/devanshbatham.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Solidity Gas Optimization Tips\nSolidity Gas Optimization Tips taken from various sources (sources listed in the #credits section below). Make pull requests to contribute gas alpha. \n\n\n# Gas Optimizations in Solidity\n\n## 1- Use of Bit shift operators\n\nCheck if arithmethic operations can be achieved using bitwise operators, if yes, implement same operations using bitwise operators and compare the gas consumed. Usually bitwise logic will be cheaper (ofc we cannot use bitwise everywhere, there some limitations)\n\n**Note:** Bit shift `\u003c\u003c` and `\u003e\u003e` operators are not among the arithmetic ones, and thus don’t revert on overflow.\n\n**Code example:** \n\n\n```diff\n-    uint256 alpha = someVar / 256;\n-    uint256 beta = someVar % 256;\n+    uint256 alpha = someVar \u003e\u003e 8;\n+    uint256 beta = someVar \u0026 0xff;\n\n\n-    uint 256 alpha = delta / 2;\n-    uint 256 beta = delta / 4; \n-    uint 256 gamma = delta * 8;\n+    uint 256 alpha = delta \u003e\u003e 1;\n+    uint 256 beta = delta \u003e\u003e 2; \n+    uint 256 gamma = delta \u003c\u003c 3;\n\n```\n\n## 2- Public vs External (External is cheaper)\n\nThe `public` visiblilty modifier is equivalent to `external` plus `internal`. In other words, both `public` and `external` can be called from outside your contract (like MetaMask), but of these two, only `public` can be called from other functions inside your contract.\n\nBecause `public` grants more access than `external` (and is costlier than the latter), the general best practice is to prefer `external`. Then you can consider switching to `public` if you fully understand the security and design implications.\n\n**Code Example:**\n\n```solidity\npragma solidity 0.8.10;\n\ncontract Test {\n \n    string message = \"Hello World\";\n    \n    // Execution cost: 24527\n    function test() public view returns (string memory){\n         return message;\n    }\n\n    //Execution cost: 24505\n    function test2() external view returns  (string memory){\n         return message;\n    }\n}\n```\n\n## 3- There is no need to initialize variables with default values\n\nIf the variable is not set/initialized, it is assumed to have a default value (0, false, 0x0, etc., depending on the data type). If you explicitly initialize it with its default value, you are just wasting gas.\n\n\n```solidity\nuint256 foo = 0;//bad, expensive\nuint256 bar;//good, cheap\n```\n\n\n## 4- Use short strings in revert, require checks\n\nIt is considered a best practice to append the error reason string with `require` statement. But these strings take up space in the deployed bytecode. Each reason string needs at least 32 bytes, so make sure your string complies with 32 bytes, otherwise it will become more expensive.\n\n\n**Code Example:**\n\n\n```solidity\nrequire(balance \u003e= amount, \"Insufficient balance\");//good\nrequire(balance \u003e= amount, \"Too bad, it appears, ser you are broke, bye bye\"; //bad\n```\n\n## 5- Avoid redundant checks\nWe should avoid unnecessary/redundant checks to save some extra gas. \n\n**Code Example:**\n\n```solidity\nBad Code:\nrequire(balance\u003e0, \"Insufficient balance\");\nif (balance\u003e0){ // this check is redundant\n    . . . //some action\n}\n\nGood Code: \nrequire(balance\u003e0, \"Insufficient balance\");\n. . . //some action\n```\n\n\n\n## 6- Use nested if and, avoid multiple check combinations\n\nUsing nested is cheaper than using \u0026\u0026 multiple check combinations. There are more advantages, such as easier to read code and better coverage reports. \n\n\n\n**Code Example:**\n\n\n\n\n```solidity\npragma solidity 0.8.10;\n\ncontract NestedIfTest {\n\n    //Execution cost: 22334 gas\n  function funcBad(uint256 input) public pure returns (string memory) { \n       if (input\u003c10 \u0026\u0026 input\u003e0 \u0026\u0026 input!=6){ \n           return \"If condition passed\";\n       } \n\n   }\n\n    //Execution cost: 22294 gas\n    function funcGood(uint256 input) public pure returns (string memory) { \n    if (input\u003c10) { \n        if (input\u003e0){\n            if (input!=6){\n                return \"If condition passed\";\n            }\n        }\n    }\n}\n}\n```\n\n## 7- Use of Multiple require(s)\nUsing mutiple require statements is cheaper than using `\u0026\u0026` multiple check combinations. There are more advantages, such as easier to read code and better coverage reports. \n\n\n\n```solidity\npragma solidity 0.8.10;\n\ncontract MultipleRequire {\n\n    // Execution cost: 21723 gas\n  function bad(uint a) public pure returns(uint256) {\n        require(a\u003e5 \u0026\u0026 a\u003e10 \u0026\u0026 a\u003e15 \u0026\u0026 a\u003e20);\n        return a;\n    }\n\n    // Execution cost: 21677 gas\n    function good(uint a) public pure returns(uint256) {\n        require(a\u003e5);\n        require(a\u003e10);\n        require(a\u003e15);\n        require(a\u003e20);\n        return a;\n    }\n}\n```\n\n## 8- Internal functions are cheaper to call\nSet approriate function visibillities, as `internal` functions are cheaper to call than `public` functions.There is no need to mark a function as `public` if it is only meant to be called internally.\n\n```diff\n//Following function will only be called interally\n- function _func() public {...}\n+ function _func() internal {...}\n```\n\n## 9- Use libraries to save some bytecode\n\nWhen you call a `public` function of the library, the bytecode of the function will not become part of your contract, so you can put complex logic in the library while keeping the contract scale small. \n\n\nThe call to the library is made through a delegate call, which means that the library can access the same data and the same permissions as the contract. This means that it is not worth doing for simple tasks.\n\n## 10- Replace state variable reads and writes within loops with local variable reads and writes.\n\n\nReading and writing local variables is cheap, whereas reading and writing state variables that are stored in contract storage is expensive.\n\n```solidity\nfunction badCode() external {                \n  for(uint256 i; i \u003c myArray.length; i++) { // state reads\n    myCounter++; // state reads and writes\n  }        \n}\n\n\nfunction goodCode() external {\n    uint256 length = myArray.length; // one state read\n    uint256 local_mycounter = myCounter; // one state read\n    for(uint256 i; i \u003c length; i++) { // local reads\n        local_mycounter++; // local reads and writes  \n    }\n    myCounter = local_mycounter; // one state write\n}\n```\n\n\n## 11- Packing Structs\n\nA common gas optimization is “packing structs” or “packing storage slots”. This is the action of using smaller types like uint128 and uint96 next to each other in contract storage. When values are read or written in contract storage a full 256 bits are read or written. So if you can pack multiple variables within one 256 bit storage slot then you are cutting the cost to read or write those storage variables in half or more.\n\n\n```solidity\n// Unoptimized\nstruct MyStruct {\n  uint256 myTime;\n  address myAddress;\n}\n\n//Optimized\nstruct MyStruct {\n  uint96 myTime;\n  address myAddress;\n}\n```\n\nIn the above a `myTime` and `myAddress` state variables take up `256` bits so both values can be read or written in a single state read or write.\n\n## 12- Solidity Gas Optimizer\nMake sure Solidity’s optimizer is enabled. It reduces gas costs. If you want to gas optimize for contract deployment (costs less to deploy a contract) then set the Solidity optimizer at a low number. If you want to optimize for run-time gas costs (when functions are called on a contract) then set the optimizer to a high number.\n\n## 13- Save on data types\nIt is better to use `uint256` and `bytes32` than using uint8 for example. While it seems like `uint8` will consume less gas than uint256 it is not true, since the Ethereum virtual Machine(EVM) will still occupy `256` bits, fill 8 bits with the uint variable and fill the extra bites with zeros. \n\n\n**Code Example:**\n```solidity\npragma solidity ^0.8.1;\n\ncontract SaveGas {\n    \n    uint8 resulta = 0;\n    uint resultb = 0;\n    \n    // Execution cost: 73446 gas\n    function UseUint() external returns (uint) {\n        uint selectedRange = 50;\n        for (uint i=0; i \u003c selectedRange; i++) {\n            resultb += 1;\n        }\n        return resultb;\n    }\n    \n    // Execution cost: 84175 gas \n    function UseUInt8() external returns (uint8){\n        uint8 selectedRange = 50;\n        for (uint8 i=0; i \u003c selectedRange; i++) {\n            resulta += 1;\n        }\n        return resulta;\n    }\n}\n```\n\n## 14- Short circuiting\nShort-circuiting is a strategy we can make use of when an operation makes use of either ``||`` or ``\u0026\u0026``. This pattern works by ordering the lower-cost operation first so that the higher-cost operation may be skipped (short-circuited) if the first operation evaluates to true.\n\n\n```solidity\n// f(x) is low cost\n// g(y) is expensive\n\n// Ordering should go as follows\nf(x) || g(y)\nf(x) \u0026\u0026 g(y)\n```\n\n## 15- Avoiding/Removing unnecessary libraries\nLibraries are often only imported for a small number of uses, meaning that they can contain a significant amount of code that is redundant to your contract. If you can safely and effectively implement the functionality imported from a library within your contract, it is optimal to do so.\n\n\n## 16- Make fewer external calls\nExternal calls are expensive, therefore, fewer external gas == fewer gas cost.\n\n\n## 17- `unchecked { ++i;}` is cheaper than `i++;` \u0026 `i=i+1;`\n\n\n**Code Example:**\n\n```solidity\n\n// Unoptimized Code\n\nfor(uint i=0; i\u003cn; i++){\n    . . . //some stuff here\n}\n\n//Optimized Code\n\nfor(uint i; i\u003cn;){\n    . . . //some stuff here\n    unchecked { ++i; }\n}\n```\n\n## 18- Avoid repeated computations\nArithmetic computations cost gas, it is recommended to avoid repeated computations. \n\n**Code Example:**\n\n```solidity\n\n// Unoptimized code: \nfor (uint i=0;i\u003clength;i++) {\ntokens[i] += limit * price;\n}\n\n\n// Optimized code:\nuint local = limit * price;\nfor (uint i=0;i\u003clength;i++) {\ntokens[i] += local;\n}\n```\n\n## 19- Avoid dead code\nThere is no point of leaving dead lines in code. Those lines are never going to execute, and but they will take place in bytecode, it is better to get rid of em.\n\n\n**Code Example:**\n\n```solidity\nfunction deadCode(uint x) public pure {\n  if(x \u003c1) {\n    if(x\u003e 2) {\n      return x;\n    }\n  }\n}\n```\n\n## 20- Delete your variables to get a gas refund.\n\nSince there's no garbage collection, you have to throw away unused data yourself. \n\n\n**Code Example:**\n\n```solidity\n\n//Using delete keyword\ndelete myVariable;\n\n//Or assigning the value 0 if integer\nmyInt = 0;\n```\n\n## 21- Single line swaps\nOne line to swap two variables without writing a function or temporary variable that needs more gas.\n\n\n**Code Example:**\n\n```solidity\n(hello, world) = (world, hello);\n```\n\n## 22- Store data on memory not storage.\n\nChoosing the perfect Data location is essential. You must know these:\n\n- **storage** - variable is stored on the blockchain. It's a persistent state variable. Costs Gas to define it and change it.\n\n- **memory** - temporary variable declared inside a function. No gas for declaring. But costs gas for changing memory variable (less than storage)\n\n- **calldata** - like memory but non-modifiable and only available as an argument of external functions\n\nAlso, it is important to note that, If not specified data location, then it storage by default.\n\n## 23- Use `calldata` instead of `memory` for function parameters\n\n\n```solidity\ncontract C {\n    function add(uint[] memory arr) external returns (uint sum) {\n        uint length = arr.length;\n        for (uint i = 0; i \u003c arr.length; i++) {\n            sum += arr[i];\n        }\n    }\n}\n```\n\nIn the above example, the dynamic array `arr` has the storage location `memory`. When the function gets called externally, the array values are kept in `calldata` and copied to memory during ABI decoding (using the opcode `calldataload` and `mstore`). And during the for loop, `arr[i]` accesses the value in memory using a `mload`. However, for the above example this is inefficient. Consider the following snippet instead:\n\n\n```solidity\ncontract C {\n    function add(uint[] calldata arr) external returns (uint sum) {\n        uint length = arr.length;\n        for (uint i = 0; i \u003c arr.length; i++) {\n            sum += arr[i];\n        }\n    }\n}\n```\n\n## 24- Some state variables can be set to immutable\n\n```solidity\n\n// Unoptimized code: \ncontract C {\n    /// The owner is set during contruction time, and never changed afterwards.\n    address public owner = msg.sender;\n}\n\n// Optimized code: \n\n\ncontract C {\n    /// The owner is set during contruction time, and never changed afterwards.\n    address public immutable owner = msg.sender;\n}\n```\n\n## 25- Declaring constructor as payable\n\nYou can cut out 10 opcodes in the creation-time EVM bytecode if you declare a constructor payable. The following opcodes are cut out:\n\n- `CALLVALUE`\n- `DUP1`\n- `ISZERO`\n- `PUSH2`\n- `JUMPI`\n- `PUSH1`\n- `DUP1`\n- `REVERT`\n- `JUMPDEST`\n- `POP`\n\nIn Solidity, this chunk of assembly would mean the following:\n\n\n```solidity\nif(msg.value != 0) revert();\n```\n\n## 26- Upgrade to at least `0.8.4`\n\nUsing newer compiler versions and the optimizer gives gas optimizations and additional safety checks for free.\n\n## 27- Use `!= 0` instead of `\u003e 0` for unsigned integer comparison\n\nWhen dealing with unsigned integer types, comparisons with `!= 0` are cheaper then with ``\u003e 0``.\n\n## 28- Limit Modifiers\n\nThe code of modifiers is inlined inside the modified function, thus adding up size and costing gas. Limit the modifiers. Internal functions are not inlined, but called as separate functions. They are slightly more expensive at run time, but save a lot of redundant bytecode in deployment, if used more than once.\n\n\n## 29- Packing booleans\n\nIn Solidity, Boolean variables are stored as uint8 (unsigned integer of 8 bits). However, only 1 bit would be enough to store them. If you need up to 32 Booleans together, you can just follow the Packing Variables pattern. If you need more, you will use more slots than actually needed.\n\nPack Booleans in a single uint256 variable. To this purpose, create functions that pack and unpack the Booleans into and from a single variable. The cost of running these functions is cheaper than the cost of extra Storage.\n\n## 30- Mappings are cheaper than arrays\n\nSolidity provides only two data types to represents list of data: arrays and maps. Mappings are cheaper, while arrays are packable and iterable.\n\nIn order to save gas, it is recommended to use mappings to manage lists of data, unless there is a need to iterate or it is possible to pack data types. This is useful both for Storage and Memory. You can manage an ordered list with a mapping using an integer index as a key.\n\n\n## 31- Fixed size variables are cheaper than variable size.\n\nWhenever it is possible to set an upper bound on the size of an array, use a fixed size array instead of a dynamic one.\n\n## 32- Use Custom errors instead of revert strings\n\nCustom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)\n\n**Code Example:**\n\n```solidity\n\n// Revert Strings \ncontract C {\n    address payable owner;\n\n    function withdraw() public {\n        require(msg.sender == owner, \"Unauthorized\");\n\n        //..\n    }\n}\n\n\n//Custom errors\nerror Unauthorized();\n\ncontract C {\n    address payable owner;\n\n    function withdraw() public {\n        if (msg.sender != owner)\n            revert Unauthorized();\n\n        //..\n    }\n}\n```\n\n## Credits\n\n- https://eip2535diamonds.substack.com/p/smart-contract-gas-optimization-with\n- https://blog.birost.com/a?ID=00950-e4e25dc9-573d-4332-8262-41131961734f\n- https://marduc812.com/2021/04/08/how-to-save-gas-in-your-ethereum-smart-contracts/\n- https://betterprogramming.pub/how-to-write-smart-contracts-that-optimize-gas-spent-on-ethereum-30b5e9c5db85\n- https://mudit.blog/solidity-gas-optimization-tips/\n- http://www.cs.toronto.edu/~fanl/papers/gas-brain21.pdf\n- https://gist.github.com/hrkrshnn/ee8fabd532058307229d65dcd5836ddc\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevanshbatham%2Fsolidity-gas-optimization-tips","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevanshbatham%2Fsolidity-gas-optimization-tips","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevanshbatham%2Fsolidity-gas-optimization-tips/lists"}