ERC-3772 压缩整数:存一个金额真的需要 256 位吗 图 1
ERC-3772 压缩整数:存一个金额真的需要 256 位吗 · 图 1

ERC-3772 压缩整数:存一个金额真的需要 256 位吗

Solidity 里存一个金额,默认住进一个 256 位的存储槽。问题在于 uint256 能表达的量程从十的负十八次方一路铺到十的五十八次方,而一个稳定币代币合约真正会碰到的数量,大概率落在千分之一到一万亿之间——提案原文算过这笔账:覆盖一千万亿个量级只需要约 50 个比特,剩下两百多位常年是零,而初始化一个存储槽的 Gas 是实打实的美金。2021 年 8 月 27 日起草的这份 Stagnant 提案 ERC-3772 由此提出:把大整数压进更小的类型里存。

有效位加移位

压缩格式叫 cint,比如 cint64 由两段拼成:一段 significant 存有效比特,一段 shift 存这个值需要左移多少位才能还原。具体操作是找原数最高位的 1,从它往下保留一段有效比特,被舍掉的低位用移位计数记账。类型越窄,留给有效位的空间越小:cint64 与 cint96 用最低的 8 位存移位,cint128 与 cint248 用 7 位。小整数左移少、还原精确;数字越大,低位被切掉的越多——压缩天然有损,丢掉的永远是最低位的精度,对金额而言就是最小面额以下的零头。提案里的例子算得很直观:一个二进制下以 1 后接五十多个零开头的大数,压进 cint64 之后有效段只剩一个 1 加一串对齐用的零,移位计数记着它离真实量级的距离。

解压因此定义成两种。decompress 直接还原,得到的是不超过原值的近似数,系统性偏小;decompressRoundingUp 把舍去的位向上进一,保证不小于原值。两种口径的存在本身就是对有损性的坦白:同一个 cint 值可以合法地还原出两个略有差异的 uint256,误差被控制在一次低位进一的范围内。什么时候用哪一种成了一条业务规则:算用户余额用保守口径,涉及扣款和结算用向上口径,两边同时用错方向都会积累误差。提案的测试要求也围绕这点——压缩后 decompress 小于等于原值,原值小于等于 decompressRoundingUp。

ERC-3772 压缩整数:存一个金额真的需要 256 位吗 图 2
ERC-3772 压缩整数:存一个金额真的需要 256 位吗 · 图 2

收益与适用边界

把账算回藏家视角:这套技巧的收益全部落在开发者一侧——存储初始化便宜了,合约里金额字段一多,部署成本能省出可见的一块;收益偶尔也传导到用户,同样的逻辑写得更省,Gas 地板价自然低一点。适用边界则很清晰:需要精确到最后一位小数的场合,比如按位计息的账务核心,直接排除有损压缩;展示、快照、统计口径类的粗粒度数字最匹配。这份提案从未成为部署热点,现实原因是以太坊的存储优化大多有更彻底的解法——把值压进更小的槽不如干脆用 packed 结构合并字段,或者把大账本挪进 Merkle 树与外部索引。

但它的思路在链上以另一种方式活着:任何把大额数字存进小于 256 位字段的设计,都是同一族有损压缩的变体,审计清单上因此多一条固定检查——余额字段是多少位、溢出怎么舍入、舍入方向对谁有利。读一个陌生合约时判断有没有用上这类技巧,看三处就够了:变量声明里有没有 cint 或自定义的窄类型、余额查询函数是不是在返回前先做了一次 decompress、测试用例有没有成对检查两种解压方向。三处只要出现一处,这个合约的余额展示就要按近似值对待。涉及结算时,永远回到完整精度的那一份数据——真实余额在压缩前的那一次求值里,不在任何展示层。精度从来不是免费的,免费的精度只存在于没人查账的地方。本文为机制说明,不构成任何投资建议。