Skip to content

Commit 2a8037d

Browse files
ben-kaufmanclaude
andcommitted
fix: use msat-precision invoices for fixed-amount LNURL withdraw
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>
1 parent 74f3972 commit 2a8037d

3 files changed

Lines changed: 29 additions & 16 deletions

File tree

app/src/main/java/to/bitkit/repositories/LightningRepo.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,15 @@ class LightningRepo @Inject constructor(
897897
runCatching { lightningService.receive(amountSats, description, expirySeconds) }
898898
}
899899

900+
suspend fun createInvoiceMsats(
901+
amountMsats: ULong,
902+
description: String,
903+
expirySeconds: UInt = 86_400u,
904+
): Result<String> = executeWhenNodeRunning("createInvoiceMsats") {
905+
updateGeoBlockState()
906+
runCatching { lightningService.receiveMsats(amountMsats, description, expirySeconds) }
907+
}
908+
900909
@Suppress("ForbiddenComment")
901910
suspend fun fetchLnurlInvoice(
902911
callbackUrl: String,

app/src/main/java/to/bitkit/services/LightningService.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,15 +590,19 @@ class LightningService @Inject constructor(
590590
}
591591

592592
suspend fun receive(sat: ULong? = null, description: String, expirySecs: UInt = 3600u): String {
593+
return receiveMsats(amountMsat = sat?.let { it * 1000u }, description = description, expirySecs = expirySecs)
594+
}
595+
596+
suspend fun receiveMsats(amountMsat: ULong? = null, description: String, expirySecs: UInt = 3600u): String {
593597
val node = this.node ?: throw ServiceError.NodeNotSetup()
594598

595599
val message = description
596600

597601
return ServiceQueue.LDK.background {
598-
val bolt11Invoice: Bolt11Invoice = if (sat != null) {
602+
val bolt11Invoice: Bolt11Invoice = if (amountMsat != null) {
599603
node.bolt11Payment()
600604
.receive(
601-
amountMsat = sat * 1000u,
605+
amountMsat = amountMsat,
602606
description = Bolt11InvoiceDescription.Direct(description = message),
603607
expirySecs = expirySecs,
604608
)

app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,23 +1887,23 @@ class AppViewModel @Inject constructor(
18871887
return@launch
18881888
}
18891889

1890-
val withdrawAmountSats = if (lnurl.data.isFixedAmount()) {
1891-
lnurl.data.fixedWithdrawAmountSat()
1890+
val invoice = if (lnurl.data.isFixedAmount()) {
1891+
lightningRepo.createInvoiceMsats(
1892+
amountMsats = lnurl.data.maxWithdrawable,
1893+
description = lnurl.data.defaultDescription,
1894+
expirySeconds = 3600u,
1895+
)
18921896
} else {
1893-
_sendUiState.value.amount.coerceAtLeast(
1897+
val withdrawAmountSats = _sendUiState.value.amount.coerceAtLeast(
18941898
(lnurl.data.minWithdrawable ?: 0u) / 1000u
18951899
)
1896-
}
1897-
1898-
_sendUiState.update {
1899-
it.copy(amount = withdrawAmountSats)
1900-
}
1901-
1902-
val invoice = lightningRepo.createInvoice(
1903-
amountSats = withdrawAmountSats,
1904-
description = lnurl.data.defaultDescription,
1905-
expirySeconds = 3600u,
1906-
).getOrNull()
1900+
_sendUiState.update { it.copy(amount = withdrawAmountSats) }
1901+
lightningRepo.createInvoice(
1902+
amountSats = withdrawAmountSats,
1903+
description = lnurl.data.defaultDescription,
1904+
expirySeconds = 3600u,
1905+
)
1906+
}.getOrNull()
19071907

19081908
if (invoice == null) {
19091909
setSendEffect(SendEffect.NavigateToWithdrawError)

0 commit comments

Comments
 (0)