Flutter Polkadot交互插件polkadot_dart的使用
Flutter Polkadot交互插件polkadot_dart的使用
简介
polkadot_dart
是一个用于与 Substrate 区块链进行交互的 Flutter 插件。它提供了全面的 Substrate 交互功能,允许你创建、签名和发送交易(支持 ECDSA、ED25519 和 SR25519),查询元数据,执行运行时调用,并利用 JSON-RPC 进行无缝的区块链操作。
主要功能
-
事务管理
- 创建、签名和验证使用多种密钥类型(ECDSA、ED25519、SR25519)的事务。
- 支持生成和管理 ECDSA、ED25519 和 SR25519 密钥的地址。
- 引入多签名(multi-sig)地址功能,增强事务安全性。
-
元数据支持
- 完全支持 Substrate 元数据版本 V14 和 V15。
-
JSON-RPC
- 提供全面的 JSON-RPC 支持,实现与区块链节点的无缝交互。
-
运行时调用
- 执行运行时调用以与区块链的运行时环境进行交互。
-
存储查询
- 执行存储查询以检索和操作区块链数据。
-
数据处理
- 编码和解码各种数据格式。
示例代码
转账示例
以下是一个完整的转账示例,展示了如何使用 polkadot_dart
插件进行转账操作:
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:polkadot_dart/polkadot_dart.dart';
void main() async {
// 设置连接到 Substrate 节点的提供者
final provider = SubstrateProvider(
SubstrateHttpService("https://westend-rpc.polkadot.io"));
// 请求区块链的元数据以确定其版本
final requestMetadata = await provider
.request(const SubstrateRequestRuntimeMetadataGetMetadataAtVersion(15));
final metadata = requestMetadata!.metadata as MetadataV15;
// 从种子生成私钥并派生相应的地址
List<int> seedBytes = BytesUtils.fromHexString(
"4bc884e0eb595d3c71dc4c62305380a43d7a820b9cf69753232196b34486a27c");
final privateKey = SubstratePrivateKey.fromSeed(
seedBytes: seedBytes, algorithm: SubstrateKeyAlgorithm.sr25519);
// 派生与私钥对应的地址
final signer = privateKey.toAddress();
// 交易的目标地址
final destination =
SubstrateAddress("5FeHntLqsucHn1CZuAsLceAN2FJhwbonP6goHzo4dWVzW33T");
// 初始化元数据 API 以进行进一步的交互
final api = MetadataApi(metadata);
// 获取运行时版本信息
final version = api.runtimeVersion();
// 获取创世哈希和最终化块哈希
final genesisHash = await provider
.request(const SubstrateRequestChainGetBlockHash<String>(number: 0));
final blockHash = await provider
.request(const SubstrateRequestChainChainGetFinalizedHead());
final blockHeader = await provider
.request(SubstrateRequestChainChainGetHeader(atBlockHash: blockHash));
final era = blockHeader.toMortalEra();
// 获取账户信息以确定交易的 nonce
final accountInfo = await api.getStorage(
request: QueryStorageRequest<Map<String, dynamic>>(
palletNameOrIndex: "System",
methodName: "account",
input: signer.toBytes(),
identifier: 0),
rpc: provider,
fromTemplate: false);
final int nonce = accountInfo.result["nonce"];
// 构建转账负载
final tmp = {
"type": "Enum",
"key": "transfer_allow_death",
"value": {
"type": "Map",
"value": {
"dest": {
"key": "Id",
"value": {"type": "[U8;32]", "value": destination.toBytes()},
},
"value": {"type": "U128", "value": SubstrateHelper.toWSD("5")}
}
},
};
final method = api.encodeCall(
palletNameOrIndex: "balances", value: tmp, fromTemplate: true);
// 构建交易负载
final payload = TransactionPayload(
blockHash: SubstrateBlockHash.hash(blockHash),
era: era,
genesisHash: SubstrateBlockHash.hash(genesisHash),
method: method,
nonce: nonce,
specVersion: version.specVersion,
transactionVersion: version.transactionVersion,
tip: BigInt.zero);
// 签名交易
final sig = privateKey.multiSignature(payload.serialize());
// 构建带有签名的外在以提交
final signature = ExtrinsicSignature(
signature: sig,
address: signer.toMultiAddress(),
era: era,
tip: BigInt.zero,
nonce: nonce);
final extrinsic = Extrinsic(signature: signature, methodBytes: method);
// 提交外在到区块链
await provider.request(
SubstrateRequestAuthorSubmitExtrinsic(extrinsic.toHex(prefix: "0x")));
}
// https://westend.subscan.io/extrinsic/0xdb7c22ac4f66fda76e053ce06dc56bda9f67bf9e2ce9311adff693e5614955c6
查询示例
以下是一个查询示例,展示了如何使用 polkadot_dart
插件查询区块链数据:
import 'package:polkadot_dart/polkadot_dart.dart';
void main() async {
// 设置连接到 Substrate 节点的提供者
final provider = SubstrateProvider(
SubstrateHttpService("https://westend-rpc.polkadot.io"));
// 解析元数据并初始化元数据 API
final metadata = VersionedMetadata<MetadataV14>.fromBytes(
BytesUtils.fromHexString(metadataV14))
.metadata;
final api = MetadataApi(metadata);
// 定义 Substrate 地址以进行测试
final addr =
SubstrateAddress("5EepAwmzpwn2PK45i3og3emvXs4NFnqzHu4T2VbUGhvkU4yB");
final addr2 =
SubstrateAddress("5GjQNXpyZoyYiQ8GdB5fgjRkZdh3EgELwGdEmPP44YDnMx43");
// 获取输入和输出模板以进行检查
final template = api.getStorageInputTemplate("System", "account");
final output = api.getStorageOutputTemplate("System", "account");
// 查询单个账户信息
final accountInfoSingleResult = await api.getStorage(
request: QueryStorageRequest<Map<String, dynamic>?>(
palletNameOrIndex: "System",
methodName: "account",
input: addr.toBytes(),
identifier: 0),
rpc: provider,
fromTemplate: false,
);
// 构建多个账户的存储查询请求
final rAccOne = QueryStorageRequest<Map<String, dynamic>>(
palletNameOrIndex: "System",
methodName: "account",
input: addr.toBytes(),
identifier: 0);
final rAccTwo = QueryStorageRequest<Map<String, dynamic>>(
palletNameOrIndex: "System",
methodName: "account",
input: addr2.toBytes(),
identifier: 1);
// 查询多个账户的存储
final accountInfoMultipleResult = await api.queryStorageAt(
requestes: [rAccOne, rAccTwo], rpc: provider, fromTemplate: false);
final account1Info = accountInfoMultipleResult.getResult(1);
// 在特定块范围内查询存储
final queryRange = await api.queryStorage(
requestes: [rAccOne, rAccTwo],
rpc: provider,
fromBlock:
"0x316ed44e1bb758de18a0307eac10cfaedf9d4eef1f7a383fb7ba1bd9270d3938",
toBlock:
"0x9afc2fbf7a31dc2f0adeedad895966c6dd6015e0dc540cf2b93c2e54fbf5afa7",
fromTemplate: false);
// 遍历指定块范围内的查询结果
for (int i = 0; i < queryRange.length; i++) {
final blockResponse = queryRange[i];
final results = blockResponse.getResult(1);
}
}
运行时 API 示例
以下是一个运行时 API 示例,展示了如何使用 polkadot_dart
插件调用运行时方法:
import 'package:polkadot_dart/polkadot_dart.dart';
void main() async {
// 设置连接到 Westend Substrate 节点的提供者
final provider = SubstrateProvider(
SubstrateHttpService("https://westend-rpc.polkadot.io"));
// 解析元数据并初始化元数据 API
final metadata = VersionedMetadata<MetadataV15>.fromBytes(
BytesUtils.fromHexString(westendV15));
final api = MetadataApi(metadata.metadata);
// 获取运行时元数据版本 15
final version = await api.runtimeCall(
apiName: "Metadata",
methodName: "metadata_at_version",
params: [15],
rpc: provider,
fromTemplate: false);
// 定义 Substrate 地址以进行账户操作
final addr =
SubstrateAddress("5GjQNXpyZoyYiQ8GdB5fgjRkZdh3EgELwGdEmPP44YDnMx43");
// 获取指定地址的账户 nonce
final accountNonce = await api.runtimeCall(
apiName: "AccountNonceApi",
methodName: "account_nonce",
params: [
{"type": "[U8;32]", "value": addr.toBytes()}
],
rpc: provider,
fromTemplate: true);
}
JSON-RPC 示例
以下是一个自定义实现的 Substrate HTTP 服务示例,展示了如何使用 polkadot_dart
插件进行 JSON-RPC 请求:
import 'package:polkadot_dart/polkadot_dart.dart';
import 'package:http/http.dart' as http;
class MySubstrateHttpService with SubstrateServiceProvider {
MySubstrateHttpService(this.url,
{http.Client? client, this.defaultTimeOut = const Duration(seconds: 30)})
: client = client ?? http.Client();
final String url;
final http.Client client;
final Duration defaultTimeOut;
@override
Future<BaseServiceResponse<T>> doRequest<T>(
SubstrateRequestDetails params, {Duration? timeout}) async {
final response = await client
.post(params.toUri(url), headers: params.headers, body: params.body())
.timeout(timeout ?? defaultTimeOut);
return params.toResponse(response.bodyBytes, response.statusCode);
}
}
void main() async {
// 创建带有自定义 Substrate HTTP 服务的提供者
final provider = SubstrateProvider(
MySubstrateHttpService("https://westend-rpc.polkadot.io"));
// 从区块链获取创世哈希
final genesisHash = await provider
.request(const SubstrateRequestChainGetBlockHash<String>(number: 0));
// 从区块链获取最终化块哈希
final blockHash = await provider
.request(const SubstrateRequestChainChainGetFinalizedHead());
// 使用块哈希获取块头
final blockHeader = await provider
.request(SubstrateRequestChainChainGetHeader(atBlockHash: blockHash));
}
地址和密钥管理示例
以下是一个地址和密钥管理示例,展示了如何使用 polkadot_dart
插件生成和管理地址及密钥:
import 'package:polkadot_dart/polkadot_dart.dart';
void main() {
// 创建用于生成私钥的种子
List<int> seedBytes = BytesUtils.fromHexString(
"4bc884e0eb595d3c71dc4c62305380a43d7a820b9cf69753232196b34486a27c");
// 使用 SR25519 算法生成 Substrate 私钥
SubstratePrivateKey privateKey = SubstratePrivateKey.fromSeed(
seedBytes: seedBytes, algorithm: SubstrateKeyAlgorithm.sr25519);
// 使用 Ed25519 算法生成 Substrate 私钥
privateKey = SubstratePrivateKey.fromSeed(
seedBytes: seedBytes, algorithm: SubstrateKeyAlgorithm.ed25519);
// 使用 Secp256k1 算法生成 Substrate 私钥
privateKey = SubstratePrivateKey.fromSeed(
seedBytes: seedBytes, algorithm: SubstrateKeyAlgorithm.secp256k1);
// 从私钥获取公钥
final pubKey = privateKey.toPublicKey();
// 从公钥获取地址
final address = pubKey.toAddress();
// 使用私钥对摘要进行签名
final sign = privateKey.sign(digest);
// 使用公钥验证消息签名
final verify = pubKey.verify(message, signature);
// 使用私钥生成 VRF 签名
final vrfSign = privateKey.vrfSign(message);
// 使用公钥验证 VRF 签名
final vrfVerify = pubKey.verify(message, signature);
// 从私钥派生子密钥
final derive = privateKey.derive("//mrtnetwork");
}
资源
- 全面测试:所有功能都经过了彻底测试,确保可靠性和准确性。
贡献
欢迎贡献!请遵循以下指南:
- 叉仓库并创建新分支。
- 进行更改并确保测试通过。
- 提交包含详细更改描述的拉取请求。
功能请求和 Bug
请在问题跟踪器中提交功能请求和 Bug。
更多关于Flutter Polkadot交互插件polkadot_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter Polkadot交互插件polkadot_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个使用 polkadot_dart
插件与 Polkadot 网络进行交互的 Flutter 示例代码。这个示例将展示如何连接到 Polkadot 节点、获取链信息以及发送一个简单的 RPC 请求。
首先,确保你已经在 pubspec.yaml
文件中添加了 polkadot_dart
依赖:
dependencies:
flutter:
sdk: flutter
polkadot_dart: ^x.y.z # 替换为最新版本号
然后运行 flutter pub get
来获取依赖。
接下来,在你的 Flutter 项目中创建一个 Dart 文件(例如 polkadot_service.dart
),用于封装与 Polkadot 的交互逻辑:
import 'package:polkadot_dart/polkadot_dart.dart';
import 'dart:convert';
class PolkadotService {
// Polkadot 节点 URL
static const String nodeUrl = 'wss://your-polkadot-node-url';
// 初始化 Polkadot API
late Api api;
PolkadotService() {
initializeApi();
}
Future<void> initializeApi() async {
api = await ApiPromise.create({
provider: new WebSocketProvider(nodeUrl),
types: {
// 可以在这里添加自定义类型
},
});
}
// 获取链信息
Future<Map<String, dynamic>> getChainInfo() async {
try {
const chainInfo = await api.rpc.system.chain();
const name = await api.rpc.system.name();
const version = await api.rpc.system.version();
return {
'chain': chainInfo.toString(),
'name': name.toString(),
'version': version.toString(),
};
} catch (error) {
print('Error fetching chain info: $error');
return {};
}
}
// 发送一个 RPC 请求示例:获取余额
Future<String?> getBalance(String address) async {
try {
const balance = await api.query.system.account(address);
if (balance.isSome) {
const data = balance.unwrap().data;
const freeBalance = data.free.toString();
return freeBalance;
} else {
return null;
}
} catch (error) {
print('Error fetching balance: $error');
return null;
}
}
}
在你的主 Dart 文件(例如 main.dart
)中,使用这个服务来获取链信息和余额:
import 'package:flutter/material.dart';
import 'polkadot_service.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late PolkadotService polkadotService;
String? chainInfo;
String? balance;
@override
void initState() {
super.initState();
polkadotService = PolkadotService();
fetchChainInfo();
fetchBalance('your-account-address');
}
Future<void> fetchChainInfo() async {
chainInfo = jsonEncode(await polkadotService.getChainInfo());
setState(() {});
}
Future<void> fetchBalance(String address) async {
balance = await polkadotService.getBalance(address);
setState(() {});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Polkadot Interaction'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text('Chain Info:'),
Text(chainInfo ?? 'Loading...'),
SizedBox(height: 20),
Text('Balance:'),
Text(balance ?? 'Loading...'),
],
),
),
),
);
}
}
注意:
- 替换
wss://your-polkadot-node-url
为实际的 Polkadot 节点 WebSocket URL。 - 替换
'your-account-address'
为你想要查询余额的账户地址。 polkadot_dart
插件可能会随着 Polkadot 网络和 API 的更新而发生变化,因此请查阅最新的文档和示例以确保兼容性。
这个示例展示了如何使用 polkadot_dart
插件进行基本的 Polkadot 网络交互。你可以根据需要扩展这个示例,以处理更复杂的操作和类型。