ERC-6357 multicall:一次交易里的多次 delegatecall,为什么全部同生共死
转出五枚 ERC-721 就要签五笔交易,每笔先交 21000 Gas 的地板费——2023 年 1 月 18 日起草的 ERC-6357 在动机部分点名的就是这个浪费。它给出的接口小得罕见:一个 multicall(bytes[] calldata data) 函数,把一串已经 ABI 编码好的调用数据收进去,逐个用 delegatecall 调自己,任何一步失败则全部回滚。这份提案如今停在 Last Call 状态,即规范内容基本定稿、等待最终确认的阶段。它与更常见的多签批量、批量铸造优化不是同一件事,独特性全在 delegatecall 这个选择上。
delegatecall 自己意味着什么
普通合约调用外部函数用的是 call:目标合约在自己的存储里执行。ERC-6357 让合约把每段调用数据用 delegatecall 发到自己的地址上,执行逻辑不变,但读写的是本合约的存储。对于 ERC-721 这类逻辑和账本同居一个合约的部署,这等于把 transferFrom 逐笔循环搬进了一笔交易:整串调用共享同一份存储、同一次 Gas 账单,要么五枚全部到账,要么一枚都不动。原文的测试用例正是这个思路——对同一个 ERC-20 合约 multicall 两段 transfer 编码,同时转给两个地址,失败则双双不生效。
可选的 multicallPayable 再多收一个 values 数组,要求各项之和不超过随交易附带的 msg.value。原文也承认这个可选项并不总能实现,因为原生币金额要在多次 delegatecall 之间拆分,容易和合约里对 msg.value 的既有假设打架。

和相邻机制的边界
批量执行这个赛道已经有好几辆马车: ERC-7821 的最小批量接口走的是标准函数加可选批量的路,各类 Relayer 走的是代发交易的路,ERC-4337 的批量入账走的是账户抽象的路。ERC-6357 的位置是最朴素的一种:不改动账户模型、不引入新执行层,就是让一个普通合约支持自带打包。对藏家,这个区别会体现在费用单上——逐笔转账付 N 份 21000 地板费,multicall 只付一份,剩下是执行本身的开销;对同一合约多枚的批量转出,这是最简单的一条省钱路。
坑同样出在 delegatecall 上。它继承的是调用上下文,若目标地址不是合约自己而是被换成别家,读取的就是自家存储、执行的是别家代码——这是代理合约历史上被攻击利用的经典错位。ERC-6357 的定义里 delegatecall 的目标恒为本合约地址,才让这个原语处于安全使用范围内。于是给用户的核验清单多出一条:对声称支持 multicall 的合约,确认它调的是自己而不是一个可配置的第三方地址;若合约本身是可升级代理,multicall 执行的是当前实现合约的逻辑,升级后的行为变化会一次性作用在整批调用上。批量让失败更干净,也让意外的影响面更大,这是所有原子批量共同的性质。还有一层容易被忽略的细节值得写进核对清单:multicall 把多段调用数据塞进同一笔交易的输入里,你在签名界面看到的调用目标是代币合约,但每一段内部调用的函数选择器、接收地址与数额都编码在嵌套的字节串中,多数钱包的默认界面并不展开这层结构。这意味着批量既是省费工具,也是藏匿地址的常见容器——历史上有钓鱼页面把用户自己的转账调用合法地打包进批量交易,每一段单独看都指向正确的合约,组合起来却把资产送到了别处。安全习惯是把批量操作的段数与目标地址和你想做的事情逐段对照,来源不可信的页面一律不做批量确认,宁可逐笔慢一点。本文为机制说明,不构成任何投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。