name: gas-optimization
description: EVM智能合约的高级gas优化技术。涵盖存储打包、内存与calldata优化、汇编/Yul、高效数据结构、批量操作以及基于基准测试的优化策略。
allowed-tools: Read, Grep, Write, Bash, Edit, Glob, WebFetch
Gas优化技能
EVM智能合约的高级gas优化技术,包含基于基准测试的分析。
能力
- 存储优化:存储打包、插槽管理、SLOAD/SSTORE最小化
- 内存管理:内存与calldata优化、扩展成本
- 汇编/Yul:关键路径的低级优化
- 数据结构:gas高效的映射、数组和结构体
- 批量操作:多调用模式、批量转账
- 基准测试:gas分析、对比分析
MCP/工具集成
存储优化
存储布局打包
// 差:3个存储插槽(使用96字节,分配96字节)
contract BadPacking {
uint128 a; // 插槽0(16字节)
uint256 b; // 插槽1(32字节)- 无法与a打包
uint128 c; // 插槽2(16字节)
}
// 好:2个存储插槽(使用80字节,分配64字节)
contract GoodPacking {
uint128 a; // 插槽0,字节0-15
uint128 c; // 插槽0,字节16-31
uint256 b; // 插槽1
}
// Gas节省:每次避免SSTORE约20,000 gas
最小化存储写入
// 差:多次存储写入
function badUpdate(uint256 newA, uint256 newB) external {
a = newA; // SSTORE:20,000 gas(冷)或2,900 gas(热)
b = newB; // SSTORE:2,900 gas(同一交易中的热插槽)
}
// 好:使用打包结构体的单次存储写入
struct Data {
uint128 a;
uint128 b;
}
Data public data;
function goodUpdate(uint128 newA, uint128 newB) external {
data = Data(newA, newB); // 单次SSTORE:20,000 gas
}
查找时使用映射而非数组
// 差:O(n)查找,大型数组成本高
uint256[] public values;
function exists(uint256 value) public view returns (bool) {
for (uint i = 0; i < values.length; i++) {
if (values[i] == value) return true; // 每次迭代SLOAD
}
return false;
}
// 好:O(1)查找
mapping(uint256 => bool) public valueExists;
function exists(uint256 value) public view returns (bool) {
return valueExists[value]; // 单次SLOAD
}
内存 vs Calldata
函数参数
// 差:将数组复制到内存
function processArray(uint256[] memory data) external {
// 内存复制成本:每个字3 gas + 扩展
}
// 好:直接从calldata读取
function processArray(uint256[] calldata data) external {
// 无复制,仅指向calldata的指针
// 节省:每32字节约60 gas
}
// 注意:如果需要修改数组,则使用内存
字符串/字节处理
// 对于只读操作,使用calldata
function validate(string calldata input) external pure returns (bool) {
return bytes(input).length > 0;
}
// 对于修改操作,使用内存
function transform(string memory input) internal pure returns (string memory) {
bytes memory b = bytes(input);
b[0] = 'X';
return string(b);
}
无检查算术
// 差:每次操作都进行溢出检查(Solidity 0.8+)
function sumArray(uint256[] calldata arr) external pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < arr.length; i++) {
sum += arr[i]; // 溢出检查:每次操作约40 gas
}
return sum;
}
// 好:溢出不可能时使用unchecked
function sumArray(uint256[] calldata arr) external pure returns (uint256) {
uint256 sum = 0;
uint256 length = arr.length;
for (uint256 i = 0; i < length;) {
unchecked {
sum += arr[i];
++i; // ++i比i++便宜
}
}
return sum;
}
// 节省:每次迭代约40 gas
循环优化
缓存数组长度
// 差:每次迭代从存储读取长度
for (uint i = 0; i < array.length; i++) { } // 每次迭代SLOAD
// 好:缓存长度
uint256 length = array.length;
for (uint i = 0; i < length; i++) { } // 单次SLOAD
前自增
// 差:后自增创建临时变量
for (uint i = 0; i < length; i++) { }
// 好:前自增更便宜
for (uint i = 0; i < length; ++i) { }
// 节省:每次迭代约5 gas
自定义错误
// 差:字符串错误消息
require(balance >= amount, "余额不足");
// 成本:每个字符约50 gas + 内存扩展
// 好:自定义错误(Solidity 0.8.4+)
error InsufficientBalance(uint256 available, uint256 required);
if (balance < amount) revert InsufficientBalance(balance, amount);
// 成本:错误选择器固定约24 gas
// 节省:典型错误消息约50+ gas
汇编/Yul优化
高效余额检查
// Solidity
function getBalance(address account) external view returns (uint256) {
return account.balance;
}
// 汇编(稍便宜)
function getBalance(address account) external view returns (uint256 bal) {
assembly {
bal := balance(account)
}
}
高效内存操作
// 高效复制32字节
function copy32(bytes32 source) internal pure returns (bytes32 dest) {
assembly {
dest := source
}
}
// 高效keccak256
function efficientHash(bytes32 a, bytes32 b) internal pure returns (bytes32 result) {
assembly {
mstore(0x00, a)
mstore(0x20, b)
result := keccak256(0x00, 0x40)
}
}
批量操作
批量转账
// 差:单独转账
function transferToMany(address[] calldata recipients, uint256 amount) external {
for (uint i = 0; i < recipients.length; ++i) {
token.transfer(recipients[i], amount); // 21000基础 + 转账成本
}
}
// 好:批量转账(如果支持)
function batchTransfer(
address[] calldata recipients,
uint256[] calldata amounts
) external {
// 单次函数调用开销
// 减少token状态的SLOAD
}
多调用模式
function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; ++i) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
require(success);
results[i] = result;
}
}
// 在单次交易中组合多个操作
基准测试工作流
使用Foundry Gas报告
# 运行测试并生成gas报告
forge test --gas-report
# 快照gas使用情况
forge snapshot
# 与之前的快照对比
forge snapshot --check
Gas快照格式
| 合约 | 函数 | 最小 | 平均 | 最大 | 调用次数 |
|----------|----------|-----|-----|-----|---------|
| Token | transfer | 51234 | 54123 | 65432 | 100 |
| Token | approve | 24356 | 24356 | 24356 | 50 |
对比测试
contract GasComparison is Test {
function test_gasComparison_approach1() public {
uint256 gasBefore = gasleft();
// 方法1
uint256 gasUsed = gasBefore - gasleft();
emit log_named_uint("方法1 gas", gasUsed);
}
function test_gasComparison_approach2() public {
uint256 gasBefore = gasleft();
// 方法2
uint256 gasUsed = gasBefore - gasleft();
emit log_named_uint("方法2 gas", gasUsed);
}
}
常见优化总结
| 技术 |
节省 |
风险 |
| 存储打包 |
20,000 gas/插槽 |
低 |
| Calldata vs 内存 |
60 gas/32字节 |
低 |
| 无检查算术 |
40 gas/操作 |
中 |
| 自定义错误 |
50+ gas/错误 |
低 |
| 缓存存储读取 |
100-2100 gas |
低 |
| 循环前自增 |
5 gas/迭代 |
低 |
| 汇编 |
可变 |
高 |
流程集成
此技能与以下集成:
gas-optimization.js - 完整优化流程
smart-contract-development-lifecycle.js - 开发最佳实践
amm-pool-development.js - DeFi特定优化
工具参考
另请参阅
skills/evm-analysis/SKILL.md - 字节码分析
agents/gas-optimizer/AGENT.md - Gas优化代理
references.md - Gas优化资源