Cw 853 mweb utxos are not displayed in coin control screen (#1873)

* fix displaying mweb utxos * fallback for failed electrum UTXO updates * remove throwing uncaught exception and un needed try/catch --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>

Serhii committed Dec 14, 2024 at 02:37 UTC d55e635f61c2f91798bc0c19fd31ffc0e813774b
3 files changed +47 -47
cw_bitcoin/lib/electrum.dart
+12 -12
@@ -235,21 +235,21 @@ class ElectrumClient {
235 return [];
236 });
237
238 - Future<List<Map<String, dynamic>>> getListUnspent(String scriptHash) =>
239 - call(method: 'blockchain.scripthash.listunspent', params: [scriptHash])
240 - .then((dynamic result) {
241 - if (result is List) {
242 - return result.map((dynamic val) {
243 - if (val is Map<String, dynamic>) {
244 - return val;
245 - }
238 + Future<List<Map<String, dynamic>>> getListUnspent(String scriptHash) async {
239 + final result = await call(method: 'blockchain.scripthash.listunspent', params: [scriptHash]);
240
247 - return <String, dynamic>{};
248 - }).toList();
241 + if (result is List) {
242 + return result.map((dynamic val) {
243 + if (val is Map<String, dynamic>) {
244 + return val;
245 }
246
251 - return [];
252 - });
247 + return <String, dynamic>{};
248 + }).toList();
249 + }
250 +
251 + return [];
252 + }
253
254 Future<List<Map<String, dynamic>>> getMempool(String scriptHash) =>
255 call(method: 'blockchain.scripthash.get_mempool', params: [scriptHash])
cw_bitcoin/lib/electrum_wallet.dart
+34 -34
@@ -1382,8 +1382,9 @@ abstract class ElectrumWalletBase
1382 }));
1383
1384 unspentCoins = updatedUnspentCoins;
1385 -
1386 - final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => element.walletId == id);
1385 +
1386 + final currentWalletUnspentCoins =
1387 + unspentCoinsInfo.values.where((element) => element.walletId == id);
1388
1389 if (currentWalletUnspentCoins.length != updatedUnspentCoins.length) {
1390 unspentCoins.forEach((coin) => addCoinInfo(coin));
@@ -1450,10 +1451,9 @@ abstract class ElectrumWalletBase
1451
1452 @action
1453 Future<void> addCoinInfo(BitcoinUnspent coin) async {
1453 -
1454 // Check if the coin is already in the unspentCoinsInfo for the wallet
1455 - final existingCoinInfo = unspentCoinsInfo.values.firstWhereOrNull(
1456 - (element) => element.walletId == walletInfo.id && element == coin);
1455 + final existingCoinInfo = unspentCoinsInfo.values
1456 + .firstWhereOrNull((element) => element.walletId == walletInfo.id && element == coin);
1457
1458 if (existingCoinInfo == null) {
1459 final newInfo = UnspentCoinsInfo(
@@ -1475,19 +1475,18 @@ abstract class ElectrumWalletBase
1475
1476 Future<void> _refreshUnspentCoinsInfo() async {
1477 try {
1478 - final List<dynamic> keys = <dynamic>[];
1478 + final List<dynamic> keys = [];
1479 final currentWalletUnspentCoins =
1480 - unspentCoinsInfo.values.where((element) => element.walletId.contains(id));
1480 + unspentCoinsInfo.values.where((record) => record.walletId == id);
1481
1482 - if (currentWalletUnspentCoins.isNotEmpty) {
1483 - currentWalletUnspentCoins.forEach((element) {
1484 - final existUnspentCoins = unspentCoins
1485 - .where((coin) => element.hash.contains(coin.hash) && element.vout == coin.vout);
1482 + for (final element in currentWalletUnspentCoins) {
1483 + if (RegexUtils.addressTypeFromStr(element.address, network) is MwebAddress) continue;
1484
1487 - if (existUnspentCoins.isEmpty) {
1488 - keys.add(element.key);
1489 - }
1490 - });
1485 + final existUnspentCoins = unspentCoins.where((coin) => element == coin);
1486 +
1487 + if (existUnspentCoins.isEmpty) {
1488 + keys.add(element.key);
1489 + }
1490 }
1491
1492 if (keys.isNotEmpty) {
@@ -1499,7 +1498,8 @@ abstract class ElectrumWalletBase
1498 }
1499
1500 Future<void> cleanUpDuplicateUnspentCoins() async {
1502 - final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => element.walletId == id);
1501 + final currentWalletUnspentCoins =
1502 + unspentCoinsInfo.values.where((element) => element.walletId == id);
1503 final Map<String, UnspentCoinsInfo> uniqueUnspentCoins = {};
1504 final List<dynamic> duplicateKeys = [];
1505
@@ -1535,7 +1535,8 @@ abstract class ElectrumWalletBase
1535 final ownAddresses = walletAddresses.allAddresses.map((addr) => addr.address).toSet();
1536
1537 final receiverAmount = outputs
1538 - .where((output) => !ownAddresses.contains(addressFromOutputScript(output.scriptPubKey, network)))
1538 + .where((output) =>
1539 + !ownAddresses.contains(addressFromOutputScript(output.scriptPubKey, network)))
1540 .fold<int>(0, (sum, output) => sum + output.amount.toInt());
1541
1542 if (receiverAmount == 0) {
@@ -1584,7 +1585,7 @@ abstract class ElectrumWalletBase
1585 allInputsAmount += outTransaction.amount.toInt();
1586
1587 final addressRecord =
1587 - walletAddresses.allAddresses.firstWhere((element) => element.address == address);
1588 + walletAddresses.allAddresses.firstWhere((element) => element.address == address);
1589 final btcAddress = RegexUtils.addressTypeFromStr(addressRecord.address, network);
1590 final privkey = generateECPrivate(
1591 hd: addressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
@@ -1602,7 +1603,7 @@ abstract class ElectrumWalletBase
1603 scriptType: _getScriptType(btcAddress),
1604 ),
1605 ownerDetails:
1605 - UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: btcAddress),
1606 + UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: btcAddress),
1607 ),
1608 );
1609 }
@@ -1630,7 +1631,7 @@ abstract class ElectrumWalletBase
1631
1632 // Calculate the total amount and fees
1633 int totalOutAmount =
1633 - outputs.fold<int>(0, (previousValue, output) => previousValue + output.value.toInt());
1634 + outputs.fold<int>(0, (previousValue, output) => previousValue + output.value.toInt());
1635 int currentFee = allInputsAmount - totalOutAmount;
1636 int remainingFee = newFee - currentFee;
1637
@@ -1686,7 +1687,7 @@ abstract class ElectrumWalletBase
1687 vout: utxo.vout,
1688 scriptType: _getScriptType(address)),
1689 ownerDetails:
1689 - UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: address),
1690 + UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: address),
1691 ));
1692
1693 allInputsAmount += utxo.value;
@@ -1743,11 +1744,11 @@ abstract class ElectrumWalletBase
1744 final changeAddresses = walletAddresses.allAddresses.where((element) => element.isHidden);
1745 final List<BitcoinOutput> changeOutputs = outputs
1746 .where((output) => changeAddresses
1746 - .any((element) => element.address == output.address.toAddress(network)))
1747 + .any((element) => element.address == output.address.toAddress(network)))
1748 .toList();
1749
1750 int totalChangeAmount =
1750 - changeOutputs.fold<int>(0, (sum, output) => sum + output.value.toInt());
1751 + changeOutputs.fold<int>(0, (sum, output) => sum + output.value.toInt());
1752
1753 // The final amount that the receiver will receive
1754 int sendingAmount = allInputsAmount - newFee - totalChangeAmount;
@@ -1764,7 +1765,7 @@ abstract class ElectrumWalletBase
1765
1766 final transaction = txb.buildTransaction((txDigest, utxo, publicKey, sighash) {
1767 final key =
1767 - privateKeys.firstWhereOrNull((element) => element.getPublic().toHex() == publicKey);
1768 + privateKeys.firstWhereOrNull((element) => element.getPublic().toHex() == publicKey);
1769 if (key == null) {
1770 throw Exception("Cannot find private key");
1771 }
@@ -1774,7 +1775,6 @@ abstract class ElectrumWalletBase
1775 } else {
1776 return key.signInput(txDigest, sigHash: sighash);
1777 }
1777 -
1778 });
1779
1780 return PendingBitcoinTransaction(
@@ -1787,16 +1787,16 @@ abstract class ElectrumWalletBase
1787 hasChange: changeOutputs.isNotEmpty,
1788 feeRate: newFee.toString(),
1789 )..addListener((transaction) async {
1790 - transactionHistory.transactions.values.forEach((tx) {
1791 - if (tx.id == hash) {
1792 - tx.isReplaced = true;
1793 - tx.isPending = false;
1794 - transactionHistory.addOne(tx);
1795 - }
1790 + transactionHistory.transactions.values.forEach((tx) {
1791 + if (tx.id == hash) {
1792 + tx.isReplaced = true;
1793 + tx.isPending = false;
1794 + transactionHistory.addOne(tx);
1795 + }
1796 + });
1797 + transactionHistory.addOne(transaction);
1798 + await updateBalance();
1799 });
1797 - transactionHistory.addOne(transaction);
1798 - await updateBalance();
1799 - });
1800 } catch (e) {
1801 throw e;
1802 }
lib/src/screens/unspent_coins/unspent_coins_list_page.dart
+1 -1
@@ -154,7 +154,7 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
154 SizedBox(height: 15),
155 Expanded(
156 child: unspentCoinsListViewModel.items.isEmpty
157 - ? Center(child: Text('No unspent coins available\ntry to reconnect',textAlign: TextAlign.center))
157 + ? Center(child: Text('No unspent coins available',textAlign: TextAlign.center))
158 : ListView.separated(
159 itemCount: unspentCoinsListViewModel.items.length,
160 separatorBuilder: (_, __) => SizedBox(height: 15),