CoinsBench

Where blockchain developers share their stories, experiences & ideas.

Follow publication

Comprehensive Guide: Tips and Tricks for Gas Optimization in Solidity

--

Comprehensive Guide: Tips and Tricks for Gas Optimization in Solidity
Comprehensive Guide: Tips and Tricks for Gas Optimization in Solidity

Gas is a crucial component of the Ethereum blockchain platform, as it determines the cost of executing transactions and contracts. The more complex and computationally intensive the transaction or contract, the more gas is needed. Gas optimization is essential for Solidity contracts as by minimizing gas consumption, transaction costs can be reduced, and transactions can fit within the block gas limit, thereby preventing failures and wasted gas.

Now, you may be wondering, with the advent of Layer 2 solutions, why bother with gas optimization at all?

While Layer 2 networks have certainly revolutionized the way we think about scalability and gas costs, the fact remains that these Layer 2 solutions like Polygon, Arbitrum, etc. operate on top of Ethereum’s basic layer and still come with a cost. Furthermore, while some Layer 2 chains may have lower gas fees due to lower adoption rates, it’s important to remember that the principle of supply and demand still applies. There is also a notion of block limit which is the maximum amount of gas that can be consumed by all transactions included in a single block in the Ethereum blockchain. The block limit is a dynamic value that Ethereum miners set but the average is 30 million units of gas. So, whether you’re a Solidity developer or an Ethereum user, understanding gas optimization is essential to navigating the network’s complexities and reducing transaction costs.

Before we dive into the world of gas optimization, it is worth exploring the latest EIP-1559 standard for analyzing gas costs. We highly recommend checking out the following article for an in-depth analysis of opcode calculation and Ethereum transaction fees:

If you are looking to reduce the size of your contract and not the transaction cost you can follow the below article:

1) Optimizer

Solidity Optimizer is a tool that is used to optimize Solidity code which reduces the gas cost by removing unnecessary operations, reducing the number of instructions, and simplifying the remaining code. This is done by performing a series of transformations on the compiled bytecode of the contract. If optimization is enabled by default it is set to 200 runs.

However, it’s important to note that “runs” is not a fixed number of iterations of the optimizer but a tradeoff parameter between runtime and deploy time gas requirements. The number of runs impacts the amount of gas required to deploy the contract and the amount of gas required to execute its functions. A higher number of runs can potentially reduce the gas cost of executing the contract but can also increase the gas cost of deploying it. There is no optimal value for the number of runs parameter because it depends on the specific use case of the contract. A contract with a high number of function calls or complex logic may benefit from a higher number of runs, while a simpler contract may not require as many runs.

Additionally, it’s important to note that Etherscan API for contract verification has a limit on the maximum value for the number of runs parameter. The limit is currently set to 1000000 runs, which means that contracts with a higher number of runs cannot be verified using the Etherscan API.


// SPDX-License-Identifier: MIT
pragma solidity >=0.8.16;

/*
* NO OPTIMIZATION: CDC = 74395, FCC = 21162
* OPTIMATION ENABLED
* RUNS SET TO 0: CDC = 72661, FCC = 21138
* RUNS SET TO 200: CDC = 72661, FCC = 21138
* RUNS SET TO 1000: CDC = 72661, FCC = 21138
* RUNS SET TO 100000: CDC = 72661, FCC = 21138
*/

contract OptimzationExample1 {
function transferEthers() public payable {}
}

/*
* NO OPTIMIZATION: CDC = 282513, FCC = 25680
* OPTIMATION ENABLED
* RUNS SET TO 0: CDC = 180298, FCC = 46143
* RUNS SET TO 200: CDC = 180298, FCC = 46143
* RUNS SET TO 1000: CDC = 188192, FCC = 24799
* RUNS SET TO 10000: CDC = 198760, FCC = 24799
* RUNS SET TO 100000: CDC = 198748, FCC = 24799
* RUNS SET TO 10000000: CDC = 198760, FCC = 24799
*/

