From 6b795b5ba3f25fc2fe3f4cb64d544e27d146c2c8 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 26 Feb 2024 15:32:54 -0300 Subject: [PATCH 01/75] feat: rebase btc-addr-types, migrate to bitcoin_base --- cw_bitcoin/lib/bitcoin_address_record.dart | 6 +- .../lib/bitcoin_receive_page_option.dart | 4 + cw_bitcoin/lib/bitcoin_unspent.dart | 28 +- cw_bitcoin/lib/bitcoin_wallet.dart | 17 +- cw_bitcoin/lib/bitcoin_wallet_addresses.dart | 4 +- cw_bitcoin/lib/electrum.dart | 6 + cw_bitcoin/lib/electrum_transaction_info.dart | 76 +- cw_bitcoin/lib/electrum_wallet.dart | 719 +++++++++++++++++- cw_bitcoin/lib/electrum_wallet_addresses.dart | 85 ++- cw_bitcoin/lib/electrum_wallet_snapshot.dart | 23 +- cw_bitcoin/pubspec.lock | 12 +- cw_bitcoin/pubspec.yaml | 3 +- cw_bitcoin_cash/pubspec.yaml | 2 +- cw_core/lib/sync_status.dart | 14 +- cw_core/lib/unspent_transaction_output.dart | 3 +- howto-build-android.md | 20 +- lib/bitcoin/cw_bitcoin.dart | 5 + lib/core/address_validator.dart | 5 +- lib/core/sync_status_title.dart | 6 +- lib/di.dart | 5 + .../present_receive_option_picker.dart | 128 ++-- lib/src/screens/receive/receive_page.dart | 32 +- lib/src/screens/send/widgets/send_card.dart | 3 +- lib/src/widgets/address_text_field.dart | 6 +- .../dashboard/dashboard_view_model.dart | 5 + lib/view_model/rescan_view_model.dart | 11 +- .../wallet_address_list_view_model.dart | 23 +- model_generator.sh | 19 +- tool/configure.dart | 1 + 29 files changed, 1047 insertions(+), 224 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index d8d9082305..2c40ba34cd 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -16,6 +16,7 @@ class BitcoinAddressRecord { required this.type, String? scriptHash, required this.network, + this.silentPaymentTweak, }) : _txCount = txCount, _balance = balance, _name = name, @@ -23,7 +24,7 @@ class BitcoinAddressRecord { scriptHash = scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null); - factory BitcoinAddressRecord.fromJSON(String jsonSource, BasedUtxoNetwork? network) { + factory BitcoinAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) { final decoded = json.decode(jsonSource) as Map; return BitcoinAddressRecord( @@ -42,6 +43,7 @@ class BitcoinAddressRecord { network: (decoded['network'] as String?) == null ? network : BasedUtxoNetwork.fromName(decoded['network'] as String), + silentPaymentTweak: decoded['silentPaymentTweak'] as String?, ); } @@ -57,6 +59,7 @@ class BitcoinAddressRecord { bool _isUsed; String? scriptHash; BasedUtxoNetwork? network; + final String? silentPaymentTweak; int get txCount => _txCount; @@ -96,5 +99,6 @@ class BitcoinAddressRecord { 'type': type.toString(), 'scriptHash': scriptHash, 'network': network?.value, + 'silentPaymentTweak': silentPaymentTweak, }); } diff --git a/cw_bitcoin/lib/bitcoin_receive_page_option.dart b/cw_bitcoin/lib/bitcoin_receive_page_option.dart index 2e246f532a..2b025965bd 100644 --- a/cw_bitcoin/lib/bitcoin_receive_page_option.dart +++ b/cw_bitcoin/lib/bitcoin_receive_page_option.dart @@ -8,6 +8,8 @@ class BitcoinReceivePageOption implements ReceivePageOption { static const p2wsh = BitcoinReceivePageOption._('Segwit (P2WSH)'); static const p2pkh = BitcoinReceivePageOption._('Legacy (P2PKH)'); + static const silent_payments = BitcoinReceivePageOption._('Silent Payments'); + const BitcoinReceivePageOption._(this.value); final String value; @@ -34,6 +36,8 @@ class BitcoinReceivePageOption implements ReceivePageOption { return BitcoinReceivePageOption.p2pkh; case P2shAddressType.p2wpkhInP2sh: return BitcoinReceivePageOption.p2sh; + case SilentPaymentsAddresType.p2sp: + return BitcoinReceivePageOption.silent_payments; case SegwitAddresType.p2wpkh: default: return BitcoinReceivePageOption.p2wpkh; diff --git a/cw_bitcoin/lib/bitcoin_unspent.dart b/cw_bitcoin/lib/bitcoin_unspent.dart index 52edea0913..131f47ab65 100644 --- a/cw_bitcoin/lib/bitcoin_unspent.dart +++ b/cw_bitcoin/lib/bitcoin_unspent.dart @@ -1,14 +1,38 @@ +import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_core/unspent_transaction_output.dart'; class BitcoinUnspent extends Unspent { - BitcoinUnspent(BitcoinAddressRecord addressRecord, String hash, int value, int vout) + BitcoinUnspent(BitcoinAddressRecord addressRecord, String hash, int value, int vout, + {this.silentPaymentTweak, this.type}) : bitcoinAddressRecord = addressRecord, super(addressRecord.address, hash, value, vout, null); factory BitcoinUnspent.fromJSON(BitcoinAddressRecord address, Map json) => BitcoinUnspent( - address, json['tx_hash'] as String, json['value'] as int, json['tx_pos'] as int); + address, + json['tx_hash'] as String, + json['value'] as int, + json['tx_pos'] as int, + silentPaymentTweak: json['silent_payment_tweak'] as String?, + type: json['type'] == null + ? null + : BitcoinAddressType.values.firstWhere((e) => e.toString() == json['type']), + ); + + Map toJson() { + final json = { + 'address_record': bitcoinAddressRecord.toJSON(), + 'tx_hash': hash, + 'value': value, + 'tx_pos': vout, + 'silent_payment_tweak': silentPaymentTweak, + 'type': type.toString(), + }; + return json; + } final BitcoinAddressRecord bitcoinAddressRecord; + String? silentPaymentTweak; + BitcoinAddressType? type; } diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 3b3e9c6363..a018abbd6a 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -30,6 +30,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, + List? initialSilentAddresses, + int initialSilentAddressIndex = 0, + SilentPaymentOwner? silentAddress, }) : super( mnemonic: mnemonic, password: password, @@ -46,10 +49,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { currency: CryptoCurrency.btc) { walletAddresses = BitcoinWalletAddresses( walletInfo, - electrumClient: electrumClient, initialAddresses: initialAddresses, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, + initialSilentAddresses: initialSilentAddresses, + initialSilentAddressIndex: initialSilentAddressIndex, + silentAddress: silentAddress, mainHd: hd, sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"), network: networkParam ?? network, @@ -67,9 +72,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { String? addressPageType, BasedUtxoNetwork? network, List? initialAddresses, + List? initialSilentAddresses, ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, + int initialSilentAddressIndex = 0, }) async { return BitcoinWallet( mnemonic: mnemonic, @@ -77,6 +84,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, initialAddresses: initialAddresses, + initialSilentAddresses: initialSilentAddresses, + initialSilentAddressIndex: initialSilentAddressIndex, + silentAddress: await SilentPaymentOwner.fromMnemonic(mnemonic, + hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp'), initialBalance: initialBalance, seedBytes: await mnemonicToSeedBytes(mnemonic), initialRegularAddressIndex: initialRegularAddressIndex, @@ -101,6 +112,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, initialAddresses: snp.addresses, + initialSilentAddresses: snp.silentAddresses, + initialSilentAddressIndex: snp.silentAddressIndex, + silentAddress: await SilentPaymentOwner.fromMnemonic(snp.mnemonic, + hrp: snp.network == BitcoinNetwork.testnet ? 'tsp' : 'sp'), initialBalance: snp.balance, seedBytes: await mnemonicToSeedBytes(snp.mnemonic), initialRegularAddressIndex: snp.regularAddressIndex, diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart index f125774929..48960ce3d1 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart @@ -15,10 +15,12 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S required super.mainHd, required super.sideHd, required super.network, - required super.electrumClient, super.initialAddresses, super.initialRegularAddressIndex, super.initialChangeAddressIndex, + super.initialSilentAddresses, + super.initialSilentAddressIndex = 0, + super.silentAddress, }) : super(walletInfo); @override diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 51a53e285b..59c864eb2e 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -349,6 +349,12 @@ class ElectrumClient { return null; }); + BehaviorSubject? chainTipUpdate() { + _id += 1; + return subscribe( + id: 'blockchain.headers.subscribe', method: 'blockchain.headers.subscribe'); + } + BehaviorSubject? scripthashUpdate(String scripthash) { _id += 1; return subscribe( diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index cfea0e089a..5a7f797f96 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -1,9 +1,8 @@ import 'package:bitcoin_base/bitcoin_base.dart'; -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; -import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData; import 'package:cw_bitcoin/address_from_output.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_amount_format.dart'; +import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/format_amount.dart'; @@ -20,6 +19,8 @@ class ElectrumTransactionBundle { } class ElectrumTransactionInfo extends TransactionInfo { + BitcoinUnspent? unspent; + ElectrumTransactionInfo(this.type, {required String id, required int height, @@ -28,7 +29,9 @@ class ElectrumTransactionInfo extends TransactionInfo { required TransactionDirection direction, required bool isPending, required DateTime date, - required int confirmations}) { + required int confirmations, + String? to, + this.unspent}) { this.id = id; this.height = height; this.amount = amount; @@ -37,6 +40,7 @@ class ElectrumTransactionInfo extends TransactionInfo { this.date = date; this.isPending = isPending; this.confirmations = confirmations; + this.to = to; } factory ElectrumTransactionInfo.fromElectrumVerbose(Map obj, WalletType type, @@ -144,50 +148,24 @@ class ElectrumTransactionInfo extends TransactionInfo { confirmations: bundle.confirmations); } - factory ElectrumTransactionInfo.fromHexAndHeader(WalletType type, String hex, - {List? addresses, required int height, int? timestamp, required int confirmations}) { - final tx = bitcoin.Transaction.fromHex(hex); - var exist = false; - var amount = 0; - - if (addresses != null) { - tx.outs.forEach((out) { - try { - final p2pkh = - bitcoin.P2PKH(data: PaymentData(output: out.script), network: bitcoin.bitcoin); - exist = addresses.contains(p2pkh.data.address); - - if (exist) { - amount += out.value!; - } - } catch (_) {} - }); - } - - final date = - timestamp != null ? DateTime.fromMillisecondsSinceEpoch(timestamp * 1000) : DateTime.now(); - - return ElectrumTransactionInfo(type, - id: tx.getId(), - height: height, - isPending: false, - fee: null, - direction: TransactionDirection.incoming, - amount: amount, - date: date, - confirmations: confirmations); - } - factory ElectrumTransactionInfo.fromJson(Map data, WalletType type) { - return ElectrumTransactionInfo(type, - id: data['id'] as String, - height: data['height'] as int, - amount: data['amount'] as int, - fee: data['fee'] as int, - direction: parseTransactionDirectionFromInt(data['direction'] as int), - date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int), - isPending: data['isPending'] as bool, - confirmations: data['confirmations'] as int); + return ElectrumTransactionInfo( + type, + id: data['id'] as String, + height: data['height'] as int, + amount: data['amount'] as int, + fee: data['fee'] as int, + direction: parseTransactionDirectionFromInt(data['direction'] as int), + date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int), + isPending: data['isPending'] as bool, + confirmations: data['confirmations'] as int, + to: data['to'] as String?, + unspent: data['unspent'] != null + ? BitcoinUnspent.fromJSON( + BitcoinAddressRecord.fromJSON(data['unspent']['address_record'] as String), + data['unspent'] as Map) + : null, + ); } final WalletType type; @@ -231,6 +209,12 @@ class ElectrumTransactionInfo extends TransactionInfo { m['isPending'] = isPending; m['confirmations'] = confirmations; m['fee'] = fee; + m['to'] = to; + m['unspent'] = unspent?.toJson() ?? {}; return m; } + + String toString() { + return 'ElectrumTransactionInfo(id: $id, height: $height, amount: $amount, fee: $fee, direction: $direction, date: $date, isPending: $isPending, confirmations: $confirmations, to: $to, unspent: $unspent)'; + } } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 873fe2977a..4bef9b7489 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:isolate'; import 'dart:math'; import 'package:bitcoin_base/bitcoin_base.dart'; @@ -143,21 +144,93 @@ abstract class ElectrumWalletBase List unspentCoins; List _feeRates; Map?> _scripthashesUpdateSubject; + BehaviorSubject? _chainTipUpdateSubject; bool _isTransactionUpdating; + // Future? _isolate; void Function(FlutterErrorDetails)? _onError; + Timer? _autoSaveTimer; + static const int _autoSaveInterval = 30; Future init() async { await walletAddresses.init(); await transactionHistory.init(); - await save(); + + _autoSaveTimer = + Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save()); } + // @action + // Future _setListeners(int height, {int? chainTip}) async { + // final currentChainTip = chainTip ?? await electrumClient.getCurrentBlockChainTip() ?? 0; + // syncStatus = AttemptingSyncStatus(); + + // if (_isolate != null) { + // final runningIsolate = await _isolate!; + // runningIsolate.kill(priority: Isolate.immediate); + // } + + // final receivePort = ReceivePort(); + // _isolate = Isolate.spawn( + // startRefresh, + // ScanData( + // sendPort: receivePort.sendPort, + // primarySilentAddress: walletAddresses.primarySilentAddress!, + // networkType: networkType, + // height: height, + // chainTip: currentChainTip, + // electrumClient: ElectrumClient(), + // transactionHistoryIds: transactionHistory.transactions.keys.toList(), + // node: electrumClient.uri.toString(), + // labels: walletAddresses.labels, + // )); + + // await for (var message in receivePort) { + // if (message is BitcoinUnspent) { + // if (!unspentCoins.any((utx) => + // utx.hash.contains(message.hash) && + // utx.vout == message.vout && + // utx.address.contains(message.address))) { + // unspentCoins.add(message); + + // if (unspentCoinsInfo.values.any((element) => + // element.walletId.contains(id) && + // element.hash.contains(message.hash) && + // element.address.contains(message.address))) { + // _addCoinInfo(message); + + // await walletInfo.save(); + // await save(); + // } + + // balance[currency] = await _fetchBalances(); + // } + // } + + // if (message is Map) { + // transactionHistory.addMany(message); + // await transactionHistory.save(); + // } + + // // check if is a SyncStatus type since "is SyncStatus" doesn't work here + // if (message is SyncResponse) { + // syncStatus = message.syncStatus; + // walletInfo.restoreHeight = message.height; + // await walletInfo.save(); + // } + // } + // } + @action @override Future startSync() async { try { - syncStatus = AttemptingSyncStatus(); + await _setInitialHeight(); + } catch (_) {} + + try { + rescan(height: walletInfo.restoreHeight); + await updateTransactions(); _subscribeForUpdates(); await updateUnspent(); @@ -187,6 +260,12 @@ abstract class ElectrumWalletBase } }; syncStatus = ConnectedSyncStatus(); + + // final currentChainTip = await electrumClient.getCurrentBlockChainTip(); + + // if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { + // _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); + // } } catch (e) { print(e.toString()); syncStatus = FailedSyncStatus(); @@ -213,6 +292,124 @@ abstract class ElectrumWalletBase allInputsAmount += utx.value; leftAmount = leftAmount - utx.value; + if (utx.bitcoinAddressRecord.silentPaymentTweak != null) { + // final d = ECPrivate.fromHex(walletAddresses.primarySilentAddress!.spendPrivkey.toHex()) + // .tweakAdd(utx.bitcoinAddressRecord.silentPaymentTweak!)!; + + // inputPrivKeys.add(bitcoin.PrivateKeyInfo(d, true)); + // address = bitcoin.P2trAddress(address: utx.address, networkType: networkType); + // keyPairs.add(bitcoin.ECPair.fromPrivateKey(d.toCompressedHex().fromHex, + // compressed: true, network: networkType)); + // scriptType = bitcoin.AddressType.p2tr; + // script = bitcoin.P2trAddress(pubkey: d.publicKey.toHex(), networkType: networkType) + // .scriptPubkey + // .toBytes(); + } + + final address = _addressTypeFromStr(utx.address, network); + final privkey = generateECPrivate( + hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd, + index: utx.bitcoinAddressRecord.index, + network: network); + + privateKeys.add(privkey); + + utxos.add( + UtxoWithAddress( + utxo: BitcoinUtxo( + txHash: utx.hash, + value: BigInt.from(utx.value), + vout: utx.vout, + scriptType: _getScriptType(address), + ), + ownerDetails: + UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: address), + ), + ); + + bool amountIsAcquired = !sendAll && leftAmount <= 0; + if ((inputsCount == null && amountIsAcquired) || inputsCount == i + 1) { + break; + } + } + } + + if (inputs.isEmpty) { + throw BitcoinTransactionNoInputsException(); + } + + final allAmountFee = transactionCredentials.feeRate != null + ? feeAmountWithFeeRate(transactionCredentials.feeRate!, inputs.length, outputs.length) + : feeAmountForPriority(transactionCredentials.priority!, inputs.length, outputs.length); + + final allAmount = allInputsAmount - allAmountFee; + + var credentialsAmount = 0; + var amount = 0; + var fee = 0; + + if (hasMultiDestination) { + if (outputs.any((item) => item.sendAll || item.formattedCryptoAmount! <= 0)) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + credentialsAmount = outputs.fold(0, (acc, value) => acc + value.formattedCryptoAmount!); + + if (allAmount - credentialsAmount < minAmount) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + amount = credentialsAmount; + + if (transactionCredentials.feeRate != null) { + fee = calculateEstimatedFeeWithFeeRate(transactionCredentials.feeRate!, amount, + outputsCount: outputs.length + 1); + } else { + fee = calculateEstimatedFee(transactionCredentials.priority, amount, + outputsCount: outputs.length + 1); + } + } else { + final output = outputs.first; + credentialsAmount = !output.sendAll ? output.formattedCryptoAmount! : 0; + + if (credentialsAmount > allAmount) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + amount = output.sendAll || allAmount - credentialsAmount < minAmount + ? allAmount + : credentialsAmount; + + if (output.sendAll || amount == allAmount) { + fee = allAmountFee; + } else if (transactionCredentials.feeRate != null) { + fee = calculateEstimatedFeeWithFeeRate(transactionCredentials.feeRate!, amount); + } else { + fee = calculateEstimatedFee(transactionCredentials.priority, amount); + } + } + + if (fee == 0) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + final totalAmount = amount + fee; + + if (totalAmount > balance[currency]!.confirmed || totalAmount > allInputsAmount) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + final txb = bitcoin.TransactionBuilder(network: networkType); + final changeAddress = await walletAddresses.getChangeAddress(); + var leftAmount = totalAmount; + var totalInputAmount = 0; + + inputs.clear(); + + for (final utx in unspentCoins) { + if (utx.isSending) { + leftAmount = leftAmount - utx.value; + final address = _addressTypeFromStr(utx.address, network); final privkey = generateECPrivate( hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd, @@ -304,23 +501,22 @@ abstract class ElectrumWalletBase } } - return EstimatedTxResult(utxos: utxos, privateKeys: privateKeys, fee: fee, amount: amount); - } - - @override - Future createTransaction(Object credentials) async { - try { - final outputs = []; - final outputAddresses = []; - final transactionCredentials = credentials as BitcoinTransactionCredentials; - final hasMultiDestination = transactionCredentials.outputs.length > 1; - final sendAll = !hasMultiDestination && transactionCredentials.outputs.first.sendAll; - - var credentialsAmount = 0; - - for (final out in transactionCredentials.outputs) { - final outputAddress = out.isParsedAddress ? out.extractedAddress! : out.address; - final address = _addressTypeFromStr(outputAddress, network); + if (SilentPaymentAddress.regex.hasMatch(outputAddress)) { + // final outpointsHash = SilentPayment.hashOutpoints(outpoints); + // final generatedOutputs = SilentPayment.generateMultipleRecipientPubkeys(inputPrivKeys, + // outpointsHash, SilentPaymentDestination.fromAddress(outputAddress, outputAmount!)); + + // generatedOutputs.forEach((recipientSilentAddress, generatedOutput) { + // generatedOutput.forEach((output) { + // outputs.add(BitcoinOutputDetails( + // address: P2trAddress( + // program: ECPublic.fromHex(output.$1.toHex()).toTapPoint(), + // networkType: networkType), + // value: BigInt.from(output.$2), + // )); + // }); + // }); + } outputAddresses.add(address); @@ -392,6 +588,8 @@ abstract class ElectrumWalletBase ? SegwitAddresType.p2wpkh.toString() : walletInfo.addressPageType.toString(), 'balance': balance[currency]?.toJSON(), + 'silent_addresses': walletAddresses.silentAddresses.map((addr) => addr.toJSON()).toList(), + 'silent_address_index': walletAddresses.currentSilentAddressIndex.toString(), 'network_type': network == BitcoinNetwork.testnet ? 'testnet' : 'mainnet', }); @@ -498,18 +696,31 @@ abstract class ElectrumWalletBase } @override - Future rescan({required int height}) async => throw UnimplementedError(); + Future rescan({required int height, int? chainTip, ScanData? scanData}) async { + // _setListeners(height); + } @override Future close() async { try { await electrumClient.close(); } catch (_) {} + _autoSaveTimer?.cancel(); } Future makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); Future updateUnspent() async { + // Update unspents stored from scanned silent payment transactions + transactionHistory.transactions.values.forEach((tx) { + if (tx.unspent != null) { + if (!unspentCoins + .any((utx) => utx.hash.contains(tx.unspent!.hash) && utx.vout == tx.unspent!.vout)) { + unspentCoins.add(tx.unspent!); + } + } + }); + List updatedUnspentCoins = []; final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet(); @@ -538,7 +749,7 @@ abstract class ElectrumWalletBase final coinInfoList = unspentCoinsInfo.values.where((element) => element.walletId.contains(id) && element.hash.contains(coin.hash) && - element.vout == coin.vout); + element.address.contains(coin.address)); if (coinInfoList.isNotEmpty) { final coinInfo = coinInfoList.first; @@ -555,6 +766,7 @@ abstract class ElectrumWalletBase await _refreshUnspentCoinsInfo(); } + @action Future _addCoinInfo(BitcoinUnspent coin) async { final newInfo = UnspentCoinsInfo( walletId: id, @@ -619,19 +831,17 @@ abstract class ElectrumWalletBase confirmations = verboseTransaction['confirmations'] as int? ?? 0; } - final original = bitcoin_base.BtcTransaction.fromRaw(transactionHex); - final ins = []; + final original = BtcTransaction.fromRaw(transactionHex); + final ins = []; for (final vin in original.inputs) { try { final id = HEX.encode(HEX.decode(vin.txId).reversed.toList()); final txHex = await electrumClient.getTransactionHex(hash: id); - final tx = bitcoin_base.BtcTransaction.fromRaw(txHex); + final tx = BtcTransaction.fromRaw(txHex); ins.add(tx); } catch (_) { - ins.add(bitcoin_base.BtcTransaction.fromRaw( - await electrumClient.getTransactionHex(hash: vin.txId), - )); + ins.add(BtcTransaction.fromRaw(await electrumClient.getTransactionHex(hash: vin.txId))); } } @@ -767,7 +977,7 @@ abstract class ElectrumWalletBase } } - void _subscribeForUpdates() { + void _subscribeForUpdates() async { scriptHashes.forEach((sh) async { await _scripthashesUpdateSubject[sh]?.close(); _scripthashesUpdateSubject[sh] = electrumClient.scripthashUpdate(sh); @@ -786,6 +996,23 @@ abstract class ElectrumWalletBase } }); }); + + await _chainTipUpdateSubject?.close(); + _chainTipUpdateSubject = electrumClient.chainTipUpdate(); + _chainTipUpdateSubject?.listen((_) async { + try { + final currentHeight = await electrumClient.getCurrentBlockChainTip(); + if (currentHeight != null) walletInfo.restoreHeight = currentHeight; + // _setListeners(walletInfo.restoreHeight, chainTip: currentHeight); + } catch (e, s) { + print(e.toString()); + _onError?.call(FlutterErrorDetails( + exception: e, + stack: s, + library: this.runtimeType.toString(), + )); + } + }); } Future _fetchBalances() async { @@ -799,21 +1026,25 @@ abstract class ElectrumWalletBase } var totalFrozen = 0; + var totalConfirmed = 0; + var totalUnconfirmed = 0; + + // Add values from unspent coins that are not fetched by the address list + // i.e. scanned silent payments unspentCoinsInfo.values.forEach((info) { unspentCoins.forEach((element) { if (element.hash == info.hash && - element.vout == info.vout && - info.isFrozen && element.bitcoinAddressRecord.address == info.address && element.value == info.value) { - totalFrozen += element.value; + if (info.isFrozen) totalFrozen += element.value; + if (element.bitcoinAddressRecord.silentPaymentTweak != null) { + totalConfirmed += element.value; + } } }); }); final balances = await Future.wait(balanceFutures); - var totalConfirmed = 0; - var totalUnconfirmed = 0; for (var i = 0; i < balances.length; i++) { final addressRecord = addresses[i]; @@ -860,6 +1091,428 @@ abstract class ElectrumWalletBase final HD = index == null ? hd : hd.derive(index); return base64Encode(HD.signMessage(message)); } + + Future _setInitialHeight() async { + if (walletInfo.isRecovery) { + return; + } + + if (walletInfo.restoreHeight == 0) { + final currentHeight = await electrumClient.getCurrentBlockChainTip(); + if (currentHeight != null) walletInfo.restoreHeight = currentHeight; + } + } +} + +class ScanData { + final SendPort sendPort; + final SilentPaymentReceiver primarySilentAddress; + final int height; + final String node; + final bitcoin.NetworkType networkType; + final int chainTip; + final ElectrumClient electrumClient; + final List transactionHistoryIds; + final Map labels; + + ScanData({ + required this.sendPort, + required this.primarySilentAddress, + required this.height, + required this.node, + required this.networkType, + required this.chainTip, + required this.electrumClient, + required this.transactionHistoryIds, + required this.labels, + }); + + factory ScanData.fromHeight(ScanData scanData, int newHeight) { + return ScanData( + sendPort: scanData.sendPort, + primarySilentAddress: scanData.primarySilentAddress, + height: newHeight, + node: scanData.node, + networkType: scanData.networkType, + chainTip: scanData.chainTip, + transactionHistoryIds: scanData.transactionHistoryIds, + electrumClient: scanData.electrumClient, + labels: scanData.labels, + ); + } +} + +class SyncResponse { + final int height; + final SyncStatus syncStatus; + + SyncResponse(this.height, this.syncStatus); +} + +// Future startRefresh(ScanData scanData) async { +// var cachedBlockchainHeight = scanData.chainTip; + +// Future getNodeHeightOrUpdate(int baseHeight) async { +// if (cachedBlockchainHeight < baseHeight || cachedBlockchainHeight == 0) { +// final electrumClient = scanData.electrumClient; +// if (!electrumClient.isConnected) { +// final node = scanData.node; +// await electrumClient.connectToUri(Uri.parse(node)); +// } + +// cachedBlockchainHeight = +// await electrumClient.getCurrentBlockChainTip() ?? cachedBlockchainHeight; +// } + +// return cachedBlockchainHeight; +// } + +// var lastKnownBlockHeight = 0; +// var initialSyncHeight = 0; + +// var syncHeight = scanData.height; +// var currentChainTip = scanData.chainTip; + +// if (syncHeight <= 0) { +// syncHeight = currentChainTip; +// } + +// if (initialSyncHeight <= 0) { +// initialSyncHeight = syncHeight; +// } + +// if (lastKnownBlockHeight == syncHeight) { +// scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus())); +// return; +// } + +// // Run this until no more blocks left to scan txs. At first this was recursive +// // i.e. re-calling the startRefresh function but this was easier for the above values to retain +// // their initial values +// while (true) { +// lastKnownBlockHeight = syncHeight; + +// final syncingStatus = +// SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight); +// scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus)); + +// if (syncingStatus.blocksLeft <= 0) { +// scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus())); +// return; +// } + +// // print(["Scanning from height:", syncHeight]); + +// try { +// final networkPath = +// scanData.networkType.network == bitcoin.BtcNetwork.mainnet ? "" : "/testnet"; + +// // This endpoint gets up to 10 latest blocks from the given height +// final tenNewestBlocks = +// (await http.get(Uri.parse("https://blockstream.info$networkPath/api/blocks/$syncHeight"))) +// .body; +// var decodedBlocks = json.decode(tenNewestBlocks) as List; + +// decodedBlocks.sort((a, b) => (a["height"] as int).compareTo(b["height"] as int)); +// decodedBlocks = +// decodedBlocks.where((element) => (element["height"] as int) >= syncHeight).toList(); + +// // for each block, get up to 25 txs +// for (var i = 0; i < decodedBlocks.length; i++) { +// final blockJson = decodedBlocks[i]; +// final blockHash = blockJson["id"]; +// final txCount = blockJson["tx_count"] as int; + +// // print(["Scanning block index:", i, "with tx count:", txCount]); + +// int startIndex = 0; +// // go through each tx in block until no more txs are left +// while (startIndex < txCount) { +// // This endpoint gets up to 25 txs from the given block hash and start index +// final twentyFiveTxs = json.decode((await http.get(Uri.parse( +// "https://blockstream.info$networkPath/api/block/$blockHash/txs/$startIndex"))) +// .body) as List; + +// // print(["Scanning txs index:", startIndex]); + +// // For each tx, apply silent payment filtering and do shared secret calculation when applied +// for (var i = 0; i < twentyFiveTxs.length; i++) { +// try { +// final tx = twentyFiveTxs[i]; +// final txid = tx["txid"] as String; + +// // print(["Scanning tx:", txid]); + +// // TODO: if tx already scanned & stored skip +// // if (scanData.transactionHistoryIds.contains(txid)) { +// // // already scanned tx, continue to next tx +// // pos++; +// // continue; +// // } + +// List pubkeys = []; +// List outpoints = []; + +// bool skip = false; + +// for (var i = 0; i < (tx["vin"] as List).length; i++) { +// final input = tx["vin"][i]; +// final prevout = input["prevout"]; +// final scriptPubkeyType = prevout["scriptpubkey_type"]; +// String? pubkey; + +// if (scriptPubkeyType == "v0_p2wpkh" || scriptPubkeyType == "v1_p2tr") { +// final witness = input["witness"]; +// if (witness == null) { +// skip = true; +// // print("Skipping, no witness"); +// break; +// } + +// if (witness.length == 2) { +// pubkey = witness[1] as String; +// } else if (witness.length == 1) { +// pubkey = "02" + (prevout["scriptpubkey"] as String).fromHex.sublist(2).hex; +// } +// } + +// if (scriptPubkeyType == "p2pkh") { +// pubkey = bitcoin.P2pkhAddress( +// scriptSig: bitcoin.Script.fromRaw(hexData: input["scriptsig"] as String)) +// .pubkey; +// } + +// if (pubkey == null) { +// skip = true; +// // print("Skipping, invalid witness"); +// break; +// } + +// pubkeys.add(pubkey); +// outpoints.add( +// bitcoin.Outpoint(txid: input["txid"] as String, index: input["vout"] as int)); +// } + +// if (skip) { +// // skipped tx, continue to next tx +// continue; +// } + +// Map outpointsByP2TRpubkey = {}; +// for (var i = 0; i < (tx["vout"] as List).length; i++) { +// final output = tx["vout"][i]; +// if (output["scriptpubkey_type"] != "v1_p2tr") { +// // print("Skipping, not a v1_p2tr output"); +// continue; +// } + +// final script = (output["scriptpubkey"] as String).fromHex; + +// // final alreadySpentOutput = (await electrumClient.getHistory( +// // scriptHashFromScript(script, networkType: scanData.networkType))) +// // .length > +// // 1; + +// // if (alreadySpentOutput) { +// // print("Skipping, invalid witness"); +// // break; +// // } + +// final p2tr = bitcoin.P2trAddress( +// program: script.sublist(2).hex, networkType: scanData.networkType); +// final address = p2tr.address; + +// print(["Verifying taproot address:", address]); + +// outpointsByP2TRpubkey[script.sublist(2).hex] = +// bitcoin.Outpoint(txid: txid, index: i, value: output["value"] as int); +// } + +// if (pubkeys.isEmpty || outpoints.isEmpty || outpointsByP2TRpubkey.isEmpty) { +// // skipped tx, continue to next tx +// continue; +// } + +// final outpointHash = bitcoin.SilentPayment.hashOutpoints(outpoints); + +// final result = bitcoin.scanOutputs( +// scanData.primarySilentAddress.scanPrivkey, +// scanData.primarySilentAddress.spendPubkey, +// bitcoin.getSumInputPubKeys(pubkeys), +// outpointHash, +// outpointsByP2TRpubkey.keys.map((e) => e.fromHex).toList(), +// labels: scanData.labels, +// ); + +// if (result.isEmpty) { +// // no results tx, continue to next tx +// continue; +// } + +// if (result.length > 1) { +// print("MULTIPLE UNSPENT COINS FOUND!"); +// } else { +// print("UNSPENT COIN FOUND!"); +// } + +// result.forEach((key, value) async { +// final outpoint = outpointsByP2TRpubkey[key]; + +// if (outpoint == null) { +// return; +// } + +// final tweak = value[0]; +// String? label; +// if (value.length > 1) label = value[1]; + +// final txInfo = ElectrumTransactionInfo( +// WalletType.bitcoin, +// id: txid, +// height: syncHeight, +// amount: outpoint.value!, +// fee: 0, +// direction: TransactionDirection.incoming, +// isPending: false, +// date: DateTime.fromMillisecondsSinceEpoch((blockJson["timestamp"] as int) * 1000), +// confirmations: currentChainTip - syncHeight, +// to: bitcoin.SilentPaymentAddress.createLabeledSilentPaymentAddress( +// scanData.primarySilentAddress.scanPubkey, +// scanData.primarySilentAddress.spendPubkey, +// label != null ? label.fromHex : "0".fromHex, +// hrp: scanData.primarySilentAddress.hrp, +// version: scanData.primarySilentAddress.version) +// .toString(), +// unspent: null, +// ); + +// final status = json.decode((await http +// .get(Uri.parse("https://blockstream.info/testnet/api/tx/$txid/outspends"))) +// .body) as List; + +// bool spent = false; +// for (final s in status) { +// if ((s["spent"] as bool) == true) { +// spent = true; + +// scanData.sendPort.send({txid: txInfo}); + +// final sentTxId = s["txid"] as String; +// final sentTx = json.decode((await http +// .get(Uri.parse("https://blockstream.info/testnet/api/tx/$sentTxId"))) +// .body); + +// int amount = 0; +// for (final out in (sentTx["vout"] as List)) { +// amount += out["value"] as int; +// } + +// final height = s["status"]["block_height"] as int; + +// scanData.sendPort.send({ +// sentTxId: ElectrumTransactionInfo( +// WalletType.bitcoin, +// id: sentTxId, +// height: height, +// amount: amount, +// fee: 0, +// direction: TransactionDirection.outgoing, +// isPending: false, +// date: DateTime.fromMillisecondsSinceEpoch( +// (s["status"]["block_time"] as int) * 1000), +// confirmations: currentChainTip - height, +// ) +// }); +// } +// } + +// if (spent) { +// return; +// } + +// final unspent = BitcoinUnspent( +// BitcoinAddressRecord( +// bitcoin.P2trAddress(program: key, networkType: scanData.networkType).address, +// index: 0, +// isHidden: true, +// isUsed: true, +// silentAddressLabel: null, +// silentPaymentTweak: tweak, +// type: bitcoin.AddressType.p2tr, +// ), +// txid, +// outpoint.value!, +// outpoint.index, +// silentPaymentTweak: tweak, +// type: bitcoin.AddressType.p2tr, +// ); + +// // found utxo for tx, send unspent coin to main isolate +// scanData.sendPort.send(unspent); + +// // also send tx data for tx history +// txInfo.unspent = unspent; +// scanData.sendPort.send({txid: txInfo}); +// }); +// } catch (_) {} +// } + +// // Finished scanning batch of txs in block, add 25 to start index and continue to next block in loop +// startIndex += 25; +// } + +// // Finished scanning block, add 1 to height and continue to next block in loop +// syncHeight += 1; +// currentChainTip = await getNodeHeightOrUpdate(syncHeight); +// scanData.sendPort.send(SyncResponse(syncHeight, +// SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); +// } +// } catch (e, stacktrace) { +// print(stacktrace); +// print(e.toString()); + +// scanData.sendPort.send(SyncResponse(syncHeight, NotConnectedSyncStatus())); +// break; +// } +// } +// } + +class EstimatedTxResult { + EstimatedTxResult( + {required this.utxos, required this.privateKeys, required this.fee, required this.amount}); + + final List utxos; + final List privateKeys; + final int fee; + final int amount; +} + +BitcoinBaseAddress _addressTypeFromStr(String address, BasedUtxoNetwork network) { + if (P2pkhAddress.regex.hasMatch(address)) { + return P2pkhAddress.fromAddress(address: address, network: network); + } else if (P2shAddress.regex.hasMatch(address)) { + return P2shAddress.fromAddress(address: address, network: network); + } else if (P2wshAddress.regex.hasMatch(address)) { + return P2wshAddress.fromAddress(address: address, network: network); + } else if (P2trAddress.regex.hasMatch(address)) { + return P2trAddress.fromAddress(address: address, network: network); + } else { + return P2wpkhAddress.fromAddress(address: address, network: network); + } +} + +BitcoinAddressType _getScriptType(BitcoinBaseAddress type) { + if (type is P2pkhAddress) { + return P2pkhAddressType.p2pkh; + } else if (type is P2shAddress) { + return P2shAddressType.p2wpkhInP2sh; + } else if (type is P2wshAddress) { + return SegwitAddresType.p2wsh; + } else if (type is P2trAddress) { + return SegwitAddresType.p2tr; + } else { + return SegwitAddresType.p2wpkh; + } } class EstimateTxParams { diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 5880f5a19b..23482e4d70 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -2,7 +2,6 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bitbox/bitbox.dart' as bitbox; import 'package:cw_bitcoin/bitcoin_address_record.dart'; -import 'package:cw_bitcoin/electrum.dart'; import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; @@ -25,12 +24,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { WalletInfo walletInfo, { required this.mainHd, required this.sideHd, - required this.electrumClient, required this.network, List? initialAddresses, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, + List? initialSilentAddresses, + int initialSilentAddressIndex = 0, + SilentPaymentOwner? silentAddress, }) : _addresses = ObservableList.of((initialAddresses ?? []).toSet()), + primarySilentAddress = silentAddress, addressesByReceiveType = ObservableList.of(([]).toSet()), receiveAddresses = ObservableList.of((initialAddresses ?? []) @@ -44,6 +46,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { _addressPageType = walletInfo.addressPageType != null ? BitcoinAddressType.fromValue(walletInfo.addressPageType!) : SegwitAddresType.p2wpkh, + silentAddresses = ObservableList.of((initialSilentAddresses ?? []) + .where((addressRecord) => addressRecord.silentPaymentTweak != null) + .toSet()), + currentSilentAddressIndex = initialSilentAddressIndex, super(walletInfo) { updateAddressesByMatch(); } @@ -61,27 +67,57 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { late ObservableList addressesByReceiveType; final ObservableList receiveAddresses; final ObservableList changeAddresses; - final ElectrumClient electrumClient; + final ObservableList silentAddresses; final BasedUtxoNetwork network; final bitcoin.HDWallet mainHd; final bitcoin.HDWallet sideHd; + final SilentPaymentOwner? primarySilentAddress; + @observable BitcoinAddressType _addressPageType = SegwitAddresType.p2wpkh; @computed BitcoinAddressType get addressPageType => _addressPageType; + @observable + String? activeSilentAddress; + @computed List get allAddresses => _addresses; @override @computed String get address { + if (addressPageType == SilentPaymentsAddresType.p2sp) { + if (activeSilentAddress != null) { + return activeSilentAddress!; + } + + return primarySilentAddress!.toString(); + } + String receiveAddress; final typeMatchingReceiveAddresses = receiveAddresses.where(_isAddressPageTypeMatch); + if ((isEnabledAutoGenerateSubaddress && receiveAddresses.isEmpty) || + typeMatchingReceiveAddresses.isEmpty) { + receiveAddress = generateNewAddress().address; + } else { + final previousAddressMatchesType = + previousAddressRecord != null && previousAddressRecord!.type == addressPageType; + + if (previousAddressMatchesType && + typeMatchingReceiveAddresses.first.address != addressesByReceiveType.first.address) { + receiveAddress = previousAddressRecord!.address; + } else { + receiveAddress = typeMatchingReceiveAddresses.first.address; + } + final receiveAddress = receiveAddresses.first.address; + + final typeMatchingReceiveAddresses = receiveAddresses.where(_isAddressPageTypeMatch); + if ((isEnabledAutoGenerateSubaddress && receiveAddresses.isEmpty) || typeMatchingReceiveAddresses.isEmpty) { receiveAddress = generateNewAddress().address; @@ -105,6 +141,11 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @override set address(String addr) { + if (addressPageType == SilentPaymentsAddresType.p2sp) { + activeSilentAddress = addr; + return; + } + if (addr.startsWith('bitcoincash:')) { addr = toLegacy(addr); } @@ -134,6 +175,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { void set currentChangeAddressIndex(int index) => currentChangeAddressIndexByType[_addressPageType.toString()] = index; + int currentSilentAddressIndex; + @observable BitcoinAddressRecord? previousAddressRecord; @@ -195,7 +238,43 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return address; } + Map get labels { + final labels = {}; + for (int i = 0; i < silentAddresses.length; i++) { + final silentAddressRecord = silentAddresses[i]; + final silentAddress = + SilentPaymentDestination.fromAddress(silentAddressRecord.address, 0).spendPubkey.toHex(); + + if (silentAddressRecord.silentPaymentTweak != null) + labels[silentAddress] = silentAddressRecord.silentPaymentTweak!; + } + return labels; + } + BitcoinAddressRecord generateNewAddress({String label = ''}) { + if (addressPageType == SilentPaymentsAddresType.p2sp) { + currentSilentAddressIndex += 1; + + final tweak = BigInt.from(currentSilentAddressIndex); + + final address = BitcoinAddressRecord( + SilentPaymentAddress.createLabeledSilentPaymentAddress( + primarySilentAddress!.scanPubkey, primarySilentAddress!.spendPubkey, tweak, + hrp: primarySilentAddress!.hrp, version: primarySilentAddress!.version) + .toString(), + index: currentSilentAddressIndex, + isHidden: false, + name: label, + silentPaymentTweak: tweak.toString(), + network: network, + type: SilentPaymentsAddresType.p2sp, + ); + + silentAddresses.add(address); + + return address; + } + final newAddressIndex = addressesByReceiveType.fold( 0, (int acc, addressRecord) => addressRecord.isHidden == false ? acc + 1 : acc); diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index 98c3753db3..ceb603f9f3 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -13,11 +13,13 @@ class ElectrumWalletSnapshot { required this.password, required this.mnemonic, required this.addresses, + required this.silentAddresses, required this.balance, required this.regularAddressIndex, required this.changeAddressIndex, required this.addressPageType, required this.network, + required this.silentAddressIndex, }); final String name; @@ -28,24 +30,36 @@ class ElectrumWalletSnapshot { String mnemonic; List addresses; + List silentAddresses; ElectrumBalance balance; Map regularAddressIndex; Map changeAddressIndex; + int silentAddressIndex; - static Future load(String name, WalletType type, String password, BasedUtxoNetwork? network) async { + static Future load( + String name, WalletType type, String password, BasedUtxoNetwork? network) async { final path = await pathForWallet(name: name, type: type); final jsonSource = await read(path: path, password: password); final data = json.decode(jsonSource) as Map; - final addressesTmp = data['addresses'] as List? ?? []; final mnemonic = data['mnemonic'] as String; + + final addressesTmp = data['addresses'] as List? ?? []; final addresses = addressesTmp .whereType() - .map((addr) => BitcoinAddressRecord.fromJSON(addr, network)) + .map((addr) => BitcoinAddressRecord.fromJSON(addr, network: network)) .toList(); + + final silentAddressesTmp = data['silent_addresses'] as List? ?? []; + final silentAddresses = silentAddressesTmp + .whereType() + .map((addr) => BitcoinAddressRecord.fromJSON(addr, network: network)) + .toList(); + final balance = ElectrumBalance.fromJSON(data['balance'] as String) ?? ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0); var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; + var silentAddressIndex = 0; try { regularAddressIndexByType = { @@ -55,6 +69,7 @@ class ElectrumWalletSnapshot { SegwitAddresType.p2wpkh.toString(): int.parse(data['change_address_index'] as String? ?? '0') }; + silentAddressIndex = int.parse(data['silent_address_index'] as String? ?? '0'); } catch (_) { try { regularAddressIndexByType = data["account_index"] as Map? ?? {}; @@ -68,11 +83,13 @@ class ElectrumWalletSnapshot { password: password, mnemonic: mnemonic, addresses: addresses, + silentAddresses: silentAddresses, balance: balance, regularAddressIndex: regularAddressIndexByType, changeAddressIndex: changeAddressIndexByType, addressPageType: data['address_page_type'] as String? ?? SegwitAddresType.p2wpkh.toString(), network: data['network_type'] == 'testnet' ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet, + silentAddressIndex: silentAddressIndex, ); } } diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 25e6f269d8..aff28df6eb 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -79,8 +79,8 @@ packages: dependency: "direct main" description: path: "." - ref: cake-update-v1 - resolved-ref: "9611e9db77e92a8434e918cdfb620068f6fcb1aa" + ref: cake-update-v2 + resolved-ref: e4686da77cace5400697de69f7885020297cb900 url: "https://github.com/cake-tech/bitcoin_base.git" source: git version: "4.0.0" @@ -93,14 +93,6 @@ packages: url: "https://github.com/cake-tech/bitcoin_flutter.git" source: git version: "2.1.0" - blockchain_utils: - dependency: "direct main" - description: - name: blockchain_utils - sha256: "9701dfaa74caad4daae1785f1ec4445cf7fb94e45620bc3a4aca1b9b281dc6c9" - url: "https://pub.dev" - source: hosted - version: "1.6.0" boolean_selector: dependency: transitive description: diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 847b777731..b6acab7f48 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -33,8 +33,7 @@ dependencies: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v1 - blockchain_utils: ^1.6.0 + ref: cake-update-v2 dev_dependencies: flutter_test: diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index 9c098c0ff3..7130b3c58f 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -32,7 +32,7 @@ dependencies: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v1 + ref: cake-update-v2 diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index 4983967d0b..afddc7c7a7 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -14,6 +14,16 @@ class SyncingSyncStatus extends SyncStatus { @override String toString() => '$blocksLeft'; + + factory SyncingSyncStatus.fromHeightValues(int chainTip, int initialSyncHeight, int syncHeight) { + final track = chainTip - initialSyncHeight; + final diff = track - (chainTip - syncHeight); + final ptc = diff <= 0 ? 0.0 : diff / track; + final left = chainTip - syncHeight; + + // sum 1 because if at the chain tip, will say "0 blocks left" + return SyncingSyncStatus(left + 1, ptc); + } } class SyncedSyncStatus extends SyncStatus { @@ -51,4 +61,6 @@ class ConnectedSyncStatus extends SyncStatus { class LostConnectionSyncStatus extends SyncStatus { @override double progress() => 1.0; -} \ No newline at end of file + @override + String toString() => 'Reconnecting'; +} diff --git a/cw_core/lib/unspent_transaction_output.dart b/cw_core/lib/unspent_transaction_output.dart index b52daf43cc..01b26cdcc6 100644 --- a/cw_core/lib/unspent_transaction_output.dart +++ b/cw_core/lib/unspent_transaction_output.dart @@ -16,5 +16,6 @@ class Unspent { bool isFrozen; String note; - bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc'); + bool get isP2wpkh => + address.startsWith('bc') || address.startsWith('tb') || address.startsWith('ltc'); } diff --git a/howto-build-android.md b/howto-build-android.md index a2a4e4d9f5..c3fe415ee3 100644 --- a/howto-build-android.md +++ b/howto-build-android.md @@ -142,27 +142,9 @@ Then we need to generate localization files. `$ flutter packages pub run tool/generate_localization.dart` -Lastly, we will generate mobx models for the project. - -Generate mobx models for `cw_core`: - -`cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..` - -Generate mobx models for `cw_monero`: - -`cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..` - -Generate mobx models for `cw_bitcoin`: - -`cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..` - -Generate mobx models for `cw_haven`: - -`cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..` - Finally build mobx models for the app: -`$ flutter packages pub run build_runner build --delete-conflicting-outputs` +`$ ./model_generator.sh` ### 9. Build! diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index f9c20d45ec..891c6298a2 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -188,4 +188,9 @@ class CWBitcoin extends Bitcoin { @override List getBitcoinReceivePageOptions() => BitcoinReceivePageOption.all; + + List getSilentAddresses(Object wallet) { + final bitcoinWallet = wallet as ElectrumWallet; + return bitcoinWallet.walletAddresses.silentAddresses; + } } diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 853762a1c8..19cdb16160 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -26,7 +26,7 @@ class AddressValidator extends TextValidator { return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$' '|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$'; case CryptoCurrency.btc: - return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$'; + return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${bitcoin.SilentPaymentAddress.REGEX.pattern}\$'; case CryptoCurrency.nano: return '[0-9a-zA-Z_]'; case CryptoCurrency.banano: @@ -274,7 +274,8 @@ class AddressValidator extends TextValidator { '([^0-9a-zA-Z]|^)${P2shAddress.regex.pattern}|\$)' '([^0-9a-zA-Z]|^)${P2wpkhAddress.regex.pattern}|\$)' '([^0-9a-zA-Z]|^)${P2wshAddress.regex.pattern}|\$)' - '([^0-9a-zA-Z]|^)${P2trAddress.regex.pattern}|\$)'; + '([^0-9a-zA-Z]|^)${P2trAddress.regex.pattern}|\$)' + '|${bitcoin.SilentPaymentAddress.REGEX.pattern}\$'; case CryptoCurrency.ltc: return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)' '|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)' diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index 66094de2b0..fbb86fa9fb 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -3,7 +3,9 @@ import 'package:cw_core/sync_status.dart'; String syncStatusTitle(SyncStatus syncStatus) { if (syncStatus is SyncingSyncStatus) { - return S.current.Blocks_remaining('${syncStatus.blocksLeft}'); + return syncStatus.blocksLeft == 1 + ? S.current.Block_remaining('${syncStatus.blocksLeft}') + : S.current.Blocks_remaining('${syncStatus.blocksLeft}'); } if (syncStatus is SyncedSyncStatus) { @@ -35,4 +37,4 @@ String syncStatusTitle(SyncStatus syncStatus) { } return ''; -} \ No newline at end of file +} diff --git a/lib/di.dart b/lib/di.dart index 473eaed00e..d2af49f4b2 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -229,6 +229,7 @@ import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart'; import 'package:cake_wallet/core/wallet_loading_service.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/entities/qr_view_data.dart'; +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as btc; import 'buy/dfx/dfx_buy_provider.dart'; import 'core/totp_request_details.dart'; @@ -657,6 +658,10 @@ Future setup({ getIt.registerFactory(() { final wallet = getIt.get().wallet!; + // if ((wallet.type == WalletType.bitcoin && + // wallet.walletAddresses.addressPageType == btc.AddressType.p2sp) || + // wallet.type == WalletType.monero || + // wallet.type == WalletType.haven) { if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) { return MoneroAccountListViewModel(wallet); } diff --git a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart index 33bceeb5c0..64125b1457 100644 --- a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart +++ b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart @@ -10,8 +10,7 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; class PresentReceiveOptionPicker extends StatelessWidget { - PresentReceiveOptionPicker( - {required this.receiveOptionViewModel, required this.color}); + PresentReceiveOptionPicker({required this.receiveOptionViewModel, required this.color}); final ReceiveOptionViewModel receiveOptionViewModel; final Color color; @@ -43,17 +42,11 @@ class PresentReceiveOptionPicker extends StatelessWidget { Text( S.current.receive, style: TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.bold, - fontFamily: 'Lato', - color: color), + fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: 'Lato', color: color), ), Observer( - builder: (_) => Text(receiveOptionViewModel.selectedReceiveOption.toString(), - style: TextStyle( - fontSize: 10.0, - fontWeight: FontWeight.w500, - color: color))) + builder: (_) => Text(describeOption(receiveOptionViewModel.selectedReceiveOption), + style: TextStyle(fontSize: 10.0, fontWeight: FontWeight.w500, color: color))) ], ), SizedBox(width: 5), @@ -73,65 +66,68 @@ class PresentReceiveOptionPicker extends StatelessWidget { backgroundColor: Colors.transparent, body: Stack( alignment: AlignmentDirectional.center, - children:[ AlertBackground( - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Spacer(), - Container( - margin: EdgeInsets.symmetric(horizontal: 24), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30), - color: Theme.of(context).colorScheme.background, - ), - child: Padding( - padding: const EdgeInsets.only(top: 24, bottom: 24), - child: (ListView.separated( - padding: EdgeInsets.zero, - shrinkWrap: true, - itemCount: receiveOptionViewModel.options.length, - itemBuilder: (_, index) { - final option = receiveOptionViewModel.options[index]; - return InkWell( - onTap: () { - Navigator.pop(popUpContext); + children: [ + AlertBackground( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Spacer(), + Container( + margin: EdgeInsets.symmetric(horizontal: 24), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + color: Theme.of(context).colorScheme.background, + ), + child: Padding( + padding: const EdgeInsets.only(top: 24, bottom: 24), + child: (ListView.separated( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: receiveOptionViewModel.options.length, + itemBuilder: (_, index) { + final option = receiveOptionViewModel.options[index]; + return InkWell( + onTap: () { + Navigator.pop(popUpContext); - receiveOptionViewModel.selectReceiveOption(option); - }, - child: Padding( - padding: const EdgeInsets.only(left: 24, right: 24), - child: Observer(builder: (_) { - final value = receiveOptionViewModel.selectedReceiveOption; + receiveOptionViewModel.selectReceiveOption(option); + }, + child: Padding( + padding: const EdgeInsets.only(left: 24, right: 24), + child: Observer(builder: (_) { + final value = receiveOptionViewModel.selectedReceiveOption; - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(option.toString(), - textAlign: TextAlign.left, - style: textSmall( - color: Theme.of(context).extension()!.titleColor, - ).copyWith( - fontWeight: - value == option ? FontWeight.w800 : FontWeight.w500, - )), - RoundedCheckbox( - value: value == option, - ) - ], - ); - }), - ), - ); - }, - separatorBuilder: (_, index) => SizedBox(height: 30), - )), + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(describeOption(option), + textAlign: TextAlign.left, + style: textSmall( + color: Theme.of(context) + .extension()! + .titleColor, + ).copyWith( + fontWeight: + value == option ? FontWeight.w800 : FontWeight.w500, + )), + RoundedCheckbox( + value: value == option, + ) + ], + ); + }), + ), + ); + }, + separatorBuilder: (_, index) => SizedBox(height: 30), + )), + ), ), - ), - Spacer() - ], + Spacer() + ], + ), ), - ), AlertCloseButton(onTap: () => Navigator.of(popUpContext).pop(), bottom: 40) ], ), diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index 75719d1239..3f3e546b3b 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -67,8 +67,7 @@ class ReceivePage extends BasePage { @override Widget Function(BuildContext, Widget) get rootWrapper => - (BuildContext context, Widget scaffold) => - GradientBackground(scaffold: scaffold); + (BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold); @override Widget trailing(BuildContext context) { @@ -159,7 +158,8 @@ class ReceivePage extends BasePage { trailingIcon: Icon( Icons.arrow_forward_ios, size: 14, - color: Theme.of(context).extension()!.iconsColor, + color: + Theme.of(context).extension()!.iconsColor, )); } @@ -185,11 +185,19 @@ class ReceivePage extends BasePage { final isCurrent = item.address == addressListViewModel.address.address; final backgroundColor = isCurrent - ? Theme.of(context).extension()!.currentTileBackgroundColor - : Theme.of(context).extension()!.tilesBackgroundColor; + ? Theme.of(context) + .extension()! + .currentTileBackgroundColor + : Theme.of(context) + .extension()! + .tilesBackgroundColor; final textColor = isCurrent - ? Theme.of(context).extension()!.currentTileTextColor - : Theme.of(context).extension()!.tilesTextColor; + ? Theme.of(context) + .extension()! + .currentTileTextColor + : Theme.of(context) + .extension()! + .tilesTextColor; return AddressCell.fromItem(item, isCurrent: isCurrent, @@ -211,6 +219,16 @@ class ReceivePage extends BasePage { child: cell, ); })), + if (!addressListViewModel.hasSilentAddresses) + Padding( + padding: EdgeInsets.fromLTRB(24, 24, 24, 32), + child: Text(S.of(context).electrum_address_disclaimer, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + color: + Theme.of(context).extension()!.labelTextColor)), + ), ], ), )) diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index 6bd2d81e9f..7b3df5175d 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -164,7 +165,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin _presentQRScanner(BuildContext context) async { bool isCameraPermissionGranted = - await PermissionHandler.checkPermission(Permission.camera, context); + await PermissionHandler.checkPermission(Permission.camera, context); if (!isCameraPermissionGranted) return; final code = await presentQRScanner(); if (code.isEmpty) { diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index da5eb03739..db932ff333 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -278,6 +278,11 @@ abstract class DashboardViewModelBase with Store { WalletBase, TransactionInfo> wallet; bool get hasRescan => wallet.type == WalletType.monero || wallet.type == WalletType.haven; + // bool get hasRescan => + // (wallet.type == WalletType.bitcoin && + // wallet.walletAddresses.addressPageType == bitcoin.AddressType.p2sp) || + // wallet.type == WalletType.monero || + // wallet.type == WalletType.haven; final KeyService keyService; diff --git a/lib/view_model/rescan_view_model.dart b/lib/view_model/rescan_view_model.dart index c973b7b3f0..e263f4a121 100644 --- a/lib/view_model/rescan_view_model.dart +++ b/lib/view_model/rescan_view_model.dart @@ -1,4 +1,5 @@ import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; part 'rescan_view_model.g.dart'; @@ -9,8 +10,8 @@ enum RescanWalletState { rescaning, none } abstract class RescanViewModelBase with Store { RescanViewModelBase(this._wallet) - : state = RescanWalletState.none, - isButtonEnabled = false; + : state = RescanWalletState.none, + isButtonEnabled = false; final WalletBase _wallet; @@ -23,8 +24,8 @@ abstract class RescanViewModelBase with Store { @action Future rescanCurrentWallet({required int restoreHeight}) async { state = RescanWalletState.rescaning; - await _wallet.rescan(height: restoreHeight); - _wallet.transactionHistory.clear(); + _wallet.rescan(height: restoreHeight); + if (_wallet.type != WalletType.bitcoin) _wallet.transactionHistory.clear(); state = RescanWalletState.none; } -} \ No newline at end of file +} diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index a2aab5251f..817b37d7a0 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -183,8 +183,6 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo }) : _baseItems = [], selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type), _cryptoNumberFormat = NumberFormat(_cryptoNumberPattern), - hasAccounts = - appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven, amount = '', _settingsStore = appStore.settingsStore, super(appStore: appStore) { @@ -196,7 +194,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo _init(); selectedCurrency = walletTypeToCryptoCurrency(wallet.type); - hasAccounts = wallet.type == WalletType.monero || wallet.type == WalletType.haven; + _hasAccounts = + hasSilentAddresses || wallet.type == WalletType.monero || wallet.type == WalletType.haven; } static const String _cryptoNumberPattern = '0.00000000'; @@ -365,7 +364,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo } @observable - bool hasAccounts; + bool _hasAccounts = false; + + @computed + bool get hasAccounts => _hasAccounts; @computed String get accountLabel { @@ -380,8 +382,21 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo return ''; } + @observable + // ignore: prefer_final_fields + bool? _hasSilentAddresses = null; + + @computed + bool get hasSilentAddresses => _hasSilentAddresses ?? wallet.type == WalletType.bitcoin; + // @computed + // bool get hasSilentAddresses => + // _hasSilentAddresses ?? + // wallet.type == WalletType.bitcoin && + // wallet.walletAddresses.addressPageType == btc.AddressType.p2sp; + @computed bool get hasAddressList => + hasSilentAddresses || wallet.type == WalletType.monero || wallet.type == WalletType.haven || wallet.type == WalletType.bitcoinCash || diff --git a/model_generator.sh b/model_generator.sh index 8a60986216..fa1ea6fac0 100755 --- a/model_generator.sh +++ b/model_generator.sh @@ -1,11 +1,10 @@ -cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. -cd cw_evm && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. -cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. -cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. -cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. -cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. -cd cw_bitcoin_cash && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. -cd cw_solana && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. -cd cw_ethereum && flutter pub get && cd .. -cd cw_polygon && flutter pub get && cd .. +cd cw_core; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. +cd cw_evm; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. +cd cw_monero; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. +cd cw_bitcoin; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. +cd cw_haven; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. +cd cw_nano; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. +cd cw_bitcoin_cash; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. +cd cw_polygon; flutter pub get; cd .. +cd cw_ethereum; flutter pub get; cd .. flutter packages pub run build_runner build --delete-conflicting-outputs diff --git a/tool/configure.dart b/tool/configure.dart index fb1647e131..91ea71d1f8 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -125,6 +125,7 @@ abstract class Bitcoin { List getAddresses(Object wallet); String getAddress(Object wallet); + List getSilentAddresses(Object wallet); List getSubAddresses(Object wallet); From 2cbf1dca880c018358ae713f3ce21d17bb3bbbbb Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 26 Feb 2024 15:33:31 -0300 Subject: [PATCH 02/75] feat: allow scanning elect-rs using get_tweaks --- .../lib/bitcoin_receive_page_option.dart | 21 +- cw_bitcoin/lib/bitcoin_wallet.dart | 26 +- cw_bitcoin/lib/electrum.dart | 13 +- cw_bitcoin/lib/electrum_wallet.dart | 685 ++++++++---------- cw_bitcoin/lib/electrum_wallet_addresses.dart | 4 +- cw_bitcoin/lib/litecoin_wallet.dart | 1 - cw_bitcoin/lib/litecoin_wallet_addresses.dart | 1 - cw_bitcoin/pubspec.yaml | 4 + .../lib/src/bitcoin_cash_wallet.dart | 1 - .../src/bitcoin_cash_wallet_addresses.dart | 1 - cw_bitcoin_cash/pubspec.yaml | 6 +- lib/bitcoin/cw_bitcoin.dart | 6 + lib/core/address_validator.dart | 4 +- lib/core/sync_status_title.dart | 2 +- .../screens/dashboard/pages/address_page.dart | 5 + .../present_receive_option_picker.dart | 4 +- .../address_edit_or_create_page.dart | 24 +- .../dashboard/dashboard_view_model.dart | 21 +- .../wallet_address_list_view_model.dart | 5 +- res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 1 + res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 1 + res/values/strings_ko.arb | 1 + res/values/strings_my.arb | 1 + res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 1 + res/values/strings_th.arb | 1 + res/values/strings_tl.arb | 1 + res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 1 + res/values/strings_yo.arb | 1 + res/values/strings_zh.arb | 1 + tool/configure.dart | 1 + 46 files changed, 413 insertions(+), 448 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_receive_page_option.dart b/cw_bitcoin/lib/bitcoin_receive_page_option.dart index 2b025965bd..b14753ffce 100644 --- a/cw_bitcoin/lib/bitcoin_receive_page_option.dart +++ b/cw_bitcoin/lib/bitcoin_receive_page_option.dart @@ -23,9 +23,28 @@ class BitcoinReceivePageOption implements ReceivePageOption { BitcoinReceivePageOption.p2sh, BitcoinReceivePageOption.p2tr, BitcoinReceivePageOption.p2wsh, - BitcoinReceivePageOption.p2pkh + BitcoinReceivePageOption.p2pkh, + BitcoinReceivePageOption.silent_payments, ]; + BitcoinAddressType toType() { + switch (this) { + case BitcoinReceivePageOption.p2tr: + return SegwitAddresType.p2tr; + case BitcoinReceivePageOption.p2wsh: + return SegwitAddresType.p2wsh; + case BitcoinReceivePageOption.p2pkh: + return P2pkhAddressType.p2pkh; + case BitcoinReceivePageOption.p2sh: + return P2shAddressType.p2wpkhInP2sh; + case BitcoinReceivePageOption.silent_payments: + return SilentPaymentsAddresType.p2sp; + case BitcoinReceivePageOption.p2wpkh: + default: + return SegwitAddresType.p2wpkh; + } + } + factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) { switch (type) { case SegwitAddresType.p2tr: diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index a018abbd6a..dfd7c8fe54 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -78,6 +78,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { Map? initialChangeAddressIndex, int initialSilentAddressIndex = 0, }) async { + final seedBytes = await mnemonicToSeedBytes(mnemonic); return BitcoinWallet( mnemonic: mnemonic, password: password, @@ -86,10 +87,18 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialSilentAddresses: initialSilentAddresses, initialSilentAddressIndex: initialSilentAddressIndex, - silentAddress: await SilentPaymentOwner.fromMnemonic(mnemonic, + silentAddress: await SilentPaymentOwner.fromPrivateKeys( + scanPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( + seedBytes, + network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, + ).derivePath(SCAN_PATH).privKey!), + spendPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( + seedBytes, + network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, + ).derivePath(SPEND_PATH).privKey!), hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp'), initialBalance: initialBalance, - seedBytes: await mnemonicToSeedBytes(mnemonic), + seedBytes: seedBytes, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, addressPageType: addressPageType, @@ -106,6 +115,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, walletInfo.network != null ? BasedUtxoNetwork.fromName(walletInfo.network!) : null); + final seedBytes = await mnemonicToSeedBytes(snp.mnemonic); return BitcoinWallet( mnemonic: snp.mnemonic, password: password, @@ -114,10 +124,18 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialAddresses: snp.addresses, initialSilentAddresses: snp.silentAddresses, initialSilentAddressIndex: snp.silentAddressIndex, - silentAddress: await SilentPaymentOwner.fromMnemonic(snp.mnemonic, + silentAddress: await SilentPaymentOwner.fromPrivateKeys( + scanPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( + seedBytes, + network: snp.network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, + ).derivePath(SCAN_PATH).privKey!), + spendPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( + seedBytes, + network: snp.network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, + ).derivePath(SPEND_PATH).privKey!), hrp: snp.network == BitcoinNetwork.testnet ? 'tsp' : 'sp'), initialBalance: snp.balance, - seedBytes: await mnemonicToSeedBytes(snp.mnemonic), + seedBytes: seedBytes, initialRegularAddressIndex: snp.regularAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex, addressPageType: snp.addressPageType, diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 59c864eb2e..27a43ddcb0 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -48,15 +48,19 @@ class ElectrumClient { Timer? _aliveTimer; String unterminatedString; - Future connectToUri(Uri uri) async => await connect(host: uri.host, port: uri.port); + Uri? uri; + + Future connectToUri(Uri uri) async { + this.uri = uri; + await connect(host: uri.host, port: uri.port); + } Future connect({required String host, required int port}) async { try { await socket?.close(); } catch (_) {} - socket = await SecureSocket.connect(host, port, - timeout: connectionTimeout, onBadCertificate: (_) => true); + socket = await Socket.connect(host, port, timeout: connectionTimeout); _setIsConnected(true); socket!.listen((Uint8List event) { @@ -275,6 +279,9 @@ class ElectrumClient { Future> getHeader({required int height}) async => await call(method: 'blockchain.block.get_header', params: [height]) as Map; + Future> getTweaks({required int height}) async => + await call(method: 'blockchain.block.tweaks', params: [height]) as Map; + Future estimatefee({required int p}) => call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) { if (result is double) { diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 4bef9b7489..f875a1b1f0 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -6,7 +6,7 @@ import 'dart:math'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; -import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin_base; +import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:collection/collection.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; @@ -146,7 +146,7 @@ abstract class ElectrumWalletBase Map?> _scripthashesUpdateSubject; BehaviorSubject? _chainTipUpdateSubject; bool _isTransactionUpdating; - // Future? _isolate; + Future? _isolate; void Function(FlutterErrorDetails)? _onError; Timer? _autoSaveTimer; @@ -160,66 +160,66 @@ abstract class ElectrumWalletBase Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save()); } - // @action - // Future _setListeners(int height, {int? chainTip}) async { - // final currentChainTip = chainTip ?? await electrumClient.getCurrentBlockChainTip() ?? 0; - // syncStatus = AttemptingSyncStatus(); - - // if (_isolate != null) { - // final runningIsolate = await _isolate!; - // runningIsolate.kill(priority: Isolate.immediate); - // } - - // final receivePort = ReceivePort(); - // _isolate = Isolate.spawn( - // startRefresh, - // ScanData( - // sendPort: receivePort.sendPort, - // primarySilentAddress: walletAddresses.primarySilentAddress!, - // networkType: networkType, - // height: height, - // chainTip: currentChainTip, - // electrumClient: ElectrumClient(), - // transactionHistoryIds: transactionHistory.transactions.keys.toList(), - // node: electrumClient.uri.toString(), - // labels: walletAddresses.labels, - // )); - - // await for (var message in receivePort) { - // if (message is BitcoinUnspent) { - // if (!unspentCoins.any((utx) => - // utx.hash.contains(message.hash) && - // utx.vout == message.vout && - // utx.address.contains(message.address))) { - // unspentCoins.add(message); - - // if (unspentCoinsInfo.values.any((element) => - // element.walletId.contains(id) && - // element.hash.contains(message.hash) && - // element.address.contains(message.address))) { - // _addCoinInfo(message); - - // await walletInfo.save(); - // await save(); - // } - - // balance[currency] = await _fetchBalances(); - // } - // } - - // if (message is Map) { - // transactionHistory.addMany(message); - // await transactionHistory.save(); - // } - - // // check if is a SyncStatus type since "is SyncStatus" doesn't work here - // if (message is SyncResponse) { - // syncStatus = message.syncStatus; - // walletInfo.restoreHeight = message.height; - // await walletInfo.save(); - // } - // } - // } + @action + Future _setListeners(int height, {int? chainTip}) async { + final currentChainTip = chainTip ?? await electrumClient.getCurrentBlockChainTip() ?? 0; + syncStatus = AttemptingSyncStatus(); + + if (_isolate != null) { + final runningIsolate = await _isolate!; + runningIsolate.kill(priority: Isolate.immediate); + } + + final receivePort = ReceivePort(); + _isolate = Isolate.spawn( + startRefresh, + ScanData( + sendPort: receivePort.sendPort, + primarySilentAddress: walletAddresses.primarySilentAddress!, + network: network, + height: height, + chainTip: currentChainTip, + electrumClient: ElectrumClient(), + transactionHistoryIds: transactionHistory.transactions.keys.toList(), + node: electrumClient.uri.toString(), + labels: walletAddresses.labels, + )); + + await for (var message in receivePort) { + if (message is BitcoinUnspent) { + if (!unspentCoins.any((utx) => + utx.hash.contains(message.hash) && + utx.vout == message.vout && + utx.address.contains(message.address))) { + unspentCoins.add(message); + + if (unspentCoinsInfo.values.any((element) => + element.walletId.contains(id) && + element.hash.contains(message.hash) && + element.address.contains(message.address))) { + _addCoinInfo(message); + + await walletInfo.save(); + await save(); + } + + balance[currency] = await _fetchBalances(); + } + } + + if (message is Map) { + transactionHistory.addMany(message); + await transactionHistory.save(); + } + + // check if is a SyncStatus type since "is SyncStatus" doesn't work here + if (message is SyncResponse) { + syncStatus = message.syncStatus; + walletInfo.restoreHeight = message.height; + await walletInfo.save(); + } + } + } @action @override @@ -261,11 +261,11 @@ abstract class ElectrumWalletBase }; syncStatus = ConnectedSyncStatus(); - // final currentChainTip = await electrumClient.getCurrentBlockChainTip(); + final currentChainTip = await electrumClient.getCurrentBlockChainTip(); - // if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { - // _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); - // } + if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { + _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); + } } catch (e) { print(e.toString()); syncStatus = FailedSyncStatus(); @@ -697,7 +697,7 @@ abstract class ElectrumWalletBase @override Future rescan({required int height, int? chainTip, ScanData? scanData}) async { - // _setListeners(height); + _setListeners(height); } @override @@ -1003,7 +1003,7 @@ abstract class ElectrumWalletBase try { final currentHeight = await electrumClient.getCurrentBlockChainTip(); if (currentHeight != null) walletInfo.restoreHeight = currentHeight; - // _setListeners(walletInfo.restoreHeight, chainTip: currentHeight); + _setListeners(walletInfo.restoreHeight, chainTip: currentHeight); } catch (e, s) { print(e.toString()); _onError?.call(FlutterErrorDetails( @@ -1106,10 +1106,10 @@ abstract class ElectrumWalletBase class ScanData { final SendPort sendPort; - final SilentPaymentReceiver primarySilentAddress; + final SilentPaymentOwner primarySilentAddress; final int height; final String node; - final bitcoin.NetworkType networkType; + final BasedUtxoNetwork network; final int chainTip; final ElectrumClient electrumClient; final List transactionHistoryIds; @@ -1120,7 +1120,7 @@ class ScanData { required this.primarySilentAddress, required this.height, required this.node, - required this.networkType, + required this.network, required this.chainTip, required this.electrumClient, required this.transactionHistoryIds, @@ -1133,7 +1133,7 @@ class ScanData { primarySilentAddress: scanData.primarySilentAddress, height: newHeight, node: scanData.node, - networkType: scanData.networkType, + network: scanData.network, chainTip: scanData.chainTip, transactionHistoryIds: scanData.transactionHistoryIds, electrumClient: scanData.electrumClient, @@ -1149,333 +1149,220 @@ class SyncResponse { SyncResponse(this.height, this.syncStatus); } -// Future startRefresh(ScanData scanData) async { -// var cachedBlockchainHeight = scanData.chainTip; - -// Future getNodeHeightOrUpdate(int baseHeight) async { -// if (cachedBlockchainHeight < baseHeight || cachedBlockchainHeight == 0) { -// final electrumClient = scanData.electrumClient; -// if (!electrumClient.isConnected) { -// final node = scanData.node; -// await electrumClient.connectToUri(Uri.parse(node)); -// } - -// cachedBlockchainHeight = -// await electrumClient.getCurrentBlockChainTip() ?? cachedBlockchainHeight; -// } - -// return cachedBlockchainHeight; -// } - -// var lastKnownBlockHeight = 0; -// var initialSyncHeight = 0; - -// var syncHeight = scanData.height; -// var currentChainTip = scanData.chainTip; - -// if (syncHeight <= 0) { -// syncHeight = currentChainTip; -// } - -// if (initialSyncHeight <= 0) { -// initialSyncHeight = syncHeight; -// } - -// if (lastKnownBlockHeight == syncHeight) { -// scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus())); -// return; -// } - -// // Run this until no more blocks left to scan txs. At first this was recursive -// // i.e. re-calling the startRefresh function but this was easier for the above values to retain -// // their initial values -// while (true) { -// lastKnownBlockHeight = syncHeight; - -// final syncingStatus = -// SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight); -// scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus)); - -// if (syncingStatus.blocksLeft <= 0) { -// scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus())); -// return; -// } - -// // print(["Scanning from height:", syncHeight]); - -// try { -// final networkPath = -// scanData.networkType.network == bitcoin.BtcNetwork.mainnet ? "" : "/testnet"; - -// // This endpoint gets up to 10 latest blocks from the given height -// final tenNewestBlocks = -// (await http.get(Uri.parse("https://blockstream.info$networkPath/api/blocks/$syncHeight"))) -// .body; -// var decodedBlocks = json.decode(tenNewestBlocks) as List; - -// decodedBlocks.sort((a, b) => (a["height"] as int).compareTo(b["height"] as int)); -// decodedBlocks = -// decodedBlocks.where((element) => (element["height"] as int) >= syncHeight).toList(); - -// // for each block, get up to 25 txs -// for (var i = 0; i < decodedBlocks.length; i++) { -// final blockJson = decodedBlocks[i]; -// final blockHash = blockJson["id"]; -// final txCount = blockJson["tx_count"] as int; - -// // print(["Scanning block index:", i, "with tx count:", txCount]); - -// int startIndex = 0; -// // go through each tx in block until no more txs are left -// while (startIndex < txCount) { -// // This endpoint gets up to 25 txs from the given block hash and start index -// final twentyFiveTxs = json.decode((await http.get(Uri.parse( -// "https://blockstream.info$networkPath/api/block/$blockHash/txs/$startIndex"))) -// .body) as List; - -// // print(["Scanning txs index:", startIndex]); - -// // For each tx, apply silent payment filtering and do shared secret calculation when applied -// for (var i = 0; i < twentyFiveTxs.length; i++) { -// try { -// final tx = twentyFiveTxs[i]; -// final txid = tx["txid"] as String; - -// // print(["Scanning tx:", txid]); - -// // TODO: if tx already scanned & stored skip -// // if (scanData.transactionHistoryIds.contains(txid)) { -// // // already scanned tx, continue to next tx -// // pos++; -// // continue; -// // } - -// List pubkeys = []; -// List outpoints = []; - -// bool skip = false; - -// for (var i = 0; i < (tx["vin"] as List).length; i++) { -// final input = tx["vin"][i]; -// final prevout = input["prevout"]; -// final scriptPubkeyType = prevout["scriptpubkey_type"]; -// String? pubkey; - -// if (scriptPubkeyType == "v0_p2wpkh" || scriptPubkeyType == "v1_p2tr") { -// final witness = input["witness"]; -// if (witness == null) { -// skip = true; -// // print("Skipping, no witness"); -// break; -// } - -// if (witness.length == 2) { -// pubkey = witness[1] as String; -// } else if (witness.length == 1) { -// pubkey = "02" + (prevout["scriptpubkey"] as String).fromHex.sublist(2).hex; -// } -// } - -// if (scriptPubkeyType == "p2pkh") { -// pubkey = bitcoin.P2pkhAddress( -// scriptSig: bitcoin.Script.fromRaw(hexData: input["scriptsig"] as String)) -// .pubkey; -// } - -// if (pubkey == null) { -// skip = true; -// // print("Skipping, invalid witness"); -// break; -// } - -// pubkeys.add(pubkey); -// outpoints.add( -// bitcoin.Outpoint(txid: input["txid"] as String, index: input["vout"] as int)); -// } - -// if (skip) { -// // skipped tx, continue to next tx -// continue; -// } - -// Map outpointsByP2TRpubkey = {}; -// for (var i = 0; i < (tx["vout"] as List).length; i++) { -// final output = tx["vout"][i]; -// if (output["scriptpubkey_type"] != "v1_p2tr") { -// // print("Skipping, not a v1_p2tr output"); -// continue; -// } - -// final script = (output["scriptpubkey"] as String).fromHex; - -// // final alreadySpentOutput = (await electrumClient.getHistory( -// // scriptHashFromScript(script, networkType: scanData.networkType))) -// // .length > -// // 1; - -// // if (alreadySpentOutput) { -// // print("Skipping, invalid witness"); -// // break; -// // } - -// final p2tr = bitcoin.P2trAddress( -// program: script.sublist(2).hex, networkType: scanData.networkType); -// final address = p2tr.address; - -// print(["Verifying taproot address:", address]); - -// outpointsByP2TRpubkey[script.sublist(2).hex] = -// bitcoin.Outpoint(txid: txid, index: i, value: output["value"] as int); -// } - -// if (pubkeys.isEmpty || outpoints.isEmpty || outpointsByP2TRpubkey.isEmpty) { -// // skipped tx, continue to next tx -// continue; -// } - -// final outpointHash = bitcoin.SilentPayment.hashOutpoints(outpoints); - -// final result = bitcoin.scanOutputs( -// scanData.primarySilentAddress.scanPrivkey, -// scanData.primarySilentAddress.spendPubkey, -// bitcoin.getSumInputPubKeys(pubkeys), -// outpointHash, -// outpointsByP2TRpubkey.keys.map((e) => e.fromHex).toList(), -// labels: scanData.labels, -// ); - -// if (result.isEmpty) { -// // no results tx, continue to next tx -// continue; -// } - -// if (result.length > 1) { -// print("MULTIPLE UNSPENT COINS FOUND!"); -// } else { -// print("UNSPENT COIN FOUND!"); -// } - -// result.forEach((key, value) async { -// final outpoint = outpointsByP2TRpubkey[key]; - -// if (outpoint == null) { -// return; -// } - -// final tweak = value[0]; -// String? label; -// if (value.length > 1) label = value[1]; - -// final txInfo = ElectrumTransactionInfo( -// WalletType.bitcoin, -// id: txid, -// height: syncHeight, -// amount: outpoint.value!, -// fee: 0, -// direction: TransactionDirection.incoming, -// isPending: false, -// date: DateTime.fromMillisecondsSinceEpoch((blockJson["timestamp"] as int) * 1000), -// confirmations: currentChainTip - syncHeight, -// to: bitcoin.SilentPaymentAddress.createLabeledSilentPaymentAddress( -// scanData.primarySilentAddress.scanPubkey, -// scanData.primarySilentAddress.spendPubkey, -// label != null ? label.fromHex : "0".fromHex, -// hrp: scanData.primarySilentAddress.hrp, -// version: scanData.primarySilentAddress.version) -// .toString(), -// unspent: null, -// ); - -// final status = json.decode((await http -// .get(Uri.parse("https://blockstream.info/testnet/api/tx/$txid/outspends"))) -// .body) as List; - -// bool spent = false; -// for (final s in status) { -// if ((s["spent"] as bool) == true) { -// spent = true; - -// scanData.sendPort.send({txid: txInfo}); - -// final sentTxId = s["txid"] as String; -// final sentTx = json.decode((await http -// .get(Uri.parse("https://blockstream.info/testnet/api/tx/$sentTxId"))) -// .body); - -// int amount = 0; -// for (final out in (sentTx["vout"] as List)) { -// amount += out["value"] as int; -// } - -// final height = s["status"]["block_height"] as int; - -// scanData.sendPort.send({ -// sentTxId: ElectrumTransactionInfo( -// WalletType.bitcoin, -// id: sentTxId, -// height: height, -// amount: amount, -// fee: 0, -// direction: TransactionDirection.outgoing, -// isPending: false, -// date: DateTime.fromMillisecondsSinceEpoch( -// (s["status"]["block_time"] as int) * 1000), -// confirmations: currentChainTip - height, -// ) -// }); -// } -// } - -// if (spent) { -// return; -// } - -// final unspent = BitcoinUnspent( -// BitcoinAddressRecord( -// bitcoin.P2trAddress(program: key, networkType: scanData.networkType).address, -// index: 0, -// isHidden: true, -// isUsed: true, -// silentAddressLabel: null, -// silentPaymentTweak: tweak, -// type: bitcoin.AddressType.p2tr, -// ), -// txid, -// outpoint.value!, -// outpoint.index, -// silentPaymentTweak: tweak, -// type: bitcoin.AddressType.p2tr, -// ); - -// // found utxo for tx, send unspent coin to main isolate -// scanData.sendPort.send(unspent); - -// // also send tx data for tx history -// txInfo.unspent = unspent; -// scanData.sendPort.send({txid: txInfo}); -// }); -// } catch (_) {} -// } - -// // Finished scanning batch of txs in block, add 25 to start index and continue to next block in loop -// startIndex += 25; -// } - -// // Finished scanning block, add 1 to height and continue to next block in loop -// syncHeight += 1; -// currentChainTip = await getNodeHeightOrUpdate(syncHeight); -// scanData.sendPort.send(SyncResponse(syncHeight, -// SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); -// } -// } catch (e, stacktrace) { -// print(stacktrace); -// print(e.toString()); - -// scanData.sendPort.send(SyncResponse(syncHeight, NotConnectedSyncStatus())); -// break; -// } -// } -// } +Future startRefresh(ScanData scanData) async { + var cachedBlockchainHeight = scanData.chainTip; + + Future getNodeHeightOrUpdate(int baseHeight) async { + if (cachedBlockchainHeight < baseHeight || cachedBlockchainHeight == 0) { + final electrumClient = scanData.electrumClient; + if (!electrumClient.isConnected) { + final node = scanData.node; + await electrumClient.connectToUri(Uri.parse(node)); + } + + cachedBlockchainHeight = + await electrumClient.getCurrentBlockChainTip() ?? cachedBlockchainHeight; + } + + return cachedBlockchainHeight; + } + + var lastKnownBlockHeight = 0; + var initialSyncHeight = 0; + + var syncHeight = scanData.height; + var currentChainTip = scanData.chainTip; + + if (syncHeight <= 0) { + syncHeight = currentChainTip; + } + + if (initialSyncHeight <= 0) { + initialSyncHeight = syncHeight; + } + + if (lastKnownBlockHeight == syncHeight) { + scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus())); + return; + } + + // Run this until no more blocks left to scan txs. At first this was recursive + // i.e. re-calling the startRefresh function but this was easier for the above values to retain + // their initial values + while (true) { + lastKnownBlockHeight = syncHeight; + + final syncingStatus = + SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight); + scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus)); + + if (syncingStatus.blocksLeft <= 0) { + scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus())); + return; + } + + print(["Scanning from height:", syncHeight]); + + try { + // Get all the tweaks from the block + final electrumClient = scanData.electrumClient; + if (!electrumClient.isConnected) { + final node = scanData.node; + await electrumClient.connectToUri(Uri.parse(node)); + } + final tweaks = await electrumClient.getTweaks(height: syncHeight); + + for (var i = 0; i < tweaks.length; i++) { + try { + // final txid = tweaks.keys.toList()[i]; + final details = tweaks.values.toList()[i]; + print(["details", details]); + final output_pubkeys = (details["output_pubkeys"] as List); + + // print(["Scanning tx:", txid]); + + // TODO: if tx already scanned & stored skip + // if (scanData.transactionHistoryIds.contains(txid)) { + // // already scanned tx, continue to next tx + // pos++; + // continue; + // } + + final result = SilentPayment.scanTweak( + scanData.primarySilentAddress.b_scan, + scanData.primarySilentAddress.B_spend, + details["tweak"] as String, + output_pubkeys.map((e) => BytesUtils.fromHexString(e)).toList(), + labels: scanData.labels, + ); + + if (result.isEmpty) { + // no results tx, continue to next tx + continue; + } + + if (result.length > 1) { + print("MULTIPLE UNSPENT COINS FOUND!"); + } else { + print("UNSPENT COIN FOUND!"); + } + + // result.forEach((key, value) async { + // final outpoint = output_pubkeys[key]; + + // if (outpoint == null) { + // return; + // } + + // final tweak = value[0]; + // String? label; + // if (value.length > 1) label = value[1]; + + // final txInfo = ElectrumTransactionInfo( + // WalletType.bitcoin, + // id: txid, + // height: syncHeight, + // amount: outpoint.value!, + // fee: 0, + // direction: TransactionDirection.incoming, + // isPending: false, + // date: DateTime.fromMillisecondsSinceEpoch((blockJson["timestamp"] as int) * 1000), + // confirmations: currentChainTip - syncHeight, + // to: bitcoin.SilentPaymentAddress.createLabeledSilentPaymentAddress( + // scanData.primarySilentAddress.scanPubkey, + // scanData.primarySilentAddress.spendPubkey, + // label != null ? label.fromHex : "0".fromHex, + // hrp: scanData.primarySilentAddress.hrp, + // version: scanData.primarySilentAddress.version) + // .toString(), + // unspent: null, + // ); + + // final status = json.decode((await http + // .get(Uri.parse("https://blockstream.info/testnet/api/tx/$txid/outspends"))) + // .body) as List; + + // bool spent = false; + // for (final s in status) { + // if ((s["spent"] as bool) == true) { + // spent = true; + + // scanData.sendPort.send({txid: txInfo}); + + // final sentTxId = s["txid"] as String; + // final sentTx = json.decode( + // (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$sentTxId"))) + // .body); + + // int amount = 0; + // for (final out in (sentTx["vout"] as List)) { + // amount += out["value"] as int; + // } + + // final height = s["status"]["block_height"] as int; + + // scanData.sendPort.send({ + // sentTxId: ElectrumTransactionInfo( + // WalletType.bitcoin, + // id: sentTxId, + // height: height, + // amount: amount, + // fee: 0, + // direction: TransactionDirection.outgoing, + // isPending: false, + // date: DateTime.fromMillisecondsSinceEpoch( + // (s["status"]["block_time"] as int) * 1000), + // confirmations: currentChainTip - height, + // ) + // }); + // } + // } + + // if (spent) { + // return; + // } + + // final unspent = BitcoinUnspent( + // BitcoinAddressRecord( + // bitcoin.P2trAddress(program: key, networkType: scanData.network).address, + // index: 0, + // isHidden: true, + // isUsed: true, + // silentAddressLabel: null, + // silentPaymentTweak: tweak, + // type: bitcoin.AddressType.p2tr, + // ), + // txid, + // outpoint.value!, + // outpoint.index, + // silentPaymentTweak: tweak, + // type: bitcoin.AddressType.p2tr, + // ); + + // // found utxo for tx, send unspent coin to main isolate + // scanData.sendPort.send(unspent); + + // // also send tx data for tx history + // txInfo.unspent = unspent; + // scanData.sendPort.send({txid: txInfo}); + // }); + } catch (_) {} + } + + // Finished scanning block, add 1 to height and continue to next block in loop + syncHeight += 1; + currentChainTip = await getNodeHeightOrUpdate(syncHeight); + scanData.sendPort.send(SyncResponse(syncHeight, + SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); + } catch (e, stacktrace) { + print(stacktrace); + print(e.toString()); + + scanData.sendPort.send(SyncResponse(syncHeight, NotConnectedSyncStatus())); + break; + } + } +} class EstimatedTxResult { EstimatedTxResult( diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 23482e4d70..bd6781ea80 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -243,7 +243,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { for (int i = 0; i < silentAddresses.length; i++) { final silentAddressRecord = silentAddresses[i]; final silentAddress = - SilentPaymentDestination.fromAddress(silentAddressRecord.address, 0).spendPubkey.toHex(); + SilentPaymentDestination.fromAddress(silentAddressRecord.address, 0).B_spend.toHex(); if (silentAddressRecord.silentPaymentTweak != null) labels[silentAddress] = silentAddressRecord.silentPaymentTweak!; @@ -259,7 +259,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final address = BitcoinAddressRecord( SilentPaymentAddress.createLabeledSilentPaymentAddress( - primarySilentAddress!.scanPubkey, primarySilentAddress!.spendPubkey, tweak, + primarySilentAddress!.B_scan, primarySilentAddress!.B_spend, tweak, hrp: primarySilentAddress!.hrp, version: primarySilentAddress!.version) .toString(), index: currentSilentAddressIndex, diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index d2379d5a5a..5641909c01 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -44,7 +44,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { currency: CryptoCurrency.ltc) { walletAddresses = LitecoinWalletAddresses( walletInfo, - electrumClient: electrumClient, initialAddresses: initialAddresses, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index 993d17933a..99b7445fc9 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -15,7 +15,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required super.mainHd, required super.sideHd, required super.network, - required super.electrumClient, super.initialAddresses, super.initialRegularAddressIndex, super.initialChangeAddressIndex, diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index b6acab7f48..13a0e26886 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -34,6 +34,10 @@ dependencies: git: url: https://github.com/cake-tech/bitcoin_base.git ref: cake-update-v2 + blockchain_utils: + git: + url: https://github.com/rafael-xmr/blockchain_utils + ref: cake-update-v1 dev_dependencies: flutter_test: diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart index 3c40cf9e91..2e4788c2d2 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart @@ -51,7 +51,6 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { currency: CryptoCurrency.bch) { walletAddresses = BitcoinCashWalletAddresses( walletInfo, - electrumClient: electrumClient, initialAddresses: initialAddresses, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart index 8291ce2a57..492b2daa80 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart @@ -15,7 +15,6 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi required super.mainHd, required super.sideHd, required super.network, - required super.electrumClient, super.initialAddresses, super.initialRegularAddressIndex, super.initialChangeAddressIndex, diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index 7130b3c58f..fd7ed5a0fc 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -33,8 +33,10 @@ dependencies: git: url: https://github.com/cake-tech/bitcoin_base.git ref: cake-update-v2 - - + blockchain_utils: + git: + url: https://github.com/rafael-xmr/blockchain_utils + ref: cake-update-v1 dev_dependencies: flutter_test: diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 891c6298a2..fb470a58f1 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -186,6 +186,12 @@ class CWBitcoin extends Bitcoin { return BitcoinReceivePageOption.fromType(bitcoinWallet.walletAddresses.addressPageType); } + @override + bool hasSelectedSilentPayments(Object wallet) { + final bitcoinWallet = wallet as ElectrumWallet; + return bitcoinWallet.walletAddresses.addressPageType == SilentPaymentsAddresType.p2sp; + } + @override List getBitcoinReceivePageOptions() => BitcoinReceivePageOption.all; diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 19cdb16160..e9ab8b827c 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -26,7 +26,7 @@ class AddressValidator extends TextValidator { return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$' '|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$'; case CryptoCurrency.btc: - return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${bitcoin.SilentPaymentAddress.REGEX.pattern}\$'; + return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${SilentPaymentAddress.regex.pattern}\$'; case CryptoCurrency.nano: return '[0-9a-zA-Z_]'; case CryptoCurrency.banano: @@ -275,7 +275,7 @@ class AddressValidator extends TextValidator { '([^0-9a-zA-Z]|^)${P2wpkhAddress.regex.pattern}|\$)' '([^0-9a-zA-Z]|^)${P2wshAddress.regex.pattern}|\$)' '([^0-9a-zA-Z]|^)${P2trAddress.regex.pattern}|\$)' - '|${bitcoin.SilentPaymentAddress.REGEX.pattern}\$'; + '|${SilentPaymentAddress.regex.pattern}\$'; case CryptoCurrency.ltc: return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)' '|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)' diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index fbb86fa9fb..226b97f508 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -4,7 +4,7 @@ import 'package:cw_core/sync_status.dart'; String syncStatusTitle(SyncStatus syncStatus) { if (syncStatus is SyncingSyncStatus) { return syncStatus.blocksLeft == 1 - ? S.current.Block_remaining('${syncStatus.blocksLeft}') + ? S.current.block_remaining : S.current.Blocks_remaining('${syncStatus.blocksLeft}'); } diff --git a/lib/src/screens/dashboard/pages/address_page.dart b/lib/src/screens/dashboard/pages/address_page.dart index 7b7c84c28c..ce1ab31f14 100644 --- a/lib/src/screens/dashboard/pages/address_page.dart +++ b/lib/src/screens/dashboard/pages/address_page.dart @@ -199,6 +199,11 @@ class AddressPage extends BasePage { } reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) { + if (option is BitcoinReceivePageOption) { + addressListViewModel.setAddressType(option.toType()); + return; + } + switch (option) { case ReceivePageOption.anonPayInvoice: Navigator.pushNamed( diff --git a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart index 64125b1457..0ae750cf99 100644 --- a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart +++ b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart @@ -45,7 +45,7 @@ class PresentReceiveOptionPicker extends StatelessWidget { fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: 'Lato', color: color), ), Observer( - builder: (_) => Text(describeOption(receiveOptionViewModel.selectedReceiveOption), + builder: (_) => Text(receiveOptionViewModel.selectedReceiveOption.toString(), style: TextStyle(fontSize: 10.0, fontWeight: FontWeight.w500, color: color))) ], ), @@ -101,7 +101,7 @@ class PresentReceiveOptionPicker extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(describeOption(option), + Text(option.toString(), textAlign: TextAlign.left, style: textSmall( color: Theme.of(context) diff --git a/lib/src/screens/subaddress/address_edit_or_create_page.dart b/lib/src/screens/subaddress/address_edit_or_create_page.dart index e067c78d0f..ed6369a736 100644 --- a/lib/src/screens/subaddress/address_edit_or_create_page.dart +++ b/lib/src/screens/subaddress/address_edit_or_create_page.dart @@ -13,8 +13,7 @@ class AddressEditOrCreatePage extends BasePage { : _formKey = GlobalKey(), _labelController = TextEditingController(), super() { - _labelController.addListener( - () => addressEditOrCreateViewModel.label = _labelController.text); + _labelController.addListener(() => addressEditOrCreateViewModel.label = _labelController.text); _labelController.text = addressEditOrCreateViewModel.label; } @@ -55,10 +54,8 @@ class AddressEditOrCreatePage extends BasePage { : S.of(context).new_subaddress_create, color: Theme.of(context).primaryColor, textColor: Colors.white, - isLoading: - addressEditOrCreateViewModel.state is AddressIsSaving, - isDisabled: - addressEditOrCreateViewModel.label?.isEmpty ?? true, + isLoading: addressEditOrCreateViewModel.state is AddressIsSaving, + isDisabled: addressEditOrCreateViewModel.label?.isEmpty ?? true, ), ) ], @@ -70,14 +67,13 @@ class AddressEditOrCreatePage extends BasePage { if (_isEffectsInstalled) { return; } - reaction((_) => addressEditOrCreateViewModel.state, - (AddressEditOrCreateState state) { - if (state is AddressSavedSuccessfully) { - WidgetsBinding.instance - .addPostFrameCallback((_) => Navigator.of(context).pop()); - } - }); + reaction((_) => addressEditOrCreateViewModel.state, (AddressEditOrCreateState state) { + if (state is AddressSavedSuccessfully) { + WidgetsBinding.instance.addPostFrameCallback((_) => Navigator.of(context).pop()); + } + }); _isEffectsInstalled = true; } -} \ No newline at end of file +} + diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index db932ff333..c214fceccb 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -42,7 +42,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:eth_sig_util/util/utils.dart'; import 'package:flutter/services.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/entities/provider_types.dart'; +import 'package:cake_wallet/bitcoin/bitcoin.dart'; part 'dashboard_view_model.g.dart'; @@ -277,12 +277,10 @@ abstract class DashboardViewModelBase with Store { @observable WalletBase, TransactionInfo> wallet; - bool get hasRescan => wallet.type == WalletType.monero || wallet.type == WalletType.haven; - // bool get hasRescan => - // (wallet.type == WalletType.bitcoin && - // wallet.walletAddresses.addressPageType == bitcoin.AddressType.p2sp) || - // wallet.type == WalletType.monero || - // wallet.type == WalletType.haven; + bool get hasRescan => + (wallet.type == WalletType.bitcoin && bitcoin!.hasSelectedSilentPayments(wallet)) || + wallet.type == WalletType.monero || + wallet.type == WalletType.haven; final KeyService keyService; @@ -344,15 +342,13 @@ abstract class DashboardViewModelBase with Store { bool hasExchangeAction; @computed - bool get isEnabledBuyAction => - !settingsStore.disableBuy && availableBuyProviders.isNotEmpty; + bool get isEnabledBuyAction => !settingsStore.disableBuy && availableBuyProviders.isNotEmpty; @observable bool hasBuyAction; @computed - bool get isEnabledSellAction => - !settingsStore.disableSell && availableSellProviders.isNotEmpty; + bool get isEnabledSellAction => !settingsStore.disableSell && availableSellProviders.isNotEmpty; @observable bool hasSellAction; @@ -477,7 +473,8 @@ abstract class DashboardViewModelBase with Store { Future> checkAffectedWallets() async { // await load file - final vulnerableSeedsString = await rootBundle.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt'); + final vulnerableSeedsString = await rootBundle + .loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt'); final vulnerableSeeds = vulnerableSeedsString.split("\n"); final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index 817b37d7a0..5e843ad789 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -410,8 +410,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo wallet.type == WalletType.bitcoinCash; @computed - bool get isAutoGenerateSubaddressEnabled => - _settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled; + bool get isAutoGenerateSubaddressEnabled => wallet.type == WalletType.bitcoin + ? !bitcoin!.hasSelectedSilentPayments(wallet) + : _settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled; List _baseItems; diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index af92cead41..02024fe1a9 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "موضوع البيتكوين الظلام", "bitcoin_light_theme": "موضوع البيتكوين الخفيفة", "bitcoin_payments_require_1_confirmation": "تتطلب مدفوعات Bitcoin تأكيدًا واحدًا ، والذي قد يستغرق 20 دقيقة أو أكثر. شكرا لصبرك! سيتم إرسال بريد إلكتروني إليك عند تأكيد الدفع.", + "block_remaining": "1 كتلة متبقية", "Blocks_remaining": "بلوك متبقي ${status}", "bright_theme": "مشرق", "buy": "اشتري", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 9f46ac2f49..807592f913 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Тъмна тема за биткойн", "bitcoin_light_theme": "Лека биткойн тема", "bitcoin_payments_require_1_confirmation": "Плащанията с Bitcoin изискват потвърждение, което може да отнеме 20 минути или повече. Благодарим за търпението! Ще получите имейл, когато плащането е потвърдено.", + "block_remaining": "1 блок останал", "Blocks_remaining": "${status} оставащи блока", "bright_theme": "Ярко", "buy": "Купуване", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index f47c7acb3a..74d7298da6 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Tmavé téma bitcoinů", "bitcoin_light_theme": "Světlé téma bitcoinů", "bitcoin_payments_require_1_confirmation": "U plateb Bitcoinem je vyžadováno alespoň 1 potvrzení, což může trvat 20 minut i déle. Děkujeme za vaši trpělivost! Až bude platba potvrzena, budete informováni e-mailem.", + "block_remaining": "1 blok zbývající", "Blocks_remaining": "Zbývá ${status} bloků", "bright_theme": "Jasný", "buy": "Koupit", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 3381120228..d71f68fd4c 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Dunkles Bitcoin-Thema", "bitcoin_light_theme": "Bitcoin Light-Thema", "bitcoin_payments_require_1_confirmation": "Bitcoin-Zahlungen erfordern 1 Bestätigung, was 20 Minuten oder länger dauern kann. Danke für Ihre Geduld! Sie erhalten eine E-Mail, wenn die Zahlung bestätigt ist.", + "block_remaining": "1 Block verbleibend", "Blocks_remaining": "${status} verbleibende Blöcke", "bright_theme": "Strahlend hell", "buy": "Kaufen", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 86dc6518d5..d943ed4cd8 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Bitcoin Dark Theme", "bitcoin_light_theme": "Bitcoin Light Theme", "bitcoin_payments_require_1_confirmation": "Bitcoin payments require 1 confirmation, which can take 20 minutes or longer. Thanks for your patience! You will be emailed when the payment is confirmed.", + "block_remaining": "1 Block Remaining", "Blocks_remaining": "${status} Blocks Remaining", "bright_theme": "Bright", "buy": "Buy", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 09ad9948b5..9cea08be43 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Tema oscuro de Bitcoin", "bitcoin_light_theme": "Tema de la luz de Bitcoin", "bitcoin_payments_require_1_confirmation": "Los pagos de Bitcoin requieren 1 confirmación, que puede demorar 20 minutos o más. ¡Gracias por su paciencia! Se le enviará un correo electrónico cuando se confirme el pago.", + "block_remaining": "1 bloqueo restante", "Blocks_remaining": "${status} Bloques restantes", "bright_theme": "Brillante", "buy": "Comprar", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index e552dfafa8..ac2d944dbb 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Thème sombre Bitcoin", "bitcoin_light_theme": "Thème léger Bitcoin", "bitcoin_payments_require_1_confirmation": "Les paiements Bitcoin nécessitent 1 confirmation, ce qui peut prendre 20 minutes ou plus. Merci pour votre patience ! Vous serez averti par e-mail lorsque le paiement sera confirmé.", + "block_remaining": "1 bloc restant", "Blocks_remaining": "Blocs Restants : ${status}", "bright_theme": "Vif", "buy": "Acheter", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 40f6b28575..557e6951ed 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Bitcoin Dark Jigo", "bitcoin_light_theme": "Jigon Hasken Bitcoin", "bitcoin_payments_require_1_confirmation": "Akwatin Bitcoin na buɗe 1 sambumbu, da yake za ta samu mintuna 20 ko yawa. Ina kira ga sabuwar lafiya! Zaka sanarwa ta email lokacin da aka samu akwatin samun lambar waya.", + "block_remaining": "1 toshe ragowar", "Blocks_remaining": "${status} Katanga ya rage", "bright_theme": "Mai haske", "buy": "Sayi", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index d35a5f7168..12383e2fb3 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "बिटकॉइन डार्क थीम", "bitcoin_light_theme": "बिटकॉइन लाइट थीम", "bitcoin_payments_require_1_confirmation": "बिटकॉइन भुगतान के लिए 1 पुष्टिकरण की आवश्यकता होती है, जिसमें 20 मिनट या अधिक समय लग सकता है। आपके धैर्य के लिए धन्यवाद! भुगतान की पुष्टि होने पर आपको ईमेल किया जाएगा।", + "block_remaining": "1 ब्लॉक शेष", "Blocks_remaining": "${status} शेष रहते हैं", "bright_theme": "उज्ज्वल", "buy": "खरीदें", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index c41310441a..ac351b03ed 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Bitcoin Tamna tema", "bitcoin_light_theme": "Bitcoin Light Theme", "bitcoin_payments_require_1_confirmation": "Bitcoin plaćanja zahtijevaju 1 potvrdu, što može potrajati 20 minuta ili dulje. Hvala na Vašem strpljenju! Dobit ćete e-poruku kada plaćanje bude potvrđeno.", + "block_remaining": "Preostalo 1 blok", "Blocks_remaining": "${status} preostalih blokova", "bright_theme": "Jarka", "buy": "Kupi", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index a9b9b50b3d..2756fcca06 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Tema Gelap Bitcoin", "bitcoin_light_theme": "Tema Cahaya Bitcoin", "bitcoin_payments_require_1_confirmation": "Pembayaran Bitcoin memerlukan 1 konfirmasi, yang bisa memakan waktu 20 menit atau lebih. Terima kasih atas kesabaran Anda! Anda akan diemail saat pembayaran dikonfirmasi.", + "block_remaining": "1 blok tersisa", "Blocks_remaining": "${status} Blok Tersisa", "bright_theme": "Cerah", "buy": "Beli", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 9682e3d7fc..83597d4c91 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Tema oscuro di Bitcoin", "bitcoin_light_theme": "Tema luce Bitcoin", "bitcoin_payments_require_1_confirmation": "I pagamenti in bitcoin richiedono 1 conferma, che può richiedere 20 minuti o più. Grazie per la vostra pazienza! Riceverai un'e-mail quando il pagamento sarà confermato.", + "block_remaining": "1 blocco rimanente", "Blocks_remaining": "${status} Blocchi Rimanenti", "bright_theme": "Colorato", "buy": "Comprare", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 0f4513eeed..eb74091a97 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "ビットコインダークテーマ", "bitcoin_light_theme": "ビットコインライトテーマ", "bitcoin_payments_require_1_confirmation": "ビットコインの支払いには 1 回の確認が必要で、これには 20 分以上かかる場合があります。お待ち頂きまして、ありがとうございます!支払いが確認されると、メールが送信されます。", + "block_remaining": "残り1ブロック", "Blocks_remaining": "${status} 残りのブロック", "bright_theme": "明るい", "buy": "購入", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 2dd80cf5c7..9a8bf48838 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "비트코인 다크 테마", "bitcoin_light_theme": "비트코인 라이트 테마", "bitcoin_payments_require_1_confirmation": "비트코인 결제는 1번의 확인이 필요하며 20분 이상이 소요될 수 있습니다. 기다려 주셔서 감사합니다! 결제가 확인되면 이메일이 전송됩니다.", + "block_remaining": "남은 블록 1 개", "Blocks_remaining": "${status} 남은 블록", "bright_theme": "선명한", "buy": "구입", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 1bc338dcb7..711c3c8841 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Bitcoin Dark Theme", "bitcoin_light_theme": "Bitcoin Light အပြင်အဆင်", "bitcoin_payments_require_1_confirmation": "Bitcoin ငွေပေးချေမှုများသည် မိနစ် 20 သို့မဟုတ် ထို့ထက်ပိုကြာနိုင်သည် 1 အတည်ပြုချက် လိုအပ်သည်။ မင်းရဲ့စိတ်ရှည်မှုအတွက် ကျေးဇူးတင်ပါတယ်။ ငွေပေးချေမှုကို အတည်ပြုပြီးသောအခါ သင့်ထံ အီးမေးလ်ပို့ပါမည်။", + "block_remaining": "ကျန်ရှိနေသေးသော block", "Blocks_remaining": "${status} ဘလောက်များ ကျန်နေပါသည်။", "bright_theme": "တောက်ပ", "buy": "ဝယ်ပါ။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 56175b636e..7527eb10fa 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Bitcoin donker thema", "bitcoin_light_theme": "Bitcoin Light-thema", "bitcoin_payments_require_1_confirmation": "Bitcoin-betalingen vereisen 1 bevestiging, wat 20 minuten of langer kan duren. Dank voor uw geduld! U ontvangt een e-mail wanneer de betaling is bevestigd.", + "block_remaining": "1 blok resterend", "Blocks_remaining": "${status} Resterende blokken", "bright_theme": "Helder", "buy": "Kopen", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index cbd1a71718..e93e56f743 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Ciemny motyw Bitcoina", "bitcoin_light_theme": "Lekki motyw Bitcoin", "bitcoin_payments_require_1_confirmation": "Płatności Bitcoin wymagają 1 potwierdzenia, co może zająć 20 minut lub dłużej. Dziękuję za cierpliwość! Otrzymasz wiadomość e-mail, gdy płatność zostanie potwierdzona.", + "block_remaining": "1 blok pozostałym", "Blocks_remaining": "Pozostało ${status} bloków", "bright_theme": "Biały", "buy": "Kup", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 7520f9327e..a7c1ff7e53 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Tema escuro Bitcoin", "bitcoin_light_theme": "Tema claro de bitcoin", "bitcoin_payments_require_1_confirmation": "Os pagamentos em Bitcoin exigem 1 confirmação, o que pode levar 20 minutos ou mais. Obrigado pela sua paciência! Você receberá um e-mail quando o pagamento for confirmado.", + "block_remaining": "1 bloco restante", "Blocks_remaining": "${status} blocos restantes", "bright_theme": "Brilhante", "buy": "Comprar", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index ee31e003a7..d339d2670f 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Биткойн Темная тема", "bitcoin_light_theme": "Легкая биткойн-тема", "bitcoin_payments_require_1_confirmation": "Биткойн-платежи требуют 1 подтверждения, что может занять 20 минут или дольше. Спасибо тебе за твое терпение! Вы получите электронное письмо, когда платеж будет подтвержден.", + "block_remaining": "1 Блок остался", "Blocks_remaining": "${status} Осталось блоков", "bright_theme": "Яркая", "buy": "Купить", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 3fddae1701..a4bb58d7de 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "ธีมมืด Bitcoin", "bitcoin_light_theme": "ธีมแสง Bitcoin", "bitcoin_payments_require_1_confirmation": "การชำระเงินด้วย Bitcoin ต้องการการยืนยัน 1 ครั้ง ซึ่งอาจใช้เวลา 20 นาทีหรือนานกว่านั้น ขอบคุณสำหรับความอดทนของคุณ! คุณจะได้รับอีเมลเมื่อการชำระเงินได้รับการยืนยัน", + "block_remaining": "เหลือ 1 บล็อก", "Blocks_remaining": "${status} บล็อกที่เหลืออยู่", "bright_theme": "สดใส", "buy": "ซื้อ", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 6dc6d793a9..2e9520dce5 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Bitcoin Madilim na Tema", "bitcoin_light_theme": "Tema ng ilaw ng bitcoin", "bitcoin_payments_require_1_confirmation": "Ang mga pagbabayad sa Bitcoin ay nangangailangan ng 1 kumpirmasyon, na maaaring tumagal ng 20 minuto o mas mahaba. Salamat sa iyong pasensya! Mag -email ka kapag nakumpirma ang pagbabayad.", + "block_remaining": "1 bloke ang natitira", "Blocks_remaining": "Ang natitirang ${status} ay natitira", "bright_theme": "Maliwanag", "buy": "Bilhin", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 585ef495a5..75148b2cab 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Bitcoin Karanlık Teması", "bitcoin_light_theme": "Bitcoin Hafif Tema", "bitcoin_payments_require_1_confirmation": "Bitcoin ödemeleri, 20 dakika veya daha uzun sürebilen 1 onay gerektirir. Sabrınız için teşekkürler! Ödeme onaylandığında e-posta ile bilgilendirileceksiniz.", + "block_remaining": "Kalan 1 blok", "Blocks_remaining": "${status} Blok Kaldı", "bright_theme": "Parlak", "buy": "Alış", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index a22fa8fb17..a461d10a5d 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Темна тема Bitcoin", "bitcoin_light_theme": "Світла тема Bitcoin", "bitcoin_payments_require_1_confirmation": "Платежі Bitcoin потребують 1 підтвердження, яке може зайняти 20 хвилин або більше. Дякую за Ваше терпіння! Ви отримаєте електронний лист, коли платіж буде підтверджено.", + "block_remaining": "1 блок, що залишився", "Blocks_remaining": "${status} Залишилось блоків", "bright_theme": "Яскрава", "buy": "Купити", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 35e17feaca..7ba95a3c57 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "بٹ کوائن ڈارک تھیم", "bitcoin_light_theme": "بٹ کوائن لائٹ تھیم", "bitcoin_payments_require_1_confirmation": "بٹ کوائن کی ادائیگی میں 1 تصدیق کی ضرورت ہوتی ہے ، جس میں 20 منٹ یا اس سے زیادہ وقت لگ سکتا ہے۔ آپ کے صبر کا شکریہ! ادائیگی کی تصدیق ہونے پر آپ کو ای میل کیا جائے گا۔", + "block_remaining": "1 بلاک باقی", "Blocks_remaining": "${status} بلاکس باقی ہیں۔", "bright_theme": "روشن", "buy": "خریدنے", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 0f141ebdf2..13af654809 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "Bitcoin Dark Akori", "bitcoin_light_theme": "Bitcoin Light Akori", "bitcoin_payments_require_1_confirmation": "Àwọn àránṣẹ́ Bitcoin nílò ìjẹ́rìísí kan. Ó lè lo ìṣéjú ogun tàbí ìṣéjú jù. A dúpẹ́ fún sùúrù yín! Ẹ máa gba ímeèlì t'ó bá jẹ́rìísí àránṣẹ́ náà.", + "block_remaining": "1 bulọọki to ku", "Blocks_remaining": "Àkójọpọ̀ ${status} kikù", "bright_theme": "Funfun", "buy": "Rà", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index ffea168a9f..9c82981346 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -75,6 +75,7 @@ "bitcoin_dark_theme": "比特币黑暗主题", "bitcoin_light_theme": "比特币浅色主题", "bitcoin_payments_require_1_confirmation": "比特币支付需要 1 次确认,这可能需要 20 分钟或更长时间。谢谢你的耐心!确认付款后,您将收到电子邮件。", + "block_remaining": "剩下1个块", "Blocks_remaining": "${status} 剩余的块", "bright_theme": "明亮", "buy": "购买", diff --git a/tool/configure.dart b/tool/configure.dart index 91ea71d1f8..bf89743efb 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -145,6 +145,7 @@ abstract class Bitcoin { Future setAddressType(Object wallet, dynamic option); BitcoinReceivePageOption getSelectedAddressType(Object wallet); + bool hasSelectedSilentPayments(Object wallet); List getBitcoinReceivePageOptions(); } """; From 3c881466521249ca332952e781ff8077e6b1bdc5 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 27 Feb 2024 11:40:56 -0300 Subject: [PATCH 03/75] feat: scanning and adding addresses working with getTweaks, add btc SP address type --- cw_bitcoin/lib/bitcoin_address_record.dart | 134 +++-- .../lib/bitcoin_receive_page_option.dart | 2 +- cw_bitcoin/lib/bitcoin_unspent.dart | 6 +- cw_bitcoin/lib/bitcoin_wallet.dart | 22 +- cw_bitcoin/lib/electrum.dart | 4 +- cw_bitcoin/lib/electrum_wallet.dart | 529 +++++++----------- cw_bitcoin/lib/electrum_wallet_addresses.dart | 63 +-- cw_bitcoin/lib/electrum_wallet_snapshot.dart | 4 +- cw_bitcoin/pubspec.lock | 11 +- cw_bitcoin/pubspec.yaml | 2 +- cw_bitcoin_cash/pubspec.yaml | 2 +- cw_core/lib/sync_status.dart | 5 + lib/bitcoin/cw_bitcoin.dart | 6 +- lib/core/sync_status_title.dart | 4 + model_generator.sh | 1 + res/values/strings_ar.arb | 7 +- res/values/strings_bg.arb | 7 +- res/values/strings_cs.arb | 7 +- res/values/strings_de.arb | 7 +- res/values/strings_en.arb | 7 +- res/values/strings_es.arb | 7 +- res/values/strings_fr.arb | 7 +- res/values/strings_ha.arb | 7 +- res/values/strings_hi.arb | 7 +- res/values/strings_hr.arb | 7 +- res/values/strings_id.arb | 7 +- res/values/strings_it.arb | 7 +- res/values/strings_ja.arb | 7 +- res/values/strings_ko.arb | 7 +- res/values/strings_my.arb | 7 +- res/values/strings_nl.arb | 7 +- res/values/strings_pl.arb | 7 +- res/values/strings_pt.arb | 7 +- res/values/strings_ru.arb | 7 +- res/values/strings_th.arb | 7 +- res/values/strings_tl.arb | 7 +- res/values/strings_tr.arb | 7 +- res/values/strings_uk.arb | 7 +- res/values/strings_ur.arb | 7 +- res/values/strings_yo.arb | 7 +- res/values/strings_zh.arb | 7 +- tool/configure.dart | 2 +- 42 files changed, 494 insertions(+), 485 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index 2c40ba34cd..afd7c34e16 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -4,8 +4,8 @@ import 'package:bitbox/bitbox.dart' as bitbox; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/script_hash.dart' as sh; -class BitcoinAddressRecord { - BitcoinAddressRecord( +abstract class BaseBitcoinAddressRecord { + BaseBitcoinAddressRecord( this.address, { required this.index, this.isHidden = false, @@ -14,41 +14,14 @@ class BitcoinAddressRecord { String name = '', bool isUsed = false, required this.type, - String? scriptHash, required this.network, - this.silentPaymentTweak, }) : _txCount = txCount, _balance = balance, _name = name, - _isUsed = isUsed, - scriptHash = - scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null); - - factory BitcoinAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) { - final decoded = json.decode(jsonSource) as Map; - - return BitcoinAddressRecord( - decoded['address'] as String, - index: decoded['index'] as int, - isHidden: decoded['isHidden'] as bool? ?? false, - isUsed: decoded['isUsed'] as bool? ?? false, - txCount: decoded['txCount'] as int? ?? 0, - name: decoded['name'] as String? ?? '', - balance: decoded['balance'] as int? ?? 0, - type: decoded['type'] != null && decoded['type'] != '' - ? BitcoinAddressType.values - .firstWhere((type) => type.toString() == decoded['type'] as String) - : SegwitAddresType.p2wpkh, - scriptHash: decoded['scriptHash'] as String?, - network: (decoded['network'] as String?) == null - ? network - : BasedUtxoNetwork.fromName(decoded['network'] as String), - silentPaymentTweak: decoded['silentPaymentTweak'] as String?, - ); - } + _isUsed = isUsed; @override - bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address; + bool operator ==(Object o) => o is BaseBitcoinAddressRecord && address == o.address; final String address; bool isHidden; @@ -57,9 +30,7 @@ class BitcoinAddressRecord { int _balance; String _name; bool _isUsed; - String? scriptHash; BasedUtxoNetwork? network; - final String? silentPaymentTweak; int get txCount => _txCount; @@ -76,18 +47,60 @@ class BitcoinAddressRecord { void setAsUsed() => _isUsed = true; void setNewName(String label) => _name = label; - @override int get hashCode => address.hashCode; String get cashAddr => bitbox.Address.toCashAddress(address); BitcoinAddressType type; + String toJSON(); +} + +class BitcoinAddressRecord extends BaseBitcoinAddressRecord { + BitcoinAddressRecord( + super.address, { + required super.index, + super.isHidden = false, + super.txCount = 0, + super.balance = 0, + super.name = '', + super.isUsed = false, + required super.type, + String? scriptHash, + required super.network, + }) : scriptHash = + scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null); + + factory BitcoinAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) { + final decoded = json.decode(jsonSource) as Map; + + return BitcoinAddressRecord( + decoded['address'] as String, + index: decoded['index'] as int, + isHidden: decoded['isHidden'] as bool? ?? false, + isUsed: decoded['isUsed'] as bool? ?? false, + txCount: decoded['txCount'] as int? ?? 0, + name: decoded['name'] as String? ?? '', + balance: decoded['balance'] as int? ?? 0, + type: decoded['type'] != null && decoded['type'] != '' + ? BitcoinAddressType.values + .firstWhere((type) => type.toString() == decoded['type'] as String) + : SegwitAddresType.p2wpkh, + scriptHash: decoded['scriptHash'] as String?, + network: (decoded['network'] as String?) == null + ? network + : BasedUtxoNetwork.fromName(decoded['network'] as String), + ); + } + + String? scriptHash; + String updateScriptHash(BasedUtxoNetwork network) { scriptHash = sh.scriptHash(address, network: network); return scriptHash!; } + @override String toJSON() => json.encode({ 'address': address, 'index': index, @@ -99,6 +112,59 @@ class BitcoinAddressRecord { 'type': type.toString(), 'scriptHash': scriptHash, 'network': network?.value, + }); +} + +class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { + BitcoinSilentPaymentAddressRecord( + super.address, { + required super.index, + super.isHidden = false, + super.txCount = 0, + super.balance = 0, + super.name = '', + super.isUsed = false, + required super.type, + required this.silentPaymentTweak, + required super.network, + }); + + factory BitcoinSilentPaymentAddressRecord.fromJSON(String jsonSource, + {BasedUtxoNetwork? network}) { + final decoded = json.decode(jsonSource) as Map; + + return BitcoinSilentPaymentAddressRecord( + decoded['address'] as String, + index: decoded['index'] as int, + isHidden: decoded['isHidden'] as bool? ?? false, + isUsed: decoded['isUsed'] as bool? ?? false, + txCount: decoded['txCount'] as int? ?? 0, + name: decoded['name'] as String? ?? '', + balance: decoded['balance'] as int? ?? 0, + type: decoded['type'] != null && decoded['type'] != '' + ? BitcoinAddressType.values + .firstWhere((type) => type.toString() == decoded['type'] as String) + : SegwitAddresType.p2wpkh, + network: (decoded['network'] as String?) == null + ? network + : BasedUtxoNetwork.fromName(decoded['network'] as String), + silentPaymentTweak: decoded['silentPaymentTweak'] as String, + ); + } + + final String silentPaymentTweak; + + @override + String toJSON() => json.encode({ + 'address': address, + 'index': index, + 'isHidden': isHidden, + 'isUsed': isUsed, + 'txCount': txCount, + 'name': name, + 'balance': balance, + 'type': type.toString(), + 'network': network?.value, 'silentPaymentTweak': silentPaymentTweak, }); } diff --git a/cw_bitcoin/lib/bitcoin_receive_page_option.dart b/cw_bitcoin/lib/bitcoin_receive_page_option.dart index b14753ffce..cd471ef282 100644 --- a/cw_bitcoin/lib/bitcoin_receive_page_option.dart +++ b/cw_bitcoin/lib/bitcoin_receive_page_option.dart @@ -19,12 +19,12 @@ class BitcoinReceivePageOption implements ReceivePageOption { } static const all = [ + BitcoinReceivePageOption.silent_payments, BitcoinReceivePageOption.p2wpkh, BitcoinReceivePageOption.p2sh, BitcoinReceivePageOption.p2tr, BitcoinReceivePageOption.p2wsh, BitcoinReceivePageOption.p2pkh, - BitcoinReceivePageOption.silent_payments, ]; BitcoinAddressType toType() { diff --git a/cw_bitcoin/lib/bitcoin_unspent.dart b/cw_bitcoin/lib/bitcoin_unspent.dart index 131f47ab65..ce3c0da166 100644 --- a/cw_bitcoin/lib/bitcoin_unspent.dart +++ b/cw_bitcoin/lib/bitcoin_unspent.dart @@ -3,12 +3,12 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_core/unspent_transaction_output.dart'; class BitcoinUnspent extends Unspent { - BitcoinUnspent(BitcoinAddressRecord addressRecord, String hash, int value, int vout, + BitcoinUnspent(BaseBitcoinAddressRecord addressRecord, String hash, int value, int vout, {this.silentPaymentTweak, this.type}) : bitcoinAddressRecord = addressRecord, super(addressRecord.address, hash, value, vout, null); - factory BitcoinUnspent.fromJSON(BitcoinAddressRecord address, Map json) => + factory BitcoinUnspent.fromJSON(BaseBitcoinAddressRecord address, Map json) => BitcoinUnspent( address, json['tx_hash'] as String, @@ -32,7 +32,7 @@ class BitcoinUnspent extends Unspent { return json; } - final BitcoinAddressRecord bitcoinAddressRecord; + final BaseBitcoinAddressRecord bitcoinAddressRecord; String? silentPaymentTweak; BitcoinAddressType? type; } diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index dfd7c8fe54..f24142493e 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -30,7 +30,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, - List? initialSilentAddresses, + List? initialSilentAddresses, int initialSilentAddressIndex = 0, SilentPaymentOwner? silentAddress, }) : super( @@ -59,9 +59,19 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"), network: networkParam ?? network, ); + hasSilentPaymentsScanning = addressPageType == SilentPaymentsAddresType.p2sp.toString(); + autorun((_) { this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; }); + + reaction((_) => walletAddresses.addressPageType, (BitcoinAddressType addressPageType) { + final prev = hasSilentPaymentsScanning; + hasSilentPaymentsScanning = addressPageType == SilentPaymentsAddresType.p2sp; + if (prev != hasSilentPaymentsScanning) { + startSync(); + } + }); } static Future create({ @@ -72,7 +82,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { String? addressPageType, BasedUtxoNetwork? network, List? initialAddresses, - List? initialSilentAddresses, + List? initialSilentAddresses, ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, @@ -88,11 +98,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialSilentAddresses: initialSilentAddresses, initialSilentAddressIndex: initialSilentAddressIndex, silentAddress: await SilentPaymentOwner.fromPrivateKeys( - scanPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( + b_scan: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( seedBytes, network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, ).derivePath(SCAN_PATH).privKey!), - spendPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( + b_spend: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( seedBytes, network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, ).derivePath(SPEND_PATH).privKey!), @@ -125,11 +135,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialSilentAddresses: snp.silentAddresses, initialSilentAddressIndex: snp.silentAddressIndex, silentAddress: await SilentPaymentOwner.fromPrivateKeys( - scanPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( + b_scan: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( seedBytes, network: snp.network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, ).derivePath(SCAN_PATH).privKey!), - spendPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( + b_spend: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( seedBytes, network: snp.network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, ).derivePath(SPEND_PATH).privKey!), diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 27a43ddcb0..236dddaa26 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -279,8 +279,8 @@ class ElectrumClient { Future> getHeader({required int height}) async => await call(method: 'blockchain.block.get_header', params: [height]) as Map; - Future> getTweaks({required int height}) async => - await call(method: 'blockchain.block.tweaks', params: [height]) as Map; + Future> getTweaks({required int height}) async => + await callWithTimeout(method: 'blockchain.block.tweaks', params: [height]) as List; Future estimatefee({required int p}) => call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) { diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index f875a1b1f0..bd63c40972 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -35,6 +35,7 @@ import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/utils/file.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; import 'package:hex/hex.dart'; import 'package:hive/hive.dart'; @@ -116,7 +117,7 @@ abstract class ElectrumWalletBase @observable SyncStatus syncStatus; - List get scriptHashes => walletAddresses.addressesByReceiveType + List get scriptHashes => walletAddresses.allAddresses .map((addr) => scriptHash(addr.address, network: network)) .toList(); @@ -136,6 +137,11 @@ abstract class ElectrumWalletBase @override bool? isTestnet; + @observable + bool hasSilentPaymentsScanning = false; + @observable + bool nodeSupportsSilentPayments = true; + @override BitcoinWalletKeys get keys => BitcoinWalletKeys(wif: hd.wif!, privateKey: hd.privKey!, publicKey: hd.pubKey!); @@ -186,6 +192,11 @@ abstract class ElectrumWalletBase )); await for (var message in receivePort) { + if (message is bool) { + nodeSupportsSilentPayments = message; + syncStatus = UnsupportedSyncStatus(); + } + if (message is BitcoinUnspent) { if (!unspentCoins.any((utx) => utx.hash.contains(message.hash) && @@ -225,11 +236,18 @@ abstract class ElectrumWalletBase @override Future startSync() async { try { - await _setInitialHeight(); - } catch (_) {} + if (hasSilentPaymentsScanning) { + try { + await _setInitialHeight(); + } catch (_) {} - try { - rescan(height: walletInfo.restoreHeight); + final currentChainTip = await electrumClient.getCurrentBlockChainTip(); + if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { + _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); + } + } else { + syncStatus = AttemptingSyncStatus(); + } await updateTransactions(); _subscribeForUpdates(); @@ -240,7 +258,9 @@ abstract class ElectrumWalletBase Timer.periodic( const Duration(minutes: 1), (timer) async => _feeRates = await electrumClient.feeRates()); + // if (!hasSilentPaymentsScanning) { syncStatus = SyncedSyncStatus(); + // } } catch (e, stacktrace) { print(stacktrace); print(e.toString()); @@ -260,12 +280,6 @@ abstract class ElectrumWalletBase } }; syncStatus = ConnectedSyncStatus(); - - final currentChainTip = await electrumClient.getCurrentBlockChainTip(); - - if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { - _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); - } } catch (e) { print(e.toString()); syncStatus = FailedSyncStatus(); @@ -278,13 +292,18 @@ abstract class ElectrumWalletBase List outputAddresses, List outputs, BitcoinTransactionCredentials transactionCredentials, - {int? inputsCount}) async { + {int? inputsCount, + bool? hasSilentPayment}) async { final utxos = []; List privateKeys = []; var leftAmount = credentialsAmount; var allInputsAmount = 0; + List vinOutpoints = []; + List inputPrivKeyInfos = []; + List inputPubKeys = []; + for (int i = 0; i < unspentCoins.length; i++) { final utx = unspentCoins[i]; @@ -292,20 +311,6 @@ abstract class ElectrumWalletBase allInputsAmount += utx.value; leftAmount = leftAmount - utx.value; - if (utx.bitcoinAddressRecord.silentPaymentTweak != null) { - // final d = ECPrivate.fromHex(walletAddresses.primarySilentAddress!.spendPrivkey.toHex()) - // .tweakAdd(utx.bitcoinAddressRecord.silentPaymentTweak!)!; - - // inputPrivKeys.add(bitcoin.PrivateKeyInfo(d, true)); - // address = bitcoin.P2trAddress(address: utx.address, networkType: networkType); - // keyPairs.add(bitcoin.ECPair.fromPrivateKey(d.toCompressedHex().fromHex, - // compressed: true, network: networkType)); - // scriptType = bitcoin.AddressType.p2tr; - // script = bitcoin.P2trAddress(pubkey: d.publicKey.toHex(), networkType: networkType) - // .scriptPubkey - // .toBytes(); - } - final address = _addressTypeFromStr(utx.address, network); final privkey = generateECPrivate( hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd, @@ -313,6 +318,9 @@ abstract class ElectrumWalletBase network: network); privateKeys.add(privkey); + inputPrivKeyInfos.add(ECPrivateInfo(privkey, address.type == SegwitAddresType.p2tr)); + inputPubKeys.add(privkey.getPublic()); + vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); utxos.add( UtxoWithAddress( @@ -334,125 +342,60 @@ abstract class ElectrumWalletBase } } - if (inputs.isEmpty) { + if (utxos.isEmpty) { throw BitcoinTransactionNoInputsException(); } - final allAmountFee = transactionCredentials.feeRate != null - ? feeAmountWithFeeRate(transactionCredentials.feeRate!, inputs.length, outputs.length) - : feeAmountForPriority(transactionCredentials.priority!, inputs.length, outputs.length); - - final allAmount = allInputsAmount - allAmountFee; - - var credentialsAmount = 0; - var amount = 0; - var fee = 0; - - if (hasMultiDestination) { - if (outputs.any((item) => item.sendAll || item.formattedCryptoAmount! <= 0)) { - throw BitcoinTransactionWrongBalanceException(currency); - } - - credentialsAmount = outputs.fold(0, (acc, value) => acc + value.formattedCryptoAmount!); - - if (allAmount - credentialsAmount < minAmount) { - throw BitcoinTransactionWrongBalanceException(currency); - } - - amount = credentialsAmount; - - if (transactionCredentials.feeRate != null) { - fee = calculateEstimatedFeeWithFeeRate(transactionCredentials.feeRate!, amount, - outputsCount: outputs.length + 1); - } else { - fee = calculateEstimatedFee(transactionCredentials.priority, amount, - outputsCount: outputs.length + 1); - } - } else { - final output = outputs.first; - credentialsAmount = !output.sendAll ? output.formattedCryptoAmount! : 0; - - if (credentialsAmount > allAmount) { - throw BitcoinTransactionWrongBalanceException(currency); - } - - amount = output.sendAll || allAmount - credentialsAmount < minAmount - ? allAmount - : credentialsAmount; + var changeValue = allInputsAmount - credentialsAmount; - if (output.sendAll || amount == allAmount) { - fee = allAmountFee; - } else if (transactionCredentials.feeRate != null) { - fee = calculateEstimatedFeeWithFeeRate(transactionCredentials.feeRate!, amount); - } else { - fee = calculateEstimatedFee(transactionCredentials.priority, amount); + if (!sendAll) { + if (changeValue > 0) { + final changeAddress = await walletAddresses.getChangeAddress(); + final address = _addressTypeFromStr(changeAddress, network); + outputAddresses.add(address); + outputs.add(BitcoinOutput(address: address, value: BigInt.from(changeValue))); } } - if (fee == 0) { - throw BitcoinTransactionWrongBalanceException(currency); - } + if (hasSilentPayment == true) { + List silentPaymentDestinations = []; - final totalAmount = amount + fee; + for (final out in outputs) { + final address = out.address; + final amount = out.value; - if (totalAmount > balance[currency]!.confirmed || totalAmount > allInputsAmount) { - throw BitcoinTransactionWrongBalanceException(currency); - } + if (address is SilentPaymentAddress) { + final silentPaymentDestination = + SilentPaymentDestination.fromAddress(address.toAddress(network), amount.toInt()); + silentPaymentDestinations.add(silentPaymentDestination); + } + } - final txb = bitcoin.TransactionBuilder(network: networkType); - final changeAddress = await walletAddresses.getChangeAddress(); - var leftAmount = totalAmount; - var totalInputAmount = 0; + final spb = SilentPaymentBuilder(pubkeys: inputPubKeys, outpoints: vinOutpoints); + final sendingOutputs = spb.createOutputs(inputPrivKeyInfos, silentPaymentDestinations); - inputs.clear(); + var outputsAdded = []; - for (final utx in unspentCoins) { - if (utx.isSending) { - leftAmount = leftAmount - utx.value; - - final address = _addressTypeFromStr(utx.address, network); - final privkey = generateECPrivate( - hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd, - index: utx.bitcoinAddressRecord.index, - network: network); + for (var i = 0; i < outputs.length; i++) { + final out = outputs[i]; - privateKeys.add(privkey); + final silentOutputs = sendingOutputs[out.address.toAddress(network)]; + if (silentOutputs != null) { + final silentOutput = + silentOutputs.firstWhereOrNull((element) => !outputsAdded.contains(element)); - utxos.add( - UtxoWithAddress( - utxo: BitcoinUtxo( - txHash: utx.hash, - value: BigInt.from(utx.value), - vout: utx.vout, - scriptType: _getScriptType(address), - ), - ownerDetails: - UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: address), - ), - ); + if (silentOutput != null) { + outputs[i] = BitcoinOutput( + address: silentOutput.address, + value: BigInt.from(silentOutput.amount), + ); - bool amountIsAcquired = !sendAll && leftAmount <= 0; - if ((inputsCount == null && amountIsAcquired) || inputsCount == i + 1) { - break; + outputsAdded.add(silentOutput); + } } } } - if (utxos.isEmpty) { - throw BitcoinTransactionNoInputsException(); - } - - var changeValue = allInputsAmount - credentialsAmount; - - if (!sendAll) { - if (changeValue > 0) { - final changeAddress = await walletAddresses.getChangeAddress(); - final address = _addressTypeFromStr(changeAddress, network); - outputAddresses.add(address); - outputs.add(BitcoinOutput(address: address, value: BigInt.from(changeValue))); - } - } - final estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize( utxos: utxos, outputs: outputs, network: network); @@ -497,25 +440,31 @@ abstract class ElectrumWalletBase return _estimateTxFeeAndInputsToUse( credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials, - inputsCount: utxos.length + 1); + inputsCount: utxos.length + 1, hasSilentPayment: hasSilentPayment); } } - if (SilentPaymentAddress.regex.hasMatch(outputAddress)) { - // final outpointsHash = SilentPayment.hashOutpoints(outpoints); - // final generatedOutputs = SilentPayment.generateMultipleRecipientPubkeys(inputPrivKeys, - // outpointsHash, SilentPaymentDestination.fromAddress(outputAddress, outputAmount!)); - - // generatedOutputs.forEach((recipientSilentAddress, generatedOutput) { - // generatedOutput.forEach((output) { - // outputs.add(BitcoinOutputDetails( - // address: P2trAddress( - // program: ECPublic.fromHex(output.$1.toHex()).toTapPoint(), - // networkType: networkType), - // value: BigInt.from(output.$2), - // )); - // }); - // }); + return EstimatedTxResult(utxos: utxos, privateKeys: privateKeys, fee: fee, amount: amount); + } + + @override + Future createTransaction(Object credentials) async { + try { + final outputs = []; + final outputAddresses = []; + final transactionCredentials = credentials as BitcoinTransactionCredentials; + final hasMultiDestination = transactionCredentials.outputs.length > 1; + final sendAll = !hasMultiDestination && transactionCredentials.outputs.first.sendAll; + + var credentialsAmount = 0; + bool hasSilentPayment = false; + + for (final out in transactionCredentials.outputs) { + final outputAddress = out.isParsedAddress ? out.extractedAddress! : out.address; + final address = _addressTypeFromStr(outputAddress, network); + + if (address is SilentPaymentAddress) { + hasSilentPayment = true; } outputAddresses.add(address); @@ -542,7 +491,8 @@ abstract class ElectrumWalletBase } final estimatedTx = await _estimateTxFeeAndInputsToUse( - credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials); + credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials, + hasSilentPayment: hasSilentPayment); final txb = BitcoinTransactionBuilder( utxos: estimatedTx.utxos, @@ -749,7 +699,7 @@ abstract class ElectrumWalletBase final coinInfoList = unspentCoinsInfo.values.where((element) => element.walletId.contains(id) && element.hash.contains(coin.hash) && - element.address.contains(coin.address)); + element.vout == coin.vout); if (coinInfoList.isNotEmpty) { final coinInfo = coinInfoList.first; @@ -916,7 +866,7 @@ abstract class ElectrumWalletBase final Map historiesWithDetails = {}; final history = await electrumClient - .getHistory(addressRecord.scriptHash ?? addressRecord.updateScriptHash(network)); + .getHistory(addressRecord.scriptHash ?? addressRecord.updateScriptHash(network)!); if (history.isNotEmpty) { addressRecord.setAsUsed(); @@ -1037,7 +987,7 @@ abstract class ElectrumWalletBase element.bitcoinAddressRecord.address == info.address && element.value == info.value) { if (info.isFrozen) totalFrozen += element.value; - if (element.bitcoinAddressRecord.silentPaymentTweak != null) { + if (element.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { totalConfirmed += element.value; } } @@ -1208,18 +1158,29 @@ Future startRefresh(ScanData scanData) async { final electrumClient = scanData.electrumClient; if (!electrumClient.isConnected) { final node = scanData.node; + print(node); await electrumClient.connectToUri(Uri.parse(node)); } - final tweaks = await electrumClient.getTweaks(height: syncHeight); + + List? tweaks; + try { + tweaks = await electrumClient.getTweaks(height: syncHeight); + } catch (e) { + if (e is RequestFailedTimeoutException) { + return scanData.sendPort.send(false); + } + } + + if (tweaks == null) { + return scanData.sendPort.send(false); + } for (var i = 0; i < tweaks.length; i++) { try { // final txid = tweaks.keys.toList()[i]; - final details = tweaks.values.toList()[i]; - print(["details", details]); - final output_pubkeys = (details["output_pubkeys"] as List); - - // print(["Scanning tx:", txid]); + final details = tweaks[i] as Map; + final output_pubkeys = (details["output_pubkeys"] as List); + final tweak = details["tweak"].toString(); // TODO: if tx already scanned & stored skip // if (scanData.transactionHistoryIds.contains(txid)) { @@ -1228,12 +1189,14 @@ Future startRefresh(ScanData scanData) async { // continue; // } - final result = SilentPayment.scanTweak( + final spb = SilentPaymentBuilder(receiverTweak: tweak); + final result = spb.scanOutputs( scanData.primarySilentAddress.b_scan, scanData.primarySilentAddress.B_spend, - details["tweak"] as String, - output_pubkeys.map((e) => BytesUtils.fromHexString(e)).toList(), - labels: scanData.labels, + output_pubkeys + .map((p) => ECPublic.fromBytes(BytesUtils.fromHexString(p.toString()).sublist(2))) + .toList(), + precomputedLabels: scanData.labels, ); if (result.isEmpty) { @@ -1241,113 +1204,102 @@ Future startRefresh(ScanData scanData) async { continue; } - if (result.length > 1) { - print("MULTIPLE UNSPENT COINS FOUND!"); - } else { - print("UNSPENT COIN FOUND!"); - } + result.forEach((key, value) async { + final t_k = value[0]; + final address = + ECPublic.fromHex(key).toTaprootAddress(tweak: false).toAddress(scanData.network); + + final listUnspent = + await electrumClient.getListUnspentWithAddress(address, scanData.network); - // result.forEach((key, value) async { - // final outpoint = output_pubkeys[key]; - - // if (outpoint == null) { - // return; - // } - - // final tweak = value[0]; - // String? label; - // if (value.length > 1) label = value[1]; - - // final txInfo = ElectrumTransactionInfo( - // WalletType.bitcoin, - // id: txid, - // height: syncHeight, - // amount: outpoint.value!, - // fee: 0, - // direction: TransactionDirection.incoming, - // isPending: false, - // date: DateTime.fromMillisecondsSinceEpoch((blockJson["timestamp"] as int) * 1000), - // confirmations: currentChainTip - syncHeight, - // to: bitcoin.SilentPaymentAddress.createLabeledSilentPaymentAddress( - // scanData.primarySilentAddress.scanPubkey, - // scanData.primarySilentAddress.spendPubkey, - // label != null ? label.fromHex : "0".fromHex, - // hrp: scanData.primarySilentAddress.hrp, - // version: scanData.primarySilentAddress.version) - // .toString(), - // unspent: null, - // ); - - // final status = json.decode((await http - // .get(Uri.parse("https://blockstream.info/testnet/api/tx/$txid/outspends"))) - // .body) as List; - - // bool spent = false; - // for (final s in status) { - // if ((s["spent"] as bool) == true) { - // spent = true; - - // scanData.sendPort.send({txid: txInfo}); - - // final sentTxId = s["txid"] as String; - // final sentTx = json.decode( - // (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$sentTxId"))) - // .body); - - // int amount = 0; - // for (final out in (sentTx["vout"] as List)) { - // amount += out["value"] as int; - // } - - // final height = s["status"]["block_height"] as int; - - // scanData.sendPort.send({ - // sentTxId: ElectrumTransactionInfo( - // WalletType.bitcoin, - // id: sentTxId, - // height: height, - // amount: amount, - // fee: 0, - // direction: TransactionDirection.outgoing, - // isPending: false, - // date: DateTime.fromMillisecondsSinceEpoch( - // (s["status"]["block_time"] as int) * 1000), - // confirmations: currentChainTip - height, - // ) - // }); - // } - // } - - // if (spent) { - // return; - // } - - // final unspent = BitcoinUnspent( - // BitcoinAddressRecord( - // bitcoin.P2trAddress(program: key, networkType: scanData.network).address, - // index: 0, - // isHidden: true, - // isUsed: true, - // silentAddressLabel: null, - // silentPaymentTweak: tweak, - // type: bitcoin.AddressType.p2tr, - // ), - // txid, - // outpoint.value!, - // outpoint.index, - // silentPaymentTweak: tweak, - // type: bitcoin.AddressType.p2tr, - // ); - - // // found utxo for tx, send unspent coin to main isolate - // scanData.sendPort.send(unspent); - - // // also send tx data for tx history - // txInfo.unspent = unspent; - // scanData.sendPort.send({txid: txInfo}); - // }); + BitcoinUnspent? info; + await Future.forEach>(listUnspent, (unspent) async { + try { + final addressRecord = BitcoinSilentPaymentAddressRecord( + address, + index: 0, + isHidden: true, + isUsed: true, + network: scanData.network, + silentPaymentTweak: t_k, + type: SegwitAddresType.p2tr, + ); + info = BitcoinUnspent.fromJSON(addressRecord, unspent); + } catch (_) {} + }); + + // final tweak = value[0]; + // String? label; + // if (value.length > 1) label = value[1]; + + final tx = info!; + final txInfo = ElectrumTransactionInfo( + WalletType.bitcoin, + id: tx.hash, + height: syncHeight, + amount: tx.value, + fee: 0, + direction: TransactionDirection.incoming, + isPending: false, + date: DateTime.now(), + confirmations: currentChainTip - syncHeight, + to: scanData.primarySilentAddress.toString(), + unspent: tx, + ); + + // final status = json.decode((await http + // .get(Uri.parse("https://blockstream.info/testnet/api/tx/$txid/outspends"))) + // .body) as List; + + // bool spent = false; + // for (final s in status) { + // if ((s["spent"] as bool) == true) { + // spent = true; + + // scanData.sendPort.send({txid: txInfo}); + + // final sentTxId = s["txid"] as String; + // final sentTx = json.decode( + // (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$sentTxId"))) + // .body); + + // int amount = 0; + // for (final out in (sentTx["vout"] as List)) { + // amount += out["value"] as int; + // } + + // final height = s["status"]["block_height"] as int; + + // scanData.sendPort.send({ + // sentTxId: ElectrumTransactionInfo( + // WalletType.bitcoin, + // id: sentTxId, + // height: height, + // amount: amount, + // fee: 0, + // direction: TransactionDirection.outgoing, + // isPending: false, + // date: DateTime.fromMillisecondsSinceEpoch( + // (s["status"]["block_time"] as int) * 1000), + // confirmations: currentChainTip - height, + // ) + // }); + // } + // } + + // if (spent) { + // return; + // } + + // found utxo for tx, send unspent coin to main isolate + // scanData.sendPort.send(txInfo); + + // also send tx data for tx history + scanData.sendPort.send({txInfo.id: txInfo}); + }); } catch (_) {} } + // break; // Finished scanning block, add 1 to height and continue to next block in loop syncHeight += 1; @@ -1383,6 +1335,8 @@ BitcoinBaseAddress _addressTypeFromStr(String address, BasedUtxoNetwork network) return P2wshAddress.fromAddress(address: address, network: network); } else if (P2trAddress.regex.hasMatch(address)) { return P2trAddress.fromAddress(address: address, network: network); + } else if (SilentPaymentAddress.regex.hasMatch(address)) { + return SilentPaymentAddress.fromAddress(address); } else { return P2wpkhAddress.fromAddress(address: address, network: network); } @@ -1397,59 +1351,8 @@ BitcoinAddressType _getScriptType(BitcoinBaseAddress type) { return SegwitAddresType.p2wsh; } else if (type is P2trAddress) { return SegwitAddresType.p2tr; - } else { - return SegwitAddresType.p2wpkh; - } -} - -class EstimateTxParams { - EstimateTxParams( - {required this.amount, - required this.feeRate, - required this.priority, - required this.outputsCount, - required this.size}); - - final int amount; - final int feeRate; - final TransactionPriority priority; - final int outputsCount; - final int size; -} - -class EstimatedTxResult { - EstimatedTxResult( - {required this.utxos, required this.privateKeys, required this.fee, required this.amount}); - - final List utxos; - final List privateKeys; - final int fee; - final int amount; -} - -BitcoinBaseAddress _addressTypeFromStr(String address, BasedUtxoNetwork network) { - if (P2pkhAddress.regex.hasMatch(address)) { - return P2pkhAddress.fromAddress(address: address, network: network); - } else if (P2shAddress.regex.hasMatch(address)) { - return P2shAddress.fromAddress(address: address, network: network); - } else if (P2wshAddress.regex.hasMatch(address)) { - return P2wshAddress.fromAddress(address: address, network: network); - } else if (P2trAddress.regex.hasMatch(address)) { - return P2trAddress.fromAddress(address: address, network: network); - } else { - return P2wpkhAddress.fromAddress(address: address, network: network); - } -} - -BitcoinAddressType _getScriptType(BitcoinBaseAddress type) { - if (type is P2pkhAddress) { - return P2pkhAddressType.p2pkh; - } else if (type is P2shAddress) { - return P2shAddressType.p2wpkhInP2sh; - } else if (type is P2wshAddress) { - return SegwitAddresType.p2wsh; - } else if (type is P2trAddress) { - return SegwitAddresType.p2tr; + } else if (type is SilentPaymentsAddresType) { + return SilentPaymentsAddresType.p2sp; } else { return SegwitAddresType.p2wpkh; } diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index bd6781ea80..ec2a27498c 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -1,6 +1,7 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bitbox/bitbox.dart' as bitbox; +import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; @@ -28,7 +29,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { List? initialAddresses, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, - List? initialSilentAddresses, + List? initialSilentAddresses, int initialSilentAddressIndex = 0, SilentPaymentOwner? silentAddress, }) : _addresses = ObservableList.of((initialAddresses ?? []).toSet()), @@ -46,9 +47,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { _addressPageType = walletInfo.addressPageType != null ? BitcoinAddressType.fromValue(walletInfo.addressPageType!) : SegwitAddresType.p2wpkh, - silentAddresses = ObservableList.of((initialSilentAddresses ?? []) - .where((addressRecord) => addressRecord.silentPaymentTweak != null) - .toSet()), + silentAddresses = ObservableList.of( + (initialSilentAddresses ?? []).toSet()), currentSilentAddressIndex = initialSilentAddressIndex, super(walletInfo) { updateAddressesByMatch(); @@ -59,15 +59,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { static const gap = 20; static String toCashAddr(String address) => bitbox.Address.toCashAddress(address); - static String toLegacy(String address) => bitbox.Address.toLegacyAddress(address); final ObservableList _addresses; - // Matched by addressPageType - late ObservableList addressesByReceiveType; + late ObservableList addressesByReceiveType; final ObservableList receiveAddresses; final ObservableList changeAddresses; - final ObservableList silentAddresses; + final ObservableList silentAddresses; final BasedUtxoNetwork network; final bitcoin.HDWallet mainHd; final bitcoin.HDWallet sideHd; @@ -101,23 +99,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final typeMatchingReceiveAddresses = receiveAddresses.where(_isAddressPageTypeMatch); - if ((isEnabledAutoGenerateSubaddress && receiveAddresses.isEmpty) || - typeMatchingReceiveAddresses.isEmpty) { - receiveAddress = generateNewAddress().address; - } else { - final previousAddressMatchesType = - previousAddressRecord != null && previousAddressRecord!.type == addressPageType; - - if (previousAddressMatchesType && - typeMatchingReceiveAddresses.first.address != addressesByReceiveType.first.address) { - receiveAddress = previousAddressRecord!.address; - } else { - receiveAddress = typeMatchingReceiveAddresses.first.address; - } - final receiveAddress = receiveAddresses.first.address; - - final typeMatchingReceiveAddresses = receiveAddresses.where(_isAddressPageTypeMatch); - if ((isEnabledAutoGenerateSubaddress && receiveAddresses.isEmpty) || typeMatchingReceiveAddresses.isEmpty) { receiveAddress = generateNewAddress().address; @@ -239,38 +220,36 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } Map get labels { + final G = ECPublic.fromBytes(BigintUtils.toBytes(Curves.generatorSecp256k1.x, length: 32)); final labels = {}; for (int i = 0; i < silentAddresses.length; i++) { final silentAddressRecord = silentAddresses[i]; - final silentAddress = - SilentPaymentDestination.fromAddress(silentAddressRecord.address, 0).B_spend.toHex(); - - if (silentAddressRecord.silentPaymentTweak != null) - labels[silentAddress] = silentAddressRecord.silentPaymentTweak!; + final silentPaymentTweak = silentAddressRecord.silentPaymentTweak; + labels[G + .tweakMul(BigintUtils.fromBytes(BytesUtils.fromHexString(silentPaymentTweak))) + .toHex()] = silentPaymentTweak; } return labels; } - BitcoinAddressRecord generateNewAddress({String label = ''}) { + @action + BaseBitcoinAddressRecord generateNewAddress({String label = ''}) { if (addressPageType == SilentPaymentsAddresType.p2sp) { currentSilentAddressIndex += 1; - final tweak = BigInt.from(currentSilentAddressIndex); - - final address = BitcoinAddressRecord( - SilentPaymentAddress.createLabeledSilentPaymentAddress( - primarySilentAddress!.B_scan, primarySilentAddress!.B_spend, tweak, - hrp: primarySilentAddress!.hrp, version: primarySilentAddress!.version) - .toString(), + final address = BitcoinSilentPaymentAddressRecord( + primarySilentAddress!.toLabeledSilentPaymentAddress(currentSilentAddressIndex).toString(), index: currentSilentAddressIndex, isHidden: false, name: label, - silentPaymentTweak: tweak.toString(), + silentPaymentTweak: + BytesUtils.toHexString(primarySilentAddress!.generateLabel(currentSilentAddressIndex)), network: network, type: SilentPaymentsAddresType.p2sp, ); silentAddresses.add(address); + updateAddressesByMatch(); return address; } @@ -321,6 +300,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @action void updateAddressesByMatch() { + if (addressPageType == SilentPaymentsAddresType.p2sp) { + addressesByReceiveType.clear(); + addressesByReceiveType.addAll(silentAddresses); + return; + } + addressesByReceiveType.clear(); addressesByReceiveType.addAll(_addresses.where(_isAddressPageTypeMatch).toList()); } diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index ceb603f9f3..9f71d0bd82 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -30,7 +30,7 @@ class ElectrumWalletSnapshot { String mnemonic; List addresses; - List silentAddresses; + List silentAddresses; ElectrumBalance balance; Map regularAddressIndex; Map changeAddressIndex; @@ -52,7 +52,7 @@ class ElectrumWalletSnapshot { final silentAddressesTmp = data['silent_addresses'] as List? ?? []; final silentAddresses = silentAddressesTmp .whereType() - .map((addr) => BitcoinAddressRecord.fromJSON(addr, network: network)) + .map((addr) => BitcoinSilentPaymentAddressRecord.fromJSON(addr, network: network)) .toList(); final balance = ElectrumBalance.fromJSON(data['balance'] as String) ?? diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index aff28df6eb..c7750f1afb 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -80,7 +80,7 @@ packages: description: path: "." ref: cake-update-v2 - resolved-ref: e4686da77cace5400697de69f7885020297cb900 + resolved-ref: "43b80bcf0ef6e7224603a6b8874b61efec3c6a4c" url: "https://github.com/cake-tech/bitcoin_base.git" source: git version: "4.0.0" @@ -93,6 +93,15 @@ packages: url: "https://github.com/cake-tech/bitcoin_flutter.git" source: git version: "2.1.0" + blockchain_utils: + dependency: "direct main" + description: + path: "." + ref: cake-update-v1 + resolved-ref: "6a0b891db4d90c647ebf5fc3a9132e614c70e1c6" + url: "https://github.com/cake-tech/blockchain_utils" + source: git + version: "1.6.0" boolean_selector: dependency: transitive description: diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 13a0e26886..bfa6c72e60 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: ref: cake-update-v2 blockchain_utils: git: - url: https://github.com/rafael-xmr/blockchain_utils + url: https://github.com/cake-tech/blockchain_utils ref: cake-update-v1 dev_dependencies: diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index fd7ed5a0fc..a967681503 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -35,7 +35,7 @@ dependencies: ref: cake-update-v2 blockchain_utils: git: - url: https://github.com/rafael-xmr/blockchain_utils + url: https://github.com/cake-tech/blockchain_utils ref: cake-update-v1 dev_dependencies: diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index afddc7c7a7..f562e7ce32 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -58,6 +58,11 @@ class ConnectedSyncStatus extends SyncStatus { double progress() => 0.0; } +class UnsupportedSyncStatus extends SyncStatus { + @override + double progress() => 1.0; +} + class LostConnectionSyncStatus extends SyncStatus { @override double progress() => 1.0; diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index fb470a58f1..1ce79352cc 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -101,7 +101,7 @@ class CWBitcoin extends Bitcoin { List getAddresses(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; return bitcoinWallet.walletAddresses.addressesByReceiveType - .map((BitcoinAddressRecord addr) => addr.address) + .map((BaseBitcoinAddressRecord addr) => addr.address) .toList(); } @@ -110,7 +110,7 @@ class CWBitcoin extends Bitcoin { List getSubAddresses(Object wallet) { final electrumWallet = wallet as ElectrumWallet; return electrumWallet.walletAddresses.addressesByReceiveType - .map((BitcoinAddressRecord addr) => ElectrumSubAddress( + .map((BaseBitcoinAddressRecord addr) => ElectrumSubAddress( id: addr.index, name: addr.name, address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address, @@ -195,7 +195,7 @@ class CWBitcoin extends Bitcoin { @override List getBitcoinReceivePageOptions() => BitcoinReceivePageOption.all; - List getSilentAddresses(Object wallet) { + List getSilentAddresses(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; return bitcoinWallet.walletAddresses.silentAddresses; } diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index 226b97f508..23bfb0db28 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -36,5 +36,9 @@ String syncStatusTitle(SyncStatus syncStatus) { return S.current.sync_status_failed_connect; } + if (syncStatus is UnsupportedSyncStatus) { + return S.current.sync_status_unsupported; + } + return ''; } diff --git a/model_generator.sh b/model_generator.sh index fa1ea6fac0..062dcf0e2c 100755 --- a/model_generator.sh +++ b/model_generator.sh @@ -5,6 +5,7 @@ cd cw_bitcoin; flutter pub get; flutter packages pub run build_runner build --de cd cw_haven; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_nano; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_bitcoin_cash; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. +cd cw_solana; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_polygon; flutter pub get; cd .. cd cw_ethereum; flutter pub get; cd .. flutter packages pub run build_runner build --delete-conflicting-outputs diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 02024fe1a9..835f1909ef 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "بدء المزامنة", "sync_status_syncronized": "متزامن", "sync_status_syncronizing": "يتم المزامنة", + "sync_status_unsupported": "عقدة غير مدعومة", "syncing_wallet_alert_content": "قد لا يكتمل رصيدك وقائمة المعاملات الخاصة بك حتى تظهر عبارة “SYNCHRONIZED“ في الأعلى. انقر / اضغط لمعرفة المزيد.", "syncing_wallet_alert_title": "محفظتك تتم مزامنتها", "template": "قالب", @@ -733,6 +734,7 @@ "view_key_private": "مفتاح العرض (خاص)", "view_key_public": "مفتاح العرض (عام)", "view_transaction_on": "عرض العملية على", + "waitFewSecondForTxUpdate": "ﺕﻼﻣﺎﻌﻤﻟﺍ ﻞﺠﺳ ﻲﻓ ﺔﻠﻣﺎﻌﻤﻟﺍ ﺲﻜﻌﻨﺗ ﻰﺘﺣ ﻥﺍﻮﺛ ﻊﻀﺒﻟ ﺭﺎﻈﺘﻧﻻﺍ ﻰﺟﺮﻳ", "wallet_keys": "سييد المحفظة / المفاتيح", "wallet_list_create_new_wallet": "إنشاء محفظة جديدة", "wallet_list_edit_wallet": "تحرير المحفظة", @@ -783,6 +785,5 @@ "you_pay": "انت تدفع", "you_will_get": "حول الى", "you_will_send": "تحويل من", - "yy": "YY", - "waitFewSecondForTxUpdate": "ﺕﻼﻣﺎﻌﻤﻟﺍ ﻞﺠﺳ ﻲﻓ ﺔﻠﻣﺎﻌﻤﻟﺍ ﺲﻜﻌﻨﺗ ﻰﺘﺣ ﻥﺍﻮﺛ ﻊﻀﺒﻟ ﺭﺎﻈﺘﻧﻻﺍ ﻰﺟﺮﻳ" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 807592f913..db73a2e15a 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "ЗАПОЧВАНЕ НА СИНХРОНИЗАЦИЯ", "sync_status_syncronized": "СИНХРОНИЗИРАНО", "sync_status_syncronizing": "СИНХРОНИЗИРАНЕ", + "sync_status_unsupported": "Неподдържан възел", "syncing_wallet_alert_content": "Списъкът ви с баланс и транзакции може да не е пълен, докато в горната част не пише „СИНХРОНИЗИРАН“. Кликнете/докоснете, за да научите повече.", "syncing_wallet_alert_title": "Вашият портфейл се синхронизира", "template": "Шаблон", @@ -733,6 +734,7 @@ "view_key_private": "View key (таен)", "view_key_public": "View key (публичен)", "view_transaction_on": "Вижте транзакция на ", + "waitFewSecondForTxUpdate": "Моля, изчакайте няколко секунди, докато транзакцията се отрази в историята на транзакциите", "wallet_keys": "Seed/keys на портфейла", "wallet_list_create_new_wallet": "Създаване на нов портфейл", "wallet_list_edit_wallet": "Редактиране на портфейла", @@ -783,6 +785,5 @@ "you_pay": "Вие плащате", "you_will_get": "Обръщане в", "you_will_send": "Обръщане от", - "yy": "гг", - "waitFewSecondForTxUpdate": "Моля, изчакайте няколко секунди, докато транзакцията се отрази в историята на транзакциите" -} + "yy": "гг" +} \ No newline at end of file diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 74d7298da6..0f2242b334 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "SPOUŠTĚNÍ SYNCHRONIZACE", "sync_status_syncronized": "SYNCHRONIZOVÁNO", "sync_status_syncronizing": "SYNCHRONIZUJI", + "sync_status_unsupported": "Nepodporovaný uzel", "syncing_wallet_alert_content": "Váš seznam zůstatků a transakcí nemusí být úplný, dokud nebude nahoře uvedeno „SYNCHRONIZOVANÉ“. Kliknutím/klepnutím se dozvíte více.", "syncing_wallet_alert_title": "Vaše peněženka se synchronizuje", "template": "Šablona", @@ -733,6 +734,7 @@ "view_key_private": "Klíč pro zobrazení (soukromý)", "view_key_public": "Klíč pro zobrazení (veřejný)", "view_transaction_on": "Zobrazit transakci na ", + "waitFewSecondForTxUpdate": "Počkejte několik sekund, než se transakce projeví v historii transakcí", "wallet_keys": "Seed/klíče peněženky", "wallet_list_create_new_wallet": "Vytvořit novou peněženku", "wallet_list_edit_wallet": "Upravit peněženku", @@ -783,6 +785,5 @@ "you_pay": "Zaplatíte", "you_will_get": "Směnit na", "you_will_send": "Směnit z", - "yy": "YY", - "waitFewSecondForTxUpdate": "Počkejte několik sekund, než se transakce projeví v historii transakcí" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index d71f68fd4c..d21d57a983 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "STARTE SYNCHRONISIERUNG", "sync_status_syncronized": "SYNCHRONISIERT", "sync_status_syncronizing": "SYNCHRONISIERE", + "sync_status_unsupported": "Nicht unterstützter Knoten", "syncing_wallet_alert_content": "Ihr Kontostand und Ihre Transaktionsliste sind möglicherweise erst vollständig, wenn oben „SYNCHRONISIERT“ steht. Klicken/tippen Sie, um mehr zu erfahren.", "syncing_wallet_alert_title": "Ihr Wallet wird synchronisiert", "template": "Vorlage", @@ -735,6 +736,7 @@ "view_key_private": "View Key (geheim)", "view_key_public": "View Key (öffentlich)", "view_transaction_on": "Anzeigen der Transaktion auf ", + "waitFewSecondForTxUpdate": "Bitte warten Sie einige Sekunden, bis die Transaktion im Transaktionsverlauf angezeigt wird", "waiting_payment_confirmation": "Warte auf Zahlungsbestätigung", "wallet_keys": "Wallet-Seed/-Schlüssel", "wallet_list_create_new_wallet": "Neue Wallet erstellen", @@ -786,6 +788,5 @@ "you_pay": "Sie bezahlen", "you_will_get": "Konvertieren zu", "you_will_send": "Konvertieren von", - "yy": "YY", - "waitFewSecondForTxUpdate": "Bitte warten Sie einige Sekunden, bis die Transaktion im Transaktionsverlauf angezeigt wird" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index d943ed4cd8..c3acebf6d1 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "STARTING SYNC", "sync_status_syncronized": "SYNCHRONIZED", "sync_status_syncronizing": "SYNCHRONIZING", + "sync_status_unsupported": "UNSUPPORTED NODE", "syncing_wallet_alert_content": "Your balance and transaction list may not be complete until it says “SYNCHRONIZED” at the top. Click/tap to learn more.", "syncing_wallet_alert_title": "Your wallet is syncing", "template": "Template", @@ -733,6 +734,7 @@ "view_key_private": "View key (private)", "view_key_public": "View key (public)", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "Kindly wait for a few seconds for transaction to reflect in transactions history", "wallet_keys": "Wallet seed/keys", "wallet_list_create_new_wallet": "Create New Wallet", "wallet_list_edit_wallet": "Edit wallet", @@ -783,6 +785,5 @@ "you_pay": "You Pay", "you_will_get": "Convert to", "you_will_send": "Convert from", - "yy": "YY", - "waitFewSecondForTxUpdate": "Kindly wait for a few seconds for transaction to reflect in transactions history" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 9cea08be43..d2fd93dfff 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "EMPEZANDO A SINCRONIZAR", "sync_status_syncronized": "SINCRONIZADO", "sync_status_syncronizing": "SINCRONIZANDO", + "sync_status_unsupported": "Nodo no compatible", "syncing_wallet_alert_content": "Es posible que su lista de saldo y transacciones no esté completa hasta que diga \"SINCRONIZADO\" en la parte superior. Haga clic/toque para obtener más información.", "syncing_wallet_alert_title": "Tu billetera se está sincronizando", "template": "Plantilla", @@ -734,6 +735,7 @@ "view_key_private": "View clave (privado)", "view_key_public": "View clave (público)", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "Espere unos segundos para que la transacción se refleje en el historial de transacciones.", "wallet_keys": "Billetera semilla/claves", "wallet_list_create_new_wallet": "Crear nueva billetera", "wallet_list_edit_wallet": "Editar billetera", @@ -784,6 +786,5 @@ "you_pay": "Tú pagas", "you_will_get": "Convertir a", "you_will_send": "Convertir de", - "yy": "YY", - "waitFewSecondForTxUpdate": "Espere unos segundos para que la transacción se refleje en el historial de transacciones." -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index ac2d944dbb..61f15497c8 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "DÉBUT DE SYNCHRO", "sync_status_syncronized": "SYNCHRONISÉ", "sync_status_syncronizing": "SYNCHRONISATION EN COURS", + "sync_status_unsupported": "Nœud non pris en charge", "syncing_wallet_alert_content": "Votre solde et votre liste de transactions peuvent ne pas être à jour tant que la mention « SYNCHRONISÉ » n'apparaît en haut de l'écran. Cliquez/appuyez pour en savoir plus.", "syncing_wallet_alert_title": "Votre portefeuille (wallet) est en cours de synchronisation", "template": "Modèle", @@ -733,6 +734,7 @@ "view_key_private": "Clef d'audit (view key) (privée)", "view_key_public": "Clef d'audit (view key) (publique)", "view_transaction_on": "Voir la Transaction sur ", + "waitFewSecondForTxUpdate": "Veuillez attendre quelques secondes pour que la transaction soit reflétée dans l'historique des transactions.", "wallet_keys": "Phrase secrète (seed)/Clefs du portefeuille (wallet)", "wallet_list_create_new_wallet": "Créer un Nouveau Portefeuille (Wallet)", "wallet_list_edit_wallet": "Modifier le portefeuille", @@ -783,6 +785,5 @@ "you_pay": "Vous payez", "you_will_get": "Convertir vers", "you_will_send": "Convertir depuis", - "yy": "AA", - "waitFewSecondForTxUpdate": "Veuillez attendre quelques secondes pour que la transaction soit reflétée dans l'historique des transactions." -} + "yy": "AA" +} \ No newline at end of file diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 557e6951ed..7b71df8539 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "KWAFI", "sync_status_syncronized": "KYAU", "sync_status_syncronizing": "KWAFI", + "sync_status_unsupported": "Ba a Taimako ba", "syncing_wallet_alert_content": "Ma'aunin ku da lissafin ma'amala bazai cika ba har sai an ce \"SYNCHRONIZED\" a saman. Danna/matsa don ƙarin koyo.", "syncing_wallet_alert_title": "Walat ɗin ku yana aiki tare", "template": "Samfura", @@ -735,6 +736,7 @@ "view_key_private": "Duba maɓallin (maɓallin kalmar sirri)", "view_key_public": "Maɓallin Duba (maɓallin jama'a)", "view_transaction_on": "Dubo aikace-aikacen akan", + "waitFewSecondForTxUpdate": "Da fatan za a jira ƴan daƙiƙa don ciniki don yin tunani a tarihin ma'amala", "wallet_keys": "Iri/maɓalli na walat", "wallet_list_create_new_wallet": "Ƙirƙiri Sabon Wallet", "wallet_list_edit_wallet": "Gyara walat", @@ -785,6 +787,5 @@ "you_pay": "Ka Bayar", "you_will_get": "Maida zuwa", "you_will_send": "Maida daga", - "yy": "YY", - "waitFewSecondForTxUpdate": "Da fatan za a jira ƴan daƙiƙa don ciniki don yin tunani a tarihin ma'amala" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 12383e2fb3..b90bb40d7e 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "सिताज़ा करना", "sync_status_syncronized": "सिंक्रनाइज़", "sync_status_syncronizing": "सिंक्रनाइज़ करने", + "sync_status_unsupported": "असमर्थित नोड", "syncing_wallet_alert_content": "आपकी शेष राशि और लेनदेन सूची तब तक पूरी नहीं हो सकती जब तक कि शीर्ष पर \"सिंक्रनाइज़्ड\" न लिखा हो। अधिक जानने के लिए क्लिक/टैप करें।", "syncing_wallet_alert_title": "आपका वॉलेट सिंक हो रहा है", "template": "खाका", @@ -735,6 +736,7 @@ "view_key_private": "कुंजी देखें(निजी)", "view_key_public": "कुंजी देखें (जनता)", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "लेन-देन इतिहास में लेन-देन प्रतिबिंबित होने के लिए कृपया कुछ सेकंड प्रतीक्षा करें", "wallet_keys": "बटुआ बीज / चाबियाँ", "wallet_list_create_new_wallet": "नया बटुआ बनाएँ", "wallet_list_edit_wallet": "बटुआ संपादित करें", @@ -785,6 +787,5 @@ "you_pay": "आप भुगतान करते हैं", "you_will_get": "में बदलें", "you_will_send": "से रूपांतरित करें", - "yy": "वाईवाई", - "waitFewSecondForTxUpdate": "लेन-देन इतिहास में लेन-देन प्रतिबिंबित होने के लिए कृपया कुछ सेकंड प्रतीक्षा करें" -} + "yy": "वाईवाई" +} \ No newline at end of file diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index ac351b03ed..8d9cce1832 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "ZAPOČINJEMO SINKRONIZIRANJE", "sync_status_syncronized": "SINKRONIZIRANO", "sync_status_syncronizing": "SINKRONIZIRANJE", + "sync_status_unsupported": "Nepodržani čvor", "syncing_wallet_alert_content": "Vaš saldo i popis transakcija možda neće biti potpuni sve dok na vrhu ne piše \"SINKRONIZIRANO\". Kliknite/dodirnite da biste saznali više.", "syncing_wallet_alert_title": "Vaš novčanik se sinkronizira", "template": "Predložak", @@ -733,6 +734,7 @@ "view_key_private": "View key (privatni)", "view_key_public": "View key (javni)", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "Pričekajte nekoliko sekundi da se transakcija prikaže u povijesti transakcija", "wallet_keys": "Pristupni izraz/ključ novčanika", "wallet_list_create_new_wallet": "Izradi novi novčanik", "wallet_list_edit_wallet": "Uredi novčanik", @@ -783,6 +785,5 @@ "you_pay": "Vi plaćate", "you_will_get": "Razmijeni u", "you_will_send": "Razmijeni iz", - "yy": "GG", - "waitFewSecondForTxUpdate": "Pričekajte nekoliko sekundi da se transakcija prikaže u povijesti transakcija" -} + "yy": "GG" +} \ No newline at end of file diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 2756fcca06..53c3446680 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "MULAI SINKRONISASI", "sync_status_syncronized": "SUDAH TERSINKRONISASI", "sync_status_syncronizing": "SEDANG SINKRONISASI", + "sync_status_unsupported": "Node yang tidak didukung", "syncing_wallet_alert_content": "Saldo dan daftar transaksi Anda mungkin belum lengkap sampai tertulis “SYNCHRONIZED” di bagian atas. Klik/ketuk untuk mempelajari lebih lanjut.", "syncing_wallet_alert_title": "Dompet Anda sedang disinkronkan", "template": "Template", @@ -736,6 +737,7 @@ "view_key_private": "Kunci tampilan (privat)", "view_key_public": "Kunci tampilan (publik)", "view_transaction_on": "Lihat Transaksi di ", + "waitFewSecondForTxUpdate": "Mohon tunggu beberapa detik hingga transaksi terlihat di riwayat transaksi", "wallet_keys": "Seed/kunci dompet", "wallet_list_create_new_wallet": "Buat Dompet Baru", "wallet_list_edit_wallet": "Edit dompet", @@ -786,6 +788,5 @@ "you_pay": "Anda Membayar", "you_will_get": "Konversi ke", "you_will_send": "Konversi dari", - "yy": "YY", - "waitFewSecondForTxUpdate": "Mohon tunggu beberapa detik hingga transaksi terlihat di riwayat transaksi" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 83597d4c91..7b9b8d9fc4 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "INIZIO SINC", "sync_status_syncronized": "SINCRONIZZATO", "sync_status_syncronizing": "SINCRONIZZAZIONE", + "sync_status_unsupported": "Nodo non supportato", "syncing_wallet_alert_content": "Il saldo e l'elenco delle transazioni potrebbero non essere completi fino a quando non viene visualizzato \"SYNCHRONIZED\" in alto. Clicca/tocca per saperne di più.", "syncing_wallet_alert_title": "Il tuo portafoglio si sta sincronizzando", "template": "Modello", @@ -735,6 +736,7 @@ "view_key_private": "Chiave di visualizzazione (privata)", "view_key_public": "Chiave di visualizzazione (pubblica)", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "Attendi qualche secondo affinché la transazione venga riflessa nella cronologia delle transazioni", "waiting_payment_confirmation": "In attesa di conferma del pagamento", "wallet_keys": "Seme Portafoglio /chiavi", "wallet_list_create_new_wallet": "Crea Nuovo Portafoglio", @@ -786,6 +788,5 @@ "you_pay": "Tu paghi", "you_will_get": "Converti a", "you_will_send": "Conveti da", - "yy": "YY", - "waitFewSecondForTxUpdate": "Attendi qualche secondo affinché la transazione venga riflessa nella cronologia delle transazioni" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index eb74091a97..1e6006a5c8 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "同期の開始", "sync_status_syncronized": "同期された", "sync_status_syncronizing": "同期", + "sync_status_unsupported": "サポートされていないノード", "syncing_wallet_alert_content": "上部に「同期済み」と表示されるまで、残高と取引リストが完了していない可能性があります。詳細については、クリック/タップしてください。", "syncing_wallet_alert_title": "ウォレットは同期中です", "template": "テンプレート", @@ -734,6 +735,7 @@ "view_key_private": "ビューキー (プライベート)", "view_key_public": "ビューキー (パブリック)", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "取引履歴に取引が反映されるまで数秒お待ちください。", "wallet_keys": "ウォレットシード/キー", "wallet_list_create_new_wallet": "新しいウォレットを作成", "wallet_list_edit_wallet": "ウォレットを編集する", @@ -784,6 +786,5 @@ "you_pay": "あなたが支払う", "you_will_get": "に変換", "you_will_send": "から変換", - "yy": "YY", - "waitFewSecondForTxUpdate": "取引履歴に取引が反映されるまで数秒お待ちください。" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 9a8bf48838..3171b67484 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "동기화 시작", "sync_status_syncronized": "동기화", "sync_status_syncronizing": "동기화", + "sync_status_unsupported": "지원되지 않은 노드", "syncing_wallet_alert_content": "상단에 \"동기화됨\"이라고 표시될 때까지 잔액 및 거래 목록이 완전하지 않을 수 있습니다. 자세히 알아보려면 클릭/탭하세요.", "syncing_wallet_alert_title": "지갑 동기화 중", "template": "주형", @@ -734,6 +735,7 @@ "view_key_private": "키보기(은밀한)", "view_key_public": "키보기 (공공의)", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "거래 내역에 거래가 반영될 때까지 몇 초 정도 기다려 주세요.", "wallet_keys": "지갑 시드 / 키", "wallet_list_create_new_wallet": "새 월렛 만들기", "wallet_list_edit_wallet": "지갑 수정", @@ -785,6 +787,5 @@ "you_will_get": "로 변환하다", "you_will_send": "다음에서 변환", "YY": "YY", - "yy": "YY", - "waitFewSecondForTxUpdate": "거래 내역에 거래가 반영될 때까지 몇 초 정도 기다려 주세요." -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 711c3c8841..2eacf4180f 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "စင့်ခ်လုပ်ခြင်း။", "sync_status_syncronized": "ထပ်တူပြုထားသည်။", "sync_status_syncronizing": "ထပ်တူပြုခြင်း။", + "sync_status_unsupported": "node မထောက်ပံ့ node ကို", "syncing_wallet_alert_content": "သင်၏လက်ကျန်နှင့် ငွေပေးငွေယူစာရင်းသည် ထိပ်တွင် \"Synchronizeed\" ဟုပြောသည်အထိ မပြီးမြောက်နိုင်ပါ။ ပိုမိုလေ့လာရန် နှိပ်/နှိပ်ပါ။", "syncing_wallet_alert_title": "သင့်ပိုက်ဆံအိတ်ကို စင့်ခ်လုပ်နေပါသည်။", "template": "ပုံစံခွက်", @@ -733,6 +734,7 @@ "view_key_private": "သော့ကိုကြည့်ရန် (သီးသန့်)", "view_key_public": "သော့ကိုကြည့်ရန် (အများပြည်သူ)", "view_transaction_on": "ငွေလွှဲခြင်းကို ဖွင့်ကြည့်ပါ။", + "waitFewSecondForTxUpdate": "ငွေပေးငွေယူ မှတ်တမ်းတွင် ရောင်ပြန်ဟပ်ရန် စက္ကန့်အနည်းငယ်စောင့်ပါ။", "wallet_keys": "ပိုက်ဆံအိတ် အစေ့/သော့များ", "wallet_list_create_new_wallet": "Wallet အသစ်ဖန်တီးပါ။", "wallet_list_edit_wallet": "ပိုက်ဆံအိတ်ကို တည်းဖြတ်ပါ။", @@ -783,6 +785,5 @@ "you_pay": "သင်ပေးချေပါ။", "you_will_get": "သို့ပြောင်းပါ။", "you_will_send": "မှပြောင်းပါ။", - "yy": "YY", - "waitFewSecondForTxUpdate": "ငွေပေးငွေယူ မှတ်တမ်းတွင် ရောင်ပြန်ဟပ်ရန် စက္ကန့်အနည်းငယ်စောင့်ပါ။" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 7527eb10fa..1151517482 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "BEGINNEN MET SYNCHRONISEREN", "sync_status_syncronized": "SYNCHRONIZED", "sync_status_syncronizing": "SYNCHRONISEREN", + "sync_status_unsupported": "Niet ondersteund knooppunt", "syncing_wallet_alert_content": "Uw saldo- en transactielijst is mogelijk pas compleet als er bovenaan 'GESYNCHRONISEERD' staat. Klik/tik voor meer informatie.", "syncing_wallet_alert_title": "Uw portemonnee wordt gesynchroniseerd", "template": "Sjabloon", @@ -733,6 +734,7 @@ "view_key_private": "Bekijk sleutel (privaat)", "view_key_public": "Bekijk sleutel (openbaar)", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "Wacht een paar seconden totdat de transactie wordt weergegeven in de transactiegeschiedenis", "waiting_payment_confirmation": "In afwachting van betalingsbevestiging", "wallet_keys": "Portemonnee zaad/sleutels", "wallet_list_create_new_wallet": "Maak een nieuwe portemonnee", @@ -784,6 +786,5 @@ "you_pay": "U betaalt", "you_will_get": "Converteren naar", "you_will_send": "Converteren van", - "yy": "JJ", - "waitFewSecondForTxUpdate": "Wacht een paar seconden totdat de transactie wordt weergegeven in de transactiegeschiedenis" -} + "yy": "JJ" +} \ No newline at end of file diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index e93e56f743..0e84e5c874 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "ROZPOCZĘCIE SYNCHRONIZACJI", "sync_status_syncronized": "ZSYNCHRONIZOWANO", "sync_status_syncronizing": "SYNCHRONIZACJA", + "sync_status_unsupported": "Nieobsługiwany węzeł", "syncing_wallet_alert_content": "Twoje saldo i lista transakcji mogą nie być kompletne, dopóki u góry nie pojawi się napis „SYNCHRONIZOWANY”. Kliknij/stuknij, aby dowiedzieć się więcej.", "syncing_wallet_alert_title": "Twój portfel się synchronizuje", "template": "Szablon", @@ -733,6 +734,7 @@ "view_key_private": "Prywatny Klucz Wglądu", "view_key_public": "Publiczny Klucz Wglądu", "view_transaction_on": "Zobacz transakcje na ", + "waitFewSecondForTxUpdate": "Poczekaj kilka sekund, aż transakcja zostanie odzwierciedlona w historii transakcji", "wallet_keys": "Klucze portfela", "wallet_list_create_new_wallet": "Utwórz nowy portfel", "wallet_list_edit_wallet": "Edytuj portfel", @@ -783,6 +785,5 @@ "you_pay": "Płacisz", "you_will_get": "Konwertuj na", "you_will_send": "Konwertuj z", - "yy": "RR", - "waitFewSecondForTxUpdate": "Poczekaj kilka sekund, aż transakcja zostanie odzwierciedlona w historii transakcji" -} + "yy": "RR" +} \ No newline at end of file diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index a7c1ff7e53..73a2988ff8 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "INICIANDO SINCRONIZAÇÃO", "sync_status_syncronized": "SINCRONIZADO", "sync_status_syncronizing": "SINCRONIZANDO", + "sync_status_unsupported": "Nó não suportado", "syncing_wallet_alert_content": "Seu saldo e lista de transações podem não estar completos até que diga “SYNCHRONIZED” no topo. Clique/toque para saber mais.", "syncing_wallet_alert_title": "Sua carteira está sincronizando", "template": "Modelo", @@ -735,6 +736,7 @@ "view_key_private": "Chave de visualização (privada)", "view_key_public": "Chave de visualização (pública)", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "Aguarde alguns segundos para que a transação seja refletida no histórico de transações", "waiting_payment_confirmation": "Aguardando confirmação de pagamento", "wallet_keys": "Semente/chaves da carteira", "wallet_list_create_new_wallet": "Criar nova carteira", @@ -786,6 +788,5 @@ "you_pay": "Você paga", "you_will_get": "Converter para", "you_will_send": "Converter de", - "yy": "aa", - "waitFewSecondForTxUpdate": "Aguarde alguns segundos para que a transação seja refletida no histórico de transações" -} + "yy": "aa" +} \ No newline at end of file diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index d339d2670f..0ce75cc87d 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "НАЧАЛО СИНХРОНИЗАЦИИ", "sync_status_syncronized": "СИНХРОНИЗИРОВАН", "sync_status_syncronizing": "СИНХРОНИЗАЦИЯ", + "sync_status_unsupported": "Неподдерживаемый узел", "syncing_wallet_alert_content": "Ваш баланс и список транзакций могут быть неполными, пока вверху не будет написано «СИНХРОНИЗИРОВАНО». Щелкните/коснитесь, чтобы узнать больше.", "syncing_wallet_alert_title": "Ваш кошелек синхронизируется", "template": "Шаблон", @@ -734,6 +735,7 @@ "view_key_private": "Приватный ключ просмотра", "view_key_public": "Публичный ключ просмотра", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "Пожалуйста, подождите несколько секунд, чтобы транзакция отразилась в истории транзакций.", "wallet_keys": "Мнемоническая фраза/ключи кошелька", "wallet_list_create_new_wallet": "Создать новый кошелёк", "wallet_list_edit_wallet": "Изменить кошелек", @@ -784,6 +786,5 @@ "you_pay": "Вы платите", "you_will_get": "Конвертировать в", "you_will_send": "Конвертировать из", - "yy": "ГГ", - "waitFewSecondForTxUpdate": "Пожалуйста, подождите несколько секунд, чтобы транзакция отразилась в истории транзакций." -} + "yy": "ГГ" +} \ No newline at end of file diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index a4bb58d7de..f165b7e35f 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "กำลังเริ่มซิงโครไนซ์", "sync_status_syncronized": "ซิงโครไนซ์แล้ว", "sync_status_syncronizing": "กำลังซิงโครไนซ์", + "sync_status_unsupported": "โหนดที่ไม่ได้รับการสนับสนุน", "syncing_wallet_alert_content": "รายการยอดเงินและธุรกรรมของคุณอาจไม่สมบูรณ์จนกว่าจะมีข้อความว่า “ซิงโครไนซ์” ที่ด้านบน คลิก/แตะเพื่อเรียนรู้เพิ่มเติม่", "syncing_wallet_alert_title": "กระเป๋าสตางค์ของคุณกำลังซิงค์", "template": "แบบฟอร์ม", @@ -733,6 +734,7 @@ "view_key_private": "คีย์มุมมอง (ส่วนตัว)", "view_key_public": "คีย์มุมมอง (สาธารณะ)", "view_transaction_on": "ดูการทำธุรกรรมบน ", + "waitFewSecondForTxUpdate": "กรุณารอสักครู่เพื่อให้ธุรกรรมปรากฏในประวัติการทำธุรกรรม", "wallet_keys": "ซีดของกระเป๋า/คีย์", "wallet_list_create_new_wallet": "สร้างกระเป๋าใหม่", "wallet_list_edit_wallet": "แก้ไขกระเป๋าสตางค์", @@ -783,6 +785,5 @@ "you_pay": "คุณจ่าย", "you_will_get": "แปลงเป็น", "you_will_send": "แปลงจาก", - "yy": "ปี", - "waitFewSecondForTxUpdate": "กรุณารอสักครู่เพื่อให้ธุรกรรมปรากฏในประวัติการทำธุรกรรม" -} + "yy": "ปี" +} \ No newline at end of file diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 2e9520dce5..b87794f2e3 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "Simula sa pag -sync", "sync_status_syncronized": "Naka -synchronize", "sync_status_syncronizing": "Pag -synchronize", + "sync_status_unsupported": "Hindi suportadong node", "syncing_wallet_alert_content": "Ang iyong balanse at listahan ng transaksyon ay maaaring hindi kumpleto hanggang sa sabihin nito na \"naka -synchronize\" sa tuktok. Mag -click/tap upang malaman ang higit pa.", "syncing_wallet_alert_title": "Ang iyong pitaka ay nag -sync", "template": "Template", @@ -733,6 +734,7 @@ "view_key_private": "Tingnan ang Key (Pribado)", "view_key_public": "Tingnan ang Key (Publiko)", "view_transaction_on": "Tingnan ang transaksyon sa", + "waitFewSecondForTxUpdate": "Mangyaring maghintay ng ilang segundo para makita ang transaksyon sa history ng mga transaksyon", "wallet_keys": "Mga buto/susi ng pitaka", "wallet_list_create_new_wallet": "Lumikha ng bagong pitaka", "wallet_list_edit_wallet": "I -edit ang Wallet", @@ -783,6 +785,5 @@ "you_pay": "Magbabayad ka", "you_will_get": "Mag -convert sa", "you_will_send": "I -convert mula sa", - "yy": "YY", - "waitFewSecondForTxUpdate": "Mangyaring maghintay ng ilang segundo para makita ang transaksyon sa history ng mga transaksyon" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 75148b2cab..80f9ced27f 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "SENKRONİZE BAŞLATILIYOR", "sync_status_syncronized": "SENKRONİZE EDİLDİ", "sync_status_syncronizing": "SENKRONİZE EDİLİYOR", + "sync_status_unsupported": "Desteklenmeyen düğüm", "syncing_wallet_alert_content": "Bakiyeniz ve işlem listeniz, en üstte \"SENKRONİZE EDİLDİ\" yazana kadar tamamlanmamış olabilir. Daha fazla bilgi edinmek için tıklayın/dokunun.", "syncing_wallet_alert_title": "Cüzdanınız senkronize ediliyor", "template": "Şablon", @@ -733,6 +734,7 @@ "view_key_private": "İzleme anahtarı (özel)", "view_key_public": "İzleme anahtarı (genel)", "view_transaction_on": "İşlemi şurada görüntüle ", + "waitFewSecondForTxUpdate": "İşlemin işlem geçmişine yansıması için lütfen birkaç saniye bekleyin", "wallet_keys": "Cüzdan tohumu/anahtarları", "wallet_list_create_new_wallet": "Yeni Cüzdan Oluştur", "wallet_list_edit_wallet": "Cüzdanı düzenle", @@ -783,6 +785,5 @@ "you_pay": "Şu kadar ödeyeceksin: ", "you_will_get": "Biçimine dönüştür:", "you_will_send": "Biçiminden dönüştür:", - "yy": "YY", - "waitFewSecondForTxUpdate": "İşlemin işlem geçmişine yansıması için lütfen birkaç saniye bekleyin" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index a461d10a5d..0363c4f48c 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "ПОЧАТОК СИНХРОНІЗАЦІЇ", "sync_status_syncronized": "СИНХРОНІЗОВАНИЙ", "sync_status_syncronizing": "СИНХРОНІЗАЦІЯ", + "sync_status_unsupported": "Непідтримуваний вузол", "syncing_wallet_alert_content": "Ваш баланс та список транзакцій може бути неповним, доки вгорі не буде написано «СИНХРОНІЗОВАНО». Натисніть/торкніться, щоб дізнатися більше.", "syncing_wallet_alert_title": "Ваш гаманець синхронізується", "template": "Шаблон", @@ -734,6 +735,7 @@ "view_key_private": "Приватний ключ перегляду", "view_key_public": "Публічний ключ перегляду", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "Будь ласка, зачекайте кілька секунд, поки транзакція відобразиться в історії транзакцій", "wallet_keys": "Мнемонічна фраза/ключі гаманця", "wallet_list_create_new_wallet": "Створити новий гаманець", "wallet_list_edit_wallet": "Редагувати гаманець", @@ -784,6 +786,5 @@ "you_pay": "Ви платите", "you_will_get": "Конвертувати в", "you_will_send": "Конвертувати з", - "yy": "YY", - "waitFewSecondForTxUpdate": "Будь ласка, зачекайте кілька секунд, поки транзакція відобразиться в історії транзакцій" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 7ba95a3c57..ed1cdef119 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "مطابقت پذیری شروع کر رہا ہے۔", "sync_status_syncronized": "مطابقت پذیر", "sync_status_syncronizing": "مطابقت پذیری", + "sync_status_unsupported": "غیر تعاون یافتہ نوڈ", "syncing_wallet_alert_content": "آپ کے بیلنس اور لین دین کی فہرست اس وقت تک مکمل نہیں ہو سکتی جب تک کہ یہ سب سے اوپر \"SYNCRONIZED\" نہ کہے۔ مزید جاننے کے لیے کلک/تھپتھپائیں۔", "syncing_wallet_alert_title": "آپ کا بٹوہ مطابقت پذیر ہو رہا ہے۔", "template": "سانچے", @@ -735,6 +736,7 @@ "view_key_private": "کلید دیکھیں (نجی)", "view_key_public": "کلید دیکھیں (عوامی)", "view_transaction_on": "لین دین دیکھیں آن", + "waitFewSecondForTxUpdate": "۔ﮟﯾﺮﮐ ﺭﺎﻈﺘﻧﺍ ﺎﮐ ﮉﻨﮑﯿﺳ ﺪﻨﭼ ﻡﺮﮐ ﮦﺍﺮﺑ ﮯﯿﻟ ﮯﮐ ﮯﻧﺮﮐ ﯽﺳﺎﮑﻋ ﯽﮐ ﻦﯾﺩ ﻦﯿﻟ ﮟﯿﻣ ﺦﯾﺭﺎﺗ ﯽﮐ ﻦ", "wallet_keys": "بٹوے کے بیج / چابیاں", "wallet_list_create_new_wallet": "نیا والیٹ بنائیں", "wallet_list_edit_wallet": "بٹوے میں ترمیم کریں۔", @@ -785,6 +787,5 @@ "you_pay": "تم ادا کرو", "you_will_get": "میں تبدیل کریں۔", "you_will_send": "سے تبدیل کریں۔", - "yy": "YY", - "waitFewSecondForTxUpdate": "۔ﮟﯾﺮﮐ ﺭﺎﻈﺘﻧﺍ ﺎﮐ ﮉﻨﮑﯿﺳ ﺪﻨﭼ ﻡﺮﮐ ﮦﺍﺮﺑ ﮯﯿﻟ ﮯﮐ ﮯﻧﺮﮐ ﯽﺳﺎﮑﻋ ﯽﮐ ﻦﯾﺩ ﻦﯿﻟ ﮟﯿﻣ ﺦﯾﺭﺎﺗ ﯽﮐ ﻦ" -} + "yy": "YY" +} \ No newline at end of file diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 13af654809..824e87bb8e 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "Ń BẸ̀RẸ̀ RẸ́", "sync_status_syncronized": "TI MÚDỌ́GBA", "sync_status_syncronizing": "Ń MÚDỌ́GBA", + "sync_status_unsupported": "Ile-igbimọ ti ko ni atilẹyin", "syncing_wallet_alert_content": "Iwontunws.funfun rẹ ati atokọ idunadura le ma pari titi ti yoo fi sọ “SYNCHRONIZED” ni oke. Tẹ/tẹ ni kia kia lati ni imọ siwaju sii.", "syncing_wallet_alert_title": "Apamọwọ rẹ n muṣiṣẹpọ", "template": "Àwòṣe", @@ -734,6 +735,7 @@ "view_key_private": "Kọ́kọ́rọ́ ìwò (àdáni)", "view_key_public": "Kọ́kọ́rọ́ ìwò (kò àdáni)", "view_transaction_on": "Wo pàṣípààrọ̀ lórí ", + "waitFewSecondForTxUpdate": "Fi inurere duro fun awọn iṣeju diẹ fun idunadura lati ṣe afihan ninu itan-akọọlẹ iṣowo", "wallet_keys": "Hóró/kọ́kọ́rọ́ àpamọ́wọ́", "wallet_list_create_new_wallet": "Ṣe àpamọ́wọ́ títun", "wallet_list_edit_wallet": "Ṣatunkọ apamọwọ", @@ -784,6 +786,5 @@ "you_pay": "Ẹ sàn", "you_will_get": "Ṣe pàṣípààrọ̀ sí", "you_will_send": "Ṣe pàṣípààrọ̀ láti", - "yy": "Ọd", - "waitFewSecondForTxUpdate": "Fi inurere duro fun awọn iṣeju diẹ fun idunadura lati ṣe afihan ninu itan-akọọlẹ iṣowo" -} + "yy": "Ọd" +} \ No newline at end of file diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 9c82981346..7b53264b61 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -635,6 +635,7 @@ "sync_status_starting_sync": "开始同步", "sync_status_syncronized": "已同步", "sync_status_syncronizing": "正在同步", + "sync_status_unsupported": "不支持的节点", "syncing_wallet_alert_content": "您的余额和交易列表可能不完整,直到顶部显示“已同步”。单击/点击以了解更多信息。", "syncing_wallet_alert_title": "您的钱包正在同步", "template": "模板", @@ -733,6 +734,7 @@ "view_key_private": "View 密钥(私钥)", "view_key_public": "View 密钥(公钥)", "view_transaction_on": "View Transaction on ", + "waitFewSecondForTxUpdate": "请等待几秒钟,交易才会反映在交易历史记录中", "wallet_keys": "钱包种子/密钥", "wallet_list_create_new_wallet": "创建新钱包", "wallet_list_edit_wallet": "编辑钱包", @@ -783,6 +785,5 @@ "you_pay": "你付钱", "you_will_get": "转换到", "you_will_send": "转换自", - "yy": "YY", - "waitFewSecondForTxUpdate": "请等待几秒钟,交易才会反映在交易历史记录中" -} + "yy": "YY" +} \ No newline at end of file diff --git a/tool/configure.dart b/tool/configure.dart index bf89743efb..2dd772bbe0 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -125,7 +125,7 @@ abstract class Bitcoin { List getAddresses(Object wallet); String getAddress(Object wallet); - List getSilentAddresses(Object wallet); + List getSilentAddresses(Object wallet); List getSubAddresses(Object wallet); From a5bc33827687126f016db0d85186e4ac58f137e2 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 27 Feb 2024 12:50:36 -0300 Subject: [PATCH 04/75] chore: pubspec.lock --- cw_bitcoin/pubspec.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index c7750f1afb..5678027397 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -71,7 +71,7 @@ packages: description: path: "." ref: master - resolved-ref: ea65073efbaf395a5557e8cd7bd72f195cd7eb11 + resolved-ref: "932f13300ba501eca16642400a8f68eefaef2708" url: "https://github.com/cake-tech/bitbox-flutter.git" source: git version: "1.0.1" @@ -178,10 +178,10 @@ packages: dependency: transitive description: name: built_value - sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6 + sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e url: "https://pub.dev" source: hosted - version: "8.9.0" + version: "8.9.1" characters: dependency: transitive description: From 2967809a0ebba3d6bfc11f3777dce7a27884235a Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 27 Feb 2024 14:46:50 -0300 Subject: [PATCH 05/75] chore: pubspec.lock --- cw_bitcoin/pubspec.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 5678027397..ac12055b2f 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -80,7 +80,7 @@ packages: description: path: "." ref: cake-update-v2 - resolved-ref: "43b80bcf0ef6e7224603a6b8874b61efec3c6a4c" + resolved-ref: be980da9ed063da13db3907ca76d534298bb1d40 url: "https://github.com/cake-tech/bitcoin_base.git" source: git version: "4.0.0" From a44bd6b8f9c225e786c80b5ffddf57f26e8853b5 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 27 Feb 2024 19:51:53 -0300 Subject: [PATCH 06/75] fix: scan when switching, fix multiple unspents in same tx --- cw_bitcoin/lib/electrum_transaction_info.dart | 18 ++- cw_bitcoin/lib/electrum_wallet.dart | 149 ++++++++++-------- cw_bitcoin/lib/electrum_wallet_addresses.dart | 11 +- cw_bitcoin/pubspec.lock | 2 +- 4 files changed, 103 insertions(+), 77 deletions(-) diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index 5a7f797f96..50896c837f 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -19,7 +19,7 @@ class ElectrumTransactionBundle { } class ElectrumTransactionInfo extends TransactionInfo { - BitcoinUnspent? unspent; + List? unspents; ElectrumTransactionInfo(this.type, {required String id, @@ -31,7 +31,7 @@ class ElectrumTransactionInfo extends TransactionInfo { required DateTime date, required int confirmations, String? to, - this.unspent}) { + this.unspents}) { this.id = id; this.height = height; this.amount = amount; @@ -160,10 +160,12 @@ class ElectrumTransactionInfo extends TransactionInfo { isPending: data['isPending'] as bool, confirmations: data['confirmations'] as int, to: data['to'] as String?, - unspent: data['unspent'] != null - ? BitcoinUnspent.fromJSON( - BitcoinAddressRecord.fromJSON(data['unspent']['address_record'] as String), - data['unspent'] as Map) + unspents: data['unspent'] != null + ? (data['unspent'] as List) + .map((unspent) => BitcoinUnspent.fromJSON( + BitcoinAddressRecord.fromJSON(unspent['address_record'] as String), + data['unspent'] as Map)) + .toList() : null, ); } @@ -210,11 +212,11 @@ class ElectrumTransactionInfo extends TransactionInfo { m['confirmations'] = confirmations; m['fee'] = fee; m['to'] = to; - m['unspent'] = unspent?.toJson() ?? {}; + m['unspent'] = unspents?.map((e) => e.toJson()) ?? []; return m; } String toString() { - return 'ElectrumTransactionInfo(id: $id, height: $height, amount: $amount, fee: $fee, direction: $direction, date: $date, isPending: $isPending, confirmations: $confirmations, to: $to, unspent: $unspent)'; + return 'ElectrumTransactionInfo(id: $id, height: $height, amount: $amount, fee: $fee, direction: $direction, date: $date, isPending: $isPending, confirmations: $confirmations, to: $to, unspent: $unspents)'; } } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index bd63c40972..455213a2e5 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -142,6 +142,9 @@ abstract class ElectrumWalletBase @observable bool nodeSupportsSilentPayments = true; + @observable + int? currentChainTip; + @override BitcoinWalletKeys get keys => BitcoinWalletKeys(wif: hd.wif!, privateKey: hd.privKey!, publicKey: hd.pubKey!); @@ -197,30 +200,37 @@ abstract class ElectrumWalletBase syncStatus = UnsupportedSyncStatus(); } - if (message is BitcoinUnspent) { - if (!unspentCoins.any((utx) => - utx.hash.contains(message.hash) && - utx.vout == message.vout && - utx.address.contains(message.address))) { - unspentCoins.add(message); - - if (unspentCoinsInfo.values.any((element) => - element.walletId.contains(id) && - element.hash.contains(message.hash) && - element.address.contains(message.address))) { - _addCoinInfo(message); - - await walletInfo.save(); - await save(); + if (message is Map) { + for (final map in message.entries) { + final txid = map.key; + final tx = map.value; + + if (tx.unspents != null) { + tx.unspents!.forEach((unspent) => walletAddresses.addSilentAddresses( + [unspent.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord])); + + final existingTxInfo = transactionHistory.transactions[txid]; + if (existingTxInfo != null) { + final newUnspents = tx.unspents! + .where((unspent) => !existingTxInfo.unspents!.any((element) => + element.hash.contains(unspent.hash) && element.vout == unspent.vout)) + .toList(); + + if (newUnspents.isNotEmpty) { + existingTxInfo.unspents ??= []; + existingTxInfo.unspents!.addAll(newUnspents); + existingTxInfo.amount += newUnspents.length > 1 + ? newUnspents.map((e) => e.value).reduce((value, unspent) => value + unspent) + : newUnspents[0].value; + } + } else { + transactionHistory.addMany(message); + transactionHistory.save(); + } } - - balance[currency] = await _fetchBalances(); } - } - if (message is Map) { - transactionHistory.addMany(message); - await transactionHistory.save(); + updateUnspent(); } // check if is a SyncStatus type since "is SyncStatus" doesn't work here @@ -236,17 +246,16 @@ abstract class ElectrumWalletBase @override Future startSync() async { try { + syncStatus = AttemptingSyncStatus(); + if (hasSilentPaymentsScanning) { try { await _setInitialHeight(); } catch (_) {} - final currentChainTip = await electrumClient.getCurrentBlockChainTip(); if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); } - } else { - syncStatus = AttemptingSyncStatus(); } await updateTransactions(); @@ -258,9 +267,9 @@ abstract class ElectrumWalletBase Timer.periodic( const Duration(minutes: 1), (timer) async => _feeRates = await electrumClient.feeRates()); - // if (!hasSilentPaymentsScanning) { - syncStatus = SyncedSyncStatus(); - // } + if (!hasSilentPaymentsScanning || walletInfo.restoreHeight == currentChainTip) { + syncStatus = SyncedSyncStatus(); + } } catch (e, stacktrace) { print(stacktrace); print(e.toString()); @@ -312,10 +321,22 @@ abstract class ElectrumWalletBase leftAmount = leftAmount - utx.value; final address = _addressTypeFromStr(utx.address, network); - final privkey = generateECPrivate( - hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd, - index: utx.bitcoinAddressRecord.index, - network: network); + + ECPrivate? privkey; + if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { + privkey = walletAddresses.primarySilentAddress!.b_spend.clone().tweakAdd( + BigintUtils.fromBytes(BytesUtils.fromHexString( + (utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord) + .silentPaymentTweak)), + ); + } else { + privkey = generateECPrivate( + hd: utx.bitcoinAddressRecord.isHidden + ? walletAddresses.sideHd + : walletAddresses.mainHd, + index: utx.bitcoinAddressRecord.index, + network: network); + } privateKeys.add(privkey); inputPrivKeyInfos.add(ECPrivateInfo(privkey, address.type == SegwitAddresType.p2tr)); @@ -661,18 +682,19 @@ abstract class ElectrumWalletBase Future makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); Future updateUnspent() async { + List updatedUnspentCoins = []; + // Update unspents stored from scanned silent payment transactions transactionHistory.transactions.values.forEach((tx) { - if (tx.unspent != null) { - if (!unspentCoins - .any((utx) => utx.hash.contains(tx.unspent!.hash) && utx.vout == tx.unspent!.vout)) { - unspentCoins.add(tx.unspent!); + if (tx.unspents != null) { + if (!unspentCoins.any((utx) => + tx.unspents!.any((element) => utx.hash.contains(element.hash)) && + tx.unspents!.any((element) => utx.vout == element.vout))) { + updatedUnspentCoins.addAll(tx.unspents!); } } }); - List updatedUnspentCoins = []; - final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet(); await Future.wait(walletAddresses.allAddresses.map((address) => electrumClient @@ -866,7 +888,7 @@ abstract class ElectrumWalletBase final Map historiesWithDetails = {}; final history = await electrumClient - .getHistory(addressRecord.scriptHash ?? addressRecord.updateScriptHash(network)!); + .getHistory(addressRecord.scriptHash ?? addressRecord.updateScriptHash(network)); if (history.isNotEmpty) { addressRecord.setAsUsed(); @@ -1048,8 +1070,8 @@ abstract class ElectrumWalletBase } if (walletInfo.restoreHeight == 0) { - final currentHeight = await electrumClient.getCurrentBlockChainTip(); - if (currentHeight != null) walletInfo.restoreHeight = currentHeight; + currentChainTip = await electrumClient.getCurrentBlockChainTip(); + if (currentChainTip != null) walletInfo.restoreHeight = currentChainTip!; } } } @@ -1102,13 +1124,18 @@ class SyncResponse { Future startRefresh(ScanData scanData) async { var cachedBlockchainHeight = scanData.chainTip; + Future connect() async { + final electrumClient = scanData.electrumClient; + if (!electrumClient.isConnected) { + final node = scanData.node; + await electrumClient.connectToUri(Uri.parse(node)); + } + return electrumClient; + } + Future getNodeHeightOrUpdate(int baseHeight) async { if (cachedBlockchainHeight < baseHeight || cachedBlockchainHeight == 0) { - final electrumClient = scanData.electrumClient; - if (!electrumClient.isConnected) { - final node = scanData.node; - await electrumClient.connectToUri(Uri.parse(node)); - } + final electrumClient = await connect(); cachedBlockchainHeight = await electrumClient.getCurrentBlockChainTip() ?? cachedBlockchainHeight; @@ -1151,16 +1178,8 @@ Future startRefresh(ScanData scanData) async { return; } - print(["Scanning from height:", syncHeight]); - try { - // Get all the tweaks from the block - final electrumClient = scanData.electrumClient; - if (!electrumClient.isConnected) { - final node = scanData.node; - print(node); - await electrumClient.connectToUri(Uri.parse(node)); - } + final electrumClient = await connect(); List? tweaks; try { @@ -1177,7 +1196,6 @@ Future startRefresh(ScanData scanData) async { for (var i = 0; i < tweaks.length; i++) { try { - // final txid = tweaks.keys.toList()[i]; final details = tweaks[i] as Map; final output_pubkeys = (details["output_pubkeys"] as List); final tweak = details["tweak"].toString(); @@ -1193,9 +1211,7 @@ Future startRefresh(ScanData scanData) async { final result = spb.scanOutputs( scanData.primarySilentAddress.b_scan, scanData.primarySilentAddress.B_spend, - output_pubkeys - .map((p) => ECPublic.fromBytes(BytesUtils.fromHexString(p.toString()).sublist(2))) - .toList(), + output_pubkeys.map((output) => output.toString()).toList(), precomputedLabels: scanData.labels, ); @@ -1206,8 +1222,7 @@ Future startRefresh(ScanData scanData) async { result.forEach((key, value) async { final t_k = value[0]; - final address = - ECPublic.fromHex(key).toTaprootAddress(tweak: false).toAddress(scanData.network); + final address = ECPublic.fromHex(key).toTaprootAddress().toAddress(scanData.network); final listUnspent = await electrumClient.getListUnspentWithAddress(address, scanData.network); @@ -1228,6 +1243,10 @@ Future startRefresh(ScanData scanData) async { } catch (_) {} }); + if (info == null) { + return; + } + // final tweak = value[0]; // String? label; // if (value.length > 1) label = value[1]; @@ -1237,20 +1256,16 @@ Future startRefresh(ScanData scanData) async { WalletType.bitcoin, id: tx.hash, height: syncHeight, - amount: tx.value, + amount: 0, // will be added later via unspent fee: 0, direction: TransactionDirection.incoming, isPending: false, date: DateTime.now(), - confirmations: currentChainTip - syncHeight, + confirmations: currentChainTip - syncHeight - 1, to: scanData.primarySilentAddress.toString(), - unspent: tx, + unspents: [tx], ); - // final status = json.decode((await http - // .get(Uri.parse("https://blockstream.info/testnet/api/tx/$txid/outspends"))) - // .body) as List; - // bool spent = false; // for (final s in status) { // if ((s["spent"] as bool) == true) { diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index ec2a27498c..2bfeeeea05 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -35,7 +35,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { }) : _addresses = ObservableList.of((initialAddresses ?? []).toSet()), primarySilentAddress = silentAddress, addressesByReceiveType = - ObservableList.of(([]).toSet()), + ObservableList.of(([]).toSet()), receiveAddresses = ObservableList.of((initialAddresses ?? []) .where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed) .toSet()), @@ -408,6 +408,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { updateAddressesByMatch(); } + @action + void addSilentAddresses(Iterable addresses) { + final addressesSet = this.silentAddresses.toSet(); + addressesSet.addAll(addresses); + this.silentAddresses.clear(); + this.silentAddresses.addAll(addressesSet); + updateAddressesByMatch(); + } + void _validateSideHdAddresses(List addrWithTransactions) { addrWithTransactions.forEach((element) { if (element.address != diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index ac12055b2f..3584b0fe3e 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -80,7 +80,7 @@ packages: description: path: "." ref: cake-update-v2 - resolved-ref: be980da9ed063da13db3907ca76d534298bb1d40 + resolved-ref: "7634511b15e5a48bd18e3c19f81971628090c04f" url: "https://github.com/cake-tech/bitcoin_base.git" source: git version: "4.0.0" From 477aff098c63e6562d0d14e49ae1ae689d085829 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 27 Feb 2024 21:33:29 -0300 Subject: [PATCH 07/75] fix: initial scan --- cw_bitcoin/lib/electrum_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 455213a2e5..1ab76bdcd8 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -253,7 +253,7 @@ abstract class ElectrumWalletBase await _setInitialHeight(); } catch (_) {} - if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { + if ((currentChainTip ?? 0) <= walletInfo.restoreHeight) { _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); } } From f58fca30201a70aa11b20085ffca54c864c72484 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 27 Feb 2024 21:36:56 -0300 Subject: [PATCH 08/75] fix: initial scan --- cw_bitcoin/lib/electrum_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 1ab76bdcd8..cd8093f72c 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -253,7 +253,7 @@ abstract class ElectrumWalletBase await _setInitialHeight(); } catch (_) {} - if ((currentChainTip ?? 0) <= walletInfo.restoreHeight) { + if ((currentChainTip ?? 0) < walletInfo.restoreHeight) { _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); } } From 0016d436f313064260a6c6691b57744b975247c4 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Wed, 28 Feb 2024 12:48:42 -0300 Subject: [PATCH 09/75] fix: scanning issues --- cw_bitcoin/lib/electrum_wallet.dart | 34 +++++++++++++---------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index cd8093f72c..9f7e675646 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -37,7 +37,6 @@ import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; -import 'package:hex/hex.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:rxdart/subjects.dart'; @@ -253,7 +252,7 @@ abstract class ElectrumWalletBase await _setInitialHeight(); } catch (_) {} - if ((currentChainTip ?? 0) < walletInfo.restoreHeight) { + if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); } } @@ -278,14 +277,17 @@ abstract class ElectrumWalletBase } @action - @override - Future connectToNode({required Node node}) async { + Future _electrumConnect(Node node, {bool? attemptedReconnect}) async { try { syncStatus = ConnectingSyncStatus(); await electrumClient.connectToUri(node.uri); - electrumClient.onConnectionStatusChange = (bool isConnected) { + electrumClient.onConnectionStatusChange = (bool isConnected) async { if (!isConnected) { syncStatus = LostConnectionSyncStatus(); + await electrumClient.close(); + if (attemptedReconnect == false) { + await _electrumConnect(node, attemptedReconnect: true); + } } }; syncStatus = ConnectedSyncStatus(); @@ -295,6 +297,10 @@ abstract class ElectrumWalletBase } } + @action + @override + Future connectToNode({required Node node}) => _electrumConnect(node); + Future _estimateTxFeeAndInputsToUse( int credentialsAmount, bool sendAll, @@ -807,14 +813,7 @@ abstract class ElectrumWalletBase final ins = []; for (final vin in original.inputs) { - try { - final id = HEX.encode(HEX.decode(vin.txId).reversed.toList()); - final txHex = await electrumClient.getTransactionHex(hash: id); - final tx = BtcTransaction.fromRaw(txHex); - ins.add(tx); - } catch (_) { - ins.add(BtcTransaction.fromRaw(await electrumClient.getTransactionHex(hash: vin.txId))); - } + ins.add(BtcTransaction.fromRaw(await electrumClient.getTransactionHex(hash: vin.txId))); } return ElectrumTransactionBundle(original, @@ -1065,10 +1064,6 @@ abstract class ElectrumWalletBase } Future _setInitialHeight() async { - if (walletInfo.isRecovery) { - return; - } - if (walletInfo.restoreHeight == 0) { currentChainTip = await electrumClient.getCurrentBlockChainTip(); if (currentChainTip != null) walletInfo.restoreHeight = currentChainTip!; @@ -1191,10 +1186,11 @@ Future startRefresh(ScanData scanData) async { } if (tweaks == null) { - return scanData.sendPort.send(false); + scanData.sendPort.send(SyncResponse(syncHeight, + SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); } - for (var i = 0; i < tweaks.length; i++) { + for (var i = 0; i < tweaks!.length; i++) { try { final details = tweaks[i] as Map; final output_pubkeys = (details["output_pubkeys"] as List); From 18697fbd0ff6d801f49acfe4bde6edca0bc70173 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 1 Mar 2024 20:38:53 -0300 Subject: [PATCH 10/75] fix: sync, storing silent unspents --- cw_bitcoin/lib/bitcoin_address_record.dart | 13 +-- cw_bitcoin/lib/bitcoin_unspent.dart | 8 +- cw_bitcoin/lib/bitcoin_wallet.dart | 26 +----- cw_bitcoin/lib/bitcoin_wallet_addresses.dart | 2 +- cw_bitcoin/lib/electrum.dart | 4 +- .../lib/electrum_transaction_history.dart | 26 +++--- cw_bitcoin/lib/electrum_transaction_info.dart | 10 +- cw_bitcoin/lib/electrum_wallet.dart | 91 ++++++++++--------- cw_bitcoin/lib/electrum_wallet_addresses.dart | 45 ++++++--- cw_bitcoin/pubspec.lock | 16 ++-- cw_bitcoin/pubspec.yaml | 8 +- cw_bitcoin_cash/pubspec.yaml | 8 +- scripts/android/app_env.fish | 75 +++++++++++++++ 13 files changed, 192 insertions(+), 140 deletions(-) create mode 100644 scripts/android/app_env.fish diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index afd7c34e16..f5731f0f12 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -124,10 +124,9 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { super.balance = 0, super.name = '', super.isUsed = false, - required super.type, required this.silentPaymentTweak, required super.network, - }); + }) : super(type: SilentPaymentsAddresType.p2sp); factory BitcoinSilentPaymentAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) { @@ -141,18 +140,14 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { txCount: decoded['txCount'] as int? ?? 0, name: decoded['name'] as String? ?? '', balance: decoded['balance'] as int? ?? 0, - type: decoded['type'] != null && decoded['type'] != '' - ? BitcoinAddressType.values - .firstWhere((type) => type.toString() == decoded['type'] as String) - : SegwitAddresType.p2wpkh, network: (decoded['network'] as String?) == null ? network : BasedUtxoNetwork.fromName(decoded['network'] as String), - silentPaymentTweak: decoded['silentPaymentTweak'] as String, + silentPaymentTweak: decoded['silent_payment_tweak'] as String?, ); } - final String silentPaymentTweak; + final String? silentPaymentTweak; @override String toJSON() => json.encode({ @@ -165,6 +160,6 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { 'balance': balance, 'type': type.toString(), 'network': network?.value, - 'silentPaymentTweak': silentPaymentTweak, + 'silent_payment_tweak': silentPaymentTweak, }); } diff --git a/cw_bitcoin/lib/bitcoin_unspent.dart b/cw_bitcoin/lib/bitcoin_unspent.dart index ce3c0da166..b2c1d90c4c 100644 --- a/cw_bitcoin/lib/bitcoin_unspent.dart +++ b/cw_bitcoin/lib/bitcoin_unspent.dart @@ -1,10 +1,9 @@ -import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_core/unspent_transaction_output.dart'; class BitcoinUnspent extends Unspent { BitcoinUnspent(BaseBitcoinAddressRecord addressRecord, String hash, int value, int vout, - {this.silentPaymentTweak, this.type}) + {this.silentPaymentTweak}) : bitcoinAddressRecord = addressRecord, super(addressRecord.address, hash, value, vout, null); @@ -15,9 +14,6 @@ class BitcoinUnspent extends Unspent { json['value'] as int, json['tx_pos'] as int, silentPaymentTweak: json['silent_payment_tweak'] as String?, - type: json['type'] == null - ? null - : BitcoinAddressType.values.firstWhere((e) => e.toString() == json['type']), ); Map toJson() { @@ -27,12 +23,10 @@ class BitcoinUnspent extends Unspent { 'value': value, 'tx_pos': vout, 'silent_payment_tweak': silentPaymentTweak, - 'type': type.toString(), }; return json; } final BaseBitcoinAddressRecord bitcoinAddressRecord; String? silentPaymentTweak; - BitcoinAddressType? type; } diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index f24142493e..fd45584caf 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -32,7 +32,6 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { Map? initialChangeAddressIndex, List? initialSilentAddresses, int initialSilentAddressIndex = 0, - SilentPaymentOwner? silentAddress, }) : super( mnemonic: mnemonic, password: password, @@ -54,10 +53,13 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialChangeAddressIndex: initialChangeAddressIndex, initialSilentAddresses: initialSilentAddresses, initialSilentAddressIndex: initialSilentAddressIndex, - silentAddress: silentAddress, mainHd: hd, sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"), network: networkParam ?? network, + masterHd: bitcoin.HDWallet.fromSeed( + seedBytes, + network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, + ), ); hasSilentPaymentsScanning = addressPageType == SilentPaymentsAddresType.p2sp.toString(); @@ -97,16 +99,6 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialSilentAddresses: initialSilentAddresses, initialSilentAddressIndex: initialSilentAddressIndex, - silentAddress: await SilentPaymentOwner.fromPrivateKeys( - b_scan: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( - seedBytes, - network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, - ).derivePath(SCAN_PATH).privKey!), - b_spend: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( - seedBytes, - network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, - ).derivePath(SPEND_PATH).privKey!), - hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp'), initialBalance: initialBalance, seedBytes: seedBytes, initialRegularAddressIndex: initialRegularAddressIndex, @@ -134,16 +126,6 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialAddresses: snp.addresses, initialSilentAddresses: snp.silentAddresses, initialSilentAddressIndex: snp.silentAddressIndex, - silentAddress: await SilentPaymentOwner.fromPrivateKeys( - b_scan: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( - seedBytes, - network: snp.network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, - ).derivePath(SCAN_PATH).privKey!), - b_spend: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed( - seedBytes, - network: snp.network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin, - ).derivePath(SPEND_PATH).privKey!), - hrp: snp.network == BitcoinNetwork.testnet ? 'tsp' : 'sp'), initialBalance: snp.balance, seedBytes: seedBytes, initialRegularAddressIndex: snp.regularAddressIndex, diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart index 48960ce3d1..486e69b111 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart @@ -20,7 +20,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S super.initialChangeAddressIndex, super.initialSilentAddresses, super.initialSilentAddressIndex = 0, - super.silentAddress, + super.masterHd, }) : super(walletInfo); @override diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 236dddaa26..d9e068d654 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -36,8 +36,8 @@ class ElectrumClient { _tasks = {}, unterminatedString = ''; - static const connectionTimeout = Duration(seconds: 5); - static const aliveTimerDuration = Duration(seconds: 4); + static const connectionTimeout = Duration(seconds: 300); + static const aliveTimerDuration = Duration(seconds: 300); bool get isConnected => _isConnected; Socket? socket; diff --git a/cw_bitcoin/lib/electrum_transaction_history.dart b/cw_bitcoin/lib/electrum_transaction_history.dart index d478c3b12d..a7de414e4d 100644 --- a/cw_bitcoin/lib/electrum_transaction_history.dart +++ b/cw_bitcoin/lib/electrum_transaction_history.dart @@ -11,13 +11,11 @@ part 'electrum_transaction_history.g.dart'; const transactionsHistoryFileName = 'transactions.json'; -class ElectrumTransactionHistory = ElectrumTransactionHistoryBase - with _$ElectrumTransactionHistory; +class ElectrumTransactionHistory = ElectrumTransactionHistoryBase with _$ElectrumTransactionHistory; abstract class ElectrumTransactionHistoryBase extends TransactionHistoryBase with Store { - ElectrumTransactionHistoryBase( - {required this.walletInfo, required String password}) + ElectrumTransactionHistoryBase({required this.walletInfo, required String password}) : _password = password, _height = 0 { transactions = ObservableMap(); @@ -30,8 +28,7 @@ abstract class ElectrumTransactionHistoryBase Future init() async => await _load(); @override - void addOne(ElectrumTransactionInfo transaction) => - transactions[transaction.id] = transaction; + void addOne(ElectrumTransactionInfo transaction) => transactions[transaction.id] = transaction; @override void addMany(Map transactions) => @@ -40,11 +37,13 @@ abstract class ElectrumTransactionHistoryBase @override Future save() async { try { - final dirPath = - await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); + final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final path = '$dirPath/$transactionsHistoryFileName'; - final data = - json.encode({'height': _height, 'transactions': transactions}); + final txjson = {}; + for (final tx in transactions.entries) { + txjson[tx.key] = tx.value.toJson(); + } + final data = json.encode({'height': _height, 'transactions': txjson}); await writeData(path: path, password: _password, data: data); } catch (e) { print('Error while save bitcoin transaction history: ${e.toString()}'); @@ -57,8 +56,7 @@ abstract class ElectrumTransactionHistoryBase } Future> _read() async { - final dirPath = - await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); + final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final path = '$dirPath/$transactionsHistoryFileName'; final content = await read(path: path, password: _password); return json.decode(content) as Map; @@ -84,7 +82,5 @@ abstract class ElectrumTransactionHistoryBase } } - void _update(ElectrumTransactionInfo transaction) => - transactions[transaction.id] = transaction; - + void _update(ElectrumTransactionInfo transaction) => transactions[transaction.id] = transaction; } diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index 50896c837f..5564ce6724 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -160,11 +160,11 @@ class ElectrumTransactionInfo extends TransactionInfo { isPending: data['isPending'] as bool, confirmations: data['confirmations'] as int, to: data['to'] as String?, - unspents: data['unspent'] != null - ? (data['unspent'] as List) + unspents: data['unspents'] != null + ? (data['unspents'] as List) .map((unspent) => BitcoinUnspent.fromJSON( - BitcoinAddressRecord.fromJSON(unspent['address_record'] as String), - data['unspent'] as Map)) + BitcoinAddressRecord.fromJSON(unspent['address_record'].toString()), + unspent as Map)) .toList() : null, ); @@ -212,7 +212,7 @@ class ElectrumTransactionInfo extends TransactionInfo { m['confirmations'] = confirmations; m['fee'] = fee; m['to'] = to; - m['unspent'] = unspents?.map((e) => e.toJson()) ?? []; + m['unspents'] = unspents?.map((e) => e.toJson()).toList() ?? []; return m; } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 9f7e675646..8d68f6c8f0 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -183,7 +183,7 @@ abstract class ElectrumWalletBase startRefresh, ScanData( sendPort: receivePort.sendPort, - primarySilentAddress: walletAddresses.primarySilentAddress!, + silentAddress: walletAddresses.silentAddress!, network: network, height: height, chainTip: currentChainTip, @@ -211,25 +211,36 @@ abstract class ElectrumWalletBase final existingTxInfo = transactionHistory.transactions[txid]; if (existingTxInfo != null) { final newUnspents = tx.unspents! - .where((unspent) => !existingTxInfo.unspents!.any((element) => - element.hash.contains(unspent.hash) && element.vout == unspent.vout)) + .where((unspent) => !(existingTxInfo.unspents?.any((element) => + element.hash.contains(unspent.hash) && element.vout == unspent.vout) ?? + false)) .toList(); if (newUnspents.isNotEmpty) { existingTxInfo.unspents ??= []; existingTxInfo.unspents!.addAll(newUnspents); - existingTxInfo.amount += newUnspents.length > 1 + + final amount = newUnspents.length > 1 ? newUnspents.map((e) => e.value).reduce((value, unspent) => value + unspent) : newUnspents[0].value; + + if (existingTxInfo.direction == TransactionDirection.incoming) { + existingTxInfo.amount += amount; + } else { + existingTxInfo.amount = amount; + existingTxInfo.direction = TransactionDirection.incoming; + } + transactionHistory.addOne(existingTxInfo); } } else { transactionHistory.addMany(message); - transactionHistory.save(); } + + await transactionHistory.save(); + await updateUnspent(); + await save(); } } - - updateUnspent(); } // check if is a SyncStatus type since "is SyncStatus" doesn't work here @@ -284,7 +295,6 @@ abstract class ElectrumWalletBase electrumClient.onConnectionStatusChange = (bool isConnected) async { if (!isConnected) { syncStatus = LostConnectionSyncStatus(); - await electrumClient.close(); if (attemptedReconnect == false) { await _electrumConnect(node, attemptedReconnect: true); } @@ -330,10 +340,10 @@ abstract class ElectrumWalletBase ECPrivate? privkey; if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { - privkey = walletAddresses.primarySilentAddress!.b_spend.clone().tweakAdd( + privkey = walletAddresses.silentAddress!.b_spend.clone().tweakAdd( BigintUtils.fromBytes(BytesUtils.fromHexString( (utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord) - .silentPaymentTweak)), + .silentPaymentTweak!)), ); } else { privkey = generateECPrivate( @@ -693,11 +703,7 @@ abstract class ElectrumWalletBase // Update unspents stored from scanned silent payment transactions transactionHistory.transactions.values.forEach((tx) { if (tx.unspents != null) { - if (!unspentCoins.any((utx) => - tx.unspents!.any((element) => utx.hash.contains(element.hash)) && - tx.unspents!.any((element) => utx.vout == element.vout))) { - updatedUnspentCoins.addAll(tx.unspents!); - } + updatedUnspentCoins.addAll(tx.unspents!); } }); @@ -799,8 +805,7 @@ abstract class ElectrumWalletBase (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$hash/status"))).body); time = status["block_time"] as int?; - final tip = await electrumClient.getCurrentBlockChainTip() ?? 0; - confirmations = tip - (status["block_height"] as int? ?? 0); + confirmations = currentChainTip! - (status["block_height"] as int? ?? 0); } else { final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash); @@ -843,13 +848,13 @@ abstract class ElectrumWalletBase try { final Map historiesWithDetails = {}; final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet(); - final currentHeight = await electrumClient.getCurrentBlockChainTip() ?? 0; + currentChainTip ??= await electrumClient.getCurrentBlockChainTip() ?? 0; await Future.wait(ADDRESS_TYPES.map((type) { final addressesByType = walletAddresses.allAddresses.where((addr) => addr.type == type); return Future.wait(addressesByType.map((addressRecord) async { - final history = await _fetchAddressHistory(addressRecord, addressesSet, currentHeight); + final history = await _fetchAddressHistory(addressRecord, addressesSet, currentChainTip!); if (history.isNotEmpty) { addressRecord.txCount = history.length; @@ -866,7 +871,7 @@ abstract class ElectrumWalletBase matchedAddresses.toList(), addressRecord.isHidden, (address, addressesSet) => - _fetchAddressHistory(address, addressesSet, currentHeight) + _fetchAddressHistory(address, addressesSet, currentChainTip!) .then((history) => history.isNotEmpty ? address.address : null), type: type); } @@ -1064,16 +1069,14 @@ abstract class ElectrumWalletBase } Future _setInitialHeight() async { - if (walletInfo.restoreHeight == 0) { - currentChainTip = await electrumClient.getCurrentBlockChainTip(); - if (currentChainTip != null) walletInfo.restoreHeight = currentChainTip!; - } + currentChainTip = await electrumClient.getCurrentBlockChainTip(); + if (currentChainTip != null) walletInfo.restoreHeight = currentChainTip!; } } class ScanData { final SendPort sendPort; - final SilentPaymentOwner primarySilentAddress; + final SilentPaymentOwner silentAddress; final int height; final String node; final BasedUtxoNetwork network; @@ -1084,7 +1087,7 @@ class ScanData { ScanData({ required this.sendPort, - required this.primarySilentAddress, + required this.silentAddress, required this.height, required this.node, required this.network, @@ -1097,7 +1100,7 @@ class ScanData { factory ScanData.fromHeight(ScanData scanData, int newHeight) { return ScanData( sendPort: scanData.sendPort, - primarySilentAddress: scanData.primarySilentAddress, + silentAddress: scanData.silentAddress, height: newHeight, node: scanData.node, network: scanData.network, @@ -1119,7 +1122,7 @@ class SyncResponse { Future startRefresh(ScanData scanData) async { var cachedBlockchainHeight = scanData.chainTip; - Future connect() async { + Future getElectrumConnection() async { final electrumClient = scanData.electrumClient; if (!electrumClient.isConnected) { final node = scanData.node; @@ -1130,7 +1133,7 @@ Future startRefresh(ScanData scanData) async { Future getNodeHeightOrUpdate(int baseHeight) async { if (cachedBlockchainHeight < baseHeight || cachedBlockchainHeight == 0) { - final electrumClient = await connect(); + final electrumClient = await getElectrumConnection(); cachedBlockchainHeight = await electrumClient.getCurrentBlockChainTip() ?? cachedBlockchainHeight; @@ -1174,7 +1177,7 @@ Future startRefresh(ScanData scanData) async { } try { - final electrumClient = await connect(); + final electrumClient = await getElectrumConnection(); List? tweaks; try { @@ -1205,9 +1208,13 @@ Future startRefresh(ScanData scanData) async { final spb = SilentPaymentBuilder(receiverTweak: tweak); final result = spb.scanOutputs( - scanData.primarySilentAddress.b_scan, - scanData.primarySilentAddress.B_spend, - output_pubkeys.map((output) => output.toString()).toList(), + scanData.silentAddress.b_scan, + scanData.silentAddress.B_spend, + output_pubkeys + .map((output) => + BytesUtils.toHexString(BytesUtils.fromHexString(output.toString()).sublist(2)) + .toString()) + .toList(), precomputedLabels: scanData.labels, ); @@ -1226,15 +1233,12 @@ Future startRefresh(ScanData scanData) async { BitcoinUnspent? info; await Future.forEach>(listUnspent, (unspent) async { try { - final addressRecord = BitcoinSilentPaymentAddressRecord( - address, - index: 0, - isHidden: true, - isUsed: true, - network: scanData.network, - silentPaymentTweak: t_k, - type: SegwitAddresType.p2tr, - ); + final addressRecord = BitcoinSilentPaymentAddressRecord(address, + index: 0, + isHidden: true, + isUsed: true, + network: scanData.network, + silentPaymentTweak: t_k); info = BitcoinUnspent.fromJSON(addressRecord, unspent); } catch (_) {} }); @@ -1258,7 +1262,7 @@ Future startRefresh(ScanData scanData) async { isPending: false, date: DateTime.now(), confirmations: currentChainTip - syncHeight - 1, - to: scanData.primarySilentAddress.toString(), + to: scanData.silentAddress.toString(), unspents: [tx], ); @@ -1310,7 +1314,6 @@ Future startRefresh(ScanData scanData) async { }); } catch (_) {} } - // break; // Finished scanning block, add 1 to height and continue to next block in loop syncHeight += 1; diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 2bfeeeea05..5a839e3a58 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -30,10 +30,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, List? initialSilentAddresses, - int initialSilentAddressIndex = 0, - SilentPaymentOwner? silentAddress, + int initialSilentAddressIndex = 1, + bitcoin.HDWallet? masterHd, }) : _addresses = ObservableList.of((initialAddresses ?? []).toSet()), - primarySilentAddress = silentAddress, addressesByReceiveType = ObservableList.of(([]).toSet()), receiveAddresses = ObservableList.of((initialAddresses ?? []) @@ -51,6 +50,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { (initialSilentAddresses ?? []).toSet()), currentSilentAddressIndex = initialSilentAddressIndex, super(walletInfo) { + if (masterHd != null) { + silentAddress = SilentPaymentOwner.fromPrivateKeys( + b_scan: ECPrivate.fromHex(masterHd.derivePath(SCAN_PATH).privKey!), + b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privKey!), + hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp'); + + if (silentAddresses.length == 0) + silentAddresses.add(BitcoinSilentPaymentAddressRecord(silentAddress.toString(), + index: 1, isHidden: false, name: "", silentPaymentTweak: null, network: network)); + } + updateAddressesByMatch(); } @@ -70,7 +80,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final bitcoin.HDWallet mainHd; final bitcoin.HDWallet sideHd; - final SilentPaymentOwner? primarySilentAddress; + @observable + SilentPaymentOwner? silentAddress; @observable BitcoinAddressType _addressPageType = SegwitAddresType.p2wpkh; @@ -92,7 +103,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return activeSilentAddress!; } - return primarySilentAddress!.toString(); + return silentAddress.toString(); } String receiveAddress; @@ -123,7 +134,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @override set address(String addr) { if (addressPageType == SilentPaymentsAddresType.p2sp) { - activeSilentAddress = addr; + final selected = silentAddresses.firstWhere((addressRecord) => addressRecord.address == addr); + + if (selected.silentPaymentTweak != null && silentAddress != null) { + activeSilentAddress = + silentAddress!.toLabeledSilentPaymentAddress(selected.index).toString(); + } else { + activeSilentAddress = silentAddress!.toString(); + } return; } @@ -225,27 +243,28 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { for (int i = 0; i < silentAddresses.length; i++) { final silentAddressRecord = silentAddresses[i]; final silentPaymentTweak = silentAddressRecord.silentPaymentTweak; - labels[G - .tweakMul(BigintUtils.fromBytes(BytesUtils.fromHexString(silentPaymentTweak))) - .toHex()] = silentPaymentTweak; + + if (silentPaymentTweak != null) + labels[G + .tweakMul(BigintUtils.fromBytes(BytesUtils.fromHexString(silentPaymentTweak))) + .toHex()] = silentPaymentTweak; } return labels; } @action BaseBitcoinAddressRecord generateNewAddress({String label = ''}) { - if (addressPageType == SilentPaymentsAddresType.p2sp) { + if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) { currentSilentAddressIndex += 1; final address = BitcoinSilentPaymentAddressRecord( - primarySilentAddress!.toLabeledSilentPaymentAddress(currentSilentAddressIndex).toString(), + silentAddress!.toLabeledSilentPaymentAddress(currentSilentAddressIndex).toString(), index: currentSilentAddressIndex, isHidden: false, name: label, silentPaymentTweak: - BytesUtils.toHexString(primarySilentAddress!.generateLabel(currentSilentAddressIndex)), + BytesUtils.toHexString(silentAddress!.generateLabel(currentSilentAddressIndex)), network: network, - type: SilentPaymentsAddresType.p2sp, ); silentAddresses.add(address); diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 3584b0fe3e..464e23843a 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -78,11 +78,9 @@ packages: bitcoin_base: dependency: "direct main" description: - path: "." - ref: cake-update-v2 - resolved-ref: "7634511b15e5a48bd18e3c19f81971628090c04f" - url: "https://github.com/cake-tech/bitcoin_base.git" - source: git + path: "/home/rafael/Working/bitcoin_base" + relative: false + source: path version: "4.0.0" bitcoin_flutter: dependency: "direct main" @@ -96,11 +94,9 @@ packages: blockchain_utils: dependency: "direct main" description: - path: "." - ref: cake-update-v1 - resolved-ref: "6a0b891db4d90c647ebf5fc3a9132e614c70e1c6" - url: "https://github.com/cake-tech/blockchain_utils" - source: git + path: "/home/rafael/Working/blockchain_utils" + relative: false + source: path version: "1.6.0" boolean_selector: dependency: transitive diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index bfa6c72e60..eafa008817 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -31,13 +31,9 @@ dependencies: unorm_dart: ^0.2.0 cryptography: ^2.0.5 bitcoin_base: - git: - url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v2 + path: /home/rafael/Working/bitcoin_base blockchain_utils: - git: - url: https://github.com/cake-tech/blockchain_utils - ref: cake-update-v1 + path: /home/rafael/Working/blockchain_utils dev_dependencies: flutter_test: diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index a967681503..c80df0458d 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -30,13 +30,9 @@ dependencies: url: https://github.com/cake-tech/bitbox-flutter.git ref: master bitcoin_base: - git: - url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v2 + path: /home/rafael/Working/bitcoin_base blockchain_utils: - git: - url: https://github.com/cake-tech/blockchain_utils - ref: cake-update-v1 + path: /home/rafael/Working/blockchain_utils dev_dependencies: flutter_test: diff --git a/scripts/android/app_env.fish b/scripts/android/app_env.fish new file mode 100644 index 0000000000..be82ab9e67 --- /dev/null +++ b/scripts/android/app_env.fish @@ -0,0 +1,75 @@ +#!/usr/bin/env fish + +set APP_ANDROID_NAME "" +set APP_ANDROID_VERSION "" +set APP_ANDROID_BUILD_VERSION "" +set APP_ANDROID_ID "" +set APP_ANDROID_PACKAGE "" +set APP_ANDROID_SCHEME "" + +set MONERO_COM "monero.com" +set CAKEWALLET "cakewallet" +set HAVEN "haven" + +set -l TYPES $MONERO_COM $CAKEWALLET $HAVEN +set APP_ANDROID_TYPE $argv[1] + +set MONERO_COM_NAME "Monero.com" +set MONERO_COM_VERSION "1.10.0" +set MONERO_COM_BUILD_NUMBER 72 +set MONERO_COM_BUNDLE_ID "com.monero.app" +set MONERO_COM_PACKAGE "com.monero.app" +set MONERO_COM_SCHEME "monero.com" + +set CAKEWALLET_NAME "Cake Wallet" +set CAKEWALLET_VERSION "4.13.0" +set CAKEWALLET_BUILD_NUMBER 189 +set CAKEWALLET_BUNDLE_ID "com.cakewallet.cake_wallet" +set CAKEWALLET_PACKAGE "com.cakewallet.cake_wallet" +set CAKEWALLET_SCHEME "cakewallet" + +set HAVEN_NAME "Haven" +set HAVEN_VERSION "1.0.0" +set HAVEN_BUILD_NUMBER 1 +set HAVEN_BUNDLE_ID "com.cakewallet.haven" +set HAVEN_PACKAGE "com.cakewallet.haven" + +if not contains $APP_ANDROID_TYPE $TYPES + echo "Wrong app type." + return 1 + exit 1 +end + +switch $APP_ANDROID_TYPE + case $MONERO_COM + set APP_ANDROID_NAME $MONERO_COM_NAME + set APP_ANDROID_VERSION $MONERO_COM_VERSION + set APP_ANDROID_BUILD_NUMBER $MONERO_COM_BUILD_NUMBER + set APP_ANDROID_BUNDLE_ID $MONERO_COM_BUNDLE_ID + set APP_ANDROID_PACKAGE $MONERO_COM_PACKAGE + set APP_ANDROID_SCHEME $MONERO_COM_SCHEME + ;; + case $CAKEWALLET + set APP_ANDROID_NAME $CAKEWALLET_NAME + set APP_ANDROID_VERSION $CAKEWALLET_VERSION + set APP_ANDROID_BUILD_NUMBER $CAKEWALLET_BUILD_NUMBER + set APP_ANDROID_BUNDLE_ID $CAKEWALLET_BUNDLE_ID + set APP_ANDROID_PACKAGE $CAKEWALLET_PACKAGE + set APP_ANDROID_SCHEME $CAKEWALLET_SCHEME + ;; + case $HAVEN + set APP_ANDROID_NAME $HAVEN_NAME + set APP_ANDROID_VERSION $HAVEN_VERSION + set APP_ANDROID_BUILD_NUMBER $HAVEN_BUILD_NUMBER + set APP_ANDROID_BUNDLE_ID $HAVEN_BUNDLE_ID + set APP_ANDROID_PACKAGE $HAVEN_PACKAGE + ;; +end + +export APP_ANDROID_TYPE +export APP_ANDROID_NAME +export APP_ANDROID_VERSION +export APP_ANDROID_BUILD_NUMBER +export APP_ANDROID_BUNDLE_ID +export APP_ANDROID_PACKAGE +export APP_ANDROID_SCHEME From 2db156ef16f33c3fe543f4737984a1bfa9eac9d3 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 1 Mar 2024 20:52:41 -0300 Subject: [PATCH 11/75] chore: deps --- cw_bitcoin/pubspec.lock | 16 ++++++++++------ cw_bitcoin/pubspec.yaml | 8 ++++++-- cw_bitcoin_cash/pubspec.yaml | 8 ++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 464e23843a..91af2d8071 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -78,9 +78,11 @@ packages: bitcoin_base: dependency: "direct main" description: - path: "/home/rafael/Working/bitcoin_base" - relative: false - source: path + path: "." + ref: cake-update-v2 + resolved-ref: f45e34d18ddff52764101a2ec96dcbc2be730555 + url: "https://github.com/cake-tech/bitcoin_base" + source: git version: "4.0.0" bitcoin_flutter: dependency: "direct main" @@ -94,9 +96,11 @@ packages: blockchain_utils: dependency: "direct main" description: - path: "/home/rafael/Working/blockchain_utils" - relative: false - source: path + path: "." + ref: cake-update-v1 + resolved-ref: "7864de88e9a0b598a61b1e50d26f6f4477a6411c" + url: "https://github.com/cake-tech/blockchain_utils" + source: git version: "1.6.0" boolean_selector: dependency: transitive diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index eafa008817..f6ee88e299 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -31,9 +31,13 @@ dependencies: unorm_dart: ^0.2.0 cryptography: ^2.0.5 bitcoin_base: - path: /home/rafael/Working/bitcoin_base + git: + url: https://github.com/cake-tech/bitcoin_base + ref: cake-update-v2 blockchain_utils: - path: /home/rafael/Working/blockchain_utils + git: + url: https://github.com/cake-tech/blockchain_utils + ref: cake-update-v1 dev_dependencies: flutter_test: diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index c80df0458d..4d500f048d 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -30,9 +30,13 @@ dependencies: url: https://github.com/cake-tech/bitbox-flutter.git ref: master bitcoin_base: - path: /home/rafael/Working/bitcoin_base + git: + url: https://github.com/cake-tech/bitcoin_base + ref: cake-update-v2 blockchain_utils: - path: /home/rafael/Working/blockchain_utils + git: + url: https://github.com/cake-tech/blockchain_utils + ref: cake-update-v1 dev_dependencies: flutter_test: From 23a368385388773a52b09a6351cbe0b07fc58057 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 5 Mar 2024 11:54:40 -0300 Subject: [PATCH 12/75] fix: label issues, clear spent utxo --- cw_bitcoin/lib/electrum_transaction_info.dart | 2 +- cw_bitcoin/lib/electrum_wallet.dart | 30 +++++++++++++++---- cw_bitcoin/lib/electrum_wallet_addresses.dart | 4 ++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index 5564ce6724..6254dc3ef2 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -163,7 +163,7 @@ class ElectrumTransactionInfo extends TransactionInfo { unspents: data['unspents'] != null ? (data['unspents'] as List) .map((unspent) => BitcoinUnspent.fromJSON( - BitcoinAddressRecord.fromJSON(unspent['address_record'].toString()), + BitcoinSilentPaymentAddressRecord.fromJSON(unspent['address_record'].toString()), unspent as Map)) .toList() : null, diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 8d68f6c8f0..0220c440e3 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -328,6 +328,7 @@ abstract class ElectrumWalletBase List vinOutpoints = []; List inputPrivKeyInfos = []; List inputPubKeys = []; + bool spendsSilentPayment = false; for (int i = 0; i < unspentCoins.length; i++) { final utx = unspentCoins[i]; @@ -345,6 +346,7 @@ abstract class ElectrumWalletBase (utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord) .silentPaymentTweak!)), ); + spendsSilentPayment = true; } else { privkey = generateECPrivate( hd: utx.bitcoinAddressRecord.isHidden @@ -481,7 +483,12 @@ abstract class ElectrumWalletBase } } - return EstimatedTxResult(utxos: utxos, privateKeys: privateKeys, fee: fee, amount: amount); + return EstimatedTxResult( + utxos: utxos, + privateKeys: privateKeys, + fee: fee, + amount: amount, + spendsSilentPayment: spendsSilentPayment); } @override @@ -559,6 +566,14 @@ abstract class ElectrumWalletBase network: network) ..addListener((transaction) async { transactionHistory.addOne(transaction); + if (estimatedTx.spendsSilentPayment) { + transactionHistory.transactions.values.forEach((tx) { + tx.unspents?.removeWhere( + (unspent) => estimatedTx.utxos.any((e) => e.utxo.txHash == unspent.hash)); + transactionHistory.addOne(tx); + }); + } + await updateBalance(); }); } catch (e) { @@ -1196,7 +1211,7 @@ Future startRefresh(ScanData scanData) async { for (var i = 0; i < tweaks!.length; i++) { try { final details = tweaks[i] as Map; - final output_pubkeys = (details["output_pubkeys"] as List); + final outputPubkeys = (details["output_pubkeys"] as List); final tweak = details["tweak"].toString(); // TODO: if tx already scanned & stored skip @@ -1210,7 +1225,7 @@ Future startRefresh(ScanData scanData) async { final result = spb.scanOutputs( scanData.silentAddress.b_scan, scanData.silentAddress.B_spend, - output_pubkeys + outputPubkeys .map((output) => BytesUtils.toHexString(BytesUtils.fromHexString(output.toString()).sublist(2)) .toString()) @@ -1235,7 +1250,7 @@ Future startRefresh(ScanData scanData) async { try { final addressRecord = BitcoinSilentPaymentAddressRecord(address, index: 0, - isHidden: true, + isHidden: false, isUsed: true, network: scanData.network, silentPaymentTweak: t_k); @@ -1332,12 +1347,17 @@ Future startRefresh(ScanData scanData) async { class EstimatedTxResult { EstimatedTxResult( - {required this.utxos, required this.privateKeys, required this.fee, required this.amount}); + {required this.utxos, + required this.privateKeys, + required this.fee, + required this.amount, + required this.spendsSilentPayment}); final List utxos; final List privateKeys; final int fee; final int amount; + final bool spendsSilentPayment; } BitcoinBaseAddress _addressTypeFromStr(String address, BasedUtxoNetwork network) { diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 5a839e3a58..cddb8f1f36 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -244,10 +244,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final silentAddressRecord = silentAddresses[i]; final silentPaymentTweak = silentAddressRecord.silentPaymentTweak; - if (silentPaymentTweak != null) + if (silentPaymentTweak != null && + SilentPaymentAddress.regex.hasMatch(silentAddressRecord.address)) { labels[G .tweakMul(BigintUtils.fromBytes(BytesUtils.fromHexString(silentPaymentTweak))) .toHex()] = silentPaymentTweak; + } } return labels; } From 0832e216381a594a7df718e31955b87149d7519c Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 5 Mar 2024 12:19:18 -0300 Subject: [PATCH 13/75] chore: deps --- pubspec_base.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec_base.yaml b/pubspec_base.yaml index d4bf981cd2..2a9321186e 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -109,8 +109,8 @@ dependencies: solana: ^0.30.1 bitcoin_base: git: - url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v1 + url: https://github.com/cake-tech/bitcoin_base + ref: cake-update-v2 dev_dependencies: flutter_test: From e8abd86d3c76cba543b7b804a0754691f1e6c499 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 5 Mar 2024 12:33:03 -0300 Subject: [PATCH 14/75] fix: build --- lib/bitcoin/cw_bitcoin.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 3e86ad3815..b27605856d 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -181,7 +181,7 @@ class CWBitcoin extends Bitcoin { } @override - ReceivePageOption getSelectedAddressType(Object wallet) { + BitcoinReceivePageOption getSelectedAddressType(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; return BitcoinReceivePageOption.fromType(bitcoinWallet.walletAddresses.addressPageType); } @@ -210,6 +210,7 @@ class CWBitcoin extends Bitcoin { default: return SegwitAddresType.p2wpkh; } + } List getSilentAddresses(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; From 4308e3e12303fc063444683c21ed7ccb43afc88a Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 5 Mar 2024 13:53:06 -0300 Subject: [PATCH 15/75] fix: missing types --- lib/bitcoin/cw_bitcoin.dart | 8 ++++++++ lib/src/screens/dashboard/pages/address_page.dart | 4 ++-- tool/configure.dart | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index b27605856d..3543ef5cc9 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -216,4 +216,12 @@ class CWBitcoin extends Bitcoin { final bitcoinWallet = wallet as ElectrumWallet; return bitcoinWallet.walletAddresses.silentAddresses; } + + bool isBitcoinReceivePageOption(ReceivePageOption option) { + return option is BitcoinReceivePageOption; + } + + BitcoinAddressType getOptionToType(ReceivePageOption option) { + return (option as BitcoinReceivePageOption).toType(); + } } diff --git a/lib/src/screens/dashboard/pages/address_page.dart b/lib/src/screens/dashboard/pages/address_page.dart index 044866f5ce..4d08cde8bb 100644 --- a/lib/src/screens/dashboard/pages/address_page.dart +++ b/lib/src/screens/dashboard/pages/address_page.dart @@ -198,8 +198,8 @@ class AddressPage extends BasePage { } reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) { - if (option is BitcoinReceivePageOption) { - addressListViewModel.setAddressType(option.toType()); + if (bitcoin!.isBitcoinReceivePageOption(option)) { + addressListViewModel.setAddressType(bitcoin!.getOptionToType(option)); return; } diff --git a/tool/configure.dart b/tool/configure.dart index 10b573655e..6d5d8e7bb5 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -149,6 +149,8 @@ abstract class Bitcoin { BitcoinAddressType getBitcoinAddressType(ReceivePageOption option); bool hasSelectedSilentPayments(Object wallet); List getBitcoinReceivePageOptions(); + bool isBitcoinReceivePageOption(ReceivePageOption option); + BitcoinAddressType getOptionToType(ReceivePageOption option); } """; From dd532b4fe0e175f85e3d5c4692b2b45810404ac7 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 11 Mar 2024 09:46:20 -0300 Subject: [PATCH 16/75] feat: new electrs API & changes, fixes for last block scanning --- cw_bitcoin/lib/bitcoin_address_record.dart | 7 +- cw_bitcoin/lib/electrum.dart | 7 +- cw_bitcoin/lib/electrum_balance.dart | 9 +- cw_bitcoin/lib/electrum_wallet.dart | 287 +++++++++--------- cw_bitcoin/lib/electrum_wallet_addresses.dart | 12 +- cw_bitcoin/pubspec.lock | 2 +- cw_core/lib/sync_status.dart | 7 + lib/core/sync_status_title.dart | 4 + lib/entities/default_settings_migration.dart | 4 +- res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 1 + res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 1 + res/values/strings_ko.arb | 1 + res/values/strings_my.arb | 1 + res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 1 + res/values/strings_th.arb | 1 + res/values/strings_tl.arb | 1 + res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 1 + res/values/strings_yo.arb | 1 + res/values/strings_zh.arb | 1 + 35 files changed, 211 insertions(+), 154 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index f5731f0f12..c6d5cfe2c2 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -126,7 +126,8 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { super.isUsed = false, required this.silentPaymentTweak, required super.network, - }) : super(type: SilentPaymentsAddresType.p2sp); + required super.type, + }) : super(); factory BitcoinSilentPaymentAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) { @@ -144,6 +145,10 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord { ? network : BasedUtxoNetwork.fromName(decoded['network'] as String), silentPaymentTweak: decoded['silent_payment_tweak'] as String?, + type: decoded['type'] != null && decoded['type'] != '' + ? BitcoinAddressType.values + .firstWhere((type) => type.toString() == decoded['type'] as String) + : SilentPaymentsAddresType.p2sp, ); } diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index d9e068d654..4aefa3bd11 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -279,8 +279,11 @@ class ElectrumClient { Future> getHeader({required int height}) async => await call(method: 'blockchain.block.get_header', params: [height]) as Map; - Future> getTweaks({required int height}) async => - await callWithTimeout(method: 'blockchain.block.tweaks', params: [height]) as List; + Future> getTweaks({required int height, required int count}) async => + await callWithTimeout( + method: 'blockchain.block.tweaks', + params: [height, count], + timeout: 10000) as Map; Future estimatefee({required int p}) => call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) { diff --git a/cw_bitcoin/lib/electrum_balance.dart b/cw_bitcoin/lib/electrum_balance.dart index 165ea447e8..45de7de6d5 100644 --- a/cw_bitcoin/lib/electrum_balance.dart +++ b/cw_bitcoin/lib/electrum_balance.dart @@ -3,8 +3,11 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart'; import 'package:cw_core/balance.dart'; class ElectrumBalance extends Balance { - const ElectrumBalance({required this.confirmed, required this.unconfirmed, required this.frozen}) - : super(confirmed, unconfirmed); + ElectrumBalance({ + required this.confirmed, + required this.unconfirmed, + required this.frozen, + }) : super(confirmed, unconfirmed); static ElectrumBalance? fromJSON(String? jsonSource) { if (jsonSource == null) { @@ -19,7 +22,7 @@ class ElectrumBalance extends Balance { frozen: decoded['frozen'] as int? ?? 0); } - final int confirmed; + int confirmed; final int unconfirmed; final int frozen; diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 0220c440e3..e0bdfde285 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -44,6 +44,8 @@ import 'package:http/http.dart' as http; part 'electrum_wallet.g.dart'; +const SCANNING_BLOCK_COUNT = 50; + class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet; abstract class ElectrumWalletBase @@ -72,8 +74,12 @@ abstract class ElectrumWalletBase _scripthashesUpdateSubject = {}, balance = ObservableMap.of(currency != null ? { - currency: - initialBalance ?? const ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0) + currency: initialBalance ?? + ElectrumBalance( + confirmed: 0, + unconfirmed: 0, + frozen: 0, + ) } : {}), this.unspentCoinsInfo = unspentCoinsInfo, @@ -194,9 +200,9 @@ abstract class ElectrumWalletBase )); await for (var message in receivePort) { - if (message is bool) { + if (message is bool && message == false) { nodeSupportsSilentPayments = message; - syncStatus = UnsupportedSyncStatus(); + syncStatus = TimedOutSyncStatus(); } if (message is Map) { @@ -238,7 +244,7 @@ abstract class ElectrumWalletBase await transactionHistory.save(); await updateUnspent(); - await save(); + await updateBalance(); } } } @@ -320,7 +326,6 @@ abstract class ElectrumWalletBase {int? inputsCount, bool? hasSilentPayment}) async { final utxos = []; - List privateKeys = []; var leftAmount = credentialsAmount; var allInputsAmount = 0; @@ -340,13 +345,15 @@ abstract class ElectrumWalletBase final address = _addressTypeFromStr(utx.address, network); ECPrivate? privkey; + bool? isSilentPayment = false; if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { - privkey = walletAddresses.silentAddress!.b_spend.clone().tweakAdd( - BigintUtils.fromBytes(BytesUtils.fromHexString( - (utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord) - .silentPaymentTweak!)), - ); + privkey = walletAddresses.silentAddress!.b_spend.tweakAdd( + BigintUtils.fromBytes(BytesUtils.fromHexString( + (utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord) + .silentPaymentTweak!)), + ); spendsSilentPayment = true; + isSilentPayment = true; } else { privkey = generateECPrivate( hd: utx.bitcoinAddressRecord.isHidden @@ -356,7 +363,6 @@ abstract class ElectrumWalletBase network: network); } - privateKeys.add(privkey); inputPrivKeyInfos.add(ECPrivateInfo(privkey, address.type == SegwitAddresType.p2tr)); inputPubKeys.add(privkey.getPublic()); vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); @@ -368,6 +374,7 @@ abstract class ElectrumWalletBase value: BigInt.from(utx.value), vout: utx.vout, scriptType: _getScriptType(address), + isSilentPayment: isSilentPayment, ), ownerDetails: UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: address), @@ -485,7 +492,7 @@ abstract class ElectrumWalletBase return EstimatedTxResult( utxos: utxos, - privateKeys: privateKeys, + inputPrivKeyInfos: inputPrivKeyInfos, fee: fee, amount: amount, spendsSilentPayment: spendsSilentPayment); @@ -535,8 +542,13 @@ abstract class ElectrumWalletBase } final estimatedTx = await _estimateTxFeeAndInputsToUse( - credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials, - hasSilentPayment: hasSilentPayment); + credentialsAmount, + sendAll, + outputAddresses, + outputs, + transactionCredentials, + hasSilentPayment: hasSilentPayment, + ); final txb = BitcoinTransactionBuilder( utxos: estimatedTx.utxos, @@ -545,17 +557,21 @@ abstract class ElectrumWalletBase network: network); final transaction = txb.buildTransaction((txDigest, utxo, publicKey, sighash) { - final key = estimatedTx.privateKeys - .firstWhereOrNull((element) => element.getPublic().toHex() == publicKey); + final key = estimatedTx.inputPrivKeyInfos + .firstWhereOrNull((element) => element.privkey.getPublic().toHex() == publicKey); if (key == null) { throw Exception("Cannot find private key"); } if (utxo.utxo.isP2tr()) { - return key.signTapRoot(txDigest, sighash: sighash); + return key.privkey.signTapRoot( + txDigest, + sighash: sighash, + tweak: utxo.utxo.isSilentPayment != true, + ); } else { - return key.signInput(txDigest, sigHash: sighash); + return key.privkey.signInput(txDigest, sigHash: sighash); } }); @@ -820,7 +836,9 @@ abstract class ElectrumWalletBase (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$hash/status"))).body); time = status["block_time"] as int?; - confirmations = currentChainTip! - (status["block_height"] as int? ?? 0); + final height = status["block_height"] as int? ?? 0; + confirmations = + height > 0 ? (await electrumClient.getCurrentBlockChainTip())! - height + 1 : 0; } else { final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash); @@ -1056,6 +1074,19 @@ abstract class ElectrumWalletBase Future updateBalance() async { balance[currency] = await _fetchBalances(); + + // Update balance stored from scanned silent payment transactions + try { + transactionHistory.transactions.values.forEach((tx) { + if (tx.unspents != null) { + balance[currency]!.confirmed += tx.unspents! + .where((unspent) => unspent.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) + .map((e) => e.value) + .reduce((value, element) => value + element); + } + }); + } catch (_) {} + await save(); } @@ -1194,9 +1225,9 @@ Future startRefresh(ScanData scanData) async { try { final electrumClient = await getElectrumConnection(); - List? tweaks; + Map? tweaks; try { - tweaks = await electrumClient.getTweaks(height: syncHeight); + tweaks = await electrumClient.getTweaks(height: syncHeight, count: SCANNING_BLOCK_COUNT); } catch (e) { if (e is RequestFailedTimeoutException) { return scanData.sendPort.send(false); @@ -1208,133 +1239,103 @@ Future startRefresh(ScanData scanData) async { SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); } - for (var i = 0; i < tweaks!.length; i++) { + final blockHeights = tweaks!.keys; + for (var i = 0; i < blockHeights.length; i++) { try { - final details = tweaks[i] as Map; - final outputPubkeys = (details["output_pubkeys"] as List); - final tweak = details["tweak"].toString(); - - // TODO: if tx already scanned & stored skip - // if (scanData.transactionHistoryIds.contains(txid)) { - // // already scanned tx, continue to next tx - // pos++; - // continue; - // } - - final spb = SilentPaymentBuilder(receiverTweak: tweak); - final result = spb.scanOutputs( - scanData.silentAddress.b_scan, - scanData.silentAddress.B_spend, - outputPubkeys - .map((output) => - BytesUtils.toHexString(BytesUtils.fromHexString(output.toString()).sublist(2)) - .toString()) - .toList(), - precomputedLabels: scanData.labels, - ); + final blockHeight = blockHeights.elementAt(i).toString(); + final blockTweaks = tweaks[blockHeight] as Map; + + for (var j = 0; j < blockTweaks.keys.length; j++) { + final txid = blockTweaks.keys.elementAt(j); + final details = blockTweaks[txid] as Map; + final outputPubkeys = (details["output_pubkeys"] as Map); + final tweak = details["tweak"].toString(); + + final spb = SilentPaymentBuilder(receiverTweak: tweak); + final addToWallet = spb.scanOutputs( + scanData.silentAddress.b_scan, + scanData.silentAddress.B_spend, + outputPubkeys.values + .map((o) => getScriptFromOutput( + o["pubkey"].toString(), int.parse(o["amount"].toString()))) + .toList(), + precomputedLabels: scanData.labels, + ); - if (result.isEmpty) { - // no results tx, continue to next tx - continue; - } + if (addToWallet.isEmpty) { + // no results tx, continue to next tx + continue; + } - result.forEach((key, value) async { - final t_k = value[0]; - final address = ECPublic.fromHex(key).toTaprootAddress().toAddress(scanData.network); + addToWallet.forEach((key, value) async { + final t_k = value.tweak; + + final addressRecord = BitcoinSilentPaymentAddressRecord( + value.output.address.toAddress(scanData.network), + index: 0, + isHidden: false, + isUsed: true, + network: scanData.network, + silentPaymentTweak: t_k, + type: SegwitAddresType.p2tr, + ); - final listUnspent = - await electrumClient.getListUnspentWithAddress(address, scanData.network); + int? amount; + int? pos; + outputPubkeys.entries.firstWhere((k) { + final matches = k.value["pubkey"] == key; + if (matches) { + amount = int.parse(k.value["amount"].toString()); + pos = int.parse(k.key.toString()); + return true; + } + return false; + }); + + final json = { + 'address_record': addressRecord.toJSON(), + 'tx_hash': txid, + 'value': amount!, + 'tx_pos': pos!, + 'silent_payment_tweak': t_k, + }; + + final tx = BitcoinUnspent.fromJSON(addressRecord, json); + + final txInfo = ElectrumTransactionInfo( + WalletType.bitcoin, + id: tx.hash, + height: syncHeight, + amount: 0, // will be added later via unspent + fee: 0, + direction: TransactionDirection.incoming, + isPending: false, + date: DateTime.now(), + confirmations: currentChainTip - syncHeight - 1, + to: value.label != null + ? SilentPaymentAddress( + version: scanData.silentAddress.version, + B_scan: scanData.silentAddress.B_scan + .tweakAdd(BigintUtils.fromBytes(BytesUtils.fromHexString(value.tweak))), + B_spend: scanData.silentAddress.B_spend, + hrp: scanData.silentAddress.hrp, + ).toString() + : scanData.silentAddress.toString(), + unspents: [tx], + ); - BitcoinUnspent? info; - await Future.forEach>(listUnspent, (unspent) async { - try { - final addressRecord = BitcoinSilentPaymentAddressRecord(address, - index: 0, - isHidden: false, - isUsed: true, - network: scanData.network, - silentPaymentTweak: t_k); - info = BitcoinUnspent.fromJSON(addressRecord, unspent); - } catch (_) {} + scanData.sendPort.send({txInfo.id: txInfo}); }); + } + } catch (e, s) { + print([e, s]); + } - if (info == null) { - return; - } - - // final tweak = value[0]; - // String? label; - // if (value.length > 1) label = value[1]; - - final tx = info!; - final txInfo = ElectrumTransactionInfo( - WalletType.bitcoin, - id: tx.hash, - height: syncHeight, - amount: 0, // will be added later via unspent - fee: 0, - direction: TransactionDirection.incoming, - isPending: false, - date: DateTime.now(), - confirmations: currentChainTip - syncHeight - 1, - to: scanData.silentAddress.toString(), - unspents: [tx], - ); - - // bool spent = false; - // for (final s in status) { - // if ((s["spent"] as bool) == true) { - // spent = true; - - // scanData.sendPort.send({txid: txInfo}); - - // final sentTxId = s["txid"] as String; - // final sentTx = json.decode( - // (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$sentTxId"))) - // .body); - - // int amount = 0; - // for (final out in (sentTx["vout"] as List)) { - // amount += out["value"] as int; - // } - - // final height = s["status"]["block_height"] as int; - - // scanData.sendPort.send({ - // sentTxId: ElectrumTransactionInfo( - // WalletType.bitcoin, - // id: sentTxId, - // height: height, - // amount: amount, - // fee: 0, - // direction: TransactionDirection.outgoing, - // isPending: false, - // date: DateTime.fromMillisecondsSinceEpoch( - // (s["status"]["block_time"] as int) * 1000), - // confirmations: currentChainTip - height, - // ) - // }); - // } - // } - - // if (spent) { - // return; - // } - - // found utxo for tx, send unspent coin to main isolate - // scanData.sendPort.send(txInfo); - - // also send tx data for tx history - scanData.sendPort.send({txInfo.id: txInfo}); - }); - } catch (_) {} + // Finished scanning block, add 1 to height and continue to next block in loop + syncHeight += 1; + scanData.sendPort.send(SyncResponse(syncHeight, + SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); } - - // Finished scanning block, add 1 to height and continue to next block in loop - syncHeight += 1; - currentChainTip = await getNodeHeightOrUpdate(syncHeight); - scanData.sendPort.send(SyncResponse(syncHeight, - SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); } catch (e, stacktrace) { print(stacktrace); print(e.toString()); @@ -1348,13 +1349,13 @@ Future startRefresh(ScanData scanData) async { class EstimatedTxResult { EstimatedTxResult( {required this.utxos, - required this.privateKeys, + required this.inputPrivKeyInfos, required this.fee, required this.amount, required this.spendsSilentPayment}); final List utxos; - final List privateKeys; + final List inputPrivKeyInfos; final int fee; final int amount; final bool spendsSilentPayment; diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index cddb8f1f36..270ad855fc 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -57,8 +57,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp'); if (silentAddresses.length == 0) - silentAddresses.add(BitcoinSilentPaymentAddressRecord(silentAddress.toString(), - index: 1, isHidden: false, name: "", silentPaymentTweak: null, network: network)); + silentAddresses.add(BitcoinSilentPaymentAddressRecord( + silentAddress.toString(), + index: 1, + isHidden: false, + name: "", + silentPaymentTweak: null, + network: network, + type: SilentPaymentsAddresType.p2sp, + )); } updateAddressesByMatch(); @@ -267,6 +274,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { silentPaymentTweak: BytesUtils.toHexString(silentAddress!.generateLabel(currentSilentAddressIndex)), network: network, + type: SilentPaymentsAddresType.p2sp, ); silentAddresses.add(address); diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 91af2d8071..8d6f6268b7 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -80,7 +80,7 @@ packages: description: path: "." ref: cake-update-v2 - resolved-ref: f45e34d18ddff52764101a2ec96dcbc2be730555 + resolved-ref: fb4c0a0b6cf24628ddad7d3cdc58e4c918eff714 url: "https://github.com/cake-tech/bitcoin_base" source: git version: "4.0.0" diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index f562e7ce32..dd2d9ca67b 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -63,6 +63,13 @@ class UnsupportedSyncStatus extends SyncStatus { double progress() => 1.0; } +class TimedOutSyncStatus extends SyncStatus { + @override + double progress() => 1.0; + @override + String toString() => 'Timed out'; +} + class LostConnectionSyncStatus extends SyncStatus { @override double progress() => 1.0; diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index 23bfb0db28..c743caf55d 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -40,5 +40,9 @@ String syncStatusTitle(SyncStatus syncStatus) { return S.current.sync_status_unsupported; } + if (syncStatus is TimedOutSyncStatus) { + return S.current.sync_status_timed_out; + } + return ''; } diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index fb3e9e80ce..d12140cb99 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -23,8 +23,8 @@ import 'package:collection/collection.dart'; const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081'; const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002'; -const publicBitcoinTestnetElectrumAddress = 'electrum.blockstream.info'; -const publicBitcoinTestnetElectrumPort = '60002'; +const publicBitcoinTestnetElectrumAddress = '198.58.111.154'; +const publicBitcoinTestnetElectrumPort = '50002'; const publicBitcoinTestnetElectrumUri = '$publicBitcoinTestnetElectrumAddress:$publicBitcoinTestnetElectrumPort'; const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002'; diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index b02e3604f6..452b20933b 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "بدء المزامنة", "sync_status_syncronized": "متزامن", "sync_status_syncronizing": "يتم المزامنة", + "sync_status_timed_out": "نفد وقته", "sync_status_unsupported": "عقدة غير مدعومة", "syncing_wallet_alert_content": "قد لا يكتمل رصيدك وقائمة المعاملات الخاصة بك حتى تظهر عبارة “SYNCHRONIZED“ في الأعلى. انقر / اضغط لمعرفة المزيد.", "syncing_wallet_alert_title": "محفظتك تتم مزامنتها", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 89c4cc24b6..1f02060ec1 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "ЗАПОЧВАНЕ НА СИНХРОНИЗАЦИЯ", "sync_status_syncronized": "СИНХРОНИЗИРАНО", "sync_status_syncronizing": "СИНХРОНИЗИРАНЕ", + "sync_status_timed_out": "ВРЕМЕТО ИЗТЕЧЕ", "sync_status_unsupported": "Неподдържан възел", "syncing_wallet_alert_content": "Списъкът ви с баланс и транзакции може да не е пълен, докато в горната част не пише „СИНХРОНИЗИРАН“. Кликнете/докоснете, за да научите повече.", "syncing_wallet_alert_title": "Вашият портфейл се синхронизира", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index e536a73380..cf045754d4 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "SPOUŠTĚNÍ SYNCHRONIZACE", "sync_status_syncronized": "SYNCHRONIZOVÁNO", "sync_status_syncronizing": "SYNCHRONIZUJI", + "sync_status_timed_out": "ČAS VYPRŠEL", "sync_status_unsupported": "Nepodporovaný uzel", "syncing_wallet_alert_content": "Váš seznam zůstatků a transakcí nemusí být úplný, dokud nebude nahoře uvedeno „SYNCHRONIZOVANÉ“. Kliknutím/klepnutím se dozvíte více.", "syncing_wallet_alert_title": "Vaše peněženka se synchronizuje", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index f1179fc8a2..2ab5e6c9fd 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "STARTE SYNCHRONISIERUNG", "sync_status_syncronized": "SYNCHRONISIERT", "sync_status_syncronizing": "SYNCHRONISIERE", + "sync_status_timed_out": "Zeitlich abgestimmt", "sync_status_unsupported": "Nicht unterstützter Knoten", "syncing_wallet_alert_content": "Ihr Kontostand und Ihre Transaktionsliste sind möglicherweise erst vollständig, wenn oben „SYNCHRONISIERT“ steht. Klicken/tippen Sie, um mehr zu erfahren.", "syncing_wallet_alert_title": "Ihr Wallet wird synchronisiert", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 5b77a4c611..78d7dd7777 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "STARTING SYNC", "sync_status_syncronized": "SYNCHRONIZED", "sync_status_syncronizing": "SYNCHRONIZING", + "sync_status_timed_out": "TIMED OUT", "sync_status_unsupported": "UNSUPPORTED NODE", "syncing_wallet_alert_content": "Your balance and transaction list may not be complete until it says “SYNCHRONIZED” at the top. Click/tap to learn more.", "syncing_wallet_alert_title": "Your wallet is syncing", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index c27c8829ba..f1c27763c7 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "EMPEZANDO A SINCRONIZAR", "sync_status_syncronized": "SINCRONIZADO", "sync_status_syncronizing": "SINCRONIZANDO", + "sync_status_timed_out": "CADUCADO", "sync_status_unsupported": "Nodo no compatible", "syncing_wallet_alert_content": "Es posible que su lista de saldo y transacciones no esté completa hasta que diga \"SINCRONIZADO\" en la parte superior. Haga clic/toque para obtener más información.", "syncing_wallet_alert_title": "Tu billetera se está sincronizando", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 8068c67a92..7f2ed7799b 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "DÉBUT DE SYNCHRO", "sync_status_syncronized": "SYNCHRONISÉ", "sync_status_syncronizing": "SYNCHRONISATION EN COURS", + "sync_status_timed_out": "FIN DU TEMPS", "sync_status_unsupported": "Nœud non pris en charge", "syncing_wallet_alert_content": "Votre solde et votre liste de transactions peuvent ne pas être à jour tant que la mention « SYNCHRONISÉ » n'apparaît en haut de l'écran. Cliquez/appuyez pour en savoir plus.", "syncing_wallet_alert_title": "Votre portefeuille (wallet) est en cours de synchronisation", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 5b79f8e9e3..5a52af0010 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "KWAFI", "sync_status_syncronized": "KYAU", "sync_status_syncronizing": "KWAFI", + "sync_status_timed_out": "ATED Out", "sync_status_unsupported": "Ba a Taimako ba", "syncing_wallet_alert_content": "Ma'aunin ku da lissafin ma'amala bazai cika ba har sai an ce \"SYNCHRONIZED\" a saman. Danna/matsa don ƙarin koyo.", "syncing_wallet_alert_title": "Walat ɗin ku yana aiki tare", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 35b5d5d01b..1aa9d3ca0e 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "सिताज़ा करना", "sync_status_syncronized": "सिंक्रनाइज़", "sync_status_syncronizing": "सिंक्रनाइज़ करने", + "sync_status_timed_out": "समय समााप्त", "sync_status_unsupported": "असमर्थित नोड", "syncing_wallet_alert_content": "आपकी शेष राशि और लेनदेन सूची तब तक पूरी नहीं हो सकती जब तक कि शीर्ष पर \"सिंक्रनाइज़्ड\" न लिखा हो। अधिक जानने के लिए क्लिक/टैप करें।", "syncing_wallet_alert_title": "आपका वॉलेट सिंक हो रहा है", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 4bbcdd4700..b8bc9f3b68 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "ZAPOČINJEMO SINKRONIZIRANJE", "sync_status_syncronized": "SINKRONIZIRANO", "sync_status_syncronizing": "SINKRONIZIRANJE", + "sync_status_timed_out": "ISTEKLO", "sync_status_unsupported": "Nepodržani čvor", "syncing_wallet_alert_content": "Vaš saldo i popis transakcija možda neće biti potpuni sve dok na vrhu ne piše \"SINKRONIZIRANO\". Kliknite/dodirnite da biste saznali više.", "syncing_wallet_alert_title": "Vaš novčanik se sinkronizira", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 04e85b5b88..a6f11c1ff7 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -639,6 +639,7 @@ "sync_status_starting_sync": "MULAI SINKRONISASI", "sync_status_syncronized": "SUDAH TERSINKRONISASI", "sync_status_syncronizing": "SEDANG SINKRONISASI", + "sync_status_timed_out": "WAKTU HABIS", "sync_status_unsupported": "Node yang tidak didukung", "syncing_wallet_alert_content": "Saldo dan daftar transaksi Anda mungkin belum lengkap sampai tertulis “SYNCHRONIZED” di bagian atas. Klik/ketuk untuk mempelajari lebih lanjut.", "syncing_wallet_alert_title": "Dompet Anda sedang disinkronkan", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 07be979386..7bdba86d14 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "INIZIO SINC", "sync_status_syncronized": "SINCRONIZZATO", "sync_status_syncronizing": "SINCRONIZZAZIONE", + "sync_status_timed_out": "FUORI TEMPO", "sync_status_unsupported": "Nodo non supportato", "syncing_wallet_alert_content": "Il saldo e l'elenco delle transazioni potrebbero non essere completi fino a quando non viene visualizzato \"SYNCHRONIZED\" in alto. Clicca/tocca per saperne di più.", "syncing_wallet_alert_title": "Il tuo portafoglio si sta sincronizzando", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 0b54614018..84c780b4bc 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "同期の開始", "sync_status_syncronized": "同期された", "sync_status_syncronizing": "同期", + "sync_status_timed_out": "タイムアウトしました", "sync_status_unsupported": "サポートされていないノード", "syncing_wallet_alert_content": "上部に「同期済み」と表示されるまで、残高と取引リストが完了していない可能性があります。詳細については、クリック/タップしてください。", "syncing_wallet_alert_title": "ウォレットは同期中です", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index a30498bf20..4adf10d43c 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "동기화 시작", "sync_status_syncronized": "동기화", "sync_status_syncronizing": "동기화", + "sync_status_timed_out": "시간 초과", "sync_status_unsupported": "지원되지 않은 노드", "syncing_wallet_alert_content": "상단에 \"동기화됨\"이라고 표시될 때까지 잔액 및 거래 목록이 완전하지 않을 수 있습니다. 자세히 알아보려면 클릭/탭하세요.", "syncing_wallet_alert_title": "지갑 동기화 중", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index d4e85a1953..92a1784aa3 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "စင့်ခ်လုပ်ခြင်း။", "sync_status_syncronized": "ထပ်တူပြုထားသည်။", "sync_status_syncronizing": "ထပ်တူပြုခြင်း။", + "sync_status_timed_out": "ထွက်အချိန်ကုန်", "sync_status_unsupported": "node မထောက်ပံ့ node ကို", "syncing_wallet_alert_content": "သင်၏လက်ကျန်နှင့် ငွေပေးငွေယူစာရင်းသည် ထိပ်တွင် \"Synchronizeed\" ဟုပြောသည်အထိ မပြီးမြောက်နိုင်ပါ။ ပိုမိုလေ့လာရန် နှိပ်/နှိပ်ပါ။", "syncing_wallet_alert_title": "သင့်ပိုက်ဆံအိတ်ကို စင့်ခ်လုပ်နေပါသည်။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 89c3054418..dd5445fdf0 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "BEGINNEN MET SYNCHRONISEREN", "sync_status_syncronized": "SYNCHRONIZED", "sync_status_syncronizing": "SYNCHRONISEREN", + "sync_status_timed_out": "Uitgeput", "sync_status_unsupported": "Niet ondersteund knooppunt", "syncing_wallet_alert_content": "Uw saldo- en transactielijst is mogelijk pas compleet als er bovenaan 'GESYNCHRONISEERD' staat. Klik/tik voor meer informatie.", "syncing_wallet_alert_title": "Uw portemonnee wordt gesynchroniseerd", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 3f301b84c0..4f5e6c279c 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "ROZPOCZĘCIE SYNCHRONIZACJI", "sync_status_syncronized": "ZSYNCHRONIZOWANO", "sync_status_syncronizing": "SYNCHRONIZACJA", + "sync_status_timed_out": "PRZEKROCZONO LIMIT CZASU", "sync_status_unsupported": "Nieobsługiwany węzeł", "syncing_wallet_alert_content": "Twoje saldo i lista transakcji mogą nie być kompletne, dopóki u góry nie pojawi się napis „SYNCHRONIZOWANY”. Kliknij/stuknij, aby dowiedzieć się więcej.", "syncing_wallet_alert_title": "Twój portfel się synchronizuje", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 1fa193b487..1621b2d963 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "INICIANDO SINCRONIZAÇÃO", "sync_status_syncronized": "SINCRONIZADO", "sync_status_syncronizing": "SINCRONIZANDO", + "sync_status_timed_out": "TEMPO ESGOTADO", "sync_status_unsupported": "Nó não suportado", "syncing_wallet_alert_content": "Seu saldo e lista de transações podem não estar completos até que diga “SYNCHRONIZED” no topo. Clique/toque para saber mais.", "syncing_wallet_alert_title": "Sua carteira está sincronizando", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index b8a58defec..0e8c6ab855 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "НАЧАЛО СИНХРОНИЗАЦИИ", "sync_status_syncronized": "СИНХРОНИЗИРОВАН", "sync_status_syncronizing": "СИНХРОНИЗАЦИЯ", + "sync_status_timed_out": "ВРЕМЯ ВЫШЛО", "sync_status_unsupported": "Неподдерживаемый узел", "syncing_wallet_alert_content": "Ваш баланс и список транзакций могут быть неполными, пока вверху не будет написано «СИНХРОНИЗИРОВАНО». Щелкните/коснитесь, чтобы узнать больше.", "syncing_wallet_alert_title": "Ваш кошелек синхронизируется", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 3cb70a13d7..e5ce652e6f 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "กำลังเริ่มซิงโครไนซ์", "sync_status_syncronized": "ซิงโครไนซ์แล้ว", "sync_status_syncronizing": "กำลังซิงโครไนซ์", + "sync_status_timed_out": "หมดเวลา", "sync_status_unsupported": "โหนดที่ไม่ได้รับการสนับสนุน", "syncing_wallet_alert_content": "รายการยอดเงินและธุรกรรมของคุณอาจไม่สมบูรณ์จนกว่าจะมีข้อความว่า “ซิงโครไนซ์” ที่ด้านบน คลิก/แตะเพื่อเรียนรู้เพิ่มเติม่", "syncing_wallet_alert_title": "กระเป๋าสตางค์ของคุณกำลังซิงค์", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index f7d10bc0c5..f032293301 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "Simula sa pag -sync", "sync_status_syncronized": "Naka -synchronize", "sync_status_syncronizing": "Pag -synchronize", + "sync_status_timed_out": "Nag -time out", "sync_status_unsupported": "Hindi suportadong node", "syncing_wallet_alert_content": "Ang iyong balanse at listahan ng transaksyon ay maaaring hindi kumpleto hanggang sa sabihin nito na \"naka -synchronize\" sa tuktok. Mag -click/tap upang malaman ang higit pa.", "syncing_wallet_alert_title": "Ang iyong pitaka ay nag -sync", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index fcd63448f8..bceec8f61c 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "SENKRONİZE BAŞLATILIYOR", "sync_status_syncronized": "SENKRONİZE EDİLDİ", "sync_status_syncronizing": "SENKRONİZE EDİLİYOR", + "sync_status_timed_out": "ZAMAN AŞIMINA UĞRADI", "sync_status_unsupported": "Desteklenmeyen düğüm", "syncing_wallet_alert_content": "Bakiyeniz ve işlem listeniz, en üstte \"SENKRONİZE EDİLDİ\" yazana kadar tamamlanmamış olabilir. Daha fazla bilgi edinmek için tıklayın/dokunun.", "syncing_wallet_alert_title": "Cüzdanınız senkronize ediliyor", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 07088a2ba0..e5ed5c049d 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "ПОЧАТОК СИНХРОНІЗАЦІЇ", "sync_status_syncronized": "СИНХРОНІЗОВАНИЙ", "sync_status_syncronizing": "СИНХРОНІЗАЦІЯ", + "sync_status_timed_out": "ТАЙМ-АУТ", "sync_status_unsupported": "Непідтримуваний вузол", "syncing_wallet_alert_content": "Ваш баланс та список транзакцій може бути неповним, доки вгорі не буде написано «СИНХРОНІЗОВАНО». Натисніть/торкніться, щоб дізнатися більше.", "syncing_wallet_alert_title": "Ваш гаманець синхронізується", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index b624e272a6..70b72dc949 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -638,6 +638,7 @@ "sync_status_starting_sync": "مطابقت پذیری شروع کر رہا ہے۔", "sync_status_syncronized": "مطابقت پذیر", "sync_status_syncronizing": "مطابقت پذیری", + "sync_status_timed_out": "وقت ختم", "sync_status_unsupported": "غیر تعاون یافتہ نوڈ", "syncing_wallet_alert_content": "آپ کے بیلنس اور لین دین کی فہرست اس وقت تک مکمل نہیں ہو سکتی جب تک کہ یہ سب سے اوپر \"SYNCRONIZED\" نہ کہے۔ مزید جاننے کے لیے کلک/تھپتھپائیں۔", "syncing_wallet_alert_title": "آپ کا بٹوہ مطابقت پذیر ہو رہا ہے۔", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 9612d561cd..a8b4b7ab71 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -637,6 +637,7 @@ "sync_status_starting_sync": "Ń BẸ̀RẸ̀ RẸ́", "sync_status_syncronized": "TI MÚDỌ́GBA", "sync_status_syncronizing": "Ń MÚDỌ́GBA", + "sync_status_timed_out": "Ti akoko jade", "sync_status_unsupported": "Ile-igbimọ ti ko ni atilẹyin", "syncing_wallet_alert_content": "Iwontunws.funfun rẹ ati atokọ idunadura le ma pari titi ti yoo fi sọ “SYNCHRONIZED” ni oke. Tẹ/tẹ ni kia kia lati ni imọ siwaju sii.", "syncing_wallet_alert_title": "Apamọwọ rẹ n muṣiṣẹpọ", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 26cd880b95..10d184cb5a 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -636,6 +636,7 @@ "sync_status_starting_sync": "开始同步", "sync_status_syncronized": "已同步", "sync_status_syncronizing": "正在同步", + "sync_status_timed_out": "时间到", "sync_status_unsupported": "不支持的节点", "syncing_wallet_alert_content": "您的余额和交易列表可能不完整,直到顶部显示“已同步”。单击/点击以了解更多信息。", "syncing_wallet_alert_title": "您的钱包正在同步", From 8ed3e41b2b5821cea7cbb26d9a52f471e9cd6f1b Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 1 Apr 2024 19:19:31 -0300 Subject: [PATCH 17/75] feat: Scan Silent Payments homepage toggle --- cw_bitcoin/lib/electrum_wallet.dart | 35 +++++- lib/bitcoin/cw_bitcoin.dart | 10 ++ .../screens/dashboard/pages/balance_page.dart | 104 +++++++++++++----- .../dashboard/balance_view_model.dart | 17 +++ res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 1 + res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 3 +- res/values/strings_ko.arb | 3 +- res/values/strings_my.arb | 3 +- res/values/strings_nl.arb | 3 +- res/values/strings_pl.arb | 3 +- res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 3 +- res/values/strings_th.arb | 3 +- res/values/strings_tl.arb | 3 +- res/values/strings_tr.arb | 3 +- res/values/strings_uk.arb | 3 +- res/values/strings_ur.arb | 3 +- res/values/strings_yo.arb | 3 +- res/values/strings_zh.arb | 3 +- 30 files changed, 172 insertions(+), 46 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index cf9a39206b..5a1cbda2f0 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -142,6 +142,33 @@ abstract class ElectrumWalletBase bool hasSilentPaymentsScanning = false; @observable bool nodeSupportsSilentPayments = true; + @observable + bool silentPaymentsScanningActive = false; + + @action + void setSilentPaymentsScanning(bool value) { + hasSilentPaymentsScanning = value; + + if (value) { + _setInitialHeight().then((_) { + if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { + _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); + } + }); + } else { + _isolate?.then((runningIsolate) => runningIsolate.kill(priority: Isolate.immediate)); + + if (electrumClient.isConnected) { + syncStatus = SyncedSyncStatus(); + } else { + if (electrumClient.uri != null) { + electrumClient.connectToUri(electrumClient.uri!).then((_) { + startSync(); + }); + } + } + } + } @observable int? currentChainTip; @@ -260,7 +287,7 @@ abstract class ElectrumWalletBase try { syncStatus = AttemptingSyncStatus(); - if (hasSilentPaymentsScanning) { + if (silentPaymentsScanningActive) { try { await _setInitialHeight(); } catch (_) {} @@ -279,7 +306,7 @@ abstract class ElectrumWalletBase Timer.periodic( const Duration(minutes: 1), (timer) async => _feeRates = await electrumClient.feeRates()); - if (!hasSilentPaymentsScanning || walletInfo.restoreHeight == currentChainTip) { + if (!silentPaymentsScanningActive || walletInfo.restoreHeight == currentChainTip) { syncStatus = SyncedSyncStatus(); } } catch (e, stacktrace) { @@ -1335,7 +1362,9 @@ abstract class ElectrumWalletBase Future _setInitialHeight() async { currentChainTip = await electrumClient.getCurrentBlockChainTip(); - if (currentChainTip != null) walletInfo.restoreHeight = currentChainTip!; + if (currentChainTip != null && walletInfo.restoreHeight == 0) { + walletInfo.restoreHeight = currentChainTip!; + } } static BasedUtxoNetwork _getNetwork(bitcoin.NetworkType networkType, CryptoCurrency? currency) { diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index e8329a4f79..8341b76e21 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -263,4 +263,14 @@ class CWBitcoin extends Bitcoin { BitcoinAddressType getOptionToType(ReceivePageOption option) { return (option as BitcoinReceivePageOption).toType(); } + + bool getScanningActive(Object wallet) { + final bitcoinWallet = wallet as ElectrumWallet; + return bitcoinWallet.silentPaymentsScanningActive; + } + + void setScanningActive(Object wallet, bool active) { + final bitcoinWallet = wallet as ElectrumWallet; + bitcoinWallet.setSilentPaymentsScanning(active); + } } diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index bb3ec70dc0..8bda1834a2 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_wi import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart'; import 'package:cake_wallet/src/widgets/introducing_card.dart'; +import 'package:cake_wallet/src/widgets/standard_switch.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; @@ -193,21 +194,29 @@ class CryptoBalanceWidget extends StatelessWidget { itemBuilder: (__, index) { final balance = dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index); - return BalanceRowWidget( - availableBalanceLabel: - '${dashboardViewModel.balanceViewModel.availableBalanceLabel}', - availableBalance: balance.availableBalance, - availableFiatBalance: balance.fiatAvailableBalance, - additionalBalanceLabel: - '${dashboardViewModel.balanceViewModel.additionalBalanceLabel}', - additionalBalance: balance.additionalBalance, - additionalFiatBalance: balance.fiatAdditionalBalance, - frozenBalance: balance.frozenBalance, - frozenFiatBalance: balance.fiatFrozenBalance, - currency: balance.asset, - hasAdditionalBalance: - dashboardViewModel.balanceViewModel.hasAdditionalBalance, - ); + return Observer(builder: (_) { + return BalanceRowWidget( + availableBalanceLabel: + '${dashboardViewModel.balanceViewModel.availableBalanceLabel}', + availableBalance: balance.availableBalance, + availableFiatBalance: balance.fiatAvailableBalance, + additionalBalanceLabel: + '${dashboardViewModel.balanceViewModel.additionalBalanceLabel}', + additionalBalance: balance.additionalBalance, + additionalFiatBalance: balance.fiatAdditionalBalance, + frozenBalance: balance.frozenBalance, + frozenFiatBalance: balance.fiatFrozenBalance, + currency: balance.asset, + hasAdditionalBalance: + dashboardViewModel.balanceViewModel.hasAdditionalBalance, + hasSilentPayments: dashboardViewModel.balanceViewModel.hasSilentPayments, + silentPaymentsScanningActive: + dashboardViewModel.balanceViewModel.silentPaymentsScanningActive, + setSilentPaymentsScanning: () => dashboardViewModel.balanceViewModel + .setSilentPaymentsScanning( + !dashboardViewModel.balanceViewModel.silentPaymentsScanningActive), + ); + }); }, ); }, @@ -231,6 +240,9 @@ class BalanceRowWidget extends StatelessWidget { required this.frozenFiatBalance, required this.currency, required this.hasAdditionalBalance, + required this.hasSilentPayments, + required this.silentPaymentsScanningActive, + required this.setSilentPaymentsScanning, super.key, }); @@ -244,6 +256,9 @@ class BalanceRowWidget extends StatelessWidget { final String frozenFiatBalance; final CryptoCurrency currency; final bool hasAdditionalBalance; + final bool hasSilentPayments; + final bool silentPaymentsScanningActive; + final void Function() setSilentPaymentsScanning; // void _showBalanceDescription(BuildContext context) { // showPopUp( @@ -339,19 +354,19 @@ class BalanceRowWidget extends StatelessWidget { height: 40, width: 40, displayOnError: Container( - height: 30.0, - width: 30.0, - child: Center( - child: Text( - currency.title.substring(0, min(currency.title.length, 2)), - style: TextStyle(fontSize: 11), - ), - ), - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.grey.shade400, - ), + height: 30.0, + width: 30.0, + child: Center( + child: Text( + currency.title.substring(0, min(currency.title.length, 2)), + style: TextStyle(fontSize: 11), ), + ), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey.shade400, + ), + ), ), const SizedBox(height: 10), Text( @@ -410,9 +425,7 @@ class BalanceRowWidget extends StatelessWidget { fontSize: 20, fontFamily: 'Lato', fontWeight: FontWeight.w400, - color: Theme.of(context) - .extension()! - .balanceAmountColor, + color: Theme.of(context).extension()!.balanceAmountColor, height: 1, ), maxLines: 1, @@ -476,6 +489,37 @@ class BalanceRowWidget extends StatelessWidget { ), ], ), + if (hasSilentPayments) ...[ + Padding( + padding: const EdgeInsets.only(right: 8, top: 8), + child: Divider( + color: Theme.of(context).extension()!.labelTextColor, + thickness: 1, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AutoSizeText( + S.of(context).silent_payments_scanning, + style: TextStyle( + fontSize: 14, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.assetTitleColor, + height: 1, + ), + maxLines: 1, + textAlign: TextAlign.center, + ), + Padding( + padding: const EdgeInsets.only(right: 8), + child: StandardSwitch( + value: silentPaymentsScanningActive, onTaped: setSilentPaymentsScanning), + ) + ], + ), + ] ], ), ), diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index eee53516e0..08bdd82522 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/sort_balance_types.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; @@ -45,6 +46,8 @@ abstract class BalanceViewModelBase with Store { : isReversing = false, isShowCard = appStore.wallet!.walletInfo.isShowIntroCakePayCard, wallet = appStore.wallet! { + silentPaymentsScanningActive = hasSilentPayments && bitcoin!.getScanningActive(wallet); + reaction((_) => appStore.wallet, _onWalletChange); } @@ -60,6 +63,20 @@ abstract class BalanceViewModelBase with Store { @observable WalletBase, TransactionInfo> wallet; + @computed + bool get hasSilentPayments => wallet.type == WalletType.bitcoin; + + @observable + bool silentPaymentsScanningActive = false; + + @action + void setSilentPaymentsScanning(bool active) { + if (hasSilentPayments) { + bitcoin!.setScanningActive(wallet, active); + silentPaymentsScanningActive = active; + } + } + @computed double get price { final price = fiatConvertationStore.prices[appStore.wallet!.currency]; diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index cc284affe9..a66f85da4f 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -610,6 +610,7 @@ "sign_up": "اشتراك", "signTransaction": " ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ", "signup_for_card_accept_terms": "قم بالتسجيل للحصول على البطاقة وقبول الشروط.", + "silent_payments_scanning": "المدفوعات الصامتة المسح الضوئي", "slidable": "قابل للانزلاق", "sort_by": "ترتيب حسب", "spend_key_private": "مفتاح الإنفاق (خاص)", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index a0174385d9..cc48a56e98 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -610,6 +610,7 @@ "sign_up": "Регистрация", "signTransaction": "Подпишете транзакция", "signup_for_card_accept_terms": "Регистрайте се за картата и приемете условията.", + "silent_payments_scanning": "Безшумни плащания за сканиране", "slidable": "Плъзгащ се", "sort_by": "Сортирай по", "spend_key_private": "Spend key (таен)", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index eb56a8070a..f3076cf6c5 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -610,6 +610,7 @@ "sign_up": "Registrovat se", "signTransaction": "Podepsat transakci", "signup_for_card_accept_terms": "Zaregistrujte se pro kartu a souhlaste s podmínkami.", + "silent_payments_scanning": "Skenování tichých plateb", "slidable": "Posuvné", "sort_by": "Seřazeno podle", "spend_key_private": "Klíč pro platby (soukromý)", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index e8b929a52e..cf4cbb7fa2 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -611,6 +611,7 @@ "sign_up": "Anmelden", "signTransaction": "Transaktion unterzeichnen", "signup_for_card_accept_terms": "Melden Sie sich für die Karte an und akzeptieren Sie die Bedingungen.", + "silent_payments_scanning": "Stille Zahlungen scannen", "slidable": "Verschiebbar", "sort_by": "Sortiere nach", "spend_key_private": "Spend Key (geheim)", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 65b3a4378d..d31435b484 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -610,6 +610,7 @@ "sign_up": "Sign Up", "signTransaction": "Sign Transaction", "signup_for_card_accept_terms": "Sign up for the card and accept the terms.", + "silent_payments_scanning": "Silent Payments Scanning", "slidable": "Slidable", "sort_by": "Sort by", "spend_key_private": "Spend key (private)", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index d85682524f..6aa1cfbd81 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -611,6 +611,7 @@ "sign_up": "Registrarse", "signTransaction": "Firmar transacción", "signup_for_card_accept_terms": "Regístrese para obtener la tarjeta y acepte los términos.", + "silent_payments_scanning": "Escaneo de pagos silenciosos", "slidable": "deslizable", "sort_by": "Ordenar por", "spend_key_private": "Spend clave (privado)", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index e24bdcb675..7b7ce56df7 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -610,6 +610,7 @@ "sign_up": "S'inscrire", "signTransaction": "Signer une transaction", "signup_for_card_accept_terms": "Inscrivez-vous pour la carte et acceptez les conditions.", + "silent_payments_scanning": "Payments silencieux SCANNING", "slidable": "Glissable", "sort_by": "Trier par", "spend_key_private": "Clef de dépense (spend key) (privée)", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index bb0787defc..6c3de1e037 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -612,6 +612,7 @@ "sign_up": "Shiga", "signTransaction": "Sa hannu Ma'amala", "signup_for_card_accept_terms": "Yi rajista don katin kuma karɓi sharuɗɗan.", + "silent_payments_scanning": "Silent biya scanning", "slidable": "Mai iya zamewa", "sort_by": "Kasa", "spend_key_private": "makullin biya (maɓallin kalmar sirri)", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index ed71470f26..82445dfa3f 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -612,6 +612,7 @@ "sign_up": "साइन अप करें", "signTransaction": "लेन-देन पर हस्ताक्षर करें", "signup_for_card_accept_terms": "कार्ड के लिए साइन अप करें और शर्तें स्वीकार करें।", + "silent_payments_scanning": "मूक भुगतान स्कैनिंग", "slidable": "फिसलने लायक", "sort_by": "इसके अनुसार क्रमबद्ध करें", "spend_key_private": "खर्च करना (निजी)", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 84bde90ec8..4c64a9a349 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -610,6 +610,7 @@ "sign_up": "Prijavite se", "signTransaction": "Potpišite transakciju", "signup_for_card_accept_terms": "Prijavite se za karticu i prihvatite uvjete.", + "silent_payments_scanning": "Skeniranje tihih plaćanja", "slidable": "Klizna", "sort_by": "Poredaj po", "spend_key_private": "Spend key (privatni)", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index b5a4391685..90a92a8f24 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -613,6 +613,7 @@ "sign_up": "Daftar", "signTransaction": "Tandatangani Transaksi", "signup_for_card_accept_terms": "Daftar untuk kartu dan terima syarat dan ketentuan.", + "silent_payments_scanning": "Pemindaian pembayaran diam", "slidable": "Dapat digeser", "sort_by": "Sortir dengan", "spend_key_private": "Kunci pengeluaran (privat)", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 83f5843e22..9795950484 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -612,6 +612,7 @@ "sign_up": "Registrati", "signTransaction": "Firma la transazione", "signup_for_card_accept_terms": "Registrati per la carta e accetta i termini.", + "silent_payments_scanning": "Scansione di pagamenti silenziosi", "slidable": "Scorrevole", "sort_by": "Ordina per", "spend_key_private": "Chiave di spesa (privata)", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 0c2bafb77d..b3a3cefe0f 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -611,6 +611,7 @@ "sign_up": "サインアップ", "signTransaction": "トランザクションに署名する", "signup_for_card_accept_terms": "カードにサインアップして、利用規約に同意してください。", + "silent_payments_scanning": "サイレントペイメントスキャン", "slidable": "スライド可能", "sort_by": "並び替え", "spend_key_private": "キーを使う (プライベート)", @@ -804,4 +805,4 @@ "you_will_get": "に変換", "you_will_send": "から変換", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index d8f39277e3..b2908056f7 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -611,6 +611,7 @@ "sign_up": "가입", "signTransaction": "거래 서명", "signup_for_card_accept_terms": "카드에 가입하고 약관에 동의합니다.", + "silent_payments_scanning": "조용한 지불 스캔", "slidable": "슬라이딩 가능", "sort_by": "정렬 기준", "spend_key_private": "지출 키 (은밀한)", @@ -805,4 +806,4 @@ "you_will_send": "다음에서 변환", "YY": "YY", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 286f8ffd9d..1453da4cc8 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -610,6 +610,7 @@ "sign_up": "ဆိုင်းအပ်", "signTransaction": "ငွေလွှဲဝင်ပါ။", "signup_for_card_accept_terms": "ကတ်အတွက် စာရင်းသွင်းပြီး စည်းကမ်းချက်များကို လက်ခံပါ။", + "silent_payments_scanning": "အသံတိတ်ငွေပေးချေမှု scanning", "slidable": "လျှောချနိုင်သည်။", "sort_by": "အလိုက်စဥ်သည်", "spend_key_private": "သော့သုံးရန် (သီးသန့်)", @@ -803,4 +804,4 @@ "you_will_get": "သို့ပြောင်းပါ။", "you_will_send": "မှပြောင်းပါ။", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 57f9943e3c..b82019e6bd 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -610,6 +610,7 @@ "sign_up": "Aanmelden", "signTransaction": "Transactie ondertekenen", "signup_for_card_accept_terms": "Meld je aan voor de kaart en accepteer de voorwaarden.", + "silent_payments_scanning": "Stille betalingen scannen", "slidable": "Verschuifbaar", "sort_by": "Sorteer op", "spend_key_private": "Sleutel uitgeven (privaat)", @@ -804,4 +805,4 @@ "you_will_get": "Converteren naar", "you_will_send": "Converteren van", "yy": "JJ" -} +} \ No newline at end of file diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 703f85c0fe..c1e677284f 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -610,6 +610,7 @@ "sign_up": "Zarejestruj się", "signTransaction": "Podpisz transakcję", "signup_for_card_accept_terms": "Zarejestruj się, aby otrzymać kartę i zaakceptuj warunki.", + "silent_payments_scanning": "Skanowanie cichych płatności", "slidable": "Przesuwne", "sort_by": "Sortuj według", "spend_key_private": "Klucz prywatny", @@ -803,4 +804,4 @@ "you_will_get": "Konwertuj na", "you_will_send": "Konwertuj z", "yy": "RR" -} +} \ No newline at end of file diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index cddce9a2a0..77e105b04e 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -612,6 +612,7 @@ "sign_up": "Inscrever-se", "signTransaction": "Assinar transação", "signup_for_card_accept_terms": "Cadastre-se no cartão e aceite os termos.", + "silent_payments_scanning": "Scanear Pagamentos Silenciosos", "slidable": "Deslizável", "sort_by": "Ordenar por", "spend_key_private": "Chave de gastos (privada)", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 14f3e27e89..c793ef6b12 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -611,6 +611,7 @@ "sign_up": "Зарегистрироваться", "signTransaction": "Подписать транзакцию", "signup_for_card_accept_terms": "Подпишитесь на карту и примите условия.", + "silent_payments_scanning": "Сканирование безмолвных платежей", "slidable": "Скользящий", "sort_by": "Сортировать по", "spend_key_private": "Приватный ключ траты", @@ -804,4 +805,4 @@ "you_will_get": "Конвертировать в", "you_will_send": "Конвертировать из", "yy": "ГГ" -} +} \ No newline at end of file diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index d369683d0c..1839cc930d 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -610,6 +610,7 @@ "sign_up": "สมัครสมาชิก", "signTransaction": "ลงนามในการทำธุรกรรม", "signup_for_card_accept_terms": "ลงทะเบียนสำหรับบัตรและยอมรับเงื่อนไข", + "silent_payments_scanning": "การสแกนการชำระเงินแบบเงียบ", "slidable": "เลื่อนได้", "sort_by": "เรียงตาม", "spend_key_private": "คีย์จ่าย (ส่วนตัว)", @@ -803,4 +804,4 @@ "you_will_get": "แปลงเป็น", "you_will_send": "แปลงจาก", "yy": "ปี" -} +} \ No newline at end of file diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index c78955a9f4..4d3ef91c04 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -610,6 +610,7 @@ "sign_up": "Mag -sign up", "signTransaction": "Mag-sign Transaksyon", "signup_for_card_accept_terms": "Mag -sign up para sa card at tanggapin ang mga termino.", + "silent_payments_scanning": "Tahimik na pag -scan ng mga pagbabayad", "slidable": "Slidable", "sort_by": "Pag -uri -uriin sa pamamagitan ng", "spend_key_private": "Gumastos ng susi (pribado)", @@ -803,4 +804,4 @@ "you_will_get": "Mag -convert sa", "you_will_send": "I -convert mula sa", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index add6cd4517..55fbf6036b 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -610,6 +610,7 @@ "sign_up": "Kaydol", "signTransaction": "İşlem İmzala", "signup_for_card_accept_terms": "Kart için kaydol ve koşulları kabul et.", + "silent_payments_scanning": "Sessiz Ödemeler Taraması", "slidable": "kaydırılabilir", "sort_by": "Göre sırala", "spend_key_private": "Harcama anahtarı (özel)", @@ -803,4 +804,4 @@ "you_will_get": "Biçimine dönüştür:", "you_will_send": "Biçiminden dönüştür:", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 8a4ca8e387..d48c5eb3db 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -611,6 +611,7 @@ "sign_up": "Зареєструватися", "signTransaction": "Підписати транзакцію", "signup_for_card_accept_terms": "Зареєструйтеся на картку та прийміть умови.", + "silent_payments_scanning": "Мовчазні платежі сканування", "slidable": "Розсувний", "sort_by": "Сортувати за", "spend_key_private": "Приватний ключ витрати", @@ -804,4 +805,4 @@ "you_will_get": "Конвертувати в", "you_will_send": "Конвертувати з", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index deaa83b2db..b5255bc150 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -612,6 +612,7 @@ "sign_up": "سائن اپ", "signTransaction": "۔ﮟﯾﺮﮐ ﻂﺨﺘﺳﺩ ﺮﭘ ﻦﯾﺩ ﻦﯿﻟ", "signup_for_card_accept_terms": "کارڈ کے لیے سائن اپ کریں اور شرائط کو قبول کریں۔", + "silent_payments_scanning": "خاموش ادائیگی اسکیننگ", "slidable": "سلائیڈ ایبل", "sort_by": "ترتیب دیں", "spend_key_private": "خرچ کی کلید (نجی)", @@ -805,4 +806,4 @@ "you_will_get": "میں تبدیل کریں۔", "you_will_send": "سے تبدیل کریں۔", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 01e946df18..3435f9ca13 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -611,6 +611,7 @@ "sign_up": "Forúkọ sílẹ̀", "signTransaction": "Wole Idunadura", "signup_for_card_accept_terms": "Ẹ f'orúkọ sílẹ̀ láti gba káàdì àti àjọrò.", + "silent_payments_scanning": "Awọn sisanwo ipalọlọ", "slidable": "Slidable", "sort_by": "Sa pelu", "spend_key_private": "Kọ́kọ́rọ́ sísan (àdáni)", @@ -804,4 +805,4 @@ "you_will_get": "Ṣe pàṣípààrọ̀ sí", "you_will_send": "Ṣe pàṣípààrọ̀ láti", "yy": "Ọd" -} +} \ No newline at end of file diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index c48bc60dad..22e74ce5b5 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -610,6 +610,7 @@ "sign_up": "注册", "signTransaction": "签署交易", "signup_for_card_accept_terms": "注册卡并接受条款。", + "silent_payments_scanning": "无声付款扫描", "slidable": "可滑动", "sort_by": "排序方式", "spend_key_private": "Spend 密钥 (私钥)", @@ -803,4 +804,4 @@ "you_will_get": "转换到", "you_will_send": "转换自", "yy": "YY" -} +} \ No newline at end of file From d6de17c7102080ec1dd86f15c624a0e2bc92654f Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 1 Apr 2024 19:36:18 -0300 Subject: [PATCH 18/75] chore: build configure --- tool/configure.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tool/configure.dart b/tool/configure.dart index 62179263dd..96891247c0 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -155,6 +155,8 @@ abstract class Bitcoin { bool isBitcoinReceivePageOption(ReceivePageOption option); BitcoinAddressType getOptionToType(ReceivePageOption option); bool hasTaprootInput(PendingTransaction pendingTransaction); + bool getScanningActive(Object wallet); + void setScanningActive(Object wallet, bool active); } """; From 226c47c6e37dcefc42a1cee26bc5e5786fb29066 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 5 Apr 2024 17:27:42 -0300 Subject: [PATCH 19/75] feat: generic fixes, testnet UI improvements, useSSL on bitcoin nodes --- assets/images/tbtc.png | Bin 0 -> 3983 bytes cw_bitcoin/lib/bitcoin_wallet.dart | 28 +- cw_bitcoin/lib/electrum.dart | 14 +- cw_bitcoin/lib/electrum_wallet.dart | 300 ++++++++++-------- cw_bitcoin/lib/electrum_wallet_snapshot.dart | 2 +- cw_core/lib/crypto_currency.dart | 2 + cw_core/lib/currency_for_wallet_type.dart | 8 +- cw_core/lib/wallet_base.dart | 2 +- cw_core/lib/wallet_type.dart | 5 +- lib/bitcoin/cw_bitcoin.dart | 7 + .../screens/dashboard/pages/balance_page.dart | 80 +++-- lib/src/screens/nodes/widgets/node_form.dart | 111 +++---- lib/src/screens/receive/receive_page.dart | 34 +- lib/src/screens/rescan/rescan_page.dart | 20 +- lib/src/screens/send/widgets/send_card.dart | 3 +- .../settings/connection_sync_page.dart | 4 +- .../screens/wallet_list/wallet_list_page.dart | 11 +- lib/src/widgets/address_text_field.dart | 6 +- lib/src/widgets/blockchain_height_widget.dart | 29 +- .../dashboard/balance_view_model.dart | 13 - .../dashboard/dashboard_view_model.dart | 29 +- lib/view_model/rescan_view_model.dart | 4 + .../wallet_address_list_view_model.dart | 28 +- .../wallet_list/wallet_list_item.dart | 2 + .../wallet_list/wallet_list_view_model.dart | 1 + res/values/strings_ar.arb | 4 + res/values/strings_bg.arb | 4 + res/values/strings_cs.arb | 4 + res/values/strings_de.arb | 4 + res/values/strings_en.arb | 4 + res/values/strings_es.arb | 4 + res/values/strings_fr.arb | 4 + res/values/strings_ha.arb | 4 + res/values/strings_hi.arb | 4 + res/values/strings_hr.arb | 4 + res/values/strings_id.arb | 4 + res/values/strings_it.arb | 4 + res/values/strings_ja.arb | 4 + res/values/strings_ko.arb | 4 + res/values/strings_my.arb | 4 + res/values/strings_nl.arb | 4 + res/values/strings_pl.arb | 4 + res/values/strings_pt.arb | 8 +- res/values/strings_ru.arb | 4 + res/values/strings_th.arb | 4 + res/values/strings_tl.arb | 4 + res/values/strings_tr.arb | 4 + res/values/strings_uk.arb | 4 + res/values/strings_ur.arb | 4 + res/values/strings_yo.arb | 4 + res/values/strings_zh.arb | 4 + tool/configure.dart | 8 +- 52 files changed, 533 insertions(+), 326 deletions(-) create mode 100644 assets/images/tbtc.png diff --git a/assets/images/tbtc.png b/assets/images/tbtc.png new file mode 100644 index 0000000000000000000000000000000000000000..bd4323edfce97bfadd2a2cf764af04af74b368b8 GIT binary patch literal 3983 zcmV;A4{-2_P)EX>4Tx04R}tkv&MmKpe$i)0V1K9Lyl%kfAzR1Ql_tRVYG*P%E_RU_SZkM+H^bh|{W*Vj)BONgw~P>leu-ldA%S z91EyHgXH?b{@{1FR&i?5ONu6e@QdSoi~^Bepw)1k?_a(JTfSK%#82Lj)CMz?or~d0+n^2Z33Ec@HGHf5Ck3eeb^C-2LA72Ka?6N@OB1 zk(jv6g$U^amawx45J~|*Lf}Cu+yK~sKm-ag06BmzfXV@;iGtP=f?C2d#oAs$QC{9& z`Ogy2?c2B8+)=7yLCNC)T>_9$3-H1M^cn$kl7Ky-kat|l&(GIRWnv_0cfwApmjrLx@8OjuRnLbJEhRN zNa*(M+imXL?RyUdFW=V@Ii*sukiI09O8dqQ&l0>IKYfYX#df-4O z?|?p^v}LA%p{D`(PqOT>QVYTIqlfl2@CN9*q|KoMh7?d>+9b`$>xe)blP#4tat>%> zN{TPlG8X~(O!7KMgFuUztgN*9k(?wLvCRZ7@D*PYLa^rPr(kVu4Y^#7K8=P?K&Ma! zkIq>Xm3O7EP-SrmO-V_yQfdX~0J@m3JDTR{fxo`}Mt|jlWl`VI2x;aKwElR5S4Y+4 z?Y`37%*yOIRH-5V;1f&$2oAcJ`ZElJ8PomH)pf5z7coB!YvWfVZ0;P0Y;3qv-?A?VIlM*>9c+gi8 zk~gom2%w3Hsa}-QJ>%gOf&&AF{9jR7HB5rmW++KdNKKtOW4HxCN%N9-ffhb;@-~jDm$`~JgM$ng?u9}j0{v&g$J=WtqYQ)Cm?iL<<_SUw zf`;4Isv7V_!QRd78%7j|rlh3UDz!{K05?l>V{L7Pv^~2Z6beVZM|XD*?CtG9$UXL@ znMZK(>u-6=Owpq15(nnz|4nIDrW86q>LmcHx!^^sWpU>Gm#D3)M^8@=oSYnCD;AGv z4;vfnAz%VPLt``Q8=IivC!2D#veDF*$_i!X07}9lB69#pxZ#1wyqwOQjWBqc+87jSWs5{eUnkAL>YDy-vPWvSMtPTyXr4o zVD(C}P%4e5n|{7NShqHw zE7o0n8ry$mP~{2es`1F#LxK$hT7CVxLC5yLf^+!O+wY*W^N!K|mn?n=uC6W|={LV?_!)t zK&-^!x++gFYg_;|BB8Z)4Q6LPQ=FWPt}7Kv?qX@eMCkxZ2txS=)7g0n+^4!3v7gt? z)49+PczPHg@N2(yi_7d}z*GtceK=!0q;q&qKA0X_v=DZ7wzyVNiJICvbaZs;6j~x9 z!jbs=T9esW`43mQES7lb0t!r>Na_v%t}YV9#w%#waP*>e{iot^0J>4BZCEv9*T zm<;X#;QJyO+HZ0wp&;J+0CnXX(BPnv4K&-^+ri#`WO+wNM<;TQ9^>>r$e;*H;3-B81gWm(8d%OEQ)!-9zU z*p#^5sFPYAw-k4}y71Yl(_G8wZQZ~gxWkup1_t;WwTqR5`tOFQsJI00yuZt&!+Gk7 z$02cc=2+!vn+~9qpmieH&wfD7YY8{Ev?A}H##P$d+6s#vh~!x1=?z^8Woa#6fEu%Z zw{Cafc1MTVdleR!m~=h?GdWHI33EfohEj%HhlTrD)BSw$n{BD6tf@hD^>s8fHX$T9 z2>wQH&>VFiOd5>_YIPsPV#Bk;cD9^1A>n7e9o^i20)`n0R`)!%8SbcTW%0YW>e)357zeTDbzN zRxUT1aRI<5pPfWo`%SJ~(zU}G(!c9ZHz`|SsY&HWq8}dBc|P7=@bUJ-@?S3BvT}D3@bne1eP^;D4;i+XlGYF_P?LFJaSSx>ezsHzJ-rbGM zg~hmBSPY=QIv*MmjM=k7FgtV>oToT(9-gUsdr@%iJeQY{JJ}1r8vMbKSVJb0sl&n} z=Kz>v@taer(A3<5B3UU;ojwB(4|n)@d2tG8MplklUDGiXMRE?N?LR*FGe%F;e}s?V ze!8#kkpHjza1|#$KZUybhA|I#QA+WzDCLp`H;zeICX>k5N&C^3?eP8+^h(tE<^l*o>^AtGRJ3wqJhEkz~s<#*Q4UMR( zu0cgrHD@D?!w~%d(9JFNR_Sd8^q^8p{s_Q@4?sIR@8IU3Ht+CWQ+Ia{E*BQ@1e96S zCSZH>^eyPEp-H7u1tIu=yK2)=z$%7iM+ao9_0L8M>0dV72KZ#WcT&;IF%<2SvptL+se9fZM@>!b zgaDHOT||RaGmbM&2^&*4fZ#CSH{t3cfk-4WIs3_zsy~~Xnf8&Xbmus**KYdU{+R^N+Vz& z1!&dJ2+bNHD_m{NtB=Zt4yNZfYz`DK8SHl*}{|J-`0YSihB9!i%D3T3?VrO<{+Hp&7EZ~CMGFjo(;D8zXgd)3U08s$!CkT+YD9Bqv ztt#Qj;f!*QiYVMt{MN1ZA~m}alx_n^&^Wp83d&#)>Ajnslas^gN2oc z2jEF?{L>W#=wgBYFf7TUubgC{{i9m`@q==T66#a002ovPDHLkV1f^$cqsq? literal 0 HcmV?d00001 diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 3be7a8b679..e0218eff55 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -33,19 +33,21 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { List? initialSilentAddresses, int initialSilentAddressIndex = 0, }) : super( - mnemonic: mnemonic, - password: password, - walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfo, - networkType: networkParam == null - ? bitcoin.bitcoin - : networkParam == BitcoinNetwork.mainnet - ? bitcoin.bitcoin - : bitcoin.testnet, - initialAddresses: initialAddresses, - initialBalance: initialBalance, - seedBytes: seedBytes, - currency: CryptoCurrency.btc) { + mnemonic: mnemonic, + password: password, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfo, + networkType: networkParam == null + ? bitcoin.bitcoin + : networkParam == BitcoinNetwork.mainnet + ? bitcoin.bitcoin + : bitcoin.testnet, + initialAddresses: initialAddresses, + initialBalance: initialBalance, + seedBytes: seedBytes, + currency: + networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc, + ) { walletAddresses = BitcoinWalletAddresses( walletInfo, initialAddresses: initialAddresses, diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index bea6c49574..5f6363c755 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -50,18 +50,24 @@ class ElectrumClient { String unterminatedString; Uri? uri; + bool? useSSL; - Future connectToUri(Uri uri) async { + Future connectToUri(Uri uri, {bool? useSSL}) async { this.uri = uri; - await connect(host: uri.host, port: uri.port); + this.useSSL = useSSL; + await connect(host: uri.host, port: uri.port, useSSL: useSSL); } - Future connect({required String host, required int port}) async { + Future connect({required String host, required int port, bool? useSSL}) async { try { await socket?.close(); } catch (_) {} - socket = await SecureSocket.connect(host, port, timeout: connectionTimeout); + if (useSSL == true) { + socket = await SecureSocket.connect(host, port, timeout: connectionTimeout); + } else { + socket = await Socket.connect(host, port, timeout: connectionTimeout); + } _setIsConnected(true); socket!.listen((Uint8List event) { diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 5a1cbda2f0..4cd2d9f226 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -44,8 +44,6 @@ import 'package:http/http.dart' as http; part 'electrum_wallet.g.dart'; -const SCANNING_BLOCK_COUNT = 50; - class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet; abstract class ElectrumWalletBase @@ -89,6 +87,10 @@ abstract class ElectrumWalletBase this.electrumClient = electrumClient ?? ElectrumClient(); this.walletInfo = walletInfo; transactionHistory = ElectrumTransactionHistory(walletInfo: walletInfo, password: password); + + reaction((_) => syncStatus, (SyncStatus syncStatus) { + silentPaymentsScanningActive = syncStatus is SyncingSyncStatus; + }); } static bitcoin.HDWallet bitcoinCashHDWallet(Uint8List seedBytes) => @@ -146,10 +148,10 @@ abstract class ElectrumWalletBase bool silentPaymentsScanningActive = false; @action - void setSilentPaymentsScanning(bool value) { - hasSilentPaymentsScanning = value; + void setSilentPaymentsScanning(bool active) { + silentPaymentsScanningActive = active; - if (value) { + if (active) { _setInitialHeight().then((_) { if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); @@ -218,16 +220,11 @@ abstract class ElectrumWalletBase chainTip: currentChainTip, electrumClient: ElectrumClient(), transactionHistoryIds: transactionHistory.transactions.keys.toList(), - node: electrumClient.uri.toString(), + node: node!, labels: walletAddresses.labels, )); await for (var message in receivePort) { - if (message is bool && message == false) { - nodeSupportsSilentPayments = message; - syncStatus = TimedOutSyncStatus(); - } - if (message is Map) { for (final map in message.entries) { final txid = map.key; @@ -274,6 +271,10 @@ abstract class ElectrumWalletBase // check if is a SyncStatus type since "is SyncStatus" doesn't work here if (message is SyncResponse) { + if (message.syncStatus is UnsupportedSyncStatus) { + nodeSupportsSilentPayments = false; + } + syncStatus = message.syncStatus; walletInfo.restoreHeight = message.height; await walletInfo.save(); @@ -316,11 +317,15 @@ abstract class ElectrumWalletBase } } + Node? node; + @action Future _electrumConnect(Node node, {bool? attemptedReconnect}) async { + this.node = node; + try { syncStatus = ConnectingSyncStatus(); - await electrumClient.connectToUri(node.uri); + await electrumClient.connectToUri(node.uri, useSSL: node.useSSL); electrumClient.onConnectionStatusChange = (bool isConnected) async { if (!isConnected) { syncStatus = LostConnectionSyncStatus(); @@ -346,6 +351,52 @@ abstract class ElectrumWalletBase bool _isBelowDust(int amount) => amount <= _getDustAmount() && network != BitcoinNetwork.testnet; + void _createSilentPayments( + List outputs, + List inputPubKeys, + List vinOutpoints, + List inputPrivKeyInfos, + ) { + List silentPaymentDestinations = []; + + for (final out in outputs) { + final address = out.address; + final amount = out.value; + + if (address is SilentPaymentAddress) { + silentPaymentDestinations.add( + SilentPaymentDestination.fromAddress(address.toAddress(network), amount.toInt()), + ); + } + } + + if (silentPaymentDestinations.isNotEmpty) { + final spb = SilentPaymentBuilder(pubkeys: inputPubKeys, outpoints: vinOutpoints); + final sendingOutputs = spb.createOutputs(inputPrivKeyInfos, silentPaymentDestinations); + + final outputsAdded = []; + + for (var i = 0; i < outputs.length; i++) { + final out = outputs[i]; + + final silentOutputs = sendingOutputs[out.address.toAddress(network)]; + if (silentOutputs != null) { + final silentOutput = + silentOutputs.firstWhereOrNull((element) => !outputsAdded.contains(element)); + + if (silentOutput != null) { + outputs[i] = BitcoinOutput( + address: silentOutput.address, + value: BigInt.from(silentOutput.amount), + ); + + outputsAdded.add(silentOutput); + } + } + } + } + } + Future estimateSendAllTx( List outputs, int feeRate, { @@ -416,6 +467,10 @@ abstract class ElectrumWalletBase throw BitcoinTransactionNoInputsException(); } + if (hasSilentPayment == true) { + _createSilentPayments(outputs, inputPubKeys, vinOutpoints, inputPrivKeyInfos); + } + int estimatedSize; if (network is BitcoinCashNetwork) { estimatedSize = ForkedTransactionBuilder.estimateTransactionSize( @@ -433,45 +488,6 @@ abstract class ElectrumWalletBase ); } - if (hasSilentPayment == true) { - List silentPaymentDestinations = []; - - for (final out in outputs) { - final address = out.address; - final amount = out.value; - - if (address is SilentPaymentAddress) { - final silentPaymentDestination = - SilentPaymentDestination.fromAddress(address.toAddress(network), amount.toInt()); - silentPaymentDestinations.add(silentPaymentDestination); - } - } - - final spb = SilentPaymentBuilder(pubkeys: inputPubKeys, outpoints: vinOutpoints); - final sendingOutputs = spb.createOutputs(inputPrivKeyInfos, silentPaymentDestinations); - - var outputsAdded = []; - - for (var i = 0; i < outputs.length; i++) { - final out = outputs[i]; - - final silentOutputs = sendingOutputs[out.address.toAddress(network)]; - if (silentOutputs != null) { - final silentOutput = - silentOutputs.firstWhereOrNull((element) => !outputsAdded.contains(element)); - - if (silentOutput != null) { - outputs[i] = BitcoinOutput( - address: silentOutput.address, - value: BigInt.from(silentOutput.amount), - ); - - outputsAdded.add(silentOutput); - } - } - } - } - int fee = feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize); if (fee == 0) { @@ -518,7 +534,9 @@ abstract class ElectrumWalletBase bool hasSilentPayment = false, }) async { final utxos = []; + List vinOutpoints = []; List inputPrivKeyInfos = []; + List inputPubKeys = []; int allInputsAmount = 0; bool spendsSilentPayment = false; @@ -553,6 +571,10 @@ abstract class ElectrumWalletBase ); } + inputPrivKeyInfos.add(ECPrivateInfo(privkey, address.type == SegwitAddresType.p2tr)); + inputPubKeys.add(privkey.getPublic()); + vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); + utxos.add( UtxoWithAddress( utxo: BitcoinUtxo( @@ -579,6 +601,10 @@ abstract class ElectrumWalletBase throw BitcoinTransactionNoInputsException(); } + if (hasSilentPayment == true) { + _createSilentPayments(outputs, inputPubKeys, vinOutpoints, inputPrivKeyInfos); + } + final spendingAllCoins = sendingCoins.length == utxos.length; // How much is being spent - how much is being sent @@ -952,8 +978,10 @@ abstract class ElectrumWalletBase await transactionHistory.changePassword(password); } + @action @override Future rescan({required int height, int? chainTip, ScanData? scanData}) async { + silentPaymentsScanningActive = true; _setListeners(height); } @@ -1388,7 +1416,7 @@ class ScanData { final SendPort sendPort; final SilentPaymentOwner silentAddress; final int height; - final String node; + final Node node; final BasedUtxoNetwork network; final int chainTip; final ElectrumClient electrumClient; @@ -1436,7 +1464,7 @@ Future startRefresh(ScanData scanData) async { final electrumClient = scanData.electrumClient; if (!electrumClient.isConnected) { final node = scanData.node; - await electrumClient.connectToUri(Uri.parse(node)); + await electrumClient.connectToUri(node.uri, useSSL: node.useSSL); } return electrumClient; } @@ -1489,21 +1517,31 @@ Future startRefresh(ScanData scanData) async { try { final electrumClient = await getElectrumConnection(); + final scanningBlockCount = scanData.network == BitcoinNetwork.testnet ? 50 : 10; + Map? tweaks; try { - tweaks = await electrumClient.getTweaks(height: syncHeight, count: SCANNING_BLOCK_COUNT); + tweaks = await electrumClient.getTweaks(height: syncHeight, count: scanningBlockCount); } catch (e) { if (e is RequestFailedTimeoutException) { - return scanData.sendPort.send(false); + return scanData.sendPort.send( + SyncResponse(syncHeight, TimedOutSyncStatus()), + ); } } if (tweaks == null) { - scanData.sendPort.send(SyncResponse(syncHeight, - SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); + return scanData.sendPort.send( + SyncResponse(syncHeight, UnsupportedSyncStatus()), + ); + } + + if (tweaks.isEmpty) { + syncHeight += scanningBlockCount; + continue; } - final blockHeights = tweaks!.keys; + final blockHeights = tweaks.keys; for (var i = 0; i < blockHeights.length; i++) { try { final blockHeight = blockHeights.elementAt(i).toString(); @@ -1515,81 +1553,83 @@ Future startRefresh(ScanData scanData) async { final outputPubkeys = (details["output_pubkeys"] as Map); final tweak = details["tweak"].toString(); - final spb = SilentPaymentBuilder(receiverTweak: tweak); - final addToWallet = spb.scanOutputs( - scanData.silentAddress.b_scan, - scanData.silentAddress.B_spend, - outputPubkeys.values - .map((o) => getScriptFromOutput( - o["pubkey"].toString(), int.parse(o["amount"].toString()))) - .toList(), - precomputedLabels: scanData.labels, - ); - - if (addToWallet.isEmpty) { - // no results tx, continue to next tx - continue; - } - - addToWallet.forEach((key, value) async { - final t_k = value.tweak; - - final addressRecord = BitcoinSilentPaymentAddressRecord( - value.output.address.toAddress(scanData.network), - index: 0, - isHidden: false, - isUsed: true, - network: scanData.network, - silentPaymentTweak: t_k, - type: SegwitAddresType.p2tr, + try { + final spb = SilentPaymentBuilder(receiverTweak: tweak); + final addToWallet = spb.scanOutputs( + scanData.silentAddress.b_scan, + scanData.silentAddress.B_spend, + outputPubkeys.values + .map((o) => getScriptFromOutput( + o["pubkey"].toString(), int.parse(o["amount"].toString()))) + .toList(), + precomputedLabels: scanData.labels, ); - int? amount; - int? pos; - outputPubkeys.entries.firstWhere((k) { - final matches = k.value["pubkey"] == key; - if (matches) { - amount = int.parse(k.value["amount"].toString()); - pos = int.parse(k.key.toString()); - return true; - } - return false; - }); - - final json = { - 'address_record': addressRecord.toJSON(), - 'tx_hash': txid, - 'value': amount!, - 'tx_pos': pos!, - 'silent_payment_tweak': t_k, - }; - - final tx = BitcoinUnspent.fromJSON(addressRecord, json); - - final txInfo = ElectrumTransactionInfo( - WalletType.bitcoin, - id: tx.hash, - height: syncHeight, - amount: 0, // will be added later via unspent - fee: 0, - direction: TransactionDirection.incoming, - isPending: false, - date: DateTime.now(), - confirmations: currentChainTip - syncHeight - 1, - to: value.label != null - ? SilentPaymentAddress( - version: scanData.silentAddress.version, - B_scan: scanData.silentAddress.B_scan - .tweakAdd(BigintUtils.fromBytes(BytesUtils.fromHexString(value.tweak))), - B_spend: scanData.silentAddress.B_spend, - hrp: scanData.silentAddress.hrp, - ).toString() - : scanData.silentAddress.toString(), - unspents: [tx], - ); + if (addToWallet.isEmpty) { + // no results tx, continue to next tx + continue; + } - scanData.sendPort.send({txInfo.id: txInfo}); - }); + addToWallet.forEach((key, value) async { + final t_k = value.tweak; + + final addressRecord = BitcoinSilentPaymentAddressRecord( + value.output.address.toAddress(scanData.network), + index: 0, + isHidden: false, + isUsed: true, + network: scanData.network, + silentPaymentTweak: t_k, + type: SegwitAddresType.p2tr, + ); + + int? amount; + int? pos; + outputPubkeys.entries.firstWhere((k) { + final matches = k.value["pubkey"] == key; + if (matches) { + amount = int.parse(k.value["amount"].toString()); + pos = int.parse(k.key.toString()); + return true; + } + return false; + }); + + final json = { + 'address_record': addressRecord.toJSON(), + 'tx_hash': txid, + 'value': amount!, + 'tx_pos': pos!, + 'silent_payment_tweak': t_k, + }; + + final tx = BitcoinUnspent.fromJSON(addressRecord, json); + + final txInfo = ElectrumTransactionInfo( + WalletType.bitcoin, + id: tx.hash, + height: syncHeight, + amount: 0, // will be added later via unspent + fee: 0, + direction: TransactionDirection.incoming, + isPending: false, + date: DateTime.now(), + confirmations: currentChainTip - syncHeight - 1, + to: value.label != null + ? SilentPaymentAddress( + version: scanData.silentAddress.version, + B_scan: scanData.silentAddress.B_scan.tweakAdd( + BigintUtils.fromBytes(BytesUtils.fromHexString(value.tweak))), + B_spend: scanData.silentAddress.B_spend, + hrp: scanData.silentAddress.hrp, + ).toString() + : scanData.silentAddress.toString(), + unspents: [tx], + ); + + scanData.sendPort.send({txInfo.id: txInfo}); + }); + } catch (_) {} } } catch (e, s) { print([e, s]); diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index 50e7d85694..8d9b6101b7 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -53,7 +53,7 @@ class ElectrumWalletSnapshot { .map((addr) => BitcoinSilentPaymentAddressRecord.fromJSON(addr, network: network)) .toList(); - final balance = ElectrumBalance.fromJSON(data['balance'] as String) ?? + final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ?? ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0); var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index f1c1cd8aea..94dbeb61af 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -29,6 +29,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen CryptoCurrency.bch, CryptoCurrency.bnb, CryptoCurrency.btc, + CryptoCurrency.tbtc, CryptoCurrency.dai, CryptoCurrency.dash, CryptoCurrency.eos, @@ -127,6 +128,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen static const bch = CryptoCurrency(title: 'BCH', fullName: 'Bitcoin Cash', raw: 2, name: 'bch', iconPath: 'assets/images/bch_icon.png', decimals: 8); static const bnb = CryptoCurrency(title: 'BNB', tag: 'BSC', fullName: 'Binance Coin', raw: 3, name: 'bnb', iconPath: 'assets/images/bnb_icon.png', decimals: 8); static const btc = CryptoCurrency(title: 'BTC', fullName: 'Bitcoin', raw: 4, name: 'btc', iconPath: 'assets/images/btc.png', decimals: 8); + static const tbtc = CryptoCurrency(title: 'tBTC', fullName: 'Testnet Bitcoin', raw: 4, name: 'tbtc', iconPath: 'assets/images/tbtc.png', decimals: 8); static const dai = CryptoCurrency(title: 'DAI', tag: 'ETH', fullName: 'Dai', raw: 5, name: 'dai', iconPath: 'assets/images/dai_icon.png', decimals: 18); static const dash = CryptoCurrency(title: 'DASH', fullName: 'Dash', raw: 6, name: 'dash', iconPath: 'assets/images/dash_icon.png', decimals: 8); static const eos = CryptoCurrency(title: 'EOS', fullName: 'EOS', raw: 7, name: 'eos', iconPath: 'assets/images/eos_icon.png', decimals: 4); diff --git a/cw_core/lib/currency_for_wallet_type.dart b/cw_core/lib/currency_for_wallet_type.dart index 58ee37669a..1baca7b02f 100644 --- a/cw_core/lib/currency_for_wallet_type.dart +++ b/cw_core/lib/currency_for_wallet_type.dart @@ -1,9 +1,12 @@ import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/wallet_type.dart'; -CryptoCurrency currencyForWalletType(WalletType type) { +CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) { switch (type) { case WalletType.bitcoin: + if (isTestnet == true) { + return CryptoCurrency.tbtc; + } return CryptoCurrency.btc; case WalletType.monero: return CryptoCurrency.xmr; @@ -24,6 +27,7 @@ CryptoCurrency currencyForWalletType(WalletType type) { case WalletType.solana: return CryptoCurrency.sol; default: - throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType'); + throw Exception( + 'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType'); } } diff --git a/cw_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart index 49f1bdc943..21b8779894 100644 --- a/cw_core/lib/wallet_base.dart +++ b/cw_core/lib/wallet_base.dart @@ -24,7 +24,7 @@ abstract class WalletBase walletInfo.type; - CryptoCurrency get currency => currencyForWalletType(type); + CryptoCurrency get currency => currencyForWalletType(type, isTestnet: isTestnet); String get id => walletInfo.id; diff --git a/cw_core/lib/wallet_type.dart b/cw_core/lib/wallet_type.dart index a63ddf37cd..9c7f6e0f97 100644 --- a/cw_core/lib/wallet_type.dart +++ b/cw_core/lib/wallet_type.dart @@ -161,11 +161,14 @@ String walletTypeToDisplayName(WalletType type) { } } -CryptoCurrency walletTypeToCryptoCurrency(WalletType type) { +CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false}) { switch (type) { case WalletType.monero: return CryptoCurrency.xmr; case WalletType.bitcoin: + if (isTestnet) { + return CryptoCurrency.tbtc; + } return CryptoCurrency.btc; case WalletType.litecoin: return CryptoCurrency.ltc; diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 8341b76e21..e623957d6f 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -264,6 +264,8 @@ class CWBitcoin extends Bitcoin { return (option as BitcoinReceivePageOption).toType(); } + @override + @computed bool getScanningActive(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; return bitcoinWallet.silentPaymentsScanningActive; @@ -273,4 +275,9 @@ class CWBitcoin extends Bitcoin { final bitcoinWallet = wallet as ElectrumWallet; bitcoinWallet.setSilentPaymentsScanning(active); } + + bool isTestnet(Object wallet) { + final bitcoinWallet = wallet as ElectrumWallet; + return bitcoinWallet.isTestnet ?? false; + } } diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index 8bda1834a2..2c77ffceaa 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -211,10 +211,12 @@ class CryptoBalanceWidget extends StatelessWidget { dashboardViewModel.balanceViewModel.hasAdditionalBalance, hasSilentPayments: dashboardViewModel.balanceViewModel.hasSilentPayments, silentPaymentsScanningActive: - dashboardViewModel.balanceViewModel.silentPaymentsScanningActive, - setSilentPaymentsScanning: () => dashboardViewModel.balanceViewModel - .setSilentPaymentsScanning( - !dashboardViewModel.balanceViewModel.silentPaymentsScanningActive), + dashboardViewModel.silentPaymentsScanningActive, + setSilentPaymentsScanning: () => + dashboardViewModel.setSilentPaymentsScanning( + !dashboardViewModel.silentPaymentsScanningActive, + ), + isTestnet: dashboardViewModel.isTestnet, ); }); }, @@ -243,6 +245,7 @@ class BalanceRowWidget extends StatelessWidget { required this.hasSilentPayments, required this.silentPaymentsScanningActive, required this.setSilentPaymentsScanning, + required this.isTestnet, super.key, }); @@ -258,6 +261,7 @@ class BalanceRowWidget extends StatelessWidget { final bool hasAdditionalBalance; final bool hasSilentPayments; final bool silentPaymentsScanningActive; + final bool isTestnet; final void Function() setSilentPaymentsScanning; // void _showBalanceDescription(BuildContext context) { @@ -333,14 +337,24 @@ class BalanceRowWidget extends StatelessWidget { maxLines: 1, textAlign: TextAlign.start), SizedBox(height: 6), - Text('${availableFiatBalance}', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16, - fontFamily: 'Lato', - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.textColor, - height: 1)), + if (isTestnet) + Text(S.current.testnet_coins_no_value, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.textColor, + height: 1)), + if (!isTestnet) + Text('${availableFiatBalance}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontFamily: 'Lato', + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.textColor, + height: 1)), ], ), ), @@ -432,17 +446,18 @@ class BalanceRowWidget extends StatelessWidget { textAlign: TextAlign.center, ), SizedBox(height: 4), - Text( - frozenFiatBalance, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12, - fontFamily: 'Lato', - fontWeight: FontWeight.w400, - color: Theme.of(context).extension()!.textColor, - height: 1, + if (!isTestnet) + Text( + frozenFiatBalance, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.textColor, + height: 1, + ), ), - ), ], ), ), @@ -476,17 +491,18 @@ class BalanceRowWidget extends StatelessWidget { textAlign: TextAlign.center, ), SizedBox(height: 4), - Text( - '${additionalFiatBalance}', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12, - fontFamily: 'Lato', - fontWeight: FontWeight.w400, - color: Theme.of(context).extension()!.textColor, - height: 1, + if (!isTestnet) + Text( + '${additionalFiatBalance}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.textColor, + height: 1, + ), ), - ), ], ), if (hasSilentPayments) ...[ diff --git a/lib/src/screens/nodes/widgets/node_form.dart b/lib/src/screens/nodes/widgets/node_form.dart index ab8dcafdf2..e4f32b0d9c 100644 --- a/lib/src/screens/nodes/widgets/node_form.dart +++ b/lib/src/screens/nodes/widgets/node_form.dart @@ -19,7 +19,7 @@ class NodeForm extends StatelessWidget { _portController = TextEditingController(text: editingNode?.uri.port.toString()), _loginController = TextEditingController(text: editingNode?.login), _passwordController = TextEditingController(text: editingNode?.password), - _socksAddressController = TextEditingController(text: editingNode?.socksProxyAddress){ + _socksAddressController = TextEditingController(text: editingNode?.socksProxyAddress) { if (editingNode != null) { nodeViewModel ..setAddress((editingNode!.uri.host.toString())) @@ -60,7 +60,8 @@ class NodeForm extends StatelessWidget { _portController.addListener(() => nodeViewModel.port = _portController.text); _loginController.addListener(() => nodeViewModel.login = _loginController.text); _passwordController.addListener(() => nodeViewModel.password = _passwordController.text); - _socksAddressController.addListener(() => nodeViewModel.socksProxyAddress = _socksAddressController.text); + _socksAddressController + .addListener(() => nodeViewModel.socksProxyAddress = _socksAddressController.text); } final NodeCreateOrEditViewModel nodeViewModel; @@ -103,6 +104,25 @@ class NodeForm extends StatelessWidget { ], ), SizedBox(height: 10.0), + Padding( + padding: EdgeInsets.symmetric(vertical: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Observer( + builder: (_) => StandardCheckbox( + value: nodeViewModel.useSSL, + gradientBackground: true, + borderColor: Theme.of(context).dividerColor, + iconColor: Colors.white, + onChanged: (value) => nodeViewModel.useSSL = value, + caption: S.of(context).use_ssl, + ), + ) + ], + ), + ), if (nodeViewModel.hasAuthCredentials) ...[ Row( children: [ @@ -123,25 +143,6 @@ class NodeForm extends StatelessWidget { )) ], ), - Padding( - padding: EdgeInsets.only(top: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - Observer( - builder: (_) => StandardCheckbox( - value: nodeViewModel.useSSL, - gradientBackground: true, - borderColor: Theme.of(context).dividerColor, - iconColor: Colors.white, - onChanged: (value) => nodeViewModel.useSSL = value, - caption: S.of(context).use_ssl, - ), - ) - ], - ), - ), Padding( padding: EdgeInsets.only(top: 20), child: Row( @@ -163,44 +164,44 @@ class NodeForm extends StatelessWidget { ), Observer( builder: (_) => Column( - children: [ - Padding( - padding: EdgeInsets.only(top: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - StandardCheckbox( - value: nodeViewModel.useSocksProxy, - gradientBackground: true, - borderColor: Theme.of(context).dividerColor, - iconColor: Colors.white, - onChanged: (value) { - if (!value) { - _socksAddressController.text = ''; - } - nodeViewModel.useSocksProxy = value; - }, - caption: 'SOCKS Proxy', - ), - ], - ), - ), - if (nodeViewModel.useSocksProxy) ...[ - SizedBox(height: 10.0), - Row( - children: [ - Expanded( - child: BaseTextFormField( + children: [ + Padding( + padding: EdgeInsets.only(top: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + StandardCheckbox( + value: nodeViewModel.useSocksProxy, + gradientBackground: true, + borderColor: Theme.of(context).dividerColor, + iconColor: Colors.white, + onChanged: (value) { + if (!value) { + _socksAddressController.text = ''; + } + nodeViewModel.useSocksProxy = value; + }, + caption: 'SOCKS Proxy', + ), + ], + ), + ), + if (nodeViewModel.useSocksProxy) ...[ + SizedBox(height: 10.0), + Row( + children: [ + Expanded( + child: BaseTextFormField( controller: _socksAddressController, hintText: '[:]', validator: SocksProxyNodeAddressValidator(), )) - ], - ), - ] - ], - )), + ], + ), + ] + ], + )), ] ], ), diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index 3f3e546b3b..e856d32008 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -102,7 +102,7 @@ class ReceivePage extends BasePage { return (addressListViewModel.type == WalletType.monero || addressListViewModel.type == WalletType.haven || addressListViewModel.type == WalletType.nano || - isElectrumWallet) + isElectrumWallet) ? KeyboardActions( config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, @@ -164,21 +164,21 @@ class ReceivePage extends BasePage { } if (item is WalletAddressListHeader) { - cell = HeaderTile( - title: S.of(context).addresses, - walletAddressListViewModel: addressListViewModel, - showTrailingButton: !addressListViewModel.isAutoGenerateSubaddressEnabled, - showSearchButton: true, - trailingButtonTap: () => - Navigator.of(context).pushNamed(Routes.newSubaddress), - trailingIcon: Icon( - Icons.add, - size: 20, - color: Theme.of(context) - .extension()! - .iconsColor, - )); - } + cell = HeaderTile( + title: S.of(context).addresses, + walletAddressListViewModel: addressListViewModel, + showTrailingButton: + !addressListViewModel.isAutoGenerateSubaddressEnabled, + showSearchButton: true, + trailingButtonTap: () => + Navigator.of(context).pushNamed(Routes.newSubaddress), + trailingIcon: Icon( + Icons.add, + size: 20, + color: + Theme.of(context).extension()!.iconsColor, + )); + } if (item is WalletAddressListItem) { cell = Observer(builder: (_) { @@ -219,7 +219,7 @@ class ReceivePage extends BasePage { child: cell, ); })), - if (!addressListViewModel.hasSilentAddresses) + if (!addressListViewModel.isSilentPayments) Padding( padding: EdgeInsets.fromLTRB(24, 24, 24, 32), child: Text(S.of(context).electrum_address_disclaimer, diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index 3a0ba24730..f4dc865f03 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -11,7 +11,8 @@ class RescanPage extends BasePage { : _blockchainHeightWidgetKey = GlobalKey(); @override - String get title => S.current.rescan; + String get title => + _rescanViewModel.isSilentPaymentsScan ? S.current.silent_payments_scanning : S.current.rescan; final GlobalKey _blockchainHeightWidgetKey; final RescanViewModel _rescanViewModel; @@ -19,20 +20,19 @@ class RescanPage extends BasePage { Widget body(BuildContext context) { return Padding( padding: EdgeInsets.only(left: 24, right: 24, bottom: 24), - child: - Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - BlockchainHeightWidget(key: _blockchainHeightWidgetKey, - onHeightOrDateEntered: (value) => - _rescanViewModel.isButtonEnabled = value), + child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + BlockchainHeightWidget( + key: _blockchainHeightWidgetKey, + onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value, + isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan, + ), Observer( builder: (_) => LoadingPrimaryButton( - isLoading: - _rescanViewModel.state == RescanWalletState.rescaning, + isLoading: _rescanViewModel.state == RescanWalletState.rescaning, text: S.of(context).rescan, onPressed: () async { await _rescanViewModel.rescanCurrentWallet( - restoreHeight: - _blockchainHeightWidgetKey.currentState!.height); + restoreHeight: _blockchainHeightWidgetKey.currentState!.height); Navigator.of(context).pop(); }, color: Theme.of(context).primaryColor, diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index 6853105067..d369978147 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -2,7 +2,6 @@ import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -167,7 +166,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin Navigator.of(context).pushNamed(Routes.rescan), ), if (DeviceInfo.instance.isMobile && FeatureFlag.isBackgroundSyncEnabled) ...[ diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index b57473cba9..2029b9f487 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -96,6 +96,7 @@ class WalletListBody extends StatefulWidget { class WalletListBodyState extends State { final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24); final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); + final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24); final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24); final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24); @@ -161,7 +162,10 @@ class WalletListBodyState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ wallet.isEnabled - ? _imageFor(type: wallet.type) + ? _imageFor( + type: wallet.type, + isTestnet: wallet.isTestnet, + ) : nonWalletTypeIcon, SizedBox(width: 10), Flexible( @@ -296,9 +300,12 @@ class WalletListBodyState extends State { ); } - Image _imageFor({required WalletType type}) { + Image _imageFor({required WalletType type, bool? isTestnet}) { switch (type) { case WalletType.bitcoin: + if (isTestnet == true) { + return tBitcoinIcon; + } return bitcoinIcon; case WalletType.monero: return moneroIcon; diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index f98e0439b1..0467b18a28 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -1,6 +1,5 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/utils/device_info.dart'; -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; @@ -134,7 +133,8 @@ class AddressTextField extends StatelessWidget { ), )), ], - if (this.options.contains(AddressTextFieldOption.qrCode)) ...[ + if (this.options.contains(AddressTextFieldOption.qrCode) && + DeviceInfo.instance.isMobile) ...[ Container( width: prefixIconWidth, height: prefixIconHeight, @@ -194,7 +194,7 @@ class AddressTextField extends StatelessWidget { Future _presentQRScanner(BuildContext context) async { bool isCameraPermissionGranted = - await PermissionHandler.checkPermission(Permission.camera, context); + await PermissionHandler.checkPermission(Permission.camera, context); if (!isCameraPermissionGranted) return; final code = await presentQRScanner(); if (code.isEmpty) { diff --git a/lib/src/widgets/blockchain_height_widget.dart b/lib/src/widgets/blockchain_height_widget.dart index 221f874468..fcfeedda32 100644 --- a/lib/src/widgets/blockchain_height_widget.dart +++ b/lib/src/widgets/blockchain_height_widget.dart @@ -12,13 +12,15 @@ class BlockchainHeightWidget extends StatefulWidget { this.onHeightChange, this.focusNode, this.onHeightOrDateEntered, - this.hasDatePicker = true}) - : super(key: key); + this.hasDatePicker = true, + this.isSilentPaymentsScan = false, + }) : super(key: key); final Function(int)? onHeightChange; final Function(bool)? onHeightOrDateEntered; final FocusNode? focusNode; final bool hasDatePicker; + final bool isSilentPaymentsScan; @override State createState() => BlockchainHeightState(); @@ -64,9 +66,10 @@ class BlockchainHeightState extends State { child: BaseTextFormField( focusNode: widget.focusNode, controller: restoreHeightController, - keyboardType: TextInputType.numberWithOptions( - signed: false, decimal: false), - hintText: S.of(context).widgets_restore_from_blockheight, + keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false), + hintText: widget.isSilentPaymentsScan + ? S.of(context).silent_payments_scan_from_height + : S.of(context).widgets_restore_from_blockheight, ))) ], ), @@ -78,8 +81,7 @@ class BlockchainHeightState extends State { style: TextStyle( fontSize: 16.0, fontWeight: FontWeight.w500, - color: - Theme.of(context).extension()!.titleColor), + color: Theme.of(context).extension()!.titleColor), ), ), Row( @@ -91,7 +93,9 @@ class BlockchainHeightState extends State { child: IgnorePointer( child: BaseTextFormField( controller: dateController, - hintText: S.of(context).widgets_restore_from_date, + hintText: widget.isSilentPaymentsScan + ? S.of(context).silent_payments_scan_from_date + : S.of(context).widgets_restore_from_date, )), ), )) @@ -100,13 +104,12 @@ class BlockchainHeightState extends State { Padding( padding: EdgeInsets.only(left: 40, right: 40, top: 24), child: Text( - S.of(context).restore_from_date_or_blockheight, + widget.isSilentPaymentsScan + ? S.of(context).silent_payments_scan_from_date_or_blockheight + : S.of(context).restore_from_date_or_blockheight, textAlign: TextAlign.center, style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.normal, - color: Theme.of(context).hintColor - ), + fontSize: 12, fontWeight: FontWeight.normal, color: Theme.of(context).hintColor), ), ) ] diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 08bdd82522..d72b339039 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -46,8 +46,6 @@ abstract class BalanceViewModelBase with Store { : isReversing = false, isShowCard = appStore.wallet!.walletInfo.isShowIntroCakePayCard, wallet = appStore.wallet! { - silentPaymentsScanningActive = hasSilentPayments && bitcoin!.getScanningActive(wallet); - reaction((_) => appStore.wallet, _onWalletChange); } @@ -66,17 +64,6 @@ abstract class BalanceViewModelBase with Store { @computed bool get hasSilentPayments => wallet.type == WalletType.bitcoin; - @observable - bool silentPaymentsScanningActive = false; - - @action - void setSilentPaymentsScanning(bool active) { - if (hasSilentPayments) { - bitcoin!.setScanningActive(wallet, active); - silentPaymentsScanningActive = active; - } - } - @computed double get price { final price = fiatConvertationStore.prices[appStore.wallet!.currency]; diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index b08a0c3c7e..b4cd026fec 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -201,6 +201,14 @@ abstract class DashboardViewModelBase with Store { return true; }); + + if (hasSilentPayments) { + silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet); + + reaction((_) => wallet.syncStatus, (SyncStatus syncStatus) { + silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet); + }); + } } @observable @@ -287,14 +295,33 @@ abstract class DashboardViewModelBase with Store { @observable WalletBase, TransactionInfo> wallet; + @computed + bool get isTestnet => wallet.type == WalletType.bitcoin && bitcoin!.isTestnet(wallet); + + @computed bool get hasRescan => - (wallet.type == WalletType.bitcoin && bitcoin!.hasSelectedSilentPayments(wallet)) || + wallet.type == WalletType.bitcoin || wallet.type == WalletType.monero || wallet.type == WalletType.haven; + @computed + bool get hasSilentPayments => hasRescan && wallet.type == WalletType.bitcoin; + final KeyService keyService; final SharedPreferences sharedPreferences; + @observable + bool silentPaymentsScanningActive = false; + + @action + void setSilentPaymentsScanning(bool active) { + silentPaymentsScanningActive = active; + + if (hasSilentPayments) { + bitcoin!.setScanningActive(wallet, active); + } + } + BalanceViewModel balanceViewModel; AppStore appStore; diff --git a/lib/view_model/rescan_view_model.dart b/lib/view_model/rescan_view_model.dart index e263f4a121..b10e29e698 100644 --- a/lib/view_model/rescan_view_model.dart +++ b/lib/view_model/rescan_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; @@ -21,6 +22,9 @@ abstract class RescanViewModelBase with Store { @observable bool isButtonEnabled; + @computed + bool get isSilentPaymentsScan => bitcoin!.hasSelectedSilentPayments(_wallet); + @action Future rescanCurrentWallet({required int restoreHeight}) async { state = RescanWalletState.rescaning; diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index 5e843ad789..c1c275a875 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -183,6 +183,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo }) : _baseItems = [], selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type), _cryptoNumberFormat = NumberFormat(_cryptoNumberPattern), + hasAccounts = + appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven, amount = '', _settingsStore = appStore.settingsStore, super(appStore: appStore) { @@ -194,8 +196,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo _init(); selectedCurrency = walletTypeToCryptoCurrency(wallet.type); - _hasAccounts = - hasSilentAddresses || wallet.type == WalletType.monero || wallet.type == WalletType.haven; + hasAccounts = wallet.type == WalletType.monero || wallet.type == WalletType.haven; } static const String _cryptoNumberPattern = '0.00000000'; @@ -364,10 +365,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo } @observable - bool _hasAccounts = false; - - @computed - bool get hasAccounts => _hasAccounts; + bool hasAccounts; @computed String get accountLabel { @@ -382,21 +380,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo return ''; } - @observable - // ignore: prefer_final_fields - bool? _hasSilentAddresses = null; - - @computed - bool get hasSilentAddresses => _hasSilentAddresses ?? wallet.type == WalletType.bitcoin; - // @computed - // bool get hasSilentAddresses => - // _hasSilentAddresses ?? - // wallet.type == WalletType.bitcoin && - // wallet.walletAddresses.addressPageType == btc.AddressType.p2sp; - @computed bool get hasAddressList => - hasSilentAddresses || wallet.type == WalletType.monero || wallet.type == WalletType.haven || wallet.type == WalletType.bitcoinCash || @@ -409,9 +394,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo wallet.type == WalletType.litecoin || wallet.type == WalletType.bitcoinCash; + @computed + bool get isSilentPayments => bitcoin!.hasSelectedSilentPayments(wallet); + @computed bool get isAutoGenerateSubaddressEnabled => wallet.type == WalletType.bitcoin - ? !bitcoin!.hasSelectedSilentPayments(wallet) + ? !isSilentPayments : _settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled; List _baseItems; diff --git a/lib/view_model/wallet_list/wallet_list_item.dart b/lib/view_model/wallet_list/wallet_list_item.dart index a644c07b3a..24b1a3bd00 100644 --- a/lib/view_model/wallet_list/wallet_list_item.dart +++ b/lib/view_model/wallet_list/wallet_list_item.dart @@ -7,6 +7,7 @@ class WalletListItem { required this.key, this.isCurrent = false, this.isEnabled = true, + this.isTestnet = false, }); final String name; @@ -14,4 +15,5 @@ class WalletListItem { final bool isCurrent; final dynamic key; final bool isEnabled; + final bool isTestnet; } diff --git a/lib/view_model/wallet_list/wallet_list_view_model.dart b/lib/view_model/wallet_list/wallet_list_view_model.dart index b31133c7d9..2ed6358f44 100644 --- a/lib/view_model/wallet_list/wallet_list_view_model.dart +++ b/lib/view_model/wallet_list/wallet_list_view_model.dart @@ -61,6 +61,7 @@ abstract class WalletListViewModelBase with Store { key: info.key, isCurrent: info.name == _appStore.wallet?.name && info.type == _appStore.wallet?.type, isEnabled: availableWalletTypes.contains(info.type), + isTestnet: info.network?.toLowerCase().contains('testnet') ?? false, ), ), ); diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index a66f85da4f..067a50ca8b 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -610,6 +610,9 @@ "sign_up": "اشتراك", "signTransaction": " ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ", "signup_for_card_accept_terms": "قم بالتسجيل للحصول على البطاقة وقبول الشروط.", + "silent_payments_scan_from_date": "فحص من التاريخ", + "silent_payments_scan_from_date_or_blockheight": "يرجى إدخال ارتفاع الكتلة الذي تريد بدء المسح الضوئي للمدفوعات الصامتة الواردة ، أو استخدام التاريخ بدلاً من ذلك. يمكنك اختيار ما إذا كانت المحفظة تواصل مسح كل كتلة ، أو تتحقق فقط من الارتفاع المحدد.", + "silent_payments_scan_from_height": "فحص من ارتفاع الكتلة", "silent_payments_scanning": "المدفوعات الصامتة المسح الضوئي", "slidable": "قابل للانزلاق", "sort_by": "ترتيب حسب", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "محفظتك تتم مزامنتها", "template": "قالب", "template_name": "اسم القالب", + "testnet_coins_no_value": "عملات TestNet ليس لها قيمة", "third_intro_content": "يعيش Yats خارج Cake Wallet أيضًا. يمكن استبدال أي عنوان محفظة على وجه الأرض بـ Yat!", "third_intro_title": "يتماشي Yat بلطف مع الآخرين", "thorchain_taproot_address_not_supported": "لا يدعم مزود Thorchain عناوين Taproot. يرجى تغيير العنوان أو تحديد مزود مختلف.", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index cc48a56e98..626ed5ae81 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -610,6 +610,9 @@ "sign_up": "Регистрация", "signTransaction": "Подпишете транзакция", "signup_for_card_accept_terms": "Регистрайте се за картата и приемете условията.", + "silent_payments_scan_from_date": "Сканиране от дата", + "silent_payments_scan_from_date_or_blockheight": "Моля, въведете височината на блока, която искате да започнете да сканирате за входящи безшумни плащания, или вместо това използвайте датата. Можете да изберете дали портфейлът продължава да сканира всеки блок или проверява само определената височина.", + "silent_payments_scan_from_height": "Сканиране от височината на блока", "silent_payments_scanning": "Безшумни плащания за сканиране", "slidable": "Плъзгащ се", "sort_by": "Сортирай по", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "Вашият портфейл се синхронизира", "template": "Шаблон", "template_name": "Име на шаблон", + "testnet_coins_no_value": "Тестовите монети нямат стойност", "third_intro_content": "Yats също живее извън Cake Wallet. Всеки адрес на портфейл може да бъде заменен с Yat!", "third_intro_title": "Yat добре се сработва с други", "thorchain_taproot_address_not_supported": "Доставчикът на Thorchain не поддържа адреси на TapRoot. Моля, променете адреса или изберете друг доставчик.", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index f3076cf6c5..0e4253ccab 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -610,6 +610,9 @@ "sign_up": "Registrovat se", "signTransaction": "Podepsat transakci", "signup_for_card_accept_terms": "Zaregistrujte se pro kartu a souhlaste s podmínkami.", + "silent_payments_scan_from_date": "Skenovat od data", + "silent_payments_scan_from_date_or_blockheight": "Zadejte výšku bloku, kterou chcete začít skenovat, zda jsou přicházející tiché platby, nebo místo toho použijte datum. Můžete si vybrat, zda peněženka pokračuje v skenování každého bloku nebo zkontroluje pouze zadanou výšku.", + "silent_payments_scan_from_height": "Skenování z výšky bloku", "silent_payments_scanning": "Skenování tichých plateb", "slidable": "Posuvné", "sort_by": "Seřazeno podle", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "Vaše peněženka se synchronizuje", "template": "Šablona", "template_name": "Název šablony", + "testnet_coins_no_value": "Mince TestNet nemají žádnou hodnotu", "third_intro_content": "Yat existuje i mimo Cake Wallet. Jakákoliv adresa peněženky na světě může být nahrazena Yatem!", "third_intro_title": "Yat dobře spolupracuje s ostatními", "thorchain_taproot_address_not_supported": "Poskytovatel Thorchain nepodporuje adresy Taproot. Změňte adresu nebo vyberte jiného poskytovatele.", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index cf4cbb7fa2..b6f9d475c9 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -611,6 +611,9 @@ "sign_up": "Anmelden", "signTransaction": "Transaktion unterzeichnen", "signup_for_card_accept_terms": "Melden Sie sich für die Karte an und akzeptieren Sie die Bedingungen.", + "silent_payments_scan_from_date": "Scan ab Datum", + "silent_payments_scan_from_date_or_blockheight": "Bitte geben Sie die Blockhöhe ein, die Sie für eingehende stille Zahlungen scannen möchten, oder verwenden Sie stattdessen das Datum. Sie können wählen, ob die Brieftasche jeden Block scannt oder nur die angegebene Höhe überprüft.", + "silent_payments_scan_from_height": "Scan aus der Blockhöhe scannen", "silent_payments_scanning": "Stille Zahlungen scannen", "slidable": "Verschiebbar", "sort_by": "Sortiere nach", @@ -647,6 +650,7 @@ "syncing_wallet_alert_title": "Ihr Wallet wird synchronisiert", "template": "Vorlage", "template_name": "Vorlagenname", + "testnet_coins_no_value": "Testnet -Münzen haben keinen Wert", "third_intro_content": "Yats leben auch außerhalb von Cake Wallet. Jede Wallet-Adresse auf der Welt kann durch ein Yat ersetzt werden!", "third_intro_title": "Yat spielt gut mit anderen", "thorchain_taproot_address_not_supported": "Der Thorchain -Anbieter unterstützt keine Taproot -Adressen. Bitte ändern Sie die Adresse oder wählen Sie einen anderen Anbieter aus.", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index d31435b484..97546bf88a 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -610,6 +610,9 @@ "sign_up": "Sign Up", "signTransaction": "Sign Transaction", "signup_for_card_accept_terms": "Sign up for the card and accept the terms.", + "silent_payments_scan_from_date": "Scan from date", + "silent_payments_scan_from_date_or_blockheight": "Please enter the block height you want to start scanning for incoming silent payments, or, use the date instead. You can choose if the wallet continues scanning every block, or checks only the specified height.", + "silent_payments_scan_from_height": "Scan from block height", "silent_payments_scanning": "Silent Payments Scanning", "slidable": "Slidable", "sort_by": "Sort by", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "Your wallet is syncing", "template": "Template", "template_name": "Template Name", + "testnet_coins_no_value": "Testnet coins have no value", "third_intro_content": "Yats live outside of Cake Wallet, too. Any wallet address on earth can be replaced with a Yat!", "third_intro_title": "Yat plays nicely with others", "thorchain_taproot_address_not_supported": "The ThorChain provider does not support Taproot addresses. Please change the address or select a different provider.", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 6aa1cfbd81..2791de8d64 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -611,6 +611,9 @@ "sign_up": "Registrarse", "signTransaction": "Firmar transacción", "signup_for_card_accept_terms": "Regístrese para obtener la tarjeta y acepte los términos.", + "silent_payments_scan_from_date": "Escanear desde la fecha", + "silent_payments_scan_from_date_or_blockheight": "Ingrese la altura del bloque que desea comenzar a escanear para pagos silenciosos entrantes, o use la fecha en su lugar. Puede elegir si la billetera continúa escaneando cada bloque, o verifica solo la altura especificada.", + "silent_payments_scan_from_height": "Escanear desde la altura del bloque", "silent_payments_scanning": "Escaneo de pagos silenciosos", "slidable": "deslizable", "sort_by": "Ordenar por", @@ -647,6 +650,7 @@ "syncing_wallet_alert_title": "Tu billetera se está sincronizando", "template": "Plantilla", "template_name": "Nombre de la plantilla", + "testnet_coins_no_value": "Las monedas de prueba no tienen valor", "third_intro_content": "Los Yats también viven fuera de Cake Wallet. Cualquier dirección de billetera en la tierra se puede reemplazar con un Yat!", "third_intro_title": "Yat juega muy bien con otras", "thorchain_taproot_address_not_supported": "El proveedor de Thorchain no admite las direcciones de Taproot. Cambie la dirección o seleccione un proveedor diferente.", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 7b7ce56df7..50772e1120 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -610,6 +610,9 @@ "sign_up": "S'inscrire", "signTransaction": "Signer une transaction", "signup_for_card_accept_terms": "Inscrivez-vous pour la carte et acceptez les conditions.", + "silent_payments_scan_from_date": "Analyser à partir de la date", + "silent_payments_scan_from_date_or_blockheight": "Veuillez saisir la hauteur du bloc que vous souhaitez commencer à scanner pour les paiements silencieux entrants, ou utilisez la date à la place. Vous pouvez choisir si le portefeuille continue de numériser chaque bloc ou ne vérifie que la hauteur spécifiée.", + "silent_payments_scan_from_height": "Scan à partir de la hauteur du bloc", "silent_payments_scanning": "Payments silencieux SCANNING", "slidable": "Glissable", "sort_by": "Trier par", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "Votre portefeuille (wallet) est en cours de synchronisation", "template": "Modèle", "template_name": "Nom du modèle", + "testnet_coins_no_value": "Les pièces TestNet n'ont aucune valeur", "third_intro_content": "Les Yats existent aussi en dehors de Cake Wallet. Toute adresse sur terre peut être remplacée par un Yat !", "third_intro_title": "Yat est universel", "thorchain_taproot_address_not_supported": "Le fournisseur de Thorchain ne prend pas en charge les adresses de tapoot. Veuillez modifier l'adresse ou sélectionner un autre fournisseur.", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 6c3de1e037..6c8a9fabe2 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -612,6 +612,9 @@ "sign_up": "Shiga", "signTransaction": "Sa hannu Ma'amala", "signup_for_card_accept_terms": "Yi rajista don katin kuma karɓi sharuɗɗan.", + "silent_payments_scan_from_date": "Scan daga kwanan wata", + "silent_payments_scan_from_date_or_blockheight": "Da fatan za a shigar da toshe wurin da kake son fara bincika don biyan silins mai shigowa, ko, yi amfani da kwanan wata. Zaka iya zabar idan walat ɗin ya ci gaba da bincika kowane toshe, ko duba tsinkaye da aka ƙayyade.", + "silent_payments_scan_from_height": "Scan daga tsayin daka", "silent_payments_scanning": "Silent biya scanning", "slidable": "Mai iya zamewa", "sort_by": "Kasa", @@ -648,6 +651,7 @@ "syncing_wallet_alert_title": "Walat ɗin ku yana aiki tare", "template": "Samfura", "template_name": "Sunan Samfura", + "testnet_coins_no_value": "TalkNet tsabar kudi ba su da darajar", "third_intro_content": "Yats suna zaune a wajen Kek Wallet, kuma. Ana iya maye gurbin kowane adireshin walat a duniya da Yat!", "third_intro_title": "Yat yana wasa da kyau tare da wasu", "thorchain_taproot_address_not_supported": "Mai ba da tallafi na ThorChain baya goyan bayan adreshin taproot. Da fatan za a canza adireshin ko zaɓi mai bayarwa daban.", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 82445dfa3f..8fb2ea0810 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -612,6 +612,9 @@ "sign_up": "साइन अप करें", "signTransaction": "लेन-देन पर हस्ताक्षर करें", "signup_for_card_accept_terms": "कार्ड के लिए साइन अप करें और शर्तें स्वीकार करें।", + "silent_payments_scan_from_date": "तिथि से स्कैन करना", + "silent_payments_scan_from_date_or_blockheight": "कृपया उस ब्लॉक ऊंचाई दर्ज करें जिसे आप आने वाले मूक भुगतान के लिए स्कैन करना शुरू करना चाहते हैं, या, इसके बजाय तारीख का उपयोग करें। आप चुन सकते हैं कि क्या वॉलेट हर ब्लॉक को स्कैन करना जारी रखता है, या केवल निर्दिष्ट ऊंचाई की जांच करता है।", + "silent_payments_scan_from_height": "ब्लॉक ऊंचाई से स्कैन करें", "silent_payments_scanning": "मूक भुगतान स्कैनिंग", "slidable": "फिसलने लायक", "sort_by": "इसके अनुसार क्रमबद्ध करें", @@ -648,6 +651,7 @@ "syncing_wallet_alert_title": "आपका वॉलेट सिंक हो रहा है", "template": "खाका", "template_name": "टेम्पलेट नाम", + "testnet_coins_no_value": "टेस्टनेट सिक्कों का कोई मूल्य नहीं है", "third_intro_content": "Yats Cake Wallet के बाहर भी रहता है। धरती पर किसी भी वॉलेट पते को Yat से बदला जा सकता है!", "third_intro_title": "Yat दूसरों के साथ अच्छा खेलता है", "thorchain_taproot_address_not_supported": "थोरचेन प्रदाता टैपरोट पते का समर्थन नहीं करता है। कृपया पता बदलें या एक अलग प्रदाता का चयन करें।", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 4c64a9a349..df1b1ce113 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -610,6 +610,9 @@ "sign_up": "Prijavite se", "signTransaction": "Potpišite transakciju", "signup_for_card_accept_terms": "Prijavite se za karticu i prihvatite uvjete.", + "silent_payments_scan_from_date": "Skeniranje iz datuma", + "silent_payments_scan_from_date_or_blockheight": "Unesite visinu bloka koju želite započeti skeniranje za dolazna tiha plaćanja ili umjesto toga upotrijebite datum. Možete odabrati da li novčanik nastavlja skenirati svaki blok ili provjerava samo navedenu visinu.", + "silent_payments_scan_from_height": "Skeniranje s visine bloka", "silent_payments_scanning": "Skeniranje tihih plaćanja", "slidable": "Klizna", "sort_by": "Poredaj po", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "Vaš novčanik se sinkronizira", "template": "Predložak", "template_name": "Naziv predloška", + "testnet_coins_no_value": "TestNet kovanice nemaju vrijednost", "third_intro_content": "Yats žive i izvan Cake Wallet -a. Bilo koja adresa novčanika na svijetu može se zamijeniti Yat!", "third_intro_title": "Yat se lijepo igra s drugima", "thorchain_taproot_address_not_supported": "Thorchain pružatelj ne podržava Taproot adrese. Promijenite adresu ili odaberite drugog davatelja usluga.", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 90a92a8f24..32d0a997a9 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -613,6 +613,9 @@ "sign_up": "Daftar", "signTransaction": "Tandatangani Transaksi", "signup_for_card_accept_terms": "Daftar untuk kartu dan terima syarat dan ketentuan.", + "silent_payments_scan_from_date": "Pindai dari tanggal", + "silent_payments_scan_from_date_or_blockheight": "Harap masukkan ketinggian blok yang ingin Anda mulai pemindaian untuk pembayaran diam yang masuk, atau, gunakan tanggal sebagai gantinya. Anda dapat memilih jika dompet terus memindai setiap blok, atau memeriksa hanya ketinggian yang ditentukan.", + "silent_payments_scan_from_height": "Pindai dari Tinggi Blok", "silent_payments_scanning": "Pemindaian pembayaran diam", "slidable": "Dapat digeser", "sort_by": "Sortir dengan", @@ -649,6 +652,7 @@ "syncing_wallet_alert_title": "Dompet Anda sedang disinkronkan", "template": "Template", "template_name": "Nama Templat", + "testnet_coins_no_value": "Koin TestNet tidak memiliki nilai", "third_intro_content": "Yats hidup di luar Cake Wallet juga. Setiap alamat dompet di dunia dapat diganti dengan Yat!", "third_intro_title": "Yat bermain baik dengan yang lain", "thorchain_taproot_address_not_supported": "Penyedia Thorchain tidak mendukung alamat Taproot. Harap ubah alamatnya atau pilih penyedia yang berbeda.", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 9795950484..b3ba636ba6 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -612,6 +612,9 @@ "sign_up": "Registrati", "signTransaction": "Firma la transazione", "signup_for_card_accept_terms": "Registrati per la carta e accetta i termini.", + "silent_payments_scan_from_date": "Scansionare dalla data", + "silent_payments_scan_from_date_or_blockheight": "Inserisci l'altezza del blocco che si desidera iniziare la scansione per i pagamenti silenziosi in arrivo o, utilizza invece la data. Puoi scegliere se il portafoglio continua a scansionare ogni blocco o controlla solo l'altezza specificata.", + "silent_payments_scan_from_height": "Scansione dall'altezza del blocco", "silent_payments_scanning": "Scansione di pagamenti silenziosi", "slidable": "Scorrevole", "sort_by": "Ordina per", @@ -648,6 +651,7 @@ "syncing_wallet_alert_title": "Il tuo portafoglio si sta sincronizzando", "template": "Modello", "template_name": "Nome modello", + "testnet_coins_no_value": "Le monete TestNet non hanno valore", "third_intro_content": "Yat può funzionare anche fuori da Cake Wallet. Qualsiasi indirizzo di portafoglio sulla terra può essere sostituito con uno Yat!", "third_intro_title": "Yat gioca bene con gli altri", "thorchain_taproot_address_not_supported": "Il provider di Thorchain non supporta gli indirizzi di TapRoot. Si prega di modificare l'indirizzo o selezionare un fornitore diverso.", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index b3a3cefe0f..7274f83ab5 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -611,6 +611,9 @@ "sign_up": "サインアップ", "signTransaction": "トランザクションに署名する", "signup_for_card_accept_terms": "カードにサインアップして、利用規約に同意してください。", + "silent_payments_scan_from_date": "日付からスキャンします", + "silent_payments_scan_from_date_or_blockheight": "着信のサイレント決済のためにスキャンを開始するブロックの高さを入力するか、代わりに日付を使用してください。ウォレットがすべてのブロックをスキャンし続けるか、指定された高さのみをチェックするかどうかを選択できます。", + "silent_payments_scan_from_height": "ブロックの高さからスキャンします", "silent_payments_scanning": "サイレントペイメントスキャン", "slidable": "スライド可能", "sort_by": "並び替え", @@ -647,6 +650,7 @@ "syncing_wallet_alert_title": "ウォレットは同期中です", "template": "テンプレート", "template_name": "テンプレート名", + "testnet_coins_no_value": "テストネットコインには価値がありません", "third_intro_content": "YatsはCakeWalletの外にも住んでいます。 地球上のどのウォレットアドレスもYatに置き換えることができます!", "third_intro_title": "Yatは他の人とうまく遊ぶ", "thorchain_taproot_address_not_supported": "Thorchainプロバイダーは、TapRootアドレスをサポートしていません。アドレスを変更するか、別のプロバイダーを選択してください。", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index b2908056f7..b38a5f5cd5 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -611,6 +611,9 @@ "sign_up": "가입", "signTransaction": "거래 서명", "signup_for_card_accept_terms": "카드에 가입하고 약관에 동의합니다.", + "silent_payments_scan_from_date": "날짜부터 스캔하십시오", + "silent_payments_scan_from_date_or_blockheight": "들어오는 사일런트 결제를 위해 스캔을 시작하려는 블록 높이를 입력하거나 대신 날짜를 사용하십시오. 지갑이 모든 블록을 계속 스캔하는지 여부를 선택하거나 지정된 높이 만 확인할 수 있습니다.", + "silent_payments_scan_from_height": "블록 높이에서 스캔하십시오", "silent_payments_scanning": "조용한 지불 스캔", "slidable": "슬라이딩 가능", "sort_by": "정렬 기준", @@ -647,6 +650,7 @@ "syncing_wallet_alert_title": "지갑 동기화 중", "template": "주형", "template_name": "템플릿 이름", + "testnet_coins_no_value": "Testnet 코인은 가치가 없습니다", "third_intro_content": "Yats는 Cake Wallet 밖에서도 살고 있습니다. 지구상의 모든 지갑 주소는 Yat!", "third_intro_title": "Yat는 다른 사람들과 잘 놉니다.", "thorchain_taproot_address_not_supported": "Thorchain 제공 업체는 Taproot 주소를 지원하지 않습니다. 주소를 변경하거나 다른 공급자를 선택하십시오.", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 1453da4cc8..68fcaeb52b 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -610,6 +610,9 @@ "sign_up": "ဆိုင်းအပ်", "signTransaction": "ငွေလွှဲဝင်ပါ။", "signup_for_card_accept_terms": "ကတ်အတွက် စာရင်းသွင်းပြီး စည်းကမ်းချက်များကို လက်ခံပါ။", + "silent_payments_scan_from_date": "ရက်စွဲမှစကင်ဖတ်ပါ", + "silent_payments_scan_from_date_or_blockheight": "ကျေးဇူးပြု. သင်ဝင်လာသောအသံတိတ်ငွေပေးချေမှုအတွက်သင်စကင်ဖတ်စစ်ဆေးလိုသည့်အမြင့်ကိုဖြည့်ပါ။ သို့မဟုတ်နေ့စွဲကိုသုံးပါ။ Wallet သည်လုပ်ကွက်တိုင်းကိုဆက်လက်စကင်ဖတ်စစ်ဆေးပါကသို့မဟုတ်သတ်မှတ်ထားသောအမြင့်ကိုသာစစ်ဆေးပါကသင်ရွေးချယ်နိုင်သည်။", + "silent_payments_scan_from_height": "ပိတ်ပင်တားဆီးမှုအမြင့်ကနေ scan", "silent_payments_scanning": "အသံတိတ်ငွေပေးချေမှု scanning", "slidable": "လျှောချနိုင်သည်။", "sort_by": "အလိုက်စဥ်သည်", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "သင့်ပိုက်ဆံအိတ်ကို စင့်ခ်လုပ်နေပါသည်။", "template": "ပုံစံခွက်", "template_name": "နမူနာပုံစံ", + "testnet_coins_no_value": "Testnet ဒင်္ဂါးပြားတန်ဖိုးမရှိပါ", "third_intro_content": "Yats သည် Cake Wallet အပြင်ဘက်တွင် နေထိုင်ပါသည်။ ကမ္ဘာပေါ်ရှိ မည်သည့်ပိုက်ဆံအိတ်လိပ်စာကို Yat ဖြင့် အစားထိုးနိုင်ပါသည်။", "third_intro_title": "Yat သည် အခြားသူများနှင့် ကောင်းစွာကစားသည်။", "thorchain_taproot_address_not_supported": "Thorchain Provider သည် Taproot လိပ်စာများကိုမထောက်ခံပါ။ ကျေးဇူးပြု. လိပ်စာကိုပြောင်းပါသို့မဟုတ်အခြားပံ့ပိုးပေးသူကိုရွေးချယ်ပါ။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index b82019e6bd..ccd25e2739 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -610,6 +610,9 @@ "sign_up": "Aanmelden", "signTransaction": "Transactie ondertekenen", "signup_for_card_accept_terms": "Meld je aan voor de kaart en accepteer de voorwaarden.", + "silent_payments_scan_from_date": "Scan vanaf datum", + "silent_payments_scan_from_date_or_blockheight": "Voer de blokhoogte in die u wilt beginnen met scannen op inkomende stille betalingen, of gebruik in plaats daarvan de datum. U kunt kiezen of de portemonnee elk blok blijft scannen of alleen de opgegeven hoogte controleert.", + "silent_payments_scan_from_height": "Scan van blokhoogte", "silent_payments_scanning": "Stille betalingen scannen", "slidable": "Verschuifbaar", "sort_by": "Sorteer op", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "Uw portemonnee wordt gesynchroniseerd", "template": "Sjabloon", "template_name": "Sjabloonnaam", + "testnet_coins_no_value": "Testnet -munten hebben geen waarde", "third_intro_content": "Yats wonen ook buiten Cake Wallet. Elk portemonnee-adres op aarde kan worden vervangen door een Yat!", "third_intro_title": "Yat speelt leuk met anderen", "thorchain_taproot_address_not_supported": "De Thorchain -provider ondersteunt geen Taprooot -adressen. Wijzig het adres of selecteer een andere provider.", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index c1e677284f..a464e78f27 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -610,6 +610,9 @@ "sign_up": "Zarejestruj się", "signTransaction": "Podpisz transakcję", "signup_for_card_accept_terms": "Zarejestruj się, aby otrzymać kartę i zaakceptuj warunki.", + "silent_payments_scan_from_date": "Skanuj z daty", + "silent_payments_scan_from_date_or_blockheight": "Wprowadź wysokość bloku, którą chcesz rozpocząć skanowanie w poszukiwaniu cichej płatności lub zamiast tego skorzystaj z daty. Możesz wybrać, czy portfel kontynuuje skanowanie każdego bloku, lub sprawdza tylko określoną wysokość.", + "silent_payments_scan_from_height": "Skanuj z wysokości bloku", "silent_payments_scanning": "Skanowanie cichych płatności", "slidable": "Przesuwne", "sort_by": "Sortuj według", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "Twój portfel się synchronizuje", "template": "Szablon", "template_name": "Nazwa szablonu", + "testnet_coins_no_value": "Monety testowe nie mają wartości", "third_intro_content": "Yats mieszkają również poza Cake Wallet. Każdy adres portfela na ziemi można zastąpić Yat!", "third_intro_title": "Yat ładnie bawi się z innymi", "thorchain_taproot_address_not_supported": "Dostawca Thorchain nie obsługuje adresów TAPROOT. Zmień adres lub wybierz innego dostawcę.", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 77e105b04e..e1e50252fa 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -612,7 +612,10 @@ "sign_up": "Inscrever-se", "signTransaction": "Assinar transação", "signup_for_card_accept_terms": "Cadastre-se no cartão e aceite os termos.", - "silent_payments_scanning": "Scanear Pagamentos Silenciosos", + "silent_payments_scan_from_date": "Escanear a partir da data", + "silent_payments_scan_from_date_or_blockheight": "Por favor, insira a altura do bloco que deseja iniciar o escaneamento para obter pagamentos silenciosos ou use a data. Você pode escolher se a carteira continua digitalizando cada bloco ou verifica apenas a altura especificada.", + "silent_payments_scan_from_height": "Escanear a partir da altura do bloco", + "silent_payments_scanning": "Escanear Pagamentos Silenciosos", "slidable": "Deslizável", "sort_by": "Ordenar por", "spend_key_private": "Chave de gastos (privada)", @@ -648,6 +651,7 @@ "syncing_wallet_alert_title": "Sua carteira está sincronizando", "template": "Modelo", "template_name": "Nome do modelo", + "testnet_coins_no_value": "As moedas de teste não têm valor", "third_intro_content": "Yats também mora fora da Cake Wallet. Qualquer endereço de carteira na Terra pode ser substituído por um Yat!", "third_intro_title": "Yat joga bem com os outros", "thorchain_taproot_address_not_supported": "O provedor de Thorchain não suporta endereços de raiz de Tap. Altere o endereço ou selecione um provedor diferente.", @@ -807,4 +811,4 @@ "you_will_get": "Converter para", "you_will_send": "Converter de", "yy": "aa" -} +} \ No newline at end of file diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index c793ef6b12..74699d479b 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -611,6 +611,9 @@ "sign_up": "Зарегистрироваться", "signTransaction": "Подписать транзакцию", "signup_for_card_accept_terms": "Подпишитесь на карту и примите условия.", + "silent_payments_scan_from_date": "Сканирование с даты", + "silent_payments_scan_from_date_or_blockheight": "Пожалуйста, введите высоту блока, которую вы хотите начать сканирование для входящих молчаливых платежей, или вместо этого используйте дату. Вы можете выбрать, продолжает ли кошелек сканировать каждый блок или проверять только указанную высоту.", + "silent_payments_scan_from_height": "Сканирование с высоты блока", "silent_payments_scanning": "Сканирование безмолвных платежей", "slidable": "Скользящий", "sort_by": "Сортировать по", @@ -647,6 +650,7 @@ "syncing_wallet_alert_title": "Ваш кошелек синхронизируется", "template": "Шаблон", "template_name": "Имя Шаблона", + "testnet_coins_no_value": "Монеты теста не имеют значения", "third_intro_content": "Yat находятся за пределами Cake Wallet. Любой адрес кошелька на земле можно заменить на Yat!", "third_intro_title": "Yat хорошо взаимодействует с другими", "thorchain_taproot_address_not_supported": "Поставщик Thorchain не поддерживает адреса taproot. Пожалуйста, измените адрес или выберите другого поставщика.", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 1839cc930d..6c5ee98f4a 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -610,6 +610,9 @@ "sign_up": "สมัครสมาชิก", "signTransaction": "ลงนามในการทำธุรกรรม", "signup_for_card_accept_terms": "ลงทะเบียนสำหรับบัตรและยอมรับเงื่อนไข", + "silent_payments_scan_from_date": "สแกนตั้งแต่วันที่", + "silent_payments_scan_from_date_or_blockheight": "โปรดป้อนความสูงของบล็อกที่คุณต้องการเริ่มการสแกนสำหรับการชำระเงินแบบเงียบ ๆ หรือใช้วันที่แทน คุณสามารถเลือกได้ว่ากระเป๋าเงินยังคงสแกนทุกบล็อกหรือตรวจสอบความสูงที่ระบุเท่านั้น", + "silent_payments_scan_from_height": "สแกนจากความสูงของบล็อก", "silent_payments_scanning": "การสแกนการชำระเงินแบบเงียบ", "slidable": "เลื่อนได้", "sort_by": "เรียงตาม", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "กระเป๋าสตางค์ของคุณกำลังซิงค์", "template": "แบบฟอร์ม", "template_name": "ชื่อแม่แบบ", + "testnet_coins_no_value": "Testnet Coins ไม่มีค่า", "third_intro_content": "Yat อาศัยอยู่นอก Cake Wallet ด้วย ที่อยู่กระเป๋าใดๆ ทั่วโลกสามารถแทนด้วย Yat ได้อีกด้วย!", "third_intro_title": "Yat ปฏิบัติตนอย่างดีกับผู้อื่น", "thorchain_taproot_address_not_supported": "ผู้ให้บริการ Thorchain ไม่รองรับที่อยู่ taproot โปรดเปลี่ยนที่อยู่หรือเลือกผู้ให้บริการอื่น", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 4d3ef91c04..ca37f2f481 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -610,6 +610,9 @@ "sign_up": "Mag -sign up", "signTransaction": "Mag-sign Transaksyon", "signup_for_card_accept_terms": "Mag -sign up para sa card at tanggapin ang mga termino.", + "silent_payments_scan_from_date": "I -scan mula sa petsa", + "silent_payments_scan_from_date_or_blockheight": "Mangyaring ipasok ang taas ng block na nais mong simulan ang pag -scan para sa papasok na tahimik na pagbabayad, o, gamitin ang petsa sa halip. Maaari kang pumili kung ang pitaka ay patuloy na pag -scan sa bawat bloke, o suriin lamang ang tinukoy na taas.", + "silent_payments_scan_from_height": "I -scan mula sa taas ng block", "silent_payments_scanning": "Tahimik na pag -scan ng mga pagbabayad", "slidable": "Slidable", "sort_by": "Pag -uri -uriin sa pamamagitan ng", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "Ang iyong pitaka ay nag -sync", "template": "Template", "template_name": "Pangalan ng Template", + "testnet_coins_no_value": "Ang mga barya ng testnet ay walang halaga", "third_intro_content": "Ang mga yats ay nakatira sa labas ng cake wallet, din. Ang anumang address ng pitaka sa mundo ay maaaring mapalitan ng isang yat!", "third_intro_title": "Si Yat ay mahusay na gumaganap sa iba", "thorchain_taproot_address_not_supported": "Ang Tagabigay ng Thorchain ay hindi sumusuporta sa mga address ng taproot. Mangyaring baguhin ang address o pumili ng ibang provider.", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 55fbf6036b..e12853b92b 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -610,6 +610,9 @@ "sign_up": "Kaydol", "signTransaction": "İşlem İmzala", "signup_for_card_accept_terms": "Kart için kaydol ve koşulları kabul et.", + "silent_payments_scan_from_date": "Tarihten tarama", + "silent_payments_scan_from_date_or_blockheight": "Lütfen gelen sessiz ödemeler için taramaya başlamak istediğiniz blok yüksekliğini girin veya bunun yerine tarihi kullanın. Cüzdanın her bloğu taramaya devam edip etmediğini veya yalnızca belirtilen yüksekliği kontrol edip etmediğini seçebilirsiniz.", + "silent_payments_scan_from_height": "Blok yüksekliğinden tarama", "silent_payments_scanning": "Sessiz Ödemeler Taraması", "slidable": "kaydırılabilir", "sort_by": "Göre sırala", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "Cüzdanınız senkronize ediliyor", "template": "Şablon", "template_name": "şablon adı", + "testnet_coins_no_value": "TestNet paralarının değeri yok", "third_intro_content": "Yat'lar Cake Wallet'ın dışında da çalışabilir. Dünya üzerindeki herhangi bir cüzdan adresi Yat ile değiştirilebilir!", "third_intro_title": "Yat diğerleriyle iyi çalışır", "thorchain_taproot_address_not_supported": "Thorchain sağlayıcısı Taproot adreslerini desteklemiyor. Lütfen adresi değiştirin veya farklı bir sağlayıcı seçin.", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index d48c5eb3db..63e4242b18 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -611,6 +611,9 @@ "sign_up": "Зареєструватися", "signTransaction": "Підписати транзакцію", "signup_for_card_accept_terms": "Зареєструйтеся на картку та прийміть умови.", + "silent_payments_scan_from_date": "Сканувати з дати", + "silent_payments_scan_from_date_or_blockheight": "Введіть висоту блоку, яку ви хочете почати сканувати для вхідних мовчазних платежів, або скористайтеся датою замість цього. Ви можете вибрати, якщо гаманець продовжує сканувати кожен блок, або перевіряє лише вказану висоту.", + "silent_payments_scan_from_height": "Сканування від висоти блоку", "silent_payments_scanning": "Мовчазні платежі сканування", "slidable": "Розсувний", "sort_by": "Сортувати за", @@ -647,6 +650,7 @@ "syncing_wallet_alert_title": "Ваш гаманець синхронізується", "template": "Шаблон", "template_name": "Назва шаблону", + "testnet_coins_no_value": "Монети TestNet не мають значення", "third_intro_content": "Yat знаходиться за межами Cake Wallet. Будь-яку адресу гаманця на землі можна замінити на Yat!", "third_intro_title": "Yat добре взаємодіє з іншими", "thorchain_taproot_address_not_supported": "Постачальник Thorchain не підтримує адреси Taproot. Будь ласка, змініть адресу або виберіть іншого постачальника.", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index b5255bc150..171c2b0d28 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -612,6 +612,9 @@ "sign_up": "سائن اپ", "signTransaction": "۔ﮟﯾﺮﮐ ﻂﺨﺘﺳﺩ ﺮﭘ ﻦﯾﺩ ﻦﯿﻟ", "signup_for_card_accept_terms": "کارڈ کے لیے سائن اپ کریں اور شرائط کو قبول کریں۔", + "silent_payments_scan_from_date": "تاریخ سے اسکین کریں", + "silent_payments_scan_from_date_or_blockheight": "براہ کرم بلاک اونچائی میں داخل ہوں جس سے آپ آنے والی خاموش ادائیگیوں کے لئے اسکیننگ شروع کرنا چاہتے ہیں ، یا اس کے بجائے تاریخ کا استعمال کریں۔ آپ یہ منتخب کرسکتے ہیں کہ اگر پرس ہر بلاک کو اسکیننگ جاری رکھے ہوئے ہے ، یا صرف مخصوص اونچائی کی جانچ پڑتال کرتا ہے۔", + "silent_payments_scan_from_height": "بلاک اونچائی سے اسکین کریں", "silent_payments_scanning": "خاموش ادائیگی اسکیننگ", "slidable": "سلائیڈ ایبل", "sort_by": "ترتیب دیں", @@ -648,6 +651,7 @@ "syncing_wallet_alert_title": "آپ کا بٹوہ مطابقت پذیر ہو رہا ہے۔", "template": "سانچے", "template_name": "ٹیمپلیٹ کا نام", + "testnet_coins_no_value": "ٹیسٹ نیٹ سکے کی کوئی قیمت نہیں ہے", "third_intro_content": "Yats بھی Cake والیٹ سے باہر رہتے ہیں۔ زمین پر کسی بھی بٹوے کے پتے کو Yat سے تبدیل کیا جا سکتا ہے!", "third_intro_title": "Yat دوسروں کے ساتھ اچھی طرح کھیلتا ہے۔", "thorchain_taproot_address_not_supported": "تھورچین فراہم کنندہ ٹیپروٹ پتے کی حمایت نہیں کرتا ہے۔ براہ کرم پتہ تبدیل کریں یا ایک مختلف فراہم کنندہ کو منتخب کریں۔", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 3435f9ca13..463076ea09 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -611,6 +611,9 @@ "sign_up": "Forúkọ sílẹ̀", "signTransaction": "Wole Idunadura", "signup_for_card_accept_terms": "Ẹ f'orúkọ sílẹ̀ láti gba káàdì àti àjọrò.", + "silent_payments_scan_from_date": "Scan lati ọjọ", + "silent_payments_scan_from_date_or_blockheight": "Jọwọ tẹ giga idibo ti o fẹ bẹrẹ ọlọjẹ fun awọn sisanwo ipalọlọ, tabi, lo ọjọ dipo. O le yan ti apamọwọ naa tẹsiwaju nṣapẹẹrẹ gbogbo bulọọki, tabi ṣayẹwo nikan giga ti o sọ tẹlẹ.", + "silent_payments_scan_from_height": "Scan lati Iga Iga", "silent_payments_scanning": "Awọn sisanwo ipalọlọ", "slidable": "Slidable", "sort_by": "Sa pelu", @@ -647,6 +650,7 @@ "syncing_wallet_alert_title": "Apamọwọ rẹ n muṣiṣẹpọ", "template": "Àwòṣe", "template_name": "Orukọ Awoṣe", + "testnet_coins_no_value": "Awọn aṣọ irekọja ko ni iye", "third_intro_content": "A sì lè lo Yats níta Cake Wallet. A lè rọ́pò Àdírẹ́sì kankan àpamọ́wọ́ fún Yat!", "third_intro_title": "Àlàáfíà ni Yat àti àwọn ìmíìn jọ wà", "thorchain_taproot_address_not_supported": "Olupese Trockchain ko ṣe atilẹyin awọn adirẹsi Taproot. Jọwọ yi adirẹsi pada tabi yan olupese ti o yatọ.", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 22e74ce5b5..14ece96ddd 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -610,6 +610,9 @@ "sign_up": "注册", "signTransaction": "签署交易", "signup_for_card_accept_terms": "注册卡并接受条款。", + "silent_payments_scan_from_date": "从日期开始扫描", + "silent_payments_scan_from_date_or_blockheight": "请输入您要开始扫描输入静音付款的块高度,或者使用日期。您可以选择钱包是否继续扫描每个块,或仅检查指定的高度。", + "silent_payments_scan_from_height": "从块高度扫描", "silent_payments_scanning": "无声付款扫描", "slidable": "可滑动", "sort_by": "排序方式", @@ -646,6 +649,7 @@ "syncing_wallet_alert_title": "您的钱包正在同步", "template": "模板", "template_name": "模板名称", + "testnet_coins_no_value": "TestNet硬币没有价值", "third_intro_content": "Yats 也住在 Cake Wallet 之外。 地球上任何一個錢包地址都可以用一個Yat來代替!", "third_intro_title": "Yat 和別人玩得很好", "thorchain_taproot_address_not_supported": "Thorchain提供商不支持Taproot地址。请更改地址或选择其他提供商。", diff --git a/tool/configure.dart b/tool/configure.dart index 96891247c0..5b0ad99aa8 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -128,7 +128,7 @@ abstract class Bitcoin { List getAddresses(Object wallet); String getAddress(Object wallet); - List getSilentAddresses(Object wallet); + List getSilentAddresses(Object wallet); Future estimateFakeSendAllTxAmount(Object wallet, TransactionPriority priority); List getSubAddresses(Object wallet); @@ -149,14 +149,15 @@ abstract class Bitcoin { Future setAddressType(Object wallet, dynamic option); BitcoinReceivePageOption getSelectedAddressType(Object wallet); + List getBitcoinReceivePageOptions(); BitcoinAddressType getBitcoinAddressType(ReceivePageOption option); bool hasSelectedSilentPayments(Object wallet); - List getBitcoinReceivePageOptions(); bool isBitcoinReceivePageOption(ReceivePageOption option); BitcoinAddressType getOptionToType(ReceivePageOption option); bool hasTaprootInput(PendingTransaction pendingTransaction); bool getScanningActive(Object wallet); void setScanningActive(Object wallet, bool active); + bool isTestnet(Object wallet); } """; @@ -1052,7 +1053,8 @@ Future generatePubspec( final inputFile = File(pubspecOutputPath); final inputText = await inputFile.readAsString(); final inputLines = inputText.split('\n'); - final dependenciesIndex = inputLines.indexWhere((line) => line.toLowerCase().contains('dependencies:')); + final dependenciesIndex = + inputLines.indexWhere((line) => line.toLowerCase().contains('dependencies:')); var output = cwCore; if (hasMonero) { From 2b23ab1993e5ae5897f1c1eda47ee110d86c557c Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 5 Apr 2024 19:20:18 -0300 Subject: [PATCH 20/75] fix: invalid Object in sendData --- cw_bitcoin/lib/electrum_wallet.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 4cd2d9f226..617770661b 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -220,7 +220,7 @@ abstract class ElectrumWalletBase chainTip: currentChainTip, electrumClient: ElectrumClient(), transactionHistoryIds: transactionHistory.transactions.keys.toList(), - node: node!, + node: ScanNode(node!.uri, node!.useSSL), labels: walletAddresses.labels, )); @@ -1412,11 +1412,18 @@ abstract class ElectrumWalletBase } } +class ScanNode { + final Uri uri; + final bool? useSSL; + + ScanNode(this.uri, this.useSSL); +} + class ScanData { final SendPort sendPort; final SilentPaymentOwner silentAddress; final int height; - final Node node; + final ScanNode node; final BasedUtxoNetwork network; final int chainTip; final ElectrumClient electrumClient; From 487be52c891969d69ae2dffded4fd9fc708b50e2 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 8 Apr 2024 18:46:33 -0300 Subject: [PATCH 21/75] feat: improve addresses page & address book displays --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 73 ++++++++++++++++++- lib/bitcoin/cw_bitcoin.dart | 13 +++- .../present_receive_option_picker.dart | 17 ++++- lib/src/screens/receive/receive_page.dart | 43 +++++++---- .../contact_list/contact_list_view_model.dart | 14 +++- .../wallet_address_list_header.dart | 5 +- .../wallet_address_list_item.dart | 9 ++- .../wallet_address_list_view_model.dart | 59 ++++++++++++--- res/values/strings_ar.arb | 4 +- res/values/strings_bg.arb | 4 +- res/values/strings_cs.arb | 4 +- res/values/strings_de.arb | 4 +- res/values/strings_en.arb | 4 +- res/values/strings_es.arb | 4 +- res/values/strings_fr.arb | 4 +- res/values/strings_ha.arb | 4 +- res/values/strings_hi.arb | 4 +- res/values/strings_hr.arb | 4 +- res/values/strings_id.arb | 4 +- res/values/strings_it.arb | 4 +- res/values/strings_ja.arb | 4 +- res/values/strings_ko.arb | 4 +- res/values/strings_my.arb | 4 +- res/values/strings_nl.arb | 4 +- res/values/strings_pl.arb | 4 +- res/values/strings_pt.arb | 4 +- res/values/strings_ru.arb | 4 +- res/values/strings_th.arb | 4 +- res/values/strings_tl.arb | 4 +- res/values/strings_tr.arb | 4 +- res/values/strings_uk.arb | 4 +- res/values/strings_ur.arb | 4 +- res/values/strings_yo.arb | 4 +- res/values/strings_zh.arb | 4 +- tool/configure.dart | 3 +- 35 files changed, 272 insertions(+), 68 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index c019da9a4c..1dd11bad3e 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -266,7 +266,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @action BaseBitcoinAddressRecord generateNewAddress({String label = ''}) { if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) { - currentSilentAddressIndex += 1; + final currentSilentAddressIndex = silentAddresses + .where((addressRecord) => addressRecord.type != SegwitAddresType.p2tr) + .length + + 1; + + this.currentSilentAddressIndex = currentSilentAddressIndex; final address = BitcoinSilentPaymentAddressRecord( silentAddress!.toLabeledSilentPaymentAddress(currentSilentAddressIndex).toString(), @@ -309,12 +314,74 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { Future updateAddressesInBox() async { try { addressesMap.clear(); - addressesMap[address] = ''; + addressesMap[address] = 'Active'; allAddressesMap.clear(); _addresses.forEach((addressRecord) { allAddressesMap[addressRecord.address] = addressRecord.name; }); + + final lastP2wpkh = _addresses + .where((addressRecord) => + _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh)) + .toList() + .last; + if (lastP2wpkh.address != address) { + addressesMap[lastP2wpkh.address] = 'P2WPKH'; + } else { + addressesMap[address] = 'Active - P2WPKH'; + } + + final lastP2pkh = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, P2pkhAddressType.p2pkh)); + if (lastP2pkh.address != address) { + addressesMap[lastP2pkh.address] = 'P2PKH'; + } else { + addressesMap[address] = 'Active - P2PKH'; + } + + final lastP2sh = _addresses.firstWhere((addressRecord) => + _isUnusedReceiveAddressByType(addressRecord, P2shAddressType.p2wpkhInP2sh)); + if (lastP2sh.address != address) { + addressesMap[lastP2sh.address] = 'P2SH'; + } else { + addressesMap[address] = 'Active - P2SH'; + } + + final lastP2tr = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2tr)); + if (lastP2tr.address != address) { + addressesMap[lastP2tr.address] = 'P2TR'; + } else { + addressesMap[address] = 'Active - P2TR'; + } + + final lastP2wsh = _addresses.firstWhere( + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wsh)); + if (lastP2wsh.address != address) { + addressesMap[lastP2wsh.address] = 'P2WSH'; + } else { + addressesMap[address] = 'Active - P2WSH'; + } + + silentAddresses.forEach((addressRecord) { + if (addressRecord.type != SilentPaymentsAddresType.p2sp) { + return; + } + + if (addressRecord.address != address) { + print([ + addressRecord.address, + addressRecord.name.isEmpty ? "Silent Payments" : addressRecord.name + ]); + addressesMap[addressRecord.address] = addressRecord.name.isEmpty + ? "Silent Payments" + : "Silent Payments - " + addressRecord.name; + } else { + addressesMap[address] = 'Active - Silent Payments'; + } + }); + await saveAddressesInBox(); } catch (e) { print(e.toString()); @@ -472,4 +539,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { bitcoin.HDWallet _getHd(bool isHidden) => isHidden ? sideHd : mainHd; bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type; + bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => + !addr.isHidden && !addr.isUsed && addr.type == type; } diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index f019a91732..bcbb0bc07b 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -297,9 +297,18 @@ class CWBitcoin extends Bitcoin { ); } - List getSilentAddresses(Object wallet) { + List getSilentPaymentAddresses(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; - return bitcoinWallet.walletAddresses.silentAddresses; + return bitcoinWallet.walletAddresses.silentAddresses + .where((addr) => addr.type != SegwitAddresType.p2tr) + .toList(); + } + + List getSilentPaymentReceivedAddresses(Object wallet) { + final bitcoinWallet = wallet as ElectrumWallet; + return bitcoinWallet.walletAddresses.silentAddresses + .where((addr) => addr.type == SegwitAddresType.p2tr) + .toList(); } bool isBitcoinReceivePageOption(ReceivePageOption option) { diff --git a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart index 0ae750cf99..bebb58107e 100644 --- a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart +++ b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart @@ -45,7 +45,13 @@ class PresentReceiveOptionPicker extends StatelessWidget { fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: 'Lato', color: color), ), Observer( - builder: (_) => Text(receiveOptionViewModel.selectedReceiveOption.toString(), + builder: (_) => Text( + receiveOptionViewModel.selectedReceiveOption + .toString() + .replaceAll(RegExp(r'silent payments', caseSensitive: false), + S.current.silent_payments) + .replaceAll( + RegExp(r'default', caseSensitive: false), S.current.string_default), style: TextStyle(fontSize: 10.0, fontWeight: FontWeight.w500, color: color))) ], ), @@ -101,7 +107,14 @@ class PresentReceiveOptionPicker extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(option.toString(), + Text( + option + .toString() + .replaceAll( + RegExp(r'silent payments', caseSensitive: false), + S.current.silent_payments) + .replaceAll(RegExp(r'default', caseSensitive: false), + S.current.string_default), textAlign: TextAlign.left, style: textSmall( color: Theme.of(context) diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index e856d32008..6090bfdd8f 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -164,20 +164,27 @@ class ReceivePage extends BasePage { } if (item is WalletAddressListHeader) { + final hasTitle = item.title != null; + cell = HeaderTile( - title: S.of(context).addresses, - walletAddressListViewModel: addressListViewModel, - showTrailingButton: - !addressListViewModel.isAutoGenerateSubaddressEnabled, - showSearchButton: true, - trailingButtonTap: () => - Navigator.of(context).pushNamed(Routes.newSubaddress), - trailingIcon: Icon( - Icons.add, - size: 20, - color: - Theme.of(context).extension()!.iconsColor, - )); + title: hasTitle ? item.title! : S.of(context).addresses, + walletAddressListViewModel: addressListViewModel, + showTrailingButton: + !addressListViewModel.isAutoGenerateSubaddressEnabled && + !hasTitle, + showSearchButton: true, + trailingButtonTap: () => + Navigator.of(context).pushNamed(Routes.newSubaddress), + trailingIcon: hasTitle + ? null + : Icon( + Icons.add, + size: 20, + color: Theme.of(context) + .extension()! + .iconsColor, + ), + ); } if (item is WalletAddressListItem) { @@ -204,9 +211,13 @@ class ReceivePage extends BasePage { hasBalance: addressListViewModel.isElectrumWallet, backgroundColor: backgroundColor, textColor: textColor, - onTap: (_) => addressListViewModel.setAddress(item), - onEdit: () => Navigator.of(context) - .pushNamed(Routes.newSubaddress, arguments: item)); + onTap: item.isOneTimeReceiveAddress == true + ? null + : (_) => addressListViewModel.setAddress(item), + onEdit: item.isOneTimeReceiveAddress == true + ? null + : () => Navigator.of(context) + .pushNamed(Routes.newSubaddress, arguments: item)); }); } diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index 6c3169be1f..f811165066 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/contact_base.dart'; import 'package:cake_wallet/entities/wallet_contact.dart'; +import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; @@ -40,14 +41,17 @@ abstract class ContactListViewModelBase with Store { }); } else if (info.addresses?.isNotEmpty == true) { info.addresses!.forEach((address, label) { + if (label.isEmpty) { + return; + } final name = _createName(info.name, label); walletContacts.add(WalletContact( address, name, - walletTypeToCryptoCurrency(info.type), + walletTypeToCryptoCurrency(info.type, + isTestnet: + info.network == null ? false : info.network!.toLowerCase().contains("testnet")), )); - // Only one contact address per wallet - return; }); } else if (info.address != null) { walletContacts.add(WalletContact( @@ -64,7 +68,9 @@ abstract class ContactListViewModelBase with Store { } String _createName(String walletName, String label) { - return label.isNotEmpty ? '$walletName ($label)' : walletName; + return label.isNotEmpty + ? '$walletName (${label.replaceAll(RegExp(r'active', caseSensitive: false), S.current.active).replaceAll(RegExp(r'silent payments', caseSensitive: false), S.current.silent_payments)})' + : walletName; } final bool isAutoGenerateEnabled; diff --git a/lib/view_model/wallet_address_list/wallet_address_list_header.dart b/lib/view_model/wallet_address_list/wallet_address_list_header.dart index 0f19166b7c..a094014952 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_header.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_header.dart @@ -1,3 +1,6 @@ import 'package:cake_wallet/utils/list_item.dart'; -class WalletAddressListHeader extends ListItem {} \ No newline at end of file +class WalletAddressListHeader extends ListItem { + final String? title; + WalletAddressListHeader({this.title}); +} diff --git a/lib/view_model/wallet_address_list/wallet_address_list_item.dart b/lib/view_model/wallet_address_list/wallet_address_list_item.dart index 1152f1404b..ebcf541073 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_item.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_item.dart @@ -8,8 +8,9 @@ class WalletAddressListItem extends ListItem { this.name, this.txCount, this.balance, - this.isChange = false}) - : super(); + this.isChange = false, + this.isOneTimeReceiveAddress = false, + }) : super(); final int? id; final bool isPrimary; @@ -18,7 +19,9 @@ class WalletAddressListItem extends ListItem { final int? txCount; final String? balance; final bool isChange; + final bool? isOneTimeReceiveAddress; @override String toString() => name ?? address; -} \ No newline at end of file +} + diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index c1c275a875..9289701fc9 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -318,20 +318,57 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo } if (isElectrumWallet) { - final addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) { - final isPrimary = subaddress.id == 0; + if (bitcoin!.hasSelectedSilentPayments(wallet)) { + final addressItems = bitcoin!.getSilentPaymentAddresses(wallet).map((address) { + final isPrimary = address.index == 0; - return WalletAddressListItem( - id: subaddress.id, + return WalletAddressListItem( + id: address.index, isPrimary: isPrimary, - name: subaddress.name, - address: subaddress.address, - txCount: subaddress.txCount, + name: address.name, + address: address.address, + txCount: address.txCount, balance: AmountConverter.amountIntToString( - walletTypeToCryptoCurrency(type), subaddress.balance), - isChange: subaddress.isChange); - }); - addressList.addAll(addressItems); + walletTypeToCryptoCurrency(type), address.balance), + isChange: address.isHidden, + ); + }); + addressList.addAll(addressItems); + addressList.add(WalletAddressListHeader(title: S.current.received)); + + final receivedAddressItems = + bitcoin!.getSilentPaymentReceivedAddresses(wallet).map((address) { + final isPrimary = address.index == 0; + + return WalletAddressListItem( + id: address.index, + isPrimary: isPrimary, + name: address.name, + address: address.address, + txCount: address.txCount, + balance: AmountConverter.amountIntToString( + walletTypeToCryptoCurrency(type), address.balance), + isChange: address.isHidden, + isOneTimeReceiveAddress: true, + ); + }); + addressList.addAll(receivedAddressItems); + } else { + final addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) { + final isPrimary = subaddress.id == 0; + + return WalletAddressListItem( + id: subaddress.id, + isPrimary: isPrimary, + name: subaddress.name, + address: subaddress.address, + txCount: subaddress.txCount, + balance: AmountConverter.amountIntToString( + walletTypeToCryptoCurrency(type), subaddress.balance), + isChange: subaddress.isChange); + }); + addressList.addAll(addressItems); + } } if (wallet.type == WalletType.ethereum) { diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index c40cb42296..2e8b9ca682 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -618,6 +618,7 @@ "sign_up": "اشتراك", "signTransaction": " ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ", "signup_for_card_accept_terms": "قم بالتسجيل للحصول على البطاقة وقبول الشروط.", + "silent_payments": "مدفوعات صامتة", "silent_payments_scan_from_date": "فحص من التاريخ", "silent_payments_scan_from_date_or_blockheight": "يرجى إدخال ارتفاع الكتلة الذي تريد بدء المسح الضوئي للمدفوعات الصامتة الواردة ، أو استخدام التاريخ بدلاً من ذلك. يمكنك اختيار ما إذا كانت المحفظة تواصل مسح كل كتلة ، أو تتحقق فقط من الارتفاع المحدد.", "silent_payments_scan_from_height": "فحص من ارتفاع الكتلة", @@ -627,6 +628,7 @@ "spend_key_private": "مفتاح الإنفاق (خاص)", "spend_key_public": "مفتاح الإنفاق (عام)", "status": "الحالة:", + "string_default": "تقصير", "subaddress_title": "قائمة العناوين الفرعية", "subaddresses": "العناوين الفرعية", "submit_request": "تقديم طلب", @@ -816,4 +818,4 @@ "you_will_get": "حول الى", "you_will_send": "تحويل من", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 7e586ecdcf..cde8ca2600 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -618,6 +618,7 @@ "sign_up": "Регистрация", "signTransaction": "Подпишете транзакция", "signup_for_card_accept_terms": "Регистрайте се за картата и приемете условията.", + "silent_payments": "Мълчаливи плащания", "silent_payments_scan_from_date": "Сканиране от дата", "silent_payments_scan_from_date_or_blockheight": "Моля, въведете височината на блока, която искате да започнете да сканирате за входящи безшумни плащания, или вместо това използвайте датата. Можете да изберете дали портфейлът продължава да сканира всеки блок или проверява само определената височина.", "silent_payments_scan_from_height": "Сканиране от височината на блока", @@ -627,6 +628,7 @@ "spend_key_private": "Spend key (таен)", "spend_key_public": "Spend key (публичен)", "status": "Статус: ", + "string_default": "По подразбиране", "subaddress_title": "Лист от подадреси", "subaddresses": "Подадреси", "submit_request": "изпращане на заявка", @@ -816,4 +818,4 @@ "you_will_get": "Обръщане в", "you_will_send": "Обръщане от", "yy": "гг" -} \ No newline at end of file +} diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 85a3b50891..29b193e8a1 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -618,6 +618,7 @@ "sign_up": "Registrovat se", "signTransaction": "Podepsat transakci", "signup_for_card_accept_terms": "Zaregistrujte se pro kartu a souhlaste s podmínkami.", + "silent_payments": "Tiché platby", "silent_payments_scan_from_date": "Skenovat od data", "silent_payments_scan_from_date_or_blockheight": "Zadejte výšku bloku, kterou chcete začít skenovat, zda jsou přicházející tiché platby, nebo místo toho použijte datum. Můžete si vybrat, zda peněženka pokračuje v skenování každého bloku nebo zkontroluje pouze zadanou výšku.", "silent_payments_scan_from_height": "Skenování z výšky bloku", @@ -627,6 +628,7 @@ "spend_key_private": "Klíč pro platby (soukromý)", "spend_key_public": "Klíč pro platby (veřejný)", "status": "Status: ", + "string_default": "Výchozí", "subaddress_title": "Seznam subadres", "subaddresses": "Subadresy", "submit_request": "odeslat požadavek", @@ -816,4 +818,4 @@ "you_will_get": "Směnit na", "you_will_send": "Směnit z", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 9dd7391789..0ab2be8e28 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -619,6 +619,7 @@ "sign_up": "Anmelden", "signTransaction": "Transaktion unterzeichnen", "signup_for_card_accept_terms": "Melden Sie sich für die Karte an und akzeptieren Sie die Bedingungen.", + "silent_payments": "Stille Zahlungen", "silent_payments_scan_from_date": "Scan ab Datum", "silent_payments_scan_from_date_or_blockheight": "Bitte geben Sie die Blockhöhe ein, die Sie für eingehende stille Zahlungen scannen möchten, oder verwenden Sie stattdessen das Datum. Sie können wählen, ob die Brieftasche jeden Block scannt oder nur die angegebene Höhe überprüft.", "silent_payments_scan_from_height": "Scan aus der Blockhöhe scannen", @@ -628,6 +629,7 @@ "spend_key_private": "Spend Key (geheim)", "spend_key_public": "Spend Key (öffentlich)", "status": "Status: ", + "string_default": "Standard", "subaddress_title": "Unteradressenliste", "subaddresses": "Unteradressen", "submit_request": "Eine Anfrage stellen", @@ -819,4 +821,4 @@ "you_will_get": "Konvertieren zu", "you_will_send": "Konvertieren von", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 87da283b86..86cd4452da 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -618,6 +618,7 @@ "sign_up": "Sign Up", "signTransaction": "Sign Transaction", "signup_for_card_accept_terms": "Sign up for the card and accept the terms.", + "silent_payments": "Silent Payments", "silent_payments_scan_from_date": "Scan from date", "silent_payments_scan_from_date_or_blockheight": "Please enter the block height you want to start scanning for incoming silent payments, or, use the date instead. You can choose if the wallet continues scanning every block, or checks only the specified height.", "silent_payments_scan_from_height": "Scan from block height", @@ -627,6 +628,7 @@ "spend_key_private": "Spend key (private)", "spend_key_public": "Spend key (public)", "status": "Status: ", + "string_default": "Default", "subaddress_title": "Subaddress list", "subaddresses": "Subaddresses", "submit_request": "submit a request", @@ -816,4 +818,4 @@ "you_will_get": "Convert to", "you_will_send": "Convert from", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index c773c8528e..61270e3760 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -619,6 +619,7 @@ "sign_up": "Registrarse", "signTransaction": "Firmar transacción", "signup_for_card_accept_terms": "Regístrese para obtener la tarjeta y acepte los términos.", + "silent_payments": "Pagos silenciosos", "silent_payments_scan_from_date": "Escanear desde la fecha", "silent_payments_scan_from_date_or_blockheight": "Ingrese la altura del bloque que desea comenzar a escanear para pagos silenciosos entrantes, o use la fecha en su lugar. Puede elegir si la billetera continúa escaneando cada bloque, o verifica solo la altura especificada.", "silent_payments_scan_from_height": "Escanear desde la altura del bloque", @@ -628,6 +629,7 @@ "spend_key_private": "Spend clave (privado)", "spend_key_public": "Spend clave (público)", "status": "Estado: ", + "string_default": "Por defecto", "subaddress_title": "Lista de subdirecciones", "subaddresses": "Subdirecciones", "submit_request": "presentar una solicitud", @@ -817,4 +819,4 @@ "you_will_get": "Convertir a", "you_will_send": "Convertir de", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 3423a08e7e..7295e26c2b 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -618,6 +618,7 @@ "sign_up": "S'inscrire", "signTransaction": "Signer une transaction", "signup_for_card_accept_terms": "Inscrivez-vous pour la carte et acceptez les conditions.", + "silent_payments": "Paiements silencieux", "silent_payments_scan_from_date": "Analyser à partir de la date", "silent_payments_scan_from_date_or_blockheight": "Veuillez saisir la hauteur du bloc que vous souhaitez commencer à scanner pour les paiements silencieux entrants, ou utilisez la date à la place. Vous pouvez choisir si le portefeuille continue de numériser chaque bloc ou ne vérifie que la hauteur spécifiée.", "silent_payments_scan_from_height": "Scan à partir de la hauteur du bloc", @@ -627,6 +628,7 @@ "spend_key_private": "Clef de dépense (spend key) (privée)", "spend_key_public": "Clef de dépense (spend key) (publique)", "status": "Statut : ", + "string_default": "Défaut", "subaddress_title": "Liste des sous-adresses", "subaddresses": "Sous-adresses", "submit_request": "soumettre une requête", @@ -816,4 +818,4 @@ "you_will_get": "Convertir vers", "you_will_send": "Convertir depuis", "yy": "AA" -} \ No newline at end of file +} diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 9bcdc35667..e2f9537903 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -620,6 +620,7 @@ "sign_up": "Shiga", "signTransaction": "Sa hannu Ma'amala", "signup_for_card_accept_terms": "Yi rajista don katin kuma karɓi sharuɗɗan.", + "silent_payments": "Biya silent", "silent_payments_scan_from_date": "Scan daga kwanan wata", "silent_payments_scan_from_date_or_blockheight": "Da fatan za a shigar da toshe wurin da kake son fara bincika don biyan silins mai shigowa, ko, yi amfani da kwanan wata. Zaka iya zabar idan walat ɗin ya ci gaba da bincika kowane toshe, ko duba tsinkaye da aka ƙayyade.", "silent_payments_scan_from_height": "Scan daga tsayin daka", @@ -629,6 +630,7 @@ "spend_key_private": "makullin biya (maɓallin kalmar sirri)", "spend_key_public": "makullin biya (maɓallin jama'a)", "status": "Matsayi:", + "string_default": "Ƙin cika alƙawari", "subaddress_title": "Jagorar subaddress", "subaddresses": "Subaddresses", "submit_request": "gabatar da bukata", @@ -818,4 +820,4 @@ "you_will_get": "Maida zuwa", "you_will_send": "Maida daga", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 8de7c421b5..37b2e22f29 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -620,6 +620,7 @@ "sign_up": "साइन अप करें", "signTransaction": "लेन-देन पर हस्ताक्षर करें", "signup_for_card_accept_terms": "कार्ड के लिए साइन अप करें और शर्तें स्वीकार करें।", + "silent_payments": "मूक भुगतान", "silent_payments_scan_from_date": "तिथि से स्कैन करना", "silent_payments_scan_from_date_or_blockheight": "कृपया उस ब्लॉक ऊंचाई दर्ज करें जिसे आप आने वाले मूक भुगतान के लिए स्कैन करना शुरू करना चाहते हैं, या, इसके बजाय तारीख का उपयोग करें। आप चुन सकते हैं कि क्या वॉलेट हर ब्लॉक को स्कैन करना जारी रखता है, या केवल निर्दिष्ट ऊंचाई की जांच करता है।", "silent_payments_scan_from_height": "ब्लॉक ऊंचाई से स्कैन करें", @@ -629,6 +630,7 @@ "spend_key_private": "खर्च करना (निजी)", "spend_key_public": "खर्च करना (जनता)", "status": "स्थिति: ", + "string_default": "गलती करना", "subaddress_title": "उपखंड सूची", "subaddresses": "उप पते", "submit_request": "एक अनुरोध सबमिट करें", @@ -818,4 +820,4 @@ "you_will_get": "में बदलें", "you_will_send": "से रूपांतरित करें", "yy": "वाईवाई" -} \ No newline at end of file +} diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index dd173e0fb2..7a570e4380 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -618,6 +618,7 @@ "sign_up": "Prijavite se", "signTransaction": "Potpišite transakciju", "signup_for_card_accept_terms": "Prijavite se za karticu i prihvatite uvjete.", + "silent_payments": "Tiha plaćanja", "silent_payments_scan_from_date": "Skeniranje iz datuma", "silent_payments_scan_from_date_or_blockheight": "Unesite visinu bloka koju želite započeti skeniranje za dolazna tiha plaćanja ili umjesto toga upotrijebite datum. Možete odabrati da li novčanik nastavlja skenirati svaki blok ili provjerava samo navedenu visinu.", "silent_payments_scan_from_height": "Skeniranje s visine bloka", @@ -627,6 +628,7 @@ "spend_key_private": "Spend key (privatni)", "spend_key_public": "Spend key (javni)", "status": "Status: ", + "string_default": "Zadano", "subaddress_title": "Lista podadresa", "subaddresses": "Podadrese", "submit_request": "podnesi zahtjev", @@ -816,4 +818,4 @@ "you_will_get": "Razmijeni u", "you_will_send": "Razmijeni iz", "yy": "GG" -} \ No newline at end of file +} diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 8ccb00b5d5..f12416b5e4 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -621,6 +621,7 @@ "sign_up": "Daftar", "signTransaction": "Tandatangani Transaksi", "signup_for_card_accept_terms": "Daftar untuk kartu dan terima syarat dan ketentuan.", + "silent_payments": "Pembayaran diam", "silent_payments_scan_from_date": "Pindai dari tanggal", "silent_payments_scan_from_date_or_blockheight": "Harap masukkan ketinggian blok yang ingin Anda mulai pemindaian untuk pembayaran diam yang masuk, atau, gunakan tanggal sebagai gantinya. Anda dapat memilih jika dompet terus memindai setiap blok, atau memeriksa hanya ketinggian yang ditentukan.", "silent_payments_scan_from_height": "Pindai dari Tinggi Blok", @@ -630,6 +631,7 @@ "spend_key_private": "Kunci pengeluaran (privat)", "spend_key_public": "Kunci pengeluaran (publik)", "status": "Status: ", + "string_default": "Bawaan", "subaddress_title": "Daftar sub-alamat", "subaddresses": "Sub-alamat", "submit_request": "kirim permintaan", @@ -819,4 +821,4 @@ "you_will_get": "Konversi ke", "you_will_send": "Konversi dari", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 5045489573..750a72d9aa 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -620,6 +620,7 @@ "sign_up": "Registrati", "signTransaction": "Firma la transazione", "signup_for_card_accept_terms": "Registrati per la carta e accetta i termini.", + "silent_payments": "Pagamenti silenziosi", "silent_payments_scan_from_date": "Scansionare dalla data", "silent_payments_scan_from_date_or_blockheight": "Inserisci l'altezza del blocco che si desidera iniziare la scansione per i pagamenti silenziosi in arrivo o, utilizza invece la data. Puoi scegliere se il portafoglio continua a scansionare ogni blocco o controlla solo l'altezza specificata.", "silent_payments_scan_from_height": "Scansione dall'altezza del blocco", @@ -629,6 +630,7 @@ "spend_key_private": "Chiave di spesa (privata)", "spend_key_public": "Chiave di spesa (pubblica)", "status": "Stato: ", + "string_default": "Predefinito", "subaddress_title": "Lista sottoindirizzi", "subaddresses": "Sottoindirizzi", "submit_request": "invia una richiesta", @@ -819,4 +821,4 @@ "you_will_get": "Converti a", "you_will_send": "Conveti da", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index cb05c1b894..2c65216938 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -619,6 +619,7 @@ "sign_up": "サインアップ", "signTransaction": "トランザクションに署名する", "signup_for_card_accept_terms": "カードにサインアップして、利用規約に同意してください。", + "silent_payments": "サイレント支払い", "silent_payments_scan_from_date": "日付からスキャンします", "silent_payments_scan_from_date_or_blockheight": "着信のサイレント決済のためにスキャンを開始するブロックの高さを入力するか、代わりに日付を使用してください。ウォレットがすべてのブロックをスキャンし続けるか、指定された高さのみをチェックするかどうかを選択できます。", "silent_payments_scan_from_height": "ブロックの高さからスキャンします", @@ -628,6 +629,7 @@ "spend_key_private": "キーを使う (プライベート)", "spend_key_public": "キーを使う (パブリック)", "status": "状態: ", + "string_default": "デフォルト", "subaddress_title": "サブアドレス一覧", "subaddresses": "サブアドレス", "submit_request": "リクエストを送信する", @@ -817,4 +819,4 @@ "you_will_get": "に変換", "you_will_send": "から変換", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 788b8fba0d..dc68515c8d 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -619,6 +619,7 @@ "sign_up": "가입", "signTransaction": "거래 서명", "signup_for_card_accept_terms": "카드에 가입하고 약관에 동의합니다.", + "silent_payments": "조용한 지불", "silent_payments_scan_from_date": "날짜부터 스캔하십시오", "silent_payments_scan_from_date_or_blockheight": "들어오는 사일런트 결제를 위해 스캔을 시작하려는 블록 높이를 입력하거나 대신 날짜를 사용하십시오. 지갑이 모든 블록을 계속 스캔하는지 여부를 선택하거나 지정된 높이 만 확인할 수 있습니다.", "silent_payments_scan_from_height": "블록 높이에서 스캔하십시오", @@ -628,6 +629,7 @@ "spend_key_private": "지출 키 (은밀한)", "spend_key_public": "지출 키 (공공의)", "status": "지위: ", + "string_default": "기본", "subaddress_title": "하위 주소 목록", "subaddresses": "하위 주소", "submit_request": "요청을 제출", @@ -818,4 +820,4 @@ "you_will_send": "다음에서 변환", "YY": "YY", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 07897bb252..d4164b4547 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -618,6 +618,7 @@ "sign_up": "ဆိုင်းအပ်", "signTransaction": "ငွေလွှဲဝင်ပါ။", "signup_for_card_accept_terms": "ကတ်အတွက် စာရင်းသွင်းပြီး စည်းကမ်းချက်များကို လက်ခံပါ။", + "silent_payments": "အသံတိတ်ငွေပေးချေမှု", "silent_payments_scan_from_date": "ရက်စွဲမှစကင်ဖတ်ပါ", "silent_payments_scan_from_date_or_blockheight": "ကျေးဇူးပြု. သင်ဝင်လာသောအသံတိတ်ငွေပေးချေမှုအတွက်သင်စကင်ဖတ်စစ်ဆေးလိုသည့်အမြင့်ကိုဖြည့်ပါ။ သို့မဟုတ်နေ့စွဲကိုသုံးပါ။ Wallet သည်လုပ်ကွက်တိုင်းကိုဆက်လက်စကင်ဖတ်စစ်ဆေးပါကသို့မဟုတ်သတ်မှတ်ထားသောအမြင့်ကိုသာစစ်ဆေးပါကသင်ရွေးချယ်နိုင်သည်။", "silent_payments_scan_from_height": "ပိတ်ပင်တားဆီးမှုအမြင့်ကနေ scan", @@ -627,6 +628,7 @@ "spend_key_private": "သော့သုံးရန် (သီးသန့်)", "spend_key_public": "သုံးစွဲရန်သော့ (အများပြည်သူ)", "status": "အခြေအနေ:", + "string_default": "ပျက်ကွက်ခြင်း", "subaddress_title": "လိပ်စာစာရင်း", "subaddresses": "လိပ်စာများ", "submit_request": "တောင်းဆိုချက်တစ်ခုတင်ပြပါ။", @@ -816,4 +818,4 @@ "you_will_get": "သို့ပြောင်းပါ။", "you_will_send": "မှပြောင်းပါ။", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 2ebc0cb930..cdd50981e7 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -618,6 +618,7 @@ "sign_up": "Aanmelden", "signTransaction": "Transactie ondertekenen", "signup_for_card_accept_terms": "Meld je aan voor de kaart en accepteer de voorwaarden.", + "silent_payments": "Stille betalingen", "silent_payments_scan_from_date": "Scan vanaf datum", "silent_payments_scan_from_date_or_blockheight": "Voer de blokhoogte in die u wilt beginnen met scannen op inkomende stille betalingen, of gebruik in plaats daarvan de datum. U kunt kiezen of de portemonnee elk blok blijft scannen of alleen de opgegeven hoogte controleert.", "silent_payments_scan_from_height": "Scan van blokhoogte", @@ -627,6 +628,7 @@ "spend_key_private": "Sleutel uitgeven (privaat)", "spend_key_public": "Sleutel uitgeven (openbaar)", "status": "Staat: ", + "string_default": "Standaard", "subaddress_title": "Subadreslijst", "subaddresses": "Subadressen", "submit_request": "een verzoek indienen", @@ -817,4 +819,4 @@ "you_will_get": "Converteren naar", "you_will_send": "Converteren van", "yy": "JJ" -} \ No newline at end of file +} diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index bc41c9b11e..cde8cad380 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -618,6 +618,7 @@ "sign_up": "Zarejestruj się", "signTransaction": "Podpisz transakcję", "signup_for_card_accept_terms": "Zarejestruj się, aby otrzymać kartę i zaakceptuj warunki.", + "silent_payments": "Ciche płatności", "silent_payments_scan_from_date": "Skanuj z daty", "silent_payments_scan_from_date_or_blockheight": "Wprowadź wysokość bloku, którą chcesz rozpocząć skanowanie w poszukiwaniu cichej płatności lub zamiast tego skorzystaj z daty. Możesz wybrać, czy portfel kontynuuje skanowanie każdego bloku, lub sprawdza tylko określoną wysokość.", "silent_payments_scan_from_height": "Skanuj z wysokości bloku", @@ -627,6 +628,7 @@ "spend_key_private": "Klucz prywatny", "spend_key_public": "Klucz publiczny", "status": "Status: ", + "string_default": "Domyślny", "subaddress_title": "Lista podadresów", "subaddresses": "Podadresy", "submit_request": "Złóż wniosek", @@ -816,4 +818,4 @@ "you_will_get": "Konwertuj na", "you_will_send": "Konwertuj z", "yy": "RR" -} \ No newline at end of file +} diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index d440177735..168b0312a1 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -620,6 +620,7 @@ "sign_up": "Inscrever-se", "signTransaction": "Assinar transação", "signup_for_card_accept_terms": "Cadastre-se no cartão e aceite os termos.", + "silent_payments": "Pagamentos silenciosos", "silent_payments_scan_from_date": "Escanear a partir da data", "silent_payments_scan_from_date_or_blockheight": "Por favor, insira a altura do bloco que deseja iniciar o escaneamento para obter pagamentos silenciosos ou use a data. Você pode escolher se a carteira continua digitalizando cada bloco ou verifica apenas a altura especificada.", "silent_payments_scan_from_height": "Escanear a partir da altura do bloco", @@ -629,6 +630,7 @@ "spend_key_private": "Chave de gastos (privada)", "spend_key_public": "Chave de gastos (pública)", "status": "Status: ", + "string_default": "Padrão", "subaddress_title": "Sub-endereços", "subaddresses": "Sub-endereços", "submit_request": "enviar um pedido", @@ -819,4 +821,4 @@ "you_will_get": "Converter para", "you_will_send": "Converter de", "yy": "aa" -} \ No newline at end of file +} diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 99cc385949..9a8f6563a6 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -619,6 +619,7 @@ "sign_up": "Зарегистрироваться", "signTransaction": "Подписать транзакцию", "signup_for_card_accept_terms": "Подпишитесь на карту и примите условия.", + "silent_payments": "Молчаливые платежи", "silent_payments_scan_from_date": "Сканирование с даты", "silent_payments_scan_from_date_or_blockheight": "Пожалуйста, введите высоту блока, которую вы хотите начать сканирование для входящих молчаливых платежей, или вместо этого используйте дату. Вы можете выбрать, продолжает ли кошелек сканировать каждый блок или проверять только указанную высоту.", "silent_payments_scan_from_height": "Сканирование с высоты блока", @@ -628,6 +629,7 @@ "spend_key_private": "Приватный ключ траты", "spend_key_public": "Публичный ключ траты", "status": "Статус: ", + "string_default": "По умолчанию", "subaddress_title": "Список субадресов", "subaddresses": "Субадреса", "submit_request": "отправить запрос", @@ -817,4 +819,4 @@ "you_will_get": "Конвертировать в", "you_will_send": "Конвертировать из", "yy": "ГГ" -} \ No newline at end of file +} diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 14ecaf62a8..8bee7acc6f 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -618,6 +618,7 @@ "sign_up": "สมัครสมาชิก", "signTransaction": "ลงนามในการทำธุรกรรม", "signup_for_card_accept_terms": "ลงทะเบียนสำหรับบัตรและยอมรับเงื่อนไข", + "silent_payments": "การชำระเงินเงียบ", "silent_payments_scan_from_date": "สแกนตั้งแต่วันที่", "silent_payments_scan_from_date_or_blockheight": "โปรดป้อนความสูงของบล็อกที่คุณต้องการเริ่มการสแกนสำหรับการชำระเงินแบบเงียบ ๆ หรือใช้วันที่แทน คุณสามารถเลือกได้ว่ากระเป๋าเงินยังคงสแกนทุกบล็อกหรือตรวจสอบความสูงที่ระบุเท่านั้น", "silent_payments_scan_from_height": "สแกนจากความสูงของบล็อก", @@ -627,6 +628,7 @@ "spend_key_private": "คีย์จ่าย (ส่วนตัว)", "spend_key_public": "คีย์จ่าย (สาธารณะ)", "status": "สถานะ: ", + "string_default": "ค่าเริ่มต้น", "subaddress_title": "รายการที่อยู่ย่อย", "subaddresses": "ที่อยู่ย่อย", "submit_request": "ส่งคำขอ", @@ -816,4 +818,4 @@ "you_will_get": "แปลงเป็น", "you_will_send": "แปลงจาก", "yy": "ปี" -} \ No newline at end of file +} diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 6680a9341f..784a91892e 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -618,6 +618,7 @@ "sign_up": "Mag -sign up", "signTransaction": "Mag-sign Transaksyon", "signup_for_card_accept_terms": "Mag -sign up para sa card at tanggapin ang mga termino.", + "silent_payments": "Tahimik na pagbabayad", "silent_payments_scan_from_date": "I -scan mula sa petsa", "silent_payments_scan_from_date_or_blockheight": "Mangyaring ipasok ang taas ng block na nais mong simulan ang pag -scan para sa papasok na tahimik na pagbabayad, o, gamitin ang petsa sa halip. Maaari kang pumili kung ang pitaka ay patuloy na pag -scan sa bawat bloke, o suriin lamang ang tinukoy na taas.", "silent_payments_scan_from_height": "I -scan mula sa taas ng block", @@ -627,6 +628,7 @@ "spend_key_private": "Gumastos ng susi (pribado)", "spend_key_public": "Gumastos ng susi (publiko)", "status": "Katayuan:", + "string_default": "Default", "subaddress_title": "Listahan ng Subaddress", "subaddresses": "Mga Subaddresses", "submit_request": "magsumite ng isang kahilingan", @@ -816,4 +818,4 @@ "you_will_get": "Mag -convert sa", "you_will_send": "I -convert mula sa", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index b9110639d6..95844f593b 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -618,6 +618,7 @@ "sign_up": "Kaydol", "signTransaction": "İşlem İmzala", "signup_for_card_accept_terms": "Kart için kaydol ve koşulları kabul et.", + "silent_payments": "Sessiz ödemeler", "silent_payments_scan_from_date": "Tarihten tarama", "silent_payments_scan_from_date_or_blockheight": "Lütfen gelen sessiz ödemeler için taramaya başlamak istediğiniz blok yüksekliğini girin veya bunun yerine tarihi kullanın. Cüzdanın her bloğu taramaya devam edip etmediğini veya yalnızca belirtilen yüksekliği kontrol edip etmediğini seçebilirsiniz.", "silent_payments_scan_from_height": "Blok yüksekliğinden tarama", @@ -627,6 +628,7 @@ "spend_key_private": "Harcama anahtarı (özel)", "spend_key_public": "Harcama anahtarı (genel)", "status": "Durum: ", + "string_default": "Varsayılan", "subaddress_title": "Alt adres listesi", "subaddresses": "Alt adresler", "submit_request": "talep gönder", @@ -816,4 +818,4 @@ "you_will_get": "Biçimine dönüştür:", "you_will_send": "Biçiminden dönüştür:", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 4b608fe45f..c9aaef6f27 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -619,6 +619,7 @@ "sign_up": "Зареєструватися", "signTransaction": "Підписати транзакцію", "signup_for_card_accept_terms": "Зареєструйтеся на картку та прийміть умови.", + "silent_payments": "Мовчазні платежі", "silent_payments_scan_from_date": "Сканувати з дати", "silent_payments_scan_from_date_or_blockheight": "Введіть висоту блоку, яку ви хочете почати сканувати для вхідних мовчазних платежів, або скористайтеся датою замість цього. Ви можете вибрати, якщо гаманець продовжує сканувати кожен блок, або перевіряє лише вказану висоту.", "silent_payments_scan_from_height": "Сканування від висоти блоку", @@ -628,6 +629,7 @@ "spend_key_private": "Приватний ключ витрати", "spend_key_public": "Публічний ключ витрати", "status": "Статус: ", + "string_default": "За замовчуванням", "subaddress_title": "Список Субадрес", "subaddresses": "Субадреси", "submit_request": "надіслати запит", @@ -817,4 +819,4 @@ "you_will_get": "Конвертувати в", "you_will_send": "Конвертувати з", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 9c6b9cf4b6..c02919463b 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -620,6 +620,7 @@ "sign_up": "سائن اپ", "signTransaction": "۔ﮟﯾﺮﮐ ﻂﺨﺘﺳﺩ ﺮﭘ ﻦﯾﺩ ﻦﯿﻟ", "signup_for_card_accept_terms": "کارڈ کے لیے سائن اپ کریں اور شرائط کو قبول کریں۔", + "silent_payments": "خاموش ادائیگی", "silent_payments_scan_from_date": "تاریخ سے اسکین کریں", "silent_payments_scan_from_date_or_blockheight": "براہ کرم بلاک اونچائی میں داخل ہوں جس سے آپ آنے والی خاموش ادائیگیوں کے لئے اسکیننگ شروع کرنا چاہتے ہیں ، یا اس کے بجائے تاریخ کا استعمال کریں۔ آپ یہ منتخب کرسکتے ہیں کہ اگر پرس ہر بلاک کو اسکیننگ جاری رکھے ہوئے ہے ، یا صرف مخصوص اونچائی کی جانچ پڑتال کرتا ہے۔", "silent_payments_scan_from_height": "بلاک اونچائی سے اسکین کریں", @@ -629,6 +630,7 @@ "spend_key_private": "خرچ کی کلید (نجی)", "spend_key_public": "خرچ کی کلید (عوامی)", "status": "حالت:", + "string_default": "پہلے سے طے شدہ", "subaddress_title": "ذیلی ایڈریس کی فہرست", "subaddresses": "ذیلی پتے", "submit_request": "درخواست بھیج دو", @@ -818,4 +820,4 @@ "you_will_get": "میں تبدیل کریں۔", "you_will_send": "سے تبدیل کریں۔", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index fcc2d71c9f..76a17bc997 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -619,6 +619,7 @@ "sign_up": "Forúkọ sílẹ̀", "signTransaction": "Wole Idunadura", "signup_for_card_accept_terms": "Ẹ f'orúkọ sílẹ̀ láti gba káàdì àti àjọrò.", + "silent_payments": "Awọn sisanwo ipalọlọ", "silent_payments_scan_from_date": "Scan lati ọjọ", "silent_payments_scan_from_date_or_blockheight": "Jọwọ tẹ giga idibo ti o fẹ bẹrẹ ọlọjẹ fun awọn sisanwo ipalọlọ, tabi, lo ọjọ dipo. O le yan ti apamọwọ naa tẹsiwaju nṣapẹẹrẹ gbogbo bulọọki, tabi ṣayẹwo nikan giga ti o sọ tẹlẹ.", "silent_payments_scan_from_height": "Scan lati Iga Iga", @@ -628,6 +629,7 @@ "spend_key_private": "Kọ́kọ́rọ́ sísan (àdáni)", "spend_key_public": "Kọ́kọ́rọ́ sísan (kò àdáni)", "status": "Tó ń ṣẹlẹ̀: ", + "string_default": "Aiyipada", "subaddress_title": "Àkọsílẹ̀ ni nínú àwọn àdírẹ́sì tíwọn rẹ̀lẹ̀", "subaddresses": "Àwọn àdírẹ́sì kékeré", "submit_request": "Ṣé ìbéèrè", @@ -817,4 +819,4 @@ "you_will_get": "Ṣe pàṣípààrọ̀ sí", "you_will_send": "Ṣe pàṣípààrọ̀ láti", "yy": "Ọd" -} \ No newline at end of file +} diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 4b77c997e6..c60807e2cc 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -618,6 +618,7 @@ "sign_up": "注册", "signTransaction": "签署交易", "signup_for_card_accept_terms": "注册卡并接受条款。", + "silent_payments": "无声付款", "silent_payments_scan_from_date": "从日期开始扫描", "silent_payments_scan_from_date_or_blockheight": "请输入您要开始扫描输入静音付款的块高度,或者使用日期。您可以选择钱包是否继续扫描每个块,或仅检查指定的高度。", "silent_payments_scan_from_height": "从块高度扫描", @@ -627,6 +628,7 @@ "spend_key_private": "Spend 密钥 (私钥)", "spend_key_public": "Spend 密钥 (公钥)", "status": "状态: ", + "string_default": "默认", "subaddress_title": "子地址列表", "subaddresses": "子地址", "submit_request": "提交请求", @@ -816,4 +818,4 @@ "you_will_get": "转换到", "you_will_send": "转换自", "yy": "YY" -} \ No newline at end of file +} diff --git a/tool/configure.dart b/tool/configure.dart index 422d0b8162..b082f64701 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -129,7 +129,8 @@ abstract class Bitcoin { List getAddresses(Object wallet); String getAddress(Object wallet); - List getSilentAddresses(Object wallet); + List getSilentPaymentAddresses(Object wallet); + List getSilentPaymentReceivedAddresses(Object wallet); Future estimateFakeSendAllTxAmount(Object wallet, TransactionPriority priority); List getSubAddresses(Object wallet); From 36c64368134c3c970ffe6a69065eba4a7504e367 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 8 Apr 2024 20:30:23 -0300 Subject: [PATCH 22/75] feat: silent payments labeled addresses disclaimer --- lib/src/screens/receive/receive_page.dart | 22 ++++++++++++---------- res/values/strings_ar.arb | 3 ++- res/values/strings_bg.arb | 3 ++- res/values/strings_cs.arb | 3 ++- res/values/strings_de.arb | 3 ++- res/values/strings_en.arb | 3 ++- res/values/strings_es.arb | 3 ++- res/values/strings_fr.arb | 3 ++- res/values/strings_ha.arb | 3 ++- res/values/strings_hi.arb | 3 ++- res/values/strings_hr.arb | 3 ++- res/values/strings_id.arb | 3 ++- res/values/strings_it.arb | 3 ++- res/values/strings_ja.arb | 3 ++- res/values/strings_ko.arb | 3 ++- res/values/strings_my.arb | 3 ++- res/values/strings_nl.arb | 3 ++- res/values/strings_pl.arb | 3 ++- res/values/strings_pt.arb | 3 ++- res/values/strings_ru.arb | 3 ++- res/values/strings_th.arb | 3 ++- res/values/strings_tl.arb | 3 ++- res/values/strings_tr.arb | 3 ++- res/values/strings_uk.arb | 3 ++- res/values/strings_ur.arb | 3 ++- res/values/strings_yo.arb | 3 ++- res/values/strings_zh.arb | 3 ++- 27 files changed, 64 insertions(+), 36 deletions(-) diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index 6090bfdd8f..4d0c6185f1 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -230,16 +230,18 @@ class ReceivePage extends BasePage { child: cell, ); })), - if (!addressListViewModel.isSilentPayments) - Padding( - padding: EdgeInsets.fromLTRB(24, 24, 24, 32), - child: Text(S.of(context).electrum_address_disclaimer, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 15, - color: - Theme.of(context).extension()!.labelTextColor)), - ), + Padding( + padding: EdgeInsets.fromLTRB(24, 24, 24, 32), + child: Text( + !addressListViewModel.isSilentPayments + ? S.of(context).electrum_address_disclaimer + : S.of(context).silent_payments_disclaimer, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + color: + Theme.of(context).extension()!.labelTextColor)), + ), ], ), )) diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 2e8b9ca682..5646dcf83e 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -619,6 +619,7 @@ "signTransaction": " ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ", "signup_for_card_accept_terms": "قم بالتسجيل للحصول على البطاقة وقبول الشروط.", "silent_payments": "مدفوعات صامتة", + "silent_payments_disclaimer": "العناوين الجديدة ليست هويات جديدة. إنها إعادة استخدام هوية موجودة مع ملصق مختلف.", "silent_payments_scan_from_date": "فحص من التاريخ", "silent_payments_scan_from_date_or_blockheight": "يرجى إدخال ارتفاع الكتلة الذي تريد بدء المسح الضوئي للمدفوعات الصامتة الواردة ، أو استخدام التاريخ بدلاً من ذلك. يمكنك اختيار ما إذا كانت المحفظة تواصل مسح كل كتلة ، أو تتحقق فقط من الارتفاع المحدد.", "silent_payments_scan_from_height": "فحص من ارتفاع الكتلة", @@ -818,4 +819,4 @@ "you_will_get": "حول الى", "you_will_send": "تحويل من", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index cde8ca2600..5c06a9cbdc 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -619,6 +619,7 @@ "signTransaction": "Подпишете транзакция", "signup_for_card_accept_terms": "Регистрайте се за картата и приемете условията.", "silent_payments": "Мълчаливи плащания", + "silent_payments_disclaimer": "Новите адреси не са нови идентичности. Това е повторна употреба на съществуваща идентичност с различен етикет.", "silent_payments_scan_from_date": "Сканиране от дата", "silent_payments_scan_from_date_or_blockheight": "Моля, въведете височината на блока, която искате да започнете да сканирате за входящи безшумни плащания, или вместо това използвайте датата. Можете да изберете дали портфейлът продължава да сканира всеки блок или проверява само определената височина.", "silent_payments_scan_from_height": "Сканиране от височината на блока", @@ -818,4 +819,4 @@ "you_will_get": "Обръщане в", "you_will_send": "Обръщане от", "yy": "гг" -} +} \ No newline at end of file diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 29b193e8a1..adb194a1a3 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -619,6 +619,7 @@ "signTransaction": "Podepsat transakci", "signup_for_card_accept_terms": "Zaregistrujte se pro kartu a souhlaste s podmínkami.", "silent_payments": "Tiché platby", + "silent_payments_disclaimer": "Nové adresy nejsou nové identity. Je to opětovné použití existující identity s jiným štítkem.", "silent_payments_scan_from_date": "Skenovat od data", "silent_payments_scan_from_date_or_blockheight": "Zadejte výšku bloku, kterou chcete začít skenovat, zda jsou přicházející tiché platby, nebo místo toho použijte datum. Můžete si vybrat, zda peněženka pokračuje v skenování každého bloku nebo zkontroluje pouze zadanou výšku.", "silent_payments_scan_from_height": "Skenování z výšky bloku", @@ -818,4 +819,4 @@ "you_will_get": "Směnit na", "you_will_send": "Směnit z", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 0ab2be8e28..7a7e60413d 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -620,6 +620,7 @@ "signTransaction": "Transaktion unterzeichnen", "signup_for_card_accept_terms": "Melden Sie sich für die Karte an und akzeptieren Sie die Bedingungen.", "silent_payments": "Stille Zahlungen", + "silent_payments_disclaimer": "Neue Adressen sind keine neuen Identitäten. Es ist eine Wiederverwendung einer bestehenden Identität mit einem anderen Etikett.", "silent_payments_scan_from_date": "Scan ab Datum", "silent_payments_scan_from_date_or_blockheight": "Bitte geben Sie die Blockhöhe ein, die Sie für eingehende stille Zahlungen scannen möchten, oder verwenden Sie stattdessen das Datum. Sie können wählen, ob die Brieftasche jeden Block scannt oder nur die angegebene Höhe überprüft.", "silent_payments_scan_from_height": "Scan aus der Blockhöhe scannen", @@ -821,4 +822,4 @@ "you_will_get": "Konvertieren zu", "you_will_send": "Konvertieren von", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 86cd4452da..3b6afa1c86 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -619,6 +619,7 @@ "signTransaction": "Sign Transaction", "signup_for_card_accept_terms": "Sign up for the card and accept the terms.", "silent_payments": "Silent Payments", + "silent_payments_disclaimer": "New addresses are not new identities. It is a re-use of an existing identity with a different label.", "silent_payments_scan_from_date": "Scan from date", "silent_payments_scan_from_date_or_blockheight": "Please enter the block height you want to start scanning for incoming silent payments, or, use the date instead. You can choose if the wallet continues scanning every block, or checks only the specified height.", "silent_payments_scan_from_height": "Scan from block height", @@ -818,4 +819,4 @@ "you_will_get": "Convert to", "you_will_send": "Convert from", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 61270e3760..1166d6bbe3 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -620,6 +620,7 @@ "signTransaction": "Firmar transacción", "signup_for_card_accept_terms": "Regístrese para obtener la tarjeta y acepte los términos.", "silent_payments": "Pagos silenciosos", + "silent_payments_disclaimer": "Las nuevas direcciones no son nuevas identidades. Es una reutilización de una identidad existente con una etiqueta diferente.", "silent_payments_scan_from_date": "Escanear desde la fecha", "silent_payments_scan_from_date_or_blockheight": "Ingrese la altura del bloque que desea comenzar a escanear para pagos silenciosos entrantes, o use la fecha en su lugar. Puede elegir si la billetera continúa escaneando cada bloque, o verifica solo la altura especificada.", "silent_payments_scan_from_height": "Escanear desde la altura del bloque", @@ -819,4 +820,4 @@ "you_will_get": "Convertir a", "you_will_send": "Convertir de", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 7295e26c2b..7ae4c3da70 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -619,6 +619,7 @@ "signTransaction": "Signer une transaction", "signup_for_card_accept_terms": "Inscrivez-vous pour la carte et acceptez les conditions.", "silent_payments": "Paiements silencieux", + "silent_payments_disclaimer": "Les nouvelles adresses ne sont pas de nouvelles identités. Il s'agit d'une réutilisation d'une identité existante avec une étiquette différente.", "silent_payments_scan_from_date": "Analyser à partir de la date", "silent_payments_scan_from_date_or_blockheight": "Veuillez saisir la hauteur du bloc que vous souhaitez commencer à scanner pour les paiements silencieux entrants, ou utilisez la date à la place. Vous pouvez choisir si le portefeuille continue de numériser chaque bloc ou ne vérifie que la hauteur spécifiée.", "silent_payments_scan_from_height": "Scan à partir de la hauteur du bloc", @@ -818,4 +819,4 @@ "you_will_get": "Convertir vers", "you_will_send": "Convertir depuis", "yy": "AA" -} +} \ No newline at end of file diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index e2f9537903..bb78d76765 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -621,6 +621,7 @@ "signTransaction": "Sa hannu Ma'amala", "signup_for_card_accept_terms": "Yi rajista don katin kuma karɓi sharuɗɗan.", "silent_payments": "Biya silent", + "silent_payments_disclaimer": "Sabbin adiresoshin ba sabon tsari bane. Wannan shine sake amfani da asalin asalin tare da wata alama daban.", "silent_payments_scan_from_date": "Scan daga kwanan wata", "silent_payments_scan_from_date_or_blockheight": "Da fatan za a shigar da toshe wurin da kake son fara bincika don biyan silins mai shigowa, ko, yi amfani da kwanan wata. Zaka iya zabar idan walat ɗin ya ci gaba da bincika kowane toshe, ko duba tsinkaye da aka ƙayyade.", "silent_payments_scan_from_height": "Scan daga tsayin daka", @@ -820,4 +821,4 @@ "you_will_get": "Maida zuwa", "you_will_send": "Maida daga", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 37b2e22f29..8b6599a4d7 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -621,6 +621,7 @@ "signTransaction": "लेन-देन पर हस्ताक्षर करें", "signup_for_card_accept_terms": "कार्ड के लिए साइन अप करें और शर्तें स्वीकार करें।", "silent_payments": "मूक भुगतान", + "silent_payments_disclaimer": "नए पते नई पहचान नहीं हैं। यह एक अलग लेबल के साथ एक मौजूदा पहचान का पुन: उपयोग है।", "silent_payments_scan_from_date": "तिथि से स्कैन करना", "silent_payments_scan_from_date_or_blockheight": "कृपया उस ब्लॉक ऊंचाई दर्ज करें जिसे आप आने वाले मूक भुगतान के लिए स्कैन करना शुरू करना चाहते हैं, या, इसके बजाय तारीख का उपयोग करें। आप चुन सकते हैं कि क्या वॉलेट हर ब्लॉक को स्कैन करना जारी रखता है, या केवल निर्दिष्ट ऊंचाई की जांच करता है।", "silent_payments_scan_from_height": "ब्लॉक ऊंचाई से स्कैन करें", @@ -820,4 +821,4 @@ "you_will_get": "में बदलें", "you_will_send": "से रूपांतरित करें", "yy": "वाईवाई" -} +} \ No newline at end of file diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 7a570e4380..ae5fff09d4 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -619,6 +619,7 @@ "signTransaction": "Potpišite transakciju", "signup_for_card_accept_terms": "Prijavite se za karticu i prihvatite uvjete.", "silent_payments": "Tiha plaćanja", + "silent_payments_disclaimer": "Nove adrese nisu novi identiteti. To je ponovna upotreba postojećeg identiteta s drugom oznakom.", "silent_payments_scan_from_date": "Skeniranje iz datuma", "silent_payments_scan_from_date_or_blockheight": "Unesite visinu bloka koju želite započeti skeniranje za dolazna tiha plaćanja ili umjesto toga upotrijebite datum. Možete odabrati da li novčanik nastavlja skenirati svaki blok ili provjerava samo navedenu visinu.", "silent_payments_scan_from_height": "Skeniranje s visine bloka", @@ -818,4 +819,4 @@ "you_will_get": "Razmijeni u", "you_will_send": "Razmijeni iz", "yy": "GG" -} +} \ No newline at end of file diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index f12416b5e4..e2c9a2e344 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -622,6 +622,7 @@ "signTransaction": "Tandatangani Transaksi", "signup_for_card_accept_terms": "Daftar untuk kartu dan terima syarat dan ketentuan.", "silent_payments": "Pembayaran diam", + "silent_payments_disclaimer": "Alamat baru bukanlah identitas baru. Ini adalah penggunaan kembali identitas yang ada dengan label yang berbeda.", "silent_payments_scan_from_date": "Pindai dari tanggal", "silent_payments_scan_from_date_or_blockheight": "Harap masukkan ketinggian blok yang ingin Anda mulai pemindaian untuk pembayaran diam yang masuk, atau, gunakan tanggal sebagai gantinya. Anda dapat memilih jika dompet terus memindai setiap blok, atau memeriksa hanya ketinggian yang ditentukan.", "silent_payments_scan_from_height": "Pindai dari Tinggi Blok", @@ -821,4 +822,4 @@ "you_will_get": "Konversi ke", "you_will_send": "Konversi dari", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 750a72d9aa..8dffcfc212 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -621,6 +621,7 @@ "signTransaction": "Firma la transazione", "signup_for_card_accept_terms": "Registrati per la carta e accetta i termini.", "silent_payments": "Pagamenti silenziosi", + "silent_payments_disclaimer": "I nuovi indirizzi non sono nuove identità. È un riutilizzo di un'identità esistente con un'etichetta diversa.", "silent_payments_scan_from_date": "Scansionare dalla data", "silent_payments_scan_from_date_or_blockheight": "Inserisci l'altezza del blocco che si desidera iniziare la scansione per i pagamenti silenziosi in arrivo o, utilizza invece la data. Puoi scegliere se il portafoglio continua a scansionare ogni blocco o controlla solo l'altezza specificata.", "silent_payments_scan_from_height": "Scansione dall'altezza del blocco", @@ -821,4 +822,4 @@ "you_will_get": "Converti a", "you_will_send": "Conveti da", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 2c65216938..bfa555ab2b 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -620,6 +620,7 @@ "signTransaction": "トランザクションに署名する", "signup_for_card_accept_terms": "カードにサインアップして、利用規約に同意してください。", "silent_payments": "サイレント支払い", + "silent_payments_disclaimer": "新しいアドレスは新しいアイデンティティではありません。これは、異なるラベルを持つ既存のアイデンティティの再利用です。", "silent_payments_scan_from_date": "日付からスキャンします", "silent_payments_scan_from_date_or_blockheight": "着信のサイレント決済のためにスキャンを開始するブロックの高さを入力するか、代わりに日付を使用してください。ウォレットがすべてのブロックをスキャンし続けるか、指定された高さのみをチェックするかどうかを選択できます。", "silent_payments_scan_from_height": "ブロックの高さからスキャンします", @@ -819,4 +820,4 @@ "you_will_get": "に変換", "you_will_send": "から変換", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index dc68515c8d..78a693a6ca 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -620,6 +620,7 @@ "signTransaction": "거래 서명", "signup_for_card_accept_terms": "카드에 가입하고 약관에 동의합니다.", "silent_payments": "조용한 지불", + "silent_payments_disclaimer": "새로운 주소는 새로운 정체성이 아닙니다. 다른 레이블로 기존 신원을 재사용하는 것입니다.", "silent_payments_scan_from_date": "날짜부터 스캔하십시오", "silent_payments_scan_from_date_or_blockheight": "들어오는 사일런트 결제를 위해 스캔을 시작하려는 블록 높이를 입력하거나 대신 날짜를 사용하십시오. 지갑이 모든 블록을 계속 스캔하는지 여부를 선택하거나 지정된 높이 만 확인할 수 있습니다.", "silent_payments_scan_from_height": "블록 높이에서 스캔하십시오", @@ -820,4 +821,4 @@ "you_will_send": "다음에서 변환", "YY": "YY", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index d4164b4547..5df33cf8b6 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -619,6 +619,7 @@ "signTransaction": "ငွေလွှဲဝင်ပါ။", "signup_for_card_accept_terms": "ကတ်အတွက် စာရင်းသွင်းပြီး စည်းကမ်းချက်များကို လက်ခံပါ။", "silent_payments": "အသံတိတ်ငွေပေးချေမှု", + "silent_payments_disclaimer": "လိပ်စာအသစ်များသည်အထောက်အထားအသစ်များမဟုတ်ပါ။ ၎င်းသည်ကွဲပြားခြားနားသောတံဆိပ်ဖြင့်ရှိပြီးသားဝိသေသလက်ခဏာကိုပြန်လည်အသုံးပြုခြင်းဖြစ်သည်။", "silent_payments_scan_from_date": "ရက်စွဲမှစကင်ဖတ်ပါ", "silent_payments_scan_from_date_or_blockheight": "ကျေးဇူးပြု. သင်ဝင်လာသောအသံတိတ်ငွေပေးချေမှုအတွက်သင်စကင်ဖတ်စစ်ဆေးလိုသည့်အမြင့်ကိုဖြည့်ပါ။ သို့မဟုတ်နေ့စွဲကိုသုံးပါ။ Wallet သည်လုပ်ကွက်တိုင်းကိုဆက်လက်စကင်ဖတ်စစ်ဆေးပါကသို့မဟုတ်သတ်မှတ်ထားသောအမြင့်ကိုသာစစ်ဆေးပါကသင်ရွေးချယ်နိုင်သည်။", "silent_payments_scan_from_height": "ပိတ်ပင်တားဆီးမှုအမြင့်ကနေ scan", @@ -818,4 +819,4 @@ "you_will_get": "သို့ပြောင်းပါ။", "you_will_send": "မှပြောင်းပါ။", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index cdd50981e7..e2bd9a2a78 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -619,6 +619,7 @@ "signTransaction": "Transactie ondertekenen", "signup_for_card_accept_terms": "Meld je aan voor de kaart en accepteer de voorwaarden.", "silent_payments": "Stille betalingen", + "silent_payments_disclaimer": "Nieuwe adressen zijn geen nieuwe identiteiten. Het is een hergebruik van een bestaande identiteit met een ander label.", "silent_payments_scan_from_date": "Scan vanaf datum", "silent_payments_scan_from_date_or_blockheight": "Voer de blokhoogte in die u wilt beginnen met scannen op inkomende stille betalingen, of gebruik in plaats daarvan de datum. U kunt kiezen of de portemonnee elk blok blijft scannen of alleen de opgegeven hoogte controleert.", "silent_payments_scan_from_height": "Scan van blokhoogte", @@ -819,4 +820,4 @@ "you_will_get": "Converteren naar", "you_will_send": "Converteren van", "yy": "JJ" -} +} \ No newline at end of file diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index cde8cad380..2834e4772b 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -619,6 +619,7 @@ "signTransaction": "Podpisz transakcję", "signup_for_card_accept_terms": "Zarejestruj się, aby otrzymać kartę i zaakceptuj warunki.", "silent_payments": "Ciche płatności", + "silent_payments_disclaimer": "Nowe adresy nie są nową tożsamością. Jest to ponowne wykorzystanie istniejącej tożsamości z inną etykietą.", "silent_payments_scan_from_date": "Skanuj z daty", "silent_payments_scan_from_date_or_blockheight": "Wprowadź wysokość bloku, którą chcesz rozpocząć skanowanie w poszukiwaniu cichej płatności lub zamiast tego skorzystaj z daty. Możesz wybrać, czy portfel kontynuuje skanowanie każdego bloku, lub sprawdza tylko określoną wysokość.", "silent_payments_scan_from_height": "Skanuj z wysokości bloku", @@ -818,4 +819,4 @@ "you_will_get": "Konwertuj na", "you_will_send": "Konwertuj z", "yy": "RR" -} +} \ No newline at end of file diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 168b0312a1..eaa51320f4 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -621,6 +621,7 @@ "signTransaction": "Assinar transação", "signup_for_card_accept_terms": "Cadastre-se no cartão e aceite os termos.", "silent_payments": "Pagamentos silenciosos", + "silent_payments_disclaimer": "Novos endereços não são novas identidades. É uma reutilização de uma identidade existente com um rótulo diferente.", "silent_payments_scan_from_date": "Escanear a partir da data", "silent_payments_scan_from_date_or_blockheight": "Por favor, insira a altura do bloco que deseja iniciar o escaneamento para obter pagamentos silenciosos ou use a data. Você pode escolher se a carteira continua digitalizando cada bloco ou verifica apenas a altura especificada.", "silent_payments_scan_from_height": "Escanear a partir da altura do bloco", @@ -821,4 +822,4 @@ "you_will_get": "Converter para", "you_will_send": "Converter de", "yy": "aa" -} +} \ No newline at end of file diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 9a8f6563a6..fb9b2c12d0 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -620,6 +620,7 @@ "signTransaction": "Подписать транзакцию", "signup_for_card_accept_terms": "Подпишитесь на карту и примите условия.", "silent_payments": "Молчаливые платежи", + "silent_payments_disclaimer": "Новые адреса не являются новыми личностями. Это повторное использование существующей идентичности с другой этикеткой.", "silent_payments_scan_from_date": "Сканирование с даты", "silent_payments_scan_from_date_or_blockheight": "Пожалуйста, введите высоту блока, которую вы хотите начать сканирование для входящих молчаливых платежей, или вместо этого используйте дату. Вы можете выбрать, продолжает ли кошелек сканировать каждый блок или проверять только указанную высоту.", "silent_payments_scan_from_height": "Сканирование с высоты блока", @@ -819,4 +820,4 @@ "you_will_get": "Конвертировать в", "you_will_send": "Конвертировать из", "yy": "ГГ" -} +} \ No newline at end of file diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 8bee7acc6f..b7dffc565a 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -619,6 +619,7 @@ "signTransaction": "ลงนามในการทำธุรกรรม", "signup_for_card_accept_terms": "ลงทะเบียนสำหรับบัตรและยอมรับเงื่อนไข", "silent_payments": "การชำระเงินเงียบ", + "silent_payments_disclaimer": "ที่อยู่ใหม่ไม่ใช่ตัวตนใหม่ มันเป็นการใช้ซ้ำของตัวตนที่มีอยู่ด้วยฉลากที่แตกต่างกัน", "silent_payments_scan_from_date": "สแกนตั้งแต่วันที่", "silent_payments_scan_from_date_or_blockheight": "โปรดป้อนความสูงของบล็อกที่คุณต้องการเริ่มการสแกนสำหรับการชำระเงินแบบเงียบ ๆ หรือใช้วันที่แทน คุณสามารถเลือกได้ว่ากระเป๋าเงินยังคงสแกนทุกบล็อกหรือตรวจสอบความสูงที่ระบุเท่านั้น", "silent_payments_scan_from_height": "สแกนจากความสูงของบล็อก", @@ -818,4 +819,4 @@ "you_will_get": "แปลงเป็น", "you_will_send": "แปลงจาก", "yy": "ปี" -} +} \ No newline at end of file diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 784a91892e..35c777530d 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -619,6 +619,7 @@ "signTransaction": "Mag-sign Transaksyon", "signup_for_card_accept_terms": "Mag -sign up para sa card at tanggapin ang mga termino.", "silent_payments": "Tahimik na pagbabayad", + "silent_payments_disclaimer": "Ang mga bagong address ay hindi mga bagong pagkakakilanlan. Ito ay isang muling paggamit ng isang umiiral na pagkakakilanlan na may ibang label.", "silent_payments_scan_from_date": "I -scan mula sa petsa", "silent_payments_scan_from_date_or_blockheight": "Mangyaring ipasok ang taas ng block na nais mong simulan ang pag -scan para sa papasok na tahimik na pagbabayad, o, gamitin ang petsa sa halip. Maaari kang pumili kung ang pitaka ay patuloy na pag -scan sa bawat bloke, o suriin lamang ang tinukoy na taas.", "silent_payments_scan_from_height": "I -scan mula sa taas ng block", @@ -818,4 +819,4 @@ "you_will_get": "Mag -convert sa", "you_will_send": "I -convert mula sa", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 95844f593b..a384bc08ea 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -619,6 +619,7 @@ "signTransaction": "İşlem İmzala", "signup_for_card_accept_terms": "Kart için kaydol ve koşulları kabul et.", "silent_payments": "Sessiz ödemeler", + "silent_payments_disclaimer": "Yeni adresler yeni kimlikler değildir. Farklı bir etikete sahip mevcut bir kimliğin yeniden kullanımıdır.", "silent_payments_scan_from_date": "Tarihten tarama", "silent_payments_scan_from_date_or_blockheight": "Lütfen gelen sessiz ödemeler için taramaya başlamak istediğiniz blok yüksekliğini girin veya bunun yerine tarihi kullanın. Cüzdanın her bloğu taramaya devam edip etmediğini veya yalnızca belirtilen yüksekliği kontrol edip etmediğini seçebilirsiniz.", "silent_payments_scan_from_height": "Blok yüksekliğinden tarama", @@ -818,4 +819,4 @@ "you_will_get": "Biçimine dönüştür:", "you_will_send": "Biçiminden dönüştür:", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index c9aaef6f27..d0cdfa7059 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -620,6 +620,7 @@ "signTransaction": "Підписати транзакцію", "signup_for_card_accept_terms": "Зареєструйтеся на картку та прийміть умови.", "silent_payments": "Мовчазні платежі", + "silent_payments_disclaimer": "Нові адреси - це не нові ідентичності. Це повторне використання існуючої ідентичності з іншою етикеткою.", "silent_payments_scan_from_date": "Сканувати з дати", "silent_payments_scan_from_date_or_blockheight": "Введіть висоту блоку, яку ви хочете почати сканувати для вхідних мовчазних платежів, або скористайтеся датою замість цього. Ви можете вибрати, якщо гаманець продовжує сканувати кожен блок, або перевіряє лише вказану висоту.", "silent_payments_scan_from_height": "Сканування від висоти блоку", @@ -819,4 +820,4 @@ "you_will_get": "Конвертувати в", "you_will_send": "Конвертувати з", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index c02919463b..ef911df1ef 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -621,6 +621,7 @@ "signTransaction": "۔ﮟﯾﺮﮐ ﻂﺨﺘﺳﺩ ﺮﭘ ﻦﯾﺩ ﻦﯿﻟ", "signup_for_card_accept_terms": "کارڈ کے لیے سائن اپ کریں اور شرائط کو قبول کریں۔", "silent_payments": "خاموش ادائیگی", + "silent_payments_disclaimer": "نئے پتے نئی شناخت نہیں ہیں۔ یہ ایک مختلف لیبل کے ساتھ موجودہ شناخت کا دوبارہ استعمال ہے۔", "silent_payments_scan_from_date": "تاریخ سے اسکین کریں", "silent_payments_scan_from_date_or_blockheight": "براہ کرم بلاک اونچائی میں داخل ہوں جس سے آپ آنے والی خاموش ادائیگیوں کے لئے اسکیننگ شروع کرنا چاہتے ہیں ، یا اس کے بجائے تاریخ کا استعمال کریں۔ آپ یہ منتخب کرسکتے ہیں کہ اگر پرس ہر بلاک کو اسکیننگ جاری رکھے ہوئے ہے ، یا صرف مخصوص اونچائی کی جانچ پڑتال کرتا ہے۔", "silent_payments_scan_from_height": "بلاک اونچائی سے اسکین کریں", @@ -820,4 +821,4 @@ "you_will_get": "میں تبدیل کریں۔", "you_will_send": "سے تبدیل کریں۔", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 76a17bc997..2fe812c08e 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -620,6 +620,7 @@ "signTransaction": "Wole Idunadura", "signup_for_card_accept_terms": "Ẹ f'orúkọ sílẹ̀ láti gba káàdì àti àjọrò.", "silent_payments": "Awọn sisanwo ipalọlọ", + "silent_payments_disclaimer": "Awọn adirẹsi tuntun kii ṣe awọn idanimọ tuntun. O jẹ yiyan ti idanimọ ti o wa pẹlu aami oriṣiriṣi.", "silent_payments_scan_from_date": "Scan lati ọjọ", "silent_payments_scan_from_date_or_blockheight": "Jọwọ tẹ giga idibo ti o fẹ bẹrẹ ọlọjẹ fun awọn sisanwo ipalọlọ, tabi, lo ọjọ dipo. O le yan ti apamọwọ naa tẹsiwaju nṣapẹẹrẹ gbogbo bulọọki, tabi ṣayẹwo nikan giga ti o sọ tẹlẹ.", "silent_payments_scan_from_height": "Scan lati Iga Iga", @@ -819,4 +820,4 @@ "you_will_get": "Ṣe pàṣípààrọ̀ sí", "you_will_send": "Ṣe pàṣípààrọ̀ láti", "yy": "Ọd" -} +} \ No newline at end of file diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index c60807e2cc..1a4d0b3414 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -619,6 +619,7 @@ "signTransaction": "签署交易", "signup_for_card_accept_terms": "注册卡并接受条款。", "silent_payments": "无声付款", + "silent_payments_disclaimer": "新地址不是新的身份。这是重复使用具有不同标签的现有身份。", "silent_payments_scan_from_date": "从日期开始扫描", "silent_payments_scan_from_date_or_blockheight": "请输入您要开始扫描输入静音付款的块高度,或者使用日期。您可以选择钱包是否继续扫描每个块,或仅检查指定的高度。", "silent_payments_scan_from_height": "从块高度扫描", @@ -818,4 +819,4 @@ "you_will_get": "转换到", "you_will_send": "转换自", "yy": "YY" -} +} \ No newline at end of file From 6ee57a7be4ab4d1bf68929c2230e620552d6ef24 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 8 Apr 2024 20:37:48 -0300 Subject: [PATCH 23/75] fix: missing i18n --- lib/src/screens/receive/widgets/address_cell.dart | 2 +- res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 1 + res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 1 + res/values/strings_ko.arb | 1 + res/values/strings_my.arb | 1 + res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 1 + res/values/strings_th.arb | 1 + res/values/strings_tl.arb | 1 + res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 1 + res/values/strings_yo.arb | 1 + res/values/strings_zh.arb | 1 + 27 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/src/screens/receive/widgets/address_cell.dart b/lib/src/screens/receive/widgets/address_cell.dart index a07456284d..69e0119004 100644 --- a/lib/src/screens/receive/widgets/address_cell.dart +++ b/lib/src/screens/receive/widgets/address_cell.dart @@ -134,7 +134,7 @@ class AddressCell extends StatelessWidget { mainAxisSize: MainAxisSize.max, children: [ Text( - 'Balance: $balance', + '${S.of(context).balance}: $txCount', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 5646dcf83e..2cdc983953 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -70,6 +70,7 @@ "backup": "نسخ الاحتياطي", "backup_file": "ملف النسخ الاحتياطي", "backup_password": "كلمة مرور النسخ الاحتياطي", + "balance": "توازن", "balance_page": "صفحة التوازن", "bill_amount": "مبلغ الفاتورة", "billing_address_info": "إذا طُلب منك عنوان إرسال فواتير ، فأدخل عنوان الشحن الخاص بك", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 5c06a9cbdc..7b7c62e506 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -70,6 +70,7 @@ "backup": "Резервно копие", "backup_file": "Резервно копие", "backup_password": "Парола за възстановяване", + "balance": "Баланс", "balance_page": "Страница за баланс", "bill_amount": "Искана сума", "billing_address_info": "Ако Ви попитат за билинг адрес, въведето своя адрес за доставка", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index adb194a1a3..b032f4ee31 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -70,6 +70,7 @@ "backup": "Záloha", "backup_file": "Soubor se zálohou", "backup_password": "Heslo pro zálohy", + "balance": "Zůstatek", "balance_page": "Stránka zůstatku", "bill_amount": "Účtovaná částka", "billing_address_info": "Při dotazu na fakturační adresu, zadejte svou doručovací adresu", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 7a7e60413d..0168f415cd 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -70,6 +70,7 @@ "backup": "Sicherung", "backup_file": "Sicherungsdatei", "backup_password": "Passwort sichern", + "balance": "Gleichgewicht", "balance_page": "Balance-Seite", "bill_amount": "Rechnungsbetrag", "billing_address_info": "Wenn Sie nach einer Rechnungsadresse gefragt werden, geben Sie bitte Ihre Lieferadresse an", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 3b6afa1c86..a22844bc18 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -70,6 +70,7 @@ "backup": "Backup", "backup_file": "Backup file", "backup_password": "Backup password", + "balance": "Balance", "balance_page": "Balance Page", "bill_amount": "Bill Amount", "billing_address_info": "If asked for a billing address, provide your shipping address", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 1166d6bbe3..6a190abc06 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -70,6 +70,7 @@ "backup": "Apoyo", "backup_file": "Archivo de respaldo", "backup_password": "Contraseña de respaldo", + "balance": "Balance", "balance_page": "Página de saldo", "bill_amount": "Importe de la factura", "billing_address_info": "Si se le solicita una dirección de facturación, proporcione su dirección de envío", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 7ae4c3da70..ecfa4bc71c 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -70,6 +70,7 @@ "backup": "Sauvegarde", "backup_file": "Fichier de sauvegarde", "backup_password": "Mot de passe de sauvegarde", + "balance": "Équilibre", "balance_page": "Page Solde", "bill_amount": "Montant de la facture", "billing_address_info": "Si une adresse de facturation vous est demandée, indiquez votre adresse de livraison", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index bb78d76765..696fc79907 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -70,6 +70,7 @@ "backup": "Ajiyayyen", "backup_file": "Ajiyayyen fayil", "backup_password": "Ajiyayyen kalmar sirri", + "balance": "Ma'auni", "balance_page": "Ma'auni Page", "bill_amount": "Adadin Bill", "billing_address_info": "Idan an nemi adireshin biyan kuɗi, samar da adireshin jigilar kaya", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 8b6599a4d7..c45cc96da2 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -70,6 +70,7 @@ "backup": "बैकअप", "backup_file": "बैकअपफ़ाइल", "backup_password": "बैकअप पासवर्ड", + "balance": "संतुलन", "balance_page": "बैलेंस पेज", "bill_amount": "बिल राशि", "billing_address_info": "यदि बिलिंग पता मांगा जाए, तो अपना शिपिंग पता प्रदान करें", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index ae5fff09d4..03e99ac551 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -70,6 +70,7 @@ "backup": "Sigurnosna kopija", "backup_file": "Sigurnosna kopija datoteke", "backup_password": "Lozinka za sigurnosnu kopiju", + "balance": "Uravnotežiti", "balance_page": "Stranica sa stanjem", "bill_amount": "Iznos računa", "billing_address_info": "Ako se od vas zatraži adresa za naplatu, navedite svoju adresu za dostavu", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index e2c9a2e344..33745c6c95 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -70,6 +70,7 @@ "backup": "Cadangan", "backup_file": "File cadangan", "backup_password": "Kata sandi cadangan", + "balance": "Keseimbangan", "balance_page": "Halaman Saldo", "bill_amount": "Jumlah Tagihan", "billing_address_info": "Jika diminta alamat billing, berikan alamat pengiriman Anda", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 8dffcfc212..77884037ae 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -70,6 +70,7 @@ "backup": "Backup", "backup_file": "Backup file", "backup_password": "Backup password", + "balance": "Bilancia", "balance_page": "Pagina di equilibrio", "bill_amount": "Importo della fattura", "billing_address_info": "Se ti viene richiesto un indirizzo di fatturazione, fornisci il tuo indirizzo di spedizione", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index bfa555ab2b..36932fbc0d 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -70,6 +70,7 @@ "backup": "バックアップ", "backup_file": "バックアップファイル", "backup_password": "バックアップパスワード", + "balance": "バランス", "balance_page": "残高ページ", "bill_amount": "請求額", "billing_address_info": "請求先住所を尋ねられた場合は、配送先住所を入力してください", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 78a693a6ca..eb6f1ce726 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -70,6 +70,7 @@ "backup": "지원", "backup_file": "백업 파일", "backup_password": "백업 비밀번호", + "balance": "균형", "balance_page": "잔액 페이지", "bill_amount": "청구 금액", "billing_address_info": "청구서 수신 주소를 묻는 메시지가 표시되면 배송 주소를 입력하세요.", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 5df33cf8b6..77fa298ebe 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -70,6 +70,7 @@ "backup": "မိတ္တူ", "backup_file": "အရန်ဖိုင်", "backup_password": "စကားဝှက်ကို အရန်သိမ်းဆည်းပါ။", + "balance": "လက်ကျန်ငေှ", "balance_page": "လက်ကျန်စာမျက်နှာ", "bill_amount": "ဘီလ်ပမာဏ", "billing_address_info": "ငွေပေးချေရမည့်လိပ်စာကို တောင်းဆိုပါက သင့်ပို့ဆောင်ရေးလိပ်စာကို ပေးပါ။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index e2bd9a2a78..4a60a994e9 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -70,6 +70,7 @@ "backup": "Back-up", "backup_file": "Backup bestand", "backup_password": "Reservewachtwoord", + "balance": "Evenwicht", "balance_page": "Saldo pagina", "bill_amount": "Bill bedrag", "billing_address_info": "Als u om een ​​factuuradres wordt gevraagd, geef dan uw verzendadres op", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 2834e4772b..54859366dd 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -70,6 +70,7 @@ "backup": "Kopia zapasowa", "backup_file": "Plik kopii zapasowej", "backup_password": "Hasło kpoii zapasowej", + "balance": "Balansować", "balance_page": "Strona salda", "bill_amount": "Kwota rachunku", "billing_address_info": "Jeśli zostaniesz poproszony o podanie adresu rozliczeniowego, podaj swój adres wysyłki", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index eaa51320f4..eb1c8e3d83 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -70,6 +70,7 @@ "backup": "Cópia de segurança", "backup_file": "Arquivo de backup", "backup_password": "Senha de backup", + "balance": "Equilíbrio", "balance_page": "Página de saldo", "bill_amount": "Valor da conta", "billing_address_info": "Se for solicitado um endereço de cobrança, forneça seu endereço de entrega", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index fb9b2c12d0..946ef093c7 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -70,6 +70,7 @@ "backup": "Резервная копия", "backup_file": "Файл резервной копии", "backup_password": "Пароль резервной копии", + "balance": "Баланс", "balance_page": "Страница баланса", "bill_amount": "Сумма счета", "billing_address_info": "Если вас попросят указать платежный адрес, укажите адрес доставки", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index b7dffc565a..7778dd876e 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -70,6 +70,7 @@ "backup": "สำรองข้อมูล", "backup_file": "ไฟล์สำรองข้อมูล", "backup_password": "รหัสผ่านสำรองข้อมูล", + "balance": "สมดุล", "balance_page": "หน้ายอดคงเหลือ", "bill_amount": "จำนวนบิล", "billing_address_info": "ถ้าถูกร้องขอที่อยู่สำหรับการวางบิล ให้ใช้ที่อยู่จัดส่งของคุณ", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 35c777530d..4e1dcfa8fe 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -70,6 +70,7 @@ "backup": "Backup", "backup_file": "Backup file", "backup_password": "Backup password", + "balance": "Balansehin", "balance_page": "Pahina ng Balanse", "bill_amount": "Halaga ng Bill", "billing_address_info": "Kung tatanungin ang isang address ng pagsingil, ibigay ang iyong address sa pagpapadala", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index a384bc08ea..321e2a0e14 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -70,6 +70,7 @@ "backup": "Yedek", "backup_file": "Yedek dosyası", "backup_password": "Yedek parolası", + "balance": "Denge", "balance_page": "Bakiye Sayfası", "bill_amount": "Fatura Tutarı", "billing_address_info": "Eğer fatura adresi istenirse, kargo adresinizi girin", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index d0cdfa7059..407d19447b 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -70,6 +70,7 @@ "backup": "Резервна копія", "backup_file": "Файл резервної копії", "backup_password": "Пароль резервної копії", + "balance": "Балансувати", "balance_page": "Сторінка балансу", "bill_amount": "Сума рахунку", "billing_address_info": "Якщо буде запропоновано платіжну адресу, вкажіть свою адресу доставки", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index ef911df1ef..62c5eda8ee 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -70,6 +70,7 @@ "backup": "بیک اپ", "backup_file": "بیک اپ فائل", "backup_password": "بیک اپ پاس ورڈ", + "balance": "بقیہ", "balance_page": "بیلنس صفحہ", "bill_amount": "بل رقم", "billing_address_info": "اگر آپ سے بلنگ کا پتہ پوچھا جائے تو اپنا شپنگ ایڈریس فراہم کریں۔", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 2fe812c08e..a90ddd3ae4 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -70,6 +70,7 @@ "backup": "Ṣẹ̀dà", "backup_file": "Ṣẹ̀dà akọsílẹ̀", "backup_password": "Ṣẹ̀dà ọ̀rọ̀ aṣínà", + "balance": "Iwọntunwọnsi", "balance_page": "Oju-iwe iwọntunwọnsi", "bill_amount": "Iye ìwé owó", "billing_address_info": "Tí ọlọ́jà bá bèèrè àdírẹ́sì sísan yín, fún òun ni àdírẹ́sì t'á ránṣẹ́ káàdì yìí sí", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 1a4d0b3414..9d5bd9a191 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -70,6 +70,7 @@ "backup": "备份", "backup_file": "备份文件", "backup_password": "备份密码", + "balance": "平衡", "balance_page": "余额页", "bill_amount": "账单金额", "billing_address_info": "如果要求提供帐单地址,请提供您的送货地址", From 058ff6aeecdb2705e15f97db44b5c04a1c0dce97 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 8 Apr 2024 21:06:49 -0300 Subject: [PATCH 24/75] chore: print --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 1dd11bad3e..c21bc6c902 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -370,10 +370,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } if (addressRecord.address != address) { - print([ - addressRecord.address, - addressRecord.name.isEmpty ? "Silent Payments" : addressRecord.name - ]); addressesMap[addressRecord.address] = addressRecord.name.isEmpty ? "Silent Payments" : "Silent Payments - " + addressRecord.name; From 8ea2e6ee407d48389e12c3829ee7807a4e74a741 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 9 Apr 2024 12:15:21 -0300 Subject: [PATCH 25/75] feat: single block scan, rescan by date working for btc mainnet --- cw_bitcoin/lib/electrum_wallet.dart | 33 +++++++++++------ cw_core/lib/get_height_by_date.dart | 29 +++++++++++++++ lib/bitcoin/cw_bitcoin.dart | 8 ++++ lib/src/screens/rescan/rescan_page.dart | 14 ++++--- lib/src/widgets/blockchain_height_widget.dart | 37 ++++++++++++++++++- lib/view_model/rescan_view_model.dart | 16 ++++++-- res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 1 + res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 1 + res/values/strings_ko.arb | 1 + res/values/strings_my.arb | 1 + res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 5 ++- res/values/strings_ru.arb | 1 + res/values/strings_th.arb | 1 + res/values/strings_tl.arb | 1 + res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 1 + res/values/strings_yo.arb | 1 + res/values/strings_zh.arb | 1 + tool/configure.dart | 4 +- 33 files changed, 147 insertions(+), 24 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 244faf3e9b..a5a29b6a6f 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -201,7 +201,7 @@ abstract class ElectrumWalletBase } @action - Future _setListeners(int height, {int? chainTip}) async { + Future _setListeners(int height, {int? chainTip, bool? doSingleScan}) async { final currentChainTip = chainTip ?? await electrumClient.getCurrentBlockChainTip() ?? 0; syncStatus = AttemptingSyncStatus(); @@ -223,6 +223,7 @@ abstract class ElectrumWalletBase transactionHistoryIds: transactionHistory.transactions.keys.toList(), node: ScanNode(node!.uri, node!.useSSL), labels: walletAddresses.labels, + isSingleScan: doSingleScan ?? false, )); await for (var message in receivePort) { @@ -981,9 +982,10 @@ abstract class ElectrumWalletBase @action @override - Future rescan({required int height, int? chainTip, ScanData? scanData}) async { + Future rescan( + {required int height, int? chainTip, ScanData? scanData, bool? doSingleScan}) async { silentPaymentsScanningActive = true; - _setListeners(height); + _setListeners(height, doSingleScan: doSingleScan); } @override @@ -1606,6 +1608,7 @@ class ScanData { final ElectrumClient electrumClient; final List transactionHistoryIds; final Map labels; + final bool isSingleScan; ScanData({ required this.sendPort, @@ -1617,6 +1620,7 @@ class ScanData { required this.electrumClient, required this.transactionHistoryIds, required this.labels, + required this.isSingleScan, }); factory ScanData.fromHeight(ScanData scanData, int newHeight) { @@ -1630,6 +1634,7 @@ class ScanData { transactionHistoryIds: scanData.transactionHistoryIds, electrumClient: scanData.electrumClient, labels: scanData.labels, + isSingleScan: scanData.isSingleScan, ); } } @@ -1689,11 +1694,17 @@ Future startRefresh(ScanData scanData) async { while (true) { lastKnownBlockHeight = syncHeight; - final syncingStatus = - SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight); + SyncingSyncStatus syncingStatus; + if (scanData.isSingleScan) { + syncingStatus = SyncingSyncStatus(1, 0); + } else { + syncingStatus = + SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight); + } + scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus)); - if (syncingStatus.blocksLeft <= 0) { + if (syncingStatus.blocksLeft <= 0 || (scanData.isSingleScan && scanData.height != syncHeight)) { scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus())); return; } @@ -1701,7 +1712,8 @@ Future startRefresh(ScanData scanData) async { try { final electrumClient = await getElectrumConnection(); - final scanningBlockCount = scanData.network == BitcoinNetwork.testnet ? 50 : 10; + final scanningBlockCount = + scanData.isSingleScan ? 1 : (scanData.network == BitcoinNetwork.testnet ? 1 : 10); Map? tweaks; try { @@ -1743,8 +1755,7 @@ Future startRefresh(ScanData scanData) async { scanData.silentAddress.b_scan, scanData.silentAddress.B_spend, outputPubkeys.values - .map((o) => getScriptFromOutput( - o["pubkey"].toString(), int.parse(o["amount"].toString()))) + .map((o) => getScriptFromOutput(o[0].toString(), int.parse(o[1].toString()))) .toList(), precomputedLabels: scanData.labels, ); @@ -1770,9 +1781,9 @@ Future startRefresh(ScanData scanData) async { int? amount; int? pos; outputPubkeys.entries.firstWhere((k) { - final matches = k.value["pubkey"] == key; + final matches = k.value[0] == key; if (matches) { - amount = int.parse(k.value["amount"].toString()); + amount = int.parse(k.value[1].toString()); pos = int.parse(k.key.toString()); return true; } diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index 6f3ccaf686..1dc624a7ff 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -242,3 +242,32 @@ Future getHavenCurrentHeight() async { throw Exception('Failed to load current blockchain height'); } } + +// Data taken from https://timechaincalendar.com/ +const bitcoinDates = { + "2024-04": 837182, + "2024-03": 832623, + "2024-02": 828319, + "2024-01": 823807, + "2023-12": 819206, + "2023-11": 814765, + "2023-10": 810098, + "2023-09": 805675, + "2023-08": 801140, + "2023-07": 796640, + "2023-06": 792330, + "2023-05": 787733, + "2023-04": 783403, + "2023-03": 778740, + "2023-02": 774525, + "2023-01": 769810, +}; + +int getBitcoinHeightByDate({required DateTime date}) { + String closestKey = + bitcoinDates.keys.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => ''); + + final oldestHeight = bitcoinDates.values.last; + + return bitcoinDates[closestKey] ?? oldestHeight; +} diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index bcbb0bc07b..49fdcedab6 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -335,4 +335,12 @@ class CWBitcoin extends Bitcoin { final bitcoinWallet = wallet as ElectrumWallet; return bitcoinWallet.isTestnet ?? false; } + + @override + int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date); + + void rescan(Object wallet, {required int height, bool? doSingleScan}) { + final bitcoinWallet = wallet as ElectrumWallet; + bitcoinWallet.rescan(height: height, doSingleScan: doSingleScan); + } } diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index f4dc865f03..da0f351378 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -21,11 +21,15 @@ class RescanPage extends BasePage { return Padding( padding: EdgeInsets.only(left: 24, right: 24, bottom: 24), child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - BlockchainHeightWidget( - key: _blockchainHeightWidgetKey, - onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value, - isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan, - ), + Observer( + builder: (_) => BlockchainHeightWidget( + key: _blockchainHeightWidgetKey, + onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value, + isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan, + doSingleScan: _rescanViewModel.doSingleScan, + toggleSingleScan: () => + _rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan, + )), Observer( builder: (_) => LoadingPrimaryButton( isLoading: _rescanViewModel.state == RescanWalletState.rescaning, diff --git a/lib/src/widgets/blockchain_height_widget.dart b/lib/src/widgets/blockchain_height_widget.dart index fcfeedda32..d85680cc8d 100644 --- a/lib/src/widgets/blockchain_height_widget.dart +++ b/lib/src/widgets/blockchain_height_widget.dart @@ -1,3 +1,5 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/src/widgets/standard_switch.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/utils/date_picker.dart'; import 'package:flutter/material.dart'; @@ -14,6 +16,8 @@ class BlockchainHeightWidget extends StatefulWidget { this.onHeightOrDateEntered, this.hasDatePicker = true, this.isSilentPaymentsScan = false, + this.toggleSingleScan, + this.doSingleScan = false, }) : super(key: key); final Function(int)? onHeightChange; @@ -21,6 +25,8 @@ class BlockchainHeightWidget extends StatefulWidget { final FocusNode? focusNode; final bool hasDatePicker; final bool isSilentPaymentsScan; + final bool doSingleScan; + final Function()? toggleSingleScan; @override State createState() => BlockchainHeightState(); @@ -101,6 +107,30 @@ class BlockchainHeightState extends State { )) ], ), + if (widget.isSilentPaymentsScan) + Padding( + padding: EdgeInsets.only(top: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + S.of(context).scan_one_block, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + color: Theme.of(context).extension()!.titleColor, + ), + ), + Padding( + padding: const EdgeInsets.only(right: 8), + child: StandardSwitch( + value: widget.doSingleScan, + onTaped: () => widget.toggleSingleScan?.call(), + ), + ) + ], + ), + ), Padding( padding: EdgeInsets.only(left: 40, right: 40, top: 24), child: Text( @@ -126,7 +156,12 @@ class BlockchainHeightState extends State { lastDate: now); if (date != null) { - final height = monero!.getHeightByDate(date: date); + int height; + if (widget.isSilentPaymentsScan) { + height = bitcoin!.getHeightByDate(date: date); + } else { + height = monero!.getHeightByDate(date: date); + } setState(() { dateController.text = DateFormat('yyyy-MM-dd').format(date); restoreHeightController.text = '$height'; diff --git a/lib/view_model/rescan_view_model.dart b/lib/view_model/rescan_view_model.dart index b10e29e698..ae7baf0083 100644 --- a/lib/view_model/rescan_view_model.dart +++ b/lib/view_model/rescan_view_model.dart @@ -12,7 +12,8 @@ enum RescanWalletState { rescaning, none } abstract class RescanViewModelBase with Store { RescanViewModelBase(this._wallet) : state = RescanWalletState.none, - isButtonEnabled = false; + isButtonEnabled = false, + doSingleScan = false; final WalletBase _wallet; @@ -22,14 +23,21 @@ abstract class RescanViewModelBase with Store { @observable bool isButtonEnabled; + @observable + bool doSingleScan; + @computed - bool get isSilentPaymentsScan => bitcoin!.hasSelectedSilentPayments(_wallet); + bool get isSilentPaymentsScan => _wallet.type == WalletType.bitcoin; @action Future rescanCurrentWallet({required int restoreHeight}) async { state = RescanWalletState.rescaning; - _wallet.rescan(height: restoreHeight); - if (_wallet.type != WalletType.bitcoin) _wallet.transactionHistory.clear(); + if (_wallet.type != WalletType.bitcoin) { + _wallet.rescan(height: restoreHeight); + _wallet.transactionHistory.clear(); + } else { + bitcoin!.rescan(_wallet, height: restoreHeight, doSingleScan: doSingleScan); + } state = RescanWalletState.none; } } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 2cdc983953..44df63a3ab 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "حفظ كلمة المرور الاحتياطية", "save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ", "saved_the_trade_id": "لقد تم حفظ معرف العملية", + "scan_one_block": "مسح كتلة واحدة", "scan_qr_code": "امسح رمز QR ضوئيًا", "scan_qr_code_to_get_address": "امسح ال QR للحصول على العنوان", "scan_qr_on_device": " ﺮﺧﺁ ﺯﺎﻬﺟ ﻰﻠﻋ ﺎﻴًﺋﻮﺿ ﺍﺬﻫ ﺔﻌﻳﺮﺴﻟﺍ ﺔﺑﺎﺠﺘﺳﻻﺍ ﺰﻣﺭ ﺢﺴﻤﺑ ﻢﻗ", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 7b7c62e506..185e22e0b6 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "Запазване на паролата за възстановяване", "save_to_downloads": "Запазване в Изтегляния", "saved_the_trade_id": "Запазих trade ID-то", + "scan_one_block": "Сканирайте един блок", "scan_qr_code": "Сканирайте QR кода, за да получите адреса", "scan_qr_code_to_get_address": "Сканирайте QR кода, за да получите адреса", "scan_qr_on_device": "Сканирайте този QR код на друго устройство", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index b032f4ee31..2146ae7251 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "Uložit heslo pro zálohy", "save_to_downloads": "Uložit do Stažených souborů", "saved_the_trade_id": "Uložil jsem si ID transakce (trade ID)", + "scan_one_block": "Prohledejte jeden blok", "scan_qr_code": "Naskenujte QR kód pro získání adresy", "scan_qr_code_to_get_address": "Prohledejte QR kód a získejte adresu", "scan_qr_on_device": "Naskenujte tento QR kód na jiném zařízení", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 0168f415cd..f4b6cd0df5 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -512,6 +512,7 @@ "save_backup_password_alert": "Sicherungskennwort speichern", "save_to_downloads": "Unter „Downloads“ speichern", "saved_the_trade_id": "Ich habe die Handels-ID gespeichert", + "scan_one_block": "Einen Block scannen", "scan_qr_code": "QR-Code scannen", "scan_qr_code_to_get_address": "Scannen Sie den QR-Code, um die Adresse zu erhalten", "scan_qr_on_device": "Scannen Sie diesen QR-Code auf einem anderen Gerät", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index a22844bc18..73b2bde1cc 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "Save backup password", "save_to_downloads": "Save to Downloads", "saved_the_trade_id": "I've saved the trade ID", + "scan_one_block": "Scan one block", "scan_qr_code": "Scan QR code", "scan_qr_code_to_get_address": "Scan the QR code to get the address", "scan_qr_on_device": "Scan this QR code on another device", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 6a190abc06..03c4775689 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -512,6 +512,7 @@ "save_backup_password_alert": "Guardar contraseña de respaldo", "save_to_downloads": "Guardar en Descargas", "saved_the_trade_id": "He salvado comercial ID", + "scan_one_block": "Escanear un bloque", "scan_qr_code": "Escanear código QR", "scan_qr_code_to_get_address": "Escanee el código QR para obtener la dirección", "scan_qr_on_device": "Escanea este código QR en otro dispositivo", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index ecfa4bc71c..583fdd489e 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "Enregistrer le mot de passe de sauvegarde", "save_to_downloads": "Enregistrer dans les téléchargements", "saved_the_trade_id": "J'ai sauvegardé l'ID d'échange", + "scan_one_block": "Scanner un bloc", "scan_qr_code": "Scannez le QR code", "scan_qr_code_to_get_address": "Scannez le QR code pour obtenir l'adresse", "scan_qr_on_device": "Scannez ce code QR sur un autre appareil", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 696fc79907..4c153ee02f 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -513,6 +513,7 @@ "save_backup_password_alert": "Ajiye kalmar sirri ta ajiya", "save_to_downloads": "Ajiye zuwa Zazzagewa", "saved_the_trade_id": "Na ajiye ID na ciniki", + "scan_one_block": "Duba toshe daya", "scan_qr_code": "Gani QR kodin", "scan_qr_code_to_get_address": "Duba lambar QR don samun adireshin", "scan_qr_on_device": "Duba wannan lambar QR akan wata na'ura", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index c45cc96da2..bbf9749158 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -513,6 +513,7 @@ "save_backup_password_alert": "बैकअप पासवर्ड सेव करें", "save_to_downloads": "डाउनलोड में सहेजें", "saved_the_trade_id": "मैंने व्यापार बचा लिया है ID", + "scan_one_block": "एक ब्लॉक को स्कैन करना", "scan_qr_code": "स्कैन क्यू आर कोड", "scan_qr_code_to_get_address": "पता प्राप्त करने के लिए QR कोड स्कैन करें", "scan_qr_on_device": "इस QR कोड को किसी अन्य डिवाइस पर स्कैन करें", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 03e99ac551..04ac5acd4e 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "Spremi lozinku za sigurnosnu kopiju", "save_to_downloads": "Spremi u Preuzimanja", "saved_the_trade_id": "Spremio/la sam transakcijski ID", + "scan_one_block": "Skenirajte jedan blok", "scan_qr_code": "Skenirajte QR kod", "scan_qr_code_to_get_address": "Skeniraj QR kod za dobivanje adrese", "scan_qr_on_device": "Skenirajte ovaj QR kod na drugom uređaju", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 33745c6c95..f45a66b5b5 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -514,6 +514,7 @@ "save_backup_password_alert": "Simpan kata sandi cadangan", "save_to_downloads": "Simpan ke Unduhan", "saved_the_trade_id": "Saya telah menyimpan ID perdagangan", + "scan_one_block": "Pindai satu blok", "scan_qr_code": "Scan kode QR untuk mendapatkan alamat", "scan_qr_code_to_get_address": "Pindai kode QR untuk mendapatkan alamat", "scan_qr_on_device": "Pindai kode QR ini di perangkat lain", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 77884037ae..133ef62d5b 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -513,6 +513,7 @@ "save_backup_password_alert": "Salva password Backup", "save_to_downloads": "Salva in Download", "saved_the_trade_id": "Ho salvato l'ID dello scambio", + "scan_one_block": "Scansionare un blocco", "scan_qr_code": "Scansiona il codice QR", "scan_qr_code_to_get_address": "Scansiona il codice QR per ottenere l'indirizzo", "scan_qr_on_device": "Scansiona questo codice QR su un altro dispositivo", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 36932fbc0d..a6191460be 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -512,6 +512,7 @@ "save_backup_password_alert": "バックアップパスワードを保存する", "save_to_downloads": "ダウンロードに保存", "saved_the_trade_id": "取引IDを保存しました", + "scan_one_block": "1つのブロックをスキャンします", "scan_qr_code": "QRコードをスキャン", "scan_qr_code_to_get_address": "QRコードをスキャンして住所を取得します", "scan_qr_on_device": "別のデバイスでこの QR コードをスキャンします", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index eb6f1ce726..9cd30a13f1 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -512,6 +512,7 @@ "save_backup_password_alert": "백업 비밀번호 저장", "save_to_downloads": "다운로드에 저장", "saved_the_trade_id": "거래 ID를 저장했습니다", + "scan_one_block": "하나의 블록을 스캔하십시오", "scan_qr_code": "QR 코드 스캔", "scan_qr_code_to_get_address": "QR 코드를 스캔하여 주소를 얻습니다.", "scan_qr_on_device": "다른 기기에서 이 QR 코드를 스캔하세요.", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 77fa298ebe..ee1fc90c7f 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "အရန်စကားဝှက်ကို သိမ်းဆည်းပါ။", "save_to_downloads": "ဒေါင်းလုဒ်များထံ သိမ်းဆည်းပါ။", "saved_the_trade_id": "ကုန်သွယ်မှု ID ကို သိမ်းဆည်းပြီးပါပြီ။", + "scan_one_block": "တစ်ကွက်ကိုစကင်ဖတ်စစ်ဆေးပါ", "scan_qr_code": "QR ကုဒ်ကို စကင်န်ဖတ်ပါ။", "scan_qr_code_to_get_address": "လိပ်စာရယူရန် QR ကုဒ်ကို စကင်န်ဖတ်ပါ။", "scan_qr_on_device": "အခြားစက်တွင် ဤ QR ကုဒ်ကို စကင်ဖတ်ပါ။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 4a60a994e9..ae7a32dbb6 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "Bewaar back-upwachtwoord", "save_to_downloads": "Opslaan in downloads", "saved_the_trade_id": "Ik heb de ruil-ID opgeslagen", + "scan_one_block": "Scan een blok", "scan_qr_code": "Scan QR-code", "scan_qr_code_to_get_address": "Scan de QR-code om het adres te krijgen", "scan_qr_on_device": "Scan deze QR-code op een ander apparaat", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 54859366dd..7e7cdfb758 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "Zapisz hasło kopii zapasowej", "save_to_downloads": "Zapisz w Pobranych", "saved_the_trade_id": "Zapisałem ID", + "scan_one_block": "Zeskanuj jeden blok", "scan_qr_code": "Skanowania QR code", "scan_qr_code_to_get_address": "Zeskanuj kod QR, aby uzyskać adres", "scan_qr_on_device": "Zeskanuj ten kod QR na innym urządzeniu", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index eb1c8e3d83..1526b6aad2 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -513,6 +513,7 @@ "save_backup_password_alert": "Salvar senha de backup", "save_to_downloads": "Salvar em Downloads", "saved_the_trade_id": "ID da troca salvo", + "scan_one_block": "Escanear um bloco", "scan_qr_code": "Escanear código QR", "scan_qr_code_to_get_address": "Digitalize o código QR para obter o endereço", "scan_qr_on_device": "Digitalize este código QR em outro dispositivo", @@ -624,7 +625,7 @@ "silent_payments": "Pagamentos silenciosos", "silent_payments_disclaimer": "Novos endereços não são novas identidades. É uma reutilização de uma identidade existente com um rótulo diferente.", "silent_payments_scan_from_date": "Escanear a partir da data", - "silent_payments_scan_from_date_or_blockheight": "Por favor, insira a altura do bloco que deseja iniciar o escaneamento para obter pagamentos silenciosos ou use a data. Você pode escolher se a carteira continua digitalizando cada bloco ou verifica apenas a altura especificada.", + "silent_payments_scan_from_date_or_blockheight": "Por favor, insira a altura do bloco que deseja iniciar o escaneamento para obter pagamentos silenciosos ou use a data. Você pode escolher se a carteira continua escaneando cada bloco ou verifica apenas a altura especificada.", "silent_payments_scan_from_height": "Escanear a partir da altura do bloco", "silent_payments_scanning": "Escanear Pagamentos Silenciosos", "slidable": "Deslizável", @@ -823,4 +824,4 @@ "you_will_get": "Converter para", "you_will_send": "Converter de", "yy": "aa" -} \ No newline at end of file +} diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 946ef093c7..0534f52d77 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -512,6 +512,7 @@ "save_backup_password_alert": "Сохранить пароль резервной копии", "save_to_downloads": "Сохранить в загрузках", "saved_the_trade_id": "Я сохранил ID сделки", + "scan_one_block": "Сканируйте один блок", "scan_qr_code": "Сканировать QR-код", "scan_qr_code_to_get_address": "Отсканируйте QR-код для получения адреса", "scan_qr_on_device": "Отсканируйте этот QR-код на другом устройстве", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 7778dd876e..434940a312 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "บันทึกรหัสผ่านสำรอง", "save_to_downloads": "บันทึกลงดาวน์โหลด", "saved_the_trade_id": "ฉันได้บันทึก ID ของการซื้อขายแล้ว", + "scan_one_block": "สแกนหนึ่งบล็อก", "scan_qr_code": "สแกนรหัส QR", "scan_qr_code_to_get_address": "สแกน QR code เพื่อรับที่อยู่", "scan_qr_on_device": "สแกนโค้ด QR นี้บนอุปกรณ์อื่น", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 4e1dcfa8fe..08be1b7277 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "I -save ang backup password", "save_to_downloads": "I -save sa mga pag -download", "saved_the_trade_id": "Nai -save ko ang trade ID", + "scan_one_block": "I -scan ang isang bloke", "scan_qr_code": "I -scan ang QR Code", "scan_qr_code_to_get_address": "I -scan ang QR code upang makuha ang address", "scan_qr_on_device": "I-scan ang QR code na ito sa ibang device", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 321e2a0e14..24f79222e5 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "Yedek parolasını kaydet", "save_to_downloads": "İndirilenlere Kaydet", "saved_the_trade_id": "Takas ID'imi kaydettim", + "scan_one_block": "Bir bloğu tara", "scan_qr_code": "QR kodunu tarayın", "scan_qr_code_to_get_address": "Adresi getirmek için QR kodunu tara", "scan_qr_on_device": "Bu QR kodunu başka bir cihazda tarayın", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 407d19447b..f3643a64b8 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -512,6 +512,7 @@ "save_backup_password_alert": "Зберегти пароль резервної копії", "save_to_downloads": "Зберегти до завантажень", "saved_the_trade_id": "Я зберіг ID операції", + "scan_one_block": "Сканувати один блок", "scan_qr_code": "Відскануйте QR-код", "scan_qr_code_to_get_address": "Скануйте QR-код для одержання адреси", "scan_qr_on_device": "Відскануйте цей QR-код на іншому пристрої", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 62c5eda8ee..b39e211ef5 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -513,6 +513,7 @@ "save_backup_password_alert": "بیک اپ پاس ورڈ محفوظ کریں۔", "save_to_downloads": "۔ﮟﯾﺮﮐ ﻅﻮﻔﺤﻣ ﮟﯿﻣ ﺯﮈﻮﻟ ﻥﺅﺍﮈ", "saved_the_trade_id": "میں نے تجارتی ID محفوظ کر لی ہے۔", + "scan_one_block": "ایک بلاک اسکین کریں", "scan_qr_code": "پتہ حاصل کرنے کے لیے QR کوڈ اسکین کریں۔", "scan_qr_code_to_get_address": "پتہ حاصل کرنے کے لئے QR کوڈ کو اسکین کریں", "scan_qr_on_device": " ۔ﮟﯾﺮﮐ ﻦﯿﮑﺳﺍ ﺮﭘ ﺲﺋﺍﻮﯾﮈ ﺭﻭﺍ ﯽﺴﮐ ﻮﮐ ﮈﻮﮐ QR ﺱﺍ", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index a90ddd3ae4..ea9caf6370 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -512,6 +512,7 @@ "save_backup_password_alert": "Pamọ́ ọ̀rọ̀ aṣínà ti ẹ̀dà", "save_to_downloads": "Fipamọ si Awọn igbasilẹ", "saved_the_trade_id": "Mo ti pamọ́ àmì ìdánimọ̀ pàṣípààrọ̀", + "scan_one_block": "Ọlọjẹ ọkan bulọki", "scan_qr_code": "Yan QR koodu", "scan_qr_code_to_get_address": "Ṣayẹwo koodu QR naa lati gba adirẹsi naa", "scan_qr_on_device": "Ṣe ayẹwo koodu QR yii lori ẹrọ miiran", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 9d5bd9a191..822dc0b166 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -511,6 +511,7 @@ "save_backup_password_alert": "保存备份密码", "save_to_downloads": "保存到下载", "saved_the_trade_id": "我已经保存了交易编号", + "scan_one_block": "扫描一个街区", "scan_qr_code": "扫描二维码", "scan_qr_code_to_get_address": "扫描二维码获取地址", "scan_qr_on_device": "在另一台设备上扫描此二维码", diff --git a/tool/configure.dart b/tool/configure.dart index b082f64701..9c5f4b5907 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -87,7 +87,7 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_bitcoin/litecoin_wallet_service.dart'; -import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; +import 'package:cw_core/get_height_by_date.dart'; import 'package:mobx/mobx.dart'; """; const bitcoinCwPart = "part 'cw_bitcoin.dart';"; @@ -167,6 +167,8 @@ abstract class Bitcoin { Future isChangeSufficientForFee(Object wallet, String txId, String newFee); int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size}); int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size}); + int getHeightByDate({required DateTime date}); + void rescan(Object wallet, {required int height, bool? doSingleScan}); } """; From ddbb63ae462501a33c5a6b3520c2d5c3b4adb357 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 9 Apr 2024 16:37:00 -0300 Subject: [PATCH 26/75] feat: new cake features page replace market page, move sp scan toggle, auto switch node pop up alert --- assets/images/cards.svg | 65 ++++++ lib/bitcoin/cw_bitcoin.dart | 26 ++- lib/di.dart | 11 +- lib/src/screens/dashboard/dashboard_page.dart | 10 +- .../desktop_dashboard_actions.dart | 8 +- .../screens/dashboard/pages/balance_page.dart | 44 ---- .../dashboard/pages/cake_features_page.dart | 204 ++++++++++++++++++ .../dashboard/pages/market_place_page.dart | 105 --------- lib/src/screens/rescan/rescan_page.dart | 32 +++ lib/src/widgets/dashboard_card_widget.dart | 63 ++++-- .../dashboard/cake_features_view_model.dart | 16 ++ .../dashboard/dashboard_view_model.dart | 2 +- .../dashboard/market_place_view_model.dart | 17 -- lib/view_model/rescan_view_model.dart | 17 +- res/values/strings_ar.arb | 4 + res/values/strings_bg.arb | 4 + res/values/strings_cs.arb | 4 + res/values/strings_de.arb | 4 + res/values/strings_en.arb | 4 + res/values/strings_es.arb | 4 + res/values/strings_fr.arb | 4 + res/values/strings_ha.arb | 4 + res/values/strings_hi.arb | 4 + res/values/strings_hr.arb | 4 + res/values/strings_id.arb | 4 + res/values/strings_it.arb | 4 + res/values/strings_ja.arb | 4 + res/values/strings_ko.arb | 4 + res/values/strings_my.arb | 4 + res/values/strings_nl.arb | 4 + res/values/strings_pl.arb | 4 + res/values/strings_pt.arb | 4 + res/values/strings_ru.arb | 4 + res/values/strings_th.arb | 4 + res/values/strings_tl.arb | 4 + res/values/strings_tr.arb | 4 + res/values/strings_uk.arb | 4 + res/values/strings_ur.arb | 4 + res/values/strings_yo.arb | 4 + res/values/strings_zh.arb | 4 + tool/configure.dart | 1 + 41 files changed, 516 insertions(+), 209 deletions(-) create mode 100644 assets/images/cards.svg create mode 100644 lib/src/screens/dashboard/pages/cake_features_page.dart delete mode 100644 lib/src/screens/dashboard/pages/market_place_page.dart create mode 100644 lib/view_model/dashboard/cake_features_view_model.dart delete mode 100644 lib/view_model/dashboard/market_place_view_model.dart diff --git a/assets/images/cards.svg b/assets/images/cards.svg new file mode 100644 index 0000000000..699f9d3110 --- /dev/null +++ b/assets/images/cards.svg @@ -0,0 +1,65 @@ + + + + diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 49fdcedab6..db1ebcf4ab 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -326,8 +326,15 @@ class CWBitcoin extends Bitcoin { return bitcoinWallet.silentPaymentsScanningActive; } - void setScanningActive(Object wallet, bool active) { + Future setScanningActive(Object wallet, SettingsStore settingsStore, bool active) async { final bitcoinWallet = wallet as ElectrumWallet; + // TODO: always when setting to scanning active, will force switch nodes. Remove when not needed anymore + if (!getNodeIsCakeElectrs(wallet)) { + final node = Node(useSSL: false, uri: '198.58.111.154:50002'); + node.type = WalletType.bitcoin; + settingsStore.nodes[WalletType.bitcoin] = node; + await bitcoinWallet.connectToNode(node: node); + } bitcoinWallet.setSilentPaymentsScanning(active); } @@ -339,8 +346,23 @@ class CWBitcoin extends Bitcoin { @override int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date); - void rescan(Object wallet, {required int height, bool? doSingleScan}) { + Future rescan(Object wallet, SettingsStore settingsStore, + {required int height, bool? doSingleScan}) async { final bitcoinWallet = wallet as ElectrumWallet; + // TODO: always when setting to scanning active, will force switch nodes. Remove when not needed anymore + if (!getNodeIsCakeElectrs(wallet)) { + final node = Node(useSSL: false, uri: '198.58.111.154:50002'); + node.type = WalletType.bitcoin; + settingsStore.nodes[WalletType.bitcoin] = node; + await bitcoinWallet.connectToNode(node: node); + } bitcoinWallet.rescan(height: height, doSingleScan: doSingleScan); } + + bool getNodeIsCakeElectrs(Object wallet) { + final bitcoinWallet = wallet as ElectrumWallet; + final node = bitcoinWallet.node; + + return node?.uri.host == '198.58.111.154' && node?.uri.port == 50002; + } } diff --git a/lib/di.dart b/lib/di.dart index 59a94d1eb1..cf5b3f8dcc 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -69,7 +69,7 @@ import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; import 'package:cake_wallet/view_model/anonpay_details_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart'; -import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart'; @@ -890,7 +890,8 @@ Future setup({ (onSuccessfulPinSetup, _) => SetupPinCodePage(getIt.get(), onSuccessfulPinSetup: onSuccessfulPinSetup)); - getIt.registerFactory(() => RescanViewModel(getIt.get().wallet!)); + getIt.registerFactory( + () => RescanViewModel(getIt.get().wallet!, getIt.get())); getIt.registerFactory(() => RescanPage(getIt.get())); @@ -1048,7 +1049,7 @@ Future setup({ getIt.registerFactory(() => IoniaGiftCardsListViewModel(ioniaService: getIt.get())); - getIt.registerFactory(() => MarketPlaceViewModel(getIt.get())); + getIt.registerFactory(() => CakeFeaturesViewModel(getIt.get())); getIt.registerFactory(() => IoniaAuthViewModel(ioniaService: getIt.get())); @@ -1145,9 +1146,9 @@ Future setup({ getIt.registerFactory(() => IoniaAccountCardsPage(getIt.get())); getIt.registerFactoryParam( - (TransactionInfo transactionInfo, _) => RBFDetailsPage( + (TransactionInfo transactionInfo, _) => RBFDetailsPage( transactionDetailsViewModel: - getIt.get(param1: transactionInfo))); + getIt.get(param1: transactionInfo))); getIt.registerFactory(() => AnonPayApi( useTorOnly: getIt.get().exchangeStatus == ExchangeApiMode.torOnly, diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index ed06f47040..2c72995a9c 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -4,7 +4,7 @@ import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/main_actions.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart'; -import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/cake_features_page.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart'; import 'package:cake_wallet/src/widgets/gradient_background.dart'; import 'package:cake_wallet/src/widgets/services_updates_widget.dart'; @@ -12,7 +12,7 @@ import 'package:cake_wallet/src/widgets/vulnerable_seeds_popup.dart'; import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/version_comparator.dart'; -import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/yat_emoji_id.dart'; @@ -291,10 +291,10 @@ class _DashboardPageView extends BasePage { if (dashboardViewModel.shouldShowMarketPlaceInDashboard) { pages.add( Semantics( - label: S.of(context).market_place, - child: MarketPlacePage( + label: 'Cake ${S.of(context).features}', + child: CakeFeaturesPage( dashboardViewModel: dashboardViewModel, - marketPlaceViewModel: getIt.get(), + cakeFeaturesViewModel: getIt.get(), ), ), ); diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart index 20ddea361e..d36c06013f 100644 --- a/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart @@ -1,9 +1,9 @@ import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/main_actions.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_action_button.dart'; -import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/cake_features_page.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; -import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -74,9 +74,9 @@ class DesktopDashboardActions extends StatelessWidget { ], ), Expanded( - child: MarketPlacePage( + child: CakeFeaturesPage( dashboardViewModel: dashboardViewModel, - marketPlaceViewModel: getIt.get(), + cakeFeaturesViewModel: getIt.get(), ), ), ], diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index 2c77ffceaa..312cf3240d 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -209,13 +209,6 @@ class CryptoBalanceWidget extends StatelessWidget { currency: balance.asset, hasAdditionalBalance: dashboardViewModel.balanceViewModel.hasAdditionalBalance, - hasSilentPayments: dashboardViewModel.balanceViewModel.hasSilentPayments, - silentPaymentsScanningActive: - dashboardViewModel.silentPaymentsScanningActive, - setSilentPaymentsScanning: () => - dashboardViewModel.setSilentPaymentsScanning( - !dashboardViewModel.silentPaymentsScanningActive, - ), isTestnet: dashboardViewModel.isTestnet, ); }); @@ -242,9 +235,6 @@ class BalanceRowWidget extends StatelessWidget { required this.frozenFiatBalance, required this.currency, required this.hasAdditionalBalance, - required this.hasSilentPayments, - required this.silentPaymentsScanningActive, - required this.setSilentPaymentsScanning, required this.isTestnet, super.key, }); @@ -259,10 +249,7 @@ class BalanceRowWidget extends StatelessWidget { final String frozenFiatBalance; final CryptoCurrency currency; final bool hasAdditionalBalance; - final bool hasSilentPayments; - final bool silentPaymentsScanningActive; final bool isTestnet; - final void Function() setSilentPaymentsScanning; // void _showBalanceDescription(BuildContext context) { // showPopUp( @@ -505,37 +492,6 @@ class BalanceRowWidget extends StatelessWidget { ), ], ), - if (hasSilentPayments) ...[ - Padding( - padding: const EdgeInsets.only(right: 8, top: 8), - child: Divider( - color: Theme.of(context).extension()!.labelTextColor, - thickness: 1, - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - AutoSizeText( - S.of(context).silent_payments_scanning, - style: TextStyle( - fontSize: 14, - fontFamily: 'Lato', - fontWeight: FontWeight.w400, - color: Theme.of(context).extension()!.assetTitleColor, - height: 1, - ), - maxLines: 1, - textAlign: TextAlign.center, - ), - Padding( - padding: const EdgeInsets.only(right: 8), - child: StandardSwitch( - value: silentPaymentsScanningActive, onTaped: setSilentPaymentsScanning), - ) - ], - ), - ] ], ), ), diff --git a/lib/src/screens/dashboard/pages/cake_features_page.dart b/lib/src/screens/dashboard/pages/cake_features_page.dart new file mode 100644 index 0000000000..aa587a5f40 --- /dev/null +++ b/lib/src/screens/dashboard/pages/cake_features_page.dart @@ -0,0 +1,204 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart'; +import 'package:cake_wallet/src/widgets/standard_switch.dart'; +import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class CakeFeaturesPage extends StatelessWidget { + CakeFeaturesPage({ + required this.dashboardViewModel, + required this.cakeFeaturesViewModel, + }); + + final DashboardViewModel dashboardViewModel; + final CakeFeaturesViewModel cakeFeaturesViewModel; + final _scrollController = ScrollController(); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: RawScrollbar( + thumbColor: Colors.white.withOpacity(0.15), + radius: Radius.circular(20), + thumbVisibility: true, + thickness: 2, + controller: _scrollController, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 50), + Text( + 'Cake ${S.of(context).features}', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.pageTitleTextColor, + ), + ), + Expanded( + child: ListView( + controller: _scrollController, + children: [ + // SizedBox(height: 20), + // DashBoardRoundedCardWidget( + // onTap: () => launchUrl( + // Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"), + // mode: LaunchMode.externalApplication, + // ), + // title: S.of(context).cake_pay_title, + // subTitle: S.of(context).cake_pay_subtitle, + // ), + SizedBox(height: 20), + DashBoardRoundedCardWidget( + onTap: () => launchUrl( + Uri.https("buy.cakepay.com"), + mode: LaunchMode.externalApplication, + ), + title: S.of(context).cake_pay_web_cards_title, + subTitle: S.of(context).cake_pay_web_cards_subtitle, + svgPicture: SvgPicture.asset( + 'assets/images/cards.svg', + height: 125, + width: 125, + fit: BoxFit.cover, + ), + ), + if (dashboardViewModel.hasSilentPayments) ...[ + SizedBox(height: 10), + DashBoardRoundedCardWidget( + title: S.of(context).silent_payments, + subTitle: S.of(context).enable_silent_payments_scanning, + hint: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => launchUrl( + // TODO: Update URL + Uri.https("guides.cakewallet.com"), + mode: LaunchMode.externalApplication, + ), + child: Row( + children: [ + Text( + S.of(context).what_is_silent_payments, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context) + .extension()! + .labelTextColor, + height: 1, + ), + softWrap: true, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Icon(Icons.help_outline, + size: 16, + color: Theme.of(context) + .extension()! + .labelTextColor), + ) + ], + ), + ), + Observer( + builder: (_) => StandardSwitch( + value: dashboardViewModel.silentPaymentsScanningActive, + onTaped: () => _toggleSilentPaymentsScanning(context), + ), + ) + ], + ), + ], + ), + onTap: () => _toggleSilentPaymentsScanning(context), + icon: Icon( + Icons.lock, + color: + Theme.of(context).extension()!.pageTitleTextColor, + size: 50, + ), + ), + ] + ], + ), + ), + ], + ), + ), + ), + ); + } + + // TODO: Remove ionia flow/files if we will discard it + void _navigatorToGiftCardsPage(BuildContext context) { + final walletType = dashboardViewModel.type; + + switch (walletType) { + case WalletType.haven: + showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).error, + alertContent: S.of(context).gift_cards_unavailable, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); + break; + default: + cakeFeaturesViewModel.isIoniaUserAuthenticated().then((value) { + if (value) { + Navigator.pushNamed(context, Routes.ioniaManageCardsPage); + return; + } + Navigator.of(context).pushNamed(Routes.ioniaWelcomePage); + }); + } + } + + Future _toggleSilentPaymentsScanning(BuildContext context) async { + final isSilentPaymentsScanningActive = dashboardViewModel.silentPaymentsScanningActive; + final newValue = !isSilentPaymentsScanningActive; + + final needsToSwitch = bitcoin!.getNodeIsCakeElectrs(dashboardViewModel.wallet) == false; + + if (needsToSwitch) { + return showPopUp( + context: context, + builder: (BuildContext context) => AlertWithTwoActions( + alertTitle: S.of(context).change_current_node_title, + alertContent: S.of(context).confirm_silent_payments_switch_node, + rightButtonText: S.of(context).ok, + leftButtonText: S.of(context).cancel, + actionRightButton: () { + dashboardViewModel.setSilentPaymentsScanning(newValue); + Navigator.of(context).pop(); + }, + actionLeftButton: () => Navigator.of(context).pop(), + )); + } + + return dashboardViewModel.setSilentPaymentsScanning(newValue); + } +} diff --git a/lib/src/screens/dashboard/pages/market_place_page.dart b/lib/src/screens/dashboard/pages/market_place_page.dart deleted file mode 100644 index 1bdcb61b4f..0000000000 --- a/lib/src/screens/dashboard/pages/market_place_page.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; -import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:url_launcher/url_launcher.dart'; -import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; - -class MarketPlacePage extends StatelessWidget { - MarketPlacePage({ - required this.dashboardViewModel, - required this.marketPlaceViewModel, - }); - - final DashboardViewModel dashboardViewModel; - final MarketPlaceViewModel marketPlaceViewModel; - final _scrollController = ScrollController(); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: RawScrollbar( - thumbColor: Colors.white.withOpacity(0.15), - radius: Radius.circular(20), - thumbVisibility: true, - thickness: 2, - controller: _scrollController, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 50), - Text( - S.of(context).market_place, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.pageTitleTextColor, - ), - ), - Expanded( - child: ListView( - controller: _scrollController, - children: [ - // SizedBox(height: 20), - // DashBoardRoundedCardWidget( - // onTap: () => launchUrl( - // Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"), - // mode: LaunchMode.externalApplication, - // ), - // title: S.of(context).cake_pay_title, - // subTitle: S.of(context).cake_pay_subtitle, - // ), - SizedBox(height: 20), - DashBoardRoundedCardWidget( - onTap: () => launchUrl( - Uri.https("buy.cakepay.com"), - mode: LaunchMode.externalApplication, - ), - title: S.of(context).cake_pay_web_cards_title, - subTitle: S.of(context).cake_pay_web_cards_subtitle, - ), - ], - ), - ), - ], - ), - ), - ), - ); - } - - // TODO: Remove ionia flow/files if we will discard it - void _navigatorToGiftCardsPage(BuildContext context) { - final walletType = dashboardViewModel.type; - - switch (walletType) { - case WalletType.haven: - showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.of(context).error, - alertContent: S.of(context).gift_cards_unavailable, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); - break; - default: - marketPlaceViewModel.isIoniaUserAuthenticated().then((value) { - if (value) { - Navigator.pushNamed(context, Routes.ioniaManageCardsPage); - return; - } - Navigator.of(context).pushNamed(Routes.ioniaWelcomePage); - }); - } - } -} diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index da0f351378..b633a127ea 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -1,3 +1,6 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/view_model/rescan_view_model.dart'; @@ -35,6 +38,10 @@ class RescanPage extends BasePage { isLoading: _rescanViewModel.state == RescanWalletState.rescaning, text: S.of(context).rescan, onPressed: () async { + if (_rescanViewModel.isSilentPaymentsScan) { + return _toggleSilentPaymentsScanning(context); + } + await _rescanViewModel.rescanCurrentWallet( restoreHeight: _blockchainHeightWidgetKey.currentState!.height); Navigator.of(context).pop(); @@ -46,4 +53,29 @@ class RescanPage extends BasePage { ]), ); } + + Future _toggleSilentPaymentsScanning(BuildContext context) async { + final needsToSwitch = bitcoin!.getNodeIsCakeElectrs(_rescanViewModel.wallet) == false; + + if (needsToSwitch) { + return showPopUp( + context: context, + builder: (BuildContext context) => AlertWithTwoActions( + alertTitle: S.of(context).change_current_node_title, + alertContent: S.of(context).confirm_silent_payments_switch_node, + rightButtonText: S.of(context).ok, + leftButtonText: S.of(context).cancel, + actionRightButton: () async { + _rescanViewModel.rescanCurrentWallet( + restoreHeight: _blockchainHeightWidgetKey.currentState!.height); + Navigator.of(context).pop(); + }, + actionLeftButton: () => Navigator.of(context).pop(), + )); + } + + await _rescanViewModel.rescanCurrentWallet( + restoreHeight: _blockchainHeightWidgetKey.currentState!.height); + Navigator.of(context).pop(); + } } diff --git a/lib/src/widgets/dashboard_card_widget.dart b/lib/src/widgets/dashboard_card_widget.dart index b3f92123a4..eb865b63b4 100644 --- a/lib/src/widgets/dashboard_card_widget.dart +++ b/lib/src/widgets/dashboard_card_widget.dart @@ -2,19 +2,24 @@ import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:flutter_svg/flutter_svg.dart'; class DashBoardRoundedCardWidget extends StatelessWidget { - - DashBoardRoundedCardWidget({ required this.onTap, required this.title, required this.subTitle, + this.hint, + this.svgPicture, + this.icon, }); final VoidCallback onTap; final String title; final String subTitle; + final Widget? hint; + final SvgPicture? svgPicture; + final Icon? icon; @override Widget build(BuildContext context) { @@ -35,32 +40,52 @@ class DashBoardRoundedCardWidget extends StatelessWidget { color: Theme.of(context).extension()!.cardBorderColor, ), ), - child: - Column( + child: Column( + children: [ + Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - title, - style: TextStyle( - color: Theme.of(context).extension()!.cardTextColor, - fontSize: 24, - fontWeight: FontWeight.w900, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + color: + Theme.of(context).extension()!.cardTextColor, + fontSize: 24, + fontWeight: FontWeight.w900, + ), + softWrap: true, + ), + SizedBox(height: 5), + Text( + subTitle, + style: TextStyle( + color: Theme.of(context) + .extension()! + .cardTextColor, + fontWeight: FontWeight.w500, + fontFamily: 'Lato'), + softWrap: true, + ), + ], ), ), - SizedBox(height: 5), - Text( - subTitle, - style: TextStyle( - color: Theme.of(context).extension()!.cardTextColor, - fontWeight: FontWeight.w500, - fontFamily: 'Lato'), - ) + if (svgPicture != null) svgPicture!, + if (icon != null) icon! ], ), + if (hint != null) ...[ + SizedBox(height: 10), + hint!, + ] + ], + ), ), ], ), ); } } - diff --git a/lib/view_model/dashboard/cake_features_view_model.dart b/lib/view_model/dashboard/cake_features_view_model.dart new file mode 100644 index 0000000000..0a8fbc6408 --- /dev/null +++ b/lib/view_model/dashboard/cake_features_view_model.dart @@ -0,0 +1,16 @@ +import 'package:cake_wallet/ionia/ionia_service.dart'; +import 'package:mobx/mobx.dart'; + +part 'cake_features_view_model.g.dart'; + +class CakeFeaturesViewModel = CakeFeaturesViewModelBase with _$CakeFeaturesViewModel; + +abstract class CakeFeaturesViewModelBase with Store { + final IoniaService _ioniaService; + + CakeFeaturesViewModelBase(this._ioniaService); + + Future isIoniaUserAuthenticated() async { + return await _ioniaService.isLogined(); + } +} diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index b4cd026fec..c90e51cd19 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -318,7 +318,7 @@ abstract class DashboardViewModelBase with Store { silentPaymentsScanningActive = active; if (hasSilentPayments) { - bitcoin!.setScanningActive(wallet, active); + bitcoin!.setScanningActive(wallet, settingsStore, active); } } diff --git a/lib/view_model/dashboard/market_place_view_model.dart b/lib/view_model/dashboard/market_place_view_model.dart deleted file mode 100644 index 4700411277..0000000000 --- a/lib/view_model/dashboard/market_place_view_model.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:cake_wallet/ionia/ionia_service.dart'; -import 'package:mobx/mobx.dart'; - -part 'market_place_view_model.g.dart'; - -class MarketPlaceViewModel = MarketPlaceViewModelBase with _$MarketPlaceViewModel; - -abstract class MarketPlaceViewModelBase with Store { - final IoniaService _ioniaService; - - MarketPlaceViewModelBase(this._ioniaService); - - - Future isIoniaUserAuthenticated() async { - return await _ioniaService.isLogined(); - } -} \ No newline at end of file diff --git a/lib/view_model/rescan_view_model.dart b/lib/view_model/rescan_view_model.dart index ae7baf0083..4008ee9f1a 100644 --- a/lib/view_model/rescan_view_model.dart +++ b/lib/view_model/rescan_view_model.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; @@ -10,12 +11,14 @@ class RescanViewModel = RescanViewModelBase with _$RescanViewModel; enum RescanWalletState { rescaning, none } abstract class RescanViewModelBase with Store { - RescanViewModelBase(this._wallet) + RescanViewModelBase(this.wallet, this.settingsStore) : state = RescanWalletState.none, isButtonEnabled = false, doSingleScan = false; - final WalletBase _wallet; + final WalletBase wallet; + + final SettingsStore settingsStore; @observable RescanWalletState state; @@ -27,16 +30,16 @@ abstract class RescanViewModelBase with Store { bool doSingleScan; @computed - bool get isSilentPaymentsScan => _wallet.type == WalletType.bitcoin; + bool get isSilentPaymentsScan => wallet.type == WalletType.bitcoin; @action Future rescanCurrentWallet({required int restoreHeight}) async { state = RescanWalletState.rescaning; - if (_wallet.type != WalletType.bitcoin) { - _wallet.rescan(height: restoreHeight); - _wallet.transactionHistory.clear(); + if (wallet.type != WalletType.bitcoin) { + wallet.rescan(height: restoreHeight); + wallet.transactionHistory.clear(); } else { - bitcoin!.rescan(_wallet, height: restoreHeight, doSingleScan: doSingleScan); + bitcoin!.rescan(wallet, settingsStore, height: restoreHeight, doSingleScan: doSingleScan); } state = RescanWalletState.none; } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 44df63a3ab..03f91bf1cd 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "تأكيد خصم الرسوم", "confirm_fee_deduction_content": "هل توافق على خصم الرسوم من الإخراج؟", "confirm_sending": "تأكيد الإرسال", + "confirm_silent_payments_switch_node": "حاليا مطلوب لتبديل العقد لمسح المدفوعات الصامتة", "confirmations": "التأكيدات", "confirmed": "رصيد مؤكد", "confirmed_tx": "مؤكد", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "نقوم بإنشاء عناوين جديدة في كل مرة تستخدم فيها عنوانًا ، لكن العناوين السابقة تستمر في العمل", "email_address": "عنوان البريد الالكترونى", "enable_replace_by_fee": "تمكين الاستبدال", + "enable_silent_payments_scanning": "تمكين المسح الضوئي للمدفوعات الصامتة", "enabled": "ممكنة", "enter_amount": "أدخل المبلغ", "enter_backup_password": "أدخل كلمة المرور الاحتياطية هنا", @@ -275,6 +277,7 @@ "extracted_address_content": "سوف ترسل الأموال إلى\n${recipient_name}", "failed_authentication": "${state_error} فشل المصادقة.", "faq": "الأسئلة الشائعة", + "features": "سمات", "fetching": "جار الجلب", "fiat_api": "Fiat API", "fiat_balance": "الرصيد فيات", @@ -793,6 +796,7 @@ "warning": "تحذير", "welcome": "مرحبا بك في", "welcome_to_cakepay": "مرحبا بكم في Cake Pay!", + "what_is_silent_payments": "ما هي المدفوعات الصامتة؟", "widgets_address": "عنوان", "widgets_or": "أو", "widgets_restore_from_blockheight": "استعادة من ارتفاع البلوك", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 185e22e0b6..4125969951 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Потвърдете приспадането на таксите", "confirm_fee_deduction_content": "Съгласни ли сте да приспадате таксата от продукцията?", "confirm_sending": "Потвърждаване на изпращането", + "confirm_silent_payments_switch_node": "Понастоящем се изисква да превключвате възлите за сканиране на мълчаливи плащания", "confirmations": "потвърждения", "confirmed": "Потвърден баланс", "confirmed_tx": "Потвърдено", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Нови адреси се генерират всеки път, когато използвате този, но и предишните продължават да работят", "email_address": "Имейл адрес", "enable_replace_by_fee": "Активиране на замяна по забрана", + "enable_silent_payments_scanning": "Активирайте безшумните плащания за сканиране", "enabled": "Активирано", "enter_amount": "Въведете сума", "enter_backup_password": "Въведете парола за възстановяване", @@ -275,6 +277,7 @@ "extracted_address_content": "Ще изпратите средства на \n${recipient_name}", "failed_authentication": "Неуспешно удостоверяване. ${state_error}", "faq": "FAQ", + "features": "Характеристика", "fetching": "Обработване", "fiat_api": "Fiat API", "fiat_balance": "Фиат Баланс", @@ -793,6 +796,7 @@ "warning": "Внимание", "welcome": "Добре дошли в", "welcome_to_cakepay": "Добре дошли в Cake Pay!", + "what_is_silent_payments": "Какво са мълчаливи плащания?", "widgets_address": "Адрес", "widgets_or": "или", "widgets_restore_from_blockheight": "Възстановяване от blockheight", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 2146ae7251..c063691f1a 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Potvrďte odpočet poplatků", "confirm_fee_deduction_content": "Souhlasíte s odečtením poplatku z výstupu?", "confirm_sending": "Potvrdit odeslání", + "confirm_silent_payments_switch_node": "V současné době je nutné přepínat uzly pro skenování tichých plateb", "confirmations": "Potvrzení", "confirmed": "Potvrzený zůstatek", "confirmed_tx": "Potvrzeno", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Po každém použití je generována nová adresa, ale předchozí adresy také stále fungují", "email_address": "E-mailová adresa", "enable_replace_by_fee": "Povolit výměnu podle poplatku", + "enable_silent_payments_scanning": "Povolte skenování tichých plateb", "enabled": "Povoleno", "enter_amount": "Zadejte částku", "enter_backup_password": "Zde zadejte své heslo pro zálohy", @@ -275,6 +277,7 @@ "extracted_address_content": "Prostředky budete posílat na\n${recipient_name}", "failed_authentication": "Ověřování selhalo. ${state_error}", "faq": "FAQ", + "features": "Funkce", "fetching": "Načítá se", "fiat_api": "Fiat API", "fiat_balance": "Fiat Balance", @@ -793,6 +796,7 @@ "warning": "Varování", "welcome": "Vítejte v", "welcome_to_cakepay": "Vítejte v Cake Pay!", + "what_is_silent_payments": "Co jsou tiché platby?", "widgets_address": "Adresa", "widgets_or": "nebo", "widgets_restore_from_blockheight": "Obnovit z výšky bloku", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index f4b6cd0df5..4f29258d1d 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Gebührenabzug bestätigen", "confirm_fee_deduction_content": "Stimmen Sie zu, die Gebühr von der Ausgabe abzuziehen?", "confirm_sending": "Senden bestätigen", + "confirm_silent_payments_switch_node": "Derzeit ist es erforderlich, Knoten zu wechseln, um stille Zahlungen zu scannen", "confirmations": "Bestätigungen", "confirmed": "Bestätigter Saldo", "confirmed_tx": "Bestätigt", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Wir generieren jedes Mal neue Adressen, wenn Sie eine verwenden, aber vorherige Adressen funktionieren weiterhin", "email_address": "E-Mail-Adresse", "enable_replace_by_fee": "Aktivieren Sie Ersatz für Fee", + "enable_silent_payments_scanning": "Aktivieren Sie stille Zahlungen Scannen", "enabled": "Ermöglicht", "enter_amount": "Betrag eingeben", "enter_backup_password": "Sicherungskennwort hier eingeben", @@ -275,6 +277,7 @@ "extracted_address_content": "Sie senden Geld an\n${recipient_name}", "failed_authentication": "Authentifizierung fehlgeschlagen. ${state_error}", "faq": "Häufig gestellte Fragen", + "features": "Merkmale", "fetching": "Frage ab", "fiat_api": "Fiat API", "fiat_balance": "Fiat Balance", @@ -796,6 +799,7 @@ "warning": "Warnung", "welcome": "Willkommen bei", "welcome_to_cakepay": "Willkommen bei Cake Pay!", + "what_is_silent_payments": "Was sind stille Zahlungen?", "widgets_address": "Adresse", "widgets_or": "oder", "widgets_restore_from_blockheight": "Ab Blockhöhe wiederherstellen", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 73b2bde1cc..677e3e50f8 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Confirm Fee Deduction", "confirm_fee_deduction_content": "Do you agree to deduct the fee from the output?", "confirm_sending": "Confirm sending", + "confirm_silent_payments_switch_node": "Currently it is required to switch nodes to scan silent payments", "confirmations": "Confirmations", "confirmed": "Confirmed Balance", "confirmed_tx": "Confirmed", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "We generate new addresses each time you use one, but previous addresses continue to work", "email_address": "Email Address", "enable_replace_by_fee": "Enable Replace-By-Fee", + "enable_silent_payments_scanning": "Enable silent payments scanning", "enabled": "Enabled", "enter_amount": "Enter Amount", "enter_backup_password": "Enter backup password here", @@ -275,6 +277,7 @@ "extracted_address_content": "You will be sending funds to\n${recipient_name}", "failed_authentication": "Failed authentication. ${state_error}", "faq": "FAQ", + "features": "Features", "fetching": "Fetching", "fiat_api": "Fiat API", "fiat_balance": "Fiat Balance", @@ -793,6 +796,7 @@ "warning": "Warning", "welcome": "Welcome to", "welcome_to_cakepay": "Welcome to Cake Pay!", + "what_is_silent_payments": "What is silent payments?", "widgets_address": "Address", "widgets_or": "or", "widgets_restore_from_blockheight": "Restore from blockheight", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 03c4775689..3c47d2c40a 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Confirmar la deducción de la tarifa", "confirm_fee_deduction_content": "¿Acepta deducir la tarifa de la producción?", "confirm_sending": "Confirmar envío", + "confirm_silent_payments_switch_node": "Actualmente se requiere cambiar los nodos para escanear pagos silenciosos", "confirmations": "Confirmaciones", "confirmed": "Saldo confirmado", "confirmed_tx": "Confirmado", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Generamos nuevas direcciones cada vez que usa una, pero las direcciones anteriores siguen funcionando", "email_address": "Dirección de correo electrónico", "enable_replace_by_fee": "Habilitar reemplazar por tarea", + "enable_silent_payments_scanning": "Habilitar escaneo de pagos silenciosos", "enabled": "Activado", "enter_amount": "Ingrese la cantidad", "enter_backup_password": "Ingrese la contraseña de respaldo aquí", @@ -275,6 +277,7 @@ "extracted_address_content": "Enviará fondos a\n${recipient_name}", "failed_authentication": "Autenticación fallida. ${state_error}", "faq": "FAQ", + "features": "Características", "fetching": "Cargando", "fiat_api": "Fiat API", "fiat_balance": "Equilibrio Fiat", @@ -794,6 +797,7 @@ "warning": "Advertencia", "welcome": "Bienvenido", "welcome_to_cakepay": "¡Bienvenido a Cake Pay!", + "what_is_silent_payments": "¿Qué son los pagos silenciosos?", "widgets_address": "Dirección", "widgets_or": "o", "widgets_restore_from_blockheight": "Restaurar desde blockheight", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 583fdd489e..594ca09ab6 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Confirmer la déduction des frais", "confirm_fee_deduction_content": "Acceptez-vous de déduire les frais de la production?", "confirm_sending": "Confirmer l'envoi", + "confirm_silent_payments_switch_node": "Actuellement, il est nécessaire de changer de nœuds pour scanner les paiements silencieux", "confirmations": "Confirmations", "confirmed": "Solde confirmé", "confirmed_tx": "Confirmé", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Nous générons de nouvelles adresses à chaque fois que vous en utilisez une, mais les adresses précédentes continuent à fonctionner", "email_address": "Adresse e-mail", "enable_replace_by_fee": "Activer Remplace-by-Fee", + "enable_silent_payments_scanning": "Activer la numérisation des paiements silencieux", "enabled": "Activé", "enter_amount": "Entrez le montant", "enter_backup_password": "Entrez le mot de passe de sauvegarde ici", @@ -275,6 +277,7 @@ "extracted_address_content": "Vous allez envoyer des fonds à\n${recipient_name}", "failed_authentication": "Échec d'authentification. ${state_error}", "faq": "FAQ", + "features": "Caractéristiques", "fetching": "Récupération", "fiat_api": "Fiat API", "fiat_balance": "Solde fiat", @@ -793,6 +796,7 @@ "warning": "Avertissement", "welcome": "Bienvenue sur", "welcome_to_cakepay": "Bienvenue sur Cake Pay !", + "what_is_silent_payments": "Qu'est-ce que les paiements silencieux?", "widgets_address": "Adresse", "widgets_or": "ou", "widgets_restore_from_blockheight": "Restaurer depuis une hauteur de bloc", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 4c153ee02f..3ada6e5281 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Tabbatar da cire kudade", "confirm_fee_deduction_content": "Shin kun yarda ku cire kuɗin daga fitarwa?", "confirm_sending": "Tabbatar da aikawa", + "confirm_silent_payments_switch_node": "A halin yanzu ana buƙatar sauya nodes don bincika biyan siliki", "confirmations": "Tabbatar", "confirmed": "An tabbatar", "confirmed_tx": "Tabbatar", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Muna samar da sababbin adireshi duk lokacin da kuka yi amfani da ɗaya, amma adiresoshin da suka gabata suna ci gaba da aiki", "email_address": "Adireshin i-mel", "enable_replace_by_fee": "Ba da damar maye gurbin-by-kudin", + "enable_silent_payments_scanning": "Kunna biya biya", "enabled": "An kunna", "enter_amount": "Shigar da Adadi", "enter_backup_password": "Shigar da kalmar wucewa ta madadin nan", @@ -275,6 +277,7 @@ "extracted_address_content": "Za ku aika da kudade zuwa\n${recipient_name}", "failed_authentication": "Binne wajen shiga. ${state_error}", "faq": "FAQ", + "features": "Fasas", "fetching": "Daukewa", "fiat_api": "API ɗin Fiat", "fiat_balance": "Fiat Balance", @@ -795,6 +798,7 @@ "warning": "Gargadi", "welcome": "Barka da zuwa", "welcome_to_cakepay": "Barka da zuwa Cake Pay!", + "what_is_silent_payments": "Menene biyan shiru?", "widgets_address": "Adireshin", "widgets_or": "ko", "widgets_restore_from_blockheight": "Sake dawo da daga blockheight", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index bbf9749158..979aaeb257 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "शुल्क कटौती की पुष्टि करें", "confirm_fee_deduction_content": "क्या आप आउटपुट से शुल्क में कटौती करने के लिए सहमत हैं?", "confirm_sending": "भेजने की पुष्टि करें", + "confirm_silent_payments_switch_node": "वर्तमान में मूक भुगतान को स्कैन करने के लिए नोड्स को स्विच करना आवश्यक है", "confirmations": "पुष्टिकरण", "confirmed": "पुष्टि की गई शेष राशिी", "confirmed_tx": "की पुष्टि", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "हर बार जब आप एक का उपयोग करते हैं तो हम नए पते उत्पन्न करते हैं, लेकिन पिछले पते काम करना जारी रखते हैं", "email_address": "ईमेल पता", "enable_replace_by_fee": "प्रतिस्थापित-दर-शुल्क सक्षम करें", + "enable_silent_payments_scanning": "मूक भुगतान स्कैनिंग सक्षम करें", "enabled": "सक्रिय", "enter_amount": "राशि दर्ज करें", "enter_backup_password": "यहां बैकअप पासवर्ड डालें", @@ -275,6 +277,7 @@ "extracted_address_content": "आपको धनराशि भेजी जाएगी\n${recipient_name}", "failed_authentication": "प्रमाणीकरण विफल. ${state_error}", "faq": "FAQ", + "features": "विशेषताएँ", "fetching": "ला रहा है", "fiat_api": "फिएट पैसे API", "fiat_balance": "फिएट बैलेंस", @@ -795,6 +798,7 @@ "warning": "चेतावनी", "welcome": "स्वागत हे सेवा मेरे", "welcome_to_cakepay": "केकपे में आपका स्वागत है!", + "what_is_silent_payments": "मूक भुगतान क्या है?", "widgets_address": "पता", "widgets_or": "या", "widgets_restore_from_blockheight": "ब्लॉकचेन से पुनर्स्थापित करें", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 04ac5acd4e..5eca97a90c 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Potvrdite odbitak naknade", "confirm_fee_deduction_content": "Slažete li se da ćete odbiti naknadu od izlaza?", "confirm_sending": "Potvrdi slanje", + "confirm_silent_payments_switch_node": "Trenutno je potrebno prebaciti čvorove na skeniranje tihih plaćanja", "confirmations": "Potvrde", "confirmed": "Potvrđeno stanje", "confirmed_tx": "Potvrđen", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Minden egyes alkalommal új címeket generálunk, de a korábbi címek továbbra is működnek", "email_address": "Adresa e-pošte", "enable_replace_by_fee": "Omogući zamjenu", + "enable_silent_payments_scanning": "Omogući skeniranje tihih plaćanja", "enabled": "Omogućeno", "enter_amount": "Unesite iznos", "enter_backup_password": "Unesite svoju lozinku za sigurnosnu kopiju ovdje", @@ -275,6 +277,7 @@ "extracted_address_content": "Poslat ćete sredstva primatelju\n${recipient_name}", "failed_authentication": "Autentifikacija neuspješna. ${state_error}", "faq": "FAQ", + "features": "Značajke", "fetching": "Dohvaćanje", "fiat_api": "Fiat API", "fiat_balance": "Fiat Bilans", @@ -793,6 +796,7 @@ "warning": "Upozorenje", "welcome": "Dobrodošli na", "welcome_to_cakepay": "Dobro došli u Cake Pay!", + "what_is_silent_payments": "Što je tiha plaćanja?", "widgets_address": "Adresa", "widgets_or": "ili", "widgets_restore_from_blockheight": "Oporavi pomoću visine bloka", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index f45a66b5b5..ca823c3d5d 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Konfirmasi pengurangan biaya", "confirm_fee_deduction_content": "Apakah Anda setuju untuk mengurangi biaya dari output?", "confirm_sending": "Konfirmasi pengiriman", + "confirm_silent_payments_switch_node": "Saat ini diminta untuk mengganti node untuk memindai pembayaran diam", "confirmations": "Konfirmasi", "confirmed": "Saldo Terkonfirmasi", "confirmed_tx": "Dikonfirmasi", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Kami menghasilkan alamat baru setiap kali Anda menggunakan satu, tetapi alamat sebelumnya tetap berfungsi", "email_address": "Alamat Email", "enable_replace_by_fee": "Aktifkan ganti-by-fee", + "enable_silent_payments_scanning": "Aktifkan pemindaian pembayaran diam", "enabled": "Diaktifkan", "enter_amount": "Masukkan Jumlah", "enter_backup_password": "Masukkan kata sandi cadangan di sini", @@ -275,6 +277,7 @@ "extracted_address_content": "Anda akan mengirim dana ke\n${recipient_name}", "failed_authentication": "Otentikasi gagal. ${state_error}", "faq": "Pertanyaan yang Sering Diajukan", + "features": "Fitur", "fetching": "Mengambil", "fiat_api": "API fiat", "fiat_balance": "Saldo Fiat", @@ -796,6 +799,7 @@ "warning": "Peringatan", "welcome": "Selamat datang di", "welcome_to_cakepay": "Selamat Datang di Cake Pay!", + "what_is_silent_payments": "Apa itu pembayaran diam?", "widgets_address": "Alamat", "widgets_or": "atau", "widgets_restore_from_blockheight": "Pulihkan dari tinggi blok", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 133ef62d5b..3b306b5ee5 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Conferma la detrazione delle commissioni", "confirm_fee_deduction_content": "Accetti di detrarre la commissione dall'output?", "confirm_sending": "Conferma l'invio", + "confirm_silent_payments_switch_node": "Attualmente è necessario cambiare nodi per scansionare i pagamenti silenziosi", "confirmations": "Conferme", "confirmed": "Saldo confermato", "confirmed_tx": "Confermato", @@ -219,6 +220,7 @@ "electrum_address_disclaimer": "Generiamo nuovi indirizzi ogni volta che ne utilizzi uno, ma gli indirizzi precedenti continuano a funzionare", "email_address": "Indirizzo e-mail", "enable_replace_by_fee": "Abilita sostituzione per fee", + "enable_silent_payments_scanning": "Abilita la scansione dei pagamenti silenziosi", "enabled": "Abilitato", "enter_amount": "Inserisci importo", "enter_backup_password": "Inserisci la password di backup qui", @@ -276,6 +278,7 @@ "extracted_address_content": "Invierai i tuoi fondi a\n${recipient_name}", "failed_authentication": "Autenticazione fallita. ${state_error}", "faq": "Domande Frequenti", + "features": "Caratteristiche", "fetching": "Recupero", "fiat_api": "Fiat API", "fiat_balance": "Equilibrio fiat", @@ -796,6 +799,7 @@ "warning": "Avvertimento", "welcome": "Benvenuto", "welcome_to_cakepay": "Benvenuto in Cake Pay!", + "what_is_silent_payments": "Che cos'è i pagamenti silenziosi?", "widgets_address": "Indirizzo", "widgets_or": "o", "widgets_restore_from_blockheight": "Recupera da altezza blocco", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index a6191460be..d1e70cb5ad 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "料金控除を確認します", "confirm_fee_deduction_content": "出力から料金を差し引くことに同意しますか?", "confirm_sending": "送信を確認", + "confirm_silent_payments_switch_node": "現在、ノードを切り替えてサイレント決済をスキャンする必要があります", "confirmations": "確認", "confirmed": "確認済み残高", "confirmed_tx": "確認済み", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "使用するたびに新しいアドレスが生成されますが、以前のアドレスは引き続き機能します", "email_address": "メールアドレス", "enable_replace_by_fee": "交換ごとに有効にします", + "enable_silent_payments_scanning": "サイレントペイメントスキャンを有効にします", "enabled": "有効", "enter_amount": "金額を入力", "enter_backup_password": "ここにバックアップパスワードを入力してください", @@ -275,6 +277,7 @@ "extracted_address_content": "に送金します\n${recipient_name}", "failed_authentication": "認証失敗. ${state_error}", "faq": "FAQ", + "features": "特徴", "fetching": "フェッチング", "fiat_api": "不換紙幣 API", "fiat_balance": "フィアットバランス", @@ -794,6 +797,7 @@ "warning": "警告", "welcome": "ようこそ に", "welcome_to_cakepay": "Cake Payへようこそ!", + "what_is_silent_payments": "サイレント支払いとは何ですか?", "widgets_address": "住所", "widgets_or": "または", "widgets_restore_from_blockheight": "ブロックの高さから復元", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 9cd30a13f1..ccc164aa0a 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "수수료 공제를 확인하십시오", "confirm_fee_deduction_content": "출력에서 수수료를 공제하는 데 동의하십니까?", "confirm_sending": "전송 확인", + "confirm_silent_payments_switch_node": "현재 사일런트 결제를 스캔하려면 노드를 전환해야합니다.", "confirmations": "확인", "confirmed": "확인된 잔액", "confirmed_tx": "확인", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "사용할 때마다 새 주소가 생성되지만 이전 주소는 계속 작동합니다.", "email_address": "이메일 주소", "enable_replace_by_fee": "대체별로 활성화하십시오", + "enable_silent_payments_scanning": "무음 지불 스캔을 활성화합니다", "enabled": "사용", "enter_amount": "금액 입력", "enter_backup_password": "여기에 백업 비밀번호를 입력하세요.", @@ -275,6 +277,7 @@ "extracted_address_content": "당신은에 자금을 보낼 것입니다\n${recipient_name}", "failed_authentication": "인증 실패. ${state_error}", "faq": "FAQ", + "features": "특징", "fetching": "가져 오는 중", "fiat_api": "명목 화폐 API", "fiat_balance": "피아트 잔액", @@ -794,6 +797,7 @@ "warning": "경고", "welcome": "환영 에", "welcome_to_cakepay": "Cake Pay에 오신 것을 환영합니다!", + "what_is_silent_payments": "조용한 지불이란 무엇입니까?", "widgets_address": "주소", "widgets_or": "또는", "widgets_restore_from_blockheight": "블록 높이에서 복원", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index ee1fc90c7f..8ffeb01d00 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "အခကြေးငွေကိုနှုတ်ယူခြင်း", "confirm_fee_deduction_content": "output မှအခကြေးငွေကိုယူရန်သဘောတူပါသလား။", "confirm_sending": "ပေးပို့အတည်ပြုပါ။", + "confirm_silent_payments_switch_node": "လောလောဆယ်အသံတိတ်ငွေပေးချေမှုကိုစကင်ဖတ်စစ်ဆေးရန် node များကိုပြောင်းရန်လိုအပ်သည်", "confirmations": "အတည်ပြုချက်များ", "confirmed": "အတည်ပြုထားသော လက်ကျန်ငွေ", "confirmed_tx": "အတည်ပြုသည်", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "သင်အသုံးပြုသည့်အချိန်တိုင်းတွင် ကျွန်ုပ်တို့သည် လိပ်စာအသစ်များကို ထုတ်ပေးသော်လည်း ယခင်လိပ်စာများသည် ဆက်လက်အလုပ်လုပ်နေပါသည်။", "email_address": "အီးမေးလ်လိပ်စာ", "enable_replace_by_fee": "အစားထိုး - by- အခကြေးငွေ enable", + "enable_silent_payments_scanning": "အသံတိတ်ငွေပေးချေမှုကို scanable လုပ်ပါ", "enabled": "ဖွင့်ထားသည်။", "enter_amount": "ပမာဏကို ထည့်ပါ။", "enter_backup_password": "အရန်စကားဝှက်ကို ဤနေရာတွင် ထည့်ပါ။", @@ -275,6 +277,7 @@ "extracted_address_content": "သင်သည် \n${recipient_name} သို့ ရန်ပုံငွေများ ပေးပို့ပါမည်", "failed_authentication": "အထောက်အထားစိစစ်ခြင်း မအောင်မြင်ပါ။. ${state_error}", "faq": "အမြဲမေးလေ့ရှိသောမေးခွန်းများ", + "features": "အင်္ဂါရပ်များ", "fetching": "ခေါ်ယူခြင်း။", "fiat_api": "Fiat API", "fiat_balance": "Fiat Balance", @@ -793,6 +796,7 @@ "warning": "သတိပေးချက်", "welcome": "မှကြိုဆိုပါတယ်။", "welcome_to_cakepay": "Cake Pay မှကြိုဆိုပါသည်။", + "what_is_silent_payments": "အသံတိတ်ငွေပေးချေမှုဆိုတာဘာလဲ", "widgets_address": "လိပ်စာ", "widgets_or": "သို့မဟုတ်", "widgets_restore_from_blockheight": "အမြင့်မှ ပြန်လည်ရယူပါ။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index ae7a32dbb6..79ad955c2a 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Bevestig de aftrek van de kosten", "confirm_fee_deduction_content": "Stemt u ermee in om de vergoeding af te trekken van de output?", "confirm_sending": "Bevestig verzending", + "confirm_silent_payments_switch_node": "Momenteel is het vereist om knooppunten te schakelen om stille betalingen te scannen", "confirmations": "Bevestigingen", "confirmed": "Bevestigd saldo", "confirmed_tx": "Bevestigd", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "We genereren nieuwe adressen elke keer dat u er een gebruikt, maar eerdere adressen blijven werken", "email_address": "E-mailadres", "enable_replace_by_fee": "Schakel vervangen door een fee", + "enable_silent_payments_scanning": "Schakel stille betalingen in scannen in", "enabled": "Ingeschakeld", "enter_amount": "Voer Bedrag in", "enter_backup_password": "Voer hier een back-upwachtwoord in", @@ -275,6 +277,7 @@ "extracted_address_content": "U stuurt geld naar\n${recipient_name}", "failed_authentication": "Mislukte authenticatie. ${state_error}", "faq": "FAQ", + "features": "Functies", "fetching": "Ophalen", "fiat_api": "Fiat API", "fiat_balance": "Fiat Balans", @@ -794,6 +797,7 @@ "warning": "Waarschuwing", "welcome": "Welkom bij", "welcome_to_cakepay": "Welkom bij Cake Pay!", + "what_is_silent_payments": "Wat zijn stille betalingen?", "widgets_address": "Adres", "widgets_or": "of", "widgets_restore_from_blockheight": "Herstel vanaf blockheight", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 7e7cdfb758..0e21faf6d1 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Potwierdź odliczenie opłaty", "confirm_fee_deduction_content": "Czy zgadzasz się odliczyć opłatę od wyników?", "confirm_sending": "Potwierdź wysłanie", + "confirm_silent_payments_switch_node": "Obecnie wymagane jest zmiana węzłów w celu skanowania cichych płatności", "confirmations": "Potwierdzenia", "confirmed": "Potwierdzone saldo", "confirmed_tx": "Potwierdzony", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Za każdym razem, gdy wykorzystasz adres, dla wiekszej prywatności generujemy nowy, ale poprzednie adresy nadal działają, i moga odbierać środki", "email_address": "Adres e-mail", "enable_replace_by_fee": "Włącz wymianę po lewej", + "enable_silent_payments_scanning": "Włącz skanowanie cichych płatności", "enabled": "Włączone", "enter_amount": "Wprowadź kwotę", "enter_backup_password": "Wprowadź tutaj hasło kopii zapasowej", @@ -275,6 +277,7 @@ "extracted_address_content": "Wysyłasz środki na\n${recipient_name}", "failed_authentication": "Nieudane uwierzytelnienie. ${state_error}", "faq": "FAQ", + "features": "Cechy", "fetching": "Pobieranie", "fiat_api": "API Walut FIAT", "fiat_balance": "Bilans Fiata", @@ -793,6 +796,7 @@ "warning": "Ostrzeżenie", "welcome": "Witamy w", "welcome_to_cakepay": "Witamy w Cake Pay!", + "what_is_silent_payments": "Co to są ciche płatności?", "widgets_address": "Adres", "widgets_or": "lub", "widgets_restore_from_blockheight": "Przywróć z wysokości bloku", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 1526b6aad2..bef5bbf835 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Confirme dedução da taxa", "confirm_fee_deduction_content": "Você concorda em deduzir a taxa da saída?", "confirm_sending": "Confirmar o envio", + "confirm_silent_payments_switch_node": "Atualmente, é necessário trocar de nós para digitalizar pagamentos silenciosos", "confirmations": "Confirmações", "confirmed": "Saldo Confirmado", "confirmed_tx": "Confirmado", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Geramos novos endereços cada vez que você usa um, mas os endereços anteriores continuam funcionando", "email_address": "Endereço de e-mail", "enable_replace_by_fee": "Habilite substituir por taxa", + "enable_silent_payments_scanning": "Ativar escaneamento de pagamentos silenciosos", "enabled": "Habilitado", "enter_amount": "Digite o valor", "enter_backup_password": "Digite a senha de backup aqui", @@ -275,6 +277,7 @@ "extracted_address_content": "Você enviará fundos para\n${recipient_name}", "failed_authentication": "Falha na autenticação. ${state_error}", "faq": "FAQ", + "features": "Funcionalidades", "fetching": "Buscando", "fiat_api": "API da Fiat", "fiat_balance": "Equilíbrio Fiat", @@ -796,6 +799,7 @@ "warning": "Aviso", "welcome": "Bem-vindo ao", "welcome_to_cakepay": "Bem-vindo ao Cake Pay!", + "what_is_silent_payments": "O que são pagamentos silenciosos?", "widgets_address": "Endereço", "widgets_or": "ou", "widgets_restore_from_blockheight": "Restaurar a partir de altura do bloco", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 0534f52d77..d4c049f593 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Подтвердите вычет платы", "confirm_fee_deduction_content": "Согласны ли вы вычесть плату из вывода?", "confirm_sending": "Подтвердить отправку", + "confirm_silent_payments_switch_node": "В настоящее время требуется переключение узлов для сканирования молчаливых платежей", "confirmations": "Подтверждения", "confirmed": "Подтвержденный баланс", "confirmed_tx": "Подтвержденный", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Мы генерируем новые адреса каждый раз, когда вы их используете, но предыдущие адреса продолжают работать.", "email_address": "Адрес электронной почты", "enable_replace_by_fee": "Включить замену за пикой", + "enable_silent_payments_scanning": "Включить сканирование безмолвных платежей", "enabled": "Включено", "enter_amount": "Введите сумму", "enter_backup_password": "Введите пароль резервной копии", @@ -275,6 +277,7 @@ "extracted_address_content": "Вы будете отправлять средства\n${recipient_name}", "failed_authentication": "Ошибка аутентификации. ${state_error}", "faq": "FAQ", + "features": "Функции", "fetching": "Загрузка", "fiat_api": "Фиат API", "fiat_balance": "Фиатный баланс", @@ -794,6 +797,7 @@ "warning": "Предупреждение", "welcome": "Приветствуем в", "welcome_to_cakepay": "Добро пожаловать в Cake Pay!", + "what_is_silent_payments": "Что такое молчаливые платежи?", "widgets_address": "Адрес", "widgets_or": "или", "widgets_restore_from_blockheight": "Восстановить на высоте блока", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 434940a312..a74c3c9e5e 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "ยืนยันการหักค่าธรรมเนียม", "confirm_fee_deduction_content": "คุณตกลงที่จะหักค่าธรรมเนียมจากผลลัพธ์หรือไม่?", "confirm_sending": "ยืนยันการส่ง", + "confirm_silent_payments_switch_node": "ขณะนี้จำเป็นต้องเปลี่ยนโหนดเพื่อสแกนการชำระเงินแบบเงียบ", "confirmations": "การยืนยัน", "confirmed": "ยอดคงเหลือที่ยืนยันแล้ว", "confirmed_tx": "ซึ่งยืนยันแล้ว", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "เราสร้างที่อยู่ใหม่ทุกครั้งที่คุณใช้หนึ่งอย่าง แต่ที่อยู่เก่ายังสามารถใช้ได้ต่อไป", "email_address": "ที่อยู่อีเมล", "enable_replace_by_fee": "เปิดใช้งานการเปลี่ยนโดยค่าธรรมเนียม", + "enable_silent_payments_scanning": "เปิดใช้งานการสแกนการชำระเงินแบบเงียบ", "enabled": "เปิดใช้งาน", "enter_amount": "กรอกจำนวน", "enter_backup_password": "ป้อนรหัสผ่านสำรองที่นี่", @@ -275,6 +277,7 @@ "extracted_address_content": "คุณกำลังจะส่งเงินไปยัง\n${recipient_name}", "failed_authentication": "การยืนยันสิทธิ์ล้มเหลว ${state_error}", "faq": "คำถามที่พบบ่อย", + "features": "คุณสมบัติ", "fetching": "กำลังโหลด", "fiat_api": "API สกุลเงินตรา", "fiat_balance": "เฟียต บาลานซ์", @@ -793,6 +796,7 @@ "warning": "คำเตือน", "welcome": "ยินดีต้อนรับสู่", "welcome_to_cakepay": "ยินดีต้อนรับสู่ Cake Pay!", + "what_is_silent_payments": "การชำระเงินเงียบคืออะไร?", "widgets_address": "ที่อยู่", "widgets_or": "หรือ", "widgets_restore_from_blockheight": "กู้คืนจากระดับบล็อก", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 08be1b7277..23c1758cf0 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Kumpirmahin ang pagbabawas ng bayad", "confirm_fee_deduction_content": "Sumasang -ayon ka bang bawasan ang bayad mula sa output?", "confirm_sending": "Kumpirmahin ang pagpapadala", + "confirm_silent_payments_switch_node": "Sa kasalukuyan kinakailangan itong lumipat ng mga node upang i -scan ang mga tahimik na pagbabayad", "confirmations": "Mga kumpirmasyon", "confirmed": "Nakumpirma na balanse", "confirmed_tx": "Nakumpirma", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Bumubuo kami ng mga bagong address sa tuwing gumagamit ka ng isa, ngunit ang mga nakaraang address ay patuloy na gumagana", "email_address": "Email address", "enable_replace_by_fee": "Paganahin ang palitan-by-fee", + "enable_silent_payments_scanning": "Paganahin ang pag -scan ng tahimik na pagbabayad", "enabled": "Pinagana", "enter_amount": "Ipasok ang halaga", "enter_backup_password": "Ipasok ang backup password dito", @@ -275,6 +277,7 @@ "extracted_address_content": "Magpapadala ka ng pondo sa\n${recipient_name}", "failed_authentication": "Nabigong pagpapatunay. ${state_error}", "faq": "FAQ", + "features": "Mga tampok", "fetching": "Pagkuha", "fiat_api": "Fiat API", "fiat_balance": "Balanse ng fiat", @@ -793,6 +796,7 @@ "warning": "Babala", "welcome": "Maligayang pagdating sa", "welcome_to_cakepay": "Maligayang pagdating sa cake pay!", + "what_is_silent_payments": "Ano ang tahimik na pagbabayad?", "widgets_address": "Address", "widgets_or": "o", "widgets_restore_from_blockheight": "Ibalik mula sa blockheight", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 24f79222e5..c349c1e53f 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Ücret kesintisini onaylayın", "confirm_fee_deduction_content": "Ücreti çıktıdan düşürmeyi kabul ediyor musunuz?", "confirm_sending": "Göndermeyi onayla", + "confirm_silent_payments_switch_node": "Şu anda sessiz ödemeleri taramak için düğümleri değiştirmek gerekiyor", "confirmations": "Onay", "confirmed": "Onaylanmış Bakiye", "confirmed_tx": "Onaylanmış", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Adresini her kullandığında yeni adres oluşturuyoruz, ancak önceki adresler de çalışmaya devam eder", "email_address": "E-posta Adresi", "enable_replace_by_fee": "Farklı Değiştir'i Etkinleştir", + "enable_silent_payments_scanning": "Sessiz ödeme taramasını etkinleştirin", "enabled": "Etkin", "enter_amount": "Miktar Girin", "enter_backup_password": "Yedekleme parolasını buraya gir", @@ -275,6 +277,7 @@ "extracted_address_content": "Parayı buraya gönderceksin:\n${recipient_name}", "failed_authentication": "Doğrulama başarısız oldu. ${state_error}", "faq": "SSS", + "features": "Özellikler", "fetching": "Getiriliyor", "fiat_api": "İtibari Para API", "fiat_balance": "Fiat Bakiyesi", @@ -793,6 +796,7 @@ "warning": "Uyarı", "welcome": "Hoş Geldiniz", "welcome_to_cakepay": "Cake Pay'e Hoş Geldiniz!", + "what_is_silent_payments": "Sessiz ödemeler nedir?", "widgets_address": "Adres", "widgets_or": "veya", "widgets_restore_from_blockheight": "Blok yüksekliğinden geri yükle", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index f3643a64b8..4bc71e0e77 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Підтвердьте відрахування комісії", "confirm_fee_deduction_content": "Чи погоджуєтесь ви вирахувати комісію з сумми одержувача?", "confirm_sending": "Підтвердити відправлення", + "confirm_silent_payments_switch_node": "В даний час потрібно перемикати вузли на сканування мовчазних платежів", "confirmations": "Підтвердження", "confirmed": "Підтверджений баланс", "confirmed_tx": "Підтверджений", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "Ми створюємо нові адреси щоразу, коли ви використовуєте їх, але попередні адреси продовжують працювати", "email_address": "Адреса електронної пошти", "enable_replace_by_fee": "Увімкнути заміну з комісією", + "enable_silent_payments_scanning": "Увімкнути мовчазні платежі сканування", "enabled": "Увімкнено", "enter_amount": "Введіть суму", "enter_backup_password": "Введіть пароль резервної копії", @@ -275,6 +277,7 @@ "extracted_address_content": "Ви будете відправляти кошти\n${recipient_name}", "failed_authentication": "Помилка аутентифікації. ${state_error}", "faq": "FAQ", + "features": "Особливості", "fetching": "Завантаження", "fiat_api": "Фіат API", "fiat_balance": "Фіат Баланс", @@ -794,6 +797,7 @@ "warning": "УВАГА", "welcome": "Вітаємо в", "welcome_to_cakepay": "Ласкаво просимо до Cake Pay!", + "what_is_silent_payments": "Що таке мовчазні платежі?", "widgets_address": "Адреса", "widgets_or": "або", "widgets_restore_from_blockheight": "Відновити на висоті блоку", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index b39e211ef5..89ebb2a63f 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "فیس میں کٹوتی کی تصدیق کریں", "confirm_fee_deduction_content": "کیا آپ آؤٹ پٹ سے فیس کم کرنے پر راضی ہیں؟", "confirm_sending": "بھیجنے کی تصدیق کریں۔", + "confirm_silent_payments_switch_node": "فی الحال خاموش ادائیگیوں کو اسکین کرنے کے لئے نوڈس کو تبدیل کرنے کی ضرورت ہے", "confirmations": "تصدیقات", "confirmed": "تصدیق شدہ بیلنس", "confirmed_tx": "تصدیق", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "جب بھی آپ ایک کا استعمال کرتے ہیں تو ہم نئے پتے تیار کرتے ہیں، لیکن پچھلے پتے کام کرتے رہتے ہیں۔", "email_address": "ای میل اڈریس", "enable_replace_by_fee": "فی فیس کو تبدیل کریں", + "enable_silent_payments_scanning": "خاموش ادائیگیوں کو اسکیننگ کے قابل بنائیں", "enabled": "فعال", "enter_amount": "رقم درج کریں۔", "enter_backup_password": "یہاں بیک اپ پاس ورڈ درج کریں۔", @@ -275,6 +277,7 @@ "extracted_address_content": "آپ فنڈز بھیج رہے ہوں گے\n${recipient_name}", "failed_authentication": "ناکام تصدیق۔ ${state_error}", "faq": "عمومی سوالات", + "features": "خصوصیات", "fetching": "لا رہا ہے۔", "fiat_api": "Fiat API", "fiat_balance": "فیاٹ بیلنس", @@ -795,6 +798,7 @@ "warning": "وارننگ", "welcome": "میں خوش آمدید", "welcome_to_cakepay": "Cake پے میں خوش آمدید!", + "what_is_silent_payments": "خاموش ادائیگی کیا ہے؟", "widgets_address": "پتہ", "widgets_or": "یا", "widgets_restore_from_blockheight": "بلاک ہائیٹ سے بحال کریں۔", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index ea9caf6370..352cb40aad 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "Jẹrisi iyọkuro owo", "confirm_fee_deduction_content": "Ṣe o gba lati yọkuro idiyele naa kuro ni iṣejade?", "confirm_sending": "Jẹ́rìí sí ránṣẹ́", + "confirm_silent_payments_switch_node": "Lọwọlọwọ o nilo lati yi awọn apa pada si awọn sisanwo ipalọlọ", "confirmations": "Àwọn ẹ̀rí", "confirmed": "A ti jẹ́rìí ẹ̀", "confirmed_tx": "Jẹrisi", @@ -219,6 +220,7 @@ "electrum_address_disclaimer": "A dá àwọn àdírẹ́sì títun ní gbogbo àwọn ìgbà t'ẹ́ lo ó kan ṣùgbọ́n ẹ lè tẹ̀síwájú lo àwọn àdírẹ́sì tẹ́lẹ̀tẹ́lẹ̀.", "email_address": "Àdírẹ́sì ímeèlì", "enable_replace_by_fee": "Mu ki o rọpo", + "enable_silent_payments_scanning": "Mu ki awọn sisanwo ipalọlọ", "enabled": "Wọ́n tíwọn ti tan", "enter_amount": "Tẹ̀ iye", "enter_backup_password": "Tẹ̀ ọ̀rọ̀ aṣínà ti ẹ̀dà ḿbí", @@ -276,6 +278,7 @@ "extracted_address_content": "Ẹ máa máa fi owó ránṣẹ́ sí\n${recipient_name}", "failed_authentication": "Ìfẹ̀rílàdí pipòfo. ${state_error}", "faq": "Àwọn ìbéèrè l'a máa ń bèèrè", + "features": "Awọn ẹya", "fetching": "ń wá", "fiat_api": "Ojú ètò áàpù owó tí ìjọba pàṣẹ wa lò", "fiat_balance": "Fiat Iwontunws.funfun", @@ -794,6 +797,7 @@ "warning": "Ikilo", "welcome": "Ẹ káàbọ sí", "welcome_to_cakepay": "Ẹ káàbọ̀ sí Cake Pay!", + "what_is_silent_payments": "Kini awọn sisanwo ipalọlọ?", "widgets_address": "Àdírẹ́sì", "widgets_or": "tàbí", "widgets_restore_from_blockheight": "Dá padà sípò láti gíga àkójọpọ̀", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 822dc0b166..794128bf00 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -139,6 +139,7 @@ "confirm_fee_deduction": "确认费用扣除", "confirm_fee_deduction_content": "您是否同意从产出中扣除费用?", "confirm_sending": "确认发送", + "confirm_silent_payments_switch_node": "目前需要切换节点来扫描无声付款", "confirmations": "确认", "confirmed": "确认余额", "confirmed_tx": "确认的", @@ -218,6 +219,7 @@ "electrum_address_disclaimer": "每次您使用一个地址时,我们都会生成新地址,但之前的地址仍然有效", "email_address": "电子邮件地址", "enable_replace_by_fee": "启用by-Fee替换", + "enable_silent_payments_scanning": "启用无声付款扫描", "enabled": "启用", "enter_amount": "输入金额", "enter_backup_password": "在此处输入備用密码", @@ -275,6 +277,7 @@ "extracted_address_content": "您将汇款至\n${recipient_name}", "failed_authentication": "身份验证失败. ${state_error}", "faq": "FAQ", + "features": "特征", "fetching": "正在获取", "fiat_api": "法币API", "fiat_balance": "法币余额", @@ -793,6 +796,7 @@ "warning": "警告", "welcome": "欢迎使用", "welcome_to_cakepay": "欢迎来到 Cake Pay!", + "what_is_silent_payments": "什么是无声付款?", "widgets_address": "地址", "widgets_or": "或者", "widgets_restore_from_blockheight": "从块高还原", diff --git a/tool/configure.dart b/tool/configure.dart index 9c5f4b5907..6eb8f0e6cd 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -72,6 +72,7 @@ import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/view_model/send/output.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:hive/hive.dart'; import 'package:bitcoin_base/bitcoin_base.dart';"""; const bitcoinCWHeaders = """ From fa5effd0ccf449ed0fb98429cff37b81c7963817 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 9 Apr 2024 19:27:26 -0300 Subject: [PATCH 27/75] feat: delete silent addresses --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 40 ++++++++++++++++--- lib/bitcoin/cw_bitcoin.dart | 5 +++ lib/src/screens/receive/receive_page.dart | 29 ++++++++------ .../screens/receive/widgets/address_cell.dart | 33 ++++++++++----- .../wallet_address_list_view_model.dart | 10 ++++- 5 files changed, 89 insertions(+), 28 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index c21bc6c902..4d11792431 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -386,12 +386,31 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @action void updateAddress(String address, String label) { - final addressRecord = - _addresses.firstWhere((addressRecord) => addressRecord.address == address); - addressRecord.setNewName(label); - final index = _addresses.indexOf(addressRecord); - _addresses.remove(addressRecord); - _addresses.insert(index, addressRecord); + BaseBitcoinAddressRecord? foundAddress; + _addresses.forEach((addressRecord) { + if (addressRecord.address == address) { + foundAddress = addressRecord; + } + }); + silentAddresses.forEach((addressRecord) { + if (addressRecord.address == address) { + foundAddress = addressRecord; + } + }); + + if (foundAddress != null) { + foundAddress!.setNewName(label); + + if (foundAddress is BitcoinAddressRecord) { + final index = _addresses.indexOf(foundAddress); + _addresses.remove(foundAddress); + _addresses.insert(index, foundAddress as BitcoinAddressRecord); + } else { + final index = silentAddresses.indexOf(foundAddress as BitcoinSilentPaymentAddressRecord); + silentAddresses.remove(foundAddress); + silentAddresses.insert(index, foundAddress as BitcoinSilentPaymentAddressRecord); + } + } } @action @@ -537,4 +556,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type; bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => !addr.isHidden && !addr.isUsed && addr.type == type; + + @action + void deleteSilentPaymentAddress(String address) { + final addressRecord = silentAddresses.firstWhere((addressRecord) => + addressRecord.type == SilentPaymentsAddresType.p2sp && addressRecord.address == address); + + silentAddresses.remove(addressRecord); + updateAddressesByMatch(); + } } diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index db1ebcf4ab..51db7c827d 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -365,4 +365,9 @@ class CWBitcoin extends Bitcoin { return node?.uri.host == '198.58.111.154' && node?.uri.port == 50002; } + + void deleteSilentPaymentAddress(Object wallet, String address) { + final bitcoinWallet = wallet as ElectrumWallet; + bitcoinWallet.walletAddresses.deleteSilentPaymentAddress(address); + } } diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index 4d0c6185f1..e6009d0c21 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -206,18 +206,23 @@ class ReceivePage extends BasePage { .extension()! .tilesTextColor; - return AddressCell.fromItem(item, - isCurrent: isCurrent, - hasBalance: addressListViewModel.isElectrumWallet, - backgroundColor: backgroundColor, - textColor: textColor, - onTap: item.isOneTimeReceiveAddress == true - ? null - : (_) => addressListViewModel.setAddress(item), - onEdit: item.isOneTimeReceiveAddress == true - ? null - : () => Navigator.of(context) - .pushNamed(Routes.newSubaddress, arguments: item)); + return AddressCell.fromItem( + item, + isCurrent: isCurrent, + hasBalance: addressListViewModel.isElectrumWallet, + backgroundColor: backgroundColor, + textColor: textColor, + onTap: item.isOneTimeReceiveAddress == true + ? null + : (_) => addressListViewModel.setAddress(item), + onEdit: item.isOneTimeReceiveAddress == true || item.isPrimary + ? null + : () => Navigator.of(context) + .pushNamed(Routes.newSubaddress, arguments: item), + onDelete: !addressListViewModel.isSilentPayments || item.isPrimary + ? null + : () => addressListViewModel.deleteAddress(item), + ); }); } diff --git a/lib/src/screens/receive/widgets/address_cell.dart b/lib/src/screens/receive/widgets/address_cell.dart index 69e0119004..a7a19fb541 100644 --- a/lib/src/screens/receive/widgets/address_cell.dart +++ b/lib/src/screens/receive/widgets/address_cell.dart @@ -14,18 +14,22 @@ class AddressCell extends StatelessWidget { required this.textColor, this.onTap, this.onEdit, + this.onDelete, this.txCount, this.balance, this.isChange = false, this.hasBalance = false}); - factory AddressCell.fromItem(WalletAddressListItem item, - {required bool isCurrent, - required Color backgroundColor, - required Color textColor, - Function(String)? onTap, - bool hasBalance = false, - Function()? onEdit}) => + factory AddressCell.fromItem( + WalletAddressListItem item, { + required bool isCurrent, + required Color backgroundColor, + required Color textColor, + Function(String)? onTap, + bool hasBalance = false, + Function()? onEdit, + Function()? onDelete, + }) => AddressCell( address: item.address, name: item.name ?? '', @@ -35,6 +39,7 @@ class AddressCell extends StatelessWidget { textColor: textColor, onTap: onTap, onEdit: onEdit, + onDelete: onDelete, txCount: item.txCount, balance: item.balance, isChange: item.isChange, @@ -48,6 +53,7 @@ class AddressCell extends StatelessWidget { final Color textColor; final Function(String)? onTap; final Function()? onEdit; + final Function()? onDelete; final int? txCount; final String? balance; final bool isChange; @@ -63,7 +69,8 @@ class AddressCell extends StatelessWidget { } else { return formatIfCashAddr.substring(0, addressPreviewLength) + '...' + - formatIfCashAddr.substring(formatIfCashAddr.length - addressPreviewLength, formatIfCashAddr.length); + formatIfCashAddr.substring( + formatIfCashAddr.length - addressPreviewLength, formatIfCashAddr.length); } } @@ -175,7 +182,7 @@ class AddressCell extends StatelessWidget { ActionPane _actionPane(BuildContext context) => ActionPane( motion: const ScrollMotion(), - extentRatio: 0.3, + extentRatio: onDelete != null ? 0.4 : 0.3, children: [ SlidableAction( onPressed: (_) => onEdit?.call(), @@ -184,6 +191,14 @@ class AddressCell extends StatelessWidget { icon: Icons.edit, label: S.of(context).edit, ), + if (onDelete != null) + SlidableAction( + onPressed: (_) => onDelete!.call(), + backgroundColor: Colors.red, + foregroundColor: Colors.white, + icon: Icons.delete, + label: S.of(context).delete, + ), ], ); } diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index 9289701fc9..1eae60c993 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -320,7 +320,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo if (isElectrumWallet) { if (bitcoin!.hasSelectedSilentPayments(wallet)) { final addressItems = bitcoin!.getSilentPaymentAddresses(wallet).map((address) { - final isPrimary = address.index == 0; + // Silent Payments index 0 is change per BIP + final isPrimary = address.index == 1; return WalletAddressListItem( id: address.index, @@ -497,4 +498,11 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo amount = ''; } } + + @action + void deleteAddress(ListItem item) { + if (wallet.type == WalletType.bitcoin && item is WalletAddressListItem) { + bitcoin!.deleteSilentPaymentAddress(wallet, item.address); + } + } } From 0777db81c7ab16b588f679d5f5a1551879528487 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 9 Apr 2024 19:38:39 -0300 Subject: [PATCH 28/75] fix: red dot in non ssl nodes --- cw_core/lib/node.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index 585bc3c38b..7e182623bb 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -232,8 +232,12 @@ class Node extends HiveObject with Keyable { Future requestElectrumServer() async { try { - await SecureSocket.connect(uri.host, uri.port, - timeout: Duration(seconds: 5), onBadCertificate: (_) => true); + if (useSSL == true) { + await SecureSocket.connect(uri.host, uri.port, + timeout: Duration(seconds: 5), onBadCertificate: (_) => true); + } else { + await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5)); + } return true; } catch (_) { return false; From 65d6a890d025edf67e8153b4a1b3874dc8dfacda Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 9 Apr 2024 20:35:00 -0300 Subject: [PATCH 29/75] fix: inconsistent connection states, fix tx history --- cw_bitcoin/lib/electrum.dart | 7 +++-- cw_bitcoin/lib/electrum_transaction_info.dart | 8 ++++-- cw_bitcoin/lib/electrum_wallet.dart | 26 ++++++++++--------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 5f6363c755..6c532a7cef 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -64,7 +64,8 @@ class ElectrumClient { } catch (_) {} if (useSSL == true) { - socket = await SecureSocket.connect(host, port, timeout: connectionTimeout); + socket = await SecureSocket.connect(host, port, + timeout: connectionTimeout, onBadCertificate: (_) => true); } else { socket = await Socket.connect(host, port, timeout: connectionTimeout); } @@ -418,7 +419,9 @@ class ElectrumClient { Future close() async { _aliveTimer?.cancel(); - await socket?.close(); + try { + await socket?.close(); + } catch (_) {} onConnectionStatusChange = null; } diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index bd2ca731c0..a0390c64a7 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -158,6 +158,8 @@ class ElectrumTransactionInfo extends TransactionInfo { } factory ElectrumTransactionInfo.fromJson(Map data, WalletType type) { + final inputAddresses = data['inputAddresses'] as List; + final outputAddresses = data['outputAddresses'] as List; return ElectrumTransactionInfo( type, id: data['id'] as String, @@ -168,8 +170,10 @@ class ElectrumTransactionInfo extends TransactionInfo { date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int), isPending: data['isPending'] as bool, confirmations: data['confirmations'] as int, - inputAddresses: data['inputAddresses'] as List, - outputAddresses: data['outputAddresses'] as List, + inputAddresses: + inputAddresses.isEmpty ? [] : inputAddresses.map((e) => e.toString()).toList(), + outputAddresses: + outputAddresses.isEmpty ? [] : outputAddresses.map((e) => e.toString()).toList(), to: data['to'] as String?, unspents: data['unspents'] != null ? (data['unspents'] as List) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index a5a29b6a6f..5168d10551 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -322,31 +322,32 @@ abstract class ElectrumWalletBase Node? node; @action - Future _electrumConnect(Node node, {bool? attemptedReconnect}) async { + @override + Future connectToNode({required Node node}) async { this.node = node; try { syncStatus = ConnectingSyncStatus(); - await electrumClient.connectToUri(node.uri, useSSL: node.useSSL); + + if (!electrumClient.isConnected) { + await electrumClient.close(); + } + electrumClient.onConnectionStatusChange = (bool isConnected) async { - if (!isConnected) { + if (isConnected) { + syncStatus = ConnectedSyncStatus(); + } else if (isConnected == false) { syncStatus = LostConnectionSyncStatus(); - if (attemptedReconnect == false) { - await _electrumConnect(node, attemptedReconnect: true); - } } }; - syncStatus = ConnectedSyncStatus(); + + await electrumClient.connectToUri(node.uri, useSSL: node.useSSL); } catch (e) { print(e.toString()); syncStatus = FailedSyncStatus(); } } - @action - @override - Future connectToNode({required Node node}) => _electrumConnect(node); - int get _dustAmount => 546; bool _isBelowDust(int amount) => amount <= _dustAmount && network != BitcoinNetwork.testnet; @@ -1420,6 +1421,7 @@ abstract class ElectrumWalletBase Future updateTransactions() async { try { if (_isTransactionUpdating) { + _isTransactionUpdating = false; return; } @@ -1713,7 +1715,7 @@ Future startRefresh(ScanData scanData) async { final electrumClient = await getElectrumConnection(); final scanningBlockCount = - scanData.isSingleScan ? 1 : (scanData.network == BitcoinNetwork.testnet ? 1 : 10); + scanData.isSingleScan ? 1 : (scanData.network == BitcoinNetwork.testnet ? 50 : 10); Map? tweaks; try { From 465c7efa73aebdbae623a41436ba872162dae337 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Wed, 10 Apr 2024 18:05:36 -0300 Subject: [PATCH 30/75] fix: tx & balance displays, cpfp sending --- cw_bitcoin/lib/electrum_wallet.dart | 70 +++++++++++---------- cw_core/lib/unspent_transaction_output.dart | 1 + 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 5168d10551..a0fc96633d 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -238,9 +238,15 @@ abstract class ElectrumWalletBase final existingTxInfo = transactionHistory.transactions[txid]; if (existingTxInfo != null) { + existingTxInfo.amount = tx.amount; + existingTxInfo.confirmations = tx.confirmations; + existingTxInfo.height = tx.height; + final newUnspents = tx.unspents! .where((unspent) => !(existingTxInfo.unspents?.any((element) => - element.hash.contains(unspent.hash) && element.vout == unspent.vout) ?? + element.hash.contains(unspent.hash) && + element.vout == unspent.vout && + element.value == unspent.value) ?? false)) .toList(); @@ -290,11 +296,13 @@ abstract class ElectrumWalletBase try { syncStatus = AttemptingSyncStatus(); - if (silentPaymentsScanningActive) { + if (hasSilentPaymentsScanning) { try { await _setInitialHeight(); } catch (_) {} + } + if (silentPaymentsScanningActive) { if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); } @@ -412,11 +420,14 @@ abstract class ElectrumWalletBase List inputPrivKeyInfos = []; List inputPubKeys = []; bool spendsSilentPayment = false; + bool spendsCPFP = false; for (int i = 0; i < unspentCoins.length; i++) { final utx = unspentCoins[i]; if (utx.isSending) { + spendsCPFP = utx.confirmations == 0; + allInputsAmount += utx.value; final address = addressTypeFromStr(utx.address, network); @@ -523,6 +534,7 @@ abstract class ElectrumWalletBase hasChange: false, memo: memo, spendsSilentPayment: spendsSilentPayment, + spendsCPFP: spendsCPFP, ); } @@ -540,12 +552,14 @@ abstract class ElectrumWalletBase List inputPubKeys = []; int allInputsAmount = 0; bool spendsSilentPayment = false; + bool spendsCPFP = false; int leftAmount = credentialsAmount; final sendingCoins = unspentCoins.where((utx) => utx.isSending).toList(); for (int i = 0; i < sendingCoins.length; i++) { final utx = sendingCoins[i]; + spendsCPFP = utx.confirmations == 0; allInputsAmount += utx.value; leftAmount = leftAmount - utx.value; @@ -725,6 +739,7 @@ abstract class ElectrumWalletBase isSendAll: false, memo: memo, spendsSilentPayment: spendsSilentPayment, + spendsCPFP: spendsCPFP, ); } @@ -802,7 +817,7 @@ abstract class ElectrumWalletBase network: network, memo: estimatedTx.memo, outputOrdering: BitcoinOrdering.none, - enableRBF: true, + enableRBF: !estimatedTx.spendsCPFP, ); } else { txb = BitcoinTransactionBuilder( @@ -812,7 +827,7 @@ abstract class ElectrumWalletBase network: network, memo: estimatedTx.memo, outputOrdering: BitcoinOrdering.none, - enableRBF: true, + enableRBF: !estimatedTx.spendsCPFP, ); } @@ -1021,6 +1036,7 @@ abstract class ElectrumWalletBase final tx = await fetchTransactionInfo( hash: coin.hash, height: 0, myAddresses: addressesSet); coin.isChange = tx?.direction == TransactionDirection.outgoing; + coin.confirmations = tx?.confirmations; updatedUnspentCoins.add(coin); } catch (_) {} })))); @@ -1425,6 +1441,12 @@ abstract class ElectrumWalletBase return; } + transactionHistory.transactions.values.forEach((tx) { + if (tx.unspents != null && currentChainTip != null) { + tx.confirmations = currentChainTip! - tx.height + 1; + } + }); + _isTransactionUpdating = true; await fetchTransactions(); walletAddresses.updateReceiveAddresses(); @@ -1526,22 +1548,6 @@ abstract class ElectrumWalletBase Future updateBalance() async { balance[currency] = await _fetchBalances(); - - if (hasSilentPaymentsScanning) { - // Update balance stored from scanned silent payment transactions - try { - transactionHistory.transactions.values.forEach((tx) { - if (tx.unspents != null) { - balance[currency]!.confirmed += tx.unspents! - .where( - (unspent) => unspent.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) - .map((e) => e.value) - .reduce((value, element) => value + element); - } - }); - } catch (_) {} - } - await save(); } @@ -1660,15 +1666,9 @@ Future startRefresh(ScanData scanData) async { return electrumClient; } - Future getNodeHeightOrUpdate(int baseHeight) async { - if (cachedBlockchainHeight < baseHeight || cachedBlockchainHeight == 0) { - final electrumClient = await getElectrumConnection(); - - cachedBlockchainHeight = - await electrumClient.getCurrentBlockChainTip() ?? cachedBlockchainHeight; - } - - return cachedBlockchainHeight; + Future getUpdatedNodeHeight() async { + final electrumClient = await getElectrumConnection(); + return await electrumClient.getCurrentBlockChainTip() ?? cachedBlockchainHeight; } var lastKnownBlockHeight = 0; @@ -1700,14 +1700,14 @@ Future startRefresh(ScanData scanData) async { if (scanData.isSingleScan) { syncingStatus = SyncingSyncStatus(1, 0); } else { - syncingStatus = - SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight); + syncingStatus = SyncingSyncStatus.fromHeightValues( + await getUpdatedNodeHeight(), initialSyncHeight, syncHeight); } scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus)); if (syncingStatus.blocksLeft <= 0 || (scanData.isSingleScan && scanData.height != syncHeight)) { - scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus())); + scanData.sendPort.send(SyncResponse(await getUpdatedNodeHeight(), SyncedSyncStatus())); return; } @@ -1806,12 +1806,12 @@ Future startRefresh(ScanData scanData) async { WalletType.bitcoin, id: tx.hash, height: syncHeight, - amount: 0, // will be added later via unspent + amount: amount!, fee: 0, direction: TransactionDirection.incoming, isPending: false, date: DateTime.now(), - confirmations: currentChainTip - syncHeight - 1, + confirmations: await getUpdatedNodeHeight() - int.parse(blockHeight) + 1, to: value.label != null ? SilentPaymentAddress( version: scanData.silentAddress.version, @@ -1857,6 +1857,7 @@ class EstimatedTxResult { required this.isSendAll, this.memo, required this.spendsSilentPayment, + required this.spendsCPFP, }); final List utxos; @@ -1867,6 +1868,7 @@ class EstimatedTxResult { final bool hasChange; final bool isSendAll; final String? memo; + final bool spendsCPFP; } BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) { diff --git a/cw_core/lib/unspent_transaction_output.dart b/cw_core/lib/unspent_transaction_output.dart index 01b26cdcc6..d225493e98 100644 --- a/cw_core/lib/unspent_transaction_output.dart +++ b/cw_core/lib/unspent_transaction_output.dart @@ -14,6 +14,7 @@ class Unspent { bool isChange; bool isSending; bool isFrozen; + int? confirmations; String note; bool get isP2wpkh => From a4f8cdfa99251961a7cfae63357cfd3a952ebff8 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 12 Apr 2024 15:28:01 -0300 Subject: [PATCH 31/75] feat: new rust lib --- cw_bitcoin/lib/electrum_wallet.dart | 155 ++++++++++-------- cw_bitcoin/lib/electrum_wallet_addresses.dart | 4 +- cw_bitcoin/pubspec.lock | 53 +++++- cw_bitcoin/pubspec.yaml | 4 + lib/bitcoin/cw_bitcoin.dart | 9 + pubspec_base.yaml | 2 +- tool/configure.dart | 8 +- 7 files changed, 162 insertions(+), 73 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index a0fc96633d..439a51a133 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -42,6 +42,7 @@ import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:rxdart/subjects.dart'; import 'package:http/http.dart' as http; +import 'package:sp_scanner/sp_scanner.dart'; part 'electrum_wallet.g.dart'; @@ -223,6 +224,10 @@ abstract class ElectrumWalletBase transactionHistoryIds: transactionHistory.transactions.keys.toList(), node: ScanNode(node!.uri, node!.useSSL), labels: walletAddresses.labels, + labelIndexes: walletAddresses.silentAddresses + .where((addr) => addr.type == SilentPaymentsAddresType.p2sp && addr.index >= 1) + .map((addr) => addr.index) + .toList(), isSingleScan: doSingleScan ?? false, )); @@ -238,6 +243,10 @@ abstract class ElectrumWalletBase final existingTxInfo = transactionHistory.transactions[txid]; if (existingTxInfo != null) { + final addressRecord = + walletAddresses.silentAddresses.firstWhere((addr) => addr.address == tx.to); + addressRecord.txCount += 1; + existingTxInfo.amount = tx.amount; existingTxInfo.confirmations = tx.confirmations; existingTxInfo.height = tx.height; @@ -1616,6 +1625,7 @@ class ScanData { final ElectrumClient electrumClient; final List transactionHistoryIds; final Map labels; + final List labelIndexes; final bool isSingleScan; ScanData({ @@ -1628,6 +1638,7 @@ class ScanData { required this.electrumClient, required this.transactionHistoryIds, required this.labels, + required this.labelIndexes, required this.isSingleScan, }); @@ -1642,6 +1653,7 @@ class ScanData { transactionHistoryIds: scanData.transactionHistoryIds, electrumClient: scanData.electrumClient, labels: scanData.labels, + labelIndexes: scanData.labelIndexes, isSingleScan: scanData.isSingleScan, ); } @@ -1714,8 +1726,9 @@ Future startRefresh(ScanData scanData) async { try { final electrumClient = await getElectrumConnection(); + // TODO: hardcoded values, if timed out decrease amount of blocks per request? final scanningBlockCount = - scanData.isSingleScan ? 1 : (scanData.network == BitcoinNetwork.testnet ? 50 : 10); + scanData.isSingleScan ? 1 : (scanData.network == BitcoinNetwork.testnet ? 25 : 10); Map? tweaks; try { @@ -1752,14 +1765,16 @@ Future startRefresh(ScanData scanData) async { final tweak = details["tweak"].toString(); try { - final spb = SilentPaymentBuilder(receiverTweak: tweak); - final addToWallet = spb.scanOutputs( - scanData.silentAddress.b_scan, - scanData.silentAddress.B_spend, - outputPubkeys.values - .map((o) => getScriptFromOutput(o[0].toString(), int.parse(o[1].toString()))) - .toList(), - precomputedLabels: scanData.labels, + final addToWallet = scanOutputs( + outputPubkeys.values.map((o) => o[0].toString()).toList(), + tweak, + Receiver( + scanData.silentAddress.b_scan.toHex(), + scanData.silentAddress.B_spend.toHex(), + scanData.network == BitcoinNetwork.testnet, + scanData.labelIndexes, + scanData.labelIndexes.length, + ), ); if (addToWallet.isEmpty) { @@ -1767,64 +1782,72 @@ Future startRefresh(ScanData scanData) async { continue; } - addToWallet.forEach((key, value) async { - final t_k = value.tweak; - - final addressRecord = BitcoinSilentPaymentAddressRecord( - value.output.address.toAddress(scanData.network), - index: 0, - isHidden: false, - isUsed: true, - network: scanData.network, - silentPaymentTweak: t_k, - type: SegwitAddresType.p2tr, - ); - - int? amount; - int? pos; - outputPubkeys.entries.firstWhere((k) { - final matches = k.value[0] == key; - if (matches) { - amount = int.parse(k.value[1].toString()); - pos = int.parse(k.key.toString()); - return true; - } - return false; + addToWallet.forEach((label, value) async { + (value as Map).forEach((output, tweak) async { + final t_k = tweak.toString(); + + final receivingOutputAddress = ECPublic.fromHex(output) + .toTaprootAddress(tweak: false) + .toAddress(scanData.network); + + final receivedAddressRecord = BitcoinSilentPaymentAddressRecord( + receivingOutputAddress, + index: 0, + isHidden: false, + isUsed: true, + network: scanData.network, + silentPaymentTweak: t_k, + type: SegwitAddresType.p2tr, + ); + + int? amount; + int? pos; + outputPubkeys.entries.firstWhere((k) { + final matches = k.value[0] == output; + if (matches) { + amount = int.parse(k.value[1].toString()); + pos = int.parse(k.key.toString()); + return true; + } + return false; + }); + + final json = { + 'address_record': receivedAddressRecord.toJSON(), + 'tx_hash': txid, + 'value': amount!, + 'tx_pos': pos!, + 'silent_payment_tweak': t_k, + }; + + final tx = BitcoinUnspent.fromJSON(receivedAddressRecord, json); + + final silentPaymentAddress = SilentPaymentAddress( + version: scanData.silentAddress.version, + B_scan: scanData.silentAddress.B_scan, + B_spend: label == "None" + ? scanData.silentAddress.B_spend + : scanData.silentAddress.B_spend + .tweakAdd(BigintUtils.fromBytes(BytesUtils.fromHexString(label))), + hrp: scanData.silentAddress.hrp, + ); + + final txInfo = ElectrumTransactionInfo( + WalletType.bitcoin, + id: tx.hash, + height: syncHeight, + amount: amount!, + fee: 0, + direction: TransactionDirection.incoming, + isPending: false, + date: DateTime.now(), + confirmations: await getUpdatedNodeHeight() - int.parse(blockHeight) + 1, + to: silentPaymentAddress.toString(), + unspents: [tx], + ); + + scanData.sendPort.send({txInfo.id: txInfo}); }); - - final json = { - 'address_record': addressRecord.toJSON(), - 'tx_hash': txid, - 'value': amount!, - 'tx_pos': pos!, - 'silent_payment_tweak': t_k, - }; - - final tx = BitcoinUnspent.fromJSON(addressRecord, json); - - final txInfo = ElectrumTransactionInfo( - WalletType.bitcoin, - id: tx.hash, - height: syncHeight, - amount: amount!, - fee: 0, - direction: TransactionDirection.incoming, - isPending: false, - date: DateTime.now(), - confirmations: await getUpdatedNodeHeight() - int.parse(blockHeight) + 1, - to: value.label != null - ? SilentPaymentAddress( - version: scanData.silentAddress.version, - B_scan: scanData.silentAddress.B_scan.tweakAdd( - BigintUtils.fromBytes(BytesUtils.fromHexString(value.tweak))), - B_spend: scanData.silentAddress.B_spend, - hrp: scanData.silentAddress.hrp, - ).toString() - : scanData.silentAddress.toString(), - unspents: [tx], - ); - - scanData.sendPort.send({txInfo.id: txInfo}); }); } catch (_) {} } diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 4d11792431..e9099ddeec 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -60,7 +60,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { if (silentAddresses.length == 0) silentAddresses.add(BitcoinSilentPaymentAddressRecord( silentAddress.toString(), - index: 1, + index: 0, isHidden: false, name: "", silentPaymentTweak: null, @@ -268,7 +268,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) { final currentSilentAddressIndex = silentAddresses .where((addressRecord) => addressRecord.type != SegwitAddresType.p2tr) - .length + + .length - 1; this.currentSilentAddressIndex = currentSilentAddressIndex; diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 0cf836a115..bd3dd41894 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -80,7 +80,7 @@ packages: description: path: "." ref: cake-update-v3 - resolved-ref: fb4c0a0b6cf24628ddad7d3cdc58e4c918eff714 + resolved-ref: "3ddad3d1a9b78f49c9ef542962758400315d64a7" url: "https://github.com/cake-tech/bitcoin_base" source: git version: "4.0.0" @@ -198,6 +198,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 + url: "https://pub.dev" + source: hosted + version: "0.4.1" clock: dependency: transitive description: @@ -285,6 +293,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + ffigen: + dependency: transitive + description: + name: ffigen + sha256: d3e76c2ad48a4e7f93a29a162006f00eba46ce7c08194a77bb5c5e97d1b5ff0a + url: "https://pub.dev" + source: hosted + version: "8.0.2" file: dependency: transitive description: @@ -607,6 +623,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.3" + quiver: + dependency: transitive + description: + name: quiver + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + url: "https://pub.dev" + source: hosted + version: "3.2.1" rxdart: dependency: "direct main" description: @@ -668,6 +692,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + sp_scanner: + dependency: "direct main" + description: + path: "." + ref: master + resolved-ref: de90b20f4250647d0f55f6bd5e7203710d0d5678 + url: "https://github.com/rafael-xmr/sp_scanner" + source: git + version: "0.0.1" stack_trace: dependency: transitive description: @@ -740,6 +773,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.0" + uuid: + dependency: transitive + description: + name: uuid + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" + source: hosted + version: "3.0.7" vector_math: dependency: transitive description: @@ -788,6 +829,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: c566f4f804215d84a7a2c377667f546c6033d5b34b4f9e60dfb09d17c4e97826 + url: "https://pub.dev" + source: hosted + version: "2.2.0" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.0.6 <4.0.0" flutter: ">=3.10.0" diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index bf749ba9bf..4d4f5f6390 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -38,6 +38,10 @@ dependencies: git: url: https://github.com/cake-tech/blockchain_utils ref: cake-update-v1 + sp_scanner: + git: + url: https://github.com/rafael-xmr/sp_scanner + ref: master dev_dependencies: flutter_test: diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 51db7c827d..40018d9693 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -297,6 +297,7 @@ class CWBitcoin extends Bitcoin { ); } + @override List getSilentPaymentAddresses(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; return bitcoinWallet.walletAddresses.silentAddresses @@ -304,6 +305,7 @@ class CWBitcoin extends Bitcoin { .toList(); } + @override List getSilentPaymentReceivedAddresses(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; return bitcoinWallet.walletAddresses.silentAddresses @@ -311,10 +313,12 @@ class CWBitcoin extends Bitcoin { .toList(); } + @override bool isBitcoinReceivePageOption(ReceivePageOption option) { return option is BitcoinReceivePageOption; } + @override BitcoinAddressType getOptionToType(ReceivePageOption option) { return (option as BitcoinReceivePageOption).toType(); } @@ -326,6 +330,7 @@ class CWBitcoin extends Bitcoin { return bitcoinWallet.silentPaymentsScanningActive; } + @override Future setScanningActive(Object wallet, SettingsStore settingsStore, bool active) async { final bitcoinWallet = wallet as ElectrumWallet; // TODO: always when setting to scanning active, will force switch nodes. Remove when not needed anymore @@ -338,6 +343,7 @@ class CWBitcoin extends Bitcoin { bitcoinWallet.setSilentPaymentsScanning(active); } + @override bool isTestnet(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; return bitcoinWallet.isTestnet ?? false; @@ -346,6 +352,7 @@ class CWBitcoin extends Bitcoin { @override int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date); + @override Future rescan(Object wallet, SettingsStore settingsStore, {required int height, bool? doSingleScan}) async { final bitcoinWallet = wallet as ElectrumWallet; @@ -359,6 +366,7 @@ class CWBitcoin extends Bitcoin { bitcoinWallet.rescan(height: height, doSingleScan: doSingleScan); } + @override bool getNodeIsCakeElectrs(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; final node = bitcoinWallet.node; @@ -366,6 +374,7 @@ class CWBitcoin extends Bitcoin { return node?.uri.host == '198.58.111.154' && node?.uri.port == 50002; } + @override void deleteSilentPaymentAddress(Object wallet, String address) { final bitcoinWallet = wallet as ElectrumWallet; bitcoinWallet.walletAddresses.deleteSilentPaymentAddress(address); diff --git a/pubspec_base.yaml b/pubspec_base.yaml index edc0f6f905..cd5e3cba0f 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -120,7 +120,7 @@ dev_dependencies: mobx_codegen: ^2.1.1 build_resolvers: ^2.0.9 hive_generator: ^1.1.3 - flutter_launcher_icons: ^0.11.0 + # flutter_launcher_icons: ^0.11.0 # check flutter_launcher_icons for usage pedantic: ^1.8.0 # replace https://github.com/dart-lang/lints#migrating-from-packagepedantic diff --git a/tool/configure.dart b/tool/configure.dart index 6eb8f0e6cd..a1a09c5bfd 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -89,6 +89,7 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_bitcoin/litecoin_wallet_service.dart'; import 'package:cw_core/get_height_by_date.dart'; +import 'package:cw_core/node.dart'; import 'package:mobx/mobx.dart'; """; const bitcoinCwPart = "part 'cw_bitcoin.dart';"; @@ -160,7 +161,7 @@ abstract class Bitcoin { BitcoinAddressType getOptionToType(ReceivePageOption option); bool hasTaprootInput(PendingTransaction pendingTransaction); bool getScanningActive(Object wallet); - void setScanningActive(Object wallet, bool active); + Future setScanningActive(Object wallet, SettingsStore settingsStore, bool active); bool isTestnet(Object wallet); Future replaceByFee(Object wallet, String transactionHash, String fee); @@ -169,7 +170,10 @@ abstract class Bitcoin { int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size}); int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size}); int getHeightByDate({required DateTime date}); - void rescan(Object wallet, {required int height, bool? doSingleScan}); + Future rescan(Object wallet, SettingsStore settingsStore, + {required int height, bool? doSingleScan}); + bool getNodeIsCakeElectrs(Object wallet); + void deleteSilentPaymentAddress(Object wallet, String address); } """; From 7f792fcd60679622f07ff6f42da3b180a79e12cf Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 12 Apr 2024 16:29:09 -0300 Subject: [PATCH 32/75] chore: node path --- lib/src/screens/nodes/widgets/node_form.dart | 26 +++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/src/screens/nodes/widgets/node_form.dart b/lib/src/screens/nodes/widgets/node_form.dart index e8c4b0ab34..c83b82d668 100644 --- a/lib/src/screens/nodes/widgets/node_form.dart +++ b/lib/src/screens/nodes/widgets/node_form.dart @@ -95,18 +95,20 @@ class NodeForm extends StatelessWidget { ) ], ), - SizedBox(height: 10.0), - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _pathController, - hintText: "/path", - validator: NodePathValidator(), - ), - ) - ], - ), + // if () ...[ + // SizedBox(height: 10.0), + // Row( + // children: [ + // Expanded( + // child: BaseTextFormField( + // controller: _pathController, + // hintText: "/path", + // validator: NodePathValidator(), + // ), + // ) + // ], + // ), + // ], SizedBox(height: 10.0), Row( children: [ From 2df4a17d5eb058b740677f680b26779377581335 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 12 Apr 2024 16:55:14 -0300 Subject: [PATCH 33/75] fix: check node based on network --- lib/bitcoin/cw_bitcoin.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 40018d9693..cf0cb783e4 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -371,7 +371,8 @@ class CWBitcoin extends Bitcoin { final bitcoinWallet = wallet as ElectrumWallet; final node = bitcoinWallet.node; - return node?.uri.host == '198.58.111.154' && node?.uri.port == 50002; + return node?.uri.host == '198.58.111.154' && + node?.uri.port == (wallet.network == BitcoinNetwork.testnet ? 50002 : 50001); } @override From 2b42b55bd016b43a03f689e097af5413f4a2fa45 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 12 Apr 2024 18:31:23 -0300 Subject: [PATCH 34/75] fix: missing txcount from addresses --- cw_bitcoin/lib/electrum_wallet.dart | 31 ++++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 439a51a133..d3aac7ca9f 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -150,15 +150,15 @@ abstract class ElectrumWalletBase bool silentPaymentsScanningActive = false; @action - void setSilentPaymentsScanning(bool active) { + Future setSilentPaymentsScanning(bool active) async { silentPaymentsScanningActive = active; if (active) { - _setInitialHeight().then((_) { - if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { - _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); - } - }); + await _setInitialHeight(); + + if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { + _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); + } } else { _isolate?.then((runningIsolate) => runningIsolate.kill(priority: Isolate.immediate)); @@ -166,9 +166,8 @@ abstract class ElectrumWalletBase syncStatus = SyncedSyncStatus(); } else { if (electrumClient.uri != null) { - electrumClient.connectToUri(electrumClient.uri!).then((_) { - startSync(); - }); + await electrumClient.connectToUri(electrumClient.uri!); + startSync(); } } } @@ -242,11 +241,10 @@ abstract class ElectrumWalletBase [unspent.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord])); final existingTxInfo = transactionHistory.transactions[txid]; - if (existingTxInfo != null) { - final addressRecord = - walletAddresses.silentAddresses.firstWhere((addr) => addr.address == tx.to); - addressRecord.txCount += 1; + final txAlreadyExisted = existingTxInfo != null; + // Updating tx after re-scanned + if (txAlreadyExisted) { existingTxInfo.amount = tx.amount; existingTxInfo.confirmations = tx.confirmations; existingTxInfo.height = tx.height; @@ -276,6 +274,10 @@ abstract class ElectrumWalletBase transactionHistory.addOne(existingTxInfo); } } else { + final addressRecord = + walletAddresses.silentAddresses.firstWhere((addr) => addr.address == tx.to); + addressRecord.txCount += 1; + transactionHistory.addMany(message); } @@ -1726,7 +1728,7 @@ Future startRefresh(ScanData scanData) async { try { final electrumClient = await getElectrumConnection(); - // TODO: hardcoded values, if timed out decrease amount of blocks per request? + // TODO: hardcoded values, if timed out decrease amount of blocks per request? final scanningBlockCount = scanData.isSingleScan ? 1 : (scanData.network == BitcoinNetwork.testnet ? 25 : 10); @@ -1798,6 +1800,7 @@ Future startRefresh(ScanData scanData) async { network: scanData.network, silentPaymentTweak: t_k, type: SegwitAddresType.p2tr, + txCount: 1, ); int? amount; From 021347ae87567cdef8d2e7f9abcd3a40a25d98be Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 12 Apr 2024 18:32:04 -0300 Subject: [PATCH 35/75] style: padding in feature page cards --- lib/src/widgets/dashboard_card_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/widgets/dashboard_card_widget.dart b/lib/src/widgets/dashboard_card_widget.dart index 87d0430400..b0831c7cf9 100644 --- a/lib/src/widgets/dashboard_card_widget.dart +++ b/lib/src/widgets/dashboard_card_widget.dart @@ -33,7 +33,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget { child: Stack( children: [ Container( - padding: EdgeInsets.fromLTRB(20, 20, 40, 20), + padding: EdgeInsets.all(20), width: double.infinity, decoration: BoxDecoration( color: Theme.of(context).extension()!.syncedBackgroundColor, From 23d62213022858a36812fdb6a459323ff0a889fc Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 12 Apr 2024 19:33:00 -0300 Subject: [PATCH 36/75] fix: restore not getting all wallet addresses by type --- cw_bitcoin/lib/electrum_wallet.dart | 66 +++++++++++++++++------------ 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index d3aac7ca9f..0ec535ab33 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1359,34 +1359,8 @@ abstract class ElectrumWalletBase final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet(); currentChainTip ??= await electrumClient.getCurrentBlockChainTip() ?? 0; - await Future.wait(ADDRESS_TYPES.map((type) { - final addressesByType = walletAddresses.allAddresses.where((addr) => addr.type == type); - - return Future.wait(addressesByType.map((addressRecord) async { - final history = await _fetchAddressHistory(addressRecord, addressesSet, currentChainTip!); - - if (history.isNotEmpty) { - addressRecord.txCount = history.length; - historiesWithDetails.addAll(history); - - final matchedAddresses = - addressesByType.where((addr) => addr.isHidden == addressRecord.isHidden); - - final isLastUsedAddress = - history.isNotEmpty && addressRecord.address == matchedAddresses.last.address; - - if (isLastUsedAddress) { - await walletAddresses.discoverAddresses( - matchedAddresses.toList(), - addressRecord.isHidden, - (address, addressesSet) => - _fetchAddressHistory(address, addressesSet, currentChainTip!) - .then((history) => history.isNotEmpty ? address.address : null), - type: type); - } - } - })); - })); + await Future.wait(ADDRESS_TYPES.map( + (type) => fetchTransactionsForAddressType(addressesSet, historiesWithDetails, type))); return historiesWithDetails; } catch (e) { @@ -1395,6 +1369,42 @@ abstract class ElectrumWalletBase } } + Future fetchTransactionsForAddressType( + Set addressesSet, + Map historiesWithDetails, + BitcoinAddressType type, + ) async { + final addressesByType = walletAddresses.allAddresses.where((addr) => addr.type == type); + final hiddenAddresses = addressesByType.where((addr) => addr.isHidden == true); + final receiveAddresses = addressesByType.where((addr) => addr.isHidden == false); + + await Future.wait(addressesByType.map((addressRecord) async { + final history = await _fetchAddressHistory(addressRecord, addressesSet, currentChainTip!); + + if (history.isNotEmpty) { + addressRecord.txCount = history.length; + historiesWithDetails.addAll(history); + + final matchedAddresses = addressRecord.isHidden ? hiddenAddresses : receiveAddresses; + final isLastUsedAddress = history.isNotEmpty && matchedAddresses.last == addressRecord; + + if (isLastUsedAddress) { + // The last address by gap limit is used, discover new addresses for the same address type + await walletAddresses.discoverAddresses( + matchedAddresses.toList(), + addressRecord.isHidden, + (address, addressesSet) => _fetchAddressHistory(address, addressesSet, currentChainTip!) + .then((history) => history.isNotEmpty ? address.address : null), + type: type, + ); + + // Continue until the last address by this address type is not used yet + await fetchTransactionsForAddressType(addressesSet, historiesWithDetails, type); + } + } + })); + } + Future> _fetchAddressHistory( BitcoinAddressRecord addressRecord, Set addressesSet, int currentHeight) async { try { From 615db5a6d40bd26106781aa342d65eb68a87bef4 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Fri, 12 Apr 2024 20:02:46 -0300 Subject: [PATCH 37/75] fix: auto switch node broken --- lib/bitcoin/cw_bitcoin.dart | 20 ++++++++++++------- .../dashboard/dashboard_view_model.dart | 2 +- lib/view_model/rescan_view_model.dart | 2 +- tool/configure.dart | 5 ++--- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index cf0cb783e4..8ee3e16879 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -331,15 +331,20 @@ class CWBitcoin extends Bitcoin { } @override - Future setScanningActive(Object wallet, SettingsStore settingsStore, bool active) async { + Future setScanningActive(Object wallet, bool active) async { final bitcoinWallet = wallet as ElectrumWallet; + // TODO: always when setting to scanning active, will force switch nodes. Remove when not needed anymore if (!getNodeIsCakeElectrs(wallet)) { - final node = Node(useSSL: false, uri: '198.58.111.154:50002'); + final node = Node( + useSSL: false, + uri: '198.58.111.154:${(wallet.network == BitcoinNetwork.testnet ? 50002 : 50001)}', + ); node.type = WalletType.bitcoin; - settingsStore.nodes[WalletType.bitcoin] = node; + await bitcoinWallet.connectToNode(node: node); } + bitcoinWallet.setSilentPaymentsScanning(active); } @@ -353,14 +358,15 @@ class CWBitcoin extends Bitcoin { int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date); @override - Future rescan(Object wallet, SettingsStore settingsStore, - {required int height, bool? doSingleScan}) async { + Future rescan(Object wallet, {required int height, bool? doSingleScan}) async { final bitcoinWallet = wallet as ElectrumWallet; // TODO: always when setting to scanning active, will force switch nodes. Remove when not needed anymore if (!getNodeIsCakeElectrs(wallet)) { - final node = Node(useSSL: false, uri: '198.58.111.154:50002'); + final node = Node( + useSSL: false, + uri: '198.58.111.154:${(wallet.network == BitcoinNetwork.testnet ? 50002 : 50001)}', + ); node.type = WalletType.bitcoin; - settingsStore.nodes[WalletType.bitcoin] = node; await bitcoinWallet.connectToNode(node: node); } bitcoinWallet.rescan(height: height, doSingleScan: doSingleScan); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 3e679c3810..f91086ef52 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -319,7 +319,7 @@ abstract class DashboardViewModelBase with Store { silentPaymentsScanningActive = active; if (hasSilentPayments) { - bitcoin!.setScanningActive(wallet, settingsStore, active); + bitcoin!.setScanningActive(wallet, active); } } diff --git a/lib/view_model/rescan_view_model.dart b/lib/view_model/rescan_view_model.dart index 4008ee9f1a..f78c51f681 100644 --- a/lib/view_model/rescan_view_model.dart +++ b/lib/view_model/rescan_view_model.dart @@ -39,7 +39,7 @@ abstract class RescanViewModelBase with Store { wallet.rescan(height: restoreHeight); wallet.transactionHistory.clear(); } else { - bitcoin!.rescan(wallet, settingsStore, height: restoreHeight, doSingleScan: doSingleScan); + bitcoin!.rescan(wallet, height: restoreHeight, doSingleScan: doSingleScan); } state = RescanWalletState.none; } diff --git a/tool/configure.dart b/tool/configure.dart index ae9ab0c355..70a74123c0 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -161,7 +161,7 @@ abstract class Bitcoin { BitcoinAddressType getOptionToType(ReceivePageOption option); bool hasTaprootInput(PendingTransaction pendingTransaction); bool getScanningActive(Object wallet); - Future setScanningActive(Object wallet, SettingsStore settingsStore, bool active); + Future setScanningActive(Object wallet, bool active); bool isTestnet(Object wallet); Future replaceByFee(Object wallet, String transactionHash, String fee); @@ -170,8 +170,7 @@ abstract class Bitcoin { int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size}); int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size}); int getHeightByDate({required DateTime date}); - Future rescan(Object wallet, SettingsStore settingsStore, - {required int height, bool? doSingleScan}); + Future rescan(Object wallet, {required int height, bool? doSingleScan}); bool getNodeIsCakeElectrs(Object wallet); void deleteSilentPaymentAddress(Object wallet, String address); } From a887ea74b5267111e89ab801f63cc6ec2dcaf9c4 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 15 Apr 2024 08:31:26 -0300 Subject: [PATCH 38/75] fix: silent payment txs not being restored --- cw_bitcoin/lib/electrum_transaction_info.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index a0390c64a7..d98d7a5b4c 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -158,8 +158,10 @@ class ElectrumTransactionInfo extends TransactionInfo { } factory ElectrumTransactionInfo.fromJson(Map data, WalletType type) { - final inputAddresses = data['inputAddresses'] as List; - final outputAddresses = data['outputAddresses'] as List; + final inputAddresses = data['inputAddresses'] as List? ?? []; + final outputAddresses = data['outputAddresses'] as List? ?? []; + final unspents = data['unspents'] as List? ?? []; + return ElectrumTransactionInfo( type, id: data['id'] as String, @@ -175,13 +177,11 @@ class ElectrumTransactionInfo extends TransactionInfo { outputAddresses: outputAddresses.isEmpty ? [] : outputAddresses.map((e) => e.toString()).toList(), to: data['to'] as String?, - unspents: data['unspents'] != null - ? (data['unspents'] as List) - .map((unspent) => BitcoinUnspent.fromJSON( - BitcoinSilentPaymentAddressRecord.fromJSON(unspent['address_record'].toString()), - unspent as Map)) - .toList() - : null, + unspents: unspents + .map((unspent) => BitcoinUnspent.fromJSON( + BitcoinSilentPaymentAddressRecord.fromJSON(unspent['address_record'].toString()), + unspent as Map)) + .toList(), ); } From e4156ba28270a4cf5657976f91a5048fe5b10245 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Wed, 17 Apr 2024 16:35:11 -0300 Subject: [PATCH 39/75] feat: change scanning to subscription model, sync improvements --- cw_bitcoin/lib/bitcoin_address_record.dart | 3 +- cw_bitcoin/lib/bitcoin_unspent.dart | 45 +- cw_bitcoin/lib/bitcoin_wallet.dart | 10 - cw_bitcoin/lib/electrum.dart | 38 +- cw_bitcoin/lib/electrum_balance.dart | 2 +- cw_bitcoin/lib/electrum_transaction_info.dart | 7 +- cw_bitcoin/lib/electrum_wallet.dart | 738 ++++++++++-------- cw_bitcoin/lib/electrum_wallet_addresses.dart | 17 +- cw_bitcoin/lib/exceptions.dart | 6 +- .../lib/pending_bitcoin_transaction.dart | 4 + cw_core/lib/exceptions.dart | 7 +- cw_core/lib/sync_status.dart | 23 +- cw_core/lib/unspent_coins_info.dart | 8 +- lib/bitcoin/cw_bitcoin.dart | 8 +- lib/core/sync_status_title.dart | 4 + lib/entities/default_settings_migration.dart | 12 +- .../screens/receive/widgets/address_cell.dart | 2 +- lib/src/screens/send/send_page.dart | 11 +- .../unspent_coins_list_page.dart | 1 + .../widgets/unspent_coins_list_item.dart | 28 +- lib/view_model/send/send_view_model.dart | 6 + .../unspent_coins/unspent_coins_item.dart | 6 +- .../unspent_coins_list_view_model.dart | 44 +- .../wallet_address_list_view_model.dart | 7 +- res/values/strings_ar.arb | 2 + res/values/strings_bg.arb | 2 + res/values/strings_cs.arb | 2 + res/values/strings_de.arb | 2 + res/values/strings_en.arb | 2 + res/values/strings_es.arb | 2 + res/values/strings_fr.arb | 2 + res/values/strings_ha.arb | 2 + res/values/strings_hi.arb | 2 + res/values/strings_hr.arb | 2 + res/values/strings_id.arb | 2 + res/values/strings_it.arb | 2 + res/values/strings_ja.arb | 2 + res/values/strings_ko.arb | 2 + res/values/strings_my.arb | 2 + res/values/strings_nl.arb | 2 + res/values/strings_pl.arb | 2 + res/values/strings_pt.arb | 4 +- res/values/strings_ru.arb | 2 + res/values/strings_th.arb | 2 + res/values/strings_tl.arb | 2 + res/values/strings_tr.arb | 2 + res/values/strings_uk.arb | 2 + res/values/strings_ur.arb | 2 + res/values/strings_yo.arb | 2 + res/values/strings_zh.arb | 2 + tool/configure.dart | 1 + 51 files changed, 691 insertions(+), 401 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index 220298e55a..bf36e6fb99 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -90,7 +90,8 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord { String? scriptHash; - String updateScriptHash(BasedUtxoNetwork network) { + String getScriptHash(BasedUtxoNetwork network) { + if (scriptHash != null) return scriptHash!; scriptHash = sh.scriptHash(address, network: network); return scriptHash!; } diff --git a/cw_bitcoin/lib/bitcoin_unspent.dart b/cw_bitcoin/lib/bitcoin_unspent.dart index b2c1d90c4c..3691a7a22a 100644 --- a/cw_bitcoin/lib/bitcoin_unspent.dart +++ b/cw_bitcoin/lib/bitcoin_unspent.dart @@ -2,20 +2,54 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_core/unspent_transaction_output.dart'; class BitcoinUnspent extends Unspent { - BitcoinUnspent(BaseBitcoinAddressRecord addressRecord, String hash, int value, int vout, - {this.silentPaymentTweak}) + BitcoinUnspent(BaseBitcoinAddressRecord addressRecord, String hash, int value, int vout) : bitcoinAddressRecord = addressRecord, super(addressRecord.address, hash, value, vout, null); - factory BitcoinUnspent.fromJSON(BaseBitcoinAddressRecord address, Map json) => + factory BitcoinUnspent.fromJSON(BaseBitcoinAddressRecord? address, Map json) => BitcoinUnspent( - address, + address ?? BitcoinAddressRecord.fromJSON(json['address_record'].toString()), + json['tx_hash'] as String, + json['value'] as int, + json['tx_pos'] as int, + ); + + Map toJson() { + final json = { + 'address_record': bitcoinAddressRecord.toJSON(), + 'tx_hash': hash, + 'value': value, + 'tx_pos': vout, + }; + return json; + } + + final BaseBitcoinAddressRecord bitcoinAddressRecord; +} + +class BitcoinSilentPaymentsUnspent extends BitcoinUnspent { + BitcoinSilentPaymentsUnspent( + BitcoinSilentPaymentAddressRecord addressRecord, + String hash, + int value, + int vout, { + required this.silentPaymentTweak, + required this.silentPaymentLabel, + }) : super(addressRecord, hash, value, vout); + + @override + factory BitcoinSilentPaymentsUnspent.fromJSON( + BitcoinSilentPaymentAddressRecord? address, Map json) => + BitcoinSilentPaymentsUnspent( + address ?? BitcoinSilentPaymentAddressRecord.fromJSON(json['address_record'].toString()), json['tx_hash'] as String, json['value'] as int, json['tx_pos'] as int, silentPaymentTweak: json['silent_payment_tweak'] as String?, + silentPaymentLabel: json['silent_payment_label'] as String?, ); + @override Map toJson() { final json = { 'address_record': bitcoinAddressRecord.toJSON(), @@ -23,10 +57,11 @@ class BitcoinUnspent extends Unspent { 'value': value, 'tx_pos': vout, 'silent_payment_tweak': silentPaymentTweak, + 'silent_payment_label': silentPaymentLabel, }; return json; } - final BaseBitcoinAddressRecord bitcoinAddressRecord; String? silentPaymentTweak; + String? silentPaymentLabel; } diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index e0218eff55..a319258177 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -64,19 +64,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { ), ); - hasSilentPaymentsScanning = true; - autorun((_) { this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; }); - - reaction((_) => walletAddresses.addressPageType, (BitcoinAddressType addressPageType) { - final prev = hasSilentPaymentsScanning; - hasSilentPaymentsScanning = addressPageType == SilentPaymentsAddresType.p2sp; - if (prev != hasSilentPaymentsScanning) { - startSync(); - } - }); } static Future create({ diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 6c532a7cef..e9383dda59 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -36,14 +36,15 @@ class ElectrumClient { _errors = {}, unterminatedString = ''; - static const connectionTimeout = Duration(seconds: 300); - static const aliveTimerDuration = Duration(seconds: 300); + static const connectionTimeout = Duration(seconds: 10); + static const aliveTimerDuration = Duration(seconds: 10); bool get isConnected => _isConnected; Socket? socket; void Function(bool)? onConnectionStatusChange; int _id; final Map _tasks; + Map get tasks => _tasks; final Map _errors; bool _isConnected; Timer? _aliveTimer; @@ -277,11 +278,18 @@ class ElectrumClient { Future> getHeader({required int height}) async => await call(method: 'blockchain.block.get_header', params: [height]) as Map; - Future> getTweaks({required int height, required int count}) async => - await callWithTimeout( - method: 'blockchain.block.tweaks', - params: [height, count], - timeout: 10000) as Map; + BehaviorSubject? tweaksSubscribe({required int height}) { + _id += 1; + return subscribe( + id: 'blockchain.tweaks.subscribe', + method: 'blockchain.tweaks.subscribe', + params: [height], + ); + } + + Future> getTweaks({required int height}) async => + await callWithTimeout(method: 'blockchain.tweaks.get', params: [height], timeout: 10000) + as Map; Future estimatefee({required int p}) => call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) { @@ -325,9 +333,6 @@ class ElectrumClient { }); Future> feeRates({BasedUtxoNetwork? network}) async { - if (network == BitcoinNetwork.testnet) { - return [1, 1, 1]; - } try { final topDoubleString = await estimatefee(p: 1); final middleDoubleString = await estimatefee(p: 5); @@ -349,7 +354,7 @@ class ElectrumClient { // "hex": "00000020890208a0ae3a3892aa047c5468725846577cfcd9b512b50000000000000000005dc2b02f2d297a9064ee103036c14d678f9afc7e3d9409cf53fd58b82e938e8ecbeca05a2d2103188ce804c4" // } Future getCurrentBlockChainTip() => - call(method: 'blockchain.headers.subscribe').then((result) { + callWithTimeout(method: 'blockchain.headers.subscribe').then((result) { if (result is Map) { return result["height"] as int; } @@ -357,6 +362,12 @@ class ElectrumClient { return null; }); + BehaviorSubject? chainTipSubscribe() { + _id += 1; + return subscribe( + id: 'blockchain.headers.subscribe', method: 'blockchain.headers.subscribe'); + } + BehaviorSubject? chainTipUpdate() { _id += 1; return subscribe( @@ -456,6 +467,11 @@ class ElectrumClient { _tasks[id]?.subject?.add(params.last); break; + case 'blockchain.tweaks.subscribe': + case 'blockchain.headers.subscribe': + final params = request['params'] as List; + _tasks[method]?.subject?.add(params.last); + break; default: break; } diff --git a/cw_bitcoin/lib/electrum_balance.dart b/cw_bitcoin/lib/electrum_balance.dart index 45de7de6d5..15d6843d87 100644 --- a/cw_bitcoin/lib/electrum_balance.dart +++ b/cw_bitcoin/lib/electrum_balance.dart @@ -23,7 +23,7 @@ class ElectrumBalance extends Balance { } int confirmed; - final int unconfirmed; + int unconfirmed; final int frozen; @override diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index d98d7a5b4c..d06cfe9de8 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -18,7 +18,7 @@ class ElectrumTransactionBundle { } class ElectrumTransactionInfo extends TransactionInfo { - List? unspents; + List? unspents; ElectrumTransactionInfo(this.type, {required String id, @@ -178,9 +178,8 @@ class ElectrumTransactionInfo extends TransactionInfo { outputAddresses.isEmpty ? [] : outputAddresses.map((e) => e.toString()).toList(), to: data['to'] as String?, unspents: unspents - .map((unspent) => BitcoinUnspent.fromJSON( - BitcoinSilentPaymentAddressRecord.fromJSON(unspent['address_record'].toString()), - unspent as Map)) + .map((unspent) => + BitcoinSilentPaymentsUnspent.fromJSON(null, unspent as Map)) .toList(), ); } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 0ec535ab33..b2962f1e0d 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -91,7 +91,13 @@ abstract class ElectrumWalletBase transactionHistory = ElectrumTransactionHistory(walletInfo: walletInfo, password: password); reaction((_) => syncStatus, (SyncStatus syncStatus) { - silentPaymentsScanningActive = syncStatus is SyncingSyncStatus; + if (syncStatus is! AttemptingSyncStatus) + silentPaymentsScanningActive = syncStatus is SyncingSyncStatus; + + if (syncStatus is NotConnectedSyncStatus) { + // Needs to re-subscribe to all scripthashes when reconnected + _scripthashesUpdateSubject = {}; + } }); } @@ -122,14 +128,7 @@ abstract class ElectrumWalletBase @observable SyncStatus syncStatus; - List get scriptHashes => walletAddresses.allAddresses - .map((addr) => scriptHash(addr.address, network: network)) - .toList(); - - List get publicScriptHashes => walletAddresses.allAddresses - .where((addr) => !addr.isHidden) - .map((addr) => scriptHash(addr.address, network: network)) - .toList(); + Set get addressesSet => walletAddresses.allAddresses.map((addr) => addr.address).toSet(); String get xpub => hd.base58!; @@ -142,8 +141,8 @@ abstract class ElectrumWalletBase @override bool? isTestnet; - @observable - bool hasSilentPaymentsScanning = false; + bool get hasSilentPaymentsScanning => type == WalletType.bitcoin; + @observable bool nodeSupportsSilentPayments = true; @observable @@ -151,13 +150,14 @@ abstract class ElectrumWalletBase @action Future setSilentPaymentsScanning(bool active) async { + syncStatus = AttemptingSyncStatus(); silentPaymentsScanningActive = active; if (active) { await _setInitialHeight(); - if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { - _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); + if (await currentChainTip > walletInfo.restoreHeight) { + _setListeners(walletInfo.restoreHeight, chainTipParam: _currentChainTip); } } else { _isolate?.then((runningIsolate) => runningIsolate.kill(priority: Isolate.immediate)); @@ -174,7 +174,11 @@ abstract class ElectrumWalletBase } @observable - int? currentChainTip; + int? _currentChainTip; + + @computed + Future get currentChainTip async => + _currentChainTip ?? await electrumClient.getCurrentBlockChainTip() ?? 0; @override BitcoinWalletKeys get keys => @@ -183,7 +187,9 @@ abstract class ElectrumWalletBase String _password; List unspentCoins; List _feeRates; + // ignore: prefer_final_fields Map?> _scripthashesUpdateSubject; + // ignore: prefer_final_fields BehaviorSubject? _chainTipUpdateSubject; bool _isTransactionUpdating; Future? _isolate; @@ -201,8 +207,8 @@ abstract class ElectrumWalletBase } @action - Future _setListeners(int height, {int? chainTip, bool? doSingleScan}) async { - final currentChainTip = chainTip ?? await electrumClient.getCurrentBlockChainTip() ?? 0; + Future _setListeners(int height, {int? chainTipParam, bool? doSingleScan}) async { + final chainTip = chainTipParam ?? await currentChainTip; syncStatus = AttemptingSyncStatus(); if (_isolate != null) { @@ -218,7 +224,7 @@ abstract class ElectrumWalletBase silentAddress: walletAddresses.silentAddress!, network: network, height: height, - chainTip: currentChainTip, + chainTip: chainTip, electrumClient: ElectrumClient(), transactionHistoryIds: transactionHistory.transactions.keys.toList(), node: ScanNode(node!.uri, node!.useSSL), @@ -237,12 +243,33 @@ abstract class ElectrumWalletBase final tx = map.value; if (tx.unspents != null) { - tx.unspents!.forEach((unspent) => walletAddresses.addSilentAddresses( - [unspent.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord])); - final existingTxInfo = transactionHistory.transactions[txid]; final txAlreadyExisted = existingTxInfo != null; + void updateSilentAddressRecord(BitcoinSilentPaymentsUnspent unspent) { + final silentAddress = walletAddresses.silentAddress!; + final silentPaymentAddress = SilentPaymentAddress( + version: silentAddress.version, + B_scan: silentAddress.B_scan, + B_spend: unspent.silentPaymentLabel != null + ? silentAddress.B_spend.tweakAdd( + BigintUtils.fromBytes( + BytesUtils.fromHexString(unspent.silentPaymentLabel!)), + ) + : silentAddress.B_spend, + hrp: silentAddress.hrp, + ); + + final addressRecord = walletAddresses.silentAddresses.firstWhereOrNull( + (address) => address.address == silentPaymentAddress.toString()); + addressRecord?.txCount += 1; + addressRecord?.balance += unspent.value; + + walletAddresses.addSilentAddresses( + [unspent.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord], + ); + } + // Updating tx after re-scanned if (txAlreadyExisted) { existingTxInfo.amount = tx.amount; @@ -258,37 +285,39 @@ abstract class ElectrumWalletBase .toList(); if (newUnspents.isNotEmpty) { + newUnspents.forEach(updateSilentAddressRecord); + existingTxInfo.unspents ??= []; existingTxInfo.unspents!.addAll(newUnspents); - final amount = newUnspents.length > 1 + final newAmount = newUnspents.length > 1 ? newUnspents.map((e) => e.value).reduce((value, unspent) => value + unspent) : newUnspents[0].value; if (existingTxInfo.direction == TransactionDirection.incoming) { - existingTxInfo.amount += amount; - } else { - existingTxInfo.amount = amount; - existingTxInfo.direction = TransactionDirection.incoming; + existingTxInfo.amount += newAmount; } + + // Updates existing TX transactionHistory.addOne(existingTxInfo); + // Update balance record + balance[currency]!.confirmed += newAmount; } } else { - final addressRecord = - walletAddresses.silentAddresses.firstWhere((addr) => addr.address == tx.to); - addressRecord.txCount += 1; + // else: First time seeing this TX after scanning + tx.unspents!.forEach(updateSilentAddressRecord); + // Add new TX record transactionHistory.addMany(message); + // Update balance record + balance[currency]!.confirmed += tx.amount; } - await transactionHistory.save(); - await updateUnspent(); - await updateBalance(); + await updateAllUnspents(); } } } - // check if is a SyncStatus type since "is SyncStatus" doesn't work here if (message is SyncResponse) { if (message.syncStatus is UnsupportedSyncStatus) { nodeSupportsSilentPayments = false; @@ -296,7 +325,6 @@ abstract class ElectrumWalletBase syncStatus = message.syncStatus; walletInfo.restoreHeight = message.height; - await walletInfo.save(); } } } @@ -305,32 +333,25 @@ abstract class ElectrumWalletBase @override Future startSync() async { try { - syncStatus = AttemptingSyncStatus(); + syncStatus = SyncronizingSyncStatus(); if (hasSilentPaymentsScanning) { - try { - await _setInitialHeight(); - } catch (_) {} + await _setInitialHeight(); } - if (silentPaymentsScanningActive) { - if ((currentChainTip ?? 0) > walletInfo.restoreHeight) { - _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip); - } + await _subscribeForUpdates(); + + int finished = 0; + void checkFinishedAllUpdates(void _) { + finished++; + if (finished == 2) syncStatus = SyncedSyncStatus(); } + // Always await first as it discovers new addresses in the process await updateTransactions(); - _subscribeForUpdates(); - await updateUnspent(); - await updateBalance(); - _feeRates = await electrumClient.feeRates(network: network); - - Timer.periodic( - const Duration(minutes: 1), (timer) async => _feeRates = await electrumClient.feeRates()); - if (!silentPaymentsScanningActive || walletInfo.restoreHeight == currentChainTip) { - syncStatus = SyncedSyncStatus(); - } + updateAllUnspents().then(checkFinishedAllUpdates); + updateBalance().then(checkFinishedAllUpdates); } catch (e, stacktrace) { print(stacktrace); print(e.toString()); @@ -338,29 +359,39 @@ abstract class ElectrumWalletBase } } + @action + Future updateFeeRates() async { + final feeRates = await electrumClient.feeRates(network: network); + if (feeRates != [0, 0, 0]) { + _feeRates = feeRates; + } + } + Node? node; @action @override Future connectToNode({required Node node}) async { + final differentNode = this.node?.uri != node.uri || this.node?.useSSL != node.useSSL; this.node = node; try { syncStatus = ConnectingSyncStatus(); - if (!electrumClient.isConnected) { - await electrumClient.close(); - } + electrumClient.onConnectionStatusChange = null; + await electrumClient.close(); - electrumClient.onConnectionStatusChange = (bool isConnected) async { - if (isConnected) { - syncStatus = ConnectedSyncStatus(); - } else if (isConnected == false) { - syncStatus = LostConnectionSyncStatus(); - } - }; + await Timer(Duration(seconds: differentNode ? 0 : 10), () async { + electrumClient.onConnectionStatusChange = (bool isConnected) async { + if (isConnected && syncStatus is! SyncedSyncStatus) { + syncStatus = ConnectedSyncStatus(); + } else if (!isConnected) { + syncStatus = LostConnectionSyncStatus(); + } + }; - await electrumClient.connectToUri(node.uri, useSSL: node.useSSL); + await electrumClient.connectToUri(node.uri, useSSL: node.useSSL); + }); } catch (e) { print(e.toString()); syncStatus = FailedSyncStatus(); @@ -436,8 +467,15 @@ abstract class ElectrumWalletBase for (int i = 0; i < unspentCoins.length; i++) { final utx = unspentCoins[i]; - if (utx.isSending) { - spendsCPFP = utx.confirmations == 0; + if (utx.isSending && !utx.isFrozen) { + if (hasSilentPayment) { + // Check inputs for shared secret derivation + if (utx.bitcoinAddressRecord.type == SegwitAddresType.p2wsh) { + throw BitcoinTransactionSilentPaymentsNotSupported(); + } + } + + if (!spendsCPFP) spendsCPFP = utx.confirmations == 0; allInputsAmount += utx.value; @@ -447,11 +485,10 @@ abstract class ElectrumWalletBase bool? isSilentPayment = false; if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { + final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord; privkey = walletAddresses.silentAddress!.b_spend.tweakAdd( BigintUtils.fromBytes( - BytesUtils.fromHexString( - (utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord).silentPaymentTweak!, - ), + BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!), ), ); spendsSilentPayment = true; @@ -464,7 +501,11 @@ abstract class ElectrumWalletBase ); } - inputPrivKeyInfos.add(ECPrivateInfo(privkey, address.type == SegwitAddresType.p2tr)); + inputPrivKeyInfos.add(ECPrivateInfo( + privkey, + address.type == SegwitAddresType.p2tr, + tweak: !isSilentPayment, + )); inputPubKeys.add(privkey.getPublic()); vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); @@ -520,6 +561,10 @@ abstract class ElectrumWalletBase // Here, when sending all, the output amount equals to the input value - fee to fully spend every input on the transaction and have no amount left for change int amount = allInputsAmount - fee; + if (amount <= 0) { + throw BitcoinTransactionWrongBalanceException(); + } + // Attempting to send less than the dust limit if (_isBelowDust(amount)) { throw BitcoinTransactionNoDustException(); @@ -580,11 +625,10 @@ abstract class ElectrumWalletBase bool? isSilentPayment = false; if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { + final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord; privkey = walletAddresses.silentAddress!.b_spend.tweakAdd( BigintUtils.fromBytes( - BytesUtils.fromHexString( - (utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord).silentPaymentTweak!, - ), + BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!), ), ); spendsSilentPayment = true; @@ -597,7 +641,11 @@ abstract class ElectrumWalletBase ); } - inputPrivKeyInfos.add(ECPrivateInfo(privkey, address.type == SegwitAddresType.p2tr)); + inputPrivKeyInfos.add(ECPrivateInfo( + privkey, + address.type == SegwitAddresType.p2tr, + tweak: !isSilentPayment, + )); inputPubKeys.add(privkey.getPublic()); vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); @@ -637,6 +685,16 @@ abstract class ElectrumWalletBase int amountLeftForChangeAndFee = allInputsAmount - credentialsAmount; if (amountLeftForChangeAndFee <= 0) { + if (!spendingAllCoins) { + return estimateTxForAmount( + credentialsAmount, + outputs, + feeRate, + inputsCount: utxos.length + 1, + memo: memo, + hasSilentPayment: hasSilentPayment, + ); + } throw BitcoinTransactionWrongBalanceException(); } @@ -684,6 +742,7 @@ abstract class ElectrumWalletBase // Still has inputs to spend before failing if (!spendingAllCoins) { + outputs.removeLast(); return estimateTxForAmount( credentialsAmount, outputs, @@ -730,6 +789,7 @@ abstract class ElectrumWalletBase outputs.removeLast(); } + outputs.removeLast(); return estimateTxForAmount( credentialsAmount, outputs, @@ -1025,7 +1085,8 @@ abstract class ElectrumWalletBase Future makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); - Future updateUnspent() async { + @action + Future updateAllUnspents() async { List updatedUnspentCoins = []; if (hasSilentPaymentsScanning) { @@ -1037,20 +1098,9 @@ abstract class ElectrumWalletBase }); } - final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet(); - - await Future.wait(walletAddresses.allAddresses.map((address) => electrumClient - .getListUnspentWithAddress(address.address, network) - .then((unspent) => Future.forEach>(unspent, (unspent) async { - try { - final coin = BitcoinUnspent.fromJSON(address, unspent); - final tx = await fetchTransactionInfo( - hash: coin.hash, height: 0, myAddresses: addressesSet); - coin.isChange = tx?.direction == TransactionDirection.outgoing; - coin.confirmations = tx?.confirmations; - updatedUnspentCoins.add(coin); - } catch (_) {} - })))); + await Future.wait(walletAddresses.allAddresses.map((address) async { + updatedUnspentCoins.addAll(await fetchUnspent(address)); + })); unspentCoins = updatedUnspentCoins; @@ -1072,6 +1122,7 @@ abstract class ElectrumWalletBase coin.isFrozen = coinInfo.isFrozen; coin.isSending = coinInfo.isSending; coin.note = coinInfo.note; + coin.bitcoinAddressRecord.balance += coinInfo.value; } else { _addCoinInfo(coin); } @@ -1081,6 +1132,55 @@ abstract class ElectrumWalletBase await _refreshUnspentCoinsInfo(); } + @action + Future updateUnspents(BitcoinAddressRecord address) async { + final newUnspentCoins = await fetchUnspent(address); + + if (newUnspentCoins.isNotEmpty) { + unspentCoins.addAll(newUnspentCoins); + + newUnspentCoins.forEach((coin) { + final coinInfoList = unspentCoinsInfo.values.where( + (element) => + element.walletId.contains(id) && + element.hash.contains(coin.hash) && + element.vout == coin.vout, + ); + + if (coinInfoList.isNotEmpty) { + final coinInfo = coinInfoList.first; + + coin.isFrozen = coinInfo.isFrozen; + coin.isSending = coinInfo.isSending; + coin.note = coinInfo.note; + coin.bitcoinAddressRecord.balance += coinInfo.value; + } else { + _addCoinInfo(coin); + } + }); + } + } + + @action + Future> fetchUnspent(BitcoinAddressRecord address) async { + final unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); + + List updatedUnspentCoins = []; + + await Future.wait(unspents.map((unspent) async { + try { + final coin = BitcoinUnspent.fromJSON(address, unspent); + final tx = await fetchTransactionInfo(hash: coin.hash, height: 0); + coin.isChange = address.isHidden; + coin.confirmations = tx?.confirmations; + + updatedUnspentCoins.add(coin); + } catch (_) {} + })); + + return updatedUnspentCoins; + } + @action Future _addCoinInfo(BitcoinUnspent coin) async { final newInfo = UnspentCoinsInfo( @@ -1093,6 +1193,7 @@ abstract class ElectrumWalletBase value: coin.value, vout: coin.vout, isChange: coin.isChange, + isSilentPayment: coin is BitcoinSilentPaymentsUnspent, ); await unspentCoinsInfo.add(newInfo); @@ -1309,8 +1410,8 @@ abstract class ElectrumWalletBase time = status["block_time"] as int?; final height = status["block_height"] as int? ?? 0; - confirmations = - height > 0 ? (await electrumClient.getCurrentBlockChainTip())! - height + 1 : 0; + final tip = await currentChainTip; + if (tip > 0) confirmations = height > 0 ? tip - height + 1 : 0; } else { final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash); @@ -1335,18 +1436,15 @@ abstract class ElectrumWalletBase } Future fetchTransactionInfo( - {required String hash, - required int height, - required Set myAddresses, - bool? retryOnFailure}) async { + {required String hash, required int height, bool? retryOnFailure}) async { try { return ElectrumTransactionInfo.fromElectrumBundle( await getTransactionExpanded(hash: hash), walletInfo.type, network, - addresses: myAddresses, height: height); + addresses: addressesSet, height: height); } catch (e) { if (e is FormatException && retryOnFailure == true) { await Future.delayed(const Duration(seconds: 2)); - return fetchTransactionInfo(hash: hash, height: height, myAddresses: myAddresses); + return fetchTransactionInfo(hash: hash, height: height); } return null; } @@ -1356,11 +1454,15 @@ abstract class ElectrumWalletBase Future> fetchTransactions() async { try { final Map historiesWithDetails = {}; - final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet(); - currentChainTip ??= await electrumClient.getCurrentBlockChainTip() ?? 0; - await Future.wait(ADDRESS_TYPES.map( - (type) => fetchTransactionsForAddressType(addressesSet, historiesWithDetails, type))); + if (type == WalletType.bitcoin) { + await Future.wait(ADDRESS_TYPES + .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); + } else if (type == WalletType.bitcoinCash) { + await fetchTransactionsForAddressType(historiesWithDetails, P2pkhAddressType.p2pkh); + } else if (type == WalletType.litecoin) { + await fetchTransactionsForAddressType(historiesWithDetails, SegwitAddresType.p2wpkh); + } return historiesWithDetails; } catch (e) { @@ -1370,7 +1472,6 @@ abstract class ElectrumWalletBase } Future fetchTransactionsForAddressType( - Set addressesSet, Map historiesWithDetails, BitcoinAddressType type, ) async { @@ -1379,39 +1480,50 @@ abstract class ElectrumWalletBase final receiveAddresses = addressesByType.where((addr) => addr.isHidden == false); await Future.wait(addressesByType.map((addressRecord) async { - final history = await _fetchAddressHistory(addressRecord, addressesSet, currentChainTip!); + final history = await _fetchAddressHistory(addressRecord, await currentChainTip); if (history.isNotEmpty) { addressRecord.txCount = history.length; historiesWithDetails.addAll(history); final matchedAddresses = addressRecord.isHidden ? hiddenAddresses : receiveAddresses; - final isLastUsedAddress = history.isNotEmpty && matchedAddresses.last == addressRecord; + final isUsedAddressUnderGap = matchedAddresses.toList().indexOf(addressRecord) >= + matchedAddresses.length - + (addressRecord.isHidden + ? ElectrumWalletAddressesBase.defaultChangeAddressesCount + : ElectrumWalletAddressesBase.defaultReceiveAddressesCount); - if (isLastUsedAddress) { - // The last address by gap limit is used, discover new addresses for the same address type + if (isUsedAddressUnderGap) { + final prevLength = walletAddresses.allAddresses.length; + + // Discover new addresses for the same address type until the gap limit is respected await walletAddresses.discoverAddresses( matchedAddresses.toList(), addressRecord.isHidden, - (address, addressesSet) => _fetchAddressHistory(address, addressesSet, currentChainTip!) - .then((history) => history.isNotEmpty ? address.address : null), + (address) async { + await _subscribeForUpdates(); + return _fetchAddressHistory(address, await currentChainTip) + .then((history) => history.isNotEmpty ? address.address : null); + }, type: type, ); - // Continue until the last address by this address type is not used yet - await fetchTransactionsForAddressType(addressesSet, historiesWithDetails, type); + final newLength = walletAddresses.allAddresses.length; + + if (newLength > prevLength) { + await fetchTransactionsForAddressType(historiesWithDetails, type); + } } } })); } Future> _fetchAddressHistory( - BitcoinAddressRecord addressRecord, Set addressesSet, int currentHeight) async { + BitcoinAddressRecord addressRecord, int? currentHeight) async { try { final Map historiesWithDetails = {}; - final history = await electrumClient - .getHistory(addressRecord.scriptHash ?? addressRecord.updateScriptHash(network)); + final history = await electrumClient.getHistory(addressRecord.getScriptHash(network)); if (history.isNotEmpty) { addressRecord.setAsUsed(); @@ -1425,14 +1537,13 @@ abstract class ElectrumWalletBase if (height > 0) { storedTx.height = height; // the tx's block itself is the first confirmation so add 1 - storedTx.confirmations = currentHeight - height + 1; + if (currentHeight != null) storedTx.confirmations = currentHeight - height + 1; storedTx.isPending = storedTx.confirmations == 0; } historiesWithDetails[txid] = storedTx; } else { - final tx = await fetchTransactionInfo( - hash: txid, height: height, myAddresses: addressesSet, retryOnFailure: true); + final tx = await fetchTransactionInfo(hash: txid, height: height, retryOnFailure: true); if (tx != null) { historiesWithDetails[txid] = tx; @@ -1462,9 +1573,9 @@ abstract class ElectrumWalletBase return; } - transactionHistory.transactions.values.forEach((tx) { - if (tx.unspents != null && currentChainTip != null) { - tx.confirmations = currentChainTip! - tx.height + 1; + transactionHistory.transactions.values.forEach((tx) async { + if (tx.unspents != null && tx.unspents!.isNotEmpty && tx.height > 0) { + tx.confirmations = await currentChainTip - tx.height + 1; } }); @@ -1479,15 +1590,24 @@ abstract class ElectrumWalletBase } } - void _subscribeForUpdates() async { - scriptHashes.forEach((sh) async { + Future _subscribeForUpdates() async { + final unsubscribedScriptHashes = walletAddresses.allAddresses.where( + (address) => !_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)), + ); + + await Future.wait(unsubscribedScriptHashes.map((address) async { + final sh = address.getScriptHash(network); await _scripthashesUpdateSubject[sh]?.close(); - _scripthashesUpdateSubject[sh] = electrumClient.scripthashUpdate(sh); + _scripthashesUpdateSubject[sh] = await electrumClient.scripthashUpdate(sh); _scripthashesUpdateSubject[sh]?.listen((event) async { try { - await updateUnspent(); - await updateBalance(); - await updateTransactions(); + await updateUnspents(address); + + final newBalance = await _fetchBalance(sh); + balance[currency]?.confirmed += newBalance.confirmed; + balance[currency]?.unconfirmed += newBalance.unconfirmed; + + await _fetchAddressHistory(address, await currentChainTip); } catch (e, s) { print(e.toString()); _onError?.call(FlutterErrorDetails( @@ -1497,24 +1617,7 @@ abstract class ElectrumWalletBase )); } }); - }); - - await _chainTipUpdateSubject?.close(); - _chainTipUpdateSubject = electrumClient.chainTipUpdate(); - _chainTipUpdateSubject?.listen((_) async { - try { - final currentHeight = await electrumClient.getCurrentBlockChainTip(); - if (currentHeight != null) walletInfo.restoreHeight = currentHeight; - _setListeners(walletInfo.restoreHeight, chainTip: currentHeight); - } catch (e, s) { - print(e.toString()); - _onError?.call(FlutterErrorDetails( - exception: e, - stack: s, - library: this.runtimeType.toString(), - )); - } - }); + })); } Future _fetchBalances() async { @@ -1534,17 +1637,15 @@ abstract class ElectrumWalletBase if (hasSilentPaymentsScanning) { // Add values from unspent coins that are not fetched by the address list // i.e. scanned silent payments - unspentCoinsInfo.values.forEach((info) { - unspentCoins.forEach((element) { - if (element.hash == info.hash && - element.bitcoinAddressRecord.address == info.address && - element.value == info.value) { - if (info.isFrozen) totalFrozen += element.value; - if (element.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { - totalConfirmed += element.value; + transactionHistory.transactions.values.forEach((tx) { + if (tx.unspents != null) { + tx.unspents!.forEach((unspent) { + if (unspent.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { + if (unspent.isFrozen) totalFrozen += unspent.value; + totalConfirmed += unspent.value; } - } - }); + }); + } }); } @@ -1567,6 +1668,13 @@ abstract class ElectrumWalletBase confirmed: totalConfirmed, unconfirmed: totalUnconfirmed, frozen: totalFrozen); } + Future _fetchBalance(String sh) async { + final balance = await electrumClient.getBalance(sh); + final confirmed = balance['confirmed'] as int? ?? 0; + final unconfirmed = balance['unconfirmed'] as int? ?? 0; + return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: 0); + } + Future updateBalance() async { balance[currency] = await _fetchBalances(); await save(); @@ -1597,10 +1705,19 @@ abstract class ElectrumWalletBase } Future _setInitialHeight() async { - currentChainTip = await electrumClient.getCurrentBlockChainTip(); - if (currentChainTip != null && walletInfo.restoreHeight == 0) { - walletInfo.restoreHeight = currentChainTip!; - } + if (_chainTipUpdateSubject != null) return; + + _chainTipUpdateSubject = await electrumClient.chainTipSubscribe(); + _chainTipUpdateSubject?.listen((e) async { + final event = e as Map; + final height = int.parse(event['height'].toString()); + + _currentChainTip = height; + + if (_currentChainTip != null && _currentChainTip! > 0 && walletInfo.restoreHeight == 0) { + walletInfo.restoreHeight = _currentChainTip!; + } + }); } static BasedUtxoNetwork _getNetwork(bitcoin.NetworkType networkType, CryptoCurrency? currency) { @@ -1679,8 +1796,6 @@ class SyncResponse { } Future startRefresh(ScanData scanData) async { - var cachedBlockchainHeight = scanData.chainTip; - Future getElectrumConnection() async { final electrumClient = scanData.electrumClient; if (!electrumClient.isConnected) { @@ -1690,11 +1805,6 @@ Future startRefresh(ScanData scanData) async { return electrumClient; } - Future getUpdatedNodeHeight() async { - final electrumClient = await getElectrumConnection(); - return await electrumClient.getCurrentBlockChainTip() ?? cachedBlockchainHeight; - } - var lastKnownBlockHeight = 0; var initialSyncHeight = 0; @@ -1714,172 +1824,182 @@ Future startRefresh(ScanData scanData) async { return; } - // Run this until no more blocks left to scan txs. At first this was recursive - // i.e. re-calling the startRefresh function but this was easier for the above values to retain - // their initial values - while (true) { - lastKnownBlockHeight = syncHeight; + BehaviorSubject? tweaksSubscription = null; - SyncingSyncStatus syncingStatus; - if (scanData.isSingleScan) { - syncingStatus = SyncingSyncStatus(1, 0); - } else { - syncingStatus = SyncingSyncStatus.fromHeightValues( - await getUpdatedNodeHeight(), initialSyncHeight, syncHeight); - } + lastKnownBlockHeight = syncHeight; - scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus)); + SyncingSyncStatus syncingStatus; + if (scanData.isSingleScan) { + syncingStatus = SyncingSyncStatus(1, 0); + } else { + syncingStatus = + SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, syncHeight); + } - if (syncingStatus.blocksLeft <= 0 || (scanData.isSingleScan && scanData.height != syncHeight)) { - scanData.sendPort.send(SyncResponse(await getUpdatedNodeHeight(), SyncedSyncStatus())); - return; - } + scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus)); - try { - final electrumClient = await getElectrumConnection(); + if (syncingStatus.blocksLeft <= 0 || (scanData.isSingleScan && scanData.height != syncHeight)) { + scanData.sendPort.send(SyncResponse(scanData.chainTip, SyncedSyncStatus())); + return; + } - // TODO: hardcoded values, if timed out decrease amount of blocks per request? - final scanningBlockCount = - scanData.isSingleScan ? 1 : (scanData.network == BitcoinNetwork.testnet ? 25 : 10); + try { + final electrumClient = await getElectrumConnection(); - Map? tweaks; + if (tweaksSubscription == null) { try { - tweaks = await electrumClient.getTweaks(height: syncHeight, count: scanningBlockCount); - } catch (e) { - if (e is RequestFailedTimeoutException) { - return scanData.sendPort.send( - SyncResponse(syncHeight, TimedOutSyncStatus()), - ); - } - } + tweaksSubscription = await electrumClient.tweaksSubscribe(height: syncHeight); + + tweaksSubscription?.listen((t) { + final tweaks = t as Map; + + if (tweaks.isEmpty) { + syncHeight += 1; + scanData.sendPort.send( + SyncResponse( + syncHeight, + SyncingSyncStatus.fromHeightValues( + currentChainTip, + initialSyncHeight, + syncHeight, + ), + ), + ); - if (tweaks == null) { - return scanData.sendPort.send( - SyncResponse(syncHeight, UnsupportedSyncStatus()), - ); - } + return; + } - if (tweaks.isEmpty) { - syncHeight += scanningBlockCount; - continue; - } + final blockHeight = tweaks.keys.first.toString(); - final blockHeights = tweaks.keys; - for (var i = 0; i < blockHeights.length; i++) { - try { - final blockHeight = blockHeights.elementAt(i).toString(); - final blockTweaks = tweaks[blockHeight] as Map; - - for (var j = 0; j < blockTweaks.keys.length; j++) { - final txid = blockTweaks.keys.elementAt(j); - final details = blockTweaks[txid] as Map; - final outputPubkeys = (details["output_pubkeys"] as Map); - final tweak = details["tweak"].toString(); - - try { - final addToWallet = scanOutputs( - outputPubkeys.values.map((o) => o[0].toString()).toList(), - tweak, - Receiver( - scanData.silentAddress.b_scan.toHex(), - scanData.silentAddress.B_spend.toHex(), - scanData.network == BitcoinNetwork.testnet, - scanData.labelIndexes, - scanData.labelIndexes.length, - ), - ); + try { + final blockTweaks = tweaks[blockHeight] as Map; - if (addToWallet.isEmpty) { - // no results tx, continue to next tx - continue; - } + for (var j = 0; j < blockTweaks.keys.length; j++) { + final txid = blockTweaks.keys.elementAt(j); + final details = blockTweaks[txid] as Map; + final outputPubkeys = (details["output_pubkeys"] as Map); + final tweak = details["tweak"].toString(); - addToWallet.forEach((label, value) async { - (value as Map).forEach((output, tweak) async { - final t_k = tweak.toString(); - - final receivingOutputAddress = ECPublic.fromHex(output) - .toTaprootAddress(tweak: false) - .toAddress(scanData.network); - - final receivedAddressRecord = BitcoinSilentPaymentAddressRecord( - receivingOutputAddress, - index: 0, - isHidden: false, - isUsed: true, - network: scanData.network, - silentPaymentTweak: t_k, - type: SegwitAddresType.p2tr, - txCount: 1, - ); - - int? amount; - int? pos; - outputPubkeys.entries.firstWhere((k) { - final matches = k.value[0] == output; - if (matches) { - amount = int.parse(k.value[1].toString()); - pos = int.parse(k.key.toString()); - return true; - } - return false; - }); + try { + // scanOutputs called from rust here + final addToWallet = scanOutputs( + outputPubkeys.values.map((o) => o[0].toString()).toList(), + tweak, + Receiver( + scanData.silentAddress.b_scan.toHex(), + scanData.silentAddress.B_spend.toHex(), + scanData.network == BitcoinNetwork.testnet, + scanData.labelIndexes, + scanData.labelIndexes.length, + ), + ); + + if (addToWallet.isEmpty) { + // no results tx, continue to next tx + continue; + } - final json = { - 'address_record': receivedAddressRecord.toJSON(), - 'tx_hash': txid, - 'value': amount!, - 'tx_pos': pos!, - 'silent_payment_tweak': t_k, - }; - - final tx = BitcoinUnspent.fromJSON(receivedAddressRecord, json); - - final silentPaymentAddress = SilentPaymentAddress( - version: scanData.silentAddress.version, - B_scan: scanData.silentAddress.B_scan, - B_spend: label == "None" - ? scanData.silentAddress.B_spend - : scanData.silentAddress.B_spend - .tweakAdd(BigintUtils.fromBytes(BytesUtils.fromHexString(label))), - hrp: scanData.silentAddress.hrp, - ); - - final txInfo = ElectrumTransactionInfo( - WalletType.bitcoin, - id: tx.hash, - height: syncHeight, - amount: amount!, - fee: 0, - direction: TransactionDirection.incoming, - isPending: false, - date: DateTime.now(), - confirmations: await getUpdatedNodeHeight() - int.parse(blockHeight) + 1, - to: silentPaymentAddress.toString(), - unspents: [tx], - ); - - scanData.sendPort.send({txInfo.id: txInfo}); + // placeholder ElectrumTransactionInfo object to update values based on new scanned unspent(s) + final txInfo = ElectrumTransactionInfo( + WalletType.bitcoin, + id: txid, + height: syncHeight, + amount: 0, + fee: 0, + direction: TransactionDirection.incoming, + isPending: false, + date: DateTime.now(), + confirmations: scanData.chainTip - int.parse(blockHeight) + 1, + unspents: [], + ); + + addToWallet.forEach((label, value) { + (value as Map).forEach((output, tweak) { + final t_k = tweak.toString(); + + final receivingOutputAddress = ECPublic.fromHex(output) + .toTaprootAddress(tweak: false) + .toAddress(scanData.network); + + final receivedAddressRecord = BitcoinSilentPaymentAddressRecord( + receivingOutputAddress, + index: 0, + isHidden: false, + isUsed: true, + network: scanData.network, + silentPaymentTweak: t_k, + type: SegwitAddresType.p2tr, + txCount: 1, + ); + + int? amount; + int? pos; + outputPubkeys.entries.firstWhere((k) { + final isMatchingOutput = k.value[0] == output; + if (isMatchingOutput) { + amount = int.parse(k.value[1].toString()); + pos = int.parse(k.key.toString()); + return true; + } + return false; + }); + + final unspent = BitcoinSilentPaymentsUnspent( + receivedAddressRecord, + txid, + amount!, + pos!, + silentPaymentTweak: t_k, + silentPaymentLabel: label == "None" ? null : label, + ); + + txInfo.unspents!.add(unspent); + txInfo.amount += unspent.value; + }); }); - }); - } catch (_) {} + + scanData.sendPort.send({txInfo.id: txInfo}); + } catch (_) {} + } + } catch (_) {} + + syncHeight += 1; + scanData.sendPort.send( + SyncResponse( + syncHeight, + SyncingSyncStatus.fromHeightValues( + currentChainTip, + initialSyncHeight, + syncHeight, + ), + ), + ); + + if (int.parse(blockHeight) >= scanData.chainTip) { + scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus())); } - } catch (e, s) { - print([e, s]); - } - // Finished scanning block, add 1 to height and continue to next block in loop - syncHeight += 1; - scanData.sendPort.send(SyncResponse(syncHeight, - SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight))); + return; + }); + } catch (e) { + if (e is RequestFailedTimeoutException) { + return scanData.sendPort.send( + SyncResponse(syncHeight, TimedOutSyncStatus()), + ); + } } - } catch (e, stacktrace) { - print(stacktrace); - print(e.toString()); + } - scanData.sendPort.send(SyncResponse(syncHeight, NotConnectedSyncStatus())); - break; + if (tweaksSubscription == null) { + return scanData.sendPort.send( + SyncResponse(syncHeight, UnsupportedSyncStatus()), + ); } + } catch (e, stacktrace) { + print(stacktrace); + print(e.toString()); + + scanData.sendPort.send(SyncResponse(syncHeight, NotConnectedSyncStatus())); } } diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index e9099ddeec..160c4ed48a 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -57,7 +57,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privKey!), hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp'); - if (silentAddresses.length == 0) + if (silentAddresses.length == 0) { silentAddresses.add(BitcoinSilentPaymentAddressRecord( silentAddress.toString(), index: 0, @@ -67,6 +67,16 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { network: network, type: SilentPaymentsAddresType.p2sp, )); + silentAddresses.add(BitcoinSilentPaymentAddressRecord( + silentAddress!.toLabeledSilentPaymentAddress(0).toString(), + index: 0, + isHidden: true, + name: "", + silentPaymentTweak: BytesUtils.toHexString(silentAddress!.generateLabel(0)), + network: network, + type: SilentPaymentsAddresType.p2sp, + )); + } } updateAddressesByMatch(); @@ -446,7 +456,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @action Future discoverAddresses(List addressList, bool isHidden, - Future Function(BitcoinAddressRecord, Set) getAddressHistory, + Future Function(BitcoinAddressRecord) getAddressHistory, {BitcoinAddressType type = SegwitAddresType.p2wpkh}) async { if (!isHidden) { _validateSideHdAddresses(addressList.toList()); @@ -456,8 +466,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { startIndex: addressList.length, isHidden: isHidden, type: type); addAddresses(newAddresses); - final addressesWithHistory = await Future.wait(newAddresses - .map((addr) => getAddressHistory(addr, _addresses.map((e) => e.address).toSet()))); + final addressesWithHistory = await Future.wait(newAddresses.map(getAddressHistory)); final isLastAddressUsed = addressesWithHistory.last == addressList.last.address; if (isLastAddressUsed) { diff --git a/cw_bitcoin/lib/exceptions.dart b/cw_bitcoin/lib/exceptions.dart index 4b03eb9221..830f56c241 100644 --- a/cw_bitcoin/lib/exceptions.dart +++ b/cw_bitcoin/lib/exceptions.dart @@ -2,7 +2,7 @@ import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/exceptions.dart'; class BitcoinTransactionWrongBalanceException extends TransactionWrongBalanceException { - BitcoinTransactionWrongBalanceException() : super(CryptoCurrency.btc); + BitcoinTransactionWrongBalanceException({super.amount}) : super(CryptoCurrency.btc); } class BitcoinTransactionNoInputsException extends TransactionNoInputsException {} @@ -25,3 +25,7 @@ class BitcoinTransactionCommitFailedDustOutputSendAll extends TransactionCommitFailedDustOutputSendAll {} class BitcoinTransactionCommitFailedVoutNegative extends TransactionCommitFailedVoutNegative {} + +class BitcoinTransactionCommitFailedBIP68Final extends TransactionCommitFailedBIP68Final {} + +class BitcoinTransactionSilentPaymentsNotSupported extends TransactionInputNotSupported {} diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index 529ac61da6..020919e43b 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -73,6 +73,10 @@ class PendingBitcoinTransaction with PendingTransaction { if (error.contains("bad-txns-vout-negative")) { throw BitcoinTransactionCommitFailedVoutNegative(); } + + if (error.contains("non-BIP68-final")) { + throw BitcoinTransactionCommitFailedBIP68Final(); + } } throw BitcoinTransactionCommitFailed(); } diff --git a/cw_core/lib/exceptions.dart b/cw_core/lib/exceptions.dart index 848ac40e63..eeb43bc384 100644 --- a/cw_core/lib/exceptions.dart +++ b/cw_core/lib/exceptions.dart @@ -1,9 +1,10 @@ import 'package:cw_core/crypto_currency.dart'; class TransactionWrongBalanceException implements Exception { - TransactionWrongBalanceException(this.currency); + TransactionWrongBalanceException(this.currency, {this.amount}); final CryptoCurrency currency; + final int? amount; } class TransactionNoInputsException implements Exception {} @@ -28,3 +29,7 @@ class TransactionCommitFailedDustOutput implements Exception {} class TransactionCommitFailedDustOutputSendAll implements Exception {} class TransactionCommitFailedVoutNegative implements Exception {} + +class TransactionCommitFailedBIP68Final implements Exception {} + +class TransactionInputNotSupported implements Exception {} diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index dd2d9ca67b..40c2109d16 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -31,6 +31,11 @@ class SyncedSyncStatus extends SyncStatus { double progress() => 1.0; } +class SyncronizingSyncStatus extends SyncStatus { + @override + double progress() => 0.0; +} + class NotConnectedSyncStatus extends SyncStatus { const NotConnectedSyncStatus(); @@ -43,10 +48,7 @@ class AttemptingSyncStatus extends SyncStatus { double progress() => 0.0; } -class FailedSyncStatus extends SyncStatus { - @override - double progress() => 1.0; -} +class FailedSyncStatus extends NotConnectedSyncStatus {} class ConnectingSyncStatus extends SyncStatus { @override @@ -58,21 +60,14 @@ class ConnectedSyncStatus extends SyncStatus { double progress() => 0.0; } -class UnsupportedSyncStatus extends SyncStatus { - @override - double progress() => 1.0; -} +class UnsupportedSyncStatus extends NotConnectedSyncStatus {} -class TimedOutSyncStatus extends SyncStatus { - @override - double progress() => 1.0; +class TimedOutSyncStatus extends NotConnectedSyncStatus { @override String toString() => 'Timed out'; } -class LostConnectionSyncStatus extends SyncStatus { - @override - double progress() => 1.0; +class LostConnectionSyncStatus extends NotConnectedSyncStatus { @override String toString() => 'Reconnecting'; } diff --git a/cw_core/lib/unspent_coins_info.dart b/cw_core/lib/unspent_coins_info.dart index 25abd3e485..ed09e17e0c 100644 --- a/cw_core/lib/unspent_coins_info.dart +++ b/cw_core/lib/unspent_coins_info.dart @@ -16,7 +16,8 @@ class UnspentCoinsInfo extends HiveObject { required this.value, this.keyImage = null, this.isChange = false, - this.accountIndex = 0 + this.accountIndex = 0, + this.isSilentPayment = false, }); static const typeId = UNSPENT_COINS_INFO_TYPE_ID; @@ -49,13 +50,16 @@ class UnspentCoinsInfo extends HiveObject { @HiveField(8, defaultValue: null) String? keyImage; - + @HiveField(9, defaultValue: false) bool isChange; @HiveField(10, defaultValue: 0) int accountIndex; + @HiveField(11, defaultValue: false) + bool? isSilentPayment; + String get note => noteRaw ?? ''; set note(String value) => noteRaw = value; diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 8ee3e16879..e66da479b9 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -187,7 +187,7 @@ class CWBitcoin extends Bitcoin { Future updateUnspents(Object wallet) async { final bitcoinWallet = wallet as ElectrumWallet; - await bitcoinWallet.updateUnspent(); + await bitcoinWallet.updateAllUnspents(); } WalletService createBitcoinWalletService( @@ -386,4 +386,10 @@ class CWBitcoin extends Bitcoin { final bitcoinWallet = wallet as ElectrumWallet; bitcoinWallet.walletAddresses.deleteSilentPaymentAddress(address); } + + @override + Future updateFeeRates(Object wallet) async { + final bitcoinWallet = wallet as ElectrumWallet; + await bitcoinWallet.updateFeeRates(); + } } diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index c743caf55d..c431963372 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -44,5 +44,9 @@ String syncStatusTitle(SyncStatus syncStatus) { return S.current.sync_status_timed_out; } + if (syncStatus is SyncronizingSyncStatus) { + return S.current.sync_status_syncronizing; + } + return ''; } diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 6033c24a65..792fd6f894 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -818,14 +818,16 @@ Future checkCurrentNodes( } if (currentBitcoinElectrumServer == null) { - final cakeWalletElectrum = Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin); + final cakeWalletElectrum = + Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin, useSSL: true); await nodeSource.add(cakeWalletElectrum); await sharedPreferences.setInt( PreferencesKey.currentBitcoinElectrumSererIdKey, cakeWalletElectrum.key as int); } if (currentLitecoinElectrumServer == null) { - final cakeWalletElectrum = Node(uri: cakeWalletLitecoinElectrumUri, type: WalletType.litecoin); + final cakeWalletElectrum = + Node(uri: cakeWalletLitecoinElectrumUri, type: WalletType.litecoin, useSSL: true); await nodeSource.add(cakeWalletElectrum); await sharedPreferences.setInt( PreferencesKey.currentLitecoinElectrumSererIdKey, cakeWalletElectrum.key as int); @@ -860,7 +862,8 @@ Future checkCurrentNodes( } if (currentBitcoinCashNodeServer == null) { - final node = Node(uri: cakeWalletBitcoinCashDefaultNodeUri, type: WalletType.bitcoinCash); + final node = + Node(uri: cakeWalletBitcoinCashDefaultNodeUri, type: WalletType.bitcoinCash, useSSL: true); await nodeSource.add(node); await sharedPreferences.setInt(PreferencesKey.currentBitcoinCashNodeIdKey, node.key as int); } @@ -888,7 +891,8 @@ Future resetBitcoinElectrumServer( .firstWhereOrNull((node) => node.uriRaw.toString() == cakeWalletBitcoinElectrumUri); if (cakeWalletNode == null) { - cakeWalletNode = Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin); + cakeWalletNode = + Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin, useSSL: true); await nodeSource.add(cakeWalletNode); } diff --git a/lib/src/screens/receive/widgets/address_cell.dart b/lib/src/screens/receive/widgets/address_cell.dart index fd34b00ac9..850c082099 100644 --- a/lib/src/screens/receive/widgets/address_cell.dart +++ b/lib/src/screens/receive/widgets/address_cell.dart @@ -146,7 +146,7 @@ class AddressCell extends StatelessWidget { mainAxisSize: MainAxisSize.max, children: [ Text( - '${S.of(context).balance}: $txCount', + '${S.of(context).balance}: $balance', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 970bb31f27..38fe184439 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; @@ -400,6 +401,10 @@ class SendPage extends BasePage { return; } + if (sendViewModel.isElectrumWallet) { + bitcoin!.updateFeeRates(sendViewModel.wallet); + } + reaction((_) => sendViewModel.state, (ExecutionState state) { if (state is FailureState) { WidgetsBinding.instance.addPostFrameCallback((_) { @@ -456,10 +461,12 @@ class SendPage extends BasePage { sendViewModel.selectedCryptoCurrency.toString()); final waitMessage = sendViewModel.walletType == WalletType.solana - ? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}' : ''; + ? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}' + : ''; final newContactMessage = newContactAddress != null - ? '\n${S.of(_dialogContext).add_contact_to_address_book}' : ''; + ? '\n${S.of(_dialogContext).add_contact_to_address_book}' + : ''; final alertContent = "$successMessage$waitMessage$newContactMessage"; diff --git a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart index 70ae7ce3f4..ee6d6dc730 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart @@ -57,6 +57,7 @@ class UnspentCoinsListFormState extends State { isSending: item.isSending, isFrozen: item.isFrozen, isChange: item.isChange, + isSilentPayment: item.isSilentPayment, onCheckBoxTap: item.isFrozen ? null : () async { diff --git a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart index e160260736..60a23c99b9 100644 --- a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart +++ b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart @@ -12,6 +12,7 @@ class UnspentCoinsListItem extends StatelessWidget { required this.isSending, required this.isFrozen, required this.isChange, + required this.isSilentPayment, this.onCheckBoxTap, }); @@ -21,18 +22,16 @@ class UnspentCoinsListItem extends StatelessWidget { final bool isSending; final bool isFrozen; final bool isChange; + final bool isSilentPayment; final Function()? onCheckBoxTap; @override Widget build(BuildContext context) { final unselectedItemColor = Theme.of(context).cardColor; final selectedItemColor = Theme.of(context).primaryColor; - final itemColor = isSending - ? selectedItemColor - : unselectedItemColor; - final amountColor = isSending - ? Colors.white - : Theme.of(context).extension()!.buttonTextColor; + final itemColor = isSending ? selectedItemColor : unselectedItemColor; + final amountColor = + isSending ? Colors.white : Theme.of(context).extension()!.buttonTextColor; final addressColor = isSending ? Colors.white.withOpacity(0.5) : Theme.of(context).extension()!.buttonSecondaryTextColor; @@ -121,6 +120,23 @@ class UnspentCoinsListItem extends StatelessWidget { ), ), ), + if (isSilentPayment) + Container( + height: 17, + padding: EdgeInsets.only(left: 6, right: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.5)), + color: Colors.white), + alignment: Alignment.center, + child: Text( + S.of(context).silent_payments, + style: TextStyle( + color: itemColor, + fontSize: 7, + fontWeight: FontWeight.w600, + ), + ), + ), ], ), ), diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 038301db4e..0bea0c59bf 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -567,9 +567,15 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor if (error is TransactionCommitFailedVoutNegative) { return S.current.tx_rejected_vout_negative; } + if (error is TransactionCommitFailedBIP68Final) { + return S.current.tx_rejected_bip68_final; + } if (error is TransactionNoDustOnChangeException) { return S.current.tx_commit_exception_no_dust_on_change(error.min, error.max); } + if (error is TransactionInputNotSupported) { + return S.current.tx_invalid_input; + } } return errorMessage; diff --git a/lib/view_model/unspent_coins/unspent_coins_item.dart b/lib/view_model/unspent_coins/unspent_coins_item.dart index bb5c4dd7ba..4ca5a10a27 100644 --- a/lib/view_model/unspent_coins/unspent_coins_item.dart +++ b/lib/view_model/unspent_coins/unspent_coins_item.dart @@ -15,7 +15,8 @@ abstract class UnspentCoinsItemBase with Store { required this.isChange, required this.amountRaw, required this.vout, - required this.keyImage + required this.keyImage, + required this.isSilentPayment, }); @observable @@ -47,4 +48,7 @@ abstract class UnspentCoinsItemBase with Store { @observable String? keyImage; + + @observable + bool isSilentPayment; } diff --git a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart index 3b90aff41f..bb04cfe5c8 100644 --- a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart +++ b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart @@ -86,22 +86,32 @@ abstract class UnspentCoinsListViewModelBase with Store { @action void _updateUnspentCoinsInfo() { _items.clear(); - _items.addAll(_getUnspents().map((elem) { - final info = - getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage); - - return UnspentCoinsItem( - address: elem.address, - amount: '${formatAmountToString(elem.value)} ${wallet.currency.title}', - hash: elem.hash, - isFrozen: info.isFrozen, - note: info.note, - isSending: info.isSending, - amountRaw: elem.value, - vout: elem.vout, - keyImage: elem.keyImage, - isChange: elem.isChange, - ); - })); + + List unspents = []; + _getUnspents().forEach((elem) { + try { + final info = + getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage); + + unspents.add(UnspentCoinsItem( + address: elem.address, + amount: '${formatAmountToString(elem.value)} ${wallet.currency.title}', + hash: elem.hash, + isFrozen: info.isFrozen, + note: info.note, + isSending: info.isSending, + amountRaw: elem.value, + vout: elem.vout, + keyImage: elem.keyImage, + isChange: elem.isChange, + isSilentPayment: info.isSilentPayment ?? false, + )); + } catch (e, s) { + print(s); + print(e.toString()); + } + }); + + _items.addAll(unspents); } } diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index 94dcd609de..f1c7d64d2f 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -316,8 +316,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo if (isElectrumWallet) { if (bitcoin!.hasSelectedSilentPayments(wallet)) { final addressItems = bitcoin!.getSilentPaymentAddresses(wallet).map((address) { - // Silent Payments index 0 is change per BIP - final isPrimary = address.index == 1; + final isPrimary = address.index == 0; return WalletAddressListItem( id: address.index, @@ -335,11 +334,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo final receivedAddressItems = bitcoin!.getSilentPaymentReceivedAddresses(wallet).map((address) { - final isPrimary = address.index == 0; - return WalletAddressListItem( id: address.index, - isPrimary: isPrimary, + isPrimary: false, name: address.name, address: address.address, txCount: address.txCount, diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index d7d68dfc92..1d1e2916a8 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -745,8 +745,10 @@ "trusted": "موثوق به", "tx_commit_exception_no_dust_on_change": "يتم رفض المعاملة مع هذا المبلغ. باستخدام هذه العملات المعدنية ، يمكنك إرسال ${min} دون تغيير أو ${max} الذي يعيد التغيير.", "tx_commit_failed": "فشل ارتكاب المعاملة. يرجى الاتصال بالدعم.", + "tx_invalid_input": "أنت تستخدم نوع الإدخال الخاطئ لهذا النوع من الدفع", "tx_no_dust_exception": "يتم رفض المعاملة عن طريق إرسال مبلغ صغير جدًا. يرجى محاولة زيادة المبلغ.", "tx_not_enough_inputs_exception": "لا يكفي المدخلات المتاحة. الرجاء تحديد المزيد تحت التحكم في العملة", + "tx_rejected_bip68_final": "تحتوي المعاملة على مدخلات غير مؤكدة وفشلت في استبدال الرسوم.", "tx_rejected_dust_change": "المعاملة التي يتم رفضها بموجب قواعد الشبكة ، ومبلغ التغيير المنخفض (الغبار). حاول إرسال كل أو تقليل المبلغ.", "tx_rejected_dust_output": "المعاملة التي يتم رفضها بموجب قواعد الشبكة ، وكمية الإخراج المنخفض (الغبار). يرجى زيادة المبلغ.", "tx_rejected_dust_output_send_all": "المعاملة التي يتم رفضها بموجب قواعد الشبكة ، وكمية الإخراج المنخفض (الغبار). يرجى التحقق من رصيد العملات المعدنية المحددة تحت التحكم في العملة.", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 2bc6caa4a6..261d4fbc87 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -745,8 +745,10 @@ "trusted": "Надежден", "tx_commit_exception_no_dust_on_change": "Сделката се отхвърля с тази сума. С тези монети можете да изпратите ${min} без промяна или ${max}, която връща промяна.", "tx_commit_failed": "Компетацията на транзакцията не успя. Моля, свържете се с поддръжката.", + "tx_invalid_input": "Използвате грешен тип вход за този тип плащане", "tx_no_dust_exception": "Сделката се отхвърля чрез изпращане на сума твърде малка. Моля, опитайте да увеличите сумата.", "tx_not_enough_inputs_exception": "Няма достатъчно налични входове. Моля, изберете повече под контрол на монети", + "tx_rejected_bip68_final": "Сделката има непотвърдени входове и не успя да се замени с такса.", "tx_rejected_dust_change": "Транзакция, отхвърлена от мрежови правила, ниска сума на промяна (прах). Опитайте да изпратите всички или да намалите сумата.", "tx_rejected_dust_output": "Транзакция, отхвърлена от мрежови правила, ниска стойност на изхода (прах). Моля, увеличете сумата.", "tx_rejected_dust_output_send_all": "Транзакция, отхвърлена от мрежови правила, ниска стойност на изхода (прах). Моля, проверете баланса на монетите, избрани под контрол на монети.", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 1ce8d1fcd7..b368947c0a 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -745,8 +745,10 @@ "trusted": "Důvěřovat", "tx_commit_exception_no_dust_on_change": "Transakce je zamítnuta s touto částkou. S těmito mincemi můžete odeslat ${min} bez změny nebo ${max}, které se vrátí změna.", "tx_commit_failed": "Transakce COMPORT selhala. Kontaktujte prosím podporu.", + "tx_invalid_input": "Pro tento typ platby používáte nesprávný typ vstupu", "tx_no_dust_exception": "Transakce je zamítnuta odesláním příliš malé. Zkuste prosím zvýšit částku.", "tx_not_enough_inputs_exception": "Není k dispozici dostatek vstupů. Vyberte prosím více pod kontrolou mincí", + "tx_rejected_bip68_final": "Transakce má nepotvrzené vstupy a nepodařilo se nahradit poplatkem.", "tx_rejected_dust_change": "Transakce zamítnuta podle síťových pravidel, množství nízké změny (prach). Zkuste odeslat vše nebo snížit částku.", "tx_rejected_dust_output": "Transakce zamítnuta síťovými pravidly, nízkým množstvím výstupu (prach). Zvyšte prosím částku.", "tx_rejected_dust_output_send_all": "Transakce zamítnuta síťovými pravidly, nízkým množstvím výstupu (prach). Zkontrolujte prosím zůstatek mincí vybraných pod kontrolou mincí.", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index b508e691e4..05b6636f54 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -746,8 +746,10 @@ "trusted": "Vertrauenswürdige", "tx_commit_exception_no_dust_on_change": "Die Transaktion wird diesen Betrag abgelehnt. Mit diesen Münzen können Sie ${min} ohne Veränderung oder ${max} senden, die Änderungen zurückgeben.", "tx_commit_failed": "Transaktionsausschüsse ist fehlgeschlagen. Bitte wenden Sie sich an Support.", + "tx_invalid_input": "Sie verwenden den falschen Eingangstyp für diese Art von Zahlung", "tx_no_dust_exception": "Die Transaktion wird abgelehnt, indem eine Menge zu klein gesendet wird. Bitte versuchen Sie, die Menge zu erhöhen.", "tx_not_enough_inputs_exception": "Nicht genügend Eingänge verfügbar. Bitte wählen Sie mehr unter Münzkontrolle aus", + "tx_rejected_bip68_final": "Die Transaktion hat unbestätigte Inputs und konnte nicht durch Gebühr ersetzt werden.", "tx_rejected_dust_change": "Transaktion abgelehnt durch Netzwerkregeln, niedriger Änderungsbetrag (Staub). Versuchen Sie, alle zu senden oder die Menge zu reduzieren.", "tx_rejected_dust_output": "Transaktion durch Netzwerkregeln, niedriger Ausgangsmenge (Staub) abgelehnt. Bitte erhöhen Sie den Betrag.", "tx_rejected_dust_output_send_all": "Transaktion durch Netzwerkregeln, niedriger Ausgangsmenge (Staub) abgelehnt. Bitte überprüfen Sie den Gleichgewicht der unter Münzkontrolle ausgewählten Münzen.", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 8110181bb4..7635583257 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -745,8 +745,10 @@ "trusted": "Trusted", "tx_commit_exception_no_dust_on_change": "The transaction is rejected with this amount. With these coins you can send ${min} without change or ${max} that returns change.", "tx_commit_failed": "Transaction commit failed. Please contact support.", + "tx_invalid_input": "You are using the wrong input type for this type of payment", "tx_no_dust_exception": "The transaction is rejected by sending an amount too small. Please try increasing the amount.", "tx_not_enough_inputs_exception": "Not enough inputs available. Please select more under Coin Control", + "tx_rejected_bip68_final": "Transaction has unconfirmed inputs and failed to replace by fee.", "tx_rejected_dust_change": "Transaction rejected by network rules, low change amount (dust). Try sending ALL or reducing the amount.", "tx_rejected_dust_output": "Transaction rejected by network rules, low output amount (dust). Please increase the amount.", "tx_rejected_dust_output_send_all": "Transaction rejected by network rules, low output amount (dust). Please check the balance of coins selected under Coin Control.", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index b29eef59ce..71b7ad423d 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -746,8 +746,10 @@ "trusted": "de confianza", "tx_commit_exception_no_dust_on_change": "La transacción se rechaza con esta cantidad. Con estas monedas puede enviar ${min} sin cambios o ${max} que devuelve el cambio.", "tx_commit_failed": "La confirmación de transacción falló. Póngase en contacto con el soporte.", + "tx_invalid_input": "Está utilizando el tipo de entrada incorrecto para este tipo de pago", "tx_no_dust_exception": "La transacción se rechaza enviando una cantidad demasiado pequeña. Intente aumentar la cantidad.", "tx_not_enough_inputs_exception": "No hay suficientes entradas disponibles. Seleccione más bajo control de monedas", + "tx_rejected_bip68_final": "La transacción tiene entradas no confirmadas y no ha podido reemplazar por tarifa.", "tx_rejected_dust_change": "Transacción rechazada por reglas de red, bajo cambio de cambio (polvo). Intente enviar todo o reducir la cantidad.", "tx_rejected_dust_output": "Transacción rechazada por reglas de red, baja cantidad de salida (polvo). Aumente la cantidad.", "tx_rejected_dust_output_send_all": "Transacción rechazada por reglas de red, baja cantidad de salida (polvo). Verifique el saldo de monedas seleccionadas bajo control de monedas.", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index f2b30a8e49..a034116027 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -745,8 +745,10 @@ "trusted": "de confiance", "tx_commit_exception_no_dust_on_change": "La transaction est rejetée avec ce montant. Avec ces pièces, vous pouvez envoyer ${min} sans changement ou ${max} qui renvoie le changement.", "tx_commit_failed": "La validation de la transaction a échoué. Veuillez contacter l'assistance.", + "tx_invalid_input": "Vous utilisez le mauvais type d'entrée pour ce type de paiement", "tx_no_dust_exception": "La transaction est rejetée en envoyant un montant trop faible. Veuillez essayer d'augmenter le montant.", "tx_not_enough_inputs_exception": "Pas assez d'entrées disponibles. Veuillez sélectionner plus sous Control Control", + "tx_rejected_bip68_final": "La transaction a des entrées non confirmées et n'a pas réussi à remplacer par les frais.", "tx_rejected_dust_change": "Transaction rejetée par les règles du réseau, montant de faible variation (poussière). Essayez d'envoyer tout ou de réduire le montant.", "tx_rejected_dust_output": "Transaction rejetée par les règles du réseau, faible quantité de sortie (poussière). Veuillez augmenter le montant.", "tx_rejected_dust_output_send_all": "Transaction rejetée par les règles du réseau, faible quantité de sortie (poussière). Veuillez vérifier le solde des pièces sélectionnées sous le contrôle des pièces de monnaie.", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 09365a462d..36e0f8a3db 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -747,8 +747,10 @@ "trusted": "Amintacce", "tx_commit_exception_no_dust_on_change": "An ƙi ma'amala da wannan adadin. Tare da waɗannan tsabar kudi Zaka iya aika ${min}, ba tare da canji ba ko ${max} wanda ya dawo canzawa.", "tx_commit_failed": "Ma'amala ya kasa. Da fatan za a tuntuɓi goyan baya.", + "tx_invalid_input": "Kuna amfani da nau'in shigar da ba daidai ba don wannan nau'in biyan kuɗi", "tx_no_dust_exception": "An ƙi ma'amala ta hanyar aika adadin ƙarami. Da fatan za a gwada ƙara adadin.", "tx_not_enough_inputs_exception": "Bai isa ba hanyoyin da ake samu. Da fatan za selectiari a karkashin Kwarewar Coin", + "tx_rejected_bip68_final": "Ma'amala tana da abubuwan da basu dace ba kuma sun kasa maye gurbin ta.", "tx_rejected_dust_change": "Ma'amala ta ƙi ta dokokin cibiyar sadarwa, ƙarancin canji (ƙura). Gwada aikawa da duka ko rage adadin.", "tx_rejected_dust_output": "Ma'adar da aka ƙi ta dokokin cibiyar sadarwa, ƙananan fitarwa (ƙura). Da fatan za a ƙara adadin.", "tx_rejected_dust_output_send_all": "Ma'adar da aka ƙi ta dokokin cibiyar sadarwa, ƙananan fitarwa (ƙura). Da fatan za a duba daidaiton tsabar kudi a ƙarƙashin ikon tsabar kudin.", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 4472b0aae1..3bfe67695b 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -747,8 +747,10 @@ "trusted": "भरोसा", "tx_commit_exception_no_dust_on_change": "लेनदेन को इस राशि से खारिज कर दिया जाता है। इन सिक्कों के साथ आप चेंज या ${min} के बिना ${max} को भेज सकते हैं जो परिवर्तन लौटाता है।", "tx_commit_failed": "लेन -देन प्रतिबद्ध विफल। कृपया संपर्क समर्थन करें।", + "tx_invalid_input": "आप इस प्रकार के भुगतान के लिए गलत इनपुट प्रकार का उपयोग कर रहे हैं", "tx_no_dust_exception": "लेनदेन को बहुत छोटी राशि भेजकर अस्वीकार कर दिया जाता है। कृपया राशि बढ़ाने का प्रयास करें।", "tx_not_enough_inputs_exception": "पर्याप्त इनपुट उपलब्ध नहीं है। कृपया सिक्का नियंत्रण के तहत अधिक चुनें", + "tx_rejected_bip68_final": "लेन -देन में अपुष्ट इनपुट हैं और शुल्क द्वारा प्रतिस्थापित करने में विफल रहे हैं।", "tx_rejected_dust_change": "नेटवर्क नियमों, कम परिवर्तन राशि (धूल) द्वारा खारिज किए गए लेनदेन। सभी भेजने या राशि को कम करने का प्रयास करें।", "tx_rejected_dust_output": "नेटवर्क नियमों, कम आउटपुट राशि (धूल) द्वारा खारिज किए गए लेनदेन। कृपया राशि बढ़ाएं।", "tx_rejected_dust_output_send_all": "नेटवर्क नियमों, कम आउटपुट राशि (धूल) द्वारा खारिज किए गए लेनदेन। कृपया सिक्का नियंत्रण के तहत चुने गए सिक्कों के संतुलन की जाँच करें।", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index fd305cf50d..ad52db1fac 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -745,8 +745,10 @@ "trusted": "vjerovao", "tx_commit_exception_no_dust_on_change": "Transakcija se odbija s tim iznosom. Pomoću ovih kovanica možete poslati ${min} bez promjene ili ${max} koja vraća promjenu.", "tx_commit_failed": "Obveza transakcije nije uspjela. Molimo kontaktirajte podršku.", + "tx_invalid_input": "Koristite pogrešnu vrstu ulaza za ovu vrstu plaćanja", "tx_no_dust_exception": "Transakcija se odbija slanjem iznosa premalo. Pokušajte povećati iznos.", "tx_not_enough_inputs_exception": "Nema dovoljno unosa. Molimo odaberite više pod kontrolom novčića", + "tx_rejected_bip68_final": "Transakcija ima nepotvrđene unose i nije zamijenila naknadom.", "tx_rejected_dust_change": "Transakcija odbijena mrežnim pravilima, niska količina promjene (prašina). Pokušajte poslati sve ili smanjiti iznos.", "tx_rejected_dust_output": "Transakcija odbijena mrežnim pravilima, niska količina izlaza (prašina). Molimo povećajte iznos.", "tx_rejected_dust_output_send_all": "Transakcija odbijena mrežnim pravilima, niska količina izlaza (prašina). Molimo provjerite ravnotežu kovanica odabranih pod kontrolom novčića.", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index dd5acfed7f..2db131a1ac 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -748,8 +748,10 @@ "trusted": "Dipercayai", "tx_commit_exception_no_dust_on_change": "Transaksi ditolak dengan jumlah ini. Dengan koin ini Anda dapat mengirim ${min} tanpa perubahan atau ${max} yang mengembalikan perubahan.", "tx_commit_failed": "Transaksi Gagal. Silakan hubungi Dukungan.", + "tx_invalid_input": "Anda menggunakan jenis input yang salah untuk jenis pembayaran ini", "tx_no_dust_exception": "Transaksi ditolak dengan mengirimkan jumlah yang terlalu kecil. Silakan coba tingkatkan jumlahnya.", "tx_not_enough_inputs_exception": "Tidak cukup input yang tersedia. Pilih lebih banyak lagi di bawah Kontrol Koin", + "tx_rejected_bip68_final": "Transaksi memiliki input yang belum dikonfirmasi dan gagal mengganti dengan biaya.", "tx_rejected_dust_change": "Transaksi ditolak oleh aturan jaringan, jumlah perubahan rendah (debu). Coba kirim semua atau mengurangi jumlahnya.", "tx_rejected_dust_output": "Transaksi ditolak oleh aturan jaringan, jumlah output rendah (debu). Harap tingkatkan jumlahnya.", "tx_rejected_dust_output_send_all": "Transaksi ditolak oleh aturan jaringan, jumlah output rendah (debu). Silakan periksa saldo koin yang dipilih di bawah kontrol koin.", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 9d88e979d7..e66bceff5b 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -747,8 +747,10 @@ "trusted": "di fiducia", "tx_commit_exception_no_dust_on_change": "La transazione viene respinta con questo importo. Con queste monete è possibile inviare ${min} senza modifiche o ${max} che restituisce il cambiamento.", "tx_commit_failed": "Commit di transazione non riuscita. Si prega di contattare il supporto.", + "tx_invalid_input": "Stai usando il tipo di input sbagliato per questo tipo di pagamento", "tx_no_dust_exception": "La transazione viene respinta inviando un importo troppo piccolo. Per favore, prova ad aumentare l'importo.", "tx_not_enough_inputs_exception": "Input non sufficienti disponibili. Seleziona di più sotto il controllo delle monete", + "tx_rejected_bip68_final": "La transazione ha input non confermati e non è stata sostituita per tassa.", "tx_rejected_dust_change": "Transazione respinta dalle regole di rete, quantità bassa variazione (polvere). Prova a inviare tutto o ridurre l'importo.", "tx_rejected_dust_output": "Transazione respinta dalle regole di rete, bassa quantità di output (polvere). Si prega di aumentare l'importo.", "tx_rejected_dust_output_send_all": "Transazione respinta dalle regole di rete, bassa quantità di output (polvere). Si prega di controllare il saldo delle monete selezionate sotto controllo delle monete.", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index d11c468510..192ea09be0 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -746,8 +746,10 @@ "trusted": "信頼できる", "tx_commit_exception_no_dust_on_change": "この金額ではトランザクションは拒否されます。 これらのコインを使用すると、おつりなしの ${min} またはおつりを返す ${max} を送信できます。", "tx_commit_failed": "トランザクションコミットは失敗しました。サポートに連絡してください。", + "tx_invalid_input": "このタイプの支払いに間違った入力タイプを使用しています", "tx_no_dust_exception": "トランザクションは、小さすぎる金額を送信することにより拒否されます。量を増やしてみてください。", "tx_not_enough_inputs_exception": "利用可能な入力が十分ではありません。コイン制御下でもっと選択してください", + "tx_rejected_bip68_final": "トランザクションには未確認の入力があり、料金で交換できませんでした。", "tx_rejected_dust_change": "ネットワークルール、低い変更量(ほこり)によって拒否されたトランザクション。すべてを送信するか、金額を減らしてみてください。", "tx_rejected_dust_output": "ネットワークルール、低出力量(ダスト)によって拒否されたトランザクション。金額を増やしてください。", "tx_rejected_dust_output_send_all": "ネットワークルール、低出力量(ダスト)によって拒否されたトランザクション。コイン管理下で選択されたコインのバランスを確認してください。", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index f0ab69a8d5..ef8424c16b 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -746,8 +746,10 @@ "trusted": "신뢰할 수 있는", "tx_commit_exception_no_dust_on_change": "이 금액으로 거래가 거부되었습니다. 이 코인을 사용하면 거스름돈 없이 ${min}를 보내거나 거스름돈을 반환하는 ${max}를 보낼 수 있습니다.", "tx_commit_failed": "거래 커밋이 실패했습니다. 지원에 연락하십시오.", + "tx_invalid_input": "이 유형의 지불에 잘못 입력 유형을 사용하고 있습니다.", "tx_no_dust_exception": "너무 작은 금액을 보내면 거래가 거부됩니다. 금액을 늘리십시오.", "tx_not_enough_inputs_exception": "사용 가능한 입력이 충분하지 않습니다. 코인 컨트롤에서 더 많은 것을 선택하십시오", + "tx_rejected_bip68_final": "거래는 확인되지 않은 입력을 받았으며 수수료로 교체하지 못했습니다.", "tx_rejected_dust_change": "네트워크 규칙, 낮은 변경 금액 (먼지)에 의해 거부 된 거래. 전부를 보내거나 금액을 줄이십시오.", "tx_rejected_dust_output": "네트워크 규칙, 낮은 출력 금액 (먼지)에 의해 거부 된 거래. 금액을 늘리십시오.", "tx_rejected_dust_output_send_all": "네트워크 규칙, 낮은 출력 금액 (먼지)에 의해 거부 된 거래. 동전 제어에서 선택한 동전의 균형을 확인하십시오.", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index fcc482bae0..b64f2ebd13 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -745,8 +745,10 @@ "trusted": "ယုံတယ်။", "tx_commit_exception_no_dust_on_change": "အဆိုပါငွေပေးငွေယူကဒီပမာဏနှင့်အတူပယ်ချခံရသည်။ ဤဒင်္ဂါးပြားများနှင့်အတူပြောင်းလဲမှုကိုပြန်လည်ပြောင်းလဲခြင်းသို့မဟုတ် ${min} မပါဘဲ ${max} ပေးပို့နိုင်သည်။", "tx_commit_failed": "ငွေပေးငွေယူကျူးလွန်မှုပျက်ကွက်။ ကျေးဇူးပြုပြီးပံ့ပိုးမှုဆက်သွယ်ပါ။", + "tx_invalid_input": "သင်သည်ဤငွေပေးချေမှုအမျိုးအစားအတွက်မှားယွင်းသော input type ကိုအသုံးပြုနေသည်", "tx_no_dust_exception": "ငွေပမာဏကိုသေးငယ်လွန်းသောငွေပမာဏကိုပေးပို့ခြင်းဖြင့်ပယ်ဖျက်ခြင်းကိုငြင်းပယ်သည်။ ကျေးဇူးပြုပြီးငွေပမာဏကိုတိုးမြှင့်ကြိုးစားပါ။", "tx_not_enough_inputs_exception": "အလုံအလောက်သွင်းအားစုများမလုံလောက်။ ကျေးဇူးပြုပြီးဒင်္ဂါးပြားထိန်းချုပ်မှုအောက်တွင်ပိုမိုရွေးချယ်ပါ", + "tx_rejected_bip68_final": "ငွေပေးငွေယူသည်အတည်မပြုရသေးသောသွင်းအားစုများရှိပြီးအခကြေးငွေဖြင့်အစားထိုးရန်ပျက်ကွက်ခဲ့သည်။", "tx_rejected_dust_change": "Network စည်းမျဉ်းစည်းကမ်းများဖြင့်ပယ်ဖျက်ခြင်းသည် Network စည်းမျဉ်းစည်းကမ်းများဖြင့်ငြင်းပယ်ခြင်း, အားလုံးပေးပို့ခြင်းသို့မဟုတ်ငွေပမာဏကိုလျှော့ချကြိုးစားပါ။", "tx_rejected_dust_output": "Network စည်းမျဉ်းစည်းကမ်းများဖြင့် ပယ်ချ. ငွေပေးချေမှုသည် output output (ဖုန်မှုန့်) ဖြင့်ပယ်ချခဲ့သည်။ ကျေးဇူးပြုပြီးငွေပမာဏကိုတိုးမြှင့်ပေးပါ။", "tx_rejected_dust_output_send_all": "Network စည်းမျဉ်းစည်းကမ်းများဖြင့် ပယ်ချ. ငွေပေးချေမှုသည် output output (ဖုန်မှုန့်) ဖြင့်ပယ်ချခဲ့သည်။ ဒင်္ဂါးပြားထိန်းချုပ်မှုအောက်တွင်ရွေးချယ်ထားသောဒင်္ဂါးများ၏လက်ကျန်ငွေကိုစစ်ဆေးပါ။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index c0220bd807..b49bdc4da5 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -745,8 +745,10 @@ "trusted": "vertrouwd", "tx_commit_exception_no_dust_on_change": "De transactie wordt afgewezen met dit bedrag. Met deze munten kunt u ${min} verzenden zonder verandering of ${max} die wijziging retourneert.", "tx_commit_failed": "Transactiebewissing is mislukt. Neem contact op met de ondersteuning.", + "tx_invalid_input": "U gebruikt het verkeerde invoertype voor dit type betaling", "tx_no_dust_exception": "De transactie wordt afgewezen door een te klein bedrag te verzenden. Probeer het bedrag te verhogen.", "tx_not_enough_inputs_exception": "Niet genoeg ingangen beschikbaar. Selecteer meer onder muntenbesturing", + "tx_rejected_bip68_final": "Transactie heeft onbevestigde ingangen en niet vervangen door vergoeding.", "tx_rejected_dust_change": "Transactie afgewezen door netwerkregels, laag wijzigingsbedrag (stof). Probeer alles te verzenden of het bedrag te verminderen.", "tx_rejected_dust_output": "Transactie afgewezen door netwerkregels, laag outputbedrag (stof). Verhoog het bedrag.", "tx_rejected_dust_output_send_all": "Transactie afgewezen door netwerkregels, laag outputbedrag (stof). Controleer het saldo van munten die zijn geselecteerd onder muntcontrole.", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index d92a193e72..dee8d0fdd8 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -745,8 +745,10 @@ "trusted": "Zaufany", "tx_commit_exception_no_dust_on_change": "Transakcja jest odrzucana z tą kwotą. Za pomocą tych monet możesz wysłać ${min} bez zmiany lub ${max}, które zwraca zmianę.", "tx_commit_failed": "Zatwierdzenie transakcji nie powiodło się. Skontaktuj się z obsługą.", + "tx_invalid_input": "Używasz niewłaściwego typu wejściowego dla tego rodzaju płatności", "tx_no_dust_exception": "Transakcja jest odrzucana przez wysyłanie zbyt małej ilości. Spróbuj zwiększyć kwotę.", "tx_not_enough_inputs_exception": "Za mało dostępnych danych wejściowych. Wybierz więcej pod kontrolą monet", + "tx_rejected_bip68_final": "Transakcja niepotwierdza wejściów i nie zastąpiła opłaty.", "tx_rejected_dust_change": "Transakcja odrzucona według reguł sieciowych, niska ilość zmiany (kurz). Spróbuj wysłać całość lub zmniejszyć kwotę.", "tx_rejected_dust_output": "Transakcja odrzucona według reguł sieciowych, niskiej ilości wyjściowej (pyłu). Zwiększ kwotę.", "tx_rejected_dust_output_send_all": "Transakcja odrzucona według reguł sieciowych, niskiej ilości wyjściowej (pyłu). Sprawdź saldo monet wybranych pod kontrolą monet.", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index cf23b4b117..0bf4e4422c 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -747,8 +747,10 @@ "trusted": "confiável", "tx_commit_exception_no_dust_on_change": "A transação é rejeitada com esse valor. Com essas moedas, você pode enviar ${min} sem alteração ou ${max} que retorna alterações.", "tx_commit_failed": "A confirmação da transação falhou. Entre em contato com o suporte.", + "tx_invalid_input": "Você está usando o tipo de entrada errado para este tipo de pagamento", "tx_no_dust_exception": "A transação é rejeitada enviando uma quantia pequena demais. Por favor, tente aumentar o valor.", "tx_not_enough_inputs_exception": "Não há entradas disponíveis. Selecione mais sob controle de moedas", + "tx_rejected_bip68_final": "A transação tem entradas não confirmadas e não substituiu por taxa.", "tx_rejected_dust_change": "Transação rejeitada pelas regras de rede, baixa quantidade de troco (poeira). Tente enviar tudo ou reduzir o valor.", "tx_rejected_dust_output": "Transação rejeitada por regras de rede, baixa quantidade de saída (poeira). Por favor, aumente o valor.", "tx_rejected_dust_output_send_all": "Transação rejeitada por regras de rede, baixa quantidade de saída (poeira). Por favor, verifique o saldo de moedas selecionadas sob controle de moedas.", @@ -760,7 +762,7 @@ "unconfirmed": "Saldo não confirmado", "understand": "Entendo", "unmatched_currencies": "A moeda da sua carteira atual não corresponde à do QR digitalizado", - "unspent_change": "Mudar", + "unspent_change": "Troco", "unspent_coins_details_title": "Detalhes de moedas não gastas", "unspent_coins_title": "Moedas não gastas", "unsupported_asset": "Não oferecemos suporte a esta ação para este recurso. Crie ou mude para uma carteira de um tipo de ativo compatível.", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index cccd725904..006ec2a7ed 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -746,8 +746,10 @@ "trusted": "доверенный", "tx_commit_exception_no_dust_on_change": "Транзакция отклоняется с этой суммой. С этими монетами вы можете отправлять ${min} без изменения или ${max}, которые возвращают изменение.", "tx_commit_failed": "Комплект транзакции не удался. Пожалуйста, свяжитесь с поддержкой.", + "tx_invalid_input": "Вы используете неправильный тип ввода для этого типа оплаты", "tx_no_dust_exception": "Транзакция отклоняется путем отправки слишком маленькой суммы. Пожалуйста, попробуйте увеличить сумму.", "tx_not_enough_inputs_exception": "Недостаточно входов доступны. Пожалуйста, выберите больше под контролем монет", + "tx_rejected_bip68_final": "Транзакция имеет неподтвержденные входные данные и не смогли заменить на плату.", "tx_rejected_dust_change": "Транзакция отклоняется в соответствии с правилами сети, низкой суммой изменений (пыль). Попробуйте отправить все или уменьшить сумму.", "tx_rejected_dust_output": "Транзакция отклоняется в соответствии с правилами сети, низкой выходной суммой (пыль). Пожалуйста, увеличьте сумму.", "tx_rejected_dust_output_send_all": "Транзакция отклоняется в соответствии с правилами сети, низкой выходной суммой (пыль). Пожалуйста, проверьте баланс монет, выбранных под контролем монет.", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index dbf461990c..a5d7552052 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -745,8 +745,10 @@ "trusted": "มั่นคง", "tx_commit_exception_no_dust_on_change": "ธุรกรรมถูกปฏิเสธด้วยจำนวนเงินนี้ ด้วยเหรียญเหล่านี้คุณสามารถส่ง ${min} โดยไม่ต้องเปลี่ยนแปลงหรือ ${max} ที่ส่งคืนการเปลี่ยนแปลง", "tx_commit_failed": "การทำธุรกรรมล้มเหลว กรุณาติดต่อฝ่ายสนับสนุน", + "tx_invalid_input": "คุณกำลังใช้ประเภทอินพุตที่ไม่ถูกต้องสำหรับการชำระเงินประเภทนี้", "tx_no_dust_exception": "การทำธุรกรรมถูกปฏิเสธโดยการส่งจำนวนน้อยเกินไป โปรดลองเพิ่มจำนวนเงิน", "tx_not_enough_inputs_exception": "มีอินพุตไม่เพียงพอ โปรดเลือกเพิ่มเติมภายใต้การควบคุมเหรียญ", + "tx_rejected_bip68_final": "การทำธุรกรรมมีอินพุตที่ไม่ได้รับการยืนยันและไม่สามารถแทนที่ด้วยค่าธรรมเนียม", "tx_rejected_dust_change": "ธุรกรรมถูกปฏิเสธโดยกฎเครือข่ายจำนวนการเปลี่ยนแปลงต่ำ (ฝุ่น) ลองส่งทั้งหมดหรือลดจำนวนเงิน", "tx_rejected_dust_output": "การทำธุรกรรมถูกปฏิเสธโดยกฎเครือข่ายจำนวนเอาต์พุตต่ำ (ฝุ่น) โปรดเพิ่มจำนวนเงิน", "tx_rejected_dust_output_send_all": "การทำธุรกรรมถูกปฏิเสธโดยกฎเครือข่ายจำนวนเอาต์พุตต่ำ (ฝุ่น) โปรดตรวจสอบยอดคงเหลือของเหรียญที่เลือกภายใต้การควบคุมเหรียญ", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 904942bffb..49ea9d842d 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -745,8 +745,10 @@ "trusted": "Pinagkakatiwalaan", "tx_commit_exception_no_dust_on_change": "Ang transaksyon ay tinanggihan sa halagang ito. Sa mga barya na ito maaari kang magpadala ng ${min} nang walang pagbabago o ${max} na nagbabalik ng pagbabago.", "tx_commit_failed": "Nabigo ang transaksyon sa transaksyon. Mangyaring makipag -ugnay sa suporta.", + "tx_invalid_input": "Gumagamit ka ng maling uri ng pag -input para sa ganitong uri ng pagbabayad", "tx_no_dust_exception": "Ang transaksyon ay tinanggihan sa pamamagitan ng pagpapadala ng isang maliit na maliit. Mangyaring subukang dagdagan ang halaga.", "tx_not_enough_inputs_exception": "Hindi sapat na magagamit ang mga input. Mangyaring pumili ng higit pa sa ilalim ng control ng barya", + "tx_rejected_bip68_final": "Ang transaksyon ay hindi nakumpirma na mga input at nabigo na palitan ng bayad.", "tx_rejected_dust_change": "Ang transaksyon na tinanggihan ng mga patakaran sa network, mababang halaga ng pagbabago (alikabok). Subukang ipadala ang lahat o bawasan ang halaga.", "tx_rejected_dust_output": "Ang transaksyon na tinanggihan ng mga patakaran sa network, mababang halaga ng output (alikabok). Mangyaring dagdagan ang halaga.", "tx_rejected_dust_output_send_all": "Ang transaksyon na tinanggihan ng mga patakaran sa network, mababang halaga ng output (alikabok). Mangyaring suriin ang balanse ng mga barya na napili sa ilalim ng kontrol ng barya.", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index d4cb237708..27e281cb1a 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -745,8 +745,10 @@ "trusted": "Güvenilir", "tx_commit_exception_no_dust_on_change": "İşlem bu miktarla reddedilir. Bu madeni paralarla değişiklik yapmadan ${min} veya değişikliği döndüren ${max} gönderebilirsiniz.", "tx_commit_failed": "İşlem taahhüdü başarısız oldu. Lütfen Destek ile iletişime geçin.", + "tx_invalid_input": "Bu tür ödeme için yanlış giriş türünü kullanıyorsunuz", "tx_no_dust_exception": "İşlem, çok küçük bir miktar gönderilerek reddedilir. Lütfen miktarı artırmayı deneyin.", "tx_not_enough_inputs_exception": "Yeterli giriş yok. Lütfen madeni para kontrolü altında daha fazlasını seçin", + "tx_rejected_bip68_final": "İşlemin doğrulanmamış girdileri var ve ücrete göre değiştirilemedi.", "tx_rejected_dust_change": "Ağ kurallarına göre reddedilen işlem, düşük değişim miktarı (toz). Tümünü göndermeyi veya miktarı azaltmayı deneyin.", "tx_rejected_dust_output": "Ağ kurallarına göre reddedilen işlem, düşük çıktı miktarı (toz). Lütfen miktarı artırın.", "tx_rejected_dust_output_send_all": "Ağ kurallarına göre reddedilen işlem, düşük çıktı miktarı (toz). Lütfen madeni para kontrolü altında seçilen madeni para dengesini kontrol edin.", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 830027c915..ad41e030f6 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -746,8 +746,10 @@ "trusted": "довіряють", "tx_commit_exception_no_dust_on_change": "Транзакція відхилена цією сумою. За допомогою цих монет ви можете надіслати ${min} без змін або ${max}, що повертає зміни.", "tx_commit_failed": "Транзакційна комісія не вдалося. Будь ласка, зв'яжіться з підтримкою.", + "tx_invalid_input": "Ви використовуєте неправильний тип введення для цього типу оплати", "tx_no_dust_exception": "Угода відхиляється, відправивши суму занадто мала. Будь ласка, спробуйте збільшити суму.", "tx_not_enough_inputs_exception": "Недостатньо доступних входів. Виберіть більше під контролем монети", + "tx_rejected_bip68_final": "Трансакція має непідтверджені входи і не замінила плату.", "tx_rejected_dust_change": "Транзакція відхилена за допомогою мережевих правил, низька кількість змін (пил). Спробуйте надіслати все або зменшити суму.", "tx_rejected_dust_output": "Транзакція відхилена за допомогою мережевих правил, низька кількість вихідної кількості (пил). Будь ласка, збільшуйте суму.", "tx_rejected_dust_output_send_all": "Транзакція відхилена за допомогою мережевих правил, низька кількість вихідної кількості (пил). Будь ласка, перевірте баланс монет, вибраних під контролем монет.", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 97e8ba4581..12bd1ccfed 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -747,8 +747,10 @@ "trusted": "قابل اعتماد", "tx_commit_exception_no_dust_on_change": "اس رقم سے لین دین کو مسترد کردیا گیا ہے۔ ان سککوں کے ذریعہ آپ بغیر کسی تبدیلی کے ${min} یا ${max} بھیج سکتے ہیں جو لوٹتے ہیں۔", "tx_commit_failed": "ٹرانزیکشن کمٹ ناکام ہوگیا۔ براہ کرم سپورٹ سے رابطہ کریں۔", + "tx_invalid_input": "آپ اس قسم کی ادائیگی کے لئے غلط ان پٹ کی قسم استعمال کررہے ہیں", "tx_no_dust_exception": "لین دین کو بہت چھوٹی رقم بھیج کر مسترد کردیا جاتا ہے۔ براہ کرم رقم میں اضافہ کرنے کی کوشش کریں۔", "tx_not_enough_inputs_exception": "کافی ان پٹ دستیاب نہیں ہے۔ براہ کرم سکے کے کنٹرول میں مزید منتخب کریں", + "tx_rejected_bip68_final": "لین دین میں غیر مصدقہ آدانوں کی ہے اور وہ فیس کے ذریعہ تبدیل کرنے میں ناکام رہا ہے۔", "tx_rejected_dust_change": "نیٹ ورک کے قواعد ، کم تبدیلی کی رقم (دھول) کے ذریعہ لین دین کو مسترد کردیا گیا۔ سب کو بھیجنے یا رقم کو کم کرنے کی کوشش کریں۔", "tx_rejected_dust_output": "لین دین کو نیٹ ورک کے قواعد ، کم آؤٹ پٹ رقم (دھول) کے ذریعہ مسترد کردیا گیا۔ براہ کرم رقم میں اضافہ کریں۔", "tx_rejected_dust_output_send_all": "لین دین کو نیٹ ورک کے قواعد ، کم آؤٹ پٹ رقم (دھول) کے ذریعہ مسترد کردیا گیا۔ براہ کرم سکے کے کنٹرول میں منتخب کردہ سکے کا توازن چیک کریں۔", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 6b268a8c10..20e415b264 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -746,8 +746,10 @@ "trusted": "A ti fọkàn ẹ̀ tán", "tx_commit_exception_no_dust_on_change": "Iṣowo naa ti kọ pẹlu iye yii. Pẹlu awọn owó wọnyi o le firanṣẹ ${min} laisi ayipada tabi ${max} ni iyipada iyipada.", "tx_commit_failed": "Idunadura iṣowo kuna. Jọwọ kan si atilẹyin.", + "tx_invalid_input": "O nlo iru titẹ nkan ti ko tọ fun iru isanwo yii", "tx_no_dust_exception": "Iṣowo naa ni kọ nipa fifiranṣẹ iye ti o kere ju. Jọwọ gbiyanju pọ si iye naa.", "tx_not_enough_inputs_exception": "Ko to awọn titẹsi to. Jọwọ yan diẹ sii labẹ iṣakoso owo", + "tx_rejected_bip68_final": "Iṣowo ni awọn igbewọle gbangba ati kuna lati rọpo nipasẹ owo.", "tx_rejected_dust_change": "Idunadura kọ nipasẹ awọn ofin nẹtiwọọki, iye iyipada kekere (eruku). Gbiyanju lati firanṣẹ gbogbo rẹ tabi dinku iye.", "tx_rejected_dust_output": "Idunadura kọ nipasẹ awọn ofin nẹtiwọọki, iye ti o wuwe kekere (eruku). Jọwọ mu iye naa pọ si.", "tx_rejected_dust_output_send_all": "Idunadura kọ nipasẹ awọn ofin nẹtiwọọki, iye ti o wuwe kekere (eruku). Jọwọ ṣayẹwo dọgbadọgba ti awọn owo ti a yan labẹ iṣakoso owo.", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index c847fc8b9d..c38ddbe764 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -745,8 +745,10 @@ "trusted": "值得信赖", "tx_commit_exception_no_dust_on_change": "交易被此金额拒绝。使用这些硬币,您可以发送${min}无需更改或返回${max}的变化。", "tx_commit_failed": "交易承诺失败。请联系支持。", + "tx_invalid_input": "您正在使用错误的输入类型进行此类付款", "tx_no_dust_exception": "通过发送太小的金额来拒绝交易。请尝试增加金额。", "tx_not_enough_inputs_exception": "没有足够的输入。请在硬币控制下选择更多", + "tx_rejected_bip68_final": "交易未确认投入,未能取代费用。", "tx_rejected_dust_change": "交易被网络规则拒绝,较低的变化数量(灰尘)。尝试发送全部或减少金额。", "tx_rejected_dust_output": "交易被网络规则,低输出量(灰尘)拒绝。请增加金额。", "tx_rejected_dust_output_send_all": "交易被网络规则,低输出量(灰尘)拒绝。请检查在硬币控制下选择的硬币的余额。", diff --git a/tool/configure.dart b/tool/configure.dart index 70a74123c0..4ac467811c 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -173,6 +173,7 @@ abstract class Bitcoin { Future rescan(Object wallet, {required int height, bool? doSingleScan}); bool getNodeIsCakeElectrs(Object wallet); void deleteSilentPaymentAddress(Object wallet, String address); + Future updateFeeRates(Object wallet); } """; From eb64a753bf6ec3da5d3b54a406beb391a0508903 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Wed, 17 Apr 2024 21:26:42 -0300 Subject: [PATCH 40/75] fix: scan re-subscription --- cw_bitcoin/lib/electrum.dart | 11 +++-- cw_bitcoin/lib/electrum_wallet.dart | 48 ++++++++++---------- lib/entities/default_settings_migration.dart | 4 +- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index e9383dda59..d7c35c6ee6 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -8,6 +8,8 @@ import 'package:cw_bitcoin/script_hash.dart'; import 'package:flutter/foundation.dart'; import 'package:rxdart/rxdart.dart'; +const int TWEAKS_COUNT = 20; + String jsonrpcparams(List params) { final _params = params.map((val) => '"${val.toString()}"').join(','); return '[$_params]'; @@ -281,9 +283,9 @@ class ElectrumClient { BehaviorSubject? tweaksSubscribe({required int height}) { _id += 1; return subscribe( - id: 'blockchain.tweaks.subscribe', + id: 'blockchain.tweaks.subscribe:${height + TWEAKS_COUNT}', method: 'blockchain.tweaks.subscribe', - params: [height], + params: [height, TWEAKS_COUNT], ); } @@ -467,11 +469,14 @@ class ElectrumClient { _tasks[id]?.subject?.add(params.last); break; - case 'blockchain.tweaks.subscribe': case 'blockchain.headers.subscribe': final params = request['params'] as List; _tasks[method]?.subject?.add(params.last); break; + case 'blockchain.tweaks.subscribe': + final params = request['params'] as List; + _tasks[_tasks.keys.first]?.subject?.add(params.last); + break; default: break; } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index b2962f1e0d..38d6c5fec6 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -156,7 +156,7 @@ abstract class ElectrumWalletBase if (active) { await _setInitialHeight(); - if (await currentChainTip > walletInfo.restoreHeight) { + if ((await getCurrentChainTip()) > walletInfo.restoreHeight) { _setListeners(walletInfo.restoreHeight, chainTipParam: _currentChainTip); } } else { @@ -176,9 +176,9 @@ abstract class ElectrumWalletBase @observable int? _currentChainTip; - @computed - Future get currentChainTip async => - _currentChainTip ?? await electrumClient.getCurrentBlockChainTip() ?? 0; + Future getCurrentChainTip() async { + return _currentChainTip ?? await electrumClient.getCurrentBlockChainTip() ?? 0; + } @override BitcoinWalletKeys get keys => @@ -208,7 +208,7 @@ abstract class ElectrumWalletBase @action Future _setListeners(int height, {int? chainTipParam, bool? doSingleScan}) async { - final chainTip = chainTipParam ?? await currentChainTip; + final chainTip = chainTipParam ?? await getCurrentChainTip(); syncStatus = AttemptingSyncStatus(); if (_isolate != null) { @@ -373,6 +373,12 @@ abstract class ElectrumWalletBase @override Future connectToNode({required Node node}) async { final differentNode = this.node?.uri != node.uri || this.node?.useSSL != node.useSSL; + + if (differentNode) { + _scripthashesUpdateSubject = {}; + _chainTipUpdateSubject = null; + } + this.node = node; try { @@ -383,6 +389,8 @@ abstract class ElectrumWalletBase await Timer(Duration(seconds: differentNode ? 0 : 10), () async { electrumClient.onConnectionStatusChange = (bool isConnected) async { + if (syncStatus is SyncingSyncStatus) return; + if (isConnected && syncStatus is! SyncedSyncStatus) { syncStatus = ConnectedSyncStatus(); } else if (!isConnected) { @@ -1410,7 +1418,7 @@ abstract class ElectrumWalletBase time = status["block_time"] as int?; final height = status["block_height"] as int? ?? 0; - final tip = await currentChainTip; + final tip = await getCurrentChainTip(); if (tip > 0) confirmations = height > 0 ? tip - height + 1 : 0; } else { final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash); @@ -1480,7 +1488,7 @@ abstract class ElectrumWalletBase final receiveAddresses = addressesByType.where((addr) => addr.isHidden == false); await Future.wait(addressesByType.map((addressRecord) async { - final history = await _fetchAddressHistory(addressRecord, await currentChainTip); + final history = await _fetchAddressHistory(addressRecord, await getCurrentChainTip()); if (history.isNotEmpty) { addressRecord.txCount = history.length; @@ -1502,7 +1510,7 @@ abstract class ElectrumWalletBase addressRecord.isHidden, (address) async { await _subscribeForUpdates(); - return _fetchAddressHistory(address, await currentChainTip) + return _fetchAddressHistory(address, await getCurrentChainTip()) .then((history) => history.isNotEmpty ? address.address : null); }, type: type, @@ -1575,7 +1583,7 @@ abstract class ElectrumWalletBase transactionHistory.transactions.values.forEach((tx) async { if (tx.unspents != null && tx.unspents!.isNotEmpty && tx.height > 0) { - tx.confirmations = await currentChainTip - tx.height + 1; + tx.confirmations = await getCurrentChainTip() - tx.height + 1; } }); @@ -1607,7 +1615,7 @@ abstract class ElectrumWalletBase balance[currency]?.confirmed += newBalance.confirmed; balance[currency]?.unconfirmed += newBalance.unconfirmed; - await _fetchAddressHistory(address, await currentChainTip); + await _fetchAddressHistory(address, await getCurrentChainTip()); } catch (e, s) { print(e.toString()); _onError?.call(FlutterErrorDetails( @@ -1853,23 +1861,13 @@ Future startRefresh(ScanData scanData) async { tweaksSubscription?.listen((t) { final tweaks = t as Map; - if (tweaks.isEmpty) { - syncHeight += 1; - scanData.sendPort.send( - SyncResponse( - syncHeight, - SyncingSyncStatus.fromHeightValues( - currentChainTip, - initialSyncHeight, - syncHeight, - ), - ), - ); - + if (tweaks["message"] != null) { + // re-subscribe to continue receiving messages + electrumClient.tweaksSubscribe(height: syncHeight); return; } - final blockHeight = tweaks.keys.first.toString(); + final blockHeight = tweaks.keys.first; try { final blockTweaks = tweaks[blockHeight] as Map; @@ -1963,7 +1961,7 @@ Future startRefresh(ScanData scanData) async { } } catch (_) {} - syncHeight += 1; + syncHeight = int.parse(blockHeight); scanData.sendPort.send( SyncResponse( syncHeight, diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 792fd6f894..0117200526 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -23,7 +23,7 @@ import 'package:encrypt/encrypt.dart' as encrypt; import 'package:collection/collection.dart'; const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081'; -const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002'; +const cakeWalletBitcoinElectrumUri = '198.58.111.154:50002'; const publicBitcoinTestnetElectrumAddress = '198.58.111.154'; const publicBitcoinTestnetElectrumPort = '50002'; const publicBitcoinTestnetElectrumUri = @@ -36,7 +36,7 @@ const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002'; const nanoDefaultNodeUri = 'rpc.nano.to'; const nanoDefaultPowNodeUri = 'rpc.nano.to'; const solanaDefaultNodeUri = 'rpc.ankr.com'; -const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002'; +const newCakeWalletBitcoinUri = '198.58.111.154:50002'; Future defaultSettingsMigration( {required int version, From 5b3128f7519586b23946a6861e432f357540ee58 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Wed, 17 Apr 2024 22:29:53 -0300 Subject: [PATCH 41/75] fix: default nodes --- lib/entities/default_settings_migration.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 0117200526..882cced1b6 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -23,7 +23,7 @@ import 'package:encrypt/encrypt.dart' as encrypt; import 'package:collection/collection.dart'; const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081'; -const cakeWalletBitcoinElectrumUri = '198.58.111.154:50002'; +const cakeWalletBitcoinElectrumUri = '198.58.111.154:50001'; const publicBitcoinTestnetElectrumAddress = '198.58.111.154'; const publicBitcoinTestnetElectrumPort = '50002'; const publicBitcoinTestnetElectrumUri = @@ -36,7 +36,7 @@ const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002'; const nanoDefaultNodeUri = 'rpc.nano.to'; const nanoDefaultPowNodeUri = 'rpc.nano.to'; const solanaDefaultNodeUri = 'rpc.ankr.com'; -const newCakeWalletBitcoinUri = '198.58.111.154:50002'; +const newCakeWalletBitcoinUri = '198.58.111.154:50001'; Future defaultSettingsMigration( {required int version, @@ -766,7 +766,8 @@ Future changeDefaultBitcoinNode( final needToReplaceCurrentBitcoinNode = currentBitcoinNode.uri.toString().contains(cakeWalletBitcoinNodeUriPattern); - final newCakeWalletBitcoinNode = Node(uri: newCakeWalletBitcoinUri, type: WalletType.bitcoin); + final newCakeWalletBitcoinNode = + Node(uri: newCakeWalletBitcoinUri, type: WalletType.bitcoin, useSSL: false); await nodeSource.add(newCakeWalletBitcoinNode); @@ -819,7 +820,7 @@ Future checkCurrentNodes( if (currentBitcoinElectrumServer == null) { final cakeWalletElectrum = - Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin, useSSL: true); + Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin, useSSL: false); await nodeSource.add(cakeWalletElectrum); await sharedPreferences.setInt( PreferencesKey.currentBitcoinElectrumSererIdKey, cakeWalletElectrum.key as int); @@ -892,7 +893,7 @@ Future resetBitcoinElectrumServer( if (cakeWalletNode == null) { cakeWalletNode = - Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin, useSSL: true); + Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin, useSSL: false); await nodeSource.add(cakeWalletNode); } From ca81d44e6c39feeba3c6bc5234450f8124a1fc57 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Thu, 18 Apr 2024 08:57:16 -0300 Subject: [PATCH 42/75] fix: improve scanning by date, fix single block scan --- cw_bitcoin/lib/electrum.dart | 8 +++----- cw_bitcoin/lib/electrum_wallet.dart | 26 ++++++++++++++------------ cw_core/lib/get_height_by_date.dart | 12 ++++++++---- lib/reactions/check_connection.dart | 21 +++++++++++---------- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index d7c35c6ee6..a76e9b7bab 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -8,8 +8,6 @@ import 'package:cw_bitcoin/script_hash.dart'; import 'package:flutter/foundation.dart'; import 'package:rxdart/rxdart.dart'; -const int TWEAKS_COUNT = 20; - String jsonrpcparams(List params) { final _params = params.map((val) => '"${val.toString()}"').join(','); return '[$_params]'; @@ -280,12 +278,12 @@ class ElectrumClient { Future> getHeader({required int height}) async => await call(method: 'blockchain.block.get_header', params: [height]) as Map; - BehaviorSubject? tweaksSubscribe({required int height}) { + BehaviorSubject? tweaksSubscribe({required int height, required int count}) { _id += 1; return subscribe( - id: 'blockchain.tweaks.subscribe:${height + TWEAKS_COUNT}', + id: 'blockchain.tweaks.subscribe:${height + count}', method: 'blockchain.tweaks.subscribe', - params: [height, TWEAKS_COUNT], + params: [height, count], ); } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 38d6c5fec6..4fa640afe6 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -48,6 +48,8 @@ part 'electrum_wallet.g.dart'; class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet; +const int TWEAKS_COUNT = 25; + abstract class ElectrumWalletBase extends WalletBase with Store { @@ -154,8 +156,6 @@ abstract class ElectrumWalletBase silentPaymentsScanningActive = active; if (active) { - await _setInitialHeight(); - if ((await getCurrentChainTip()) > walletInfo.restoreHeight) { _setListeners(walletInfo.restoreHeight, chainTipParam: _currentChainTip); } @@ -1855,19 +1855,22 @@ Future startRefresh(ScanData scanData) async { final electrumClient = await getElectrumConnection(); if (tweaksSubscription == null) { + final count = scanData.isSingleScan ? 1 : TWEAKS_COUNT; + try { - tweaksSubscription = await electrumClient.tweaksSubscribe(height: syncHeight); + tweaksSubscription = await electrumClient.tweaksSubscribe(height: syncHeight, count: count); - tweaksSubscription?.listen((t) { + tweaksSubscription?.listen((t) async { final tweaks = t as Map; - if (tweaks["message"] != null) { + if (tweaks["message"] != null && !scanData.isSingleScan) { // re-subscribe to continue receiving messages - electrumClient.tweaksSubscribe(height: syncHeight); + electrumClient.tweaksSubscribe(height: syncHeight, count: count); return; } final blockHeight = tweaks.keys.first; + final tweakHeight = int.parse(blockHeight); try { final blockTweaks = tweaks[blockHeight] as Map; @@ -1901,13 +1904,13 @@ Future startRefresh(ScanData scanData) async { final txInfo = ElectrumTransactionInfo( WalletType.bitcoin, id: txid, - height: syncHeight, + height: tweakHeight, amount: 0, fee: 0, direction: TransactionDirection.incoming, isPending: false, date: DateTime.now(), - confirmations: scanData.chainTip - int.parse(blockHeight) + 1, + confirmations: scanData.chainTip - tweakHeight + 1, unspents: [], ); @@ -1961,7 +1964,7 @@ Future startRefresh(ScanData scanData) async { } } catch (_) {} - syncHeight = int.parse(blockHeight); + syncHeight = tweakHeight; scanData.sendPort.send( SyncResponse( syncHeight, @@ -1973,11 +1976,10 @@ Future startRefresh(ScanData scanData) async { ), ); - if (int.parse(blockHeight) >= scanData.chainTip) { + if (int.parse(blockHeight) >= scanData.chainTip || scanData.isSingleScan) { scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus())); + await tweaksSubscription!.close(); } - - return; }); } catch (e) { if (e is RequestFailedTimeoutException) { diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index 1dc624a7ff..11bc370c5b 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -264,10 +264,14 @@ const bitcoinDates = { }; int getBitcoinHeightByDate({required DateTime date}) { - String closestKey = - bitcoinDates.keys.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => ''); + String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; + int startBlock = bitcoinDates[dateKey] ?? bitcoinDates.values.last; + + DateTime startOfMonth = DateTime(date.year, date.month); + int daysDifference = date.difference(startOfMonth).inDays; - final oldestHeight = bitcoinDates.values.last; + // approximately 6 blocks per hour, 24 hours per day + int estimatedBlocksSinceStartOfMonth = (daysDifference * 24 * 6); - return bitcoinDates[closestKey] ?? oldestHeight; + return startBlock + estimatedBlocksSinceStartOfMonth; } diff --git a/lib/reactions/check_connection.dart b/lib/reactions/check_connection.dart index 9185ffe152..3252797ddd 100644 --- a/lib/reactions/check_connection.dart +++ b/lib/reactions/check_connection.dart @@ -3,15 +3,19 @@ import 'dart:async'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/sync_status.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/store/settings_store.dart'; + Timer? _checkConnectionTimer; -void startCheckConnectionReaction( - WalletBase wallet, SettingsStore settingsStore, +void startCheckConnectionReaction(WalletBase wallet, SettingsStore settingsStore, {int timeInterval = 5}) { _checkConnectionTimer?.cancel(); - _checkConnectionTimer = - Timer.periodic(Duration(seconds: timeInterval), (_) async { + _checkConnectionTimer = Timer.periodic(Duration(seconds: timeInterval), (_) async { + if (wallet.type == WalletType.bitcoin && wallet.syncStatus is SyncingSyncStatus) { + return; + } + try { final connectivityResult = await (Connectivity().checkConnectivity()); @@ -20,14 +24,11 @@ void startCheckConnectionReaction( return; } - if (wallet.syncStatus is LostConnectionSyncStatus || - wallet.syncStatus is FailedSyncStatus) { - final alive = - await settingsStore.getCurrentNode(wallet.type).requestNode(); + if (wallet.syncStatus is LostConnectionSyncStatus || wallet.syncStatus is FailedSyncStatus) { + final alive = await settingsStore.getCurrentNode(wallet.type).requestNode(); if (alive) { - await wallet.connectToNode( - node: settingsStore.getCurrentNode(wallet.type)); + await wallet.connectToNode(node: settingsStore.getCurrentNode(wallet.type)); } } } catch (e) { From 67256d30e6844e099b3be205c9f2a6e99994a59d Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Thu, 18 Apr 2024 10:38:18 -0300 Subject: [PATCH 43/75] refactor: common function for input tx selection --- cw_bitcoin/lib/electrum_wallet.dart | 353 +++++++++-------------- lib/view_model/send/send_view_model.dart | 4 + res/values/strings_ar.arb | 3 +- res/values/strings_bg.arb | 3 +- res/values/strings_cs.arb | 3 +- res/values/strings_de.arb | 3 +- res/values/strings_en.arb | 3 +- res/values/strings_es.arb | 3 +- res/values/strings_fr.arb | 3 +- res/values/strings_ha.arb | 3 +- res/values/strings_hi.arb | 3 +- res/values/strings_hr.arb | 3 +- res/values/strings_id.arb | 3 +- res/values/strings_it.arb | 3 +- res/values/strings_ja.arb | 3 +- res/values/strings_ko.arb | 3 +- res/values/strings_my.arb | 3 +- res/values/strings_nl.arb | 3 +- res/values/strings_pl.arb | 3 +- res/values/strings_pt.arb | 3 +- res/values/strings_ru.arb | 3 +- res/values/strings_th.arb | 3 +- res/values/strings_tl.arb | 3 +- res/values/strings_tr.arb | 3 +- res/values/strings_uk.arb | 3 +- res/values/strings_ur.arb | 3 +- res/values/strings_yo.arb | 3 +- res/values/strings_zh.arb | 3 +- 28 files changed, 200 insertions(+), 235 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 4fa640afe6..b171d08d64 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -352,6 +352,8 @@ abstract class ElectrumWalletBase updateAllUnspents().then(checkFinishedAllUpdates); updateBalance().then(checkFinishedAllUpdates); + + updateFeeRates(); } catch (e, stacktrace) { print(stacktrace); print(e.toString()); @@ -410,128 +412,87 @@ abstract class ElectrumWalletBase bool _isBelowDust(int amount) => amount <= _dustAmount && network != BitcoinNetwork.testnet; - void _createSilentPayments( - List outputs, - List inputPubKeys, - List vinOutpoints, - List inputPrivKeyInfos, - ) { - List silentPaymentDestinations = []; - - for (final out in outputs) { - final address = out.address; - final amount = out.value; - - if (address is SilentPaymentAddress) { - silentPaymentDestinations.add( - SilentPaymentDestination.fromAddress(address.toAddress(network), amount.toInt()), - ); - } - } - - if (silentPaymentDestinations.isNotEmpty) { - final spb = SilentPaymentBuilder(pubkeys: inputPubKeys, outpoints: vinOutpoints); - final sendingOutputs = spb.createOutputs(inputPrivKeyInfos, silentPaymentDestinations); - - final outputsAdded = []; - - for (var i = 0; i < outputs.length; i++) { - final out = outputs[i]; - - final silentOutputs = sendingOutputs[out.address.toAddress(network)]; - if (silentOutputs != null) { - final silentOutput = - silentOutputs.firstWhereOrNull((element) => !outputsAdded.contains(element)); - - if (silentOutput != null) { - outputs[i] = BitcoinOutput( - address: silentOutput.address, - value: BigInt.from(silentOutput.amount), - ); - - outputsAdded.add(silentOutput); - } - } - } - } - } - - Future estimateSendAllTx( - List outputs, - int feeRate, { - String? memo, - int credentialsAmount = 0, - bool hasSilentPayment = false, - }) async { - final utxos = []; - int allInputsAmount = 0; - + UtxoDetails _createUTXOS({ + required bool sendAll, + required int credentialsAmount, + required bool paysToSilentPayment, + int? inputsCount, + }) { + List utxos = []; List vinOutpoints = []; List inputPrivKeyInfos = []; - List inputPubKeys = []; + int allInputsAmount = 0; bool spendsSilentPayment = false; bool spendsCPFP = false; - for (int i = 0; i < unspentCoins.length; i++) { - final utx = unspentCoins[i]; + int leftAmount = credentialsAmount; + final availableInputs = unspentCoins.where((utx) => utx.isSending && !utx.isFrozen).toList(); - if (utx.isSending && !utx.isFrozen) { - if (hasSilentPayment) { - // Check inputs for shared secret derivation - if (utx.bitcoinAddressRecord.type == SegwitAddresType.p2wsh) { - throw BitcoinTransactionSilentPaymentsNotSupported(); - } - } + for (int i = 0; i < availableInputs.length; i++) { + final utx = availableInputs[i]; - if (!spendsCPFP) spendsCPFP = utx.confirmations == 0; + if (paysToSilentPayment) { + // Check inputs for shared secret derivation + if (utx.bitcoinAddressRecord.type == SegwitAddresType.p2wsh) { + throw BitcoinTransactionSilentPaymentsNotSupported(); + } + } - allInputsAmount += utx.value; + spendsCPFP = (utx.confirmations ?? 0) <= 0; - final address = addressTypeFromStr(utx.address, network); + allInputsAmount += utx.value; + leftAmount = leftAmount - utx.value; - ECPrivate? privkey; - bool? isSilentPayment = false; + final address = addressTypeFromStr(utx.address, network); + ECPrivate? privkey; + bool? isSilentPayment = false; - if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { - final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord; - privkey = walletAddresses.silentAddress!.b_spend.tweakAdd( - BigintUtils.fromBytes( - BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!), - ), - ); - spendsSilentPayment = true; - isSilentPayment = true; - } else { - privkey = generateECPrivate( - hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd, - index: utx.bitcoinAddressRecord.index, - network: network, - ); - } + if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { + final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord; + privkey = walletAddresses.silentAddress!.b_spend.tweakAdd( + BigintUtils.fromBytes( + BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!), + ), + ); + spendsSilentPayment = true; + isSilentPayment = true; + } else { + privkey = generateECPrivate( + hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd, + index: utx.bitcoinAddressRecord.index, + network: network, + ); + } - inputPrivKeyInfos.add(ECPrivateInfo( - privkey, - address.type == SegwitAddresType.p2tr, - tweak: !isSilentPayment, - )); - inputPubKeys.add(privkey.getPublic()); - vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); + vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); + inputPrivKeyInfos.add(ECPrivateInfo( + privkey, + address.type == SegwitAddresType.p2tr, + tweak: !isSilentPayment, + )); - utxos.add( - UtxoWithAddress( - utxo: BitcoinUtxo( - txHash: utx.hash, - value: BigInt.from(utx.value), - vout: utx.vout, - scriptType: _getScriptType(address), - isSilentPayment: isSilentPayment, - ), - ownerDetails: UtxoAddressDetails( - publicKey: privkey.getPublic().toHex(), - address: address, - ), + utxos.add( + UtxoWithAddress( + utxo: BitcoinUtxo( + txHash: utx.hash, + value: BigInt.from(utx.value), + vout: utx.vout, + scriptType: _getScriptType(address), + isSilentPayment: isSilentPayment, ), - ); + ownerDetails: UtxoAddressDetails( + publicKey: privkey.getPublic().toHex(), + address: address, + ), + ), + ); + + // sendAll continues for all inputs + if (!sendAll) { + bool amountIsAcquired = leftAmount <= 0; + if ((inputsCount == null && amountIsAcquired) || inputsCount == i + 1) { + break; + } } } @@ -539,24 +500,46 @@ abstract class ElectrumWalletBase throw BitcoinTransactionNoInputsException(); } - if (hasSilentPayment == true) { - _createSilentPayments(outputs, inputPubKeys, vinOutpoints, inputPrivKeyInfos); - } + return UtxoDetails( + availableInputs: availableInputs, + utxos: utxos, + vinOutpoints: vinOutpoints, + inputPrivKeyInfos: inputPrivKeyInfos, + allInputsAmount: allInputsAmount, + spendsSilentPayment: spendsSilentPayment, + spendsCPFP: spendsCPFP, + ); + } + + Future estimateSendAllTx( + List outputs, + int feeRate, { + String? memo, + int credentialsAmount = 0, + bool hasSilentPayment = false, + }) async { + final utxoDetails = _createUTXOS( + sendAll: true, + credentialsAmount: credentialsAmount, + paysToSilentPayment: hasSilentPayment, + ); int estimatedSize; if (network is BitcoinCashNetwork) { estimatedSize = ForkedTransactionBuilder.estimateTransactionSize( - utxos: utxos, + utxos: utxoDetails.utxos, outputs: outputs, network: network as BitcoinCashNetwork, memo: memo, ); } else { estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize( - utxos: utxos, + utxos: utxoDetails.utxos, outputs: outputs, network: network, memo: memo, + inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos, + vinOutpoints: utxoDetails.vinOutpoints, ); } @@ -567,10 +550,10 @@ abstract class ElectrumWalletBase } // Here, when sending all, the output amount equals to the input value - fee to fully spend every input on the transaction and have no amount left for change - int amount = allInputsAmount - fee; + int amount = utxoDetails.allInputsAmount - fee; if (amount <= 0) { - throw BitcoinTransactionWrongBalanceException(); + throw BitcoinTransactionWrongBalanceException(amount: utxoDetails.allInputsAmount + fee); } // Attempting to send less than the dust limit @@ -590,15 +573,15 @@ abstract class ElectrumWalletBase BitcoinOutput(address: outputs.last.address, value: BigInt.from(amount)); return EstimatedTxResult( - utxos: utxos, - inputPrivKeyInfos: inputPrivKeyInfos, + utxos: utxoDetails.utxos, + inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos, fee: fee, amount: amount, isSendAll: true, hasChange: false, memo: memo, - spendsSilentPayment: spendsSilentPayment, - spendsCPFP: spendsCPFP, + spendsSilentPayment: utxoDetails.spendsSilentPayment, + spendsCPFP: utxoDetails.spendsCPFP, ); } @@ -610,87 +593,17 @@ abstract class ElectrumWalletBase String? memo, bool hasSilentPayment = false, }) async { - final utxos = []; - List vinOutpoints = []; - List inputPrivKeyInfos = []; - List inputPubKeys = []; - int allInputsAmount = 0; - bool spendsSilentPayment = false; - bool spendsCPFP = false; - - int leftAmount = credentialsAmount; - final sendingCoins = unspentCoins.where((utx) => utx.isSending).toList(); - - for (int i = 0; i < sendingCoins.length; i++) { - final utx = sendingCoins[i]; - spendsCPFP = utx.confirmations == 0; - - allInputsAmount += utx.value; - leftAmount = leftAmount - utx.value; - - final address = addressTypeFromStr(utx.address, network); - ECPrivate? privkey; - bool? isSilentPayment = false; - - if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) { - final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord; - privkey = walletAddresses.silentAddress!.b_spend.tweakAdd( - BigintUtils.fromBytes( - BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!), - ), - ); - spendsSilentPayment = true; - isSilentPayment = true; - } else { - privkey = generateECPrivate( - hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd, - index: utx.bitcoinAddressRecord.index, - network: network, - ); - } - - inputPrivKeyInfos.add(ECPrivateInfo( - privkey, - address.type == SegwitAddresType.p2tr, - tweak: !isSilentPayment, - )); - inputPubKeys.add(privkey.getPublic()); - vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout)); - - utxos.add( - UtxoWithAddress( - utxo: BitcoinUtxo( - txHash: utx.hash, - value: BigInt.from(utx.value), - vout: utx.vout, - scriptType: _getScriptType(address), - isSilentPayment: isSilentPayment, - ), - ownerDetails: UtxoAddressDetails( - publicKey: privkey.getPublic().toHex(), - address: address, - ), - ), - ); - - bool amountIsAcquired = leftAmount <= 0; - if ((inputsCount == null && amountIsAcquired) || inputsCount == i + 1) { - break; - } - } - - if (utxos.isEmpty) { - throw BitcoinTransactionNoInputsException(); - } - - if (hasSilentPayment == true) { - _createSilentPayments(outputs, inputPubKeys, vinOutpoints, inputPrivKeyInfos); - } + final utxoDetails = _createUTXOS( + sendAll: false, + credentialsAmount: credentialsAmount, + inputsCount: inputsCount, + paysToSilentPayment: hasSilentPayment, + ); - final spendingAllCoins = sendingCoins.length == utxos.length; + final spendingAllCoins = utxoDetails.availableInputs.length == utxoDetails.utxos.length; // How much is being spent - how much is being sent - int amountLeftForChangeAndFee = allInputsAmount - credentialsAmount; + int amountLeftForChangeAndFee = utxoDetails.allInputsAmount - credentialsAmount; if (amountLeftForChangeAndFee <= 0) { if (!spendingAllCoins) { @@ -698,11 +611,12 @@ abstract class ElectrumWalletBase credentialsAmount, outputs, feeRate, - inputsCount: utxos.length + 1, + inputsCount: utxoDetails.utxos.length + 1, memo: memo, hasSilentPayment: hasSilentPayment, ); } + throw BitcoinTransactionWrongBalanceException(); } @@ -716,17 +630,19 @@ abstract class ElectrumWalletBase int estimatedSize; if (network is BitcoinCashNetwork) { estimatedSize = ForkedTransactionBuilder.estimateTransactionSize( - utxos: utxos, + utxos: utxoDetails.utxos, outputs: outputs, network: network as BitcoinCashNetwork, memo: memo, ); } else { estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize( - utxos: utxos, + utxos: utxoDetails.utxos, outputs: outputs, network: network, memo: memo, + inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos, + vinOutpoints: utxoDetails.vinOutpoints, ); } @@ -755,7 +671,7 @@ abstract class ElectrumWalletBase credentialsAmount, outputs, feeRate, - inputsCount: utxos.length + 1, + inputsCount: utxoDetails.utxos.length + 1, memo: memo, ); } @@ -771,7 +687,7 @@ abstract class ElectrumWalletBase } // Estimate to user how much is needed to send to cover the fee - final maxAmountWithReturningChange = allInputsAmount - _dustAmount - fee - 1; + final maxAmountWithReturningChange = utxoDetails.allInputsAmount - _dustAmount - fee - 1; throw BitcoinTransactionNoDustOnChangeException( bitcoinAmountToString(amount: maxAmountWithReturningChange), bitcoinAmountToString(amount: estimatedSendAll.amount), @@ -789,7 +705,7 @@ abstract class ElectrumWalletBase throw BitcoinTransactionWrongBalanceException(); } - if (totalAmount > allInputsAmount) { + if (totalAmount > utxoDetails.allInputsAmount) { if (spendingAllCoins) { throw BitcoinTransactionWrongBalanceException(); } else { @@ -797,12 +713,11 @@ abstract class ElectrumWalletBase outputs.removeLast(); } - outputs.removeLast(); return estimateTxForAmount( credentialsAmount, outputs, feeRate, - inputsCount: utxos.length + 1, + inputsCount: utxoDetails.utxos.length + 1, memo: memo, hasSilentPayment: hasSilentPayment, ); @@ -810,15 +725,15 @@ abstract class ElectrumWalletBase } return EstimatedTxResult( - utxos: utxos, - inputPrivKeyInfos: inputPrivKeyInfos, + utxos: utxoDetails.utxos, + inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos, fee: fee, amount: amount, hasChange: true, isSendAll: false, memo: memo, - spendsSilentPayment: spendsSilentPayment, - spendsCPFP: spendsCPFP, + spendsSilentPayment: utxoDetails.spendsSilentPayment, + spendsCPFP: utxoDetails.spendsCPFP, ); } @@ -2067,3 +1982,23 @@ BitcoinAddressType _getScriptType(BitcoinBaseAddress type) { return SegwitAddresType.p2wpkh; } } + +class UtxoDetails { + final List availableInputs; + final List utxos; + final List vinOutpoints; + final List inputPrivKeyInfos; + final int allInputsAmount; + final bool spendsSilentPayment; + final bool spendsCPFP; + + UtxoDetails({ + required this.availableInputs, + required this.utxos, + required this.vinOutpoints, + required this.inputPrivKeyInfos, + required this.allInputsAmount, + required this.spendsSilentPayment, + required this.spendsCPFP, + }); +} diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 0bea0c59bf..3cc379a282 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -541,6 +541,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor walletType == WalletType.litecoin || walletType == WalletType.bitcoinCash) { if (error is TransactionWrongBalanceException) { + if (error.amount != null) + return S.current + .tx_wrong_balance_with_amount_exception(currency.toString(), error.amount.toString()); + return S.current.tx_wrong_balance_exception(currency.toString()); } if (error is TransactionNoInputsException) { diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 7fd44c588f..eaf39c7faa 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "المعاملة التي يتم رفضها بموجب قواعد الشبكة ، وكمية الإخراج المنخفض (الغبار). يرجى التحقق من رصيد العملات المعدنية المحددة تحت التحكم في العملة.", "tx_rejected_vout_negative": "لا يوجد ما يكفي من الرصيد لدفع رسوم هذه الصفقة. يرجى التحقق من رصيد العملات المعدنية تحت السيطرة على العملة.", "tx_wrong_balance_exception": "ليس لديك ما يكفي من ${currency} لإرسال هذا المبلغ.", + "tx_wrong_balance_with_amount_exception": "ليس لديك ما يكفي ${currency} لإرسال المبلغ الإجمالي ${amount}", "tx_zero_fee_exception": "لا يمكن إرسال معاملة مع 0 رسوم. حاول زيادة المعدل أو التحقق من اتصالك للحصول على أحدث التقديرات.", "unavailable_balance": "ﺮﻓﻮﺘﻣ ﺮﻴﻏ ﺪﻴﺻﺭ", "unavailable_balance_description": ".ﺎﻫﺪﻴﻤﺠﺗ ءﺎﻐﻟﺇ ﺭﺮﻘﺗ ﻰﺘﺣ ﺕﻼﻣﺎﻌﻤﻠﻟ ﻝﻮﺻﻮﻠﻟ ﺔﻠﺑﺎﻗ ﺮﻴﻏ ﺓﺪﻤﺠﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﻞﻈﺗ ﺎﻤﻨﻴﺑ ،ﺎﻬﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻣﺎﻌﻤﻟﺍ ﻝﺎﻤﺘﻛﺍ ﺩﺮﺠﻤﺑ ﺔﺣﺎﺘﻣ ﺔﻠﻔﻘﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﺢﺒﺼﺘﺳ .ﻚﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻤﻌﻟﺍ ﻲﻓ ﻢﻜﺤﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻂﺸﻧ ﻞﻜﺸﺑ ﺎﻫﺪﻴﻤﺠﺘﺑ ﺖﻤﻗ", @@ -837,4 +838,4 @@ "you_will_get": "حول الى", "you_will_send": "تحويل من", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 6acbfd6646..17795c1ccb 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "Транзакция, отхвърлена от мрежови правила, ниска стойност на изхода (прах). Моля, проверете баланса на монетите, избрани под контрол на монети.", "tx_rejected_vout_negative": "Няма достатъчно баланс, за да платите за таксите на тази транзакция. Моля, проверете баланса на монетите под контрол на монетите.", "tx_wrong_balance_exception": "Нямате достатъчно ${currency}, за да изпратите тази сума.", + "tx_wrong_balance_with_amount_exception": "Нямате достатъчно ${currency} За да изпратите общата сума на ${amount}", "tx_zero_fee_exception": "Не може да изпраща транзакция с 0 такса. Опитайте да увеличите скоростта или да проверите връзката си за най -новите оценки.", "unavailable_balance": "Неналично салдо", "unavailable_balance_description": "Неналично салдо: Тази обща сума включва средства, които са заключени в чакащи транзакции и тези, които сте замразили активно в настройките за контрол на монетите. Заключените баланси ще станат достъпни, след като съответните им транзакции бъдат завършени, докато замразените баланси остават недостъпни за транзакции, докато не решите да ги размразите.", @@ -837,4 +838,4 @@ "you_will_get": "Обръщане в", "you_will_send": "Обръщане от", "yy": "гг" -} \ No newline at end of file +} diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 9bd20e6606..902f506930 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "Transakce zamítnuta síťovými pravidly, nízkým množstvím výstupu (prach). Zkontrolujte prosím zůstatek mincí vybraných pod kontrolou mincí.", "tx_rejected_vout_negative": "Nedostatek zůstatek na zaplacení poplatků za tuto transakci. Zkontrolujte prosím zůstatek mincí pod kontrolou mincí.", "tx_wrong_balance_exception": "Nemáte dost ${currency} pro odeslání této částky.", + "tx_wrong_balance_with_amount_exception": "Nemáte dost ${currency} na odeslání celkové částky ${amount}", "tx_zero_fee_exception": "Nelze odeslat transakci s 0 poplatkem. Zkuste zvýšit sazbu nebo zkontrolovat připojení pro nejnovější odhady.", "unavailable_balance": "Nedostupný zůstatek", "unavailable_balance_description": "Nedostupný zůstatek: Tento součet zahrnuje prostředky, které jsou uzamčeny v nevyřízených transakcích a ty, které jste aktivně zmrazili v nastavení kontroly mincí. Uzamčené zůstatky budou k dispozici po dokončení příslušných transakcí, zatímco zmrazené zůstatky zůstanou pro transakce nepřístupné, dokud se nerozhodnete je uvolnit.", @@ -837,4 +838,4 @@ "you_will_get": "Směnit na", "you_will_send": "Směnit z", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 2ab9a4b62c..4c424264c6 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -757,6 +757,7 @@ "tx_rejected_dust_output_send_all": "Transaktion durch Netzwerkregeln, niedriger Ausgangsmenge (Staub) abgelehnt. Bitte überprüfen Sie den Gleichgewicht der unter Münzkontrolle ausgewählten Münzen.", "tx_rejected_vout_negative": "Nicht genug Guthaben, um die Gebühren dieser Transaktion zu bezahlen. Bitte überprüfen Sie den Restbetrag der Münzen unter Münzkontrolle.", "tx_wrong_balance_exception": "Sie haben nicht genug ${currency}, um diesen Betrag zu senden.", + "tx_wrong_balance_with_amount_exception": "Sie haben nicht genug ${currency}, um die Gesamtmenge von ${amount} zu senden", "tx_zero_fee_exception": "Transaktion kann nicht mit 0 Gebühren gesendet werden. Versuchen Sie, die Rate zu erhöhen oder Ihre Verbindung auf die neuesten Schätzungen zu überprüfen.", "unavailable_balance": "Nicht verfügbares Guthaben", "unavailable_balance_description": "Nicht verfügbares Guthaben: Diese Summe umfasst Gelder, die in ausstehenden Transaktionen gesperrt sind, und solche, die Sie in Ihren Münzkontrolleinstellungen aktiv eingefroren haben. Gesperrte Guthaben werden verfügbar, sobald die entsprechenden Transaktionen abgeschlossen sind, während eingefrorene Guthaben für Transaktionen nicht zugänglich bleiben, bis Sie sich dazu entschließen, sie wieder freizugeben.", @@ -840,4 +841,4 @@ "you_will_get": "Konvertieren zu", "you_will_send": "Konvertieren von", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 53c892b0d4..491264ba2f 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "Transaction rejected by network rules, low output amount (dust). Please check the balance of coins selected under Coin Control.", "tx_rejected_vout_negative": "Not enough balance to pay for this transaction's fees. Please check the balance of coins under Coin Control.", "tx_wrong_balance_exception": "You do not have enough ${currency} to send this amount.", + "tx_wrong_balance_with_amount_exception": "You do not have enough ${currency} to send the total amount of ${amount}", "tx_zero_fee_exception": "Cannot send transaction with 0 fee. Try increasing the rate or checking your connection for latest estimates.", "unavailable_balance": "Unavailable balance", "unavailable_balance_description": "Unavailable Balance: This total includes funds that are locked in pending transactions and those you have actively frozen in your coin control settings. Locked balances will become available once their respective transactions are completed, while frozen balances remain inaccessible for transactions until you decide to unfreeze them.", @@ -837,4 +838,4 @@ "you_will_get": "Convert to", "you_will_send": "Convert from", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 1f6fe23977..0752b8ce1a 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -757,6 +757,7 @@ "tx_rejected_dust_output_send_all": "Transacción rechazada por reglas de red, baja cantidad de salida (polvo). Verifique el saldo de monedas seleccionadas bajo control de monedas.", "tx_rejected_vout_negative": "No es suficiente saldo para pagar las tarifas de esta transacción. Verifique el saldo de monedas bajo control de monedas.", "tx_wrong_balance_exception": "No tiene suficiente ${currency} para enviar esta cantidad.", + "tx_wrong_balance_with_amount_exception": "No tiene suficiente ${currency} para enviar la cantidad total de ${amount}", "tx_zero_fee_exception": "No se puede enviar transacciones con 0 tarifa. Intente aumentar la tasa o verificar su conexión para las últimas estimaciones.", "unavailable_balance": "Saldo no disponible", "unavailable_balance_description": "Saldo no disponible: este total incluye fondos que están bloqueados en transacciones pendientes y aquellos que usted ha congelado activamente en su configuración de control de monedas. Los saldos bloqueados estarán disponibles una vez que se completen sus respectivas transacciones, mientras que los saldos congelados permanecerán inaccesibles para las transacciones hasta que usted decida descongelarlos.", @@ -838,4 +839,4 @@ "you_will_get": "Convertir a", "you_will_send": "Convertir de", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 060bf33090..b65347ad1f 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "Transaction rejetée par les règles du réseau, faible quantité de sortie (poussière). Veuillez vérifier le solde des pièces sélectionnées sous le contrôle des pièces de monnaie.", "tx_rejected_vout_negative": "Pas assez de solde pour payer les frais de cette transaction. Veuillez vérifier le solde des pièces sous le contrôle des pièces.", "tx_wrong_balance_exception": "Vous n'avez pas assez ${currency} pour envoyer ce montant.", + "tx_wrong_balance_with_amount_exception": "Vous n'avez pas assez ${currency} pour envoyer le montant total de ${amount}", "tx_zero_fee_exception": "Impossible d'envoyer une transaction avec 0 frais. Essayez d'augmenter le taux ou de vérifier votre connexion pour les dernières estimations.", "unavailable_balance": "Solde indisponible", "unavailable_balance_description": "Solde indisponible : ce total comprend les fonds bloqués dans les transactions en attente et ceux que vous avez activement gelés dans vos paramètres de contrôle des pièces. Les soldes bloqués deviendront disponibles une fois leurs transactions respectives terminées, tandis que les soldes gelés resteront inaccessibles aux transactions jusqu'à ce que vous décidiez de les débloquer.", @@ -837,4 +838,4 @@ "you_will_get": "Convertir vers", "you_will_send": "Convertir depuis", "yy": "AA" -} \ No newline at end of file +} diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 28e4747abf..a663139505 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -758,6 +758,7 @@ "tx_rejected_dust_output_send_all": "Ma'adar da aka ƙi ta dokokin cibiyar sadarwa, ƙananan fitarwa (ƙura). Da fatan za a duba daidaiton tsabar kudi a ƙarƙashin ikon tsabar kudin.", "tx_rejected_vout_negative": "Bai isa daidai ba don biyan wannan kudin ma'amala. Da fatan za a duba daidaiton tsabar kudi a ƙarƙashin ikon tsabar kudin.", "tx_wrong_balance_exception": "Ba ku da isasshen ${currency} don aika wannan adadin.", + "tx_wrong_balance_with_amount_exception": "Ba ku da isasshen ${currency} don aika jimlar adadin ${amount}", "tx_zero_fee_exception": "Ba zai iya aika ma'amala da kuɗi 0 ba. Gwada ƙara ƙimar ko bincika haɗin ku don mahimmin ƙididdiga.", "unavailable_balance": "Ma'aunin da ba ya samuwa", "unavailable_balance_description": "Ma'auni Babu: Wannan jimlar ya haɗa da kuɗi waɗanda ke kulle a cikin ma'amaloli da ke jiran aiki da waɗanda kuka daskare sosai a cikin saitunan sarrafa kuɗin ku. Ma'auni da aka kulle za su kasance da zarar an kammala ma'amalolinsu, yayin da daskararrun ma'auni ba za su iya samun damar yin ciniki ba har sai kun yanke shawarar cire su.", @@ -839,4 +840,4 @@ "you_will_get": "Maida zuwa", "you_will_send": "Maida daga", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index b92eed8370..df9e42b277 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -758,6 +758,7 @@ "tx_rejected_dust_output_send_all": "नेटवर्क नियमों, कम आउटपुट राशि (धूल) द्वारा खारिज किए गए लेनदेन। कृपया सिक्का नियंत्रण के तहत चुने गए सिक्कों के संतुलन की जाँच करें।", "tx_rejected_vout_negative": "इस लेनदेन की फीस के लिए भुगतान करने के लिए पर्याप्त शेष राशि नहीं है। कृपया सिक्के नियंत्रण के तहत सिक्कों के संतुलन की जाँच करें।", "tx_wrong_balance_exception": "इस राशि को भेजने के लिए आपके पास पर्याप्त ${currency} नहीं है।", + "tx_wrong_balance_with_amount_exception": "आपके पास पर्याप्त नहीं है${currency} ${amount} की कुल राशि भेजने के लिए", "tx_zero_fee_exception": "0 शुल्क के साथ लेनदेन नहीं भेज सकते। नवीनतम अनुमानों के लिए दर बढ़ाने या अपने कनेक्शन की जांच करने का प्रयास करें।", "unavailable_balance": "अनुपलब्ध शेष", "unavailable_balance_description": "अनुपलब्ध शेष राशि: इस कुल में वे धनराशि शामिल हैं जो लंबित लेनदेन में बंद हैं और जिन्हें आपने अपनी सिक्का नियंत्रण सेटिंग्स में सक्रिय रूप से जमा कर रखा है। लॉक किए गए शेष उनके संबंधित लेन-देन पूरे होने के बाद उपलब्ध हो जाएंगे, जबकि जमे हुए शेष लेन-देन के लिए अप्राप्य रहेंगे जब तक कि आप उन्हें अनफ्रीज करने का निर्णय नहीं लेते।", @@ -839,4 +840,4 @@ "you_will_get": "में बदलें", "you_will_send": "से रूपांतरित करें", "yy": "वाईवाई" -} \ No newline at end of file +} diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index b4200861d2..a6a1c976d1 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "Transakcija odbijena mrežnim pravilima, niska količina izlaza (prašina). Molimo provjerite ravnotežu kovanica odabranih pod kontrolom novčića.", "tx_rejected_vout_negative": "Nema dovoljno salda za plaćanje naknada ove transakcije. Molimo provjerite ravnotežu kovanica pod kontrolom novčića.", "tx_wrong_balance_exception": "Nemate dovoljno ${currency} da biste poslali ovaj iznos.", + "tx_wrong_balance_with_amount_exception": "Nemate dovoljno ${currency} za slanje ukupne količine ${amount}", "tx_zero_fee_exception": "Ne mogu poslati transakciju s 0 naknade. Pokušajte povećati stopu ili provjeriti vezu za najnovije procjene.", "unavailable_balance": "Nedostupno stanje", "unavailable_balance_description": "Nedostupno stanje: Ovaj ukupni iznos uključuje sredstva koja su zaključana u transakcijama na čekanju i ona koja ste aktivno zamrznuli u postavkama kontrole novčića. Zaključani saldi postat će dostupni kada se dovrše njihove transakcije, dok zamrznuti saldi ostaju nedostupni za transakcije sve dok ih ne odlučite odmrznuti.", @@ -837,4 +838,4 @@ "you_will_get": "Razmijeni u", "you_will_send": "Razmijeni iz", "yy": "GG" -} \ No newline at end of file +} diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 43ab36319a..3cd9cc7600 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -759,6 +759,7 @@ "tx_rejected_dust_output_send_all": "Transaksi ditolak oleh aturan jaringan, jumlah output rendah (debu). Silakan periksa saldo koin yang dipilih di bawah kontrol koin.", "tx_rejected_vout_negative": "Tidak cukup saldo untuk membayar biaya transaksi ini. Silakan periksa saldo koin di bawah kendali koin.", "tx_wrong_balance_exception": "Anda tidak memiliki cukup ${currency} untuk mengirim jumlah ini.", + "tx_wrong_balance_with_amount_exception": "Anda tidak memiliki cukup ${currency} untuk mengirim jumlah total ${amount}", "tx_zero_fee_exception": "Tidak dapat mengirim transaksi dengan biaya 0. Coba tingkatkan tarif atau periksa koneksi Anda untuk perkiraan terbaru.", "unavailable_balance": "Saldo tidak tersedia", "unavailable_balance_description": "Saldo Tidak Tersedia: Total ini termasuk dana yang terkunci dalam transaksi yang tertunda dan dana yang telah Anda bekukan secara aktif di pengaturan kontrol koin Anda. Saldo yang terkunci akan tersedia setelah transaksi masing-masing selesai, sedangkan saldo yang dibekukan tetap tidak dapat diakses untuk transaksi sampai Anda memutuskan untuk mencairkannya.", @@ -840,4 +841,4 @@ "you_will_get": "Konversi ke", "you_will_send": "Konversi dari", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 0dfd3ef47b..bd8764cae8 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -758,6 +758,7 @@ "tx_rejected_dust_output_send_all": "Transazione respinta dalle regole di rete, bassa quantità di output (polvere). Si prega di controllare il saldo delle monete selezionate sotto controllo delle monete.", "tx_rejected_vout_negative": "Non abbastanza saldo per pagare le commissioni di questa transazione. Si prega di controllare il saldo delle monete sotto controllo delle monete.", "tx_wrong_balance_exception": "Non hai abbastanza ${currency} per inviare questo importo.", + "tx_wrong_balance_with_amount_exception": "Non hai abbastanza ${currency} per inviare la quantità totale di ${amount}", "tx_zero_fee_exception": "Impossibile inviare transazioni con 0 tassa. Prova ad aumentare la tariffa o controlla la connessione per le ultime stime.", "unavailable_balance": "Saldo non disponibile", "unavailable_balance_description": "Saldo non disponibile: questo totale include i fondi bloccati nelle transazioni in sospeso e quelli che hai congelato attivamente nelle impostazioni di controllo delle monete. I saldi bloccati diventeranno disponibili una volta completate le rispettive transazioni, mentre i saldi congelati rimarranno inaccessibili per le transazioni finché non deciderai di sbloccarli.", @@ -840,4 +841,4 @@ "you_will_get": "Converti a", "you_will_send": "Conveti da", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 4902e56cb3..5c0c0b2322 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -757,6 +757,7 @@ "tx_rejected_dust_output_send_all": "ネットワークルール、低出力量(ダスト)によって拒否されたトランザクション。コイン管理下で選択されたコインのバランスを確認してください。", "tx_rejected_vout_negative": "この取引の料金に支払うのに十分な残高はありません。コイン制御下のコインのバランスを確認してください。", "tx_wrong_balance_exception": "この金額を送信するのに十分な${currency}はありません。", + "tx_wrong_balance_with_amount_exception": "あなたは十分なものを持っていませ${currency} ${amount}", "tx_zero_fee_exception": "0料金でトランザクションを送信できません。レートを上げて、最新の見積もりについて接続を確認してみてください。", "unavailable_balance": "利用できない残高", "unavailable_balance_description": "利用不可能な残高: この合計には、保留中のトランザクションにロックされている資金と、コイン管理設定でアクティブに凍結した資金が含まれます。ロックされた残高は、それぞれの取引が完了すると利用可能になりますが、凍結された残高は、凍結を解除するまで取引にアクセスできません。", @@ -838,4 +839,4 @@ "you_will_get": "に変換", "you_will_send": "から変換", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index cbf4b5e89c..d02639ebb4 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -757,6 +757,7 @@ "tx_rejected_dust_output_send_all": "네트워크 규칙, 낮은 출력 금액 (먼지)에 의해 거부 된 거래. 동전 제어에서 선택한 동전의 균형을 확인하십시오.", "tx_rejected_vout_negative": "이 거래 수수료를 지불하기에 잔액이 충분하지 않습니다. 동전 통제하에 동전의 균형을 확인하십시오.", "tx_wrong_balance_exception": "이 금액을 보내기에 충분한 ${currency}가 충분하지 않습니다.", + "tx_wrong_balance_with_amount_exception": "충분하지 않습니다 ${currency} 총 ${amount} 총 금액을 보내십시오.", "tx_zero_fee_exception": "0 수수료로 거래를 보낼 수 없습니다. 최신 견적에 대해서는 속도를 높이거나 연결을 확인하십시오.", "unavailable_balance": "사용할 수 없는 잔액", "unavailable_balance_description": "사용할 수 없는 잔액: 이 총계에는 보류 중인 거래에 잠겨 있는 자금과 코인 관리 설정에서 적극적으로 동결된 자금이 포함됩니다. 잠긴 잔액은 해당 거래가 완료되면 사용할 수 있게 되며, 동결된 잔액은 동결을 해제하기 전까지 거래에 액세스할 수 없습니다.", @@ -839,4 +840,4 @@ "you_will_send": "다음에서 변환", "YY": "YY", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index a3e570ead3..3b438e707b 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "Network စည်းမျဉ်းစည်းကမ်းများဖြင့် ပယ်ချ. ငွေပေးချေမှုသည် output output (ဖုန်မှုန့်) ဖြင့်ပယ်ချခဲ့သည်။ ဒင်္ဂါးပြားထိန်းချုပ်မှုအောက်တွင်ရွေးချယ်ထားသောဒင်္ဂါးများ၏လက်ကျန်ငွေကိုစစ်ဆေးပါ။", "tx_rejected_vout_negative": "ဒီငွေပေးငွေယူရဲ့အခကြေးငွေအတွက်ပေးဆောင်ဖို့လုံလောက်တဲ့ဟန်ချက်မလုံလောက်။ ဒင်္ဂါးပြား၏လက်ကျန်ငွေလက်ကျန်ငွေကိုစစ်ဆေးပါ။", "tx_wrong_balance_exception": "ဤငွေပမာဏကိုပေးပို့ရန်သင့်တွင် ${currency} မရှိပါ။", +"tx_wrong_balance_with_amount_exception": "သင့်တွင်လုံလောက်မှုမရှိပါ${currency} ${amount}", "tx_zero_fee_exception": "0 ကြေးနှင့်အတူငွေပေးငွေယူပေးပို့လို့မရပါဘူး။ နှုန်းကိုတိုးမြှင့်ခြင်းသို့မဟုတ်နောက်ဆုံးခန့်မှန်းချက်များအတွက်သင်၏ connection ကိုစစ်ဆေးပါ။", "unavailable_balance": "လက်ကျန်ငွေ မရရှိနိုင်ပါ။", "unavailable_balance_description": "မရရှိနိုင်သော လက်ကျန်ငွေ- ဤစုစုပေါင်းတွင် ဆိုင်းငံ့ထားသော ငွေပေးငွေယူများတွင် သော့ခတ်ထားသော ငွေကြေးများနှင့် သင်၏ coin ထိန်းချုပ်မှုဆက်တင်များတွင် သင် တက်ကြွစွာ အေးခဲထားသော ငွေများ ပါဝင်သည်။ သော့ခတ်ထားသော လက်ကျန်ငွေများကို ၎င်းတို့၏ သက်ဆိုင်ရာ ငွေပေးငွေယူများ ပြီးမြောက်သည်နှင့် တပြိုင်နက် ရရှိနိုင်မည်ဖြစ်ပြီး၊ အေးခဲထားသော လက်ကျန်များကို ၎င်းတို့အား ပြန်ဖြုတ်ရန် သင်ဆုံးဖြတ်သည်အထိ ငွေပေးငွေယူများအတွက် ဆက်လက်၍မရနိုင်ပါ။", @@ -837,4 +838,4 @@ "you_will_get": "သို့ပြောင်းပါ။", "you_will_send": "မှပြောင်းပါ။", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index f017959642..6e8714554d 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "Transactie afgewezen door netwerkregels, laag outputbedrag (stof). Controleer het saldo van munten die zijn geselecteerd onder muntcontrole.", "tx_rejected_vout_negative": "Niet genoeg saldo om te betalen voor de kosten van deze transactie. Controleer het saldo van munten onder muntcontrole.", "tx_wrong_balance_exception": "Je hebt niet genoeg ${currency} om dit bedrag te verzenden.", + "tx_wrong_balance_with_amount_exception": "Je hebt niet genoeg ${currency} om de totale hoeveelheid ${amount} te verzenden", "tx_zero_fee_exception": "Kan geen transactie verzenden met 0 kosten. Probeer het tarief te verhogen of uw verbinding te controleren op de laatste schattingen.", "unavailable_balance": "Onbeschikbaar saldo", "unavailable_balance_description": "Niet-beschikbaar saldo: Dit totaal omvat het geld dat is vergrendeld in lopende transacties en het geld dat u actief hebt bevroren in uw muntcontrole-instellingen. Vergrendelde saldi komen beschikbaar zodra de betreffende transacties zijn voltooid, terwijl bevroren saldi ontoegankelijk blijven voor transacties totdat u besluit ze weer vrij te geven.", @@ -838,4 +839,4 @@ "you_will_get": "Converteren naar", "you_will_send": "Converteren van", "yy": "JJ" -} \ No newline at end of file +} diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 07c8fc7924..f3805bf5e9 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "Transakcja odrzucona według reguł sieciowych, niskiej ilości wyjściowej (pyłu). Sprawdź saldo monet wybranych pod kontrolą monet.", "tx_rejected_vout_negative": "Za mało salda, aby zapłacić za opłaty tej transakcji. Sprawdź saldo monet pod kontrolą monet.", "tx_wrong_balance_exception": "Nie masz wystarczającej ilości ${currency}, aby wysłać tę kwotę.", + "tx_wrong_balance_with_amount_exception": "Nie masz wystarczająco dużo ${currency} aby wysłać całkowitą ilość ${amount}", "tx_zero_fee_exception": "Nie można wysłać transakcji z 0 opłatą. Spróbuj zwiększyć stawkę lub sprawdzić połączenie w poszukiwaniu najnowszych szacunków.", "unavailable_balance": "Niedostępne saldo", "unavailable_balance_description": "Niedostępne saldo: Suma ta obejmuje środki zablokowane w transakcjach oczekujących oraz te, które aktywnie zamroziłeś w ustawieniach kontroli monet. Zablokowane salda staną się dostępne po zakończeniu odpowiednich transakcji, natomiast zamrożone salda pozostaną niedostępne dla transakcji, dopóki nie zdecydujesz się ich odblokować.", @@ -837,4 +838,4 @@ "you_will_get": "Konwertuj na", "you_will_send": "Konwertuj z", "yy": "RR" -} \ No newline at end of file +} diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index f010e6c2b9..d31dfd91b9 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -758,6 +758,7 @@ "tx_rejected_dust_output_send_all": "Transação rejeitada por regras de rede, baixa quantidade de saída (poeira). Por favor, verifique o saldo de moedas selecionadas sob controle de moedas.", "tx_rejected_vout_negative": "Não há saldo suficiente para pagar as taxas desta transação. Por favor, verifique o saldo de moedas sob controle de moedas.", "tx_wrong_balance_exception": "Você não tem o suficiente ${currency} para enviar esse valor.", + "tx_wrong_balance_with_amount_exception": "Você não tem o suficiente ${currency} para enviar o valor total de ${amount}", "tx_zero_fee_exception": "Não pode enviar transação com taxa 0. Tente aumentar a taxa ou verificar sua conexão para obter as estimativas mais recentes.", "unavailable_balance": "Saldo indisponível", "unavailable_balance_description": "Saldo Indisponível: Este total inclui fundos bloqueados em transações pendentes e aqueles que você congelou ativamente nas configurações de controle de moedas. Os saldos bloqueados ficarão disponíveis assim que suas respectivas transações forem concluídas, enquanto os saldos congelados permanecerão inacessíveis para transações até que você decida descongelá-los.", @@ -840,4 +841,4 @@ "you_will_get": "Converter para", "you_will_send": "Converter de", "yy": "aa" -} \ No newline at end of file +} diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 52d2cfe9e4..5c48d926f6 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -757,6 +757,7 @@ "tx_rejected_dust_output_send_all": "Транзакция отклоняется в соответствии с правилами сети, низкой выходной суммой (пыль). Пожалуйста, проверьте баланс монет, выбранных под контролем монет.", "tx_rejected_vout_negative": "Недостаточно баланс, чтобы оплатить плату этой транзакции. Пожалуйста, проверьте баланс монет под контролем монет.", "tx_wrong_balance_exception": "У вас не хватает ${currency}, чтобы отправить эту сумму.", + "tx_wrong_balance_with_amount_exception": "У вас недостаточно ${currency}, чтобы отправить общее количество ${amount}", "tx_zero_fee_exception": "Не может отправить транзакцию с платой 0. Попробуйте увеличить ставку или проверить соединение на наличие последних оценок.", "unavailable_balance": "Недоступный баланс", "unavailable_balance_description": "Недоступный баланс: в эту сумму входят средства, заблокированные в ожидающих транзакциях, и средства, которые вы активно заморозили в настройках управления монетами. Заблокированные балансы станут доступны после завершения соответствующих транзакций, а замороженные балансы останутся недоступными для транзакций, пока вы не решите их разморозить.", @@ -838,4 +839,4 @@ "you_will_get": "Конвертировать в", "you_will_send": "Конвертировать из", "yy": "ГГ" -} \ No newline at end of file +} diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 25ca76830b..2e8cbe2b9f 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "การทำธุรกรรมถูกปฏิเสธโดยกฎเครือข่ายจำนวนเอาต์พุตต่ำ (ฝุ่น) โปรดตรวจสอบยอดคงเหลือของเหรียญที่เลือกภายใต้การควบคุมเหรียญ", "tx_rejected_vout_negative": "ยอดคงเหลือไม่เพียงพอที่จะจ่ายสำหรับค่าธรรมเนียมการทำธุรกรรมนี้ โปรดตรวจสอบยอดคงเหลือของเหรียญภายใต้การควบคุมเหรียญ", "tx_wrong_balance_exception": "คุณมีไม่เพียงพอ ${currency} ในการส่งจำนวนนี้", + "tx_wrong_balance_with_amount_exception": "คุณมีไม่เพียงพอ ${currency} เพื่อส่งจำนวนทั้งหมดของ ${amount}", "tx_zero_fee_exception": "ไม่สามารถส่งธุรกรรมด้วยค่าธรรมเนียม 0 ลองเพิ่มอัตราหรือตรวจสอบการเชื่อมต่อของคุณสำหรับการประมาณการล่าสุด", "unavailable_balance": "ยอดคงเหลือไม่พร้อมใช้งาน", "unavailable_balance_description": "ยอดคงเหลือที่ไม่พร้อมใช้งาน: ยอดรวมนี้รวมถึงเงินทุนที่ถูกล็อคในการทำธุรกรรมที่รอดำเนินการและที่คุณได้แช่แข็งไว้ในการตั้งค่าการควบคุมเหรียญของคุณ ยอดคงเหลือที่ถูกล็อคจะพร้อมใช้งานเมื่อธุรกรรมที่เกี่ยวข้องเสร็จสมบูรณ์ ในขณะที่ยอดคงเหลือที่แช่แข็งจะไม่สามารถเข้าถึงได้สำหรับธุรกรรมจนกว่าคุณจะตัดสินใจยกเลิกการแช่แข็ง", @@ -837,4 +838,4 @@ "you_will_get": "แปลงเป็น", "you_will_send": "แปลงจาก", "yy": "ปี" -} \ No newline at end of file +} diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 53eac9da83..25f450d0a1 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "Ang transaksyon na tinanggihan ng mga patakaran sa network, mababang halaga ng output (alikabok). Mangyaring suriin ang balanse ng mga barya na napili sa ilalim ng kontrol ng barya.", "tx_rejected_vout_negative": "Hindi sapat na balanse upang magbayad para sa mga bayarin ng transaksyon na ito. Mangyaring suriin ang balanse ng mga barya sa ilalim ng kontrol ng barya.", "tx_wrong_balance_exception": "Wala kang sapat na ${currency} upang maipadala ang halagang ito.", + "tx_wrong_balance_with_amount_exception": "Wala kang sapat ${currency} Upang ipadala ang kabuuang halaga ng ${amount}", "tx_zero_fee_exception": "Hindi maaaring magpadala ng transaksyon na may 0 bayad. Subukan ang pagtaas ng rate o pagsuri sa iyong koneksyon para sa pinakabagong mga pagtatantya.", "unavailable_balance": "Hindi available na balanse", "unavailable_balance_description": "Hindi Available na Balanse: Kasama sa kabuuang ito ang mga pondong naka-lock sa mga nakabinbing transaksyon at ang mga aktibong na-freeze mo sa iyong mga setting ng kontrol ng coin. Magiging available ang mga naka-lock na balanse kapag nakumpleto na ang kani-kanilang mga transaksyon, habang ang mga nakapirming balanse ay nananatiling hindi naa-access para sa mga transaksyon hanggang sa magpasya kang i-unfreeze ang mga ito.", @@ -837,4 +838,4 @@ "you_will_get": "Mag -convert sa", "you_will_send": "I -convert mula sa", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index c1c36dc037..a024cbcd25 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "Ağ kurallarına göre reddedilen işlem, düşük çıktı miktarı (toz). Lütfen madeni para kontrolü altında seçilen madeni para dengesini kontrol edin.", "tx_rejected_vout_negative": "Bu işlem ücretleri için ödeme yapmak için yeterli bakiye yok. Lütfen madeni para kontrolü altındaki madeni para dengesini kontrol edin.", "tx_wrong_balance_exception": "Bu miktarı göndermek için yeterli ${currency} yok.", + "tx_wrong_balance_with_amount_exception": "Yeterli değilsiniz ${currency} toplam ${amount} miktarını göndermek için", "tx_zero_fee_exception": "0 ücret ile işlem gönderilemez. En son tahminler için oranı artırmayı veya bağlantınızı kontrol etmeyi deneyin.", "unavailable_balance": "Kullanılamayan bakiye", "unavailable_balance_description": "Kullanılamayan Bakiye: Bu toplam, bekleyen işlemlerde kilitlenen fonları ve jeton kontrol ayarlarınızda aktif olarak dondurduğunuz fonları içerir. Kilitli bakiyeler, ilgili işlemleri tamamlandıktan sonra kullanılabilir hale gelir; dondurulmuş bakiyeler ise siz onları dondurmaya karar verene kadar işlemler için erişilemez durumda kalır.", @@ -837,4 +838,4 @@ "you_will_get": "Biçimine dönüştür:", "you_will_send": "Biçiminden dönüştür:", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 8d05c27a38..669ba1e82a 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -757,6 +757,7 @@ "tx_rejected_dust_output_send_all": "Транзакція відхилена за допомогою мережевих правил, низька кількість вихідної кількості (пил). Будь ласка, перевірте баланс монет, вибраних під контролем монет.", "tx_rejected_vout_negative": "Недостатньо балансу, щоб оплатити плату за цю транзакцію. Будь ласка, перевірте баланс монет під контролем монет.", "tx_wrong_balance_exception": "У вас недостатньо ${currency}, щоб надіслати цю суму.", + "tx_wrong_balance_with_amount_exception": "У вас недостатньо ${currency}, щоб надіслати загальну кількість ${amount}", "tx_zero_fee_exception": "Не вдається відправити транзакцію з 0 платежами. Спробуйте збільшити ставку або перевірити з'єднання на останні оцінки.", "unavailable_balance": "Недоступний баланс", "unavailable_balance_description": "Недоступний баланс: ця сума включає кошти, заблоковані в незавершених транзакціях, і ті, які ви активно заморозили в налаштуваннях контролю монет. Заблоковані баланси стануть доступними після завершення відповідних транзакцій, тоді як заморожені баланси залишаються недоступними для транзакцій, доки ви не вирішите їх розморозити.", @@ -838,4 +839,4 @@ "you_will_get": "Конвертувати в", "you_will_send": "Конвертувати з", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index c2672e224a..40e7e71dbd 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -758,6 +758,7 @@ "tx_rejected_dust_output_send_all": "لین دین کو نیٹ ورک کے قواعد ، کم آؤٹ پٹ رقم (دھول) کے ذریعہ مسترد کردیا گیا۔ براہ کرم سکے کے کنٹرول میں منتخب کردہ سکے کا توازن چیک کریں۔", "tx_rejected_vout_negative": "اس لین دین کی فیسوں کی ادائیگی کے لئے کافی توازن نہیں ہے۔ براہ کرم سکے کے کنٹرول میں سکے کا توازن چیک کریں۔", "tx_wrong_balance_exception": "آپ کے پاس یہ رقم بھیجنے کے لئے کافی ${currency} نہیں ہے۔", + "tx_wrong_balance_with_amount_exception": "آپ کے پاس کافی نہیں ہے ${currency} ${amount}", "tx_zero_fee_exception": "0 فیس کے ساتھ لین دین نہیں بھیج سکتا۔ شرح کو بڑھانے یا تازہ ترین تخمینے کے ل your اپنے کنکشن کی جانچ پڑتال کرنے کی کوشش کریں۔", "unavailable_balance": "ﺲﻨﻠﯿﺑ ﺏﺎﯿﺘﺳﺩ ﺮﯿﻏ", "unavailable_balance_description": "۔ﮯﺗﺮﮐ ﮟﯿﮩﻧ ﮧﻠﺼﯿﻓ ﺎﮐ ﮯﻧﺮﮐ ﺪﻤﺠﻨﻣ ﻥﺍ ﮟﯿﮩﻧﺍ ﭖﺁ ﮧﮐ ﮏﺗ ﺐﺟ ﮟﯿﮨ ﮯﺘﮨﺭ ﯽﺋﺎﺳﺭ ﻞﺑﺎﻗﺎﻧ ﮏﺗ ﺖﻗﻭ ﺱﺍ ﮯﯿﻟ ﮯﮐ ﻦﯾﺩ ﻦﯿﻟ ﺲﻨﻠﯿﺑ ﺪﻤﺠﻨﻣ ﮧﮐ ﺐﺟ ،ﮯﮔ ﮟﯿﺋﺎﺟ ﻮﮨ ﺏﺎﯿﺘﺳﺩ ﺲﻨﻠﯿﺑ ﻞﻔﻘﻣ ﺪﻌﺑ ﮯﮐ ﮯﻧﻮﮨ ﻞﻤﮑﻣ ﻦﯾﺩ ﻦﯿﻟ ﮧﻘﻠﻌﺘﻣ ﮯﮐ ﻥﺍ ۔ﮯﮨ ﺎﮭﮐﺭ ﺮ", @@ -839,4 +840,4 @@ "you_will_get": "میں تبدیل کریں۔", "you_will_send": "سے تبدیل کریں۔", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 39e65f82d3..4de13273d8 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -757,6 +757,7 @@ "tx_rejected_dust_output_send_all": "Idunadura kọ nipasẹ awọn ofin nẹtiwọọki, iye ti o wuwe kekere (eruku). Jọwọ ṣayẹwo dọgbadọgba ti awọn owo ti a yan labẹ iṣakoso owo.", "tx_rejected_vout_negative": "Iwontunws.funfun ti o to lati sanwo fun awọn idiyele iṣowo yii. Jọwọ ṣayẹwo iwọntunwọnsi ti awọn owo labẹ iṣakoso owo.", "tx_wrong_balance_exception": "O ko ni to ${currency} lati firanṣẹ iye yii.", + "tx_wrong_balance_with_amount_exception": "O ko ni to ${currency} Lati firanṣẹ lapapọ iye ${amount}", "tx_zero_fee_exception": "Ko le firanṣẹ idunadura pẹlu ọya 0. Gbiyanju jijẹ oṣuwọn tabi ṣayẹwo asopọ rẹ fun awọn iṣiro tuntun.", "unavailable_balance": "Iwontunwonsi ti ko si", "unavailable_balance_description": "Iwontunws.funfun ti ko si: Lapapọ yii pẹlu awọn owo ti o wa ni titiipa ni awọn iṣowo isunmọ ati awọn ti o ti didi ni itara ninu awọn eto iṣakoso owo rẹ. Awọn iwọntunwọnsi titiipa yoo wa ni kete ti awọn iṣowo oniwun wọn ba ti pari, lakoko ti awọn iwọntunwọnsi tio tutunini ko ni iraye si fun awọn iṣowo titi iwọ o fi pinnu lati mu wọn kuro.", @@ -838,4 +839,4 @@ "you_will_get": "Ṣe pàṣípààrọ̀ sí", "you_will_send": "Ṣe pàṣípààrọ̀ láti", "yy": "Ọd" -} \ No newline at end of file +} diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 400a2ed3cf..2f283532a7 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -756,6 +756,7 @@ "tx_rejected_dust_output_send_all": "交易被网络规则,低输出量(灰尘)拒绝。请检查在硬币控制下选择的硬币的余额。", "tx_rejected_vout_negative": "没有足够的余额来支付此交易费用。请检查硬币控制下的硬币余额。", "tx_wrong_balance_exception": "您没有足够的${currency}来发送此金额。", + "tx_wrong_balance_with_amount_exception": "您没有足够的${currency} ${amount}", "tx_zero_fee_exception": "无法以0费用发送交易。尝试提高速率或检查连接以获取最新估计。", "unavailable_balance": "不可用余额", "unavailable_balance_description": "不可用余额:此总额包括锁定在待处理交易中的资金以及您在硬币控制设置中主动冻结的资金。一旦各自的交易完成,锁定的余额将变得可用,而冻结的余额在您决定解冻之前仍然无法进行交易。", @@ -837,4 +838,4 @@ "you_will_get": "转换到", "you_will_send": "转换自", "yy": "YY" -} \ No newline at end of file +} From 39845d6904511b3dab587f1066685b0ae01fe862 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 22 Apr 2024 17:03:16 -0300 Subject: [PATCH 44/75] fix: nodes & build --- cw_bitcoin/lib/electrum_wallet.dart | 2 +- cw_bitcoin/pubspec.lock | 36 ++++++++++---------- lib/entities/default_settings_migration.dart | 6 ++++ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 38d6c5fec6..21ee56307b 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -430,7 +430,7 @@ abstract class ElectrumWalletBase } if (silentPaymentDestinations.isNotEmpty) { - final spb = SilentPaymentBuilder(pubkeys: inputPubKeys, outpoints: vinOutpoints); + final spb = SilentPaymentBuilder(pubkeys: inputPubKeys, vinOutpoints: vinOutpoints); final sendingOutputs = spb.createOutputs(inputPrivKeyInfos, silentPaymentDestinations); final outputsAdded = []; diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index bd3dd41894..86a2084452 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" asn1lib: dependency: transitive description: @@ -80,7 +80,7 @@ packages: description: path: "." ref: cake-update-v3 - resolved-ref: "3ddad3d1a9b78f49c9ef542962758400315d64a7" + resolved-ref: "2a18ab92a9f7136b76fcd1bf8480eaaa90e0a6b2" url: "https://github.com/cake-tech/bitcoin_base" source: git version: "4.0.0" @@ -154,10 +154,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" url: "https://pub.dev" source: hosted - version: "2.4.8" + version: "2.4.9" build_runner_core: dependency: transitive description: @@ -178,10 +178,10 @@ packages: dependency: transitive description: name: built_value - sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.9.1" + version: "8.9.2" characters: dependency: transitive description: @@ -326,10 +326,10 @@ packages: dependency: "direct main" description: name: flutter_mobx - sha256: "4a5d062ff85ed3759f4aac6410ff0ffae32e324b2e71ca722ae1b37b32e865f4" + sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e" url: "https://pub.dev" source: hosted - version: "2.2.0+2" + version: "2.2.1+1" flutter_test: dependency: "direct dev" description: flutter @@ -339,10 +339,10 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" glob: dependency: transitive description: @@ -483,10 +483,10 @@ packages: dependency: "direct main" description: name: mobx - sha256: "74ee54012dc7c1b3276eaa960a600a7418ef5f9997565deb8fca1fd88fb36b78" + sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e" url: "https://pub.dev" source: hosted - version: "2.3.0+1" + version: "2.3.3+2" mobx_codegen: dependency: "direct dev" description: @@ -587,10 +587,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" + sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333" url: "https://pub.dev" source: hosted - version: "3.7.4" + version: "3.8.0" pool: dependency: transitive description: @@ -603,10 +603,10 @@ packages: dependency: transitive description: name: provider - sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "6.1.2" pub_semver: dependency: transitive description: @@ -697,7 +697,7 @@ packages: description: path: "." ref: master - resolved-ref: de90b20f4250647d0f55f6bd5e7203710d0d5678 + resolved-ref: "0ac9108db2f475c5b685af9eb9df393dcc978820" url: "https://github.com/rafael-xmr/sp_scanner" source: git version: "0.0.1" diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 882cced1b6..856ac48631 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -822,6 +822,9 @@ Future checkCurrentNodes( final cakeWalletElectrum = Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin, useSSL: false); await nodeSource.add(cakeWalletElectrum); + final cakeWalletElectrumTestnet = + Node(uri: publicBitcoinTestnetElectrumUri, type: WalletType.bitcoin, useSSL: false); + await nodeSource.add(cakeWalletElectrumTestnet); await sharedPreferences.setInt( PreferencesKey.currentBitcoinElectrumSererIdKey, cakeWalletElectrum.key as int); } @@ -894,6 +897,9 @@ Future resetBitcoinElectrumServer( if (cakeWalletNode == null) { cakeWalletNode = Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin, useSSL: false); + final cakeWalletElectrumTestnet = + Node(uri: publicBitcoinTestnetElectrumUri, type: WalletType.bitcoin, useSSL: false); + await nodeSource.add(cakeWalletElectrumTestnet); await nodeSource.add(cakeWalletNode); } From 4a7d8ca006892de72da1cd59621199c1bacb4c06 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Tue, 30 Apr 2024 12:50:58 -0300 Subject: [PATCH 45/75] fix: send all with multiple outs --- cw_bitcoin/lib/electrum_wallet.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index b171d08d64..ac2b8e4fd0 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -569,8 +569,9 @@ abstract class ElectrumWalletBase } } - outputs[outputs.length - 1] = - BitcoinOutput(address: outputs.last.address, value: BigInt.from(amount)); + if (outputs.length == 1) { + outputs[0] = BitcoinOutput(address: outputs.last.address, value: BigInt.from(amount)); + } return EstimatedTxResult( utxos: utxoDetails.utxos, From 781cbc27e8962d7f6552b3ec97593f7412175ae0 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Mon, 6 May 2024 17:31:41 -0300 Subject: [PATCH 46/75] refactor: unchanged file --- .../address_edit_or_create_page.dart | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/src/screens/subaddress/address_edit_or_create_page.dart b/lib/src/screens/subaddress/address_edit_or_create_page.dart index ed6369a736..750af846ed 100644 --- a/lib/src/screens/subaddress/address_edit_or_create_page.dart +++ b/lib/src/screens/subaddress/address_edit_or_create_page.dart @@ -13,7 +13,8 @@ class AddressEditOrCreatePage extends BasePage { : _formKey = GlobalKey(), _labelController = TextEditingController(), super() { - _labelController.addListener(() => addressEditOrCreateViewModel.label = _labelController.text); + _labelController.addListener( + () => addressEditOrCreateViewModel.label = _labelController.text); _labelController.text = addressEditOrCreateViewModel.label; } @@ -54,8 +55,10 @@ class AddressEditOrCreatePage extends BasePage { : S.of(context).new_subaddress_create, color: Theme.of(context).primaryColor, textColor: Colors.white, - isLoading: addressEditOrCreateViewModel.state is AddressIsSaving, - isDisabled: addressEditOrCreateViewModel.label?.isEmpty ?? true, + isLoading: + addressEditOrCreateViewModel.state is AddressIsSaving, + isDisabled: + addressEditOrCreateViewModel.label?.isEmpty ?? true, ), ) ], @@ -67,13 +70,14 @@ class AddressEditOrCreatePage extends BasePage { if (_isEffectsInstalled) { return; } - reaction((_) => addressEditOrCreateViewModel.state, (AddressEditOrCreateState state) { - if (state is AddressSavedSuccessfully) { - WidgetsBinding.instance.addPostFrameCallback((_) => Navigator.of(context).pop()); - } - }); + reaction((_) => addressEditOrCreateViewModel.state, + (AddressEditOrCreateState state) { + if (state is AddressSavedSuccessfully) { + WidgetsBinding.instance + .addPostFrameCallback((_) => Navigator.of(context).pop()); + } + }); _isEffectsInstalled = true; } } - From 69dd7a5321bec4fb824f1387e94804ee96150009 Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Wed, 8 May 2024 05:04:47 +0300 Subject: [PATCH 47/75] Update pr_test_build.yml --- .github/workflows/pr_test_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 23902f110c..aa14573d15 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -42,7 +42,7 @@ jobs: - name: Flutter action uses: subosito/flutter-action@v1 with: - flutter-version: "3.19.5" + flutter-version: "3.19.6" channel: stable - name: Install package dependencies From 15ca7d6219f0ac2754f82d0428fc45b5f051dc5d Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Wed, 8 May 2024 08:40:57 -0300 Subject: [PATCH 48/75] chore: upgrade --- cw_nano/pubspec.lock | 124 ++++++++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 50 deletions(-) diff --git a/cw_nano/pubspec.lock b/cw_nano/pubspec.lock index 51e43145ea..10d254698a 100644 --- a/cw_nano/pubspec.lock +++ b/cw_nano/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" asn1lib: dependency: transitive description: @@ -77,10 +77,10 @@ packages: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.1" build_config: dependency: transitive description: @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: build_runner_core - sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" url: "https://pub.dev" source: hosted - version: "7.2.7" + version: "7.2.10" built_collection: dependency: transitive description: @@ -228,10 +228,10 @@ packages: dependency: transitive description: name: encrypt - sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb" + sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.0.3" fake_async: dependency: transitive description: @@ -244,10 +244,10 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" file: dependency: transitive description: @@ -281,10 +281,10 @@ packages: dependency: transitive description: name: flutter_mobx - sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e" + sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e" url: "https://pub.dev" source: hosted - version: "2.0.6+5" + version: "2.2.1+1" flutter_test: dependency: "direct dev" description: flutter @@ -299,10 +299,10 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" glob: dependency: transitive description: @@ -347,10 +347,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -387,18 +387,18 @@ packages: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.7.1" json_annotation: dependency: transitive description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" leak_tracker: dependency: transitive description: @@ -467,10 +467,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" mobx: dependency: "direct main" description: @@ -504,6 +504,14 @@ packages: url: "https://github.com/perishllc/nanoutil.git" source: git version: "1.0.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" package_config: dependency: transitive description: @@ -524,26 +532,26 @@ packages: dependency: transitive description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.4" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -556,10 +564,10 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: @@ -580,10 +588,10 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: @@ -596,10 +604,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.9.1" pool: dependency: transitive description: @@ -608,6 +616,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + provider: + dependency: transitive + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" pub_semver: dependency: transitive description: @@ -636,26 +652,26 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" url: "https://pub.dev" source: hosted - version: "2.3.5" + version: "2.4.0" shared_preferences_linux: dependency: transitive description: @@ -676,10 +692,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" shared_preferences_windows: dependency: transitive description: @@ -713,10 +729,10 @@ packages: dependency: transitive description: name: socks5_proxy - sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + sha256: "045cbba84f6e2b01c1c77634a63e926352bf110ef5f07fc462c6d43bbd4b6a83" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5+dev.2" source_gen: dependency: transitive description: @@ -829,22 +845,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.5" win32: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.5.0" xdg_directories: dependency: transitive description: @@ -862,5 +886,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.10.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" From 1ebd19fc738d08d8b164b0db7f59a100dab2885f Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Wed, 8 May 2024 10:28:25 -0300 Subject: [PATCH 49/75] chore: merge changes --- cw_bitcoin/lib/bitcoin_wallet.dart | 10 +- cw_bitcoin/lib/electrum.dart | 4 +- cw_bitcoin/lib/electrum_wallet.dart | 20 +- cw_bitcoin/pubspec.lock | 86 ++++---- cw_core/pubspec.lock | 112 +++++----- cw_haven/pubspec.lock | 184 +++++++++-------- cw_monero/pubspec.lock | 192 ++++++++++-------- cw_shared_external/pubspec.lock | 147 -------------- lib/di.dart | 90 ++++---- .../screens/dashboard/pages/balance_page.dart | 94 ++++++++- .../dashboard/pages/cake_features_page.dart | 89 +------- .../dashboard/pages/market_place_page.dart | 119 ----------- .../wallet_address_list_item.dart | 2 +- 13 files changed, 462 insertions(+), 687 deletions(-) delete mode 100644 cw_shared_external/pubspec.lock delete mode 100644 lib/src/screens/dashboard/pages/market_place_page.dart diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index f27a8bf1af..3091cacf54 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -58,6 +58,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { currency: networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc, ) { + // in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here) + // the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here) + // String derivationPath = walletInfo.derivationInfo!.derivationPath!; + // String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1"; + // final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType); walletAddresses = BitcoinWalletAddresses( walletInfo, initialAddresses: initialAddresses, @@ -68,7 +73,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { mainHd: hd, sideHd: accountHD.derive(1), network: networkParam ?? network, - masterHd: hd, + masterHd: + seedBytes != null ? bitcoin.HDWallet.fromSeed(seedBytes, network: networkType) : null, ); autorun((_) { @@ -171,7 +177,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialSilentAddresses: snp.silentAddresses, initialSilentAddressIndex: snp.silentAddressIndex, initialBalance: snp.balance, - seedBytes: seedBytes!, + seedBytes: seedBytes, initialRegularAddressIndex: snp.regularAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex, addressPageType: snp.addressPageType, diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index a76e9b7bab..89c5fb785b 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -36,8 +36,8 @@ class ElectrumClient { _errors = {}, unterminatedString = ''; - static const connectionTimeout = Duration(seconds: 10); - static const aliveTimerDuration = Duration(seconds: 10); + static const connectionTimeout = Duration(seconds: 5); + static const aliveTimerDuration = Duration(seconds: 4); bool get isConnected => _isConnected; Socket? socket; diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 58e807ed3e..2d17222131 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -376,19 +376,11 @@ abstract class ElectrumWalletBase await _subscribeForUpdates(); - int finished = 0; - void checkFinishedAllUpdates(void _) { - finished++; - if (finished == 2) syncStatus = SyncedSyncStatus(); - } - - // Always await first as it discovers new addresses in the process await updateTransactions(); + await updateAllUnspents(); + await updateBalance(); - updateAllUnspents().then(checkFinishedAllUpdates); - updateBalance().then(checkFinishedAllUpdates); - - updateFeeRates(); + await updateFeeRates(); } catch (e, stacktrace) { print(stacktrace); print(e.toString()); @@ -631,8 +623,8 @@ abstract class ElectrumWalletBase isSendAll: true, hasChange: false, memo: memo, - spendsSilentPayment: utxoDetails.spendsSilentPayment, spendsUnconfirmedTX: utxoDetails.spendsUnconfirmedTX, + spendsSilentPayment: utxoDetails.spendsSilentPayment, ); } @@ -790,8 +782,8 @@ abstract class ElectrumWalletBase hasChange: true, isSendAll: false, memo: memo, - spendsSilentPayment: utxoDetails.spendsSilentPayment, spendsUnconfirmedTX: utxoDetails.spendsUnconfirmedTX, + spendsSilentPayment: utxoDetails.spendsSilentPayment, ); } @@ -986,6 +978,8 @@ abstract class ElectrumWalletBase ? SegwitAddresType.p2wpkh.toString() : walletInfo.addressPageType.toString(), 'balance': balance[currency]?.toJSON(), + 'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index, + 'derivationPath': walletInfo.derivationInfo?.derivationPath, 'silent_addresses': walletAddresses.silentAddresses.map((addr) => addr.toJSON()).toList(), 'silent_address_index': walletAddresses.currentSilentAddressIndex.toString(), }); diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 65648d2a05..85c7bd7f7f 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -80,10 +80,10 @@ packages: description: path: "." ref: cake-update-v3 - resolved-ref: "2a18ab92a9f7136b76fcd1bf8480eaaa90e0a6b2" + resolved-ref: cc99eedb1d28ee9376dda0465ef72aa627ac6149 url: "https://github.com/cake-tech/bitcoin_base" source: git - version: "4.0.0" + version: "4.2.1" bitcoin_flutter: dependency: "direct main" description: @@ -98,10 +98,10 @@ packages: description: path: "." ref: cake-update-v1 - resolved-ref: "7864de88e9a0b598a61b1e50d26f6f4477a6411c" + resolved-ref: cabd7e0e16c4da9920338c76eff3aeb8af0211f3 url: "https://github.com/cake-tech/blockchain_utils" source: git - version: "1.6.0" + version: "2.1.2" boolean_selector: dependency: transitive description: @@ -250,10 +250,10 @@ packages: dependency: "direct main" description: name: cryptography - sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35 + sha256: d146b76d33d94548cf035233fbc2f4338c1242fa119013bead807d033fc4ae05 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.7.0" cw_core: dependency: "direct main" description: @@ -297,10 +297,10 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" ffigen: dependency: transitive description: @@ -363,10 +363,10 @@ packages: dependency: transitive description: name: functional_data - sha256: aefdec4365452283b2a7cf420a3169654d51d3e9553069a22d76680d7a9d7c3d + sha256: "76d17dc707c40e552014f5a49c0afcc3f1e3f05e800cd6b7872940bfe41a5039" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" glob: dependency: transitive description: @@ -411,10 +411,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -459,10 +459,10 @@ packages: dependency: transitive description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" leak_tracker: dependency: transitive description: @@ -548,10 +548,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" mobx: dependency: "direct main" description: @@ -596,26 +596,26 @@ packages: dependency: "direct main" description: name: path_provider - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.4" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -660,10 +660,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333" + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" url: "https://pub.dev" source: hosted - version: "3.8.0" + version: "3.9.1" pool: dependency: transitive description: @@ -761,10 +761,10 @@ packages: dependency: transitive description: name: socks5_proxy - sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + sha256: "045cbba84f6e2b01c1c77634a63e926352bf110ef5f07fc462c6d43bbd4b6a83" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5+dev.2" source_gen: dependency: transitive description: @@ -794,7 +794,7 @@ packages: description: path: "." ref: master - resolved-ref: "0ac9108db2f475c5b685af9eb9df393dcc978820" + resolved-ref: a6b14bcc37ec16f56931e48afa8a8f8e6939431d url: "https://github.com/rafael-xmr/sp_scanner" source: git version: "0.0.1" @@ -870,14 +870,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.0" - uuid: - dependency: transitive - description: - name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" - url: "https://pub.dev" - source: hosted - version: "3.0.7" vector_math: dependency: transitive description: @@ -902,22 +894,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.5" win32: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.5.0" xdg_directories: dependency: transitive description: @@ -938,10 +938,10 @@ packages: dependency: transitive description: name: yaml_edit - sha256: c566f4f804215d84a7a2c377667f546c6033d5b34b4f9e60dfb09d17c4e97826 + sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.10.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.16.6" diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index abfdbfc586..43c1ffb359 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -5,34 +5,34 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" url: "https://pub.dev" source: hosted - version: "64.0.0" + version: "67.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.4.1" args: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" asn1lib: dependency: transitive description: name: asn1lib - sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd" + sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6 url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.5.2" async: dependency: transitive description: @@ -85,18 +85,18 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" url: "https://pub.dev" source: hosted - version: "2.4.8" + version: "2.4.9" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" url: "https://pub.dev" source: hosted - version: "7.2.11" + version: "7.3.0" built_collection: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: built_value - sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.8.1" + version: "8.9.2" characters: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.6" encrypt: dependency: "direct main" description: @@ -197,10 +197,10 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" file: dependency: "direct main" description: @@ -226,10 +226,10 @@ packages: dependency: "direct main" description: name: flutter_mobx - sha256: "4a5d062ff85ed3759f4aac6410ff0ffae32e324b2e71ca722ae1b37b32e865f4" + sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e" url: "https://pub.dev" source: hosted - version: "2.2.0+2" + version: "2.2.1+1" flutter_test: dependency: "direct dev" description: flutter @@ -239,10 +239,10 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" glob: dependency: transitive description: @@ -279,10 +279,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -319,18 +319,18 @@ packages: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.7.1" json_annotation: dependency: transitive description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" leak_tracker: dependency: transitive description: @@ -391,26 +391,26 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" mobx: dependency: "direct main" description: name: mobx - sha256: "74ee54012dc7c1b3276eaa960a600a7418ef5f9997565deb8fca1fd88fb36b78" + sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e" url: "https://pub.dev" source: hosted - version: "2.3.0+1" + version: "2.3.3+2" mobx_codegen: dependency: "direct dev" description: name: mobx_codegen - sha256: b26c7f9c20b38f0ea572c1ed3f29d8e027cb265538bbd1aed3ec198642cfca42 + sha256: "8e0d8653a0c720ad933cd8358f6f89f740ce89203657c13f25bea772ef1fff7c" url: "https://pub.dev" source: hosted - version: "2.6.0+1" + version: "2.6.1" nested: dependency: transitive description: @@ -439,26 +439,26 @@ packages: dependency: "direct main" description: name: path_provider - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.4" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -503,10 +503,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" url: "https://pub.dev" source: hosted - version: "3.7.4" + version: "3.9.1" pool: dependency: transitive description: @@ -519,10 +519,10 @@ packages: dependency: transitive description: name: provider - sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "6.1.2" pub_semver: dependency: transitive description: @@ -564,10 +564,10 @@ packages: dependency: "direct main" description: name: socks5_proxy - sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + sha256: "045cbba84f6e2b01c1c77634a63e926352bf110ef5f07fc462c6d43bbd4b6a83" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5+dev.2" source_gen: dependency: transitive description: @@ -680,22 +680,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.5" win32: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.5.0" xdg_directories: dependency: transitive description: @@ -713,5 +721,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.10.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.16.6" diff --git a/cw_haven/pubspec.lock b/cw_haven/pubspec.lock index 8aeb70a972..63c0d345fe 100644 --- a/cw_haven/pubspec.lock +++ b/cw_haven/pubspec.lock @@ -21,18 +21,18 @@ packages: dependency: transitive description: name: args - sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611" + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.5.0" asn1lib: dependency: transitive description: name: asn1lib - sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039 + sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.2" async: dependency: transitive description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.1" build_config: dependency: transitive description: @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: "direct dev" description: @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: build_runner_core - sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" url: "https://pub.dev" source: hosted - version: "7.2.7" + version: "7.2.10" built_collection: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: built_value - sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725" + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.4.3" + version: "8.9.2" characters: dependency: transitive description: @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: checked_yaml - sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.3" clock: dependency: transitive description: @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.10.0" collection: dependency: transitive description: @@ -165,10 +165,10 @@ packages: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" cw_core: dependency: "direct main" description: @@ -188,10 +188,10 @@ packages: dependency: transitive description: name: encrypt - sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb" + sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.0.3" fake_async: dependency: transitive description: @@ -204,10 +204,10 @@ packages: dependency: "direct main" description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.2" file: dependency: transitive description: @@ -233,10 +233,10 @@ packages: dependency: "direct main" description: name: flutter_mobx - sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e" + sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e" url: "https://pub.dev" source: hosted - version: "2.0.6+5" + version: "2.2.1+1" flutter_test: dependency: "direct dev" description: flutter @@ -246,26 +246,26 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" glob: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" graphs: dependency: transitive description: name: graphs - sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" hive: dependency: transitive description: @@ -286,10 +286,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -326,18 +326,18 @@ packages: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.7.1" json_annotation: dependency: transitive description: name: json_annotation - sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.9.0" leak_tracker: dependency: transitive description: @@ -366,10 +366,10 @@ packages: dependency: transitive description: name: logging - sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" matcher: dependency: transitive description: @@ -398,26 +398,34 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" mobx: dependency: "direct main" description: name: mobx - sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a + sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e" url: "https://pub.dev" source: hosted - version: "2.1.3+1" + version: "2.3.3+2" mobx_codegen: dependency: "direct dev" description: name: mobx_codegen - sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181" + sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" package_config: dependency: transitive description: @@ -438,26 +446,26 @@ packages: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.4" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -470,10 +478,10 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: @@ -486,26 +494,26 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.8" pointycastle: dependency: transitive description: name: pointycastle - sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" url: "https://pub.dev" source: hosted - version: "3.6.2" + version: "3.9.1" pool: dependency: transitive description: @@ -514,46 +522,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: + provider: dependency: transitive description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "4.2.4" + version: "6.1.2" pub_semver: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a" + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.3" shelf: dependency: transitive description: name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -563,10 +571,10 @@ packages: dependency: transitive description: name: socks5_proxy - sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + sha256: "045cbba84f6e2b01c1c77634a63e926352bf110ef5f07fc462c6d43bbd4b6a83" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5+dev.2" source_gen: dependency: transitive description: @@ -651,10 +659,10 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" vector_math: dependency: transitive description: @@ -679,38 +687,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.5" win32: dependency: transitive description: name: win32 - sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "5.5.0" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "0.2.0+3" + version: "1.0.4" yaml: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.7.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.16.6" diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock index adb50bd027..623ad119d3 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -21,18 +21,18 @@ packages: dependency: transitive description: name: args - sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611" + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.5.0" asn1lib: dependency: transitive description: name: asn1lib - sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039 + sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.2" async: dependency: transitive description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.1" build_config: dependency: transitive description: @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: "direct dev" description: @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: build_runner_core - sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" url: "https://pub.dev" source: hosted - version: "7.2.7" + version: "7.2.10" built_collection: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: built_value - sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725" + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.4.3" + version: "8.9.2" characters: dependency: transitive description: @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: checked_yaml - sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.3" clock: dependency: transitive description: @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.10.0" collection: dependency: transitive description: @@ -165,10 +165,10 @@ packages: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" cw_core: dependency: "direct main" description: @@ -188,10 +188,10 @@ packages: dependency: "direct main" description: name: encrypt - sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb" + sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.0.3" fake_async: dependency: transitive description: @@ -204,10 +204,10 @@ packages: dependency: "direct main" description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.2" file: dependency: transitive description: @@ -233,10 +233,10 @@ packages: dependency: "direct main" description: name: flutter_mobx - sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e" + sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e" url: "https://pub.dev" source: hosted - version: "2.0.6+5" + version: "2.2.1+1" flutter_test: dependency: "direct dev" description: flutter @@ -246,34 +246,34 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" glob: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" graphs: dependency: transitive description: name: graphs - sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" hashlib: dependency: transitive description: name: hashlib - sha256: "71bf102329ddb8e50c8a995ee4645ae7f1728bb65e575c17196b4d8262121a96" + sha256: b9e2528c341c8e060f410bf049b18aea06218e77f857562bd18e8cc2e003e467 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.14.0" hashlib_codecs: dependency: transitive description: @@ -302,10 +302,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -342,18 +342,18 @@ packages: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.7.1" json_annotation: dependency: transitive description: name: json_annotation - sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.9.0" leak_tracker: dependency: transitive description: @@ -382,10 +382,10 @@ packages: dependency: transitive description: name: logging - sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" matcher: dependency: transitive description: @@ -414,26 +414,34 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" mobx: dependency: "direct main" description: name: mobx - sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a + sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e" url: "https://pub.dev" source: hosted - version: "2.1.3+1" + version: "2.3.3+2" mobx_codegen: dependency: "direct dev" description: name: mobx_codegen - sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181" + sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" package_config: dependency: transitive description: @@ -454,26 +462,26 @@ packages: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.4" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -486,10 +494,10 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: @@ -502,34 +510,34 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.8" pointycastle: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.9.1" polyseed: dependency: "direct main" description: name: polyseed - sha256: "9b48ec535b10863f78f6354ec983b4cc0c88ca69ff48fee469d0fd1954b01d4f" + sha256: a340962242d7917b0f3e6bd02c4acc3f90eae8ff766f1244f793ae7a6414dd68 url: "https://pub.dev" source: hosted - version: "0.0.2" + version: "0.0.4" pool: dependency: transitive description: @@ -538,46 +546,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: + provider: dependency: transitive description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "4.2.4" + version: "6.1.2" pub_semver: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a" + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.3" shelf: dependency: transitive description: name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -587,10 +595,10 @@ packages: dependency: transitive description: name: socks5_proxy - sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a" + sha256: "045cbba84f6e2b01c1c77634a63e926352bf110ef5f07fc462c6d43bbd4b6a83" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5+dev.2" source_gen: dependency: transitive description: @@ -675,10 +683,10 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" vector_math: dependency: transitive description: @@ -703,38 +711,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.5" win32: dependency: transitive description: name: win32 - sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "5.5.0" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "0.2.0+3" + version: "1.0.4" yaml: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.7.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.16.6" diff --git a/cw_shared_external/pubspec.lock b/cw_shared_external/pubspec.lock deleted file mode 100644 index ef01c9f9ab..0000000000 --- a/cw_shared_external/pubspec.lock +++ /dev/null @@ -1,147 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.5.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.10" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.19" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" -sdks: - dart: ">=2.12.0-0.0 <3.0.0" - flutter: ">=1.20.0" diff --git a/lib/di.dart b/lib/di.dart index 15a72e80c7..610d148317 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -26,9 +26,9 @@ import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; -import 'package:cake_wallet/view_model/link_view_model.dart'; import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart'; +import 'package:cake_wallet/view_model/link_view_model.dart'; import 'package:cw_core/receive_page_option.dart'; import 'package:cake_wallet/entities/qr_view_data.dart'; import 'package:cake_wallet/entities/template.dart'; @@ -123,54 +123,15 @@ import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.d import 'package:cake_wallet/src/screens/support/support_page.dart'; import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart'; import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart'; -import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart'; -import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart'; -import 'package:cake_wallet/themes/theme_list.dart'; -import 'package:cake_wallet/utils/device_info.dart'; -import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; -import 'package:cake_wallet/utils/payment_request.dart'; -import 'package:cake_wallet/utils/responsive_layout_util.dart'; -import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart'; -import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; -import 'package:cake_wallet/view_model/anonpay_details_view_model.dart'; -import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart'; -import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart'; -import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; -import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; -import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart'; -import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart'; -import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart'; -import 'package:cake_wallet/view_model/ionia/ionia_custom_redeem_view_model.dart'; -import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart'; -import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart'; -import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart'; -import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart'; -import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart'; -import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart'; -import 'package:cake_wallet/view_model/seed_type_view_model.dart'; -import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart'; -import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart'; -import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; -import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart'; -import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; -import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart'; -import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; -import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart'; -import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart'; -import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart'; -import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; -import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart'; -import 'package:cw_core/nano_account.dart'; -import 'package:cw_core/unspent_coins_info.dart'; -import 'package:cw_core/wallet_service.dart'; -import 'package:cw_core/transaction_info.dart'; -import 'package:cw_core/node.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; +import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart'; +import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart'; import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart'; +import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/authentication_store.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; @@ -185,6 +146,13 @@ import 'package:cake_wallet/store/templates/exchange_template_store.dart'; import 'package:cake_wallet/store/templates/send_template_store.dart'; import 'package:cake_wallet/store/wallet_list_store.dart'; import 'package:cake_wallet/store/yat/yat_store.dart'; +import 'package:cake_wallet/themes/theme_list.dart'; +import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cake_wallet/utils/payment_request.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; +import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; +import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; +import 'package:cake_wallet/view_model/anonpay_details_view_model.dart'; import 'package:cake_wallet/view_model/auth_view_model.dart'; import 'package:cake_wallet/view_model/backup_view_model.dart'; import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart'; @@ -193,22 +161,45 @@ import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart'; import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_custom_redeem_view_model.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart'; +import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart'; +import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart'; +import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart'; import 'package:cake_wallet/view_model/order_details_view_model.dart'; import 'package:cake_wallet/view_model/rescan_view_model.dart'; +import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart'; import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart'; +import 'package:cake_wallet/view_model/seed_type_view_model.dart'; import 'package:cake_wallet/view_model/send/send_template_view_model.dart'; import 'package:cake_wallet/view_model/send/send_view_model.dart'; +import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart'; +import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart'; import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart'; import 'package:cake_wallet/view_model/support_view_model.dart'; import 'package:cake_wallet/view_model/trade_details_view_model.dart'; @@ -217,15 +208,24 @@ import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_ import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_keys_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_new_vm.dart'; +import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/nano_account.dart'; +import 'package:cw_core/node.dart'; +import 'package:cw_core/transaction_info.dart'; +import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; @@ -689,10 +689,6 @@ Future setup({ getIt.registerFactory(() { final wallet = getIt.get().wallet!; - // if ((wallet.type == WalletType.bitcoin && - // wallet.walletAddresses.addressPageType == btc.AddressType.p2sp) || - // wallet.type == WalletType.monero || - // wallet.type == WalletType.haven) { if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) { return MoneroAccountListViewModel(wallet); } diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index 0ed8795a7c..f6c0450e69 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -1,11 +1,13 @@ import 'dart:math'; import 'package:auto_size_text/auto_size_text.dart'; +import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart'; import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart'; @@ -22,6 +24,7 @@ import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:url_launcher/url_launcher.dart'; class BalancePage extends StatelessWidget { BalancePage({ @@ -243,12 +246,101 @@ class CryptoBalanceWidget extends StatelessWidget { }, ); }, - ) + ), + if (dashboardViewModel.hasSilentPayments) ...[ + SizedBox(height: 10), + Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), + child: DashBoardRoundedCardWidget( + title: S.of(context).silent_payments, + subTitle: S.of(context).enable_silent_payments_scanning, + hint: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => launchUrl( + // TODO: Update URL + Uri.https("guides.cakewallet.com"), + mode: LaunchMode.externalApplication, + ), + child: Row( + children: [ + Text( + S.of(context).what_is_silent_payments, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context) + .extension()! + .labelTextColor, + height: 1, + ), + softWrap: true, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Icon(Icons.help_outline, + size: 16, + color: Theme.of(context) + .extension()! + .labelTextColor), + ) + ], + ), + ), + Observer( + builder: (_) => StandardSwitch( + value: dashboardViewModel.silentPaymentsScanningActive, + onTaped: () => _toggleSilentPaymentsScanning(context), + ), + ) + ], + ), + ], + ), + onTap: () => _toggleSilentPaymentsScanning(context), + icon: Icon( + Icons.lock, + color: Theme.of(context).extension()!.pageTitleTextColor, + size: 50, + ), + ), + ), + ] ], ), ), ); } + + Future _toggleSilentPaymentsScanning(BuildContext context) async { + final isSilentPaymentsScanningActive = dashboardViewModel.silentPaymentsScanningActive; + final newValue = !isSilentPaymentsScanningActive; + + final needsToSwitch = bitcoin!.getNodeIsCakeElectrs(dashboardViewModel.wallet) == false; + + if (needsToSwitch) { + return showPopUp( + context: context, + builder: (BuildContext context) => AlertWithTwoActions( + alertTitle: S.of(context).change_current_node_title, + alertContent: S.of(context).confirm_silent_payments_switch_node, + rightButtonText: S.of(context).ok, + leftButtonText: S.of(context).cancel, + actionRightButton: () { + dashboardViewModel.setSilentPaymentsScanning(newValue); + Navigator.of(context).pop(); + }, + actionLeftButton: () => Navigator.of(context).pop(), + )); + } + + return dashboardViewModel.setSilentPaymentsScanning(newValue); + } } class BalanceRowWidget extends StatelessWidget { diff --git a/lib/src/screens/dashboard/pages/cake_features_page.dart b/lib/src/screens/dashboard/pages/cake_features_page.dart index 89c0435e15..c85ef08f82 100644 --- a/lib/src/screens/dashboard/pages/cake_features_page.dart +++ b/lib/src/screens/dashboard/pages/cake_features_page.dart @@ -78,7 +78,7 @@ class CakeFeaturesPage extends StatelessWidget { fit: BoxFit.cover, ), ), - const SizedBox(height: 20), + SizedBox(height: 10), DashBoardRoundedCardWidget( title: "NanoGPT", subTitle: S.of(context).nanogpt_subtitle, @@ -87,68 +87,6 @@ class CakeFeaturesPage extends StatelessWidget { mode: LaunchMode.externalApplication, ), ), - if (dashboardViewModel.hasSilentPayments) ...[ - SizedBox(height: 10), - DashBoardRoundedCardWidget( - title: S.of(context).silent_payments, - subTitle: S.of(context).enable_silent_payments_scanning, - hint: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => launchUrl( - // TODO: Update URL - Uri.https("guides.cakewallet.com"), - mode: LaunchMode.externalApplication, - ), - child: Row( - children: [ - Text( - S.of(context).what_is_silent_payments, - style: TextStyle( - fontSize: 12, - fontFamily: 'Lato', - fontWeight: FontWeight.w400, - color: Theme.of(context) - .extension()! - .labelTextColor, - height: 1, - ), - softWrap: true, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Icon(Icons.help_outline, - size: 16, - color: Theme.of(context) - .extension()! - .labelTextColor), - ) - ], - ), - ), - Observer( - builder: (_) => StandardSwitch( - value: dashboardViewModel.silentPaymentsScanningActive, - onTaped: () => _toggleSilentPaymentsScanning(context), - ), - ) - ], - ), - ], - ), - onTap: () => _toggleSilentPaymentsScanning(context), - icon: Icon( - Icons.lock, - color: - Theme.of(context).extension()!.pageTitleTextColor, - size: 50, - ), - ), - ] ], ), ), @@ -185,29 +123,4 @@ class CakeFeaturesPage extends StatelessWidget { }); } } - - Future _toggleSilentPaymentsScanning(BuildContext context) async { - final isSilentPaymentsScanningActive = dashboardViewModel.silentPaymentsScanningActive; - final newValue = !isSilentPaymentsScanningActive; - - final needsToSwitch = bitcoin!.getNodeIsCakeElectrs(dashboardViewModel.wallet) == false; - - if (needsToSwitch) { - return showPopUp( - context: context, - builder: (BuildContext context) => AlertWithTwoActions( - alertTitle: S.of(context).change_current_node_title, - alertContent: S.of(context).confirm_silent_payments_switch_node, - rightButtonText: S.of(context).ok, - leftButtonText: S.of(context).cancel, - actionRightButton: () { - dashboardViewModel.setSilentPaymentsScanning(newValue); - Navigator.of(context).pop(); - }, - actionLeftButton: () => Navigator.of(context).pop(), - )); - } - - return dashboardViewModel.setSilentPaymentsScanning(newValue); - } } diff --git a/lib/src/screens/dashboard/pages/market_place_page.dart b/lib/src/screens/dashboard/pages/market_place_page.dart deleted file mode 100644 index d280488446..0000000000 --- a/lib/src/screens/dashboard/pages/market_place_page.dart +++ /dev/null @@ -1,119 +0,0 @@ -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; -import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:url_launcher/url_launcher.dart'; -import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; - -class MarketPlacePage extends StatelessWidget { - MarketPlacePage({ - required this.dashboardViewModel, - required this.marketPlaceViewModel, - }); - - final DashboardViewModel dashboardViewModel; - final MarketPlaceViewModel marketPlaceViewModel; - final _scrollController = ScrollController(); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: RawScrollbar( - thumbColor: Colors.white.withOpacity(0.15), - radius: Radius.circular(20), - thumbVisibility: true, - thickness: 2, - controller: _scrollController, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 50), - Text( - S.of(context).market_place, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.pageTitleTextColor, - ), - ), - Expanded( - child: ListView( - controller: _scrollController, - children: [ - // SizedBox(height: 20), - // DashBoardRoundedCardWidget( - // onTap: () => launchUrl( - // Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"), - // mode: LaunchMode.externalApplication, - // ), - // title: S.of(context).cake_pay_title, - // subTitle: S.of(context).cake_pay_subtitle, - // ), - SizedBox(height: 20), - DashBoardRoundedCardWidget( - title: S.of(context).cake_pay_web_cards_title, - subTitle: S.of(context).cake_pay_web_cards_subtitle, - onTap: () => _launchMarketPlaceUrl("buy.cakepay.com"), - ), - const SizedBox(height: 20), - DashBoardRoundedCardWidget( - title: "NanoGPT", - subTitle: S.of(context).nanogpt_subtitle, - onTap: () => _launchMarketPlaceUrl("cake.nano-gpt.com"), - ), - ], - ), - ), - ], - ), - ), - ), - ); - } - - void _launchMarketPlaceUrl(String url) async { - try { - launchUrl( - Uri.https(url), - mode: LaunchMode.externalApplication, - ); - } catch (e) { - print(e); - } - } - - // TODO: Remove ionia flow/files if we will discard it - void _navigatorToGiftCardsPage(BuildContext context) { - final walletType = dashboardViewModel.type; - - switch (walletType) { - case WalletType.haven: - showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.of(context).error, - alertContent: S.of(context).gift_cards_unavailable, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); - break; - default: - marketPlaceViewModel.isIoniaUserAuthenticated().then((value) { - if (value) { - Navigator.pushNamed(context, Routes.ioniaManageCardsPage); - return; - } - Navigator.of(context).pushNamed(Routes.ioniaWelcomePage); - }); - } - } -} diff --git a/lib/view_model/wallet_address_list/wallet_address_list_item.dart b/lib/view_model/wallet_address_list/wallet_address_list_item.dart index ebcf541073..6a6e341137 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_item.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_item.dart @@ -9,6 +9,7 @@ class WalletAddressListItem extends ListItem { this.txCount, this.balance, this.isChange = false, + // Address that is only ever used once, shouldn't be used to receive funds, copy and paste, share etc this.isOneTimeReceiveAddress = false, }) : super(); @@ -24,4 +25,3 @@ class WalletAddressListItem extends ListItem { @override String toString() => name ?? address; } - From f090d09a651ad0a2a5ca8a958a4edf6095e6a980 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Wed, 8 May 2024 10:32:45 -0300 Subject: [PATCH 50/75] refactor: unchanged files [skip ci] --- cw_bitcoin/lib/bitcoin_wallet.dart | 33 +++++++++++++++--------------- lib/di.dart | 2 +- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 3091cacf54..02a9125e11 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -41,23 +41,22 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { List? initialSilentAddresses, int initialSilentAddressIndex = 0, }) : super( - mnemonic: mnemonic, - passphrase: passphrase, - xpub: xpub, - password: password, - walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfo, - networkType: networkParam == null - ? bitcoin.bitcoin - : networkParam == BitcoinNetwork.mainnet - ? bitcoin.bitcoin - : bitcoin.testnet, - initialAddresses: initialAddresses, - initialBalance: initialBalance, - seedBytes: seedBytes, - currency: - networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc, - ) { + mnemonic: mnemonic, + passphrase: passphrase, + xpub: xpub, + password: password, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfo, + networkType: networkParam == null + ? bitcoin.bitcoin + : networkParam == BitcoinNetwork.mainnet + ? bitcoin.bitcoin + : bitcoin.testnet, + initialAddresses: initialAddresses, + initialBalance: initialBalance, + seedBytes: seedBytes, + currency: + networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc) { // in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here) // the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here) // String derivationPath = walletInfo.derivationInfo!.derivationPath!; diff --git a/lib/di.dart b/lib/di.dart index 610d148317..338c599b08 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -26,9 +26,9 @@ import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; +import 'package:cake_wallet/view_model/link_view_model.dart'; import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart'; -import 'package:cake_wallet/view_model/link_view_model.dart'; import 'package:cw_core/receive_page_option.dart'; import 'package:cake_wallet/entities/qr_view_data.dart'; import 'package:cake_wallet/entities/template.dart'; From 8e5d997562c66075c96d3636a508e9f02d43ac76 Mon Sep 17 00:00:00 2001 From: Rafael Saes Date: Thu, 9 May 2024 17:06:39 -0300 Subject: [PATCH 51/75] fix: scan fixes, add date, allow sending while scanning --- cw_bitcoin/lib/electrum_wallet.dart | 312 ++++++++---------- cw_bitcoin/pubspec.lock | 2 +- cw_core/lib/get_height_by_date.dart | 11 +- cw_core/lib/wallet_info.dart | 35 +- lib/entities/default_settings_migration.dart | 1 - .../on_wallet_sync_status_change.dart | 35 +- .../screens/dashboard/pages/balance_page.dart | 1 + lib/src/widgets/dashboard_card_widget.dart | 4 +- lib/view_model/send/send_view_model.dart | 5 +- 9 files changed, 191 insertions(+), 215 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 2d17222131..8f924eacc9 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -37,6 +37,7 @@ import 'package:cw_core/utils/file.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:cw_core/get_height_by_date.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:http/http.dart' as http; @@ -359,7 +360,7 @@ abstract class ElectrumWalletBase } syncStatus = message.syncStatus; - walletInfo.restoreHeight = message.height; + await walletInfo.updateRestoreHeight(message.height); } } } @@ -381,6 +382,8 @@ abstract class ElectrumWalletBase await updateBalance(); await updateFeeRates(); + + syncStatus = SyncedSyncStatus(); } catch (e, stacktrace) { print(stacktrace); print(e.toString()); @@ -1141,7 +1144,8 @@ abstract class ElectrumWalletBase coin.isFrozen = coinInfo.isFrozen; coin.isSending = coinInfo.isSending; coin.note = coinInfo.note; - coin.bitcoinAddressRecord.balance += coinInfo.value; + if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) + coin.bitcoinAddressRecord.balance += coinInfo.value; } else { _addCoinInfo(coin); } @@ -1172,7 +1176,8 @@ abstract class ElectrumWalletBase coin.isFrozen = coinInfo.isFrozen; coin.isSending = coinInfo.isSending; coin.note = coinInfo.note; - coin.bitcoinAddressRecord.balance += coinInfo.value; + if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord) + coin.bitcoinAddressRecord.balance += coinInfo.value; } else { _addCoinInfo(coin); } @@ -1588,7 +1593,6 @@ abstract class ElectrumWalletBase Future updateTransactions() async { try { if (_isTransactionUpdating) { - _isTransactionUpdating = false; return; } @@ -1734,7 +1738,7 @@ abstract class ElectrumWalletBase _currentChainTip = height; if (_currentChainTip != null && _currentChainTip! > 0 && walletInfo.restoreHeight == 0) { - walletInfo.restoreHeight = _currentChainTip!; + await walletInfo.updateRestoreHeight(_currentChainTip!); } }); } @@ -1818,202 +1822,156 @@ class SyncResponse { } Future startRefresh(ScanData scanData) async { - Future getElectrumConnection() async { - final electrumClient = scanData.electrumClient; - if (!electrumClient.isConnected) { - final node = scanData.node; - await electrumClient.connectToUri(node.uri, useSSL: node.useSSL); - } - return electrumClient; - } - - var lastKnownBlockHeight = 0; - var initialSyncHeight = 0; - - var syncHeight = scanData.height; - var currentChainTip = scanData.chainTip; - - if (syncHeight <= 0) { - syncHeight = currentChainTip; - } - - if (initialSyncHeight <= 0) { - initialSyncHeight = syncHeight; - } - - if (lastKnownBlockHeight == syncHeight) { - scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus())); - return; - } + int syncHeight = scanData.height; + int initialSyncHeight = syncHeight; BehaviorSubject? tweaksSubscription = null; - lastKnownBlockHeight = syncHeight; - - SyncingSyncStatus syncingStatus; - if (scanData.isSingleScan) { - syncingStatus = SyncingSyncStatus(1, 0); - } else { - syncingStatus = - SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, syncHeight); - } + final syncingStatus = scanData.isSingleScan + ? SyncingSyncStatus(1, 0) + : SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, syncHeight); + // Initial status UI update, send how many blocks left to scan scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus)); - if (syncingStatus.blocksLeft <= 0 || (scanData.isSingleScan && scanData.height != syncHeight)) { - scanData.sendPort.send(SyncResponse(scanData.chainTip, SyncedSyncStatus())); - return; - } + final electrumClient = scanData.electrumClient; + await electrumClient.connectToUri(scanData.node.uri, useSSL: scanData.node.useSSL); + + if (tweaksSubscription == null) { + final count = scanData.isSingleScan ? 1 : TWEAKS_COUNT; + final receiver = Receiver( + scanData.silentAddress.b_scan.toHex(), + scanData.silentAddress.B_spend.toHex(), + scanData.network == BitcoinNetwork.testnet, + scanData.labelIndexes, + scanData.labelIndexes.length, + ); + + tweaksSubscription = await electrumClient.tweaksSubscribe(height: syncHeight, count: count); + tweaksSubscription?.listen((t) async { + final tweaks = t as Map; - try { - final electrumClient = await getElectrumConnection(); + if (tweaks["message"] != null) { + // re-subscribe to continue receiving messages + electrumClient.tweaksSubscribe(height: syncHeight, count: count); + return; + } - if (tweaksSubscription == null) { - final count = scanData.isSingleScan ? 1 : TWEAKS_COUNT; + final blockHeight = tweaks.keys.first; + final tweakHeight = int.parse(blockHeight); try { - tweaksSubscription = await electrumClient.tweaksSubscribe(height: syncHeight, count: count); + final blockTweaks = tweaks[blockHeight] as Map; - tweaksSubscription?.listen((t) async { - final tweaks = t as Map; + for (var j = 0; j < blockTweaks.keys.length; j++) { + final txid = blockTweaks.keys.elementAt(j); + final details = blockTweaks[txid] as Map; + final outputPubkeys = (details["output_pubkeys"] as Map); + final tweak = details["tweak"].toString(); - if (tweaks["message"] != null && !scanData.isSingleScan) { - // re-subscribe to continue receiving messages - electrumClient.tweaksSubscribe(height: syncHeight, count: count); - return; - } + try { + // scanOutputs called from rust here + final addToWallet = scanOutputs( + outputPubkeys.values.toList(), + tweak, + receiver, + ); + + if (addToWallet.isEmpty) { + // no results tx, continue to next tx + continue; + } - final blockHeight = tweaks.keys.first; - final tweakHeight = int.parse(blockHeight); + // placeholder ElectrumTransactionInfo object to update values based on new scanned unspent(s) + final txInfo = ElectrumTransactionInfo( + WalletType.bitcoin, + id: txid, + height: tweakHeight, + amount: 0, + fee: 0, + direction: TransactionDirection.incoming, + isPending: false, + date: scanData.network == BitcoinNetwork.mainnet + ? getDateByBitcoinHeight(tweakHeight) + : DateTime.now(), + confirmations: scanData.chainTip - tweakHeight + 1, + unspents: [], + ); + + addToWallet.forEach((label, value) { + (value as Map).forEach((output, tweak) { + final t_k = tweak.toString(); + + final receivingOutputAddress = ECPublic.fromHex(output) + .toTaprootAddress(tweak: false) + .toAddress(scanData.network); + + int? amount; + int? pos; + outputPubkeys.entries.firstWhere((k) { + final isMatchingOutput = k.value[0] == output; + if (isMatchingOutput) { + amount = int.parse(k.value[1].toString()); + pos = int.parse(k.key.toString()); + return true; + } + return false; + }); - try { - final blockTweaks = tweaks[blockHeight] as Map; - - for (var j = 0; j < blockTweaks.keys.length; j++) { - final txid = blockTweaks.keys.elementAt(j); - final details = blockTweaks[txid] as Map; - final outputPubkeys = (details["output_pubkeys"] as Map); - final tweak = details["tweak"].toString(); - - try { - // scanOutputs called from rust here - final addToWallet = scanOutputs( - outputPubkeys.values.map((o) => o[0].toString()).toList(), - tweak, - Receiver( - scanData.silentAddress.b_scan.toHex(), - scanData.silentAddress.B_spend.toHex(), - scanData.network == BitcoinNetwork.testnet, - scanData.labelIndexes, - scanData.labelIndexes.length, - ), + final receivedAddressRecord = BitcoinSilentPaymentAddressRecord( + receivingOutputAddress, + index: 0, + isHidden: false, + isUsed: true, + network: scanData.network, + silentPaymentTweak: t_k, + type: SegwitAddresType.p2tr, + txCount: 1, + balance: amount!, ); - if (addToWallet.isEmpty) { - // no results tx, continue to next tx - continue; - } - - // placeholder ElectrumTransactionInfo object to update values based on new scanned unspent(s) - final txInfo = ElectrumTransactionInfo( - WalletType.bitcoin, - id: txid, - height: tweakHeight, - amount: 0, - fee: 0, - direction: TransactionDirection.incoming, - isPending: false, - date: DateTime.now(), - confirmations: scanData.chainTip - tweakHeight + 1, - unspents: [], + final unspent = BitcoinSilentPaymentsUnspent( + receivedAddressRecord, + txid, + amount!, + pos!, + silentPaymentTweak: t_k, + silentPaymentLabel: label == "None" ? null : label, ); - addToWallet.forEach((label, value) { - (value as Map).forEach((output, tweak) { - final t_k = tweak.toString(); - - final receivingOutputAddress = ECPublic.fromHex(output) - .toTaprootAddress(tweak: false) - .toAddress(scanData.network); - - final receivedAddressRecord = BitcoinSilentPaymentAddressRecord( - receivingOutputAddress, - index: 0, - isHidden: false, - isUsed: true, - network: scanData.network, - silentPaymentTweak: t_k, - type: SegwitAddresType.p2tr, - txCount: 1, - ); - - int? amount; - int? pos; - outputPubkeys.entries.firstWhere((k) { - final isMatchingOutput = k.value[0] == output; - if (isMatchingOutput) { - amount = int.parse(k.value[1].toString()); - pos = int.parse(k.key.toString()); - return true; - } - return false; - }); - - final unspent = BitcoinSilentPaymentsUnspent( - receivedAddressRecord, - txid, - amount!, - pos!, - silentPaymentTweak: t_k, - silentPaymentLabel: label == "None" ? null : label, - ); - - txInfo.unspents!.add(unspent); - txInfo.amount += unspent.value; - }); - }); + txInfo.unspents!.add(unspent); + txInfo.amount += unspent.value; + }); + }); - scanData.sendPort.send({txInfo.id: txInfo}); - } catch (_) {} - } + scanData.sendPort.send({txInfo.id: txInfo}); } catch (_) {} - - syncHeight = tweakHeight; - scanData.sendPort.send( - SyncResponse( - syncHeight, - SyncingSyncStatus.fromHeightValues( - currentChainTip, - initialSyncHeight, - syncHeight, - ), - ), - ); - - if (int.parse(blockHeight) >= scanData.chainTip || scanData.isSingleScan) { - scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus())); - await tweaksSubscription!.close(); - } - }); - } catch (e) { - if (e is RequestFailedTimeoutException) { - return scanData.sendPort.send( - SyncResponse(syncHeight, TimedOutSyncStatus()), - ); } - } - } + } catch (_) {} - if (tweaksSubscription == null) { - return scanData.sendPort.send( - SyncResponse(syncHeight, UnsupportedSyncStatus()), + syncHeight = tweakHeight; + scanData.sendPort.send( + SyncResponse( + syncHeight, + SyncingSyncStatus.fromHeightValues( + scanData.chainTip, + initialSyncHeight, + syncHeight, + ), + ), ); - } - } catch (e, stacktrace) { - print(stacktrace); - print(e.toString()); - scanData.sendPort.send(SyncResponse(syncHeight, NotConnectedSyncStatus())); + if (tweakHeight >= scanData.chainTip || scanData.isSingleScan) { + scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus())); + await tweaksSubscription!.close(); + } + }); + } + + if (tweaksSubscription == null) { + return scanData.sendPort.send( + SyncResponse(syncHeight, UnsupportedSyncStatus()), + ); } } diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 85c7bd7f7f..fe2d0c2af1 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -794,7 +794,7 @@ packages: description: path: "." ref: master - resolved-ref: a6b14bcc37ec16f56931e48afa8a8f8e6939431d + resolved-ref: "4977a0d31fc8614d27193b07d92c5992d163131e" url: "https://github.com/rafael-xmr/sp_scanner" source: git version: "0.0.1" diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index 11bc370c5b..3d23d24af7 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -245,6 +245,7 @@ Future getHavenCurrentHeight() async { // Data taken from https://timechaincalendar.com/ const bitcoinDates = { + "2024-05": 841590, "2024-04": 837182, "2024-03": 832623, "2024-02": 828319, @@ -265,7 +266,9 @@ const bitcoinDates = { int getBitcoinHeightByDate({required DateTime date}) { String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}'; - int startBlock = bitcoinDates[dateKey] ?? bitcoinDates.values.last; + final closestKey = bitcoinDates.keys + .firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => bitcoinDates.keys.last); + int startBlock = bitcoinDates[dateKey] ?? bitcoinDates[closestKey]!; DateTime startOfMonth = DateTime(date.year, date.month); int daysDifference = date.difference(startOfMonth).inDays; @@ -275,3 +278,9 @@ int getBitcoinHeightByDate({required DateTime date}) { return startBlock + estimatedBlocksSinceStartOfMonth; } + +DateTime getDateByBitcoinHeight(int height) { + final date = bitcoinDates.entries + .lastWhere((entry) => entry.value >= height, orElse: () => bitcoinDates.entries.last); + return formatMapKey(date.key); +} diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart index 57cdad81b0..ff0c011bbe 100644 --- a/cw_core/lib/wallet_info.dart +++ b/cw_core/lib/wallet_info.dart @@ -66,21 +66,21 @@ class DerivationInfo extends HiveObject { @HiveType(typeId: WalletInfo.typeId) class WalletInfo extends HiveObject { WalletInfo( - this.id, - this.name, - this.type, - this.isRecovery, - this.restoreHeight, - this.timestamp, - this.dirPath, - this.path, - this.address, - this.yatEid, - this.yatLastUsedAddressRaw, - this.showIntroCakePayCard, - this.derivationInfo, - this.hardwareWalletType, - ): _yatLastUsedAddressController = StreamController.broadcast(); + this.id, + this.name, + this.type, + this.isRecovery, + this.restoreHeight, + this.timestamp, + this.dirPath, + this.path, + this.address, + this.yatEid, + this.yatLastUsedAddressRaw, + this.showIntroCakePayCard, + this.derivationInfo, + this.hardwareWalletType, + ) : _yatLastUsedAddressController = StreamController.broadcast(); factory WalletInfo.external({ required String id, @@ -207,4 +207,9 @@ class WalletInfo extends HiveObject { Stream get yatLastUsedAddressStream => _yatLastUsedAddressController.stream; StreamController _yatLastUsedAddressController; + + Future updateRestoreHeight(int height) async { + restoreHeight = height; + await save(); + } } diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 44a1fdd468..3c9670f11b 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -36,7 +36,6 @@ const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002'; const nanoDefaultNodeUri = 'rpc.nano.to'; const nanoDefaultPowNodeUri = 'rpc.nano.to'; const solanaDefaultNodeUri = 'rpc.ankr.com'; -const tronDefaultNodeUri = 'api.trongrid.io'; const tronDefaultNodeUri = 'tron-rpc.publicnode.com:443'; const newCakeWalletBitcoinUri = '198.58.111.154:50001'; diff --git a/lib/reactions/on_wallet_sync_status_change.dart b/lib/reactions/on_wallet_sync_status_change.dart index 9a13db5979..a52d4edf57 100644 --- a/lib/reactions/on_wallet_sync_status_change.dart +++ b/lib/reactions/on_wallet_sync_status_change.dart @@ -12,28 +12,27 @@ import 'package:wakelock_plus/wakelock_plus.dart'; ReactionDisposer? _onWalletSyncStatusChangeReaction; void startWalletSyncStatusChangeReaction( - WalletBase, - TransactionInfo> wallet, + WalletBase, TransactionInfo> wallet, FiatConversionStore fiatConversionStore) { _onWalletSyncStatusChangeReaction?.reaction.dispose(); - _onWalletSyncStatusChangeReaction = - reaction((_) => wallet.syncStatus, (SyncStatus status) async { - try { - if (status is ConnectedSyncStatus) { - await wallet.startSync(); + _onWalletSyncStatusChangeReaction = reaction((_) => wallet.syncStatus, (SyncStatus status) async { + if (!(status is SyncingSyncStatus) || wallet.type != WalletType.bitcoin) + try { + if (status is ConnectedSyncStatus) { + await wallet.startSync(); - if (wallet.type == WalletType.haven) { - await updateHavenRate(fiatConversionStore); + if (wallet.type == WalletType.haven) { + await updateHavenRate(fiatConversionStore); + } } + if (status is SyncingSyncStatus) { + await WakelockPlus.enable(); + } + if (status is SyncedSyncStatus || status is FailedSyncStatus) { + await WakelockPlus.disable(); + } + } catch (e) { + print(e.toString()); } - if (status is SyncingSyncStatus) { - await WakelockPlus.enable(); - } - if (status is SyncedSyncStatus || status is FailedSyncStatus) { - await WakelockPlus.disable(); - } - } catch(e) { - print(e.toString()); - } }); } diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index f6c0450e69..5e96effab6 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -252,6 +252,7 @@ class CryptoBalanceWidget extends StatelessWidget { Padding( padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), child: DashBoardRoundedCardWidget( + customBorder: 30, title: S.of(context).silent_payments, subTitle: S.of(context).enable_silent_payments_scanning, hint: Column( diff --git a/lib/src/widgets/dashboard_card_widget.dart b/lib/src/widgets/dashboard_card_widget.dart index b0831c7cf9..5a8ca14a49 100644 --- a/lib/src/widgets/dashboard_card_widget.dart +++ b/lib/src/widgets/dashboard_card_widget.dart @@ -13,6 +13,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget { this.svgPicture, this.icon, this.onClose, + this.customBorder, }); final VoidCallback onTap; @@ -22,6 +23,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget { final Widget? hint; final SvgPicture? svgPicture; final Icon? icon; + final double? customBorder; @override Widget build(BuildContext context) { @@ -37,7 +39,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget { width: double.infinity, decoration: BoxDecoration( color: Theme.of(context).extension()!.syncedBackgroundColor, - borderRadius: BorderRadius.circular(20), + borderRadius: BorderRadius.circular(customBorder ?? 20), border: Border.all( color: Theme.of(context).extension()!.cardBorderColor, ), diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 635d43d53e..f9c3dd9228 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -218,7 +218,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor isFiatDisabled ? '' : pendingTransactionFeeFiatAmount + ' ' + fiat.title; @computed - bool get isReadyForSend => wallet.syncStatus is SyncedSyncStatus; + bool get isReadyForSend => + wallet.syncStatus is SyncedSyncStatus || + // If silent payments scanning, can still send payments + (wallet.type == WalletType.bitcoin && wallet.syncStatus is SyncingSyncStatus); @computed List