Files
hash-wallet/lib/view_model/transaction_details_view_model.dart
T
ef9b0fbae6 Ywallet zcash (#2810)
* feat(zcash): initial commit
- initial work on cw_zcash
  - use single .db for everything zcash to speed up sync and scanning
  - implement silent autoshielding of non-orchard funds
  - use FFI for everything to fetch the data
  - add support for passphrase by adding extra word to seed phrase
  - enforce lints on CI for cw_zcash directory
- use correct zcash name, and call transparent zcash tZEC
- minor cleanups outsite of cw_zcash
  - gitmodules, gitignore
  - db debug
  - fix broken addressPageType in WalletInfo
  - enable debug options in kDebugMode and kProfileMode

* feat: T address rotation (and cache) wip: minor fixes

* wip: shield tx list

* fix fiat amounts fix derrivation of rotation account addresses screen
for zcash block height fix T address shields improvements Amount in
disposable T shields UI improvements
show pending outgoing tx in tx history

* fix null check on passphrase page fix tx showing as pending rename auto
shield txs fixed send all to include fee use rotating address in
addressForExchange wrap all calls of WarpApi.getLatestHeight in try
catch to prevent leaking errors

other minor fixes from notion

* wallet group debug cache sent tx address wallet T address rotation fixes
automatically pick the fee for auto-shield

* fix T address cache deserialization fix autoshield icon fix autoshield
text getCachedDestinationAddress fix send address cache

* address comments from review

* wip: iOS fix

* fix swap

* Allow cakepay payment from zcash wallet

* fix: ios (I'm stupid) fix: reorder addresses to make orchard default
fix: send all balance

* 5.6.6 bump fix: ios apple review add transport error to ignored
exceptions

* fix: leaving dust behind when sweeping all

* Change wallets order

* fix: reflect balance of pending out txs fix: restore height support

* linux support (disabled)

* fix: date picker for zcash on restore screen fix: build script for linux
match WarpApi.warpSync args with upstream

* fix: linter script

* ensure all exchange providers support ZEC (#2802)

* ensure all exchange providers support ZEC

* remove zaddr and tzec currencies and refactor providers

* Update lib/exchange/provider/near_Intents_exchange_provider.dart

* add back old zcash currencies for backward compatibility, thanks Czarek

* minor fix [skip ci]

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* Update android/app/src/main/AndroidManifestBase.xml [skip ci]

* Update ios/Runner/InfoBase.plist [skip ci]

* fix: remove unused code fix: macos fix: disable on linux and macos

* update build numbers [skip ci]

* Add zcash to birdpay

---------

Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>
Co-authored-by: Serhii <borodenko.sv@gmail.com>
2026-01-16 03:16:41 +01:00

998 lines
37 KiB
Dart

import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/reactions/wallet_connect.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/blockexplorer_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/textfield_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/settings_store.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/src/intl/date_format.dart';
import 'package:mobx/mobx.dart';
import 'package:url_launcher/url_launcher.dart';
part 'transaction_details_view_model.g.dart';
class TransactionDetailsViewModel = TransactionDetailsViewModelBase
with _$TransactionDetailsViewModel;
abstract class TransactionDetailsViewModelBase with Store {
TransactionDetailsViewModelBase(
{required this.transactionInfo,
required this.transactionDescriptionBox,
required this.wallet,
required this.settingsStore,
required this.sendViewModel,
this.canReplaceByFee = false})
: items = [],
RBFListItems = [],
newFee = 0,
isRecipientAddressShown = false,
showRecipientAddress = settingsStore.shouldSaveRecipientAddress {
final dateFormat = DateFormatter.withCurrentLocal();
final tx = transactionInfo;
// TODO: can be cleaned further
switch (wallet.type) {
case WalletType.monero:
_addMoneroListItems(tx, dateFormat);
break;
case WalletType.bitcoin:
_addElectrumListItems(tx, dateFormat);
if (!canReplaceByFee) _checkForRBF(tx);
break;
case WalletType.litecoin:
_addLitecoinListItems(tx, dateFormat);
case WalletType.bitcoinCash:
_addElectrumListItems(tx, dateFormat);
break;
case WalletType.haven:
_addHavenListItems(tx, dateFormat);
break;
case WalletType.ethereum:
case WalletType.polygon:
case WalletType.base:
case WalletType.arbitrum:
_addEVMListItems(tx, dateFormat);
break;
case WalletType.nano:
_addNanoListItems(tx, dateFormat);
break;
case WalletType.solana:
_addSolanaListItems(tx, dateFormat);
break;
case WalletType.tron:
_addTronListItems(tx, dateFormat);
break;
case WalletType.wownero:
_addWowneroListItems(tx, dateFormat);
break;
case WalletType.zano:
_addZanoListItems(tx, dateFormat);
break;
case WalletType.decred:
_addDecredListItems(tx, dateFormat);
break;
case WalletType.dogecoin:
_addDogecoinListItems(tx, dateFormat);
break;
case WalletType.zcash:
_addZcashListItems(tx, dateFormat);
case WalletType.none:
case WalletType.banano:
break;
}
final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
final description = transactionDescriptionBox.values.firstWhere(
(val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
orElse: () => TransactionDescription(id: descriptionKey));
if (showRecipientAddress && !isRecipientAddressShown) {
final recipientAddress = description.recipientAddress;
if (recipientAddress?.isNotEmpty ?? false) {
items.add(
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: recipientAddress!,
key: ValueKey('standard_list_item_${recipientAddress}_key'),
),
);
}
}
final type = wallet.type;
items.add(
BlockExplorerListItem(
title: S.current.view_in_block_explorer,
value: _explorerDescription(type, wallet.chainId),
onTap: () async {
try {
final uri = Uri.parse(_explorerUrl(type, tx.txHash, wallet.chainId));
if (await canLaunchUrl(uri)) await launchUrl(uri, mode: LaunchMode.externalApplication);
} catch (e) {}
},
key: ValueKey('block_explorer_list_item_${type.name}_wallet_type_key'),
),
);
items.add(
TextFieldListItem(
title: S.current.note_tap_to_change,
value: description.note,
onSubmitted: (value) {
description.transactionNote = value;
if (description.isInBox) {
description.save();
} else {
transactionDescriptionBox.add(description);
}
},
key: ValueKey('textfield_list_item_note_entry_key'),
),
);
}
final TransactionInfo transactionInfo;
final Box<TransactionDescription> transactionDescriptionBox;
final SettingsStore settingsStore;
final WalletBase wallet;
final SendViewModel sendViewModel;
final List<TransactionDetailsListItem> items;
final List<TransactionDetailsListItem> RBFListItems;
bool showRecipientAddress;
bool isRecipientAddressShown;
int newFee;
String? rawTransaction;
TransactionPriority? transactionPriority;
@observable
bool canReplaceByFee;
String _explorerUrl(WalletType type, String txId, int? chainId) {
if (chainId != null) {
final explorerUrl = evm!.getExplorerUrlForChainId(chainId);
if (explorerUrl != null) return '$explorerUrl/tx/${txId}';
}
switch (type) {
case WalletType.monero:
return 'https://monero.com/tx/${txId}';
case WalletType.bitcoin:
return 'https://mempool.cakewallet.com/${wallet.isTestnet ? "testnet/" : ""}tx/${txId}';
case WalletType.litecoin:
return 'https://blockchair.com/litecoin/transaction/${txId}';
case WalletType.bitcoinCash:
return 'https://blockchair.com/bitcoin-cash/transaction/${txId}';
case WalletType.haven:
return 'https://explorer.havenprotocol.org/search?value=${txId}';
case WalletType.ethereum:
return 'https://etherscan.io/tx/${txId}';
case WalletType.base:
return 'https://basescan.org/tx/${txId}';
case WalletType.arbitrum:
return 'https://arbiscan.io/tx/${txId}';
case WalletType.polygon:
return 'https://polygonscan.com/tx/${txId}';
case WalletType.nano:
return 'https://nanexplorer.com/nano/block/${txId}';
case WalletType.banano:
return 'https://nanexplorer.com/banano/block/${txId}';
case WalletType.solana:
return 'https://solscan.io/tx/${txId}';
case WalletType.tron:
return 'https://tronscan.org/#/transaction/${txId}';
case WalletType.wownero:
return 'https://explore.wownero.com/tx/${txId}';
case WalletType.zano:
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}';
case WalletType.zcash:
return 'https://blockchair.com/zcash/transaction/${txId}';
case WalletType.none:
return '';
}
}
String _explorerDescription(WalletType type, int? chainId) {
if (chainId != null) {
final explorerUrl = evm!.getExplorerUrlForChainId(chainId, showProtocol: false);
if (explorerUrl != null) {
return S.current.view_transaction_on + explorerUrl;
}
}
switch (type) {
case WalletType.monero:
return S.current.view_transaction_on + 'Monero.com';
case WalletType.bitcoin:
return S.current.view_transaction_on + 'mempool.space';
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.dogecoin:
return S.current.view_transaction_on + 'Blockchair.com';
case WalletType.haven:
return S.current.view_transaction_on + 'explorer.havenprotocol.org';
case WalletType.ethereum:
return S.current.view_transaction_on + 'etherscan.io';
case WalletType.nano:
return S.current.view_transaction_on + 'nanexplorer.com';
case WalletType.banano:
return S.current.view_transaction_on + 'nanexplorer.com';
case WalletType.polygon:
return S.current.view_transaction_on + 'polygonscan.com';
case WalletType.solana:
return S.current.view_transaction_on + 'solscan.io';
case WalletType.tron:
return S.current.view_transaction_on + 'tronscan.org';
case WalletType.wownero:
return S.current.view_transaction_on + 'Wownero.com';
case WalletType.zano:
return S.current.view_transaction_on + 'explorer.zano.org';
case WalletType.decred:
return S.current.view_transaction_on + 'dcrdata.decred.org';
case WalletType.base:
return S.current.view_transaction_on + 'basescan.org';
case WalletType.arbitrum:
return S.current.view_transaction_on + 'arbiscan.io';
case WalletType.zcash:
return S.current.view_transaction_on + 'blockchair.com';
case WalletType.none:
return '';
}
}
void _addMoneroListItems(TransactionInfo tx, DateFormat dateFormat) {
final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
final description = transactionDescriptionBox.values.firstWhere(
(val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
orElse: () => TransactionDescription(id: descriptionKey));
final key = tx.additionalInfo['key'] as String? ?? description.transactionKey;
final accountIndex = tx.additionalInfo['accountIndex'] as int;
final addressIndex = tx.additionalInfo['addressIndex'] as int;
final feeFormatted = tx.feeFormatted();
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.transaction_details_height,
value: '${tx.height}',
key: ValueKey('standard_list_item_transaction_details_height_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
if (feeFormatted != null)
StandartListItem(
title: S.current.transaction_details_fee,
value: feeFormatted,
key: ValueKey('standard_list_item_transaction_details_fee_key'),
),
if (key?.isNotEmpty ?? false)
StandartListItem(
title: S.current.transaction_key,
value: key!,
key: ValueKey('standard_list_item_transaction_key'),
),
];
if (tx.direction == TransactionDirection.incoming) {
try {
final address = monero!.getTransactionAddress(wallet, accountIndex, addressIndex);
final label = monero!.getSubaddressLabel(wallet, accountIndex, addressIndex);
if (address.isNotEmpty) {
isRecipientAddressShown = true;
_items.add(
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: address,
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
),
);
}
if (label.isNotEmpty) {
_items.add(StandartListItem(
title: S.current.address_label,
value: label,
key: ValueKey('standard_list_item_address_label_key'),
));
}
} catch (e) {
printV(e.toString());
}
}
items.addAll(_items);
}
void _addElectrumListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.confirmations,
value: tx.confirmations.toString(),
key: ValueKey('standard_list_item_transaction_confirmations_key'),
),
StandartListItem(
title: S.current.transaction_details_height,
value: '${tx.height}',
key: ValueKey('standard_list_item_transaction_details_height_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
if (tx.feeFormatted()?.isNotEmpty ?? false)
StandartListItem(
title: S.current.transaction_details_fee,
value: tx.feeFormatted()!,
key: ValueKey('standard_list_item_transaction_details_fee_key'),
),
];
if (wallet.type == WalletType.bitcoin && tx.direction == TransactionDirection.incoming) {
try {
final addresses = bitcoin!.getTransactionAddresses(wallet, tx);
if (addresses != null) {
isRecipientAddressShown = true;
for (final address in addresses) {
_items.add(
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: address,
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
),
);
}
}
} catch (e) {
printV(e.toString());
}
}
items.addAll(_items);
}
void _addLitecoinListItems(TransactionInfo tx, DateFormat dateFormat) {
_addElectrumListItems(tx, dateFormat);
bool isMweb = bitcoin!.txIsMweb(tx);
final _items = [
if (isMweb)
BlockExplorerListItem(
title: S.current.view_in_block_explorer,
value: S.current.view_transaction_on + 'mwebexplorer.com',
onTap: () async {
try {
final uri = Uri.parse('https://www.mwebexplorer.com/blocks/block/${tx.height}');
if (await canLaunchUrl(uri))
await launchUrl(uri, mode: LaunchMode.externalApplication);
} catch (e) {}
},
key: ValueKey('block_explorer_list_item_mweb_wallet_type_key'),
),
];
items.addAll(_items);
}
void _addHavenListItems(TransactionInfo tx, DateFormat dateFormat) {
items.addAll([
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.transaction_details_height,
value: '${tx.height}',
key: ValueKey('standard_list_item_transaction_details_height_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
if (tx.feeFormatted()?.isNotEmpty ?? false)
StandartListItem(
title: S.current.transaction_details_fee,
value: tx.feeFormatted()!,
key: ValueKey('standard_list_item_transaction_details_fee_key'),
),
]);
}
void _addEVMListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.confirmations,
value: tx.confirmations.toString(),
key: ValueKey('standard_list_item_transaction_confirmations_key'),
),
StandartListItem(
title: S.current.transaction_details_height,
value: '${tx.height}',
key: ValueKey('standard_list_item_transaction_details_height_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
if (tx.feeFormatted()?.isNotEmpty ?? false)
StandartListItem(
title: S.current.transaction_details_fee,
value: tx.feeFormatted()!,
key: ValueKey('standard_list_item_transaction_details_fee_key'),
),
if (showRecipientAddress && tx.to != null)
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: tx.to!,
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
),
if (tx.direction == TransactionDirection.incoming && tx.from != null)
StandartListItem(
title: S.current.transaction_details_source_address,
value: tx.from!,
key: ValueKey('standard_list_item_transaction_details_source_address_key'),
),
];
items.addAll(_items);
}
void _addNanoListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
if (showRecipientAddress && tx.to != null)
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: tx.to!,
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
),
if (showRecipientAddress && tx.from != null)
StandartListItem(
title: S.current.transaction_details_source_address,
value: tx.from!,
key: ValueKey('standard_list_item_transaction_details_source_address_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.confirmed_tx,
value: (tx.confirmations > 0).toString(),
key: ValueKey('standard_list_item_transaction_confirmed_key'),
),
StandartListItem(
title: S.current.transaction_details_height,
value: '${tx.height}',
key: ValueKey('standard_list_item_transaction_details_height_key'),
),
];
items.addAll(_items);
}
void _addSolanaListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
if (tx.feeFormatted()?.isNotEmpty ?? false)
StandartListItem(
title: S.current.transaction_details_fee,
value: tx.feeFormatted()!,
key: ValueKey('standard_list_item_transaction_details_fee_key'),
),
if (showRecipientAddress && tx.to != null)
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: tx.to!,
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
),
if (tx.from != null)
StandartListItem(
title: S.current.transaction_details_source_address,
value: tx.from!,
key: ValueKey('standard_list_item_transaction_details_source_address_key'),
),
];
items.addAll(_items);
}
void addBumpFeesListItems(TransactionInfo tx, String rawTransaction) {
transactionPriority = bitcoin!.getBitcoinTransactionPriorityMedium();
final inputsCount = (transactionInfo.inputAddresses?.isEmpty ?? true)
? 1
: transactionInfo.inputAddresses!.length;
final outputsCount = (transactionInfo.outputAddresses?.isEmpty ?? true)
? 1
: transactionInfo.outputAddresses!.length;
newFee = bitcoin!.getFeeAmountForPriority(
wallet, bitcoin!.getBitcoinTransactionPriorityMedium(), inputsCount, outputsCount);
RBFListItems.add(
StandartListItem(
title: S.current.old_fee,
value: tx.feeFormatted() ?? '0.0',
key: ValueKey('standard_list_item_rbf_old_fee_key'),
),
);
if (transactionInfo.fee != null && rawTransaction.isNotEmpty) {
final size = bitcoin!.getTransactionVSize(wallet, rawTransaction);
final recommendedRate = (transactionInfo.fee! / size).round() + 1;
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);
final customItemIndex = customItem != null ? priorities.indexOf(customItem) : null;
final maxCustomFeeRate = sendViewModel.feesViewModel.maxCustomFeeRate?.toDouble();
RBFListItems.add(
StandardPickerListItem(
key: ValueKey('standard_picker_list_item_transaction_priorities_key'),
title: S.current.estimated_new_fee,
value: bitcoin!.formatterBitcoinAmountToString(amount: newFee) + ' ${wallet.currency}',
items: priorityForWalletType(wallet.type),
customValue: settingsStore.customBitcoinFeeRate.toDouble(),
maxValue: maxCustomFeeRate,
selectedIdx: selectedItem,
customItemIndex: customItemIndex ?? 0,
displayItem: (dynamic priority, double sliderValue) =>
sendViewModel.feesViewModel.displayFeeRate(priority, sliderValue.round()),
onSliderChanged: (double newValue) =>
setNewFee(value: newValue, priority: transactionPriority!),
onItemSelected: (dynamic item, double sliderValue) {
transactionPriority = item as TransactionPriority;
return setNewFee(value: sliderValue, priority: transactionPriority!);
},
),
);
if (transactionInfo.inputAddresses != null && transactionInfo.inputAddresses!.isNotEmpty) {
RBFListItems.add(
StandardExpandableListItem(
key: ValueKey('standard_expandable_list_item_transaction_input_addresses_key'),
title: S.current.inputs,
expandableItems: transactionInfo.inputAddresses!,
),
);
}
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) + '...';
}
return element;
}).toList();
RBFListItems.add(
StandardExpandableListItem(
title: S.current.outputs,
expandableItems: outputAddresses,
key: ValueKey('standard_expandable_list_item_transaction_output_addresses_key'),
),
);
}
}
void _addTronListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
if (tx.feeFormatted()?.isNotEmpty ?? false)
StandartListItem(
title: S.current.transaction_details_fee,
value: tx.feeFormatted()!,
key: ValueKey('standard_list_item_transaction_details_fee_key'),
),
if (showRecipientAddress && tx.to != null)
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: tron!.getTronBase58Address(tx.to!, wallet),
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
),
if (tx.from != null)
StandartListItem(
title: S.current.transaction_details_source_address,
value: tron!.getTronBase58Address(tx.from!, wallet),
key: ValueKey('standard_list_item_transaction_details_source_address_key'),
),
];
items.addAll(_items);
}
void _addDecredListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.transaction_details_height,
value: '${tx.height}',
key: ValueKey('standard_list_item_transaction_details_height_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
if (tx.feeFormatted()?.isNotEmpty ?? false)
StandartListItem(
title: S.current.transaction_details_fee,
value: tx.feeFormatted()!,
key: ValueKey('standard_list_item_transaction_details_fee_key'),
),
if (showRecipientAddress && tx.to != null)
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: tx.to!,
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
),
if (tx.from != null)
StandartListItem(
title: S.current.transaction_details_source_address,
value: tx.from!,
key: ValueKey('standard_list_item_transaction_details_source_address_key'),
),
];
items.addAll(_items);
}
void _addZcashListItems(TransactionInfo tx, DateFormat dateFormat) {
final memo = tx.additionalInfo['memo'] as String?;
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
if (tx.to != null)
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: tx.to!,
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.confirmations,
value: tx.confirmations.toString(),
key: ValueKey('standard_list_item_transaction_confirmations_key'),
),
StandartListItem(
title: S.current.transaction_details_height,
value: '${tx.height ?? 0}',
key: ValueKey('standard_list_item_transaction_details_height_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
if (tx.feeFormatted() != null && tx.feeFormatted()!.isNotEmpty)
StandartListItem(
title: S.current.transaction_details_fee,
value: tx.feeFormatted()!,
key: ValueKey('standard_list_item_transaction_details_fee_key'),
),
if (memo != null && memo.isNotEmpty)
StandartListItem(
title: S.current.memo,
value: memo,
key: ValueKey('standard_list_item_transaction_details_memo_key'),
),
];
items.addAll(_items);
}
void _addDogecoinListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.transaction_details_height,
value: '${tx.height}',
key: ValueKey('standard_list_item_transaction_details_height_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
if (tx.feeFormatted()?.isNotEmpty ?? false)
StandartListItem(
title: S.current.transaction_details_fee,
value: tx.feeFormatted()!,
key: ValueKey('standard_list_item_transaction_details_fee_key'),
),
if (showRecipientAddress && tx.to != null)
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: tx.to!,
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
),
if (tx.from != null)
StandartListItem(
title: S.current.transaction_details_source_address,
value: tx.from!,
key: ValueKey('standard_list_item_transaction_details_source_address_key'),
),
];
items.addAll(_items);
}
@action
Future<void> _checkForRBF(TransactionInfo tx) async {
if (wallet.type == WalletType.bitcoin &&
transactionInfo.direction == TransactionDirection.outgoing) {
final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
final description = transactionDescriptionBox.values
.firstWhereOrNull((val) => val.id == descriptionKey || val.id == transactionInfo.txHash);
if (RegExp(AddressValidator.silentPaymentAddressPatternMainnet)
.hasMatch(description?.recipientAddress ?? "")) {
canReplaceByFee = false;
return;
}
rawTransaction = await bitcoin!.canReplaceByFee(wallet, tx);
if (rawTransaction != null) {
canReplaceByFee = true;
}
}
}
String setNewFee({double? value, required TransactionPriority priority}) {
newFee = priority == bitcoin!.getBitcoinTransactionPriorityCustom() && value != null
? bitcoin!.feeAmountWithFeeRate(
wallet,
value.round(),
transactionInfo.inputAddresses?.length ?? 1,
transactionInfo.outputAddresses?.length ?? 1)
: bitcoin!.getFeeAmountForPriority(
wallet,
priority,
transactionInfo.inputAddresses?.length ?? 1,
transactionInfo.outputAddresses?.length ?? 1);
return bitcoin!.formatterBitcoinAmountToString(amount: newFee);
}
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;
void _addWowneroListItems(TransactionInfo tx, DateFormat dateFormat) {
final key = tx.additionalInfo['key'] as String?;
final accountIndex = tx.additionalInfo['accountIndex'] as int;
final addressIndex = tx.additionalInfo['addressIndex'] as int;
final feeFormatted = tx.feeFormatted();
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id,
value: tx.txHash,
key: ValueKey('standard_list_item_transaction_details_id_key'),
),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date),
key: ValueKey('standard_list_item_transaction_details_date_key'),
),
StandartListItem(
title: S.current.transaction_details_height,
value: '${tx.height}',
key: ValueKey('standard_list_item_transaction_details_height_key'),
),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted(),
key: ValueKey('standard_list_item_transaction_details_amount_key'),
),
if (feeFormatted != null)
StandartListItem(
title: S.current.transaction_details_fee,
value: feeFormatted,
key: ValueKey('standard_list_item_transaction_details_fee_key'),
),
if (key?.isNotEmpty ?? false)
StandartListItem(
title: S.current.transaction_key,
value: key!,
key: ValueKey('standard_list_item_transaction_key'),
),
];
if (tx.direction == TransactionDirection.incoming) {
try {
final address = wownero!.getTransactionAddress(wallet, accountIndex, addressIndex);
final label = wownero!.getSubaddressLabel(wallet, accountIndex, addressIndex);
if (address.isNotEmpty) {
isRecipientAddressShown = true;
_items.add(
StandartListItem(
title: S.current.transaction_details_recipient_address,
value: address,
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
),
);
}
if (label.isNotEmpty) {
_items.add(
StandartListItem(
title: S.current.address_label,
value: label,
key: ValueKey('standard_list_item_address_label_key'),
),
);
}
} catch (e) {
printV(e.toString());
}
}
items.addAll(_items);
}
void _addZanoListItems(TransactionInfo tx, DateFormat dateFormat) {
final comment = tx.additionalInfo['comment'] as String?;
items.addAll([
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(
title: 'Asset ID', value: tx.additionalInfo['assetId'] as String? ?? "Unknown asset id"),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'),
StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()),
if (tx.feeFormatted()?.isNotEmpty ?? false)
StandartListItem(title: S.current.transaction_details_fee, value: tx.feeFormatted()!),
if (comment != null && comment.isNotEmpty)
StandartListItem(title: S.current.transaction_details_title, value: comment),
]);
}
}