solidity-gas-optimization

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Solidity Gas Optimization

Solidity Gas优化

Overview

概述

Comprehensive gas optimization guide for Solidity smart contracts, containing 80+ techniques across 8 categories. Based on the RareSkills Book of Gas Optimization. Rules are prioritized by impact and safety.
这是一份全面的Solidity智能合约Gas优化指南,包含8个类别下的80余种优化技巧。基于RareSkills的《Gas优化手册》编写,规则按影响程度和安全性排序。

When to Apply

适用场景

Reference these guidelines when:
  • Writing new Solidity smart contracts
  • Reviewing or auditing existing contracts
  • Optimizing gas costs for deployment or execution
  • Refactoring contract storage layouts
  • Implementing cross-contract interactions
  • Choosing between design patterns (ERC721 vs ERC1155, etc.)
在以下场景中可参考本指南:
  • 编写新的Solidity智能合约时
  • 评审或审计现有合约时
  • 优化部署或执行的Gas成本时
  • 重构合约存储布局时
  • 实现跨合约交互时
  • 在设计模式间做选择时(如ERC721与ERC1155等)

Priority-Ordered Categories

按优先级排序的类别

PriorityCategoryImpactRisk
1Storage OptimizationCRITICALLOW
2Deployment OptimizationHIGHLOW
3Calldata OptimizationHIGHLOW
4Design PatternsHIGHMEDIUM
5Cross-Contract CallsMEDIUM-HIGHMEDIUM
6Compiler OptimizationsMEDIUMLOW
7Assembly TricksMEDIUMHIGH
8Dangerous TechniquesLOWCRITICAL
优先级类别影响程度风险程度
1存储优化极高
2部署优化
3调用数据优化
4设计模式
5跨合约调用中高
6编译器优化
7汇编技巧
8危险技巧极高

Quick Reference

快速参考

Critical: Storage Optimization (Apply First)

极高优先级:存储优化(优先应用)

Zero-to-One Writes:
  • Avoid zero-to-one storage writes (costs 22,100 gas)
  • Use 1/2 instead of 0/1 for boolean-like values
  • Keep minimum balances in ERC20 contracts
Variable Packing:
solidity
// Bad: 3 slots
struct Unpacked {
    uint64 time;      // slot 1
    uint256 amount;   // slot 2
    address user;     // slot 3
}

// Good: 2 slots
struct Packed {
    uint64 time;      // slot 1 (with address)
    address user;     // slot 1
    uint256 amount;   // slot 2
}
Caching:
solidity
// Bad: reads storage twice
function increment() public {
    require(count < 10);
    count = count + 1;
}

// Good: reads storage once
function increment() public {
    uint256 _count = count;
    require(_count < 10);
    count = _count + 1;
}
Constants & Immutables:
solidity
uint256 constant MAX = 100;        // No storage slot
address immutable owner;           // Set in constructor, no storage
零到一写入优化:
  • 避免零到一的存储写入(成本为22,100 Gas)
  • 用1/2替代0/1来表示类布尔值
  • 在ERC20合约中保留最低余额
变量打包:
solidity
// 糟糕:占用3个存储槽
struct Unpacked {
    uint64 time;      // 槽1
    uint256 amount;   // 槽2
    address user;     // 槽3
}

// 良好:占用2个存储槽
struct Packed {
    uint64 time;      // 槽1(与address共用)
    address user;     // 槽1
    uint256 amount;   // 槽2
}
缓存优化:
solidity
// 糟糕:读取存储两次
function increment() public {
    require(count < 10);
    count = count + 1;
}

// 良好:读取存储一次
function increment() public {
    uint256 _count = count;
    require(_count < 10);
    count = _count + 1;
}
常量与不可变变量:
solidity
uint256 constant MAX = 100;        // 不占用存储槽
address immutable owner;           // 在构造函数中设置,不占用存储

High: Deployment Optimization

高优先级:部署优化

Custom Errors:
solidity
// Bad: ~64+ bytes
require(amount <= limit, "Amount exceeds limit");

