Flutter区块链交互插件dart_web3_core的使用

Flutter区块链交互插件dart_web3_core的使用

dart_web3_core 是一个用于 Dart 的 Web3 库,允许你通过 HTTP 或 WebSocket 与本地或远程以太坊节点进行交互。它支持自定义凭据提供者,如 WalletConnect 和 MetaMask。

特性

  • 通过 RPC API 连接到以太坊节点并调用常用方法。
  • 发送签名的以太坊交易。
  • 生成私钥并设置新的以太坊地址。
  • 调用智能合约上的函数并监听合约事件。
  • 基于智能合约的 ABI 自动生成 Dart 绑定,以便更轻松地交互。

TODO

  • 编码所有支持的 Solidity 类型,目前仅不支持 (u)fixed 类型。
  • 提高测试覆盖率。
  • 提供 Wallet Connect 示例。

使用

凭证和钱包

为了在以太坊网络上发送交易,需要一些凭证。该库支持原始私钥和版本 3 钱包文件。

import 'dart:math'; // 用于随机数生成器
import 'package:dart_web3_core/dart_web3_core.dart';

// 可以从私钥创建凭证
Credentials fromHex = EthPrivateKey.fromHex("c87509a[...]dc0d3");

// 或者随机生成一个新的密钥
var rng = Random.secure();
Credentials random = EthPrivateKey.createRandom(rng);

// 不管哪种方式,库都可以从私钥派生出公钥和地址:
var address = credentials.address;
print(address.hex);

// 另一种获取凭证的方式是使用钱包文件。钱包文件安全地存储了私钥,并且需要密码解锁。
import 'dart:io';
import 'package:dart_web3_core/dart_web3_core.dart';

String content = File("wallet.json").readAsStringSync();
Wallet wallet = Wallet.fromJson(content, "testpassword");

Credentials unlocked = wallet.privateKey;
// 现在可以使用这些凭证来签署交易或消息

你也可以使用此库创建钱包文件。为此,首先需要你想要加密的私钥和一个密码。然后创建你的钱包:

Wallet wallet = Wallet.createNew(credentials, "password", random);
print(wallet.toJson());

你可以将 wallet.toJson() 写入一个文件,稍后可以用 MyEtherWallet(选择 Keystore / JSON 文件)或其他以太坊客户端打开它。

自定义凭证

如果你想将 dart_web3_core 与其他钱包提供商集成,可以实现 Credentials 并覆盖适当的方法。

连接到 RPC 服务器

该库不会自行向矿工发送已签名的交易。相反,它依赖于一个 RPC 客户端来完成这项工作。你可以使用 Infura 提供的公共 RPC API,设置自己的 RPC 服务(例如使用 Geth),或者如果你只想测试一下,可以使用 Truffle 和 Ganache 设置一个私有测试网。所有这些选项都会为你提供一个 RPC 端点,该库可以连接到该端点。

import 'package:dart_web3_core/dart_web3_core.dart';

final client = Web3Client("https://mainnet.infura.io/v3/your_project_id", Client());

// 当你不再使用客户端时,务必调用 dispose() 关闭打开的连接。
// 该客户端基于 http.Client,因此关闭的方式取决于你使用的客户端。
// 使用默认的 http.Client 时,可以创建一个新的客户端并在完成后调用 client.close()。
// 这将关闭所有待处理的 HTTP 请求,因此在真正停止与客户端的交互之前,请不要调用 dispose。

与智能合约交互

该库有一个高级类用于与智能合约进行交互。首先,你需要一个 DeployedContract 对象,它代表部署在区块链上的合约。你需要知道它的地址和 ABI 来创建它。

import 'package:dart_web3_core/dart_web3_core.dart';
import 'package:web_socket_channel/io.dart';

// 你可以从 ABI 和合约地址创建 DeployedContract ...
var abiCode = '[{"constant":true,"inputs":[],"name":"count","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]';
var contractAddress = EthereumAddress.fromHex('0x2c...');

var contract = DeployedContract(ContractAbi.fromJson(abiCode, 'MyContract'), contractAddress);

