首页
/ fuels-rs Provider 链上查询实战:获取地址币、可花费资源与资产余额

fuels-rs Provider 链上查询实战:获取地址币、可花费资源与资产余额

2026-09-05 16:07:42作者:冯爽妲Honey

本篇围绕 fuels-rs(Fuel Network 官方 Rust SDK)的链上查询能力展开:在通过 Provider 连接 fuel-core 节点后,如何查询某个地址的所有未花费币(coins)、满足金额要求的可花费资源(spendable resources)以及各资产的汇总余额。读完后你将能够直接使用 get_coinsget_spendable_resourcesget_balances 三个核心查询接口完成余额类业务开发,并理解其底层的分页拉取、资源过滤与缓存重试机制。

Provider 与查询接口的总体关系

在 fuels-rs 中,所有与节点的交互都通过 Provider 完成。从 provider.rs 的源码结构看,Provider 内部封装了 CachedClient<RetryableClient>:内层 RetryableClient 负责按配置对请求进行重试,外层 CachedClient 对部分 RPC 结果做带 TTL 的缓存。这意味着文档中的三个查询方法不仅是简单的 RPC 转发,还自带重试与缓存能力。

连接节点的方式有两种(参见 connecting 总览):

  • Provider::connect(url):连接到一个已存在的节点(测试网或自建的 fuel-core 节点);
  • setup_test_provider(...):SDK 内置的测试辅助函数,会启动一个带预置状态的短时本地节点,适合在测试中使用。

除本文涉及的三个查询接口外,Provider 还提供 chain_infonode_infodry_runlatest_gas_price 等大量方法,更完整的 API 面可查阅 fuels crate 中 fuels::accounts::provider::Provider 的公开 API 文档。

搭建测试链:setup_test_provider

如果你要连接的是外部区块链(测试网或自建节点),可以跳过本节。对于本地验证,示例代码位于 examples/providers/src/lib.rsquery_the_blockchain 测试中,完整设置如下:

use fuels::prelude::*;

// Create a random signer
let wallet_signer = PrivateKeySigner::random(&mut rand::thread_rng());

// How many coins in our wallet.
let number_of_coins = 1;

// The amount/value in each coin in our wallet.
let amount_per_coin = 3;

let coins = setup_single_asset_coins(
    wallet_signer.address(),
    AssetId::zeroed(),
    number_of_coins,
    amount_per_coin,
);

let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?;
let provider = setup_test_provider(coins.clone(), vec![], None, None)
    .await?
    .with_retry_config(retry_config);

这段代码各部分在源码中的含义如下:

  • setup_single_asset_coins(owner, asset_id, num_coins, amount_per_coin):定义在 fuels-test-helpers/src/lib.rs,为同一地址、同一资产 ID 生成 num_coins 个 UTXO,每个 UTXO 的数额为 amount_per_coin。从源码看,它使用随机数据填充 UtxoId 并将区块高度固定为 0,即生成一批“测试预置币”。

  • AssetId::zeroed():这里预置的资产是零值资产 ID,与测试链的默认基础资产(base asset)对应。

  • setup_test_provider(coins, messages, node_config, chain_config):定义在 fuels-test-helpers/src/lib.rs。它把 coinsmessages 写入本地测试节点的 StateConfig(预置状态),启动一个本地 FuelService,最后通过 Provider::from(bound_address) 建立连接。第二个参数是消息(message)预置列表,查询余额场景传 vec![] 即可;后两个参数可分别覆盖节点配置与链配置,传 None 时使用默认值(测试链配置会把交易大小上限提高到 10,000,000 字节)。

  • RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2))):为 Provider 配置“最多尝试 3 次、每次间隔固定 2 秒”的重试策略。Backoff 的三种策略与重试机制实现见 retry_util.rs

    • Backoff::Fixed(d):每次重试间隔固定为 d
    • Backoff::Linear(d):间隔随尝试次数线性增长(第 n 次等待 d * (n + 1));
    • Backoff::Exponential(d):间隔指数翻倍(第 n 次等待 d * 2^n)。

    从源码看,RetryConfig 默认值为“最多 1 次尝试 + Linear(10ms) 间隔”,即默认不重试;max_attempts 使用 NonZeroU32 保证必须大于 0。

查询地址的全部未花费币:get_coins

get_coins 返回某地址在指定资产 ID 下的所有未花费 UTXO(coin)对象,包括每个 coin 的 utxo_idamountownerasset_id 等字段。示例(继承自文档 querying.mdexamples/providers/src/lib.rs):

let consensus_parameters = provider.consensus_parameters().await?;
let coins = provider
    .get_coins(
        &wallet_signer.address(),
        *consensus_parameters.base_asset_id(),
    )
    .await?;
assert_eq!(coins.len(), 1);

要点:

  • 资产 ID 必须显式传入get_coins(from: &Address, asset_id: AssetId) 不接受 Option,因此示例先从 consensus_parameters() 取出链的 base_asset_id()consensus_parameters() 本身走缓存客户端(见 provider.rs),重复调用开销较低。
  • 分页是自动完成的:从 provider.rs 的实现看,get_coins 内部使用游标(cursor)循环调用底层 coins RPC,每批拉取 NUM_RESULTS_PER_REQUEST = 100 条(provider.rs 定义的常量),方向为 PageDirection::Forward,直到某一批返回空结果为止。因此即使某地址持有成千上万个 UTXO,SDK 也会自动翻页并聚合成一个完整的 Vec<Coin> 返回给调用方。

