From 2cda645fdca90184ba479aaef081104fd3b3ff96 Mon Sep 17 00:00:00 2001 From: ant013 Date: Tue, 19 Sep 2023 17:16:15 +0600 Subject: [PATCH] Implement classes for full backup --- .../project.pbxproj | 54 ++-- .../UnstoppableWallet/Core/App.swift | 30 +- .../Core/Crypto/AppearanceBackup.swift | 29 -- .../Core/Crypto/BackupCrypto.swift | 141 +++++++++ .../Core/Crypto/FullBackup.swift | 42 ++- .../Core/Crypto/SettingsBackup.swift | 55 ++++ .../Core/Crypto/WalletBackup.swift | 8 +- .../Core/Crypto/WalletBackupCrypto.swift | 51 --- .../Managers/BalanceConversionManager.swift | 7 + ...Manager.swift => CloudBackupManager.swift} | 96 +++--- .../Core/Managers/EvmSyncSourceManager.swift | 265 ++++++++++------ .../Core/Storage/ContactBookManager.swift | 10 +- .../Core/Storage/EvmSyncSourceStorage.swift | 6 + .../Storage/FavoriteCoinRecordStorage.swift | 7 + .../Core/Storage/LocalStorage.swift | 63 +--- .../Extensions/ThemeMode.swift | 3 + .../Models/BackupContact.swift | 16 +- .../Models/BalancePrimaryValue.swift | 2 +- .../UnstoppableWallet/Models/Contact.swift | 21 +- .../Models/LaunchScreen.swift | 9 + .../Modules/Backup/BackupModule.swift | 2 +- .../Backup/ICloud/AppBackupProvider.swift | 291 ++++++++++++++++++ .../Backup/ICloud/BackupCloudModule.swift | 6 +- .../ICloud/Name/ICloudBackupNameService.swift | 4 +- .../BackupCloudPassphraseService.swift | 26 +- .../BackupCloudPassphraseViewModel.swift | 14 +- .../Terms/ICloudBackupTermsService.swift | 4 +- .../Backup/ICloud/WalletBackupConverter.swift | 73 ----- .../Modules/Favorites/FavoritesManager.swift | 4 + .../ManageAccount/ManageAccountModule.swift | 2 +- .../ManageAccount/ManageAccountService.swift | 6 +- .../ManageAccounts/ManageAccountsModule.swift | 2 +- .../ManageAccountsService.swift | 6 +- .../RestoreCloud/RestoreCloudModule.swift | 18 +- .../RestoreCloudPassphraseModule.swift | 3 +- .../RestoreCloudPassphraseService.swift | 102 ++---- .../RestoreCloudPassphraseViewModel.swift | 2 +- .../RestoreCloud/RestoreCloudService.swift | 8 +- .../RestoreType/RestoreTypeModule.swift | 2 +- .../RestoreType/RestoreTypeViewModel.swift | 4 +- .../Settings/Main/MainSettingsModule.swift | 2 +- .../Settings/Main/MainSettingsService.swift | 4 +- .../Receive/SelectCoin/CoinProvider.swift | 2 - .../WalletTokenBalanceModule.swift | 2 +- .../WalletTokenBalanceService.swift | 4 +- .../Modules/Wallet/WalletModule.swift | 2 +- .../Modules/Wallet/WalletService.swift | 4 +- .../WalletConnectAppShowModule.swift | 2 +- .../WalletConnectAppShowService.swift | 4 +- 49 files changed, 985 insertions(+), 535 deletions(-) delete mode 100644 UnstoppableWallet/UnstoppableWallet/Core/Crypto/AppearanceBackup.swift create mode 100644 UnstoppableWallet/UnstoppableWallet/Core/Crypto/BackupCrypto.swift create mode 100644 UnstoppableWallet/UnstoppableWallet/Core/Crypto/SettingsBackup.swift delete mode 100644 UnstoppableWallet/UnstoppableWallet/Core/Crypto/WalletBackupCrypto.swift rename UnstoppableWallet/UnstoppableWallet/Core/Managers/{CloudAccountBackupManager.swift => CloudBackupManager.swift} (67%) create mode 100644 UnstoppableWallet/UnstoppableWallet/Extensions/ThemeMode.swift create mode 100644 UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/AppBackupProvider.swift delete mode 100644 UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/WalletBackupConverter.swift diff --git a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj index db5d929dfe..6782e6ca86 100644 --- a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj +++ b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj @@ -1912,8 +1912,8 @@ 6BCD53172A161F4800993F20 /* BackupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BCD53112A161F4800993F20 /* BackupViewController.swift */; }; 6BCD53192A161F9200993F20 /* BackupService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BCD53182A161F9100993F20 /* BackupService.swift */; }; 6BCD531A2A161F9200993F20 /* BackupService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BCD53182A161F9100993F20 /* BackupService.swift */; }; - 6BCD531C2A16203F00993F20 /* CloudAccountBackupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BCD531B2A16203F00993F20 /* CloudAccountBackupManager.swift */; }; - 6BCD531D2A16203F00993F20 /* CloudAccountBackupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BCD531B2A16203F00993F20 /* CloudAccountBackupManager.swift */; }; + 6BCD531C2A16203F00993F20 /* CloudBackupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BCD531B2A16203F00993F20 /* CloudBackupManager.swift */; }; + 6BCD531D2A16203F00993F20 /* CloudBackupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BCD531B2A16203F00993F20 /* CloudBackupManager.swift */; }; 6BDA29AB29D6F37C003847ED /* ECashKit in Frameworks */ = {isa = PBXBuildFile; productRef = 6BDA29AA29D6F37C003847ED /* ECashKit */; }; 6BDA29AD29D6F384003847ED /* ECashKit in Frameworks */ = {isa = PBXBuildFile; productRef = 6BDA29AC29D6F384003847ED /* ECashKit */; }; 6BDA29B029D6F934003847ED /* HsToolKit in Frameworks */ = {isa = PBXBuildFile; productRef = 6BDA29AF29D6F934003847ED /* HsToolKit */; }; @@ -2066,6 +2066,7 @@ ABC9A57EB423CAD56190F36B /* ChartIndicatorSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9ACE2CCBDF21572F5600C /* ChartIndicatorSettingsViewModel.swift */; }; ABC9A59B465A9C59F93DFB96 /* ChartCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A9F6635146BEBFB432D1 /* ChartCell.swift */; }; ABC9A5A0C65184DF54C48C5A /* TechnicalIndicatorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A3EE670713BA4B6110F4 /* TechnicalIndicatorService.swift */; }; + ABC9A5A4C6213D58CDA2EB73 /* ThemeMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A830FE79DBF62FD63CC4 /* ThemeMode.swift */; }; ABC9A5BBFC1960B1DD8F62B7 /* SendBinanceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A3F41BDCD5F4146E6E06 /* SendBinanceService.swift */; }; ABC9A5C2E2976341520D2F6D /* WalletConnectListModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AC09A586D88BAB3B9C67 /* WalletConnectListModule.swift */; }; ABC9A5CB5C5D56F50FE5F64C /* SendTimeLockErrorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9ADF114FCFABEA148AF04 /* SendTimeLockErrorService.swift */; }; @@ -2083,7 +2084,7 @@ ABC9A63EC83A82A76E67778B /* SendNftModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A82A1E9AE6CC0E24756B /* SendNftModule.swift */; }; ABC9A66E5775762856F8927D /* NftAssetOverviewModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A90781302D793E0773CB /* NftAssetOverviewModule.swift */; }; ABC9A67A87DFB11102AB607A /* SendBitcoinFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A3DC5DA5B7BFDBF72B5D /* SendBitcoinFactory.swift */; }; - ABC9A6887B716464A5813EE9 /* WalletBackupCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AAEA86EF9D14503A4791 /* WalletBackupCrypto.swift */; }; + ABC9A6887B716464A5813EE9 /* BackupCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AAEA86EF9D14503A4791 /* BackupCrypto.swift */; }; ABC9A69264C2086E4B3B09D2 /* WalletTokenBalanceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A352F3EAA38107897CEF /* WalletTokenBalanceService.swift */; }; ABC9A69A1A01DBD07CAAC9CD /* ContactBookAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A55B0E99C1DD25839EDB /* ContactBookAddressViewController.swift */; }; ABC9A69BADD39C6E9239A2A1 /* SendViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AAF2ADD900F32D87C7BE /* SendViewModel.swift */; }; @@ -2091,6 +2092,7 @@ ABC9A69FA41A9BC474DD1915 /* DiffLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A916C64B5EA9D96B8FDA /* DiffLabel.swift */; }; ABC9A6A484F9B3F7F1054379 /* WalletConnectMainPendingRequestService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AFF8093DEB7AFD7DBBCC /* WalletConnectMainPendingRequestService.swift */; }; ABC9A6A792282ACC8DAB62BC /* IntegerFormAmountInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB9077A6A0ABE4909B76 /* IntegerFormAmountInputView.swift */; }; + ABC9A6A9C28C95352232B062 /* ThemeMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A830FE79DBF62FD63CC4 /* ThemeMode.swift */; }; ABC9A6BC79804B3D3AAFA8F1 /* SendZcashService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AE6D2CD14194802E7976 /* SendZcashService.swift */; }; ABC9A6C0A45A33C83B632D58 /* SendFeeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9ADB77831DCB474B24C8A /* SendFeeService.swift */; }; ABC9A6C1B2F55F1FFA8910CA /* ContactBookSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A06A4A02C5E889265463 /* ContactBookSettingsViewModel.swift */; }; @@ -2122,7 +2124,7 @@ ABC9A7CBFDC0DF741E29EA44 /* Integer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AF26FDCB363793BF66E1 /* Integer.swift */; }; ABC9A7E1F93B0A85976C826D /* UniswapV3Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A253877D9FB972EFB8D7 /* UniswapV3Provider.swift */; }; ABC9A7E28714A9A19A2160D4 /* SendModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AD1F2311CC6425CF9D90 /* SendModule.swift */; }; - ABC9A7EACB2FA65355C2BA4E /* WalletBackupConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB61EA3B39D8BDB1EEDE /* WalletBackupConverter.swift */; }; + ABC9A7EACB2FA65355C2BA4E /* AppBackupProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB61EA3B39D8BDB1EEDE /* AppBackupProvider.swift */; }; ABC9A7F5ECEC3311216A407F /* SendMemoInputService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A80143F95E28346C81FE /* SendMemoInputService.swift */; }; ABC9A802418438F6BD1FC1E3 /* WalletTokenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A64A66778C137FA9642C /* WalletTokenViewController.swift */; }; ABC9A806CB34CB9A5E27A0A3 /* RestoreCloudPassphraseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AF12879C62002DFE946A /* RestoreCloudPassphraseViewController.swift */; }; @@ -2144,7 +2146,7 @@ ABC9A8916A5DFA7A33F4FF79 /* SendBinanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AF15BD67548E6D755CA0 /* SendBinanceViewController.swift */; }; ABC9A89499016C8AC8341238 /* NftCollectionCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A8CF40E995105E7F38AC /* NftCollectionCellFactory.swift */; }; ABC9A8A74C527C4E01EBB8A5 /* RestoreCloudModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A45E29D1773EF27A0074 /* RestoreCloudModule.swift */; }; - ABC9A8AE39B8925B28B97F77 /* WalletBackupConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB61EA3B39D8BDB1EEDE /* WalletBackupConverter.swift */; }; + ABC9A8AE39B8925B28B97F77 /* AppBackupProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB61EA3B39D8BDB1EEDE /* AppBackupProvider.swift */; }; ABC9A8CBDB7CF4E781896C49 /* RestoreTypeModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AAC741F9A54293CD21B1 /* RestoreTypeModule.swift */; }; ABC9A8D215CC5D6A70736E84 /* SendBaseService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A48552CF0C90E22686A9 /* SendBaseService.swift */; }; ABC9A8D8709EC2B40D74A97A /* SwapRevokeConfirmationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A1360FE305343B1049CF /* SwapRevokeConfirmationViewController.swift */; }; @@ -2162,7 +2164,7 @@ ABC9A96132AD85DD613EC773 /* ProFeaturesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB785128005F6C2C9F9A /* ProFeaturesStorage.swift */; }; ABC9A994D6AC5771ED49EFD1 /* DonateAddressViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A72B62F6152709348A6D /* DonateAddressViewModel.swift */; }; ABC9A99724D817AF0E6C5EC3 /* FileStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB0A37663BC3F17C7A81 /* FileStorage.swift */; }; - ABC9A99861B1F83A19EA370D /* AppearanceBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AA7FC181E0E0FB74BEF5 /* AppearanceBackup.swift */; }; + ABC9A99861B1F83A19EA370D /* SettingsBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AA7FC181E0E0FB74BEF5 /* SettingsBackup.swift */; }; ABC9A998ECDE5438D94FBAE7 /* MarketDiscoveryCategoryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9ADFD9DA59BD2FB21C51B /* MarketDiscoveryCategoryService.swift */; }; ABC9A9A9FE5A83A6F0C3BFE9 /* SendEip721ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A7C3BC5FC664BBF14C4F /* SendEip721ViewModel.swift */; }; ABC9A9AC7890BE4AAE7DDC84 /* WalletConnectSessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AC0B5943DF3B61B20BF6 /* WalletConnectSessionManager.swift */; }; @@ -2175,7 +2177,7 @@ ABC9AA27A42D7E2A72B4A932 /* RestoreTypeModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AAC741F9A54293CD21B1 /* RestoreTypeModule.swift */; }; ABC9AA27A709AC5F85176A53 /* WalletConnectModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A0F966294A4E629CCB65 /* WalletConnectModule.swift */; }; ABC9AA309248821942E78740 /* MarketCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A2B7FBA735A76083990C /* MarketCardCell.swift */; }; - ABC9AA39ED35D6EF41A5353D /* AppearanceBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AA7FC181E0E0FB74BEF5 /* AppearanceBackup.swift */; }; + ABC9AA39ED35D6EF41A5353D /* SettingsBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AA7FC181E0E0FB74BEF5 /* SettingsBackup.swift */; }; ABC9AA462C94586CD8233295 /* WalletConnectAppShowModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A9E2C039C005650491D2 /* WalletConnectAppShowModule.swift */; }; ABC9AA4B0A6C33CAD5F3B050 /* ChartIndicatorsModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB3EC7A1FB0D6C9F7F89 /* ChartIndicatorsModule.swift */; }; ABC9AA78419B8BFEC23E8E02 /* TokenSelectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A791A47F4F1E71B51B3B /* TokenSelectView.swift */; }; @@ -2297,7 +2299,7 @@ ABC9AE9A8BECB2CE0EEF8271 /* DonateAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A525C1E9A53F37EC3918 /* DonateAddressViewController.swift */; }; ABC9AEA4EF88D31D00781014 /* ContactLabelService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB89F64056FFB98928E7 /* ContactLabelService.swift */; }; ABC9AEA5C042362B5B5BE81C /* WalletConnectMainModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AE8A193F58021C411311 /* WalletConnectMainModule.swift */; }; - ABC9AEA715281555878BF2A9 /* WalletBackupCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AAEA86EF9D14503A4791 /* WalletBackupCrypto.swift */; }; + ABC9AEA715281555878BF2A9 /* BackupCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AAEA86EF9D14503A4791 /* BackupCrypto.swift */; }; ABC9AEAA851D9BB91E8338D1 /* SwapInputAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A56611CF5E7B3F25CD5C /* SwapInputAccessoryView.swift */; }; ABC9AEB71EB75575A97408BC /* DonateDescriptionDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9ABFE62D22F9FB0B3409A /* DonateDescriptionDataSource.swift */; }; ABC9AEC9C350F3CD059C9716 /* SendMemoInputService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A80143F95E28346C81FE /* SendMemoInputService.swift */; }; @@ -3654,7 +3656,7 @@ 6BCD53102A161F4800993F20 /* BackupViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackupViewModel.swift; sourceTree = ""; }; 6BCD53112A161F4800993F20 /* BackupViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackupViewController.swift; sourceTree = ""; }; 6BCD53182A161F9100993F20 /* BackupService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackupService.swift; sourceTree = ""; }; - 6BCD531B2A16203F00993F20 /* CloudAccountBackupManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudAccountBackupManager.swift; sourceTree = ""; }; + 6BCD531B2A16203F00993F20 /* CloudBackupManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudBackupManager.swift; sourceTree = ""; }; ABC9A021D71EDD24DFB6BA62 /* CoinProChartModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoinProChartModule.swift; sourceTree = ""; }; ABC9A03401172C4C65D66764 /* SingleLineFormTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleLineFormTextView.swift; sourceTree = ""; }; ABC9A044BFF4E76CD17835CA /* IndicatorAdviceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndicatorAdviceView.swift; sourceTree = ""; }; @@ -3754,6 +3756,7 @@ 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 = ""; }; 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 = ""; }; ABC9A86EA911DA12C7A6AC20 /* WalletTokenBalanceViewItemFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletTokenBalanceViewItemFactory.swift; sourceTree = ""; }; ABC9A88E126AB21F856522A7 /* IntegerAmountInputView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntegerAmountInputView.swift; sourceTree = ""; }; @@ -3788,7 +3791,7 @@ ABC9AA751C8B09F90F716231 /* RestoreCloudViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoreCloudViewController.swift; sourceTree = ""; }; ABC9AA77C414AC06C41F9319 /* SessionRequestFilterManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionRequestFilterManager.swift; sourceTree = ""; }; ABC9AA7F2ECF212EF8B70470 /* SendConfirmationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendConfirmationViewController.swift; sourceTree = ""; }; - ABC9AA7FC181E0E0FB74BEF5 /* AppearanceBackup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppearanceBackup.swift; sourceTree = ""; }; + ABC9AA7FC181E0E0FB74BEF5 /* SettingsBackup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsBackup.swift; sourceTree = ""; }; ABC9AA8F31619609907AD67E /* MacdIndicatorDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MacdIndicatorDataSource.swift; sourceTree = ""; }; ABC9AA99463E646706E8E36D /* RestoreCloudPassphraseViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoreCloudPassphraseViewModel.swift; sourceTree = ""; }; ABC9AAB6BA03FFE92F247FF6 /* ProChartFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProChartFetcher.swift; sourceTree = ""; }; @@ -3796,14 +3799,14 @@ ABC9AACC40370E1E0CFC7639 /* IndicatorAdviceCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndicatorAdviceCell.swift; sourceTree = ""; }; ABC9AAD55B8932EE75E3C037 /* SwapInputModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwapInputModule.swift; sourceTree = ""; }; ABC9AAD79FD756DA69A52578 /* WalletConnectPendingRequestsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletConnectPendingRequestsViewController.swift; sourceTree = ""; }; - ABC9AAEA86EF9D14503A4791 /* WalletBackupCrypto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletBackupCrypto.swift; sourceTree = ""; }; + ABC9AAEA86EF9D14503A4791 /* BackupCrypto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackupCrypto.swift; sourceTree = ""; }; ABC9AAF2ADD900F32D87C7BE /* SendViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendViewModel.swift; sourceTree = ""; }; ABC9AB0A37663BC3F17C7A81 /* FileStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileStorage.swift; sourceTree = ""; }; ABC9AB2DC4C4412EFE6BEFF7 /* WalletTokenBalanceCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletTokenBalanceCell.swift; sourceTree = ""; }; ABC9AB2ED4E48D4FCEDBE769 /* ContactBookContactModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactBookContactModule.swift; sourceTree = ""; }; ABC9AB3EC7A1FB0D6C9F7F89 /* ChartIndicatorsModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartIndicatorsModule.swift; sourceTree = ""; }; ABC9AB612DE3C8AA3A1EEAC7 /* SendEip1155AvailableBalanceViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendEip1155AvailableBalanceViewModel.swift; sourceTree = ""; }; - ABC9AB61EA3B39D8BDB1EEDE /* WalletBackupConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletBackupConverter.swift; sourceTree = ""; }; + ABC9AB61EA3B39D8BDB1EEDE /* AppBackupProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppBackupProvider.swift; sourceTree = ""; }; ABC9AB69D8053840476C26FA /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; ABC9AB785128005F6C2C9F9A /* ProFeaturesStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProFeaturesStorage.swift; sourceTree = ""; }; ABC9AB8907B0E779CA4DF8F1 /* NftAssetOverviewViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NftAssetOverviewViewModel.swift; sourceTree = ""; }; @@ -4423,6 +4426,7 @@ ABC9A8CE84FA36438BE4D6B5 /* FileManager.swift */, 11B3593FBD158050C9FEF6B9 /* Misc.swift */, ABC9A9B35C58F6525F3B2D5C /* FullCoin.swift */, + ABC9A830FE79DBF62FD63CC4 /* ThemeMode.swift */, ); path = Extensions; sourceTree = ""; @@ -4582,7 +4586,7 @@ 1A5641CDB00EF52E18BF70F3 /* AppVersionManager.swift */, 11B35FA360A91FDE3EB0B85C /* RateAppManager.swift */, 1A5646B6231F2C52F27526F7 /* BtcBlockchainManager.swift */, - 6BCD531B2A16203F00993F20 /* CloudAccountBackupManager.swift */, + 6BCD531B2A16203F00993F20 /* CloudBackupManager.swift */, 11B35D9767615D8FBF7A314F /* GuidesManager.swift */, 11B35EB9BA551F2F1AF7739D /* TermsManager.swift */, 2FA5D690E78A4568F9FD9554 /* LogRecordManager.swift */, @@ -6653,7 +6657,7 @@ 6BCD52F82A161F4100993F20 /* Terms */, 6BCD52FC2A161F4100993F20 /* Name */, ABC9A3CED3BD03C1DBF797E2 /* Passphrase */, - ABC9AB61EA3B39D8BDB1EEDE /* WalletBackupConverter.swift */, + ABC9AB61EA3B39D8BDB1EEDE /* AppBackupProvider.swift */, ABC9AB0A37663BC3F17C7A81 /* FileStorage.swift */, ); path = ICloud; @@ -7002,11 +7006,11 @@ children = ( ABC9AD5CB1911A698718213F /* BackupCryptoHelper.swift */, ABC9A6663522498A53CF4174 /* KdfParams.swift */, - ABC9AAEA86EF9D14503A4791 /* WalletBackupCrypto.swift */, + ABC9AAEA86EF9D14503A4791 /* BackupCrypto.swift */, ABC9A89726499CDB4F697EDD /* CipherParams.swift */, ABC9AECEEB35D57CB0965E79 /* WalletBackup.swift */, ABC9A41F6AA0B65FDA91EB68 /* FullBackup.swift */, - ABC9AA7FC181E0E0FB74BEF5 /* AppearanceBackup.swift */, + ABC9AA7FC181E0E0FB74BEF5 /* SettingsBackup.swift */, ); path = Crypto; sourceTree = ""; @@ -8069,7 +8073,7 @@ 11B356F4F8D8486B00A2AA47 /* MainBadgeService.swift in Sources */, 11B3576791792D356B0BE916 /* MainViewModel.swift in Sources */, 11B35EFAFFA1E30F7765FEB2 /* MainService.swift in Sources */, - 6BCD531D2A16203F00993F20 /* CloudAccountBackupManager.swift in Sources */, + 6BCD531D2A16203F00993F20 /* CloudBackupManager.swift in Sources */, 58AAAF4236075971CC88F7ED /* SwapApproveService.swift in Sources */, 58AAAE666BAD91283206BA1C /* SwapApproveViewModel.swift in Sources */, 58AAA0D14CD9EDAE2DBF7540 /* SwapApproveViewController.swift in Sources */, @@ -8946,10 +8950,10 @@ ABC9ABE3189E497EC732B331 /* BackupCloudPassphraseViewModel.swift in Sources */, ABC9A2A249A94B271F56EBD0 /* BackupCryptoHelper.swift in Sources */, ABC9A543EB59D153FAD103F6 /* KdfParams.swift in Sources */, - ABC9AEA715281555878BF2A9 /* WalletBackupCrypto.swift in Sources */, + ABC9AEA715281555878BF2A9 /* BackupCrypto.swift in Sources */, ABC9ACDD29B7F82884A5AE39 /* CipherParams.swift in Sources */, ABC9AA80C5197F9CC6221FC8 /* WalletBackup.swift in Sources */, - ABC9A8AE39B8925B28B97F77 /* WalletBackupConverter.swift in Sources */, + ABC9A8AE39B8925B28B97F77 /* AppBackupProvider.swift in Sources */, ABC9A99724D817AF0E6C5EC3 /* FileStorage.swift in Sources */, ABC9A3BC9A18F74818EF5C17 /* MetadataMonitor.swift in Sources */, ABC9A8CBDB7CF4E781896C49 /* RestoreTypeModule.swift in Sources */, @@ -9117,8 +9121,9 @@ 11B35FFD159D864F6D914F08 /* AppearanceView.swift in Sources */, 11B350CA618DD7BBA452FC33 /* AppearanceViewModel.swift in Sources */, ABC9A13D78DD5F176A170B65 /* FullBackup.swift in Sources */, - ABC9AA39ED35D6EF41A5353D /* AppearanceBackup.swift in Sources */, + ABC9AA39ED35D6EF41A5353D /* SettingsBackup.swift in Sources */, ABC9AE1E60CABA0101D62738 /* FullCoin.swift in Sources */, + ABC9A6A9C28C95352232B062 /* ThemeMode.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9349,7 +9354,7 @@ 11B35A108457DC44DD870138 /* MainBadgeService.swift in Sources */, 11B35EF9D9E8C1A814005CFD /* MainViewModel.swift in Sources */, 11B35CE67B7F5C5A5244C951 /* MainService.swift in Sources */, - 6BCD531C2A16203F00993F20 /* CloudAccountBackupManager.swift in Sources */, + 6BCD531C2A16203F00993F20 /* CloudBackupManager.swift in Sources */, 58AAA996A8547DBE1BF378CE /* SwapApproveService.swift in Sources */, 58AAA4915E1B70248A8DC620 /* SwapApproveViewModel.swift in Sources */, 58AAA7B99324DDA9C53692AD /* SwapApproveViewController.swift in Sources */, @@ -10224,10 +10229,10 @@ ABC9A191F1E62A20D2D38262 /* BackupCloudPassphraseViewModel.swift in Sources */, ABC9AB83EE3F909BD80E0539 /* BackupCryptoHelper.swift in Sources */, ABC9A60BA5DF119C7FC8A859 /* KdfParams.swift in Sources */, - ABC9A6887B716464A5813EE9 /* WalletBackupCrypto.swift in Sources */, + ABC9A6887B716464A5813EE9 /* BackupCrypto.swift in Sources */, ABC9AB6EB596E2F8B15D00E4 /* CipherParams.swift in Sources */, ABC9AF9C828BEBB740468204 /* WalletBackup.swift in Sources */, - ABC9A7EACB2FA65355C2BA4E /* WalletBackupConverter.swift in Sources */, + ABC9A7EACB2FA65355C2BA4E /* AppBackupProvider.swift in Sources */, ABC9AF5B0B1D5FE002288AE1 /* FileStorage.swift in Sources */, ABC9AF371FBB4BEA654A78B6 /* MetadataMonitor.swift in Sources */, ABC9AA27A42D7E2A72B4A932 /* RestoreTypeModule.swift in Sources */, @@ -10396,8 +10401,9 @@ 11B35245CD0D5B0E44E413F4 /* AppearanceView.swift in Sources */, 11B35A18AA61F8C06AB1C15B /* AppearanceViewModel.swift in Sources */, ABC9A2C4301447E0EEA1D16F /* FullBackup.swift in Sources */, - ABC9A99861B1F83A19EA370D /* AppearanceBackup.swift in Sources */, + ABC9A99861B1F83A19EA370D /* SettingsBackup.swift in Sources */, ABC9A3EA19771B14B0502A0A /* FullCoin.swift in Sources */, + ABC9A5A4C6213D58CDA2EB73 /* ThemeMode.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/UnstoppableWallet/UnstoppableWallet/Core/App.swift b/UnstoppableWallet/UnstoppableWallet/Core/App.swift index bb6e55aaae..b519b4335a 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/App.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/App.swift @@ -6,6 +6,7 @@ import MarketKit import PinKit import StorageKit import ThemeKit +import LanguageKit class App { static var instance: App? @@ -105,7 +106,8 @@ class App { let appManager: AppManager let contactManager: ContactBookManager - let cloudAccountBackupManager: CloudAccountBackupManager + let appBackupProvider: AppBackupProvider + let cloudBackupManager: CloudBackupManager let appEventHandler = EventHandler() @@ -191,8 +193,6 @@ class App { restoreSettingsManager = RestoreSettingsManager(storage: restoreSettingsStorage) predefinedBlockchainService = PredefinedBlockchainService(restoreSettingsManager: restoreSettingsManager) - cloudAccountBackupManager = CloudAccountBackupManager(ubiquityContainerIdentifier: AppConfig.sharedCloudContainer, restoreSettingsManager: restoreSettingsManager, logger: logger) - let hsLabelProvider = HsLabelProvider(networkManager: networkManager) let evmLabelStorage = EvmLabelStorage(dbPool: dbPool) let syncerStateStorage = SyncerStateStorage(dbPool: dbPool) @@ -306,6 +306,30 @@ class App { let cexAssetRecordStorage = CexAssetRecordStorage(dbPool: dbPool) cexAssetManager = CexAssetManager(accountManager: accountManager, marketKit: marketKit, storage: cexAssetRecordStorage) + appBackupProvider = AppBackupProvider( + accountManager: accountManager, + accountFactory: accountFactory, + walletManager: walletManager, + favoritesManager: favoritesManager, + evmSyncSourceManager: evmSyncSourceManager, + restoreSettingsManager: restoreSettingsManager, + localStorage: localStorage, + languageManager: LanguageManager.shared, + currencyKit: currencyKit, + themeManager: themeManager, + launchScreenManager: launchScreenManager, + appIconManager: appIconManager, + balancePrimaryValueManager: balancePrimaryValueManager, + balanceConversionManager: balanceConversionManager, + balanceHiddenManager: balanceHiddenManager, + contactManager: contactManager + ) + cloudBackupManager = CloudBackupManager( + ubiquityContainerIdentifier: AppConfig.sharedCloudContainer, + appBackupProvider: appBackupProvider, + logger: logger + ) + appManager = AppManager( accountManager: accountManager, walletManager: walletManager, diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/AppearanceBackup.swift b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/AppearanceBackup.swift deleted file mode 100644 index 271721518a..0000000000 --- a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/AppearanceBackup.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation -import Chart -import CurrencyKit -import ThemeKit - -struct AppearanceBackup { - let lockTimeEnabled: Bool - let remoteContactsSync: Bool - let defaultProviders: [DefaultProvider] - let chartIndicators: [ChartIndicator] - let indicatorsShown: Bool - let currentLanguage: String - let baseCurrency: Currency - - let mode: ThemeMode - let showMarketTab: Bool - let launchScreen: LaunchScreen - let conversionTokenQueryId: String? - let balancePrimaryValue: BalancePrimaryValue - let balanceAutoHide: Bool - let appIcon: AppIcon -} - -extension AppearanceBackup { - struct DefaultProvider { - let blockchainTypeId: String - let provider: String - } -} \ No newline at end of file diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/BackupCrypto.swift b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/BackupCrypto.swift new file mode 100644 index 0000000000..e3fb6d3577 --- /dev/null +++ b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/BackupCrypto.swift @@ -0,0 +1,141 @@ +import Foundation + +class BackupCrypto: Codable { + static var defaultBackup = KdfParams(dklen: 32, n: 16384, p: 4, r: 8, salt: AppConfig.backupSalt) + + let cipher: String + let cipherParams: CipherParams + let cipherText: String + let kdf: String + let kdfParams: KdfParams + let mac: String + + enum CodingKeys: String, CodingKey { + case cipher + case cipherParams = "cipherparams" + case cipherText = "ciphertext" + case kdf + case kdfParams = "kdfparams" + case mac + } + + init(cipher: String, cipherParams: CipherParams, cipherText: String, kdf: String, kdfParams: KdfParams, mac: String) { + self.cipher = cipher + self.cipherParams = cipherParams + self.cipherText = cipherText + self.kdf = kdf + self.kdfParams = kdfParams + self.mac = mac + } + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + cipher = try container.decode(String.self, forKey: .cipher) + cipherParams = try container.decode(CipherParams.self, forKey: .cipherParams) + cipherText = try container.decode(String.self, forKey: .cipherText) + kdf = try container.decode(String.self, forKey: .kdf) + kdfParams = try container.decode(KdfParams.self, forKey: .kdfParams) + mac = try container.decode(String.self, forKey: .mac) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(cipher, forKey: .cipher) + try container.encode(cipherParams, forKey: .cipherParams) + try container.encode(cipherText, forKey: .cipherText) + try container.encode(kdf, forKey: .kdf) + try container.encode(kdfParams, forKey: .kdfParams) + try container.encode(mac, forKey: .mac) + } +} + +extension BackupCrypto { + func data(passphrase: String) throws -> Data { + try Self.validate(passphrase: passphrase) + // Validation data + guard let data = Data(base64Encoded: cipherText) else { + throw RestoreCloudModule.RestoreError.invalidBackup + } + + // validation passphrase + let isValid = (try? BackupCryptoHelper.isValid( + macHex: mac, + pass: passphrase, + message: cipherText.hs.data, + kdf: kdfParams + )) ?? false + guard isValid else { + throw RestoreCloudModule.RestoreError.invalidPassword + } + + return try BackupCryptoHelper.AES128( + operation: .decrypt, + ivHex: cipherParams.iv, + pass: passphrase, + message: data, + 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 { + static func validate(passphrase: String) throws { + // Validation passphrase + guard !passphrase.isEmpty else { + throw ValidationError.emptyPassphrase + } + guard passphrase.count >= BackupCloudModule.minimumPassphraseLength else { + throw ValidationError.simplePassword + } + + let allSatisfy = BackupCloudModule.PassphraseCharacterSet.allCases.allSatisfy { set in set.contains(passphrase) } + if !allSatisfy { + throw ValidationError.simplePassword + } + } + + static func instance(data: Data, passphrase: String, kdf: KdfParams = .defaultBackup) throws -> BackupCrypto { + let iv = BackupCryptoHelper.generateInitialVector().hs.hex + + let cipherText = try BackupCryptoHelper.AES128( + operation: .encrypt, + ivHex: iv, + pass: passphrase, + message: data, + kdf: kdf + ) + + let encodedCipherText = cipherText.base64EncodedString() + let mac = try BackupCryptoHelper.mac( + pass: passphrase, + message: encodedCipherText.hs.data, + kdf: kdf + ) + + return BackupCrypto( + cipher: BackupCryptoHelper.defaultCypher, + cipherParams: CipherParams(iv: iv), + cipherText: encodedCipherText, + kdf: BackupCryptoHelper.defaultKdf, + kdfParams: kdf, + mac: mac.hs.hex + ) + } +} + +extension BackupCrypto { + enum ValidationError: Error { + case emptyPassphrase + case simplePassword + } +} \ No newline at end of file diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/FullBackup.swift b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/FullBackup.swift index abc65b27c8..6a2fc7d82c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/FullBackup.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/FullBackup.swift @@ -1,8 +1,44 @@ import Foundation struct FullBackup { - let wallets: [WalletBackup] + let id: String + let wallets: [RestoreCloudModule.RestoredBackup] let watchlistIds: [String] - let contacts: ContactBook? - let appearance: AppearanceBackup? + let contacts: BackupCrypto? + let settings: SettingsBackup? + let version: Int + let timestamp: TimeInterval? +} + +extension FullBackup: Codable { + enum CodingKeys: String, CodingKey { + case id + case wallets + case watchlistIds = "watchlist" + case contacts + case settings + case version + case timestamp + } + +// init(from decoder: Decoder) throws { +// let container = try decoder.container(keyedBy: CodingKeys.self) +// wallets = (try? container.decode([RestoreCloudModule.RestoredBackup].self, forKey: .wallets)) ?? [] +// watchlistIds = (try? container.decode([String].self, forKey: .watchlistIds)) ?? [] +// contacts = try? container.decode([BackupContact].self, forKey: .contacts) +// evmSyncSources = try? container.decode(SyncSourceBackup.self, forKey: .evmSyncSources) +// settings = try? container.decode(SettingsBackup.self, forKey: .settings) +// } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(id, forKey: .id) + 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(version, forKey: .version) + try container.encode(version, forKey: .version) + try? container.encode(timestamp, forKey: .timestamp) + } } diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/SettingsBackup.swift b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/SettingsBackup.swift new file mode 100644 index 0000000000..b4e3643a4e --- /dev/null +++ b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/SettingsBackup.swift @@ -0,0 +1,55 @@ +import Foundation +import Chart +import CurrencyKit +import ThemeKit + +struct SettingsBackup: Codable { + let evmSyncSources: EvmSyncSourceManager.SyncSourceBackup + + let lockTimeEnabled: Bool + let remoteContactsSync: Bool + let defaultProviders: [DefaultProvider] + let chartIndicators: [ChartIndicator] + let indicatorsShown: Bool + let currentLanguage: String + let baseCurrency: String + + let mode: ThemeMode + let showMarketTab: Bool + let launchScreen: LaunchScreen + let conversionTokenQueryId: String? + let balancePrimaryValue: BalancePrimaryValue + let balanceAutoHide: Bool + let appIcon: String + + enum CodingKeys: String, CodingKey { + case evmSyncSources = "evm_sync_sources" + case lockTimeEnabled = "lock_time" + case remoteContactsSync = "contacts_sync" + case defaultProviders = "default_providers" + case chartIndicators = "indicators" + case indicatorsShown = "indicators_shown" + case currentLanguage = "language" + case baseCurrency = "currency" + case mode = "theme_mode" + case showMarketTab = "show_market" + case launchScreen = "launch_screen" + case conversionTokenQueryId = "conversion_token_query_id" + case balancePrimaryValue = "balance_primary_value" + case balanceAutoHide = "balance_auto_hide" + case appIcon = "app_icon" + } + +} + +extension SettingsBackup { + struct DefaultProvider: Codable { + enum CodingKeys: String, CodingKey { + case blockchainTypeId = "blockchain_type_id" + case provider + } + + let blockchainTypeId: String + let provider: String + } +} \ No newline at end of file diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/WalletBackup.swift b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/WalletBackup.swift index e2691ab3f7..df55844c58 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/WalletBackup.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/WalletBackup.swift @@ -1,7 +1,7 @@ import Foundation class WalletBackup: Codable { - let crypto: WalletBackupCrypto + let crypto: BackupCrypto let id: String let type: AccountType.Abstract let isManualBackedUp: Bool @@ -19,19 +19,19 @@ class WalletBackup: Codable { case timestamp } - init(crypto: WalletBackupCrypto, enabledWallets: [EnabledWallet], id: String, type: AccountType.Abstract, isManualBackedUp: Bool, version: Int, timestamp: TimeInterval) { + init(crypto: BackupCrypto, enabledWallets: [EnabledWallet], id: String, type: AccountType.Abstract, isManualBackedUp: Bool, version: Int, timestamp: TimeInterval) { self.crypto = crypto self.enabledWallets = enabledWallets self.id = id self.type = type self.isManualBackedUp = isManualBackedUp self.version = version - self.timestamp = timestamp.rounded() + self.timestamp = timestamp } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - crypto = try container.decode(WalletBackupCrypto.self, forKey: .crypto) + crypto = try container.decode(BackupCrypto.self, forKey: .crypto) enabledWallets = (try? container.decode([EnabledWallet].self, forKey: .enabledWallets)) ?? [] id = try container.decode(String.self, forKey: .id) type = try container.decode(AccountType.Abstract.self, forKey: .type) diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/WalletBackupCrypto.swift b/UnstoppableWallet/UnstoppableWallet/Core/Crypto/WalletBackupCrypto.swift deleted file mode 100644 index e992aade68..0000000000 --- a/UnstoppableWallet/UnstoppableWallet/Core/Crypto/WalletBackupCrypto.swift +++ /dev/null @@ -1,51 +0,0 @@ -import Foundation - -class WalletBackupCrypto: Codable { - static var defaultBackup = KdfParams(dklen: 32, n: 16384, p: 4, r: 8, salt: AppConfig.backupSalt) - - let cipher: String - let cipherParams: CipherParams - let cipherText: String - let kdf: String - let kdfParams: KdfParams - let mac: String - - enum CodingKeys: String, CodingKey { - case cipher - case cipherParams = "cipherparams" - case cipherText = "ciphertext" - case kdf - case kdfParams = "kdfparams" - case mac - } - - init(cipher: String, cipherParams: CipherParams, cipherText: String, kdf: String, kdfParams: KdfParams, mac: String) { - self.cipher = cipher - self.cipherParams = cipherParams - self.cipherText = cipherText - self.kdf = kdf - self.kdfParams = kdfParams - self.mac = mac - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - cipher = try container.decode(String.self, forKey: .cipher) - cipherParams = try container.decode(CipherParams.self, forKey: .cipherParams) - cipherText = try container.decode(String.self, forKey: .cipherText) - kdf = try container.decode(String.self, forKey: .kdf) - kdfParams = try container.decode(KdfParams.self, forKey: .kdfParams) - mac = try container.decode(String.self, forKey: .mac) - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(cipher, forKey: .cipher) - try container.encode(cipherParams, forKey: .cipherParams) - try container.encode(cipherText, forKey: .cipherText) - try container.encode(kdf, forKey: .kdf) - try container.encode(kdfParams, forKey: .kdfParams) - try container.encode(mac, forKey: .mac) - } - -} diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Managers/BalanceConversionManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Managers/BalanceConversionManager.swift index 48d366230f..c8054b88d7 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Managers/BalanceConversionManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Managers/BalanceConversionManager.swift @@ -66,4 +66,11 @@ extension BalanceConversionManager { func set(conversionToken: Token?) { self.conversionToken = conversionToken } + + func set(tokenQueryId: String?) { + conversionToken = tokenQueryId + .flatMap { TokenQuery(id: $0) } + .flatMap { try? marketKit.token(query: $0) } ?? + conversionTokens.first + } } diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Managers/CloudAccountBackupManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Managers/CloudBackupManager.swift similarity index 67% rename from UnstoppableWallet/UnstoppableWallet/Core/Managers/CloudAccountBackupManager.swift rename to UnstoppableWallet/UnstoppableWallet/Core/Managers/CloudBackupManager.swift index e8000f493b..7f26b1c417 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Managers/CloudAccountBackupManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Managers/CloudBackupManager.swift @@ -3,13 +3,13 @@ import Foundation import HsExtensions import HsToolKit -class CloudAccountBackupManager { +class CloudBackupManager { private static let batchingInterval: TimeInterval = 1 private static let fileExtension = ".json" private let ubiquityContainerIdentifier: String? private let fileStorage: FileStorage - private let restoreSettingsManager: RestoreSettingsManager + private let appBackupProvider: AppBackupProvider private let logger: Logger? private var metadataMonitor: MetadataMonitor? @@ -22,12 +22,13 @@ class CloudAccountBackupManager { .appendingPathComponent("Documents") } - @PostPublished private(set) var items = [String: WalletBackup]() + @PostPublished private(set) var oneWalletItems = [String: WalletBackup]() + @PostPublished private(set) var fullBackupItems = [String: FullBackup]() @PostPublished private(set) var state = State.loading - init(ubiquityContainerIdentifier: String?, restoreSettingsManager: RestoreSettingsManager, logger: Logger?) { + init(ubiquityContainerIdentifier: String?, appBackupProvider: AppBackupProvider, logger: Logger?) { self.ubiquityContainerIdentifier = ubiquityContainerIdentifier - self.restoreSettingsManager = restoreSettingsManager + self.appBackupProvider = appBackupProvider fileStorage = FileStorage(logger: logger) self.logger = logger @@ -70,11 +71,14 @@ class CloudAccountBackupManager { do { forceDownloadContainerFiles(url: url) - let items = try Self.downloadItems(url: url, fileStorage: fileStorage, logger: logger) + let oneWalletItems: [String: WalletBackup] = try Self.downloadItems(url: url, fileStorage: fileStorage, logger: logger) + let fullBackupItems: [String: FullBackup] = try Self.downloadItems(url: url, fileStorage: fileStorage, logger: logger) state = .success logger?.log(level: .debug, message: "CloudAccountManager.state = \(state)") - self.items = items + + self.oneWalletItems = oneWalletItems + self.fullBackupItems = fullBackupItems } catch { state = .error(error) logger?.log(level: .debug, message: "CloudAccountManager.state = \(state)") @@ -98,14 +102,14 @@ class CloudAccountBackupManager { } } - private static func downloadItems(url: URL, fileStorage: FileStorage, logger: Logger? = nil) throws -> [String: WalletBackup] { + private static func downloadItems(url: URL, fileStorage: FileStorage, logger: Logger? = nil) throws -> [String: T] { let files = try fileStorage.fileList(url: url).filter { s in s.contains(Self.fileExtension) } - var items = [String: WalletBackup]() + var items = [String: T]() for file in files { do { let data = try fileStorage.read(directoryUrl: url, filename: file) - let backup = try JSONDecoder().decode(WalletBackup.self, from: data) + let backup = try JSONDecoder().decode(T.self, from: data) items[file] = backup } catch { logger?.log(level: .debug, message: "CloudAccountManager.downloadItems, can't read \(file). Because: \(error)") @@ -115,45 +119,66 @@ class CloudAccountBackupManager { logger?.log(level: .debug, message: "CloudAccountManager.downloadItems, read \(items.count) files") return items } + + private func save(encoded: Data, name: String) throws { + guard let iCloudUrl else { + throw BackupError.urlNotAvailable + } + + do { + let name = name + Self.fileExtension + + try fileStorage.write(directoryUrl: iCloudUrl, filename: name, data: encoded) + logger?.log(level: .debug, message: "CloudAccountManager.downloadItems, save \(name)") + } catch { + logger?.log(level: .debug, message: "CloudAccountManager.downloadItems, can't save \(name). Because: \(error)") + throw error + } + } } -extension CloudAccountBackupManager { +extension CloudBackupManager { func backedUp(uniqueId: Data) -> Bool { - items.contains { _, backup in backup.id == uniqueId.hs.hex } + oneWalletItems.contains { _, backup in backup.id == uniqueId.hs.hex } } var existFilenames: [String] { - items.map { ($0.key as NSString).deletingPathExtension } + oneWalletItems.map { ($0.key as NSString).deletingPathExtension } + + fullBackupItems.map { ($0.key as NSString).deletingPathExtension } } } -extension CloudAccountBackupManager { +extension CloudBackupManager { var isAvailable: Bool { iCloudUrl != nil } - func save(account: Account, wallets: [Wallet], isManualBackedUp: Bool, passphrase: String, name: String) throws { - guard let iCloudUrl else { - throw BackupError.urlNotAvailable - } + func save(account: Account, passphrase: String, name: String) throws { + let backup = try appBackupProvider.walletBackup( + account: account, + passphrase: passphrase + ) do { - let name = name + Self.fileExtension - let encoded = try WalletBackupConverter.encode( - accountType: account.type, - wallets: wallets.map { - let settings = restoreSettingsManager - .settings(accountId: account.id, blockchainType: $0.token.blockchainType) - .reduce(into: [:], { $0[$1.0.rawValue] = $1.1 }) - - return WalletBackup.EnabledWallet($0, settings: settings) - }, - isManualBackedUp: isManualBackedUp, + let encoded = try JSONEncoder().encode(backup) + try save(encoded: encoded, name: name) + } catch { + logger?.log(level: .debug, message: "CloudAccountManager.downloadItems, can't save \(name). Because: \(error)") + throw error + } + } + + func save(fields: [AppBackupProvider.Field], passphrase: String, name: String) throws { + let backup = try appBackupProvider.fullBackup( + fields: fields, passphrase: passphrase - ) + ) - try fileStorage.write(directoryUrl: iCloudUrl, filename: name, data: encoded) - logger?.log(level: .debug, message: "CloudAccountManager.downloadItems, save \(name)") + do { + let encoder = JSONEncoder() + let data = try encoder.encode(backup) + let encoded = try JSONEncoder().encode(backup) + try save(encoded: encoded, name: name) } catch { logger?.log(level: .debug, message: "CloudAccountManager.downloadItems, can't save \(name). Because: \(error)") throw error @@ -170,7 +195,7 @@ extension CloudAccountBackupManager { throw BackupError.urlNotAvailable } - guard let item = items.first(where: { _, backup in backup.id == uniqueId }) else { + guard let item = oneWalletItems.first(where: { _, backup in backup.id == uniqueId }) else { throw BackupError.itemNotFound } @@ -179,8 +204,7 @@ extension CloudAccountBackupManager { try fileStorage.deleteFile(url: fileUrl) // system will automatically updates items but after 1-2 seconds. So we need force update - items[item.key] = nil - + oneWalletItems[item.key] = nil logger?.log(level: .debug, message: "CloudAccountManager.delete \(item.key) successful") } catch { logger?.log(level: .debug, message: "CloudAccountManager.delete \(item.key) unsuccessful because: \(error)") @@ -189,7 +213,7 @@ extension CloudAccountBackupManager { } } -extension CloudAccountBackupManager { +extension CloudBackupManager { enum BackupError: Error { case urlNotAvailable case itemNotFound diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmSyncSourceManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmSyncSourceManager.swift index b5194abbfe..e4561c32a1 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmSyncSourceManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Managers/EvmSyncSourceManager.swift @@ -1,8 +1,8 @@ -import Foundation -import RxSwift -import RxRelay import EvmKit +import Foundation import MarketKit +import RxRelay +import RxSwift class EvmSyncSourceManager { private let testNetManager: TestNetManager @@ -31,11 +31,9 @@ class EvmSyncSourceManager { default: fatalError("Non-supported EVM blockchain") } } - } extension EvmSyncSourceManager { - var syncSourceObservable: Observable { syncSourceRelay.asObservable() } @@ -50,141 +48,141 @@ extension EvmSyncSourceManager { if testNetManager.testNetEnabled { return [ EvmSyncSource( - name: "Infura Sepolia", - rpcSource: .http(urls: [URL(string: "https://sepolia.infura.io/v3/\(AppConfig.infuraCredentials.id)")!], auth: AppConfig.infuraCredentials.secret), - transactionSource: EvmKit.TransactionSource( - name: "sepolia.etherscan.io", - type: .etherscan(apiBaseUrl: "https://api-sepolia.etherscan.io", txBaseUrl: "https://sepiloa.etherscan.io", apiKey: AppConfig.etherscanKey) - ) - ) + name: "Infura Sepolia", + rpcSource: .http(urls: [URL(string: "https://sepolia.infura.io/v3/\(AppConfig.infuraCredentials.id)")!], auth: AppConfig.infuraCredentials.secret), + transactionSource: EvmKit.TransactionSource( + name: "sepolia.etherscan.io", + type: .etherscan(apiBaseUrl: "https://api-sepolia.etherscan.io", txBaseUrl: "https://sepiloa.etherscan.io", apiKey: AppConfig.etherscanKey) + ) + ), ] } else { return [ EvmSyncSource( - name: "Infura", - rpcSource: .ethereumInfuraWebsocket(projectId: AppConfig.infuraCredentials.id, projectSecret: AppConfig.infuraCredentials.secret), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: "Infura", + rpcSource: .ethereumInfuraWebsocket(projectId: AppConfig.infuraCredentials.id, projectSecret: AppConfig.infuraCredentials.secret), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ), EvmSyncSource( - name: "Infura", - rpcSource: .ethereumInfuraHttp(projectId: AppConfig.infuraCredentials.id, projectSecret: AppConfig.infuraCredentials.secret), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: "Infura", + rpcSource: .ethereumInfuraHttp(projectId: AppConfig.infuraCredentials.id, projectSecret: AppConfig.infuraCredentials.secret), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ), EvmSyncSource( - name: "LlamaNodes", - rpcSource: .http(urls: [URL(string: "https://eth.llamarpc.com")!], auth: nil), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) - ) + name: "LlamaNodes", + rpcSource: .http(urls: [URL(string: "https://eth.llamarpc.com")!], auth: nil), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) + ), ] } case .binanceSmartChain: if testNetManager.testNetEnabled { return [ EvmSyncSource( - name: "Binance TestNet", - rpcSource: .http(urls: [URL(string: "https://data-seed-prebsc-1-s1.binance.org:8545")!], auth: nil), - transactionSource: EvmKit.TransactionSource( - name: "testnet.bscscan.com", - type: .etherscan(apiBaseUrl: "https://api-testnet.bscscan.com", txBaseUrl: "https://testnet.bscscan.com", apiKey: AppConfig.bscscanKey) - ) - ) + name: "Binance TestNet", + rpcSource: .http(urls: [URL(string: "https://data-seed-prebsc-1-s1.binance.org:8545")!], auth: nil), + transactionSource: EvmKit.TransactionSource( + name: "testnet.bscscan.com", + type: .etherscan(apiBaseUrl: "https://api-testnet.bscscan.com", txBaseUrl: "https://testnet.bscscan.com", apiKey: AppConfig.bscscanKey) + ) + ), ] } else { return [ EvmSyncSource( - name: "Binance", - rpcSource: .binanceSmartChainHttp(), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: "Binance", + rpcSource: .binanceSmartChainHttp(), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ), EvmSyncSource( - name: "BSC RPC", - rpcSource: .bscRpcHttp(), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: "BSC RPC", + rpcSource: .bscRpcHttp(), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ), EvmSyncSource( - name: "Omnia", - rpcSource: .http(urls: [URL(string: "https://endpoints.omniatech.io/v1/bsc/mainnet/public")!], auth: nil), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) - ) + name: "Omnia", + rpcSource: .http(urls: [URL(string: "https://endpoints.omniatech.io/v1/bsc/mainnet/public")!], auth: nil), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) + ), ] } case .polygon: return [ EvmSyncSource( - name: "Polygon RPC", - rpcSource: .polygonRpcHttp(), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: "Polygon RPC", + rpcSource: .polygonRpcHttp(), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ), EvmSyncSource( - name: "LlamaNodes", - rpcSource: .http(urls: [URL(string: "https://polygon.llamarpc.com")!], auth: nil), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) - ) + name: "LlamaNodes", + rpcSource: .http(urls: [URL(string: "https://polygon.llamarpc.com")!], auth: nil), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) + ), ] case .avalanche: return [ EvmSyncSource( - name: "Avax Network", - rpcSource: .avaxNetworkHttp(), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: "Avax Network", + rpcSource: .avaxNetworkHttp(), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ), EvmSyncSource( - name: "PublicNode", - rpcSource: .http(urls: [URL(string: "https://avalanche-evm.publicnode.com")!], auth: nil), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) - ) + name: "PublicNode", + rpcSource: .http(urls: [URL(string: "https://avalanche-evm.publicnode.com")!], auth: nil), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) + ), ] case .optimism: return [ EvmSyncSource( - name: "Optimism", - rpcSource: .optimismRpcHttp(), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: "Optimism", + rpcSource: .optimismRpcHttp(), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ), EvmSyncSource( - name: "Omnia", - rpcSource: .http(urls: [URL(string: "https://endpoints.omniatech.io/v1/op/mainnet/public")!], auth: nil), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) - ) + name: "Omnia", + rpcSource: .http(urls: [URL(string: "https://endpoints.omniatech.io/v1/op/mainnet/public")!], auth: nil), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) + ), ] case .arbitrumOne: return [ EvmSyncSource( - name: "Arbitrum", - rpcSource: .arbitrumOneRpcHttp(), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: "Arbitrum", + rpcSource: .arbitrumOneRpcHttp(), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ), EvmSyncSource( - name: "Omnia", - rpcSource: .http(urls: [URL(string: "https://endpoints.omniatech.io/v1/arbitrum/one/public")!], auth: nil), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) - ) + name: "Omnia", + rpcSource: .http(urls: [URL(string: "https://endpoints.omniatech.io/v1/arbitrum/one/public")!], auth: nil), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) + ), ] case .gnosis: return [ EvmSyncSource( - name: "Gnosis Chain", - rpcSource: .gnosisRpcHttp(), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: "Gnosis Chain", + rpcSource: .gnosisRpcHttp(), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ), EvmSyncSource( - name: "Ankr", - rpcSource: .http(urls: [URL(string: "https://rpc.ankr.com/gnosis")!], auth: nil), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) - ) + name: "Ankr", + rpcSource: .http(urls: [URL(string: "https://rpc.ankr.com/gnosis")!], auth: nil), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) + ), ] case .fantom: return [ EvmSyncSource( - name: "Fantom Chain", - rpcSource: .fantomRpcHttp(), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: "Fantom Chain", + rpcSource: .fantomRpcHttp(), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ), EvmSyncSource( - name: "Ankr", - rpcSource: .http(urls: [URL(string: "https://rpc.ankr.com/fantom")!], auth: nil), - transactionSource: defaultTransactionSource(blockchainType: blockchainType) - ) + name: "Ankr", + rpcSource: .http(urls: [URL(string: "https://rpc.ankr.com/fantom")!], auth: nil), + transactionSource: defaultTransactionSource(blockchainType: blockchainType) + ), ] default: return [] @@ -209,9 +207,9 @@ extension EvmSyncSourceManager { } return EvmSyncSource( - name: url.host ?? "", - rpcSource: rpcSource, - transactionSource: defaultTransactionSource(blockchainType: blockchainType) + name: url.host ?? "", + rpcSource: rpcSource, + transactionSource: defaultTransactionSource(blockchainType: blockchainType) ) } } catch { @@ -227,7 +225,8 @@ extension EvmSyncSourceManager { let syncSources = allSyncSources(blockchainType: blockchainType) if let urlString = blockchainSettingsStorage.evmSyncSourceUrl(blockchainType: blockchainType), - let syncSource = syncSources.first(where: { $0.rpcSource.url.absoluteString == urlString }) { + let syncSource = syncSources.first(where: { $0.rpcSource.url.absoluteString == urlString }) + { return syncSource } @@ -238,7 +237,8 @@ extension EvmSyncSourceManager { let syncSources = allSyncSources(blockchainType: blockchainType) if let urlString = blockchainSettingsStorage.evmSyncSourceUrl(blockchainType: blockchainType), - let syncSource = syncSources.first(where: { $0.rpcSource.url.absoluteString == urlString }), syncSource.isHttp { + let syncSource = syncSources.first(where: { $0.rpcSource.url.absoluteString == urlString }), syncSource.isHttp + { return syncSource } @@ -252,9 +252,9 @@ extension EvmSyncSourceManager { func saveSyncSource(blockchainType: BlockchainType, url: URL, auth: String?) { let record = EvmSyncSourceRecord( - blockchainTypeUid: blockchainType.uid, - url: url.absoluteString, - auth: auth + blockchainTypeUid: blockchainType.uid, + url: url.absoluteString, + auth: auth ) try? evmSyncSourceStorage.save(record: record) @@ -277,5 +277,92 @@ extension EvmSyncSourceManager { syncSourcesUpdatedRelay.accept(blockchainType) } +} + +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 } + .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) + } + .flatMap { String(data: $0, encoding: .utf8) } + let record = EvmSyncSourceRecord( + blockchainTypeUid: source.blockchainTypeUid, + url: source.url, + auth: auth + ) + + blockchainTypes.insert(BlockchainType(uid: source.blockchainTypeUid)) + try? evmSyncSourceStorage.save(record: record) + } + + backup.selected.forEach { source in + let blockchainType = BlockchainType(uid: source.blockchainTypeUid) + if let syncSource = allSyncSources(blockchainType: blockchainType) + .first(where: { $0.rpcSource.url.absoluteString == source.url }) { + saveCurrent(syncSource: syncSource, blockchainType: blockchainType) + } + } + + blockchainTypes.forEach { blockchainType in + syncSourcesUpdatedRelay.accept(blockchainType) + } + } +} + +extension EvmSyncSourceManager { + struct SelectedSource: Codable { + let blockchainTypeUid: String + let url: String + + enum CodingKeys: String, CodingKey { + case blockchainTypeUid = "blockchain_type_id" + case url + } + } + struct CustomSyncSource: Codable { + let blockchainTypeUid: String + let url: String + let auth: BackupCrypto? + + enum CodingKeys: String, CodingKey { + case blockchainTypeUid = "blockchain_type_id" + case url + case auth + } + } + + struct SyncSourceBackup: Codable { + let selected: [SelectedSource] + let custom: [CustomSyncSource] + } } diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Storage/ContactBookManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Storage/ContactBookManager.swift index 1a8e2182f3..8002df04ba 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Storage/ContactBookManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Storage/ContactBookManager.swift @@ -415,7 +415,15 @@ extension ContactBookManager { return contacts } - func restore(contacts:[BackupContact]) throws { + func restore(crypto: BackupCrypto, passphrase: String) throws { + let data = try crypto.data(passphrase: passphrase) + let decoder = JSONDecoder() + let contacts = try decoder.decode([BackupContact].self, from: data) + + try restore(contacts: contacts) + } + + func restore(contacts: [BackupContact]) throws { guard let localUrl else { state = .failed(ContactBookManager.StorageError.localUrlNotAvailable) return diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Storage/EvmSyncSourceStorage.swift b/UnstoppableWallet/UnstoppableWallet/Core/Storage/EvmSyncSourceStorage.swift index d408b1d25a..b6222c4f10 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Storage/EvmSyncSourceStorage.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Storage/EvmSyncSourceStorage.swift @@ -11,6 +11,12 @@ class EvmSyncSourceStorage { extension EvmSyncSourceStorage { + func getAll() throws -> [EvmSyncSourceRecord] { + try dbPool.read { db in + try EvmSyncSourceRecord.fetchAll(db) + } + } + func records(blockchainTypeUid: String) throws -> [EvmSyncSourceRecord] { try dbPool.read { db in try EvmSyncSourceRecord.filter(EvmSyncSourceRecord.Columns.blockchainTypeUid == blockchainTypeUid).fetchAll(db) diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Storage/FavoriteCoinRecordStorage.swift b/UnstoppableWallet/UnstoppableWallet/Core/Storage/FavoriteCoinRecordStorage.swift index 89eb3dd144..400355c607 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Storage/FavoriteCoinRecordStorage.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Storage/FavoriteCoinRecordStorage.swift @@ -31,6 +31,13 @@ extension FavoriteCoinRecordStorage { } } + func deleteAll() { + _ = try! dbPool.write { db in + try FavoriteCoinRecord + .deleteAll(db) + } + } + func deleteFavoriteCoinRecord(coinUid: String) { _ = try! dbPool.write { db in try FavoriteCoinRecord diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Storage/LocalStorage.swift b/UnstoppableWallet/UnstoppableWallet/Core/Storage/LocalStorage.swift index e7549ad6cc..cbe9fee884 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Storage/LocalStorage.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Storage/LocalStorage.swift @@ -101,60 +101,15 @@ extension LocalStorage { } extension LocalStorage { - var backup: [String: Any] { - var fields: [String: Any] = [ - "lock_time_enabled": lockTimeEnabled.description, - "contacts_sync": remoteContactsSync.description, - "show_indicators": indicatorsShown.description, - ] - - if let chartIndicators { - fields["chart_indicators"] = chartIndicators.hs.hexString - } - - let providers: [String: String] = BlockchainType - .supported - .filter { !$0.allowedProviders.isEmpty } - .map { ($0.uid, defaultProvider(blockchainType: $0).rawValue) } - .reduce(into: [:]) { $0[$1.0] = $1.1 } - - fields["swap_providers"] = providers - - return fields - } - - private func bool(value: Any?) -> Bool? { - if let string = value as? String, - let enabled = Bool(string) { - return enabled - } - return nil - } - - func restore(backup _: [String: Any]) { - if let lockTime = bool(value: backup["lock_time_enabled"]) { - lockTimeEnabled = lockTime - } - if let contactsSync = bool(value: backup["contacts_sync"]) { - remoteContactsSync = contactsSync - } - if let showIndicators = bool(value: backup["show_indicators"]) { - indicatorsShown = showIndicators - } - - if let chartIndicators = backup["chart_indicators"] as? String, - let data = chartIndicators.hs.hexData { - self.chartIndicators = data - } - - if let providers = backup["swap_providers"] as? [String: String] { - providers.forEach { key, value in - let type = BlockchainType(uid: key) - if let provider = SwapModule.Dex.Provider(rawValue: value), - !type.allowedProviders.isEmpty { - - setDefaultProvider(blockchainType: type, provider: provider) - } + func restore(backup: SettingsBackup) { + lockTimeEnabled = backup.lockTimeEnabled + remoteContactsSync = backup.remoteContactsSync + indicatorsShown = backup.indicatorsShown + chartIndicators = backup.chartIndicators.encoded //todo: + backup.defaultProviders.forEach { provider in + let blockchainType = BlockchainType(uid: provider.blockchainTypeId) + if let dexProvider = SwapModule.Dex.Provider(rawValue: provider.provider) { + return setDefaultProvider(blockchainType: blockchainType, provider: dexProvider) } } } diff --git a/UnstoppableWallet/UnstoppableWallet/Extensions/ThemeMode.swift b/UnstoppableWallet/UnstoppableWallet/Extensions/ThemeMode.swift new file mode 100644 index 0000000000..0b4e5b9a96 --- /dev/null +++ b/UnstoppableWallet/UnstoppableWallet/Extensions/ThemeMode.swift @@ -0,0 +1,3 @@ +import ThemeKit + +extension ThemeMode: Codable {} diff --git a/UnstoppableWallet/UnstoppableWallet/Models/BackupContact.swift b/UnstoppableWallet/UnstoppableWallet/Models/BackupContact.swift index 344f5328bf..ae0b6c698e 100644 --- a/UnstoppableWallet/UnstoppableWallet/Models/BackupContact.swift +++ b/UnstoppableWallet/UnstoppableWallet/Models/BackupContact.swift @@ -1,7 +1,7 @@ import Foundation import ObjectMapper -class BackupContact: ImmutableMappable, Hashable, Equatable { +class BackupContact: Codable, ImmutableMappable, Hashable, Equatable { let uid: String let name: String let addresses: [ContactAddress] @@ -19,12 +19,12 @@ class BackupContact: ImmutableMappable, Hashable, Equatable { } func mapping(map: Map) { - uid >>> map["uid"] - name >>> map["name"] - addresses >>> map["addresses"] + uid >>> map["uid"] + name >>> map["name"] + addresses >>> map["addresses"] } - static func ==(lhs: BackupContact, rhs: BackupContact) -> Bool { + static func == (lhs: BackupContact, rhs: BackupContact) -> Bool { lhs.uid == rhs.uid } @@ -33,17 +33,15 @@ class BackupContact: ImmutableMappable, Hashable, Equatable { } func address(blockchainUid: String) -> ContactAddress? { - addresses.first { $0.blockchainUid == blockchainUid } + addresses.first { $0.blockchainUid == blockchainUid } } - } -class BackupContactBook { +class BackupContactBook: Codable { static let empty = BackupContactBook(contacts: []) let contacts: [BackupContact] init(contacts: [BackupContact]) { self.contacts = contacts } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Models/BalancePrimaryValue.swift b/UnstoppableWallet/UnstoppableWallet/Models/BalancePrimaryValue.swift index 409abf7688..c4dc1d4c76 100644 --- a/UnstoppableWallet/UnstoppableWallet/Models/BalancePrimaryValue.swift +++ b/UnstoppableWallet/UnstoppableWallet/Models/BalancePrimaryValue.swift @@ -1,4 +1,4 @@ -enum BalancePrimaryValue: String, CaseIterable { +enum BalancePrimaryValue: String, CaseIterable, Codable { case coin case currency diff --git a/UnstoppableWallet/UnstoppableWallet/Models/Contact.swift b/UnstoppableWallet/UnstoppableWallet/Models/Contact.swift index e1e046855d..19710a987e 100644 --- a/UnstoppableWallet/UnstoppableWallet/Models/Contact.swift +++ b/UnstoppableWallet/UnstoppableWallet/Models/Contact.swift @@ -1,23 +1,28 @@ import Foundation import ObjectMapper -class ContactAddress: ImmutableMappable, Hashable, Equatable { +class ContactAddress: Codable, ImmutableMappable, Hashable, Equatable { let blockchainUid: String let address: String + enum CodingKeys: String, CodingKey { + case blockchainUid = "blockchain_uid" + case address = "address" + } + init(blockchainUid: String, address: String) { self.blockchainUid = blockchainUid self.address = address } required init(map: Map) throws { - blockchainUid = try map.value("blockchain_uid") - address = try map.value("address") + blockchainUid = try map.value(CodingKeys.blockchainUid.rawValue) + address = try map.value(CodingKeys.address.rawValue) } func mapping(map: Map) { - blockchainUid >>> map["blockchain_uid"] - address >>> map["address"] + blockchainUid >>> map[CodingKeys.blockchainUid.rawValue] + address >>> map[CodingKeys.address.rawValue] } func hash(into hasher: inout Hasher) { @@ -40,7 +45,7 @@ extension Array where Element == ContactAddress { } -class Contact: ImmutableMappable, Hashable, Equatable { +class Contact: Codable, ImmutableMappable, Hashable, Equatable { let uid: String let modifiedAt: TimeInterval let name: String @@ -81,7 +86,7 @@ class Contact: ImmutableMappable, Hashable, Equatable { } -class DeletedContact: ImmutableMappable, Hashable, Equatable { +class DeletedContact: Codable, ImmutableMappable, Hashable, Equatable { let uid: String let deletedAt: TimeInterval @@ -110,7 +115,7 @@ class DeletedContact: ImmutableMappable, Hashable, Equatable { } -class ContactBook: ImmutableMappable { +class ContactBook: Codable, ImmutableMappable { static let empty = ContactBook(contacts: [], deletedContacts: []) let version: Int let contacts: [Contact] diff --git a/UnstoppableWallet/UnstoppableWallet/Models/LaunchScreen.swift b/UnstoppableWallet/UnstoppableWallet/Models/LaunchScreen.swift index 9240948c28..6b4e4ec30c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Models/LaunchScreen.swift +++ b/UnstoppableWallet/UnstoppableWallet/Models/LaunchScreen.swift @@ -23,3 +23,12 @@ enum LaunchScreen: String, CaseIterable { } } + +extension LaunchScreen: Codable { + enum CodingKeys: String, CodingKey { + case auto + case balance + case marketOverview = "market_overview" + case watchlist + } +} diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/BackupModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/BackupModule.swift index f78e5a9a2d..aa65f89a97 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/BackupModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/BackupModule.swift @@ -15,7 +15,7 @@ struct BackupModule { } static func cloudViewController(account: Account) -> UIViewController { - let service = ICloudBackupTermsService(cloudAccountBackupManager: App.shared.cloudAccountBackupManager, account: account) + let service = ICloudBackupTermsService(cloudAccountBackupManager: App.shared.cloudBackupManager, account: account) let viewModel = ICloudBackupTermsViewModel(service: service) let viewController = ICloudBackupTermsViewController(viewModel: viewModel) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/AppBackupProvider.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/AppBackupProvider.swift new file mode 100644 index 0000000000..9e6276eb2f --- /dev/null +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/AppBackupProvider.swift @@ -0,0 +1,291 @@ +import CurrencyKit +import Foundation +import LanguageKit +import MarketKit +import ThemeKit + +class AppBackupProvider { + private static let version = 2 + + private let accountManager: AccountManager + private let accountFactory: AccountFactory + private let walletManager: WalletManager + private let favoritesManager: FavoritesManager + private let evmSyncSourceManager: EvmSyncSourceManager + private let restoreSettingsManager: RestoreSettingsManager + private let localStorage: LocalStorage + private let languageManager: LanguageManager + private let currencyKit: CurrencyKit.Kit + private let themeManager: ThemeManager + private let launchScreenManager: LaunchScreenManager + private let appIconManager: AppIconManager + private let balancePrimaryValueManager: BalancePrimaryValueManager + private let balanceConversionManager: BalanceConversionManager + private let balanceHiddenManager: BalanceHiddenManager + private let contactManager: ContactBookManager + + init(accountManager: AccountManager, + accountFactory: AccountFactory, + walletManager: WalletManager, + favoritesManager: FavoritesManager, + evmSyncSourceManager: EvmSyncSourceManager, + restoreSettingsManager: RestoreSettingsManager, + localStorage: LocalStorage, + languageManager: LanguageManager, + currencyKit: CurrencyKit.Kit, + themeManager: ThemeManager, + launchScreenManager: LaunchScreenManager, + appIconManager: AppIconManager, + balancePrimaryValueManager: BalancePrimaryValueManager, + balanceConversionManager: BalanceConversionManager, + balanceHiddenManager: BalanceHiddenManager, + contactManager: ContactBookManager) + { + self.accountManager = accountManager + self.accountFactory = accountFactory + self.walletManager = walletManager + self.favoritesManager = favoritesManager + self.evmSyncSourceManager = evmSyncSourceManager + self.restoreSettingsManager = restoreSettingsManager + self.localStorage = localStorage + self.languageManager = languageManager + self.currencyKit = currencyKit + self.themeManager = themeManager + self.launchScreenManager = launchScreenManager + self.appIconManager = appIconManager + self.balancePrimaryValueManager = balancePrimaryValueManager + self.balanceConversionManager = balanceConversionManager + self.balanceHiddenManager = balanceHiddenManager + 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 + .wallets(account: account).map { + let settings = restoreSettingsManager + .settings(accountId: account.id, blockchainType: $0.token.blockchainType) + .reduce(into: [:]) { $0[$1.0.rawValue] = $1.1 } + + return WalletBackup.EnabledWallet($0, settings: settings) + } + return try AppBackupProvider.walletBackup( + accountType: account.type, + wallets: wallets, + isManualBackedUp: account.backedUp, + 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).rawValue + ) + } + + settings = SettingsBackup( + evmSyncSources: evmSyncSourceManager.backup(passphrase: passphrase), + lockTimeEnabled: localStorage.lockTimeEnabled, + remoteContactsSync: localStorage.remoteContactsSync, + defaultProviders: providers, + chartIndicators: [], + 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 + ) + } + } + + guard !wallets.isEmpty || + !watchlistIds.isEmpty || + !contacts.isEmpty || + settings != nil + else { + throw CodingError.emptyParameters + } + + + var contactCrypto: BackupCrypto? + if !contacts.isEmpty { + let encoder = JSONEncoder() + let data = try encoder.encode(contacts) + contactCrypto = try BackupCrypto.instance(data: data, passphrase: passphrase) + } + + return FullBackup( + id: UUID().uuidString, + wallets: wallets, + watchlistIds: watchlistIds, + contacts: contactCrypto, + settings: settings, + version: AppBackupProvider.version, + timestamp: Date().timeIntervalSince1970.rounded() + ) + } + + func walletRestore(backup: RestoreCloudModule.RestoredBackup, accountType: AccountType) { + switch accountType { + case .cex: + let account = accountFactory.account( + type: accountType, + origin: .restored, + backedUp: backup.walletBackup.isManualBackedUp, + name: backup.name + ) + accountManager.save(account: account) + default: + let account = accountFactory.account(type: accountType, origin: .restored, backedUp: backup.walletBackup.isManualBackedUp, name: backup.name) + accountManager.save(account: account) + + let wallets = backup.walletBackup.enabledWallets.map { + if !$0.settings.isEmpty { + var restoreSettings = [RestoreSettingType: String]() + $0.settings.forEach { key, value in + if let key = RestoreSettingType(rawValue: key) { + restoreSettings[key] = value + } + } + if let tokenQuery = TokenQuery(id: $0.tokenQueryId) { + restoreSettingsManager.save(settings: restoreSettings, account: account, blockchainType: tokenQuery.blockchainType) + } + } + return EnabledWallet( + tokenQueryId: $0.tokenQueryId, + accountId: account.id, + coinName: $0.coinName, + coinCode: $0.coinCode, + tokenDecimals: $0.tokenDecimals + ) + } + walletManager.save(enabledWallets: wallets) + } + } + + 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 + } + } + + if encodedWallets.count != backup.wallets.count { + encryptionError = CodingError.invalidPassword + } + + if let encryptionError { + throw encryptionError + } + // restore only if all wallet was encrypted with password + encodedWallets.forEach { wallet in + walletRestore( + backup: wallet.0, + accountType: wallet.1 + ) + } + + favoritesManager.add(coinUids: backup.watchlistIds) + + if let contacts = backup.contacts { + try contactManager.restore(crypto: contacts, passphrase: passphrase) + } + + if let settings = backup.settings { + evmSyncSourceManager.restore(backup: settings.evmSyncSources) + 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 + + 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 + } + } + } +} + +extension AppBackupProvider { + static func walletBackup(accountType: AccountType, wallets: [WalletBackup.EnabledWallet], isManualBackedUp: Bool, name: String, passphrase: String) throws -> RestoreCloudModule.RestoredBackup { + let message = accountType.uniqueId(hashed: false) + let crypto = try BackupCrypto.instance(data: message, passphrase: passphrase) + + let walletBackup = WalletBackup( + crypto: crypto, + enabledWallets: wallets, + id: accountType.uniqueId().hs.hex, + type: AccountType.Abstract(accountType), + isManualBackedUp: isManualBackedUp, + version: Self.version, + timestamp: Date().timeIntervalSince1970.rounded() + ) + + return .init(name: name, walletBackup: walletBackup) + } +} + +extension AppBackupProvider { + enum CodingError: Error { + case invalidPassword + case emptyParameters + } + + enum Field { + static func all(ids: [String]) -> [Self] { + [.accounts(ids: ids), .watchlist, .contacts, .settings] + } + + case accounts(ids: [String]) + case watchlist + case contacts + case settings + } +} diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/BackupCloudModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/BackupCloudModule.swift index dabc1a6829..ac491d3782 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/BackupCloudModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/BackupCloudModule.swift @@ -6,7 +6,7 @@ class BackupCloudModule { static let minimumPassphraseLength = 8 static func backupTerms(account: Account) -> UIViewController { - let service = ICloudBackupTermsService(cloudAccountBackupManager: App.shared.cloudAccountBackupManager, account: account) + let service = ICloudBackupTermsService(cloudAccountBackupManager: App.shared.cloudBackupManager, account: account) let viewModel = ICloudBackupTermsViewModel(service: service) let controller = ICloudBackupTermsViewController(viewModel: viewModel) @@ -14,7 +14,7 @@ class BackupCloudModule { } static func backupName(account: Account) -> UIViewController { - let service = ICloudBackupNameService(iCloudManager: App.shared.cloudAccountBackupManager, account: account) + let service = ICloudBackupNameService(iCloudManager: App.shared.cloudBackupManager, account: account) let viewModel = ICloudBackupNameViewModel(service: service) let controller = ICloudBackupNameViewController(viewModel: viewModel) @@ -22,7 +22,7 @@ class BackupCloudModule { } static func backupPassword(account: Account, name: String) -> UIViewController { - let service = BackupCloudPassphraseService(iCloudManager: App.shared.cloudAccountBackupManager, walletManager: App.shared.walletManager, account: account, name: name) + let service = BackupCloudPassphraseService(iCloudManager: App.shared.cloudBackupManager, account: account, name: name) let viewModel = BackupCloudPassphraseViewModel(service: service) let controller = BackupCloudPassphraseViewController(viewModel: viewModel) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Name/ICloudBackupNameService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Name/ICloudBackupNameService.swift index 57573b2e57..bb83f3cc2e 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Name/ICloudBackupNameService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Name/ICloudBackupNameService.swift @@ -3,12 +3,12 @@ import Combine import HsExtensions class ICloudBackupNameService { - private let iCloudManager: CloudAccountBackupManager + private let iCloudManager: CloudBackupManager let account: Account @PostPublished private(set) var state: State = .failure(error: NameError.empty) - init(iCloudManager: CloudAccountBackupManager, account: Account) { + init(iCloudManager: CloudBackupManager, account: Account) { self.iCloudManager = iCloudManager self.account = account diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Passphrase/BackupCloudPassphraseService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Passphrase/BackupCloudPassphraseService.swift index d59eeb3917..abf936caa0 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Passphrase/BackupCloudPassphraseService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Passphrase/BackupCloudPassphraseService.swift @@ -1,17 +1,15 @@ import Foundation class BackupCloudPassphraseService { - private let iCloudManager: CloudAccountBackupManager - private let walletManager: WalletManager + private let iCloudManager: CloudBackupManager private let account: Account private let name: String var passphrase: String = "" var passphraseConfirmation: String = "" - init(iCloudManager: CloudAccountBackupManager, walletManager: WalletManager, account: Account, name: String) { + init(iCloudManager: CloudBackupManager, account: Account, name: String) { self.iCloudManager = iCloudManager - self.walletManager = walletManager self.account = account self.name = name } @@ -25,28 +23,16 @@ extension BackupCloudPassphraseService { } func createBackup() throws { - guard !passphrase.isEmpty else { - throw CreateError.emptyPassphrase - } - - guard passphrase.count >= BackupCloudModule.minimumPassphraseLength else { - throw CreateError.simplePassword - } - - let allSatisfy = BackupCloudModule.PassphraseCharacterSet.allCases.allSatisfy { set in set.contains(passphrase) } - if !allSatisfy { - throw CreateError.simplePassword - } + try BackupCrypto.validate(passphrase: passphrase) guard passphrase == passphraseConfirmation else { throw CreateError.invalidConfirmation } do { - let wallets = App.shared.walletManager.wallets(account: account) - try iCloudManager.save(account: account, wallets: wallets, isManualBackedUp: account.backedUp, passphrase: passphrase, name: name) + try iCloudManager.save(account: account, passphrase: passphrase, name: name) } catch { - if case .urlNotAvailable = error as? CloudAccountBackupManager.BackupError { + if case .urlNotAvailable = error as? CloudBackupManager.BackupError { throw CreateError.urlNotAvailable } throw CreateError.cantSaveFile(error) @@ -58,8 +44,6 @@ extension BackupCloudPassphraseService { extension BackupCloudPassphraseService { enum CreateError: Error { - case emptyPassphrase - case simplePassword case invalidConfirmation case urlNotAvailable case cantSaveFile(Error) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Passphrase/BackupCloudPassphraseViewModel.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Passphrase/BackupCloudPassphraseViewModel.swift index 2ceae1565f..53331cd609 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Passphrase/BackupCloudPassphraseViewModel.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Passphrase/BackupCloudPassphraseViewModel.swift @@ -81,18 +81,18 @@ extension BackupCloudPassphraseViewModel { processing = false finishSubject.send(()) } catch { - switch (error as? BackupCloudPassphraseService.CreateError) { - case .emptyPassphrase: + switch error { + case BackupCrypto.ValidationError.emptyPassphrase: passphraseCaution = Caution(text: "backup.cloud.password.error.empty_passphrase".localized, type: .error) - case .simplePassword: + case BackupCrypto.ValidationError.simplePassword: passphraseCaution = Caution(text: "backup.cloud.password.error.minimum_requirement".localized, type: .error) - case .invalidConfirmation: + case BackupCloudPassphraseService.CreateError.invalidConfirmation: passphraseConfirmationCaution = Caution(text: "backup.cloud.password.confirm.error.doesnt_match".localized, type: .error) - case .urlNotAvailable: + case BackupCloudPassphraseService.CreateError.urlNotAvailable: showErrorSubject.send("backup.cloud.not_available".localized) - case .cantSaveFile: + case BackupCloudPassphraseService.CreateError.cantSaveFile: showErrorSubject.send("backup.cloud.cant_create_file".localized) - case .none: + default: showErrorSubject.send(error.smartDescription) } processing = false diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Terms/ICloudBackupTermsService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Terms/ICloudBackupTermsService.swift index 5734c0cc0b..5f4cd25e40 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Terms/ICloudBackupTermsService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/Terms/ICloudBackupTermsService.swift @@ -6,11 +6,11 @@ class ICloudBackupTermsService { let account: Account let termCount = 1 - private let cloudAccountBackupManager: CloudAccountBackupManager + private let cloudAccountBackupManager: CloudBackupManager @PostPublished private(set) var state: State = .selectedTerms(Set()) - init(cloudAccountBackupManager: CloudAccountBackupManager, account: Account) { + init(cloudAccountBackupManager: CloudBackupManager, account: Account) { self.account = account self.cloudAccountBackupManager = cloudAccountBackupManager } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/WalletBackupConverter.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/WalletBackupConverter.swift deleted file mode 100644 index 19917aaeb7..0000000000 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Backup/ICloud/WalletBackupConverter.swift +++ /dev/null @@ -1,73 +0,0 @@ -import Foundation -import MarketKit - -class WalletBackupConverter { - private static let version = 2 - - static func encode(accountType: AccountType, wallets: [WalletBackup.EnabledWallet], isManualBackedUp: Bool, passphrase: String) throws -> Data { - let message = accountType.uniqueId(hashed: false) - let iv = BackupCryptoHelper.generateInitialVector().hs.hex - - let cipherText = try BackupCryptoHelper.AES128( - operation: .encrypt, - ivHex: iv, - pass: passphrase, - message: message, - kdf: .defaultBackup - ) - let encodedCipherText = cipherText.base64EncodedString() - let mac = try BackupCryptoHelper.mac( - pass: passphrase, - message: encodedCipherText.hs.data, - kdf: .defaultBackup - ) - - let crypto = WalletBackupCrypto( - cipher: BackupCryptoHelper.defaultCypher, - cipherParams: CipherParams(iv: iv), - cipherText: encodedCipherText, - kdf: BackupCryptoHelper.defaultKdf, - kdfParams: .defaultBackup, - mac: mac.hs.hex - ) - let backup = WalletBackup( - crypto: crypto, - enabledWallets: wallets, - id: accountType.uniqueId().hs.hex, - type: AccountType.Abstract(accountType), - isManualBackedUp: isManualBackedUp, - version: Self.version, - timestamp: Date().timeIntervalSince1970 - ) - return try JSONEncoder().encode(backup) - } - - static func decode(data: Data, passphrase: String) throws -> AccountType { - let backup = try JSONDecoder().decode(WalletBackup.self, from: data) - - guard let message = Data(base64Encoded: backup.crypto.cipherText) else { - throw CodingError.cantDecodeCipherText - } - - let decryptData = try BackupCryptoHelper.AES128( - operation: .decrypt, - ivHex: backup.crypto.cipherParams.iv, - pass: passphrase, - message: message, - kdf: .defaultBackup - ) - - guard let accountType = AccountType.decode(uniqueId: decryptData, type: backup.type) else { - throw CodingError.cantDecodeAccountType - } - - return accountType - } -} - -extension WalletBackupConverter { - enum CodingError: Error { - case cantDecodeCipherText - case cantDecodeAccountType - } -} diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Favorites/FavoritesManager.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Favorites/FavoritesManager.swift index 3d0a6771db..12d09d3e4d 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Favorites/FavoritesManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Favorites/FavoritesManager.swift @@ -32,6 +32,10 @@ extension FavoritesManager { coinUidsUpdatedRelay.accept(()) } + func removeAll() { + storage.deleteAll() + } + func remove(coinUid: String) { storage.deleteFavoriteCoinRecord(coinUid: coinUid) coinUidsUpdatedRelay.accept(()) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccount/ManageAccountModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccount/ManageAccountModule.swift index 203d07d749..e110217c4a 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccount/ManageAccountModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccount/ManageAccountModule.swift @@ -9,7 +9,7 @@ struct ManageAccountModule { guard let service = ManageAccountService( accountId: accountId, accountManager: App.shared.accountManager, - cloudBackupManager: App.shared.cloudAccountBackupManager, + cloudBackupManager: App.shared.cloudBackupManager, pinKit: App.shared.pinKit ) else { return nil diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccount/ManageAccountService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccount/ManageAccountService.swift index e98a1eaa27..2fd125acc1 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccount/ManageAccountService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccount/ManageAccountService.swift @@ -13,7 +13,7 @@ class ManageAccountService { } private let accountManager: AccountManager - private let cloudBackupManager: CloudAccountBackupManager + private let cloudBackupManager: CloudBackupManager private let pinKit: PinKit.Kit private let disposeBag = DisposeBag() private var cancellables = Set() @@ -30,7 +30,7 @@ class ManageAccountService { private var newName: String - init?(accountId: String, accountManager: AccountManager, cloudBackupManager: CloudAccountBackupManager, pinKit: PinKit.Kit) { + init?(accountId: String, accountManager: AccountManager, cloudBackupManager: CloudBackupManager, pinKit: PinKit.Kit) { guard let account = accountManager.account(id: accountId) else { return nil } @@ -46,7 +46,7 @@ class ManageAccountService { subscribe(disposeBag, accountManager.accountUpdatedObservable) { [weak self] in self?.handleUpdated(account: $0) } subscribe(disposeBag, accountManager.accountDeletedObservable) { [weak self] in self?.handleDeleted(account: $0) } - cloudBackupManager.$items + cloudBackupManager.$oneWalletItems .sink { [weak self] _ in self?.cloudBackedUpRelay.accept(()) } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccounts/ManageAccountsModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccounts/ManageAccountsModule.swift index 054d153095..20d474027c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccounts/ManageAccountsModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccounts/ManageAccountsModule.swift @@ -3,7 +3,7 @@ import UIKit struct ManageAccountsModule { static func viewController(mode: Mode, createAccountListener: ICreateAccountListener? = nil) -> UIViewController { - let service = ManageAccountsService(accountManager: App.shared.accountManager, cloudBackupManager: App.shared.cloudAccountBackupManager) + let service = ManageAccountsService(accountManager: App.shared.accountManager, cloudBackupManager: App.shared.cloudBackupManager) let viewModel = ManageAccountsViewModel(service: service, mode: mode) return ManageAccountsViewController(viewModel: viewModel, createAccountListener: createAccountListener) } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccounts/ManageAccountsService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccounts/ManageAccountsService.swift index e437dbccf6..6053ac1ea2 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccounts/ManageAccountsService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/ManageAccounts/ManageAccountsService.swift @@ -4,7 +4,7 @@ import Combine class ManageAccountsService { private let accountManager: AccountManager - private let cloudBackupManager: CloudAccountBackupManager + private let cloudBackupManager: CloudBackupManager private let disposeBag = DisposeBag() private var cancellables = Set() @@ -15,14 +15,14 @@ class ManageAccountsService { } } - init(accountManager: AccountManager, cloudBackupManager: CloudAccountBackupManager) { + init(accountManager: AccountManager, cloudBackupManager: CloudBackupManager) { self.accountManager = accountManager self.cloudBackupManager = cloudBackupManager subscribe(disposeBag, accountManager.accountsObservable) { [weak self] _ in self?.syncItems() } subscribe(disposeBag, accountManager.activeAccountObservable) { [weak self] _ in self?.syncItems() } - cloudBackupManager.$items + cloudBackupManager.$oneWalletItems .sink { [weak self] _ in self?.syncItems() } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudModule.swift index b6fe4d0854..c3801524d5 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudModule.swift @@ -6,16 +6,21 @@ struct RestoreCloudModule { static func viewController(returnViewController: UIViewController?) -> UIViewController { let service = RestoreCloudService( - cloudAccountBackupManager: App.shared.cloudAccountBackupManager, + cloudAccountBackupManager: App.shared.cloudBackupManager, accountManager: App.shared.accountManager ) let viewModel = RestoreCloudViewModel(service: service) return RestoreCloudViewController(viewModel: viewModel, returnViewController: returnViewController) } - struct RestoredBackup { + struct RestoredBackup: Codable { let name: String let walletBackup: WalletBackup + + enum CodingKeys: String, CodingKey { + case name + case walletBackup = "backup" + } } struct RestoredAccount { @@ -26,3 +31,12 @@ struct RestoreCloudModule { } } + +extension RestoreCloudModule { + enum RestoreError: Error { + case emptyPassphrase + case simplePassword + case invalidPassword + case invalidBackup + } +} \ No newline at end of file diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseModule.swift index 840a0d1417..9b89c77638 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseModule.swift @@ -5,7 +5,8 @@ class RestoreCloudPassphraseModule { static func restorePassword(item: RestoreCloudModule.RestoredBackup, returnViewController: UIViewController?) -> UIViewController { let service = RestoreCloudPassphraseService( - iCloudManager: App.shared.cloudAccountBackupManager, + iCloudManager: App.shared.cloudBackupManager, + appBackupProvider: App.shared.appBackupProvider, accountFactory: App.shared.accountFactory, accountManager: App.shared.accountManager, walletManager: App.shared.walletManager, diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseService.swift index 665c3b3283..b29f427712 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseService.swift @@ -2,7 +2,8 @@ import Foundation import MarketKit class RestoreCloudPassphraseService { - private let iCloudManager: CloudAccountBackupManager + private let iCloudManager: CloudBackupManager + private let appBackupProvider: AppBackupProvider private let accountFactory: AccountFactory private let accountManager: AccountManager private let walletManager: WalletManager @@ -12,8 +13,9 @@ class RestoreCloudPassphraseService { var passphrase: String = "" - init(iCloudManager: CloudAccountBackupManager, accountFactory: AccountFactory, accountManager: AccountManager, walletManager: WalletManager, restoreSettingsManager: RestoreSettingsManager, item: RestoreCloudModule.RestoredBackup) { + init(iCloudManager: CloudBackupManager, appBackupProvider: AppBackupProvider, accountFactory: AccountFactory, accountManager: AccountManager, walletManager: WalletManager, restoreSettingsManager: RestoreSettingsManager, item: RestoreCloudModule.RestoredBackup) { self.iCloudManager = iCloudManager + self.appBackupProvider = appBackupProvider self.accountFactory = accountFactory self.accountManager = accountManager self.walletManager = walletManager @@ -38,104 +40,42 @@ class RestoreCloudPassphraseService { } } return EnabledWallet( - tokenQueryId: $0.tokenQueryId, - accountId: account.id, - coinName: $0.coinName, - coinCode: $0.coinCode, - tokenDecimals: $0.tokenDecimals + tokenQueryId: $0.tokenQueryId, + accountId: account.id, + coinName: $0.coinName, + coinCode: $0.coinCode, + tokenDecimals: $0.tokenDecimals ) } walletManager.save(enabledWallets: wallets) } - } extension RestoreCloudPassphraseService { - func validate(text: String?) -> Bool { PassphraseValidator.validate(text: text) } func importWallet() async throws -> RestoreResult { - let crypto = restoredBackup.walletBackup.crypto - - guard !passphrase.isEmpty else { - throw RestoreError.emptyPassphrase - } - guard passphrase.count >= BackupCloudModule.minimumPassphraseLength else { - throw RestoreError.simplePassword - } - - let allSatisfy = BackupCloudModule.PassphraseCharacterSet.allCases.allSatisfy { set in set.contains(passphrase) } - if !allSatisfy { - throw RestoreError.simplePassword - } - - guard let walletData = Data(base64Encoded: crypto.cipherText) else { - throw RestoreError.invalidBackup - } - - let isValid = (try? BackupCryptoHelper.isValid( - macHex: crypto.mac, - pass: passphrase, - message: crypto.cipherText.hs.data, - kdf: crypto.kdfParams - )) ?? false - - guard isValid else { - throw RestoreError.invalidPassword - } - - do { - let data = try BackupCryptoHelper.AES128( - operation: .decrypt, - ivHex: crypto.cipherParams.iv, - pass: passphrase, - message: walletData, - kdf: crypto.kdfParams) - - guard let accountType = AccountType.decode(uniqueId: data, type: restoredBackup.walletBackup.type) else { - throw RestoreError.invalidBackup - } - - switch accountType { - case .cex: - let account = accountFactory.account( - type: accountType, - origin: .restored, - backedUp: restoredBackup.walletBackup.isManualBackedUp, - name: restoredBackup.name - ) - accountManager.save(account: account) - return .success - default: - createAccount(accountType: accountType) - return .restoredAccount(RestoreCloudModule.RestoredAccount( - name: restoredBackup.name, - accountType: accountType, - isManualBackedUp: restoredBackup.walletBackup.isManualBackedUp, - showSelectCoins: restoredBackup.walletBackup.enabledWallets.isEmpty - )) - } - } catch { - throw RestoreError.invalidBackup + let accountType = try restoredBackup.walletBackup.crypto.accountType(type: restoredBackup.walletBackup.type, passphrase: passphrase) + appBackupProvider.walletRestore(backup: restoredBackup, accountType: accountType) + switch accountType { + case .cex: + return .success + default: + return .restoredAccount(RestoreCloudModule.RestoredAccount( + name: restoredBackup.name, + accountType: accountType, + isManualBackedUp: restoredBackup.walletBackup.isManualBackedUp, + showSelectCoins: restoredBackup.walletBackup.enabledWallets.isEmpty + )) } } - } extension RestoreCloudPassphraseService { - - enum RestoreError: Error { - case emptyPassphrase - case simplePassword - case invalidPassword - case invalidBackup - } - enum RestoreResult { case restoredAccount(RestoreCloudModule.RestoredAccount) case success } - } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseViewModel.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseViewModel.swift index 0f9d9da4e9..bd7ef35223 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseViewModel.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudPassphrase/RestoreCloudPassphraseViewModel.swift @@ -77,7 +77,7 @@ extension RestoreCloudPassphraseViewModel { } } } catch { - switch (error as? RestoreCloudPassphraseService.RestoreError) { + switch (error as? RestoreCloudModule.RestoreError) { case .emptyPassphrase: self?.passphraseCaution = Caution(text: "backup.cloud.password.error.empty_passphrase".localized, type: .error) case .simplePassword: diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudService.swift index 98f773800c..1c85262138 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCloud/RestoreCloudService.swift @@ -2,7 +2,7 @@ import Foundation import Combine class RestoreCloudService { - private let cloudAccountBackupManager: CloudAccountBackupManager + private let cloudAccountBackupManager: CloudBackupManager private let accountManager: AccountManager private var cancellables = Set() @@ -10,17 +10,17 @@ class RestoreCloudService { private let deleteItemCompletedSubject = PassthroughSubject() @Published var items = [Item]() - init(cloudAccountBackupManager: CloudAccountBackupManager, accountManager: AccountManager) { + init(cloudAccountBackupManager: CloudBackupManager, accountManager: AccountManager) { self.cloudAccountBackupManager = cloudAccountBackupManager self.accountManager = accountManager - cloudAccountBackupManager.$items + cloudAccountBackupManager.$oneWalletItems .sink { [weak self] in self?.sync(backups: $0) } .store(in: &cancellables) - sync(backups: cloudAccountBackupManager.items) + sync(backups: cloudAccountBackupManager.oneWalletItems) } private func sync(backups: [String: WalletBackup]) { diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreType/RestoreTypeModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreType/RestoreTypeModule.swift index cc24f64694..1ee4f286d4 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreType/RestoreTypeModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreType/RestoreTypeModule.swift @@ -4,7 +4,7 @@ import ThemeKit struct RestoreTypeModule { static func viewController(sourceViewController: UIViewController? = nil, returnViewController: UIViewController? = nil) -> UIViewController { - let viewModel = RestoreTypeViewModel(cloudAccountBackupManager: App.shared.cloudAccountBackupManager) + let viewModel = RestoreTypeViewModel(cloudAccountBackupManager: App.shared.cloudBackupManager) let viewController = RestoreTypeViewController(viewModel: viewModel, returnViewController: returnViewController) let module = ThemeNavigationController(rootViewController: viewController) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreType/RestoreTypeViewModel.swift b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreType/RestoreTypeViewModel.swift index cffc62d0ae..12a7dceb28 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreType/RestoreTypeViewModel.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/RestoreAccount/RestoreType/RestoreTypeViewModel.swift @@ -2,12 +2,12 @@ import UIKit import Combine class RestoreTypeViewModel { - private let cloudAccountBackupManager: CloudAccountBackupManager + private let cloudAccountBackupManager: CloudBackupManager private let showCloudNotAvailableSubject = PassthroughSubject() private let showModuleSubject = PassthroughSubject() - init(cloudAccountBackupManager: CloudAccountBackupManager) { + init(cloudAccountBackupManager: CloudBackupManager) { self.cloudAccountBackupManager = cloudAccountBackupManager } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Settings/Main/MainSettingsModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Settings/Main/MainSettingsModule.swift index 942996a0d3..26a69bc867 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Settings/Main/MainSettingsModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Settings/Main/MainSettingsModule.swift @@ -5,7 +5,7 @@ struct MainSettingsModule { static func viewController() -> UIViewController { let service = MainSettingsService( backupManager: App.shared.backupManager, - cloudAccountBackupManager: App.shared.cloudAccountBackupManager, + cloudAccountBackupManager: App.shared.cloudBackupManager, accountRestoreWarningManager: App.shared.accountRestoreWarningManager, accountManager: App.shared.accountManager, contactBookManager: App.shared.contactManager, diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Settings/Main/MainSettingsService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Settings/Main/MainSettingsService.swift index e75eae927f..7314d41c78 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Settings/Main/MainSettingsService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Settings/Main/MainSettingsService.swift @@ -12,7 +12,7 @@ class MainSettingsService { private let disposeBag = DisposeBag() private let backupManager: BackupManager - private let cloudAccountBackupManager: CloudAccountBackupManager + private let cloudAccountBackupManager: CloudBackupManager private let accountRestoreWarningManager: AccountRestoreWarningManager private let accountManager: AccountManager private let contactBookManager: ContactBookManager @@ -26,7 +26,7 @@ class MainSettingsService { private let iCloudAvailableErrorRelay = BehaviorRelay(value: false) private let noWalletRequiredActionsRelay = BehaviorRelay(value: false) - init(backupManager: BackupManager, cloudAccountBackupManager: CloudAccountBackupManager, accountRestoreWarningManager: AccountRestoreWarningManager, accountManager: AccountManager, contactBookManager: ContactBookManager, pinKit: PinKit.Kit, termsManager: TermsManager, + init(backupManager: BackupManager, cloudAccountBackupManager: CloudBackupManager, accountRestoreWarningManager: AccountRestoreWarningManager, accountManager: AccountManager, contactBookManager: ContactBookManager, pinKit: PinKit.Kit, termsManager: TermsManager, systemInfoManager: SystemInfoManager, currencyKit: CurrencyKit.Kit, walletConnectSessionManager: WalletConnectSessionManager, subscriptionManager: SubscriptionManager) { self.cloudAccountBackupManager = cloudAccountBackupManager self.backupManager = backupManager diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Receive/SelectCoin/CoinProvider.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Receive/SelectCoin/CoinProvider.swift index d385315d28..0b67bde67d 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Receive/SelectCoin/CoinProvider.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Receive/SelectCoin/CoinProvider.swift @@ -60,8 +60,6 @@ extension CoinProvider { } catch { return [] } - - return predefined } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceModule.swift index ad1a23ca77..ab4a64792c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceModule.swift @@ -25,7 +25,7 @@ struct WalletTokenBalanceModule { coinPriceService: coinPriceService, elementService: elementService, appManager: App.shared.appManager, - cloudAccountBackupManager: App.shared.cloudAccountBackupManager, + cloudAccountBackupManager: App.shared.cloudBackupManager, balanceHiddenManager: App.shared.balanceHiddenManager, reachabilityManager: App.shared.reachabilityManager, account: account, diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceService.swift index 9f286d71ff..fe816e2892 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/Token/DataSources/WalletTokenBalance/WalletTokenBalanceService.swift @@ -9,7 +9,7 @@ class WalletTokenBalanceService { private let coinPriceService: WalletCoinPriceService private let elementService: IWalletElementService - private let cloudAccountBackupManager: CloudAccountBackupManager + private let cloudAccountBackupManager: CloudBackupManager private let balanceHiddenManager: BalanceHiddenManager private let reachabilityManager: IReachabilityManager @@ -23,7 +23,7 @@ class WalletTokenBalanceService { private let queue = DispatchQueue(label: "\(AppConfig.label).wallet-token-balance-service", qos: .userInitiated) init(coinPriceService: WalletCoinPriceService, elementService: IWalletElementService, - appManager: IAppManager, cloudAccountBackupManager: CloudAccountBackupManager, + appManager: IAppManager, cloudAccountBackupManager: CloudBackupManager, balanceHiddenManager: BalanceHiddenManager, reachabilityManager: IReachabilityManager, account: Account, element: WalletModule.Element) { self.coinPriceService = coinPriceService diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/WalletModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/WalletModule.swift index 177c67cda9..321f6c3e4d 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/WalletModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/WalletModule.swift @@ -30,7 +30,7 @@ struct WalletModule { balancePrimaryValueManager: App.shared.balancePrimaryValueManager, balanceHiddenManager: App.shared.balanceHiddenManager, balanceConversionManager: App.shared.balanceConversionManager, - cloudAccountBackupManager: App.shared.cloudAccountBackupManager, + cloudAccountBackupManager: App.shared.cloudBackupManager, rateAppManager: App.shared.rateAppManager, appManager: App.shared.appManager, feeCoinProvider: App.shared.feeCoinProvider, diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/WalletService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/WalletService.swift index c12823142f..bd4a20827a 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/WalletService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Wallet/WalletService.swift @@ -37,7 +37,7 @@ class WalletService { private let balancePrimaryValueManager: BalancePrimaryValueManager private let balanceHiddenManager: BalanceHiddenManager private let balanceConversionManager: BalanceConversionManager - private let cloudAccountBackupManager: CloudAccountBackupManager + private let cloudAccountBackupManager: CloudBackupManager private let rateAppManager: RateAppManager private let feeCoinProvider: FeeCoinProvider private let localStorage: StorageKit.ILocalStorage @@ -85,7 +85,7 @@ class WalletService { init(elementServiceFactory: WalletElementServiceFactory, coinPriceService: WalletCoinPriceService, accountManager: AccountManager, cacheManager: EnabledWalletCacheManager, accountRestoreWarningManager: AccountRestoreWarningManager, reachabilityManager: IReachabilityManager, balancePrimaryValueManager: BalancePrimaryValueManager, balanceHiddenManager: BalanceHiddenManager, balanceConversionManager: BalanceConversionManager, - cloudAccountBackupManager: CloudAccountBackupManager, rateAppManager: RateAppManager, appManager: IAppManager, feeCoinProvider: FeeCoinProvider, + cloudAccountBackupManager: CloudBackupManager, rateAppManager: RateAppManager, appManager: IAppManager, feeCoinProvider: FeeCoinProvider, localStorage: StorageKit.ILocalStorage ) { self.elementServiceFactory = elementServiceFactory diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Workers/WalletConnectAppShowWorker/WalletConnectAppShowModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Workers/WalletConnectAppShowWorker/WalletConnectAppShowModule.swift index f88fc97ca4..6a8f45983d 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Workers/WalletConnectAppShowWorker/WalletConnectAppShowModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Workers/WalletConnectAppShowWorker/WalletConnectAppShowModule.swift @@ -4,7 +4,7 @@ class WalletConnectAppShowModule { static func handler(parentViewController: UIViewController? = nil) -> IEventHandler { let walletConnectWorkerService = WalletConnectAppShowService( walletConnectManager: App.shared.walletConnectSessionManager, - cloudAccountBackupManager: App.shared.cloudAccountBackupManager, + cloudAccountBackupManager: App.shared.cloudBackupManager, accountManager: App.shared.accountManager, pinKit: App.shared.pinKit ) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Workers/WalletConnectAppShowWorker/WalletConnectAppShowService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Workers/WalletConnectAppShowWorker/WalletConnectAppShowService.swift index 1e30b3f5df..c789afa373 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Workers/WalletConnectAppShowWorker/WalletConnectAppShowService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/WalletConnect/Workers/WalletConnectAppShowWorker/WalletConnectAppShowService.swift @@ -8,14 +8,14 @@ class WalletConnectAppShowService { private var disposeBag = DisposeBag() private var cancellables = Set() private let walletConnectManager: WalletConnectSessionManager - private let cloudAccountBackupManager: CloudAccountBackupManager + private let cloudAccountBackupManager: CloudBackupManager private let accountManager: AccountManager private let pinKit: PinKit.Kit private let showSessionProposalSubject = PassthroughSubject() private let showSessionRequestSubject = PassthroughSubject() - init(walletConnectManager: WalletConnectSessionManager, cloudAccountBackupManager: CloudAccountBackupManager, accountManager: AccountManager, pinKit: PinKit.Kit) { + init(walletConnectManager: WalletConnectSessionManager, cloudAccountBackupManager: CloudBackupManager, accountManager: AccountManager, pinKit: PinKit.Kit) { self.walletConnectManager = walletConnectManager self.cloudAccountBackupManager = cloudAccountBackupManager self.accountManager = accountManager