From 9a4ea774b90113b72d47e1df12f5260ce6ef0e97 Mon Sep 17 00:00:00 2001 From: Rubens Date: Sat, 24 May 2025 15:52:07 -0300 Subject: [PATCH 01/50] tests --- .../project.pbxproj | 20 ++ .../Connection+Extensions.swift | 6 + .../Extensions/URL+Extensions.swift | 31 +++ .../BDKSyncService/BDKSyncService.swift | 191 ++++++++++++++++++ .../EsploraServerSyncService.swift | 114 +++++++++++ .../Service/Key Service/KeyService.swift | 17 +- 6 files changed, 376 insertions(+), 3 deletions(-) create mode 100644 BDKSwiftExampleWallet/Extensions/URL+Extensions.swift create mode 100644 BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift create mode 100644 BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift diff --git a/BDKSwiftExampleWallet.xcodeproj/project.pbxproj b/BDKSwiftExampleWallet.xcodeproj/project.pbxproj index ef41cd63..83c20ce0 100644 --- a/BDKSwiftExampleWallet.xcodeproj/project.pbxproj +++ b/BDKSwiftExampleWallet.xcodeproj/project.pbxproj @@ -12,6 +12,9 @@ 779E70892DB9C9AB006E22D3 /* WalletFullScanScriptInspector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 779E70882DB9C9AB006E22D3 /* WalletFullScanScriptInspector.swift */; }; 77AD9F062DBB031D00182E65 /* ActivityHomeHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77AD9F052DBB031D00182E65 /* ActivityHomeHeaderView.swift */; }; 77F0FDC92DA9A93D00B30E4F /* Connection+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F0FDC82DA9A93700B30E4F /* Connection+Extensions.swift */; }; + 77F6A9E32DE247B2003568F0 /* BDKSyncService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E22DE247AD003568F0 /* BDKSyncService.swift */; }; + 77F6A9E52DE24841003568F0 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E42DE24837003568F0 /* URL+Extensions.swift */; }; + 77F6A9E72DE248A2003568F0 /* EsploraServerSyncService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E62DE248A1003568F0 /* EsploraServerSyncService.swift */; }; A733D6D02A81113000F333B4 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = A733D6CF2A81113000F333B4 /* Localizable.xcstrings */; }; A73F7A362A3B778E00B87FC6 /* Int+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A73F7A352A3B778E00B87FC6 /* Int+Extensions.swift */; }; AE0C30F72A804A2D008F1EAE /* TransactionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE0C30F62A804A2D008F1EAE /* TransactionListView.swift */; }; @@ -117,6 +120,9 @@ 779E70882DB9C9AB006E22D3 /* WalletFullScanScriptInspector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletFullScanScriptInspector.swift; sourceTree = ""; }; 77AD9F052DBB031D00182E65 /* ActivityHomeHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityHomeHeaderView.swift; sourceTree = ""; }; 77F0FDC82DA9A93700B30E4F /* Connection+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Extensions.swift"; sourceTree = ""; }; + 77F6A9E22DE247AD003568F0 /* BDKSyncService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BDKSyncService.swift; sourceTree = ""; }; + 77F6A9E42DE24837003568F0 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; + 77F6A9E62DE248A1003568F0 /* EsploraServerSyncService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EsploraServerSyncService.swift; sourceTree = ""; }; A733D6CF2A81113000F333B4 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; A73F7A352A3B778E00B87FC6 /* Int+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Extensions.swift"; sourceTree = ""; }; AE0C30F62A804A2D008F1EAE /* TransactionListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionListView.swift; sourceTree = ""; }; @@ -246,6 +252,15 @@ path = Home; sourceTree = ""; }; + 77F6A9E12DE247A3003568F0 /* BDKSyncService */ = { + isa = PBXGroup; + children = ( + 77F6A9E62DE248A1003568F0 /* EsploraServerSyncService.swift */, + 77F6A9E22DE247AD003568F0 /* BDKSyncService.swift */, + ); + path = BDKSyncService; + sourceTree = ""; + }; A7FBCE392A72944C007C960E /* Resources */ = { isa = PBXGroup; children = ( @@ -271,6 +286,7 @@ AE1C341E2A42440A008F807A /* Extensions */ = { isa = PBXGroup; children = ( + 77F6A9E42DE24837003568F0 /* URL+Extensions.swift */, AE18E9372A9528200019D2A4 /* Bundle+Extensions.swift */, AE3646252BEDB01200B04E25 /* FileManager+Extensions.swift */, A73F7A352A3B778E00B87FC6 /* Int+Extensions.swift */, @@ -512,6 +528,7 @@ AEB905C52A7EECD900CD0337 /* Service */ = { isa = PBXGroup; children = ( + 77F6A9E12DE247A3003568F0 /* BDKSyncService */, AE1C34212A424434008F807A /* BDK Service */, AEB905C42A7EECAF00CD0337 /* Price Service */, AE6715FB2A9ABF30005C193F /* Fee Service */, @@ -690,6 +707,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 77F6A9E32DE247B2003568F0 /* BDKSyncService.swift in Sources */, AE83EFDB2C9D07B200B41244 /* ChainPosition+Extensions.swift in Sources */, AE2ADD782B61EFFF00C2A823 /* SettingsViewModel.swift in Sources */, AEAB03112ABDDB86000C9528 /* FeeViewModel.swift in Sources */, @@ -706,6 +724,7 @@ AE7F670C2A7451D700CED561 /* CurrencyCode.swift in Sources */, AE2ADD762B61EFEB00C2A823 /* HomeViewModel.swift in Sources */, AE783A032AB4ECC2005F0CBA /* AddressView.swift in Sources */, + 77F6A9E52DE24841003568F0 /* URL+Extensions.swift in Sources */, AEB159D52D51A8680006AE9E /* View+Extensions.swift in Sources */, AE7F67052A7446B600CED561 /* PriceService.swift in Sources */, AEAB03132ABDDBF4000C9528 /* AmountViewModel.swift in Sources */, @@ -756,6 +775,7 @@ AE0C30F92A804B65008F1EAE /* OnboardingViewModel.swift in Sources */, AE3902A42A3B4CD900BEC318 /* HomeView.swift in Sources */, AE0C30FD2A804BC1008F1EAE /* ReceiveViewModel.swift in Sources */, + 77F6A9E72DE248A2003568F0 /* EsploraServerSyncService.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift index 3f09b059..170cdd68 100644 --- a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift +++ b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift @@ -17,4 +17,10 @@ extension Connection { let connection = try Connection(path: persistenceBackendPath) return connection } + + static func loadConnection() throws -> Connection { + let persistenceBackendPath = URL.persistenceBackendPath + let connection = try Connection(path: persistenceBackendPath) + return connection + } } diff --git a/BDKSwiftExampleWallet/Extensions/URL+Extensions.swift b/BDKSwiftExampleWallet/Extensions/URL+Extensions.swift new file mode 100644 index 00000000..fe1b54a6 --- /dev/null +++ b/BDKSwiftExampleWallet/Extensions/URL+Extensions.swift @@ -0,0 +1,31 @@ +// +// URL+Extensions.swift +// BDKSwiftExampleWallet +// +// Created by Rubens Machion on 17/05/25. +// + +import Foundation + +extension URL { + + static var defaultWalletDirectory: URL { + URL.documentsDirectory + } + + static var walletDirectoryName: String { + "wallet_data" + } + + static var walletDBName: String { + "wallet.sqlite" + } + + static var walletDataDirectoryURL: URL { + defaultWalletDirectory.appendingPathComponent(walletDirectoryName) + } + + static var persistenceBackendPath: String { + walletDataDirectoryURL.appendingPathComponent(walletDBName).path + } +} diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift new file mode 100644 index 00000000..6d0a3597 --- /dev/null +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -0,0 +1,191 @@ +// +// BDKService2.swift +// BDKSwiftExampleWallet +// +// Created by Rubens Machion on 16/05/25. +// + +import BitcoinDevKit +import Foundation + +protocol BDKSyncService { + var connection: Connection? { get } + var keyClient: KeyClient { get } + var network: Network { get } + var wallet: Wallet? { get } + + func createWallet(params: String?) throws + func loadWallet() throws + func deleteWallet() throws + func startSync(progress: SyncScriptInspector) async throws + func startFullScan(progress: FullScanScriptInspector) async throws + + func updateNetwork(network: Network) + func updateEsploraURL(_ url: String) + + func getTransactions() throws -> [CanonicalTx] + func getBalance() throws -> Balance +} + +extension BDKSyncService { + func buildWallet(params: String?) throws -> Wallet { + guard let connection = self.connection else { + throw WalletError.dbNotFound + } + + let backupInfo = try buildBackupInfo(params: params ?? Mnemonic(wordCount: WordCount.words12).description) + + try keyClient.saveBackupInfo(backupInfo) + try keyClient.saveNetwork(self.network.description) + + let descriptor = try Descriptor(descriptor: backupInfo.descriptor, network: network) + let changeDescriptor = try Descriptor(descriptor: backupInfo.changeDescriptor, network: network) + + let wallet = try Wallet( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + network: network, + connection: connection + ) + + return wallet + } + + func buildBackupInfo(params: String) throws -> BackupInfo { + if isXPub(params) { + let descriptorPublicKey = try DescriptorPublicKey.fromString(publicKey: params) + let fingerprint = descriptorPublicKey.masterFingerprint() + let descriptor = Descriptor.newBip86Public( + publicKey: descriptorPublicKey, + fingerprint: fingerprint, + keychain: .external, + network: network + ) + let changeDescriptor = Descriptor.newBip86Public( + publicKey: descriptorPublicKey, + fingerprint: fingerprint, + keychain: .internal, + network: network + ) + return .init( + mnemonic: "", + descriptor: descriptor.description, + changeDescriptor: changeDescriptor.description + ) + } + + if isDescriptor(params) { // is a descriptor? + + let descriptorStrings = params.components(separatedBy: "\n") + .map { $0.split(separator: "#").first?.trimmingCharacters(in: .whitespaces) ?? "" } + .filter { !$0.isEmpty } + let descriptor: Descriptor + let changeDescriptor: Descriptor + + if descriptorStrings.count == 1 { + let parsedDescriptor = try Descriptor( + descriptor: descriptorStrings[0], + network: network + ) + let singleDescriptors = try parsedDescriptor.toSingleDescriptors() + guard singleDescriptors.count >= 2 else { + throw AppError.generic(message: "Too many output descriptors to parse") + } + descriptor = singleDescriptors[0] + changeDescriptor = singleDescriptors[1] + } else if descriptorStrings.count == 2 { + descriptor = try Descriptor(descriptor: descriptorStrings[0], network: network) + changeDescriptor = try Descriptor(descriptor: descriptorStrings[1], network: network) + } else { + throw AppError.generic(message: "Descriptor parsing failed") + } + + return .init( + mnemonic: "", + descriptor: descriptor.description, + changeDescriptor: changeDescriptor.description + ) + } + + guard let mnemonic = try? Mnemonic.fromString(mnemonic: params) else { + throw AppError.generic(message: "Invalid mnemonic") + } + let secretKey = DescriptorSecretKey( + network: network, + mnemonic: mnemonic, + password: nil + ) + let descriptor = Descriptor.newBip86( + secretKey: secretKey, + keychain: .external, + network: network + ) + let changeDescriptor = Descriptor.newBip86( + secretKey: secretKey, + keychain: .internal, + network: network + ) + return .init( + mnemonic: mnemonic.description, + descriptor: descriptor.description, + changeDescriptor: changeDescriptor.description + ) + } + + func deleteData() throws { + do { + try keyClient.deleteAllData() + + if let bundleID = Bundle.main.bundleIdentifier { + UserDefaults.standard.removePersistentDomain(forName: bundleID) + } + + let walletDataDirectoryURL = URL.walletDataDirectoryURL + if FileManager.default.fileExists(atPath: walletDataDirectoryURL.path) { + try FileManager.default.removeItem(at: walletDataDirectoryURL) + } + + } catch { + throw AppError.generic(message: "Failed to remove Keychain data") + } + } + + func loadWalleFromBackup() throws -> Wallet { + guard let connection = self.connection else { + throw WalletError.dbNotFound + } + + let backupInfo = try keyClient.getBackupInfo() + let descriptor = try Descriptor(descriptor: backupInfo.descriptor, network: self.network) + let changeDescriptor = try Descriptor( + descriptor: backupInfo.changeDescriptor, + network: self.network + ) + let wallet = try Wallet.load( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + connection: connection + ) + + return wallet + } + + // MARK: - Optionals methods + + func updateEsploraURL(_ url: String) { + // Optional implementation + } + + // MARK: - Private + + private func isDescriptor(_ param: String) -> Bool { + param.hasPrefix("tr(") || + param.hasPrefix("wpkh(") || + param.hasPrefix("wsh(") || + param.hasPrefix("sh(") + } + + private func isXPub(_ param: String) -> Bool { + param.hasPrefix("xpub") || param.hasPrefix("tpub") || param.hasPrefix("vpub") || param.hasPrefix("zpub") + } +} diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift new file mode 100644 index 00000000..57e82459 --- /dev/null +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift @@ -0,0 +1,114 @@ +// +// Untitled.swift +// BDKSwiftExampleWallet +// +// Created by Rubens Machion on 16/05/25. +// + +import BitcoinDevKit +import Foundation + +final class EsploraServerSyncService: BDKSyncService { + + static let shared = EsploraServerSyncService() + + var connection: Connection? + var keyClient: KeyClient + var network: Network + var wallet: Wallet? + + private var esploraClient: EsploraClient + + init( + keyClient: KeyClient = .live, + network: Network = .signet, + connection: Connection? = nil + ) { + self.connection = connection + self.keyClient = keyClient + self.network = network + + let url = (try? keyClient.getEsploraURL()) ?? network.url + self.esploraClient = .init( + url: url + ) + } + + func createWallet(params: String?) throws { + self.connection = try Connection.createConnection() + self.wallet = try buildWallet(params: params) + } + + func loadWallet() throws { + self.connection = try Connection.loadConnection() + let wallet = try loadWalleFromBackup() + self.wallet = wallet + } + + func deleteWallet() throws { + try deleteData() + } + + func updateNetwork(network: Network) { + self.network = network + } + + func updateEsploraURL(_ url: String) { + try? keyClient.saveEsploraURL(url) + self.esploraClient = .init(url: url) + } + + func startSync(progress: SyncScriptInspector) async throws { + guard let wallet = self.wallet else { throw WalletError.walletNotFound } + let esploraClient = self.esploraClient + let syncRequest = try wallet.startSyncWithRevealedSpks() + .inspectSpks(inspector: progress) + .build() + let update = try esploraClient.sync( + request: syncRequest, + parallelRequests: UInt64(5) + ) + let _ = try wallet.applyUpdate(update: update) + guard let connection = self.connection else { + throw WalletError.dbNotFound + } + let _ = try wallet.persist(connection: connection) + } + + func startFullScan(progress: FullScanScriptInspector) async throws { + guard let wallet = self.wallet else { throw WalletError.walletNotFound } + let esploraClient = esploraClient + let fullScanRequest = try wallet.startFullScan() + .inspectSpksForAllKeychains(inspector: progress) + .build() + let update = try esploraClient.fullScan( + request: fullScanRequest, + // using https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#address-gap-limit + stopGap: UInt64(20), + // using https://github.com/bitcoindevkit/bdk/blob/master/example-crates/example_wallet_esplora_blocking/src/main.rs + parallelRequests: UInt64(5) + ) + let _ = try wallet.applyUpdate(update: update) + guard let connection = self.connection else { + throw WalletError.dbNotFound + } + let _ = try wallet.persist(connection: connection) + } + + func getTransactions() throws -> [CanonicalTx] { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let transactions = wallet.transactions() + let sortedTransactions = transactions.sorted { (tx1, tx2) in + return tx1.chainPosition.isBefore(tx2.chainPosition) + } + return sortedTransactions + } + + func getBalance() throws -> Balance { + guard let wallet = self.wallet else { throw WalletError.walletNotFound } + let balance = wallet.balance() + return balance + } +} diff --git a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift index 5ac41e8e..2949bd2c 100644 --- a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift +++ b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift @@ -62,6 +62,12 @@ private struct KeyService { func saveNetwork(network: String) throws { keychain[string: "SelectedNetwork"] = network } + + func deletaAllData() throws { + try deleteNetwork() + try deleteBackupInfo() + try deleteEsploraURL() + } } struct KeyClient { @@ -74,6 +80,7 @@ struct KeyClient { let saveEsploraURL: (String) throws -> Void let saveBackupInfo: (BackupInfo) throws -> Void let saveNetwork: (String) throws -> Void + let deleteAllData: () throws -> Void private init( deleteBackupInfo: @escaping () throws -> Void, @@ -84,7 +91,8 @@ struct KeyClient { getNetwork: @escaping () throws -> String?, saveBackupInfo: @escaping (BackupInfo) throws -> Void, saveEsploraURL: @escaping (String) throws -> Void, - saveNetwork: @escaping (String) throws -> Void + saveNetwork: @escaping (String) throws -> Void, + deleteAllData: @escaping () throws -> Void ) { self.deleteBackupInfo = deleteBackupInfo self.deleteEsplora = deleteEsplora @@ -95,6 +103,7 @@ struct KeyClient { self.saveBackupInfo = saveBackupInfo self.saveEsploraURL = saveEsploraURL self.saveNetwork = saveNetwork + self.deleteAllData = deleteAllData } } @@ -108,7 +117,8 @@ extension KeyClient { getNetwork: { try KeyService().getNetwork() }, saveBackupInfo: { backupInfo in try KeyService().saveBackupInfo(backupInfo: backupInfo) }, saveEsploraURL: { url in try KeyService().saveEsploraURL(url: url) }, - saveNetwork: { network in try KeyService().saveNetwork(network: network) } + saveNetwork: { network in try KeyService().saveNetwork(network: network) }, + deleteAllData: { try KeyService().deletaAllData() } ) } @@ -148,7 +158,8 @@ extension KeyClient { getNetwork: { nil }, saveBackupInfo: { _ in }, saveEsploraURL: { _ in }, - saveNetwork: { _ in } + saveNetwork: { _ in }, + deleteAllData: { } ) } #endif From e188b6c36ddd5d5cd2bf23af081daafbbc8af49d Mon Sep 17 00:00:00 2001 From: Rubens Date: Sat, 24 May 2025 16:24:08 -0300 Subject: [PATCH 02/50] added sentAndReceived --- .../Service/BDK Service/BDKService.swift | 502 +++++++++--------- .../BDKSyncService/BDKSyncService.swift | 1 + .../EsploraServerSyncService.swift | 8 + 3 files changed, 268 insertions(+), 243 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 1d4ac2e5..38dd1e0b 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -10,6 +10,8 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() + + private let service: BDKSyncService = EsploraServerSyncService() private var balance: Balance? private var connection: Connection? @@ -69,20 +71,23 @@ private class BDKService { } func getBalance() throws -> Balance { - guard let wallet = self.wallet else { throw WalletError.walletNotFound } - let balance = wallet.balance() - return balance + try service.getBalance() + +// guard let wallet = self.wallet else { throw WalletError.walletNotFound } +// let balance = wallet.balance() +// return balance } func transactions() throws -> [CanonicalTx] { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let transactions = wallet.transactions() - let sortedTransactions = transactions.sorted { (tx1, tx2) in - return tx1.chainPosition.isBefore(tx2.chainPosition) - } - return sortedTransactions + try service.getTransactions() +// guard let wallet = self.wallet else { +// throw WalletError.walletNotFound +// } +// let transactions = wallet.transactions() +// let sortedTransactions = transactions.sorted { (tx1, tx2) in +// return tx1.chainPosition.isBefore(tx2.chainPosition) +// } +// return sortedTransactions } func listUnspent() throws -> [LocalOutput] { @@ -94,215 +99,221 @@ private class BDKService { } func createWallet(words: String?) throws { - self.connection = try Connection.createConnection() - guard let connection = connection else { - throw WalletError.dbNotFound - } - - let savedURL = try? keyClient.getEsploraURL() - let baseUrl = savedURL ?? network.url - - var words12: String - if let words = words, !words.isEmpty { - words12 = words - needsFullScan = true - } else { - let mnemonic = Mnemonic(wordCount: WordCount.words12) - words12 = mnemonic.description - needsFullScan = false - } - let mnemonic = try Mnemonic.fromString(mnemonic: words12) - let secretKey = DescriptorSecretKey( - network: network, - mnemonic: mnemonic, - password: nil - ) - let descriptor = Descriptor.newBip86( - secretKey: secretKey, - keychain: .external, - network: network - ) - let changeDescriptor = Descriptor.newBip86( - secretKey: secretKey, - keychain: .internal, - network: network - ) - let backupInfo = BackupInfo( - mnemonic: mnemonic.description, - descriptor: descriptor.toStringWithSecret(), - changeDescriptor: changeDescriptor.toStringWithSecret() - ) - - try keyClient.saveBackupInfo(backupInfo) - try keyClient.saveNetwork(self.network.description) - try keyClient.saveEsploraURL(baseUrl) - self.esploraURL = baseUrl - updateEsploraClient() - - let wallet = try Wallet( - descriptor: descriptor, - changeDescriptor: changeDescriptor, - network: network, - connection: connection - ) - self.wallet = wallet + try service.createWallet(params: words) + +// self.connection = try Connection.createConnection() +// guard let connection = connection else { +// throw WalletError.dbNotFound +// } +// +// let savedURL = try? keyClient.getEsploraURL() +// let baseUrl = savedURL ?? network.url +// +// var words12: String +// if let words = words, !words.isEmpty { +// words12 = words +// needsFullScan = true +// } else { +// let mnemonic = Mnemonic(wordCount: WordCount.words12) +// words12 = mnemonic.description +// needsFullScan = false +// } +// let mnemonic = try Mnemonic.fromString(mnemonic: words12) +// let secretKey = DescriptorSecretKey( +// network: network, +// mnemonic: mnemonic, +// password: nil +// ) +// let descriptor = Descriptor.newBip86( +// secretKey: secretKey, +// keychain: .external, +// network: network +// ) +// let changeDescriptor = Descriptor.newBip86( +// secretKey: secretKey, +// keychain: .internal, +// network: network +// ) +// let backupInfo = BackupInfo( +// mnemonic: mnemonic.description, +// descriptor: descriptor.toStringWithSecret(), +// changeDescriptor: changeDescriptor.toStringWithSecret() +// ) +// +// try keyClient.saveBackupInfo(backupInfo) +// try keyClient.saveNetwork(self.network.description) +// try keyClient.saveEsploraURL(baseUrl) +// self.esploraURL = baseUrl +// updateEsploraClient() +// +// let wallet = try Wallet( +// descriptor: descriptor, +// changeDescriptor: changeDescriptor, +// network: network, +// connection: connection +// ) +// self.wallet = wallet } func createWallet(descriptor: String?) throws { - self.connection = try Connection.createConnection() - guard let connection = connection else { - throw WalletError.dbNotFound - } - - let savedURL = try? keyClient.getEsploraURL() - let baseUrl = savedURL ?? network.url - - guard let descriptorString = descriptor, !descriptorString.isEmpty else { - throw WalletError.walletNotFound - } - - let descriptorStrings = descriptorString.components(separatedBy: "\n") - .map { $0.split(separator: "#").first?.trimmingCharacters(in: .whitespaces) ?? "" } - .filter { !$0.isEmpty } - let descriptor: Descriptor - let changeDescriptor: Descriptor - if descriptorStrings.count == 1 { - let parsedDescriptor = try Descriptor( - descriptor: descriptorStrings[0], - network: network - ) - let singleDescriptors = try parsedDescriptor.toSingleDescriptors() - guard singleDescriptors.count >= 2 else { - throw WalletError.walletNotFound - } - descriptor = singleDescriptors[0] - changeDescriptor = singleDescriptors[1] - } else if descriptorStrings.count == 2 { - descriptor = try Descriptor(descriptor: descriptorStrings[0], network: network) - changeDescriptor = try Descriptor(descriptor: descriptorStrings[1], network: network) - } else { - throw WalletError.walletNotFound - } - - let backupInfo = BackupInfo( - mnemonic: "", - descriptor: descriptor.toStringWithSecret(), - changeDescriptor: changeDescriptor.toStringWithSecret() - ) - - try keyClient.saveBackupInfo(backupInfo) - try keyClient.saveNetwork(self.network.description) - try keyClient.saveEsploraURL(baseUrl) - - let wallet = try Wallet( - descriptor: descriptor, - changeDescriptor: changeDescriptor, - network: network, - connection: connection - ) - self.wallet = wallet + try service.createWallet(params: descriptor) +// self.connection = try Connection.createConnection() +// guard let connection = connection else { +// throw WalletError.dbNotFound +// } +// +// let savedURL = try? keyClient.getEsploraURL() +// let baseUrl = savedURL ?? network.url +// +// guard let descriptorString = descriptor, !descriptorString.isEmpty else { +// throw WalletError.walletNotFound +// } +// +// let descriptorStrings = descriptorString.components(separatedBy: "\n") +// .map { $0.split(separator: "#").first?.trimmingCharacters(in: .whitespaces) ?? "" } +// .filter { !$0.isEmpty } +// let descriptor: Descriptor +// let changeDescriptor: Descriptor +// if descriptorStrings.count == 1 { +// let parsedDescriptor = try Descriptor( +// descriptor: descriptorStrings[0], +// network: network +// ) +// let singleDescriptors = try parsedDescriptor.toSingleDescriptors() +// guard singleDescriptors.count >= 2 else { +// throw WalletError.walletNotFound +// } +// descriptor = singleDescriptors[0] +// changeDescriptor = singleDescriptors[1] +// } else if descriptorStrings.count == 2 { +// descriptor = try Descriptor(descriptor: descriptorStrings[0], network: network) +// changeDescriptor = try Descriptor(descriptor: descriptorStrings[1], network: network) +// } else { +// throw WalletError.walletNotFound +// } +// +// let backupInfo = BackupInfo( +// mnemonic: "", +// descriptor: descriptor.toStringWithSecret(), +// changeDescriptor: changeDescriptor.toStringWithSecret() +// ) +// +// try keyClient.saveBackupInfo(backupInfo) +// try keyClient.saveNetwork(self.network.description) +// try keyClient.saveEsploraURL(baseUrl) +// +// let wallet = try Wallet( +// descriptor: descriptor, +// changeDescriptor: changeDescriptor, +// network: network, +// connection: connection +// ) +// self.wallet = wallet } func createWallet(xpub: String?) throws { - self.connection = try Connection.createConnection() - guard let connection = connection else { - throw WalletError.dbNotFound - } - - let savedURL = try? keyClient.getEsploraURL() - - let baseUrl = savedURL ?? network.url - - guard let xpubString = xpub, !xpubString.isEmpty else { - throw WalletError.walletNotFound - } - - let descriptorPublicKey = try DescriptorPublicKey.fromString(publicKey: xpubString) - let fingerprint = descriptorPublicKey.masterFingerprint() - let descriptor = Descriptor.newBip86Public( - publicKey: descriptorPublicKey, - fingerprint: fingerprint, - keychain: .external, - network: network - ) - let changeDescriptor = Descriptor.newBip86Public( - publicKey: descriptorPublicKey, - fingerprint: fingerprint, - keychain: .internal, - network: network - ) - - let backupInfo = BackupInfo( - mnemonic: "", - descriptor: descriptor.toStringWithSecret(), - changeDescriptor: changeDescriptor.toStringWithSecret() - ) - - try keyClient.saveBackupInfo(backupInfo) - try keyClient.saveNetwork(self.network.description) - try keyClient.saveEsploraURL(baseUrl) - self.esploraURL = baseUrl - updateEsploraClient() - - let wallet = try Wallet( - descriptor: descriptor, - changeDescriptor: changeDescriptor, - network: network, - connection: connection - ) - self.wallet = wallet + try service.createWallet(params: xpub) +// self.connection = try Connection.createConnection() +// guard let connection = connection else { +// throw WalletError.dbNotFound +// } +// +// let savedURL = try? keyClient.getEsploraURL() +// +// let baseUrl = savedURL ?? network.url +// +// guard let xpubString = xpub, !xpubString.isEmpty else { +// throw WalletError.walletNotFound +// } +// +// let descriptorPublicKey = try DescriptorPublicKey.fromString(publicKey: xpubString) +// let fingerprint = descriptorPublicKey.masterFingerprint() +// let descriptor = Descriptor.newBip86Public( +// publicKey: descriptorPublicKey, +// fingerprint: fingerprint, +// keychain: .external, +// network: network +// ) +// let changeDescriptor = Descriptor.newBip86Public( +// publicKey: descriptorPublicKey, +// fingerprint: fingerprint, +// keychain: .internal, +// network: network +// ) +// +// let backupInfo = BackupInfo( +// mnemonic: "", +// descriptor: descriptor.toStringWithSecret(), +// changeDescriptor: changeDescriptor.toStringWithSecret() +// ) +// +// try keyClient.saveBackupInfo(backupInfo) +// try keyClient.saveNetwork(self.network.description) +// try keyClient.saveEsploraURL(baseUrl) +// self.esploraURL = baseUrl +// updateEsploraClient() +// +// let wallet = try Wallet( +// descriptor: descriptor, +// changeDescriptor: changeDescriptor, +// network: network, +// connection: connection +// ) +// self.wallet = wallet } - private func loadWallet(descriptor: Descriptor, changeDescriptor: Descriptor) throws { - let documentsDirectoryURL = URL.documentsDirectory - let walletDataDirectoryURL = documentsDirectoryURL.appendingPathComponent("wallet_data") - try FileManager.default.ensureDirectoryExists(at: walletDataDirectoryURL) - try FileManager.default.removeOldFlatFileIfNeeded(at: documentsDirectoryURL) - let persistenceBackendPath = walletDataDirectoryURL.appendingPathComponent("wallet.sqlite") - .path - let connection = try Connection(path: persistenceBackendPath) - self.connection = connection - let wallet = try Wallet.load( - descriptor: descriptor, - changeDescriptor: changeDescriptor, - connection: connection - ) - self.wallet = wallet - } +// private func loadWallet(descriptor: Descriptor, changeDescriptor: Descriptor) throws { +// let documentsDirectoryURL = URL.documentsDirectory +// let walletDataDirectoryURL = documentsDirectoryURL.appendingPathComponent("wallet_data") +// try FileManager.default.ensureDirectoryExists(at: walletDataDirectoryURL) +// try FileManager.default.removeOldFlatFileIfNeeded(at: documentsDirectoryURL) +// let persistenceBackendPath = walletDataDirectoryURL.appendingPathComponent("wallet.sqlite") +// .path +// let connection = try Connection(path: persistenceBackendPath) +// self.connection = connection +// let wallet = try Wallet.load( +// descriptor: descriptor, +// changeDescriptor: changeDescriptor, +// connection: connection +// ) +// self.wallet = wallet +// } func loadWalletFromBackup() throws { - let backupInfo = try keyClient.getBackupInfo() - let descriptor = try Descriptor(descriptor: backupInfo.descriptor, network: self.network) - let changeDescriptor = try Descriptor( - descriptor: backupInfo.changeDescriptor, - network: self.network - ) - try self.loadWallet(descriptor: descriptor, changeDescriptor: changeDescriptor) + try service.loadWallet() +// let backupInfo = try keyClient.getBackupInfo() +// let descriptor = try Descriptor(descriptor: backupInfo.descriptor, network: self.network) +// let changeDescriptor = try Descriptor( +// descriptor: backupInfo.changeDescriptor, +// network: self.network +// ) +// try self.loadWallet(descriptor: descriptor, changeDescriptor: changeDescriptor) } func deleteWallet() throws { - let savedURL = try? keyClient.getEsploraURL() - let savedNetwork = try? keyClient.getNetwork() - - if let bundleID = Bundle.main.bundleIdentifier { - UserDefaults.standard.removePersistentDomain(forName: bundleID) - } - - try self.keyClient.deleteBackupInfo() - - let documentsDirectoryURL = URL.documentsDirectory - let walletDataDirectoryURL = documentsDirectoryURL.appendingPathComponent("wallet_data") - if FileManager.default.fileExists(atPath: walletDataDirectoryURL.path) { - try FileManager.default.removeItem(at: walletDataDirectoryURL) - } - - if let savedURL = savedURL { - try keyClient.saveEsploraURL(savedURL) - } - if let savedNetwork = savedNetwork { - try keyClient.saveNetwork(savedNetwork) - } - + try service.deleteWallet() +// let savedURL = try? keyClient.getEsploraURL() +// let savedNetwork = try? keyClient.getNetwork() +// +// if let bundleID = Bundle.main.bundleIdentifier { +// UserDefaults.standard.removePersistentDomain(forName: bundleID) +// } +// +// try self.keyClient.deleteBackupInfo() +// +// let documentsDirectoryURL = URL.documentsDirectory +// let walletDataDirectoryURL = documentsDirectoryURL.appendingPathComponent("wallet_data") +// if FileManager.default.fileExists(atPath: walletDataDirectoryURL.path) { +// try FileManager.default.removeItem(at: walletDataDirectoryURL) +// } +// +// if let savedURL = savedURL { +// try keyClient.saveEsploraURL(savedURL) +// } +// if let savedNetwork = savedNetwork { +// try keyClient.saveNetwork(savedNetwork) +// } +// needsFullScan = true } @@ -353,40 +364,44 @@ private class BDKService { } func syncWithInspector(inspector: SyncScriptInspector) async throws { - guard let wallet = self.wallet else { throw WalletError.walletNotFound } - let esploraClient = self.esploraClient - let syncRequest = try wallet.startSyncWithRevealedSpks() - .inspectSpks(inspector: inspector) - .build() - let update = try esploraClient.sync( - request: syncRequest, - parallelRequests: UInt64(5) - ) - let _ = try wallet.applyUpdate(update: update) - guard let connection = self.connection else { - throw WalletError.dbNotFound - } - let _ = try wallet.persist(connection: connection) + try await service.startSync(progress: inspector) + +// guard let wallet = self.wallet else { throw WalletError.walletNotFound } +// let esploraClient = self.esploraClient +// let syncRequest = try wallet.startSyncWithRevealedSpks() +// .inspectSpks(inspector: inspector) +// .build() +// let update = try esploraClient.sync( +// request: syncRequest, +// parallelRequests: UInt64(5) +// ) +// let _ = try wallet.applyUpdate(update: update) +// guard let connection = self.connection else { +// throw WalletError.dbNotFound +// } +// let _ = try wallet.persist(connection: connection) } func fullScanWithInspector(inspector: FullScanScriptInspector) async throws { - guard let wallet = self.wallet else { throw WalletError.walletNotFound } - let esploraClient = esploraClient - let fullScanRequest = try wallet.startFullScan() - .inspectSpksForAllKeychains(inspector: inspector) - .build() - let update = try esploraClient.fullScan( - request: fullScanRequest, - // using https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#address-gap-limit - stopGap: UInt64(20), - // using https://github.com/bitcoindevkit/bdk/blob/master/example-crates/example_wallet_esplora_blocking/src/main.rs - parallelRequests: UInt64(5) - ) - let _ = try wallet.applyUpdate(update: update) - guard let connection = self.connection else { - throw WalletError.dbNotFound - } - let _ = try wallet.persist(connection: connection) + try await service.startFullScan(progress: inspector) + +// guard let wallet = self.wallet else { throw WalletError.walletNotFound } +// let esploraClient = esploraClient +// let fullScanRequest = try wallet.startFullScan() +// .inspectSpksForAllKeychains(inspector: inspector) +// .build() +// let update = try esploraClient.fullScan( +// request: fullScanRequest, +// // using https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#address-gap-limit +// stopGap: UInt64(20), +// // using https://github.com/bitcoindevkit/bdk/blob/master/example-crates/example_wallet_esplora_blocking/src/main.rs +// parallelRequests: UInt64(5) +// ) +// let _ = try wallet.applyUpdate(update: update) +// guard let connection = self.connection else { +// throw WalletError.dbNotFound +// } +// let _ = try wallet.persist(connection: connection) } func calculateFee(tx: Transaction) throws -> Amount { @@ -406,11 +421,12 @@ private class BDKService { } func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let values = wallet.sentAndReceived(tx: tx) - return values + try service.sentAndReceived(tx: tx) +// guard let wallet = self.wallet else { +// throw WalletError.walletNotFound +// } +// let values = wallet.sentAndReceived(tx: tx) +// return values } } diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 6d0a3597..bb8ac960 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -25,6 +25,7 @@ protocol BDKSyncService { func getTransactions() throws -> [CanonicalTx] func getBalance() throws -> Balance + func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues } extension BDKSyncService { diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift index 57e82459..ced9485e 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift @@ -111,4 +111,12 @@ final class EsploraServerSyncService: BDKSyncService { let balance = wallet.balance() return balance } + + func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let values = wallet.sentAndReceived(tx: tx) + return values + } } From 572942f3f44803c15bd041dc902527d63c4b4e59 Mon Sep 17 00:00:00 2001 From: Rubens Date: Sat, 24 May 2025 16:47:03 -0300 Subject: [PATCH 03/50] implemented new functions --- .../Service/BDK Service/BDKService.swift | 110 +++++++++--------- .../BDKSyncService/BDKSyncService.swift | 6 + .../EsploraServerSyncService.swift | 81 +++++++++++++ 3 files changed, 145 insertions(+), 52 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 38dd1e0b..0a5f9c4b 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -59,15 +59,16 @@ private class BDKService { } func getAddress() throws -> String { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - guard let connection = self.connection else { - throw WalletError.dbNotFound - } - let addressInfo = wallet.revealNextAddress(keychain: .external) - let _ = try wallet.persist(connection: connection) - return addressInfo.address.description + try service.getAddress() +// guard let wallet = self.wallet else { +// throw WalletError.walletNotFound +// } +// guard let connection = self.connection else { +// throw WalletError.dbNotFound +// } +// let addressInfo = wallet.revealNextAddress(keychain: .external) +// let _ = try wallet.persist(connection: connection) +// return addressInfo.address.description } func getBalance() throws -> Balance { @@ -91,11 +92,12 @@ private class BDKService { } func listUnspent() throws -> [LocalOutput] { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let localOutputs = wallet.listUnspent() - return localOutputs + try service.listUnspent() +// guard let wallet = self.wallet else { +// throw WalletError.walletNotFound +// } +// let localOutputs = wallet.listUnspent() +// return localOutputs } func createWallet(words: String?) throws { @@ -327,41 +329,43 @@ private class BDKService { amount: UInt64, feeRate: UInt64 ) async throws { - let psbt = try buildTransaction( - address: address, - amount: amount, - feeRate: feeRate - ) - try signAndBroadcast(psbt: psbt) + try await service.send(address: address, amount: amount, feeRate: feeRate) +// let psbt = try buildTransaction( +// address: address, +// amount: amount, +// feeRate: feeRate +// ) +// try signAndBroadcast(psbt: psbt) } func buildTransaction(address: String, amount: UInt64, feeRate: UInt64) throws -> Psbt { - guard let wallet = self.wallet else { throw WalletError.walletNotFound } - let script = try Address(address: address, network: self.network) - .scriptPubkey() - let txBuilder = try TxBuilder() - .addRecipient( - script: script, - amount: Amount.fromSat(satoshi: amount) - ) - .feeRate(feeRate: FeeRate.fromSatPerVb(satVb: feeRate)) - .finish(wallet: wallet) - return txBuilder + try service.buildTransaction(address: address, amount: amount, feeRate: feeRate) +// guard let wallet = self.wallet else { throw WalletError.walletNotFound } +// let script = try Address(address: address, network: self.network) +// .scriptPubkey() +// let txBuilder = try TxBuilder() +// .addRecipient( +// script: script, +// amount: Amount.fromSat(satoshi: amount) +// ) +// .feeRate(feeRate: FeeRate.fromSatPerVb(satVb: feeRate)) +// .finish(wallet: wallet) +// return txBuilder } - private func signAndBroadcast(psbt: Psbt) throws { - guard let wallet = self.wallet else { throw WalletError.walletNotFound } - let isSigned = try wallet.sign(psbt: psbt) - if isSigned { - let transaction = try psbt.extractTx() - let client = self.esploraClient - try client.broadcast(transaction: transaction) - } else { - throw WalletError.notSigned - } - } +// private func signAndBroadcast(psbt: Psbt) throws { +// guard let wallet = self.wallet else { throw WalletError.walletNotFound } +// let isSigned = try wallet.sign(psbt: psbt) +// if isSigned { +// let transaction = try psbt.extractTx() +// let client = self.esploraClient +// try client.broadcast(transaction: transaction) +// } else { +// throw WalletError.notSigned +// } +// } func syncWithInspector(inspector: SyncScriptInspector) async throws { try await service.startSync(progress: inspector) @@ -405,19 +409,21 @@ private class BDKService { } func calculateFee(tx: Transaction) throws -> Amount { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let fee = try wallet.calculateFee(tx: tx) - return fee + try service.calculateFee(tx: tx) +// guard let wallet = self.wallet else { +// throw WalletError.walletNotFound +// } +// let fee = try wallet.calculateFee(tx: tx) +// return fee } func calculateFeeRate(tx: Transaction) throws -> UInt64 { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let feeRate = try wallet.calculateFeeRate(tx: tx) - return feeRate.toSatPerVbCeil() + try service.calculateFeeRate(tx: tx) +// guard let wallet = self.wallet else { +// throw WalletError.walletNotFound +// } +// let feeRate = try wallet.calculateFeeRate(tx: tx) +// return feeRate.toSatPerVbCeil() } func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index bb8ac960..39e0e5ce 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -26,6 +26,12 @@ protocol BDKSyncService { func getTransactions() throws -> [CanonicalTx] func getBalance() throws -> Balance func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues + func calculateFeeRate(tx: Transaction) throws -> UInt64 + func calculateFee(tx: Transaction) throws -> Amount + func buildTransaction(address: String, amount: UInt64, feeRate: UInt64) throws -> Psbt + func send(address: String, amount: UInt64, feeRate: UInt64) async throws + func listUnspent() throws -> [LocalOutput] + func getAddress() throws -> String } extension BDKSyncService { diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift index ced9485e..00ebc144 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift @@ -119,4 +119,85 @@ final class EsploraServerSyncService: BDKSyncService { let values = wallet.sentAndReceived(tx: tx) return values } + + func calculateFeeRate(tx: Transaction) throws -> UInt64 { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let feeRate = try wallet.calculateFeeRate(tx: tx) + return feeRate.toSatPerVbCeil() + } + + func calculateFee(tx: Transaction) throws -> Amount { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let fee = try wallet.calculateFee(tx: tx) + return fee + } + + func buildTransaction( + address: String, + amount: UInt64, + feeRate: UInt64 + ) throws -> Psbt { + guard let wallet = self.wallet else { throw WalletError.walletNotFound } + let script = try Address(address: address, network: self.network) + .scriptPubkey() + let txBuilder = try TxBuilder() + .addRecipient( + script: script, + amount: Amount.fromSat(satoshi: amount) + ) + .feeRate(feeRate: FeeRate.fromSatPerVb(satVb: feeRate)) + .finish(wallet: wallet) + return txBuilder + } + + func send( + address: String, + amount: UInt64, + feeRate: UInt64 + ) async throws { + let psbt = try buildTransaction( + address: address, + amount: amount, + feeRate: feeRate + ) + try signAndBroadcast(psbt: psbt) + } + + func listUnspent() throws -> [LocalOutput] { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let localOutputs = wallet.listUnspent() + return localOutputs + } + + func getAddress() throws -> String { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + guard let connection = self.connection else { + throw WalletError.dbNotFound + } + let addressInfo = wallet.revealNextAddress(keychain: .external) + let _ = try wallet.persist(connection: connection) + return addressInfo.address.description + } + + // MARK: - Private + + private func signAndBroadcast(psbt: Psbt) throws { + guard let wallet = self.wallet else { throw WalletError.walletNotFound } + let isSigned = try wallet.sign(psbt: psbt) + if isSigned { + let transaction = try psbt.extractTx() + let client = self.esploraClient + try client.broadcast(transaction: transaction) + } else { + throw WalletError.notSigned + } + } } From 01be1b9814b30aab8cbc95bb95baf1e42e236aab Mon Sep 17 00:00:00 2001 From: Rubens Date: Sat, 24 May 2025 16:50:03 -0300 Subject: [PATCH 04/50] fix secret --- .../Service/BDKSyncService/BDKSyncService.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 39e0e5ce..cf1c1c85 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -109,8 +109,8 @@ extension BDKSyncService { return .init( mnemonic: "", - descriptor: descriptor.description, - changeDescriptor: changeDescriptor.description + descriptor: descriptor.toStringWithSecret(), + changeDescriptor: changeDescriptor.toStringWithSecret() ) } @@ -134,8 +134,8 @@ extension BDKSyncService { ) return .init( mnemonic: mnemonic.description, - descriptor: descriptor.description, - changeDescriptor: changeDescriptor.description + descriptor: descriptor.toStringWithSecret(), + changeDescriptor: changeDescriptor.toStringWithSecret() ) } From 899457e3b3fa65518d6093a2eac9cd530be83a30 Mon Sep 17 00:00:00 2001 From: Rubens Date: Sat, 24 May 2025 16:55:45 -0300 Subject: [PATCH 05/50] fix create new wallet --- .../Service/BDKSyncService/BDKSyncService.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index cf1c1c85..a3dc4db7 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -114,7 +114,8 @@ extension BDKSyncService { ) } - guard let mnemonic = try? Mnemonic.fromString(mnemonic: params) else { + let words = !params.isEmpty ? params : Mnemonic(wordCount: WordCount.words12).description + guard let mnemonic = try? Mnemonic.fromString(mnemonic: words) else { throw AppError.generic(message: "Invalid mnemonic") } let secretKey = DescriptorSecretKey( From 8b66948ccc5a7a48883bac041fb6dc0d4bde677d Mon Sep 17 00:00:00 2001 From: Rubens Date: Sat, 24 May 2025 16:58:44 -0300 Subject: [PATCH 06/50] chore: removed comments --- BDKSwiftExampleWallet/Model/BackupInfo.swift | 2 +- .../Service/BDK Service/BDKService.swift | 301 ------------------ .../BDKSyncService/BDKSyncService.swift | 2 - 3 files changed, 1 insertion(+), 304 deletions(-) diff --git a/BDKSwiftExampleWallet/Model/BackupInfo.swift b/BDKSwiftExampleWallet/Model/BackupInfo.swift index 042bcc3d..1acdce1c 100644 --- a/BDKSwiftExampleWallet/Model/BackupInfo.swift +++ b/BDKSwiftExampleWallet/Model/BackupInfo.swift @@ -12,7 +12,7 @@ struct BackupInfo: Codable, Equatable { var descriptor: String var changeDescriptor: String - init(mnemonic: String, descriptor: String, changeDescriptor: String) { + init(mnemonic: String = "", descriptor: String, changeDescriptor: String) { self.mnemonic = mnemonic self.descriptor = descriptor self.changeDescriptor = changeDescriptor diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 0a5f9c4b..10bd1f7b 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -60,262 +60,38 @@ private class BDKService { func getAddress() throws -> String { try service.getAddress() -// guard let wallet = self.wallet else { -// throw WalletError.walletNotFound -// } -// guard let connection = self.connection else { -// throw WalletError.dbNotFound -// } -// let addressInfo = wallet.revealNextAddress(keychain: .external) -// let _ = try wallet.persist(connection: connection) -// return addressInfo.address.description } func getBalance() throws -> Balance { try service.getBalance() - -// guard let wallet = self.wallet else { throw WalletError.walletNotFound } -// let balance = wallet.balance() -// return balance } func transactions() throws -> [CanonicalTx] { try service.getTransactions() -// guard let wallet = self.wallet else { -// throw WalletError.walletNotFound -// } -// let transactions = wallet.transactions() -// let sortedTransactions = transactions.sorted { (tx1, tx2) in -// return tx1.chainPosition.isBefore(tx2.chainPosition) -// } -// return sortedTransactions } func listUnspent() throws -> [LocalOutput] { try service.listUnspent() -// guard let wallet = self.wallet else { -// throw WalletError.walletNotFound -// } -// let localOutputs = wallet.listUnspent() -// return localOutputs } func createWallet(words: String?) throws { try service.createWallet(params: words) - -// self.connection = try Connection.createConnection() -// guard let connection = connection else { -// throw WalletError.dbNotFound -// } -// -// let savedURL = try? keyClient.getEsploraURL() -// let baseUrl = savedURL ?? network.url -// -// var words12: String -// if let words = words, !words.isEmpty { -// words12 = words -// needsFullScan = true -// } else { -// let mnemonic = Mnemonic(wordCount: WordCount.words12) -// words12 = mnemonic.description -// needsFullScan = false -// } -// let mnemonic = try Mnemonic.fromString(mnemonic: words12) -// let secretKey = DescriptorSecretKey( -// network: network, -// mnemonic: mnemonic, -// password: nil -// ) -// let descriptor = Descriptor.newBip86( -// secretKey: secretKey, -// keychain: .external, -// network: network -// ) -// let changeDescriptor = Descriptor.newBip86( -// secretKey: secretKey, -// keychain: .internal, -// network: network -// ) -// let backupInfo = BackupInfo( -// mnemonic: mnemonic.description, -// descriptor: descriptor.toStringWithSecret(), -// changeDescriptor: changeDescriptor.toStringWithSecret() -// ) -// -// try keyClient.saveBackupInfo(backupInfo) -// try keyClient.saveNetwork(self.network.description) -// try keyClient.saveEsploraURL(baseUrl) -// self.esploraURL = baseUrl -// updateEsploraClient() -// -// let wallet = try Wallet( -// descriptor: descriptor, -// changeDescriptor: changeDescriptor, -// network: network, -// connection: connection -// ) -// self.wallet = wallet } func createWallet(descriptor: String?) throws { try service.createWallet(params: descriptor) -// self.connection = try Connection.createConnection() -// guard let connection = connection else { -// throw WalletError.dbNotFound -// } -// -// let savedURL = try? keyClient.getEsploraURL() -// let baseUrl = savedURL ?? network.url -// -// guard let descriptorString = descriptor, !descriptorString.isEmpty else { -// throw WalletError.walletNotFound -// } -// -// let descriptorStrings = descriptorString.components(separatedBy: "\n") -// .map { $0.split(separator: "#").first?.trimmingCharacters(in: .whitespaces) ?? "" } -// .filter { !$0.isEmpty } -// let descriptor: Descriptor -// let changeDescriptor: Descriptor -// if descriptorStrings.count == 1 { -// let parsedDescriptor = try Descriptor( -// descriptor: descriptorStrings[0], -// network: network -// ) -// let singleDescriptors = try parsedDescriptor.toSingleDescriptors() -// guard singleDescriptors.count >= 2 else { -// throw WalletError.walletNotFound -// } -// descriptor = singleDescriptors[0] -// changeDescriptor = singleDescriptors[1] -// } else if descriptorStrings.count == 2 { -// descriptor = try Descriptor(descriptor: descriptorStrings[0], network: network) -// changeDescriptor = try Descriptor(descriptor: descriptorStrings[1], network: network) -// } else { -// throw WalletError.walletNotFound -// } -// -// let backupInfo = BackupInfo( -// mnemonic: "", -// descriptor: descriptor.toStringWithSecret(), -// changeDescriptor: changeDescriptor.toStringWithSecret() -// ) -// -// try keyClient.saveBackupInfo(backupInfo) -// try keyClient.saveNetwork(self.network.description) -// try keyClient.saveEsploraURL(baseUrl) -// -// let wallet = try Wallet( -// descriptor: descriptor, -// changeDescriptor: changeDescriptor, -// network: network, -// connection: connection -// ) -// self.wallet = wallet } func createWallet(xpub: String?) throws { try service.createWallet(params: xpub) -// self.connection = try Connection.createConnection() -// guard let connection = connection else { -// throw WalletError.dbNotFound -// } -// -// let savedURL = try? keyClient.getEsploraURL() -// -// let baseUrl = savedURL ?? network.url -// -// guard let xpubString = xpub, !xpubString.isEmpty else { -// throw WalletError.walletNotFound -// } -// -// let descriptorPublicKey = try DescriptorPublicKey.fromString(publicKey: xpubString) -// let fingerprint = descriptorPublicKey.masterFingerprint() -// let descriptor = Descriptor.newBip86Public( -// publicKey: descriptorPublicKey, -// fingerprint: fingerprint, -// keychain: .external, -// network: network -// ) -// let changeDescriptor = Descriptor.newBip86Public( -// publicKey: descriptorPublicKey, -// fingerprint: fingerprint, -// keychain: .internal, -// network: network -// ) -// -// let backupInfo = BackupInfo( -// mnemonic: "", -// descriptor: descriptor.toStringWithSecret(), -// changeDescriptor: changeDescriptor.toStringWithSecret() -// ) -// -// try keyClient.saveBackupInfo(backupInfo) -// try keyClient.saveNetwork(self.network.description) -// try keyClient.saveEsploraURL(baseUrl) -// self.esploraURL = baseUrl -// updateEsploraClient() -// -// let wallet = try Wallet( -// descriptor: descriptor, -// changeDescriptor: changeDescriptor, -// network: network, -// connection: connection -// ) -// self.wallet = wallet } -// private func loadWallet(descriptor: Descriptor, changeDescriptor: Descriptor) throws { -// let documentsDirectoryURL = URL.documentsDirectory -// let walletDataDirectoryURL = documentsDirectoryURL.appendingPathComponent("wallet_data") -// try FileManager.default.ensureDirectoryExists(at: walletDataDirectoryURL) -// try FileManager.default.removeOldFlatFileIfNeeded(at: documentsDirectoryURL) -// let persistenceBackendPath = walletDataDirectoryURL.appendingPathComponent("wallet.sqlite") -// .path -// let connection = try Connection(path: persistenceBackendPath) -// self.connection = connection -// let wallet = try Wallet.load( -// descriptor: descriptor, -// changeDescriptor: changeDescriptor, -// connection: connection -// ) -// self.wallet = wallet -// } - func loadWalletFromBackup() throws { try service.loadWallet() -// let backupInfo = try keyClient.getBackupInfo() -// let descriptor = try Descriptor(descriptor: backupInfo.descriptor, network: self.network) -// let changeDescriptor = try Descriptor( -// descriptor: backupInfo.changeDescriptor, -// network: self.network -// ) -// try self.loadWallet(descriptor: descriptor, changeDescriptor: changeDescriptor) } func deleteWallet() throws { try service.deleteWallet() -// let savedURL = try? keyClient.getEsploraURL() -// let savedNetwork = try? keyClient.getNetwork() -// -// if let bundleID = Bundle.main.bundleIdentifier { -// UserDefaults.standard.removePersistentDomain(forName: bundleID) -// } -// -// try self.keyClient.deleteBackupInfo() -// -// let documentsDirectoryURL = URL.documentsDirectory -// let walletDataDirectoryURL = documentsDirectoryURL.appendingPathComponent("wallet_data") -// if FileManager.default.fileExists(atPath: walletDataDirectoryURL.path) { -// try FileManager.default.removeItem(at: walletDataDirectoryURL) -// } -// -// if let savedURL = savedURL { -// try keyClient.saveEsploraURL(savedURL) -// } -// if let savedNetwork = savedNetwork { -// try keyClient.saveNetwork(savedNetwork) -// } -// needsFullScan = true } @@ -330,109 +106,32 @@ private class BDKService { feeRate: UInt64 ) async throws { try await service.send(address: address, amount: amount, feeRate: feeRate) -// let psbt = try buildTransaction( -// address: address, -// amount: amount, -// feeRate: feeRate -// ) -// try signAndBroadcast(psbt: psbt) } func buildTransaction(address: String, amount: UInt64, feeRate: UInt64) throws -> Psbt { try service.buildTransaction(address: address, amount: amount, feeRate: feeRate) -// guard let wallet = self.wallet else { throw WalletError.walletNotFound } -// let script = try Address(address: address, network: self.network) -// .scriptPubkey() -// let txBuilder = try TxBuilder() -// .addRecipient( -// script: script, -// amount: Amount.fromSat(satoshi: amount) -// ) -// .feeRate(feeRate: FeeRate.fromSatPerVb(satVb: feeRate)) -// .finish(wallet: wallet) -// return txBuilder } -// private func signAndBroadcast(psbt: Psbt) throws { -// guard let wallet = self.wallet else { throw WalletError.walletNotFound } -// let isSigned = try wallet.sign(psbt: psbt) -// if isSigned { -// let transaction = try psbt.extractTx() -// let client = self.esploraClient -// try client.broadcast(transaction: transaction) -// } else { -// throw WalletError.notSigned -// } -// } - func syncWithInspector(inspector: SyncScriptInspector) async throws { try await service.startSync(progress: inspector) - -// guard let wallet = self.wallet else { throw WalletError.walletNotFound } -// let esploraClient = self.esploraClient -// let syncRequest = try wallet.startSyncWithRevealedSpks() -// .inspectSpks(inspector: inspector) -// .build() -// let update = try esploraClient.sync( -// request: syncRequest, -// parallelRequests: UInt64(5) -// ) -// let _ = try wallet.applyUpdate(update: update) -// guard let connection = self.connection else { -// throw WalletError.dbNotFound -// } -// let _ = try wallet.persist(connection: connection) } func fullScanWithInspector(inspector: FullScanScriptInspector) async throws { try await service.startFullScan(progress: inspector) - -// guard let wallet = self.wallet else { throw WalletError.walletNotFound } -// let esploraClient = esploraClient -// let fullScanRequest = try wallet.startFullScan() -// .inspectSpksForAllKeychains(inspector: inspector) -// .build() -// let update = try esploraClient.fullScan( -// request: fullScanRequest, -// // using https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#address-gap-limit -// stopGap: UInt64(20), -// // using https://github.com/bitcoindevkit/bdk/blob/master/example-crates/example_wallet_esplora_blocking/src/main.rs -// parallelRequests: UInt64(5) -// ) -// let _ = try wallet.applyUpdate(update: update) -// guard let connection = self.connection else { -// throw WalletError.dbNotFound -// } -// let _ = try wallet.persist(connection: connection) } func calculateFee(tx: Transaction) throws -> Amount { try service.calculateFee(tx: tx) -// guard let wallet = self.wallet else { -// throw WalletError.walletNotFound -// } -// let fee = try wallet.calculateFee(tx: tx) -// return fee } func calculateFeeRate(tx: Transaction) throws -> UInt64 { try service.calculateFeeRate(tx: tx) -// guard let wallet = self.wallet else { -// throw WalletError.walletNotFound -// } -// let feeRate = try wallet.calculateFeeRate(tx: tx) -// return feeRate.toSatPerVbCeil() } func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { try service.sentAndReceived(tx: tx) -// guard let wallet = self.wallet else { -// throw WalletError.walletNotFound -// } -// let values = wallet.sentAndReceived(tx: tx) -// return values } } diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index a3dc4db7..063e43bc 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -75,7 +75,6 @@ extension BDKSyncService { network: network ) return .init( - mnemonic: "", descriptor: descriptor.description, changeDescriptor: changeDescriptor.description ) @@ -108,7 +107,6 @@ extension BDKSyncService { } return .init( - mnemonic: "", descriptor: descriptor.toStringWithSecret(), changeDescriptor: changeDescriptor.toStringWithSecret() ) From ebe05e93deea24b9769d00561dcabe26b83d98cb Mon Sep 17 00:00:00 2001 From: Rubens Date: Sat, 24 May 2025 17:04:06 -0300 Subject: [PATCH 07/50] added kyoto structure --- .../project.pbxproj | 12 ++- .../Service/BDK Service/BDKService.swift | 2 +- .../BDKSyncService/BDKSyncService.swift | 4 + ...SyncService.swift => EsploraService.swift} | 4 +- .../Service/BDKSyncService/KyotoService.swift | 85 +++++++++++++++++++ 5 files changed, 100 insertions(+), 7 deletions(-) rename BDKSwiftExampleWallet/Service/BDKSyncService/{EsploraServerSyncService.swift => EsploraService.swift} (98%) create mode 100644 BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift diff --git a/BDKSwiftExampleWallet.xcodeproj/project.pbxproj b/BDKSwiftExampleWallet.xcodeproj/project.pbxproj index 83c20ce0..482bb5ee 100644 --- a/BDKSwiftExampleWallet.xcodeproj/project.pbxproj +++ b/BDKSwiftExampleWallet.xcodeproj/project.pbxproj @@ -14,7 +14,8 @@ 77F0FDC92DA9A93D00B30E4F /* Connection+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F0FDC82DA9A93700B30E4F /* Connection+Extensions.swift */; }; 77F6A9E32DE247B2003568F0 /* BDKSyncService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E22DE247AD003568F0 /* BDKSyncService.swift */; }; 77F6A9E52DE24841003568F0 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E42DE24837003568F0 /* URL+Extensions.swift */; }; - 77F6A9E72DE248A2003568F0 /* EsploraServerSyncService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E62DE248A1003568F0 /* EsploraServerSyncService.swift */; }; + 77F6A9E72DE248A2003568F0 /* EsploraService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E62DE248A1003568F0 /* EsploraService.swift */; }; + 77F6A9E92DE25C9C003568F0 /* KyotoService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E82DE25C98003568F0 /* KyotoService.swift */; }; A733D6D02A81113000F333B4 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = A733D6CF2A81113000F333B4 /* Localizable.xcstrings */; }; A73F7A362A3B778E00B87FC6 /* Int+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A73F7A352A3B778E00B87FC6 /* Int+Extensions.swift */; }; AE0C30F72A804A2D008F1EAE /* TransactionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE0C30F62A804A2D008F1EAE /* TransactionListView.swift */; }; @@ -122,7 +123,8 @@ 77F0FDC82DA9A93700B30E4F /* Connection+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Extensions.swift"; sourceTree = ""; }; 77F6A9E22DE247AD003568F0 /* BDKSyncService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BDKSyncService.swift; sourceTree = ""; }; 77F6A9E42DE24837003568F0 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; - 77F6A9E62DE248A1003568F0 /* EsploraServerSyncService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EsploraServerSyncService.swift; sourceTree = ""; }; + 77F6A9E62DE248A1003568F0 /* EsploraService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EsploraService.swift; sourceTree = ""; }; + 77F6A9E82DE25C98003568F0 /* KyotoService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KyotoService.swift; sourceTree = ""; }; A733D6CF2A81113000F333B4 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; A73F7A352A3B778E00B87FC6 /* Int+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Extensions.swift"; sourceTree = ""; }; AE0C30F62A804A2D008F1EAE /* TransactionListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionListView.swift; sourceTree = ""; }; @@ -255,7 +257,8 @@ 77F6A9E12DE247A3003568F0 /* BDKSyncService */ = { isa = PBXGroup; children = ( - 77F6A9E62DE248A1003568F0 /* EsploraServerSyncService.swift */, + 77F6A9E82DE25C98003568F0 /* KyotoService.swift */, + 77F6A9E62DE248A1003568F0 /* EsploraService.swift */, 77F6A9E22DE247AD003568F0 /* BDKSyncService.swift */, ); path = BDKSyncService; @@ -717,6 +720,7 @@ 774586B52DB7B2BC00A631E1 /* BalanceView.swift in Sources */, AEB905C32A7EEBF000CD0337 /* BackupInfo.swift in Sources */, AE783A072AB4F7C7005F0CBA /* FeeView.swift in Sources */, + 77F6A9E92DE25C9C003568F0 /* KyotoService.swift in Sources */, AE2B8C1D2A9678C900815B2F /* FeeService.swift in Sources */, AE8D001C2D19F1760029C4C9 /* UIScreen+Extensions.swift in Sources */, AEC2CF5A2ABFBA19008065E4 /* BuildTransactionViewModel.swift in Sources */, @@ -775,7 +779,7 @@ AE0C30F92A804B65008F1EAE /* OnboardingViewModel.swift in Sources */, AE3902A42A3B4CD900BEC318 /* HomeView.swift in Sources */, AE0C30FD2A804BC1008F1EAE /* ReceiveViewModel.swift in Sources */, - 77F6A9E72DE248A2003568F0 /* EsploraServerSyncService.swift in Sources */, + 77F6A9E72DE248A2003568F0 /* EsploraService.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 10bd1f7b..e14a96ac 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -11,7 +11,7 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() - private let service: BDKSyncService = EsploraServerSyncService() + private let service: BDKSyncService = EsploraService() private var balance: Balance? private var connection: Connection? diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 063e43bc..14b50578 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -182,6 +182,10 @@ extension BDKSyncService { // Optional implementation } + func updateNetwork(network: Network) { + // Optional implementation + } + // MARK: - Private private func isDescriptor(_ param: String) -> Bool { diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift similarity index 98% rename from BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift rename to BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index 00ebc144..f3e1d19d 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraServerSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -8,9 +8,9 @@ import BitcoinDevKit import Foundation -final class EsploraServerSyncService: BDKSyncService { +final class EsploraService: BDKSyncService { - static let shared = EsploraServerSyncService() + static let shared = EsploraService() var connection: Connection? var keyClient: KeyClient diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift new file mode 100644 index 00000000..7f1f1515 --- /dev/null +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -0,0 +1,85 @@ +// +// KyotoService.swift +// BDKSwiftExampleWallet +// +// Created by Rubens Machion on 24/05/25. +// + +import BitcoinDevKit +import Foundation + +final class KyotoService: BDKSyncService { + + static let shared = KyotoService() + + var connection: Connection? + var keyClient: KeyClient + var network: Network + var wallet: Wallet? + + init( + keyClient: KeyClient = .live, + network: Network = .signet, + connection: Connection? = nil + ) { + self.connection = connection + self.keyClient = keyClient + self.network = network + } + + func createWallet(params: String?) throws { + + } + + func loadWallet() throws { + + } + + func deleteWallet() throws { + + } + + func startSync(progress: any SyncScriptInspector) async throws { + + } + + func startFullScan(progress: any FullScanScriptInspector) async throws { + + } + + func getTransactions() throws -> [CanonicalTx] { + [] + } + + func getBalance() throws -> Balance { + .mock + } + + func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { + .mock + } + + func calculateFeeRate(tx: Transaction) throws -> UInt64 { + .zero + } + + func calculateFee(tx: Transaction) throws -> Amount { + try .fromBtc(btc: .zero) + } + + func buildTransaction(address: String, amount: UInt64, feeRate: UInt64) throws -> Psbt { + .init(noPointer: .init()) + } + + func send(address: String, amount: UInt64, feeRate: UInt64) async throws { + + } + + func listUnspent() throws -> [LocalOutput] { + [] + } + + func getAddress() throws -> String { + "" + } +} From a0708dd9b8b4e69b748e98622f02f633bd3aa2cc Mon Sep 17 00:00:00 2001 From: Rubens Date: Sat, 24 May 2025 17:24:23 -0300 Subject: [PATCH 08/50] Added Kyoto sync --- .../Connection+Extensions.swift | 6 ++ .../Service/BDK Service/BDKService.swift | 2 +- .../Service/BDKSyncService/KyotoService.swift | 76 ++++++++++++++++++- 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift index 170cdd68..834c53d5 100644 --- a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift +++ b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift @@ -2,6 +2,12 @@ import BitcoinDevKit import Foundation extension Connection { + static var dataDir: String { + let documentsDirectoryURL = URL.documentsDirectory + let walletDataDirectoryURL = documentsDirectoryURL.appendingPathComponent("wallet_data") + return walletDataDirectoryURL.path() + } + static func createConnection() throws -> Connection { let documentsDirectoryURL = URL.documentsDirectory let walletDataDirectoryURL = documentsDirectoryURL.appendingPathComponent("wallet_data") diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index e14a96ac..5a4376de 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -11,7 +11,7 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() - private let service: BDKSyncService = EsploraService() + private let service: BDKSyncService = KyotoService() private var balance: Balance? private var connection: Connection? diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 7f1f1515..3887d0ca 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -17,6 +17,10 @@ final class KyotoService: BDKSyncService { var network: Network var wallet: Wallet? + private var client: CbfClient? + private var node: CbfNode? + private var connected = false + init( keyClient: KeyClient = .live, network: Network = .signet, @@ -28,11 +32,19 @@ final class KyotoService: BDKSyncService { } func createWallet(params: String?) throws { - + self.connection = try Connection.createConnection() + self.wallet = try buildWallet(params: params) } func loadWallet() throws { + self.connection = try Connection.loadConnection() + let wallet = try loadWalleFromBackup() + self.wallet = wallet + let nodeComponents = try buildNode(from: wallet) + self.client = nodeComponents.client + self.node = nodeComponents.node + startListen() } func deleteWallet() throws { @@ -82,4 +94,66 @@ final class KyotoService: BDKSyncService { func getAddress() throws -> String { "" } + + // MARK: - Private + + private func buildNode(from wallet: Wallet) throws -> CbfComponents { + try CbfBuilder() + .dataDir(dataDir: Connection.dataDir) + .logLevel(logLevel: .debug) + .scanType(scanType: .recovery(fromHeight: 200_000)) + .build(wallet: wallet) + } + + private func startListen() { + node?.run() + continuallyUpdate() + printLogs() + updateWarn() + } + + private func continuallyUpdate() { + Task { + while true { + guard let update = await self.client?.update() else { return } + try self.wallet?.applyUpdate(update: update) + let _ = try self.wallet?.persist(connection: self.connection ?? Connection.loadConnection()) + print("######### walletUpdated") +// DispatchQueue.main.async { +// NotificationCenter.default.post(name: .walletUpdated, object: nil) +// } + } + } + } + + private func printLogs() { + Task { + while true { + if let log = try? await self.client?.nextLog() { + print("\(log)") + } + } + } + } + + private func updateWarn() { + Task { + while true { + if let warn = try? await self.client!.nextWarning() { + switch warn { + case .needConnections: + print("######### connectionsChanged") + self.connected = false +// DispatchQueue.main.async { +// NotificationCenter.default.post(name: .connectionsChanged, object: nil) +// } + default: +#if DEBUG + print(warn) +#endif + } + } + } + } + } } From 91acf937a79498a356a782a3ae25361edf551982 Mon Sep 17 00:00:00 2001 From: Rubens Date: Sat, 24 May 2025 17:39:33 -0300 Subject: [PATCH 09/50] no message --- .../BDKSyncService/BDKSyncService.swift | 83 +++++++++++++++++++ .../BDKSyncService/EsploraService.swift | 83 ------------------- .../Service/BDKSyncService/KyotoService.swift | 65 +++++---------- 3 files changed, 103 insertions(+), 128 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 14b50578..46cf0ab2 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -138,6 +138,10 @@ extension BDKSyncService { ) } + func deleteWallet() throws { + try deleteData() + } + func deleteData() throws { do { try keyClient.deleteAllData() @@ -176,6 +180,85 @@ extension BDKSyncService { return wallet } + func getBalance() throws -> Balance { + guard let wallet = self.wallet else { throw WalletError.walletNotFound } + let balance = wallet.balance() + return balance + } + + func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let values = wallet.sentAndReceived(tx: tx) + return values + } + + func calculateFeeRate(tx: Transaction) throws -> UInt64 { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let feeRate = try wallet.calculateFeeRate(tx: tx) + return feeRate.toSatPerVbCeil() + } + + func calculateFee(tx: Transaction) throws -> Amount { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let fee = try wallet.calculateFee(tx: tx) + return fee + } + + func buildTransaction( + address: String, + amount: UInt64, + feeRate: UInt64 + ) throws -> Psbt { + guard let wallet = self.wallet else { throw WalletError.walletNotFound } + let script = try Address(address: address, network: self.network) + .scriptPubkey() + let txBuilder = try TxBuilder() + .addRecipient( + script: script, + amount: Amount.fromSat(satoshi: amount) + ) + .feeRate(feeRate: FeeRate.fromSatPerVb(satVb: feeRate)) + .finish(wallet: wallet) + return txBuilder + } + + func listUnspent() throws -> [LocalOutput] { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let localOutputs = wallet.listUnspent() + return localOutputs + } + + func getAddress() throws -> String { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + guard let connection = self.connection else { + throw WalletError.dbNotFound + } + let addressInfo = wallet.revealNextAddress(keychain: .external) + let _ = try wallet.persist(connection: connection) + return addressInfo.address.description + } + + func getTransactions() throws -> [CanonicalTx] { + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let transactions = wallet.transactions() + let sortedTransactions = transactions.sorted { (tx1, tx2) in + return tx1.chainPosition.isBefore(tx2.chainPosition) + } + return sortedTransactions + } + // MARK: - Optionals methods func updateEsploraURL(_ url: String) { diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index f3e1d19d..fd83b01d 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -45,10 +45,6 @@ final class EsploraService: BDKSyncService { self.wallet = wallet } - func deleteWallet() throws { - try deleteData() - } - func updateNetwork(network: Network) { self.network = network } @@ -95,65 +91,6 @@ final class EsploraService: BDKSyncService { let _ = try wallet.persist(connection: connection) } - func getTransactions() throws -> [CanonicalTx] { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let transactions = wallet.transactions() - let sortedTransactions = transactions.sorted { (tx1, tx2) in - return tx1.chainPosition.isBefore(tx2.chainPosition) - } - return sortedTransactions - } - - func getBalance() throws -> Balance { - guard let wallet = self.wallet else { throw WalletError.walletNotFound } - let balance = wallet.balance() - return balance - } - - func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let values = wallet.sentAndReceived(tx: tx) - return values - } - - func calculateFeeRate(tx: Transaction) throws -> UInt64 { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let feeRate = try wallet.calculateFeeRate(tx: tx) - return feeRate.toSatPerVbCeil() - } - - func calculateFee(tx: Transaction) throws -> Amount { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let fee = try wallet.calculateFee(tx: tx) - return fee - } - - func buildTransaction( - address: String, - amount: UInt64, - feeRate: UInt64 - ) throws -> Psbt { - guard let wallet = self.wallet else { throw WalletError.walletNotFound } - let script = try Address(address: address, network: self.network) - .scriptPubkey() - let txBuilder = try TxBuilder() - .addRecipient( - script: script, - amount: Amount.fromSat(satoshi: amount) - ) - .feeRate(feeRate: FeeRate.fromSatPerVb(satVb: feeRate)) - .finish(wallet: wallet) - return txBuilder - } - func send( address: String, amount: UInt64, @@ -167,26 +104,6 @@ final class EsploraService: BDKSyncService { try signAndBroadcast(psbt: psbt) } - func listUnspent() throws -> [LocalOutput] { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let localOutputs = wallet.listUnspent() - return localOutputs - } - - func getAddress() throws -> String { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - guard let connection = self.connection else { - throw WalletError.dbNotFound - } - let addressInfo = wallet.revealNextAddress(keychain: .external) - let _ = try wallet.persist(connection: connection) - return addressInfo.address.description - } - // MARK: - Private private func signAndBroadcast(psbt: Psbt) throws { diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 3887d0ca..2eaefa4c 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -40,68 +40,43 @@ final class KyotoService: BDKSyncService { self.connection = try Connection.loadConnection() let wallet = try loadWalleFromBackup() self.wallet = wallet - - let nodeComponents = try buildNode(from: wallet) - self.client = nodeComponents.client - self.node = nodeComponents.node - startListen() - } - - func deleteWallet() throws { - } func startSync(progress: any SyncScriptInspector) async throws { - + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let nodeComponents = try buildNode( + from: wallet, scanType: .sync + ) + self.client = nodeComponents.client + self.node = nodeComponents.node + startListen() } func startFullScan(progress: any FullScanScriptInspector) async throws { - - } - - func getTransactions() throws -> [CanonicalTx] { - [] - } - - func getBalance() throws -> Balance { - .mock - } - - func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { - .mock - } - - func calculateFeeRate(tx: Transaction) throws -> UInt64 { - .zero - } - - func calculateFee(tx: Transaction) throws -> Amount { - try .fromBtc(btc: .zero) - } - - func buildTransaction(address: String, amount: UInt64, feeRate: UInt64) throws -> Psbt { - .init(noPointer: .init()) + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let nodeComponents = try buildNode( + from: wallet, scanType: .recovery(fromHeight: 200_000) + ) + self.client = nodeComponents.client + self.node = nodeComponents.node + startListen() } func send(address: String, amount: UInt64, feeRate: UInt64) async throws { } - func listUnspent() throws -> [LocalOutput] { - [] - } - - func getAddress() throws -> String { - "" - } - // MARK: - Private - private func buildNode(from wallet: Wallet) throws -> CbfComponents { + private func buildNode(from wallet: Wallet, scanType: ScanType) throws -> CbfComponents { try CbfBuilder() .dataDir(dataDir: Connection.dataDir) .logLevel(logLevel: .debug) - .scanType(scanType: .recovery(fromHeight: 200_000)) + .scanType(scanType: scanType) .build(wallet: wallet) } From 8af10a87a496c15ab334d9169e6253189680a653 Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 11:04:46 -0300 Subject: [PATCH 10/50] no message --- .../Service/BDK Service/BDKService.swift | 21 ++++- .../BDKSyncService/BDKSyncService.swift | 6 ++ .../BDKSyncService/EsploraService.swift | 43 ++++++++++ .../Service/BDKSyncService/KyotoService.swift | 82 +++++++++++++++---- .../View Model/WalletViewModel.swift | 73 ++++++++++------- 5 files changed, 177 insertions(+), 48 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 5a4376de..318f8e1f 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -11,7 +11,8 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() - private let service: BDKSyncService = KyotoService() +// private let service: BDKSyncService = KyotoService() + private let service: BDKSyncService = EsploraService() private var balance: Balance? private var connection: Connection? @@ -121,6 +122,14 @@ private class BDKService { func fullScanWithInspector(inspector: FullScanScriptInspector) async throws { try await service.startFullScan(progress: inspector) } + + func syncWithInspector2(progress: @escaping SyncScanProgress) async throws { + try await service.startSync2(progress: progress) + } + + func fullScanWithInspector2(progress: @escaping FullScanProgress) async throws { + try await service.startFullScan2(progress: progress) + } func calculateFee(tx: Transaction) throws -> Amount { try service.calculateFee(tx: tx) @@ -156,6 +165,8 @@ struct BDKClient { let listUnspent: () throws -> [LocalOutput] let syncWithInspector: (SyncScriptInspector) async throws -> Void let fullScanWithInspector: (FullScanScriptInspector) async throws -> Void + let syncScanWithSyncScanProgress: (@escaping SyncScanProgress) async throws -> Void + let fullScanWithFullScanProgress: (@escaping FullScanProgress) async throws -> Void let getAddress: () throws -> String let send: (String, UInt64, UInt64) throws -> Void let calculateFee: (Transaction) throws -> Amount @@ -191,6 +202,12 @@ extension BDKClient { fullScanWithInspector: { inspector in try await BDKService.shared.fullScanWithInspector(inspector: inspector) }, + syncScanWithSyncScanProgress: { progress in + try await BDKService.shared.syncWithInspector2(progress: progress) + }, + fullScanWithFullScanProgress: { progress in + try await BDKService.shared.fullScanWithInspector2(progress: progress) + }, getAddress: { try BDKService.shared.getAddress() }, send: { (address, amount, feeRate) in Task { @@ -246,6 +263,8 @@ extension BDKClient { }, syncWithInspector: { _ in }, fullScanWithInspector: { _ in }, + syncScanWithSyncScanProgress: { _ in }, + fullScanWithFullScanProgress: { _ in }, getAddress: { "tb1pd8jmenqpe7rz2mavfdx7uc8pj7vskxv4rl6avxlqsw2u8u7d4gfs97durt" }, send: { _, _, _ in }, calculateFee: { _ in Amount.fromSat(satoshi: UInt64(615)) }, diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 46cf0ab2..fa17b1f0 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -8,6 +8,9 @@ import BitcoinDevKit import Foundation +typealias FullScanProgress = (UInt64) -> Void +typealias SyncScanProgress = (UInt64, UInt64) -> Void + protocol BDKSyncService { var connection: Connection? { get } var keyClient: KeyClient { get } @@ -20,6 +23,9 @@ protocol BDKSyncService { func startSync(progress: SyncScriptInspector) async throws func startFullScan(progress: FullScanScriptInspector) async throws + func startSync2(progress: @escaping SyncScanProgress) async throws + func startFullScan2(progress: @escaping FullScanProgress) async throws + func updateNetwork(network: Network) func updateEsploraURL(_ url: String) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index fd83b01d..227521d3 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -71,6 +71,49 @@ final class EsploraService: BDKSyncService { let _ = try wallet.persist(connection: connection) } + func startSync2(progress: @escaping SyncScanProgress) async throws { + guard let wallet = self.wallet else { throw WalletError.walletNotFound } + let syncScanInspector = WalletSyncScriptInspector { scripts, total in + progress(scripts, total) + } + let esploraClient = self.esploraClient + let syncRequest = try wallet.startSyncWithRevealedSpks() + .inspectSpks(inspector: syncScanInspector) + .build() + let update = try esploraClient.sync( + request: syncRequest, + parallelRequests: UInt64(5) + ) + let _ = try wallet.applyUpdate(update: update) + guard let connection = self.connection else { + throw WalletError.dbNotFound + } + let _ = try wallet.persist(connection: connection) + } + + func startFullScan2(progress: @escaping FullScanProgress) async throws { + guard let wallet = self.wallet else { throw WalletError.walletNotFound } + let fullScanInspector = WalletFullScanScriptInspector { inspected in + progress(inspected) + } + let esploraClient = esploraClient + let fullScanRequest = try wallet.startFullScan() + .inspectSpksForAllKeychains(inspector: fullScanInspector) + .build() + let update = try esploraClient.fullScan( + request: fullScanRequest, + // using https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#address-gap-limit + stopGap: UInt64(20), + // using https://github.com/bitcoindevkit/bdk/blob/master/example-crates/example_wallet_esplora_blocking/src/main.rs + parallelRequests: UInt64(5) + ) + let _ = try wallet.applyUpdate(update: update) + guard let connection = self.connection else { + throw WalletError.dbNotFound + } + let _ = try wallet.persist(connection: connection) + } + func startFullScan(progress: FullScanScriptInspector) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let esploraClient = esploraClient diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 2eaefa4c..c5e23a35 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -10,6 +10,8 @@ import Foundation final class KyotoService: BDKSyncService { + private static let nodeHeight: UInt32 = 251_000 + static let shared = KyotoService() var connection: Connection? @@ -21,6 +23,10 @@ final class KyotoService: BDKSyncService { private var node: CbfNode? private var connected = false + private var fullScanProgress: FullScanScriptInspector? + private var fullScanProgress2: FullScanProgress? + private var syncProgress: SyncScriptInspector? + init( keyClient: KeyClient = .live, network: Network = .signet, @@ -42,28 +48,47 @@ final class KyotoService: BDKSyncService { self.wallet = wallet } - func startSync(progress: any SyncScriptInspector) async throws { - guard let wallet = self.wallet else { - throw WalletError.walletNotFound - } - let nodeComponents = try buildNode( - from: wallet, scanType: .sync - ) - self.client = nodeComponents.client - self.node = nodeComponents.node - startListen() + func startSync(progress: SyncScriptInspector) async throws { +// guard let wallet = self.wallet else { +// throw WalletError.walletNotFound +// } +// let nodeComponents = try buildNode( +// from: wallet, scanType: .sync +// ) +// self.syncProgress = progress +// self.client = nodeComponents.client +// self.node = nodeComponents.node +// await startListen() } - func startFullScan(progress: any FullScanScriptInspector) async throws { + func startFullScan(progress: FullScanScriptInspector) async throws { +// guard let wallet = self.wallet else { +// throw WalletError.walletNotFound +// } +// let nodeComponents = try buildNode( +// from: wallet, scanType: .recovery(fromHeight: 200_000) +// ) +// self.fullScanProgress = progress +// self.client = nodeComponents.client +// self.node = nodeComponents.node +// await startListen() + } + + func startSync2(progress: @escaping SyncScanProgress) async throws { + + } + + func startFullScan2(progress: @escaping FullScanProgress) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let nodeComponents = try buildNode( - from: wallet, scanType: .recovery(fromHeight: 200_000) + from: wallet, scanType: .recovery(fromHeight: KyotoService.nodeHeight) ) + self.fullScanProgress2 = progress self.client = nodeComponents.client self.node = nodeComponents.node - startListen() + try await startListen() } func send(address: String, amount: UInt64, feeRate: UInt64) async throws { @@ -80,14 +105,25 @@ final class KyotoService: BDKSyncService { .build(wallet: wallet) } - private func startListen() { + private func startListen() async throws { node?.run() - continuallyUpdate() printLogs() updateWarn() +// await continuallyUpdate() + try await startUpdating() } - private func continuallyUpdate() { + @discardableResult + func startUpdating() async throws -> Bool { + guard let update = await self.client?.update() else { return false } + try self.wallet?.applyUpdate(update: update) + let _ = try self.wallet?.persist(connection: self.connection ?? Connection.loadConnection()) + print("######### walletUpdated") + + return true + } + + private func continuallyUpdate() async { Task { while true { guard let update = await self.client?.update() else { return } @@ -106,6 +142,18 @@ final class KyotoService: BDKSyncService { while true { if let log = try? await self.client?.nextLog() { print("\(log)") + switch log { + case .connectionsMet: + print("######### connected") + self.connected = true + case .progress(let progress): + if let fullScanProgress = self.fullScanProgress2 { + let _progress = UInt64(progress * 100.0) + fullScanProgress(_progress) + } + default: + break + } } } } @@ -117,7 +165,7 @@ final class KyotoService: BDKSyncService { if let warn = try? await self.client!.nextWarning() { switch warn { case .needConnections: - print("######### connectionsChanged") + print("######### disconnected") self.connected = false // DispatchQueue.main.async { // NotificationCenter.default.post(name: .connectionsChanged, object: nil) diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index d0e3a42d..05a950d9 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -75,27 +75,6 @@ class WalletViewModel { self.walletSyncState = walletSyncState } - private func fullScanWithProgress() async { - self.walletSyncState = .syncing - do { - let inspector = WalletFullScanScriptInspector(updateProgress: updateProgressFullScan) - try await bdkClient.fullScanWithInspector(inspector) - self.walletSyncState = .synced - } catch let error as CannotConnectError { - self.walletViewError = .generic(message: error.localizedDescription) - self.showingWalletViewErrorAlert = true - } catch let error as EsploraError { - self.walletViewError = .generic(message: error.localizedDescription) - self.showingWalletViewErrorAlert = true - } catch let error as PersistenceError { - self.walletViewError = .generic(message: error.localizedDescription) - self.showingWalletViewErrorAlert = true - } catch { - self.walletSyncState = .error(error) - self.showingWalletViewErrorAlert = true - } - } - func getBalance() { do { let balance = try bdkClient.getBalance() @@ -133,11 +112,28 @@ class WalletViewModel { } } + func syncOrFullScan() async { + if bdkClient.needsFullScan() { + await fullScanWithProgress() + bdkClient.setNeedsFullScan(false) + } else { + await startSyncWithProgress() + } + } + private func startSyncWithProgress() async { self.walletSyncState = .syncing do { - let inspector = WalletSyncScriptInspector(updateProgress: updateProgress) - try await bdkClient.syncWithInspector(inspector) +// let inspector = WalletSyncScriptInspector(updateProgress: updateProgress) +// try await bdkClient.syncWithInspector(inspector) + + try await bdkClient.syncScanWithSyncScanProgress { [weak self] inspected, total in + DispatchQueue.main.async { + self?.totalScripts = total + self?.inspectedScripts = inspected + self?.progress = total > 0 ? Float(inspected) / Float(total) : 0 + } + } self.walletSyncState = .synced } catch let error as CannotConnectError { self.walletViewError = .generic(message: error.localizedDescription) @@ -156,13 +152,30 @@ class WalletViewModel { self.showingWalletViewErrorAlert = true } } - - func syncOrFullScan() async { - if bdkClient.needsFullScan() { - await fullScanWithProgress() - bdkClient.setNeedsFullScan(false) - } else { - await startSyncWithProgress() + + private func fullScanWithProgress() async { + self.walletSyncState = .syncing + do { +// let inspector = WalletFullScanScriptInspector(updateProgress: updateProgressFullScan) +// try await bdkClient.fullScanWithInspector(inspector) + try await bdkClient.fullScanWithFullScanProgress { [weak self] progress in + DispatchQueue.main.async { + self?.inspectedScripts = progress + } + } + self.walletSyncState = .synced + } catch let error as CannotConnectError { + self.walletViewError = .generic(message: error.localizedDescription) + self.showingWalletViewErrorAlert = true + } catch let error as EsploraError { + self.walletViewError = .generic(message: error.localizedDescription) + self.showingWalletViewErrorAlert = true + } catch let error as PersistenceError { + self.walletViewError = .generic(message: error.localizedDescription) + self.showingWalletViewErrorAlert = true + } catch { + self.walletSyncState = .error(error) + self.showingWalletViewErrorAlert = true } } } From 9f770fe99d9c2e40e9775574eb87faf9ecfd4ffc Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 11:10:01 -0300 Subject: [PATCH 11/50] removed ActivityListViewModel dead code --- .../Activity/ActivityListViewModel.swift | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift index 5519e3d3..92c8321e 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift @@ -73,31 +73,31 @@ class ActivityListViewModel { } } - private func startSyncWithProgress() async { - self.walletSyncState = .syncing - do { - let inspector = WalletSyncScriptInspector(updateProgress: updateProgress) - try await bdkClient.syncWithInspector(inspector) - self.walletSyncState = .synced - } catch let error as CannotConnectError { - self.walletViewError = .generic(message: error.localizedDescription) - self.showingWalletViewErrorAlert = true - } catch let error as EsploraError { - self.walletViewError = .generic(message: error.localizedDescription) - self.showingWalletViewErrorAlert = true - } catch let error as RequestBuilderError { - self.walletViewError = .generic(message: error.localizedDescription) - self.showingWalletViewErrorAlert = true - } catch let error as PersistenceError { - self.walletViewError = .generic(message: error.localizedDescription) - self.showingWalletViewErrorAlert = true - } catch { - self.walletSyncState = .error(error) - self.showingWalletViewErrorAlert = true - } - } +// private func startSyncWithProgress() async { +// self.walletSyncState = .syncing +// do { +// let inspector = WalletSyncScriptInspector(updateProgress: updateProgress) +// try await bdkClient.syncWithInspector(inspector) +// self.walletSyncState = .synced +// } catch let error as CannotConnectError { +// self.walletViewError = .generic(message: error.localizedDescription) +// self.showingWalletViewErrorAlert = true +// } catch let error as EsploraError { +// self.walletViewError = .generic(message: error.localizedDescription) +// self.showingWalletViewErrorAlert = true +// } catch let error as RequestBuilderError { +// self.walletViewError = .generic(message: error.localizedDescription) +// self.showingWalletViewErrorAlert = true +// } catch let error as PersistenceError { +// self.walletViewError = .generic(message: error.localizedDescription) +// self.showingWalletViewErrorAlert = true +// } catch { +// self.walletSyncState = .error(error) +// self.showingWalletViewErrorAlert = true +// } +// } - func syncOrFullScan() async { - await startSyncWithProgress() - } +// func syncOrFullScan() async { +// await startSyncWithProgress() +// } } From 0b490dbaa6c3c1d046136516ecd76a2d39449360 Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 11:17:56 -0300 Subject: [PATCH 12/50] no message --- .../Actor/WalletFullScanScriptInspector.swift | 1 + .../Service/BDK Service/BDKService.swift | 18 --------- .../BDKSyncService/BDKSyncService.swift | 5 +-- .../BDKSyncService/EsploraService.swift | 37 ----------------- .../Service/BDKSyncService/KyotoService.swift | 40 +++++-------------- .../Settings/SettingsViewModel.swift | 7 +++- .../View Model/WalletViewModel.swift | 5 --- 7 files changed, 18 insertions(+), 95 deletions(-) diff --git a/BDKSwiftExampleWallet/Actor/WalletFullScanScriptInspector.swift b/BDKSwiftExampleWallet/Actor/WalletFullScanScriptInspector.swift index 3a6563a1..b28c7112 100644 --- a/BDKSwiftExampleWallet/Actor/WalletFullScanScriptInspector.swift +++ b/BDKSwiftExampleWallet/Actor/WalletFullScanScriptInspector.swift @@ -5,6 +5,7 @@ // Created by Rubens Machion on 23/04/25. // + import BitcoinDevKit actor WalletFullScanScriptInspector: @preconcurrency FullScanScriptInspector { diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 318f8e1f..b2aef298 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -114,14 +114,6 @@ private class BDKService { { try service.buildTransaction(address: address, amount: amount, feeRate: feeRate) } - - func syncWithInspector(inspector: SyncScriptInspector) async throws { - try await service.startSync(progress: inspector) - } - - func fullScanWithInspector(inspector: FullScanScriptInspector) async throws { - try await service.startFullScan(progress: inspector) - } func syncWithInspector2(progress: @escaping SyncScanProgress) async throws { try await service.startSync2(progress: progress) @@ -163,8 +155,6 @@ struct BDKClient { let getBalance: () throws -> Balance let transactions: () throws -> [CanonicalTx] let listUnspent: () throws -> [LocalOutput] - let syncWithInspector: (SyncScriptInspector) async throws -> Void - let fullScanWithInspector: (FullScanScriptInspector) async throws -> Void let syncScanWithSyncScanProgress: (@escaping SyncScanProgress) async throws -> Void let fullScanWithFullScanProgress: (@escaping FullScanProgress) async throws -> Void let getAddress: () throws -> String @@ -196,12 +186,6 @@ extension BDKClient { getBalance: { try BDKService.shared.getBalance() }, transactions: { try BDKService.shared.transactions() }, listUnspent: { try BDKService.shared.listUnspent() }, - syncWithInspector: { inspector in - try await BDKService.shared.syncWithInspector(inspector: inspector) - }, - fullScanWithInspector: { inspector in - try await BDKService.shared.fullScanWithInspector(inspector: inspector) - }, syncScanWithSyncScanProgress: { progress in try await BDKService.shared.syncWithInspector2(progress: progress) }, @@ -261,8 +245,6 @@ extension BDKClient { .mock ] }, - syncWithInspector: { _ in }, - fullScanWithInspector: { _ in }, syncScanWithSyncScanProgress: { _ in }, fullScanWithFullScanProgress: { _ in }, getAddress: { "tb1pd8jmenqpe7rz2mavfdx7uc8pj7vskxv4rl6avxlqsw2u8u7d4gfs97durt" }, diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index fa17b1f0..2c313172 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -19,10 +19,7 @@ protocol BDKSyncService { func createWallet(params: String?) throws func loadWallet() throws - func deleteWallet() throws - func startSync(progress: SyncScriptInspector) async throws - func startFullScan(progress: FullScanScriptInspector) async throws - + func deleteWallet() throws func startSync2(progress: @escaping SyncScanProgress) async throws func startFullScan2(progress: @escaping FullScanProgress) async throws diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index 227521d3..7fdb7a15 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -54,23 +54,6 @@ final class EsploraService: BDKSyncService { self.esploraClient = .init(url: url) } - func startSync(progress: SyncScriptInspector) async throws { - guard let wallet = self.wallet else { throw WalletError.walletNotFound } - let esploraClient = self.esploraClient - let syncRequest = try wallet.startSyncWithRevealedSpks() - .inspectSpks(inspector: progress) - .build() - let update = try esploraClient.sync( - request: syncRequest, - parallelRequests: UInt64(5) - ) - let _ = try wallet.applyUpdate(update: update) - guard let connection = self.connection else { - throw WalletError.dbNotFound - } - let _ = try wallet.persist(connection: connection) - } - func startSync2(progress: @escaping SyncScanProgress) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let syncScanInspector = WalletSyncScriptInspector { scripts, total in @@ -114,26 +97,6 @@ final class EsploraService: BDKSyncService { let _ = try wallet.persist(connection: connection) } - func startFullScan(progress: FullScanScriptInspector) async throws { - guard let wallet = self.wallet else { throw WalletError.walletNotFound } - let esploraClient = esploraClient - let fullScanRequest = try wallet.startFullScan() - .inspectSpksForAllKeychains(inspector: progress) - .build() - let update = try esploraClient.fullScan( - request: fullScanRequest, - // using https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#address-gap-limit - stopGap: UInt64(20), - // using https://github.com/bitcoindevkit/bdk/blob/master/example-crates/example_wallet_esplora_blocking/src/main.rs - parallelRequests: UInt64(5) - ) - let _ = try wallet.applyUpdate(update: update) - guard let connection = self.connection else { - throw WalletError.dbNotFound - } - let _ = try wallet.persist(connection: connection) - } - func send( address: String, amount: UInt64, diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index c5e23a35..90413d61 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -23,9 +23,8 @@ final class KyotoService: BDKSyncService { private var node: CbfNode? private var connected = false - private var fullScanProgress: FullScanScriptInspector? private var fullScanProgress2: FullScanProgress? - private var syncProgress: SyncScriptInspector? + private var syncProgress: SyncScanProgress? init( keyClient: KeyClient = .live, @@ -48,34 +47,17 @@ final class KyotoService: BDKSyncService { self.wallet = wallet } - func startSync(progress: SyncScriptInspector) async throws { -// guard let wallet = self.wallet else { -// throw WalletError.walletNotFound -// } -// let nodeComponents = try buildNode( -// from: wallet, scanType: .sync -// ) -// self.syncProgress = progress -// self.client = nodeComponents.client -// self.node = nodeComponents.node -// await startListen() - } - - func startFullScan(progress: FullScanScriptInspector) async throws { -// guard let wallet = self.wallet else { -// throw WalletError.walletNotFound -// } -// let nodeComponents = try buildNode( -// from: wallet, scanType: .recovery(fromHeight: 200_000) -// ) -// self.fullScanProgress = progress -// self.client = nodeComponents.client -// self.node = nodeComponents.node -// await startListen() - } - func startSync2(progress: @escaping SyncScanProgress) async throws { - + guard let wallet = self.wallet else { + throw WalletError.walletNotFound + } + let nodeComponents = try buildNode( + from: wallet, scanType: .sync + ) + self.syncProgress = progress + self.client = nodeComponents.client + self.node = nodeComponents.node + try await startListen() } func startFullScan2(progress: @escaping FullScanProgress) async throws { diff --git a/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift b/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift index 790fc87b..8b86c245 100644 --- a/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift @@ -52,8 +52,11 @@ class SettingsViewModel: ObservableObject { self.walletSyncState = .syncing } do { - let inspector = WalletFullScanScriptInspector(updateProgress: updateProgressFullScan) - try await bdkClient.fullScanWithInspector(inspector) + try await bdkClient.fullScanWithFullScanProgress { [weak self] progress in + DispatchQueue.main.async { + self?.inspectedScripts = progress + } + } DispatchQueue.main.async { NotificationCenter.default.post( name: Notification.Name("TransactionSent"), diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index 05a950d9..998a78f7 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -124,9 +124,6 @@ class WalletViewModel { private func startSyncWithProgress() async { self.walletSyncState = .syncing do { -// let inspector = WalletSyncScriptInspector(updateProgress: updateProgress) -// try await bdkClient.syncWithInspector(inspector) - try await bdkClient.syncScanWithSyncScanProgress { [weak self] inspected, total in DispatchQueue.main.async { self?.totalScripts = total @@ -156,8 +153,6 @@ class WalletViewModel { private func fullScanWithProgress() async { self.walletSyncState = .syncing do { -// let inspector = WalletFullScanScriptInspector(updateProgress: updateProgressFullScan) -// try await bdkClient.fullScanWithInspector(inspector) try await bdkClient.fullScanWithFullScanProgress { [weak self] progress in DispatchQueue.main.async { self?.inspectedScripts = progress From 8c2a9d90a4c9ca9815215c1b614a41abe7c3553e Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 11:19:53 -0300 Subject: [PATCH 13/50] fix the code --- .../Service/BDK Service/BDKService.swift | 12 +++---- .../BDKSyncService/BDKSyncService.swift | 4 +-- .../BDKSyncService/EsploraService.swift | 4 +-- .../Service/BDKSyncService/KyotoService.swift | 4 +-- .../View Model/WalletViewModel.swift | 34 +++++++++---------- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index b2aef298..9b773a3a 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -115,12 +115,12 @@ private class BDKService { try service.buildTransaction(address: address, amount: amount, feeRate: feeRate) } - func syncWithInspector2(progress: @escaping SyncScanProgress) async throws { - try await service.startSync2(progress: progress) + func syncWithInspector(progress: @escaping SyncScanProgress) async throws { + try await service.startSync(progress: progress) } - func fullScanWithInspector2(progress: @escaping FullScanProgress) async throws { - try await service.startFullScan2(progress: progress) + func fullScanWithInspector(progress: @escaping FullScanProgress) async throws { + try await service.startFullScan(progress: progress) } func calculateFee(tx: Transaction) throws -> Amount { @@ -187,10 +187,10 @@ extension BDKClient { transactions: { try BDKService.shared.transactions() }, listUnspent: { try BDKService.shared.listUnspent() }, syncScanWithSyncScanProgress: { progress in - try await BDKService.shared.syncWithInspector2(progress: progress) + try await BDKService.shared.syncWithInspector(progress: progress) }, fullScanWithFullScanProgress: { progress in - try await BDKService.shared.fullScanWithInspector2(progress: progress) + try await BDKService.shared.fullScanWithInspector(progress: progress) }, getAddress: { try BDKService.shared.getAddress() }, send: { (address, amount, feeRate) in diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 2c313172..bb1b2b19 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -20,8 +20,8 @@ protocol BDKSyncService { func createWallet(params: String?) throws func loadWallet() throws func deleteWallet() throws - func startSync2(progress: @escaping SyncScanProgress) async throws - func startFullScan2(progress: @escaping FullScanProgress) async throws + func startSync(progress: @escaping SyncScanProgress) async throws + func startFullScan(progress: @escaping FullScanProgress) async throws func updateNetwork(network: Network) func updateEsploraURL(_ url: String) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index 7fdb7a15..21c03248 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -54,7 +54,7 @@ final class EsploraService: BDKSyncService { self.esploraClient = .init(url: url) } - func startSync2(progress: @escaping SyncScanProgress) async throws { + func startSync(progress: @escaping SyncScanProgress) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let syncScanInspector = WalletSyncScriptInspector { scripts, total in progress(scripts, total) @@ -74,7 +74,7 @@ final class EsploraService: BDKSyncService { let _ = try wallet.persist(connection: connection) } - func startFullScan2(progress: @escaping FullScanProgress) async throws { + func startFullScan(progress: @escaping FullScanProgress) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let fullScanInspector = WalletFullScanScriptInspector { inspected in progress(inspected) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 90413d61..1497ecd0 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -47,7 +47,7 @@ final class KyotoService: BDKSyncService { self.wallet = wallet } - func startSync2(progress: @escaping SyncScanProgress) async throws { + func startSync(progress: @escaping SyncScanProgress) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } @@ -60,7 +60,7 @@ final class KyotoService: BDKSyncService { try await startListen() } - func startFullScan2(progress: @escaping FullScanProgress) async throws { + func startFullScan(progress: @escaping FullScanProgress) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index 998a78f7..4dc3b262 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -43,23 +43,23 @@ class WalletViewModel { bdkClient.needsFullScan() } - private var updateProgress: @Sendable (UInt64, UInt64) -> Void { - { [weak self] inspected, total in - DispatchQueue.main.async { - self?.totalScripts = total - self?.inspectedScripts = inspected - self?.progress = total > 0 ? Float(inspected) / Float(total) : 0 - } - } - } - - private var updateProgressFullScan: @Sendable (UInt64) -> Void { - { [weak self] inspected in - DispatchQueue.main.async { - self?.inspectedScripts = inspected - } - } - } +// private var updateProgress: @Sendable (UInt64, UInt64) -> Void { +// { [weak self] inspected, total in +// DispatchQueue.main.async { +// self?.totalScripts = total +// self?.inspectedScripts = inspected +// self?.progress = total > 0 ? Float(inspected) / Float(total) : 0 +// } +// } +// } +// +// private var updateProgressFullScan: @Sendable (UInt64) -> Void { +// { [weak self] inspected in +// DispatchQueue.main.async { +// self?.inspectedScripts = inspected +// } +// } +// } init( bdkClient: BDKClient = .live, From bf844b5294a2ea8d45d05a5511f8dca0f90e18b6 Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 11:23:19 -0300 Subject: [PATCH 14/50] chore: organizing code --- .../View Model/WalletViewModel.swift | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index 4dc3b262..a91613aa 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -43,24 +43,6 @@ class WalletViewModel { bdkClient.needsFullScan() } -// private var updateProgress: @Sendable (UInt64, UInt64) -> Void { -// { [weak self] inspected, total in -// DispatchQueue.main.async { -// self?.totalScripts = total -// self?.inspectedScripts = inspected -// self?.progress = total > 0 ? Float(inspected) / Float(total) : 0 -// } -// } -// } -// -// private var updateProgressFullScan: @Sendable (UInt64) -> Void { -// { [weak self] inspected in -// DispatchQueue.main.async { -// self?.inspectedScripts = inspected -// } -// } -// } - init( bdkClient: BDKClient = .live, keyClient: KeyClient = .live, @@ -125,11 +107,7 @@ class WalletViewModel { self.walletSyncState = .syncing do { try await bdkClient.syncScanWithSyncScanProgress { [weak self] inspected, total in - DispatchQueue.main.async { - self?.totalScripts = total - self?.inspectedScripts = inspected - self?.progress = total > 0 ? Float(inspected) / Float(total) : 0 - } + self?.updateSyncProgress(inspected, total) } self.walletSyncState = .synced } catch let error as CannotConnectError { @@ -154,9 +132,7 @@ class WalletViewModel { self.walletSyncState = .syncing do { try await bdkClient.fullScanWithFullScanProgress { [weak self] progress in - DispatchQueue.main.async { - self?.inspectedScripts = progress - } + self?.updateFullProgress(progress) } self.walletSyncState = .synced } catch let error as CannotConnectError { @@ -173,4 +149,19 @@ class WalletViewModel { self.showingWalletViewErrorAlert = true } } + + private func updateFullProgress(_ progress: UInt64) { + DispatchQueue.main.async { [weak self] in + self?.inspectedScripts = progress + } + } + + private func updateSyncProgress(_ inspected: UInt64, _ total: UInt64) { + DispatchQueue.main.async { [weak self] in + self?.totalScripts = total + self?.inspectedScripts = inspected + self?.progress = total > 0 ? Float(inspected) / Float(total) : 0 + } + } + } From c13fc7458200595e3b961cb9c917be2831395f0e Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 11:29:10 -0300 Subject: [PATCH 15/50] testing kyoto --- .../Service/BDK Service/BDKService.swift | 4 +-- .../Service/BDKSyncService/KyotoService.swift | 29 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 9b773a3a..4a9f3dff 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -11,8 +11,8 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() -// private let service: BDKSyncService = KyotoService() - private let service: BDKSyncService = EsploraService() + private let service: BDKSyncService = KyotoService() +// private let service: BDKSyncService = EsploraService() private var balance: Balance? private var connection: Connection? diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 1497ecd0..1d964886 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -10,7 +10,7 @@ import Foundation final class KyotoService: BDKSyncService { - private static let nodeHeight: UInt32 = 251_000 + private static let nodeHeight: UInt32 = 253_000 static let shared = KyotoService() @@ -91,7 +91,6 @@ final class KyotoService: BDKSyncService { node?.run() printLogs() updateWarn() -// await continuallyUpdate() try await startUpdating() } @@ -105,19 +104,19 @@ final class KyotoService: BDKSyncService { return true } - private func continuallyUpdate() async { - Task { - while true { - guard let update = await self.client?.update() else { return } - try self.wallet?.applyUpdate(update: update) - let _ = try self.wallet?.persist(connection: self.connection ?? Connection.loadConnection()) - print("######### walletUpdated") -// DispatchQueue.main.async { -// NotificationCenter.default.post(name: .walletUpdated, object: nil) -// } - } - } - } +// private func continuallyUpdate() async { +// Task { +// while true { +// guard let update = await self.client?.update() else { return } +// try self.wallet?.applyUpdate(update: update) +// let _ = try self.wallet?.persist(connection: self.connection ?? Connection.loadConnection()) +// print("######### walletUpdated") +//// DispatchQueue.main.async { +//// NotificationCenter.default.post(name: .walletUpdated, object: nil) +//// } +// } +// } +// } private func printLogs() { Task { From db1a908aaab17ff912d6303784ae49e7177612af Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 14:19:03 -0300 Subject: [PATCH 16/50] added Kyoto stop service --- .../Service/BDK Service/BDKService.swift | 11 ++++++++++- .../Service/BDKSyncService/BDKSyncService.swift | 11 +++++------ .../Service/BDKSyncService/KyotoService.swift | 6 +++++- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 4a9f3dff..807f5740 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -134,6 +134,10 @@ private class BDKService { func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { try service.sentAndReceived(tx: tx) } + + func stop() async throws { + try await service.stopService() + } } extension BDKService { @@ -170,6 +174,7 @@ struct BDKClient { let getEsploraURL: () -> String let updateNetwork: (Network) -> Void let updateEsploraURL: (String) -> Void + let stop: () async throws -> Void } extension BDKClient { @@ -222,6 +227,9 @@ extension BDKClient { }, updateEsploraURL: { newURL in BDKService.shared.updateEsploraURL(newURL) + }, + stop: { + try await BDKService.shared.stop() } ) } @@ -278,7 +286,8 @@ extension BDKClient { getNetwork: { .signet }, getEsploraURL: { Constants.Config.EsploraServerURLNetwork.Signet.mutiny }, updateNetwork: { _ in }, - updateEsploraURL: { _ in } + updateEsploraURL: { _ in }, + stop: { } ) } #endif diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index bb1b2b19..3d6b13ee 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -35,6 +35,7 @@ protocol BDKSyncService { func send(address: String, amount: UInt64, feeRate: UInt64) async throws func listUnspent() throws -> [LocalOutput] func getAddress() throws -> String + func stopService() async throws } extension BDKSyncService { @@ -264,13 +265,11 @@ extension BDKSyncService { // MARK: - Optionals methods - func updateEsploraURL(_ url: String) { - // Optional implementation - } + func updateEsploraURL(_ url: String) { } - func updateNetwork(network: Network) { - // Optional implementation - } + func updateNetwork(network: Network) { } + + func stopService() async throws { } // MARK: - Private diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 1d964886..2612d1ae 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -9,7 +9,7 @@ import BitcoinDevKit import Foundation final class KyotoService: BDKSyncService { - + private static let nodeHeight: UInt32 = 253_000 static let shared = KyotoService() @@ -77,6 +77,10 @@ final class KyotoService: BDKSyncService { } + func stopService() async throws { + try await client?.shutdown() + } + // MARK: - Private private func buildNode(from wallet: Wallet, scanType: ScanType) throws -> CbfComponents { From 93562f24830bb5007a4f94d08f59dc5290a098c1 Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 14:42:52 -0300 Subject: [PATCH 17/50] organize code --- .../Service/BDKSyncService/KyotoService.swift | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 2612d1ae..bd58adcc 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -10,7 +10,7 @@ import Foundation final class KyotoService: BDKSyncService { - private static let nodeHeight: UInt32 = 253_000 + private static let nodeHeight: UInt32 = 300_000 static let shared = KyotoService() @@ -23,7 +23,7 @@ final class KyotoService: BDKSyncService { private var node: CbfNode? private var connected = false - private var fullScanProgress2: FullScanProgress? + private var fullScanProgress: FullScanProgress? private var syncProgress: SyncScanProgress? init( @@ -67,7 +67,7 @@ final class KyotoService: BDKSyncService { let nodeComponents = try buildNode( from: wallet, scanType: .recovery(fromHeight: KyotoService.nodeHeight) ) - self.fullScanProgress2 = progress + self.fullScanProgress = progress self.client = nodeComponents.client self.node = nodeComponents.node try await startListen() @@ -108,20 +108,6 @@ final class KyotoService: BDKSyncService { return true } -// private func continuallyUpdate() async { -// Task { -// while true { -// guard let update = await self.client?.update() else { return } -// try self.wallet?.applyUpdate(update: update) -// let _ = try self.wallet?.persist(connection: self.connection ?? Connection.loadConnection()) -// print("######### walletUpdated") -//// DispatchQueue.main.async { -//// NotificationCenter.default.post(name: .walletUpdated, object: nil) -//// } -// } -// } -// } - private func printLogs() { Task { while true { @@ -132,7 +118,7 @@ final class KyotoService: BDKSyncService { print("######### connected") self.connected = true case .progress(let progress): - if let fullScanProgress = self.fullScanProgress2 { + if let fullScanProgress = self.fullScanProgress { let _progress = UInt64(progress * 100.0) fullScanProgress(_progress) } @@ -152,9 +138,6 @@ final class KyotoService: BDKSyncService { case .needConnections: print("######### disconnected") self.connected = false -// DispatchQueue.main.async { -// NotificationCenter.default.post(name: .connectionsChanged, object: nil) -// } default: #if DEBUG print(warn) From e1543e28a80b5fe40cec09f7555459068cb223ac Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 15:38:00 -0300 Subject: [PATCH 18/50] no message --- .../Service/BDK Service/BDKService.swift | 7 ++-- .../Service/BDKSyncService/KyotoService.swift | 35 ++++++++++++++++--- .../Utilities/Constants.swift | 2 ++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 807f5740..00f212af 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -11,8 +11,8 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() - private let service: BDKSyncService = KyotoService() -// private let service: BDKSyncService = EsploraService() +// private let service: BDKSyncService = KyotoService() + private let service: BDKSyncService = EsploraService() private var balance: Balance? private var connection: Connection? @@ -92,6 +92,9 @@ private class BDKService { } func deleteWallet() throws { + Task { + try await service.stopService() + } try service.deleteWallet() needsFullScan = true } diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index bd58adcc..f0513757 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -10,7 +10,7 @@ import Foundation final class KyotoService: BDKSyncService { - private static let nodeHeight: UInt32 = 300_000 + private static let nodeHeight: UInt32 = 200_000 static let shared = KyotoService() @@ -22,6 +22,7 @@ final class KyotoService: BDKSyncService { private var client: CbfClient? private var node: CbfNode? private var connected = false + private var isScanRunning = false private var fullScanProgress: FullScanProgress? private var syncProgress: SyncScanProgress? @@ -48,6 +49,7 @@ final class KyotoService: BDKSyncService { } func startSync(progress: @escaping SyncScanProgress) async throws { + if isScanRunning { return } guard let wallet = self.wallet else { throw WalletError.walletNotFound } @@ -57,10 +59,12 @@ final class KyotoService: BDKSyncService { self.syncProgress = progress self.client = nodeComponents.client self.node = nodeComponents.node + isScanRunning = true try await startListen() } func startFullScan(progress: @escaping FullScanProgress) async throws { + if isScanRunning { return } guard let wallet = self.wallet else { throw WalletError.walletNotFound } @@ -70,19 +74,37 @@ final class KyotoService: BDKSyncService { self.fullScanProgress = progress self.client = nodeComponents.client self.node = nodeComponents.node + isScanRunning = true try await startListen() } func send(address: String, amount: UInt64, feeRate: UInt64) async throws { - + let psbt = try buildTransaction( + address: address, + amount: amount, + feeRate: feeRate + ) + try await signAndBroadcast(psbt: psbt) } func stopService() async throws { + isScanRunning = false try await client?.shutdown() } // MARK: - Private + private func signAndBroadcast(psbt: Psbt) async throws { + guard let wallet = self.wallet else { throw WalletError.walletNotFound } + let isSigned = try wallet.sign(psbt: psbt) + if isSigned { + let transaction = try psbt.extractTx() + try await client?.broadcast(transaction: transaction) + } else { + throw WalletError.notSigned + } + } + private func buildNode(from wallet: Wallet, scanType: ScanType) throws -> CbfComponents { try CbfBuilder() .dataDir(dataDir: Connection.dataDir) @@ -100,11 +122,14 @@ final class KyotoService: BDKSyncService { @discardableResult func startUpdating() async throws -> Bool { - guard let update = await self.client?.update() else { return false } + guard let update = await self.client?.update() else { + isScanRunning = false + return false + } try self.wallet?.applyUpdate(update: update) let _ = try self.wallet?.persist(connection: self.connection ?? Connection.loadConnection()) print("######### walletUpdated") - + isScanRunning = false return true } @@ -133,7 +158,7 @@ final class KyotoService: BDKSyncService { private func updateWarn() { Task { while true { - if let warn = try? await self.client!.nextWarning() { + if let warn = try? await self.client?.nextWarning() { switch warn { case .needConnections: print("######### disconnected") diff --git a/BDKSwiftExampleWallet/Utilities/Constants.swift b/BDKSwiftExampleWallet/Utilities/Constants.swift index 173014c7..dfc3e823 100644 --- a/BDKSwiftExampleWallet/Utilities/Constants.swift +++ b/BDKSwiftExampleWallet/Utilities/Constants.swift @@ -29,9 +29,11 @@ struct Constants { struct Signet { static let bdk = "http://signet.bitcoindevkit.net" static let mutiny = "https://mutinynet.com/api" + static let mempoolspace = "https://mempool.space/signet/api" static let allValues = [ mutiny, bdk, + mempoolspace, ] } struct Testnet { From a8d06f76883d0ea71e1148600956c2f7fa8b9014 Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 18:53:59 -0300 Subject: [PATCH 19/50] clean dead code --- .../Service/BDK Service/BDKService.swift | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 00f212af..5d588472 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -13,15 +13,10 @@ private class BDKService { // private let service: BDKSyncService = KyotoService() private let service: BDKSyncService = EsploraService() - - private var balance: Balance? - private var connection: Connection? - private var esploraClient: EsploraClient private let keyClient: KeyClient private var needsFullScan: Bool = false private(set) var network: Network private(set) var esploraURL: String - private var wallet: Wallet? init(keyClient: KeyClient = .live) { self.keyClient = keyClient @@ -29,8 +24,6 @@ private class BDKService { self.network = Network(stringValue: storedNetworkString ?? "") ?? .signet self.esploraURL = (try? keyClient.getEsploraURL()) ?? self.network.url - - self.esploraClient = EsploraClient(url: self.esploraURL) } func updateNetwork(_ newNetwork: Network) { @@ -51,14 +44,9 @@ private class BDKService { if newURL != self.esploraURL { self.esploraURL = newURL try? keyClient.saveEsploraURL(newURL) - updateEsploraClient() } } - private func updateEsploraClient() { - self.esploraClient = EsploraClient(url: self.esploraURL) - } - func getAddress() throws -> String { try service.getAddress() } From 4a9a4fdd6a7aa60147d916e9c85ce5e2d763981e Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 19:16:47 -0300 Subject: [PATCH 20/50] create live for EsploraService and KyotoService --- BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift | 4 ++-- .../Service/BDKSyncService/EsploraService.swift | 4 ++++ .../Service/BDKSyncService/KyotoService.swift | 6 +++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 5d588472..962a9009 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -11,8 +11,8 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() -// private let service: BDKSyncService = KyotoService() - private let service: BDKSyncService = EsploraService() + private let service: BDKSyncService = KyotoService.live +// private let service: BDKSyncService = EsploraService.live private let keyClient: KeyClient private var needsFullScan: Bool = false private(set) var network: Network diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index 21c03248..8ea424c7 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -8,6 +8,10 @@ import BitcoinDevKit import Foundation +extension EsploraService { + static var live: BDKSyncService = EsploraService() +} + final class EsploraService: BDKSyncService { static let shared = EsploraService() diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index f0513757..49de4b68 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -8,9 +8,13 @@ import BitcoinDevKit import Foundation +extension KyotoService { + static var live: BDKSyncService = KyotoService() +} + final class KyotoService: BDKSyncService { - private static let nodeHeight: UInt32 = 200_000 + private static let nodeHeight: UInt32 = 250_000 static let shared = KyotoService() From f7cf0adc1a31add91771f2b7feae52fd0a5f6a6d Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 19:27:26 -0300 Subject: [PATCH 21/50] test main net --- BDKSwiftExampleWallet/Resources/Localizable.xcstrings | 3 +++ BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift | 4 ++-- .../Service/BDKSyncService/KyotoService.swift | 3 ++- BDKSwiftExampleWallet/Utilities/Constants.swift | 2 +- BDKSwiftExampleWallet/View/OnboardingView.swift | 1 + 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings index 2744a873..958089f5 100644 --- a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings +++ b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings @@ -409,6 +409,9 @@ } } } + }, + "Bitcoin" : { + }, "Bitcoin Balance" : { "localizations" : { diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 962a9009..9354bf50 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -11,8 +11,8 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() - private let service: BDKSyncService = KyotoService.live -// private let service: BDKSyncService = EsploraService.live +// private let service: BDKSyncService = KyotoService.live + private let service: BDKSyncService = EsploraService.live private let keyClient: KeyClient private var needsFullScan: Bool = false private(set) var network: Network diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 49de4b68..48ef7064 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -14,7 +14,8 @@ extension KyotoService { final class KyotoService: BDKSyncService { - private static let nodeHeight: UInt32 = 250_000 +// private static let nodeHeight: UInt32 = 250_000 + private static let nodeHeight: UInt32 = 800_000 static let shared = KyotoService() diff --git a/BDKSwiftExampleWallet/Utilities/Constants.swift b/BDKSwiftExampleWallet/Utilities/Constants.swift index dfc3e823..6d4dfde2 100644 --- a/BDKSwiftExampleWallet/Utilities/Constants.swift +++ b/BDKSwiftExampleWallet/Utilities/Constants.swift @@ -16,8 +16,8 @@ struct Constants { private static let blockstream = "https://blockstream.info/api" private static let mempoolspace = "https://mempool.space/api" static let allValues = [ - mempoolspace, blockstream, + mempoolspace, ] } struct Regtest { diff --git a/BDKSwiftExampleWallet/View/OnboardingView.swift b/BDKSwiftExampleWallet/View/OnboardingView.swift index 62ac3a9e..2bfad1e0 100644 --- a/BDKSwiftExampleWallet/View/OnboardingView.swift +++ b/BDKSwiftExampleWallet/View/OnboardingView.swift @@ -112,6 +112,7 @@ struct OnboardingView: View { Group { Picker("Network", selection: $viewModel.selectedNetwork) { + Text("Bitcoin").tag(Network.bitcoin) Text("Signet").tag(Network.signet) Text("Testnet").tag(Network.testnet) Text("Testnet4").tag(Network.testnet4) From d46e6f3465ef12e47c60240718b9b8c61dfea95e Mon Sep 17 00:00:00 2001 From: Rubens Date: Sun, 25 May 2025 19:50:19 -0300 Subject: [PATCH 22/50] testing main net with bip84 --- .../Service/BDK Service/BDKService.swift | 8 ++++---- .../Service/BDKSyncService/BDKSyncService.swift | 8 ++++---- .../Service/BDKSyncService/EsploraService.swift | 2 +- .../Service/BDKSyncService/KyotoService.swift | 2 +- .../Service/Key Service/KeyService.swift | 4 ++-- .../View Model/OnboardingViewModel.swift | 2 +- .../Service/BDKSwiftExampleWalletKeyServiceTests.swift | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 9354bf50..67c40a3b 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -11,8 +11,8 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() -// private let service: BDKSyncService = KyotoService.live - private let service: BDKSyncService = EsploraService.live + private let service: BDKSyncService = KyotoService.live +// private let service: BDKSyncService = EsploraService.live private let keyClient: KeyClient private var needsFullScan: Bool = false private(set) var network: Network @@ -20,8 +20,8 @@ private class BDKService { init(keyClient: KeyClient = .live) { self.keyClient = keyClient - let storedNetworkString = try? keyClient.getNetwork() ?? Network.signet.description - self.network = Network(stringValue: storedNetworkString ?? "") ?? .signet + let storedNetworkString = try? keyClient.getNetwork() ?? Network.bitcoin.description + self.network = Network(stringValue: storedNetworkString ?? "") ?? .bitcoin self.esploraURL = (try? keyClient.getEsploraURL()) ?? self.network.url } diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 3d6b13ee..95e17a9e 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -66,13 +66,13 @@ extension BDKSyncService { if isXPub(params) { let descriptorPublicKey = try DescriptorPublicKey.fromString(publicKey: params) let fingerprint = descriptorPublicKey.masterFingerprint() - let descriptor = Descriptor.newBip86Public( + let descriptor = Descriptor.newBip84Public( publicKey: descriptorPublicKey, fingerprint: fingerprint, keychain: .external, network: network ) - let changeDescriptor = Descriptor.newBip86Public( + let changeDescriptor = Descriptor.newBip84Public( publicKey: descriptorPublicKey, fingerprint: fingerprint, keychain: .internal, @@ -125,12 +125,12 @@ extension BDKSyncService { mnemonic: mnemonic, password: nil ) - let descriptor = Descriptor.newBip86( + let descriptor = Descriptor.newBip84( secretKey: secretKey, keychain: .external, network: network ) - let changeDescriptor = Descriptor.newBip86( + let changeDescriptor = Descriptor.newBip84( secretKey: secretKey, keychain: .internal, network: network diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index 8ea424c7..1fd34eb1 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -25,7 +25,7 @@ final class EsploraService: BDKSyncService { init( keyClient: KeyClient = .live, - network: Network = .signet, + network: Network = .bitcoin, connection: Connection? = nil ) { self.connection = connection diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 48ef7064..c5ae0f77 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -34,7 +34,7 @@ final class KyotoService: BDKSyncService { init( keyClient: KeyClient = .live, - network: Network = .signet, + network: Network = .bitcoin, connection: Connection? = nil ) { self.connection = connection diff --git a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift index 2949bd2c..be7f1b02 100644 --- a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift +++ b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift @@ -137,12 +137,12 @@ extension KeyClient { mnemonic: mnemonic, password: nil ) - let descriptor = Descriptor.newBip86( + let descriptor = Descriptor.newBip84( secretKey: secretKey, keychain: .external, network: mockKeyClientNetwork ) - let changeDescriptor = Descriptor.newBip86( + let changeDescriptor = Descriptor.newBip84( secretKey: secretKey, keychain: .internal, network: mockKeyClientNetwork diff --git a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift index e503c8ea..c886f05d 100644 --- a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift @@ -26,7 +26,7 @@ class OnboardingViewModel: ObservableObject { } @Published var networkColor = Color.gray @Published var onboardingViewError: AppError? - @Published var selectedNetwork: Network = .signet { + @Published var selectedNetwork: Network = .bitcoin { didSet { bdkClient.updateNetwork(selectedNetwork) selectedURL = availableURLs.first ?? "" diff --git a/BDKSwiftExampleWalletTests/Service/BDKSwiftExampleWalletKeyServiceTests.swift b/BDKSwiftExampleWalletTests/Service/BDKSwiftExampleWalletKeyServiceTests.swift index 3b5f63b8..6a39abf3 100644 --- a/BDKSwiftExampleWalletTests/Service/BDKSwiftExampleWalletKeyServiceTests.swift +++ b/BDKSwiftExampleWalletTests/Service/BDKSwiftExampleWalletKeyServiceTests.swift @@ -23,12 +23,12 @@ final class BDKSwiftExampleWalletKeyServiceTests: XCTestCase { mnemonic: mnemonic, password: nil ) - let descriptor = Descriptor.newBip86( + let descriptor = Descriptor.newBip84( secretKey: secretKey, keychain: .external, network: mockKeyClientNetwork ) - let changeDescriptor = Descriptor.newBip86( + let changeDescriptor = Descriptor.newBip84( secretKey: secretKey, keychain: .internal, network: mockKeyClientNetwork From a6ee9bf109003b68f283cfae7155e1251557ad4e Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 26 May 2025 13:50:53 -0300 Subject: [PATCH 23/50] chore: removed main net chore: back to bip86 wallet --- .../BDK+Extensions/Network+Extensions.swift | 2 +- BDKSwiftExampleWallet/Resources/Localizable.xcstrings | 3 --- .../Service/BDK Service/BDKService.swift | 4 ++-- .../Service/BDKSyncService/BDKSyncService.swift | 8 ++++---- .../Service/BDKSyncService/EsploraService.swift | 2 +- .../Service/BDKSyncService/KyotoService.swift | 5 +++-- .../Service/Key Service/KeyService.swift | 4 ++-- BDKSwiftExampleWallet/Utilities/Constants.swift | 11 ++++++++++- .../View Model/OnboardingViewModel.swift | 2 +- .../View/Activity/TransactionListView.swift | 3 ++- BDKSwiftExampleWallet/View/OnboardingView.swift | 1 - .../BDKSwiftExampleWalletKeyServiceTests.swift | 4 ++-- 12 files changed, 28 insertions(+), 21 deletions(-) diff --git a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Network+Extensions.swift b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Network+Extensions.swift index 37b6febc..8dce2a51 100644 --- a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Network+Extensions.swift +++ b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Network+Extensions.swift @@ -21,7 +21,7 @@ extension Network { init?(stringValue: String) { switch stringValue { - case "bitcoin": self = .bitcoin + case "bitcoin": self = .signet case "testnet": self = .testnet case "testnet4": self = .testnet4 case "signet": self = .signet diff --git a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings index 958089f5..2744a873 100644 --- a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings +++ b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings @@ -409,9 +409,6 @@ } } } - }, - "Bitcoin" : { - }, "Bitcoin Balance" : { "localizations" : { diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 67c40a3b..71087fe2 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -11,8 +11,8 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() - private let service: BDKSyncService = KyotoService.live -// private let service: BDKSyncService = EsploraService.live +// private let service: BDKSyncService = KyotoService.live + private let service: BDKSyncService = EsploraService.live private let keyClient: KeyClient private var needsFullScan: Bool = false private(set) var network: Network diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 95e17a9e..3d6b13ee 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -66,13 +66,13 @@ extension BDKSyncService { if isXPub(params) { let descriptorPublicKey = try DescriptorPublicKey.fromString(publicKey: params) let fingerprint = descriptorPublicKey.masterFingerprint() - let descriptor = Descriptor.newBip84Public( + let descriptor = Descriptor.newBip86Public( publicKey: descriptorPublicKey, fingerprint: fingerprint, keychain: .external, network: network ) - let changeDescriptor = Descriptor.newBip84Public( + let changeDescriptor = Descriptor.newBip86Public( publicKey: descriptorPublicKey, fingerprint: fingerprint, keychain: .internal, @@ -125,12 +125,12 @@ extension BDKSyncService { mnemonic: mnemonic, password: nil ) - let descriptor = Descriptor.newBip84( + let descriptor = Descriptor.newBip86( secretKey: secretKey, keychain: .external, network: network ) - let changeDescriptor = Descriptor.newBip84( + let changeDescriptor = Descriptor.newBip86( secretKey: secretKey, keychain: .internal, network: network diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index 1fd34eb1..8ea424c7 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -25,7 +25,7 @@ final class EsploraService: BDKSyncService { init( keyClient: KeyClient = .live, - network: Network = .bitcoin, + network: Network = .signet, connection: Connection? = nil ) { self.connection = connection diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index c5ae0f77..f1876d86 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -34,7 +34,7 @@ final class KyotoService: BDKSyncService { init( keyClient: KeyClient = .live, - network: Network = .bitcoin, + network: Network = .signet, connection: Connection? = nil ) { self.connection = connection @@ -74,8 +74,9 @@ final class KyotoService: BDKSyncService { throw WalletError.walletNotFound } let nodeComponents = try buildNode( - from: wallet, scanType: .recovery(fromHeight: KyotoService.nodeHeight) + from: wallet, scanType: .recovery(fromHeight: network.taprootHeight) ) + self.fullScanProgress = progress self.client = nodeComponents.client self.node = nodeComponents.node diff --git a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift index be7f1b02..2949bd2c 100644 --- a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift +++ b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift @@ -137,12 +137,12 @@ extension KeyClient { mnemonic: mnemonic, password: nil ) - let descriptor = Descriptor.newBip84( + let descriptor = Descriptor.newBip86( secretKey: secretKey, keychain: .external, network: mockKeyClientNetwork ) - let changeDescriptor = Descriptor.newBip84( + let changeDescriptor = Descriptor.newBip86( secretKey: secretKey, keychain: .internal, network: mockKeyClientNetwork diff --git a/BDKSwiftExampleWallet/Utilities/Constants.swift b/BDKSwiftExampleWallet/Utilities/Constants.swift index 6d4dfde2..2d7b62c8 100644 --- a/BDKSwiftExampleWallet/Utilities/Constants.swift +++ b/BDKSwiftExampleWallet/Utilities/Constants.swift @@ -31,9 +31,9 @@ struct Constants { static let mutiny = "https://mutinynet.com/api" static let mempoolspace = "https://mempool.space/signet/api" static let allValues = [ + mempoolspace, mutiny, bdk, - mempoolspace, ] } struct Testnet { @@ -95,4 +95,13 @@ extension Network { Constants.Config.EsploraServerURLNetwork.Testnet4.allValues.first ?? "" } } + + var taprootHeight: UInt32 { + switch self { + case .bitcoin: + return 700_000 + default: + return 250_000 + } + } } diff --git a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift index c886f05d..e503c8ea 100644 --- a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift @@ -26,7 +26,7 @@ class OnboardingViewModel: ObservableObject { } @Published var networkColor = Color.gray @Published var onboardingViewError: AppError? - @Published var selectedNetwork: Network = .bitcoin { + @Published var selectedNetwork: Network = .signet { didSet { bdkClient.updateNetwork(selectedNetwork) selectedURL = availableURLs.first ?? "" diff --git a/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift b/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift index 43104481..7d913c71 100644 --- a/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift +++ b/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift @@ -36,7 +36,8 @@ struct TransactionListView: View { .font(.subheadline) let mutinyFaucetURL = URL(string: "https://faucet.mutinynet.com") - let signetFaucetURL = URL(string: "https://signetfaucet.com") +// let signetFaucetURL = URL(string: "https://signetfaucet.com") + let signetFaucetURL = URL(string: "https://signet25.bublina.eu.org/") if let mutinyFaucetURL, let signetFaucetURL, diff --git a/BDKSwiftExampleWallet/View/OnboardingView.swift b/BDKSwiftExampleWallet/View/OnboardingView.swift index 2bfad1e0..62ac3a9e 100644 --- a/BDKSwiftExampleWallet/View/OnboardingView.swift +++ b/BDKSwiftExampleWallet/View/OnboardingView.swift @@ -112,7 +112,6 @@ struct OnboardingView: View { Group { Picker("Network", selection: $viewModel.selectedNetwork) { - Text("Bitcoin").tag(Network.bitcoin) Text("Signet").tag(Network.signet) Text("Testnet").tag(Network.testnet) Text("Testnet4").tag(Network.testnet4) diff --git a/BDKSwiftExampleWalletTests/Service/BDKSwiftExampleWalletKeyServiceTests.swift b/BDKSwiftExampleWalletTests/Service/BDKSwiftExampleWalletKeyServiceTests.swift index 6a39abf3..3b5f63b8 100644 --- a/BDKSwiftExampleWalletTests/Service/BDKSwiftExampleWalletKeyServiceTests.swift +++ b/BDKSwiftExampleWalletTests/Service/BDKSwiftExampleWalletKeyServiceTests.swift @@ -23,12 +23,12 @@ final class BDKSwiftExampleWalletKeyServiceTests: XCTestCase { mnemonic: mnemonic, password: nil ) - let descriptor = Descriptor.newBip84( + let descriptor = Descriptor.newBip86( secretKey: secretKey, keychain: .external, network: mockKeyClientNetwork ) - let changeDescriptor = Descriptor.newBip84( + let changeDescriptor = Descriptor.newBip86( secretKey: secretKey, keychain: .internal, network: mockKeyClientNetwork From 37046bf3859821dea106e1054866f4010a549cb3 Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 26 May 2025 16:10:49 -0300 Subject: [PATCH 24/50] feat: added option to in onboard to synchronise using kyoto and saving in keychain --- .../Resources/Localizable.xcstrings | 6 +++ .../Service/BDK Service/BDKService.swift | 37 ++++++++++++++----- .../BDKSyncService/BDKSyncService.swift | 4 ++ .../BDKSyncService/EsploraService.swift | 2 - .../Service/BDKSyncService/KyotoService.swift | 5 --- .../Service/Key Service/KeyService.swift | 27 ++++++++++++-- .../View Model/OnboardingViewModel.swift | 5 +++ .../View/OnboardingView.swift | 37 ++++++++++++------- 8 files changed, 91 insertions(+), 32 deletions(-) diff --git a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings index 2744a873..af56ca61 100644 --- a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings +++ b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings @@ -691,6 +691,9 @@ } } } + }, + "Kyoto" : { + }, "Navigation Title" : { "extractionState" : "stale", @@ -1143,6 +1146,9 @@ } } } + }, + "Sync type" : { + }, "Syncing" : { "extractionState" : "manual", diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 71087fe2..a62039d9 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -11,8 +11,15 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() -// private let service: BDKSyncService = KyotoService.live - private let service: BDKSyncService = EsploraService.live + private var syncMode: SyncMode? + private var service: BDKSyncService { + switch try? keyClient.getSyncMode() { + case .kyoto: + return KyotoService.live + default: + return EsploraService.live + } + } private let keyClient: KeyClient private var needsFullScan: Bool = false private(set) var network: Network @@ -46,6 +53,13 @@ private class BDKService { try? keyClient.saveEsploraURL(newURL) } } + + func updateSyncMode(_ mode: SyncMode) { + if syncMode != mode { + self.syncMode = mode + try? keyClient.saveSyncMode(mode) + } + } func getAddress() throws -> String { try service.getAddress() @@ -114,15 +128,15 @@ private class BDKService { try await service.startFullScan(progress: progress) } - func calculateFee(tx: Transaction) throws -> Amount { + func calculateFee(tx: BitcoinDevKit.Transaction) throws -> Amount { try service.calculateFee(tx: tx) } - func calculateFeeRate(tx: Transaction) throws -> UInt64 { + func calculateFeeRate(tx: BitcoinDevKit.Transaction) throws -> UInt64 { try service.calculateFeeRate(tx: tx) } - func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { + func sentAndReceived(tx: BitcoinDevKit.Transaction) throws -> SentAndReceivedValues { try service.sentAndReceived(tx: tx) } @@ -154,9 +168,9 @@ struct BDKClient { let fullScanWithFullScanProgress: (@escaping FullScanProgress) async throws -> Void let getAddress: () throws -> String let send: (String, UInt64, UInt64) throws -> Void - let calculateFee: (Transaction) throws -> Amount - let calculateFeeRate: (Transaction) throws -> UInt64 - let sentAndReceived: (Transaction) throws -> SentAndReceivedValues + let calculateFee: (BitcoinDevKit.Transaction) throws -> Amount + let calculateFeeRate: (BitcoinDevKit.Transaction) throws -> UInt64 + let sentAndReceived: (BitcoinDevKit.Transaction) throws -> SentAndReceivedValues let buildTransaction: (String, UInt64, UInt64) throws -> Psbt let getBackupInfo: () throws -> BackupInfo let needsFullScan: () -> Bool @@ -166,6 +180,7 @@ struct BDKClient { let updateNetwork: (Network) -> Void let updateEsploraURL: (String) -> Void let stop: () async throws -> Void + let upateSyncMode: (SyncMode) -> Void } extension BDKClient { @@ -221,6 +236,9 @@ extension BDKClient { }, stop: { try await BDKService.shared.stop() + }, + upateSyncMode: { mode in + BDKService.shared.updateSyncMode(mode) } ) } @@ -278,7 +296,8 @@ extension BDKClient { getEsploraURL: { Constants.Config.EsploraServerURLNetwork.Signet.mutiny }, updateNetwork: { _ in }, updateEsploraURL: { _ in }, - stop: { } + stop: { }, + upateSyncMode: { _ in } ) } #endif diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 3d6b13ee..62f1240f 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -11,6 +11,10 @@ import Foundation typealias FullScanProgress = (UInt64) -> Void typealias SyncScanProgress = (UInt64, UInt64) -> Void +enum SyncMode: String { + case esplora, kyoto +} + protocol BDKSyncService { var connection: Connection? { get } var keyClient: KeyClient { get } diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index 8ea424c7..1df0b7d4 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -14,8 +14,6 @@ extension EsploraService { final class EsploraService: BDKSyncService { - static let shared = EsploraService() - var connection: Connection? var keyClient: KeyClient var network: Network diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index f1876d86..7122df5d 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -13,11 +13,6 @@ extension KyotoService { } final class KyotoService: BDKSyncService { - -// private static let nodeHeight: UInt32 = 250_000 - private static let nodeHeight: UInt32 = 800_000 - - static let shared = KyotoService() var connection: Connection? var keyClient: KeyClient diff --git a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift index 2949bd2c..4178ea73 100644 --- a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift +++ b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift @@ -68,6 +68,17 @@ private struct KeyService { try deleteBackupInfo() try deleteEsploraURL() } + + func saveSyncMode(_ mode: SyncMode) throws { + keychain[string: "SyncMode"] = mode.rawValue + } + + func getSyncMode() throws -> SyncMode? { + guard let mode = keychain[string: "SyncMode"] else { + return nil + } + return SyncMode(rawValue: mode) + } } struct KeyClient { @@ -81,6 +92,8 @@ struct KeyClient { let saveBackupInfo: (BackupInfo) throws -> Void let saveNetwork: (String) throws -> Void let deleteAllData: () throws -> Void + let saveSyncMode: (SyncMode) throws -> Void + let getSyncMode: () throws -> SyncMode? private init( deleteBackupInfo: @escaping () throws -> Void, @@ -92,7 +105,9 @@ struct KeyClient { saveBackupInfo: @escaping (BackupInfo) throws -> Void, saveEsploraURL: @escaping (String) throws -> Void, saveNetwork: @escaping (String) throws -> Void, - deleteAllData: @escaping () throws -> Void + deleteAllData: @escaping () throws -> Void, + saveSyncMode: @escaping (SyncMode) throws -> Void, + getSyncMode: @escaping () throws -> SyncMode? ) { self.deleteBackupInfo = deleteBackupInfo self.deleteEsplora = deleteEsplora @@ -104,6 +119,8 @@ struct KeyClient { self.saveEsploraURL = saveEsploraURL self.saveNetwork = saveNetwork self.deleteAllData = deleteAllData + self.saveSyncMode = saveSyncMode + self.getSyncMode = getSyncMode } } @@ -118,7 +135,9 @@ extension KeyClient { saveBackupInfo: { backupInfo in try KeyService().saveBackupInfo(backupInfo: backupInfo) }, saveEsploraURL: { url in try KeyService().saveEsploraURL(url: url) }, saveNetwork: { network in try KeyService().saveNetwork(network: network) }, - deleteAllData: { try KeyService().deletaAllData() } + deleteAllData: { try KeyService().deletaAllData() }, + saveSyncMode: { mode in try KeyService().saveSyncMode(mode) }, + getSyncMode: { try KeyService().getSyncMode() } ) } @@ -159,7 +178,9 @@ extension KeyClient { saveBackupInfo: { _ in }, saveEsploraURL: { _ in }, saveNetwork: { _ in }, - deleteAllData: { } + deleteAllData: { }, + saveSyncMode: { _ in }, + getSyncMode: { .esplora } ) } #endif diff --git a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift index e503c8ea..c730add6 100644 --- a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift @@ -16,6 +16,11 @@ class OnboardingViewModel: ObservableObject { let bdkClient: BDKClient @AppStorage("isOnboarding") var isOnboarding: Bool? + @Published var syncMode: SyncMode? { + didSet { + bdkClient.upateSyncMode(syncMode ?? .esplora) + } + } @Published var createWithPersistError: CreateWithPersistError? var isDescriptor: Bool { words.hasPrefix("tr(") || words.hasPrefix("wpkh(") || words.hasPrefix("wsh(") diff --git a/BDKSwiftExampleWallet/View/OnboardingView.swift b/BDKSwiftExampleWallet/View/OnboardingView.swift index 62ac3a9e..18a7b6b5 100644 --- a/BDKSwiftExampleWallet/View/OnboardingView.swift +++ b/BDKSwiftExampleWallet/View/OnboardingView.swift @@ -122,24 +122,35 @@ struct OnboardingView: View { .opacity(animateContent ? 1 : 0) .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) - Picker("Esplora Server", selection: $viewModel.selectedURL) { - ForEach(viewModel.availableURLs, id: \.self) { url in - Text( - url.replacingOccurrences( - of: "https://", - with: "" - ).replacingOccurrences( - of: "http://", - with: "" - ) - ) - .tag(url) - } + Picker("Sync type", selection: $viewModel.syncMode) { + Text("Esplora Server").tag(SyncMode.esplora) + Text("Kyoto").tag(SyncMode.kyoto) } .pickerStyle(.automatic) .tint(.primary) .opacity(animateContent ? 1 : 0) .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) + + if viewModel.syncMode == nil || viewModel.syncMode == .esplora { + Picker("Esplora Server", selection: $viewModel.selectedURL) { + ForEach(viewModel.availableURLs, id: \.self) { url in + Text( + url.replacingOccurrences( + of: "https://", + with: "" + ).replacingOccurrences( + of: "http://", + with: "" + ) + ) + .tag(url) + } + } + .pickerStyle(.automatic) + .tint(.primary) + .opacity(animateContent ? 1 : 0) + .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) + } } if !viewModel.words.isEmpty { From 19a85181bbb37367c36f688ce3fdecf4ff670917 Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 26 May 2025 16:12:34 -0300 Subject: [PATCH 25/50] organize code --- .../App/BDKSwiftExampleWalletApp.swift | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift b/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift index 7a054f50..c725c495 100644 --- a/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift +++ b/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift @@ -12,17 +12,24 @@ import SwiftUI struct BDKSwiftExampleWalletApp: App { @AppStorage("isOnboarding") var isOnboarding: Bool = true @State private var navigationPath = NavigationPath() - + var body: some Scene { WindowGroup { NavigationStack(path: $navigationPath) { let value = try? KeyClient.live.getBackupInfo() - if isOnboarding && (value == nil) { - OnboardingView(viewModel: .init(bdkClient: .live)) - } else if !isOnboarding && (value == nil) { - OnboardingView(viewModel: .init(bdkClient: .live)) + if value != nil && !isOnboarding { + HomeView( + viewModel: .init( + bdkClient: .live + ), + navigationPath: $navigationPath + ) } else { - HomeView(viewModel: .init(bdkClient: .live), navigationPath: $navigationPath) + OnboardingView( + viewModel: .init( + bdkClient: .live + ) + ) } } .onChange(of: isOnboarding) { oldValue, newValue in From a37b954b8c810c0faf5dc1b5aab9e308559649a3 Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 26 May 2025 17:12:57 -0300 Subject: [PATCH 26/50] added getSyncMode --- .../Service/BDK Service/BDKService.swift | 11 ++++++++++- .../View Model/OnboardingViewModel.swift | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index a62039d9..572f127e 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -60,6 +60,10 @@ private class BDKService { try? keyClient.saveSyncMode(mode) } } + + func getSyncMode() -> SyncMode? { + try? keyClient.getSyncMode() + } func getAddress() throws -> String { try service.getAddress() @@ -181,6 +185,7 @@ struct BDKClient { let updateEsploraURL: (String) -> Void let stop: () async throws -> Void let upateSyncMode: (SyncMode) -> Void + let getSyncMode: () -> SyncMode? } extension BDKClient { @@ -239,6 +244,9 @@ extension BDKClient { }, upateSyncMode: { mode in BDKService.shared.updateSyncMode(mode) + }, + getSyncMode: { + BDKService.shared.getSyncMode() } ) } @@ -297,7 +305,8 @@ extension BDKClient { updateNetwork: { _ in }, updateEsploraURL: { _ in }, stop: { }, - upateSyncMode: { _ in } + upateSyncMode: { _ in }, + getSyncMode: { .esplora } ) } #endif diff --git a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift index c730add6..a59e398c 100644 --- a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift @@ -86,6 +86,7 @@ class OnboardingViewModel: ObservableObject { self.bdkClient = bdkClient self.selectedNetwork = bdkClient.getNetwork() self.selectedURL = bdkClient.getEsploraURL() + self.syncMode = bdkClient.getSyncMode() } func createWallet() { From cc2e69db66a634e24ea0069bf3855c730233d235 Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 26 May 2025 17:21:44 -0300 Subject: [PATCH 27/50] added kyoto in settings --- .../Resources/Localizable.xcstrings | 3 +++ .../Service/BDK Service/BDKService.swift | 4 +-- .../Settings/SettingsViewModel.swift | 11 +++----- .../View/Settings/SettingsView.swift | 26 ++++++++++++------- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings index af56ca61..7a516d1c 100644 --- a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings +++ b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings @@ -1322,6 +1322,9 @@ } } } + }, + "Using Kyoto" : { + }, "Vout: %u" : { "localizations" : { diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 572f127e..213cbc84 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -27,8 +27,8 @@ private class BDKService { init(keyClient: KeyClient = .live) { self.keyClient = keyClient - let storedNetworkString = try? keyClient.getNetwork() ?? Network.bitcoin.description - self.network = Network(stringValue: storedNetworkString ?? "") ?? .bitcoin + let storedNetworkString = try? keyClient.getNetwork() ?? Network.signet.description + self.network = Network(stringValue: storedNetworkString ?? "") ?? .signet self.esploraURL = (try? keyClient.getEsploraURL()) ?? self.network.url } diff --git a/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift b/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift index 8b86c245..72418674 100644 --- a/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift @@ -21,13 +21,7 @@ class SettingsViewModel: ObservableObject { @Published var showingSettingsViewErrorAlert = false @Published var walletSyncState: WalletSyncState = .notStarted - private var updateProgressFullScan: @Sendable (UInt64) -> Void { - { [weak self] inspected in - DispatchQueue.main.async { - self?.inspectedScripts = inspected - } - } - } + let syncMode: SyncMode init( bdkClient: BDKClient = .live @@ -35,6 +29,7 @@ class SettingsViewModel: ObservableObject { self.bdkClient = bdkClient self.network = bdkClient.getNetwork().description self.esploraURL = bdkClient.getEsploraURL() + self.syncMode = bdkClient.getSyncMode() ?? .esplora } func delete() { @@ -46,7 +41,7 @@ class SettingsViewModel: ObservableObject { self.showingSettingsViewErrorAlert = true } } - + func fullScanWithProgress() async { DispatchQueue.main.async { self.walletSyncState = .syncing diff --git a/BDKSwiftExampleWallet/View/Settings/SettingsView.swift b/BDKSwiftExampleWallet/View/Settings/SettingsView.swift index df1aab97..7fc7ecb4 100644 --- a/BDKSwiftExampleWallet/View/Settings/SettingsView.swift +++ b/BDKSwiftExampleWallet/View/Settings/SettingsView.swift @@ -35,16 +35,24 @@ struct SettingsView: View { .foregroundStyle(.secondary) Form { - - Section(header: Text("Network")) { - if let network = viewModel.network, let url = viewModel.esploraURL { - Text( - "\(network.capitalized) • \(url.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "http://", with: ""))" - ) - .foregroundStyle(.primary) + Group { + if viewModel.syncMode == .kyoto { + Section(header: Text("Network")) { + Text("Using Kyoto") + .foregroundStyle(.primary) + } } else { - HStack { - Text("No Network") + Section(header: Text("Network")) { + if let network = viewModel.network, let url = viewModel.esploraURL { + Text( + "\(network.capitalized) • \(url.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "http://", with: ""))" + ) + .foregroundStyle(.primary) + } else { + HStack { + Text("No Network") + } + } } } } From a646078508c1e950fb214810b40781873fad8a0e Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 26 May 2025 17:46:14 -0300 Subject: [PATCH 28/50] fix needFullScan --- BDKSwiftExampleWallet.xcodeproj/project.pbxproj | 4 ++++ .../Service/BDK Service/BDKService.swift | 4 ++-- .../Utilities/AppStorageUtil.swift | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift diff --git a/BDKSwiftExampleWallet.xcodeproj/project.pbxproj b/BDKSwiftExampleWallet.xcodeproj/project.pbxproj index 482bb5ee..a329c2f3 100644 --- a/BDKSwiftExampleWallet.xcodeproj/project.pbxproj +++ b/BDKSwiftExampleWallet.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 77F6A9E52DE24841003568F0 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E42DE24837003568F0 /* URL+Extensions.swift */; }; 77F6A9E72DE248A2003568F0 /* EsploraService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E62DE248A1003568F0 /* EsploraService.swift */; }; 77F6A9E92DE25C9C003568F0 /* KyotoService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E82DE25C98003568F0 /* KyotoService.swift */; }; + 77F6A9EB2DE5072E003568F0 /* AppStorageUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9EA2DE50727003568F0 /* AppStorageUtil.swift */; }; A733D6D02A81113000F333B4 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = A733D6CF2A81113000F333B4 /* Localizable.xcstrings */; }; A73F7A362A3B778E00B87FC6 /* Int+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A73F7A352A3B778E00B87FC6 /* Int+Extensions.swift */; }; AE0C30F72A804A2D008F1EAE /* TransactionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE0C30F62A804A2D008F1EAE /* TransactionListView.swift */; }; @@ -125,6 +126,7 @@ 77F6A9E42DE24837003568F0 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; 77F6A9E62DE248A1003568F0 /* EsploraService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EsploraService.swift; sourceTree = ""; }; 77F6A9E82DE25C98003568F0 /* KyotoService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KyotoService.swift; sourceTree = ""; }; + 77F6A9EA2DE50727003568F0 /* AppStorageUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStorageUtil.swift; sourceTree = ""; }; A733D6CF2A81113000F333B4 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; A73F7A352A3B778E00B87FC6 /* Int+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Extensions.swift"; sourceTree = ""; }; AE0C30F62A804A2D008F1EAE /* TransactionListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionListView.swift; sourceTree = ""; }; @@ -321,6 +323,7 @@ AE1C34202A42441F008F807A /* Utilities */ = { isa = PBXGroup; children = ( + 77F6A9EA2DE50727003568F0 /* AppStorageUtil.swift */, AE79538D2A2D59F000CCB277 /* Constants.swift */, AE2F255C2BED0BFB002A9AC6 /* AppError.swift */, ); @@ -769,6 +772,7 @@ AE6716012A9AC089005C193F /* KeyServiceError.swift in Sources */, 77AD9F062DBB031D00182E65 /* ActivityHomeHeaderView.swift in Sources */, AE0C30FB2A804B95008F1EAE /* WalletViewModel.swift in Sources */, + 77F6A9EB2DE5072E003568F0 /* AppStorageUtil.swift in Sources */, AE49847C2A1BBBD6009951E2 /* BDKSwiftExampleWalletApp.swift in Sources */, AE6715FF2A9AC066005C193F /* FeeServiceError.swift in Sources */, AE2381AD2C60578500F6B00C /* ActivityListView.swift in Sources */, diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 213cbc84..54444f39 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -151,11 +151,11 @@ private class BDKService { extension BDKService { func needsFullScanOfWallet() -> Bool { - return needsFullScan + return StorageUtil.shared.isNeedFullScan ?? true } func setNeedsFullScan(_ value: Bool) { - needsFullScan = value + StorageUtil.shared.isNeedFullScan = value } } diff --git a/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift b/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift new file mode 100644 index 00000000..b0ede79c --- /dev/null +++ b/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift @@ -0,0 +1,14 @@ +// +// AppStorageUtil.swift +// BDKSwiftExampleWallet +// +// Created by Rubens Machion on 26/05/25. +// + +import SwiftUI + +struct StorageUtil { + @AppStorage("isNeedFullScan") var isNeedFullScan: Bool? + + static var shared = StorageUtil() +} From 699385a258d763cc3b4260552114bdaab081f69f Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 26 May 2025 17:48:42 -0300 Subject: [PATCH 29/50] no message --- .../Activity/ActivityListViewModel.swift | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift index 92c8321e..0e1d79ad 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift @@ -72,32 +72,4 @@ class ActivityListViewModel { self.showingWalletViewErrorAlert = true } } - -// private func startSyncWithProgress() async { -// self.walletSyncState = .syncing -// do { -// let inspector = WalletSyncScriptInspector(updateProgress: updateProgress) -// try await bdkClient.syncWithInspector(inspector) -// self.walletSyncState = .synced -// } catch let error as CannotConnectError { -// self.walletViewError = .generic(message: error.localizedDescription) -// self.showingWalletViewErrorAlert = true -// } catch let error as EsploraError { -// self.walletViewError = .generic(message: error.localizedDescription) -// self.showingWalletViewErrorAlert = true -// } catch let error as RequestBuilderError { -// self.walletViewError = .generic(message: error.localizedDescription) -// self.showingWalletViewErrorAlert = true -// } catch let error as PersistenceError { -// self.walletViewError = .generic(message: error.localizedDescription) -// self.showingWalletViewErrorAlert = true -// } catch { -// self.walletSyncState = .error(error) -// self.showingWalletViewErrorAlert = true -// } -// } - -// func syncOrFullScan() async { -// await startSyncWithProgress() -// } } From 05406217217805491c1c347db9ec6800fab274f0 Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 26 May 2025 18:00:42 -0300 Subject: [PATCH 30/50] swift-format --- .../Actor/WalletFullScanScriptInspector.swift | 1 - .../App/BDKSwiftExampleWalletApp.swift | 2 +- .../Connection+Extensions.swift | 4 +- .../Extensions/URL+Extensions.swift | 10 +- .../Service/BDK Service/BDKService.swift | 14 +-- .../BDKSyncService/BDKSyncService.swift | 103 ++++++++++-------- .../BDKSyncService/EsploraService.swift | 26 ++--- .../Service/BDKSyncService/KyotoService.swift | 48 ++++---- .../Service/Key Service/KeyService.swift | 8 +- .../Utilities/AppStorageUtil.swift | 2 +- .../Utilities/Constants.swift | 2 +- .../Settings/SettingsViewModel.swift | 2 +- .../View Model/WalletViewModel.swift | 10 +- .../View/Activity/TransactionListView.swift | 2 +- .../View/Home/ActivityHomeHeaderView.swift | 16 +-- .../View/OnboardingView.swift | 2 +- .../View/Settings/SettingsView.swift | 2 +- BDKSwiftExampleWallet/View/WalletView.swift | 2 +- 18 files changed, 132 insertions(+), 124 deletions(-) diff --git a/BDKSwiftExampleWallet/Actor/WalletFullScanScriptInspector.swift b/BDKSwiftExampleWallet/Actor/WalletFullScanScriptInspector.swift index b28c7112..3a6563a1 100644 --- a/BDKSwiftExampleWallet/Actor/WalletFullScanScriptInspector.swift +++ b/BDKSwiftExampleWallet/Actor/WalletFullScanScriptInspector.swift @@ -5,7 +5,6 @@ // Created by Rubens Machion on 23/04/25. // - import BitcoinDevKit actor WalletFullScanScriptInspector: @preconcurrency FullScanScriptInspector { diff --git a/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift b/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift index c725c495..d32f3b50 100644 --- a/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift +++ b/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift @@ -12,7 +12,7 @@ import SwiftUI struct BDKSwiftExampleWalletApp: App { @AppStorage("isOnboarding") var isOnboarding: Bool = true @State private var navigationPath = NavigationPath() - + var body: some Scene { WindowGroup { NavigationStack(path: $navigationPath) { diff --git a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift index 834c53d5..fcad6623 100644 --- a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift +++ b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Connection+Extensions.swift @@ -7,7 +7,7 @@ extension Connection { let walletDataDirectoryURL = documentsDirectoryURL.appendingPathComponent("wallet_data") return walletDataDirectoryURL.path() } - + static func createConnection() throws -> Connection { let documentsDirectoryURL = URL.documentsDirectory let walletDataDirectoryURL = documentsDirectoryURL.appendingPathComponent("wallet_data") @@ -23,7 +23,7 @@ extension Connection { let connection = try Connection(path: persistenceBackendPath) return connection } - + static func loadConnection() throws -> Connection { let persistenceBackendPath = URL.persistenceBackendPath let connection = try Connection(path: persistenceBackendPath) diff --git a/BDKSwiftExampleWallet/Extensions/URL+Extensions.swift b/BDKSwiftExampleWallet/Extensions/URL+Extensions.swift index fe1b54a6..5800fe4c 100644 --- a/BDKSwiftExampleWallet/Extensions/URL+Extensions.swift +++ b/BDKSwiftExampleWallet/Extensions/URL+Extensions.swift @@ -8,23 +8,23 @@ import Foundation extension URL { - + static var defaultWalletDirectory: URL { URL.documentsDirectory } - + static var walletDirectoryName: String { "wallet_data" } - + static var walletDBName: String { "wallet.sqlite" } - + static var walletDataDirectoryURL: URL { defaultWalletDirectory.appendingPathComponent(walletDirectoryName) } - + static var persistenceBackendPath: String { walletDataDirectoryURL.appendingPathComponent(walletDBName).path } diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 54444f39..e7da0799 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -10,7 +10,7 @@ import Foundation private class BDKService { static var shared: BDKService = BDKService() - + private var syncMode: SyncMode? private var service: BDKSyncService { switch try? keyClient.getSyncMode() { @@ -53,14 +53,14 @@ private class BDKService { try? keyClient.saveEsploraURL(newURL) } } - + func updateSyncMode(_ mode: SyncMode) { if syncMode != mode { self.syncMode = mode try? keyClient.saveSyncMode(mode) } } - + func getSyncMode() -> SyncMode? { try? keyClient.getSyncMode() } @@ -123,11 +123,11 @@ private class BDKService { { try service.buildTransaction(address: address, amount: amount, feeRate: feeRate) } - + func syncWithInspector(progress: @escaping SyncScanProgress) async throws { try await service.startSync(progress: progress) } - + func fullScanWithInspector(progress: @escaping FullScanProgress) async throws { try await service.startFullScan(progress: progress) } @@ -143,7 +143,7 @@ private class BDKService { func sentAndReceived(tx: BitcoinDevKit.Transaction) throws -> SentAndReceivedValues { try service.sentAndReceived(tx: tx) } - + func stop() async throws { try await service.stopService() } @@ -304,7 +304,7 @@ extension BDKClient { getEsploraURL: { Constants.Config.EsploraServerURLNetwork.Signet.mutiny }, updateNetwork: { _ in }, updateEsploraURL: { _ in }, - stop: { }, + stop: {}, upateSyncMode: { _ in }, getSyncMode: { .esplora } ) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 62f1240f..f0fced72 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -20,16 +20,16 @@ protocol BDKSyncService { var keyClient: KeyClient { get } var network: Network { get } var wallet: Wallet? { get } - + func createWallet(params: String?) throws func loadWallet() throws - func deleteWallet() throws + func deleteWallet() throws func startSync(progress: @escaping SyncScanProgress) async throws func startFullScan(progress: @escaping FullScanProgress) async throws - + func updateNetwork(network: Network) func updateEsploraURL(_ url: String) - + func getTransactions() throws -> [CanonicalTx] func getBalance() throws -> Balance func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues @@ -47,25 +47,30 @@ extension BDKSyncService { guard let connection = self.connection else { throw WalletError.dbNotFound } - - let backupInfo = try buildBackupInfo(params: params ?? Mnemonic(wordCount: WordCount.words12).description) + + let backupInfo = try buildBackupInfo( + params: params ?? Mnemonic(wordCount: WordCount.words12).description + ) try keyClient.saveBackupInfo(backupInfo) try keyClient.saveNetwork(self.network.description) let descriptor = try Descriptor(descriptor: backupInfo.descriptor, network: network) - let changeDescriptor = try Descriptor(descriptor: backupInfo.changeDescriptor, network: network) - + let changeDescriptor = try Descriptor( + descriptor: backupInfo.changeDescriptor, + network: network + ) + let wallet = try Wallet( descriptor: descriptor, changeDescriptor: changeDescriptor, network: network, connection: connection ) - + return wallet } - + func buildBackupInfo(params: String) throws -> BackupInfo { if isXPub(params) { let descriptorPublicKey = try DescriptorPublicKey.fromString(publicKey: params) @@ -87,15 +92,15 @@ extension BDKSyncService { changeDescriptor: changeDescriptor.description ) } - - if isDescriptor(params) { // is a descriptor? - + + if isDescriptor(params) { // is a descriptor? + let descriptorStrings = params.components(separatedBy: "\n") .map { $0.split(separator: "#").first?.trimmingCharacters(in: .whitespaces) ?? "" } .filter { !$0.isEmpty } let descriptor: Descriptor let changeDescriptor: Descriptor - + if descriptorStrings.count == 1 { let parsedDescriptor = try Descriptor( descriptor: descriptorStrings[0], @@ -109,17 +114,20 @@ extension BDKSyncService { changeDescriptor = singleDescriptors[1] } else if descriptorStrings.count == 2 { descriptor = try Descriptor(descriptor: descriptorStrings[0], network: network) - changeDescriptor = try Descriptor(descriptor: descriptorStrings[1], network: network) + changeDescriptor = try Descriptor( + descriptor: descriptorStrings[1], + network: network + ) } else { throw AppError.generic(message: "Descriptor parsing failed") } - + return .init( descriptor: descriptor.toStringWithSecret(), changeDescriptor: changeDescriptor.toStringWithSecret() ) } - + let words = !params.isEmpty ? params : Mnemonic(wordCount: WordCount.words12).description guard let mnemonic = try? Mnemonic.fromString(mnemonic: words) else { throw AppError.generic(message: "Invalid mnemonic") @@ -145,34 +153,34 @@ extension BDKSyncService { changeDescriptor: changeDescriptor.toStringWithSecret() ) } - + func deleteWallet() throws { try deleteData() } - + func deleteData() throws { do { try keyClient.deleteAllData() - + if let bundleID = Bundle.main.bundleIdentifier { UserDefaults.standard.removePersistentDomain(forName: bundleID) } - + let walletDataDirectoryURL = URL.walletDataDirectoryURL if FileManager.default.fileExists(atPath: walletDataDirectoryURL.path) { try FileManager.default.removeItem(at: walletDataDirectoryURL) } - + } catch { throw AppError.generic(message: "Failed to remove Keychain data") } } - + func loadWalleFromBackup() throws -> Wallet { guard let connection = self.connection else { throw WalletError.dbNotFound } - + let backupInfo = try keyClient.getBackupInfo() let descriptor = try Descriptor(descriptor: backupInfo.descriptor, network: self.network) let changeDescriptor = try Descriptor( @@ -184,16 +192,16 @@ extension BDKSyncService { changeDescriptor: changeDescriptor, connection: connection ) - + return wallet } - + func getBalance() throws -> Balance { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let balance = wallet.balance() return balance } - + func sentAndReceived(tx: Transaction) throws -> SentAndReceivedValues { guard let wallet = self.wallet else { throw WalletError.walletNotFound @@ -201,7 +209,7 @@ extension BDKSyncService { let values = wallet.sentAndReceived(tx: tx) return values } - + func calculateFeeRate(tx: Transaction) throws -> UInt64 { guard let wallet = self.wallet else { throw WalletError.walletNotFound @@ -209,7 +217,7 @@ extension BDKSyncService { let feeRate = try wallet.calculateFeeRate(tx: tx) return feeRate.toSatPerVbCeil() } - + func calculateFee(tx: Transaction) throws -> Amount { guard let wallet = self.wallet else { throw WalletError.walletNotFound @@ -217,7 +225,7 @@ extension BDKSyncService { let fee = try wallet.calculateFee(tx: tx) return fee } - + func buildTransaction( address: String, amount: UInt64, @@ -235,7 +243,7 @@ extension BDKSyncService { .finish(wallet: wallet) return txBuilder } - + func listUnspent() throws -> [LocalOutput] { guard let wallet = self.wallet else { throw WalletError.walletNotFound @@ -243,7 +251,7 @@ extension BDKSyncService { let localOutputs = wallet.listUnspent() return localOutputs } - + func getAddress() throws -> String { guard let wallet = self.wallet else { throw WalletError.walletNotFound @@ -255,7 +263,7 @@ extension BDKSyncService { let _ = try wallet.persist(connection: connection) return addressInfo.address.description } - + func getTransactions() throws -> [CanonicalTx] { guard let wallet = self.wallet else { throw WalletError.walletNotFound @@ -266,25 +274,24 @@ extension BDKSyncService { } return sortedTransactions } - + // MARK: - Optionals methods - - func updateEsploraURL(_ url: String) { } - - func updateNetwork(network: Network) { } - - func stopService() async throws { } - + + func updateEsploraURL(_ url: String) {} + + func updateNetwork(network: Network) {} + + func stopService() async throws {} + // MARK: - Private - + private func isDescriptor(_ param: String) -> Bool { - param.hasPrefix("tr(") || - param.hasPrefix("wpkh(") || - param.hasPrefix("wsh(") || - param.hasPrefix("sh(") + param.hasPrefix("tr(") || param.hasPrefix("wpkh(") || param.hasPrefix("wsh(") + || param.hasPrefix("sh(") } - + private func isXPub(_ param: String) -> Bool { - param.hasPrefix("xpub") || param.hasPrefix("tpub") || param.hasPrefix("vpub") || param.hasPrefix("zpub") + param.hasPrefix("xpub") || param.hasPrefix("tpub") || param.hasPrefix("vpub") + || param.hasPrefix("zpub") } } diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index 1df0b7d4..aa571e01 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -13,14 +13,14 @@ extension EsploraService { } final class EsploraService: BDKSyncService { - + var connection: Connection? var keyClient: KeyClient var network: Network var wallet: Wallet? - + private var esploraClient: EsploraClient - + init( keyClient: KeyClient = .live, network: Network = .signet, @@ -29,33 +29,33 @@ final class EsploraService: BDKSyncService { self.connection = connection self.keyClient = keyClient self.network = network - + let url = (try? keyClient.getEsploraURL()) ?? network.url self.esploraClient = .init( url: url ) } - + func createWallet(params: String?) throws { self.connection = try Connection.createConnection() self.wallet = try buildWallet(params: params) } - + func loadWallet() throws { self.connection = try Connection.loadConnection() let wallet = try loadWalleFromBackup() self.wallet = wallet } - + func updateNetwork(network: Network) { self.network = network } - + func updateEsploraURL(_ url: String) { try? keyClient.saveEsploraURL(url) self.esploraClient = .init(url: url) } - + func startSync(progress: @escaping SyncScanProgress) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let syncScanInspector = WalletSyncScriptInspector { scripts, total in @@ -75,7 +75,7 @@ final class EsploraService: BDKSyncService { } let _ = try wallet.persist(connection: connection) } - + func startFullScan(progress: @escaping FullScanProgress) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let fullScanInspector = WalletFullScanScriptInspector { inspected in @@ -98,7 +98,7 @@ final class EsploraService: BDKSyncService { } let _ = try wallet.persist(connection: connection) } - + func send( address: String, amount: UInt64, @@ -111,9 +111,9 @@ final class EsploraService: BDKSyncService { ) try signAndBroadcast(psbt: psbt) } - + // MARK: - Private - + private func signAndBroadcast(psbt: Psbt) throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let isSigned = try wallet.sign(psbt: psbt) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 7122df5d..0ac9f201 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -13,20 +13,20 @@ extension KyotoService { } final class KyotoService: BDKSyncService { - + var connection: Connection? var keyClient: KeyClient var network: Network var wallet: Wallet? - + private var client: CbfClient? private var node: CbfNode? private var connected = false private var isScanRunning = false - + private var fullScanProgress: FullScanProgress? private var syncProgress: SyncScanProgress? - + init( keyClient: KeyClient = .live, network: Network = .signet, @@ -36,25 +36,26 @@ final class KyotoService: BDKSyncService { self.keyClient = keyClient self.network = network } - + func createWallet(params: String?) throws { self.connection = try Connection.createConnection() self.wallet = try buildWallet(params: params) } - + func loadWallet() throws { self.connection = try Connection.loadConnection() let wallet = try loadWalleFromBackup() self.wallet = wallet } - + func startSync(progress: @escaping SyncScanProgress) async throws { if isScanRunning { return } guard let wallet = self.wallet else { throw WalletError.walletNotFound } let nodeComponents = try buildNode( - from: wallet, scanType: .sync + from: wallet, + scanType: .sync ) self.syncProgress = progress self.client = nodeComponents.client @@ -62,23 +63,24 @@ final class KyotoService: BDKSyncService { isScanRunning = true try await startListen() } - + func startFullScan(progress: @escaping FullScanProgress) async throws { if isScanRunning { return } guard let wallet = self.wallet else { throw WalletError.walletNotFound } let nodeComponents = try buildNode( - from: wallet, scanType: .recovery(fromHeight: network.taprootHeight) + from: wallet, + scanType: .recovery(fromHeight: network.taprootHeight) ) - + self.fullScanProgress = progress self.client = nodeComponents.client self.node = nodeComponents.node isScanRunning = true try await startListen() } - + func send(address: String, amount: UInt64, feeRate: UInt64) async throws { let psbt = try buildTransaction( address: address, @@ -87,14 +89,14 @@ final class KyotoService: BDKSyncService { ) try await signAndBroadcast(psbt: psbt) } - + func stopService() async throws { isScanRunning = false try await client?.shutdown() } - + // MARK: - Private - + private func signAndBroadcast(psbt: Psbt) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let isSigned = try wallet.sign(psbt: psbt) @@ -105,7 +107,7 @@ final class KyotoService: BDKSyncService { throw WalletError.notSigned } } - + private func buildNode(from wallet: Wallet, scanType: ScanType) throws -> CbfComponents { try CbfBuilder() .dataDir(dataDir: Connection.dataDir) @@ -113,14 +115,14 @@ final class KyotoService: BDKSyncService { .scanType(scanType: scanType) .build(wallet: wallet) } - + private func startListen() async throws { node?.run() printLogs() updateWarn() try await startUpdating() } - + @discardableResult func startUpdating() async throws -> Bool { guard let update = await self.client?.update() else { @@ -133,7 +135,7 @@ final class KyotoService: BDKSyncService { isScanRunning = false return true } - + private func printLogs() { Task { while true { @@ -155,7 +157,7 @@ final class KyotoService: BDKSyncService { } } } - + private func updateWarn() { Task { while true { @@ -165,9 +167,9 @@ final class KyotoService: BDKSyncService { print("######### disconnected") self.connected = false default: -#if DEBUG - print(warn) -#endif + #if DEBUG + print(warn) + #endif } } } diff --git a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift index 4178ea73..c3459145 100644 --- a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift +++ b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift @@ -62,17 +62,17 @@ private struct KeyService { func saveNetwork(network: String) throws { keychain[string: "SelectedNetwork"] = network } - + func deletaAllData() throws { try deleteNetwork() try deleteBackupInfo() try deleteEsploraURL() } - + func saveSyncMode(_ mode: SyncMode) throws { keychain[string: "SyncMode"] = mode.rawValue } - + func getSyncMode() throws -> SyncMode? { guard let mode = keychain[string: "SyncMode"] else { return nil @@ -178,7 +178,7 @@ extension KeyClient { saveBackupInfo: { _ in }, saveEsploraURL: { _ in }, saveNetwork: { _ in }, - deleteAllData: { }, + deleteAllData: {}, saveSyncMode: { _ in }, getSyncMode: { .esplora } ) diff --git a/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift b/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift index b0ede79c..68e475d5 100644 --- a/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift +++ b/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift @@ -9,6 +9,6 @@ import SwiftUI struct StorageUtil { @AppStorage("isNeedFullScan") var isNeedFullScan: Bool? - + static var shared = StorageUtil() } diff --git a/BDKSwiftExampleWallet/Utilities/Constants.swift b/BDKSwiftExampleWallet/Utilities/Constants.swift index 2d7b62c8..b578eefd 100644 --- a/BDKSwiftExampleWallet/Utilities/Constants.swift +++ b/BDKSwiftExampleWallet/Utilities/Constants.swift @@ -95,7 +95,7 @@ extension Network { Constants.Config.EsploraServerURLNetwork.Testnet4.allValues.first ?? "" } } - + var taprootHeight: UInt32 { switch self { case .bitcoin: diff --git a/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift b/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift index 72418674..ec79f9b5 100644 --- a/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift @@ -41,7 +41,7 @@ class SettingsViewModel: ObservableObject { self.showingSettingsViewErrorAlert = true } } - + func fullScanWithProgress() async { DispatchQueue.main.async { self.walletSyncState = .syncing diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index a91613aa..7d2bc5c2 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -102,7 +102,7 @@ class WalletViewModel { await startSyncWithProgress() } } - + private func startSyncWithProgress() async { self.walletSyncState = .syncing do { @@ -127,7 +127,7 @@ class WalletViewModel { self.showingWalletViewErrorAlert = true } } - + private func fullScanWithProgress() async { self.walletSyncState = .syncing do { @@ -149,13 +149,13 @@ class WalletViewModel { self.showingWalletViewErrorAlert = true } } - + private func updateFullProgress(_ progress: UInt64) { DispatchQueue.main.async { [weak self] in self?.inspectedScripts = progress } } - + private func updateSyncProgress(_ inspected: UInt64, _ total: UInt64) { DispatchQueue.main.async { [weak self] in self?.totalScripts = total @@ -163,5 +163,5 @@ class WalletViewModel { self?.progress = total > 0 ? Float(inspected) / Float(total) : 0 } } - + } diff --git a/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift b/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift index 7d913c71..46a0f3f6 100644 --- a/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift +++ b/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift @@ -36,7 +36,7 @@ struct TransactionListView: View { .font(.subheadline) let mutinyFaucetURL = URL(string: "https://faucet.mutinynet.com") -// let signetFaucetURL = URL(string: "https://signetfaucet.com") + // let signetFaucetURL = URL(string: "https://signetfaucet.com") let signetFaucetURL = URL(string: "https://signet25.bublina.eu.org/") if let mutinyFaucetURL, diff --git a/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift b/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift index 2309f52b..2da5b4f8 100644 --- a/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift +++ b/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift @@ -8,20 +8,20 @@ import SwiftUI struct ActivityHomeHeaderView: View { - + let walletSyncState: WalletSyncState let progress: Float let inspectedScripts: UInt64 let totalScripts: UInt64 let needsFullScan: Bool - + let showAllTransactions: () -> Void - + var body: some View { HStack { Text("Activity") Spacer() - + HStack { if needsFullScan { Text("\(inspectedScripts)") @@ -78,7 +78,7 @@ struct ActivityHomeHeaderView: View { } .foregroundStyle(.secondary) .font(.caption) - + if walletSyncState == .synced { Button { self.showAllTransactions() @@ -95,7 +95,7 @@ struct ActivityHomeHeaderView: View { } .fontWeight(.bold) } - + @ViewBuilder private func syncImageIndicator() -> some View { switch walletSyncState { @@ -104,7 +104,7 @@ struct ActivityHomeHeaderView: View { Image(systemName: "checkmark.circle.fill") .foregroundStyle(.green) ) - + case .syncing: AnyView( Image(systemName: "slowmo") @@ -112,7 +112,7 @@ struct ActivityHomeHeaderView: View { .variableColor.cumulative ) ) - + case .notStarted: AnyView( Image(systemName: "arrow.clockwise") diff --git a/BDKSwiftExampleWallet/View/OnboardingView.swift b/BDKSwiftExampleWallet/View/OnboardingView.swift index 18a7b6b5..f8496ad0 100644 --- a/BDKSwiftExampleWallet/View/OnboardingView.swift +++ b/BDKSwiftExampleWallet/View/OnboardingView.swift @@ -130,7 +130,7 @@ struct OnboardingView: View { .tint(.primary) .opacity(animateContent ? 1 : 0) .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) - + if viewModel.syncMode == nil || viewModel.syncMode == .esplora { Picker("Esplora Server", selection: $viewModel.selectedURL) { ForEach(viewModel.availableURLs, id: \.self) { url in diff --git a/BDKSwiftExampleWallet/View/Settings/SettingsView.swift b/BDKSwiftExampleWallet/View/Settings/SettingsView.swift index 7fc7ecb4..1ed27fb9 100644 --- a/BDKSwiftExampleWallet/View/Settings/SettingsView.swift +++ b/BDKSwiftExampleWallet/View/Settings/SettingsView.swift @@ -39,7 +39,7 @@ struct SettingsView: View { if viewModel.syncMode == .kyoto { Section(header: Text("Network")) { Text("Using Kyoto") - .foregroundStyle(.primary) + .foregroundStyle(.primary) } } else { Section(header: Text("Network")) { diff --git a/BDKSwiftExampleWallet/View/WalletView.swift b/BDKSwiftExampleWallet/View/WalletView.swift index c6e59eff..010a6223 100644 --- a/BDKSwiftExampleWallet/View/WalletView.swift +++ b/BDKSwiftExampleWallet/View/WalletView.swift @@ -51,7 +51,7 @@ struct WalletView: View { ) { showAllTransactions = true } - + TransactionListView( viewModel: .init(), transactions: viewModel.recentTransactions, From 59e039e485a6ce8d85be529af36643414f505b57 Mon Sep 17 00:00:00 2001 From: Rubens Date: Thu, 5 Jun 2025 18:58:21 -0300 Subject: [PATCH 31/50] fix: using concrete type for EsploraService and Kyoto --- .../project.pbxproj | 8 + .../App/BDKSwiftExampleWalletApp.swift | 6 +- .../BDK Service/BDKClient+Esplora.swift | 91 ++++++++ .../Service/BDK Service/BDKClient+Kyoto.swift | 91 ++++++++ .../Service/BDK Service/BDKService.swift | 207 ++++++------------ .../BDKSyncService/EsploraService.swift | 2 + .../Service/BDKSyncService/KyotoService.swift | 2 + .../Activity/ActivityListViewModel.swift | 2 +- .../Activity/TransactionDetailViewModel.swift | 2 +- .../Activity/TransactionListViewModel.swift | 2 +- .../View Model/HomeViewModel.swift | 2 +- .../View Model/OnboardingViewModel.swift | 2 +- .../View Model/Receive/ReceiveViewModel.swift | 2 +- .../View Model/Send/AmountViewModel.swift | 2 +- .../Send/BuildTransactionViewModel.swift | 2 +- .../View Model/Send/FeeViewModel.swift | 2 +- .../Settings/SettingsViewModel.swift | 2 +- .../Settings/WalletRecoveryViewModel.swift | 2 +- .../View Model/WalletViewModel.swift | 2 +- .../View/Activity/TransactionListView.swift | 2 +- BDKSwiftExampleWallet/View/HomeView.swift | 2 +- 21 files changed, 276 insertions(+), 159 deletions(-) create mode 100644 BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Esplora.swift create mode 100644 BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Kyoto.swift diff --git a/BDKSwiftExampleWallet.xcodeproj/project.pbxproj b/BDKSwiftExampleWallet.xcodeproj/project.pbxproj index a329c2f3..ef6d16a1 100644 --- a/BDKSwiftExampleWallet.xcodeproj/project.pbxproj +++ b/BDKSwiftExampleWallet.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 779E70872DB9C98A006E22D3 /* WalletSyncScriptInspector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 779E70862DB9C98A006E22D3 /* WalletSyncScriptInspector.swift */; }; 779E70892DB9C9AB006E22D3 /* WalletFullScanScriptInspector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 779E70882DB9C9AB006E22D3 /* WalletFullScanScriptInspector.swift */; }; 77AD9F062DBB031D00182E65 /* ActivityHomeHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77AD9F052DBB031D00182E65 /* ActivityHomeHeaderView.swift */; }; + 77DB13092DEBB4AC004B735D /* BDKClient+Kyoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77DB13082DEBB4A9004B735D /* BDKClient+Kyoto.swift */; }; + 77DB130B2DEBB553004B735D /* BDKClient+Esplora.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77DB130A2DEBB550004B735D /* BDKClient+Esplora.swift */; }; 77F0FDC92DA9A93D00B30E4F /* Connection+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F0FDC82DA9A93700B30E4F /* Connection+Extensions.swift */; }; 77F6A9E32DE247B2003568F0 /* BDKSyncService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E22DE247AD003568F0 /* BDKSyncService.swift */; }; 77F6A9E52DE24841003568F0 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E42DE24837003568F0 /* URL+Extensions.swift */; }; @@ -121,6 +123,8 @@ 779E70862DB9C98A006E22D3 /* WalletSyncScriptInspector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletSyncScriptInspector.swift; sourceTree = ""; }; 779E70882DB9C9AB006E22D3 /* WalletFullScanScriptInspector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletFullScanScriptInspector.swift; sourceTree = ""; }; 77AD9F052DBB031D00182E65 /* ActivityHomeHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityHomeHeaderView.swift; sourceTree = ""; }; + 77DB13082DEBB4A9004B735D /* BDKClient+Kyoto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BDKClient+Kyoto.swift"; sourceTree = ""; }; + 77DB130A2DEBB550004B735D /* BDKClient+Esplora.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BDKClient+Esplora.swift"; sourceTree = ""; }; 77F0FDC82DA9A93700B30E4F /* Connection+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Extensions.swift"; sourceTree = ""; }; 77F6A9E22DE247AD003568F0 /* BDKSyncService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BDKSyncService.swift; sourceTree = ""; }; 77F6A9E42DE24837003568F0 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; @@ -333,6 +337,8 @@ AE1C34212A424434008F807A /* BDK Service */ = { isa = PBXGroup; children = ( + 77DB130A2DEBB550004B735D /* BDKClient+Esplora.swift */, + 77DB13082DEBB4A9004B735D /* BDKClient+Kyoto.swift */, AED4CC092A1D297600CE1831 /* BDKService.swift */, AE79538F2A2D5B4400CCB277 /* BDKSwiftExampleWalletError.swift */, ); @@ -772,9 +778,11 @@ AE6716012A9AC089005C193F /* KeyServiceError.swift in Sources */, 77AD9F062DBB031D00182E65 /* ActivityHomeHeaderView.swift in Sources */, AE0C30FB2A804B95008F1EAE /* WalletViewModel.swift in Sources */, + 77DB130B2DEBB553004B735D /* BDKClient+Esplora.swift in Sources */, 77F6A9EB2DE5072E003568F0 /* AppStorageUtil.swift in Sources */, AE49847C2A1BBBD6009951E2 /* BDKSwiftExampleWalletApp.swift in Sources */, AE6715FF2A9AC066005C193F /* FeeServiceError.swift in Sources */, + 77DB13092DEBB4AC004B735D /* BDKClient+Kyoto.swift in Sources */, AE2381AD2C60578500F6B00C /* ActivityListView.swift in Sources */, AE2381B32C60877600F6B00C /* LocalOutputListView.swift in Sources */, AE783A052AB4F51F005F0CBA /* String+Extensions.swift in Sources */, diff --git a/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift b/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift index d32f3b50..9ed32d7c 100644 --- a/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift +++ b/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift @@ -20,20 +20,20 @@ struct BDKSwiftExampleWalletApp: App { if value != nil && !isOnboarding { HomeView( viewModel: .init( - bdkClient: .live + bdkClient: .esplora ), navigationPath: $navigationPath ) } else { OnboardingView( viewModel: .init( - bdkClient: .live + bdkClient: .esplora ) ) } } .onChange(of: isOnboarding) { oldValue, newValue in - BDKClient.live.setNeedsFullScan(true) + BDKClient.esplora.setNeedsFullScan(true) navigationPath = NavigationPath() } } diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Esplora.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Esplora.swift new file mode 100644 index 00000000..3f243883 --- /dev/null +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Esplora.swift @@ -0,0 +1,91 @@ +// +// BDKClient+Esplora.swift +// BDKSwiftExampleWallet +// +// Created by Rubens Machion on 31/05/25. +// + +extension BDKClient { + static let esplora = Self( + loadWallet: { + try EsploraService.shared.loadWallet() + }, + deleteWallet: { + try EsploraService.shared.deleteWallet() + }, + createWalletFromSeed: { words in + try EsploraService.shared.createWallet(params: words) + }, + createWalletFromDescriptor: { descriptor in + try EsploraService.shared.createWallet(params: descriptor) + }, + createWalletFromXPub: { xpub in + try EsploraService.shared.createWallet(params: xpub) + }, + getBalance: { + try EsploraService.shared.getBalance() + }, + transactions: { + try EsploraService.shared.getTransactions() + }, + listUnspent: { + try EsploraService.shared.listUnspent() + }, + syncScanWithSyncScanProgress: { progress in + try await EsploraService.shared.startSync(progress: progress) + }, + fullScanWithFullScanProgress: { progress in + try await EsploraService.shared.startFullScan(progress: progress) + }, + getAddress: { + try EsploraService.shared.getAddress() + }, + send: { (address, amount, feeRate) in + Task { + try await EsploraService.shared.send(address: address, amount: amount, feeRate: feeRate) + } + }, + calculateFee: { tx in + try EsploraService.shared.calculateFee(tx: tx) + }, + calculateFeeRate: { tx in + try EsploraService.shared.calculateFeeRate(tx: tx) + }, + sentAndReceived: { tx in + try EsploraService.shared.sentAndReceived(tx: tx) + }, + buildTransaction: { (address, amount, feeRate) in + try EsploraService.shared.buildTransaction(address: address, amount: amount, feeRate: feeRate) + }, + getBackupInfo: { + try BDKService.shared.getBackupInfo() + }, + needsFullScan: { + BDKService.shared.needsFullScanOfWallet() + }, + setNeedsFullScan: { value in + BDKService.shared.setNeedsFullScan(value) + }, + getNetwork: { + BDKService.shared.network + }, + getEsploraURL: { + BDKService.shared.esploraURL + }, + updateNetwork: { newNetwork in + BDKService.shared.updateNetwork(newNetwork) + }, + updateEsploraURL: { newURL in + BDKService.shared.updateEsploraURL(newURL) + }, + stop: { + try await EsploraService.shared.stopService() + }, + upateSyncMode: { mode in + BDKService.shared.updateSyncMode(mode) + }, + getSyncMode: { + BDKService.shared.getSyncMode() + } + ) +} diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Kyoto.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Kyoto.swift new file mode 100644 index 00000000..1717e163 --- /dev/null +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Kyoto.swift @@ -0,0 +1,91 @@ +// +// BDKClient+.swift +// BDKSwiftExampleWallet +// +// Created by Rubens Machion on 31/05/25. +// + +extension BDKClient { + static let kyoto = Self( + loadWallet: { + try KyotoService.shared.loadWallet() + }, + deleteWallet: { + try KyotoService.shared.deleteWallet() + }, + createWalletFromSeed: { words in + try KyotoService.shared.createWallet(params: words) + }, + createWalletFromDescriptor: { descriptor in + try KyotoService.shared.createWallet(params: descriptor) + }, + createWalletFromXPub: { xpub in + try KyotoService.shared.createWallet(params: xpub) + }, + getBalance: { + try KyotoService.shared.getBalance() + }, + transactions: { + try KyotoService.shared.getTransactions() + }, + listUnspent: { + try KyotoService.shared.listUnspent() + }, + syncScanWithSyncScanProgress: { progress in + try await KyotoService.shared.startSync(progress: progress) + }, + fullScanWithFullScanProgress: { progress in + try await KyotoService.shared.startFullScan(progress: progress) + }, + getAddress: { + try KyotoService.shared.getAddress() + }, + send: { (address, amount, feeRate) in + Task { + try await KyotoService.shared.send(address: address, amount: amount, feeRate: feeRate) + } + }, + calculateFee: { tx in + try KyotoService.shared.calculateFee(tx: tx) + }, + calculateFeeRate: { tx in + try KyotoService.shared.calculateFeeRate(tx: tx) + }, + sentAndReceived: { tx in + try KyotoService.shared.sentAndReceived(tx: tx) + }, + buildTransaction: { (address, amount, feeRate) in + try KyotoService.shared.buildTransaction(address: address, amount: amount, feeRate: feeRate) + }, + getBackupInfo: { + try BDKService.shared.getBackupInfo() + }, + needsFullScan: { + BDKService.shared.needsFullScanOfWallet() + }, + setNeedsFullScan: { value in + BDKService.shared.setNeedsFullScan(value) + }, + getNetwork: { + BDKService.shared.network + }, + getEsploraURL: { + BDKService.shared.esploraURL + }, + updateNetwork: { newNetwork in + BDKService.shared.updateNetwork(newNetwork) + }, + updateEsploraURL: { newURL in + BDKService.shared.updateEsploraURL(newURL) + }, + stop: { + try await KyotoService.shared.stopService() + }, + upateSyncMode: { mode in + BDKService.shared.updateSyncMode(mode) + }, + getSyncMode: { + BDKService.shared.getSyncMode() + } + ) +} diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index e7da0799..4c163985 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -8,16 +8,16 @@ import BitcoinDevKit import Foundation -private class BDKService { +class BDKService { static var shared: BDKService = BDKService() private var syncMode: SyncMode? - private var service: BDKSyncService { + private var service: BDKClient { switch try? keyClient.getSyncMode() { case .kyoto: - return KyotoService.live + return .kyoto default: - return EsploraService.live + return .esplora } } private let keyClient: KeyClient @@ -65,88 +65,10 @@ private class BDKService { try? keyClient.getSyncMode() } - func getAddress() throws -> String { - try service.getAddress() - } - - func getBalance() throws -> Balance { - try service.getBalance() - } - - func transactions() throws -> [CanonicalTx] { - try service.getTransactions() - } - - func listUnspent() throws -> [LocalOutput] { - try service.listUnspent() - } - - func createWallet(words: String?) throws { - try service.createWallet(params: words) - } - - func createWallet(descriptor: String?) throws { - try service.createWallet(params: descriptor) - } - - func createWallet(xpub: String?) throws { - try service.createWallet(params: xpub) - } - - func loadWalletFromBackup() throws { - try service.loadWallet() - } - - func deleteWallet() throws { - Task { - try await service.stopService() - } - try service.deleteWallet() - needsFullScan = true - } - func getBackupInfo() throws -> BackupInfo { let backupInfo = try keyClient.getBackupInfo() return backupInfo } - - func send( - address: String, - amount: UInt64, - feeRate: UInt64 - ) async throws { - try await service.send(address: address, amount: amount, feeRate: feeRate) - } - - func buildTransaction(address: String, amount: UInt64, feeRate: UInt64) throws - -> Psbt - { - try service.buildTransaction(address: address, amount: amount, feeRate: feeRate) - } - - func syncWithInspector(progress: @escaping SyncScanProgress) async throws { - try await service.startSync(progress: progress) - } - - func fullScanWithInspector(progress: @escaping FullScanProgress) async throws { - try await service.startFullScan(progress: progress) - } - - func calculateFee(tx: BitcoinDevKit.Transaction) throws -> Amount { - try service.calculateFee(tx: tx) - } - - func calculateFeeRate(tx: BitcoinDevKit.Transaction) throws -> UInt64 { - try service.calculateFeeRate(tx: tx) - } - - func sentAndReceived(tx: BitcoinDevKit.Transaction) throws -> SentAndReceivedValues { - try service.sentAndReceived(tx: tx) - } - - func stop() async throws { - try await service.stopService() - } } extension BDKService { @@ -189,66 +111,67 @@ struct BDKClient { } extension BDKClient { - static let live = Self( - loadWallet: { try BDKService.shared.loadWalletFromBackup() }, - deleteWallet: { try BDKService.shared.deleteWallet() }, - createWalletFromSeed: { words in try BDKService.shared.createWallet(words: words) }, - createWalletFromDescriptor: { descriptor in - try BDKService.shared.createWallet(descriptor: descriptor) - }, - createWalletFromXPub: { xpub in - try BDKService.shared.createWallet(xpub: xpub) - }, - getBalance: { try BDKService.shared.getBalance() }, - transactions: { try BDKService.shared.transactions() }, - listUnspent: { try BDKService.shared.listUnspent() }, - syncScanWithSyncScanProgress: { progress in - try await BDKService.shared.syncWithInspector(progress: progress) - }, - fullScanWithFullScanProgress: { progress in - try await BDKService.shared.fullScanWithInspector(progress: progress) - }, - getAddress: { try BDKService.shared.getAddress() }, - send: { (address, amount, feeRate) in - Task { - try await BDKService.shared.send(address: address, amount: amount, feeRate: feeRate) - } - }, - calculateFee: { tx in try BDKService.shared.calculateFee(tx: tx) }, - calculateFeeRate: { tx in try BDKService.shared.calculateFeeRate(tx: tx) }, - sentAndReceived: { tx in try BDKService.shared.sentAndReceived(tx: tx) }, - buildTransaction: { (address, amount, feeRate) in - try BDKService.shared.buildTransaction( - address: address, - amount: amount, - feeRate: feeRate - ) - }, - getBackupInfo: { try BDKService.shared.getBackupInfo() }, - needsFullScan: { BDKService.shared.needsFullScanOfWallet() }, - setNeedsFullScan: { value in BDKService.shared.setNeedsFullScan(value) }, - getNetwork: { - BDKService.shared.network - }, - getEsploraURL: { - BDKService.shared.esploraURL - }, - updateNetwork: { newNetwork in - BDKService.shared.updateNetwork(newNetwork) - }, - updateEsploraURL: { newURL in - BDKService.shared.updateEsploraURL(newURL) - }, - stop: { - try await BDKService.shared.stop() - }, - upateSyncMode: { mode in - BDKService.shared.updateSyncMode(mode) - }, - getSyncMode: { - BDKService.shared.getSyncMode() - } - ) + // MARK: - live +// static let live = Self( +// loadWallet: { try BDKService.shared.loadWalletFromBackup() }, +// deleteWallet: { try BDKService.shared.deleteWallet() }, +// createWalletFromSeed: { words in try BDKService.shared.createWallet(words: words) }, +// createWalletFromDescriptor: { descriptor in +// try BDKService.shared.createWallet(descriptor: descriptor) +// }, +// createWalletFromXPub: { xpub in +// try BDKService.shared.createWallet(xpub: xpub) +// }, +// getBalance: { try BDKService.shared.getBalance() }, +// transactions: { try BDKService.shared.transactions() }, +// listUnspent: { try BDKService.shared.listUnspent() }, +// syncScanWithSyncScanProgress: { progress in +// try await BDKService.shared.syncWithInspector(progress: progress) +// }, +// fullScanWithFullScanProgress: { progress in +// try await BDKService.shared.fullScanWithInspector(progress: progress) +// }, +// getAddress: { try BDKService.shared.getAddress() }, +// send: { (address, amount, feeRate) in +// Task { +// try await BDKService.shared.send(address: address, amount: amount, feeRate: feeRate) +// } +// }, +// calculateFee: { tx in try BDKService.shared.calculateFee(tx: tx) }, +// calculateFeeRate: { tx in try BDKService.shared.calculateFeeRate(tx: tx) }, +// sentAndReceived: { tx in try BDKService.shared.sentAndReceived(tx: tx) }, +// buildTransaction: { (address, amount, feeRate) in +// try BDKService.shared.buildTransaction( +// address: address, +// amount: amount, +// feeRate: feeRate +// ) +// }, +// getBackupInfo: { try BDKService.shared.getBackupInfo() }, +// needsFullScan: { BDKService.shared.needsFullScanOfWallet() }, +// setNeedsFullScan: { value in BDKService.shared.setNeedsFullScan(value) }, +// getNetwork: { +// BDKService.shared.network +// }, +// getEsploraURL: { +// BDKService.shared.esploraURL +// }, +// updateNetwork: { newNetwork in +// BDKService.shared.updateNetwork(newNetwork) +// }, +// updateEsploraURL: { newURL in +// BDKService.shared.updateEsploraURL(newURL) +// }, +// stop: { +// try await BDKService.shared.stop() +// }, +// upateSyncMode: { mode in +// BDKService.shared.updateSyncMode(mode) +// }, +// getSyncMode: { +// BDKService.shared.getSyncMode() +// } +// ) } #if DEBUG diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index aa571e01..b1b35540 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -14,6 +14,8 @@ extension EsploraService { final class EsploraService: BDKSyncService { + static let shared: EsploraService = .init() + var connection: Connection? var keyClient: KeyClient var network: Network diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 0ac9f201..898b777a 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -14,6 +14,8 @@ extension KyotoService { final class KyotoService: BDKSyncService { + static let shared: KyotoService = .init() + var connection: Connection? var keyClient: KeyClient var network: Network diff --git a/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift index 0e1d79ad..2881a2f2 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift @@ -39,7 +39,7 @@ class ActivityListViewModel { } init( - bdkClient: BDKClient = .live, + bdkClient: BDKClient = .esplora, transactions: [CanonicalTx] = [], walletSyncState: WalletSyncState = .notStarted ) { diff --git a/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift index e500e67e..e2c882e9 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift @@ -23,7 +23,7 @@ class TransactionDetailViewModel { var transactionDetailsError: AppError? init( - bdkClient: BDKClient = .live + bdkClient: BDKClient = .esplora ) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift index 18a3d6a3..fb2940ab 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift @@ -17,7 +17,7 @@ class TransactionListViewModel { var walletTransactionsViewError: AppError? init( - bdkClient: BDKClient = .live + bdkClient: BDKClient = .esplora ) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/HomeViewModel.swift b/BDKSwiftExampleWallet/View Model/HomeViewModel.swift index 1c2e2687..ee616f2d 100644 --- a/BDKSwiftExampleWallet/View Model/HomeViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/HomeViewModel.swift @@ -17,7 +17,7 @@ class HomeViewModel: ObservableObject { var isWalletLoaded = false var showingHomeViewErrorAlert = false - init(bdkClient: BDKClient = .live) { + init(bdkClient: BDKClient = .esplora) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift index a59e398c..42ea621b 100644 --- a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift @@ -81,7 +81,7 @@ class OnboardingViewModel: ObservableObject { } init( - bdkClient: BDKClient = .live + bdkClient: BDKClient = .esplora ) { self.bdkClient = bdkClient self.selectedNetwork = bdkClient.getNetwork() diff --git a/BDKSwiftExampleWallet/View Model/Receive/ReceiveViewModel.swift b/BDKSwiftExampleWallet/View Model/Receive/ReceiveViewModel.swift index 7bc403e4..7001f3ef 100644 --- a/BDKSwiftExampleWallet/View Model/Receive/ReceiveViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Receive/ReceiveViewModel.swift @@ -20,7 +20,7 @@ class ReceiveViewModel: NSObject, NFCNDEFReaderSessionDelegate { var receiveViewError: AppError? var showingReceiveViewErrorAlert = false - init(bdkClient: BDKClient = .live) { + init(bdkClient: BDKClient = .esplora) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Send/AmountViewModel.swift b/BDKSwiftExampleWallet/View Model/Send/AmountViewModel.swift index 64fbbade..d0fb4be8 100644 --- a/BDKSwiftExampleWallet/View Model/Send/AmountViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Send/AmountViewModel.swift @@ -18,7 +18,7 @@ class AmountViewModel { var balanceTotal: UInt64? var showingAmountViewErrorAlert = false - init(bdkClient: BDKClient = .live) { + init(bdkClient: BDKClient = .esplora) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift b/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift index 0d28276a..048d5f6c 100644 --- a/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift @@ -19,7 +19,7 @@ class BuildTransactionViewModel { var showingBuildTransactionViewErrorAlert = false init( - bdkClient: BDKClient = .live + bdkClient: BDKClient = .esplora ) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Send/FeeViewModel.swift b/BDKSwiftExampleWallet/View Model/Send/FeeViewModel.swift index 835a033e..508836ed 100644 --- a/BDKSwiftExampleWallet/View Model/Send/FeeViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Send/FeeViewModel.swift @@ -37,7 +37,7 @@ class FeeViewModel { var recommendedFees: RecommendedFees? var showingFeeViewErrorAlert = false - init(feeClient: FeeClient = .live, bdkClient: BDKClient = .live) { + init(feeClient: FeeClient = .live, bdkClient: BDKClient = .esplora) { self.feeClient = feeClient self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift b/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift index ec79f9b5..07ac9c22 100644 --- a/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift @@ -24,7 +24,7 @@ class SettingsViewModel: ObservableObject { let syncMode: SyncMode init( - bdkClient: BDKClient = .live + bdkClient: BDKClient = .esplora ) { self.bdkClient = bdkClient self.network = bdkClient.getNetwork().description diff --git a/BDKSwiftExampleWallet/View Model/Settings/WalletRecoveryViewModel.swift b/BDKSwiftExampleWallet/View Model/Settings/WalletRecoveryViewModel.swift index 0d613d2b..882d43a0 100644 --- a/BDKSwiftExampleWallet/View Model/Settings/WalletRecoveryViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Settings/WalletRecoveryViewModel.swift @@ -21,7 +21,7 @@ class WalletRecoveryViewModel { var showingWalletRecoveryViewErrorAlert: Bool init( - bdkClient: BDKClient = .live, + bdkClient: BDKClient = .esplora, backupInfo: BackupInfo? = nil, walletRecoveryViewError: AppError? = nil, showingWalletRecoveryViewErrorAlert: Bool = false diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index 7d2bc5c2..a8bc73d1 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -44,7 +44,7 @@ class WalletViewModel { } init( - bdkClient: BDKClient = .live, + bdkClient: BDKClient = .esplora, keyClient: KeyClient = .live, priceClient: PriceClient = .live, transactions: [CanonicalTx] = [], diff --git a/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift b/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift index 46a0f3f6..9c5aa428 100644 --- a/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift +++ b/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift @@ -100,7 +100,7 @@ struct TransactionListView: View { NavigationLink( destination: TransactionDetailView( viewModel: .init( - bdkClient: .live + bdkClient: .esplora ), amount: sentAndReceivedValues.sent.toSat() == 0 ? sentAndReceivedValues.received.toSat() diff --git a/BDKSwiftExampleWallet/View/HomeView.swift b/BDKSwiftExampleWallet/View/HomeView.swift index 119e31b6..52ebbf2d 100644 --- a/BDKSwiftExampleWallet/View/HomeView.swift +++ b/BDKSwiftExampleWallet/View/HomeView.swift @@ -17,7 +17,7 @@ struct HomeView: View { WalletView( viewModel: .init( - bdkClient: .live, + bdkClient: .esplora, priceClient: .live ), sendNavigationPath: $navigationPath From 7b7e2ab62aa7b8691ca587a19a92038140fa6b8b Mon Sep 17 00:00:00 2001 From: Rubens Date: Thu, 5 Jun 2025 19:49:50 -0300 Subject: [PATCH 32/50] added .live --- .../App/BDKSwiftExampleWalletApp.swift | 6 +- .../Service/BDK Service/BDKService.swift | 83 +++---------------- .../Activity/ActivityListViewModel.swift | 2 +- .../Activity/TransactionDetailViewModel.swift | 2 +- .../Activity/TransactionListViewModel.swift | 2 +- .../View Model/HomeViewModel.swift | 2 +- .../View Model/Receive/ReceiveViewModel.swift | 2 +- .../View Model/Send/AmountViewModel.swift | 2 +- .../Send/BuildTransactionViewModel.swift | 2 +- .../View Model/Send/FeeViewModel.swift | 2 +- .../Settings/SettingsViewModel.swift | 2 +- .../Settings/WalletRecoveryViewModel.swift | 2 +- .../View Model/WalletViewModel.swift | 2 +- .../View/Activity/TransactionListView.swift | 2 +- BDKSwiftExampleWallet/View/HomeView.swift | 2 +- 15 files changed, 29 insertions(+), 86 deletions(-) diff --git a/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift b/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift index 9ed32d7c..d32f3b50 100644 --- a/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift +++ b/BDKSwiftExampleWallet/App/BDKSwiftExampleWalletApp.swift @@ -20,20 +20,20 @@ struct BDKSwiftExampleWalletApp: App { if value != nil && !isOnboarding { HomeView( viewModel: .init( - bdkClient: .esplora + bdkClient: .live ), navigationPath: $navigationPath ) } else { OnboardingView( viewModel: .init( - bdkClient: .esplora + bdkClient: .live ) ) } } .onChange(of: isOnboarding) { oldValue, newValue in - BDKClient.esplora.setNeedsFullScan(true) + BDKClient.live.setNeedsFullScan(true) navigationPath = NavigationPath() } } diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 4c163985..3608c0f9 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -12,15 +12,7 @@ class BDKService { static var shared: BDKService = BDKService() private var syncMode: SyncMode? - private var service: BDKClient { - switch try? keyClient.getSyncMode() { - case .kyoto: - return .kyoto - default: - return .esplora - } - } - private let keyClient: KeyClient + let keyClient: KeyClient private var needsFullScan: Bool = false private(set) var network: Network private(set) var esploraURL: String @@ -111,67 +103,18 @@ struct BDKClient { } extension BDKClient { - // MARK: - live -// static let live = Self( -// loadWallet: { try BDKService.shared.loadWalletFromBackup() }, -// deleteWallet: { try BDKService.shared.deleteWallet() }, -// createWalletFromSeed: { words in try BDKService.shared.createWallet(words: words) }, -// createWalletFromDescriptor: { descriptor in -// try BDKService.shared.createWallet(descriptor: descriptor) -// }, -// createWalletFromXPub: { xpub in -// try BDKService.shared.createWallet(xpub: xpub) -// }, -// getBalance: { try BDKService.shared.getBalance() }, -// transactions: { try BDKService.shared.transactions() }, -// listUnspent: { try BDKService.shared.listUnspent() }, -// syncScanWithSyncScanProgress: { progress in -// try await BDKService.shared.syncWithInspector(progress: progress) -// }, -// fullScanWithFullScanProgress: { progress in -// try await BDKService.shared.fullScanWithInspector(progress: progress) -// }, -// getAddress: { try BDKService.shared.getAddress() }, -// send: { (address, amount, feeRate) in -// Task { -// try await BDKService.shared.send(address: address, amount: amount, feeRate: feeRate) -// } -// }, -// calculateFee: { tx in try BDKService.shared.calculateFee(tx: tx) }, -// calculateFeeRate: { tx in try BDKService.shared.calculateFeeRate(tx: tx) }, -// sentAndReceived: { tx in try BDKService.shared.sentAndReceived(tx: tx) }, -// buildTransaction: { (address, amount, feeRate) in -// try BDKService.shared.buildTransaction( -// address: address, -// amount: amount, -// feeRate: feeRate -// ) -// }, -// getBackupInfo: { try BDKService.shared.getBackupInfo() }, -// needsFullScan: { BDKService.shared.needsFullScanOfWallet() }, -// setNeedsFullScan: { value in BDKService.shared.setNeedsFullScan(value) }, -// getNetwork: { -// BDKService.shared.network -// }, -// getEsploraURL: { -// BDKService.shared.esploraURL -// }, -// updateNetwork: { newNetwork in -// BDKService.shared.updateNetwork(newNetwork) -// }, -// updateEsploraURL: { newURL in -// BDKService.shared.updateEsploraURL(newURL) -// }, -// stop: { -// try await BDKService.shared.stop() -// }, -// upateSyncMode: { mode in -// BDKService.shared.updateSyncMode(mode) -// }, -// getSyncMode: { -// BDKService.shared.getSyncMode() -// } -// ) + static var live: BDKClient { + do { + let syncMode = try BDKService.shared.keyClient.getSyncMode() + if syncMode == .kyoto { + return .kyoto + } else { + return .esplora + } + } catch { + return .esplora + } + } } #if DEBUG diff --git a/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift index 2881a2f2..0e1d79ad 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/ActivityListViewModel.swift @@ -39,7 +39,7 @@ class ActivityListViewModel { } init( - bdkClient: BDKClient = .esplora, + bdkClient: BDKClient = .live, transactions: [CanonicalTx] = [], walletSyncState: WalletSyncState = .notStarted ) { diff --git a/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift index e2c882e9..e500e67e 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift @@ -23,7 +23,7 @@ class TransactionDetailViewModel { var transactionDetailsError: AppError? init( - bdkClient: BDKClient = .esplora + bdkClient: BDKClient = .live ) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift index fb2940ab..18a3d6a3 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift @@ -17,7 +17,7 @@ class TransactionListViewModel { var walletTransactionsViewError: AppError? init( - bdkClient: BDKClient = .esplora + bdkClient: BDKClient = .live ) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/HomeViewModel.swift b/BDKSwiftExampleWallet/View Model/HomeViewModel.swift index ee616f2d..1c2e2687 100644 --- a/BDKSwiftExampleWallet/View Model/HomeViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/HomeViewModel.swift @@ -17,7 +17,7 @@ class HomeViewModel: ObservableObject { var isWalletLoaded = false var showingHomeViewErrorAlert = false - init(bdkClient: BDKClient = .esplora) { + init(bdkClient: BDKClient = .live) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Receive/ReceiveViewModel.swift b/BDKSwiftExampleWallet/View Model/Receive/ReceiveViewModel.swift index 7001f3ef..7bc403e4 100644 --- a/BDKSwiftExampleWallet/View Model/Receive/ReceiveViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Receive/ReceiveViewModel.swift @@ -20,7 +20,7 @@ class ReceiveViewModel: NSObject, NFCNDEFReaderSessionDelegate { var receiveViewError: AppError? var showingReceiveViewErrorAlert = false - init(bdkClient: BDKClient = .esplora) { + init(bdkClient: BDKClient = .live) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Send/AmountViewModel.swift b/BDKSwiftExampleWallet/View Model/Send/AmountViewModel.swift index d0fb4be8..64fbbade 100644 --- a/BDKSwiftExampleWallet/View Model/Send/AmountViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Send/AmountViewModel.swift @@ -18,7 +18,7 @@ class AmountViewModel { var balanceTotal: UInt64? var showingAmountViewErrorAlert = false - init(bdkClient: BDKClient = .esplora) { + init(bdkClient: BDKClient = .live) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift b/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift index 048d5f6c..0d28276a 100644 --- a/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift @@ -19,7 +19,7 @@ class BuildTransactionViewModel { var showingBuildTransactionViewErrorAlert = false init( - bdkClient: BDKClient = .esplora + bdkClient: BDKClient = .live ) { self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Send/FeeViewModel.swift b/BDKSwiftExampleWallet/View Model/Send/FeeViewModel.swift index 508836ed..835a033e 100644 --- a/BDKSwiftExampleWallet/View Model/Send/FeeViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Send/FeeViewModel.swift @@ -37,7 +37,7 @@ class FeeViewModel { var recommendedFees: RecommendedFees? var showingFeeViewErrorAlert = false - init(feeClient: FeeClient = .live, bdkClient: BDKClient = .esplora) { + init(feeClient: FeeClient = .live, bdkClient: BDKClient = .live) { self.feeClient = feeClient self.bdkClient = bdkClient } diff --git a/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift b/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift index 07ac9c22..ec79f9b5 100644 --- a/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Settings/SettingsViewModel.swift @@ -24,7 +24,7 @@ class SettingsViewModel: ObservableObject { let syncMode: SyncMode init( - bdkClient: BDKClient = .esplora + bdkClient: BDKClient = .live ) { self.bdkClient = bdkClient self.network = bdkClient.getNetwork().description diff --git a/BDKSwiftExampleWallet/View Model/Settings/WalletRecoveryViewModel.swift b/BDKSwiftExampleWallet/View Model/Settings/WalletRecoveryViewModel.swift index 882d43a0..0d613d2b 100644 --- a/BDKSwiftExampleWallet/View Model/Settings/WalletRecoveryViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Settings/WalletRecoveryViewModel.swift @@ -21,7 +21,7 @@ class WalletRecoveryViewModel { var showingWalletRecoveryViewErrorAlert: Bool init( - bdkClient: BDKClient = .esplora, + bdkClient: BDKClient = .live, backupInfo: BackupInfo? = nil, walletRecoveryViewError: AppError? = nil, showingWalletRecoveryViewErrorAlert: Bool = false diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index a8bc73d1..7d2bc5c2 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -44,7 +44,7 @@ class WalletViewModel { } init( - bdkClient: BDKClient = .esplora, + bdkClient: BDKClient = .live, keyClient: KeyClient = .live, priceClient: PriceClient = .live, transactions: [CanonicalTx] = [], diff --git a/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift b/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift index 9c5aa428..46a0f3f6 100644 --- a/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift +++ b/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift @@ -100,7 +100,7 @@ struct TransactionListView: View { NavigationLink( destination: TransactionDetailView( viewModel: .init( - bdkClient: .esplora + bdkClient: .live ), amount: sentAndReceivedValues.sent.toSat() == 0 ? sentAndReceivedValues.received.toSat() diff --git a/BDKSwiftExampleWallet/View/HomeView.swift b/BDKSwiftExampleWallet/View/HomeView.swift index 52ebbf2d..119e31b6 100644 --- a/BDKSwiftExampleWallet/View/HomeView.swift +++ b/BDKSwiftExampleWallet/View/HomeView.swift @@ -17,7 +17,7 @@ struct HomeView: View { WalletView( viewModel: .init( - bdkClient: .esplora, + bdkClient: .live, priceClient: .live ), sendNavigationPath: $navigationPath From 3f0341b7c4081313b3807f5cca40e7392f93e71a Mon Sep 17 00:00:00 2001 From: Rubens Date: Thu, 5 Jun 2025 19:52:37 -0300 Subject: [PATCH 33/50] code review fixe --- BDKSwiftExampleWallet/Service/Key Service/KeyService.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift index c3459145..5dd7da57 100644 --- a/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift +++ b/BDKSwiftExampleWallet/Service/Key Service/KeyService.swift @@ -63,7 +63,7 @@ private struct KeyService { keychain[string: "SelectedNetwork"] = network } - func deletaAllData() throws { + func deleteAllData() throws { try deleteNetwork() try deleteBackupInfo() try deleteEsploraURL() @@ -135,7 +135,7 @@ extension KeyClient { saveBackupInfo: { backupInfo in try KeyService().saveBackupInfo(backupInfo: backupInfo) }, saveEsploraURL: { url in try KeyService().saveEsploraURL(url: url) }, saveNetwork: { network in try KeyService().saveNetwork(network: network) }, - deleteAllData: { try KeyService().deletaAllData() }, + deleteAllData: { try KeyService().deleteAllData() }, saveSyncMode: { mode in try KeyService().saveSyncMode(mode) }, getSyncMode: { try KeyService().getSyncMode() } ) From 608aecdcd79d245ae619bcb385656c4ce37d31f8 Mon Sep 17 00:00:00 2001 From: Rubens Date: Thu, 5 Jun 2025 19:53:59 -0300 Subject: [PATCH 34/50] Code review fixes --- .../Extensions/BDK+Extensions/Network+Extensions.swift | 2 +- BDKSwiftExampleWallet/View/Settings/SettingsView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Network+Extensions.swift b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Network+Extensions.swift index 8dce2a51..37b6febc 100644 --- a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Network+Extensions.swift +++ b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/Network+Extensions.swift @@ -21,7 +21,7 @@ extension Network { init?(stringValue: String) { switch stringValue { - case "bitcoin": self = .signet + case "bitcoin": self = .bitcoin case "testnet": self = .testnet case "testnet4": self = .testnet4 case "signet": self = .signet diff --git a/BDKSwiftExampleWallet/View/Settings/SettingsView.swift b/BDKSwiftExampleWallet/View/Settings/SettingsView.swift index 1ed27fb9..df69cb69 100644 --- a/BDKSwiftExampleWallet/View/Settings/SettingsView.swift +++ b/BDKSwiftExampleWallet/View/Settings/SettingsView.swift @@ -38,7 +38,7 @@ struct SettingsView: View { Group { if viewModel.syncMode == .kyoto { Section(header: Text("Network")) { - Text("Using Kyoto") + Text("Kyoto") .foregroundStyle(.primary) } } else { From 79a94bee8e57851a4ea86f3e8568f6f286f55fc5 Mon Sep 17 00:00:00 2001 From: Rubens Date: Thu, 5 Jun 2025 20:01:24 -0300 Subject: [PATCH 35/50] code review fixes --- BDKSwiftExampleWallet/Resources/Localizable.xcstrings | 3 --- 1 file changed, 3 deletions(-) diff --git a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings index 7a516d1c..af56ca61 100644 --- a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings +++ b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings @@ -1322,9 +1322,6 @@ } } } - }, - "Using Kyoto" : { - }, "Vout: %u" : { "localizations" : { From 2521e9d11e62b761f0d43a07324e93d8f3ad3d66 Mon Sep 17 00:00:00 2001 From: Rubens Date: Fri, 13 Jun 2025 16:26:03 -0300 Subject: [PATCH 36/50] Removed full scan when syncing with Kyoto --- .../BDKSyncService/BDKSyncService.swift | 2 +- .../View/Settings/SettingsView.swift | 34 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index f0fced72..40d6efc6 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -1,5 +1,5 @@ // -// BDKService2.swift +// BDKSyncService.swift // BDKSwiftExampleWallet // // Created by Rubens Machion on 16/05/25. diff --git a/BDKSwiftExampleWallet/View/Settings/SettingsView.swift b/BDKSwiftExampleWallet/View/Settings/SettingsView.swift index df69cb69..9e54509d 100644 --- a/BDKSwiftExampleWallet/View/Settings/SettingsView.swift +++ b/BDKSwiftExampleWallet/View/Settings/SettingsView.swift @@ -60,25 +60,27 @@ struct SettingsView: View { colorScheme == .light ? Color.gray.opacity(0.1) : Color.black.opacity(0.2) ) - Section(header: Text("Wallet")) { - Button { - Task { - await viewModel.fullScanWithProgress() + if viewModel.syncMode == .esplora { + Section(header: Text("Wallet")) { + Button { + Task { + await viewModel.fullScanWithProgress() + } + } label: { + Text("Full Scan") + } + .foregroundStyle(Color.bitcoinOrange) + if viewModel.walletSyncState == .syncing { + Text("\(viewModel.inspectedScripts)") + .contentTransition(.numericText()) + .foregroundStyle(.primary) + .animation(.easeInOut, value: viewModel.inspectedScripts) } - } label: { - Text("Full Scan") - } - .foregroundStyle(Color.bitcoinOrange) - if viewModel.walletSyncState == .syncing { - Text("\(viewModel.inspectedScripts)") - .contentTransition(.numericText()) - .foregroundStyle(.primary) - .animation(.easeInOut, value: viewModel.inspectedScripts) } + .listRowBackground( + colorScheme == .light ? Color.gray.opacity(0.1) : Color.black.opacity(0.2) + ) } - .listRowBackground( - colorScheme == .light ? Color.gray.opacity(0.1) : Color.black.opacity(0.2) - ) Section(header: Text("Danger Zone")) { Button { From ddf573f9c5c3ecc9b3337f5a1e86a04b15b1d7cb Mon Sep 17 00:00:00 2001 From: Rubens Date: Fri, 13 Jun 2025 17:38:10 -0300 Subject: [PATCH 37/50] feat: Added circular progress view --- .../project.pbxproj | 4 + .../Resources/Localizable.xcstrings | 6 + .../View Model/WalletViewModel.swift | 3 + .../View/Activity/CircularProgressView.swift | 31 +++++ .../View/Home/ActivityHomeHeaderView.swift | 109 +++++++++++------- BDKSwiftExampleWallet/View/WalletView.swift | 3 +- 6 files changed, 111 insertions(+), 45 deletions(-) create mode 100644 BDKSwiftExampleWallet/View/Activity/CircularProgressView.swift diff --git a/BDKSwiftExampleWallet.xcodeproj/project.pbxproj b/BDKSwiftExampleWallet.xcodeproj/project.pbxproj index ef6d16a1..27cde1df 100644 --- a/BDKSwiftExampleWallet.xcodeproj/project.pbxproj +++ b/BDKSwiftExampleWallet.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 77F6A9E72DE248A2003568F0 /* EsploraService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E62DE248A1003568F0 /* EsploraService.swift */; }; 77F6A9E92DE25C9C003568F0 /* KyotoService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9E82DE25C98003568F0 /* KyotoService.swift */; }; 77F6A9EB2DE5072E003568F0 /* AppStorageUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F6A9EA2DE50727003568F0 /* AppStorageUtil.swift */; }; + 77F80F582DFCC33D002ACBA2 /* CircularProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F80F572DFCC33D002ACBA2 /* CircularProgressView.swift */; }; A733D6D02A81113000F333B4 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = A733D6CF2A81113000F333B4 /* Localizable.xcstrings */; }; A73F7A362A3B778E00B87FC6 /* Int+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A73F7A352A3B778E00B87FC6 /* Int+Extensions.swift */; }; AE0C30F72A804A2D008F1EAE /* TransactionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE0C30F62A804A2D008F1EAE /* TransactionListView.swift */; }; @@ -131,6 +132,7 @@ 77F6A9E62DE248A1003568F0 /* EsploraService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EsploraService.swift; sourceTree = ""; }; 77F6A9E82DE25C98003568F0 /* KyotoService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KyotoService.swift; sourceTree = ""; }; 77F6A9EA2DE50727003568F0 /* AppStorageUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStorageUtil.swift; sourceTree = ""; }; + 77F80F572DFCC33D002ACBA2 /* CircularProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressView.swift; sourceTree = ""; }; A733D6CF2A81113000F333B4 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; A73F7A352A3B778E00B87FC6 /* Int+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Extensions.swift"; sourceTree = ""; }; AE0C30F62A804A2D008F1EAE /* TransactionListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionListView.swift; sourceTree = ""; }; @@ -363,6 +365,7 @@ AE2381B62C61253200F6B00C /* Activity */ = { isa = PBXGroup; children = ( + 77F80F572DFCC33D002ACBA2 /* CircularProgressView.swift */, AE2381AC2C60578500F6B00C /* ActivityListView.swift */, AE0C30F62A804A2D008F1EAE /* TransactionListView.swift */, AE29ED102BBE318A00EB9C4F /* TransactionItemView.swift */, @@ -749,6 +752,7 @@ AEB159D32D51A7E00006AE9E /* BalanceDisplayFormat.swift in Sources */, AE18E9382A9528200019D2A4 /* Bundle+Extensions.swift in Sources */, AE79538E2A2D59F000CCB277 /* Constants.swift in Sources */, + 77F80F582DFCC33D002ACBA2 /* CircularProgressView.swift in Sources */, AE2F255D2BED0BFB002A9AC6 /* AppError.swift in Sources */, AEE6C74F2ABCBA4600442ADD /* WalletSyncState.swift in Sources */, AE1C34242A424456008F807A /* ReceiveView.swift in Sources */, diff --git a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings index af56ca61..d66981d9 100644 --- a/BDKSwiftExampleWallet/Resources/Localizable.xcstrings +++ b/BDKSwiftExampleWallet/Resources/Localizable.xcstrings @@ -236,6 +236,9 @@ } } } + }, + "%lld%%" : { + }, "%llu" : { "localizations" : { @@ -451,6 +454,9 @@ } } } + }, + "Conecting" : { + }, "confirmed" : { "localizations" : { diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index 7d2bc5c2..991343b6 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -42,6 +42,9 @@ class WalletViewModel { var needsFullScan: Bool { bdkClient.needsFullScan() } + var syncMode: SyncMode { + bdkClient.getSyncMode() ?? .esplora + } init( bdkClient: BDKClient = .live, diff --git a/BDKSwiftExampleWallet/View/Activity/CircularProgressView.swift b/BDKSwiftExampleWallet/View/Activity/CircularProgressView.swift new file mode 100644 index 00000000..da5bc255 --- /dev/null +++ b/BDKSwiftExampleWallet/View/Activity/CircularProgressView.swift @@ -0,0 +1,31 @@ +// +// CircularProgressView.swift +// BDKSwiftExampleWallet +// +// Created by Rubens Machion on 13/06/25. +// + +import SwiftUI + +struct CircularProgressView: View { + var progress: Float + private let lineWidth: CGFloat = 2.0 + var body: some View { + ZStack { + Circle() + .stroke(Color.gray.opacity(0.3), lineWidth: lineWidth) + + Circle() + .trim(from: 0.0, to: CGFloat(progress)) + .stroke(Color.blue, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)) + .rotationEffect(.degrees(-90)) + .animation(.easeInOut(duration: 0.5), value: progress) + + Text("\(Int(progress * 100))%") + .contentTransition(.numericText()) + .font(.system(size: 9.0)) + .foregroundStyle(.secondary) + .fontWeight(.regular) + } + } +} diff --git a/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift b/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift index 2da5b4f8..5b6aec4f 100644 --- a/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift +++ b/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift @@ -14,6 +14,7 @@ struct ActivityHomeHeaderView: View { let inspectedScripts: UInt64 let totalScripts: UInt64 let needsFullScan: Bool + let syncMode: SyncMode let showAllTransactions: () -> Void @@ -24,60 +25,80 @@ struct ActivityHomeHeaderView: View { HStack { if needsFullScan { - Text("\(inspectedScripts)") - .padding(.trailing, -5.0) - .fontWeight(.semibold) - .contentTransition(.numericText()) - .transition(.opacity) + if syncMode == .esplora { + Text("\(inspectedScripts)") + .padding(.trailing, -5.0) + .fontWeight(.semibold) + .contentTransition(.numericText()) + .transition(.opacity) + .fontDesign(.monospaced) + .foregroundStyle(.secondary) + .font(.caption2) + .fontWeight(.thin) + .animation(.easeInOut, value: inspectedScripts) + } else if syncMode == .kyoto { + CircularProgressView( + progress: Float(inspectedScripts) / Float(100.0) + ) + .frame(width: 30.0, height: 30.0) + } + + } else if walletSyncState == .syncing { + if syncMode == .esplora { + HStack { + if progress < 1.0 { + Text("\(inspectedScripts)") + .padding(.trailing, -5.0) + .fontWeight(.semibold) + .contentTransition(.numericText()) + .transition(.opacity) + + Text("/") + .padding(.trailing, -5.0) + .transition(.opacity) + Text("\(totalScripts)") + .contentTransition(.numericText()) + .transition(.opacity) + } + Text( + String( + format: "%.0f%%", + progress * 100 + ) + ) + .contentTransition(.numericText()) + .transition(.opacity) + } .fontDesign(.monospaced) .foregroundStyle(.secondary) .font(.caption2) .fontWeight(.thin) .animation(.easeInOut, value: inspectedScripts) - } else if walletSyncState == .syncing { - HStack { - if progress < 1.0 { - Text("\(inspectedScripts)") - .padding(.trailing, -5.0) - .fontWeight(.semibold) - .contentTransition(.numericText()) - .transition(.opacity) - - Text("/") - .padding(.trailing, -5.0) - .transition(.opacity) - Text("\(totalScripts)") - .contentTransition(.numericText()) - .transition(.opacity) - } - - Text( - String( - format: "%.0f%%", - progress * 100 - ) - ) - .contentTransition(.numericText()) - .transition(.opacity) + .animation(.easeInOut, value: totalScripts) + .animation(.easeInOut, value: progress) + } else if syncMode == .kyoto { + Text("Conecting") + .font(.caption) + .foregroundStyle(.secondary) + .fontWeight(.regular) } - .fontDesign(.monospaced) - .foregroundStyle(.secondary) - .font(.caption2) - .fontWeight(.thin) - .animation(.easeInOut, value: inspectedScripts) - .animation(.easeInOut, value: totalScripts) - .animation(.easeInOut, value: progress) } } - HStack { - HStack(spacing: 5) { - self.syncImageIndicator() - } - .contentTransition(.symbolEffect(.replace.offUp)) + switch syncMode { + case .esplora: + HStack { + HStack(spacing: 5) { + self.syncImageIndicator() + } + .contentTransition(.symbolEffect(.replace.offUp)) + } + .foregroundStyle(.secondary) + .font(.caption) + + case .kyoto: + EmptyView() } - .foregroundStyle(.secondary) - .font(.caption) if walletSyncState == .synced { Button { diff --git a/BDKSwiftExampleWallet/View/WalletView.swift b/BDKSwiftExampleWallet/View/WalletView.swift index 010a6223..eeb7d2a4 100644 --- a/BDKSwiftExampleWallet/View/WalletView.swift +++ b/BDKSwiftExampleWallet/View/WalletView.swift @@ -47,7 +47,8 @@ struct WalletView: View { progress: viewModel.progress, inspectedScripts: viewModel.inspectedScripts, totalScripts: viewModel.totalScripts, - needsFullScan: viewModel.needsFullScan + needsFullScan: viewModel.needsFullScan, + syncMode: viewModel.syncMode ) { showAllTransactions = true } From be0e172e972413cfe402f603668dc3eca274bae9 Mon Sep 17 00:00:00 2001 From: Rubens Date: Fri, 13 Jun 2025 17:45:12 -0300 Subject: [PATCH 38/50] Removed option to choose network when using kyoto --- .../View/OnboardingView.swift | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/BDKSwiftExampleWallet/View/OnboardingView.swift b/BDKSwiftExampleWallet/View/OnboardingView.swift index f8496ad0..6afaa15f 100644 --- a/BDKSwiftExampleWallet/View/OnboardingView.swift +++ b/BDKSwiftExampleWallet/View/OnboardingView.swift @@ -111,17 +111,6 @@ struct OnboardingView: View { .padding() Group { - Picker("Network", selection: $viewModel.selectedNetwork) { - Text("Signet").tag(Network.signet) - Text("Testnet").tag(Network.testnet) - Text("Testnet4").tag(Network.testnet4) - } - .pickerStyle(.automatic) - .tint(.primary) - .accessibilityLabel("Select Bitcoin Network") - .opacity(animateContent ? 1 : 0) - .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) - Picker("Sync type", selection: $viewModel.syncMode) { Text("Esplora Server").tag(SyncMode.esplora) Text("Kyoto").tag(SyncMode.kyoto) @@ -130,7 +119,7 @@ struct OnboardingView: View { .tint(.primary) .opacity(animateContent ? 1 : 0) .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) - + if viewModel.syncMode == nil || viewModel.syncMode == .esplora { Picker("Esplora Server", selection: $viewModel.selectedURL) { ForEach(viewModel.availableURLs, id: \.self) { url in @@ -150,7 +139,18 @@ struct OnboardingView: View { .tint(.primary) .opacity(animateContent ? 1 : 0) .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) - } + + Picker("Network", selection: $viewModel.selectedNetwork) { + Text("Signet").tag(Network.signet) + Text("Testnet").tag(Network.testnet) + Text("Testnet4").tag(Network.testnet4) + } + .pickerStyle(.automatic) + .tint(.primary) + .accessibilityLabel("Select Bitcoin Network") + .opacity(animateContent ? 1 : 0) + .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) + } } if !viewModel.words.isEmpty { From 364a9967dd11c1032c329b506559cce714499087 Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 16 Jun 2025 21:47:32 -0300 Subject: [PATCH 39/50] added automatic update --- .../Service/BDKSyncService/KyotoService.swift | 44 +++++++++++++++---- .../View Model/WalletViewModel.swift | 8 ++++ BDKSwiftExampleWallet/View/WalletView.swift | 29 +++++++----- 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 898b777a..178e9985 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -12,6 +12,10 @@ extension KyotoService { static var live: BDKSyncService = KyotoService() } +extension Notification.Name { + static let shouldUpdateWallet = Notification.Name("ShouldUpdateWallet") +} + final class KyotoService: BDKSyncService { static let shared: KyotoService = .init() @@ -23,7 +27,7 @@ final class KyotoService: BDKSyncService { private var client: CbfClient? private var node: CbfNode? - private var connected = false + private var isConnected = false private var isScanRunning = false private var fullScanProgress: FullScanProgress? @@ -63,7 +67,7 @@ final class KyotoService: BDKSyncService { self.client = nodeComponents.client self.node = nodeComponents.node isScanRunning = true - try await startListen() + try await startNode() } func startFullScan(progress: @escaping FullScanProgress) async throws { @@ -80,7 +84,7 @@ final class KyotoService: BDKSyncService { self.client = nodeComponents.client self.node = nodeComponents.node isScanRunning = true - try await startListen() + try await startNode() } func send(address: String, amount: UInt64, feeRate: UInt64) async throws { @@ -118,17 +122,19 @@ final class KyotoService: BDKSyncService { .build(wallet: wallet) } - private func startListen() async throws { + private func startNode() async throws { node?.run() printLogs() updateWarn() - try await startUpdating() + try await updateWallet() + startRealTimeWalletUpdate() } @discardableResult - func startUpdating() async throws -> Bool { + private func updateWallet() async throws -> Bool { guard let update = await self.client?.update() else { isScanRunning = false + print("Nothing to update") return false } try self.wallet?.applyUpdate(update: update) @@ -137,7 +143,27 @@ final class KyotoService: BDKSyncService { isScanRunning = false return true } - + + private func startRealTimeWalletUpdate() { + print(#function) + Task { + while true { + print("Updating: \(Date())") + if let update = await client?.update() { + do { + try wallet?.applyUpdate(update: update) + NotificationCenter.default.post(name: .shouldUpdateWallet, object: nil) + print("Updated wallet") + } catch { + print(error) + } + } else { + print("Nothing to update") + } + } + } + } + private func printLogs() { Task { while true { @@ -146,7 +172,7 @@ final class KyotoService: BDKSyncService { switch log { case .connectionsMet: print("######### connected") - self.connected = true + self.isConnected = true case .progress(let progress): if let fullScanProgress = self.fullScanProgress { let _progress = UInt64(progress * 100.0) @@ -167,7 +193,7 @@ final class KyotoService: BDKSyncService { switch warn { case .needConnections: print("######### disconnected") - self.connected = false + self.isConnected = false default: #if DEBUG print(warn) diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index 991343b6..e2f5bba7 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -58,6 +58,14 @@ class WalletViewModel { self.priceClient = priceClient self.transactions = transactions self.walletSyncState = walletSyncState + NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .shouldUpdateWallet, object: nil) + } + + @objc private func receiveNotification(_ notification: Notification) { + if notification.name == .shouldUpdateWallet { + getBalance() + getTransactions() + } } func getBalance() { diff --git a/BDKSwiftExampleWallet/View/WalletView.swift b/BDKSwiftExampleWallet/View/WalletView.swift index eeb7d2a4..6a9ee0c8 100644 --- a/BDKSwiftExampleWallet/View/WalletView.swift +++ b/BDKSwiftExampleWallet/View/WalletView.swift @@ -52,17 +52,24 @@ struct WalletView: View { ) { showAllTransactions = true } - - TransactionListView( - viewModel: .init(), - transactions: viewModel.recentTransactions, - walletSyncState: viewModel.walletSyncState - ) - .refreshable { - await viewModel.syncOrFullScan() - viewModel.getBalance() - viewModel.getTransactions() - await viewModel.getPrices() + if viewModel.syncMode == .esplora { + TransactionListView( + viewModel: .init(), + transactions: viewModel.recentTransactions, + walletSyncState: viewModel.walletSyncState + ) + .refreshable { + await viewModel.syncOrFullScan() + viewModel.getBalance() + viewModel.getTransactions() + await viewModel.getPrices() + } + } else { + TransactionListView( + viewModel: .init(), + transactions: viewModel.recentTransactions, + walletSyncState: viewModel.walletSyncState + ) } HStack { From 840e0f874fc4fe32525a55afb8e57e01be0e8347 Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 16 Jun 2025 22:10:56 -0300 Subject: [PATCH 40/50] added network indicator --- .../Service/BDKSyncService/KyotoService.swift | 14 ++++++++--- .../View Model/WalletViewModel.swift | 23 +++++++++++++++++-- BDKSwiftExampleWallet/View/WalletView.swift | 6 +++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 178e9985..fe0b47a3 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -13,7 +13,9 @@ extension KyotoService { } extension Notification.Name { - static let shouldUpdateWallet = Notification.Name("ShouldUpdateWallet") + static let walletDidUpdate = Notification.Name("walletDidUpdate") + static let walletDidConnect = Notification.Name("walletDidConnect") + static let walletDidDisconnect = Notification.Name("walletDidDisconnect") } final class KyotoService: BDKSyncService { @@ -27,7 +29,13 @@ final class KyotoService: BDKSyncService { private var client: CbfClient? private var node: CbfNode? - private var isConnected = false + private var isConnected = false { + didSet { + isConnected ? + NotificationCenter.default.post(name: .walletDidConnect, object: nil) : + NotificationCenter.default.post(name: .walletDidDisconnect, object: nil) + } + } private var isScanRunning = false private var fullScanProgress: FullScanProgress? @@ -152,7 +160,7 @@ final class KyotoService: BDKSyncService { if let update = await client?.update() { do { try wallet?.applyUpdate(update: update) - NotificationCenter.default.post(name: .shouldUpdateWallet, object: nil) + NotificationCenter.default.post(name: .walletDidUpdate, object: nil) print("Updated wallet") } catch { print(error) diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index e2f5bba7..02edf0cc 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -45,6 +45,7 @@ class WalletViewModel { var syncMode: SyncMode { bdkClient.getSyncMode() ?? .esplora } + var isConnected: Bool = false init( bdkClient: BDKClient = .live, @@ -58,13 +59,31 @@ class WalletViewModel { self.priceClient = priceClient self.transactions = transactions self.walletSyncState = walletSyncState - NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .shouldUpdateWallet, object: nil) + addNotifications() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + private func addNotifications() { + NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .walletDidUpdate, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .walletDidConnect, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .walletDidConnect, object: nil) } @objc private func receiveNotification(_ notification: Notification) { - if notification.name == .shouldUpdateWallet { + switch notification.name { + case .walletDidUpdate: getBalance() getTransactions() + case .walletDidConnect: + isConnected = true + + case .walletDidDisconnect: + isConnected = false + + default: break } } diff --git a/BDKSwiftExampleWallet/View/WalletView.swift b/BDKSwiftExampleWallet/View/WalletView.swift index 6a9ee0c8..2c264fe0 100644 --- a/BDKSwiftExampleWallet/View/WalletView.swift +++ b/BDKSwiftExampleWallet/View/WalletView.swift @@ -182,6 +182,12 @@ struct WalletView: View { ) } .toolbar { + if viewModel.syncMode == .kyoto { + ToolbarItem(placement: .topBarLeading) { + Image(systemName: viewModel.isConnected ? "network" : "network.slash") + .foregroundStyle(viewModel.isConnected ? .blue : .red) + } + } ToolbarItem(placement: .navigationBarTrailing) { Button { showSettingsView = true From 2a109a3fcf6bb3f5f278b1f590667605854e6d00 Mon Sep 17 00:00:00 2001 From: Rubens Date: Mon, 16 Jun 2025 22:30:13 -0300 Subject: [PATCH 41/50] realtime update wallet --- .../Service/BDKSyncService/KyotoService.swift | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index fe0b47a3..304515fb 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -132,7 +132,7 @@ final class KyotoService: BDKSyncService { private func startNode() async throws { node?.run() - printLogs() + getNextLog() updateWarn() try await updateWallet() startRealTimeWalletUpdate() @@ -142,44 +142,42 @@ final class KyotoService: BDKSyncService { private func updateWallet() async throws -> Bool { guard let update = await self.client?.update() else { isScanRunning = false - print("Nothing to update") return false } try self.wallet?.applyUpdate(update: update) let _ = try self.wallet?.persist(connection: self.connection ?? Connection.loadConnection()) - print("######### walletUpdated") isScanRunning = false return true } private func startRealTimeWalletUpdate() { - print(#function) Task { while true { - print("Updating: \(Date())") - if let update = await client?.update() { - do { - try wallet?.applyUpdate(update: update) + do { + if try await updateWallet() { NotificationCenter.default.post(name: .walletDidUpdate, object: nil) - print("Updated wallet") - } catch { - print(error) } - } else { - print("Nothing to update") + } catch { + print(error) } +// if let update = await client?.update() { +// do { +// try wallet?.applyUpdate(update: update) +// NotificationCenter.default.post(name: .walletDidUpdate, object: nil) +// } catch { +// print(error) +// } +// } } } } - private func printLogs() { + private func getNextLog() { Task { while true { if let log = try? await self.client?.nextLog() { - print("\(log)") switch log { case .connectionsMet: - print("######### connected") self.isConnected = true case .progress(let progress): if let fullScanProgress = self.fullScanProgress { @@ -200,7 +198,6 @@ final class KyotoService: BDKSyncService { if let warn = try? await self.client?.nextWarning() { switch warn { case .needConnections: - print("######### disconnected") self.isConnected = false default: #if DEBUG From 531cc7b12805de19a3e6991a161bf0df8a18afb6 Mon Sep 17 00:00:00 2001 From: Rubens Date: Tue, 17 Jun 2025 08:15:02 -0300 Subject: [PATCH 42/50] Stop any service when delete a wallet --- .../Service/BDKSyncService/BDKSyncService.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 40d6efc6..32be7ddc 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -155,6 +155,9 @@ extension BDKSyncService { } func deleteWallet() throws { + Task { + try await stopService() + } try deleteData() } From 35c7072088e6d3ba01909f19f78f59f41e0ac142 Mon Sep 17 00:00:00 2001 From: Rubens Date: Tue, 17 Jun 2025 08:53:06 -0300 Subject: [PATCH 43/50] removed taprootheight --- .../Service/BDKSyncService/KyotoService.swift | 13 +++---------- BDKSwiftExampleWallet/Utilities/Constants.swift | 9 --------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 304515fb..09478530 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -61,7 +61,7 @@ final class KyotoService: BDKSyncService { let wallet = try loadWalleFromBackup() self.wallet = wallet } - + func startSync(progress: @escaping SyncScanProgress) async throws { if isScanRunning { return } guard let wallet = self.wallet else { @@ -85,7 +85,7 @@ final class KyotoService: BDKSyncService { } let nodeComponents = try buildNode( from: wallet, - scanType: .recovery(fromHeight: network.taprootHeight) + scanType: .new ) self.fullScanProgress = progress @@ -160,14 +160,6 @@ final class KyotoService: BDKSyncService { } catch { print(error) } -// if let update = await client?.update() { -// do { -// try wallet?.applyUpdate(update: update) -// NotificationCenter.default.post(name: .walletDidUpdate, object: nil) -// } catch { -// print(error) -// } -// } } } } @@ -184,6 +176,7 @@ final class KyotoService: BDKSyncService { let _progress = UInt64(progress * 100.0) fullScanProgress(_progress) } + print("Progress: \(progress)") default: break } diff --git a/BDKSwiftExampleWallet/Utilities/Constants.swift b/BDKSwiftExampleWallet/Utilities/Constants.swift index b578eefd..3a016032 100644 --- a/BDKSwiftExampleWallet/Utilities/Constants.swift +++ b/BDKSwiftExampleWallet/Utilities/Constants.swift @@ -95,13 +95,4 @@ extension Network { Constants.Config.EsploraServerURLNetwork.Testnet4.allValues.first ?? "" } } - - var taprootHeight: UInt32 { - switch self { - case .bitcoin: - return 700_000 - default: - return 250_000 - } - } } From 98f86d81a07dc016a66d94f2f56b8e10284f06ae Mon Sep 17 00:00:00 2001 From: Rubens Date: Tue, 17 Jun 2025 09:00:32 -0300 Subject: [PATCH 44/50] no message --- .../View Model/Activity/TransactionDetailViewModel.swift | 6 +----- BDKSwiftExampleWallet/View Model/WalletViewModel.swift | 5 +++-- .../View/Activity/TransactionListView.swift | 1 - 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift index e500e67e..347b8dda 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift @@ -45,11 +45,7 @@ class TransactionDetailViewModel { switch network { case "signet": - if savedEsploraURL == Constants.Config.EsploraServerURLNetwork.Signet.bdk { - self.esploraURL = "https://mempool.space/signet" - } else { - self.esploraURL = "https://mutinynet.com" - } + self.esploraURL = "https://mempool.space/signet" case "testnet": if savedEsploraURL == Constants.Config.EsploraServerURLNetwork.Testnet.blockstream { self.esploraURL = "https://blockstream.info/testnet" diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index 02edf0cc..591c2af9 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -70,11 +70,13 @@ class WalletViewModel { NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .walletDidUpdate, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .walletDidConnect, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .walletDidConnect, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: UIApplication.willEnterForegroundNotification, object: nil + ) } @objc private func receiveNotification(_ notification: Notification) { switch notification.name { - case .walletDidUpdate: + case .walletDidUpdate, UIApplication.willEnterForegroundNotification: getBalance() getTransactions() case .walletDidConnect: @@ -193,5 +195,4 @@ class WalletViewModel { self?.progress = total > 0 ? Float(inspected) / Float(total) : 0 } } - } diff --git a/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift b/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift index 46a0f3f6..b1512fa1 100644 --- a/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift +++ b/BDKSwiftExampleWallet/View/Activity/TransactionListView.swift @@ -36,7 +36,6 @@ struct TransactionListView: View { .font(.subheadline) let mutinyFaucetURL = URL(string: "https://faucet.mutinynet.com") - // let signetFaucetURL = URL(string: "https://signetfaucet.com") let signetFaucetURL = URL(string: "https://signet25.bublina.eu.org/") if let mutinyFaucetURL, From 13a363be40a4b8830cebd5901276a6313189c817 Mon Sep 17 00:00:00 2001 From: Rubens Date: Tue, 17 Jun 2025 11:46:48 -0300 Subject: [PATCH 45/50] fix onboarding --- BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift | 6 +++--- BDKSwiftExampleWallet/View/OnboardingView.swift | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift index 42ea621b..3e23215e 100644 --- a/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/OnboardingViewModel.swift @@ -16,9 +16,9 @@ class OnboardingViewModel: ObservableObject { let bdkClient: BDKClient @AppStorage("isOnboarding") var isOnboarding: Bool? - @Published var syncMode: SyncMode? { + @Published var syncMode: SyncMode = .esplora { didSet { - bdkClient.upateSyncMode(syncMode ?? .esplora) + bdkClient.upateSyncMode(syncMode) } } @Published var createWithPersistError: CreateWithPersistError? @@ -86,7 +86,7 @@ class OnboardingViewModel: ObservableObject { self.bdkClient = bdkClient self.selectedNetwork = bdkClient.getNetwork() self.selectedURL = bdkClient.getEsploraURL() - self.syncMode = bdkClient.getSyncMode() + self.syncMode = bdkClient.getSyncMode() ?? .esplora } func createWallet() { diff --git a/BDKSwiftExampleWallet/View/OnboardingView.swift b/BDKSwiftExampleWallet/View/OnboardingView.swift index 6afaa15f..e81a4309 100644 --- a/BDKSwiftExampleWallet/View/OnboardingView.swift +++ b/BDKSwiftExampleWallet/View/OnboardingView.swift @@ -120,7 +120,7 @@ struct OnboardingView: View { .opacity(animateContent ? 1 : 0) .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) - if viewModel.syncMode == nil || viewModel.syncMode == .esplora { + if viewModel.syncMode == .esplora { Picker("Esplora Server", selection: $viewModel.selectedURL) { ForEach(viewModel.availableURLs, id: \.self) { url in Text( From 3c5cdf452a2b8f16347359f9ac02055cc51e180e Mon Sep 17 00:00:00 2001 From: Rubens Date: Tue, 17 Jun 2025 11:49:36 -0300 Subject: [PATCH 46/50] chore: swift-format --- .../BDK Service/BDKClient+Esplora.swift | 12 +++++- .../Service/BDK Service/BDKClient+Kyoto.swift | 12 +++++- .../BDKSyncService/EsploraService.swift | 2 +- .../Service/BDKSyncService/KyotoService.swift | 14 +++---- .../View Model/WalletViewModel.swift | 37 ++++++++++++++----- .../View/Home/ActivityHomeHeaderView.swift | 4 +- .../View/OnboardingView.swift | 6 +-- 7 files changed, 61 insertions(+), 26 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Esplora.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Esplora.swift index 3f243883..5cfbb683 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Esplora.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Esplora.swift @@ -42,7 +42,11 @@ extension BDKClient { }, send: { (address, amount, feeRate) in Task { - try await EsploraService.shared.send(address: address, amount: amount, feeRate: feeRate) + try await EsploraService.shared.send( + address: address, + amount: amount, + feeRate: feeRate + ) } }, calculateFee: { tx in @@ -55,7 +59,11 @@ extension BDKClient { try EsploraService.shared.sentAndReceived(tx: tx) }, buildTransaction: { (address, amount, feeRate) in - try EsploraService.shared.buildTransaction(address: address, amount: amount, feeRate: feeRate) + try EsploraService.shared.buildTransaction( + address: address, + amount: amount, + feeRate: feeRate + ) }, getBackupInfo: { try BDKService.shared.getBackupInfo() diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Kyoto.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Kyoto.swift index 1717e163..8b7f2a94 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Kyoto.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKClient+Kyoto.swift @@ -42,7 +42,11 @@ extension BDKClient { }, send: { (address, amount, feeRate) in Task { - try await KyotoService.shared.send(address: address, amount: amount, feeRate: feeRate) + try await KyotoService.shared.send( + address: address, + amount: amount, + feeRate: feeRate + ) } }, calculateFee: { tx in @@ -55,7 +59,11 @@ extension BDKClient { try KyotoService.shared.sentAndReceived(tx: tx) }, buildTransaction: { (address, amount, feeRate) in - try KyotoService.shared.buildTransaction(address: address, amount: amount, feeRate: feeRate) + try KyotoService.shared.buildTransaction( + address: address, + amount: amount, + feeRate: feeRate + ) }, getBackupInfo: { try BDKService.shared.getBackupInfo() diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift index b1b35540..32266559 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/EsploraService.swift @@ -15,7 +15,7 @@ extension EsploraService { final class EsploraService: BDKSyncService { static let shared: EsploraService = .init() - + var connection: Connection? var keyClient: KeyClient var network: Network diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift index 09478530..91b11c5c 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/KyotoService.swift @@ -21,7 +21,7 @@ extension Notification.Name { final class KyotoService: BDKSyncService { static let shared: KyotoService = .init() - + var connection: Connection? var keyClient: KeyClient var network: Network @@ -31,9 +31,9 @@ final class KyotoService: BDKSyncService { private var node: CbfNode? private var isConnected = false { didSet { - isConnected ? - NotificationCenter.default.post(name: .walletDidConnect, object: nil) : - NotificationCenter.default.post(name: .walletDidDisconnect, object: nil) + isConnected + ? NotificationCenter.default.post(name: .walletDidConnect, object: nil) + : NotificationCenter.default.post(name: .walletDidDisconnect, object: nil) } } private var isScanRunning = false @@ -61,7 +61,7 @@ final class KyotoService: BDKSyncService { let wallet = try loadWalleFromBackup() self.wallet = wallet } - + func startSync(progress: @escaping SyncScanProgress) async throws { if isScanRunning { return } guard let wallet = self.wallet else { @@ -149,7 +149,7 @@ final class KyotoService: BDKSyncService { isScanRunning = false return true } - + private func startRealTimeWalletUpdate() { Task { while true { @@ -163,7 +163,7 @@ final class KyotoService: BDKSyncService { } } } - + private func getNextLog() { Task { while true { diff --git a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift index 591c2af9..7fbdad5c 100644 --- a/BDKSwiftExampleWallet/View Model/WalletViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/WalletViewModel.swift @@ -61,19 +61,38 @@ class WalletViewModel { self.walletSyncState = walletSyncState addNotifications() } - + deinit { NotificationCenter.default.removeObserver(self) } - + private func addNotifications() { - NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .walletDidUpdate, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .walletDidConnect, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: .walletDidConnect, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(receiveNotification(_:)), name: UIApplication.willEnterForegroundNotification, object: nil + NotificationCenter.default.addObserver( + self, + selector: #selector(receiveNotification(_:)), + name: .walletDidUpdate, + object: nil + ) + NotificationCenter.default.addObserver( + self, + selector: #selector(receiveNotification(_:)), + name: .walletDidConnect, + object: nil + ) + NotificationCenter.default.addObserver( + self, + selector: #selector(receiveNotification(_:)), + name: .walletDidConnect, + object: nil + ) + NotificationCenter.default.addObserver( + self, + selector: #selector(receiveNotification(_:)), + name: UIApplication.willEnterForegroundNotification, + object: nil ) } - + @objc private func receiveNotification(_ notification: Notification) { switch notification.name { case .walletDidUpdate, UIApplication.willEnterForegroundNotification: @@ -81,10 +100,10 @@ class WalletViewModel { getTransactions() case .walletDidConnect: isConnected = true - + case .walletDidDisconnect: isConnected = false - + default: break } } diff --git a/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift b/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift index 5b6aec4f..dc8bccbc 100644 --- a/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift +++ b/BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift @@ -42,7 +42,7 @@ struct ActivityHomeHeaderView: View { ) .frame(width: 30.0, height: 30.0) } - + } else if walletSyncState == .syncing { if syncMode == .esplora { HStack { @@ -95,7 +95,7 @@ struct ActivityHomeHeaderView: View { } .foregroundStyle(.secondary) .font(.caption) - + case .kyoto: EmptyView() } diff --git a/BDKSwiftExampleWallet/View/OnboardingView.swift b/BDKSwiftExampleWallet/View/OnboardingView.swift index e81a4309..d8407f75 100644 --- a/BDKSwiftExampleWallet/View/OnboardingView.swift +++ b/BDKSwiftExampleWallet/View/OnboardingView.swift @@ -119,7 +119,7 @@ struct OnboardingView: View { .tint(.primary) .opacity(animateContent ? 1 : 0) .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) - + if viewModel.syncMode == .esplora { Picker("Esplora Server", selection: $viewModel.selectedURL) { ForEach(viewModel.availableURLs, id: \.self) { url in @@ -139,7 +139,7 @@ struct OnboardingView: View { .tint(.primary) .opacity(animateContent ? 1 : 0) .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) - + Picker("Network", selection: $viewModel.selectedNetwork) { Text("Signet").tag(Network.signet) Text("Testnet").tag(Network.testnet) @@ -150,7 +150,7 @@ struct OnboardingView: View { .accessibilityLabel("Select Bitcoin Network") .opacity(animateContent ? 1 : 0) .animation(.easeOut(duration: 0.5).delay(1.5), value: animateContent) - } + } } if !viewModel.words.isEmpty { From a05f17261424da691124118ce21d2cc794eedbc0 Mon Sep 17 00:00:00 2001 From: Rubens Date: Wed, 18 Jun 2025 08:55:33 -0300 Subject: [PATCH 47/50] code review fix --- BDKSwiftExampleWallet/Model/BackupInfo.swift | 2 +- .../Service/BDKSyncService/BDKSyncService.swift | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/BDKSwiftExampleWallet/Model/BackupInfo.swift b/BDKSwiftExampleWallet/Model/BackupInfo.swift index 1acdce1c..042bcc3d 100644 --- a/BDKSwiftExampleWallet/Model/BackupInfo.swift +++ b/BDKSwiftExampleWallet/Model/BackupInfo.swift @@ -12,7 +12,7 @@ struct BackupInfo: Codable, Equatable { var descriptor: String var changeDescriptor: String - init(mnemonic: String = "", descriptor: String, changeDescriptor: String) { + init(mnemonic: String, descriptor: String, changeDescriptor: String) { self.mnemonic = mnemonic self.descriptor = descriptor self.changeDescriptor = changeDescriptor diff --git a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift index 32be7ddc..2d1491f9 100644 --- a/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift +++ b/BDKSwiftExampleWallet/Service/BDKSyncService/BDKSyncService.swift @@ -88,6 +88,7 @@ extension BDKSyncService { network: network ) return .init( + mnemonic: "", descriptor: descriptor.description, changeDescriptor: changeDescriptor.description ) @@ -123,6 +124,7 @@ extension BDKSyncService { } return .init( + mnemonic: "", descriptor: descriptor.toStringWithSecret(), changeDescriptor: changeDescriptor.toStringWithSecret() ) From ce793a930bbb6f855d42844caf9e855cb5ea28b4 Mon Sep 17 00:00:00 2001 From: Rubens Date: Wed, 18 Jun 2025 09:12:08 -0300 Subject: [PATCH 48/50] Code review fixes --- BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift | 6 +++--- .../View Model/Activity/TransactionDetailViewModel.swift | 4 ++-- .../View Model/Activity/TransactionListViewModel.swift | 2 +- .../View Model/Send/BuildTransactionViewModel.swift | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 3608c0f9..780e6c77 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -86,9 +86,9 @@ struct BDKClient { let fullScanWithFullScanProgress: (@escaping FullScanProgress) async throws -> Void let getAddress: () throws -> String let send: (String, UInt64, UInt64) throws -> Void - let calculateFee: (BitcoinDevKit.Transaction) throws -> Amount - let calculateFeeRate: (BitcoinDevKit.Transaction) throws -> UInt64 - let sentAndReceived: (BitcoinDevKit.Transaction) throws -> SentAndReceivedValues + let calculateFee: (Transaction) throws -> Amount + let calculateFeeRate: (Transaction) throws -> UInt64 + let sentAndReceived: (Transaction) throws -> SentAndReceivedValues let buildTransaction: (String, UInt64, UInt64) throws -> Psbt let getBackupInfo: () throws -> BackupInfo let needsFullScan: () -> Bool diff --git a/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift index 347b8dda..46eeb69c 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/TransactionDetailViewModel.swift @@ -28,7 +28,7 @@ class TransactionDetailViewModel { self.bdkClient = bdkClient } - func getCalulateFee(tx: BitcoinDevKit.Transaction) { + func getCalulateFee(tx: Transaction) { do { let calculateFee = try bdkClient.calculateFee(tx) let feeString = String(calculateFee.toSat()) @@ -61,7 +61,7 @@ class TransactionDetailViewModel { self.network = bdkClient.getNetwork().description } - func getSentAndReceived(tx: BitcoinDevKit.Transaction) -> SentAndReceivedValues? { + func getSentAndReceived(tx: Transaction) -> SentAndReceivedValues? { do { let sentAndReceived = try bdkClient.sentAndReceived(tx) return sentAndReceived diff --git a/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift b/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift index 18a3d6a3..84991aef 100644 --- a/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Activity/TransactionListViewModel.swift @@ -22,7 +22,7 @@ class TransactionListViewModel { self.bdkClient = bdkClient } - func getSentAndReceived(tx: BitcoinDevKit.Transaction) -> SentAndReceivedValues? { + func getSentAndReceived(tx: Transaction) -> SentAndReceivedValues? { do { let sentAndReceived = try bdkClient.sentAndReceived(tx) return sentAndReceived diff --git a/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift b/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift index 0d28276a..ce1b4fba 100644 --- a/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift +++ b/BDKSwiftExampleWallet/View Model/Send/BuildTransactionViewModel.swift @@ -40,7 +40,7 @@ class BuildTransactionViewModel { } } - func extractTransaction() -> BitcoinDevKit.Transaction? { + func extractTransaction() -> Transaction? { guard let psbt = self.psbt else { return nil } @@ -56,7 +56,7 @@ class BuildTransactionViewModel { } } - func getCalulateFee(tx: BitcoinDevKit.Transaction) { + func getCalulateFee(tx: Transaction) { do { let calculateFee = try bdkClient.calculateFee(tx) let feeString = String(calculateFee.toSat()) From 71507d441900da6b1e01be37e3c36c2c9b47b867 Mon Sep 17 00:00:00 2001 From: Rubens Date: Wed, 18 Jun 2025 09:24:41 -0300 Subject: [PATCH 49/50] code review fix --- BDKSwiftExampleWallet/Utilities/Constants.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BDKSwiftExampleWallet/Utilities/Constants.swift b/BDKSwiftExampleWallet/Utilities/Constants.swift index 3a016032..205ca8ae 100644 --- a/BDKSwiftExampleWallet/Utilities/Constants.swift +++ b/BDKSwiftExampleWallet/Utilities/Constants.swift @@ -16,8 +16,8 @@ struct Constants { private static let blockstream = "https://blockstream.info/api" private static let mempoolspace = "https://mempool.space/api" static let allValues = [ - blockstream, mempoolspace, + blockstream, ] } struct Regtest { From 57620a23a5c21a0369aab9cbc3f6d3a1e70ae436 Mon Sep 17 00:00:00 2001 From: Rubens Date: Wed, 18 Jun 2025 09:26:47 -0300 Subject: [PATCH 50/50] code review fix --- BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift | 4 ++-- BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 780e6c77..54bc9d5a 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -65,11 +65,11 @@ class BDKService { extension BDKService { func needsFullScanOfWallet() -> Bool { - return StorageUtil.shared.isNeedFullScan ?? true + return AppStorageUtil.shared.isNeedFullScan ?? true } func setNeedsFullScan(_ value: Bool) { - StorageUtil.shared.isNeedFullScan = value + AppStorageUtil.shared.isNeedFullScan = value } } diff --git a/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift b/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift index 68e475d5..83af03c1 100644 --- a/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift +++ b/BDKSwiftExampleWallet/Utilities/AppStorageUtil.swift @@ -7,8 +7,8 @@ import SwiftUI -struct StorageUtil { +struct AppStorageUtil { @AppStorage("isNeedFullScan") var isNeedFullScan: Bool? - static var shared = StorageUtil() + static var shared = AppStorageUtil() }