CW-1208: Pay Anything EVM enahancements (#2600)

* feat: Pay Anything EVM enahancements * ifx: Add baseEth to address validator switch case * fix: Error detecting other wallet types * feat: Preserve QR amount and other data through evm chain selection flow * Update container color to surfaceContainer * feat: Add support for Bitcoin Lightning Network detection - Identify Base QR codes when scanned - Display all available currencies for network - Enhance detector to recognize lightning addresses * fix: Update token selection for currently selected wallet * fix: Improve token matching logic for EVM networks * feat: Update selected currency when wallet is switched * ui: Add chain badge to the swap confirmation bottomsheet * fix: Asks to switch wallets even if the current wallet type is the same as the QR. Fix merge conflicts too * fix: Handle matching logic internally and general cleanups * fix: Terminate flow if address is not a valid parsed address --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: tuxsudo <tuxsudo@tux.pizza>

David Adegoke committed Nov 11, 2025 at 21:15 UTC 238022c466c2a46b859b28c0049f9ac44c358e8f
43 files changed +764 -73
cw_core/lib/crypto_currency.dart
+1
@@ -284,6 +284,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
284 'wrappedether': weth,
285 'shibainu': shib,
286 'zcash': zec,
287 + 'base': baseEth,
288 };
289
290 static CryptoCurrency deserialize({required int raw}) {
cw_core/lib/wallet_type.dart
+1
@@ -249,6 +249,7 @@ WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
249 case CryptoCurrency.xmr:
250 return WalletType.monero;
251 case CryptoCurrency.btc:
252 + case CryptoCurrency.btcln:
253 return WalletType.bitcoin;
254 case CryptoCurrency.ltc:
255 return WalletType.litecoin;
lib/core/address_validator.dart
+1
@@ -332,6 +332,7 @@ class AddressValidator extends TextValidator {
332 '|([^0-9a-zA-Z]|^)((ltc|t)mweb1q[ac-hj-np-z02-9]{90,120})([^0-9a-zA-Z]|\$)';
333 case CryptoCurrency.eth:
334 case CryptoCurrency.maticpoly:
335 + case CryptoCurrency.baseEth:
336 pattern = '0x[0-9a-zA-Z]+';
337 case CryptoCurrency.nano:
338 pattern = 'nano_[0-9a-zA-Z]{60}';
lib/core/universal_address_detector.dart
+12
@@ -104,6 +104,18 @@ class UniversalAddressDetector {
104 pattern: RegExp(r'^(lnbc|LNBC)[a-km-zA-HJ-NP-Z1-9]{1,}[a-zA-Z0-9]+$'),
105 currency: CryptoCurrency.btcln,
106 ),
107 +
108 + // Lightning Address (email format)
109 + _DetectionPattern(
110 + pattern: RegExp(r'^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'),
111 + currency: CryptoCurrency.btcln,
112 + ),
113 +
114 + // LNURL format
115 + _DetectionPattern(
116 + pattern: RegExp(r'^(LNURL|lnurl)[a-zA-Z0-9]+$', caseSensitive: false),
117 + currency: CryptoCurrency.btcln,
118 + ),
119
120 // Bitcoin (P2PKH, P2SH, Bech32, Silent Payments)
121 _DetectionPattern(
lib/src/screens/send/widgets/send_card.dart
+51
@@ -6,6 +6,7 @@ import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dar
6 import 'package:cake_wallet/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart';
7 import 'package:cake_wallet/src/widgets/bottom_sheet/wallet_switcher_bottom_sheet.dart';
8 import 'package:cake_wallet/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart';
9 +import 'package:cake_wallet/src/widgets/bottom_sheet/evm_payment_flow_bottom_sheet.dart';
10 import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
11 import 'package:cake_wallet/src/widgets/picker.dart';
12 import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
@@ -166,6 +167,13 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
167 result,
168 );
169 break;
170 + case PaymentFlowType.evmNetworkSelection:
171 + await _showEVMPaymentFlow(
172 + paymentViewModel,
173 + walletSwitcherViewModel,
174 + paymentRequest,
175 + );
176 + break;
177 case PaymentFlowType.currentWalletCompatible:
178 case PaymentFlowType.error:
179 case PaymentFlowType.incompatible:
@@ -198,6 +206,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
206 paymentViewModel,
207 walletSwitcherViewModel,
208 paymentRequest,
209 + result,
210 ),
211 onChangeWallet: () => _handleChangeWallet(
212 paymentViewModel,
@@ -211,10 +220,43 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
220 );
221 }
222
223 + Future<void> _showEVMPaymentFlow(
224 + PaymentViewModel paymentViewModel,
225 + WalletSwitcherViewModel walletSwitcherViewModel,
226 + PaymentRequest paymentRequest,
227 + ) async {
228 + await showModalBottomSheet<void>(
229 + context: context,
230 + isDismissible: true,
231 + isScrollControlled: true,
232 + builder: (BuildContext context) {
233 + return EVMPaymentFlowBottomSheet(
234 + paymentViewModel: paymentViewModel,
235 + paymentRequest: paymentRequest,
236 + onNext: (PaymentFlowResult newResult) {
237 + if (newResult.addressDetectionResult!.detectedWalletType ==
238 + paymentViewModel.currentWalletType) {
239 + sendViewModel.setSelectedCryptoCurrency(
240 + newResult.addressDetectionResult!.detectedCurrency!.title);
241 + } else {
242 + _showPaymentConfirmation(
243 + paymentViewModel,
244 + walletSwitcherViewModel,
245 + paymentRequest,
246 + newResult,
247 + );
248 + }
249 + },
250 + );
251 + },
252 + );
253 + }
254 +
255 Future<void> _handleSelectWallet(
256 PaymentViewModel paymentViewModel,
257 WalletSwitcherViewModel walletSwitcherViewModel,
258 PaymentRequest paymentRequest,
259 + PaymentFlowResult result,
260 ) async {
261 Navigator.of(context).pop();
262
@@ -233,6 +275,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
275 final success = await walletSwitcherViewModel.switchToSelectedWallet();
276
277 if (success) {
278 + await sendViewModel.wallet.updateBalance();
279 + sendViewModel
280 + .setSelectedCryptoCurrency(result.addressDetectionResult!.detectedCurrency!.title);
281 _applyPaymentRequest(paymentRequest);
282 }
283 }
@@ -269,6 +314,10 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
314 if (loadingBottomSheetContext != null && loadingBottomSheetContext!.mounted) {
315 Navigator.of(loadingBottomSheetContext!).pop();
316 }
317 +
318 + await sendViewModel.wallet.updateBalance();
319 + sendViewModel
320 + .setSelectedCryptoCurrency(result.addressDetectionResult!.detectedCurrency!.title);
321 _applyPaymentRequest(paymentRequest);
322 }
323 }
@@ -397,6 +446,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
446 final address =
447 output.isParsedAddress ? output.extractedAddress : output.address;
448
449 + if (address.contains('@')) return;
450 +
451 await _handlePaymentFlow(
452 address,
453 PaymentRequest(
lib/src/widgets/bottom_sheet/evm_payment_flow_bottom_sheet.dart new
+309
@@ -0,0 +1,309 @@
1 +import 'package:cake_wallet/core/universal_address_detector.dart';
2 +import 'package:cake_wallet/generated/i18n.dart';
3 +import 'package:cake_wallet/reactions/wallet_connect.dart';
4 +import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
5 +import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
6 +import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
7 +import 'package:cake_wallet/src/widgets/picker.dart';
8 +import 'package:cake_wallet/src/widgets/primary_button.dart';
9 +import 'package:cake_wallet/utils/payment_request.dart';
10 +import 'package:cake_wallet/utils/qr_util.dart';
11 +import 'package:cake_wallet/utils/show_pop_up.dart';
12 +import 'package:cake_wallet/utils/token_utilities.dart';
13 +import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
14 +import 'package:cake_wallet/wallet_types.g.dart';
15 +import 'package:cw_core/crypto_currency.dart';
16 +import 'package:cw_core/currency.dart';
17 +import 'package:cw_core/utils/print_verbose.dart';
18 +import 'package:cw_core/wallet_type.dart';
19 +import 'package:flutter/material.dart';
20 +
21 +class EVMPaymentFlowBottomSheet extends BaseBottomSheet {
22 + EVMPaymentFlowBottomSheet({
23 + Key? key,
24 + required this.paymentViewModel,
25 + required this.paymentRequest,
26 + required this.onNext,
27 + }) : super(
28 + titleText: '',
29 + footerType: FooterType.none,
30 + maxHeight: 900,
31 + );
32 +
33 + final PaymentViewModel paymentViewModel;
34 + final PaymentRequest paymentRequest;
35 + final Function(PaymentFlowResult) onNext;
36 +
37 + @override
38 + Widget contentWidget(BuildContext context) {
39 + return _EVMPaymentFlowContent(
40 + paymentViewModel: paymentViewModel,
41 + paymentRequest: paymentRequest,
42 + onNext: onNext,
43 + );
44 + }
45 +}
46 +
47 +class _EVMPaymentFlowContent extends StatefulWidget {
48 + const _EVMPaymentFlowContent({
49 + required this.paymentViewModel,
50 + required this.paymentRequest,
51 + required this.onNext,
52 + });
53 +
54 + final PaymentViewModel paymentViewModel;
55 + final PaymentRequest paymentRequest;
56 + final Function(PaymentFlowResult) onNext;
57 +
58 + @override
59 + State<_EVMPaymentFlowContent> createState() => _EVMPaymentFlowContentState();
60 +}
61 +
62 +class _EVMPaymentFlowContentState extends State<_EVMPaymentFlowContent> {
63 + WalletType? selectedNetwork;
64 + CryptoCurrency? selectedToken;
65 +
66 + @override
67 + void initState() {
68 + super.initState();
69 + selectedNetwork = WalletType.ethereum;
70 + _autoSelectToken();
71 + }
72 +
73 + Future<void> _autoSelectToken() async {
74 + if (selectedNetwork == null) return;
75 +
76 + try {
77 + final tokens = await TokenUtilities.getAvailableTokensForNetwork(selectedNetwork!);
78 + if (tokens.isNotEmpty) {
79 + setState(() {
80 + selectedToken = tokens.first;
81 + });
82 + }
83 + } catch (e) {
84 + printV('Auto-select token error: $e');
85 + }
86 + }
87 +
88 + @override
89 + Widget build(BuildContext context) {
90 + return Container(
91 + padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
92 + child: Column(
93 + mainAxisSize: MainAxisSize.min,
94 + children: [
95 + CakeImageWidget(
96 + imageUrl: 'assets/images/eth_chain_mono.svg',
97 + width: 50,
98 + height: 50,
99 + color: Theme.of(context).colorScheme.onSurface,
100 + ),
101 + const SizedBox(height: 24),
102 + Padding(
103 + padding: const EdgeInsets.symmetric(horizontal: 24),
104 + child: Text(
105 + '${S.current.ethereum_ecosystem}\n${S.current.address_detected.toLowerCase()}',
106 + style: Theme.of(context).textTheme.titleMedium!.copyWith(
107 + fontSize: 20,
108 + fontWeight: FontWeight.w900,
109 + color: Theme.of(context).colorScheme.onSurface,
110 + ),
111 + textAlign: TextAlign.center,
112 + ),
113 + ),
114 + const SizedBox(height: 36),
115 + Text(
116 + S.current.evm_ecosystem_description,
117 + textAlign: TextAlign.center,
118 + style: Theme.of(context).textTheme.bodyMedium!.copyWith(
119 + fontSize: 16,
120 + fontWeight: FontWeight.w600,
121 + color: Theme.of(context).colorScheme.onSurfaceVariant,
122 + letterSpacing: 0.0,
123 + ),
124 + ),
125 + const SizedBox(height: 32),
126 + EVMTileWidget(
127 + value: selectedNetwork != null
128 + ? walletTypeToString(selectedNetwork!)
129 + : S.current.select_network,
130 + imagePath: selectedNetwork != null ? getChainMonoImage(selectedNetwork!) : null,
131 + color: selectedNetwork != null ? Theme.of(context).colorScheme.primary : null,
132 + enabled: true,
133 + onTap: () => _showNetworkSelection(context),
134 + ),
135 + const SizedBox(height: 16),
136 + EVMTileWidget(
137 + imagePath: selectedToken != null ? selectedToken!.iconPath : null,
138 + value: selectedToken != null ? selectedToken!.title : S.current.select_token,
139 + enabled: selectedNetwork != null,
140 + onTap: () => _showTokenSelection(context),
141 + color: selectedToken == null ? Theme.of(context).colorScheme.primary : null,
142 + ),
143 + const SizedBox(height: 32),
144 + PrimaryButton(
145 + text: S.current.restore_next,
146 + color: Theme.of(context).colorScheme.primary,
147 + textColor: Theme.of(context).colorScheme.onPrimary,
148 + onPressed: selectedNetwork != null && selectedToken != null
149 + ? () async => await _handleNext(context)
150 + : null,
151 + ),
152 + const SizedBox(height: 48),
153 + ],
154 + ),
155 + );
156 + }
157 +
158 + void _showNetworkSelection(BuildContext context) async {
159 + final evmNetworks =
160 + availableWalletTypes.where((walletType) => isEVMCompatibleChain(walletType)).toList();
161 + final selectedIndex = evmNetworks.indexOf(selectedNetwork ?? WalletType.ethereum);
162 +
163 + await showPopUp<void>(
164 + context: context,
165 + builder: (BuildContext context) {
166 + return Picker(
167 + items: evmNetworks,
168 + displayItem: (WalletType network) => walletTypeToString(network),
169 + selectedAtIndex: selectedIndex,
170 + title: S.current.select_network,
171 + closeOnItemSelected: true,
172 + hasTitleSpacing: true,
173 + images: evmNetworks
174 + .map((network) => CakeImageWidget(
175 + imageUrl: getChainMonoImage(network),
176 + width: 20,
177 + height: 20,
178 + color: Theme.of(context).colorScheme.primary))
179 + .toList(),
180 + onItemSelected: (WalletType network) {
181 + setState(() {
182 + selectedNetwork = network;
183 + selectedToken = null;
184 + });
185 + _autoSelectToken();
186 + },
187 + );
188 + },
189 + );
190 + }
191 +
192 + void _showTokenSelection(BuildContext context) async {
193 + if (selectedNetwork == null) return;
194 +
195 + try {
196 + final availableTokens = await TokenUtilities.getAvailableTokensForNetwork(selectedNetwork!);
197 +
198 + if (availableTokens.isEmpty) return;
199 +
200 + final selectedIndex = selectedToken != null ? availableTokens.indexOf(selectedToken!) : 0;
201 +
202 + await showPopUp<void>(
203 + context: context,
204 + builder: (BuildContext context) {
205 + return CurrencyPicker(
206 + selectedAtIndex: selectedIndex >= 0 ? selectedIndex : 0,
207 + items: availableTokens.cast<Currency>(),
208 + hintText: S.current.add_token,
209 + onItemSelected: (Currency currency) {
210 + setState(() {
211 + selectedToken = currency as CryptoCurrency;
212 + });
213 + },
214 + );
215 + },
216 + );
217 + } catch (e) {
218 + printV('Error showing token selection: $e');
219 + }
220 + }
221 +
222 + Future<void> _handleNext(BuildContext context) async {
223 + if (selectedNetwork == null || selectedToken == null) return;
224 +
225 + Navigator.of(context).pop();
226 +
227 + final compatibleWallets = await widget.paymentViewModel.getWalletsByType(selectedNetwork!);
228 +
229 + final newResult = PaymentFlowResult.evmNetworkSelection(
230 + AddressDetectionResult(
231 + address: widget.paymentRequest.address,
232 + detectedWalletType: selectedNetwork!,
233 + detectedCurrency: selectedToken!,
234 + isValid: true,
235 + amount: widget.paymentRequest.amount,
236 + note: widget.paymentRequest.note,
237 + scheme: widget.paymentRequest.scheme,
238 + pjUri: widget.paymentRequest.pjUri,
239 + callbackUrl: widget.paymentRequest.callbackUrl,
240 + callbackMessage: widget.paymentRequest.callbackMessage,
241 + ),
242 + compatibleWallets: compatibleWallets,
243 + wallet: compatibleWallets.isNotEmpty ? compatibleWallets.first : null,
244 + );
245 +
246 + widget.paymentViewModel.detectedWalletType = selectedNetwork!;
247 + widget.onNext(newResult);
248 + }
249 +}
250 +
251 +class EVMTileWidget extends StatelessWidget {
252 + const EVMTileWidget({
253 + super.key,
254 + required this.value,
255 + required this.enabled,
256 + required this.onTap,
257 + this.imagePath,
258 + this.color,
259 + });
260 +
261 + final String value;
262 + final bool enabled;
263 + final VoidCallback? onTap;
264 + final String? imagePath;
265 + final Color? color;
266 +
267 + @override
268 + Widget build(BuildContext context) {
269 + return InkWell(
270 + onTap: enabled ? onTap : null,
271 + borderRadius: BorderRadius.circular(12),
272 + child: Container(
273 + padding: const EdgeInsets.all(16),
274 + decoration: BoxDecoration(
275 + borderRadius: BorderRadius.circular(12),
276 + color: Theme.of(context).colorScheme.surfaceContainer,
277 + ),
278 + child: Row(
279 + children: [
280 + if (imagePath != null) ...[
281 + CakeImageWidget(
282 + imageUrl: imagePath!,
283 + color: color,
284 + width: 20,
285 + height: 20,
286 + ),
287 + const SizedBox(width: 16),
288 + ],
289 + Expanded(
290 + child: Text(
291 + value,
292 + style: Theme.of(context).textTheme.titleMedium!.copyWith(
293 + fontSize: 14,
294 + fontWeight: FontWeight.w400,
295 + color: Theme.of(context).colorScheme.onSurface,
296 + ),
297 + ),
298 + ),
299 + Icon(
300 + Icons.keyboard_arrow_down,
301 + size: 24,
302 + color: Theme.of(context).colorScheme.primary,
303 + ),
304 + ],
305 + ),
306 + ),
307 + );
308 + }
309 +}
lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart
+79 -30
@@ -88,11 +88,16 @@ class _PaymentConfirmationContent extends StatelessWidget {
88 final currencyName = walletTypeToString(paymentViewModel.detectedWalletType!);
89 final currentWalletName = walletTypeToString(paymentViewModel.currentWalletType);
90
91 - final hasMultipleWallets = paymentFlowResult.type == PaymentFlowType.multipleWallets;
92 - final noAvailableWallets = paymentFlowResult.type == PaymentFlowType.noWallets;
91 + final hasSingleWallet = paymentFlowResult.type == PaymentFlowType.singleWallet ||
92 + paymentFlowResult.wallets.length == 1;
93
94 - final hasAtLeastOneWallet =
95 - paymentFlowResult.type == PaymentFlowType.singleWallet || hasMultipleWallets;
94 + final hasMultipleWallets = paymentFlowResult.type == PaymentFlowType.multipleWallets ||
95 + paymentFlowResult.wallets.length > 1;
96 +
97 + final noAvailableWallets = paymentFlowResult.type == PaymentFlowType.noWallets ||
98 + paymentFlowResult.wallets.isEmpty;
99 +
100 + final hasAtLeastOneWallet = hasSingleWallet || hasMultipleWallets;
101
102 final isMwebOrSpAddress =
103 _isMwebOrSpAddress(paymentFlowResult.addressDetectionResult?.address ?? '');
@@ -103,34 +108,78 @@ class _PaymentConfirmationContent extends StatelessWidget {
108 mainAxisSize: MainAxisSize.min,
109 crossAxisAlignment: CrossAxisAlignment.center,
110 children: [
106 - Image.asset(
107 - walletTypeToCryptoCurrency(paymentViewModel.detectedWalletType!).iconPath!,
108 - width: 118,
109 - height: 118,
110 - ),
111 - const SizedBox(height: 20),
112 - Text(
113 - '$currencyName ${S.current.address_detected.toLowerCase()}',
114 - style: Theme.of(context).textTheme.titleMedium!.copyWith(
115 - fontSize: 20,
116 - fontWeight: FontWeight.w900,
117 - color: Theme.of(context).colorScheme.onSurface,
118 - letterSpacing: 0.0,
111 + if (paymentFlowResult.type == PaymentFlowType.evmNetworkSelection) ...[
112 + Stack(
113 + clipBehavior: Clip.none,
114 + children: [
115 + Image.asset(
116 + paymentFlowResult.addressDetectionResult!.detectedCurrency!.iconPath!,
117 + width: 70,
118 + height: 70,
119 ),
120 - textAlign: TextAlign.center,
121 - ),
122 - const SizedBox(height: 24),
123 - Text(
124 - '''Looks like you scanned a $currencyName address.\n\n'''
125 - '''Would you like to ${!noAvailableWallets ? 'switch to a $currencyName wallet or' : ''} swap $currentWalletName for $currencyName for this payment?''',
126 - textAlign: TextAlign.center,
127 - style: Theme.of(context).textTheme.bodyMedium!.copyWith(
128 - fontSize: 16,
129 - fontWeight: FontWeight.w600,
130 - color: Theme.of(context).colorScheme.onSurfaceVariant,
131 - letterSpacing: 0.0,
120 + Positioned(
121 + bottom: 0,
122 + right: 0,
123 + child: Image.asset(
124 + walletTypeToCryptoCurrency(paymentViewModel.detectedWalletType!).iconPath!,
125 + width: 32,
126 + height: 32,
127 + ),
128 ),
133 - ),
129 + ],
130 + ),
131 + const SizedBox(height: 20),
132 + Text(
133 + '${paymentFlowResult.addressDetectionResult?.detectedCurrency} (${walletTypeToString(paymentFlowResult.walletType!)})',
134 + style: Theme.of(context).textTheme.titleMedium!.copyWith(
135 + fontSize: 20,
136 + fontWeight: FontWeight.w900,
137 + color: Theme.of(context).colorScheme.onSurface,
138 + letterSpacing: 0.0,
139 + ),
140 + textAlign: TextAlign.center,
141 + ),
142 + const SizedBox(height: 24),
143 + Text(
144 + '''Would you like to ${!noAvailableWallets ? 'switch to a $currencyName wallet or' : ''} swap $currentWalletName for ${paymentFlowResult.addressDetectionResult?.detectedCurrency} (${walletTypeToString(paymentFlowResult.walletType!)}) for this payment?''',
145 + textAlign: TextAlign.center,
146 + style: Theme.of(context).textTheme.bodyMedium!.copyWith(
147 + fontSize: 16,
148 + fontWeight: FontWeight.w600,
149 + color: Theme.of(context).colorScheme.onSurfaceVariant,
150 + letterSpacing: 0.0,
151 + ),
152 + ),
153 + ] else ...[
154 + Image.asset(
155 + walletTypeToCryptoCurrency(paymentViewModel.detectedWalletType!).iconPath!,
156 + width: 118,
157 + height: 118,
158 + ),
159 + const SizedBox(height: 20),
160 + Text(
161 + '$currencyName ${S.current.address_detected.toLowerCase()}',
162 + style: Theme.of(context).textTheme.titleMedium!.copyWith(
163 + fontSize: 20,
164 + fontWeight: FontWeight.w900,
165 + color: Theme.of(context).colorScheme.onSurface,
166 + letterSpacing: 0.0,
167 + ),
168 + textAlign: TextAlign.center,
169 + ),
170 + const SizedBox(height: 24),
171 + Text(
172 + '''Looks like you scanned a $currencyName address.\n\n'''
173 + '''Would you like to ${!noAvailableWallets ? 'switch to a $currencyName wallet or' : ''} swap $currentWalletName for $currencyName for this payment?''',
174 + textAlign: TextAlign.center,
175 + style: Theme.of(context).textTheme.bodyMedium!.copyWith(
176 + fontSize: 16,
177 + fontWeight: FontWeight.w600,
178 + color: Theme.of(context).colorScheme.onSurfaceVariant,
179 + letterSpacing: 0.0,
180 + ),
181 + ),
182 + ],
183 const SizedBox(height: 72),
184 if (hasAtLeastOneWallet) ...[
185 if (!isMwebOrSpAddress) ...[
lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart
+22 -7
@@ -137,7 +137,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
137
138 @override
139 Widget build(BuildContext context) {
140 - final detectedCurrency = walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!);
140 + final detectedCurrency = widget.paymentFlowResult.detectedCurrency!;
141
142 return Form(
143 key: _formKey,
@@ -158,11 +158,25 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
158 const SizedBox(width: 12),
159 Icon(Icons.arrow_forward, size: 24),
160 const SizedBox(width: 12),
161 - CakeImageWidget(
162 - imageUrl:
163 - detectedCurrency.iconPath!,
164 - width: 32,
165 - height: 32,
161 + Stack(
162 + clipBehavior: Clip.none,
163 + children: [
164 + CakeImageWidget(
165 + imageUrl: detectedCurrency.iconPath!,
166 + width: 32,
167 + height: 32,
168 + ),
169 + Positioned(
170 + bottom: -4,
171 + right: -4,
172 + child: CakeImageWidget(
173 + imageUrl: walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!)
174 + .iconPath!,
175 + width: 16,
176 + height: 16,
177 + ),
178 + ),
179 + ],
180 ),
181 ],
182 ),
@@ -414,7 +428,8 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
428 }
429 });
430
417 - exchangeViewModel.receiveCurrency = walletTypeToCryptoCurrency(paymentFlowResult.walletType!);
431 + exchangeViewModel.receiveCurrency = paymentFlowResult.detectedCurrency!;
432 +
433 await exchangeViewModel.fetchFiatPrice(exchangeViewModel.receiveCurrency);
434
435 exchangeViewModel.receiveAddress = _addressController.text;
lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart
+2 -2
@@ -272,13 +272,13 @@ class _SwapDetailsContent extends StatelessWidget {
272 children: [
273 _SwapDetailsTile(
274 label: 'You Send',
275 - value: '${trade.amount} ${trade.from?.title ?? ''}',
275 + value: '${trade.amount} ${trade.from?.title ?? trade.userCurrencyFrom?.title ?? ''}',
276 valueFiatFormatted: exchangeTradeViewModel.sendAmountFiatFormatted,
277 ),
278 const SizedBox(height: 8),
279 _SwapDetailsTile(
280 label: 'You Get',
281 - value: '${trade.receiveAmount ?? '0'} ${trade.to?.title ?? ''}',
281 + value: '${trade.receiveAmount ?? '0'} ${trade.to?.title ?? trade.userCurrencyTo?.title ?? ''}',
282 valueFiatFormatted: exchangeTradeViewModel
283 .getReceiveAmountFiatFormatted(trade.receiveAmount ?? '0.0'),
284 ),
lib/src/widgets/picker.dart
+12 -5
@@ -18,7 +18,9 @@ class Picker<Item> extends StatefulWidget {
18 required this.onItemSelected,
19 this.title,
20 this.displayItem,
21 - this.images = const <Image>[],
21 + this.displayItemColor,
22 + this.displayItemFontSize,
23 + this.images = const <Widget>[],
24 this.description,
25 this.mainAxisAlignment = MainAxisAlignment.start,
26 this.isGridView = false,
@@ -34,6 +36,7 @@ class Picker<Item> extends StatefulWidget {
36 this.borderColor,
37 this.onSliderChanged,
38 this.matchingCriteria,
39 + this.hasTitleSpacing = false,
40 }) : assert(hintText == null || matchingCriteria != null) {
41 // make sure that if the search field is enabled then there is a searching criteria provided
42 if (sliderValue != null && maxValue != null) {
@@ -45,12 +48,14 @@ class Picker<Item> extends StatefulWidget {
48
49 final int selectedAtIndex;
50 final List<Item> items;
48 - final List<Image> images;
51 + final List<Widget> images;
52 final String? title;
53 final String? description;
54 final Function(Item) onItemSelected;
55 final MainAxisAlignment mainAxisAlignment;
56 final String Function(Item)? displayItem;
57 + final Color? displayItemColor;
58 + final double? displayItemFontSize;
59 final bool isGridView;
60 final bool isSeparated;
61 final String? hintText;
@@ -64,7 +69,7 @@ class Picker<Item> extends StatefulWidget {
69 final Function(double)? onSliderChanged;
70 final bool Function(Item, String)? matchingCriteria;
71 final double? maxValue;
67 -
72 + final bool hasTitleSpacing;
73 @override
74 _PickerState<Item> createState() => _PickerState<Item>(items, images, onItemSelected);
75 }
@@ -74,9 +79,9 @@ class _PickerState<Item> extends State<Picker<Item>> {
79
80 final Function(Item) onItemSelected;
81 List<Item> items;
77 - List<Image> images;
82 + List<Widget> images;
83 List<Item> filteredItems = [];
79 - List<Image> filteredImages = [];
84 + List<Widget> filteredImages = [];
85 final TextEditingController searchController = TextEditingController();
86
87 ScrollController controller = ScrollController();
@@ -163,6 +168,7 @@ class _PickerState<Item> extends State<Picker<Item>> {
168 ),
169 ),
170 ),
171 + widget.hasTitleSpacing ? const SizedBox(height: 24) : const SizedBox.shrink(),
172 Padding(
173 padding: EdgeInsets.symmetric(horizontal: padding),
174 child: Container(
@@ -342,6 +348,7 @@ class _PickerState<Item> extends State<Picker<Item>> {
348 style: Theme.of(context).textTheme.bodyMedium!.copyWith(
349 fontWeight: FontWeight.w600,
350 decoration: TextDecoration.none,
351 + color: widget.displayItemColor,
352 ),
353 ),
354 ),
lib/utils/qr_util.dart
+21
@@ -38,3 +38,24 @@ String getQrImage(WalletType type) {
38 return 'assets/images/qr-cake.png';
39 }
40 }
41 +
42 +String getChainMonoImage(WalletType type) {
43 + switch (type) {
44 + case WalletType.ethereum:
45 + return 'assets/images/eth_chain_mono.svg';
46 + case WalletType.solana:
47 + return 'assets/images/sol_chain_mono.svg';
48 + case WalletType.polygon:
49 + return 'assets/images/pol_chain_mono.svg';
50 + case WalletType.tron:
51 + return 'assets/images/trx_chain_mono.svg';
52 + case WalletType.zano:
53 + return 'assets/images/zano_chain_mono.svg';
54 + case WalletType.base:
55 + return 'assets/images/base_chain_mono.svg';
56 + case WalletType.arbitrum:
57 + return 'assets/images/arbitrum_chain_mono.svg';
58 + default:
59 + return 'assets/images/eth_chain_mono.svg';
60 + }
61 +}
lib/utils/token_utilities.dart
+70 -6
@@ -1,6 +1,7 @@
1 import 'package:cake_wallet/reactions/wallet_connect.dart';
2 import 'package:cw_core/cake_hive.dart';
3 import 'package:cw_core/crypto_currency.dart';
4 +import 'package:cw_core/currency_for_wallet_type.dart';
5 import 'package:cw_core/erc20_token.dart';
6 import 'package:cw_core/spl_token.dart';
7 import 'package:cw_core/tron_token.dart';
@@ -149,7 +150,7 @@ class TokenUtilities {
150
151 // More of a fallback for us
152 for (final balanceCurrency in wallet.balance.keys) {
152 - if (balanceCurrency is Erc20Token && _matchesToken(balanceCurrency, currency)) {
153 + if (balanceCurrency is Erc20Token && _matchesCurrency(balanceCurrency, currency)) {
154 return balanceCurrency;
155 }
156 }
@@ -217,10 +218,73 @@ class TokenUtilities {
218 return 1;
219 }
220
220 - /// Checks if two currencies match (by title and tag)
221 - static bool _matchesToken(Erc20Token token, CryptoCurrency currency) {
222 - return token.title.toLowerCase() == currency.title.toLowerCase() &&
223 - (token.tag?.toLowerCase() == currency.tag?.toLowerCase() ||
224 - (token.tag == null && currency.tag == null));
221 + static Future<List<CryptoCurrency>> getAvailableTokensForNetwork(
222 + WalletType network,
223 + ) async {
224 + final baseCurrency = walletTypeToCryptoCurrency(network);
225 + final allTokens = <CryptoCurrency>[];
226 + final addedAddresses = <String>{};
227 +
228 + allTokens.add(baseCurrency);
229 +
230 + for (final currency in CryptoCurrency.all) {
231 + // For EVM networks: ETH has no tag, POL/BASE have tags
232 + // Match by tag for POL/BASE, match by title==tag for ETH
233 + final matches = (baseCurrency.tag == null && baseCurrency.title == currency.tag) ||
234 + (baseCurrency.tag != null &&
235 + currency.tag?.toLowerCase() == baseCurrency.tag?.toLowerCase());
236 +
237 + if (matches && _shouldAddToken(allTokens, currency, addedAddresses)) {
238 + allTokens.add(currency);
239 + }
240 + }
241 +
242 + // Add user tokens that don't already exist
243 + final userTokens = await _getUserTokensForNetwork(baseCurrency);
244 + for (final token in userTokens) {
245 + if (_shouldAddToken(allTokens, token, addedAddresses)) {
246 + allTokens.add(token);
247 + if (token is Erc20Token) {
248 + addedAddresses.add(token.contractAddress.toLowerCase());
249 + }
250 + }
251 + }
252 +
253 + return allTokens;
254 + }
255 +
256 + static bool _shouldAddToken(
257 + List<CryptoCurrency> existingTokens,
258 + CryptoCurrency token,
259 + Set<String> addedAddresses,
260 + ) {
261 + if (token is Erc20Token) {
262 + final address = token.contractAddress.toLowerCase();
263 + if (addedAddresses.contains(address)) {
264 + return false;
265 + }
266 + if (existingTokens.any((existing) => _matchesCurrency(existing, token))) {
267 + return false;
268 + }
269 + return true;
270 + }
271 +
272 + return !existingTokens.any((existing) => _matchesCurrency(existing, token));
273 + }
274 +
275 + static bool _matchesCurrency(CryptoCurrency a, CryptoCurrency b) {
276 + return a.title.toUpperCase() == b.title.toUpperCase() &&
277 + (a.tag?.toUpperCase() == b.tag?.toUpperCase());
278 + }
279 +
280 + static Future<List<CryptoCurrency>> _getUserTokensForNetwork(CryptoCurrency baseCurrency) async {
281 + final tokens = await TokenUtilities.loadAllUniqueEvmTokens();
282 +
283 + return tokens.where((token) {
284 + // Match by tag, except for ETH which has no tag - match by title instead
285 + if (baseCurrency.tag == null) return token.tag == baseCurrency.title;
286 +
287 + return token.tag?.toLowerCase() == baseCurrency.tag?.toLowerCase();
288 + }).toList();
289 }
290 }
lib/view_model/payment/payment_view_model.dart
+41 -3
@@ -5,6 +5,8 @@ import 'package:cake_wallet/store/app_store.dart';
5 import 'package:cw_core/wallet_type.dart';
6 import 'package:cw_core/wallet_info.dart';
7 import 'package:cw_core/utils/print_verbose.dart';
8 +import 'package:cw_core/currency_for_wallet_type.dart';
9 +import 'package:cw_core/crypto_currency.dart';
10 import 'package:mobx/mobx.dart';
11
12 part 'payment_view_model.g.dart';
@@ -37,11 +39,15 @@ abstract class PaymentViewModelBase with Store {
39 // Detect address type
40 final detectionResult = UniversalAddressDetector.detectAddress(addressData);
41
40 - if (!detectionResult.isValid || detectionResult.detectedWalletType == null) {
42 + detectedWalletType = detectionResult.detectedWalletType;
43 +
44 + if (!detectionResult.isValid || detectedWalletType == null) {
45 return PaymentFlowResult.incompatible('Unable to detect address type');
46 }
47
44 - detectedWalletType = detectionResult.detectedWalletType;
48 + if (_isEVMAddress(detectionResult.address)) {
49 + return PaymentFlowResult.evmNetworkSelection(detectionResult);
50 + }
51
52 // Check if current wallet is compatible
53 final currentWallet = appStore.wallet;
@@ -67,6 +73,10 @@ abstract class PaymentViewModelBase with Store {
73 }
74 }
75
76 + bool _isEVMAddress(String address) {
77 + return RegExp(r'^0x[a-fA-F0-9]{40}$').hasMatch(address);
78 + }
79 +
80 Future<List<WalletInfo>> getWalletsByType(WalletType walletType) async {
81 return (await WalletInfo.getAll()).where((wallet) => wallet.type == walletType).toList();
82 }
@@ -89,13 +99,30 @@ class PaymentFlowResult {
99 this.addressDetectionResult,
100 });
101
102 + /// EVM address detected - needs network selection
103 + /// We'll also take note of the number of compatible wallets for EVM ecosystem
104 + factory PaymentFlowResult.evmNetworkSelection(
105 + AddressDetectionResult addressDetectionResult, {
106 + List<WalletInfo>? compatibleWallets,
107 + WalletInfo? wallet,
108 + }) =>
109 + PaymentFlowResult._(
110 + type: PaymentFlowType.evmNetworkSelection,
111 + addressDetectionResult: addressDetectionResult,
112 + walletType: addressDetectionResult.detectedWalletType,
113 + wallets: compatibleWallets ?? [],
114 + wallet: wallet,
115 + );
116 +
117 /// Current wallet is compatible
118 factory PaymentFlowResult.currentWalletCompatible() =>
119 PaymentFlowResult._(type: PaymentFlowType.currentWalletCompatible);
120
121 /// Single compatible wallet available
122 factory PaymentFlowResult.singleWallet(
98 - WalletInfo wallet, AddressDetectionResult addressDetectionResult) =>
123 + WalletInfo wallet,
124 + AddressDetectionResult addressDetectionResult,
125 + ) =>
126 PaymentFlowResult._(
127 type: PaymentFlowType.singleWallet,
128 wallet: wallet,
@@ -126,6 +153,16 @@ class PaymentFlowResult {
153 /// Incompatible address
154 factory PaymentFlowResult.incompatible(String message) =>
155 PaymentFlowResult._(type: PaymentFlowType.incompatible, message: message);
156 +
157 + CryptoCurrency? get detectedCurrency {
158 + if (type == PaymentFlowType.evmNetworkSelection) {
159 + return addressDetectionResult?.detectedCurrency;
160 + }
161 + if (walletType != null) {
162 + return walletTypeToCryptoCurrency(walletType!);
163 + }
164 + return null;
165 + }
166 }
167
168 enum PaymentFlowType {
@@ -133,6 +170,7 @@ enum PaymentFlowType {
170 singleWallet,
171 multipleWallets,
172 noWallets,
173 + evmNetworkSelection,
174 error,
175 incompatible,
176 }
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+1 -20
@@ -474,26 +474,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
474 String get qrImage => getQrImage(type);
475
476 @computed
477 - String get monoImage {
478 - switch (type) {
479 - case WalletType.ethereum:
480 - return 'assets/images/eth_chain_mono.svg';
481 - case WalletType.solana:
482 - return 'assets/images/sol_chain_mono.svg';
483 - case WalletType.polygon:
484 - return 'assets/images/pol_chain_mono.svg';
485 - case WalletType.tron:
486 - return 'assets/images/trx_chain_mono.svg';
487 - case WalletType.zano:
488 - return 'assets/images/zano_chain_mono.svg';
489 - case WalletType.base:
490 - return 'assets/images/base_chain_mono.svg';
491 - case WalletType.arbitrum:
492 - return 'assets/images/arbitrum_chain_mono.svg';
493 - default:
494 - return 'assets/images/eth_chain_mono.svg';
495 - }
496 - }
477 + String get monoImage => getChainMonoImage(type);
478
479 @computed
480 bool get isBalanceAvailable => isElectrumWallet;
res/values/strings_ar.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "رسوم جديدة مقدرة",
366 "estimated_receive_amount": "مقدرة المبلغ الاستقبال",
367 "eta": "إيتا",
368 + "ethereum_ecosystem": "النظام البيئي للإيثريوم",
369 "etherscan_history": "Etherscan تاريخ",
370 "event": "ﺙﺪﺣ",
371 "events": "ﺙﺍﺪﺣﻷﺍ",
372 + "evm_ecosystem_description": "إذا لزم الأمر، قم بتعديل الشبكة والرمز المميز الذي ترغب في استخدامه لهذه الدفعة.",
373 "exchange": "تبادل",
374 "exchange_extra_info": "يرجى عدم نسيان تحديد المذكرة أو معرف إضافي أثناء إرسال هذه المعاملة للتبادل",
375 "exchange_incorrect_current_wallet_for_xmr": "إذا كنت ترغب في تبديل XMR من رصيد محفظة الكعكة ، فيرجى التبديل إلى محفظة Monero أولاً.",
@@ -792,7 +794,9 @@
794 "select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ",
795 "select_hw_account_below": "الرجاء تحديد حساب الاستعادة أدناه:",
796 "select_manufacturer_title": "حدد الشركة المصنعة",
797 + "select_network": "حدد الشبكة",
798 "select_sell_provider_notice": ".ﻖﻴﺒﻄﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻚﺑ ﺹﺎﺨﻟﺍ ﻲﺿﺍﺮﺘﻓﻻﺍ ﻊﻴﺒﻟﺍ ﺩﻭﺰﻣ ﻦﻴﻴﻌﺗ ﻖﻳﺮﻃ ﻦﻋ ﺔﺷﺎﺸﻟﺍ ﻩﺬﻫ ﻲﻄﺨﺗ",
799 + "select_token": "حدد الرمز المميز",
800 "select_your_country": "الرجاء تحديد بلدك",
801 "selected_trocador_provider": "مزود تروكادور المختار",
802 "sell": "بيع",
res/values/strings_bg.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Прогнозна нова такса",
366 "estimated_receive_amount": "Прогнозна сума за получаване",
367 "eta": "Ета",
368 + "ethereum_ecosystem": "Екосистема Ethereum",
369 "etherscan_history": "История на Etherscan",
370 "event": "Събитие",
371 "events": "събития",
372 + "evm_ecosystem_description": "Ако е необходимо, променете мрежата и токена, които искате да използвате за това плащане.",
373 "exchange": "Обмен",
374 "exchange_extra_info": "Моля, не забравяйте да посочите бележката или допълнителен лична карта, докато изпращате тази транзакция за борсата",
375 "exchange_incorrect_current_wallet_for_xmr": "Ако искате да смените XMR от вашия баланс на портфейла на тортата Monero, моля, преминете първо към вашия портфейл Monero.",
@@ -792,7 +794,9 @@
794 "select_destination": "Моля, изберете дестинация за архивния файл.",
795 "select_hw_account_below": "Моля, изберете кой акаунт да възстановите по -долу:",
796 "select_manufacturer_title": "Изберете производител",
797 + "select_network": "Изберете Мрежа",
798 "select_sell_provider_notice": "Изберете доставчик на продажба по-горе. Можете да пропуснете този екран, като зададете своя доставчик на продажба по подразбиране в настройките на приложението.",
799 + "select_token": "Изберете Token",
800 "select_your_country": "Моля, изберете вашата страна",
801 "selected_trocador_provider": "Избран доставчик на трокадор",
802 "sell": "Продаване",
res/values/strings_cs.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Odhadovaný nový poplatek",
366 "estimated_receive_amount": "Odhadovaná částka přijímání",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "Ekosystém Ethereum",
369 "etherscan_history": "Historie Etherscanu",
370 "event": "událost",
371 "events": "Události",
372 + "evm_ecosystem_description": "V případě potřeby upravte síť a token, které chcete pro tuto platbu použít.",
373 "exchange": "Výměna",
374 "exchange_extra_info": "Při odesílání této transakce na burzu prosím nezapomeňte zadat poznámku nebo další ID",
375 "exchange_incorrect_current_wallet_for_xmr": "Pokud chcete vyměnit XMR z vaší dortové peněženky Monero Balance, nejprve přepněte na peněženku Monero.",
@@ -792,7 +794,9 @@
794 "select_destination": "Vyberte cíl pro záložní soubor.",
795 "select_hw_account_below": "Níže vyberte, který účet chcete obnovit:",
796 "select_manufacturer_title": "Vyberte výrobce",
797 + "select_network": "Vyberte Síť",
798 "select_sell_provider_notice": "Výše vyberte poskytovatele prodeje. Tuto obrazovku můžete přeskočit nastavením výchozího poskytovatele prodeje v nastavení aplikace.",
799 + "select_token": "Vyberte Token",
800 "select_your_country": "Vyberte prosím svou zemi",
801 "selected_trocador_provider": "Vybraný poskytovatel Trocador",
802 "sell": "Prodat",
res/values/strings_de.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Geschätzte neue Gebühr",
366 "estimated_receive_amount": "Geschätzter Empfangsbetrag",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "Ethereum-Ökosystem",
369 "etherscan_history": "Etherscan-Historie",
370 "event": "Ereignis",
371 "events": "Veranstaltungen",
372 + "evm_ecosystem_description": "Ändern Sie bei Bedarf das Netzwerk und den Token, den Sie für diese Zahlung verwenden möchten.",
373 "exchange": "Tauschen",
374 "exchange_extra_info": "Bitte vergessen Sie nicht, das Memo oder die zusätzliche ID anzugeben, während Sie diese Transaktion für den Austausch senden",
375 "exchange_incorrect_current_wallet_for_xmr": "Wenn Sie XMR aus Ihrem Cake Wallet Monero-Saldo tauschen möchten, wechseln Sie zuerst zu Ihrer Monero-Wallet.",
@@ -793,7 +795,9 @@
795 "select_destination": "Bitte wählen Sie das Ziel für die Sicherungsdatei aus.",
796 "select_hw_account_below": "Bitte wählen Sie unten, welches Konto unten wiederhergestellt werden soll:",
797 "select_manufacturer_title": "Hersteller auswählen",
798 + "select_network": "Wählen Sie Netzwerk",
799 "select_sell_provider_notice": "Wählen Sie oben einen Verkaufsanbieter aus. Sie können diesen Bildschirm überspringen, indem Sie in den App-Einstellungen Ihren Standard-Verkaufsanbieter festlegen.",
800 + "select_token": "Wählen Sie Token aus",
801 "select_your_country": "Bitte wählen Sie Ihr Land aus",
802 "selected_trocador_provider": "Ausgewählter Trocador-Anbieter",
803 "sell": "Verkaufen",
res/values/strings_en.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Estimated new fee",
366 "estimated_receive_amount": "Estimated receive amount",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "Ethereum Ecosystem",
369 "etherscan_history": "Etherscan history",
370 "event": "Event",
371 "events": "Events",
372 + "evm_ecosystem_description": "If necessary, modify the network and token you would like to use for this payment.",
373 "exchange": "Exchange",
374 "exchange_extra_info": "Please don’t forget to specify the Memo or Extra ID while sending this transaction for the exchange",
375 "exchange_incorrect_current_wallet_for_xmr": "If you want to swap XMR from your Cake Wallet Monero balance, please switch to your Monero wallet first.",
@@ -793,7 +795,9 @@
795 "select_destination": "Please select destination for the backup file.",
796 "select_hw_account_below": "Please select which account to restore below:",
797 "select_manufacturer_title": "Select Manufacturer",
798 + "select_network": "Select Network",
799 "select_sell_provider_notice": "Select a sell provider above. You can skip this screen by setting your default sell provider in app settings.",
800 + "select_token": "Select Token",
801 "select_your_country": "Please select your country",
802 "selected_trocador_provider": "selected Trocador provider",
803 "sell": "Sell",
res/values/strings_es.arb
+4
@@ -366,8 +366,10 @@
366 "estimated_receive_amount": "Cantidad estimada a recibir",
367 "eta": "ETA",
368 "etherscan_history": "Historial de Etherscan",
369 + "ethereum_ecosystem": "Ecosistema Etereum",
370 "event": "Evento",
371 "events": "Eventos",
372 + "evm_ecosystem_description": "Si es necesario, modifica la red y el token que deseas utilizar para este pago.",
373 "exchange": "Intercambio",
374 "exchange_extra_info": "Por favor, no olvide especificar el Memo o el Extra ID al enviar esta transacción al exchange.",
375 "exchange_incorrect_current_wallet_for_xmr": "Si deseas intercambiar XMR de tu saldo de Monero en Cake Wallet, por favor cambia primero a tu billetera Monero.",
@@ -793,7 +795,9 @@
795 "select_destination": "Selecciona la ubicación del archivo de copia de seguridad.",
796 "select_hw_account_below": "Selecciona la cuenta que deseas restaurar a continuación:",
797 "select_manufacturer_title": "Seleccionar Fabricante",
798 + "select_network": "Seleccionar red",
799 "select_sell_provider_notice": "Selecciona un proveedor de venta arriba. Puedes omitir esta pantalla configurando tu proveedor de venta predeterminado en la configuración de la aplicación.",
800 + "select_token": "Seleccionar ficha",
801 "select_your_country": "Por favor, selecciona tu país",
802 "selected_trocador_provider": "proveedor de Trocador seleccionado",
803 "sell": "Vender",
res/values/strings_fr.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Nouveaux frais estimés",
366 "estimated_receive_amount": "Montant estimé à recevoir",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "Écosystème Ethereum",
369 "etherscan_history": "Historique Etherscan",
370 "event": "Événement",
371 "events": "Événements",
372 + "evm_ecosystem_description": "Si nécessaire, modifiez le réseau et le token que vous souhaitez utiliser pour ce paiement.",
373 "exchange": "Acheter / Vendre",
374 "exchange_extra_info": "N'oubliez pas de spécifier le mémo ou l'identifiant supplémentaire lors de l'envoi de cette transaction pour l'échange",
375 "exchange_incorrect_current_wallet_for_xmr": "Si vous souhaitez échanger des XMR depuis le solde Monero de votre Cake Wallet, veuillez d'abord passer à votre portefeuille Monero.",
@@ -792,7 +794,9 @@
794 "select_destination": "Veuillez sélectionner la destination du fichier de sauvegarde.",
795 "select_hw_account_below": "Veuillez sélectionner le compte à restaurer ci-dessous:",
796 "select_manufacturer_title": "Sélectionner le fabricant",
797 + "select_network": "Sélectionnez le réseau",
798 "select_sell_provider_notice": "Sélectionnez un fournisseur de vente ci-dessus. Vous pouvez ignorer cet écran en définissant votre fournisseur de vente par défaut dans les paramètres de l'application.",
799 + "select_token": "Sélectionnez le jeton",
800 "select_your_country": "Veuillez sélectionner votre pays",
801 "selected_trocador_provider": "fournisseur de trocador sélectionné",
802 "sell": "Vendre",
res/values/strings_ha.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "An kiyasta sabon kudin",
366 "estimated_receive_amount": "Kiyasta samun adadin",
367 "eta": "Tsire-tsire",
368 + "ethereum_ecosystem": "Ecetereum ecosysterem",
369 "etherscan_history": "Etherscan tarihin kowane zamani",
370 "event": "Lamarin",
371 "events": "Abubuwan da suka faru",
372 + "evm_ecosystem_description": "Idan ya cancanta, gyara cibiyar sadarwa da alama kuna son amfani da wannan biyan.",
373 "exchange": "Canji",
374 "exchange_extra_info": "Don Allah kar a manta su saka memba ko karin ID yayin aika wannan ma'amalar don musayar",
375 "exchange_incorrect_current_wallet_for_xmr": "Idan kana son canza XMR daga walat ɗin Bed Wallet ɗinka, da fatan za a canza zuwa walat ɗinku na Monero.",
@@ -794,7 +796,9 @@
796 "select_destination": "Da fatan za a zaɓi wurin da za a yi wa madadin fayil ɗin.",
797 "select_hw_account_below": "Da fatan za a zabi wanda asusun zai gyara a ƙasa:",
798 "select_manufacturer_title": "Select Manaser",
799 + "select_network": "Zaɓi Hadawa",
800 "select_sell_provider_notice": "Zaɓi mai bada siyarwa a sama. Kuna iya tsallake wannan allon ta saita mai bada siyar da ku a cikin saitunan app.",
801 + "select_token": "Zabi JKE",
802 "select_your_country": "Da fatan za a zabi ƙasarku",
803 "selected_trocador_provider": "Zabi mai bada TORACAD",
804 "sell": "sayar",
res/values/strings_hi.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "अनुमानित नया शुल्क",
366 "estimated_receive_amount": "अनुमानित राशि",
367 "eta": "ईटा",
368 + "ethereum_ecosystem": "एथेरियम पारिस्थितिकी तंत्र",
369 "etherscan_history": "इथरस्कैन इतिहास",
370 "event": "आयोजन",
371 "events": "आयोजन",
372 + "evm_ecosystem_description": "यदि आवश्यक हो, तो उस नेटवर्क और टोकन को संशोधित करें जिसका उपयोग आप इस भुगतान के लिए करना चाहते हैं।",
373 "exchange": "अदला-बदली",
374 "exchange_extra_info": "कृपया एक्सचेंज के लिए इस लेनदेन को भेजते समय मेमो या अतिरिक्त आईडी निर्दिष्ट करना न भूलें",
375 "exchange_incorrect_current_wallet_for_xmr": "यदि आप अपने केक वॉलेट मोनेरो बैलेंस से XMR को स्वैप करना चाहते हैं, तो कृपया पहले अपने मोनेरो वॉलेट पर स्विच करें।",
@@ -794,7 +796,9 @@
796 "select_destination": "कृपया बैकअप फ़ाइल के लिए गंतव्य का चयन करें।",
797 "select_hw_account_below": "कृपया नीचे पुनर्स्थापित करने के लिए कौन सा खाता चुनें:",
798 "select_manufacturer_title": "निर्माता का चयन करें",
799 + "select_network": "नेटवर्क चुनें",
800 "select_sell_provider_notice": "ऊपर एक विक्रय प्रदाता का चयन करें। आप ऐप सेटिंग में अपना डिफ़ॉल्ट विक्रय प्रदाता सेट करके इस स्क्रीन को छोड़ सकते हैं।",
801 + "select_token": "टोकन चुनें",
802 "select_your_country": "कृपया अपने देश का चयन करें",
803 "selected_trocador_provider": "चयनित ट्रोकैडर प्रदाता",
804 "sell": "बेचना",
res/values/strings_hr.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Procijenjena nova naknada",
366 "estimated_receive_amount": "Procijenjeni iznos primanja",
367 "eta": "Eta",
368 + "ethereum_ecosystem": "Ethereum ekosustav",
369 "etherscan_history": "Etherscan povijest",
370 "event": "Događaj",
371 "events": "Događaji",
372 + "evm_ecosystem_description": "Ako je potrebno, promijenite mrežu i token koji želite koristiti za ovo plaćanje.",
373 "exchange": "Razmjena",
374 "exchange_extra_info": "Ne zaboravite navesti dopis ili dodatni ID dok šaljete ovu transakciju na razmjenu",
375 "exchange_incorrect_current_wallet_for_xmr": "Ako želite zamijeniti XMR iz vašeg novčanika za kolač Monero, prvo se prebacite na svoj novčanik Monero.",
@@ -792,7 +794,9 @@
794 "select_destination": "Odaberite odredište za datoteku sigurnosne kopije.",
795 "select_hw_account_below": "Molimo odaberite koji će se račun vratiti u nastavku:",
796 "select_manufacturer_title": "Odaberite proizvođača",
797 + "select_network": "Odaberite Mreža",
798 "select_sell_provider_notice": "Gore odaberite pružatelja usluga prodaje. Ovaj zaslon možete preskočiti postavljanjem zadanog pružatelja usluga prodaje u postavkama aplikacije.",
799 + "select_token": "Odaberite Token",
800 "select_your_country": "Odaberite svoju zemlju",
801 "selected_trocador_provider": "Odabrani pružatelj usluga trokadora",
802 "sell": "Prodavati",
res/values/strings_hy.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Գնահատված նոր միջնորդավճար",
366 "estimated_receive_amount": "Գնահատված ստացված գումար",
367 "eta": "Eta",
368 + "ethereum_ecosystem": "Ethereum էկոհամակարգ",
369 "etherscan_history": "Etherscan պատմություն",
370 "event": "Իրադարձություն",
371 "events": "Իրադարձություններ",
372 + "evm_ecosystem_description": "Անհրաժեշտության դեպքում փոփոխեք ցանցը և նշանը, որը ցանկանում եք օգտագործել այս վճարման համար:",
373 "exchange": "Փոխանակում",
374 "exchange_extra_info": "Խնդրում ենք մի մոռացեք նշել հուշագիրը կամ լրացուցիչ ID- ն, այս գործարքը փոխանակման համար ուղարկելիս",
375 "exchange_incorrect_current_wallet_for_xmr": "Եթե ​​ցանկանում եք փոխանակել XMR ձեր տորթի դրամապանակից Monero Relandal- ից, խնդրում ենք նախ անցնել ձեր Monero դրամապանակին:",
@@ -791,7 +793,9 @@
793 "select_destination": "Խնդրում ենք ընտրել կրկնօրինակ ֆայլի նպատակակետը",
794 "select_hw_account_below": "Խնդրում ենք ընտրել, թե որ հաշիվն է վերականգնել հետեւյալը.",
795 "select_manufacturer_title": "Ընտրեք արտադրող",
796 + "select_network": "Ընտրեք Ցանց",
797 "select_sell_provider_notice": "Ընտրեք վաճառքի մատակարարը վերևում։ Դուք կարող եք բաց թողնել այս էկրանը ձեր լռելայն վաճառքի մատակարարը հավելվածի կարգավորումներում սահմանելով",
798 + "select_token": "Ընտրեք նշան",
799 "select_your_country": "Խնդրում ենք ընտրել ձեր երկիրը",
800 "sell": "Ծախել",
801 "sell_alert_content": "Մենք ներկայումս պաշտպանում ենք միայն Bitcoin, Ethereum և Litecoin վաճառքը։ Խնդրում ենք ստեղծել կամ միացնել ձեր Bitcoin, Ethereum կամ Litecoin դրամապանակը",
res/values/strings_id.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Perkiraan biaya baru",
366 "estimated_receive_amount": "Diperkirakan jumlah menerima",
367 "eta": "Eta",
368 + "ethereum_ecosystem": "Ekosistem Ethereum",
369 "etherscan_history": "Sejarah Etherscan",
370 "event": "Peristiwa",
371 "events": "Acara",
372 + "evm_ecosystem_description": "Jika perlu, ubah jaringan dan token yang ingin Anda gunakan untuk pembayaran ini.",
373 "exchange": "Menukarkan",
374 "exchange_extra_info": "Harap jangan lupa untuk menentukan memo atau ID tambahan saat mengirim transaksi ini untuk pertukaran",
375 "exchange_incorrect_current_wallet_for_xmr": "Jika Anda ingin bertukar XMR dari Saldo Monero Dompet Kue Anda, silakan beralih ke Monero Wallet Anda terlebih dahulu.",
@@ -795,7 +797,9 @@
797 "select_destination": "Silakan pilih tujuan untuk file cadangan.",
798 "select_hw_account_below": "Pilih akun mana yang akan dikembalikan di bawah ini:",
799 "select_manufacturer_title": "Pilih produsen",
800 + "select_network": "Pilih Jaringan",
801 "select_sell_provider_notice": "Pilih penyedia jual di atas. Anda dapat melewati layar ini dengan mengatur penyedia penjualan default Anda di pengaturan aplikasi.",
802 + "select_token": "Pilih Token",
803 "select_your_country": "Pilih negara Anda",
804 "selected_trocador_provider": "Penyedia Trocador Terpilih",
805 "sell": "Jual",
res/values/strings_it.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Nuova commissione stimata",
366 "estimated_receive_amount": "Importo di ricezione stimato",
367 "eta": "Eta",
368 + "ethereum_ecosystem": "Ecosistema Ethereum",
369 "etherscan_history": "Cronologia Etherscan",
370 "event": "Evento",
371 "events": "Eventi",
372 + "evm_ecosystem_description": "Se necessario, modifica la rete e il token che desideri utilizzare per questo pagamento.",
373 "exchange": "Scambio",
374 "exchange_extra_info": "Non dimenticare di specificare la memo o l'ID extra durante l'invio di questa transazione per lo scambio",
375 "exchange_incorrect_current_wallet_for_xmr": "Se vuoi scambiare XMR dal tuo portafoglio Cake Wallet, devi prima passare al portafoglio Monero.",
@@ -793,7 +795,9 @@
795 "select_destination": "Seleziona la destinazione per il file di backup.",
796 "select_hw_account_below": "Seleziona quale account ripristinare:",
797 "select_manufacturer_title": "Seleziona il produttore",
798 + "select_network": "Seleziona Rete",
799 "select_sell_provider_notice": "Seleziona un provider di vendita sopra. Puoi saltare questa schermata impostando il tuo provider di vendita predefinito nelle impostazioni dell'app.",
800 + "select_token": "Seleziona Gettone",
801 "select_your_country": "Seleziona il tuo paese",
802 "selected_trocador_provider": "Provider di Trocador selezionato",
803 "sell": "Vendi",
res/values/strings_ja.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "推定新しい料金",
366 "estimated_receive_amount": "推定受信金額",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "イーサリアムエコシステム",
369 "etherscan_history": "イーサスキャンの歴史",
370 "event": "イベント",
371 "events": "イベント",
372 + "evm_ecosystem_description": "必要に応じて、この支払いに使用するネットワークとトークンを変更します。",
373 "exchange": "交換",
374 "exchange_extra_info": "Exchangeのためにこのトランザクションを送信する際に、メモまたは追加IDを指定することを忘れないでください",
375 "exchange_incorrect_current_wallet_for_xmr": "XMRをケーキウォレットモネロバランスから交換したい場合は、最初にMoneroウォレットに切り替えてください。",
@@ -793,7 +795,9 @@
795 "select_destination": "バックアップファイルの保存先を選択してください。",
796 "select_hw_account_below": "以下に復元するアカウントを選択してください。",
797 "select_manufacturer_title": "メーカーを選択します",
798 + "select_network": "ネットワークの選択",
799 "select_sell_provider_notice": "上記の販売プロバイダーを選択してください。アプリ設定でデフォルトの販売プロバイダーを設定することで、この画面をスキップできます。",
800 + "select_token": "トークンの選択",
801 "select_your_country": "あなたの国を選択してください",
802 "selected_trocador_provider": "選択したTrocadorプロバイダー",
803 "sell": "売る",
res/values/strings_ko.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "예상 새 수수료",
366 "estimated_receive_amount": "예상 수령 금액",
367 "eta": "에타",
368 + "ethereum_ecosystem": "이더리움 생태계",
369 "etherscan_history": "Etherscan 내역",
370 "event": "이벤트",
371 "events": "이벤트",
372 + "evm_ecosystem_description": "필요한 경우 이 결제에 사용할 네트워크와 토큰을 수정하세요.",
373 "exchange": "교환",
374 "exchange_extra_info": "교환을 위해이 거래를 보내는 동안 메모 또는 추가 ID를 지정하는 것을 잊지 마십시오.",
375 "exchange_incorrect_current_wallet_for_xmr": "Cake Wallet 모네로 잔액에서 XMR을 스왑하려면 먼저 모네로 지갑으로 전환하세요.",
@@ -793,7 +795,9 @@
795 "select_destination": "백업 파일의 대상을 선택하세요.",
796 "select_hw_account_below": "아래에서 복구할 계정을 선택하세요:",
797 "select_manufacturer_title": "제조업체를 선택하십시오",
798 + "select_network": "네트워크 선택",
799 "select_sell_provider_notice": "위에서 판매 제공 업체를 선택하세요. 앱 설정에서 기본 판매 제공 업체를 설정하여 이 화면을 건너뛸 수 있습니다.",
800 + "select_token": "토큰 선택",
801 "select_your_country": "국가를 선택하세요",
802 "selected_trocador_provider": "선택된 Trocador 제공 업체",
803 "sell": "판매",
res/values/strings_my.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "ခန့်မှန်းသစ်ခန့်မှန်း",
366 "estimated_receive_amount": "ခန့်မှန်းရရှိသောပမာဏ",
367 "eta": "eta",
368 + "ethereum_ecosystem": "Etheumeum ဂေဟစနစ်",
369 "etherscan_history": "Etherscan သမိုင်း",
370 "event": "ပွဲ",
371 "events": "အဲ့ဒါနဲ့",
372 + "evm_ecosystem_description": "လိုအပ်ပါကဤငွေပေးချေမှုအတွက်သင်အသုံးပြုလိုသည့်ကွန်ယက်နှင့်တိုကင်ကိုပြင်ဆင်ပါ။",
373 "exchange": "လဲလှယ်",
374 "exchange_extra_info": "ငွေလဲလှယ်မှုအတွက်ဤငွေပေးငွေယူကိုပေးပို့စဉ်မှတ်စုတိုသို့မဟုတ်အပို ID ကိုသတ်မှတ်ရန်မမေ့ပါနှင့်",
375 "exchange_incorrect_current_wallet_for_xmr": "အကယ်. သင်သည် XMR ကိုသင်၏ကိတ်မုန့် Monero Balance မှ Swap ကိုလဲလှယ်လိုပါကသင်၏ Monero Wallet ကိုပထမဆုံးအကြိမ်ပြောင်းပါ။",
@@ -792,7 +794,9 @@
794 "select_destination": "အရန်ဖိုင်အတွက် ဦးတည်ရာကို ရွေးပါ။",
795 "select_hw_account_below": "အောက်ဖော်ပြပါမည်သည့်အကောင့်ကိုရွေးပါ။",
796 "select_manufacturer_title": "ထုတ်လုပ်သူကိုရွေးချယ်ပါ",
797 + "select_network": "ကွန်ယက်ကိုရွေးချယ်ပါ",
798 "select_sell_provider_notice": "အထက်ဖော်ပြပါ အရောင်းဝန်ဆောင်မှုပေးသူကို ရွေးပါ။ အက်ပ်ဆက်တင်များတွင် သင်၏မူလရောင်းချပေးသူကို သတ်မှတ်ခြင်းဖြင့် ဤစခရင်ကို ကျော်နိုင်သည်။",
799 + "select_token": "Token ကိုရွေးချယ်ပါ",
800 "select_your_country": "ကျေးဇူးပြု. သင့်နိုင်ငံကိုရွေးချယ်ပါ",
801 "selected_trocador_provider": "ရွေးချယ်ထား Tracador ပံ့ပိုးပေး",
802 "sell": "ရောင်း",
res/values/strings_nl.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Geschatte nieuwe vergoeding",
366 "estimated_receive_amount": "Geschat ontvangen bedrag",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "Ethereum-ecosysteem",
369 "etherscan_history": "Etherscan-geschiedenis",
370 "event": "Evenement",
371 "events": "Evenementen",
372 + "evm_ecosystem_description": "Wijzig indien nodig het netwerk en het token dat u voor deze betaling wilt gebruiken.",
373 "exchange": "Aandelenbeurs",
374 "exchange_extra_info": "Vergeet niet om de memo of extra ID op te geven tijdens het verzenden van deze transactie voor de uitwisseling",
375 "exchange_incorrect_current_wallet_for_xmr": "Als je XMR uit je cake -portemonnee Monero -balans wilt ruilen, schakel dan eerst over naar je Monero -portemonnee.",
@@ -792,7 +794,9 @@
794 "select_destination": "Selecteer de bestemming voor het back-upbestand.",
795 "select_hw_account_below": "Selecteer welk account u hieronder moet herstellen:",
796 "select_manufacturer_title": "Selecteer fabrikant",
797 + "select_network": "Selecteer Netwerk",
798 "select_sell_provider_notice": "Selecteer hierboven een verkoopaanbieder. U kunt dit scherm overslaan door uw standaardverkoopprovider in te stellen in de app-instellingen.",
799 + "select_token": "Selecteer Token",
800 "select_your_country": "Selecteer uw land",
801 "selected_trocador_provider": "Geselecteerde Trocador -provider",
802 "sell": "Verkopen",
res/values/strings_pl.arb
+3
@@ -366,6 +366,7 @@
366 "estimated_receive_amount": "Szacowana kwota otrzymania",
367 "eta": "ETA",
368 "etherscan_history": "Historia Etherscan",
369 + "ethereum_ecosystem": "Ekosystem Ethereum",
370 "event": "Zdarzenie",
371 "events": "Zdarzenia",
372 "exchange": "Wymiana",
@@ -792,7 +793,9 @@
793 "select_destination": "Wybierz miejsce docelowe dla pliku kopii zapasowej.",
794 "select_hw_account_below": "Wybierz konto, które chcesz przywrócić poniżej:",
795 "select_manufacturer_title": "Wybierz producenta",
796 + "select_network": "Wybierz opcję Sieć",
797 "select_sell_provider_notice": "Wybierz dostawcę sprzedaży powyżej. Możesz pominąć ten ekran, ustawiając domyślnego dostawcę sprzedaży w ustawieniach aplikacji.",
798 + "select_token": "Wybierz Token",
799 "select_your_country": "Wybierz swój kraj",
800 "selected_trocador_provider": "wybrany dostawca Trocador",
801 "sell": "Sprzedaj",
res/values/strings_pt.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Nova taxa estimada",
366 "estimated_receive_amount": "Valor estimado de recebimento",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "Ecossistema Ethereum",
369 "etherscan_history": "história Etherscan",
370 "event": "Evento",
371 "events": "Eventos",
372 + "evm_ecosystem_description": "Se necessário, modifique a rede e o token que você gostaria de usar para este pagamento.",
373 "exchange": "Intercâmbio",
374 "exchange_extra_info": "Por favor, não se esqueça de especificar o memorando ou o ID extra ao enviar esta transação para a troca",
375 "exchange_incorrect_current_wallet_for_xmr": "Se você deseja trocar o XMR do balanço da Cake Wallet, mude para a sua carteira Monero primeiro.",
@@ -794,7 +796,9 @@
796 "select_destination": "Selecione o destino para o arquivo de backup.",
797 "select_hw_account_below": "Selecione qual conta para restaurar abaixo:",
798 "select_manufacturer_title": "Selecione o fabricante",
799 + "select_network": "Selecione Rede",
800 "select_sell_provider_notice": "Selecione um fornecedor de venda acima. Você pode pular esta tela definindo seu provedor de venda padrão nas configurações do aplicativo.",
801 + "select_token": "Selecione o token",
802 "select_your_country": "Selecione seu país",
803 "selected_trocador_provider": "Provedor de Trocador selecionado",
804 "sell": "Vender",
res/values/strings_ru.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Расчетная новая плата",
366 "estimated_receive_amount": "Расчетная сумма получения",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "Экосистема Эфириума",
369 "etherscan_history": "История Эфириума",
370 "event": "Событие",
371 "events": "События",
372 + "evm_ecosystem_description": "При необходимости измените сеть и токен, который вы хотите использовать для этого платежа.",
373 "exchange": "Обмен",
374 "exchange_extra_info": "Пожалуйста, не забудьте указать меморандум или дополнительный идентификатор при отправке этой транзакции для обмена",
375 "exchange_incorrect_current_wallet_for_xmr": "Если вы хотите поменять XMR с баланса с кошельком для торта Monero, сначала переключитесь на свой кошелек Monero.",
@@ -793,7 +795,9 @@
795 "select_destination": "Пожалуйста, выберите место для файла резервной копии.",
796 "select_hw_account_below": "Пожалуйста, выберите, какую учетную запись восстановить ниже:",
797 "select_manufacturer_title": "Выберите производителя",
798 + "select_network": "Выберите сеть",
799 "select_sell_provider_notice": "Выберите поставщика услуг продажи выше. Вы можете пропустить этот экран, установив поставщика услуг продаж по умолчанию в настройках приложения.",
800 + "select_token": "Выберите токен",
801 "select_your_country": "Пожалуйста, выберите свою страну",
802 "selected_trocador_provider": "Выбранный провайдер Трокадора",
803 "sell": "Продавать",
res/values/strings_th.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "ค่าธรรมเนียมใหม่โดยประมาณ",
366 "estimated_receive_amount": "โดยประมาณว่าจำนวนเงินที่ได้รับ",
367 "eta": "กทพ.",
368 + "ethereum_ecosystem": "ระบบนิเวศอีเธอเรียม",
369 "etherscan_history": "ประวัติอีเธอร์สแกน",
370 "event": "เหตุการณ์",
371 "events": "กิจกรรม",
372 + "evm_ecosystem_description": "หากจำเป็น ให้แก้ไขเครือข่ายและโทเค็นที่คุณต้องการใช้สำหรับการชำระเงินนี้",
373 "exchange": "แลกเปลี่ยน",
374 "exchange_extra_info": "โปรดอย่าลืมระบุบันทึกหรือรหัสพิเศษในขณะที่ส่งธุรกรรมนี้สำหรับการแลกเปลี่ยน",
375 "exchange_incorrect_current_wallet_for_xmr": "หากคุณต้องการสลับ XMR จาก Cake Wallet Monero Balance โปรดเปลี่ยนไปใช้กระเป๋าเงิน Monero ก่อน",
@@ -792,7 +794,9 @@
794 "select_destination": "โปรดเลือกปลายทางสำหรับไฟล์สำรอง",
795 "select_hw_account_below": "กรุณาเลือกบัญชีที่จะกู้คืนด้านล่าง:",
796 "select_manufacturer_title": "เลือกผู้ผลิต",
797 + "select_network": "เลือกเครือข่าย",
798 "select_sell_provider_notice": "เลือกผู้ให้บริการการขายด้านบน คุณสามารถข้ามหน้าจอนี้ได้โดยการตั้งค่าผู้ให้บริการการขายเริ่มต้นในการตั้งค่าแอป",
799 + "select_token": "เลือกโทเค็น",
800 "select_your_country": "กรุณาเลือกประเทศของคุณ",
801 "selected_trocador_provider": "ผู้ให้บริการ Trocador ที่เลือก",
802 "sell": "ขาย",
res/values/strings_tl.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Tinatayang bagong fee",
366 "estimated_receive_amount": "Tinatayang natanggap na halaga",
367 "eta": "Eta",
368 + "ethereum_ecosystem": "Ethereum ecosystem",
369 "etherscan_history": "Kasaysayan ng Etherscan",
370 "event": "Kaganapan",
371 "events": "Mga kaganapan",
372 + "evm_ecosystem_description": "Kung kinakailangan, baguhin ang network at token na nais mong gamitin para sa pagbabayad na ito.",
373 "exchange": "Palitan",
374 "exchange_extra_info": "Mangyaring huwag kalimutan na tukuyin ang memo o dagdag na ID habang ipinapadala ang transaksyon na ito para sa palitan",
375 "exchange_incorrect_current_wallet_for_xmr": "Kung nais mong magpalit ng XMR mula sa iyong balanse ng Wallet Monero, mangyaring lumipat sa iyong Monero Wallet muna.",
@@ -792,7 +794,9 @@
794 "select_destination": "Mangyaring piliin ang patutunguhan para sa backup na file.",
795 "select_hw_account_below": "Mangyaring piliin kung aling account ang ibabalik sa ibaba:",
796 "select_manufacturer_title": "Piliin ang Tagagawa",
797 + "select_network": "Piliin ang Network",
798 "select_sell_provider_notice": "Pumili ng provider ng nagbebenta sa itaas. Maaari mong laktawan ang screen na ito sa pamamagitan ng pagtatakda ng iyong default na sell provider sa mga setting ng app.",
799 + "select_token": "Piliin ang Token",
800 "select_your_country": "Mangyaring piliin ang iyong bansa",
801 "selected_trocador_provider": "Napiling Trocador Provider",
802 "sell": "Ibenta",
res/values/strings_tr.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Tahmini yeni ücret",
366 "estimated_receive_amount": "Tahmini alma miktarı",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "Ethereum Ekosistemi",
369 "etherscan_history": "Etherscan geçmişi",
370 "event": "Etkinlik",
371 "events": "Olaylar",
372 + "evm_ecosystem_description": "Gerekirse bu ödeme için kullanmak istediğiniz ağı ve jetonu değiştirin.",
373 "exchange": "Değişme",
374 "exchange_extra_info": "Bu işlemi borsaya gönderirken lütfen notu veya ekstra kimliği belirtmeyi unutmayın.",
375 "exchange_incorrect_current_wallet_for_xmr": "XMR'yi kek cüzdanı Monero bakiyenizden değiştirmek istiyorsanız, lütfen önce Monero cüzdanınıza geçin.",
@@ -792,7 +794,9 @@
794 "select_destination": "Lütfen yedekleme dosyası için hedef seçin.",
795 "select_hw_account_below": "Lütfen aşağıda hangi hesabı geri yükleyeceğinizi seçin:",
796 "select_manufacturer_title": "Üretici seçin",
797 + "select_network": "Ağ Seçin",
798 "select_sell_provider_notice": "Yukarıdan bir satış sağlayıcısı seçin. Uygulama ayarlarında varsayılan satış sağlayıcınızı ayarlayarak bu ekranı atlayabilirsiniz.",
799 + "select_token": "Jeton Seç",
800 "select_your_country": "Lütfen ülkenizi seçin",
801 "selected_trocador_provider": "Seçilmiş Trocador Sağlayıcı",
802 "sell": "Satış",
res/values/strings_uk.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "Орієнтовна нова комісія",
366 "estimated_receive_amount": "Орієнтовна сума отримує",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "Екосистема Ethereum",
369 "etherscan_history": "Історія Etherscan",
370 "event": "Подія",
371 "events": "Події",
372 + "evm_ecosystem_description": "Якщо необхідно, змініть мережу та токен, який ви хочете використовувати для цього платежу.",
373 "exchange": "Обмін",
374 "exchange_extra_info": "Не забудьте вказати пам’ятку або додатковий ідентифікатор під час надсилання цієї транзакції для обміну",
375 "exchange_incorrect_current_wallet_for_xmr": "Якщо ви хочете поміняти XMR зі свого балансу для тортів Monero Balance, спочатку перейдіть на свій гаманець Monero.",
@@ -793,7 +795,9 @@
795 "select_destination": "Виберіть місце призначення для файлу резервної копії.",
796 "select_hw_account_below": "Виберіть, який рахунок відновити нижче:",
797 "select_manufacturer_title": "Виберіть виробника",
798 + "select_network": "Виберіть Мережа",
799 "select_sell_provider_notice": "Виберіть вище постачальника послуг продажу. Ви можете пропустити цей екран, встановивши постачальника послуг продажу за умовчанням у налаштуваннях програми.",
800 + "select_token": "Виберіть Токен",
801 "select_your_country": "Будь ласка, виберіть свою країну",
802 "selected_trocador_provider": "Вибраний постачальник Trocador",
803 "sell": "Продати",
res/values/strings_ur.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "تخمینہ شدہ نئی فیس",
366 "estimated_receive_amount": "تخمینہ وصول کی رقم",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "ایتھریم ماحولیاتی نظام",
369 "etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ",
370 "event": "ﺐﯾﺮﻘﺗ",
371 "events": "ﺕﺎﺒﯾﺮﻘﺗ",
372 + "evm_ecosystem_description": "اگر ضروری ہو تو ، نیٹ ورک اور ٹوکن میں ترمیم کریں جس کی آپ اس ادائیگی کے لئے استعمال کرنا چاہتے ہیں۔",
373 "exchange": "تبادلہ",
374 "exchange_extra_info": "ایکسچینج کے ل this اس لین دین کو بھیجتے وقت براہ کرم میمو یا اضافی ID کی وضاحت کرنا نہ بھولیں",
375 "exchange_incorrect_current_wallet_for_xmr": "اگر آپ اپنے کیک پرس مونیرو بیلنس سے XMR تبدیل کرنا چاہتے ہیں تو ، براہ کرم پہلے اپنے مونیرو پرس میں جائیں۔",
@@ -794,7 +796,9 @@
796 "select_destination": "۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﻝﺰﻨﻣ ﮯﯿﻟ ﮯﮐ ﻞﺋﺎﻓ ﭖﺍ ﮏﯿﺑ ﻡﺮﮐ ﮦﺍﺮﺑ",
797 "select_hw_account_below": "براہ کرم ذیل میں کون سا اکاؤنٹ بحال کرنا ہے منتخب کریں:",
798 "select_manufacturer_title": "کارخانہ دار کو منتخب کریں",
799 + "select_network": "نیٹ ورک کو منتخب کریں",
800 "select_sell_provider_notice": "۔ﮟﯿﮨ ﮯﺘﮑﺳ ﮌﻮﮭﭼ ﻮﮐ ﻦﯾﺮﮑﺳﺍ ﺱﺍ ﺮﮐ ﮮﺩ ﺐﯿﺗﺮﺗ ﻮﮐ ﮦﺪﻨﻨﮐ ﻢﮨﺍﺮﻓ ﻞﯿﺳ ﭧﻟﺎﻔﯾﮈ ﮯﻨﭘﺍ ﮟﯿﻣ ﺕﺎﺒ",
801 + "select_token": "ٹوکن منتخب کریں",
802 "select_your_country": "براہ کرم اپنے ملک کو منتخب کریں",
803 "selected_trocador_provider": "منتخب کردہ ٹروکاڈور فراہم کنندہ",
804 "sell": "بیچنا",
res/values/strings_vi.arb
+4
@@ -364,9 +364,11 @@
364 "estimated_new_fee": "Phí mới ước tính",
365 "estimated_receive_amount": "Số tiền nhận ước tính",
366 "eta": "Eta",
367 + "ethereum_ecosystem": "Hệ sinh thái Ethereum",
368 "etherscan_history": "Lịch sử Etherscan",
369 "event": "Sự kiện",
370 "events": "Các sự kiện",
371 + "evm_ecosystem_description": "Nếu cần, hãy sửa đổi mạng và mã thông báo bạn muốn sử dụng cho khoản thanh toán này.",
372 "exchange": "Trao đổi",
373 "exchange_extra_info": "Xin đừng quên chỉ định bản ghi nhớ hoặc ID bổ sung trong khi gửi giao dịch này để trao đổi",
374 "exchange_incorrect_current_wallet_for_xmr": "Nếu bạn muốn trao đổi XMR từ CAPE CAME MONERO BALANCE, vui lòng chuyển sang ví Monero của bạn trước.",
@@ -790,7 +792,9 @@
792 "select_destination": "Vui lòng chọn đích cho tệp sao lưu.",
793 "select_hw_account_below": "Vui lòng chọn tài khoản nào để khôi phục bên dưới:",
794 "select_manufacturer_title": "Chọn nhà sản xuất",
795 + "select_network": "Chọn mạng",
796 "select_sell_provider_notice": "Chọn nhà cung cấp bán ở trên. Bạn có thể bỏ qua màn hình này bằng cách thiết lập nhà cung cấp bán mặc định trong cài đặt ứng dụng.",
797 + "select_token": "Chọn mã thông báo",
798 "select_your_country": "Vui lòng chọn quốc gia của bạn",
799 "sell": "Bán",
800 "sell_alert_content": "Hiện tại chúng tôi chỉ hỗ trợ bán Bitcoin, Ethereum và Litecoin. Vui lòng tạo hoặc chuyển sang ví Bitcoin, Ethereum hoặc Litecoin của bạn.",
res/values/strings_yo.arb
+4
@@ -366,9 +366,11 @@
366 "estimated_new_fee": "Ifoju tuntun owo tuntun",
367 "estimated_receive_amount": "Ifoju gba iye",
368 "eta": "Eta",
369 + "ethereum_ecosystem": "Etheum Ecosystem",
370 "etherscan_history": "Etherscan itan",
371 "event": "Iṣẹlẹ",
372 "events": "Awọn iṣẹlẹ",
373 + "evm_ecosystem_description": "Ti o ba jẹ dandan, yi nẹtiwọki pada ati ami iwọ yoo fẹ lati lo fun isanwo yii.",
374 "exchange": "Paarọ",
375 "exchange_extra_info": "Jọwọ maṣe gbagbe lati tokasi akọsilẹ tabi id afikun lakoko ti o nfi iṣowo yii fun paṣipaarọ naa",
376 "exchange_incorrect_current_wallet_for_xmr": "Ti o ba fẹ lati yi XMR lati dọgba oyinbo oyinbo kekere rẹ ti a fi omi ṣan rẹ, jọwọ yipada si apamọwọ Monrou akọkọ.",
@@ -793,7 +795,9 @@
795 "select_destination": "Jọwọ yan ibi ti o nlo fun faili afẹyinti.",
796 "select_hw_account_below": "Jọwọ yan iru iroyin lati mu pada ni isalẹ:",
797 "select_manufacturer_title": "Yan olupese",
798 + "select_network": "Yan Nẹtiwọọki",
799 "select_sell_provider_notice": "Yan olupese ti o ta loke. O le foju iboju yii nipa tito olupese iṣẹ tita aiyipada rẹ ni awọn eto app.",
800 + "select_token": "Yan Token",
801 "select_your_country": "Jọwọ yan orilẹ-ede rẹ",
802 "selected_trocador_provider": "olupese trocador ti a yan",
803 "sell": "Tà",
res/values/strings_zh.arb
+4
@@ -365,9 +365,11 @@
365 "estimated_new_fee": "估计新费用",
366 "estimated_receive_amount": "估计接收金额",
367 "eta": "ETA",
368 + "ethereum_ecosystem": "以太坊生态系统",
369 "etherscan_history": "以太扫描历史",
370 "event": "事件",
371 "events": "活动",
372 + "evm_ecosystem_description": "如有必要,请修改您要用于此付款的网络和令牌。",
373 "exchange": "交换",
374 "exchange_extra_info": "请不要忘记在向交易所发送此交易时指定备忘录或额外的ID",
375 "exchange_incorrect_current_wallet_for_xmr": "如果您想从蛋糕钱包Monero余额中交换XMR,请先切换到Monero Wallet。",
@@ -792,7 +794,9 @@
794 "select_destination": "请选择备份文件的目的地。",
795 "select_hw_account_below": "请在下面选择要还原的帐户:",
796 "select_manufacturer_title": "选择制造商",
797 + "select_network": "选择网络",
798 "select_sell_provider_notice": "选择上面的销售提供商。您可以通过在应用程序设置中设置默认销售提供商来跳过此屏幕。",
799 + "select_token": "选择代币",
800 "select_your_country": "请选择你的国家",
801 "selected_trocador_provider": "选定的trocador提供商",
802 "sell": "卖",
test/core/universal_address_detector_test.dart
+30
@@ -46,6 +46,36 @@ void main() {
46 expect(result.address, '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa');
47 expect(result.amount, '0.001');
48 });
49 +
50 + test('detects Lightning invoice', () {
51 + const invoice = 'lnbc1u1p3zj7j8pp5vkwxc4vxfzgzxcjzk5ksp0l7jgrgzmqxqmexxp8e8gfqk6s0knnqdlc35hsggzypnxw7xxxnrvd3hscqzpgxqrrsssp5txs5g8vkdmdtnm8g5v9m3u094nnxkl0jjf38jj5xn2pptxlqkwn7gpgfkt7q6z0frcj7vjy4mv7659hccln2e0gzt4ufnsukxcnknh7e58kwnr8pk6kgv6vrghcpld2z5n';
52 + final result = UniversalAddressDetector.detectAddress(invoice);
53 +
54 + expect(result.isValid, true);
55 + expect(result.detectedCurrency, CryptoCurrency.btcln);
56 + expect(result.detectedWalletType, WalletType.bitcoin);
57 + expect(result.address, invoice);
58 + });
59 +
60 + test('detects Lightning address', () {
61 + const address = 'alice@lightning.com';
62 + final result = UniversalAddressDetector.detectAddress(address);
63 +
64 + expect(result.isValid, true);
65 + expect(result.detectedCurrency, CryptoCurrency.btcln);
66 + expect(result.detectedWalletType, WalletType.bitcoin);
67 + expect(result.address, address);
68 + });
69 +
70 + test('detects LNURL format', () {
71 + const address = 'LNURL1DP68GURN8GHJ7UM9WFMXCCRVFH8RHYEE5QDKURZQDF8JUK6RKCE0E';
72 + final result = UniversalAddressDetector.detectAddress(address);
73 +
74 + expect(result.isValid, true);
75 + expect(result.detectedCurrency, CryptoCurrency.btcln);
76 + expect(result.detectedWalletType, WalletType.bitcoin);
77 + expect(result.address, address);
78 + });
79 });
80
81 group('Monero Address Detection', () {