fuels-rs 调用其他合约实战:with_contracts 与 with_contract_ids 的输入输出准备机制 fuels-rs 调用其他合约实战with_contracts 与 with_contract_ids 的输入输出准备机制【免费下载链接】fuels-rsFuel Network Rust SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-rs当 Sway 合约的方法内部会调用其他合约时交易必须包含相应的Input::Contract/Output::Contract否则运行时无法为该外部合约分配状态上下文。本文基于 fuels-rs 官方文档docs/src/calling-contracts/other-contracts.md完整讲解CallHandler提供的两个依赖注入方法with_contracts与with_contract_ids的用法差异、端到端测试的完整示例并结合源码剖析其底层实现链路帮助你在跨合约调用、日志解码和缺失合约修复场景中正确配置交易输入输出。背景为什么调用其他合约需要显式声明依赖在 FuelVM 的模型中合约本身不是活的进程每次调用合约都需要把合约二进制和状态上下文重新载入交易。因此如果你的合约方法内部会调用另一个合约例如链上 A 合约调用 B 合约你在构造这笔交易时就必须把 B 合约声明为交易的Input和Output这样 FuelVM 才能在执行过程中为 B 分配可读写内存与状态。这一点与 EVM 的心智模型不同在以太坊上你只需知道目标合约地址即可发起调用而 Fuel 要求在交易层面显式列出所有参与计算的合约。fuels-rs 的CallHandler调用链构建器替你完成这项工作——你只需要把外部合约喂给它它会在构建ScriptTransaction时自动生成对应的输入输出配对。两个方法with_contracts 与 with_contract_idsCallHandler提供了两个设置外部合约依赖的构建器方法builder method可按链式方式插在methods().xxx(...)之后、.call()之前with_contracts传入 abigen 生成的合约实例// 传入通过 abigen! 宏生成的合约实例 let response contract_caller_instance .methods() .increment_from_contract(lib_contract_id, 42) .with_contracts([lib_contract_instance]) .call() .await?;with_contracts([contract_instance, ...])要求你传入由abigen!宏创建的合约实例即实现了ContractDependencytrait 的实例。它的核心优势在于不仅会登记合约 ID还会把外部合约的LogDecoder合并进当前调用的日志解码器。这意味着外部合约发出的log事件以及require失败等回滚错误都能被调用方合约正确传播并解码而不是以裸字节形式出现在 receipts 中。with_contract_ids只传入合约 ID// 只传入合约 ID适合不需要解码外部合约日志的场景 let response contract_caller_instance .methods() .increment_from_contract(lib_contract_id, 42) .with_contract_ids([lib_contract_id]) .call() .await?;如果你不需要解码外部合约的日志或者手边根本没有abigen!生成的合约实例比如你只拿得到一个ContractId就可以改用with_contract_ids([contract_id, ...])直接提供所需的合约 ID 列表。这是更轻量、更通用的方式。端到端测试test_contract_calling_contract上面的两段代码并非凭空构造它们来自仓库中的 E2E 测试 test_contract_calling_contract。该测试的完整流程值得完整阅读因为它同时覆盖了两条路径和多种调用形态测试资产测试通过setup_program_test!宏同时部署了两个合约——被调用方 LibContract提供increment与require方法和调用方 LibContractCaller并且把LibContract部署了两份lib_contract_instance与lib_contract_instance2以模拟对两个不同合约的并发依赖。Sway 侧的实现ContractCaller通过abi(LibContract, contract_id.into())动态构造外部合约代理并发起调用例如fn increment_from_contract(contract_id: ContractId, value: u64) - u64 { let contract_instance abi(LibContract, contract_id.into()); contract_instance.increment(value) }单合约依赖with_contracts路径let response contract_caller_instance .methods() .increment_from_contract(lib_contract_id, 42) .with_contracts([lib_contract_instance]) .call() .await?; assert_eq!(43, response.value);多合约依赖测试中还演示了同时依赖两个LibContract实例的写法——注意两个实例类型相同但部署位置不同SDK 按传入顺序与合约 ID 对应let response contract_caller_instance .methods() .increment_from_contracts(lib_contract_id, lib_contract_id2, 42) // Note that the two lib_contract_instances have different types .with_contracts([lib_contract_instance, lib_contract_instance2]) .call() .await?; assert_eq!(86, response.value); // 43 43仅 ID 依赖with_contract_ids路径与第 3 步完全等价的调用只是用with_contract_ids([lib_contract_id])替代合约实例最终同样得到43。这个测试验证了一个关键结论两种写法在能否成功调用上没有区别区别只在于是否携带日志解码能力。源码剖析从 CallHandler 到 ContractDependencyConfigurator方法定义与日志解码器合并两个方法都定义在 CallHandler 上源码位于 call_handler.rswith_contract_idsL174-L178把 ID 切片拷贝成Vec后调用self.call.with_external_contracts(...)把外部合约登记到调用对象上。文档注释明确说明其效果是创建fuel_tx::Input::Contract/fuel_tx::Output::Contract配对并写入交易。with_contractsL188-L197在同样的登记动作之外多了一个循环——pub fn with_contracts(mut self, contracts: [dyn ContractDependency]) - Self { self.call self .call .with_external_contracts(contracts.iter().map(|c| c.id()).collect()); for c in contracts { self.log_decoder.merge(c.log_decoder()); } self }这里正是外部合约日志可解码的来源每个传入实例的LogDecoder被合并进CallHandler持有的解码器后续CallResponse::logs()与回滚错误解析都会用到它。ContractDependency trait合约实例为什么够用with_contracts的参数类型是dyn ContractDependency这个 trait 定义在 call_handler.rs L33-L36极其精简// Trait implemented by contract instances so that // they can be passed to the with_contracts method pub trait ContractDependency { fn id(self) - ContractId; fn log_decoder(self) - LogDecoder; }也就是说SDK 只需要合约实例暴露两样东西合约 ID 和日志解码器。任何由abigen!生成的合约实例都实现了该 trait因此可以无差别地传入with_contracts。ContractDependencyConfigurator依赖如何落到交易上with_external_contracts本身来自密封 trait ContractDependencyConfigurator。它同时为ContractCall与ScriptCall提供实现把 ID 写入各自结构体的external_contracts字段impl ContractDependencyConfigurator for ContractCall { fn with_external_contracts(self, external_contracts: VecContractId) - Self { ContractCall { external_contracts, ..self } } }在后续构建交易的环节external_contracts与主合约 ID 一起被收集为去重集合见 utils.rs 中的 extract_unique_contract_ids再由交易构建器转换为成对的Input::Contract/Output::Contract。这也解释了为什么同一个合约即使被多次调用也只需登记一次。进阶缺失合约的自动探测——determine_missing_contracts即使你声明了依赖如果 Sway 侧通过运行时变量例如配置常量或存储槽动态确定了被调用合约静态构建时也可能漏掉某些 ID。SDK 为此提供了一个自愈机制 determine_missing_contractspub async fn determine_missing_contracts(mut self) - ResultSelf { match self.simulate(Execution::realistic()).await { Ok(_) Ok(self), Err(Error::Transaction(Reason::Failure { ref receipts, .. })) { for contract_id in find_ids_of_missing_contracts(receipts) { self.call.append_external_contract(contract_id); } Ok(self) } Err(other_error) Err(other_error), } }其工作方式先以真实执行方式做一次模拟若模拟因PanicReason::ContractNotInInputs失败就通过 find_ids_of_missing_contracts 扫描 receipts从每条Receipt::Panic中提取缺失的合约 ID 并自动追加为外部依赖供下一次真实提交使用。这为合约 ID 在运行时才确定的场景提供了兜底手段。选型建议与小结场景推荐方法理由持有abigen!生成的合约实例且需要解码外部合约日志或require回滚信息with_contracts([instance, ...])自动合并LogDecoder日志与回滚可解码只有ContractId不关心外部合约日志with_contract_ids([id, ...])轻量、无需实例被调用合约 ID 运行时才确定、静态无法枚举with_contract_ids后配合determine_missing_contracts模拟失败自动补齐缺失依赖小结fuels-rs 中合约调用合约的核心约束是交易必须携带外部合约的Input/Output而CallHandler::with_contracts与with_contract_ids分别以合约实例和合约 ID两种粒度替你完成这项登记前者额外提供日志解码能力。参考文档 other-contracts.md、E2E 测试 contracts.rs 与源码 call_handler.rs即可在项目中正确接入跨合约调用。【免费下载链接】fuels-rsFuel Network Rust SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-rs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

