Address resolver refactoring (#3291)

* restructure address resolver sources * refactor address resolver into lookup service * refactor address resolver to provider pattern * use addressSource to drive providers & settings * error handling * add address source icons * Update lib/core/address_validator.dart [skip ci] Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * review fixes * add validations to extractParsedAddress * skip address resolver for valid addresses * minor fix [skip ci] * fix formattedName x[skip ci] Co-authored-by: malik1004x <malikowskirobert@gmail.com> * fix formatted name zano [skip ci] Co-authored-by: malik1004x <malikowskirobert@gmail.com> * address PR review comments * add icons for new address source resolvers * confirm parsed addresses in contacts * minor fixes * LNUrl for BTCLN only and contact name handling --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: malik1004x <malikowskirobert@gmail.com>

Serhii committed Jul 6, 2026 at 16:48 UTC 7928c912e81d88df803e7ec5c3c1065b252ec9f6
93 files changed +3043 -1795
lib/cake_pay/src/cards/cake_pay_buy_card_page.dart
+11 -5
@@ -11,8 +11,8 @@ import 'package:cake_wallet/cake_pay/src/widgets/link_extractor.dart';
11 import 'package:cake_wallet/cake_pay/src/widgets/rounded_overlay_cards_widget.dart';
12 import 'package:cake_wallet/cake_pay/src/widgets/text_icon_button.dart';
13 import 'package:cake_wallet/cake_pay/src/widgets/three_checkbox_alert_content_widget.dart';
14 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
15 import 'package:cake_wallet/core/execution_state.dart';
15 -import 'package:cake_wallet/entities/parsed_address.dart';
16 import 'package:cake_wallet/generated/i18n.dart';
17 import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
18 import 'package:cake_wallet/routes.dart';
@@ -584,8 +584,11 @@ class CakePayBuyCardPage extends BasePage {
584 final displayingOutputs = _sendViewModel.outputs
585 .map((o) => o.OutputCopyWithParsedAddress(
586 parsedAddress: ParsedAddress(
587 - addresses: [o.address],
588 - name: 'Cake Pay',
587 + parsedAddressByCurrencyMap: {
588 + cakePayBuyCardViewModel.sendViewModel.selectedCryptoCurrency:
589 + o.address,
590 + },
591 + handle: 'Cake Pay',
592 profileName: order?.cards.first.cardName ?? 'Cake Pay',
593 profileImageUrl: order?.cards.first.cardImagePath ?? '',
594 ),
@@ -653,8 +656,11 @@ class CakePayBuyCardPage extends BasePage {
656 final displayingOutputs = outputsCopy
657 .map((o) => o.OutputCopyWithParsedAddress(
658 parsedAddress: ParsedAddress(
656 - addresses: [o.address],
657 - name: 'Cake Pay',
659 + parsedAddressByCurrencyMap: {
660 + cakePayBuyCardViewModel.sendViewModel.selectedCryptoCurrency:
661 + o.address,
662 + },
663 + handle: 'Cake Pay',
664 profileName: order?.cards.first.cardName ?? 'Cake Pay',
665 profileImageUrl: order?.cards.first.cardImagePath ?? '',
666 ),
lib/core/address_resolver/address_lookup_provider.dart new
+22
@@ -0,0 +1,22 @@
1 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
2 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
3 +import 'package:cake_wallet/store/settings_store.dart';
4 +import 'package:cw_core/crypto_currency.dart';
5 +import 'package:cw_core/wallet_base.dart';
6 +
7 +abstract class AddressLookupProvider {
8 +
9 + AddressSource get source;
10 +
11 + List<CryptoCurrency> get supportedCurrencies;
12 +
13 + bool isEnabled(SettingsStore settingsStore);
14 +
15 + bool canHandle(String query);
16 +
17 + Future<List<ParsedAddress>> resolve({
18 + required String query,
19 + required List<CryptoCurrency> currencies,
20 + required WalletBase wallet,
21 + });
22 +}
lib/core/address_resolver/address_resolver_service.dart new
+143
@@ -0,0 +1,143 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
3 +import 'package:cake_wallet/core/address_resolver/bip_353/bip_353_address_provider.dart';
4 +import 'package:cake_wallet/core/address_resolver/ens/ens_address_provider.dart';
5 +import 'package:cake_wallet/core/address_resolver/fio/fio_address_provider.dart';
6 +import 'package:cake_wallet/core/address_resolver/lnurl_pay/lnurl_pay_address_provider.dart';
7 +import 'package:cake_wallet/core/address_resolver/mastodon/mastodon_address_provider.dart';
8 +import 'package:cake_wallet/core/address_resolver/nostr/nostr_address_provider.dart';
9 +import 'package:cake_wallet/core/address_resolver/openalias/openalias_address_provider.dart';
10 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
11 +import 'package:cake_wallet/core/address_resolver/thorchain/thorchain_address_provider.dart';
12 +import 'package:cake_wallet/core/address_resolver/twitter/twitter_address_provider.dart';
13 +import 'package:cake_wallet/core/address_resolver/unstoppable/unstoppable_address_provider.dart';
14 +import 'package:cake_wallet/core/address_resolver/wellknown/wellknown_address_provider.dart';
15 +import 'package:cake_wallet/core/address_resolver/yat/yat_address_provider.dart';
16 +import 'package:cake_wallet/core/address_resolver/zano/zano_alias_address_provider.dart';
17 +import 'package:cake_wallet/core/address_resolver/zcash/zcash_address_provider.dart';
18 +import 'package:cake_wallet/core/address_resolver/zcash/zcash_me_address_provider.dart';
19 +import 'package:cake_wallet/store/settings_store.dart';
20 +import 'package:cw_core/crypto_currency.dart';
21 +import 'package:cw_core/utils/print_verbose.dart';
22 +import 'package:cw_core/wallet_base.dart';
23 +
24 +class AddressResolverService {
25 + AddressResolverService({required this.settingsStore});
26 +
27 + final SettingsStore settingsStore;
28 +
29 +// Sources available for address resolution and lookup settings.
30 + static const supportedSources = [
31 + AddressSource.lnurlPay,
32 + AddressSource.twitter,
33 + AddressSource.mastodon,
34 + AddressSource.yatRecord,
35 + AddressSource.unstoppableDomains,
36 + AddressSource.openAlias,
37 + AddressSource.ens,
38 + AddressSource.zcashName,
39 + AddressSource.zcashAddress,
40 + AddressSource.wellKnown,
41 + AddressSource.zanoAlias,
42 + AddressSource.bip353,
43 + AddressSource.fio,
44 + AddressSource.thorChain,
45 + AddressSource.nostr,
46 + ];
47 +
48 + List<AddressLookupProvider> _buildProviders() {
49 + final allProviders = <AddressLookupProvider>[
50 + LNUrlPayAddressProvider(),
51 + TwitterAddressProvider(),
52 + MastodonAddressProvider(),
53 + YatAddressProvider(),
54 + UnstoppableAddressProvider(),
55 + OpenaliasAddressProvider(),
56 + EnsAddressProvider(),
57 + ZcashNameAddressProvider(),
58 + ZcashMeAddressProvider(),
59 + WellKnownAddressProvider(),
60 + ZanoAliasAddressProvider(),
61 + Bip353AddressProvider(),
62 + FioAddressProvider(),
63 + ThorchainAddressProvider(),
64 + NostrAddressProvider(),
65 + ];
66 +
67 + return allProviders.where((provider) => supportedSources.contains(provider.source)).toList();
68 + }
69 +
70 + Future<List<ParsedAddress>> resolve({
71 + required String query,
72 + required WalletBase wallet,
73 + required CryptoCurrency currency,
74 + }) async {
75 + try {
76 + final providers = _buildProviders();
77 + // return first entry if currency is specified and provider supports it
78 + if (currency != null) {
79 + for (final provider in providers) {
80 + final cur = currency.title;
81 + final src = provider.source.label;
82 +
83 + printV('[address resolver service] checking $src - $cur - $query');
84 + if (!provider.isEnabled(settingsStore)) {
85 + printV('[address resolver service] ...skipping $src (disabled)');
86 + continue;
87 + }
88 + if (!provider.canHandle(query)) {
89 + printV('[address resolver service] ...skipping $src (cannot handle)');
90 + continue;
91 + }
92 + if (!provider.supportedCurrencies.contains(currency)) {
93 + printV('[address resolver service] ...skipping $src (unsupported currency)');
94 + continue;
95 + }
96 +
97 + try {
98 + printV('[address resolver service] ...resolving $src - $cur - $query');
99 + final results = await provider.resolve(
100 + query: query,
101 + currencies: [currency],
102 + wallet: wallet,
103 + );
104 +
105 + if (results.isNotEmpty) {
106 + final parsedAddress = results.first.parsedAddressByCurrencyMap[currency];
107 + final parsedAddressStr = parsedAddress == null || parsedAddress.isEmpty ? 'N/A' : parsedAddress;
108 + printV('[address resolver service] resolved $src - $cur - $parsedAddressStr');
109 + return [results.first];
110 + }
111 + } catch (e) {
112 + printV('[address resolver service] Error resolving $src: $e');
113 + }
114 + }
115 +
116 + return [];
117 + }
118 +
119 + return []; // TODO: Implement multi-currency resolution if no specific currency is provided.
120 +
121 + final tasks = <Future<List<ParsedAddress>>>[];
122 +
123 + for (final provider in providers) {
124 + if (!provider.isEnabled(settingsStore)) continue;
125 + if (!provider.canHandle(query)) continue;
126 +
127 + tasks.add(
128 + provider.resolve(
129 + query: query,
130 + currencies: provider.supportedCurrencies,
131 + wallet: wallet,
132 + ),
133 + );
134 + }
135 +
136 + final results = await Future.wait(tasks);
137 + return results.expand((items) => items).toList();
138 + } catch (e) {
139 + printV('Error resolving address: $e');
140 + return [];
141 + }
142 + }
143 +}
lib/core/address_resolver/address_resolver_utils.dart new
+104
@@ -0,0 +1,104 @@
1 +import 'package:cake_wallet/core/address_resolver/unstoppable/unstoppable_address_provider.dart';
2 +import 'package:cake_wallet/core/address_validator.dart';
3 +import 'package:cw_core/crypto_currency.dart';
4 +import 'package:cw_core/utils/print_verbose.dart';
5 +
6 +class AddressResolverUtils {
7 + static Future<Map<CryptoCurrency, String>> resolveUnstoppableDomainFromText({
8 + required String raw,
9 + required List<CryptoCurrency> currencies,
10 + }) async {
11 + final domain = extractUnstoppableDomain(raw);
12 + if (domain.isEmpty) return {};
13 +
14 + final result = <CryptoCurrency, String>{};
15 +
16 + for (final currency in currencies) {
17 + final address = await UnstoppableAddressProvider.fetchUnstoppableDomainAddress(domain, currency.title);
18 + if (address.isNotEmpty) {
19 + result[currency] = address;
20 + }
21 + }
22 +
23 + return result;
24 + }
25 +
26 + static String extractUnstoppableDomain(String raw) {
27 +// sort by length to avoid matching shorter tld instead of longer
28 + final domains = List<String>.from(UnstoppableAddressProvider.unstoppableDomains)
29 + ..sort((a, b) => b.length.compareTo(a.length));
30 + for (final tld in domains) {
31 + final pattern = RegExp(r'([A-Za-z0-9-]+)\.' + RegExp.escape(tld), caseSensitive: false);
32 + final match = pattern.firstMatch(raw);
33 + if (match != null) return match.group(0)!;
34 + }
35 + return '';
36 + }
37 +
38 + static Map<CryptoCurrency, String> extractAddressesFromText({
39 + required String raw,
40 + required List<CryptoCurrency> currencies,
41 + bool requireSurroundingWhitespaces = true,
42 + }) {
43 + final result = <CryptoCurrency, String>{};
44 + var text = raw;
45 +
46 + for (final currency in currencies) {
47 + final address = extractAddressByType(
48 + raw: text,
49 + type: currency,
50 + requireSurroundingWhitespaces: requireSurroundingWhitespaces,
51 + );
52 +
53 + if (address != null && address.isNotEmpty) {
54 + result[currency] = address;
55 + text = text.replaceFirst(address, '');
56 + }
57 + }
58 +
59 + return result;
60 + }
61 +
62 + static String? extractAddressByType({
63 + required String raw,
64 + required CryptoCurrency type,
65 + bool requireSurroundingWhitespaces = true,
66 + }) {
67 + var addressPattern = AddressValidator.getAddressFromStringPattern(type);
68 + if (addressPattern == null) {
69 + printV('Unknown pattern for $type');
70 + return null;
71 + }
72 + if (requireSurroundingWhitespaces) addressPattern = "$BEFORE_REGEX$addressPattern$AFTER_REGEX";
73 + final text = stripHtmlTags(raw);
74 + final match = RegExp(addressPattern, multiLine: true, caseSensitive: false).firstMatch(text);
75 + if (match == null) return null;
76 + return match.group(0)?.replaceAllMapped(RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_|ban_'),
77 + (Match match) {
78 + String group = match.group(0)!;
79 + if (group.startsWith('bitcoincash:') ||
80 + group.startsWith('nano_') ||
81 + group.startsWith('ban_')) {
82 + return group;
83 + }
84 + return '';
85 + });
86 + }
87 +
88 + static bool isEmailFormat(String address) {
89 + final RegExp emailRegex = RegExp(
90 + r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
91 + caseSensitive: false,
92 + );
93 + return emailRegex.hasMatch(address);
94 + }
95 +
96 + static String stripHtmlTags(String value) {
97 + return value
98 + .replaceAll(RegExp(r'[\u2028\u2029]'), '\n')
99 + .replaceAll(RegExp(r'<br\s*/?>', caseSensitive: false), '\n')
100 + .replaceAll(RegExp(r'</p\s*>', caseSensitive: false), '\n')
101 + .replaceAll(RegExp(r'<[^>]*>'), ' ')
102 + .trim();
103 + }
104 +}
lib/core/address_resolver/address_sources.dart new
+87
@@ -0,0 +1,87 @@
1 +import 'package:cake_wallet/core/address_validator.dart';
2 +import 'package:cw_core/crypto_currency.dart';
3 +
4 +enum AddressSource {
5 + twitter(label: 'X', iconPath: 'assets/new-ui/address_sources/x.svg', alias: '@username'),
6 + unstoppableDomains(
7 + label: 'Unstoppable Domains',
8 + iconPath: 'assets/new-ui/address_sources/unstoppable.svg',
9 + alias: 'domain.tld'),
10 + openAlias(
11 + label: 'OpenAlias',
12 + iconPath: 'assets/new-ui/address_sources/openalias.svg',
13 + alias: 'name.domain.tld'),
14 + yatRecord(label: 'Yat', iconPath: 'assets/new-ui/address_sources/yat.svg', alias: '🎂🎂🎂'),
15 + fio(label: 'FIO', iconPath: 'assets/new-ui/address_sources/fio.svg', alias: 'user@domain'),
16 + ens(
17 + label: 'Ethereum Name Service',
18 + iconPath: 'assets/new-ui/address_sources/ens.svg',
19 + alias: 'domain.eth'),
20 + mastodon(
21 + label: 'Mastodon',
22 + iconPath: 'assets/new-ui/address_sources/mastodon.svg',
23 + alias: '@username@domain.tld'),
24 + nostr(
25 + label: 'Nostr',
26 + iconPath: 'assets/new-ui/address_sources/nostr.svg',
27 + alias: 'user@domain.tld'),
28 + thorChain(
29 + label: 'ThorChain',
30 + iconPath: 'assets/new-ui/address_sources/icon-thorchain.svg',
31 + alias: 'name'),
32 + wellKnown(
33 + label: '.wellknown',
34 + iconPath: 'assets/new-ui/address_sources/wellknown.svg',
35 + alias: 'domain.tld'),
36 + zanoAlias(
37 + label: 'Zano Alias', iconPath: 'assets/new-ui/address_sources/zano.svg', alias: '@alias'),
38 + bip353(
39 + label: 'BIP353',
40 + iconPath: 'assets/new-ui/address_sources/bip353.svg',
41 + alias: 'user@domain.com'),
42 + zcashAddress(
43 + label: 'Zcash.me',
44 + iconPath: 'assets/new-ui/address_sources/icon-zcash-me.svg',
45 + alias: 'zcash.me/username'),
46 + zcashName(
47 + label: 'Zcash Names',
48 + iconPath: 'assets/new-ui/address_sources/icon-zcashnames.svg',
49 + alias: 'name.zec'),
50 + lnurlPay(
51 + label: 'LNURL Pay',
52 + iconPath: 'assets/new-ui/address_sources/icon-lnurl-pay.svg',
53 + alias: 'user@domain.com'),
54 + contact(label: 'Contact', iconPath: ''),
55 + notParsed(label: 'Unknown', iconPath: ''),
56 + ;
57 +
58 + const AddressSource({
59 + required this.label,
60 + required this.iconPath,
61 + this.alias = '',
62 + });
63 +
64 + final String label;
65 + final String iconPath;
66 + final String alias;
67 +}
68 +
69 +extension AddressSourceIndex on AddressSource {
70 + int get raw => index;
71 +
72 + static AddressSource fromRaw(int raw) =>
73 + AddressSource.values[raw.clamp(0, AddressSource.values.length - 1)];
74 +}
75 +
76 +extension AddressSourceNameParser on AddressSource {
77 + static AddressSource fromLabel(String? text) {
78 + if (text == null || text.trim().isEmpty) {
79 + return AddressSource.notParsed;
80 + }
81 + final needle = text.trim().toLowerCase();
82 + return AddressSource.values.firstWhere(
83 + (src) => src.label.toLowerCase() == needle,
84 + orElse: () => AddressSource.notParsed,
85 + );
86 + }
87 +}
lib/core/address_resolver/bip_353/bip_353_address_provider.dart new
+68
@@ -0,0 +1,68 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
3 +import 'package:cake_wallet/core/address_resolver/bip_353/bip_353_record.dart';
4 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
5 +import 'package:cake_wallet/store/settings_store.dart';
6 +import 'package:cw_core/crypto_currency.dart';
7 +import 'package:cw_core/utils/print_verbose.dart';
8 +import 'package:cw_core/wallet_base.dart';
9 +
10 +class Bip353AddressProvider extends AddressLookupProvider {
11 + @override
12 + AddressSource get source => AddressSource.bip353;
13 +
14 + @override
15 + List<CryptoCurrency> get supportedCurrencies => [CryptoCurrency.btc];
16 +
17 + @override
18 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsBip353;
19 +
20 + @override
21 + bool canHandle(String query) {
22 + final value = query.trim();
23 + return value.contains('@') && value.contains('.') && !value.startsWith('@');
24 + }
25 +
26 + @override
27 + Future<List<ParsedAddress>> resolve({
28 + required String query,
29 + required List<CryptoCurrency> currencies,
30 + required WalletBase wallet,
31 + }) async {
32 + try {
33 + final result = <CryptoCurrency, String>{};
34 + String? dnsProof;
35 +
36 + for (final cur in currencies) {
37 + final bip353AddressMap = await Bip353Record.fetchUriByCryptoCurrency(query, cur.title);
38 + if (bip353AddressMap == null || bip353AddressMap.isEmpty) continue;
39 +
40 + final spAddress = bip353AddressMap['sp'];
41 + final address = bip353AddressMap['address'];
42 + final chosenAddress = spAddress?.isNotEmpty == true ? spAddress : address;
43 +
44 + if (chosenAddress != null && chosenAddress.isNotEmpty) {
45 + result[cur] = chosenAddress;
46 + }
47 + }
48 +
49 + if (result.isEmpty) return [];
50 +
51 + try {
52 + dnsProof = await Bip353Record.fetchDnsProof(query);
53 + } catch (e) {
54 + printV('Bip353Record.fetchDnsProof error: $e');
55 + }
56 +
57 + return [ParsedAddress(
58 + parsedAddressByCurrencyMap: result,
59 + addressSource: AddressSource.bip353,
60 + handle: query,
61 + bip353DnsProof: dnsProof ?? '',
62 + )];
63 + } catch (e) {
64 + printV('[address resolver] Error resolving BIP353 address: $e');
65 + return [];
66 + }
67 + }
68 +}
\ No newline at end of file
lib/core/address_resolver/bip_353/bip_353_record.dart renamed
+1 -70
@@ -2,13 +2,9 @@ import 'dart:convert';
2 import 'dart:isolate';
3
4 import 'package:basic_utils/basic_utils.dart';
5 -import 'package:cake_wallet/generated/i18n.dart';
6 -import 'package:cake_wallet/src/widgets/alert_with_picker_option.dart';
7 -import 'package:cake_wallet/utils/show_pop_up.dart';
5 import 'package:cw_core/crypto_currency.dart';
6 import 'package:cw_core/utils/print_verbose.dart';
7 import 'package:dnssec_proof/dnssec_proof.dart';
11 -import 'package:flutter/material.dart';
8
9 class Bip353Record {
10 Bip353Record({
@@ -104,69 +100,4 @@ class Bip353Record {
100 }
101 }
102
107 - static Future<String?> pickBip353AddressChoice(
108 - BuildContext context,
109 - String bip353Name,
110 - Map<String, String> addressMap,
111 - ) async {
112 - if (addressMap.length == 1) {
113 - return addressMap.values.first;
114 - }
115 -
116 - final chosenAddress = await _showAddressChoiceDialog(context, bip353Name, addressMap);
117 -
118 - return chosenAddress;
119 - }
120 -
121 - static Future<String?> _showAddressChoiceDialog(
122 - BuildContext context,
123 - String bip353Name,
124 - Map<String, String> addressMap,
125 - ) async {
126 - final entriesList = addressMap.entries.toList();
127 - final List<Map<String, String>> displayItems = entriesList.map((entry) {
128 - final originalKey = entry.key;
129 - final originalValue = entry.value;
130 -
131 - final extendedKeyName = keyDisplayMap[originalKey] ?? originalKey;
132 - final truncatedValue = _truncate(originalValue, front: 6, back: 6);
133 -
134 - return {
135 - 'displayKey': extendedKeyName,
136 - 'displayValue': truncatedValue,
137 - 'originalKey': originalKey,
138 - 'originalValue': originalValue,
139 - };
140 - }).toList();
141 -
142 - String? selectedOriginalValue;
143 -
144 - if (context.mounted) {
145 - await showPopUp<void>(
146 - context: context,
147 - builder: (dialogContext) {
148 - return AlertWithPickerOption(
149 - alertTitle: S.of(context).multiple_addresses_detected + '\n$bip353Name',
150 - alertTitleTextSize: 14,
151 - alertSubtitle: S.of(context).please_choose_one + ':',
152 - options: displayItems,
153 - onOptionSelected: (Map<String, String> chosenItem) {
154 - selectedOriginalValue = chosenItem['originalValue'];
155 - },
156 - alertBarrierDismissible: true,
157 - );
158 - },
159 - );
160 - }
161 - return selectedOriginalValue;
162 - }
163 -
164 - static String _truncate(String value, {int front = 6, int back = 6}) {
165 - if (value.length <= front + back) return value;
166 -
167 - final start = value.substring(0, front);
168 - final end = value.substring(value.length - back);
169 - return '$start...$end';
170 - }
171 -
172 -}
103 +}
\ No newline at end of file
lib/core/address_resolver/ens/ens_address_provider.dart new
+49
@@ -0,0 +1,49 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
3 +import 'package:cake_wallet/core/address_resolver/ens/ens_record.dart';
4 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
5 +import 'package:cake_wallet/store/settings_store.dart';
6 +import 'package:cw_core/crypto_currency.dart';
7 +import 'package:cw_core/wallet_base.dart';
8 +
9 +class EnsAddressProvider extends AddressLookupProvider {
10 + @override
11 + AddressSource get source => AddressSource.ens;
12 +
13 + @override
14 + List<CryptoCurrency> get supportedCurrencies =>
15 + [CryptoCurrency.xmr, CryptoCurrency.btc, CryptoCurrency.eth];
16 +
17 + @override
18 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsENS;
19 +
20 + @override
21 + bool canHandle(String query) => query.endsWith('.eth'); // ENS handle example: name.eth
22 +
23 + @override
24 + Future<List<ParsedAddress>> resolve({
25 + required String query,
26 + required List<CryptoCurrency> currencies,
27 + required WalletBase wallet,
28 + }) async {
29 + final Map<CryptoCurrency, String> result = {};
30 +
31 + for (final cur in currencies) {
32 + final address = await EnsRecord.fetchEnsAddress(query, cur, wallet: wallet);
33 + if (address.isNotEmpty && address != "0x0000000000000000000000000000000000000000") {
34 + result[cur] = address;
35 + }
36 + }
37 +
38 + if (result.isNotEmpty) {
39 + return [
40 + ParsedAddress(
41 + parsedAddressByCurrencyMap: result,
42 + addressSource: AddressSource.ens,
43 + handle: query,
44 + )
45 + ];
46 + }
47 + return [];
48 + }
49 +}
lib/core/address_resolver/ens/ens_record.dart new
+55
@@ -0,0 +1,55 @@
1 +import 'package:cake_wallet/evm/evm.dart';
2 +import 'package:cw_core/crypto_currency.dart';
3 +import 'package:cw_core/utils/proxy_wrapper.dart';
4 +import 'package:cw_core/utils/print_verbose.dart';
5 +import 'package:cw_core/wallet_base.dart';
6 +import 'package:cw_core/wallet_type.dart';
7 +import 'package:ens_dart/ens_dart.dart';
8 +import 'package:web3dart/web3dart.dart';
9 +
10 +class EnsRecord {
11 + static Future<String> fetchEnsAddress(
12 + String name,
13 + CryptoCurrency cur, {
14 + WalletBase? wallet,
15 + }) async {
16 + Web3Client? _client;
17 +
18 + if (wallet?.type == WalletType.ethereum || wallet?.type == WalletType.polygon) {
19 + _client = evm!.getWeb3Client(wallet!);
20 + }
21 +
22 + _client ??= Web3Client(
23 + 'https://ethereum-rpc.publicnode.com',
24 + ProxyWrapper().getHttpIOClient(),
25 + );
26 +
27 + final ens = Ens(client: _client);
28 + final coinType = getEnsCoinType(cur);
29 +
30 + if (coinType == null) {
31 + printV('Unsupported currency for ENS: $cur');
32 + return '';
33 + }
34 +
35 + try {
36 + if (coinType == CoinType.ETH || coinType == CoinType.MATIC) {
37 + return (await ens.withName(name).getAddress()).hex;
38 + } else {
39 + return await ens.withName(name).getCoinAddress(coinType);
40 + }
41 + } catch (e) {
42 + printV(e);
43 + return '';
44 + }
45 + }
46 +
47 + static CoinType? getEnsCoinType(CryptoCurrency cur) => switch (cur) {
48 + CryptoCurrency.xmr => CoinType.XMR,
49 + CryptoCurrency.btc => CoinType.BTC,
50 + CryptoCurrency.ltc => CoinType.LTC,
51 + CryptoCurrency.eth => CoinType.ETH,
52 + CryptoCurrency.matic => CoinType.MATIC,
53 + _ => null,
54 + };
55 +}
\ No newline at end of file
lib/core/address_resolver/fio/fio_address_provider.dart new
+125
@@ -0,0 +1,125 @@
1 +import 'dart:convert';
2 +
3 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
4 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
5 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
6 +import 'package:cake_wallet/core/address_validator.dart';
7 +import 'package:cake_wallet/store/settings_store.dart';
8 +import 'package:cw_core/crypto_currency.dart';
9 +import 'package:cw_core/utils/print_verbose.dart';
10 +import 'package:cw_core/utils/proxy_wrapper.dart';
11 +import 'package:cw_core/wallet_base.dart';
12 +
13 +class FioAddressProvider extends AddressLookupProvider {
14 + @override
15 + AddressSource get source => AddressSource.fio;
16 +
17 + @override
18 + List<CryptoCurrency> get supportedCurrencies => AddressValidator.reliableValidateCurrencies;
19 +
20 + @override
21 + bool canHandle(String q) =>
22 + !q.startsWith('@') &&
23 + q.contains('@') &&
24 + !q.contains('.'); // FIO handle example: username@domain
25 +
26 + @override
27 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsFio;
28 +
29 + @override
30 + Future<List<ParsedAddress>> resolve({
31 + required String query,
32 + required List<CryptoCurrency> currencies,
33 + required WalletBase wallet,
34 + }) async {
35 + final Map<CryptoCurrency, String> result = {};
36 + try {
37 + final bool isFioRegistered = await FioAddressProvider.checkAvail(query);
38 + if (!isFioRegistered) return [];
39 +
40 + for (final cur in currencies) {
41 + final address = await FioAddressProvider.getPubAddress(query, cur.title);
42 + if (address != null && address.isNotEmpty) {
43 + result[cur] = address;
44 + }
45 + }
46 +
47 + if (result.isNotEmpty) {
48 + return [
49 + ParsedAddress(
50 + parsedAddressByCurrencyMap: result, addressSource: AddressSource.fio, handle: query)
51 + ];
52 + }
53 + return [];
54 + } catch (e) {
55 + printV('FioAddressProvider.resolve error: $e');
56 + return [];
57 + }
58 + }
59 +
60 + static const _apiAuthority = 'fio.blockpane.com';
61 + static const _availCheck = '/v1/chain/avail_check';
62 + static const _getAddress = '/v1/chain/get_pub_address';
63 +
64 + static Future<bool> checkAvail(String fioAddress) async {
65 + bool isFioRegistered = false;
66 + final headers = {'Content-Type': 'application/json'};
67 + final body = <String, String>{"fio_name": fioAddress};
68 +
69 + final uri = Uri.https(_apiAuthority, _availCheck);
70 + final response = await ProxyWrapper().post(
71 + clearnetUri: uri,
72 + headers: headers,
73 + body: json.encode(body),
74 + );
75 +
76 + if (response.statusCode != 200) {
77 + return isFioRegistered;
78 + }
79 +
80 + final responseJSON = json.decode(response.body) as Map<String, dynamic>;
81 + isFioRegistered = responseJSON['is_registered'] as int == 1;
82 +
83 + return isFioRegistered;
84 + }
85 +
86 + static Future<String?> getPubAddress(String fioAddress, String token) async {
87 + final headers = {'Content-Type': 'application/json'};
88 + final body = <String, String>{
89 + "fio_address": fioAddress,
90 + "chain_code": token.toUpperCase(),
91 + "token_code": token.toUpperCase(),
92 + };
93 +
94 + final uri = Uri.https(_apiAuthority, _getAddress);
95 + final response = await ProxyWrapper().post(
96 + clearnetUri: uri,
97 + headers: headers,
98 + body: json.encode(body),
99 + );
100 +
101 + final responseJSON = json.decode(response.body) as Map<String, dynamic>;
102 +
103 + if (response.statusCode == 400) {
104 + final error = responseJSON['error'] as String;
105 + final message = responseJSON['message'] as String;
106 + printV('${error}\n$message');
107 + return null;
108 + }
109 +
110 + if (response.statusCode != 200) {
111 + final String message = responseJSON['message'] as String? ?? 'Unknown error';
112 +
113 + printV('Error fetching public address for token $token: $message');
114 + return null;
115 + }
116 +
117 + final String pubAddress = responseJSON['public_address'] as String? ?? '';
118 +
119 + if (pubAddress.isNotEmpty) {
120 + return pubAddress;
121 + }
122 +
123 + return null;
124 + }
125 +}
lib/core/address_resolver/lnurl_pay/lnurl_pay_address_provider.dart new
+66
@@ -0,0 +1,66 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
3 +import 'package:cake_wallet/core/address_resolver/lnurl_pay/lnurlpay_record.dart';
4 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
5 +import 'package:cake_wallet/store/settings_store.dart';
6 +import 'package:cw_core/crypto_currency.dart';
7 +import 'package:cw_core/utils/print_verbose.dart';
8 +import 'package:cw_core/wallet_base.dart';
9 +import 'package:cw_core/wallet_type.dart';
10 +
11 +class LNUrlPayAddressProvider extends AddressLookupProvider {
12 + @override
13 + AddressSource get source => AddressSource.lnurlPay;
14 +
15 + @override
16 + List<CryptoCurrency> get supportedCurrencies => [CryptoCurrency.btcln];
17 +
18 + @override
19 + bool canHandle(String q) => q.contains('.') && q.contains('@'); // LNURL-pay handle example: user@domain.com
20 +
21 + @override
22 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsLNUrl;
23 +
24 + @override
25 + Future<List<ParsedAddress>> resolve({
26 + required String query,
27 + required List<CryptoCurrency> currencies,
28 + required WalletBase wallet,
29 + }) async {
30 + try {
31 + if (wallet.type != WalletType.bitcoin) return [];
32 +
33 + final formattedName = query.trim();
34 + if (formattedName.isEmpty) return [];
35 +
36 + final result = <CryptoCurrency, String>{};
37 +
38 + for (final currency in currencies) {
39 + if (!supportedCurrencies.contains(currency)) continue;
40 +
41 + final record = await LNUrlPayRecord.fetchAddressAndName(
42 + formattedName: formattedName,
43 + currency: currency,
44 + );
45 +
46 + if (record != null && record.address.isNotEmpty) {
47 + result[currency] = record.address;
48 + }
49 + }
50 +
51 + if (result.isEmpty) return [];
52 +
53 + return [
54 + ParsedAddress(
55 + parsedAddressByCurrencyMap: result,
56 + addressSource: AddressSource.lnurlPay,
57 + handle: formattedName,
58 + profileName: formattedName,
59 + ),
60 + ];
61 + } catch (e) {
62 + printV('[address resolver] Error resolving LNURL-pay address: $e');
63 + return [];
64 + }
65 + }
66 +}
\ No newline at end of file
lib/core/address_resolver/lnurl_pay/lnurlpay_record.dart renamed
lib/core/address_resolver/mastodon/mastodon_address_provider.dart new
+93
@@ -0,0 +1,93 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_resolver_utils.dart';
3 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
4 +import 'package:cake_wallet/core/address_resolver/mastodon/mastodon_api.dart';
5 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
6 +import 'package:cake_wallet/core/address_validator.dart';
7 +import 'package:cake_wallet/store/settings_store.dart';
8 +import 'package:cw_core/crypto_currency.dart';
9 +import 'package:cw_core/utils/print_verbose.dart';
10 +import 'package:cw_core/wallet_base.dart';
11 +
12 +class MastodonAddressProvider extends AddressLookupProvider {
13 + @override
14 + AddressSource get source => AddressSource.mastodon;
15 +
16 + @override
17 + List<CryptoCurrency> get supportedCurrencies => AddressValidator.reliableValidateCurrencies;
18 +
19 + @override
20 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsMastodon;
21 +
22 + @override
23 + bool canHandle(String query) =>
24 + query.startsWith('@') &&
25 + query.contains('@', 1) &&
26 + query.contains('.', 1); // Mastodon handle example: @username@hostname.xxx
27 +
28 + @override
29 + Future<List<ParsedAddress>> resolve({
30 + required String query,
31 + required List<CryptoCurrency> currencies,
32 + required WalletBase wallet,
33 + }) async {
34 + try {
35 + final subText = query.substring(1); // Remove '@' from the beginning of the handle
36 + final hostNameIndex = subText.indexOf('@');
37 + if (hostNameIndex <= 0 || hostNameIndex == subText.length - 1) return [];
38 +
39 + final userName = subText.substring(0, hostNameIndex);
40 + final hostName = subText.substring(hostNameIndex + 1);
41 +
42 + final result = <CryptoCurrency, String>{};
43 +
44 + final mastodonUser = await MastodonAPI.lookupUserByUserName(
45 + userName: userName,
46 + apiHost: hostName,
47 + );
48 + if (mastodonUser == null) return [];
49 +
50 + final bio = AddressResolverUtils.stripHtmlTags(mastodonUser.note);
51 + result.addAll(
52 + AddressResolverUtils.extractAddressesFromText(
53 + raw: bio,
54 + currencies: currencies,
55 + ),
56 + );
57 +
58 + final pinnedPosts = await MastodonAPI.getPinnedPosts(
59 + userId: mastodonUser.id,
60 + apiHost: hostName,
61 + );
62 +
63 + if (pinnedPosts.isNotEmpty) {
64 + final pinnedPostsText = pinnedPosts
65 + .map((item) => AddressResolverUtils.stripHtmlTags(item.content))
66 + .where((text) => text.isNotEmpty)
67 + .join('\n');
68 +
69 + result.addAll(
70 + AddressResolverUtils.extractAddressesFromText(
71 + raw: pinnedPostsText,
72 + currencies: currencies,
73 + ),
74 + );
75 + }
76 +
77 + if (result.isEmpty) return [];
78 +
79 + return [
80 + ParsedAddress(
81 + parsedAddressByCurrencyMap: result,
82 + addressSource: AddressSource.mastodon,
83 + handle: query,
84 + profileImageUrl: mastodonUser.profileImageUrl,
85 + profileName: mastodonUser.username,
86 + ),
87 + ];
88 + } catch (e) {
89 + printV('[address resolver] Error resolving Mastodon address: $e');
90 + return [];
91 + }
92 + }
93 +}
lib/core/address_resolver/mastodon/mastodon_api.dart renamed
+1 -1
@@ -1,7 +1,7 @@
1 import 'dart:convert';
2 +import 'package:cake_wallet/core/address_resolver/mastodon/mastodon_user.dart';
3 import 'package:cw_core/utils/proxy_wrapper.dart';
4 import 'package:cw_core/utils/print_verbose.dart';
4 -import 'package:cake_wallet/mastodon/mastodon_user.dart';
5
6 class MastodonAPI {
7 static const httpsScheme = 'https';
lib/core/address_resolver/mastodon/mastodon_user.dart renamed
lib/core/address_resolver/nostr/nostr_address_provider.dart new
+66
@@ -0,0 +1,66 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_resolver_utils.dart';
3 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
4 +import 'package:cake_wallet/core/address_resolver/nostr/nostr_api.dart';
5 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
6 +import 'package:cake_wallet/core/address_validator.dart';
7 +import 'package:cake_wallet/store/settings_store.dart';
8 +import 'package:cw_core/crypto_currency.dart';
9 +import 'package:cw_core/utils/print_verbose.dart';
10 +import 'package:cw_core/wallet_base.dart';
11 +
12 +class NostrAddressProvider extends AddressLookupProvider {
13 + @override
14 + AddressSource get source => AddressSource.nostr;
15 +
16 + @override
17 + List<CryptoCurrency> get supportedCurrencies => AddressValidator.reliableValidateCurrencies;
18 +
19 + @override
20 + bool canHandle(String q) =>
21 + AddressResolverUtils.isEmailFormat(q); // Nostr handle example: username@domain
22 +
23 + @override
24 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsNostr;
25 +
26 + @override
27 + Future<List<ParsedAddress>> resolve({
28 + required String query,
29 + required List<CryptoCurrency> currencies,
30 + required WalletBase wallet,
31 + }) async {
32 + try {
33 + final profile = await NostrProfileHandler.queryProfile(query);
34 + if (profile == null) return [];
35 +
36 + final data = await NostrProfileHandler.processRelays(profile, query);
37 + if (data == null) return [];
38 +
39 + final result = <CryptoCurrency, String>{};
40 +
41 + String queryTxt = data.about;
42 +
43 + for (final cur in currencies) {
44 + final addr = AddressResolverUtils.extractAddressByType(raw: queryTxt, type: cur);
45 + if (addr != null && addr.isNotEmpty) {
46 + result[cur] = addr;
47 + queryTxt = queryTxt.replaceFirst(addr, '');
48 + }
49 + }
50 + if (result.isEmpty) return [];
51 +
52 + return [
53 + ParsedAddress(
54 + parsedAddressByCurrencyMap: result,
55 + addressSource: AddressSource.nostr,
56 + handle: query,
57 + profileImageUrl: data.picture,
58 + profileName: data.name,
59 + )
60 + ];
61 + } catch (e) {
62 + printV('Error looking up Nostr profile: $e');
63 + return [];
64 + }
65 + }
66 +}
lib/core/address_resolver/nostr/nostr_api.dart new
+127
@@ -0,0 +1,127 @@
1 +import 'dart:async';
2 +import 'dart:convert';
3 +
4 +import 'package:cake_wallet/core/address_resolver/nostr/nostr_user.dart';
5 +import 'package:nostr_tools/nostr_tools.dart';
6 +import 'dart:async' show Completer, TimeoutException, runZonedGuarded;
7 +
8 +class NostrProfileHandler {
9 + static final relayToDomainMap = {
10 + 'relay.snort.social': 'snort.social',
11 + };
12 +
13 + static final Nip05 _nip05 = Nip05();
14 +
15 + static Future<ProfilePointer?> queryProfile(String nip05Address) async {
16 + final profile = await _nip05.queryProfile(nip05Address);
17 + if (profile?.pubkey != null && profile?.relays?.isNotEmpty == true) {
18 + return profile;
19 + }
20 + return null;
21 + }
22 +
23 + static Future<UserMetadata?> processRelays(
24 + ProfilePointer profile,
25 + String nip05Address,
26 + ) async {
27 + final userDomain = _extractDomain(nip05Address);
28 + const int metaKind = 0;
29 +
30 + // Domain-matched relays first
31 + for (final String relayUrl in profile.relays ?? []) {
32 + final relayDomain =
33 + relayToDomainMap[_getDomainFromRelayUrl(relayUrl)] ?? _getDomainFromRelayUrl(relayUrl);
34 +
35 + if (relayDomain == userDomain) {
36 + final data = await _fetchInfoFromRelay(relayUrl, profile.pubkey, [metaKind]);
37 + if (data != null) return data;
38 + }
39 + }
40 +
41 + // Then try every remaining relay
42 + for (final String relayUrl in profile.relays ?? []) {
43 + final data = await _fetchInfoFromRelay(relayUrl, profile.pubkey, [metaKind]);
44 + if (data != null) return data;
45 + }
46 +
47 + // Nothing found
48 + return null;
49 + }
50 +
51 + static const Duration _relayTimeout = Duration(seconds: 3);
52 +
53 + static Future<UserMetadata?> _fetchInfoFromRelay(
54 + String relayUrl, String userPubKey, List<int> kinds) async {
55 + // sanitize so obvious junk (like '#') doesn't reach connect()
56 + final clean = _sanitizeRelay(relayUrl);
57 + if (clean.isEmpty) return null;
58 +
59 + final result = Completer<UserMetadata?>();
60 +
61 + runZonedGuarded(() async {
62 + try {
63 + final relay = RelayApi(relayUrl: clean);
64 +
65 + final stream = await relay.connect().timeout(
66 + _relayTimeout,
67 + onTimeout: () {
68 + relay.close();
69 + throw TimeoutException('Relay connect timeout');
70 + },
71 + );
72 +
73 + relay.sub([
74 + Filter(kinds: kinds, authors: [userPubKey])
75 + ]);
76 +
77 + final sub = stream.listen((msg) {
78 + if (msg.type == 'EVENT' && !result.isCompleted) {
79 + try {
80 + final event = msg.message as Event;
81 + final jsonMap = json.decode(event.content) as Map<String, dynamic>;
82 + result.complete(UserMetadata.fromJson(jsonMap));
83 + } catch (_) {
84 + if (!result.isCompleted) result.complete(null);
85 + }
86 + }
87 + }, onError: (_) {
88 + if (!result.isCompleted) result.complete(null);
89 + }, onDone: () {
90 + if (!result.isCompleted) result.complete(null);
91 + });
92 +
93 + final value = await result.future.timeout(_relayTimeout, onTimeout: () => null);
94 + await sub.cancel();
95 + relay.close();
96 + if (!result.isCompleted) result.complete(value);
97 + } catch (_) {
98 + if (!result.isCompleted) result.complete(null);
99 + }
100 + }, (error, stack) {
101 + // swallow ALL async errors from the websocket layer (including "was not upgraded to websocket")
102 + if (!result.isCompleted) result.complete(null);
103 + });
104 +
105 + return result.future;
106 + }
107 +
108 + static String _sanitizeRelay(String url) {
109 + url = url.replaceFirst(RegExp(r'^https?://'), 'wss://');
110 + final uri = Uri.parse(url);
111 + return Uri(
112 + scheme: uri.scheme.isEmpty ? 'wss' : uri.scheme,
113 + host: uri.host,
114 + port: uri.hasPort ? uri.port : 443,
115 + ).toString();
116 + }
117 +
118 + static String _extractDomain(String nip05) => nip05.split('@').last;
119 +
120 + static String _getDomainFromRelayUrl(String url) {
121 + try {
122 + return Uri.parse(url).host;
123 + } catch (_) {
124 + return '';
125 + }
126 + }
127 +}
\ No newline at end of file
lib/core/address_resolver/nostr/nostr_user.dart renamed
lib/core/address_resolver/openalias/openalias_address_provider.dart new
+64
@@ -0,0 +1,64 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
3 +import 'package:cake_wallet/core/address_resolver/openalias/openalias_record.dart';
4 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
5 +import 'package:cake_wallet/store/settings_store.dart';
6 +import 'package:cw_core/crypto_currency.dart';
7 +import 'package:cw_core/utils/print_verbose.dart';
8 +import 'package:cw_core/wallet_base.dart';
9 +
10 +class OpenaliasAddressProvider extends AddressLookupProvider {
11 + @override
12 + AddressSource get source => AddressSource.openAlias;
13 +
14 + @override
15 + List<CryptoCurrency> get supportedCurrencies => [CryptoCurrency.xmr, CryptoCurrency.btc];
16 +
17 + @override
18 + bool canHandle(String q) {
19 + final formattedName = OpenaliasRecord.formatDomainName(q);
20 + return formattedName.contains(".");
21 + }
22 +
23 + @override
24 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsOpenAlias;
25 +
26 + @override
27 + Future<List<ParsedAddress>> resolve({
28 + required String query,
29 + required List<CryptoCurrency> currencies,
30 + required WalletBase wallet,
31 + }) async {
32 + try {
33 + final formatted = OpenaliasRecord.formatDomainName(query);
34 +
35 + final txtRecords = await OpenaliasRecord.lookupOpenAliasRecord(formatted);
36 + if (txtRecords == null) return [];
37 +
38 + final result = <CryptoCurrency, String>{};
39 +
40 + for (final cur in currencies) {
41 + final rec = OpenaliasRecord.fetchAddressAndName(
42 + formattedName: formatted,
43 + ticker: cur.title.toLowerCase(),
44 + txtRecord: txtRecords,
45 + );
46 +
47 + if (rec.address.isNotEmpty) result[cur] = rec.address;
48 + }
49 +
50 + if (result.isEmpty) return [];
51 +
52 + return [
53 + ParsedAddress(
54 + parsedAddressByCurrencyMap: result,
55 + addressSource: AddressSource.openAlias,
56 + handle: query,
57 + )
58 + ];
59 + } catch (e) {
60 + printV('[address resolver] Error resolving OpenAlias address: $e');
61 + return [];
62 + }
63 + }
64 +}
lib/core/address_resolver/openalias/openalias_record.dart new
+69
@@ -0,0 +1,69 @@
1 +import 'package:basic_utils/basic_utils.dart';
2 +import 'package:cw_core/utils/print_verbose.dart';
3 +
4 +class OpenaliasRecord {
5 + OpenaliasRecord({
6 + required this.address,
7 + required this.name,
8 + required this.description,
9 + });
10 +
11 + final String name;
12 + final String address;
13 + final String description;
14 +
15 + static String formatDomainName(String name) {
16 + String formattedName = name;
17 +
18 + if (name.contains("@")) {
19 + formattedName = name.replaceAll("@", ".");
20 + }
21 +
22 + return formattedName;
23 + }
24 +
25 + static Future<List<RRecord>?> lookupOpenAliasRecord(String name) async {
26 + try {
27 + final txtRecord = await DnsUtils.lookupRecord(name, RRecordType.TXT, dnssec: true);
28 +
29 + return txtRecord;
30 + } catch (e) {
31 + printV("${e.toString()}");
32 + return null;
33 + }
34 + }
35 +
36 + static OpenaliasRecord fetchAddressAndName({
37 + required String formattedName,
38 + required String ticker,
39 + required List<RRecord> txtRecord,
40 + }) {
41 + var address = '';
42 + var name = formattedName;
43 + var note = '';
44 +
45 + final addrRe = RegExp(r'recipient_address=([^;\s]+)', caseSensitive: false);
46 + final nameRe = RegExp(r'recipient_name=([^;]+)', caseSensitive: false);
47 + final noteRe = RegExp(r'tx_description=([^;]+)', caseSensitive: false);
48 + final tag = 'oa1:$ticker';
49 +
50 + for (final rr in txtRecord) {
51 + final txt = rr.data.replaceAll('"', '');
52 +
53 + if (!txt.toLowerCase().contains(tag)) continue;
54 +
55 + final addrM = addrRe.firstMatch(txt);
56 + if (addrM != null) address = addrM.group(1)!;
57 +
58 + final nameM = nameRe.firstMatch(txt);
59 + if (nameM != null) name = nameM.group(1)!.trim();
60 +
61 + final noteM = noteRe.firstMatch(txt);
62 + if (noteM != null) note = noteM.group(1)!.trim();
63 +
64 + break;
65 + }
66 +
67 + return OpenaliasRecord(address: address, name: name, description: note);
68 + }
69 +}
\ No newline at end of file
lib/core/address_resolver/parsed_address.dart new
+22
@@ -0,0 +1,22 @@
1 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
2 +import 'package:cw_core/crypto_currency.dart';
3 +
4 +class ParsedAddress {
5 + const ParsedAddress({
6 + required this.parsedAddressByCurrencyMap,
7 + this.addressSource = AddressSource.notParsed,
8 + this.handle = '',
9 + this.profileImageUrl = '',
10 + this.profileName = '',
11 + this.description = '',
12 + this.bip353DnsProof,
13 + });
14 +
15 + final Map<CryptoCurrency, String> parsedAddressByCurrencyMap;
16 + final AddressSource addressSource;
17 + final String handle;
18 + final String profileImageUrl;
19 + final String profileName;
20 + final String description;
21 + final String? bip353DnsProof;
22 +}
\ No newline at end of file
lib/core/address_resolver/thorchain/thorchain_address_provider.dart new
+65
@@ -0,0 +1,65 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_resolver_utils.dart';
3 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
4 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
5 +import 'package:cake_wallet/core/address_validator.dart';
6 +import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
7 +import 'package:cake_wallet/store/settings_store.dart';
8 +import 'package:cw_core/crypto_currency.dart';
9 +import 'package:cw_core/utils/print_verbose.dart';
10 +import 'package:cw_core/wallet_base.dart';
11 +
12 +class ThorchainAddressProvider extends AddressLookupProvider {
13 + @override
14 + AddressSource get source => AddressSource.thorChain;
15 +
16 + @override
17 + List<CryptoCurrency> get supportedCurrencies => AddressValidator.reliableValidateCurrencies;
18 +
19 + @override
20 + bool canHandle(String q) => q.isNotEmpty; // can be any string
21 +
22 + @override
23 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsThorChain;
24 +
25 + @override
26 + Future<List<ParsedAddress>> resolve({
27 + required String query,
28 + required List<CryptoCurrency> currencies,
29 + required WalletBase wallet,
30 + }) async {
31 + try {
32 + final isNormalAddress = currencies.any((cur) =>
33 + AddressResolverUtils.extractAddressByType(raw: query, type: cur)?.isNotEmpty ?? false);
34 + if (query.length > 30 || isNormalAddress) return [];
35 +
36 + final map = await ThorChainExchangeProvider.lookupAddressByName(query);
37 + if (map == null || map.isEmpty) return [];
38 +
39 + final result = <CryptoCurrency, String>{};
40 +
41 + for (final cur in currencies) {
42 + final key = cur.title.toUpperCase();
43 + final addr = map[key];
44 + final runeAddr = cur.title.toUpperCase() == 'RUNE' ? map['THOR'] : null;
45 + final resolvedAddress = addr ?? runeAddr;
46 + if (resolvedAddress != null && resolvedAddress.isNotEmpty) {
47 + if (!result.containsValue(resolvedAddress)) result[cur] = resolvedAddress;
48 + }
49 + }
50 +
51 + if (result.isEmpty) return [];
52 +
53 + return [
54 + ParsedAddress(
55 + parsedAddressByCurrencyMap: result,
56 + addressSource: AddressSource.thorChain,
57 + handle: query,
58 + )
59 + ];
60 + } catch (e) {
61 + printV('[address resolver] Error resolving address: $e');
62 + return [];
63 + }
64 + }
65 +}
lib/core/address_resolver/twitter/twitter_address_provider.dart new
+97
@@ -0,0 +1,97 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_resolver_utils.dart';
3 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
4 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
5 +import 'package:cake_wallet/core/address_resolver/twitter/twitter_api.dart';
6 +import 'package:cake_wallet/core/address_resolver/twitter/twitter_user.dart';
7 +import 'package:cake_wallet/core/address_validator.dart';
8 +import 'package:cake_wallet/store/settings_store.dart';
9 +import 'package:cw_core/crypto_currency.dart';
10 +import 'package:cw_core/utils/print_verbose.dart';
11 +import 'package:cw_core/wallet_base.dart';
12 +
13 +class TwitterAddressProvider extends AddressLookupProvider {
14 +
15 + @override
16 + AddressSource get source => AddressSource.twitter;
17 +
18 + @override
19 + List<CryptoCurrency> get supportedCurrencies => AddressValidator.reliableValidateCurrencies;
20 +
21 + @override
22 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsTwitter;
23 +
24 + @override
25 + bool canHandle(String query) =>
26 + query.startsWith('@') && !query.substring(1).contains('@'); // x handle example: @username
27 +
28 + /// Resolves a Twitter handle to a list of ParsedAddress objects by extracting potential cryptocurrency addresses
29 + /// from the user's, profile.
30 + /// fallback to resolving Unstoppable Domains if no addresses are found in each of the Twitter user's bio, location, and pinned tweet.
31 +
32 + @override
33 + Future<List<ParsedAddress>> resolve({
34 + required String query,
35 + required List<CryptoCurrency> currencies,
36 + required WalletBase wallet,
37 + }) async {
38 + final formattedName = query.replaceFirst("@", "");
39 + try {
40 + final result = <CryptoCurrency, String>{};
41 +
42 + final twitterUser = await TwitterApi.lookupUserByName(userName: formattedName);
43 +
44 + final bio = (text: twitterUser.description, whitespaces: true);
45 + final location = (text: twitterUser.location, whitespaces: false);
46 + final pinnedTweet = (text: twitterUser.pinnedTweet?.text ?? '', whitespaces: true);
47 +
48 + final twitterTexts = [
49 + bio,
50 + location,
51 + pinnedTweet,
52 + ].where((item) => item.text.isNotEmpty);
53 +
54 + for (final item in twitterTexts) {
55 + result.addAll(
56 + AddressResolverUtils.extractAddressesFromText(
57 + raw: item.text,
58 + currencies: currencies,
59 + requireSurroundingWhitespaces: item.whitespaces,
60 + ),
61 + );
62 +
63 + if (result.isEmpty) {
64 + final domainResult = await AddressResolverUtils.resolveUnstoppableDomainFromText(
65 + raw: item.text,
66 + currencies: currencies,
67 + );
68 +
69 + if (domainResult.isNotEmpty) {
70 + return [_buildTwitterParsedAddress(query, twitterUser, domainResult)];
71 + }
72 + }
73 + }
74 +
75 + if (result.isEmpty) return [];
76 +
77 + return [_buildTwitterParsedAddress(query, twitterUser, result)];
78 + } catch (e) {
79 + printV('[address resolver service] Twitter error: $e');
80 + return [];
81 + }
82 + }
83 +
84 + ParsedAddress _buildTwitterParsedAddress(
85 + String query,
86 + TwitterUser twitterUser,
87 + Map<CryptoCurrency, String> result,
88 + ) {
89 + return ParsedAddress(
90 + parsedAddressByCurrencyMap: result,
91 + addressSource: AddressSource.twitter,
92 + handle: query,
93 + profileImageUrl: twitterUser.profileImageUrl,
94 + profileName: twitterUser.name,
95 + );
96 + }
97 +}
lib/core/address_resolver/twitter/twitter_api.dart renamed
+1 -1
@@ -1,7 +1,7 @@
1 import 'dart:convert';
2
3 import 'package:cake_wallet/.secrets.g.dart' as secrets;
4 -import 'package:cake_wallet/twitter/twitter_user.dart';
4 +import 'package:cake_wallet/core/address_resolver/twitter/twitter_user.dart';
5 import 'package:cw_core/utils/proxy_wrapper.dart';
6
7 class TwitterApi {
lib/core/address_resolver/twitter/twitter_user.dart renamed
lib/core/address_resolver/unstoppable/unstoppable_address_provider.dart new
+235
@@ -0,0 +1,235 @@
1 +import 'dart:convert';
2 +
3 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
4 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
5 +import 'package:cake_wallet/core/address_resolver/openalias/openalias_record.dart';
6 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
7 +import 'package:cake_wallet/store/settings_store.dart';
8 +import 'package:cw_core/crypto_currency.dart';
9 +import 'package:cw_core/utils/print_verbose.dart';
10 +import 'package:cw_core/utils/proxy_wrapper.dart';
11 +import 'package:cw_core/wallet_base.dart';
12 +
13 +class UnstoppableAddressProvider extends AddressLookupProvider {
14 + static const unstoppableDomains = [
15 + "888",
16 + "academy",
17 + "agency",
18 + "altimist",
19 + "anime",
20 + "austin",
21 + "bald",
22 + "bay",
23 + "benji",
24 + "bet",
25 + "binanceus",
26 + "bitcoin",
27 + "bitget",
28 + "bitscrunch",
29 + "blockchain",
30 + "boomer",
31 + "boston",
32 + "ca",
33 + "caw",
34 + "cc",
35 + "chat",
36 + "chomp",
37 + "clay",
38 + "club",
39 + "co",
40 + "com",
41 + "company",
42 + "crypto",
43 + "dao",
44 + "design",
45 + "dfz",
46 + "digital",
47 + "doga",
48 + "donut",
49 + "dream",
50 + "email",
51 + "emir",
52 + "eth",
53 + "ethermail",
54 + "family",
55 + "farms",
56 + "finance",
57 + "fun",
58 + "fyi",
59 + "games",
60 + "global",
61 + "go",
62 + "group",
63 + "guru",
64 + "hi",
65 + "hockey",
66 + "host",
67 + "info",
68 + "io",
69 + "klever",
70 + "kresus",
71 + "kryptic",
72 + "lfg",
73 + "life",
74 + "live",
75 + "llc",
76 + "ltc",
77 + "ltd",
78 + "manga",
79 + "me",
80 + "media",
81 + "metropolis",
82 + "miami",
83 + "miku",
84 + "money",
85 + "moon",
86 + "mumu",
87 + "net",
88 + "network",
89 + "news",
90 + "nft",
91 + "npc",
92 + "onchain",
93 + "online",
94 + "org",
95 + "podcast",
96 + "pog",
97 + "polygon",
98 + "base",
99 + "arbitrum",
100 + "press",
101 + "privacy",
102 + "pro",
103 + "propykeys",
104 + "pudgy",
105 + "pw",
106 + "quantum",
107 + "rad",
108 + "raiin",
109 + "retardio",
110 + "rip",
111 + "rocks",
112 + "secret",
113 + "services",
114 + "site",
115 + "smobler",
116 + "social",
117 + "solutions",
118 + "space",
119 + "stepn",
120 + "store",
121 + "studio",
122 + "systems",
123 + "tball",
124 + "tea",
125 + "team",
126 + "tech",
127 + "technology",
128 + "today",
129 + "tribe",
130 + "u",
131 + "ubu",
132 + "uno",
133 + "unstoppable",
134 + "vip",
135 + "wallet",
136 + "website",
137 + "wif",
138 + "wifi",
139 + "witg",
140 + "work",
141 + "world",
142 + "wrkx",
143 + "wtf",
144 + "x",
145 + "xmr",
146 + "xyz",
147 + "zil",
148 + "zone",
149 + "pizza"
150 + ];
151 +
152 + @override
153 + AddressSource get source => AddressSource.unstoppableDomains;
154 +
155 + @override
156 + List<CryptoCurrency> get supportedCurrencies => [CryptoCurrency.xmr, CryptoCurrency.btc];
157 +
158 + @override
159 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsUnstoppableDomains;
160 +
161 + @override
162 + bool canHandle(String query) {
163 + // Unstoppable Domains handle example: name.crypto
164 + final formattedName = OpenaliasRecord.formatDomainName(query);
165 + final domainParts = formattedName.split('.');
166 + final name = domainParts.last;
167 + return domainParts.length > 1 &&
168 + domainParts.first.isNotEmpty &&
169 + name.isNotEmpty &&
170 + unstoppableDomains.any((domain) => name.trim() == domain);
171 + }
172 +
173 + @override
174 + Future<List<ParsedAddress>> resolve({
175 + required String query,
176 + required List<CryptoCurrency> currencies,
177 + required WalletBase wallet,
178 + }) async {
179 + try {
180 + final formattedName = OpenaliasRecord.formatDomainName(query);
181 + final result = <CryptoCurrency, String>{};
182 +
183 + for (final currency in currencies) {
184 + final address = await fetchUnstoppableDomainAddress(
185 + formattedName,
186 + currency.title,
187 + );
188 +
189 + if (address.isNotEmpty) {
190 + result[currency] = address;
191 + }
192 + }
193 +
194 + if (result.isEmpty) return [];
195 +
196 + return [
197 + ParsedAddress(
198 + parsedAddressByCurrencyMap: result,
199 + profileName: formattedName,
200 + addressSource: AddressSource.unstoppableDomains,
201 + handle: formattedName,
202 + ),
203 + ];
204 + } catch (e) {
205 + printV('[address resolver] Error resolving Unstoppable Domain: $e');
206 + return [];
207 + }
208 + }
209 +
210 + static Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async {
211 + var address = '';
212 +
213 + try {
214 + final uri = Uri.parse("https://api.unstoppabledomains.com/profile/public/${Uri.encodeQueryComponent(domain)}?fields=records");
215 + final response = await ProxyWrapper().get(clearnetUri: uri);
216 +
217 + final jsonParsed = json.decode(response.body) as Map<String, dynamic>;
218 + if (jsonParsed["records"] == null) {
219 + throw Exception(".records response from $uri is empty");
220 + };
221 + final records = jsonParsed["records"] as Map<String, dynamic>;
222 + final key = "crypto.${ticker.toUpperCase()}.address";
223 + if (records[key] == null) {
224 + throw Exception(".records.${key} response from $uri is empty");
225 + }
226 +
227 + return records[key] as String? ?? '';
228 + } catch (e) {
229 + printV('Unstoppable domain error: ${e.toString()}');
230 + address = '';
231 + }
232 +
233 + return address;
234 + }
235 +}
lib/core/address_resolver/wellknown/wellknown_address_provider.dart new
+49
@@ -0,0 +1,49 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
3 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
4 +import 'package:cake_wallet/core/address_resolver/wellknown/wellknown_record.dart';
5 +import 'package:cake_wallet/store/settings_store.dart';
6 +import 'package:cw_core/crypto_currency.dart';
7 +import 'package:cw_core/utils/print_verbose.dart';
8 +import 'package:cw_core/wallet_base.dart';
9 +
10 +class WellKnownAddressProvider extends AddressLookupProvider {
11 + @override
12 + AddressSource get source => AddressSource.wellKnown;
13 +
14 + @override
15 + List<CryptoCurrency> get supportedCurrencies => [CryptoCurrency.nano];
16 +
17 + @override
18 + bool canHandle(String q) => q.contains('.') && q.contains('@');
19 +
20 + // .well-known handle example:
21 +
22 + @override
23 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsWellKnown;
24 +
25 + @override
26 + Future<List<ParsedAddress>> resolve({
27 + required String query,
28 + required List<CryptoCurrency> currencies,
29 + required WalletBase wallet,
30 + }) async {
31 + try {
32 + final rec = await WellKnownRecord.fetch(query, CryptoCurrency.nano);
33 + if (rec == null || rec.address.isEmpty) return [];
34 +
35 + return [
36 + ParsedAddress(
37 + parsedAddressByCurrencyMap: {CryptoCurrency.nano: rec.address},
38 + addressSource: AddressSource.wellKnown,
39 + handle: query,
40 + profileName: rec.title ?? '',
41 + profileImageUrl: rec.imageUrl ?? '',
42 + )
43 + ];
44 + } catch (e) {
45 + printV('[address resolver] Error resolving Well-Known address: $e');
46 + return [];
47 + }
48 + }
49 +}
lib/core/address_resolver/wellknown/wellknown_record.dart new
+78
@@ -0,0 +1,78 @@
1 +import 'dart:convert';
2 +
3 +import 'package:cw_core/crypto_currency.dart';
4 +import 'package:cw_core/utils/print_verbose.dart';
5 +import 'package:cw_core/utils/proxy_wrapper.dart';
6 +
7 +class WellKnownRecord {
8 + WellKnownRecord({
9 + required this.address,
10 + required this.alias,
11 + required this.imageUrl,
12 + required this.title,
13 + });
14 +
15 + final String address;
16 + final String alias;
17 + final String? imageUrl;
18 + final String? title;
19 +
20 + static const _jsonLocation = {CryptoCurrency.nano: 'nano-currency'};
21 +
22 + static Future<WellKnownRecord?> fetch(
23 + String username,
24 + CryptoCurrency currency,
25 + ) async {
26 + final location = _jsonLocation[currency];
27 + if (location == null) {
28 + printV('well-known: unsupported coin $currency');
29 + return null;
30 + }
31 +
32 + final parts = username.split('@');
33 + if (parts.length < 2) {
34 + printV('well-known: missing @ in "$username"');
35 + return null;
36 + }
37 + final alias = (parts.length == 3 ? parts[1] : parts[0]).trim();
38 + final domain = parts.last.trim();
39 + final encoded = Uri.encodeComponent(alias.isEmpty ? '_' : alias);
40 +
41 + final uri = Uri.https(
42 + domain,
43 + '/.well-known/$location.json',
44 + {'names': encoded},
45 + );
46 +
47 + try {
48 + final res = await ProxyWrapper().get(
49 + clearnetUri: uri,
50 + headers: {'Accept': 'application/json'},
51 + );
52 +
53 + final cType = res.headers['content-type']?.toLowerCase() ?? '';
54 + if (res.statusCode != 200 || !cType.contains('application/json')) {
55 + printV('well-known: $uri → ${res.statusCode} $cType');
56 + return null;
57 + }
58 +
59 + final body = json.decode(res.body) as Map<String, dynamic>;
60 + final items = body['names'] as List<dynamic>? ?? const [];
61 +
62 + for (final raw in items.whereType<Map>()) {
63 + if (raw['name'].toString().toLowerCase() == alias.toLowerCase()) {
64 + return WellKnownRecord(
65 + address: raw['address']?.toString() ?? '',
66 + alias: alias,
67 + imageUrl: raw['image']?.toString(),
68 + title: raw['name']?.toString(),
69 + );
70 + }
71 + }
72 + printV('well-known: alias "$alias" not found in list');
73 + } catch (e) {
74 + printV('well-known: network / JSON error → $e');
75 + }
76 + return null;
77 + }
78 +}
\ No newline at end of file
lib/core/address_resolver/yat/yat_address_provider.dart new
+59
@@ -0,0 +1,59 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
3 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
4 +import 'package:cake_wallet/core/address_resolver/yat/yat_service.dart';
5 +import 'package:cake_wallet/entities/emoji_string_extension.dart';
6 +import 'package:cake_wallet/store/settings_store.dart';
7 +import 'package:cw_core/crypto_currency.dart';
8 +import 'package:cw_core/utils/print_verbose.dart';
9 +import 'package:cw_core/wallet_base.dart';
10 +
11 +class YatAddressProvider extends AddressLookupProvider {
12 + @override
13 + AddressSource get source => AddressSource.yatRecord;
14 +
15 + @override
16 + List<CryptoCurrency> get supportedCurrencies =>
17 + [CryptoCurrency.xmr, CryptoCurrency.btc, CryptoCurrency.eth, CryptoCurrency.ltc];
18 +
19 + @override
20 + bool canHandle(String q) => q.hasOnlyEmojis; // Yat handle example: 🐶🐾
21 +
22 + @override
23 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsWellKnown;
24 +
25 + @override
26 + Future<List<ParsedAddress>> resolve({
27 + required String query,
28 + required List<CryptoCurrency> currencies,
29 + required WalletBase wallet,
30 + }) async {
31 + try {
32 + final result = <CryptoCurrency, String>{};
33 +
34 + for (final cur in currencies) {
35 + final records = await YatService.fetchYatAddress(query, cur.title);
36 + if (records.isEmpty) continue;
37 +
38 + final chosen = cur == CryptoCurrency.xmr
39 + ? records.firstWhere((r) => r.isMoneroSub, orElse: () => records.first)
40 + : records.first;
41 +
42 + result[cur] = chosen.address;
43 + }
44 +
45 + if (result.isEmpty) return [];
46 +
47 + return [
48 + ParsedAddress(
49 + parsedAddressByCurrencyMap: result,
50 + addressSource: AddressSource.yatRecord,
51 + handle: query,
52 + )
53 + ];
54 + } catch (e) {
55 + printV('[address resolver] Error resolving Yat address: $e');
56 + return [];
57 + }
58 + }
59 +}
lib/core/address_resolver/yat/yat_record.dart new
+19
@@ -0,0 +1,19 @@
1 +import 'package:cake_wallet/core/address_resolver/yat/yat_service.dart';
2 +
3 +class YatRecord {
4 + YatRecord({
5 + required this.address,
6 + required this.tag,
7 + });
8 +
9 + final String address;
10 + final String tag;
11 +
12 + factory YatRecord.fromJson(Map<String, dynamic> json, String tag) =>
13 + YatRecord(address: (json['address'] ?? '').toString(), tag: tag);
14 +
15 + bool get isMoneroSub => tag == YatService.MONERO_SUB_ADDRESS;
16 +
17 + @override
18 + String toString() => 'YatRecord(tag: $tag, address: $address)';
19 +}
\ No newline at end of file
lib/core/address_resolver/yat/yat_service.dart new
+67
@@ -0,0 +1,67 @@
1 +import 'dart:convert';
2 +
3 +import 'package:cake_wallet/core/address_resolver/yat/yat_record.dart';
4 +import 'package:cw_core/utils/proxy_wrapper.dart';
5 +
6 +class YatService {
7 + static bool isDevMode = false;
8 +
9 + static String get apiUrl =>
10 + YatService.isDevMode ? YatService.apiDevUrl : YatService.apiReleaseUrl;
11 + static const apiReleaseUrl = "https://a.y.at";
12 + static const apiDevUrl = 'https://a.yat.fyi';
13 +
14 + static String lookupEmojiUrl(String emojiId) =>
15 + "$apiUrl/emoji_id/$emojiId/payment";
16 +
17 + static const String MONERO_SUB_ADDRESS = '0x1002';
18 + static const String MONERO_STD_ADDRESS = '0x1001';
19 + static const tags = {
20 + 'XMR': "$MONERO_STD_ADDRESS,$MONERO_SUB_ADDRESS",
21 + 'BTC': '0x1003',
22 + 'LTC': '0x1019'
23 + };
24 +
25 + static Future<List<YatRecord>> fetchYatAddress(
26 + String emojiId,
27 + String ticker,
28 + ) async {
29 + final tagQuery = tags[ticker.toUpperCase()];
30 + if (tagQuery == null) return const [];
31 +
32 + final uri = Uri.parse(
33 + lookupEmojiUrl(emojiId.replaceAll(' ', '')),
34 + ).replace(queryParameters: {'tags': tagQuery});
35 +
36 + try {
37 + final response = await ProxyWrapper().get(clearnetUri: uri);
38 + final body = json.decode(response.body) as Map<String, dynamic>;
39 +
40 + final res = body['result'];
41 + if (res == null) return const [];
42 +
43 + final records = <YatRecord>[];
44 +
45 + if (res is Map) {
46 + res.forEach((tag, data) {
47 + if (data is Map<String, dynamic>) {
48 + records.add(YatRecord.fromJson(data, tag.toString()));
49 + }
50 + });
51 + }
52 +
53 + else if (res is List) {
54 + for (final item in res) {
55 + if (item is Map<String, dynamic>) {
56 + final tag = item['tag']?.toString() ?? '';
57 + records.add(YatRecord.fromJson(item, tag));
58 + }
59 + }
60 + }
61 +
62 + return records;
63 + } catch (_) {
64 + return const [];
65 + }
66 + }
67 +}
\ No newline at end of file
lib/core/address_resolver/yat/yat_store.dart renamed
lib/core/address_resolver/zano/zano_alias_address_provider.dart new
+75
@@ -0,0 +1,75 @@
1 +import 'dart:convert';
2 +
3 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
4 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
5 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
6 +import 'package:cake_wallet/store/settings_store.dart';
7 +import 'package:cw_core/crypto_currency.dart';
8 +import 'package:cw_core/utils/print_verbose.dart';
9 +import 'package:cw_core/utils/proxy_wrapper.dart';
10 +import 'package:cw_core/wallet_base.dart';
11 +
12 +class ZanoAliasAddressProvider extends AddressLookupProvider {
13 +
14 + @override
15 + AddressSource get source => AddressSource.zanoAlias;
16 +
17 + @override
18 + List<CryptoCurrency> get supportedCurrencies => [CryptoCurrency.zano];
19 +
20 + @override
21 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsZanoAlias;
22 +
23 + @override
24 + bool canHandle(String query) => query.startsWith('@'); // zano handle example: @username
25 +
26 + static const _zanoUrl = 'http://37.27.100.59:10500/json_rpc';
27 + @override
28 + Future<List<ParsedAddress>> resolve({
29 + required String query,
30 + required List<CryptoCurrency> currencies,
31 + required WalletBase wallet,
32 + }) async {
33 + try {
34 + if (!currencies.contains(CryptoCurrency.zano)) return [];
35 +
36 + final formattedName = query.replaceFirst("@", "");
37 + if (formattedName.isEmpty) return [];
38 +
39 + final zanoAddress = await fetchZanoAliasAddress(formattedName);
40 + if (zanoAddress == null || zanoAddress.isEmpty) return [];
41 +
42 + return [
43 + ParsedAddress(
44 + parsedAddressByCurrencyMap: {CryptoCurrency.zano: zanoAddress},
45 + addressSource: AddressSource.zanoAlias,
46 + handle: query,
47 + ),
48 + ];
49 + } catch (e) {
50 + printV('[address resolver] Error resolving Zano Alias: $e');
51 + return [];
52 + }
53 + }
54 +
55 + static Future<String?> fetchZanoAliasAddress(String alias) async {
56 + try {
57 + final uri = Uri.parse(_zanoUrl);
58 + final response = await ProxyWrapper().post(
59 + clearnetUri: uri,
60 + body: json.encode({
61 + 'id': 0,
62 + 'jsonrpc': '2.0',
63 + 'method': 'get_alias_details',
64 + 'params': {'alias': alias},
65 + }),
66 + );
67 +
68 + final jsonParsed = json.decode(response.body) as Map<String, dynamic>;
69 + return jsonParsed['result']?['alias_details']?['address'] as String?;
70 + } catch (e) {
71 + printV('[address resolver] Zano Alias error: $e');
72 + return null;
73 + }
74 + }
75 +}
lib/core/address_resolver/zcash/zcash_address_provider.dart new
+49
@@ -0,0 +1,49 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
3 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
4 +import 'package:cake_wallet/core/address_resolver/zcash/zcash_names_record.dart';
5 +import 'package:cake_wallet/store/settings_store.dart';
6 +import 'package:cw_core/crypto_currency.dart';
7 +import 'package:cw_core/utils/print_verbose.dart';
8 +import 'package:cw_core/wallet_base.dart';
9 +
10 +class ZcashNameAddressProvider extends AddressLookupProvider {
11 + @override
12 + AddressSource get source => AddressSource.zcashName;
13 +
14 + @override
15 + List<CryptoCurrency> get supportedCurrencies => [CryptoCurrency.zec];
16 +
17 + @override
18 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsZcashNames;
19 +
20 + @override
21 + bool canHandle(String query) {
22 + final lowerText = query.toLowerCase();
23 + return lowerText.endsWith(".zec") || lowerText.endsWith(".zcash");
24 + } // Zcash handle example: name.zec
25 +
26 + @override
27 + Future<List<ParsedAddress>> resolve({
28 + required String query,
29 + required List<CryptoCurrency> currencies,
30 + required WalletBase wallet,
31 + }) async {
32 + try {
33 + final address = await ZcashNamesRecord.fetchZcashNamesAddress(query);
34 + if (address == null || address.isEmpty) return [];
35 +
36 + return [
37 + ParsedAddress(
38 + parsedAddressByCurrencyMap: {CryptoCurrency.zec: address},
39 + profileName: query,
40 + addressSource: AddressSource.zcashName,
41 + handle: query,
42 + ),
43 + ];
44 + } catch (e) {
45 + printV('[address resolver] Error resolving Zcash Name: $e');
46 + return [];
47 + }
48 + }
49 +}
lib/core/address_resolver/zcash/zcash_me_address_provider.dart new
+74
@@ -0,0 +1,74 @@
1 +import 'package:cake_wallet/core/address_resolver/address_lookup_provider.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
3 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
4 +import 'package:cake_wallet/store/settings_store.dart';
5 +import 'package:cw_core/crypto_currency.dart';
6 +import 'package:cw_core/utils/print_verbose.dart';
7 +import 'package:cw_core/utils/proxy_wrapper.dart';
8 +import 'package:cw_core/wallet_base.dart';
9 +
10 +class ZcashMeAddressProvider extends AddressLookupProvider {
11 + @override
12 + AddressSource get source => AddressSource.zcashAddress;
13 +
14 + @override
15 + List<CryptoCurrency> get supportedCurrencies => [CryptoCurrency.zec];
16 +
17 + @override
18 + bool isEnabled(SettingsStore settingsStore) => settingsStore.lookupsZcashNames;
19 +
20 + @override
21 + bool canHandle(String query) {
22 + final value = query.trim().toLowerCase();
23 + return value.startsWith('zcash.me/') && value.split('/').last.isNotEmpty;
24 + } // zcash.me profile example: zcash.me/username
25 +
26 + @override
27 + Future<List<ParsedAddress>> resolve({
28 + required String query,
29 + required List<CryptoCurrency> currencies,
30 + required WalletBase wallet,
31 + }) async {
32 + try {
33 + final handle = query.trim().split('/').last;
34 + if (handle.isEmpty) return [];
35 +
36 + final address = await _fetchZcashAddress(handle);
37 + if (address == null || address.isEmpty) return [];
38 +
39 + return [
40 + ParsedAddress(
41 + parsedAddressByCurrencyMap: {CryptoCurrency.zec: address},
42 + profileName: handle,
43 + addressSource: AddressSource.zcashAddress,
44 + handle: query,
45 + ),
46 + ];
47 + } catch (e) {
48 + printV('[address resolver] Error resolving zcash.me address: $e');
49 + return [];
50 + }
51 + }
52 +
53 + Future<String?> _fetchZcashAddress(String handle) async {
54 + final url = Uri.parse('https://zcash.me/$handle');
55 +
56 + try {
57 + final response = await ProxyWrapper().get(clearnetUri: url);
58 +
59 + if (response.statusCode == 200) {
60 + final addressRegex = RegExp(
61 + r'(t1[0-9A-Za-z]{33}|t3[0-9A-Za-z]{33}|zs[a-z0-9]{76}|u1[a-z0-9]{1,300})',
62 + caseSensitive: true,
63 + );
64 +
65 + final match = addressRegex.firstMatch(response.body);
66 + return match?.group(0);
67 + }
68 + } catch (e) {
69 + printV('[address resolver] Error fetching zcash.me profile: $e');
70 + }
71 +
72 + return null;
73 + }
74 +}
lib/core/address_resolver/zcash/zcash_names_record.dart renamed
lib/core/address_validator.dart
+56 -31
@@ -12,29 +12,54 @@ const AFTER_REGEX = '(\$|\\s)';
12 class AddressValidator extends TextValidator {
13 AddressValidator({required CryptoCurrency type, bool isTestnet = false})
14 : super(
15 - errorMessage: S.current.error_text_address,
16 - useAdditionalValidation: [CryptoCurrency.btc, CryptoCurrency.ltc].contains(type)
17 - ? (String txt) {
18 - final RegExp lightningInvoiceRegex = RegExp(
19 - r'^(lightning:)?(lnbc|lntb|lnbs|lnbcrt|lnurl)[a-z0-9]+$',
20 - caseSensitive: false);
21 - if (lightningInvoiceRegex.hasMatch(txt)) return true;
15 + errorMessage: S.current.error_text_address,
16 + useAdditionalValidation: [CryptoCurrency.btc, CryptoCurrency.ltc].contains(type)
17 + ? (String txt) {
18 + final RegExp lightningInvoiceRegex = RegExp(
19 + r'^(lightning:)?(lnbc|lntb|lnbs|lnbcrt|lnurl)[a-z0-9]+$',
20 + caseSensitive: false);
21 + if (lightningInvoiceRegex.hasMatch(txt)) return true;
22
23 - return BitcoinAddressUtils.validateAddress(
24 - address: txt,
25 - network: type == CryptoCurrency.btc
26 - ? isTestnet
27 - ? BitcoinNetwork.testnet
28 - : BitcoinNetwork.mainnet
29 - : LitecoinNetwork.mainnet,
30 - );
31 - }
32 - : type == CryptoCurrency.zano
33 - ? zano?.validateAddress
34 - : null,
35 - pattern: getPattern(type, isTestnet: isTestnet),
36 - length: getLength(type),
37 - );
23 + return BitcoinAddressUtils.validateAddress(
24 + address: txt,
25 + network: type == CryptoCurrency.btc
26 + ? isTestnet
27 + ? BitcoinNetwork.testnet
28 + : BitcoinNetwork.mainnet
29 + : LitecoinNetwork.mainnet,
30 + );
31 + }
32 + : type == CryptoCurrency.zano
33 + ? zano?.validateAddress
34 + : null,
35 + pattern: getPattern(type, isTestnet: isTestnet),
36 + length: getLength(type),
37 + );
38 +
39 + static const List<CryptoCurrency> reliableValidateCurrencies = [
40 + CryptoCurrency.xmr,
41 + CryptoCurrency.btc,
42 + CryptoCurrency.ltc,
43 + CryptoCurrency.bch,
44 + CryptoCurrency.trx,
45 + CryptoCurrency.nano,
46 + CryptoCurrency.sol,
47 + CryptoCurrency.zano,
48 + CryptoCurrency.ada,
49 + CryptoCurrency.xrp,
50 + CryptoCurrency.xhv,
51 + CryptoCurrency.zaddr,
52 + CryptoCurrency.zec,
53 + CryptoCurrency.dcr,
54 + CryptoCurrency.rvn,
55 + CryptoCurrency.near,
56 + CryptoCurrency.rune,
57 + CryptoCurrency.scrt,
58 + CryptoCurrency.stx,
59 + CryptoCurrency.kmd,
60 + CryptoCurrency.doge,
61 + CryptoCurrency.btcln,
62 + ];
63
64 static String getPattern(CryptoCurrency type, {bool isTestnet = false}) {
65 var pattern = "";
@@ -146,13 +171,13 @@ class AddressValidator extends TextValidator {
171 pattern = '[0-9a-zA-Z.]+';
172 case CryptoCurrency.zec:
173 pattern =
149 - '(?:'
150 - 't1[0-9A-Za-z]{33}'
151 - '|t3[0-9A-Za-z]{33}'
152 - '|zs[a-z0-9]{76}'
153 - '|zxviews[a-z0-9]{278}'
154 - '|u1[a-z0-9]{1,300}'
155 - ')';
174 + '(?:'
175 + 't1[0-9A-Za-z]{33}'
176 + '|t3[0-9A-Za-z]{33}'
177 + '|zs[a-z0-9]{76}'
178 + '|zxviews[a-z0-9]{278}'
179 + '|u1[a-z0-9]{1,300}'
180 + ')';
181 case CryptoCurrency.dcr:
182 pattern = '(D|T|S)[ksecS]([0-9a-zA-Z])+';
183 case CryptoCurrency.rvn:
@@ -344,7 +369,7 @@ class AddressValidator extends TextValidator {
369 '|([0-9a-zA-Z]{106})';
370 case CryptoCurrency.btc:
371 pattern =
347 - '${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
372 + '${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
373 case CryptoCurrency.ltc:
374 pattern = '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
375 '|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'
@@ -394,4 +419,4 @@ class AddressValidator extends TextValidator {
419
420 return pattern != null ? "($pattern)" : null;
421 }
397 -}
422 +}
\ No newline at end of file
lib/core/yat_service.dart deleted
-55
@@ -1,55 +0,0 @@
1 -import 'dart:convert';
2 -
3 -import 'package:cake_wallet/entities/yat_record.dart';
4 -import 'package:cw_core/utils/proxy_wrapper.dart';
5 -
6 -class YatService {
7 - static bool isDevMode = false;
8 -
9 - static String get apiUrl =>
10 - YatService.isDevMode ? YatService.apiDevUrl : YatService.apiReleaseUrl;
11 - static const apiReleaseUrl = "https://a.y.at";
12 - static const apiDevUrl = 'https://a.yat.fyi';
13 -
14 - static String lookupEmojiUrl(String emojiId) =>
15 - "$apiUrl/emoji_id/$emojiId/payment";
16 -
17 - static const String MONERO_SUB_ADDRESS = '0x1002';
18 - static const String MONERO_STD_ADDRESS = '0x1001';
19 - static const tags = {
20 - 'XMR': "$MONERO_STD_ADDRESS,$MONERO_SUB_ADDRESS",
21 - 'BTC': '0x1003',
22 - 'LTC': '0x1019'
23 - };
24 -
25 - Future<List<YatRecord>> fetchYatAddress(String emojiId, String ticker) async {
26 - final formattedTicker = ticker.toUpperCase();
27 - final formattedEmojiId = emojiId.replaceAll(' ', '');
28 - final tag = tags[formattedTicker];
29 - final uri = Uri.parse(lookupEmojiUrl(formattedEmojiId)).replace(
30 - queryParameters: <String, dynamic>{
31 - "tags": tag
32 - });
33 - final yatRecords = <YatRecord>[];
34 -
35 - try {
36 - final response = await ProxyWrapper().get(clearnetUri: uri);
37 -
38 - final resBody = json.decode(response.body) as Map<String, dynamic>;
39 - final results = resBody["result"] as Map<dynamic, dynamic>;
40 - // Favour a subaddress over a standard address.
41 - final yatRecord = (
42 - results[MONERO_SUB_ADDRESS] ??
43 - results[MONERO_STD_ADDRESS] ??
44 - results[tag]) as Map<String, dynamic>;
45 -
46 - if (yatRecord.isNotEmpty) {
47 - yatRecords.add(YatRecord.fromJson(yatRecord));
48 - }
49 -
50 - return yatRecords;
51 - } catch (_) {
52 - return yatRecords;
53 - }
54 - }
55 -}
lib/di.dart
+17 -10
@@ -6,6 +6,9 @@ import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
6 import 'package:cake_wallet/anypay/anypay_api.dart';
7 import 'package:cake_wallet/bitcoin/bitcoin.dart';
8 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
9 +import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
10 +import 'package:cake_wallet/core/address_resolver/yat/yat_service.dart';
11 +import 'package:cake_wallet/core/address_resolver/yat/yat_store.dart';
12 import 'package:cake_wallet/entities/bitcoin_amount_display_mode.dart';
13 import 'package:cake_wallet/evm/evm.dart';
14 import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
@@ -29,7 +32,6 @@ import 'package:cake_wallet/core/totp_request_details.dart';
32 import 'package:cake_wallet/core/trade_monitor.dart';
33 import 'package:cake_wallet/core/wallet_creation_service.dart';
34 import 'package:cake_wallet/core/wallet_loading_service.dart';
32 -import 'package:cake_wallet/core/yat_service.dart';
35 import 'package:cake_wallet/decred/decred.dart';
36 import 'package:cake_wallet/entities/biometric_auth.dart';
37 import 'package:cake_wallet/entities/bridge_transfer.dart';
@@ -37,7 +39,6 @@ import 'package:cake_wallet/entities/contact.dart';
39 import 'package:cake_wallet/entities/contact_record.dart';
40 import 'package:cake_wallet/entities/exchange_api_mode.dart';
41 import 'package:cake_wallet/entities/hardware_wallet/require_hardware_wallet_connection.dart';
40 -import 'package:cake_wallet/entities/parse_address_from_domain.dart';
42 import 'package:cake_wallet/entities/preferences_key.dart';
43 import 'package:cake_wallet/entities/qr_view_data.dart';
44 import 'package:cake_wallet/entities/template.dart';
@@ -198,7 +199,6 @@ import 'package:cake_wallet/store/settings_store.dart';
199 import 'package:cake_wallet/store/templates/exchange_template_store.dart';
200 import 'package:cake_wallet/store/templates/send_template_store.dart';
201 import 'package:cake_wallet/store/wallet_list_store.dart';
201 -import 'package:cake_wallet/store/yat/yat_store.dart';
202 import 'package:cake_wallet/themes/core/theme_store.dart';
203 import 'package:cake_wallet/tron/tron.dart';
204 import 'package:cake_wallet/utils/device_info.dart';
@@ -899,6 +899,7 @@ Future<void> setup({
899 getIt.get<AppStore>(),
900 getIt.get<SendTemplateViewModel>(),
901 getIt.get<FiatConversionStore>(),
902 + getIt.get<AddressResolverService>(),
903 getIt.get<BalanceViewModel>(),
904 getIt.get<ContactListViewModel>(),
905 _transactionDescriptionBox,
@@ -1100,7 +1101,10 @@ Future<void> setup({
1101 AnimatedURPage(getIt.get<AnimatedURModel>(), urQr: urQr));
1102
1103 getIt.registerFactoryParam<ContactViewModel, ContactRecord?, void>(
1103 - (ContactRecord? contact, _) => ContactViewModel(_contactSource, contact: contact));
1104 + (ContactRecord? contact, _) => ContactViewModel(_contactSource,
1105 + getIt.get<AppStore>(),
1106 + getIt.get<AddressResolverService>(),
1107 + contact: contact));
1108
1109 getIt.registerFactoryParam<ContactListViewModel, CryptoCurrency?, void>(
1110 (CryptoCurrency? cur, _) =>
@@ -1249,7 +1253,10 @@ Future<void> setup({
1253
1254 getIt.registerFactoryParam<ExchangePage, PaymentRequest?, void>(
1255 (PaymentRequest? paymentRequest, __) {
1252 - return ExchangePage(getIt.get<ExchangeViewModel>(), getIt.get<AuthService>(), paymentRequest);
1256 + return ExchangePage(getIt.get<ExchangeViewModel>(),
1257 + getIt.get<AuthService>(),
1258 + getIt.get<AddressResolverService>(),
1259 + paymentRequest);
1260 });
1261
1262 getIt.registerFactoryParam<NewSwapPage, PaymentRequest?, CryptoCurrency?>(
@@ -1257,6 +1264,7 @@ Future<void> setup({
1264 return NewSwapPage(
1265 getIt.get<ExchangeViewModel>(),
1266 getIt.get<AuthService>(),
1267 + getIt.get<AddressResolverService>(),
1268 paymentRequest,
1269 walletSwitcherViewModel: getIt.get<WalletSwitcherViewModel>(),
1270 initialCurrency: initialCurrency,
@@ -1494,7 +1502,7 @@ Future<void> setup({
1502
1503 getIt.registerFactory(() => BuySellViewModel(getIt.get<AppStore>()));
1504
1497 - getIt.registerFactory(() => BuySellPage(getIt.get<BuySellViewModel>()));
1505 + getIt.registerFactory(() => BuySellPage(getIt.get<BuySellViewModel>(), getIt.get<AddressResolverService>()));
1506
1507 getIt.registerFactoryParam<BuyOptionsPage, List<dynamic>, void>((List<dynamic> args, _) {
1508 final items = args.first as List<SelectableItem>;
@@ -1592,10 +1600,9 @@ Future<void> setup({
1600
1601 getIt.registerFactory(() => YatService());
1602
1595 - getIt.registerFactory(() => AddressResolver(
1596 - yatService: getIt.get<YatService>(),
1597 - wallet: getIt.get<AppStore>().wallet!,
1598 - settingsStore: getIt.get<SettingsStore>()));
1603 + getIt.registerFactory<AddressResolverService>(
1604 + () => AddressResolverService(settingsStore: getIt.get<SettingsStore>()),
1605 + );
1606
1607 getIt.registerFactoryParam<FullscreenQRPage, QrViewData, void>(
1608 (QrViewData viewData, _) => FullscreenQRPage(qrViewData: viewData));
lib/entities/ens_record.dart deleted
-54
@@ -1,54 +0,0 @@
1 -import 'package:cake_wallet/evm/evm.dart';
2 -import 'package:cake_wallet/reactions/wallet_connect.dart';
3 -import 'package:cw_core/utils/proxy_wrapper.dart';
4 -import 'package:cw_core/utils/print_verbose.dart';
5 -import 'package:cw_core/wallet_base.dart';
6 -import 'package:cw_core/wallet_type.dart';
7 -import 'package:ens_dart/ens_dart.dart';
8 -import 'package:web3dart/web3dart.dart';
9 -
10 -class EnsRecord {
11 - static Future<String> fetchEnsAddress(String name, {WalletBase? wallet}) async {
12 - Web3Client? _client;
13 -
14 - if (wallet != null && (isEVMCompatibleChain(wallet.type))) {
15 - _client = evm!.getWeb3Client(wallet);
16 - }
17 -
18 - if (_client == null) {
19 - late final client = ProxyWrapper().getHttpIOClient();
20 -
21 - _client = Web3Client("https://ethereum-rpc.publicnode.com", client);
22 - }
23 -
24 - try {
25 - final ens = Ens(client: _client);
26 -
27 - if (wallet != null) {
28 - switch (wallet.type) {
29 - case WalletType.monero:
30 - return await ens.withName(name).getCoinAddress(CoinType.XMR);
31 - case WalletType.bitcoin:
32 - return await ens.withName(name).getCoinAddress(CoinType.BTC);
33 - case WalletType.litecoin:
34 - return await ens.withName(name).getCoinAddress(CoinType.LTC);
35 - case WalletType.haven:
36 - return await ens.withName(name).getCoinAddress(CoinType.XHV);
37 - case WalletType.ethereum:
38 - case WalletType.polygon:
39 - case WalletType.base:
40 - case WalletType.arbitrum:
41 - case WalletType.bsc:
42 - default:
43 - return (await ens.withName(name).getAddress()).hex;
44 - }
45 - }
46 -
47 - final addr = await ens.withName(name).getAddress();
48 - return addr.hex;
49 - } catch (e) {
50 - printV(e);
51 - return "";
52 - }
53 - }
54 -}
lib/entities/fio_address_provider.dart deleted
-65
@@ -1,65 +0,0 @@
1 -import 'dart:convert';
2 -
3 -import 'package:cw_core/utils/proxy_wrapper.dart';
4 -
5 -class FioAddressProvider {
6 - static const apiAuthority = 'fio.blockpane.com';
7 - static const availCheck = '/v1/chain/avail_check';
8 - static const getAddress = '/v1/chain/get_pub_address';
9 -
10 - static Future<bool> checkAvail(String fioAddress) async {
11 - bool isFioRegistered = false;
12 - final headers = {'Content-Type': 'application/json'};
13 - final body = <String, String>{"fio_name": fioAddress};
14 -
15 - final uri = Uri.https(apiAuthority, availCheck);
16 - final response = await ProxyWrapper().post(
17 - clearnetUri: uri,
18 - headers: headers,
19 - body: json.encode(body),
20 - );
21 -
22 - if (response.statusCode != 200) {
23 - return isFioRegistered;
24 - }
25 -
26 -
27 - final responseJSON = json.decode(response.body) as Map<String, dynamic>;
28 - isFioRegistered = responseJSON['is_registered'] as int == 1;
29 -
30 - return isFioRegistered;
31 - }
32 -
33 - static Future<String> getPubAddress(String fioAddress, String token) async {
34 - final headers = {'Content-Type': 'application/json'};
35 - final body = <String, String>{
36 - "fio_address": fioAddress,
37 - "chain_code": token.toUpperCase(),
38 - "token_code": token.toUpperCase(),
39 - };
40 -
41 - final uri = Uri.https(apiAuthority, getAddress);
42 - final response = await ProxyWrapper().post(
43 - clearnetUri: uri,
44 - headers: headers,
45 - body: json.encode(body),
46 - );
47 -
48 -
49 - if (response.statusCode == 400) {
50 - final responseJSON = json.decode(response.body) as Map<String, dynamic>;
51 - final error = responseJSON['error'] as String;
52 - final message = responseJSON['message'] as String;
53 - throw Exception('${error}\n$message');
54 - }
55 -
56 - if (response.statusCode != 200) {
57 - throw Exception('Unexpected response http status: ${response.statusCode}');
58 - }
59 -
60 - final responseJSON = json.decode(response.body) as Map<String, dynamic>;
61 - final String pubAddress = responseJSON['public_address'] as String;
62 -
63 - return pubAddress;
64 - }
65 -}
lib/entities/openalias_record.dart deleted
-89
@@ -1,89 +0,0 @@
1 -import 'package:basic_utils/basic_utils.dart';
2 -import 'package:cw_core/utils/print_verbose.dart';
3 -
4 -class OpenaliasRecord {
5 - OpenaliasRecord({
6 - required this.address,
7 - required this.name,
8 - required this.description,
9 - });
10 -
11 - final String name;
12 - final String address;
13 - final String description;
14 -
15 - static String formatDomainName(String name) {
16 - String formattedName = name;
17 -
18 - if (name.contains("@")) {
19 - formattedName = name.replaceAll("@", ".");
20 - }
21 -
22 - return formattedName;
23 - }
24 -
25 - static Future<List<RRecord>?> lookupOpenAliasRecord(String name) async {
26 - try {
27 - final txtRecord = await DnsUtils.lookupRecord(name, RRecordType.TXT, dnssec: true);
28 -
29 - return txtRecord;
30 - } catch (e) {
31 - printV("${e.toString()}");
32 - return null;
33 - }
34 - }
35 -
36 - static OpenaliasRecord fetchAddressAndName({
37 - required String formattedName,
38 - required String ticker,
39 - required List<RRecord> txtRecord,
40 - }) {
41 - String address = '';
42 - String name = formattedName;
43 - String note = '';
44 -
45 - for (RRecord element in txtRecord) {
46 - String record = element.data;
47 -
48 - if (record.contains("oa1:$ticker") && record.contains("recipient_address")) {
49 - record = record.replaceAll('\"', "");
50 -
51 - final dataList = record.split(";");
52 -
53 - address = dataList
54 - .where((item) => (item.contains("recipient_address")))
55 - .toString()
56 - .replaceAll("oa1:$ticker recipient_address=", "")
57 - .replaceAll("(", "")
58 - .replaceAll(")", "")
59 - .trim();
60 -
61 - final recipientName = dataList
62 - .where((item) => (item.contains("recipient_name")))
63 - .toString()
64 - .replaceAll("(", "")
65 - .replaceAll(")", "")
66 - .trim();
67 -
68 - if (recipientName.isNotEmpty) {
69 - name = recipientName.replaceAll("recipient_name=", "");
70 - }
71 -
72 - final description = dataList
73 - .where((item) => (item.contains("tx_description")))
74 - .toString()
75 - .replaceAll("(", "")
76 - .replaceAll(")", "")
77 - .trim();
78 -
79 - if (description.isNotEmpty) {
80 - note = description.replaceAll("tx_description=", "");
81 - }
82 -
83 - break;
84 - }
85 -
86 - }
87 - return OpenaliasRecord(address: address, name: name, description: note);
88 -}
89 -}
lib/entities/parse_address_from_domain.dart deleted
-549
@@ -1,549 +0,0 @@
1 -import 'package:cake_wallet/core/address_validator.dart';
2 -import 'package:cake_wallet/core/yat_service.dart';
3 -import 'package:cake_wallet/entities/emoji_string_extension.dart';
4 -import 'package:cake_wallet/entities/ens_record.dart';
5 -import 'package:cake_wallet/entities/lnurlpay_record.dart';
6 -import 'package:cake_wallet/entities/fio_address_provider.dart';
7 -import 'package:cake_wallet/entities/openalias_record.dart';
8 -import 'package:cake_wallet/entities/parsed_address.dart';
9 -import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
10 -import 'package:cake_wallet/entities/wellknown_record.dart';
11 -import 'package:cake_wallet/entities/zano_alias.dart';
12 -import 'package:cake_wallet/entities/zcash_names_record.dart';
13 -import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
14 -import 'package:cake_wallet/mastodon/mastodon_api.dart';
15 -import 'package:cake_wallet/nostr/nostr_api.dart';
16 -import 'package:cake_wallet/store/settings_store.dart';
17 -import 'package:cake_wallet/twitter/twitter_api.dart';
18 -import 'package:cw_core/crypto_currency.dart';
19 -import 'package:cw_core/utils/print_verbose.dart';
20 -import 'package:cw_core/utils/proxy_wrapper.dart';
21 -import 'package:cw_core/wallet_base.dart';
22 -import 'package:cw_core/wallet_type.dart';
23 -import 'package:flutter/cupertino.dart';
24 -
25 -import 'bip_353_record.dart';
26 -
27 -class AddressResolver {
28 - AddressResolver({required this.yatService, required this.wallet, required this.settingsStore})
29 - : walletType = wallet.type;
30 -
31 - final YatService yatService;
32 - final WalletType walletType;
33 - final WalletBase wallet;
34 - final SettingsStore settingsStore;
35 -
36 - static const unstoppableDomains = [
37 - "888",
38 - "academy",
39 - "agency",
40 - "altimist",
41 - "anime",
42 - "austin",
43 - "bald",
44 - "bay",
45 - "benji",
46 - "bet",
47 - "binanceus",
48 - "bitcoin",
49 - "bitget",
50 - "bitscrunch",
51 - "blockchain",
52 - "boomer",
53 - "boston",
54 - "ca",
55 - "caw",
56 - "cc",
57 - "chat",
58 - "chomp",
59 - "clay",
60 - "club",
61 - "co",
62 - "com",
63 - "company",
64 - "crypto",
65 - "dao",
66 - "design",
67 - "dfz",
68 - "digital",
69 - "doga",
70 - "donut",
71 - "dream",
72 - "email",
73 - "emir",
74 - "eth",
75 - "ethermail",
76 - "family",
77 - "farms",
78 - "finance",
79 - "fun",
80 - "fyi",
81 - "games",
82 - "global",
83 - "go",
84 - "group",
85 - "guru",
86 - "hi",
87 - "hockey",
88 - "host",
89 - "info",
90 - "io",
91 - "klever",
92 - "kresus",
93 - "kryptic",
94 - "lfg",
95 - "life",
96 - "live",
97 - "llc",
98 - "ltc",
99 - "ltd",
100 - "manga",
101 - "me",
102 - "media",
103 - "metropolis",
104 - "miami",
105 - "miku",
106 - "money",
107 - "moon",
108 - "mumu",
109 - "net",
110 - "network",
111 - "news",
112 - "nft",
113 - "npc",
114 - "onchain",
115 - "online",
116 - "org",
117 - "podcast",
118 - "pog",
119 - "polygon",
120 - "base",
121 - "arbitrum"
122 - "press",
123 - "privacy",
124 - "pro",
125 - "propykeys",
126 - "pudgy",
127 - "pw",
128 - "quantum",
129 - "rad",
130 - "raiin",
131 - "retardio",
132 - "rip",
133 - "rocks",
134 - "secret",
135 - "services",
136 - "site",
137 - "smobler",
138 - "social",
139 - "solutions",
140 - "space",
141 - "stepn",
142 - "store",
143 - "studio",
144 - "systems",
145 - "tball",
146 - "tea",
147 - "team",
148 - "tech",
149 - "technology",
150 - "today",
151 - "tribe",
152 - "u",
153 - "ubu",
154 - "uno",
155 - "unstoppable",
156 - "vip",
157 - "wallet",
158 - "website",
159 - "wif",
160 - "wifi",
161 - "witg",
162 - "work",
163 - "world",
164 - "wrkx",
165 - "wtf",
166 - "x",
167 - "xmr",
168 - "xyz",
169 - "zil",
170 - "zone",
171 - "pizza"
172 - ];
173 -
174 - static String? extractAddressByType(
175 - {required String raw,
176 - required CryptoCurrency type,
177 - bool requireSurroundingWhitespaces = true}) {
178 - var addressPattern = AddressValidator.getAddressFromStringPattern(type);
179 -
180 - if (addressPattern == null) {
181 - return null;
182 - }
183 -
184 - if (requireSurroundingWhitespaces)
185 - addressPattern = "$BEFORE_REGEX$addressPattern$AFTER_REGEX";
186 -
187 - final match = RegExp(addressPattern, multiLine: true).firstMatch(raw);
188 - return match?.group(0)?.replaceAllMapped(RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_|ban_'),
189 - (Match match) {
190 - String group = match.group(0)!;
191 - if (group.startsWith('bitcoincash:') ||
192 - group.startsWith('nano_') ||
193 - group.startsWith('ban_')) {
194 - return group;
195 - }
196 - return '';
197 - });
198 - }
199 -
200 - static String extractUnstoppableDomain(String raw) {
201 - // sort by length to avoid matching shorter tld instead of longer
202 - final domains = List<String>.from(unstoppableDomains)..sort((a, b) => b.length.compareTo(a.length));
203 - for (final tld in domains) {
204 - final pattern = RegExp(r'([A-Za-z0-9-]+)\.' + RegExp.escape(tld), caseSensitive: false);
205 - final match = pattern.firstMatch(raw);
206 - if (match != null) return match.group(0)!;
207 - }
208 - return '';
209 - }
210 -
211 - bool isEmailFormat(String address) {
212 - final RegExp emailRegex = RegExp(
213 - r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
214 - caseSensitive: false,
215 - );
216 - return emailRegex.hasMatch(address);
217 - }
218 -
219 - String _stripHtmlTags(String value) {
220 - return value
221 - .replaceAll(RegExp(r'<br\s*/?>', caseSensitive: false), '\n')
222 - .replaceAll(RegExp(r'</p\s*>', caseSensitive: false), '\n')
223 - .replaceAll(RegExp(r'<[^>]*>'), '')
224 - .trim();
225 - }
226 -
227 - Future<ParsedAddress> resolve(BuildContext context, String text, CryptoCurrency currency) async {
228 - final ticker = currency.title;
229 - try {
230 - if (text.startsWith("zcash.me")) {
231 - final parts = text.split("/");
232 - final handle = parts.last;
233 - if (parts.length == 2 && handle.isNotEmpty) {
234 - final extractZcashAddress = await _fetchZcashAddress(handle);
235 - if (extractZcashAddress != null) {
236 - return ParsedAddress.zcashAddress(
237 - address: extractZcashAddress,
238 - name: handle,
239 - );
240 - }
241 - }
242 - }
243 - // twitter handle example: @username
244 - if (text.startsWith('@') && !text.substring(1).contains('@')) {
245 - if (currency == CryptoCurrency.zano && settingsStore.lookupsZanoAlias) {
246 - final formattedName = text.substring(1);
247 - final zanoAddress = await ZanoAlias.fetchZanoAliasAddress(formattedName);
248 - if (zanoAddress != null && zanoAddress.isNotEmpty) {
249 - return ParsedAddress.zanoAddress(
250 - address: zanoAddress,
251 - name: text,
252 - );
253 - }
254 - }
255 - if (settingsStore.lookupsTwitter) {
256 - final formattedName = text.substring(1);
257 - final twitterUser = await TwitterApi.lookupUserByName(userName: formattedName);
258 - final addressFromBio = extractAddressByType(
259 - raw: twitterUser.description,
260 - type: CryptoCurrency.fromString(ticker, walletCurrency: wallet.currency));
261 - if (addressFromBio != null && addressFromBio.isNotEmpty) {
262 - return ParsedAddress.fetchTwitterAddress(
263 - address: addressFromBio,
264 - name: text,
265 - profileImageUrl: twitterUser.profileImageUrl,
266 - profileName: twitterUser.name);
267 - } else {
268 - final domain = extractUnstoppableDomain(twitterUser.description);
269 - if (domain.isNotEmpty) {
270 - final parsedAddressFromDomain = await resolve(context, domain, currency);
271 - if (parsedAddressFromDomain.addresses.isNotEmpty &&
272 - parsedAddressFromDomain.addresses.first != domain) {
273 - return ParsedAddress(
274 - addresses: parsedAddressFromDomain.addresses,
275 - name: text,
276 - profileImageUrl: twitterUser.profileImageUrl,
277 - profileName: twitterUser.name,
278 - parseFrom: ParseFrom.twitter,
279 - );
280 - }
281 -
282 - }
283 -
284 - }
285 -
286 - final addressFromLocation = extractAddressByType(
287 - raw: twitterUser.location,
288 - type: CryptoCurrency.fromString(ticker,
289 - walletCurrency: wallet.currency), requireSurroundingWhitespaces: false);
290 - if (addressFromLocation != null && addressFromLocation.isNotEmpty) {
291 - return ParsedAddress.fetchTwitterAddress(
292 - address: addressFromLocation,
293 - name: text,
294 - profileImageUrl: twitterUser.profileImageUrl,
295 - profileName: twitterUser.name);
296 - } else {
297 - final domain = extractUnstoppableDomain(twitterUser.location);
298 - if (domain.isNotEmpty) {
299 - final parsedAddressFromDomain = await resolve(context, domain, currency);
300 - if (parsedAddressFromDomain.addresses.isNotEmpty &&
301 - parsedAddressFromDomain.addresses.first != domain) {
302 - return ParsedAddress(
303 - addresses: parsedAddressFromDomain.addresses,
304 - name: text,
305 - profileImageUrl: twitterUser.profileImageUrl,
306 - profileName: twitterUser.name,
307 - parseFrom: ParseFrom.twitter,
308 - );
309 - }
310 -
311 - }
312 -
313 - }
314 -
315 - final pinnedTweet = twitterUser.pinnedTweet?.text;
316 - if (pinnedTweet != null) {
317 - final addressFromPinnedTweet = extractAddressByType(
318 - raw: pinnedTweet,
319 - type: CryptoCurrency.fromString(ticker, walletCurrency: wallet.currency),
320 - requireSurroundingWhitespaces: true);
321 - if (addressFromPinnedTweet != null) {
322 - return ParsedAddress.fetchTwitterAddress(
323 - address: addressFromPinnedTweet,
324 - name: text,
325 - profileImageUrl: twitterUser.profileImageUrl,
326 - profileName: twitterUser.name);
327 - } else {
328 - final domain = extractUnstoppableDomain(pinnedTweet);
329 - if (domain.isNotEmpty) {
330 - final parsedAddressFromDomain = await resolve(context, domain, currency);
331 - if (parsedAddressFromDomain.addresses.isNotEmpty &&
332 - parsedAddressFromDomain.addresses.first != domain) {
333 - return ParsedAddress(
334 - addresses: parsedAddressFromDomain.addresses,
335 - name: text,
336 - profileImageUrl: twitterUser.profileImageUrl,
337 - profileName: twitterUser.name,
338 - parseFrom: ParseFrom.twitter,
339 - );
340 - }
341 - }
342 - }
343 - }
344 - }
345 - }
346 -
347 - // Mastodon example: @username@hostname.xxx
348 - if (text.startsWith('@') && text.contains('@', 1) && text.contains('.', 1)) {
349 - if (settingsStore.lookupsMastodon) {
350 - final subText = text.substring(1);
351 - final hostNameIndex = subText.indexOf('@');
352 - final hostName = subText.substring(hostNameIndex + 1);
353 - final userName = subText.substring(0, hostNameIndex);
354 -
355 - final mastodonUser =
356 - await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName);
357 -
358 - if (mastodonUser != null) {
359 - final mastodonNote = _stripHtmlTags(mastodonUser.note);
360 - String? addressFromBio = extractAddressByType(raw: mastodonNote, type: currency, requireSurroundingWhitespaces: true);
361 -
362 - if (addressFromBio != null && addressFromBio.isNotEmpty) {
363 - return ParsedAddress.fetchMastodonAddress(
364 - address: addressFromBio,
365 - name: text,
366 - profileImageUrl: mastodonUser.profileImageUrl,
367 - profileName: mastodonUser.username);
368 - } else {
369 - final pinnedPosts =
370 - await MastodonAPI.getPinnedPosts(userId: mastodonUser.id, apiHost: hostName);
371 -
372 - if (pinnedPosts.isNotEmpty) {
373 - final userPinnedPostsText = pinnedPosts.map((item) => _stripHtmlTags(item.content)).join('\n');
374 - String? addressFromPinnedPost =
375 - extractAddressByType(raw: userPinnedPostsText, type: currency,
376 - requireSurroundingWhitespaces: true);
377 -
378 - if (addressFromPinnedPost != null && addressFromPinnedPost.isNotEmpty) {
379 - return ParsedAddress.fetchMastodonAddress(
380 - address: addressFromPinnedPost,
381 - name: text,
382 - profileImageUrl: mastodonUser.profileImageUrl,
383 - profileName: mastodonUser.username);
384 - }
385 - }
386 - }
387 - }
388 - }
389 - }
390 -
391 - // .well-known scheme:
392 - if (text.contains('.') && text.contains('@')) {
393 - if (settingsStore.lookupsWellKnown) {
394 - final record =
395 - await WellKnownRecord.fetchAddressAndName(formattedName: text, currency: currency);
396 - if (record != null) {
397 - return ParsedAddress.fetchWellKnownAddress(address: record.address, name: text);
398 - }
399 - }
400 -
401 - if (walletType == WalletType.bitcoin) {
402 - final record =
403 - await LNUrlPayRecord.fetchAddressAndName(formattedName: text, currency: currency);
404 - if (record != null) {
405 - return ParsedAddress.fetchLNUrlPayAddress(address: record.address, name: text);
406 - }
407 - }
408 - }
409 -
410 - if (!text.startsWith('@') && text.contains('@') && !text.contains('.')) {
411 - final bool isFioRegistered = await FioAddressProvider.checkAvail(text);
412 - if (isFioRegistered) {
413 - final address = await FioAddressProvider.getPubAddress(text, ticker);
414 - return ParsedAddress.fetchFioAddress(address: address, name: text);
415 - }
416 - }
417 - if (text.hasOnlyEmojis) {
418 - if (settingsStore.lookupsYatService) {
419 - if (walletType != WalletType.haven) {
420 - final addresses = await yatService.fetchYatAddress(text, ticker);
421 - return ParsedAddress.fetchEmojiAddress(addresses: addresses, name: text);
422 - }
423 - }
424 - }
425 -
426 - final isNormalAddress = extractAddressByType(raw: text, type: currency)?.isNotEmpty ?? false;
427 -
428 - if (text.length <= 30 && !isNormalAddress) {
429 - final thorChainAddress = await ThorChainExchangeProvider.lookupAddressByName(text);
430 - if (thorChainAddress != null && thorChainAddress.isNotEmpty) {
431 - String? address =
432 - thorChainAddress[ticker] ?? (ticker == 'RUNE' ? thorChainAddress['THOR'] : null);
433 - if (address != null) {
434 - return ParsedAddress.thorChainAddress(address: address, name: text);
435 - }
436 - }
437 - }
438 -
439 - final formattedName = OpenaliasRecord.formatDomainName(text);
440 - final domainParts = formattedName.split('.');
441 - final name = domainParts.last;
442 -
443 - if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) {
444 - return ParsedAddress(addresses: [text]);
445 - }
446 -
447 - if (unstoppableDomains.any((domain) => name.trim() == domain)) {
448 - if (settingsStore.lookupsUnstoppableDomains) {
449 - final address = await fetchUnstoppableDomainAddress(text, ticker);
450 - if (address.isNotEmpty) {
451 - return ParsedAddress.fetchUnstoppableDomainAddress(address: address, name: text);
452 - }
453 - }
454 - }
455 -
456 - final bip353AddressMap = await Bip353Record.fetchUriByCryptoCurrency(text, ticker);
457 -
458 - if (bip353AddressMap != null && bip353AddressMap.isNotEmpty) {
459 - final chosenAddress = await Bip353Record.pickBip353AddressChoice(context, text, bip353AddressMap);
460 - if (chosenAddress != null) {
461 - try {
462 - final dnsProof = await Bip353Record.fetchDnsProof(text);
463 - return ParsedAddress.fetchBip353AddressAddress(address: chosenAddress, name: text, dnsProof: dnsProof);
464 - } catch (e) {
465 - printV('Bip353Record.fetchBip353AddressAddress error: $e');
466 - return ParsedAddress.fetchBip353AddressAddress(address: chosenAddress, name: text);
467 - }
468 - }
469 - }
470 -
471 - final lowerText = text.toLowerCase();
472 - if (lowerText.endsWith(".zec") || lowerText.endsWith(".zcash")) {
473 - if (settingsStore.lookupsZcashNames) {
474 - final address = await ZcashNamesRecord.fetchZcashNamesAddress(text);
475 - if (address != null && address.isNotEmpty) {
476 - return ParsedAddress.zcashNameAddress(address: address, name: text);
477 - }
478 - }
479 - }
480 -
481 - if (text.endsWith(".eth")) {
482 - if (settingsStore.lookupsENS) {
483 - final address = await EnsRecord.fetchEnsAddress(text, wallet: wallet);
484 - if (address.isNotEmpty && address != "0x0000000000000000000000000000000000000000") {
485 - return ParsedAddress.fetchEnsAddress(name: text, address: address);
486 - }
487 - }
488 - }
489 -
490 - if (formattedName.contains(".")) {
491 - if (settingsStore.lookupsOpenAlias) {
492 - final txtRecord = await OpenaliasRecord.lookupOpenAliasRecord(formattedName);
493 -
494 - if (txtRecord != null) {
495 - final record = await OpenaliasRecord.fetchAddressAndName(
496 - formattedName: formattedName, ticker: ticker.toLowerCase(), txtRecord: txtRecord);
497 - return ParsedAddress.fetchOpenAliasAddress(record: record, name: text);
498 - }
499 - }
500 - }
501 - if (isEmailFormat(text)) {
502 - final nostrProfile = await NostrProfileHandler.queryProfile(context, text);
503 - if (nostrProfile?.relays != null) {
504 - final nostrUserData =
505 - await NostrProfileHandler.processRelays(context, nostrProfile!, text);
506 -
507 - if (nostrUserData != null) {
508 - String? addressFromBio = extractAddressByType(raw: nostrUserData.about, type: currency);
509 - if (addressFromBio != null && addressFromBio.isNotEmpty) {
510 - return ParsedAddress.nostrAddress(
511 - address: addressFromBio,
512 - name: text,
513 - profileImageUrl: nostrUserData.picture,
514 - profileName: nostrUserData.name);
515 - }
516 - }
517 - }
518 - }
519 - } catch (e) {
520 - printV(e.toString());
521 - }
522 -
523 - return ParsedAddress(addresses: [text]);
524 - }
525 -
526 - Future<String?> _fetchZcashAddress(String handle) async {
527 - final url = Uri.parse('https://zcash.me/$handle');
528 -
529 - try {
530 - final response = await ProxyWrapper().get(clearnetUri: url);
531 -
532 - if (response.statusCode == 200) {
533 - final addressRegex = RegExp(
534 - r'(t1[0-9A-Za-z]{33}|t3[0-9A-Za-z]{33}|zs[a-z0-9]{76}|u1[a-z0-9]{1,300})',
535 - caseSensitive: true,
536 - );
537 -
538 - final match = addressRegex.firstMatch(response.body);
539 -
540 - if (match != null) {
541 - return match.group(0);
542 - }
543 - }
544 - } catch (e) {
545 - printV('Error fetching zcash.me profile: $e');
546 - }
547 - return null;
548 - }
549 -}
lib/entities/parsed_address.dart deleted
-212
@@ -1,212 +0,0 @@
1 -import 'package:cake_wallet/entities/openalias_record.dart';
2 -import 'package:cake_wallet/entities/yat_record.dart';
3 -
4 -enum ParseFrom {
5 - unstoppableDomains,
6 - openAlias,
7 - yatRecord,
8 - fio,
9 - notParsed,
10 - twitter,
11 - ens,
12 - contact,
13 - mastodon,
14 - nostr,
15 - thorChain,
16 - wellKnown,
17 - zanoAlias,
18 - zcashAddress,
19 - zcashName,
20 - bip353,
21 - lnurlpay,
22 -}
23 -
24 -class ParsedAddress {
25 - ParsedAddress({
26 - required this.addresses,
27 - this.name = '',
28 - this.description = '',
29 - this.profileImageUrl = '',
30 - this.profileName = '',
31 - this.parseFrom = ParseFrom.notParsed,
32 - this.bip353DnsProof,
33 - });
34 -
35 - factory ParsedAddress.fetchEmojiAddress({
36 - List<YatRecord>? addresses,
37 - required String name,
38 - }) {
39 - if (addresses?.isEmpty ?? true) {
40 - return ParsedAddress(addresses: [name], parseFrom: ParseFrom.yatRecord);
41 - }
42 - return ParsedAddress(
43 - addresses: addresses!.map((e) => e.address).toList(),
44 - name: name,
45 - parseFrom: ParseFrom.yatRecord,
46 - );
47 - }
48 -
49 - factory ParsedAddress.fetchUnstoppableDomainAddress({
50 - String? address,
51 - required String name,
52 - }) {
53 - if (address?.isEmpty ?? true) {
54 - return ParsedAddress(addresses: [name]);
55 - }
56 - return ParsedAddress(
57 - addresses: [address!],
58 - name: name,
59 - parseFrom: ParseFrom.unstoppableDomains,
60 - );
61 - }
62 -
63 - factory ParsedAddress.fetchBip353AddressAddress ({
64 - required String address,
65 - required String name,
66 - String? dnsProof,
67 - }) {
68 - return ParsedAddress(
69 - addresses: [address],
70 - name: name,
71 - parseFrom: ParseFrom.bip353,
72 - bip353DnsProof: dnsProof,
73 - );
74 - }
75 -
76 - factory ParsedAddress.fetchOpenAliasAddress(
77 - {required OpenaliasRecord record, required String name}) {
78 - if (record.address.isEmpty) {
79 - return ParsedAddress(addresses: [name]);
80 - }
81 - return ParsedAddress(
82 - addresses: [record.address],
83 - name: record.name,
84 - description: record.description,
85 - parseFrom: ParseFrom.openAlias,
86 - );
87 - }
88 -
89 - factory ParsedAddress.fetchFioAddress({required String address, required String name}) {
90 - return ParsedAddress(
91 - addresses: [address],
92 - name: name,
93 - parseFrom: ParseFrom.fio,
94 - );
95 - }
96 -
97 - factory ParsedAddress.fetchTwitterAddress(
98 - {required String address,
99 - required String name,
100 - required String profileImageUrl,
101 - required String profileName,
102 - String? description}) {
103 - return ParsedAddress(
104 - addresses: [address],
105 - name: name,
106 - description: description ?? '',
107 - profileImageUrl: profileImageUrl,
108 - profileName: profileName,
109 - parseFrom: ParseFrom.twitter,
110 - );
111 - }
112 -
113 - factory ParsedAddress.fetchMastodonAddress(
114 - {required String address,
115 - required String name,
116 - required String profileImageUrl,
117 - required String profileName}) {
118 - return ParsedAddress(
119 - addresses: [address],
120 - name: name,
121 - parseFrom: ParseFrom.mastodon,
122 - profileImageUrl: profileImageUrl,
123 - profileName: profileName,
124 - );
125 - }
126 -
127 - factory ParsedAddress.fetchContactAddress({required String address, required String name}) {
128 - return ParsedAddress(
129 - addresses: [address],
130 - name: name,
131 - parseFrom: ParseFrom.contact,
132 - );
133 - }
134 -
135 - factory ParsedAddress.fetchEnsAddress({required String address, required String name}) {
136 - return ParsedAddress(
137 - addresses: [address],
138 - name: name,
139 - parseFrom: ParseFrom.ens,
140 - );
141 - }
142 -
143 - factory ParsedAddress.nostrAddress(
144 - {required String address,
145 - required String name,
146 - required String profileImageUrl,
147 - required String profileName}) {
148 - return ParsedAddress(
149 - addresses: [address],
150 - name: name,
151 - parseFrom: ParseFrom.nostr,
152 - profileImageUrl: profileImageUrl,
153 - profileName: profileName,
154 - );
155 - }
156 -
157 - factory ParsedAddress.thorChainAddress({required String address, required String name}) {
158 - return ParsedAddress(
159 - addresses: [address],
160 - name: name,
161 - parseFrom: ParseFrom.thorChain,
162 - );
163 - }
164 -
165 - factory ParsedAddress.zanoAddress({required String address, required String name}) {
166 - return ParsedAddress(
167 - addresses: [address],
168 - name: name,
169 - parseFrom: ParseFrom.zanoAlias,
170 - );
171 - }
172 -
173 - factory ParsedAddress.zcashAddress({required String address, required String name}) {
174 - return ParsedAddress(
175 - addresses: [address],
176 - name: name,
177 - parseFrom: ParseFrom.zcashAddress,
178 - );
179 - }
180 -
181 - factory ParsedAddress.zcashNameAddress({required String address, required String name}) {
182 - return ParsedAddress(
183 - addresses: [address],
184 - name: name,
185 - parseFrom: ParseFrom.zcashName,
186 - );
187 - }
188 -
189 - factory ParsedAddress.fetchWellKnownAddress({required String address, required String name}) {
190 - return ParsedAddress(
191 - addresses: [address],
192 - name: name,
193 - parseFrom: ParseFrom.wellKnown,
194 - );
195 - }
196 -
197 - factory ParsedAddress.fetchLNUrlPayAddress({required String address, required String name}) {
198 - return ParsedAddress(
199 - addresses: [address],
200 - name: name,
201 - parseFrom: ParseFrom.lnurlpay,
202 - );
203 - }
204 -
205 - final List<String> addresses;
206 - final String name;
207 - final String description;
208 - final String profileImageUrl;
209 - final String profileName;
210 - final ParseFrom parseFrom;
211 - final String? bip353DnsProof;
212 -}
lib/entities/preferences_key.dart
+6
@@ -102,7 +102,13 @@ class PreferencesKey {
102 static const lookupsOpenAlias = 'looks_up_open_alias';
103 static const lookupsENS = 'looks_up_ens';
104 static const lookupsZcashNames = 'looks_up_zcash_names';
105 + static const lookupsZcashAddress = 'looks_up_zcash_address';
106 static const lookupsWellKnown = 'looks_up_well_known';
107 + static const lookupsFio = 'looks_up_fio';
108 + static const lookupsNostr = 'looks_up_nostr';
109 + static const lookupsThorChain = 'looks_up_thor_chain';
110 + static const lookupsBip353 = 'looks_up_bip353';
111 + static const lookupsLNUrl = 'looks_up_lnurl';
112 static const useBlinkProtection = 'use_blink_protection';
113 static const usePayjoin = 'use_payjoin';
114 static const showPayjoinCard = 'show_payjoin_card';
lib/entities/unstoppable_domain_address.dart deleted
-30
@@ -1,30 +0,0 @@
1 -import 'dart:convert';
2 -
3 -import 'package:cw_core/utils/print_verbose.dart';
4 -import 'package:cw_core/utils/proxy_wrapper.dart';
5 -
6 -Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async {
7 - var address = '';
8 -
9 - try {
10 - final uri = Uri.parse("https://api.unstoppabledomains.com/profile/public/${Uri.encodeQueryComponent(domain)}?fields=records");
11 - final response = await ProxyWrapper().get(clearnetUri: uri);
12 -
13 - final jsonParsed = json.decode(response.body) as Map<String, dynamic>;
14 - if (jsonParsed["records"] == null) {
15 - throw Exception(".records response from $uri is empty");
16 - };
17 - final records = jsonParsed["records"] as Map<String, dynamic>;
18 - final key = "crypto.${ticker.toUpperCase()}.address";
19 - if (records[key] == null) {
20 - throw Exception(".records.${key} response from $uri is empty");
21 - }
22 -
23 - return records[key] as String? ?? '';
24 - } catch (e) {
25 - printV('Unstoppable domain error: ${e.toString()}');
26 - address = '';
27 - }
28 -
29 - return address;
30 -}
\ No newline at end of file
lib/entities/wellknown_record.dart deleted
-93
@@ -1,93 +0,0 @@
1 -import 'dart:convert';
2 -
3 -import 'package:cw_core/crypto_currency.dart';
4 -import 'package:cw_core/utils/print_verbose.dart';
5 -import 'package:cw_core/utils/proxy_wrapper.dart';
6 -
7 -class WellKnownRecord {
8 - WellKnownRecord({
9 - required this.address,
10 - required this.name,
11 - });
12 -
13 - final String name;
14 - final String address;
15 -
16 - static Future<String?> checkWellKnownUsername(String username, CryptoCurrency currency) async {
17 - String jsonLocation = "";
18 - switch (currency) {
19 - case CryptoCurrency.nano:
20 - jsonLocation = "nano-currency";
21 - break;
22 - // TODO: add other currencies
23 - default:
24 - return null;
25 - }
26 -
27 - // split the string by the @ symbol:
28 - try {
29 - final List<String> splitStrs = username.split("@");
30 - String name = splitStrs.first.toLowerCase();
31 - final String domain = splitStrs.last;
32 -
33 - if (splitStrs.length == 3) {
34 - // for username like @alice@domain.org instead of alice@domain.org
35 - name = splitStrs[1];
36 - }
37 -
38 - if (name.isEmpty) {
39 - name = "_";
40 - }
41 -
42 - // lookup domain/.well-known/nano-currency.json and check if it has a nano address:
43 - final response = await ProxyWrapper().get(
44 - clearnetUri: Uri.parse("https://$domain/.well-known/$jsonLocation.json?names=$name"),
45 - headers: <String, String>{"Accept": "application/json"},
46 - );
47 -
48 - if (response.statusCode != 200) {
49 - return null;
50 - }
51 -
52 - final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
53 -
54 - // Access the first element in the names array and retrieve its address
55 - final List<dynamic> names = decoded["names"] as List<dynamic>;
56 - for (final dynamic item in names) {
57 - if (item["name"].toLowerCase() == name) {
58 - return item["address"] as String;
59 - }
60 - }
61 - } catch (e) {
62 - printV("error checking well-known username: $e");
63 - }
64 - return null;
65 - }
66 -
67 - static String formatDomainName(String name) {
68 - String formattedName = name;
69 -
70 - if (name.contains("@")) {
71 - formattedName = name.replaceAll("@", ".");
72 - }
73 -
74 - return formattedName;
75 - }
76 -
77 - static Future<WellKnownRecord?> fetchAddressAndName({
78 - required String formattedName,
79 - required CryptoCurrency currency,
80 - }) async {
81 - String name = formattedName;
82 -
83 - printV("formattedName: $formattedName");
84 -
85 - final address = await checkWellKnownUsername(formattedName, currency);
86 -
87 - if (address == null) {
88 - return null;
89 - }
90 -
91 - return WellKnownRecord(address: address, name: name);
92 - }
93 -}
lib/entities/yat_record.dart deleted
-13
@@ -1,13 +0,0 @@
1 -class YatRecord {
2 - YatRecord({
3 - required this.category,
4 - required this.address,
5 - });
6 -
7 - YatRecord.fromJson(Map<String, dynamic> json)
8 - : address = json['address'] as String,
9 - category = json['category'] as String;
10 -
11 - String category;
12 - String address;
13 -}
lib/entities/zano_alias.dart deleted
-29
@@ -1,29 +0,0 @@
1 -import 'dart:convert';
2 -
3 -import 'package:cw_core/utils/print_verbose.dart';
4 -import 'package:cw_core/utils/proxy_wrapper.dart';
5 -
6 -class ZanoAlias {
7 - static Future<String?> fetchZanoAliasAddress(String alias) async {
8 - try {
9 - final uri = Uri.parse("http://37.27.100.59:10500/json_rpc");
10 - final response = await ProxyWrapper().post(
11 - clearnetUri: uri,
12 - body: json.encode({
13 - "id": 0,
14 - "jsonrpc": "2.0",
15 - "method": "get_alias_details",
16 - "params": {"alias": alias}
17 - }),
18 - );
19 -
20 - final jsonParsed = json.decode(response.body) as Map<String, dynamic>;
21 -
22 - return jsonParsed['result']['alias_details']['address'] as String?;
23 - } catch (e) {
24 - printV('Zano Alias error: ${e.toString()}');
25 - }
26 -
27 - return null;
28 - }
29 -}
lib/new-ui/pages/send_page.dart
+52 -7
@@ -50,6 +50,7 @@ import 'package:cake_wallet/utils/payment_request.dart';
50 import 'package:cake_wallet/utils/show_pop_up.dart';
51 import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
52 import 'package:cake_wallet/view_model/send/output.dart';
53 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
54 import 'package:cake_wallet/view_model/send/send_view_model.dart';
55 import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
56 import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
@@ -263,15 +264,34 @@ class _NewSendPageState extends State<NewSendPage> {
264 _addressFocusNode.addListener(() async {
265 if (!_addressFocusNode.hasFocus && _addressControllers[_selectedOutput].text.isNotEmpty) {
266 final output = widget.sendViewModel.outputs[_selectedOutput];
266 - output.fetchParsedAddress(context).then((val) {
267 - if (_addressControllers[_selectedOutput].text != output.extractedAddress) {
268 - _addressControllers[_selectedOutput].text = output.extractedAddress;
269 - }
270 - });
267 + await _resolveAddressForOutput(output);
268 }
269 });
270 }
271
272 + Future<void> _resolveAddressForOutput(Output output) async {
273 + final result = await widget.sendViewModel.resolveAddressForOutput(output);
274 + if (result == null) return;
275 +
276 + final confirmed = await showParsedAddressConfirmationAlert(context, result);
277 +
278 + if (confirmed) {
279 + output.applyAddressLookupResult(result);
280 + } else {
281 + output.resetParsedAddress();
282 + return;
283 + }
284 +
285 + final outputIndex = widget.sendViewModel.outputs.indexOf(output);
286 + if (outputIndex >= 0 &&
287 + outputIndex < _addressControllers.length &&
288 + output.extractedAddress.isNotEmpty &&
289 + _addressControllers[outputIndex].text != output.extractedAddress) {
290 + _addressControllers[outputIndex].text = output.extractedAddress;
291 + }
292 + }
293 +
294 +
295 @override
296 Widget build(BuildContext context) {
297 return Observer(builder: (_) {
@@ -377,7 +397,7 @@ class _NewSendPageState extends State<NewSendPage> {
397 focusNode: _addressFocusNode,
398 onURIScanned: (uri) async {
399 output.resetParsedAddress();
380 - await output.fetchParsedAddress(context);
400 + await _resolveAddressForOutput(output);
401
402 // Process the payment through the new flow
403 await _handlePaymentFlow(
@@ -404,7 +424,7 @@ class _NewSendPageState extends State<NewSendPage> {
424 _justHandledPasteButton = true;
425 try {
426 output.resetParsedAddress();
407 - await output.fetchParsedAddress(context);
427 + await _resolveAddressForOutput(output);
428
429 final address = output.isParsedAddress
430 ? output.extractedAddress
@@ -1429,3 +1449,28 @@ class SendHelpPage extends StatelessWidget {
1449 );
1450 }
1451 }
1452 +
1453 +Future<bool> showParsedAddressConfirmationAlert(
1454 + BuildContext context,
1455 + ParsedAddress parsedAddress,
1456 +) async {
1457 + final confirmed = await showPopUp<bool>(
1458 + context: context,
1459 + builder: (BuildContext context) {
1460 + return AlertWithOneAction(
1461 + alertTitle: S.of(context).address_detected,
1462 + headerTitleText: parsedAddress.profileName.isEmpty ? null : parsedAddress.profileName,
1463 + headerImageProfileUrl: parsedAddress.profileImageUrl.isEmpty
1464 + ? parsedAddress.addressSource.iconPath
1465 + : parsedAddress.profileImageUrl,
1466 + alertContent: S.of(context).extracted_address_content(
1467 + '${parsedAddress.handle} (${parsedAddress.addressSource.label})',
1468 + ),
1469 + buttonText: S.of(context).ok,
1470 + buttonAction: () => Navigator.of(context).pop(true),
1471 + );
1472 + },
1473 + );
1474 +
1475 + return confirmed ?? false;
1476 +}
\ No newline at end of file
lib/new-ui/pages/swap_page.dart
+8 -5
@@ -1,8 +1,8 @@
1 +import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
2 import 'package:cake_wallet/core/address_validator.dart';
3 import 'package:cake_wallet/core/amount_validator.dart';
4 import 'package:cake_wallet/core/auth_service.dart';
5 import 'package:cake_wallet/di.dart';
5 -import 'package:cake_wallet/entities/parse_address_from_domain.dart';
6 import 'package:cake_wallet/entities/qr_scanner.dart';
7 import 'package:cake_wallet/exchange/exchange_trade_state.dart';
8 import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
@@ -23,7 +23,6 @@ import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.
23 import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart';
24 import 'package:cake_wallet/new-ui/widgets/swap_page/swap_options_page.dart';
25 import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
26 -import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
26 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
27 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
28 import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
@@ -52,7 +51,7 @@ import 'package:mobx/mobx.dart';
51 import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
52
53 class NewSwapPage extends StatefulWidget {
55 - NewSwapPage(this.exchangeViewModel, this.authService, this.initialPaymentRequest,
54 + NewSwapPage(this.exchangeViewModel, this.authService,this.adrResService, this.initialPaymentRequest,
55 {required this.walletSwitcherViewModel, CryptoCurrency? initialCurrency}) {
56 depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
57 ? exchangeViewModel.wallet.name
@@ -68,6 +67,7 @@ class NewSwapPage extends StatefulWidget {
67 final ExchangeViewModel exchangeViewModel;
68 final WalletSwitcherViewModel walletSwitcherViewModel;
69 final AuthService authService;
70 + final AddressResolverService adrResService;
71 final PaymentRequest? initialPaymentRequest;
72 late final String? depositWalletName;
73 late final String? receiveWalletName;
@@ -423,8 +423,11 @@ class _NewSwapPageState extends State<NewSwapPage> {
423 Future<String> fetchParsedAddress(
424 BuildContext context, String domain, CryptoCurrency currency) async {
425 printV("$domain");
426 - final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
427 - return extractAddressFromParsed(context, parsedAddress);
426 + final parsedAddress = await widget.adrResService.resolve(
427 + query: domain,
428 + wallet: widget.exchangeViewModel.wallet,
429 + currency: currency);
430 + return parsedAddress.isNotEmpty ? parsedAddress.first.parsedAddressByCurrencyMap[currency] ?? '' : '';
431 }
432
433 void _showFeeAlert(BuildContext context) async {
lib/new-ui/viewmodels/lightning_username/lightning_username_bloc.dart
+1 -1
@@ -1,7 +1,7 @@
1 import 'package:bloc/bloc.dart';
2 import 'package:bloc_concurrency/bloc_concurrency.dart';
3 import 'package:cake_wallet/bitcoin/bitcoin.dart';
4 -import 'package:cake_wallet/entities/lnurlpay_record.dart';
4 +import 'package:cake_wallet/core/address_resolver/lnurl_pay/lnurlpay_record.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
6 import 'package:cw_core/crypto_currency.dart';
7 import 'package:cw_core/wallet_base.dart';
lib/new-ui/widgets/send_page/send_confirm_sheet.dart
+8 -7
@@ -227,13 +227,14 @@ class SendTransactionDetails extends StatelessWidget {
227 decimals: 2)
228 : sendViewModel.pendingTransactionFeeFiatAmountFormatted;
229
230 - final showAddress = !sendViewModel.outputs.any((e) =>
231 - RegExp(AddressValidator.bolt11InvoiceMatcher).hasMatch(e.address.toLowerCase()) ||
232 - RegExp(AddressValidator.lnurlMatcher).hasMatch(e.address.toLowerCase()) ||
233 - (e.isParsedAddress &&
234 - e.parsedAddress.addresses.isNotEmpty &&
235 - RegExp(AddressValidator.lnurlMatcher)
236 - .hasMatch(e.parsedAddress.addresses.firstOrNull?.toLowerCase() ?? "")));
230 + final showAddress = !sendViewModel.outputs.any((e) =>
231 + RegExp(AddressValidator.bolt11InvoiceMatcher).hasMatch(e.address.toLowerCase()) ||
232 + RegExp(AddressValidator.lnurlMatcher).hasMatch(e.address.toLowerCase()) ||
233 + (e.isParsedAddress &&
234 + e.parsedAddress.parsedAddressByCurrencyMap[sendViewModel.selectedCryptoCurrency] != null &&
235 + e.parsedAddress.parsedAddressByCurrencyMap[sendViewModel.selectedCryptoCurrency]!.isNotEmpty &&
236 + RegExp(AddressValidator.lnurlMatcher)
237 + .hasMatch(e.parsedAddress.parsedAddressByCurrencyMap[sendViewModel.selectedCryptoCurrency]!.toLowerCase())));
238
239 final outputs = sendViewModel.outputs;
240
lib/nostr/nostr_api.dart deleted
-150
@@ -1,150 +0,0 @@
1 -import 'dart:convert';
2 -
3 -import 'package:cake_wallet/generated/i18n.dart';
4 -import 'package:cake_wallet/nostr/nostr_user.dart';
5 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
6 -import 'package:cake_wallet/src/widgets/picker.dart';
7 -import 'package:cake_wallet/utils/show_pop_up.dart';
8 -import 'package:cw_core/utils/print_verbose.dart';
9 -import 'package:flutter/material.dart';
10 -import 'package:nostr_tools/nostr_tools.dart';
11 -
12 -class NostrProfileHandler {
13 - static final relayToDomainMap = {
14 - 'relay.snort.social': 'snort.social',
15 - };
16 -
17 - static Nip05 _nip05 = Nip05();
18 -
19 - static Future<ProfilePointer?> queryProfile(BuildContext context, String nip05Address) async {
20 - var profile = await _nip05.queryProfile(nip05Address);
21 - if (profile?.pubkey != null) {
22 - if (profile?.relays?.isNotEmpty == true) {
23 - return profile;
24 - } else {
25 - await _showErrorDialog(context, S.of(context).no_relays, S.of(context).no_relays_message);
26 - }
27 - }
28 - return null;
29 - }
30 -
31 - static Future<UserMetadata?> processRelays(
32 - BuildContext context,
33 - ProfilePointer profile,
34 - String nip05Address,
35 - ) async {
36 - String userDomain = _extractDomain(nip05Address);
37 - const int metaData = 0;
38 -
39 - for (String relayUrl in profile.relays ?? []) {
40 - final relayDomain = _getDomainFromRelayUrl(relayUrl);
41 - final formattedRelayDomain = relayToDomainMap[relayDomain] ?? relayDomain;
42 - if (formattedRelayDomain == userDomain) {
43 - final userDomainData = await _fetchInfoFromRelay(relayUrl, profile.pubkey, [metaData]);
44 - if (userDomainData != null) {
45 - return userDomainData;
46 - }
47 - }
48 - }
49 - await _showErrorDialog(context, S.of(context).no_relays, S.of(context).no_relay_on_domain);
50 -
51 - String? chosenRelayUrl =
52 - await _showRelayChoiceDialog(context, profile.relays ?? []);
53 - if (chosenRelayUrl != null) {
54 - final userData = await _fetchInfoFromRelay(chosenRelayUrl, profile.pubkey, [metaData]);
55 - if (userData != null) {
56 - return userData;
57 - }
58 - }
59 -
60 - return null;
61 - }
62 -
63 - static Future<UserMetadata?> _fetchInfoFromRelay(
64 - String relayUrl, String userPubKey, List<int> kinds) async {
65 - try {
66 - final relay = RelayApi(relayUrl: relayUrl);
67 - final stream = await relay.connect();
68 -
69 - relay.sub([
70 - Filter(
71 - kinds: kinds,
72 - authors: [userPubKey],
73 - )
74 - ]);
75 -
76 - await for (var message in stream) {
77 - if (message.type == 'EVENT') {
78 - final event = message.message as Event;
79 -
80 - final eventContent = json.decode(event.content) as Map<String, dynamic>;
81 -
82 - final userMetadata = UserMetadata.fromJson(eventContent);
83 - relay.close();
84 - return userMetadata;
85 - }
86 - }
87 -
88 - relay.close();
89 - return null;
90 - } catch (e) {
91 - printV('[!] Error with relay $relayUrl: $e');
92 - return null;
93 - }
94 - }
95 -
96 - static Future<void> _showErrorDialog(
97 - BuildContext context, String title, String errorMessage) async {
98 - if (context.mounted) {
99 - await showPopUp<void>(
100 - context: context,
101 - builder: (BuildContext dialogContext) {
102 - return AlertWithOneAction(
103 - alertTitle: title,
104 - alertContent: errorMessage,
105 - buttonText: S.of(dialogContext).ok,
106 - buttonAction: () => Navigator.of(dialogContext).pop(),
107 - );
108 - },
109 - );
110 - }
111 - }
112 -
113 - static String _extractDomain(String nip05Address) {
114 - var parts = nip05Address.split('@');
115 - return parts.length == 2 ? parts[1] : '';
116 - }
117 -
118 - static String _getDomainFromRelayUrl(String relayUrl) {
119 - try {
120 - var uri = Uri.parse(relayUrl);
121 - return uri.host;
122 - } catch (e) {
123 - printV('Error parsing URL: $e');
124 - return '';
125 - }
126 - }
127 -
128 - static Future<String?> _showRelayChoiceDialog(
129 - BuildContext context,
130 - List<String> relays,
131 - ) async {
132 - String? selectedRelay;
133 -
134 - if (context.mounted) {
135 - await showPopUp<void>(
136 - context: context,
137 - builder: (BuildContext dialogContext) {
138 - return Picker<String>(
139 - selectedAtIndex: 0,
140 - title: S.of(dialogContext).choose_relay,
141 - items: relays,
142 - onItemSelected: (String relay) => selectedRelay = relay,
143 - );
144 - },
145 - );
146 - }
147 -
148 - return selectedRelay;
149 - }
150 -}
lib/src/screens/buy/buy_sell_page.dart
+30 -12
@@ -1,7 +1,7 @@
1 import 'package:cake_wallet/buy/sell_buy_states.dart';
2 +import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
3 import 'package:cake_wallet/core/address_validator.dart';
4 import 'package:cake_wallet/di.dart';
4 -import 'package:cake_wallet/entities/parse_address_from_domain.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
6 import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
7 import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart';
@@ -16,7 +16,6 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
16 import 'package:cake_wallet/src/widgets/provider_optoin_tile.dart';
17 import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
18 import 'package:cake_wallet/src/widgets/trail_button.dart';
19 -import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
19 import 'package:cake_wallet/utils/responsive_layout_util.dart';
20 import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart';
21 import 'package:cw_core/crypto_currency.dart';
@@ -28,9 +27,10 @@ import 'package:keyboard_actions/keyboard_actions.dart';
27 import 'package:mobx/mobx.dart';
28
29 class BuySellPage extends BasePage {
31 - BuySellPage(this.buySellViewModel);
30 + BuySellPage(this.buySellViewModel, this._resolver);
31
32 final BuySellViewModel buySellViewModel;
33 + final AddressResolverService _resolver;
34 final cryptoCurrencyKey = GlobalKey<ExchangeCardState>();
35 final fiatCurrencyKey = GlobalKey<ExchangeCardState>();
36 final _formKey = GlobalKey<FormState>();
@@ -300,7 +300,7 @@ class BuySellPage extends BasePage {
300 });
301
302 reaction((_) => buySellViewModel.cryptoCurrencyAddress, (String address) {
303 - if (cryptoAddressController != address) {
303 + if (cryptoAddressController.text != address) {
304 cryptoCurrencyKey.currentState!.addressController.text = address;
305 }
306 });
@@ -320,8 +320,10 @@ class BuySellPage extends BasePage {
320 _cryptoAddressFocus.addListener(() async {
321 if (!_cryptoAddressFocus.hasFocus && cryptoAddressController.text.isNotEmpty) {
322 final domain = cryptoAddressController.text;
323 - buySellViewModel.cryptoCurrencyAddress = await fetchParsedAddress(
324 - context, domain, buySellViewModel.cryptoCurrency);
323 + final parsed = await fetchParsedAddress(context, domain, buySellViewModel.cryptoCurrency);
324 + if (parsed.isNotEmpty) {
325 + buySellViewModel.cryptoCurrencyAddress = parsed;
326 + }
327 }
328 });
329
@@ -410,8 +412,23 @@ class BuySellPage extends BasePage {
412 currencyButtonColor: Colors.transparent,
413 addressButtonsColor: Theme.of(context).colorScheme.surfaceContainerHighest,
414 borderColor: Theme.of(context).colorScheme.outlineVariant,
413 - onPushPasteButton: (context) async {},
414 - onPushAddressBookButton: (context) async {},
415 + onPushPasteButton: (context) async {
416 + final domain = cryptoCurrencyKey.currentState!.addressController.text;
417 + final parsed =
418 + await fetchParsedAddress(context, domain, buySellViewModel.cryptoCurrency);
419 + if (parsed.isNotEmpty) {
420 + buySellViewModel.cryptoCurrencyAddress = parsed;
421 + }
422 + },
423 +
424 + onPushAddressBookButton: (context) async {
425 + final domain = cryptoCurrencyKey.currentState!.addressController.text;
426 + final parsed =
427 + await fetchParsedAddress(context, domain, buySellViewModel.cryptoCurrency);
428 + if (parsed.isNotEmpty) {
429 + buySellViewModel.cryptoCurrencyAddress = parsed;
430 + }
431 + },
432 fillColor: buySellViewModel.isBuyAction
433 ? Theme.of(context).colorScheme.surfaceContainer
434 : Theme.of(context).colorScheme.surfaceContainerLow,
@@ -537,9 +554,10 @@ class BuySellPage extends BasePage {
554 String domain,
555 CryptoCurrency currency,
556 ) async {
540 - final parsedAddress =
541 - await getIt.get<AddressResolver>().resolve(context, domain, currency);
542 - final address = await extractAddressFromParsed(context, parsedAddress);
543 - return address;
557 + final parsedAddresses = await _resolver.resolve(
558 + query: domain, wallet: buySellViewModel.wallet, currency: currency);
559 + return parsedAddresses.isNotEmpty
560 + ? parsedAddresses.first.parsedAddressByCurrencyMap[currency] ?? ''
561 + : '';
562 }
563 }
lib/src/screens/contact/contact_list_page.dart
+1
@@ -340,6 +340,7 @@ class _ContactListBodyState extends State<ContactListBody> {
340 updateFunction: widget.contactListViewModel.reorderAccordingToContactList,
341 canReorder: widget.contactListViewModel.isEditable,
342 shrinkWrap: true,
343 + padding: const EdgeInsets.only(bottom: 250),
344 itemBuilder: (context, index) {
345 final contact = contacts[index];
346 final contactContent =
lib/src/screens/contact/contact_page.dart
+48 -4
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
2 import 'package:cake_wallet/core/address_validator.dart';
3 import 'package:cake_wallet/new-ui/widgets/send_page/send_address_input.dart';
4 import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -27,7 +28,15 @@ class ContactPage extends BasePage {
28 _nameController.text = contactViewModel.name;
29 _addressController.text = contactViewModel.address;
30 _nameController.addListener(() => contactViewModel.name = _nameController.text);
30 - _addressController.addListener(() => contactViewModel.address = _addressController.text);
31 + _addressController.addListener(() {
32 + final address = _addressController.text;
33 +
34 + if (address != contactViewModel.address) {
35 + contactViewModel.displayName = '';
36 + }
37 +
38 + contactViewModel.address = address;
39 + });
40
41 autorun((_) => _currencyTypeController.text = "${contactViewModel.currency?.toString() ?? ""} ${contactViewModel.currency?.tag != null ? "(${contactViewModel.currency?.tag})" : ""}");
42 }
@@ -98,7 +107,8 @@ class ContactPage extends BasePage {
107 focusNode: addressFocusNode,
108 displayName: contactViewModel.displayName,
109 addressController: _addressController,
101 - onEditingComplete: ()=> contactViewModel.extractParsedAddress(context),
110 + onEditingComplete: () {},
111 + onPushPasteButton: (context) => _extractParsedAddress(context),
112 validator: AddressValidator(type: contactViewModel.currency!),
113 selectedCurrency: contactViewModel.currency!,
114 );
@@ -129,8 +139,7 @@ class ContactPage extends BasePage {
139 builder: (_) => PrimaryButton(
140 onPressed: () async {
141 FocusScope.of(context).unfocus();
132 -
133 - await contactViewModel.extractParsedAddress(context);
142 + _extractParsedAddress(context);
143
144 if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
145 return;
@@ -150,6 +159,16 @@ class ContactPage extends BasePage {
159 );
160 }
161
162 + Future<void> _extractParsedAddress(BuildContext context) async {
163 + final parsedAddress = await contactViewModel.extractParsedAddress(context);
164 + if (parsedAddress == null) return;
165 +
166 + final confirmed = await showParsedAddressConfirmationAlert(context, parsedAddress);
167 + if (!confirmed) return;
168 +
169 + contactViewModel.applyParsedAddress(parsedAddress);
170 + }
171 +
172 void _presentCurrencyPicker(BuildContext context) {
173 showPopUp<void>(
174 builder: (_) => CurrencyPicker(
@@ -203,4 +222,29 @@ class ContactPage extends BasePage {
222 }
223 });
224 }
225 +
226 + Future<bool> showParsedAddressConfirmationAlert(
227 + BuildContext context,
228 + ParsedAddress parsedAddress,
229 + ) async {
230 + final confirmed = await showPopUp<bool>(
231 + context: context,
232 + builder: (BuildContext context) {
233 + return AlertWithOneAction(
234 + alertTitle: S.of(context).address_detected,
235 + headerTitleText: parsedAddress.profileName.isEmpty ? null : parsedAddress.profileName,
236 + headerImageProfileUrl: parsedAddress.profileImageUrl.isEmpty
237 + ? parsedAddress.addressSource.iconPath
238 + : parsedAddress.profileImageUrl,
239 + alertContent: S.of(context).extracted_address_content(
240 + '${parsedAddress.handle} (${parsedAddress.addressSource.label})',
241 + ),
242 + buttonText: S.of(context).ok,
243 + buttonAction: () => Navigator.of(context).pop(true),
244 + );
245 + },
246 + );
247 +
248 + return confirmed ?? false;
249 + }
250 }
lib/src/screens/exchange/exchange_page.dart
+5 -5
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
2 import 'package:cake_wallet/exchange/exchange_provider_description.dart';
3 import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
4 import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
@@ -11,8 +12,6 @@ import 'package:cake_wallet/utils/payment_request.dart';
12 import 'package:cake_wallet/utils/responsive_layout_util.dart';
13 import 'package:cw_core/sync_status.dart';
14 import 'package:cw_core/utils/print_verbose.dart';
14 -import 'package:cake_wallet/entities/parse_address_from_domain.dart';
15 -import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
15 import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
16 import 'package:flutter/material.dart';
17 import 'package:flutter_mobx/flutter_mobx.dart';
@@ -41,7 +40,7 @@ import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker
40 import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
41
42 class ExchangePage extends BasePage {
44 - ExchangePage(this.exchangeViewModel, this.authService, this.initialPaymentRequest) {
43 + ExchangePage(this.exchangeViewModel, this.authService, this.adrResService, this.initialPaymentRequest) {
44 depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
45 ? exchangeViewModel.wallet.name
46 : null;
@@ -52,6 +51,7 @@ class ExchangePage extends BasePage {
51
52 final ExchangeViewModel exchangeViewModel;
53 final AuthService authService;
54 + final AddressResolverService adrResService;
55 final PaymentRequest? initialPaymentRequest;
56 final depositKey = GlobalKey<ExchangeCardState>();
57 final receiveKey = GlobalKey<ExchangeCardState>();
@@ -655,8 +655,8 @@ class ExchangePage extends BasePage {
655
656 Future<String> fetchParsedAddress(
657 BuildContext context, String domain, CryptoCurrency currency) async {
658 - final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
659 - return extractAddressFromParsed(context, parsedAddress);
658 + final parsedAddresses = await adrResService.resolve(query: domain, wallet:exchangeViewModel.wallet, currency: currency);
659 + return parsedAddresses.isNotEmpty ? parsedAddresses.first.parsedAddressByCurrencyMap[currency] ?? '' : '';
660 }
661
662 void _showFeeAlert(BuildContext context) async {
lib/src/screens/send/send_page.dart
-1
@@ -780,7 +780,6 @@ class SendPage extends BasePage {
780 }
781
782 output.resetParsedAddress();
783 - await output.fetchParsedAddress(context);
783 }
784
785 Output _defineCurrentOutput() {
lib/src/screens/send/widgets/extract_address_from_parsed.dart deleted
-150
@@ -1,150 +0,0 @@
1 -import 'package:cake_wallet/entities/parsed_address.dart';
2 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
3 -import 'package:cake_wallet/utils/show_pop_up.dart';
4 -import 'package:flutter/material.dart';
5 -import 'package:cake_wallet/generated/i18n.dart';
6 -import 'choose_yat_address_alert.dart';
7 -
8 -Future<String> extractAddressFromParsed(
9 - BuildContext? context,
10 - ParsedAddress parsedAddress) async {
11 - if (context == null || !context.mounted) return parsedAddress.addresses.first;
12 -
13 - var title = '';
14 - var content = '';
15 - var address = '';
16 - var profileImageUrl = '';
17 - var profileName = '';
18 -
19 - switch (parsedAddress.parseFrom) {
20 - case ParseFrom.unstoppableDomains:
21 - title = S.of(context).address_detected;
22 - content = S.of(context).address_from_domain(parsedAddress.name);
23 - address = parsedAddress.addresses.first;
24 - break;
25 - case ParseFrom.ens:
26 - title = S.of(context).address_detected;
27 - content = S.of(context).extracted_address_content('${parsedAddress.name} (ENS)');
28 - address = parsedAddress.addresses.first;
29 - break;
30 - case ParseFrom.openAlias:
31 - title = S.of(context).address_detected;
32 - content = S.of(context).extracted_address_content('${parsedAddress.name} (OpenAlias)');
33 - address = parsedAddress.addresses.first;
34 - break;
35 - case ParseFrom.wellKnown:
36 - title = S.of(context).address_detected;
37 - content = S.of(context).extracted_address_content('${parsedAddress.name} (Well-Known)');
38 - address = parsedAddress.addresses.first;
39 - break;
40 - case ParseFrom.fio:
41 - title = S.of(context).address_detected;
42 - content = S.of(context).extracted_address_content('${parsedAddress.name} (FIO)');
43 - address = parsedAddress.addresses.first;
44 - break;
45 - case ParseFrom.twitter:
46 - title = S.of(context).address_detected;
47 - content = S.of(context).extracted_address_content('${parsedAddress.name} (Twitter)');
48 - address = parsedAddress.addresses.first;
49 - profileImageUrl = parsedAddress.profileImageUrl;
50 - profileName = parsedAddress.profileName;
51 - break;
52 - case ParseFrom.mastodon:
53 - title = S.of(context).address_detected;
54 - content = S.of(context).extracted_address_content('${parsedAddress.name} (Mastodon)');
55 - address = parsedAddress.addresses.first;
56 - profileImageUrl = parsedAddress.profileImageUrl;
57 - profileName = parsedAddress.profileName;
58 - break;
59 - case ParseFrom.nostr:
60 - title = S.of(context).address_detected;
61 - content = S.of(context).extracted_address_content('${parsedAddress.name} (Nostr NIP-05)');
62 - address = parsedAddress.addresses.first;
63 - profileImageUrl = parsedAddress.profileImageUrl;
64 - profileName = parsedAddress.profileName;
65 - break;
66 - case ParseFrom.thorChain:
67 - title = S.of(context).address_detected;
68 - content = S.of(context).extracted_address_content('${parsedAddress.name} (ThorChain)');
69 - address = parsedAddress.addresses.first;
70 - break;
71 - case ParseFrom.zanoAlias:
72 - title = S.of(context).address_detected;
73 - content = S.of(context).extracted_address_content('${parsedAddress.name} (Zano Alias)');
74 - address = parsedAddress.addresses.first;
75 - break;
76 - case ParseFrom.zcashAddress:
77 - title = S.of(context).address_detected;
78 - content = S.of(context).extracted_address_content('${parsedAddress.name} (Zcash.me)');
79 - address = parsedAddress.addresses.first;
80 - break;
81 - case ParseFrom.zcashName:
82 - title = S.of(context).address_detected;
83 - content = S.of(context).extracted_address_content('${parsedAddress.name} (Zcash Names)');
84 - address = parsedAddress.addresses.first;
85 - break;
86 - case ParseFrom.bip353:
87 - title = S.of(context).address_detected;
88 - content = S.of(context).extracted_address_content('${parsedAddress.name} (BIP-353)');
89 - address = parsedAddress.addresses.first;
90 - break;
91 - case ParseFrom.lnurlpay:
92 - title = S.of(context).address_detected;
93 - content = S.of(context).extracted_address_content('${parsedAddress.name} (Lightning)');
94 - address = parsedAddress.addresses.first;
95 - break;
96 - case ParseFrom.yatRecord:
97 - if (parsedAddress.name.isEmpty) {
98 - title = S.of(context).yat_error;
99 - content = S.of(context).yat_error_content;
100 - address = parsedAddress.addresses.first;
101 - break;
102 - }
103 -
104 - title = S.of(context).address_detected;
105 - content = S.of(context).address_from_yat(parsedAddress.name);
106 -
107 - if (parsedAddress.addresses.length == 1) {
108 - address = parsedAddress.addresses.first;
109 - break;
110 - }
111 -
112 - content += S.of(context).choose_address;
113 -
114 - address = await showPopUp<String?>(
115 - context: context,
116 - builder: (context) => PopScope(
117 - child: ChooseYatAddressAlert(
118 - alertTitle: title,
119 - alertContent: content,
120 - addresses: parsedAddress.addresses,
121 - ),
122 - canPop: false,
123 - ),
124 - ) ??
125 - '';
126 -
127 - if (address.isEmpty) {
128 - return parsedAddress.name;
129 - }
130 -
131 - return address;
132 - case ParseFrom.contact:
133 - case ParseFrom.notParsed:
134 - return parsedAddress.addresses.first;
135 - }
136 -
137 - await showPopUp<void>(
138 - context: context,
139 - builder: (context) => AlertWithOneAction(
140 - alertTitle: title,
141 - headerTitleText: profileName.isEmpty ? null : profileName,
142 - headerImageProfileUrl: profileImageUrl.isEmpty ? null : profileImageUrl,
143 - alertContent: content,
144 - buttonText: S.of(context).ok,
145 - buttonAction: () => Navigator.of(context).pop(),
146 - ),
147 - );
148 -
149 - return address;
150 -}
lib/src/screens/send/widgets/send_card.dart
-5
@@ -594,7 +594,6 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
594 controller: addressController,
595 onURIScanned: (uri) async {
596 output.resetParsedAddress();
597 - await output.fetchParsedAddress(context);
597
598 // Process the payment through the new flow
599 await _handlePaymentFlow(
@@ -620,7 +619,6 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
619 _justHandledPasteButton = true;
620 try {
621 output.resetParsedAddress();
623 - await output.fetchParsedAddress(context);
622
623 final address =
624 output.isParsedAddress ? output.extractedAddress : output.address;
@@ -643,7 +641,6 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
641 output.resetParsedAddress();
642 },
643 onSelectedContact: (contact) {
646 - output.loadContact(contact);
644 },
645 validator: validator,
646 selectedCurrency: sendViewModel.selectedCryptoCurrency,
@@ -1033,8 +1030,6 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
1030 if (current.isEmpty) return;
1031 if (_justHandledPasteButton || _lastHandledAddress == current) return;
1032
1036 - await output.fetchParsedAddress(context);
1037 -
1033 // If it's a URI with params, go through URI flow
1034 if (current.contains('=')) {
1035 try {
lib/src/screens/send/widgets/send_template_card.dart
-2
@@ -92,11 +92,9 @@ class SendTemplateCard extends StatelessWidget {
92 ],
93 onPushPasteButton: (context) async {
94 template.output.resetParsedAddress();
95 - await template.output.fetchParsedAddress(context);
95 },
96 onPushAddressBookButton: (context) async {
97 template.output.resetParsedAddress();
99 - await template.output.fetchParsedAddress(context);
98 },
99 buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
100 textStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
lib/src/screens/settings/domain_lookups_page.dart
+16 -41
@@ -1,6 +1,7 @@
1 import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/src/screens/base_page.dart';
3 import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
4 +import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
5 import 'package:cake_wallet/view_model/settings/connection_sync_view_model.dart';
6 import 'package:flutter/material.dart';
7 import 'package:flutter_mobx/flutter_mobx.dart';
@@ -20,47 +21,21 @@ class DomainLookupsPage extends BasePage {
21 return Container(
22 padding: EdgeInsets.only(top: 10),
23 child: Column(
23 - children: [
24 - SettingsSwitcherCell(
25 - title: 'Twitter',
26 - value: _connectionsSyncViewModel.lookupTwitter,
27 - onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsTwitter(value)),
28 - SettingsSwitcherCell(
29 - title: 'Mastodon',
30 - value: _connectionsSyncViewModel.looksUpMastodon,
31 - onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsMastodon(value)),
32 - SettingsSwitcherCell(
33 - title: 'Yat service',
34 - value: _connectionsSyncViewModel.looksUpYatService,
35 - onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsYatService(value)),
36 - SettingsSwitcherCell(
37 - title: 'Unstoppable Domains',
38 - value: _connectionsSyncViewModel.looksUpUnstoppableDomains,
39 - onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsUnstoppableDomains(value)),
40 - SettingsSwitcherCell(
41 - title: 'OpenAlias',
42 - value: _connectionsSyncViewModel.looksUpOpenAlias,
43 - onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsOpenAlias(value)),
44 - SettingsSwitcherCell(
45 - title: 'Ethereum Name Service',
46 - value: _connectionsSyncViewModel.looksUpENS,
47 - onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsENS(value)),
48 - SettingsSwitcherCell(
49 - title: 'Zcash Names',
50 - value: _connectionsSyncViewModel.lookupsZcashNames,
51 - onValueChange: (_, bool value) =>
52 - _connectionsSyncViewModel.setLookupsZcashNames(value)),
53 - SettingsSwitcherCell(
54 - title: '.well-known',
55 - value: _connectionsSyncViewModel.looksUpWellKnown,
56 - onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsWellKnown(value)),
57 - SettingsSwitcherCell(
58 - title: 'Zano Aliases',
59 - value: _connectionsSyncViewModel.lookupsZanoAlias,
60 - onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsZanoAlias(value)),
61 -
62 - //if (!isHaven) it does not work correctly
63 - ],
24 + children: _connectionsSyncViewModel.domainLookupSources
25 + .map(
26 + (source) => SettingsSwitcherCell(
27 + title: source.label,
28 + leading: source.iconPath.isNotEmpty ? CakeImageWidget(
29 + imageUrl: source.iconPath,
30 + width: 24,
31 + height: 24,
32 + ) : SizedBox(width: 24, height: 24),
33 + value: _connectionsSyncViewModel.lookupValue(source),
34 + onValueChange: (_, bool value) =>
35 + _connectionsSyncViewModel.setLookupValue(source, value),
36 + ),
37 + )
38 + .toList(),
39 ),
40 );
41 }),
lib/src/screens/wallet_list/filtered_list.dart
+3
@@ -10,6 +10,7 @@ class FilteredList extends StatefulWidget {
10 this.canReorder = true,
11 this.shrinkWrap = false,
12 this.physics,
13 + this.padding,
14 });
15
16 final ObservableList<dynamic> list;
@@ -18,6 +19,7 @@ class FilteredList extends StatefulWidget {
19 final bool canReorder;
20 final bool shrinkWrap;
21 final ScrollPhysics? physics;
22 + final EdgeInsets? padding;
23
24 @override
25 FilteredListState createState() => FilteredListState();
@@ -48,6 +50,7 @@ class FilteredListState extends State<FilteredList> {
50 ),
51 shrinkWrap: widget.shrinkWrap,
52 physics: widget.physics ?? const BouncingScrollPhysics(),
53 + padding: widget.padding,
54 itemBuilder: widget.itemBuilder,
55 itemCount: widget.list.length,
56 onReorder: (int oldIndex, int newIndex) {
lib/src/screens/yat/yat_popup.dart
+1 -1
@@ -1,10 +1,10 @@
1 +import 'package:cake_wallet/core/address_resolver/yat/yat_store.dart';
2 import 'package:cake_wallet/src/screens/yat/widgets/first_introduction.dart';
3 import 'package:cake_wallet/src/screens/yat/widgets/second_introduction.dart';
4 import 'package:cake_wallet/src/screens/yat/widgets/third_introduction.dart';
5 import 'package:cake_wallet/src/screens/yat/widgets/yat_close_button.dart';
6 import 'package:cake_wallet/src/widgets/alert_background.dart';
7 import 'package:cake_wallet/src/widgets/primary_button.dart';
7 -import 'package:cake_wallet/store/yat/yat_store.dart';
8 import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
9 import 'package:flutter/material.dart';
10 import 'package:animate_do/animate_do.dart';
lib/src/widgets/base_alert_dialog.dart
+2 -4
@@ -1,6 +1,7 @@
1 import 'dart:ui';
2
3 import 'package:auto_size_text/auto_size_text.dart';
4 +import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
5 import 'package:cake_wallet/src/widgets/section_divider.dart';
6 import 'package:flutter/material.dart';
7
@@ -184,10 +185,7 @@ class BaseAlertDialog extends StatelessWidget {
185 radius: 50,
186 backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
187 child: ClipOval(
187 - child: Image.network(
188 - imageUrl,
189 - fit: BoxFit.cover,
190 - ),
188 + child: CakeImageWidget (imageUrl: imageUrl, width: 100, height: 100, fit: BoxFit.cover),
189 ),
190 ),
191 );
lib/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart
+1 -1
@@ -163,7 +163,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
163 itemBuilder: (context, index) {
164 final isBatchSending = outputs.length > 1;
165 final item = outputs[index];
166 - final contactName = item.parsedAddress.name;
166 + final contactName = item.parsedAddress.profileName;
167 final isCakePayName = contactName == 'Cake Pay';
168 final batchContactTitle =
169 '${index + 1}/${outputs.length} - ${contactName.isEmpty ? 'Address' : contactName}';
lib/store/settings_store.dart
+67 -2
@@ -132,7 +132,13 @@ abstract class SettingsStoreBase with Store {
132 required this.lookupsOpenAlias,
133 required this.lookupsENS,
134 required this.lookupsZcashNames,
135 + required this.lookupsZcashAddress,
136 required this.lookupsWellKnown,
137 + required this.lookupsFio,
138 + required this.lookupsNostr,
139 + required this.lookupsThorChain,
140 + required this.lookupsBip353,
141 + required this.lookupsLNUrl,
142 required this.usePayjoin,
143 required this.showPayjoinCard,
144 required this.customBitcoinFeeRate,
@@ -567,11 +573,41 @@ abstract class SettingsStoreBase with Store {
573 (bool looksUpZcashNames) => _sharedPreferences.setBool(
574 PreferencesKey.lookupsZcashNames, looksUpZcashNames));
575
576 + reaction(
577 + (_) => lookupsZcashAddress,
578 + (bool lookupsZcashAddress) => _sharedPreferences.setBool(
579 + PreferencesKey.lookupsZcashAddress, lookupsZcashAddress));
580 +
581 reaction(
582 (_) => lookupsWellKnown,
583 (bool looksUpWellKnown) =>
584 _sharedPreferences.setBool(PreferencesKey.lookupsWellKnown, looksUpWellKnown));
585
586 + reaction(
587 + (_) => lookupsFio,
588 + (bool lookupsFio) =>
589 + _sharedPreferences.setBool(PreferencesKey.lookupsFio, lookupsFio));
590 +
591 + reaction(
592 + (_) => lookupsNostr,
593 + (bool lookupsNostr) =>
594 + _sharedPreferences.setBool(PreferencesKey.lookupsNostr, lookupsNostr));
595 +
596 + reaction(
597 + (_) => lookupsThorChain,
598 + (bool lookupsThorChain) =>
599 + _sharedPreferences.setBool(PreferencesKey.lookupsThorChain, lookupsThorChain));
600 +
601 + reaction(
602 + (_) => lookupsBip353,
603 + (bool lookupsBip353) =>
604 + _sharedPreferences.setBool(PreferencesKey.lookupsBip353, lookupsBip353));
605 +
606 + reaction(
607 + (_) => lookupsLNUrl,
608 + (bool lookupsLNUrl) =>
609 + _sharedPreferences.setBool(PreferencesKey.lookupsLNUrl, lookupsLNUrl));
610 +
611 reaction((_) => usePayjoin,
612 (bool usePayjoin) => _sharedPreferences.setBool(PreferencesKey.usePayjoin, usePayjoin));
613
@@ -962,9 +998,27 @@ abstract class SettingsStoreBase with Store {
998 @observable
999 bool lookupsZcashNames;
1000
1001 + @observable
1002 + bool lookupsZcashAddress;
1003 +
1004 @observable
1005 bool lookupsWellKnown;
1006
1007 + @observable
1008 + bool lookupsFio;
1009 +
1010 + @observable
1011 + bool lookupsNostr;
1012 +
1013 + @observable
1014 + bool lookupsThorChain;
1015 +
1016 + @observable
1017 + bool lookupsBip353;
1018 +
1019 + @observable
1020 + bool lookupsLNUrl;
1021 +
1022 @observable
1023 bool usePayjoin;
1024
@@ -1268,9 +1322,14 @@ abstract class SettingsStoreBase with Store {
1322 sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains) ?? true;
1323 final lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true;
1324 final lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true;
1271 - final lookupsZcashNames =
1272 - sharedPreferences.getBool(PreferencesKey.lookupsZcashNames) ?? true;
1325 + final lookupsZcashNames = sharedPreferences.getBool(PreferencesKey.lookupsZcashNames) ?? true;
1326 + final lookupsZcashAddress = sharedPreferences.getBool(PreferencesKey.lookupsZcashAddress) ?? true;
1327 final lookupsWellKnown = sharedPreferences.getBool(PreferencesKey.lookupsWellKnown) ?? true;
1328 + final lookupsFio = sharedPreferences.getBool(PreferencesKey.lookupsFio) ?? true;
1329 + final lookupsNostr = sharedPreferences.getBool(PreferencesKey.lookupsNostr) ?? true;
1330 + final lookupsThorChain = sharedPreferences.getBool(PreferencesKey.lookupsThorChain) ?? false;
1331 + final lookupsBip353 = sharedPreferences.getBool(PreferencesKey.lookupsBip353) ?? true;
1332 + final lookupsLNUrl = sharedPreferences.getBool(PreferencesKey.lookupsLNUrl) ?? true;
1333 final usePayjoin = sharedPreferences.getBool(PreferencesKey.usePayjoin) ?? false;
1334 final showPayjoinCard = sharedPreferences.getBool(PreferencesKey.showPayjoinCard) ?? true;
1335 final customBitcoinFeeRate = sharedPreferences.getInt(PreferencesKey.customBitcoinFeeRate) ?? 1;
@@ -1659,7 +1718,13 @@ abstract class SettingsStoreBase with Store {
1718 lookupsOpenAlias: lookupsOpenAlias,
1719 lookupsENS: lookupsENS,
1720 lookupsZcashNames: lookupsZcashNames,
1721 + lookupsZcashAddress: lookupsZcashAddress,
1722 lookupsWellKnown: lookupsWellKnown,
1723 + lookupsFio: lookupsFio,
1724 + lookupsNostr: lookupsNostr,
1725 + lookupsThorChain: lookupsThorChain,
1726 + lookupsBip353: lookupsBip353,
1727 + lookupsLNUrl: lookupsLNUrl,
1728 usePayjoin: usePayjoin,
1729 showPayjoinCard: showPayjoinCard,
1730 customBitcoinFeeRate: customBitcoinFeeRate,
lib/store/yat/yat_exception.dart deleted
-10
@@ -1,10 +0,0 @@
1 -import 'package:flutter/foundation.dart';
2 -
3 -class YatException implements Exception {
4 - YatException({required this.text});
5 -
6 - final String text;
7 -
8 - @override
9 - String toString() => 'Yat exception: $text';
10 -}
\ No newline at end of file
lib/view_model/contact_list/contact_view_model.dart
+52 -9
@@ -1,7 +1,10 @@
1 +import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
2 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
3 +import 'package:cake_wallet/core/address_validator.dart';
4 import 'package:cake_wallet/di.dart';
5 import 'package:cake_wallet/entities/contact_record.dart';
3 -import 'package:cake_wallet/entities/parse_address_from_domain.dart';
4 -import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
6 +import 'package:cake_wallet/main.dart';
7 +import 'package:cake_wallet/store/app_store.dart';
8 import 'package:cw_core/utils/print_verbose.dart';
9 import 'package:flutter/material.dart';
10 import 'package:hive/hive.dart';
@@ -16,7 +19,8 @@ part 'contact_view_model.g.dart';
19 class ContactViewModel = ContactViewModelBase with _$ContactViewModel;
20
21 abstract class ContactViewModelBase with Store {
19 - ContactViewModelBase(this._contacts, {ContactRecord? contact})
22 + ContactViewModelBase(this._contacts, this.appStore,this.adrResService,
23 + {ContactRecord? contact})
24 : state = InitialExecutionState(),
25 currencies = CryptoCurrency.all,
26 _contact = contact,
@@ -26,6 +30,9 @@ abstract class ContactViewModelBase with Store {
30 currency = contact?.type,
31 lastChange = contact?.lastChange;
32
33 + final AppStore appStore;
34 + final AddressResolverService adrResService;
35 +
36
37 @observable
38 ExecutionState state;
@@ -58,16 +65,52 @@ abstract class ContactViewModelBase with Store {
65 void reset() {
66 address = '';
67 name = '';
68 + displayName = '';
69 currency = null;
70 }
71
64 - Future<void> extractParsedAddress(BuildContext context) async{
65 - if(currency == null) return;
66 - final parsedAddress = await getIt.get<AddressResolver>().resolve(context, address, currency!);
67 - if(parsedAddress.name.isNotEmpty) {
68 - displayName = parsedAddress.name;
72 + Future<ParsedAddress?> extractParsedAddress(BuildContext context) async {
73 + final wallet = appStore.wallet;
74 + final currentCurrency = currency;
75 + final query = address.trim();
76 +
77 + if (wallet == null) return null;
78 + if (currentCurrency == null) return null;
79 + if (query.isEmpty) return null;
80 +
81 +
82 + // Check if the address is valid for the current currency (except for Zano, which can use handles as addresses)
83 + if (currentCurrency != CryptoCurrency.zano) {
84 + final isValidAddress = AddressValidator(type: currentCurrency).isValid(query);
85 + if (isValidAddress) return null;
86 }
70 - address = await extractAddressFromParsed(null, parsedAddress);
87 +
88 + final parsedAddresses = await adrResService.resolve(
89 + query: query,
90 + wallet: wallet,
91 + currency: currentCurrency,
92 + );
93 +
94 + if (parsedAddresses.isEmpty) return null;
95 +
96 + final resolvedAddress = parsedAddresses.first.parsedAddressByCurrencyMap[currentCurrency];
97 + if (resolvedAddress == null || resolvedAddress.isEmpty) return null;
98 +
99 + return parsedAddresses.first;
100 + }
101 +
102 + @action
103 + void applyParsedAddress(ParsedAddress parsedAddress) {
104 + final currentCurrency = currency;
105 + if (currentCurrency == null) return;
106 +
107 + final resolvedAddress = parsedAddress.parsedAddressByCurrencyMap[currentCurrency];
108 + if (resolvedAddress == null || resolvedAddress.isEmpty) return;
109 +
110 + address = resolvedAddress;
111 + displayName = parsedAddress.profileName.isNotEmpty
112 + ? parsedAddress.profileName
113 + : parsedAddress.handle;
114 }
115
116 Future<void> save() async {
lib/view_model/dashboard/dashboard_view_model.dart
+1 -1
@@ -4,6 +4,7 @@ import 'dart:io' show Platform;
4
5 import 'package:cake_wallet/.secrets.g.dart' as secrets;
6 import 'package:cake_wallet/bitcoin/bitcoin.dart';
7 +import 'package:cake_wallet/core/address_resolver/yat/yat_store.dart';
8 import 'package:cake_wallet/core/key_service.dart';
9 import "package:cw_core/balance_card_style_settings.dart";
10 import 'package:cake_wallet/core/trade_monitor.dart';
@@ -35,7 +36,6 @@ import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
36 import 'package:cake_wallet/store/dashboard/trades_store.dart';
37 import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
38 import 'package:cake_wallet/store/settings_store.dart';
38 -import 'package:cake_wallet/store/yat/yat_store.dart';
39 import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
40 import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
41 import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
lib/view_model/restore/wallet_restore_from_qr_code.dart
+2 -2
@@ -1,7 +1,7 @@
1 import 'dart:convert';
2
3 +import 'package:cake_wallet/core/address_resolver/address_resolver_utils.dart';
4 import 'package:cake_wallet/core/seed_validator.dart';
4 -import 'package:cake_wallet/entities/parse_address_from_domain.dart';
5 import 'package:cake_wallet/entities/qr_scanner.dart';
6 import 'package:cake_wallet/reactions/wallet_connect.dart';
7 import 'package:cake_wallet/routes.dart';
@@ -87,7 +87,7 @@ class WalletRestoreFromQRCode {
87
88 static String? _extractAddressFromUrl(String rawString, WalletType type) {
89 try {
90 - return AddressResolver.extractAddressByType(
90 + return AddressResolverUtils.extractAddressByType(
91 raw: rawString,
92 type: walletTypeToCryptoCurrency(
93 type,
lib/view_model/send/output.dart
+35 -28
@@ -1,16 +1,16 @@
1 import 'dart:math' show min;
2 import 'package:cake_wallet/bitcoin/bitcoin.dart';
3 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
4 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
5 +import 'package:cake_wallet/decred/decred.dart';
6 import 'package:cake_wallet/di.dart';
7 import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
8 import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
9 import 'package:cake_wallet/entities/contact_base.dart';
7 -import 'package:cake_wallet/entities/parse_address_from_domain.dart';
8 -import 'package:cake_wallet/entities/parsed_address.dart';
10 import 'package:cake_wallet/evm/evm.dart';
11 import 'package:cake_wallet/generated/i18n.dart';
12 import 'package:cake_wallet/reactions/wallet_connect.dart';
13 import 'package:cake_wallet/solana/solana.dart';
13 -import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
14 import 'package:cake_wallet/store/app_store.dart';
15 import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
16 import 'package:cake_wallet/store/settings_store.dart';
@@ -37,7 +37,8 @@ class Output = OutputBase with _$Output;
37
38 abstract class OutputBase with Store {
39 OutputBase(this._wallet, this._appStore, this._fiatConversationStore, this.cryptoCurrencyHandler)
40 - : key = UniqueKey(),
40 + :
41 + key = UniqueKey(),
42 sendAll = false,
43 cryptoAmount = '',
44 cryptoFullBalance = '',
@@ -47,7 +48,7 @@ abstract class OutputBase with Store {
48 memo = "",
49 extractedAddress = '',
50 estimatedFee = Money.zero(cryptoCurrencyHandler()),
50 - parsedAddress = ParsedAddress(addresses: []) {
51 + parsedAddress = ParsedAddress(parsedAddressByCurrencyMap: {}) {
52 autorun((_) {
53 final status = _wallet.syncStatus;
54 printV("Sync status changed to $status. Recalculating fees");
@@ -56,6 +57,7 @@ abstract class OutputBase with Store {
57 });
58 }
59
60 +
61 Key key;
62
63 bool get useSatoshi => _appStore.amountParsingProxy.useSatoshi(cryptoCurrencyHandler());
@@ -83,7 +85,7 @@ abstract class OutputBase with Store {
85
86 @observable
87 String note;
86 -
88 +
89 @observable
90 String memo;
91
@@ -99,7 +101,7 @@ abstract class OutputBase with Store {
101
102 @computed
103 bool get isParsedAddress =>
102 - parsedAddress.parseFrom != ParseFrom.notParsed && parsedAddress.name.isNotEmpty;
104 + parsedAddress.addressSource != AddressSource.notParsed && parsedAddress.handle.isNotEmpty;
105
106 String roundedCryptoAmount(int digits) => displayCryptoAmount.withMaxDecimals(digits);
107
@@ -272,10 +274,22 @@ abstract class OutputBase with Store {
274 resetParsedAddress();
275 }
276
277 + @action
278 void resetParsedAddress() {
279 displayName = null;
280 extractedAddress = '';
278 - parsedAddress = ParsedAddress(addresses: []);
281 + note = '';
282 + parsedAddress = ParsedAddress(parsedAddressByCurrencyMap: {});
283 + }
284 +
285 + @action
286 + void applyAddressLookupResult(ParsedAddress result) {
287 + final currency = cryptoCurrencyHandler();
288 +
289 + parsedAddress = result;
290 + extractedAddress = result.parsedAddressByCurrencyMap[currency] ?? '';
291 + note = result.description;
292 + displayName = result.profileName.isNotEmpty ? result.profileName : result.handle;
293 }
294
295 @action
@@ -334,33 +348,26 @@ abstract class OutputBase with Store {
348
349 Map<String, dynamic> get extra {
350 final fields = <String, dynamic>{};
337 - if (parsedAddress.parseFrom == ParseFrom.bip353) {
338 - fields['bip353_name'] = parsedAddress.name;
351 + if (parsedAddress.addressSource == AddressSource.bip353) {
352 + fields['bip353_name'] = parsedAddress.handle;
353 fields['bip353_proof'] = parsedAddress.bip353DnsProof;
354 }
355 return fields;
356 }
357
344 - Future<void> fetchParsedAddress(BuildContext context) async {
345 - final domain = address;
358 + @action
359 + void loadContact(ContactBase contact) {
360 final currency = cryptoCurrencyHandler();
347 - parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
348 - if(parsedAddress.name.isNotEmpty) {
349 - displayName = parsedAddress.name;
350 - }
351 -
352 - if (parsedAddress.parseFrom == ParseFrom.lnurlpay) {
353 - cryptoCurrencyHandler(CryptoCurrency.btcln);
354 - }
355 - extractedAddress = await extractAddressFromParsed(context, parsedAddress);
356 - note = parsedAddress.description;
357 - }
361
359 - void loadContact(ContactBase contact) {
360 - address = contact.name;
361 - parsedAddress = ParsedAddress.fetchContactAddress(address: contact.address, name: contact.name);
362 - extractedAddress = parsedAddress.addresses.first;
363 - note = parsedAddress.description;
362 + address = contact.address;
363 + applyAddressLookupResult(
364 + ParsedAddress(
365 + parsedAddressByCurrencyMap: {currency: contact.address},
366 + addressSource: AddressSource.contact,
367 + handle: contact.name,
368 + profileName: contact.name,
369 + ),
370 + );
371 }
372 }
373
lib/view_model/send/send_view_model.dart
+38 -12
@@ -1,6 +1,9 @@
1 import 'dart:async';
2
3 import 'package:cake_wallet/bitcoin/bitcoin.dart';
4 +import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
5 +import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
6 +import 'package:cake_wallet/di.dart';
7 import 'package:cake_wallet/core/address_validator.dart';
8 import 'package:cake_wallet/core/amount_parsing_proxy.dart';
9 import 'package:cake_wallet/core/amount_validator.dart';
@@ -17,7 +20,6 @@ import 'package:cake_wallet/entities/contact.dart';
20 import 'package:cake_wallet/entities/contact_record.dart';
21 import 'package:cake_wallet/entities/evm_transaction_error_fees_handler.dart';
22 import 'package:cake_wallet/entities/fiat_currency.dart';
20 -import 'package:cake_wallet/entities/parsed_address.dart';
23 import 'package:cake_wallet/entities/preferences_key.dart';
24 import 'package:cake_wallet/entities/template.dart';
25 import 'package:cake_wallet/entities/transaction_description.dart';
@@ -103,6 +105,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
105 this._appStore,
106 this.sendTemplateViewModel,
107 this._fiatConversationStore,
108 + this._adrResService,
109 this.balanceViewModel,
110 this.contactListViewModel,
111 this.transactionDescriptionBox,
@@ -182,6 +185,26 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
185 if (isBatchSending) outputs.remove(output);
186 }
187
188 + Future<ParsedAddress?> resolveAddressForOutput(Output output) async {
189 + final query = output.address.trim();
190 + if (query.isEmpty) return null;
191 +
192 + // Check if the address is valid for the current currency (except for Zano, which can use handles as addresses)
193 + if (selectedCryptoCurrency != CryptoCurrency.zano) {
194 + final isValidAddress = AddressValidator(type: selectedCryptoCurrency).isValid(query);
195 + if (isValidAddress) return null;
196 + }
197 +
198 +
199 + final results = await _adrResService.resolve(
200 + query: query,
201 + wallet: wallet,
202 + currency: selectedCryptoCurrency,
203 + );
204 +
205 + return results.isEmpty ? null : results.first;
206 + }
207 +
208 @action
209 void clearOutputs() {
210 outputs.clear();
@@ -383,14 +406,17 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
406 // If silent payments scanning, can still send payments
407 (wallet.type == WalletType.bitcoin && wallet.syncStatus is SyncingSyncStatus);
408
386 - bool isSendToSilentPayments(Output output) =>
387 - wallet.type == WalletType.bitcoin &&
388 - (RegExp(AddressValidator.silentPaymentAddressPatternMainnet).hasMatch(output.address) ||
389 - RegExp(AddressValidator.silentPaymentAddressPatternMainnet)
390 - .hasMatch(output.extractedAddress) ||
391 - (output.parsedAddress.addresses.isNotEmpty &&
392 - RegExp(AddressValidator.silentPaymentAddressPatternMainnet)
393 - .hasMatch(output.parsedAddress.addresses[0])));
409 + bool isSendToSilentPayments(Output output) {
410 + if (wallet.type != WalletType.bitcoin) return false;
411 +
412 + final sp = RegExp(AddressValidator.silentPaymentAddressPatternMainnet);
413 +
414 + final address = output.extractedAddress.isNotEmpty
415 + ? output.extractedAddress
416 + : output.address;
417 +
418 + return sp.hasMatch(address);
419 + }
420
421 @computed
422 List<Template> get templates => sendTemplateViewModel.templates
@@ -454,6 +480,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
480 final HardwareWalletViewModel? hardwareWalletViewModel;
481 final FeesViewModel feesViewModel;
482 final FiatConversionStore _fiatConversationStore;
483 + final AddressResolverService _adrResService;
484 final Box<TransactionDescription> transactionDescriptionBox;
485
486 @computed
@@ -606,8 +633,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
633 clearOutputs();
634
635 outputs.first.address = paymentRequest.address;
609 - outputs.first.parsedAddress =
610 - ParsedAddress(addresses: [paymentRequest.address], name: ocpRequest!.receiverName);
636 + outputs.first.parsedAddress = ParsedAddress(parsedAddressByCurrencyMap: {currency:paymentRequest.address}, handle: ocpRequest!.receiverName);
637 outputs.first.setCryptoAmount(paymentRequest.amount);
638 outputs.first.note = ocpRequest!.receiverName;
639
@@ -1342,7 +1368,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
1368
1369 for (final output in outputs) {
1370 final address =
1345 - output.isParsedAddress ? output.parsedAddress.addresses.first : output.address;
1371 + output.isParsedAddress ? output.parsedAddress.parsedAddressByCurrencyMap[selectedCryptoCurrency] ?? '' : output.address;
1372
1373 if (address.isNotEmpty &&
1374 !contactAddresses.contains(address) &&
lib/view_model/settings/connection_sync_view_model.dart
+113 -26
@@ -1,5 +1,7 @@
1 import 'dart:async';
2
3 +import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
4 +import 'package:cake_wallet/core/address_resolver/address_sources.dart';
5 import 'package:cake_wallet/entities/exchange_api_mode.dart';
6 import 'package:cake_wallet/entities/fiat_api_mode.dart';
7 import 'package:cake_wallet/evm/evm.dart';
@@ -55,6 +57,24 @@ abstract class ConnectionSyncViewModelBase with Store {
57 @computed
58 bool get looksUpWellKnown => _settingsStore.lookupsWellKnown;
59
60 + @computed
61 + bool get lookupsNostr => _settingsStore.lookupsNostr;
62 +
63 + @computed
64 + bool get lookupsFio => _settingsStore.lookupsFio;
65 +
66 + @computed
67 + bool get lookupsBip353 => _settingsStore.lookupsBip353;
68 +
69 + @computed
70 + bool get lookupsThorChain => _settingsStore.lookupsThorChain;
71 +
72 + @computed
73 + bool get lookupsZcashAddress => _settingsStore.lookupsZcashAddress;
74 +
75 + @computed
76 + bool get lookupsLNUrlPay => _settingsStore.lookupsLNUrl;
77 +
78 @computed
79 ExchangeApiMode get exchangeStatus => _settingsStore.exchangeStatus;
80
@@ -68,6 +88,99 @@ abstract class ConnectionSyncViewModelBase with Store {
88 @computed
89 bool get builtinTor => _settingsStore.currentBuiltinTor;
90
91 + List<AddressSource> get domainLookupSources => AddressResolverService.supportedSources;
92 +
93 + bool lookupValue(AddressSource source) {
94 + switch (source) {
95 + case AddressSource.twitter:
96 + return lookupTwitter;
97 + case AddressSource.mastodon:
98 + return looksUpMastodon;
99 + case AddressSource.yatRecord:
100 + return looksUpYatService;
101 + case AddressSource.unstoppableDomains:
102 + return looksUpUnstoppableDomains;
103 + case AddressSource.openAlias:
104 + return looksUpOpenAlias;
105 + case AddressSource.ens:
106 + return looksUpENS;
107 + case AddressSource.zcashName:
108 + return lookupsZcashNames;
109 + case AddressSource.zcashAddress:
110 + return lookupsZcashAddress;
111 + case AddressSource.wellKnown:
112 + return looksUpWellKnown;
113 + case AddressSource.zanoAlias:
114 + return lookupsZanoAlias;
115 + case AddressSource.bip353:
116 + return lookupsBip353;
117 + case AddressSource.fio:
118 + return lookupsFio;
119 + case AddressSource.lnurlPay:
120 + return lookupsLNUrlPay;
121 + case AddressSource.thorChain:
122 + return lookupsThorChain;
123 + case AddressSource.nostr:
124 + return lookupsNostr;
125 + case AddressSource.contact:
126 + case AddressSource.notParsed:
127 + return false;
128 + }
129 + }
130 +
131 + @action
132 + void setLookupValue(AddressSource source, bool value) {
133 + switch (source) {
134 + case AddressSource.twitter:
135 + _settingsStore.lookupsTwitter = value;
136 + break;
137 + case AddressSource.mastodon:
138 + _settingsStore.lookupsMastodon = value;
139 + break;
140 + case AddressSource.yatRecord:
141 + _settingsStore.lookupsYatService = value;
142 + break;
143 + case AddressSource.unstoppableDomains:
144 + _settingsStore.lookupsUnstoppableDomains = value;
145 + break;
146 + case AddressSource.openAlias:
147 + _settingsStore.lookupsOpenAlias = value;
148 + break;
149 + case AddressSource.ens:
150 + _settingsStore.lookupsENS = value;
151 + break;
152 + case AddressSource.zcashName:
153 + _settingsStore.lookupsZcashNames = value;
154 + break;
155 + case AddressSource.zcashAddress:
156 + _settingsStore.lookupsZcashAddress = value;
157 + break;
158 + case AddressSource.wellKnown:
159 + _settingsStore.lookupsWellKnown = value;
160 + break;
161 + case AddressSource.zanoAlias:
162 + _settingsStore.lookupsZanoAlias = value;
163 + break;
164 + case AddressSource.bip353:
165 + _settingsStore.lookupsBip353 = value;
166 + break;
167 + case AddressSource.fio:
168 + _settingsStore.lookupsFio = value;
169 + break;
170 + case AddressSource.lnurlPay:
171 + _settingsStore.lookupsLNUrl = value;
172 + break;
173 + case AddressSource.thorChain:
174 + _settingsStore.lookupsThorChain = value;
175 + break;
176 + case AddressSource.nostr:
177 + _settingsStore.lookupsNostr = value;
178 + break;
179 + case AddressSource.contact:
180 + case AddressSource.notParsed:
181 + break;
182 + }
183 + }
184
185 @computed
186 bool get hasPowNodes => [WalletType.nano, WalletType.banano].contains(_wallet.type);
@@ -119,32 +232,6 @@ abstract class ConnectionSyncViewModelBase with Store {
232 @computed
233 bool get canUseTronGrid => _wallet.type == WalletType.tron;
234
122 - @action
123 - void setLookupsTwitter(bool value) => _settingsStore.lookupsTwitter = value;
124 -
125 - @action
126 - void setLookupsZanoAlias(bool value) => _settingsStore.lookupsZanoAlias = value;
127 -
128 - @action
129 - void setLookupsMastodon(bool value) => _settingsStore.lookupsMastodon = value;
130 -
131 - @action
132 - void setLookupsENS(bool value) => _settingsStore.lookupsENS = value;
133 -
134 - @action
135 - void setLookupsZcashNames(bool value) => _settingsStore.lookupsZcashNames = value;
136 -
137 - @action
138 - void setLookupsWellKnown(bool value) => _settingsStore.lookupsWellKnown = value;
139 -
140 - @action
141 - void setLookupsYatService(bool value) => _settingsStore.lookupsYatService = value;
142 -
143 - @action
144 - void setLookupsUnstoppableDomains(bool value) => _settingsStore.lookupsUnstoppableDomains = value;
145 -
146 - @action
147 - void setLookupsOpenAlias(bool value) => _settingsStore.lookupsOpenAlias = value;
235
236 @action
237 void setUseMempoolFeeAPI(bool value) => _settingsStore.useMempoolFeeAPI = value;
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+1 -1
@@ -2,6 +2,7 @@ import 'dart:core';
2 import 'dart:developer' as dev;
3
4 import 'package:cake_wallet/bitcoin/bitcoin.dart';
5 +import 'package:cake_wallet/core/address_resolver/yat/yat_store.dart';
6 import 'package:cake_wallet/core/amount_parsing_proxy.dart';
7 import 'package:cake_wallet/core/fiat_conversion_service.dart';
8 import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
@@ -17,7 +18,6 @@ import 'package:cake_wallet/reactions/wallet_utils.dart';
18 import 'package:cake_wallet/solana/solana.dart';
19 import 'package:cake_wallet/store/app_store.dart';
20 import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
20 -import 'package:cake_wallet/store/yat/yat_store.dart';
21 import 'package:cake_wallet/tron/tron.dart';
22 import 'package:cake_wallet/utils/list_item.dart';
23 import 'package:cake_wallet/utils/qr_util.dart';
pubspec_base.yaml
+1
@@ -295,6 +295,7 @@ flutter:
295 - assets/new-ui/crypto_full_icons/
296 - assets/new-ui/hardware_wallets/
297 - assets/new-ui/node_speed_badges/
298 + - assets/new-ui/address_sources/
299
300 fonts:
301 - family: Lato
res/pictures/address_sources/bip353.svg new
+4
@@ -0,0 +1,4 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="#F7931A"/>
3 +<path d="M74.5527 42.2225C75.6531 34.8671 70.0528 30.9131 62.3952 28.2753L64.8792 18.3116L58.8142 16.8001L56.3959 26.5013C54.8015 26.1039 53.1639 25.7291 51.5366 25.3577L53.9723 15.5926L47.9108 14.0811L45.4251 24.0414C44.1053 23.7408 42.8097 23.4438 41.5522 23.1311L41.5591 23.1L33.195 21.0115L31.5816 27.4893C31.5816 27.4893 36.0815 28.5206 35.9865 28.5845C38.4429 29.1977 38.8868 30.8232 38.8125 32.1119L35.983 43.4627C36.1523 43.5059 36.3717 43.5681 36.6135 43.6648C36.4114 43.6147 36.1955 43.5595 35.9727 43.5059L32.0065 59.4068C31.7059 60.153 30.9442 61.2724 29.2271 60.8474C29.2876 60.9355 24.8188 59.7471 24.8188 59.7471L21.8079 66.6896L29.7004 68.657C31.1687 69.025 32.6076 69.4102 34.0241 69.7729L31.5142 79.8507L37.5722 81.3622L40.058 71.3916C41.7129 71.8407 43.3194 72.2553 44.8913 72.6457L42.4142 82.5697L48.4791 84.0811L50.9891 74.0224C61.3311 75.9796 69.1079 75.1901 72.3814 65.8362C75.0191 58.3047 72.2501 53.9602 66.8087 51.1273C70.7714 50.2135 73.7564 47.6068 74.5527 42.2225ZM60.6954 61.6541C58.8211 69.1857 46.1402 65.1141 42.029 64.0932L45.3594 50.742C49.4707 51.7681 62.6543 53.7996 60.6954 61.6541ZM62.5714 42.1136C60.8612 48.9645 50.3067 45.4838 46.883 44.6305L49.9025 32.5213C53.3263 33.3747 64.3523 34.9673 62.5714 42.1136Z" fill="white"/>
4 +</svg>
res/pictures/address_sources/ens.svg new
+14
@@ -0,0 +1,14 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="#0080BC"/>
3 +<g clip-path="url(#clip0_1796_8124)">
4 +<path d="M48.295 13.5958L28.0864 46.8476C27.9279 47.1082 27.5597 47.1373 27.3631 46.9039C25.5841 44.7915 18.956 35.8046 27.1575 27.6136C34.6414 20.1394 44.1738 14.8104 47.7065 12.9685C48.1073 12.7596 48.5295 13.2098 48.295 13.5958Z" fill="white"/>
5 +<path d="M47.1773 85.9954C47.5807 86.2778 48.0775 85.7964 47.8073 85.3851C43.2934 78.5197 28.2888 55.6765 26.216 52.2476C24.1716 48.8653 20.1506 43.2443 19.8152 38.4353C19.7817 37.9553 19.1178 37.8578 18.9508 38.3093C18.6815 39.0373 18.3948 39.9064 18.1275 40.8992C14.7539 53.4315 19.6535 66.7299 30.2942 74.1779L47.1773 85.9954Z" fill="white"/>
6 +<path d="M49.9531 85.4041L70.1617 52.1524C70.3201 51.8918 70.6884 51.8627 70.885 52.0961C72.664 54.2083 79.292 63.1952 71.0906 71.3863C63.6067 78.8605 54.0744 84.1895 50.5415 86.0314C50.1408 86.2403 49.7186 85.7901 49.9531 85.4041Z" fill="white"/>
7 +<path d="M51.0928 13.0001C50.6895 12.7178 50.1926 13.1992 50.4628 13.6103C54.9767 20.4758 69.9815 43.3189 72.054 46.7479C74.0986 50.1302 78.1195 55.7512 78.455 60.5601C78.4885 61.0403 79.1523 61.1378 79.3193 60.6863C79.5886 59.9581 79.8754 59.089 80.1427 58.0963C83.5162 45.5642 78.6167 32.2657 67.9759 24.8176L51.0928 13.0001Z" fill="white"/>
8 +</g>
9 +<defs>
10 +<clipPath id="clip0_1796_8124">
11 +<rect width="65.8537" height="75" fill="white" transform="translate(17.0732 12)"/>
12 +</clipPath>
13 +</defs>
14 +</svg>
res/pictures/address_sources/fio.svg new
+20
@@ -0,0 +1,20 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="white"/>
3 +<g clip-path="url(#clip0_1796_8061)">
4 +<mask id="mask0_1796_8061" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="30" y="7" width="39" height="85">
5 +<path d="M68.6254 7H30.3754V92H68.6254V7Z" fill="white"/>
6 +</mask>
7 +<g mask="url(#mask0_1796_8061)">
8 +<path fill-rule="evenodd" clip-rule="evenodd" d="M50.4602 62.2937V90.2068C50.4602 91.6904 49.5269 92.3988 48.3755 91.7711L32.5551 83.1131C31.2768 82.2526 30.4957 80.8197 30.4602 79.2804V52.0003C30.6935 52.1875 30.937 52.3491 31.1957 52.4911L50.4602 62.2937Z" fill="#765CD6"/>
9 +<path fill-rule="evenodd" clip-rule="evenodd" d="M30.4986 27.0001V28.6668C30.4875 28.0585 30.4875 27.5056 30.4986 27.0001Z" fill="#765CD6"/>
10 +<path fill-rule="evenodd" clip-rule="evenodd" d="M66.3823 8.91749V29.2234C66.3562 30.5088 65.679 31.5001 64.5952 32.1871L55.3363 37.0709L54.6596 37.2712L31.9748 25.8617C31.9435 25.8409 31.9123 25.8252 31.881 25.8096C31.7976 25.768 31.7143 25.742 31.6309 25.716C31.5736 25.7055 31.5163 25.6899 31.4642 25.6847C31.4121 25.6743 31.36 25.6743 31.3079 25.6743C31.1516 25.6795 31.0005 25.716 30.8598 25.7784C30.8129 25.8044 30.766 25.8252 30.7244 25.8565C30.7244 25.8565 30.7244 25.8617 30.7139 25.8617L30.6983 25.8773C31.3548 24.6856 32.6312 23.9986 34.5329 22.9787L64.5952 7.58006C65.5748 7.08048 66.3823 7.65812 66.3823 8.91749Z" fill="#765CD6"/>
11 +<path fill-rule="evenodd" clip-rule="evenodd" d="M65.4602 44.6319V64.8773C65.4602 66.1336 64.6748 66.7197 63.7221 66.2216L31.9956 49.553C31.276 49.0909 30.744 48.3955 30.4602 47.5959C30.4855 47.6375 30.5058 47.679 30.5362 47.7152C30.5565 47.7414 30.5768 47.7673 30.6021 47.7932C30.6072 47.8035 30.6122 47.8089 30.6224 47.8191C30.6426 47.8399 30.6629 47.8658 30.6882 47.8866C30.7136 47.9177 30.744 47.9385 30.7744 47.9644C30.82 47.9957 30.8605 48.0267 30.9112 48.0475C31.2102 48.2084 31.5902 48.1981 31.9956 47.9852L54.0587 36.3988L55.4358 37.0006L63.7221 41.4652C64.7811 42.1557 65.4349 43.3445 65.4602 44.6319Z" fill="#A08AEE"/>
12 +<path fill-rule="evenodd" clip-rule="evenodd" d="M55.4603 37.0071L32.3006 48.4954C31.8751 48.7065 31.4762 48.7168 31.1623 48.5573C31.1091 48.5367 31.0666 48.5057 31.0187 48.4749C30.9868 48.4492 30.9549 48.4286 30.9283 48.3978C30.9017 48.377 30.8804 48.3513 30.8591 48.3308C30.8485 48.3205 30.8432 48.3154 30.8379 48.3051C30.8113 48.2792 30.79 48.2535 30.7687 48.2278C30.7368 48.1918 30.7155 48.1507 30.6889 48.1094C30.5453 47.8623 30.4602 47.5434 30.4602 47.1725V26.8363C30.4602 26.2289 30.6677 25.776 30.9974 25.5341C31.0028 25.5289 31.0081 25.5238 31.0134 25.5238C31.0134 25.5238 31.0187 25.5135 31.024 25.5135C31.0666 25.4826 31.1145 25.462 31.1623 25.4362C31.3059 25.3745 31.4602 25.3385 31.6198 25.3333C31.673 25.3333 31.7262 25.3333 31.7794 25.3436C31.8325 25.3488 31.8911 25.3642 31.9496 25.3745C32.0347 25.4002 32.1198 25.426 32.2049 25.4671C32.2368 25.4825 32.2687 25.498 32.3006 25.5186L55.4603 37.0071Z" fill="#8B73E2"/>
13 +</g>
14 +</g>
15 +<defs>
16 +<clipPath id="clip0_1796_8061">
17 +<rect width="38.5302" height="85" fill="white" transform="translate(30.2349 7)"/>
18 +</clipPath>
19 +</defs>
20 +</svg>
res/pictures/address_sources/icon-lnurl-pay.svg new
+4
@@ -0,0 +1,4 @@
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="24" height="24" rx="4.8" fill="#F7931A"/>
3 +<path d="M5.27657 12.4243L14.9284 4.10719C15.349 3.83785 15.7499 4.10719 15.4932 4.56924L12.4128 10.6274H17.9061C17.9061 10.6274 18.7789 10.6274 17.9061 11.3461L8.4083 19.7145C7.74089 20.2792 7.27883 19.9712 7.74089 19.0984L10.7186 13.1944H5.27657C5.27657 13.1944 4.4038 13.1944 5.27657 12.4243Z" fill="white"/>
4 +</svg>
res/pictures/address_sources/icon-thorchain.svg new
+16
@@ -0,0 +1,16 @@
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="24" height="24" rx="4.8" fill="#0080BC"/>
3 +<rect width="24" height="24" rx="4.8" fill="black"/>
4 +<g clip-path="url(#clip0_11690_63602)">
5 +<path d="M4.5918 20.4602L16.32 15.5214L12.6072 11.7565L4.5918 20.4602ZM8.90023 7.99751L12.613 11.7565L19.2405 3.66016L8.90023 7.99751Z" fill="url(#paint0_linear_11690_63602)"/>
6 +</g>
7 +<defs>
8 +<linearGradient id="paint0_linear_11690_63602" x1="4.59205" y1="12.0603" x2="19.2384" y2="12.0603" gradientUnits="userSpaceOnUse">
9 +<stop stop-color="#00CCFF"/>
10 +<stop offset="1" stop-color="#33FF99"/>
11 +</linearGradient>
12 +<clipPath id="clip0_11690_63602">
13 +<rect width="14.6487" height="16.8" fill="white" transform="translate(4.5918 3.66016)"/>
14 +</clipPath>
15 +</defs>
16 +</svg>
res/pictures/address_sources/icon-zcash-me.svg new
+230
@@ -0,0 +1,230 @@
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<g clip-path="url(#clip0_22246_309363)">
3 +<rect width="24" height="24" rx="4.8" fill="#FBF6EC"/>
4 +<path d="M11.8507 2.73741C11.8795 2.73738 11.9083 2.73734 11.938 2.7373C12.4098 2.73737 12.8684 2.76998 13.3352 2.83526C13.4295 2.84568 13.5207 2.84626 13.6154 2.84117C13.6604 2.84422 13.6604 2.84422 13.7093 2.89305C13.785 2.90773 13.8609 2.91638 13.9375 2.92576C14.0252 2.94161 14.1033 2.96593 14.1871 2.99514C14.2483 3.01578 14.3098 3.03372 14.372 3.05108C14.6859 3.13897 14.9924 3.23977 15.2983 3.35201C15.326 3.36216 15.3537 3.37231 15.3822 3.38276C15.4069 3.3919 15.4315 3.40104 15.4569 3.41046C15.5133 3.43274 15.5133 3.43274 15.5647 3.43016C15.5647 3.44627 15.5647 3.46239 15.5647 3.47899C15.5953 3.48824 15.6259 3.49749 15.6573 3.50703C15.7989 3.55419 15.9284 3.62109 16.0606 3.68956C16.1078 3.71387 16.155 3.7381 16.2023 3.76223C16.2229 3.77289 16.2435 3.78354 16.2647 3.79452C16.3197 3.82208 16.3197 3.82208 16.3746 3.83398C16.4192 3.8452 16.4192 3.8452 16.4711 3.88792C16.5334 3.93664 16.5867 3.96696 16.6573 4.00084C16.7813 4.06104 16.892 4.13296 17.0052 4.21141C17.0564 4.24667 17.1078 4.28176 17.1593 4.3167C17.1834 4.33322 17.2074 4.34974 17.2322 4.36676C17.2938 4.40874 17.2938 4.40874 17.3541 4.41998C17.3679 4.42366 17.3816 4.42734 17.3958 4.43114C17.4039 4.45531 17.4119 4.47948 17.4202 4.50438C17.4631 4.54018 17.5076 4.57399 17.553 4.60661C17.58 4.62636 17.607 4.64613 17.6339 4.66593C17.6541 4.6807 17.6541 4.6807 17.6748 4.69576C18.1489 5.0456 18.6128 5.45658 18.9962 5.90418C19.0231 5.93508 19.0514 5.9647 19.0804 5.99364C19.1822 6.10757 19.2761 6.2266 19.3672 6.34917C19.3797 6.36586 19.3922 6.38256 19.4051 6.39976C19.4684 6.48456 19.5306 6.56992 19.5913 6.65654C19.6025 6.67236 19.6136 6.68819 19.625 6.7045C19.6461 6.73464 19.667 6.76489 19.6877 6.79525C19.712 6.83001 19.7378 6.86362 19.764 6.89696C19.7801 6.89696 19.7962 6.89696 19.8128 6.89696C19.8141 6.91279 19.8154 6.92862 19.8168 6.94493C19.8465 7.05283 19.9066 7.13567 19.9684 7.22807C19.9805 7.2465 19.9926 7.26493 20.005 7.28391C20.05 7.35219 20.0946 7.41979 20.1449 7.48432C20.179 7.53172 20.179 7.53172 20.2034 7.60497C20.2266 7.6511 20.2506 7.69685 20.2751 7.74229C20.2943 7.77827 20.2943 7.77827 20.3139 7.81496C20.3444 7.87615 20.3444 7.87615 20.3987 7.89793C20.4076 7.93848 20.4156 7.9792 20.4231 8.02C20.4395 8.06096 20.4571 8.10142 20.4757 8.14141C20.4919 8.17691 20.4919 8.17691 20.5084 8.21312C20.52 8.23852 20.5317 8.26392 20.5437 8.29009C20.6635 8.55551 20.7648 8.82416 20.8583 9.09967C20.8726 9.14116 20.8726 9.14116 20.8871 9.18349C20.8995 9.22043 20.8995 9.22043 20.9121 9.25811C20.9303 9.3178 20.9303 9.3178 20.9847 9.33836C20.9902 9.38846 20.9955 9.43858 21 9.48877C21.0221 9.65805 21.0746 9.8232 21.1183 9.98796C21.1576 10.1401 21.1897 10.2848 21.1973 10.4421C21.1996 10.4565 21.202 10.471 21.2044 10.4858C21.2205 10.4939 21.2366 10.5019 21.2532 10.5102C21.262 10.5706 21.262 10.5706 21.2662 10.6501C21.2767 10.8185 21.2947 10.9851 21.3143 11.1526C21.5911 13.606 20.8741 16.0308 19.365 17.9741C19.2759 18.0855 19.1818 18.1929 19.0874 18.2999C19.0745 18.3146 19.0616 18.3292 19.0483 18.3443C19.0311 18.3637 19.0311 18.3637 19.0135 18.3835C18.9777 18.425 18.9777 18.425 18.9339 18.4936C18.9178 18.4936 18.9017 18.4936 18.8851 18.4936C18.8792 18.5074 18.8734 18.5212 18.8674 18.5353C18.8295 18.6034 18.7837 18.6501 18.7287 18.7053C18.707 18.7271 18.6852 18.7489 18.6628 18.7714C18.64 18.794 18.6171 18.8167 18.5936 18.84C18.5716 18.8622 18.5496 18.8844 18.527 18.9072C18.3953 19.0385 18.2597 19.1601 18.1138 19.2754C18.0579 19.3213 18.0083 19.3702 17.9573 19.4214C17.9276 19.4452 17.8974 19.4684 17.8667 19.4908C17.8497 19.5034 17.8326 19.5159 17.8151 19.5289C17.7975 19.5417 17.78 19.5546 17.762 19.5679C17.7281 19.5935 17.6942 19.6192 17.6603 19.6449C17.0996 20.0669 16.5057 20.4139 15.8722 20.714C15.8517 20.7237 15.8313 20.7334 15.8102 20.7434C15.7601 20.7641 15.7601 20.7641 15.7112 20.7641C15.7032 20.7803 15.6951 20.7964 15.6868 20.813C15.6461 20.8302 15.6461 20.8302 15.5927 20.8463C15.5062 20.8735 15.4211 20.9027 15.3361 20.9346C14.8177 21.1277 14.2967 21.2853 13.752 21.3837C13.7355 21.3866 13.7191 21.3896 13.7022 21.3926C13.0434 21.51 13.0434 21.51 12.7327 21.4966C12.7247 21.5127 12.7166 21.5288 12.7083 21.5454C12.6685 21.545 12.6685 21.545 12.6278 21.5445C12.4172 21.543 12.21 21.5472 12.0003 21.5698C12.0003 21.5779 12.0003 21.5859 12.0003 21.5942C11.9036 21.5942 11.8069 21.5942 11.7073 21.5942C11.7073 21.5862 11.7073 21.5781 11.7073 21.5698C11.6605 21.5706 11.6605 21.5706 11.6127 21.5713C11.5181 21.5729 11.5181 21.5729 11.4632 21.5454C11.4145 21.5399 11.3656 21.5358 11.3167 21.5321C10.496 21.4666 9.68956 21.3324 8.91224 21.0517C8.82366 21.0202 8.74983 21.0008 8.65556 21.0083C8.65556 20.9841 8.65556 20.9599 8.65556 20.935C8.63643 20.9471 8.61729 20.9592 8.59758 20.9717C8.53349 21.0083 8.53349 21.0083 8.48466 21.0083C8.48114 20.9932 8.47761 20.9781 8.47398 20.9625C8.46192 20.9098 8.46192 20.9098 8.43583 20.8618C8.39222 20.8459 8.39222 20.8459 8.34123 20.8359C8.23286 20.8117 8.1585 20.7826 8.06962 20.7153C7.99392 20.6734 7.91575 20.6367 7.83769 20.5994C7.80639 20.5838 7.80639 20.5838 7.77446 20.568C7.75429 20.5583 7.73411 20.5487 7.71333 20.5387C7.69503 20.5298 7.67673 20.521 7.65787 20.5118C7.59763 20.4931 7.56681 20.4992 7.5081 20.52C7.50589 20.505 7.50368 20.4901 7.50141 20.4747C7.47881 20.408 7.45722 20.3981 7.39547 20.3663C7.37722 20.3566 7.35898 20.3469 7.34018 20.3369C7.31076 20.3218 7.31076 20.3218 7.28074 20.3064C7.24023 20.2847 7.19976 20.263 7.15934 20.2412C7.12964 20.2251 7.12964 20.2251 7.09934 20.2088C6.94517 20.1233 6.80226 20.0202 6.66 19.9165C6.64447 19.9053 6.62895 19.894 6.61296 19.8824C6.58267 19.8603 6.55251 19.8381 6.5225 19.8156C6.4385 19.7524 6.4385 19.7524 6.33622 19.7388C6.32817 19.7146 6.32011 19.6904 6.31181 19.6655C6.26327 19.6241 6.21502 19.5858 6.1638 19.548C6.14228 19.5318 6.14228 19.5318 6.12032 19.5153C6.09164 19.4938 6.06291 19.4723 6.03411 19.4509C5.983 19.4129 5.93229 19.3744 5.88151 19.3359C5.83037 19.2975 5.77918 19.2616 5.72587 19.2261C5.69332 19.1854 5.66077 19.1447 5.62822 19.104C5.60405 19.0879 5.57988 19.0718 5.55497 19.0552C5.55497 19.039 5.55497 19.0229 5.55497 19.0063C5.54087 19.0004 5.52678 18.9944 5.51225 18.9882C5.42934 18.9419 5.37432 18.8729 5.31083 18.8032C5.26017 18.7517 5.26017 18.7517 5.16435 18.7622C5.16309 18.7451 5.16183 18.7281 5.16053 18.7105C5.13263 18.6152 5.07374 18.5595 5.00566 18.4906C4.97867 18.4623 4.95175 18.434 4.92488 18.4056C4.90492 18.3846 4.90492 18.3846 4.88455 18.3631C4.76707 18.2369 4.66155 18.0997 4.55743 17.9623C4.54546 17.9467 4.53348 17.9311 4.52115 17.9149C4.49773 17.8843 4.47452 17.8534 4.45157 17.8224C4.38961 17.7403 4.38961 17.7403 4.30194 17.6905C4.28844 17.6896 4.27494 17.6888 4.26103 17.688C4.26103 17.6638 4.26103 17.6396 4.26103 17.6147C4.24492 17.6067 4.2288 17.5986 4.2122 17.5903C4.22026 17.5742 4.22831 17.5581 4.23661 17.5415C4.21539 17.4962 4.21539 17.4962 4.18197 17.4476C4.16999 17.4293 4.15802 17.411 4.14568 17.392C4.12649 17.3633 4.12649 17.3633 4.10691 17.334C4.0944 17.3148 4.08189 17.2956 4.06901 17.2759C4.0009 17.1714 4.0009 17.1714 3.91923 17.0776C3.89338 17.0344 3.86857 16.9906 3.84446 16.9464C3.83159 16.923 3.81872 16.8997 3.80546 16.8756C3.77792 16.8196 3.76002 16.7723 3.74833 16.7114C3.70805 16.7114 3.66777 16.7114 3.62626 16.7114C3.65068 16.6626 3.65068 16.6626 3.67509 16.6382C3.66474 16.617 3.65438 16.5958 3.64371 16.574C3.6054 16.4955 3.56721 16.4169 3.52906 16.3384C3.51252 16.3043 3.49594 16.2703 3.47933 16.2363C3.45548 16.1875 3.43176 16.1387 3.40806 16.0898C3.39685 16.067 3.39685 16.067 3.38541 16.0436C3.33329 15.9357 3.33329 15.9357 3.33329 15.8813C3.31718 15.8733 3.30107 15.8652 3.28447 15.8569C3.26711 15.8159 3.26711 15.8159 3.2509 15.7623C3.2092 15.6295 3.16165 15.4992 3.11357 15.3686C3.09745 15.3686 3.08134 15.3686 3.06474 15.3686C3.062 15.3407 3.05926 15.3127 3.05644 15.284C3.0426 15.1732 3.0127 15.0715 2.97776 14.9658C2.97205 14.9481 2.96634 14.9303 2.96045 14.912C2.94654 14.8689 2.93242 14.8258 2.91825 14.7827C2.89408 14.7827 2.86992 14.7827 2.84501 14.7827C2.84984 14.7588 2.85466 14.7349 2.85963 14.7103C2.87491 14.6021 2.85531 14.5092 2.8308 14.4037C2.82442 14.3749 2.82442 14.3749 2.81791 14.3455C2.80892 14.3052 2.79978 14.265 2.7905 14.2247C2.77629 14.1629 2.76263 14.1011 2.74903 14.0391C2.7402 13.9999 2.73135 13.9607 2.72247 13.9215C2.71837 13.903 2.71427 13.8844 2.71005 13.8653C2.69964 13.8208 2.68707 13.7767 2.67411 13.7329C2.658 13.7248 2.64189 13.7168 2.62529 13.7085C2.62571 13.6902 2.62614 13.672 2.62657 13.6532C2.6292 13.4482 2.61427 13.2475 2.59396 13.0439C2.46799 11.7571 2.59521 10.4456 3.00255 9.21648C3.01912 9.16416 3.01912 9.16416 3.01591 9.09422C3.03202 9.09422 3.04814 9.09422 3.06474 9.09422C3.06767 9.07294 3.0706 9.05166 3.07362 9.02974C3.08879 8.94964 3.1114 8.88288 3.14237 8.80764C3.15828 8.76853 3.15828 8.76853 3.17451 8.72863C3.18562 8.70174 3.19673 8.67485 3.20817 8.64714C3.28002 8.47208 3.34914 8.29803 3.40654 8.11766C3.42265 8.11766 3.43876 8.11766 3.45536 8.11766C3.46141 8.09802 3.46745 8.07838 3.47367 8.05815C3.57692 7.76392 3.57692 7.76392 3.65068 7.72704C3.64237 7.69909 3.64237 7.69909 3.63389 7.67058C3.63012 7.6381 3.63012 7.6381 3.62626 7.60497C3.66136 7.56072 3.66136 7.56072 3.6995 7.53172C3.70756 7.53978 3.71562 7.54784 3.72392 7.55614C3.73695 7.53219 3.74998 7.50824 3.7634 7.48356C3.87079 7.28835 3.98334 7.09959 4.10995 6.91617C4.15341 6.8529 4.18777 6.79934 4.2122 6.72606C4.22831 6.70995 4.24443 6.69383 4.26103 6.67723C4.36728 6.55833 4.46058 6.43387 4.55323 6.30415C4.61123 6.22653 4.6742 6.15555 4.74082 6.08529C4.77696 6.03826 4.7849 6.00179 4.79814 5.94481C4.81324 5.93776 4.82835 5.93071 4.84391 5.92345C4.90348 5.89835 4.90348 5.89835 4.93242 5.82274C4.96904 5.7495 4.96904 5.7495 5.01186 5.72861C5.08785 5.68989 5.14089 5.63162 5.19944 5.57097C5.21079 5.5594 5.22213 5.54784 5.23382 5.53592C5.2677 5.50139 5.30147 5.46675 5.33525 5.43211C5.35743 5.41015 5.37962 5.38818 5.40248 5.36555C5.45696 5.31318 5.45696 5.31318 5.49394 5.25969C5.53988 5.20035 5.59372 5.16133 5.65263 5.11473C5.69949 5.07463 5.74628 5.03444 5.79301 4.99419C6.17837 4.66612 6.58971 4.35648 7.02449 4.09678C7.03906 4.08627 7.05363 4.07576 7.06865 4.06493C7.06865 4.04881 7.06865 4.0327 7.06865 4.0161C7.09013 4.01344 7.11162 4.01078 7.13376 4.00805C7.21661 3.99139 7.27207 3.96668 7.34521 3.92521C7.36875 3.91207 7.39229 3.89893 7.41655 3.8854C7.44072 3.87163 7.46489 3.85786 7.48979 3.84367C7.53819 3.81634 7.58663 3.78909 7.63513 3.76194C7.65644 3.74983 7.67776 3.73771 7.69972 3.72522C7.75224 3.69872 7.75224 3.69872 7.80107 3.69872C7.80107 3.67455 7.80107 3.65038 7.80107 3.62547C7.8343 3.62396 7.8343 3.62396 7.86821 3.62242C7.98964 3.60603 8.09052 3.54518 8.19732 3.48814C8.25539 3.45932 8.30063 3.4454 8.36259 3.43016C8.39592 3.41796 8.42904 3.40517 8.46196 3.39192C9.21131 3.09538 9.98057 2.89916 10.7796 2.7954C10.8108 2.79105 10.842 2.7867 10.8742 2.78222C11.1992 2.7395 11.5233 2.737 11.8507 2.73741Z" fill="#0D0E0D"/>
5 +<path d="M11.8161 3.81884C13.6628 3.76183 15.5309 4.42348 16.9803 5.55436C17.01 5.57744 17.01 5.57744 17.0404 5.60099C17.1024 5.64983 17.1635 5.69951 17.2244 5.74967C17.2432 5.76508 17.2432 5.76508 17.2624 5.78081C17.5642 6.03 17.8556 6.30028 18.1033 6.60416C18.1133 6.61621 18.1232 6.62825 18.1335 6.64066C19.1087 7.825 19.8016 9.22992 20.0321 10.7546C20.0352 10.7751 20.0384 10.7957 20.0417 10.8168C20.0519 10.8835 20.0619 10.9503 20.0717 11.017C20.0747 11.0372 20.0778 11.0574 20.0809 11.0782C20.1288 11.4153 20.1355 11.7514 20.1358 12.0912C20.1358 12.1144 20.1359 12.1376 20.1359 12.1615C20.1358 12.6351 20.1118 13.1035 19.9832 13.5622C19.8821 13.4786 19.7881 13.3921 19.6964 13.2982C19.5929 13.1935 19.4841 13.1009 19.3693 13.0091C19.2947 12.9477 19.2238 12.8834 19.1531 12.8175C19.0425 12.7147 18.9282 12.6178 18.8106 12.523C18.6773 12.4137 18.5482 12.2995 18.4182 12.1864C18.3456 12.1233 18.2725 12.0607 18.1995 11.9981C18.1855 11.9861 18.1714 11.9741 18.157 11.9618C18.1147 11.9256 18.0724 11.8894 18.0301 11.8532C17.9161 11.7556 17.8021 11.658 17.6883 11.5602C17.6732 11.5472 17.658 11.5342 17.6424 11.5208C17.4567 11.3612 17.2734 11.1996 17.0935 11.0334C17.0239 10.9695 16.9532 10.9073 16.8819 10.8453C16.807 10.7799 16.7329 10.7136 16.6593 10.6468C16.6222 10.6132 16.5852 10.5796 16.5478 10.5464C16.2013 10.2381 16.0389 9.92915 15.8634 9.50381C15.646 8.98227 15.3126 8.58174 14.838 8.27042C14.8162 8.25599 14.7944 8.24155 14.7719 8.22668C14.5238 8.06443 14.2671 7.90872 14.0018 7.77604C13.9991 7.59158 14.0024 7.4092 14.014 7.22519C14.0922 5.97811 14.0922 5.97811 13.7058 4.81431C13.5483 4.7219 13.3712 4.76705 13.2022 4.80973C12.5414 5.01676 12.065 5.55828 11.5909 6.03501C11.5522 6.07378 11.5136 6.11255 11.4749 6.15131C11.3813 6.24523 11.2877 6.33922 11.1942 6.43326C11.1041 6.34468 11.0247 6.25302 10.9485 6.1525C10.8122 5.97606 10.6647 5.81297 10.5106 5.65201C10.4902 5.63039 10.4699 5.60877 10.449 5.5865C10.0893 5.21348 9.60787 4.86385 9.07319 4.83567C8.95869 4.83788 8.89476 4.86087 8.81427 4.94267C8.43386 5.45706 8.42657 6.38208 8.41096 6.99479C8.38758 7.00605 8.3642 7.01732 8.3401 7.02893C7.53608 7.42083 6.81336 7.95913 6.25995 8.66972C6.21098 8.73162 6.1589 8.78923 6.10536 8.8472C5.98922 8.97482 5.8856 9.11073 5.78157 9.24813C5.72857 9.31756 5.6743 9.38598 5.62013 9.4545C5.52132 9.57978 5.4239 9.70604 5.32716 9.83292C4.61431 10.7672 4.61431 10.7672 4.27095 11.1322C4.23249 11.1735 4.19467 11.2154 4.15691 11.2573C4.14366 11.272 4.13041 11.2866 4.11676 11.3017C4.09112 11.33 4.06557 11.3584 4.04012 11.3869C3.984 11.4485 3.95036 11.4846 3.86995 11.5114C3.86096 11.2803 3.88731 11.0561 3.91877 10.8278C3.92166 10.8058 3.92455 10.7839 3.92752 10.7612C4.13319 9.22291 4.81943 7.76438 5.84748 6.60416C5.86728 6.58166 5.88708 6.55916 5.90747 6.53597C6.19801 6.21241 6.50244 5.89208 6.84846 5.6276C6.87493 5.60633 6.90134 5.585 6.92771 5.56361C7.12918 5.40101 7.33791 5.2536 7.55647 5.1149C7.57057 5.10592 7.58468 5.09693 7.59921 5.08768C8.60433 4.45274 9.7281 4.06563 10.9012 3.8942C10.9197 3.89143 10.9382 3.88867 10.9572 3.88582C11.2429 3.84375 11.5279 3.82896 11.8161 3.81884Z" fill="#16A34A"/>
6 +<path d="M9.4899 6.40039C9.74821 6.42163 9.97989 6.60973 10.1686 6.77423C10.1833 6.78537 10.1979 6.79651 10.213 6.80799C10.4807 7.0155 10.6463 7.32422 10.779 7.62872C10.7957 7.66531 10.7957 7.66531 10.8127 7.70263C10.9165 7.97294 10.8853 8.27193 10.8766 8.55646C10.9996 8.5175 11.1174 8.47739 11.2322 8.4176C11.2528 8.40693 11.2735 8.39627 11.2948 8.38527C11.3099 8.37731 11.325 8.36935 11.3405 8.36115C11.4617 8.59009 11.4611 8.75891 11.3869 9.00507C11.3263 9.18163 11.2354 9.31141 11.116 9.45491C11.0691 9.51032 11.0691 9.51032 11.0231 9.58185C11.0299 9.5951 11.0367 9.60835 11.0436 9.622C11.0569 9.6482 11.0569 9.6482 11.0704 9.67493C11.0792 9.6922 11.088 9.70948 11.097 9.72728C11.2087 9.96191 11.1787 10.3274 11.0979 10.5691C10.9969 10.8073 10.8538 11.0225 10.7083 11.2354C10.5347 11.4901 10.4001 11.7365 10.3639 12.0477C10.7389 11.828 11.0807 11.5469 11.3893 11.242C11.4229 11.2094 11.4229 11.2094 11.457 11.1761C11.657 10.9705 11.849 10.6836 11.8546 10.3871C11.8521 10.2691 11.8484 10.1512 11.8427 10.0333C11.8384 9.84975 11.8577 9.7379 11.9629 9.58642C12.0141 9.51203 12.0394 9.43995 12.0684 9.35449C12.0781 9.32639 12.0878 9.29828 12.0978 9.26933C12.2372 8.81458 12.1855 8.23775 11.9664 7.81755C11.9509 7.77521 11.9509 7.77521 11.9753 7.70197C12.1259 7.65174 12.2447 7.67535 12.3837 7.74288C12.6124 7.87632 12.7421 8.06378 12.8145 8.31472C12.8427 8.44553 12.8426 8.57568 12.842 8.70905C12.8419 8.75348 12.8419 8.75348 12.8419 8.79881C12.8373 9.08234 12.7816 9.3329 12.7077 9.60626C12.9494 9.61835 12.9494 9.61835 13.196 9.63068C13.2159 9.54829 13.2357 9.46589 13.2555 9.38348C13.2611 9.36026 13.2667 9.33703 13.2725 9.3131C13.3218 9.10751 13.3464 8.92052 13.3455 8.70905C13.3456 8.68613 13.3457 8.66322 13.3458 8.63962C13.3456 8.48843 13.336 8.34035 13.318 8.19025C13.4978 8.23171 13.6391 8.28671 13.7575 8.43439C13.8271 8.56532 13.8433 8.67178 13.8414 8.81891C13.8413 8.84888 13.8413 8.84888 13.8411 8.87946C13.8363 9.08497 13.7953 9.27995 13.7463 9.47888C13.7241 9.56983 13.7043 9.6613 13.6843 9.75275C13.8972 9.84471 13.8972 9.84471 14.1237 9.8504C14.246 9.76982 14.2714 9.66628 14.3049 9.53092C14.3377 9.36544 14.3498 9.2022 14.3572 9.03406C14.3587 9.00028 14.3587 9.00028 14.3604 8.96582C14.3629 8.91075 14.3654 8.85568 14.3678 8.8006C14.6793 8.85352 14.9249 9.0726 15.1073 9.3214C15.2524 9.54768 15.3523 9.78647 15.436 10.0411C15.4432 10.0632 15.4505 10.0852 15.4579 10.108C15.4864 10.1959 15.5136 10.2832 15.5365 10.3727C15.5964 10.5888 15.7594 10.6975 15.9303 10.827C15.9711 10.8594 16.0118 10.8919 16.0524 10.9246C16.0718 10.94 16.0913 10.9554 16.1113 10.9713C16.1996 11.0426 16.286 11.1162 16.3725 11.1898C16.4571 11.2618 16.5427 11.3324 16.6292 11.4022C16.744 11.4956 16.853 11.5942 16.9618 11.6944C17.0275 11.7533 17.0946 11.8092 17.1633 11.8646C17.2557 11.9391 17.3445 12.0161 17.4318 12.0965C17.5393 12.1953 17.65 12.2887 17.7636 12.3804C17.8583 12.4576 17.9513 12.5367 18.0443 12.6161C18.1112 12.6731 18.1786 12.7284 18.2497 12.7801C18.211 12.8762 18.1443 12.9398 18.0712 13.0105C18.0596 13.0218 18.048 13.0332 18.0361 13.0449C18.0016 13.0788 17.9669 13.1125 17.9323 13.1463C17.7379 13.3366 17.5411 13.542 17.499 13.8192C17.468 13.9773 17.3456 14.0459 17.222 14.1355C17.2056 14.1475 17.1893 14.1594 17.1724 14.1717C17.156 14.1833 17.1395 14.195 17.1225 14.207C17.0749 14.2474 17.0509 14.2867 17.0183 14.3395C16.9839 14.3834 16.9839 14.3834 16.9313 14.4158C16.843 14.4219 16.7589 14.4203 16.6707 14.4163C16.5446 14.4119 16.4624 14.4188 16.3606 14.5005C16.3476 14.5129 16.3345 14.5252 16.321 14.5379C16.3064 14.5497 16.2918 14.5616 16.2767 14.5738C16.0583 14.7658 15.9943 15.0177 15.9548 15.2947C15.3443 15.3174 14.7346 15.3222 14.1237 15.3192C14.0811 15.0592 14.0665 14.8056 14.0672 14.5424C14.0673 14.4811 14.0667 14.4198 14.0661 14.3586C14.065 14.0968 14.0687 13.8452 14.2458 13.6346C14.4963 13.3987 14.8362 13.3357 15.1613 13.2668C15.6077 13.173 15.6077 13.173 16.028 12.9998C16.0441 12.9998 16.0602 12.9998 16.0768 12.9998C16.0666 12.8996 16.0666 12.8996 16.0234 12.8106C15.8239 12.673 15.5558 12.7348 15.3307 12.7633C15.0951 12.792 14.8596 12.8158 14.6232 12.8366C14.0231 12.8642 14.0231 12.8642 13.5042 13.1265C13.3796 13.3421 13.3389 13.5455 13.3375 13.7931C13.3373 13.813 13.3372 13.8329 13.337 13.8534C13.3366 13.9178 13.3364 13.9822 13.3364 14.0466C13.3363 14.0684 13.3362 14.0903 13.3362 14.1128C13.3359 14.4468 13.3553 14.7688 13.4088 15.099C13.4168 15.1565 13.4186 15.2123 13.4157 15.2703C13.0918 15.2909 12.7129 15.2302 12.4559 15.0201C12.1807 14.7565 12.1261 14.376 12.1159 14.0087C12.1076 13.4407 12.1386 12.9028 12.52 12.449C12.7286 12.2368 12.9664 12.1394 13.2448 12.0477C13.2856 12.0317 13.3264 12.0156 13.3669 11.9988C13.2612 11.9231 13.1567 11.8707 13.0342 11.8264C13.0171 11.8202 12.9999 11.8139 12.9823 11.8075C12.5907 11.6658 12.5907 11.6658 12.1852 11.7012C11.773 11.9105 11.488 12.3348 11.3374 12.7603C11.1761 13.3212 11.2587 13.9596 11.3917 14.5185C11.4087 14.59 11.4244 14.6611 11.4382 14.7332C11.0966 14.6449 10.8283 14.416 10.5837 14.1717C10.5674 14.1557 10.5511 14.1397 10.5344 14.1232C10.1774 13.7462 10.0567 13.248 10.0622 12.7439C10.069 12.5321 10.0987 12.3243 10.129 12.1148C10.2376 11.3606 10.3287 10.5579 10.193 9.80157C10.1892 9.7802 10.1854 9.75883 10.1815 9.73681C10.1686 9.66761 10.1544 9.5988 10.1396 9.52997C10.1354 9.50883 10.1313 9.4877 10.1269 9.46592C10.0893 9.30459 10.0164 9.20914 9.90008 9.09357C9.42654 8.59894 9.26187 7.89056 9.2581 7.2244C9.25782 7.19 9.25733 7.15562 9.2566 7.12123C9.25171 6.88768 9.26919 6.67511 9.36297 6.45685C9.41179 6.40802 9.41179 6.40802 9.4899 6.40039Z" fill="#F7ECDB"/>
7 +<path d="M19.422 15.4414C19.4464 15.4902 19.4464 15.4902 19.4333 15.5332C19.4256 15.5505 19.418 15.5677 19.4101 15.5855C19.4015 15.605 19.3929 15.6245 19.3841 15.6446C19.3696 15.6762 19.3696 15.6762 19.3548 15.7084C19.3449 15.7303 19.3349 15.7521 19.3247 15.7746C19.1446 16.1615 18.919 16.5151 18.6651 16.8574C18.6493 16.8788 18.6493 16.8788 18.6332 16.9007C18.1928 17.4941 17.6785 18.031 17.1026 18.4932C17.0796 18.5117 17.0566 18.5303 17.0329 18.5494C16.2173 19.1931 15.2762 19.684 14.2737 19.958C14.2477 19.9651 14.2217 19.9722 14.195 19.9795C14.0413 20.0197 13.892 20.0455 13.7335 20.0557C13.465 19.6662 13.2758 19.2476 13.1211 18.8017C13.1023 18.7476 13.0829 18.6938 13.0632 18.64C12.9015 18.1699 12.8023 17.6331 12.8017 17.1364C12.8016 17.1147 12.8015 17.0931 12.8014 17.0708C12.8013 17.0253 12.8012 16.9797 12.8012 16.9342C12.8012 16.8657 12.8007 16.7971 12.8003 16.7285C12.7984 16.1154 12.7984 16.1154 12.879 15.9541C12.9029 15.9558 12.9268 15.9575 12.9514 15.9593C13.2397 15.9787 13.527 15.9833 13.8159 15.9831C13.8399 15.9831 13.864 15.9831 13.8888 15.9831C14.2522 15.9831 14.6142 15.9725 14.9771 15.9541C15.0211 15.9519 15.0651 15.9498 15.1091 15.9477C15.3368 15.9366 15.3368 15.9366 15.5644 15.9238C15.6443 15.9192 15.7241 15.9158 15.8041 15.9129C15.8257 15.9113 15.8472 15.9096 15.8694 15.9079C15.9299 15.9065 15.9719 15.9077 16.0284 15.9297C16.0903 15.9961 16.1313 16.0703 16.1749 16.1494C16.4107 16.4586 16.7353 16.6765 17.1187 16.7521C17.4283 16.7934 17.7191 16.7369 17.9815 16.5645C18.0681 16.4776 18.1238 16.3811 18.1769 16.2715C18.1937 16.2723 18.2105 16.2732 18.2279 16.2741C18.5588 16.2836 18.7524 16.1617 18.9925 15.9418C19.1519 15.7882 19.2936 15.6216 19.422 15.4414Z" fill="#16A34A"/>
8 +<path d="M8.29041 8.59512C8.85124 9.00579 9.07425 9.96343 9.1801 10.6027C9.20993 10.7986 9.22782 10.9953 9.24075 11.193C9.2428 11.2228 9.24485 11.2525 9.24696 11.2832C9.26213 11.5132 9.2687 11.7427 9.27224 11.9732C9.28358 12.706 9.35232 13.383 9.6802 14.0495C9.68769 14.0649 9.69518 14.0803 9.70289 14.0962C9.92781 14.5537 10.2487 14.9395 10.6065 15.2996C10.6224 15.3157 10.6224 15.3157 10.6386 15.332C10.6831 15.3716 10.6831 15.3716 10.7487 15.4065C10.8458 15.4681 10.9326 15.5234 10.9638 15.64C10.9835 15.7937 10.9742 15.9476 10.9665 16.1018C10.9652 16.149 10.9639 16.1962 10.9628 16.2434C10.9599 16.3586 10.9553 16.4735 10.9497 16.5885C10.7958 16.531 10.6535 16.4679 10.5149 16.3795C10.49 16.3637 10.49 16.3637 10.4645 16.3477C10.2961 16.2386 10.1466 16.112 9.99759 15.9782C9.97631 15.9596 9.95504 15.941 9.93312 15.9218C9.68875 15.7063 9.46594 15.478 9.26516 15.2213C9.23996 15.1901 9.21475 15.1588 9.18954 15.1276C8.99819 14.8875 8.843 14.6401 8.70364 14.3669C8.695 14.3504 8.68636 14.3339 8.67745 14.317C8.3847 13.7566 8.26507 13.1543 8.17024 12.5356C8.04814 11.7401 7.84002 11.0609 7.3672 10.4007C7.26867 10.2621 7.17688 10.1214 7.09231 9.97385C7.08158 9.9558 7.07085 9.93776 7.0598 9.91916C6.99186 9.79705 6.99186 9.79705 7.00314 9.71607C7.06345 9.62468 7.13035 9.54045 7.20065 9.45658C7.21602 9.43815 7.23139 9.41972 7.24723 9.40073C7.9968 8.51163 7.9968 8.51163 8.29041 8.59512Z" fill="#F8EDDB"/>
9 +<path d="M6.38477 10.9495C6.60009 11.0283 6.76228 11.1539 6.92187 11.3157C6.93714 11.3307 6.9524 11.3458 6.96813 11.3612C7.39361 11.8098 7.49269 12.4789 7.59987 13.0623C7.77101 13.9893 8.06195 14.8035 8.75407 15.4682C8.79829 15.5116 8.83819 15.5561 8.87805 15.6035C9.06442 15.8204 9.27309 16.0258 9.49165 16.2101C9.53935 16.2519 9.58307 16.2965 9.62726 16.342C9.72158 16.4376 9.82284 16.5216 9.92767 16.6055C9.97324 16.6423 10.0182 16.68 10.0623 16.7186C10.2584 16.8892 10.4704 17.0305 10.6876 17.1726C10.7076 17.1859 10.7277 17.1992 10.7484 17.2128C10.7818 17.2343 10.8158 17.255 10.8505 17.2742C10.9266 17.3174 10.9484 17.3402 10.9752 17.4252C10.978 17.4544 10.9808 17.4837 10.9838 17.5139C10.987 17.543 10.9902 17.5721 10.9935 17.6022C10.9985 17.6799 11.0002 17.7565 10.999 17.8343C10.5076 17.9669 9.89494 17.5783 9.48316 17.3444C9.2072 17.1828 8.94886 16.9899 8.7041 16.7845C8.69176 16.7743 8.67943 16.7641 8.66672 16.7537C8.32994 16.4758 8.01834 16.1774 7.75849 15.8254C7.7278 15.7839 7.6966 15.7427 7.66536 15.7016C7.2303 15.1202 6.95028 14.4284 6.68034 13.759C6.41633 13.1048 6.099 12.466 5.60552 11.9504C5.56384 11.9063 5.56384 11.9063 5.50586 11.8284C5.51563 11.5851 5.74366 11.3646 5.90106 11.1921C5.91294 11.1791 5.92481 11.166 5.93705 11.1525C6.06352 11.0179 6.19008 10.9074 6.38477 10.9495Z" fill="#F7EDDB"/>
10 +<path d="M5.27583 13.14C5.76879 13.3968 5.95515 13.8621 6.1746 14.344C6.43437 14.9144 6.68808 15.4702 7.09277 15.954C7.10308 15.9665 7.11339 15.979 7.12402 15.9919C7.26704 16.1652 7.41612 16.3305 7.57495 16.4896C7.58963 16.5043 7.60432 16.519 7.61945 16.5341C8.07938 16.9884 8.60482 17.3944 9.16797 17.7118C9.19103 17.7248 9.21409 17.7378 9.23785 17.7512C9.48727 17.8916 9.73727 18.0308 9.99023 18.1647C10.2581 18.3065 10.5218 18.452 10.7793 18.6121C10.8027 18.6266 10.8262 18.6411 10.8504 18.656C11.0303 18.7697 11.2239 18.8932 11.3652 19.0546C11.3652 19.0707 11.3652 19.0868 11.3652 19.1034C10.0368 19.1759 8.56362 18.48 7.50781 17.7118C7.48332 17.694 7.48332 17.694 7.45834 17.6759C6.85824 17.2389 6.32084 16.7419 5.87207 16.1493C5.86129 16.1353 5.8505 16.1213 5.8394 16.1068C5.28465 15.3848 4.85019 14.5516 4.60224 13.675C4.56389 13.5405 4.51604 13.4157 4.45319 13.2904C4.43164 13.244 4.43164 13.244 4.43164 13.1952C4.66666 13.0077 5.00954 13.025 5.27583 13.14Z" fill="#F7ECDB"/>
11 +<path d="M9.4899 6.40039C9.74821 6.42163 9.97989 6.60973 10.1686 6.77423C10.1833 6.78537 10.1979 6.79651 10.213 6.80799C10.4807 7.0155 10.6463 7.32422 10.779 7.62872C10.7957 7.66531 10.7957 7.66531 10.8127 7.70263C10.9165 7.97294 10.8853 8.27193 10.8766 8.55646C10.9996 8.5175 11.1174 8.47739 11.2322 8.4176C11.2528 8.40693 11.2735 8.39627 11.2948 8.38527C11.3099 8.37731 11.325 8.36935 11.3405 8.36115C11.4617 8.59009 11.4611 8.75891 11.387 9.00507C11.3263 9.18163 11.2354 9.31141 11.116 9.45491C11.0691 9.51032 11.0691 9.51032 11.0231 9.58185C11.0299 9.5951 11.0367 9.60835 11.0436 9.622C11.0569 9.6482 11.0569 9.6482 11.0704 9.67493C11.0792 9.6922 11.088 9.70948 11.097 9.72728C11.2087 9.96191 11.1787 10.3274 11.0979 10.5691C10.9969 10.8073 10.8538 11.0225 10.7083 11.2354C10.5229 11.5074 10.4004 11.7703 10.3395 12.0965C10.3315 12.0965 10.3234 12.0965 10.3151 12.0965C10.303 12.0361 10.303 12.0361 10.2907 11.9744C10.2665 11.9744 10.2424 11.9744 10.2175 11.9744C10.2094 12.0147 10.2013 12.055 10.193 12.0965C10.1769 12.0965 10.1608 12.0965 10.1442 12.0965C10.1487 12.0608 10.1487 12.0608 10.1532 12.0244C10.2403 11.2991 10.323 10.5259 10.193 9.80157C10.1892 9.7802 10.1854 9.75883 10.1815 9.73681C10.1686 9.66761 10.1544 9.5988 10.1396 9.52997C10.1354 9.50883 10.1313 9.4877 10.1269 9.46592C10.0893 9.30459 10.0164 9.20914 9.90008 9.09357C9.42654 8.59894 9.26187 7.89056 9.2581 7.2244C9.25782 7.19 9.25733 7.15562 9.2566 7.12123C9.25171 6.88768 9.26919 6.67511 9.36297 6.45685C9.41179 6.40802 9.41179 6.40802 9.4899 6.40039Z" fill="#F8EDDB"/>
12 +<path d="M14.8136 10.4938C14.9017 10.5768 14.9542 10.6743 15.0026 10.7842C15.1839 11.1863 15.5756 11.5315 15.9077 11.812C15.9681 11.8637 16.0263 11.9176 16.0848 11.9715C16.1727 12.0518 16.262 12.1289 16.3549 12.2035C16.462 12.29 16.5648 12.3803 16.6661 12.4735C16.68 12.4862 16.6939 12.499 16.7082 12.5121C16.8065 12.6033 16.8065 12.6033 16.834 12.6582C16.7212 12.7146 16.588 12.7047 16.4696 12.668C16.1919 12.5632 15.9321 12.4118 15.6734 12.2678C15.5317 12.1891 15.3876 12.1152 15.2433 12.0415C15.1269 11.9807 15.0198 11.9129 14.9124 11.8375C14.7183 11.7021 14.5863 11.642 14.3438 11.6816C14.3153 11.6855 14.2869 11.6895 14.2576 11.6935C14.1996 11.7018 14.1416 11.7103 14.0837 11.7192C13.793 11.7592 13.572 11.7131 13.329 11.5443C12.9318 11.2368 12.5983 10.7951 12.5127 10.29C13.0973 10.0309 14.2826 10.0978 14.8136 10.4938Z" fill="#0C0D0D"/>
13 +<path d="M21.2285 10.6562C21.2366 10.6562 21.2446 10.6562 21.2529 10.6562C21.3679 11.4505 21.4934 12.3542 21.3018 13.1465C21.2937 13.1465 21.2856 13.1465 21.2773 13.1465C21.2773 12.5342 21.2773 11.9219 21.2773 11.291C21.2693 11.291 21.2612 11.291 21.2529 11.291C21.2481 11.2482 21.2481 11.2482 21.2431 11.2045C21.2388 11.1667 21.2344 11.1289 21.23 11.0911C21.2269 11.063 21.2269 11.063 21.2236 11.0342C21.2127 10.9398 21.2004 10.8469 21.1797 10.7539C21.1958 10.7539 21.2119 10.7539 21.2285 10.7539C21.2285 10.7217 21.2285 10.6895 21.2285 10.6562Z" fill="#181918"/>
14 +<path d="M15.9307 15.0996C15.9387 15.0996 15.9468 15.0996 15.9551 15.0996C15.9551 15.1641 15.9551 15.2285 15.9551 15.2949C15.3446 15.3176 14.7349 15.3223 14.124 15.3193C14.124 15.2629 14.124 15.2065 14.124 15.1484C14.1401 15.1807 14.1562 15.2129 14.1729 15.2461C14.7449 15.2542 15.3169 15.2622 15.9062 15.2705C15.9143 15.2141 15.9224 15.1577 15.9307 15.0996Z" fill="#FCFBF7"/>
15 +<path d="M15.8574 11.8037C15.9473 11.8419 16.0079 11.8974 16.0787 11.9639C16.1683 12.0473 16.2593 12.1269 16.3549 12.2035C16.462 12.2901 16.5648 12.3804 16.6661 12.4736C16.68 12.4863 16.6939 12.499 16.7082 12.5121C16.8065 12.6033 16.8065 12.6033 16.834 12.6582C16.721 12.7147 16.567 12.6991 16.45 12.6608C16.3977 12.6373 16.347 12.6127 16.2969 12.585C16.3049 12.5688 16.313 12.5527 16.3213 12.5361C16.3445 12.5477 16.3676 12.5593 16.3915 12.5712C16.5031 12.6198 16.6159 12.6268 16.7363 12.6338C16.6446 12.5104 16.5364 12.4211 16.4167 12.3253C16.328 12.2532 16.2436 12.1771 16.1593 12.1001C16.0891 12.0366 16.0173 11.975 15.9452 11.9138C15.9062 11.877 15.9062 11.877 15.8574 11.8037Z" fill="#070606"/>
16 +<path d="M12.9277 21.4474C12.9277 21.4554 12.9277 21.4635 12.9277 21.4718C12.8869 21.4763 12.8869 21.4763 12.8453 21.4809C12.7561 21.4876 12.7561 21.4876 12.708 21.545C12.6431 21.548 12.5805 21.5483 12.5157 21.5465C12.3424 21.5439 12.1726 21.5508 12 21.5694C12 21.5775 12 21.5855 12 21.5938C11.9033 21.5938 11.8066 21.5938 11.707 21.5938C11.707 21.5858 11.707 21.5777 11.707 21.5694C11.6602 21.5709 11.6602 21.5709 11.6124 21.5725C11.5592 21.5742 11.5592 21.5742 11.5117 21.5694C11.4956 21.5533 11.4795 21.5372 11.4629 21.5206C11.4842 21.52 11.5055 21.5194 11.5275 21.5188C11.6063 21.5164 11.6852 21.514 11.764 21.5115C11.7982 21.5105 11.8324 21.5094 11.8666 21.5085C11.9156 21.5071 11.9646 21.5055 12.0136 21.5039C12.0432 21.503 12.0727 21.5021 12.1031 21.5012C12.1686 21.5038 12.1686 21.5038 12.1953 21.4718C12.2631 21.4658 12.3303 21.4614 12.3983 21.458C12.418 21.457 12.4378 21.456 12.4582 21.4549C12.6148 21.4472 12.771 21.4446 12.9277 21.4474Z" fill="#282927"/>
17 +<path d="M2.57667 11.8281C2.59278 11.8362 2.60889 11.8442 2.62549 11.8525C2.62549 12.1667 2.62549 12.481 2.62549 12.8047C2.61744 12.8047 2.60938 12.8047 2.60108 12.8047C2.55331 12.5949 2.54555 12.3936 2.54767 12.1791C2.54769 12.1479 2.54771 12.1167 2.54772 12.0846C2.54882 11.856 2.54882 11.856 2.57667 11.8281Z" fill="#1A1B19"/>
18 +<path d="M21.2285 10.6562C21.2366 10.6562 21.2446 10.6562 21.2529 10.6562C21.2647 10.7442 21.2764 10.8322 21.288 10.9202C21.2913 10.9448 21.2946 10.9693 21.298 10.9946C21.355 11.4269 21.355 11.4269 21.3521 11.6252C21.3519 11.6454 21.3517 11.6657 21.3514 11.6865C21.351 11.7083 21.351 11.7083 21.3506 11.7305C21.3143 11.7056 21.3143 11.7056 21.2773 11.6572C21.272 11.5925 21.2704 11.5339 21.2728 11.4695C21.2733 11.4438 21.2733 11.4438 21.2738 11.4176C21.2746 11.3754 21.2759 11.3332 21.2773 11.291C21.2693 11.291 21.2612 11.291 21.2529 11.291C21.2481 11.2482 21.2481 11.2482 21.2431 11.2045C21.2388 11.1667 21.2344 11.1289 21.23 11.0911C21.2269 11.063 21.2269 11.063 21.2236 11.0342C21.2127 10.9398 21.2004 10.8469 21.1797 10.7539C21.1958 10.7539 21.2119 10.7539 21.2285 10.7539C21.2285 10.7217 21.2285 10.6895 21.2285 10.6562Z" fill="#111211"/>
19 +<path d="M7.8252 15.9297C7.91602 16.0038 7.99939 16.0769 8.07544 16.1662C8.24793 16.3679 8.43562 16.5468 8.64526 16.7092C8.70643 16.7618 8.75343 16.8175 8.80176 16.8818C8.55863 16.7702 8.36847 16.5506 8.18225 16.3646C8.16106 16.3434 8.13986 16.3222 8.11802 16.3004C7.8252 16.005 7.8252 16.005 7.8252 15.9297Z" fill="#080707"/>
20 +<path d="M14.1241 15.0264C14.1402 15.0344 14.1563 15.0425 14.1729 15.0508C14.181 15.1072 14.189 15.1636 14.1973 15.2217C14.6646 15.2297 15.1319 15.2378 15.6134 15.2461C15.6134 15.2542 15.6134 15.2622 15.6134 15.2705C15.4268 15.2722 15.2403 15.2735 15.0537 15.2743C14.9671 15.2747 14.8805 15.2752 14.7939 15.276C14.6943 15.277 14.5947 15.2773 14.4952 15.2777C14.464 15.278 14.4329 15.2784 14.4008 15.2788C14.372 15.2788 14.3432 15.2788 14.3135 15.2788C14.2753 15.2791 14.2753 15.2791 14.2364 15.2793C14.1729 15.2705 14.1729 15.2705 14.127 15.239C14.0813 15.1692 14.1079 15.1043 14.1241 15.0264Z" fill="#ECE0CD"/>
21 +<path d="M13.3184 8.19043C13.3989 8.2146 13.4795 8.23877 13.5625 8.26367C13.486 8.28917 13.4651 8.28648 13.3916 8.26367C13.3934 8.27761 13.3951 8.29156 13.3969 8.30592C13.4305 8.59949 13.4161 8.87577 13.3672 9.16699C13.3591 9.16699 13.3511 9.16699 13.3428 9.16699C13.3429 9.14608 13.343 9.12517 13.3432 9.10363C13.3436 9.02491 13.3439 8.9462 13.3441 8.86748C13.3442 8.83362 13.3444 8.79977 13.3446 8.76591C13.3458 8.57249 13.3421 8.38264 13.3184 8.19043Z" fill="#FAF8F3"/>
22 +<path d="M8.58203 15.2705C8.69242 15.3613 8.79624 15.457 8.89789 15.5574C8.91001 15.5692 8.92214 15.581 8.93463 15.5932C8.96456 15.623 8.99315 15.6542 9.02148 15.6855C9.02148 15.7017 9.02148 15.7178 9.02148 15.7344C9.03508 15.7399 9.04868 15.7455 9.06268 15.7512C9.13458 15.792 9.18461 15.8455 9.24121 15.9053C9.23315 15.9294 9.2251 15.9536 9.2168 15.9785C9.15108 15.9135 9.08551 15.8484 9.01996 15.7832C9.0016 15.765 8.98323 15.7469 8.96431 15.7282C8.64659 15.4118 8.64659 15.4118 8.58203 15.2705Z" fill="#131210"/>
23 +<path d="M9.0459 14.8799C9.14318 14.9711 9.22428 15.071 9.30682 15.1756C9.45915 15.3658 9.61942 15.5497 9.77832 15.7344C9.47326 15.599 9.22992 15.1997 9.0459 14.9287C9.0459 14.9126 9.0459 14.8965 9.0459 14.8799Z" fill="#F4F3EF"/>
24 +<path d="M16.0771 3.72266C16.1681 3.75227 16.2571 3.78438 16.3457 3.82031C16.3699 3.82837 16.394 3.83643 16.4189 3.84473C16.4361 3.85883 16.4532 3.87292 16.4708 3.88745C16.5615 3.95839 16.6593 4.01054 16.7607 4.06445C16.7366 4.08862 16.7124 4.11279 16.6875 4.1377C16.6794 4.10547 16.6714 4.07324 16.6631 4.04004C16.6322 4.03697 16.6322 4.03697 16.6007 4.03384C16.5028 4.01263 16.4272 3.97086 16.3427 3.91949C16.2121 3.84139 16.2121 3.84139 16.0771 3.77148C16.0771 3.75537 16.0771 3.73926 16.0771 3.72266Z" fill="#1B1C1B"/>
25 +<path d="M10.1448 12.3164C10.1528 12.3164 10.1609 12.3164 10.1692 12.3164C10.1615 12.4466 10.1533 12.5768 10.1448 12.707C10.1421 12.7486 10.1421 12.7486 10.1394 12.791C10.1377 12.8147 10.1359 12.8383 10.1341 12.8627C10.1326 12.8835 10.1311 12.9043 10.1296 12.9258C10.1203 12.9756 10.1203 12.9756 10.0715 13.0244C10.0709 12.9512 10.0704 12.8779 10.07 12.8047C10.0698 12.7842 10.0696 12.7637 10.0694 12.7426C10.0689 12.6145 10.0735 12.4915 10.0959 12.3652C10.112 12.3491 10.1282 12.333 10.1448 12.3164Z" fill="#F7F2E8"/>
26 +<path d="M4.23633 17.5166C4.29704 17.5261 4.32723 17.5339 4.37022 17.5791C4.38042 17.5942 4.39062 17.6093 4.40112 17.6249C4.41157 17.64 4.42202 17.6551 4.43279 17.6706C4.45605 17.7119 4.45605 17.7119 4.45605 17.7607C4.47217 17.7607 4.48828 17.7607 4.50488 17.7607C4.53711 17.8171 4.56934 17.8735 4.60254 17.9316C4.57837 17.9316 4.5542 17.9316 4.5293 17.9316C4.48256 17.878 4.44352 17.8207 4.40274 17.7624C4.35492 17.708 4.33135 17.696 4.26074 17.6875C4.26074 17.6633 4.26074 17.6392 4.26074 17.6143C4.24463 17.6062 4.22852 17.5981 4.21191 17.5898C4.21997 17.5657 4.22803 17.5415 4.23633 17.5166Z" fill="#222321"/>
27 +<path d="M5.84766 4.94336C5.87183 4.95142 5.896 4.95947 5.9209 4.96777C5.76898 5.15832 5.76898 5.15832 5.65234 5.21191C5.62817 5.21191 5.604 5.21191 5.5791 5.21191C5.56299 5.24414 5.54688 5.27637 5.53027 5.30957C5.51416 5.30151 5.49805 5.29346 5.48145 5.28516C5.51794 5.18894 5.56534 5.16532 5.65234 5.11426C5.70322 5.06904 5.7529 5.02285 5.80207 4.97578C5.81711 4.96508 5.83216 4.95438 5.84766 4.94336Z" fill="#1D1E1C"/>
28 +<path d="M9.19252 6.94531C9.22877 6.96948 9.22877 6.96948 9.26576 6.99414C9.27631 7.05571 9.27631 7.05571 9.27825 7.13242C9.27912 7.1598 9.27998 7.18718 9.28087 7.21539C9.28142 7.24409 9.28197 7.2728 9.28254 7.30237C9.28338 7.33126 9.28421 7.36015 9.28507 7.38992C9.28709 7.46144 9.28877 7.53295 9.29017 7.60449C9.27406 7.60449 9.25794 7.60449 9.24134 7.60449C9.19717 7.45422 9.18635 7.32116 9.18946 7.16504C9.18968 7.1438 9.1899 7.12255 9.19013 7.10067C9.1907 7.04888 9.19158 6.99709 9.19252 6.94531Z" fill="#212220"/>
29 +<path d="M5.26207 5.50488C5.27013 5.521 5.27818 5.53711 5.28648 5.55371C5.26836 5.57083 5.25023 5.58795 5.23155 5.60559C5.16619 5.67392 5.11826 5.7433 5.06676 5.82227C5.02708 5.86804 5.02708 5.86804 4.99352 5.89551C4.9774 5.89551 4.96129 5.89551 4.94469 5.89551C4.93706 5.83905 4.93706 5.83905 4.94469 5.77344C4.98436 5.7474 5.0254 5.72346 5.06676 5.7002C5.13434 5.63748 5.19832 5.57148 5.26207 5.50488Z" fill="#181917"/>
30 +<path d="M3.87012 16.9307C3.97511 16.9672 4.01831 17.0443 4.07306 17.1351C4.08214 17.1492 4.09122 17.1633 4.10057 17.1779C4.1647 17.2825 4.1647 17.2825 4.15651 17.3558C4.15062 17.3686 4.14474 17.3814 4.13867 17.3945C4.1269 17.3757 4.11513 17.3569 4.103 17.3375C4.07987 17.3008 4.07987 17.3008 4.05627 17.2633C4.04098 17.2389 4.02568 17.2146 4.00993 17.1895C3.97039 17.1254 3.97039 17.1254 3.91895 17.0771C3.90151 17.0287 3.8851 16.9799 3.87012 16.9307Z" fill="#181917"/>
31 +<path d="M19.7881 17.2725C19.7961 17.2966 19.8042 17.3208 19.8125 17.3457C19.7765 17.4127 19.7765 17.4127 19.724 17.4891C19.7071 17.5142 19.6901 17.5394 19.6727 17.5652C19.6176 17.6381 19.5611 17.698 19.4951 17.7607C19.5102 17.6838 19.5358 17.6321 19.5807 17.5681C19.5927 17.5508 19.6047 17.5335 19.6171 17.5156C19.6297 17.4978 19.6423 17.48 19.6553 17.4617C19.668 17.4435 19.6807 17.4253 19.6938 17.4065C19.7251 17.3618 19.7565 17.3171 19.7881 17.2725Z" fill="#181918"/>
32 +<path d="M13.3428 14.8311C13.3508 14.8311 13.3589 14.8311 13.3672 14.8311C13.3754 14.8814 13.3836 14.9317 13.3916 14.9821C13.3961 15.0102 13.4007 15.0382 13.4053 15.0671C13.416 15.1484 13.416 15.1484 13.416 15.2705C13.3354 15.2705 13.2549 15.2705 13.1719 15.2705C13.184 15.2343 13.184 15.2343 13.1963 15.1973C13.2527 15.1973 13.3091 15.1973 13.3672 15.1973C13.3591 15.1731 13.3511 15.1489 13.3428 15.124C13.3411 15.0737 13.3407 15.0233 13.3412 14.973C13.3415 14.9464 13.3417 14.9199 13.3419 14.8926C13.3422 14.8723 13.3425 14.852 13.3428 14.8311Z" fill="#E5E0D9"/>
33 +<path d="M11.2432 13.7324C11.2512 13.7324 11.2593 13.7324 11.2676 13.7324C11.2708 13.7619 11.2741 13.7913 11.2774 13.8217C11.2818 13.8611 11.2861 13.9005 11.2905 13.9399C11.2926 13.9593 11.2947 13.9786 11.2969 13.9985C11.3105 14.1215 11.3252 14.2444 11.3408 14.3672C11.3247 14.3752 11.3086 14.3833 11.292 14.3916C11.2759 14.3191 11.2598 14.2466 11.2432 14.1719C11.2351 14.1719 11.2271 14.1719 11.2188 14.1719C11.2308 13.9543 11.2308 13.9543 11.2432 13.7324Z" fill="#E2DFD8"/>
34 +<path d="M13.9043 8.67871C13.9043 8.79956 13.9043 8.92041 13.9043 9.04492C13.8882 9.04492 13.8721 9.04492 13.8555 9.04492C13.8555 9.08521 13.8555 9.12549 13.8555 9.16699C13.8394 9.16699 13.8232 9.16699 13.8066 9.16699C13.8147 9.01392 13.8228 8.86084 13.8311 8.70312C13.8799 8.67871 13.8799 8.67871 13.9043 8.67871Z" fill="#1A1816"/>
35 +<path d="M17.1758 4.38184C17.3069 4.39029 17.3069 4.39029 17.3711 4.40625C17.397 4.44897 17.397 4.44897 17.4199 4.50391C17.4741 4.54879 17.5319 4.58755 17.5908 4.62598C17.5667 4.63403 17.5425 4.64209 17.5176 4.65039C17.456 4.61267 17.3997 4.57447 17.3421 4.53137C17.3261 4.51982 17.3102 4.50827 17.2937 4.49637C17.2538 4.46715 17.2147 4.43679 17.1758 4.40625C17.1758 4.39819 17.1758 4.39014 17.1758 4.38184Z" fill="#1B1C1A"/>
36 +<path d="M19.4219 15.4414C19.4299 15.4575 19.438 15.4736 19.4463 15.4902C19.4244 15.545 19.4001 15.5988 19.3746 15.652C19.3608 15.681 19.3469 15.71 19.3327 15.7399C19.3218 15.7623 19.311 15.7846 19.2998 15.8076C19.2917 15.7754 19.2837 15.7432 19.2754 15.71C19.2351 15.7422 19.1948 15.7744 19.1533 15.8076C19.1931 15.7159 19.2513 15.6461 19.3151 15.5696C19.335 15.5455 19.355 15.5215 19.3755 15.4967C19.3908 15.4785 19.4061 15.4602 19.4219 15.4414Z" fill="#16A34A"/>
37 +<path d="M10.9502 17.4922C10.9583 17.4922 10.9663 17.4922 10.9746 17.4922C10.9827 17.605 10.9907 17.7178 10.999 17.834C10.8298 17.8219 10.8298 17.8219 10.6572 17.8096C10.6572 17.8015 10.6572 17.7935 10.6572 17.7852C10.7458 17.7852 10.8345 17.7852 10.9258 17.7852C10.9253 17.7549 10.9248 17.7247 10.9243 17.6936C10.9241 17.6225 10.9275 17.5603 10.9502 17.4922Z" fill="#E2DACE"/>
38 +<path d="M9.58301 15.5879C9.70318 15.6884 9.81523 15.7934 9.9248 15.9053C9.94868 15.9292 9.97258 15.9531 9.99652 15.977C10.0131 15.9936 10.0298 16.0102 10.0469 16.0273C9.94211 16.0157 9.89815 15.9541 9.82715 15.8809C9.80559 15.8599 9.78403 15.8389 9.76182 15.8172C9.74058 15.7964 9.71934 15.7756 9.69745 15.7542C9.67589 15.7332 9.65433 15.7122 9.63212 15.6906C9.58301 15.6367 9.58301 15.6367 9.58301 15.5879Z" fill="#080706"/>
39 +<path d="M6.65332 11.0469C6.73399 11.0989 6.80679 11.1484 6.87305 11.2178C6.89722 11.2339 6.92139 11.25 6.94629 11.2666C6.9731 11.2943 6.99902 11.3228 7.02411 11.3521C7.03704 11.3669 7.04998 11.3818 7.0633 11.3971C7.09277 11.4375 7.09277 11.4375 7.09277 11.4863C7.01094 11.4591 6.9947 11.4386 6.94171 11.3734C6.86494 11.2822 6.78229 11.2063 6.69004 11.131C6.67792 11.1193 6.6658 11.1077 6.65332 11.0957C6.65332 11.0796 6.65332 11.0635 6.65332 11.0469Z" fill="#070605"/>
40 +<path d="M9.77832 21.252C9.85162 21.2596 9.92484 21.2679 9.99805 21.2764C10.0396 21.2809 10.0396 21.2809 10.082 21.2855C10.1445 21.3008 10.1445 21.3008 10.1934 21.374C10.0505 21.3615 9.91675 21.3385 9.77832 21.3008C9.77832 21.2847 9.77832 21.2686 9.77832 21.252Z" fill="#1F1F1E"/>
41 +<path d="M5.65234 19.0791C5.73141 19.1144 5.78954 19.1586 5.85376 19.2164C5.87101 19.2319 5.88825 19.2473 5.90602 19.2632C5.91899 19.2749 5.93195 19.2867 5.94531 19.2988C5.9292 19.3149 5.91309 19.3311 5.89648 19.3477C5.88188 19.3366 5.86728 19.3255 5.85223 19.3141C5.81089 19.2834 5.76844 19.2542 5.72559 19.2256C5.65234 19.1388 5.65234 19.1388 5.65234 19.0791Z" fill="#1A1B1A"/>
42 +<path d="M9.85156 6.55469C10.0158 6.63866 10.1506 6.75325 10.291 6.87207C10.2749 6.88818 10.2588 6.9043 10.2422 6.9209C10.2242 6.90412 10.2062 6.88735 10.1877 6.87007C10.1637 6.84784 10.1396 6.82562 10.1155 6.80341C10.092 6.7816 10.0685 6.75979 10.0443 6.73732C9.97668 6.67586 9.97668 6.67586 9.90373 6.63527C9.88651 6.62479 9.8693 6.61431 9.85156 6.60352C9.85156 6.5874 9.85156 6.57129 9.85156 6.55469Z" fill="#F4F2ED"/>
43 +<path d="M11.2676 21.4477C11.2676 21.4558 11.2676 21.4638 11.2676 21.4721C11.3401 21.4842 11.3401 21.4842 11.4141 21.4965C11.4141 21.5046 11.4141 21.5127 11.4141 21.521C11.2332 21.5238 11.0566 21.5188 10.877 21.4965C10.877 21.4885 10.877 21.4804 10.877 21.4721C11.0081 21.4555 11.1354 21.4449 11.2676 21.4477Z" fill="#2B2B2A"/>
44 +<path d="M21.1552 14.001C21.1906 14.0955 21.1685 14.1676 21.1446 14.2634C21.1375 14.292 21.1305 14.3206 21.1233 14.35C21.1177 14.3718 21.1122 14.3936 21.1064 14.416C21.0477 14.3279 21.0559 14.3088 21.0759 14.2085C21.0803 14.1851 21.0847 14.1617 21.0893 14.1375C21.1064 14.0742 21.1064 14.0742 21.1552 14.001Z" fill="#1E1F1E"/>
45 +<path d="M14.1001 6.94531C14.1001 7.06616 14.1001 7.18701 14.1001 7.31152C14.076 7.32361 14.076 7.32361 14.0513 7.33594C14.0161 7.26552 14.0246 7.20368 14.0254 7.12537C14.0256 7.09635 14.0258 7.06733 14.026 7.03744C14.0263 7.01509 14.0266 6.99275 14.0269 6.96973C14.0757 6.94531 14.0757 6.94531 14.1001 6.94531Z" fill="#16A34A"/>
46 +<path d="M6.28711 14.6113C6.30322 14.6113 6.31934 14.6113 6.33594 14.6113C6.35233 14.6428 6.36858 14.6744 6.38477 14.7059C6.39383 14.7235 6.40289 14.7411 6.41223 14.7591C6.43359 14.8066 6.43359 14.8066 6.43359 14.8555C6.44971 14.8555 6.46582 14.8555 6.48242 14.8555C6.48242 14.8877 6.48242 14.9199 6.48242 14.9531C6.46631 14.9531 6.4502 14.9531 6.43359 14.9531C6.40906 14.9054 6.38467 14.8575 6.36035 14.8097C6.33996 14.7698 6.33996 14.7698 6.31915 14.729C6.28711 14.6602 6.28711 14.6602 6.28711 14.6113Z" fill="#181614"/>
47 +<path d="M21.302 11.2178C21.31 11.2178 21.3181 11.2178 21.3264 11.2178C21.3446 11.3893 21.3542 11.558 21.3508 11.7305C21.3032 11.6911 21.2793 11.6711 21.2715 11.6086C21.2736 11.5815 21.2736 11.5815 21.2757 11.5539C21.2771 11.5344 21.2784 11.5148 21.2798 11.4946C21.2816 11.4742 21.2834 11.4539 21.2852 11.4329C21.2867 11.4123 21.2882 11.3917 21.2897 11.3704C21.2935 11.3195 21.2977 11.2686 21.302 11.2178Z" fill="#292B2A"/>
48 +<path d="M2.64941 10.7051C2.65747 10.7051 2.66553 10.7051 2.67383 10.7051C2.68188 10.7856 2.68994 10.8662 2.69824 10.9492C2.69019 10.9492 2.68213 10.9492 2.67383 10.9492C2.67534 10.9799 2.67685 11.0107 2.67841 11.0423C2.68178 11.1109 2.68241 11.1345 2.63692 11.1875C2.62493 11.1975 2.61294 11.2075 2.60059 11.2178C2.60405 11.1598 2.60763 11.1018 2.61127 11.0438C2.61223 11.0276 2.61319 11.0113 2.61418 10.9946C2.62041 10.8967 2.63154 10.8015 2.64941 10.7051Z" fill="#2E2F2D"/>
49 +<path d="M3.08919 8.94727C3.11723 8.98656 3.11723 8.98656 3.13802 9.04492C3.11608 9.11855 3.11608 9.11855 3.08003 9.19751C3.06826 9.22382 3.05649 9.25013 3.04437 9.27724C3.03499 9.29725 3.02561 9.31727 3.01595 9.33789C2.98978 9.24873 2.99729 9.18495 3.01595 9.09375C3.03206 9.09375 3.04817 9.09375 3.06478 9.09375C3.07283 9.04541 3.08089 8.99707 3.08919 8.94727Z" fill="#202120"/>
50 +<path d="M7.80078 20.5439C7.91942 20.5588 8.01417 20.6105 8.11816 20.666C8.11816 20.6821 8.11816 20.6982 8.11816 20.7148C8.08355 20.7037 8.04897 20.6925 8.0144 20.6813C7.99514 20.675 7.97588 20.6688 7.95604 20.6624C7.9017 20.6428 7.85182 20.6197 7.80078 20.5928C7.80078 20.5767 7.80078 20.5605 7.80078 20.5439Z" fill="#1A1B19"/>
51 +<path d="M14.124 15.1484C14.1295 15.1642 14.135 15.1799 14.1406 15.1961C14.1687 15.252 14.1687 15.252 14.231 15.261C14.2642 15.2627 14.2642 15.2627 14.298 15.2644C14.3202 15.2658 14.3424 15.2672 14.3653 15.2686C14.3824 15.2692 14.3994 15.2699 14.417 15.2705C14.417 15.2866 14.417 15.3027 14.417 15.3193C14.3203 15.3193 14.2236 15.3193 14.124 15.3193C14.124 15.2629 14.124 15.2065 14.124 15.1484Z" fill="#F1EFE9"/>
52 +<path d="M4.79785 5.94434C4.82202 5.96851 4.84619 5.99268 4.87109 6.01758C4.85397 6.03419 4.83685 6.05081 4.81921 6.06793C4.7422 6.14472 4.67238 6.22727 4.60254 6.31055C4.60254 6.2188 4.62466 6.20596 4.68646 6.14117C4.74806 6.07659 4.78369 6.03348 4.79785 5.94434Z" fill="#232423"/>
53 +<path d="M13.2695 11.9258C13.3179 11.95 13.3662 11.9741 13.416 11.999C13.3529 12.0411 13.3174 12.0588 13.2466 12.0768C13.2303 12.0812 13.214 12.0856 13.1971 12.0901C13.1475 12.0967 13.1475 12.0967 13.0498 12.0723C13.1223 12.0481 13.1948 12.0239 13.2695 11.999C13.2695 11.9749 13.2695 11.9507 13.2695 11.9258Z" fill="#070504"/>
54 +<path d="M20.4961 16.0029C20.5122 16.0029 20.5283 16.0029 20.5449 16.0029C20.4743 16.2372 20.4743 16.2372 20.3984 16.3203C20.4106 16.1989 20.4364 16.1094 20.4961 16.0029Z" fill="#1D1D1C"/>
55 +<path d="M15.2715 12.7075C15.2634 12.7317 15.2554 12.7558 15.2471 12.7807C15.1021 12.7727 14.957 12.7646 14.8076 12.7563C14.8076 12.7483 14.8076 12.7402 14.8076 12.7319C14.9629 12.7159 15.1153 12.7046 15.2715 12.7075Z" fill="#ECE1D0"/>
56 +<path d="M14.417 9.26465C14.419 9.36624 14.4166 9.45867 14.3926 9.55762C14.3684 9.54956 14.3442 9.5415 14.3193 9.5332C14.3221 9.493 14.3252 9.45282 14.3285 9.41266C14.3302 9.39028 14.3319 9.36791 14.3336 9.34485C14.337 9.32644 14.3403 9.30803 14.3438 9.28906C14.3926 9.26465 14.3926 9.26465 14.417 9.26465Z" fill="#E8E0D4"/>
57 +<path d="M7.80078 3.625C7.84912 3.64111 7.89746 3.65723 7.94727 3.67383C7.86748 3.72702 7.79149 3.75922 7.70312 3.7959C7.68701 3.77979 7.6709 3.76367 7.6543 3.74707C7.70264 3.73096 7.75098 3.71484 7.80078 3.69824C7.80078 3.67407 7.80078 3.6499 7.80078 3.625Z" fill="#1E201E"/>
58 +<path d="M3.57715 16.3691C3.59326 16.3691 3.60937 16.3691 3.62598 16.3691C3.64312 16.3995 3.65982 16.43 3.67633 16.4607C3.68568 16.4777 3.69502 16.4947 3.70465 16.5122C3.72684 16.5733 3.72079 16.6019 3.69922 16.6621C3.67868 16.6271 3.65838 16.592 3.63818 16.5568C3.62119 16.5275 3.62119 16.5275 3.60385 16.4976C3.57715 16.4424 3.57715 16.4424 3.57715 16.3691Z" fill="#1B1C1B"/>
59 +<path d="M2.6497 13.3906C2.65776 13.4229 2.66581 13.4551 2.67411 13.4883C2.69023 13.4883 2.70634 13.4883 2.72294 13.4883C2.71488 13.5688 2.70683 13.6494 2.69853 13.7324C2.67436 13.7244 2.65019 13.7163 2.62529 13.708C2.62342 13.5977 2.62279 13.4983 2.6497 13.3906Z" fill="#2C2C2B"/>
60 +<path d="M9.26562 3.16107C9.21729 3.16913 9.16895 3.17719 9.11914 3.18549C9.11914 3.2016 9.11914 3.21772 9.11914 3.23432C9.04066 3.27356 8.96201 3.26013 8.875 3.25873C8.93809 3.21667 8.98606 3.19308 9.05658 3.1687C9.08448 3.15874 9.08448 3.15874 9.11294 3.14858C9.1733 3.13551 9.20776 3.14143 9.26562 3.16107Z" fill="#262725"/>
61 +<path d="M4.84668 18.2979C4.95049 18.3094 5.00227 18.3655 5.06641 18.4443C5.05754 18.4973 5.05754 18.4973 5.04199 18.542C5.00937 18.5059 4.97683 18.4698 4.94434 18.4337C4.92621 18.4135 4.90808 18.3934 4.8894 18.3727C4.84668 18.3223 4.84668 18.3223 4.84668 18.2979Z" fill="#1B1C1B"/>
62 +<path d="M4.40723 17.6875C4.4314 17.6996 4.4314 17.6996 4.45605 17.7119C4.45605 17.728 4.45605 17.7441 4.45605 17.7607C4.47217 17.7607 4.48828 17.7607 4.50488 17.7607C4.53711 17.8171 4.56934 17.8735 4.60254 17.9316C4.57837 17.9316 4.5542 17.9316 4.5293 17.9316C4.49544 17.8968 4.49544 17.8968 4.46368 17.8508C4.45302 17.8358 4.44235 17.8207 4.43135 17.8053C4.40723 17.7607 4.40723 17.7607 4.40723 17.6875Z" fill="#191918"/>
63 +<path d="M3.62598 16.6621C3.70685 16.6652 3.70685 16.6652 3.79688 16.6865C3.84823 16.7587 3.87421 16.8019 3.86249 16.891C3.85695 16.9041 3.85141 16.9172 3.8457 16.9307C3.74805 16.7659 3.74805 16.7659 3.74805 16.7109C3.70776 16.7109 3.66748 16.7109 3.62598 16.7109C3.62598 16.6948 3.62598 16.6787 3.62598 16.6621Z" fill="#1E1F1E"/>
64 +<path d="M9.02148 15.7344C9.11467 15.7723 9.17266 15.8329 9.24121 15.9053C9.23315 15.9294 9.2251 15.9536 9.2168 15.9785C9.1841 15.9471 9.15158 15.9156 9.11914 15.8839C9.09195 15.8576 9.09195 15.8576 9.06421 15.8307C9.02148 15.7832 9.02148 15.7832 9.02148 15.7344Z" fill="#1C1B17"/>
65 +<path d="M10.2422 2.91651C10.2261 2.91651 10.21 2.91651 10.1934 2.91651C10.1934 2.93263 10.1934 2.94874 10.1934 2.96534C9.99717 2.99804 9.99717 2.99804 9.90039 2.94093C9.93899 2.93201 9.97766 2.92343 10.0164 2.91499C10.0379 2.91017 10.0594 2.90536 10.0816 2.90039C10.1442 2.89214 10.1829 2.8967 10.2422 2.91651Z" fill="#262726"/>
66 +<path d="M12.1465 13.293C12.1707 13.293 12.1948 13.293 12.2197 13.293C12.2197 13.3252 12.2197 13.3574 12.2197 13.3906C12.2036 13.3906 12.1875 13.3906 12.1709 13.3906C12.1709 13.4551 12.1709 13.5195 12.1709 13.5859C12.1548 13.5859 12.1387 13.5859 12.1221 13.5859C12.1301 13.4893 12.1382 13.3926 12.1465 13.293Z" fill="#E7E3DC"/>
67 +<path d="M9.33887 8.16602C9.36304 8.16602 9.38721 8.16602 9.41211 8.16602C9.43958 8.24689 9.43958 8.24689 9.46094 8.33691C9.43677 8.37317 9.43677 8.37317 9.41211 8.41016C9.33887 8.24841 9.33887 8.24841 9.33887 8.16602Z" fill="#171716"/>
68 +<path d="M3.74805 7.50684C3.76965 7.56733 3.7753 7.59621 3.75415 7.6579C3.72363 7.70215 3.72363 7.70215 3.65039 7.72656C3.63361 7.6701 3.63361 7.6701 3.62598 7.60449C3.66107 7.56024 3.66107 7.56024 3.69922 7.53125C3.70728 7.53931 3.71533 7.54736 3.72363 7.55566C3.73169 7.53955 3.73975 7.52344 3.74805 7.50684Z" fill="#232322"/>
69 +<path d="M17.6396 4.6748C17.6707 4.69762 17.7017 4.72052 17.7327 4.74347C17.75 4.75622 17.7673 4.76896 17.7851 4.78209C17.8297 4.81717 17.8691 4.85347 17.9082 4.89453C17.7989 4.86402 17.7216 4.8276 17.6396 4.74805C17.6396 4.72388 17.6396 4.69971 17.6396 4.6748Z" fill="#1E1E1D"/>
70 +<path d="M8.19141 3.50293C8.20752 3.51099 8.22363 3.51904 8.24023 3.52734C8.22412 3.54346 8.20801 3.55957 8.19141 3.57617C8.17529 3.56812 8.15918 3.56006 8.14258 3.55176C8.14258 3.56787 8.14258 3.58398 8.14258 3.60059C8.06604 3.64594 8.01136 3.65433 7.92285 3.64941C8.00638 3.57925 8.08982 3.54056 8.19141 3.50293Z" fill="#1F201E"/>
71 +<path d="M15.8086 20.7157C15.793 20.7227 15.7774 20.7298 15.7613 20.7371C15.7072 20.7603 15.7072 20.7603 15.6865 20.8133C15.6118 20.8286 15.6118 20.8286 15.54 20.8378C15.532 20.8216 15.5239 20.8055 15.5156 20.7889C15.6935 20.7052 15.6935 20.7052 15.8086 20.7157Z" fill="#1E1F1E"/>
72 +<path d="M3.11328 15.3193C3.13745 15.3274 3.16162 15.3354 3.18652 15.3438C3.18652 15.3921 3.18652 15.4404 3.18652 15.4902C3.20264 15.4902 3.21875 15.4902 3.23535 15.4902C3.24341 15.5144 3.25146 15.5386 3.25977 15.5635C3.2356 15.5997 3.2356 15.5997 3.21094 15.6367C3.19457 15.593 3.17831 15.5493 3.16211 15.5055C3.15305 15.4811 3.14398 15.4568 3.13464 15.4317C3.11328 15.3682 3.11328 15.3682 3.11328 15.3193Z" fill="#232322"/>
73 +<path d="M7.92285 11.5596C7.94702 11.5596 7.97119 11.5596 7.99609 11.5596C8.02026 11.6563 8.04443 11.7529 8.06934 11.8525C7.9839 11.7956 7.98064 11.7742 7.9549 11.6786C7.94876 11.6564 7.94262 11.6342 7.9363 11.6113C7.92964 11.5857 7.92964 11.5857 7.92285 11.5596Z" fill="#161514"/>
74 +<path d="M18.665 18.6641C18.6892 18.6721 18.7134 18.6802 18.7383 18.6885C18.6819 18.7449 18.6255 18.8013 18.5674 18.8594C18.5513 18.8513 18.5352 18.8433 18.5186 18.835C18.5186 18.8188 18.5186 18.8027 18.5186 18.7861C18.5872 18.7205 18.5872 18.7205 18.665 18.6641Z" fill="#1C1D1C"/>
75 +<path d="M13.2695 14.5137C13.3058 14.5378 13.3058 14.5378 13.3428 14.5625C13.3347 14.635 13.3267 14.7075 13.3184 14.7822C13.3022 14.7742 13.2861 14.7661 13.2695 14.7578C13.2695 14.6772 13.2695 14.5967 13.2695 14.5137Z" fill="#ECE4D5"/>
76 +<path d="M21.2285 10.6562C21.2366 10.6562 21.2446 10.6562 21.2529 10.6562C21.261 10.7529 21.269 10.8496 21.2773 10.9492C21.2693 10.925 21.2612 10.9009 21.2529 10.876C21.2288 10.8679 21.2046 10.8599 21.1797 10.8516C21.1797 10.8193 21.1797 10.7871 21.1797 10.7539C21.1958 10.7539 21.2119 10.7539 21.2285 10.7539C21.2285 10.7217 21.2285 10.6895 21.2285 10.6562Z" fill="#202120"/>
77 +<path d="M13.3428 8.26367C13.3508 8.35229 13.3589 8.44092 13.3672 8.53223C13.3511 8.53223 13.335 8.53223 13.3184 8.53223C13.3184 8.49194 13.3184 8.45166 13.3184 8.41016C13.3022 8.41016 13.2861 8.41016 13.2695 8.41016C13.2695 8.36987 13.2695 8.32959 13.2695 8.28809C13.3184 8.26367 13.3184 8.26367 13.3428 8.26367Z" fill="#1C1C1A"/>
78 +<path d="M3.89453 7.2627C3.91284 7.30084 3.91284 7.30084 3.91895 7.36035C3.89134 7.42146 3.85739 7.47437 3.82129 7.53125C3.80518 7.52319 3.78906 7.51514 3.77246 7.50684C3.80505 7.41909 3.84473 7.34192 3.89453 7.2627Z" fill="#1B1C1B"/>
79 +<path d="M15.1982 3.33203C15.2375 3.34307 15.2766 3.35429 15.3157 3.3656C15.3484 3.37495 15.3484 3.37495 15.3818 3.38448C15.4424 3.40527 15.4424 3.40527 15.5156 3.4541C15.3508 3.45715 15.3508 3.45715 15.2959 3.42969C15.2959 3.41357 15.2959 3.39746 15.2959 3.38086C15.2637 3.38086 15.2314 3.38086 15.1982 3.38086C15.1982 3.36475 15.1982 3.34863 15.1982 3.33203Z" fill="#222322"/>
80 +<path d="M13.2695 2.84375C13.3984 2.84375 13.5273 2.84375 13.6602 2.84375C13.6602 2.85181 13.6602 2.85986 13.6602 2.86816C13.5554 2.88428 13.4507 2.90039 13.3428 2.91699C13.3428 2.90088 13.3428 2.88477 13.3428 2.86816C13.3186 2.86011 13.2944 2.85205 13.2695 2.84375Z" fill="#2B2B2A"/>
81 +<path d="M20.0322 16.8818C20.0483 16.8818 20.0645 16.8818 20.0811 16.8818C20.0485 16.9696 20.0088 17.0468 19.959 17.126C19.9407 17.0863 19.9407 17.0863 19.9346 17.0283C19.9788 16.949 19.9788 16.949 20.0322 16.8818Z" fill="#1A1A1A"/>
82 +<path d="M20.7157 15.5391C20.7401 15.5879 20.7401 15.5879 20.7211 15.6461C20.7118 15.6682 20.7024 15.6903 20.6928 15.713C20.6835 15.7352 20.6742 15.7575 20.6647 15.7803C20.6573 15.7974 20.65 15.8145 20.6424 15.832C20.6344 15.832 20.6263 15.832 20.618 15.832C20.615 15.6917 20.615 15.6917 20.6424 15.6367C20.6586 15.6367 20.6747 15.6367 20.6913 15.6367C20.6832 15.6125 20.6752 15.5884 20.6669 15.5635C20.683 15.5554 20.6991 15.5474 20.7157 15.5391Z" fill="#1F1F1E"/>
83 +<path d="M21.0576 9.82617C21.116 9.91956 21.1201 10.0116 21.1308 10.1191C21.1147 10.1191 21.0986 10.1191 21.082 10.1191C21.0264 9.92296 21.0264 9.92296 21.0576 9.82617Z" fill="#1D1D1C"/>
84 +<path d="M20.5449 8.41016C20.561 8.41016 20.5771 8.41016 20.5938 8.41016C20.6179 8.48267 20.6421 8.55518 20.667 8.62988C20.6428 8.63794 20.6187 8.646 20.5938 8.6543C20.5776 8.57373 20.5615 8.49316 20.5449 8.41016Z" fill="#1A1B19"/>
85 +<path d="M20.2764 7.84863C20.3368 7.8728 20.3368 7.8728 20.3984 7.89746C20.4065 7.95386 20.4146 8.01025 20.4229 8.06836C20.3496 8.04395 20.3496 8.04395 20.3084 7.97223C20.2764 7.89746 20.2764 7.89746 20.2764 7.84863Z" fill="#1E1F1E"/>
86 +<path d="M3.04004 15.0996C3.06421 15.1077 3.08838 15.1157 3.11328 15.124C3.13007 15.2049 3.13007 15.2049 3.1377 15.2949C3.11353 15.3191 3.08936 15.3433 3.06445 15.3682C3.0564 15.2795 3.04834 15.1909 3.04004 15.0996Z" fill="#232423"/>
87 +<path d="M2.74707 14.0254C2.77124 14.0334 2.79541 14.0415 2.82031 14.0498C2.85083 14.2085 2.85083 14.2085 2.7959 14.3184C2.77979 14.2217 2.76367 14.125 2.74707 14.0254Z" fill="#252524"/>
88 +<path d="M10.8037 11.0469C10.8279 11.0549 10.8521 11.063 10.877 11.0713C10.8044 11.168 10.8044 11.168 10.7305 11.2666C10.7144 11.2585 10.6982 11.2505 10.6816 11.2422C10.7219 11.1777 10.7622 11.1133 10.8037 11.0469Z" fill="#080807"/>
89 +<path d="M12.7812 10.2173C12.8828 10.2154 12.9753 10.2178 13.0742 10.2418C13.0742 10.2498 13.0742 10.2579 13.0742 10.2662C12.9292 10.2783 12.9292 10.2783 12.7812 10.2906C12.7812 10.2664 12.7812 10.2422 12.7812 10.2173Z" fill="#171615"/>
90 +<path d="M10.9016 8.38574C10.9097 8.38574 10.9178 8.38574 10.9261 8.38574C10.9261 8.42603 10.9261 8.46631 10.9261 8.50781C10.9583 8.49976 10.9905 8.4917 11.0237 8.4834C11.0157 8.50757 11.0076 8.53174 10.9993 8.55664C10.959 8.55664 10.9187 8.55664 10.8772 8.55664C10.8762 8.50782 10.8762 8.45897 10.8772 8.41016C10.8853 8.4021 10.8933 8.39404 10.9016 8.38574Z" fill="#060606"/>
91 +<path d="M15.833 3.60059C15.9285 3.6288 16.0008 3.65809 16.0771 3.72266C16.0039 3.74707 16.0039 3.74707 15.9276 3.71655C15.8574 3.67383 15.8574 3.67383 15.833 3.60059Z" fill="#1C1D1C"/>
92 +<path d="M10.8525 21.4476C10.8525 21.4557 10.8525 21.4637 10.8525 21.472C10.6833 21.4599 10.6833 21.4599 10.5107 21.4476C10.5107 21.4396 10.5107 21.4315 10.5107 21.4232C10.643 21.4004 10.7257 21.3937 10.8525 21.4476Z" fill="#2E2F2D"/>
93 +<path d="M16.3457 20.3975C16.3699 20.4136 16.394 20.4297 16.4189 20.4463C16.3875 20.4627 16.3559 20.4789 16.3243 20.4951C16.3068 20.5042 16.2892 20.5132 16.2711 20.5226C16.2236 20.5439 16.2236 20.5439 16.1748 20.5439C16.1748 20.5278 16.1748 20.5117 16.1748 20.4951C16.2312 20.4629 16.2876 20.4307 16.3457 20.3975Z" fill="#1B1C1B"/>
94 +<path d="M2.82031 14.3184C2.89355 14.3672 2.89355 14.3672 2.91339 14.4374C2.91808 14.5155 2.91119 14.547 2.86914 14.6113C2.84137 14.5072 2.82031 14.4266 2.82031 14.3184Z" fill="#262726"/>
95 +<path d="M14.5391 13.415C14.5391 13.4312 14.5391 13.4473 14.5391 13.4639C14.4424 13.5122 14.4424 13.5122 14.3438 13.5615C14.3888 13.4361 14.3991 13.415 14.5391 13.415Z" fill="#141412"/>
96 +<path d="M12.8301 9.55762C12.8865 9.55762 12.9429 9.55762 13.001 9.55762C13.009 9.58179 13.0171 9.60596 13.0254 9.63086C12.9529 9.63086 12.8804 9.63086 12.8057 9.63086C12.8137 9.60669 12.8218 9.58252 12.8301 9.55762Z" fill="#151513"/>
97 +<path d="M12.7568 9.45996C12.7729 9.47607 12.7891 9.49219 12.8057 9.50879C12.7976 9.5249 12.7896 9.54102 12.7812 9.55762C12.8215 9.56567 12.8618 9.57373 12.9033 9.58203C12.9033 9.59009 12.9033 9.59814 12.9033 9.60645C12.8389 9.60645 12.7744 9.60645 12.708 9.60645C12.7294 9.48743 12.7294 9.48743 12.7568 9.45996Z" fill="#040504"/>
98 +<path d="M5.45703 5.30957C5.46509 5.33374 5.47314 5.35791 5.48145 5.38281C5.40894 5.44324 5.40894 5.44324 5.33496 5.50488C5.31885 5.49683 5.30273 5.48877 5.28613 5.48047C5.3431 5.4235 5.40007 5.36654 5.45703 5.30957Z" fill="#161716"/>
99 +<path d="M15.418 20.8859C15.3378 20.9393 15.2664 20.9591 15.1738 20.9835C15.1658 20.9674 15.1577 20.9513 15.1494 20.9347C15.2429 20.8907 15.315 20.8811 15.418 20.8859Z" fill="#20211F"/>
100 +<path d="M20.2031 16.5889C20.2192 16.5889 20.2354 16.5889 20.252 16.5889C20.2253 16.681 20.2054 16.7468 20.1299 16.8086C20.142 16.7174 20.1512 16.6667 20.2031 16.5889Z" fill="#1D1E1D"/>
101 +<path d="M12.1222 14.1719C12.1302 14.1719 12.1383 14.1719 12.1466 14.1719C12.1547 14.2524 12.1627 14.333 12.171 14.416C12.1549 14.416 12.1388 14.416 12.1222 14.416C12.1141 14.4321 12.1061 14.4482 12.0978 14.4648C12.0943 14.2556 12.0943 14.2556 12.1222 14.1719Z" fill="#161614"/>
102 +<path d="M21.1797 10.3877C21.1827 10.4033 21.1857 10.4189 21.1888 10.435C21.1939 10.4516 21.1989 10.4682 21.2041 10.4854C21.2202 10.4934 21.2363 10.5015 21.2529 10.5098C21.2449 10.55 21.2368 10.5903 21.2285 10.6318C21.2043 10.6157 21.1802 10.5996 21.1553 10.583C21.1633 10.5186 21.1714 10.4541 21.1797 10.3877Z" fill="#232423"/>
103 +<path d="M20.6667 8.62988C20.7202 8.71007 20.7399 8.78142 20.7644 8.87402C20.7402 8.86597 20.7161 8.85791 20.6912 8.84961C20.6359 8.72247 20.6359 8.72247 20.6667 8.62988Z" fill="#1E1E1D"/>
104 +<path d="M18.8359 5.74902C18.9006 5.81196 18.9566 5.86905 19.0068 5.94434C18.928 5.93074 18.8842 5.91182 18.8359 5.84668C18.8359 5.81445 18.8359 5.78223 18.8359 5.74902Z" fill="#1C1C1B"/>
105 +<path d="M12.8789 2.79492C13.0481 2.80701 13.0481 2.80701 13.2207 2.81934C13.2207 2.82739 13.2207 2.83545 13.2207 2.84375C13.1821 2.84986 13.1434 2.85533 13.1047 2.86053C13.0724 2.86521 13.0724 2.86521 13.0395 2.86998C12.9766 2.86816 12.9766 2.86816 12.8789 2.79492Z" fill="#2F302D"/>
106 +<path d="M8.43555 20.8613C8.49353 20.8674 8.49353 20.8674 8.55762 20.8857C8.58966 20.9361 8.58966 20.9361 8.60645 20.9834C8.56616 20.9915 8.52588 20.9995 8.48438 21.0078C8.48085 20.9927 8.47733 20.9776 8.47369 20.962C8.46163 20.9094 8.46163 20.9094 8.43555 20.8613Z" fill="#323331"/>
107 +<path d="M3.87012 16.9307C3.91742 16.9459 3.91742 16.9459 3.96777 16.9795C3.98608 17.0558 3.98608 17.0558 3.99219 17.126C3.90414 17.0673 3.89808 17.0296 3.87012 16.9307Z" fill="#212221"/>
108 +<path d="M14.0264 14.9287C14.0506 14.9368 14.0747 14.9448 14.0996 14.9531C14.0996 15.0176 14.0996 15.082 14.0996 15.1484C14.0755 15.1323 14.0513 15.1162 14.0264 15.0996C14.0218 15.0111 14.0218 15.0111 14.0264 14.9287Z" fill="#151514"/>
109 +<path d="M5.79883 12.1943C5.81494 12.1943 5.83105 12.1943 5.84766 12.1943C5.87913 12.2371 5.87913 12.2371 5.91174 12.292C5.92263 12.3101 5.93352 12.3282 5.94474 12.3469C5.95299 12.361 5.96123 12.3751 5.96973 12.3896C5.91174 12.3744 5.91174 12.3744 5.84766 12.3408C5.81561 12.2645 5.81561 12.2645 5.79883 12.1943Z" fill="#151411"/>
110 +<path d="M10.1934 9.94824C10.2095 9.94824 10.2256 9.94824 10.2422 9.94824C10.2422 10.0127 10.2422 10.0771 10.2422 10.1436C10.204 10.1146 10.204 10.1146 10.1689 10.0703C10.1766 10.0047 10.1766 10.0047 10.1934 9.94824Z" fill="#191917"/>
111 +<path d="M2.9668 9.33789C2.99274 9.38214 2.99274 9.38214 3.01562 9.43555C3.00757 9.45972 2.99951 9.48389 2.99121 9.50879C2.9751 9.50879 2.95898 9.50879 2.94238 9.50879C2.93433 9.53296 2.92627 9.55713 2.91797 9.58203C2.93933 9.39282 2.93933 9.39282 2.9668 9.33789Z" fill="#222322"/>
112 +<path d="M13.709 9.3623C13.7251 9.37036 13.7412 9.37842 13.7578 9.38672C13.7498 9.43506 13.7417 9.4834 13.7334 9.5332C13.7092 9.5332 13.6851 9.5332 13.6602 9.5332C13.6815 9.38977 13.6815 9.38977 13.709 9.3623Z" fill="#EBE5D9"/>
113 +<path d="M20.4473 8.19043C20.4634 8.19043 20.4795 8.19043 20.4961 8.19043C20.5222 8.2384 20.5465 8.28732 20.5693 8.33691C20.5613 8.35303 20.5532 8.36914 20.5449 8.38574C20.4741 8.32727 20.4594 8.28046 20.4473 8.19043Z" fill="#1D1D1C"/>
114 +<path d="M3.40638 8.11719C3.42249 8.11719 3.4386 8.11719 3.4552 8.11719C3.46326 8.14136 3.47132 8.16553 3.47962 8.19043C3.43232 8.26825 3.43232 8.26825 3.38196 8.33691C3.34289 8.25877 3.38119 8.1972 3.40638 8.11719Z" fill="#212221"/>
115 +<path d="M18.9824 5.96875C19.0267 5.97333 19.0267 5.97333 19.0801 5.99316C19.1228 6.05573 19.1228 6.05573 19.1533 6.11523C19.1292 6.11523 19.105 6.11523 19.0801 6.11523C19.0267 6.04199 19.0267 6.04199 18.9824 5.96875Z" fill="#151615"/>
116 +<path d="M7.09277 4.08887C7.07666 4.12915 7.06055 4.16943 7.04395 4.21094C7.00366 4.20288 6.96338 4.19482 6.92188 4.18652C7.03784 4.08887 7.03784 4.08887 7.09277 4.08887Z" fill="#1B1C1B"/>
117 +<path d="M15.5645 3.47852C15.637 3.50269 15.7095 3.52686 15.7842 3.55176C15.7842 3.55981 15.7842 3.56787 15.7842 3.57617C15.6469 3.58228 15.6469 3.58228 15.5645 3.52734C15.5645 3.51123 15.5645 3.49512 15.5645 3.47852Z" fill="#1F1F1F"/>
118 +<path d="M13.8555 2.94141C13.928 2.94141 14.0005 2.94141 14.0752 2.94141C14.051 2.97766 14.051 2.97766 14.0264 3.01465C13.9501 3.01923 13.9501 3.01923 13.8799 3.01465C13.8718 2.99048 13.8638 2.96631 13.8555 2.94141Z" fill="#252625"/>
119 +<path d="M7.53223 20.3975C7.59265 20.4216 7.59265 20.4216 7.6543 20.4463C7.6543 20.4624 7.6543 20.4785 7.6543 20.4951C7.60596 20.5032 7.55762 20.5112 7.50781 20.5195C7.51587 20.4792 7.52393 20.439 7.53223 20.3975Z" fill="#232322"/>
120 +<path d="M16.985 20.0639C16.9996 20.0689 17.0143 20.074 17.0293 20.0792C16.8889 20.1768 16.8889 20.1768 16.834 20.1768C16.8622 20.0921 16.8895 20.0475 16.985 20.0639Z" fill="#1C1E1C"/>
121 +<path d="M11.3408 14.5137C11.3896 14.5381 11.3896 14.5381 11.4051 14.5832C11.4091 14.601 11.413 14.6189 11.4171 14.6373C11.4212 14.6552 11.4253 14.6731 11.4295 14.6915C11.4325 14.7053 11.4354 14.7192 11.4385 14.7334C11.4062 14.7173 11.374 14.7012 11.3408 14.6846C11.3408 14.6276 11.3408 14.5706 11.3408 14.5137Z" fill="#F6F4F0"/>
122 +<path d="M9.14355 10.9004C9.17981 10.9246 9.17981 10.9246 9.2168 10.9492C9.20874 11.0056 9.20068 11.062 9.19238 11.1201C9.17627 11.1121 9.16016 11.104 9.14355 11.0957C9.14355 11.0312 9.14355 10.9668 9.14355 10.9004Z" fill="#EFE6D8"/>
123 +<path d="M9.19238 10.7783C9.21655 10.7783 9.24072 10.7783 9.26562 10.7783C9.26562 10.8267 9.26562 10.875 9.26562 10.9248C9.24146 10.9329 9.21729 10.9409 9.19238 10.9492C9.19238 10.8928 9.19238 10.8364 9.19238 10.7783Z" fill="#171714"/>
124 +<path d="M12.8789 10.1436C12.9514 10.1436 13.0239 10.1436 13.0986 10.1436C13.0624 10.1798 13.0624 10.1798 13.0254 10.2168C12.9476 10.2092 12.9476 10.2092 12.8789 10.1924C12.8789 10.1763 12.8789 10.1602 12.8789 10.1436Z" fill="#ECE3D4"/>
125 +<path d="M3.52832 7.92188C3.54443 7.92993 3.56055 7.93799 3.57715 7.94629C3.55731 8.01801 3.55731 8.01801 3.52832 8.09277C3.50415 8.10083 3.47998 8.10889 3.45508 8.11719C3.50085 7.97681 3.50085 7.97681 3.52832 7.92188Z" fill="#1B1C1B"/>
126 +<path d="M16.7852 4.06445C16.8416 4.10474 16.8979 4.14502 16.9561 4.18652C16.9238 4.19458 16.8916 4.20264 16.8584 4.21094C16.8035 4.16364 16.8035 4.16364 16.7607 4.11328C16.7688 4.09717 16.7769 4.08105 16.7852 4.06445Z" fill="#1E1E1D"/>
127 +<path d="M10.7061 2.81975C10.6694 2.8579 10.6694 2.8579 10.6084 2.893C10.5407 2.89222 10.4802 2.88088 10.4131 2.86858C10.4131 2.86053 10.4131 2.85247 10.4131 2.84417C10.5123 2.82527 10.6051 2.81735 10.7061 2.81975Z" fill="#313331"/>
128 +<path d="M11.1943 21.5449C11.2588 21.5449 11.3232 21.5449 11.3896 21.5449C11.3977 21.561 11.4058 21.5771 11.4141 21.5938C11.3416 21.5857 11.269 21.5776 11.1943 21.5693C11.1943 21.5613 11.1943 21.5532 11.1943 21.5449Z" fill="#2B2C29"/>
129 +<path d="M6.94629 4.01562C6.99664 4.02631 6.99664 4.02631 7.04395 4.04004C7.02783 4.04004 7.01172 4.04004 6.99512 4.04004C6.98706 4.07227 6.979 4.10449 6.9707 4.1377C6.95459 4.10547 6.93848 4.07324 6.92188 4.04004C6.92993 4.03198 6.93799 4.02393 6.94629 4.01562Z" fill="#2C2E2B"/>
130 +<path d="M6.33594 4.40625C6.35205 4.40625 6.36816 4.40625 6.38477 4.40625C6.39282 4.43848 6.40088 4.4707 6.40918 4.50391C6.38501 4.50391 6.36084 4.50391 6.33594 4.50391C6.33594 4.47168 6.33594 4.43945 6.33594 4.40625Z" fill="#2D2F2D"/>
131 +<path d="M21.3506 12.9512C21.3586 12.9512 21.3667 12.9512 21.375 12.9512C21.375 13.0076 21.375 13.064 21.375 13.1221C21.3669 13.1221 21.3589 13.1221 21.3506 13.1221C21.3506 13.0657 21.3506 13.0093 21.3506 12.9512Z" fill="#2D2D2C"/>
132 +<path d="M12.0244 21.5693C12.0728 21.5693 12.1211 21.5693 12.1709 21.5693C12.1709 21.5774 12.1709 21.5854 12.1709 21.5938C12.1226 21.5938 12.0742 21.5938 12.0244 21.5938C12.0244 21.5857 12.0244 21.5776 12.0244 21.5693Z" fill="#2A2929"/>
133 +<path d="M3.8457 16.9795C3.86182 16.9875 3.87793 16.9956 3.89453 17.0039C3.89453 17.02 3.89453 17.0361 3.89453 17.0527C3.87036 17.0447 3.84619 17.0366 3.82129 17.0283C3.82935 17.0122 3.8374 16.9961 3.8457 16.9795Z" fill="#2B2C2A"/>
134 +<path d="M3.11328 15.4414C3.12134 15.4736 3.12939 15.5059 3.1377 15.5391C3.12158 15.531 3.10547 15.5229 3.08887 15.5146C3.09692 15.4905 3.10498 15.4663 3.11328 15.4414Z" fill="#2D2F2D"/>
135 +<path d="M2.625 10.5586C2.63306 10.5586 2.64111 10.5586 2.64941 10.5586C2.64941 10.6069 2.64941 10.6553 2.64941 10.7051C2.64136 10.7051 2.6333 10.7051 2.625 10.7051C2.625 10.6567 2.625 10.6084 2.625 10.5586Z" fill="#2C2C2C"/>
136 +<path d="M4.38281 6.35938C4.40698 6.37146 4.40698 6.37146 4.43164 6.38379C4.41553 6.41602 4.39941 6.44824 4.38281 6.48145C4.38281 6.44116 4.38281 6.40088 4.38281 6.35938Z" fill="#2B2D2A"/>
137 +<path d="M7.70312 3.625C7.72729 3.63306 7.75146 3.64111 7.77637 3.64941C7.76831 3.66553 7.76025 3.68164 7.75195 3.69824C7.73584 3.69019 7.71973 3.68213 7.70312 3.67383C7.70312 3.65771 7.70312 3.6416 7.70312 3.625Z" fill="#2B2E2A"/>
138 +<path d="M13.2695 21.4717C13.3018 21.4717 13.334 21.4717 13.3672 21.4717C13.3591 21.4878 13.3511 21.5039 13.3428 21.5205C13.3186 21.5044 13.2944 21.4883 13.2695 21.4717Z" fill="#2B2B2A"/>
139 +<path d="M9.85156 21.3496C9.89185 21.3496 9.93213 21.3496 9.97363 21.3496C9.97363 21.3577 9.97363 21.3657 9.97363 21.374C9.93335 21.374 9.89307 21.374 9.85156 21.374C9.85156 21.366 9.85156 21.3579 9.85156 21.3496Z" fill="#2A2A29"/>
140 +<path d="M9.11914 21.1299C9.16748 21.1541 9.16748 21.1541 9.2168 21.1787C9.18457 21.1787 9.15234 21.1787 9.11914 21.1787C9.11914 21.1626 9.11914 21.1465 9.11914 21.1299Z" fill="#282926"/>
141 +<path d="M5.38379 18.9082C5.3999 18.9243 5.41602 18.9404 5.43262 18.957C5.40845 18.957 5.38428 18.957 5.35938 18.957C5.36743 18.9409 5.37549 18.9248 5.38379 18.9082Z" fill="#2B2B29"/>
142 +<path d="M5.06641 18.5908C5.08252 18.6069 5.09863 18.623 5.11523 18.6396C5.09106 18.6396 5.06689 18.6396 5.04199 18.6396C5.05005 18.6235 5.05811 18.6074 5.06641 18.5908Z" fill="#2B2C2A"/>
143 +<path d="M2.64941 13.7568C2.65747 13.7568 2.66553 13.7568 2.67383 13.7568C2.67383 13.7971 2.67383 13.8374 2.67383 13.8789C2.66577 13.8789 2.65771 13.8789 2.64941 13.8789C2.64941 13.8386 2.64941 13.7983 2.64941 13.7568Z" fill="#2E2F2D"/>
144 +<path d="M21.3506 11.2422C21.3586 11.2422 21.3667 11.2422 21.375 11.2422C21.375 11.2825 21.375 11.3228 21.375 11.3643C21.3669 11.3643 21.3589 11.3643 21.3506 11.3643C21.3506 11.324 21.3506 11.2837 21.3506 11.2422Z" fill="#2D2E2E"/>
145 +<path d="M4.57812 6.16406C4.60229 6.17212 4.62646 6.18018 4.65137 6.18848C4.63525 6.18848 4.61914 6.18848 4.60254 6.18848C4.59448 6.20459 4.58643 6.2207 4.57812 6.2373C4.57812 6.21313 4.57812 6.18896 4.57812 6.16406Z" fill="#292A28"/>
146 +<path d="M5.60352 5.0166C5.62769 5.0166 5.65186 5.0166 5.67676 5.0166C5.6687 5.04077 5.66064 5.06494 5.65234 5.08984C5.65234 5.07373 5.65234 5.05762 5.65234 5.04102C5.63623 5.03296 5.62012 5.0249 5.60352 5.0166Z" fill="#2D2E2C"/>
147 +<path d="M9.26562 3.03906C9.28174 3.03906 9.29785 3.03906 9.31445 3.03906C9.31445 3.05518 9.31445 3.07129 9.31445 3.08789C9.29028 3.07983 9.26611 3.07178 9.24121 3.06348C9.24927 3.05542 9.25732 3.04736 9.26562 3.03906Z" fill="#2D2D2A"/>
148 +<path d="M13.709 2.86816C13.7493 2.86816 13.7896 2.86816 13.8311 2.86816C13.8311 2.87622 13.8311 2.88428 13.8311 2.89258C13.7908 2.89258 13.7505 2.89258 13.709 2.89258C13.709 2.88452 13.709 2.87646 13.709 2.86816Z" fill="#2C2D2A"/>
149 +<path d="M12.4639 21.5449C12.5615 21.5693 12.5615 21.5693 12.5615 21.5693L12.4639 21.5449Z" fill="#2B2C2B"/>
150 +<path d="M11.0479 21.5205C11.072 21.5286 11.0962 21.5366 11.1211 21.5449C11.0969 21.5449 11.0728 21.5449 11.0479 21.5449C11.0479 21.5369 11.0479 21.5288 11.0479 21.5205Z" fill="#292A28"/>
151 +<path d="M14.8564 21.1299C14.8726 21.146 14.8887 21.1621 14.9053 21.1787C14.8811 21.1908 14.8811 21.1908 14.8564 21.2031C14.8564 21.179 14.8564 21.1548 14.8564 21.1299Z" fill="#2B2D29"/>
152 +<path d="M8.09375 20.7637C8.11792 20.7717 8.14209 20.7798 8.16699 20.7881C8.14282 20.7881 8.11865 20.7881 8.09375 20.7881C8.09375 20.78 8.09375 20.772 8.09375 20.7637Z" fill="#2A2B29"/>
153 +<path d="M7.72754 20.5684C7.76379 20.5925 7.76379 20.5925 7.80078 20.6172C7.77661 20.6172 7.75244 20.6172 7.72754 20.6172C7.72754 20.6011 7.72754 20.585 7.72754 20.5684Z" fill="#2A2A27"/>
154 +<path d="M16.5898 20.3486C16.606 20.3567 16.6221 20.3647 16.6387 20.373C16.6145 20.373 16.5903 20.373 16.5654 20.373C16.5735 20.365 16.5815 20.3569 16.5898 20.3486Z" fill="#292A28"/>
155 +<path d="M6.36035 19.7383C6.39661 19.7625 6.39661 19.7625 6.43359 19.7871C6.40942 19.7871 6.38525 19.7871 6.36035 19.7871C6.36035 19.771 6.36035 19.7549 6.36035 19.7383Z" fill="#2B2B29"/>
156 +<path d="M6.14062 19.5674C6.16479 19.5795 6.16479 19.5795 6.18945 19.5918C6.1814 19.6079 6.17334 19.624 6.16504 19.6406C6.15698 19.6165 6.14893 19.5923 6.14062 19.5674Z" fill="#2C2D2B"/>
157 +<path d="M5.18848 18.7617C5.21265 18.798 5.21265 18.798 5.2373 18.835C5.22119 18.835 5.20508 18.835 5.18848 18.835C5.18848 18.8108 5.18848 18.7866 5.18848 18.7617Z" fill="#2C2E2B"/>
158 +<path d="M4.84668 18.3955C4.87085 18.4036 4.89502 18.4116 4.91992 18.4199C4.89575 18.4199 4.87158 18.4199 4.84668 18.4199C4.84668 18.4119 4.84668 18.4038 4.84668 18.3955Z" fill="#2A2B29"/>
159 +<path d="M3.43066 16.1982C3.45483 16.2345 3.45483 16.2345 3.47949 16.2715C3.46338 16.2715 3.44727 16.2715 3.43066 16.2715C3.43066 16.2473 3.43066 16.2231 3.43066 16.1982Z" fill="#2C2D29"/>
160 +<path d="M2.82031 14.6113C2.83643 14.6113 2.85254 14.6113 2.86914 14.6113C2.86108 14.6355 2.85303 14.6597 2.84473 14.6846C2.83667 14.6604 2.82861 14.6362 2.82031 14.6113Z" fill="#2E2F2D"/>
161 +<path d="M2.52734 11.4619C2.55176 11.5596 2.55176 11.5596 2.55176 11.5596L2.52734 11.4619Z" fill="#2E2F2D"/>
162 +<path d="M21.3262 11.0469C21.3423 11.063 21.3584 11.0791 21.375 11.0957C21.3508 11.1078 21.3508 11.1078 21.3262 11.1201C21.3262 11.0959 21.3262 11.0718 21.3262 11.0469Z" fill="#2D2D2C"/>
163 +<path d="M21.1064 9.85059C21.1226 9.8667 21.1387 9.88281 21.1553 9.89941C21.1311 9.9115 21.1311 9.9115 21.1064 9.92383C21.1064 9.89966 21.1064 9.87549 21.1064 9.85059Z" fill="#2C2D2A"/>
164 +<path d="M20.1787 7.43359C20.1868 7.45776 20.1948 7.48193 20.2031 7.50684C20.187 7.49878 20.1709 7.49072 20.1543 7.48242C20.1624 7.46631 20.1704 7.4502 20.1787 7.43359Z" fill="#2A2D2B"/>
165 +<path d="M4.21191 6.55469C4.23608 6.56274 4.26025 6.5708 4.28516 6.5791C4.26099 6.5791 4.23682 6.5791 4.21191 6.5791C4.21191 6.57104 4.21191 6.56299 4.21191 6.55469Z" fill="#2C2D29"/>
166 +<path d="M10.999 2.72168C11.0967 2.74609 11.0967 2.74609 11.0967 2.74609L10.999 2.72168Z" fill="#2F312D"/>
167 +<path d="M12.5859 21.5449C12.6592 21.5693 12.6592 21.5693 12.6592 21.5693L12.5859 21.5449Z" fill="#292A28"/>
168 +<path d="M10.9258 21.5449C10.9338 21.561 10.9419 21.5771 10.9502 21.5938C10.9341 21.5857 10.918 21.5776 10.9014 21.5693C10.9094 21.5613 10.9175 21.5532 10.9258 21.5449Z" fill="#2C2C2A"/>
169 +<path d="M12.8057 21.5205C12.8789 21.5449 12.8789 21.5449 12.8789 21.5449L12.8057 21.5205Z" fill="#2B2B29"/>
170 +<path d="M9.68066 21.3008C9.75391 21.3252 9.75391 21.3252 9.75391 21.3252L9.68066 21.3008Z" fill="#282927"/>
171 +<path d="M15.2227 20.9834C15.2959 21.0078 15.2959 21.0078 15.2959 21.0078L15.2227 20.9834Z" fill="#292927"/>
172 +<path d="M6.9707 20.1533C6.99487 20.1654 6.99487 20.1654 7.01953 20.1777C6.99536 20.1898 6.99536 20.1898 6.9707 20.2021C6.9707 20.186 6.9707 20.1699 6.9707 20.1533Z" fill="#2A2929"/>
173 +<path d="M6.53125 19.8848C6.54736 19.9009 6.56348 19.917 6.58008 19.9336C6.56396 19.9336 6.54785 19.9336 6.53125 19.9336C6.53125 19.9175 6.53125 19.9014 6.53125 19.8848Z" fill="#2C2C2A"/>
174 +<path d="M5.45703 18.9814C5.47314 18.9976 5.48926 19.0137 5.50586 19.0303C5.48975 19.0303 5.47363 19.0303 5.45703 19.0303C5.45703 19.0142 5.45703 18.998 5.45703 18.9814Z" fill="#2A2B28"/>
175 +<path d="M5.26172 18.7861C5.27783 18.8022 5.29395 18.8184 5.31055 18.835C5.29443 18.835 5.27832 18.835 5.26172 18.835C5.26172 18.8188 5.26172 18.8027 5.26172 18.7861Z" fill="#2A2C28"/>
176 +<path d="M4.43164 17.834C4.44775 17.8501 4.46387 17.8662 4.48047 17.8828C4.46436 17.8828 4.44824 17.8828 4.43164 17.8828C4.43164 17.8667 4.43164 17.8506 4.43164 17.834Z" fill="#282A28"/>
177 +<path d="M4.38281 17.8096C4.40698 17.8217 4.40698 17.8217 4.43164 17.834C4.40747 17.8461 4.40747 17.8461 4.38281 17.8584C4.38281 17.8423 4.38281 17.8262 4.38281 17.8096Z" fill="#2A2C2A"/>
178 +<path d="M19.7637 17.4434C19.7717 17.4595 19.7798 17.4756 19.7881 17.4922C19.772 17.4841 19.7559 17.4761 19.7393 17.4678C19.7473 17.4597 19.7554 17.4517 19.7637 17.4434Z" fill="#2B2D2B"/>
179 +<path d="M3.82129 16.9307C3.82935 16.9468 3.8374 16.9629 3.8457 16.9795C3.82959 16.9714 3.81348 16.9634 3.79688 16.9551C3.80493 16.947 3.81299 16.939 3.82129 16.9307Z" fill="#2B2C2B"/>
180 +<path d="M2.625 10.3389C2.64111 10.3389 2.65723 10.3389 2.67383 10.3389C2.66577 10.355 2.65771 10.3711 2.64941 10.3877C2.64136 10.3716 2.6333 10.3555 2.625 10.3389Z" fill="#2D2F2D"/>
181 +<path d="M3.40625 7.99512C3.42236 7.99512 3.43848 7.99512 3.45508 7.99512C3.44702 8.01123 3.43896 8.02734 3.43066 8.04395C3.42261 8.02783 3.41455 8.01172 3.40625 7.99512Z" fill="#2B2D2A"/>
182 +<path d="M18.8848 5.77344C18.9009 5.77344 18.917 5.77344 18.9336 5.77344C18.9255 5.78955 18.9175 5.80566 18.9092 5.82227C18.9011 5.80615 18.8931 5.79004 18.8848 5.77344Z" fill="#292B28"/>
183 +<path d="M6.16504 4.60156C6.23828 4.62598 6.23828 4.62598 6.23828 4.62598L6.16504 4.60156Z" fill="#2C2E2D"/>
184 +<path d="M15.9795 3.625C16.0527 3.64941 16.0527 3.64941 16.0527 3.64941L15.9795 3.625Z" fill="#2A2D2A"/>
185 +<path d="M8.11816 3.47852C8.19141 3.50293 8.19141 3.50293 8.19141 3.50293L8.11816 3.47852Z" fill="#2B2C29"/>
186 +<path d="M15.2471 3.30762C15.3203 3.33203 15.3203 3.33203 15.3203 3.33203L15.2471 3.30762Z" fill="#2A2C2A"/>
187 +<path d="M15.1982 3.25879C15.2144 3.2749 15.2305 3.29102 15.2471 3.30762C15.231 3.30762 15.2148 3.30762 15.1982 3.30762C15.1982 3.2915 15.1982 3.27539 15.1982 3.25879Z" fill="#2D302C"/>
188 +<path d="M8.58203 3.23438C8.65527 3.25879 8.65527 3.25879 8.65527 3.25879L8.58203 3.23438Z" fill="#2F312D"/>
189 +<path d="M14.6855 3.08789C14.7017 3.104 14.7178 3.12012 14.7344 3.13672C14.7183 3.13672 14.7021 3.13672 14.6855 3.13672C14.6855 3.12061 14.6855 3.10449 14.6855 3.08789Z" fill="#2B2C2A"/>
190 +<path d="M14.4658 3.03906C14.5391 3.06348 14.5391 3.06348 14.5391 3.06348L14.4658 3.03906Z" fill="#2D2E2B"/>
191 +<path d="M9.33887 3.03906C9.41211 3.06348 9.41211 3.06348 9.41211 3.06348L9.33887 3.03906Z" fill="#2C2D2A"/>
192 +<path d="M13.9043 2.86816C13.9124 2.88428 13.9204 2.90039 13.9287 2.91699C13.9126 2.90894 13.8965 2.90088 13.8799 2.89258C13.8879 2.88452 13.896 2.87646 13.9043 2.86816Z" fill="#2C2F2C"/>
193 +<path d="M10.0713 2.84375C10.0793 2.85986 10.0874 2.87598 10.0957 2.89258C10.0796 2.88452 10.0635 2.87646 10.0469 2.86816C10.0549 2.86011 10.063 2.85205 10.0713 2.84375Z" fill="#2E302D"/>
194 +<path d="M10.8281 2.72168C10.8442 2.73779 10.8604 2.75391 10.877 2.77051C10.8608 2.77051 10.8447 2.77051 10.8281 2.77051C10.8281 2.75439 10.8281 2.73828 10.8281 2.72168Z" fill="#2D2E2B"/>
195 +<path d="M12.7324 21.5449C12.7812 21.5693 12.7812 21.5693 12.7812 21.5693L12.7324 21.5449Z" fill="#2B2C2A"/>
196 +<path d="M11.1211 21.5449C11.1699 21.5693 11.1699 21.5693 11.1699 21.5693L11.1211 21.5449Z" fill="#2C2C2A"/>
197 +<path d="M13.8555 21.374C13.9043 21.3984 13.9043 21.3984 13.9043 21.3984L13.8555 21.374Z" fill="#2D2C2B"/>
198 +<path d="M14.6367 21.1787C14.6855 21.2031 14.6855 21.2031 14.6855 21.2031L14.6367 21.1787Z" fill="#2B2C29"/>
199 +<path d="M14.7832 21.1299C14.832 21.1543 14.832 21.1543 14.832 21.1543L14.7832 21.1299Z" fill="#282928"/>
200 +<path d="M8.99707 21.1055C9.0459 21.1299 9.0459 21.1299 9.0459 21.1299L8.99707 21.1055Z" fill="#2C2B29"/>
201 +<path d="M15.8086 20.7881C15.8574 20.8125 15.8574 20.8125 15.8574 20.8125L15.8086 20.7881Z" fill="#2B2A28"/>
202 +<path d="M16.1748 20.5684C16.2236 20.5928 16.2236 20.5928 16.2236 20.5928L16.1748 20.5684Z" fill="#2A2C2A"/>
203 +<path d="M7.04395 20.2021C7.09277 20.2266 7.09277 20.2266 7.09277 20.2266L7.04395 20.2021Z" fill="#2B2A28"/>
204 +<path d="M19.5928 17.6631C19.6416 17.6875 19.6416 17.6875 19.6416 17.6875L19.5928 17.6631Z" fill="#2E2D2D"/>
205 +<path d="M19.959 17.126C20.0078 17.1504 20.0078 17.1504 20.0078 17.1504L19.959 17.126Z" fill="#2A2A29"/>
206 +<path d="M3.04004 8.70312C3.08887 8.72754 3.08887 8.72754 3.08887 8.72754L3.04004 8.70312Z" fill="#2D2F2C"/>
207 +<path d="M20.4717 8.11719C20.5205 8.1416 20.5205 8.1416 20.5205 8.1416L20.4717 8.11719Z" fill="#2A2C2A"/>
208 +<path d="M18.9824 5.77344C19.0312 5.79785 19.0312 5.79785 19.0312 5.79785L18.9824 5.77344Z" fill="#2B2D2B"/>
209 +<path d="M5.21289 5.48047C5.26172 5.50488 5.26172 5.50488 5.26172 5.50488L5.21289 5.48047Z" fill="#282924"/>
210 +<path d="M5.48145 5.21191C5.53027 5.23633 5.53027 5.23633 5.53027 5.23633L5.48145 5.21191Z" fill="#2B2C29"/>
211 +<path d="M5.35938 5.1875C5.4082 5.21191 5.4082 5.21191 5.4082 5.21191L5.35938 5.1875Z" fill="#2D2E2B"/>
212 +<path d="M5.55469 4.99219C5.60352 5.0166 5.60352 5.0166 5.60352 5.0166L5.55469 4.99219Z" fill="#2D2F2D"/>
213 +<path d="M5.96973 4.79688C6.01855 4.82129 6.01855 4.82129 6.01855 4.82129L5.96973 4.79688Z" fill="#2B2D2A"/>
214 +<path d="M6.48242 4.40625C6.53125 4.43066 6.53125 4.43066 6.53125 4.43066L6.48242 4.40625Z" fill="#2C2E2A"/>
215 +<path d="M17.0293 4.21094C17.0781 4.23535 17.0781 4.23535 17.0781 4.23535L17.0293 4.21094Z" fill="#272A27"/>
216 +<path d="M16.7119 4.01562C16.7607 4.04004 16.7607 4.04004 16.7607 4.04004L16.7119 4.01562Z" fill="#282826"/>
217 +<path d="M8.11816 3.42969C8.16699 3.4541 8.16699 3.4541 8.16699 3.4541L8.11816 3.42969Z" fill="#2D2F2D"/>
218 +<path d="M8.60645 3.2832C8.65527 3.30762 8.65527 3.30762 8.65527 3.30762L8.60645 3.2832Z" fill="#2E302C"/>
219 +<path d="M8.65527 3.25879C8.7041 3.2832 8.7041 3.2832 8.7041 3.2832L8.65527 3.25879Z" fill="#2F302D"/>
220 +<path d="M15.0762 3.23438C15.125 3.25879 15.125 3.25879 15.125 3.25879L15.0762 3.23438Z" fill="#2C2D2B"/>
221 +<path d="M10.1934 2.84375C10.2422 2.86816 10.2422 2.86816 10.2422 2.86816L10.1934 2.84375Z" fill="#2E312E"/>
222 +<path d="M11.3652 2.69727C11.4141 2.72168 11.4141 2.72168 11.4141 2.72168L11.3652 2.69727Z" fill="#2F302D"/>
223 +<path d="M12.0732 2.64844C12.1221 2.67285 12.1221 2.67285 12.1221 2.67285L12.0732 2.64844Z" fill="#302F2F"/>
224 +</g>
225 +<defs>
226 +<clipPath id="clip0_22246_309363">
227 +<rect width="24" height="24" rx="4.8" fill="white"/>
228 +</clipPath>
229 +</defs>
230 +</svg>
res/pictures/address_sources/icon-zcashnames.svg new
+52
@@ -0,0 +1,52 @@
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="24" height="24" rx="4.8" fill="#0080BC"/>
3 +<rect width="24" height="24" rx="4.8" fill="black"/>
4 +<g clip-path="url(#clip0_22246_199867)">
5 +<g clip-path="url(#clip1_22246_199867)">
6 +<path d="M12.3074 3.1143C12.4678 3.1144 12.6881 3.0909 12.8159 3.20262C12.8336 3.20053 12.8514 3.19942 12.8692 3.1993C13.033 3.19795 13.2106 3.19993 13.3733 3.21827C13.7938 3.26567 14.5098 3.43849 14.9056 3.58075C15.0616 3.63681 15.2125 3.71507 15.3642 3.7824C15.5206 3.85185 15.6807 3.9111 15.835 3.98581C15.985 4.05855 16.1265 4.14724 16.2715 4.22912C16.5665 4.3956 16.8695 4.56466 17.1455 4.76152C17.2606 4.84366 17.3627 4.94189 17.4733 5.02958C17.5969 5.12739 17.7305 5.21448 17.8454 5.32256C17.9414 5.41274 18.0259 5.51544 18.1173 5.61017C18.3406 5.84136 18.5734 6.07324 18.7741 6.32459C18.8588 6.43078 18.903 6.56882 18.9958 6.66867C19.062 6.7398 19.1571 6.78636 19.2287 6.85483C19.2325 6.92577 19.2397 6.99508 19.2497 7.0654C19.2858 7.10633 19.3215 7.14766 19.3533 7.19184C19.5642 7.48348 19.6871 7.84635 19.8434 8.16899C19.8839 8.25254 19.9575 8.35265 19.9809 8.4402C19.9865 8.46131 19.9814 8.48488 19.9777 8.5058C19.9775 8.50689 19.9723 8.53302 19.9722 8.53559C19.9707 8.56194 20.0103 8.59625 20.028 8.61587C20.102 8.69802 20.1266 8.88369 20.1608 8.99022C20.2051 9.12826 20.2631 9.26368 20.3019 9.40319C20.3333 9.51599 20.3359 9.6352 20.3641 9.74856C20.3951 9.87341 20.4354 9.99564 20.4645 10.1212C20.6195 10.7875 20.7166 11.4865 20.7168 12.1714C20.7168 12.3272 20.7207 12.4852 20.7117 12.6408C20.7057 12.7456 20.6897 12.8497 20.6826 12.9545C20.672 13.1048 20.6699 13.2552 20.652 13.405C20.6348 13.5493 20.5985 13.6906 20.5737 13.8337C20.5356 14.054 20.5018 14.2773 20.4516 14.4951C20.2845 15.2193 20.0016 15.965 19.6248 16.607C19.5023 16.8157 19.3633 17.0318 19.218 17.225C19.1389 17.3299 19.0509 17.4275 18.9723 17.5327C18.8672 17.6734 18.7805 17.8281 18.6728 17.967C18.5865 18.0784 18.4787 18.1764 18.3834 18.2803C18.2596 18.4149 18.1469 18.5557 18.0172 18.6854C17.8603 18.8419 17.6917 18.9668 17.5203 19.1052C17.333 19.2563 17.1494 19.4116 16.9536 19.5521C16.7738 19.681 16.5823 19.7932 16.3931 19.9078C16.114 20.0766 15.8313 20.2456 15.5362 20.3854C15.3703 20.4639 15.2004 20.5281 15.0312 20.5984C14.8568 20.671 14.6833 20.7555 14.5041 20.8151C14.3239 20.875 14.138 20.8957 13.9535 20.937C13.7687 20.9785 13.588 21.0381 13.4023 21.0756C13.2135 21.1137 13.0239 21.1178 12.8336 21.1408C12.5438 21.176 12.3448 21.2185 12.0442 21.2185C11.8404 21.2204 11.645 21.1945 11.4427 21.1798C11.2397 21.1652 11.0393 21.1629 10.8366 21.1376C10.5899 21.1069 10.3501 21.0484 10.1083 20.9927C9.88613 20.9413 9.65864 20.901 9.44006 20.8365C9.2727 20.7872 9.11619 20.7118 8.95524 20.6456C8.79056 20.5779 8.62017 20.5181 8.46068 20.4388C8.35134 20.3844 8.2501 20.3143 8.14607 20.2507L7.65198 19.9517C7.54344 19.8861 7.40149 19.8507 7.28757 19.7784C7.16394 19.6998 7.05547 19.5499 6.94334 19.4734C6.66801 19.2855 6.4266 19.1529 6.17674 18.9261C5.95293 18.7231 5.88168 18.6146 5.70396 18.3774C5.58385 18.2348 5.44873 18.1049 5.34084 17.9522C5.20619 17.7616 5.10154 17.5438 4.97185 17.351C4.7344 16.9982 4.5526 16.627 4.40485 16.2283C4.359 16.1046 4.27854 15.9823 4.2361 15.8554C4.01441 15.1885 3.82009 14.5084 3.72249 13.8111C3.68836 13.5683 3.67864 13.3073 3.65566 13.0658C3.60872 12.5986 3.59337 12.1395 3.66414 11.6747C3.68458 11.5317 3.59671 11.2381 3.63008 11.1109C3.69621 10.8589 3.69226 10.6013 3.73731 10.3549C3.79405 10.0447 3.90124 9.72219 3.97998 9.41726C3.99304 9.36668 3.98569 9.29859 4.0003 9.24633C4.20379 8.50785 4.52777 7.82942 4.95206 7.19417C5.04991 7.0537 5.1238 6.86144 5.23163 6.73132C5.39299 6.53663 5.5326 6.30298 5.70002 6.11604C5.85275 5.94552 6.02998 5.80554 6.18923 5.62993C6.6473 5.12481 7.19376 4.70865 7.78407 4.3695C7.97244 4.26129 8.15532 4.13799 8.34598 4.03313C8.48502 3.96074 8.62255 3.90423 8.76314 3.83334C9.53805 3.4426 10.4269 3.31317 11.2762 3.18452C11.408 3.16883 11.5525 3.18246 11.6832 3.16613C11.8954 3.13963 12.0936 3.12074 12.3074 3.1143ZM11.4839 19.3813C11.5841 19.416 11.9057 19.3887 12.0238 19.3901C12.1418 19.3915 12.2676 19.4031 12.3827 19.4285C12.6336 19.3198 12.9275 19.3206 13.1941 19.2698C13.2965 19.2503 13.3923 19.1751 13.4929 19.1661C13.5364 19.1879 13.5332 19.2217 13.5685 19.2491C13.6494 19.217 13.6638 19.1337 13.7647 19.177C13.7708 19.1797 13.7769 19.1823 13.783 19.1852C13.8682 19.1738 13.9329 19.0938 14.0158 19.0698C14.0908 19.048 14.1755 19.08 14.2492 19.0465C14.3322 19.0086 14.3967 18.9346 14.4811 18.9001C14.5928 18.8543 14.7151 18.8352 14.8262 18.7865C14.9162 18.747 15.0011 18.6935 15.0887 18.6487C15.1955 18.5942 15.3087 18.5517 15.414 18.4943C15.5401 18.4254 15.6597 18.3432 15.7836 18.2701C15.8876 18.2089 16.0023 18.1562 16.1009 18.0872C16.2603 17.9756 16.4103 17.8316 16.5587 17.7046C16.7584 17.5336 16.9729 17.3665 17.1624 17.1855C17.2784 17.0746 17.3758 16.9465 17.4815 16.8262C17.6021 16.6888 17.7276 16.5569 17.8386 16.411C17.9738 16.2334 18.087 16.0336 18.204 15.8439C18.3613 15.5889 18.5248 15.3228 18.6515 15.051C18.73 14.8825 18.7903 14.7025 18.85 14.5265C18.9046 14.3651 18.9653 14.2025 19.0104 14.0381C19.0975 13.7202 19.1499 13.3949 19.2055 13.071C19.2459 12.8365 19.2996 12.6075 19.3137 12.3688C19.322 12.2246 19.292 12.0805 19.2848 11.9368C19.2679 11.597 19.2735 11.2518 19.2267 10.9141C19.2005 10.7248 19.1216 10.5533 19.0647 10.3727C18.9897 10.1343 18.9262 9.89149 18.836 9.65819C18.5359 8.8824 18.0439 8.0937 17.4951 7.47064C17.3733 7.33247 17.2651 7.17432 17.1178 7.06238C16.8844 6.88505 16.6829 6.68045 16.4582 6.4939C16.2917 6.35569 16.1008 6.23145 15.9197 6.1129C15.5126 5.84651 14.6575 5.45445 14.1864 5.32134C13.9464 5.25352 13.7039 5.21841 13.4602 5.1691C13.2492 5.12641 13.0407 5.07522 12.826 5.05418C12.6891 5.04077 12.55 5.04946 12.4125 5.04298C12.1932 5.03264 11.9617 5.01431 11.7428 5.03733C11.5186 5.0456 11.2824 5.11895 11.0608 5.15991C10.4625 5.2705 9.78254 5.44759 9.23682 5.72305C9.05565 5.81449 8.89033 5.93214 8.71482 6.0332C8.46465 6.17723 8.19671 6.32535 7.97121 6.50595C7.78825 6.6525 7.632 6.82481 7.46201 6.98501C7.3334 7.10622 7.19664 7.21097 7.07564 7.34122C6.99842 7.42434 6.91628 7.50778 6.84644 7.59714C6.69678 7.78863 6.57456 8.00622 6.43808 8.20728C6.31868 8.38316 6.19733 8.5597 6.09103 8.74395C6.01574 8.87448 5.95065 9.01087 5.8781 9.14294C5.81274 9.26194 5.73519 9.37649 5.6806 9.50085C5.55292 9.79185 5.46345 10.0986 5.36979 10.4017C5.29733 10.6363 5.21862 10.8714 5.17771 11.114C5.15431 11.2528 5.16329 11.3914 5.1536 11.5312C5.14534 11.6503 5.12291 11.7687 5.11379 11.8881C5.09604 12.1202 5.10003 12.3576 5.10864 12.5901C5.11681 12.8111 5.15395 13.0255 5.18073 13.2445C5.20817 13.4689 5.21821 13.6916 5.26385 13.914C5.29865 14.0836 5.36594 14.2532 5.41811 14.4188C5.48338 14.6259 5.53964 14.8459 5.62244 15.0463C5.6717 15.1656 5.7359 15.2778 5.78918 15.3951C5.83758 15.5017 5.8709 15.6176 5.92765 15.72C5.99503 15.8415 6.08459 15.9485 6.15871 16.0656C6.2271 16.1735 6.28028 16.2913 6.35754 16.3936C6.43923 16.5017 6.53366 16.6003 6.61688 16.7074C6.75503 16.8851 6.85492 17.0735 7.02225 17.2294C7.11712 17.3177 7.22096 17.396 7.31501 17.4853C7.49112 17.6525 7.65736 17.8319 7.84556 17.9859C7.9236 18.0496 8.02402 18.0806 8.10486 18.1401C8.19696 18.208 8.27241 18.299 8.36559 18.3655C8.46583 18.437 8.58135 18.4975 8.68839 18.5586C9.0218 18.7489 9.305 18.8845 9.6593 19.0294C9.80149 19.0875 9.93259 19.1788 10.0849 19.21C10.1163 19.2038 10.133 19.1841 10.1551 19.1628L10.1849 19.1638C10.2019 19.1979 10.1736 19.2054 10.1775 19.2336C10.1834 19.276 10.3961 19.3496 10.4379 19.3634C10.5822 19.4109 11.0862 19.5246 11.2252 19.4859L11.223 19.4726C11.1224 19.4164 10.9527 19.4141 10.839 19.3938C10.7304 19.3743 10.3183 19.2902 10.2597 19.2229C10.2599 19.1942 10.259 19.1952 10.2787 19.1745C10.3771 19.1618 10.4853 19.2247 10.5813 19.2408C10.8796 19.2907 11.1908 19.3054 11.4839 19.3813ZM4.7319 10.8369C4.72035 10.906 4.64939 11.0773 4.68924 11.1354C4.70224 11.1543 4.72351 11.1719 4.74254 11.1845C4.76511 11.1291 4.78112 10.9084 4.75384 10.8578C4.7483 10.8475 4.74114 10.8439 4.7319 10.8369ZM15.425 5.462C15.3711 5.37965 15.1294 5.25553 15.0302 5.23294C15.0811 5.30153 15.3024 5.44687 15.3915 5.46008C15.4026 5.46173 15.4137 5.46223 15.425 5.462ZM17.7365 5.60784C17.6757 5.5503 17.397 5.26317 17.3531 5.25215C17.3641 5.29689 17.6653 5.5825 17.7147 5.63026C17.7652 5.68137 17.9125 5.84307 17.9715 5.85995L17.98 5.84753C17.9613 5.77438 17.807 5.6727 17.7365 5.60784ZM7.96683 18.8992C7.9601 18.8357 7.88959 18.7792 7.84223 18.7405C7.80043 18.7065 7.74147 18.6621 7.68625 18.6596C7.75515 18.7357 7.86801 18.8698 7.96683 18.8992ZM4.67445 11.6869C4.75341 11.6052 4.68197 11.5534 4.65353 11.4572L4.65004 11.445C4.65701 11.388 4.72244 11.2524 4.7183 11.2288C4.64781 11.2264 4.62047 11.4499 4.62573 11.5113C4.65054 11.6699 4.63663 11.6066 4.63326 11.7386C4.63234 11.7938 4.62543 11.9062 4.6279 11.9556C4.62292 12.1598 4.56922 12.5528 4.59999 12.7384C4.60603 12.7712 4.61817 12.8678 4.64402 12.8823C4.6563 12.8262 4.64248 12.7551 4.63487 12.6966C4.68968 12.5724 4.67258 12.0554 4.65791 11.9017C4.66173 11.8267 4.66638 11.7615 4.67445 11.6869ZM14.258 19.8418C14.1818 19.9104 13.5296 20.0496 13.3905 20.0709C13.2194 20.0973 13.0446 20.1093 12.8724 20.1279C12.7488 20.1413 12.625 20.1623 12.5011 20.1716C12.3674 20.1815 12.1981 20.1473 12.0716 20.179C12.0442 20.1859 12.0373 20.2025 12.0263 20.226L12.0367 20.2455L12.057 20.2501C12.2258 20.247 12.3975 20.2496 12.5657 20.2419C12.6612 20.2376 12.758 20.219 12.853 20.207C13.0553 20.1818 13.2587 20.1596 13.4578 20.1127C13.6266 20.0731 13.7898 20.0071 13.9579 19.9628C14.0391 19.9416 14.2357 19.9228 14.2886 19.8705L14.2926 19.8499C14.2762 19.8411 14.278 19.8391 14.258 19.8418ZM20.3966 12.4956C20.4262 12.4104 20.4479 12.1837 20.4102 12.104C20.4081 12.0998 20.4056 12.0958 20.4032 12.0917C20.3639 12.1843 20.3748 12.3969 20.3966 12.4956ZM15.9096 5.70196C15.8496 5.6379 15.7407 5.54021 15.6543 5.51928C15.696 5.59646 15.8058 5.66916 15.8883 5.69739C15.8981 5.70074 15.9005 5.70073 15.9096 5.70196ZM7.03891 18.0806C6.95617 17.9791 6.74016 17.6735 6.62535 17.6331C6.67058 17.7304 6.92039 18.038 7.01834 18.0759C7.02781 18.0796 7.03009 18.0795 7.03891 18.0806ZM19.1397 7.24413C19.1039 7.15876 19.0347 7.07609 18.9771 7.00357L18.9519 6.9942C18.9938 7.09031 19.0437 7.19263 19.1397 7.24413ZM17.6246 17.7054C17.732 17.8053 17.4729 18.0278 17.3604 18.0193L17.4264 17.9001C17.4755 17.8428 17.5601 17.7385 17.6246 17.7054ZM17.3247 17.9822C17.2735 18.0668 17.1589 18.1277 17.0797 18.1998C16.7277 18.5197 16.2773 18.8616 15.8771 19.1145C15.5738 19.2889 15.2606 19.4352 14.9431 19.5805C14.7731 19.6582 14.6207 19.7639 14.4381 19.8296C14.4135 19.8266 14.4078 19.8223 14.3878 19.808L14.3729 19.8237L14.3781 19.8531C14.4433 19.8924 14.6815 19.8095 14.7605 19.7853C14.8913 19.7452 15.0389 19.7083 15.1651 19.6577C15.7191 19.4353 16.2149 19.0929 16.7227 18.784C16.8881 18.6693 17.0257 18.5213 17.1785 18.3916C17.2966 18.2915 17.4248 18.2052 17.5395 18.1007C17.7525 17.9069 17.9202 17.6733 18.1229 17.4682C18.2526 17.3337 18.3309 17.1671 18.4273 17.0113C18.6405 16.6669 18.8688 16.327 19.0195 15.949C19.0864 15.7895 19.1558 15.6183 19.2424 15.4683C19.3811 15.2284 19.4134 14.9604 19.5142 14.7053C19.5345 14.6539 19.5528 14.6258 19.55 14.5679C19.5276 14.5784 19.5104 14.6071 19.5019 14.6292C19.3342 15.0671 19.1837 15.5129 18.9696 15.9311C18.915 16.0341 18.809 16.1334 18.7529 16.2316C18.4907 16.6906 18.1667 17.1105 17.7965 17.4874C17.7352 17.5498 17.6846 17.6234 17.62 17.6871C17.5312 17.7238 17.483 17.7845 17.419 17.856C17.3822 17.8994 17.3518 17.9318 17.3247 17.9822ZM18.7976 8.97515C18.7863 8.91631 18.7674 8.86411 18.7289 8.81718L18.7063 8.809C18.7207 8.8612 18.7392 8.9447 18.79 8.97194C18.7926 8.97323 18.7951 8.97408 18.7976 8.97515ZM12.4058 3.85636C12.4723 3.86807 12.5766 3.88729 12.644 3.88179C12.8061 3.92704 12.8806 3.91786 13.0467 3.93458C13.1882 3.90871 13.7845 4.06274 13.9599 4.10026C14.0038 4.10964 14.1211 4.15687 14.1545 4.152L14.164 4.13425C14.0937 4.02927 13.1828 3.88456 13.0384 3.88997C12.944 3.86861 12.8223 3.82171 12.7316 3.84809C12.6057 3.84515 12.5307 3.84697 12.4058 3.85636ZM12.7098 4.65427C12.6749 4.65649 12.6395 4.65476 12.6089 4.67336C12.6499 4.69348 12.7161 4.69666 12.7609 4.68892C12.7898 4.68973 12.8063 4.69565 12.8299 4.67815C12.7998 4.65548 12.7471 4.65174 12.7098 4.65427ZM5.05278 14.8708C5.06289 15.0244 5.13141 15.1177 5.18098 15.2569C5.19995 15.3103 5.20705 15.3697 5.21806 15.4251L5.23244 15.4287L5.2341 15.4196C5.27897 15.2968 5.2371 15.1606 5.18252 15.0457C5.1553 14.9883 5.10938 14.8987 5.05278 14.8708ZM18.0835 5.99141C18.123 6.03932 18.5465 6.50801 18.5665 6.51697C18.491 6.37537 18.2302 6.04439 18.0835 5.99141ZM5.3649 7.04233C5.30777 7.08993 5.01854 7.52321 5.01151 7.59023L5.02888 7.61379C5.11013 7.49772 5.1751 7.37172 5.25739 7.25608C5.28567 7.21635 5.36721 7.13835 5.38218 7.09648C5.38556 7.08705 5.38418 7.07656 5.38517 7.06659L5.3649 7.04233ZM11.0213 4.66739C10.8409 4.69248 10.6804 4.75189 10.5107 4.78232C10.1002 4.85598 9.73013 4.98203 9.34887 5.14649C9.28035 5.17604 9.16069 5.1729 9.08969 5.20539C8.97057 5.27033 8.83371 5.33533 8.71835 5.40477C8.5458 5.50861 8.30309 5.60455 8.16112 5.74405C8.01226 5.91162 7.77208 5.98833 7.6091 6.11455C7.48861 6.20786 7.26316 6.38706 7.16329 6.49767C6.86349 6.82969 6.53865 7.13907 6.30125 7.5223C6.25882 7.59076 6.15183 7.65652 6.15703 7.74026L6.17048 7.7475C6.27746 7.60244 6.39206 7.483 6.50102 7.3471C6.67231 7.13346 6.81226 6.93445 7.02478 6.75424C7.08173 6.70595 7.15901 6.68541 7.20273 6.65073C7.26419 6.60198 7.35484 6.48412 7.4177 6.43534C7.4624 6.40064 7.53964 6.37686 7.59485 6.33525C7.71921 6.24154 7.99575 5.94354 8.11337 5.87357C8.30692 5.75842 8.60793 5.65069 8.81647 5.53403C8.95727 5.45526 9.0159 5.36486 9.13184 5.29387C9.12087 5.30059 9.39686 5.2365 9.43129 5.22647C9.60857 5.17484 9.77083 5.08893 9.93871 5.02989C10.0926 4.97578 10.3101 4.9311 10.4721 4.88529C10.716 4.81635 10.9969 4.80127 11.2363 4.71659C11.2502 4.71167 11.2419 4.71402 11.2571 4.71066C11.432 4.68787 11.7549 4.68896 11.9465 4.67953C12.0418 4.68954 12.4054 4.69405 12.4841 4.6817C12.2676 4.60003 11.8056 4.59859 11.5889 4.65401C11.4866 4.64108 11.1258 4.65141 11.0213 4.66739ZM5.33506 15.6157C5.33241 15.6508 5.33793 15.6799 5.34985 15.7128C5.38395 15.8067 5.44071 15.8972 5.48373 15.988C5.54367 16.1148 5.59232 16.2587 5.66559 16.3776C5.75462 16.522 5.8723 16.6472 5.96989 16.7857C6.14713 17.0371 6.30145 17.2971 6.50477 17.5307C6.53116 17.561 6.56093 17.5857 6.59506 17.6069C6.57727 17.5188 6.55705 17.4343 6.50624 17.3587C6.45281 17.2792 6.35959 17.254 6.29735 17.1862C6.18873 17.0678 6.1078 16.9165 6.01698 16.7839C5.94283 16.6758 5.85855 16.5719 5.79195 16.459C5.73834 16.3681 5.7241 16.2666 5.67915 16.1735C5.63373 16.0794 5.54739 16.0059 5.49139 15.9182C5.43161 15.8245 5.42507 15.6866 5.33506 15.6157ZM4.98106 9.57078C4.9745 9.58884 4.97092 9.60253 4.96914 9.6216C4.95708 9.7501 4.7542 10.1999 4.79203 10.2865L4.80605 10.2938C4.85347 10.2253 4.94621 9.88425 4.96833 9.78987C4.98467 9.72016 4.96039 9.63863 5.00218 9.57769C4.98698 9.56802 4.99435 9.56876 4.98106 9.57078ZM4.67558 13.3773C4.67571 13.598 4.79877 14.3005 4.90725 14.4837C4.90161 14.3741 4.74508 13.4581 4.71048 13.4009L4.67558 13.3773Z" fill="#FEFEFE"/>
7 +<path d="M8.59489 8.01074C8.64751 8.0185 8.69613 8.04794 8.74405 8.07035C9.03007 8.20418 9.22015 8.35766 9.4607 8.55799C9.6376 8.70529 9.79297 8.88654 9.95817 9.04737C10.0697 9.15595 10.192 9.2489 10.3067 9.35317C10.4212 9.4572 10.5267 9.57142 10.6397 9.67715L12.9444 11.7915C13.1031 11.9361 13.281 12.0641 13.4435 12.2056C13.5343 12.2847 13.6172 12.3724 13.7069 12.4527C13.8457 12.5769 13.9967 12.6895 14.1381 12.8112C14.2546 12.9114 14.3617 13.0219 14.4781 13.1219C14.6469 13.2671 14.835 13.3992 14.9936 13.5541C14.9722 13.4155 14.9823 13.2633 14.9822 13.123C14.9819 12.8319 14.9885 12.5385 14.9773 12.2477C14.9699 12.0553 14.9109 11.8729 14.8929 11.6826C14.8756 11.5008 14.8879 11.3138 14.8811 11.131C14.8753 10.9758 14.8519 10.822 14.843 10.6671C14.825 10.3554 14.8378 10.0357 14.8377 9.72313C14.8375 9.46533 14.8185 9.19008 14.851 8.93485C14.8652 8.82284 14.9502 8.47644 15.0331 8.39657C15.057 8.37365 15.0713 8.37438 15.1028 8.37535C15.1469 8.3971 15.1709 8.43362 15.2002 8.47152C15.2437 8.36196 15.2998 8.26777 15.384 8.18533C15.4442 8.21321 15.4985 8.24343 15.5535 8.28031C15.6074 8.25548 15.6404 8.23282 15.7 8.25307C15.7226 8.26076 15.7394 8.266 15.7631 8.26765C15.8111 8.24505 15.8256 8.19056 15.8625 8.15511C15.8824 8.13597 15.8875 8.13703 15.9135 8.13729C16.0631 8.22513 16.1326 8.45432 16.2499 8.58207L16.2605 8.59343C16.33 8.95134 16.3209 9.33223 16.3281 9.6954C16.3326 9.92515 16.3598 10.1521 16.3688 10.3812C16.3772 10.5928 16.369 10.8049 16.3811 11.0166C16.3892 11.1562 16.4115 11.2945 16.4323 11.4327C16.5567 11.4156 16.687 11.4365 16.8082 11.4156C16.8632 11.4061 16.9136 11.376 16.9679 11.3632C17.1017 11.3319 17.7386 11.2934 17.839 11.3556C17.8464 11.3753 17.8537 11.3921 17.8647 11.4102C17.8834 11.4023 17.8798 11.4033 17.8901 11.3878C17.921 11.3405 17.9461 11.2889 18.0054 11.2764C18.0784 11.2887 18.1415 11.4041 18.1829 11.4614C18.1768 11.6592 18.2074 11.8203 18.2512 12.0101C18.2704 12.0933 18.2681 12.1814 18.2699 12.2665C18.2766 12.58 18.2168 12.5993 18.059 12.8525C18.0153 12.8693 17.9741 12.8812 17.9272 12.884C17.7402 12.895 17.5451 12.873 17.3574 12.8678C17.0673 12.8621 16.777 12.86 16.4868 12.8618C16.4937 12.9425 16.4897 13.0243 16.4935 13.1054C16.5176 13.6198 16.5992 15.3908 16.4146 15.7805C16.3738 15.8667 16.2344 16.0227 16.1419 16.0522C16.0989 16.0659 16.0717 16.0515 16.0347 16.031L16.0226 16.0244L16.0003 16.0357C15.9405 16.177 15.8198 16.1243 15.7245 16.2152C15.7217 16.2178 15.7192 16.2206 15.7165 16.2232C15.6952 16.251 15.6736 16.2812 15.6471 16.3044C15.6044 16.3419 15.5435 16.3482 15.4886 16.3452C15.3817 16.3394 15.2579 16.2971 15.1659 16.243C15.0392 16.1685 14.9202 16.0753 14.8007 15.9902C14.6936 15.9138 14.582 15.8416 14.4804 15.7578C14.3887 15.6821 14.3048 15.5974 14.2145 15.5202C13.9961 15.3334 13.7773 15.15 13.5619 14.9596L12.1967 13.7293C12.0961 13.6375 11.9875 13.5547 11.8878 13.4618C11.736 13.3203 11.5956 13.1663 11.4448 13.0235C11.2391 12.8286 11.0067 12.6632 10.798 12.4718C10.6834 12.3666 10.5775 12.251 10.467 12.1414C10.182 11.8586 9.90528 11.5764 9.6397 11.2755C9.64465 11.3884 9.67012 11.5 9.67886 11.6128C9.69 11.7565 9.67917 11.9121 9.70525 12.053C9.71829 12.1234 9.76964 12.1808 9.77919 12.2532C9.79711 12.389 9.78646 12.5297 9.80076 12.6664C9.81293 12.7828 9.8374 12.8981 9.85222 13.0143C9.88191 13.2467 9.90394 13.4855 9.92563 13.719C9.93955 13.8689 9.93707 14.0202 9.95032 14.1702C9.97111 14.4059 10.008 14.6419 10.0192 14.8782C10.0349 15.2116 10.0138 15.5027 10.0816 15.8371C10.0137 15.8972 9.9387 15.9498 9.86935 16.0085C9.77471 16.0885 9.68519 16.1989 9.57289 16.2531C9.5095 16.2174 9.46614 16.0427 9.43606 15.9749C9.39032 16.0731 9.37257 16.1825 9.32635 16.2799C9.30756 16.3195 9.2914 16.3483 9.24952 16.363C9.21364 16.3409 9.21394 16.325 9.19927 16.2865L9.17835 16.2782C9.0371 16.309 8.9237 16.4528 8.82879 16.5537C8.66348 16.4932 8.44239 16.4554 8.33737 16.2972C8.31873 16.2691 8.31371 16.2568 8.32039 16.2247C8.33439 16.2174 8.35451 16.2095 8.36613 16.1991C8.40151 16.1676 8.28505 15.9989 8.26766 15.9502C8.25304 15.9093 8.25133 15.8649 8.24725 15.8219C8.20852 15.4145 8.18204 15.0058 8.14443 14.5982C8.13161 14.4594 8.10636 14.3219 8.09301 14.183C8.07913 14.0387 8.08366 13.8951 8.07789 13.7507C8.07363 13.6443 8.06091 13.5403 8.06045 13.4334C7.67869 13.4318 6.87585 13.5587 6.54518 13.4463C6.49567 13.4295 6.4525 13.4055 6.43048 13.3558C6.37123 13.2221 6.38023 12.9855 6.43418 12.851C6.30774 12.6961 6.37763 12.629 6.36215 12.4554C6.35538 12.3797 6.3071 12.3314 6.25342 12.2842C6.27902 12.2076 6.3136 12.1347 6.36182 12.0695C6.40494 12.0112 6.45939 11.9683 6.52819 11.9447C6.67575 11.8941 6.83904 11.8999 6.99289 11.8911C7.311 11.8729 7.64628 11.8692 7.96039 11.8198C7.95695 11.8071 7.95392 11.7943 7.95074 11.7815C7.91399 11.6331 7.92828 11.4649 7.91121 11.3121C7.89556 11.172 7.85727 11.0363 7.8372 10.8972C7.79488 10.6038 7.81979 10.2815 7.81948 9.98407C7.81913 9.64933 7.80057 9.30277 7.82733 8.96979C7.83405 8.88607 7.84163 8.77186 7.88676 8.69819C7.95351 8.58922 8.21753 8.43257 8.32539 8.33294C8.42863 8.23758 8.51066 8.12247 8.59489 8.01074ZM12.89 12.0527C12.8403 11.963 12.663 11.7686 12.565 11.7357C12.5628 11.735 12.5604 11.7345 12.5581 11.7339C12.6239 11.8131 12.7993 12.0212 12.89 12.0527ZM8.9529 11.7649C8.96703 11.8834 8.96596 11.9274 9.0051 12.0384C9.01515 11.9438 9.00126 11.8635 8.97819 11.7733C8.98739 11.716 8.99229 11.4942 8.93952 11.4657C8.89696 11.5233 8.93443 11.6981 8.9529 11.7649ZM11.2432 11.3604C11.181 11.2708 11.0597 11.1236 10.9429 11.107C10.9388 11.1064 10.9347 11.1061 10.9307 11.1057C10.958 11.1467 11.1978 11.3401 11.2432 11.3604ZM9.39297 15.3673C9.39961 15.2648 9.41105 15.1122 9.36616 15.0176C9.35536 15.1145 9.32936 15.2862 9.39297 15.3673ZM9.23952 13.7113C9.24332 13.6407 9.25284 13.5653 9.24026 13.4954L9.22139 13.4766C9.21906 13.5386 9.20062 13.6637 9.23952 13.7113Z" fill="#FEFEFE"/>
8 +<path d="M12.5581 11.7334C12.5604 11.734 12.5628 11.7344 12.565 11.7352C12.663 11.768 12.8402 11.9625 12.89 12.0522C12.7993 12.0206 12.6238 11.8126 12.5581 11.7334Z" fill="#030203" fill-opacity="0.290196"/>
9 +<path d="M8.95237 11.765C8.93389 11.6982 8.89642 11.5234 8.93899 11.4658C8.99176 11.4944 8.98685 11.7161 8.97765 11.7735L8.95237 11.765Z" fill="#030203" fill-opacity="0.266667"/>
10 +<path d="M10.9307 11.1055C10.9347 11.1059 10.9388 11.1062 10.9429 11.1068C11.0597 11.1234 11.181 11.2706 11.2432 11.3601C11.1978 11.3399 10.958 11.1465 10.9307 11.1055Z" fill="#868587" fill-opacity="0.121569"/>
11 +<path d="M9.36606 15.0176C9.41094 15.1121 9.39951 15.2648 9.39287 15.3672C9.32926 15.2861 9.35526 15.1144 9.36606 15.0176Z" fill="#1E1621" fill-opacity="0.0941176"/>
12 +<path d="M8.95264 11.7646L8.97792 11.7731C9.00099 11.8633 9.01488 11.9436 9.00483 12.0382C8.96569 11.9272 8.96676 11.8832 8.95264 11.7646Z" fill="#030203" fill-opacity="0.419608"/>
13 +<path d="M9.22124 13.4766L9.24011 13.4954C9.25269 13.5653 9.24317 13.6406 9.23937 13.7113C9.20047 13.6637 9.21891 13.5385 9.22124 13.4766Z" fill="#FEFEFE" fill-opacity="0.615686"/>
14 +<path d="M18.0248 17.2979C18.0447 17.3152 18.0422 17.3172 18.0453 17.3438C18.028 17.3865 17.9979 17.4112 17.965 17.442C17.9557 17.4408 17.9478 17.4333 17.9402 17.4281C17.9399 17.4238 17.9395 17.4194 17.9395 17.4149C17.9395 17.3702 17.9948 17.3266 18.0248 17.2979Z" fill="#FEFEFE"/>
15 +<path d="M5.33561 15.6152C5.42562 15.6861 5.43217 15.824 5.49194 15.9177C5.54795 16.0054 5.63428 16.079 5.6797 16.173C5.72465 16.2661 5.73889 16.3676 5.7925 16.4585C5.8591 16.5715 5.94339 16.6753 6.01754 16.7834C6.10836 16.9161 6.18929 17.0673 6.29791 17.1857C6.36014 17.2535 6.45337 17.2787 6.50679 17.3582C6.55761 17.4339 6.57783 17.5184 6.59561 17.6065C6.56149 17.5852 6.53172 17.5605 6.50533 17.5302C6.302 17.2966 6.14769 17.0366 5.97045 16.7852C5.87286 16.6468 5.75518 16.5216 5.66614 16.3771C5.59288 16.2582 5.54422 16.1143 5.48428 15.9875C5.44126 15.8967 5.3845 15.8062 5.3504 15.7123C5.33849 15.6795 5.33296 15.6503 5.33561 15.6152Z" fill="#030203" fill-opacity="0.0392157"/>
16 +<path d="M14.2585 19.8419C14.2785 19.8393 14.2767 19.8412 14.2931 19.85L14.2891 19.8707C14.2362 19.923 14.0396 19.9417 13.9585 19.9629C13.7903 20.0072 13.6271 20.0732 13.4583 20.1129C13.2593 20.1598 13.0559 20.182 12.8535 20.2072C12.7585 20.2191 12.6617 20.2377 12.5662 20.242C12.398 20.2498 12.2263 20.2471 12.0576 20.2503L12.0372 20.2456L12.0269 20.2261C12.0378 20.2026 12.0447 20.186 12.0721 20.1791C12.1986 20.1475 12.368 20.1816 12.5016 20.1717C12.6256 20.1624 12.7493 20.1414 12.873 20.128C13.0451 20.1094 13.2199 20.0975 13.391 20.0711C13.5302 20.0497 14.1823 19.9105 14.2585 19.8419Z" fill="#030203" fill-opacity="0.0431373"/>
17 +<path d="M13.0391 3.89077C13.1834 3.88535 14.0944 4.03007 14.1646 4.13505L14.1551 4.1528C14.1218 4.15767 14.0045 4.11044 13.9606 4.10106C13.7851 4.06354 13.1888 3.90951 13.0473 3.93538C13.0465 3.92392 13.0415 3.9029 13.0391 3.89077Z" fill="#030203" fill-opacity="0.368627"/>
18 +<path d="M4.62819 11.9552L4.6582 11.9014C4.67287 12.055 4.68997 12.572 4.63516 12.6962L4.60028 12.738C4.56951 12.5525 4.62321 12.1594 4.62819 11.9552Z" fill="#030203" fill-opacity="0.0509804"/>
19 +<path d="M11.5889 4.65436C11.8055 4.59894 12.2676 4.60038 12.4841 4.68205C12.4054 4.6944 12.0418 4.68989 11.9465 4.67988C11.8385 4.65611 11.6854 4.68551 11.5889 4.65436Z" fill="#FEFEFE" fill-opacity="0.01"/>
20 +<path d="M17.4268 17.8998C17.4759 17.8425 17.5605 17.7381 17.625 17.7051C17.7324 17.805 17.4733 18.0275 17.3608 18.019L17.4268 17.8998Z" fill="#FEFEFE"/>
21 +<path d="M4.98133 9.57059C4.99461 9.56858 4.98725 9.56784 5.00244 9.5775C4.96065 9.63845 4.98493 9.71998 4.96859 9.78968C4.94647 9.88407 4.85374 10.2251 4.80632 10.2936L4.79229 10.2864C4.75446 10.1997 4.95735 9.74991 4.9694 9.62142C4.97118 9.60234 4.97476 9.58865 4.98133 9.57059Z" fill="#030203" fill-opacity="0.0313725"/>
22 +<path d="M11.0215 4.66766C11.126 4.65168 11.4868 4.64135 11.5891 4.65428C11.6857 4.68544 11.8387 4.65604 11.9468 4.6798C11.7551 4.68923 11.4322 4.68815 11.2573 4.71093L11.2757 4.69532L11.2733 4.68121C11.2169 4.66135 11.1245 4.69146 11.0215 4.66766Z" fill="#030203" fill-opacity="0.701961"/>
23 +<path d="M5.36511 7.04199L5.38537 7.06626C5.38438 7.07623 5.38577 7.08672 5.38239 7.09615C5.36742 7.13802 5.28587 7.21601 5.2576 7.25575C5.17531 7.37139 5.11034 7.49739 5.02909 7.61346L5.01172 7.58989C5.01875 7.52287 5.30798 7.08959 5.36511 7.04199Z" fill="#1E1621" fill-opacity="0.0313725"/>
24 +<path d="M12.732 3.84768C12.8227 3.8213 12.9445 3.8682 13.0389 3.88957C13.0413 3.9017 13.0463 3.92272 13.0472 3.93417C12.8811 3.91745 12.8065 3.92663 12.6445 3.88138L12.6328 3.86932C12.6678 3.86288 12.6975 3.85623 12.732 3.84768Z" fill="#FEFEFE" fill-opacity="0.0705882"/>
25 +<path d="M18.084 5.99219C18.2307 6.04516 18.4915 6.37614 18.5669 6.51774C18.547 6.50878 18.1235 6.04009 18.084 5.99219Z" fill="#030203" fill-opacity="0.388235"/>
26 +<path d="M4.73243 10.8369C4.74166 10.8439 4.74883 10.8475 4.75436 10.8578C4.78164 10.9084 4.76563 11.1291 4.74306 11.1845C4.72403 11.1719 4.70276 11.1543 4.68977 11.1354C4.64991 11.0773 4.72087 10.906 4.73243 10.8369Z" fill="#FEFEFE" fill-opacity="0.0117647"/>
27 +<path d="M15.0303 5.2334C15.1295 5.25598 15.3712 5.38011 15.425 5.46246C15.4138 5.46268 15.4027 5.46218 15.3916 5.46053C15.3024 5.44732 15.0811 5.30198 15.0303 5.2334Z" fill="#030203" fill-opacity="0.0823529"/>
28 +<path d="M7.68652 18.6602C7.74174 18.6627 7.80069 18.7071 7.8425 18.7411C7.88986 18.7798 7.96037 18.8362 7.9671 18.8997C7.86828 18.8704 7.75542 18.7363 7.68652 18.6602Z" fill="#030203" fill-opacity="0.01"/>
29 +<path d="M17.7151 5.63006C17.6657 5.5823 17.3645 5.29669 17.3535 5.25195C17.3973 5.26297 17.676 5.5501 17.7369 5.60764L17.7151 5.63006Z" fill="#030203" fill-opacity="0.482353"/>
30 +<path d="M17.7367 5.6084C17.8071 5.67326 17.9614 5.77493 17.9802 5.84809L17.9716 5.86051C17.9126 5.84363 17.7653 5.68193 17.7148 5.63081L17.7367 5.6084Z" fill="#030203" fill-opacity="0.270588"/>
31 +<path d="M20.4037 12.0918C20.4061 12.0959 20.4086 12.0999 20.4107 12.1041C20.4484 12.1838 20.4266 12.4105 20.397 12.4957C20.3753 12.397 20.3643 12.1844 20.4037 12.0918Z" fill="#FEFEFE" fill-opacity="0.196078"/>
32 +<path d="M15.6543 5.51953C15.7407 5.54046 15.8497 5.63814 15.9096 5.70221C15.9005 5.70098 15.8982 5.70099 15.8884 5.69763C15.8059 5.6694 15.6961 5.59671 15.6543 5.51953Z" fill="#1E1621" fill-opacity="0.0823529"/>
33 +<path d="M18.9521 6.99414L18.9773 7.00351C19.035 7.07604 19.1041 7.15871 19.1399 7.24408C19.0439 7.19257 18.994 7.09025 18.9521 6.99414Z" fill="#FEFEFE" fill-opacity="0.313725"/>
34 +<path d="M17.4194 17.8554C17.4834 17.7839 17.5316 17.7232 17.6204 17.6865L17.625 17.7048C17.5605 17.7379 17.4759 17.8422 17.4268 17.8995L17.4194 17.8554Z" fill="#1E1621" fill-opacity="0.168627"/>
35 +<path d="M4.62612 11.511C4.62086 11.4497 4.6482 11.2262 4.71869 11.2285C4.72283 11.2521 4.65741 11.3877 4.65043 11.4448L4.62612 11.511Z" fill="#FEFEFE" fill-opacity="0.403922"/>
36 +<path d="M12.7102 4.65498C12.7476 4.65245 12.8003 4.65619 12.8304 4.67887C12.8067 4.69637 12.7902 4.69044 12.7613 4.68963C12.7165 4.69737 12.6504 4.69419 12.6094 4.67407C12.64 4.65547 12.6754 4.6572 12.7102 4.65498Z" fill="#FEFEFE" fill-opacity="0.25098"/>
37 +<path d="M12.4062 3.85624C12.5312 3.84685 12.6062 3.84503 12.7321 3.84797C12.6976 3.85651 12.6678 3.86316 12.6328 3.86961L12.6445 3.88166C12.5771 3.88717 12.4728 3.86795 12.4062 3.85624Z" fill="#FEFEFE" fill-opacity="0.411765"/>
38 +<path d="M4.67501 11.6875C4.66694 11.7621 4.66229 11.8273 4.65847 11.9023L4.62846 11.9562C4.62599 11.9068 4.6329 11.7944 4.63382 11.7392C4.64848 11.7243 4.66217 11.7045 4.67501 11.6875Z" fill="#FEFEFE" fill-opacity="0.254902"/>
39 +<path d="M17.4195 17.8564L17.4269 17.9005L17.3609 18.0197L17.3392 18.0142L17.3252 17.9826C17.3523 17.9322 17.3826 17.8998 17.4195 17.8564Z" fill="#030203" fill-opacity="0.027451"/>
40 +<path d="M4.6001 12.739L4.63498 12.6973C4.64258 12.7558 4.6564 12.8269 4.64412 12.883C4.61828 12.8685 4.60614 12.7719 4.6001 12.739Z" fill="#FEFEFE" fill-opacity="0.145098"/>
41 +<path d="M18.7065 8.80957L18.7292 8.81775C18.7677 8.86468 18.7866 8.91688 18.7979 8.97572C18.7953 8.97465 18.7928 8.9738 18.7903 8.97251C18.7395 8.94527 18.721 8.86177 18.7065 8.80957Z" fill="#FEFEFE" fill-opacity="0.0980392"/>
42 +</g>
43 +</g>
44 +<defs>
45 +<clipPath id="clip0_22246_199867">
46 +<rect width="18" height="19.2414" fill="white" transform="translate(3 2.5)"/>
47 +</clipPath>
48 +<clipPath id="clip1_22246_199867">
49 +<rect width="18" height="19.2414" fill="white" transform="translate(3 2.5)"/>
50 +</clipPath>
51 +</defs>
52 +</svg>
res/pictures/address_sources/mastodon.svg new
+11
@@ -0,0 +1,11 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="white"/>
3 +<rect width="100" height="100" rx="20" fill="url(#paint0_linear_1796_8080)"/>
4 +<path d="M48.9203 12C61.2483 12 66.5702 12.7569 67.975 12.9648C76.3007 14.1954 83.3244 20.5461 84.4086 28.6045C84.9984 34.5903 84.4871 44.3329 84.4838 45.9316C84.4837 46.4039 84.4153 50.6972 84.3881 51.1504C83.6583 62.6651 76.4779 67.2124 68.933 68.6611C68.8308 68.6918 68.7114 68.7125 68.5922 68.7363C63.8088 69.6704 58.6844 69.9193 53.8226 70.0557C52.66 70.0864 51.5007 70.0859 50.3383 70.0859C45.5042 70.0872 40.6867 69.5154 35.9877 68.3818C35.9629 68.3755 35.9365 68.3744 35.9115 68.3799C35.8865 68.3853 35.863 68.3969 35.8431 68.4131C35.8232 68.4293 35.8076 68.4503 35.7972 68.4736C35.7869 68.4972 35.7822 68.5232 35.7836 68.5488C35.9164 70.0785 36.2484 71.5848 36.7719 73.0283C37.4231 74.6988 39.6979 78.7099 48.1527 78.71C53.0659 78.719 57.9628 78.1472 62.7416 77.0059C62.7658 77.0003 62.7916 77.0004 62.8158 77.0059C62.84 77.0113 62.8627 77.0219 62.8822 77.0371C62.9018 77.0525 62.918 77.0725 62.9291 77.0947C62.94 77.117 62.9462 77.1413 62.9467 77.166V82.8184C62.9457 82.8449 62.9385 82.871 62.9261 82.8945C62.9138 82.918 62.8958 82.9382 62.8744 82.9541C61.3129 84.0858 59.1891 84.7306 57.3754 85.3066C56.5754 85.5589 55.7651 85.7799 54.9447 85.9707C47.4862 87.6693 39.7015 87.2592 32.4633 84.7852C25.7024 82.4126 18.8017 76.5964 17.097 69.6084C16.1867 65.8248 15.5448 61.9816 15.1771 58.1074C14.6453 52.275 14.6009 46.4286 14.3724 40.5723C14.2122 36.4887 14.3048 32.037 15.1674 28.0215C16.9607 19.8676 24.3518 14.1613 32.4457 12.9648C33.8504 12.7569 36.496 12 48.8275 12H48.9203ZM60.8353 24.2715C56.7818 24.2716 53.7139 25.8332 51.6683 28.9521L49.6908 32.2656L47.7162 28.9521C45.6705 25.8331 42.602 24.2715 38.5414 24.2715C35.0333 24.2716 32.2069 25.506 30.0521 27.9092C27.9635 30.318 26.9193 33.5677 26.9193 37.6582V57.6777H34.849V38.248C34.849 34.1574 36.5711 32.0713 40.0179 32.0713C43.8297 32.0713 45.7426 34.5394 45.7426 39.4141V50.0488H53.6283V39.4141C53.6283 34.5395 55.5374 32.0714 59.349 32.0713C62.8164 32.0713 64.5219 34.1574 64.5219 38.248V57.6777H72.4554V37.6582C72.4578 33.5655 71.415 30.3157 69.3285 27.9092C67.167 25.5059 64.3403 24.2715 60.8353 24.2715Z" fill="white"/>
5 +<defs>
6 +<linearGradient id="paint0_linear_1796_8080" x1="50" y1="0" x2="50" y2="100" gradientUnits="userSpaceOnUse">
7 +<stop stop-color="#6364FF"/>
8 +<stop offset="1" stop-color="#563ACC"/>
9 +</linearGradient>
10 +</defs>
11 +</svg>
res/pictures/address_sources/nostr.svg new
+4
@@ -0,0 +1,4 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="white"/>
3 +<path d="M39.1444 22.0044C45.185 17.9363 51.8829 16.7852 59.0741 17.0318C71.0322 17.4427 80.6067 25.6207 82.4559 37.4966C82.949 40.5785 83.1961 43.7427 83.2372 46.8657C83.3194 57.1388 83.2782 67.412 83.2782 77.6851C83.2782 81.2601 82.5386 82.0005 78.9637 82.0005H54.1024C51.0206 82.0005 50.3622 81.3428 50.3622 78.3023C50.3211 71.6041 50.2805 64.9055 51.6776 58.2896C52.9104 52.5777 55.7457 50.1121 61.6219 49.9888C65.4025 49.9066 69.1422 50.1122 72.9227 49.9068C74.4843 49.8246 76.1691 49.2901 77.5663 48.5093C79.3333 47.5641 79.4562 45.7558 78.7987 43.9888C78.1823 42.3456 76.8678 42.3452 75.3065 42.3452C72.5122 42.3863 69.6759 42.1813 66.9637 41.606C64.3752 41.0717 62.6085 39.3044 62.5262 36.4282C62.403 32.8532 60.3484 30.6749 57.2255 29.3189C53.3627 27.634 49.2938 27.2638 45.1844 27.4693C42.1848 27.6336 39.226 28.0865 36.2675 28.5796C32.3227 29.2372 30.1037 31.5381 30.4735 35.4829C30.9666 40.7017 31.9529 45.8796 33.0624 50.9751C35.0348 60.0155 36.72 69.0563 36.5145 78.3433C36.4734 81.1783 35.6923 82.0004 32.8573 82.0005H21.1454C18.5155 82.0005 17.6113 81.0962 17.6112 78.3843C17.529 68.8508 17.5292 59.3171 17.5292 49.8247V21.8814C17.5292 18.635 18.2692 17.854 21.4745 17.854C25.0906 17.854 28.7481 17.8951 32.3641 17.854C34.2131 17.813 35.2814 18.5937 35.6512 20.4839C36.0211 22.4152 37.4596 23.1139 39.1444 22.0044ZM53.0341 33.2222C55.7459 33.2224 58.1706 35.606 58.1708 38.3179C58.2119 41.0711 55.8276 43.4137 52.91 43.3726C50.1159 43.3313 47.8973 41.0711 47.9384 38.2769C47.9796 35.5237 50.2809 33.2222 53.0341 33.2222Z" fill="#9544DC"/>
4 +</svg>
res/pictures/address_sources/openalias.svg new
+13
@@ -0,0 +1,13 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="black"/>
3 +<path d="M53.7579 9.88531C73.8474 12.0444 89.5589 29.0155 89.4991 49.7047C89.4991 71.7734 71.6287 89.7044 49.5001 89.7047C27.4313 89.7047 9.50012 71.8335 9.50012 49.7047C9.50036 29.0754 25.152 12.0443 45.3615 9.88531L43.3224 16.8414C27.8505 19.78 16.1566 33.3932 16.1564 49.7047C16.1564 68.1154 31.149 83.108 49.5597 83.108C67.9704 83.1079 82.963 68.1153 82.963 49.7047C82.9628 33.3931 71.2681 19.7799 55.796 16.8414L53.7579 9.88531ZM61.0743 48.2662C61.7941 50.9646 64.2528 52.8835 68.8702 51.6841L79.1847 46.4664L52.6779 73.4527L49.5001 5.6275L61.0743 48.2662ZM46.381 73.3931L19.8751 46.4664L30.1896 51.6841C34.8072 52.8835 37.2658 50.9644 37.9855 48.3257L49.5001 5.6275L46.381 73.3931Z" fill="#00C3FF"/>
4 +<path d="M53.7579 9.88531C73.8474 12.0444 89.5589 29.0155 89.4991 49.7047C89.4991 71.7734 71.6287 89.7044 49.5001 89.7047C27.4313 89.7047 9.50012 71.8335 9.50012 49.7047C9.50036 29.0754 25.152 12.0443 45.3615 9.88531L43.3224 16.8414C27.8505 19.78 16.1566 33.3932 16.1564 49.7047C16.1564 68.1154 31.149 83.108 49.5597 83.108C67.9704 83.1079 82.963 68.1153 82.963 49.7047C82.9628 33.3931 71.2681 19.7799 55.796 16.8414L53.7579 9.88531ZM61.0743 48.2662C61.7941 50.9646 64.2528 52.8835 68.8702 51.6841L79.1847 46.4664L52.6779 73.4527L49.5001 5.6275L61.0743 48.2662ZM46.381 73.3931L19.8751 46.4664L30.1896 51.6841C34.8072 52.8835 37.2658 50.9644 37.9855 48.3257L49.5001 5.6275L46.381 73.3931Z" fill="url(#paint0_linear_1796_8073)"/>
5 +<defs>
6 +<linearGradient id="paint0_linear_1796_8073" x1="9.50012" y1="7.66632" x2="78.7647" y2="76.9306" gradientUnits="userSpaceOnUse">
7 +<stop offset="0.180653" stop-color="#007EC4"/>
8 +<stop offset="0.585847" stop-color="#00AEEF"/>
9 +<stop offset="0.813526" stop-color="#43C9F4"/>
10 +<stop offset="1" stop-color="#A3E1F9"/>
11 +</linearGradient>
12 +</defs>
13 +</svg>
res/pictures/address_sources/unstoppable.svg new
+5
@@ -0,0 +1,5 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="#0D67FE"/>
3 +<path d="M87.5 15.1819V42.417L7.5 74.598L87.5 15.1819Z" fill="#00C9FF"/>
4 +<path d="M72.5025 14V60.9822C72.5025 67.5477 69.8684 73.8444 65.1797 78.487C60.491 83.1297 54.1317 85.7378 47.5009 85.7378C40.8702 85.7378 34.5109 83.1297 29.8222 78.487C25.1335 73.8444 22.4994 67.5477 22.4994 60.9822V41.1851L37.4977 33.0122V60.9822C37.3574 62.2028 37.479 63.4391 37.8546 64.6101C38.2304 65.7811 38.8516 66.8601 39.6778 67.7767C40.5041 68.6933 41.5165 69.4266 42.6489 69.9288C43.7814 70.431 45.0081 70.6907 46.249 70.6907C47.4897 70.6907 48.7166 70.431 49.849 69.9288C50.9813 69.4266 51.9939 68.6933 52.82 67.7767C53.6463 66.8601 54.2677 65.7811 54.6433 64.6101C55.0191 63.4391 55.1407 62.2028 55.0001 60.9822V23.4811L72.5025 14Z" fill="white"/>
5 +</svg>
res/pictures/address_sources/wellknown.svg new
+4
@@ -0,0 +1,4 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="#7D7D7D"/>
3 +<path d="M50 83.3333C45.4444 83.3333 41.1389 82.4583 37.0833 80.7083C33.0278 78.9583 29.4861 76.5694 26.4583 73.5417C23.4305 70.5139 21.0417 66.9722 19.2917 62.9167C17.5417 58.8611 16.6667 54.5556 16.6667 50C16.6667 45.3889 17.5417 41.0694 19.2917 37.0417C21.0417 33.0139 23.4305 29.4861 26.4583 26.4583C29.4861 23.4306 33.0278 21.0417 37.0833 19.2917C41.1389 17.5417 45.4444 16.6667 50 16.6667C54.6111 16.6667 58.9305 17.5417 62.9583 19.2917C66.9861 21.0417 70.5139 23.4306 73.5417 26.4583C76.5694 29.4861 78.9583 33.0139 80.7083 37.0417C82.4583 41.0694 83.3333 45.3889 83.3333 50C83.3333 54.5556 82.4583 58.8611 80.7083 62.9167C78.9583 66.9722 76.5694 70.5139 73.5417 73.5417C70.5139 76.5694 66.9861 78.9583 62.9583 80.7083C58.9305 82.4583 54.6111 83.3333 50 83.3333ZM50 76.5C51.4444 74.5 52.6944 72.4167 53.75 70.25C54.8055 68.0833 55.6667 65.7778 56.3333 63.3333H43.6667C44.3333 65.7778 45.1944 68.0833 46.25 70.25C47.3055 72.4167 48.5555 74.5 50 76.5ZM41.3333 75.1667C40.3333 73.3333 39.4583 71.4306 38.7083 69.4583C37.9583 67.4861 37.3333 65.4444 36.8333 63.3333H27C28.6111 66.1111 30.625 68.5278 33.0417 70.5833C35.4583 72.6389 38.2222 74.1667 41.3333 75.1667ZM58.6667 75.1667C61.7778 74.1667 64.5417 72.6389 66.9583 70.5833C69.375 68.5278 71.3889 66.1111 73 63.3333H63.1667C62.6667 65.4444 62.0417 67.4861 61.2917 69.4583C60.5417 71.4306 59.6667 73.3333 58.6667 75.1667ZM24.1667 56.6667H35.5C35.3333 55.5556 35.2083 54.4583 35.125 53.375C35.0417 52.2917 35 51.1667 35 50C35 48.8333 35.0417 47.7083 35.125 46.625C35.2083 45.5417 35.3333 44.4444 35.5 43.3333H24.1667C23.8889 44.4444 23.6805 45.5417 23.5417 46.625C23.4028 47.7083 23.3333 48.8333 23.3333 50C23.3333 51.1667 23.4028 52.2917 23.5417 53.375C23.6805 54.4583 23.8889 55.5556 24.1667 56.6667ZM42.1667 56.6667H57.8333C58 55.5556 58.125 54.4583 58.2083 53.375C58.2917 52.2917 58.3333 51.1667 58.3333 50C58.3333 48.8333 58.2917 47.7083 58.2083 46.625C58.125 45.5417 58 44.4444 57.8333 43.3333H42.1667C42 44.4444 41.875 45.5417 41.7917 46.625C41.7083 47.7083 41.6667 48.8333 41.6667 50C41.6667 51.1667 41.7083 52.2917 41.7917 53.375C41.875 54.4583 42 55.5556 42.1667 56.6667ZM64.5 56.6667H75.8333C76.1111 55.5556 76.3194 54.4583 76.4583 53.375C76.5972 52.2917 76.6667 51.1667 76.6667 50C76.6667 48.8333 76.5972 47.7083 76.4583 46.625C76.3194 45.5417 76.1111 44.4444 75.8333 43.3333H64.5C64.6667 44.4444 64.7917 45.5417 64.875 46.625C64.9583 47.7083 65 48.8333 65 50C65 51.1667 64.9583 52.2917 64.875 53.375C64.7917 54.4583 64.6667 55.5556 64.5 56.6667ZM63.1667 36.6667H73C71.3889 33.8889 69.375 31.4722 66.9583 29.4167C64.5417 27.3611 61.7778 25.8333 58.6667 24.8333C59.6667 26.6667 60.5417 28.5694 61.2917 30.5417C62.0417 32.5139 62.6667 34.5556 63.1667 36.6667ZM43.6667 36.6667H56.3333C55.6667 34.2222 54.8055 31.9167 53.75 29.75C52.6944 27.5833 51.4444 25.5 50 23.5C48.5555 25.5 47.3055 27.5833 46.25 29.75C45.1944 31.9167 44.3333 34.2222 43.6667 36.6667ZM27 36.6667H36.8333C37.3333 34.5556 37.9583 32.5139 38.7083 30.5417C39.4583 28.5694 40.3333 26.6667 41.3333 24.8333C38.2222 25.8333 35.4583 27.3611 33.0417 29.4167C30.625 31.4722 28.6111 33.8889 27 36.6667Z" fill="white"/>
4 +</svg>
res/pictures/address_sources/x.svg new
+11
@@ -0,0 +1,11 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="black"/>
3 +<g clip-path="url(#clip0_1796_8041)">
4 +<path d="M70.0666 17.8833H80.8L57.2333 44.7167L84.7666 81.1167H63.16L46.2433 58.9967L26.8766 81.1167H16.1433L41.11 52.4167L14.7433 17.8833H36.8866L52.17 38.09L70.0666 17.8833ZM66.31 74.8167H72.26L33.76 23.95H27.3666L66.31 74.8167Z" fill="white"/>
5 +</g>
6 +<defs>
7 +<clipPath id="clip0_1796_8041">
8 +<rect width="70" height="63.2333" fill="white" transform="translate(15 17.8833)"/>
9 +</clipPath>
10 +</defs>
11 +</svg>
res/pictures/address_sources/yat.svg new
+11
@@ -0,0 +1,11 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="#151327"/>
3 +<g clip-path="url(#clip0_1796_8108)">
4 +<path d="M46.7845 87.5C40.1522 87.5 34.1818 85.057 29.0489 80.2495C26.2123 77.5833 24.3212 74.1157 23.092 69.3214C22.3355 66.3662 22.0249 63.4501 21.8087 60.9809C21.5386 57.7758 21.2414 54.663 20.9848 52.036L20.9577 51.6944C20.7281 49.3694 20.485 46.9527 20.2013 44.6014C19.9177 42.3162 19.5935 39.9781 19.2693 37.732L18.9181 35.197C18.8506 34.7242 18.7695 34.2382 18.702 33.7653C18.5264 32.6489 18.3508 31.5455 18.2832 30.4159C18.0806 27.0272 20.1203 24.0981 23.4702 22.9422C24.6994 22.5219 25.9961 22.4299 27.2253 22.6401C27.3874 20.2364 28.6977 18.0429 30.8454 16.7294C33.5199 15.0876 36.8293 15.232 39.4768 17.0972C41.1112 18.2531 42.0973 19.8949 42.489 22.1147C43.1104 25.5692 43.7317 29.0105 44.3531 32.465L44.5422 33.4763L46.5954 23.5464C46.9195 21.9834 47.2436 20.3809 47.5543 18.7916C48.2703 15.0876 51.4041 12.5 55.1593 12.5C59.0359 12.5131 62.2912 15.324 62.7505 19.028C62.764 19.1068 62.764 19.1856 62.7776 19.2513C64.0067 19.0674 65.263 19.1594 66.4382 19.5797C70.2069 20.9063 72.2466 24.6497 71.4089 28.6953L70.6526 32.3205C69.707 36.7995 68.7614 41.331 67.8969 45.8231C69.1938 44.2074 70.9768 43.1568 72.9355 42.8678C74.9345 42.5788 76.9607 43.1043 78.6626 44.3389C81.1347 46.1384 82.2692 49.3169 81.4858 52.2592C80.7159 55.1358 79.3785 57.7233 78.0819 60.232C77.7441 60.8756 77.4199 61.5191 77.0957 62.176C74.2457 67.9028 71.3414 73.4326 67.0866 78.1873C63.0611 82.6928 58.7659 85.4511 53.957 86.6199C51.4987 87.198 49.0944 87.5 46.7845 87.5ZM25.9016 26.9615C25.5909 26.9615 25.2802 27.014 24.983 27.1191C23.5377 27.6182 22.7408 28.7215 22.8353 30.1401C22.8894 31.0727 23.0514 32.0578 23.2 33.0035C23.2946 33.5683 23.3756 34.0806 23.4432 34.5928L23.7944 37.1278C24.1185 39.4002 24.4427 41.7514 24.7399 44.0632C25.0371 46.4666 25.2802 48.9099 25.5099 51.2609L25.5369 51.6024C25.7935 54.2427 26.1042 57.3817 26.3743 60.5998C26.5635 62.8986 26.8606 65.5911 27.536 68.2311C28.5491 72.1847 30.0349 74.9692 32.2232 77.0314C37.991 82.443 44.9069 84.1899 52.836 82.2722C56.6991 81.3266 60.2247 79.0279 63.6285 75.232C67.5187 70.8843 70.2608 65.6436 72.9623 60.2058C73.3001 59.536 73.6379 58.8662 73.9889 58.2093C75.2316 55.8056 76.3933 53.5334 77.0553 51.1163C77.3795 49.9342 76.9067 48.6076 75.9205 47.8853C75.1912 47.36 74.3942 47.1367 73.6107 47.2547C72.8273 47.373 72.0845 47.8195 71.5035 48.5421L65.6411 59.2732C65.128 60.219 64.0067 60.6656 62.9533 60.3633C61.8995 60.0483 61.2242 59.0632 61.3324 58.0123C61.6834 54.6367 62.2373 51.0376 63.1289 46.3487C64.0609 41.3837 65.1144 36.366 66.141 31.5193L66.9245 27.8021C67.3162 25.9238 66.5192 24.3345 64.8848 23.7566C63.8582 23.3888 62.5751 23.704 61.7241 24.5184C61.0217 25.1883 59.968 25.3459 59.0899 24.9124C58.2121 24.479 57.7122 23.5596 57.8608 22.6138C57.8879 22.4693 57.9014 22.3117 57.9147 22.1672C57.9554 21.8389 58.0093 21.4843 58.0635 21.1559C58.1714 20.5517 58.266 19.9869 58.2121 19.5666C58.0229 18.0823 56.7262 16.9527 55.1729 16.9396C53.6059 16.9396 52.3633 18.0166 52.0526 19.606C51.7419 21.2084 51.4041 22.824 51.0799 24.4002L46.6359 45.9151C46.4198 46.9527 45.4742 47.7015 44.3936 47.7015C44.3801 47.7015 44.3801 47.7015 44.3666 47.7015C43.2725 47.6883 42.3269 46.9132 42.1378 45.8626C41.6516 43.1831 41.1653 40.4904 40.679 37.8109L39.8415 33.2137C39.2202 29.7592 38.5853 26.3047 37.9774 22.8503C37.7343 21.5105 37.2345 20.9983 36.7753 20.6699C35.6676 19.8818 34.3574 19.803 33.2768 20.4728C32.1691 21.1559 31.6018 22.4168 31.845 23.704C32.007 24.5447 32.1556 25.3984 32.2907 26.2522C32.4663 27.2373 31.9395 28.2355 30.994 28.6559C30.0484 29.0762 28.9543 28.8529 28.2789 28.0911C27.6441 27.3555 26.7796 26.9615 25.9016 26.9615Z" fill="white"/>
5 +</g>
6 +<defs>
7 +<clipPath id="clip0_1796_8108">
8 +<rect width="75" height="75" fill="white" transform="translate(12.5 12.5)"/>
9 +</clipPath>
10 +</defs>
11 +</svg>
res/pictures/address_sources/zano.svg new
+37
@@ -0,0 +1,37 @@
1 +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<rect width="100" height="100" rx="20" fill="#0C0C3A"/>
3 +<path fill-rule="evenodd" clip-rule="evenodd" d="M55.3609 17.0015H62.9359C65.5294 17.0015 67.7119 17.0015 69.4984 17.15C71.3674 17.3045 73.1479 17.642 74.8384 18.518C77.4239 19.8612 79.5179 21.9883 80.8204 24.5945C81.6829 26.3135 82.0144 28.121 82.1674 30.02C82.3129 31.835 82.3129 34.052 82.3129 36.6875V43.9865C82.3129 46.622 82.3129 48.839 82.1674 50.654C82.0144 52.553 81.6829 54.362 80.8204 56.0795C79.5182 58.6863 77.4241 60.8139 74.8384 62.1575C73.1479 63.032 71.3674 63.3695 69.4984 63.524C67.7119 63.674 65.5294 63.674 62.9344 63.674H40.3174C39.5362 63.6705 38.7736 63.4351 38.1263 62.9977C37.479 62.5603 36.9761 61.9405 36.6814 61.217C36.3802 60.4891 36.2975 59.6892 36.4435 58.9151C36.5894 58.141 36.9578 57.4262 37.5034 56.858L56.1799 37.5305L61.8094 43.145L49.7179 55.658H62.7769C65.5714 55.658 67.4299 55.655 68.8564 55.538C70.2364 55.4225 70.8634 55.22 71.2564 55.016C72.3522 54.4467 73.2396 53.5451 73.7914 52.4405C73.9924 52.0415 74.1919 51.4055 74.3044 50.0015C74.4214 48.5525 74.4244 46.664 74.4244 43.826V36.8465C74.4244 34.0085 74.4214 32.1215 74.3044 30.671C74.1919 29.2685 73.9924 28.631 73.7914 28.232C73.2396 27.1274 72.3522 26.2258 71.2564 25.6565C70.8634 25.4525 70.2364 25.2515 68.8564 25.136C67.4299 25.016 65.5714 25.0145 62.7769 25.0145H55.5214C52.6969 25.0145 50.8159 25.0175 49.3729 25.1375C47.9779 25.2545 47.3449 25.46 46.9504 25.667C45.8484 26.2449 44.9593 27.1592 44.4124 28.277C44.2129 28.6805 44.0194 29.324 43.9219 30.746C43.8199 32.213 43.8394 34.121 43.8739 36.992L43.9144 40.2875L36.0259 40.385L35.9854 36.9275C35.9524 34.262 35.9254 32.0225 36.0514 30.1865C36.1834 28.2695 36.4984 26.4395 37.3534 24.6995C38.6437 22.0611 40.7419 19.903 43.3429 18.539C45.0454 17.6495 46.8424 17.309 48.7279 17.15C50.5324 17 52.7389 17 55.3609 17V17.0015Z" fill="url(#paint0_linear_1796_8095)"/>
4 +<path fill-rule="evenodd" clip-rule="evenodd" d="M55.3609 17.0015H62.9359C65.5294 17.0015 67.7119 17.0015 69.4984 17.15C71.3674 17.3045 73.1479 17.642 74.8384 18.518C77.4239 19.8612 79.5179 21.9883 80.8204 24.5945C81.6829 26.3135 82.0144 28.121 82.1674 30.02C82.3129 31.835 82.3129 34.052 82.3129 36.6875V43.9865C82.3129 46.622 82.3129 48.839 82.1674 50.654C82.0144 52.553 81.6829 54.362 80.8204 56.0795C79.5182 58.6863 77.4241 60.8139 74.8384 62.1575C73.1479 63.032 71.3674 63.3695 69.4984 63.524C67.7119 63.674 65.5294 63.674 62.9344 63.674H40.3174C39.5362 63.6705 38.7736 63.4351 38.1263 62.9977C37.479 62.5603 36.9761 61.9405 36.6814 61.217C36.3802 60.4891 36.2975 59.6892 36.4435 58.9151C36.5894 58.141 36.9578 57.4262 37.5034 56.858L56.1799 37.5305L61.8094 43.145L49.7179 55.658H62.7769C65.5714 55.658 67.4299 55.655 68.8564 55.538C70.2364 55.4225 70.8634 55.22 71.2564 55.016C72.3522 54.4467 73.2396 53.5451 73.7914 52.4405C73.9924 52.0415 74.1919 51.4055 74.3044 50.0015C74.4214 48.5525 74.4244 46.664 74.4244 43.826V36.8465C74.4244 34.0085 74.4214 32.1215 74.3044 30.671C74.1919 29.2685 73.9924 28.631 73.7914 28.232C73.2396 27.1274 72.3522 26.2258 71.2564 25.6565C70.8634 25.4525 70.2364 25.2515 68.8564 25.136C67.4299 25.016 65.5714 25.0145 62.7769 25.0145H55.5214C52.6969 25.0145 50.8159 25.0175 49.3729 25.1375C47.9779 25.2545 47.3449 25.46 46.9504 25.667C45.8484 26.2449 44.9593 27.1592 44.4124 28.277C44.2129 28.6805 44.0194 29.324 43.9219 30.746C43.8199 32.213 43.8394 34.121 43.8739 36.992L43.9144 40.2875L36.0259 40.385L35.9854 36.9275C35.9524 34.262 35.9254 32.0225 36.0514 30.1865C36.1834 28.2695 36.4984 26.4395 37.3534 24.6995C38.6437 22.0611 40.7419 19.903 43.3429 18.539C45.0454 17.6495 46.8424 17.309 48.7279 17.15C50.5324 17 52.7389 17 55.3609 17V17.0015Z" fill="url(#paint1_radial_1796_8095)"/>
5 +<path fill-rule="evenodd" clip-rule="evenodd" d="M55.3609 17.0015H62.9359C65.5294 17.0015 67.7119 17.0015 69.4984 17.15C71.3674 17.3045 73.1479 17.642 74.8384 18.518C77.4239 19.8612 79.5179 21.9883 80.8204 24.5945C81.6829 26.3135 82.0144 28.121 82.1674 30.02C82.3129 31.835 82.3129 34.052 82.3129 36.6875V43.9865C82.3129 46.622 82.3129 48.839 82.1674 50.654C82.0144 52.553 81.6829 54.362 80.8204 56.0795C79.5182 58.6863 77.4241 60.8139 74.8384 62.1575C73.1479 63.032 71.3674 63.3695 69.4984 63.524C67.7119 63.674 65.5294 63.674 62.9344 63.674H40.3174C39.5362 63.6705 38.7736 63.4351 38.1263 62.9977C37.479 62.5603 36.9761 61.9405 36.6814 61.217C36.3802 60.4891 36.2975 59.6892 36.4435 58.9151C36.5894 58.141 36.9578 57.4262 37.5034 56.858L56.1799 37.5305L61.8094 43.145L49.7179 55.658H62.7769C65.5714 55.658 67.4299 55.655 68.8564 55.538C70.2364 55.4225 70.8634 55.22 71.2564 55.016C72.3522 54.4467 73.2396 53.5451 73.7914 52.4405C73.9924 52.0415 74.1919 51.4055 74.3044 50.0015C74.4214 48.5525 74.4244 46.664 74.4244 43.826V36.8465C74.4244 34.0085 74.4214 32.1215 74.3044 30.671C74.1919 29.2685 73.9924 28.631 73.7914 28.232C73.2396 27.1274 72.3522 26.2258 71.2564 25.6565C70.8634 25.4525 70.2364 25.2515 68.8564 25.136C67.4299 25.016 65.5714 25.0145 62.7769 25.0145H55.5214C52.6969 25.0145 50.8159 25.0175 49.3729 25.1375C47.9779 25.2545 47.3449 25.46 46.9504 25.667C45.8484 26.2449 44.9593 27.1592 44.4124 28.277C44.2129 28.6805 44.0194 29.324 43.9219 30.746C43.8199 32.213 43.8394 34.121 43.8739 36.992L43.9144 40.2875L36.0259 40.385L35.9854 36.9275C35.9524 34.262 35.9254 32.0225 36.0514 30.1865C36.1834 28.2695 36.4984 26.4395 37.3534 24.6995C38.6437 22.0611 40.7419 19.903 43.3429 18.539C45.0454 17.6495 46.8424 17.309 48.7279 17.15C50.5324 17 52.7389 17 55.3609 17V17.0015Z" fill="url(#paint2_radial_1796_8095)"/>
6 +<path fill-rule="evenodd" clip-rule="evenodd" d="M55.3609 17.0015H62.9359C65.5294 17.0015 67.7119 17.0015 69.4984 17.15C71.3674 17.3045 73.1479 17.642 74.8384 18.518C77.4239 19.8612 79.5179 21.9883 80.8204 24.5945C81.6829 26.3135 82.0144 28.121 82.1674 30.02C82.3129 31.835 82.3129 34.052 82.3129 36.6875V43.9865C82.3129 46.622 82.3129 48.839 82.1674 50.654C82.0144 52.553 81.6829 54.362 80.8204 56.0795C79.5182 58.6863 77.4241 60.8139 74.8384 62.1575C73.1479 63.032 71.3674 63.3695 69.4984 63.524C67.7119 63.674 65.5294 63.674 62.9344 63.674H40.3174C39.5362 63.6705 38.7736 63.4351 38.1263 62.9977C37.479 62.5603 36.9761 61.9405 36.6814 61.217C36.3802 60.4891 36.2975 59.6892 36.4435 58.9151C36.5894 58.141 36.9578 57.4262 37.5034 56.858L56.1799 37.5305L61.8094 43.145L49.7179 55.658H62.7769C65.5714 55.658 67.4299 55.655 68.8564 55.538C70.2364 55.4225 70.8634 55.22 71.2564 55.016C72.3522 54.4467 73.2396 53.5451 73.7914 52.4405C73.9924 52.0415 74.1919 51.4055 74.3044 50.0015C74.4214 48.5525 74.4244 46.664 74.4244 43.826V36.8465C74.4244 34.0085 74.4214 32.1215 74.3044 30.671C74.1919 29.2685 73.9924 28.631 73.7914 28.232C73.2396 27.1274 72.3522 26.2258 71.2564 25.6565C70.8634 25.4525 70.2364 25.2515 68.8564 25.136C67.4299 25.016 65.5714 25.0145 62.7769 25.0145H55.5214C52.6969 25.0145 50.8159 25.0175 49.3729 25.1375C47.9779 25.2545 47.3449 25.46 46.9504 25.667C45.8484 26.2449 44.9593 27.1592 44.4124 28.277C44.2129 28.6805 44.0194 29.324 43.9219 30.746C43.8199 32.213 43.8394 34.121 43.8739 36.992L43.9144 40.2875L36.0259 40.385L35.9854 36.9275C35.9524 34.262 35.9254 32.0225 36.0514 30.1865C36.1834 28.2695 36.4984 26.4395 37.3534 24.6995C38.6437 22.0611 40.7419 19.903 43.3429 18.539C45.0454 17.6495 46.8424 17.309 48.7279 17.15C50.5324 17 52.7389 17 55.3609 17V17.0015Z" fill="url(#paint3_radial_1796_8095)"/>
7 +<path fill-rule="evenodd" clip-rule="evenodd" d="M30.455 74.8655C31.883 74.984 33.74 74.987 36.5345 74.987H43.913C46.46 74.987 48.1535 74.984 49.4585 74.885C50.7245 74.789 51.3035 74.6195 51.668 74.45C52.9351 73.8565 53.9474 72.8282 54.521 71.552C54.686 71.183 54.854 70.592 54.9485 69.308C55.046 67.982 55.049 66.2615 55.049 63.6725H62.9375V63.8195C62.9375 66.2225 62.9375 68.2445 62.816 69.905C62.6885 71.639 62.411 73.2965 61.691 74.8925C60.3375 77.9061 57.9477 80.3342 54.956 81.7355C53.3855 82.4675 51.7535 82.748 50.0465 82.877C48.4115 83.0015 46.4225 83.0015 44.057 83.0015H36.377C33.782 83.0015 31.5995 83.0015 29.813 82.853C27.944 82.6985 26.1635 82.361 24.473 81.485C21.8875 80.1418 19.7935 78.0147 18.491 75.4085C17.6285 73.6895 17.297 71.882 17.144 69.9815C17 68.168 17 65.951 17 63.3155V56.0165C17 53.381 17 51.164 17.1455 49.349C17.2985 47.45 17.63 45.641 18.4925 43.9235C19.7948 41.3167 21.8888 39.1891 24.4745 37.8455C26.165 36.971 27.9455 36.6335 29.8145 36.4775C31.601 36.3305 33.7835 36.3305 36.3785 36.3305H58.9955C61.1735 36.3305 62.939 38.1245 62.939 40.337C62.939 42.5495 61.1735 44.345 58.994 44.345H36.536C33.7415 44.345 31.8845 44.348 30.4565 44.465C29.0765 44.5805 28.4495 44.783 28.0565 44.987C26.9607 45.5563 26.0733 46.4578 25.5215 47.5625C25.3205 47.9615 25.121 48.5975 25.0085 50.0015C24.8915 51.4505 24.8885 53.339 24.8885 56.177V63.155C24.8885 65.993 24.8915 67.88 25.0085 69.3305C25.121 70.733 25.3205 71.3705 25.5215 71.7695C26.0765 72.8795 26.9645 73.7795 28.0565 74.345C28.4495 74.549 29.075 74.75 30.455 74.8655Z" fill="url(#paint4_linear_1796_8095)"/>
8 +<path fill-rule="evenodd" clip-rule="evenodd" d="M30.455 74.8655C31.883 74.984 33.74 74.987 36.5345 74.987H43.913C46.46 74.987 48.1535 74.984 49.4585 74.885C50.7245 74.789 51.3035 74.6195 51.668 74.45C52.9351 73.8565 53.9474 72.8282 54.521 71.552C54.686 71.183 54.854 70.592 54.9485 69.308C55.046 67.982 55.049 66.2615 55.049 63.6725H62.9375V63.8195C62.9375 66.2225 62.9375 68.2445 62.816 69.905C62.6885 71.639 62.411 73.2965 61.691 74.8925C60.3375 77.9061 57.9477 80.3342 54.956 81.7355C53.3855 82.4675 51.7535 82.748 50.0465 82.877C48.4115 83.0015 46.4225 83.0015 44.057 83.0015H36.377C33.782 83.0015 31.5995 83.0015 29.813 82.853C27.944 82.6985 26.1635 82.361 24.473 81.485C21.8875 80.1418 19.7935 78.0147 18.491 75.4085C17.6285 73.6895 17.297 71.882 17.144 69.9815C17 68.168 17 65.951 17 63.3155V56.0165C17 53.381 17 51.164 17.1455 49.349C17.2985 47.45 17.63 45.641 18.4925 43.9235C19.7948 41.3167 21.8888 39.1891 24.4745 37.8455C26.165 36.971 27.9455 36.6335 29.8145 36.4775C31.601 36.3305 33.7835 36.3305 36.3785 36.3305H58.9955C61.1735 36.3305 62.939 38.1245 62.939 40.337C62.939 42.5495 61.1735 44.345 58.994 44.345H36.536C33.7415 44.345 31.8845 44.348 30.4565 44.465C29.0765 44.5805 28.4495 44.783 28.0565 44.987C26.9607 45.5563 26.0733 46.4578 25.5215 47.5625C25.3205 47.9615 25.121 48.5975 25.0085 50.0015C24.8915 51.4505 24.8885 53.339 24.8885 56.177V63.155C24.8885 65.993 24.8915 67.88 25.0085 69.3305C25.121 70.733 25.3205 71.3705 25.5215 71.7695C26.0765 72.8795 26.9645 73.7795 28.0565 74.345C28.4495 74.549 29.075 74.75 30.455 74.8655Z" fill="url(#paint5_radial_1796_8095)"/>
9 +<defs>
10 +<linearGradient id="paint0_linear_1796_8095" x1="48.1204" y1="63.311" x2="48.6544" y2="16.9415" gradientUnits="userSpaceOnUse">
11 +<stop offset="0.431" stop-color="#498FFD"/>
12 +<stop offset="1" stop-color="#16D1D6"/>
13 +</linearGradient>
14 +<radialGradient id="paint1_radial_1796_8095" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(35.41 31.1318) rotate(-15.65) scale(29.8203 29.9076)">
15 +<stop stop-color="#18CFD7"/>
16 +<stop offset="1" stop-color="#18CFD7" stop-opacity="0"/>
17 +</radialGradient>
18 +<radialGradient id="paint2_radial_1796_8095" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(52.8292 44.3816) rotate(152.23) scale(11.1721 11.1879)">
19 +<stop stop-color="#4990FE"/>
20 +<stop offset="0.354" stop-color="#4990FE"/>
21 +<stop offset="1" stop-color="#4990FE" stop-opacity="0"/>
22 +</radialGradient>
23 +<radialGradient id="paint3_radial_1796_8095" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(39.1765 59.5247) rotate(-46.089) scale(20.3623 20.3268)">
24 +<stop stop-color="#4990FE"/>
25 +<stop offset="0.406" stop-color="#4990FE"/>
26 +<stop offset="1" stop-color="#4990FE" stop-opacity="0"/>
27 +</radialGradient>
28 +<linearGradient id="paint4_linear_1796_8095" x1="63.182" y1="83.186" x2="63.353" y2="35.8655" gradientUnits="userSpaceOnUse">
29 +<stop stop-color="#2950FF"/>
30 +<stop offset="0.822" stop-color="#498FFD"/>
31 +</linearGradient>
32 +<radialGradient id="paint5_radial_1796_8095" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(63.182 63.311) rotate(129.142) scale(20.1345 19.9017)">
33 +<stop offset="0.337" stop-color="#2950FF"/>
34 +<stop offset="0.792" stop-color="#2950FF" stop-opacity="0"/>
35 +</radialGradient>
36 +</defs>
37 +</svg>
test/entities/parse_address_from_domain_test.dart
+1 -1
@@ -1,4 +1,4 @@
1 -import 'package:cake_wallet/entities/parse_address_from_domain.dart';
1 +import 'package:cake_wallet/core/address_resolver/parse_address_from_domain.dart';
2 import 'package:cw_core/crypto_currency.dart';
3 import 'package:flutter_test/flutter_test.dart';
4