From 32c7b13e9fee4fc57f95b996302b4282d320fde9 Mon Sep 17 00:00:00 2001 From: Ashen <310210685+ashen0x@users.noreply.github.com> Date: Fri, 2 Oct 2026 22:47:10 +0530 Subject: [PATCH] fix(history): show which entries are waiting or cancelled --- src/cli.ts | 10 +++++++--- src/utils/history.test.ts | 9 ++++++++- src/utils/history.ts | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index c572b5f..c2da59e 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -60,6 +60,7 @@ import * as QRCode from "qrcode"; import { normalizeNostrPubkey, npubFromPubkey, npubFromSecretKey } from "./utils/nip98"; import { HISTORY_ENTRY_TYPES, + historyStatus, isHistoryEntryType, } from "./utils/history"; import { generateSecretKey, nip19 } from "nostr-tools"; @@ -1917,6 +1918,7 @@ program const idCol = "ID"; const timeCol = "Date/Time"; const typeCol = "Type"; + const statusCol = "Status"; const mintCol = "Mint"; const amtCol = "Amount"; @@ -1924,16 +1926,18 @@ program const id = String(entry.id ?? ""); const time = new Date(Number(entry.createdAt)).toISOString().replace("T", " ").slice(0, 19); const type = String(entry.type ?? "").toUpperCase(); + const status = historyStatus(String(entry.type ?? ""), String(entry.state ?? "")); const mint = String(entry.mintUrl ?? ""); const unit = String(entry.unit ?? "sat"); const amount = `${entry.amount} ${unit}`; - return { id, time, type, mint, amount }; + return { id, time, type, status, mint, amount }; }); const widths = { id: Math.max(idCol.length, ...rows.map((r) => r.id.length)), time: Math.max(timeCol.length, ...rows.map((r) => r.time.length)), type: Math.max(typeCol.length, ...rows.map((r) => r.type.length)), + status: Math.max(statusCol.length, ...rows.map((r) => r.status.length)), mint: Math.max(mintCol.length, ...rows.map((r) => r.mint.length)), amount: Math.max(amtCol.length, ...rows.map((r) => r.amount.length)), }; @@ -1944,14 +1948,14 @@ program // A single transaction lookup prints just its summary line. if (!options.id) { console.log( - `${pad(idCol, widths.id)} | ${pad(timeCol, widths.time)} | ${pad(typeCol, widths.type)} | ${pad(mintCol, widths.mint)} | ${pad(amtCol, widths.amount)}`, + `${pad(idCol, widths.id)} | ${pad(timeCol, widths.time)} | ${pad(typeCol, widths.type)} | ${pad(statusCol, widths.status)} | ${pad(mintCol, widths.mint)} | ${pad(amtCol, widths.amount)}`, ); console.log(sep); } for (const row of rows) { console.log( - `${pad(row.id, widths.id)} | ${pad(row.time, widths.time)} | ${pad(row.type, widths.type)} | ${pad(row.mint, widths.mint)} | ${pad(row.amount, widths.amount)}`, + `${pad(row.id, widths.id)} | ${pad(row.time, widths.time)} | ${pad(row.type, widths.type)} | ${pad(row.status, widths.status)} | ${pad(row.mint, widths.mint)} | ${pad(row.amount, widths.amount)}`, ); } }); diff --git a/src/utils/history.test.ts b/src/utils/history.test.ts index 581ce28..e7de1f5 100644 --- a/src/utils/history.test.ts +++ b/src/utils/history.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "bun:test"; -import { HISTORY_ENTRY_TYPES, isHistoryEntryType } from "./history"; +import { HISTORY_ENTRY_TYPES, historyStatus, isHistoryEntryType } from "./history"; describe("history entry types", () => { it("lists the four supported transaction types", () => { @@ -18,3 +18,10 @@ describe("history entry types", () => { expect(isHistoryEntryType("refund")).toBe(false); }); }); + +describe("history status", () => { + it("tells a stuck mint apart from a finished melt", () => { + expect(historyStatus("mint", "PAID")).toBe("paid, not minted"); + expect(historyStatus("melt", "PAID")).toBe(""); + }); +}); diff --git a/src/utils/history.ts b/src/utils/history.ts index 3640655..73f29c1 100644 --- a/src/utils/history.ts +++ b/src/utils/history.ts @@ -7,3 +7,21 @@ export type HistoryEntryType = (typeof HISTORY_ENTRY_TYPES)[number]; export function isHistoryEntryType(value: string): value is HistoryEntryType { return (HISTORY_ENTRY_TYPES as readonly string[]).includes(value); } + +// Finished entries map to "" so only the unusual rows carry a label. +const HISTORY_STATUS: Record = { + finalized: "", + ISSUED: "", + PAID: "", + prepared: "waiting", + pending: "waiting", + UNPAID: "waiting", + PENDING: "waiting", + rolledBack: "cancelled", +}; + +export function historyStatus(type: string, state: string): string { + // A PAID mint quote means paid but not minted yet, the case `wallet recover` handles. + if (type === "mint" && state === "PAID") return "paid, not minted"; + return HISTORY_STATUS[state] ?? state; +}