m mybian.xyz
Gas优化代码示例

Gas优化代码示例:六个可直接复用的 Solidity 节流片段

Talk is cheap,show me the code。本文给出六段经过实战验证的 Gas 优化代码示例,附上前后对比,可直接搬到你的项目。

m
mybian.xyz 编辑部
1378 字· 约 3 分钟阅读· 2026-05-24T06:12:20.351569+00:00
Gas优化代码示例 - Gas优化代码示例:六个可直接复用的 Solidity 节流片段
关于「Gas优化代码示例」的视觉延伸

示例一:变量缓存

原代码每次循环都读取 storage:

for (uint256 i = 0; i < users.length; i++) {
    totalLocked += userLocked[users[i]];
}

优化后:

uint256 _total = totalLocked;
uint256 len = users.length;
for (uint256 i; i < len; ) {
    _total += userLocked[users[i]];
    unchecked { ++i; }
}
totalLocked = _total;

收益:循环越长收益越大,Binance 智能链常见 30% 以上下降。

示例二:storage 槽合并

uint128 totalLocked;
uint128 totalSupply; // 同一槽位

配合 getter 函数显式拆分读取,节省一次 SSTORE。打包前后用 forge inspect storage-layout 校验,避免误踩字段对齐陷阱。

示例三:custom errors

error NotOwner(address caller);
function transfer(address to, uint256 amount) external {
    if (msg.sender != owner) revert NotOwner(msg.sender);
}

对比 require 字符串平均节省 50 gas/次,且前端能解析错误码。币安 智能链上多家钱包已支持解析 custom errors。

示例四:calldata 替代 memory

function batchTransfer(address[] calldata to, uint256[] calldata amounts) external {
    uint256 len = to.length;
    for (uint256 i; i < len; ) {
        _transfer(msg.sender, to[i], amounts[i]);
        unchecked { ++i; }
    }
}

外部函数尽量用 calldata,避免内存复制开销。B安 上的批量空投合约普遍采用这种写法。

示例五:位运算压缩布尔状态

uint256 flags; // 256 个布尔标志
function setFlag(uint8 idx) external {
    flags |= (1 << idx);
}
function hasFlag(uint8 idx) external view returns (bool) {
    return (flags >> idx) & 1 == 1;
}

比起每个布尔占一个 slot,能极大节省存储。必安 上线协议中的白名单/权限位常用此手法。

示例六:高频函数前置

通过 Solady 的 LibSelector 或外部脚本,调整 function 名称使其选择器靠前。例如把 transferFrom 选择器排在 0x23b872dd 前,每次调用省下数百 gas。

验证流程

所有改动后跑 forge snapshot --diff,对比 baseline 与 optimized;同时跑完整单元测试与 fuzz 测试,确保功能与安全无回归。把对比报告附在 PR 描述里,方便 review。

复用建议

建议把这些片段抽成内部 lint 规则或代码模板,新项目启动即引入。配合团队 Code Review 流程,gas 优化将不再依赖个别专家,而成为团队共识。你的合约自然会在 BN 智能链等多链场景中保持低 gas 优势。