feat(wallet): show Lightning, Cashu and Fedimint activity in the transactions modal

- new lnd.lightning-history RPC: settled invoices (incoming) + succeeded
  payments (outgoing) normalized to the wallet-transaction shape; on-chain
  history stays in lnd.gettransactions
- Home merges on-chain + lightning + ecash history into one sorted list;
  every entry now carries kind (onchain/lightning/cashu/fedimint)
- modal renders a rail badge for non-onchain rows instead of a bogus
  confirmation count, and only on-chain rows link into mempool

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-07-13 14:10:45 +01:00
co-authored by Claude Fable 5
parent 533c0713c7
commit f600eaf145
5 changed files with 152 additions and 8 deletions
+30 -6
View File
@@ -7,9 +7,10 @@
<div v-else class="flex-1 overflow-y-auto -mx-2 px-2 divide-y divide-white/5">
<div
v-for="tx in transactions"
:key="tx.tx_hash"
class="flex items-center justify-between gap-3 py-3 hover:bg-white/5 rounded-lg px-2 cursor-pointer transition-colors"
@click="openInMempool(tx.tx_hash)"
:key="(tx.kind || 'onchain') + tx.tx_hash + tx.time_stamp"
class="flex items-center justify-between gap-3 py-3 hover:bg-white/5 rounded-lg px-2 transition-colors"
:class="isOnchain(tx) ? 'cursor-pointer' : 'cursor-default'"
@click="openInMempool(tx)"
>
<div class="flex items-center gap-3 min-w-0 flex-1">
<div
@@ -38,6 +39,7 @@
{{ tx.direction === 'incoming' ? '+' : '-' }}{{ Math.abs(tx.amount_sats).toLocaleString() }} sats
</span>
<span
v-if="isOnchain(tx)"
class="text-[10px] px-1.5 py-0.5 rounded-full font-medium"
:class="tx.num_confirmations === 0
? 'bg-yellow-500/15 text-yellow-400'
@@ -47,6 +49,13 @@
>
{{ tx.num_confirmations === 0 ? t('transactions.unconfirmed') : t('transactions.confirmations', { count: tx.num_confirmations }) }}
</span>
<span
v-else
class="text-[10px] px-1.5 py-0.5 rounded-full font-medium"
:class="tx.kind === 'lightning' ? 'bg-yellow-500/15 text-yellow-400' : tx.kind === 'cashu' ? 'bg-purple-500/15 text-purple-400' : 'bg-blue-500/15 text-blue-400'"
>
{{ kindLabel(tx) }}
</span>
</div>
<div class="flex items-center gap-2 mt-0.5">
<p class="text-[11px] text-white/40 font-mono truncate">{{ tx.tx_hash }}</p>
@@ -56,7 +65,7 @@
</div>
<div class="flex items-center gap-2 shrink-0">
<span class="text-[11px] text-white/40">{{ formatTxTime(tx.time_stamp) }}</span>
<svg class="w-3.5 h-3.5 text-white/30" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg v-if="isOnchain(tx)" class="w-3.5 h-3.5 text-white/30" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
</div>
@@ -80,6 +89,8 @@ interface WalletTransaction {
dest_addresses: string[]
label: string
block_height: number
// Which rail the transaction happened on; absent = onchain (older backends)
kind?: 'onchain' | 'lightning' | 'cashu' | 'fedimint'
}
defineProps<{
@@ -95,8 +106,21 @@ function close() {
emit('close')
}
function openInMempool(txHash: string) {
router.push({ name: 'app-session', params: { appId: 'mempool' }, query: { path: `/tx/${txHash}` } })
// Only on-chain transactions exist in mempool; Lightning/ecash rows don't link
function isOnchain(tx: WalletTransaction): boolean {
return !tx.kind || tx.kind === 'onchain'
}
function kindLabel(tx: WalletTransaction): string {
if (tx.kind === 'lightning') return '⚡ Lightning'
if (tx.kind === 'cashu') return 'Cashu'
if (tx.kind === 'fedimint') return 'Fedimint'
return ''
}
function openInMempool(tx: WalletTransaction) {
if (!isOnchain(tx)) return
router.push({ name: 'app-session', params: { appId: 'mempool' }, query: { path: `/tx/${tx.tx_hash}` } })
}
function formatTxTime(timestamp: number): string {