最新新闻

单相全控桥晶闸管整流电路设计与仿真:从公式到Multisim实战

单相全控桥晶闸管整流电路设计与仿真:从公式到Multisim实战

简介:单相全控桥式晶闸管整流电路设计(纯电阻负载)是电力电子课程设计中常见的综合性课题,覆盖了主电路计算、触发电路设计与仿真验证等多个关键环节。文档围绕1000W输出功率、220V/50Hz交流输入、0~180移相范围的设计要求&#x…

2026/9/6 19:52:13
百度网盘解锁SVIP版下载不限速2025最新教程分享

百度网盘解锁SVIP版下载不限速2025最新教程分享

百度网盘解锁SVIP版下载不限速2025最新教程分享。你还在纠结怎么快速下载的时候。我已经找到对应的方法了。下面就是我测试的图片速度还是非常惊艳到我的。没想到这个方法竟然能让下载速度达到这个值,下面开始今天的教学首先,打开上面我给大家的方法这个…

2026/9/6 19:52:13
GoogleTest 断言宏完全参考:从 EXPECT/ASSERT 家族到浮点、异常与 Death Test 的实现剖析

GoogleTest 断言宏完全参考:从 EXPECT/ASSERT 家族到浮点、异常与 Death Test 的实现剖析

GoogleTest 断言宏完全参考:从 EXPECT/ASSERT 家族到浮点、异常与 Death Test 的实现剖析 【免费下载链接】googletest GoogleTest - Google Testing and Mocking Framework 项目地址: https://gitcode.com/GitHub_Trending/go/googletest GoogleTest 的断言…

