Polkadot 账户信息查询完整指南:5 种 SDK 代码对比实战(PAPI / Polkadot.js / Dedot / Python / Rust)

原文作者:PaperMoon 团队

你写了一个 Polkadot 上的应用,用户连接钱包后,你需要显示余额。看上去很简单,在以太坊里调用 `eth_getBalance` 就完事了。到了 Polkadot,账户结构多了好几层:`free`、`reserved`、`frozen`、nonce、consumers……这些字段分别是什么,从哪个 SDK 查,怎么转换成人类可读的数字?

本文把五种 SDK 的代码都列出来,你选一个自己熟悉的语言跑一遍就清楚了。

说明:本文示例连接的是 Paseo 测试网(Asset Hub)。如果是主网,把 WebSocket 地址换掉即可,代码逻辑完全相同。

一、账户数据字段详解

在写代码之前,先把要查的字段弄清楚。Polkadot 账户信息存在链上的 `System.Account` 这个 storage 里,查出来的数据结构长这样:

```
nonce
consumers
providers
sufficients
data:
  free
  reserved
  frozen
```

nonce:账户发出的交易计数器,和以太坊的 nonce 含义相同。每发一笔交易加 1,用于防止同一笔交易被重放。构建交易时需要用到它。

free:可自由使用的余额,转账、支付手续费都从这里扣。查到的数值是最小单位(无小数),除以 `10_000_000_000`(10 的 10 次方)才是显示给用户看的 PAS/DOT 数量。

reserved:被锁定用于特定目的的余额,比如参与链上治理需要存款、创建资产需要押金。这部分余额不能转账,但还是你的。

frozen:被冻结的余额,不能用于转账,但某些操作(如质押投票)仍可使用。通常由特定 pallet 设置。

consumers / providers / sufficients:这三个是账户的"引用计数",用于管理账户的生命周期。`providers` 表示维持账户存在的来源数(比如余额不为零);`consumers` 表示有几个模块依赖了这个账户;`sufficients` 表示独立维持账户存在的来源数。一般开发者不需要直接操作这三个字段,知道它们的含义就够了。

二、选哪个 SDK?

本文涉及五个 SDK,先说一个重要信息:**Polkadot.js API 已经不再主动维护**,官方文档明确标注了这一点。如果你是新项目,直接选 PAPI 或 Dedot;如果你的现有代码用了 Polkadot.js,可以考虑逐步迁移。

SDK

语言

维护状态

推荐使用场景

一句话理解

PAPI (Polkadot API)

TypeScript

主动维护 ✅

新的 TS/JS dApp、前端应用

新一代官方交互库(推荐默认选择)

Polkadot.js API

JavaScript

停止维护 ⚠️

仅用于维护旧项目

历史主流库,但已进入维护模式

Dedot

TypeScript

主动维护 ✅

轻量级 TS 项目、性能敏感前端

更轻、更快的现代 TS 方案

Python Substrate Interface

Python

主动维护 ✅

自动化脚本、数据抓取、后端工具

写脚本最舒服的方案

Subxt

Rust

主动维护 ✅

高性能服务、Indexer、链工具

Rust 原生类型安全 SDK

三、PAPI 实现(推荐)

3.1 环境准备

```bash
mkdir papi-query-account-example && cd papi-query-account-example
npm init -y && npm pkg set type=module
```

安装依赖:

```bash
npm install polkadot-api
npm install --save-dev @types/node tsx typescript
```

生成链类型(连接测试网生成类型描述文件):

```bash
npx papi add polkadotTestNet -w wss://asset-hub-paseo.dotters.network
```

这一步 PAPI 区别于 Polkadot.js 的地方:它会从链上拉取元数据并生成强类型的 TypeScript 描述,后续调用时有完整的代码补全,不容易写错字段名。

3.2 代码

新建 `query-account.ts`:

