@@ -136,6 +136,21 @@ class WalletViewModel: ObservableObject {
136136 MigrationsService . shared. pendingChannelMigration = nil
137137 }
138138
139+ // If no local migration data, try fetching from RN remote backup (one-time)
140+ if channelMigration == nil {
141+ let ( remoteMigration, allRetrieved) = await fetchOrphanedChannelMonitorsIfNeeded ( walletIndex: walletIndex)
142+ if let remoteMigration {
143+ channelMigration = ChannelDataMigration (
144+ channelManager: [ UInt8] ( remoteMigration. channelManager) ,
145+ channelMonitors: remoteMigration. channelMonitors. map { [ UInt8] ( $0) }
146+ )
147+ MigrationsService . shared. pendingChannelMigration = nil
148+ }
149+ if allRetrieved {
150+ MigrationsService . shared. isChannelRecoveryChecked = true
151+ }
152+ }
153+
139154 await runLegacyNetworkGraphCleanupIfNeeded ( )
140155
141156 try await lightningService. setup (
@@ -236,6 +251,43 @@ class WalletViewModel: ObservableObject {
236251 }
237252 }
238253
254+ /// Fetches orphaned channel monitors from RN remote backup before LDK setup.
255+ /// Returns (migration data if found, whether all monitors were successfully retrieved).
256+ private func fetchOrphanedChannelMonitorsIfNeeded( walletIndex: Int ) async -> ( PendingChannelMigration ? , Bool ) {
257+ let migrations = MigrationsService . shared
258+ guard !migrations. isChannelRecoveryChecked else { return ( nil , true ) }
259+
260+ Logger . info ( " Running pre-startup channel monitor recovery check " , context: " WalletViewModel " )
261+
262+ do {
263+ guard let mnemonic = try Keychain . loadString ( key: . bip39Mnemonic( index: walletIndex) ) else {
264+ Logger . debug ( " Channel recovery: no mnemonic, skipping " , context: " WalletViewModel " )
265+ migrations. isChannelRecoveryChecked = true
266+ return ( nil , true )
267+ }
268+ let passphrase = try ? Keychain . loadString ( key: . bip39Passphrase( index: walletIndex) )
269+
270+ RNBackupClient . shared. reset ( )
271+ try await RNBackupClient . shared. setup ( mnemonic: mnemonic, passphrase: passphrase)
272+
273+ let allRetrieved = await migrations. fetchRNRemoteLdkData ( )
274+
275+ if let migration = migrations. pendingChannelMigration {
276+ Logger . info (
277+ " Found \( migration. channelMonitors. count) monitors on RN backup for pre-startup recovery " ,
278+ context: " WalletViewModel "
279+ )
280+ return ( migration, allRetrieved)
281+ } else {
282+ Logger . info ( " No channel monitors found on RN backup " , context: " WalletViewModel " )
283+ return ( nil , allRetrieved)
284+ }
285+ } catch {
286+ Logger . error ( " Pre-startup channel monitor fetch failed: \( error) " , context: " WalletViewModel " )
287+ return ( nil , false )
288+ }
289+ }
290+
239291 private func fetchTrustedPeersFromBlocktank( ) async -> [ LnPeer ] ? {
240292 switch Self . peerSimulation {
241293 case . apiFailure:
0 commit comments