From 671cdb3af944b5e6d39361f4a208547d0e9f7291 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 21 Sep 2026 19:21:29 -0700 Subject: send: quote the lightning routing fee up front and add Max to lightning sends The lightning send page now asks the server what the payment will cost before showing the Send button: the exact routing fee from the route query, the platform fee, and the total. The quoted fee is submitted as the maxfee, so what the user sees is what gets debited; advanced settings still let them override it, and if the quote fails the page falls back to the old 2% reserve. Max on the amount step asks the server for the largest amount whose amount + routing fee + platform fee lands exactly on the balance, and for an offer picks up the invoice fetched for that exact amount. LNURL-pay gets a Max button too. The invoice only exists once we name an amount, so the action fetches a probe invoice at the ceiling, lets the server solve for the amount, then fetches the real invoice for it. Co-Authored-By: Claude Fable 5.1 --- src/components/Lnurlp.svelte | 17 ++++- src/lib/samples.ts | 14 ++++ src/locales/en.json | 4 +- src/routes/(app)/ln/[lnurl]/+page.server.ts | 55 +++++++++++++++- .../lightning/[payreq]/[...memo]/+page.server.ts | 45 ++++++++++++- .../send/lightning/[payreq]/[...memo]/+page.svelte | 76 +++++++++++++++++++--- src/routes/(static)/docs/+page.svelte | 17 ++++- 7 files changed, 213 insertions(+), 15 deletions(-) diff --git a/src/components/Lnurlp.svelte b/src/components/Lnurlp.svelte index 8f14ddff..2b715667 100644 --- a/src/components/Lnurlp.svelte +++ b/src/components/Lnurlp.svelte @@ -6,11 +6,13 @@ import Numpad from "$comp/Numpad.svelte"; import Spinner from "$comp/Spinner.svelte"; import { pin } from "$lib/store"; + import { s } from "$lib/utils"; let { data, form, send = $bindable(), comment = $bindable() } = $props(); let { currency } = data.user; - let { minSendable, maxSendable, commentAllowed, callback, metadata, rate } = + let { minSendable, maxSendable, commentAllowed, callback, metadata, rate, balance } = data; + let max = $derived(Math.min(balance || 0, Math.floor(maxSendable / 1000))); let amount = $state(Math.round(minSendable / 1000)), loading = $state(); @@ -56,8 +58,17 @@ > {/if} -
- + {/if} + + +
+ + +
{/if}
diff --git a/src/routes/(static)/docs/+page.svelte b/src/routes/(static)/docs/+page.svelte index c323f9bd..7cf7e2cb 100644 --- a/src/routes/(static)/docs/+page.svelte +++ b/src/routes/(static)/docs/+page.svelte @@ -162,8 +162,23 @@

+

POST /lightning/quote

+

+ Find out what a lightning payment will cost before sending it. The routing + fee comes from querying the route ahead of time and is exact; pass it as + fee to POST /payments. Pass amount for an amountless invoice, + or max to solve for the largest amount whose amount + fees uses the + whole balance. +

+ +

Sample response

+ +

POST /payments

-

Send a lightning payment

+

+ Send a lightning payment. fee caps the routing fee; if omitted, the + route is quoted and the exact fee used. +

Send an internal payment to another user

-- cgit v1.2.3