ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

如何用 AbiFormatter 把 fuels-rs 中的脚本交易解码为可读的合约函数调用

如何用 AbiFormatter 把 fuels-rs 中的脚本交易解码为可读的合约函数调用 如何用 AbiFormatter 把 fuels-rs 中的脚本交易解码为可读的合约函数调用【免费下载链接】fuels-rsFuel Network Rust SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-rs在 Fuel 链上合约调用以脚本交易script transaction的形式执行。拿到一笔交易的 ID 后你能看到的只是script字节码和script_data编码数据无法直接知道它调用了哪个合约方法、传了什么参数。fuels-rs 提供了ScriptType::detect和ABIFormatter两个工具前者判断一笔脚本交易是合约调用、loader 脚本还是其他脚本后者在持有合约 ABI 文件时把函数选择器和编码参数还原成可读文本例如initialize_counter(42)。本文的主路径是在 Rust 项目中通过Provider取回一笔脚本交易判定其类型再用ABIFormatter解码出函数名和参数。适用环境是已依赖fuels的 Rust 项目并且你拥有被调用合约的 ABI JSON 文件由 Sway 构建产物out/release/contract-abi.json生成。注意命名文档正文写作AbiFormatter但源码中的实际类型名是ABIFormatter本文代码一律使用ABIFormatter。准备条件完成主路径需要三样东西一笔可查询的脚本交易 ID。最简单的方式是先用 SDK 自己发一笔合约调用从响应里取tx_id示例代码即如此也可以是对已有链上交易做离线解码。一个连接到节点的Provider能够执行get_transaction_by_id。被调用合约的 ABI JSON 文件。解码参数依赖 ABI 中的函数签名与类型定义没有对应 ABI 就无法还原参数。相关入口调试文档描述了这个能力和示例输出完整可运行示例在 examples/contracts/src/lib.rs 的decoding_script_transactions测试中ABIFormatter实现见 packages/fuels-core/src/codec/abi_formatter.rsScriptType实现见 packages/fuels-programs/src/debug.rs。取回交易并判断脚本类型以下代码来自仓库示例原样保留setup_program_test!测试脚手架它负责启动本地节点、生成钱包并部署contract_test合约project和 ABI 路径指向 fuels-rs 仓库自带的 Sway 测试工程换成你自己的项目时请替换为你自己的 Sway 合约工程路径use fuels::prelude::*; use fuels::programs::debug::ScriptType; setup_program_test!( Abigen(Contract( name MyContract, project e2e/sway/contracts/contract_test )), Wallets(wallet), Deploy( name contract_instance, contract MyContract, wallet wallet ) ); // 先发起一次合约调用拿到交易 ID let tx_id contract_instance .methods() .initialize_counter(42) .call() .await? .tx_id .unwrap(); let provider: Provider wallet.provider(); // 第一步确认取回的是脚本交易 let TransactionType::Script(tx) provider .get_transaction_by_id(tx_id) .await? .unwrap() .transaction else { panic!(Transaction is not a script transaction); }; // 第二步判断脚本属于哪种类型 let ScriptType::ContractCall(calls) ScriptType::detect(tx.script(), tx.script_data())? else { panic!(Script is not a contract call); };ScriptType::detect(tx.script(), tx.script_data())接收交易的script字节码与script_data返回三种互斥结果变体含义能拿到什么ContractCall(VecContractCallData)合约调用可能是多个调用每个ContractCallData含contract_id、amount、asset_id、fn_selector_encoded、encoded_args、gas_forwardedLoader { script, blob_id }通过 blob 加载大合约的 loader 脚本blob IDOther(ScriptCallData)既不是合约调用也不是 loader脚本数据data与数据段data_section()含 configurables文档说明这三种正是 SDK 能区分的脚本交易类别。如果detect的结果不是ContractCall就无法走函数选择器解码需要按对应分支处理见下文。用 ABIFormatter 解码函数选择器与参数确认是合约调用后每个ContractCallData自带函数选择器即方法名原文和编码参数。解码步骤let json_abi std::fs::read_to_string( ../../e2e/sway/contracts/contract_test/out/release/contract_test-abi.json, )?; let abi_formatter ABIFormatter::from_json_abi(json_abi)?; let call calls[0]; let fn_selector call.decode_fn_selector()?; let decoded_args abi_formatter.decode_fn_args(fn_selector, call.encoded_args.as_slice())?; eprintln!( The script called: {fn_selector}({}), decoded_args.join(, ) );要点ABIFormatter::from_json_abi接受 ABI JSON 字符串内部按函数名建立参数类型表实现见 abi_formatter.rs 的from_json_abi/from_abi。decode_fn_selector把fn_selector_encoded字节还原为 UTF-8 函数名字符串。decode_fn_args按 ABI 中的参数类型列表解码encoded_args返回VecString每个元素是一个参数的调试格式字符串可直接拼进日志。示例测试对上面的代码打印的输出文档示例The script called: initialize_counter(42)一笔交易可能包含多个合约调用ContractCall携带的是VecContractCallData仓库的 e2e 测试 e2e/tests/debug_utils.rs 展示了多调用场景——call_descriptions.len() 2时逐个取出选择器与参数解码两个调用分别得到check_struct_integrity与i_am_called_differently参数解码结果为类似AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }的调试字符串。其他两种脚本类型的解码方式主路径之外的两个分支同样基于同一套工具文档与 e2e 测试给出了对应做法loader 脚本ScriptType::Loader { script, blob_id }分支可以直接读取blob_id[u8; 32]这是 loader 脚本解码的核心产物。普通 Sway 脚本OtherScriptCallData提供data脚本参数和data_section()configurables 所在的数据段。e2e 测试 e2e/tests/debug_utils.rs 展示了解码方式let ScriptType::Other(desc) ScriptType::detect(tb.script, tb.script_data).unwrap() else { panic!(expected a script); }; // 解码 main 函数的参数 let args decoder.decode_fn_args(main, desc.data.as_slice())?; // 解码 configurables仅当存在数据段时 let configurables decoder.decode_configurables(desc.data_section().unwrap())?;decode_fn_args对脚本按函数名main解码decode_configurables返回Vec(名称, 调试字符串)例如 e2e 测试断言得到(A_NUMBER, 11)与(MY_STRUCT, MyStruct { number: 10, boolean: true })该测试中的预期值。脚本没有数据段时data_section()返回None此时只做参数解码即可。结果判断与常见报错解码结果可以直接参与断言e2e 测试的做法就是比对decode_fn_args返回的字符串向量与预期调试格式如上文check_struct_integrity的结构体字符串。两类报错有明确来源便于定位问题函数不在 ABI 中decode_fn_args会返回 Codec 错误Function fn_name not found in the ABI见 abi_formatter.rs 中的测试。此时说明选择器解出的函数名与你加载的 ABI 对不上——通常是 ABI 文件与合约二进制版本不一致。选择器不是合法 UTF-8decode_fn_selector返回 Codec 错误cannot decode function selector: invalid utf-8 sequence of 1 bytes from index 0见 debug.rs 中的测试。这说明交易数据与预期不符不应按合约调用继续解码。限制参数解码强依赖 ABI 文件没有 ABI 时只能拿到函数名decode_fn_selector不依赖 ABI拿不到参数内容。ScriptType::detect只覆盖文档列出的三类脚本Other分支下脚本参数必须按函数名通常为main手动指定后才能解码。本路径要求交易以TransactionType::Script形式存在于所连节点get_transaction_by_id返回None时交易 ID 在该节点上查不到需换一个能查询到该交易的 Provider。下一步可以查看 调试文档 与 调试入口页以及ABIFormatter的 rust 文档文档提到 configurables 解码的更多细节以 rust docs 为准。【免费下载链接】fuels-rsFuel Network Rust SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-rs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表