feat: Pay Anything from Any Wallet (#2450)
* feat: Pay Anything from Any Wallet * feat: Trigger payment flow when address is pasted * chore: Remove unneeded changes * feat Expand address checks and fix scanning bch qrs * fix: Add support for dogecoin * feat: More modifications for Pay from Any Wallet This change: - Adds the pay from any wallet flow to externally signed QR codes - Adds fixes for review comments - Fixes failing tests --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
David Adegoke committed
Aug 21, 2025 at 16:37 UTC
13a74133f512d61a80d149393edfdcdecbcea3ef
39 files changed
+1228
-27
cw_core/lib/crypto_currency.dart
+22
@@ -261,6 +261,24 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
261
return acc;
262
});
263
264
+ // Scheme to currency mapping for URI scheme
265
+ static final Map<String, CryptoCurrency> _schemeCurrencyMap = {
266
+ 'bitcoin': btc,
267
+ 'bitcoincash': bch,
268
+ 'polygon': maticpoly,
269
+ 'nano-gpt': nano,
270
+ 'secret': scrt,
271
+ 'stellar': xlm,
272
+ 'avalanche': avaxc,
273
+ 'binance': bnb,
274
+ 'tether': usdt,
275
+ 'usdcoin': usdc,
276
+ 'wrappedbitcoin': wbtc,
277
+ 'wrappedether': weth,
278
+ 'shibainu': shib,
279
+ 'zcash': zec,
280
+ };
281
+
282
static CryptoCurrency deserialize({required int raw}) {
283
if (CryptoCurrency._rawCurrencyMap[raw] == null) {
284
final s = 'Unexpected token: $raw for CryptoCurrency deserialize';
@@ -271,6 +289,10 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
289
290
// TODO: refactor this
291
static CryptoCurrency fromString(String name, {CryptoCurrency? walletCurrency}) {
292
+
293
+ final schemeMatch = _schemeCurrencyMap[name.toLowerCase()];
294
+ if (schemeMatch != null) return schemeMatch;
295
+
296
try {
297
return CryptoCurrency.all.firstWhere((element) =>
298
element.title.toLowerCase() == name.toLowerCase() &&
lib/core/universal_address_detector.dart
new
+232
@@ -0,0 +1,232 @@
1
+import 'package:cake_wallet/utils/payment_request.dart';
2
+import 'package:cw_core/crypto_currency.dart';
3
+import 'package:cw_core/wallet_type.dart';
4
+
5
+class AddressDetectionResult {
6
+ AddressDetectionResult({
7
+ required this.address,
8
+ this.detectedCurrency,
9
+ this.detectedWalletType,
10
+ this.amount,
11
+ this.note,
12
+ this.scheme,
13
+ this.pjUri,
14
+ this.callbackUrl,
15
+ this.callbackMessage,
16
+ required this.isValid,
17
+ this.errorMessage,
18
+ });
19
+
20
+ final String address;
21
+ final CryptoCurrency? detectedCurrency;
22
+ final WalletType? detectedWalletType;
23
+ final String? amount;
24
+ final String? note;
25
+ final String? scheme;
26
+ final String? pjUri;
27
+ final String? callbackUrl;
28
+ final String? callbackMessage;
29
+ final bool isValid;
30
+ final String? errorMessage;
31
+}
32
+
33
+/// Universal address detector that can identify cryptocurrency addresses from various formats
34
+class UniversalAddressDetector {
35
+ /// Detects cryptocurrency address from various input formats
36
+ /// Supports: raw addresses, URIs, and QR codes
37
+ static AddressDetectionResult detectAddress(String input) {
38
+ if (input.trim().isEmpty) {
39
+ return AddressDetectionResult(
40
+ address: '',
41
+ detectedCurrency: null,
42
+ isValid: false,
43
+ errorMessage: 'Empty input provided',
44
+ );
45
+ }
46
+
47
+ // Try to parse as URI first
48
+ final uriResult = _detectFromUri(input);
49
+ if (uriResult.isValid) return uriResult;
50
+
51
+ // Try to detect from raw address patterns
52
+ final rawAddressResult = _detectFromRawAddress(input);
53
+ if (rawAddressResult.isValid) return rawAddressResult;
54
+
55
+ return AddressDetectionResult(
56
+ address: input,
57
+ detectedCurrency: null,
58
+ isValid: false,
59
+ errorMessage: 'Unable to detect valid cryptocurrency address',
60
+ );
61
+ }
62
+
63
+ /// Detects address from URI format (e.g., bitcoin:address?amount=0.001)
64
+ static AddressDetectionResult _detectFromUri(String input) {
65
+ try {
66
+ final uri = Uri.parse(input);
67
+
68
+ final paymentRequest = PaymentRequest.fromUri(uri);
69
+
70
+ // Determine currency from scheme
71
+ final currency = CryptoCurrency.fromString(uri.scheme.toLowerCase());
72
+
73
+ return AddressDetectionResult(
74
+ address: paymentRequest.address,
75
+ detectedCurrency: currency,
76
+ detectedWalletType: cryptoCurrencyToWalletType(currency),
77
+ amount: paymentRequest.amount,
78
+ note: paymentRequest.note,
79
+ scheme: paymentRequest.scheme,
80
+ pjUri: paymentRequest.pjUri,
81
+ callbackUrl: paymentRequest.callbackUrl,
82
+ callbackMessage: paymentRequest.callbackMessage,
83
+ isValid: true,
84
+ );
85
+ } catch (e) {
86
+ return AddressDetectionResult(
87
+ address: input,
88
+ detectedCurrency: null,
89
+ isValid: false,
90
+ errorMessage: 'Failed to parse URI: $e',
91
+ );
92
+ }
93
+ }
94
+
95
+ /// Detect address from raw address patterns
96
+ static AddressDetectionResult _detectFromRawAddress(String input) {
97
+ final cleanInput = input.trim();
98
+
99
+ // Detection patterns ordered by specificity (most specific first)
100
+ final detectionPatterns = [
101
+ // Lightning Network
102
+ _DetectionPattern(
103
+ pattern: RegExp(r'^(lnbc|LNBC)[a-km-zA-HJ-NP-Z1-9]{1,}[a-zA-Z0-9]+$'),
104
+ currency: CryptoCurrency.btcln,
105
+ ),
106
+
107
+ // Bitcoin Bech32
108
+ _DetectionPattern(
109
+ pattern: RegExp(r'^bc1[a-km-zA-HJ-NP-Z1-9]{25,39}$'),
110
+ currency: CryptoCurrency.btc,
111
+ ),
112
+
113
+ // Litecoin Bech32
114
+ _DetectionPattern(
115
+ pattern: RegExp(r'^ltc1[a-z0-9]{25,50}$'),
116
+ currency: CryptoCurrency.ltc,
117
+ ),
118
+
119
+ // Bitcoin Cash
120
+ _DetectionPattern(
121
+ pattern: RegExp(r'^(q|p)[a-z0-9]{41}$'),
122
+ currency: CryptoCurrency.bch,
123
+ ),
124
+
125
+ // Ethereum
126
+ _DetectionPattern(
127
+ pattern: RegExp(r'^0x[a-fA-F0-9]{40}$'),
128
+ currency: CryptoCurrency.eth,
129
+ ),
130
+
131
+ // Nano
132
+ _DetectionPattern(
133
+ pattern: RegExp(r'^nano_[a-km-zA-HJ-NP-Z1-9]{60}$'),
134
+ currency: CryptoCurrency.nano,
135
+ ),
136
+
137
+ // Banano
138
+ _DetectionPattern(
139
+ pattern: RegExp(r'^ban_[a-km-zA-HJ-NP-Z1-9]{60}$'),
140
+ currency: CryptoCurrency.banano,
141
+ ),
142
+
143
+ // Tron
144
+ _DetectionPattern(
145
+ pattern: RegExp(r'^T[a-km-zA-HJ-NP-Z1-9]{33}$'),
146
+ currency: CryptoCurrency.trx,
147
+ ),
148
+
149
+ // Zano alias
150
+ _DetectionPattern(
151
+ pattern: RegExp(r'^@[\w\d.-]+$'),
152
+ currency: CryptoCurrency.zano,
153
+ ),
154
+
155
+ // Monero (standard) - 95 characters
156
+ _DetectionPattern(
157
+ pattern: RegExp(r'^4[a-km-zA-HJ-NP-Z1-9]{94}$'),
158
+ currency: CryptoCurrency.xmr,
159
+ ),
160
+
161
+ // Monero (integrated) - 95 characters
162
+ _DetectionPattern(
163
+ pattern: RegExp(r'^8[a-km-zA-HJ-NP-Z1-9]{94}$'),
164
+ currency: CryptoCurrency.xmr,
165
+ ),
166
+
167
+ // Wownero - 97 characters
168
+ _DetectionPattern(
169
+ pattern: RegExp(r'^W[a-km-zA-HJ-NP-Z1-9]{96}$'),
170
+ currency: CryptoCurrency.wow,
171
+ ),
172
+
173
+ // Zano (long addresses) - 97 characters
174
+ _DetectionPattern(
175
+ pattern: RegExp(r'^Z[a-km-zA-HJ-NP-Z1-9]{96}$'),
176
+ currency: CryptoCurrency.zano,
177
+ ),
178
+
179
+ // Decred
180
+ _DetectionPattern(
181
+ pattern: RegExp(r'^(D|T|S)[ksecS][a-km-zA-HJ-NP-Z1-9]+$'),
182
+ currency: CryptoCurrency.dcr,
183
+ ),
184
+
185
+ // Bitcoin P2PKH/P2SH (legacy formats)
186
+ _DetectionPattern(
187
+ pattern: RegExp(r'^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$'),
188
+ currency: CryptoCurrency.btc,
189
+ ),
190
+
191
+ // Litecoin P2PKH/P2SH
192
+ _DetectionPattern(
193
+ pattern: RegExp(r'^(L|M|3)[a-km-zA-HJ-NP-Z1-9]{25,34}$'),
194
+ currency: CryptoCurrency.ltc,
195
+ ),
196
+
197
+ // Solana (Base58 format, 32-44 chars)
198
+ _DetectionPattern(
199
+ pattern: RegExp(r'^[1-9A-HJ-NP-Za-km-z]{32,44}$'),
200
+ currency: CryptoCurrency.sol,
201
+ ),
202
+ ];
203
+
204
+ // Test each pattern in order of specificity
205
+ for (final pattern in detectionPatterns) {
206
+ if (pattern.pattern.hasMatch(cleanInput)) {
207
+ return AddressDetectionResult(
208
+ address: cleanInput,
209
+ detectedCurrency: pattern.currency,
210
+ detectedWalletType: cryptoCurrencyToWalletType(pattern.currency),
211
+ isValid: true,
212
+ );
213
+ }
214
+ }
215
+
216
+ return AddressDetectionResult(
217
+ address: cleanInput,
218
+ detectedCurrency: null,
219
+ isValid: false,
220
+ );
221
+ }
222
+}
223
+
224
+class _DetectionPattern {
225
+ const _DetectionPattern({
226
+ required this.pattern,
227
+ required this.currency,
228
+ });
229
+
230
+ final RegExp pattern;
231
+ final CryptoCurrency currency;
232
+}
lib/di.dart
+15
@@ -282,6 +282,8 @@ import 'package:cake_wallet/core/trade_monitor.dart';
282
import 'package:cake_wallet/core/reset_service.dart';
283
import 'package:cake_wallet/view_model/dev/socket_health_logs_view_model.dart';
284
import 'package:cake_wallet/src/screens/dev/socket_health_logs_page.dart';
285
+import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
286
+import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
287
288
final getIt = GetIt.instance;
289
@@ -816,6 +818,8 @@ Future<void> setup({
818
sendViewModel: getIt.get<SendViewModel>(param1: coinTypeToSpendFrom),
819
authService: getIt.get<AuthService>(),
820
initialPaymentRequest: initialPaymentRequest,
821
+ paymentViewModel: getIt.get<PaymentViewModel>(),
822
+ walletSwitcherViewModel: getIt.get<WalletSwitcherViewModel>(),
823
));
824
825
getIt.registerFactory(
@@ -1126,6 +1130,17 @@ Future<void> setup({
1130
1131
getIt.registerFactory(() => ExchangeTemplatePage(getIt.get<ExchangeViewModel>()));
1132
1133
+ getIt.registerFactory(() => PaymentViewModel(
1134
+ appStore: getIt.get<AppStore>(),
1135
+ walletInfoSource: _walletInfoSource,
1136
+ ));
1137
+
1138
+ getIt.registerFactory(() => WalletSwitcherViewModel(
1139
+ appStore: getIt.get<AppStore>(),
1140
+ walletLoadingService: getIt.get<WalletLoadingService>(),
1141
+ walletInfoSource: _walletInfoSource,
1142
+ ));
1143
+
1144
getIt.registerFactoryParam<WalletService, WalletType, void>((WalletType param1, __) {
1145
switch (param1) {
1146
case WalletType.monero:
lib/src/screens/send/send_page.dart
+8
@@ -31,7 +31,9 @@ import 'package:cake_wallet/utils/payment_request.dart';
31
import 'package:cake_wallet/utils/request_review_handler.dart';
32
import 'package:cake_wallet/utils/responsive_layout_util.dart';
33
import 'package:cake_wallet/utils/show_pop_up.dart';
34
+import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
35
import 'package:cake_wallet/view_model/send/output.dart';
36
+import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
37
import 'package:cw_core/utils/print_verbose.dart';
38
import 'package:cw_core/wallet_type.dart';
39
import 'package:cake_wallet/view_model/send/send_view_model.dart';
@@ -48,10 +50,14 @@ class SendPage extends BasePage {
50
SendPage({
51
required this.sendViewModel,
52
required this.authService,
53
+ required this.paymentViewModel,
54
+ required this.walletSwitcherViewModel,
55
this.initialPaymentRequest,
56
}) : _formKey = GlobalKey<FormState>();
57
58
final SendViewModel sendViewModel;
59
+ final PaymentViewModel paymentViewModel;
60
+ final WalletSwitcherViewModel walletSwitcherViewModel;
61
final AuthService authService;
62
final GlobalKey<FormState> _formKey;
63
final controller = PageController(initialPage: 0);
@@ -179,6 +185,8 @@ class SendPage extends BasePage {
185
key: output.key,
186
output: output,
187
sendViewModel: sendViewModel,
188
+ paymentViewModel: paymentViewModel,
189
+ walletSwitcherViewModel: walletSwitcherViewModel,
190
initialPaymentRequest: initialPaymentRequest,
191
cryptoAmountFocus: cryptoAmountFocus,
192
fiatAmountFocus: fiatAmountFocus,
lib/src/screens/send/widgets/send_card.dart
+172
-26
@@ -1,6 +1,8 @@
1
import 'package:cake_wallet/core/open_crypto_pay/open_cryptopay_service.dart';
2
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
3
import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dart';
4
+import 'package:cake_wallet/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart';
5
+import 'package:cake_wallet/src/widgets/bottom_sheet/wallet_switcher_bottom_sheet.dart';
6
import 'package:cake_wallet/src/widgets/picker.dart';
7
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
8
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
@@ -8,12 +10,16 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
10
import 'package:cake_wallet/themes/core/material_base_theme.dart';
11
import 'package:cake_wallet/utils/payment_request.dart';
12
import 'package:cake_wallet/utils/responsive_layout_util.dart';
13
+import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
14
+import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
15
import 'package:cw_core/crypto_currency.dart';
16
import 'package:cw_core/currency.dart';
17
import 'package:cake_wallet/routes.dart';
18
import 'package:cake_wallet/view_model/send/output.dart';
19
import 'package:cw_core/transaction_priority.dart';
20
import 'package:cw_core/unspent_coin_type.dart';
21
+import 'package:cw_core/utils/print_verbose.dart';
22
+import 'package:cw_core/wallet_info.dart';
23
import 'package:cw_core/wallet_type.dart';
24
import 'package:flutter/material.dart';
25
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -29,6 +35,8 @@ class SendCard extends StatefulWidget {
35
Key? key,
36
required this.output,
37
required this.sendViewModel,
38
+ required this.paymentViewModel,
39
+ required this.walletSwitcherViewModel,
40
required this.currentTheme,
41
this.initialPaymentRequest,
42
this.cryptoAmountFocus,
@@ -37,6 +45,8 @@ class SendCard extends StatefulWidget {
45
46
final Output output;
47
final SendViewModel sendViewModel;
48
+ final PaymentViewModel paymentViewModel;
49
+ final WalletSwitcherViewModel walletSwitcherViewModel;
50
final PaymentRequest? initialPaymentRequest;
51
final FocusNode? cryptoAmountFocus;
52
final FocusNode? fiatAmountFocus;
@@ -44,14 +54,16 @@ class SendCard extends StatefulWidget {
54
55
@override
56
SendCardState createState() => SendCardState(
47
- output: output,
48
- sendViewModel: sendViewModel,
49
- initialPaymentRequest: initialPaymentRequest,
50
- currentTheme: currentTheme
51
- // cryptoAmountFocus: cryptoAmountFocus ?? FocusNode(),
52
- // fiatAmountFocus: fiatAmountFocus ?? FocusNode(),
53
- // cryptoAmountFocus: FocusNode(),
54
- // fiatAmountFocus: FocusNode(),
57
+ output: output,
58
+ sendViewModel: sendViewModel,
59
+ paymentViewModel: paymentViewModel,
60
+ walletSwitcherViewModel: walletSwitcherViewModel,
61
+ initialPaymentRequest: initialPaymentRequest,
62
+ currentTheme: currentTheme,
63
+ // cryptoAmountFocus: cryptoAmountFocus ?? FocusNode(),
64
+ // fiatAmountFocus: fiatAmountFocus ?? FocusNode(),
65
+ // cryptoAmountFocus: FocusNode(),
66
+ // fiatAmountFocus: FocusNode(),
67
);
68
}
69
@@ -59,6 +71,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
71
SendCardState({
72
required this.output,
73
required this.sendViewModel,
74
+ required this.paymentViewModel,
75
+ required this.walletSwitcherViewModel,
76
this.initialPaymentRequest,
77
required this.currentTheme,
78
}) : addressController = TextEditingController(),
@@ -74,6 +88,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
88
final MaterialThemeBase currentTheme;
89
final Output output;
90
final SendViewModel sendViewModel;
91
+ final PaymentViewModel paymentViewModel;
92
+ final WalletSwitcherViewModel walletSwitcherViewModel;
93
final PaymentRequest? initialPaymentRequest;
94
95
final TextEditingController addressController;
@@ -98,23 +114,141 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
114
WidgetsBinding.instance.addPostFrameCallback(
115
(timeStamp) {
116
if (mounted) {
101
- showPopUp<void>(
102
- context: context,
103
- builder: (BuildContext context) {
104
- return AlertWithOneAction(
105
- alertTitle: S.of(context).error,
106
- alertContent: S.of(context).unmatched_currencies,
107
- buttonText: S.of(context).ok,
108
- buttonAction: () => Navigator.of(context).pop(),
109
- );
110
- },
111
- );
117
+ final separator = initialPaymentRequest!.scheme.isNotEmpty ? ":" : "";
118
+ final uri = initialPaymentRequest!.scheme + separator + initialPaymentRequest!.address;
119
+ _handlePaymentFlow(uri, initialPaymentRequest!);
120
}
121
},
122
);
123
}
124
}
125
126
+ @override
127
+ void dispose() {
128
+ addressController.dispose();
129
+ cryptoAmountController.dispose();
130
+ fiatAmountController.dispose();
131
+ noteController.dispose();
132
+ extractedAddressController.dispose();
133
+ addressFocusNode.dispose();
134
+ super.dispose();
135
+ }
136
+
137
+ Future<void> _handlePaymentFlow(String uri, PaymentRequest paymentRequest) async {
138
+ try {
139
+ final result = await paymentViewModel.processAddress(uri);
140
+
141
+ switch (result.type) {
142
+ case PaymentFlowType.singleWallet:
143
+ case PaymentFlowType.multipleWallets:
144
+ case PaymentFlowType.noWallets:
145
+ await _showPaymentConfirmation(
146
+ paymentViewModel,
147
+ walletSwitcherViewModel,
148
+ paymentRequest,
149
+ result,
150
+ );
151
+ break;
152
+ case PaymentFlowType.currentWalletCompatible:
153
+ case PaymentFlowType.error:
154
+ case PaymentFlowType.incompatible:
155
+ _applyPaymentRequest(paymentRequest);
156
+ break;
157
+ }
158
+ } catch (e) {
159
+ printV('Payment flow error: $e');
160
+ _applyPaymentRequest(paymentRequest);
161
+ }
162
+ }
163
+
164
+ Future<void> _showPaymentConfirmation(
165
+ PaymentViewModel paymentViewModel,
166
+ WalletSwitcherViewModel walletSwitcherViewModel,
167
+ PaymentRequest paymentRequest,
168
+ PaymentFlowResult result,
169
+ ) async {
170
+ await showModalBottomSheet<void>(
171
+ context: context,
172
+ isDismissible: true,
173
+ isScrollControlled: true,
174
+ builder: (BuildContext context) {
175
+ return PaymentConfirmationBottomSheet(
176
+ paymentFlowResult: result,
177
+ paymentViewModel: paymentViewModel,
178
+ walletSwitcherViewModel: walletSwitcherViewModel,
179
+ currentTheme: currentTheme,
180
+ paymentRequest: paymentRequest,
181
+ onSelectWallet: () => _handleSelectWallet(
182
+ paymentViewModel,
183
+ walletSwitcherViewModel,
184
+ paymentRequest,
185
+ ),
186
+ onChangeWallet: () => _handleChangeWallet(
187
+ paymentViewModel,
188
+ walletSwitcherViewModel,
189
+ paymentRequest,
190
+ result,
191
+ ),
192
+ onSwap: () => Navigator.of(context).pushNamed(Routes.exchange, arguments: paymentRequest),
193
+ );
194
+ },
195
+ );
196
+ }
197
+
198
+ Future<void> _handleSelectWallet(
199
+ PaymentViewModel paymentViewModel,
200
+ WalletSwitcherViewModel walletSwitcherViewModel,
201
+ PaymentRequest paymentRequest,
202
+ ) async {
203
+ Navigator.of(context).pop();
204
+
205
+ await showModalBottomSheet<WalletInfo>(
206
+ context: context,
207
+ isDismissible: true,
208
+ isScrollControlled: true,
209
+ builder: (BuildContext dialogContext) {
210
+ return WalletSwitcherBottomSheet(
211
+ viewModel: walletSwitcherViewModel,
212
+ currentTheme: currentTheme,
213
+ filterWalletType: paymentViewModel.detectedWalletType,
214
+ );
215
+ },
216
+ );
217
+
218
+ final success = await walletSwitcherViewModel.switchToSelectedWallet();
219
+
220
+ if (success) {
221
+ _applyPaymentRequest(paymentRequest);
222
+ }
223
+ }
224
+
225
+ Future<void> _handleChangeWallet(
226
+ PaymentViewModel paymentViewModel,
227
+ WalletSwitcherViewModel walletSwitcherViewModel,
228
+ PaymentRequest paymentRequest,
229
+ PaymentFlowResult result,
230
+ ) async {
231
+ Navigator.of(context).pop();
232
+
233
+ if (result.wallet != null) {
234
+ walletSwitcherViewModel.selectWallet(result.wallet!);
235
+ final success = await walletSwitcherViewModel.switchToSelectedWallet();
236
+ if (success) {
237
+ _applyPaymentRequest(paymentRequest);
238
+ }
239
+ }
240
+ }
241
+
242
+ /// Apply payment request to current form
243
+ void _applyPaymentRequest(PaymentRequest paymentRequest) {
244
+ if (sendViewModel.usePayjoin) {
245
+ sendViewModel.payjoinUri = paymentRequest.pjUri;
246
+ }
247
+ addressController.text = paymentRequest.address;
248
+ cryptoAmountController.text = paymentRequest.amount;
249
+ noteController.text = paymentRequest.note;
250
+ }
251
+
252
@override
253
Widget build(BuildContext context) {
254
super.build(context);
@@ -185,13 +319,11 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
319
if (OpenCryptoPayService.isOpenCryptoPayQR(uri.toString())) {
320
sendViewModel.createOpenCryptoPayTransaction(uri.toString());
321
} else {
188
- final paymentRequest = PaymentRequest.fromUri(uri);
189
- if (sendViewModel.usePayjoin) {
190
- sendViewModel.payjoinUri = paymentRequest.pjUri;
191
- }
192
- addressController.text = paymentRequest.address;
193
- cryptoAmountController.text = paymentRequest.amount;
194
- noteController.text = paymentRequest.note;
322
+ // Process the payment through the new flow
323
+ await _handlePaymentFlow(
324
+ uri.toString(),
325
+ PaymentRequest.fromUri(uri),
326
+ );
327
}
328
},
329
options: [
@@ -211,6 +343,20 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
343
onPushPasteButton: (context) async {
344
output.resetParsedAddress();
345
await output.fetchParsedAddress(context);
346
+
347
+ final address =
348
+ output.isParsedAddress ? output.extractedAddress : output.address;
349
+
350
+ await _handlePaymentFlow(
351
+ address,
352
+ PaymentRequest(
353
+ address,
354
+ cryptoAmountController.text,
355
+ noteController.text,
356
+ "",
357
+ null,
358
+ ),
359
+ );
360
},
361
onPushAddressBookButton: (context) async {
362
output.resetParsedAddress();
lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart
new
+161
@@ -0,0 +1,161 @@
1
+import 'package:cake_wallet/generated/i18n.dart';
2
+import 'package:cake_wallet/utils/payment_request.dart';
3
+import 'package:flutter/material.dart';
4
+import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
5
+import 'package:cake_wallet/src/widgets/primary_button.dart';
6
+import 'package:cw_core/wallet_type.dart';
7
+import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
8
+import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
9
+import 'package:cake_wallet/themes/core/material_base_theme.dart';
10
+import 'package:flutter_mobx/flutter_mobx.dart';
11
+
12
+class PaymentConfirmationBottomSheet extends BaseBottomSheet {
13
+ PaymentConfirmationBottomSheet({
14
+ Key? key,
15
+ required this.paymentFlowResult,
16
+ required this.paymentViewModel,
17
+ required this.walletSwitcherViewModel,
18
+ required this.currentTheme,
19
+ required this.paymentRequest,
20
+ required this.onSelectWallet,
21
+ required this.onChangeWallet,
22
+ required this.onSwap,
23
+ }) : super(
24
+ titleText: '',
25
+ footerType: FooterType.none,
26
+ maxHeight: 900,
27
+ currentTheme: currentTheme,
28
+ );
29
+
30
+ final PaymentFlowResult paymentFlowResult;
31
+ final PaymentViewModel paymentViewModel;
32
+ final WalletSwitcherViewModel walletSwitcherViewModel;
33
+ final MaterialThemeBase currentTheme;
34
+ final PaymentRequest paymentRequest;
35
+ final VoidCallback onSelectWallet;
36
+ final VoidCallback onChangeWallet;
37
+ final VoidCallback onSwap;
38
+
39
+ @override
40
+ Widget contentWidget(BuildContext context) {
41
+ return _PaymentConfirmationContent(
42
+ paymentFlowResult: paymentFlowResult,
43
+ paymentViewModel: paymentViewModel,
44
+ walletSwitcherViewModel: walletSwitcherViewModel,
45
+ paymentRequest: paymentRequest,
46
+ onSelectWallet: onSelectWallet,
47
+ onChangeWallet: onChangeWallet,
48
+ onSwap: onSwap,
49
+ );
50
+ }
51
+}
52
+
53
+class _PaymentConfirmationContent extends StatelessWidget {
54
+ const _PaymentConfirmationContent({
55
+ required this.paymentFlowResult,
56
+ required this.paymentViewModel,
57
+ required this.walletSwitcherViewModel,
58
+ required this.paymentRequest,
59
+ required this.onSelectWallet,
60
+ required this.onChangeWallet,
61
+ required this.onSwap,
62
+ });
63
+
64
+ final PaymentFlowResult paymentFlowResult;
65
+ final PaymentViewModel paymentViewModel;
66
+ final WalletSwitcherViewModel walletSwitcherViewModel;
67
+ final PaymentRequest paymentRequest;
68
+ final VoidCallback onSelectWallet;
69
+ final VoidCallback onChangeWallet;
70
+ final VoidCallback onSwap;
71
+
72
+ @override
73
+ Widget build(BuildContext context) {
74
+ return Observer(
75
+ builder: (_) {
76
+ final currencyName = walletTypeToString(paymentViewModel.detectedWalletType!);
77
+ final currentWalletName = walletTypeToString(paymentViewModel.currentWalletType);
78
+
79
+ final hasMultipleWallets = paymentFlowResult.type == PaymentFlowType.multipleWallets;
80
+ final noAvailableWallets = paymentFlowResult.type == PaymentFlowType.noWallets;
81
+
82
+ final hasAtLeastOneWallet =
83
+ paymentFlowResult.type == PaymentFlowType.singleWallet || hasMultipleWallets;
84
+
85
+ return Container(
86
+ padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
87
+ child: Column(
88
+ mainAxisSize: MainAxisSize.min,
89
+ crossAxisAlignment: CrossAxisAlignment.center,
90
+ children: [
91
+ Image.asset(
92
+ walletTypeToCryptoCurrency(paymentViewModel.detectedWalletType!).iconPath!,
93
+ width: 118,
94
+ height: 118,
95
+ ),
96
+ const SizedBox(height: 20),
97
+ Text(
98
+ '$currencyName ${S.current.address_detected.toLowerCase()}',
99
+ style: Theme.of(context).textTheme.titleMedium!.copyWith(
100
+ fontSize: 20,
101
+ fontWeight: FontWeight.w900,
102
+ color: Theme.of(context).colorScheme.onSurface,
103
+ letterSpacing: 0.0,
104
+ ),
105
+ textAlign: TextAlign.center,
106
+ ),
107
+ const SizedBox(height: 24),
108
+ Text(
109
+ '''Looks like you scanned a $currencyName address.\n\n'''
110
+ '''Would you like to ${!noAvailableWallets ? 'switch to a $currencyName wallet or' : ''} swap $currentWalletName for $currencyName for this payment?''',
111
+ textAlign: TextAlign.center,
112
+ style: Theme.of(context).textTheme.bodyMedium!.copyWith(
113
+ fontSize: 16,
114
+ fontWeight: FontWeight.w600,
115
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
116
+ letterSpacing: 0.0,
117
+ ),
118
+ ),
119
+ const SizedBox(height: 72),
120
+ if (hasAtLeastOneWallet) ...[
121
+ PrimaryButton(
122
+ onPressed: onSwap,
123
+ text: '${S.current.swap} $currentWalletName',
124
+ color: Theme.of(context).colorScheme.surfaceContainer,
125
+ textColor: Theme.of(context).colorScheme.onSecondaryContainer,
126
+ ),
127
+ const SizedBox(height: 10),
128
+ PrimaryButton(
129
+ onPressed: hasMultipleWallets ? onSelectWallet : onChangeWallet,
130
+ text: S.current.switch_wallet,
131
+ color: Theme.of(context).colorScheme.primary,
132
+ textColor: Theme.of(context).colorScheme.onPrimary,
133
+ ),
134
+ const SizedBox(height: 10),
135
+ ] else ...[
136
+ PrimaryButton(
137
+ onPressed: () => Navigator.of(context).pop(),
138
+ text: S.current.cancel,
139
+ color: Theme.of(context).colorScheme.surfaceContainer,
140
+ textColor: Theme.of(context).colorScheme.onSecondaryContainer,
141
+ ),
142
+ const SizedBox(height: 10),
143
+ PrimaryButton(
144
+ onPressed: onSwap,
145
+ text: '${S.current.swap} $currentWalletName',
146
+ color: hasAtLeastOneWallet
147
+ ? Theme.of(context).colorScheme.surfaceContainer
148
+ : Theme.of(context).colorScheme.primary,
149
+ textColor: hasAtLeastOneWallet
150
+ ? Theme.of(context).colorScheme.onSecondaryContainer
151
+ : Theme.of(context).colorScheme.onPrimary,
152
+ ),
153
+ const SizedBox(height: 32),
154
+ ],
155
+ ],
156
+ ),
157
+ );
158
+ },
159
+ );
160
+ }
161
+}
lib/src/widgets/bottom_sheet/wallet_switcher_bottom_sheet.dart
new
+130
@@ -0,0 +1,130 @@
1
+import 'package:cake_wallet/generated/i18n.dart';
2
+import 'package:flutter/material.dart';
3
+import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
4
+import 'package:cake_wallet/src/widgets/standard_list.dart';
5
+import 'package:cw_core/wallet_info.dart';
6
+import 'package:cw_core/wallet_type.dart';
7
+import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
8
+import 'package:flutter_mobx/flutter_mobx.dart';
9
+import 'package:cake_wallet/themes/core/material_base_theme.dart';
10
+
11
+class WalletSwitcherBottomSheet extends BaseBottomSheet {
12
+ WalletSwitcherBottomSheet({
13
+ Key? key,
14
+ required this.viewModel,
15
+ required this.currentTheme,
16
+ this.filterWalletType,
17
+ this.subtitle,
18
+ this.onWalletSelected,
19
+ }) : super(
20
+ titleText: S.current.select_a_wallet,
21
+ footerType: FooterType.none,
22
+ maxHeight: 900,
23
+ currentTheme: currentTheme,
24
+ );
25
+
26
+ final WalletSwitcherViewModel viewModel;
27
+ final MaterialThemeBase currentTheme;
28
+ final WalletType? filterWalletType;
29
+ final String? subtitle;
30
+ final Function(WalletInfo)? onWalletSelected;
31
+
32
+ @override
33
+ Widget contentWidget(BuildContext context) {
34
+ return _WalletSwitcherContent(
35
+ viewModel: viewModel,
36
+ filterWalletType: filterWalletType,
37
+ subtitle: subtitle,
38
+ onWalletSelected: onWalletSelected,
39
+ );
40
+ }
41
+}
42
+
43
+class _WalletSwitcherContent extends StatelessWidget {
44
+ const _WalletSwitcherContent({
45
+ required this.viewModel,
46
+ this.filterWalletType,
47
+ this.subtitle,
48
+ this.onWalletSelected,
49
+ });
50
+
51
+ final WalletSwitcherViewModel viewModel;
52
+ final WalletType? filterWalletType;
53
+ final String? subtitle;
54
+ final Function(WalletInfo)? onWalletSelected;
55
+
56
+ @override
57
+ Widget build(BuildContext context) {
58
+ return Observer(
59
+ builder: (_) {
60
+ final wallets = viewModel.getWallets(filterWalletType);
61
+
62
+ if (viewModel.isProcessing) {
63
+ return Container(
64
+ height: 200,
65
+ child: Center(
66
+ child: CircularProgressIndicator(
67
+ color: Theme.of(context).colorScheme.primary,
68
+ ),
69
+ ),
70
+ );
71
+ }
72
+
73
+ return Container(
74
+ height: 400,
75
+ child: Column(
76
+ crossAxisAlignment: CrossAxisAlignment.start,
77
+ children: [
78
+ Expanded(
79
+ child: StandardList(
80
+ itemCount: wallets.length,
81
+ itemBuilder: (context, index) {
82
+ final wallet = wallets[index];
83
+
84
+ return InkWell(
85
+ onTap: () {
86
+ viewModel.selectWallet(wallet);
87
+ Navigator.of(context).pop();
88
+ },
89
+ borderRadius: BorderRadius.circular(12),
90
+ child: Container(
91
+ margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
92
+ decoration: BoxDecoration(
93
+ color: Theme.of(context).colorScheme.surfaceContainer,
94
+ borderRadius: BorderRadius.circular(16),
95
+ ),
96
+ height: 60,
97
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
98
+ child: Row(
99
+ children: [
100
+ Image.asset(
101
+ walletTypeToCryptoCurrency(wallet.type).iconPath!,
102
+ width: 32,
103
+ height: 32,
104
+ ),
105
+ const SizedBox(width: 16),
106
+ Text(
107
+ wallet.name,
108
+ style: Theme.of(context).textTheme.bodyMedium!.copyWith(
109
+ fontSize: 18,
110
+ fontWeight: FontWeight.w700,
111
+ color: Theme.of(context).colorScheme.onSurface,
112
+ letterSpacing: 0.0,
113
+ ),
114
+ maxLines: 1,
115
+ overflow: TextOverflow.ellipsis,
116
+ ),
117
+ ],
118
+ ),
119
+ ),
120
+ );
121
+ },
122
+ ),
123
+ ),
124
+ ],
125
+ ),
126
+ );
127
+ },
128
+ );
129
+ }
130
+}
lib/view_model/payment/payment_view_model.dart
new
+124
@@ -0,0 +1,124 @@
1
+import 'dart:async';
2
+
3
+import 'package:cake_wallet/core/universal_address_detector.dart';
4
+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:hive/hive.dart';
9
+import 'package:mobx/mobx.dart';
10
+
11
+part 'payment_view_model.g.dart';
12
+
13
+class PaymentViewModel = PaymentViewModelBase with _$PaymentViewModel;
14
+
15
+abstract class PaymentViewModelBase with Store {
16
+ PaymentViewModelBase({
17
+ required this.appStore,
18
+ required this.walletInfoSource,
19
+ });
20
+
21
+ final AppStore appStore;
22
+ final Box<WalletInfo> walletInfoSource;
23
+
24
+ @observable
25
+ WalletType? detectedWalletType;
26
+
27
+ @observable
28
+ bool isProcessing = false;
29
+
30
+ @computed
31
+ WalletType get currentWalletType => appStore.wallet!.type;
32
+
33
+ /// Main entry point - detect address type and check compatibility
34
+ @action
35
+ Future<PaymentFlowResult> processAddress(String addressData) async {
36
+ try {
37
+ detectedWalletType = null;
38
+ isProcessing = true;
39
+
40
+ // Detect address type
41
+ final detectionResult = UniversalAddressDetector.detectAddress(addressData);
42
+
43
+ if (!detectionResult.isValid || detectionResult.detectedWalletType == null) {
44
+ return PaymentFlowResult.incompatible('Unable to detect address type');
45
+ }
46
+
47
+ detectedWalletType = detectionResult.detectedWalletType!;
48
+
49
+ // Check if current wallet is compatible
50
+ final currentWallet = appStore.wallet;
51
+ if (currentWallet != null && currentWallet.type == detectedWalletType) {
52
+ return PaymentFlowResult.currentWalletCompatible();
53
+ }
54
+
55
+ final compatibleWallets = getWalletsByType(detectedWalletType!);
56
+
57
+ switch (compatibleWallets.length) {
58
+ case 0:
59
+ return PaymentFlowResult.noWallets(detectedWalletType!);
60
+ case 1:
61
+ return PaymentFlowResult.singleWallet(compatibleWallets.first);
62
+ default:
63
+ return PaymentFlowResult.multipleWallets(compatibleWallets);
64
+ }
65
+ } catch (e) {
66
+ printV('PaymentViewModel error: $e');
67
+ return PaymentFlowResult.error('Payment processing failed: $e');
68
+ } finally {
69
+ isProcessing = false;
70
+ }
71
+ }
72
+
73
+ List<WalletInfo> getWalletsByType(WalletType walletType) {
74
+ return walletInfoSource.values.where((wallet) => wallet.type == walletType).toList();
75
+ }
76
+}
77
+
78
+class PaymentFlowResult {
79
+ final PaymentFlowType type;
80
+ final String? message;
81
+ final WalletInfo? wallet;
82
+ final List<WalletInfo> wallets;
83
+ final WalletType? walletType;
84
+
85
+ PaymentFlowResult._({
86
+ required this.type,
87
+ this.message,
88
+ this.wallet,
89
+ this.wallets = const [],
90
+ this.walletType,
91
+ });
92
+
93
+ /// Current wallet is compatible
94
+ factory PaymentFlowResult.currentWalletCompatible() => PaymentFlowResult._(type: PaymentFlowType.currentWalletCompatible);
95
+
96
+ /// Single compatible wallet available
97
+ factory PaymentFlowResult.singleWallet(WalletInfo wallet) =>
98
+ PaymentFlowResult._(type: PaymentFlowType.singleWallet, wallet: wallet);
99
+
100
+ /// Multiple compatible wallets available
101
+ factory PaymentFlowResult.multipleWallets(List<WalletInfo> wallets) =>
102
+ PaymentFlowResult._(type: PaymentFlowType.multipleWallets, wallets: wallets);
103
+
104
+ /// No compatible wallets available
105
+ factory PaymentFlowResult.noWallets(WalletType walletType) =>
106
+ PaymentFlowResult._(type: PaymentFlowType.noWallets, walletType: walletType);
107
+
108
+ /// Error occurred
109
+ factory PaymentFlowResult.error(String message) =>
110
+ PaymentFlowResult._(type: PaymentFlowType.error, message: message);
111
+
112
+ /// Incompatible address
113
+ factory PaymentFlowResult.incompatible(String message) =>
114
+ PaymentFlowResult._(type: PaymentFlowType.incompatible, message: message);
115
+}
116
+
117
+enum PaymentFlowType {
118
+ currentWalletCompatible,
119
+ singleWallet,
120
+ multipleWallets,
121
+ noWallets,
122
+ error,
123
+ incompatible,
124
+}
lib/view_model/send/send_view_model.dart
+2
@@ -37,7 +37,9 @@ import 'package:cake_wallet/utils/payment_request.dart';
37
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
38
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
39
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
40
+import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
41
import 'package:cake_wallet/view_model/send/fees_view_model.dart';
42
+import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
43
import 'package:cake_wallet/view_model/send/output.dart';
44
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
45
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
lib/view_model/wallet_switcher_view_model.dart
new
+63
@@ -0,0 +1,63 @@
1
+import 'dart:async';
2
+
3
+import 'package:cake_wallet/store/app_store.dart';
4
+import 'package:cake_wallet/core/wallet_loading_service.dart';
5
+import 'package:cw_core/wallet_info.dart';
6
+import 'package:cw_core/wallet_type.dart';
7
+import 'package:cw_core/utils/print_verbose.dart';
8
+import 'package:hive/hive.dart';
9
+import 'package:mobx/mobx.dart';
10
+
11
+part 'wallet_switcher_view_model.g.dart';
12
+
13
+class WalletSwitcherViewModel = WalletSwitcherViewModelBase with _$WalletSwitcherViewModel;
14
+
15
+abstract class WalletSwitcherViewModelBase with Store {
16
+ WalletSwitcherViewModelBase({
17
+ required this.appStore,
18
+ required this.walletLoadingService,
19
+ required this.walletInfoSource,
20
+ });
21
+
22
+ final AppStore appStore;
23
+ final WalletLoadingService walletLoadingService;
24
+ final Box<WalletInfo> walletInfoSource;
25
+
26
+ @observable
27
+ WalletInfo? selectedWallet;
28
+
29
+ @observable
30
+ bool isProcessing = false;
31
+
32
+ @action
33
+ List<WalletInfo> getWallets(WalletType? walletType) {
34
+ if (walletType == null) {
35
+ return walletInfoSource.values.toList();
36
+ }
37
+
38
+ return walletInfoSource.values.where((wallet) => wallet.type == walletType).toList();
39
+ }
40
+
41
+ @action
42
+ void selectWallet(WalletInfo walletInfo) => selectedWallet = walletInfo;
43
+
44
+ @action
45
+ Future<bool> switchToSelectedWallet() async {
46
+ if (selectedWallet == null) return false;
47
+
48
+ try {
49
+ isProcessing = true;
50
+
51
+ final wallet = await walletLoadingService.load(selectedWallet!.type, selectedWallet!.name);
52
+
53
+ await appStore.changeCurrentWallet(wallet);
54
+
55
+ return true;
56
+ } catch (e) {
57
+ printV('Failed to switch wallet: $e');
58
+ return false;
59
+ } finally {
60
+ isProcessing = false;
61
+ }
62
+ }
63
+}
res/values/strings_ar.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "البذور",
775
"seedtype_alert_content": "مشاركة البذور مع محافظ أخرى ممكن فقط مع BIP39 Seedtype.",
776
"seedtype_alert_title": "تنبيه البذور",
777
+ "select_a_wallet": "حدد محفظة",
778
"select_backup_file": "حدد ملف النسخ الاحتياطي",
779
"select_buy_provider_notice": "حدد مزود شراء أعلاه. يمكنك تخطي هذه الشاشة عن طريق تعيين مزود شراء الافتراضي في إعدادات التطبيق.",
780
"select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ",
@@ -906,6 +907,7 @@
907
"swap": "تبديل",
908
"sweeping_wallet": "كنس المحفظة",
909
"sweeping_wallet_alert": "لن يستغرق هذا وقتًا طويلاً. لا تترك هذه الشاشة وإلا فقد يتم فقد أموال سويبت",
910
+ "switch_wallet": "تبديل المحفظة",
911
"switchToETHWallet": "ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ Ethereum ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ",
912
"switchToEVMCompatibleWallet": " (Ethereum، Polygon) ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ EVM ﻊﻣ ﺔﻘﻓﺍﻮﺘﻣ ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ",
913
"symbol": "ﺰﻣﺭ",
res/values/strings_bg.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "Семенна тип",
775
"seedtype_alert_content": "Споделянето на семена с други портфейли е възможно само с BIP39 Seedtype.",
776
"seedtype_alert_title": "Сигнал за семена",
777
+ "select_a_wallet": "Изберете портфейл",
778
"select_backup_file": "Избор на резервно копие",
779
"select_buy_provider_notice": "Изберете доставчик на покупка по -горе. Можете да пропуснете този екран, като зададете вашия доставчик по подразбиране по подразбиране в настройките на приложението.",
780
"select_destination": "Моля, изберете дестинация за архивния файл.",
@@ -906,6 +907,7 @@
907
"swap": "Разметка",
908
"sweeping_wallet": "Метещ портфейл",
909
"sweeping_wallet_alert": "Това не трябва да отнема много време. Не оставяйте този екран или пометените средства могат да бъдат загубени.",
910
+ "switch_wallet": "Превключвайте портфейла",
911
"switchToETHWallet": "Моля, преминете към портфейл Ethereum и опитайте отново",
912
"switchToEVMCompatibleWallet": "Моля, превключете към портфейл, съвместим с EVM, и опитайте отново (Ethereum, Polygon)",
913
"symbol": "Символ",
res/values/strings_cs.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "SeedType",
775
"seedtype_alert_content": "Sdílení semen s jinými peněženkami je možné pouze u BIP39 SeedType.",
776
"seedtype_alert_title": "Upozornění seedtype",
777
+ "select_a_wallet": "Vyberte peněženku",
778
"select_backup_file": "Vybrat soubor se zálohou",
779
"select_buy_provider_notice": "Vyberte výše uvedeného poskytovatele nákupu. Tuto obrazovku můžete přeskočit nastavením výchozího poskytovatele nákupu v nastavení aplikace.",
780
"select_destination": "Vyberte cíl pro záložní soubor.",
@@ -906,6 +907,7 @@
907
"swap": "Swap",
908
"sweeping_wallet": "Zametací peněženka",
909
"sweeping_wallet_alert": "To by nemělo trvat dlouho. Nenechávejte tuto obrazovku, jinak mohou být ztraceny prostředky.",
910
+ "switch_wallet": "Přepněte peněženku",
911
"switchToETHWallet": "Přejděte na peněženku Ethereum a zkuste to znovu",
912
"switchToEVMCompatibleWallet": "Přepněte na peněženku kompatibilní s EVM a zkuste to znovu (Ethereum, Polygon)",
913
"symbol": "Symbol",
res/values/strings_de.arb
+2
@@ -775,6 +775,7 @@
775
"seedtype": "Seedtyp",
776
"seedtype_alert_content": "Das Teilen von Seeds mit anderen Wallet ist nur mit bip39 Seedype möglich.",
777
"seedtype_alert_title": "Seedype-Alarm",
778
+ "select_a_wallet": "Wählen Sie eine Brieftasche",
779
"select_backup_file": "Sicherungsdatei auswählen",
780
"select_buy_provider_notice": "Wählen Sie oben einen Anbieter kaufen. Sie können diese Seite überspringen, indem Sie Ihren Standard-Kaufanbieter in den App-Einstellungen festlegen.",
781
"select_destination": "Bitte wählen Sie das Ziel für die Sicherungsdatei aus.",
@@ -907,6 +908,7 @@
908
"swap": "Tauschen",
909
"sweeping_wallet": "Wallet leeren",
910
"sweeping_wallet_alert": "Das sollte nicht lange dauern. VERLASSEN SIE DIESEN BILDSCHIRM NICHT, ANDERNFALLS KÖNNEN DIE GELDER VERLOREN GEHEN",
911
+ "switch_wallet": "Brieftasche schalten",
912
"switchToETHWallet": "Bitte wechseln Sie zu einem Ethereum-Wallet und versuchen Sie es erneut",
913
"switchToEVMCompatibleWallet": "Bitte wechseln Sie zu einem EVM-kompatiblen Wallet und versuchen Sie es erneut (Ethereum, Polygon)",
914
"symbol": "Symbol",
res/values/strings_en.arb
+2
@@ -775,6 +775,7 @@
775
"seedtype": "Seedtype",
776
"seedtype_alert_content": "Sharing seeds with other wallets is only possible with BIP39 SeedType.",
777
"seedtype_alert_title": "SeedType Alert",
778
+ "select_a_wallet": "Select a wallet",
779
"select_backup_file": "Select backup file",
780
"select_buy_provider_notice": "Select a buy provider above. You can skip this screen by setting your default buy provider in app settings.",
781
"select_destination": "Please select destination for the backup file.",
@@ -907,6 +908,7 @@
908
"swap": "Swap",
909
"sweeping_wallet": "Sweeping wallet",
910
"sweeping_wallet_alert": "This shouldn’t take long. DO NOT LEAVE THIS SCREEN OR THE SWEPT FUNDS MAY BE LOST.",
911
+ "switch_wallet": "Switch Wallet",
912
"switchToETHWallet": "Please switch to an Ethereum wallet and try again",
913
"switchToEVMCompatibleWallet": "Please switch to an EVM compatible wallet and try again (Ethereum, Polygon)",
914
"symbol": "Symbol",
res/values/strings_es.arb
+2
@@ -775,6 +775,7 @@
775
"seedtype": "Tipos de semillas",
776
"seedtype_alert_content": "Compartir semillas con otras billeteras solo es posible con semillas bip39 - un tipo específico de semilla.",
777
"seedtype_alert_title": "Alerta de tipo de semillas",
778
+ "select_a_wallet": "Seleccione una billetera",
779
"select_backup_file": "Seleccionar archivo de respaldo",
780
"select_buy_provider_notice": "Selecciona un proveedor de compra arriba. Puede omitir esta pantalla configurando su proveedor de compra predeterminado en la configuración de la aplicación.",
781
"select_destination": "Selecciona el destino del archivo de copia de seguridad.",
@@ -907,6 +908,7 @@
908
"swap": "Cambio",
909
"sweeping_wallet": "Barrer billetera (gastar todos los fondos disponibles)",
910
"sweeping_wallet_alert": "Esto no debería llevar mucho tiempo. NO DEJES ESTA PANTALLA O SE PUEDEN PERDER LOS FONDOS BARRIDOS",
911
+ "switch_wallet": "Billetera",
912
"switchToETHWallet": "Cambia a una billetera Ethereum e inténtelo nuevamente.",
913
"switchToEVMCompatibleWallet": "Cambia a una billetera compatible con EVM e inténtelo nuevamente (Ethereum, Polygon)",
914
"symbol": "Símbolo",
res/values/strings_fr.arb
+3
-1
@@ -71,7 +71,7 @@
71
"auto_generate_subaddresses": "Générer automatiquement des sous-adresses",
72
"automatic": "Automatique",
73
"available_balance": "Solde disponible",
74
- "available_balance_description": "Le "Solde disponible" ou "Solde confirmé" sont les montants que vous pouvez dépenser immédiatement. Si des fonds apparaissent dans le solde inférieur mais pas dans le solde supérieur, vous devez attendre quelques minutes pour que les fonds entrants obtiennent plus de confirmations réseau. Une fois qu’ils ont reçu plus de confirmations, ils seront utilisables.",
74
+ "available_balance_description": "Le Solde disponible ou Solde confirmé sont les montants que vous pouvez dépenser immédiatement. Si des fonds apparaissent dans le solde inférieur mais pas dans le solde supérieur, vous devez attendre quelques minutes pour que les fonds entrants obtiennent plus de confirmations réseau. Une fois qu’ils ont reçu plus de confirmations, ils seront utilisables.",
75
"avg_savings": "Économies moy.",
76
"awaitDAppProcessing": "Veuillez attendre que l'application décentralisée (dApp) termine le traitement.",
77
"awaiting_payment_confirmation": "En attente de confirmation de paiement",
@@ -774,6 +774,7 @@
774
"seedtype": "Type de graine",
775
"seedtype_alert_content": "Le partage de graines avec d'autres portefeuilles n'est possible qu'avec le type de graine BIP39.",
776
"seedtype_alert_title": "Alerte Type de Graine",
777
+ "select_a_wallet": "Sélectionnez un portefeuille",
778
"select_backup_file": "Sélectionnez le fichier de sauvegarde",
779
"select_buy_provider_notice": "Sélectionnez un fournisseur d'achat ci-dessus. Vous pouvez ignorer cet écran en définissant votre fournisseur d'achat par défaut dans les paramètres de l'application.",
780
"select_destination": "Veuillez sélectionner la destination du fichier de sauvegarde.",
@@ -906,6 +907,7 @@
907
"swap": "Échanger",
908
"sweeping_wallet": "Portefeuille (wallet) de consolidation",
909
"sweeping_wallet_alert": "Cela ne devrait pas prendre longtemps. NE QUITTEZ PAS CET ÉCRAN OU LES FONDS TRANSFÉRÉS POURRAIENT ÊTRE PERDUS.",
910
+ "switch_wallet": "Portefeuille de commutation",
911
"switchToETHWallet": "Veuillez passer à un portefeuille (wallet) Ethereum et réessayer",
912
"switchToEVMCompatibleWallet": "Veuillez passer à un portefeuille compatible EVM et réessayer (Ethereum, Polygon)",
913
"symbol": "Symbole",
res/values/strings_ha.arb
+2
@@ -776,6 +776,7 @@
776
"seedtype": "Seedtype",
777
"seedtype_alert_content": "Raba tsaba tare da sauran wallets yana yiwuwa ne kawai tare da Bip39 seedtype.",
778
"seedtype_alert_title": "Seedtype farke",
779
+ "select_a_wallet": "Zaɓi Wallet",
780
"select_backup_file": "Zaɓi fayil ɗin madadin",
781
"select_buy_provider_notice": "Zaɓi mai ba da kyauta a sama. Zaka iya tsallake wannan allon ta hanyar saita mai ba da isasshen busasshen mai ba da isasshen busasshiyar saiti.",
782
"select_destination": "Da fatan za a zaɓi wurin da za a yi wa madadin fayil ɗin.",
@@ -908,6 +909,7 @@
909
"swap": "Musya",
910
"sweeping_wallet": "Kashi na kasa",
911
"sweeping_wallet_alert": "Wannan ba zai samu lokacin mai tsaski. KADA KA SAMU KUNGIYARAN KUHON, ZAMAN DADIN BANKUNCI ZAI HAŘA",
912
+ "switch_wallet": "Zanen rubutu",
913
"switchToETHWallet": "Da fatan za a canza zuwa walat ɗin Ethereum kuma a sake gwadawa",
914
"switchToEVMCompatibleWallet": "Da fatan za a canza zuwa walat ɗin EVM mai jituwa kuma a sake gwadawa (Ethereum, Polygon)",
915
"symbol": "Alama",
res/values/strings_hi.arb
+2
@@ -776,6 +776,7 @@
776
"seedtype": "बीज",
777
"seedtype_alert_content": "अन्य पर्स के साथ बीज साझा करना केवल BIP39 सीडटाइप के साथ संभव है।",
778
"seedtype_alert_title": "बीजगणित अलर्ट",
779
+ "select_a_wallet": "एक वॉलेट का चयन करें",
780
"select_backup_file": "बैकअप फ़ाइल का चयन करें",
781
"select_buy_provider_notice": "ऊपर एक खरीद प्रदाता का चयन करें। आप इस स्क्रीन को ऐप सेटिंग्स में अपना डिफ़ॉल्ट बाय प्रदाता सेट करके छोड़ सकते हैं।",
782
"select_destination": "कृपया बैकअप फ़ाइल के लिए गंतव्य का चयन करें।",
@@ -908,6 +909,7 @@
909
"swap": "बदलना",
910
"sweeping_wallet": "स्वीपिंग वॉलेट",
911
"sweeping_wallet_alert": "इसमें अधिक समय नहीं लगना चाहिए। इस स्क्रीन को न छोड़ें या स्वैप्ट फंड खो सकते हैं",
912
+ "switch_wallet": "स्विच वॉलेट",
913
"switchToETHWallet": "कृपया एथेरियम वॉलेट पर स्विच करें और पुनः प्रयास करें",
914
"switchToEVMCompatibleWallet": "कृपया ईवीएम संगत वॉलेट पर स्विच करें और पुनः प्रयास करें (एथेरियम, पॉलीगॉन)",
915
"symbol": "प्रतीक",
res/values/strings_hr.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "Sjemenska vrsta",
775
"seedtype_alert_content": "Dijeljenje sjemena s drugim novčanicima moguće je samo s BIP39 sjemenom.",
776
"seedtype_alert_title": "Upozorenje o sjemenu",
777
+ "select_a_wallet": "Odaberite novčanik",
778
"select_backup_file": "Odaberite datoteku sigurnosne kopije",
779
"select_buy_provider_notice": "Odaberite gornji davatelj kupnje. Ovaj zaslon možete preskočiti postavljanjem zadanog davatelja usluga kupnje u postavkama aplikacija.",
780
"select_destination": "Odaberite odredište za datoteku sigurnosne kopije.",
@@ -906,6 +907,7 @@
907
"swap": "Mijenjati",
908
"sweeping_wallet": "Čisti novčanik",
909
"sweeping_wallet_alert": "Ovo ne bi trebalo dugo trajati. NE NAPUŠTAJTE OVAJ ZASLON INAČE SE POBREŠENA SREDSTVA MOGU IZGUBITI",
910
+ "switch_wallet": "PRIKLJUČAK WALINE",
911
"switchToETHWallet": "Prijeđite na Ethereum novčanik i pokušajte ponovno",
912
"switchToEVMCompatibleWallet": "Prijeđite na novčanik kompatibilan s EVM-om i pokušajte ponovno (Ethereum, Polygon)",
913
"symbol": "Simbol",
res/values/strings_hy.arb
+2
@@ -773,6 +773,7 @@
773
"seedtype": "Սերմի տեսակ",
774
"seedtype_alert_content": "Այլ դրամապանակներով սերմերի փոխանակումը հնարավոր է միայն BIP39 SEEDTYPE- ով:",
775
"seedtype_alert_title": "SEEDTYPE ALERT",
776
+ "select_a_wallet": "Ընտրեք դրամապանակ",
777
"select_backup_file": "Ընտրել կրկնօրինակ ֆայլ",
778
"select_buy_provider_notice": "Ընտրեք գնման մատակարարը վերևում։ Դուք կարող եք բաց թողնել այս էկրանը ձեր լռելայն գնման մատակարարը հավելվածի կարգավորումներում սահմանելով",
779
"select_destination": "Խնդրում ենք ընտրել կրկնօրինակ ֆայլի նպատակակետը",
@@ -904,6 +905,7 @@
905
"swap": "Փոխանակել",
906
"sweeping_wallet": "Դրամապանակը մաքրվում է",
907
"sweeping_wallet_alert": "Սա չի տևի երկար։ Խնդրում ենք չլքել այս էկրանը կամ մաքրված միջոցները կկորչեն։",
908
+ "switch_wallet": "Անջատեք դրամապանակը",
909
"switchToETHWallet": "Խնդրում ենք անցնել Ethereum դրամապանակ և փորձել կրկին",
910
"switchToEVMCompatibleWallet": "Խնդրում ենք անցնել EVM համատեղելի դրամապանակ և փորձել կրկին (Ethereum, Polygon)",
911
"symbol": "Նշան",
res/values/strings_id.arb
+2
@@ -777,6 +777,7 @@
777
"seedtype": "Seedtype",
778
"seedtype_alert_content": "Berbagi biji dengan dompet lain hanya dimungkinkan dengan BIP39 seedtype.",
779
"seedtype_alert_title": "Peringatan seedtype",
780
+ "select_a_wallet": "Pilih dompet",
781
"select_backup_file": "Pilih file cadangan",
782
"select_buy_provider_notice": "Pilih penyedia beli di atas. Anda dapat melewatkan layar ini dengan mengatur penyedia pembelian default Anda di pengaturan aplikasi.",
783
"select_destination": "Silakan pilih tujuan untuk file cadangan.",
@@ -909,6 +910,7 @@
910
"swap": "Menukar",
911
"sweeping_wallet": "Dompet menyapu",
912
"sweeping_wallet_alert": "Ini seharusnya tidak memakan waktu lama. Jangan tinggalkan layar ini atau dana swept mungkin hilang.",
913
+ "switch_wallet": "Switch Wallet",
914
"switchToETHWallet": "Silakan beralih ke dompet Ethereum dan coba lagi",
915
"switchToEVMCompatibleWallet": "Silakan beralih ke dompet yang kompatibel dengan EVM dan coba lagi (Ethereum, Polygon)",
916
"symbol": "Simbol",
res/values/strings_it.arb
+2
@@ -775,6 +775,7 @@
775
"seedtype": "Seedtype",
776
"seedtype_alert_content": "La condivisione di semi con altri portafogli è possibile solo con Bip39 SeedType.",
777
"seedtype_alert_title": "Avviso seedType",
778
+ "select_a_wallet": "Seleziona un portafoglio",
779
"select_backup_file": "Seleziona file di backup",
780
"select_buy_provider_notice": "Seleziona un provider di acquisto sopra. È possibile saltare questa schermata impostando il provider di acquisto predefinito nelle impostazioni dell'app.",
781
"select_destination": "Seleziona la destinazione per il file di backup.",
@@ -907,6 +908,7 @@
908
"swap": "Scambio",
909
"sweeping_wallet": "Svuota portafoglio",
910
"sweeping_wallet_alert": "Questo non dovrebbe richiedere molto tempo. NON LASCIARE QUESTA SCHERMATA O I FONDI DA TRASFERIRE POTREBBERO ANDARE PERSI",
911
+ "switch_wallet": "Switch Porthet",
912
"switchToETHWallet": "Passa a un portafoglio Ethereum e riprova",
913
"switchToEVMCompatibleWallet": "Passa a un portafoglio compatibile con EVM e riprova (Ethereum, Polygon)",
914
"symbol": "Simbolo",
res/values/strings_ja.arb
+2
@@ -775,6 +775,7 @@
775
"seedtype": "SeedType",
776
"seedtype_alert_content": "他の財布と種子を共有することは、BIP39 SeedTypeでのみ可能です。",
777
"seedtype_alert_title": "SeedTypeアラート",
778
+ "select_a_wallet": "ウォレットを選択します",
779
"select_backup_file": "バックアップファイルを選択",
780
"select_buy_provider_notice": "上記の購入プロバイダーを選択してください。デフォルトの購入プロバイダーをアプリ設定で設定して、この画面をスキップできます。",
781
"select_destination": "バックアップファイルの保存先を選択してください。",
@@ -907,6 +908,7 @@
908
"swap": "スワップ",
909
"sweeping_wallet": "スイープウォレット",
910
"sweeping_wallet_alert": "これには時間がかかりません。この画面から離れないでください。そうしないと、スイープ ファンドが失われる可能性があります",
911
+ "switch_wallet": "スイッチウォレット",
912
"switchToETHWallet": "イーサリアムウォレットに切り替えてもう一度お試しください",
913
"switchToEVMCompatibleWallet": "EVM 互換のウォレットに切り替えて再試行してください (イーサリアム、ポリゴン)",
914
"symbol": "シンボル",
res/values/strings_ko.arb
+2
@@ -775,6 +775,7 @@
775
"seedtype": "시드 유형",
776
"seedtype_alert_content": "다른 지갑과의 시드 공유는 BIP39 시드 유형에서만 가능합니다.",
777
"seedtype_alert_title": "시드 유형 알림",
778
+ "select_a_wallet": "지갑을 선택하십시오",
779
"select_backup_file": "백업 파일 선택",
780
"select_buy_provider_notice": "위에서 구매 제공 업체를 선택하세요. 앱 설정에서 기본 구매 제공 업체를 설정하여 이 화면을 건너뛸 수 있습니다.",
781
"select_destination": "백업 파일의 대상을 선택하세요.",
@@ -907,6 +908,7 @@
908
"swap": "스왑",
909
"sweeping_wallet": "지갑 스윕 중",
910
"sweeping_wallet_alert": "오래 걸리지 않습니다. 이 화면을 떠나거나 스윕된 자금을 잃을 수 있습니다.",
911
+ "switch_wallet": "스위치 지갑",
912
"switchToETHWallet": "이더리움 지갑으로 전환하고 다시 시도하세요",
913
"switchToEVMCompatibleWallet": "EVM 호환 지갑(이더리움, 폴리곤)으로 전환하고 다시 시도하세요",
914
"symbol": "심볼",
res/values/strings_my.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "မျိုးပွားခြင်း",
775
"seedtype_alert_content": "အခြားပိုက်ဆံအိတ်များနှင့်မျိုးစေ့များကိုမျှဝေခြင်းသည် BIP39 sebyspe ဖြင့်သာဖြစ်သည်။",
776
"seedtype_alert_title": "ပျိုးပင်သတိပေးချက်",
777
+ "select_a_wallet": "ပိုက်ဆံအိတ်တစ်ခုရွေးပါ",
778
"select_backup_file": "အရန်ဖိုင်ကို ရွေးပါ။",
779
"select_buy_provider_notice": "အပေါ်ကဝယ်သူတစ် ဦး ကိုရွေးချယ်ပါ။ သင်၏ default 0 ယ်သူအား app settings တွင် setting လုပ်ခြင်းဖြင့်ဤ screen ကိုကျော်သွားနိုင်သည်။",
780
"select_destination": "အရန်ဖိုင်အတွက် ဦးတည်ရာကို ရွေးပါ။",
@@ -906,6 +907,7 @@
907
"swap": "လဲလှယ်",
908
"sweeping_wallet": "ိုက်ဆံအိတ် တံမြက်လှည်း",
909
"sweeping_wallet_alert": "ဒါက ကြာကြာမခံသင့်ပါဘူး။ ဤစခရင်ကို ချန်မထားပါနှင့် သို့မဟုတ် ထုတ်ယူထားသော ရန်ပုံငွေများ ဆုံးရှုံးနိုင်သည်",
910
+ "switch_wallet": "Wallet switch",
911
"switchToETHWallet": "ကျေးဇူးပြု၍ Ethereum ပိုက်ဆံအိတ်သို့ ပြောင်းပြီး ထပ်စမ်းကြည့်ပါ။",
912
"switchToEVMCompatibleWallet": "ကျေးဇူးပြု၍ EVM တွဲဖက်သုံးနိုင်သော ပိုက်ဆံအိတ်သို့ ပြောင်းပြီး ထပ်စမ်းကြည့်ပါ (Ethereum၊ Polygon)",
913
"symbol": "သင်္ကေတ",
res/values/strings_nl.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "Zaadtype",
775
"seedtype_alert_content": "Het delen van zaden met andere portefeuilles is alleen mogelijk met BIP39 SeedType.",
776
"seedtype_alert_title": "Zaadtype alert",
777
+ "select_a_wallet": "Selecteer een portemonnee",
778
"select_backup_file": "Selecteer een back-upbestand",
779
"select_buy_provider_notice": "Selecteer hierboven een koopprovider. U kunt dit scherm overslaan door uw standaard kopenprovider in te stellen in app -instellingen.",
780
"select_destination": "Selecteer de bestemming voor het back-upbestand.",
@@ -906,6 +907,7 @@
907
"swap": "Ruil",
908
"sweeping_wallet": "Vegende portemonnee",
909
"sweeping_wallet_alert": "Dit duurt niet lang. VERLAAT DIT SCHERM NIET, ANDERS KAN HET SWEPT-GELD VERLOREN WORDEN",
910
+ "switch_wallet": "Schakel portemonnee",
911
"switchToETHWallet": "Schakel over naar een Ethereum-portemonnee en probeer het opnieuw",
912
"switchToEVMCompatibleWallet": "Schakel over naar een EVM-compatibele portemonnee en probeer het opnieuw (Ethereum, Polygon)",
913
"symbol": "Symbool",
res/values/strings_pl.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "Sedtype",
775
"seedtype_alert_content": "Dzielenie się nasionami z innymi portfelami jest możliwe tylko z BIP39 sededType.",
776
"seedtype_alert_title": "Ustanowienie typu sedype",
777
+ "select_a_wallet": "Wybierz portfel",
778
"select_backup_file": "Wybierz plik kopii zapasowej",
779
"select_buy_provider_notice": "Wybierz powyższe dostawcę zakupu. Możesz pominąć ten ekran, ustawiając domyślnego dostawcę zakupu w ustawieniach aplikacji.",
780
"select_destination": "Wybierz miejsce docelowe dla pliku kopii zapasowej.",
@@ -906,6 +907,7 @@
907
"swap": "Wymiana",
908
"sweeping_wallet": "Zamiatanie portfela",
909
"sweeping_wallet_alert": "To nie powinno zająć dużo czasu. NIE WYCHODŹ Z TEGO EKRANU, W PRZECIWNYM WYPADKU MOŻE ZOSTAĆ UTRACONA ŚRODKI",
910
+ "switch_wallet": "Portfel przełącz",
911
"switchToETHWallet": "Przejdź na portfel Ethereum i spróbuj ponownie",
912
"switchToEVMCompatibleWallet": "Przejdź na portfel zgodny z EVM i spróbuj ponownie (Ethereum, Polygon)",
913
"symbol": "Symbol",
res/values/strings_pt.arb
+2
@@ -776,6 +776,7 @@
776
"seedtype": "SeedType",
777
"seedtype_alert_content": "Compartilhar sementes com outras carteiras só é possível com o BIP39 SeedType.",
778
"seedtype_alert_title": "Alerta de SeedType",
779
+ "select_a_wallet": "Selecione uma carteira",
780
"select_backup_file": "Selecione o arquivo de backup",
781
"select_buy_provider_notice": "Selecione um provedor de compra acima. Você pode pular esta tela definindo seu provedor de compra padrão nas configurações de aplicativos.",
782
"select_destination": "Selecione o destino para o arquivo de backup.",
@@ -908,6 +909,7 @@
909
"swap": "Trocar",
910
"sweeping_wallet": "Carteira varrendo",
911
"sweeping_wallet_alert": "To nie powinno zająć dużo czasu. NIE WYCHODŹ Z TEGO EKRANU, W PRZECIWNYM WYPADKU MOŻE ZOSTAĆ UTRACONA ŚRODKI",
912
+ "switch_wallet": "CALLETA DE CURSÃO",
913
"switchToETHWallet": "Mude para uma carteira Ethereum e tente novamente",
914
"switchToEVMCompatibleWallet": "Mude para uma carteira compatível com EVM e tente novamente (Ethereum, Polygon)",
915
"symbol": "Símbolo",
res/values/strings_ru.arb
+2
@@ -775,6 +775,7 @@
775
"seedtype": "SEEDTYPE",
776
"seedtype_alert_content": "Обмен семенами с другими кошельками возможно только с BIP39 SeedType.",
777
"seedtype_alert_title": "SEEDTYPE ALERT",
778
+ "select_a_wallet": "Выберите кошелек",
779
"select_backup_file": "Выберите файл резервной копии",
780
"select_buy_provider_notice": "Выберите поставщика покупки выше. Вы можете пропустить этот экран, установив поставщика покупки по умолчанию в настройках приложения.",
781
"select_destination": "Пожалуйста, выберите место для файла резервной копии.",
@@ -907,6 +908,7 @@
908
"swap": "Менять",
909
"sweeping_wallet": "Подметание кошелька",
910
"sweeping_wallet_alert": "Это не должно занять много времени. НЕ ПОКИДАЙТЕ ЭТОТ ЭКРАН, ИНАЧЕ ВЫЧИСЛЕННЫЕ СРЕДСТВА МОГУТ БЫТЬ ПОТЕРЯНЫ",
911
+ "switch_wallet": "Переключить кошелек",
912
"switchToETHWallet": "Пожалуйста, переключитесь на кошелек Ethereum и повторите попытку.",
913
"switchToEVMCompatibleWallet": "Пожалуйста, переключитесь на кошелек, совместимый с EVM, и повторите попытку (Ethereum, Polygon).",
914
"symbol": "Символ",
res/values/strings_th.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "เมล็ดพันธุ์",
775
"seedtype_alert_content": "การแบ่งปันเมล็ดกับกระเป๋าเงินอื่น ๆ เป็นไปได้เฉพาะกับ bip39 seedtype",
776
"seedtype_alert_title": "การแจ้งเตือน seedtype",
777
+ "select_a_wallet": "เลือกกระเป๋าเงิน",
778
"select_backup_file": "เลือกไฟล์สำรอง",
779
"select_buy_provider_notice": "เลือกผู้ให้บริการซื้อด้านบน คุณสามารถข้ามหน้าจอนี้ได้โดยการตั้งค่าผู้ให้บริการซื้อเริ่มต้นในการตั้งค่าแอป",
780
"select_destination": "โปรดเลือกปลายทางสำหรับไฟล์สำรอง",
@@ -906,6 +907,7 @@
907
"swap": "แลกเปลี่ยน",
908
"sweeping_wallet": "กวาดกระเป๋าสตางค์",
909
"sweeping_wallet_alert": "การดำเนินการนี้ใช้เวลาไม่นาน อย่าออกจากหน้าจอนี้ มิฉะนั้นเงินที่กวาดไปอาจสูญหาย",
910
+ "switch_wallet": "สลับกระเป๋าเงิน",
911
"switchToETHWallet": "โปรดเปลี่ยนไปใช้กระเป๋าเงิน Ethereum แล้วลองอีกครั้ง",
912
"switchToEVMCompatibleWallet": "โปรดเปลี่ยนไปใช้กระเป๋าเงินที่รองรับ EVM แล้วลองอีกครั้ง (Ethereum, Polygon)",
913
"symbol": "เครื่องหมาย",
res/values/strings_tl.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "Seed type",
775
"seedtype_alert_content": "Ang pagbabahagi ng mga buto sa iba pang mga pitaka ay posible lamang sa bip39 seedtype.",
776
"seedtype_alert_title": "Alerto ng Seedtype",
777
+ "select_a_wallet": "Pumili ng isang pitaka",
778
"select_backup_file": "Piliin ang backup na file",
779
"select_buy_provider_notice": "Pumili ng provider ng pagbili sa itaas. Maaari mong laktawan ang screen na ito sa pamamagitan ng pagtatakda ng iyong default na provider ng pagbili sa mga setting ng app.",
780
"select_destination": "Mangyaring piliin ang patutunguhan para sa backup na file.",
@@ -906,6 +907,7 @@
907
"swap": "Palitan",
908
"sweeping_wallet": "Sweeping wallet",
909
"sweeping_wallet_alert": "Hindi ito dapat magtagal. HUWAG iwanan ang screen na ito o maaaring mawala ang mga pondo.",
910
+ "switch_wallet": "Lumipat ng pitaka",
911
"switchToETHWallet": "Mangyaring lumipat sa isang Ethereum wallet at subukang muli",
912
"switchToEVMCompatibleWallet": "Mangyaring lumipat sa isang EVM compatible na wallet at subukang muli (Ethereum, Polygon)",
913
"symbol": "Simbolo",
res/values/strings_tr.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "Tohum",
775
"seedtype_alert_content": "Tohumları diğer cüzdanlarla paylaşmak sadece BIP39 tohumu ile mümkündür.",
776
"seedtype_alert_title": "SeedType uyarısı",
777
+ "select_a_wallet": "Bir Cüzdan Seçin",
778
"select_backup_file": "Yedek dosyası seç",
779
"select_buy_provider_notice": "Yukarıda bir satın alma sağlayıcısı seçin. App ayarlarında varsayılan satın alma sağlayıcınızı ayarlayarak bu ekranı atlayabilirsiniz.",
780
"select_destination": "Lütfen yedekleme dosyası için hedef seçin.",
@@ -906,6 +907,7 @@
907
"swap": "Takas",
908
"sweeping_wallet": "Süpürme cüzdanı",
909
"sweeping_wallet_alert": "Bu uzun sürmemeli. BU EKRANDAN BIRAKMAYIN YOKSA SÜPÜRÜLEN FONLAR KAYBOLABİLİR",
910
+ "switch_wallet": "Cüzdan",
911
"switchToETHWallet": "Lütfen bir Ethereum cüzdanına geçin ve tekrar deneyin",
912
"switchToEVMCompatibleWallet": "Lütfen EVM uyumlu bir cüzdana geçin ve tekrar deneyin (Ethereum, Polygon)",
913
"symbol": "Sembol",
res/values/strings_uk.arb
+2
@@ -775,6 +775,7 @@
775
"seedtype": "Насіннєвий тип",
776
"seedtype_alert_content": "Спільний доступ до інших гаманців можливе лише за допомогою BIP39 Seedtype.",
777
"seedtype_alert_title": "Попередження насінника",
778
+ "select_a_wallet": "Виберіть гаманець",
779
"select_backup_file": "Виберіть файл резервної копії",
780
"select_buy_provider_notice": "Виберіть постачальника купівлі вище. Ви можете пропустити цей екран, встановивши свого постачальника купівлі за замовчуванням у налаштуваннях додатків.",
781
"select_destination": "Виберіть місце призначення для файлу резервної копії.",
@@ -907,6 +908,7 @@
908
"swap": "Обміняти",
909
"sweeping_wallet": "Підмітаня гаманця",
910
"sweeping_wallet_alert": "Це не повинно зайняти багато часу. НЕ ЗАЛИШАЙТЕ ЦЬОГО ЕКРАНУ, АБО КОШТИ МОЖУТЬ БУТИ ВТРАЧЕНІ",
911
+ "switch_wallet": "Перемикач гаманця",
912
"switchToETHWallet": "Перейдіть на гаманець Ethereum і повторіть спробу",
913
"switchToEVMCompatibleWallet": "Перейдіть на гаманець, сумісний з EVM, і повторіть спробу (Ethereum, Polygon)",
914
"symbol": "символ",
res/values/strings_ur.arb
+2
@@ -776,6 +776,7 @@
776
"seedtype": "سیڈ ٹائپ",
777
"seedtype_alert_content": "دوسرے بٹوے کے ساتھ بیجوں کا اشتراک صرف BIP39 بیج ٹائپ کے ساتھ ہی ممکن ہے۔",
778
"seedtype_alert_title": "سیڈ ٹائپ الرٹ",
779
+ "select_a_wallet": "ایک پرس منتخب کریں",
780
"select_backup_file": "بیک اپ فائل کو منتخب کریں۔",
781
"select_buy_provider_notice": "اوپر خریدنے والا خریدنے والا منتخب کریں۔ آپ ایپ کی ترتیبات میں اپنے پہلے سے طے شدہ خریدنے والے کو ترتیب دے کر اس اسکرین کو چھوڑ سکتے ہیں۔",
782
"select_destination": "۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﻝﺰﻨﻣ ﮯﯿﻟ ﮯﮐ ﻞﺋﺎﻓ ﭖﺍ ﮏﯿﺑ ﻡﺮﮐ ﮦﺍﺮﺑ",
@@ -908,6 +909,7 @@
909
"swap": "تبادلہ",
910
"sweeping_wallet": "جھاڑو دینے والا پرس",
911
"sweeping_wallet_alert": "اس میں زیادہ وقت نہیں لینا چاہئے۔ اس اسکرین کو مت چھوڑیں یا بہہ جانے والے فنڈز ضائع ہوسکتے ہیں۔",
912
+ "switch_wallet": "سوئچ پرس",
913
"switchToETHWallet": "۔ﮟﯾﺮﮐ ﺶﺷﻮﮐ ﮦﺭﺎﺑﻭﺩ ﺭﻭﺍ ﮟﯾﺮﮐ ﭻﺋﻮﺳ ﺮﭘ ﭧﯿﻟﺍﻭ Ethereum ﻡﺮﮐ ﮦﺍﺮﺑ",
914
"switchToEVMCompatibleWallet": "(Ethereum, Polygon) ﮟﯾﺮﮐ ﺶﺷﻮﮐ ﮦﺭﺎﺑﻭﺩ ﺭﻭﺍ ﮟﯾﺮﮐ ﭻﺋﻮﺳ ﺮﭘ ﭧﯿﻟﺍﻭ ﮯﻟﺍﻭ ﮯﻨﮭﮐﺭ ﺖﻘﺑﺎﻄﻣ ",
915
"symbol": "ﺖﻣﻼﻋ",
res/values/strings_vi.arb
+2
@@ -772,6 +772,7 @@
772
"seedtype": "Loại hạt giống",
773
"seedtype_alert_content": "Chia sẻ hạt giống với ví khác chỉ có thể với BIP39 SeedType.",
774
"seedtype_alert_title": "Cảnh báo hạt giống",
775
+ "select_a_wallet": "Chọn một ví",
776
"select_backup_file": "Chọn tệp sao lưu",
777
"select_buy_provider_notice": "Chọn nhà cung cấp mua ở 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 mua mặc định trong cài đặt ứng dụng.",
778
"select_destination": "Vui lòng chọn đích cho tệp sao lưu.",
@@ -903,6 +904,7 @@
904
"swap": "Tráo đổi",
905
"sweeping_wallet": "Quét ví",
906
"sweeping_wallet_alert": "Việc này không nên mất nhiều thời gian. KHÔNG RỜI KHỎI MÀN HÌNH NÀY HOẶC CÁC KHOẢN TIỀN ĐƯỢC QUÉT CÓ THỂ BỊ MẤT.",
907
+ "switch_wallet": "Chuyển ví",
908
"switchToETHWallet": "Vui lòng chuyển sang ví Ethereum và thử lại",
909
"switchToEVMCompatibleWallet": "Vui lòng chuyển sang ví tương thích EVM và thử lại (Ethereum, Polygon)",
910
"symbol": "Ký hiệu",
res/values/strings_yo.arb
+2
@@ -775,6 +775,7 @@
775
"seedtype": "Irugbin-seetypu",
776
"seedtype_alert_content": "Pinpin awọn irugbin pẹlu awọn gedo miiran ṣee ṣe pẹlu Bip39 irugbin.",
777
"seedtype_alert_title": "Ṣajọpọ Seeytype",
778
+ "select_a_wallet": "Yan apamọwọ kan",
779
"select_backup_file": "Select backup file",
780
"select_buy_provider_notice": "Yan olupese Ra loke. O le skii iboju yii nipa ṣiṣeto olupese rẹ ni awọn eto App.",
781
"select_destination": "Jọwọ yan ibi ti o nlo fun faili afẹyinti.",
@@ -907,6 +908,7 @@
908
"swap": "Eepo",
909
"sweeping_wallet": "Fi owo iwe iwe wofo",
910
"sweeping_wallet_alert": "Yio kọja pada si ikan yii. Kì yoo daadaa leede yii tabi owo ti o ti fi se iwe iwe naa yoo gbe.",
911
+ "switch_wallet": "Yipada apamọwọ",
912
"switchToETHWallet": "Jọwọ yipada si apamọwọ Ethereum ki o tun gbiyanju lẹẹkansi",
913
"switchToEVMCompatibleWallet": "Jọwọ yipada si apamọwọ ibaramu EVM ki o tun gbiyanju lẹẹkansi (Ethereum, Polygon)",
914
"symbol": "Aami",
res/values/strings_zh.arb
+2
@@ -774,6 +774,7 @@
774
"seedtype": "籽粒",
775
"seedtype_alert_content": "只有BIP39籽粒可以与其他钱包共享种子。",
776
"seedtype_alert_title": "籽粒警报",
777
+ "select_a_wallet": "选择一个钱包",
778
"select_backup_file": "选择备份文件",
779
"select_buy_provider_notice": "在上面选择买入提供商。您可以通过在应用程序设置中设置默认的购买提供商来跳过此屏幕。",
780
"select_destination": "请选择备份文件的目的地。",
@@ -906,6 +907,7 @@
907
"swap": "交换",
908
"sweeping_wallet": "扫一扫钱包",
909
"sweeping_wallet_alert": "\n这应该不会花很长时间。请勿离开此屏幕,否则可能会丢失所掠取的资金",
910
+ "switch_wallet": "开关钱包",
911
"switchToETHWallet": "请切换到以太坊钱包并重试",
912
"switchToEVMCompatibleWallet": "请切换到 EVM 兼容钱包并重试(以太坊、Polygon)",
913
"symbol": "象征",
test/core/universal_address_detector_test.dart
new
+242
@@ -0,0 +1,242 @@
1
+import 'package:cake_wallet/core/universal_address_detector.dart';
2
+import 'package:cw_core/crypto_currency.dart';
3
+import 'package:cw_core/wallet_type.dart';
4
+import 'package:flutter_test/flutter_test.dart';
5
+
6
+void main() {
7
+ group('UniversalAddressDetector', () {
8
+ group('Bitcoin Address Detection', () {
9
+ test('detects Bitcoin P2PKH address', () {
10
+ const address = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa';
11
+ final result = UniversalAddressDetector.detectAddress(address);
12
+
13
+ expect(result.isValid, true);
14
+ expect(result.detectedCurrency, CryptoCurrency.btc);
15
+ expect(result.detectedWalletType, WalletType.bitcoin);
16
+ expect(result.address, address);
17
+ });
18
+
19
+ test('detects Bitcoin P2SH address', () {
20
+ const address = '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy';
21
+ final result = UniversalAddressDetector.detectAddress(address);
22
+
23
+ expect(result.isValid, true);
24
+ expect(result.detectedCurrency, CryptoCurrency.btc);
25
+ expect(result.detectedWalletType, WalletType.bitcoin);
26
+ expect(result.address, address);
27
+ });
28
+
29
+ test('detects Bitcoin Bech32 address', () {
30
+ const address = 'bc1q56x5hha4mm35wmnqmj8ajkgxykf9cnjmrv3tmj';
31
+ final result = UniversalAddressDetector.detectAddress(address);
32
+
33
+ expect(result.isValid, true);
34
+ expect(result.detectedCurrency, CryptoCurrency.btc);
35
+ expect(result.detectedWalletType, WalletType.bitcoin);
36
+ expect(result.address, address);
37
+ });
38
+
39
+ test('detects Bitcoin URI with amount', () {
40
+ const uri = 'bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?amount=0.001';
41
+ final result = UniversalAddressDetector.detectAddress(uri);
42
+
43
+ expect(result.isValid, true);
44
+ expect(result.detectedCurrency, CryptoCurrency.btc);
45
+ expect(result.detectedWalletType, WalletType.bitcoin);
46
+ expect(result.address, '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa');
47
+ expect(result.amount, '0.001');
48
+ });
49
+ });
50
+
51
+ group('Monero Address Detection', () {
52
+ test('detects Monero address', () {
53
+ const address =
54
+ '48HHtdQvxyH5jwX5N6fAQD6rxx7EQT7GKZvsixtgJRkRaD8wHqwKp6eGSuStiUN5MHXR19vF3W4Jc7MumryYTTH7LGTfanS';
55
+ final result = UniversalAddressDetector.detectAddress(address);
56
+
57
+ expect(result.isValid, true);
58
+ expect(result.detectedCurrency, CryptoCurrency.xmr);
59
+ expect(result.detectedWalletType, WalletType.monero);
60
+ expect(result.address, address);
61
+ });
62
+
63
+ test('detects Monero integrated address', () {
64
+ const address =
65
+ '88HHtdQvxyH5jwX5N6fAQD6rxx7EQT7GKZvsixtgJRkRaD8wHqwKp6eGSuStiUN5MHXR19vF3W4Jc7MumryYTTH7LGTfanS';
66
+ final result = UniversalAddressDetector.detectAddress(address);
67
+
68
+ expect(result.isValid, true);
69
+ expect(result.detectedCurrency, CryptoCurrency.xmr);
70
+ expect(result.detectedWalletType, WalletType.monero);
71
+ expect(result.address, address);
72
+ });
73
+ });
74
+
75
+ group('Ethereum Address Detection', () {
76
+ test('detects Ethereum address', () {
77
+ const address = '0xAE3A8C650CDFad88e87621F8371642bd4B578601';
78
+ final result = UniversalAddressDetector.detectAddress(address);
79
+
80
+ expect(result.isValid, true);
81
+ expect(result.detectedCurrency, CryptoCurrency.eth);
82
+ expect(result.detectedWalletType, WalletType.ethereum);
83
+ expect(result.address, address);
84
+ });
85
+
86
+ test('detects Ethereum URI', () {
87
+ const uri = 'ethereum:0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6?value=1000000000000000000';
88
+ final result = UniversalAddressDetector.detectAddress(uri);
89
+
90
+ expect(result.isValid, true);
91
+ expect(result.detectedCurrency, CryptoCurrency.eth);
92
+ expect(result.detectedWalletType, WalletType.ethereum);
93
+ expect(result.address, '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6');
94
+ expect(result.amount, '1');
95
+ });
96
+ });
97
+
98
+ group('Litecoin Address Detection', () {
99
+ test('detects Litecoin Bech32 address', () {
100
+ const address = 'ltc1qk4ewr0fjgltsvwymfz7az66q2w73qj0z06cj36';
101
+ final result = UniversalAddressDetector.detectAddress(address);
102
+
103
+ expect(result.isValid, true);
104
+ expect(result.detectedCurrency, CryptoCurrency.ltc);
105
+ expect(result.detectedWalletType, WalletType.litecoin);
106
+ expect(result.address, address);
107
+ });
108
+ });
109
+
110
+ group('Bitcoin Cash Address Detection', () {
111
+ test('detects Bitcoin Cash address', () {
112
+ const address = 'bitcoincash:qqdjjymdpfmqzq69t4vdcxsmrzmlzlwgaucwm878p5';
113
+ final result = UniversalAddressDetector.detectAddress(address);
114
+
115
+ expect(result.isValid, true);
116
+ expect(result.detectedCurrency, CryptoCurrency.bch);
117
+ expect(result.detectedWalletType, WalletType.bitcoinCash);
118
+ expect(result.address, 'qqdjjymdpfmqzq69t4vdcxsmrzmlzlwgaucwm878p5');
119
+ });
120
+
121
+ test('detects Bitcoin Cash address without prefix', () {
122
+ const address = 'qqdjjymdpfmqzq69t4vdcxsmrzmlzlwgaucwm878p5';
123
+ final result = UniversalAddressDetector.detectAddress(address);
124
+
125
+ expect(result.isValid, true);
126
+ expect(result.detectedCurrency, CryptoCurrency.bch);
127
+ expect(result.detectedWalletType, WalletType.bitcoinCash);
128
+ expect(result.address, address);
129
+ });
130
+ });
131
+
132
+ group('Nano Address Detection', () {
133
+ test('detects Nano address', () {
134
+ const address = 'nano_3gsnr4a3ku3k9hjczobbr5fhhz94c66yczxbpjukeyy3edpxiyp1tqi8angm';
135
+ final result = UniversalAddressDetector.detectAddress(address);
136
+
137
+ expect(result.isValid, true);
138
+ expect(result.detectedCurrency, CryptoCurrency.nano);
139
+ expect(result.detectedWalletType, WalletType.nano);
140
+ expect(result.address, address);
141
+ });
142
+ });
143
+
144
+ group('Solana Address Detection', () {
145
+ test('detects Solana address', () {
146
+ const address = '7TSTXnQZxQDA4JoNqs4DqVqE7oNWk2kmHkMT6DD6vw2S';
147
+ final result = UniversalAddressDetector.detectAddress(address);
148
+
149
+ expect(result.isValid, true);
150
+ expect(result.detectedCurrency, CryptoCurrency.sol);
151
+ expect(result.detectedWalletType, WalletType.solana);
152
+ expect(result.address, address);
153
+ });
154
+ });
155
+
156
+ group('Tron Address Detection', () {
157
+ test('detects Tron address', () {
158
+ const address = 'TF7yQKp7pwLBSXBXthcrXMqTyjtvkJK28V';
159
+ final result = UniversalAddressDetector.detectAddress(address);
160
+
161
+ expect(result.isValid, true);
162
+ expect(result.detectedCurrency, CryptoCurrency.trx);
163
+ expect(result.detectedWalletType, WalletType.tron);
164
+ expect(result.address, address);
165
+ });
166
+ });
167
+
168
+ group('Wownero Address Detection', () {
169
+ test('detects Wownero address', () {
170
+ const address =
171
+ 'WW3uV9iygJjefkU5bmzeoCFkqZEwg1xC92BKcUFJMcSVF1HQoK4ehDTJScTU28CYvJaZ9sZt5xH8qF1n6vXqXE9G2KMC9JsKh';
172
+ final result = UniversalAddressDetector.detectAddress(address);
173
+
174
+ expect(result.isValid, true);
175
+ expect(result.detectedCurrency, CryptoCurrency.wow);
176
+ expect(result.detectedWalletType, WalletType.wownero);
177
+ expect(result.address, address);
178
+ });
179
+ });
180
+
181
+ group('Zano Address Detection', () {
182
+ test('detects Zano address', () {
183
+ const address =
184
+ 'ZxDREZKHjUAUkRpWrHV9AjJHe12RhtoY9TjUUx2EznNNdwJK9yiHEhuQkqGYnyDXRnFC3Uehu6kLGZmiQab8fWoQ1JrLsuJ1i';
185
+ final result = UniversalAddressDetector.detectAddress(address);
186
+
187
+ expect(result.isValid, true);
188
+ expect(result.detectedCurrency, CryptoCurrency.zano);
189
+ expect(result.detectedWalletType, WalletType.zano);
190
+ expect(result.address, address);
191
+ });
192
+
193
+ test('detects Zano alias', () {
194
+ const address = '@testuser';
195
+ final result = UniversalAddressDetector.detectAddress(address);
196
+
197
+ expect(result.isValid, true);
198
+ expect(result.detectedCurrency, CryptoCurrency.zano);
199
+ expect(result.detectedWalletType, WalletType.zano);
200
+ expect(result.address, address);
201
+ });
202
+ });
203
+
204
+ group('Decred Address Detection', () {
205
+ test('detects Decred address', () {
206
+ const address = 'DsW4ZtRV1DcPCuKXVVtGnCy3AtAZHg5N8nR';
207
+ final result = UniversalAddressDetector.detectAddress(address);
208
+
209
+ expect(result.isValid, true);
210
+ expect(result.detectedCurrency, CryptoCurrency.dcr);
211
+ expect(result.detectedWalletType, WalletType.decred);
212
+ expect(result.address, address);
213
+ });
214
+ });
215
+
216
+ group('Invalid Address Detection', () {
217
+ test('handles empty input', () {
218
+ const address = '';
219
+ final result = UniversalAddressDetector.detectAddress(address);
220
+
221
+ expect(result.isValid, false);
222
+ expect(result.errorMessage, 'Empty input provided');
223
+ });
224
+
225
+ test('handles whitespace only', () {
226
+ const address = ' ';
227
+ final result = UniversalAddressDetector.detectAddress(address);
228
+
229
+ expect(result.isValid, false);
230
+ expect(result.errorMessage, 'Empty input provided');
231
+ });
232
+
233
+ test('handles random text', () {
234
+ const address = 'this_is_not_an_address_123';
235
+ final result = UniversalAddressDetector.detectAddress(address);
236
+
237
+ expect(result.isValid, false);
238
+ expect(result.errorMessage, 'Unable to detect valid cryptocurrency address');
239
+ });
240
+ });
241
+ });
242
+}