什么是 Gas Golfing?
Gas golfing 是指对智能合约代码进行小幅且有针对性的修改,以降低其 Gas 消耗。这个术语源自 code golf(一种编程挑战),开发者需要尝试用尽可能少的源代码解决问题。在加密货币开发中,目标不一定是减少字符数量。目标是在保留合约所需行为的同时,生成消耗更少区块链资源的字节码。Gas golfing 最常见于 Ethereum,以及其他通过 Ethereum Virtual Machine 执行智能合约的网络。开发者可以通过减少存储操作、简化计算、缩短回滚数据、避免不必要的内存复制,或选择成本更低的 EVM 指令来优化合约的 Gas 消耗。Gas golfing 通常侧重微优化,而非彻底重新设计应用。因此,它的范围比一般的 Gas 优化更窄。后者还可能涉及合约架构、交易批处理、部署到 Layer 2、应用设计和链下计算。成功的 Gas golfing 修改必须保留安全性、正确性、可读性和兼容性。如果一项修改引入漏洞,或导致合约无法安全维护,那么即使节省少量 Gas 也不值得。为什么叫 Gas Golfing?
“Golfing”指的是努力获得更低分数。传统高尔夫中,击球次数越少越好。在智能合约 Gas golfing 中,如果优化后的代码能安全地完成相同任务,通常认为 Gas 消耗越低越好。开发者可以通过测量部署 Gas、运行时 Gas、字节码大小,或合约预期生命周期内的总成本,来比较两种实现方式。Gas-golfing 挑战可能会要求参与者用尽可能少的 Gas 实现某项功能。但实际生产开发更复杂,因为 Gas 消耗最低的方案不一定最安全,也不一定最容易审计。这个术语比较非正式,并不代表 Ethereum 协议、代币、交易类型或官方编程标准。Gas Golfing 如何运作
Ethereum 会为 Ethereum Virtual Machine 执行的每项操作设定 Gas 成本。官方 Ethereum Gas 文档说明,Gas 用于衡量执行交易和智能合约操作所需的计算工作量。
算术运算、内存使用、持久化存储、事件日志、合约调用、合约创建和交易数据都会消耗 Gas。Gas golfing 会寻找成本更低、但能产生相同预期结果的操作序列。例如,开发者可以将多次存储读取改为读取一次,并在局部变量中复用已加载的值。另一种做法是用简短的自定义错误替代冗长的回滚字符串。在更底层工作的开发者,可以用专门用于内存复制的 EVM 指令,替换手动构建的内存复制循环。必须在编译后的字节码上测量最终优化效果,因为 Solidity 代码看起来更短,并不一定意味着 EVM 执行成本更低。Gas Golfing 与 Gas 优化
Gas 优化是降低智能合约部署和执行成本的广义过程。Gas golfing 是范围更窄的一类优化,通常强调代码层面的微小节省。完整的 Gas 优化项目可能会重新设计应用的存储模型、将适合的计算移至链下、使用 Layer 2 网络,或减少用户需要提交的交易数量。Gas golfing 则可能比较两种循环结构、两种错误处理方式,或两种加载同一数值的方法。架构优化通常比微优化带来更大的节省。不过,如果某个函数被执行数百万次,每次节省少量 Gas 也可能累积成可观的节省。专业开发者应先从架构层面去除不必要的工作,再对剩余的重要执行路径进行 Gas golfing。Gas Golfing 与 Code Golf
Code golf 旨在缩短源代码,通常不会优先考虑可读性或长期维护。Gas golfing 旨在降低区块链执行成本。最短的 Solidity 表达式,编译后可能比更长、更清晰的表达式包含更多指令。编译器优化也可能将多种不同的源代码风格转换成完全相同的字节码。因此,开发者应比较编译结果并测量 Gas,而不是统计源代码字符数。Code-golf 方案可能很有趣,但不适合用于控制有价值加密货币的合约。生产环境中的 Gas golfing 始终应包括安全测试、文档和审核。Gas Golfing 与降低 Gas 价格
Gas golfing 会减少交易消耗的 Gas 单位数量。它不会直接降低每个 Gas 单位的市场价格。交易手续费可表示为:Gas 消耗量 × 实际 Gas 价格。Gas golfing 修改针对的是公式中的 Gas 消耗量部分。网络需求、协议基础费和用户优先费会影响实际 Gas 价格。即使合约经过高度优化,在网络严重拥堵时使用成本仍可能很高。即使 Gas 价格暂时较低,优化不佳的合约也可能仍然浪费资源。开发者为什么要进行 Gas Golfing
Gas golfing 可以降低每位用户与高频调用的智能合约函数交互时所支付的成本。降低交易成本有助于让去中心化金融、代币转账、区块链游戏、NFT 操作和治理系统更容易被用户使用。如果优化减少了初始化工作或运行时字节码大小,也可以降低合约部署成本。自动化加密货币策略可能依赖微小的 Gas 节省,因为交易手续费会直接降低其预期回报。需要反复执行大量内部操作的协议,即使每次优化幅度很小,也可能节省大量成本。降低 Gas 使用量还可以为其他交易留出更多区块容量。不过,优化应基于预期使用情况,而不是为了在不切实际的测试场景中取得尽可能低的基准数据。进行 Gas Golfing 前先测量
开发者应在尝试微优化前测量 Gas 使用量。现有编译器可能已经移除了看似不必要的操作。在较旧 Solidity 版本中能降低 Gas 的修改,在当前编译器中可能毫无收益。另一些修改可能降低运行时 Gas,却提高合约部署成本。Gas 测试应涵盖用户最常执行的函数。测试还应涵盖最小输入、最大支持输入、成功执行路径和失败路径。Solidity 编译器文档介绍了编译器输出选项和 Gas 估算。
如果循环、外部调用或动态区块链状态会影响执行,编译器估算可能并不完整。因此,开发者应结合编译器输出、实际交易测试和执行追踪进行评估。使用当前版本的 Solidity 编译器
Solidity 编译器会定期发布新版本,增加优化、EVM 支持和语言功能,并修复安全问题。官方 Solidity 0.8.36 发布公告指出,0.8.36 版本是 2026 年 7 月的当前版本。
该版本包含安全修复,并进一步改进了实验性的 SSA 形式代码生成流水线。使用某个编译器版本得出的 Gas-golfing 结果,可能与另一个版本的结果不同。开发者应固定生产构建所使用的编译器版本、优化器配置、EVM 目标和依赖版本。在部署价值较高的加密货币合约前,还应查看官方 Solidity 编译器错误列表。
如果只为降低 Gas 而升级编译器,却不重新测试整个应用,可能会引入意外行为或字节码差异。手动优化前先使用 Solidity 优化器
Solidity 优化器可以简化表达式、删除冗余操作、合并计算并缩小字节码。官方 Solidity 优化器文档说明,优化可以同时降低部署大小和执行成本。
手动 Gas golfing 应考虑优化器已经完成的工作。两段表面上不同的 Solidity 函数,编译后可能得到相同的优化字节码。优化器的 runs 设置会影响部署代码较小与重复执行成本较低之间的权衡。较低的 runs 值通常更重视部署大小。较高的 runs 值通常更重视反复执行时的运行成本。最佳设置取决于合约的部署频率和各函数的调用频率。开发者应比较多个经过测试的配置,而不是不经测量就采用某个传统数值。缓存重复的存储读取
访问持久化存储通常比使用 EVM 堆栈或内存中已有的值更昂贵。一种常见的 Gas-golfing 技巧是将存储值加载到局部变量中一次,然后重复使用。Ethereum 根据 EIP-2929采用冷访问和热访问成本机制。
交易中首次访问某个存储槽通常比之后再次访问同一存储槽更昂贵。如果合约已有正确的局部副本,即使是热存储读取也可以避免。开发者还可以在本地计算最终结果,然后只进行一次存储写入,而不是逐一写入每个中间值。如果外部调用可能在缓存值再次使用前改变相关状态,缓存就不安全。应用这一技巧前,必须考虑重入、回调、委托调用和继承行为。减少存储写入
写入持久化状态是智能合约中常见且成本最高的操作之一。如果要写入的值已经存储,合约应避免重复写入。对于可以根据现有状态安全推导的信息,合约应避免再次记录。在某些情况下,可以用一个累积索引或记账检查点表示多个相关值。函数也可以在本地完成计算,只将最终结果写入存储。与改写一小段算术表达式相比,这些改动可能节省更多 Gas。开发者必须确认,简化后的状态模型仍能正确支持余额、权限、历史记账和升级。打包存储变量
Ethereum 将合约存储划分为 32 字节的槽位。如果变量顺序和总大小允许,Solidity 可以将多个较小的值类型变量打包到一个槽位中。Solidity 存储布局文档介绍了变量的分配和打包方式。
在适用情况下,减少所需存储槽数量可以降低部署和运行时 Gas。单独存储较小的整数类型并不会自动节省 Gas。EVM 通常以 256 位字执行算术,因此较小类型可能需要额外的掩码操作或转换。如果打包的值经常一起访问或修改,打包的效果通常最好。已部署的可升级合约不得重新排列存储变量,否则新布局可能会错误解读已有数据。使用常量和不可变值
普通状态变量在需要从持久化状态读取时会产生存储成本。编译时已知的值可以声明为
在构造时选定、部署后固定不变的值可以声明为
Solidity 合约文档说明,这些值的存储方式与可变状态变量所用的普通存储槽不同。
永久地址、数学常量和固定配置限制都是常见的候选项。如果应用确实需要授权更新流程,就不应将相应的值设为 immutable。为了节省 Gas 而取消必要的配置灵活性,可能造成代价高昂或危险的运营限制。只读输入使用 Calldata
不需要修改的外部函数输入通常可以保留在
Solidity 数据位置文档介绍了 calldata、memory、storage 和 transient storage 之间的区别。
对于函数只读取的动态外部输入,这种技巧最有用。如果合约必须修改该值或构建新的内存数据,则仍需要使用 memory。开发者还应删除不必要的交易输入,因为 calldata 字节会计入内在 Gas 成本。以更紧凑的方式编码数值可以节省 calldata Gas,但比较时也必须计入解码计算成本。使用自定义错误
与冗长的错误字符串相比,自定义错误可以降低合约字节码和回滚数据成本。自定义错误使用简短的 4 字节选择器,并可包含结构化参数。官方 Solidity 自定义错误文档将自定义错误描述为一种节省 Gas 的错误报告方式。
例如,错误可以包含调用者地址和所需余额,而无需将冗长句子嵌入运行时字节码。自定义错误不会让成功执行路径变成零成本,因为合约仍须检查条件。错误信息仍应足够清晰,以便钱包、界面、审计人员和开发者诊断失败的加密货币交易。利用短路求值安排条件顺序
如果 Solidity 布尔表达式的最终结果已经确定,求值可以提前结束。对于使用逻辑 AND 的表达式,第一个 false 条件出现后,求值可以停止。对于使用逻辑 OR 的表达式,第一个 true 条件出现后,求值可以停止。如果某个低成本条件经常能决定结果,将其放在高成本条件之前,可能降低平均 Gas 消耗。使用此技巧时,必须保留预期逻辑行为,并考虑可能回滚或产生副作用的函数相关假设。如果条件顺序会影响安全性或错误报告,不应盲目调整顺序。谨慎优化循环
如果循环处理大量元素,可能消耗很多 Gas。如果集合在循环期间不会改变,开发者可以缓存反复访问的数组长度。也可以缓存每次迭代都会使用的存储值。如果数学上可以证明不会溢出,循环计数器可以使用
只有经过证明才使用 Unchecked 算术
Solidity 0.8 及之后的版本通常会检查整数算术是否溢出或下溢。这些检查会消耗一定 Gas,但能防止一些重要的编程错误。使用瞬态存储保存临时状态
瞬态存储提供仅在单笔交易期间有效的状态,并会在交易结束时自动清除。Ethereum 通过 EIP-1153引入了
使用现代 EVM 指令
Ethereum 升级可能会引入新指令,以替代由多个旧操作组成且成本更高的序列。EIP-3855引入了
EIP-5656引入了
减少外部调用
外部合约调用会产生账户访问、参数编码、返回数据处理和被调用合约执行等成本。如果多次调用会在同一笔交易中返回相同值,避免重复调用可能节省 Gas。有时可以将多个相关读取合并到一次调用中,并返回结构化结果。但更改调用结构可能影响重入、错误处理、可组合性和合约边界。仍须正确检查返回值和成功条件。如果廉价调用会导致不安全的假设,就不构成有效优化。适当情况下用事件替代存储
对于仅供链下应用使用的信息,事件日志通常比持久化存储便宜。事件可用于交易历史、分析、活动动态和用户界面通知。未来的智能合约执行通常无法搜索历史事件日志。因此,余额、权限、所有权、抵押品和其他可执行的状态必须仍能由合约访问。只有在未来链上决策不需要读取已删除的存储值时,才适合通过事件进行 Gas golfing。设计事件时,开发者还应考虑索引事件主题和日志数据的成本。缩减部署字节码
Ethereum 在部署期间存储合约运行时字节码时会收取 Gas。删除未使用的函数、重复逻辑、冗长回滚字符串和不必要的嵌入数据,可以降低部署成本。自定义错误、内部库、共享实现以及经过谨慎配置的编译器优化,可能缩小代码体积。根据 EIP-170,Ethereum 对正常部署的运行时代码设有限制。
初始化代码则受 EIP-3860的单独计量和限制。
合约更小,并不意味着其整个生命周期的成本必然更低,因为将代码拆分到多个合约中可能增加重复的外部调用成本。开发者应综合评估部署成本和重复执行成本。了解 Gas 退款的限制
较早的 Ethereum Gas-golfing 策略有时严重依赖先创建、后删除存储。当前 Ethereum 规则大幅限制了这种做法。EIP-3529降低了清除存储的退款,取消了原先的
Calldata Golfing 与 EIP-7623
开发者有时会通过紧凑类型、位打包、自定义编码或更短的表示方式来优化交易输入数据。减少交易字节数可以降低内在 Gas,尤其是在删除大量重复值时。不过,Pectra 已启用 EIP-7623,为执行量相对较少、但数据量较大的交易引入了更高的 Gas 下限。
紧凑格式所需的解码操作也会消耗 Gas。对于大量重复的工作负载,自定义编码可能降低成本,但对于小型交易可能没有任何收益。非标准编码会增加钱包、审计、集成和调试的难度。开发者应根据当前协议规则,对完整的编码和解码流程进行基准测试。何时适合使用内联汇编
内联汇编允许开发者直接控制 EVM 指令、内存、存储、调用和返回数据。如果高级 Solidity 无法高效表达某种专门操作,内联汇编可能带来 Gas 节省。密码学例程、紧凑数据解析、特殊内存操作和高频调用的底层函数,通常会考虑使用汇编。但汇编也会绕过 Solidity 提供的许多类型、内存和安全保护。汇编错误可能破坏内存、错误编码数据、处理返回值不当,或导致加密货币被盗。每个汇编实现都应与清晰的高级语言参考实现进行比较。测试应确认其在正常输入、边界输入、恶意输入和回滚情形下的行为完全一致。何时 Gas Golfing 会变得危险
当 Gas 成本优先级高于合约安全性和清晰度时,Gas golfing 就会变得危险。为了节省 Gas 而删除访问控制检查,可能允许未经授权的用户控制合约。删除重入保护可能使余额暴露于重复外部调用的风险。删除滑点限制或截止时间,可能让用户面临不利的加密货币交易执行结果。将多个值合并到一个打包字中,可能引入位掩码和转换错误。用自定义格式替代标准编码,可能造成签名、重放或解析漏洞。如果节省 100 Gas 却带来用户资金损失的可能性,这种优化在实际中就得不偿失。过时的 Gas Golfing 建议
Solidity 和 EVM 发生变化后,Gas-golfing 建议可能会过时。编译器可能开始自动优化以前需要手动重写的模式。网络升级可能改变某条操作码的价格,或增加效率更高的指令。在当前 Ethereum 上,过去使用
如何评估 Gas-Golfing 修改
优化后的实现首先必须与原实现具有相同的预期行为。开发者应比较部署 Gas、成功路径运行时 Gas、失败路径 Gas 和预期生命周期总成本。还应注明测试所用的编译器、优化器设置、EVM 版本和区块链状态。单元测试应验证函数的正常行为。模糊测试应探索意外输入和边界条件。不变量测试应确认关键记账和安全属性仍然成立。执行追踪应确认节省来自预期操作,而不是因为某项要求被意外跳过。人工审核人员应判断修改后代码是否仍易于理解和维护。生产合约中的 Gas Golfing
生产环境中的 Gas golfing 应聚焦于使用频繁的函数和经过测量的瓶颈。如果某函数执行数百万次,即使每次只节省少量 Gas,也可能值得投入精力谨慎实现。如果类似的节省只发生在一次性的管理函数中,则未必值得增加复杂度。除非优化属于计划中的协议升级,否则应保持公开接口和预期行为稳定。可升级合约必须保留存储布局和授权模型。价值较高的合约在进行重大底层优化后,应接受独立安全审查。部署的字节码应能通过已发布的源代码和构建配置复现。常见的 Gas Golfing 错误
一种常见错误是比较源代码长度,而不是编译后的 Gas 使用量。另一种错误是未启用与生产环境相同的优化器设置就进行测试。开发者可能只测量成功交易,而忽略成本异常高的回滚路径。他们可能优化很少使用的函数,却忽略每笔交易都会发生的重复存储写入。他们可能使用较小的整数类型,却没有获得任何存储打包收益。他们可能在确认编译器生成的代码确实成本高昂之前,就用汇编替换清晰的 Solidity 代码。他们可能依赖已无法反映当前 EVM 情况的旧 Gas 计价规则。最严重的错误,是在没有证明优化版本具有相同安全行为的情况下,仅因 Gas 数字更低就接受修改。常见问题
用简单的话说,什么是 Gas golfing?
Gas golfing 是通过小幅代码修改,降低智能合约的 Gas 消耗,同时不改变其预期行为。Gas golfing 和 Gas 优化是一回事吗?
不是。Gas golfing 通常是范围较窄的微优化,而 Gas 优化还包括架构、存储设计、链下计算、批处理和网络选择。Gas golfing 是 Ethereum 协议功能吗?
不是。这是开发者使用的非正式术语,并非官方操作码、交易类型或 Ethereum 标准。Solidity 代码越短,Gas 消耗就一定越少吗?
不一定。源代码长度无法可靠预测编译后的 EVM 字节码大小或执行成本。Solidity 优化器能自动进行 Gas golfing 吗?
优化器会执行许多类似转换,但开发者仍须设计高效的存储、数据流和合约架构。最好的 Gas-golfing 技巧是什么?
最有效的方法通常取决于具体合约,不过减少重复存储访问通常比改动细微的算术表达式更有帮助。较小的整数总是更便宜吗?
不是。只有当较小整数能让多个值共用一个存储槽时,通常才更有用。Calldata 总是比 memory 便宜吗?
对于只读外部输入,calldata 通常可以避免复制,因此更便宜;但应根据数据的实际用途选择位置。自定义错误为什么有助于 Gas golfing?
自定义错误使用紧凑的选择器和参数,而不是嵌入并返回冗长的回滚字符串。缓存存储值总能节省 Gas 吗?
如果该值仍然有效,缓存可以节省 Gas;但如果外部调用可能改变状态,缓存就可能不安全。Unchecked 算术是一种安全的优化吗?
只有在开发者能够证明所有可达输入都不可能导致溢出或下溢时,它才是安全的。Gas golfing 是否应该使用内联汇编?
内联汇编应仅用于经测量确认的瓶颈,因为它可能引入严重的内存、编码和安全错误。前缀递增总是比后缀递增成本低吗?
不是。当前编译器优化可能生成等效代码,因此必须测量具体结果。事件可以替代合约存储吗?
只有在未来智能合约执行不需要读取或执行相关信息时,事件才能替代存储。Gas golfing 能让交易免费吗?
不能。根据网络手续费规则,每笔交易仍须为必要的执行、数据和状态使用付费。Gas golfing 会降低 Gas 价格吗?
不会。它会减少 Gas 使用量,而 Gas 价格会随网络需求和交易费设置变化。Gas 退款是一种 Gas-golfing 方法吗?
符合条件的存储清除操作可以获得有限退款,但当前规则对退款设有上限,且不再支持过去依赖大量退款的策略。Gas golfing 会带来安全风险吗?
会。激进压缩、unchecked 算术、汇编和删除验证,如果未经仔细审核,都可能造成漏洞。应如何测试 Gas-golfing 的结果?
应使用固定的编译器设置、真实输入、执行追踪、模糊测试、不变量测试和安全审核进行测试。什么时候值得进行 Gas golfing?
当一项安全修改能降低用户或自动化系统频繁执行的函数成本时,通常最值得进行。总结
Gas golfing 是通过有针对性的智能合约修改,降低部署或执行 Gas 的做法。它与 Gas 优化有关,但通常侧重较小的代码层面改进,而非彻底重新设计应用。常见的 Gas-golfing 方法包括缓存存储读取、减少存储写入、打包变量、使用 calldata、选择自定义错误、简化循环和使用现代 EVM 功能。每种方法的效果取决于编译器版本、优化器设置、EVM 目标、输入值和区块链状态。在较旧 Ethereum 环境中能节省 Gas 的建议,在当前编译器和协议规则下可能不再有效。开发者应测量编译后的字节码,而不是根据 Solidity 源代码的外观或长度判断效率。通常应先考虑架构改进,再进行底层微优化。内联汇编和 unchecked 操作应仅用于节省效果可测量且安全性可以证明的情形。每项 Gas-golfing 修改都必须保留合约行为、安全性、可读性和兼容性。最佳的 Gas-golfing 结果并非单纯追求最低 Gas 数字,而是在安全前提下,以最低成本提供所需的加密货币功能。您可能也喜欢
反恐融资(CTF)
框架
波动性爆发
热门
目前热门备受市场关注的加密货币
最高成交量
按交易量计算交易量最大的加密货币