```typescript
import { createClient } from 'polkadot-api';
// 中文:PAPI 的核心入口,用于创建到链的连接客户端
import { getWsProvider } from 'polkadot-api/ws-provider';
// 中文:WebSocket provider,负责建立 ws 连接
import { withPolkadotSdkCompat } from 'polkadot-api/polkadot-sdk-compat';
// 中文:兼容层,确保 PAPI 能正确处理 Polkadot SDK 链的响应格式
import { polkadotTestNet } from '@polkadot-api/descriptors';
// 中文:由 npx papi add 命令生成的链类型描述,提供强类型支持

const POLKADOT_HUB_RPC = 'INSERT_WS_ENDPOINT';   // 替换为你的 WebSocket 地址
const ACCOUNT_ADDRESS = 'INSERT_ACCOUNT_ADDRESS'; // 替换为要查询的 SS58 地址
const PAS_UNITS = 10_000_000_000;
// 中文:PAS/DOT 的最小单位换算系数(精度 10 位),链上数据除以此值得到人类可读余额

async function main() {
  try {
    const client = createClient(
      withPolkadotSdkCompat(getWsProvider(POLKADOT_HUB_RPC))
    );

    const api = client.getTypedApi(polkadotTestNet);
    console.log('Connected to Polkadot Hub');

    const accountInfo = await api.query.System.Account.getValue(
      ACCOUNT_ADDRESS
    );
    // 中文:查询 System pallet 下的 Account storage,传入 SS58 地址,返回账户完整信息

    console.log(`Nonce: ${accountInfo.nonce}`);
    console.log(`Consumers: ${accountInfo.consumers}`);
    console.log(`Providers: ${accountInfo.providers}`);
    console.log(`Sufficients: ${accountInfo.sufficients}`);

    console.log(
      `Free Balance: ${accountInfo.data.free} (${
        Number(accountInfo.data.free) / PAS_UNITS
      } PAS)`
    );
    console.log(
      `Reserved Balance: ${accountInfo.data.reserved} (${
        Number(accountInfo.data.reserved) / PAS_UNITS
      } PAS)`
    );
    console.log(
      `Frozen Balance: ${accountInfo.data.frozen} (${
        Number(accountInfo.data.frozen) / PAS_UNITS
      } PAS)`
    );

    const total =
      Number(accountInfo.data.free) + Number(accountInfo.data.reserved);
    console.log(`Total Balance: ${total} (${total / PAS_UNITS} PAS)`);
    // 中文:总余额 = free + reserved,frozen 不单独计入(它是 free 的子集)

    await client.destroy();
  } catch (error) {
    console.error('Error:', error);
    process.exit(1);
  }
}

main();
```

3.3 运行

```bash
npx tsx query-account.ts
```

预期输出:

```
Connected to Polkadot Hub

Querying account: 5GgbDVeKZwCmMHzn58iFSgSZDTojRMM52arXnuNXto28R7mg

Account Information:
===================
Nonce: 15
Consumers: 0
Providers: 1
Sufficients: 0

Balance Details:
================
Free Balance: 59781317040 (5.978131704 PAS)
Reserved Balance: 0 (0 PAS)
Frozen Balance: 0 (0 PAS)

Total Balance: 59781317040 (5.978131704 PAS)

Disconnected
```

四、Polkadot.js API 实现(旧项目维护用)

官方已明确该库不再主动开发,新项目建议直接跳过这一节,用 PAPI 或 Dedot。

```bash
mkdir pjs-query-account-example && cd pjs-query-account-example
npm init -y && npm pkg set type=module
npm install @polkadot/api
```

新建 `query-account.js`:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
// 中文:ApiPromise 是 Polkadot.js 的主要 API 类;WsProvider 建立 WebSocket 连接

const POLKADOT_HUB_RPC = 'INSERT_WS_ENDPOINT';
const ACCOUNT_ADDRESS = 'INSERT_ACCOUNT_ADDRESS';
const PAS_UNITS = 10_000_000_000;

async function main() {
  const wsProvider = new WsProvider(POLKADOT_HUB_RPC);
  const api = await ApiPromise.create({ provider: wsProvider });

  const accountInfo = await api.query.system.account(ACCOUNT_ADDRESS);
  // 中文:注意大小写:PAPI 用 System.Account(大写),Polkadot.js 用 system.account(小写)

  console.log(`Nonce: ${accountInfo.nonce.toString()}`);
  // 中文:Polkadot.js 返回的字段是 Codec 类型,需要 .toString() 或 .toBigInt() 才能转成普通数值

  console.log(`Free Balance: ${Number(accountInfo.data.free.toBigInt()) / PAS_UNITS} PAS`);
  console.log(`Reserved Balance: ${Number(accountInfo.data.reserved.toBigInt()) / PAS_UNITS} PAS`);
  console.log(`Frozen Balance: ${Number(accountInfo.data.frozen.toBigInt()) / PAS_UNITS} PAS`);

  await api.disconnect();
}

