feat(wallet): Lightning deposit and withdrawal in the client

Deposit shows a scannable QR, because nobody types a 400-character
invoice at a party. The QR encoder is written rather than imported: a CDN
script is a dependency on the outside world, and this box has to work on
a network with no internet.

Its tests caught a real bug — the format-information loop wrote eight
cells down column 8, but the eighth is the dark module, which is fixed.
Overwriting it produces symbols some readers reject.

The deposit and withdraw cards stay hidden unless the server reports a
node, so a play-money deployment does not advertise a deposit it cannot
honour. Settlement is polled and confirmed server-side against the node,
so the client cannot claim a payment that never arrived.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
drjones
2026-08-06 04:18:57 +00:00
parent bf75302e07
commit c250ed2f80
4 changed files with 572 additions and 0 deletions

View File

@@ -177,6 +177,46 @@
<code class="pubkey" id="pubkey"></code>
<button id="copykey">Copy</button>
</div>
<!-- Lightning in. Hidden unless the server has a node configured, so a
play-money deployment does not advertise a deposit it cannot take. -->
<div class="card" id="card-deposit" hidden>
<h2>Add sats</h2>
<div class="stakerow">
<button class="chip" data-dep="1000">1k</button>
<button class="chip" data-dep="5000">5k</button>
<button class="chip" data-dep="25000">25k</button>
<input id="dep-amt" type="number" min="1" inputmode="numeric" placeholder="sats">
</div>
<button class="primary" id="do-deposit">Create invoice</button>
<div class="hint" id="dep-hint"></div>
<div id="dep-invoice" hidden>
<div class="qrwrap" id="dep-qr"></div>
<code class="pubkey" id="dep-bolt11"></code>
<div class="stakerow">
<button id="dep-copy">Copy invoice</button>
<button id="dep-check">I have paid</button>
</div>
<p class="muted small" id="dep-status">
Scan with any Lightning wallet. Your balance updates once it settles.
</p>
</div>
</div>
<!-- Lightning out. -->
<div class="card" id="card-withdraw" hidden>
<h2>Cash out to Lightning</h2>
<p class="muted small">
Paste an invoice from your own wallet for the amount you want. Large
withdrawals are held for the operator to approve.
</p>
<input id="wd-bolt11" placeholder="lnbc… invoice" autocomplete="off">
<input id="wd-amt" type="number" min="1" inputmode="numeric"
placeholder="amount in sats">
<button class="primary" id="do-withdraw">Withdraw</button>
<div class="hint" id="wd-hint"></div>
</div>
<div class="card">
<h2>Send sats</h2>
<input id="to-key" placeholder="recipient key" autocomplete="off">