CW-1208: Pay Anything EVM enahancements (#2600)
What changed, and why it matters
This commit adds user-facing features to Cake Wallet for handling EVM (Ethereum-like) addresses and Bitcoin Lightning payments. It lets users pick which EVM network and token to use when scanning a generic 0x address, improves token matching, and adds detection for Lightning invoices, Lightning email-style addresses, and LNURL codes. There is no clear security bug in the diff, but the new address-detection logic is broad and could misclassify ordinary email addresses as Lightning payment addresses, which might confuse users or lead to incorrect payment flows.
Review the new address-detection heuristics before release. Tighten the Lightning-address regex so it does not match arbitrary email addresses, and add explicit allow-lists or separators for LNURL strings. Add unit tests for false-positive cases (e.g., regular emails, non-Lightning `LNURL...` strings). Verify that the EVM network-selection flow cannot be used to send funds to a contract address intended for a different chain, and that token deduplication by contract address does not hide legitimate user tokens.
Security signals we found
Overly permissive regex now classifies any email-like string as a Lightning address
LNURL detection uses a broad case-insensitive prefix match without length/character-set validation
EVM address detection treats all 0x40-hex strings as requiring network selection, shifting chain resolution to user choice
Token matching logic changed from title+tag equality to title+tag case-insensitive comparison with tag fallback semantics
No input sanitization changes visible for addresses passed into swap/payment flows
Evidence from the diff
The patch is a feature enhancement for the ‘Pay Anything’ flow. Key code changes: (1) UniversalAddressDetector now treats any user@host.tld string and any LNURL... string as a Bitcoin Lightning address/currency. (2) PaymentViewModel routes any 0x[a-fA-F0-9]{40} address into a new evmNetworkSelection flow instead of treating it as a specific EVM chain. (3) A new bottom sheet (EVMPaymentFlowBottomSheet) lets the user choose network and token, defaulting to Ethereum and the first token. (4) TokenUtilities was refactored to match currencies by title/tag and deduplicate by contract address. (5) UI updates preserve amount/note through chain selection and show chain badges. No explicit vulnerability is introduced, but the Lightning-address regex is overly permissive and the EVM flow relies on user selection rather than deterministic chain detection.
Changed components
lib/core/universal_address_detector.dartlib/core/address_validator.dartlib/view_model/payment/payment_view_model.dartlib/utils/token_utilities.dartlib/src/widgets/bottom_sheet/evm_payment_flow_bottom_sheet.dartlib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dartlib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dartlib/src/screens/send/widgets/send_card.dartcw_core/lib/crypto_currency.dartcw_core/lib/wallet_type.dartInspect captured patch +764 / −73
diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart
index 4ae3aca..0857c00 100644
--- a/cw_core/lib/crypto_currency.dart
+++ b/cw_core/lib/crypto_currency.dart
@@ -284,6 +284,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
'wrappedether': weth,
'shibainu': shib,
'zcash': zec,
+ 'base': baseEth,
};
static CryptoCurrency deserialize({required int raw}) {
diff --git a/cw_core/lib/wallet_type.dart b/cw_core/lib/wallet_type.dart
index 70a61d1..5ef50ee 100644
--- a/cw_core/lib/wallet_type.dart
+++ b/cw_core/lib/wallet_type.dart
@@ -249,6 +249,7 @@ WalletType? cryptoCurrencyToWalletType(CryptoCurrency type) {
case CryptoCurrency.xmr:
return WalletType.monero;
case CryptoCurrency.btc:
+ case CryptoCurrency.btcln:
return WalletType.bitcoin;
case CryptoCurrency.ltc:
return WalletType.litecoin;
diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart
index d7f15d2..64d9667 100644
--- a/lib/core/address_validator.dart
+++ b/lib/core/address_validator.dart
@@ -332,6 +332,7 @@ class AddressValidator extends TextValidator {
'|([^0-9a-zA-Z]|^)((ltc|t)mweb1q[ac-hj-np-z02-9]{90,120})([^0-9a-zA-Z]|\$)';
case CryptoCurrency.eth:
case CryptoCurrency.maticpoly:
+ case CryptoCurrency.baseEth:
pattern = '0x[0-9a-zA-Z]+';
case CryptoCurrency.nano:
pattern = 'nano_[0-9a-zA-Z]{60}';
diff --git a/lib/core/universal_address_detector.dart b/lib/core/universal_address_detector.dart
index 129e7d3..41a4f59 100644
--- a/lib/core/universal_address_detector.dart
+++ b/lib/core/universal_address_detector.dart
@@ -104,6 +104,18 @@ class UniversalAddressDetector {
pattern: RegExp(r'^(lnbc|LNBC)[a-km-zA-HJ-NP-Z1-9]{1,}[a-zA-Z0-9]+$'),
currency: CryptoCurrency.btcln,
),
+
+ // Lightning Address (email format)
+ _DetectionPattern(
+ pattern: RegExp(r'^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'),
+ currency: CryptoCurrency.btcln,
+ ),
+
+ // LNURL format
+ _DetectionPattern(
+ pattern: RegExp(r'^(LNURL|lnurl)[a-zA-Z0-9]+$', caseSensitive: false),
+ currency: CryptoCurrency.btcln,
+ ),
// Bitcoin (P2PKH, P2SH, Bech32, Silent Payments)
_DetectionPattern(
diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart
index accac02..b0d4f76 100644
--- a/lib/src/screens/send/widgets/send_card.dart
+++ b/lib/src/screens/send/widgets/send_card.dart
@@ -6,6 +6,7 @@ import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dar
import 'package:cake_wallet/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/wallet_switcher_bottom_sheet.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart';
+import 'package:cake_wallet/src/widgets/bottom_sheet/evm_payment_flow_bottom_sheet.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
@@ -166,6 +167,13 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
result,
);
break;
+ case PaymentFlowType.evmNetworkSelection:
+ await _showEVMPaymentFlow(
+ paymentViewModel,
+ walletSwitcherViewModel,
+ paymentRequest,
+ );
+ break;
case PaymentFlowType.currentWalletCompatible:
case PaymentFlowType.error:
case PaymentFlowType.incompatible:
@@ -198,6 +206,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
paymentViewModel,
walletSwitcherViewModel,
paymentRequest,
+ result,
),
onChangeWallet: () => _handleChangeWallet(
paymentViewModel,
@@ -211,10 +220,43 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
);
}
+ Future<void> _showEVMPaymentFlow(
+ PaymentViewModel paymentViewModel,
+ WalletSwitcherViewModel walletSwitcherViewModel,
+ PaymentRequest paymentRequest,
+ ) async {
+ await showModalBottomSheet<void>(
+ context: context,
+ isDismissible: true,
+ isScrollControlled: true,
+ builder: (BuildContext context) {
+ return EVMPaymentFlowBottomSheet(
+ paymentViewModel: paymentViewModel,
+ paymentRequest: paymentRequest,
+ onNext: (PaymentFlowResult newResult) {
+ if (newResult.addressDetectionResult!.detectedWalletType ==
+ paymentViewModel.currentWalletType) {
+ sendViewModel.setSelectedCryptoCurrency(
+ newResult.addressDetectionResult!.detectedCurrency!.title);
+ } else {
+ _showPaymentConfirmation(
+ paymentViewModel,
+ walletSwitcherViewModel,
+ paymentRequest,
+ newResult,
+ );
+ }
+ },
+ );
+ },
+ );
+ }
+
Future<void> _handleSelectWallet(
PaymentViewModel paymentViewModel,
WalletSwitcherViewModel walletSwitcherViewModel,
PaymentRequest paymentRequest,
+ PaymentFlowResult result,
) async {
Navigator.of(context).pop();
@@ -233,6 +275,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
final success = await walletSwitcherViewModel.switchToSelectedWallet();
if (success) {
+ await sendViewModel.wallet.updateBalance();
+ sendViewModel
+ .setSelectedCryptoCurrency(result.addressDetectionResult!.detectedCurrency!.title);
_applyPaymentRequest(paymentRequest);
}
}
@@ -269,6 +314,10 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
if (loadingBottomSheetContext != null && loadingBottomSheetContext!.mounted) {
Navigator.of(loadingBottomSheetContext!).pop();
}
+
+ await sendViewModel.wallet.updateBalance();
+ sendViewModel
+ .setSelectedCryptoCurrency(result.addressDetectionResult!.detectedCurrency!.title);
_applyPaymentRequest(paymentRequest);
}
}
@@ -397,6 +446,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
final address =
output.isParsedAddress ? output.extractedAddress : output.address;
+ if (address.contains('@')) return;
+
await _handlePaymentFlow(
address,
PaymentRequest(
diff --git a/lib/src/widgets/bottom_sheet/evm_payment_flow_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/evm_payment_flow_bottom_sheet.dart
new file mode 100644
index 0000000..411cda6
--- /dev/null
+++ b/lib/src/widgets/bottom_sheet/evm_payment_flow_bottom_sheet.dart
@@ -0,0 +1,309 @@
+import 'package:cake_wallet/core/universal_address_detector.dart';
+import 'package:cake_wallet/generated/i18n.dart';
+import 'package:cake_wallet/reactions/wallet_connect.dart';
+import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
+import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
+import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
+import 'package:cake_wallet/src/widgets/picker.dart';
+import 'package:cake_wallet/src/widgets/primary_button.dart';
+import 'package:cake_wallet/utils/payment_request.dart';
+import 'package:cake_wallet/utils/qr_util.dart';
+import 'package:cake_wallet/utils/show_pop_up.dart';
+import 'package:cake_wallet/utils/token_utilities.dart';
+import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
+import 'package:cake_wallet/wallet_types.g.dart';
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/currency.dart';
+import 'package:cw_core/utils/print_verbose.dart';
+import 'package:cw_core/wallet_type.dart';
+import 'package:flutter/material.dart';
+
+class EVMPaymentFlowBottomSheet extends BaseBottomSheet {
+ EVMPaymentFlowBottomSheet({
+ Key? key,
+ required this.paymentViewModel,
+ required this.paymentRequest,
+ required this.onNext,
+ }) : super(
+ titleText: '',
+ footerType: FooterType.none,
+ maxHeight: 900,
+ );
+
+ final PaymentViewModel paymentViewModel;
+ final PaymentRequest paymentRequest;
+ final Function(PaymentFlowResult) onNext;
+
+ @override
+ Widget contentWidget(BuildContext context) {
+ return _EVMPaymentFlowContent(
+ paymentViewModel: paymentViewModel,
+ paymentRequest: paymentRequest,
+ onNext: onNext,
+ );
+ }
+}
+
+class _EVMPaymentFlowContent extends StatefulWidget {
+ const _EVMPaymentFlowContent({
+ required this.paymentViewModel,
+ required this.paymentRequest,
+ required this.onNext,
+ });
+
+ final PaymentViewModel paymentViewModel;
+ final PaymentRequest paymentRequest;
+ final Function(PaymentFlowResult) onNext;
+
+ @override
+ State<_EVMPaymentFlowContent> createState() => _EVMPaymentFlowContentState();
+}
+
+class _EVMPaymentFlowContentState extends State<_EVMPaymentFlowContent> {
+ WalletType? selectedNetwork;
+ CryptoCurrency? selectedToken;
+
+ @override
+ void initState() {
+ super.initState();
+ selectedNetwork = WalletType.ethereum;
+ _autoSelectToken();
+ }
+
+ Future<void> _autoSelectToken() async {
+ if (selectedNetwork == null) return;
+
+ try {
+ final tokens = await TokenUtilities.getAvailableTokensForNetwork(selectedNetwork!);
+ if (tokens.isNotEmpty) {
+ setState(() {
+ selectedToken = tokens.first;
+ });
+ }
+ } catch (e) {
+ printV('Auto-select token error: $e');
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ CakeImageWidget(
+ imageUrl: 'assets/images/eth_chain_mono.svg',
+ width: 50,
+ height: 50,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
+ const SizedBox(height: 24),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: Text(
+ '${S.current.ethereum_ecosystem}\n${S.current.address_detected.toLowerCase()}',
+ style: Theme.of(context).textTheme.titleMedium!.copyWith(
+ fontSize: 20,
+ fontWeight: FontWeight.w900,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
+ textAlign: TextAlign.center,
+ ),
+ ),
+ const SizedBox(height: 36),
+ Text(
+ S.current.evm_ecosystem_description,
+ textAlign: TextAlign.center,
+ style: Theme.of(context).textTheme.bodyMedium!.copyWith(
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ letterSpacing: 0.0,
+ ),
+ ),
+ const SizedBox(height: 32),
+ EVMTileWidget(
+ value: selectedNetwork != null
+ ? walletTypeToString(selectedNetwork!)
+ : S.current.select_network,
+ imagePath: selectedNetwork != null ? getChainMonoImage(selectedNetwork!) : null,
+ color: selectedNetwork != null ? Theme.of(context).colorScheme.primary : null,
+ enabled: true,
+ onTap: () => _showNetworkSelection(context),
+ ),
+ const SizedBox(height: 16),
+ EVMTileWidget(
+ imagePath: selectedToken != null ? selectedToken!.iconPath : null,
+ value: selectedToken != null ? selectedToken!.title : S.current.select_token,
+ enabled: selectedNetwork != null,
+ onTap: () => _showTokenSelection(context),
+ color: selectedToken == null ? Theme.of(context).colorScheme.primary : null,
+ ),
+ const SizedBox(height: 32),
+ PrimaryButton(
+ text: S.current.restore_next,
+ color: Theme.of(context).colorScheme.primary,
+ textColor: Theme.of(context).colorScheme.onPrimary,
+ onPressed: selectedNetwork != null && selectedToken != null
+ ? () async => await _handleNext(context)
+ : null,
+ ),
+ const SizedBox(height: 48),
+ ],
+ ),
+ );
+ }
+
+ void _showNetworkSelection(BuildContext context) async {
+ final evmNetworks =
+ availableWalletTypes.where((walletType) => isEVMCompatibleChain(walletType)).toList();
+ final selectedIndex = evmNetworks.indexOf(selectedNetwork ?? WalletType.ethereum);
+
+ await showPopUp<void>(
+ context: context,
+ builder: (BuildContext context) {
+ return Picker(
+ items: evmNetworks,
+ displayItem: (WalletType network) => walletTypeToString(network),
+ selectedAtIndex: selectedIndex,
+ title: S.current.select_network,
+ closeOnItemSelected: true,
+ hasTitleSpacing: true,
+ images: evmNetworks
+ .map((network) => CakeImageWidget(
+ imageUrl: getChainMonoImage(network),
+ width: 20,
+ height: 20,
+ color: Theme.of(context).colorScheme.primary))
+ .toList(),
+ onItemSelected: (WalletType network) {
+ setState(() {
+ selectedNetwork = network;
+ selectedToken = null;
+ });
+ _autoSelectToken();
+ },
+ );
+ },
+ );
+ }
+
+ void _showTokenSelection(BuildContext context) async {
+ if (selectedNetwork == null) return;
+
+ try {
+ final availableTokens = await TokenUtilities.getAvailableTokensForNetwork(selectedNetwork!);
+
+ if (availableTokens.isEmpty) return;
+
+ final selectedIndex = selectedToken != null ? availableTokens.indexOf(selectedToken!) : 0;
+
+ await showPopUp<void>(
+ context: context,
+ builder: (BuildContext context) {
+ return CurrencyPicker(
+ selectedAtIndex: selectedIndex >= 0 ? selectedIndex : 0,
+ items: availableTokens.cast<Currency>(),
+ hintText: S.current.add_token,
+ onItemSelected: (Currency currency) {
+ setState(() {
+ selectedToken = currency as CryptoCurrency;
+ });
+ },
+ );
+ },
+ );
+ } catch (e) {
+ printV('Error showing token selection: $e');
+ }
+ }
+
+ Future<void> _handleNext(BuildContext context) async {
+ if (selectedNetwork == null || selectedToken == null) return;
+
+ Navigator.of(context).pop();
+
+ final compatibleWallets = await widget.paymentViewModel.getWalletsByType(selectedNetwork!);
+
+ final newResult = PaymentFlowResult.evmNetworkSelection(
+ AddressDetectionResult(
+ address: widget.paymentRequest.address,
+ detectedWalletType: selectedNetwork!,
+ detectedCurrency: selectedToken!,
+ isValid: true,
+ amount: widget.paymentRequest.amount,
+ note: widget.paymentRequest.note,
+ scheme: widget.paymentRequest.scheme,
+ pjUri: widget.paymentRequest.pjUri,
+ callbackUrl: widget.paymentRequest.callbackUrl,
+ callbackMessage: widget.paymentRequest.callbackMessage,
+ ),
+ compatibleWallets: compatibleWallets,
+ wallet: compatibleWallets.isNotEmpty ? compatibleWallets.first : null,
+ );
+
+ widget.paymentViewModel.detectedWalletType = selectedNetwork!;
+ widget.onNext(newResult);
+ }
+}
+
+class EVMTileWidget extends StatelessWidget {
+ const EVMTileWidget({
+ super.key,
+ required this.value,
+ required this.enabled,
+ required this.onTap,
+ this.imagePath,
+ this.color,
+ });
+
+ final String value;
+ final bool enabled;
+ final VoidCallback? onTap;
+ final String? imagePath;
+ final Color? color;
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: enabled ? onTap : null,
+ borderRadius: BorderRadius.circular(12),
+ child: Container(
+ padding: const EdgeInsets.all(16),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(12),
+ color: Theme.of(context).colorScheme.surfaceContainer,
+ ),
+ child: Row(
+ children: [
+ if (imagePath != null) ...[
+ CakeImageWidget(
+ imageUrl: imagePath!,
+ color: color,
+ width: 20,
+ height: 20,
+ ),
+ const SizedBox(width: 16),
+ ],
+ Expanded(
+ child: Text(
+ value,
+ style: Theme.of(context).textTheme.titleMedium!.copyWith(
+ fontSize: 14,
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
+ ),
+ ),
+ Icon(
+ Icons.keyboard_arrow_down,
+ size: 24,
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart
index 56f6a57..7185056 100644
--- a/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart
+++ b/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart
@@ -88,11 +88,16 @@ class _PaymentConfirmationContent extends StatelessWidget {
final currencyName = walletTypeToString(paymentViewModel.detectedWalletType!);
final currentWalletName = walletTypeToString(paymentViewModel.currentWalletType);
- final hasMultipleWallets = paymentFlowResult.type == PaymentFlowType.multipleWallets;
- final noAvailableWallets = paymentFlowResult.type == PaymentFlowType.noWallets;
+ final hasSingleWallet = paymentFlowResult.type == PaymentFlowType.singleWallet ||
+ paymentFlowResult.wallets.length == 1;
- final hasAtLeastOneWallet =
- paymentFlowResult.type == PaymentFlowType.singleWallet || hasMultipleWallets;
+ final hasMultipleWallets = paymentFlowResult.type == PaymentFlowType.multipleWallets ||
+ paymentFlowResult.wallets.length > 1;
+
+ final noAvailableWallets = paymentFlowResult.type == PaymentFlowType.noWallets ||
+ paymentFlowResult.wallets.isEmpty;
+
+ final hasAtLeastOneWallet = hasSingleWallet || hasMultipleWallets;
final isMwebOrSpAddress =
_isMwebOrSpAddress(paymentFlowResult.addressDetectionResult?.address ?? '');
@@ -103,34 +108,78 @@ class _PaymentConfirmationContent extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
- Image.asset(
- walletTypeToCryptoCurrency(paymentViewModel.detectedWalletType!).iconPath!,
- width: 118,
- height: 118,
- ),
- const SizedBox(height: 20),
- Text(
- '$currencyName ${S.current.address_detected.toLowerCase()}',
- style: Theme.of(context).textTheme.titleMedium!.copyWith(
- fontSize: 20,
- fontWeight: FontWeight.w900,
- color: Theme.of(context).colorScheme.onSurface,
- letterSpacing: 0.0,
+ if (paymentFlowResult.type == PaymentFlowType.evmNetworkSelection) ...[
+ Stack(
+ clipBehavior: Clip.none,
+ children: [
+ Image.asset(
+ paymentFlowResult.addressDetectionResult!.detectedCurrency!.iconPath!,
+ width: 70,
+ height: 70,
),
- textAlign: TextAlign.center,
- ),
- const SizedBox(height: 24),
- Text(
- '''Looks like you scanned a $currencyName address.\n\n'''
- '''Would you like to ${!noAvailableWallets ? 'switch to a $currencyName wallet or' : ''} swap $currentWalletName for $currencyName for this payment?''',
- textAlign: TextAlign.center,
- style: Theme.of(context).textTheme.bodyMedium!.copyWith(
- fontSize: 16,
- fontWeight: FontWeight.w600,
- color: Theme.of(context).colorScheme.onSurfaceVariant,
- letterSpacing: 0.0,
+ Positioned(
+ bottom: 0,
+ right: 0,
+ child: Image.asset(
+ walletTypeToCryptoCurrency(paymentViewModel.detectedWalletType!).iconPath!,
+ width: 32,
+ height: 32,
+ ),
),
- ),
+ ],
+ ),
+ const SizedBox(height: 20),
+ Text(
+ '${paymentFlowResult.addressDetectionResult?.detectedCurrency} (${walletTypeToString(paymentFlowResult.walletType!)})',
+ style: Theme.of(context).textTheme.titleMedium!.copyWith(
+ fontSize: 20,
+ fontWeight: FontWeight.w900,
+ color: Theme.of(context).colorScheme.onSurface,
+ letterSpacing: 0.0,
+ ),
+ textAlign: TextAlign.center,
+ ),
+ const SizedBox(height: 24),
+ Text(
+ '''Would you like to ${!noAvailableWallets ? 'switch to a $currencyName wallet or' : ''} swap $currentWalletName for ${paymentFlowResult.addressDetectionResult?.detectedCurrency} (${walletTypeToString(paymentFlowResult.walletType!)}) for this payment?''',
+ textAlign: TextAlign.center,
+ style: Theme.of(context).textTheme.bodyMedium!.copyWith(
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ letterSpacing: 0.0,
+ ),
+ ),
+ ] else ...[
+ Image.asset(
+ walletTypeToCryptoCurrency(paymentViewModel.detectedWalletType!).iconPath!,
+ width: 118,
+ height: 118,
+ ),
+ const SizedBox(height: 20),
+ Text(
+ '$currencyName ${S.current.address_detected.toLowerCase()}',
+ style: Theme.of(context).textTheme.titleMedium!.copyWith(
+ fontSize: 20,
+ fontWeight: FontWeight.w900,
+ color: Theme.of(context).colorScheme.onSurface,
+ letterSpacing: 0.0,
+ ),
+ textAlign: TextAlign.center,
+ ),
+ const SizedBox(height: 24),
+ Text(
+ '''Looks like you scanned a $currencyName address.\n\n'''
+ '''Would you like to ${!noAvailableWallets ? 'switch to a $currencyName wallet or' : ''} swap $currentWalletName for $currencyName for this payment?''',
+ textAlign: TextAlign.center,
+ style: Theme.of(context).textTheme.bodyMedium!.copyWith(
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ letterSpacing: 0.0,
+ ),
+ ),
+ ],
const SizedBox(height: 72),
if (hasAtLeastOneWallet) ...[
if (!isMwebOrSpAddress) ...[
diff --git a/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart
index afb1b5d..8bcf717 100644
--- a/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart
+++ b/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart
@@ -137,7 +137,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
@override
Widget build(BuildContext context) {
- final detectedCurrency = walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!);
+ final detectedCurrency = widget.paymentFlowResult.detectedCurrency!;
return Form(
key: _formKey,
@@ -158,11 +158,25 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
const SizedBox(width: 12),
Icon(Icons.arrow_forward, size: 24),
const SizedBox(width: 12),
- CakeImageWidget(
- imageUrl:
- detectedCurrency.iconPath!,
- width: 32,
- height: 32,
+ Stack(
+ clipBehavior: Clip.none,
+ children: [
+ CakeImageWidget(
+ imageUrl: detectedCurrency.iconPath!,
+ width: 32,
+ height: 32,
+ ),
+ Positioned(
+ bottom: -4,
+ right: -4,
+ child: CakeImageWidget(
+ imageUrl: walletTypeToCryptoCurrency(widget.paymentFlowResult.walletType!)
+ .iconPath!,
+ width: 16,
+ height: 16,
+ ),
+ ),
+ ],
),
],
),
@@ -414,7 +428,8 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
}
});
- exchangeViewModel.receiveCurrency = walletTypeToCryptoCurrency(paymentFlowResult.walletType!);
+ exchangeViewModel.receiveCurrency = paymentFlowResult.detectedCurrency!;
+
await exchangeViewModel.fetchFiatPrice(exchangeViewModel.receiveCurrency);
exchangeViewModel.receiveAddress = _addressController.text;
diff --git a/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart
index 79e6cff..c09d410 100644
--- a/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart
+++ b/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart
@@ -272,13 +272,13 @@ class _SwapDetailsContent extends StatelessWidget {
children: [
_SwapDetailsTile(
label: 'You Send',
- value: '${trade.amount} ${trade.from?.title ?? ''}',
+ value: '${trade.amount} ${trade.from?.title ?? trade.userCurrencyFrom?.title ?? ''}',
valueFiatFormatted: exchangeTradeViewModel.sendAmountFiatFormatted,
),
const SizedBox(height: 8),
_SwapDetailsTile(
label: 'You Get',
- value: '${trade.receiveAmount ?? '0'} ${trade.to?.title ?? ''}',
+ value: '${trade.receiveAmount ?? '0'} ${trade.to?.title ?? trade.userCurrencyTo?.title ?? ''}',
valueFiatFormatted: exchangeTradeViewModel
.getReceiveAmountFiatFormatted(trade.receiveAmount ?? '0.0'),
),
diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart
index 5726457..352f344 100644
--- a/lib/src/widgets/picker.dart
+++ b/lib/src/widgets/picker.dart
@@ -18,7 +18,9 @@ class Picker<Item> extends StatefulWidget {
required this.onItemSelected,
this.title,
this.displayItem,
- this.images = const <Image>[],
+ this.displayItemColor,
+ this.displayItemFontSize,
+ this.images = const <Widget>[],
this.description,
this.mainAxisAlignment = MainAxisAlignment.start,
this.isGridView = false,
@@ -34,6 +36,7 @@ class Picker<Item> extends StatefulWidget {
this.borderColor,
this.onSliderChanged,
this.matchingCriteria,
+ this.hasTitleSpacing = false,
}) : assert(hintText == null || matchingCriteria != null) {
// make sure that if the search field is enabled then there is a searching criteria provided
if (sliderValue != null && maxValue != null) {
@@ -45,12 +48,14 @@ class Picker<Item> extends StatefulWidget {
final int selectedAtIndex;
final List<Item> items;
- final List<Image> images;
+ final List<Widget> images;
final String? title;
final String? description;
final Function(Item) onItemSelected;
final MainAxisAlignment mainAxisAlignment;
final String Function(Item)? displayItem;
+ final Color? displayItemColor;
+ final double? displayItemFontSize;
final bool isGridView;
final bool isSeparated;
final String? hintText;
@@ -64,7 +69,7 @@ class Picker<Item> extends StatefulWidget {
final Function(double)? onSliderChanged;
final bool Function(Item, String)? matchingCriteria;
final double? maxValue;
-
+ final bool hasTitleSpacing;
@override
_PickerState<Item> createState() => _PickerState<Item>(items, images, onItemSelected);
}
@@ -74,9 +79,9 @@ class _PickerState<Item> extends State<Picker<Item>> {
final Function(Item) onItemSelected;
List<Item> items;
- List<Image> images;
+ List<Widget> images;
List<Item> filteredItems = [];
- List<Image> filteredImages = [];
+ List<Widget> filteredImages = [];
final TextEditingController searchController = TextEditingController();
ScrollController controller = ScrollController();
@@ -163,6 +168,7 @@ class _PickerState<Item> extends State<Picker<Item>> {
),
),
),
+ widget.hasTitleSpacing ? const SizedBox(height: 24) : const SizedBox.shrink(),
Padding(
padding: EdgeInsets.symmetric(horizontal: padding),
child: Container(
@@ -342,6 +348,7 @@ class _PickerState<Item> extends State<Picker<Item>> {
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
+ color: widget.displayItemColor,
),
),
),
diff --git a/lib/utils/qr_util.dart b/lib/utils/qr_util.dart
index 3a9981b..74776c8 100644
--- a/lib/utils/qr_util.dart
+++ b/lib/utils/qr_util.dart
@@ -38,3 +38,24 @@ String getQrImage(WalletType type) {
return 'assets/images/qr-cake.png';
}
}
+
+String getChainMonoImage(WalletType type) {
+ switch (type) {
+ case WalletType.ethereum:
+ return 'assets/images/eth_chain_mono.svg';
+ case WalletType.solana:
+ return 'assets/images/sol_chain_mono.svg';
+ case WalletType.polygon:
+ return 'assets/images/pol_chain_mono.svg';
+ case WalletType.tron:
+ return 'assets/images/trx_chain_mono.svg';
+ case WalletType.zano:
+ return 'assets/images/zano_chain_mono.svg';
+ case WalletType.base:
+ return 'assets/images/base_chain_mono.svg';
+ case WalletType.arbitrum:
+ return 'assets/images/arbitrum_chain_mono.svg';
+ default:
+ return 'assets/images/eth_chain_mono.svg';
+ }
+}
diff --git a/lib/utils/token_utilities.dart b/lib/utils/token_utilities.dart
index 184eb56..13c868b 100644
--- a/lib/utils/token_utilities.dart
+++ b/lib/utils/token_utilities.dart
@@ -1,6 +1,7 @@
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/spl_token.dart';
import 'package:cw_core/tron_token.dart';
@@ -149,7 +150,7 @@ class TokenUtilities {
// More of a fallback for us
for (final balanceCurrency in wallet.balance.keys) {
- if (balanceCurrency is Erc20Token && _matchesToken(balanceCurrency, currency)) {
+ if (balanceCurrency is Erc20Token && _matchesCurrency(balanceCurrency, currency)) {
return balanceCurrency;
}
}
@@ -217,10 +218,73 @@ class TokenUtilities {
return 1;
}
- /// Checks if two currencies match (by title and tag)
- static bool _matchesToken(Erc20Token token, CryptoCurrency currency) {
- return token.title.toLowerCase() == currency.title.toLowerCase() &&
- (token.tag?.toLowerCase() == currency.tag?.toLowerCase() ||
- (token.tag == null && currency.tag == null));
+ static Future<List<CryptoCurrency>> getAvailableTokensForNetwork(
+ WalletType network,
+ ) async {
+ final baseCurrency = walletTypeToCryptoCurrency(network);
+ final allTokens = <CryptoCurrency>[];
+ final addedAddresses = <String>{};
+
+ allTokens.add(baseCurrency);
+
+ for (final currency in CryptoCurrency.all) {
+ // For EVM networks: ETH has no tag, POL/BASE have tags
+ // Match by tag for POL/BASE, match by title==tag for ETH
+ final matches = (baseCurrency.tag == null && baseCurrency.title == currency.tag) ||
+ (baseCurrency.tag != null &&
+ currency.tag?.toLowerCase() == baseCurrency.tag?.toLowerCase());
+
+ if (matches && _shouldAddToken(allTokens, currency, addedAddresses)) {
+ allTokens.add(currency);
+ }
+ }
+
+ // Add user tokens that don't already exist
+ final userTokens = await _getUserTokensForNetwork(baseCurrency);
+ for (final token in userTokens) {
+ if (_shouldAddToken(allTokens, token, addedAddresses)) {
+ allTokens.add(token);
+ if (token is Erc20Token) {
+ addedAddresses.add(token.contractAddress.toLowerCase());
+ }
+ }
+ }
+
+ return allTokens;
+ }
+
+ static bool _shouldAddToken(
+ List<CryptoCurrency> existingTokens,
+ CryptoCurrency token,
+ Set<String> addedAddresses,
+ ) {
+ if (token is Erc20Token) {
+ final address = token.contractAddress.toLowerCase();
+ if (addedAddresses.contains(address)) {
+ return false;
+ }
+ if (existingTokens.any((existing) => _matchesCurrency(existing, token))) {
+ return false;
+ }
+ return true;
+ }
+
+ return !existingTokens.any((existing) => _matchesCurrency(existing, token));
+ }
+
+ static bool _matchesCurrency(CryptoCurrency a, CryptoCurrency b) {
+ return a.title.toUpperCase() == b.title.toUpperCase() &&
+ (a.tag?.toUpperCase() == b.tag?.toUpperCase());
+ }
+
+ static Future<List<CryptoCurrency>> _getUserTokensForNetwork(CryptoCurrency baseCurrency) async {
+ final tokens = await TokenUtilities.loadAllUniqueEvmTokens();
+
+ return tokens.where((token) {
+ // Match by tag, except for ETH which has no tag - match by title instead
+ if (baseCurrency.tag == null) return token.tag == baseCurrency.title;
+
+ return token.tag?.toLowerCase() == baseCurrency.tag?.toLowerCase();
+ }).toList();
}
}
diff --git a/lib/view_model/payment/payment_view_model.dart b/lib/view_model/payment/payment_view_model.dart
index 5676a6b..9e8bf31 100644
--- a/lib/view_model/payment/payment_view_model.dart
+++ b/lib/view_model/payment/payment_view_model.dart
@@ -5,6 +5,8 @@ import 'package:cake_wallet/store/app_store.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/utils/print_verbose.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
+import 'package:cw_core/crypto_currency.dart';
import 'package:mobx/mobx.dart';
part 'payment_view_model.g.dart';
@@ -37,11 +39,15 @@ abstract class PaymentViewModelBase with Store {
// Detect address type
final detectionResult = UniversalAddressDetector.detectAddress(addressData);
- if (!detectionResult.isValid || detectionResult.detectedWalletType == null) {
+ detectedWalletType = detectionResult.detectedWalletType;
+
+ if (!detectionResult.isValid || detectedWalletType == null) {
return PaymentFlowResult.incompatible('Unable to detect address type');
}
- detectedWalletType = detectionResult.detectedWalletType;
+ if (_isEVMAddress(detectionResult.address)) {
+ return PaymentFlowResult.evmNetworkSelection(detectionResult);
+ }
// Check if current wallet is compatible
final currentWallet = appStore.wallet;
@@ -67,6 +73,10 @@ abstract class PaymentViewModelBase with Store {
}
}
+ bool _isEVMAddress(String address) {
+ return RegExp(r'^0x[a-fA-F0-9]{40}$').hasMatch(address);
+ }
+
Future<List<WalletInfo>> getWalletsByType(WalletType walletType) async {
return (await WalletInfo.getAll()).where((wallet) => wallet.type == walletType).toList();
}
@@ -89,13 +99,30 @@ class PaymentFlowResult {
this.addressDetectionResult,
});
+ /// EVM address detected - needs network selection
+ /// We'll also take note of the number of compatible wallets for EVM ecosystem
+ factory PaymentFlowResult.evmNetworkSelection(
+ AddressDetectionResult addressDetectionResult, {
+ List<WalletInfo>? compatibleWallets,
+ WalletInfo? wallet,
+ }) =>
+ PaymentFlowResult._(
+ type: PaymentFlowType.evmNetworkSelection,
+ addressDetectionResult: addressDetectionResult,
+ walletType: addressDetectionResult.detectedWalletType,
+ wallets: compatibleWallets ?? [],
+ wallet: wallet,
+ );
+
/// Current wallet is compatible
factory PaymentFlowResult.currentWalletCompatible() =>
PaymentFlowResult._(type: PaymentFlowType.currentWalletCompatible);
/// Single compatible wallet available
factory PaymentFlowResult.singleWallet(
- WalletInfo wallet, AddressDetectionResult addressDetectionResult) =>
+ WalletInfo wallet,
+ AddressDetectionResult addressDetectionResult,
+ ) =>
PaymentFlowResult._(
type: PaymentFlowType.singleWallet,
wallet: wallet,
@@ -126,6 +153,16 @@ class PaymentFlowResult {
/// Incompatible address
factory PaymentFlowResult.incompatible(String message) =>
PaymentFlowResult._(type: PaymentFlowType.incompatible, message: message);
+
+ CryptoCurrency? get detectedCurrency {
+ if (type == PaymentFlowType.evmNetworkSelection) {
+ return addressDetectionResult?.detectedCurrency;
+ }
+ if (walletType != null) {
+ return walletTypeToCryptoCurrency(walletType!);
+ }
+ return null;
+ }
}
enum PaymentFlowType {
@@ -133,6 +170,7 @@ enum PaymentFlowType {
singleWallet,
multipleWallets,
noWallets,
+ evmNetworkSelection,
error,
incompatible,
}
diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
index 259c015..fd6b29b 100644
--- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
@@ -474,26 +474,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
String get qrImage => getQrImage(type);
@computed
- String get monoImage {
- switch (type) {
- case WalletType.ethereum:
- return 'assets/images/eth_chain_mono.svg';
- case WalletType.solana:
- return 'assets/images/sol_chain_mono.svg';
- case WalletType.polygon:
- return 'assets/images/pol_chain_mono.svg';
- case WalletType.tron:
- return 'assets/images/trx_chain_mono.svg';
- case WalletType.zano:
- return 'assets/images/zano_chain_mono.svg';
- case WalletType.base:
- return 'assets/images/base_chain_mono.svg';
- case WalletType.arbitrum:
- return 'assets/images/arbitrum_chain_mono.svg';
- default:
- return 'assets/images/eth_chain_mono.svg';
- }
- }
+ String get monoImage => getChainMonoImage(type);
@computed
bool get isBalanceAvailable => isElectrumWallet;
diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb
index 66867ef..22dec10 100644
--- a/res/values/strings_ar.arb
+++ b/res/values/strings_ar.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "رسوم جديدة مقدرة",
"estimated_receive_amount": "مقدرة المبلغ الاستقبال",
"eta": "إيتا",
+ "ethereum_ecosystem": "النظام البيئي للإيثريوم",
"etherscan_history": "Etherscan تاريخ",
"event": "ﺙﺪﺣ",
"events": "ﺙﺍﺪﺣﻷﺍ",
+ "evm_ecosystem_description": "إذا لزم الأمر، قم بتعديل الشبكة والرمز المميز الذي ترغب في استخدامه لهذه الدفعة.",
"exchange": "تبادل",
"exchange_extra_info": "يرجى عدم نسيان تحديد المذكرة أو معرف إضافي أثناء إرسال هذه المعاملة للتبادل",
"exchange_incorrect_current_wallet_for_xmr": "إذا كنت ترغب في تبديل XMR من رصيد محفظة الكعكة ، فيرجى التبديل إلى محفظة Monero أولاً.",
@@ -792,7 +794,9 @@
"select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ",
"select_hw_account_below": "الرجاء تحديد حساب الاستعادة أدناه:",
"select_manufacturer_title": "حدد الشركة المصنعة",
+ "select_network": "حدد الشبكة",
"select_sell_provider_notice": ".ﻖﻴﺒﻄﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻚﺑ ﺹﺎﺨﻟﺍ ﻲﺿﺍﺮﺘﻓﻻﺍ ﻊﻴﺒﻟﺍ ﺩﻭﺰﻣ ﻦﻴﻴﻌﺗ ﻖﻳﺮﻃ ﻦﻋ ﺔﺷﺎﺸﻟﺍ ﻩﺬﻫ ﻲﻄﺨﺗ",
+ "select_token": "حدد الرمز المميز",
"select_your_country": "الرجاء تحديد بلدك",
"selected_trocador_provider": "مزود تروكادور المختار",
"sell": "بيع",
diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb
index e5af0b2..5ccf69b 100644
--- a/res/values/strings_bg.arb
+++ b/res/values/strings_bg.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Прогнозна нова такса",
"estimated_receive_amount": "Прогнозна сума за получаване",
"eta": "Ета",
+ "ethereum_ecosystem": "Екосистема Ethereum",
"etherscan_history": "История на Etherscan",
"event": "Събитие",
"events": "събития",
+ "evm_ecosystem_description": "Ако е необходимо, променете мрежата и токена, които искате да използвате за това плащане.",
"exchange": "Обмен",
"exchange_extra_info": "Моля, не забравяйте да посочите бележката или допълнителен лична карта, докато изпращате тази транзакция за борсата",
"exchange_incorrect_current_wallet_for_xmr": "Ако искате да смените XMR от вашия баланс на портфейла на тортата Monero, моля, преминете първо към вашия портфейл Monero.",
@@ -792,7 +794,9 @@
"select_destination": "Моля, изберете дестинация за архивния файл.",
"select_hw_account_below": "Моля, изберете кой акаунт да възстановите по -долу:",
"select_manufacturer_title": "Изберете производител",
+ "select_network": "Изберете Мрежа",
"select_sell_provider_notice": "Изберете доставчик на продажба по-горе. Можете да пропуснете този екран, като зададете своя доставчик на продажба по подразбиране в настройките на приложението.",
+ "select_token": "Изберете Token",
"select_your_country": "Моля, изберете вашата страна",
"selected_trocador_provider": "Избран доставчик на трокадор",
"sell": "Продаване",
diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb
index 96c8b97..d349696 100644
--- a/res/values/strings_cs.arb
+++ b/res/values/strings_cs.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Odhadovaný nový poplatek",
"estimated_receive_amount": "Odhadovaná částka přijímání",
"eta": "ETA",
+ "ethereum_ecosystem": "Ekosystém Ethereum",
"etherscan_history": "Historie Etherscanu",
"event": "událost",
"events": "Události",
+ "evm_ecosystem_description": "V případě potřeby upravte síť a token, které chcete pro tuto platbu použít.",
"exchange": "Výměna",
"exchange_extra_info": "Při odesílání této transakce na burzu prosím nezapomeňte zadat poznámku nebo další ID",
"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 @@
"select_destination": "Vyberte cíl pro záložní soubor.",
"select_hw_account_below": "Níže vyberte, který účet chcete obnovit:",
"select_manufacturer_title": "Vyberte výrobce",
+ "select_network": "Vyberte Síť",
"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.",
+ "select_token": "Vyberte Token",
"select_your_country": "Vyberte prosím svou zemi",
"selected_trocador_provider": "Vybraný poskytovatel Trocador",
"sell": "Prodat",
diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb
index cd7efdb..b238504 100644
--- a/res/values/strings_de.arb
+++ b/res/values/strings_de.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Geschätzte neue Gebühr",
"estimated_receive_amount": "Geschätzter Empfangsbetrag",
"eta": "ETA",
+ "ethereum_ecosystem": "Ethereum-Ökosystem",
"etherscan_history": "Etherscan-Historie",
"event": "Ereignis",
"events": "Veranstaltungen",
+ "evm_ecosystem_description": "Ändern Sie bei Bedarf das Netzwerk und den Token, den Sie für diese Zahlung verwenden möchten.",
"exchange": "Tauschen",
"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",
"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 @@
"select_destination": "Bitte wählen Sie das Ziel für die Sicherungsdatei aus.",
"select_hw_account_below": "Bitte wählen Sie unten, welches Konto unten wiederhergestellt werden soll:",
"select_manufacturer_title": "Hersteller auswählen",
+ "select_network": "Wählen Sie Netzwerk",
"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.",
+ "select_token": "Wählen Sie Token aus",
"select_your_country": "Bitte wählen Sie Ihr Land aus",
"selected_trocador_provider": "Ausgewählter Trocador-Anbieter",
"sell": "Verkaufen",
diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb
index 74be3be..f436f41 100644
--- a/res/values/strings_en.arb
+++ b/res/values/strings_en.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Estimated new fee",
"estimated_receive_amount": "Estimated receive amount",
"eta": "ETA",
+ "ethereum_ecosystem": "Ethereum Ecosystem",
"etherscan_history": "Etherscan history",
"event": "Event",
"events": "Events",
+ "evm_ecosystem_description": "If necessary, modify the network and token you would like to use for this payment.",
"exchange": "Exchange",
"exchange_extra_info": "Please don’t forget to specify the Memo or Extra ID while sending this transaction for the exchange",
"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 @@
"select_destination": "Please select destination for the backup file.",
"select_hw_account_below": "Please select which account to restore below:",
"select_manufacturer_title": "Select Manufacturer",
+ "select_network": "Select Network",
"select_sell_provider_notice": "Select a sell provider above. You can skip this screen by setting your default sell provider in app settings.",
+ "select_token": "Select Token",
"select_your_country": "Please select your country",
"selected_trocador_provider": "selected Trocador provider",
"sell": "Sell",
diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb
index 0b16b19..f3fd8c5 100644
--- a/res/values/strings_es.arb
+++ b/res/values/strings_es.arb
@@ -366,8 +366,10 @@
"estimated_receive_amount": "Cantidad estimada a recibir",
"eta": "ETA",
"etherscan_history": "Historial de Etherscan",
+ "ethereum_ecosystem": "Ecosistema Etereum",
"event": "Evento",
"events": "Eventos",
+ "evm_ecosystem_description": "Si es necesario, modifica la red y el token que deseas utilizar para este pago.",
"exchange": "Intercambio",
"exchange_extra_info": "Por favor, no olvide especificar el Memo o el Extra ID al enviar esta transacción al exchange.",
"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 @@
"select_destination": "Selecciona la ubicación del archivo de copia de seguridad.",
"select_hw_account_below": "Selecciona la cuenta que deseas restaurar a continuación:",
"select_manufacturer_title": "Seleccionar Fabricante",
+ "select_network": "Seleccionar red",
"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.",
+ "select_token": "Seleccionar ficha",
"select_your_country": "Por favor, selecciona tu país",
"selected_trocador_provider": "proveedor de Trocador seleccionado",
"sell": "Vender",
diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb
index ff65379..67c62b7 100644
--- a/res/values/strings_fr.arb
+++ b/res/values/strings_fr.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Nouveaux frais estimés",
"estimated_receive_amount": "Montant estimé à recevoir",
"eta": "ETA",
+ "ethereum_ecosystem": "Écosystème Ethereum",
"etherscan_history": "Historique Etherscan",
"event": "Événement",
"events": "Événements",
+ "evm_ecosystem_description": "Si nécessaire, modifiez le réseau et le token que vous souhaitez utiliser pour ce paiement.",
"exchange": "Acheter / Vendre",
"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",
"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 @@
"select_destination": "Veuillez sélectionner la destination du fichier de sauvegarde.",
"select_hw_account_below": "Veuillez sélectionner le compte à restaurer ci-dessous:",
"select_manufacturer_title": "Sélectionner le fabricant",
+ "select_network": "Sélectionnez le réseau",
"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.",
+ "select_token": "Sélectionnez le jeton",
"select_your_country": "Veuillez sélectionner votre pays",
"selected_trocador_provider": "fournisseur de trocador sélectionné",
"sell": "Vendre",
diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb
index d6366aa..5058e46 100644
--- a/res/values/strings_ha.arb
+++ b/res/values/strings_ha.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "An kiyasta sabon kudin",
"estimated_receive_amount": "Kiyasta samun adadin",
"eta": "Tsire-tsire",
+ "ethereum_ecosystem": "Ecetereum ecosysterem",
"etherscan_history": "Etherscan tarihin kowane zamani",
"event": "Lamarin",
"events": "Abubuwan da suka faru",
+ "evm_ecosystem_description": "Idan ya cancanta, gyara cibiyar sadarwa da alama kuna son amfani da wannan biyan.",
"exchange": "Canji",
"exchange_extra_info": "Don Allah kar a manta su saka memba ko karin ID yayin aika wannan ma'amalar don musayar",
"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 @@
"select_destination": "Da fatan za a zaɓi wurin da za a yi wa madadin fayil ɗin.",
"select_hw_account_below": "Da fatan za a zabi wanda asusun zai gyara a ƙasa:",
"select_manufacturer_title": "Select Manaser",
+ "select_network": "Zaɓi Hadawa",
"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.",
+ "select_token": "Zabi JKE",
"select_your_country": "Da fatan za a zabi ƙasarku",
"selected_trocador_provider": "Zabi mai bada TORACAD",
"sell": "sayar",
diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb
index fdecedd..625b8e1 100644
--- a/res/values/strings_hi.arb
+++ b/res/values/strings_hi.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "अनुमानित नया शुल्क",
"estimated_receive_amount": "अनुमानित राशि",
"eta": "ईटा",
+ "ethereum_ecosystem": "एथेरियम पारिस्थितिकी तंत्र",
"etherscan_history": "इथरस्कैन इतिहास",
"event": "आयोजन",
"events": "आयोजन",
+ "evm_ecosystem_description": "यदि आवश्यक हो, तो उस नेटवर्क और टोकन को संशोधित करें जिसका उपयोग आप इस भुगतान के लिए करना चाहते हैं।",
"exchange": "अदला-बदली",
"exchange_extra_info": "कृपया एक्सचेंज के लिए इस लेनदेन को भेजते समय मेमो या अतिरिक्त आईडी निर्दिष्ट करना न भूलें",
"exchange_incorrect_current_wallet_for_xmr": "यदि आप अपने केक वॉलेट मोनेरो बैलेंस से XMR को स्वैप करना चाहते हैं, तो कृपया पहले अपने मोनेरो वॉलेट पर स्विच करें।",
@@ -794,7 +796,9 @@
"select_destination": "कृपया बैकअप फ़ाइल के लिए गंतव्य का चयन करें।",
"select_hw_account_below": "कृपया नीचे पुनर्स्थापित करने के लिए कौन सा खाता चुनें:",
"select_manufacturer_title": "निर्माता का चयन करें",
+ "select_network": "नेटवर्क चुनें",
"select_sell_provider_notice": "ऊपर एक विक्रय प्रदाता का चयन करें। आप ऐप सेटिंग में अपना डिफ़ॉल्ट विक्रय प्रदाता सेट करके इस स्क्रीन को छोड़ सकते हैं।",
+ "select_token": "टोकन चुनें",
"select_your_country": "कृपया अपने देश का चयन करें",
"selected_trocador_provider": "चयनित ट्रोकैडर प्रदाता",
"sell": "बेचना",
diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb
index fe3e387..726e00b 100644
--- a/res/values/strings_hr.arb
+++ b/res/values/strings_hr.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Procijenjena nova naknada",
"estimated_receive_amount": "Procijenjeni iznos primanja",
"eta": "Eta",
+ "ethereum_ecosystem": "Ethereum ekosustav",
"etherscan_history": "Etherscan povijest",
"event": "Događaj",
"events": "Događaji",
+ "evm_ecosystem_description": "Ako je potrebno, promijenite mrežu i token koji želite koristiti za ovo plaćanje.",
"exchange": "Razmjena",
"exchange_extra_info": "Ne zaboravite navesti dopis ili dodatni ID dok šaljete ovu transakciju na razmjenu",
"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 @@
"select_destination": "Odaberite odredište za datoteku sigurnosne kopije.",
"select_hw_account_below": "Molimo odaberite koji će se račun vratiti u nastavku:",
"select_manufacturer_title": "Odaberite proizvođača",
+ "select_network": "Odaberite Mreža",
"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.",
+ "select_token": "Odaberite Token",
"select_your_country": "Odaberite svoju zemlju",
"selected_trocador_provider": "Odabrani pružatelj usluga trokadora",
"sell": "Prodavati",
diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb
index 630efee..b750b1e 100644
--- a/res/values/strings_hy.arb
+++ b/res/values/strings_hy.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Գնահատված նոր միջնորդավճար",
"estimated_receive_amount": "Գնահատված ստացված գումար",
"eta": "Eta",
+ "ethereum_ecosystem": "Ethereum էկոհամակարգ",
"etherscan_history": "Etherscan պատմություն",
"event": "Իրադարձություն",
"events": "Իրադարձություններ",
+ "evm_ecosystem_description": "Անհրաժեշտության դեպքում փոփոխեք ցանցը և նշանը, որը ցանկանում եք օգտագործել այս վճարման համար:",
"exchange": "Փոխանակում",
"exchange_extra_info": "Խնդրում ենք մի մոռացեք նշել հուշագիրը կամ լրացուցիչ ID- ն, այս գործարքը փոխանակման համար ուղարկելիս",
"exchange_incorrect_current_wallet_for_xmr": "Եթե ցանկանում եք փոխանակել XMR ձեր տորթի դրամապանակից Monero Relandal- ից, խնդրում ենք նախ անցնել ձեր Monero դրամապանակին:",
@@ -791,7 +793,9 @@
"select_destination": "Խնդրում ենք ընտրել կրկնօրինակ ֆայլի նպատակակետը",
"select_hw_account_below": "Խնդրում ենք ընտրել, թե որ հաշիվն է վերականգնել հետեւյալը.",
"select_manufacturer_title": "Ընտրեք արտադրող",
+ "select_network": "Ընտրեք Ցանց",
"select_sell_provider_notice": "Ընտրեք վաճառքի մատակարարը վերևում։ Դուք կարող եք բաց թողնել այս էկրանը ձեր լռելայն վաճառքի մատակարարը հավելվածի կարգավորումներում սահմանելով",
+ "select_token": "Ընտրեք նշան",
"select_your_country": "Խնդրում ենք ընտրել ձեր երկիրը",
"sell": "Ծախել",
"sell_alert_content": "Մենք ներկայումս պաշտպանում ենք միայն Bitcoin, Ethereum և Litecoin վաճառքը։ Խնդրում ենք ստեղծել կամ միացնել ձեր Bitcoin, Ethereum կամ Litecoin դրամապանակը",
diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb
index a9f4ad9..12489e8 100644
--- a/res/values/strings_id.arb
+++ b/res/values/strings_id.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Perkiraan biaya baru",
"estimated_receive_amount": "Diperkirakan jumlah menerima",
"eta": "Eta",
+ "ethereum_ecosystem": "Ekosistem Ethereum",
"etherscan_history": "Sejarah Etherscan",
"event": "Peristiwa",
"events": "Acara",
+ "evm_ecosystem_description": "Jika perlu, ubah jaringan dan token yang ingin Anda gunakan untuk pembayaran ini.",
"exchange": "Menukarkan",
"exchange_extra_info": "Harap jangan lupa untuk menentukan memo atau ID tambahan saat mengirim transaksi ini untuk pertukaran",
"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 @@
"select_destination": "Silakan pilih tujuan untuk file cadangan.",
"select_hw_account_below": "Pilih akun mana yang akan dikembalikan di bawah ini:",
"select_manufacturer_title": "Pilih produsen",
+ "select_network": "Pilih Jaringan",
"select_sell_provider_notice": "Pilih penyedia jual di atas. Anda dapat melewati layar ini dengan mengatur penyedia penjualan default Anda di pengaturan aplikasi.",
+ "select_token": "Pilih Token",
"select_your_country": "Pilih negara Anda",
"selected_trocador_provider": "Penyedia Trocador Terpilih",
"sell": "Jual",
diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb
index 55ae9a1..55f676c 100644
--- a/res/values/strings_it.arb
+++ b/res/values/strings_it.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Nuova commissione stimata",
"estimated_receive_amount": "Importo di ricezione stimato",
"eta": "Eta",
+ "ethereum_ecosystem": "Ecosistema Ethereum",
"etherscan_history": "Cronologia Etherscan",
"event": "Evento",
"events": "Eventi",
+ "evm_ecosystem_description": "Se necessario, modifica la rete e il token che desideri utilizzare per questo pagamento.",
"exchange": "Scambio",
"exchange_extra_info": "Non dimenticare di specificare la memo o l'ID extra durante l'invio di questa transazione per lo scambio",
"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 @@
"select_destination": "Seleziona la destinazione per il file di backup.",
"select_hw_account_below": "Seleziona quale account ripristinare:",
"select_manufacturer_title": "Seleziona il produttore",
+ "select_network": "Seleziona Rete",
"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.",
+ "select_token": "Seleziona Gettone",
"select_your_country": "Seleziona il tuo paese",
"selected_trocador_provider": "Provider di Trocador selezionato",
"sell": "Vendi",
diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb
index 108f229..261943d 100644
--- a/res/values/strings_ja.arb
+++ b/res/values/strings_ja.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "推定新しい料金",
"estimated_receive_amount": "推定受信金額",
"eta": "ETA",
+ "ethereum_ecosystem": "イーサリアムエコシステム",
"etherscan_history": "イーサスキャンの歴史",
"event": "イベント",
"events": "イベント",
+ "evm_ecosystem_description": "必要に応じて、この支払いに使用するネットワークとトークンを変更します。",
"exchange": "交換",
"exchange_extra_info": "Exchangeのためにこのトランザクションを送信する際に、メモまたは追加IDを指定することを忘れないでください",
"exchange_incorrect_current_wallet_for_xmr": "XMRをケーキウォレットモネロバランスから交換したい場合は、最初にMoneroウォレットに切り替えてください。",
@@ -793,7 +795,9 @@
"select_destination": "バックアップファイルの保存先を選択してください。",
"select_hw_account_below": "以下に復元するアカウントを選択してください。",
"select_manufacturer_title": "メーカーを選択します",
+ "select_network": "ネットワークの選択",
"select_sell_provider_notice": "上記の販売プロバイダーを選択してください。アプリ設定でデフォルトの販売プロバイダーを設定することで、この画面をスキップできます。",
+ "select_token": "トークンの選択",
"select_your_country": "あなたの国を選択してください",
"selected_trocador_provider": "選択したTrocadorプロバイダー",
"sell": "売る",
diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb
index 0a87c2c..f2f4cd5 100644
--- a/res/values/strings_ko.arb
+++ b/res/values/strings_ko.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "예상 새 수수료",
"estimated_receive_amount": "예상 수령 금액",
"eta": "에타",
+ "ethereum_ecosystem": "이더리움 생태계",
"etherscan_history": "Etherscan 내역",
"event": "이벤트",
"events": "이벤트",
+ "evm_ecosystem_description": "필요한 경우 이 결제에 사용할 네트워크와 토큰을 수정하세요.",
"exchange": "교환",
"exchange_extra_info": "교환을 위해이 거래를 보내는 동안 메모 또는 추가 ID를 지정하는 것을 잊지 마십시오.",
"exchange_incorrect_current_wallet_for_xmr": "Cake Wallet 모네로 잔액에서 XMR을 스왑하려면 먼저 모네로 지갑으로 전환하세요.",
@@ -793,7 +795,9 @@
"select_destination": "백업 파일의 대상을 선택하세요.",
"select_hw_account_below": "아래에서 복구할 계정을 선택하세요:",
"select_manufacturer_title": "제조업체를 선택하십시오",
+ "select_network": "네트워크 선택",
"select_sell_provider_notice": "위에서 판매 제공 업체를 선택하세요. 앱 설정에서 기본 판매 제공 업체를 설정하여 이 화면을 건너뛸 수 있습니다.",
+ "select_token": "토큰 선택",
"select_your_country": "국가를 선택하세요",
"selected_trocador_provider": "선택된 Trocador 제공 업체",
"sell": "판매",
diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb
index 54fef16..39eabdf 100644
--- a/res/values/strings_my.arb
+++ b/res/values/strings_my.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "ခန့်မှန်းသစ်ခန့်မှန်း",
"estimated_receive_amount": "ခန့်မှန်းရရှိသောပမာဏ",
"eta": "eta",
+ "ethereum_ecosystem": "Etheumeum ဂေဟစနစ်",
"etherscan_history": "Etherscan သမိုင်း",
"event": "ပွဲ",
"events": "အဲ့ဒါနဲ့",
+ "evm_ecosystem_description": "လိုအပ်ပါကဤငွေပေးချေမှုအတွက်သင်အသုံးပြုလိုသည့်ကွန်ယက်နှင့်တိုကင်ကိုပြင်ဆင်ပါ။",
"exchange": "လဲလှယ်",
"exchange_extra_info": "ငွေလဲလှယ်မှုအတွက်ဤငွေပေးငွေယူကိုပေးပို့စဉ်မှတ်စုတိုသို့မဟုတ်အပို ID ကိုသတ်မှတ်ရန်မမေ့ပါနှင့်",
"exchange_incorrect_current_wallet_for_xmr": "အကယ်. သင်သည် XMR ကိုသင်၏ကိတ်မုန့် Monero Balance မှ Swap ကိုလဲလှယ်လိုပါကသင်၏ Monero Wallet ကိုပထမဆုံးအကြိမ်ပြောင်းပါ။",
@@ -792,7 +794,9 @@
"select_destination": "အရန်ဖိုင်အတွက် ဦးတည်ရာကို ရွေးပါ။",
"select_hw_account_below": "အောက်ဖော်ပြပါမည်သည့်အကောင့်ကိုရွေးပါ။",
"select_manufacturer_title": "ထုတ်လုပ်သူကိုရွေးချယ်ပါ",
+ "select_network": "ကွန်ယက်ကိုရွေးချယ်ပါ",
"select_sell_provider_notice": "အထက်ဖော်ပြပါ အရောင်းဝန်ဆောင်မှုပေးသူကို ရွေးပါ။ အက်ပ်ဆက်တင်များတွင် သင်၏မူလရောင်းချပေးသူကို သတ်မှတ်ခြင်းဖြင့် ဤစခရင်ကို ကျော်နိုင်သည်။",
+ "select_token": "Token ကိုရွေးချယ်ပါ",
"select_your_country": "ကျေးဇူးပြု. သင့်နိုင်ငံကိုရွေးချယ်ပါ",
"selected_trocador_provider": "ရွေးချယ်ထား Tracador ပံ့ပိုးပေး",
"sell": "ရောင်း",
diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb
index 2f64bfd..dd9ad4e 100644
--- a/res/values/strings_nl.arb
+++ b/res/values/strings_nl.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Geschatte nieuwe vergoeding",
"estimated_receive_amount": "Geschat ontvangen bedrag",
"eta": "ETA",
+ "ethereum_ecosystem": "Ethereum-ecosysteem",
"etherscan_history": "Etherscan-geschiedenis",
"event": "Evenement",
"events": "Evenementen",
+ "evm_ecosystem_description": "Wijzig indien nodig het netwerk en het token dat u voor deze betaling wilt gebruiken.",
"exchange": "Aandelenbeurs",
"exchange_extra_info": "Vergeet niet om de memo of extra ID op te geven tijdens het verzenden van deze transactie voor de uitwisseling",
"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 @@
"select_destination": "Selecteer de bestemming voor het back-upbestand.",
"select_hw_account_below": "Selecteer welk account u hieronder moet herstellen:",
"select_manufacturer_title": "Selecteer fabrikant",
+ "select_network": "Selecteer Netwerk",
"select_sell_provider_notice": "Selecteer hierboven een verkoopaanbieder. U kunt dit scherm overslaan door uw standaardverkoopprovider in te stellen in de app-instellingen.",
+ "select_token": "Selecteer Token",
"select_your_country": "Selecteer uw land",
"selected_trocador_provider": "Geselecteerde Trocador -provider",
"sell": "Verkopen",
diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb
index e9cfe08..5b27af5 100644
--- a/res/values/strings_pl.arb
+++ b/res/values/strings_pl.arb
@@ -366,6 +366,7 @@
"estimated_receive_amount": "Szacowana kwota otrzymania",
"eta": "ETA",
"etherscan_history": "Historia Etherscan",
+ "ethereum_ecosystem": "Ekosystem Ethereum",
"event": "Zdarzenie",
"events": "Zdarzenia",
"exchange": "Wymiana",
@@ -792,7 +793,9 @@
"select_destination": "Wybierz miejsce docelowe dla pliku kopii zapasowej.",
"select_hw_account_below": "Wybierz konto, które chcesz przywrócić poniżej:",
"select_manufacturer_title": "Wybierz producenta",
+ "select_network": "Wybierz opcję Sieć",
"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.",
+ "select_token": "Wybierz Token",
"select_your_country": "Wybierz swój kraj",
"selected_trocador_provider": "wybrany dostawca Trocador",
"sell": "Sprzedaj",
diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb
index b47c65c..f883153 100644
--- a/res/values/strings_pt.arb
+++ b/res/values/strings_pt.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Nova taxa estimada",
"estimated_receive_amount": "Valor estimado de recebimento",
"eta": "ETA",
+ "ethereum_ecosystem": "Ecossistema Ethereum",
"etherscan_history": "história Etherscan",
"event": "Evento",
"events": "Eventos",
+ "evm_ecosystem_description": "Se necessário, modifique a rede e o token que você gostaria de usar para este pagamento.",
"exchange": "Intercâmbio",
"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",
"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 @@
"select_destination": "Selecione o destino para o arquivo de backup.",
"select_hw_account_below": "Selecione qual conta para restaurar abaixo:",
"select_manufacturer_title": "Selecione o fabricante",
+ "select_network": "Selecione Rede",
"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.",
+ "select_token": "Selecione o token",
"select_your_country": "Selecione seu país",
"selected_trocador_provider": "Provedor de Trocador selecionado",
"sell": "Vender",
diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb
index a687193..de31f95 100644
--- a/res/values/strings_ru.arb
+++ b/res/values/strings_ru.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Расчетная новая плата",
"estimated_receive_amount": "Расчетная сумма получения",
"eta": "ETA",
+ "ethereum_ecosystem": "Экосистема Эфириума",
"etherscan_history": "История Эфириума",
"event": "Событие",
"events": "События",
+ "evm_ecosystem_description": "При необходимости измените сеть и токен, который вы хотите использовать для этого платежа.",
"exchange": "Обмен",
"exchange_extra_info": "Пожалуйста, не забудьте указать меморандум или дополнительный идентификатор при отправке этой транзакции для обмена",
"exchange_incorrect_current_wallet_for_xmr": "Если вы хотите поменять XMR с баланса с кошельком для торта Monero, сначала переключитесь на свой кошелек Monero.",
@@ -793,7 +795,9 @@
"select_destination": "Пожалуйста, выберите место для файла резервной копии.",
"select_hw_account_below": "Пожалуйста, выберите, какую учетную запись восстановить ниже:",
"select_manufacturer_title": "Выберите производителя",
+ "select_network": "Выберите сеть",
"select_sell_provider_notice": "Выберите поставщика услуг продажи выше. Вы можете пропустить этот экран, установив поставщика услуг продаж по умолчанию в настройках приложения.",
+ "select_token": "Выберите токен",
"select_your_country": "Пожалуйста, выберите свою страну",
"selected_trocador_provider": "Выбранный провайдер Трокадора",
"sell": "Продавать",
diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb
index 268f30c..6b4fcbd 100644
--- a/res/values/strings_th.arb
+++ b/res/values/strings_th.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "ค่าธรรมเนียมใหม่โดยประมาณ",
"estimated_receive_amount": "โดยประมาณว่าจำนวนเงินที่ได้รับ",
"eta": "กทพ.",
+ "ethereum_ecosystem": "ระบบนิเวศอีเธอเรียม",
"etherscan_history": "ประวัติอีเธอร์สแกน",
"event": "เหตุการณ์",
"events": "กิจกรรม",
+ "evm_ecosystem_description": "หากจำเป็น ให้แก้ไขเครือข่ายและโทเค็นที่คุณต้องการใช้สำหรับการชำระเงินนี้",
"exchange": "แลกเปลี่ยน",
"exchange_extra_info": "โปรดอย่าลืมระบุบันทึกหรือรหัสพิเศษในขณะที่ส่งธุรกรรมนี้สำหรับการแลกเปลี่ยน",
"exchange_incorrect_current_wallet_for_xmr": "หากคุณต้องการสลับ XMR จาก Cake Wallet Monero Balance โปรดเปลี่ยนไปใช้กระเป๋าเงิน Monero ก่อน",
@@ -792,7 +794,9 @@
"select_destination": "โปรดเลือกปลายทางสำหรับไฟล์สำรอง",
"select_hw_account_below": "กรุณาเลือกบัญชีที่จะกู้คืนด้านล่าง:",
"select_manufacturer_title": "เลือกผู้ผลิต",
+ "select_network": "เลือกเครือข่าย",
"select_sell_provider_notice": "เลือกผู้ให้บริการการขายด้านบน คุณสามารถข้ามหน้าจอนี้ได้โดยการตั้งค่าผู้ให้บริการการขายเริ่มต้นในการตั้งค่าแอป",
+ "select_token": "เลือกโทเค็น",
"select_your_country": "กรุณาเลือกประเทศของคุณ",
"selected_trocador_provider": "ผู้ให้บริการ Trocador ที่เลือก",
"sell": "ขาย",
diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb
index b5c6df9..31f530b 100644
--- a/res/values/strings_tl.arb
+++ b/res/values/strings_tl.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Tinatayang bagong fee",
"estimated_receive_amount": "Tinatayang natanggap na halaga",
"eta": "Eta",
+ "ethereum_ecosystem": "Ethereum ecosystem",
"etherscan_history": "Kasaysayan ng Etherscan",
"event": "Kaganapan",
"events": "Mga kaganapan",
+ "evm_ecosystem_description": "Kung kinakailangan, baguhin ang network at token na nais mong gamitin para sa pagbabayad na ito.",
"exchange": "Palitan",
"exchange_extra_info": "Mangyaring huwag kalimutan na tukuyin ang memo o dagdag na ID habang ipinapadala ang transaksyon na ito para sa palitan",
"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 @@
"select_destination": "Mangyaring piliin ang patutunguhan para sa backup na file.",
"select_hw_account_below": "Mangyaring piliin kung aling account ang ibabalik sa ibaba:",
"select_manufacturer_title": "Piliin ang Tagagawa",
+ "select_network": "Piliin ang Network",
"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.",
+ "select_token": "Piliin ang Token",
"select_your_country": "Mangyaring piliin ang iyong bansa",
"selected_trocador_provider": "Napiling Trocador Provider",
"sell": "Ibenta",
diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb
index a00bb77..1236c3c 100644
--- a/res/values/strings_tr.arb
+++ b/res/values/strings_tr.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Tahmini yeni ücret",
"estimated_receive_amount": "Tahmini alma miktarı",
"eta": "ETA",
+ "ethereum_ecosystem": "Ethereum Ekosistemi",
"etherscan_history": "Etherscan geçmişi",
"event": "Etkinlik",
"events": "Olaylar",
+ "evm_ecosystem_description": "Gerekirse bu ödeme için kullanmak istediğiniz ağı ve jetonu değiştirin.",
"exchange": "Değişme",
"exchange_extra_info": "Bu işlemi borsaya gönderirken lütfen notu veya ekstra kimliği belirtmeyi unutmayın.",
"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 @@
"select_destination": "Lütfen yedekleme dosyası için hedef seçin.",
"select_hw_account_below": "Lütfen aşağıda hangi hesabı geri yükleyeceğinizi seçin:",
"select_manufacturer_title": "Üretici seçin",
+ "select_network": "Ağ Seçin",
"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.",
+ "select_token": "Jeton Seç",
"select_your_country": "Lütfen ülkenizi seçin",
"selected_trocador_provider": "Seçilmiş Trocador Sağlayıcı",
"sell": "Satış",
diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb
index 8c6cd57..cabc4a9 100644
--- a/res/values/strings_uk.arb
+++ b/res/values/strings_uk.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "Орієнтовна нова комісія",
"estimated_receive_amount": "Орієнтовна сума отримує",
"eta": "ETA",
+ "ethereum_ecosystem": "Екосистема Ethereum",
"etherscan_history": "Історія Etherscan",
"event": "Подія",
"events": "Події",
+ "evm_ecosystem_description": "Якщо необхідно, змініть мережу та токен, який ви хочете використовувати для цього платежу.",
"exchange": "Обмін",
"exchange_extra_info": "Не забудьте вказати пам’ятку або додатковий ідентифікатор під час надсилання цієї транзакції для обміну",
"exchange_incorrect_current_wallet_for_xmr": "Якщо ви хочете поміняти XMR зі свого балансу для тортів Monero Balance, спочатку перейдіть на свій гаманець Monero.",
@@ -793,7 +795,9 @@
"select_destination": "Виберіть місце призначення для файлу резервної копії.",
"select_hw_account_below": "Виберіть, який рахунок відновити нижче:",
"select_manufacturer_title": "Виберіть виробника",
+ "select_network": "Виберіть Мережа",
"select_sell_provider_notice": "Виберіть вище постачальника послуг продажу. Ви можете пропустити цей екран, встановивши постачальника послуг продажу за умовчанням у налаштуваннях програми.",
+ "select_token": "Виберіть Токен",
"select_your_country": "Будь ласка, виберіть свою країну",
"selected_trocador_provider": "Вибраний постачальник Trocador",
"sell": "Продати",
diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb
index b0e1be2..8f361ae 100644
--- a/res/values/strings_ur.arb
+++ b/res/values/strings_ur.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "تخمینہ شدہ نئی فیس",
"estimated_receive_amount": "تخمینہ وصول کی رقم",
"eta": "ETA",
+ "ethereum_ecosystem": "ایتھریم ماحولیاتی نظام",
"etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ",
"event": "ﺐﯾﺮﻘﺗ",
"events": "ﺕﺎﺒﯾﺮﻘﺗ",
+ "evm_ecosystem_description": "اگر ضروری ہو تو ، نیٹ ورک اور ٹوکن میں ترمیم کریں جس کی آپ اس ادائیگی کے لئے استعمال کرنا چاہتے ہیں۔",
"exchange": "تبادلہ",
"exchange_extra_info": "ایکسچینج کے ل this اس لین دین کو بھیجتے وقت براہ کرم میمو یا اضافی ID کی وضاحت کرنا نہ بھولیں",
"exchange_incorrect_current_wallet_for_xmr": "اگر آپ اپنے کیک پرس مونیرو بیلنس سے XMR تبدیل کرنا چاہتے ہیں تو ، براہ کرم پہلے اپنے مونیرو پرس میں جائیں۔",
@@ -794,7 +796,9 @@
"select_destination": "۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﻝﺰﻨﻣ ﮯﯿﻟ ﮯﮐ ﻞﺋﺎﻓ ﭖﺍ ﮏﯿﺑ ﻡﺮﮐ ﮦﺍﺮﺑ",
"select_hw_account_below": "براہ کرم ذیل میں کون سا اکاؤنٹ بحال کرنا ہے منتخب کریں:",
"select_manufacturer_title": "کارخانہ دار کو منتخب کریں",
+ "select_network": "نیٹ ورک کو منتخب کریں",
"select_sell_provider_notice": "۔ﮟﯿﮨ ﮯﺘﮑﺳ ﮌﻮﮭﭼ ﻮﮐ ﻦﯾﺮﮑﺳﺍ ﺱﺍ ﺮﮐ ﮮﺩ ﺐﯿﺗﺮﺗ ﻮﮐ ﮦﺪﻨﻨﮐ ﻢﮨﺍﺮﻓ ﻞﯿﺳ ﭧﻟﺎﻔﯾﮈ ﮯﻨﭘﺍ ﮟﯿﻣ ﺕﺎﺒ",
+ "select_token": "ٹوکن منتخب کریں",
"select_your_country": "براہ کرم اپنے ملک کو منتخب کریں",
"selected_trocador_provider": "منتخب کردہ ٹروکاڈور فراہم کنندہ",
"sell": "بیچنا",
diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb
index 234c42d..be4d2a4 100644
--- a/res/values/strings_vi.arb
+++ b/res/values/strings_vi.arb
@@ -364,9 +364,11 @@
"estimated_new_fee": "Phí mới ước tính",
"estimated_receive_amount": "Số tiền nhận ước tính",
"eta": "Eta",
+ "ethereum_ecosystem": "Hệ sinh thái Ethereum",
"etherscan_history": "Lịch sử Etherscan",
"event": "Sự kiện",
"events": "Các sự kiện",
+ "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.",
"exchange": "Trao đổi",
"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",
"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 @@
"select_destination": "Vui lòng chọn đích cho tệp sao lưu.",
"select_hw_account_below": "Vui lòng chọn tài khoản nào để khôi phục bên dưới:",
"select_manufacturer_title": "Chọn nhà sản xuất",
+ "select_network": "Chọn mạng",
"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.",
+ "select_token": "Chọn mã thông báo",
"select_your_country": "Vui lòng chọn quốc gia của bạn",
"sell": "Bán",
"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.",
diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb
index 223609b..7f2a603 100644
--- a/res/values/strings_yo.arb
+++ b/res/values/strings_yo.arb
@@ -366,9 +366,11 @@
"estimated_new_fee": "Ifoju tuntun owo tuntun",
"estimated_receive_amount": "Ifoju gba iye",
"eta": "Eta",
+ "ethereum_ecosystem": "Etheum Ecosystem",
"etherscan_history": "Etherscan itan",
"event": "Iṣẹlẹ",
"events": "Awọn iṣẹlẹ",
+ "evm_ecosystem_description": "Ti o ba jẹ dandan, yi nẹtiwọki pada ati ami iwọ yoo fẹ lati lo fun isanwo yii.",
"exchange": "Paarọ",
"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",
"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 @@
"select_destination": "Jọwọ yan ibi ti o nlo fun faili afẹyinti.",
"select_hw_account_below": "Jọwọ yan iru iroyin lati mu pada ni isalẹ:",
"select_manufacturer_title": "Yan olupese",
+ "select_network": "Yan Nẹtiwọọki",
"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.",
+ "select_token": "Yan Token",
"select_your_country": "Jọwọ yan orilẹ-ede rẹ",
"selected_trocador_provider": "olupese trocador ti a yan",
"sell": "Tà",
diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb
index a642c48..cee2aa8 100644
--- a/res/values/strings_zh.arb
+++ b/res/values/strings_zh.arb
@@ -365,9 +365,11 @@
"estimated_new_fee": "估计新费用",
"estimated_receive_amount": "估计接收金额",
"eta": "ETA",
+ "ethereum_ecosystem": "以太坊生态系统",
"etherscan_history": "以太扫描历史",
"event": "事件",
"events": "活动",
+ "evm_ecosystem_description": "如有必要,请修改您要用于此付款的网络和令牌。",
"exchange": "交换",
"exchange_extra_info": "请不要忘记在向交易所发送此交易时指定备忘录或额外的ID",
"exchange_incorrect_current_wallet_for_xmr": "如果您想从蛋糕钱包Monero余额中交换XMR,请先切换到Monero Wallet。",
@@ -792,7 +794,9 @@
"select_destination": "请选择备份文件的目的地。",
"select_hw_account_below": "请在下面选择要还原的帐户:",
"select_manufacturer_title": "选择制造商",
+ "select_network": "选择网络",
"select_sell_provider_notice": "选择上面的销售提供商。您可以通过在应用程序设置中设置默认销售提供商来跳过此屏幕。",
+ "select_token": "选择代币",
"select_your_country": "请选择你的国家",
"selected_trocador_provider": "选定的trocador提供商",
"sell": "卖",
diff --git a/test/core/universal_address_detector_test.dart b/test/core/universal_address_detector_test.dart
index 992fecb..9ea5901 100644
--- a/test/core/universal_address_detector_test.dart
+++ b/test/core/universal_address_detector_test.dart
@@ -46,6 +46,36 @@ void main() {
expect(result.address, '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa');
expect(result.amount, '0.001');
});
+
+ test('detects Lightning invoice', () {
+ const invoice = 'lnbc1u1p3zj7j8pp5vkwxc4vxfzgzxcjzk5ksp0l7jgrgzmqxqmexxp8e8gfqk6s0knnqdlc35hsggzypnxw7xxxnrvd3hscqzpgxqrrsssp5txs5g8vkdmdtnm8g5v9m3u094nnxkl0jjf38jj5xn2pptxlqkwn7gpgfkt7q6z0frcj7vjy4mv7659hccln2e0gzt4ufnsukxcnknh7e58kwnr8pk6kgv6vrghcpld2z5n';
+ final result = UniversalAddressDetector.detectAddress(invoice);
+
+ expect(result.isValid, true);
+ expect(result.detectedCurrency, CryptoCurrency.btcln);
+ expect(result.detectedWalletType, WalletType.bitcoin);
+ expect(result.address, invoice);
+ });
+
+ test('detects Lightning address', () {
+ const address = 'alice@lightning.com';
+ final result = UniversalAddressDetector.detectAddress(address);
+
+ expect(result.isValid, true);
+ expect(result.detectedCurrency, CryptoCurrency.btcln);
+ expect(result.detectedWalletType, WalletType.bitcoin);
+ expect(result.address, address);
+ });
+
+ test('detects LNURL format', () {
+ const address = 'LNURL1DP68GURN8GHJ7UM9WFMXCCRVFH8RHYEE5QDKURZQDF8JUK6RKCE0E';
+ final result = UniversalAddressDetector.detectAddress(address);
+
+ expect(result.isValid, true);
+ expect(result.detectedCurrency, CryptoCurrency.btcln);
+ expect(result.detectedWalletType, WalletType.bitcoin);
+ expect(result.address, address);
+ });
});
group('Monero Address Detection', () {
Why this scored 34/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.