fuels-ts 脚本日志解析实战:用 `logs` 与 `groupedLogs` 读取 Sway 脚本日志

发布时间:2026/9/9 22:25:38

fuels-ts 脚本日志解析实战:用 `logs` 与 `groupedLogs` 读取 Sway 脚本日志 fuels-ts 脚本日志解析实战用logs与groupedLogs读取 Sway 脚本日志【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts本文以 Fuel Network TypeScript SDKfuels-ts的官方指南 apps/docs/src/guide/scripts/logs.md 为核心系统讲解如何在 TypeScript 侧解析 Sway 脚本Script在执行过程中产生的日志。脚本一旦在 Sway 中调用log(...)就会产生一条写入交易回执receipt的日志条目同时变量类型会记录在脚本的 ABI 中本指南将演示 SDK 如何借助 ABI 将这些原始数据解析为 TypeScript 类型并通过脚本调用响应的logs与groupedLogs属性按需读取。读完本文你将掌握「读取脚本自身日志」与「按合约/脚本来源区分读取分组日志」两种模式并理解其背后的回执解码链路。背景Sway 的log与 SDK 的日志解码机制在 Fuel 链上无论是 Sway 合约还是脚本都可以通过内置的log()函数打印值。从 fuels-ts 的角度看日志的流转过程是脚本执行到log(value)时Fuel 虚拟机把日志写入交易结果TransactionResult的回执数组receipts中日志条目的具体字段类型由程序 ABI 记录fuels-ts 拿到回执后结合本次调用涉及的 ABI脚本自身 ABI 被调用合约的 ABI把原始回执解码成真实的 TypeScript 值。因此日志解析的前提条件是代码已通过 typegen 生成类型安全的脚本/合约封装本文示例中的ScriptLogSimple、ScriptLogWithContract即生成产物。关于脚本实例化与调用的一般流程可参考 运行脚本指南。解码结果统一放在脚本调用响应的两个属性中logs一维数组按发生顺序汇总本次调用产生的全部日志groupedLogs对象Recordstring, T[]按日志来源合约地址 / 脚本占位地址分组。下面分别通过两个官方示例说明。简单场景用logs读取脚本自身日志Sway 侧一段只打日志的脚本官方示例脚本位于 apps/docs/sway/script-log-simple/src/main.swSway 代码非常简短script; fn main(log_value: str[7]) - str[7] { log(log_value); log_value }要点分析函数入参log_value: str[7]是一个长度固定的字符串数组string array类型str[7]表示恰好容纳 7 个字符log(log_value)是 Sway 内置日志函数把该字符串写入回执函数随后将入参原样返回这并不影响日志行为只是展示「脚本既能返回值、也能附带日志」的常见形态。TypeScript 侧读取logs属性对应 TypeScript 示例见 apps/docs/src/guide/scripts/snippets/script-log-simple.ts完整代码如下import { Provider, Wallet } from fuels; import { WALLET_PVT_KEY, LOCAL_NETWORK_URL } from ../../../env; import { ScriptLogSimple } from ../../../typegend; const provider new Provider(LOCAL_NETWORK_URL); const wallet Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); const script new ScriptLogSimple(wallet); const { waitForResult } await script.functions.main(ScriptA).call(); const { logs } await waitForResult(); // logs: [ScriptA]逐步拆解这段代码连接与签名者准备new Provider(LOCAL_NETWORK_URL)建立与本地 Fuel 节点的连接Wallet.fromPrivateKey(WALLET_PVT_KEY, provider)用测试私钥派生签名者。LOCAL_NETWORK_URL与WALLET_PVT_KEY是文档项目测试环境变量见 apps/docs/src/env.ts 及docs应用的package.json脚本通常由本地启动的 Fuel 节点提供。实例化脚本封装new ScriptLogSimple(wallet)使用 typegen 生成的类创建脚本调用载体其方法名与 ABI 中的main一一对应。发起调用script.functions.main(ScriptA).call()把字符串参数编码进脚本字节码输入并提交返回调用句柄。注意ScriptA恰好 7 个字符与 Sway 侧str[7]类型匹配。等待出块并取日志await waitForResult()等待交易被确认也可用 pre-confirmation 流程提前拿到结果解构出logs属性。执行后logs的值就是[ScriptA]—— Sway 里log(log_value)记录的原始值在 SDK 侧已被还原为同等的 TypeScript 字符串。示例文件末尾还附带了断言逻辑校验日志长度与内容用于测试场景const expectedLogs [ScriptA]; console.log( Logs should have the same length, logs.length expectedLogs.length ); console.log( Logs should be in the correct format, expectedLogs.every((log) logs.includes(log)) );分组场景用groupedLogs区分日志来源当脚本调用合约、而合约内部也打印日志时仅凭一维的logs很难判断每条日志出自谁。此时应使用响应的groupedLogs属性——它按「日志产生者的地址」把日志归类。Sway 侧脚本内跨合约调用并各自打日志示例脚本位于 apps/docs/sway/script-log-with-contract/src/main.swscript; use log_simple_abi::LogSimple; fn main(contract_id: b256) { log(Script started); let log_contract abi(LogSimple, contract_id); log_contract.log_simple(__to_str_array(ContractA)); log(Script finished); }要点分析脚本先log(Script started)随后通过 ABI 接口LogSimple调用外部合约入参contract_id: b256是合约地址让合约打印ContractA脚本最后再log(Script finished)被调合约的 ABI 定义在 apps/docs/sway/log-simple-abi/src/main.swabi LogSimple { fn log_simple(val: str[9]); }即接收一个 9 字符的定长字符串并打印__to_str_array(ContractA)是 Sway 提供的字符串字面量转定长数组辅助函数配合str[9]使用。TypeScript 侧按来源读取groupedLogs完整示例见 apps/docs/src/guide/scripts/snippets/script-log-with-contract.tsimport { Provider, Wallet, ZeroBytes32 } from fuels; import { WALLET_PVT_KEY, LOCAL_NETWORK_URL } from ../../../env; import { LogSimpleFactory, ScriptLogWithContract } from ../../../typegend; const provider new Provider(LOCAL_NETWORK_URL); const wallet Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); // Create a contract instance const { waitForResult: waitForDeploy } await LogSimpleFactory.deploy(wallet); const { contract } await waitForDeploy(); // Create a script instance const script new ScriptLogWithContract(wallet); // Call the script const { waitForResult } await script.functions .main(contract.id.toB256()) .addContracts([contract]) .call(); // Wait for the script to finish and get the logs const { groupedLogs } await waitForResult(); // groupedLogs { // [ZeroBytes32]: [Script started, Script finished], // [contract.id.toB256()]: [ContractA], // }与简单场景相比这里有三个关键差异先部署被调合约LogSimpleFactory.deploy(wallet)是 typegen 生成的合约工厂waitForDeploy()返回{ contract }拿到合约实例及其id。把合约加入调用上下文script.functions.main(contract.id.toB256()).addContracts([contract]).call()中contract.id.toB256()作为 Sway 侧main的参数b256地址.addContracts([contract])则告诉 SDK 本次调用会触及该合约从而把它的 ABI 纳入日志解码范围。这一步对分组日志的解码至关重要——缺少 ABIContractA这类值将无法还原类型。读取groupedLogs结果是以 32 字节十六进制地址为键的对象。脚本没有地址为何使用ZeroBytes32需要特别说明的是脚本Script本身没有 ID 或地址但它仍能产生日志。为了让分组结果统一、可索引SDK 使用一个全零的十六进制地址32 字节 b256 常量即ZeroBytes32值为0x0000...00作为「脚本自身」的分组键。因此上面示例的结果结构为groupedLogs[ZeroBytes32]→ 脚本直接打印的两条[Script started, Script finished]groupedLogs[contract.id.toB256()]→ 被调合约打印的一条[ContractA]。示例文件尾部同样提供了针对该结构的断言console.log( Grouped logs should be defined, typeof groupedLogs ! undefined ); console.log( Should have zeroed log, typeof groupedLogs[ZeroBytes32] ! undefined ); console.log( Should script logs [Script Started], groupedLogs[ZeroBytes32].includes(Script started) ); console.log( Should script logs [Script Finished], groupedLogs[ZeroBytes32].includes(Script finished) );在实际应用中你可以通过判断键是否等于ZeroBytes32来区分「脚本自身日志」与「合约日志」从而在 UI、事件总线或测试断言中按来源分派处理。深入底层logs/groupedLogs是如何解码出来的了解了用法后不妨顺着源码确认一下 SDK 内部的处理链路本仓库内实现路径如下。当脚本调用等待结果时FunctionResult的组装集中在 packages/program/src/response.ts 的buildFunctionResult。其流程是先transactionResponse.waitForResult()拿到交易结果与回执数组再通过getAllResultLogs({ receipts, mainCallConfig, functionScopes })同时产出logs与groupedLogs见该文件第 136–168 行最终把两者随value、gasUsed、transactionId等一起放入FunctionResult返回。也就是说logs/groupedLogs并非交易响应的原始字段而是 SDK 在每次调用后即时解码生成的。更底层的逐条解码与分组逻辑位于 packages/account/src/providers/transaction-response/getAllDecodedLogs.ts 的getAllDecodedLogs它遍历每条回执结合本次调用收集到的全部 ABI脚本 ABI .addContracts()传入的合约 ABI把 Log 数据解码为类型化条目logEntry解码结果以归约reduce方式累积累积器始终维护{ logs, groupedLogs }logs是平铺的完整列表groupedLogs[receipt.id]则把每条日志追加到其来源地址即回执中的id对应的分组数组中。从源码结构可以推断正因为分组键直接取自回执的id字段而被调合约执行日志时其id即为合约地址脚本自身的日志则需要一个占位标识——这就是官方文档强调使用零地址ZeroBytes32的原因。另外仓库的集成测试也对分组日志有直接覆盖例如 packages/fuel-gauge/src/advanced-logging.test.ts 等多处用例都解构并断言了groupedLogs可作为理解实际数据结构与断言的参考。小结与使用建议只关心脚本自身打印的值用脚本调用响应的logs一维数组按顺序读取即可前提是传入的参数类型与 Sway 侧的定长类型如str[7]严格一致。脚本调用了合约、需要区分日志来源用groupedLogs并以ZeroBytes32定位脚本日志、以contract.id.toB256()定位各合约日志调用时务必通过.addContracts([...])把涉及合约的 ABI 提供给 SDK否则对应日志无法被正确解码。注意解码前置条件日志值能还原为 TypeScript 类型依赖 typegen 生成的封装类以及本地运行节点LOCAL_NETWORK_URL与测试钱包WALLET_PVT_KEY就绪相关运行环境的搭建可参考 部署脚本指南 与 运行脚本指南。【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/9 22:25:38

