Flutter Polkadot交互插件polkadot_dart的使用

发布于 1周前 作者 eggper 来自 Flutter

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

1 回复

更多关于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...'),
            ],
          ),
        ),
      ),
    );
  }
}

注意:

  1. 替换 wss://your-polkadot-node-url 为实际的 Polkadot 节点 WebSocket URL。
  2. 替换 'your-account-address' 为你想要查询余额的账户地址。
  3. polkadot_dart 插件可能会随着 Polkadot 网络和 API 的更新而发生变化,因此请查阅最新的文档和示例以确保兼容性。

这个示例展示了如何使用 polkadot_dart 插件进行基本的 Polkadot 网络交互。你可以根据需要扩展这个示例,以处理更复杂的操作和类型。

回到顶部