代理壳子和逻辑本体怎么换搭档:ERC-1822 的 PROXIABLE 槽位与升级自检
“你买的 NFT 合约可能是个壳,随时能换芯”——这句话常被提起,但壳怎么换才不砸到自己的脚,是另一层问题。ERC-1822 给出的答案叫通用可升级代理标准,把自己定义为一个通用兼容的代理合约规范,文件头记录创建于 2019 年 3 月 4 日,仓库记录状态为 Stagnant。
一个固定槽位解决壳与字段的碰撞
代理模式的原理在术语部分写得干净:代理合约 A 存数据,逻辑合约 B 出代码,A 用 delegatecall 让 B 的代码在 A 的存储上运行。麻烦在于:如果逻辑合约把“下一个逻辑合约的地址”存在普通变量里,这个变量的存储位置和逻辑自身字段的槽号很容易撞上,升级时互相覆盖。ERC-1822 的做法是把逻辑合约地址写死在一个由字符串算出的槽位——keccak256("PROXIABLE"),也就是正文里那个十六进制常量 0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7。壳的 fallback 从这槽读地址再转发,逻辑合约的字段各占各的低槽号,互不干扰。这就是“通用”的由来:壳对任何逻辑合约即插即用,不需要为每个项目改一遍。
构造阶段还有第二个设计:壳的构造函数接受任意数量任意类型的参数,并把构造数据 delegatecall 给逻辑合约执行,等于允许从多个构造函数里选一个初始化;因为初始化数据可以先按壳的 ABI 再按逻辑 ABI 解码,验证字节码仍然可行。

升级必须带自检,否则会升死
规范在 Proxiable 合约部分加了一句带警告符号的硬话:updateCodeAddress 和 proxiable 必须存在于逻辑合约里,缺了可能升级不了,甚至让代理彻底不可用。机制是 updateCodeAddress 在覆盖槽位前,先调用新逻辑合约的 proxiableUUID,比对它是否返回那个 PROXIABLE 常量——确认“接班的也承认这套协议”才肯换。逻辑合约通过继承 Proxiable 把这个入口带上,是否允许升级、谁能升级,留给项目自己的逻辑。
对读 NFT 合约的人,这条提供了一个具体查法:在区块浏览器里读目标合约在 PROXIABLE 槽的值,能得到它当前指向的逻辑合约;再点开逻辑合约看它有没有实现这两个升级入口。一个不实现自检的“自称 UUPS”合约,升级时把地址指错对象,链上不会好心拦你。
读一个 NFT 合约时怎么用这份标准
把 ERC-1822 当成体检手册,流程很短。第一步在浏览器打开合约的存储读数页,直接定位到正文给出的那个常量槽位 0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7,读出的地址就是当前逻辑合约;如果这个槽是零,说明它多半不是这种槽位约定的代理,得换别的代理模式去认。第二步点开逻辑合约,确认它是不是带升级入口的实现——规范对这一点带警告符号:逻辑合约缺了 updateCodeAddress 和 proxiable 这些函数,代理可能从此无法升级、彻底不可用。第三步回代理合约看升级函数有没有访问控制、最近有没有发生过逻辑地址变更,变更历史从事件里就能拉出时间线。三步都有明确的链上数据可查,不依赖任何项目的自述。
与“不可变”宣传的对照
这份提案当年的动机,就是改进既有代理实现、统一字节码验证方式。今天代理方案已进化出透明的代理模式、可升级克隆、多面代理等一大堆变体,ERC-1822 在仓库里停留在 Stagnant,说明它作为标准文本没有等来大规模正式采纳,但它定下的“固定槽位加 UUID 自检”两个部件被后续大量实现借用。读它最实用的收获是两把尺子:看存储,确认壳的数据位置和逻辑字段分开没有;看函数,确认换芯路径上有没有那道兼容性检查。
把这两把尺子用在“永久上链”的宣传语上,边界就清楚了:链上所有权账本可以在壳里、图片指针可以在事件里,但只要执行代币逻辑的代码在另一个可替换的地址上,“不可改”三个字就只适用于存储层,不适用于行为层。这是看可升级 NFT 合约和看代理壳子两个话题最不该混的一点。本文为机制说明,不构成任何投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。