mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-29 06:38:45 +01:00
Merge pull request #152 from cake-tech/CAKE-192-implement-unstoppable-domain
Cake 192 implement unstoppable domain
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import 'package:cake_wallet/entities/openalias_record.dart';
|
||||
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
|
||||
|
||||
const topLevelDomain = 'crypto';
|
||||
|
||||
Future<ParsedAddress> parseAddressFromDomain(
|
||||
String domain, String ticker) async {
|
||||
try {
|
||||
final formattedName = OpenaliasRecord.formatDomainName(domain);
|
||||
final domainParts = formattedName.split('.');
|
||||
final name = domainParts.last;
|
||||
|
||||
if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) {
|
||||
return ParsedAddress(address: domain);
|
||||
}
|
||||
|
||||
if (name.contains(topLevelDomain)) {
|
||||
final address =
|
||||
await fetchUnstoppableDomainAddress(domain, ticker);
|
||||
|
||||
if (address?.isEmpty ?? true) {
|
||||
return ParsedAddress(address: domain);
|
||||
}
|
||||
|
||||
return ParsedAddress(
|
||||
address: address,
|
||||
name: domain,
|
||||
parseFrom: ParseFrom.unstoppableDomains);
|
||||
}
|
||||
|
||||
final record = await OpenaliasRecord.fetchAddressAndName(formattedName);
|
||||
|
||||
if (record == null || record.address.contains(formattedName)) {
|
||||
return ParsedAddress(address: domain);
|
||||
}
|
||||
|
||||
return ParsedAddress(
|
||||
address: record.address,
|
||||
name: record.name,
|
||||
parseFrom: ParseFrom.openAlias);
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
|
||||
return ParsedAddress(address: domain);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
enum ParseFrom {unstoppableDomains, openAlias, notParsed}
|
||||
|
||||
class ParsedAddress {
|
||||
ParsedAddress({
|
||||
this.address = '',
|
||||
this.name = '',
|
||||
this.parseFrom = ParseFrom.notParsed});
|
||||
|
||||
final String address;
|
||||
final String name;
|
||||
final ParseFrom parseFrom;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
const channel = MethodChannel('com.cake_wallet/native_utils');
|
||||
|
||||
Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async {
|
||||
var address = '';
|
||||
|
||||
try {
|
||||
address = await channel.invokeMethod(
|
||||
'getUnstoppableDomainAddress',
|
||||
<String, String> {
|
||||
'domain' : domain,
|
||||
'ticker' : ticker
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
print('Unstoppable domain error: ${e.toString()}');
|
||||
address = '';
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'dart:ui';
|
||||
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||
import 'package:cake_wallet/entities/sync_status.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@@ -9,8 +11,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_template.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/limits_state.dart';
|
||||
@@ -23,7 +23,6 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
@@ -42,7 +41,9 @@ class ExchangePage extends BasePage {
|
||||
final checkBoxKey = GlobalKey<StandardCheckboxState>();
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _depositAmountFocus = FocusNode();
|
||||
final _depositAddressFocus = FocusNode();
|
||||
final _receiveAmountFocus = FocusNode();
|
||||
final _receiveAddressFocus = FocusNode();
|
||||
var _isReactionsSet = false;
|
||||
|
||||
@override
|
||||
@@ -171,6 +172,7 @@ class ExchangePage extends BasePage {
|
||||
.calculateDepositAllAmount()
|
||||
: null,
|
||||
amountFocusNode: _depositAmountFocus,
|
||||
addressFocusNode: _depositAddressFocus,
|
||||
key: depositKey,
|
||||
title: S.of(context).you_will_send,
|
||||
initialCurrency:
|
||||
@@ -223,6 +225,15 @@ class ExchangePage extends BasePage {
|
||||
type: exchangeViewModel.wallet.type),
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type: exchangeViewModel.depositCurrency),
|
||||
onPushPasteButton: (context) async {
|
||||
final domain =
|
||||
exchangeViewModel.depositAddress;
|
||||
final ticker = exchangeViewModel
|
||||
.depositCurrency.title.toLowerCase();
|
||||
exchangeViewModel.depositAddress =
|
||||
await applyOpenaliasOrUnstoppableDomains(
|
||||
context, domain, ticker);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -232,6 +243,7 @@ class ExchangePage extends BasePage {
|
||||
child: Observer(
|
||||
builder: (_) => ExchangeCard(
|
||||
amountFocusNode: _receiveAmountFocus,
|
||||
addressFocusNode: _receiveAddressFocus,
|
||||
key: receiveKey,
|
||||
title: S.of(context).you_will_get,
|
||||
initialCurrency:
|
||||
@@ -268,6 +280,15 @@ class ExchangePage extends BasePage {
|
||||
AddressValidator(
|
||||
type: exchangeViewModel
|
||||
.receiveCurrency),
|
||||
onPushPasteButton: (context) async {
|
||||
final domain =
|
||||
exchangeViewModel.receiveAddress;
|
||||
final ticker = exchangeViewModel
|
||||
.receiveCurrency.title.toLowerCase();
|
||||
exchangeViewModel.receiveAddress =
|
||||
await applyOpenaliasOrUnstoppableDomains(
|
||||
context, domain, ticker);
|
||||
},
|
||||
)),
|
||||
)
|
||||
],
|
||||
@@ -371,7 +392,7 @@ class ExchangePage extends BasePage {
|
||||
from: template.depositCurrency,
|
||||
to: template.receiveCurrency,
|
||||
onTap: () {
|
||||
applyTemplate(
|
||||
applyTemplate(context,
|
||||
exchangeViewModel, template);
|
||||
},
|
||||
onRemove: () {
|
||||
@@ -472,8 +493,8 @@ class ExchangePage extends BasePage {
|
||||
));
|
||||
}
|
||||
|
||||
void applyTemplate(
|
||||
ExchangeViewModel exchangeViewModel, ExchangeTemplate template) {
|
||||
void applyTemplate(BuildContext context,
|
||||
ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
|
||||
exchangeViewModel.changeDepositCurrency(
|
||||
currency: CryptoCurrency.fromString(template.depositCurrency));
|
||||
exchangeViewModel.changeReceiveCurrency(
|
||||
@@ -491,6 +512,16 @@ class ExchangePage extends BasePage {
|
||||
exchangeViewModel.receiveAddress = template.receiveAddress;
|
||||
exchangeViewModel.isReceiveAmountEntered = false;
|
||||
exchangeViewModel.isFixedRateMode = false;
|
||||
|
||||
var domain = template.depositAddress;
|
||||
var ticker = template.depositCurrency.toLowerCase();
|
||||
exchangeViewModel.depositAddress =
|
||||
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
|
||||
|
||||
domain = template.receiveAddress;
|
||||
ticker = template.receiveCurrency.toLowerCase();
|
||||
exchangeViewModel.receiveAddress =
|
||||
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
|
||||
}
|
||||
|
||||
void _setReactions(
|
||||
@@ -657,6 +688,26 @@ class ExchangePage extends BasePage {
|
||||
}
|
||||
});
|
||||
|
||||
_depositAddressFocus.addListener(() async {
|
||||
if (!_depositAddressFocus.hasFocus &&
|
||||
depositAddressController.text.isNotEmpty) {
|
||||
final domain = depositAddressController.text;
|
||||
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
|
||||
exchangeViewModel.depositAddress =
|
||||
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
|
||||
}
|
||||
});
|
||||
|
||||
_receiveAddressFocus.addListener(() async {
|
||||
if (!_receiveAddressFocus.hasFocus &&
|
||||
receiveAddressController.text.isNotEmpty) {
|
||||
final domain = receiveAddressController.text;
|
||||
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
|
||||
exchangeViewModel.receiveAddress =
|
||||
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
|
||||
}
|
||||
});
|
||||
|
||||
_receiveAmountFocus.addListener(() {
|
||||
if (_receiveAmountFocus.hasFocus && !exchangeViewModel.isFixedRateMode) {
|
||||
showPopUp<void>(
|
||||
@@ -728,4 +779,28 @@ class ExchangePage extends BasePage {
|
||||
key.currentState.addressController.text = null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> applyOpenaliasOrUnstoppableDomains(
|
||||
BuildContext context, String domain, String ticker) async {
|
||||
final parsedAddress = await parseAddressFromDomain(domain, ticker);
|
||||
|
||||
switch (parsedAddress.parseFrom) {
|
||||
case ParseFrom.unstoppableDomains:
|
||||
showAddressAlert(
|
||||
context,
|
||||
S.of(context).address_detected,
|
||||
S.of(context).address_from_domain(parsedAddress.name));
|
||||
break;
|
||||
case ParseFrom.openAlias:
|
||||
showAddressAlert(
|
||||
context,
|
||||
S.of(context).openalias_alert_title,
|
||||
S.of(context).openalias_alert_content(parsedAddress.name));
|
||||
break;
|
||||
case ParseFrom.notParsed:
|
||||
break;
|
||||
}
|
||||
|
||||
return parsedAddress.address;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,9 +150,8 @@ class ExchangeTemplatePage extends BasePage {
|
||||
.color,
|
||||
currencyValueValidator: AmountValidator(
|
||||
type: exchangeViewModel.wallet.type),
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type:
|
||||
exchangeViewModel.depositCurrency),
|
||||
//addressTextFieldValidator: AddressValidator(
|
||||
// type: exchangeViewModel.depositCurrency),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -190,8 +189,8 @@ class ExchangeTemplatePage extends BasePage {
|
||||
.decorationColor,
|
||||
currencyValueValidator: AmountValidator(
|
||||
type: exchangeViewModel.wallet.type),
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type: exchangeViewModel.receiveCurrency),
|
||||
//addressTextFieldValidator: AddressValidator(
|
||||
// type: exchangeViewModel.receiveCurrency),
|
||||
)),
|
||||
)
|
||||
],
|
||||
|
||||
@@ -28,8 +28,10 @@ class ExchangeCard extends StatefulWidget {
|
||||
this.currencyValueValidator,
|
||||
this.addressTextFieldValidator,
|
||||
this.amountFocusNode,
|
||||
this.addressFocusNode,
|
||||
this.hasAllAmount = false,
|
||||
this.allAmount})
|
||||
this.allAmount,
|
||||
this.onPushPasteButton})
|
||||
: super(key: key);
|
||||
|
||||
final List<CryptoCurrency> currencies;
|
||||
@@ -49,8 +51,10 @@ class ExchangeCard extends StatefulWidget {
|
||||
final FormFieldValidator<String> currencyValueValidator;
|
||||
final FormFieldValidator<String> addressTextFieldValidator;
|
||||
final FocusNode amountFocusNode;
|
||||
final FocusNode addressFocusNode;
|
||||
final bool hasAllAmount;
|
||||
Function allAmount;
|
||||
final Function allAmount;
|
||||
final Function(BuildContext context) onPushPasteButton;
|
||||
|
||||
@override
|
||||
ExchangeCardState createState() => ExchangeCardState();
|
||||
@@ -288,6 +292,7 @@ class ExchangeCardState extends State<ExchangeCard> {
|
||||
? Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: AddressTextField(
|
||||
focusNode: widget.addressFocusNode,
|
||||
controller: addressController,
|
||||
placeholder: widget.hasRefundAddress
|
||||
? S.of(context).refund_address
|
||||
@@ -311,6 +316,7 @@ class ExchangeCardState extends State<ExchangeCard> {
|
||||
.decorationColor),
|
||||
buttonColor: widget.addressButtonsColor,
|
||||
validator: widget.addressTextFieldValidator,
|
||||
onPushPasteButton: widget.onPushPasteButton,
|
||||
),
|
||||
)
|
||||
: Padding(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'dart:ui';
|
||||
import 'package:cake_wallet/entities/monero_transaction_priority.dart';
|
||||
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||
import 'package:cake_wallet/entities/transaction_priority.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
@@ -40,7 +42,7 @@ class SendPage extends BasePage {
|
||||
_addressFocusNode = FocusNode() {
|
||||
_addressFocusNode.addListener(() {
|
||||
if (!_addressFocusNode.hasFocus && _addressController.text.isNotEmpty) {
|
||||
getOpenaliasRecord(_addressFocusNode.context);
|
||||
applyOpenaliasOrUnstoppableDomains(_addressFocusNode.context);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -173,6 +175,9 @@ class SendPage extends BasePage {
|
||||
.headline
|
||||
.decorationColor),
|
||||
validator: sendViewModel.addressValidator,
|
||||
onPushPasteButton: (context) {
|
||||
applyOpenaliasOrUnstoppableDomains(context);
|
||||
},
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => Padding(
|
||||
@@ -518,7 +523,7 @@ class SendPage extends BasePage {
|
||||
template.address;
|
||||
_cryptoAmountController.text =
|
||||
template.amount;
|
||||
getOpenaliasRecord(context);
|
||||
applyOpenaliasOrUnstoppableDomains(context);
|
||||
},
|
||||
onRemove: () {
|
||||
showPopUp<void>(
|
||||
@@ -733,26 +738,6 @@ class SendPage extends BasePage {
|
||||
_effectsInstalled = true;
|
||||
}
|
||||
|
||||
Future<void> getOpenaliasRecord(BuildContext context) async {
|
||||
final record =
|
||||
await sendViewModel.decodeOpenaliasRecord(_addressController.text);
|
||||
|
||||
if (record != null) {
|
||||
_addressController.text = record.address;
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).openalias_alert_title,
|
||||
alertContent:
|
||||
S.of(context).openalias_alert_content(record.name),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _setTransactionPriority(BuildContext context) async {
|
||||
final items = priorityForWalletType(sendViewModel.walletType);
|
||||
final selectedItem = items.indexOf(sendViewModel.transactionPriority);
|
||||
@@ -770,4 +755,28 @@ class SendPage extends BasePage {
|
||||
),
|
||||
context: context);
|
||||
}
|
||||
|
||||
void applyOpenaliasOrUnstoppableDomains(BuildContext context) async {
|
||||
final domain = _addressController.text;
|
||||
final ticker = sendViewModel.currency.title.toLowerCase();
|
||||
final parsedAddress = await parseAddressFromDomain(domain, ticker);
|
||||
_addressController.text = parsedAddress.address;
|
||||
|
||||
switch (parsedAddress.parseFrom) {
|
||||
case ParseFrom.unstoppableDomains:
|
||||
showAddressAlert(
|
||||
context,
|
||||
S.of(context).address_detected,
|
||||
S.of(context).address_from_domain(parsedAddress.name));
|
||||
break;
|
||||
case ParseFrom.openAlias:
|
||||
showAddressAlert(
|
||||
context,
|
||||
S.of(context).openalias_alert_title,
|
||||
S.of(context).openalias_alert_content(parsedAddress.name));
|
||||
break;
|
||||
case ParseFrom.notParsed:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ class SendTemplatePage extends BasePage {
|
||||
.primaryTextTheme
|
||||
.headline
|
||||
.decorationColor),
|
||||
validator: sendViewModel.addressValidator,
|
||||
//validator: sendViewModel.addressValidator,
|
||||
),
|
||||
),
|
||||
Observer(builder: (_) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
void showAddressAlert(BuildContext context, String title, String content) async {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
|
||||
return AlertWithOneAction(
|
||||
alertTitle: title,
|
||||
alertContent: content,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
}
|
||||
@@ -24,7 +24,8 @@ class AddressTextField extends StatelessWidget {
|
||||
this.iconColor,
|
||||
this.textStyle,
|
||||
this.hintStyle,
|
||||
this.validator});
|
||||
this.validator,
|
||||
this.onPushPasteButton});
|
||||
|
||||
static const prefixIconWidth = 34.0;
|
||||
static const prefixIconHeight = 34.0;
|
||||
@@ -43,6 +44,7 @@ class AddressTextField extends StatelessWidget {
|
||||
final TextStyle textStyle;
|
||||
final TextStyle hintStyle;
|
||||
final FocusNode focusNode;
|
||||
final Function(BuildContext context) onPushPasteButton;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -225,5 +227,7 @@ class AddressTextField extends StatelessWidget {
|
||||
if (address?.isNotEmpty ?? false) {
|
||||
controller.text = address;
|
||||
}
|
||||
|
||||
onPushPasteButton?.call(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
import 'package:cake_wallet/entities/transaction_priority.dart';
|
||||
@@ -10,7 +9,6 @@ import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/entities/openalias_record.dart';
|
||||
import 'package:cake_wallet/entities/template.dart';
|
||||
import 'package:cake_wallet/store/templates/send_template_store.dart';
|
||||
import 'package:cake_wallet/core/template_validator.dart';
|
||||
@@ -20,7 +18,6 @@ import 'package:cake_wallet/core/pending_transaction.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_credentials.dart';
|
||||
import 'package:cake_wallet/monero/monero_wallet.dart';
|
||||
import 'package:cake_wallet/monero/monero_transaction_creation_credentials.dart';
|
||||
@@ -264,13 +261,6 @@ abstract class SendViewModelBase with Store {
|
||||
void setTransactionPriority(TransactionPriority priority) =>
|
||||
_settingsStore.priority[_wallet.type] = priority;
|
||||
|
||||
Future<OpenaliasRecord> decodeOpenaliasRecord(String name) async {
|
||||
final record = await OpenaliasRecord.fetchAddressAndName(
|
||||
OpenaliasRecord.formatDomainName(name));
|
||||
|
||||
return record.name != name ? record : null;
|
||||
}
|
||||
|
||||
@action
|
||||
void _updateFiatAmount() {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user