contract OptimzationExample2 {
uint256 private _fixer;
uint256 private _lastCalculatedValue;

constructor(uint256 fixer) {
_fixer = fixer;
}

function getValue(uint256 num1, uint256 num2) public returns (uint256) {
uint256 fixer = _fixer;
require(num1 < num2, "num2 must be greater than num1");
require(
(num1 + num2) > fixer,
"sum of num1 and num2 must be greater than fixer"
);
uint256 sum = num1 + num2;
uint256 fixedSum = sum * fixer;
_lastCalculatedValue = fixedSum;
return fixedSum;
}
}

Here, CDC refers to the Contract Deployment Cost and FCC refers to the Function Calling Cost. As we can see, the optimization worked for small contracts with fewer runs and afterward, it became constant. For larger contracts, it took more runs after which it became constant. Additionally, we can observe that our CDC decreased with fewer runs, but as we increased the number of runs, the CDC increased while the FCC decreased.

2) Unchecked Blocks:

Before Solidity 0.8.0, arithmetic operations would always wrap in case of underflow or overflow leading to the widespread use of libraries that introduce additional checks. Since Solidity 0.8.0, all arithmetic operations revert on overflow and underflow by default, but this behavior also includes some checks that cause the gas.

An unchecked block was introduced in Solidity that bypasses certain safety checks performed by the Solidity compiler. It is typically used when the developer wants to optimize the gas usage of their smart contract. However, using unchecked blocks also carries risks and by bypassing certain safety checks, the developer is taking on additional responsibility to ensure the code is secure and not vulnerable to attacks. The following functions are executed at the optimization of 200 runs:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.16;

contract WithoutUnchecked {
uint256 private _value = 0;
// function execution cost = 22322 gas
function increment() public {
_value += 1;
}
}

contract WithUnchecked {
uint256 private _value = 0;
// function execution cost = 22225 gas
function increment() public {
unchecked {
_value += 1;
}
}
}

As you can see using the unchecked block saved us 22322–22225 = 97 gas but only use this unchecked block if you are sure that the value will not overflow or underflow.

3) Storage — Zero vs Non-Zero Costing:

In Solidity, storage refers to a persistent data storage area on the Ethereum blockchain. When storing a value in a variable the gas costing goes as follows:

  • If we set storage from 0 to a non-zero value, it will cost (G𝘴𝘴𝘦𝘵 + G𝘤𝘰𝘭𝘥𝘴𝘭𝘰𝘢𝘥) 22,100 units of gas.
  • If we set storage from non-zero to a non-zero value, it will cost (G𝘴𝘳𝘦𝘴𝘦𝘵 + G𝘤𝘰𝘭𝘥𝘴𝘭𝘰𝘢𝘥) 5,000 units of gas.
  • If we set storage from a non-zero value to 0 it will refund some gas. We will be discussing how much gas will be refunded below in detail using both legacy and the latest methods.

According to Ethereum Yellow Paper, the function and gas cost of the above operations are as follows:

Cost of different operations — Ethereum Yellow Paper
Cost of different operations — Ethereum Yellow Paper

The formula for the refund goes as follows:

The formula for a refund — Ethereum Yellow Paper
The formula for a refund — Ethereum Yellow Paper

(Legacy) — According to Ethereum Yellow Paper:

The following are the values using which the refund was calculated in Berlin Fork and as of now, Ethereum Yellow Paper is not updated. Now, in case of a refund please keep in mind that there are some technicalities here:

  • First, the refund gas is calculated according to the above formula which refunds the full 15,000 gas only if your transaction is above 30,000 gas else it will return 50% of the total transaction.
  • Secondly, changing the value also involves first reading it from the storage so the cost will be the difference between refund gas and (G𝘴𝘳𝘦𝘴𝘦𝘵 + G𝘤𝘰𝘭𝘥𝘴𝘭𝘰𝘢𝘥) 5,000 units of gas.

(Latest) — London Hard Fork (EIP-3529):

Following are the specifications the latest EIP-3529 standard provides:

EIP-3529 Specification
EIP-3529 Specification

Now according to this specification, the 15,000 gas we used to get in Berlin Fork is reduced to 4,800 gas and now we will get only 20% of the total transaction as a refund instead of 50% if the transaction costs a minimum of 24,000 gas.

