diff --git a/Bitkit.xcodeproj/project.pbxproj b/Bitkit.xcodeproj/project.pbxproj index 5559d45a..d04c6dc1 100644 --- a/Bitkit.xcodeproj/project.pbxproj +++ b/Bitkit.xcodeproj/project.pbxproj @@ -7,6 +7,15 @@ objects = { /* Begin PBXBuildFile section */ + 9637E6D32C32CE79004A92FC /* Env.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9637E6D22C32CE79004A92FC /* Env.swift */; }; + 9637E6D52C32D811004A92FC /* OnChainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9637E6D42C32D811004A92FC /* OnChainService.swift */; }; + 9637E6D82C32D8A7004A92FC /* BitcoinDevKit in Frameworks */ = {isa = PBXBuildFile; productRef = 9637E6D72C32D8A7004A92FC /* BitcoinDevKit */; }; + 9637E6DA2C32E573004A92FC /* OnChainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9637E6D92C32E573004A92FC /* OnChainViewModel.swift */; }; + 9637E6DD2C32EAA8004A92FC /* WalletNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9637E6DC2C32EAA8004A92FC /* WalletNetwork.swift */; }; + 9637E6DF2C32ED7B004A92FC /* LnPeer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9637E6DE2C32ED7B004A92FC /* LnPeer.swift */; }; + 96B129FD2C2EC05D00DD07B0 /* LDKNode in Frameworks */ = {isa = PBXBuildFile; productRef = 96B129FC2C2EC05D00DD07B0 /* LDKNode */; }; + 96B12A002C2EC37B00DD07B0 /* LightningViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96B129FF2C2EC37B00DD07B0 /* LightningViewModel.swift */; }; + 96B12A032C2EC65000DD07B0 /* LightningService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96B12A022C2EC65000DD07B0 /* LightningService.swift */; }; 96FE1F652C2DE6AA006D0C8B /* BitkitApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96FE1F642C2DE6AA006D0C8B /* BitkitApp.swift */; }; 96FE1F672C2DE6AA006D0C8B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96FE1F662C2DE6AA006D0C8B /* ContentView.swift */; }; 96FE1F692C2DE6AC006D0C8B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96FE1F682C2DE6AC006D0C8B /* Assets.xcassets */; }; @@ -34,6 +43,13 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 9637E6D22C32CE79004A92FC /* Env.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Env.swift; sourceTree = ""; }; + 9637E6D42C32D811004A92FC /* OnChainService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnChainService.swift; sourceTree = ""; }; + 9637E6D92C32E573004A92FC /* OnChainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnChainViewModel.swift; sourceTree = ""; }; + 9637E6DC2C32EAA8004A92FC /* WalletNetwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletNetwork.swift; sourceTree = ""; }; + 9637E6DE2C32ED7B004A92FC /* LnPeer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LnPeer.swift; sourceTree = ""; }; + 96B129FF2C2EC37B00DD07B0 /* LightningViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightningViewModel.swift; sourceTree = ""; }; + 96B12A022C2EC65000DD07B0 /* LightningService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightningService.swift; sourceTree = ""; }; 96FE1F612C2DE6AA006D0C8B /* Bitkit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Bitkit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 96FE1F642C2DE6AA006D0C8B /* BitkitApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitkitApp.swift; sourceTree = ""; }; 96FE1F662C2DE6AA006D0C8B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -52,6 +68,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 9637E6D82C32D8A7004A92FC /* BitcoinDevKit in Frameworks */, + 96B129FD2C2EC05D00DD07B0 /* LDKNode in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -72,6 +90,41 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 9637E6D12C32CE65004A92FC /* Constants */ = { + isa = PBXGroup; + children = ( + 9637E6D22C32CE79004A92FC /* Env.swift */, + ); + path = Constants; + sourceTree = ""; + }; + 9637E6DB2C32EA84004A92FC /* Models */ = { + isa = PBXGroup; + children = ( + 9637E6DC2C32EAA8004A92FC /* WalletNetwork.swift */, + 9637E6DE2C32ED7B004A92FC /* LnPeer.swift */, + ); + path = Models; + sourceTree = ""; + }; + 96B129FE2C2EC0ED00DD07B0 /* ViewModels */ = { + isa = PBXGroup; + children = ( + 96B129FF2C2EC37B00DD07B0 /* LightningViewModel.swift */, + 9637E6D92C32E573004A92FC /* OnChainViewModel.swift */, + ); + path = ViewModels; + sourceTree = ""; + }; + 96B12A012C2EC61500DD07B0 /* Services */ = { + isa = PBXGroup; + children = ( + 96B12A022C2EC65000DD07B0 /* LightningService.swift */, + 9637E6D42C32D811004A92FC /* OnChainService.swift */, + ); + path = Services; + sourceTree = ""; + }; 96FE1F582C2DE6AA006D0C8B = { isa = PBXGroup; children = ( @@ -97,6 +150,10 @@ children = ( 96FE1F642C2DE6AA006D0C8B /* BitkitApp.swift */, 96FE1F662C2DE6AA006D0C8B /* ContentView.swift */, + 96B129FE2C2EC0ED00DD07B0 /* ViewModels */, + 96B12A012C2EC61500DD07B0 /* Services */, + 9637E6DB2C32EA84004A92FC /* Models */, + 9637E6D12C32CE65004A92FC /* Constants */, 96FE1F682C2DE6AC006D0C8B /* Assets.xcassets */, 96FE1F6A2C2DE6AC006D0C8B /* Bitkit.entitlements */, 96FE1F6B2C2DE6AC006D0C8B /* Preview Content */, @@ -145,6 +202,10 @@ dependencies = ( ); name = Bitkit; + packageProductDependencies = ( + 96B129FC2C2EC05D00DD07B0 /* LDKNode */, + 9637E6D72C32D8A7004A92FC /* BitcoinDevKit */, + ); productName = Bitkit; productReference = 96FE1F612C2DE6AA006D0C8B /* Bitkit.app */; productType = "com.apple.product-type.application"; @@ -217,6 +278,10 @@ Base, ); mainGroup = 96FE1F582C2DE6AA006D0C8B; + packageReferences = ( + 96B129FB2C2EC05D00DD07B0 /* XCRemoteSwiftPackageReference "ldk-node" */, + 9637E6D62C32D8A7004A92FC /* XCRemoteSwiftPackageReference "bdk-swift" */, + ); productRefGroup = 96FE1F622C2DE6AA006D0C8B /* Products */; projectDirPath = ""; projectRoot = ""; @@ -259,7 +324,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9637E6D32C32CE79004A92FC /* Env.swift in Sources */, + 96B12A002C2EC37B00DD07B0 /* LightningViewModel.swift in Sources */, + 9637E6DD2C32EAA8004A92FC /* WalletNetwork.swift in Sources */, + 9637E6DA2C32E573004A92FC /* OnChainViewModel.swift in Sources */, + 9637E6DF2C32ED7B004A92FC /* LnPeer.swift in Sources */, 96FE1F672C2DE6AA006D0C8B /* ContentView.swift in Sources */, + 96B12A032C2EC65000DD07B0 /* LightningService.swift in Sources */, + 9637E6D52C32D811004A92FC /* OnChainService.swift in Sources */, 96FE1F652C2DE6AA006D0C8B /* BitkitApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -621,6 +693,38 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 9637E6D62C32D8A7004A92FC /* XCRemoteSwiftPackageReference "bdk-swift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/bitcoindevkit/bdk-swift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.31.1; + }; + }; + 96B129FB2C2EC05D00DD07B0 /* XCRemoteSwiftPackageReference "ldk-node" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/lightningdevkit/ldk-node"; + requirement = { + branch = main; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 9637E6D72C32D8A7004A92FC /* BitcoinDevKit */ = { + isa = XCSwiftPackageProductDependency; + package = 9637E6D62C32D8A7004A92FC /* XCRemoteSwiftPackageReference "bdk-swift" */; + productName = BitcoinDevKit; + }; + 96B129FC2C2EC05D00DD07B0 /* LDKNode */ = { + isa = XCSwiftPackageProductDependency; + package = 96B129FB2C2EC05D00DD07B0 /* XCRemoteSwiftPackageReference "ldk-node" */; + productName = LDKNode; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 96FE1F592C2DE6AA006D0C8B /* Project object */; } diff --git a/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..6090409f --- /dev/null +++ b/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,24 @@ +{ + "originHash" : "957b506793f070febdbc6a14c01a0fcfc0c18f7f437da5a1d36c9627c119b759", + "pins" : [ + { + "identity" : "bdk-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/bitcoindevkit/bdk-swift", + "state" : { + "revision" : "f2398109ebd8759322f601d9938779f3cdf99a12", + "version" : "0.31.1" + } + }, + { + "identity" : "ldk-node", + "kind" : "remoteSourceControl", + "location" : "https://github.com/lightningdevkit/ldk-node", + "state" : { + "branch" : "main", + "revision" : "66fec694f2b1de81dcb3d40073698a03dfe25522" + } + } + ], + "version" : 3 +} diff --git a/Bitkit.xcodeproj/project.xcworkspace/xcuserdata/jason.xcuserdatad/UserInterfaceState.xcuserstate b/Bitkit.xcodeproj/project.xcworkspace/xcuserdata/jason.xcuserdatad/UserInterfaceState.xcuserstate index e56afd0a..9a82fa0f 100644 Binary files a/Bitkit.xcodeproj/project.xcworkspace/xcuserdata/jason.xcuserdatad/UserInterfaceState.xcuserstate and b/Bitkit.xcodeproj/project.xcworkspace/xcuserdata/jason.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Bitkit/Constants/Env.swift b/Bitkit/Constants/Env.swift new file mode 100644 index 00000000..95b18b4c --- /dev/null +++ b/Bitkit/Constants/Env.swift @@ -0,0 +1,93 @@ +// +// Env.swift +// Bitkit +// +// Created by Jason van den Berg on 2024/07/01. +// + +import Foundation + +struct Env { + static let isPreview = ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" + static let isTestFlight = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" + static let isUnitTest = ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil + +#if targetEnvironment(simulator) + static let isSim = true +#else + static let isSim = false +#endif + +#if DEBUG + static let isDebug = true +#else + static let isDebug = false +#endif + + //MARK: wallet services + static let network: WalletNetwork = .regtest + static var esploraServerUrl: String { + switch network { + case .regtest: + //cargo run --release --bin electrs -- -vvv --jsonrpc-import --daemon-rpc-addr 127.0.0.1:18443 --cookie polaruser:polarpass + // return "https://jaybird-logical-sadly.ngrok-free.app" + return "http://localhost:3000" + case .bitcoin: + fatalError("Bitcoin network not implemented") + case .testnet: + fatalError("Testnet network not implemented") + case .signet: + fatalError("Signet network not implemented") + } + } + + static var appStorageUrl: URL { + //TODO move to app group so files can be shared with extensions + guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { + fatalError("Could not find documents directory") + } + + return documentsDirectory + } + + static var ldkStorage: URL { + let storageDirPath = appStorageUrl.appendingPathComponent("ldk") + + switch network { + case .regtest: + return storageDirPath.appendingPathComponent("regtest") + case .bitcoin: + fatalError("Bitcoin network not implemented") + case .testnet: + fatalError("Testnet network not implemented") + case .signet: + fatalError("Signet network not implemented") + } + } + + static var ldkRgsServerUrl: String? { + switch network { + case .regtest: + return nil + case .bitcoin: + return "https://rapidsync.lightningdevkit.org/snapshot/" + case .testnet: + return nil + case .signet: + return nil + } + } + + static var trustedLnPeers: [LnPeer] { + switch network { + case .regtest: + return [.init(nodeId: "021de6ad59a78caf8f376cbd022e8c6ede2a1ef0a4fa035174e8b9c25ad5866584", address: "127.0.0.1:9736")] + case .bitcoin: + return [] + case .testnet: + return [] + case .signet: + return [] + } + } +} diff --git a/Bitkit/ContentView.swift b/Bitkit/ContentView.swift index 40c89e7d..22b4ac04 100644 --- a/Bitkit/ContentView.swift +++ b/Bitkit/ContentView.swift @@ -8,14 +8,68 @@ import SwiftUI struct ContentView: View { + @StateObject var lnViewModel = LightningViewModel() + @StateObject var onChainViewModel = OnChainViewModel() + var body: some View { VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, Bitcoin!") + Group { + Text("LDK-Node running: \(lnViewModel.status?.isRunning == true ? "✅" : "❌")") + + + if let nodeId = lnViewModel.nodeId { + Text("LN Node ID: \(nodeId)") + .onTapGesture { + UIPasteboard.general.string = nodeId + } + } + + if let peers = lnViewModel.peers { + ForEach(peers, id: \.nodeId) { peer in + Text("Peer: \(peer.nodeId) \(peer.address) \(peer.isConnected ? "✅" : "❌")") + } + } + + if let lnBalance = lnViewModel.balance { + Text("Lightning \(lnBalance.totalLightningBalanceSats)") + } + + if let onchainBalance = onChainViewModel.balance { + Text("On Chain \(onchainBalance.total)") + } + + if let receiveAddress = onChainViewModel.address { + Text("Receive Address: \(receiveAddress)") + .onTapGesture { + UIPasteboard.general.string = receiveAddress + } + } + + Button("New Receive Address") { + try! onChainViewModel.newReceiveAddress() + } + + Button("Sync") { + Task { + await lnViewModel.sync() + await onChainViewModel.sync() + } + } + } + .multilineTextAlignment(.center) + .padding() + } + .padding(4) + .onAppear { + Task { + do { + try await lnViewModel.start() + try await onChainViewModel.start() + } catch { + print("Error: \(error)") + } + } } - .padding() } } diff --git a/Bitkit/Models/LnPeer.swift b/Bitkit/Models/LnPeer.swift new file mode 100644 index 00000000..c39f592e --- /dev/null +++ b/Bitkit/Models/LnPeer.swift @@ -0,0 +1,30 @@ +// +// LnPeer.swift +// Bitkit +// +// Created by Jason van den Berg on 2024/07/01. +// + +import Foundation + +struct LnPeer { + let nodeId: String + let address: String + + init(nodeId: String, address: String) { + self.nodeId = nodeId + self.address = address + } + + init(connection: String) throws { + let parts = connection.split(separator: "@") + guard parts.count == 2 else { +// throw LnPeerError.invalidConnection + //TODO throw custom error + fatalError("Invalid connection") + } + + nodeId = String(parts[0]) + address = String(parts[1]) + } +} diff --git a/Bitkit/Models/WalletNetwork.swift b/Bitkit/Models/WalletNetwork.swift new file mode 100644 index 00000000..9332a9a8 --- /dev/null +++ b/Bitkit/Models/WalletNetwork.swift @@ -0,0 +1,56 @@ +// +// Network.swift +// Bitkit +// +// Created by Jason van den Berg on 2024/07/01. +// + +import Foundation +import LDKNode +import BitcoinDevKit + +enum WalletNetwork { + case regtest + case bitcoin + case testnet + case signet + + var displayName: String { + switch self { + case .regtest: + return "Regtest" + case .bitcoin: + return "Bitcoin" + case .testnet: + return "Testnet" + case .signet: + return "Signet" + } + } + + var ldkNetwork: LDKNode.Network { + switch self { + case .regtest: + return .regtest + case .bitcoin: + return .bitcoin + case .testnet: + return .testnet + case .signet: + return .signet + } + } + + var bdkNetwork: BitcoinDevKit.Network { + switch self { + case .regtest: + return .regtest + case .bitcoin: + return .bitcoin + case .testnet: + return .testnet + case .signet: + return .signet + } + } +} diff --git a/Bitkit/Services/LightningService.swift b/Bitkit/Services/LightningService.swift new file mode 100644 index 00000000..15724d67 --- /dev/null +++ b/Bitkit/Services/LightningService.swift @@ -0,0 +1,72 @@ +// +// LightningService.swift +// Bitkit +// +// Created by Jason van den Berg on 2024/06/28. +// + +import Foundation +import LDKNode + +//TODO catch all errors and pass a readable error message to the UI + +class LightningService { + private var node: Node? + + static var shared: LightningService = LightningService() + + private init() {} + + func setup(mnemonic: String, passphrase: String?) throws { + var config = defaultConfig() + config.storageDirPath = Env.ldkStorage.path + config.logDirPath = Env.ldkStorage.path + config.network = Env.network.ldkNetwork + config.logLevel = .trace + config.anchorChannelsConfig = .init( + trustedPeersNoReserve: Env.trustedLnPeers.map({ $0.nodeId }), + perChannelReserveSats: 2000 //TODO set correctly + ) + + let nodeBuilder = Builder.fromConfig(config: config) + nodeBuilder.setEsploraServer(esploraServerUrl: Env.esploraServerUrl) + + if let rgsServerUrl = Env.ldkRgsServerUrl { + nodeBuilder.setGossipSourceRgs(rgsServerUrl: rgsServerUrl) + } + + nodeBuilder.setEntropyBip39Mnemonic(mnemonic: mnemonic, passphrase: nil) + + node = try nodeBuilder.build() + } + + func start() throws { + guard let node else { + //TODO throw custom error + return + } + + try node.start() + for peer in Env.trustedLnPeers { + try node.connect(nodeId: peer.nodeId, address: peer.address, persist: true) + } + } + + func sync() throws { + guard let node else { + //TODO throw custom error + return + } + try node.syncWallets() + } +} + +//MARK: UI Helpers (Published via LightningViewModel) +extension LightningService { + var nodeId: String? { node?.nodeId() } + var balances: BalanceDetails? { node?.listBalances() } + var status: NodeStatus? { node?.status() } + var peers: [PeerDetails]? { node?.listPeers() } + var Channels: [ChannelDetails]? { node?.listChannels() } + var payments: [PaymentDetails]? { node?.listPayments() } +} diff --git a/Bitkit/Services/OnChainService.swift b/Bitkit/Services/OnChainService.swift new file mode 100644 index 00000000..835d994b --- /dev/null +++ b/Bitkit/Services/OnChainService.swift @@ -0,0 +1,83 @@ +// +// OnChainService.swift +// Bitkit +// +// Created by Jason van den Berg on 2024/07/01. +// + +import Foundation +import BitcoinDevKit + +class OnChainService { + private var wallet: Wallet? + private var blockchainConfig: BlockchainConfig? + + static var shared: OnChainService = OnChainService() + + private init() {} + + func setup() throws { + //TODO maybe better as a lazy var + let esploraConfig = EsploraConfig( + baseUrl: Env.esploraServerUrl, + proxy: nil, + concurrency: nil, + stopGap: UInt64(20), + timeout: nil + ) + + blockchainConfig = BlockchainConfig.esplora(config: esploraConfig) + } + + func createWallet(mnemonic: String, passphrase: String?) throws { + let mnemonic = try Mnemonic.fromString(mnemonic: mnemonic) + let secretKey = DescriptorSecretKey( + network: Env.network.bdkNetwork, + mnemonic: mnemonic, + password: passphrase + ) + let descriptor = Descriptor.newBip86( + secretKey: secretKey, + keychain: .external, + network: Env.network.bdkNetwork + ) + let changeDescriptor = Descriptor.newBip86( + secretKey: secretKey, + keychain: .internal, + network: Env.network.bdkNetwork + ) + + //TODO save to keychain + + wallet = try Wallet( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + network: Env.network.bdkNetwork, + databaseConfig: .memory //TODO use sqlite + ) + } + + func getAddress() throws -> String { + guard let wallet else { + //TODO throw custom error + return "error" + } + let addressInfo = try wallet.getAddress(addressIndex: .new) + return addressInfo.address.asString() + } + + func sync() throws { + guard let wallet, let blockchainConfig else { + //TODO throw custom error + return + } + let blockchain = try Blockchain(config: blockchainConfig) + try wallet.sync(blockchain: blockchain, progress: nil) + } +} + +//MARK: UI Helpers (Published via OnChainViewModel) +extension OnChainService { + //TODO catch errors? + var balance: Balance? { try? wallet?.getBalance() } +} diff --git a/Bitkit/ViewModels/LightningViewModel.swift b/Bitkit/ViewModels/LightningViewModel.swift new file mode 100644 index 00000000..6e168f6c --- /dev/null +++ b/Bitkit/ViewModels/LightningViewModel.swift @@ -0,0 +1,40 @@ +// +// LightningViewModel.swift +// Bitkit +// +// Created by Jason van den Berg on 2024/06/28. +// + +import SwiftUI +import LDKNode + +@MainActor +class LightningViewModel: ObservableObject { + @Published var status: NodeStatus? + @Published var nodeId: String? + @Published var balance: BalanceDetails? + @Published var peers: [PeerDetails]? + + func start() async throws { + let mnemonic = "science fatigue phone inner pipe solve acquire nothing birth slow armor flip debate gorilla select settle talk badge uphold firm video vibrant banner casual" // = generateEntropyMnemonic() + let passphrase: String? = nil + + try LightningService.shared.setup(mnemonic: mnemonic, passphrase: passphrase) + try LightningService.shared.start() + await sync() + } + + func sync() async { + do { + try LightningService.shared.sync() + status = LightningService.shared.status + nodeId = LightningService.shared.nodeId + balance = LightningService.shared.balances + peers = LightningService.shared.peers + + //TODO sync everything else for the UI + } catch { + print("Error: \(error)") + } + } +} diff --git a/Bitkit/ViewModels/OnChainViewModel.swift b/Bitkit/ViewModels/OnChainViewModel.swift new file mode 100644 index 00000000..f5e98573 --- /dev/null +++ b/Bitkit/ViewModels/OnChainViewModel.swift @@ -0,0 +1,37 @@ +// +// OnChainViewModel.swift +// Bitkit +// +// Created by Jason van den Berg on 2024/07/01. +// + +import SwiftUI +import BitcoinDevKit + +@MainActor +class OnChainViewModel: ObservableObject { + @Published var balance: Balance? + @Published var address: String? + + func start() async throws { + let mnemonic = "science fatigue phone inner pipe solve acquire nothing birth slow armor flip debate gorilla select settle talk badge uphold firm video vibrant banner casual" // = generateEntropyMnemonic() + let passphrase: String? = nil + + try OnChainService.shared.setup() + try OnChainService.shared.createWallet(mnemonic: mnemonic, passphrase: passphrase) + await sync() + } + + func newReceiveAddress() throws { + address = try OnChainService.shared.getAddress() + } + + func sync() async { + do { + try OnChainService.shared.sync() + balance = OnChainService.shared.balance + } catch { + print("Error: \(error)") + } + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..e69de29b diff --git a/README.md b/README.md new file mode 100644 index 00000000..99f2b839 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +## Error handling + +- Don't pass exceptions up to the UI +- Translations +- Known lightning errors