Files
nut-history/src/index.ts
T
Jon Staab 65f7179557 fix: Address review findings (CRITICAL/HIGH)
CRITICAL fixes:
- Stop passing mint URLs as relay URLs in initNDK (protocol mismatch)
- Add unit tests for buildTransactionHistory (dedup, fallback, sort)

HIGH fixes:
- Restore .claude entry in .gitignore
- Add warning logging for skipped malformed proof tags in nutzap parsing
- Add error handling + 30s timeout to NDK initialization
- Add unit tests for formatCSV and escapeField
- Export escapeField for testability

Also: add test script to package.json, include test/ in tsconfig

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 05:59:23 -07:00

87 lines
2.3 KiB
TypeScript

import { initNDK } from "./ndk.js";
import {
fetchWalletData,
fetchTokenEvents,
fetchHistoryEvents,
fetchNutzapEvents,
fetchNutzapRedemptions,
buildTransactionHistory,
} from "./events.js";
import { formatCSV } from "./csv.js";
function usage(): never {
console.error(`Usage: NOSTR_SECRET_KEY=<hex|nsec> bun run src/index.ts [--mints <url1,url2,...>]
Options:
--mints Comma-separated list of additional mint URLs to include
Environment:
NOSTR_SECRET_KEY Required. Nostr private key in hex or nsec format.`);
process.exit(1);
}
async function main() {
const secretKey = process.env.NOSTR_SECRET_KEY;
if (!secretKey) {
console.error("Error: NOSTR_SECRET_KEY environment variable is required");
usage();
}
// Parse --mints flag
const args = process.argv.slice(2);
const mintsIdx = args.indexOf("--mints");
const extraMints =
mintsIdx !== -1 && args[mintsIdx + 1]
? args[mintsIdx + 1].split(",").filter(Boolean)
: [];
// Initialize NDK (don't pass mints as relays — they use different protocols)
const { ndk, signer, user } = await initNDK(secretKey);
// Fetch wallet data first (need mints for context)
const walletData = await fetchWalletData(ndk, user, signer);
const allMints = [...new Set([...walletData.mints, ...extraMints])];
if (allMints.length > 0) {
console.error(`Discovered mints: ${allMints.join(", ")}`);
}
// Fetch all events in parallel
const [tokens, history, nutzaps, redemptions] = await Promise.all([
fetchTokenEvents(ndk, user, signer),
fetchHistoryEvents(ndk, user, signer),
fetchNutzapEvents(ndk, user),
fetchNutzapRedemptions(ndk, user, signer),
]);
// Build unified transaction history
const records = buildTransactionHistory(
walletData,
tokens,
history,
nutzaps,
redemptions
);
console.error(`Total transactions: ${records.length}`);
// Output CSV to stdout
const csv = formatCSV(records);
process.stdout.write(csv + "\n");
// Clean shutdown
// NDK doesn't have a disconnect method that returns a promise reliably,
// so we just exit after output
process.exit(0);
}
// Handle SIGINT
process.on("SIGINT", () => {
console.error("\nInterrupted.");
process.exit(1);
});
main().catch((err) => {
console.error(`Error: ${err instanceof Error ? err.message : err}`);
process.exit(1);
});