So, the key takeaway here is rather than going incremental from 0 to n, go reverse i.e. from n to 0. If that does not suit you, start values from 1 instead of from 0 so that the function execution cost remains consistent.

4) Storage — Cold Access vs Warm Access:

Cold access typically refers to accessing data that is stored on the blockchain but is not currently in the cache or memory of the executing contract while warm access typically refers to accessing data that is already in the cache or memory of the executing contract. Warm Access is a faster process and as a result, it requires less gas than cold access which can be seen in the following image taken from Ethereum Yellow Paper:

Cold and Warm Access Cost — Ethereum Yellow Paper
Cold and Warm Access Cost — Ethereum Yellow Paper

We can save the gas by caching (warming) the value, and this makes a great difference if you are using a variable multiple times or looping through an array. Checkout the following that is compiled at the optimization of 200 runs:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.16;

contract WithoutCaching {
uint256[] private _arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// function execution cost = 25506 gas
function getVal() public view returns (uint256) {
uint256 sum;
for (uint256 i = 0; i < _arr.length; i++) {
sum += _arr[i];
}
return sum;
}
}

contract WithCaching {
uint256[] private _arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// function execution cost = 24228 gas
function getVal() public view returns (uint256) {
uint256 sum;
uint256[] memory arr = _arr; // caching the array
for (uint256 i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
}

So, From the above result, it is clear that we can save gas by caching the variable and here we saved about 25506–24228 = 1278 gas.

5) Function Names:

In solidity, the length of function names does not matter because these names are hashed using Keccak256, and the first four bytes of those hashes are used to reference these functions. On the contrary, the sequence generated after hashing matters, because solidity arranges these function name hashes in ascending order, so jumping from one function to another, causes an extra 22 gas. This can be viewed in the following code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract FunctionNames {
// Here functions are not sorted according to the hash:
function blue() external {}
function yellow() external {}
function red() external {}
function purple() external {}
function green() external {}
function orange() external {}
/*
* After sorting:
* "2930cf24": "red()", - 21186
* "529c46e9": "orange()", - 21208
* "be9faf13": "yellow()" - 21230
* "ed18f0a7": "blue()", - 21252
* "ed44cdd4": "purple()", - 2127
* "f2f1e132": "green()", - 21296
*/

}

As you can see red() and orange() have a difference of 21186–21208 = 22 and if we compare every function with its preceding function we will notice that each jump will cost 22 gas. So, the key takeaway from this is to name the functions so that the functions that consume more gas should be on the top of the list when converted into hashes.

6) Uint256 vs Other Datatypes:

In solidity either we store in boolean, uint128, uint256, or any other datatype, under the hood, it takes 32 bytes. So when we use a datatype other than uint256, the gas cost increases because either datatype requires more opcodes for offsetting purposes. This can be seen in the following code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract withUint256 {
uint256 a = 1;
// 2246
function getUint() public view returns (uint256) {
return a;
}
}

contract withUint128 {
uint128 a = 1;
// 2258
function getUint() public view returns (uint128) {
return a;
}
}

contract withBoolean {
bool a = true;
// 2258
function getBool() public view returns (bool) {
return a;
}
}

7) Variable Packing:

Contrary to the above where storing using other than uint256 costs more, when we use structs if the space is enough for the successive variable it packs into the previous one. For example, solidity uses 32 bytes slot so if we use an address that is 20 bytes, and a boolean which is 1 byte then it will be packed into the 32-byte slot and when we access or update them it will act as a single variable which saves us the cost of accessing the two variables. This can be seen in the following code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract withoutPacking {
struct MyStruct {
uint256 a; // 32 bytes
address b; // 20 bytes
uint256 c; // 32 bytes
}
MyStruct myStruct;
// Function Execution Cost = 87614
function updateStruct() public {
myStruct = MyStruct({a: 5, b: msg.sender, c: 6});
}
}

contract withPacking {
struct MyStruct {
uint256 a; // 32 bytes
address b; // 20 bytes
bool c; // 1 bytes
}
MyStruct myStruct;
// Function Execution Cost = 65547
function updateStruct() public {
myStruct = MyStruct({a: 5, b: msg.sender, c: true});
}
}

