关于 solidity-gas-optimization
面向智能合约开发者和审查者的 Solidity Gas 优化参考,按影响和风险整理八类技巧,并说明不安全及过时模式的注意事项。
主要功能
- 01涵盖八类优化技巧
- 02按影响和风险排序
- 03存储、部署与 calldata 技巧
- 04编译器与汇编模式
- 05不安全或过时做法警示
- 06基准测试与可读性指导
适用场景
- 01降低合约部署或执行成本
- 02审查合约中的 Gas 优化机会
- 03比较存储布局或设计选择
- 04评估底层优化技巧的风险
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ pseudoyu/ agent-skills --skill solidity-gas-optimization安装后在 Claude Code 中输入/solidity-gas-optimization确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/solidity-gas-guidelines.mdreferences/rules/assembly-01-basics.mdcalldata-01-memory-vs-calldata.mdcompiler-01-loops.mdcompiler-02-math.mdcrosscall-01-multicall.mddeploy-01-custom-errors.mddeploy-02-clones-proxies.mddesign-01-token-standards.mddesign-02-signatures-vs-merkle.mdstorage-01-zero-to-one.mdstorage-02-cache-variables.mdstorage-03-packing.mdstorage-04-constants-immutables.md详细介绍
Solidity Gas 优化
概述
全面的 Solidity 智能合约 Gas 优化指南,涵盖 8 个类别中的 80 多种技巧。基于 RareSkills Book of Gas Optimization。规则按影响和安全性排序。
适用场景
在以下情况下参考这些指南:
- 编写新的 Solidity 智能合约
- 审查或审计现有合约
- 优化部署或执行的 Gas 成本
- 重构合约存储布局
- 实现跨合约交互
- 在设计模式之间进行选择(ERC721 与 ERC1155 等)
按优先级排序的类别
| 优先级 | 类别 | 影响 | 风险 |
|---|---|---|---|
| 1 | 存储优化 | 严重 | 低 |
| 2 | 部署优化 | 高 | 低 |
| 3 | Calldata 优化 | 高 | 低 |
| 4 | 设计模式 | 高 | 中 |
| 5 | 跨合约调用 | 中高 | 中 |
| 6 | 编译器优化 | 中 | 低 |
| 7 | Assembly 技巧 | 中 | 高 |
| 8 | 危险技巧 | 低 | 严重 |
快速参考
严重:存储优化(优先应用)
从零到非零的写入:
- 避免向存储中写入从零变为非零的值(耗费 22,100 Gas)
- 对类似布尔值的数据使用 1/2,而非 0/1
- 在 ERC20 合约中保留最低余额
变量打包:
// 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
}
缓存:
// 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;
}
常量与不可变量:
uint256 constant MAX = 100; // No storage slot
address immutable owner; // Set in constructor, no storage
高:部署优化
自定义错误:
// Bad: ~64+ bytes
require(amount <= limit, "Amount exceeds limit");
// Good: ~4 bytes
error ExceedsLimit();
if (amount > limit) revert ExceedsLimit();
可支付构造函数:
// Saves ~200 gas on deployment
constructor() payable {}
克隆模式:
- 对重复部署使用 EIP-1167 最小代理
- 对可升级合约,优先选择 UUPS,而非 Transparent Proxy
高:Calldata 优化
Calldata 与 Memory:
// Bad: copies to memory
function process(bytes memory data) external {}
// Good: reads directly from calldata
function process(bytes calldata data) external {}
避免使用有符号整数:
- 小的负数开销较高(例如,-1 = 0xffff...)
- 在函数参数中使用无符号整数
高:设计模式
代币标准:
- 对 NFT 优先选择 ERC1155,而非 ERC721(没有 balanceOf 开销)
- 考虑将多个 ERC20 合并为一个 ERC1155
签名与 Merkle:
- 对白名单优先选择 ECDSA 签名,而非 Merkle 树
- 实现 ERC20Permit,以便在一个 tx 中完成 approve 和 transfer
替代库:
- 为提高 Gas 效率,考虑使用 Solmate/Solady,而非 OpenZeppelin
中高:跨合约调用
减少交互:
- 使用 ERC1363 transferAndCall,而非 approve + transferFrom
- 实现 multicall 以批量处理操作
- 缓存外部调用结果(例如 Chainlink 预言机)
访问列表:
- 使用 ERC2930 访问列表交易预热存储
中:编译器优化
循环模式:
// Good: unchecked increment, cached length
uint256 len = arr.length;
for (uint256 i; i < len; ) {
// logic
unchecked { ++i; }
}
命名返回值:
// More efficient bytecode
function calc(uint256 x) pure returns (uint256 result) {
result = x * 2;
}
位移:
// Cheaper: 3 gas
x << 1 // x * 2
x >> 2 // x / 4
// Expensive: 5 gas
x * 2
x / 4
短路布尔表达式:
- 在
&&中将最可能失败的条件放在前面 - 在
||中将最可能成功的条件放在前面
中:Assembly(谨慎使用)
高效检查:
// Check address(0) with assembly
assembly {
if iszero(caller()) { revert(0, 0) }
}
// Even/odd check
x & 1 // instead of x % 2
内存复用:
- 对小型操作复用暂存空间(0x00-0x40)
- 避免在循环中扩展内存
避免:危险技巧
这些做法不适合用于生产环境:
- 将所有函数设为 payable
- 忽略 send() 的返回值
- 使用 gasleft() 进行分支判断
- 在测试中操纵 block.number
已过时的模式
这些模式不再适用于现代 Solidity:
- “external 比 public 便宜”——不再成立
- “!= 0 比 > 0 便宜”——在 0.8.12 左右已发生变化
参考资料
包含代码示例的完整文档:
references/solidity-gas-guidelines.md- 完整指南references/rules/- 按类别整理的单项模式
查找特定模式:
grep -l "storage" references/rules/
grep -l "assembly" references/rules/
grep -l "struct" references/rules/
references/rules/ 中的规则类别
storage-*- 存储优化模式deploy-*- 部署 Gas 节省技巧calldata-*- Calldata 优化design-*- 设计模式选择crosscall-*- 跨合约调用优化compiler-*- 编译器优化模式assembly-*- 底层 Assembly 技巧
核心原则
- 始终进行基准测试 - 编译器行为会因上下文和版本而异
- 兼顾可读性 - 并非所有优化都值得增加代码复杂度
- 两种方案都要测试 - 违反直觉的优化有时反而会增加开销
- 考虑使用
--via-ir- 现代编译器选项可能会使某些技巧过时 - 使用替代库 - 在 Gas 方面,Solmate/Solady 往往优于 OpenZeppelin
评论
支持 Markdown