main().catch(console.error);
```

```bash
node query-account.js
```

五、Dedot 实现

Dedot 是 Polkadot.js 的轻量级替代品,API 风格更接近 Polkadot.js,但体积更小,也在主动维护。从 Polkadot.js 迁移过来的成本比切换到 PAPI 更低。

```bash
mkdir dedot-query-account-example && cd dedot-query-account-example
npm init -y && npm pkg set type=module
npm install dedot
npm install --save-dev @dedot/chaintypes @types/node tsx typescript
```

新建 `query-account.ts`:

```typescript
import { DedotClient, WsProvider } from 'dedot';
// 中文:DedotClient 是 Dedot 的主客户端,用法和 Polkadot.js 的 ApiPromise 类似
import type { PolkadotAssetHubApi } from '@dedot/chaintypes';
// 中文:来自 @dedot/chaintypes 的链类型定义,提供代码补全

const POLKADOT_HUB_RPC = 'INSERT_WS_ENDPOINT';
const ACCOUNT_ADDRESS = 'INSERT_ACCOUNT_ADDRESS';
const PAS_UNITS = 10_000_000_000;

async function main() {
  const provider = new WsProvider(POLKADOT_HUB_RPC);
  const client = await DedotClient.new<PolkadotAssetHubApi>(provider);

  const accountInfo = await client.query.system.account(ACCOUNT_ADDRESS);
  // 中文:查询方式和 Polkadot.js 几乎一致(system.account 小写),
  // 但返回值是原生 TypeScript 类型,不需要 .toString() 转换

  console.log(`Nonce: ${accountInfo.nonce}`);
  console.log(`Free Balance: ${Number(accountInfo.data.free) / PAS_UNITS} PAS`);
  console.log(`Reserved Balance: ${Number(accountInfo.data.reserved) / PAS_UNITS} PAS`);
  console.log(`Frozen Balance: ${Number(accountInfo.data.frozen) / PAS_UNITS} PAS`);

  const total = Number(accountInfo.data.free) + Number(accountInfo.data.reserved);
  console.log(`Total Balance: ${total / PAS_UNITS} PAS`);

  await client.disconnect();
}

main().catch(console.error);
```

```bash
npx tsx query-account.ts

六、Python Substrate Interface 实现

```bash
mkdir psi-query-account-example && cd psi-query-account-example
python3 -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install substrate-interface
```

新建 `query_account.py`:

```python
from substrateinterface import SubstrateInterface
# 中文:SubstrateInterface 是 Python 连接 Substrate 链的主要接口类

POLKADOT_HUB_RPC = "INSERT_WS_ENDPOINT"
ACCOUNT_ADDRESS = "INSERT_ACCOUNT_ADDRESS"
PAS_UNITS = 10_000_000_000

def main():
    substrate = SubstrateInterface(url=POLKADOT_HUB_RPC)

    account_info = substrate.query(
        module="System",
        storage_function="Account",
        params=[ACCOUNT_ADDRESS]
    )
    # 中文:Python 版查询方式是字符串形式的 module + storage_function,没有代码补全,
    # 拼写必须和链上 pallet 名完全匹配(大小写敏感)

    free_balance = account_info.value["data"]["free"]
    reserved_balance = account_info.value["data"]["reserved"]
    frozen_balance = account_info.value["data"]["frozen"]

    print(f"Nonce: {account_info.value['nonce']}")
    print(f"Free Balance: {free_balance / PAS_UNITS} PAS")
    print(f"Reserved Balance: {reserved_balance / PAS_UNITS} PAS")
    print(f"Frozen Balance: {frozen_balance / PAS_UNITS} PAS")
    print(f"Total Balance: {(free_balance + reserved_balance) / PAS_UNITS} PAS")

    substrate.close()

if __name__ == "__main__":
    main()
```
```bash
python query_account.py
```

七、Subxt 实现(Rust)

Subxt 是 Rust 生态中查询和提交 Substrate 链交易的主要库。和之前账户创建用的 `sp-core` 不同,Subxt 面向的是链上交互,需要先拉取链的元数据。

```bash
cargo new subxt-query-account-example && cd subxt-query-account-example
cargo install subxt-cli@0.35.3
```

下载链元数据(只需执行一次):

