diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 4f622da7d..ce509015c 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -247,7 +247,16 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen return CryptoCurrency._rawCurrencyMap[raw]!; } - static CryptoCurrency fromString(String name) { + // TODO: refactor this + static CryptoCurrency fromString(String name, {CryptoCurrency? walletCurrency}) { + try { + return CryptoCurrency.all.firstWhere((element) => + element.title.toLowerCase() == name && + (element.tag == null || + element.tag == walletCurrency?.title || + element.tag == walletCurrency?.tag)); + } catch (_) {} + if (CryptoCurrency._nameCurrencyMap[name.toLowerCase()] == null) { final s = 'Unexpected token: $name for CryptoCurrency fromString'; throw ArgumentError.value(name, 'name', s); diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 84fcb9e2e..ad2c761a3 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -295,6 +295,16 @@ class AddressValidator extends TextValidator { case CryptoCurrency.sol: return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)'; default: + if (type.tag == CryptoCurrency.eth.title) { + return '0x[0-9a-zA-Z]{42}'; + } + if (type.tag == CryptoCurrency.maticpoly.tag) { + return '0x[0-9a-zA-Z]{42}'; + } + if (type.tag == CryptoCurrency.sol.title) { + return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)'; + } + return null; } } diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index 3ebc08c55..bab0ef51d 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -51,7 +51,8 @@ class AddressResolver { } final match = RegExp(addressPattern).firstMatch(raw); - return match?.group(0)?.replaceAllMapped(RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_'), (Match match) { + return match?.group(0)?.replaceAllMapped(RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_'), + (Match match) { String group = match.group(0)!; if (group.startsWith('bitcoincash:') || group.startsWith('nano_')) { return group; @@ -68,7 +69,7 @@ class AddressResolver { return emailRegex.hasMatch(address); } - + // TODO: refactor this to take Crypto currency instead of ticker, or at least pass in the tag as well Future resolve(BuildContext context, String text, String ticker) async { try { if (text.startsWith('@') && !text.substring(1).contains('@')) { @@ -76,7 +77,8 @@ class AddressResolver { final formattedName = text.substring(1); final twitterUser = await TwitterApi.lookupUserByName(userName: formattedName); final addressFromBio = extractAddressByType( - raw: twitterUser.description, type: CryptoCurrency.fromString(ticker)); + raw: twitterUser.description, + type: CryptoCurrency.fromString(ticker, walletCurrency: wallet.currency)); if (addressFromBio != null) { return ParsedAddress.fetchTwitterAddress( address: addressFromBio, @@ -87,8 +89,9 @@ class AddressResolver { final pinnedTweet = twitterUser.pinnedTweet?.text; if (pinnedTweet != null) { - final addressFromPinnedTweet = - extractAddressByType(raw: pinnedTweet, type: CryptoCurrency.fromString(ticker)); + final addressFromPinnedTweet = extractAddressByType( + raw: pinnedTweet, + type: CryptoCurrency.fromString(ticker, walletCurrency: wallet.currency)); if (addressFromPinnedTweet != null) { return ParsedAddress.fetchTwitterAddress( address: addressFromPinnedTweet, @@ -108,11 +111,11 @@ class AddressResolver { final userName = subText.substring(0, hostNameIndex); final mastodonUser = - await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName); + await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName); if (mastodonUser != null) { - String? addressFromBio = - extractAddressByType(raw: mastodonUser.note, type: CryptoCurrency.fromString(ticker)); + String? addressFromBio = extractAddressByType( + raw: mastodonUser.note, type: CryptoCurrency.fromString(ticker)); if (addressFromBio != null) { return ParsedAddress.fetchMastodonAddress( @@ -122,7 +125,7 @@ class AddressResolver { profileName: mastodonUser.username); } else { final pinnedPosts = - await MastodonAPI.getPinnedPosts(userId: mastodonUser.id, apiHost: hostName); + await MastodonAPI.getPinnedPosts(userId: mastodonUser.id, apiHost: hostName); if (pinnedPosts.isNotEmpty) { final userPinnedPostsText = pinnedPosts.map((item) => item.content).join('\n'); @@ -150,7 +153,7 @@ class AddressResolver { } } if (text.hasOnlyEmojis) { - if(settingsStore.lookupsYatService) { + if (settingsStore.lookupsYatService) { if (walletType != WalletType.haven) { final addresses = await yatService.fetchYatAddress(text, ticker); return ParsedAddress.fetchEmojiAddress(addresses: addresses, name: text); @@ -166,7 +169,7 @@ class AddressResolver { } if (unstoppableDomains.any((domain) => name.trim() == domain)) { - if(settingsStore.lookupsUnstoppableDomains) { + if (settingsStore.lookupsUnstoppableDomains) { final address = await fetchUnstoppableDomainAddress(text, ticker); return ParsedAddress.fetchUnstoppableDomainAddress(address: address, name: text); } @@ -182,7 +185,7 @@ class AddressResolver { } if (formattedName.contains(".")) { - if(settingsStore.lookupsOpenAlias) { + if (settingsStore.lookupsOpenAlias) { final txtRecord = await OpenaliasRecord.lookupOpenAliasRecord(formattedName); if (txtRecord != null) { final record = await OpenaliasRecord.fetchAddressAndName( @@ -201,7 +204,11 @@ class AddressResolver { String? addressFromBio = extractAddressByType( raw: nostrUserData.about, type: CryptoCurrency.fromString(ticker)); if (addressFromBio != null) { - return ParsedAddress.nostrAddress(address: addressFromBio, name: text); + return ParsedAddress.nostrAddress( + address: addressFromBio, + name: text, + profileImageUrl: nostrUserData.picture, + profileName: nostrUserData.name); } } } diff --git a/lib/entities/parsed_address.dart b/lib/entities/parsed_address.dart index d87deb9e8..fc8ab2440 100644 --- a/lib/entities/parsed_address.dart +++ b/lib/entities/parsed_address.dart @@ -119,11 +119,17 @@ class ParsedAddress { ); } - factory ParsedAddress.nostrAddress({required String address, required String name}) { + factory ParsedAddress.nostrAddress( + {required String address, + required String name, + required String profileImageUrl, + required String profileName}) { return ParsedAddress( addresses: [address], name: name, parseFrom: ParseFrom.nostr, + profileImageUrl: profileImageUrl, + profileName: profileName, ); } diff --git a/lib/src/screens/send/widgets/extract_address_from_parsed.dart b/lib/src/screens/send/widgets/extract_address_from_parsed.dart index 42e646d58..eb997c11b 100644 --- a/lib/src/screens/send/widgets/extract_address_from_parsed.dart +++ b/lib/src/screens/send/widgets/extract_address_from_parsed.dart @@ -53,6 +53,8 @@ Future extractAddressFromParsed( title = S.of(context).address_detected; content = S.of(context).extracted_address_content('${parsedAddress.name} (Nostr NIP-05)'); address = parsedAddress.addresses.first; + profileImageUrl = parsedAddress.profileImageUrl; + profileName = parsedAddress.profileName; break; case ParseFrom.yatRecord: if (parsedAddress.name.isEmpty) { diff --git a/lib/src/widgets/base_alert_dialog.dart b/lib/src/widgets/base_alert_dialog.dart index e9ef522df..b251e4b45 100644 --- a/lib/src/widgets/base_alert_dialog.dart +++ b/lib/src/widgets/base_alert_dialog.dart @@ -1,4 +1,3 @@ -import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'dart:ui'; import 'package:cake_wallet/src/widgets/section_divider.dart'; diff --git a/lib/twitter/twitter_api.dart b/lib/twitter/twitter_api.dart index bf6298dae..5acb00e2a 100644 --- a/lib/twitter/twitter_api.dart +++ b/lib/twitter/twitter_api.dart @@ -32,7 +32,10 @@ class TwitterApi { } final Map responseJSON = jsonDecode(response.body) as Map; - if (responseJSON['errors'] != null) { + if (responseJSON['errors'] != null && + !responseJSON['errors'][0]['detail'] + .toString() + .contains("Could not find tweet with pinned_tweet_id")) { throw Exception(responseJSON['errors'][0]['detail']); } @@ -40,20 +43,24 @@ class TwitterApi { } static Tweet? _getPinnedTweet(Map responseJSON) { - final tweetId = responseJSON['data']['pinned_tweet_id'] as String?; - if (tweetId == null || responseJSON['includes'] == null) return null; + try { + final tweetId = responseJSON['data']['pinned_tweet_id'] as String?; + if (tweetId == null || responseJSON['includes'] == null) return null; - final tweetIncludes = List.from(responseJSON['includes']['tweets'] as List); - final pinnedTweetData = tweetIncludes.firstWhere( - (tweet) => tweet['id'] == tweetId, - orElse: () => null, - ) as Map?; + final tweetIncludes = List.from(responseJSON['includes']['tweets'] as List); + final pinnedTweetData = tweetIncludes.firstWhere( + (tweet) => tweet['id'] == tweetId, + orElse: () => null, + ) as Map?; - if (pinnedTweetData == null) return null; + if (pinnedTweetData == null) return null; - final pinnedTweetText = - (pinnedTweetData['note_tweet']?['text'] ?? pinnedTweetData['text']) as String; + final pinnedTweetText = + (pinnedTweetData['note_tweet']?['text'] ?? pinnedTweetData['text']) as String; - return Tweet(id: tweetId, text: pinnedTweetText); + return Tweet(id: tweetId, text: pinnedTweetText); + } catch (e) { + return null; + } } }