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>
This commit is contained in:
David Adegoke
2026-04-30 20:17:21 +02:00
committed by GitHub
co-authored by Omar Hatem
parent 40c4118b66
commit 2651260fe4
3 changed files with 138 additions and 23 deletions
-2
View File
@@ -401,8 +401,6 @@ class _NewSwapPageState extends State<NewSwapPage> {
address: isCurrentTypeWallet
? exchangeViewModel.wallet.walletAddresses.addressForExchange
: '');
key.currentState!.changeAmount(amount: '');
}
void _onWalletNameChange(ExchangeViewModel exchangeViewModel, CryptoCurrency currency,
@@ -636,8 +636,6 @@ class ExchangePage extends BasePage {
key.currentState!.changeAddress(
address:
isCurrentTypeWallet ? exchangeViewModel.wallet.walletAddresses.addressForExchange : '');
key.currentState!.changeAmount(amount: '');
}
void _onWalletNameChange(ExchangeViewModel exchangeViewModel, CryptoCurrency currency,
+138 -19
View File
@@ -48,6 +48,7 @@ import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/evm/evm.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/token_utilities.dart';
@@ -250,6 +251,20 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
}
return true;
});
} else if (isEVMCompatibleChain(wallet.type)) {
final currency = depositCurrency;
final balanceCurrency = wallet.balance.keys.firstWhereOrNull(
(c) =>
c.title == depositCurrency.title &&
(c.tag == depositCurrency.tag || c.tag == depositCurrency.title),
);
final balanceForCurrency =
balanceCurrency != null ? wallet.balance[balanceCurrency] : null;
if (depositCurrency == currency && balanceForCurrency != null) {
depositAvailableAmount = _appStore.amountParsingProxy
.getDisplayCryptoStringFromBigInt(
balanceForCurrency.fullAvailableBalance, balanceCurrency!);
}
} else {
final currency = depositCurrency;
final amount = _appStore.amountParsingProxy.getDisplayCryptoString(
@@ -478,9 +493,20 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@computed
String? get balanceDisplay {
final bal = wallet.balance[depositCurrency]?.fullAvailableBalance;
if(bal == null) return null;
return amountParsingProxy.getDisplayCryptoStringFromBigInt(bal, depositCurrency);
CryptoCurrency? balanceCurrency;
if (isEVMCompatibleChain(wallet.type)) {
balanceCurrency = wallet.balance.keys.firstWhereOrNull(
(c) =>
c.title == depositCurrency.title &&
(c.tag == depositCurrency.tag || c.tag == depositCurrency.title),
);
} else {
balanceCurrency = depositCurrency;
}
final bal = balanceCurrency != null ? wallet.balance[balanceCurrency]?.fullAvailableBalance : null;
if (bal == null) return null;
return amountParsingProxy.getDisplayCryptoStringFromBigInt(
bal, balanceCurrency ?? depositCurrency);
}
//* Still open to further optimize these checks
@@ -515,15 +541,23 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
return priority;
}
bool get hasAllAmount =>
[
WalletType.monero,
WalletType.bitcoin,
WalletType.litecoin,
WalletType.bitcoinCash,
WalletType.dogecoin,
].contains(wallet.type) &&
(depositCurrency == wallet.currency);
bool get hasAllAmount {
if ([
WalletType.monero,
WalletType.bitcoin,
WalletType.litecoin,
WalletType.bitcoinCash,
WalletType.dogecoin,
].contains(wallet.type)) return (depositCurrency == wallet.currency);
if (!isEVMCompatibleChain(wallet.type)) return false;
if (depositCurrency == wallet.currency) return true;
return wallet.balance.keys.any((c) =>
c.title == depositCurrency.title &&
(c.tag == depositCurrency.tag || c.tag == depositCurrency.title));
}
bool get isMoneroWallet => wallet.type == WalletType.monero;
@@ -706,26 +740,38 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@action
void changeDepositCurrency({required CryptoCurrency currency}) {
final previousCurrency = depositCurrency;
final wasSendAllEnabled = isSendAllEnabled;
depositCurrency = currency;
isFixedRateMode = false;
_onPairChange();
isDepositAddressEnabled = !useSameWalletAddress(depositCurrency);
if (previousCurrency != currency) {
_onPairChange(clearBoth: wasSendAllEnabled);
}
fetchFiatPrice(currency);
updateDepositAvailableAmount();
}
@action
void changeReceiveCurrency({required CryptoCurrency currency}) {
final previousCurrency = receiveCurrency;
if (!(currency.tag == receiveCurrency.tag ||
currency.tag == receiveCurrency.title ||
currency.title == receiveCurrency.tag ||
currency == receiveCurrency)) {
receiveAddress = "";
}
receiveCurrency = currency;
isFixedRateMode = false;
_onPairChange();
isDepositAddressEnabled = !useSameWalletAddress(depositCurrency);
if (previousCurrency != currency) {
_onPairChange();
}
fetchFiatPrice(currency);
}
@@ -836,6 +882,11 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
isReceiveAmount: isFixedRateMode);
}
bool _excludeProviderForSwapAll(ExchangeProvider provider) =>
isSendAllEnabled &&
(provider.description == ExchangeProviderDescription.swapsXyz ||
provider.description == ExchangeProviderDescription.nearIntents);
Future<void> calculateBestRate() async {
if (depositCurrency == receiveCurrency) {
bestRate = 0.0;
@@ -846,6 +897,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
initialAmountByAssets(isFixedRateMode ? receiveCurrency : depositCurrency);
final validProvidersForAmount = _tradeAvailableProviders.where((provider) {
if (_excludeProviderForSwapAll(provider)) return false;
final limits = _providerLimits[provider];
if (limits == null) return false;
@@ -962,7 +1015,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
if (lowestMin != double.maxFinite) {
limits = Limits(min: lowestMin, max: highestMax);
limitsState = LimitsLoadedSuccessfully(limits: limits);
} else {
limitsState = LimitsLoadedFailure(error: 'Limits loading failed');
@@ -1081,6 +1133,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
printV('createTrade: trying provider=${provider.title}');
if (_excludeProviderForSwapAll(provider)) {
printV('Skipping Swaps.xyz or Near Intents for swap all');
continue;
}
// should not happen but just as an extra check
if (isFixedRateMode && provider.supportsFixedRate == false) {
continue;
@@ -1254,6 +1310,56 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
final amount = await unspentCoinsListViewModel.getSendingBalance(UnspentCoinType.any);
changeDepositAmount(amount: wallet.currency.formatAmount(BigInt.from(amount)), isCanonical: true);
} else if (isEVMCompatibleChain(wallet.type)) {
final balanceCurrency = wallet.balance.keys.firstWhereOrNull(
(currency) =>
currency.title == depositCurrency.title &&
(currency.tag == depositCurrency.tag || currency.tag == depositCurrency.title),
);
final balanceForCurrency = balanceCurrency != null ? wallet.balance[balanceCurrency] : null;
if (balanceForCurrency == null) {
changeDepositAmount(amount: wallet.currency.formatAmount(BigInt.zero), isCanonical: true);
return;
}
final balanceAmount = _appStore.amountParsingProxy.getDisplayCryptoStringFromBigInt(balanceForCurrency.fullAvailableBalance, balanceCurrency!);
final balanceDouble = double.tryParse(balanceAmount.replaceAll(',', '.')) ?? 0.0;
if (balanceDouble <= 0) {
changeDepositAmount(amount: wallet.currency.formatAmount(BigInt.zero), isCanonical: true);
return;
}
final isNative = depositCurrency == wallet.currency;
if (!isNative) {
changeDepositAmount(amount: balanceAmount, isCanonical: true);
return;
}
try {
final priority = _settingsStore.getPriority(wallet.type, chainId: wallet.chainId);
await wallet.updateEstimatedFeesParams(priority);
String? feeString;
if (isEVMCompatibleChain(wallet.type)) {
feeString = evm!.getEVMNativeEstimatedFee(wallet);
}
if (feeString == null || feeString.isEmpty) {
changeDepositAmount(amount: balanceAmount, isCanonical: true);
return;
}
final balanceWei = balanceForCurrency.fullAvailableBalance;
final feeWei = BigInt.parse(feeString);
final amountAfterFeeWei = balanceWei > feeWei ? balanceWei - feeWei : BigInt.zero;
changeDepositAmount(
amount: wallet.currency.formatAmount(amountAfterFeeWei), isCanonical: true);
} catch (e) {
printV('Error calculating send all for EVM: $e');
changeDepositAmount(amount: balanceAmount, isCanonical: true);
}
}
}
@@ -1293,13 +1399,26 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
void removeTemplate({required ExchangeTemplate template}) =>
_exchangeTemplateStore.remove(template: template);
void _onPairChange() {
_depositAmount = '';
_receiveAmount = '';
void _onPairChange({bool clearBoth = false}) {
if (clearBoth) {
_depositAmount = '';
_receiveAmount = '';
if (!hasAllAmount) isSendAllEnabled = false;
} else {
if (isFixedRateMode) {
_depositAmount = '';
} else {
_receiveAmount = '';
}
}
bestRate = 0.0;
bestRateProvider = null;
_sortedAvailableProviders.clear();
loadLimits();
loadLimits().then((_) {
if (clearBoth && hasAllAmount) {
calculateDepositAllAmount();
}
});
_setAvailableProviders();
}