Foundry 对 Tempo 浏览器钱包不完整估算提示的保守处理:实现与调用链解析
2026/9/16 17:30:18 网站建设 项目流程

Foundry 对 Tempo 浏览器钱包不完整估算提示的保守处理:实现与调用链解析

【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry

导读

本文围绕 Foundry 仓库中的一条 changelog 条目(.changelog/tempo-browser-estimation-hints.md)展开,讲解 Foundry 如何在 Tempo 网络上,面对浏览器钱包给出的**不完整 gas 估算提示(key_type / key_data)**时采取保守兜底策略:完整提示原样保留,缺失或残缺的提示一律回退到固定尺寸的 WebAuthn 签名占位值。读完本文,你将理解这条foundry-common: patch变更背后的源码实现、四种提示组合的处理规则、它在castforgescript中的实际调用位置,以及仓库内配套的单元测试与集成测试验证。

一、变更背景:浏览器钱包估算提示为什么需要"保守处理"

1.1 changelog 条目本身

仓库根目录下.changelog/tempo-browser-estimation-hints.md全文如下:

--- foundry-common: patch --- Handled incomplete Tempo browser-wallet estimation hints conservatively.

按照.changelog/README.md中定义的版本契约,每个 changelog 片段用 YAML frontmatter 把工作区包名映射到patch/minor/major,正文则是一条非空的发布说明。这里将foundry-common标记为patch,表示该 crate 获得一次补丁级版本提升,变更内容为"以保守方式处理不完整的 Tempo 浏览器钱包估算提示"。

1.2 为什么要关心估算提示

Tempo 是一个支持账户抽象(AA)与浏览器钱包签名的网络。当用户通过浏览器钱包(例如浏览器扩展配合 WebAuthn 签名)发起交易时,最终签名的类型和长度会直接影响交易的 gas 用量:不同类型签名(Secp256k1 / P256 / WebAuthn)在 calldata 中的长度不同,签名越长,交易的 calldata 与执行开销越大。

在 gas 估算阶段,交易往往尚未真正签名,此时估算请求只能依赖keyType(签名类型)与keyData(签名密钥数据)这两个"提示"字段来估算未来签名占用的字节数。如果提示缺失或不完整,Tempo RPC 端无法准确计算签名部分的大小,可能给出偏小的 gas 估值,最终导致真实交易因 gas 不足而失败。这正是本条变更要解决的问题:在提示不完整时,用保守(偏大)的占位值补全,宁可高估也不低估。

二、源码实现:browser_wallet_gas_estimation_request的补全逻辑

2.1 统一的 trait 接口

crates/common/src/transactions/builder.rs中定义了FoundryTransactionBuildertrait,其中的默认方法即为此功能预留了扩展点:

/// Clone this request and prepare it for browser-wallet gas estimation. /// /// Complete signer hints are preserved. Missing or incomplete hints default to a conservative /// WebAuthn signature size. fn browser_wallet_gas_estimation_request(&self) -> Self where Self: Clone, { self.clone() }

非 Tempo 网络直接走默认实现(原样克隆),只有 Tempo 网络覆盖了该方法。

2.2 保守占位值的定义

crates/common/src/transactions/builder.rs第 399~403 行定义了占位常量:

/// Viem's Tempo formatter uses a 1,400-byte WebAuthn placeholder when the signature is not yet /// available. Tempo RPC encodes that size as a two-byte big-endian `keyData` value (`0x0578`). const TEMPO_BROWSER_WEBAUTHN_DATA_SIZE: u16 = 1_400;

1_400转成两字节大端序正是0x05780x0578 = 1400)。也就是说,Tempo RPC 用keyData编码"签名数据字节数",而浏览器钱包生态(代码注释引用了 viem 的 Tempo Formatter)在签名尚未产生时使用 1400 字节的 WebAuthn 占位。Foundry 直接复用同一尺寸,保证估算请求与真实签名后的交易结构一致。

2.3 四种情况的处理规则

crates/common/src/transactions/builder.rs第 452~466 行是 Tempo 网络的覆盖实现:

fn browser_wallet_gas_estimation_request(&self) -> Self { let mut request = self.clone(); if request.key_type.is_none() { request.key_type = Some(SignatureType::WebAuthn); request.key_data = Some(Bytes::copy_from_slice(&TEMPO_BROWSER_WEBAUTHN_DATA_SIZE.to_be_bytes())); } else if matches!(request.key_type, Some(SignatureType::WebAuthn)) && request.key_data.is_none() { request.key_data = Some(Bytes::copy_from_slice(&TEMPO_BROWSER_WEBAUTHN_DATA_SIZE.to_be_bytes())); } request.convert_create_to_call(); request }

结合crates/common/src/tempo/tests.rs中的四个测试用例,规则可以归纳为下表:

估算请求中的提示状态处理行为对应测试
key_typekey_data均缺失回退为WebAuthn+keyData = 0x0578browser_gas_estimation_uses_conservative_webauthn_hint
key_typeWebAuthnkey_data缺失保留WebAuthn,补填keyData = 0x0578browser_gas_estimation_fills_missing_webauthn_key_data
只有key_data、没有key_type视为不完整,整体替换为WebAuthn+0x0578browser_gas_estimation_replaces_key_data_without_key_type
key_typekey_data完整(如 Secp256k1 / P256 / WebAuthn)原样保留,不做任何修改browser_gas_estimation_preserves_complete_signer_hints

