Top 10 Best Practices for Gas Optimization in Ethereum Smart Contracts

CN
PANews
Follow
5 days ago

The Gas fees on the Ethereum mainnet have always been a significant issue, especially during network congestion. During peak times, users often have to pay extremely high transaction fees. Therefore, optimizing Gas fees during the smart contract development phase is particularly important. Optimizing Gas consumption can not only effectively reduce transaction costs but also improve transaction efficiency, providing users with a more economical and efficient blockchain experience.

This article will outline the Gas fee mechanism of the Ethereum Virtual Machine (EVM), the core concepts related to Gas fee optimization, and best practices for optimizing Gas fees when developing smart contracts. It is hoped that this content will inspire and provide practical help to developers, while also assisting ordinary users in better understanding how EVM's Gas fee operates, collectively addressing the challenges in the blockchain ecosystem.

Overview of EVM's Gas Fee Mechanism

In EVM-compatible networks, "Gas" refers to the unit used to measure the computational power required to execute specific operations.

The diagram below illustrates the structural layout of the EVM. In the diagram, Gas consumption is divided into three parts: operation execution, external message calls, and reading and writing memory and storage.

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

Source: Ethereum Official Website[1]

Since the implementation of EIP-1559 (London Hard Fork), Gas fees are calculated using the following formula:

Gas fee = units of gas used * (base fee + priority fee)

The base fee is burned, while the priority fee serves as an incentive to encourage validators to add transactions to the blockchain. Setting a higher priority fee when sending a transaction can increase the likelihood of the transaction being included in the next block. This is similar to a "tip" paid by users to validators.

1. Understanding Gas Optimization in EVM

When compiling smart contracts with Solidity, the contract is converted into a series of "operation codes," or opcodes.

Each segment of opcodes (such as creating contracts, making message calls, accessing account storage, and executing operations on the virtual machine) has a recognized Gas consumption cost, which is recorded in the Ethereum Yellow Paper[2].

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

After multiple EIP modifications, the Gas costs of some opcodes have been adjusted and may differ from those in the Yellow Paper. For detailed information on the latest costs of opcodes, please refer to this link[3].

2. Basic Concepts of Gas Optimization

The core idea of Gas optimization is to prioritize cost-efficient operations on the EVM blockchain and avoid operations with high Gas costs.

In the EVM, the following operations have lower costs:

  • Reading and writing memory variables
  • Reading constant and immutable variables
  • Reading and writing local variables
  • Reading calldata variables, such as calldata arrays and structs
  • Internal function calls

Higher-cost operations include:

  • Reading and writing state variables stored in contract storage
  • External function calls
  • Loop operations

Best Practices for EVM Gas Fee Optimization

Based on the above basic concepts, we have compiled a list of best practices for Gas fee optimization for the developer community. By following these practices, developers can reduce the Gas fee consumption of smart contracts, lower transaction costs, and create more efficient and user-friendly applications.

1. Minimize Storage Usage

In Solidity, Storage is a limited resource, and its Gas consumption is much higher than that of Memory. Each time a smart contract reads from or writes to storage, it incurs high Gas costs.

According to the definition in the Ethereum Yellow Paper, the cost of storage operations is over 100 times higher than that of memory operations. For example, the OPcodes mload and mstore instructions consume only 3 Gas units, while storage operations like sload and sstore cost at least 100 units even in the most ideal conditions.

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

Methods to limit storage usage include:

  • Storing non-permanent data in memory
  • Reducing the number of storage modifications: by keeping intermediate results in memory and assigning the results to storage variables only after all calculations are complete.

2. Variable Packing

The number of Storage slots used in a smart contract and the way developers represent data can greatly affect Gas fee consumption.

The Solidity compiler packs contiguous storage variables during the compilation process, using 32-byte storage slots as the basic unit for variable storage. Variable packing refers to arranging variables so that multiple variables can fit into a single storage slot.

The left side shows a less efficient implementation that consumes 3 storage slots; the right side shows a more efficient implementation.

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

