首页
/ fuels-rs 合约调用参数详解:用 CallParameters 向 Fuel 合约转发资产与 Gas

fuels-rs 合约调用参数详解:用 CallParameters 向 Fuel 合约转发资产与 Gas

2026-09-05 14:10:34作者:邓越浪Henry

本文基于 fuels-rs 官方文档 Call parameters 展开,系统讲解 Fuel Network Rust SDK 中合约调用参数(Call Parameters)的三个核心字段——金额、资产 ID、转发 Gas——的含义、默认值与底层实现;读完你可以掌握如何通过 CallParameters 在发起合约调用时向合约转发任意资产、精确控制单次调用的 Gas 上限,并理解 SDK 对非 #[payable] 方法的防护机制。

合约调用参数是什么

在 fuels-rs 中,每次向合约发起调用时都可以附带一组「调用参数」,用于向合约转发 coins。文档将其归纳为三个字段:

  1. Amount(金额):本次调用随交易转发给合约的资产数量;
  2. Asset ID(资产 ID):转发的资产种类;
  3. Gas forwarded(转发 Gas):转发给合约实际执行的 Gas 额度。

这三个字段在 SDK 中被封装为 CallParameters 结构体,定义在 contract_call.rs 中:

#[derive(Debug, Clone)]
pub struct CallParameters {
    amount: u64,
    asset_id: Option<AssetId>,
    gas_forwarded: Option<u64>,
}

从源码结构看,asset_idgas_forwarded 都是 Option 类型,意味着两者均可缺省;而 amount 是裸的 u64。当 asset_id 缺省时,SDK 会自动回退到链上的 base asset——这一行为发生在 ContractCall::data() 中,即 contract_call.rs 的如下映射逻辑:

Ok(ContractCallData {
    amount: self.call_parameters.amount(),
    asset_id: self.call_parameters.asset_id().unwrap_or(base_asset_id), // 未指定则回退 base asset
    contract_id: self.contract_id,
    fn_selector_encoded: self.encoded_selector.clone(),
    encoded_args,
    gas_forwarded: self.call_parameters.gas_forwarded,
})

构造与配置:new 与链式 builder

CallParameters 提供两种构造方式,均定义在 contract_call.rs

  • CallParameters::new(amount, asset_id, gas_forwarded):一次性显式指定全部三个字段;
  • CallParameters::default() 配合链式 builder 方法 with_amount / with_asset_id / with_gas_forwarded,按需覆盖单个字段,其余保持默认。

默认值由 constants.rs 中的常量决定:

// ANCHOR: default_call_parameters
pub const DEFAULT_CALL_PARAMS_AMOUNT: u64 = 0;
// ANCHOR_END: default_call_parameters

