diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 4fc4c1ad8..1f5c369e3 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -235,7 +235,7 @@ class ElectrumClient { return []; }); - Future>> getListUnspent(String scriptHash) async { + Future>?> getListUnspent(String scriptHash) async { final result = await call(method: 'blockchain.scripthash.listunspent', params: [scriptHash]); if (result is List) { @@ -248,7 +248,7 @@ class ElectrumClient { }).toList(); } - return []; + return null; } Future>> getMempool(String scriptHash) => diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index b3b336249..76c14dc4e 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1359,6 +1359,7 @@ abstract class ElectrumWalletBase @action Future updateAllUnspents() async { List updatedUnspentCoins = []; + final previousUnspentCoins = List.from(unspentCoins); if (hasSilentPaymentsScanning) { // Update unspents stored from scanned silent payment transactions @@ -1376,13 +1377,27 @@ abstract class ElectrumWalletBase if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0; }); - await Future.wait(walletAddresses.allAddresses + final addressFutures = walletAddresses.allAddresses .where((element) => element.type != SegwitAddresType.mweb) - .map((address) async { - updatedUnspentCoins.addAll(await fetchUnspent(address)); - })); + .map((address) => fetchUnspent(address)) + .toList(); - unspentCoins = updatedUnspentCoins; + final results = await Future.wait(addressFutures); + final failedCount = results.where((result) => result == null).length; + + if (failedCount == 0) { + for (final result in results) { + updatedUnspentCoins.addAll(result!); + } + unspentCoins = updatedUnspentCoins; + } else { + unspentCoins = handleFailedUtxoFetch( + failedCount: failedCount, + previousUnspentCoins: previousUnspentCoins, + updatedUnspentCoins: updatedUnspentCoins, + results: results, + ); + } final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => element.walletId == id); @@ -1395,6 +1410,38 @@ abstract class ElectrumWalletBase await _refreshUnspentCoinsInfo(); } + List handleFailedUtxoFetch({ + required int failedCount, + required List previousUnspentCoins, + required List updatedUnspentCoins, + required List?> results, + }) { + + if (failedCount == results.length) { + printV("All UTXOs failed to fetch, falling back to previous UTXOs"); + return previousUnspentCoins; + } + + final successfulUtxos = []; + for (final result in results) { + if (result != null) { + successfulUtxos.addAll(result); + } + } + + if (failedCount > 0 && successfulUtxos.isEmpty) { + printV("Some UTXOs failed, but no successful UTXOs, falling back to previous UTXOs"); + return previousUnspentCoins; + } + + if (failedCount > 0) { + printV("Some UTXOs failed, updating with successful UTXOs"); + updatedUnspentCoins.addAll(successfulUtxos); + } + + return updatedUnspentCoins; + } + Future updateCoins(List newUnspentCoins) async { if (newUnspentCoins.isEmpty) { return; @@ -1426,15 +1473,17 @@ abstract class ElectrumWalletBase @action Future updateUnspentsForAddress(BitcoinAddressRecord address) async { final newUnspentCoins = await fetchUnspent(address); - await updateCoins(newUnspentCoins); + await updateCoins(newUnspentCoins ?? []); } @action - Future> fetchUnspent(BitcoinAddressRecord address) async { - List> unspents = []; + Future?> fetchUnspent(BitcoinAddressRecord address) async { List updatedUnspentCoins = []; - unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); + final unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); + + // Failed to fetch unspents + if (unspents == null) return null; await Future.wait(unspents.map((unspent) async { try {