首页
/ fuels-ts 合约调用参数详解:callParams 的 forward 与 gasLimit 配置实战

fuels-ts 合约调用参数详解:callParams 的 forward 与 gasLimit 配置实战

2026-09-05 20:05:51作者:鲍丁臣Ursa

在 Fuel Network 上用 fuels-ts 调用合约函数时,SDK 提供了 callParams 方法让你针对单次合约调用配置专属参数,目前支持两类:forward(向合约转发指定数量的币)和 gasLimit(限定本次合约调用可消耗的 Gas 上限)。本文以官方文档 call-parameters.md 为核心,逐参数拆解用法与示例代码,并深入 @fuel-ts/program 包源码,说明这些参数在 SDK 内部如何被解析、如何校验、又如何影响最终组装出的交易请求。

1. 支持的调用参数与适用场景

调用合约时,callParams 方法可用的调用参数目前有两个:

  1. forward —— 在调用函数时向合约转账指定数量的币;
  2. gasLimit —— 限定本次合约调用本身可消耗的 Gas 上限(独立于交易整体 Gas)。

提示:调用合约时同样可以设置交易参数(transaction parameters),其用法参见仓库文档 Transaction Parameters

CallParams 的类型定义位于 packages/program/src/types.ts

export type CallParams = Partial<{
  forward: CoinQuantityLike;
  gasLimit: BigNumberish;
}>;

即两个字段均为可选:forward 接受"数量 + Asset ID"的组合,gasLimit 接受任意大数形态(numberstringBN 等)。

2. 文档使用的示例合约

文档示例统一基于仓库中的 return-context 合约,其实现见 apps/docs/sway/return-context/src/main.sw

// contract;
// use std::context::msg_amount;

// abi ReturnContext {
//     #[payable]
//     fn return_context_amount() -> u64;
// }

impl ReturnContext for Contract {
    #[payable]
    fn return_context_amount() -> u64 {
        msg_amount()
    }
}

该合约标记为 #[payable],并把 msg_amount()(调用时转发的币数量)作为返回值,天然适合演示 forward 参数;同时它执行足够简单,可用于演示 gasLimit 过小时触发 OutOfGas 的回滚行为。对应片段代码位于 apps/docs/src/guide/contracts/snippets/call-parameters/

3. Forward 参数:向合约转发币

forward 参数允许你在调用合约函数时向合约发送指定数量的币。这在合约函数执行需要币的场景中非常有用,例如向其他账户或合约转账。它帮助你控制分配给合约调用的资源,并保护你免受潜在的高成本操作影响。

完整可运行示例(摘自 forward.ts):

import { Provider, Wallet } from 'fuels';
import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env';
import { ReturnContextFactory } from '../../../../typegend';

const provider = new Provider(LOCAL_NETWORK_URL);
const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);

const deploy = await ReturnContextFactory.deploy(wallet);
const { contract } = await deploy.waitForResult();

const amountToForward = 10;
const baseAssetId = await provider.getBaseAssetId();

const { waitForResult } = await contract.functions
  .return_context_amount()
  .callParams({
    forward: [amountToForward, baseAssetId],
  })
  .call();

const { value } = await waitForResult();

console.log('forwarded amount:', value.toNumber());
// forwarded amount: 10

要点说明:

  • forward 采用 [数量, assetId] 元组形式传入,assetId 通过 provider.getBaseAssetId() 获取链上基础资产 ID;
  • 返回值正是合约侧 msg_amount() 读到的转发金额 10,验证了转发链路完整生效。

源码层面:forward 如何生效

packages/program/src/functions/invocation-scope.tsFunctionInvocationScope.callParams 中可以看到:

  1. payable 校验:如果设置了 forward 但目标函数没有 #[payable] 属性,SDK 会直接抛出 FuelErrorThe target function ... cannot accept forwarded funds as it's not marked as 'payable'.)——这就是为什么示例合约必须标注 #[payable]
  2. 数量归一化coinQuantityfy(callParams.forward)[amount, assetId] 元组转换为标准的 CoinQuantity 结构;
  3. 进入交易请求:在 base-invocation-scope.tscreateContractCall 中,forward.assetIdforward.amount 分别映射到 ContractCallassetIdamount 字段;getRequiredCoins 再把所有非零的转发金额汇总为交易所需币种,最终在 fundWithRequiredCoins 阶段自动选取钱包中的 coin 作为输入并完成资金注入。