// Good: ~4 bytes
error ExceedsLimit();
if (amount > limit) revert ExceedsLimit();
Payable Constructors:
solidity
// Saves ~200 gas on deployment
constructor() payable {}
Clone Patterns:
  • Use EIP-1167 minimal proxies for repeated deployments
  • Use UUPS over Transparent Proxy for upgradeable contracts
自定义错误:
solidity
// 糟糕:约占用64+字节
require(amount <= limit, "Amount exceeds limit");

// 良好:约占用4字节
error ExceedsLimit();
if (amount > limit) revert ExceedsLimit();
可支付构造函数:
solidity
// 部署时节省约200 Gas
constructor() payable {}
克隆模式:
  • 对于重复部署,使用EIP-1167最小代理
  • 对于可升级合约,优先使用UUPS而非透明代理

High: Calldata Optimization

高优先级:调用数据优化

Calldata vs Memory:
solidity
// Bad: copies to memory
function process(bytes memory data) external {}

// Good: reads directly from calldata
function process(bytes calldata data) external {}
Avoid Signed Integers:
  • Small negative numbers are expensive (e.g., -1 = 0xffff...)
  • Use unsigned integers in function parameters
Calldata与Memory对比:
solidity
// 糟糕:复制到内存
function process(bytes memory data) external {}

// 良好:直接从calldata读取
function process(bytes calldata data) external {}
避免使用有符号整数:
  • 小的负数成本很高(例如:-1 = 0xffff...)
  • 在函数参数中使用无符号整数

High: Design Patterns

高优先级:设计模式

Token Standards:
  • Prefer ERC1155 over ERC721 for NFTs (no balanceOf overhead)
  • Consider consolidating multiple ERC20s into one ERC1155
Signature vs Merkle:
  • Prefer ECDSA signatures over Merkle trees for allowlists
  • Implement ERC20Permit for approve + transfer in one tx
Alternative Libraries:
  • Consider Solmate/Solady over OpenZeppelin for gas efficiency
代币标准:
  • 对于NFT,优先选择ERC1155而非ERC721(无balanceOf开销)
  • 考虑将多个ERC20合并为一个ERC1155
签名与默克尔树对比:
  • 对于白名单,优先选择ECDSA签名而非默克尔树
  • 实现ERC20Permit,将approve与transfer合并为一笔交易
替代库选择:
  • 为了Gas效率,考虑使用Solmate/Solady替代OpenZeppelin

Medium-High: Cross-Contract Calls

中高优先级:跨合约调用

Reduce Interactions:
  • Use ERC1363 transferAndCall instead of approve + transferFrom
  • Implement multicall for batching operations
  • Cache external call results (e.g., Chainlink oracles)
Access Lists:
  • Use ERC2930 access list transactions to pre-warm storage
减少交互次数:
  • 使用ERC1363的transferAndCall替代approve + transferFrom
  • 实现multicall来批量操作
  • 缓存外部调用结果(如Chainlink预言机)
访问列表:
  • 使用ERC2930访问列表交易来预加载存储

Medium: Compiler Optimizations

中优先级:编译器优化

Loop Patterns:
solidity
// Good: unchecked increment, cached length
uint256 len = arr.length;
for (uint256 i; i < len; ) {
    // logic
    unchecked { ++i; }
}
Named Returns:
solidity
// More efficient bytecode
function calc(uint256 x) pure returns (uint256 result) {
    result = x * 2;
}
Bitshifting:
solidity
// Cheaper: 3 gas
x << 1   // x * 2
x >> 2   // x / 4

// Expensive: 5 gas
x * 2
x / 4
Short-Circuit Booleans:
  • Place likely-to-fail conditions first in
    &&
  • Place likely-to-succeed conditions first in
    ||
循环模式:
solidity
// 良好:无检查递增,缓存长度
uint256 len = arr.length;
for (uint256 i; i < len; ) {
    // 逻辑代码
    unchecked { ++i; }
}
命名返回值:
solidity
// 字节码更高效
function calc(uint256 x) pure returns (uint256 result) {
    result = x * 2;
}
位运算替代乘除:
solidity
// 低成本:3 Gas
x << 1   // x * 2
x >> 2   // x / 4

