Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
440f2b1
feat: add PendingSweepBalance extension functions
jvsena42 Feb 25, 2026
14db4c9
fix: improve syncTransferStates for batched force-close sweeps
jvsena42 Feb 25, 2026
28f1993
fix: add ConnectionClosed sheet variant and UI
jvsena42 Feb 25, 2026
03c9cb5
fix: update handleChannelClosed in AppViewModel
jvsena42 Feb 25, 2026
71aeef6
test: add force-close sync tests
jvsena42 Feb 25, 2026
a179821
chore: lint
jvsena42 Feb 25, 2026
33a77ec
chore: lint
jvsena42 Feb 26, 2026
356a1d7
fix: mark closing transaction as transfer activity
jvsena42 Feb 26, 2026
0fff4aa
Merge branch 'fix/reimport-channel-monitor' into fix/channel-close-ui
jvsena42 Feb 26, 2026
7e284ef
fix: sheet size medium
jvsena42 Feb 26, 2026
4030583
Merge remote-tracking branch 'origin/fix/channel-close-ui' into fix/c…
jvsena42 Feb 27, 2026
9c4f80d
fix: settle COOP_CLOSE immediately
jvsena42 Feb 27, 2026
298bdf5
fix: balance calculation includes COOP_CLOSE whole they are unsettled…
jvsena42 Feb 27, 2026
d3af0c3
test: update coop close tests to settle immediately
jvsena42 Feb 27, 2026
793e0b9
feat: add claimableAtHeight to TransferEntity
jvsena42 Feb 27, 2026
f4849a4
feat: pass claimableAtHeight when creating force close transfer
jvsena42 Feb 27, 2026
8777244
chore: create helpers to display the duration
jvsena42 Feb 27, 2026
0aa2049
feat: update banner for dynamic title
jvsena42 Feb 27, 2026
6af900c
fix: show dynamic duration for force-close
jvsena42 Feb 27, 2026
ae0f027
fix: suppress receive sheet for transfers
jvsena42 Feb 27, 2026
78efaf5
fix: claimableAtHeight type
jvsena42 Feb 27, 2026
5480492
chore: migration
jvsena42 Feb 27, 2026
37235c3
fix: claimableAtHeight type
jvsena42 Feb 27, 2026
dafdea3
test: BlockTimer helper tests
jvsena42 Feb 27, 2026
aad07e4
chore: lint
jvsena42 Feb 27, 2026
c1715b4
refactor: move force close duration calculation to TransferRepo.kt
jvsena42 Feb 27, 2026
94c86a3
fix: subtract coop closing amount from balanceInTransferToSavings
jvsena42 Feb 27, 2026
8302993
fix: transfer banner visibility
jvsena42 Feb 27, 2026
ed56df9
fix: check if this transaction is a pending sweep from a channel closure
jvsena42 Feb 27, 2026
e56d56a
Merge branch 'fix/reimport-channel-monitor' into fix/channel-close-ui
jvsena42 Feb 27, 2026
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
Prev Previous commit
Next Next commit
fix: subtract coop closing amount from balanceInTransferToSavings
  • Loading branch information
