清存储还能退 Gas 吗?EIP-3529 把退款上限砍到五分之一 图 1
清存储还能退 Gas 吗?EIP-3529 把退款上限砍到五分之一 · 图 1

清空合约存储理论上能拿到 Gas 退款,早年间甚至有人把退款当成“燃气电池”:一笔交易里清大量存储攒下退款额度,下一笔交易再把它兑现成更大的执行空间。EIP-3529 就是冲着这种玩法来的,它把退款砍掉了大半。这篇讲退款规则怎么演变、为什么砍,以及普通用户还能不能从清数据里省到 Gas。

退款制度原本想鼓励什么

以太坊最早的退款设计针对两类操作:清空存储槽的 SSTORE 和自毁的 SELFDESTRUCT。官方动机写得很直白——激励开发者做“状态卫生”,主动清掉不再使用的存储和合约。EIP-2200 细分了三种情形,其中最诱人的一种是:把原本非零的存储槽清成零,可记入 15000 Gas 退款。问题出在退款总额的上限:当时规定一笔交易最多退到实际用量的二分之一。退款在交易执行完之后才结算,等于给下一笔交易腾出更多区块空间,于是有人把退款囤成电池,制造持续的二倍区块用量高峰——EIP-3529 文本指出,这让 1559 的费用机制都难以压制用量尖峰。

EIP-3529 改了三处

第一,删掉 SELFDESTRUCT 的全部退款。第二,把“清一个读过的好数据”的退款上限从 15000 降到 SSTORE_RESET_GASACCESS_LIST_STORAGE_KEY_COST 之和,按 EIP-2929 与 2930 的常数是 4800。第三,退款占交易用量的上限从二分之一降到五分之一——文本把常数命名为 MAX_REFUND_QUOTIENT 并设为 5。三条一起生效于 2021 年 8 月的伦敦升级,与 EIP-1559 同批,状态为 Final。文本还给了一句很形象的直觉:清空从未被读过的数据(多半是无用数据)基本不再产生净退款,清空被读过的数据(有用的数据)仍有净退款。

退款上限从二分之一压到五分之一的天平示意

对开发者:省 Gas 策略怎么改写

旧时代有不少合约把“顺手清数据”写进业务逻辑,赌的就是高额退款补贴。EIP-3529 之后这笔账要重算:清一个读过的槽净支出约五千,名义回收 4800 还被总上限截断,退款只在交易本身用量足够大时才有意义。一个常见误区是把退款当成 Gas 折扣券——它不减少交易本身的消耗,只影响结算后返给区块的空间额度,对钱包显示的预估 Gas 与最终扣费不是同一件事。做批量清理的方案(比如一次清理上百槽的迁移脚本)应比较两件事:清理收益是否值得那约五十乘五千的固定开销,而不是指望退款摊平成本。

对用户意味着什么

靠清存储给交易“回血”的空间被大幅压缩:即使合约逻辑帮你清了槽,返还到你手里的 Gas 也不足以显著抬高这笔交易可用的区块空间。钱包用 estimateGas 估算含大量存储清理的交易时,估出的值会更接近真实消耗,不会再出现“估算便宜、上链后区块空间被退款挤占”的错位。对做存储卫生的团队,退款仍有但不厚,清不清数据应该由业务需要决定,而不是由补贴决定。

一次清槽操作的账目

把常数串起来看一笔“清一个读过的槽”的完整账:写入前该槽未被本交易碰过,先付 2100 冷读附加费;执行改写本身,非零改零走 SSTORE_RESET_GAS,按 EIP-2929 修订后的定义等于 5000 减 2100 即 2900,合计花掉约 5000。记账侧,这笔操作记入 4800 退款额度,交易结束结算时再受“不超过实际用量五分之一”的第二重封顶。拿清十个槽的交易算:约 21000 基础加五万清理开销,用量七万一左右,五分之一约 14200,而名义退款是四万八——只能兑现三成。同样结构放到旧规则下,二分之一上限能兑现三万五千五,退款几乎够给下一笔交易腾出同等的区块空间。这就是“燃气电池”失效的具体原因:不是清槽不再退钱,而是五分之一上限加 4800 低基准叠加后,退款再也攒不成规模。

边界提示

退款是执行层的结算规则,不是价格补贴,也不是任何收益承诺;不同交易类型与回滚场景下计数器的行为细节应以执行规范为准。本文只做机制说明,不构成投资建议。