CW-1260: Enable Swap all for EVM wallets and persist amount when currency changes (#2828)
* feat: Enable Swap all for EVM wallets and persist amount when currency changes * Update local * ffeat: Recalculate amounts when swapping all and currency changes * revert formatting * fix: Review comments * refactor: streamline currency change logic * refactor: simplify currency change handling * fix: handle amount and gas estimation for swap xyz trades for swap all * Revert "fix: handle amount and gas estimation for swap xyz trades for swap all" This reverts commit 408c9dbeffdc82e08e9349d0f14b229031060c62. * feat: exclude specific providers for swap all --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
David Adegoke committed
Apr 30, 2026 at 19:17 UTC
2651260fe4a7ba054de9a27336920a5e8570c4ae
3 files changed
+138
-23
lib/new-ui/pages/swap_page.dart
-2
@@ -401,8 +401,6 @@ class _NewSwapPageState extends State<NewSwapPage> {
401
address: isCurrentTypeWallet
402
? exchangeViewModel.wallet.walletAddresses.addressForExchange
403
: '');
404
-
405
- key.currentState!.changeAmount(amount: '');
404
}
405
406
void _onWalletNameChange(ExchangeViewModel exchangeViewModel, CryptoCurrency currency,
lib/src/screens/exchange/exchange_page.dart
-2
@@ -636,8 +636,6 @@ class ExchangePage extends BasePage {
636
key.currentState!.changeAddress(
637
address:
638
isCurrentTypeWallet ? exchangeViewModel.wallet.walletAddresses.addressForExchange : '');
639
-
640
- key.currentState!.changeAmount(amount: '');
639
}
640
641
void _onWalletNameChange(ExchangeViewModel exchangeViewModel, CryptoCurrency currency,
lib/view_model/exchange/exchange_view_model.dart
+138
-19
@@ -48,6 +48,7 @@ import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
48
import 'package:cake_wallet/store/dashboard/trades_store.dart';
49
import 'package:cake_wallet/store/settings_store.dart';
50
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
51
+import 'package:cake_wallet/evm/evm.dart';
52
import 'package:cake_wallet/reactions/wallet_connect.dart';
53
import 'package:cake_wallet/utils/feature_flag.dart';
54
import 'package:cake_wallet/utils/token_utilities.dart';
@@ -250,6 +251,20 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
251
}
252
return true;
253
});
254
+ } else if (isEVMCompatibleChain(wallet.type)) {
255
+ final currency = depositCurrency;
256
+ final balanceCurrency = wallet.balance.keys.firstWhereOrNull(
257
+ (c) =>
258
+ c.title == depositCurrency.title &&
259
+ (c.tag == depositCurrency.tag || c.tag == depositCurrency.title),
260
+ );
261
+ final balanceForCurrency =
262
+ balanceCurrency != null ? wallet.balance[balanceCurrency] : null;
263
+ if (depositCurrency == currency && balanceForCurrency != null) {
264
+ depositAvailableAmount = _appStore.amountParsingProxy
265
+ .getDisplayCryptoStringFromBigInt(
266
+ balanceForCurrency.fullAvailableBalance, balanceCurrency!);
267
+ }
268
} else {
269
final currency = depositCurrency;
270
final amount = _appStore.amountParsingProxy.getDisplayCryptoString(
@@ -478,9 +493,20 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
493
494
@computed
495
String? get balanceDisplay {
481
- final bal = wallet.balance[depositCurrency]?.fullAvailableBalance;
482
- if(bal == null) return null;
483
- return amountParsingProxy.getDisplayCryptoStringFromBigInt(bal, depositCurrency);
496
+ CryptoCurrency? balanceCurrency;
497
+ if (isEVMCompatibleChain(wallet.type)) {
498
+ balanceCurrency = wallet.balance.keys.firstWhereOrNull(
499
+ (c) =>
500
+ c.title == depositCurrency.title &&
501
+ (c.tag == depositCurrency.tag || c.tag == depositCurrency.title),
502
+ );
503
+ } else {
504
+ balanceCurrency = depositCurrency;
505
+ }
506
+ final bal = balanceCurrency != null ? wallet.balance[balanceCurrency]?.fullAvailableBalance : null;
507
+ if (bal == null) return null;
508
+ return amountParsingProxy.getDisplayCryptoStringFromBigInt(
509
+ bal, balanceCurrency ?? depositCurrency);
510
}
511
512
//* Still open to further optimize these checks
@@ -515,15 +541,23 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
541
return priority;
542
}
543
518
- bool get hasAllAmount =>
519
- [
520
- WalletType.monero,
521
- WalletType.bitcoin,
522
- WalletType.litecoin,
523
- WalletType.bitcoinCash,
524
- WalletType.dogecoin,
525
- ].contains(wallet.type) &&
526
- (depositCurrency == wallet.currency);
544
+ bool get hasAllAmount {
545
+ if ([
546
+ WalletType.monero,
547
+ WalletType.bitcoin,
548
+ WalletType.litecoin,
549
+ WalletType.bitcoinCash,
550
+ WalletType.dogecoin,
551
+ ].contains(wallet.type)) return (depositCurrency == wallet.currency);
552
+
553
+ if (!isEVMCompatibleChain(wallet.type)) return false;
554
+
555
+ if (depositCurrency == wallet.currency) return true;
556
+
557
+ return wallet.balance.keys.any((c) =>
558
+ c.title == depositCurrency.title &&
559
+ (c.tag == depositCurrency.tag || c.tag == depositCurrency.title));
560
+ }
561
562
bool get isMoneroWallet => wallet.type == WalletType.monero;
563
@@ -706,26 +740,38 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
740
741
@action
742
void changeDepositCurrency({required CryptoCurrency currency}) {
743
+ final previousCurrency = depositCurrency;
744
+ final wasSendAllEnabled = isSendAllEnabled;
745
+
746
depositCurrency = currency;
747
isFixedRateMode = false;
711
- _onPairChange();
748
isDepositAddressEnabled = !useSameWalletAddress(depositCurrency);
749
+
750
+ if (previousCurrency != currency) {
751
+ _onPairChange(clearBoth: wasSendAllEnabled);
752
+ }
753
fetchFiatPrice(currency);
754
updateDepositAvailableAmount();
755
}
756
757
@action
758
void changeReceiveCurrency({required CryptoCurrency currency}) {
759
+ final previousCurrency = receiveCurrency;
760
+
761
if (!(currency.tag == receiveCurrency.tag ||
762
currency.tag == receiveCurrency.title ||
763
currency.title == receiveCurrency.tag ||
764
currency == receiveCurrency)) {
765
receiveAddress = "";
766
}
767
+
768
receiveCurrency = currency;
769
isFixedRateMode = false;
727
- _onPairChange();
770
isDepositAddressEnabled = !useSameWalletAddress(depositCurrency);
771
+
772
+ if (previousCurrency != currency) {
773
+ _onPairChange();
774
+ }
775
fetchFiatPrice(currency);
776
}
777
@@ -836,6 +882,11 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
882
isReceiveAmount: isFixedRateMode);
883
}
884
885
+ bool _excludeProviderForSwapAll(ExchangeProvider provider) =>
886
+ isSendAllEnabled &&
887
+ (provider.description == ExchangeProviderDescription.swapsXyz ||
888
+ provider.description == ExchangeProviderDescription.nearIntents);
889
+
890
Future<void> calculateBestRate() async {
891
if (depositCurrency == receiveCurrency) {
892
bestRate = 0.0;
@@ -846,6 +897,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
897
initialAmountByAssets(isFixedRateMode ? receiveCurrency : depositCurrency);
898
899
final validProvidersForAmount = _tradeAvailableProviders.where((provider) {
900
+ if (_excludeProviderForSwapAll(provider)) return false;
901
+
902
final limits = _providerLimits[provider];
903
904
if (limits == null) return false;
@@ -962,7 +1015,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
1015
1016
if (lowestMin != double.maxFinite) {
1017
limits = Limits(min: lowestMin, max: highestMax);
965
-
1018
limitsState = LimitsLoadedSuccessfully(limits: limits);
1019
} else {
1020
limitsState = LimitsLoadedFailure(error: 'Limits loading failed');
@@ -1081,6 +1133,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
1133
1134
printV('createTrade: trying provider=${provider.title}');
1135
1136
+ if (_excludeProviderForSwapAll(provider)) {
1137
+ printV('Skipping Swaps.xyz or Near Intents for swap all');
1138
+ continue;
1139
+ }
1140
// should not happen but just as an extra check
1141
if (isFixedRateMode && provider.supportsFixedRate == false) {
1142
continue;
@@ -1254,6 +1310,56 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
1310
final amount = await unspentCoinsListViewModel.getSendingBalance(UnspentCoinType.any);
1311
1312
changeDepositAmount(amount: wallet.currency.formatAmount(BigInt.from(amount)), isCanonical: true);
1313
+ } else if (isEVMCompatibleChain(wallet.type)) {
1314
+ final balanceCurrency = wallet.balance.keys.firstWhereOrNull(
1315
+ (currency) =>
1316
+ currency.title == depositCurrency.title &&
1317
+ (currency.tag == depositCurrency.tag || currency.tag == depositCurrency.title),
1318
+ );
1319
+
1320
+ final balanceForCurrency = balanceCurrency != null ? wallet.balance[balanceCurrency] : null;
1321
+ if (balanceForCurrency == null) {
1322
+ changeDepositAmount(amount: wallet.currency.formatAmount(BigInt.zero), isCanonical: true);
1323
+ return;
1324
+ }
1325
+
1326
+ final balanceAmount = _appStore.amountParsingProxy.getDisplayCryptoStringFromBigInt(balanceForCurrency.fullAvailableBalance, balanceCurrency!);
1327
+ final balanceDouble = double.tryParse(balanceAmount.replaceAll(',', '.')) ?? 0.0;
1328
+ if (balanceDouble <= 0) {
1329
+ changeDepositAmount(amount: wallet.currency.formatAmount(BigInt.zero), isCanonical: true);
1330
+ return;
1331
+ }
1332
+
1333
+ final isNative = depositCurrency == wallet.currency;
1334
+
1335
+ if (!isNative) {
1336
+ changeDepositAmount(amount: balanceAmount, isCanonical: true);
1337
+ return;
1338
+ }
1339
+
1340
+ try {
1341
+ final priority = _settingsStore.getPriority(wallet.type, chainId: wallet.chainId);
1342
+ await wallet.updateEstimatedFeesParams(priority);
1343
+
1344
+ String? feeString;
1345
+ if (isEVMCompatibleChain(wallet.type)) {
1346
+ feeString = evm!.getEVMNativeEstimatedFee(wallet);
1347
+ }
1348
+
1349
+ if (feeString == null || feeString.isEmpty) {
1350
+ changeDepositAmount(amount: balanceAmount, isCanonical: true);
1351
+ return;
1352
+ }
1353
+
1354
+ final balanceWei = balanceForCurrency.fullAvailableBalance;
1355
+ final feeWei = BigInt.parse(feeString);
1356
+ final amountAfterFeeWei = balanceWei > feeWei ? balanceWei - feeWei : BigInt.zero;
1357
+ changeDepositAmount(
1358
+ amount: wallet.currency.formatAmount(amountAfterFeeWei), isCanonical: true);
1359
+ } catch (e) {
1360
+ printV('Error calculating send all for EVM: $e');
1361
+ changeDepositAmount(amount: balanceAmount, isCanonical: true);
1362
+ }
1363
}
1364
}
1365
@@ -1293,13 +1399,26 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
1399
void removeTemplate({required ExchangeTemplate template}) =>
1400
_exchangeTemplateStore.remove(template: template);
1401
1296
- void _onPairChange() {
1297
- _depositAmount = '';
1298
- _receiveAmount = '';
1402
+ void _onPairChange({bool clearBoth = false}) {
1403
+ if (clearBoth) {
1404
+ _depositAmount = '';
1405
+ _receiveAmount = '';
1406
+ if (!hasAllAmount) isSendAllEnabled = false;
1407
+ } else {
1408
+ if (isFixedRateMode) {
1409
+ _depositAmount = '';
1410
+ } else {
1411
+ _receiveAmount = '';
1412
+ }
1413
+ }
1414
bestRate = 0.0;
1415
bestRateProvider = null;
1416
_sortedAvailableProviders.clear();
1302
- loadLimits();
1417
+ loadLimits().then((_) {
1418
+ if (clearBoth && hasAllAmount) {
1419
+ calculateDepositAllAmount();
1420
+ }
1421
+ });
1422
_setAvailableProviders();
1423
}
1424