Polkadot 开发教程:使用 Subxt(Rust)查询链上状态与资产信息|Polkadot Hub On-Chain Storage 查询完整指南
本文介绍了如何使用Subxt(Rust库)查询基于PolkadotSDK构建的区块链上的状态数据。主要内容包括:1. 链上状态的概念和存储内容(账户余额、资产信息等);2. Subxt作为静态强类型API的特点和优势;3. 具体实现步骤:- 环境准备和项目创建- 下载元数据- 配置Cargo.toml- 实现账户余额查询- 实现资产信息查询4. 核心知识补充:Polkadot资产与ERC-20的区
原文作者:PaperMoon团队
一、引言(Introduction)
基于 Polkadot SDK(Substrate) 构建的区块链,其所有运行时数据都存储在链上的“数据库”(Key-Value Storage)中,外部应用可以直接进行查询(Query)。
所谓“链上状态(On-Chain State)”包括但不限于:
• 账户余额(Account Balance)
• 资产信息(Asset Metadata)
• 治理提案(Governance Proposals)
• 运行时模块(Pallet)管理的任意数据
也就是说,链 ≠ 只负责转账,它本质上是一台可被外部程序实时读取的数据库。
开发者可以通过 SDK 直接读取这些存储,而无需运行区块浏览器。
二、可用于查询链上状态的 SDK
Polkadot 生态目前支持多种语言的链上查询 SDK:
|
SDK |
语言 |
特点 |
|---|---|---|
|
Polkadot API (PAPI) |
TypeScript |
现代、强类型 |
|
Polkadot.js API |
JavaScript |
功能全面(维护模式) |
|
Dedot |
TypeScript |
轻量、高性能 |
|
Python Substrate Interface |
Python |
数据分析常用 |
|
Subxt |
Rust |
编译期类型安全(推荐后端开发) |
本文将重点讲解:使用 Subxt(Rust)查询 Polkadot Hub 的账户余额与 USDT 资产信息。
三、Subxt 是什么?
Subxt 是一个 Rust 库,它通过读取区块链的 runtime metadata 自动生成类型安全接口,从而在编译阶段保证调用正确性。
简单理解:
- Polkadot.js 是“动态 API”
- Subxt 是“静态强类型 API”
这意味着:
• 如果你查询了不存在的 Storage
• 或参数类型错误
Rust 会直接编译失败,而不是运行时报错。这对于后端服务(Indexer、钱包、交易系统)非常重要。
四、开发环境准备(Prerequisites)
需要安装:
• 最新稳定版 Rust Toolchain
• Cargo 包管理器
五、创建项目
cargo new subxt-query-example && cd subxt-query-example
六、安装 Subxt CLI
cargo install subxt-cli@0.44.2
七、下载 Polkadot Hub Metadata
Subxt 的核心机制依赖链的 Metadata(运行时元数据)。
执行:
subxt metadata --url INSERT_WS_ENDPOINT -o asset_hub_metadata.scale
INSERT_WS_ENDPOINT
请将上文替换为真实 WebSocket RPC:
wss://polkadot-asset-hub-rpc.polkadot.io
Metadata 是链运行时的“ABI”,描述了所有 Pallet、Storage、Call、Event 结构。
八、配置 Cargo.toml
[package]
name = "subxt-query-example"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "query_balance"
path = "src/bin/query_balance.rs"
[[bin]]
name = "query_asset"
path = "src/bin/query_asset.rs"
[dependencies]
subxt = "0.44.0"
tokio = { version = "1", features = ["rt", "macros"] }
九、查询账户余额(System.Account)
我们将读取 System Pallet 的 Account Storage。在 Substrate 中,账户余额并不是 ERC-20 合约,而是 Runtime 原生存储。
创建文件:
src/bin/query_balance.rs
代码示例
use std::str::FromStr;
use subxt::utils::AccountId32;
use subxt::{OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "asset_hub_metadata.scale")]
pub mod asset_hub {}
const ASSET_HUB_RPC: &str = "INSERT_WS_ENDPOINT";
const ADDRESS: &str = "INSERT_ADDRESS";
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api = OnlineClient::<PolkadotConfig>::from_url(ASSET_HUB_RPC).await?;
println!("Connected to Polkadot Hub");
println!("Querying balance for: {}\n", ADDRESS);
let account = AccountId32::from_str(ADDRESS)?;
let storage_query = asset_hub::storage().system().account(account);
let account_info = api
.storage()
.at_latest()
.await?
.fetch(&storage_query)
.await?;
match account_info {
Some(info) => {
println!("Nonce: {}", info.nonce);
println!("Free Balance: {}", info.data.free);
println!("Reserved: {}", info.data.reserved);
println!("Frozen: {}", info.data.frozen);
}
None => {
println!("Account not found");
}
}
Ok(())
}
运行
cargo run --bin query_balance
重要说明
如果返回:
Account not found
并不代表地址不存在,而是:该账户从未发生过交易 → 链上没有 storage entry → fetch() 返回 None
十、查询资产信息(Assets Pallet)
接下来查询 USDT(Asset ID = 1984)
Polkadot Hub 的资产并不是 ERC-20,而是 Runtime 的 Assets Pallet 管理。
创建:
src/bin/query_asset.rs
示例代码
use std::str::FromStr;
use subxt::utils::AccountId32;
use subxt::{OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "asset_hub_metadata.scale")]
pub mod asset_hub {}
const ASSET_HUB_RPC: &str = "INSERT_WS_ENDPOINT";
const USDT_ASSET_ID: u32 = 1984;
const ADDRESS: &str = "INSERT_ADDRESS";
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api = OnlineClient::<PolkadotConfig>::from_url(ASSET_HUB_RPC).await?;
println!("Connected to Polkadot Hub");
let metadata_query = asset_hub::storage().assets().metadata(USDT_ASSET_ID);
let metadata = api.storage().at_latest().await?.fetch(&metadata_query).await?;
if let Some(meta) = metadata {
println!("Name: {}", String::from_utf8_lossy(&meta.name.0));
println!("Symbol: {}", String::from_utf8_lossy(&meta.symbol.0));
println!("Decimals: {}", meta.decimals);
}
let asset_query = asset_hub::storage().assets().asset(USDT_ASSET_ID);
let asset_details = api.storage().at_latest().await?.fetch(&asset_query).await?;
if let Some(details) = asset_details {
println!("Owner: {}", details.owner);
println!("Supply: {}", details.supply);
println!("Accounts: {}", details.accounts);
println!("Min Balance: {}", details.min_balance);
}
let account = AccountId32::from_str(ADDRESS)?;
let account_query = asset_hub::storage().assets().account(USDT_ASSET_ID, account);
let asset_account = api.storage().at_latest().await?.fetch(&account_query).await?;
match asset_account {
Some(account) => println!("Balance: {}", account.balance),
None => println!("No asset balance found for this account"),
}
Ok(())
}
运行
cargo run --bin query_asset
示例输出:
Name: Tether USD
Symbol: USDT
Decimals: 6
Supply: 77998622834581
Accounts: 13545
Min Balance: 10000
No asset balance found for this account
十一、核心知识补充
1)Polkadot 的资产不是智能合约
很多开发者第一次会困惑:
为什么不用调用合约 ABI?
因为:Polkadot Hub 上的资产是 Runtime Native Asset,而非 ERC-20。
它的结构更像:系统模块存储(Storage),而不是用户部署的智能合约(Contract)。
优点:
• Gas 更低
• 无需合约执行
• 钱包读取更快
• 更安全(无重入攻击)
2)为什么要用 Metadata
Substrate 不是固定 ABI 链:
以太坊:ABI 固定 → 节点无需解释
Substrate:Runtime 可升级 → 接口会变化 → 必须动态生成接口
Subxt 正是解决这个问题。
阅读原文:https://docs.polkadot.com/chain-interactions/query-data/query-sdks/#__tabbed_1_5
更多推荐



所有评论(0)