2026/9/6 19:52:13
Rocket.Chat 密码重置邮件接口加固:未授权 DDP 方法 sendForgotPasswordEmail 的按客户端速率限制

Rocket.Chat 密码重置邮件接口加固:未授权 DDP 方法 sendForgotPasswordEmail 的按客户端速率限制

Rocket.Chat 密码重置邮件接口加固:未授权 DDP 方法 sendForgotPasswordEmail 的按客户端速率限制 【免费下载链接】Rocket.Chat The Secure CommsOS™ for mission-critical operations 项目地址: https://gitcode.com/GitHub_Trending/ro/Rocket.Chat 本文…

2026/9/6 19:52:13
HyperFrames 变更日志视频中的可视化路由表:从 visualization-registry 读懂“用画面代替文字“的决策体系

HyperFrames 变更日志视频中的可视化路由表:从 visualization-registry 读懂“用画面代替文字“的决策体系

HyperFrames 变更日志视频中的可视化路由表:从 visualization-registry 读懂"用画面代替文字"的决策体系 【免费下载链接】hyperframes Write HTML. Render video. Built for agents. 项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes …

2026/9/6 19:52:13
GitHub CLI 设计基础:gh 命令语言、终端排版与机器可读输出的设计原则及源码印证

GitHub CLI 设计基础:gh 命令语言、终端排版与机器可读输出的设计原则及源码印证

GitHub CLI 设计基础:gh 命令语言、终端排版与机器可读输出的设计原则及源码印证 【免费下载链接】cli GitHub’s official command line tool 项目地址: https://gitcode.com/GitHub_Trending/cli/cli GitHub CLI(gh)仓库中的设计 Pr…

2026/9/6 19:47:13