Skip to content
Merged

fixes #236

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
15 changes: 15 additions & 0 deletions LDKNodeMonday/App/WalletClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class WalletClient {
await MainActor.run {
self.network = lightningClient.getNetwork()
self.server = lightningClient.getServer()
self.lsp = lightningClient.getLsp()

// Load LSP from keychain if available
if let savedLSPNodeId = try? self.keyClient.getLSP(),
Expand All @@ -73,6 +74,20 @@ public class WalletClient {
self.appState = .wallet
}
} catch let error {
if let nodeError = error as? NodeError,
case .FeerateEstimationUpdateTimeout = nodeError
{
let warning = handleNodeError(nodeError)
await MainActor.run {
self.appError = NSError(
domain: warning.title,
code: nodeError.hashValue,
userInfo: [NSLocalizedDescriptionKey: warning.detail]
)
self.appState = .error
}
return
}
await MainActor.run {
self.appError = error
self.appState = .error
Expand Down
13 changes: 10 additions & 3 deletions LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ class LightningNodeService {
fatalError("Configuration error: No Esplora servers available for \(network)")
}
self.server = server
let resolvedLspNodeId =
backupInfo.lspNodeId ?? LightningServiceProvider.see_signet.nodeId
self.lsp =
LightningServiceProvider.getByNodeId(
backupInfo.lspNodeId ?? LightningServiceProvider.see_signet.nodeId
) ?? .see_signet
LightningServiceProvider.getByNodeId(resolvedLspNodeId) ?? .see_signet
} else {
self.network = .signet
self.server = .mutiny_signet
Expand Down Expand Up @@ -427,6 +427,10 @@ class LightningNodeService {
return payments
}

func currentLsp() -> LightningServiceProvider {
return lsp
}

func status() -> NodeStatus {
let status = ldkNode.status()
return status
Expand Down Expand Up @@ -516,6 +520,7 @@ public struct LightningNodeClient {
let listPeers: () -> [PeerDetails]
let listChannels: () -> [ChannelDetails]
let listPayments: () -> [PaymentDetails]
let getLsp: () -> LightningServiceProvider
let status: () -> NodeStatus
let deleteWallet: () throws -> Void
let getBackupInfo: () throws -> BackupInfo
Expand Down Expand Up @@ -597,6 +602,7 @@ extension LightningNodeClient {
listPeers: { LightningNodeService.shared.listPeers() },
listChannels: { LightningNodeService.shared.listChannels() },
listPayments: { LightningNodeService.shared.listPayments() },
getLsp: { LightningNodeService.shared.currentLsp() },
status: { LightningNodeService.shared.status() },
deleteWallet: { try LightningNodeService.shared.deleteWallet() },
getBackupInfo: { try LightningNodeService.shared.getBackupInfo() },
Expand Down Expand Up @@ -643,6 +649,7 @@ extension LightningNodeClient {
listPeers: { [] },
listChannels: { [] },
listPayments: { mockPayments },
getLsp: { .see_signet },
status: {
NodeStatus(
isRunning: true,
Expand Down
57 changes: 41 additions & 16 deletions LDKNodeMonday/View Model/Home/Receive/ReceiveViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class ReceiveViewModel: ObservableObject {
@Published var paymentAddresses: [PaymentAddress?] = []
@Published var addressGenerationStatus = AddressGenerationStatus.generating
@Published var receiveViewError: MondayError?
@Published var lightningWarning: MondayError?
@Published var networkColor = Color.gray
@Published var amountSat: UInt64 = 0
@Published var message: String = ""
Expand All @@ -33,6 +34,8 @@ class ReceiveViewModel: ObservableObject {
func receivePayment(amountSat: UInt64, message: String, expirySecs: UInt32) async {
await MainActor.run {
self.addressGenerationStatus = .generating
self.receiveViewError = nil
self.lightningWarning = nil
}
let receiveCapacity = maxReceiveCapacity()
let needsJIT =
Expand Down Expand Up @@ -109,23 +112,45 @@ class ReceiveViewModel: ObservableObject {
let needsJIT =
amountSat.satsAsMsats > receiveCapacity || (amountSat == 0 && receiveCapacity == 0)

// Always try to generate bolt11 invoice
// The needsJIT flag will handle JIT channel creation when capacity is insufficient
do {
let bolt11Invoice = try await lightningClient.bolt11Payment(
amountSat.satsAsMsats,
Bolt11InvoiceDescription.direct(description: message), //message,
expirySecs,
nil,
needsJIT
)
let bolt11InvoiceString = bolt11Invoice.description
bolt11PaymentAddress = PaymentAddress(
type: needsJIT ? .bolt11Jit : .bolt11,
address: bolt11InvoiceString
// Always try to generate bolt11 invoice unless we know it will fail (e.g., zero amount with no inbound)
if amountSat == 0 && needsJIT {
debugPrint(
"Skipping Bolt11 generation: zero-amount request with no inbound capacity (needsJIT=true)"
)
} catch {
debugPrint("Error generating Bolt11:", error.localizedDescription)
} else {
do {
let bolt11Invoice = try await lightningClient.bolt11Payment(
amountSat.satsAsMsats,
Bolt11InvoiceDescription.direct(description: message),
expirySecs,
nil,
needsJIT
)
let bolt11InvoiceString = bolt11Invoice.description
bolt11PaymentAddress = PaymentAddress(
type: needsJIT ? .bolt11Jit : .bolt11,
address: bolt11InvoiceString
)
} catch let nodeError as NodeError {
let mondayError = handleNodeError(nodeError)
let pendingInbound = lightningClient.listPayments()
.filter { $0.direction == .inbound && $0.status == .pending }
let pendingSummary = pendingInbound.map {
"id=\($0.id), amountMsat=\($0.amountMsat), kind=\(String(describing: $0.kind))"
}
debugPrint(
"""
Error generating Bolt11 (needsJIT=\(needsJIT), amountMsat=\(amountSat.satsAsMsats), message=\(message)):
\(nodeError)
Pending inbound payments: \(pendingSummary)
"""
)
await MainActor.run {
self.lightningWarning = mondayError
}
} catch {
debugPrint("Error generating Bolt11:", error.localizedDescription)
}
}

// Unified
Expand Down
35 changes: 27 additions & 8 deletions LDKNodeMonday/View Model/Profile/Danger/SeedViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,44 @@ class SeedViewModel: ObservableObject {
serverURL: EsploraServer.mutiny_signet.url
)
@Published var seedViewError: MondayError?
private let lightningClient: LightningNodeClient
private let keyClient: KeyClient
private let lightningClient: LightningNodeClient?

init(lightningClient: LightningNodeClient) {
init(keyClient: KeyClient, lightningClient: LightningNodeClient? = nil) {
self.keyClient = keyClient
self.lightningClient = lightningClient
}

func getSeed() {
do {
let seed = try lightningClient.getBackupInfo()
self.seed = seed
} catch let error as NodeError {
let errorString = handleNodeError(error)
let seed = try keyClient.getBackupInfo()
DispatchQueue.main.async {
self.seedViewError = .init(title: errorString.title, detail: errorString.detail)
self.seed = seed
}
} catch {
if let lightningClient {
do {
let seed = try lightningClient.getBackupInfo()
DispatchQueue.main.async {
self.seed = seed
}
return
} catch let nodeError as NodeError {
let errorString = handleNodeError(nodeError)
DispatchQueue.main.async {
self.seedViewError = .init(
title: errorString.title,
detail: errorString.detail
)
}
return
} catch {
// Fall through to generic error handling below.
}
}
DispatchQueue.main.async {
self.seedViewError = .init(
title: "Unexpected error",
title: "Recovery phrase unavailable",
detail: error.localizedDescription
)
}
Expand Down
13 changes: 13 additions & 0 deletions LDKNodeMonday/View/Home/Receive/ReceiveView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ struct ReceiveView: View {
selectedPaymentAddress: selectedPaymentAddress,
addressArray: viewModel.paymentAddresses.compactMap { $0 }
)
if let warning = viewModel.lightningWarning {
VStack(spacing: 4) {
Text(warning.title)
.font(.subheadline.weight(.semibold))
.foregroundStyle(.secondary)
Text(warning.detail)
.font(.caption)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
}
.padding(.top, 16)
.padding(.horizontal, 32)
}
}

Spacer()
Expand Down
13 changes: 10 additions & 3 deletions LDKNodeMonday/View/Settings/Danger/SeedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ import LDKNode
import SwiftUI

struct SeedView: View {
@ObservedObject var viewModel: SeedViewModel
@StateObject private var viewModel: SeedViewModel
@State private var showAlert = false
@State private var showRecoveryPhrase = false
@State private var isCopied = false
@State private var showCheckmark = false
@State private var showingSeedViewErrorAlert = false

init(keyClient: KeyClient, lightningClient: LightningNodeClient) {
_viewModel = StateObject(
wrappedValue: SeedViewModel(keyClient: keyClient, lightningClient: lightningClient)
)
}

var body: some View {

VStack(alignment: .center) {
Expand Down Expand Up @@ -46,7 +52,8 @@ struct SeedView: View {
preferredWordsPerRow: 2,
usePaging: true,
wordsPerPage: 12
).padding()
)
.padding()

HStack {
Button(
Expand Down Expand Up @@ -98,6 +105,6 @@ struct SeedView: View {

#if DEBUG
#Preview {
SeedView(viewModel: .init(lightningClient: .mock))
SeedView(keyClient: .mock, lightningClient: .mock)
}
#endif
3 changes: 2 additions & 1 deletion LDKNodeMonday/View/Settings/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ struct SettingsView: View {
Section {
NavigationLink(
destination: SeedView(
viewModel: .init(lightningClient: viewModel.lightningClient)
keyClient: viewModel.keyClient,
lightningClient: viewModel.lightningClient
)
) {
Label("Recovery Phrase", systemImage: "lock")
Expand Down