```bash
subxt metadata --url INSERT_WS_ENDPOINT -o polkadot_testnet_metadata.scale
```

在 `Cargo.toml` 中添加依赖:

```toml
[package]
name = "subxt-query-account-example"
version = "0.1.0"
edition = "2021"

[[bin]]
name = "query_account"
path = "src/bin/query_account.rs"

[dependencies]
subxt = { version = "0.44.0" }
tokio = { version = "1.36.0", features = ["macros", "rt"] }
```

新建 `src/bin/query_account.rs`:

```rust
use std::str::FromStr;
use subxt::utils::AccountId32;
use subxt::{OnlineClient, PolkadotConfig};
// 中文:OnlineClient 建立到链的连接;PolkadotConfig 包含 Polkadot 网络的配置(地址类型、哈希函数等)

#[subxt::subxt(runtime_metadata_path = "polkadot_testnet_metadata.scale")]
pub mod polkadot_testnet {}
// 中文:这个宏从本地的 .scale 元数据文件生成链的类型绑定,类似 PAPI 的 npx papi add 步骤

const POLKADOT_TESTNET_RPC: &str = "INSERT_WS_ENDPOINT";
const ACCOUNT_ADDRESS: &str = "INSERT_ACCOUNT_ADDRESS";
const PAS_UNITS: u128 = 10_000_000_000;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(POLKADOT_TESTNET_RPC).await?;

    let account = AccountId32::from_str(ACCOUNT_ADDRESS)?;
    // 中文:将 SS58 地址字符串解析为 AccountId32 类型,Subxt 的 storage 查询接受这个类型

    let storage_query = polkadot_testnet::storage().system().account(account);
    // 中文:构建 storage 查询:访问 system pallet 下的 account storage,传入账户 ID
    let account_info = api
        .storage()
        .at_latest()    // 中文:查询最新区块的状态
        .await?
        .fetch(&storage_query)
        .await?;

    if let Some(info) = account_info {
        println!("Nonce: {}", info.nonce);
        println!("Free Balance: {} PAS", info.data.free as f64 / PAS_UNITS as f64);
        println!("Reserved Balance: {} PAS", info.data.reserved as f64 / PAS_UNITS as f64);
        println!("Frozen Balance: {} PAS", info.data.frozen as f64 / PAS_UNITS as f64);

        let total = info.data.free + info.data.reserved;
        println!("Total Balance: {} PAS", total as f64 / PAS_UNITS as f64);
    } else {
        println!("Account not found");
        // 中文:账户从未接收过任何 DOT 时,链上不会存储账户记录,fetch 返回 None
    }

    Ok(())
}
```
```bash
cargo run --bin query_account
```

八、五种 SDK 对比

对比项

PAPI

Polkadot.js

Dedot

Python SI

Subxt

语言

TypeScript

JavaScript

TypeScript

Python

Rust

维护状态

✅ 主动维护

⚠️ 已停止维护

✅ 主动维护

✅ 主动维护

✅ 主动维护

类型安全

强(自动生成类型)

弱(动态 + Codec)

中(需手动类型包)

强(宏 + 编译期检查)

元数据获取方式

npx papi add 生成类型

自动加载

引入 @dedot/chaintypes

自动

subxt metadata 生成代码

余额字段类型

原生 JS 数值 / bigint

Codec 类型(需 .toBigInt())

原生 JS 数值

Python dict

Rust u128

开发体验

现代前端体验

历史 Web3 体验

轻量快速

简单直接

工程化最强

学习成本

很低

典型用途

dApp、钱包、课程教学

旧项目维护

轻量前端

脚本/运维

如果你用 TypeScript,从 PAPI 开始。如果你已经有了 Polkadot.js 的项目,切 Dedot 的迁移成本最低。如果你写 Python 脚本,`substrate-interface` 就够用。需要高性能或要深度集成 Substrate 的场景,用 Subxt。

查账户余额这件事本身不难,代码跑通了也就十几行。真正需要花时间搞清楚的是 free / reserved / frozen 这三种余额的区别——在用户实际使用中,如果你只显示 free balance,用户可能会困惑"为什么我明明有这么多余额,却转不出去那么多"。把这三个字段都显示出来,或者至少显示"可用余额(free)"和"总余额(free + reserved)",对用户体验会有明显改善。

阅读原文:https://docs.polkadot.com/chain-interactions/accounts/query-accounts/
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值