fuels-ts 自定义交易实战:用 ScriptTransactionRequest 与 assembleTx 完成多资产转账到合约

发布时间:2026/9/6 21:25:42
fuels-ts 自定义交易实战:用 ScriptTransactionRequest 与 assembleTx 完成多资产转账到合约 fuels-ts 自定义交易实战用 ScriptTransactionRequest 与 assembleTx 完成多资产转账到合约【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts本篇基于 fuels-ts 官方文档《Custom Transactions》讲解当一笔交易涉及多种程序类型与多种资产时如何手动构建自定义交易从 Sway 脚本编写到用ScriptTransactionRequest逐步填充脚本字节码、main函数入参、合约输入/输出再到通过provider.assembleTx完成资源估算与注资、最终发送并验证合约余额变化的完整链路。读完本篇你将掌握在 fuels-ts 中脱离高层封装、自主控制交易请求每一步的实战能力并理解assembleTx底层参数如feePayerAccount、accountCoinQuantities、changeOutputAccount的源码级语义。什么场景需要“自定义交易”fuels-ts 中的高层 API如contract.functions.xxx()已经为常见的合约调用封装好了交易组装流程但存在一类场景一笔交易需要同时涉及多种程序类型script、contract、predicate 等与多种资产或者交易结构无法用“一次合约调用”表达例如把两种不同的资产分别转给同一个合约。这类场景可以通过实例化ScriptTransactionRequest来完成该类允许你在同一个交易中追加多种程序类型的输入/输出并逐个资产地声明资源需求。核心文档见 custom-transactions.md。第一步编写 Sway 侧的多资产转账脚本以“向合约转账两种资产”为例对应 Sway 源码位于 script-transfer-to-contractscript; use std::asset::transfer; fn main( contract_address: b256, asset_a: AssetId, amount_asset_a: u64, asset_b: AssetId, amount_asset_b: u64, ) - bool { let wrapped_contract ContractId::from(contract_address); let contract_id Identity::ContractId(wrapped_contract); transfer(contract_id, asset_a, amount_asset_a); transfer(contract_id, asset_b, amount_asset_b); true }几个关键点脚本的main接收 5 个参数合约地址b256、两个AssetId以及对应的转账数量u64。脚本本身不“拥有”资源它只发出transfer调用实际的输入/输出资源必须由 TypeScript 侧在交易中准备好Identity::ContractId是 Fuel 中对transfer目标身份的统一抽象资产可以转给账户、合约等身份该脚本在 apps/docs/sway/Forc.toml 中注册编译产物字节码与 ABI会通过 typegen 生成ScriptTransferToContract类供 TypeScript 引用。第二步TypeScript 侧构建 ScriptTransactionRequest文档给出的执行片段完整可运行代码见 script-custom-transaction.ts分为 5 步import { BN, ScriptTransactionRequest, coinQuantityfy } from fuels; import { ASSET_A, ASSET_B, launchTestNode } from fuels/test-utils; // 1. Create a script transaction using the script binary const request new ScriptTransactionRequest({ ...defaultTxParams, gasLimit: 3_000_000, script: ScriptTransferToContract.bytecode, }); // 2. Instantiate the script main arguments const scriptArguments [ contract.id.toB256(), { bits: ASSET_A }, new BN(1000), { bits: ASSET_B }, new BN(500), ]; // 3. Populate the script data and add the contract input and output request .setData(ScriptTransferToContract.abi, scriptArguments) .addContractInputAndOutput(contract.id); // 4. Estimate and fund the transaction const { assembledRequest } await provider.assembleTx({ request, feePayerAccount: wallet, accountCoinQuantities: [ { amount: 1000, assetId: ASSET_A, account: wallet, changeOutputAccount: wallet, }, { amount: 500, assetId: ASSET_B, account: wallet, changeOutputAccount: wallet, }, ], }); // 5. Send the transaction const tx await wallet.sendTransaction(assembledRequest); await tx.waitForResult(); const contractFinalBalanceAssetA await contract.getBalance(ASSET_A); const contractFinalBalanceAssetB await contract.getBalance(ASSET_B);逐步说明创建请求new ScriptTransactionRequest({ script, gasLimit, ... })。传入script脚本字节码与gasLimit示例中用 3,000,000 覆盖默认值defaultTxParams.gasLimit 10000。gasLimit只是初始值后续assembleTx会依据 dry-run 的 gas 实际消耗回填实例化main入参参数顺序必须与 Sway 脚本main签名一致——合约地址、ASSET_A的AssetId、数量 1000、ASSET_B的AssetId、数量 500。注意AssetId需用{ bits: ASSET_A }包装对应 ABI 中AssetId类型的编码约定u64数量用BN表达填充脚本数据并挂上合约输入/输出setData(abi, args)把参数按 ABI 编码进scriptDataaddContractInputAndOutput(contract.id)同时 push 一条InputContract与一条OutputContract使合约成为交易可访问的资源估算与注资provider.assembleTx接收accountCoinQuantities数组——这里声明了两个条目ASSET_A需 1000、ASSET_B需 500这是“多资产”交易的核心表达方式发送并验证wallet.sendTransaction提交组装好的请求waitForResult等待执行完成后用contract.getBalance(assetId)分别校验合约侧两种资产的最终余额。源码解读ScriptTransactionRequest 的关键方法以下实现事实来自 script-transaction-request.ts 对应的实际文件 packages/account/src/providers/transaction-request/script-transaction-request.ts构造器L65-L71若未提供script或scriptData会回退到内置的returnZeroScript空转脚本即new ScriptTransactionRequest({})得到一个合法但什么都不做的 Script 交易请求。gasLimit经bn()转为BN类型setDataL275-L279内部通过new Interface(abi).functions.main.encodeArguments(args)对main参数编码因此传入的 ABI 必须是脚本的 JSON ABItypegen 产物ScriptTransferToContract.abi参数数量与类型错误会在编码阶段暴露addContractInputAndOutputL235-L255先做去重——若已存在同一contractId的InputContract则直接返回避免重复挂接否则 push 一条InputType.Contract带占位txPointer和一条指向该输入索引的OutputType.Contract并返回this以支持链式调用可变输出L171-L198如果脚本需要从交易中“取出”资源还可调用addVariableOutputs(number)或addVariableOutput(to, amount, assetId)pushOutputType.Variable由节点在执行时填充具体地址与数量——这是自定义交易中常见的“多输出”场景toTransactionL106-L119将请求序列化为TransactionScript自动计算scriptLength、scriptDataLength并初始化receiptsRoot是assembleTx将请求转为字节toTransactionBytes提交 dry-run 的基础类上还带有getContractInputs / getContractOutputs / getVariableOutputs等过滤方法便于在自定义逻辑中读取已挂接的资源。从源码结构看ScriptTransactionRequest继承自BaseTransactionRequestinputs/outputs/witnesses的管理都复用基类开发者只需关注“脚本特有”的部分script、scriptData、gasLimit与资源的追加。assembleTx估算与注资的底层参数上一步的provider.assembleTx是 SDK 中所有高层 API 共用的交易组装入口账户转账、合约/ blob 部署、合约调用均由它驱动其实现位于 provider.ts参数定义见 AssembleTxParams。结合 assemble-tx.md 的说明关键参数如下参数必填说明request是要组装的交易请求本例为ScriptTransactionRequestfeePayerAccount是支付交易费的账户若accountCoinQuantities中未单独指定account默认由它出资源accountCoinQuantities否*资源需求数组每项含amount不含费用、assetId、account默认feePayerAccount、changeOutputAccount默认account多资产交易就是靠多个条目表达blockHorizon否gas 价格估算向前看的区块数默认10estimatePredicates否是否为 predicate 估算 gas默认trueresourcesIdsToIgnore否注资时要忽略的资源UTXO 或 messageIDreserveGas否额外预留的 gas 量* 若交易只需支付费用不涉及额外资产可省略。每个assetId只能有一个 change 输出这是理解changeOutputAccount的关键。Fuel 采用 UTXO 模型交易会整体花费被选中的 UTXO即使实际只用其中一小部分花费后剩余的部分change会发往OutputChange指定的地址。由于同一交易内每个assetId只允许一条OutputChange当多个账户提供同一assetId的资源时只能由一个账户收回找零——changeOutputAccount就是用来显式指定这个“找零接收方”的。源码中可以看到对应逻辑provider.assembleTx会为每个accountCoinQuantities条目生成changePolicy: { change: address }且当feePayerAccount未出现在任何条目中时会为其追加一条金额为 0 的基础资产条目以保证费用来源provider.ts L1772-L1823。本文示例中单一钱包既付费又出资account与changeOutputAccount都显式设为wallet是最直观也最不易出错的写法。AssembleTxResponse返回assembledRequest已填充全部输入/输出/policies 的最终请求、gasPrice估算的气价以及 dry-run 产生的receipts/rawReceipts可直接用于后续发送或断言。完整示例与验证将 Sway 脚本与 TypeScript 步骤组合起来的完整可运行示例如下对应文档中#full区域源文件为 script-custom-transaction.tsimport { BN, ScriptTransactionRequest, coinQuantityfy } from fuels; import { ASSET_A, ASSET_B, launchTestNode } from fuels/test-utils; import { EchoValuesFactory } from ../../../typegend/contracts/EchoValuesFactory; import { ScriptTransferToContract } from ../../../typegend/scripts/ScriptTransferToContract; using launched await launchTestNode({ contractsConfigs: [{ factory: EchoValuesFactory }], }); const { contracts: [contract], wallets: [wallet], provider, } launched; const defaultTxParams { gasLimit: 10000, }; // 1. Create a script transaction using the script binary const request new ScriptTransactionRequest({ ...defaultTxParams, gasLimit: 3_000_000, script: ScriptTransferToContract.bytecode, }); // 2. Instantiate the script main arguments const scriptArguments [ contract.id.toB256(), { bits: ASSET_A }, new BN(1000), { bits: ASSET_B }, new BN(500), ]; // 3. Populate the script data and add the contract input and output request .setData(ScriptTransferToContract.abi, scriptArguments) .addContractInputAndOutput(contract.id); // 4. Estimate and fund the transaction const { assembledRequest } await provider.assembleTx({ request, feePayerAccount: wallet, accountCoinQuantities: [ { amount: 1000, assetId: ASSET_A, account: wallet, changeOutputAccount: wallet, }, { amount: 500, assetId: ASSET_B, account: wallet, changeOutputAccount: wallet, }, ], }); // 5. Send the transaction const tx await wallet.sendTransaction(assembledRequest); await tx.waitForResult(); const contractFinalBalanceAssetA await contract.getBalance(ASSET_A); const contractFinalBalanceAssetB await contract.getBalance(ASSET_B);运行前提说明该示例依赖fuels/test-utils的launchTestNode本地测试节点以及 typegen 生成的EchoValuesFactory合约工厂与ScriptTransferToContract脚本封装脚本字节码与 ABI 均来自 Sway 侧编译产物EchoValuesFactory在本例中仅用于启动节点并部署一个合约实例作为转账目标ASSET_A/ASSET_B是test-utils提供的两个测试资产 ID断言逻辑为转账完成后合约在ASSET_A与ASSET_B上的余额应分别增加 1000 与 500以脚本执行true且余额校验通过为准。相关文档与测试自定义交易核心文档custom-transactions.mdassembleTx完整参数与 change 语义详解assemble-tx.md多账户同一资产 change 冲突的示例片段见 multiple-output-change.tsassembleTx的迁移指南旧版estimateAndFund用法对照assemble-tx-migration-guide.md源码中estimateAndFund已标注deprecated并指向该迁移文档script-transaction-request.ts L83-L99端到端行为验证assembleTx的集成测试位于 assemble-tx.test.ts覆盖了注资、dry-run 失败与资源处理等路径可作为自定义交易行为断言的参考【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考