让被调用方看到另一个来电人
在 EVM 的常识里,合约眼里的来电者只有两个来源:直接调你的 msg.sender,和最早发起交易的那条地址线。2020 年 9 月 24 日提交的 EIP-2997 想在中间再塞第三种:新增一条 IMPERSONATECALL 操作码,让发起调用的合约指定一个盐值,被调方看到的 CALLER 不再是真实调用者,而是一枚由真实调用者地址与盐值共同推导出的”假想地址”。提案作者的动机表述很朴素:一个合约常常需要在很多其他合约里拥有”身份”,但为每个身份部署一个真合约太浪费。

规格:假想地址怎么算
IMPERSONATECALL 定在 0xf6,吃七个栈参数:Gas、目标地址、输入起止、输出起止,外加 32 字节的 salt。假想 sender 的计算公式是 keccak256(0xff ++ address ++ salt ++ zeros32)[12:]——与 CREATE2 地址派生同一族写法:0xff 是单字节前缀,address 是 20 字节的真实调用者合约地址,salt 32 字节,尾部 32 个零字节替换掉了本应放 initcode 哈希的位置。提案特意说明这模拟了 CREATE2 的推导但无法与其地址空间实质相撞:CREATE2 的哈希输入带真实代码哈希,而这里的对应槽位是恒定的零。指令其余行为与 CALL 完全一致,Gas 计法照抄。
关键差异:钱从假账户出
真正让这条指令危险又迷人的是一条注释:调用发生转账时,价值从假想账户转出,而不是真实调用者。换句话说,只要目标合约肯从那个推导地址付款,IMPERSONATECALL 就能”替不存在的主人”挪动这个地址名下的资产。配合推导公式,调用者合约相当于免费获得无限个可预测的地址身份:派生、使用、注销,全程不创建任何真实账户。提案把这种行为类比为一种隐式的账户”虚拟化”——身份是哈希镜像,责任仍然由真实调用者合约的执行逻辑兜底。
语义边界与危险的误会
两个边界必须写清。第一,假想地址骗不过协议层:交易仍然由真实发起者签名,EIP-3607 仍禁止有代码的账户直接发交易,这条操作码改变的是合约调用语境里 CALLER 的返回值,不是交易归属。第二,一个从未存在过的”账户”要能支出,前提是假想地址名下确实有资产或有可被推导身份操作的状态——这类资产从哪来、谁有权再动它,提案本身不管,全留给上层合约自己设计。这也是该思路长期停留在草图层面的原因:审计一个会点头的替身,比审计一个真代理合约更难写清楚。
同槽竞争的另一半
0xf6 槽位上不止这一份图纸。一个月后 EIP-3074 把 AUTH 也标在 0xf6,路线却相反:3074 不造镜像身份,而是让真实 EOA 签名授权、由合约携带真实地址的权限行动,这条线最终走到 Withdrawn,撤回理由写明由 EIP-7702 取代。7702 已是 Final、真实改变过主网行为,两个老提案的分歧也因此尘埃落定。想看 0xf6 上第三个认领者与费用角度的分析,见 《智能账户批量部署贵在代码搬运:EIP-5478 的 CREATE2COPY 给重复代码开的价》。
一个具体的使用图景
把假想地址放进一个具体场景最能看清取舍。设想一个订阅型合约要为成千上万个用户合约各自维护一份”余额”,常规做法是用户合约真金白银地调用、事件满链飞;改用 2997 的想象后,平台合约可以用不同盐值为每个用户合约派生一枚专属镜像地址,用户资金名义上”挂在”镜像地址名下,实际全由平台合约的存储布局兑现。省掉的是部署成本与地址管理,付出的代价是每个镜像地址的行为完全依赖平台合约代码的正确性——它没有自己的代码可以自证,审计只能审平台本体。这也解释了为什么类似的”虚拟地址”最终多以应用层映射(比如状态通道或聚合账户的内部记账)落地,而不是靠新增操作码:语义相同,风险却小得多。想看同槽位真实落地路线的最终形态,7702 之后地址与密钥关系的重排,见 同一个前缀,两种焊死私钥的活路:7702 之后 0xef0101 的两次认领。
钱包用户需要防什么
这条提案从未激活,也因此任何产品宣传”我们用了 IMPERSONATECALL 替你管理地址”都属于话术借用。更普适的防御问题是:一个地址名下有没有资产,与这个地址有没有”人”,是两个正交的事实;看到陌生地址向你授权、转账或请求签名,先查它有没有代码、创建交易来自哪里、地址形态是合约还是普通账户,再决定信任。本文只提供机制解释与防御信息,不构成任何投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。