值得注意的细节:

  • 完整提示绝不覆盖。只要key_typekey_data都齐全,即使是WebAuthn之外的签名类型,也保持原值,避免干扰用户明确指定的签名方案。
  • key_data无法单独成立。仅有key_data而缺少key_type被判定为"不完整",会连同key_type一起被替换为保守的 WebAuthn 占位。
  • CREATE 转换同步进行。方法末尾调用convert_create_to_call(),把合约创建转换为 AA 兼容的calls列表条目,确保估算请求的结构与真实 Tempo AA 交易一致(详见 trait 中该方法的注释)。

2.4 单元测试验证

crates/common/src/tempo/tests.rs的第 28~89 行集中验证了上述规则。以"空提示"用例为例:

#[test] fn browser_gas_estimation_uses_conservative_webauthn_hint() { let request = TempoTransactionRequest { inner: TransactionRequest { /* ... */ }, ..Default::default() }; let estimate_request = request.browser_wallet_gas_estimation_request(); assert_eq!(estimate_request.key_type, Some(SignatureType::WebAuthn)); assert_eq!(estimate_request.key_data, Some(Bytes::from_static(&[0x05, 0x78]))); let json = serde_json::to_value(&estimate_request).unwrap(); assert_eq!(json["keyType"], "webAuthn"); assert_eq!(json["keyData"], "0x0578"); // ... }

该测试还断言了 JSON 序列化结果为keyType: "webAuthn"keyData: "0x0578",从侧面确认了补全后的请求在网络传输中的实际形态;同时断言calls列表正确承载了原to/value/input,而inner.to等字段被清空,验证了 CREATE 转calls的副作用没有破坏原始请求(原始requestkey_typekey_data仍为None)。

三、调用链:这条兜底逻辑在哪些命令中生效

通过搜索browser_wallet_gas_estimation_request的调用点,可以确认该逻辑覆盖了 Foundry 的四大交易入口,全部遵循"浏览器签名器 + Tempo 链"的双重条件:

入口调用位置触发条件
cast estimatecrates/cast/src/cmd/estimate.rs#L157is_browser为真
cast send等走CastTxBuilder的命令crates/cast/src/tx.rs#L718self.browser && self.chain.is_tempo()
forge createcrates/forge/src/cmd/create.rs#L551browser_signer.is_some() && chain.is_tempo()
forge script广播阶段crates/script/src/broadcast.rs#L1351函数参数tempo_browser为真

例如在CastTxBuilder中:

if fill && self.tx.gas_limit().is_none() { let request = if self.browser && self.chain.is_tempo() { self.tx.browser_wallet_gas_estimation_request() } else { self.tx.clone() }; let estimated = Self::estimate_gas(&self.provider, request, self.gas_estimate_multiplier).await?; self.tx.set_gas_limit(estimated); }

也就是说:只有 gas limit 尚未设置、且当前是 Tempo 链上的浏览器钱包交易时,才走保守补全路径;其余场景一律使用原始请求,避免引入无谓的占位开销。forge scriptestimate_gas还会在估算前reset_gas_limit(),防止 RPC 直接返回请求中已设置的 gas 值而跳过真实估算(见 crates/script/src/broadcast.rs#L1346-L1357)。

此外,crates/anvil/tests/it/tempo.rs#L5888 的 Anvil 集成测试同样使用provider.estimate_gas(request.browser_wallet_gas_estimation_request())来模拟真实 RPC 估算流程,进一步印证了该请求结构是 Tempo 网络上的标准估算形态。

四、为什么要"保守":设计与使用要点

4.1 保守 = 宁高勿低

WebAuthn 在三种签名类型中占位最大(1400 字节),当提示不完整时选择它作为兜底,能确保估算出的 gas 足以覆盖后续任何一种真实签名。这与"精确但可能偏低"相比,牺牲了少量精度,换来了估算结果一定可用的确定性——对浏览器钱包这类"先估算、后签名"的流程而言,这是更安全的设计取向。

4.2 与 viem 生态对齐

占位常量直接对齐了 viem Tempo Formatter 的行为(代码注释中给出了对应源码引用的具体行号)。这意味着无论用户侧用 viem 构造请求、还是用 Foundry 的cast/forge构造请求,在"未签名 → 估算"阶段看到的keyData都是同一份 1400 字节占位,RPC 端无需区分请求来源。

4.3 使用前提与限制

  • 该逻辑仅在Tempo 网络chain.is_tempo())上生效,其他网络走默认的克隆实现;
  • 仅当交易尚未设置 gas limit时才会触发估算补全;
  • 补全后的请求只用于estimate_gas调用,不会污染用户原始的tx对象——方法返回的是克隆副本;
  • 用户如果明确提供了完整的签名提示,则不受此逻辑影响。

五、总结

这条foundry-common: patch变更虽然只有一句话,背后却是一套完整的"补全 + 保留 + 转换"机制:在FoundryTransactionBuilder::browser_wallet_gas_estimation_request中,通过TEMPO_BROWSER_WEBAUTHN_DATA_SIZE = 1400(编码为0x0578)对不完整的浏览器钱包估算提示做保守回退,同时由 crates/common/src/tempo/tests.rs 的四个用例覆盖全部输入组合,并贯通cast estimatecast sendforge createforge script四大调用链。对于在 Tempo 网络上开发浏览器钱包集成、或排查"估算 gas 偏低导致交易失败"的开发者来说,这条兜底路径值得重点了解。

【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询