跳转到内容

以太坊 Gas 机制详解

以太坊 Gas 机制详解

Gas 是以太坊网络中衡量执行特定操作所需计算工作量的单位。Gas 机制是以太坊经济安全性的核心,防止网络资源被恶意滥用。

什么是 Gas

在以太坊中,每一笔交易和每一个智能合约操作都需要消耗 Gas。Gas 的作用类似于给汽车加油,没有 Gas,交易就无法执行。

Gas 的核心概念

  • Gas:衡量计算工作量的抽象单位
  • Gas Price:每单位 Gas 愿意支付的价格(以 Gwei 计)
  • Gas Limit:用户设定的交易最大 Gas 消耗量
  • Gas Used:交易实际消耗的 Gas 量
交易费用(ETH)= Gas Used × Gas Price(Gwei)/ 10^9

EIP-1559 费用机制

2021 年 8 月,以太坊通过 EIP-1559 对 Gas 费用机制进行了重大改革,引入了更可预测的费用结构。

新的费用结构

总费用 = (Base Fee + Priority Fee) × Gas Used

Base Fee(基础费用)

  • 由协议算法自动确定,根据网络拥堵情况调整
  • 被销毁(不给矿工/验证者)
  • 每个区块调整幅度最大 12.5%
  • 目标区块 Gas 使用量为区块限制的 50%

Priority Fee(优先费/小费)

  • 直接支付给矿工/验证者
  • 用于激励将交易打包进区块
  • 网络不拥堵时设置较低值即可(通常 1-2 Gwei)

Max Fee(最大费用)

  • 用户愿意支付的每单位 Gas 最高价格
  • Max Fee ≥ Base Fee + Priority Fee
  • 多余部分(Max Fee - Base Fee - Priority Fee)退还给用户

Base Fee 调整算法

新 Base Fee = 上一区块 Base Fee × (1 + 0.125 × (Gas Used - Gas Target) / Gas Target)
区块使用率Base Fee 变化
100%(满载)+12.5%
50%(目标)不变
0%(空块)-12.5%

不同操作的 Gas 消耗

基础交易

操作Gas 消耗
普通 ETH 转账21,000
合约部署32,000 + 代码大小
零字节 calldata4
非零字节 calldata16

EVM 操作码 Gas 成本

操作码Gas说明
ADD/SUB3基础算术
MUL/DIV5乘除法
SLOAD2100读 storage(冷访问)
SSTORE20000写 storage(从零到非零)
SSTORE5000写 storage(修改非零值)
SSTORE2900写 storage(清零退款)
CREATE32000创建合约
CALL700+调用合约
LOG0-4375+发出事件

Gas Limit 的含义

交易 Gas Limit

用户在发送交易时设置,是愿意消耗的最大 Gas 量。

  • 如果实际消耗 < Gas Limit:多余的 Gas 退还
  • 如果实际消耗 > Gas Limit:交易失败,已消耗的 Gas 不退还

区块 Gas Limit

每个区块能包含的最大 Gas 总量,限制了每个区块中可以执行的操作总量。

区块 Gas Limit ≈ 30,000,000(约 3000 万,可动态调整)

Gas 优化策略

1. 减少 Storage 操作

Storage 读写是最昂贵的操作。优化策略:

// ❌ 低效 - 多次读写 storage
contract Inefficient {
uint256 public total;
function sumArray(uint256[] memory arr) external {
for (uint256 i = 0; i < arr.length; i++) {
total += arr[i]; // 每次循环都 SLOAD + SSTORE
}
}
}
// ✅ 高效 - 使用内存变量缓存
contract Efficient {
uint256 public total;
function sumArray(uint256[] memory arr) external {
uint256 _total = total; // 一次 SLOAD
for (uint256 i = 0; i < arr.length; i++) {
_total += arr[i]; // 纯内存操作
}
total = _total; // 一次 SSTORE
}
}

2. 使用合适的数据类型

