feat: allow all for no-fixed monero swaps (#2353)
* feat: allow all for no-fixed monero swaps * fix: deposit amount calculation logic and remove unused print statements * feat: integrate UnspentCoinsListViewModel into ExchangeViewModel for more streamlined unspent coins management * fix: unpopulating the fiat field due to a race condition * fix: ReceiveAmount not updating on all if bestRate changes --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
Konstantin Ullrich committed
Jul 9, 2025 at 16:08 UTC
0a3c51206c99557ddafec1b2b37ee819a6000858
4 files changed
+58
-44
lib/di.dart
+1
@@ -521,6 +521,7 @@ Future<void> setup({
521
getIt.get<AppStore>().settingsStore,
522
getIt.get<SharedPreferences>(),
523
getIt.get<ContactListViewModel>(),
524
+ getIt.get<UnspentCoinsListViewModel>(),
525
getIt.get<FeesViewModel>(),
526
),
527
);
lib/src/screens/exchange/exchange_page.dart
+11
-8
@@ -6,7 +6,6 @@ import 'package:cake_wallet/di.dart';
6
import 'package:cake_wallet/src/screens/exchange/widgets/desktop_exchange_cards_section.dart';
7
import 'package:cake_wallet/src/screens/exchange/widgets/mobile_exchange_cards_section.dart';
8
import 'package:cake_wallet/src/widgets/add_template_button.dart';
9
-import 'package:cake_wallet/themes/core/material_base_theme.dart';
9
import 'package:cake_wallet/utils/debounce.dart';
10
import 'package:cake_wallet/utils/payment_request.dart';
11
import 'package:cake_wallet/utils/responsive_layout_util.dart';
@@ -417,12 +416,12 @@ class ExchangePage extends BasePage {
416
417
reaction(
418
(_) => exchangeViewModel.wallet.name,
420
- (String _) =>
419
+ (_) =>
420
_onWalletNameChange(exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
421
422
reaction(
423
(_) => exchangeViewModel.wallet.name,
425
- (String _) =>
424
+ (_) =>
425
_onWalletNameChange(exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
426
427
reaction((_) => exchangeViewModel.receiveCurrency,
@@ -432,8 +431,10 @@ class ExchangePage extends BasePage {
431
(CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey));
432
433
reaction((_) => exchangeViewModel.depositAmount, (String amount) {
435
- if (depositKey.currentState!.amountController.text != amount && amount != S.of(context).all) {
436
- depositKey.currentState!.amountController.text = amount;
434
+ if (exchangeViewModel.isSendAllEnabled) {
435
+ depositAmountController.text = S.of(context).all;
436
+ } else if (depositAmountController.text != amount && amount != S.of(context).all) {
437
+ depositAmountController.text = amount;
438
}
439
});
440
@@ -521,7 +522,10 @@ class ExchangePage extends BasePage {
522
if (exchangeViewModel.isFixedRateMode) {
523
exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text);
524
} else {
524
- exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
525
+ if (depositAmountController.text == S.current.all)
526
+ exchangeViewModel.changeDepositAmount(amount: exchangeViewModel.depositAmount);
527
+ else
528
+ exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
529
}
530
});
531
@@ -642,8 +646,7 @@ class ExchangePage extends BasePage {
646
Future<String> fetchParsedAddress(
647
BuildContext context, String domain, CryptoCurrency currency) async {
648
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
645
- final address = await extractAddressFromParsed(context, parsedAddress);
646
- return address;
649
+ return extractAddressFromParsed(context, parsedAddress);
650
}
651
652
void _showFeeAlert(BuildContext context) async {
lib/src/screens/send/widgets/send_card.dart
+1
-4
@@ -528,10 +528,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
528
});
529
530
reaction((_) => output.sendAll, (bool all) {
531
- if (all) {
532
- cryptoAmountController.text = S.current.all;
533
- fiatAmountController.text = '';
534
- }
531
+ if (all) cryptoAmountController.text = S.current.all;
532
});
533
534
reaction((_) => sendViewModel.selectedCryptoCurrency, (Currency currency) async {
lib/view_model/exchange/exchange_view_model.dart
+45
-32
@@ -3,25 +3,9 @@ import 'dart:collection';
3
import 'dart:convert';
4
5
import 'package:bitcoin_base/bitcoin_base.dart';
6
-import 'package:cake_wallet/core/create_trade_result.dart';
7
-import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
8
-import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
9
-import 'package:cw_core/utils/proxy_wrapper.dart';
10
-import 'package:cake_wallet/view_model/send/fees_view_model.dart';
11
-import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
12
-import 'package:cw_core/crypto_currency.dart';
13
-import 'package:cw_core/sync_status.dart';
14
-import 'package:cw_core/transaction_priority.dart';
15
-import 'package:cw_core/unspent_coin_type.dart';
16
-import 'package:cw_core/utils/print_verbose.dart';
17
-import 'package:cw_core/wallet_type.dart';
18
-import 'package:hive/hive.dart';
19
-import 'package:intl/intl.dart';
20
-import 'package:mobx/mobx.dart';
21
-import 'package:shared_preferences/shared_preferences.dart';
22
-
6
import 'package:cake_wallet/.secrets.g.dart' as secrets;
7
import 'package:cake_wallet/bitcoin/bitcoin.dart';
8
+import 'package:cake_wallet/core/create_trade_result.dart';
9
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
10
import 'package:cake_wallet/entities/exchange_api_mode.dart';
11
import 'package:cake_wallet/entities/preferences_key.dart';
@@ -31,13 +15,17 @@ import 'package:cake_wallet/exchange/exchange_template.dart';
15
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
16
import 'package:cake_wallet/exchange/limits.dart';
17
import 'package:cake_wallet/exchange/limits_state.dart';
18
+import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
19
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
20
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
21
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
37
-import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart';
22
+import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart';
23
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
24
+import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
25
+import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart';
26
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
27
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
28
+import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
29
import 'package:cake_wallet/exchange/trade.dart';
30
import 'package:cake_wallet/exchange/trade_request.dart';
31
import 'package:cake_wallet/generated/i18n.dart';
@@ -47,6 +35,19 @@ import 'package:cake_wallet/store/settings_store.dart';
35
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
36
import 'package:cake_wallet/utils/feature_flag.dart';
37
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
38
+import 'package:cake_wallet/view_model/send/fees_view_model.dart';
39
+import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
40
+import 'package:cw_core/crypto_currency.dart';
41
+import 'package:cw_core/sync_status.dart';
42
+import 'package:cw_core/transaction_priority.dart';
43
+import 'package:cw_core/unspent_coin_type.dart';
44
+import 'package:cw_core/utils/print_verbose.dart';
45
+import 'package:cw_core/utils/proxy_wrapper.dart';
46
+import 'package:cw_core/wallet_type.dart';
47
+import 'package:hive/hive.dart';
48
+import 'package:intl/intl.dart';
49
+import 'package:mobx/mobx.dart';
50
+import 'package:shared_preferences/shared_preferences.dart';
51
52
part 'exchange_view_model.g.dart';
53
@@ -67,6 +68,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
68
this._settingsStore,
69
this.sharedPreferences,
70
this.contactListViewModel,
71
+ this.unspentCoinsListViewModel,
72
this.feesViewModel,
73
) : _cryptoNumberFormat = NumberFormat(),
74
isSendAllEnabled = false,
@@ -100,6 +102,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
102
];
103
_initialPairBasedOnWallet();
104
105
+ unspentCoinsListViewModel.initialSetup().then((_) {
106
+ unspentCoinsListViewModel.resetUnspentCoinsInfoSelections();
107
+ });
108
+
109
final Map<String, dynamic> exchangeProvidersSelection =
110
json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
111
as Map<String, dynamic>;
@@ -160,13 +166,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
166
}
167
168
bool get isElectrumWallet =>
163
- wallet.type == WalletType.bitcoin ||
164
- wallet.type == WalletType.litecoin ||
165
- wallet.type == WalletType.bitcoinCash;
169
+ [WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type);
170
171
bool get hideAddressAfterExchange =>
168
- wallet.type == WalletType.monero ||
169
- wallet.type == WalletType.wownero;
172
+ [WalletType.monero, WalletType.wownero].contains(wallet.type);
173
174
bool _useTorOnly;
175
final Box<Trade> trades;
@@ -187,7 +190,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
190
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
191
];
192
190
-
193
@observable
194
ExchangeProvider? provider;
195
@@ -306,9 +308,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
308
}
309
310
bool get hasAllAmount =>
309
- (wallet.type == WalletType.bitcoin ||
310
- wallet.type == WalletType.litecoin ||
311
- wallet.type == WalletType.bitcoinCash) &&
311
+ [
312
+ WalletType.monero,
313
+ WalletType.bitcoin,
314
+ WalletType.litecoin,
315
+ WalletType.bitcoinCash,
316
+ ].contains(wallet.type) &&
317
depositCurrency == wallet.currency;
318
319
bool get isMoneroWallet => wallet.type == WalletType.monero;
@@ -323,6 +328,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
328
329
final ContactListViewModel contactListViewModel;
330
331
+ final UnspentCoinsListViewModel unspentCoinsListViewModel;
332
+
333
final FeesViewModel feesViewModel;
334
335
@observable
@@ -468,7 +475,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
475
if (depositCurrency == receiveCurrency) {
476
limitsState = LimitsLoadedSuccessfully(limits: Limits(min: 0, max: 0));
477
return;
471
- };
478
+ }
479
if (selectedProviders.isEmpty) return;
480
481
limitsState = LimitsIsLoading();
@@ -661,9 +668,11 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
668
669
@action
670
Future<void> calculateDepositAllAmount() async {
664
- if (wallet.type == WalletType.litecoin ||
665
- wallet.type == WalletType.bitcoin ||
666
- wallet.type == WalletType.bitcoinCash) {
671
+ if ([
672
+ WalletType.litecoin,
673
+ WalletType.bitcoin,
674
+ WalletType.bitcoinCash,
675
+ ].contains(wallet.type)) {
676
final priority = _settingsStore.priority[wallet.type]!;
677
678
final amount = await bitcoin!.estimateFakeSendAllTxAmount(
@@ -673,7 +682,11 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
682
wallet.type == WalletType.litecoin ? UnspentCoinType.nonMweb : UnspentCoinType.any,
683
);
684
676
- changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
685
+ changeDepositAmount(amount: wallet.formatCryptoAmount(amount.toString()));
686
+ } else if (wallet.type == WalletType.monero) {
687
+ final amount = await unspentCoinsListViewModel.getSendingBalance(UnspentCoinType.any);
688
+
689
+ changeDepositAmount(amount: wallet.formatCryptoAmount(amount.toString()));
690
}
691
}
692