As you can see, we have considerably lower gas than the previous one and the gas saved by packing is 87614 — 65547= 22067, which is a very good saving. This drastic change in gas is due to the cold and warm access concept which is discussed above. Even if we pack more variables it will still cost low than the first one as you can see below:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract withPackingMore {
struct MyStruct {
uint256 a; // 32 bytes
address b; // 20 bytes
bool c; // 1 bytes
uint40 d; // 5 bytes
bool e; // 1 bytes
uint32 f; // 3 bytes
}
MyStruct myStruct;
// Function Execution Cost = 65628
function updateStruct() public {
myStruct = MyStruct({a: 5, b: msg.sender, c: true, d: 3, e: true, f: 23});
}
}

8) Array Length Caching:

We have already learned variable caching above which is very useful if a variable is being used multiple times or we are looping through a fixed-length array in a function. But when we use an array with dynamic length then while looping through the array compiler calls SLOAD every time to get the length of the array and by caching this length, we can save that gas.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract withoutLengthCaching {
uint256[] private _arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Function Execution Cost = 26627
function getVal() public view returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < _arr.length; i++) {
sum += _arr[i];
}
return sum;
}
}

contract withLengthCaching {
uint256[] private _arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Function Execution Cost = 26610
function getVal() public view returns (uint256) {
uint256[] memory arr = _arr;
uint256 length = arr.length;
uint256 sum = 0;
for (uint256 i = 0; i < length; i++) {
sum += arr[i];
}
return sum;
}
}

9) Calldata vs Memory:

In older versions of solidity, the arguments were calldata by default in external functions while arguments were memory by default in public functions dues to which external functions used to cost less. Now we can define these arguments explicitly.

Calldata costs less gas than the memory because calldata parameters are not modifiable that’s why we don’t need to create a new memory variable unnecessarily.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract withMemory {
// Function Execution Cost = 22011
function doNothing(bytes memory _bytes) external {}
}

contract withCalldata {
// Function Execution Cost = 21865
function doNothing(bytes calldata _bytes) external {}
}

As you can see calldata saved us 22011–21865 = 146 gas.

10) Memory Explosion:

Memory is very cheap to allocate as long as it is small but past a certain point i.e., 32 kilobytes of memory storage in a single transaction, the memory cost enters into a quadratic section and the formula for this calculation is given in Ethereum Yellow Paper as follows:

The formula for computing Array Gas Cost — Ethereum Yellow Paper
The formula for computing Array Gas Cost — Ethereum Yellow Paper

So, you can see the implication of creating a very large array in the following code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract smallArraySize {
// Function Execution Cost = 21,903
function checkArray() external {
uint256[100] memory myArr;
}
}

contract LargeArraySize {
// Function Execution Cost = 276,750
function checkArray() external {
uint256[10000] memory myArr;
}
}

contract VeryLargeArraySize {
// Function Execution Cost = 20,154,094
function checkArray() external {
uint256[100000] memory myArr;
}
}

So, if we subtract the contract execution cost i.e., 21,000 then the cost for each function becomes:

  1. 21,903–21,000 = 903 gas
  2. 276,750–21,000 = 255,750 gas
  3. 20,154,094–21,000 = 20,133,094 gas

Now, these calculations clearly show how gas cost increased quadratically with the size of the array.

11) Less/Greater Than vs Less/Greater Than or Equal To:

Solidity has an opcode for less than and greater than i.e. LT and GT respectively but it does not have an operator for less than or equal to and greater than or equal to. So, when we use ≤ or ≥ the compiler first executes LT/GT and then executes ISZERO which costs more gas.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract GreaterThan {
// Function Execution Cost = 21,451
function checkArray(uint256 x) external returns (bool) {
return x > 15;
}
}

contract GreaterThanOrEqualTo {
// Function Execution Cost = 21,454
function checkArray(uint256 x) external returns (bool) {
return x >= 15;
}
}

The gas difference is very minimal, but it still saves gas.

12) Revert Early:

