diff --git a/cw_bitcoin/lib/electrum_balance.dart b/cw_bitcoin/lib/electrum_balance.dart index 37c34058b..aeb06f1f0 100644 --- a/cw_bitcoin/lib/electrum_balance.dart +++ b/cw_bitcoin/lib/electrum_balance.dart @@ -65,6 +65,6 @@ class ElectrumBalance extends Balance { 'unconfirmed': unconfirmed, 'frozen': frozen, 'secondConfirmed': secondConfirmed, - 'secondUnconfirmed': secondUnconfirmed + 'secondUnconfirmed': secondUnconfirmed, }); } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index fd778571f..14fd1d3a9 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -4,6 +4,8 @@ import 'dart:io'; import 'dart:isolate'; import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:cw_bitcoin/bitcoin_amount_format.dart'; +import 'package:cw_core/format_amount.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_bitcoin/bitcoin_wallet.dart'; import 'package:cw_bitcoin/litecoin_wallet.dart'; @@ -2240,10 +2242,11 @@ abstract class ElectrumWalletBase 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; + } } }); }); @@ -2499,6 +2502,12 @@ abstract class ElectrumWalletBase transactionHistory.addOne(tx); } } + + @override + String formatCryptoAmount(String amount) { + final amountInt = int.parse(amount); + return bitcoinAmountToString(amount: amountInt); + } } class ScanNode { diff --git a/cw_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart index e15dca89b..0ca7ff2b1 100644 --- a/cw_core/lib/wallet_base.dart +++ b/cw_core/lib/wallet_base.dart @@ -36,6 +36,8 @@ abstract class WalletBase get balance; + String formatCryptoAmount(String amount) => amount; + SyncStatus get syncStatus; set syncStatus(SyncStatus status); diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index eff5ae909..00e50f37f 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -3,6 +3,7 @@ import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; +import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/account.dart'; @@ -905,4 +906,9 @@ abstract class MoneroWalletBase extends WalletBase with AutomaticKeepAliveClientMixin output.setSendAll(sendViewModel.balance)), + allAmountCallback: () async => output.setSendAll(sendViewModel.sendingBalance)), Divider( height: 1, color: Theme.of(context).extension()!.textFieldHintColor), @@ -266,7 +266,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin Navigator.of(context).pushNamed( - Routes.unspentCoinsList, - arguments: widget.sendViewModel.coinTypeToSpendFrom, - ), + onTap: () async { + await Navigator.of(context).pushNamed( + Routes.unspentCoinsList, + arguments: widget.sendViewModel.coinTypeToSpendFrom, + ); + if (mounted) { + // we just got back from the unspent coins list screen, so we need to recompute the sending balance: + sendViewModel.updateSendingBalance(); + } + }, child: Container( color: Colors.transparent, child: Row( @@ -505,7 +511,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin sendViewModel.selectedCryptoCurrency, (Currency currency) { if (output.sendAll) { - output.setSendAll(sendViewModel.balance); + output.setSendAll(sendViewModel.sendingBalance); } output.setCryptoAmount(cryptoAmountController.text); diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 27bef933b..4ee9cb86f 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -225,6 +225,42 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor return wallet.balance[selectedCryptoCurrency]!.formattedFullAvailableBalance; } + @action + Future updateSendingBalance() async { + // force the sendingBalance to recompute since unspent coins aren't observable + // or at least mobx can't detect the changes + + final currentType = coinTypeToSpendFrom; + + if (currentType == UnspentCoinType.any) { + coinTypeToSpendFrom = UnspentCoinType.nonMweb; + } else if (currentType == UnspentCoinType.nonMweb) { + coinTypeToSpendFrom = UnspentCoinType.any; + } else if (currentType == UnspentCoinType.mweb) { + coinTypeToSpendFrom = UnspentCoinType.nonMweb; + } + + // set it back to the original value: + coinTypeToSpendFrom = currentType; + } + + @computed + String get sendingBalance { + // only for electrum, monero, wownero, decred wallets atm: + switch (wallet.type) { + case WalletType.bitcoin: + case WalletType.litecoin: + case WalletType.bitcoinCash: + case WalletType.monero: + case WalletType.wownero: + case WalletType.decred: + return wallet.formatCryptoAmount( + unspentCoinsListViewModel.getSendingBalance(coinTypeToSpendFrom).toString()); + default: + return balance; + } + } + @computed bool get isFiatDisabled => balanceViewModel.isFiatDisabled; @@ -502,14 +538,14 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor return bitcoin!.createBitcoinTransactionCredentials( outputs, priority: priority!, - feeRate:feesViewModel. customBitcoinFeeRate, + feeRate: feesViewModel.customBitcoinFeeRate, coinTypeToSpendFrom: coinTypeToSpendFrom, ); case WalletType.litecoin: return bitcoin!.createBitcoinTransactionCredentials( outputs, priority: priority!, - feeRate:feesViewModel. customBitcoinFeeRate, + feeRate: feesViewModel.customBitcoinFeeRate, // if it's an exchange flow then disable sending from mweb coins coinTypeToSpendFrom: provider != null ? UnspentCoinType.nonMweb : coinTypeToSpendFrom, ); 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 d4fadb2f1..a54e2be49 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 @@ -131,6 +131,36 @@ abstract class UnspentCoinsListViewModelBase with Store { } } + List _getSpecificUnspents(UnspentCoinType overrideCoinTypeToSpendFrom) { + switch (wallet.type) { + case WalletType.monero: + return monero!.getUnspents(wallet); + case WalletType.wownero: + return wownero!.getUnspents(wallet); + case WalletType.bitcoin: + case WalletType.litecoin: + case WalletType.bitcoinCash: + return bitcoin!.getUnspents(wallet, coinTypeToSpendFrom: overrideCoinTypeToSpendFrom); + case WalletType.decred: + return decred!.getUnspents(wallet); + default: + return List.empty(); + } + } + + @action + int getSendingBalance(UnspentCoinType overrideCoinTypeToSpendFrom) { + // return items.where((element) => element.isSending).fold(0, (previousValue, element) => previousValue + element.value); + // go through all unspent coins and add up the value minus frozen and non sending: + int total = 0; + + for (final item in _getSpecificUnspents(overrideCoinTypeToSpendFrom)) { + if (item.isFrozen || !item.isSending) continue; + total += item.value; + } + return total; + } + @action void _updateUnspentCoinsInfo() { items.clear();