fix: avoid msat truncation when paying invoices with built-in amounts#879
fix: avoid msat truncation when paying invoices with built-in amounts#879
Conversation
Bump bitkit-core to v0.1.56 which rounds up sub-satoshi invoice amounts. Additionally, stop overriding the amount for invoices that already have one. Pass null so LDK uses the invoice's native msat precision instead of our truncated sats value converted back to msat. Only pass the amount for zero-amount invoices where the user specifies it. Closes #877
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
|
Observations from testing: 1) The fix rounds up to full sats precision for the payment and display amount, so ln invoice with from synonymdev/bitkit-e2e-tests#141. Screen.Recording.2026-04-02.at.12.58.54.mov2) There is an issue with lnurl-pay and and lnurl-withdrawal. For testing on local regtest, more utility commands for
Screen.Recording.2026-04-02.at.13.53.59.mov
Screen.Recording.2026-04-02.at.14.26.12.mov |
This comment has been minimized.
This comment has been minimized.
LNURL protocol uses millisatoshis, but the app was converting to sats and back for callbacks, losing the fractional part. For fixed-amount LNURL-pay (e.g. 500500 msat), ceil(min)=501 > floor(max)=500 caused the UI to show 0/invalid amount. - Add isFixedAmount() helpers that detect sub-sat fixed amounts - Add callbackAmountMsats() to return original msat for fixed amounts - Change fetchLnurlInvoice to accept amountMsats directly - For fixed-amount LNURL-withdraw, use floor division for invoice amount - Fix validateAmount for withdraw: use <= instead of < for max bound - Add unit tests for all new helper functions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| if (hasAmount) { | ||
| Logger.info("Found amount $$minSendable in lnurlPay, proceeding with payment", context = TAG) | ||
| if (isFixed) { | ||
| Logger.info("Found fixed amount $displaySats sats in lnurlPay, proceeding with payment", context = TAG) |
There was a problem hiding this comment.
CLAUDE.md violation: log parameter values must be wrapped in single quotes
Per CLAUDE.md:
ALWAYS wrap parameter values in log messages with single quotes, e.g.
Logger.info("Received event '$eventName'", context = TAG)
| Logger.info("Found fixed amount $displaySats sats in lnurlPay, proceeding with payment", context = TAG) | |
| Logger.info("Found fixed amount '$displaySats' sats in lnurlPay, proceeding with payment", context = TAG) |
This comment has been minimized.
This comment has been minimized.
- Pass null instead of data.sats for LNURL quick-pay so LDK uses the invoice's native msat precision (same fix as Bolt11 path) - Wrap log parameter values in single quotes per CLAUDE.md - Consolidate duplicate changelog entries into one Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Thanks for the thorough testing @piotr-iohk! Issue 1 (display 501 vs 500): This is because Issue 2 (LNURL-pay and LNURL-withdraw): Both fixed in the latest commits. Root cause was msat→sats→msat round-trip losing precision for LNURL callbacks. The fix:
Ready for re-testing. |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
For LNURL-withdraw with sub-sat precision (e.g. 222538 msat), neither floor (222 sats) nor ceiling (223 sats) matches the server's exact amount range. Add receiveMsats/createInvoiceMsats to create invoices with native msat precision, used for fixed-amount LNURL withdrawals. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
Ok, clear, thanks for explanation. I think that it is fine as it is then. |
|
I found remaining amount-display inconsistencies vs regular BOLT11 when using an msat-precision LNURL server.
Notes
Expected
|
Previously LNURL-pay showed 222 sats on review but 223 after sending, and LNURL-withdraw showed 222 while BOLT11 showed 223 for the same 222222 msat amount. Use ceiling division consistently for display. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@piotr-iohk Display inconsistency fixed — all LNURL display amounts now use ceiling division ( |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fixedWithdrawAmountSat() had no production call sites after switching to createInvoiceMsats for fixed-amount withdrawals and satsCeil for display. Remove the function, its test, and simplify the redundant displayAmount conditional. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
Ok, that works consistent accross BOLT11, LNURL pay and withdraw now. 👍 Unfortunately, while hardening/adapting e2e tests to this behavior, I spotted inconsistency on what's reported on the screens vs. on what is reported on the activity list... activity details show 222. This is common for LNURL-pay, withdraw and BOLT11 for invoices with msats precision... Screen.Recording.2026-04-03.at.18.19.40.mov |
Activity list showed 222 sats while review screen showed 223 for a 222222 msat payment. The PaymentDetails extension used floor division (amountMsat / 1000). Use ceiling to match all other display paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@piotr-iohk Fixed — the activity list was using floor division ( |
This comment has been minimized.
This comment has been minimized.
WakeNodeWorker and NotifyPaymentReceivedHandler used floor division for msat amounts in payment notifications. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
|
I'm testing using synonymdev/bitkit-docker#5 by generating a LNURL-PAY invoice via url: http://localhost:3000/generate/pay?minSendable=32222&maxSendable=32222During the send flow, I see 33 sat, after it's paid I see 32. It's strange seeing the conversation, descriptions, etc, which supposedly is expected to have the right implementation amendments for this exact issue that I encounter: bug_sat.mp4Had me double-check if I'm on the latest commit of this PR's branch, it sure seems so. EDIT
Looks like the problem might've been on my side.
|
There was a problem hiding this comment.
tAck
Tests
All tests used msat values.
- LnUrl-Pay (QuickPay ON & OFF) using synonymdev/bitkit-docker#5
- LnUrl-Withdraw (QuickPay ON & OFF) using synonymdev/bitkit-docker#5
- Bolt11 (QuickPay ON & OFF) using synonymdev/bitkit-docker#6
- regression: Bolt11 zero-amount invoice

Summary
nullso LDK uses the invoice's native msat precision instead of our truncated sats value converted back to msatTest plan
lnd.addInvoice({ valueMsat })using amounts222538,222222,500500msatDepends on synonymdev/bitkit-core#85
Closes #877
🤖 Generated with Claude Code