// ❌ 使用 uint8 在结构体中可能导致额外的位操作
// ✅ 使用 uint256 更高效(EVM 原生 32 字节操作)
// ✅ 变量打包 - 将小类型放在一起节省 storage 槽位
struct Packed {
uint128 a; // 16 bytes
uint128 b; // 16 bytes → 两个变量共用一个 storage 槽位!
}
struct Unpacked {
uint256 a; // 32 bytes
uint256 b; // 32 bytes → 两个 storage 槽位
}

3. 减少链上数据

// ❌ 存储大量数据到 storage
string public largeData; // 非常昂贵
// ✅ 只存哈希,数据放链下
bytes32 public dataHash; // 便宜
// ✅ 使用事件存储历史数据
event DataStored(bytes32 indexed hash, string data);

4. 使用 calldata 而非 memory

// ❌ 使用 memory - 复制数据
function process(string memory data) external { ... }
// ✅ 使用 calldata - 直接读取
function process(string calldata data) external { ... }

5. 短路求值

// ✅ 将低成本检查放在前面(短路时避免执行后续昂贵操作)
require(msg.value > 0 && complexCheck(), "Failed");
// 等价于更清晰的写法
if (msg.value == 0) revert("No value");
if (!complexCheck()) revert("Check failed");

6. 避免冗余计算

// ❌ 重复调用函数
function bad() external view returns (uint256) {
return expensiveOperation() + expensiveOperation();
}
// ✅ 缓存结果
function good() external view returns (uint256) {
uint256 result = expensiveOperation();
return result + result;
}

如何估算 Gas

使用 ethers.js 估算

const { ethers } = require('ethers');
const provider = new ethers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR_KEY');
// 估算转账 Gas
const gasEstimate = await provider.estimateGas({
to: '0xRecipient',
value: ethers.parseEther('0.1'),
});
console.log('预估 Gas:', gasEstimate.toString());
// 获取当前 Gas 价格信息
const feeData = await provider.getFeeData();
console.log('Base Fee:', ethers.formatUnits(feeData.gasPrice, 'gwei'), 'Gwei');
console.log('Max Priority Fee:', ethers.formatUnits(feeData.maxPriorityFeePerGas, 'gwei'), 'Gwei');

使用 Hardhat Gas Reporter

Terminal window
npm install hardhat-gas-reporter --save-dev
hardhat.config.js
require("hardhat-gas-reporter");
module.exports = {
gasReporter: {
enabled: true,
currency: 'USD',
coinmarketcap: 'YOUR_API_KEY',
}
};

运行测试后自动生成 Gas 消耗报告:

·-------------------------------------|---------------------------|-------------|----------------------------·
| Solidity and EVM gas reporting · Gas price: 20 gwei/gas · · 1 eth = 1800.00 USD │
··············|···················|···|·········|·········|·········|·············|··············|············
| Contract · Method · · Min · Max · Avg · # calls · usd (avg) │
··············|···················|···|·········|·········|·········|·············|··············|············
| Token · transfer · · 51832 · 51832 · 51832 · 5 · $1.87 │
··············|···················|···|·········|·········|·········|·············|··············|············

Gas 费用查询工具

  • ETH Gas Station (ethgasstation.info):查询当前 Gas 价格
  • Blocknative Gas Estimator:实时 Gas 费用估算
  • Etherscan Gas Tracker:区块链浏览器的 Gas 追踪功能

ETH 通缩机制

EIP-1559 引入的 Base Fee 销毁机制在网络繁忙时会使 ETH 变为通缩资产。

ETH 净发行量 = 区块奖励 + 质押奖励 - 销毁的 Base Fee

当网络活跃时,销毁量可能超过发行量,形成通缩。这也是”超声货币”(Ultrasound Money)论点的基础。

总结

理解 Gas 机制对于以太坊开发至关重要:

  1. Gas 是计算工作量的度量单位
  2. EIP-1559 引入了 Base Fee(销毁)+ Priority Fee(给验证者)的双层机制
  3. Gas 优化主要从减少 storage 操作、合理使用数据类型、减少链上数据等方面入手
  4. 使用工具(hardhat-gas-reporter、tenderly)可以有效分析和优化 Gas 消耗