多签钱包执行交易时那五个手续费字段:默认全零意味着谁点执行谁垫付 图 1
多签钱包执行交易时那五个手续费字段:默认全零意味着谁点执行谁垫付 · 图 1

多签钱包的转账要经过发起、凑签名、执行三步。前两步只发生在链下——大家签的是一条待执行消息,不写链,具体见多签钱包是怎么完成一笔转账的?从发起、凑签名到执行。真正把交易推上链的,是最后一步:某个外部账户调用 Safe 合约的 execTransaction,提交的人垫付本轮的网络手续费。但这个函数里还藏着五个与费用有关的参数:safeTxGasbaseGasgasPricegasTokenrefundReceiver。多数团队从没动过它们,默认全是零。它们到底是干什么的,什么时候才需要填?

默认场景:谁点执行,谁掏gas

执行交易和你在普通钱包点一次发送没有本质区别,提交账户(一个普通钱包,或一个自动脚本用的地址)必须自己准备好gas,从它自己的余额里扣。Safe 金库不会预先出钱。所以团队分工里”谁负责点执行”实际上等价于”谁承担这一轮手续费”。多人轮值执行时,最好提前在内部账里记清垫付关系,不然月底对账说不清。

五个字段是一套报销机制,不是外包付费

Safe 合约源码对 execTransaction 的注释写得很直白:按 gasPrice * gasLimit 的口径,用 gasToken 指定的代币把费用转给 refundReceiver。拆开看:

  • safeTxGas:这次多签交易本身执行允许消耗的gas上限;
  • baseGas:与业务执行无关的基础开销,比如验证签名、发送这笔报销本身;
  • gasPrice:报销的单价,填零就意味着不报销;
  • gasToken:报销用的结算代币,零地址表示用原生ETH;
  • refundReceiver:收报销的地址,填零地址时默认退给交易发起方。

也就是说,这套字段解决的是”金库替执行者出钱”:把参数配好,执行者垫付之后,Safe 在同一笔链上交易里把费用从金库划给执行方。全部保持默认值时没有任何报销转账发生,执行者的gas就是自担成本。

执行失败,报销照付

源码注释明确写了:费用划转无论用户交易成功还是失败都会执行。对应链上会发出 ExecutionSuccessExecutionFailure 事件,两者都带一个payment字段记录实际支付额。这条规则对执行者是保护——只要配置了报销参数,即便业务回滚,垫付也能拿回来;反过来,参数没配置的人要为失败交易自担gas,这和主链上”失败也烧手续费”是同一件事的两种账法,具体口径见一笔手续费的三本账:钱包估算、签名上限与链上回执怎么对上

源码同时提醒:合约不做这些检查

execTransaction 的注释还专门列了它不会替你做的事:不检查 to 地址上有没有合约代码,也不检查 gasToken 是不是一个真实代币合约,这些都由调用方自己负责。这意味着把 gasToken 填成任意字符串地址不会报错中止,报销转账可能以一种谁也收不到的方式执行完毕。自动化脚本要启用报销路径时,先把代币地址放进一次只涉及小额的试验交易里验证到账,再放进正式流程。

阈值签名不改变费用结构

补充一个容易混淆的点:把”发起时多人签名”误解为”签名也要各自付gas”。签名(确认)只是对 EIP-712 哈希生成一段可验证的签名数据,不广播、不上链,因此凑够阈值的过程中无论几个人签字,链上手续费都只发生在执行那一次。这也是多签比”每笔都要轮流发交易”省钱的根本原因。反过来的推论同样成立:签名不花钱,所以任何人可以对任何提案免费凑签名——签字本身不构成执行承诺,执行参数被改动后旧签名即失效,核对时应以交易内容哈希为准而不是签名人数。

点确认之前核对什么

把执行权交给自动化脚本之前,先用钱包的交易模拟看一遍 execTransaction 的参数,重点看 gasPrice 是否非零、refundReceiver 指向谁。报销收件人写错地址,等于金库替别人付了钱,这类划转在链上不可撤回。另一个常见误区是把 safeTxGas 调得很大来”防失败”:它只是划定执行 gas 边界,超出部分照样由提交者的gas limit兜底,填大了只会让报销基数虚高,防不了业务报错。

多签涉及多人共同保管资产,任何一笔执行都难以事后撤销,执行前的参数核对比速度重要。本文只做机制与参数解释,不构成投资建议。