By making this adjustment, developers can save 20,000 Gas units (storing an unused storage slot costs 20,000 Gas), but now only need two storage slots.

Since each storage slot consumes Gas, variable packing optimizes Gas usage by reducing the number of required storage slots.

3. Optimize Data Types

A variable can be represented by multiple data types, but the operational costs associated with different data types vary. Choosing the appropriate data type helps optimize Gas usage.

For example, in Solidity, integers can be subdivided into different sizes: uint8, uint16, uint32, etc. Since the EVM executes operations in 256-bit units, using uint8 means the EVM must first convert it to uint256, which incurs additional Gas costs.

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

We can compare the Gas costs of uint8 and uint256 in the code shown. The UseUint() function consumes 120,382 Gas units, while the UseUInt8() function consumes 166,111 Gas units.

Individually, using uint256 is cheaper than uint8. However, if we apply the previously suggested variable packing optimization, it would be different. If developers can pack four uint8 variables into a single storage slot, the total cost of iterating through them will be lower than that of four uint256 variables. This way, the smart contract can read and write to a storage slot once and place four uint8 variables into memory/storage in a single operation.

4. Use Fixed-Size Variables Instead of Dynamic Variables

If the data can be controlled within 32 bytes, it is recommended to use the bytes32 data type instead of bytes or strings. Generally, fixed-size variables consume less Gas than variable-size variables. If the byte length can be limited, choose the smallest length from bytes1 to bytes32.

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

5. Mappings vs. Arrays

Data lists in Solidity can be represented by two data types: Arrays and Mappings, but their syntax and structure are entirely different.

Mappings are generally more efficient and cost-effective in most cases, while arrays support iterability and data type packing. Therefore, it is recommended to prioritize using mappings when managing data lists, unless iteration is required or Gas consumption can be optimized through data type packing.

6. Use calldata Instead of Memory

Variables declared in function parameters can be stored in calldata or memory. The main difference is that memory can be modified by the function, while calldata is immutable.

Remember this principle: if function parameters are read-only, prefer using calldata over memory. This avoids unnecessary copying operations from function calldata to memory.

Example 1: Using Memory

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

When using the memory keyword, the values of the array are copied from the encoded calldata to memory during ABI decoding. The execution cost of this code block is 3,694 Gas units.

Example 2: Using Calldata

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

When reading values directly from calldata, it skips the intermediate memory operations. This optimization reduces the execution cost to only 2,413 Gas units, improving Gas efficiency by 35%.

7. Use Constant/Immutable Keywords Whenever Possible

Constant/Immutable variables are not stored in the contract's storage. These variables are computed at compile time and stored in the contract's bytecode. Therefore, their access cost is much lower compared to storage, and it is recommended to use Constant or Immutable keywords whenever possible.

8. Use Unchecked When Overflow/Underflow Can Be Safely Assumed

When developers can ensure that arithmetic operations will not lead to overflow or underflow, they can use the unchecked keyword introduced in Solidity v0.8.0 to avoid unnecessary overflow or underflow checks, thus saving Gas costs.

In the diagram below, the conditionally constrained i

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

Additionally, compilers version 0.8.0 and above no longer require the use of the SafeMath library, as the compiler itself has built-in overflow and underflow protection.

9. Optimize Modifiers

The code of modifiers is embedded into the modified functions, and each time a modifier is used, its code is copied. This increases the bytecode size and raises Gas consumption. Here is a method to optimize modifier Gas costs:

Before Optimization:

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

After Optimization:

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

In this example, by restructuring the logic into an internal function _checkOwner(), the internal function can be reused in the modifier, reducing bytecode size and lowering Gas costs.

10. Short-Circuit Optimization

For the || and && operators, logical operations undergo short-circuit evaluation, meaning that if the first condition can already determine the result of the logical expression, the second condition will not be evaluated.

To optimize Gas consumption, low-cost conditions should be placed first, allowing for the possibility of skipping high-cost calculations.

Additional General Recommendations

1. Remove Unused Code

