diff --git a/lib/cake_pay/src/cards/cake_pay_buy_card_page.dart b/lib/cake_pay/src/cards/cake_pay_buy_card_page.dart
index 5442dcff1..8380897b9 100644
--- a/lib/cake_pay/src/cards/cake_pay_buy_card_page.dart
+++ b/lib/cake_pay/src/cards/cake_pay_buy_card_page.dart
@@ -11,8 +11,8 @@ import 'package:cake_wallet/cake_pay/src/widgets/link_extractor.dart';
import 'package:cake_wallet/cake_pay/src/widgets/rounded_overlay_cards_widget.dart';
import 'package:cake_wallet/cake_pay/src/widgets/text_icon_button.dart';
import 'package:cake_wallet/cake_pay/src/widgets/three_checkbox_alert_content_widget.dart';
+import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
import 'package:cake_wallet/core/execution_state.dart';
-import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
import 'package:cake_wallet/routes.dart';
@@ -584,8 +584,11 @@ class CakePayBuyCardPage extends BasePage {
final displayingOutputs = _sendViewModel.outputs
.map((o) => o.OutputCopyWithParsedAddress(
parsedAddress: ParsedAddress(
- addresses: [o.address],
- name: 'Cake Pay',
+ parsedAddressByCurrencyMap: {
+ cakePayBuyCardViewModel.sendViewModel.selectedCryptoCurrency:
+ o.address,
+ },
+ handle: 'Cake Pay',
profileName: order?.cards.first.cardName ?? 'Cake Pay',
profileImageUrl: order?.cards.first.cardImagePath ?? '',
),
@@ -653,8 +656,11 @@ class CakePayBuyCardPage extends BasePage {
final displayingOutputs = outputsCopy
.map((o) => o.OutputCopyWithParsedAddress(
parsedAddress: ParsedAddress(
- addresses: [o.address],
- name: 'Cake Pay',
+ parsedAddressByCurrencyMap: {
+ cakePayBuyCardViewModel.sendViewModel.selectedCryptoCurrency:
+ o.address,
+ },
+ handle: 'Cake Pay',
profileName: order?.cards.first.cardName ?? 'Cake Pay',
profileImageUrl: order?.cards.first.cardImagePath ?? '',
),
diff --git a/lib/core/address_resolver/address_lookup_provider.dart b/lib/core/address_resolver/address_lookup_provider.dart
new file mode 100644
index 000000000..9feff5610
--- /dev/null
+++ b/lib/core/address_resolver/address_lookup_provider.dart
@@ -0,0 +1,22 @@
+import 'package:cake_wallet/core/address_resolver/address_sources.dart';
+import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
+import 'package:cake_wallet/store/settings_store.dart';
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/wallet_base.dart';
+
+abstract class AddressLookupProvider {
+
+ AddressSource get source;
+
+ List get supportedCurrencies;
+
+ bool isEnabled(SettingsStore settingsStore);
+
+ bool canHandle(String query);
+
+ Future> resolve({
+ required String query,
+ required List currencies,
+ required WalletBase wallet,
+ });
+}
diff --git a/lib/core/address_resolver/address_resolver_service.dart b/lib/core/address_resolver/address_resolver_service.dart
new file mode 100644
index 000000000..8592c53c6
--- /dev/null
+++ b/lib/core/address_resolver/address_resolver_service.dart
@@ -0,0 +1,143 @@
+import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
+import 'package:cake_wallet/core/address_resolver/address_sources.dart';
+import 'package:cake_wallet/core/address_resolver/bip_353/bip_353_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/ens/ens_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/fio/fio_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/lnurl_pay/lnurl_pay_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/mastodon/mastodon_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/nostr/nostr_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/openalias/openalias_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
+import 'package:cake_wallet/core/address_resolver/thorchain/thorchain_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/twitter/twitter_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/unstoppable/unstoppable_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/wellknown/wellknown_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/yat/yat_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/zano/zano_alias_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/zcash/zcash_address_provider.dart';
+import 'package:cake_wallet/core/address_resolver/zcash/zcash_me_address_provider.dart';
+import 'package:cake_wallet/store/settings_store.dart';
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/utils/print_verbose.dart';
+import 'package:cw_core/wallet_base.dart';
+
+class AddressResolverService {
+ AddressResolverService({required this.settingsStore});
+
+ final SettingsStore settingsStore;
+
+// Sources available for address resolution and lookup settings.
+ static const supportedSources = [
+ AddressSource.lnurlPay,
+ AddressSource.twitter,
+ AddressSource.mastodon,
+ AddressSource.yatRecord,
+ AddressSource.unstoppableDomains,
+ AddressSource.openAlias,
+ AddressSource.ens,
+ AddressSource.zcashName,
+ AddressSource.zcashAddress,
+ AddressSource.wellKnown,
+ AddressSource.zanoAlias,
+ AddressSource.bip353,
+ AddressSource.fio,
+ AddressSource.thorChain,
+ AddressSource.nostr,
+ ];
+
+ List _buildProviders() {
+ final allProviders = [
+ LNUrlPayAddressProvider(),
+ TwitterAddressProvider(),
+ MastodonAddressProvider(),
+ YatAddressProvider(),
+ UnstoppableAddressProvider(),
+ OpenaliasAddressProvider(),
+ EnsAddressProvider(),
+ ZcashNameAddressProvider(),
+ ZcashMeAddressProvider(),
+ WellKnownAddressProvider(),
+ ZanoAliasAddressProvider(),
+ Bip353AddressProvider(),
+ FioAddressProvider(),
+ ThorchainAddressProvider(),
+ NostrAddressProvider(),
+ ];
+
+ return allProviders.where((provider) => supportedSources.contains(provider.source)).toList();
+ }
+
+ Future> resolve({
+ required String query,
+ required WalletBase wallet,
+ required CryptoCurrency currency,
+ }) async {
+ try {
+ final providers = _buildProviders();
+ // return first entry if currency is specified and provider supports it
+ if (currency != null) {
+ for (final provider in providers) {
+ final cur = currency.title;
+ final src = provider.source.label;
+
+ printV('[address resolver service] checking $src - $cur - $query');
+ if (!provider.isEnabled(settingsStore)) {
+ printV('[address resolver service] ...skipping $src (disabled)');
+ continue;
+ }
+ if (!provider.canHandle(query)) {
+ printV('[address resolver service] ...skipping $src (cannot handle)');
+ continue;
+ }
+ if (!provider.supportedCurrencies.contains(currency)) {
+ printV('[address resolver service] ...skipping $src (unsupported currency)');
+ continue;
+ }
+
+ try {
+ printV('[address resolver service] ...resolving $src - $cur - $query');
+ final results = await provider.resolve(
+ query: query,
+ currencies: [currency],
+ wallet: wallet,
+ );
+
+ if (results.isNotEmpty) {
+ final parsedAddress = results.first.parsedAddressByCurrencyMap[currency];
+ final parsedAddressStr = parsedAddress == null || parsedAddress.isEmpty ? 'N/A' : parsedAddress;
+ printV('[address resolver service] resolved $src - $cur - $parsedAddressStr');
+ return [results.first];
+ }
+ } catch (e) {
+ printV('[address resolver service] Error resolving $src: $e');
+ }
+ }
+
+ return [];
+ }
+
+ return []; // TODO: Implement multi-currency resolution if no specific currency is provided.
+
+ final tasks = >>[];
+
+ for (final provider in providers) {
+ if (!provider.isEnabled(settingsStore)) continue;
+ if (!provider.canHandle(query)) continue;
+
+ tasks.add(
+ provider.resolve(
+ query: query,
+ currencies: provider.supportedCurrencies,
+ wallet: wallet,
+ ),
+ );
+ }
+
+ final results = await Future.wait(tasks);
+ return results.expand((items) => items).toList();
+ } catch (e) {
+ printV('Error resolving address: $e');
+ return [];
+ }
+ }
+}
diff --git a/lib/core/address_resolver/address_resolver_utils.dart b/lib/core/address_resolver/address_resolver_utils.dart
new file mode 100644
index 000000000..f9f59240c
--- /dev/null
+++ b/lib/core/address_resolver/address_resolver_utils.dart
@@ -0,0 +1,104 @@
+import 'package:cake_wallet/core/address_resolver/unstoppable/unstoppable_address_provider.dart';
+import 'package:cake_wallet/core/address_validator.dart';
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/utils/print_verbose.dart';
+
+class AddressResolverUtils {
+ static Future
', caseSensitive: false), '\n')
+ .replaceAll(RegExp(r'<[^>]*>'), ' ')
+ .trim();
+ }
+}
diff --git a/lib/core/address_resolver/address_sources.dart b/lib/core/address_resolver/address_sources.dart
new file mode 100644
index 000000000..729ef478a
--- /dev/null
+++ b/lib/core/address_resolver/address_sources.dart
@@ -0,0 +1,87 @@
+import 'package:cake_wallet/core/address_validator.dart';
+import 'package:cw_core/crypto_currency.dart';
+
+enum AddressSource {
+ twitter(label: 'X', iconPath: 'assets/new-ui/address_sources/x.svg', alias: '@username'),
+ unstoppableDomains(
+ label: 'Unstoppable Domains',
+ iconPath: 'assets/new-ui/address_sources/unstoppable.svg',
+ alias: 'domain.tld'),
+ openAlias(
+ label: 'OpenAlias',
+ iconPath: 'assets/new-ui/address_sources/openalias.svg',
+ alias: 'name.domain.tld'),
+ yatRecord(label: 'Yat', iconPath: 'assets/new-ui/address_sources/yat.svg', alias: '🎂🎂🎂'),
+ fio(label: 'FIO', iconPath: 'assets/new-ui/address_sources/fio.svg', alias: 'user@domain'),
+ ens(
+ label: 'Ethereum Name Service',
+ iconPath: 'assets/new-ui/address_sources/ens.svg',
+ alias: 'domain.eth'),
+ mastodon(
+ label: 'Mastodon',
+ iconPath: 'assets/new-ui/address_sources/mastodon.svg',
+ alias: '@username@domain.tld'),
+ nostr(
+ label: 'Nostr',
+ iconPath: 'assets/new-ui/address_sources/nostr.svg',
+ alias: 'user@domain.tld'),
+ thorChain(
+ label: 'ThorChain',
+ iconPath: 'assets/new-ui/address_sources/icon-thorchain.svg',
+ alias: 'name'),
+ wellKnown(
+ label: '.wellknown',
+ iconPath: 'assets/new-ui/address_sources/wellknown.svg',
+ alias: 'domain.tld'),
+ zanoAlias(
+ label: 'Zano Alias', iconPath: 'assets/new-ui/address_sources/zano.svg', alias: '@alias'),
+ bip353(
+ label: 'BIP353',
+ iconPath: 'assets/new-ui/address_sources/bip353.svg',
+ alias: 'user@domain.com'),
+ zcashAddress(
+ label: 'Zcash.me',
+ iconPath: 'assets/new-ui/address_sources/icon-zcash-me.svg',
+ alias: 'zcash.me/username'),
+ zcashName(
+ label: 'Zcash Names',
+ iconPath: 'assets/new-ui/address_sources/icon-zcashnames.svg',
+ alias: 'name.zec'),
+ lnurlPay(
+ label: 'LNURL Pay',
+ iconPath: 'assets/new-ui/address_sources/icon-lnurl-pay.svg',
+ alias: 'user@domain.com'),
+ contact(label: 'Contact', iconPath: ''),
+ notParsed(label: 'Unknown', iconPath: ''),
+ ;
+
+ const AddressSource({
+ required this.label,
+ required this.iconPath,
+ this.alias = '',
+ });
+
+ final String label;
+ final String iconPath;
+ final String alias;
+}
+
+extension AddressSourceIndex on AddressSource {
+ int get raw => index;
+
+ static AddressSource fromRaw(int raw) =>
+ AddressSource.values[raw.clamp(0, AddressSource.values.length - 1)];
+}
+
+extension AddressSourceNameParser on AddressSource {
+ static AddressSource fromLabel(String? text) {
+ if (text == null || text.trim().isEmpty) {
+ return AddressSource.notParsed;
+ }
+ final needle = text.trim().toLowerCase();
+ return AddressSource.values.firstWhere(
+ (src) => src.label.toLowerCase() == needle,
+ orElse: () => AddressSource.notParsed,
+ );
+ }
+}
diff --git a/lib/core/address_resolver/bip_353/bip_353_address_provider.dart b/lib/core/address_resolver/bip_353/bip_353_address_provider.dart
new file mode 100644
index 000000000..3a46f87a4
--- /dev/null
+++ b/lib/core/address_resolver/bip_353/bip_353_address_provider.dart
@@ -0,0 +1,68 @@
+import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
+import 'package:cake_wallet/core/address_resolver/address_sources.dart';
+import 'package:cake_wallet/core/address_resolver/bip_353/bip_353_record.dart';
+import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
+import 'package:cake_wallet/store/settings_store.dart';
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/utils/print_verbose.dart';
+import 'package:cw_core/wallet_base.dart';
+
+class Bip353AddressProvider extends AddressLookupProvider {
+ @override
+ AddressSource get source => AddressSource.bip353;
+
+ @override
+ List get supportedCurrencies => [CryptoCurrency.btc];
+
+ @override
+ bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsBip353;
+
+ @override
+ bool canHandle(String query) {
+ final value = query.trim();
+ return value.contains('@') && value.contains('.') && !value.startsWith('@');
+ }
+
+ @override
+ Future> resolve({
+ required String query,
+ required List currencies,
+ required WalletBase wallet,
+ }) async {
+ try {
+ final result = {};
+ String? dnsProof;
+
+ for (final cur in currencies) {
+ final bip353AddressMap = await Bip353Record.fetchUriByCryptoCurrency(query, cur.title);
+ if (bip353AddressMap == null || bip353AddressMap.isEmpty) continue;
+
+ final spAddress = bip353AddressMap['sp'];
+ final address = bip353AddressMap['address'];
+ final chosenAddress = spAddress?.isNotEmpty == true ? spAddress : address;
+
+ if (chosenAddress != null && chosenAddress.isNotEmpty) {
+ result[cur] = chosenAddress;
+ }
+ }
+
+ if (result.isEmpty) return [];
+
+ try {
+ dnsProof = await Bip353Record.fetchDnsProof(query);
+ } catch (e) {
+ printV('Bip353Record.fetchDnsProof error: $e');
+ }
+
+ return [ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ addressSource: AddressSource.bip353,
+ handle: query,
+ bip353DnsProof: dnsProof ?? '',
+ )];
+ } catch (e) {
+ printV('[address resolver] Error resolving BIP353 address: $e');
+ return [];
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/entities/bip_353_record.dart b/lib/core/address_resolver/bip_353/bip_353_record.dart
similarity index 59%
rename from lib/entities/bip_353_record.dart
rename to lib/core/address_resolver/bip_353/bip_353_record.dart
index 9a2a50e0a..257d76737 100644
--- a/lib/entities/bip_353_record.dart
+++ b/lib/core/address_resolver/bip_353/bip_353_record.dart
@@ -2,13 +2,9 @@ import 'dart:convert';
import 'dart:isolate';
import 'package:basic_utils/basic_utils.dart';
-import 'package:cake_wallet/generated/i18n.dart';
-import 'package:cake_wallet/src/widgets/alert_with_picker_option.dart';
-import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:dnssec_proof/dnssec_proof.dart';
-import 'package:flutter/material.dart';
class Bip353Record {
Bip353Record({
@@ -104,69 +100,4 @@ class Bip353Record {
}
}
- static Future pickBip353AddressChoice(
- BuildContext context,
- String bip353Name,
- Map addressMap,
- ) async {
- if (addressMap.length == 1) {
- return addressMap.values.first;
- }
-
- final chosenAddress = await _showAddressChoiceDialog(context, bip353Name, addressMap);
-
- return chosenAddress;
- }
-
- static Future _showAddressChoiceDialog(
- BuildContext context,
- String bip353Name,
- Map addressMap,
- ) async {
- final entriesList = addressMap.entries.toList();
- final List