什么是 Gas 优化技巧?
Gas 优化技巧是指用于减少加密货币交易或智能合约所需计算资源和区块链存储的各种方法。在 Ethereum 和其他兼容 EVM 的网络上,Ethereum Virtual Machine 执行的每项操作都有 Gas 成本。读取合约存储、变更区块链状态、复制数据、调用其他合约、发出事件以及部署字节码,都可能消耗 Gas。提交交易的用户通常需要根据计费 Gas 和交易的实际 Gas 价格支付费用。基本公式为:交易费 = 计费 Gas × 实际 Gas 价格。Gas 优化会减少该公式中的计费 Gas 部分。它不会直接控制每个 Gas 单位的市场价格,后者会随区块空间需求变化。经过良好优化的合约可以降低用户成本、提高每个区块可容纳的活动量、提升交易可靠性,并让去中心化应用在网络需求旺盛时更实用。不过,Gas 优化绝不能削弱访问控制、算术安全性、重入保护、记账准确性或其他安全属性。最好的优化通常是简化设计、减少工作量,而不是为了节省少量 Gas 编写复杂代码。Gas 优化为何对加密货币领域很重要
每项不必要的链上操作,都会给提交交易的个人或应用带来成本。单笔交易的微小差异可能看似无关紧要,但智能合约处理数千甚至数百万次交互时,这种差异就会变得显著。降低 Gas 使用量可以让代币转账、去中心化金融操作、NFT 活动、治理投票和区块链游戏更加实惠。高效合约也更便于验证者和节点处理,因为其所需的存储读取、写入和计算步骤更少。对于会执行重复循环、更新多个余额、创建新合约或在一笔交易中调用多个外部系统的应用,Gas 优化尤为重要。它对自动化加密货币策略也很重要,因为较高的执行成本可能会抵消交易或强制平仓的预期利润。Gas 优化无法保证交易的本国货币费用较低,因为 Gas 价格和原生加密货币的市场价格仍可能上涨。它减少的是用户需要为之付费的网络工作量。了解 Gas 的消耗位置
更改智能合约代码前,开发者应先了解 Gas 消耗的主要来源。持久存储通常是成本最高的资源之一,因为生成的数据必须保留在区块链状态中。内存是单次执行期间使用的临时数据,通常比持久存储便宜,但其成本会随分配的内存区域扩大而上升。Calldata 包含交易或外部函数的输入;除非复制,否则不会变成可修改的合约内存。合约部署需要消耗 Gas 来执行初始化代码并存储生成的运行时字节码。外部合约调用会带来账户访问、参数编码、返回数据处理以及被调用合约执行所产生的额外成本。密码学计算、事件日志、合约创建和大型循环也可能显著增加总 Gas 使用量。Ethereum 官方Gas 文档介绍了 Gas 如何衡量交易和 EVM 操作所需的计算工作量。
优化前先测量 Gas
Gas 优化应从测量开始,而不是依赖假设。开发者应在更改代码前记录部署成本和重要函数的运行成本。测试应覆盖符合实际情况的交易规模、正常用户行为、最大支持输入、失败路径和少见的区块链状态。包含 1 个数组元素的函数可能成本很低,但处理 1,000 个元素时可能变得不安全或费用过高。Gas 快照可以比较不同代码版本的函数成本,并发现意外的费用增加。执行跟踪可以显示哪些操作码、存储槽、内部调用和外部调用消耗的 Gas 最多。Solidity 编译器可以生成 Gas 估算值,详情见其编译器文档;但如果执行结果取决于动态循环或外部合约行为,部分估算值可能无法提供或不够可靠。
在本地区块链或持续维护的公共测试网上进行测试,可以得到更贴近实际的测量结果。开发者应优化用户实际执行的高成本路径,而不是在很少调用的代码上耗费时间。使用当前版本的 Solidity 编译器
较新的 Solidity 编译器版本通常会改进代码生成、修正优化问题、支持新的 EVM 指令并修复安全漏洞。Solidity 官方网站显示,2026 年 7 月最新发布的编译器版本为 0.8.36。选择用于生产环境的编译器前,开发者应查看Solidity 官方网站和已知编译器错误列表。
应在构建配置中固定所选版本,以便每位开发者、测试系统和部署流水线都生成一致的字节码。如果使用宽泛的版本指示符,却不控制实际构建环境,不同编译器版本可能会生成不同代码。即使使用较新的编译器,仍应仔细测试,因为优化和语言变更可能影响字节码、部署成本和合约行为。启用并配置 Solidity 优化器
Solidity 优化器可以简化表达式、移除不必要的操作、合并重复计算,并降低部署字节码和运行时 Gas 消耗。Solidity 优化器文档说明,优化可以缩小代码并降低执行成本。
优化器的 runs 设置表示开发者预期已部署代码会被执行的频率。较低的 runs 值通常更侧重于缩小部署字节码。较高的 runs 值通常允许编译器生成更大的部署代码,但可能降低重复运行时调用的成本。不存在适用于所有加密货币合约的通用最佳 runs 值。部署次数很多但很少调用的合约,与预计要处理数百万笔交易的合约,可能适合不同的设置。开发者应使用真实测试比较多种配置,并评估整个生命周期的总成本,而不只关注部署或运行成本。via-IR 编译流程可能采用与旧流程不同的优化方式,但切换编译流程应视为重要的构建变更,并进行完整测试。减少持久存储写入
持久存储写入通常是节省 Gas 的最大机会之一。如果某项信息可以根据现有状态安全计算,或在需要时由用户提供,合约就应避免存储该信息。如果合约可以先确认无需变更,就应避免反复写入相同数值。更新 5 个独立状态变量的合约,可能比采用写入更少的简化记账模型更昂贵。开发者还应考虑其他智能合约是否需要获取这些信息,还是只有链下应用需要获取。会影响未来合约执行的数据通常应存储在链上。如果信息仅供钱包、界面或分析工具使用,有时可以改为记录在事件中。按照当前 Ethereum 规则,删除存储可以获得有限的 Gas 退款,但退款有上限,不应将其视为让存储变成免费的方法。移除过时状态应基于合理的设计考虑,而不应为了以后获得退款而创建不必要的数据。缓存重复的存储读取
如果底层状态在多次读取期间无需变化,读取一次存储槽并重复使用本地值,比反复读取该槽更省 Gas。根据 EIP-2929,Ethereum 区分冷状态访问和热状态访问。
交易中首次访问之前未触及的存储槽属于冷访问,费用较高。同一笔交易中再次访问该存储槽时属于热访问,费用较低。即便是热存储读取,其成本也可能高于使用函数中已有的堆栈值或内存值。如果某个状态变量经常被使用,函数通常可以先将其复制到局部变量中进行计算,再将最终结果写回一次。如果外部调用可能改变相关状态,或缓存值会因此过时,就不能使用这种方法。开发者应遵循“检查-生效-交互”原则,并在外部调用前后缓存状态时仔细分析重入风险。谨慎打包状态变量
Ethereum 存储按 32 字节的槽组织。如果变量的排序和总大小允许,多个较小的值类型变量可以共用一个槽。例如,多个较小的整数或布尔值可能放在同一个槽中,而不必各占一个槽。Solidity 存储布局文档说明了变量、结构体、数组、继承和打包方式如何影响槽位分配。
因此,变量顺序可能改变合约所需的槽数量。不过,使用尽可能小的整数不一定更便宜。EVM 通常使用 256 位字执行算术运算,因此较小的整数类型可能需要额外的掩码或转换操作。多个打包值经常一起读取或写入时,打包的优势最明显。只更新打包槽中的一个值时,可能需要先读取槽、修改字中的一部分,再将组合后的值写回。存储布局也是可升级合约兼容性的一部分,因此部署后不应仅为了节省 Gas 而重新排列现有状态变量。使用 constant 和 immutable 变量
如果某个值在编译后永不改变,且 Solidity 允许,通常应将其声明为
如果某个值在合约构造时确定、之后固定不变,可以声明为
官方 Solidity 合约文档介绍了这两类变量与普通状态变量的区别。
常见候选值包括固定配置、永久地址、数学常量以及部署后无法更改的限制值。如果应用确实需要治理流程或其他授权流程更新某个变量,就不应将其设为不可变。仅为节省 Gas 而取消必要的灵活性,可能会在之后造成更大的运维问题。只读外部输入使用 Calldata
无需修改的外部函数参数通常可以使用
Solidity 数据位置指南建议尽可能使用 calldata,因为这样可以避免复制并防止修改。
该技巧尤其适用于传入外部函数的数组、字节序列、字符串和结构体。如果合约需要修改数据或构建新的内存值,使用 memory 仍然合适。开发者不应认为将函数参数从
尽量精简交易 Calldata
交易 Calldata 中的每个字节都会增加交易内在成本。不必要的数组、重复字段、冗长文本字符串和低效编码都会增加用户交易成本。开发者有时可以使用简短标识符替代重复的完整数值,再由合约映射到已存储的配置。如果合约可以从现有状态或交易发送方安全推导某项信息,也可以避免发送该信息。压缩对特定系统可能有帮助,但解压本身也会消耗 Gas,可能抵消预期节省。Pectra 已启用 EIP-7623,为数据密集型交易引入了更高的 Gas 下限。
因此,对于携带大量数据但 EVM 执行量相对较少的交易,Calldata 效率尤为重要。开发者应比较精简 Calldata 的成本与解码所需的额外执行成本。将瞬态存储用于仅限单笔交易的状态
瞬态存储允许合约在一笔交易中的内部调用之间保存可用值,并在交易结束时自动清除。Ethereum 通过 EIP-1153 引入了
Solidity 瞬态存储文档提醒开发者,瞬态状态仅在单笔交易期间有效,并且在不同调用类型下具有特定的所有权行为。
在新实现经过充分测试和审计前,不应以瞬态存储替换经过验证的安全机制。使用自定义错误,而不是冗长的回滚字符串
自定义错误可以高效返回结构化的失败信息。冗长的回滚字符串会写入已部署合约的字节码,并在条件失败时生成编码后的返回数据。自定义错误使用 4 字节选择器及其声明的参数,可以减少部署大小和失败路径成本。Solidity 自定义错误文档介绍了自定义错误这一便捷且节省 Gas 的错误处理机制。
错误名称和参数仍应足够清晰,便于钱包、开发者和监控系统诊断问题。删除所有有用的错误信息或许能节省少量 Gas,却会让失败的加密货币交易更难排查。优化循环和数组处理
当循环次数取决于可能无限增长的数组或存储集合时,循环可能变得非常昂贵。无界循环最终可能需要超过单笔交易可提供的 Gas,导致重要合约函数无法使用。如果可行,应将大型工作量拆分为有界批次。如果循环反复从存储读取数组长度,有时可以将长度缓存在局部变量中。在安全的前提下,每次迭代使用的存储值也可以缓存。只有能够证明溢出不可能发生时,开发者才可以对循环计数器使用
避免重复的外部调用
调用其他合约会消耗 Gas,并使交易受被调用合约的执行行为影响。如果可以安全调用一次并重复使用结果,函数就应避免反复请求相同的外部值。有时可以将多个相关操作合并为一次合约调用。不过,批处理会增加可能一起失败的工作量,也可能扩大 Calldata 载荷。外部调用还可能引发重入、返回意外数据、消耗大量 Gas,或导致整笔交易回滚。绝不能为了节省 Gas 而移除返回值检查或安全调用处理。开发者应判断缓存的外部值在整个执行期间是否仍然有效,以及外部系统是否可能通过回调改变该值。适当情况下使用事件代替存储
事件会将信息记录在交易日志中,链下应用可以对这些信息编制索引。与将等量信息存入合约状态相比,记录事件通常成本更低。事件适用于活动历史、用户界面通知、分析数据,以及未来智能合约执行无需读取的信息。事件不能替代决定余额、所有权、权限、债务、抵押品或其他合约行为的状态。智能合约通常无法在 EVM 执行期间检索历史日志。如果系统只将关键记账信息存入事件,就必须依赖链下服务重建信息,且无法直接在链上执行相关约束。事件参数也应谨慎设计,因为索引主题和日志数据的 Gas 成本及搜索特性不同。缩小合约部署体积
合约部署需要为执行初始化逻辑和存储运行时字节码支付 Gas。未使用的函数、重复逻辑、冗长回滚字符串和嵌入数据都会增加部署成本。开发者应移除无效代码;如果大型库只有一小部分会用到,也应避免整库引入。外部库可以减少多个部署之间的重复字节码,但调用库可能增加运行时开销并带来运维依赖。如果需要部署多个相似的合约实例,工厂和克隆模式可以降低部署成本,因为它们能够共享实现代码。代理模式可以支持升级和重复部署,但会增加委托调用开销,并带来严重的存储布局、治理和安全问题。Ethereum 根据 EIP-170 限制普通已部署运行时代码的大小;EIP-3860 则为初始化代码计量并设定上限。
仅为满足大小限制而拆分代码可能增加调用成本,因此应从合约整个生命周期的角度评估架构。设置正确的 EVM 版本
编译器的输出取决于所选 EVM 目标版本。面向较新 EVM 编译的代码可能使用旧网络不支持的指令。例如,Shanghai 通过 EIP-3855 引入了
Dencun 通过 EIP-5656 引入了
仅在经过测试后考虑使用访问列表
访问列表允许交易列出预计会访问的账户和存储密钥。执行开始时,列表中的项目会被视为热访问,从而降低后续冷访问费用。EIP-2930 会对访问列表中包含的每个地址和存储密钥收费。
对于某些可预测的交易,准确的访问列表可以降低成本。如果列表不完整或没有必要,用户可能会为收益太小或实际未使用的条目付费,反而增加总费用。如果确切执行路径取决于变化中的区块链状态,生成访问列表可能很困难。开发者应分别模拟带有和不带访问列表的完整交易,而不要假定预加载状态总是更便宜。谨慎批处理用户操作
将多个操作合并到一笔交易中,可以避免多次支付基础交易开销。例如,应用可以让用户在一个协调流程中完成操作授权和实际操作。Pectra 已启用 EIP-7702,允许外部拥有账户将执行委托给智能合约代码,并支持批处理和 Gas 代付等钱包功能。
批处理不会自动降低执行的所有成本,因为每个操作仍须执行必要的合约工作。大型批次可能超过实际可用的 Gas 限制、增加 Calldata,或在某一步失败时导致所有操作回滚。应用应明确是否允许部分完成,以及如何报告失败的子操作。应仔细审核钱包授权和委托代码,因为批处理系统可能获得对用户资产的广泛控制权。适当情况下使用 Layer 2 网络
应用层优化可以减少消耗的 Gas 单位数量,而 Layer 2 网络可以降低用户为执行和数据支付的价格。Rollup 会在 Ethereum 主网之外处理活动,并将数据或证明发布回 Ethereum。Dencun 通过 EIP-4844 引入了携带 Blob 的交易,为 Rollup 提供临时且更专用的数据通道。此后,Pectra 和 Fusaka 进一步扩展了 Ethereum 的扩容和数据可用性能力。当前的 Ethereum 开发者指南介绍了近期升级如何改变主网费用、账户功能和 Rollup 经济模型。
部署到 Layer 2 网络并不意味着可以不再优化合约,因为不必要的存储和计算仍会增加用户费用并占用网络容量。选择网络前,开发者还应评估跨链桥、提现、流动性、安全性和互操作性要求。仅在必要时使用内联汇编
内联汇编可以让开发者精确控制 EVM 操作,有时还能降低 Gas。但它也会移除或绕过 Solidity 的一些类型检查、内存保护和高级安全功能。审查者可能难以理解简短的汇编例程,而这类代码可能造成内存损坏、编码错误或不安全的调用行为。编译器可能已经能将普通 Solidity 优化为与手动实现同样高效的代码。因此,汇编仅应用于经过测量确认的性能瓶颈、特定密码学操作、底层数据处理或 Solidity 无法高效表达的功能。每项汇编优化都应配备测试,并将其输出与清晰的参考实现进行比较。如果汇编涉及代币余额、签名、权限或外部调用,独立安全审查尤为重要。不要为了节省 Gas 而牺牲安全性
如果合约可能导致用户加密货币损失,那么再便宜也没有意义。开发者不应仅为节省 Gas 而移除访问控制检查、输入验证、重入保护、截止时间验证、滑点保护或记账检查。只有在所有可达条件下都能从数学上证明不会发生溢出或下溢时,才应使用未检查算术。将多个变量压缩到一个数值中可以节省存储,但会增加位操作错误的可能性。使用代理可以降低部署成本,但会带来升级权限和存储冲突风险。链下签名可以减少交易数量,但需要正确处理 Nonce、有效期、域分隔和重放保护。除 Gas 成本外,还应评估每项优化的可读性、可审计性、运维风险和后续维护需求。常见的 Gas 优化错误
一种常见错误是选择较小的整数类型,却没有将其与其他值打包。EVM 使用 256 位字运算,因此单独使用较小类型本身可能不会降低运行时成本。另一种错误是将所有 Calldata 复制到内存,即使函数只需读取数据。第三种错误是为了追求退款而清空存储,却计划之后重新创建相同状态。第四种错误是在不断增长的存储数组上使用无界循环。第五种错误是没有比较部署和运行时表现,就选择较高的优化器 runs 值。第六种错误是在对合约进行性能分析之前就手写汇编。第七种错误是认为编译器或网络升级改变操作码行为后,原有优化仍然有效。第八种错误是只测量成功交易,却忽略成本高昂的回滚路径。第九种错误是将更低的 Gas 估算误认为两种实现的安全性和业务逻辑完全相同。如何建立 Gas 优化流程
可靠的流程应从明确合约所需行为和安全属性开始。团队应先创建功能测试,再进行底层优化。然后测量部署成本和每种重要交易路径的 Gas 使用量。通过性能分析找出成本最高的存储写入、循环、Calldata 输入和外部调用。开发者可以简化架构、减少状态变更、选择合适的数据位置,并启用经过测试的编译器优化设置。每项变更都应与原始版本进行测量比较,并审查是否带来新的安全风险。项目应保留 Gas 回归测试,防止后续开发悄然抹去已取得的节省。最终生产字节码应使用固定版本的编译器、经过验证的配置、正确的 EVM 目标和经过审查的依赖版本进行编译。即使高价值加密货币合约通过内部测试,仍应接受独立安全审计。常见问题
哪种 Gas 优化技巧最好?
最有效的技巧通常是减少持久存储操作,并简化每笔交易执行的链上工作量。Solidity 优化器总能降低 Gas 吗?
不能。某种优化器设置可能降低运行时 Gas,却增加部署成本,因此开发者应测试合约的完整生命周期。优化器 runs 设置是什么意思?
该设置告诉编译器开发者预计运行时代码会执行多少次,并影响部署大小与重复执行成本之间的权衡。存储比内存贵吗?
是。持久存储通常贵得多,因为交易结束后数据仍会保留在区块链状态中。Calldata 比 memory 便宜吗?
对于只读外部输入,Calldata 可能更便宜,因为合约无需先将全部数据复制到内存。较小的整数类型总能节省 Gas 吗?
不能。只有在多个值可以打包到同一存储槽时,它们才最有用;单独使用较小整数可能需要额外的转换操作。开发者为什么要缓存存储值?
如果某个值保持不变且可以安全地从堆栈或内存重复使用,缓存有助于避免重复读取存储。什么是瞬态存储?
瞬态存储是按交易范围生效的 EVM 存储,会自动清除,可用于重入锁等成本较低的临时状态。自定义错误比回滚字符串便宜吗?
通常更省 Gas,因为自定义错误使用简短选择器和编码参数,而不是存储并返回冗长文本。事件可以代替合约存储吗?
只有未来智能合约执行不需要读取或执行约束的信息,才可以用事件代替存储。删除存储会退还全部 Gas 成本吗?
不会。清除存储的退款有限,并受协议级 Gas 退款上限约束。开发者应使用 unchecked 算术来节省 Gas 吗?
只有在能够证明不会溢出或下溢,并且安全依据已有记录和测试时,才应使用 unchecked 算术。批处理交易总能节省 Gas 吗?
不能。批处理可以减少重复交易开销,但也可能增加 Calldata、执行复杂度和“一处失败、全部回滚”的风险。Layer 2 网络可以代替合约优化吗?
不能。较低的网络费用可以降低用户成本,但在同一网络上,低效合约仍会浪费执行容量,成本也高于优化后的合约。访问列表总能降低 Gas 吗?
不能。访问列表条目本身也有成本;条目不准确或不必要时,反而会增加费用。内联汇编总能减少 Gas 吗?
不能。编译器可能生成同样高效的代码,而不安全的汇编可能带来严重的安全和维护问题。开发者如何测量 Gas 使用量?
可以使用编译器估算、自动化 Gas 快照、本地交易测试、执行跟踪和性能分析工具。网络升级后,Gas 成本为什么可能变化?
Ethereum 升级可能增加操作码、重新定价现有操作、调整 Calldata 规则,或引入更高效的存储和内存功能。Gas 优化能避免网络费用高涨吗?
优化可以减少 Gas 使用量,但用户争夺区块空间时,无法阻止市场 Gas 价格上涨。Gas 最低的合约总是最好的吗?
不是。正确性、安全性、可读性和可维护性比小幅节省 Gas 更重要。结语
Gas 优化技巧可以减少加密货币智能合约所需的计算、存储、Calldata 和部署资源。节省效果最大之处通常包括减少持久存储写入、安全地缓存存储读取、选择正确的数据位置、减少不必要的 Calldata,以及避免重复的外部调用。现代 Solidity 编译器可以自动优化字节码,但应通过贴近实际的测量来选择优化器设置,而不是凭猜测决定。瞬态存储、
您可能也喜欢
反恐融资(CTF)
框架
波动性爆发
热门
目前热门备受市场关注的加密货币
最高成交量
按交易量计算交易量最大的加密货币

