aboutsummaryrefslogtreecommitdiffstats
path: root/lib/nwc.ts
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-16 04:53:22 -0700
committerAdam Soltys <[email protected]>2026-09-16 04:53:22 -0700
commitc72fa2a5ccf1cc04073f9ff12606491971dadd36 (patch)
treee58124db24fb734c4a443b4ef20e9f25efe78c24 /lib/nwc.ts
parent19f73ae9cf2afffbbb2abc71eb80c6d5bdccca52 (diff)
downloadcoinos-server-c72fa2a5ccf1cc04073f9ff12606491971dadd36.tar.gz
coinos-server-c72fa2a5ccf1cc04073f9ff12606491971dadd36.zip
ecash: redeem tokens by melting to the user's own invoice
Redeeming ecash is now a plain NUT-05 melt into a Lightning invoice owned by the redeeming user, credited by the regular lightning listener. Drop the house proof pool and its lock, the /mint and /melt routes, and the special-casing of a mint account in NWC and payment routing. Tokens are decoded against the mint's keyset list so short v1 ids resolve, and an empty keyset list is never cached. Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'lib/nwc.ts')
-rw-r--r--lib/nwc.ts79
1 files changed, 40 insertions, 39 deletions
diff --git a/lib/nwc.ts b/lib/nwc.ts
index a15d6c86..5dba6e97 100644
--- a/lib/nwc.ts
+++ b/lib/nwc.ts
@@ -657,9 +657,12 @@ const handle = (method, params, ev, app, user) =>
if (payee === id) {
const invoice = await getInvoice(pr);
- const recipient = await g(`user:${invoice.uid}`);
+ const recipient = await g(`user:${invoice?.uid}`);
- if (recipient?.username !== "mint") {
+ // No coinos invoice record means the bolt11 is on our node but not
+ // ours to settle internally (a mint quote, say) — pay it over
+ // Lightning below like any other.
+ if (invoice) {
const { id: pid } = await sendInternal({
amount,
invoice,
@@ -823,48 +826,46 @@ const handle = (method, params, ev, app, user) =>
if (invoice) {
const recipient = await g(`user:${invoice.uid}`);
- if (recipient?.username !== "mint") {
- // Internal settlement never builds a bolt12 invoice request, so the
- // spec's deliver-or-reject rule for payer_note is met by handing the
- // note to the recipient as the payment memo (bolt11 + payer_note was
- // already rejected above)
- const { id: pid } = await sendInternal({
- amount,
- invoice,
- memo: payer_note,
- recipient,
- sender: user,
- maxTotal: remaining,
- });
+ // Internal settlement never builds a bolt12 invoice request, so the
+ // spec's deliver-or-reject rule for payer_note is met by handing the
+ // note to the recipient as the payment memo (bolt11 + payer_note was
+ // already rejected above)
+ const { id: pid } = await sendInternal({
+ amount,
+ invoice,
+ memo: payer_note,
+ recipient,
+ sender: user,
+ maxTotal: remaining,
+ });
- await db.lPush(`${pubkey}:payments`, pid);
+ await db.lPush(`${pubkey}:payments`, pid);
- if (invoice.memo?.includes("9734")) {
- const { invoices } = await ln.listinvoices({ invstring: pr });
- const inv = invoices[0];
- if (inv) {
- inv.payment_preimage = pid;
- inv.paid_at = created_at;
- try {
- await handleZap(inv, user.pubkey);
- } catch (e) {
- console.log("zap receipt failed", e);
- }
+ if (invoice.memo?.includes("9734")) {
+ const { invoices } = await ln.listinvoices({ invstring: pr });
+ const inv = invoices[0];
+ if (inv) {
+ inv.payment_preimage = pid;
+ inv.paid_at = created_at;
+ try {
+ await handleZap(inv, user.pubkey);
+ } catch (e) {
+ console.log("zap receipt failed", e);
}
}
-
- // Internal settlement never touches CLN, so there's no real
- // preimage or payer proof to hand back
- return result({
- transaction_id: pid,
- state: "settled",
- instruction_type,
- amount: amountMsat,
- fees_paid: 0,
- created_at,
- settled_at: Math.floor(Date.now() / 1000),
- });
}
+
+ // Internal settlement never touches CLN, so there's no real
+ // preimage or payer proof to hand back
+ return result({
+ transaction_id: pid,
+ state: "settled",
+ instruction_type,
+ amount: amountMsat,
+ fees_paid: 0,
+ created_at,
+ settled_at: Math.floor(Date.now() / 1000),
+ });
}
try {