From 48eb895cfb21c7e679344eaafebd446c4ccd949b Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sat, 7 Nov 2026 13:47:19 -0700 Subject: Prepaid Lightning withdraw links: Send → ⚡ Withdraw link makes an LNURL (QR + copy) that anyone can withdraw sats from, like a voucher, with this wallet asleep. "Do we have lnurl withdraw links somewhere? can we add them?" → "Prepaid" (Adam, 2026-11-07); redeeming them already worked (38ce0b4), making them didn't. Amount per withdrawal, how many withdrawals (1–1000), an optional description; the whole amount is paid up front from Spending over Lightning to the coinos registrar (coinosv3-server 912944d, names/withdraw.js), which pays each claim out of it, so it works with any Lightning wallet while the phone is off; "Your withdraw links" lists them (what's left, used up, closed) and a link's page has Take back {what's left}, paid to a fresh Lightning receive of this wallet, which closes the link. Links belong to the wallet's nostr key (NIP-98), so every device with the seed sees them. Lives with the gift link (deferred gifts chunk, ui.giftMode === 'lnurl'). tools/lnurl-withdraw-links-test.js: makes one against the real service behind a stand-in server with a fake node, claims it through our own redeem flow, takes the rest back MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q --- src/features/gifts.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/features/gifts.js') diff --git a/src/features/gifts.js b/src/features/gifts.js index f23ecc8..c6ccbd9 100644 --- a/src/features/gifts.js +++ b/src/features/gifts.js @@ -8,6 +8,7 @@ import { decryptWithCode, npubOf } from '../nostr.js'; import { installGiftWallet, previewGift, giftOutpoints, giftClaimTxs, isRetiredGift, giftMinimum, lockGift, previewLockedGift } from './gifts-wallet.js'; import { t } from '../i18n.js'; import { qrSvg } from '../qr.js'; +import { withdrawLinks } from './withdraw-links.js'; import { fmtBtc, timeAgo, shortAddr } from '../format.js'; import { getEventHash, verifyEvent } from 'nostr-tools/pure'; import { sha256 } from '@noble/hashes/sha256'; @@ -26,6 +27,7 @@ export function giftsFeature(ctx) { // every call degrades to null in an on-chain-only build. const hook = ctx.hook || (() => null); const arkGiftOf = (code) => hook('arkGiftDecode', code); + const wl = withdrawLinks(ctx); // prepaid LNURL-withdraw links, the gift link's sibling on Send // ================================================================ GIFT / CLAIM // Read a gift link, returning the code (and scrubbing it from the URL so the @@ -1085,9 +1087,11 @@ export function giftsFeature(ctx) { return { blob, claimCode, url: `${location.origin}/lg/${blob}` }; }, receiveTakeover() { return claimCelebration(); }, - sendView() { return ui.giftMode ? giftView() : null; }, + sendView() { return ui.giftMode === 'lnurl' ? wl.view() : ui.giftMode ? giftView() : null; }, sendFormExtras() { - return h('button', { class: 'linklike small', style: 'align-self:center;margin-top:2px', onClick: () => { ui.giftMode = true; ui.sendError = ''; render(); } }, '🎁 ' + t('giftLink')); + return h('div', { class: 'row gap6 wrap', style: 'justify-content:center;column-gap:18px' }, + h('button', { class: 'linklike small', style: 'margin-top:2px', onClick: () => { ui.giftMode = true; ui.sendError = ''; render(); } }, '🎁 ' + t('giftLink')), + wl.entry()); }, balanceLines() { const locked = wallet.giftLockedValue(); -- cgit v1.2.3