Skip to content

fix(lnurlp): emit min/maxSendable in msats per LUD-06 - #39

Merged
islandbitcoin merged 2 commits into
mainfrom
eng-540-lnurlp-msat-units
Aug 6, 2026
Merged

fix(lnurlp): emit min/maxSendable in msats per LUD-06#39
islandbitcoin merged 2 commits into
mainfrom
eng-540-lnurlp-msat-units

Conversation

@islandbitcoin

Copy link
Copy Markdown

Summary

Fixes the root cause of ENG-540: every Flash lightning address advertised maxSendable: 150000000 msats (= 150k sats ≈ $97) and minSendable: 1 msat.

The /.well-known/lnurlp/[username] handler resolves the user's underlying IBEX lnurl with requestPayServiceParams from the lnurl-pay package — which returns details.min/details.max in satoshis — and re-emitted those numbers as minSendable/maxSendable, which LUD-06 defines in millisatoshis. A silent 1000× under-advertisement.

IBEX's raw payRequest is already correct (maxSendable: 150000000000 msats = 150M sats, minSendable: 1000 msats = 1 sat). Spec-compliant wallets (Breez SDK in flash-mobile, Phoenix, Zeus, Strike…) enforce the advertised bounds client-side, so every payment above ~150k sats to any name@flashapp.me address was refused before an invoice was ever requested — including in-app BTC-wallet sends to Flash addresses (fixed UX-side in lnflash/flash-mobile#680).

The fix passes the upstream service's raw msat values through verbatim, falling back to sats * 1000 if rawData is unavailable.

Test plan

  • prettier --check, eslint, tsc --noEmit all clean (repo has no unit-test runner)
  • Live verification of the exact mapping logic against IBEX's payRequest for the rewards account: raw msats 1000 / 150000000000 pass through unchanged
  • Post-deploy: curl -s https://flashapp.me/.well-known/lnurlp/rewards | jq '.maxSendable, .minSendable' → expect 150000000000 and 1000, then a >$100 BTC-wallet send in the app and a >150k-sat send from an external wallet

🤖 Generated with Claude Code

https://claude.ai/code/session_01G7A41jET8aH7u5WXP7EZoS

The well-known lnurlp handler re-emitted lnurl-pay's requestPayServiceParams
values (details.min/max, denominated in SATS) directly as minSendable /
maxSendable, which LUD-06 defines as MILLISATS. Every Flash lightning
address therefore advertised a max-receive of 150,000 sats (~$97) instead
of the 150M sats IBEX actually allows (raw maxSendable 150,000,000,000
msats), and a sub-1-sat minSendable of 1 msat.

Spec-compliant wallets (Breez SDK in flash-mobile, Phoenix, Zeus, etc.)
enforce these bounds client-side, so all payments above ~150k sats to any
name@flashapp.me address were refused before an invoice was ever requested.

Prefer the upstream service's raw msat values verbatim; fall back to
sats * 1000 if rawData is unavailable.

Verified against the live IBEX payRequest: raw msats 1,000 /
150,000,000,000 now pass through unchanged (150M sats max).

Linear: ENG-540

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G7A41jET8aH7u5WXP7EZoS
@linear

linear Bot commented Aug 6, 2026

Copy link
Copy Markdown

ENG-540

… byte-faithfully

Review follow-ups on #39:

- move the min/maxSendable resolution into a pure lib/lnurl.ts helper so it
  can be unit-tested once the repo grows a test runner, and reused by any
  future handler needing LUD-06 fields
- prefer the upstream service's raw metadata string verbatim: wallets verify
  the invoice description hash against the exact bytes of this string, so a
  re-serialized parse (key order, whitespace) could invalidate otherwise-good
  invoices — same passthrough-fidelity class as the msat fix

Live-verified against IBEX: emitted minSendable 1000 / maxSendable
150000000000 msats, metadata byte-identical to the raw service response,
and the rawData-less fallback path produces the same msat values.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G7A41jET8aH7u5WXP7EZoS
@islandbitcoin
islandbitcoin merged commit ec69592 into main Aug 6, 2026
islandbitcoin added a commit to lnflash/charts that referenced this pull request Aug 6, 2026
Picks up lnflash/flash-pay#39 — the lnurlp well-known handler was re-emitting
lnurl-pay's sat-denominated values as the msat LUD-06 minSendable/maxSendable
fields, advertising a 150k-sat (~$97) receive cap on every Flash lightning
address instead of IBEX's actual 150M sats. Also passes metadata through
byte-faithfully.

Image digest sha256:62a48b94... = flash-pay 0.6.1, built from ec69592
(post-merge main). ENG-540.


Claude-Session: https://claude.ai/code/session_01G7A41jET8aH7u5WXP7EZoS

Co-authored-by: Dread <dread@example.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@islandbitcoin
islandbitcoin deleted the eng-540-lnurlp-msat-units branch August 7, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant