From db3495dac6578bd2ddd671aec202d8bc533d2301 Mon Sep 17 00:00:00 2001 From: ant013 Date: Sun, 8 Oct 2023 17:43:48 +0600 Subject: [PATCH] Refactor backup and crypto structures. - Extract many small functions from encrypt/decrypt, refactor names and logic for compose full backups --- .../project.pbxproj | 6 + .../Core/Crypto/BackupCrypto.swift | 14 +- .../Core/Crypto/FullBackup.swift | 6 +- .../Core/Crypto/RawFullBackup.swift | 14 + .../Core/Crypto/SettingsBackup.swift | 2 +- .../Core/Managers/CloudBackupManager.swift | 21 +- .../Core/Managers/EvmBlockchainManager.swift | 4 +- .../Core/Managers/EvmSyncSourceManager.swift | 70 ++-- .../Core/Storage/ContactBookManager.swift | 10 +- .../Models/AccountType.swift | 65 ++-- .../Backup/ICloud/AppBackupProvider.swift | 299 ++++++++---------- .../RestoreFileConfigurationService.swift | 4 +- .../RestorePassphraseService.swift | 27 +- .../RestorePassphraseViewController.swift | 10 +- .../RestorePassphraseViewModel.swift | 14 +- .../BackupApp/Backup/BackupAppViewModel.swift | 28 +- .../Modules/Swap/SwapProviderManager.swift | 21 +- 17 files changed, 296 insertions(+), 319 deletions(-) create mode 100644 UnstoppableWallet/UnstoppableWallet/Core/Crypto/RawFullBackup.swift diff --git a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj index acfcf7af1f..b16b709e92 100644 --- a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj +++ b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj @@ -2080,6 +2080,7 @@ ABC9A36297D869E49C152CAB /* SwapRevokeConfirmationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A1360FE305343B1049CF /* SwapRevokeConfirmationViewController.swift */; }; ABC9A36D3A4EEABF6EA6DBA0 /* Shake.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A104D916039D690E454E /* Shake.swift */; }; ABC9A372F53F1F1D59BF8969 /* RestoreFileConfigurationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9ADA345301F29B947F281 /* RestoreFileConfigurationModule.swift */; }; + ABC9A37FB71FA7DA14553EFC /* RawFullBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A819E6708797C571CA0B /* RawFullBackup.swift */; }; ABC9A394D1D0D165160F5F43 /* RestoreFileConfigurationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AD9A0F91C825D149B289 /* RestoreFileConfigurationService.swift */; }; ABC9A395A96C1F7C30F21940 /* WalletConnectPendingRequestsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AD3F677671FB57CCD886 /* WalletConnectPendingRequestsService.swift */; }; ABC9A3B155B3F6E7E0F2CB07 /* HudHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A381CB4C09FF7CB62A94 /* HudHelper.swift */; }; @@ -2403,6 +2404,7 @@ ABC9AEF62C857F322FFA87E4 /* ContactBookAddressService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AC8CCF3B57FDFC817356 /* ContactBookAddressService.swift */; }; ABC9AF04946C86FA6DBD4225 /* RestorePassphraseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AE8D5944EB202A471C80 /* RestorePassphraseViewModel.swift */; }; ABC9AF1729BA19223BB39E06 /* SendEip721ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A7C3BC5FC664BBF14C4F /* SendEip721ViewModel.swift */; }; + ABC9AF309AAE5C54D2020B23 /* RawFullBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A819E6708797C571CA0B /* RawFullBackup.swift */; }; ABC9AF371FBB4BEA654A78B6 /* MetadataMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AA2491ADC4E5E089CD42 /* MetadataMonitor.swift */; }; ABC9AF4D82ACDFBFBDC2D23C /* MarketCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A37521CD6E2CC5BA4E68 /* MarketCardView.swift */; }; ABC9AF5B0B1D5FE002288AE1 /* FileStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB0A37663BC3F17C7A81 /* FileStorage.swift */; }; @@ -3882,6 +3884,7 @@ ABC9A80143F95E28346C81FE /* SendMemoInputService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendMemoInputService.swift; sourceTree = ""; }; ABC9A806FD17A129212E3F7C /* NftAssetOverviewViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NftAssetOverviewViewController.swift; sourceTree = ""; }; ABC9A8080797194017F736AB /* ContactBookContactViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactBookContactViewModel.swift; sourceTree = ""; }; + ABC9A819E6708797C571CA0B /* RawFullBackup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawFullBackup.swift; sourceTree = ""; }; ABC9A82A1E9AE6CC0E24756B /* SendNftModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendNftModule.swift; sourceTree = ""; }; ABC9A830FE79DBF62FD63CC4 /* ThemeMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeMode.swift; sourceTree = ""; }; ABC9A845B2969166028BA5F0 /* WalletConnectAppShowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletConnectAppShowView.swift; sourceTree = ""; }; @@ -7230,6 +7233,7 @@ ABC9AECEEB35D57CB0965E79 /* WalletBackup.swift */, ABC9A41F6AA0B65FDA91EB68 /* FullBackup.swift */, ABC9AA7FC181E0E0FB74BEF5 /* SettingsBackup.swift */, + ABC9A819E6708797C571CA0B /* RawFullBackup.swift */, ); path = Crypto; sourceTree = ""; @@ -9459,6 +9463,7 @@ ABC9A904FCE6BFE793C944AE /* RestoreFileConfigurationModule.swift in Sources */, ABC9A394D1D0D165160F5F43 /* RestoreFileConfigurationService.swift in Sources */, ABC9A481F1C13DBAAD3F632B /* RestoreFileConfigurationViewModel.swift in Sources */, + ABC9AF309AAE5C54D2020B23 /* RawFullBackup.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -10785,6 +10790,7 @@ ABC9A372F53F1F1D59BF8969 /* RestoreFileConfigurationModule.swift in Sources */, ABC9A2A327AF3D72F9842DCA /* RestoreFileConfigurationService.swift in Sources */, ABC9A2F6D2A2AAFA31C64BAB /* RestoreFileConfigurationViewModel.swift in Sources */, + ABC9A37FB71FA7DA14553EFC /* RawFullBackup.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/BackupCrypto.swift b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/BackupCrypto.swift index e3fb6d3577..4f670d4b3a 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/BackupCrypto.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/BackupCrypto.swift @@ -50,7 +50,7 @@ class BackupCrypto: Codable { } extension BackupCrypto { - func data(passphrase: String) throws -> Data { + func decrypt(passphrase: String) throws -> Data { try Self.validate(passphrase: passphrase) // Validation data guard let data = Data(base64Encoded: cipherText) else { @@ -76,16 +76,6 @@ extension BackupCrypto { kdf: kdfParams ) } - - func accountType(type: AccountType.Abstract, passphrase: String) throws -> AccountType { - let data = try data(passphrase: passphrase) - - guard let accountType = AccountType.decode(uniqueId: data, type: type) else { - throw RestoreCloudModule.RestoreError.invalidBackup - } - - return accountType - } } extension BackupCrypto { @@ -104,7 +94,7 @@ extension BackupCrypto { } } - static func instance(data: Data, passphrase: String, kdf: KdfParams = .defaultBackup) throws -> BackupCrypto { + static func encrypt(data: Data, passphrase: String, kdf: KdfParams = .defaultBackup) throws -> BackupCrypto { let iv = BackupCryptoHelper.generateInitialVector().hs.hex let cipherText = try BackupCryptoHelper.AES128( diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/FullBackup.swift b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/FullBackup.swift index e22ba17c5f..628a5f3d2a 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/FullBackup.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/FullBackup.swift @@ -5,7 +5,7 @@ struct FullBackup { let wallets: [RestoreCloudModule.RestoredBackup] let watchlistIds: [String] let contacts: BackupCrypto? - let settings: SettingsBackup? + let settings: SettingsBackup let version: Int let timestamp: TimeInterval? } @@ -27,7 +27,7 @@ extension FullBackup: Codable { wallets = (try? container.decode([RestoreCloudModule.RestoredBackup].self, forKey: .wallets)) ?? [] watchlistIds = (try? container.decode([String].self, forKey: .watchlistIds)) ?? [] contacts = try? container.decode(BackupCrypto.self, forKey: .contacts) - settings = try? container.decode(SettingsBackup.self, forKey: .settings) + settings = try container.decode(SettingsBackup.self, forKey: .settings) version = try container.decode(Int.self, forKey: .version) timestamp = try? container.decode(TimeInterval.self, forKey: .timestamp) } @@ -38,7 +38,7 @@ extension FullBackup: Codable { if !wallets.isEmpty { try container.encode(wallets, forKey: .wallets) } if !watchlistIds.isEmpty { try container.encode(watchlistIds, forKey: .watchlistIds) } if let contacts { try container.encode(contacts, forKey: .contacts) } - if let settings { try container.encode(settings, forKey: .settings) } + try container.encode(settings, forKey: .settings) try container.encode(version, forKey: .version) try? container.encode(timestamp, forKey: .timestamp) } diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/RawFullBackup.swift b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/RawFullBackup.swift new file mode 100644 index 0000000000..19a02a468f --- /dev/null +++ b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/RawFullBackup.swift @@ -0,0 +1,14 @@ +import Foundation + +struct RawFullBackup { + var accounts: [RawWalletBackup] + let watchlistIds: [String] + let contacts: [BackupContact] + let settings: SettingsBackup + let customSyncSources: [EvmSyncSourceRecord] +} + +struct RawWalletBackup { + let account: Account + let enabledWallets: [WalletBackup.EnabledWallet] +} diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/SettingsBackup.swift b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/SettingsBackup.swift index 2bea154ba2..696c6502ce 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/SettingsBackup.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/SettingsBackup.swift @@ -4,7 +4,7 @@ import CurrencyKit import ThemeKit struct SettingsBackup: Codable { - let evmSyncSources: EvmSyncSourceManager.SyncSourceBackup + var evmSyncSources: EvmSyncSourceManager.SyncSourceBackup let btcModes: [BtcBlockchainManager.BtcRestoreModeBackup] let lockTimeEnabled: Bool diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Managers/CloudBackupManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Managers/CloudBackupManager.swift index 4d9c105b0e..52dd8e128d 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Managers/CloudBackupManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Managers/CloudBackupManager.swift @@ -76,7 +76,7 @@ class CloudBackupManager { let oneWalletItemsV2: [String: RestoreCloudModule.RestoredBackup] = try Self.downloadItems(url: url, fileStorage: fileStorage, logger: logger) let mapped = oneWalletItemsV2.reduce(into: [:]) { $0[$1.value.name] = $1.value.walletBackup } - oneWalletItems.merge(mapped) { backup, backup2 in backup2 } + oneWalletItems.merge(mapped) { _, backup2 in backup2 } let fullBackupItems: [String: FullBackup] = try Self.downloadItems(url: url, fileStorage: fileStorage, logger: logger) state = .success @@ -159,8 +159,9 @@ extension CloudBackupManager { } func save(account: Account, passphrase: String, name: String) throws { - let backup = try appBackupProvider.walletBackup( + let backup = try AppBackupProvider.encrypt( account: account, + wallets: appBackupProvider.enabledWallets(account: account), passphrase: passphrase ) @@ -173,16 +174,14 @@ extension CloudBackupManager { } } - private func data(fields: [AppBackupProvider.Field], passphrase: String) throws -> Data { - let backup = try appBackupProvider.fullBackup( - fields: fields, - passphrase: passphrase - ) + private func data(accountIds: [String], passphrase: String) throws -> Data { + let rawBackup = appBackupProvider.fullBackup(accountIds: accountIds) + let backup = try appBackupProvider.encrypt(raw: rawBackup, passphrase: passphrase) return try JSONEncoder().encode(backup) } - func file(fields: [AppBackupProvider.Field], passphrase: String, name: String) throws -> URL { - let data = try data(fields: fields, passphrase: passphrase) + func file(accountIds: [String], passphrase: String, name: String) throws -> URL { + let data = try data(accountIds: accountIds, passphrase: passphrase) // save book to temporary file guard let temporaryFileUrl = ContactBookManager.localUrl?.appendingPathComponent(name + ".json") else { @@ -193,9 +192,9 @@ extension CloudBackupManager { return temporaryFileUrl } - func save(fields: [AppBackupProvider.Field], passphrase: String, name: String) throws { + func save(accountIds: [String], passphrase: String, name: String) throws { do { - let encoded = try data(fields: fields, passphrase: passphrase) + let encoded = try data(accountIds: accountIds, passphrase: passphrase) try save(encoded: encoded, name: name) } catch { logger?.log(level: .debug, message: "CloudAccountManager.downloadItems, can't save \(name). Because: \(error)") diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmBlockchainManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmBlockchainManager.swift index fc851d6ac0..010a5bb92f 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmBlockchainManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmBlockchainManager.swift @@ -3,7 +3,7 @@ import MarketKit import HsToolKit class EvmBlockchainManager { - private let blockchainTypes: [BlockchainType] = [ + static let blockchainTypes: [BlockchainType] = [ .ethereum, .binanceSmartChain, .polygon, @@ -24,7 +24,7 @@ class EvmBlockchainManager { var allBlockchains: [Blockchain] { do { - return try marketKit.blockchains(uids: blockchainTypes.map { $0.uid }) + return try marketKit.blockchains(uids: EvmBlockchainManager.blockchainTypes.map { $0.uid }) } catch { return [] } diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmSyncSourceManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmSyncSourceManager.swift index 953b4e6283..316a3911ba 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmSyncSourceManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmSyncSourceManager.swift @@ -286,54 +286,66 @@ extension EvmSyncSourceManager { } extension EvmSyncSourceManager { - func backup(passphrase: String) -> SyncSourceBackup { - let customSources = ((try? evmSyncSourceStorage.getAll()) ?? []) - .map { record in - let crypto = record.auth - .flatMap { $0.isEmpty ? nil : $0 } - .flatMap { $0.data(using: .utf8) } - .flatMap { try? BackupCrypto.instance(data: $0, passphrase: passphrase) } - - return CustomSyncSource( - blockchainTypeUid: record.blockchainTypeUid, - url: record.url, - auth: crypto - ) - } - let selected = BlockchainType - .supported - .filter { !$0.allowedProviders.isEmpty } + var customSources: [EvmSyncSourceRecord] { + (try? evmSyncSourceStorage.getAll()) ?? [] + } + + var selectedSources: [SelectedSource] { + EvmBlockchainManager + .blockchainTypes .map { type in SelectedSource( blockchainTypeUid: type.uid, url: syncSource(blockchainType: type).rpcSource.url.absoluteString ) } - return .init(selected: selected, custom: customSources) } +} - func restore(backup: SyncSourceBackup, passphrase: String? = nil) { - var blockchainTypes = Set() - backup.custom.forEach { source in - let auth: String? = source.auth.flatMap { - guard let passphrase else { return nil } - return try? $0.data(passphrase: passphrase) - } +extension EvmSyncSourceManager { + func decrypt(sources: [CustomSyncSource], passphrase: String) throws -> [EvmSyncSourceRecord] { + try sources.map { source in + let auth = try source.auth + .flatMap { try $0.decrypt(passphrase: passphrase) } .flatMap { String(data: $0, encoding: .utf8) } - let record = EvmSyncSourceRecord( + + return EvmSyncSourceRecord( blockchainTypeUid: source.blockchainTypeUid, url: source.url, auth: auth ) + } + } + func encrypt(sources: [EvmSyncSourceRecord], passphrase: String) throws -> [CustomSyncSource] { + try sources.map { source in + let crypto = try source.auth + .flatMap { $0.isEmpty ? nil : $0 } + .flatMap { $0.data(using: .utf8) } + .flatMap { try BackupCrypto.encrypt(data: $0, passphrase: passphrase) } + + return CustomSyncSource( + blockchainTypeUid: source.blockchainTypeUid, + url: source.url, + auth: crypto + ) + } + } +} + +extension EvmSyncSourceManager { + func restore(selected: [SelectedSource], custom: [EvmSyncSourceRecord]) { + var blockchainTypes = Set() + custom.forEach { source in blockchainTypes.insert(BlockchainType(uid: source.blockchainTypeUid)) - try? evmSyncSourceStorage.save(record: record) + try? evmSyncSourceStorage.save(record: source) } - backup.selected.forEach { source in + selected.forEach { source in let blockchainType = BlockchainType(uid: source.blockchainTypeUid) if let syncSource = allSyncSources(blockchainType: blockchainType) - .first(where: { $0.rpcSource.url.absoluteString == source.url }) { + .first(where: { $0.rpcSource.url.absoluteString == source.url }) + { saveCurrent(syncSource: syncSource, blockchainType: blockchainType) } } diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Storage/ContactBookManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Storage/ContactBookManager.swift index ef32071e96..26cb76fe8c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Storage/ContactBookManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Storage/ContactBookManager.swift @@ -431,8 +431,14 @@ extension ContactBookManager { } extension ContactBookManager { - static func encode(crypto: BackupCrypto, passphrase: String) throws -> [BackupContact] { - let data = try crypto.data(passphrase: passphrase) + static func encrypt(contacts: [BackupContact], passphrase: String) throws -> BackupCrypto { + let encoder = JSONEncoder() + let data = try encoder.encode(contacts) + return try BackupCrypto.encrypt(data: data, passphrase: passphrase) + } + + static func decrypt(crypto: BackupCrypto, passphrase: String) throws -> [BackupContact] { + let data = try crypto.decrypt(passphrase: passphrase) let decoder = JSONDecoder() let contacts = try decoder.decode([BackupContact].self, from: data) diff --git a/UnstoppableWallet/UnstoppableWallet/Models/AccountType.swift b/UnstoppableWallet/UnstoppableWallet/Models/AccountType.swift index 4151add3f7..457064d8ff 100644 --- a/UnstoppableWallet/UnstoppableWallet/Models/AccountType.swift +++ b/UnstoppableWallet/UnstoppableWallet/Models/AccountType.swift @@ -1,10 +1,10 @@ +import BitcoinCore +import Crypto +import EvmKit import Foundation import HdWalletKit -import EvmKit -import TronKit -import BitcoinCore import MarketKit -import Crypto +import TronKit enum AccountType { case mnemonic(words: [String], salt: String, bip39Compliant: Bool) @@ -18,8 +18,8 @@ enum AccountType { switch self { case let .mnemonic(words, salt, bip39Compliant): return bip39Compliant - ? Mnemonic.seed(mnemonic: words, passphrase: salt) - : Mnemonic.seedNonStandard(mnemonic: words, passphrase: salt) + ? Mnemonic.seed(mnemonic: words, passphrase: salt) + : Mnemonic.seedNonStandard(mnemonic: words, passphrase: salt) default: return nil } @@ -79,7 +79,7 @@ enum AccountType { case (.tron, .native), (.tron, .eip20): return true default: return false } - case .hdExtendedKey(let key): + case let .hdExtendedKey(key): switch token.blockchainType { case .bitcoin, .litecoin: guard let derivation = token.type.derivation, key.purposes.contains(where: { $0.mnemonicDerivation == derivation }) else { @@ -141,7 +141,7 @@ enum AccountType { var withdrawalAllowed: Bool { switch self { - case .cex(cexAccount: let account): return account.cex.withdrawalAllowed + case let .cex(cexAccount: account): return account.cex.withdrawalAllowed default: return true } } @@ -155,7 +155,7 @@ enum AccountType { var description: String { switch self { - case .mnemonic(let words, let salt, _): + case let .mnemonic(words, salt, _): let count = "\(words.count)" return salt.isEmpty ? "manage_accounts.n_words".localized(count) : "manage_accounts.n_words_with_passphrase".localized(count) case .evmPrivateKey: @@ -164,7 +164,7 @@ enum AccountType { return "EVM Address" case .tronAddress: return "TRON Address" - case .hdExtendedKey(let key): + case let .hdExtendedKey(key): switch key { case .private: switch key.derivedType { @@ -178,16 +178,16 @@ enum AccountType { default: return "" } } - case .cex(let cexAccount): + case let .cex(cexAccount): return cexAccount.cex.title } } var detailedDescription: String { switch self { - case .evmAddress(let address): + case let .evmAddress(address): return address.eip55.shortened - case .tronAddress(let address): + case let .tronAddress(address): return address.base58.shortened default: return description } @@ -201,7 +201,7 @@ enum AccountType { } return try? Signer.address(seed: mnemonicSeed, chain: chain) - case .evmPrivateKey(let data): + case let .evmPrivateKey(data): return Signer.address(privateKey: data) default: return nil @@ -220,17 +220,15 @@ enum AccountType { } return try? EvmKit.Kit.sign(message: message, privateKey: privateKey, isLegacy: isLegacy) - case .evmPrivateKey(let data): + case let .evmPrivateKey(data): return try? EvmKit.Kit.sign(message: message, privateKey: data, isLegacy: isLegacy) default: return nil } } - } extension AccountType { - private static func split(_ string: String, separator: String) -> (String, String) { if let index = string.firstIndex(of: Character(separator)) { let left = String(string.prefix(upTo: index)) @@ -256,7 +254,7 @@ extension AccountType { return AccountType.evmPrivateKey(data: uniqueId) case .hdExtendedKey: do { - return AccountType.hdExtendedKey(key: try HDExtendedKey(data: uniqueId)) + return try AccountType.hdExtendedKey(key: HDExtendedKey(data: uniqueId)) } catch { return nil } @@ -264,7 +262,7 @@ extension AccountType { return AccountType.evmAddress(address: EvmKit.Address(raw: uniqueId)) case .tronAddress: do { - return AccountType.tronAddress(address: try TronKit.Address(raw: uniqueId)) + return try AccountType.tronAddress(address: TronKit.Address(raw: uniqueId)) } catch { return nil } @@ -278,12 +276,12 @@ extension AccountType { } enum Abstract: String, Codable { - case mnemonic = "mnemonic" + case mnemonic case evmPrivateKey = "private_key" case evmAddress = "evm_address" case tronAddress = "tron_address" case hdExtendedKey = "hd_extended_key" - case cex = "cex" + case cex init(_ type: AccountType) { switch type { @@ -303,24 +301,22 @@ extension AccountType { } } } - } extension AccountType: Hashable { - - public static func ==(lhs: AccountType, rhs: AccountType) -> Bool { + public static func == (lhs: AccountType, rhs: AccountType) -> Bool { switch (lhs, rhs) { case (let .mnemonic(lhsWords, lhsSalt, lhsBip39Compliant), let .mnemonic(rhsWords, rhsSalt, rhsBip39Compliant)): return lhsWords == rhsWords && lhsSalt == rhsSalt && lhsBip39Compliant == rhsBip39Compliant - case (let .evmPrivateKey(lhsData), let .evmPrivateKey(rhsData)): + case let (.evmPrivateKey(lhsData), .evmPrivateKey(rhsData)): return lhsData == rhsData - case (let .evmAddress(lhsAddress), let .evmAddress(rhsAddress)): + case let (.evmAddress(lhsAddress), .evmAddress(rhsAddress)): return lhsAddress == rhsAddress - case (let .tronAddress(lhsAddress), let .tronAddress(rhsAddress)): + case let (.tronAddress(lhsAddress), .tronAddress(rhsAddress)): return lhsAddress == rhsAddress - case (let .hdExtendedKey(lhsKey), let .hdExtendedKey(rhsKey)): + case let (.hdExtendedKey(lhsKey), .hdExtendedKey(rhsKey)): return lhsKey == rhsKey - case (let .cex(lhsCexAccount), let .cex(rhsCexAccount)): + case let (.cex(lhsCexAccount), .cex(rhsCexAccount)): return lhsCexAccount == rhsCexAccount default: return false } @@ -350,5 +346,16 @@ extension AccountType: Hashable { hasher.combine(cexAccount) } } +} + +extension AccountType { + static func decrypt(crypto: BackupCrypto, type: AccountType.Abstract, passphrase: String) throws -> AccountType { + let data = try crypto.decrypt(passphrase: passphrase) + guard let accountType = AccountType.decode(uniqueId: data, type: type) else { + throw RestoreCloudModule.RestoreError.invalidBackup + } + + return accountType + } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/AppBackupProvider.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/AppBackupProvider.swift index fbf37d1d14..14ff7a2f2c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/AppBackupProvider.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/AppBackupProvider.swift @@ -65,19 +65,9 @@ class AppBackupProvider { self.contactManager = contactManager } - private func walletBackups(ids: [String], passphrase: String) -> [RestoreCloudModule.RestoredBackup] { - ids.compactMap { - accountManager.account(id: $0) - }.compactMap { - try? walletBackup(account: $0, passphrase: passphrase) - } - } -} - -extension AppBackupProvider { - func walletBackup(account: Account, passphrase: String) throws -> RestoreCloudModule.RestoredBackup { - let wallets = App.shared - .walletManager + // Parts of backups + func enabledWallets(account: Account) -> [WalletBackup.EnabledWallet] { + walletManager .wallets(account: account).map { let settings = restoreSettingsManager .settings(accountId: account.id, blockchainType: $0.token.blockchainType) @@ -85,108 +75,75 @@ extension AppBackupProvider { return WalletBackup.EnabledWallet($0, settings: settings) } - return try AppBackupProvider.walletBackup( - accountType: account.type, - wallets: wallets, - isManualBackedUp: account.backedUp, - isFileBackedUp: account.fileBackedUp, - name: account.name, - passphrase: passphrase - ) } - func fullBackup(fields: [Field], passphrase: String) throws -> FullBackup { - var wallets = [RestoreCloudModule.RestoredBackup]() - var watchlistIds = [String]() - var contacts = [BackupContact]() - var settings: SettingsBackup? - for field in fields { - switch field { - case let .accounts(ids): - wallets.append(contentsOf: walletBackups(ids: ids, passphrase: passphrase)) - case .watchlist: - watchlistIds = favoritesManager.allCoinUids - case .contacts: - contacts = contactManager.backupContactBook?.contacts ?? [] - case .settings: - let providers: [SettingsBackup.DefaultProvider] = BlockchainType - .supported - .filter { !$0.allowedProviders.isEmpty } - .map { - SettingsBackup.DefaultProvider( - blockchainTypeId: $0.uid, - provider: localStorage.defaultProvider(blockchainType: $0).id - ) - } - settings = SettingsBackup( - evmSyncSources: evmSyncSourceManager.backup(passphrase: passphrase), - btcModes: btcBlockchainManager.backup, - lockTimeEnabled: localStorage.lockTimeEnabled, - remoteContactsSync: localStorage.remoteContactsSync, - swapProviders: providers, - chartIndicators: chartRepository.backup, - indicatorsShown: localStorage.indicatorsShown, - currentLanguage: languageManager.currentLanguage, - baseCurrency: currencyKit.baseCurrency.code, - mode: themeManager.themeMode, - showMarketTab: launchScreenManager.showMarket, - launchScreen: launchScreenManager.launchScreen, - conversionTokenQueryId: balanceConversionManager.conversionToken?.tokenQuery.id, - balancePrimaryValue: balancePrimaryValueManager.balancePrimaryValue, - balanceAutoHide: balanceHiddenManager.balanceAutoHide, - appIcon: appIconManager.appIcon.title + private var swapProviders: [SettingsBackup.DefaultProvider] { + EvmBlockchainManager + .blockchainTypes + .map { + SettingsBackup.DefaultProvider( + blockchainTypeId: $0.uid, + provider: localStorage.defaultProvider(blockchainType: $0).id ) } - } + } - guard !wallets.isEmpty || - !watchlistIds.isEmpty || - !contacts.isEmpty || - settings != nil - else { - throw CodingError.emptyParameters - } + private func settings(evmSyncSources: EvmSyncSourceManager.SyncSourceBackup) -> SettingsBackup { + SettingsBackup( + evmSyncSources: evmSyncSources, + btcModes: btcBlockchainManager.backup, + lockTimeEnabled: localStorage.lockTimeEnabled, + remoteContactsSync: localStorage.remoteContactsSync, + swapProviders: swapProviders, + chartIndicators: chartRepository.backup, + indicatorsShown: localStorage.indicatorsShown, + currentLanguage: languageManager.currentLanguage, + baseCurrency: currencyKit.baseCurrency.code, + mode: themeManager.themeMode, + showMarketTab: launchScreenManager.showMarket, + launchScreen: launchScreenManager.launchScreen, + conversionTokenQueryId: balanceConversionManager.conversionToken?.tokenQuery.id, + balancePrimaryValue: balancePrimaryValueManager.balancePrimaryValue, + balanceAutoHide: balanceHiddenManager.balanceAutoHide, + appIcon: appIconManager.appIcon.title + ) + } - var contactCrypto: BackupCrypto? - if !contacts.isEmpty { - let encoder = JSONEncoder() - let data = try encoder.encode(contacts) - contactCrypto = try BackupCrypto.instance(data: data, passphrase: passphrase) + func encrypt(accountIds: [String], passphrase: String) throws -> [RestoreCloudModule.RestoredBackup] { + try accountIds.compactMap { + accountManager.account(id: $0) + }.compactMap { + try Self.encrypt(account: $0, wallets: enabledWallets(account: $0), passphrase: passphrase) } + } - return FullBackup( - id: UUID().uuidString, - wallets: wallets, - watchlistIds: watchlistIds, - contacts: contactCrypto, - settings: settings, - version: AppBackupProvider.version, - timestamp: Date().timeIntervalSince1970.rounded() + func fullBackup(accountIds: [String]) -> RawFullBackup { + let accounts = accountIds + .compactMap { accountManager.account(id: $0) } + .compactMap { RawWalletBackup(account: $0, enabledWallets: enabledWallets(account: $0)) } + + let custom = evmSyncSourceManager.customSources + let selected = evmSyncSourceManager.selectedSources + let syncSources = EvmSyncSourceManager.SyncSourceBackup(selected: selected, custom: []) + return RawFullBackup( + accounts: accounts, + watchlistIds: favoritesManager.allCoinUids, + contacts: contactManager.backupContactBook?.contacts ?? [], + settings: settings(evmSyncSources: syncSources), + customSyncSources: custom ) } +} - func walletRestore(backup: RestoreCloudModule.RestoredBackup, accountType: AccountType) { - switch accountType { +extension AppBackupProvider { + func restore(raw: RawWalletBackup) { + switch raw.account.type { case .cex: - let account = accountFactory.account( - type: accountType, - origin: .restored, - backedUp: backup.walletBackup.isManualBackedUp, - fileBackedUp: backup.walletBackup.isFileBackedUp, - name: backup.name - ) - accountManager.save(account: account) + accountManager.save(account: raw.account) default: - let account = accountFactory.account( - type: accountType, - origin: .restored, - backedUp: backup.walletBackup.isManualBackedUp, - fileBackedUp: backup.walletBackup.isFileBackedUp, - name: backup.name - ) - accountManager.save(account: account) + accountManager.save(account: raw.account) - let wallets = backup.walletBackup.enabledWallets.map { + let wallets = raw.enabledWallets.map { if !$0.settings.isEmpty { var restoreSettings = [RestoreSettingType: String]() $0.settings.forEach { key, value in @@ -195,12 +152,12 @@ extension AppBackupProvider { } } if let tokenQuery = TokenQuery(id: $0.tokenQueryId) { - restoreSettingsManager.save(settings: restoreSettings, account: account, blockchainType: tokenQuery.blockchainType) + restoreSettingsManager.save(settings: restoreSettings, account: raw.account, blockchainType: tokenQuery.blockchainType) } } return EnabledWallet( tokenQueryId: $0.tokenQueryId, - accountId: account.id, + accountId: raw.account.id, coinName: $0.coinName, coinCode: $0.coinCode, tokenDecimals: $0.tokenDecimals @@ -210,83 +167,107 @@ extension AppBackupProvider { } } - func fullRestore(backup: FullBackup, passphrase: String) throws { - var encryptionError: Error? - var encodedWallets = [(RestoreCloudModule.RestoredBackup, AccountType)]() - backup.wallets.forEach { wallet in - do { - let accountType = try wallet - .walletBackup - .crypto - .accountType(type: wallet.walletBackup.type, passphrase: passphrase) - encodedWallets.append((wallet, accountType)) - } catch { - encryptionError = error - } + func restore(raw: RawFullBackup) { + raw.accounts.forEach { wallet in + restore(raw: wallet) } + favoritesManager.add(coinUids: raw.watchlistIds) - if encodedWallets.count != backup.wallets.count { - encryptionError = CodingError.invalidPassword + if !raw.contacts.isEmpty { + try? contactManager.restore(contacts: raw.contacts) } - if let encryptionError { - throw encryptionError + evmSyncSourceManager.restore(selected: raw.settings.evmSyncSources.selected, custom: raw.customSyncSources) + btcBlockchainManager.restore(backup: raw.settings.btcModes) + chartRepository.restore(backup: raw.settings.chartIndicators) + localStorage.restore(backup: raw.settings) + languageManager.currentLanguage = raw.settings.currentLanguage + if let currency = currencyKit.currencies.first(where: { $0.code == raw.settings.baseCurrency }) { + currencyKit.baseCurrency = currency } - // restore only if all wallet was encrypted with password - encodedWallets.forEach { wallet in - walletRestore( - backup: wallet.0, - accountType: wallet.1 - ) + themeManager.themeMode = raw.settings.mode + launchScreenManager.showMarket = raw.settings.showMarketTab + launchScreenManager.launchScreen = raw.settings.launchScreen + + balanceConversionManager.set(tokenQueryId: raw.settings.conversionTokenQueryId) + balanceHiddenManager.set(balanceAutoHide: raw.settings.balanceAutoHide) + let appIcon = AppIconManager.allAppIcons.first { $0.title == raw.settings.appIcon } ?? .main + if appIconManager.appIcon != appIcon { + appIconManager.appIcon = appIcon } + } +} - favoritesManager.add(coinUids: backup.watchlistIds) +extension AppBackupProvider { + func decrypt(walletBackup: WalletBackup, name: String, passphrase: String) throws -> RawWalletBackup { + let accountType = try AccountType.decrypt( + crypto: walletBackup.crypto, + type: walletBackup.type, + passphrase: passphrase + ) + let account = accountFactory.account( + type: accountType, + origin: .restored, + backedUp: walletBackup.isManualBackedUp, + fileBackedUp: walletBackup.isFileBackedUp, + name: name + ) - if let contacts = backup.contacts { - let contacts = try ContactBookManager.encode(crypto: contacts, passphrase: passphrase) - try contactManager.restore(contacts: contacts) - } + return RawWalletBackup(account: account, enabledWallets: walletBackup.enabledWallets) + } - if let settings = backup.settings { - evmSyncSourceManager.restore(backup: settings.evmSyncSources) - btcBlockchainManager.restore(backup: settings.btcModes) - chartRepository.restore(backup: settings.chartIndicators) - localStorage.restore(backup: settings) - languageManager.currentLanguage = settings.currentLanguage - if let currency = currencyKit.currencies.first(where: { $0.code == settings.baseCurrency }) { - currencyKit.baseCurrency = currency - } - themeManager.themeMode = settings.mode - launchScreenManager.showMarket = settings.showMarketTab - launchScreenManager.launchScreen = settings.launchScreen + func decrypt(fullBackup: FullBackup, passphrase: String) throws -> RawFullBackup { + let wallets = try fullBackup.wallets + .map { try decrypt(walletBackup: $0.walletBackup, name: $0.name, passphrase: passphrase) } - balanceConversionManager.set(tokenQueryId: settings.conversionTokenQueryId) - balanceHiddenManager.set(balanceAutoHide: settings.balanceAutoHide) - let appIcon = AppIconManager.allAppIcons.first { $0.title == settings.appIcon } ?? .main - if appIconManager.appIcon != appIcon { - appIconManager.appIcon = appIcon - } + let contacts = try fullBackup.contacts.map { try ContactBookManager.decrypt(crypto: $0, passphrase: passphrase) } + + let customSources = try evmSyncSourceManager.decrypt(sources: fullBackup.settings.evmSyncSources.custom, passphrase: passphrase) + + return RawFullBackup( + accounts: wallets, + watchlistIds: fullBackup.watchlistIds, + contacts: contacts ?? [], + settings: fullBackup.settings, + customSyncSources: customSources + ) + } + + func encrypt(raw: RawFullBackup, passphrase: String) throws -> FullBackup { + let wallets = try raw.accounts.map { + try Self.encrypt(account: $0.account, wallets: $0.enabledWallets, passphrase: passphrase) } + + let contacts = try ContactBookManager.encrypt(contacts: raw.contacts, passphrase: passphrase) + let custom = try evmSyncSourceManager.encrypt(sources: raw.customSyncSources, passphrase: passphrase) + + return FullBackup( + id: UUID().uuidString, + wallets: wallets, + watchlistIds: raw.watchlistIds, + contacts: contacts, + settings: settings(evmSyncSources: .init(selected: raw.settings.evmSyncSources.selected, custom: custom)), + version: AppBackupProvider.version, + timestamp: Date().timeIntervalSince1970.rounded() + ) } -} -extension AppBackupProvider { - static func walletBackup(accountType: AccountType, wallets: [WalletBackup.EnabledWallet], isManualBackedUp: Bool, isFileBackedUp: Bool, name: String, passphrase: String) throws -> RestoreCloudModule.RestoredBackup { - let message = accountType.uniqueId(hashed: false) - let crypto = try BackupCrypto.instance(data: message, passphrase: passphrase) + static func encrypt(account: Account, wallets: [WalletBackup.EnabledWallet], passphrase: String) throws -> RestoreCloudModule.RestoredBackup { + let message = account.type.uniqueId(hashed: false) + let crypto = try BackupCrypto.encrypt(data: message, passphrase: passphrase) let walletBackup = WalletBackup( crypto: crypto, enabledWallets: wallets, - id: accountType.uniqueId().hs.hex, - type: AccountType.Abstract(accountType), - isManualBackedUp: isManualBackedUp, - isFileBackedUp: isFileBackedUp, + id: account.type.uniqueId().hs.hex, + type: AccountType.Abstract(account.type), + isManualBackedUp: account.backedUp, + isFileBackedUp: account.fileBackedUp, version: Self.version, timestamp: Date().timeIntervalSince1970.rounded() ) - return .init(name: name, walletBackup: walletBackup) + return .init(name: account.name, walletBackup: walletBackup) } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/BakcupFileConfiguration/RestoreFileConfigurationService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/BakcupFileConfiguration/RestoreFileConfigurationService.swift index af02e1fa27..3eef61fed6 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/BakcupFileConfiguration/RestoreFileConfigurationService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/BakcupFileConfiguration/RestoreFileConfigurationService.swift @@ -35,14 +35,14 @@ extension RestoreFileConfigurationService { .filter { $0.walletBackup.type.isWatch } .count - let contacts = fullBackup.contacts.flatMap { try? ContactBookManager.encode(crypto: $0, passphrase: passphrase) } + let contacts = fullBackup.contacts.flatMap { try? ContactBookManager.decrypt(crypto: $0, passphrase: passphrase) } let contactAddressCount = (contacts ?? []).reduce(into: 0) { $0 += $1.addresses.count } return BackupAppModule.items( watchAccountCount: watchAccountCount, watchlistCount: fullBackup.watchlistIds.count, contactAddressCount: contactAddressCount, - blockchainSourcesCount: fullBackup.settings?.evmSyncSources.custom.count ?? 0 + blockchainSourcesCount: fullBackup.settings.evmSyncSources.custom.count ) } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseService.swift index c8ad21d080..40ff63bced 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseService.swift @@ -31,34 +31,25 @@ extension RestorePassphraseService { func next() async throws -> RestoreResult { switch restoredBackup.source { case let .wallet(walletBackup): - let accountType = try walletBackup - .crypto - .accountType(type: walletBackup.type, passphrase: passphrase) - let restoredBackup = RestoreCloudModule.RestoredBackup(name: restoredBackup.name, walletBackup: walletBackup) - appBackupProvider.walletRestore(backup: restoredBackup, accountType: accountType) - switch accountType { + let rawBackup = try appBackupProvider.decrypt(walletBackup: walletBackup, name: restoredBackup.name, passphrase: passphrase) + appBackupProvider.restore(raw: rawBackup) + switch rawBackup.account.type { case .cex: return .success default: - return .restoredAccount(RestoreCloudModule.RestoredAccount( - name: restoredBackup.name, - accountType: accountType, - isManualBackedUp: walletBackup.isManualBackedUp, - isFileBackedUp: walletBackup.isFileBackedUp, - showSelectCoins: walletBackup.enabledWallets.isEmpty - )) + return .restoredAccount(rawBackup) } - case .full: - print("Lets try!") - return .success + case let .full(fullBackup): + let rawBackup = try appBackupProvider.decrypt(fullBackup: fullBackup, passphrase: passphrase) + return .restoredFullBackup(rawBackup) } } } extension RestorePassphraseService { enum RestoreResult { - case restoredAccount(RestoreCloudModule.RestoredAccount) - case source(BackupModule.Source) + case restoredAccount(RawWalletBackup) + case restoredFullBackup(RawFullBackup) case success } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseViewController.swift index 5a14b0d4b9..cffa763a92 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseViewController.swift @@ -99,12 +99,12 @@ class RestorePassphraseViewController: KeyboardAwareViewController { viewModel.openSelectCoinsPublisher .receive(on: DispatchQueue.main) - .sink { [weak self] backupAccount in + .sink { [weak self] account in self?.openSelectCoins( - accountName: backupAccount.name, - accountType: backupAccount.accountType, - isManualBackedUp: backupAccount.isManualBackedUp, - isFileBackedUp: backupAccount.isFileBackedUp + accountName: account.name, + accountType: account.type, + isManualBackedUp: account.backedUp, + isFileBackedUp: account.fileBackedUp ) } .store(in: &cancellables) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseViewModel.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseViewModel.swift index c313b81d19..374adadebf 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseViewModel.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreFile/RestorePassphrase/RestorePassphraseViewModel.swift @@ -11,7 +11,7 @@ class RestorePassphraseViewModel { private let clearInputsSubject = PassthroughSubject() private let showErrorSubject = PassthroughSubject() - private let openSelectCoinsSubject = PassthroughSubject() + private let openSelectCoinsSubject = PassthroughSubject() private let successSubject = PassthroughSubject() init(service: RestorePassphraseService) { @@ -34,7 +34,7 @@ extension RestorePassphraseViewModel { showErrorSubject.eraseToAnyPublisher() } - var openSelectCoinsPublisher: AnyPublisher { + var openSelectCoinsPublisher: AnyPublisher { openSelectCoinsSubject.eraseToAnyPublisher() } @@ -67,14 +67,14 @@ extension RestorePassphraseViewModel { switch result { case .success: self?.successSubject.send() - case let .restoredAccount(account): - if account.showSelectCoins { - self?.openSelectCoinsSubject.send(account) + case let .restoredAccount(rawBackup): + if rawBackup.enabledWallets.isEmpty { + self?.openSelectCoinsSubject.send(rawBackup.account) } else { self?.successSubject.send() } - case let .source(source): - print("Open next source list") + case let .restoredFullBackup(rawBackup): + print("Result: \(rawBackup)") } } catch { switch error as? RestoreCloudModule.RestoreError { diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Settings/BackupApp/Backup/BackupAppViewModel.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Settings/BackupApp/Backup/BackupAppViewModel.swift index e0373a8722..8e52d037ae 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Settings/BackupApp/Backup/BackupAppViewModel.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Settings/BackupApp/Backup/BackupAppViewModel.swift @@ -162,30 +162,6 @@ extension BackupAppViewModel { blockchainSourcesCount: evmSyncSourceManager.customSyncSources(blockchainType: nil).count ) } - - var configuration: [AppBackupProvider.Field] { - var fields = [AppBackupProvider.Field.settings] - - var accountIds = accounts(watch: true).map { $0.id } - selected.forEach { id, selected in - if selected { - accountIds.append(id) - } - } - - fields.append(.accounts(ids: accountIds)) - - let contacts = contactManager.all ?? [] - if contacts.count != 0 { - fields.append(.contacts) - } - - if !favoritesManager.allCoinUids.isEmpty { - fields.append(.watchlist) - } - - return fields - } } extension BackupAppViewModel { @@ -285,7 +261,7 @@ extension BackupAppViewModel { case .none: () case .cloud: do { - try cloudBackupManager.save(fields: configuration, passphrase: password, name: name) + try cloudBackupManager.save(accountIds: accountIds, passphrase: password, name: name) passwordButtonProcessing = false await showSuccess() dismissSubject.send() @@ -295,7 +271,7 @@ extension BackupAppViewModel { } case .local: do { - let url = try cloudBackupManager.file(fields: configuration, passphrase: password, name: name) + let url = try cloudBackupManager.file(accountIds: accountIds, passphrase: password, name: name) sharePresented = url passwordButtonProcessing = false } catch { diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Swap/SwapProviderManager.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Swap/SwapProviderManager.swift index fa1a0b7e5b..969b379d1c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Swap/SwapProviderManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Swap/SwapProviderManager.swift @@ -1,23 +1,23 @@ -import UIKit import MarketKit -import SectionsTableView -import RxSwift +import OneInchKit import RxCocoa +import RxSwift +import SectionsTableView +import UIKit import UniswapKit -import OneInchKit class SwapProviderManager { private let localStorage: LocalStorage private let evmBlockchainManager: EvmBlockchainManager - private let dataSourceUpdatedRelay = PublishRelay<()>() + private let dataSourceUpdatedRelay = PublishRelay() private(set) var dataSourceProvider: ISwapProvider? { didSet { dataSourceUpdatedRelay.accept(()) } } - private let dexUpdatedRelay = PublishRelay<()>() + private let dexUpdatedRelay = PublishRelay() var dex: SwapModule.Dex? { didSet { dexUpdatedRelay.accept(()) @@ -63,11 +63,9 @@ class SwapProviderManager { return OneInchModule(dex: dex, dataSourceState: state) } } - } extension SwapProviderManager: ISwapDexManager { - func set(provider: SwapModule.Dex.Provider) { guard provider != dex?.provider else { return @@ -88,14 +86,12 @@ extension SwapProviderManager: ISwapDexManager { dataSourceProvider = self.provider(dex: dex) } - var dexUpdated: Signal<()> { + var dexUpdated: Signal { dexUpdatedRelay.asSignal() } - } extension SwapProviderManager: ISwapDataSourceManager { - var dataSource: ISwapDataSource? { dataSourceProvider?.dataSource } @@ -104,8 +100,7 @@ extension SwapProviderManager: ISwapDataSourceManager { dataSourceProvider?.settingsDataSource } - var dataSourceUpdated: Signal<()> { + var dataSourceUpdated: Signal { dataSourceUpdatedRelay.asSignal() } - }