智能合约部署成本详解:六大 Gas 组成部分

拆解合约创建的全部 Gas 来源:基础费、创建费、存储初始化、calldata、初始化执行与代码存储

5 分钟阅读
智能合约部署成本详解:六大 Gas 组成部分

智能合约部署成本详解:六大 Gas 组成部分

目录


一、概览:部署到底花多少钱

智能合约创建成本可以从 $10 到 $2,000 不等(假设 ETH 在 $1,500–$2,000)。

主要影响因素:ETH 价格、编译后合约大小、当前网络 Gas 价格。理解成本构成,才能针对性优化(呼应 Module 9 的部署优化技巧)。

二、部署 Gas 的六大组成部分

#组成Gas
1基础交易费21,000(所有以太坊交易的固定底费)
2合约创建费32,000(创建新合约的固定成本)
3存储变量初始化22,100 / 个(构造函数里设置的每个存储变量,零→非零写入)
4Calldata 成本零字节 4 gas、非零字节 16 gas(部署交易的 data 即创建码)
5初始化代码执行可变(取决于构造函数执行了哪些操作码)
6代码存储成本200 / 字节(部署的运行时字节码,code deposit cost)

要点:

  • 第 1、2 项是固定的 53,000 gas 起步;
  • 第 3 项最贵——每个在构造函数里初始化的存储变量都是 22,100 gas(这就是 Module 9 强调”避免零到一写入”的原因);
  • 第 4、6 项与合约大小成正比——合约越大、calldata 越长、运行时字节码越多,越贵;
  • 第 6 项也解释了为何元数据那 51 字节要 10,600 gas(51 × 200)。

三、完整计算示例

一个极简 Solidity 合约的部署:

组成计算Gas
基础部署固定21,000
合约创建固定32,000
Calldata75 非零 × 16 + 5 零 × 41,220
运行时字节码存储63 字节 × 20012,600
初始化执行构造函数操作码42
合计66,862

(注意这个例子没有存储变量初始化,否则每个变量再 +22,100。)

四、Gas 转美元公式

美元成本 = gas × gas单价(gwei) × ETH价格 ÷ 10亿

例:ETH $1,000、Gas 价 20 gwei,66,862 gas:

66,862 × 20 × 1000 ÷ 1,000,000,000 ≈ $1.34

五、初始化代码 vs 运行时代码

这是部署的核心二分(下一篇《合约创建码》会深入):

  • 初始化代码(initialization/init code):部署时执行一次,负责设置存储、把运行时代码返回给 EVM——执行完就丢弃,对应第 5 项成本;
  • 运行时代码(runtime code):永久存储上链的字节码,被外部调用执行——对应第 6 项的 200 gas/字节存储成本。

两者有各自独立的 Gas 计费机制。calldata(第 4 项)装的是”init code + runtime code + 构造参数”整体。

六、如何降低部署成本

结合六大组成针对性优化:

  • 减少存储初始化(第 3 项):能用 immutable/constant 就不用存储变量(嵌入字节码,不触发 22,100);
  • 减小合约体积(第 4、6 项):用自定义错误代替字符串、移除元数据(--no-cbor-metadata)、用 Clone/Metaproxy 代替完整部署;
  • 构造函数设为 payable:省去隐式 require(msg.value==0),省约 200 gas;
  • 优化 IPFS 哈希多零字节:降低 calldata 成本。

七、总结

  • 部署成本 = 21,000(基础)+ 32,000(创建)+ 22,100×存储变量 + calldata(16/4 每字节)+ 初始化执行 + 200×运行时字节数;
  • 固定起步 53,000 gas,存储初始化是最贵的可变项;
  • 合约越大(calldata + 运行时字节码)越贵;
  • 区分一次性的 init code 和永久的 runtime code,各自计费;
  • 降本靠:少存储变量、小体积、payable 构造、去元数据、用代理。

八、动手练习项目:部署成本估算器 DeployCostEstimator

项目目标

写一个能预估任意合约部署 Gas 的工具,把六大组成逐项算出来,并通过实际部署不同合约验证估算准确度。部署到 Sepolia。

合约/脚本要求

1. 准备一组对照合约

  • Empty.sol:空合约(只有编译器默认);
  • OneStorageVar.sol:构造函数初始化 1 个存储变量;
  • ThreeStorageVars.sol:初始化 3 个存储变量;
  • WithImmutable.sol:用 immutable 代替存储变量(对照第 3 项节省);
  • Large.sol:含大量代码撑大运行时字节码。

2. CostEstimator(链下 Foundry script 或 JS)

  • 读取每个合约的 creationCodetype(C).creationCode)和 runtimeCodetype(C).runtimeCode);
  • 计算:
    • calldata 成本 = 遍历 creationCode 字节,零字节 ×4 + 非零 ×16;
    • 运行时存储 = runtimeCode.length × 200
    • 固定 = 21,000 + 32,000;
    • 存储初始化 = 已知变量数 × 22,100;
    • 估算总 = 上述之和 + 一个初始化执行估计值;
  • 输出估算表。

测试要求(Foundry)

  1. test_EstimateMatchesActual:用 vm.startBroadcast/部署计量真实 Gas,对比估算值,误差在合理范围(初始化执行那一项较难精确,允许偏差);
  2. test_StorageVarCost:OneStorageVar 比 Empty 多约 22,100 gas;
  3. test_ImmutableCheaper:WithImmutable 比 OneStorageVar 部署便宜(省了存储初始化);
  4. test_RuntimeSizeCost:Large 比 Empty 多的 Gas ≈ 字节差 × 200;
  5. test_CalldataCost:手动数 creationCode 的零/非零字节,验证 calldata 成本计算;
  6. test_PayableConstructorSaves:payable vs 非 payable 构造函数对比,省约 200 gas。

Sepolia 部署与验证步骤

  1. 依次部署上述对照合约,在 Etherscan 上记录每笔的实际部署 Gas;
  2. 用 CostEstimator 估算同样的合约;
  3. 制表对比”估算 vs 实际”,分析偏差主要来自哪一项(通常是初始化执行);
  4. 验证 immutable 版本确实省下约 22,100 gas/变量。

进阶挑战(可选)

  • 加入 EIP-3860 的 initcode 成本(每字节 initcode 额外 2 gas),让估算更精确;
  • 写注释回答:为什么存储变量初始化是 22,100 而不是 20,000?这 22,100 = 20,000(SSTORE 写)+ 2,100(冷访问),结合 Module 9 解释。

💬 评论