// ... 或者如果你有源代码和 ABI,可以使用 build_runner 和 web3dart 生成一个。
// 查看示例和文档了解详细信息。

// 一旦有了合约,就可以调用其方法!例如,要查询上面声明的 "count" 方法,我们可以使用
var count = contract.function('count');

// 现在我们可以读取合约的数据。所有由合约返回的数据都包装在一个 `List<dynamic>` 中,
// 因此我们需要提取第一个元素并将其转换为 BigInt。
// 注意,我们没有为这次调用指定任何参数 - 如果函数需要参数,只需将它们作为第二个参数传递给 `call`。
var result = await client.call(contract: contract, function: count, params: []);

BigInt count = result.first as BigInt;
print(count); // 打印 count 的当前值

要发送交易,使用你的凭证对象上的 sendTransaction 方法。例如,要增加上面合约中的 count 值,我们可以调用:

await credentials.sendTransaction(client,
  Transaction.callContract(contract: contract, function: count, parameters: []));

要监听合约发出的事件,可以使用:

var subscription = client.events(FilterOptions.events(
  contract: contract, event: contract.event('MyEvent'))).listen((event) {
    final decoded = contract.event('MyEvent').decodeResults(event.topics, event.data);

    // 处理事件数据
    print(decoded);
  });

// 完成后别忘了取消订阅
await subscription.cancel();

使用流

该库支持 Dart 的流抽象,用于实时更新。例如,你可以订阅新挖掘的区块:

var sub = client.newBlocks().listen((block) {
    print(block);
  });

// 和任何流一样,完成时别忘了取消订阅。
await sub.cancel();

MetaMask 示例

import 'dart:convert';
import 'dart:html';
import 'dart:typed_data';

// 为了支持单个代码库的 web 和其他平台构建,有条件地导入依赖项
import 'package:js/js.dart'
    if (dart.library.io) 'package:dart_web3_core/lib/src/browser/js-stub.dart'
    if (dart.library.js) 'package:js/js.dart';
import 'package:dart_web3_core/browser.dart'
    if (dart.library.io) 'package:dart_web3_core/lib/src/browser/dart_wrappers_stub.dart'
    if (dart.library.js) 'package:dart_web3_core/browser.dart';
import 'package:dart_web3_core/dart_web3_core.dart';

@JS()
@anonymous
class JSrawRequestParams {
  external String get chainId;

  // 必须有一个具有命名参数的无名工厂构造函数
  external factory JSrawRequestParams({String chainId});
}

Future<void> main() async {
  final eth = window.ethereum;
  if (eth == null) {
    print('MetaMask 未可用');
    return;
  }

  final client = Web3Client.custom(eth.asRpcService());
  final credentials = await eth.requestAccount();
  // 你也可以使用 eth.requestAllAccounts() 获取所有授权的 MetaMask 账户。
  print('使用 ${credentials.address}');
  print('客户端正在监听: ${await client.isListeningForNetwork()}');

  final message = Uint8List.fromList(utf8.encode('Hello from webthree'));
  final signature = await credentials.signPersonalMessage(message);
  print('签名: ${base64.encode(signature)}');

  await eth.rawRequest('wallet_switchEthereumChain',
      params: [JSrawRequestParams(chainId: '0x507')]);
  final String chainIDHex = await eth.rawRequest('eth_chainId') as String;
  final chainID = int.parse(chainIDHex);
  print('链 ID: $chainID');
}

智能合约

该库可以解析智能合约的 ABI 并向其发送数据。它还可以监听智能合约发出的事件。查看以下文件以获得示例:contracts.dart

Dart 代码生成器

通过使用 Dart 的构建系统,web3dart 可以生成 Dart 代码,从而更容易访问智能合约。

要使用此功能,将合约的 ABI JSON 放入 lib/src/generated/abi 目录。文件名必须以 .abi.json 结尾。

dart run build_runner build

现在你会找到一个 .g.dart 文件,其中包含与合约交互的代码。

可选:忽略生成文件的命名建议

