MODULE 2 · GAS 机制

Lesson 6: Gas 估算与优化

📖 阅读 ~12 分钟🧪 2 个实验❓ 2 道测验

eth_estimateGas:预估交易成本

发送交易之前,你可以用 eth_estimateGas RPC 方法预估 Gas 消耗。钱包(MetaMask)在每笔交易前都自动调用它。

// eth_estimateGas 的工作原理 1. 在当前状态上模拟执行交易(不上链) 2. 记录消耗的 Gas 3. 返回估算值(通常会加一点余量) // 注意:估算可能不准,因为: - 状态在估算和实际执行之间可能改变 - 合约逻辑可能依赖区块号、时间戳 - 其他用户的交易可能先执行,改变状态
⚠️ 估算失败 = 交易会 revert 如果 eth_estimateGas 返回错误,说明交易在当前状态下会失败。MetaMask 会提示"交易可能失败"—— 这就是它在做 Gas 估算并发现了 revert。

Cold vs Warm 访问

EIP-2929(柏林升级)引入了 冷/暖访问的概念:同一笔交易中,首次访问一个地址或 storage slot 更贵(cold),后续访问更便宜(warm)。

操作Cold(首次)Warm(后续)
SLOAD(读 storage)2,100 gas100 gas
BALANCE / EXTCODESIZE2,600 gas100 gas
CALL / STATICCALL2,600 gas100 gas
💡 为什么区分冷暖 Cold 访问需要从磁盘加载数据到内存;warm 访问数据已在内存中。区分定价让 Gas 更公平地反映实际计算成本。这也是为什么访问列表 (Access List)可以节省 Gas —— 它预声明要访问的地址/slot。

Solidity Gas 优化技巧

🥇 Top 1: 减少 Storage 读写

// ❌ 坏:每次循环都读写 storage for (uint i = 0; i < arr.length; i++) { total += arr[i]; // SLOAD × N 次 } storedTotal = total; // SSTORE // ✅ 好:用 memory 变量缓存 uint256 len = arr.length; // SLOAD 1 次 uint256 sum = 0; // memory,几乎免费 for (uint i = 0; i < len; i++) { sum += arr[i]; } storedTotal = sum; // SSTORE 1 次

🥈 Top 2: 变量打包

// ❌ 浪费 3 个 slot uint256 amount; // slot 0 (32B) bool active; // slot 1 (32B 只用了 1B) address owner; // slot 2 (32B 只用了 20B) // ✅ 只用 2 个 slot uint256 amount; // slot 0 address owner; // slot 1 (20B) bool active; // slot 1 (打包! +1B)

🥉 Top 3: 用 calldata 代替 memory

// ❌ memory: 复制数据到内存,消耗 Gas function process(uint[] memory data) external { ... } // ✅ calldata: 直接读取输入数据,不复制 function process(uint[] calldata data) external { ... } // 对于只读的外部函数参数,calldata 更便宜

更多优化技巧

短路运算
把便宜的条件放前面:if (a && expensiveCheck())
节省:跳过不必要的计算
用 error 代替 require string
error Unauthorized();require(x, "msg") 便宜
节省:~50 gas + 部署时的字节码
unchecked 算术
确定不会溢出时,用 unchecked { i++; }
节省:~20 gas/次(省去溢出检查)
immutable / constant
不变的值用 immutableconstant,不占 storage
节省:2100 gas/读(避免 SLOAD)

Gas 费用的美元换算

// 公式: 费用(USD) = gasUsed × gasPrice(gwei) × ETH价格(USD) ÷ 10⁹ // 例:Uniswap swap @ 10 gwei, ETH=$2500 费用 = 150,000 × 10 × 2500 ÷ 1,000,000,000 = $3.75 // 同样的 swap 在 L2 (Arbitrum, 0.01 gwei) 费用 = 150,000 × 0.01 × 2500 ÷ 1,000,000,000 = $0.00375 ← 便宜 1000 倍
🔍 Gas 省钱策略 时机选择:周末和 UTC 深夜 Gas 通常最便宜。
链的选择:不需要 L1 安全性的操作放到 L2(Arbitrum/Base/Optimism)。
批量操作:多个操作合并为一笔交易,分摊 21,000 基础 Gas。

🧪 动手实验

🔬 实验 1:多链 Gas 价格对比

对比以太坊 L1 和 L2 的实时 Gas 价格差异。

chainlab tool gas

计算:在当前 Gas 价格下,一笔 Uniswap swap(150k gas)在各链上分别花多少钱?

🔬 实验 2:查看市场行情辅助计算

获取当前 ETH 价格,结合 Gas 价格算出美元成本。

chainlab tool prices

用公式:gasUsed × gasPrice(gwei) × ETH价格 ÷ 10⁹ 亲手算一下。

❓ 自测

检验你的理解

Q1: 同一笔交易中第二次读取同一个 storage slot (SLOAD) 消耗多少 Gas?

✅ 正确!首次 SLOAD 是 cold(2,100 gas),后续都是 warm(100 gas)。所以用 memory 变量缓存 storage 值非常值得。
❌ 提示:EIP-2929 引入了冷/暖机制。首次访问 = cold,后续 = warm。

Q2: 以下哪个不是有效的 Solidity Gas 优化技巧?

✅ 正确!声明 public 会让编译器自动生成 getter 函数,增加字节码大小和部署成本。可见性应按需设置。
❌ 想想:哪个选项不但不能省 Gas,反而会增加成本?

📝 本课小结

核心要点 1. eth_estimateGas 通过模拟执行预估 Gas,估算失败 = 交易会 revert
2. Cold 访问比 warm 访问贵 20 倍(2100 vs 100 gas)
3. Gas 优化三板斧:减少 SSTORE/SLOAD、变量打包、用 calldata
4. constant/immutable 避免 SLOAD,unchecked 省去溢出检查
5. 费用(USD) = gasUsed × gasPrice × ETH价格 ÷ 10⁹
上一课
Lesson 5: EIP-1559 与 Gas
Module 2 完成!下一模块
Module 3: Solidity 开发