From d872216e49066702225d42c33236cbbbf31df894 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Tue, 27 Aug 2024 16:26:54 +0200 Subject: [PATCH 1/7] solana v0 transaction example --- example/dapp/lib/main.dart | 31 ++-- example/dapp/lib/utils/crypto/solana.dart | 36 +++- example/dapp/lib/widgets/session_widget.dart | 1 + .../dependencies/chains/solana_service.dart | 161 ++++++++++++++++-- .../lib/dependencies/web3wallet_service.dart | 32 ++-- 5 files changed, 225 insertions(+), 36 deletions(-) diff --git a/example/dapp/lib/main.dart b/example/dapp/lib/main.dart index cf5565c..1e73cc9 100644 --- a/example/dapp/lib/main.dart +++ b/example/dapp/lib/main.dart @@ -99,17 +99,7 @@ class _MyHomePageState extends State { _web3App!.onSessionAuthResponse.subscribe(_onSessionAuthResponse); await _web3App!.init(); - - // Loop through all the chain data - for (final ChainMetadata chain in ChainData.allChains) { - // Loop through the events for that chain - for (final event in getChainEvents(chain.type)) { - _web3App!.registerEventHandler( - chainId: chain.chainId, - event: event, - ); - } - } + await _registerEventHandlers(); setState(() { _pageDatas = [ @@ -139,6 +129,25 @@ class _MyHomePageState extends State { }); } + Future _registerEventHandlers() async { + if (!_web3App!.core.connectivity.isOnline) { + await Future.delayed(const Duration(milliseconds: 500)); + _registerEventHandlers(); + return; + } + + // Loop through all the chain data + for (final ChainMetadata chain in ChainData.allChains) { + // Loop through the events for that chain + for (final event in getChainEvents(chain.type)) { + _web3App!.registerEventHandler( + chainId: chain.chainId, + event: event, + ); + } + } + } + void _onSessionConnect(SessionConnect? event) { log('[SampleDapp] _onSessionConnect $event'); } diff --git a/example/dapp/lib/utils/crypto/solana.dart b/example/dapp/lib/utils/crypto/solana.dart index 241309f..1b0abc7 100644 --- a/example/dapp/lib/utils/crypto/solana.dart +++ b/example/dapp/lib/utils/crypto/solana.dart @@ -28,6 +28,7 @@ class Solana { required String method, required ChainMetadata chainData, required String address, + bool isV0 = false, }) { final bytes = utf8.encode( 'This is an example message to be signed - ${DateTime.now()}', @@ -47,7 +48,40 @@ class Solana { ), ); case 'solana_signTransaction': - return web3App.request( + if (isV0) { + return web3App.request( + topic: topic, + chainId: chainData.chainId, + request: SessionRequestParams( + method: method, + params: { + "version": 0, + "message": { + "header": { + "numRequiredSignatures": 1, + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1 + }, + "recentBlockhash": + "H32Ss1hxpP2ZJM4whREVNyUWRgzFLVA97UXJUjBrEsgx", + "accountKeys": [ + "EbdEmCpKGvEwfwV4ACmVYHFRkwvXdogJhMZeEekDFVVJ", + "4SzUq9NNYSYGp41ED5NgSDoCrEh9MoD7zSvmtkwseW8s", + "11111111111111111111111111111111" + ], + "instructions": [ + { + "programIdIndex": 2, + "accounts": [0, 1], + "data": "AgAAAEAAABAAAAAA" + } + ] + } + }, + ), + ); + } + return web3App.signEngine.request( topic: topic, chainId: chainData.chainId, request: SessionRequestParams( diff --git a/example/dapp/lib/widgets/session_widget.dart b/example/dapp/lib/widgets/session_widget.dart index 1f20a00..7dd7d9e 100644 --- a/example/dapp/lib/widgets/session_widget.dart +++ b/example/dapp/lib/widgets/session_widget.dart @@ -249,6 +249,7 @@ class SessionWidgetState extends State { method: method, chainData: chainMetadata, address: address, + isV0: true, ); // case ChainType.kadena: // return Kadena.callMethod( diff --git a/example/wallet/lib/dependencies/chains/solana_service.dart b/example/wallet/lib/dependencies/chains/solana_service.dart index 3c330f5..a616bd2 100644 --- a/example/wallet/lib/dependencies/chains/solana_service.dart +++ b/example/wallet/lib/dependencies/chains/solana_service.dart @@ -10,8 +10,8 @@ import 'package:walletconnect_flutter_v2_wallet/dependencies/i_web3wallet_servic import 'package:walletconnect_flutter_v2_wallet/dependencies/key_service/i_key_service.dart'; import 'package:walletconnect_flutter_v2_wallet/models/chain_metadata.dart'; -import 'package:solana/solana.dart'; -import 'package:solana/encoder.dart'; +import 'package:solana/solana.dart' as solana; +import 'package:solana/encoder.dart' as encoder; // ignore: depend_on_referenced_packages import 'package:bs58/bs58.dart'; @@ -49,14 +49,19 @@ class SolanaService { ); final secKeyBytes = keys[0].privateKey.parse32Bytes(); - final keyPair = await Ed25519HDKeyPair.fromPrivateKeyBytes( + final keyPair = await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( privateKey: secKeyBytes, ); // it's being sent encoded from dapp final base58Decoded = base58.decode(message); final decodedMessage = utf8.decode(base58Decoded); - if (await CommonMethods.requestApproval(decodedMessage, title: method)) { + if (await CommonMethods.requestApproval( + decodedMessage, + method: method, + chainId: pRequest.chainId, + address: keyPair.address, + )) { final signature = await keyPair.sign(base58Decoded.toList()); response = response.copyWith( @@ -94,6 +99,10 @@ class SolanaService { try { final params = parameters as Map; + if (params.containsKey('version')) { + return await solanaSignV0Transaction(topic, parameters); + } + final feePayer = params['feePayer'].toString(); final recentBlockHash = params['recentBlockhash'].toString(); final instructionsList = params['instructions'] as List; @@ -103,7 +112,7 @@ class SolanaService { ); final secKeyBytes = keys[0].privateKey.parse32Bytes(); - final keyPair = await Ed25519HDKeyPair.fromPrivateKeyBytes( + final keyPair = await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( privateKey: secKeyBytes, ); @@ -113,16 +122,21 @@ class SolanaService { const encoder = JsonEncoder.withIndent(' '); final transaction = encoder.convert(params); - if (await CommonMethods.requestApproval(transaction, title: method)) { + if (await CommonMethods.requestApproval( + transaction, + method: method, + chainId: pRequest.chainId, + address: keyPair.address, + )) { // Sign the transaction. final instructions = instructionsList.map((json) { return (json as Map).toInstruction(); }).toList(); - final message = Message(instructions: instructions); + final message = solana.Message(instructions: instructions); final compiledMessage = message.compile( recentBlockhash: recentBlockHash, - feePayer: Ed25519HDPublicKey.fromBase58(feePayer), + feePayer: solana.Ed25519HDPublicKey.fromBase58(feePayer), ); final signature = await keyPair.sign(compiledMessage.toByteArray()); @@ -151,6 +165,95 @@ class SolanaService { CommonMethods.goBackToDapp(topic, response.result ?? response.error); } + + Future solanaSignV0Transaction(String topic, dynamic parameters) async { + debugPrint( + '[SampleWallet] solanaSignV0Transaction: ${jsonEncode(parameters)}'); + const method = 'solana_signTransaction'; + final pRequest = _web3Wallet.pendingRequests.getAll().last; + var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); + + try { + final params = parameters as Map; + final version = params['version'] as int; + if (version != 0) { + response = response.copyWith( + error: const JsonRpcError( + code: 0, + message: 'Transaction is not v0', + ), + ); + } else { + final message = params['message'] as Map; + // final header = message['header'] as Map; + final recentBlockHash = message['recentBlockhash'] as String; + final accountKeys = (message['accountKeys'] as List) + .map((key) => solana.Ed25519HDPublicKey.fromBase58(key)) + .toList(); + + final feePayer = accountKeys.first.toString(); + final instructionsList = message['instructions'] as List; + + final keys = GetIt.I().getKeysForChain( + chainSupported.chainId, + ); + final secKeyBytes = keys[0].privateKey.parse32Bytes(); + + final keyPair = await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( + privateKey: secKeyBytes, + ); + + if (keyPair.address != feePayer) { + throw Exception('Error'); + } + + const encoder = JsonEncoder.withIndent(' '); + final transaction = encoder.convert(params); + if (await CommonMethods.requestApproval( + transaction, + method: method, + chainId: pRequest.chainId, + address: keyPair.address, + )) { + // Sign the transaction. + final instructions = instructionsList.map((json) { + debugPrint('[SampleWallet] json $json'); + return (json as Map).toV0Instruction(accountKeys); + }).toList(); + + final message = solana.Message(instructions: instructions); + final compiledMessage = message.compile( + recentBlockhash: recentBlockHash, + feePayer: solana.Ed25519HDPublicKey.fromBase58(feePayer), + ); + + final signature = await keyPair.sign(compiledMessage.toByteArray()); + + response = response.copyWith( + result: { + 'signature': signature.toBase58(), + }, + ); + } else { + response = response.copyWith( + error: const JsonRpcError(code: 5001, message: 'User rejected'), + ); + } + } + } catch (e, s) { + debugPrint('[SampleWallet] solanaSignV0Transaction error $e, $s'); + response = response.copyWith( + error: JsonRpcError(code: 0, message: e.toString()), + ); + } + + await _web3Wallet.respondSessionRequest( + topic: topic, + response: response, + ); + + CommonMethods.goBackToDapp(topic, response.result ?? response.error); + } } extension on String { @@ -166,26 +269,56 @@ extension on String { } extension on Map { - Instruction toInstruction() { + encoder.Instruction toInstruction() { final programId = this['programId'] as String; - final programKey = Ed25519HDPublicKey(base58.decode(programId).toList()); + final programKey = + solana.Ed25519HDPublicKey(base58.decode(programId).toList()); final data = (this['data'] as List).map((e) => e as int).toList(); final data58 = base58.encode(Uint8List.fromList(data)); - final dataBytes = ByteArray.fromBase58(data58); + final dataBytes = encoder.ByteArray.fromBase58(data58); final keys = this['keys'] as List; - return Instruction( + return encoder.Instruction( programId: programKey, data: dataBytes, accounts: keys.map((k) { final kParams = (k as Map); - return AccountMeta( - pubKey: Ed25519HDPublicKey.fromBase58(kParams['pubkey']), + return encoder.AccountMeta( + pubKey: solana.Ed25519HDPublicKey.fromBase58(kParams['pubkey']), isWriteable: kParams['isWritable'] as bool, isSigner: kParams['isSigner'] as bool, ); }).toList(), ); } + + encoder.Instruction toV0Instruction( + List accountKeys, + ) { + final programIdIndex = this['programIdIndex'] as int; + final programKey = accountKeys.elementAt(programIdIndex); + + final accounts = (this['accounts'] as List).map((index) { + // Assuming first account in each instruction is signer + final isSigner = index == 0; + // Assuming second account in each instruction is writable + final isWriteable = index == 1; + return encoder.AccountMeta( + pubKey: accountKeys[index], + isSigner: isSigner, + isWriteable: isWriteable, + ); + }).toList(); + + final data = base64Decode(this['data'].toString()); + final data58 = base58.encode(Uint8List.fromList(data)); + final dataBytes = encoder.ByteArray.fromBase58(data58); + + return encoder.Instruction( + programId: programKey, + data: dataBytes, + accounts: accounts, + ); + } } diff --git a/example/wallet/lib/dependencies/web3wallet_service.dart b/example/wallet/lib/dependencies/web3wallet_service.dart index 183a21e..8ae5391 100644 --- a/example/wallet/lib/dependencies/web3wallet_service.dart +++ b/example/wallet/lib/dependencies/web3wallet_service.dart @@ -98,22 +98,34 @@ class Web3WalletService extends IWeb3WalletService { Future init() async { // Await the initialization of the web3wallet await _web3Wallet!.init(); + await _emitEvent(); + } + + Future _emitEvent() async { + if (!_web3Wallet!.core.connectivity.isOnline) { + await Future.delayed(const Duration(milliseconds: 500)); + _emitEvent(); + return; + } final sessions = _web3Wallet!.sessions.getAll(); - final chainKeys = GetIt.I().getKeysForChain('eip155'); for (var session in sessions) { try { - final chainIds = NamespaceUtils.getChainIdsFromNamespaces( + final events = NamespaceUtils.getNamespacesEventsForChain( + chainId: 'eip155:1', namespaces: session.namespaces, ); - _web3Wallet!.emitSessionEvent( - topic: session.topic, - chainId: chainIds.first, - event: SessionEventParams( - name: 'accountsChanged', - data: [chainKeys.first.address], - ), - ); + if (events.contains('accountsChanged')) { + final chainKeys = GetIt.I().getKeysForChain('eip155'); + _web3Wallet!.emitSessionEvent( + topic: session.topic, + chainId: 'eip155:1', + event: SessionEventParams( + name: 'accountsChanged', + data: [chainKeys.first.address], + ), + ); + } } catch (_) {} } } From 75c8ea42f1493e436c8bd6fd614fd99cf30fba16 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Thu, 29 Aug 2024 10:05:33 +0200 Subject: [PATCH 2/7] solana v0 support --- .gitignore | 3 + example/dapp/lib/utils/crypto/chain_data.dart | 22 +-- example/dapp/lib/utils/crypto/solana.dart | 117 ++++++------ example/dapp/pubspec.yaml | 1 + .../lib/dependencies/chains/common.dart | 13 +- .../dependencies/chains/solana_service.dart | 179 ++++-------------- .../lib/dependencies/deep_link_handler.dart | 2 +- example/wallet/lib/models/chain_data.dart | 22 +-- 8 files changed, 130 insertions(+), 229 deletions(-) diff --git a/.gitignore b/.gitignore index de3a30b..f9a8f74 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,9 @@ app.*.map.json # fvm .fvm/ +**/secrets.properties +**/*.keystore + # vscode .vscode/ diff --git a/example/dapp/lib/utils/crypto/chain_data.dart b/example/dapp/lib/utils/crypto/chain_data.dart index 14eafe5..489f549 100644 --- a/example/dapp/lib/utils/crypto/chain_data.dart +++ b/example/dapp/lib/utils/crypto/chain_data.dart @@ -98,25 +98,19 @@ class ChainData { static final List solanaChains = [ const ChainMetadata( type: ChainType.solana, - chainId: 'solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ', - name: 'Solana Mainnet 1', + chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', + name: 'Solana Mainnet', logo: '/chain-logos/solana.png', color: Color.fromARGB(255, 247, 0, 255), - rpc: [ - 'https://rpc.ankr.com/solana', - 'https://api.tatum.io/v3/blockchain/node/solana-mainnet', - ], + rpc: ['https://api.mainnet-beta.solana.com'], ), const ChainMetadata( type: ChainType.solana, - chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', - name: 'Solana Mainnet 2', + chainId: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1', + name: 'Solana Devnet', logo: '/chain-logos/solana.png', color: Color.fromARGB(255, 247, 0, 255), - rpc: [ - 'https://rpc.ankr.com/solana', - 'https://api.tatum.io/v3/blockchain/node/solana-mainnet', - ], + rpc: ['https://api.devnet.solana.com'], ), const ChainMetadata( type: ChainType.solana, @@ -125,9 +119,7 @@ class ChainData { logo: '/chain-logos/solana.png', color: Colors.black, isTestnet: true, - rpc: [ - 'https://api.testnet.solana.com', - ], + rpc: ['https://api.testnet.solana.com'], ), ]; diff --git a/example/dapp/lib/utils/crypto/solana.dart b/example/dapp/lib/utils/crypto/solana.dart index 1b0abc7..318813e 100644 --- a/example/dapp/lib/utils/crypto/solana.dart +++ b/example/dapp/lib/utils/crypto/solana.dart @@ -2,6 +2,11 @@ import 'dart:convert'; // ignore: depend_on_referenced_packages import 'package:bs58/bs58.dart'; +import 'package:solana_web3/solana_web3.dart' as solana; +// import 'package:solana_web3/programs.dart'; +// import 'package:solana_web3/src/encodings/lamports.dart'; +// import 'package:solana_web3/src/rpc/models/blockhash_with_expiry_block_height.dart'; + import 'package:walletconnect_flutter_v2_dapp/models/chain_metadata.dart'; import 'package:walletconnect_flutter_v2_dapp/imports.dart'; @@ -29,13 +34,13 @@ class Solana { required ChainMetadata chainData, required String address, bool isV0 = false, - }) { - final bytes = utf8.encode( - 'This is an example message to be signed - ${DateTime.now()}', - ); - final message = base58.encode(bytes); + }) async { switch (method) { case 'solana_signMessage': + final bytes = utf8.encode( + 'This is an example message to be signed - ${DateTime.now()}', + ); + final message = base58.encode(bytes); return web3App.request( topic: topic, chainId: chainData.chainId, @@ -48,65 +53,63 @@ class Solana { ), ); case 'solana_signTransaction': - if (isV0) { - return web3App.request( - topic: topic, - chainId: chainData.chainId, - request: SessionRequestParams( - method: method, - params: { - "version": 0, - "message": { - "header": { - "numRequiredSignatures": 1, - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 1 - }, - "recentBlockhash": - "H32Ss1hxpP2ZJM4whREVNyUWRgzFLVA97UXJUjBrEsgx", - "accountKeys": [ - "EbdEmCpKGvEwfwV4ACmVYHFRkwvXdogJhMZeEekDFVVJ", - "4SzUq9NNYSYGp41ED5NgSDoCrEh9MoD7zSvmtkwseW8s", - "11111111111111111111111111111111" - ], - "instructions": [ - { - "programIdIndex": 2, - "accounts": [0, 1], - "data": "AgAAAEAAABAAAAAA" - } - ] + // Create a connection to the devnet cluster. + final cluster = solana.Cluster.https( + Uri.parse(chainData.rpc.first).authority, + ); + // final cluster = solana.Cluster.devnet; + final connection = solana.Connection(cluster); + + // Fetch the latest blockhash. + final blockhash = await connection.getLatestBlockhash(); + + // Create a System Program instruction to transfer 0.5 SOL from [address1] to [address2]. + final transactionv0 = solana.Transaction.legacy( + payer: solana.Pubkey.fromBase58(address), + recentBlockhash: blockhash.blockhash, + instructions: [ + solana.TransactionInstruction.fromJson({ + "programId": "11111111111111111111111111111111", + "data": [2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + "keys": [ + { + "isSigner": true, + "isWritable": true, + "pubkey": address, + }, + { + "isSigner": false, + "isWritable": true, + "pubkey": "8vCyX7oB6Pc3pbWMGYYZF5pbSnAdQ7Gyr32JqxqCy8ZR" } - }, - ), - ); - } + ] + }), + // SystemProgram.transfer( + // fromPubkey: solana.Pubkey.fromBase58(address), + // toPubkey: solana.Pubkey.fromBase58( + // '8vCyX7oB6Pc3pbWMGYYZF5pbSnAdQ7Gyr32JqxqCy8ZR', + // ), + // lamports: solana.solToLamports(0.5), + // ), + ], + ); + + const config = solana.TransactionSerializableConfig( + verifySignatures: false, + ); + final bytes = transactionv0.serialize(config).asUint8List(); + final encodedV0Trx = base64.encode(bytes); + return web3App.signEngine.request( topic: topic, chainId: chainData.chainId, request: SessionRequestParams( method: method, params: { - "feePayer": address, - "recentBlockhash": "H32Ss1hxpP2ZJM4whREVNyUWRgzFLVA97UXJUjBrEsgx", - "instructions": [ - { - "programId": "11111111111111111111111111111111", - "data": [2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - "keys": [ - { - "isSigner": true, - "isWritable": true, - "pubkey": "EbdEmCpKGvEwfwV4ACmVYHFRkwvXdogJhMZeEekDFVVJ" - }, - { - "isSigner": false, - "isWritable": true, - "pubkey": "4SzUq9NNYSYGp41ED5NgSDoCrEh9MoD7zSvmtkwseW8s" - } - ] - } - ] + 'transaction': encodedV0Trx, + 'pubkey': address, + 'feePayer': address, + ...transactionv0.message.toJson(), }, ), ); diff --git a/example/dapp/pubspec.yaml b/example/dapp/pubspec.yaml index 5c60bea..e1979bd 100644 --- a/example/dapp/pubspec.yaml +++ b/example/dapp/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: url_launcher: ^6.2.2 # intl: ^0.19.0 package_info_plus: ^7.0.0 + solana_web3: ^0.1.3 walletconnect_modal_flutter: ^2.1.20 dependency_overrides: diff --git a/example/wallet/lib/dependencies/chains/common.dart b/example/wallet/lib/dependencies/chains/common.dart index 4e5f475..6f664e5 100644 --- a/example/wallet/lib/dependencies/chains/common.dart +++ b/example/wallet/lib/dependencies/chains/common.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/bottom_sheet/i_bottom_sheet_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/deep_link_handler.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/i_web3wallet_service.dart'; @@ -13,8 +14,16 @@ class CommonMethods { final web3Wallet = GetIt.I().web3wallet; final session = web3Wallet.sessions.get(topic); final scheme = session?.peer.metadata.redirect?.native ?? ''; - if (result is String) { - DeepLinkHandler.goTo(scheme, modalTitle: 'Success'); + if (result is! JsonRpcError) { + if (scheme.isEmpty) { + DeepLinkHandler.goBackModal( + title: 'Success', + message: result.toString(), + success: true, + ); + } else { + DeepLinkHandler.goTo(scheme, modalTitle: 'Success'); + } } else { DeepLinkHandler.goTo( scheme, diff --git a/example/wallet/lib/dependencies/chains/solana_service.dart b/example/wallet/lib/dependencies/chains/solana_service.dart index a616bd2..1ba3204 100644 --- a/example/wallet/lib/dependencies/chains/solana_service.dart +++ b/example/wallet/lib/dependencies/chains/solana_service.dart @@ -11,7 +11,7 @@ import 'package:walletconnect_flutter_v2_wallet/dependencies/key_service/i_key_s import 'package:walletconnect_flutter_v2_wallet/models/chain_metadata.dart'; import 'package:solana/solana.dart' as solana; -import 'package:solana/encoder.dart' as encoder; +import 'package:solana/encoder.dart' as solana_encoder; // ignore: depend_on_referenced_packages import 'package:bs58/bs58.dart'; @@ -93,19 +93,11 @@ class SolanaService { Future solanaSignTransaction(String topic, dynamic parameters) async { debugPrint( '[SampleWallet] solanaSignTransaction: ${jsonEncode(parameters)}'); - const method = 'solana_signTransaction'; final pRequest = _web3Wallet.pendingRequests.getAll().last; var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); try { final params = parameters as Map; - if (params.containsKey('version')) { - return await solanaSignV0Transaction(topic, parameters); - } - - final feePayer = params['feePayer'].toString(); - final recentBlockHash = params['recentBlockhash'].toString(); - final instructionsList = params['instructions'] as List; final keys = GetIt.I().getKeysForChain( chainSupported.chainId, @@ -116,109 +108,44 @@ class SolanaService { privateKey: secKeyBytes, ); - if (keyPair.address != feePayer) { - throw Exception('Error'); - } - const encoder = JsonEncoder.withIndent(' '); final transaction = encoder.convert(params); if (await CommonMethods.requestApproval( transaction, - method: method, + method: 'solana_signTransaction', chainId: pRequest.chainId, address: keyPair.address, )) { // Sign the transaction. - final instructions = instructionsList.map((json) { - return (json as Map).toInstruction(); - }).toList(); - - final message = solana.Message(instructions: instructions); - final compiledMessage = message.compile( - recentBlockhash: recentBlockHash, - feePayer: solana.Ed25519HDPublicKey.fromBase58(feePayer), - ); - - final signature = await keyPair.sign(compiledMessage.toByteArray()); - - response = response.copyWith( - result: { - 'signature': signature.toBase58(), - }, - ); - } else { - response = response.copyWith( - error: const JsonRpcError(code: 5001, message: 'User rejected'), - ); - } - } catch (e) { - debugPrint('[SampleWallet] solanaSignTransaction error $e'); - response = response.copyWith( - error: JsonRpcError(code: 0, message: e.toString()), - ); - } - - await _web3Wallet.respondSessionRequest( - topic: topic, - response: response, - ); - - CommonMethods.goBackToDapp(topic, response.result ?? response.error); - } - - Future solanaSignV0Transaction(String topic, dynamic parameters) async { - debugPrint( - '[SampleWallet] solanaSignV0Transaction: ${jsonEncode(parameters)}'); - const method = 'solana_signTransaction'; - final pRequest = _web3Wallet.pendingRequests.getAll().last; - var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); - - try { - final params = parameters as Map; - final version = params['version'] as int; - if (version != 0) { - response = response.copyWith( - error: const JsonRpcError( - code: 0, - message: 'Transaction is not v0', - ), - ); - } else { - final message = params['message'] as Map; - // final header = message['header'] as Map; - final recentBlockHash = message['recentBlockhash'] as String; - final accountKeys = (message['accountKeys'] as List) - .map((key) => solana.Ed25519HDPublicKey.fromBase58(key)) - .toList(); - - final feePayer = accountKeys.first.toString(); - final instructionsList = message['instructions'] as List; - - final keys = GetIt.I().getKeysForChain( - chainSupported.chainId, - ); - final secKeyBytes = keys[0].privateKey.parse32Bytes(); + // if params contains `transaction` key we should parse that one and disregard the rest + if (params.containsKey('transaction')) { + debugPrint('[SampleWallet] sign transaction 1 ${jsonEncode(params)}'); + + final transaction = params['transaction'] as String; + final transactionBytes = base64.decode(transaction); + final signedTx = solana_encoder.SignedTx.fromBytes( + transactionBytes, + ); - final keyPair = await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( - privateKey: secKeyBytes, - ); + // Sign the transaction. + final signature = await keyPair.sign( + signedTx.compiledMessage.toByteArray(), + ); - if (keyPair.address != feePayer) { - throw Exception('Error'); - } + response = response.copyWith( + result: { + 'signature': signature.toBase58(), + }, + ); + } else { + // else we parse the other key/values, see https://docs.walletconnect.com/advanced/multichain/rpc-reference/solana-rpc#solana_signtransaction + debugPrint('[SampleWallet] sign transaction 2 ${jsonEncode(params)}'); + final feePayer = params['feePayer'].toString(); + final recentBlockHash = params['recentBlockhash'].toString(); + final instructionsList = params['instructions'] as List; - const encoder = JsonEncoder.withIndent(' '); - final transaction = encoder.convert(params); - if (await CommonMethods.requestApproval( - transaction, - method: method, - chainId: pRequest.chainId, - address: keyPair.address, - )) { - // Sign the transaction. final instructions = instructionsList.map((json) { - debugPrint('[SampleWallet] json $json'); - return (json as Map).toV0Instruction(accountKeys); + return (json as Map).toInstruction(); }).toList(); final message = solana.Message(instructions: instructions); @@ -227,21 +154,24 @@ class SolanaService { feePayer: solana.Ed25519HDPublicKey.fromBase58(feePayer), ); - final signature = await keyPair.sign(compiledMessage.toByteArray()); + // Sign the transaction. + final signature = await keyPair.sign( + compiledMessage.toByteArray(), + ); response = response.copyWith( result: { 'signature': signature.toBase58(), }, ); - } else { - response = response.copyWith( - error: const JsonRpcError(code: 5001, message: 'User rejected'), - ); } + } else { + response = response.copyWith( + error: const JsonRpcError(code: 5001, message: 'User rejected'), + ); } } catch (e, s) { - debugPrint('[SampleWallet] solanaSignV0Transaction error $e, $s'); + debugPrint('[SampleWallet] solanaSignTransaction error $e, $s'); response = response.copyWith( error: JsonRpcError(code: 0, message: e.toString()), ); @@ -269,22 +199,22 @@ extension on String { } extension on Map { - encoder.Instruction toInstruction() { + solana_encoder.Instruction toInstruction() { final programId = this['programId'] as String; final programKey = solana.Ed25519HDPublicKey(base58.decode(programId).toList()); final data = (this['data'] as List).map((e) => e as int).toList(); final data58 = base58.encode(Uint8List.fromList(data)); - final dataBytes = encoder.ByteArray.fromBase58(data58); + final dataBytes = solana_encoder.ByteArray.fromBase58(data58); final keys = this['keys'] as List; - return encoder.Instruction( + return solana_encoder.Instruction( programId: programKey, data: dataBytes, accounts: keys.map((k) { final kParams = (k as Map); - return encoder.AccountMeta( + return solana_encoder.AccountMeta( pubKey: solana.Ed25519HDPublicKey.fromBase58(kParams['pubkey']), isWriteable: kParams['isWritable'] as bool, isSigner: kParams['isSigner'] as bool, @@ -292,33 +222,4 @@ extension on Map { }).toList(), ); } - - encoder.Instruction toV0Instruction( - List accountKeys, - ) { - final programIdIndex = this['programIdIndex'] as int; - final programKey = accountKeys.elementAt(programIdIndex); - - final accounts = (this['accounts'] as List).map((index) { - // Assuming first account in each instruction is signer - final isSigner = index == 0; - // Assuming second account in each instruction is writable - final isWriteable = index == 1; - return encoder.AccountMeta( - pubKey: accountKeys[index], - isSigner: isSigner, - isWriteable: isWriteable, - ); - }).toList(); - - final data = base64Decode(this['data'].toString()); - final data58 = base58.encode(Uint8List.fromList(data)); - final dataBytes = encoder.ByteArray.fromBase58(data58); - - return encoder.Instruction( - programId: programKey, - data: dataBytes, - accounts: accounts, - ); - } } diff --git a/example/wallet/lib/dependencies/deep_link_handler.dart b/example/wallet/lib/dependencies/deep_link_handler.dart index 57adba7..08b3747 100644 --- a/example/wallet/lib/dependencies/deep_link_handler.dart +++ b/example/wallet/lib/dependencies/deep_link_handler.dart @@ -72,7 +72,7 @@ class DeepLinkHandler { closeAfter: success ? 3 : 0, widget: Container( color: Colors.white, - height: 210.0, + height: 250.0, width: double.infinity, padding: const EdgeInsets.all(20.0), child: Column( diff --git a/example/wallet/lib/models/chain_data.dart b/example/wallet/lib/models/chain_data.dart index f69380c..7e4aeec 100644 --- a/example/wallet/lib/models/chain_data.dart +++ b/example/wallet/lib/models/chain_data.dart @@ -98,25 +98,19 @@ class ChainData { static final List solanaChains = [ const ChainMetadata( type: ChainType.solana, - chainId: 'solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ', - name: 'Solana Mainnet 1', + chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', + name: 'Solana Mainnet', logo: '/chain-logos/solana.png', color: Color.fromARGB(255, 247, 0, 255), - rpc: [ - 'https://rpc.ankr.com/solana', - 'https://api.tatum.io/v3/blockchain/node/solana-mainnet', - ], + rpc: ['https://api.mainnet-beta.solana.com'], ), const ChainMetadata( type: ChainType.solana, - chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', - name: 'Solana Mainnet 2', + chainId: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1', + name: 'Solana Devnet', logo: '/chain-logos/solana.png', color: Color.fromARGB(255, 247, 0, 255), - rpc: [ - 'https://rpc.ankr.com/solana', - 'https://api.tatum.io/v3/blockchain/node/solana-mainnet', - ], + rpc: ['https://api.devnet.solana.com'], ), const ChainMetadata( type: ChainType.solana, @@ -125,9 +119,7 @@ class ChainData { logo: '/chain-logos/solana.png', color: Colors.black, isTestnet: true, - rpc: [ - 'https://api.testnet.solana.com', - ], + rpc: ['https://api.testnet.solana.com'], ), ]; From a877598ff813ae15b27d6bf2bd6b3c50353752da Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Thu, 29 Aug 2024 12:21:40 +0200 Subject: [PATCH 3/7] added second solana service to use solana_web3 package --- example/dapp/lib/utils/crypto/solana.dart | 2 +- .../dependencies/chains/solana_service.dart | 47 +++++++++---------- example/wallet/lib/main.dart | 7 ++- example/wallet/pubspec.yaml | 1 + 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/example/dapp/lib/utils/crypto/solana.dart b/example/dapp/lib/utils/crypto/solana.dart index 318813e..412a5e6 100644 --- a/example/dapp/lib/utils/crypto/solana.dart +++ b/example/dapp/lib/utils/crypto/solana.dart @@ -64,7 +64,7 @@ class Solana { final blockhash = await connection.getLatestBlockhash(); // Create a System Program instruction to transfer 0.5 SOL from [address1] to [address2]. - final transactionv0 = solana.Transaction.legacy( + final transactionv0 = solana.Transaction.v0( payer: solana.Pubkey.fromBase58(address), recentBlockhash: blockhash.blockhash, instructions: [ diff --git a/example/wallet/lib/dependencies/chains/solana_service.dart b/example/wallet/lib/dependencies/chains/solana_service.dart index 1ba3204..8e2cfd1 100644 --- a/example/wallet/lib/dependencies/chains/solana_service.dart +++ b/example/wallet/lib/dependencies/chains/solana_service.dart @@ -15,6 +15,9 @@ import 'package:solana/encoder.dart' as solana_encoder; // ignore: depend_on_referenced_packages import 'package:bs58/bs58.dart'; +/// +/// Uses solana: ^0.30.4 +/// class SolanaService { Map get solanaRequestHandlers => { 'solana_signMessage': solanaSignMessage, @@ -36,7 +39,6 @@ class SolanaService { Future solanaSignMessage(String topic, dynamic parameters) async { debugPrint('[SampleWallet] solanaSignMessage request: $parameters'); - const method = 'solana_signMessage'; final pRequest = _web3Wallet.pendingRequests.getAll().last; var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); @@ -44,21 +46,14 @@ class SolanaService { final params = parameters as Map; final message = params['message'].toString(); - final keys = GetIt.I().getKeysForChain( - chainSupported.chainId, - ); - final secKeyBytes = keys[0].privateKey.parse32Bytes(); - - final keyPair = await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( - privateKey: secKeyBytes, - ); + final keyPair = await _getKeyPair(); // it's being sent encoded from dapp final base58Decoded = base58.decode(message); final decodedMessage = utf8.decode(base58Decoded); if (await CommonMethods.requestApproval( decodedMessage, - method: method, + method: pRequest.method, chainId: pRequest.chainId, address: keyPair.address, )) { @@ -90,6 +85,18 @@ class SolanaService { CommonMethods.goBackToDapp(topic, response.result ?? response.error); } + Future _getKeyPair() async { + final keys = GetIt.I().getKeysForChain( + chainSupported.chainId, + ); + final secKeyBytes = keys[0].privateKey.parse32Bytes(); + + final keyPair = await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( + privateKey: secKeyBytes, + ); + return keyPair; + } + Future solanaSignTransaction(String topic, dynamic parameters) async { debugPrint( '[SampleWallet] solanaSignTransaction: ${jsonEncode(parameters)}'); @@ -98,29 +105,20 @@ class SolanaService { try { final params = parameters as Map; + final beautifiedTrx = const JsonEncoder.withIndent(' ').convert(params); - final keys = GetIt.I().getKeysForChain( - chainSupported.chainId, - ); - final secKeyBytes = keys[0].privateKey.parse32Bytes(); - - final keyPair = await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( - privateKey: secKeyBytes, - ); + final keyPair = await _getKeyPair(); - const encoder = JsonEncoder.withIndent(' '); - final transaction = encoder.convert(params); if (await CommonMethods.requestApproval( - transaction, - method: 'solana_signTransaction', + // Show Approval Modal + beautifiedTrx, + method: pRequest.method, chainId: pRequest.chainId, address: keyPair.address, )) { // Sign the transaction. // if params contains `transaction` key we should parse that one and disregard the rest if (params.containsKey('transaction')) { - debugPrint('[SampleWallet] sign transaction 1 ${jsonEncode(params)}'); - final transaction = params['transaction'] as String; final transactionBytes = base64.decode(transaction); final signedTx = solana_encoder.SignedTx.fromBytes( @@ -139,7 +137,6 @@ class SolanaService { ); } else { // else we parse the other key/values, see https://docs.walletconnect.com/advanced/multichain/rpc-reference/solana-rpc#solana_signtransaction - debugPrint('[SampleWallet] sign transaction 2 ${jsonEncode(params)}'); final feePayer = params['feePayer'].toString(); final recentBlockHash = params['recentBlockhash'].toString(); final instructionsList = params['instructions'] as List; diff --git a/example/wallet/lib/main.dart b/example/wallet/lib/main.dart index bd4acca..2a31af1 100644 --- a/example/wallet/lib/main.dart +++ b/example/wallet/lib/main.dart @@ -7,7 +7,9 @@ import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/cosmos_servi import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/evm_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/kadena_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/polkadot_service.dart'; +// ignore: unused_import import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/solana_service.dart'; +import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/solana_service_2.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/deep_link_handler.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/i_web3wallet_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/key_service/i_key_service.dart'; @@ -92,9 +94,10 @@ class _MyHomePageState extends State with GetItStateMixin { } // Support Solana Chains + // Change SolanaService2 to SolanaService to switch between solana_web3: ^0.1.3 to solana: ^0.30.4 for (final chainData in ChainData.solanaChains) { - GetIt.I.registerSingleton( - SolanaService(chainSupported: chainData), + GetIt.I.registerSingleton( + SolanaService2(chainSupported: chainData), instanceName: chainData.chainId, ); } diff --git a/example/wallet/pubspec.yaml b/example/wallet/pubspec.yaml index b40d507..fbf81f7 100644 --- a/example/wallet/pubspec.yaml +++ b/example/wallet/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: # CHECK WEB SUPPORT kadena_dart_sdk: ^2.3.2 solana: ^0.30.4 + solana_web3: ^0.1.3 polkadart_keyring: ^0.4.3 polkadart: ^0.4.6 # From ae59206fd7e385f76e24dd7346cb54fbabe64424 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Thu, 29 Aug 2024 12:22:09 +0200 Subject: [PATCH 4/7] added second solana service to use solana_web3 package --- .../dependencies/chains/solana_service_2.dart | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 example/wallet/lib/dependencies/chains/solana_service_2.dart diff --git a/example/wallet/lib/dependencies/chains/solana_service_2.dart b/example/wallet/lib/dependencies/chains/solana_service_2.dart new file mode 100644 index 0000000..30a5098 --- /dev/null +++ b/example/wallet/lib/dependencies/chains/solana_service_2.dart @@ -0,0 +1,217 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; + +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/common.dart'; +import 'package:walletconnect_flutter_v2_wallet/dependencies/i_web3wallet_service.dart'; +import 'package:walletconnect_flutter_v2_wallet/dependencies/key_service/i_key_service.dart'; +import 'package:walletconnect_flutter_v2_wallet/models/chain_metadata.dart'; + +import 'package:solana_web3/solana_web3.dart' as solana; + +// ignore: depend_on_referenced_packages +import 'package:bs58/bs58.dart'; +// ignore: implementation_imports +import 'package:solana_web3/src/crypto/nacl.dart' as nacl; + +/// +/// Uses solana_web3: ^0.1.3 +/// +class SolanaService2 { + Map get solanaRequestHandlers => { + 'solana_signMessage': solanaSignMessage, + 'solana_signTransaction': solanaSignTransaction, + }; + + final _web3Wallet = GetIt.I().web3wallet; + final ChainMetadata chainSupported; + + SolanaService2({required this.chainSupported}) { + for (var handler in solanaRequestHandlers.entries) { + _web3Wallet.registerRequestHandler( + chainId: chainSupported.chainId, + method: handler.key, + handler: handler.value, + ); + } + } + + Future solanaSignMessage(String topic, dynamic parameters) async { + debugPrint('[SampleWallet] solanaSignMessage request: $parameters'); + final pRequest = _web3Wallet.pendingRequests.getAll().last; + var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); + + try { + final params = parameters as Map; + final message = params['message'].toString(); + + final keyPair = await _getKeyPair(); + + // it's being sent encoded from dapp + final base58Decoded = base58.decode(message); + final decodedMessage = utf8.decode(base58Decoded); + if (await CommonMethods.requestApproval( + decodedMessage, + method: pRequest.method, + chainId: pRequest.chainId, + address: keyPair.pubkey.toBase58(), + )) { + final signature = await nacl.sign.detached( + base58Decoded, + keyPair.seckey, + ); + + response = response.copyWith( + result: { + 'signature': signature.toBase58(), + }, + ); + } else { + response = response.copyWith( + error: const JsonRpcError(code: 5001, message: 'User rejected'), + ); + } + // + } catch (e) { + debugPrint('[SampleWallet] polkadotSignMessage error $e'); + response = response.copyWith( + error: JsonRpcError(code: 0, message: e.toString()), + ); + } + + await _web3Wallet.respondSessionRequest( + topic: topic, + response: response, + ); + + CommonMethods.goBackToDapp(topic, response.result ?? response.error); + } + + Future _getKeyPair() async { + final keys = GetIt.I().getKeysForChain( + chainSupported.chainId, + ); + final secKeyBytes = keys[0].privateKey.parse32Bytes(); + return solana.Keypair.fromSeedSync(secKeyBytes); + } + + Future solanaSignTransaction(String topic, dynamic parameters) async { + debugPrint( + '[SampleWallet] solanaSignTransaction: ${jsonEncode(parameters)}'); + final pRequest = _web3Wallet.pendingRequests.getAll().last; + var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); + + try { + final params = parameters as Map; + final beautifiedTrx = const JsonEncoder.withIndent(' ').convert(params); + + final keyPair = await _getKeyPair(); + + if (await CommonMethods.requestApproval( + // Show Approval Modal + beautifiedTrx, + method: pRequest.method, + chainId: pRequest.chainId, + address: keyPair.pubkey.toBase58(), + )) { + // Sign the transaction. + // if params contains `transaction` key we should parse that one and disregard the rest, see https://docs.walletconnect.com/advanced/multichain/rpc-reference/solana-rpc#solana_signtransaction + if (params.containsKey('transaction')) { + final encodedTx = params['transaction'] as String; + final decodedTx = solana.Transaction.fromBase64(encodedTx); + + // Sign the transaction. + decodedTx.sign([keyPair]); + + response = response.copyWith( + result: { + 'signature': decodedTx.signatures.first.toBase58(), + }, + ); + } else { + // else we parse the other key/values, see https://docs.walletconnect.com/advanced/multichain/rpc-reference/solana-rpc#solana_signtransaction + final feePayer = params['feePayer'].toString(); + final recentBlockHash = params['recentBlockhash'].toString(); + final instructionsList = params['instructions'] as List; + + final instructions = instructionsList.map((json) { + return (json as Map).toInstruction(); + }).toList(); + + final decodedTx = solana.Transaction.v0( + payer: solana.Pubkey.fromBase58(feePayer), + instructions: instructions, + recentBlockhash: recentBlockHash, + ); + + // Sign the transaction. + decodedTx.sign([keyPair]); + + response = response.copyWith( + result: { + 'signature': decodedTx.signatures.first.toBase58(), + }, + ); + } + } else { + response = response.copyWith( + error: const JsonRpcError(code: 5001, message: 'User rejected'), + ); + } + } catch (e, s) { + debugPrint('[SampleWallet] solanaSignTransaction error $e, $s'); + response = response.copyWith( + error: JsonRpcError(code: 0, message: e.toString()), + ); + } + + await _web3Wallet.respondSessionRequest( + topic: topic, + response: response, + ); + + CommonMethods.goBackToDapp(topic, response.result ?? response.error); + } +} + +extension on Map { + solana.TransactionInstruction toInstruction() { + final programId = this['programId'] as String; + + final data = (this['data'] as String); + final dataBytes = base64.decode(data); + + final keys = this['keys'] as List; + return solana.TransactionInstruction( + programId: solana.Pubkey.fromBase58(programId), + data: dataBytes, + keys: keys.map((k) { + final kParams = (k as Map); + return solana.AccountMeta( + solana.Pubkey.fromBase58(kParams['pubkey']), + isSigner: kParams['isSigner'] as bool, + isWritable: kParams['isWritable'] as bool, + ); + }).toList(), + ); + } +} + +extension on String { + // SigningKey used by solana package requires a 32 bytes key + Uint8List parse32Bytes() { + try { + final List secBytes = split(',').map((e) => int.parse(e)).toList(); + return Uint8List.fromList(secBytes.sublist(0, 32)); + } catch (e) { + rethrow; + } + } +} + +extension on Uint8List { + String toBase58() => base58.encode(this); +} From 157ad6e14269dd53d474aafe7fc79e97fff6a5a8 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Fri, 6 Sep 2024 11:12:54 +0200 Subject: [PATCH 5/7] fix --- .../dependencies/chains/solana_service.dart | 7 +++---- .../dependencies/chains/solana_service_2.dart | 18 ++++++++++-------- example/wallet/lib/utils/dart_defines.dart | 4 +--- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/example/wallet/lib/dependencies/chains/solana_service.dart b/example/wallet/lib/dependencies/chains/solana_service.dart index 8e2cfd1..897086e 100644 --- a/example/wallet/lib/dependencies/chains/solana_service.dart +++ b/example/wallet/lib/dependencies/chains/solana_service.dart @@ -90,11 +90,9 @@ class SolanaService { chainSupported.chainId, ); final secKeyBytes = keys[0].privateKey.parse32Bytes(); - - final keyPair = await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( + return await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( privateKey: secKeyBytes, ); - return keyPair; } Future solanaSignTransaction(String topic, dynamic parameters) async { @@ -190,7 +188,8 @@ extension on String { final List secBytes = split(',').map((e) => int.parse(e)).toList(); return Uint8List.fromList(secBytes.sublist(0, 32)); } catch (e) { - rethrow; + final secKeyBytes = base58.decode(this); + return Uint8List.fromList(secKeyBytes.sublist(0, 32)); } } } diff --git a/example/wallet/lib/dependencies/chains/solana_service_2.dart b/example/wallet/lib/dependencies/chains/solana_service_2.dart index 30a5098..fcedcec 100644 --- a/example/wallet/lib/dependencies/chains/solana_service_2.dart +++ b/example/wallet/lib/dependencies/chains/solana_service_2.dart @@ -94,8 +94,14 @@ class SolanaService2 { final keys = GetIt.I().getKeysForChain( chainSupported.chainId, ); - final secKeyBytes = keys[0].privateKey.parse32Bytes(); - return solana.Keypair.fromSeedSync(secKeyBytes); + try { + final secKeyBytes = keys[0].privateKey.parse32Bytes(); + return solana.Keypair.fromSeedSync(secKeyBytes); + } catch (e) { + final secKeyBytes = base58.decode(keys[0].privateKey); + // final bytes = Uint8List.fromList(secKeyBytes.sublist(0, 32)); + return solana.Keypair.fromSeckeySync(secKeyBytes); + } } Future solanaSignTransaction(String topic, dynamic parameters) async { @@ -203,12 +209,8 @@ extension on Map { extension on String { // SigningKey used by solana package requires a 32 bytes key Uint8List parse32Bytes() { - try { - final List secBytes = split(',').map((e) => int.parse(e)).toList(); - return Uint8List.fromList(secBytes.sublist(0, 32)); - } catch (e) { - rethrow; - } + final List secBytes = split(',').map((e) => int.parse(e)).toList(); + return Uint8List.fromList(secBytes.sublist(0, 32)); } } diff --git a/example/wallet/lib/utils/dart_defines.dart b/example/wallet/lib/utils/dart_defines.dart index 6c5964e..aa08209 100644 --- a/example/wallet/lib/utils/dart_defines.dart +++ b/example/wallet/lib/utils/dart_defines.dart @@ -1,7 +1,5 @@ class DartDefines { - static const String projectId = String.fromEnvironment( - 'PROJECT_ID', - ); + static const projectId = String.fromEnvironment('PROJECT_ID'); // HARDCODED TEST KEYS // KADENA static const kadenaSecretKey = String.fromEnvironment( From f8efcfee49c6785819abac3f4dc63d3f708c0230 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Fri, 6 Sep 2024 12:24:52 +0200 Subject: [PATCH 6/7] update --- example/dapp/lib/main.dart | 13 +-- .../chain_services/solana_service.dart | 62 +++++++------- .../chain_services/solana_service_2.dart | 80 ++++++++++++------- .../lib/dependencies/deep_link_handler.dart | 39 --------- .../lib/dependencies/web3wallet_service.dart | 2 +- example/wallet/lib/main.dart | 9 +-- 6 files changed, 84 insertions(+), 121 deletions(-) diff --git a/example/dapp/lib/main.dart b/example/dapp/lib/main.dart index e9c5a57..4a18c32 100644 --- a/example/dapp/lib/main.dart +++ b/example/dapp/lib/main.dart @@ -129,17 +129,6 @@ class _MyHomePageState extends State { DeepLinkHandler.init(_web3App!); DeepLinkHandler.checkInitialLink(); - // Loop through all the chain data - for (final ChainMetadata chain in ChainData.allChains) { - // Loop through the events for that chain - for (final event in getChainEvents(chain.type)) { - _web3App!.registerEventHandler( - chainId: chain.chainId, - event: event, - ); - } - } - setState(() { _pageDatas = [ PageData( @@ -169,7 +158,7 @@ class _MyHomePageState extends State { } Future _registerEventHandlers() async { - if (!_web3App!.core.connectivity.isOnline) { + if (!_web3App!.core.connectivity.isOnline.value) { await Future.delayed(const Duration(milliseconds: 500)); _registerEventHandlers(); return; diff --git a/example/wallet/lib/dependencies/chain_services/solana_service.dart b/example/wallet/lib/dependencies/chain_services/solana_service.dart index 24868cd..d14a976 100644 --- a/example/wallet/lib/dependencies/chain_services/solana_service.dart +++ b/example/wallet/lib/dependencies/chain_services/solana_service.dart @@ -24,12 +24,12 @@ class SolanaService { 'solana_signTransaction': solanaSignTransaction, }; - final _web3Wallet = GetIt.I().web3wallet; + final _web3wallet = GetIt.I().web3wallet; final ChainMetadata chainSupported; SolanaService({required this.chainSupported}) { for (var handler in solanaRequestHandlers.entries) { - _web3Wallet.registerRequestHandler( + _web3wallet.registerRequestHandler( chainId: chainSupported.chainId, method: handler.key, handler: handler.value, @@ -39,7 +39,7 @@ class SolanaService { Future solanaSignMessage(String topic, dynamic parameters) async { debugPrint('[SampleWallet] solanaSignMessage request: $parameters'); - final pRequest = _web3Wallet.pendingRequests.getAll().last; + final pRequest = _web3wallet.pendingRequests.getAll().last; var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); try { @@ -51,14 +51,11 @@ class SolanaService { // it's being sent encoded from dapp final base58Decoded = base58.decode(message); final decodedMessage = utf8.decode(base58Decoded); - if (await CommonMethods.requestApproval( + if (await MethodsUtils.requestApproval( decodedMessage, method: pRequest.method, chainId: pRequest.chainId, address: keyPair.address, - if (await MethodsUtils.requestApproval( - decodedMessage, - title: method, transportType: pRequest.transportType.name, )) { final signature = await keyPair.sign(base58Decoded.toList()); @@ -81,23 +78,18 @@ class SolanaService { ); } - _handleResponseForTopic(topic, response); - } - - Future _getKeyPair() async { - final keys = GetIt.I().getKeysForChain( - chainSupported.chainId, - ); - final secKeyBytes = keys[0].privateKey.parse32Bytes(); - return await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( - privateKey: secKeyBytes, + await _web3wallet.respondSessionRequest( + topic: topic, + response: response, ); + + _handleResponseForTopic(topic, response); } Future solanaSignTransaction(String topic, dynamic parameters) async { debugPrint( '[SampleWallet] solanaSignTransaction: ${jsonEncode(parameters)}'); - final pRequest = _web3Wallet.pendingRequests.getAll().last; + final pRequest = _web3wallet.pendingRequests.getAll().last; var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); try { @@ -106,25 +98,12 @@ class SolanaService { final keyPair = await _getKeyPair(); - if (await CommonMethods.requestApproval( + if (await MethodsUtils.requestApproval( // Show Approval Modal beautifiedTrx, method: pRequest.method, chainId: pRequest.chainId, address: keyPair.address, - final keyPair = await Ed25519HDKeyPair.fromPrivateKeyBytes( - privateKey: secKeyBytes, - ); - - if (keyPair.address != feePayer) { - throw Exception('Error'); - } - - const encoder = JsonEncoder.withIndent(' '); - final transaction = encoder.convert(params); - if (await MethodsUtils.requestApproval( - transaction, - title: method, transportType: pRequest.transportType.name, )) { // Sign the transaction. @@ -185,14 +164,29 @@ class SolanaService { ); } + await _web3wallet.respondSessionRequest( + topic: topic, + response: response, + ); + _handleResponseForTopic(topic, response); } + Future _getKeyPair() async { + final keys = GetIt.I().getKeysForChain( + chainSupported.chainId, + ); + final secKeyBytes = keys[0].privateKey.parse32Bytes(); + return await solana.Ed25519HDKeyPair.fromPrivateKeyBytes( + privateKey: secKeyBytes, + ); + } + void _handleResponseForTopic(String topic, JsonRpcResponse response) async { - final session = _web3Wallet.sessions.get(topic); + final session = _web3wallet.sessions.get(topic); try { - await _web3Wallet.respondSessionRequest( + await _web3wallet.respondSessionRequest( topic: topic, response: response, ); diff --git a/example/wallet/lib/dependencies/chain_services/solana_service_2.dart b/example/wallet/lib/dependencies/chain_services/solana_service_2.dart index fcedcec..065c64d 100644 --- a/example/wallet/lib/dependencies/chain_services/solana_service_2.dart +++ b/example/wallet/lib/dependencies/chain_services/solana_service_2.dart @@ -5,17 +5,17 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; -import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/common.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/i_web3wallet_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/key_service/i_key_service.dart'; import 'package:walletconnect_flutter_v2_wallet/models/chain_metadata.dart'; import 'package:solana_web3/solana_web3.dart' as solana; - -// ignore: depend_on_referenced_packages -import 'package:bs58/bs58.dart'; // ignore: implementation_imports import 'package:solana_web3/src/crypto/nacl.dart' as nacl; +// ignore: depend_on_referenced_packages +import 'package:bs58/bs58.dart'; + +import 'package:walletconnect_flutter_v2_wallet/utils/methods_utils.dart'; /// /// Uses solana_web3: ^0.1.3 @@ -26,12 +26,12 @@ class SolanaService2 { 'solana_signTransaction': solanaSignTransaction, }; - final _web3Wallet = GetIt.I().web3wallet; + final _web3wallet = GetIt.I().web3wallet; final ChainMetadata chainSupported; SolanaService2({required this.chainSupported}) { for (var handler in solanaRequestHandlers.entries) { - _web3Wallet.registerRequestHandler( + _web3wallet.registerRequestHandler( chainId: chainSupported.chainId, method: handler.key, handler: handler.value, @@ -41,7 +41,7 @@ class SolanaService2 { Future solanaSignMessage(String topic, dynamic parameters) async { debugPrint('[SampleWallet] solanaSignMessage request: $parameters'); - final pRequest = _web3Wallet.pendingRequests.getAll().last; + final pRequest = _web3wallet.pendingRequests.getAll().last; var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); try { @@ -53,11 +53,12 @@ class SolanaService2 { // it's being sent encoded from dapp final base58Decoded = base58.decode(message); final decodedMessage = utf8.decode(base58Decoded); - if (await CommonMethods.requestApproval( + if (await MethodsUtils.requestApproval( decodedMessage, method: pRequest.method, chainId: pRequest.chainId, address: keyPair.pubkey.toBase58(), + transportType: pRequest.transportType.name, )) { final signature = await nacl.sign.detached( base58Decoded, @@ -82,32 +83,18 @@ class SolanaService2 { ); } - await _web3Wallet.respondSessionRequest( + await _web3wallet.respondSessionRequest( topic: topic, response: response, ); - CommonMethods.goBackToDapp(topic, response.result ?? response.error); - } - - Future _getKeyPair() async { - final keys = GetIt.I().getKeysForChain( - chainSupported.chainId, - ); - try { - final secKeyBytes = keys[0].privateKey.parse32Bytes(); - return solana.Keypair.fromSeedSync(secKeyBytes); - } catch (e) { - final secKeyBytes = base58.decode(keys[0].privateKey); - // final bytes = Uint8List.fromList(secKeyBytes.sublist(0, 32)); - return solana.Keypair.fromSeckeySync(secKeyBytes); - } + _handleResponseForTopic(topic, response); } Future solanaSignTransaction(String topic, dynamic parameters) async { debugPrint( '[SampleWallet] solanaSignTransaction: ${jsonEncode(parameters)}'); - final pRequest = _web3Wallet.pendingRequests.getAll().last; + final pRequest = _web3wallet.pendingRequests.getAll().last; var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); try { @@ -116,12 +103,13 @@ class SolanaService2 { final keyPair = await _getKeyPair(); - if (await CommonMethods.requestApproval( + if (await MethodsUtils.requestApproval( // Show Approval Modal beautifiedTrx, method: pRequest.method, chainId: pRequest.chainId, address: keyPair.pubkey.toBase58(), + transportType: pRequest.transportType.name, )) { // Sign the transaction. // if params contains `transaction` key we should parse that one and disregard the rest, see https://docs.walletconnect.com/advanced/multichain/rpc-reference/solana-rpc#solana_signtransaction @@ -174,12 +162,48 @@ class SolanaService2 { ); } - await _web3Wallet.respondSessionRequest( + await _web3wallet.respondSessionRequest( topic: topic, response: response, ); - CommonMethods.goBackToDapp(topic, response.result ?? response.error); + _handleResponseForTopic(topic, response); + } + + Future _getKeyPair() async { + final keys = GetIt.I().getKeysForChain( + chainSupported.chainId, + ); + try { + final secKeyBytes = keys[0].privateKey.parse32Bytes(); + return solana.Keypair.fromSeedSync(secKeyBytes); + } catch (e) { + final secKeyBytes = base58.decode(keys[0].privateKey); + // final bytes = Uint8List.fromList(secKeyBytes.sublist(0, 32)); + return solana.Keypair.fromSeckeySync(secKeyBytes); + } + } + + void _handleResponseForTopic(String topic, JsonRpcResponse response) async { + final session = _web3wallet.sessions.get(topic); + + try { + await _web3wallet.respondSessionRequest( + topic: topic, + response: response, + ); + MethodsUtils.handleRedirect( + topic, + session!.peer.metadata.redirect, + response.error?.message, + ); + } on WalletConnectError catch (error) { + MethodsUtils.handleRedirect( + topic, + session!.peer.metadata.redirect, + error.message, + ); + } } } diff --git a/example/wallet/lib/dependencies/deep_link_handler.dart b/example/wallet/lib/dependencies/deep_link_handler.dart index e3e965f..b736f97 100644 --- a/example/wallet/lib/dependencies/deep_link_handler.dart +++ b/example/wallet/lib/dependencies/deep_link_handler.dart @@ -39,45 +39,6 @@ class DeepLinkHandler { Uri.parse(_web3wallet.metadata.redirect?.universal ?? ''); static String get host => universalUri.host; - static void goBackModal({ - String? title, - String? message, - bool success = true, - }) async { - waiting.value = false; - await GetIt.I().queueBottomSheet( - closeAfter: success ? 3 : 0, - widget: Container( - color: Colors.white, - height: 250.0, - width: double.infinity, - padding: const EdgeInsets.all(20.0), - child: Column( - children: [ - Icon( - success ? Icons.check_circle_sharp : Icons.error_outline_sharp, - color: success ? Colors.green[100] : Colors.red[100], - size: 80.0, - ), - Text( - title ?? 'Connected', - style: StyleConstants.subtitleText.copyWith( - color: Colors.black, - fontSize: 18.0, - ), - ), - Text(message ?? 'You can go back to your dApp now'), - ], - ), - ), - ); - } - - static void _onLink(Object? event) { - final decodedUri = Uri.parse(Uri.decodeFull(event.toString())); - if (decodedUri.isScheme('wc')) { - waiting.value = true; - _linksController.sink.add(decodedUri.toString()); static void _onLink(Object? event) async { final ev = WalletConnectUtils.getSearchParamFromURL('$event', 'wc_ev'); if (ev.isNotEmpty) { diff --git a/example/wallet/lib/dependencies/web3wallet_service.dart b/example/wallet/lib/dependencies/web3wallet_service.dart index 374ff9b..3943a7e 100644 --- a/example/wallet/lib/dependencies/web3wallet_service.dart +++ b/example/wallet/lib/dependencies/web3wallet_service.dart @@ -123,7 +123,7 @@ class Web3WalletService extends IWeb3WalletService { } Future _emitEvent() async { - if (!_web3Wallet!.core.connectivity.isOnline) { + if (!_web3Wallet!.core.connectivity.isOnline.value) { await Future.delayed(const Duration(milliseconds: 500)); _emitEvent(); return; diff --git a/example/wallet/lib/main.dart b/example/wallet/lib/main.dart index e6bd453..6a530f0 100644 --- a/example/wallet/lib/main.dart +++ b/example/wallet/lib/main.dart @@ -3,18 +3,13 @@ import 'package:get_it_mixin/get_it_mixin.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/bottom_sheet/bottom_sheet_listener.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/bottom_sheet/bottom_sheet_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/bottom_sheet/i_bottom_sheet_service.dart'; -import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/cosmos_service.dart'; -import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/evm_service.dart'; -import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/kadena_service.dart'; -import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/polkadot_service.dart'; // ignore: unused_import -import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/solana_service.dart'; -import 'package:walletconnect_flutter_v2_wallet/dependencies/chains/solana_service_2.dart'; +import 'package:walletconnect_flutter_v2_wallet/dependencies/chain_services/solana_service.dart'; +import 'package:walletconnect_flutter_v2_wallet/dependencies/chain_services/solana_service_2.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/chain_services/cosmos_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/chain_services/evm_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/chain_services/kadena_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/chain_services/polkadot_service.dart'; -import 'package:walletconnect_flutter_v2_wallet/dependencies/chain_services/solana_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/deep_link_handler.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/i_web3wallet_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/key_service/i_key_service.dart'; From a8477837fef8bb60c3c53c58023768ff78fdb1c3 Mon Sep 17 00:00:00 2001 From: Alfreedom <00tango.bromine@icloud.com> Date: Mon, 9 Sep 2024 09:52:34 +0200 Subject: [PATCH 7/7] added bitcoin attempt --- example/dapp/lib/models/chain_metadata.dart | 1 + example/dapp/lib/pages/connect_page.dart | 11 + example/dapp/lib/utils/crypto/bitcoin.dart | 86 ++++++++ example/dapp/lib/utils/crypto/chain_data.dart | 24 ++ example/dapp/lib/utils/crypto/helpers.dart | 3 + example/dapp/lib/widgets/session_widget.dart | 13 +- example/dapp/pubspec.yaml | 3 + .../chain_services/bitcoin_service.dart | 206 ++++++++++++++++++ .../chain_services/polkadot_service.dart | 2 +- .../chain_services/solana_service_2.dart | 10 - .../dependencies/key_service/key_service.dart | 62 +++++- example/wallet/lib/main.dart | 9 + example/wallet/lib/models/chain_data.dart | 23 ++ example/wallet/lib/models/chain_metadata.dart | 1 + example/wallet/lib/pages/settings_page.dart | 51 +++++ example/wallet/lib/utils/dart_defines.dart | 8 +- example/wallet/lib/utils/eth_utils.dart | 2 +- example/wallet/lib/utils/methods_utils.dart | 3 +- example/wallet/pubspec.yaml | 5 +- example/wallet/test/widget_test.dart | 2 +- 20 files changed, 502 insertions(+), 23 deletions(-) create mode 100644 example/dapp/lib/utils/crypto/bitcoin.dart create mode 100644 example/wallet/lib/dependencies/chain_services/bitcoin_service.dart diff --git a/example/dapp/lib/models/chain_metadata.dart b/example/dapp/lib/models/chain_metadata.dart index 834a0ba..d5d0f99 100644 --- a/example/dapp/lib/models/chain_metadata.dart +++ b/example/dapp/lib/models/chain_metadata.dart @@ -7,6 +7,7 @@ enum ChainType { kadena, cosmos, polkadot, + bip122, } class ChainMetadata { diff --git a/example/dapp/lib/pages/connect_page.dart b/example/dapp/lib/pages/connect_page.dart index 17ca242..832d95e 100644 --- a/example/dapp/lib/pages/connect_page.dart +++ b/example/dapp/lib/pages/connect_page.dart @@ -11,6 +11,7 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:walletconnect_flutter_v2_dapp/models/chain_metadata.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/constants.dart'; +import 'package:walletconnect_flutter_v2_dapp/utils/crypto/bitcoin.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/crypto/chain_data.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/crypto/eip155.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/crypto/polkadot.dart'; @@ -91,6 +92,16 @@ class ConnectPageState extends State { ); } + final btcChains = + _selectedChains.where((e) => e.type == ChainType.bip122).toList(); + if (btcChains.isNotEmpty) { + optionalNamespaces['bip122'] = RequiredNamespace( + chains: btcChains.map((c) => c.chainId).toList(), + methods: Bitcoin.methods.values.toList(), + events: Bitcoin.events.values.toList(), + ); + } + final solanaChains = _selectedChains.where((e) => e.type == ChainType.solana).toList(); if (solanaChains.isNotEmpty) { diff --git a/example/dapp/lib/utils/crypto/bitcoin.dart b/example/dapp/lib/utils/crypto/bitcoin.dart new file mode 100644 index 0000000..8241e6e --- /dev/null +++ b/example/dapp/lib/utils/crypto/bitcoin.dart @@ -0,0 +1,86 @@ +import 'package:walletconnect_flutter_v2_dapp/models/chain_metadata.dart'; +import 'package:walletconnect_flutter_v2_dapp/imports.dart'; + +enum BitcoinMethods { + bitcoinSignTransaction, + bitcoinSignMessage, +} + +enum BitcoinEvents { + none, +} + +class Bitcoin { + static final Map methods = { + BitcoinMethods.bitcoinSignTransaction: 'btc_sendTransaction', + BitcoinMethods.bitcoinSignMessage: 'btc_signMessage' + }; + + static final Map events = {}; + + static Future callMethod({ + required Web3App web3App, + required String topic, + required String method, + required ChainMetadata chainData, + required String address, + bool isV0 = false, + }) async { + switch (method) { + case 'btc_signMessage': + const message = "This is a message to be signed for BIP122"; + final result = await web3App.request( + topic: topic, + chainId: chainData.chainId, + request: SessionRequestParams( + method: method, + params: [message, address], + ), + ); + // final checkSegwitAlways = (result.segwitType == "p2wpkh") || + // (result.segwitType == 'p2sh(p2wpkh)'); + return { + 'method': method, + 'address': address, + // 'valid': verifyBitcoinMessage( + // message, + // result.signature, + // address, + // // undefined, + // // checkSegwitAlways + // ), + 'result': '' + 'signature: ${result["signature"]}\n' + 'segwitType: ${result["segwitType"]}', + }; + case 'btc_sendTransaction': + // final utxos = await apiGetAddressUtxos(address, chainId); + // final availableBalance = getAvailableBalanceFromUtxos(utxos); // in satoshis + return web3App.request( + topic: topic, + chainId: chainData.chainId, + request: SessionRequestParams( + method: method, + params: { + 'address': address, + 'value': 0.0000001, // availableBalance, + 'transactionType': 'p2wpkh', + }, + ), + ); + default: + throw 'Method unimplemented'; + } + } +} + +// Future apiGetAddressUtxos(String address, String chainId) { +// return await (await fetch(`https://mempool.space/signet/api/address/${address}/utxo`)).json(); +// } + +// int getAvailableBalanceFromUtxos(List utxos) { +// if (!utxos || !utxos.length) { +// return 0; +// } +// return utxos.reduce((acc, { value }) => acc + value, 0); +// } diff --git a/example/dapp/lib/utils/crypto/chain_data.dart b/example/dapp/lib/utils/crypto/chain_data.dart index 489f549..d0213bf 100644 --- a/example/dapp/lib/utils/crypto/chain_data.dart +++ b/example/dapp/lib/utils/crypto/chain_data.dart @@ -189,10 +189,34 @@ class ChainData { ), ]; + static final List bitcoinChains = [ + const ChainMetadata( + type: ChainType.bip122, + chainId: + 'bip122:000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', + name: 'Bitcoin Mainnet', + logo: '/chain-logos/bitcoin.png', + color: Color.fromARGB(255, 255, 161, 9), + rpc: ['https://bitcoin.drpc.org/'], + ), + const ChainMetadata( + type: ChainType.bip122, + chainId: + 'bip122:000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943', + name: 'Bitcoin Signet', + logo: '/chain-logos/bitcoin.png', + color: Color.fromARGB(255, 255, 161, 9), + rpc: ['https://signet.bitcoinrpc.org'], + // https://bitcoin-testnet.drpc.org + isTestnet: true, + ), + ]; + static final List allChains = [ ...eip155Chains, ...solanaChains, ...polkadotChains, + ...bitcoinChains, // ...kadenaChains, // ...cosmosChains, ]; diff --git a/example/dapp/lib/utils/crypto/helpers.dart b/example/dapp/lib/utils/crypto/helpers.dart index a37dfbb..aaa5187 100644 --- a/example/dapp/lib/utils/crypto/helpers.dart +++ b/example/dapp/lib/utils/crypto/helpers.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:walletconnect_flutter_v2_dapp/models/chain_metadata.dart'; +import 'package:walletconnect_flutter_v2_dapp/utils/crypto/bitcoin.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/crypto/chain_data.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/crypto/eip155.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/crypto/polkadot.dart'; @@ -36,6 +37,8 @@ List getChainMethods(ChainType value) { return Solana.methods.values.toList(); case ChainType.polkadot: return Polkadot.methods.values.toList(); + case ChainType.bip122: + return Bitcoin.methods.values.toList(); default: return []; } diff --git a/example/dapp/lib/widgets/session_widget.dart b/example/dapp/lib/widgets/session_widget.dart index 2bb27ee..923e4cb 100644 --- a/example/dapp/lib/widgets/session_widget.dart +++ b/example/dapp/lib/widgets/session_widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:walletconnect_flutter_v2_dapp/models/chain_metadata.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/constants.dart'; +import 'package:walletconnect_flutter_v2_dapp/utils/crypto/bitcoin.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/crypto/eip155.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/crypto/helpers.dart'; import 'package:walletconnect_flutter_v2_dapp/utils/crypto/polkadot.dart'; @@ -170,7 +171,8 @@ class SessionWidgetState extends State { ) { final List buttons = []; // Add Methods - for (final String method in getChainMethods(chainMetadata.type)) { + final chainMethods = getChainMethods(chainMetadata.type); + for (final method in chainMethods) { final namespaces = widget.session.namespaces[chainMetadata.type.name]; final supported = namespaces?.methods.contains(method) ?? false; buttons.add( @@ -233,6 +235,14 @@ class SessionWidgetState extends State { chainData: chainMetadata, address: address, ); + case ChainType.bip122: + return Bitcoin.callMethod( + web3App: widget.web3App, + topic: widget.session.topic, + method: method, + chainData: chainMetadata, + address: address, + ); case ChainType.polkadot: return Polkadot.callMethod( web3App: widget.web3App, @@ -250,6 +260,7 @@ class SessionWidgetState extends State { address: address, isV0: true, ); + // case ChainType.kadena: // return Kadena.callMethod( // web3App: widget.web3App, diff --git a/example/dapp/pubspec.yaml b/example/dapp/pubspec.yaml index 54a3385..5120912 100644 --- a/example/dapp/pubspec.yaml +++ b/example/dapp/pubspec.yaml @@ -9,6 +9,9 @@ environment: sdk: ">=2.18.6 <3.0.0" dependencies: + bitcoin_base: ^4.7.0 + pointycastle: ^3.5.2 + hex: ^0.2.0 flutter: sdk: flutter diff --git a/example/wallet/lib/dependencies/chain_services/bitcoin_service.dart b/example/wallet/lib/dependencies/chain_services/bitcoin_service.dart new file mode 100644 index 0000000..ec30817 --- /dev/null +++ b/example/wallet/lib/dependencies/chain_services/bitcoin_service.dart @@ -0,0 +1,206 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; + +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:walletconnect_flutter_v2_wallet/dependencies/i_web3wallet_service.dart'; +import 'package:walletconnect_flutter_v2_wallet/dependencies/key_service/i_key_service.dart'; +import 'package:walletconnect_flutter_v2_wallet/models/chain_metadata.dart'; +import 'package:walletconnect_flutter_v2_wallet/utils/methods_utils.dart'; + +import 'package:bitcoin_base/bitcoin_base.dart'; +// import 'package:bitcoin_message_signer/bitcoin_message_signer.dart'; + +class BitcoinService { + Map get bitcoinRequestHandlers => { + 'btc_signMessage': bitcoinSignMessage, + 'btc_sendTransaction': bitcoinSendTransaction, + }; + + final _web3wallet = GetIt.I().web3wallet; + final ChainMetadata chainSupported; + + BitcoinService({required this.chainSupported}) { + for (var handler in bitcoinRequestHandlers.entries) { + _web3wallet.registerRequestHandler( + chainId: chainSupported.chainId, + method: handler.key, + handler: handler.value, + ); + } + } + + Future bitcoinSignMessage(String topic, dynamic parameters) async { + debugPrint('[SampleWallet] bitcoinSignMessage request: $parameters'); + final pRequest = _web3wallet.pendingRequests.getAll().last; + var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); + + try { + final params = parameters as List; + final message = params.first.toString(); + + final keys = GetIt.I().getKeysForChain( + chainSupported.chainId, + ); + + final privateKey = ECPrivate.fromWif( + keys.first.privateKey, + netVersion: BitcoinNetwork.mainnet.wifNetVer, + ); + + if (await MethodsUtils.requestApproval( + message, + method: pRequest.method, + chainId: pRequest.chainId, + address: keys.first.address, + transportType: pRequest.transportType.name, + )) { + // final messageSigner = BitcoinMessageSigner( + // privateKey: Uint8List.fromList(privateKey.toBytes()), + // scriptType: P2WPKH(), + // ); + + final messageBytes = utf8.encode(message); + + final signature = privateKey.signMessage( + messageBytes, + messagePrefix: '\x18Bitcoin Signed Message:\n', + ); + debugPrint('[$runtimeType] signature: $signature'); + + // final signature2 = messageSigner.signMessage( + // message: message, + // messagePrefix: '\x18Bitcoin Signed Message:\n', + // ); + + // debugPrint('[$runtimeType] signature2: $signature2'); + + final publicKey = privateKey.getPublic(); + + final isValid = publicKey.verify( + messageBytes, + base64.decode(signature).sublist(0, 64), + messagePrefix: '\x18Bitcoin Signed Message:\n', + ); + + debugPrint('[$runtimeType] signature: $signature, valid: $isValid'); + + response = response.copyWith( + result: { + 'signature': '0x$signature', + // 'segwitType': 'p2wsh', + }, + ); + } else { + response = response.copyWith( + error: const JsonRpcError(code: 5001, message: 'User rejected'), + ); + } + // + } catch (e) { + debugPrint('[SampleWallet] bitcoinSignMessage error $e'); + response = response.copyWith( + error: JsonRpcError(code: 0, message: e.toString()), + ); + } + + _handleResponseForTopic(topic, response); + } + + Future bitcoinSendTransaction(String topic, dynamic parameters) async { + debugPrint( + '[SampleWallet] bitcoinSendTransaction: ${jsonEncode(parameters)}'); + final pRequest = _web3wallet.pendingRequests.getAll().last; + var response = JsonRpcResponse(id: pRequest.id, jsonrpc: '2.0'); + + try { + final params = parameters as Map; + final beautifiedTrx = const JsonEncoder.withIndent(' ').convert(params); + + final keys = GetIt.I().getKeysForChain( + chainSupported.chainId, + ); + + final privateKey = ECPrivate.fromWif( + keys.first.privateKey, + netVersion: BitcoinNetwork.mainnet.wifNetVer, + ); + + if (await MethodsUtils.requestApproval( + // Show Approval Modal + beautifiedTrx, + method: pRequest.method, + chainId: pRequest.chainId, + address: keys.first.address, + transportType: pRequest.transportType.name, + )) { + // Sign the transaction. + // else we parse the other key/values, see https://docs.walletconnect.com/advanced/multichain/rpc-reference/solana-rpc#solana_signtransaction + final address = params['address'].toString(); + final value = params['value'] as num; + final transactionType = params['transactionType'].toString(); + + final List utxos = + []; //await this.getUtXOs(this.address); + + final List outPuts = []; + + BitcoinTransactionBuilder( + outPuts: outPuts, + fee: BigInt.zero, + network: BitcoinNetwork.mainnet, + utxos: utxos, + ); + + // response = response.copyWith( + // result: { + // 'signature': signature.toBase58(), + // }, + // ); + } else { + response = response.copyWith( + error: const JsonRpcError(code: 5001, message: 'User rejected'), + ); + } + } catch (e, s) { + debugPrint('[SampleWallet] bitcoinSendTransaction error $e, $s'); + response = response.copyWith( + error: JsonRpcError(code: 0, message: e.toString()), + ); + } + + _handleResponseForTopic(topic, response); + } + + void _handleResponseForTopic(String topic, JsonRpcResponse response) async { + final session = _web3wallet.sessions.get(topic); + + try { + await _web3wallet.respondSessionRequest( + topic: topic, + response: response, + ); + MethodsUtils.handleRedirect( + topic, + session!.peer.metadata.redirect, + response.error?.message, + ); + } on WalletConnectError catch (error) { + MethodsUtils.handleRedirect( + topic, + session!.peer.metadata.redirect, + error.message, + ); + } + } +} + +// String _padEncodeIfNeeded(String encoded) { +// final padding = encoded.length % 4; +// if (padding > 0) { +// encoded += '=' * (4 - padding); +// } +// return encoded; +// } diff --git a/example/wallet/lib/dependencies/chain_services/polkadot_service.dart b/example/wallet/lib/dependencies/chain_services/polkadot_service.dart index acc2b42..6ec9b5a 100644 --- a/example/wallet/lib/dependencies/chain_services/polkadot_service.dart +++ b/example/wallet/lib/dependencies/chain_services/polkadot_service.dart @@ -1,9 +1,9 @@ import 'dart:convert'; -import 'package:convert/convert.dart'; import 'package:flutter/foundation.dart'; import 'package:get_it/get_it.dart'; import 'package:polkadart/scale_codec.dart'; +import 'package:solana_web3/solana_web3.dart' show hex; import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/i_web3wallet_service.dart'; diff --git a/example/wallet/lib/dependencies/chain_services/solana_service_2.dart b/example/wallet/lib/dependencies/chain_services/solana_service_2.dart index 065c64d..2da351d 100644 --- a/example/wallet/lib/dependencies/chain_services/solana_service_2.dart +++ b/example/wallet/lib/dependencies/chain_services/solana_service_2.dart @@ -83,11 +83,6 @@ class SolanaService2 { ); } - await _web3wallet.respondSessionRequest( - topic: topic, - response: response, - ); - _handleResponseForTopic(topic, response); } @@ -162,11 +157,6 @@ class SolanaService2 { ); } - await _web3wallet.respondSessionRequest( - topic: topic, - response: response, - ); - _handleResponseForTopic(topic, response); } diff --git a/example/wallet/lib/dependencies/key_service/key_service.dart b/example/wallet/lib/dependencies/key_service/key_service.dart index 69c4ef6..fd8bd6d 100644 --- a/example/wallet/lib/dependencies/key_service/key_service.dart +++ b/example/wallet/lib/dependencies/key_service/key_service.dart @@ -1,6 +1,7 @@ import 'dart:convert'; -import 'package:convert/convert.dart'; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:solana_web3/solana_web3.dart' show hex; import 'package:flutter/foundation.dart'; import 'package:walletconnect_flutter_v2/apis/core/crypto/crypto_models.dart'; import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; @@ -83,8 +84,7 @@ class KeyService extends IKeyService { @override Future loadDefaultWallet() async { - const mnemonic = - 'spoil video deputy round immense setup wasp secret maze slight bag what'; + const mnemonic = DartDefines.ethereumSecretKey; await restoreWalletFromSeed(mnemonic: mnemonic); } @@ -136,10 +136,10 @@ class KeyService extends IKeyService { } final seed = bip39.mnemonicToSeed(mnemonic); - final root = bip32.BIP32.fromSeed(seed); + final node = bip32.BIP32.fromSeed(seed); + final child = node.derivePath("m/44'/60'/0'/0/$index"); - final child = root.derivePath("m/44'/60'/0'/0/$index"); - final private = hex.encode(child.privateKey as List); + final private = hex.encode(child.privateKey!); final public = hex.encode(child.publicKey); return CryptoKeyPair(private, public); } @@ -164,11 +164,13 @@ class KeyService extends IKeyService { final kadenaChainKey = _kadenaChainKey(); final polkadotChainKey = _polkadotChainKey(); final solanaChainKeys = _solanaChainKey(); + final bitcoinChainKeys = await _bitcoinChainKey(); // return [ kadenaChainKey, polkadotChainKey, solanaChainKeys, + bitcoinChainKeys, ]; } @@ -198,4 +200,52 @@ class KeyService extends IKeyService { address: DartDefines.solanaAddress, ); } + + Future _bitcoinChainKey() async { + final mnemonic = await getMnemonic(); + final seed = bip39.mnemonicToSeed(mnemonic); + final node = bip32.BIP32.fromSeed(seed); + final child = node.derivePath("m/84'/0'/0'/0/0"); + final strng = child.toBase58(); + final restored = bip32.BIP32.fromBase58(strng); + final privateKey = ECPrivate.fromBytes(restored.privateKey!.toList()); + + const network = BitcoinNetwork.mainnet; + final wif = privateKey.toWif(network: network); + final publicKey = privateKey.getPublic(); + + // Generate a Pay-to-Public-Key-Hash (P2PKH) address from the public key. + final p2pkh = publicKey.toAddress(); + debugPrint('[$runtimeType] p2pkh ${p2pkh.toAddress(network)}'); + // Generate a Pay-to-Witness-Public-Key-Hash (P2WPKH) Segregated Witness (SegWit) address from the public key. + final p2wpkh = publicKey.toSegwitAddress(); + debugPrint('[$runtimeType] p2wpkh ${p2wpkh.toAddress(network)}'); + // // Generate a Pay-to-Witness-Script-Hash (P2WSH) Segregated Witness (SegWit) address from the public key. + // final p2wsh = publicKey.toP2wshAddress(); + // debugPrint('[$runtimeType] p2wsh ${p2wsh.toAddress(network)}'); + // // Generate a Taproot address from the public key. + // final p2tr = publicKey.toTaprootAddress(); + // debugPrint('[$runtimeType] p2tr ${p2tr.toAddress(network)}'); + // // Generate a Pay-to-Public-Key-Hash (P2PKH) inside Pay-to-Script-Hash (P2SH) address from the public key. + // final p2pkhInP2sh = publicKey.toP2pkhInP2sh(); + // debugPrint('[$runtimeType] p2pkhInP2sh ${p2pkhInP2sh.toAddress(network)}'); + // // Generate a Pay-to-Witness-Public-Key-Hash (P2WPKH) inside Pay-to-Script-Hash (P2SH) address from the public key. + // final p2wpkhInP2sh = publicKey.toP2wpkhInP2sh(); + // debugPrint( + // '[$runtimeType] p2wpkhInP2sh ${p2wpkhInP2sh.toAddress(network)}'); + // // Generate a Pay-to-Witness-Script-Hash (P2WSH) inside Pay-to-Script-Hash (P2SH) address from the public key. + // final p2wshInP2sh = publicKey.toP2wshInP2sh(); + // debugPrint('[$runtimeType] p2wshInP2sh ${p2wshInP2sh.toAddress(network)}'); + // // Generate a Pay-to-Public-Key (P2PK) inside Pay-to-Script-Hash (P2SH) address from the public key. + // final p2pkInP2sh = publicKey.toP2pkInP2sh(); + // debugPrint('[$runtimeType] p2pkInP2sh ${p2pkInP2sh.toAddress(network)}'); + + // + return ChainKey( + chains: ChainData.bitcoinChains.map((e) => e.chainId).toList(), + privateKey: wif, + publicKey: p2pkh.toAddress(network), + address: p2wpkh.toAddress(network), + ); + } } diff --git a/example/wallet/lib/main.dart b/example/wallet/lib/main.dart index 6a530f0..ecc111f 100644 --- a/example/wallet/lib/main.dart +++ b/example/wallet/lib/main.dart @@ -3,6 +3,7 @@ import 'package:get_it_mixin/get_it_mixin.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/bottom_sheet/bottom_sheet_listener.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/bottom_sheet/bottom_sheet_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/bottom_sheet/i_bottom_sheet_service.dart'; +import 'package:walletconnect_flutter_v2_wallet/dependencies/chain_services/bitcoin_service.dart'; // ignore: unused_import import 'package:walletconnect_flutter_v2_wallet/dependencies/chain_services/solana_service.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/chain_services/solana_service_2.dart'; @@ -77,6 +78,14 @@ class _MyHomePageState extends State with GetItStateMixin { ); } + // Support Bitcoin Chains + for (final chainData in ChainData.bitcoinChains) { + GetIt.I.registerSingleton( + BitcoinService(chainSupported: chainData), + instanceName: chainData.chainId, + ); + } + // Support Kadena Chains for (final chainData in ChainData.kadenaChains) { GetIt.I.registerSingleton( diff --git a/example/wallet/lib/models/chain_data.dart b/example/wallet/lib/models/chain_data.dart index 7e4aeec..4cffb8f 100644 --- a/example/wallet/lib/models/chain_data.dart +++ b/example/wallet/lib/models/chain_data.dart @@ -187,4 +187,27 @@ class ChainData { ], ), ]; + + static final List bitcoinChains = [ + const ChainMetadata( + type: ChainType.bitcoin, + chainId: + 'bip122:000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', + name: 'Bitcoin Mainnet', + logo: '/chain-logos/bitcoin.png', + color: Color.fromARGB(255, 255, 161, 9), + rpc: ['https://bitcoin.drpc.org/'], + ), + const ChainMetadata( + type: ChainType.bitcoin, + chainId: + 'bip122:000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943', + name: 'Bitcoin Signet', + logo: '/chain-logos/bitcoin.png', + color: Color.fromARGB(255, 255, 161, 9), + rpc: ['https://signet.bitcoinrpc.org'], + // https://bitcoin-testnet.drpc.org + isTestnet: true, + ), + ]; } diff --git a/example/wallet/lib/models/chain_metadata.dart b/example/wallet/lib/models/chain_metadata.dart index 38aee3d..f6b99db 100644 --- a/example/wallet/lib/models/chain_metadata.dart +++ b/example/wallet/lib/models/chain_metadata.dart @@ -7,6 +7,7 @@ enum ChainType { cosmos, kadena, polkadot, + bitcoin, } class ChainMetadata { diff --git a/example/wallet/lib/pages/settings_page.dart b/example/wallet/lib/pages/settings_page.dart index 82d15a5..07d0d91 100644 --- a/example/wallet/lib/pages/settings_page.dart +++ b/example/wallet/lib/pages/settings_page.dart @@ -61,6 +61,9 @@ class _SettingsPageState extends State { // const SizedBox(height: 20.0), const Divider(height: 1.0), + _BitcoinAccounts(), + const SizedBox(height: 20.0), + const Divider(height: 1.0), _SolanaAccounts(), const SizedBox(height: 20.0), const Divider(height: 1.0), @@ -468,6 +471,54 @@ class _KadenaAccounts extends StatelessWidget { } } +class _BitcoinAccounts extends StatelessWidget { + @override + Widget build(BuildContext context) { + final keysService = GetIt.I(); + final chainKeys = keysService.getKeysForChain('bip122'); + if (chainKeys.isEmpty) return const SizedBox.shrink(); + return Column( + children: [ + const Padding( + padding: EdgeInsets.all(12.0), + child: Row( + children: [ + SizedBox.square(dimension: 8.0), + Expanded( + child: Text( + 'Bitcoin Account', + style: TextStyle( + color: Colors.black, + fontSize: 16.0, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Column( + children: [ + _DataContainer( + title: 'Address', + data: chainKeys.first.address, + ), + const SizedBox(height: 12.0), + _DataContainer( + title: 'Secret key', + data: chainKeys.first.privateKey, + blurred: true, + ), + ], + ), + ) + ], + ); + } +} + class _DeviceData extends StatelessWidget { @override Widget build(BuildContext context) { diff --git a/example/wallet/lib/utils/dart_defines.dart b/example/wallet/lib/utils/dart_defines.dart index aa08209..33b3eec 100644 --- a/example/wallet/lib/utils/dart_defines.dart +++ b/example/wallet/lib/utils/dart_defines.dart @@ -1,6 +1,12 @@ class DartDefines { static const projectId = String.fromEnvironment('PROJECT_ID'); // HARDCODED TEST KEYS + // ETHEREUM + static const ethereumSecretKey = String.fromEnvironment( + 'ETH_SECRET_KEY', + defaultValue: + 'spoil video deputy round immense setup wasp secret maze slight bag what', + ); // KADENA static const kadenaSecretKey = String.fromEnvironment( 'KADENA_SECRET_KEY', @@ -26,7 +32,7 @@ class DartDefines { static const polkadotMnemonic = String.fromEnvironment( 'POLKADOT_MNEMONIC', defaultValue: - 'shove trumpet draw priority either tonight million worry dust vivid twelve solid', + 'spoil video deputy round immense setup wasp secret maze slight bag what', ); static const polkadotAddress = String.fromEnvironment( 'POLKADOT_ADDRESS', diff --git a/example/wallet/lib/utils/eth_utils.dart b/example/wallet/lib/utils/eth_utils.dart index 244c4b3..d8cb5af 100644 --- a/example/wallet/lib/utils/eth_utils.dart +++ b/example/wallet/lib/utils/eth_utils.dart @@ -1,8 +1,8 @@ import 'dart:convert'; -import 'package:convert/convert.dart'; import 'package:flutter/foundation.dart'; import 'package:get_it/get_it.dart'; +import 'package:solana_web3/solana_web3.dart' show hex; import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; import 'package:walletconnect_flutter_v2_wallet/dependencies/i_web3wallet_service.dart'; diff --git a/example/wallet/lib/utils/methods_utils.dart b/example/wallet/lib/utils/methods_utils.dart index c6fbcf3..5dbe89c 100644 --- a/example/wallet/lib/utils/methods_utils.dart +++ b/example/wallet/lib/utils/methods_utils.dart @@ -57,6 +57,7 @@ class MethodsUtils { debugPrint( '[SampleWallet] handleRedirect topic: $topic, redirect: $redirect, error: $error'); openApp(topic, redirect, onFail: (e) { + debugPrint('[SampleWallet] handleRedirect error $e'); goBackModal( title: 'Error', message: error, @@ -93,7 +94,7 @@ class MethodsUtils { closeAfter: success ? 3 : 0, widget: Container( color: Colors.white, - height: 210.0, + height: 300.0, width: double.infinity, padding: const EdgeInsets.all(20.0), child: Column( diff --git a/example/wallet/pubspec.yaml b/example/wallet/pubspec.yaml index 9a93180..7a4d54b 100644 --- a/example/wallet/pubspec.yaml +++ b/example/wallet/pubspec.yaml @@ -9,6 +9,9 @@ environment: sdk: ">=2.19.0 <4.0.0" dependencies: + bitcoin_base: ^4.2.1 + # flutter_bitcoin: ^1.0.1 + # bitcoin_message_signer: ^1.0.2 flutter: sdk: flutter @@ -21,7 +24,7 @@ dependencies: eth_sig_util: ^0.0.9 get_it_mixin: ^4.0.0 package_info_plus: ^7.0.0 - convert: ^3.0.1 + # convert: ^3.0.1 # CHECK WEB SUPPORT kadena_dart_sdk: ^2.3.2 diff --git a/example/wallet/test/widget_test.dart b/example/wallet/test/widget_test.dart index 990fad2..aee7c63 100644 --- a/example/wallet/test/widget_test.dart +++ b/example/wallet/test/widget_test.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:convert/convert.dart'; +import 'package:solana_web3/solana_web3.dart' show hex; import 'package:flutter_test/flutter_test.dart'; import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';