@@ -150,6 +150,7 @@ class ActivityService {
150150 let preservedTransferTxId = existingOnchain? . transferTxId
151151 let preservedFeeRate = existingOnchain? . feeRate ?? 1
152152 let preservedAddress = existingOnchain? . address ?? " Loading... "
153+ let preservedDoesExist = existingOnchain? . doesExist ?? true
153154
154155 // Check if this transaction is a channel transfer (open or close)
155156 if preservedChannelId == nil || !preservedIsTransfer {
@@ -211,6 +212,9 @@ class ActivityService {
211212 let finalChannelId = preservedChannelId
212213 let finalTransferTxId = preservedTransferTxId
213214
215+ // If confirmed, set doesExist to true; otherwise preserve existing value
216+ let finalDoesExist = isConfirmed ? true : preservedDoesExist
217+
214218 let onchain = OnchainActivity (
215219 id: payment. id,
216220 txType: payment. direction == . outbound ? . sent : . received,
@@ -224,7 +228,7 @@ class ActivityService {
224228 isBoosted: shouldMarkAsBoosted, // Mark as boosted if it's a replacement transaction
225229 boostTxIds: boostTxIds,
226230 isTransfer: finalIsTransfer,
227- doesExist: true ,
231+ doesExist: finalDoesExist ,
228232 confirmTimestamp: confirmedTimestamp,
229233 channelId: finalChannelId,
230234 transferTxId: finalTransferTxId,
@@ -241,6 +245,11 @@ class ActivityService {
241245 print ( payment)
242246 addedCount += 1
243247 }
248+
249+ // If a removed transaction confirms, mark its replacement transactions as removed
250+ if !preservedDoesExist && isConfirmed {
251+ try await self . markReplacementTransactionsAsRemoved ( originalTxId: txid)
252+ }
244253 } else if case let . bolt11( hash, preimage, secret, description, bolt11) = payment. kind {
245254 // Skip pending inbound payments, just means they created an invoice
246255 guard !( payment. status == . pending && payment. direction == . inbound) else { continue }
@@ -298,6 +307,35 @@ class ActivityService {
298307 }
299308 }
300309
310+ /// Marks replacement transactions (with originalTxId in boostTxIds) as doesExist = false when original confirms
311+ private func markReplacementTransactionsAsRemoved( originalTxId: String ) async throws {
312+ let allActivities = try getActivities (
313+ filter: . onchain,
314+ txType: nil ,
315+ tags: nil ,
316+ search: nil ,
317+ minDate: nil ,
318+ maxDate: nil ,
319+ limit: nil ,
320+ sortDirection: nil
321+ )
322+
323+ for activity in allActivities {
324+ guard case let . onchain( onchainActivity) = activity else { continue }
325+
326+ if onchainActivity. boostTxIds. contains ( originalTxId) && onchainActivity. doesExist {
327+ Logger . info (
328+ " Marking replacement transaction \( onchainActivity. txId) as doesExist = false (original \( originalTxId) confirmed) " ,
329+ context: " CoreService.markReplacementTransactionsAsRemoved "
330+ )
331+
332+ var updatedActivity = onchainActivity
333+ updatedActivity. doesExist = false
334+ try updateActivity ( activityId: onchainActivity. id, activity: . onchain( updatedActivity) )
335+ }
336+ }
337+ }
338+
301339 /// Finds the channel ID associated with a transaction based on its direction
302340 private func findChannelForTransaction( txid: String , direction: PaymentDirection ) async -> String ? {
303341 switch direction {
@@ -693,25 +731,25 @@ class ActivityService {
693731 " Added original transaction \( onchainActivity. txId) to replaced transactions list " , context: " CoreService.boostOnchainTransaction "
694732 )
695733
696- // For RBF, delete the original activity since it's been replaced
697- // The new transaction will be synced automatically from LDK
734+ // For RBF, mark the old activity as boosted before marking it as replaced
735+ onchainActivity . isBoosted = true
698736 Logger . debug (
699- " Attempting to delete original activity \( activityId) before RBF replacement " , context: " CoreService.boostOnchainTransaction "
737+ " Marked original activity \( activityId) as boosted before RBF replacement " ,
738+ context: " CoreService.boostOnchainTransaction "
700739 )
701740
702- // Use the proper delete function that returns a Bool
703- let deleteResult = try deleteActivityById ( activityId: activityId)
704- Logger . info ( " Delete result for original activity \( activityId) : \( deleteResult) " , context: " CoreService.boostOnchainTransaction " )
705-
706- // Double-check that the activity was deleted
707- let checkActivity = try getActivityById ( activityId: activityId)
708- if checkActivity == nil {
709- Logger . info ( " Confirmed: Original activity \( activityId) was successfully deleted " , context: " CoreService.boostOnchainTransaction " )
710- } else {
711- Logger . error (
712- " Warning: Original activity \( activityId) still exists after deletion attempt " , context: " CoreService.boostOnchainTransaction "
713- )
714- }
741+ // For RBF, mark the original activity as doesExist = false instead of deleting it
742+ // This allows it to be displayed with the "removed" status
743+ Logger . debug (
744+ " Marking original activity \( activityId) as doesExist = false (replaced by RBF) " , context: " CoreService.boostOnchainTransaction "
745+ )
746+
747+ onchainActivity. doesExist = false
748+ try updateActivity ( activityId: activityId, activity: . onchain( onchainActivity) )
749+ Logger . info (
750+ " Successfully marked activity \( activityId) as doesExist = false (replaced by RBF) " ,
751+ context: " CoreService.boostOnchainTransaction "
752+ )
715753
716754 self . activitiesChangedSubject. send ( )
717755 }
0 commit comments