We should revert early because reverting early saves the gas which is then returned to the caller. The following code is a good example of reverting as early as possible:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract RevertEarly {
uint256 private _x;
// In case of revert, Function Execution Cost = 21,527
function revertTest(uint256 x) external {
require(x < 5, "x must be less than 5");
_x = x;
}
}

contract RevertLate {
uint256 private _x;
// In case of revert, Function Execution Cost = 43,636
function revertTest(uint256 x) external {
_x = x;
require(x < 5, "x must be less than 5");
}
}

In the above code, we saved costs because we checked the condition beforehand and didn’t store the value.

13) Short-Circuiting (AND/OR):

Like other programming languages AND/OR operators are also available in Solidity so, when writing a `require` statement that includes two or more expressions, it’s advisable to place the expression that consumes less gas first. For instance, consider the following examples:

  • require (A || B): If A evaluates to true, the Solidity compiler will not check B because it is unnecessary.
  • require (A && B): If A evaluates to false, the Solidity compiler will not check B because it is unnecessary.

Therefore, it’s best practice to prioritize the least expensive expressions in `require` statements to minimize gas costs and optimize efficiency.

14) Payable vs Non-Payable Functions:

Compared to non-payable functions, payable functions are generally cheaper to execute. This is because non-payable functions require additional opcodes to be included in the smart contract code to check if another contract or external account is trying to send Ether to it. These extra opcodes can increase the gas cost associated with executing the function, making it more expensive. This can be seen in the following code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract withPayable {
// Function Execution Cost = 21,138
function pay() external payable {}
}

contract withNonPayable {
// Function Execution Cost = 21,162
function nonPay() external {}
}

15) Incrementing/Decrementing By 1:

If we increment or decrease the number by 1, there are 4 ways to do that, and all these ways cost different amounts of gas which can be seen in the following code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract IncDecV1 {
uint256 private _num = 5;
// Function Execution Cost = 26,401
function inc() external {
_num += 1;
}
}

contract IncDecV2 {
uint256 private _num = 5;
// Function Execution Cost = 26,388
function inc() external {
_num = _num + 1;
}
}

contract IncDecV3 {
uint256 private _num = 5;
// Function Execution Cost = 26,343
function inc() external {
_num++;
}
}

contract IncDecV4 {
uint256 private _num = 5;
// Function Execution Cost = 26,337
function inc() external {
++_num;
}
}

16) Use Immutable and Constant:

If you want to assign a value that won’t change after construction, use immutable. If you already know the value and it won’t change, use constants. Both can be directly embedded in bytecode, which saves on SLOAD operations.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17;

contract Test {
uint256 constant a = 13;
uint256 immutable b;

constructor() {
// immutable can be updated in constructor
b = 10;
}
}

17) Call function in modifier:

Copying modifier code in multiple instances increases bytecode size. To reduce the size significantly, consider refactoring the code into an internal function, which can be called by the modifier. This optimization should only be used if bytecode size is a concern. Note that this approach incurs the cost of an additional JUMP operation.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract Modifiers {
address private _owner;

modifier onlyOwnerWithoutCall() {
require(_owner == msg.sender, "Ownable: caller is not the owner");
_;
}

// use this instead of above
modifier onlyOwner() {
_checkOwner();
_;
}
function _checkOwner() internal view virtual {
require(_owner == msg.sender, "Ownable: caller is not the owner");
}
}

18) Indexed Events:

Using the indexed keyword for value types like uint, bool, and address can reduce gas costs but this optimization only applies to value types, as indexing bytes and strings can increase gas costs compared to their unindexed versions. Consider the following code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract UnindexedEvents {
event Bid(uint256 tokens, address bidder, uint256 when);
// cost = 23,544
function bid(uint256 tokens) public {
emit Bid(tokens, msg.sender, block.timestamp);
}
}

contract IndexedEvents {
event Bid(
uint256 indexed tokens,
address indexed bidder,
uint256 indexed when
);
// cost = 23,503 gas
function bid(uint256 tokens) public {
emit Bid(tokens, msg.sender, block.timestamp);
}
}

19) Prefer Mapping Instead of Array:

