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._(