STM32F407四路电机编码器驱动与PID角度控制实战

简介:面向需要同时管理多路步进电机的嵌入式开发者,这套基于STM32F407VET6的驱动工程实现了四路电机的速度与方向控制,并通过编码器反馈完成闭环角度精确定位,适用于机器人、自动化设备等运动控制场景。压缩包共171个文件&#xf…

2026/9/9 22:20:37

基于灰狼算法的PID参数整定:原理、Matlab实现与仿真分析

拿到“基于灰狼算法的PID参数整定”这个题目时,很多人第一反应都是:灰狼算法和PID,这不是两个世界的东西吗?一个来自仿生智能优化,一个是控制理论里的常青树。但恰恰是这种组合,在课程设计、毕业设计、研究…

2026/9/9 23:10:49

图片无损放大哪个方法好?四个实用方法来帮你

在日常工作和学习生活中,相信很多人都遇到过这样的尴尬场景:好不容易找到一张满意的素材图,结果分辨率过低,一放大就全是马赛克。无论是做PPT汇报、设计海报,还是处理老旧照片,怎么把图片放大四倍保持清晰度…

2026/9/9 23:10:49

矩阵方法:从SVD到PCA,数据分析与信号处理的共同语言

在数据分析和信号处理的学习路径里,矩阵方法(Matrix Methods)是一个绕不开的核心主题。麻省理工学院的 18.065 课程 Matrix Methods in Data Analysis, Signal Processing, and Machine Learning 正是围绕这个主题展开的系统课程,…

