mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Merge branch 'dev' into cw-1497-transaction-history-improved-layout
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
|
||||
import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
|
||||
import 'package:cake_wallet/main.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
@@ -16,7 +19,8 @@ part 'contact_view_model.g.dart';
|
||||
class ContactViewModel = ContactViewModelBase with _$ContactViewModel;
|
||||
|
||||
abstract class ContactViewModelBase with Store {
|
||||
ContactViewModelBase(this._contacts, {ContactRecord? contact})
|
||||
ContactViewModelBase(this._contacts, this.appStore,this.adrResService,
|
||||
{ContactRecord? contact})
|
||||
: state = InitialExecutionState(),
|
||||
currencies = CryptoCurrency.all,
|
||||
_contact = contact,
|
||||
@@ -26,6 +30,9 @@ abstract class ContactViewModelBase with Store {
|
||||
currency = contact?.type,
|
||||
lastChange = contact?.lastChange;
|
||||
|
||||
final AppStore appStore;
|
||||
final AddressResolverService adrResService;
|
||||
|
||||
|
||||
@observable
|
||||
ExecutionState state;
|
||||
@@ -58,16 +65,52 @@ abstract class ContactViewModelBase with Store {
|
||||
void reset() {
|
||||
address = '';
|
||||
name = '';
|
||||
displayName = '';
|
||||
currency = null;
|
||||
}
|
||||
|
||||
Future<void> extractParsedAddress(BuildContext context) async{
|
||||
if(currency == null) return;
|
||||
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, address, currency!);
|
||||
if(parsedAddress.name.isNotEmpty) {
|
||||
displayName = parsedAddress.name;
|
||||
Future<ParsedAddress?> extractParsedAddress(BuildContext context) async {
|
||||
final wallet = appStore.wallet;
|
||||
final currentCurrency = currency;
|
||||
final query = address.trim();
|
||||
|
||||
if (wallet == null) return null;
|
||||
if (currentCurrency == null) return null;
|
||||
if (query.isEmpty) return null;
|
||||
|
||||
|
||||
// Check if the address is valid for the current currency (except for Zano, which can use handles as addresses)
|
||||
if (currentCurrency != CryptoCurrency.zano) {
|
||||
final isValidAddress = AddressValidator(type: currentCurrency).isValid(query);
|
||||
if (isValidAddress) return null;
|
||||
}
|
||||
address = await extractAddressFromParsed(null, parsedAddress);
|
||||
|
||||
final parsedAddresses = await adrResService.resolve(
|
||||
query: query,
|
||||
wallet: wallet,
|
||||
currency: currentCurrency,
|
||||
);
|
||||
|
||||
if (parsedAddresses.isEmpty) return null;
|
||||
|
||||
final resolvedAddress = parsedAddresses.first.parsedAddressByCurrencyMap[currentCurrency];
|
||||
if (resolvedAddress == null || resolvedAddress.isEmpty) return null;
|
||||
|
||||
return parsedAddresses.first;
|
||||
}
|
||||
|
||||
@action
|
||||
void applyParsedAddress(ParsedAddress parsedAddress) {
|
||||
final currentCurrency = currency;
|
||||
if (currentCurrency == null) return;
|
||||
|
||||
final resolvedAddress = parsedAddress.parsedAddressByCurrencyMap[currentCurrency];
|
||||
if (resolvedAddress == null || resolvedAddress.isEmpty) return;
|
||||
|
||||
address = resolvedAddress;
|
||||
displayName = parsedAddress.profileName.isNotEmpty
|
||||
? parsedAddress.profileName
|
||||
: parsedAddress.handle;
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'dart:io' show Platform;
|
||||
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/address_resolver/yat/yat_store.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/date_section_item.dart';
|
||||
import "package:cw_core/balance_card_style_settings.dart";
|
||||
@@ -36,7 +37,6 @@ import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||
|
||||
@@ -55,6 +55,12 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
}
|
||||
|
||||
String get formattedTitle {
|
||||
|
||||
if (balanceViewModel.wallet.type == WalletType.bitcoin &&
|
||||
transaction.additionalInfo['hasMissingInputTx'] == true) {
|
||||
return 'Transaction has missing data';
|
||||
}
|
||||
|
||||
if (transaction.additionalInfo['autoShield'] == true) {
|
||||
return "Autoshield";
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/core/amount_parsing_proxy.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
@@ -24,6 +25,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:cake_wallet/utils/exchange_provider_logger.dart';
|
||||
import 'package:cake_wallet/utils/qr_util.dart';
|
||||
import 'package:cake_wallet/utils/token_utilities.dart';
|
||||
import 'package:cake_wallet/view_model/send/fees_view_model.dart';
|
||||
@@ -48,7 +50,6 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
required this.feesViewModel,
|
||||
required this.fiatConversionStore,
|
||||
}) : trade = tradesStore.trade!,
|
||||
isSendable = _checkIfCanSend(tradesStore, wallet),
|
||||
isSwapsXYZCanSendFromExternal = _checkIfSwapsXYZCanSendFromExternal(tradesStore.trade!, wallet),
|
||||
items = ObservableList<ExchangeTradeItem>() {
|
||||
setUpOutput();
|
||||
@@ -115,12 +116,10 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
@observable
|
||||
Trade trade;
|
||||
|
||||
@observable
|
||||
bool isSendable;
|
||||
|
||||
|
||||
bool isSwapsXYZCanSendFromExternal;
|
||||
|
||||
bool get isSendable => checkIfCanSend(trade, wallet) == null;
|
||||
|
||||
/// Providers that should hide the "send from external" button
|
||||
static const List<Type> _providersThatHideExternalSend = [
|
||||
JupiterExchangeProvider,
|
||||
@@ -212,7 +211,13 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
|
||||
@action
|
||||
Future<void> confirmSending() async {
|
||||
if (!isSendable) return;
|
||||
final canSendError = checkIfCanSend(trade, wallet);
|
||||
|
||||
if (canSendError != null) {
|
||||
_logCanSendError(trade, wallet, canSendError);
|
||||
sendViewModel.state = FailureState(canSendError);
|
||||
return;
|
||||
}
|
||||
|
||||
final selected = trade.from;
|
||||
if (selected == null) {
|
||||
@@ -348,48 +353,56 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
);
|
||||
}
|
||||
|
||||
static bool _checkIfCanSend(TradesStore tradesStore, WalletBase wallet) {
|
||||
final trade = tradesStore.trade!;
|
||||
String? checkIfCanSend(Trade? trade, WalletBase wallet) {
|
||||
if (trade == null) return 'Trade is null';
|
||||
|
||||
final tradeFrom = trade.from;
|
||||
if (tradeFrom == null) return 'Trade from currency is null';
|
||||
|
||||
bool _sameCurrency(CryptoCurrency? a, CryptoCurrency? b) =>
|
||||
a != null && b != null && a.titleAndTagEqual(b);
|
||||
bool _sameCurrency(CryptoCurrency a, CryptoCurrency b) => a.titleAndTagEqual(b);
|
||||
|
||||
bool _isEthToken() =>
|
||||
wallet.currency == CryptoCurrency.eth && tradeFrom?.tag == CryptoCurrency.eth.title;
|
||||
bool _isTokenBelongingToWallet(CryptoCurrency cur) {
|
||||
final chainTag = cur.tag ?? cur.title;
|
||||
return wallet.currency == cur &&
|
||||
(tradeFrom.tag?.toUpperCase() == chainTag.toUpperCase() ||
|
||||
tradeFrom.title.toUpperCase() == chainTag.toUpperCase());
|
||||
}
|
||||
|
||||
bool _isPolygonToken() =>
|
||||
wallet.currency == CryptoCurrency.maticpoly &&
|
||||
tradeFrom?.tag == CryptoCurrency.maticpoly.tag;
|
||||
|
||||
bool _isBaseToken() =>
|
||||
wallet.currency == CryptoCurrency.baseEth && tradeFrom?.tag == CryptoCurrency.baseEth.tag;
|
||||
|
||||
bool _isArbitrumToken() =>
|
||||
wallet.currency == CryptoCurrency.arbEth &&
|
||||
(tradeFrom?.tag == CryptoCurrency.arbEth.tag ||
|
||||
tradeFrom?.title == CryptoCurrency.arbEth.tag); // This is to handle the CryptoCurrency.arb that doesn't have a tag but fully belongs to the Arbitrum chain
|
||||
|
||||
bool _isTronToken() =>
|
||||
wallet.currency == CryptoCurrency.trx && tradeFrom?.tag == CryptoCurrency.trx.title;
|
||||
|
||||
bool _isSplToken() =>
|
||||
wallet.currency == CryptoCurrency.sol && tradeFrom?.tag == CryptoCurrency.sol.title;
|
||||
|
||||
bool _isBscToken() =>
|
||||
wallet.currency == CryptoCurrency.bnb && tradeFrom?.tag == CryptoCurrency.bnb.tag;
|
||||
|
||||
return _sameCurrency(tradeFrom, wallet.currency) ||
|
||||
final canSend = _sameCurrency(tradeFrom, wallet.currency) ||
|
||||
(_sameCurrency(tradeFrom, CryptoCurrency.btcln) &&
|
||||
wallet.currency == CryptoCurrency.btc) ||
|
||||
tradesStore.trade!.provider == ExchangeProviderDescription.xmrto ||
|
||||
_isEthToken() ||
|
||||
_isPolygonToken() ||
|
||||
_isSplToken() ||
|
||||
_isTronToken() ||
|
||||
_isBaseToken() ||
|
||||
_isArbitrumToken() ||
|
||||
_isBscToken();
|
||||
trade.provider == ExchangeProviderDescription.xmrto ||
|
||||
_isTokenBelongingToWallet(CryptoCurrency.eth) ||
|
||||
_isTokenBelongingToWallet(CryptoCurrency.maticpoly) ||
|
||||
_isTokenBelongingToWallet(CryptoCurrency.baseEth) ||
|
||||
_isTokenBelongingToWallet(CryptoCurrency.arbEth) ||
|
||||
_isTokenBelongingToWallet(CryptoCurrency.trx) ||
|
||||
_isTokenBelongingToWallet(CryptoCurrency.sol) ||
|
||||
_isTokenBelongingToWallet(CryptoCurrency.bnb);
|
||||
|
||||
if (!canSend) {
|
||||
return 'Wallet currency ${wallet.currency.title} does not match trade from currency ${tradeFrom.title} or is not a supported token for this wallet.';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void _logCanSendError(Trade? trade, WalletBase wallet, String error) {
|
||||
ExchangeProviderLogger.logError(
|
||||
provider: trade?.provider,
|
||||
function: '_checkIfCanSend',
|
||||
error: error,
|
||||
requestData: {
|
||||
'tradeId': trade?.id,
|
||||
'tradeFrom': trade?.from?.title,
|
||||
'tradeFromTag': trade?.from?.tag,
|
||||
'tradeTo': trade?.to?.title,
|
||||
'tradeToTag': trade?.to?.tag,
|
||||
'walletName': wallet.name,
|
||||
'walletCurrency': wallet.currency.title,
|
||||
'walletCurrencyTag': wallet.currency.tag,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static bool _checkIfSwapsXYZCanSendFromExternal(Trade trade, WalletBase wallet) {
|
||||
@@ -415,9 +428,13 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Using trade currency here so the external-send QR encodes the correct scheme
|
||||
final uriWalletType =
|
||||
cryptoCurrencyOrTokenToWalletType(fromCurrency) ?? wallet.type;
|
||||
// Using the trade's `from` currency so the external-send QR encodes the correct scheme.
|
||||
final uriWalletType = cryptoCurrencyOrTokenToWalletType(fromCurrency);
|
||||
|
||||
// for other currencies that we don't have wallets for
|
||||
if (uriWalletType == null) {
|
||||
return ExternalAddressURI(address: inputAddress, amount: amount);
|
||||
}
|
||||
|
||||
printV(uriWalletType);
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/utils/exchange_provider_logger.dart';
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/amount/money_double.dart';
|
||||
import "package:cw_core/wallet_info.dart";
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
@@ -804,7 +805,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
|
||||
final amount_ = _enteredAmount / (forcedProvider == null ? bestRate : forcedProviderRate);
|
||||
_depositAmount = Money.tryParse(amount_.toStringAsFixed(depositCurrency.decimals), depositCurrency);
|
||||
_depositAmount = amount_.tryToMoney(depositCurrency);
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -863,8 +864,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
|
||||
final amount_ = _enteredAmount * (forcedProvider == null ? bestRate : forcedProviderRate);
|
||||
_receiveAmount =
|
||||
Money.tryParse(amount_.toStringAsFixed(min(20, receiveCurrency.decimals)), receiveCurrency);
|
||||
_receiveAmount = amount_.tryToMoney(receiveCurrency);
|
||||
}
|
||||
|
||||
bool checkIfInputMeetsMinOrMaxCondition(String input) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/core/address_resolver/address_resolver_utils.dart';
|
||||
import 'package:cake_wallet/core/seed_validator.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/entities/qr_scanner.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
@@ -87,7 +87,7 @@ class WalletRestoreFromQRCode {
|
||||
|
||||
static String? _extractAddressFromUrl(String rawString, WalletType type) {
|
||||
try {
|
||||
return AddressResolver.extractAddressByType(
|
||||
return AddressResolverUtils.extractAddressByType(
|
||||
raw: rawString,
|
||||
type: walletTypeToCryptoCurrency(
|
||||
type,
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import 'dart:math' show min;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/address_resolver/address_sources.dart';
|
||||
import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
|
||||
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
|
||||
import 'package:cake_wallet/entities/contact_base.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
@@ -37,7 +37,8 @@ class Output = OutputBase with _$Output;
|
||||
|
||||
abstract class OutputBase with Store {
|
||||
OutputBase(this._wallet, this._appStore, this._fiatConversationStore, this.cryptoCurrencyHandler)
|
||||
: key = UniqueKey(),
|
||||
:
|
||||
key = UniqueKey(),
|
||||
sendAll = false,
|
||||
cryptoAmount = '',
|
||||
cryptoFullBalance = '',
|
||||
@@ -47,7 +48,7 @@ abstract class OutputBase with Store {
|
||||
memo = "",
|
||||
extractedAddress = '',
|
||||
estimatedFee = Money.zero(cryptoCurrencyHandler()),
|
||||
parsedAddress = ParsedAddress(addresses: []) {
|
||||
parsedAddress = ParsedAddress(parsedAddressByCurrencyMap: {}) {
|
||||
autorun((_) {
|
||||
final status = _wallet.syncStatus;
|
||||
printV("Sync status changed to $status. Recalculating fees");
|
||||
@@ -56,6 +57,7 @@ abstract class OutputBase with Store {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Key key;
|
||||
|
||||
bool get useSatoshi => _appStore.amountParsingProxy.useSatoshi(cryptoCurrencyHandler());
|
||||
@@ -83,7 +85,7 @@ abstract class OutputBase with Store {
|
||||
|
||||
@observable
|
||||
String note;
|
||||
|
||||
|
||||
@observable
|
||||
String memo;
|
||||
|
||||
@@ -99,7 +101,7 @@ abstract class OutputBase with Store {
|
||||
|
||||
@computed
|
||||
bool get isParsedAddress =>
|
||||
parsedAddress.parseFrom != ParseFrom.notParsed && parsedAddress.name.isNotEmpty;
|
||||
parsedAddress.addressSource != AddressSource.notParsed && parsedAddress.handle.isNotEmpty;
|
||||
|
||||
String roundedCryptoAmount(int digits) => displayCryptoAmount.withMaxDecimals(digits);
|
||||
|
||||
@@ -272,10 +274,22 @@ abstract class OutputBase with Store {
|
||||
resetParsedAddress();
|
||||
}
|
||||
|
||||
@action
|
||||
void resetParsedAddress() {
|
||||
displayName = null;
|
||||
extractedAddress = '';
|
||||
parsedAddress = ParsedAddress(addresses: []);
|
||||
note = '';
|
||||
parsedAddress = ParsedAddress(parsedAddressByCurrencyMap: {});
|
||||
}
|
||||
|
||||
@action
|
||||
void applyAddressLookupResult(ParsedAddress result) {
|
||||
final currency = cryptoCurrencyHandler();
|
||||
|
||||
parsedAddress = result;
|
||||
extractedAddress = result.parsedAddressByCurrencyMap[currency] ?? '';
|
||||
note = result.description;
|
||||
displayName = result.profileName.isNotEmpty ? result.profileName : result.handle;
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -334,33 +348,26 @@ abstract class OutputBase with Store {
|
||||
|
||||
Map<String, dynamic> get extra {
|
||||
final fields = <String, dynamic>{};
|
||||
if (parsedAddress.parseFrom == ParseFrom.bip353) {
|
||||
fields['bip353_name'] = parsedAddress.name;
|
||||
if (parsedAddress.addressSource == AddressSource.bip353) {
|
||||
fields['bip353_name'] = parsedAddress.handle;
|
||||
fields['bip353_proof'] = parsedAddress.bip353DnsProof;
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
Future<void> fetchParsedAddress(BuildContext context) async {
|
||||
final domain = address;
|
||||
final currency = cryptoCurrencyHandler();
|
||||
parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
|
||||
if(parsedAddress.name.isNotEmpty) {
|
||||
displayName = parsedAddress.name;
|
||||
}
|
||||
|
||||
if (parsedAddress.parseFrom == ParseFrom.lnurlpay) {
|
||||
cryptoCurrencyHandler(CryptoCurrency.btcln);
|
||||
}
|
||||
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
|
||||
note = parsedAddress.description;
|
||||
}
|
||||
|
||||
@action
|
||||
void loadContact(ContactBase contact) {
|
||||
address = contact.name;
|
||||
parsedAddress = ParsedAddress.fetchContactAddress(address: contact.address, name: contact.name);
|
||||
extractedAddress = parsedAddress.addresses.first;
|
||||
note = parsedAddress.description;
|
||||
final currency = cryptoCurrencyHandler();
|
||||
|
||||
address = contact.address;
|
||||
applyAddressLookupResult(
|
||||
ParsedAddress(
|
||||
parsedAddressByCurrencyMap: {currency: contact.address},
|
||||
addressSource: AddressSource.contact,
|
||||
handle: contact.name,
|
||||
profileName: contact.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/address_resolver/parsed_address.dart';
|
||||
import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/core/amount_parsing_proxy.dart';
|
||||
import 'package:cake_wallet/core/amount_validator.dart';
|
||||
@@ -17,7 +20,6 @@ import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/evm_transaction_error_fees_handler.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/template.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
@@ -103,6 +105,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
this._appStore,
|
||||
this.sendTemplateViewModel,
|
||||
this._fiatConversationStore,
|
||||
this._adrResService,
|
||||
this.balanceViewModel,
|
||||
this.contactListViewModel,
|
||||
this.transactionDescriptionBox,
|
||||
@@ -182,6 +185,26 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
if (isBatchSending) outputs.remove(output);
|
||||
}
|
||||
|
||||
Future<ParsedAddress?> resolveAddressForOutput(Output output) async {
|
||||
final query = output.address.trim();
|
||||
if (query.isEmpty) return null;
|
||||
|
||||
// Check if the address is valid for the current currency (except for Zano, which can use handles as addresses)
|
||||
if (selectedCryptoCurrency != CryptoCurrency.zano) {
|
||||
final isValidAddress = AddressValidator(type: selectedCryptoCurrency).isValid(query);
|
||||
if (isValidAddress) return null;
|
||||
}
|
||||
|
||||
|
||||
final results = await _adrResService.resolve(
|
||||
query: query,
|
||||
wallet: wallet,
|
||||
currency: selectedCryptoCurrency,
|
||||
);
|
||||
|
||||
return results.isEmpty ? null : results.first;
|
||||
}
|
||||
|
||||
@action
|
||||
void clearOutputs() {
|
||||
outputs.clear();
|
||||
@@ -383,14 +406,17 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
// If silent payments scanning, can still send payments
|
||||
(wallet.type == WalletType.bitcoin && wallet.syncStatus is SyncingSyncStatus);
|
||||
|
||||
bool isSendToSilentPayments(Output output) =>
|
||||
wallet.type == WalletType.bitcoin &&
|
||||
(RegExp(AddressValidator.silentPaymentAddressPatternMainnet).hasMatch(output.address) ||
|
||||
RegExp(AddressValidator.silentPaymentAddressPatternMainnet)
|
||||
.hasMatch(output.extractedAddress) ||
|
||||
(output.parsedAddress.addresses.isNotEmpty &&
|
||||
RegExp(AddressValidator.silentPaymentAddressPatternMainnet)
|
||||
.hasMatch(output.parsedAddress.addresses[0])));
|
||||
bool isSendToSilentPayments(Output output) {
|
||||
if (wallet.type != WalletType.bitcoin) return false;
|
||||
|
||||
final sp = RegExp(AddressValidator.silentPaymentAddressPatternMainnet);
|
||||
|
||||
final address = output.extractedAddress.isNotEmpty
|
||||
? output.extractedAddress
|
||||
: output.address;
|
||||
|
||||
return sp.hasMatch(address);
|
||||
}
|
||||
|
||||
@computed
|
||||
List<Template> get templates => sendTemplateViewModel.templates
|
||||
@@ -454,6 +480,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
final HardwareWalletViewModel? hardwareWalletViewModel;
|
||||
final FeesViewModel feesViewModel;
|
||||
final FiatConversionStore _fiatConversationStore;
|
||||
final AddressResolverService _adrResService;
|
||||
final Box<TransactionDescription> transactionDescriptionBox;
|
||||
|
||||
@computed
|
||||
@@ -606,8 +633,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
clearOutputs();
|
||||
|
||||
outputs.first.address = paymentRequest.address;
|
||||
outputs.first.parsedAddress =
|
||||
ParsedAddress(addresses: [paymentRequest.address], name: ocpRequest!.receiverName);
|
||||
outputs.first.parsedAddress = ParsedAddress(parsedAddressByCurrencyMap: {currency:paymentRequest.address}, handle: ocpRequest!.receiverName);
|
||||
outputs.first.setCryptoAmount(paymentRequest.amount);
|
||||
outputs.first.note = ocpRequest!.receiverName;
|
||||
|
||||
@@ -1342,7 +1368,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
for (final output in outputs) {
|
||||
final address =
|
||||
output.isParsedAddress ? output.parsedAddress.addresses.first : output.address;
|
||||
output.isParsedAddress ? output.parsedAddress.parsedAddressByCurrencyMap[selectedCryptoCurrency] ?? '' : output.address;
|
||||
|
||||
if (address.isNotEmpty &&
|
||||
!contactAddresses.contains(address) &&
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart';
|
||||
import 'package:cake_wallet/core/address_resolver/address_sources.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
@@ -55,6 +57,24 @@ abstract class ConnectionSyncViewModelBase with Store {
|
||||
@computed
|
||||
bool get looksUpWellKnown => _settingsStore.lookupsWellKnown;
|
||||
|
||||
@computed
|
||||
bool get lookupsNostr => _settingsStore.lookupsNostr;
|
||||
|
||||
@computed
|
||||
bool get lookupsFio => _settingsStore.lookupsFio;
|
||||
|
||||
@computed
|
||||
bool get lookupsBip353 => _settingsStore.lookupsBip353;
|
||||
|
||||
@computed
|
||||
bool get lookupsThorChain => _settingsStore.lookupsThorChain;
|
||||
|
||||
@computed
|
||||
bool get lookupsZcashAddress => _settingsStore.lookupsZcashAddress;
|
||||
|
||||
@computed
|
||||
bool get lookupsLNUrlPay => _settingsStore.lookupsLNUrl;
|
||||
|
||||
@computed
|
||||
ExchangeApiMode get exchangeStatus => _settingsStore.exchangeStatus;
|
||||
|
||||
@@ -68,6 +88,99 @@ abstract class ConnectionSyncViewModelBase with Store {
|
||||
@computed
|
||||
bool get builtinTor => _settingsStore.currentBuiltinTor;
|
||||
|
||||
List<AddressSource> get domainLookupSources => AddressResolverService.supportedSources;
|
||||
|
||||
bool lookupValue(AddressSource source) {
|
||||
switch (source) {
|
||||
case AddressSource.twitter:
|
||||
return lookupTwitter;
|
||||
case AddressSource.mastodon:
|
||||
return looksUpMastodon;
|
||||
case AddressSource.yatRecord:
|
||||
return looksUpYatService;
|
||||
case AddressSource.unstoppableDomains:
|
||||
return looksUpUnstoppableDomains;
|
||||
case AddressSource.openAlias:
|
||||
return looksUpOpenAlias;
|
||||
case AddressSource.ens:
|
||||
return looksUpENS;
|
||||
case AddressSource.zcashName:
|
||||
return lookupsZcashNames;
|
||||
case AddressSource.zcashAddress:
|
||||
return lookupsZcashAddress;
|
||||
case AddressSource.wellKnown:
|
||||
return looksUpWellKnown;
|
||||
case AddressSource.zanoAlias:
|
||||
return lookupsZanoAlias;
|
||||
case AddressSource.bip353:
|
||||
return lookupsBip353;
|
||||
case AddressSource.fio:
|
||||
return lookupsFio;
|
||||
case AddressSource.lnurlPay:
|
||||
return lookupsLNUrlPay;
|
||||
case AddressSource.thorChain:
|
||||
return lookupsThorChain;
|
||||
case AddressSource.nostr:
|
||||
return lookupsNostr;
|
||||
case AddressSource.contact:
|
||||
case AddressSource.notParsed:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void setLookupValue(AddressSource source, bool value) {
|
||||
switch (source) {
|
||||
case AddressSource.twitter:
|
||||
_settingsStore.lookupsTwitter = value;
|
||||
break;
|
||||
case AddressSource.mastodon:
|
||||
_settingsStore.lookupsMastodon = value;
|
||||
break;
|
||||
case AddressSource.yatRecord:
|
||||
_settingsStore.lookupsYatService = value;
|
||||
break;
|
||||
case AddressSource.unstoppableDomains:
|
||||
_settingsStore.lookupsUnstoppableDomains = value;
|
||||
break;
|
||||
case AddressSource.openAlias:
|
||||
_settingsStore.lookupsOpenAlias = value;
|
||||
break;
|
||||
case AddressSource.ens:
|
||||
_settingsStore.lookupsENS = value;
|
||||
break;
|
||||
case AddressSource.zcashName:
|
||||
_settingsStore.lookupsZcashNames = value;
|
||||
break;
|
||||
case AddressSource.zcashAddress:
|
||||
_settingsStore.lookupsZcashAddress = value;
|
||||
break;
|
||||
case AddressSource.wellKnown:
|
||||
_settingsStore.lookupsWellKnown = value;
|
||||
break;
|
||||
case AddressSource.zanoAlias:
|
||||
_settingsStore.lookupsZanoAlias = value;
|
||||
break;
|
||||
case AddressSource.bip353:
|
||||
_settingsStore.lookupsBip353 = value;
|
||||
break;
|
||||
case AddressSource.fio:
|
||||
_settingsStore.lookupsFio = value;
|
||||
break;
|
||||
case AddressSource.lnurlPay:
|
||||
_settingsStore.lookupsLNUrl = value;
|
||||
break;
|
||||
case AddressSource.thorChain:
|
||||
_settingsStore.lookupsThorChain = value;
|
||||
break;
|
||||
case AddressSource.nostr:
|
||||
_settingsStore.lookupsNostr = value;
|
||||
break;
|
||||
case AddressSource.contact:
|
||||
case AddressSource.notParsed:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@computed
|
||||
bool get hasPowNodes => [WalletType.nano, WalletType.banano].contains(_wallet.type);
|
||||
@@ -119,32 +232,6 @@ abstract class ConnectionSyncViewModelBase with Store {
|
||||
@computed
|
||||
bool get canUseTronGrid => _wallet.type == WalletType.tron;
|
||||
|
||||
@action
|
||||
void setLookupsTwitter(bool value) => _settingsStore.lookupsTwitter = value;
|
||||
|
||||
@action
|
||||
void setLookupsZanoAlias(bool value) => _settingsStore.lookupsZanoAlias = value;
|
||||
|
||||
@action
|
||||
void setLookupsMastodon(bool value) => _settingsStore.lookupsMastodon = value;
|
||||
|
||||
@action
|
||||
void setLookupsENS(bool value) => _settingsStore.lookupsENS = value;
|
||||
|
||||
@action
|
||||
void setLookupsZcashNames(bool value) => _settingsStore.lookupsZcashNames = value;
|
||||
|
||||
@action
|
||||
void setLookupsWellKnown(bool value) => _settingsStore.lookupsWellKnown = value;
|
||||
|
||||
@action
|
||||
void setLookupsYatService(bool value) => _settingsStore.lookupsYatService = value;
|
||||
|
||||
@action
|
||||
void setLookupsUnstoppableDomains(bool value) => _settingsStore.lookupsUnstoppableDomains = value;
|
||||
|
||||
@action
|
||||
void setLookupsOpenAlias(bool value) => _settingsStore.lookupsOpenAlias = value;
|
||||
|
||||
@action
|
||||
void setUseMempoolFeeAPI(bool value) => _settingsStore.useMempoolFeeAPI = value;
|
||||
|
||||
@@ -9,7 +9,6 @@ import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
@@ -51,10 +50,9 @@ String _moneroRecipientAddressForDisplay(String raw, WalletType walletType) {
|
||||
return match?.group(0) ?? raw.trim();
|
||||
}
|
||||
|
||||
bool isLightning(TransactionInfo tx) {
|
||||
printV(tx.additionalInfo);
|
||||
return (tx.additionalInfo["isLightning"] as bool?) ?? false;
|
||||
}
|
||||
bool isLightning(TransactionInfo tx) => (tx.additionalInfo["isLightning"] as bool?) ?? false;
|
||||
|
||||
bool hasLightningPreimage(TransactionInfo tx) => (tx.additionalInfo["preimage"] as String?) != null;
|
||||
|
||||
class TxDetailRowDefinition {
|
||||
final String keyString;
|
||||
@@ -188,6 +186,13 @@ class TxDetailRowDefinition {
|
||||
},
|
||||
applicable: (vm) => vm.wallet.type == WalletType.monero),
|
||||
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_lightning_preimage",
|
||||
title: S.current.transaction_preimage,
|
||||
valueGetter: (vm) => vm.transactionInfo.additionalInfo['preimage'] as String? ?? "",
|
||||
applicable: (vm) =>
|
||||
hasLightningPreimage(vm.transactionInfo) && isLightning(vm.transactionInfo),
|
||||
),
|
||||
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_confirmed_key",
|
||||
@@ -316,10 +321,9 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
TransactionPriority? transactionPriority;
|
||||
|
||||
CryptoCurrency get transactionAsset {
|
||||
if (isEVMCompatibleChain(wallet.type)) return evm!.assetOfTransaction(wallet, transactionInfo);
|
||||
|
||||
if (isEVMCompatibleChain(wallet.type)) {
|
||||
return evm!.assetOfTransaction(wallet, transactionInfo);
|
||||
}
|
||||
if (isLightning(transactionInfo)) return CryptoCurrency.btcln;
|
||||
|
||||
return switch (wallet.type) {
|
||||
WalletType.solana => solana!.assetOfTransaction(wallet, transactionInfo),
|
||||
@@ -329,7 +333,6 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// TODO integrate these getters with the TransactionInfo object
|
||||
String get formattedPendingStatus {
|
||||
switch (wallet.type) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:core';
|
||||
import 'dart:developer' as dev;
|
||||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/address_resolver/yat/yat_store.dart';
|
||||
import 'package:cake_wallet/core/amount_parsing_proxy.dart';
|
||||
import 'package:cake_wallet/core/fiat_conversion_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
@@ -17,7 +18,6 @@ import 'package:cake_wallet/reactions/wallet_utils.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
import 'package:cake_wallet/utils/list_item.dart';
|
||||
import 'package:cake_wallet/utils/qr_util.dart';
|
||||
|
||||
Reference in New Issue
Block a user