mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Cw 1539 lightning enhancements (#3400)
* feat: add Lightning transaction URL support in transaction details view model * fix: update currency selection logic in WalletAddressListViewModel and ReceivePage * feat: integrate computed transaction amount and fee getters in TransactionDetailsViewModel, refactor CopyWrapper logic in TransactionDetailsModal * refactor: apply lint rules * refactor: apply lint rules and improve LNURL handling with new methods for withdrawal requests and error checks * auto-reformat * fix: receive_page regression * fix: Withdraw lightning to other btc wallet gives LN address * chore: update .lock files [skip ci] --------- Co-authored-by: Robert Malikowski <malikowskirobert@gmail.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
Robert Malikowski
Omar Hatem
parent
28c2cc152e
commit
03edd658a5
@@ -1,50 +1,50 @@
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/address_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/confirmations_list_item.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
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/wallet_base.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/transaction_expandable_list_item.dart';
|
||||
import 'package:cake_wallet/utils/date_formatter.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import "package:cake_wallet/bitcoin/bitcoin.dart";
|
||||
import "package:cake_wallet/core/address_validator.dart";
|
||||
import "package:cake_wallet/entities/priority_for_wallet_type.dart";
|
||||
import "package:cake_wallet/entities/transaction_description.dart";
|
||||
import "package:cake_wallet/evm/evm.dart";
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/monero/monero.dart";
|
||||
import "package:cake_wallet/reactions/wallet_connect.dart";
|
||||
import "package:cake_wallet/solana/solana.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/address_list_item.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/confirmations_list_item.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/standart_list_item.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/transaction_expandable_list_item.dart";
|
||||
import "package:cake_wallet/store/app_store.dart";
|
||||
import "package:cake_wallet/tron/tron.dart";
|
||||
import "package:cake_wallet/view_model/send/send_view_model.dart";
|
||||
import "package:cake_wallet/zano/zano.dart";
|
||||
import "package:collection/collection.dart";
|
||||
import "package:cw_core/crypto_currency.dart";
|
||||
import "package:cw_core/currency_for_wallet_type.dart";
|
||||
import "package:cw_core/transaction_direction.dart";
|
||||
import "package:cw_core/transaction_info.dart";
|
||||
import "package:cw_core/transaction_priority.dart";
|
||||
import "package:cw_core/wallet_base.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:hive/hive.dart";
|
||||
import "package:intl/intl.dart";
|
||||
import "package:mobx/mobx.dart";
|
||||
import "package:url_launcher/url_launcher.dart";
|
||||
|
||||
part 'transaction_details_view_model.g.dart';
|
||||
part "transaction_details_view_model.g.dart";
|
||||
|
||||
bool _trueFunc(_) => true;
|
||||
|
||||
/// We're adding a regex here so we can remove any already saved address that has the account in it.
|
||||
/// In the refactor, we will make another separate variable for accounts and the UI would handle it as needed.
|
||||
String _moneroRecipientAddressForDisplay(String raw, WalletType walletType) {
|
||||
if (walletType != WalletType.monero || raw.isEmpty) return raw;
|
||||
if (walletType != WalletType.monero || raw.isEmpty) {
|
||||
return raw;
|
||||
}
|
||||
|
||||
final compact = raw.replaceAll(RegExp(r'\s'), '');
|
||||
final compact = raw.replaceAll(RegExp(r"\s"), "");
|
||||
final match = RegExp(
|
||||
r'4[0-9a-zA-Z]{94}|8[0-9a-zA-Z]{94}|[0-9a-zA-Z]{106}',
|
||||
r"4[0-9a-zA-Z]{94}|8[0-9a-zA-Z]{94}|[0-9a-zA-Z]{106}",
|
||||
caseSensitive: false,
|
||||
).firstMatch(compact);
|
||||
return match?.group(0) ?? raw.trim();
|
||||
@@ -55,6 +55,14 @@ bool isLightning(TransactionInfo tx) => (tx.additionalInfo["isLightning"] as boo
|
||||
bool hasLightningPreimage(TransactionInfo tx) => (tx.additionalInfo["preimage"] as String?) != null;
|
||||
|
||||
class TxDetailRowDefinition {
|
||||
TxDetailRowDefinition({
|
||||
required this.keyString,
|
||||
required this.title,
|
||||
required this.valueGetter,
|
||||
this.applicable = _trueFunc,
|
||||
this.listItemBuilder = StandartListItem.new,
|
||||
});
|
||||
|
||||
final String keyString;
|
||||
final String title;
|
||||
final String Function(TransactionDetailsViewModelBase) valueGetter;
|
||||
@@ -65,20 +73,13 @@ class TxDetailRowDefinition {
|
||||
required Key key,
|
||||
}) listItemBuilder;
|
||||
|
||||
TxDetailRowDefinition(
|
||||
{required this.keyString,
|
||||
required this.title,
|
||||
required this.valueGetter,
|
||||
this.applicable = _trueFunc,
|
||||
this.listItemBuilder = StandartListItem.new});
|
||||
|
||||
static final List<TxDetailRowDefinition> defs = [
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_date_key",
|
||||
title: S.current.transaction_details_date,
|
||||
valueGetter: (vm) =>
|
||||
DateFormat("d MMMM yyyy, HH:mm", vm._appStore.settingsStore.languageCode)
|
||||
.format(vm.transactionInfo.date)),
|
||||
keyString: "standard_list_item_transaction_details_date_key",
|
||||
title: S.current.transaction_details_date,
|
||||
valueGetter: (vm) => DateFormat("d MMMM yyyy, HH:mm", vm._appStore.settingsStore.languageCode)
|
||||
.format(vm.transactionInfo.date),
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_height_key",
|
||||
title: S.current.transaction_details_height,
|
||||
@@ -88,125 +89,137 @@ class TxDetailRowDefinition {
|
||||
!isLightning(vm.transactionInfo),
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_fee_key",
|
||||
title: S.current.transaction_details_fee,
|
||||
valueGetter: (vm) => vm.transactionInfo.fee!.toStringWithSymbol(),
|
||||
applicable: (vm) =>
|
||||
vm.wallet.type != WalletType.nano &&
|
||||
(vm.transactionInfo.fee?.toStringWithSymbol() ?? "").isNotEmpty),
|
||||
keyString: "standard_list_item_transaction_details_fee_key",
|
||||
title: S.current.transaction_details_fee,
|
||||
valueGetter: (vm) => vm.feeAmount,
|
||||
applicable: (vm) =>
|
||||
vm.wallet.type != WalletType.nano &&
|
||||
(vm.transactionInfo.fee?.toStringWithSymbol() ?? "").isNotEmpty,
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_confirmations_key",
|
||||
title: S.current.confirmations,
|
||||
valueGetter: (vm) => "${vm.transactionInfo.confirmations}/${vm.neededConfirmations}",
|
||||
applicable: (vm) =>
|
||||
[...electrumWalletTypes, ...evmWalletTypes, WalletType.zcash, WalletType.monero]
|
||||
.contains(vm.wallet.type) &&
|
||||
!isLightning(vm.transactionInfo),
|
||||
listItemBuilder: ConfirmationsListItem.new),
|
||||
keyString: "standard_list_item_transaction_confirmations_key",
|
||||
title: S.current.confirmations,
|
||||
valueGetter: (vm) => "${vm.transactionInfo.confirmations}/${vm.neededConfirmations}",
|
||||
applicable: (vm) =>
|
||||
[...electrumWalletTypes, ...evmWalletTypes, WalletType.zcash, WalletType.monero]
|
||||
.contains(vm.wallet.type) &&
|
||||
!isLightning(vm.transactionInfo),
|
||||
listItemBuilder: ConfirmationsListItem.new,
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_recipient_address_key",
|
||||
title: S.current.transaction_details_recipient_address,
|
||||
valueGetter: (vm) {
|
||||
String? ret = null;
|
||||
keyString: "standard_list_item_transaction_details_recipient_address_key",
|
||||
title: S.current.transaction_details_recipient_address,
|
||||
valueGetter: (vm) {
|
||||
String? ret;
|
||||
|
||||
switch (vm.wallet.type) {
|
||||
case WalletType.monero:
|
||||
if (vm.transactionInfo.direction == TransactionDirection.incoming) {
|
||||
ret = monero!.getTransactionAddress(
|
||||
vm.wallet,
|
||||
vm.transactionInfo.additionalInfo['accountIndex'] as int,
|
||||
vm.transactionInfo.additionalInfo['addressIndex'] as int);
|
||||
}
|
||||
case WalletType.bitcoin:
|
||||
ret = (bitcoin!.getTransactionAddresses(vm.wallet, vm.transactionInfo) ?? [])
|
||||
.firstOrNull ??
|
||||
"";
|
||||
case WalletType.tron:
|
||||
if (vm.transactionInfo.to != null)
|
||||
ret = tron!.getTronBase58Address(vm.transactionInfo.to!, vm.wallet);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (ret == null) {
|
||||
ret = vm.transactionInfo.to ?? "";
|
||||
}
|
||||
final resolvedAddress = _moneroRecipientAddressForDisplay(ret, vm.wallet.type);
|
||||
vm.isRecipientAddressShown = resolvedAddress.isNotEmpty;
|
||||
return resolvedAddress;
|
||||
},
|
||||
applicable: (vm) =>
|
||||
vm.showRecipientAddress &&
|
||||
(vm.transactionInfo.to != null ||
|
||||
[WalletType.monero, WalletType.tron].contains(vm.wallet.type) ||
|
||||
vm.wallet.type == WalletType.bitcoin &&
|
||||
vm.transactionInfo.direction == TransactionDirection.incoming),
|
||||
listItemBuilder: AddressListItem.new),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_source_address_key",
|
||||
title: S.current.transaction_details_source_address,
|
||||
valueGetter: (vm) {
|
||||
switch (vm.wallet.type) {
|
||||
case WalletType.tron:
|
||||
return tron!.getTronBase58Address(vm.transactionInfo.from!, vm.wallet);
|
||||
default:
|
||||
return vm.transactionInfo.from!;
|
||||
}
|
||||
},
|
||||
applicable: (vm) => vm.transactionInfo.from != null,
|
||||
listItemBuilder: AddressListItem.new),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_address_label_key",
|
||||
title: S.current.address_label,
|
||||
valueGetter: (vm) => monero!.getSubaddressLabel(
|
||||
vm.wallet,
|
||||
vm.transactionInfo.additionalInfo['accountIndex'] as int,
|
||||
vm.transactionInfo.additionalInfo['addressIndex'] as int),
|
||||
applicable: (vm) => vm.wallet.type == WalletType.monero),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_key",
|
||||
title: S.current.transaction_key,
|
||||
valueGetter: (vm) {
|
||||
final descriptionKey =
|
||||
'${vm.transactionInfo.txHash}_${vm.wallet.walletAddresses.primaryAddress}';
|
||||
switch (vm.wallet.type) {
|
||||
case WalletType.monero:
|
||||
if (vm.transactionInfo.direction == TransactionDirection.incoming) {
|
||||
ret = monero!.getTransactionAddress(
|
||||
vm.wallet,
|
||||
vm.transactionInfo.additionalInfo["accountIndex"] as int,
|
||||
vm.transactionInfo.additionalInfo["addressIndex"] as int,
|
||||
);
|
||||
}
|
||||
case WalletType.bitcoin:
|
||||
ret = (bitcoin!.getTransactionAddresses(vm.wallet, vm.transactionInfo) ?? [])
|
||||
.firstOrNull ??
|
||||
"";
|
||||
case WalletType.tron:
|
||||
if (vm.transactionInfo.to != null) {
|
||||
ret = tron!.getTronBase58Address(vm.transactionInfo.to!, vm.wallet);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ret ??= vm.transactionInfo.to ?? "";
|
||||
|
||||
final description = vm.transactionDescriptionBox.values.firstWhere(
|
||||
(val) => val.id == descriptionKey || val.id == vm.transactionInfo.txHash,
|
||||
orElse: () => TransactionDescription(id: descriptionKey));
|
||||
return vm.transactionInfo.additionalInfo['key'] as String? ??
|
||||
description.transactionKey ??
|
||||
"";
|
||||
},
|
||||
applicable: (vm) => vm.wallet.type == WalletType.monero),
|
||||
final resolvedAddress = _moneroRecipientAddressForDisplay(ret, vm.wallet.type);
|
||||
vm.isRecipientAddressShown = resolvedAddress.isNotEmpty;
|
||||
return resolvedAddress;
|
||||
},
|
||||
applicable: (vm) =>
|
||||
vm.showRecipientAddress &&
|
||||
(vm.transactionInfo.to != null ||
|
||||
[WalletType.monero, WalletType.tron].contains(vm.wallet.type) ||
|
||||
vm.wallet.type == WalletType.bitcoin &&
|
||||
vm.transactionInfo.direction == TransactionDirection.incoming),
|
||||
listItemBuilder: AddressListItem.new,
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_source_address_key",
|
||||
title: S.current.transaction_details_source_address,
|
||||
valueGetter: (vm) {
|
||||
switch (vm.wallet.type) {
|
||||
case WalletType.tron:
|
||||
return tron!.getTronBase58Address(vm.transactionInfo.from!, vm.wallet);
|
||||
default:
|
||||
return vm.transactionInfo.from!;
|
||||
}
|
||||
},
|
||||
applicable: (vm) => vm.transactionInfo.from != null,
|
||||
listItemBuilder: AddressListItem.new,
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_address_label_key",
|
||||
title: S.current.address_label,
|
||||
valueGetter: (vm) => monero!.getSubaddressLabel(
|
||||
vm.wallet,
|
||||
vm.transactionInfo.additionalInfo["accountIndex"] as int,
|
||||
vm.transactionInfo.additionalInfo["addressIndex"] as int,
|
||||
),
|
||||
applicable: (vm) => vm.wallet.type == WalletType.monero,
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_key",
|
||||
title: S.current.transaction_key,
|
||||
valueGetter: (vm) {
|
||||
final descriptionKey =
|
||||
"${vm.transactionInfo.txHash}_${vm.wallet.walletAddresses.primaryAddress}";
|
||||
|
||||
final description = vm.transactionDescriptionBox.values.firstWhere(
|
||||
(val) => val.id == descriptionKey || val.id == vm.transactionInfo.txHash,
|
||||
orElse: () => TransactionDescription(id: descriptionKey),
|
||||
);
|
||||
return vm.transactionInfo.additionalInfo["key"] as String? ??
|
||||
description.transactionKey ??
|
||||
"";
|
||||
},
|
||||
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? ?? "",
|
||||
valueGetter: (vm) => vm.transactionInfo.additionalInfo["preimage"] as String? ?? "",
|
||||
applicable: (vm) =>
|
||||
hasLightningPreimage(vm.transactionInfo) && isLightning(vm.transactionInfo),
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_confirmed_key",
|
||||
title: S.current.confirmed_tx,
|
||||
valueGetter: (vm) => (vm.transactionInfo.confirmations > 0).toString(),
|
||||
applicable: (vm) => vm.wallet.type == WalletType.nano),
|
||||
keyString: "standard_list_item_transaction_confirmed_key",
|
||||
title: S.current.confirmed_tx,
|
||||
valueGetter: (vm) => (vm.transactionInfo.confirmations > 0).toString(),
|
||||
applicable: (vm) => vm.wallet.type == WalletType.nano,
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_memo_key",
|
||||
title: S.current.memo,
|
||||
valueGetter: (vm) => vm.transactionInfo.additionalInfo['memo'] as String,
|
||||
applicable: (vm) =>
|
||||
vm.wallet.type == WalletType.zcash &&
|
||||
vm.transactionInfo.additionalInfo["memo"] != null),
|
||||
keyString: "standard_list_item_transaction_details_memo_key",
|
||||
title: S.current.memo,
|
||||
valueGetter: (vm) => vm.transactionInfo.additionalInfo["memo"] as String,
|
||||
applicable: (vm) =>
|
||||
vm.wallet.type == WalletType.zcash && vm.transactionInfo.additionalInfo["memo"] != null,
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_asset_id_key",
|
||||
title: "Asset ID",
|
||||
valueGetter: (vm) =>
|
||||
vm.transactionInfo.additionalInfo["assetId"] as String? ?? "Unknown asset id",
|
||||
applicable: (vm) => vm.wallet.type == WalletType.zano),
|
||||
keyString: "standard_list_item_transaction_details_asset_id_key",
|
||||
title: "Asset ID",
|
||||
valueGetter: (vm) =>
|
||||
vm.transactionInfo.additionalInfo["assetId"] as String? ?? "Unknown asset id",
|
||||
applicable: (vm) => vm.wallet.type == WalletType.zano,
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_comment_key",
|
||||
title: S.current.transaction_details_title,
|
||||
valueGetter: (vm) => vm.transactionInfo.additionalInfo['comment'] as String? ?? "",
|
||||
applicable: (vm) => vm.wallet.type == WalletType.zano),
|
||||
keyString: "standard_list_item_transaction_details_comment_key",
|
||||
title: S.current.transaction_details_title,
|
||||
valueGetter: (vm) => vm.transactionInfo.additionalInfo["comment"] as String? ?? "",
|
||||
applicable: (vm) => vm.wallet.type == WalletType.zano,
|
||||
),
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_id_key",
|
||||
title: S.current.transaction_details_transaction_id,
|
||||
@@ -227,7 +240,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
required this.sendViewModel,
|
||||
this.canReplaceByFee = false,
|
||||
}) : items = [],
|
||||
RBFListItems = [],
|
||||
rbfListItems = [],
|
||||
newFee = 0,
|
||||
isRecipientAddressShown = false,
|
||||
_appStore = appStore,
|
||||
@@ -236,19 +249,23 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
|
||||
for (final def in TxDetailRowDefinition.defs) {
|
||||
if (def.applicable(this)) {
|
||||
items.add(def.listItemBuilder(
|
||||
items.add(
|
||||
def.listItemBuilder(
|
||||
title: def.title,
|
||||
value: def.valueGetter(this),
|
||||
key: ValueKey(def.keyString)) as TransactionDetailsListItem);
|
||||
key: ValueKey(def.keyString),
|
||||
) as TransactionDetailsListItem,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_checkForRBF(tx);
|
||||
|
||||
final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
|
||||
final descriptionKey = "${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}";
|
||||
final description = transactionDescriptionBox.values.firstWhere(
|
||||
(val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
|
||||
orElse: () => TransactionDescription(id: descriptionKey));
|
||||
(val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
|
||||
orElse: () => TransactionDescription(id: descriptionKey),
|
||||
);
|
||||
|
||||
if (showRecipientAddress && !isRecipientAddressShown) {
|
||||
final recipientAddress = description.recipientAddress;
|
||||
@@ -260,7 +277,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
AddressListItem(
|
||||
title: S.current.transaction_details_recipient_address,
|
||||
value: recipientAddressForDisplay,
|
||||
key: ValueKey('standard_list_item_${recipientAddressForDisplay}_key'),
|
||||
key: ValueKey("standard_list_item_${recipientAddressForDisplay}_key"),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -268,10 +285,11 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
}
|
||||
|
||||
void updateNote(String note) {
|
||||
final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
|
||||
final descriptionKey = "${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}";
|
||||
final description = transactionDescriptionBox.values.firstWhere(
|
||||
(val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
|
||||
orElse: () => TransactionDescription(id: descriptionKey));
|
||||
(val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
|
||||
orElse: () => TransactionDescription(id: descriptionKey),
|
||||
);
|
||||
|
||||
description.transactionNote = note;
|
||||
|
||||
@@ -283,9 +301,10 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
}
|
||||
|
||||
String get note {
|
||||
final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
|
||||
final descriptionKey = "${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}";
|
||||
final description = transactionDescriptionBox.values
|
||||
.firstWhereOrNull((val) => val.id == descriptionKey || val.id == transactionInfo.txHash);
|
||||
.firstWhereOrNull((val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
|
||||
);
|
||||
return description?.transactionNote ?? "";
|
||||
}
|
||||
|
||||
@@ -296,7 +315,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
final AppStore _appStore;
|
||||
|
||||
final List<TransactionDetailsListItem> items;
|
||||
final List<TransactionDetailsListItem> RBFListItems;
|
||||
final List<TransactionDetailsListItem> rbfListItems;
|
||||
bool showRecipientAddress;
|
||||
bool isRecipientAddressShown;
|
||||
int newFee;
|
||||
@@ -304,9 +323,13 @@ 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;
|
||||
if (isLightning(transactionInfo)) {
|
||||
return CryptoCurrency.btcln;
|
||||
}
|
||||
|
||||
return switch (wallet.type) {
|
||||
WalletType.solana => solana!.assetOfTransaction(wallet, transactionInfo),
|
||||
@@ -316,26 +339,38 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO integrate these getters with the TransactionInfo object
|
||||
@computed
|
||||
String get transactionAmount =>
|
||||
_appStore.amountParsingProxy.asDisplayStringWithSymbol(transactionInfo.amount);
|
||||
|
||||
@computed
|
||||
String get feeAmount =>
|
||||
_appStore.amountParsingProxy.asDisplayStringWithSymbol(transactionInfo.fee!);
|
||||
|
||||
@computed
|
||||
String get transactionCopyAmount =>
|
||||
_appStore.amountParsingProxy.asDisplayString(transactionInfo.amount);
|
||||
|
||||
// TODO(malik1004x): integrate these getters with the TransactionInfo object
|
||||
String get formattedPendingStatus {
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
case WalletType.haven:
|
||||
case WalletType.zano:
|
||||
if (transactionInfo.confirmations >= 0 && transactionInfo.confirmations < 10) {
|
||||
return ' (${transactionInfo.confirmations}/10)';
|
||||
return " (${transactionInfo.confirmations}/10)";
|
||||
}
|
||||
break;
|
||||
case WalletType.wownero:
|
||||
if (transactionInfo.confirmations >= 0 && transactionInfo.confirmations < 3) {
|
||||
return ' (${transactionInfo.confirmations}/3)';
|
||||
return " (${transactionInfo.confirmations}/3)";
|
||||
}
|
||||
break;
|
||||
case WalletType.litecoin:
|
||||
bool isPegIn = (transactionInfo.additionalInfo["isPegIn"] as bool?) ?? false;
|
||||
bool isPegOut = (transactionInfo.additionalInfo["isPegOut"] as bool?) ?? false;
|
||||
bool fromPegOut = (transactionInfo.additionalInfo["fromPegOut"] as bool?) ?? false;
|
||||
String str = '';
|
||||
final isPegIn = (transactionInfo.additionalInfo["isPegIn"] as bool?) ?? false;
|
||||
final isPegOut = (transactionInfo.additionalInfo["isPegOut"] as bool?) ?? false;
|
||||
final fromPegOut = (transactionInfo.additionalInfo["fromPegOut"] as bool?) ?? false;
|
||||
String str = "";
|
||||
if (transactionInfo.confirmations <= 0) {
|
||||
str = S.current.pending;
|
||||
}
|
||||
@@ -352,10 +387,10 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
}
|
||||
return str;
|
||||
default:
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
|
||||
String get formattedStatus {
|
||||
@@ -369,7 +404,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
return formattedPendingStatus;
|
||||
}
|
||||
|
||||
return transactionInfo.isPending ? S.current.pending : '';
|
||||
return transactionInfo.isPending ? S.current.pending : "";
|
||||
}
|
||||
|
||||
int get neededConfirmations {
|
||||
@@ -381,9 +416,11 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
case WalletType.wownero:
|
||||
return 3;
|
||||
case WalletType.litecoin:
|
||||
bool isPegOut = (transactionInfo.additionalInfo["isPegOut"] as bool?) ?? false;
|
||||
bool fromPegOut = (transactionInfo.additionalInfo["fromPegOut"] as bool?) ?? false;
|
||||
if (isPegOut || fromPegOut) return 6;
|
||||
final isPegOut = (transactionInfo.additionalInfo["isPegOut"] as bool?) ?? false;
|
||||
final fromPegOut = (transactionInfo.additionalInfo["fromPegOut"] as bool?) ?? false;
|
||||
if (isPegOut || fromPegOut) {
|
||||
return 6;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -408,52 +445,56 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
final txId = transactionInfo.txHash;
|
||||
if (wallet.chainId != null) {
|
||||
final explorerUrl = evm!.getExplorerUrlForChainId(wallet.chainId!);
|
||||
if (explorerUrl != null) return '$explorerUrl/tx/${txId}';
|
||||
if (explorerUrl != null) {
|
||||
return "$explorerUrl/tx/${txId}";
|
||||
}
|
||||
}
|
||||
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
return 'https://monero.com/tx/${txId}';
|
||||
return "https://monero.com/tx/${txId}";
|
||||
case WalletType.bitcoin:
|
||||
return 'https://mempool.cakewallet.com/${wallet.isTestnet ? "testnet/" : ""}tx/${txId}';
|
||||
return isLightning(transactionInfo)
|
||||
? "https://sparkscan.io/tx/${txId}"
|
||||
: 'https://mempool.cakewallet.com/${wallet.isTestnet ? "testnet/" : ""}tx/${txId}';
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.txIsMweb(transactionInfo)
|
||||
? "https://www.mwebexplorer.com/blocks/block/${transactionInfo.height}"
|
||||
: 'https://blockchair.com/litecoin/transaction/${txId}';
|
||||
: "https://blockchair.com/litecoin/transaction/${txId}";
|
||||
case WalletType.bitcoinCash:
|
||||
return 'https://blockchair.com/bitcoin-cash/transaction/${txId}';
|
||||
return "https://blockchair.com/bitcoin-cash/transaction/${txId}";
|
||||
case WalletType.haven:
|
||||
return 'https://explorer.havenprotocol.org/search?value=${txId}';
|
||||
return "https://explorer.havenprotocol.org/search?value=${txId}";
|
||||
case WalletType.ethereum:
|
||||
return 'https://etherscan.io/tx/${txId}';
|
||||
return "https://etherscan.io/tx/${txId}";
|
||||
case WalletType.base:
|
||||
return 'https://basescan.org/tx/${txId}';
|
||||
return "https://basescan.org/tx/${txId}";
|
||||
case WalletType.arbitrum:
|
||||
return 'https://arbiscan.io/tx/${txId}';
|
||||
return "https://arbiscan.io/tx/${txId}";
|
||||
case WalletType.bsc:
|
||||
return 'https://bscscan.com/tx/${txId}';
|
||||
return "https://bscscan.com/tx/${txId}";
|
||||
case WalletType.polygon:
|
||||
return 'https://polygonscan.com/tx/${txId}';
|
||||
return "https://polygonscan.com/tx/${txId}";
|
||||
case WalletType.nano:
|
||||
return 'https://nanexplorer.com/nano/block/${txId}';
|
||||
return "https://nanexplorer.com/nano/block/${txId}";
|
||||
case WalletType.banano:
|
||||
return 'https://nanexplorer.com/banano/block/${txId}';
|
||||
return "https://nanexplorer.com/banano/block/${txId}";
|
||||
case WalletType.solana:
|
||||
return 'https://solscan.io/tx/${txId}';
|
||||
return "https://solscan.io/tx/${txId}";
|
||||
case WalletType.tron:
|
||||
return 'https://tronscan.org/#/transaction/${txId}';
|
||||
return "https://tronscan.org/#/transaction/${txId}";
|
||||
case WalletType.wownero:
|
||||
return 'https://explore.wownero.com/tx/${txId}';
|
||||
return "https://explore.wownero.com/tx/${txId}";
|
||||
case WalletType.zano:
|
||||
return 'https://explorer.zano.org/transaction/${txId}';
|
||||
return "https://explorer.zano.org/transaction/${txId}";
|
||||
case WalletType.decred:
|
||||
return 'https://${wallet.isTestnet ? "testnet" : "dcrdata"}.decred.org/tx/${txId.split(':')[0]}';
|
||||
case WalletType.dogecoin:
|
||||
return 'https://blockchair.com/dogecoin/transaction/${txId}';
|
||||
return "https://blockchair.com/dogecoin/transaction/${txId}";
|
||||
case WalletType.zcash:
|
||||
return 'https://blockchair.com/zcash/transaction/${txId}';
|
||||
return "https://blockchair.com/zcash/transaction/${txId}";
|
||||
case WalletType.none:
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,13 +514,17 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
: transactionInfo.outputAddresses!.length;
|
||||
|
||||
newFee = bitcoin!.getFeeAmountForPriority(
|
||||
wallet, bitcoin!.getBitcoinTransactionPriorityMedium(), inputsCount, outputsCount);
|
||||
wallet,
|
||||
bitcoin!.getBitcoinTransactionPriorityMedium(),
|
||||
inputsCount,
|
||||
outputsCount,
|
||||
);
|
||||
|
||||
RBFListItems.add(
|
||||
rbfListItems.add(
|
||||
StandartListItem(
|
||||
title: S.current.old_fee,
|
||||
value: tx.fee?.toStringWithSymbol() ?? '0.0',
|
||||
key: ValueKey('standard_list_item_rbf_old_fee_key'),
|
||||
value: tx.fee?.toStringWithSymbol() ?? "0.0",
|
||||
key: const ValueKey("standard_list_item_rbf_old_fee_key"),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -488,32 +533,33 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
final recommendedRate = (transactionInfo.fee! / BigInt.from(size)) +
|
||||
transactionInfo.fee!.copyWith(amount: BigInt.one);
|
||||
|
||||
RBFListItems.add(
|
||||
StandartListItem(title: 'New recommended fee rate', value: '$recommendedRate sat/byte'));
|
||||
rbfListItems.add(
|
||||
StandartListItem(title: "New recommended fee rate", value: "$recommendedRate sat/byte"),
|
||||
);
|
||||
}
|
||||
|
||||
final priorities = priorityForWalletType(wallet.type);
|
||||
final selectedItem = priorities.indexOf(sendViewModel.feesViewModel.transactionPriority);
|
||||
final customItem = priorities.firstWhereOrNull(
|
||||
(element) => element == sendViewModel.feesViewModel.bitcoinTransactionPriorityCustom);
|
||||
(element) => element == sendViewModel.feesViewModel.bitcoinTransactionPriorityCustom,
|
||||
);
|
||||
final customItemIndex = customItem != null ? priorities.indexOf(customItem) : null;
|
||||
final maxCustomFeeRate = sendViewModel.feesViewModel.maxCustomFeeRate?.toDouble();
|
||||
|
||||
RBFListItems.add(
|
||||
rbfListItems.add(
|
||||
StandardPickerListItem(
|
||||
key: ValueKey('standard_picker_list_item_transaction_priorities_key'),
|
||||
key: const ValueKey("standard_picker_list_item_transaction_priorities_key"),
|
||||
title: S.current.estimated_new_fee,
|
||||
value: bitcoin!.formatterBitcoinAmountToString(amount: newFee) + ' ${wallet.currency}',
|
||||
value: "${bitcoin!.formatterBitcoinAmountToString(amount: newFee)} ${wallet.currency}",
|
||||
items: priorityForWalletType(wallet.type),
|
||||
customValue: _appStore.settingsStore.customBitcoinFeeRate.toDouble(),
|
||||
maxValue: maxCustomFeeRate,
|
||||
selectedIdx: selectedItem,
|
||||
customItemIndex: customItemIndex ?? 0,
|
||||
displayItem: (dynamic priority, double sliderValue) =>
|
||||
displayItem: (dynamic priority, sliderValue) =>
|
||||
sendViewModel.feesViewModel.displayFeeRate(priority, sliderValue.round()),
|
||||
onSliderChanged: (double newValue) =>
|
||||
setNewFee(value: newValue, priority: transactionPriority!),
|
||||
onItemSelected: (dynamic item, double sliderValue) {
|
||||
onSliderChanged: (newValue) => setNewFee(value: newValue, priority: transactionPriority!),
|
||||
onItemSelected: (dynamic item, sliderValue) {
|
||||
transactionPriority = item as TransactionPriority;
|
||||
return setNewFee(value: sliderValue, priority: transactionPriority!);
|
||||
},
|
||||
@@ -521,9 +567,9 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
);
|
||||
|
||||
if (transactionInfo.inputAddresses != null && transactionInfo.inputAddresses!.isNotEmpty) {
|
||||
RBFListItems.add(
|
||||
rbfListItems.add(
|
||||
StandardExpandableListItem(
|
||||
key: ValueKey('standard_expandable_list_item_transaction_input_addresses_key'),
|
||||
key: const ValueKey("standard_expandable_list_item_transaction_input_addresses_key"),
|
||||
title: S.current.inputs,
|
||||
expandableItems: transactionInfo.inputAddresses!,
|
||||
),
|
||||
@@ -532,17 +578,17 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
|
||||
if (transactionInfo.outputAddresses != null && transactionInfo.outputAddresses!.isNotEmpty) {
|
||||
final outputAddresses = transactionInfo.outputAddresses!.map((element) {
|
||||
if (element.contains('OP_RETURN:') && element.length > 40) {
|
||||
return element.substring(0, 40) + '...';
|
||||
if (element.contains("OP_RETURN:") && element.length > 40) {
|
||||
return "${element.substring(0, 40)}...";
|
||||
}
|
||||
return element;
|
||||
}).toList();
|
||||
|
||||
RBFListItems.add(
|
||||
rbfListItems.add(
|
||||
StandardExpandableListItem(
|
||||
title: S.current.outputs,
|
||||
expandableItems: outputAddresses,
|
||||
key: ValueKey('standard_expandable_list_item_transaction_output_addresses_key'),
|
||||
key: const ValueKey("standard_expandable_list_item_transaction_output_addresses_key"),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -552,7 +598,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
Future<void> _checkForRBF(TransactionInfo tx) async {
|
||||
if (wallet.type == WalletType.bitcoin &&
|
||||
transactionInfo.direction == TransactionDirection.outgoing) {
|
||||
final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
|
||||
final descriptionKey = "${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}";
|
||||
final description = transactionDescriptionBox.values
|
||||
.firstWhereOrNull((val) => val.id == descriptionKey || val.id == transactionInfo.txHash);
|
||||
|
||||
@@ -569,45 +615,23 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
}
|
||||
}
|
||||
|
||||
String setNewFee({double? value, required TransactionPriority priority}) {
|
||||
String setNewFee({required TransactionPriority priority, double? value}) {
|
||||
newFee = priority == bitcoin!.getBitcoinTransactionPriorityCustom() && value != null
|
||||
? bitcoin!.feeAmountWithFeeRate(
|
||||
wallet,
|
||||
value.round(),
|
||||
transactionInfo.inputAddresses?.length ?? 1,
|
||||
transactionInfo.outputAddresses?.length ?? 1)
|
||||
transactionInfo.outputAddresses?.length ?? 1,
|
||||
)
|
||||
: bitcoin!.getFeeAmountForPriority(
|
||||
wallet,
|
||||
priority,
|
||||
transactionInfo.inputAddresses?.length ?? 1,
|
||||
transactionInfo.outputAddresses?.length ?? 1);
|
||||
transactionInfo.outputAddresses?.length ?? 1,
|
||||
);
|
||||
|
||||
return bitcoin!.formatterBitcoinAmountToString(amount: newFee);
|
||||
}
|
||||
|
||||
String get formattedCryptoAmount {
|
||||
if (wallet.type == WalletType.bitcoin) {
|
||||
final crypto = isLightning(transactionInfo) ? CryptoCurrency.btcln : CryptoCurrency.btc;
|
||||
final amount = _appStore.amountParsingProxy
|
||||
.asDisplayString(transactionInfo.amount)
|
||||
.withMaxDecimals(8)
|
||||
.withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
|
||||
return '$amount ${_appStore.amountParsingProxy.getCryptoSymbol(crypto)}';
|
||||
}
|
||||
|
||||
return transactionInfo.amount.toStringWithSymbol();
|
||||
}
|
||||
|
||||
void replaceByFee(String newFee) => sendViewModel.replaceByFee(transactionInfo, newFee);
|
||||
|
||||
@computed
|
||||
String get pendingTransactionFiatAmountValueFormatted => sendViewModel.isFiatDisabled
|
||||
? ''
|
||||
: sendViewModel.pendingTransactionFiatAmount + ' ' + sendViewModel.fiat.title;
|
||||
|
||||
@computed
|
||||
String get pendingTransactionFeeFiatAmountFormatted => sendViewModel.isFiatDisabled
|
||||
? ''
|
||||
: sendViewModel.pendingTransactionFeeFiatAmount + ' ' + sendViewModel.fiat.title;
|
||||
}
|
||||
|
||||
@@ -1,59 +1,58 @@
|
||||
import 'dart:core';
|
||||
import 'dart:developer' as dev;
|
||||
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';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
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/tron/tron.dart';
|
||||
import 'package:cake_wallet/utils/list_item.dart';
|
||||
import 'package:cake_wallet/utils/qr_util.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_hidden_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_util.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/zcash/zcash.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/payment_uris.dart';
|
||||
import 'package:cw_core/spl_token.dart';
|
||||
import 'package:cw_core/tron_token.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
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";
|
||||
import "package:cake_wallet/decred/decred.dart";
|
||||
import "package:cake_wallet/entities/auto_generate_subaddress_status.dart";
|
||||
import "package:cake_wallet/entities/fiat_api_mode.dart";
|
||||
import "package:cake_wallet/entities/fiat_currency.dart";
|
||||
import "package:cake_wallet/evm/evm.dart";
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/monero/monero.dart";
|
||||
import "package:cake_wallet/reactions/wallet_connect.dart";
|
||||
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/tron/tron.dart";
|
||||
import "package:cake_wallet/utils/list_item.dart";
|
||||
import "package:cake_wallet/utils/qr_util.dart";
|
||||
import "package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart";
|
||||
import "package:cake_wallet/view_model/wallet_address_list/wallet_address_hidden_list_header.dart";
|
||||
import "package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart";
|
||||
import "package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart";
|
||||
import "package:cake_wallet/view_model/wallet_address_list/wallet_address_util.dart";
|
||||
import "package:cake_wallet/wownero/wownero.dart";
|
||||
import "package:cake_wallet/zano/zano.dart";
|
||||
import "package:cake_wallet/zcash/zcash.dart";
|
||||
import "package:cw_core/crypto_currency.dart";
|
||||
import "package:cw_core/currency.dart";
|
||||
import "package:cw_core/currency_for_wallet_type.dart";
|
||||
import "package:cw_core/erc20_token.dart";
|
||||
import "package:cw_core/payment_uris.dart";
|
||||
import "package:cw_core/spl_token.dart";
|
||||
import "package:cw_core/tron_token.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
import "package:mobx/mobx.dart";
|
||||
|
||||
part 'wallet_address_list_view_model.g.dart';
|
||||
part "wallet_address_list_view_model.g.dart";
|
||||
|
||||
class WalletAddressListViewModel = WalletAddressListViewModelBase with _$WalletAddressListViewModel;
|
||||
|
||||
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
|
||||
WalletAddressListViewModelBase({
|
||||
required AppStore appStore,
|
||||
required super.appStore,
|
||||
required this.yatStore,
|
||||
required this.fiatConversionStore,
|
||||
}) : _baseItems = <ListItem>[],
|
||||
selectedCurrency = appStore.wallet!.currency,
|
||||
hasAccounts = [WalletType.monero, WalletType.wownero].contains(appStore.wallet!.type),
|
||||
_appStore = appStore,
|
||||
receivePageOption = appStore.wallet!.walletAddresses.walletInfo.addressPageType ?? '',
|
||||
super(appStore: appStore) {
|
||||
receivePageOption = appStore.wallet!.walletAddresses.walletInfo.addressPageType ?? "" {
|
||||
_init();
|
||||
}
|
||||
|
||||
@@ -81,8 +80,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
CryptoCurrency? tokenCurrency;
|
||||
|
||||
@computed
|
||||
String get cryptoCurrencySymbol =>
|
||||
_appStore.amountParsingProxy.getCryptoSymbol(tokenCurrency ?? wallet.currency);
|
||||
String get cryptoCurrencySymbol => _appStore.amountParsingProxy.getCryptoSymbol(
|
||||
tokenCurrency ??
|
||||
(selectedCurrency is CryptoCurrency
|
||||
? selectedCurrency as CryptoCurrency
|
||||
: wallet.currency),
|
||||
);
|
||||
|
||||
void setTokenCurrency(Currency curr) {
|
||||
if (curr == wallet.currency || curr == CryptoCurrency.btcln) {
|
||||
@@ -122,7 +125,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
_appStore.amountParsingProxy.useSatoshi(selectedCurrency as CryptoCurrency);
|
||||
|
||||
@observable
|
||||
String searchText = '';
|
||||
String searchText = "";
|
||||
|
||||
@computed
|
||||
int get selectedCurrencyIndex => currencies.indexOf(selectedCurrency);
|
||||
@@ -131,30 +134,45 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
int get tokenCurrencyIndex => tokenCurrency == null ? 0 : tokenCurrencies.indexOf(tokenCurrency!);
|
||||
|
||||
@observable
|
||||
String _amount = '';
|
||||
String _amount = "";
|
||||
|
||||
@computed
|
||||
String get displayAmount => _appStore.amountParsingProxy
|
||||
.getDisplayCryptoAmount(_amount, tokenCurrency ?? wallet.currency);
|
||||
String get displayAmount => _appStore.amountParsingProxy.getDisplayCryptoAmount(
|
||||
_amount,
|
||||
tokenCurrency ??
|
||||
(selectedCurrency is CryptoCurrency
|
||||
? selectedCurrency as CryptoCurrency
|
||||
: wallet.currency),
|
||||
);
|
||||
|
||||
// NOT PRECISE! just for display purposes.
|
||||
@computed
|
||||
String get fiatAmount {
|
||||
if (_amount.isEmpty) return "";
|
||||
if (_amount.isEmpty) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var cryptoCurrency = tokenCurrency ?? wallet.currency;
|
||||
if (cryptoCurrency == CryptoCurrency.btcln) cryptoCurrency = CryptoCurrency.btc;
|
||||
if (cryptoCurrency == CryptoCurrency.btcln) {
|
||||
cryptoCurrency = CryptoCurrency.btc;
|
||||
}
|
||||
|
||||
if (selectedCurrency is FiatCurrency && _fiatRate != null) {
|
||||
return selectedCurrencyFiatAmount;
|
||||
}
|
||||
|
||||
if (!fiatConversionStore.prices.containsKey(cryptoCurrency)) return "";
|
||||
if (!fiatConversionStore.prices.containsKey(cryptoCurrency)) {
|
||||
return "";
|
||||
}
|
||||
final amount = double.tryParse(_amount) ?? 0;
|
||||
return (amount * fiatConversionStore.prices[cryptoCurrency]!).toStringAsFixed(2);
|
||||
}
|
||||
|
||||
@computed
|
||||
String get selectedCurrencyFiatAmount {
|
||||
if (_fiatRate == null) return "";
|
||||
if (_fiatRate == null) {
|
||||
return "";
|
||||
}
|
||||
final amount = double.tryParse(_amount) ?? 0;
|
||||
return (amount * _fiatRate!).toStringAsFixed(2);
|
||||
}
|
||||
@@ -201,26 +219,29 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
PaymentURI get uri {
|
||||
if (tokenCurrency != null && isEVMCompatibleChain(wallet.type)) {
|
||||
return ERC681URI(
|
||||
chainId: wallet.chainId ?? 1,
|
||||
address: wallet.walletAddresses.address,
|
||||
amount: _amount,
|
||||
contractAddress: (tokenCurrency as Erc20Token).contractAddress);
|
||||
chainId: wallet.chainId ?? 1,
|
||||
address: wallet.walletAddresses.address,
|
||||
amount: _amount,
|
||||
contractAddress: (tokenCurrency! as Erc20Token).contractAddress,
|
||||
);
|
||||
}
|
||||
if (tokenCurrency is TronToken && wallet.type == WalletType.tron) {
|
||||
return TronURI(
|
||||
amount: _amount,
|
||||
address: wallet.walletAddresses.address,
|
||||
contractAddress: (tokenCurrency as TronToken).contractAddress,
|
||||
contractAddress: (tokenCurrency! as TronToken).contractAddress,
|
||||
);
|
||||
}
|
||||
if (tokenCurrency is SPLToken && wallet.type == WalletType.solana) {
|
||||
return SolanaURI(
|
||||
amount: _amount,
|
||||
address: wallet.walletAddresses.address,
|
||||
contractAddress: (tokenCurrency as SPLToken).mintAddress,
|
||||
contractAddress: (tokenCurrency! as SPLToken).mintAddress,
|
||||
);
|
||||
}
|
||||
if (isLightning && _lnPaymentRequest != null) return _lnPaymentRequest!;
|
||||
if (isLightning && _lnPaymentRequest != null) {
|
||||
return _lnPaymentRequest!;
|
||||
}
|
||||
return wallet.walletAddresses.getPaymentUri(_amount);
|
||||
}
|
||||
|
||||
@@ -257,10 +278,11 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
final isPrimary = subaddress == primaryAddress;
|
||||
|
||||
return WalletAddressListItem(
|
||||
id: subaddress.id,
|
||||
isPrimary: isPrimary,
|
||||
name: subaddress.label,
|
||||
address: subaddress.address);
|
||||
id: subaddress.id,
|
||||
isPrimary: isPrimary,
|
||||
name: subaddress.label,
|
||||
address: subaddress.address,
|
||||
);
|
||||
});
|
||||
addressList.addAll(addressItems);
|
||||
}
|
||||
@@ -285,37 +307,37 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
addressList.addAll(addressItems);
|
||||
addressList.add(WalletAddressListHeader(title: S.current.received));
|
||||
|
||||
final receivedAddressItems =
|
||||
bitcoin!.getSilentPaymentReceivedAddresses(wallet).map((address) {
|
||||
return WalletAddressListItem(
|
||||
id: address.id,
|
||||
isPrimary: false,
|
||||
name: address.name,
|
||||
address: address.address,
|
||||
txCount: address.txCount,
|
||||
balance: _appStore.amountParsingProxy
|
||||
.getDisplayCryptoString(address.balance, walletTypeToCryptoCurrency(type)),
|
||||
isChange: address.isChange,
|
||||
isOneTimeReceiveAddress: true,
|
||||
derivationPath: address.derivationPath,
|
||||
);
|
||||
});
|
||||
final receivedAddressItems = bitcoin!.getSilentPaymentReceivedAddresses(wallet).map(
|
||||
(address) => WalletAddressListItem(
|
||||
id: address.id,
|
||||
isPrimary: false,
|
||||
name: address.name,
|
||||
address: address.address,
|
||||
txCount: address.txCount,
|
||||
balance: _appStore.amountParsingProxy
|
||||
.getDisplayCryptoString(address.balance, walletTypeToCryptoCurrency(type)),
|
||||
isChange: address.isChange,
|
||||
isOneTimeReceiveAddress: true,
|
||||
derivationPath: address.derivationPath,
|
||||
),
|
||||
);
|
||||
addressList.addAll(receivedAddressItems);
|
||||
} else {
|
||||
var addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) {
|
||||
final isPrimary = subaddress.id == 0;
|
||||
|
||||
return WalletAddressListItem(
|
||||
id: subaddress.id,
|
||||
isPrimary: isPrimary,
|
||||
name: subaddress.name,
|
||||
address: subaddress.address,
|
||||
txCount: subaddress.txCount,
|
||||
balance: _appStore.amountParsingProxy
|
||||
.getDisplayCryptoString(subaddress.balance, walletTypeToCryptoCurrency(type)),
|
||||
isChange: subaddress.isChange,
|
||||
isLegacyDerivation: subaddress.isLegacyDerivation,
|
||||
derivationPath: subaddress.derivationPath);
|
||||
id: subaddress.id,
|
||||
isPrimary: isPrimary,
|
||||
name: subaddress.name,
|
||||
address: subaddress.address,
|
||||
txCount: subaddress.txCount,
|
||||
balance: _appStore.amountParsingProxy
|
||||
.getDisplayCryptoString(subaddress.balance, walletTypeToCryptoCurrency(type)),
|
||||
isChange: subaddress.isChange,
|
||||
isLegacyDerivation: subaddress.isLegacyDerivation,
|
||||
derivationPath: subaddress.derivationPath,
|
||||
);
|
||||
});
|
||||
|
||||
// don't show all 1000+ mweb addresses:
|
||||
@@ -346,11 +368,13 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.nano) {
|
||||
addressList.add(WalletAddressListItem(
|
||||
isPrimary: true,
|
||||
name: null,
|
||||
address: wallet.walletAddresses.address,
|
||||
));
|
||||
addressList.add(
|
||||
WalletAddressListItem(
|
||||
isPrimary: true,
|
||||
name: null,
|
||||
address: wallet.walletAddresses.address,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.tron) {
|
||||
@@ -361,29 +385,35 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
|
||||
if (wallet.type == WalletType.decred) {
|
||||
final addrInfos = decred!.getAddressInfos(wallet);
|
||||
addrInfos.forEach((info) {
|
||||
for (final info in addrInfos) {
|
||||
addressList.add(
|
||||
new WalletAddressListItem(isPrimary: false, address: info.address, name: info.label));
|
||||
});
|
||||
WalletAddressListItem(isPrimary: false, address: info.address, name: info.label),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.zcash) {
|
||||
final addrInfos = zcash!.getAddressInfos(wallet);
|
||||
addrInfos.forEach((info) {
|
||||
for (final info in addrInfos) {
|
||||
addressList.add(
|
||||
new WalletAddressListItem(isPrimary: false, address: info.address, name: info.label));
|
||||
});
|
||||
WalletAddressListItem(isPrimary: false, address: info.address, name: info.label),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < addressList.length; i++) {
|
||||
if (!(addressList[i] is WalletAddressListItem)) continue;
|
||||
if (addressList[i] is! WalletAddressListItem) {
|
||||
continue;
|
||||
}
|
||||
final item = addressList[i] as WalletAddressListItem;
|
||||
item.isHidden = wallet.walletAddresses.hiddenAddresses.contains(item.address) ||
|
||||
(isElectrumWallet && item.isLegacyDerivation);
|
||||
}
|
||||
|
||||
for (var i = 0; i < addressList.length; i++) {
|
||||
if (!(addressList[i] is WalletAddressListItem)) continue;
|
||||
if (addressList[i] is! WalletAddressListItem) {
|
||||
continue;
|
||||
}
|
||||
(addressList[i] as WalletAddressListItem).isManual = wallet.walletAddresses.manualAddresses
|
||||
.contains((addressList[i] as WalletAddressListItem).address);
|
||||
}
|
||||
@@ -395,25 +425,25 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
}
|
||||
|
||||
if (searchText.isNotEmpty) {
|
||||
return ObservableList.of(addressList.where((item) {
|
||||
if (item is WalletAddressListItem) {
|
||||
return item.address.toLowerCase().contains(searchText.toLowerCase());
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
return ObservableList.of(
|
||||
addressList.where((item) {
|
||||
if (item is WalletAddressListItem) {
|
||||
return item.address.toLowerCase().contains(searchText.toLowerCase());
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return addressList;
|
||||
}
|
||||
|
||||
@computed
|
||||
ObservableList<ListItem> get addressList {
|
||||
return _computeAddressList();
|
||||
}
|
||||
ObservableList<ListItem> get addressList => _computeAddressList();
|
||||
|
||||
List<ListItem> get forceRecomputeItems {
|
||||
// necessary because the addressList contains non-observable items
|
||||
List<ListItem> recomputed = [];
|
||||
final recomputed = <ListItem>[];
|
||||
recomputed.addAll(_baseItems);
|
||||
recomputed.addAll(_computeAddressList());
|
||||
return recomputed;
|
||||
@@ -451,9 +481,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
case WalletType.wownero:
|
||||
wownero!.getCurrentAccount(wallet).label;
|
||||
default:
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
|
||||
@computed
|
||||
@@ -473,7 +503,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
WalletType.litecoin,
|
||||
WalletType.decred,
|
||||
WalletType.dogecoin,
|
||||
WalletType.zcash
|
||||
WalletType.zcash,
|
||||
].contains(wallet.type) &&
|
||||
!isLightning &&
|
||||
isZCashTransparent;
|
||||
@@ -486,7 +516,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
WalletType.bitcoin,
|
||||
WalletType.litecoin,
|
||||
WalletType.bitcoinCash,
|
||||
WalletType.dogecoin
|
||||
WalletType.dogecoin,
|
||||
].contains(wallet.type);
|
||||
|
||||
List<String> getWalletImages(int? chainId) {
|
||||
@@ -494,44 +524,44 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
switch (chainId) {
|
||||
case 1:
|
||||
return [
|
||||
'assets/new-ui/crypto_full_icons/ethereum.svg',
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/usdt_wallet_icon.svg',
|
||||
'assets/images/deuro_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
"assets/new-ui/crypto_full_icons/ethereum.svg",
|
||||
"assets/images/usdc_icon.svg",
|
||||
"assets/images/usdt_wallet_icon.svg",
|
||||
"assets/images/deuro_icon.svg",
|
||||
"assets/images/more_tokens.svg",
|
||||
];
|
||||
case 137:
|
||||
return [
|
||||
'assets/new-ui/crypto_full_icons/polygon.svg',
|
||||
'assets/images/eth_pol_icon.svg',
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/usdt_wallet_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
"assets/new-ui/crypto_full_icons/polygon.svg",
|
||||
"assets/images/eth_pol_icon.svg",
|
||||
"assets/images/usdc_icon.svg",
|
||||
"assets/images/usdt_wallet_icon.svg",
|
||||
"assets/images/more_tokens.svg",
|
||||
];
|
||||
case 8453:
|
||||
return [
|
||||
'assets/new-ui/crypto_full_icons/ethereum.svg',
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
"assets/new-ui/crypto_full_icons/ethereum.svg",
|
||||
"assets/images/usdc_icon.svg",
|
||||
"assets/images/more_tokens.svg",
|
||||
];
|
||||
case 42161:
|
||||
return [
|
||||
'assets/new-ui/crypto_full_icons/arbitrum.svg',
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
"assets/new-ui/crypto_full_icons/arbitrum.svg",
|
||||
"assets/images/usdc_icon.svg",
|
||||
"assets/images/more_tokens.svg",
|
||||
];
|
||||
case 56:
|
||||
return [
|
||||
'assets/new-ui/crypto_full_icons/bnb.svg',
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/usdt_wallet_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
"assets/new-ui/crypto_full_icons/bnb.svg",
|
||||
"assets/images/usdc_icon.svg",
|
||||
"assets/images/usdt_wallet_icon.svg",
|
||||
"assets/images/more_tokens.svg",
|
||||
];
|
||||
default:
|
||||
return [
|
||||
'assets/new-ui/crypto_full_icons/ethereum.svg',
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/usdt_wallet_icon.svg',
|
||||
"assets/new-ui/crypto_full_icons/ethereum.svg",
|
||||
"assets/images/usdc_icon.svg",
|
||||
"assets/images/usdt_wallet_icon.svg",
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -539,22 +569,22 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
switch (wallet.type) {
|
||||
case WalletType.solana:
|
||||
return [
|
||||
'assets/images/sol_icon.svg',
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/usdt_wallet_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
"assets/images/sol_icon.svg",
|
||||
"assets/images/usdc_icon.svg",
|
||||
"assets/images/usdt_wallet_icon.svg",
|
||||
"assets/images/more_tokens.svg",
|
||||
];
|
||||
case WalletType.tron:
|
||||
return [
|
||||
'assets/images/trx_icon.svg',
|
||||
'assets/images/usdc_icon.svg',
|
||||
'assets/images/usdt_wallet_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
"assets/images/trx_icon.svg",
|
||||
"assets/images/usdc_icon.svg",
|
||||
"assets/images/usdt_wallet_icon.svg",
|
||||
"assets/images/more_tokens.svg",
|
||||
];
|
||||
case WalletType.zano:
|
||||
return [
|
||||
'assets/images/zano_icon.svg',
|
||||
'assets/images/more_tokens.svg',
|
||||
"assets/images/zano_icon.svg",
|
||||
"assets/images/more_tokens.svg",
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
@@ -563,7 +593,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
|
||||
@computed
|
||||
String get qrImage {
|
||||
if (isLightning) return 'assets/images/btc_chain_qr_lightning.svg';
|
||||
if (isLightning) {
|
||||
return "assets/images/btc_chain_qr_lightning.svg";
|
||||
}
|
||||
return getQrImage(type);
|
||||
}
|
||||
|
||||
@@ -582,8 +614,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
|
||||
@computed
|
||||
bool get isLightning =>
|
||||
wallet.type == WalletType.bitcoin &&
|
||||
(wallet.walletAddresses.getPaymentUri(_amount) is LightningPaymentRequest);
|
||||
wallet.type == WalletType.bitcoin && selectedCurrency == CryptoCurrency.btcln;
|
||||
|
||||
@computed
|
||||
bool get isZCashTransparent {
|
||||
@@ -698,16 +729,14 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
@action
|
||||
void changeAmount(String amount) {
|
||||
if (selectedCurrency is FiatCurrency) {
|
||||
this._amount = amount;
|
||||
_amount = amount;
|
||||
_convertAmountToCrypto();
|
||||
} else if (selectedCurrency is CryptoCurrency) {
|
||||
this._amount = _appStore.amountParsingProxy
|
||||
_amount = _appStore.amountParsingProxy
|
||||
.getCanonicalCryptoAmount(amount, selectedCurrency as CryptoCurrency);
|
||||
}
|
||||
if (isLightning) {
|
||||
wallet.walletAddresses
|
||||
.getPaymentRequestUri(this._amount)
|
||||
.then((uri) => _lnPaymentRequest = uri);
|
||||
wallet.walletAddresses.getPaymentRequestUri(_amount).then((uri) => _lnPaymentRequest = uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,22 +748,24 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
@action
|
||||
void _convertAmountToCrypto() {
|
||||
var cryptoCurrency = tokenCurrency ?? wallet.currency;
|
||||
if (cryptoCurrency == CryptoCurrency.btcln) cryptoCurrency = CryptoCurrency.btc;
|
||||
if (cryptoCurrency == CryptoCurrency.btcln) {
|
||||
cryptoCurrency = CryptoCurrency.btc;
|
||||
}
|
||||
final fiatRate = _fiatRate ?? (fiatConversionStore.prices[cryptoCurrency] ?? 0.0);
|
||||
|
||||
if (fiatRate <= 0.0) {
|
||||
dev.log("Invalid Fiat Rate $fiatRate");
|
||||
_amount = '';
|
||||
_amount = "";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final crypto = (double.parse(_amount.replaceAll(',', '.')) / fiatRate).toStringAsFixed(8);
|
||||
final crypto = (double.parse(_amount.replaceAll(",", ".")) / fiatRate).toStringAsFixed(8);
|
||||
if (_amount != crypto) {
|
||||
_amount = crypto;
|
||||
}
|
||||
} catch (e) {
|
||||
_amount = '';
|
||||
_amount = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user