Flutter Solana钱包管理插件solana_wallet_adapter的使用

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

Flutter Solana钱包管理插件 solana_wallet_adapter 的使用

solana_wallet_adapter 是一个实现了 Solana 移动钱包适配器协议的 Dart 库。该库允许 dApps 与 Android 和 iOS 上的钱包应用程序(如 Solflare)进行交互。

非特权方法

非特权方法不需要当前会话处于授权状态即可调用:

  • authorize
  • deauthorize
  • reauthorize
  • getCapabilities

特权方法

特权方法需要当前会话处于授权状态才能调用:

  • signTransactions
  • signAndSendTransactions
  • signMessages

设置

确保你的钱包应用(例如 Phantom)和 SolanaWalletAdapter 连接到相同的网络。请注意,钱包应用可能不支持本地主机。

示例:授权

以下是一个简单的示例,展示了如何使用 solana_wallet_adapter 来授权应用:

import 'package:flutter/material.dart';
import 'package:solana_wallet_adapter/solana_wallet_adapter.dart';

void main() {
  runApp(const MaterialApp(
    home: Scaffold(
      body: Center(
        child: AuthorizeButton(),
      ),
    ),
  ));
}

// 创建 [SolanaWalletAdapter] 实例。
final adapter = SolanaWalletAdapter(
  const AppIdentity(),
  // 注意:将钱包应用连接到同一个网络。
  cluster: Cluster.devnet,
);

class AuthorizeButton extends StatefulWidget {
  const AuthorizeButton({super.key});
  [@override](/user/override)
  State<AuthorizeButton> createState() => _AuthorizeButtonState();
}

class _AuthorizeButtonState extends State<AuthorizeButton> {
  Object? _output;
  
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        TextButton(
          onPressed: () {
            // 授权应用与钱包交互。
            adapter.authorize()
              .then((result) => setState(() => _output = result.toJson()))
              .catchError((error) => setState(() => _output = error));
          },
          child: const Text('Authorize'),
        ),
        if (_output != null)
          Text(_output.toString()),
      ],
    );
  }
}

完整示例 Demo

下面是一个更完整的示例,展示了如何连接、断开连接、签名交易以及发送交易等操作:

/// 导入必要的包
import 'package:flutter/material.dart';
import 'package:solana_wallet_adapter/solana_wallet_adapter.dart';
import 'package:solana_web3/programs.dart';
import 'package:solana_web3/solana_web3.dart';

/// 主函数
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MaterialApp(
    home: Scaffold(
      body: ExampleApp(),
    ),
  ));
}

/// 转账数据结构
class TransferData {
  const TransferData({
    required this.transaction,
    required this.receiver,
    required this.lamports,
  });
  final Transaction transaction;
  final Keypair receiver;
  final BigInt lamports;
}

/// 示例应用
class ExampleApp extends StatefulWidget {
  const ExampleApp({super.key});

  [@override](/user/override)
  State<ExampleApp> createState() => _ExampleAppState();
}

/// 示例应用状态
class _ExampleAppState extends State<ExampleApp> {
  late final Future<void> _future;
  String? _status;
  static final Cluster cluster = Cluster.devnet;

  final SolanaWalletAdapter adapter = SolanaWalletAdapter(
    AppIdentity(),
    cluster: cluster,
    hostAuthority: null,
  );

  [@override](/user/override)
  void initState() {
    super.initState();
    _future = SolanaWalletAdapter.initialize();
  }

  Future<void> _connect() async {
    if (!adapter.isAuthorized) {
      await adapter.authorize(walletUriBase: adapter.store.apps[1].walletUriBase);
      setState(() {});
    }
  }

  Future<void> _disconnect() async {
    if (adapter.isAuthorized) {
      await adapter.deauthorize();
      setState(() {});
    }
  }

  Future<void> _airdrop(final Connection connection, final Pubkey wallet) async {
    if (cluster != Cluster.mainnet) {
      setState(() => _status = "Requesting airdrop...");
      await connection.requestAndConfirmAirdrop(wallet, solToLamports(2).toInt());
    }
  }