如果导入的合约 ABI 具有不符合 Dart 命名约定的函数名称,Dart 分析器会默认显示警告。这可以通过排除所有生成的文件来分析来缓解。

在项目根目录下创建一个名为 analysis_options.yaml 的文件:

analyzer:
  excluding: 
    - '**/*.g.dart'

更多关于Flutter区块链交互插件dart_web3_core的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter区块链交互插件dart_web3_core的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个使用 dart_web3_core 库与区块链进行交互的 Flutter 代码示例。这个示例展示了如何连接到以太坊区块链,获取账户余额,以及发送一个简单的交易。

首先,确保你的 Flutter 项目中已经添加了 dart_web3_core 依赖。在你的 pubspec.yaml 文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  dart_web3_core: ^x.y.z  # 请替换为最新版本号

然后运行 flutter pub get 来获取依赖。

接下来,我们编写一个 Flutter 应用来演示如何使用 dart_web3_core

import 'package:flutter/material.dart';
import 'package:dart_web3_core/dart_web3_core.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Blockchain Interaction'),
        ),
        body: Center(
          child: BlockchainInteraction(),
        ),
      ),
    );
  }
}

class BlockchainInteraction extends StatefulWidget {
  @override
  _BlockchainInteractionState createState() => _BlockchainInteractionState();
}

class _BlockchainInteractionState extends State<BlockchainInteraction> {
  String accountBalance = '';

  @override
  void initState() {
    super.initState();
    _connectToBlockchain();
  }

  Future<void> _connectToBlockchain() async {
    // 配置你的以太坊节点URL
    final String infuraUrl = 'https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID';
    final Web3Client client = Web3Client(infuraUrl, http.Client());

    // 替换为你的以太坊账户地址
    final String accountAddress = 'YOUR_ETHEREUM_ACCOUNT_ADDRESS';

    // 获取账户余额
    final BigInt balance = await client.getBalance(accountAddress);
    setState(() {
      accountBalance = 'Account Balance: ${balance.toStringAsFixed(18)} ETH';
    });

    // 示例:发送交易(这里仅作为示例,实际发送交易需要私钥和更多的处理)
    // 注意:发送交易需要私钥,私钥不应硬编码在客户端应用中,这里仅用于演示
    // final String privateKey = 'YOUR_PRIVATE_KEY';
    // final Credentials credentials = Credentials.fromPrivateKey(Uint8List.fromList(privateKey.codeUnits));
    // final TransactionReceipt receipt = await client.sendTransaction(
    //   credentials,
    //   Transaction(
    //     to: accountAddress,
    //     value: EtherAmount.fromUnitAndValue(EtherUnit.wei, BigInt.from(0.01 * 1e18)), // 0.01 ETH
    //     gasPrice: await client.getGasPrice(),
    //     maxGas: BigInt.from(21000), // Gas limit
    //   ),
    // );
    // print('Transaction receipt: $receipt');
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(accountBalance),
        // 添加一个按钮来触发交易(仅作为示例,不建议在真实应用中这样处理私钥)
        // ElevatedButton(
        //   onPressed: () async {
        //     await _sendTransaction();
        //   },
        //   child: Text('Send Transaction'),
        // ),
      ],
    );
  }
}

注意事项:

  1. Infura URL 和账户地址:替换 YOUR_INFURA_PROJECT_IDYOUR_ETHEREUM_ACCOUNT_ADDRESS 为你自己的 Infura 项目 ID 和以太坊账户地址。
  2. 私钥管理:在真实应用中,私钥应该安全存储,并且绝不应该硬编码在客户端应用中。私钥管理通常涉及服务器端组件或使用硬件钱包。
  3. 交易发送:示例代码中发送交易的部分被注释掉了,因为在实际应用中,私钥管理非常敏感,不应在客户端代码中直接处理。

这个示例展示了如何使用 dart_web3_core 库连接到以太坊区块链,获取账户余额,并提供了发送交易的框架代码。你可以根据具体需求进一步扩展和完善这个示例。

回到顶部