2026/9/9 23:10:49

用Python实现剪映关键帧批量处理:桌面版源码解析

简介:面向视频剪辑与Python开发者的剪映关键帧自动化工具源码包,用于解决人工逐帧筛选效率低、剪辑节奏把控难的问题。工具基于Python实现视频关键帧的识别与智能筛选,可按用户设定条件自动挑选最佳剪辑点,并开放参数调整接口&…

2026/9/9 13:11:35

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/9 0:00:48

MHS模型硬件标准:让大模型像调用软件一样控制物理设备

让Claude真正看着显微镜说“这个细胞形态不太对”,或者让大模型自己调一版机械臂的运动轨迹,这事儿听上去已经很接近科幻片了。但你真上手试一次就会发现,模型不缺智商,缺的是一个能插进显微镜、机械臂、激光控制器里的“通用插座…

2026/9/9 0:00:48

AI五大核心方向详解:从机器学习到大模型,零基础转行选哪条?

会有人告诉我,他想转行学AI,但打开招聘网站一看直接傻眼:机器学习、深度学习、自然语言处理、计算机视觉、大模型应用……满屏都是这些词,好像每个都会一点,又好像每个都离自己很远。还有人上来就问“学Python还是学Ja…

2026/9/9 0:00:49

从50行最小循环到生产级AI引擎:工程化改造全解析

直接说干货。这一章我写的不是那种"hello world跑通某个模型"的教程,而是把AI引擎当做一个真正要上线、要被人调用、要扛流量的系统来聊。从最初只有50行的最小循环,到能够承载生产流量的AI引擎,中间差的不是代码量,而是…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/9 10:21:54

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码