如何用 Predicate 为 fuels-ts 合约调用支付 Gas 费用
2026/9/10 16:54:38 网站建设 项目流程

如何用 Predicate 为 fuels-ts 合约调用支付 Gas 费用

【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts

在 fuels-ts 中,合约调用默认由发起调用的钱包支付 gas 费用。如果你的业务场景类似文档中的流动性池合约——用户只需提供要存入的资产,而费用由另一方承担——可以把一个 Predicate 配置为feePayerAccount,让 Predicate 地址上的基础资产来支付本次交易的手续费。本文基于仓库中的 Custom Contract Call 文档 和 AssembleTx 文档,给出两条可执行的配置路径。

适用前提

  • 已有一个运行中的 Fuel 节点,可通过Provider连接(示例代码中的LOCAL_NETWORK_URL);
  • 持有一个有余额的钱包私钥(示例中的WALLET_PVT_KEY),用于部署合约、给 Predicate 转账;
  • 已完成合约和 Predicate 的编译,并通过 typegen 生成了对应的 TypeScript 类。文档示例使用了LiquidityPoolLiquidityPoolFactoryReturnTruePredicate三个生成类,Predicate 源码可参考仓库中的 return-true-predicate Sway 项目。

Predicate 的验证在链下完成,只有当 Predicate 返回true时,其地址上的资金才能被使用来支付费用;否则 SDK 会抛出验证错误。因此选择 Predicate 时,其验证逻辑必须在你执行合约调用时能够返回true

准备:先给 Predicate 充值

Predicate 要支付 gas,前提是它的地址上有足够的基础资产。文档示例中用钱包向 Predicate 转了500_000单位的基础资产:

const provider = new Provider(LOCAL_NETWORK_URL); const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); const predicate = new ReturnTruePredicate({ provider }); // 部署合约(省略部分:使用 configurableConstants 配置 TOKEN 资产 ID) const deploy = await LiquidityPoolFactory.deploy(wallet, { configurableConstants }); const { contract } = await deploy.waitForResult(); const contractId = contract.id.toB256(); // 给 Predicate 充值,供其支付后续交易的手续费 const res = await wallet.transfer(predicate.address, 500_000); await res.waitForResult();

500_000是文档示例值,你需要按实际预估的手续费调整。Predicate 余额不足时无法完成支付,这一点在 Send And Spend Funds From Predicates 文档 中有对应的失败示例。

主路径:通过 assembleTxParams 指定费用支付方

大多数情况下只需要在调用链上加一个.assembleTxParams(),把feePayerAccount指向 Predicate,并用accountCoinQuantities声明合约需要的资产由哪个账户提供:

// 创建合约实例 const liquidityPoolContract = new LiquidityPool(contractId, wallet); // 执行合约调用,指定 Predicate 作为费用支付方 const { waitForResult } = await liquidityPoolContract.functions .deposit({ bits: wallet.address.toB256() }) .callParams({ forward: [1000, TestAssetId.A.value], // 存入 1000 单位的 TOKEN 资产 }) .assembleTxParams({ feePayerAccount: predicate, // 使用 Predicate 支付手续费 accountCoinQuantities: [ { amount: 1000, assetId: TestAssetId.A.value, account: wallet, changeOutputAccount: wallet, }, ], }) .call(); const { transactionResult: { isStatusSuccess }, } = await waitForResult(); console.log('isStatusSuccess', isStatusSuccess);

各参数的作用(依据 AssembleTx 文档):

  • feePayerAccount: predicate:由该账户支付交易费用,SDK 内部用它估算并补足手续费所需的基础资产;
  • accountCoinQuantities:声明除手续费外交易需要的资产。amount是不含手续费的所需数量;account提供这些资源,缺省为feePayerAccountchangeOutputAccount接收该资产花费后的找零,缺省为account
  • forward: [1000, TestAssetId.A.value]是合约deposit方法本身要求转入的资产,与accountCoinQuantities声明的是同一笔资产。

结果验证:waitForResult()返回后检查transactionResult.isStatusSuccess,为true表示交易上链成功。

可选路径:手动调用 assembleTx

需要更细粒度控制时(例如在提交前预置交易、获取交易 ID),可以跳过 SDK 自动的assembleTx,改为手动组装后再提交:

// 创建调用作用域 const scope = liquidityPoolContract.functions .deposit({ bits: wallet.address.toB256() }) .callParams({ forward: [1000, TestAssetId.A.value], }); // 取出交易请求 const request = await scope.getTransactionRequest(); // 手动调用 assembleTx 估算并填充交易 await provider.assembleTx({ request, feePayerAccount: predicate, // 使用 Predicate 作为费用支付方 accountCoinQuantities: [ { amount: 1000, assetId: TestAssetId.A.value, account: wallet, changeOutputAccount: wallet, }, ], }); // 提交交易,跳过 SDK 的自动 assembleTx 步骤 const response = await scope .fromRequest(request) .call({ skipAssembleTx: true }); const { transactionResult: { isStatusSuccess }, } = await response.waitForResult(); console.log('isStatusSuccess', isStatusSuccess);

这条路径有两个关键点,缺一不可:

  1. fromRequest(request):把手动修改过的交易请求交还给调用流程,保证你手动做的改动在提交时生效;
  2. call({ skipAssembleTx: true }):告诉 SDK 跳过自动assembleTx。如果不跳过,SDK 会重新估算交易,覆盖掉你手动指定的 Predicate 费用支付逻辑。

provider.assembleTx()还支持blockHorizon(gas 价格估算向前看的块数,默认10)、estimatePredicates(是否为 Predicate 估算 gas)、reserveGas(额外预留 gas)等参数,完整参数表见 AssembleTx 文档。

常见失败与限制

Predicate 余额不足:Predicate 要支付的手续费超过其持有的基础资产时,转账/调用会失败。Send And Spend Funds From Predicates 文档 给出了这类场景下 SDK 抛出的错误信息(文档示例):

Insufficient funds or too many small value coins. Consider combining UTXOs. For the following asset ID: '{baseAssetId}'.

遇到该错误时,先给 Predicate 补充基础资产,再重新发起调用。

每个 assetId 只能有一个找零输出:Fuel 的 UTXO 模型中,交易会花费掉包含在内的全部 UTXO,找零(OutputChange)按assetId归到唯一账户。当同一资产来自多个账户时,只有changeOutputAccount指定的账户收到找零。如果你的调用涉及多个账户的同一资产,必须显式指定changeOutputAccount,否则会出现一个账户出资、另一个账户收回找零的预期外行为。

Predicate 验证失败:Predicate 返回false时,SDK 会直接抛出验证错误,交易不会上链。

参考文档

  • 合约调用中指定 Predicate 支付费用的两种方式:custom-contract-calls.md
  • assembleTx参数与默认行为:assemble-tx.md
  • Predicate 的资金流转与验证失败现象:send-and-spend-funds-from-predicates.md

【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts

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

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

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

立即咨询