如何优化 Solidity 智能合约中的燃气费
本指南提供了一个实用的分步指南,介绍如何在 Solidity 中编写智能合约时优化气体成本。
为什么气体优化很重要?
气体优化是智能合约开发的关键部分。 它有助于确保智能合约即使在网络高度拥堵的情况下也能保持高效和成本效益。 通过 减少合约执行的计算开销,开发人员可以降低交易费用,加快确认时间,并提高其 dApp 的整体可扩展性。
对于开发人员来说,气体优化就是编写简洁、安全和可预测的代码,尽量减少不必要的计算。 对于用户来说,这关系到确保他们能在不支付过高费用的情况下与您的合同进行互动。
为什么对 Kaia 尤为重要
目前,Kaia 区块链已推出超过 83 款 Mini dApp,成为交易量领先的 EVM 兼容链,这主要得益于这些链上应用的爆炸式增长。
每个 Mini dApp 都依赖智能合约来执行链上操作。 无论是铸造物品、下注还是管理游戏中的资产,每一次合同互动都会消耗气体。 如果不进行优化,这些应用程序很快就会变得过于昂贵,用户无法与之互动,尤其是在大规模互动时。
这就是为什么燃气效率不仅仅是一个 "可有可无 "的东西。 这是必须的。 在 Kaia 基础上开发的开发人员必须确保对每个函数调用进行优化,以最大限度地降低成本,同时保持功能性和安全性。
气体优化技术
存储包装
在区块链上存储和检索数据是最耗气的操作之一,尤其是当数据必须跨交易和区块持续存在时。 在 Solidity 中,这些数据存储在合同存储器中,这是永久性的,会产生气体成本。 为了降低这些成本,开发人员必须仔细优化存储的使用方式,尤其是在声明状态变量时。
Kaia 虚拟机(KVM)将合同数据存储在称为存储槽的单元中。 每个存储槽可容纳 256 位(32 字节)数据。 Solidity 数据类型有不同大小,例如,bool 为 1 字节,地址为 20 字节。
通过一种称为 "存储打包 "的技术,我们可以将较小的变量紧密地排列在一个 32 字节的存储槽中。 这有助于减少气体用量,因为从一个存储槽读取或写入气体的成本要比访问多个存储槽低得多。
让我们来看看下面这个例子:

分解:
在未优化版本(SlotUnOptimized)中,Solidity 会这样存储结构:
- 地址至 -> 占用 20 个字节 -> 存储在 0 号插槽中
- uint256 numConfirmations -> 占用 32 个字节 -> 保存在 1 号插槽中
- uint80 值(10 个字节)和执行的 bool(1 个字节) -> 存储在第 2 个插槽中
尽管value和executed变量很小,但由于对齐填充的原因,除非明确地重新排序,否则Solidity会将它们放在各自的存储槽中。 因此,该结构使用 3 个储气槽,这意味着储气操作的天然气成本是原来的 3 倍。 但是,"地址(20 字节)+ uint80(10 字节)+ bool(1 字节)"的总大小为 31字节,在单槽的 32字节限制之内。 只需对声明重新排序,将较小的变量集中在一起,Solidity 就能将它们打包到同一个槽中。 这就是仓储包装的精髓。
如上图所示,在优化版本(SlotOptimized)中,所有较小的变量都相邻放置,这样编译器就能将它们存储在较少的槽中,从而降低部署和运行时的气体成本。
缓存存储
除了变量在存储槽中的布局外,了解与访问和修改存储相关的气体成本也很重要。
Kaia 虚拟机上每个存储插槽的成本: 初始化(首次写入)需要 20,000 加仑 更新(后续写入)需要 5,000 加仑 因此,尽量减少直接读取和写入存储空间的次数至关重要,尤其是在频繁调用的函数中。 一种有效的模式是,当需要在函数中多次访问存储变量时,将其缓存到内存中。
让我们来看看下面这个例子:

避免将变量初始化为默认值
在 Solidity 中,每种数据类型都有一个预定义的默认值。 例如,address 默认为 address(0),bool 默认为 false,uint 默认为 0。 如果开发人员在变量声明时明确指定这些默认值,例如写入 bool isActive = false 或 uint total = 0,就会出现常见的低效情况。
虽然这在功能上是正确的,但在部署过程中会带来不必要的气体成本,因为 Solidity 已经默认设置了这些值。 通过声明状态变量而不赋值,可以减少合约字节码的大小,避免额外的存储操作。 这一小小的调整有助于提高智能合约的效率,使其更易于维护,尤其是在处理多个变量时。
请看下面的例子:

最小化链上数据
我们清楚地知道,交易的大部分气体成本都来自于合同存储中的数据。 最好经常询问哪些数据需要存储在链上或链下,并考虑这两种选择的利弊。 我们可以从完全链上 NFT 的案例中看到这一点,以及与传统的链下元数据 NFT 相比,它们有多么昂贵。 这意味着,通过在链外存储信息,你可以大大减少智能合约的耗气量,这只是因为你分配给存储的变量较少。
释放闲置存储空间
有时,我们会忘记释放合同中未使用的数据,这无形中有时会增加燃气成本,也会导致网络膨胀。 无论如何,释放未使用的存储空间非常简单,只要确定不再使用该存储空间,就可以将其值设置为 0。 您还可以使用 solidity 中的特殊关键字 delete 来释放任何数据类型。
请看下面的例子:
