· EVM Gas 优化 · 5 min read

EVM Layer 2 智能合约 Gas 优化技巧与实践

虽然 Layer 2 的 Gas 费远低于以太坊主网,但在高频交互或批量清算场景下,Gas 优化依然能为项目方节省巨大成本。本文分享实用的 Solidity 内存与存储优化技巧。

引言

随着 Arbitrum、Optimism、Base 和 Scroll 等 EVM Layer 2 网络的高速普及,单次交易的 Gas 费已经降至美分甚至更低。这导致很多开发者在编写 Solidity 合约时完全忽略了 Gas 优化的环节。

然而,在高频的流动性矿池(Yield Farming)、游戏链上资产发放以及批量清算协议中,累计的 Gas 消耗仍然是一笔巨大的开支。尤其是在以太坊主网拥堵导致 L2 提交数据(L1 Calldata)成本上升时,低效的代码会导致用户流失。

Northstar Protocol Studio 整理了以下几种业界通用的 Solidity 合约 Gas 优化方案。


1. 优化变量存储:使用 Calldata 代替 Memory

在 Solidity 函数参数中,凡是只读类型的数组、结构体或字符串参数,应当优先声明为 calldata 替代 memory

  • Memory(内存):会将参数数据完整复制到内存空间中,以便后续读写,这会产生额外的分配和复制 Gas。
  • Calldata(调用数据):它是只读的临时存储区,避免了内存分配,可以直接进行指针级别的数据拉取,极大节省 Gas 消耗。
// 低效做法
function checkUsers(User[] memory users) public pure { ... }

// 优化做法
function checkUsers(User[] calldata users) public pure { ... }

2. 紧凑排列状态变量 (Variable Packing)

EVM 虚拟机每次读写状态变量都是以 32 字节(256 位)的 Slot(插槽)为基本单位进行操作。如果变量排列合理,可以被打包存放在同一个 Slot 中,从而将多次 SSTORE(写入存储)操作合并为一次。

在排列状态变量时,应该按照大小顺序从高到低连续声明变量

// 低效布局:占用了 3 个 Slot
uint128 public variableA;
uint256 public variableB;
uint128 public variableC;

// 优化布局:A 和 C 打包在同一个 Slot 中,总共只占用 2 个 Slot
uint256 public variableB;
uint128 public variableA;
uint128 public variableC;

3. 巧用 unchecked 块绕过数学检查

从 Solidity 0.8.0 版本开始,编译器默认在所有的加减乘除计算中自动注入溢出校验。这保障了安全性,但每一步计算都会增加额外的判断指令(Gas 损耗)。

如果您能通过逻辑 100% 确认某项操作不会发生溢出(例如在一个固定范围的 for 循环计数器中),可以使用 unchecked 块进行优化:

// 低效写法
for (uint256 i = 0; i < length; i++) {
    // 每次 i++ 都会有溢出检查
}

// 优化写法:节省约 20% 的循环 Gas
for (uint256 i = 0; i < length; ) {
    // 核心逻辑...
    unchecked { i++; }
}

4. 优化条件控制语句:将低成本检查置前

在逻辑判断(如 requireif 块)中,有多个判断条件并联时,应将计算成本最低、最可能拦截流量的条件放在最前面

由于 Solidity 采用“短路求值(Short-Circuit Evaluation)”规则,如果第一个并联条件已经能确定表达式的值,后续高成本的条件(如查询其他合约或读取 State 变量)就不会执行,从而大幅节省 Gas 消耗。


结论

Gas 优化是一项细致入微的工程实践,不仅体现在编译器层面的调优,更考验着开发者对 EVM 存储布局的深度掌握。可在适用项目中使用 Foundry 的 gas-report 工具进行性能对比,具体结果以测试数据为准。

Back to Blog

Related Posts

View All Posts »

Solidity 智能合约安全开发最佳实践

在 Web3 协议开发中,智能合约的安全性是决定项目成败的关键。本文详细探讨了 Solidity 合约编写中的高频漏洞(如重入攻击、权限溢出、溢出等)及对应的防御性编程技巧与审计规范。