jvsena42 committed Feb 27, 2026
commit 94c86a332dc4dd36175ab0f0d4c8f0031fb16da0
6 changes: 0 additions & 6 deletions app/src/main/java/to/bitkit/repositories/TransferRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,6 @@ class TransferRepo @Inject constructor(
val toSavings = activeTransfers.filter { it.type.isToSavings() }

for (transfer in toSavings) {
if (transfer.type == TransferType.COOP_CLOSE) {
markSettled(transfer.id)
Logger.debug("Coop close settled immediately: ${transfer.id}", context = TAG)
continue
}

val channelId = resolveChannelIdForTransfer(transfer, channels)
val hasBalance = balances?.lightningBalances?.any {
it.channelId() == channelId
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/to/bitkit/repositories/WalletRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class WalletRepo @Inject constructor(
private val deriveBalanceStateUseCase: DeriveBalanceStateUseCase,
private val wipeWalletUseCase: WipeWalletUseCase,
private val transferRepo: TransferRepo,
private val activityRepo: ActivityRepo,
) {
private val repoScope = CoroutineScope(bgDispatcher + SupervisorJob())

Expand Down Expand Up @@ -204,6 +205,7 @@ class WalletRepo @Inject constructor(
delay(EVENT_SYNC_DEBOUNCE_MS)
syncBalances()
transferRepo.syncTransferStates()
activityRepo.syncActivities()
}
}

Expand Down
19 changes: 17 additions & 2 deletions app/src/main/java/to/bitkit/usecases/DeriveBalanceStateUseCase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class DeriveBalanceStateUseCase @Inject constructor(
val pendingChannelsSats = getPendingChannelsSats(activeTransfers, channels, balanceDetails)

val toSavingsAmount = getTransferToSavingsSats(activeTransfers, channels, balanceDetails)
val coopCloseSavingsSats = getCoopCloseTransferSats(activeTransfers, channels, balanceDetails)
val toSpendingAmount = paidOrdersSats.safe() + pendingChannelsSats.safe()

val totalOnchainSats = balanceDetails.totalOnchainBalanceSats
Expand All @@ -44,7 +45,7 @@ class DeriveBalanceStateUseCase @Inject constructor(
totalLightningSats = totalLightningSats,
maxSendLightningSats = lightningRepo.getChannels().totalNextOutboundHtlcLimitSats(),
maxSendOnchainSats = getMaxSendAmount(balanceDetails),
balanceInTransferToSavings = toSavingsAmount,
balanceInTransferToSavings = toSavingsAmount.safe() - coopCloseSavingsSats.safe(),
balanceInTransferToSpending = toSpendingAmount,
)

Expand Down Expand Up @@ -87,7 +88,7 @@ class DeriveBalanceStateUseCase @Inject constructor(
balanceDetails: BalanceDetails,
): ULong {
var toSavingsAmount = 0uL
val toSavings = transfers.filter { it.type.isToSavings() && it.type != TransferType.COOP_CLOSE }
val toSavings = transfers.filter { it.type.isToSavings() }

for (transfer in toSavings) {
val channelId = transferRepo.resolveChannelIdForTransfer(transfer, channels)
Expand All @@ -98,6 +99,20 @@ class DeriveBalanceStateUseCase @Inject constructor(
return toSavingsAmount
}

private suspend fun getCoopCloseTransferSats(
transfers: List<TransferEntity>,
channels: List<ChannelDetails>,
balanceDetails: BalanceDetails,
): ULong {
var amount = 0uL
for (transfer in transfers.filter { it.type == TransferType.COOP_CLOSE }) {
val channelId = transferRepo.resolveChannelIdForTransfer(transfer, channels)
val channelBalance = balanceDetails.lightningBalances.find { it.channelId() == channelId }
amount += channelBalance?.amountSats() ?: 0u
}
return amount
}

private suspend fun getMaxSendAmount(balanceDetails: BalanceDetails): ULong {
val spendableOnchainSats = balanceDetails.spendableOnchainBalanceSats
if (spendableOnchainSats == 0uL) return 0u
Expand Down
34 changes: 32 additions & 2 deletions app/src/test/java/to/bitkit/repositories/TransferRepoTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,7 @@ class TransferRepoTest : BaseUnitTest() {
}

@Test
fun `syncTransferStates settles COOP_CLOSE immediately without waiting for balance sweep`() = test {
val settledAt = setupClockNowMock()
fun `syncTransferStates does not settle COOP_CLOSE while LDK balance exists`() = test {
val transfer = TransferEntity(
id = ID_TRANSFER,
type = TransferType.COOP_CLOSE,
Expand All @@ -489,6 +488,37 @@ class TransferRepoTest : BaseUnitTest() {
pendingBalancesFromChannelClosures = emptyList(),
)

whenever(transferDao.getActiveTransfers()).thenReturn(flowOf(listOf(transfer)))
whenever(lightningRepo.getChannels()).thenReturn(emptyList())
whenever(lightningRepo.getBalancesAsync()).thenReturn(Result.success(balances))

val result = sut.syncTransferStates()

assertTrue(result.isSuccess)
verify(transferDao, never()).markSettled(any(), any())
}

@Test
fun `syncTransferStates settles COOP_CLOSE when LDK balance is gone`() = test {
val settledAt = setupClockNowMock()
val transfer = TransferEntity(
id = ID_TRANSFER,
type = TransferType.COOP_CLOSE,
amountSats = 75000L,
channelId = ID_CHANNEL,
isSettled = false,
createdAt = 1000L,
)

val balances = BalanceDetails(
totalOnchainBalanceSats = 75000u,
spendableOnchainBalanceSats = 75000u,
totalAnchorChannelsReserveSats = 0u,
totalLightningBalanceSats = 0u,
lightningBalances = emptyList(),
pendingBalancesFromChannelClosures = emptyList(),
)

whenever(transferDao.getActiveTransfers()).thenReturn(flowOf(listOf(transfer)))
whenever(lightningRepo.getChannels()).thenReturn(emptyList())
whenever(lightningRepo.getBalancesAsync()).thenReturn(Result.success(balances))
Expand Down
2 changes: 2 additions & 0 deletions app/src/test/java/to/bitkit/repositories/WalletRepoTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class WalletRepoTest : BaseUnitTest() {
private val deriveBalanceStateUseCase = mock<DeriveBalanceStateUseCase>()
private val wipeWalletUseCase = mock<WipeWalletUseCase>()
private val transferRepo = mock<TransferRepo>()
private val activityRepo = mock<ActivityRepo>()

companion object Fixtures {
const val ACTIVITY_TAG = "testTag"
Expand Down Expand Up @@ -109,6 +110,7 @@ class WalletRepoTest : BaseUnitTest() {
deriveBalanceStateUseCase = deriveBalanceStateUseCase,
wipeWalletUseCase = wipeWalletUseCase,
transferRepo = transferRepo,
activityRepo = activityRepo,
)

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ class DeriveBalanceStateUseCaseTest : BaseUnitTest() {
}

@Test
fun `should not count coop close channel balance for transfer to savings`() = test {
fun `should subtract coop close balance from lightning without showing transfer in progress`() = test {
val channelId = "closing-channel-id"
val amountSats = 40_000uL
val closingChannelBalance = newClosingChannelBalance(channelId, amountSats)
Expand All @@ -215,16 +215,17 @@ class DeriveBalanceStateUseCaseTest : BaseUnitTest() {

whenever(lightningRepo.getChannels()).thenReturn(emptyList())
whenever(transferRepo.activeTransfers).thenReturn(flowOf(transfers))
wheneverBlocking { transferRepo.resolveChannelIdForTransfer(any(), any()) }.thenReturn(channelId)

val result = sut()

assertTrue(result.isSuccess)
val balanceState = result.getOrThrow()
assertEquals(0uL, balanceState.balanceInTransferToSavings)
assertEquals(0uL, balanceState.balanceInTransferToSavings, "No transfer in progress for coop close")
assertEquals(
amountSats,
0uL,
balanceState.totalLightningSats,
"Lightning balance not reduced - coop close funds are immediately spendable"
"Lightning balance reduced - coop close balance subtracted"
)
}

Expand Down