Flutter中Solana钱包交易失败问题求助
Solana钱包适配器转账问题修复指南
问题背景
使用solana_wallet_adapter调用Phantom、Solflare钱包进行SOL转账时遇到以下问题:
Phantom钱包问题
- 能正常显示连接授权弹窗和交易批准弹窗,但点击「批准」后无响应,一段时间后控制台报错:
E/flutter (28131): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: [JsonRpcException<dynamic>] -3 : sign request declined
Solflare钱包问题
- 能成功连接钱包,但滑动批准交易时提示「发生未知错误」,控制台反复出现:
E/flutter (29334): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: MissingPluginException(No implementation found for method callAsyncJavaScript on channel com.pichillilorenzo/flutter_inappwebview_...)
原代码
final adapter = SolanaWalletAdapter( const AppIdentity(), cluster: Cluster.devnet, ); final Cluster cluster = Cluster.devnet; var blockHash = await web3.Connection(cluster).getLatestBlockhash(); final connection = web3.Connection( cluster, ); AuthorizeResult result = await adapter.authorize(); final connectedAccount = adapter.connectedAccount?.address; if (connectedAccount == null) throw Exception('Connect wallet before sending tx.'); // Mobile wallet displays the address as base-58. final rcv = web3.PublicKey.fromBase58("<some-address-here>"); // Mobile wallet adapter specification returns address as base-64 final snd = web3.PublicKey.fromBase64(connectedAccount); print('creating transaction'); // Get latest blockhash info. final blockhash = await connection.getLatestBlockhash(); // Create transfer tx. final transaction = web3.Transaction( feePayer: snd, // The account that will sign the tx (and pay any fees). recentBlockhash: blockhash.blockhash, lastValidBlockHeight: blockhash.lastValidBlockHeight, instructions: [ SystemProgram.transfer( fromPublicKey: snd, toPublicKey: rcv, lamports: web3.solToLamports(1), ), ], ); // Serialize tx. const config = web3.SerializeConfig(requireAllSignatures: false); final tt = transaction.serialize(config).getString(web3.BufferEncoding.base64); print('creating transaction string $tt'); // Sign and send tx. final txresult = await adapter.signAndSendTransactions(transactions: [tt]); // Wait for confirmation. await connection.confirmTransaction(base64ToBase58(txresult.signatures.first!));
解决方案
针对Phantom钱包的修复
- 复用钱包适配器实例:不要每次点击按钮都新建
SolanaWalletAdapter,重复创建会导致会话异常,建议将其作为类成员变量复用。 - 优化Blockhash获取时机:提前获取的blockhash可能过期,必须在创建交易前才获取最新的blockhash,确保交易有效性。
- 简化交易流程:移除冗余的
blockHash变量,避免混淆。
针对Solflare钱包的修复
- 集成flutter_inappwebview插件:Solflare内部依赖该插件的
callAsyncJavaScript方法,需在项目中添加依赖:- 在
pubspec.yaml中添加:dependencies: flutter_inappwebview: ^6.0.0 # 版本建议与solana_wallet_adapter依赖的版本匹配 - 执行
flutter pub get后重新运行项目。
- 在
- 配置平台权限:
- Android:在
AndroidManifest.xml中添加网络权限:<uses-permission android:name="android.permission.INTERNET" /> - iOS:在
Info.plist中添加App Transport Security例外(若需要):<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
- Android:在
- 清理缓存重建项目:执行
flutter clean后重新构建,避免缓存导致的插件未加载问题。
修正后的代码示例
// 作为类成员变量,复用适配器实例 final SolanaWalletAdapter _adapter = SolanaWalletAdapter( const AppIdentity(), cluster: Cluster.devnet, ); final Cluster _cluster = Cluster.devnet; late final web3.Connection _connection; @override void initState() { super.initState(); _connection = web3.Connection(_cluster); } // 按钮点击触发的转账方法 Future<void> _sendSol() async { try { // 授权连接钱包 await _adapter.authorize(); final connectedAccount = _adapter.connectedAccount?.address; if (connectedAccount == null) throw Exception('请先连接钱包'); // 解析收款和付款地址 final receiver = web3.PublicKey.fromBase58("<some-address-here>"); final sender = web3.PublicKey.fromBase64(connectedAccount); // 创建交易前获取最新Blockhash final blockhashInfo = await _connection.getLatestBlockhash(); // 构建转账交易 final transaction = web3.Transaction( feePayer: sender, recentBlockhash: blockhashInfo.blockhash, lastValidBlockHeight: blockhashInfo.lastValidBlockHeight, instructions: [ SystemProgram.transfer( fromPublicKey: sender, toPublicKey: receiver, lamports: web3.solToLamports(1), ), ], ); // 序列化交易 const serializeConfig = web3.SerializeConfig(requireAllSignatures: false); final serializedTx = transaction.serialize(serializeConfig).getString(web3.BufferEncoding.base64); // 签名并发送交易 final txResult = await _adapter.signAndSendTransactions(transactions: [serializedTx]); // 确认交易 if (txResult.signatures.isNotEmpty) { await _connection.confirmTransaction(txResult.signatures.first); } } catch (e) { print('转账失败:$e'); } }
内容的提问来源于stack exchange,提问作者Wilfred Almeida
相关产品推荐
相关产品推荐

