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
更多关于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'),
// ),
],
);
}
}
注意事项:
- Infura URL 和账户地址:替换
YOUR_INFURA_PROJECT_ID
和YOUR_ETHEREUM_ACCOUNT_ADDRESS
为你自己的 Infura 项目 ID 和以太坊账户地址。 - 私钥管理:在真实应用中,私钥应该安全存储,并且绝不应该硬编码在客户端应用中。私钥管理通常涉及服务器端组件或使用硬件钱包。
- 交易发送:示例代码中发送交易的部分被注释掉了,因为在实际应用中,私钥管理非常敏感,不应在客户端代码中直接处理。
这个示例展示了如何使用 dart_web3_core
库连接到以太坊区块链,获取账户余额,并提供了发送交易的框架代码。你可以根据具体需求进一步扩展和完善这个示例。