mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
thor name to address lookup (#1390)
* thor name to address lookup * minor fix [skip ci] * Addressing code review comments * minor fix
This commit is contained in:
parent
cd41766e69
commit
3f3cd10158
6 changed files with 82 additions and 35 deletions
|
@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/openalias_record.dart';
|
||||||
import 'package:cake_wallet/entities/parsed_address.dart';
|
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||||
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
|
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
|
||||||
import 'package:cake_wallet/entities/emoji_string_extension.dart';
|
import 'package:cake_wallet/entities/emoji_string_extension.dart';
|
||||||
|
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||||
import 'package:cake_wallet/mastodon/mastodon_api.dart';
|
import 'package:cake_wallet/mastodon/mastodon_api.dart';
|
||||||
import 'package:cake_wallet/nostr/nostr_api.dart';
|
import 'package:cake_wallet/nostr/nostr_api.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
@ -71,8 +72,8 @@ class AddressResolver {
|
||||||
return emailRegex.hasMatch(address);
|
return emailRegex.hasMatch(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor this to take Crypto currency instead of ticker, or at least pass in the tag as well
|
Future<ParsedAddress> resolve(BuildContext context, String text, CryptoCurrency currency) async {
|
||||||
Future<ParsedAddress> resolve(BuildContext context, String text, String ticker) async {
|
final ticker = currency.title;
|
||||||
try {
|
try {
|
||||||
if (text.startsWith('@') && !text.substring(1).contains('@')) {
|
if (text.startsWith('@') && !text.substring(1).contains('@')) {
|
||||||
if (settingsStore.lookupsTwitter) {
|
if (settingsStore.lookupsTwitter) {
|
||||||
|
@ -116,8 +117,7 @@ class AddressResolver {
|
||||||
await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName);
|
await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName);
|
||||||
|
|
||||||
if (mastodonUser != null) {
|
if (mastodonUser != null) {
|
||||||
String? addressFromBio = extractAddressByType(
|
String? addressFromBio = extractAddressByType(raw: mastodonUser.note, type: currency);
|
||||||
raw: mastodonUser.note, type: CryptoCurrency.fromString(ticker));
|
|
||||||
|
|
||||||
if (addressFromBio != null) {
|
if (addressFromBio != null) {
|
||||||
return ParsedAddress.fetchMastodonAddress(
|
return ParsedAddress.fetchMastodonAddress(
|
||||||
|
@ -131,8 +131,8 @@ class AddressResolver {
|
||||||
|
|
||||||
if (pinnedPosts.isNotEmpty) {
|
if (pinnedPosts.isNotEmpty) {
|
||||||
final userPinnedPostsText = pinnedPosts.map((item) => item.content).join('\n');
|
final userPinnedPostsText = pinnedPosts.map((item) => item.content).join('\n');
|
||||||
String? addressFromPinnedPost = extractAddressByType(
|
String? addressFromPinnedPost =
|
||||||
raw: userPinnedPostsText, type: CryptoCurrency.fromString(ticker));
|
extractAddressByType(raw: userPinnedPostsText, type: currency);
|
||||||
|
|
||||||
if (addressFromPinnedPost != null) {
|
if (addressFromPinnedPost != null) {
|
||||||
return ParsedAddress.fetchMastodonAddress(
|
return ParsedAddress.fetchMastodonAddress(
|
||||||
|
@ -162,6 +162,16 @@ class AddressResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final thorChainAddress = await ThorChainExchangeProvider.lookupAddressByName(text);
|
||||||
|
if (thorChainAddress != null) {
|
||||||
|
String? address =
|
||||||
|
thorChainAddress[ticker] ?? (ticker == 'RUNE' ? thorChainAddress['THOR'] : null);
|
||||||
|
if (address != null) {
|
||||||
|
return ParsedAddress.thorChainAddress(address: address, name: text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final formattedName = OpenaliasRecord.formatDomainName(text);
|
final formattedName = OpenaliasRecord.formatDomainName(text);
|
||||||
final domainParts = formattedName.split('.');
|
final domainParts = formattedName.split('.');
|
||||||
final name = domainParts.last;
|
final name = domainParts.last;
|
||||||
|
@ -204,7 +214,7 @@ class AddressResolver {
|
||||||
|
|
||||||
if (nostrUserData != null) {
|
if (nostrUserData != null) {
|
||||||
String? addressFromBio = extractAddressByType(
|
String? addressFromBio = extractAddressByType(
|
||||||
raw: nostrUserData.about, type: CryptoCurrency.fromString(ticker));
|
raw: nostrUserData.about, type: currency);
|
||||||
if (addressFromBio != null) {
|
if (addressFromBio != null) {
|
||||||
return ParsedAddress.nostrAddress(
|
return ParsedAddress.nostrAddress(
|
||||||
address: addressFromBio,
|
address: addressFromBio,
|
||||||
|
|
|
@ -11,7 +11,8 @@ enum ParseFrom {
|
||||||
ens,
|
ens,
|
||||||
contact,
|
contact,
|
||||||
mastodon,
|
mastodon,
|
||||||
nostr
|
nostr,
|
||||||
|
thorChain
|
||||||
}
|
}
|
||||||
|
|
||||||
class ParsedAddress {
|
class ParsedAddress {
|
||||||
|
@ -133,6 +134,14 @@ class ParsedAddress {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory ParsedAddress.thorChainAddress({required String address, required String name}) {
|
||||||
|
return ParsedAddress(
|
||||||
|
addresses: [address],
|
||||||
|
name: name,
|
||||||
|
parseFrom: ParseFrom.thorChain,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final List<String> addresses;
|
final List<String> addresses;
|
||||||
final String name;
|
final String name;
|
||||||
final String description;
|
final String description;
|
||||||
|
|
|
@ -34,11 +34,13 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
|
|
||||||
static final isRefundAddressSupported = [CryptoCurrency.eth];
|
static final isRefundAddressSupported = [CryptoCurrency.eth];
|
||||||
|
|
||||||
static const _baseURL = 'thornode.ninerealms.com';
|
static const _baseNodeURL = 'thornode.ninerealms.com';
|
||||||
|
static const _baseURL = 'midgard.ninerealms.com';
|
||||||
static const _quotePath = '/thorchain/quote/swap';
|
static const _quotePath = '/thorchain/quote/swap';
|
||||||
static const _txInfoPath = '/thorchain/tx/status/';
|
static const _txInfoPath = '/thorchain/tx/status/';
|
||||||
static const _affiliateName = 'cakewallet';
|
static const _affiliateName = 'cakewallet';
|
||||||
static const _affiliateBps = '175';
|
static const _affiliateBps = '175';
|
||||||
|
static const _nameLookUpPath= 'v2/thorname/lookup/';
|
||||||
|
|
||||||
final Box<Trade> tradesStore;
|
final Box<Trade> tradesStore;
|
||||||
|
|
||||||
|
@ -154,7 +156,7 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
Future<Trade> findTradeById({required String id}) async {
|
Future<Trade> findTradeById({required String id}) async {
|
||||||
if (id.isEmpty) throw Exception('Trade id is empty');
|
if (id.isEmpty) throw Exception('Trade id is empty');
|
||||||
final formattedId = id.startsWith('0x') ? id.substring(2) : id;
|
final formattedId = id.startsWith('0x') ? id.substring(2) : id;
|
||||||
final uri = Uri.https(_baseURL, '$_txInfoPath$formattedId');
|
final uri = Uri.https(_baseNodeURL, '$_txInfoPath$formattedId');
|
||||||
final response = await http.get(uri);
|
final response = await http.get(uri);
|
||||||
|
|
||||||
if (response.statusCode == 404) {
|
if (response.statusCode == 404) {
|
||||||
|
@ -206,8 +208,35 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, String>?>? lookupAddressByName(String name) async {
|
||||||
|
final uri = Uri.https(_baseURL, '$_nameLookUpPath$name');
|
||||||
|
final response = await http.get(uri);
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final body = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
final entries = body['entries'] as List<dynamic>?;
|
||||||
|
|
||||||
|
if (entries == null || entries.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> chainToAddressMap = {};
|
||||||
|
|
||||||
|
for (final entry in entries) {
|
||||||
|
final chain = entry['chain'] as String;
|
||||||
|
final address = entry['address'] as String;
|
||||||
|
chainToAddressMap[chain] = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chainToAddressMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
|
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
|
||||||
Uri uri = Uri.https(_baseURL, _quotePath, params);
|
Uri uri = Uri.https(_baseNodeURL, _quotePath, params);
|
||||||
|
|
||||||
final response = await http.get(uri);
|
final response = await http.get(uri);
|
||||||
|
|
||||||
|
|
|
@ -330,10 +330,12 @@ class ExchangePage extends BasePage {
|
||||||
|
|
||||||
void applyTemplate(
|
void applyTemplate(
|
||||||
BuildContext context, ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
|
BuildContext context, ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
|
||||||
exchangeViewModel.changeDepositCurrency(
|
|
||||||
currency: CryptoCurrency.fromString(template.depositCurrency));
|
final depositCryptoCurrency = CryptoCurrency.fromString(template.depositCurrency);
|
||||||
exchangeViewModel.changeReceiveCurrency(
|
final receiveCryptoCurrency = CryptoCurrency.fromString(template.receiveCurrency);
|
||||||
currency: CryptoCurrency.fromString(template.receiveCurrency));
|
|
||||||
|
exchangeViewModel.changeDepositCurrency(currency: depositCryptoCurrency);
|
||||||
|
exchangeViewModel.changeReceiveCurrency(currency: receiveCryptoCurrency);
|
||||||
|
|
||||||
exchangeViewModel.changeDepositAmount(amount: template.amount);
|
exchangeViewModel.changeDepositAmount(amount: template.amount);
|
||||||
exchangeViewModel.depositAddress = template.depositAddress;
|
exchangeViewModel.depositAddress = template.depositAddress;
|
||||||
|
@ -342,12 +344,10 @@ class ExchangePage extends BasePage {
|
||||||
exchangeViewModel.isFixedRateMode = false;
|
exchangeViewModel.isFixedRateMode = false;
|
||||||
|
|
||||||
var domain = template.depositAddress;
|
var domain = template.depositAddress;
|
||||||
var ticker = template.depositCurrency.toLowerCase();
|
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, depositCryptoCurrency);
|
||||||
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, ticker);
|
|
||||||
|
|
||||||
domain = template.receiveAddress;
|
domain = template.receiveAddress;
|
||||||
ticker = template.receiveCurrency.toLowerCase();
|
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, receiveCryptoCurrency);
|
||||||
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, ticker);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) {
|
void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) {
|
||||||
|
@ -519,16 +519,14 @@ class ExchangePage extends BasePage {
|
||||||
_depositAddressFocus.addListener(() async {
|
_depositAddressFocus.addListener(() async {
|
||||||
if (!_depositAddressFocus.hasFocus && depositAddressController.text.isNotEmpty) {
|
if (!_depositAddressFocus.hasFocus && depositAddressController.text.isNotEmpty) {
|
||||||
final domain = depositAddressController.text;
|
final domain = depositAddressController.text;
|
||||||
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
|
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||||
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, ticker);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_receiveAddressFocus.addListener(() async {
|
_receiveAddressFocus.addListener(() async {
|
||||||
if (!_receiveAddressFocus.hasFocus && receiveAddressController.text.isNotEmpty) {
|
if (!_receiveAddressFocus.hasFocus && receiveAddressController.text.isNotEmpty) {
|
||||||
final domain = receiveAddressController.text;
|
final domain = receiveAddressController.text;
|
||||||
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
|
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||||
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, ticker);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -575,8 +573,8 @@ class ExchangePage extends BasePage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> fetchParsedAddress(BuildContext context, String domain, String ticker) async {
|
Future<String> fetchParsedAddress(BuildContext context, String domain, CryptoCurrency currency) async {
|
||||||
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, ticker);
|
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
|
||||||
final address = await extractAddressFromParsed(context, parsedAddress);
|
final address = await extractAddressFromParsed(context, parsedAddress);
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
@ -663,15 +661,13 @@ class ExchangePage extends BasePage {
|
||||||
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.depositCurrency),
|
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.depositCurrency),
|
||||||
onPushPasteButton: (context) async {
|
onPushPasteButton: (context) async {
|
||||||
final domain = exchangeViewModel.depositAddress;
|
final domain = exchangeViewModel.depositAddress;
|
||||||
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
|
|
||||||
exchangeViewModel.depositAddress =
|
exchangeViewModel.depositAddress =
|
||||||
await fetchParsedAddress(context, domain, ticker);
|
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||||
},
|
},
|
||||||
onPushAddressBookButton: (context) async {
|
onPushAddressBookButton: (context) async {
|
||||||
final domain = exchangeViewModel.depositAddress;
|
final domain = exchangeViewModel.depositAddress;
|
||||||
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
|
|
||||||
exchangeViewModel.depositAddress =
|
exchangeViewModel.depositAddress =
|
||||||
await fetchParsedAddress(context, domain, ticker);
|
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -712,15 +708,13 @@ class ExchangePage extends BasePage {
|
||||||
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.receiveCurrency),
|
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.receiveCurrency),
|
||||||
onPushPasteButton: (context) async {
|
onPushPasteButton: (context) async {
|
||||||
final domain = exchangeViewModel.receiveAddress;
|
final domain = exchangeViewModel.receiveAddress;
|
||||||
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
|
|
||||||
exchangeViewModel.receiveAddress =
|
exchangeViewModel.receiveAddress =
|
||||||
await fetchParsedAddress(context, domain, ticker);
|
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||||
},
|
},
|
||||||
onPushAddressBookButton: (context) async {
|
onPushAddressBookButton: (context) async {
|
||||||
final domain = exchangeViewModel.receiveAddress;
|
final domain = exchangeViewModel.receiveAddress;
|
||||||
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
|
|
||||||
exchangeViewModel.receiveAddress =
|
exchangeViewModel.receiveAddress =
|
||||||
await fetchParsedAddress(context, domain, ticker);
|
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,11 @@ Future<String> extractAddressFromParsed(
|
||||||
profileImageUrl = parsedAddress.profileImageUrl;
|
profileImageUrl = parsedAddress.profileImageUrl;
|
||||||
profileName = parsedAddress.profileName;
|
profileName = parsedAddress.profileName;
|
||||||
break;
|
break;
|
||||||
|
case ParseFrom.thorChain:
|
||||||
|
title = S.of(context).address_detected;
|
||||||
|
content = S.of(context).extracted_address_content('${parsedAddress.name} (ThorChain)');
|
||||||
|
address = parsedAddress.addresses.first;
|
||||||
|
break;
|
||||||
case ParseFrom.yatRecord:
|
case ParseFrom.yatRecord:
|
||||||
if (parsedAddress.name.isEmpty) {
|
if (parsedAddress.name.isEmpty) {
|
||||||
title = S.of(context).yat_error;
|
title = S.of(context).yat_error;
|
||||||
|
|
|
@ -296,8 +296,8 @@ abstract class OutputBase with Store {
|
||||||
|
|
||||||
Future<void> fetchParsedAddress(BuildContext context) async {
|
Future<void> fetchParsedAddress(BuildContext context) async {
|
||||||
final domain = address;
|
final domain = address;
|
||||||
final ticker = cryptoCurrencyHandler().title.toLowerCase();
|
final currency = cryptoCurrencyHandler();
|
||||||
parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, ticker);
|
parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
|
||||||
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
|
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
|
||||||
note = parsedAddress.description;
|
note = parsedAddress.description;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue