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';