diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 4ae3aca19..0857c0072 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -284,6 +284,7 @@ class CryptoCurrency extends EnumerableItem with Serializable 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 70a61d12f..5ef50ee78 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 d7f15d25c..64d96677a 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 129e7d313..41a4f5948 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 accac022c..b0d4f767e 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 with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin _handleChangeWallet( paymentViewModel, @@ -211,10 +220,43 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin _showEVMPaymentFlow( + PaymentViewModel paymentViewModel, + WalletSwitcherViewModel walletSwitcherViewModel, + PaymentRequest paymentRequest, + ) async { + await showModalBottomSheet( + 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 _handleSelectWallet( PaymentViewModel paymentViewModel, WalletSwitcherViewModel walletSwitcherViewModel, PaymentRequest paymentRequest, + PaymentFlowResult result, ) async { Navigator.of(context).pop(); @@ -233,6 +275,9 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin createState() => _EVMPaymentFlowContentState(); +} + +class _EVMPaymentFlowContentState extends State<_EVMPaymentFlowContent> { + WalletType? selectedNetwork; + CryptoCurrency? selectedToken; + + @override + void initState() { + super.initState(); + selectedNetwork = WalletType.ethereum; + _autoSelectToken(); + } + + Future _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( + 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( + context: context, + builder: (BuildContext context) { + return CurrencyPicker( + selectedAtIndex: selectedIndex >= 0 ? selectedIndex : 0, + items: availableTokens.cast(), + hintText: S.current.add_token, + onItemSelected: (Currency currency) { + setState(() { + selectedToken = currency as CryptoCurrency; + }); + }, + ); + }, + ); + } catch (e) { + printV('Error showing token selection: $e'); + } + } + + Future _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 56f6a5793..718505679 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 afb1b5d20..8bcf71788 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 { @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 { 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 { } }); - 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 79e6cff68..c09d4101b 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 572645749..352f3441e 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -18,7 +18,9 @@ class Picker extends StatefulWidget { required this.onItemSelected, this.title, this.displayItem, - this.images = const [], + this.displayItemColor, + this.displayItemFontSize, + this.images = const [], this.description, this.mainAxisAlignment = MainAxisAlignment.start, this.isGridView = false, @@ -34,6 +36,7 @@ class Picker 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 extends StatefulWidget { final int selectedAtIndex; final List items; - final List images; + final List 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 extends StatefulWidget { final Function(double)? onSliderChanged; final bool Function(Item, String)? matchingCriteria; final double? maxValue; - + final bool hasTitleSpacing; @override _PickerState createState() => _PickerState(items, images, onItemSelected); } @@ -74,9 +79,9 @@ class _PickerState extends State> { final Function(Item) onItemSelected; List items; - List images; + List images; List filteredItems = []; - List filteredImages = []; + List filteredImages = []; final TextEditingController searchController = TextEditingController(); ScrollController controller = ScrollController(); @@ -163,6 +168,7 @@ class _PickerState extends State> { ), ), ), + widget.hasTitleSpacing ? const SizedBox(height: 24) : const SizedBox.shrink(), Padding( padding: EdgeInsets.symmetric(horizontal: padding), child: Container( @@ -342,6 +348,7 @@ class _PickerState extends State> { 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 3a9981bd6..74776c8f7 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 184eb56c1..13c868bff 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> getAvailableTokensForNetwork( + WalletType network, + ) async { + final baseCurrency = walletTypeToCryptoCurrency(network); + final allTokens = []; + final addedAddresses = {}; + + 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 existingTokens, + CryptoCurrency token, + Set 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> _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 5676a6b92..9e8bf3136 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> 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? 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 259c0158f..fd6b29bdd 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 66867ef92..22dec1012 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 e5af0b224..5ccf69bd9 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 96c8b9724..d34969614 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 cd7efdb97..b23850445 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 74be3be65..f436f41ac 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 0b16b19cd..f3fd8c57d 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 ff6537951..67c62b7a4 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 d6366aa2a..5058e465a 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 fdecedd65..625b8e1b5 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 fe3e387f6..726e00bad 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 630efeeaa..b750b1e61 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 a9f4ad929..12489e80a 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 55ae9a10e..55f676cf3 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 108f229ca..261943d25 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 0a87c2c4d..f2f4cd597 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 54fef16c5..39eabdfbc 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 2f64bfde7..dd9ad4e3c 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 e9cfe0810..5b27af510 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 b47c65cff..f8831534e 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 a6871939d..de31f95f4 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 268f30c71..6b4fcbd55 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 b5c6df98d..31f530b52 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 a00bb7709..1236c3c83 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 8c6cd57b2..cabc4a937 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 b0e1be268..8f361ae6a 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 234c42dc8..be4d2a401 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 223609be1..7f2a60347 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 a642c48a7..cee2aa801 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 992fecbb1..9ea590127 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', () {