diff --git a/assets/images/add_contact_coins_img.png b/assets/images/add_contact_coins_img.png
new file mode 100644
index 0000000000..32fc4556fb
Binary files /dev/null and b/assets/images/add_contact_coins_img.png differ
diff --git a/assets/images/address_providers/bip353.svg b/assets/images/address_providers/bip353.svg
new file mode 100644
index 0000000000..c8aea38119
--- /dev/null
+++ b/assets/images/address_providers/bip353.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/address_providers/ens.svg b/assets/images/address_providers/ens.svg
new file mode 100644
index 0000000000..6a4e0e4870
--- /dev/null
+++ b/assets/images/address_providers/ens.svg
@@ -0,0 +1,14 @@
+
diff --git a/assets/images/address_providers/fio.svg b/assets/images/address_providers/fio.svg
new file mode 100644
index 0000000000..d657995d5f
--- /dev/null
+++ b/assets/images/address_providers/fio.svg
@@ -0,0 +1,20 @@
+
diff --git a/assets/images/address_providers/mastodon.svg b/assets/images/address_providers/mastodon.svg
new file mode 100644
index 0000000000..5d3c0a3a7c
--- /dev/null
+++ b/assets/images/address_providers/mastodon.svg
@@ -0,0 +1,11 @@
+
diff --git a/assets/images/address_providers/nostr.svg b/assets/images/address_providers/nostr.svg
new file mode 100644
index 0000000000..0c200a25e7
--- /dev/null
+++ b/assets/images/address_providers/nostr.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/address_providers/openalias.svg b/assets/images/address_providers/openalias.svg
new file mode 100644
index 0000000000..d119b93865
--- /dev/null
+++ b/assets/images/address_providers/openalias.svg
@@ -0,0 +1,13 @@
+
diff --git a/assets/images/address_providers/thorchain.svg b/assets/images/address_providers/thorchain.svg
new file mode 100644
index 0000000000..9db98af5d7
--- /dev/null
+++ b/assets/images/address_providers/thorchain.svg
@@ -0,0 +1,15 @@
+
diff --git a/assets/images/address_providers/unstoppable.svg b/assets/images/address_providers/unstoppable.svg
new file mode 100644
index 0000000000..4fd89b2e2d
--- /dev/null
+++ b/assets/images/address_providers/unstoppable.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/address_providers/wellknown.svg b/assets/images/address_providers/wellknown.svg
new file mode 100644
index 0000000000..dd993a3754
--- /dev/null
+++ b/assets/images/address_providers/wellknown.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/address_providers/x.svg b/assets/images/address_providers/x.svg
new file mode 100644
index 0000000000..5c3a58d96d
--- /dev/null
+++ b/assets/images/address_providers/x.svg
@@ -0,0 +1,11 @@
+
diff --git a/assets/images/address_providers/yat.svg b/assets/images/address_providers/yat.svg
new file mode 100644
index 0000000000..2cc039a473
--- /dev/null
+++ b/assets/images/address_providers/yat.svg
@@ -0,0 +1,11 @@
+
diff --git a/assets/images/address_providers/zano.svg b/assets/images/address_providers/zano.svg
new file mode 100644
index 0000000000..5a24e38e64
--- /dev/null
+++ b/assets/images/address_providers/zano.svg
@@ -0,0 +1,37 @@
+
diff --git a/assets/images/trash_can_icon.png b/assets/images/trash_can_icon.png
new file mode 100644
index 0000000000..077f0650db
Binary files /dev/null and b/assets/images/trash_can_icon.png differ
diff --git a/lib/address_resolver/address_resolver_service.dart b/lib/address_resolver/address_resolver_service.dart
new file mode 100644
index 0000000000..87d194a663
--- /dev/null
+++ b/lib/address_resolver/address_resolver_service.dart
@@ -0,0 +1,716 @@
+import 'package:cake_wallet/core/address_validator.dart';
+import 'package:cake_wallet/core/yat_service.dart';
+import 'package:cake_wallet/entities/emoji_string_extension.dart';
+import 'package:cake_wallet/entities/ens_record.dart';
+import 'package:cake_wallet/entities/fio_address_provider.dart';
+import 'package:cake_wallet/entities/openalias_record.dart';
+import 'package:cake_wallet/address_resolver/parsed_address.dart';
+import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
+import 'package:cake_wallet/entities/wellknown_record.dart';
+import 'package:cake_wallet/entities/zano_alias.dart';
+import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
+import 'package:cake_wallet/mastodon/mastodon_api.dart';
+import 'package:cake_wallet/nostr/nostr_api.dart';
+import 'package:cake_wallet/store/settings_store.dart';
+import 'package:cake_wallet/twitter/twitter_api.dart';
+import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/utils/print_verbose.dart';
+import 'package:cw_core/wallet_base.dart';
+
+import '../entities/bip_353_record.dart';
+
+class AddressResolverService {
+ AddressResolverService({required this.yatService, required this.settingsStore}) {
+ _buildLookupTable();
+ }
+
+ final YatService yatService;
+ final SettingsStore settingsStore;
+
+ static const unstoppableDomains = [
+ "888",
+ "academy",
+ "agency",
+ "altimist",
+ "anime",
+ "austin",
+ "bald",
+ "bay",
+ "benji",
+ "bet",
+ "binanceus",
+ "bitcoin",
+ "bitget",
+ "bitscrunch",
+ "blockchain",
+ "boomer",
+ "boston",
+ "ca",
+ "caw",
+ "cc",
+ "chat",
+ "chomp",
+ "clay",
+ "club",
+ "co",
+ "com",
+ "company",
+ "crypto",
+ "dao",
+ "design",
+ "dfz",
+ "digital",
+ "doga",
+ "donut",
+ "dream",
+ "email",
+ "emir",
+ "eth",
+ "ethermail",
+ "family",
+ "farms",
+ "finance",
+ "fun",
+ "fyi",
+ "games",
+ "global",
+ "go",
+ "group",
+ "guru",
+ "hi",
+ "hockey",
+ "host",
+ "info",
+ "io",
+ "klever",
+ "kresus",
+ "kryptic",
+ "lfg",
+ "life",
+ "live",
+ "llc",
+ "ltc",
+ "ltd",
+ "manga",
+ "me",
+ "media",
+ "metropolis",
+ "miami",
+ "miku",
+ "money",
+ "moon",
+ "mumu",
+ "net",
+ "network",
+ "news",
+ "nft",
+ "npc",
+ "onchain",
+ "online",
+ "org",
+ "podcast",
+ "pog",
+ "polygon",
+ "press",
+ "privacy",
+ "pro",
+ "propykeys",
+ "pudgy",
+ "pw",
+ "quantum",
+ "rad",
+ "raiin",
+ "retardio",
+ "rip",
+ "rocks",
+ "secret",
+ "services",
+ "site",
+ "smobler",
+ "social",
+ "solutions",
+ "space",
+ "stepn",
+ "store",
+ "studio",
+ "systems",
+ "tball",
+ "tea",
+ "team",
+ "tech",
+ "technology",
+ "today",
+ "tribe",
+ "u",
+ "ubu",
+ "uno",
+ "unstoppable",
+ "vip",
+ "wallet",
+ "website",
+ "wif",
+ "wifi",
+ "witg",
+ "work",
+ "world",
+ "wrkx",
+ "wtf",
+ "x",
+ "xmr",
+ "xyz",
+ "zil",
+ "zone"
+ ];
+
+ late final List _lookupTable;
+
+ void _buildLookupTable() {
+ _lookupTable = [
+ LookupEntry(
+ source: AddressSource.twitter,
+ currencies: AddressSource.twitter.supportedCurrencies,
+ applies: (q) => settingsStore.lookupsTwitter && q.startsWith('@'),
+ // x handle example: @username
+ run: _lookupTwitter,
+ ),
+ LookupEntry(
+ source: AddressSource.zanoAlias,
+ currencies: AddressSource.zanoAlias.supportedCurrencies,
+ applies: (q) => settingsStore.lookupsZanoAlias && q.startsWith('@'),
+ // zano handle example: @username
+ run: _lookupZano,
+ ),
+ LookupEntry(
+ source: AddressSource.mastodon,
+ currencies: AddressSource.mastodon.supportedCurrencies,
+ applies: (q) =>
+ settingsStore.lookupsMastodon &&
+ q.startsWith('@') &&
+ q.contains('@', 1) &&
+ q.contains('.', 1),
+ // Mastodon handle example: @username@hostname.xxx
+ run: _lookupMastodon,
+ ),
+ LookupEntry(
+ source: AddressSource.wellKnown,
+ currencies: AddressSource.wellKnown.supportedCurrencies,
+ applies: (q) => settingsStore.lookupsWellKnown && q.contains('.') && q.contains('@'),
+ // .well-known handle example:
+ run: _lookupWellKnown,
+ ),
+ LookupEntry(
+ source: AddressSource.fio,
+ currencies: AddressSource.fio.supportedCurrencies,
+ applies: (q) =>
+ settingsStore.lookupsFio && !q.startsWith('@') && q.contains('@') && !q.contains('.'),
+ // FIO handle example: username@domain
+ run: _lookupFio,
+ ),
+ LookupEntry(
+ source: AddressSource.yatRecord,
+ currencies: AddressSource.yatRecord.supportedCurrencies,
+ applies: (q) => settingsStore.lookupsYatService && q.hasOnlyEmojis,
+ // Yat handle example: 🐶🐾
+ run: _lookupYatService,
+ ),
+ LookupEntry(
+ source: AddressSource.thorChain,
+ currencies: AddressSource.thorChain.supportedCurrencies,
+ applies: (q) => settingsStore.lookupsThorChain && q.isNotEmpty,
+ run: _lookupThorChain,
+ ),
+ LookupEntry(
+ source: AddressSource.unstoppableDomains,
+ currencies: AddressSource.unstoppableDomains.supportedCurrencies,
+ applies: (q) {
+ if (!settingsStore.lookupsUnstoppableDomains) return false;
+ // Unstoppable Domains handle example: name.crypto
+ final formattedName = OpenaliasRecord.formatDomainName(q);
+ final domainParts = formattedName.split('.');
+ final name = domainParts.last;
+ return domainParts.length > 1 &&
+ domainParts.first.isNotEmpty &&
+ name.isNotEmpty &&
+ unstoppableDomains.any((domain) => name.trim() == domain);
+ },
+ run: _lookupsUnstoppableDomains,
+ ),
+ LookupEntry(
+ source: AddressSource.bip353,
+ currencies: AddressSource.bip353.supportedCurrencies,
+ applies: (q) => settingsStore.lookupsBip353 && q.contains('@') && q.contains('.'),
+ run: _lookupsBip353,
+ ),
+ LookupEntry(
+ source: AddressSource.ens,
+ currencies: AddressSource.ens.supportedCurrencies,
+ applies: (q) => settingsStore.lookupsENS && q.endsWith('.eth'),
+ // ENS handle example: name.eth
+ run: _lookupEns,
+ ),
+ LookupEntry(
+ source: AddressSource.openAlias,
+ currencies: AddressSource.openAlias.supportedCurrencies,
+ applies: (q) {
+ if (!settingsStore.lookupsOpenAlias) return false;
+ // OpenAlias handle example:
+ final formattedName = OpenaliasRecord.formatDomainName(q);
+ return formattedName.contains(".");
+ },
+ run: _lookupsOpenAlias,
+ ),
+ LookupEntry(
+ source: AddressSource.nostr,
+ currencies: AddressSource.nostr.supportedCurrencies,
+ applies: (q) => settingsStore.lookupsNostr && isEmailFormat(q),
+ // Nostr handle example: name@domain
+ run: _lookupsNostr,
+ ),
+ ];
+ }
+
+ static String _cleanInput(String raw) =>
+ raw.replaceAll(RegExp(r'[\u2028\u2029]'), '\n').replaceAll(RegExp(r'<[^>]+>'), ' ');
+
+ static String? extractAddressByType({
+ required String raw,
+ required CryptoCurrency type,
+ bool requireSurroundingWhitespaces = true,
+ }) {
+ var addressPattern = AddressValidator.getAddressFromStringPattern(type);
+ if (addressPattern == null) {
+ printV('Unknown pattern for $type');
+ return null;
+ }
+ if (requireSurroundingWhitespaces)
+ addressPattern = "$BEFORE_REGEX$addressPattern$AFTER_REGEX";
+ final text = _cleanInput(raw);
+ final match = RegExp(addressPattern, multiLine: true, caseSensitive: false)
+ .firstMatch(text);
+ if (match == null) return null;
+ return match.group(0)?.replaceAllMapped(
+ RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_|ban_'), (Match match) {
+ String group = match.group(0)!;
+ if (group.startsWith('bitcoincash:') ||
+ group.startsWith('nano_') ||
+ group.startsWith('ban_')) {
+ return group;
+ }
+ return '';
+ });
+ }
+
+ bool isEmailFormat(String address) {
+ final RegExp emailRegex = RegExp(
+ r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
+ caseSensitive: false,
+ );
+ return emailRegex.hasMatch(address);
+ }
+
+ Future> resolve({
+ required String query,
+ required WalletBase wallet,
+ CryptoCurrency? currency,
+ }) async {
+ try {
+ final tasks = >[];
+
+ for (final entry in _lookupTable) {
+ if (!supportedSources.contains(entry.source)) continue;
+ if (!entry.applies(query)) continue;
+
+ final coins = currency == null
+ ? entry.currencies.toList()
+ : (entry.currencies.contains(currency) ? [currency] : const []);
+
+ if (coins.isEmpty) continue;
+ tasks.add(entry.run(query, coins, wallet));
+ }
+
+ final results = await Future.wait(tasks);
+
+ return results.whereType().toList();
+ } catch (e) {
+ printV('Error resolving address: $e');
+ return [];
+ }
+ }
+
+ Future _lookupTwitter(
+ String text, List currencies, WalletBase wallet) async {
+ final formattedName = text.substring(1);
+ final twitterUser = await TwitterApi.lookupUserByName(userName: formattedName);
+
+ if (twitterUser == null) return null;
+
+ final Map result = {};
+
+ String queryTxt = twitterUser.description;
+
+ try {
+ for (final cur in currencies) {
+ final addressFromBio = extractAddressByType(
+ raw: queryTxt, type: CryptoCurrency.fromString(cur.title));
+ printV('Address from bio: $addressFromBio');
+
+ if (addressFromBio != null && addressFromBio.isNotEmpty) {
+ result[cur] = addressFromBio;
+ queryTxt = queryTxt.replaceFirst(addressFromBio, '');
+ }
+ }
+ } catch (e) {
+ printV('Error extracting address from Twitter bio: $e');
+ }
+
+ String pinnedTweet = twitterUser.pinnedTweet?.text ?? '';
+
+ try {
+
+ if (pinnedTweet.isNotEmpty) {
+ for (final cur in currencies) {
+ final addressFromPinnedTweet =
+ extractAddressByType(raw: pinnedTweet, type: CryptoCurrency.fromString(cur.title));
+ if (addressFromPinnedTweet != null && addressFromPinnedTweet.isNotEmpty) {
+ result[cur] = addressFromPinnedTweet;
+ pinnedTweet = pinnedTweet = pinnedTweet.replaceFirst(addressFromPinnedTweet, '');
+ }
+ }
+ }
+ } catch (e) {
+ printV('Error extracting address from Twitter pinned tweet: $e');
+ }
+
+ if (result.isNotEmpty) {
+ return ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ addressSource: AddressSource.twitter,
+ handle: text,
+ profileImageUrl: twitterUser.profileImageUrl,
+ profileName: twitterUser.name,
+ );
+ }
+ return null;
+ }
+
+ Future _lookupZano(String text, List _, WalletBase __) async {
+ final formattedName = text.substring(1);
+
+ final zanoAddress = await ZanoAlias.fetchZanoAliasAddress(formattedName);
+ if (zanoAddress != null && zanoAddress.isNotEmpty) {
+ return ParsedAddress(
+ parsedAddressByCurrencyMap: {CryptoCurrency.zano: zanoAddress},
+ addressSource: AddressSource.zanoAlias,
+ handle: text,
+ );
+ }
+ return null;
+ }
+
+ Future _lookupMastodon(
+ String text, List currencies, WalletBase _) async {
+ final subText = text.substring(1);
+ final hostNameIndex = subText.indexOf('@');
+ final hostName = subText.substring(hostNameIndex + 1);
+ final userName = subText.substring(0, hostNameIndex);
+
+ final Map result = {};
+
+ final mastodonUser =
+ await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName);
+
+ if (mastodonUser != null) {
+ String queryTxt = mastodonUser.note;
+ for (final cur in currencies) {
+ String? addressFromBio = extractAddressByType(raw: queryTxt, type: cur);
+ if (addressFromBio != null && addressFromBio.isNotEmpty) {
+ result[cur] = addressFromBio;
+ queryTxt = queryTxt.replaceFirst(addressFromBio, '');
+ }
+ }
+
+ final pinnedPosts =
+ await MastodonAPI.getPinnedPosts(userId: mastodonUser.id, apiHost: hostName);
+
+ if (pinnedPosts.isNotEmpty) {
+ String userPinnedPostsText = pinnedPosts.map((item) => item.content).join('\n');
+
+
+ for (final cur in currencies) {
+ String? addressFromPinnedPost = extractAddressByType(raw: userPinnedPostsText, type: cur);
+ if (addressFromPinnedPost != null && addressFromPinnedPost.isNotEmpty) {
+ result[cur] = addressFromPinnedPost;
+ userPinnedPostsText = userPinnedPostsText.replaceFirst(addressFromPinnedPost, '');
+ }
+ }
+ }
+
+ if (result.isNotEmpty) {
+ return ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ addressSource: AddressSource.mastodon,
+ handle: text,
+ profileImageUrl: mastodonUser.profileImageUrl,
+ profileName: mastodonUser.username,
+ );
+ }
+ }
+ return null;
+ }
+
+ Future _lookupWellKnown(
+ String text, List currencies, WalletBase _) async {
+ if (!currencies.contains(CryptoCurrency.nano)) return null;
+
+ final rec = await WellKnownRecord.fetch(text, CryptoCurrency.nano);
+ if (rec == null || rec.address.isEmpty) return null;
+
+ return ParsedAddress(
+ parsedAddressByCurrencyMap: {CryptoCurrency.nano: rec.address},
+ addressSource: AddressSource.wellKnown,
+ handle: text,
+ profileName: rec.title ?? '',
+ profileImageUrl: rec.imageUrl ?? '',
+ );
+ }
+
+ Future _lookupFio(
+ String text, List currencies, WalletBase _) async {
+ final Map result = {};
+ final bool isFioRegistered = await FioAddressProvider.checkAvail(text);
+ if (!isFioRegistered) return null;
+
+ for (final cur in currencies) {
+ final address = await FioAddressProvider.getPubAddress(text, cur.title);
+ if (address != null && address.isNotEmpty) {
+ result[cur] = address;
+ }
+ }
+
+ if (result.isNotEmpty) {
+ return ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ addressSource: AddressSource.fio,
+ handle: text,
+ );
+ }
+ return null;
+ }
+
+ Future _lookupYatService(
+ String text, List currencies, WalletBase _) async {
+ final result = {};
+
+ for (final cur in currencies) {
+ final records = await yatService.fetchYatAddress(text, cur.title);
+ if (records.isEmpty) continue;
+
+ final chosen = cur == CryptoCurrency.xmr
+ ? records.firstWhere((r) => r.isMoneroSub, orElse: () => records.first)
+ : records.first;
+
+ result[cur] = chosen.address;
+ }
+
+ return result.isEmpty
+ ? null
+ : ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ addressSource: AddressSource.yatRecord,
+ handle: text,
+ );
+ }
+
+ Future _lookupThorChain(
+ String text, List currencies, WalletBase _) async {
+ final map = await ThorChainExchangeProvider.lookupAddressByName(text);
+ if (map == null || map.isEmpty) return null;
+
+ final result = {};
+
+ for (final cur in currencies) {
+ final key = cur.title.toUpperCase();
+ final addr = map[key];
+ if (addr != null && addr.isNotEmpty) {
+ if (!result.containsValue(addr)) result[cur] = addr;
+ }
+ }
+
+ return result.isEmpty
+ ? null
+ : ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ addressSource: AddressSource.thorChain,
+ handle: text,
+ );
+ }
+
+ Future _lookupsUnstoppableDomains(
+ String text, List currency, WalletBase _) async {
+ final Map result = {};
+ for (final cur in currency) {
+ final address = await fetchUnstoppableDomainAddress(text, cur.title);
+ if (address.isNotEmpty) {
+ result[cur] = address;
+ }
+ }
+
+ if (result.isNotEmpty) {
+ return ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ profileImageUrl: 'assets/images/profile.png',
+ profileName: text,
+ addressSource: AddressSource.unstoppableDomains,
+ handle: text,
+ );
+ }
+
+ return null;
+ }
+
+ Future _lookupsBip353(
+ String text, List currencies, WalletBase _) async {
+ final Map result = {};
+
+ String? dnsProof;
+ try {
+ dnsProof = await Bip353Record.fetchDnsProof(text);
+
+ for (final cur in currencies) {
+ final bip353AddressMap = await Bip353Record.fetchUriByCryptoCurrency(text, cur.title);
+
+ if (bip353AddressMap != null && bip353AddressMap.isNotEmpty) {
+ if (cur == CryptoCurrency.btc) {
+ bip353AddressMap.forEach((key, value) {
+ final spAddress = bip353AddressMap['sp'];
+ final address = bip353AddressMap['address'];
+
+ if (spAddress != null && spAddress.isNotEmpty) {
+ result[cur] = spAddress;
+ }
+ if (address != null && address.isNotEmpty) {
+ result[cur] = address;
+ }
+ });
+ }
+ }
+ }
+ } catch (e) {
+ printV('Error fetching BIP-353 DNS proof: $e');
+ return null;
+ }
+
+ if (result.isNotEmpty) {
+ return ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ addressSource: AddressSource.bip353,
+ handle: text,
+ bip353DnsProof: dnsProof ?? '',
+ );
+ }
+ return null;
+ }
+
+ Future _lookupEns(
+ String text, List currency, WalletBase wallet) async {
+ final Map result = {};
+
+ for (final cur in currency) {
+ final address = await EnsRecord.fetchEnsAddress(text, cur, wallet: wallet);
+ if (address.isNotEmpty && address != "0x0000000000000000000000000000000000000000") {
+ result[cur] = address;
+ }
+ }
+
+ if (result.isNotEmpty) {
+ return ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ addressSource: AddressSource.ens,
+ handle: text,
+ );
+ }
+ return null;
+ }
+
+ Future _lookupsOpenAlias(
+ String text,
+ List currencies,
+ WalletBase _,
+ ) async {
+ final formatted = OpenaliasRecord.formatDomainName(text);
+
+ final txtRecords = await OpenaliasRecord.lookupOpenAliasRecord(formatted);
+ if (txtRecords == null) return null;
+
+ final result = {};
+
+ for (final cur in currencies) {
+ final rec = OpenaliasRecord.fetchAddressAndName(
+ formattedName: formatted,
+ ticker: cur.title.toLowerCase(),
+ txtRecord: txtRecords,
+ );
+
+ if (rec.address.isNotEmpty) result[cur] = rec.address;
+ }
+
+ return result.isEmpty
+ ? null
+ : ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ addressSource: AddressSource.openAlias,
+ handle: text,
+ );
+ }
+
+ Future _lookupsNostr(
+ String text, List currencies, WalletBase _) async {
+
+ try {
+ final profile = await NostrProfileHandler.queryProfile(text);
+ if (profile == null) return null;
+
+ final data = await NostrProfileHandler.processRelays(profile, text);
+ if (data == null) return null;
+
+ final result = {};
+
+ String queryTxt = data.about;
+
+ for (final cur in currencies) {
+ final addr = extractAddressByType(raw: queryTxt, type: cur);
+ if (addr != null && addr.isNotEmpty) {
+ result[cur] = addr;
+ queryTxt = queryTxt.replaceFirst(addr, '');
+ }
+ }
+ if (result.isEmpty) return null;
+
+ return ParsedAddress(
+ parsedAddressByCurrencyMap: result,
+ addressSource: AddressSource.nostr,
+ handle: text,
+ profileImageUrl: data.picture,
+ profileName: data.name,
+ );
+ } catch (e) {
+ printV('Error looking up Nostr profile: $e');
+ return null;
+ }
+ }
+}
+
+class LookupEntry {
+ const LookupEntry({
+ required this.source,
+ required this.currencies,
+ required this.applies,
+ required this.run,
+ });
+
+ final AddressSource source;
+ final List currencies;
+ final bool Function(String query) applies;
+ final Future Function(
+ String query, List currencies, WalletBase wallet) run;
+}
diff --git a/lib/address_resolver/parsed_address.dart b/lib/address_resolver/parsed_address.dart
new file mode 100644
index 0000000000..ea51edd19f
--- /dev/null
+++ b/lib/address_resolver/parsed_address.dart
@@ -0,0 +1,163 @@
+import 'package:cake_wallet/core/address_validator.dart';
+import 'package:cake_wallet/entities/openalias_record.dart';
+import 'package:cake_wallet/entities/yat_record.dart';
+import 'package:cw_core/crypto_currency.dart';
+
+const supportedSources = [
+ AddressSource.twitter,
+ AddressSource.unstoppableDomains,
+ AddressSource.mastodon,
+ AddressSource.bip353,
+ AddressSource.fio,
+ AddressSource.zanoAlias,
+ AddressSource.thorChain,
+ AddressSource.ens,
+ AddressSource.yatRecord,
+ AddressSource.openAlias,
+ AddressSource.wellKnown,
+ AddressSource.nostr,
+];
+
+///Do not use '-' in the label, it is used to separate the label from the alias.
+enum AddressSource {
+ twitter(
+ label: 'X',
+ iconPath: 'assets/images/address_providers/x.svg',
+ alias: '@username',
+ supportedCurrencies: AddressValidator.reliableValidateCurrencies),
+ unstoppableDomains(
+ label: 'Unstoppable Domains',
+ iconPath: 'assets/images/address_providers/unstoppable.svg',
+ alias: 'domain.tld',
+ supportedCurrencies: [CryptoCurrency.xmr, CryptoCurrency.btc]),
+ openAlias(
+ label: 'OpenAlias',
+ iconPath: 'assets/images/address_providers/openalias.svg',
+ alias: 'name.domain.tld',
+ supportedCurrencies: [CryptoCurrency.xmr, CryptoCurrency.btc]),
+ yatRecord(
+ label: 'Yat',
+ iconPath: 'assets/images/address_providers/yat.svg',
+ alias: '🎂🎂🎂',
+ supportedCurrencies: [
+ CryptoCurrency.xmr,
+ CryptoCurrency.btc,
+ CryptoCurrency.eth,
+ CryptoCurrency.ltc
+ ]),
+ fio(label: 'FIO', iconPath: 'assets/images/address_providers/fio.svg', alias: 'user@domain',
+ supportedCurrencies: AddressValidator.reliableValidateCurrencies),
+ ens(
+ label: 'Ethereum Name Service',
+ iconPath: 'assets/images/address_providers/ens.svg',
+ alias: 'domain.eth',
+ supportedCurrencies: [CryptoCurrency.xmr, CryptoCurrency.btc, CryptoCurrency.eth]),
+ mastodon(
+ label: 'Mastodon',
+ iconPath: 'assets/images/address_providers/mastodon.svg',
+ alias: 'user@domain.tld',
+ supportedCurrencies: AddressValidator.reliableValidateCurrencies),
+ nostr(
+ label: 'Nostr',
+ iconPath: 'assets/images/address_providers/nostr.svg',
+ alias: 'user@domain.tld',
+ supportedCurrencies: AddressValidator.reliableValidateCurrencies),
+ thorChain(
+ label: 'ThorChain',
+ iconPath: 'assets/images/address_providers/thorchain.svg',
+ alias: 'name',
+ supportedCurrencies: CryptoCurrency.all),
+ wellKnown(
+ label: '.wellknown',
+ iconPath: 'assets/images/address_providers/wellknown.svg',
+ alias: 'domain.tld',
+ supportedCurrencies: [CryptoCurrency.nano]),
+ zanoAlias(
+ label: 'Zano Alias',
+ iconPath: 'assets/images/address_providers/zano.svg',
+ alias: '@alias',
+ supportedCurrencies: [CryptoCurrency.zano]),
+ bip353(
+ label: 'BIP353',
+ iconPath: 'assets/images/address_providers/bip353.svg',
+ alias: 'user@domain.com',
+ supportedCurrencies: [CryptoCurrency.btc]),
+ contact(label: 'Contact', iconPath: '', supportedCurrencies: []),
+ notParsed(label: 'Unknown', iconPath: '', supportedCurrencies: []);
+
+ const AddressSource({
+ required this.label,
+ required this.iconPath,
+ this.alias = '',
+ this.supportedCurrencies = const [],
+ });
+
+ final String label;
+ final String iconPath;
+ final String alias;
+ final List supportedCurrencies;
+}
+
+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,
+ );
+ }
+}
+
+class ParsedAddress {
+ const ParsedAddress({
+ required this.parsedAddressByCurrencyMap,
+ this.manualAddressByCurrencyMap,
+ this.addressSource = AddressSource.notParsed,
+ this.handle = '',
+ this.profileImageUrl = '',
+ this.profileName = '',
+ this.description = '',
+ this.bip353DnsProof,
+ });
+
+ final Map parsedAddressByCurrencyMap;
+ final Map? manualAddressByCurrencyMap;
+ final AddressSource addressSource;
+ final String handle;
+ final String profileImageUrl;
+ final String profileName;
+ final String description;
+ final String? bip353DnsProof;
+
+ ParsedAddress copyWith({
+ Map? parsedAddressByCurrencyMap,
+ Map? manualAddressByCurrencyMap,
+ AddressSource? addressSource,
+ String? handle,
+ String? profileImageUrl,
+ String? profileName,
+ String? description,
+ String? bip353DnsProof,
+ }) {
+ return ParsedAddress(
+ parsedAddressByCurrencyMap: parsedAddressByCurrencyMap ?? this.parsedAddressByCurrencyMap,
+ manualAddressByCurrencyMap: manualAddressByCurrencyMap ?? this.manualAddressByCurrencyMap,
+ addressSource: addressSource ?? this.addressSource,
+ handle: handle ?? this.handle,
+ profileImageUrl: profileImageUrl ?? this.profileImageUrl,
+ profileName: profileName ?? this.profileName,
+ description: description ?? this.description,
+ bip353DnsProof: bip353DnsProof ?? this.bip353DnsProof,
+ );
+ }
+}
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 926e9f04ff..3e7f359df9 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
@@ -12,7 +12,7 @@ import 'package:cake_wallet/cake_pay/src/widgets/rounded_overlay_cards_widget.da
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/execution_state.dart';
-import 'package:cake_wallet/entities/parsed_address.dart';
+import 'package:cake_wallet/address_resolver/parsed_address.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
@@ -595,8 +595,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 ?? '',
),
@@ -661,10 +664,12 @@ class CakePayBuyCardPage extends BasePage {
final displayingOutputs = outputsCopy
.map((o) => o.OutputCopyWithParsedAddress(
parsedAddress: ParsedAddress(
- addresses: [o.address],
- name: 'Cake Pay',
+ handle: 'Cake Pay',
profileName: order?.cards.first.cardName ?? 'Cake Pay',
- profileImageUrl: order?.cards.first.cardImagePath ?? '',
+ profileImageUrl: order?.cards.first.cardImagePath ?? '', parsedAddressByCurrencyMap: {
+ cakePayBuyCardViewModel.sendViewModel.selectedCryptoCurrency:
+ o.address,
+ },
),
fiatAmount: '${order?.totalReceiveAmount}',
))
diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart
index a18f2feace..90e0174e18 100644
--- a/lib/core/address_validator.dart
+++ b/lib/core/address_validator.dart
@@ -28,7 +28,66 @@ class AddressValidator extends TextValidator {
pattern: getPattern(type, isTestnet: isTestnet),
length: getLength(type));
- static String getPattern(CryptoCurrency type, {bool isTestnet = false}) {
+ static const List reliableValidateCurrencies = [
+ CryptoCurrency.xmr,
+ CryptoCurrency.btc,
+ CryptoCurrency.ltc,
+ CryptoCurrency.bch,
+ CryptoCurrency.trx,
+ CryptoCurrency.nano,
+ CryptoCurrency.banano,
+ CryptoCurrency.sol,
+ CryptoCurrency.wow,
+ CryptoCurrency.zano,
+ CryptoCurrency.ada,
+ CryptoCurrency.xrp,
+ CryptoCurrency.xhv,
+ CryptoCurrency.zaddr,
+ CryptoCurrency.zec,
+ CryptoCurrency.dcr,
+ CryptoCurrency.rvn,
+ CryptoCurrency.near,
+ CryptoCurrency.rune,
+ CryptoCurrency.scrt,
+ CryptoCurrency.stx,
+ CryptoCurrency.kmd,
+ CryptoCurrency.doge,
+ CryptoCurrency.btcln,
+ ];
+
+ static Set detectAddressBookCurrencies(
+ String txt, {
+ bool isTestnet = false,
+ bool includeGeneric = false
+ }) {
+ final Set matches = {};
+
+ final currencies = includeGeneric
+ ? CryptoCurrency.all
+ : reliableValidateCurrencies;
+
+ for (final cur in currencies) {
+ final pattern = AddressValidator.getPattern(cur, isTestnet: isTestnet, ignoreZanoAlias: true);
+ if (pattern.isEmpty) continue;
+
+ final lengths = AddressValidator.getLength(cur);
+ if (lengths != null && !lengths.contains(txt.length)) continue;
+
+ final formatedPattern = pattern
+ .replaceFirst(BEFORE_REGEX, '')
+ .replaceFirst(AFTER_REGEX, '');
+
+ if (!RegExp('^$formatedPattern\$',
+ caseSensitive: false, multiLine: false)
+ .hasMatch(txt)) continue;
+
+ matches.add(cur);
+ }
+
+ return matches;
+ }
+
+ static String getPattern(CryptoCurrency type, {bool isTestnet = false, bool? ignoreZanoAlias}) {
var pattern = "";
if (type is Erc20Token) {
pattern = '0x[0-9a-zA-Z]+';
@@ -63,9 +122,11 @@ class AddressValidator extends TextValidator {
'|(ltc1q[ac-hj-np-z02-9]{40,80})'
'|(${MwebAddress.regex.pattern})(\$|\s)';
case CryptoCurrency.nano:
- pattern = '[0-9a-zA-Z_]+';
+ pattern = '(?:nano_|xrb_)[13][13456789abcdefghijkmnopqrstuwxyz]{59}';
case CryptoCurrency.banano:
- pattern = '[0-9a-zA-Z_]+';
+ pattern = 'ban_[13][13456789abcdefghijkmnopqrstuwxyz]{59}';
+ case CryptoCurrency.sol:
+ pattern = r'[1-9A-HJ-NP-Za-km-z]{43,44}';
case CryptoCurrency.usdc:
case CryptoCurrency.usdcpoly:
case CryptoCurrency.usdtPoly:
@@ -103,7 +164,10 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.shib:
pattern = '0x[0-9a-zA-Z]+';
case CryptoCurrency.xrp:
- pattern = '[0-9a-zA-Z]{34}|[0-9a-zA-Z]{33}|X[0-9a-zA-Z]{46}';
+ pattern =
+ '^(?:r[1-9A-HJ-NP-Za-km-z]{25,34}' // classic
+ '|X[1-9A-HJ-NP-Za-km-z]{46,55}' // X-address (main-net)
+ '|T[1-9A-HJ-NP-Za-km-z]{45,54})';
case CryptoCurrency.xhv:
pattern = 'hvx|hvi|hvs[0-9a-zA-Z]+';
case CryptoCurrency.xag:
@@ -123,11 +187,14 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.usdterc20:
case CryptoCurrency.xlm:
case CryptoCurrency.trx:
+ pattern = '^(?:T[1-9A-HJ-NP-Za-km-z]{33}|41[0-9A-Fa-f]{40}|0x41[0-9A-Fa-f]{40})';
case CryptoCurrency.dai:
case CryptoCurrency.dash:
case CryptoCurrency.eos:
case CryptoCurrency.wow:
- pattern = '[0-9a-zA-Z]+';
+ pattern = r'(?:W(?:o|m|W)[1-9A-HJ-NP-Za-km-z]{94,96}'
+ r'|Wo[1-9A-HJ-NP-Za-km-z]{106,107})';
+ break;
case CryptoCurrency.bch:
pattern = '(?:bitcoincash:)?(q|p)[0-9a-zA-Z]{41}'
'|[13][a-km-zA-HJ-NP-Z1-9]{25,34}';
@@ -156,7 +223,9 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.btcln:
pattern = '(lnbc|LNBC)([0-9]{1,}[a-zA-Z0-9]+)';
case CryptoCurrency.zano:
- pattern = r'([1-9A-HJ-NP-Za-km-z]{90,200})|(@[\w\d.-]+)';
+ pattern = ignoreZanoAlias == true
+ ? r'(?:Z[1-9A-HJ-NP-Za-km-z]{96}|iZ[1-9A-HJ-NP-Za-km-z]{106})'
+ : r'(?:Z[1-9A-HJ-NP-Za-km-z]{96}|iZ[1-9A-HJ-NP-Za-km-z]{106}|@[a-z0-9]{6,32})';
case CryptoCurrency.doge:
pattern = r'^D[a-km-zA-HJ-NP-Z1-9]{25,34}';
default:
@@ -179,6 +248,7 @@ class AddressValidator extends TextValidator {
switch (type) {
case CryptoCurrency.xmr:
case CryptoCurrency.wow:
+ case CryptoCurrency.zano:
return null;
case CryptoCurrency.ada:
return null;
@@ -243,7 +313,7 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.usdcsol:
return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44];
case CryptoCurrency.trx:
- return [34];
+ return [34, 42, 44];
case CryptoCurrency.usdt:
return [34];
case CryptoCurrency.usdttrc20:
@@ -298,7 +368,6 @@ class AddressValidator extends TextValidator {
return [64];
case CryptoCurrency.btcln:
case CryptoCurrency.kaspa:
- case CryptoCurrency.zano:
default:
return null;
}
@@ -318,10 +387,9 @@ class AddressValidator extends TextValidator {
'|(8[0-9a-zA-Z]{94})'
'|([0-9a-zA-Z]{106})';
case CryptoCurrency.wow:
- pattern = '(W[0-9a-zA-Z]{94})'
- '|(W[0-9a-zA-Z]{94})'
- '|(W[0-9a-zA-Z]{96})'
- '|([0-9a-zA-Z]{106})';
+ const base58 = r'[1-9A-HJ-NP-Za-km-z]';
+ pattern = '(?:W(?:o|m|W)$base58{94,95}|Wo$base58{106,107})';
+ break;
case CryptoCurrency.btc:
pattern =
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
@@ -340,9 +408,9 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.bch:
pattern = '(bitcoincash:)?q[0-9a-zA-Z]{41,42}';
case CryptoCurrency.sol:
- pattern = '[1-9A-HJ-NP-Za-km-z]+';
+ pattern = '[1-9A-HJ-NP-Za-km-z]{43,44}';
case CryptoCurrency.trx:
- pattern = '(T|t)[1-9A-HJ-NP-Za-km-z]{33}';
+ pattern = '^(?:T[1-9A-HJ-NP-Za-km-z]{33}|0x?41[0-9A-Fa-f]{40})';
case CryptoCurrency.zano:
pattern = '([1-9A-HJ-NP-Za-km-z]{90,200})|(@[\w\d.-]+)';
default:
diff --git a/lib/core/auth_service.dart b/lib/core/auth_service.dart
index 378c52ec0e..f58ad7ade7 100644
--- a/lib/core/auth_service.dart
+++ b/lib/core/auth_service.dart
@@ -30,7 +30,6 @@ class AuthService with Store {
Routes.modify2FAPage,
Routes.newWallet,
Routes.newWalletType,
- Routes.addressBookAddContact,
Routes.restoreOptions,
];
diff --git a/lib/core/yat_service.dart b/lib/core/yat_service.dart
index 84aac16265..5de6f2d32f 100644
--- a/lib/core/yat_service.dart
+++ b/lib/core/yat_service.dart
@@ -22,34 +22,46 @@ class YatService {
'LTC': '0x1019'
};
- Future> fetchYatAddress(String emojiId, String ticker) async {
- final formattedTicker = ticker.toUpperCase();
- final formattedEmojiId = emojiId.replaceAll(' ', '');
- final tag = tags[formattedTicker];
- final uri = Uri.parse(lookupEmojiUrl(formattedEmojiId)).replace(
- queryParameters: {
- "tags": tag
- });
- final yatRecords = [];
+ Future> fetchYatAddress(
+ String emojiId,
+ String ticker,
+ ) async {
+ final tagQuery = tags[ticker.toUpperCase()];
+ if (tagQuery == null) return const [];
+
+ final uri = Uri.parse(
+ lookupEmojiUrl(emojiId.replaceAll(' ', '')),
+ ).replace(queryParameters: {'tags': tagQuery});
try {
final response = await ProxyWrapper().get(clearnetUri: uri);
-
- final resBody = json.decode(response.body) as Map;
- final results = resBody["result"] as Map;
- // Favour a subaddress over a standard address.
- final yatRecord = (
- results[MONERO_SUB_ADDRESS] ??
- results[MONERO_STD_ADDRESS] ??
- results[tag]) as Map;
-
- if (yatRecord.isNotEmpty) {
- yatRecords.add(YatRecord.fromJson(yatRecord));
+ final body = json.decode(response.body) as Map;
+
+ final res = body['result'];
+ if (res == null) return const [];
+
+ final records = [];
+
+ if (res is Map) {
+ res.forEach((tag, data) {
+ if (data is Map) {
+ records.add(YatRecord.fromJson(data, tag.toString()));
+ }
+ });
+ }
+
+ else if (res is List) {
+ for (final item in res) {
+ if (item is Map) {
+ final tag = item['tag']?.toString() ?? '';
+ records.add(YatRecord.fromJson(item, tag));
+ }
+ }
}
- return yatRecords;
+ return records;
} catch (_) {
- return yatRecords;
+ return const [];
}
}
}
diff --git a/lib/di.dart b/lib/di.dart
index 8aadf96751..1a80812c6a 100644
--- a/lib/di.dart
+++ b/lib/di.dart
@@ -29,9 +29,20 @@ import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/hardware_wallet/require_hardware_wallet_connection.dart';
-import 'package:cake_wallet/entities/parse_address_from_domain.dart';
+import 'package:cake_wallet/address_resolver/address_resolver_service.dart';
+import 'package:cake_wallet/address_resolver/parsed_address.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/haven/cw_haven.dart';
+import 'package:cake_wallet/src/screens/address_book/contact_refresh_page.dart';
+import 'package:cake_wallet/src/screens/address_book/contact_welcome_page.dart';
+import 'package:cake_wallet/src/screens/address_book/edit_address_page.dart';
+import 'package:cake_wallet/src/screens/address_book/edit_alias_page.dart';
+import 'package:cake_wallet/src/screens/address_book/edit_contact_page.dart';
+import 'package:cake_wallet/src/screens/address_book/contact_page.dart';
+import 'package:cake_wallet/src/screens/address_book/edit_new_contact_page.dart';
+import 'package:cake_wallet/src/screens/address_book/entities/address_edit_request.dart';
+import 'package:cake_wallet/src/screens/address_book/supported_handles_page.dart';
+import 'package:cake_wallet/src/screens/address_book/address_book_page.dart';
import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart';
import 'package:cake_wallet/src/screens/dev/moneroc_cache_debug.dart';
import 'package:cake_wallet/src/screens/dev/moneroc_call_profiler.dart';
@@ -97,8 +108,6 @@ import 'package:cake_wallet/src/screens/backup/backup_page.dart';
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
-import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
-import 'package:cake_wallet/src/screens/contact/contact_page.dart';
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
@@ -992,24 +1001,82 @@ Future setup({
getIt.registerFactory(() => WalletKeysViewModel(getIt.get()));
getIt.registerFactory(() => WalletKeysPage(getIt.get()));
-
+
getIt.registerFactory(() => AnimatedURModel(getIt.get()));
getIt.registerFactoryParam, void>((Map urQr, _) =>
AnimatedURPage(getIt.get(), urQr: urQr));
- getIt.registerFactoryParam(
- (ContactRecord? contact, _) => ContactViewModel(_contactSource, contact: contact));
+ getIt.registerFactoryParam(
+ (req, _) => ContactViewModel(_contactSource,getIt().wallet!,getIt(), request: req,),
+ );
getIt.registerFactoryParam(
- (CryptoCurrency? cur, _) =>
- ContactListViewModel(_contactSource, _walletInfoSource, cur, getIt.get()));
+ (cur, _) => ContactListViewModel(
+ _contactSource,
+ _walletInfoSource,
+ cur,
+ getIt(),
+ ),
+ );
- getIt.registerFactoryParam((CryptoCurrency? cur, _) =>
- ContactListPage(getIt.get(param1: cur), getIt.get()));
+ getIt.registerFactoryParam(
+ (cur, _) => AddressBookPage(
+ getIt.get(param1: cur),
+ getIt(),
+ ),
+ );
- getIt.registerFactoryParam(
- (ContactRecord? contact, _) => ContactPage(getIt.get(param1: contact)));
+ getIt.registerFactoryParam(
+ (contact, _) => ContactWelcomePage(
+ contactViewModel: getIt.get(
+ param1: AddressEditRequest.contact(contact),
+ ),
+ ),
+ );
+
+ getIt.registerFactoryParam(
+ (contact, _) => ContactPage(
+ contactViewModel: getIt.get(
+ param1: AddressEditRequest.contact(contact),
+ ),
+ ),
+ );
+
+ getIt.registerFactoryParam(
+ (contact, cur) => ContactRefreshPage(
+ contactViewModel : getIt.get(param1: AddressEditRequest.contact(contact)),
+ currency: cur,
+ ),
+ );
+
+ getIt.registerFactoryParam, void>(
+ (list, _) => EditAddressPage(list),
+ );
+
+ getIt.registerFactory(
+ () => SupportedHandlesPage(contactViewModel: getIt()),
+ );
+
+ getIt.registerFactoryParam(
+ (contactViewModel, _) => EditContactPage(contactViewModel: contactViewModel),
+ );
+
+ getIt.registerFactoryParam(
+ (contactViewModel, handleKey) => EditAliasPage(contactViewModel: contactViewModel,
+ handleKey: handleKey),
+ );
+
+ getIt.registerFactoryParam(
+ (parsedAddress, record) {
+ return EditNewContactPage(
+ selectedParsedAddress: parsedAddress,
+ contactViewModel: getIt(
+ param1: AddressEditRequest.contact(record),
+ ),
+ );
+ },
+ );
getIt.registerFactory(() => AddressListPage(getIt.get()));
@@ -1428,10 +1495,12 @@ Future setup({
getIt.registerFactory(() => YatService());
- getIt.registerFactory(() => AddressResolver(
- yatService: getIt.get(),
- wallet: getIt.get().wallet!,
- settingsStore: getIt.get()));
+ getIt.registerLazySingleton(
+ () => AddressResolverService(
+ yatService: getIt(),
+ settingsStore: getIt(),
+ ),
+ );
getIt.registerFactoryParam(
(QrViewData viewData, _) => FullscreenQRPage(qrViewData: viewData));
@@ -1556,18 +1625,18 @@ Future setup({
getIt.registerFactory(() => DevSharedPreferencesPage(getIt.get()));
getIt.registerFactory(() => DevSecurePreferencesPage(getIt.get()));
-
+
getIt.registerFactory(() => BackgroundSyncLogsViewModel());
-
+
getIt.registerFactory(() => DevBackgroundSyncLogsPage(getIt.get()));
getIt.registerFactory(() => SocketHealthLogsViewModel());
getIt.registerFactory(() => DevSocketHealthLogsPage(getIt.get()));
-
+
getIt.registerFactory(() => DevNetworkRequests());
getIt.registerFactory(() => StartTorPage(StartTorViewModel(),));
-
+
getIt.registerFactory(() => DEuroViewModel(
getIt(),
getIt(),
diff --git a/lib/entities/bip_353_record.dart b/lib/entities/bip_353_record.dart
index 9a2a50e0a5..16a6e661a9 100644
--- a/lib/entities/bip_353_record.dart
+++ b/lib/entities/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