// npub.cash-Abholung, Stufe 2: bezahlte Quotes bei der Mint einloesen. // Proofs landen SOFORT in wallet-proofs.json (Platte vor Kontext!). // Nutzung: node claim.js — alle bezahlten Quotes einloesen // node claim.js balance — lokale Ecash-Summe zeigen // Stand Wake 7: lockQuote=false (kein NUT-20 noetig). Mint: minibits. const fs = require('fs'); const path = require('path'); const { finalizeEvent } = require('nostr-tools'); const { Wallet, MintQuoteState } = require('@cashu/cashu-ts'); const key = JSON.parse(fs.readFileSync(__dirname + '/nostr-key.json', 'utf8')); const sk = Uint8Array.from(Buffer.from(key.sk_hex, 'hex')); const BASE = 'https://npubx.cash'; const STORE = path.join(__dirname, 'wallet-proofs.json'); function nip98(url, method) { const ev = finalizeEvent({ kind: 27235, created_at: Math.floor(Date.now() / 1000), tags: [['u', url], ['method', method]], content: '' }, sk); return Buffer.from(JSON.stringify(ev)).toString('base64'); } function loadStore() { try { return JSON.parse(fs.readFileSync(STORE, 'utf8')); } catch { return { proofs: [], claimed_quotes: [], mint: null }; } } function saveStore(s) { fs.writeFileSync(STORE, JSON.stringify(s, null, 2)); } function sum(proofs) { return proofs.reduce((a, p) => a + p.amount, 0); } (async () => { const store = loadStore(); if (process.argv[2] === 'balance') { console.log(`Lokal: ${sum(store.proofs)} sat in ${store.proofs.length} Proofs (Mint: ${store.mint || '-'})`); return; } // 1. JWT + Quotes const authUrl = `${BASE}/api/v2/auth/nip98`; const ar = await fetch(authUrl, { headers: { Authorization: `Nostr ${nip98(authUrl, 'GET')}`, Accept: 'application/json' } }); if (!ar.ok) throw new Error('auth ' + ar.status); const jwt = (await ar.json()).data.token; const qr = await fetch(`${BASE}/api/v2/wallet/quotes?offset=0&limit=50`, { headers: { Authorization: `Bearer ${jwt}`, Accept: 'application/json' } }); if (!qr.ok) throw new Error('quotes ' + qr.status); const quotes = (await qr.json()).data.quotes; console.log(`Quotes vom Server: ${quotes.length}`); const todo = quotes.filter(q => !store.claimed_quotes.includes(q.id)); if (!todo.length) { console.log('Nichts Neues einzuloesen. Lokal:', sum(store.proofs), 'sat'); return; } // 2. Pro Quote: Zustand pruefen, minten for (const q of todo) { const mintUrl = q.mint_url || 'https://mint.minibits.cash/Bitcoin'; const wallet = new Wallet(mintUrl); await wallet.loadMint(); const st = await wallet.checkMintQuoteBolt11(q.id); console.log(`Quote ${q.id}: ${q.amount} ${q.unit}, Mint-Zustand=${st.state}`); if (st.state !== MintQuoteState.PAID) continue; const proofs = await wallet.mintProofsBolt11(q.amount, q.id); store.proofs.push(...proofs); store.claimed_quotes.push(q.id); store.mint = mintUrl; saveStore(store); // sofort, pro Quote console.log(` -> ${sum(proofs)} sat gemintet und gespeichert. Gesamt lokal: ${sum(store.proofs)} sat`); } })().catch(e => { console.error('FEHLER:', e.message); process.exit(1); });