dev
dart 922 lines 29.6 KB
Raw
1 part of 'bitcoin.dart';
2
3 class CWBitcoin extends Bitcoin {
4 WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({
5 required String name,
6 required String mnemonic,
7 required String password,
8 required DerivationType derivationType,
9 required String derivationPath,
10 String? passphrase,
11 }) =>
12 BitcoinRestoreWalletFromSeedCredentials(
13 name: name,
14 mnemonic: mnemonic,
15 password: password,
16 derivationType: derivationType,
17 derivationPath: derivationPath,
18 passphrase: passphrase,
19 );
20
21 @override
22 WalletCredentials createBitcoinWalletFromKeys({
23 required String name,
24 required String password,
25 required String xpub,
26 HardwareWalletType? hardwareWalletType,
27 }) =>
28 BitcoinWalletFromKeysCredentials(
29 name: name, password: password, xpub: xpub, hardwareWalletType: hardwareWalletType);
30
31 @override
32 WalletCredentials createLitecoinWalletFromKeys({
33 required String name,
34 required String password,
35 required String xpub,
36 required String scanSecret,
37 required String spendPubkey,
38 HardwareWalletType? hardwareWalletType,
39 }) =>
40 LitecoinWalletFromKeysCredentials(
41 name: name,
42 password: password,
43 xpub: xpub,
44 scanSecret: scanSecret,
45 spendPubkey: spendPubkey,
46 hardwareWalletType: hardwareWalletType,
47 );
48
49 @override
50 WalletCredentials createBitcoinRestoreWalletFromWIFCredentials(
51 {required String name,
52 required String password,
53 required String wif,
54 WalletInfo? walletInfo}) =>
55 BitcoinRestoreWalletFromWIFCredentials(
56 name: name, password: password, wif: wif, walletInfo: walletInfo);
57
58 @override
59 WalletCredentials createBitcoinNewWalletCredentials({
60 required String name,
61 WalletInfo? walletInfo,
62 String? password,
63 String? passphrase,
64 String? mnemonic,
65 }) =>
66 BitcoinNewWalletCredentials(
67 name: name,
68 walletInfo: walletInfo,
69 password: password,
70 passphrase: passphrase,
71 mnemonic: mnemonic,
72 );
73
74 @override
75 WalletCredentials createBitcoinHardwareWalletCredentials(
76 {required String name,
77 required HardwareAccountData accountData,
78 WalletInfo? walletInfo}) =>
79 BitcoinRestoreWalletFromHardware(
80 name: name, hwAccountData: accountData, walletInfo: walletInfo);
81
82 @override
83 TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium;
84
85 @override
86 List<String> getWordList() => wordlist;
87
88 @override
89 Map<String, String> getWalletKeys(Object wallet) {
90 final bitcoinWallet = wallet as ElectrumWallet;
91 final keys = bitcoinWallet.keys;
92
93 return bitcoinWallet.keys.toJson();
94 }
95
96 @override
97 Map<String, String> getSilentPaymentKeys(Object wallet) {
98 final bitcoinWallet = wallet as ElectrumWallet;
99 final keysOwner = bitcoinWallet.walletAddresses.silentAddress;
100
101 if (keysOwner == null) return {};
102
103 return <String, String>{
104 'privateSpendKey': keysOwner.b_spend.toHex(),
105 'publicSpendKey': keysOwner.B_spend.toHex(),
106 'privateViewKey': keysOwner.b_scan.toHex(),
107 'publicViewKey': keysOwner.B_scan.toHex(),
108 };
109 }
110
111 @override
112 List<TransactionPriority> getTransactionPriorities() => BitcoinTransactionPriority.all;
113
114 @override
115 List<TransactionPriority> getLitecoinTransactionPriorities() => LitecoinTransactionPriority.all;
116
117 @override
118 TransactionPriority deserializeBitcoinTransactionPriority(int raw) =>
119 BitcoinTransactionPriority.deserialize(raw: raw);
120
121 @override
122 TransactionPriority deserializeLitecoinTransactionPriority(int raw) =>
123 LitecoinTransactionPriority.deserialize(raw: raw);
124
125 @override
126 int getFeeRate(Object wallet, TransactionPriority priority) {
127 final bitcoinWallet = wallet as ElectrumWallet;
128 return bitcoinWallet.feeRate(priority);
129 }
130
131 @override
132 Future<void> generateNewAddress(Object wallet, String label) async {
133 final bitcoinWallet = wallet as ElectrumWallet;
134 await bitcoinWallet.walletAddresses.generateNewAddress(label: label);
135 await wallet.save();
136 }
137
138 @override
139 Future<void> updateAddress(Object wallet, String address, String label) async {
140 final bitcoinWallet = wallet as ElectrumWallet;
141 bitcoinWallet.walletAddresses.updateAddress(address, label);
142 await wallet.save();
143 }
144
145 @override
146 Object createBitcoinTransactionCredentials(
147 List<Output> outputs, {
148 required TransactionPriority priority,
149 int? feeRate,
150 UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any,
151 String? payjoinUri,
152 }) {
153 final bitcoinFeeRate =
154 priority == BitcoinTransactionPriority.custom && feeRate != null ? feeRate : null;
155 return BitcoinTransactionCredentials(
156 outputs
157 .map((out) => OutputInfo(
158 fiatAmount: out.fiatAmount,
159 cryptoAmount: out.cryptoAmountMoney,
160 address: out.address,
161 note: out.note,
162 sendAll: out.sendAll,
163 extractedAddress: out.extractedAddress,
164 isParsedAddress: out.isParsedAddress,
165 memo: out.memo.isNotEmpty ? out.memo : null,
166 extra: out.extra,
167 ))
168 .toList(),
169 priority: priority as BitcoinTransactionPriority,
170 feeRate: bitcoinFeeRate,
171 coinTypeToSpendFrom: coinTypeToSpendFrom,
172 payjoinUri: payjoinUri);
173 }
174
175 @override
176 @computed
177 List<ElectrumSubAddress> getSubAddresses(Object wallet) {
178 final electrumWallet = wallet as ElectrumWallet;
179 return electrumWallet.walletAddresses.addressesByReceiveType
180 .map<ElectrumSubAddress>((addr) => ElectrumSubAddress(
181 id: addr.index,
182 name: addr.name,
183 address: addr.address,
184 txCount: addr.txCount,
185 balance: addr.balance,
186 isChange: addr.isHidden,
187 isLegacyDerivation: addr.isLegacyDerivation,
188 derivationPath: addr.derivationPath))
189 .toList();
190 }
191
192 @override
193 Future<Money> estimateFakeSendAllTxAmount(WalletBase wallet, TransactionPriority priority,
194 {UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any}) async {
195 try {
196 final sk = ECPrivate.random();
197 final electrumWallet = wallet as ElectrumWallet;
198
199 if (wallet.type == WalletType.bitcoinCash) {
200 final p2pkhAddr = sk.getPublic().toP2pkhAddress();
201 final estimatedTx = await electrumWallet.estimateSendAllTx(
202 [BitcoinOutput(address: p2pkhAddr, value: BigInt.zero)],
203 getFeeRate(wallet, priority as BitcoinCashTransactionPriority),
204 );
205
206 return estimatedTx.amount;
207 }
208
209 if (wallet.type == WalletType.dogecoin) {
210 final dogeAddr = sk.getPublic().toP2pkhAddress();
211 final estimatedTx = await electrumWallet.estimateSendAllTx(
212 [BitcoinOutput(address: dogeAddr, value: BigInt.zero)],
213 getFeeRate(wallet, priority as BitcoinTransactionPriority),
214 coinTypeToSpendFrom: coinTypeToSpendFrom,
215 );
216 return estimatedTx.amount;
217 }
218
219 final p2shAddr = sk.getPublic().toP2pkhAddress();
220 final estimatedTx = await electrumWallet.estimateSendAllTx(
221 [BitcoinOutput(address: p2shAddr, value: BigInt.zero)],
222 getFeeRate(
223 wallet,
224 wallet.type == WalletType.litecoin
225 ? priority as LitecoinTransactionPriority
226 : priority as BitcoinTransactionPriority,
227 ),
228 coinTypeToSpendFrom: coinTypeToSpendFrom,
229 );
230
231 return estimatedTx.amount;
232 } catch (_) {
233 return Money.zero(wallet.currency);
234 }
235 }
236
237 @override
238 String getAddress(Object wallet) {
239 final bitcoinWallet = wallet as ElectrumWallet;
240 return bitcoinWallet.walletAddresses.address;
241 }
242
243 @override
244 String formatterBitcoinAmountToString({required int amount}) =>
245 bitcoinAmountToString(amount: amount);
246
247 @override
248 int formatterStringDoubleToBitcoinAmount(String amount) => stringDoubleToBitcoinAmount(amount);
249
250 @override
251 String bitcoinTransactionPriorityWithLabel(TransactionPriority priority, int rate,
252 {int? customRate}) =>
253 (priority as BitcoinTransactionPriority).labelWithRate(rate, customRate);
254
255 @override
256 List<BitcoinUnspent> getUnspents(Object wallet,
257 {UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any}) {
258 final bitcoinWallet = wallet as ElectrumWallet;
259 return bitcoinWallet.unspentCoins.where((element) {
260 switch (coinTypeToSpendFrom) {
261 case UnspentCoinType.mweb:
262 return element.bitcoinAddressRecord.type == SegwitAddresType.mweb;
263 case UnspentCoinType.nonMweb:
264 return element.bitcoinAddressRecord.type != SegwitAddresType.mweb;
265 case UnspentCoinType.lightning:
266 case UnspentCoinType.any:
267 return true;
268 }
269 }).toList();
270 }
271
272 Future<void> updateUnspents(Object wallet) async {
273 final bitcoinWallet = wallet as ElectrumWallet;
274 await bitcoinWallet.updateAllUnspents();
275 }
276
277 WalletService createBitcoinWalletService(Box<UnspentCoinsInfo> unspentCoinSource,
278 Box<PayjoinSession> payjoinSessionSource, bool isDirect) {
279 return BitcoinWalletService(unspentCoinSource, payjoinSessionSource, isDirect);
280 }
281
282 WalletService createLitecoinWalletService(
283 Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
284 return LitecoinWalletService(unspentCoinSource, isDirect);
285 }
286
287 @override
288 TransactionPriority getBitcoinTransactionPriorityMedium() => BitcoinTransactionPriority.medium;
289
290 @override
291 TransactionPriority getBitcoinTransactionPriorityCustom() => BitcoinTransactionPriority.custom;
292
293 @override
294 TransactionPriority getLitecoinTransactionPriorityMedium() => LitecoinTransactionPriority.medium;
295
296 @override
297 TransactionPriority getBitcoinTransactionPrioritySlow() => BitcoinTransactionPriority.slow;
298
299 @override
300 TransactionPriority getLitecoinTransactionPrioritySlow() => LitecoinTransactionPriority.slow;
301
302 @override
303 Future<void> setAddressType(Object wallet, dynamic option) async {
304 final bitcoinWallet = wallet as ElectrumWallet;
305 await bitcoinWallet.walletAddresses.setAddressType(option as BitcoinAddressType);
306 }
307
308 @override
309 ReceivePageOption getSelectedAddressType(Object wallet) {
310 final bitcoinWallet = wallet as ElectrumWallet;
311 return BitcoinReceivePageOption.fromType(bitcoinWallet.walletAddresses.addressPageType);
312 }
313
314 @override
315 bool hasSelectedSilentPayments(Object wallet) {
316 final bitcoinWallet = wallet as ElectrumWallet;
317 return bitcoinWallet.walletAddresses.addressPageType == SilentPaymentsAddresType.p2sp;
318 }
319
320 @override
321 bool hasSelectedLightning(Object wallet) {
322 final bitcoinWallet = wallet as ElectrumWallet;
323 return bitcoinWallet.walletAddresses.addressPageType is LightningAddressType;
324 }
325
326 @override
327 BitcoinAddressType getBitcoinAddressType(ReceivePageOption option) {
328 switch (option) {
329 case BitcoinReceivePageOption.p2pkh:
330 return P2pkhAddressType.p2pkh;
331 case BitcoinReceivePageOption.p2sh:
332 return P2shAddressType.p2wpkhInP2sh;
333 case BitcoinReceivePageOption.p2tr:
334 return SegwitAddresType.p2tr;
335 case BitcoinReceivePageOption.p2wsh:
336 return SegwitAddresType.p2wsh;
337 case BitcoinReceivePageOption.mweb:
338 return SegwitAddresType.mweb;
339 case BitcoinReceivePageOption.p2wpkh:
340 default:
341 return SegwitAddresType.p2wpkh;
342 }
343 }
344
345 @override
346 BitcoinReceivePageOption getBitcoinLightningReceivePageOption() =>
347 BitcoinReceivePageOption.lightning;
348 @override
349 BitcoinReceivePageOption getBitcoinSegwitPageOption() => BitcoinReceivePageOption.p2wpkh;
350 @override
351 BitcoinReceivePageOption getLitecoinMwebReceivePageOption() => BitcoinReceivePageOption.mweb;
352
353 @override
354 Future<List<DerivationType>> compareDerivationMethods(
355 {required String mnemonic, required Node node}) async {
356 if (await checkIfMnemonicIsElectrum2(mnemonic)) {
357 return [DerivationType.electrum];
358 }
359
360 return [DerivationType.bip39, DerivationType.electrum];
361 }
362
363 int _countCharOccurrences(String str, String charToCount) {
364 int count = 0;
365 for (int i = 0; i < str.length; i++) {
366 if (str[i] == charToCount) {
367 count++;
368 }
369 }
370 return count;
371 }
372
373 @override
374 Future<List<DerivationInfo>> getDerivationsFromMnemonic({
375 required String mnemonic,
376 required Node node,
377 String? passphrase,
378 }) async {
379 List<DerivationInfo> list = [];
380
381 List<DerivationType> types = await compareDerivationMethods(mnemonic: mnemonic, node: node);
382 if (types.length == 1 && types.first == DerivationType.electrum) {
383 return [getElectrumDerivations()[DerivationType.electrum]!.first];
384 }
385
386 final electrumClient = ElectrumClient();
387 await electrumClient.connectToUri(node.uri, useSSL: node.useSSL);
388
389 late BasedUtxoNetwork network;
390 switch (node.type) {
391 case WalletType.litecoin:
392 network = LitecoinNetwork.mainnet;
393 break;
394 case WalletType.bitcoin:
395 default:
396 network = BitcoinNetwork.mainnet;
397 break;
398 }
399
400 for (DerivationType dType in electrum_derivations.keys) {
401 late Uint8List seedBytes;
402 if (dType == DerivationType.electrum) {
403 seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
404 } else if (dType == DerivationType.bip39) {
405 seedBytes = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? '');
406 }
407
408 for (DerivationInfo dInfo in electrum_derivations[dType]!) {
409 try {
410 DerivationInfo dInfoCopy = DerivationInfo(
411 derivationType: dInfo.derivationType,
412 derivationPath: dInfo.derivationPath,
413 description: dInfo.description,
414 scriptType: dInfo.scriptType,
415 );
416
417 String balancePath = dInfoCopy.derivationPath!;
418 int derivationDepth = _countCharOccurrences(balancePath, '/');
419
420 // for BIP44
421 if (derivationDepth == 3 || derivationDepth == 1) {
422 // we add "/0" so that we generate account 0
423 balancePath += "/0";
424 }
425
426 final hd = Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath(balancePath)
427 as Bip32Slip10Secp256k1;
428
429 // derive address at index 0:
430 String? address;
431 switch (dInfoCopy.scriptType) {
432 case "p2wpkh":
433 address = generateP2WPKHAddress(hd: hd, network: network, index: 0);
434 break;
435 case "p2pkh":
436 address = generateP2PKHAddress(hd: hd, network: network, index: 0);
437 break;
438 case "p2wpkh-p2sh":
439 address = generateP2SHAddress(hd: hd, network: network, index: 0);
440 break;
441 case "p2tr":
442 address = generateP2TRAddress(hd: hd, network: network, index: 0);
443 break;
444 default:
445 continue;
446 }
447
448 final sh = BitcoinAddressUtils.scriptHash(address, network: network);
449 final history = await electrumClient.getHistory(sh);
450
451 final balance = await electrumClient.getBalance(sh);
452 dInfoCopy.balance = balance.entries.firstOrNull?.value.toString() ?? "0";
453 dInfoCopy.address = address;
454 dInfoCopy.transactionsCount = history.length;
455
456 list.add(dInfoCopy);
457 } catch (e, s) {
458 printV("derivationInfoError: $e");
459 printV("derivationInfoStack: $s");
460 }
461 }
462 }
463
464 // sort the list such that derivations with the most transactions are first:
465 list.sort((a, b) => b.transactionsCount.compareTo(a.transactionsCount));
466
467 return list;
468 }
469
470 @override
471 Map<DerivationType, List<DerivationInfo>> getElectrumDerivations() {
472 return electrum_derivations;
473 }
474
475 @override
476 bool hasTaprootInput(PendingTransaction pendingTransaction) {
477 return (pendingTransaction as PendingBitcoinTransaction).hasTaprootInputs;
478 }
479
480 @override
481 Future<PendingBitcoinTransaction> replaceByFee(
482 Object wallet, String transactionHash, String fee) async {
483 final bitcoinWallet = wallet as ElectrumWallet;
484 return await bitcoinWallet.replaceByFee(transactionHash, int.parse(fee));
485 }
486
487 @override
488 Future<String?> canReplaceByFee(Object wallet, Object transactionInfo) async {
489 final bitcoinWallet = wallet as ElectrumWallet;
490 final tx = transactionInfo as ElectrumTransactionInfo;
491 return bitcoinWallet.canReplaceByFee(tx);
492 }
493
494 @override
495 int getTransactionVSize(Object wallet, String transactionHex) {
496 final bitcoinWallet = wallet as ElectrumWallet;
497 return bitcoinWallet.transactionVSize(transactionHex);
498 }
499
500 @override
501 Future<bool> isChangeSufficientForFee(Object wallet, String txId, String newFee) async {
502 final bitcoinWallet = wallet as ElectrumWallet;
503 return bitcoinWallet.isChangeSufficientForFee(txId, int.parse(newFee));
504 }
505
506 @override
507 int getFeeAmountForPriority(
508 Object wallet, TransactionPriority priority, int inputsCount, int outputsCount,
509 {int? size}) {
510 final bitcoinWallet = wallet as ElectrumWallet;
511 return bitcoinWallet.feeAmountForPriority(
512 priority as BitcoinTransactionPriority, inputsCount, outputsCount);
513 }
514
515 @override
516 int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount,
517 {int? outputsCount, int? size}) {
518 final bitcoinWallet = wallet as ElectrumWallet;
519 return bitcoinWallet.calculateEstimatedFeeWithFeeRate(
520 feeRate,
521 amount,
522 outputsCount: outputsCount,
523 size: size,
524 );
525 }
526
527 @override
528 int feeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount,
529 {int? size}) {
530 final bitcoinWallet = wallet as ElectrumWallet;
531 return bitcoinWallet.feeAmountWithFeeRate(feeRate, inputsCount, outputsCount, size: size);
532 }
533
534 @override
535 int getMaxCustomFeeRate(Object wallet) {
536 final bitcoinWallet = wallet as ElectrumWallet;
537 return (bitcoinWallet.feeRate(BitcoinTransactionPriority.fast) * 10).round();
538 }
539
540 @override
541 Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
542 (wallet as ElectrumWallet).hardwareWalletService = service;
543 }
544
545 @override
546 HardwareWalletService getLedgerHardwareWalletService(
547 ledger.LedgerConnection connection, bool isBitcoin) {
548 if (isBitcoin) return BitcoinLedgerService(connection);
549 return LitecoinLedgerService(connection);
550 }
551
552 @override
553 HardwareWalletService getBitboxHardwareWalletService(
554 bitbox.BitboxManager manager, bool isBitcoin) {
555 if (isBitcoin) return BitcoinBitboxService(manager);
556 return LitecoinBitboxService(manager);
557 }
558
559 @override
560 HardwareWalletService getTrezorHardwareWalletService(
561 trezor.TrezorConnect connect, bool isBitcoin) {
562 if (isBitcoin) return BitcoinTrezorService(connect);
563 return LitecoinTrezorService(connect);
564 }
565
566 @override
567 List<ElectrumSubAddress> getSilentPaymentAddresses(Object wallet) {
568 final bitcoinWallet = wallet as ElectrumWallet;
569 return bitcoinWallet.walletAddresses.silentAddresses
570 .where((addr) => addr.type != SegwitAddresType.p2tr)
571 .map((addr) => ElectrumSubAddress(
572 id: addr.index,
573 name: addr.name,
574 address: addr.address,
575 txCount: addr.txCount,
576 balance: addr.balance,
577 isChange: addr.isHidden,
578 derivationPath: addr.derivationPath))
579 .toList();
580 }
581
582 @override
583 List<ElectrumSubAddress> getSilentPaymentReceivedAddresses(Object wallet) {
584 final bitcoinWallet = wallet as ElectrumWallet;
585 return bitcoinWallet.walletAddresses.silentAddresses
586 .where((addr) => addr.type == SegwitAddresType.p2tr)
587 .map((addr) => ElectrumSubAddress(
588 id: addr.index,
589 name: addr.name,
590 address: addr.address,
591 txCount: addr.txCount,
592 balance: addr.balance,
593 isChange: addr.isHidden,
594 derivationPath: addr.derivationPath))
595 .toList();
596 }
597
598 @override
599 bool isBitcoinReceivePageOption(ReceivePageOption option) {
600 return option is BitcoinReceivePageOption;
601 }
602
603 @override
604 bool isPayjoinAvailable(Object wallet) =>
605 (wallet is BitcoinWallet) && (wallet as BitcoinWallet).isPayjoinAvailable;
606
607 @override
608 BitcoinAddressType getOptionToType(ReceivePageOption option) {
609 return (option as BitcoinReceivePageOption).toType();
610 }
611
612 @override
613 @computed
614 bool getScanningActive(Object wallet) {
615 final bitcoinWallet = wallet as ElectrumWallet;
616 return bitcoinWallet.silentPaymentsScanningActive;
617 }
618
619 @override
620 Future<void> setScanningActive(Object wallet, bool active) async {
621 final bitcoinWallet = wallet as ElectrumWallet;
622 bitcoinWallet.setSilentPaymentsScanning(active);
623 }
624
625 Future<void> setIsAlwaysScanningSP(Object wallet, bool active) async {
626 final bitcoinWallet = wallet as ElectrumWallet;
627 bitcoinWallet.alwaysScan = active;
628 bitcoinWallet.save();
629 }
630
631 @computed
632 bool getIsAlwaysScanningSP(Object wallet) => (wallet as ElectrumWallet).alwaysScan ?? false;
633
634 @override
635 bool isTestnet(Object wallet) {
636 final bitcoinWallet = wallet as ElectrumWallet;
637 return bitcoinWallet.isTestnet;
638 }
639
640 @override
641 Future<bool> checkIfMempoolAPIIsEnabled(Object wallet) async {
642 final bitcoinWallet = wallet as ElectrumWallet;
643 return await bitcoinWallet.checkIfMempoolAPIIsEnabled();
644 }
645
646 @override
647 Future<int> getHeightByDate({required DateTime date, bool? bitcoinMempoolAPIEnabled}) async {
648 if (bitcoinMempoolAPIEnabled ?? false) {
649 try {
650 return await getBitcoinHeightByDateAPI(date: date);
651 } catch (_) {}
652 }
653 return await getBitcoinHeightByDate(date: date);
654 }
655
656 @override
657 int getLitecoinHeightByDate({required DateTime date}) => getLtcHeightByDate(date: date);
658
659 @override
660 Future<void> rescan(Object wallet, {required int height, bool? doSingleScan}) async {
661 final bitcoinWallet = wallet as ElectrumWallet;
662 bitcoinWallet.rescan(height: height, doSingleScan: doSingleScan);
663 }
664
665 @override
666 Future<bool> getNodeIsElectrsSPEnabled(Object wallet) async {
667 final bitcoinWallet = wallet as ElectrumWallet;
668 return bitcoinWallet.getNodeSupportsSilentPayments();
669 }
670
671 @override
672 void deleteSilentPaymentAddress(Object wallet, String address) {
673 final bitcoinWallet = wallet as ElectrumWallet;
674 bitcoinWallet.walletAddresses.deleteSilentPaymentAddress(address);
675 }
676
677 @override
678 Future<void> updateFeeRates(Object wallet) async {
679 final bitcoinWallet = wallet as ElectrumWallet;
680 await bitcoinWallet.updateFeeRates();
681 }
682
683 @override
684 Future<void> setMwebEnabled(Object wallet, bool enabled) async {
685 final litecoinWallet = wallet as LitecoinWallet;
686 litecoinWallet.setMwebEnabled(enabled);
687 }
688
689 @override
690 bool getMwebEnabled(Object wallet) {
691 final litecoinWallet = wallet as LitecoinWallet;
692 return litecoinWallet.mwebEnabled;
693 }
694
695 List<Output> updateOutputs(PendingTransaction pendingTransaction, List<Output> outputs) {
696 if (pendingTransaction is! PendingBitcoinTransaction || !pendingTransaction.hasSilentPayment) {
697 return outputs;
698 }
699
700 final stealthAddresses = pendingTransaction.stealthAddresses;
701 if (stealthAddresses.length != outputs.length) {
702 printV("well, that shouldn't happen");
703 return outputs;
704 }
705
706 for (var i = 0; i < outputs.length; i++) {
707 outputs[i].stealthAddress = stealthAddresses[i];
708 }
709 return outputs;
710 }
711
712 @override
713 bool txIsReceivedSilentPayment(TransactionInfo txInfo) {
714 final tx = txInfo as ElectrumTransactionInfo;
715 return tx.isReceivedSilentPayment;
716 }
717
718 @override
719 bool txIsMweb(TransactionInfo txInfo) {
720 final tx = txInfo as ElectrumTransactionInfo;
721
722 List<String> inputAddresses = tx.inputAddresses ?? [];
723 List<String> outputAddresses = tx.outputAddresses ?? [];
724 bool inputAddressesContainMweb = false;
725 bool outputAddressesContainMweb = false;
726
727 for (var address in inputAddresses) {
728 if (address.toLowerCase().contains('mweb')) {
729 inputAddressesContainMweb = true;
730 break;
731 }
732 }
733
734 for (var address in outputAddresses) {
735 if (address.toLowerCase().contains('mweb')) {
736 outputAddressesContainMweb = true;
737 break;
738 }
739 }
740
741 // TODO: this could be improved:
742 return inputAddressesContainMweb || outputAddressesContainMweb;
743 }
744
745 String? getUnusedMwebAddress(Object wallet) {
746 try {
747 final electrumWallet = wallet as ElectrumWallet;
748 final mwebAddress =
749 electrumWallet.walletAddresses.mwebAddresses.firstWhere((element) => !element.isUsed);
750 return mwebAddress.address;
751 } catch (_) {
752 return null;
753 }
754 }
755
756 String? getUnusedSegwitAddress(Object wallet) {
757 try {
758 final electrumWallet = wallet as ElectrumWallet;
759 final segwitAddress = electrumWallet.walletAddresses.allAddresses
760 .firstWhere((element) => !element.isUsed && element.type == SegwitAddresType.p2wpkh);
761 return segwitAddress.address;
762 } catch (_) {
763 return null;
764 }
765 }
766
767 Future<String?> getUnusedSpakDepositAddress(Object wallet) async {
768 try {
769 final bitcoinWallet = wallet as BitcoinWallet;
770 return wallet.lightningWallet?.getDepositAddress();
771 } catch (_) {
772 return null;
773 }
774 }
775
776 @override
777 Future<void> commitPsbtUR(Object wallet, List<String> urCodes) {
778 if (wallet is LitecoinWallet) return wallet.commitPsbtUR(urCodes);
779 final _wallet = wallet as BitcoinWalletBase;
780 return _wallet.commitPsbtUR(urCodes);
781 }
782
783 @override
784 String getPayjoinEndpoint(Object wallet) {
785 final _wallet = wallet as ElectrumWallet;
786 if (!isPayjoinAvailable(wallet)) return '';
787 return (_wallet.walletAddresses as BitcoinWalletAddresses).payjoinEndpoint ?? '';
788 }
789
790 @override
791 void updatePayjoinState(Object wallet, bool value) {
792 final _wallet = wallet as ElectrumWallet;
793 if (value) {
794 (_wallet.walletAddresses as BitcoinWalletAddresses).initPayjoin();
795 } else {
796 stopPayjoinSessions(wallet);
797 }
798 }
799
800 @override
801 bool useLightning(Object wallet) {
802 final _wallet = wallet as ElectrumWallet;
803 if (_wallet is BitcoinWallet) return _wallet.useLightning;
804
805 return false;
806 }
807
808 @override
809 void updateUseLightning(Object wallet, bool value) {
810 final _wallet = wallet as ElectrumWallet;
811 if (_wallet is BitcoinWallet) {
812 _wallet.useLightning = value;
813 }
814 }
815
816 @override
817 void resumePayjoinSessions(Object wallet) {
818 final _wallet = wallet as ElectrumWallet;
819 (_wallet.walletAddresses as BitcoinWalletAddresses).initPayjoin();
820 }
821
822 @override
823 void stopPayjoinSessions(Object wallet) {
824 final _wallet = wallet as ElectrumWallet;
825 (_wallet.walletAddresses as BitcoinWalletAddresses).payjoinManager.cleanupSessions();
826 (_wallet.walletAddresses as BitcoinWalletAddresses).currentPayjoinReceiver = null;
827 (_wallet.walletAddresses as BitcoinWalletAddresses).payjoinEndpoint = null;
828 }
829
830 @override
831 List<String>? getTransactionAddresses(Object wallet, TransactionInfo tx) {
832 final bitcoinWallet = wallet as BitcoinWallet;
833 final bitcoinTx = tx as ElectrumTransactionInfo;
834
835 final addresses = <String>[];
836
837 if (bitcoinTx.unspents == null || bitcoinTx.unspents!.isEmpty) {
838 if (bitcoinTx.outputAddresses == null) return null;
839 for (final addr in bitcoinTx.outputAddresses!) {
840 if (bitcoinWallet.walletAddresses.allAddresses
841 .firstWhereOrNull((item) => item.address == addr) !=
842 null) {
843 addresses.add(addr);
844 }
845 }
846 return addresses;
847 }
848
849 final labels = <String>[];
850 try {
851 bitcoinTx.unspents!.forEach((unspent) {
852 addresses.add(bitcoinWallet.walletAddresses.silentAddresses
853 .firstWhere((address) => address.silentPaymentTweak == unspent.silentPaymentLabel)
854 .address);
855 });
856 } catch (e) {}
857
858 return addresses;
859 }
860
861 @override
862 String getNetworkName(Object wallet) {
863 return (wallet as ElectrumWallet).network.value;
864 }
865
866 @override
867 Future<void> setLightningUsername(Object wallet, String username) async {
868 final electrumWallet = wallet as ElectrumWallet;
869 await electrumWallet.walletAddresses.setLightningAddress(wallet.name, newAddress: username);
870 }
871
872 @override
873 Future<String?> getLightningUsername(Object wallet) async {
874 final electrumWallet = wallet as ElectrumWallet;
875
876 if (electrumWallet.walletAddresses.lightningWallet == null) {
877 printV("lightning wallet is null");
878 return null;
879 }
880 return (await electrumWallet.walletAddresses.lightningWallet!.getAddress())
881 ?.replaceFirst("@cake.cash", "");
882 }
883
884 @override
885 Future<String?> getLightningInvoice(Object wallet, BigInt amount) async {
886 final electrumWallet = wallet as ElectrumWallet;
887
888 if (electrumWallet is BitcoinWallet && electrumWallet.lightningWallet != null) {
889 return electrumWallet.lightningWallet!.getBolt11Invoice(amount, "Send to CakeWallet");
890 }
891 return null;
892 }
893
894 @override
895 String? getBreezSdkError(Object exception) {
896 if (exception is SdkError_SparkError) {
897 return (exception as SdkError_SparkError).field0.toString();
898 }
899 if (exception is SdkError_InvalidUuid) {
900 return (exception as SdkError_InvalidUuid).field0.toString();
901 }
902 if (exception is SdkError_InvalidInput) {
903 return (exception as SdkError_InvalidInput).field0.toString();
904 }
905 if (exception is SdkError_NetworkError) {
906 return (exception as SdkError_NetworkError).field0.toString();
907 }
908 if (exception is SdkError_StorageError) {
909 return (exception as SdkError_StorageError).field0.toString();
910 }
911 if (exception is SdkError_ChainServiceError) {
912 return (exception as SdkError_ChainServiceError).field0.toString();
913 }
914 if (exception is SdkError_LnurlError) {
915 return (exception as SdkError_LnurlError).field0.toString();
916 }
917 if (exception is SdkError_Generic) {
918 return (exception as SdkError_Generic).field0.toString();
919 }
920 return null;
921 }
922 }