feat(ux): scan-to-cash-out, first-run walkthrough, plain language
Cashing out was the least approachable thing here: open your wallet, create an invoice for exactly the right amount, copy it, come back, paste it. That is the step people abandon, leaving sats behind. LNURL-withdraw replaces it with a scan. The arcade shows a code, the wallet pulls the funds, and the player never handles an invoice or types an amount. The paste path is kept for wallets without LNURL support, but folded away. The withdraw token is a bearer instrument, so it is random, single-use, bound to one account and one amount, and expires in five minutes. Sixteen goroutines racing one code yield exactly one payment. Funds are debited when the code is issued — otherwise a player could cash out and bet the same sats before the wallet claimed them — and a sweep refunds any code that is never scanned. bech32 is verified against the BIP-173 vectors, including the invalid ones. Getting this wrong produces codes that silently fail to scan with no useful error for the player. Adds a three-card first-run walkthrough, an explanation of what a multiplier target means, and a one-time confirmation before a player's first real-money action — the interface is deliberately frictionless, and that is the one place a moment of friction is worth it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -93,6 +93,10 @@
|
||||
<button data-auto="5">5</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="explain" id="auto-explain">
|
||||
Leave this off to cash out by tapping. Set it and you stop
|
||||
automatically — 2× means you double your stake and get out.
|
||||
</p>
|
||||
|
||||
<button class="primary big" id="action">Place bet</button>
|
||||
<div class="hint" id="hint"></div>
|
||||
@@ -203,18 +207,38 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lightning out. -->
|
||||
<!-- Lightning out. Scanning is the whole flow: no invoice to create, no
|
||||
amount to type into a second app. -->
|
||||
<div class="card" id="card-withdraw" hidden>
|
||||
<h2>Cash out to Lightning</h2>
|
||||
<h2>Cash out</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.
|
||||
Pick an amount and scan the code with your Lightning wallet. Your
|
||||
wallet pulls the sats — you never make an invoice.
|
||||
</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="stakerow">
|
||||
<button class="chip" data-wd="1000">1k</button>
|
||||
<button class="chip" data-wd="5000">5k</button>
|
||||
<button class="chip" data-wd="all">All</button>
|
||||
<input id="wd-amt" type="number" min="1" inputmode="numeric" placeholder="sats">
|
||||
</div>
|
||||
<button class="primary" id="do-withdraw">Show cash-out code</button>
|
||||
<div class="hint" id="wd-hint"></div>
|
||||
|
||||
<div id="wd-code" hidden>
|
||||
<div class="qrwrap" id="wd-qr"></div>
|
||||
<p class="muted small center" id="wd-expiry"></p>
|
||||
<button id="wd-copy">Copy code</button>
|
||||
</div>
|
||||
|
||||
<details class="proof">
|
||||
<summary>Paste an invoice instead</summary>
|
||||
<p class="muted small">
|
||||
If your wallet cannot scan LNURL, make an invoice for the amount and
|
||||
paste it here.
|
||||
</p>
|
||||
<input id="wd-bolt11" placeholder="lnbc… invoice" autocomplete="off">
|
||||
<button id="do-withdraw-manual">Withdraw to invoice</button>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
@@ -294,6 +318,18 @@
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- First run only. Three cards, then it never appears again. -->
|
||||
<div class="tour" id="tour" hidden>
|
||||
<div class="tourcard">
|
||||
<div class="tourstep" id="tour-step"></div>
|
||||
<h2 id="tour-title"></h2>
|
||||
<p id="tour-body"></p>
|
||||
<div class="tourdots" id="tour-dots"></div>
|
||||
<button class="primary" id="tour-next">Got it</button>
|
||||
<button class="tourskip" id="tour-skip">Skip</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module" src="/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user