多签钱包只有一个全局序号:Safe 的 nonce 怎么让交易互相排队与替换 图 1
多签钱包只有一个全局序号:Safe 的 nonce 怎么让交易互相排队与替换 · 图 1

多签钱包里,一笔交易从发起到真正执行要凑齐签名,而多笔交易之间还有一层看不见的先后锁:合约只发一个全局序号,没轮到它的那笔就只能挂着。理解 Safe 合约的这个序号机制,才知道为什么交易会被堵在队列里,也知道怎么把它换掉。

合约里的一个全局计数器

Safe 合约源码在注释里写得很直白:每一笔交易应当有不同的 nonce,用来防重放。实现上合约存储只有一个递增计数器:execTransaction 执行时读取当前值校验传入的序号,随后在链上把计数器加一。也就是说,多签钱包没有按持有人分列的一排小本本,全体签名人共享同一条序列。交易哈希的构成里也包含这个序号字段——签名绑定的是这笔交易在这个序号上的完整参数,序号变了签名即作废。多签转账的发起流程见 多签钱包是怎么完成一笔转账的?从发起、凑签名到执行

排队与堵车的由来

序号规则推导出两个日常现象。第一,严格排队:你想连着发起三笔交易,它们的序号必然是一个挨一个的连续值,后一笔的执行以前一笔把序号推进为前提,队列顺序就是执行顺序,跨持有者也不例外。第二,堵车:若序号较小的那笔因签名凑不齐、参数有争议而被搁置,后面所有序号更大的交易会全体无法执行——不是谁卡了谁的操作权限,而是合约只认当前序号这一把钥匙。团队运营里常见的每月固定转出突然全部失效,八成就是最前面挂了一笔没人推进的交易。所以任何多签的协调表上都该有 nonce 这一列。

用相同序号替换那笔交易

想清掉挡路的那笔交易,机制上只有一个方向:提交一笔使用相同序号的不同交易,把签名凑齐并执行。执行成功后全局序号前移,旧序号上那笔交易的哈希从此对不上合约状态,永远失效,它的签名也就再也拼不成执行条件。这就是取消的实质——不是删除队列里的记录,而是把序号从它脚下抽走。两个细节:替换交易本身要重新走凑签名流程,旧签名不能挪用;在界面层发起替换时,新交易的参数与费用字段要逐项核对,谁点执行谁垫付的细节见 多签钱包执行交易时那五个手续费字段:默认全零意味着谁点执行谁垫付

把序号当纪律的习惯清单

把机制变成动作:发起前先查合约当前序号,把它写进每笔提案的说明里;并行操作确认是否真需要并行——相同序号是替换不是同时,想办两件事就用两个序号;每次执行后确认序号已推进再动下一笔;恢复或审计多签状态时,以链上合约存储的序号为唯一权威,本地记录只是参考。序号理解到位,多签的多数堵单问题能在发起环节避免,而不是堵死后手忙脚乱。

一次堵单事故的完整推演

用一周的时间线把机制走一遍。周一,Alice 发起一笔序号为 7 的月费支出,签名需要三人凑够,她签完把哈希发进群,另外两人没回。周二,Bob 想把一批代币转到金库,界面显示序号 8,他发起并凑齐了签名,交易却一直显示待执行——合约此时的期望序号仍是 7,8 进不去。周四,团队发现根源,用相同序号 7 提交一笔转出金额为零的替换交易重新凑签名,执行成功后序号推进到 8,Bob 那笔自然开始执行。整场事故里没有资产损失,损失的是两天时间与一次全员排查;如果第一次发起时就登记序号与到期日,事故根本不会发生。这也解释了为什么把执行权交给自动化脚本的多签,反而更要在提案层面保留人工可见的序号记录。

风险提示:本文是多签机制说明,不构成投资建议,也不构成对任何产品界面行为的描述。具体操作请以你使用的合约版本与官方文档为准。