From c6843183737f4356c032f16c3537f16ef3b5ea1d Mon Sep 17 00:00:00 2001 From: David Adegoke <64401859+Blazebrain@users.noreply.github.com> Date: Wed, 20 May 2026 16:41:04 +0100 Subject: [PATCH] Pay anything fixes (#3224) * fix: Default to current chain for generic evm format addresses * fix: Handle pay anything triggers in monero.com builds --- .../token_selection_bottom_sheet.dart | 51 +++++++++++++++---- .../payment/payment_view_model.dart | 28 +++++++--- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/lib/src/widgets/bottom_sheet/token_selection_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/token_selection_bottom_sheet.dart index ea21c69f3..ce316f90c 100644 --- a/lib/src/widgets/bottom_sheet/token_selection_bottom_sheet.dart +++ b/lib/src/widgets/bottom_sheet/token_selection_bottom_sheet.dart @@ -77,48 +77,77 @@ class _TokenSelectionContentState extends State<_TokenSelectionContent> { @override void initState() { super.initState(); - selectedNetwork = widget.fixedNetwork ?? WalletType.ethereum; + final baseNetwork = widget.fixedNetwork ?? WalletType.ethereum; + selectedNetwork = _resolveGenericETHDetectionResultToSpecificChain(baseNetwork); _autoSelectToken(); } bool get _isNetworkSelectable => widget.fixedNetwork == null || isEVMCompatibleChain(widget.fixedNetwork!); + CryptoCurrency? _tokenMatchingContract(List tokens, String? rawContract) { + final trimmed = rawContract?.trim(); + if (trimmed == null || trimmed.isEmpty) return null; + + for (final token in tokens) { + if (token is Erc20Token && token.contractAddress.toLowerCase() == trimmed.toLowerCase()) { + return token; + } else if (token is SPLToken && token.mintAddress == trimmed) { + return token; + } else if (token is TronToken && token.contractAddress == trimmed) { + return token; + } + } + return null; + } + + WalletType _resolveGenericETHDetectionResultToSpecificChain(WalletType network) { + if (network != WalletType.ethereum) return network; + final current = widget.paymentViewModel.currentWalletType; + if (isEVMCompatibleChain(current)) return current; + return network; + } + Future _autoSelectToken() async { - if (selectedNetwork == null) return; + final initialNetwork = selectedNetwork; + if (initialNetwork == null || !mounted) return; + + final network = _resolveGenericETHDetectionResultToSpecificChain(initialNetwork); setState(() { + selectedNetwork = network; isLoadingTokens = true; tokenLoadError = null; }); try { - final tokens = await TokenUtilities.getAvailableTokensForNetwork( - selectedNetwork!, - ); + final tokens = await TokenUtilities.getAvailableTokensForNetwork(network); + if (!mounted) return; if (tokens.isEmpty) { setState(() { tokenLoadError = 'No tokens available'; isLoadingTokens = false; + selectedToken = null; }); return; } + final matchedToken = + _tokenMatchingContract(tokens, widget.paymentRequest.contractAddress) ?? tokens.first; + setState(() { - selectedToken = tokens.firstWhere((e) { - return (e is Erc20Token && e.contractAddress == widget.paymentRequest.contractAddress) || - (e is SPLToken && e.mintAddress == widget.paymentRequest.contractAddress) || - (e is TronToken && e.contractAddress == widget.paymentRequest.contractAddress); - }, orElse: () => tokens.first); + selectedToken = matchedToken; isLoadingTokens = false; + tokenLoadError = null; }); } catch (e) { printV('Auto-select token error: $e'); + if (!mounted) return; setState(() { tokenLoadError = 'Failed to load tokens'; isLoadingTokens = false; - selectedToken = walletTypeToCryptoCurrency(selectedNetwork!); + selectedToken = walletTypeToCryptoCurrency(network); }); } } diff --git a/lib/view_model/payment/payment_view_model.dart b/lib/view_model/payment/payment_view_model.dart index 13fca7ee3..258758e9b 100644 --- a/lib/view_model/payment/payment_view_model.dart +++ b/lib/view_model/payment/payment_view_model.dart @@ -3,6 +3,8 @@ import 'dart:async'; import 'package:cake_wallet/core/universal_address_detector.dart'; import 'package:cake_wallet/evm/evm.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; +import 'package:cake_wallet/solana/solana.dart'; +import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_info.dart'; @@ -41,7 +43,7 @@ abstract class PaymentViewModelBase with Store { return getChainIdByCryptoCurrency(_lastDetectionResult!.detectedCurrency!); } - return evm!.getChainIdByWalletType(detectedWalletType!); + return evm?.getChainIdByWalletType(detectedWalletType!); } @observable @@ -52,7 +54,7 @@ abstract class PaymentViewModelBase with Store { @computed int? get currentChainId { - if (!isEVMCompatibleChain(currentWalletType)) return null; + if (!isEVMCompatibleChain(currentWalletType) || evm == null) return null; return evm!.getSelectedChainId(appStore.wallet!); } @@ -74,6 +76,18 @@ abstract class PaymentViewModelBase with Store { return PaymentFlowResult.incompatible('Unable to detect address type'); } + if (detectedWalletType == WalletType.solana && solana == null) { + return PaymentFlowResult.incompatible('Solana is not available in this app build.'); + } + if (detectedWalletType == WalletType.tron && tron == null) { + return PaymentFlowResult.incompatible('Tron is not available in this app build.'); + } + if (isEVMCompatibleChain(detectedWalletType!) && evm == null) { + return PaymentFlowResult.incompatible( + 'Ethereum-compatible addresses are not supported in this app build.', + ); + } + final currentWallet = appStore.wallet; if (currentWallet != null && @@ -124,7 +138,7 @@ abstract class PaymentViewModelBase with Store { @action Future selectChain() async { - if (detectedWalletType == null) return; + if (detectedWalletType == null || evm == null || detectedChainId == null) return; final node = appStore.settingsStore.getCurrentNode(detectedWalletType!, chainId: detectedChainId); @@ -173,7 +187,7 @@ class PaymentFlowResult { chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!); } if (chainId == null && addressDetectionResult.detectedWalletType != null) { - chainId = evm!.getChainIdByWalletType(addressDetectionResult.detectedWalletType!); + chainId = evm?.getChainIdByWalletType(addressDetectionResult.detectedWalletType!); } return PaymentFlowResult._( @@ -228,7 +242,7 @@ class PaymentFlowResult { chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!); } if (chainId == null) { - chainId = evm!.getChainIdByWalletType(wallet.type); + chainId = evm?.getChainIdByWalletType(wallet.type); } return PaymentFlowResult._( @@ -250,7 +264,7 @@ class PaymentFlowResult { chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!); } if (chainId == null) { - chainId = evm!.getChainIdByWalletType(wallets.first.type); + chainId = evm?.getChainIdByWalletType(wallets.first.type); } return PaymentFlowResult._( @@ -272,7 +286,7 @@ class PaymentFlowResult { chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!); } if (chainId == null) { - chainId = evm!.getChainIdByWalletType(walletType); + chainId = evm?.getChainIdByWalletType(walletType); } return PaymentFlowResult._(