// 高成本:5 Gas
x * 2
x / 4
布尔短路优化:
  • &&
    中,将可能失败的条件放在前面
  • ||
    中,将可能成功的条件放在前面

Medium: Assembly (Use Carefully)

中优先级:汇编(谨慎使用)

Efficient Checks:
solidity
// Check address(0) with assembly
assembly {
    if iszero(caller()) { revert(0, 0) }
}

// Even/odd check
x & 1  // instead of x % 2
Memory Reuse:
  • Reuse scratch space (0x00-0x40) for small operations
  • Avoid memory expansion in loops
高效检查:
solidity
// 用汇编检查address(0)
assembly {
    if iszero(caller()) { revert(0, 0) }
}

// 奇偶检查
x & 1  // 替代x % 2
内存复用:
  • 复用临时空间(0x00-0x40)用于小型操作
  • 避免在循环中扩展内存

Avoid: Dangerous Techniques

需避免:危险技巧

These are unsafe for production:
  • Making all functions payable
  • Ignoring send() return values
  • Using gasleft() for branching
  • Manipulating block.number in tests
这些技巧在生产环境中不安全:
  • 将所有函数设为payable
  • 忽略send()的返回值
  • 使用gasleft()进行分支判断
  • 在测试中操纵block.number

Outdated Patterns

已过时的模式

These no longer apply in modern Solidity:
  • "external is cheaper than public" - No longer true
  • "!= 0 is cheaper than > 0" - Changed around 0.8.12
这些模式在现代Solidity中不再适用:
  • "external比public更便宜" - 已不再成立
  • "!=0比>0更便宜" - 在0.8.12左右发生了变化

References

参考资料

Full documentation with code examples:
  • references/solidity-gas-guidelines.md
    - Complete guide
  • references/rules/
    - Individual patterns by category
To look up specific patterns:
bash
grep -l "storage" references/rules/
grep -l "assembly" references/rules/
grep -l "struct" references/rules/
包含代码示例的完整文档:
  • references/solidity-gas-guidelines.md
    - 完整指南
  • references/rules/
    - 按类别划分的单个模式
查找特定模式的方法:
bash
grep -l "storage" references/rules/
grep -l "assembly" references/rules/
grep -l "struct" references/rules/

Rule Categories in
references/rules/

references/rules/
中的规则类别

  • storage-*
    - Storage optimization patterns
  • deploy-*
    - Deployment gas savings
  • calldata-*
    - Calldata optimization
  • design-*
    - Design pattern choices
  • crosscall-*
    - Cross-contract call optimization
  • compiler-*
    - Compiler optimization patterns
  • assembly-*
    - Low-level assembly tricks
  • storage-*
    - 存储优化模式
  • deploy-*
    - 部署Gas节省技巧
  • calldata-*
    - 调用数据优化
  • design-*
    - 设计模式选择
  • crosscall-*
    - 跨合约调用优化
  • compiler-*
    - 编译器优化模式
  • assembly-*
    - 底层汇编技巧

Key Principles

核心原则

  1. Always Benchmark - Compiler behavior varies by context and version
  2. Balance Readability - Not all optimizations are worth code complexity
  3. Test Both Approaches - Counterintuitive optimizations sometimes increase costs
  4. Consider
    --via-ir
    - Modern compiler option may obsolete some tricks
  5. Use Alternative Libraries - Solmate/Solady often beat OpenZeppelin on gas
  1. 始终进行基准测试 - 编译器行为会因上下文和版本而异
  2. 平衡可读性 - 并非所有优化都值得牺牲代码复杂度
  3. 测试两种实现方式 - 违反直觉的优化有时会增加成本
  4. 考虑使用
    --via-ir
    - 现代编译器选项可能会使某些技巧过时
  5. 使用替代库 - Solmate/Solady在Gas效率上通常优于OpenZeppelin