diff --git a/cw_bitcoin/lib/electrum_balance.dart b/cw_bitcoin/lib/electrum_balance.dart index 3367511e7..4e37f40b1 100644 --- a/cw_bitcoin/lib/electrum_balance.dart +++ b/cw_bitcoin/lib/electrum_balance.dart @@ -36,7 +36,8 @@ class ElectrumBalance extends Balance { int secondUnconfirmed = 0; @override - String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed - frozen); + String get formattedAvailableBalance => + bitcoinAmountToString(amount: confirmed - frozen); @override String get formattedAdditionalBalance => bitcoinAmountToString(amount: unconfirmed); diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 2027a3263..5d5dd9b6e 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -116,6 +116,7 @@ abstract class ElectrumWalletBase switch (currency) { case CryptoCurrency.btc: case CryptoCurrency.ltc: + case CryptoCurrency.tbtc: return Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath( _hardenedDerivationPath(derivationInfo?.derivationPath ?? electrum_path)) as Bip32Slip10Secp256k1; diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 942f4427e..a5d393206 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -4,6 +4,7 @@ import 'package:convert/convert.dart' as convert; import 'dart:math'; import 'package:collection/collection.dart'; import 'package:crypto/crypto.dart'; +import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/mweb_utxo.dart'; import 'package:cw_mweb/mwebd.pbgrpc.dart'; @@ -98,9 +99,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { late final Box mwebUtxosBox; Timer? _syncTimer; Timer? _feeRatesTimer; + Timer? _processingTimer; StreamSubscription? _utxoStream; late RpcClient _stub; late bool mwebEnabled; + bool processingUtxos = false; List get scanSecret => mwebHd.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw; List get spendSecret => mwebHd.childKey(Bip32KeyIndex(0x80000001)).privateKey.privKey.raw; @@ -243,7 +246,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @action @override Future startSync() async { - print("STARTING SYNC @@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); if (syncStatus is SyncronizingSyncStatus) { return; } @@ -251,8 +253,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { try { syncStatus = SyncronizingSyncStatus(); await subscribeForUpdates(); + updateFeeRates(); - await updateFeeRates(); _feeRatesTimer?.cancel(); _feeRatesTimer = Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); @@ -317,7 +319,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // prevent unnecessary reaction triggers: if (syncStatus is! SyncedSyncStatus) { - syncStatus = SyncedSyncStatus(); + // mwebd is synced, but we could still be processing incoming utxos: + if (!processingUtxos) { + syncStatus = SyncedSyncStatus(); + } } return; } @@ -454,12 +459,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { return; } - // if our address isn't in the inputs, update the txCount: - final inputAddresses = tx.inputAddresses ?? []; - if (!inputAddresses.contains(utxo.address)) { - addressRecord.txCount++; - } - + // update the txCount: + addressRecord.txCount++; addressRecord.balance += utxo.value.toInt(); addressRecord.setAsUsed(); } @@ -493,6 +494,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // we're processing utxos, so our balance could still be innacurate: if (syncStatus is! SyncronizingSyncStatus && syncStatus is! SyncingSyncStatus) { syncStatus = SyncronizingSyncStatus(); + processingUtxos = true; + _processingTimer?.cancel(); + _processingTimer = Timer.periodic(const Duration(seconds: 2), (timer) async { + processingUtxos = false; + timer.cancel(); + }); } final utxo = MwebUtxo( @@ -535,7 +542,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // check if any of the pending outgoing transactions are now confirmed: bool updatedAny = false; for (final tx in pendingOutgoingTransactions) { - updatedAny = await checkPendingTransaction(tx) || updatedAny; + updatedAny = await isConfirmed(tx) || updatedAny; } // get output ids of all the mweb utxos that have > 0 height: @@ -600,7 +607,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } // checks if a pending transaction is now confirmed, and updates the tx info accordingly: - Future checkPendingTransaction(ElectrumTransactionInfo tx) async { + Future isConfirmed(ElectrumTransactionInfo tx) async { if (!mwebEnabled) return false; if (!tx.isPending) return false; @@ -696,6 +703,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } await getStub(); + // update unspent balances: + await updateUnspent(); + int confirmed = balance.confirmed; int unconfirmed = balance.unconfirmed; int confirmedMweb = 0; @@ -713,9 +723,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } catch (_) {} - // update unspent balances: - await updateUnspent(); - for (var addressRecord in walletAddresses.allAddresses) { addressRecord.balance = 0; addressRecord.txCount = 0; @@ -815,6 +822,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } if (outputs.length == 1 && outputs[0].toOutput.amount == BigInt.zero) { + // TODO: for some reason we can't type cast BitcoinScriptOutput to BitcoinBaseOutput (even though one implements the other) + // this breaks using the ALL button on litecoin mweb tx's: outputs = [ BitcoinScriptOutput( script: outputs[0].toOutput.scriptPubKey, value: utxos.sumOfUtxosValue()) @@ -876,7 +885,29 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { )); final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx)); + // check if the transaction doesn't contain any mweb inputs or outputs: + final transactionCredentials = credentials as BitcoinTransactionCredentials; + + bool hasMwebInput = false; + bool hasMwebOutput = false; + + for (final output in transactionCredentials.outputs) { + if (output.extractedAddress?.toLowerCase().contains("mweb") ?? false) { + hasMwebOutput = true; + break; + } + } + + if (tx2.mwebBytes != null && tx2.mwebBytes!.isNotEmpty) { + hasMwebInput = true; + } + + if (!hasMwebInput && !hasMwebOutput) { + return tx; + } + // check if any of the inputs of this transaction are hog-ex: + // this list is only non-mweb inputs: tx2.inputs.forEach((txInput) { bool isHogEx = true; @@ -957,6 +988,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { _utxoStream?.cancel(); _feeRatesTimer?.cancel(); _syncTimer?.cancel(); + _processingTimer?.cancel(); await stopSync(); await super.close(); } diff --git a/cw_core/lib/balance.dart b/cw_core/lib/balance.dart index 09ca8efbb..7350c80f1 100644 --- a/cw_core/lib/balance.dart +++ b/cw_core/lib/balance.dart @@ -13,6 +13,5 @@ abstract class Balance { String get formattedUnAvailableBalance => ''; String get formattedSecondAvailableBalance => ''; String get formattedSecondAdditionalBalance => ''; - String get formattedFullAvailableBalance => ''; - String get formattedFullUnAvailableBalance => ''; + String get formattedFullAvailableBalance => formattedAvailableBalance; } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 4edaad872..79f473eb3 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -217,16 +217,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor PendingTransaction? pendingTransaction; @computed - String get balance { - String fullFormattedBalance = - wallet.balance[selectedCryptoCurrency]!.formattedFullAvailableBalance; - String formattedAvailableBalance = - wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance; - if (fullFormattedBalance.isNotEmpty) { - return fullFormattedBalance; - } - return formattedAvailableBalance; - } + String get balance => wallet.balance[selectedCryptoCurrency]!.formattedFullAvailableBalance; @computed bool get isFiatDisabled => balanceViewModel.isFiatDisabled; 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 b8274ce7c..17fc4b849 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 @@ -409,7 +409,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo }); addressList.addAll(receivedAddressItems); } else { - final addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) { + var addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) { final isPrimary = subaddress.id == 0; return WalletAddressListItem( @@ -422,6 +422,16 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo walletTypeToCryptoCurrency(type), subaddress.balance), isChange: subaddress.isChange); }); + + // don't show all 1000+ mweb addresses: + if (wallet.type == WalletType.litecoin && addressItems.length >= 1000) { + // find the index of the last item with a txCount > 0 + final addressItemsList = addressItems.toList(); + final lastItemWithTxCount = addressItemsList.lastWhere((item) => (item.txCount ?? 0) > 0); + final index = addressItemsList.indexOf(lastItemWithTxCount); + // show only up to that index + 20: + addressItems = addressItemsList.sublist(0, index + 20); + } addressList.addAll(addressItems); } }