结合 contract_call.rsDefault 实现,可以确认 CallParameters::default() 的语义为:金额 0、资产 ID 为 base asset(None 回退)、Gas 转发量不限制(None

向合约转发资产:完整示例

先看一个典型的 Sway 合约示例。仓库中的 contract_test 合约 使用 Sway 标准库的 msg_amount() 把交易带入的金额原样返回,这正是文档选用的演示合约:

// ANCHOR: msg_amount
#[payable]
fn get_msg_amount() -> u64 {
    msg_amount()
}
// ANCHOR_END: msg_amount

注意方法上的 #[payable] 标注——它是接收资产转发的前提(详见后文 payable 校验一节)。

部署该合约后,在 Rust 侧通过 call_params 链式方法配置转发金额,完整示例见 examples/contracts/src/lib.rs

// ANCHOR: call_parameters
let contract_methods = MyContract::new(contract_id, wallet.clone()).methods();

let tx_policies = TxPolicies::default();

// Forward 1_000_000 coin amount of base asset_id
// this is a big number for checking that amount can be a u64
let call_params = CallParameters::default().with_amount(1_000_000);

let response = contract_methods
    .get_msg_amount() // Our contract method.
    .with_tx_policies(tx_policies) // Chain the tx policies.
    .call_params(call_params)? // Chain the call parameters.
    .call() // Perform the contract call.
    .await?; // This is an async call, `.await` it.
// ANCHOR_END: call_parameters

示例中转发 1_000_000 单位的 base asset(因为未调用 with_asset_id,按上文逻辑回退为链上 base asset)。合约方法 get_msg_amount 返回的 response.value 即该金额,读者可据此验证转发是否生效。

从调用链看,call_paramsCallHandler 上的 builder 方法,实现位于 call_handler.rs。它把参数写入内部 ContractCall.call_parameters 字段,后续在构建交易时经由 ContractCall::data() 落到 ContractCallData,最终编码进合约调用的 context 中。

为什么 call_params 返回 Result:payable 校验

call_params 与链上其他 builder 方法不同,它的签名是 pub fn call_params(mut self, params: CallParameters) -> Result<Self>,返回 Result 是为了防止把资产转发给非 payable 方法。源码中的校验逻辑只有一行,但语义明确(call_handler.rs):

pub fn call_params(mut self, params: CallParameters) -> Result<Self> {
    if !self.is_payable() && params.amount() > 0 {
        return Err(error!(Other, "assets forwarded to non-payable method"));
    }
    self.call.call_parameters = params;

    Ok(self)
}

其中 is_payable 标志由 abigen! 宏从合约 ABI 中解析 #[payable] 注解读取。以仓库的 payable_annotation 测试合约 为例,它同时定义了带标注与不带标注的两个方法:

abi TestContract {
    #[payable]
    fn payable() -> u64;
    fn non_payable() -> u64;
}

对应的 e2e 测试 contracts.rs 验证了校验行为:向 non_payable 转发 100 单位资产会直接返回编译期之外的运行时错误:

// ANCHOR: non_payable_params
let err = contract_methods
    .non_payable()
    .call_params(CallParameters::default().with_amount(100))
    .expect_err("should return error");

assert!(matches!(err, Error::Other(s) if s.contains("assets forwarded to non-payable method")));
// ANCHOR_END: non_payable_params

注意(原文档强调):向合约调用转发 Gas 始终是被允许的,即使目标方法没有 #[payable] 标注。上面测试的后半段(contracts.rs)也验证了这一点:non_payable().call_params(CallParameters::default().with_gas_forwarded(20_000)) 可以正常执行并成功返回。

默认行为:不设置参数会转发什么

如果既不调用 call_params 也不显式传入 CallParameters::default(),SDK 的默认行为是:转发整笔交易的 gas limit,而不是转发给单次合约调用。换言之,gas_forwarded 缺省(None)等价于「把交易层 Gas 上限全部作为本次调用的可用 Gas」。

若你只想显式走一遍默认路径,可以像 examples/contracts/src/lib.rs 这样写:

// ANCHOR: call_parameters_default
let response = contract_methods
    .initialize_counter(42)
    .call_params(CallParameters::default())?
    .call()
    .await?;
// ANCHOR_END: call_parameters_default

注意即使在这里 call_params 也返回 Result,链式调用中必须用 ? 处理。

gas_forwarded:为单次调用设定 Gas 上限

gas_forwarded 参数定义的是本次合约调用本身的 Gas 上限,区别于 TxPolicies 中配置的整笔交易script_gas_limit。文档给出的约束关系是:

  • 调用级 Gas 上限受交易级 Gas 上限约束;
  • 如果把 gas_forwarded 设置得比交易实际可用的 Gas 还大,则实际转发的就是全部可用 Gas(而不是报错或取你设置的值)。

官方示例完整展示了两个层面的配合,见 examples/contracts/src/lib.rs

// ANCHOR: call_params_gas
// Set the transaction `gas_limit` to 1_000_000 and `gas_forwarded` to 4300 to specify that
// the contract call transaction may consume up to 1_000_000 gas, while the actual call may
// only use 4300 gas
let tx_policies = TxPolicies::default().with_script_gas_limit(1_000_000);
let call_params = CallParameters::default().with_gas_forwarded(4300);

let response = contract_methods
    .get_msg_amount() // Our contract method.
    .with_tx_policies(tx_policies) // Chain the tx policies.
    .call_params(call_params)? // Chain the call parameters.
    .call() // Perform the contract call.
    .await?;
// ANCHOR_END: call_params_gas

这里交易层允许消耗最多 1_000_000 Gas,而真正转发给合约执行的只有 4300 Gas。这种分层控制在多调用(multi-call)场景中尤为有用:你可以让单笔交易里的某个调用只拿到有限的 Gas,从而隔离资源消耗。

速查表与小结

字段 类型 默认值 设置方法 语义
amount u64 0(见 constants.rs with_amount 转发资产的单位数量
asset_id Option<AssetId> None,回退 base asset with_asset_id 转发的资产种类
gas_forwarded Option<u64> None,转发交易 gas limit with_gas_forwarded 本次合约调用的 Gas 上限,受交易 gas limit 约束

小结:在 fuels-rs 中,CallParameters 是「交易级策略」(TxPolicies)之外、针对单次合约调用的资源控制手段。核心要点有四:

  1. 通过 CallParameters::default().with_amount(...) 等 builder 方法配置,并以 .call_params(params)? 链入调用;
  2. asset_id 缺省时自动使用链上 base asset(见 ContractCall::data 的回退逻辑);
  3. call_params 返回 Result,向非 #[payable] 方法转发非零金额会被 SDK 在本地拦截并报 assets forwarded to non-payable method(见 call_handler.rs);
  4. 转发 Gas 不受 payable 限制,而 gas_forwarded 与交易 script_gas_limit 是「调用级 vs 交易级」两层独立的 Gas 预算。

更多合约调用相关内容可参考同目录文档:合约调用总览自定义资产转移交易策略

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