合约体积若按字收钱:EIP-7907 的 64KB 上限与逐字 gas 账 图 1
合约体积若按字收钱:EIP-7907 的 64KB 上限与逐字 gas 账 · 图 1

Solidity 开发者大多见过 24KB 那堵墙:合约代码超过 24576 字节就拒绝部署,为此人们把逻辑塞进代理、拆成 Diamond、靠各种间接手段绕路。EIP-7907 想把这堵墙挪到 64KB,同时给加载大合约代码这件事装上 gas 计价表——上限放宽,但为大体积买单的人要按消耗多付钱。本文按 EIP 文本拆解它的公式、参数与状态边界。

24KB 的墙从哪来

代码体积上限由 EIP-170 引入,动机是防拒绝服务:合约代码越大,节点在磁盘读取、EVM 预处理上的成本随体积线性增长,还会撑大 Merkle 证明,而这些成本没有被常规 gas 充分覆盖,直接设一个上限是省事的防法。代价是所有合法的大合约都被迫做拆分工程。EIP-7907 的思路是:与其一刀切,不如把加载代码的资源成本逐字计费,让 gas 回归「按消耗收费」的原则——这也与以太坊的一贯哲学一致。

数字与公式

规范把 MAX_CODE_SIZE 从 24576 字节(0x6000)抬到 65536 字节(0x10000),并把 EIP-3860 定的 MAX_INITCODE_SIZE 从 48KB 抬到 128KB,始终取代码上限的两倍。计费核心是超量代码成本:对超出 24KB 的部分,每 32 字节字收取 2 gas,作为动态的 EXCESS_CODE_COST 项加进 CALLSTATICCALLDELEGATECALLCALLCODEEXTCODECOPY 这些会加载代码的指令。为此 EIP 给合约代码引入冷/暖状态:冷代码的访问要叠加冷加载存储与冷账户访问的成本,暖代码只按暖读收费;空代码永远算暖。举例来说,一笔交易里第一次加载一个 40KB 的冷合约,费用在访问成本之外再加 1024 gas 的超量项(超出的 16384 字节合 512 个字、每字 2 gas);同一笔交易里第二次碰到它,只按暖读的低价计。当合约代码走 EIP-7702 委托指向另一个账户时,被指向账户的代码若为冷态也要计入这份费用。代码的暖化遵循 EIP-2930 的日志回滚规则——交易回滚时暖化一并回滚,不留后遗症。如果大合约是整笔交易的入口合约,超量费在执行开始前预扣且不并入初始 gas 费,gas 不够就直接停住、余额不划转。

示意

为什么上限只到 64KB

文档给了两层理由。一层是资源假设:把上限无限放开,区块 gas limit 的变化会打破 p2p 层的既有假设,64KB 是谨慎的刻度。另一层是开发体验:24KB 天花板逼着开发者使用代理、delegatecall 间接层、Diamond 模式这些与自己需求无关的高级技巧,抬升上限可以让逻辑留在一个合约里,减少跨合约调用,也降低新人从想法到部署的门槛。

账户里多出来的一个字段

按字计费有个前提:不用加载代码就得知道每个合约多大。而现有账户元组只有 nonce、balance、存储根、code hash 四元组,多数生产级键值库不加载整段代码就窥不到大小。EIP-7907 因此给账户追加 codesize 字段,并用两条规则免掉全量迁移:激活后新建或更新的账户写入该字段;没有该字段的存量账户一律视为不超过 24KB。换句话说,在某个老账户下次被更新之前,节点对它的计费都基于「小合约」假设。

现在该拿它当什么

还要交代一个容易忽略的配套细节:codesize 字段采取的是免迁移的渐进策略——激活后新建或更新的账户才写入该字段,缺字段的存量账户一律按「不超过 24KB」计费。这个假设对老合约恒成立(它们诞生时 24KB 硬上限就在),所以计费覆盖面随状态更新自然扩全,不需要任何一次性数据搬家。这是理解本 EIP 工程取舍的关键:它用一条「缺字段即小合约」的旁路,换来了节点不必为升级而重建状态。

必须交代状态边界:EIP 仓库把这份提案登记为 Draft(草案)状态,声明依赖 EIP-170、2929、3860 与 7702,尚未在任何已激活的网络升级中。它落地之前,链上的现实仍是 24KB,代理模式与 Diamond 等工程手法不会因这份草案作废。对读者而言,它是一份现成的样本,展示 gas 计费与硬上限之间那条第三条路怎么设计——最终参数如何落锤,以走完流程后的规范文本为准。本文为机制说明,不构成投资建议。