4. Gas Limit 参数:限定单次合约调用的 Gas

调用参数的 gasLimit 指的是该次合约调用本身可消耗的最大 Gas,独立于交易其余部分。

示例(摘自 gas-fee.ts):

try {
  await contract.functions
    .return_context_amount()
    .callParams({
      forward: [10, await provider.getBaseAssetId()],
      gasLimit: 1,
    })
    .call();
} catch (e) {
  console.log('error', e);
  // error _FuelError: The transaction reverted with reason: "OutOfGas"
}

gasLimit 设为 1 会直接导致调用执行时 Gas 耗尽,交易以 "OutOfGas" 原因回滚,SDK 抛出 _FuelError。这正是该参数的价值所在:为合约调用设置成本护栏,防止意外的高成本执行。

源码层面:gasLimit 的流转与校验

  • callParams 被调用时,gasLimit 进入 callParameters,并通过 base-invocation-scope.tscreateContractCallgas: callParameters?.gasLimit 写入 ContractCall
  • 组装交易前,prepareTransaction 会执行 checkGasLimitTotalbase-invocation-scope.ts#L228-L239):交易级 gasLimit 必须大于等于所有调用 gasLimit 之和,否则抛出 Transaction's gasLimit must be equal to or greater than the combined forwarded gas of all calls.。若未设置交易 gasLimit,则自动取各调用 gasLimit 之和;
  • callParams 内部,只要任一调用设置了 gasLimit,就会置位 hasCallParamsGasLimit 标志(invocation-scope.ts#L77-L79),影响默认 Gas/费用推导逻辑(setDefaultTxParams),确保用户显式设置的值优先于估算值。

5. 调用参数 gasLimit vs 交易参数 gasLimit

这两个 gasLimit 作用域不同,文档的对比结论如下:

参数 作用范围 说明
调用参数 gasLimitcallParams 本次合约调用 限定合约调用本身允许消耗的最大 Gas
交易参数 gasLimittxParams 整笔交易 限定整个交易允许消耗的最大 Gas,并对调用参数的 gasLimit 形成约束

关键规则(与 base-invocation-scope.tscheckGasLimitTotal 实现一致):

  • 若调用参数 gasLimit 大于可用的交易 Gas,则整笔可用交易 Gas 都会被分配给该合约调用执行;
  • 若不设置调用参数 gasLimit,则交易 gasLimit 会被应用(即默认全部交给调用);
  • 多调用场景下,所有调用的 gasLimit 之和不能超过交易 gasLimit,否则 SDK 在提交前即报错拦截。

6. 同时设置两类参数

你完全可以在同一次合约函数调用中同时设置调用参数与交易参数:

const contractCallGasLimit = 4_000;
const transactionGasLimit = 100_000;

const call = await contract.functions
  .return_context_amount()
  .callParams({
    forward: [10, await provider.getBaseAssetId()],
    gasLimit: contractCallGasLimit,
  })
  .txParams({
    gasLimit: transactionGasLimit,
  })
  .call();

(摘自 setting-both-parameters.ts

txParams 接受的交易参数类型见 packages/program/src/types.ts,除 gasLimit 外还包括 tipmaxFeewitnessLimitmaturityexpirationvariableOutputs 等。txParams 的赋值逻辑在 base-invocation-scope.ts#L368-L382:各字段以"用户传值优先,否则保留请求已有值"的方式合并进 ScriptTransactionRequest,因此链式调用顺序灵活,最终在 fundWithRequiredCoins 中通过 setAndValidateGasAndFeeForAssembledTx 完成 Gas/费用的最终校验与落定。

7. 小结

  • callParams({ forward: [amount, assetId] }):向 #[payable] 函数转发币,非 payable 函数会被 SDK 前置拦截;
  • callParams({ gasLimit }):为单次合约调用设置 Gas 上限,过小时交易回滚 OutOfGas
  • txParams({ gasLimit, ... }):约束整笔交易的 Gas 与费用参数,并对各调用的 gasLimit 求和校验;
  • 两者可在同一次调用中链式组合,SDK 会在组装交易前自动完成"调用 Gas 总和 ≤ 交易 Gas"的合法性检查,失败早于广播,便于快速定位配置问题。

参考路径:文档源文件调用参数类型FunctionInvocationScopeBaseInvocationScope示例合约

登录后查看全文
热门项目推荐
相关项目推荐