diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index ca43afaf30..1432bcbbc3 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -12,14 +12,12 @@ import ../../app_service/service/token/service as token_service import ../../app_service/service/transaction/service as transaction_service import ../../app_service/service/collectible/service as collectible_service import ../../app_service/service/wallet_account/service as wallet_account_service -import ../../app_service/service/setting/service as setting_service import ../../app_service/service/bookmarks/service as bookmark_service import ../../app_service/service/dapp_permissions/service as dapp_permissions_service import ../../app_service/service/mnemonic/service as mnemonic_service import ../../app_service/service/privacy/service as privacy_service import ../../app_service/service/provider/service as provider_service import ../../app_service/service/ens/service as ens_service - import ../../app_service/service/profile/service as profile_service import ../../app_service/service/settings/service as settings_service import ../../app_service/service/about/service as about_service @@ -92,7 +90,6 @@ type transactionService: transaction_service.Service collectibleService: collectible_service.Service walletAccountService: wallet_account_service.Service - settingService: setting_service.Service bookmarkService: bookmark_service.Service dappPermissionsService: dapp_permissions_service.Service ensService: ens_service.Service @@ -167,7 +164,6 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = # Services result.osNotificationService = os_notification_service.newService(statusFoundation.status.events) result.keychainService = keychain_service.newService(statusFoundation.status.events) - result.settingService = setting_service.newService() result.settingsService = settings_service.newService() result.accountsService = accounts_service.newService() result.contactsService = contacts_service.newService(statusFoundation.status.events, statusFoundation.threadpool) @@ -175,9 +171,9 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.communityService = community_service.newService(result.chatService) result.messageService = message_service.newService(statusFoundation.status.events, statusFoundation.threadpool) result.tokenService = token_service.newService(statusFoundation.status.events, statusFoundation.threadpool, - result.settingService, result.settingsService) - result.collectibleService = collectible_service.newService(result.settingService) - result.walletAccountService = wallet_account_service.newService(statusFoundation.status.events, result.settingService, + result.settingsService) + result.collectibleService = collectible_service.newService(result.settingsService) + result.walletAccountService = wallet_account_service.newService(statusFoundation.status.events, result.settingsService, result.tokenService) result.transactionService = transaction_service.newService(statusFoundation.status.events, statusFoundation.threadpool, result.walletAccountService) @@ -212,7 +208,6 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.collectibleService, result.walletAccountService, result.bookmarkService, - result.settingService, result.profileService, result.settingsService, result.contactsService, @@ -236,18 +231,6 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.connect() ################################################# - # Adding status and statusFoundation here now is just because of having a controll - # over order of execution while we integrating this refactoring architecture - # into the current app state. - # Once we complete refactoring process we will get rid of "status" part/lib. - # - # This to will be adapted to appropriate modules later: - # result.login = login.newController(statusFoundation.status, statusFoundation) - # result.onboarding = onboarding.newController(statusFoundation.status) - # singletonInstance.engine.setRootContextProperty("loginModel", result.login.variant) - # singletonInstance.engine.setRootContextProperty("onboardingModel", result.onboarding.variant) - #result.connect() - proc delete*(self: AppController) = self.osNotificationService.delete self.contactsService.delete @@ -279,7 +262,7 @@ proc delete*(self: AppController) = self.tokenService.delete self.transactionService.delete self.collectibleService.delete - self.settingService.delete + self.settingsService.delete self.walletAccountService.delete self.aboutService.delete self.dappPermissionsService.delete @@ -323,13 +306,12 @@ proc start*(self: AppController) = self.startupModule.load() proc load(self: AppController) = - self.settingService.init() + self.settingsService.init() self.contactsService.init() self.chatService.init() self.communityService.init() self.bookmarkService.init() self.tokenService.init() - self.settingsService.init() self.dappPermissionsService.init() self.ensService.init() self.providerService.init() @@ -337,12 +319,9 @@ proc load(self: AppController) = self.transactionService.init() self.languageService.init() - ################################################# - # Once SettingService gets added, `pubKey` should be fetched from there, instead the following line: - let pubKey = self.settingsService.getPubKey() + let pubKey = self.settingsService.getPublicKey() singletonInstance.localAccountSensitiveSettings.setFileName(pubKey) singletonInstance.engine.setRootContextProperty("localAccountSensitiveSettings", self.localAccountSensitiveSettingsVariant) - ################################################# # other global instances self.buildAndRegisterLocalAccountSensitiveSettings() @@ -361,24 +340,34 @@ proc userLoggedIn*(self: AppController) = self.load() proc buildAndRegisterLocalAccountSensitiveSettings(self: AppController) = - var pubKey = self.settingsService.getPubKey() + var pubKey = self.settingsService.getPublicKey() singletonInstance.localAccountSensitiveSettings.setFileName(pubKey) singletonInstance.engine.setRootContextProperty("localAccountSensitiveSettings", self.localAccountSensitiveSettingsVariant) proc buildAndRegisterUserProfile(self: AppController) = - let loggedInAccount = self.accountsService.getLoggedInAccount() - - let pubKey = self.settingsService.getPubKey() - let sendUserStatus = self.settingsService.getSendUserStatus() + let pubKey = self.settingsService.getPublicKey() + let sendUserStatus = self.settingsService.getSendStatusUpdates() ## This is still not in use. Read a comment in UserProfile. ## let currentUserStatus = self.settingsService.getCurrentUserStatus() - let obj = self.settingsService.getIdentityImage(loggedInAccount.keyUid) + + let loggedInAccount = self.accountsService.getLoggedInAccount() + var thumbnail, large: string + for img in loggedInAccount.images: + if(img.imgType == "large"): + large = img.uri + elif(img.imgType == "thumbnail"): + thumbnail = img.uri + + let meAsContact = self.contactsService.getContactById(pubKey) + var ensName: string + if(meAsContact.ensVerified): + ensName = prettyEnsName(meAsContact.name) singletonInstance.userProfile.setFixedData(loggedInAccount.name, loggedInAccount.keyUid, loggedInAccount.identicon, pubKey) - singletonInstance.userProfile.setEnsName("") # in this moment we don't know ens name - singletonInstance.userProfile.setThumbnailImage(obj.thumbnail) - singletonInstance.userProfile.setLargeImage(obj.large) + singletonInstance.userProfile.setEnsName(ensName) + singletonInstance.userProfile.setThumbnailImage(thumbnail) + singletonInstance.userProfile.setLargeImage(large) singletonInstance.userProfile.setUserStatus(sendUserStatus) singletonInstance.engine.setRootContextProperty("userProfile", self.userProfileVariant) diff --git a/src/app/global/user_profile.nim b/src/app/global/user_profile.nim index d8e4e3b21a..f19429c766 100644 --- a/src/app/global/user_profile.nim +++ b/src/app/global/user_profile.nim @@ -12,7 +12,7 @@ QtObject: thumbnailImage: string largeImage: string userStatus: bool - currentUserStatus: int + #currentUserStatus: int proc setup(self: UserProfile) = self.QObject.setup diff --git a/src/app/modules/main/browser_section/provider/controller.nim b/src/app/modules/main/browser_section/provider/controller.nim index d7519e1750..c173e99f45 100644 --- a/src/app/modules/main/browser_section/provider/controller.nim +++ b/src/app/modules/main/browser_section/provider/controller.nim @@ -35,11 +35,11 @@ method getDappsAddress*(self: Controller): string = return self.settingsService.getDappsAddress() method setDappsAddress*(self: Controller, address: string) = - if self.settingsService.setDappsAddress(address): + if self.settingsService.saveDappsAddress(address): self.delegate.onDappAddressChanged(address) -method getCurrentNetworkDetails*(self: Controller): NetworkDetails = - return self.settingsService.getCurrentNetworkDetails() +method getCurrentNetworkId*(self: Controller): int = + return self.settingsService.getCurrentNetworkId() method disconnect*(self: Controller) = discard self.dappPermissionsService.revoke("web3".toPermission()) diff --git a/src/app/modules/main/browser_section/provider/controller_interface.nim b/src/app/modules/main/browser_section/provider/controller_interface.nim index be427d8d8d..30442d584a 100644 --- a/src/app/modules/main/browser_section/provider/controller_interface.nim +++ b/src/app/modules/main/browser_section/provider/controller_interface.nim @@ -1,5 +1,3 @@ -import ../../../../../app_service/service/settings/service as settings_service - type AccessInterface* {.pure inheritable.} = ref object of RootObj ## Abstract class for any input/interaction with this module. @@ -16,7 +14,7 @@ method getDappsAddress*(self: AccessInterface): string {.base.} = method setDappsAddress*(self: AccessInterface, address: string) {.base.} = raise newException(ValueError, "No implementation available") -method getCurrentNetworkDetails*(self: AccessInterface): NetworkDetails {.base.} = +method getCurrentNetworkId*(self: AccessInterface): int {.base.} = raise newException(ValueError, "No implementation available") method disconnect*(self: AccessInterface) {.base.} = diff --git a/src/app/modules/main/browser_section/provider/module.nim b/src/app/modules/main/browser_section/provider/module.nim index 6603c49c3c..870aab7dbf 100644 --- a/src/app/modules/main/browser_section/provider/module.nim +++ b/src/app/modules/main/browser_section/provider/module.nim @@ -36,7 +36,7 @@ method delete*(self: Module) = method load*(self: Module) = singletonInstance.engine.setRootContextProperty("providerModule", self.viewVariant) self.view.dappsAddress = self.controller.getDappsAddress() - self.view.networkId = self.controller.getCurrentNetworkDetails().config.networkId + self.view.networkId = self.controller.getCurrentNetworkId() self.view.load() method isLoaded*(self: Module): bool = diff --git a/src/app/modules/main/controller.nim b/src/app/modules/main/controller.nim index f988f9d6c1..18312078ab 100644 --- a/src/app/modules/main/controller.nim +++ b/src/app/modules/main/controller.nim @@ -1,4 +1,4 @@ -import item, controller_interface, io_interface +import item, controller_interface, io_interface, chronicles import ../../global/global_singleton import ../../../app_service/service/settings/service_interface as settings_service @@ -12,6 +12,9 @@ import eventemitter export controller_interface +logScope: + topics = "main-module-controller" + type Controller* = ref object of controller_interface.AccessInterface delegate: io_interface.AccessInterface @@ -139,5 +142,7 @@ method getNumOfNotificaitonsForCommunity*(self: Controller, communityId: string) result.mentions += chat.unviewedMentionsCount method setUserStatus*(self: Controller, status: bool) = - self.settingsService.setSendUserStatus(status) - singletonInstance.userProfile.setUserStatus(status) \ No newline at end of file + if(self.settingsService.saveSendStatusUpdates(status)): + singletonInstance.userProfile.setUserStatus(status) + else: + error "error updating user status" \ No newline at end of file diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 94de1f5874..b05eb7ff5c 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -18,14 +18,12 @@ import ../../../app_service/service/token/service as token_service import ../../../app_service/service/transaction/service as transaction_service import ../../../app_service/service/collectible/service as collectible_service import ../../../app_service/service/wallet_account/service as wallet_account_service -import ../../../app_service/service/setting/service as setting_service import ../../../app_service/service/bookmarks/service as bookmark_service import ../../../app_service/service/dapp_permissions/service as dapp_permissions_service import ../../../app_service/service/provider/service as provider_service - import ../../../app_service/service/profile/service as profile_service import ../../../app_service/service/accounts/service as accounts_service -import ../../../app_service/service/settings/service as settings_service +import ../../../app_service/service/settings/service_interface as settings_service import ../../../app_service/service/contacts/service as contacts_service import ../../../app_service/service/about/service as about_service import ../../../app_service/service/language/service as language_service @@ -63,7 +61,6 @@ proc newModule*[T]( collectibleService: collectible_service.Service, walletAccountService: wallet_account_service.Service, bookmarkService: bookmark_service.ServiceInterface, - settingService: setting_service.Service, profileService: profile_service.ServiceInterface, settingsService: settings_service.ServiceInterface, contactsService: contacts_service.Service, @@ -87,7 +84,7 @@ proc newModule*[T]( communityService, messageService) result.communitySectionsModule = initOrderedTable[string, chat_section_module.AccessInterface]() result.walletSectionModule = wallet_section_module.newModule[Module[T]](result, events, tokenService, - transactionService, collectible_service, walletAccountService, settingService) + transactionService, collectible_service, walletAccountService, settingsService) result.browserSectionModule = browser_section_module.newModule(result, bookmarkService, settingsService, dappPermissionsService, providerService) result.profileSectionModule = profile_section_module.newModule(result, events, accountsService, settingsService, diff --git a/src/app/modules/main/profile_section/controller.nim b/src/app/modules/main/profile_section/controller.nim index a8d496d3e2..cc3aad15b7 100644 --- a/src/app/modules/main/profile_section/controller.nim +++ b/src/app/modules/main/profile_section/controller.nim @@ -4,7 +4,7 @@ import controller_interface import ../../../../app_service/service/profile/service as profile_service import ../../../../app_service/service/accounts/service as accounts_service -import ../../../../app_service/service/settings/service as settings_service +import ../../../../app_service/service/settings/service_interface as settings_service import ../../../../app_service/service/language/service as language_service import ../../../../app_service/service/mnemonic/service as mnemonic_service import ../../../../app_service/service/privacy/service as privacy_service @@ -22,7 +22,10 @@ type mnemonicService: mnemonic_service.ServiceInterface privacyService: privacy_service.ServiceInterface -proc newController*[T](delegate: T, accountsService: accounts_service.ServiceInterface, settingsService: settings_service.ServiceInterface, profileService: profile_service.ServiceInterface, languageService: language_service.ServiceInterface, mnemonicService: mnemonic_service.ServiceInterface, privacyService: privacy_service.ServiceInterface): Controller[T] = +proc newController*[T](delegate: T, accountsService: accounts_service.ServiceInterface, + settingsService: settings_service.ServiceInterface, profileService: profile_service.ServiceInterface, + languageService: language_service.ServiceInterface, mnemonicService: mnemonic_service.ServiceInterface, + privacyService: privacy_service.ServiceInterface): Controller[T] = result = Controller[T]() result.delegate = delegate result.profileService = profileService @@ -42,10 +45,14 @@ method enableDeveloperFeatures*[T](self: Controller[T]) = self.settingsService.enableDeveloperFeatures() method toggleTelemetry*[T](self: Controller[T]) = - self.settingsService.toggleTelemetry() + var value = "" + if(not self.isTelemetryEnabled()): + value = DEFAULT_TELEMETRY_SERVER_URL + + discard self.settingsService.saveTelemetryServerUrl(value) method isTelemetryEnabled*[T](self: Controller[T]): bool = - return self.settingsService.isTelemetryEnabled() + return self.settingsService.getTelemetryServerUrl().len > 0 method toggleAutoMessage*[T](self: Controller[T]) = self.settingsService.toggleAutoMessage() @@ -54,7 +61,11 @@ method isAutoMessageEnabled*[T](self: Controller[T]): bool = return self.settingsService.isAutoMessageEnabled() method toggleDebug*[T](self: Controller[T]) = - self.settingsService.toggleDebug() + discard + # Need to sort out this + #self.settingsService.toggleDebug() method isDebugEnabled*[T](self: Controller[T]): bool = - return self.settingsService.isDebugEnabled() + return true + # Need to sort out this + #return self.settingsService.isDebugEnabled() diff --git a/src/app/modules/main/profile_section/module.nim b/src/app/modules/main/profile_section/module.nim index 63b6e8220b..6e85f9464b 100644 --- a/src/app/modules/main/profile_section/module.nim +++ b/src/app/modules/main/profile_section/module.nim @@ -4,7 +4,7 @@ import ../../../global/global_singleton import ../../../../app_service/service/profile/service as profile_service import ../../../../app_service/service/accounts/service as accounts_service -import ../../../../app_service/service/settings/service as settings_service +import ../../../../app_service/service/settings/service_interface as settings_service import ../../../../app_service/service/contacts/service as contacts_service import ../../../../app_service/service/about/service as about_service import ../../../../app_service/service/language/service as language_service diff --git a/src/app/modules/main/profile_section/profile/controller.nim b/src/app/modules/main/profile_section/profile/controller.nim index b6c76b98b8..320f1633ae 100644 --- a/src/app/modules/main/profile_section/profile/controller.nim +++ b/src/app/modules/main/profile_section/profile/controller.nim @@ -3,7 +3,7 @@ import ./controller_interface import ../../../../global/global_singleton import ../../../../../app_service/service/profile/service as profile_service import ../../../../../app_service/service/accounts/service as accounts_service -import ../../../../../app_service/service/settings/service as settings_service +import ../../../../../app_service/service/settings/service_interface as settings_service import ./item as item import status/types/identity_image @@ -17,7 +17,8 @@ type settingsService: settings_service.ServiceInterface accountsService: accounts_service.ServiceInterface -proc newController*[T](delegate: T, accountsService: accounts_service.ServiceInterface, settingsService: settings_service.ServiceInterface, profileService: profile_service.ServiceInterface): Controller[T] = +proc newController*[T](delegate: T, accountsService: accounts_service.ServiceInterface, + settingsService: settings_service.ServiceInterface, profileService: profile_service.ServiceInterface): Controller[T] = result = Controller[T]() result.delegate = delegate result.profileService = profileService @@ -32,7 +33,6 @@ method init*[T](self: Controller[T]) = method getProfile*[T](self: Controller[T]): item.Item = - var network = self.settingsService.getNetwork() var appearance = self.settingsService.getAppearance() var messagesFromContactsOnly = self.settingsService.getMessagesFromContactsOnly() diff --git a/src/app/modules/main/profile_section/profile/module.nim b/src/app/modules/main/profile_section/profile/module.nim index 86540576b4..8db45ec071 100644 --- a/src/app/modules/main/profile_section/profile/module.nim +++ b/src/app/modules/main/profile_section/profile/module.nim @@ -5,7 +5,7 @@ import ../../../../global/global_singleton import ../../../../../app_service/service/profile/service as profile_service import ../../../../../app_service/service/accounts/service as accounts_service -import ../../../../../app_service/service/settings/service as settings_service +import ../../../../../app_service/service/settings/service_interface as settings_service import status/types/identity_image @@ -19,7 +19,8 @@ type viewVariant: QVariant moduleLoaded: bool -proc newModule*[T](delegate: T, accountsService: accounts_service.ServiceInterface, settingsService: settings_service.ServiceInterface, profileService: profile_service.ServiceInterface): Module[T] = +proc newModule*[T](delegate: T, accountsService: accounts_service.ServiceInterface, + settingsService: settings_service.ServiceInterface, profileService: profile_service.ServiceInterface): Module[T] = result = Module[T]() result.delegate = delegate result.view = newView(result) diff --git a/src/app/modules/main/wallet_section/controller.nim b/src/app/modules/main/wallet_section/controller.nim index 828fb5aa5f..4623c9d02a 100644 --- a/src/app/modules/main/wallet_section/controller.nim +++ b/src/app/modules/main/wallet_section/controller.nim @@ -1,5 +1,5 @@ import ./controller_interface -import ../../../../app_service/service/setting/service as setting_service +import ../../../../app_service/service/settings/service_interface as settings_service import ../../../../app_service/service/wallet_account/service as wallet_account_service export controller_interface @@ -7,17 +7,17 @@ export controller_interface type Controller*[T: controller_interface.DelegateInterface] = ref object of controller_interface.AccessInterface delegate: T - settingService: setting_service.ServiceInterface + settingsService: settings_service.ServiceInterface walletAccountService: wallet_account_service.ServiceInterface proc newController*[T]( delegate: T, - settingService: setting_service.ServiceInterface, + settingsService: settings_service.ServiceInterface, walletAccountService: wallet_account_service.ServiceInterface, ): Controller[T] = result = Controller[T]() result.delegate = delegate - result.settingService = settingService + result.settingsService = settingsService result.walletAccountService = walletAccountService method delete*[T](self: Controller[T]) = @@ -26,8 +26,14 @@ method delete*[T](self: Controller[T]) = method init*[T](self: Controller[T]) = discard -method getSetting*[T](self: Controller[T]): setting_service.SettingDto = - return self.settingService.getSetting() +method getCurrency*[T](self: Controller[T]): string = + return self.settingsService.getCurrency() + +method getSigningPhrase*[T](self: Controller[T]): string = + return self.settingsService.getSigningPhrase() + +method isMnemonicBackedUp*[T](self: Controller[T]): bool = + return self.settingsService.getMnemonic().len > 0 method getCurrencyBalance*[T](self: Controller[T]): float64 = return self.walletAccountService.getCurrencyBalance() diff --git a/src/app/modules/main/wallet_section/controller_interface.nim b/src/app/modules/main/wallet_section/controller_interface.nim index 98c209a08b..805464e3df 100644 --- a/src/app/modules/main/wallet_section/controller_interface.nim +++ b/src/app/modules/main/wallet_section/controller_interface.nim @@ -1,5 +1,3 @@ -import ../../../../app_service/service/setting/service_interface as setting_service - type AccessInterface* {.pure inheritable.} = ref object of RootObj ## Abstract class for any input/interaction with this module. @@ -10,7 +8,13 @@ method delete*(self: AccessInterface) {.base.} = method init*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method getSetting*(self: AccessInterface): setting_service.SettingDto {.base.} = +method getCurrency*(self: AccessInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method getSigningPhrase*(self: AccessInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method isMnemonicBackedUp*(self: AccessInterface): bool {.base.} = raise newException(ValueError, "No implementation available") method getCurrencyBalance*(self: AccessInterface): float64 {.base.} = diff --git a/src/app/modules/main/wallet_section/module.nim b/src/app/modules/main/wallet_section/module.nim index 1b0dab96bd..03f03a7722 100644 --- a/src/app/modules/main/wallet_section/module.nim +++ b/src/app/modules/main/wallet_section/module.nim @@ -17,7 +17,7 @@ import ../../../../app_service/service/token/service as token_service import ../../../../app_service/service/transaction/service as transaction_service import ../../../../app_service/service/collectible/service as collectible_service import ../../../../app_service/service/wallet_account/service as wallet_account_service -import ../../../../app_service/service/setting/service as setting_service +import ../../../../app_service/service/settings/service_interface as settings_service import io_interface export io_interface @@ -44,13 +44,13 @@ proc newModule*[T]( transactionService: transaction_service.Service, collectibleService: collectible_service.ServiceInterface, walletAccountService: wallet_account_service.ServiceInterface, - settingService: setting_service.ServiceInterface + settingsService: settings_service.ServiceInterface ): Module[T] = result = Module[T]() result.delegate = delegate result.events = events result.moduleLoaded = false - result.controller = newController(result, settingService, walletAccountService) + result.controller = newController(result, settingsService, walletAccountService) result.view = newView(result) result.accountTokensModule = account_tokens_module.newModule[Module[T]](result, events, walletAccountService) @@ -102,8 +102,10 @@ method load*[T](self: Module[T]) = self.transactionsModule.load() self.switchAccount(0) - let setting = self.controller.getSetting() - self.view.updateFromSetting(setting) + let currency = self.controller.getCurrency() + let signingPhrase = self.controller.getSigningPhrase() + let mnemonicBackedUp = self.controller.isMnemonicBackedUp() + self.view.setData(currency, signingPhrase, mnemonicBackedUp) self.setTotalCurrencyBalance() self.moduleLoaded = true self.delegate.walletSectionDidLoad() diff --git a/src/app/modules/main/wallet_section/view.nim b/src/app/modules/main/wallet_section/view.nim index b97363acf8..1ce4dc1962 100644 --- a/src/app/modules/main/wallet_section/view.nim +++ b/src/app/modules/main/wallet_section/view.nim @@ -1,6 +1,5 @@ import NimQml -import ../../../../app_service/service/setting/service as setting_service import ./io_interface QtObject: @@ -65,8 +64,8 @@ QtObject: self.totalCurrencyBalance = totalCurrencyBalance self.totalCurrencyBalanceChanged() - proc updateFromSetting*(self: View, setting: setting_service.SettingDto) = - self.currentCurrency = setting.currency - self.currentCurrencyChanged() - self.signingPhrase = setting.signingPhrase - self.isMnemonicBackedUp = setting.isMnemonicBackedUp \ No newline at end of file + proc setData*(self: View, currency, signingPhrase: string, mnemonicBackedUp: bool) = + self.currentCurrency = currency + self.signingPhrase = signingPhrase + self.isMnemonicBackedUp = mnemonicBackedUp + self.currentCurrencyChanged() \ No newline at end of file diff --git a/src/app_service/service/collectible/service.nim b/src/app_service/service/collectible/service.nim index 0bf8131dd0..cc1527d415 100644 --- a/src/app_service/service/collectible/service.nim +++ b/src/app_service/service/collectible/service.nim @@ -1,7 +1,7 @@ import chronicles, sequtils, json import ./service_interface, ./dto -import ../setting/service as setting_service +import ../settings/service_interface as settings_service import status/statusgo_backend_new/collectibles as collectibles @@ -14,21 +14,21 @@ logScope: type Service* = ref object of service_interface.ServiceInterface - settingService: setting_service.ServiceInterface + settingsService: settings_service.ServiceInterface method delete*(self: Service) = discard -proc newService*(settingService: setting_service.ServiceInterface): Service = +proc newService*(settingsService: settings_service.ServiceInterface): Service = result = Service() - result.settingService = settingService + result.settingsService = settingsService method init*(self: Service) = discard method getCollections(self: Service, address: string): seq[CollectionDto] = try: - let networkId = self.settingService.getSetting().currentNetwork.id + let networkId = self.settingsService.getCurrentNetworkId() let response = collectibles.getOpenseaCollections(networkId, address) return map(response.result.getElems(), proc(x: JsonNode): CollectionDto = x.toCollectionDto()) except Exception as e: @@ -38,7 +38,7 @@ method getCollections(self: Service, address: string): seq[CollectionDto] = method getCollectibles(self: Service, address: string, collectionSlug: string): seq[CollectibleDto] = try: - let networkId = self.settingService.getSetting().currentNetwork.id + let networkId = self.settingsService.getCurrentNetworkId() let response = collectibles.getOpenseaAssets(networkId, address, collectionSlug, limit) return map(response.result.getElems(), proc(x: JsonNode): CollectibleDto = x.toCollectibleDto()) except Exception as e: diff --git a/src/app_service/service/provider/service.nim b/src/app_service/service/provider/service.nim index 7d2d5406f4..9cef6b3e53 100644 --- a/src/app_service/service/provider/service.nim +++ b/src/app_service/service/provider/service.nim @@ -4,13 +4,14 @@ import options import strutils include ../../common/json_utils import ../dapp_permissions/service as dapp_permissions_service -import ../settings/service as settings_service +import ../settings/service_interface as settings_service import ../ens/service as ens_service import service_interface import status/statusgo_backend_new/permissions as status_go_permissions import status/statusgo_backend_new/accounts as status_go_accounts import status/statusgo_backend_new/core as status_go_core -import status/statusgo_backend_new/provider as status_go_provider +from stew/base32 import nil +from stew/base58 import nil import stew/byteutils export service_interface @@ -18,6 +19,74 @@ logScope: topics = "provider-service" const HTTPS_SCHEME* = "https" +const IPFS_GATEWAY* = ".infura.status.im" +const SWARM_GATEWAY* = "swarm-gateways.net" + +type + RequestTypes {.pure.} = enum + Web3SendAsyncReadOnly = "web3-send-async-read-only", + HistoryStateChanged = "history-state-changed", + APIRequest = "api-request" + Unknown = "unknown" + + ResponseTypes {.pure.} = enum + Web3SendAsyncCallback = "web3-send-async-callback", + APIResponse = "api-response", + Web3ResponseError = "web3-response-error" + +type + Payload = ref object + id: JsonNode + rpcMethod: string + + Web3SendAsyncReadOnly = ref object + messageId: JsonNode + payload: Payload + request: string + hostname: string + + APIRequest = ref object + isAllowed: bool + messageId: JsonNode + permission: Permission + hostname: string + +const AUTH_METHODS = toHashSet(["eth_accounts", "eth_coinbase", "eth_sendTransaction", "eth_sign", "keycard_signTypedData", "eth_signTypedData", "eth_signTypedData_v3", "personal_sign", "personal_ecRecover"]) +const SIGN_METHODS = toHashSet(["eth_sign", "personal_sign", "eth_signTypedData", "eth_signTypedData_v3"]) +const ACC_METHODS = toHashSet(["eth_accounts", "eth_coinbase"]) + +proc requestType(message: string): RequestTypes = + let data = message.parseJson + result = RequestTypes.Unknown + try: + result = parseEnum[RequestTypes](data["type"].getStr()) + except: + warn "Unknown request type received", value=data["permission"].getStr() + + +proc toWeb3SendAsyncReadOnly(message: string): Web3SendAsyncReadOnly = + let data = message.parseJson + result = Web3SendAsyncReadOnly( + messageId: data["messageId"], + request: $data["payload"], + hostname: data{"hostname"}.getStr(), + payload: Payload( + id: data["payload"]{"id"}, + rpcMethod: data["payload"]["method"].getStr() + ) + ) + +proc toAPIRequest(message: string): APIRequest = + let data = message.parseJson + + result = APIRequest( + messageId: data["messageId"], + isAllowed: data{"isAllowed"}.getBool(), + permission: data["permission"].getStr().toPermission(), + hostname: data{"hostname"}.getStr() + ) + + type Service* = ref object of service_interface.ServiceInterface @@ -39,18 +108,217 @@ proc newService*(dappPermissionsService: dapp_permissions_service.ServiceInterfa method init*(self: Service) = discard +proc process(self: Service, data: Web3SendAsyncReadOnly): string = + if AUTH_METHODS.contains(data.payload.rpcMethod) and not self.dappPermissionsService.hasPermission(data.hostname, Permission.Web3): + return $ %* { + "type": ResponseTypes.Web3SendAsyncCallback, + "messageId": data.messageId, + "error": { + "code": 4100 + } + } -method ensResourceURL*(self: Service, username: string, url: string): (string, string, string, string, bool) = - let (scheme, host, path) = self.ensService.resourceUrl(username) - if host == "": + if data.payload.rpcMethod == "eth_sendTransaction": + try: + let request = data.request.parseJson + let fromAddress = request["params"][0]["from"].getStr() + let to = request["params"][0]{"to"}.getStr() + let value = if (request["params"][0]["value"] != nil): + request["params"][0]["value"].getStr() + else: + "0" + let password = request["password"].getStr() + let selectedGasLimit = request["selectedGasLimit"].getStr() + let selectedGasPrice = request["selectedGasPrice"].getStr() + let selectedTipLimit = request{"selectedTipLimit"}.getStr() + let selectedOverallLimit = request{"selectedOverallLimit"}.getStr() + let txData = if (request["params"][0].hasKey("data") and request["params"][0]["data"].kind != JNull): + request["params"][0]["data"].getStr() + else: + "" + + var success: bool + var errorMessage = "" + var response = "" + var validInput: bool = true + + + # TODO: use the transaction service to send the trx + + #[ + let eip1559Enabled = self.wallet.isEIP1559Enabled() + + try: + validateTransactionInput(fromAddress, to, "", value, selectedGasLimit, selectedGasPrice, txData, eip1559Enabled, selectedTipLimit, selectedOverallLimit, "dummy") + except Exception as e: + validInput = false + success = false + errorMessage = e.msg + + if validInput: + # TODO make this async + response = wallet.sendTransaction(fromAddress, to, value, selectedGasLimit, selectedGasPrice, eip1559Enabled, selectedTipLimit, selectedOverallLimit, password, success, txData) + errorMessage = if not success: + if response == "": + "web3-response-error" + else: + response + else: + "" + + return $ %* { + "type": ResponseTypes.Web3SendAsyncCallback, + "messageId": data.messageId, + "error": errorMessage, + "result": { + "jsonrpc": "2.0", + "id": data.payload.id, + "result": if (success): response else: "" + } + } + + ]# + # TODO: delete this: + return $ %* { + "type": ResponseTypes.Web3SendAsyncCallback, + "messageId": data.messageId, + "error": "", + "result": { + "jsonrpc": "2.0", + "id": data.payload.id, + "result": "" + } + } + + + except Exception as e: + error "Error sending the transaction", msg = e.msg + return $ %* { + "type": ResponseTypes.Web3SendAsyncCallback, + "messageId": data.messageId, + "error": { + "code": 4100, + "message": e.msg + } + } + + if SIGN_METHODS.contains(data.payload.rpcMethod): + try: + let request = data.request.parseJson + var params = request["params"] + let password = hashPassword(request["password"].getStr()) + let dappAddress = self.settingsService.getDappsAddress() + var rpcResult = "{}" + + case data.payload.rpcMethod: + of "eth_signTypedData", "eth_signTypedData_v3": + rpcResult = signTypedData(params[1].getStr(), dappAddress, password) + else: + rpcResult = signMessage($ %* { + "data": params[0].getStr(), + "password": password, + "account": dappAddress + }) + + let jsonRpcResult = rpcResult.parseJson + let success: bool = not jsonRpcResult.hasKey("error") + let errorMessage = if success: "" else: jsonRpcResult["error"]{"message"}.getStr() + let response = if success: jsonRpcResult["result"].getStr() else: "" + + return $ %* { + "type": ResponseTypes.Web3SendAsyncCallback, + "messageId": data.messageId, + "error": errorMessage, + "result": { + "jsonrpc": "2.0", + "id": if data.payload.id == nil: newJNull() else: data.payload.id, + "result": if (success): response else: "" + } + } + + except Exception as e: + error "Error signing message", msg = e.msg + return $ %* { + "type": ResponseTypes.Web3SendAsyncCallback, + "messageId": data.messageId, + "error": { + "code": 4100, + "message": e.msg + } + } + + + + if ACC_METHODS.contains(data.payload.rpcMethod): + let dappAddress = self.settingsService.getDappsAddress() + return $ %* { + "type": ResponseTypes.Web3SendAsyncCallback, + "messageId": data.messageId, + "result": { + "jsonrpc": "2.0", + "id": data.payload.id, + "result": if data.payload.rpcMethod == "eth_coinbase": newJString(dappAddress) else: %*[dappAddress] + } + } + + let rpcResult = callRPC(data.request) + + return $ %* { + "type": ResponseTypes.Web3SendAsyncCallback, + "messageId": data.messageId, + "error": (if rpcResult == "": newJString("web3-response-error") else: newJNull()), + "result": rpcResult.parseJson + } + +proc process(self: Service, data: APIRequest): string = + var value:JsonNode = case data.permission + of Permission.Web3: %* [self.settingsService.getDappsAddress()] + of Permission.ContactCode: %* self.settingsService.getPublicKey() + of Permission.Unknown: newJNull() + + let isAllowed = data.isAllowed and data.permission != Permission.Unknown + + info "API request received", host=data.hostname, value=data.permission, isAllowed + + if isAllowed: + discard self.dappPermissionsService.addPermission(data.hostname, data.permission) + + return $ %* { + "type": ResponseTypes.APIResponse, + "isAllowed": isAllowed, + "permission": data.permission, + "messageId": data.messageId, + "data": value + } + +method postMessage*(self: Service, message: string): string = + case message.requestType(): + of RequestTypes.Web3SendAsyncReadOnly: self.process(message.toWeb3SendAsyncReadOnly()) + of RequestTypes.HistoryStateChanged: """{"type":"TODO-IMPLEMENT-THIS"}""" ############# TODO: + of RequestTypes.APIRequest: self.process(message.toAPIRequest()) + else: """{"type":"TODO-IMPLEMENT-THIS"}""" ##################### TODO: + +method ensResourceURL*(self: Service, ens: string, url: string): (string, string, string, string, bool) = + let contentHash = self.ensService.getContentHash(ens) + if contentHash.isNone(): # ENS does not have a content hash return (url, url, HTTPS_SCHEME, "", false) - return (url, host, scheme, path, true) + let decodedHash = self.ensService.decodeENSContentHash(contentHash.get()) -method postMessage*(self: Service, requestType: string, message: string): string = - try: - return $providerRequest(requestType, message).result - except Exception as e: - let errDescription = e.msg - error "error: ", errDescription - \ No newline at end of file + case decodedHash[0]: + of ENSType.IPFS: + let + base58bytes = base58.decode(base58.BTCBase58, decodedHash[1]) + base32Hash = base32.encode(base32.Base32Lower, base58bytes) + + result = (url, base32Hash & IPFS_GATEWAY, HTTPS_SCHEME, "", true) + + of ENSType.SWARM: + result = (url, SWARM_GATEWAY, HTTPS_SCHEME, + "/bzz:/" & decodedHash[1] & "/", true) + + of ENSType.IPNS: + result = (url, decodedHash[1], HTTPS_SCHEME, "", true) + + else: + warn "Unknown content for", ens, contentHash diff --git a/src/app_service/service/setting/dto.nim b/src/app_service/service/setting/dto.nim deleted file mode 100644 index 4e4b9822f3..0000000000 --- a/src/app_service/service/setting/dto.nim +++ /dev/null @@ -1,63 +0,0 @@ -import json, options - -include ../../common/json_utils - -const DEFAULT_NETWORK_SLUG = "mainnet_rpc" -const DEFAULT_CURRENCY = "usd" - -type NetworkDto* = ref object of RootObj - id*: int - slug*: string - etherscanLink*: string - name*: string - -type - SettingDto* = ref object of RootObj - currentNetwork*: NetworkDto - activeTokenSymbols*: seq[string] - rawActiveTokenSymbols*: JsonNode - signingPhrase*: string - currency*: string - mnemonic*: string - walletRootAddress*: string - latestDerivedPath*: int - -proc toSettingDto*(jsonObj: JsonNode): SettingDto = - result = SettingDto() - - discard jsonObj.getProp("signing-phrase", result.signingPhrase) - discard jsonObj.getProp("wallet-root-address", result.walletRootAddress) - discard jsonObj.getProp("latest-derived-path", result.latestDerivedPath) - discard jsonObj.getProp("mnemonic", result.mnemonic) - - if not jsonObj.getProp("currency", result.currency): - result.currency = DEFAULT_CURRENCY - - var currentNetworkSlug: string - if not jsonObj.getProp("networks/current-network", currentNetworkSlug): - currentNetworkSlug = DEFAULT_NETWORK_SLUG - - var networks: JsonNode - discard jsonObj.getProp("networks/networks", networks) - for networkJson in networks.getElems(): - if networkJson{"id"}.getStr != currentNetworkSlug: - continue - - var networkDto = NetworkDto() - discard networkJson{"config"}.getProp("NetworkId", networkDto.id) - discard networkJson.getProp("id", networkDto.slug) - discard networkJson.getProp("name", networkDto.name) - discard networkJson.getProp("etherscan-link", networkDto.etherscanLink) - result.currentNetwork = networkDto - break - - result.rawActiveTokenSymbols = newJObject() - result.activeTokenSymbols = @[] - if jsonObj.hasKey("wallet/visible-tokens"): - result.rawActiveTokenSymbols = parseJson(jsonObj{"wallet/visible-tokens"}.getStr) - - for symbol in result.rawActiveTokenSymbols{$result.currentNetwork.id}.getElems(): - result.activeTokenSymbols.add(symbol.getStr) - -proc isMnemonicBackedUp*(self: SettingDto): bool = - return self.mnemonic == "" \ No newline at end of file diff --git a/src/app_service/service/setting/service.nim b/src/app_service/service/setting/service.nim deleted file mode 100644 index 3a3b2db389..0000000000 --- a/src/app_service/service/setting/service.nim +++ /dev/null @@ -1,51 +0,0 @@ -import chronicles, json - -import ./service_interface, ./dto -import status/statusgo_backend_new/settings as status_go - -export service_interface - -logScope: - topics = "setting-service" - -type - Service* = ref object of service_interface.ServiceInterface - setting: SettingDto - -method delete*(self: Service) = - discard - -proc newService*(): Service = - result = Service() - -method init*(self: Service) = - try: - let response = status_go.getSettings() - self.setting = response.result.toSettingDto() - except Exception as e: - let errDesription = e.msg - error "error: ", errDesription - return - -method saveSetting*( - self: Service, attribute: string, value: string | JsonNode | bool | int | seq[string] -): SettingDto = - case attribute: - of "latest-derived-path": - self.setting.latestDerivedPath = cast[int](value) - status_go.saveSettings(attribute, self.setting.latestDerivedPath) - of "currency": - self.setting.currency = cast[string](value) - status_go.saveSettings(attribute, self.setting.currency) - of "wallet/visible-tokens": - let newValue = cast[seq[string]](value) - self.setting.activeTokenSymbols = newValue - self.setting.rawActiveTokenSymbols[$self.setting.currentNetwork.id] = newJArray() - self.setting.rawActiveTokenSymbols[$self.setting.currentNetwork.id] = %* newValue - - status_go.saveSettings(attribute, $self.setting.rawActiveTokenSymbols) - - return self.setting - -method getSetting*(self: Service): SettingDto = - return self.setting \ No newline at end of file diff --git a/src/app_service/service/setting/service_interface.nim b/src/app_service/service/setting/service_interface.nim deleted file mode 100644 index 8eea77f357..0000000000 --- a/src/app_service/service/setting/service_interface.nim +++ /dev/null @@ -1,22 +0,0 @@ -import json -import ./dto - -export dto - -type - ServiceInterface* {.pure inheritable.} = ref object of RootObj - ## Abstract class for this service access. - -method delete*(self: ServiceInterface) {.base.} = - raise newException(ValueError, "No implementation available") - -method init*(self: ServiceInterface) {.base.} = - raise newException(ValueError, "No implementation available") - -method getSetting*(self: ServiceInterface): SettingDto {.base.} = - raise newException(ValueError, "No implementation available") - -method saveSetting*( - self: ServiceInterface, attribute: string, value: string | JsonNode | bool | int | seq[string] -): SettingDto {.base.} = - raise newException(ValueError, "No implementation available") diff --git a/src/app_service/service/settings/dto.nim b/src/app_service/service/settings/dto.nim deleted file mode 100644 index 8e2b93abf6..0000000000 --- a/src/app_service/service/settings/dto.nim +++ /dev/null @@ -1,5 +0,0 @@ - -type - IdentityImage* = ref object - thumbnail*: string - large*: string diff --git a/src/app_service/service/settings/dto/network_details.nim b/src/app_service/service/settings/dto/network_details.nim deleted file mode 100644 index fcb264ec4b..0000000000 --- a/src/app_service/service/settings/dto/network_details.nim +++ /dev/null @@ -1,12 +0,0 @@ -{.used.} - -import json_serialization - -import node_config - -type - NetworkDetails* = object - id*: string - name*: string - etherscanLink* {.serializedFieldName("etherscan-link").}: string - config*: NodeConfig diff --git a/src/app_service/service/settings/dto/node_config.nim b/src/app_service/service/settings/dto/node_config.nim deleted file mode 100644 index 0c54dfbb9c..0000000000 --- a/src/app_service/service/settings/dto/node_config.nim +++ /dev/null @@ -1,9 +0,0 @@ -import json_serialization - -import upstream_config - -type - NodeConfig* = object - networkId* {.serializedFieldName("NetworkId").}: int - dataDir* {.serializedFieldName("DataDir").}: string - upstreamConfig* {.serializedFieldName("UpstreamConfig").}: UpstreamConfig \ No newline at end of file diff --git a/src/app_service/service/settings/dto/settings.nim b/src/app_service/service/settings/dto/settings.nim new file mode 100644 index 0000000000..0884445831 --- /dev/null +++ b/src/app_service/service/settings/dto/settings.nim @@ -0,0 +1,147 @@ +import json, options + +include ../../../common/json_utils + +type UpstreamConfig* = object + enabled*: bool + url*: string + +type Config* = object + networkId*: int + dataDir*: string + upstreamConfig*: UpstreamConfig + +type Network* = object + id*: string + etherscanLink*: string + name*: string + config*: Config + +type PinnedMailservers* = object + ethProd*: string + +type CurrentUserStatus* = object + statusType*: int + clock*: int64 + text*: string + +type WalletVisibleTokens* = object + tokens*: seq[string] + +type + SettingsDto* = object # There is no point to keep all these info as settings, but we must follow status-go response + address*: string + currency*: string + currentNetwork*: string + availableNetworks*: seq[Network] + dappsAddress*: string + eip1581Address*: string + installationId*: string + keyUid*: string + latestDerivedPath*: int + linkPreviewRequestEnabled*: bool + messagesFromContactsOnly*: bool + mnemonic*: string + name*: string # user alias + photoPath*: string + pinnedMailservers*: PinnedMailservers + previewPrivacy*: bool + publicKey*: string + signingPhrase*: string + defaultSyncPeriod*: int + sendPushNotifications*: bool + appearance*: int + profilePicturesShowTo*: int + profilePicturesVisibility*: int + useMailservers*: bool + walletRootAddress*: string + sendStatusUpdates*: bool + telemetryServerUrl*: string + fleet*: string + currentUserStatus*: CurrentUserStatus + walletVisibleTokens*: WalletVisibleTokens + +proc toUpstreamConfig*(jsonObj: JsonNode): UpstreamConfig = + discard jsonObj.getProp("Enabled", result.enabled) + discard jsonObj.getProp("URL", result.url) + +proc toConfig*(jsonObj: JsonNode): Config = + discard jsonObj.getProp("NetworkId", result.networkId) + discard jsonObj.getProp("DataDir", result.dataDir) + + var upstreamConfigObj: JsonNode + if(jsonObj.getProp("UpstreamConfig", upstreamConfigObj)): + result.upstreamConfig = toUpstreamConfig(upstreamConfigObj) + +proc toNetwork*(jsonObj: JsonNode): Network = + discard jsonObj.getProp("id", result.id) + discard jsonObj.getProp("etherscan-link", result.etherscanLink) + discard jsonObj.getProp("name", result.name) + + var configObj: JsonNode + if(jsonObj.getProp("config", configObj)): + result.config = toConfig(configObj) + +proc toPinnedMailservers*(jsonObj: JsonNode): PinnedMailservers = + discard jsonObj.getProp("eth.prod", result.ethProd) + +proc toCurrentUserStatus*(jsonObj: JsonNode): CurrentUserStatus = + discard jsonObj.getProp("statusType", result.statusType) + discard jsonObj.getProp("clock", result.clock) + discard jsonObj.getProp("text", result.text) + +proc toWalletVisibleTokens*(jsonObj: JsonNode, networkId: string): WalletVisibleTokens = + for netId, tokenArr in jsonObj: + if(netId != networkId or tokenArr.kind != JArray): + continue + + for token in tokenArr: + result.tokens.add(token.getStr) + +proc toSettingsDto*(jsonObj: JsonNode): SettingsDto = + + discard jsonObj.getProp("address", result.address) + discard jsonObj.getProp("currency", result.currency) + discard jsonObj.getProp("networks/current-network", result.currentNetwork) + + var networksArr: JsonNode + if(jsonObj.getProp("networks/networks", networksArr)): + if(networksArr.kind == JArray): + for networkObj in networksArr: + result.availableNetworks.add(toNetwork(networkObj)) + + discard jsonObj.getProp("dapps-address", result.dappsAddress) + discard jsonObj.getProp("eip1581-address", result.eip1581Address) + discard jsonObj.getProp("installation-id", result.installationId) + discard jsonObj.getProp("key-uid", result.keyUid) + discard jsonObj.getProp("latest-derived-path", result.latestDerivedPath) + discard jsonObj.getProp("link-preview-request-enabled", result.linkPreviewRequestEnabled) + discard jsonObj.getProp("messages-from-contacts-only", result.messagesFromContactsOnly) + discard jsonObj.getProp("mnemonic", result.mnemonic) + discard jsonObj.getProp("name", result.name) + discard jsonObj.getProp("photo-path", result.photoPath) + discard jsonObj.getProp("preview-privacy?", result.previewPrivacy) + discard jsonObj.getProp("public-key", result.publicKey) + discard jsonObj.getProp("signing-phrase", result.signingPhrase) + discard jsonObj.getProp("default-sync-period", result.defaultSyncPeriod) + discard jsonObj.getProp("send-push-notifications?", result.sendPushNotifications) + discard jsonObj.getProp("appearance", result.appearance) + discard jsonObj.getProp("profile-pictures-show-to", result.profilePicturesShowTo) + discard jsonObj.getProp("profile-pictures-visibility", result.profilePicturesVisibility) + discard jsonObj.getProp("use-mailservers?", result.useMailservers) + discard jsonObj.getProp("wallet-root-address", result.walletRootAddress) + discard jsonObj.getProp("send-status-updates?", result.sendStatusUpdates) + discard jsonObj.getProp("telemetry-server-url", result.telemetryServerUrl) + discard jsonObj.getProp("fleet", result.fleet) + + var pinnedMailserversObj: JsonNode + if(jsonObj.getProp("pinned-mailservers", pinnedMailserversObj)): + result.pinnedMailservers = toPinnedMailservers(pinnedMailserversObj) + + var currentUserStatusObj: JsonNode + if(jsonObj.getProp("current-user-status", currentUserStatusObj)): + result.currentUserStatus = toCurrentUserStatus(currentUserStatusObj) + + var walletVisibleTokensObj: JsonNode + if(jsonObj.getProp("wallet/visible-tokens", walletVisibleTokensObj)): + result.walletVisibleTokens = toWalletVisibleTokens(walletVisibleTokensObj, result.currentNetwork) diff --git a/src/app_service/service/settings/dto/upstream_config.nim b/src/app_service/service/settings/dto/upstream_config.nim deleted file mode 100644 index 26e7169eb5..0000000000 --- a/src/app_service/service/settings/dto/upstream_config.nim +++ /dev/null @@ -1,6 +0,0 @@ -import json_serialization - -type - UpstreamConfig* = object - enabled* {.serializedFieldName("Enabled").}: bool - url* {.serializedFieldName("URL").}: string \ No newline at end of file diff --git a/src/app_service/service/settings/service.nim b/src/app_service/service/settings/service.nim index ee1eb569a8..e6b5ec7dd1 100644 --- a/src/app_service/service/settings/service.nim +++ b/src/app_service/service/settings/service.nim @@ -1,28 +1,45 @@ -import json, json_serialization, sugar, sequtils, chronicles -# import status/statusgo_backend_new/custom_tokens as custom_tokens -import json, tables, sugar, sequtils, strutils, atomics, os +import chronicles, json -import status/statusgo_backend/settings as status_go_settings -import status/statusgo_backend/accounts as status_accounts -from status/types/setting import Setting - -import ./service_interface, ./dto - -import dto/network_details -import dto/node_config -import dto/upstream_config +import service_interface, ./dto/settings +import status/statusgo_backend_new/settings as status_go export service_interface logScope: topics = "settings-service" -const DEFAULT_NETWORK_NAME = "mainnet_rpc" -const TELEMETRY_BASE_URL = "https://telemetry.status.im" +# Setting keys: +const KEY_ADDRESS = "address" +const KEY_CURRENCY = "currency" +const KEY_NETWORKS_CURRENT_NETWORK = "networks/current-network" +const KEY_DAPPS_ADDRESS = "dapps-address" +const KEY_EIP1581_ADDRESS = "eip1581-address" +const KEY_INSTALLATION_ID = "installation-id" +const KEY_KEY_UID = "key-uid" +const KEY_LATEST_DERIVED_PATH = "latest-derived-path" +const KEY_LINK_PREVIEW_REQUEST_ENABLED = "link-preview-request-enabled" +const KEY_MESSAGES_FROM_CONTACTS_ONLY = "messages-from-contacts-only" +const KEY_MNEMONIC = "mnemonic" +const KEY_NAME = "name" +const KEY_PHOTO_PATH = "photo-path" +const KEY_PREVIEW_PRIVACY = "preview-privacy?" +const KEY_PUBLIC_KEY = "public-key" +const KEY_SIGNING_PHRASE = "signing-phrase" +const KEY_DEFAULT_SYNC_PERIOD = "default-sync-period" +const KEY_SEND_PUSH_NOTIFICATIONS = "send-push-notifications?" +const KEY_APPEARANCE = "appearance" +const KEY_PROFILE_PICTURES_SHOW_TO = "profile-pictures-show-to" +const KEY_PROFILE_PICTURES_VISIBILITY = "profile-pictures-visibility" +const KEY_USE_MAILSERVERS = "use-mailservers?" +const KEY_WALLET_ROOT_ADDRESS = "wallet-root-address" +const KEY_SEND_STATUS_UPDATES = "send-status-updates?" +const KEY_TELEMETRY_SERVER_URL = "telemetry-server-url" +const KEY_FLEET = "fleet" +const KEY_WALLET_VISIBLE_TOKENS = "wallet/visible-tokens" -type - Service* = ref object of ServiceInterface - # profile: Dto +type + Service* = ref object of service_interface.ServiceInterface + settings: SettingsDto method delete*(self: Service) = discard @@ -32,69 +49,289 @@ proc newService*(): Service = method init*(self: Service) = try: - echo "init" - + let response = status_go.getSettings() + self.settings = response.result.toSettingsDto() except Exception as e: let errDesription = e.msg error "error: ", errDesription return -method getPubKey*(self: Service): string= - return status_go_settings.getSetting(Setting.PublicKey, "0x0") +proc saveSetting(self: Service, attribute: string, value: string | JsonNode | bool | int): bool = + let response = status_go.saveSettings(attribute, value) + if(not response.error.isNil): + error "error saving settings: ", errDescription = response.error.message + return false -method getNetwork*(self: Service): string = - return status_go_settings.getSetting(Setting.Networks_CurrentNetwork, DEFAULT_NETWORK_NAME) + return true -method getAppearance*(self: Service): int = - let appearance: int = status_go_settings.getSetting[int](Setting.Appearance, 0) - return appearance +method saveAddress*(self: Service, value: string): bool = + if(self.saveSetting(KEY_ADDRESS, value)): + self.settings.address = value + return true + return false -method getMessagesFromContactsOnly*(self: Service): bool = - return status_go_settings.getSetting[bool](Setting.MessagesFromContactsOnly) +method getAddress*(self: Service): string = + return self.settings.address -method getSendUserStatus*(self: Service): bool = - return status_go_settings.getSetting[bool](Setting.SendUserStatus) +method saveCurrency*(self: Service, value: string): bool = + if(self.saveSetting(KEY_CURRENCY, value)): + self.settings.currency = value + return true + return false -method setSendUserStatus*(self: Service, value: bool) = - # this will be done in a proper way in `base_bc`, so far this is just a fix - discard status_go_settings.saveSetting(Setting.SendUserStatus, value) +method getCurrency*(self: Service): string = + if(self.settings.currency.len == 0): + self.settings.currency = DEFAULT_CURRENCY -method getCurrentUserStatus*(self: Service): int = - let userStatus = status_go_settings.getSetting[JsonNode](Setting.CurrentUserStatus) - return userStatus{"statusType"}.getInt() + return self.settings.currency -method getIdentityImage*(self: Service, address: string): IdentityImage = - var obj = status_accounts.getIdentityImage(address) - var identityImage = IdentityImage(thumbnail: obj.thumbnail, large: obj.large) - return identityImage +method saveCurrentNetwork*(self: Service, value: string): bool = + if(self.saveSetting(KEY_NETWORKS_CURRENT_NETWORK, value)): + self.settings.currentNetwork = value + return true + return false + +method getCurrentNetwork*(self: Service): string = + if(self.settings.currentNetwork.len == 0): + self.settings.currentNetwork = DEFAULT_CURRENT_NETWORK + + return self.settings.currentNetwork + +method saveDappsAddress*(self: Service, value: string): bool = + if(self.saveSetting(KEY_DAPPS_ADDRESS, value)): + self.settings.dappsAddress = value + return true + return false method getDappsAddress*(self: Service): string = - return status_go_settings.getSetting[string](Setting.DappsAddress) + return self.settings.dappsAddress -method setDappsAddress*(self: Service, address: string): bool = - let r = status_go_settings.saveSetting(Setting.DappsAddress, address) - return r.error == "" +method saveEip1581Address*(self: Service, value: string): bool = + if(self.saveSetting(KEY_EIP1581_ADDRESS, value)): + self.settings.eip1581Address = value + return true + return false -method getCurrentNetworkDetails*(self: Service): NetworkDetails = - let currNetwork = getSetting[string](Setting.Networks_CurrentNetwork, DEFAULT_NETWORK_NAME) - let networks = getSetting[seq[NetworkDetails]](Setting.Networks_Networks) - for n in networks: - if n.id == currNetwork: +method getEip1581Address*(self: Service): string = + return self.settings.eip1581Address + +method saveInstallationId*(self: Service, value: string): bool = + if(self.saveSetting(KEY_INSTALLATION_ID, value)): + self.settings.installationId = value + return true + return false + +method getInstallationId*(self: Service): string = + return self.settings.installationId + +method saveKeyUid*(self: Service, value: string): bool = + if(self.saveSetting(KEY_KEY_UID, value)): + self.settings.keyUid = value + return true + return false + +method getKeyUid*(self: Service): string = + return self.settings.keyUid + +method saveLatestDerivedPath*(self: Service, value: int): bool = + if(self.saveSetting(KEY_LATEST_DERIVED_PATH, value)): + self.settings.latestDerivedPath = value + return true + return false + +method getLatestDerivedPath*(self: Service): int = + self.settings.latestDerivedPath + +method saveLinkPreviewRequestEnabled*(self: Service, value: bool): bool = + if(self.saveSetting(KEY_LINK_PREVIEW_REQUEST_ENABLED, value)): + self.settings.linkPreviewRequestEnabled = value + return true + return false + +method getLinkPreviewRequestEnabled*(self: Service): bool = + self.settings.linkPreviewRequestEnabled + +method saveMessagesFromContactsOnly*(self: Service, value: bool): bool = + if(self.saveSetting(KEY_MESSAGES_FROM_CONTACTS_ONLY, value)): + self.settings.messagesFromContactsOnly = value + return true + return false + +method getMessagesFromContactsOnly*(self: Service): bool = + self.settings.messagesFromContactsOnly + +method saveMnemonic*(self: Service, value: string): bool = + if(self.saveSetting(KEY_MNEMONIC, value)): + self.settings.mnemonic = value + return true + return false + +method getMnemonic*(self: Service): string = + return self.settings.mnemonic + +method saveName*(self: Service, value: string): bool = + if(self.saveSetting(KEY_NAME, value)): + self.settings.name = value + return true + return false + +method getName*(self: Service): string = + return self.settings.name + +method savePhotoPath*(self: Service, value: string): bool = + if(self.saveSetting(KEY_PHOTO_PATH, value)): + self.settings.photoPath = value + return true + return false + +method getPhotoPath*(self: Service): string = + return self.settings.photoPath + +method savePreviewPrivacy*(self: Service, value: bool): bool = + if(self.saveSetting(KEY_PREVIEW_PRIVACY, value)): + self.settings.previewPrivacy = value + return true + return false + +method getPreviewPrivacy*(self: Service): bool = + self.settings.previewPrivacy + +method savePublicKey*(self: Service, value: string): bool = + if(self.saveSetting(KEY_PUBLIC_KEY, value)): + self.settings.publicKey = value + return true + return false + +method getPublicKey*(self: Service): string = + return self.settings.publicKey + +method saveSigningPhrase*(self: Service, value: string): bool = + if(self.saveSetting(KEY_SIGNING_PHRASE, value)): + self.settings.signingPhrase = value + return true + return false + +method getSigningPhrase*(self: Service): string = + return self.settings.signingPhrase + +method saveDefaultSyncPeriod*(self: Service, value: int): bool = + if(self.saveSetting(KEY_DEFAULT_SYNC_PERIOD, value)): + self.settings.defaultSyncPeriod = value + return true + return false + +method getDefaultSyncPeriod*(self: Service): int = + self.settings.defaultSyncPeriod + +method saveSendPushNotifications*(self: Service, value: bool): bool = + if(self.saveSetting(KEY_SEND_PUSH_NOTIFICATIONS, value)): + self.settings.sendPushNotifications = value + return true + return false + +method getSendPushNotifications*(self: Service): bool = + self.settings.sendPushNotifications + +method saveAppearance*(self: Service, value: int): bool = + if(self.saveSetting(KEY_APPEARANCE, value)): + self.settings.appearance = value + return true + return false + +method getAppearance*(self: Service): int = + self.settings.appearance + +method saveProfilePicturesShowTo*(self: Service, value: int): bool = + if(self.saveSetting(KEY_PROFILE_PICTURES_SHOW_TO, value)): + self.settings.profilePicturesShowTo = value + return true + return false + +method getProfilePicturesShowTo*(self: Service): int = + self.settings.profilePicturesShowTo + +method saveProfilePicturesVisibility*(self: Service, value: int): bool = + if(self.saveSetting(KEY_PROFILE_PICTURES_VISIBILITY, value)): + self.settings.profilePicturesVisibility = value + return true + return false + +method getProfilePicturesVisibility*(self: Service): int = + self.settings.profilePicturesVisibility + +method saveUseMailservers*(self: Service, value: bool): bool = + if(self.saveSetting(KEY_USE_MAILSERVERS, value)): + self.settings.useMailservers = value + return true + return false + +method getUseMailservers*(self: Service): bool = + self.settings.useMailservers + +method saveWalletRootAddress*(self: Service, value: string): bool = + if(self.saveSetting(KEY_WALLET_ROOT_ADDRESS, value)): + self.settings.walletRootAddress = value + return true + return false + +method getWalletRootAddress*(self: Service): string = + return self.settings.walletRootAddress + +method saveSendStatusUpdates*(self: Service, value: bool): bool = + if(self.saveSetting(KEY_SEND_STATUS_UPDATES, value)): + self.settings.sendStatusUpdates = value + return true + return false + +method getSendStatusUpdates*(self: Service): bool = + self.settings.sendStatusUpdates + +method saveTelemetryServerUrl*(self: Service, value: string): bool = + if(self.saveSetting(KEY_TELEMETRY_SERVER_URL, value)): + self.settings.telemetryServerUrl = value + return true + return false + +method getTelemetryServerUrl*(self: Service): string = + return self.settings.telemetryServerUrl + +method saveFleet*(self: Service, value: string): bool = + if(self.saveSetting(KEY_FLEET, value)): + self.settings.fleet = value + return true + return false + +method getFleet*(self: Service): string = + return self.settings.fleet + +method getAvailableNetworks*(self: Service): seq[Network] = + return self.settings.availableNetworks + +method getCurrentNetworkDetails*(self: Service): Network = + for n in self.settings.availableNetworks: + if(n.id == self.getCurrentNetwork()): return n + + # we should never be here + error "error: current network is not among available networks" -method enableDeveloperFeatures*(self: Service) = - discard status_go_settings.saveSetting(Setting.TelemetryServerUrl, TELEMETRY_BASE_URL) - discard status_go_settings.saveSetting(Setting.AutoMessageEnabled, true) - var nodeConfig = status_go_settings.getNodeConfig() - nodeConfig["LogLevel"] = newJString($LogLevel.DEBUG) - discard status_go_settings.saveSetting(Setting.NodeConfig, nodeConfig) - quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported +method getCurrentNetworkId*(self: Service): int = + self.getCurrentNetworkDetails().config.networkId + +method getCurrentUserStatus*(self: Service): CurrentUserStatus = + self.settings.currentUserStatus + +method getPinnedMailservers*(self: Service): PinnedMailservers = + self.settings.pinnedMailservers + +method getWalletVisibleTokens*(self: Service): seq[string] = + self.settings.walletVisibleTokens.tokens method toggleTelemetry*(self: Service) = let telemetryServerUrl = status_go_settings.getSetting[string](Setting.TelemetryServerUrl) var newValue = "" if telemetryServerUrl == "": - newValue = TELEMETRY_BASE_URL + newValue = "https://telemetry.status.im" discard status_go_settings.saveSetting(Setting.TelemetryServerUrl, newValue) @@ -116,7 +353,16 @@ method toggleDebug*(self: Service) = else: nodeConfig["LogLevel"] = newJString($LogLevel.INFO) discard status_go_settings.saveSetting(Setting.NodeConfig, nodeConfig) + quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported method isDebugEnabled*(self: Service): bool = let nodeConfig = status_go_settings.getNodeConfig() return nodeConfig["LogLevel"].getStr() != $LogLevel.INFO + +method saveWalletVisibleTokens*(self: Service, tokens: seq[string]): bool = + var obj = newJObject() + obj[self.getCurrentNetwork()] = %* tokens + if(self.saveSetting(KEY_WALLET_VISIBLE_TOKENS, obj)): + self.settings.walletVisibleTokens.tokens = tokens + return true + return false diff --git a/src/app_service/service/settings/service_interface.nim b/src/app_service/service/settings/service_interface.nim index 91341d4985..6fa3faa4e0 100644 --- a/src/app_service/service/settings/service_interface.nim +++ b/src/app_service/service/settings/service_interface.nim @@ -1,14 +1,13 @@ -import dto -import dto/network_details -import dto/node_config -import dto/upstream_config +import ./dto/settings as settings_dto -export dto -export network_details -export node_config -export upstream_config +export settings_dto -type +# Default values: +const DEFAULT_CURRENT_NETWORK* = "mainnet_rpc" +const DEFAULT_CURRENCY* = "usd" +const DEFAULT_TELEMETRY_SERVER_URL* = "https://telemetry.status.im" + +type ServiceInterface* {.pure inheritable.} = ref object of RootObj ## Abstract class for this service access. @@ -18,43 +17,106 @@ method delete*(self: ServiceInterface) {.base.} = method init*(self: ServiceInterface) {.base.} = raise newException(ValueError, "No implementation available") -method getPubKey*(self: ServiceInterface): string {.base.} = +method saveAddress*(self: ServiceInterface, value: string): bool {.base.} = raise newException(ValueError, "No implementation available") -method getNetwork*(self: ServiceInterface): string {.base.} = +method getAddress*(self: ServiceInterface): string {.base.} = raise newException(ValueError, "No implementation available") -method getAppearance*(self: ServiceInterface): int {.base.} = +method saveCurrency*(self: ServiceInterface, value: string): bool {.base.} = raise newException(ValueError, "No implementation available") -method getMessagesFromContactsOnly*(self: ServiceInterface): bool {.base.} = +method getCurrency*(self: ServiceInterface): string {.base.} = raise newException(ValueError, "No implementation available") -method getSendUserStatus*(self: ServiceInterface): bool {.base.} = +method saveCurrentNetwork*(self: ServiceInterface, value: string): bool {.base.} = raise newException(ValueError, "No implementation available") -method setSendUserStatus*(self: ServiceInterface, value: bool) {.base.} = +method getCurrentNetwork*(self: ServiceInterface): string {.base.} = raise newException(ValueError, "No implementation available") -method getCurrentUserStatus*(self: ServiceInterface): int {.base.} = - raise newException(ValueError, "No implementation available") - -method getIdentityImage*(self: ServiceInterface, address: string): IdentityImage {.base.} = +method saveDappsAddress*(self: ServiceInterface, value: string): bool {.base.} = raise newException(ValueError, "No implementation available") method getDappsAddress*(self: ServiceInterface): string {.base.} = raise newException(ValueError, "No implementation available") -method setDappsAddress*(self: ServiceInterface, address: string): bool {.base.} = +method saveEip1581Address*(self: ServiceInterface, value: string): bool {.base.} = raise newException(ValueError, "No implementation available") -method getCurrentNetworkDetails*(self: ServiceInterface): NetworkDetails {.base.} = +method getEip1581Address*(self: ServiceInterface): string {.base.} = raise newException(ValueError, "No implementation available") -method toggleTelemetry*(self: ServiceInterface) {.base.} = +method saveInstallationId*(self: ServiceInterface, value: string): bool {.base.} = raise newException(ValueError, "No implementation available") -method isTelemetryEnabled*(self: ServiceInterface): bool {.base.} = +method getInstallationId*(self: ServiceInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method saveKeyUid*(self: ServiceInterface, value: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getKeyUid*(self: ServiceInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method saveLatestDerivedPath*(self: ServiceInterface, value: int): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getLatestDerivedPath*(self: ServiceInterface): int {.base.} = + raise newException(ValueError, "No implementation available") + +method saveLinkPreviewRequestEnabled*(self: ServiceInterface, value: bool): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getLinkPreviewRequestEnabled*(self: ServiceInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method saveMessagesFromContactsOnly*(self: ServiceInterface, value: bool): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getMessagesFromContactsOnly*(self: ServiceInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method saveMnemonic*(self: ServiceInterface, value: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getMnemonic*(self: ServiceInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method saveName*(self: ServiceInterface, value: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getName*(self: ServiceInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method savePhotoPath*(self: ServiceInterface, value: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getPhotoPath*(self: ServiceInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method savePreviewPrivacy*(self: ServiceInterface, value: bool): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getPreviewPrivacy*(self: ServiceInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method savePublicKey*(self: ServiceInterface, value: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getPublicKey*(self: ServiceInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method saveSigningPhrase*(self: ServiceInterface, value: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getSigningPhrase*(self: ServiceInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method saveDefaultSyncPeriod*(self: ServiceInterface, value: int): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getDefaultSyncPeriod*(self: ServiceInterface): int {.base.} = raise newException(ValueError, "No implementation available") method toggleAutoMessage*(self: ServiceInterface) {.base.} = @@ -66,8 +128,77 @@ method isAutoMessageEnabled*(self: ServiceInterface): bool {.base.} = method toggleDebug*(self: ServiceInterface) {.base.} = raise newException(ValueError, "No implementation available") -method isDebugEnabled*(self: ServiceInterface): bool {.base.} = +method saveSendPushNotifications*(self: ServiceInterface, value: bool): bool {.base.} = raise newException(ValueError, "No implementation available") -method enableDeveloperFeatures*(self: ServiceInterface) {.base.} = +method getSendPushNotifications*(self: ServiceInterface): bool {.base.} = raise newException(ValueError, "No implementation available") + +method saveAppearance*(self: ServiceInterface, value: int): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getAppearance*(self: ServiceInterface): int {.base.} = + raise newException(ValueError, "No implementation available") + +method saveProfilePicturesShowTo*(self: ServiceInterface, value: int): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getProfilePicturesShowTo*(self: ServiceInterface): int {.base.} = + raise newException(ValueError, "No implementation available") + +method saveProfilePicturesVisibility*(self: ServiceInterface, value: int): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getProfilePicturesVisibility*(self: ServiceInterface): int {.base.} = + raise newException(ValueError, "No implementation available") + +method saveUseMailservers*(self: ServiceInterface, value: bool): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getUseMailservers*(self: ServiceInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method saveWalletRootAddress*(self: ServiceInterface, value: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getWalletRootAddress*(self: ServiceInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method saveSendStatusUpdates*(self: ServiceInterface, value: bool): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getSendStatusUpdates*(self: ServiceInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method saveTelemetryServerUrl*(self: ServiceInterface, value: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getTelemetryServerUrl*(self: ServiceInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method saveFleet*(self: ServiceInterface, value: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getFleet*(self: ServiceInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method getAvailableNetworks*(self: ServiceInterface): seq[Network] {.base.} = + raise newException(ValueError, "No implementation available") + +method getCurrentNetworkDetails*(self: ServiceInterface): Network {.base.} = + raise newException(ValueError, "No implementation available") + +method getCurrentNetworkId*(self: ServiceInterface): int {.base.} = + raise newException(ValueError, "No implementation available") + +method getCurrentUserStatus*(self: ServiceInterface): CurrentUserStatus {.base.} = + raise newException(ValueError, "No implementation available") + +method getPinnedMailservers*(self: ServiceInterface): PinnedMailservers {.base.} = + raise newException(ValueError, "No implementation available") + +method getWalletVisibleTokens*(self: ServiceInterface): seq[string] {.base.} = + raise newException(ValueError, "No implementation available") + +method saveWalletVisibleTokens*(self: ServiceInterface, tokens: seq[string]): bool {.base.} = + raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app_service/service/token/service.nim b/src/app_service/service/token/service.nim index 6c7235462d..ac145bb569 100644 --- a/src/app_service/service/token/service.nim +++ b/src/app_service/service/token/service.nim @@ -4,8 +4,7 @@ from sugar import `=>` import web3/ethtypes from web3/conversions import `$` import status/statusgo_backend_new/custom_tokens as custom_tokens -import ../setting/service as setting_service -import ../settings/service as settings_service +import ../settings/service_interface as settings_service import ../../../app/core/tasks/[qt, threadpool] import ./dto, ./static_token @@ -39,8 +38,7 @@ QtObject: type Service* = ref object of QObject events: EventEmitter threadpool: ThreadPool - settingService: setting_service.Service - settingsService: settings_service.Service + settingsService: settings_service.ServiceInterface tokens: seq[TokenDto] proc delete*(self: Service) = @@ -49,21 +47,19 @@ QtObject: proc newService*( events: EventEmitter, threadpool: ThreadPool, - settingService: setting_service.Service, - settingsService: settings_service.Service + settingsService: settings_service.ServiceInterface ): Service = new(result, delete) result.QObject.setup result.events = events result.threadpool = threadpool - result.settingService = settingService result.settingsService = settingsService result.tokens = @[] proc getDefaultVisibleSymbols(self: Service): seq[string] = - let networkSlug = self.settingService.getSetting().currentNetwork.slug + let networkSlug = self.settingsService.getCurrentNetwork() - if networkSlug == "mainnet_rpc": + if networkSlug == DEFAULT_CURRENT_NETWORK: return @["SNT"] if networkSlug == "testnet_rpc" or networkSlug == "rinkeby_rpc": @@ -74,7 +70,7 @@ QtObject: proc init*(self: Service) = try: - var activeTokenSymbols = self.settingService.getSetting().activeTokenSymbols + var activeTokenSymbols = self.settingsService.getWalletVisibleTokens() if activeTokenSymbols.len == 0: activeTokenSymbols = self.getDefaultVisibleSymbols() @@ -89,7 +85,7 @@ QtObject: static_tokens, map(response.result.getElems(), proc(x: JsonNode): TokenDto = x.toTokenDto(activeTokenSymbols)) ).filter( - proc(x: TokenDto): bool = x.chainId == self.settingService.getSetting().currentNetwork.id + proc(x: TokenDto): bool = x.chainId == self.settingsService.getCurrentNetworkId() ) except Exception as e: @@ -104,7 +100,7 @@ QtObject: custom_tokens.addCustomToken(address, name, symbol, decimals, "") let token = newDto( name, - self.settingService.getSetting().currentNetwork.id, + self.settingsService.getCurrentNetworkId(), fromHex(Address, address), symbol, decimals, @@ -123,7 +119,7 @@ QtObject: break let visibleSymbols = self.tokens.filter(t => t.isVisible).map(t => t.symbol) - discard self.settingService.saveSetting("wallet/visible-tokens", visibleSymbols) + discard self.settingsService.saveWalletVisibleTokens(visibleSymbols) self.events.emit("token/visibilityToggled", VisibilityToggled(token: tokenChanged)) proc removeCustomToken*(self: Service, address: string) = @@ -145,7 +141,7 @@ QtObject: )) proc getTokenDetails*(self: Service, address: string) = - let chainId = self.settingsService.getCurrentNetworkDetails().config.networkId + let chainId = self.settingsService.getCurrentNetworkId() let arg = GetTokenDetailsTaskArg( tptr: cast[ByteAddress](getTokenDetailsTask), vptr: cast[ByteAddress](self.vptr), diff --git a/src/app_service/service/wallet_account/service.nim b/src/app_service/service/wallet_account/service.nim index c1b892f6f4..0613bd0c14 100644 --- a/src/app_service/service/wallet_account/service.nim +++ b/src/app_service/service/wallet_account/service.nim @@ -2,14 +2,13 @@ import Tables, json, sequtils, sugar, chronicles, strformat, stint, httpclient, import web3/[ethtypes, conversions] import eventemitter -import ../setting/service as setting_service +import ../settings/service_interface as settings_service import ../token/service as token_service import ../../common/account_constants import ../../../constants import ./service_interface, ./dto import status/statusgo_backend_new/accounts as status_go_accounts -import status/statusgo_backend_new/tokens as status_go_tokens import status/statusgo_backend_new/eth as status_go_eth export service_interface @@ -61,7 +60,21 @@ proc fetchAccounts(): seq[WalletAccountDto] = return response.result.getElems().map( x => x.toWalletAccountDto() ).filter(a => not a.isChat) + + +proc fetchTokenBalance(tokenAddress, accountAddress: string, decimals: int): float64 = + let key = tokenAddress & accountAddress + if balanceCache.hasKey(key): + return balanceCache[key] + + try: + let tokenBalanceResponse = status_go_eth.getTokenBalance(tokenAddress, accountAddress) + result = parsefloat(hex2Balance(tokenBalanceResponse.result.getStr, decimals)) + balanceCache[key] = result + except Exception as e: + error "Error getting token balance", msg = e.msg + proc fetchEthBalance(accountAddress: string): float64 = let key = "0x0" & accountAddress if balanceCache.hasKey(key): @@ -88,7 +101,7 @@ type WalletAccountUpdated = ref object of Args type Service* = ref object of service_interface.ServiceInterface events: EventEmitter - settingService: setting_service.Service + settingsService: settings_service.ServiceInterface tokenService: token_service.Service accounts: OrderedTable[string, WalletAccountDto] @@ -96,11 +109,11 @@ method delete*(self: Service) = discard proc newService*( - events: EventEmitter, settingService: setting_service.Service, tokenService: token_service.Service -): Service = + events: EventEmitter, settingsService: settings_service.ServiceInterface, tokenService: token_service.Service): + Service = result = Service() result.events = events - result.settingService = settingService + result.settingsService = settingsService result.tokenService = tokenService result.accounts = initOrderedTable[string, WalletAccountDto]() @@ -110,8 +123,7 @@ method getVisibleTokens(self: Service): seq[TokenDto] = method buildTokens( self: Service, account: WalletAccountDto, - prices: Table[string, float64], - balances: JsonNode, + prices: Table[string, float64] ): seq[WalletTokenDto] = let balance = fetchEthBalance(account.address) result = @[WalletTokenDto( @@ -127,7 +139,7 @@ method buildTokens( )] for token in self.getVisibleTokens(): - let balance = parsefloat(hex2Balance(balances{token.addressAsString()}.getStr, token.decimals)) + let balance = fetchTokenBalance($token.address, account.address, token.decimals) result.add( WalletTokenDto( name: token.name, @@ -143,26 +155,18 @@ method buildTokens( ) method fetchPrices(self: Service): Table[string, float64] = - let currency = self.settingService.getSetting().currency + let currency = self.settingsService.getCurrency() var prices = {"ETH": fetchPrice("ETH", currency)}.toTable for token in self.getVisibleTokens(): prices[token.symbol] = fetchPrice(token.symbol, currency) return prices -method fetchBalances(self: Service, accounts: seq[string]): JsonNode = - let network = self.settingService.getSetting().currentNetwork - let tokens = self.getVisibleTokens().map(t => t.addressAsString()) - - return status_go_tokens.getBalances(network.id, accounts, tokens).result - method refreshBalances(self: Service) = let prices = self.fetchPrices() - let accounts = toSeq(self.accounts.keys) - let balances = self.fetchBalances(accounts) - + for account in toSeq(self.accounts.values): - account.tokens = self.buildTokens(account, prices, balances{account.address}) + account.tokens = self.buildTokens(account, prices) method init*(self: Service) = try: @@ -189,74 +193,128 @@ method getWalletAccount*(self: Service, accountIndex: int): WalletAccountDto = method getCurrencyBalance*(self: Service): float64 = return self.getWalletAccounts().map(a => a.getCurrencyBalance()).foldl(a + b, 0.0) -method addNewAccountToLocalStore(self: Service) = - let accounts = fetchAccounts() - let prices = self.fetchPrices() +method getDefaultAccount(self: Service): string = + return status_go_eth.getAccounts().result[0].getStr - var newAccount = accounts[0] - for account in accounts: - if not self.accounts.haskey(account.address): - newAccount = account - break +method saveAccount( + self: Service, + address: string, + name: string, + password: string, + color: string, + accountType: string, + isADerivedAccount = true, + walletIndex: int = 0, + id: string = "", + publicKey: string = "", +): string = + try: + status_go_accounts.saveAccount( + address, + name, + password, + color, + accountType, + isADerivedAccount = true, + walletIndex, + id, + publicKey, + ) + let accounts = fetchAccounts() + let prices = self.fetchPrices() - let balances = self.fetchBalances(@[newAccount.address]) - newAccount.tokens = self.buildTokens(newAccount, prices, balances{newAccount.address}) - self.accounts[newAccount.address] = newAccount - self.events.emit("walletAccount/accountSaved", AccountSaved(account: newAccount)) + var newAccount = accounts[0] + for account in accounts: + if not self.accounts.haskey(account.address): + newAccount = account + break + + newAccount.tokens = self.buildTokens(newAccount, prices) + self.accounts[newAccount.address] = newAccount + self.events.emit("walletAccount/accountSaved", AccountSaved(account: newAccount)) + except Exception as e: + return fmt"Error adding new account: {e.msg}" method generateNewAccount*(self: Service, password: string, accountName: string, color: string): string = - try: - discard status_go_accounts.generateAccount( - password, - accountName, - color, - ) - except Exception as e: - return fmt"Error generating new account: {e.msg}" + let + walletRootAddress = self.settingsService.getWalletRootAddress() + walletIndex = self.settingsService.getLatestDerivedPath() + 1 + defaultAccount = self.getDefaultAccount() + isPasswordOk = status_go_accounts.verifyAccountPassword(defaultAccount, password, KEYSTOREDIR) - self.addNewAccountToLocalStore() + if not isPasswordOk: + return "Error generating new account: invalid password" + + let accountResponse = status_go_accounts.loadAccount(walletRootAddress, password) + let accountId = accountResponse.result{"id"}.getStr + let path = "m/" & $walletIndex + let deriveResponse = status_go_accounts.deriveAccounts(accountId, @[path]) + let errMsg = self.saveAccount( + deriveResponse.result[path]{"address"}.getStr, + accountName, + password, + color, + status_go_accounts.GENERATED, + true, + walletIndex, + accountId, + deriveResponse.result[path]{"publicKey"}.getStr + ) + if errMsg != "": + return errMsg + + discard self.settingsService.saveLatestDerivedPath(walletIndex) return "" method addAccountsFromPrivateKey*(self: Service, privateKey: string, password: string, accountName: string, color: string): string = - try: - discard status_go_accounts.addAccountWithPrivateKey( - privateKey, - password, - accountName, - color, - ) - except Exception as e: - return fmt"Error adding account with private key: {e.msg}" + let + accountResponse = status_go_accounts.multiAccountImportPrivateKey(privateKey) + defaultAccount = self.getDefaultAccount() + isPasswordOk = status_go_accounts.verifyAccountPassword(defaultAccount, password, KEYSTOREDIR) - self.addNewAccountToLocalStore() - return "" + if not isPasswordOk: + return "Error generating new account: invalid password" -method addAccountsFromSeed*(self: Service, mnemonic: string, password: string, accountName: string, color: string): string = - try: - discard status_go_accounts.addAccountWithMnemonic( - mnemonic, - password, - accountName, - color, - ) - except Exception as e: - return fmt"Error adding account with mnemonic: {e.msg}" + return self.saveAccount( + accountResponse.result{"address"}.getStr, + accountName, + password, + color, + status_go_accounts.KEY, + false, + 0, + accountResponse.result{"accountId"}.getStr, + accountResponse.result{"publicKey"}.getStr, + ) - self.addNewAccountToLocalStore() - return "" +method addAccountsFromSeed*(self: Service, seedPhrase: string, password: string, accountName: string, color: string): string = + let mnemonic = replace(seedPhrase, ',', ' ') + let paths = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET] + let accountResponse = status_go_accounts.multiAccountImportMnemonic(mnemonic) + let accountId = accountResponse.result{"id"}.getStr + let deriveResponse = status_go_accounts.deriveAccounts(accountId, paths) + + let + defaultAccount = self.getDefaultAccount() + isPasswordOk = status_go_accounts.verifyAccountPassword(defaultAccount, password, KEYSTOREDIR) + + if not isPasswordOk: + return "Error generating new account: invalid password" + + return self.saveAccount( + deriveResponse.result[PATH_DEFAULT_WALLET]{"address"}.getStr, + accountName, + password, + color, + status_go_accounts.SEED, + true, + 0, + accountId, + deriveResponse.result[PATH_DEFAULT_WALLET]{"publicKey"}.getStr + ) method addWatchOnlyAccount*(self: Service, address: string, accountName: string, color: string): string = - try: - discard status_go_accounts.addAccountWatch( - address, - accountName, - color, - ) - except Exception as e: - return fmt"Error adding account with mnemonic: {e.msg}" - - self.addNewAccountToLocalStore() - return "" + return self.saveAccount(address, accountName, "", color, status_go_accounts.WATCH, false) method deleteAccount*(self: Service, address: string) = discard status_go_accounts.deleteAccount(address) @@ -266,7 +324,7 @@ method deleteAccount*(self: Service, address: string) = self.events.emit("walletAccount/accountDeleted", AccountDeleted(account: accountDeleted)) method updateCurrency*(self: Service, newCurrency: string) = - discard self.settingService.saveSetting("currency", newCurrency) + discard self.settingsService.saveCurrency(newCurrency) self.refreshBalances() self.events.emit("walletAccount/currencyUpdated", CurrencyUpdated())