  Future<List<TransferData>> _createTransfers(final Connection connection, {required int count}) async {
    final Pubkey? wallet = Pubkey.tryFromBase64(adapter.connectedAccount?.address);
    if (wallet == null) {
      throw 'Wallet not connected';
    }

    final BigInt lamports = solToLamports(0.001);
    if ((await connection.getBalance(wallet)) < (1000000 + lamports.toInt())) {
      await _airdrop(connection, wallet);
    }

    final latestBlockhash = await connection.getLatestBlockhash();
    final List<TransferData> txs = [];
    for (int i = 0; i < count; ++i) {
      final Keypair receiver = Keypair.generateSync();
      final Transaction transaction = Transaction.v0(
        payer: wallet,
        recentBlockhash: latestBlockhash.blockhash,
        instructions: [
          SystemProgram.transfer(
            fromPubkey: wallet, 
            toPubkey: receiver.pubkey, 
            lamports: lamports,
          )
        ]
      );
      txs.add(TransferData(transaction: transaction, receiver: receiver, lamports: lamports));
    }
    return txs;
  }

  Future<void> _confirmTransfers(final Connection connection, {required List<String?> signatures, required List<TransferData> transfers}) async {
    await Future.wait(signatures.map((sig) => connection.confirmTransaction(base58To64Decode(sig!))), eagerError: true);

    final List<int> receiverBalances = await Future.wait(transfers.map((transfer) => connection.getBalance(transfer.receiver.pubkey)), eagerError: true);

    final List<String> results = [];
    for (int i = 0; i < receiverBalances.length; ++i) {
      final TransferData transfer = transfers[i];
      final Pubkey pubkey = transfer.receiver.pubkey;
      final BigInt balance = receiverBalances[i].toBigInt();
      if (balance != transfer.lamports) throw Exception('Post transaction balance mismatch.');
      results.add("Transfer: Address $pubkey received $balance SOL");
    }

    setState(() => _status = "Success!\n\nSignatures: $signatures\n\n${results.join('\n')}");
  }

  void _signTransactions(final int count) async {
    try {
      final Connection connection = Connection(cluster);
      final List<TransferData> transfers = await _createTransfers(connection, count: count);

      final SignTransactionsResult result = await adapter.signTransactions(
        transfers.map((transfer) => adapter.encodeTransaction(transfer.transaction)).toList(),
      );

      final List<String?> signatures = await connection.sendSignedTransactions(result.signedPayloads, eagerError: true);
      await _confirmTransfers(connection, signatures: signatures.map((e) => base58To64Encode(e!)).toList(), transfers: transfers);
    } catch (error, stack) {
      print('Sign Transactions Error: $error');
      print('Sign Transactions Stack: $stack');
      setState(() => _status = error.toString());
    }
  }

  Widget _builder(final BuildContext context, final AsyncSnapshot snapshot) {
    if (snapshot.connectionState != ConnectionState.done) {
      return const CircularProgressIndicator();
    }
    return ListView(
      shrinkWrap: true,
      padding: const EdgeInsets.all(24.0),
      children: [
        adapter.isAuthorized
          ? Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Text('${adapter.connectedAccount?.toBase58()}', textAlign: TextAlign.center),
                ElevatedButton(onPressed: _disconnect, style: ElevatedButton.styleFrom(backgroundColor: Colors.red), child: const Text('Disconnect')),
              ],
            )
          : ElevatedButton(onPressed: _connect, child: const Text('Connect')),
        const Divider(),
        Wrap(
          spacing: 24.0,
          runSpacing: 8.0,
          children: [
            ElevatedButton(onPressed: adapter.isAuthorized ? () => _signTransactions(1) : null, child: const Text('Sign Transactions (1)')),
            ElevatedButton(onPressed: adapter.isAuthorized ? () => _signTransactions(3) : null, child: const Text('Sign Transactions (3)')),
          ],
        ),
        Text(_status ?? ''),
      ],
    );
  }

  [@override](/user/override)
  Widget build(final BuildContext context) {
    return Center(child: FutureBuilder(future: _future, builder: _builder));
  }
}