适合场景:需要审计、展示或自行选择具体 UTXO(例如做自定义转账逻辑)时使用。

查询可花费资源:get_spendable_resources 与 ResourceFilter

get_spendable_resources 解决的是另一类问题:我不关心具体是哪几个 UTXO,只要节点帮我凑出总额不低于 amount 的一批可花费资源。其入参是 ResourceFilter,定义见 provider.rs

#[derive(Default)]
pub struct ResourceFilter {
    pub from: Address,               // 资源所有者地址
    pub asset_id: Option<AssetId>,   // 目标资产,None 时回退到链的 base asset
    pub amount: u128,                // 需要凑够的最小总额
    pub excluded_utxos: Vec<UtxoId>,              // 需要排除的 UTXO ID 列表
    pub excluded_message_nonces: Vec<Nonce>,      // 需要排除的消息 nonce 列表
}
字段 类型 默认值 / 缺省行为
from Address 必须指定,即资源所属地址
asset_id Option<AssetId> None 时解析为链共识参数中的 base asset ID
amount u128 需要达到的最小总额
excluded_utxos Vec<UtxoId> 空向量,表示不排除任何 UTXO
excluded_message_nonces Vec<Nonce> 空向量,表示不排除任何消息

文档给出的示例(examples/providers/src/lib.rs)只设置了所有者与金额,其余字段走默认值:

let filter = ResourceFilter {
    from: wallet_signer.address(),
    amount: 1,
    ..Default::default()
};
let spendable_resources = provider.get_spendable_resources(filter).await?;
assert_eq!(spendable_resources.len(), 1);

结合源码,这个调用的执行链路是(见 provider.rs):

  1. filter.resource_queries()ResourceFilter 转换为内部查询参数;由于 asset_idNonespend_query 会先读取共识参数,把资产 ID 解析为 base asset ID,金额为 1;由于两个排除列表均为空,exclusion_query() 返回 None,即不带排除条件。
  2. SDK 调用节点的 coins_to_spend RPC,由节点在 UTXO 与消息中挑选凑足金额的资源组合。文档注释明确指出:返回的 coin 数量会经过优化以防止尘埃(dust)积累,因此返回数量通常恰好是满足金额的最优组合(示例中预置了 1 个数额为 3 的 coin,查询金额为 1,节点返回这 1 个 UTXO)。
  3. 返回值是 Vec<CoinType>——CoinType 同时覆盖 UTXO coin 与 L1 转入的消息(message)两类可花费资源,这比 get_coinsVec<Coin> 覆盖面更广。

两个补充细节:

  • coin-cache 特性:启用 coin-cache 特性后(参见 provider.rs),get_spendable_resources 会先执行 extend_filter_with_cached,把本进程近期已提交交易中使用的 coin 自动追加到 excluded_utxos / excluded_message_nonces,从而避免在节点状态尚未确认时重复选用“在途”资源。这解释了源码注释中“recently submitted coins will be ignored”的行为。
  • 排除列表的用途:当你已经手工选定部分 UTXO 用于其他交易时,可把它们的 UtxoId 放入 excluded_utxos,让节点在凑资源时绕开这些币。

查询资产余额汇总:get_balances

get_balances 返回某地址所有资产的可花费余额汇总。它和 get_coins 的本质区别是:只返回数字(每个资产 ID 对应 UTXO 数额之和),不返回 UTXO 本身,因此开销和返回体积都小得多。示例(examples/providers/src/lib.rs):

let _balances = provider.get_balances(&wallet_signer.address()).await?;

返回值类型为 HashMap<String, u128>,key 是资产 ID 的十六进制字符串,value 是该资产的余额总和。

provider.rs 的实现看,该方法还处理了节点索引能力差异:

  1. 先通过 node_info() 检查节点的 indexation.balances 标志位;
  2. 若节点已启用余额索引,则按游标分页(每批 100 条)循环拉取,直到拉空为止;
  3. 若节点未启用余额索引,则回退为单次请求最多 9999 条的兜底查询。

因此同一方法在不同节点配置下行为略有差异,但接口对调用方保持一致。

此外还有两个更细粒度的余额接口,适合只关心单一资产的场景:

  • get_asset_balance(&Address, &AssetId) -> Result<u128>:查询某地址在某资产下的汇总余额(见 provider.rs);
  • get_contract_asset_balance(&ContractId, &AssetId) -> Result<u64>:查询某合约在某资产下的余额(见 provider.rs)。

三个查询方法的选型小结

方法 返回 适用场景
get_coins Vec<Coin>:单资产下全部未花费 UTXO 明细 需要查看/自选具体 UTXO,做资产审计或自定义转账
get_spendable_resources Vec<CoinType>:由节点凑出的、总额 ≥ amount 的最优资源组合(含 UTXO 与消息) 发交易前让节点自动凑资源,SDK 内部转账逻辑同样走此接口
get_balances HashMap<String, u128>:所有资产的余额总和 展示钱包余额、做余额充足性判断

三者均返回 Result,错误处理遵循 SDK 统一的 fuels 错误类型;在连接外部节点时,可通过 with_retry_config 为 Provider 附加重试策略以提升弱网下的查询稳定性。

延伸阅读

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