Pay anything fixes (#3224)

* fix: Default to current chain for generic evm format addresses * fix: Handle pay anything triggers in monero.com builds

David Adegoke committed May 20, 2026 at 16:41 UTC c6843183737f4356c032f16c3537f16ef3b5ea1d
2 files changed +61 -18
lib/src/widgets/bottom_sheet/token_selection_bottom_sheet.dart
+40 -11
@@ -77,48 +77,77 @@ class _TokenSelectionContentState extends State<_TokenSelectionContent> {
77 @override
78 void initState() {
79 super.initState();
80 - selectedNetwork = widget.fixedNetwork ?? WalletType.ethereum;
80 + final baseNetwork = widget.fixedNetwork ?? WalletType.ethereum;
81 + selectedNetwork = _resolveGenericETHDetectionResultToSpecificChain(baseNetwork);
82 _autoSelectToken();
83 }
84
85 bool get _isNetworkSelectable =>
86 widget.fixedNetwork == null || isEVMCompatibleChain(widget.fixedNetwork!);
87
88 + CryptoCurrency? _tokenMatchingContract(List<CryptoCurrency> tokens, String? rawContract) {
89 + final trimmed = rawContract?.trim();
90 + if (trimmed == null || trimmed.isEmpty) return null;
91 +
92 + for (final token in tokens) {
93 + if (token is Erc20Token && token.contractAddress.toLowerCase() == trimmed.toLowerCase()) {
94 + return token;
95 + } else if (token is SPLToken && token.mintAddress == trimmed) {
96 + return token;
97 + } else if (token is TronToken && token.contractAddress == trimmed) {
98 + return token;
99 + }
100 + }
101 + return null;
102 + }
103 +
104 + WalletType _resolveGenericETHDetectionResultToSpecificChain(WalletType network) {
105 + if (network != WalletType.ethereum) return network;
106 + final current = widget.paymentViewModel.currentWalletType;
107 + if (isEVMCompatibleChain(current)) return current;
108 + return network;
109 + }
110 +
111 Future<void> _autoSelectToken() async {
88 - if (selectedNetwork == null) return;
112 + final initialNetwork = selectedNetwork;
113 + if (initialNetwork == null || !mounted) return;
114 +
115 + final network = _resolveGenericETHDetectionResultToSpecificChain(initialNetwork);
116
117 setState(() {
118 + selectedNetwork = network;
119 isLoadingTokens = true;
120 tokenLoadError = null;
121 });
122
123 try {
96 - final tokens = await TokenUtilities.getAvailableTokensForNetwork(
97 - selectedNetwork!,
98 - );
124 + final tokens = await TokenUtilities.getAvailableTokensForNetwork(network);
125 + if (!mounted) return;
126
127 if (tokens.isEmpty) {
128 setState(() {
129 tokenLoadError = 'No tokens available';
130 isLoadingTokens = false;
131 + selectedToken = null;
132 });
133 return;
134 }
135
136 + final matchedToken =
137 + _tokenMatchingContract(tokens, widget.paymentRequest.contractAddress) ?? tokens.first;
138 +
139 setState(() {
109 - selectedToken = tokens.firstWhere((e) {
110 - return (e is Erc20Token && e.contractAddress == widget.paymentRequest.contractAddress) ||
111 - (e is SPLToken && e.mintAddress == widget.paymentRequest.contractAddress) ||
112 - (e is TronToken && e.contractAddress == widget.paymentRequest.contractAddress);
113 - }, orElse: () => tokens.first);
140 + selectedToken = matchedToken;
141 isLoadingTokens = false;
142 + tokenLoadError = null;
143 });
144 } catch (e) {
145 printV('Auto-select token error: $e');
146 + if (!mounted) return;
147 setState(() {
148 tokenLoadError = 'Failed to load tokens';
149 isLoadingTokens = false;
121 - selectedToken = walletTypeToCryptoCurrency(selectedNetwork!);
150 + selectedToken = walletTypeToCryptoCurrency(network);
151 });
152 }
153 }
lib/view_model/payment/payment_view_model.dart
+21 -7
@@ -3,6 +3,8 @@ import 'dart:async';
3 import 'package:cake_wallet/core/universal_address_detector.dart';
4 import 'package:cake_wallet/evm/evm.dart';
5 import 'package:cake_wallet/reactions/wallet_connect.dart';
6 +import 'package:cake_wallet/solana/solana.dart';
7 +import 'package:cake_wallet/tron/tron.dart';
8 import 'package:cake_wallet/store/app_store.dart';
9 import 'package:cw_core/wallet_type.dart';
10 import 'package:cw_core/wallet_info.dart';
@@ -41,7 +43,7 @@ abstract class PaymentViewModelBase with Store {
43 return getChainIdByCryptoCurrency(_lastDetectionResult!.detectedCurrency!);
44 }
45
44 - return evm!.getChainIdByWalletType(detectedWalletType!);
46 + return evm?.getChainIdByWalletType(detectedWalletType!);
47 }
48
49 @observable
@@ -52,7 +54,7 @@ abstract class PaymentViewModelBase with Store {
54
55 @computed
56 int? get currentChainId {
55 - if (!isEVMCompatibleChain(currentWalletType)) return null;
57 + if (!isEVMCompatibleChain(currentWalletType) || evm == null) return null;
58
59 return evm!.getSelectedChainId(appStore.wallet!);
60 }
@@ -74,6 +76,18 @@ abstract class PaymentViewModelBase with Store {
76 return PaymentFlowResult.incompatible('Unable to detect address type');
77 }
78
79 + if (detectedWalletType == WalletType.solana && solana == null) {
80 + return PaymentFlowResult.incompatible('Solana is not available in this app build.');
81 + }
82 + if (detectedWalletType == WalletType.tron && tron == null) {
83 + return PaymentFlowResult.incompatible('Tron is not available in this app build.');
84 + }
85 + if (isEVMCompatibleChain(detectedWalletType!) && evm == null) {
86 + return PaymentFlowResult.incompatible(
87 + 'Ethereum-compatible addresses are not supported in this app build.',
88 + );
89 + }
90 +
91 final currentWallet = appStore.wallet;
92
93 if (currentWallet != null &&
@@ -124,7 +138,7 @@ abstract class PaymentViewModelBase with Store {
138
139 @action
140 Future<void> selectChain() async {
127 - if (detectedWalletType == null) return;
141 + if (detectedWalletType == null || evm == null || detectedChainId == null) return;
142
143 final node =
144 appStore.settingsStore.getCurrentNode(detectedWalletType!, chainId: detectedChainId);
@@ -173,7 +187,7 @@ class PaymentFlowResult {
187 chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!);
188 }
189 if (chainId == null && addressDetectionResult.detectedWalletType != null) {
176 - chainId = evm!.getChainIdByWalletType(addressDetectionResult.detectedWalletType!);
190 + chainId = evm?.getChainIdByWalletType(addressDetectionResult.detectedWalletType!);
191 }
192
193 return PaymentFlowResult._(
@@ -228,7 +242,7 @@ class PaymentFlowResult {
242 chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!);
243 }
244 if (chainId == null) {
231 - chainId = evm!.getChainIdByWalletType(wallet.type);
245 + chainId = evm?.getChainIdByWalletType(wallet.type);
246 }
247
248 return PaymentFlowResult._(
@@ -250,7 +264,7 @@ class PaymentFlowResult {
264 chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!);
265 }
266 if (chainId == null) {
253 - chainId = evm!.getChainIdByWalletType(wallets.first.type);
267 + chainId = evm?.getChainIdByWalletType(wallets.first.type);
268 }
269
270 return PaymentFlowResult._(
@@ -272,7 +286,7 @@ class PaymentFlowResult {
286 chainId = getChainIdByCryptoCurrency(addressDetectionResult.detectedCurrency!);
287 }
288 if (chainId == null) {
275 - chainId = evm!.getChainIdByWalletType(walletType);
289 + chainId = evm?.getChainIdByWalletType(walletType);
290 }
291
292 return PaymentFlowResult._(