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:
Konstantin Ullrich
2026-07-17 17:49:17 +03:00
committed by GitHub
co-authored by Robert Malikowski Omar Hatem
parent 28c2cc152e
commit 03edd658a5
18 changed files with 1193 additions and 1003 deletions
+275 -251
View File
@@ -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 = "";
}
}