If there are unused functions or variables in the contract, it is advisable to delete them. This is the most direct way to reduce contract deployment costs and keep the contract size small.

Here are some practical suggestions:

Use the most efficient algorithms for calculations. If certain calculations are directly used in the contract, then redundant calculation processes should be removed. Essentially, any unused calculations should be deleted.

In Ethereum, developers can receive Gas rewards by freeing up storage space. If a variable is no longer needed, it should be deleted using the delete keyword or set to its default value.

Loop optimization: Avoid high-cost loop operations, merge loops whenever possible, and move repeated calculations out of the loop body.

2. Use Precompiled Contracts

Precompiled contracts provide complex library functions, such as cryptographic and hashing operations. Since the code does not run on the EVM but rather on the client node locally, it requires less Gas. Using precompiled contracts can save Gas by reducing the computational workload required to execute smart contracts.

Examples of precompiled contracts include the Elliptic Curve Digital Signature Algorithm (ECDSA) and the SHA2-256 hashing algorithm. By using these precompiled contracts in smart contracts, developers can lower Gas costs and improve application runtime efficiency.

For a complete list of precompiled contracts supported by the Ethereum network, please refer to this link[4].

3. Use Inline Assembly Code

Inline assembly allows developers to write low-level yet efficient code that can be executed directly by the EVM without using expensive Solidity opcodes. Inline assembly also allows for more precise control over memory and storage usage, further reducing Gas fees. Additionally, inline assembly can perform complex operations that are difficult to achieve using Solidity alone, providing more flexibility for optimizing Gas consumption.

Here is an example of code that saves Gas using inline assembly:

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

As seen in the above image, the second use case that employs inline assembly technology has higher Gas efficiency compared to the standard use case.

However, using inline assembly can also introduce risks and is prone to errors. Therefore, it should be used cautiously and limited to experienced developers.

4. Use Layer 2 Solutions

Using Layer 2 solutions can reduce the amount of data that needs to be stored and computed on the Ethereum mainnet.

Layer 2 solutions such as rollups, sidechains, and state channels can offload transaction processing from the main Ethereum chain, enabling faster and cheaper transactions.

By bundling a large number of transactions together, these solutions reduce the number of on-chain transactions, thereby lowering Gas fees. Using Layer 2 solutions can also enhance Ethereum's scalability, allowing more users and applications to participate in the network without causing congestion due to overload.

5. Use Optimization Tools and Libraries

There are several optimization tools available, such as the solc optimizer, Truffle's build optimizer, and Remix's Solidity compiler.

Top Ten Best Practices for Gas Optimization in Ethereum Smart Contracts

These tools can help minimize bytecode size, remove unused code, and reduce the number of operations required to execute smart contracts. Combined with other Gas optimization libraries, such as "solmate," developers can effectively lower Gas costs and improve the efficiency of smart contracts.

Conclusion

Optimizing Gas consumption is an important step for developers, as it minimizes transaction costs and improves the efficiency of smart contracts on EVM-compatible networks. By prioritizing cost-saving operations, reducing storage usage, utilizing inline assembly, and following other best practices discussed in this article, developers can effectively lower the Gas consumption of their contracts.

However, it is crucial to note that during the optimization process, developers must proceed with caution to avoid introducing security vulnerabilities. In the process of optimizing code and reducing Gas consumption, the inherent security of the smart contract should never be sacrificed.

[1]: https://ethereum.org/en/developers/docs/gas/

[2]: https://ethereum.github.io/yellowpaper/paper.pdf

[3]: https://www.evm.codes/

[4]: https://www.evm.codes/precompiled

免责声明:本文章仅代表作者个人观点,不代表本平台的立场和观点。本文章仅供信息分享,不构成对任何人的任何投资建议。用户与作者之间的任何争议,与本平台无关。如网页中刊载的文章或图片涉及侵权,请提供相关的权利证明和身份证明发送邮件到support@aicoin.com,本平台相关工作人员将会进行核查。

Share To
APP

X

Telegram

Facebook

Reddit

CopyLink