diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart
index a1d4bb3fd..7081f5929 100644
--- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart
+++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart
@@ -42,6 +42,7 @@ import '../../../../wallets/crypto_currency/intermediate/nano_currency.dart';
 import '../../../../wallets/isar/models/spark_coin.dart';
 import '../../../../wallets/isar/providers/wallet_info_provider.dart';
 import '../../../../wallets/wallet/impl/epiccash_wallet.dart';
+import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart';
 import '../../../../wallets/wallet/wallet_mixin_interfaces/rbf_interface.dart';
 import '../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
 import '../../../../widgets/background.dart';
@@ -57,6 +58,7 @@ import '../../../../widgets/icon_widgets/copy_icon.dart';
 import '../../../../widgets/icon_widgets/pencil_icon.dart';
 import '../../../../widgets/rounded_white_container.dart';
 import '../../../../widgets/stack_dialog.dart';
+import '../../../../widgets/tx_key_widget.dart';
 import '../../sub_widgets/tx_icon.dart';
 import '../../wallet_view.dart';
 import '../dialogs/cancelling_transaction_progress_dialog.dart';
@@ -96,6 +98,7 @@ class _TransactionV2DetailsViewState
   late final int minConfirms;
   late final EthContract? ethContract;
   late final bool supportsRbf;
+  late final bool hasTxKeyProbably;
 
   bool get isTokenTx => ethContract != null;
 
@@ -180,10 +183,16 @@ class _TransactionV2DetailsViewState
     _transaction = widget.transaction;
     walletId = widget.walletId;
 
+    final wallet = ref.read(pWallets).getWallet(walletId);
+
+    hasTxKeyProbably = wallet is LibMoneroWallet &&
+        (_transaction.type == TransactionType.outgoing ||
+            _transaction.type == TransactionType.sentToSelf);
+
     if (_transaction.type
         case TransactionType.sentToSelf || TransactionType.outgoing) {
       supportsRbf = _transaction.subType == TransactionSubType.none &&
-          ref.read(pWallets).getWallet(walletId) is RbfInterface;
+          wallet is RbfInterface;
     } else {
       supportsRbf = false;
     }
@@ -1704,6 +1713,17 @@ class _TransactionV2DetailsViewState
                                   ],
                                 ),
                               ),
+                            if (hasTxKeyProbably)
+                              isDesktop
+                                  ? const _Divider()
+                                  : const SizedBox(
+                                      height: 12,
+                                    ),
+                            if (hasTxKeyProbably)
+                              TxKeyWidget(
+                                walletId: walletId,
+                                txid: _transaction.txid,
+                              ),
                             isDesktop
                                 ? const _Divider()
                                 : const SizedBox(
diff --git a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart
index 76009f729..e515a570f 100644
--- a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart
+++ b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart
@@ -164,6 +164,13 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
 
   bool walletExists(String path);
 
+  String getTxKeyFor({required String txid}) {
+    if (libMoneroWallet == null) {
+      throw Exception("Cannot get tx key in uninitialized libMoneroWallet");
+    }
+    return libMoneroWallet!.getTxKey(txid);
+  }
+
   void _setListener() {
     if (libMoneroWallet != null && libMoneroWallet!.getListeners().isEmpty) {
       libMoneroWallet?.addListener(
diff --git a/lib/widgets/tx_key_widget.dart b/lib/widgets/tx_key_widget.dart
new file mode 100644
index 000000000..ba8e9b33c
--- /dev/null
+++ b/lib/widgets/tx_key_widget.dart
@@ -0,0 +1,102 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import '../pages/pinpad_views/pinpad_dialog.dart';
+import '../pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart';
+import '../pages_desktop_specific/password/request_desktop_auth_dialog.dart';
+import '../providers/global/wallets_provider.dart';
+import '../utilities/text_styles.dart';
+import '../utilities/util.dart';
+import '../wallets/wallet/intermediate/lib_monero_wallet.dart';
+import 'custom_buttons/blue_text_button.dart';
+import 'custom_buttons/simple_copy_button.dart';
+import 'detail_item.dart';
+
+class TxKeyWidget extends ConsumerStatefulWidget {
+  /// The [walletId] MUST be the id of a [LibMoneroWallet]!
+  const TxKeyWidget({
+    super.key,
+    required this.walletId,
+    required this.txid,
+  });
+
+  final String walletId;
+  final String txid;
+
+  @override
+  ConsumerState<TxKeyWidget> createState() => _TxKeyWidgetState();
+}
+
+class _TxKeyWidgetState extends ConsumerState<TxKeyWidget> {
+  String? _private;
+
+  bool _lock = false;
+
+  Future<void> _loadTxKey() async {
+    if (_lock) {
+      return;
+    }
+    _lock = true;
+
+    try {
+      final verified = await showDialog<String?>(
+        context: context,
+        builder: (context) => Util.isDesktop
+            ? const RequestDesktopAuthDialog(title: "Show private view key")
+            : const PinpadDialog(
+                biometricsAuthenticationTitle: "Show private view key",
+                biometricsLocalizedReason:
+                    "Authenticate to show private view key",
+                biometricsCancelButtonString: "CANCEL",
+              ),
+        barrierDismissible: !Util.isDesktop,
+      );
+
+      if (verified == "verified success" && mounted) {
+        final wallet =
+            ref.read(pWallets).getWallet(widget.walletId) as LibMoneroWallet;
+
+        _private = wallet.getTxKeyFor(txid: widget.txid);
+        if (_private!.isEmpty) {
+          _private = "Unavailable";
+        }
+
+        if (context.mounted) {
+          setState(() {});
+        } else {
+          _private == null;
+        }
+      }
+    } finally {
+      _lock = false;
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return DetailItemBase(
+      button: _private == null
+          ? CustomTextButton(
+              text: "Show",
+              onTap: _loadTxKey,
+              enabled: _private == null,
+            )
+          : Util.isDesktop
+              ? IconCopyButton(
+                  data: _private!,
+                )
+              : SimpleCopyButton(
+                  data: _private!,
+                ),
+      title: Text(
+        "Private view key",
+        style: STextStyles.itemSubtitle(context),
+      ),
+      detail: SelectableText(
+        // TODO
+        _private ?? "*" * 52, // 52 is approx length
+        style: STextStyles.w500_14(context),
+      ),
+    );
+  }
+}