From 0a5aa755ceb3b438532194ad73a52f8c6369bc9e Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Mon, 19 Jan 2026 19:06:31 -0300 Subject: [PATCH 1/2] fix: check for viable lightning invoice when setting the BIP21 on text input --- .../java/to/bitkit/viewmodels/AppViewModel.kt | 58 ++++++++++++------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt index 019b447e3..1abe2e91e 100644 --- a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt @@ -780,6 +780,18 @@ class AppViewModel @Inject constructor( return } + extractViableLightningInvoice(invoice.params)?.let { lnInvoice -> + _sendUiState.update { + it.copy( + isAddressInputValid = true, + isUnified = true, + decodedInvoice = lnInvoice, + payMethod = SendMethod.LIGHTNING, + ) + } + return + } + val maxSendOnchain = walletRepo.balanceState.value.maxSendOnchainSats if (maxSendOnchain == 0uL) { @@ -805,6 +817,30 @@ class AppViewModel @Inject constructor( _sendUiState.update { it.copy(isAddressInputValid = true) } } + private suspend fun extractViableLightningInvoice(params: Map?): LightningInvoice? = + params?.get("lightning")?.let { bolt11 -> + runCatching { decode(bolt11) }.getOrNull() + ?.let { it as? Scanner.Lightning } + ?.invoice + ?.takeIf { lnInv -> + if (lnInv.isExpired) { + Logger.debug( + "Lightning invoice expired in unified URI, defaulting to onchain-only", + context = TAG + ) + return@takeIf false + } + val canSend = lightningRepo.canSend(lnInv.amountSatoshis.coerceAtLeast(1u)) + if (!canSend) { + Logger.debug( + "Cannot pay unified invoice using LN, defaulting to onchain-only", + context = TAG + ) + } + return@takeIf canSend + } + } + private fun showAddressValidationError( @StringRes titleRes: Int, @StringRes descriptionRes: Int, @@ -1056,27 +1092,7 @@ class AppViewModel @Inject constructor( return } - val lnInvoice: LightningInvoice? = invoice.params?.get("lightning")?.let { bolt11 -> - runCatching { decode(bolt11) }.getOrNull() - ?.let { it as? Scanner.Lightning } - ?.invoice - ?.takeIf { invoice -> - if (invoice.isExpired) { - Logger.debug( - "Lightning invoice expired in unified URI, defaulting to onchain-only", - context = TAG - ) - return@takeIf false - } - - // Then check sending capacity - val canSend = lightningRepo.canSend(invoice.amountSatoshis.coerceAtLeast(1u)) - if (!canSend) { - Logger.debug("Cannot pay unified invoice using LN, defaulting to onchain-only", context = TAG) - } - return@takeIf canSend - } - } + val lnInvoice = extractViableLightningInvoice(invoice.params) _sendUiState.update { it.copy( address = invoice.address, From 82afb0dcca4d52e4342e0884f70095b35c872f3d Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Mon, 19 Jan 2026 19:32:06 -0300 Subject: [PATCH 2/2] fix: only trigger checks for on-chain if the payment method is on-chain --- .../main/java/to/bitkit/viewmodels/AppViewModel.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt index 1abe2e91e..025d8b97a 100644 --- a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt @@ -1091,6 +1091,7 @@ class AppViewModel @Inject constructor( ) return } + val maxSendOnchain = walletRepo.balanceState.value.maxSendOnchainSats val lnInvoice = extractViableLightningInvoice(invoice.params) _sendUiState.update { @@ -1099,7 +1100,7 @@ class AppViewModel @Inject constructor( addressInput = scanResult, isAddressInputValid = true, amount = invoice.amountSatoshis, - isUnified = lnInvoice != null, + isUnified = lnInvoice != null && invoice.amountSatoshis <= maxSendOnchain && maxSendOnchain > 0u, decodedInvoice = lnInvoice, payMethod = lnInvoice?.let { SendMethod.LIGHTNING } ?: SendMethod.ONCHAIN, ) @@ -1125,8 +1126,7 @@ class AppViewModel @Inject constructor( } // Check on-chain balance before proceeding to amount screen - val maxSendOnchain = walletRepo.balanceState.value.maxSendOnchainSats - if (maxSendOnchain == 0uL) { + if (maxSendOnchain == 0uL && _sendUiState.value.payMethod == SendMethod.ONCHAIN) { toast( type = Toast.ToastType.ERROR, title = context.getString(R.string.other__pay_insufficient_savings), @@ -1137,7 +1137,11 @@ class AppViewModel @Inject constructor( } // Check if on-chain invoice amount exceeds available balance - if (invoice.amountSatoshis > 0uL && invoice.amountSatoshis > maxSendOnchain) { + if ( + invoice.amountSatoshis > 0uL && + invoice.amountSatoshis > maxSendOnchain && + _sendUiState.value.payMethod == SendMethod.ONCHAIN + ) { val shortfall = invoice.amountSatoshis - maxSendOnchain toast( type = Toast.ToastType.ERROR,