Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ private fun Content(
) {
val clientBalance = state.clientBalanceSat
val lspFee = state.feeSat.safe() - clientBalance.safe()
val total = state.feeSat.safe() + miningFeeSats.safe()
val total = state.confirmLeavingAmountSats
val lspBalance = state.lspBalanceSat

VerticalSpacer(32.dp)
Expand Down
20 changes: 16 additions & 4 deletions app/src/main/java/to/bitkit/viewmodels/TransferViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,7 @@ class TransferViewModel @Inject constructor(
fun prepareSpendingConfirmFunding() {
confirmFeeJob?.cancel()
confirmFeeJob = viewModelScope.launch {
_spendingUiState.update {
it.copy(isConfirmFeeReady = false, miningFeeSats = 0uL)
}
_spendingUiState.update { it.copy(isConfirmFeeReady = false) }
val order = _spendingUiState.value.order
val target = SpendingFundingTarget(
feeSat = _spendingUiState.value.feeSat,
Expand All @@ -294,13 +292,19 @@ class TransferViewModel @Inject constructor(
isConfirmFeeReady = true,
miningFeeSats = plan.miningFeeSats,
shouldUseSendAll = plan.shouldUseSendAll,
spendableBalance = plan.spendableBalance,
Comment thread
ovitrif marked this conversation as resolved.
)
}
}
.onFailure {
spendingConfirmFundingPlan = null
_spendingUiState.update {
it.copy(isConfirmFeeReady = false, miningFeeSats = 0uL, shouldUseSendAll = false)
it.copy(
isConfirmFeeReady = false,
miningFeeSats = 0uL,
shouldUseSendAll = false,
spendableBalance = 0uL,
)
}
Logger.error("Failed to prepare transfer funding fee", it, context = TAG)
if (it is AppError) {
Expand Down Expand Up @@ -1915,12 +1919,20 @@ data class TransferToSpendingUiState(
val isConfirmFeeReady: Boolean = false,
val isConfirmPaying: Boolean = false,
val shouldUseSendAll: Boolean = false,
val spendableBalance: ULong = 0uL,
val receivingAmount: Long = 0,
val feeEstimate: Long? = null,
val fundingBudgetSats: ULong? = null,
val hwFundingWalletId: String? = null,
) {
val isBusy: Boolean get() = isConfirmPaying || isSigning

val confirmLeavingAmountSats: ULong
get() = if (shouldUseSendAll) {
spendableBalance
} else {
feeSat.safe() + miningFeeSats.safe()
}
}

private data class SpendingFundingTarget(
Expand Down
53 changes: 53 additions & 0 deletions app/src/test/java/to/bitkit/viewmodels/TransferViewModelTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.synonym.bitkitcore.TrezorFeatures
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.TimeoutCancellationException
Expand Down Expand Up @@ -1023,10 +1024,62 @@ class TransferViewModelTest : BaseUnitTest() {
assertEquals(true, state.isConfirmFeeReady)
assertEquals(1_000uL, state.miningFeeSats)
assertEquals(false, state.shouldUseSendAll)
assertEquals(100_000uL, state.spendableBalance)
assertEquals(99_000uL, state.confirmLeavingAmountSats)
verify(lightningRepo).calculateTotalFee(eq(98_000uL), eq(WALLET_ADDRESS), any(), anyOrNull(), anyOrNull())
verify(blocktankRepo, never()).createOrder(any(), any(), any())
}

@Test
fun `prepareSpendingConfirmFunding shows spendable balance as total for send-all`() = test {
quoteOrder(spendingOrder(feeSat = 99_000uL))
val selected = listOf(stubUtxo(100_000u))
stubSpendableBalances(spendable = 100_000u)
whenever(lightningRepo.estimateSendAllFee(any(), any(), anyOrNull())).thenReturn(Result.success(500uL))
whenever {
lightningRepo.selectUtxosWithAlgorithm(any(), any(), any(), anyOrNull())
}.thenReturn(Result.success(selected))
whenever(lightningRepo.calculateTotalFee(any(), any(), any(), anyOrNull(), anyOrNull()))
.thenReturn(Result.success(500uL))

sut.prepareSpendingConfirmFunding()
advanceUntilIdle()

val state = sut.spendingUiState.value
assertEquals(true, state.isConfirmFeeReady)
assertEquals(500uL, state.miningFeeSats)
assertEquals(true, state.shouldUseSendAll)
assertEquals(100_000uL, state.spendableBalance)
assertEquals(100_000uL, state.confirmLeavingAmountSats)
verify(blocktankRepo, never()).createOrder(any(), any(), any())
}

@Test
fun `repreparing send-all funding keeps the leaving amount until the next plan`() = test {
quoteOrder(spendingOrder(feeSat = 99_000uL))
val selected = listOf(stubUtxo(100_000u))
stubSpendableBalances(spendable = 100_000u)
whenever(lightningRepo.estimateSendAllFee(any(), any(), anyOrNull())).thenReturn(Result.success(500uL))
whenever {
lightningRepo.selectUtxosWithAlgorithm(any(), any(), any(), anyOrNull())
}.thenReturn(Result.success(selected))
whenever(lightningRepo.calculateTotalFee(any(), any(), any(), anyOrNull(), anyOrNull()))
.thenReturn(Result.success(500uL))

sut.prepareSpendingConfirmFunding()
advanceUntilIdle()
assertEquals(100_000uL, sut.spendingUiState.value.confirmLeavingAmountSats)

whenever(lightningRepo.getBalancesAsync()).doSuspendableAnswer { suspendCancellableCoroutine { } }
sut.prepareSpendingConfirmFunding()
runCurrent()

val refreshing = sut.spendingUiState.value
assertEquals(false, refreshing.isConfirmFeeReady)
assertEquals(true, refreshing.shouldUseSendAll)
assertEquals(100_000uL, refreshing.confirmLeavingAmountSats)
}

@Test
fun `onTransferToSpendingConfirm uses send-all when selected inputs would create dust change`() = test {
val order = spendingOrder(feeSat = 99_000uL)
Expand Down
1 change: 1 addition & 0 deletions changelog.d/next/1326.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Spending confirm now shows the amount of bitcoin that leaves the wallet when a savings transfer uses send-all.
Loading