Mapping in Solidity costs less gas than the arrays. If you don’t have strict requirements prefer to use mappings. Consider the following code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract UsingArray {
uint256[] myArr = [1];
// gas cost = 229,417
function setValues() external {
for(uint256 i = 1; i < 10; i++) {
myArr.push(i);
}
}
}

contract UsingMapping {
mapping(uint256 => uint256) myMap;
// gas cost = 222,538
function setValues() external {
for(uint256 i = 1; i < 10; i++) {
myMap[i] = i;
}
}
}

As you can see mapping saved us 229,417–222,538 = 6879 gas which is a huge saving.

20) Avoid Assigning a Value that Will Never be Used:

In Solidity, every variable assignment has an associated gas cost. When initializing variables, default values are often assigned that may never be used, resulting in wasted gas. So avoid assigning the default values that are not used.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract withAssignment {
// gas cost = 21,993
function getVal(uint256 x) external returns (uint256) {
uint256 val = 0;
val = val + x;
return val;
}
}

contract withoutAssignment {
// gas cost = 21,985
function getVal(uint256 x) external returns (uint256) {
uint256 val;
val = val + x;
return val;
}
}

21) Use Double Require Instead of AND:

Using double require instead of AND can save some gas which can be seen in the following code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7;

contract withOperator {
// gas cost = 21510
function check(uint256 x) external returns (uint256) {
require(x > 0 && x < 10);
return x;
}
}

contract withDoubleRequire {
// gas cost = 21502
function check(uint256 x) external returns (uint256) {
require(x > 0);
require(x < 10);
return x;
}
}

22) Files and Large Amounts of Data:

Please refrain from putting files or large amounts of data on blockchain because it can cost a huge sum of money. For this purpose, you can use IPFS which is a distributed storage and costs very little. It provides an ID which then can be stored on the blockchain for reference.

Conclusion:

In conclusion, this article has explored the intricacies of the Solidity compiler and how its behavior relates to gas usage. As developers, we can leverage this knowledge to optimize costs for investors, users, or both. Understanding gas optimization is a crucial skill for Solidity developers looking to create sustainable and cost-effective projects on the Ethereum blockchain. The key takeaways are summarized below:

  • Use Optimizer to optimize your code. Be sure to remember the tradeoffs between contract deployment cost and function execution cost.
  • Use the `unchecked` block wherever you are sure your values won’t be underflowed or overflowed.
  • Go in decrementing order rather than incrementing to make sure gas cost remains consistent.
  • Be sure to cache the value to reduce the gas.
  • Write function names so that when their hashes are ordered the highly cost functions are ordered at the top.
  • Prefer to use Uint256 on other datatypes if there are no strict requirements.
  • When using structs try to pack as many variables as possible.
  • Cache the length of the array to save some gas.
  • Use calldata to save the gas if you only want to read and don’t want to mutate the argument variable itself.
  • Don’t use arrays with greater sizes as the gas cost associated with larger array sizes increases exponentially after a certain point.
  • Prefer to use Less Than/Greater Than rather than Less Than or Equal to/Greater Than or Equal To.
  • If your work allows, revert early as it can save a huge amount of gas.
  • When using the AND/OR operator, use a less expensive operation on the left side of the operator.
  • Payable functions cost less than non-payable functions due to some extra checks.
  • When incrementing/decrementing a value by 1 be sure to use the prefix increment/decrement operator.
  • Use immutable and constants where possible.
  • Call functions in the modifier.
  • Prefer indexed events over unindexed events when using value types.
  • Prefer mappings over arrays wherever possible.
  • Avoid assigning the default values that would never be used.
  • Use double require rather than AND operator.
  • Don’t store files or huge data on the blockchain rather use IPFS to save sums of gas.

References:

https://ethereum.github.io/yellowpaper/paper.pdf

https://www.udemy.com/course/advanced-solidity-understanding-and-optimizing-gas-costs/

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Published in CoinsBench

Where blockchain developers share their stories, experiences & ideas.

Written by Farasat Ali

Tech Savvy 💖 Blockchain, Web & Artificial Intelligence. Love to travel, explore culture & watch anime. Visit My Portfolio 👇 https://linktr.ee/faraasat

No responses yet

Write a response