@@ -26,6 +26,7 @@ import org.lightningdevkit.ldknode.ChannelDetails
2626import org.lightningdevkit.ldknode.PaymentDetails
2727import org.lightningdevkit.ldknode.PaymentDirection
2828import org.lightningdevkit.ldknode.PaymentKind
29+ import org.lightningdevkit.ldknode.TransactionDetails
2930import to.bitkit.data.CacheStore
3031import to.bitkit.data.dto.PendingBoostActivity
3132import to.bitkit.di.BgDispatcher
@@ -36,7 +37,6 @@ import to.bitkit.ext.nowTimestamp
3637import to.bitkit.ext.rawId
3738import to.bitkit.models.ActivityBackupV1
3839import to.bitkit.services.CoreService
39- import to.bitkit.utils.AddressChecker
4040import to.bitkit.utils.Logger
4141import javax.inject.Inject
4242import javax.inject.Singleton
@@ -50,7 +50,6 @@ class ActivityRepo @Inject constructor(
5050 private val coreService : CoreService ,
5151 private val lightningRepo : LightningRepo ,
5252 private val blocktankRepo : BlocktankRepo ,
53- private val addressChecker : AddressChecker ,
5453 private val cacheStore : CacheStore ,
5554 private val transferRepo : TransferRepo ,
5655 private val clock : Clock ,
@@ -83,8 +82,6 @@ class ActivityRepo @Inject constructor(
8382
8483 isSyncingLdkNodePayments.update { true }
8584
86- deletePendingActivities()
87-
8885 lightningRepo.getPayments().mapCatching { payments ->
8986 Logger .debug(" Got payments with success, syncing activities" , context = TAG )
9087 syncLdkNodePayments(payments).getOrThrow()
@@ -167,67 +164,13 @@ class ActivityRepo @Inject constructor(
167164 }
168165
169166 private suspend fun findClosedChannelForTransaction (txid : String ): String? {
170- return try {
171- val closedChannelsResult = getClosedChannels(SortDirection .DESC )
172- val closedChannels = closedChannelsResult.getOrNull() ? : return null
173- if (closedChannels.isEmpty()) return null
174-
175- val txDetails = addressChecker.getTransaction(txid)
176-
177- txDetails.vin.firstNotNullOfOrNull { input ->
178- val inputTxid = input.txid ? : return @firstNotNullOfOrNull null
179- val inputVout = input.vout ? : return @firstNotNullOfOrNull null
180-
181- closedChannels.firstOrNull { channel ->
182- channel.fundingTxoTxid == inputTxid && channel.fundingTxoIndex == inputVout.toUInt()
183- }?.channelId
184- }
185- } catch (e: Exception ) {
186- Logger .warn(
187- " Failed to check if transaction $txid spends closed channel funding UTXO" ,
188- e,
189- context = TAG
190- )
191- null
192- }
167+ return coreService.activity.findClosedChannelForTransaction(txid, null )
193168 }
194169
195- private suspend fun getOnchainActivityByTxId (txid : String ): OnchainActivity ? {
170+ suspend fun getOnchainActivityByTxId (txid : String ): OnchainActivity ? {
196171 return coreService.activity.getOnchainActivityByTxId(txid)
197172 }
198173
199- /* *
200- * Determines whether to show the payment received UI for an onchain transaction.
201- * Returns false for:
202- * - Zero value transactions
203- * - Channel closure transactions (transfers to savings)
204- * - RBF replacement transactions with the same value as the original
205- */
206- suspend fun shouldShowPaymentReceived (txid : String , value : ULong ): Boolean = withContext(bgDispatcher) {
207- if (value == 0uL ) return @withContext false
208-
209- if (findClosedChannelForTransaction(txid) != null ) {
210- Logger .debug(" Skipping payment received UI for channel closure tx: $txid " , context = TAG )
211- return @withContext false
212- }
213-
214- val onchainActivity = getOnchainActivityByTxId(txid)
215- if (onchainActivity != null && onchainActivity.boostTxIds.isNotEmpty()) {
216- for (replacedTxid in onchainActivity.boostTxIds) {
217- val replacedActivity = getOnchainActivityByTxId(replacedTxid)
218- if (replacedActivity != null && replacedActivity.value == value) {
219- Logger .info(
220- " Skipping payment received UI for RBF replacement $txid with same value as $replacedTxid " ,
221- context = TAG
222- )
223- return @withContext false
224- }
225- }
226- }
227-
228- return @withContext true
229- }
230-
231174 /* *
232175 * Checks if a transaction is inbound (received) by looking up the payment direction.
233176 */
@@ -247,6 +190,58 @@ class ActivityRepo @Inject constructor(
247190 return @withContext ! onchainActivity.doesExist
248191 }
249192
193+ suspend fun handleOnchainTransactionReceived (
194+ txid : String ,
195+ details : TransactionDetails ,
196+ ) {
197+ coreService.activity.handleOnchainTransactionReceived(txid, details)
198+ notifyActivitiesChanged()
199+ }
200+
201+ suspend fun handleOnchainTransactionConfirmed (
202+ txid : String ,
203+ details : TransactionDetails ,
204+ ) {
205+ coreService.activity.handleOnchainTransactionConfirmed(txid, details)
206+ notifyActivitiesChanged()
207+ }
208+
209+ suspend fun handleOnchainTransactionReplaced (txid : String , conflicts : List <String >) {
210+ coreService.activity.handleOnchainTransactionReplaced(txid, conflicts)
211+ notifyActivitiesChanged()
212+ }
213+
214+ suspend fun handleOnchainTransactionReorged (txid : String ) {
215+ coreService.activity.handleOnchainTransactionReorged(txid)
216+ notifyActivitiesChanged()
217+ }
218+
219+ suspend fun handleOnchainTransactionEvicted (txid : String ) {
220+ coreService.activity.handleOnchainTransactionEvicted(txid)
221+ notifyActivitiesChanged()
222+ }
223+
224+ suspend fun handlePaymentEvent (paymentHash : String ) {
225+ coreService.activity.handlePaymentEvent(paymentHash)
226+ notifyActivitiesChanged()
227+ }
228+
229+ suspend fun shouldShowReceivedSheet (txid : String , value : ULong ): Boolean {
230+ return coreService.activity.shouldShowReceivedSheet(txid, value)
231+ }
232+
233+ suspend fun getBoostTxDoesExist (boostTxIds : List <String >): Map <String , Boolean > {
234+ return coreService.activity.getBoostTxDoesExist(boostTxIds)
235+ }
236+
237+ suspend fun isCpfpChildTransaction (txId : String ): Boolean {
238+ return coreService.activity.isCpfpChildTransaction(txId)
239+ }
240+
241+ suspend fun getTxIdsInBoostTxIds (): Set <String > {
242+ return coreService.activity.getTxIdsInBoostTxIds()
243+ }
244+
250245 /* *
251246 * Gets a specific activity by payment hash or txID with retry logic
252247 */
@@ -392,22 +387,13 @@ class ActivityRepo @Inject constructor(
392387 ).fold(
393388 onSuccess = {
394389 Logger .debug(
395- " Activity $id updated with success. new data: $activity . " +
396- " Marking activity $activityIdToDelete as removed from mempool" ,
390+ " Activity $id updated with success. new data: $activity " ,
397391 context = TAG
398392 )
399393
400394 val tags = coreService.activity.tags(activityIdToDelete)
401395 addTagsToActivity(activityId = id, tags = tags)
402396
403- markActivityAsRemovedFromMempool(activityIdToDelete).onFailure { e ->
404- Logger .warn(
405- " Failed to mark $activityIdToDelete as removed from mempool, caching to retry on next sync" ,
406- e = e,
407- context = TAG
408- )
409- cacheStore.addActivityToPendingDelete(activityId = activityIdToDelete)
410- }
411397 Result .success(Unit )
412398 },
413399 onFailure = { e ->
@@ -422,16 +408,6 @@ class ActivityRepo @Inject constructor(
422408 )
423409 }
424410
425- private suspend fun deletePendingActivities () = withContext(bgDispatcher) {
426- cacheStore.data.first().activitiesPendingDelete.map { activityId ->
427- async {
428- markActivityAsRemovedFromMempool(activityId).onSuccess {
429- cacheStore.removeActivityFromPendingDelete(activityId)
430- }
431- }
432- }.awaitAll()
433- }
434-
435411 private suspend fun boostPendingActivities () = withContext(bgDispatcher) {
436412 cacheStore.data.first().pendingBoostActivities.map { pendingBoostActivity ->
437413 async {
@@ -484,32 +460,6 @@ class ActivityRepo @Inject constructor(
484460 }.awaitAll()
485461 }
486462
487- /* *
488- * Marks an activity as removed from mempool (sets doesExist = false).
489- * Used for RBFed transactions that are replaced.
490- */
491- private suspend fun markActivityAsRemovedFromMempool (activityId : String ): Result <Unit > = withContext(bgDispatcher) {
492- return @withContext runCatching {
493- val existingActivity = getActivity(activityId).getOrNull()
494- ? : return @withContext Result .failure(Exception (" Activity $activityId not found" ))
495-
496- if (existingActivity is Activity .Onchain ) {
497- val updatedActivity = Activity .Onchain (
498- v1 = existingActivity.v1.copy(
499- doesExist = false ,
500- updatedAt = nowTimestamp().toEpochMilli().toULong()
501- )
502- )
503- updateActivity(id = activityId, activity = updatedActivity, forceUpdate = true ).getOrThrow()
504- notifyActivitiesChanged()
505- } else {
506- return @withContext Result .failure(Exception (" Activity $activityId is not an onchain activity" ))
507- }
508- }.onFailure { e ->
509- Logger .error(" markActivityAsRemovedFromMempool error for ID: $activityId " , e, context = TAG )
510- }
511- }
512-
513463 /* *
514464 * Deletes an activity
515465 */
0 commit comments