Fixes node connection, and sp, and electrum (#1577)

* refactor: remove bitcoin_flutter, update deps, electrs node improvements * feat: connecting/disconnecting improvements, fix rescan by date, scanning message * chore: print * Update pubspec.yaml * Update pubspec.yaml * handle null sockets, retry connection on connect failure * fix imports * fix transaction history * fix RBF * minor fixes/readability enhancements [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: Matthew Fosse <matt@fosse.co>

Rafael committed Aug 11, 2024 at 20:49 UTC bbba41396d32b0fde52b6d676ee310c9fa26f639
60 files changed +524 -454
cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart
+3 -2
@@ -1,7 +1,7 @@
1 import 'dart:async';
2
3 import 'package:bitcoin_base/bitcoin_base.dart';
4 -import 'package:bitcoin_flutter/bitcoin_flutter.dart';
4 +import 'package:blockchain_utils/blockchain_utils.dart';
5 import 'package:cw_bitcoin/utils.dart';
6 import 'package:cw_core/hardware/hardware_account_data.dart';
7 import 'package:ledger_bitcoin/ledger_bitcoin.dart';
@@ -25,7 +25,8 @@ class BitcoinHardwareWalletService {
25 for (final i in indexRange) {
26 final derivationPath = "m/84'/0'/$i'";
27 final xpub = await bitcoinLedgerApp.getXPubKey(device, derivationPath: derivationPath);
28 - HDWallet hd = HDWallet.fromBase58(xpub).derive(0);
28 + Bip32Slip10Secp256k1 hd =
29 + Bip32Slip10Secp256k1.fromExtendedKey(xpub).childKey(Bip32KeyIndex(0));
30
31 final address = generateP2WPKHAddress(hd: hd, index: 0, network: BitcoinNetwork.mainnet);
32
cw_bitcoin/lib/bitcoin_wallet.dart
+8 -10
@@ -2,8 +2,7 @@ import 'dart:convert';
2
3 import 'package:bip39/bip39.dart' as bip39;
4 import 'package:bitcoin_base/bitcoin_base.dart';
5 -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
6 -import 'package:convert/convert.dart';
5 +import 'package:blockchain_utils/blockchain_utils.dart';
6 import 'package:cw_bitcoin/bitcoin_address_record.dart';
7 import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
8 import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
@@ -51,11 +50,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
50 password: password,
51 walletInfo: walletInfo,
52 unspentCoinsInfo: unspentCoinsInfo,
54 - networkType: networkParam == null
55 - ? bitcoin.bitcoin
53 + network: networkParam == null
54 + ? BitcoinNetwork.mainnet
55 : networkParam == BitcoinNetwork.mainnet
57 - ? bitcoin.bitcoin
58 - : bitcoin.testnet,
56 + ? BitcoinNetwork.mainnet
57 + : BitcoinNetwork.testnet,
58 initialAddresses: initialAddresses,
59 initialBalance: initialBalance,
60 seedBytes: seedBytes,
@@ -76,10 +75,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
75 initialSilentAddresses: initialSilentAddresses,
76 initialSilentAddressIndex: initialSilentAddressIndex,
77 mainHd: hd,
79 - sideHd: accountHD.derive(1),
78 + sideHd: accountHD.childKey(Bip32KeyIndex(1)),
79 network: networkParam ?? network,
81 - masterHd:
82 - seedBytes != null ? bitcoin.HDWallet.fromSeed(seedBytes, network: networkType) : null,
80 + masterHd: seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
81 );
82
83 autorun((_) {
@@ -253,7 +251,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
251 PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF);
252
253 final rawHex = await _bitcoinLedgerApp!.signPsbt(_ledgerDevice!, psbt: psbt.psbt);
256 - return BtcTransaction.fromRaw(hex.encode(rawHex));
254 + return BtcTransaction.fromRaw(BytesUtils.toHexString(rawHex));
255 }
256
257 @override
cw_bitcoin/lib/bitcoin_wallet_addresses.dart
+3 -2
@@ -1,5 +1,5 @@
1 import 'package:bitcoin_base/bitcoin_base.dart';
2 -import 'package:bitcoin_flutter/bitcoin_flutter.dart';
2 +import 'package:blockchain_utils/bip/bip/bip32/bip32.dart';
3 import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
4 import 'package:cw_bitcoin/utils.dart';
5 import 'package:cw_core/wallet_info.dart';
@@ -24,7 +24,8 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
24 }) : super(walletInfo);
25
26 @override
27 - String getAddress({required int index, required HDWallet hd, BitcoinAddressType? addressType}) {
27 + String getAddress(
28 + {required int index, required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType}) {
29 if (addressType == P2pkhAddressType.p2pkh)
30 return generateP2PKHAddress(hd: hd, index: index, network: network);
31
cw_bitcoin/lib/electrum.dart
+74 -37
@@ -8,6 +8,8 @@ import 'package:cw_bitcoin/script_hash.dart';
8 import 'package:flutter/foundation.dart';
9 import 'package:rxdart/rxdart.dart';
10
11 +enum ConnectionStatus { connected, disconnected, connecting, failed }
12 +
13 String jsonrpcparams(List<Object> params) {
14 final _params = params.map((val) => '"${val.toString()}"').join(',');
15 return '[$_params]';
@@ -41,7 +43,7 @@ class ElectrumClient {
43
44 bool get isConnected => _isConnected;
45 Socket? socket;
44 - void Function(bool?)? onConnectionStatusChange;
46 + void Function(ConnectionStatus)? onConnectionStatusChange;
47 int _id;
48 final Map<String, SocketTask> _tasks;
49 Map<String, SocketTask> get tasks => _tasks;
@@ -60,17 +62,33 @@ class ElectrumClient {
62 }
63
64 Future<void> connect({required String host, required int port, bool? useSSL}) async {
65 + _setConnectionStatus(ConnectionStatus.connecting);
66 +
67 try {
68 await socket?.close();
69 } catch (_) {}
70
67 - if (useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum"))) {
68 - socket = await Socket.connect(host, port, timeout: connectionTimeout);
69 - } else {
70 - socket = await SecureSocket.connect(host, port,
71 - timeout: connectionTimeout, onBadCertificate: (_) => true);
71 + try {
72 + if (useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum"))) {
73 + socket = await Socket.connect(host, port, timeout: connectionTimeout);
74 + } else {
75 + socket = await SecureSocket.connect(
76 + host,
77 + port,
78 + timeout: connectionTimeout,
79 + onBadCertificate: (_) => true,
80 + );
81 + }
82 + } catch (_) {
83 + _setConnectionStatus(ConnectionStatus.failed);
84 + return;
85 + }
86 +
87 + if (socket == null) {
88 + _setConnectionStatus(ConnectionStatus.failed);
89 + return;
90 }
73 - _setIsConnected(true);
91 + _setConnectionStatus(ConnectionStatus.connected);
92
93 socket!.listen((Uint8List event) {
94 try {
@@ -86,13 +104,20 @@ class ElectrumClient {
104 print(e.toString());
105 }
106 }, onError: (Object error) {
89 - print(error.toString());
107 + final errorMsg = error.toString();
108 + print(errorMsg);
109 unterminatedString = '';
91 - _setIsConnected(false);
110 +
111 + final currentHost = socket?.address.host;
112 + final isErrorForCurrentHost = errorMsg.contains(" ${currentHost} ");
113 +
114 + if (currentHost != null && isErrorForCurrentHost)
115 + _setConnectionStatus(ConnectionStatus.failed);
116 }, onDone: () {
117 unterminatedString = '';
94 - _setIsConnected(null);
118 + if (host == socket?.address.host) _setConnectionStatus(ConnectionStatus.disconnected);
119 });
120 +
121 keepAlive();
122 }
123
@@ -144,9 +169,9 @@ class ElectrumClient {
169 Future<void> ping() async {
170 try {
171 await callWithTimeout(method: 'server.ping');
147 - _setIsConnected(true);
172 + _setConnectionStatus(ConnectionStatus.connected);
173 } on RequestFailedTimeoutException catch (_) {
149 - _setIsConnected(null);
174 + _setConnectionStatus(ConnectionStatus.disconnected);
175 }
176 }
177
@@ -236,37 +261,39 @@ class ElectrumClient {
261 return [];
262 });
263
239 - Future<Map<String, dynamic>> getTransactionRaw({required String hash}) async {
264 + Future<dynamic> getTransaction({required String hash, required bool verbose}) async {
265 try {
266 final result = await callWithTimeout(
242 - method: 'blockchain.transaction.get', params: [hash, true], timeout: 10000);
267 + method: 'blockchain.transaction.get', params: [hash, verbose], timeout: 10000);
268 if (result is Map<String, dynamic>) {
269 return result;
270 }
271 } on RequestFailedTimeoutException catch (_) {
272 return <String, dynamic>{};
273 } catch (e) {
249 - print("getTransactionRaw: ${e.toString()}");
274 + print("getTransaction: ${e.toString()}");
275 return <String, dynamic>{};
276 }
277 return <String, dynamic>{};
278 }
279
255 - Future<String> getTransactionHex({required String hash}) async {
256 - try {
257 - final result = await callWithTimeout(
258 - method: 'blockchain.transaction.get', params: [hash, false], timeout: 10000);
259 - if (result is String) {
260 - return result;
261 - }
262 - } on RequestFailedTimeoutException catch (_) {
263 - return '';
264 - } catch (e) {
265 - print("getTransactionHex: ${e.toString()}");
266 - return '';
267 - }
268 - return '';
269 - }
280 + Future<Map<String, dynamic>> getTransactionVerbose({required String hash}) =>
281 + getTransaction(hash: hash, verbose: true).then((dynamic result) {
282 + if (result is Map<String, dynamic>) {
283 + return result;
284 + }
285 +
286 + return <String, dynamic>{};
287 + });
288 +
289 + Future<String> getTransactionHex({required String hash}) =>
290 + getTransaction(hash: hash, verbose: false).then((dynamic result) {
291 + if (result is String) {
292 + return result;
293 + }
294 +
295 + return '';
296 + });
297
298 Future<String> broadcastTransaction(
299 {required String transactionRaw,
@@ -348,7 +375,7 @@ class ElectrumClient {
375 try {
376 final topDoubleString = await estimatefee(p: 1);
377 final middleDoubleString = await estimatefee(p: 5);
351 - final bottomDoubleString = await estimatefee(p: 100);
378 + final bottomDoubleString = await estimatefee(p: 10);
379 final top = (stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000).round();
380 final middle = (stringDoubleToBitcoinAmount(middleDoubleString.toString()) / 1000).round();
381 final bottom = (stringDoubleToBitcoinAmount(bottomDoubleString.toString()) / 1000).round();
@@ -398,6 +425,10 @@ class ElectrumClient {
425 BehaviorSubject<T>? subscribe<T>(
426 {required String id, required String method, List<Object> params = const []}) {
427 try {
428 + if (socket == null) {
429 + _setConnectionStatus(ConnectionStatus.failed);
430 + return null;
431 + }
432 final subscription = BehaviorSubject<T>();
433 _regisrySubscription(id, subscription);
434 socket!.write(jsonrpc(method: method, id: _id, params: params));
@@ -411,6 +442,10 @@ class ElectrumClient {
442
443 Future<dynamic> call(
444 {required String method, List<Object> params = const [], Function(int)? idCallback}) async {
445 + if (socket == null) {
446 + _setConnectionStatus(ConnectionStatus.failed);
447 + return null;
448 + }
449 final completer = Completer<dynamic>();
450 _id += 1;
451 final id = _id;
@@ -424,6 +459,10 @@ class ElectrumClient {
459 Future<dynamic> callWithTimeout(
460 {required String method, List<Object> params = const [], int timeout = 4000}) async {
461 try {
462 + if (socket == null) {
463 + _setConnectionStatus(ConnectionStatus.failed);
464 + return null;
465 + }
466 final completer = Completer<dynamic>();
467 _id += 1;
468 final id = _id;
@@ -445,6 +484,7 @@ class ElectrumClient {
484 _aliveTimer?.cancel();
485 try {
486 await socket?.close();
487 + socket = null;
488 } catch (_) {}
489 onConnectionStatusChange = null;
490 }
@@ -493,12 +533,9 @@ class ElectrumClient {
533 }
534 }
535
496 - void _setIsConnected(bool? isConnected) {
497 - if (_isConnected != isConnected) {
498 - onConnectionStatusChange?.call(isConnected);
499 - }
500 -
501 - _isConnected = isConnected ?? false;
536 + void _setConnectionStatus(ConnectionStatus status) {
537 + onConnectionStatusChange?.call(status);
538 + _isConnected = status == ConnectionStatus.connected;
539 }
540
541 void _handleResponse(Map<String, dynamic> response) {
cw_bitcoin/lib/electrum_transaction_info.dart
+2 -2
@@ -22,7 +22,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
22
23 ElectrumTransactionInfo(this.type,
24 {required String id,
25 - required int height,
25 + int? height,
26 required int amount,
27 int? fee,
28 List<String>? inputAddresses,
@@ -99,7 +99,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
99
100 factory ElectrumTransactionInfo.fromElectrumBundle(
101 ElectrumTransactionBundle bundle, WalletType type, BasedUtxoNetwork network,
102 - {required Set<String> addresses, required int height}) {
102 + {required Set<String> addresses, int? height}) {
103 final date = bundle.time != null
104 ? DateTime.fromMillisecondsSinceEpoch(bundle.time! * 1000)
105 : DateTime.now();
cw_bitcoin/lib/electrum_wallet.dart
+186 -107
@@ -5,7 +5,6 @@ import 'dart:isolate';
5 import 'dart:math';
6
7 import 'package:bitcoin_base/bitcoin_base.dart';
8 -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
8 import 'package:blockchain_utils/blockchain_utils.dart';
9 import 'package:collection/collection.dart';
10 import 'package:cw_bitcoin/address_from_output.dart';
@@ -22,7 +21,6 @@ import 'package:cw_bitcoin/electrum_transaction_history.dart';
21 import 'package:cw_bitcoin/electrum_transaction_info.dart';
22 import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
23 import 'package:cw_bitcoin/exceptions.dart';
25 -import 'package:cw_bitcoin/litecoin_network.dart';
24 import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
25 import 'package:cw_bitcoin/script_hash.dart';
26 import 'package:cw_bitcoin/utils.dart';
@@ -42,7 +40,6 @@ import 'package:cw_core/wallet_type.dart';
40 import 'package:cw_core/get_height_by_date.dart';
41 import 'package:flutter/foundation.dart';
42 import 'package:hive/hive.dart';
45 -import 'package:http/http.dart' as http;
43 import 'package:mobx/mobx.dart';
44 import 'package:rxdart/subjects.dart';
45 import 'package:sp_scanner/sp_scanner.dart';
@@ -60,7 +57,7 @@ abstract class ElectrumWalletBase
57 required String password,
58 required WalletInfo walletInfo,
59 required Box<UnspentCoinsInfo> unspentCoinsInfo,
63 - required this.networkType,
60 + required this.network,
61 String? xpub,
62 String? mnemonic,
63 Uint8List? seedBytes,
@@ -71,7 +68,7 @@ abstract class ElectrumWalletBase
68 CryptoCurrency? currency,
69 this.alwaysScan,
70 }) : accountHD =
74 - getAccountHDWallet(currency, networkType, seedBytes, xpub, walletInfo.derivationInfo),
71 + getAccountHDWallet(currency, network, seedBytes, xpub, walletInfo.derivationInfo),
72 syncStatus = NotConnectedSyncStatus(),
73 _password = password,
74 _feeRates = <int>[],
@@ -90,8 +87,7 @@ abstract class ElectrumWalletBase
87 }
88 : {}),
89 this.unspentCoinsInfo = unspentCoinsInfo,
93 - this.network = _getNetwork(networkType, currency),
94 - this.isTestnet = networkType == bitcoin.testnet,
90 + this.isTestnet = network == BitcoinNetwork.testnet,
91 this._mnemonic = mnemonic,
92 super(walletInfo) {
93 this.electrumClient = electrumClient ?? ElectrumClient();
@@ -101,12 +97,8 @@ abstract class ElectrumWalletBase
97 reaction((_) => syncStatus, _syncStatusReaction);
98 }
99
104 - static bitcoin.HDWallet getAccountHDWallet(
105 - CryptoCurrency? currency,
106 - bitcoin.NetworkType networkType,
107 - Uint8List? seedBytes,
108 - String? xpub,
109 - DerivationInfo? derivationInfo) {
100 + static Bip32Slip10Secp256k1 getAccountHDWallet(CryptoCurrency? currency, BasedUtxoNetwork network,
101 + Uint8List? seedBytes, String? xpub, DerivationInfo? derivationInfo) {
102 if (seedBytes == null && xpub == null) {
103 throw Exception(
104 "To create a Wallet you need either a seed or an xpub. This should not happen");
@@ -115,25 +107,26 @@ abstract class ElectrumWalletBase
107 if (seedBytes != null) {
108 return currency == CryptoCurrency.bch
109 ? bitcoinCashHDWallet(seedBytes)
118 - : bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
119 - .derivePath(_hardenedDerivationPath(derivationInfo?.derivationPath ?? electrum_path));
110 + : Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath(
111 + _hardenedDerivationPath(derivationInfo?.derivationPath ?? electrum_path))
112 + as Bip32Slip10Secp256k1;
113 }
114
122 - return bitcoin.HDWallet.fromBase58(xpub!);
115 + return Bip32Slip10Secp256k1.fromExtendedKey(xpub!);
116 }
117
125 - static bitcoin.HDWallet bitcoinCashHDWallet(Uint8List seedBytes) =>
126 - bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'");
118 + static Bip32Slip10Secp256k1 bitcoinCashHDWallet(Uint8List seedBytes) =>
119 + Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/44'/145'/0'") as Bip32Slip10Secp256k1;
120
121 static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
122 inputsCount * 68 + outputsCounts * 34 + 10;
123
124 bool? alwaysScan;
125
133 - final bitcoin.HDWallet accountHD;
126 + final Bip32Slip10Secp256k1 accountHD;
127 final String? _mnemonic;
128
136 - bitcoin.HDWallet get hd => accountHD.derive(0);
129 + Bip32Slip10Secp256k1 get hd => accountHD.childKey(Bip32KeyIndex(0));
130 final String? passphrase;
131
132 @override
@@ -165,7 +158,7 @@ abstract class ElectrumWalletBase
158 .map((addr) => scriptHash(addr.address, network: network))
159 .toList();
160
168 - String get xpub => accountHD.base58!;
161 + String get xpub => accountHD.publicKey.toExtended;
162
163 @override
164 String? get seed => _mnemonic;
@@ -174,7 +167,6 @@ abstract class ElectrumWalletBase
167 WalletKeysData get walletKeysData =>
168 WalletKeysData(mnemonic: _mnemonic, xPub: xpub, passphrase: passphrase);
169
177 - bitcoin.NetworkType networkType;
170 BasedUtxoNetwork network;
171
172 @override
@@ -190,24 +182,21 @@ abstract class ElectrumWalletBase
182 bool _isTryingToConnect = false;
183
184 @action
193 - Future<void> setSilentPaymentsScanning(bool active, bool usingElectrs) async {
185 + Future<void> setSilentPaymentsScanning(bool active) async {
186 silentPaymentsScanningActive = active;
187
188 if (active) {
197 - syncStatus = AttemptingSyncStatus();
189 + syncStatus = StartingScanSyncStatus();
190
191 final tip = await getUpdatedChainTip();
192
193 if (tip == walletInfo.restoreHeight) {
194 syncStatus = SyncedTipSyncStatus(tip);
195 + return;
196 }
197
198 if (tip > walletInfo.restoreHeight) {
206 - _setListeners(
207 - walletInfo.restoreHeight,
208 - chainTipParam: _currentChainTip,
209 - usingElectrs: usingElectrs,
210 - );
199 + _setListeners(walletInfo.restoreHeight, chainTipParam: _currentChainTip);
200 }
201 } else {
202 alwaysScan = false;
@@ -245,8 +234,11 @@ abstract class ElectrumWalletBase
234 }
235
236 @override
248 - BitcoinWalletKeys get keys =>
249 - BitcoinWalletKeys(wif: hd.wif!, privateKey: hd.privKey!, publicKey: hd.pubKey!);
237 + BitcoinWalletKeys get keys => BitcoinWalletKeys(
238 + wif: WifEncoder.encode(hd.privateKey.raw, netVer: network.wifNetVer),
239 + privateKey: hd.privateKey.toHex(),
240 + publicKey: hd.publicKey.toHex(),
241 + );
242
243 String _password;
244 List<BitcoinUnspent> unspentCoins;
@@ -278,7 +270,7 @@ abstract class ElectrumWalletBase
270 int height, {
271 int? chainTipParam,
272 bool? doSingleScan,
281 - bool? usingElectrs,
273 + bool? usingSupportedNode,
274 }) async {
275 final chainTip = chainTipParam ?? await getUpdatedChainTip();
276
@@ -287,7 +279,7 @@ abstract class ElectrumWalletBase
279 return;
280 }
281
290 - syncStatus = AttemptingSyncStatus();
282 + syncStatus = StartingScanSyncStatus();
283
284 if (_isolate != null) {
285 final runningIsolate = await _isolate!;
@@ -305,7 +297,9 @@ abstract class ElectrumWalletBase
297 chainTip: chainTip,
298 electrumClient: ElectrumClient(),
299 transactionHistoryIds: transactionHistory.transactions.keys.toList(),
308 - node: usingElectrs == true ? ScanNode(node!.uri, node!.useSSL) : null,
300 + node: (await getNodeSupportsSilentPayments()) == true
301 + ? ScanNode(node!.uri, node!.useSSL)
302 + : null,
303 labels: walletAddresses.labels,
304 labelIndexes: walletAddresses.silentAddresses
305 .where((addr) => addr.type == SilentPaymentsAddresType.p2sp && addr.index >= 1)
@@ -393,7 +387,7 @@ abstract class ElectrumWalletBase
387 BigintUtils.fromBytes(BytesUtils.fromHexString(unspent.silentPaymentLabel!)),
388 )
389 : silentAddress.B_spend,
396 - hrp: silentAddress.hrp,
390 + network: network,
391 );
392
393 final addressRecord = walletAddresses.silentAddresses
@@ -422,8 +416,6 @@ abstract class ElectrumWalletBase
416 await updateAllUnspents();
417 await updateBalance();
418
425 - Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates());
426 -
419 if (alwaysScan == true) {
420 _setListeners(walletInfo.restoreHeight);
421 } else {
@@ -446,6 +438,58 @@ abstract class ElectrumWalletBase
438
439 Node? node;
440
441 + Future<bool> getNodeIsElectrs() async {
442 + if (node == null) {
443 + return false;
444 + }
445 +
446 + final version = await electrumClient.version();
447 +
448 + if (version.isNotEmpty) {
449 + final server = version[0];
450 +
451 + if (server.toLowerCase().contains('electrs')) {
452 + node!.isElectrs = true;
453 + node!.save();
454 + return node!.isElectrs!;
455 + }
456 + }
457 +
458 +
459 + node!.isElectrs = false;
460 + node!.save();
461 + return node!.isElectrs!;
462 + }
463 +
464 + Future<bool> getNodeSupportsSilentPayments() async {
465 + // As of today (august 2024), only ElectrumRS supports silent payments
466 + if (!(await getNodeIsElectrs())) {
467 + return false;
468 + }
469 +
470 + if (node == null) {
471 + return false;
472 + }
473 +
474 + try {
475 + final tweaksResponse = await electrumClient.getTweaks(height: 0);
476 +
477 + if (tweaksResponse != null) {
478 + node!.supportsSilentPayments = true;
479 + node!.save();
480 + return node!.supportsSilentPayments!;
481 + }
482 + } on RequestFailedTimeoutException catch (_) {
483 + node!.supportsSilentPayments = false;
484 + node!.save();
485 + return node!.supportsSilentPayments!;
486 + } catch (_) {}
487 +
488 + node!.supportsSilentPayments = false;
489 + node!.save();
490 + return node!.supportsSilentPayments!;
491 + }
492 +
493 @action
494 @override
495 Future<void> connectToNode({required Node node}) async {
@@ -507,13 +551,6 @@ abstract class ElectrumWalletBase
551
552 final hd =
553 utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
510 - final derivationPath =
511 - "${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
512 - "/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
513 - "/${utx.bitcoinAddressRecord.index}";
514 - final pubKeyHex = hd.derive(utx.bitcoinAddressRecord.index).pubKey!;
515 -
516 - publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
554
555 if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
556 final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
@@ -530,6 +567,7 @@ abstract class ElectrumWalletBase
567 }
568
569 vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout));
570 + String pubKeyHex;
571
572 if (privkey != null) {
573 inputPrivKeyInfos.add(ECPrivateInfo(
@@ -537,8 +575,18 @@ abstract class ElectrumWalletBase
575 address.type == SegwitAddresType.p2tr,
576 tweak: !isSilentPayment,
577 ));
578 +
579 + pubKeyHex = privkey.getPublic().toHex();
580 + } else {
581 + pubKeyHex = hd.childKey(Bip32KeyIndex(utx.bitcoinAddressRecord.index)).publicKey.toHex();
582 }
583
584 + final derivationPath =
585 + "${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
586 + "/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
587 + "/${utx.bitcoinAddressRecord.index}";
588 + publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
589 +
590 utxos.add(
591 UtxoWithAddress(
592 utxo: BitcoinUtxo(
@@ -1127,10 +1175,9 @@ abstract class ElectrumWalletBase
1175 int? chainTip,
1176 ScanData? scanData,
1177 bool? doSingleScan,
1130 - bool? usingElectrs,
1178 }) async {
1179 silentPaymentsScanningActive = true;
1133 - _setListeners(height, doSingleScan: doSingleScan, usingElectrs: usingElectrs);
1180 + _setListeners(height, doSingleScan: doSingleScan);
1181 }
1182
1183 @override
@@ -1228,7 +1275,7 @@ abstract class ElectrumWalletBase
1275 await Future.wait(unspents.map((unspent) async {
1276 try {
1277 final coin = BitcoinUnspent.fromJSON(address, unspent);
1231 - final tx = await fetchTransactionInfo(hash: coin.hash, height: 0);
1278 + final tx = await fetchTransactionInfo(hash: coin.hash);
1279 coin.isChange = address.isHidden;
1280 coin.confirmations = tx?.confirmations;
1281
@@ -1283,9 +1330,17 @@ abstract class ElectrumWalletBase
1330 }
1331
1332 Future<bool> canReplaceByFee(String hash) async {
1286 - final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash);
1287 - final confirmations = verboseTransaction['confirmations'] as int? ?? 0;
1288 - final transactionHex = verboseTransaction['hex'] as String?;
1333 + final verboseTransaction = await electrumClient.getTransactionVerbose(hash: hash);
1334 +
1335 + final String? transactionHex;
1336 + int confirmations = 0;
1337 +
1338 + if (verboseTransaction.isEmpty) {
1339 + transactionHex = await electrumClient.getTransactionHex(hash: hash);
1340 + } else {
1341 + confirmations = verboseTransaction['confirmations'] as int? ?? 0;
1342 + transactionHex = verboseTransaction['hex'] as String?;
1343 + }
1344
1345 if (confirmations > 0) return false;
1346
@@ -1293,10 +1348,7 @@ abstract class ElectrumWalletBase
1348 return false;
1349 }
1350
1296 - final original = bitcoin.Transaction.fromHex(transactionHex);
1297 -
1298 - return original.ins
1299 - .any((element) => element.sequence != null && element.sequence! < 4294967293);
1351 + return BtcTransaction.fromRaw(transactionHex).canReplaceByFee;
1352 }
1353
1354 Future<bool> isChangeSufficientForFee(String txId, int newFee) async {
@@ -1455,50 +1507,73 @@ abstract class ElectrumWalletBase
1507 }
1508 }
1509
1458 - Future<ElectrumTransactionBundle> getTransactionExpanded({required String hash}) async {
1510 + Future<ElectrumTransactionBundle> getTransactionExpanded(
1511 + {required String hash, int? height}) async {
1512 String transactionHex;
1513 + // TODO: time is not always available, and calculating it from height is not always accurate.
1514 + // Add settings to choose API provider and use and http server instead of electrum for this.
1515 int? time;
1461 - int confirmations = 0;
1462 - if (network == BitcoinNetwork.testnet) {
1463 - // Testnet public electrum server does not support verbose transaction fetching
1464 - transactionHex = await electrumClient.getTransactionHex(hash: hash);
1516 + int? confirmations;
1517
1466 - final status = json.decode(
1467 - (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$hash/status"))).body);
1518 + final verboseTransaction = await electrumClient.getTransactionVerbose(hash: hash);
1519
1469 - time = status["block_time"] as int?;
1470 - final height = status["block_height"] as int? ?? 0;
1471 - final tip = await getUpdatedChainTip();
1472 - if (tip > 0) confirmations = height > 0 ? tip - height + 1 : 0;
1520 + if (verboseTransaction.isEmpty) {
1521 + transactionHex = await electrumClient.getTransactionHex(hash: hash);
1522 } else {
1474 - final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash);
1475 -
1523 transactionHex = verboseTransaction['hex'] as String;
1524 time = verboseTransaction['time'] as int?;
1478 - confirmations = verboseTransaction['confirmations'] as int? ?? 0;
1525 + confirmations = verboseTransaction['confirmations'] as int?;
1526 + }
1527 +
1528 + if (height != null) {
1529 + if (time == null) {
1530 + time = (getDateByBitcoinHeight(height).millisecondsSinceEpoch / 1000).round();
1531 + }
1532 +
1533 + if (confirmations == null) {
1534 + final tip = await getUpdatedChainTip();
1535 + if (tip > 0 && height > 0) {
1536 + // Add one because the block itself is the first confirmation
1537 + confirmations = tip - height + 1;
1538 + }
1539 + }
1540 }
1541
1542 final original = BtcTransaction.fromRaw(transactionHex);
1543 final ins = <BtcTransaction>[];
1544
1545 for (final vin in original.inputs) {
1485 - ins.add(BtcTransaction.fromRaw(await electrumClient.getTransactionHex(hash: vin.txId)));
1546 + final verboseTransaction = await electrumClient.getTransactionVerbose(hash: vin.txId);
1547 +
1548 + final String inputTransactionHex;
1549 +
1550 + if (verboseTransaction.isEmpty) {
1551 + inputTransactionHex = await electrumClient.getTransactionHex(hash: hash);
1552 + } else {
1553 + inputTransactionHex = verboseTransaction['hex'] as String;
1554 + }
1555 +
1556 + ins.add(BtcTransaction.fromRaw(inputTransactionHex));
1557 }
1558
1559 return ElectrumTransactionBundle(
1560 original,
1561 ins: ins,
1562 time: time,
1492 - confirmations: confirmations,
1563 + confirmations: confirmations ?? 0,
1564 );
1565 }
1566
1567 Future<ElectrumTransactionInfo?> fetchTransactionInfo(
1497 - {required String hash, required int height, bool? retryOnFailure}) async {
1568 + {required String hash, int? height, bool? retryOnFailure}) async {
1569 try {
1570 return ElectrumTransactionInfo.fromElectrumBundle(
1500 - await getTransactionExpanded(hash: hash), walletInfo.type, network,
1501 - addresses: addressesSet, height: height);
1571 + await getTransactionExpanded(hash: hash, height: height),
1572 + walletInfo.type,
1573 + network,
1574 + addresses: addressesSet,
1575 + height: height,
1576 + );
1577 } catch (e) {
1578 if (e is FormatException && retryOnFailure == true) {
1579 await Future.delayed(const Duration(seconds: 2));
@@ -1649,8 +1724,8 @@ abstract class ElectrumWalletBase
1724 await getCurrentChainTip();
1725
1726 transactionHistory.transactions.values.forEach((tx) async {
1652 - if (tx.unspents != null && tx.unspents!.isNotEmpty && tx.height > 0) {
1653 - tx.confirmations = await getCurrentChainTip() - tx.height + 1;
1727 + if (tx.unspents != null && tx.unspents!.isNotEmpty && tx.height != null && tx.height! > 0) {
1728 + tx.confirmations = await getCurrentChainTip() - tx.height! + 1;
1729 }
1730 });
1731
@@ -1766,8 +1841,12 @@ abstract class ElectrumWalletBase
1841 final index = address != null
1842 ? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
1843 : null;
1769 - final HD = index == null ? hd : hd.derive(index);
1770 - return base64Encode(HD.signMessage(message));
1844 + final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index));
1845 + final priv = ECPrivate.fromWif(
1846 + WifEncoder.encode(HD.privateKey.raw, netVer: network.wifNetVer),
1847 + netVersion: network.wifNetVer,
1848 + );
1849 + return priv.signMessage(StringUtils.encode(message));
1850 }
1851
1852 Future<void> _setInitialHeight() async {
@@ -1793,43 +1872,42 @@ abstract class ElectrumWalletBase
1872 });
1873 }
1874
1796 - static BasedUtxoNetwork _getNetwork(bitcoin.NetworkType networkType, CryptoCurrency? currency) {
1797 - if (networkType == bitcoin.bitcoin && currency == CryptoCurrency.bch) {
1798 - return BitcoinCashNetwork.mainnet;
1799 - }
1800 -
1801 - if (networkType == litecoinNetwork) {
1802 - return LitecoinNetwork.mainnet;
1803 - }
1804 -
1805 - if (networkType == bitcoin.testnet) {
1806 - return BitcoinNetwork.testnet;
1807 - }
1808 -
1809 - return BitcoinNetwork.mainnet;
1810 - }
1811 -
1875 static String _hardenedDerivationPath(String derivationPath) =>
1876 derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
1877
1878 @action
1816 - void _onConnectionStatusChange(bool? isConnected) {
1817 - if (syncStatus is SyncingSyncStatus) return;
1879 + void _onConnectionStatusChange(ConnectionStatus status) {
1880 + switch (status) {
1881 + case ConnectionStatus.connected:
1882 + if (syncStatus is NotConnectedSyncStatus ||
1883 + syncStatus is LostConnectionSyncStatus ||
1884 + syncStatus is ConnectingSyncStatus) {
1885 + syncStatus = AttemptingSyncStatus();
1886 + startSync();
1887 + }
1888
1819 - if (isConnected == true && syncStatus is! SyncedSyncStatus) {
1820 - syncStatus = ConnectedSyncStatus();
1821 - } else if (isConnected == false) {
1822 - syncStatus = LostConnectionSyncStatus();
1823 - } else if (isConnected != true && syncStatus is! ConnectingSyncStatus) {
1824 - syncStatus = NotConnectedSyncStatus();
1889 + break;
1890 + case ConnectionStatus.disconnected:
1891 + syncStatus = NotConnectedSyncStatus();
1892 + break;
1893 + case ConnectionStatus.failed:
1894 + syncStatus = LostConnectionSyncStatus();
1895 + // wait for 5 seconds and then try to reconnect:
1896 + Future.delayed(Duration(seconds: 5), () {
1897 + electrumClient.connectToUri(
1898 + node!.uri,
1899 + useSSL: node!.useSSL ?? false,
1900 + );
1901 + });
1902 + break;
1903 + case ConnectionStatus.connecting:
1904 + syncStatus = ConnectingSyncStatus();
1905 + break;
1906 + default:
1907 }
1908 }
1909
1910 void _syncStatusReaction(SyncStatus syncStatus) async {
1829 - if (syncStatus is! AttemptingSyncStatus && syncStatus is! SyncedTipSyncStatus) {
1830 - silentPaymentsScanningActive = syncStatus is SyncingSyncStatus;
1831 - }
1832 -
1911 if (syncStatus is NotConnectedSyncStatus) {
1912 // Needs to re-subscribe to all scripthashes when reconnected
1913 _scripthashesUpdateSubject = {};
@@ -1950,8 +2028,8 @@ Future<void> startRefresh(ScanData scanData) async {
2028 final tweaks = t as Map<String, dynamic>;
2029
2030 if (tweaks["message"] != null) {
1953 - // re-subscribe to continue receiving messages
1954 - electrumClient.tweaksSubscribe(height: syncHeight, count: count);
2031 + // re-subscribe to continue receiving messages, starting from the next unscanned height
2032 + electrumClient.tweaksSubscribe(height: syncHeight + 1, count: count);
2033 return;
2034 }
2035
@@ -2180,3 +2258,4 @@ class UtxoDetails {
2258 required this.spendsUnconfirmedTX,
2259 });
2260 }
2261 +
cw_bitcoin/lib/electrum_wallet_addresses.dart
+17 -13
@@ -1,5 +1,4 @@
1 import 'package:bitcoin_base/bitcoin_base.dart';
2 -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
2 import 'package:blockchain_utils/blockchain_utils.dart';
3 import 'package:cw_bitcoin/bitcoin_address_record.dart';
4 import 'package:cw_core/wallet_addresses.dart';
@@ -30,7 +29,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
29 Map<String, int>? initialChangeAddressIndex,
30 List<BitcoinSilentPaymentAddressRecord>? initialSilentAddresses,
31 int initialSilentAddressIndex = 0,
33 - bitcoin.HDWallet? masterHd,
32 + Bip32Slip10Secp256k1? masterHd,
33 BitcoinAddressType? initialAddressPageType,
34 }) : _addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
35 addressesByReceiveType =
@@ -53,9 +52,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
52 super(walletInfo) {
53 if (masterHd != null) {
54 silentAddress = SilentPaymentOwner.fromPrivateKeys(
56 - b_scan: ECPrivate.fromHex(masterHd.derivePath(SCAN_PATH).privKey!),
57 - b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privKey!),
58 - hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp');
55 + b_scan: ECPrivate.fromHex(masterHd.derivePath(SCAN_PATH).privateKey.toHex()),
56 + b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privateKey.toHex()),
57 + network: network,
58 + );
59
60 if (silentAddresses.length == 0) {
61 silentAddresses.add(BitcoinSilentPaymentAddressRecord(
@@ -92,8 +92,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
92 final ObservableList<BitcoinAddressRecord> changeAddresses;
93 final ObservableList<BitcoinSilentPaymentAddressRecord> silentAddresses;
94 final BasedUtxoNetwork network;
95 - final bitcoin.HDWallet mainHd;
96 - final bitcoin.HDWallet sideHd;
95 + final Bip32Slip10Secp256k1 mainHd;
96 + final Bip32Slip10Secp256k1 sideHd;
97
98 @observable
99 SilentPaymentOwner? silentAddress;
@@ -318,7 +318,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
318 }
319
320 String getAddress(
321 - {required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) =>
321 + {required int index,
322 + required Bip32Slip10Secp256k1 hd,
323 + BitcoinAddressType? addressType}) =>
324 '';
325
326 @override
@@ -540,11 +542,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
542
543 void _validateAddresses() {
544 _addresses.forEach((element) {
543 - if (!element.isHidden && element.address !=
544 - getAddress(index: element.index, hd: mainHd, addressType: element.type)) {
545 + if (!element.isHidden &&
546 + element.address !=
547 + getAddress(index: element.index, hd: mainHd, addressType: element.type)) {
548 element.isHidden = true;
546 - } else if (element.isHidden && element.address !=
547 - getAddress(index: element.index, hd: sideHd, addressType: element.type)) {
549 + } else if (element.isHidden &&
550 + element.address !=
551 + getAddress(index: element.index, hd: sideHd, addressType: element.type)) {
552 element.isHidden = false;
553 }
554 });
@@ -562,7 +566,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
566 return _isAddressByType(addressRecord, addressPageType);
567 }
568
565 - bitcoin.HDWallet _getHd(bool isHidden) => isHidden ? sideHd : mainHd;
569 + Bip32Slip10Secp256k1 _getHd(bool isHidden) => isHidden ? sideHd : mainHd;
570 bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
571 bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) =>
572 !addr.isHidden && !addr.isUsed && addr.type == type;
cw_bitcoin/lib/litecoin_network.dart deleted
-9
@@ -1,9 +0,0 @@
1 -import 'package:bitcoin_flutter/bitcoin_flutter.dart';
2 -
3 -final litecoinNetwork = NetworkType(
4 - messagePrefix: '\x19Litecoin Signed Message:\n',
5 - bech32: 'ltc',
6 - bip32: Bip32Type(public: 0x0488b21e, private: 0x0488ade4),
7 - pubKeyHash: 0x30,
8 - scriptHash: 0x32,
9 - wif: 0xb0);
cw_bitcoin/lib/litecoin_wallet.dart
+3 -3
@@ -1,12 +1,12 @@
1 import 'package:bip39/bip39.dart' as bip39;
2 import 'package:bitcoin_base/bitcoin_base.dart';
3 +import 'package:blockchain_utils/blockchain_utils.dart';
4 import 'package:cw_bitcoin/bitcoin_address_record.dart';
5 import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
6 import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
7 import 'package:cw_bitcoin/electrum_balance.dart';
8 import 'package:cw_bitcoin/electrum_wallet.dart';
9 import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
9 -import 'package:cw_bitcoin/litecoin_network.dart';
10 import 'package:cw_bitcoin/litecoin_wallet_addresses.dart';
11 import 'package:cw_core/crypto_currency.dart';
12 import 'package:cw_core/transaction_priority.dart';
@@ -38,7 +38,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
38 password: password,
39 walletInfo: walletInfo,
40 unspentCoinsInfo: unspentCoinsInfo,
41 - networkType: litecoinNetwork,
41 + network: LitecoinNetwork.mainnet,
42 initialAddresses: initialAddresses,
43 initialBalance: initialBalance,
44 seedBytes: seedBytes,
@@ -49,7 +49,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
49 initialRegularAddressIndex: initialRegularAddressIndex,
50 initialChangeAddressIndex: initialChangeAddressIndex,
51 mainHd: hd,
52 - sideHd: accountHD.derive(1),
52 + sideHd: accountHD.childKey(Bip32KeyIndex(1)),
53 network: network,
54 );
55 autorun((_) {
cw_bitcoin/lib/litecoin_wallet_addresses.dart
+4 -2
@@ -1,5 +1,5 @@
1 import 'package:bitcoin_base/bitcoin_base.dart';
2 -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
2 +import 'package:blockchain_utils/blockchain_utils.dart';
3 import 'package:cw_bitcoin/utils.dart';
4 import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
5 import 'package:cw_core/wallet_info.dart';
@@ -22,6 +22,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
22
23 @override
24 String getAddress(
25 - {required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) =>
25 + {required int index,
26 + required Bip32Slip10Secp256k1 hd,
27 + BitcoinAddressType? addressType}) =>
28 generateP2WPKHAddress(hd: hd, index: index, network: network);
29 }
cw_bitcoin/lib/utils.dart
+29 -43
@@ -1,68 +1,54 @@
1 -import 'dart:typed_data';
1 import 'package:bitcoin_base/bitcoin_base.dart';
3 -import 'package:flutter/foundation.dart';
4 -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
5 -import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
6 -import 'package:hex/hex.dart';
7 -
8 -bitcoin.PaymentData generatePaymentData({
9 - required bitcoin.HDWallet hd,
10 - required int index,
11 -}) {
12 - final pubKey = hd.derive(index).pubKey!;
13 - return PaymentData(pubkey: Uint8List.fromList(HEX.decode(pubKey)));
14 -}
2 +import 'package:blockchain_utils/blockchain_utils.dart';
3
4 ECPrivate generateECPrivate({
17 - required bitcoin.HDWallet hd,
5 + required Bip32Slip10Secp256k1 hd,
6 required BasedUtxoNetwork network,
7 required int index,
20 -}) {
21 - final wif = hd.derive(index).wif!;
22 - return ECPrivate.fromWif(wif, netVersion: network.wifNetVer);
23 -}
8 +}) =>
9 + ECPrivate(hd.childKey(Bip32KeyIndex(index)).privateKey);
10
11 String generateP2WPKHAddress({
26 - required bitcoin.HDWallet hd,
12 + required Bip32Slip10Secp256k1 hd,
13 required BasedUtxoNetwork network,
14 required int index,
29 -}) {
30 - final pubKey = hd.derive(index).pubKey!;
31 - return ECPublic.fromHex(pubKey).toP2wpkhAddress().toAddress(network);
32 -}
15 +}) =>
16 + ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey)
17 + .toP2wpkhAddress()
18 + .toAddress(network);
19
20 String generateP2SHAddress({
35 - required bitcoin.HDWallet hd,
21 + required Bip32Slip10Secp256k1 hd,
22 required BasedUtxoNetwork network,
23 required int index,
38 -}) {
39 - final pubKey = hd.derive(index).pubKey!;
40 - return ECPublic.fromHex(pubKey).toP2wpkhInP2sh().toAddress(network);
41 -}
24 +}) =>
25 + ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey)
26 + .toP2wshInP2sh()
27 + .toAddress(network);
28
29 String generateP2WSHAddress({
44 - required bitcoin.HDWallet hd,
30 + required Bip32Slip10Secp256k1 hd,
31 required BasedUtxoNetwork network,
32 required int index,
47 -}) {
48 - final pubKey = hd.derive(index).pubKey!;
49 - return ECPublic.fromHex(pubKey).toP2wshAddress().toAddress(network);
50 -}
33 +}) =>
34 + ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey)
35 + .toP2wshAddress()
36 + .toAddress(network);
37
38 String generateP2PKHAddress({
53 - required bitcoin.HDWallet hd,
39 + required Bip32Slip10Secp256k1 hd,
40 required BasedUtxoNetwork network,
41 required int index,
56 -}) {
57 - final pubKey = hd.derive(index).pubKey!;
58 - return ECPublic.fromHex(pubKey).toP2pkhAddress().toAddress(network);
59 -}
42 +}) =>
43 + ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey)
44 + .toP2pkhAddress()
45 + .toAddress(network);
46
47 String generateP2TRAddress({
62 - required bitcoin.HDWallet hd,
48 + required Bip32Slip10Secp256k1 hd,
49 required BasedUtxoNetwork network,
50 required int index,
65 -}) {
66 - final pubKey = hd.derive(index).pubKey!;
67 - return ECPublic.fromHex(pubKey).toTaprootAddress().toAddress(network);
68 -}
51 +}) =>
52 + ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey)
53 + .toTaprootAddress()
54 + .toAddress(network);
cw_bitcoin/pubspec.lock
+27 -52
@@ -41,15 +41,6 @@ packages:
41 url: "https://pub.dev"
42 source: hosted
43 version: "2.11.0"
44 - bech32:
45 - dependency: transitive
46 - description:
47 - path: "."
48 - ref: "cake-0.2.2"
49 - resolved-ref: "05755063b593aa6cca0a4820a318e0ce17de6192"
50 - url: "https://github.com/cake-tech/bech32.git"
51 - source: git
52 - version: "0.2.2"
44 bip32:
45 dependency: transitive
46 description:
@@ -76,32 +67,23 @@ packages:
67 source: git
68 version: "1.0.1"
69 bitcoin_base:
79 - dependency: "direct main"
80 - description:
81 - path: "."
82 - ref: cake-update-v3
83 - resolved-ref: cc99eedb1d28ee9376dda0465ef72aa627ac6149
84 - url: "https://github.com/cake-tech/bitcoin_base"
85 - source: git
86 - version: "4.2.1"
87 - bitcoin_flutter:
70 dependency: "direct main"
71 description:
72 path: "."
73 ref: cake-update-v4
92 - resolved-ref: e19ffb7e7977278a75b27e0479b3c6f4034223b3
93 - url: "https://github.com/cake-tech/bitcoin_flutter.git"
74 + resolved-ref: "574486bfcdbbaf978dcd006b46fc8716f880da29"
75 + url: "https://github.com/cake-tech/bitcoin_base"
76 source: git
95 - version: "2.1.0"
77 + version: "4.7.0"
78 blockchain_utils:
79 dependency: "direct main"
80 description:
81 path: "."
100 - ref: cake-update-v1
101 - resolved-ref: cabd7e0e16c4da9920338c76eff3aeb8af0211f3
82 + ref: cake-update-v2
83 + resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
84 url: "https://github.com/cake-tech/blockchain_utils"
85 source: git
104 - version: "2.1.2"
86 + version: "3.3.0"
87 boolean_selector:
88 dependency: transitive
89 description:
@@ -411,10 +393,10 @@ packages:
393 dependency: "direct main"
394 description:
395 name: http
414 - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
396 + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
397 url: "https://pub.dev"
398 source: hosted
417 - version: "1.2.1"
399 + version: "1.2.2"
400 http_multi_server:
401 dependency: transitive
402 description:
@@ -499,11 +481,12 @@ packages:
481 ledger_flutter:
482 dependency: "direct main"
483 description:
502 - name: ledger_flutter
503 - sha256: f1680060ed6ff78f275837e0024ccaf667715a59ba7aa29fa7354bc7752e71c8
504 - url: "https://pub.dev"
505 - source: hosted
506 - version: "1.0.1"
484 + path: "."
485 + ref: cake-v3
486 + resolved-ref: "66469ff9dffe2417c70ae7287c9d76d2fe7157a4"
487 + url: "https://github.com/cake-tech/ledger-flutter.git"
488 + source: git
489 + version: "1.0.2"
490 ledger_usb:
491 dependency: transitive
492 description:
@@ -596,10 +579,10 @@ packages:
579 dependency: "direct main"
580 description:
581 name: path_provider
599 - sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
582 + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
583 url: "https://pub.dev"
584 source: hosted
602 - version: "2.1.3"
585 + version: "2.1.4"
586 path_provider_android:
587 dependency: transitive
588 description:
@@ -636,18 +619,18 @@ packages:
619 dependency: transitive
620 description:
621 name: path_provider_windows
639 - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
622 + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
623 url: "https://pub.dev"
624 source: hosted
642 - version: "2.2.1"
625 + version: "2.3.0"
626 platform:
627 dependency: transitive
628 description:
629 name: platform
647 - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
630 + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
631 url: "https://pub.dev"
632 source: hosted
650 - version: "3.1.4"
633 + version: "3.1.5"
634 plugin_platform_interface:
635 dependency: transitive
636 description:
@@ -700,10 +683,10 @@ packages:
683 dependency: transitive
684 description:
685 name: pubspec_parse
703 - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
686 + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
687 url: "https://pub.dev"
688 source: hosted
706 - version: "1.2.3"
689 + version: "1.3.0"
690 quiver:
691 dependency: transitive
692 description:
@@ -761,10 +744,10 @@ packages:
744 dependency: transitive
745 description:
746 name: socks5_proxy
764 - sha256: "045cbba84f6e2b01c1c77634a63e926352bf110ef5f07fc462c6d43bbd4b6a83"
747 + sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
748 url: "https://pub.dev"
749 source: hosted
767 - version: "1.0.5+dev.2"
750 + version: "1.0.6"
751 source_gen:
752 dependency: transitive
753 description:
@@ -793,9 +776,9 @@ packages:
776 dependency: "direct main"
777 description:
778 path: "."
796 - ref: "sp_v2.0.0"
797 - resolved-ref: "62c152b9086cd968019128845371072f7e1168de"
798 - url: "https://github.com/cake-tech/sp_scanner"
779 + ref: "sp_v4.0.0"
780 + resolved-ref: "3b8ae38592c0584f53560071dc18bc570758fe13"
781 + url: "https://github.com/rafael-xmr/sp_scanner"
782 source: git
783 version: "0.0.1"
784 stack_trace:
@@ -910,14 +893,6 @@ packages:
893 url: "https://pub.dev"
894 source: hosted
895 version: "2.4.5"
913 - win32:
914 - dependency: transitive
915 - description:
916 - name: win32
917 - sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
918 - url: "https://pub.dev"
919 - source: hosted
920 - version: "5.5.0"
896 xdg_directories:
897 dependency: transitive
898 description:
cw_bitcoin/pubspec.yaml
+4 -8
@@ -19,10 +19,6 @@ dependencies:
19 intl: ^0.18.0
20 cw_core:
21 path: ../cw_core
22 - bitcoin_flutter:
23 - git:
24 - url: https://github.com/cake-tech/bitcoin_flutter.git
25 - ref: cake-update-v4
22 bitbox:
23 git:
24 url: https://github.com/cake-tech/bitbox-flutter.git
@@ -32,19 +28,19 @@ dependencies:
28 bitcoin_base:
29 git:
30 url: https://github.com/cake-tech/bitcoin_base
35 - ref: cake-update-v3
31 + ref: cake-update-v4
32 blockchain_utils:
33 git:
34 url: https://github.com/cake-tech/blockchain_utils
39 - ref: cake-update-v1
35 + ref: cake-update-v2
36 ledger_flutter: ^1.0.1
37 ledger_bitcoin:
38 git:
39 url: https://github.com/cake-tech/ledger-bitcoin
40 sp_scanner:
41 git:
46 - url: https://github.com/cake-tech/sp_scanner
47 - ref: sp_v2.0.0
42 + url: https://github.com/rafael-xmr/sp_scanner
43 + ref: sp_v4.0.0
44
45
46 dev_dependencies:
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart
+15 -11
@@ -1,8 +1,6 @@
1 -import 'dart:convert';
2 -
1 import 'package:bitbox/bitbox.dart' as bitbox;
2 import 'package:bitcoin_base/bitcoin_base.dart';
5 -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
3 +import 'package:blockchain_utils/blockchain_utils.dart';
4 import 'package:cw_bitcoin/bitcoin_address_record.dart';
5 import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
6 import 'package:cw_bitcoin/electrum_balance.dart';
@@ -40,7 +38,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
38 password: password,
39 walletInfo: walletInfo,
40 unspentCoinsInfo: unspentCoinsInfo,
43 - networkType: bitcoin.bitcoin,
41 + network: BitcoinCashNetwork.mainnet,
42 initialAddresses: initialAddresses,
43 initialBalance: initialBalance,
44 seedBytes: seedBytes,
@@ -51,7 +49,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
49 initialRegularAddressIndex: initialRegularAddressIndex,
50 initialChangeAddressIndex: initialChangeAddressIndex,
51 mainHd: hd,
54 - sideHd: accountHD.derive(1),
52 + sideHd: accountHD.childKey(Bip32KeyIndex(1)),
53 network: network,
54 initialAddressPageType: addressPageType,
55 );
@@ -77,7 +75,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
75 unspentCoinsInfo: unspentCoinsInfo,
76 initialAddresses: initialAddresses,
77 initialBalance: initialBalance,
80 - seedBytes: await Mnemonic.toSeed(mnemonic),
78 + seedBytes: await MnemonicBip39.toSeed(mnemonic),
79 initialRegularAddressIndex: initialRegularAddressIndex,
80 initialChangeAddressIndex: initialChangeAddressIndex,
81 addressPageType: P2pkhAddressType.p2pkh,
@@ -136,15 +134,17 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
134 }
135 }).toList(),
136 initialBalance: snp?.balance,
139 - seedBytes: await Mnemonic.toSeed(keysData.mnemonic!),
137 + seedBytes: await MnemonicBip39.toSeed(keysData.mnemonic!),
138 initialRegularAddressIndex: snp?.regularAddressIndex,
139 initialChangeAddressIndex: snp?.changeAddressIndex,
140 addressPageType: P2pkhAddressType.p2pkh,
141 );
142 }
143
146 - bitbox.ECPair generateKeyPair({required bitcoin.HDWallet hd, required int index}) =>
147 - bitbox.ECPair.fromWIF(hd.derive(index).wif!);
144 + bitbox.ECPair generateKeyPair({required Bip32Slip10Secp256k1 hd, required int index}) =>
145 + bitbox.ECPair.fromPrivateKey(
146 + Uint8List.fromList(hd.childKey(Bip32KeyIndex(index)).privateKey.raw),
147 + );
148
149 int calculateEstimatedFeeWithFeeRate(int feeRate, int? amount, {int? outputsCount, int? size}) {
150 int inputsCount = 0;
@@ -190,7 +190,11 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
190 .firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
191 .index
192 : null;
193 - final HD = index == null ? hd : hd.derive(index);
194 - return base64Encode(HD.signMessage(message));
193 + final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index));
194 + final priv = ECPrivate.fromWif(
195 + WifEncoder.encode(HD.privateKey.raw, netVer: network.wifNetVer),
196 + netVersion: network.wifNetVer,
197 + );
198 + return priv.signMessage(StringUtils.encode(message));
199 }
200 }
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart
+4 -2
@@ -1,5 +1,5 @@
1 import 'package:bitcoin_base/bitcoin_base.dart';
2 -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
2 +import 'package:blockchain_utils/blockchain_utils.dart';
3 import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
4 import 'package:cw_bitcoin/utils.dart';
5 import 'package:cw_core/wallet_info.dart';
@@ -23,6 +23,8 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
23
24 @override
25 String getAddress(
26 - {required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) =>
26 + {required int index,
27 + required Bip32Slip10Secp256k1 hd,
28 + BitcoinAddressType? addressType}) =>
29 generateP2PKHAddress(hd: hd, index: index, network: network);
30 }
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart
+8 -4
@@ -11,8 +11,11 @@ import 'package:cw_core/wallet_type.dart';
11 import 'package:collection/collection.dart';
12 import 'package:hive/hive.dart';
13
14 -class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredentials,
15 - BitcoinCashRestoreWalletFromSeedCredentials, BitcoinCashRestoreWalletFromWIFCredentials, BitcoinCashNewWalletCredentials> {
14 +class BitcoinCashWalletService extends WalletService<
15 + BitcoinCashNewWalletCredentials,
16 + BitcoinCashRestoreWalletFromSeedCredentials,
17 + BitcoinCashRestoreWalletFromWIFCredentials,
18 + BitcoinCashNewWalletCredentials> {
19 BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
20
21 final Box<WalletInfo> walletInfoSource;
@@ -30,7 +33,7 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
33 final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
34
35 final wallet = await BitcoinCashWalletBase.create(
33 - mnemonic: await Mnemonic.generate(strength: strength),
36 + mnemonic: await MnemonicBip39.generate(strength: strength),
37 password: credentials.password!,
38 walletInfo: credentials.walletInfo!,
39 unspentCoinsInfo: unspentCoinsInfoSource);
@@ -97,7 +100,8 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
100
101 @override
102 Future<BitcoinCashWallet> restoreFromHardwareWallet(BitcoinCashNewWalletCredentials credentials) {
100 - throw UnimplementedError("Restoring a Bitcoin Cash wallet from a hardware wallet is not yet supported!");
103 + throw UnimplementedError(
104 + "Restoring a Bitcoin Cash wallet from a hardware wallet is not yet supported!");
105 }
106
107 @override
cw_bitcoin_cash/lib/src/mnemonic.dart
+1 -1
@@ -2,7 +2,7 @@ import 'dart:typed_data';
2
3 import 'package:bip39/bip39.dart' as bip39;
4
5 -class Mnemonic {
5 +class MnemonicBip39 {
6 /// Generate bip39 mnemonic
7 static String generate({int strength = 128}) => bip39.generateMnemonic(strength: strength);
8
cw_bitcoin_cash/pubspec.yaml
+2 -6
@@ -21,10 +21,6 @@ dependencies:
21 path: ../cw_core
22 cw_bitcoin:
23 path: ../cw_bitcoin
24 - bitcoin_flutter:
25 - git:
26 - url: https://github.com/cake-tech/bitcoin_flutter.git
27 - ref: cake-update-v4
24 bitbox:
25 git:
26 url: https://github.com/cake-tech/bitbox-flutter.git
@@ -32,11 +28,11 @@ dependencies:
28 bitcoin_base:
29 git:
30 url: https://github.com/cake-tech/bitcoin_base
35 - ref: cake-update-v3
31 + ref: cake-update-v4
32 blockchain_utils:
33 git:
34 url: https://github.com/cake-tech/blockchain_utils
39 - ref: cake-update-v1
35 + ref: cake-update-v2
36
37 dev_dependencies:
38 flutter_test:
cw_core/lib/get_height_by_date.dart
+5 -2
@@ -245,6 +245,8 @@ Future<int> getHavenCurrentHeight() async {
245
246 // Data taken from https://timechaincalendar.com/
247 const bitcoinDates = {
248 + "2024-08": 854889,
249 + "2024-07": 850182,
250 "2024-06": 846005,
251 "2024-05": 841590,
252 "2024-04": 837182,
@@ -371,7 +373,8 @@ const wowDates = {
373
374 int getWowneroHeightByDate({required DateTime date}) {
375 String closestKey =
374 - wowDates.keys.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => '');
376 + wowDates.keys.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => '');
377
378 return wowDates[closestKey] ?? 0;
377 -}
\ No newline at end of file
379 +}
380 +
cw_core/lib/node.dart
+8 -1
@@ -11,7 +11,8 @@ import 'package:http/io_client.dart' as ioc;
11
12 part 'node.g.dart';
13
14 -Uri createUriFromElectrumAddress(String address, String path) => Uri.tryParse('tcp://$address$path')!;
14 +Uri createUriFromElectrumAddress(String address, String path) =>
15 + Uri.tryParse('tcp://$address$path')!;
16
17 @HiveType(typeId: Node.typeId)
18 class Node extends HiveObject with Keyable {
@@ -72,6 +73,12 @@ class Node extends HiveObject with Keyable {
73 @HiveField(7, defaultValue: '')
74 String? path;
75
76 + @HiveField(8)
77 + bool? isElectrs;
78 +
79 + @HiveField(9)
80 + bool? supportsSilentPayments;
81 +
82 bool get isSSL => useSSL ?? false;
83
84 bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty;
cw_core/lib/sync_status.dart
+5
@@ -3,6 +3,11 @@ abstract class SyncStatus {
3 double progress();
4 }
5
6 +class StartingScanSyncStatus extends SyncStatus {
7 + @override
8 + double progress() => 0.0;
9 +}
10 +
11 class SyncingSyncStatus extends SyncStatus {
12 SyncingSyncStatus(this.blocksLeft, this.ptc);
13
cw_core/lib/transaction_info.dart
+3 -2
@@ -9,7 +9,7 @@ abstract class TransactionInfo extends Object with Keyable {
9 late TransactionDirection direction;
10 late bool isPending;
11 late DateTime date;
12 - late int height;
12 + int? height;
13 late int confirmations;
14 String amountFormatted();
15 String fiatAmount();
@@ -25,4 +25,5 @@ abstract class TransactionInfo extends Object with Keyable {
25 dynamic get keyIndex => id;
26
27 late Map<String, dynamic> additionalInfo;
28 -}
\ No newline at end of file
28 +}
29 +
cw_haven/pubspec.lock
+4 -12
@@ -254,10 +254,10 @@ packages:
254 dependency: transitive
255 description:
256 name: glob
257 - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
257 + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
258 url: "https://pub.dev"
259 source: hosted
260 - version: "2.1.1"
260 + version: "2.1.2"
261 graphs:
262 dependency: transitive
263 description:
@@ -514,14 +514,6 @@ packages:
514 url: "https://pub.dev"
515 source: hosted
516 version: "1.5.1"
517 - process:
518 - dependency: transitive
519 - description:
520 - name: process
521 - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
522 - url: "https://pub.dev"
523 - source: hosted
524 - version: "4.2.4"
517 pub_semver:
518 dependency: transitive
519 description:
@@ -707,10 +699,10 @@ packages:
699 dependency: transitive
700 description:
701 name: xdg_directories
710 - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
702 + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
703 url: "https://pub.dev"
704 source: hosted
713 - version: "0.2.0+3"
705 + version: "1.0.4"
706 yaml:
707 dependency: transitive
708 description:
cw_monero/pubspec.lock
+2 -2
@@ -438,8 +438,8 @@ packages:
438 dependency: "direct main"
439 description:
440 path: "impls/monero.dart"
441 - ref: "bcb328a4956105dc182afd0ce2e48fe263f5f20b"
442 - resolved-ref: "bcb328a4956105dc182afd0ce2e48fe263f5f20b"
441 + ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
442 + resolved-ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
443 url: "https://github.com/mrcyjanek/monero_c"
444 source: git
445 version: "0.0.0"
cw_nano/pubspec.lock
+27 -27
@@ -29,10 +29,10 @@ packages:
29 dependency: transitive
30 description:
31 name: asn1lib
32 - sha256: b74e3842a52c61f8819a1ec8444b4de5419b41a7465e69d4aa681445377398b0
32 + sha256: "58082b3f0dca697204dbab0ef9ff208bfaea7767ea771076af9a343488428dda"
33 url: "https://pub.dev"
34 source: hosted
35 - version: "1.4.1"
35 + version: "1.5.3"
36 async:
37 dependency: transitive
38 description:
@@ -114,7 +114,7 @@ packages:
114 source: hosted
115 version: "2.4.9"
116 build_runner_core:
117 - dependency: transitive
117 + dependency: "direct overridden"
118 description:
119 name: build_runner_core
120 sha256: "0671ad4162ed510b70d0eb4ad6354c249f8429cab4ae7a4cec86bbc2886eb76e"
@@ -133,10 +133,10 @@ packages:
133 dependency: transitive
134 description:
135 name: built_value
136 - sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166"
136 + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
137 url: "https://pub.dev"
138 source: hosted
139 - version: "8.6.1"
139 + version: "8.9.2"
140 characters:
141 dependency: transitive
142 description:
@@ -165,10 +165,10 @@ packages:
165 dependency: transitive
166 description:
167 name: code_builder
168 - sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189"
168 + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
169 url: "https://pub.dev"
170 source: hosted
171 - version: "4.5.0"
171 + version: "4.10.0"
172 collection:
173 dependency: transitive
174 description:
@@ -220,10 +220,10 @@ packages:
220 dependency: "direct main"
221 description:
222 name: ed25519_hd_key
223 - sha256: "326608234e986ea826a5db4cf4cd6826058d860875a3fff7926c0725fe1a604d"
223 + sha256: c5c9f11a03f5789bf9dcd9ae88d641571c802640851f1cacdb13123f171b3a26
224 url: "https://pub.dev"
225 source: hosted
226 - version: "2.2.0"
226 + version: "2.2.1"
227 encrypt:
228 dependency: transitive
229 description:
@@ -244,10 +244,10 @@ packages:
244 dependency: transitive
245 description:
246 name: ffi
247 - sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
247 + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
248 url: "https://pub.dev"
249 source: hosted
250 - version: "2.0.2"
250 + version: "2.1.2"
251 file:
252 dependency: transitive
253 description:
@@ -475,10 +475,10 @@ packages:
475 dependency: "direct main"
476 description:
477 name: mobx
478 - sha256: "0afcf88b3ee9d6819890bf16c11a727fc8c62cf736fda8e5d3b9b4eace4e62ea"
478 + sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e"
479 url: "https://pub.dev"
480 source: hosted
481 - version: "2.2.0"
481 + version: "2.3.3+2"
482 mobx_codegen:
483 dependency: "direct dev"
484 description:
@@ -572,10 +572,10 @@ packages:
572 dependency: transitive
573 description:
574 name: pinenacl
575 - sha256: e5fb0bce1717b7f136f35ee98b5c02b3e6383211f8a77ca882fa7812232a07b9
575 + sha256: "3a5503637587d635647c93ea9a8fecf48a420cc7deebe6f1fc85c2a5637ab327"
576 url: "https://pub.dev"
577 source: hosted
578 - version: "0.3.4"
578 + version: "0.5.1"
579 platform:
580 dependency: transitive
581 description:
@@ -588,10 +588,10 @@ packages:
588 dependency: transitive
589 description:
590 name: plugin_platform_interface
591 - sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd"
591 + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
592 url: "https://pub.dev"
593 source: hosted
594 - version: "2.1.5"
594 + version: "2.1.8"
595 pointycastle:
596 dependency: transitive
597 description:
@@ -652,10 +652,10 @@ packages:
652 dependency: transitive
653 description:
654 name: shared_preferences_foundation
655 - sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
655 + sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833"
656 url: "https://pub.dev"
657 source: hosted
658 - version: "2.3.4"
658 + version: "2.5.0"
659 shared_preferences_linux:
660 dependency: transitive
661 description:
@@ -668,10 +668,10 @@ packages:
668 dependency: transitive
669 description:
670 name: shared_preferences_platform_interface
671 - sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
671 + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
672 url: "https://pub.dev"
673 source: hosted
674 - version: "2.3.1"
674 + version: "2.4.1"
675 shared_preferences_web:
676 dependency: transitive
677 description:
@@ -849,18 +849,18 @@ packages:
849 dependency: transitive
850 description:
851 name: win32
852 - sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c"
852 + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
853 url: "https://pub.dev"
854 source: hosted
855 - version: "4.1.4"
855 + version: "5.5.0"
856 xdg_directories:
857 dependency: transitive
858 description:
859 name: xdg_directories
860 - sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff
860 + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
861 url: "https://pub.dev"
862 source: hosted
863 - version: "1.0.1"
863 + version: "1.0.4"
864 yaml:
865 dependency: transitive
866 description:
@@ -870,5 +870,5 @@ packages:
870 source: hosted
871 version: "3.1.2"
872 sdks:
873 - dart: ">=3.2.0-0 <4.0.0"
874 - flutter: ">=3.7.0"
873 + dart: ">=3.3.0 <4.0.0"
874 + flutter: ">=3.16.6"
cw_nano/pubspec.yaml
+1
@@ -38,6 +38,7 @@ dev_dependencies:
38
39 dependency_overrides:
40 watcher: ^1.1.0
41 + build_runner_core: 7.2.7+1
42
43 # For information on the generic Dart part of this file, see the
44 # following page: https://dart.dev/tools/pub/pubspec
cw_tron/pubspec.yaml
+2 -2
@@ -18,11 +18,11 @@ dependencies:
18 on_chain:
19 git:
20 url: https://github.com/cake-tech/On_chain
21 - ref: cake-update-v1
21 + ref: cake-update-v2
22 blockchain_utils:
23 git:
24 url: https://github.com/cake-tech/blockchain_utils
25 - ref: cake-update-v1
25 + ref: cake-update-v2
26 mobx: ^2.3.0+1
27 bip39: ^1.0.6
28 hive: ^2.2.3
cw_wownero/pubspec.lock
+6 -14
@@ -254,10 +254,10 @@ packages:
254 dependency: transitive
255 description:
256 name: glob
257 - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
257 + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
258 url: "https://pub.dev"
259 source: hosted
260 - version: "2.1.1"
260 + version: "2.1.2"
261 graphs:
262 dependency: transitive
263 description:
@@ -438,8 +438,8 @@ packages:
438 dependency: "direct main"
439 description:
440 path: "impls/monero.dart"
441 - ref: "bcb328a4956105dc182afd0ce2e48fe263f5f20b"
442 - resolved-ref: "bcb328a4956105dc182afd0ce2e48fe263f5f20b"
441 + ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
442 + resolved-ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
443 url: "https://github.com/mrcyjanek/monero_c"
444 source: git
445 version: "0.0.0"
@@ -555,14 +555,6 @@ packages:
555 url: "https://pub.dev"
556 source: hosted
557 version: "1.5.1"
558 - process:
559 - dependency: transitive
560 - description:
561 - name: process
562 - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
563 - url: "https://pub.dev"
564 - source: hosted
565 - version: "4.2.4"
558 pub_semver:
559 dependency: transitive
560 description:
@@ -748,10 +740,10 @@ packages:
740 dependency: transitive
741 description:
742 name: xdg_directories
751 - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
743 + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
744 url: "https://pub.dev"
745 source: hosted
754 - version: "0.2.0+3"
746 + version: "1.0.4"
747 yaml:
748 dependency: transitive
749 description:
ios/Podfile.lock
+9 -3
@@ -94,6 +94,8 @@ PODS:
94 - shared_preferences_foundation (0.0.1):
95 - Flutter
96 - FlutterMacOS
97 + - sp_scanner (0.0.1):
98 + - Flutter
99 - SwiftProtobuf (1.26.0)
100 - SwiftyGif (5.4.5)
101 - Toast (4.1.1)
@@ -132,6 +134,7 @@ DEPENDENCIES:
134 - sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
135 - share_plus (from `.symlinks/plugins/share_plus/ios`)
136 - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
137 + - sp_scanner (from `.symlinks/plugins/sp_scanner/ios`)
138 - uni_links (from `.symlinks/plugins/uni_links/ios`)
139 - UnstoppableDomainsResolution (~> 4.0.0)
140 - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@@ -197,6 +200,8 @@ EXTERNAL SOURCES:
200 :path: ".symlinks/plugins/share_plus/ios"
201 shared_preferences_foundation:
202 :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
203 + sp_scanner:
204 + :path: ".symlinks/plugins/sp_scanner/ios"
205 uni_links:
206 :path: ".symlinks/plugins/uni_links/ios"
207 url_launcher_ios:
@@ -227,7 +232,7 @@ SPEC CHECKSUMS:
232 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
233 OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
234 package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
230 - package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
235 + package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
236 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
237 permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
238 Protobuf: fb2c13674723f76ff6eede14f78847a776455fa2
@@ -235,15 +240,16 @@ SPEC CHECKSUMS:
240 reactive_ble_mobile: 9ce6723d37ccf701dbffd202d487f23f5de03b4c
241 SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d
242 sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
238 - share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
243 + share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
244 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
245 + sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12
246 SwiftProtobuf: 5e8349171e7c2f88f5b9e683cb3cb79d1dc780b3
247 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
248 Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
249 uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
250 UnstoppableDomainsResolution: c3c67f4d0a5e2437cb00d4bd50c2e00d6e743841
251 url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
246 - wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
252 + wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
253 workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
254
255 PODFILE CHECKSUM: a2fe518be61cdbdc5b0e2da085ab543d556af2d3
lib/bitcoin/cw_bitcoin.dart
+4 -46
@@ -302,16 +302,13 @@ class CWBitcoin extends Bitcoin {
302 await electrumClient.connectToUri(node.uri, useSSL: node.useSSL);
303
304 late BasedUtxoNetwork network;
305 - btc.NetworkType networkType;
305 switch (node.type) {
306 case WalletType.litecoin:
307 network = LitecoinNetwork.mainnet;
309 - networkType = litecoinNetwork;
308 break;
309 case WalletType.bitcoin:
310 default:
311 network = BitcoinNetwork.mainnet;
314 - networkType = btc.bitcoin;
312 break;
313 }
314
@@ -341,10 +338,8 @@ class CWBitcoin extends Bitcoin {
338 balancePath += "/0";
339 }
340
344 - final hd = btc.HDWallet.fromSeed(
345 - seedBytes,
346 - network: networkType,
347 - ).derivePath(balancePath);
341 + final hd = Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath(balancePath)
342 + as Bip32Slip10Secp256k1;
343
344 // derive address at index 0:
345 String? address;
@@ -515,10 +510,7 @@ class CWBitcoin extends Bitcoin {
510 @override
511 Future<void> setScanningActive(Object wallet, bool active) async {
512 final bitcoinWallet = wallet as ElectrumWallet;
518 - bitcoinWallet.setSilentPaymentsScanning(
519 - active,
520 - active && (await getNodeIsElectrsSPEnabled(wallet)),
521 - );
513 + bitcoinWallet.setSilentPaymentsScanning(active);
514 }
515
516 @override
@@ -536,44 +528,10 @@ class CWBitcoin extends Bitcoin {
528 bitcoinWallet.rescan(height: height, doSingleScan: doSingleScan);
529 }
530
539 - Future<bool> getNodeIsElectrs(Object wallet) async {
540 - final bitcoinWallet = wallet as ElectrumWallet;
541 -
542 - final version = await bitcoinWallet.electrumClient.version();
543 -
544 - if (version.isEmpty) {
545 - return false;
546 - }
547 -
548 - final server = version[0];
549 -
550 - if (server.toLowerCase().contains('electrs')) {
551 - return true;
552 - }
553 -
554 - return false;
555 - }
556 -
531 @override
532 Future<bool> getNodeIsElectrsSPEnabled(Object wallet) async {
559 - if (!(await getNodeIsElectrs(wallet))) {
560 - return false;
561 - }
562 -
533 final bitcoinWallet = wallet as ElectrumWallet;
564 - try {
565 - final tweaksResponse = await bitcoinWallet.electrumClient.getTweaks(height: 0);
566 -
567 - if (tweaksResponse != null) {
568 - return true;
569 - }
570 - } on RequestFailedTimeoutException catch (_) {
571 - return false;
572 - } catch (_) {
573 - rethrow;
574 - }
575 -
576 - return false;
534 + return bitcoinWallet.getNodeSupportsSilentPayments();
535 }
536
537 @override
lib/core/sync_status_title.dart
+4
@@ -52,5 +52,9 @@ String syncStatusTitle(SyncStatus syncStatus) {
52 return S.current.sync_status_syncronizing;
53 }
54
55 + if (syncStatus is StartingScanSyncStatus) {
56 + return S.current.sync_status_starting_scan;
57 + }
58 +
59 return '';
60 }
lib/src/screens/cake_pay/cards/cake_pay_confirm_purchase_card_page.dart
+26 -22
@@ -34,7 +34,7 @@ class CakePayBuyCardDetailPage extends BasePage {
34
35 @override
36 Widget? middle(BuildContext context) {
37 - return Text(
37 + return Text(
38 title,
39 textAlign: TextAlign.center,
40 maxLines: 2,
@@ -359,7 +359,7 @@ class CakePayBuyCardDetailPage extends BasePage {
359 reaction((_) => cakePayPurchaseViewModel.sendViewModel.state, (ExecutionState state) {
360 if (state is FailureState) {
361 WidgetsBinding.instance.addPostFrameCallback((_) {
362 - showStateAlert(context, S.of(context).error, state.error);
362 + if (context.mounted) showStateAlert(context, S.of(context).error, state.error);
363 });
364 }
365
@@ -381,31 +381,35 @@ class CakePayBuyCardDetailPage extends BasePage {
381 }
382
383 void showStateAlert(BuildContext context, String title, String content) {
384 - showPopUp<void>(
385 - context: context,
386 - builder: (BuildContext context) {
387 - return AlertWithOneAction(
388 - alertTitle: title,
389 - alertContent: content,
390 - buttonText: S.of(context).ok,
391 - buttonAction: () => Navigator.of(context).pop());
392 - });
384 + if (context.mounted) {
385 + showPopUp<void>(
386 + context: context,
387 + builder: (BuildContext context) {
388 + return AlertWithOneAction(
389 + alertTitle: title,
390 + alertContent: content,
391 + buttonText: S.of(context).ok,
392 + buttonAction: () => Navigator.of(context).pop());
393 + });
394 + }
395 }
396
397 Future<void> showSentAlert(BuildContext context) async {
398 + if (!context.mounted) {
399 + return;
400 + }
401 final order = cakePayPurchaseViewModel.order!.orderId;
402 final isCopy = await showPopUp<bool>(
398 - context: context,
399 - builder: (BuildContext context) {
400 - return AlertWithTwoActions(
401 - alertTitle: S.of(context).transaction_sent,
402 - alertContent:
403 - S.of(context).cake_pay_save_order + '\n${order}',
404 - leftButtonText: S.of(context).ignor,
405 - rightButtonText: S.of(context).copy,
406 - actionLeftButton: () => Navigator.of(context).pop(false),
407 - actionRightButton: () => Navigator.of(context).pop(true));
408 - }) ??
403 + context: context,
404 + builder: (BuildContext context) {
405 + return AlertWithTwoActions(
406 + alertTitle: S.of(context).transaction_sent,
407 + alertContent: S.of(context).cake_pay_save_order + '\n${order}',
408 + leftButtonText: S.of(context).ignor,
409 + rightButtonText: S.of(context).copy,
410 + actionLeftButton: () => Navigator.of(context).pop(false),
411 + actionRightButton: () => Navigator.of(context).pop(true));
412 + }) ??
413 false;
414
415 if (isCopy) {
pubspec_base.yaml
+1 -5
@@ -87,10 +87,6 @@ dependencies:
87 git:
88 url: https://github.com/cake-tech/ens_dart.git
89 ref: main
90 - bitcoin_flutter:
91 - git:
92 - url: https://github.com/cake-tech/bitcoin_flutter.git
93 - ref: cake-update-v4
90 fluttertoast: 8.1.4
91 # tor:
92 # git:
@@ -104,7 +100,7 @@ dependencies:
100 bitcoin_base:
101 git:
102 url: https://github.com/cake-tech/bitcoin_base
107 - ref: cake-update-v3
103 + ref: cake-update-v4
104 ledger_flutter: ^1.0.1
105 hashlib: 1.12.0
106
res/values/strings_ar.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "يتم التوصيل",
696 "sync_status_failed_connect": "انقطع الاتصال",
697 "sync_status_not_connected": "غير متصل",
698 + "sync_status_starting_scan": "بدء المسح",
699 "sync_status_starting_sync": "بدء المزامنة",
700 "sync_status_syncronized": "متزامن",
701 "sync_status_syncronizing": "يتم المزامنة",
res/values/strings_bg.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "СВЪРЗВАНЕ",
696 "sync_status_failed_connect": "НЕУСПЕШНО СВЪРЗВАНЕ",
697 "sync_status_not_connected": "НЯМА ВРЪЗКА",
698 + "sync_status_starting_scan": "Стартово сканиране",
699 "sync_status_starting_sync": "ЗАПОЧВАНЕ НА СИНХРОНИЗАЦИЯ",
700 "sync_status_syncronized": "СИНХРОНИЗИРАНО",
701 "sync_status_syncronizing": "СИНХРОНИЗИРАНЕ",
res/values/strings_cs.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "PŘIPOJOVÁNÍ",
696 "sync_status_failed_connect": "ODPOJENO",
697 "sync_status_not_connected": "NEPŘIPOJENO",
698 + "sync_status_starting_scan": "Počáteční skenování",
699 "sync_status_starting_sync": "SPOUŠTĚNÍ SYNCHRONIZACE",
700 "sync_status_syncronized": "SYNCHRONIZOVÁNO",
701 "sync_status_syncronizing": "SYNCHRONIZUJI",
res/values/strings_de.arb
+1
@@ -696,6 +696,7 @@
696 "sync_status_connecting": "VERBINDEN",
697 "sync_status_failed_connect": "GETRENNT",
698 "sync_status_not_connected": "NICHT VERBUNDEN",
699 + "sync_status_starting_scan": "Scan beginnen",
700 "sync_status_starting_sync": "STARTE SYNCHRONISIERUNG",
701 "sync_status_syncronized": "SYNCHRONISIERT",
702 "sync_status_syncronizing": "SYNCHRONISIERE",
res/values/strings_en.arb
+1
@@ -696,6 +696,7 @@
696 "sync_status_connecting": "CONNECTING",
697 "sync_status_failed_connect": "DISCONNECTED",
698 "sync_status_not_connected": "NOT CONNECTED",
699 + "sync_status_starting_scan": "STARTING SCAN",
700 "sync_status_starting_sync": "STARTING SYNC",
701 "sync_status_syncronized": "SYNCHRONIZED",
702 "sync_status_syncronizing": "SYNCHRONIZING",
res/values/strings_es.arb
+1
@@ -696,6 +696,7 @@
696 "sync_status_connecting": "CONECTANDO",
697 "sync_status_failed_connect": "DESCONECTADO",
698 "sync_status_not_connected": "NO CONECTADO",
699 + "sync_status_starting_scan": "Escaneo inicial",
700 "sync_status_starting_sync": "EMPEZANDO A SINCRONIZAR",
701 "sync_status_syncronized": "SINCRONIZADO",
702 "sync_status_syncronizing": "SINCRONIZANDO",
res/values/strings_fr.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "CONNEXION EN COURS",
696 "sync_status_failed_connect": "DÉCONNECTÉ",
697 "sync_status_not_connected": "NON CONNECTÉ",
698 + "sync_status_starting_scan": "Démarrage",
699 "sync_status_starting_sync": "DÉBUT DE SYNCHRO",
700 "sync_status_syncronized": "SYNCHRONISÉ",
701 "sync_status_syncronizing": "SYNCHRONISATION EN COURS",
res/values/strings_ha.arb
+1
@@ -697,6 +697,7 @@
697 "sync_status_connecting": "HADA",
698 "sync_status_failed_connect": "BABU INTERNET",
699 "sync_status_not_connected": "BABU INTERNET",
700 + "sync_status_starting_scan": "Fara scan",
701 "sync_status_starting_sync": "KWAFI",
702 "sync_status_syncronized": "KYAU",
703 "sync_status_syncronizing": "KWAFI",
res/values/strings_hi.arb
+1
@@ -697,6 +697,7 @@
697 "sync_status_connecting": "कनेक्ट",
698 "sync_status_failed_connect": "डिस्कनेक्ट किया गया",
699 "sync_status_not_connected": "जुड़े नहीं हैं",
700 + "sync_status_starting_scan": "स्कैन शुरू करना",
701 "sync_status_starting_sync": "सिताज़ा करना",
702 "sync_status_syncronized": "सिंक्रनाइज़",
703 "sync_status_syncronizing": "सिंक्रनाइज़ करने",
res/values/strings_hr.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "SPAJANJE",
696 "sync_status_failed_connect": "ISKLJUČENO",
697 "sync_status_not_connected": "NIJE POVEZANO",
698 + "sync_status_starting_scan": "Početno skeniranje",
699 "sync_status_starting_sync": "ZAPOČINJEMO SINKRONIZIRANJE",
700 "sync_status_syncronized": "SINKRONIZIRANO",
701 "sync_status_syncronizing": "SINKRONIZIRANJE",
res/values/strings_id.arb
+1
@@ -698,6 +698,7 @@
698 "sync_status_connecting": "MENGHUBUNGKAN",
699 "sync_status_failed_connect": "GAGAL TERHUBUNG",
700 "sync_status_not_connected": "TIDAK TERHUBUNG",
701 + "sync_status_starting_scan": "Mulai pindai",
702 "sync_status_starting_sync": "MULAI SINKRONISASI",
703 "sync_status_syncronized": "SUDAH TERSINKRONISASI",
704 "sync_status_syncronizing": "SEDANG SINKRONISASI",
res/values/strings_it.arb
+1
@@ -697,6 +697,7 @@
697 "sync_status_connecting": "CONNESSIONE",
698 "sync_status_failed_connect": "DISCONNESSO",
699 "sync_status_not_connected": "NON CONNESSO",
700 + "sync_status_starting_scan": "Scansione di partenza",
701 "sync_status_starting_sync": "INIZIO SINC",
702 "sync_status_syncronized": "SINCRONIZZATO",
703 "sync_status_syncronizing": "SINCRONIZZAZIONE",
res/values/strings_ja.arb
+1
@@ -696,6 +696,7 @@
696 "sync_status_connecting": "接続中",
697 "sync_status_failed_connect": "切断されました",
698 "sync_status_not_connected": "接続されていません",
699 + "sync_status_starting_scan": "スキャンを開始します",
700 "sync_status_starting_sync": "同期の開始",
701 "sync_status_syncronized": "同期された",
702 "sync_status_syncronizing": "同期",
res/values/strings_ko.arb
+1
@@ -696,6 +696,7 @@
696 "sync_status_connecting": "연결 중",
697 "sync_status_failed_connect": "연결 해제",
698 "sync_status_not_connected": "연결되지 않은",
699 + "sync_status_starting_scan": "스캔 시작",
700 "sync_status_starting_sync": "동기화 시작",
701 "sync_status_syncronized": "동기화",
702 "sync_status_syncronizing": "동기화",
res/values/strings_my.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "ချိတ်ဆက်ခြင်း။",
696 "sync_status_failed_connect": "အဆက်အသွယ်ဖြတ်ထားသည်။",
697 "sync_status_not_connected": "မချိတ်ဆက်ပါ။",
698 + "sync_status_starting_scan": "စကင်ဖတ်စစ်ဆေးမှု",
699 "sync_status_starting_sync": "စင့်ခ်လုပ်ခြင်း။",
700 "sync_status_syncronized": "ထပ်တူပြုထားသည်။",
701 "sync_status_syncronizing": "ထပ်တူပြုခြင်း။",
res/values/strings_nl.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "AANSLUITING",
696 "sync_status_failed_connect": "LOSGEKOPPELD",
697 "sync_status_not_connected": "NIET VERBONDEN",
698 + "sync_status_starting_scan": "Startscan",
699 "sync_status_starting_sync": "BEGINNEN MET SYNCHRONISEREN",
700 "sync_status_syncronized": "SYNCHRONIZED",
701 "sync_status_syncronizing": "SYNCHRONISEREN",
res/values/strings_pl.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "ŁĄCZENIE",
696 "sync_status_failed_connect": "POŁĄCZENIE NIEUDANE",
697 "sync_status_not_connected": "NIE POŁĄCZONY",
698 + "sync_status_starting_scan": "Rozpoczęcie skanowania",
699 "sync_status_starting_sync": "ROZPOCZĘCIE SYNCHRONIZACJI",
700 "sync_status_syncronized": "ZSYNCHRONIZOWANO",
701 "sync_status_syncronizing": "SYNCHRONIZACJA",
res/values/strings_pt.arb
+1
@@ -697,6 +697,7 @@
697 "sync_status_connecting": "CONECTANDO",
698 "sync_status_failed_connect": "DESCONECTADO",
699 "sync_status_not_connected": "DESCONECTADO",
700 + "sync_status_starting_scan": "Diretor inicial",
701 "sync_status_starting_sync": "INICIANDO SINCRONIZAÇÃO",
702 "sync_status_syncronized": "SINCRONIZADO",
703 "sync_status_syncronizing": "SINCRONIZANDO",
res/values/strings_ru.arb
+1
@@ -696,6 +696,7 @@
696 "sync_status_connecting": "ПОДКЛЮЧЕНИЕ",
697 "sync_status_failed_connect": "ОТКЛЮЧЕНО",
698 "sync_status_not_connected": "НЕ ПОДКЛЮЧЁН",
699 + "sync_status_starting_scan": "Начальное сканирование",
700 "sync_status_starting_sync": "НАЧАЛО СИНХРОНИЗАЦИИ",
701 "sync_status_syncronized": "СИНХРОНИЗИРОВАН",
702 "sync_status_syncronizing": "СИНХРОНИЗАЦИЯ",
res/values/strings_th.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "กำลังเชื่อมต่อ",
696 "sync_status_failed_connect": "การเชื่อมต่อล้มเหลว",
697 "sync_status_not_connected": "ไม่ได้เชื่อมต่อ",
698 + "sync_status_starting_scan": "เริ่มการสแกน",
699 "sync_status_starting_sync": "กำลังเริ่มซิงโครไนซ์",
700 "sync_status_syncronized": "ซิงโครไนซ์แล้ว",
701 "sync_status_syncronizing": "กำลังซิงโครไนซ์",
res/values/strings_tl.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "Pagkonekta",
696 "sync_status_failed_connect": "Naka -disconnect",
697 "sync_status_not_connected": "HINDI KONEKTADO",
698 + "sync_status_starting_scan": "Simula sa pag -scan",
699 "sync_status_starting_sync": "Simula sa pag -sync",
700 "sync_status_syncronized": "Naka -synchronize",
701 "sync_status_syncronizing": "Pag -synchronize",
res/values/strings_tr.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "BAĞLANILIYOR",
696 "sync_status_failed_connect": "BAĞLANTI KESİLDİ",
697 "sync_status_not_connected": "BAĞLI DEĞİL",
698 + "sync_status_starting_scan": "Başlangıç ​​taraması",
699 "sync_status_starting_sync": "SENKRONİZE BAŞLATILIYOR",
700 "sync_status_syncronized": "SENKRONİZE EDİLDİ",
701 "sync_status_syncronizing": "SENKRONİZE EDİLİYOR",
res/values/strings_uk.arb
+1
@@ -696,6 +696,7 @@
696 "sync_status_connecting": "ПІДКЛЮЧЕННЯ",
697 "sync_status_failed_connect": "ВІДКЛЮЧЕНО",
698 "sync_status_not_connected": "НЕ ПІДКЛЮЧЕННИЙ",
699 + "sync_status_starting_scan": "Початок сканування",
700 "sync_status_starting_sync": "ПОЧАТОК СИНХРОНІЗАЦІЇ",
701 "sync_status_syncronized": "СИНХРОНІЗОВАНИЙ",
702 "sync_status_syncronizing": "СИНХРОНІЗАЦІЯ",
res/values/strings_ur.arb
+1
@@ -697,6 +697,7 @@
697 "sync_status_connecting": "جڑ رہا ہے۔",
698 "sync_status_failed_connect": "منقطع",
699 "sync_status_not_connected": "منسلک نہیں",
700 + "sync_status_starting_scan": "اسکین شروع کرنا",
701 "sync_status_starting_sync": "مطابقت پذیری شروع کر رہا ہے۔",
702 "sync_status_syncronized": "مطابقت پذیر",
703 "sync_status_syncronizing": "مطابقت پذیری",
res/values/strings_yo.arb
+1
@@ -696,6 +696,7 @@
696 "sync_status_connecting": "Ń DÁRAPỌ̀ MỌ́",
697 "sync_status_failed_connect": "ÌKÀNPỌ̀ TI KÚ",
698 "sync_status_not_connected": "KÒ TI DÁRAPỌ̀ MỌ́ Ọ",
699 + "sync_status_starting_scan": "Bibẹrẹ ọlọjẹ",
700 "sync_status_starting_sync": "Ń BẸ̀RẸ̀ RẸ́",
701 "sync_status_syncronized": "TI MÚDỌ́GBA",
702 "sync_status_syncronizing": "Ń MÚDỌ́GBA",
res/values/strings_zh.arb
+1
@@ -695,6 +695,7 @@
695 "sync_status_connecting": "连接中",
696 "sync_status_failed_connect": "断线",
697 "sync_status_not_connected": "未连接",
698 + "sync_status_starting_scan": "开始扫描",
699 "sync_status_starting_sync": "开始同步",
700 "sync_status_syncronized": "已同步",
701 "sync_status_syncronizing": "正在同步",
tool/configure.dart
+1 -2
@@ -94,12 +94,11 @@ import 'package:cw_core/wallet_service.dart';
94 import 'package:cw_core/wallet_type.dart';
95 import 'package:hive/hive.dart';
96 import 'package:ledger_flutter/ledger_flutter.dart';
97 -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as btc;
97 +import 'package:blockchain_utils/blockchain_utils.dart';
98 import 'package:bip39/bip39.dart' as bip39;
99 """;
100 const bitcoinCWHeaders = """
101 import 'package:cw_bitcoin/utils.dart';
102 -import 'package:cw_bitcoin/litecoin_network.dart';
102 import 'package:cw_bitcoin/electrum_derivations.dart';
103 import 'package:cw_bitcoin/electrum.dart';
104 import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';