更多关于Flutter Solana钱包管理插件solana_wallet_adapter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter Solana钱包管理插件solana_wallet_adapter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用solana_wallet_adapter插件的示例代码。这个插件允许你管理Solana钱包,包括连接、签名交易等功能。

首先,确保你已经在pubspec.yaml文件中添加了solana_wallet_adapter依赖项:

dependencies:
  flutter:
    sdk: flutter
  solana_wallet_adapter: ^最新版本号  # 替换为实际的最新版本号

然后运行flutter pub get来安装依赖。

接下来,我们创建一个简单的Flutter应用来演示如何使用solana_wallet_adapter

主应用文件 (main.dart)

import 'package:flutter/material.dart';
import 'package:solana_wallet_adapter/solana_wallet_adapter.dart';
import 'package:solana_wallet_adapter_wallets/solana_wallet_adapter_wallets.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Solana Wallet Adapter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: WalletManagerPage(),
    );
  }
}

class WalletManagerPage extends StatefulWidget {
  @override
  _WalletManagerPageState createState() => _WalletManagerPageState();
}

class _WalletManagerPageState extends State<WalletManagerPage> {
  late WalletAdapter walletAdapter;

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

    // 初始化钱包适配器
    walletAdapter = WalletAdapter(
      wallets: [
        // 添加你希望支持的钱包类型
        PhantomWallet(),
        SolletWallet(),
        // 添加更多钱包类型...
      ],
      autoConnect: true,  // 自动连接最后一个使用的钱包
    );

    // 监听钱包状态变化
    walletAdapter.onReady.listen((_) {
      print('Wallet is ready!');
      // 这里可以执行一些初始化操作,比如获取钱包余额等
    });

    walletAdapter.onDisconnected.listen((_) {
      print('Wallet is disconnected!');
    });

    // 加载钱包
    walletAdapter.load();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Solana Wallet Manager'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (walletAdapter.connected)
              Text(
                'Connected Wallet: ${walletAdapter.publicKey?.toBase58()}',
                style: TextStyle(fontSize: 20),
              )
            else
              Text(
                'No Wallet Connected',
                style: TextStyle(fontSize: 20, color: Colors.red),
              ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                if (!walletAdapter.connected) {
                  await walletAdapter.connect();
                } else {
                  await walletAdapter.disconnect();
                }
              },
              child: Text(walletAdapter.connected ? 'Disconnect' : 'Connect'),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                if (walletAdapter.connected) {
                  // 这里可以执行一些需要签名的交易操作
                  // 例如,发送SOL,或者调用一个智能合约
                  // 注意:这只是一个示例,实际交易需要更多的代码和安全性检查
                  print('Executing a transaction...');
                  // await walletAdapter.signTransaction(...);
                } else {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('Please connect a wallet first!')),
                  );
                }
              },
              child: Text('Execute Transaction'),
            ),
          ],
        ),
      ),
    );
  }
}

注意事项

  1. 安全性:在实际应用中,处理钱包和交易时需要特别注意安全性。确保你的应用通过安全的通道与钱包交互,并且用户充分理解他们正在执行的操作。

  2. 依赖管理:确保你的项目依赖是最新的,因为solana_wallet_adapter和相关插件可能会更新。

  3. 错误处理:示例代码中没有包含详细的错误处理逻辑。在实际应用中,你应该添加适当的错误处理来应对连接失败、交易失败等情况。

  4. 更多功能solana_wallet_adapter插件提供了更多功能,比如监听账户余额变化、监听交易确认等。你可以查阅官方文档来了解这些功能。

这个示例代码展示了如何使用solana_wallet_adapter插件来管理Solana钱包的基本操作。你可以根据需要进行扩展和修改。

回到顶部