From 00ed4f9c44ba0e67acc0b7627122beeeb9b10e79 Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Thu, 19 May 2022 10:53:57 +0200 Subject: [PATCH] feat(@wallet): multi transaction simple view --- src/app/boot/app_controller.nim | 2 +- .../current_account/module.nim | 13 +- src/app/modules/main/module.nim | 5 +- .../profile_section/advanced/io_interface.nim | 3 + .../main/profile_section/advanced/module.nim | 3 + .../main/profile_section/advanced/view.nim | 6 + .../ens_usernames/controller.nim | 27 +-- .../ens_usernames/io_interface.nim | 13 +- .../profile_section/ens_usernames/module.nim | 28 +-- .../profile_section/ens_usernames/view.nim | 27 +-- .../modules/main/profile_section/module.nim | 6 +- src/app/modules/main/stickers/controller.nim | 22 +- .../modules/main/stickers/io_interface.nim | 7 +- src/app/modules/main/stickers/module.nim | 27 ++- src/app/modules/main/stickers/view.nim | 24 +- .../main/wallet_section/accounts/module.nim | 7 +- .../main/wallet_section/controller.nim | 3 - .../wallet_section/current_account/module.nim | 15 +- .../main/wallet_section/io_interface.nim | 3 - .../modules/main/wallet_section/module.nim | 5 +- .../transactions/controller.nim | 40 ++-- .../transactions/io_interface.nim | 24 +- .../wallet_section/transactions/module.nim | 37 ++-- .../main/wallet_section/transactions/view.nim | 40 ++-- src/app/modules/main/wallet_section/view.nim | 5 +- .../modules/shared_models/balance_model.nim | 86 ++++++++ src/app/modules/shared_models/token_item.nim | 61 +++-- src/app/modules/shared_models/token_model.nim | 52 +++-- src/app_service/common/network_constants.nim | 2 +- .../service/collectible/service.nim | 16 +- src/app_service/service/ens/async_tasks.nim | 15 +- src/app_service/service/ens/service.nim | 70 ++---- .../service/eth/dto/method_dto.nim | 12 +- src/app_service/service/message/service.nim | 2 +- src/app_service/service/network/service.nim | 50 +++-- src/app_service/service/network/types.nim | 8 +- src/app_service/service/settings/service.nim | 19 -- .../service/stickers/async_tasks.nim | 14 +- src/app_service/service/stickers/service.nim | 56 +---- src/app_service/service/token/dto.nim | 6 +- src/app_service/service/token/service.nim | 16 +- .../service/transaction/service.nim | 154 ++++++++----- .../service/wallet_account/async_tasks.nim | 74 +++++-- .../service/wallet_account/dto.nim | 18 +- .../service/wallet_account/service.nim | 2 +- src/backend/backend.nim | 3 - src/backend/core.nim | 4 +- src/backend/eth.nim | 17 +- ui/StatusQ | 2 +- ui/app/AppLayouts/Browser/BrowserLayout.qml | 3 +- .../AppLayouts/Browser/stores/RootStore.qml | 1 + .../AppLayouts/Browser/stores/WalletStore.qml | 9 - .../Browser/views/WebProviderObj.qml | 9 +- .../Chat/popups/ChatCommandModal.qml | 2 +- ui/app/AppLayouts/Chat/stores/RootStore.qml | 32 +-- .../AppLayouts/Chat/stores/StickersStore.qml | 16 +- .../AppLayouts/Chat/views/ChatColumnView.qml | 4 - .../Profile/stores/AdvancedStore.qml | 4 + .../Profile/stores/EnsUsernamesStore.qml | 29 ++- .../AppLayouts/Profile/views/AdvancedView.qml | 2 +- .../Profile/views/EnsDetailsView.qml | 4 +- .../Profile/views/EnsSearchView.qml | 9 +- .../views/EnsTermsAndConditionsView.qml | 6 +- ui/app/AppLayouts/Profile/views/EnsView.qml | 4 +- ui/app/AppLayouts/stores/RootStore.qml | 33 +-- ui/app/mainui/AppMain.qml | 4 - ui/imports/shared/controls/AssetDelegate.qml | 6 +- ui/imports/shared/controls/GasSelector.qml | 68 +++--- ui/imports/shared/controls/GasValidator.qml | 8 +- .../controls/chat/GasSelectorButton.qml | 1 + ui/imports/shared/panels/BalanceValidator.qml | 3 +- ui/imports/shared/popups/SendModal.qml | 118 ++++++---- .../shared/popups/SignTransactionModal.qml | 64 +++--- .../status/StatusETHTransactionModal.qml | 17 +- .../status/StatusSNTTransactionModal.qml | 20 +- .../shared/status/StatusStickerMarket.qml | 6 +- .../status/StatusStickerPackClickPopup.qml | 6 +- ui/imports/shared/views/NetworkSelector.qml | 130 +++++++++++ ui/imports/shared/views/SendModalHeader.qml | 11 +- ui/imports/shared/views/TabNetworkAndFees.qml | 208 ------------------ .../views/chat/AcceptTransactionView.qml | 2 +- .../views/chat/TransactionBubbleView.qml | 2 +- ui/imports/utils/Global.qml | 5 +- ui/imports/utils/Utils.qml | 12 +- vendor/nim-status-go | 2 +- vendor/status-go | 2 +- 86 files changed, 1038 insertions(+), 975 deletions(-) create mode 100644 src/app/modules/shared_models/balance_model.nim create mode 100644 ui/imports/shared/views/NetworkSelector.qml delete mode 100644 ui/imports/shared/views/TabNetworkAndFees.qml diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index a0caed2afb..beff413d0c 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -143,7 +143,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.tokenService = token_service.newService( statusFoundation.events, statusFoundation.threadpool, result.networkService ) - result.collectibleService = collectible_service.newService(result.settingsService) + result.collectibleService = collectible_service.newService(result.networkService) result.walletAccountService = wallet_account_service.newService( statusFoundation.events, statusFoundation.threadpool, result.settingsService, result.accountsService, result.tokenService, result.networkService, diff --git a/src/app/modules/main/browser_section/current_account/module.nim b/src/app/modules/main/browser_section/current_account/module.nim index 0754700c59..3b9f5eb68f 100644 --- a/src/app/modules/main/browser_section/current_account/module.nim +++ b/src/app/modules/main/browser_section/current_account/module.nim @@ -1,4 +1,4 @@ -import NimQml, Tables +import NimQml, Tables, sequtils import ../../../../global/global_singleton import ../../../../core/eventemitter @@ -41,7 +41,16 @@ method delete*(self: Module) = proc setAssets(self: Module, tokens: seq[WalletTokenDto]) = var items: seq[Item] for t in tokens: - let item = token_item.initItem(t.name, t.symbol, t.totalBalance.balance, t.address, t.totalBalance.currencyBalance) + let item = token_item.initItem( + t.name, + t.symbol, + t.totalBalance.balance, + t.totalBalance.currencyBalance, + t.enabledNetworkBalance.balance, + t.enabledNetworkBalance.currencybalance, + t.visible, + toSeq(t.balancesPerChain.values), + ) items.add(item) self.view.getAssetsModel().setItems(items) diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 7282fca26e..c1a4ea6d40 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -152,9 +152,10 @@ proc newModule*[T]( result.profileSectionModule = profile_section_module.newModule( result, events, accountsService, settingsService, stickersService, profileService, contactsService, aboutService, languageService, privacyService, nodeConfigurationService, - devicesService, mailserversService, chatService, ensService, walletAccountService, generalService, communityService + devicesService, mailserversService, chatService, ensService, walletAccountService, generalService, communityService, + networkService, ) - result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService) + result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService, networkService) result.activityCenterModule = activity_center_module.newModule(result, events, activityCenterService, contactsService, messageService, chatService) result.communitiesModule = communities_module.newModule(result, events, communityService, contactsService) diff --git a/src/app/modules/main/profile_section/advanced/io_interface.nim b/src/app/modules/main/profile_section/advanced/io_interface.nim index 293332635c..d163b67891 100644 --- a/src/app/modules/main/profile_section/advanced/io_interface.nim +++ b/src/app/modules/main/profile_section/advanced/io_interface.nim @@ -53,6 +53,9 @@ method getCurrentNetworkName*(self: AccessInterface): string {.base.} = method getCurrentNetworkId*(self: AccessInterface): string {.base.} = raise newException(ValueError, "No implementation available") +method getCurrentChainId*(self: AccessInterface): int {.base.} = + raise newException(ValueError, "No implementation available") + method setCurrentNetwork*(self: AccessInterface, network: string) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/profile_section/advanced/module.nim b/src/app/modules/main/profile_section/advanced/module.nim index a113b906fb..c7b82ca3f5 100644 --- a/src/app/modules/main/profile_section/advanced/module.nim +++ b/src/app/modules/main/profile_section/advanced/module.nim @@ -63,6 +63,9 @@ method getCurrentNetworkName*(self: Module): string = method getCurrentNetworkId*(self: Module): string = return self.controller.getCurrentNetworkDetails().id +method getCurrentChainId*(self: Module): int = + return self.controller.getCurrentNetworkDetails().config.NetworkId + method setCurrentNetwork*(self: Module, network: string) = self.controller.changeCurrentNetworkTo(network) diff --git a/src/app/modules/main/profile_section/advanced/view.nim b/src/app/modules/main/profile_section/advanced/view.nim index 57d589c1ec..c81c0745bf 100644 --- a/src/app/modules/main/profile_section/advanced/view.nim +++ b/src/app/modules/main/profile_section/advanced/view.nim @@ -44,6 +44,12 @@ QtObject: read = getCurrentNetworkId notify = currentNetworkChanged + proc getCurrentChainId*(self: View): int {.slot.} = + return self.delegate.getCurrentChainId() + QtProperty[int] currentChainId: + read = getCurrentChainId + notify = currentNetworkChanged + proc fleetChanged*(self: View) {.signal.} proc getFleet*(self: View): string {.slot.} = return self.delegate.getFleet() diff --git a/src/app/modules/main/profile_section/ens_usernames/controller.nim b/src/app/modules/main/profile_section/ens_usernames/controller.nim index 8d0659f41b..2cc4daed8e 100644 --- a/src/app/modules/main/profile_section/ens_usernames/controller.nim +++ b/src/app/modules/main/profile_section/ens_usernames/controller.nim @@ -5,6 +5,7 @@ import ../../../../global/global_singleton import ../../../../core/eventemitter import ../../../../../app_service/service/settings/service as settings_service import ../../../../../app_service/service/ens/service as ens_service +import ../../../../../app_service/service/network/service as network_service import ../../../../../app_service/service/wallet_account/service as wallet_account_service import ../../../../../app_service/service/token/dto @@ -17,17 +18,21 @@ type events: EventEmitter settingsService: settings_service.Service ensService: ens_service.Service + networkService: network_service.Service walletAccountService: wallet_account_service.Service -proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter, +proc newController*( + delegate: io_interface.AccessInterface, events: EventEmitter, settingsService: settings_service.Service, ensService: ens_service.Service, - walletAccountService: wallet_account_service.Service): Controller = + walletAccountService: wallet_account_service.Service, networkService: network_service.Service, +): Controller = result = Controller() result.delegate = delegate result.events = events result.settingsService = settingsService result.ensService = ensService result.walletAccountService = walletAccountService + result.networkService = networkService proc delete*(self: Controller) = discard @@ -41,10 +46,6 @@ proc init*(self: Controller) = let args = EnsUsernameDetailsArgs(e) self.delegate.onDetailsForEnsUsername(args.ensUsername, args.address, args.pubkey, args.isStatus, args.expirationTime) - self.events.on(SIGNAL_GAS_PRICE_FETCHED) do(e:Args): - let args = GasPriceArgs(e) - self.delegate.gasPriceFetched(args.gasPrice) - self.events.on(SIGNAL_ENS_TRANSACTION_CONFIRMED) do(e:Args): let args = EnsTransactionArgs(e) self.delegate.ensTransactionConfirmed(args.transactionType, args.ensUsername, args.transactionHash) @@ -65,15 +66,12 @@ proc getAllMyEnsUsernames*(self: Controller, includePendingEnsUsernames: bool): proc fetchDetailsForEnsUsername*(self: Controller, ensUsername: string) = self.ensService.fetchDetailsForEnsUsername(ensUsername) -proc fetchGasPrice*(self: Controller) = - self.ensService.fetchGasPrice() - proc setPubKeyGasEstimate*(self: Controller, ensUsername: string, address: string): int = return self.ensService.setPubKeyGasEstimate(ensUsername, address) proc setPubKey*(self: Controller, ensUsername: string, address: string, gas: string, gasPrice: string, - maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string = - return self.ensService.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) + maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string = + return self.ensService.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) proc getCurrentNetworkDetails*(self: Controller): Network = return self.settingsService.getCurrentNetworkDetails() @@ -107,8 +105,8 @@ proc registerEnsGasEstimate*(self: Controller, ensUsername: string, address: str return self.ensService.registerEnsGasEstimate(ensUsername, address) proc registerEns*(self: Controller, ensUsername: string, address: string, gas: string, gasPrice: string, - maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string = - return self.ensService.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) + maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string = + return self.ensService.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) proc getSNTBalance*(self: Controller): string = return self.ensService.getSNTBalance() @@ -131,3 +129,6 @@ proc getStatusToken*(self: Controller): string = "address": token.addressAsString() } return $jsonObj + +proc getChainIdForEns*(self: Controller): int = + return self.networkService.getNetworkForEns().chainId \ No newline at end of file diff --git a/src/app/modules/main/profile_section/ens_usernames/io_interface.nim b/src/app/modules/main/profile_section/ens_usernames/io_interface.nim index 2dc7b6c47f..13a9a839fc 100644 --- a/src/app/modules/main/profile_section/ens_usernames/io_interface.nim +++ b/src/app/modules/main/profile_section/ens_usernames/io_interface.nim @@ -22,9 +22,6 @@ method onDetailsForEnsUsername*(self: AccessInterface, ensUsername: string, addr isStatus: bool, expirationTime: int) {.base.} = raise newException(ValueError, "No implementation available") -method gasPriceFetched*(self: AccessInterface, gasPrice: string) {.base.} = - raise newException(ValueError, "No implementation available") - method ensTransactionConfirmed*(self: AccessInterface, trxType: string, ensUsername: string, transactionHash: string) {.base.} = raise newException(ValueError, "No implementation available") @@ -45,14 +42,11 @@ method numOfPendingEnsUsernames*(self: AccessInterface): int {.base.} = method fetchDetailsForEnsUsername*(self: AccessInterface, ensUsername: string) {.base.} = raise newException(ValueError, "No implementation available") -method fetchGasPrice*(self: AccessInterface) {.base.} = - raise newException(ValueError, "No implementation available") - method setPubKeyGasEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} = raise newException(ValueError, "No implementation available") method setPubKey*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string, - maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.base.} = + maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.base.} = raise newException(ValueError, "No implementation available") method releaseEnsEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} = @@ -72,7 +66,7 @@ method registerEnsGasEstimate*(self: AccessInterface, ensUsername: string, addre raise newException(ValueError, "No implementation available") method registerEns*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string, - maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.base.} = + maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.base.} = raise newException(ValueError, "No implementation available") method getSNTBalance*(self: AccessInterface): string {.base.} = @@ -94,5 +88,8 @@ method getGasEthValue*(self: AccessInterface, gweiValue: string, gasLimit: strin method getStatusToken*(self: AccessInterface): string {.base.} = raise newException(ValueError, "No implementation available") +method getChainIdForEns*(self: AccessInterface): int {.base.} = + raise newException(ValueError, "No implementation available") + method setPrefferedEnsUsername*(self: AccessInterface, ensUsername: string) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/profile_section/ens_usernames/module.nim b/src/app/modules/main/profile_section/ens_usernames/module.nim index 37017da3a3..03d4259d2c 100644 --- a/src/app/modules/main/profile_section/ens_usernames/module.nim +++ b/src/app/modules/main/profile_section/ens_usernames/module.nim @@ -8,6 +8,7 @@ import ../../../../core/eventemitter import ../../../../../app_service/common/conversion as service_conversion import ../../../../../app_service/service/settings/service as settings_service import ../../../../../app_service/service/ens/service as ens_service +import ../../../../../app_service/service/network/service as network_service import ../../../../../app_service/service/ens/utils as ens_utils import ../../../../../app_service/service/wallet_account/service as wallet_account_service @@ -26,14 +27,17 @@ type controller: Controller moduleLoaded: bool -proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, +proc newModule*( + delegate: delegate_interface.AccessInterface, events: EventEmitter, settingsService: settings_service.Service, ensService: ens_service.Service, - walletAccountService: wallet_account_service.Service): Module = + walletAccountService: wallet_account_service.Service, + networkService: network_service.Service, +): Module = result = Module() result.delegate = delegate result.view = view.newView(result) result.viewVariant = newQVariant(result.view) - result.controller = controller.newController(result, events, settingsService, ensService, walletAccountService) + result.controller = controller.newController(result, events, settingsService, ensService, walletAccountService, networkService) result.moduleLoaded = false method delete*(self: Module) = @@ -53,7 +57,6 @@ method isLoaded*(self: Module): bool = return self.moduleLoaded method viewDidLoad*(self: Module) = - self.fetchGasPrice() # add registered ens usernames let registeredEnsUsernames = self.controller.getAllMyEnsUsernames(includePendingEnsUsernames = false) for u in registeredEnsUsernames: @@ -85,18 +88,12 @@ method onDetailsForEnsUsername*(self: Module, ensUsername: string, address: stri expirationTime: int) = self.view.setDetailsForEnsUsername(ensUsername, address, pubkey, isStatus, expirationTime) -method fetchGasPrice*(self: Module) = - self.controller.fetchGasPrice() - -method gasPriceFetched*(self: Module, gasPrice: string) = - self.view.setGasPrice(gasPrice) - method setPubKeyGasEstimate*(self: Module, ensUsername: string, address: string): int = return self.controller.setPubKeyGasEstimate(ensUsername, address) method setPubKey*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string, - maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string = - let response = self.controller.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) + maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string = + let response = self.controller.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) if(response.len == 0): info "expected response is empty", methodName="setPubKey" return @@ -182,8 +179,8 @@ method registerEnsGasEstimate*(self: Module, ensUsername: string, address: strin return self.controller.registerEnsGasEstimate(ensUsername, address) method registerEns*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string, - maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string = - let response = self.controller.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) + maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string = + let response = self.controller.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) let responseObj = response.parseJson if (responseObj.kind != JObject): @@ -238,5 +235,8 @@ method getGasEthValue*(self: Module, gweiValue: string, gasLimit: string): strin method getStatusToken*(self: Module): string = return self.controller.getStatusToken() +method getChainIdForEns*(self: Module): int = + return self.controller.getChainIdForEns() + method setPrefferedEnsUsername*(self: Module, ensUsername: string) = self.controller.setPreferredName(ensUsername) diff --git a/src/app/modules/main/profile_section/ens_usernames/view.nim b/src/app/modules/main/profile_section/ens_usernames/view.nim index b53f0f1944..3dbdbd3dd8 100644 --- a/src/app/modules/main/profile_section/ens_usernames/view.nim +++ b/src/app/modules/main/profile_section/ens_usernames/view.nim @@ -11,7 +11,6 @@ QtObject: modelVariant: QVariant etherscanLink: string signingPhrase: string - gasPrice: string proc delete*(self: View) = self.model.delete @@ -24,7 +23,6 @@ QtObject: result.model = newModel() result.modelVariant = newQVariant(result.model) result.delegate = delegate - result.gasPrice = "0" proc load*(self: View, link: string, signingPhrase: string) = self.etherscanLink = link @@ -66,9 +64,6 @@ QtObject: self.loading(false) self.detailsObtained(ensUsername, address, pubkey, isStatus, expirationTime) - proc fetchGasPrice*(self: View) {.slot.} = - self.delegate.fetchGasPrice() - proc transactionWasSent(self: View, txResult: string) {.signal.} proc emitTransactionWasSentSignal*(self: View, txResult: string) = self.transactionWasSent(txResult) @@ -77,8 +72,8 @@ QtObject: return self.delegate.setPubKeyGasEstimate(ensUsername, address) proc setPubKey*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string, - maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.slot.} = - return self.delegate.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) + maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.slot.} = + return self.delegate.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) proc getEtherscanLink*(self: View): string {.slot.} = return self.etherscanLink @@ -86,17 +81,6 @@ QtObject: proc getSigningPhrase*(self: View): string {.slot.} = return self.signingPhrase - proc gasPriceChanged(self: View) {.signal.} - proc getGasPrice(self: View): string {.slot.} = - return self.gasPrice - QtProperty[string] gasPrice: - read = getGasPrice - notify = gasPriceChanged - - proc setGasPrice*(self: View, gasPrice: string) = # this is not a slot - self.gasPrice = gasPrice - self.gasPriceChanged() - proc usernameConfirmed(self: View, username: string) {.signal.} proc emitUsernameConfirmedSignal*(self: View, ensUsername: string) = self.usernameConfirmed(ensUsername) @@ -124,8 +108,8 @@ QtObject: return self.delegate.registerEnsGasEstimate(ensUsername, address) proc registerEns*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string, - maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.slot.} = - return self.delegate.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) + maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.slot.} = + return self.delegate.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) proc getSNTBalance*(self: View): string {.slot.} = return self.delegate.getSNTBalance() @@ -145,5 +129,8 @@ QtObject: proc getStatusToken*(self: View): string {.slot.} = return self.delegate.getStatusToken() + proc getChainIdForEns*(self: View): int {.slot.} = + return self.delegate.getChainIdForEns() + proc setPrefferedEnsUsername*(self: View, ensUsername: string) {.slot.} = self.delegate.setPrefferedEnsUsername(ensUsername) diff --git a/src/app/modules/main/profile_section/module.nim b/src/app/modules/main/profile_section/module.nim index 73c228354d..6c6a1f4f59 100644 --- a/src/app/modules/main/profile_section/module.nim +++ b/src/app/modules/main/profile_section/module.nim @@ -16,6 +16,7 @@ import ../../../../app_service/service/mailservers/service as mailservers_servic import ../../../../app_service/service/chat/service as chat_service import ../../../../app_service/service/stickers/service as stickersService import ../../../../app_service/service/ens/service as ens_service +import ../../../../app_service/service/network/service as network_service import ../../../../app_service/service/wallet_account/service as wallet_account_service import ../../../../app_service/service/general/service as general_service import ../../../../app_service/service/community/service as community_service @@ -72,7 +73,8 @@ proc newModule*(delegate: delegate_interface.AccessInterface, ensService: ens_service.Service, walletAccountService: wallet_account_service.Service, generalService: general_service.Service, - communityService: community_service.Service + communityService: community_service.Service, + networkService: network_service.Service, ): Module = result = Module() result.delegate = delegate @@ -91,7 +93,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, result.syncModule = sync_module.newModule(result, events, settingsService, mailserversService) result.notificationsModule = notifications_module.newModule(result, events, settingsService, chatService, contactsService) result.ensUsernamesModule = ens_usernames_module.newModule( - result, events, settingsService, ensService, walletAccountService + result, events, settingsService, ensService, walletAccountService, networkService ) result.communitiesModule = communities_module.newModule(result, communityService) diff --git a/src/app/modules/main/stickers/controller.nim b/src/app/modules/main/stickers/controller.nim index 1249aa9007..883863ed08 100644 --- a/src/app/modules/main/stickers/controller.nim +++ b/src/app/modules/main/stickers/controller.nim @@ -7,6 +7,7 @@ import ../../../../app_service/service/node/service as node_service import ../../../../app_service/service/stickers/service as stickers_service import ../../../../app_service/service/token/service import ../../../../app_service/service/settings/service as settings_service +import ../../../../app_service/service/network/service as network_service import ../../../../app_service/service/eth/utils as eth_utils import ../../../../app_service/service/wallet_account/service as wallet_account_service @@ -17,6 +18,7 @@ type events: EventEmitter stickerService: stickers_service.Service settingsService: settings_service.Service + networkService: network_service.Service walletAccountService: wallet_account_service.Service disconnected: bool @@ -29,13 +31,15 @@ proc newController*( events: EventEmitter, stickerService: stickers_service.Service, settingsService: settings_service.Service, - walletAccountService: wallet_account_service.Service + walletAccountService: wallet_account_service.Service, + networkService: network_service.Service, ): Controller = result = Controller() result.delegate = delegate result.events = events result.stickerService = stickerService result.settingsService = settingsService + result.networkService = networkService result.walletAccountService = walletAccountService result.disconnected = false @@ -76,10 +80,6 @@ proc init*(self: Controller) = self.events.on(SIGNAL_ALL_STICKER_PACKS_LOADED) do(e: Args): self.delegate.allPacksLoaded() - self.events.on(SIGNAL_GAS_PRICE_FETCHED) do(e:Args): - let args = GasPriceArgs(e) - self.delegate.gasPriceFetched(args.gasPrice) - self.events.on(SIGNAL_STICKER_GAS_ESTIMATED) do(e: Args): let args = StickerGasEstimatedArgs(e) self.delegate.gasEstimateReturned(args.estimate, args.uuid) @@ -92,8 +92,8 @@ proc init*(self: Controller) = let args = StickerTransactionArgs(e) self.delegate.stickerTransactionReverted(args.transactionType, args.packID, args.transactionHash, args.revertReason) -proc buy*(self: Controller, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): tuple[response: string, success: bool] = - self.stickerService.buy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) +proc buy*(self: Controller, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): tuple[response: string, success: bool] = + self.stickerService.buy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) proc estimate*(self: Controller, packId: string, address: string, price: string, uuid: string) = self.stickerService.estimate(packId, address, price, uuid) @@ -148,6 +148,9 @@ proc getCurrentCurrency*(self: Controller): string = proc getPrice*(self: Controller, crypto: string, fiat: string): float64 = return self.walletAccountService.getPrice(crypto, fiat) +proc getChainIdForStickers*(self: Controller): int = + return self.networkService.getNetworkForStickers().chainId + proc getStatusToken*(self: Controller): string = let token = self.stickerService.getStatusToken() @@ -156,7 +159,4 @@ proc getStatusToken*(self: Controller): string = "symbol": token.symbol, "address": token.addressAsString() } - return $jsonObj - -proc fetchGasPrice*(self: Controller) = - self.stickerService.fetchGasPrice() + return $jsonObj \ No newline at end of file diff --git a/src/app/modules/main/stickers/io_interface.nim b/src/app/modules/main/stickers/io_interface.nim index c3c87e0af7..1a6b47307f 100644 --- a/src/app/modules/main/stickers/io_interface.nim +++ b/src/app/modules/main/stickers/io_interface.nim @@ -19,7 +19,7 @@ method isLoaded*(self: AccessInterface): bool {.base.} = method viewDidLoad*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method buy*(self: AccessInterface, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): tuple[response: string, success: bool] {.base.} = +method buy*(self: AccessInterface, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): tuple[response: string, success: bool] {.base.} = raise newException(ValueError, "No implementation available") method getInstalledStickerPacks*(self: AccessInterface): Table[string, StickerPackDto] {.base.} = @@ -89,10 +89,7 @@ method getGasEthValue*(self: AccessInterface, gweiValue: string, gasLimit: strin method getStatusToken*(self: AccessInterface): string {.base.} = raise newException(ValueError, "No implementation available") -method fetchGasPrice*(self: AccessInterface) {.base.} = - raise newException(ValueError, "No implementation available") - -method gasPriceFetched*(self: AccessInterface, gasPrice: string) {.base.} = +method getChainIdForStickers*(self: AccessInterface): int {.base.} = raise newException(ValueError, "No implementation available") method stickerTransactionConfirmed*(self: AccessInterface, trxType: string, packID: string, transactionHash: string) {.base.} = diff --git a/src/app/modules/main/stickers/module.nim b/src/app/modules/main/stickers/module.nim index 8185e78242..cf0db50825 100644 --- a/src/app/modules/main/stickers/module.nim +++ b/src/app/modules/main/stickers/module.nim @@ -5,6 +5,7 @@ import ../../../global/global_singleton import ../../../core/eventemitter import ../../../../app_service/service/stickers/service as stickers_service import ../../../../app_service/service/settings/service as settings_service +import ../../../../app_service/service/network/service as network_service import ../../../../app_service/common/conversion as service_conversion import ../../../../app_service/service/wallet_account/service as wallet_account_service @@ -19,17 +20,18 @@ type moduleLoaded: bool proc newModule*( - delegate: delegate_interface.AccessInterface, - events: EventEmitter, - stickersService: stickers_service.Service, - settingsService: settings_Service.Service, - walletAccountService: wallet_account_service.Service - ): Module = + delegate: delegate_interface.AccessInterface, + events: EventEmitter, + stickersService: stickers_service.Service, + settingsService: settings_Service.Service, + walletAccountService: wallet_account_service.Service, + networkService: network_service.Service, +): Module = result = Module() result.delegate = delegate result.view = newView(result) result.viewVariant = newQVariant(result.view) - result.controller = controller.newController(result, events, stickersService, settingsService, walletAccountService) + result.controller = controller.newController(result, events, stickersService, settingsService, walletAccountService, networkService) result.moduleLoaded = false singletonInstance.engine.setRootContextProperty("stickersModule", result.viewVariant) @@ -50,8 +52,8 @@ method viewDidLoad*(self: Module) = self.moduleLoaded = true self.delegate.stickersDidLoad() -method buy*(self: Module, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): tuple[response: string, success: bool] = - return self.controller.buy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) +method buy*(self: Module, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): tuple[response: string, success: bool] = + return self.controller.buy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) method getInstalledStickerPacks*(self: Module): Table[string, StickerPackDto] = self.controller.getInstalledStickerPacks() @@ -167,11 +169,8 @@ method getGasEthValue*(self: Module, gweiValue: string, gasLimit: string): strin method getStatusToken*(self: Module): string = return self.controller.getStatusToken() -method fetchGasPrice*(self: Module) = - self.controller.fetchGasPrice() - -method gasPriceFetched*(self: Module, gasPrice: string) = - self.view.setGasPrice(gasPrice) +method getChainIdForStickers*(self: Module): int = + return self.controller.getChainIdForStickers() method stickerTransactionConfirmed*(self: Module, trxType: string, packID: string, transactionHash: string) = self.view.stickerPacks.updateStickerPackInList(packID, true, false) diff --git a/src/app/modules/main/stickers/view.nim b/src/app/modules/main/stickers/view.nim index c43948e13f..536128432a 100644 --- a/src/app/modules/main/stickers/view.nim +++ b/src/app/modules/main/stickers/view.nim @@ -13,7 +13,6 @@ QtObject: recentStickers*: StickerList signingPhrase: string stickersMarketAddress: string - gasPrice: string proc delete*(self: View) = self.QObject.delete @@ -28,7 +27,6 @@ QtObject: proc load*(self: View, signingPhrase: string, stickersMarketAddress: string) = self.signingPhrase = signingPhrase self.stickersMarketAddress = stickersMarketAddress - self.gasPrice = "0" self.delegate.viewDidLoad() proc addStickerPackToList*(self: View, stickerPack: PackItem, isInstalled, isBought, isPending: bool) = @@ -63,8 +61,8 @@ QtObject: proc gasEstimateReturned*(self: View, estimate: int, uuid: string) {.signal.} - proc buy*(self: View, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.slot.} = - let responseTuple = self.delegate.buy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) + proc buy*(self: View, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): string {.slot.} = + let responseTuple = self.delegate.buy(packId, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) let response = responseTuple.response let success = responseTuple.success if success: @@ -139,6 +137,9 @@ QtObject: proc getSNTBalance*(self: View): string {.slot.} = return self.delegate.getSNTBalance() + proc getChainIdForStickers*(self: View): int {.slot.} = + return self.delegate.getChainIdForStickers() + proc getWalletDefaultAddress*(self: View): string {.slot.} = return self.delegate.getWalletDefaultAddress() @@ -154,21 +155,6 @@ QtObject: proc getStatusToken*(self: View): string {.slot.} = return self.delegate.getStatusToken() - proc fetchGasPrice*(self: View) {.slot.} = - self.delegate.fetchGasPrice() - - proc gasPriceChanged(self: View) {.signal.} - proc getGasPrice(self: View): string {.slot.} = - return self.gasPrice - - QtProperty[string] gasPrice: - read = getGasPrice - notify = gasPriceChanged - - proc setGasPrice*(self: View, gasPrice: string) = # this is not a slot - self.gasPrice = gasPrice - self.gasPriceChanged() - proc transactionCompleted(self: View, success: bool, txHash: string, packID: string, trxType: string, revertReason: string) {.signal.} proc emitTransactionCompletedSignal*(self: View, success: bool, txHash: string, packID: string, trxType: string, diff --git a/src/app/modules/main/wallet_section/accounts/module.nim b/src/app/modules/main/wallet_section/accounts/module.nim index 71665441e9..19efef3ade 100644 --- a/src/app/modules/main/wallet_section/accounts/module.nim +++ b/src/app/modules/main/wallet_section/accounts/module.nim @@ -1,4 +1,4 @@ -import NimQml, sequtils, sugar +import tables, NimQml, sequtils, sugar import ./io_interface, ./view, ./item, ./controller import ../io_interface as delegate_interface @@ -47,8 +47,11 @@ method refreshWalletAccounts*(self: Module) = t.name, t.symbol, t.totalBalance.balance, - t.address, t.totalBalance.currencyBalance, + t.enabledNetworkBalance.balance, + t.enabledNetworkBalance.currencyBalance, + t.visible, + toSeq(t.balancesPerChain.values), )) ) diff --git a/src/app/modules/main/wallet_section/controller.nim b/src/app/modules/main/wallet_section/controller.nim index c310d19218..e16bdf640d 100644 --- a/src/app/modules/main/wallet_section/controller.nim +++ b/src/app/modules/main/wallet_section/controller.nim @@ -43,8 +43,5 @@ proc getCurrencyBalance*(self: Controller): float64 = proc updateCurrency*(self: Controller, currency: string) = self.walletAccountService.updateCurrency(currency) -proc isEIP1559Enabled*(self: Controller): bool = - return self.networkService.isEIP1559Enabled() - proc getIndex*(self: Controller, address: string): int = return self.walletAccountService.getIndex(address) \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/current_account/module.nim b/src/app/modules/main/wallet_section/current_account/module.nim index f3250a9d25..d8c2794fe8 100644 --- a/src/app/modules/main/wallet_section/current_account/module.nim +++ b/src/app/modules/main/wallet_section/current_account/module.nim @@ -1,4 +1,4 @@ -import NimQml, Tables +import NimQml, Tables, sequtils import ../../../../global/global_singleton import ../../../../core/eventemitter @@ -72,9 +72,18 @@ proc setAssetsAndBalance(self: Module, tokens: seq[WalletTokenDto]) = var totalCurrencyBalanceForAllAssets = 0.0 var items: seq[Item] for t in tokens: - let item = token_item.initItem(t.name, t.symbol, t.totalBalance.balance, t.address, t.totalBalance.currencyBalance) + let item = token_item.initItem( + t.name, + t.symbol, + t.totalBalance.balance, + t.totalBalance.currencyBalance, + t.enabledNetworkBalance.balance, + t.enabledNetworkBalance.currencybalance, + t.visible, + toSeq(t.balancesPerChain.values), + ) items.add(item) - totalCurrencyBalanceForAllAssets += t.totalBalance.currencyBalance + totalCurrencyBalanceForAllAssets += t.enabledNetworkBalance.currencybalance self.view.getAssetsModel().setItems(items) self.view.setCurrencyBalance(totalCurrencyBalanceForAllAssets) diff --git a/src/app/modules/main/wallet_section/io_interface.nim b/src/app/modules/main/wallet_section/io_interface.nim index e1edcd1580..fdcbf90bd5 100644 --- a/src/app/modules/main/wallet_section/io_interface.nim +++ b/src/app/modules/main/wallet_section/io_interface.nim @@ -48,8 +48,5 @@ method transactionsModuleDidLoad*(self: AccessInterface) {.base.} = method savedAddressesModuleDidLoad*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method isEIP1559Enabled*(self: AccessInterface): bool {.base.} = - raise newException(ValueError, "No implementation available") - method buySellCryptoModuleDidLoad*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/wallet_section/module.nim b/src/app/modules/main/wallet_section/module.nim index 600cd59b34..4e78f9e7c4 100644 --- a/src/app/modules/main/wallet_section/module.nim +++ b/src/app/modules/main/wallet_section/module.nim @@ -63,7 +63,7 @@ proc newModule*( result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService) result.collectiblesModule = collectibles_module.newModule(result, events, collectibleService, walletAccountService) result.currentAccountModule = current_account_module.newModule(result, events, walletAccountService) - result.transactionsModule = transactions_module.newModule(result, events, transactionService, walletAccountService) + result.transactionsModule = transactions_module.newModule(result, events, transactionService, walletAccountService, networkService) result.savedAddressesModule = saved_addresses_module.newModule(result, events, savedAddressService) result.buySellCryptoModule = buy_sell_crypto_module.newModule(result, events, transactionService) @@ -93,9 +93,6 @@ method switchAccountByAddress*(self: Module, address: string) = method setTotalCurrencyBalance*(self: Module) = self.view.setTotalCurrencyBalance(self.controller.getCurrencyBalance()) -method isEIP1559Enabled*(self: Module): bool = - return self.controller.isEIP1559Enabled() - method load*(self: Module) = singletonInstance.engine.setRootContextProperty("walletSection", newQVariant(self.view)) diff --git a/src/app/modules/main/wallet_section/transactions/controller.nim b/src/app/modules/main/wallet_section/transactions/controller.nim index f3db6b7323..37fd0544b1 100644 --- a/src/app/modules/main/wallet_section/transactions/controller.nim +++ b/src/app/modules/main/wallet_section/transactions/controller.nim @@ -1,6 +1,7 @@ import NimQml, json, json_serialization, stint, tables, sugar, sequtils import io_interface import ../../../../../app_service/service/transaction/service as transaction_service +import ../../../../../app_service/service/network/service as network_service import ../../../../../app_service/service/wallet_account/service as wallet_account_service import ../../../../core/[main] @@ -11,6 +12,7 @@ type delegate: io_interface.AccessInterface events: EventEmitter transactionService: transaction_service.Service + networkService: network_service.Service walletAccountService: wallet_account_service.Service # Forward declaration @@ -21,13 +23,15 @@ proc newController*( delegate: io_interface.AccessInterface, events: EventEmitter, transactionService: transaction_service.Service, - walletAccountService: wallet_account_service.Service + walletAccountService: wallet_account_service.Service, + networkService: network_service.Service, ): Controller = result = Controller() result.events = events result.delegate = delegate result.transactionService = transactionService result.walletAccountService = walletAccountService + result.networkService = networkService proc delete*(self: Controller) = discard @@ -79,22 +83,26 @@ proc getAccountByAddress*(self: Controller, address: string): WalletAccountDto = proc loadTransactions*(self: Controller, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false) = self.transactionService.loadTransactions(address, toBlock, limit, loadMore) -proc estimateGas*(self: Controller, from_addr: string, to: string, assetAddress: string, value: string, data: string): string = - result = self.transactionService.estimateGas(from_addr, to, assetAddress, value, data) +proc estimateGas*(self: Controller, from_addr: string, to: string, assetSymbol: string, value: string, data: string): string = + result = self.transactionService.estimateGas(from_addr, to, assetSymbol, value, data) -proc transferEth*(self: Controller, from_addr: string, to_addr: string, value: string, - gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, - password: string, uuid: string): bool = - result = self.transactionService.transferEth(from_addr, to_addr, value, gas, gasPrice, - maxPriorityFeePerGas, maxFeePerGas, password, uuid) - -proc transferTokens*(self: Controller, from_addr: string, to_addr: string, contractAddress: string, +proc transfer*(self: Controller, from_addr: string, to_addr: string, tokenSymbol: string, value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,maxFeePerGas: string, - password: string, uuid: string + password: string, chainId: string, uuid: string, eip1559Enabled: bool, ): bool = - result = self.transactionService.transferTokens(from_addr, to_addr, contractAddress, value, gas, - gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, uuid) + result = self.transactionService.transfer(from_addr, to_addr, tokenSymbol, value, gas, + gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled) -proc suggestedFees*(self: Controller): string = - let suggestedFees = self.transactionService.suggestedFees() - return suggestedFees.toJson() \ No newline at end of file +proc suggestedFees*(self: Controller, chainId: int): string = + let suggestedFees = self.transactionService.suggestedFees(chainId) + return suggestedFees.toJson() + +proc suggestedRoutes*(self: Controller, account: string, amount: float64, token: string): string = + let suggestedRoutes = self.transactionService.suggestedRoutes(account, amount, token) + return suggestedRoutes.toJson() + +proc getChainIdForChat*(self: Controller): int = + return self.networkService.getNetworkForChat().chainId + +proc getChainIdForBrowser*(self: Controller): int = + return self.networkService.getNetworkForBrowser().chainId \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/transactions/io_interface.nim b/src/app/modules/main/wallet_section/transactions/io_interface.nim index 79f2cb9cf3..d0582231cd 100644 --- a/src/app/modules/main/wallet_section/transactions/io_interface.nim +++ b/src/app/modules/main/wallet_section/transactions/io_interface.nim @@ -40,24 +40,28 @@ method setHistoryFetchState*(self: AccessInterface, addresses: seq[string], isFe method setIsNonArchivalNode*(self: AccessInterface, isNonArchivalNode: bool) {.base.} = raise newException(ValueError, "No implementation available") -method estimateGas*(self: AccessInterface, from_addr: string, to: string, assetAddress: string, value: string, data: string): string {.base.} = +method estimateGas*(self: AccessInterface, from_addr: string, to: string, assetSymbol: string, value: string, data: string): string {.base.} = raise newException(ValueError, "No implementation available") -method transferEth*(self: AccessInterface, from_addr: string, to_addr: string, value: string, - gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, - password: string, uuid: string): bool {.base.} = - raise newException(ValueError, "No implementation available") - -method transferTokens*(self: AccessInterface, from_addr: string, to_addr: string, - contractAddress: string, value: string, gas: string, gasPrice: string, +method transfer*(self: AccessInterface, from_addr: string, to_addr: string, + tokenSymbol: string, value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, - uuid: string): bool {.base.} = + chainId: string, uuid: string, eip1559Enabled: bool): bool {.base.} = raise newException(ValueError, "No implementation available") method transactionWasSent*(self: AccessInterface, result: string) {.base.} = raise newException(ValueError, "No implementation available") -method suggestedFees*(self: AccessInterface): string {.base.} = +method suggestedFees*(self: AccessInterface, chainId: int): string {.base.} = + raise newException(ValueError, "No implementation available") + +method suggestedRoutes*(self: AccessInterface, account: string, amount: float64, token: string): string {.base.} = + raise newException(ValueError, "No implementation available") + +method getChainIdForChat*(self: AccessInterface): int = + raise newException(ValueError, "No implementation available") + +method getChainIdForBrowser*(self: AccessInterface): int = raise newException(ValueError, "No implementation available") # View Delegate Interface diff --git a/src/app/modules/main/wallet_section/transactions/module.nim b/src/app/modules/main/wallet_section/transactions/module.nim index 88ad7df484..efad932f43 100644 --- a/src/app/modules/main/wallet_section/transactions/module.nim +++ b/src/app/modules/main/wallet_section/transactions/module.nim @@ -6,6 +6,7 @@ import ../../../../global/global_singleton import ../../../../core/eventemitter import ../../../../../app_service/service/transaction/service as transaction_service import ../../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../../app_service/service/network/service as network_service export io_interface @@ -25,12 +26,13 @@ proc newModule*( delegate: delegate_interface.AccessInterface, events: EventEmitter, transactionService: transaction_service.Service, - walletAccountService: wallet_account_service.Service + walletAccountService: wallet_account_service.Service, + networkService: network_service.Service, ): Module = result = Module() result.delegate = delegate result.view = newView(result) - result.controller = controller.newController(result, events, transactionService, walletAccountService) + result.controller = controller.newController(result, events, transactionService, walletAccountService, networkService) result.moduleLoaded = false method delete*(self: Module) = @@ -82,26 +84,29 @@ method setTrxHistoryResult*(self: Module, transactions: seq[TransactionDto], add method setHistoryFetchState*(self: Module, addresses: seq[string], isFetching: bool) = self.view.setHistoryFetchStateForAccounts(addresses, isFetching) -method estimateGas*(self: Module, from_addr: string, to: string, assetAddress: string, value: string, data: string): string = - result = self.controller.estimateGas(from_addr, to, assetAddress, value, data) +method estimateGas*(self: Module, from_addr: string, to: string, assetSymbol: string, value: string, data: string): string = + result = self.controller.estimateGas(from_addr, to, assetSymbol, value, data) method setIsNonArchivalNode*(self: Module, isNonArchivalNode: bool) = self.view.setIsNonArchivalNode(isNonArchivalNode) -method transferEth*(self: Module, from_addr: string, to_addr: string, value: string, gas: string, - gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, - uuid: string): bool = - result = self.controller.transferEth(from_addr, to_addr, value, gas, gasPrice, - maxPriorityFeePerGas, maxFeePerGas, password, uuid) - -method transferTokens*(self: Module, from_addr: string, to_addr: string, contractAddress: string, +method transfer*(self: Module, from_addr: string, to_addr: string, tokenSymbol: string, value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, - maxFeePerGas: string, password: string, uuid: string): bool = - result = self.controller.transferTokens(from_addr, to_addr, contractAddress, value, gas, gasPrice, - maxPriorityFeePerGas, maxFeePerGas, password, uuid) + maxFeePerGas: string, password: string, chainId: string, uuid: string, eip1559Enabled: bool): bool = + result = self.controller.transfer(from_addr, to_addr, tokenSymbol, value, gas, gasPrice, + maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled) method transactionWasSent*(self: Module, result: string) = self.view.transactionWasSent(result) -method suggestedFees*(self: Module): string = - return self.controller.suggestedFees() +method suggestedFees*(self: Module, chainId: int): string = + return self.controller.suggestedFees(chainId) + +method suggestedRoutes*(self: Module, account: string, amount: float64, token: string): string = + return self.controller.suggestedRoutes(account, amount, token) + +method getChainIdForChat*(self: Module): int = + return self.controller.getChainIdForChat() + +method getChainIdForBrowser*(self: Module): int = + return self.controller.getChainIdForBrowser() \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/transactions/view.nim b/src/app/modules/main/wallet_section/transactions/view.nim index 92448f1447..a0d302357e 100644 --- a/src/app/modules/main/wallet_section/transactions/view.nim +++ b/src/app/modules/main/wallet_section/transactions/view.nim @@ -1,4 +1,4 @@ -import NimQml, tables, stint, json, strformat, sequtils +import NimQml, tables, stint, json, strformat, sequtils, strutils import ./item import ./model @@ -108,25 +108,35 @@ QtObject: read = getIsNonArchivalNode notify = isNonArchivalNodeChanged - proc estimateGas*(self: View, from_addr: string, to: string, assetAddress: string, value: string, data: string): string {.slot.} = - result = self.delegate.estimateGas(from_addr, to, assetAddress, value, data) + proc estimateGas*(self: View, from_addr: string, to: string, assetSymbol: string, value: string, data: string): string {.slot.} = + result = self.delegate.estimateGas(from_addr, to, assetSymbol, value, data) proc transactionSent*(self: View, txResult: string) {.signal.} proc transactionWasSent*(self: View,txResult: string) {.slot} = self.transactionSent(txResult) - proc transferEth*(self: View, from_addr: string, to_addr: string, value: string, gas: string, - gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, - uuid: string): bool {.slot.} = - result = self.delegate.transferEth(from_addr, to_addr, value, gas, gasPrice, - maxPriorityFeePerGas, maxFeePerGas, password, uuid) - - proc transferTokens*(self: View, from_addr: string, to_addr: string, contractAddress: string, + proc transfer*(self: View, from_addr: string, to_addr: string, tokenSymbol: string, value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, - maxFeePerGas: string, password: string, uuid: string): bool {.slot.} = - result = self.delegate.transferTokens(from_addr, to_addr, contractAddress, value, gas, gasPrice, - maxPriorityFeePerGas, maxFeePerGas, password, uuid) + maxFeePerGas: string, password: string, chainId: string, uuid: string, eip1559Enabled: bool): bool {.slot.} = + result = self.delegate.transfer(from_addr, to_addr, tokenSymbol, value, gas, gasPrice, + maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled) - proc suggestedFees*(self: View): string {.slot.} = - return self.delegate.suggestedFees() + proc suggestedFees*(self: View, chainId: int): string {.slot.} = + return self.delegate.suggestedFees(chainId) + + proc suggestedRoutes*(self: View, account: string, amount: string, token: string): string {.slot.} = + var parsedAmount = 0.0 + + try: + parsedAmount = parsefloat(amount) + except: + discard + + return self.delegate.suggestedRoutes(account, parsedAmount, token) + + proc getChainIdForChat*(self: View): int {.slot.} = + return self.delegate.getChainIdForChat() + + proc getChainIdForBrowser*(self: View): int {.slot.} = + return self.delegate.getChainIdForBrowser() \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/view.nim b/src/app/modules/main/wallet_section/view.nim index 8803debaa7..4c79c4d392 100644 --- a/src/app/modules/main/wallet_section/view.nim +++ b/src/app/modules/main/wallet_section/view.nim @@ -74,7 +74,4 @@ QtObject: self.currentCurrency = currency self.signingPhrase = signingPhrase self.isMnemonicBackedUp = mnemonicBackedUp - self.currentCurrencyChanged() - - proc isEIP1559Enabled*(self: View): QVariant {.slot.} = - return newQVariant(self.delegate.isEIP1559Enabled()) \ No newline at end of file + self.currentCurrencyChanged() \ No newline at end of file diff --git a/src/app/modules/shared_models/balance_model.nim b/src/app/modules/shared_models/balance_model.nim new file mode 100644 index 0000000000..fc621836c9 --- /dev/null +++ b/src/app/modules/shared_models/balance_model.nim @@ -0,0 +1,86 @@ +import NimQml, Tables, strutils, strformat + +import ../../../app_service/service/wallet_account/dto + +type + ModelRole {.pure.} = enum + ChainId = UserRole + 1, + Address + Balance + CurrencyBalance + +QtObject: + type + BalanceModel* = ref object of QAbstractListModel + items*: seq[BalanceDto] + + proc delete(self: BalanceModel) = + self.items = @[] + self.QAbstractListModel.delete + + proc setup(self: BalanceModel) = + self.QAbstractListModel.setup + + proc newModel*(): BalanceModel = + new(result, delete) + result.setup + + proc `$`*(self: BalanceModel): string = + for i in 0 ..< self.items.len: + result &= fmt"""[{i}]:({$self.items[i]})""" + + proc countChanged(self: BalanceModel) {.signal.} + + proc getCount*(self: BalanceModel): int {.slot.} = + self.items.len + + QtProperty[int] count: + read = getCount + notify = countChanged + + method rowCount(self: BalanceModel, index: QModelIndex = nil): int = + return self.items.len + + method roleNames(self: BalanceModel): Table[int, string] = + { + ModelRole.ChainId.int:"chainId", + ModelRole.Address.int:"address", + ModelRole.Balance.int:"balance", + ModelRole.CurrencyBalance.int:"currencyBalance", + }.toTable + + method data(self: BalanceModel, index: QModelIndex, role: int): QVariant = + if (not index.isValid): + return + + if (index.row < 0 or index.row >= self.items.len): + return + + let item = self.items[index.row] + let enumRole = role.ModelRole + + case enumRole: + of ModelRole.ChainId: + result = newQVariant(item.chainId) + of ModelRole.Address: + result = newQVariant(item.address) + of ModelRole.Balance: + result = newQVariant(item.balance) + of ModelRole.CurrencyBalance: + result = newQVariant(item.currencyBalance) + + proc rowData(self: BalanceModel, index: int, column: string): string {.slot.} = + if (index >= self.items.len): + return + let item = self.items[index] + case column: + of "chainId": result = $item.chainId + of "address": result = $item.address + of "balance": result = $item.balance + of "currencyBalance": result = $item.currencyBalance + + proc setItems*(self: BalanceModel, items: seq[BalanceDto]) = + self.beginResetModel() + self.items = items + self.endResetModel() + self.countChanged() diff --git a/src/app/modules/shared_models/token_item.nim b/src/app/modules/shared_models/token_item.nim index 9d4516a255..871405d002 100644 --- a/src/app/modules/shared_models/token_item.nim +++ b/src/app/modules/shared_models/token_item.nim @@ -1,27 +1,47 @@ import strformat +import ../../../app_service/service/wallet_account/dto +import ./balance_model as balance_model + type Item* = object name: string symbol: string - balance: float - address: string - currencyBalance: float + totalBalance: float + totalCurrencyBalance: float + enabledNetworkCurrencyBalance: float + enabledNetworkBalance: float + networkVisible: bool + balances: balance_model.BalanceModel -proc initItem*(name, symbol: string, balance: float, address: string, currencyBalance: float): Item = +proc initItem*( + name, symbol: string, + totalBalance: float, + totalCurrencyBalance: float, + enabledNetworkBalance: float, + enabledNetworkCurrencyBalance: float, + networkVisible: bool, + balances: seq[BalanceDto] +): Item = result.name = name result.symbol = symbol - result.balance = balance - result.address = address - result.currencyBalance = currencyBalance + result.totalBalance = totalBalance + result.totalCurrencyBalance = totalCurrencyBalance + result.enabledNetworkBalance = enabledNetworkBalance + result.enabledNetworkCurrencyBalance = enabledNetworkCurrencyBalance + result.networkVisible = networkVisible + result.balances = balance_model.newModel() + result.balances.setItems(balances) proc `$`*(self: Item): string = result = fmt"""AllTokensItem( name: {self.name}, symbol: {self.symbol}, - balance: {self.balance}, - address: {self.address}, - currencyBalance: {self.currencyBalance}, + totalBalance: {self.totalBalance}, + totalCurrencyBalance: {self.totalCurrencyBalance}, + enabledNetworkBalance: {self.enabledNetworkBalance}, + enabledNetworkCurrencyBalance: {self.enabledNetworkCurrencyBalance}, + networkVisible: {self.networkVisible}, ]""" proc getName*(self: Item): string = @@ -30,11 +50,20 @@ proc getName*(self: Item): string = proc getSymbol*(self: Item): string = return self.symbol -proc getBalance*(self: Item): float = - return self.balance +proc getTotalBalance*(self: Item): float = + return self.totalBalance -proc getAddress*(self: Item): string = - return self.address +proc getTotalCurrencyBalance*(self: Item): float = + return self.totalCurrencyBalance -proc getCurrencyBalance*(self: Item): float = - return self.currencyBalance +proc getEnabledNetworkBalance*(self: Item): float = + return self.enabledNetworkBalance + +proc getEnabledNetworkCurrencyBalance*(self: Item): float = + return self.enabledNetworkCurrencyBalance + +proc getNetworkVisible*(self: Item): bool = + return self.networkVisible + +proc getBalances*(self: Item): balance_model.BalanceModel = + return self.balances \ No newline at end of file diff --git a/src/app/modules/shared_models/token_model.nim b/src/app/modules/shared_models/token_model.nim index 37d1889ee8..887b4ac5be 100644 --- a/src/app/modules/shared_models/token_model.nim +++ b/src/app/modules/shared_models/token_model.nim @@ -6,9 +6,12 @@ type ModelRole {.pure.} = enum Name = UserRole + 1, Symbol - Balance - Address - CurrencyBalance + TotalBalance + TotalCurrencyBalance + EnabledNetworkCurrencyBalance + EnabledNetworkBalance + NetworkVisible + Balances QtObject: type @@ -46,9 +49,12 @@ QtObject: { ModelRole.Name.int:"name", ModelRole.Symbol.int:"symbol", - ModelRole.Balance.int:"balance", - ModelRole.Address.int:"address", - ModelRole.CurrencyBalance.int:"currencyBalance", + ModelRole.TotalBalance.int:"totalBalance", + ModelRole.TotalCurrencyBalance.int:"totalCurrencyBalance", + ModelRole.EnabledNetworkCurrencyBalance.int:"enabledNetworkCurrencyBalance", + ModelRole.EnabledNetworkBalance.int:"enabledNetworkBalance", + ModelRole.NetworkVisible.int:"networkVisible", + ModelRole.Balances.int:"balances", }.toTable method data(self: Model, index: QModelIndex, role: int): QVariant = @@ -66,12 +72,18 @@ QtObject: result = newQVariant(item.getName()) of ModelRole.Symbol: result = newQVariant(item.getSymbol()) - of ModelRole.Balance: - result = newQVariant(item.getBalance()) - of ModelRole.Address: - result = newQVariant(item.getAddress()) - of ModelRole.CurrencyBalance: - result = newQVariant(item.getCurrencyBalance()) + of ModelRole.TotalBalance: + result = newQVariant(item.getTotalBalance()) + of ModelRole.TotalCurrencyBalance: + result = newQVariant(item.getTotalCurrencyBalance()) + of ModelRole.EnabledNetworkCurrencyBalance: + result = newQVariant(item.getEnabledNetworkCurrencyBalance()) + of ModelRole.EnabledNetworkBalance: + result = newQVariant(item.getEnabledNetworkBalance()) + of ModelRole.NetworkVisible: + result = newQVariant(item.getNetworkVisible()) + of ModelRole.Balances: + result = newQVariant(item.getBalances()) proc rowData(self: Model, index: int, column: string): string {.slot.} = if (index >= self.items.len): @@ -80,12 +92,22 @@ QtObject: case column: of "name": result = $item.getName() of "symbol": result = $item.getSymbol() - of "balance": result = $item.getBalance() - of "address": result = $item.getAddress() - of "currencyBalance": result = $item.getCurrencyBalance() + of "totalBalance": result = $item.getTotalBalance() + of "totalCurrencyBalance": result = $item.getTotalCurrencyBalance() + of "enabledNetworkCurrencyBalance": result = $item.getEnabledNetworkCurrencyBalance() + of "enabledNetworkBalance": result = $item.getEnabledNetworkBalance() + of "networkVisible": result = $item.getNetworkVisible() proc setItems*(self: Model, items: seq[Item]) = self.beginResetModel() self.items = items self.endResetModel() self.countChanged() + + proc hasChain*(self: Model, index: int, chainId: int): bool {.slot.} = + let item = self.items[index] + for balance in item.getBalances().items: + if (balance.chainId == chainId): + return true + + return false diff --git a/src/app_service/common/network_constants.nim b/src/app_service/common/network_constants.nim index a2185be6aa..dd7cf00aaf 100644 --- a/src/app_service/common/network_constants.nim +++ b/src/app_service/common/network_constants.nim @@ -134,7 +134,7 @@ let NETWORKS* = %* [ "nativeCurrencyDecimals": 18, "isTest": true, "layer": 1, - "enabled": false, + "enabled": true, }, { "chainId": 4, diff --git a/src/app_service/service/collectible/service.nim b/src/app_service/service/collectible/service.nim index 872ea80523..959cd95b15 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 dto -import ../settings/service as settings_service +import ../network/service as network_service import ../../../backend/backend @@ -14,22 +14,22 @@ const limit = 200 type Service* = ref object of RootObj - settingsService: settings_service.Service + networkService: network_service.Service proc delete*(self: Service) = discard -proc newService*(settingsService: settings_service.Service): Service = +proc newService*(networkService: network_service.Service): Service = result = Service() - result.settingsService = settingsService + result.networkService = networkService proc init*(self: Service) = discard proc getCollections*(self: Service, address: string): seq[CollectionDto] = try: - let networkId = self.settingsService.getCurrentNetworkId() - let response = backend.getOpenseaCollectionsByOwner(networkId, address) + let chainId = self.networkService.getNetworkForCollectibles().chainId + let response = backend.getOpenseaCollectionsByOwner(chainId, address) return map(response.result.getElems(), proc(x: JsonNode): CollectionDto = x.toCollectionDto()) except Exception as e: let errDesription = e.msg @@ -38,8 +38,8 @@ proc getCollections*(self: Service, address: string): seq[CollectionDto] = proc getCollectibles*(self: Service, address: string, collectionSlug: string): seq[CollectibleDto] = try: - let networkId = self.settingsService.getCurrentNetworkId() - let response = backend.getOpenseaAssetsByOwnerAndCollection(networkId, address, collectionSlug, limit) + let chainId = self.networkService.getNetworkForCollectibles().chainId + let response = backend.getOpenseaAssetsByOwnerAndCollection(chainId, address, collectionSlug, limit) return map(response.result.getElems(), proc(x: JsonNode): CollectibleDto = x.toCollectibleDto()) except Exception as e: let errDesription = e.msg diff --git a/src/app_service/service/ens/async_tasks.nim b/src/app_service/service/ens/async_tasks.nim index a47ef957fc..49cfc63632 100644 --- a/src/app_service/service/ens/async_tasks.nim +++ b/src/app_service/service/ens/async_tasks.nim @@ -65,17 +65,4 @@ const ensUsernameDetailsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall. "isStatus": arg.isStatus, "expirationTime": expirationTime } - arg.finish(responseJson) - - -################################################# -# Async fetch gas price -################################################# - -const fetchGasPriceTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = - let arg = decode[QObjectTaskArg](argEncoded) - let response = status_eth.getGasPrice() - let responseJson = %* { - "gasPrice": response.result.getStr - } - arg.finish(responseJson) + arg.finish(responseJson) \ No newline at end of file diff --git a/src/app_service/service/ens/service.nim b/src/app_service/service/ens/service.nim index 70660d2bf1..504df6ab05 100644 --- a/src/app_service/service/ens/service.nim +++ b/src/app_service/service/ens/service.nim @@ -62,7 +62,6 @@ type # Signals which may be emitted by this service: const SIGNAL_ENS_USERNAME_AVAILABILITY_CHECKED* = "ensUsernameAvailabilityChecked" const SIGNAL_ENS_USERNAME_DETAILS_FETCHED* = "ensUsernameDetailsFetched" -const SIGNAL_GAS_PRICE_FETCHED* = "ensGasPriceFetched" const SIGNAL_ENS_TRANSACTION_CONFIRMED* = "ensTransactionConfirmed" const SIGNAL_ENS_TRANSACTION_REVERTED* = "ensTransactionReverted" @@ -183,7 +182,7 @@ QtObject: vptr: cast[ByteAddress](self.vptr), slot: "onEnsUsernameAvailabilityChecked", ensUsername: ensUsername, - chainId: self.settingsService.getCurrentNetworkId(), + chainId: self.networkService.getNetworkForEns().chainId, isStatus: isStatus, myPublicKey: self.settingsService.getPublicKey(), myWalletAddress: self.walletAccountService.getWalletAccount(0).address @@ -220,46 +219,18 @@ QtObject: vptr: cast[ByteAddress](self.vptr), slot: "onEnsUsernameDetailsFetched", ensUsername: ensUsername, - chainId: self.settingsService.getCurrentNetworkId(), + chainId: self.networkService.getNetworkForEns().chainId, isStatus: isStatus ) self.threadpool.start(arg) - proc onGasPriceFetched*(self: Service, response: string) {.slot.} = - let responseObj = response.parseJson - if (responseObj.kind != JObject): - info "expected response is not a json object", procName="onGasPriceFetched" - # notify view, this is important - self.events.emit(SIGNAL_GAS_PRICE_FETCHED, GasPriceArgs(gasPrice: "0")) - return - - var gasPriceHex: string - if(not responseObj.getProp("gasPrice", gasPriceHex)): - info "expected response doesn't contain gas price", procName="onGasPriceFetched" - # notify view, this is important - self.events.emit(SIGNAL_GAS_PRICE_FETCHED, GasPriceArgs(gasPrice: "0")) - return - - let gasPrice = $fromHex(Stuint[256], gasPriceHex) - let parsedGasPrice = parseFloat(wei2gwei(gasPrice)) - var data = GasPriceArgs(gasPrice: fmt"{parsedGasPrice:.3f}") - self.events.emit(SIGNAL_GAS_PRICE_FETCHED, data) - - proc fetchGasPrice*(self: Service) = - let arg = QObjectTaskArg( - tptr: cast[ByteAddress](fetchGasPriceTask), - vptr: cast[ByteAddress](self.vptr), - slot: "onGasPriceFetched" - ) - self.threadpool.start(arg) - proc extractCoordinates(self: Service, pubkey: string):tuple[x: string, y:string] = result = ("0x" & pubkey[4..67], "0x" & pubkey[68..131]) proc setPubKeyGasEstimate*(self: Service, ensUsername: string, address: string): int = try: let - chainId = self.settingsService.getCurrentNetworkId() + chainId = self.networkService.getNetworkForEns().chainId txData = ens_utils.buildTransaction(parseAddress(address), 0.u256) let resp = status_ens.setPubKeyEstimate(chainId, %txData, ensUsername, @@ -277,12 +248,12 @@ QtObject: gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, - password: string + password: string, + eip1559Enabled: bool, ): string = try: let - chainId = self.settingsService.getCurrentNetworkId() - eip1559Enabled = self.networkService.isEIP1559Enabled() + chainId = self.networkService.getNetworkForEns().chainId txData = ens_utils.buildTransaction(parseAddress(address), 0.u256, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas) @@ -303,7 +274,7 @@ QtObject: proc releaseEnsEstimate*(self: Service, ensUsername: string, address: string): int = try: let - chainId = self.settingsService.getCurrentNetworkId() + chainId = self.networkService.getNetworkForEns().chainId txData = ens_utils.buildTransaction(parseAddress(address), 0.u256) let resp = status_ens.releaseEstimate(chainId, %txData, ensUsername) @@ -313,8 +284,7 @@ QtObject: result = 100000 proc getEnsRegisteredAddress*(self: Service): string = - let networkType = self.settingsService.getCurrentNetwork().toNetworkType() - let networkDto = self.networkService.getNetwork(networkType) + let networkDto = self.networkService.getNetworkForEns() return status_ens.getRegistrarAddress(networkDto.chainId).result.getStr @@ -328,7 +298,7 @@ QtObject: ): string = try: let - chainId = self.settingsService.getCurrentNetworkId() + chainId = self.networkService.getNetworkForEns().chainId txData = ens_utils.buildTransaction(parseAddress(address), 0.u256, gas, gasPrice) let resp = status_ens.release(chainId, %txData, password, ensUsername) @@ -347,7 +317,7 @@ QtObject: proc registerENSGasEstimate*(self: Service, ensUsername: string, address: string): int = try: let - chainId = self.settingsService.getCurrentNetworkId() + chainId = self.networkService.getNetworkForEns().chainId txData = ens_utils.buildTransaction(parseAddress(address), 0.u256) let resp = status_ens.registerEstimate(chainId, %txData, ensUsername, @@ -358,10 +328,9 @@ QtObject: error "error occurred", procName="registerENSGasEstimate", msg = e.msg proc getStatusToken*(self: Service): TokenDto = - let networkType = self.settingsService.getCurrentNetwork().toNetworkType() - let networkDto = self.networkService.getNetwork(networkType) + let networkDto = self.networkService.getNetworkForEns() - return self.tokenService.findTokenBySymbol(networkDto, networkType.sntSymbol()) + return self.tokenService.findTokenBySymbol(networkDto, networkDto.sntSymbol()) proc registerEns*( self: Service, @@ -371,14 +340,12 @@ QtObject: gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, - password: string + password: string, + eip1559Enabled: bool, ): string = try: let - networkType = self.settingsService.getCurrentNetwork().toNetworkType() - network = self.networkService.getNetwork(networkType) - chainId = network.chainId - eip1559Enabled = self.networkService.isEIP1559Enabled() + chainId = self.networkService.getNetworkForEns().chainId txData = ens_utils.buildTransaction(parseAddress(address), 0.u256, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas) @@ -399,15 +366,14 @@ QtObject: proc getSNTBalance*(self: Service): string = let token = self.getStatusToken() let account = self.walletAccountService.getWalletAccount(0).address - let networkType = self.settingsService.getCurrentNetwork().toNetworkType() - let network = self.networkService.getNetwork(networkType) + let networkDto = self.networkService.getNetworkForEns() - let balances = status_go_backend.getTokensBalancesForChainIDs(@[network.chainId], @[account], @[token.addressAsString()]).result + let balances = status_go_backend.getTokensBalancesForChainIDs(@[networkDto.chainId], @[account], @[token.addressAsString()]).result return ens_utils.hex2Token(balances{account}{token.addressAsString()}.getStr, token.decimals) proc resourceUrl*(self: Service, username: string): (string, string, string) = try: - let chainId = self.settingsService.getCurrentNetworkId() + let chainId = self.networkService.getNetworkForEns().chainId let response = status_ens.resourceURL(chainId, username) return (response.result{"Scheme"}.getStr, response.result{"Host"}.getStr, response.result{"Path"}.getStr) except Exception as e: diff --git a/src/app_service/service/eth/dto/method_dto.nim b/src/app_service/service/eth/dto/method_dto.nim index be17974b42..f8b594a85e 100644 --- a/src/app_service/service/eth/dto/method_dto.nim +++ b/src/app_service/service/eth/dto/method_dto.nim @@ -74,12 +74,12 @@ proc encodeAbi*(self: MethodDto, obj: object = RootObj()): string = result &= encoded.data result &= data -proc estimateGas*(self: MethodDto, tx: var TransactionDataDto, procDescriptor: object, success: var bool): string = +proc estimateGas*(self: MethodDto, chainId: int, tx: var TransactionDataDto, procDescriptor: object, success: var bool): string = success = true tx.data = self.encodeAbi(procDescriptor) try: # this call should not be part of this file, we need to move it to appropriate place, or this should not be a DTO class. - let response = status_eth.estimateGas(%*[%tx]) + let response = status_eth.estimateGas(chainId, %*[%tx]) result = response.result.getStr # gas estimate in hex except RpcException as e: success = false @@ -89,20 +89,20 @@ proc getEstimateGasData*(self: MethodDto, tx: var TransactionDataDto, procDescri tx.data = self.encodeAbi(procDescriptor) return %*[%tx] -proc send*(self: MethodDto, tx: var TransactionDataDto, procDescriptor: object, password: string, success: var bool): RpcResponse[JsonNode] = +proc send*(self: MethodDto, chainId: int, tx: var TransactionDataDto, procDescriptor: object, password: string, success: var bool): RpcResponse[JsonNode] = tx.data = self.encodeAbi(procDescriptor) # this call should not be part of this file, we need to move it to appropriate place, or this should not be a DTO class. - let response = status_eth.sendTransaction($(%tx), password) + let response = status_eth.sendTransaction(chainId, $(%tx), password) success = response.error.isNil return response -proc call[T](self: MethodDto, tx: var TransactionDataDto, procDescriptor: object, success: var bool): T = +proc call[T](self: MethodDto, chainId: int, tx: var TransactionDataDto, procDescriptor: object, success: var bool): T = success = true tx.data = self.encodeAbi(procDescriptor) let response: RpcResponse try: # this call should not be part of this file, we need to move it to appropriate place, or this should not be a DTO class. - response = status_eth.doEthCall(tx) + response = status_eth.doEthCall(chainId, tx) except RpcException as e: success = false result = e.msg diff --git a/src/app_service/service/message/service.nim b/src/app_service/service/message/service.nim index 6fdc6c9ec7..0635c649c2 100644 --- a/src/app_service/service/message/service.nim +++ b/src/app_service/service/message/service.nim @@ -305,7 +305,7 @@ QtObject: proc getTransactionDetails*(self: Service, message: MessageDto): (string, string) = # TODO(alaibe): handle multi network - let networkDto = self.networkService.getEnabledNetworks()[0] + let networkDto = self.networkService.getNetworks()[0] let ethereum = newTokenDto("Ethereum", networkDto.chainId, parseAddress(ZERO_ADDRESS), "ETH", 18, true) let tokenContract = if message.transactionParameters.contract == "" : ethereum else: self.tokenService.findTokenByAddress(networkDto, parseAddress(message.transactionParameters.contract)) let tokenContractStr = if tokenContract == nil: "{}" else: $(Json.encode(tokenContract)) diff --git a/src/app_service/service/network/service.nim b/src/app_service/service/network/service.nim index 5cf58c5a5c..7db615817f 100644 --- a/src/app_service/service/network/service.nim +++ b/src/app_service/service/network/service.nim @@ -56,18 +56,6 @@ proc getNetworks*(self: Service): seq[NetworkDto] = if not testNetworksEnabled and not network.isTest: result.add(network) -proc getEnabledNetworks*(self: Service): seq[NetworkDto] = - if not singletonInstance.localAccountSensitiveSettings.getIsMultiNetworkEnabled(): - let currentNetworkType = self.settingsService.getCurrentNetwork().toNetworkType() - for network in self.fetchNetworks(): - if currentNetworkType.toChainId() == network.chainId: - return @[network] - - let networks = self.getNetworks() - for network in networks: - if network.enabled: - result.add(network) - proc upsertNetwork*(self: Service, network: NetworkDto) = discard backend.addEthereumChain(backend.Network( chainId: network.chainId, @@ -107,13 +95,31 @@ proc toggleNetwork*(self: Service, chainId: int) = network.enabled = not network.enabled self.upsertNetwork(network) -proc isEIP1559Enabled*(self: Service): bool = - # TODO: Assume multi network is not enabled - # TODO: add block number chain for other chains - let network = self.getEnabledNetworks()[0] - case network.chainId: - of 3: return true - of 4: return true - of 5: return true - of 1: return true - else: return false \ No newline at end of file +proc getNetworkForEns*(self: Service): NetworkDto = + if not singletonInstance.localAccountSensitiveSettings.getIsMultiNetworkEnabled(): + let networkType = self.settingsService.getCurrentNetwork().toNetworkType() + return self.getNetwork(networkType) + + if self.settingsService.areTestNetworksEnabled(): + return self.getNetwork(Ropsten) + + return self.getNetwork(Mainnet) + +proc getNetworkForStickers*(self: Service): NetworkDto = + return self.getNetworkForEns() + +proc getNetworkForBrowser*(self: Service): NetworkDto = + return self.getNetworkForEns() + +proc getNetworkForChat*(self: Service): NetworkDto = + return self.getNetworkForEns() + +proc getNetworkForCollectibles*(self: Service): NetworkDto = + if not singletonInstance.localAccountSensitiveSettings.getIsMultiNetworkEnabled(): + let networkType = self.settingsService.getCurrentNetwork().toNetworkType() + return self.getNetwork(networkType) + + if self.settingsService.areTestNetworksEnabled(): + return self.getNetwork(Rinkeby) + + return self.getNetwork(Mainnet) \ No newline at end of file diff --git a/src/app_service/service/network/types.nim b/src/app_service/service/network/types.nim index bd0947d86e..fc2812d0af 100644 --- a/src/app_service/service/network/types.nim +++ b/src/app_service/service/network/types.nim @@ -43,10 +43,4 @@ proc toChainId*(self: NetworkType): int = of NetworkType.Goerli: result = Goerli of NetworkType.XDai: result = XDai of NetworkType.Poa: result = 99 - of NetworkType.Other: result = -1 - -proc sntSymbol*(networkType: NetworkType): string = - if networkType == NetworkType.Mainnet: - return "SNT" - else: - return "STT" + of NetworkType.Other: result = -1 \ 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 4ad2704170..26315fd2a5 100644 --- a/src/app_service/service/settings/service.nim +++ b/src/app_service/service/settings/service.nim @@ -25,7 +25,6 @@ logScope: QtObject: type Service* = ref object of QObject settings: SettingsDto - eip1559Enabled*: bool proc delete*(self: Service) = self.QObject.delete @@ -33,7 +32,6 @@ QtObject: proc newService*(): Service = new(result, delete) result.QObject.setup - result.eip1559Enabled = false proc init*(self: Service) = try: @@ -414,23 +412,6 @@ QtObject: proc unpinMailserver*(self: Service, fleet: Fleet): bool = return self.pinMailserver("", fleet) - proc isEIP1559Enabled*(self: Service, blockNumber: int): bool = - let networkId = self.getCurrentNetworkDetails().config.NetworkId - let activationBlock = case networkId: - of 3: 10499401 # Ropsten - of 4: 8897988 # Rinkeby - of 5: 5062605 # Goerli - of 1: 12965000 # Mainnet - else: -1 - if activationBlock > -1 and blockNumber >= activationBlock: - result = true - else: - result = false - self.eip1559Enabled = result - - proc isEIP1559Enabled*(self: Service): bool = - result = self.eip1559Enabled - proc saveNodeConfiguration*(self: Service, value: JsonNode): bool = if(self.saveSetting(KEY_NODE_CONFIG, value)): self.settings.nodeConfig = value diff --git a/src/app_service/service/stickers/async_tasks.nim b/src/app_service/service/stickers/async_tasks.nim index bcfdb22138..3219cf3062 100644 --- a/src/app_service/service/stickers/async_tasks.nim +++ b/src/app_service/service/stickers/async_tasks.nim @@ -49,16 +49,4 @@ const obtainMarketStickerPacksTask: Task = proc(argEncoded: string) {.gcsafe, ni var packs: seq[StickerPackDto] = @[] for packId, stickerPack in marketStickerPacks.pairs: packs.add(stickerPack) - arg.finish(%*(packs)) - -################################################# -# Async fetch gas price -################################################# - -const fetchGasPriceTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = - let arg = decode[QObjectTaskArg](argEncoded) - let response = status_eth.getGasPrice() - let responseJson = %* { - "gasPrice": response.result.getStr - } - arg.finish(responseJson) + arg.finish(%*(packs)) \ No newline at end of file diff --git a/src/app_service/service/stickers/service.nim b/src/app_service/service/stickers/service.nim index ca40a3fc5d..6216ffa6c8 100644 --- a/src/app_service/service/stickers/service.nim +++ b/src/app_service/service/stickers/service.nim @@ -56,7 +56,6 @@ const SIGNAL_STICKER_PACK_LOADED* = "stickerPackLoaded" const SIGNAL_ALL_STICKER_PACKS_LOADED* = "allStickerPacksLoaded" const SIGNAL_STICKER_GAS_ESTIMATED* = "stickerGasEstimated" const SIGNAL_INSTALLED_STICKER_PACKS_LOADED* = "installedStickerPacksLoaded" -const SIGNAL_GAS_PRICE_FETCHED* = "stickersGasPriceFetched" const SIGNAL_STICKER_TRANSACTION_CONFIRMED* = "stickerTransactionConfirmed" const SIGNAL_STICKER_TRANSACTION_REVERTED* = "stickerTransactionReverted" @@ -113,7 +112,7 @@ QtObject: proc getStickerMarketAddress*(self: Service): string = try: - let chainId = self.settingsService.getCurrentNetworkId() + let chainId = self.networkService.getNetworkForStickers().chainId let response = status_stickers.stickerMarketAddress(chainId) return response.result.getStr() except RpcException: @@ -196,16 +195,13 @@ QtObject: result.gasPrice = (if gasPrice.isEmptyOrWhitespace: int.none else: gwei2Wei(parseFloat(gasPrice)).truncate(int).some) proc getStatusToken*(self: Service): TokenDto = - let networkType = self.settingsService.getCurrentNetwork().toNetworkType() - let networkDto = self.networkService.getNetwork(networkType) + let networkDto = self.networkService.getNetworkForStickers() - return self.tokenService.findTokenBySymbol(networkDto, networkType.sntSymbol()) + return self.tokenService.findTokenBySymbol(networkDto, networkDto.sntSymbol()) proc buyPack*(self: Service, packId: string, address, gas, gasPrice: string, eip1559Enabled: bool, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, success: var bool): tuple[txHash: string, error: string] = let - networkType = self.settingsService.getCurrentNetwork().toNetworkType() - network = self.networkService.getNetwork(networkType) - chainId = network.chainId + chainId = self.networkService.getNetworkForStickers().chainId txData = buildTransaction(parseAddress(address), gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas) try: @@ -230,8 +226,7 @@ QtObject: except RpcException: error "Error sending transaction", message = getCurrentExceptionMsg() - proc buy*(self: Service, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): tuple[response: string, success: bool] = - let eip1559Enabled = self.networkService.isEIP1559Enabled() + proc buy*(self: Service, packId: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): tuple[response: string, success: bool] = try: status_utils.validateTransactionInput(address, address, "", "0", gas, gasPrice, "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, "ok") except Exception as e: @@ -264,7 +259,7 @@ QtObject: )) - let chainId = self.settingsService.getCurrentNetworkId() + let chainId = self.networkService.getNetworkForStickers().chainId let pendingStickerPacksResponse = status_stickers.pending() for (packID, stickerPackJson) in pendingStickerPacksResponse.result.pairs(): if self.marketStickerPacks.contains(packID): continue @@ -278,7 +273,7 @@ QtObject: self.events.emit(SIGNAL_ALL_STICKER_PACKS_LOADED, Args()) proc obtainMarketStickerPacks*(self: Service) = - let chainId = self.settingsService.getCurrentNetworkId() + let chainId = self.networkService.getNetworkForStickers().chainId let arg = ObtainMarketStickerPacksTaskArg( tptr: cast[ByteAddress](obtainMarketStickerPacksTask), @@ -297,7 +292,7 @@ QtObject: # definition so we'll need to setup the type, task, and helper outside of body # passed to `QtObject:` proc estimate*(self: Service, packId: string, address: string, price: string, uuid: string) = - let chainId = self.settingsService.getCurrentNetworkId() + let chainId = self.networkService.getNetworkForStickers().chainId let arg = EstimateTaskArg( tptr: cast[ByteAddress](estimateTask), @@ -339,7 +334,7 @@ QtObject: return 0 proc installStickerPack*(self: Service, packId: string) = - let chainId = self.settingsService.getCurrentNetworkId() + let chainId = self.networkService.getNetworkForStickers().chainId if not self.marketStickerPacks.hasKey(packId): return let installResponse = status_stickers.install(chainId, packId) @@ -380,36 +375,7 @@ QtObject: proc getSNTBalance*(self: Service): string = let token = self.getStatusToken() let account = self.walletAccountService.getWalletAccount(0).address - let networkType = self.settingsService.getCurrentNetwork().toNetworkType() - let network = self.networkService.getNetwork(networkType) + let network = self.networkService.getNetworkForStickers() let balances = status_go_backend.getTokensBalancesForChainIDs(@[network.chainId], @[account], @[token.addressAsString()]).result - return ens_utils.hex2Token(balances{account}{token.addressAsString()}.getStr, token.decimals) - - proc onGasPriceFetched*(self: Service, response: string) {.slot.} = - let responseObj = response.parseJson - if (responseObj.kind != JObject): - info "expected response is not a json object", procName="onGasPriceFetched" - # notify view, this is important - self.events.emit(SIGNAL_GAS_PRICE_FETCHED, GasPriceArgs(gasPrice: "0")) - return - - var gasPriceHex: string - if(not responseObj.getProp("gasPrice", gasPriceHex)): - info "expected response doesn't contain gas price", procName="onGasPriceFetched" - # notify view, this is important - self.events.emit(SIGNAL_GAS_PRICE_FETCHED, GasPriceArgs(gasPrice: "0")) - return - - let gasPrice = $fromHex(Stuint[256], gasPriceHex) - let parsedGasPrice = parseFloat(wei2gwei(gasPrice)) - var data = GasPriceArgs(gasPrice: fmt"{parsedGasPrice:.3f}") - self.events.emit(SIGNAL_GAS_PRICE_FETCHED, data) - - proc fetchGasPrice*(self: Service) = - let arg = QObjectTaskArg( - tptr: cast[ByteAddress](fetchGasPriceTask), - vptr: cast[ByteAddress](self.vptr), - slot: "onGasPriceFetched" - ) - self.threadpool.start(arg) + return ens_utils.hex2Token(balances{account}{token.addressAsString()}.getStr, token.decimals) \ No newline at end of file diff --git a/src/app_service/service/token/dto.nim b/src/app_service/service/token/dto.nim index 13ef7f6972..c66325aa61 100644 --- a/src/app_service/service/token/dto.nim +++ b/src/app_service/service/token/dto.nim @@ -25,11 +25,10 @@ proc newTokenDto*( name: name, chainId: chainId, address: address, symbol: symbol, decimals: decimals, hasIcon: hasIcon, isCustom: isCustom ) -proc toTokenDto*(jsonObj: JsonNode, activeTokenSymbols: seq[string], hasIcon: bool = false, isCustom: bool = true): TokenDto = +proc toTokenDto*(jsonObj: JsonNode, isVisible: bool, hasIcon: bool = false, isCustom: bool = true): TokenDto = result = TokenDto() result.isCustom = isCustom result.hasIcon = hasIcon - result.isVisible = false discard jsonObj.getProp("name", result.name) discard jsonObj.getProp("chainId", result.chainId) @@ -38,8 +37,7 @@ proc toTokenDto*(jsonObj: JsonNode, activeTokenSymbols: seq[string], hasIcon: bo discard jsonObj.getProp("decimals", result.decimals) discard jsonObj.getProp("color", result.color) - if activeTokenSymbols.contains(result.symbol): - result.isVisible = true + result.isVisible = isVisible proc addressAsString*(self: TokenDto): string = return $self.address \ 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 d0234ef219..2c5d448c7d 100644 --- a/src/app_service/service/token/service.nim +++ b/src/app_service/service/token/service.nim @@ -63,22 +63,20 @@ QtObject: proc init*(self: Service) = try: self.tokens = initTable[NetworkDto, seq[TokenDto]]() - let networks = self.networkService.getEnabledNetworks() + let networks = self.networkService.getNetworks() let chainIds = networks.map(n => n.chainId) - let visibleTokens = backend.getVisibleTokens(chainIds).result let responseCustomTokens = backend.getCustomTokens() for network in networks: - let activeTokenSymbols = visibleTokens[$network.chainId].getElems().map(n => n["symbol"].getStr) let responseTokens = backend.getTokens(network.chainId) let default_tokens = map( responseTokens.result.getElems(), - proc(x: JsonNode): TokenDto = x.toTokenDto(activeTokenSymbols, hasIcon=true, isCustom=false) + proc(x: JsonNode): TokenDto = x.toTokenDto(network.enabled, hasIcon=true, isCustom=false) ) self.tokens[network] = concat( default_tokens, - map(responseCustomTokens.result.getElems(), proc(x: JsonNode): TokenDto = x.toTokenDto(activeTokenSymbols)) + map(responseCustomTokens.result.getElems(), proc(x: JsonNode): TokenDto = x.toTokenDto(network.enabled)) ).filter( proc(x: TokenDto): bool = x.chainId == network.chainId ) @@ -106,15 +104,9 @@ QtObject: if token.address == address: return token - proc getVisibleTokens*(self: Service): seq[TokenDto] = - for tokens in self.getTokens().values: - for token in tokens: - if token.isVisible: - result.add(token) - proc addCustomToken*(self: Service, chainId: int, address: string, name: string, symbol: string, decimals: int): string = # TODO(alaile): use chainId rather than first enabled network - let networkWIP = self.networkService.getEnabledNetworks()[0] + let networkWIP = self.networkService.getNetworks()[0] let foundToken = self.findTokenByAddress(networkWIP, parseAddress(address)) if not foundToken.isNil: diff --git a/src/app_service/service/transaction/service.nim b/src/app_service/service/transaction/service.nim index 92e12c776a..6b68edc3aa 100644 --- a/src/app_service/service/transaction/service.nim +++ b/src/app_service/service/transaction/service.nim @@ -57,6 +57,11 @@ type SuggestedFees = object maxFeePerGasL: float maxFeePerGasM: float maxFeePerGasH: float + eip1559Enabled: bool + +# Initial version of suggested routes is a list of network where the tx is possible +type SuggestedRoutes = object + networks: seq[NetworkDto] QtObject: type Service* = ref object of QObject @@ -213,56 +218,56 @@ QtObject: loadMore: loadMore ) self.threadpool.start(arg) - + proc estimateGas*( self: Service, from_addr: string, to: string, - assetAddress: string, + assetSymbol: string, value: string, - data: string = "" + chainId: string, + data: string = "", ): string {.slot.} = var response: RpcResponse[JsonNode] + var success: bool # TODO make this async - if assetAddress != ZERO_ADDRESS and not assetAddress.isEmptyOrWhitespace: - var tx = buildTokenTransaction( + let network = self.networkService.getNetwork(parseInt(chainId)) + + if network.nativeCurrencySymbol == assetSymbol: + var tx = ens_utils.buildTransaction( parseAddress(from_addr), - parseAddress(assetAddress) + eth2Wei(parseFloat(value), 18), + data = data ) - let networkType = self.settingsService.getCurrentNetwork().toNetworkType() - let network = self.networkService.getNetwork(networkType) - let token = self.tokenService.findTokenByAddress(network, parseAddress(assetAddress)) - - if token == nil: - raise newException(ValueError, fmt"Could not find ERC-20 contract with address '{assetAddress}' for the current network") - - let transfer = Transfer(to: parseAddress(to), value: conversion.eth2Wei(parseFloat(value), token.decimals)) - let transferproc = ERC20_procS.toTable["transfer"] - var success: bool + tx.to = parseAddress(to).some try: - let gas = transferproc.estimateGas(tx, transfer, success) - - let res = fromHex[int](gas) - return $(%* { "result": res, "success": success }) + response = eth.estimateGas(parseInt(chainId), %*[%tx]) + let res = fromHex[int](response.result.getStr) + return $(%* { "result": res, "success": true }) except Exception as e: error "Error estimating gas", msg = e.msg return $(%* { "result": "-1", "success": false, "error": { "message": e.msg } }) - var tx = ens_utils.buildTransaction( + let token = self.tokenService.findTokenBySymbol(network, assetSymbol) + if token == nil: + raise newException(ValueError, fmt"Could not find ERC-20 contract with symbol '{assetSymbol}' for the current network") + + var tx = buildTokenTransaction( parseAddress(from_addr), - eth2Wei(parseFloat(value), 18), - data = data + token.address, ) - tx.to = parseAddress(to).some + + let transfer = Transfer(to: parseAddress(to), value: conversion.eth2Wei(parseFloat(value), token.decimals)) + let transferproc = ERC20_procS.toTable["transfer"] try: - response = eth.estimateGas(%*[%tx]) - let res = fromHex[int](response.result.getStr) + let gas = transferproc.estimateGas(parseInt(chainId), tx, transfer, success) + let res = fromHex[int](gas) + return $(%* { "result": res, "success": success }) except Exception as e: error "Error estimating gas", msg = e.msg - return $(%* { "result": "-1", "success": false }) - + return $(%* { "result": "-1", "success": false, "error": { "message": e.msg } }) - proc transferEth*( + proc transferEth( self: Service, from_addr: string, to_addr: string, @@ -272,10 +277,11 @@ QtObject: maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, - uuid: string - ): bool {.slot.} = + chainId: string, + uuid: string, + eip1559Enabled: bool, + ): bool {.slot.} = try: - let eip1559Enabled = self.networkService.isEIP1559Enabled() eth_utils.validateTransactionInput(from_addr, to_addr, assetAddress = "", value, gas, gasPrice, data = "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, uuid) @@ -285,8 +291,7 @@ QtObject: tx.to = parseAddress(to_addr).some let json: JsonNode = %tx - let response = eth.sendTransaction($json, password) - + let response = eth.sendTransaction(parseInt(chainId), $json, password) let output = %* { "result": response.result.getStr, "success": %(response.error.isNil), "uuid": %uuid } self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(result: $output)) @@ -298,34 +303,35 @@ QtObject: return true proc transferTokens*( - self: Service, - from_addr: string, - to_addr: string, - assetAddress: string, - value: string, - gas: string, - gasPrice: string, - maxPriorityFeePerGas: string, - maxFeePerGas: string, - password: string, - uuid: string - ): bool = + self: Service, + from_addr: string, + to_addr: string, + tokenSymbol: string, + value: string, + gas: string, + gasPrice: string, + maxPriorityFeePerGas: string, + maxFeePerGas: string, + password: string, + chainId: string, + uuid: string, + eip1559Enabled: bool, + ): bool = + # TODO move this to another thread try: - let eip1559Enabled = self.networkService.isEIP1559Enabled() - eth_utils.validateTransactionInput(from_addr, to_addr, assetAddress, value, gas, - gasPrice, data = "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, uuid) + let network = self.networkService.getNetwork(parseInt(chainId)) + let token = self.tokenService.findTokenBySymbol(network, tokenSymbol) - # TODO move this to another thread - let networkType = self.settingsService.getCurrentNetwork().toNetworkType() - let network = self.networkService.getNetwork(networkType) - let token = self.tokenService.findTokenByAddress(network, parseAddress(assetAddress)) - var tx = ens_utils.buildTokenTransaction(parseAddress(from_addr), parseAddress(assetAddress), + eth_utils.validateTransactionInput(from_addr, to_addr, token.addressAsString(), value, gas, + gasPrice, data = "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, uuid) + + var tx = ens_utils.buildTokenTransaction(parseAddress(from_addr), token.address, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas) var success: bool let transfer = Transfer(to: parseAddress(to_addr), value: conversion.eth2Wei(parseFloat(value), token.decimals)) let transferproc = ERC20_procS.toTable["transfer"] - let response = transferproc.send(tx, transfer, password, success) + let response = transferproc.send(parseInt(chainId), tx, transfer, password, success) let txHash = response.result.getStr let output = %* { "result": txHash, "success": %success, "uuid": %uuid } self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(result: $output)) @@ -337,19 +343,45 @@ QtObject: return false return true - proc suggestedFees*(self: Service): SuggestedFees = - let networkType = self.settingsService.getCurrentNetwork().toNetworkType() - let network = self.networkService.getNetwork(networkType) - let response = eth.suggestedFees(network.chainId).result + proc transfer*( + self: Service, + from_addr: string, + to_addr: string, + assetSymbol: string, + value: string, + gas: string, + gasPrice: string, + maxPriorityFeePerGas: string, + maxFeePerGas: string, + password: string, + chainId: string, + uuid: string, + eip1559Enabled: bool, + ): bool = + let network = self.networkService.getNetwork(parseInt(chainId)) + if network.nativeCurrencySymbol == assetSymbol: + return self.transferEth(from_addr, to_addr, value, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled) + + return self.transferTokens(from_addr, to_addr, assetSymbol, value, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled) + + proc suggestedFees*(self: Service, chainId: int): SuggestedFees = + let response = eth.suggestedFees(chainId).result return SuggestedFees( gasPrice: parseFloat(response{"gasPrice"}.getStr), baseFee: parseFloat(response{"baseFee"}.getStr), maxPriorityFeePerGas: parseFloat(response{"maxPriorityFeePerGas"}.getStr), maxFeePerGasL: parseFloat(response{"maxFeePerGasLow"}.getStr), maxFeePerGasM: parseFloat(response{"maxFeePerGasMedium"}.getStr), - maxFeePerGasH: parseFloat(response{"maxFeePerGasHigh"}.getStr) - ) + maxFeePerGasH: parseFloat(response{"maxFeePerGasHigh"}.getStr), + eip1559Enabled: response{"eip1559Enabled"}.getbool, + ) + + proc suggestedRoutes*(self: Service, account: string, amount: float64, token: string): SuggestedRoutes = + let response = eth.suggestedRoutes(account, amount, token) + return SuggestedRoutes( + networks: Json.decode($response.result{"networks"}, seq[NetworkDto]) + ) proc fetchCryptoServices*(self: Service): seq[CryptoRampDto] = try: diff --git a/src/app_service/service/wallet_account/async_tasks.nim b/src/app_service/service/wallet_account/async_tasks.nim index 8a1c53aefb..6273bdb58e 100644 --- a/src/app_service/service/wallet_account/async_tasks.nim +++ b/src/app_service/service/wallet_account/async_tasks.nim @@ -98,19 +98,27 @@ type proc getCustomTokens(): seq[TokenDto] = try: let responseCustomTokens = backend.getCustomTokens() - result = map(responseCustomTokens.result.getElems(), proc(x: JsonNode): TokenDto = x.toTokenDto(@[])) + result = map(responseCustomTokens.result.getElems(), proc(x: JsonNode): TokenDto = x.toTokenDto(true)) except Exception as e: error "error fetching custom tokens: ", message = e.msg -proc getTokensForChainId(chainId: int): seq[TokenDto] = +proc getTokensForChainId(network: NetworkDto): seq[TokenDto] = try: - let responseTokens = backend.getTokens(chainId) - let defaultTokens = map(responseTokens.result.getElems(), - proc(x: JsonNode): TokenDto = x.toTokenDto(@[], hasIcon=true, isCustom=false) - ) + let responseTokens = backend.getTokens(network.chainId) + let defaultTokens = map( + responseTokens.result.getElems(), + proc(x: JsonNode): TokenDto = x.toTokenDto(network.enabled, hasIcon=true, isCustom=false) + ) result.add(defaultTokens) except Exception as e: - error "error fetching tokens: ", message = e.msg, chainId=chainId + error "error fetching tokens: ", message = e.msg, chainId=network.chainId + +proc isNetworkEnabledForChainId(networks: seq[NetworkDto], chainId: int): bool = + for network in networks: + if network.chainId == chainId: + return network.enabled + + return false proc prepareSymbols(networkSymbols: seq[string], allTokens: seq[TokenDto]): seq[seq[string]] = # we have to use up to 300 characters in a single request when we're fetching prices @@ -151,6 +159,9 @@ proc fetchNativeChainBalance(chainId: int, nativeCurrencyDecimals: int, accountA proc fetchPrices(networkSymbols: seq[string], allTokens: seq[TokenDto], currency: string): Table[string, float] = let allSymbols = prepareSymbols(networkSymbols, allTokens) for symbols in allSymbols: + if symbols.len == 0: + continue + try: let response = backend.fetchPrices(symbols, currency) for (symbol, value) in response.result.pairs: @@ -197,15 +208,13 @@ const prepareTokensTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = let arg = decode[BuildTokensTaskArg](argEncoded) var networkSymbols: seq[string] - var chainIdsFromSettings: seq[int] + var allTokens: seq[TokenDto] + for network in arg.networks: networkSymbols.add(network.nativeCurrencySymbol) - chainIdsFromSettings.add(network.chainId) - - var allTokens: seq[TokenDto] + allTokens.add(getTokensForChainId(network)) + allTokens.add(getCustomTokens()) - for chainId in chainIdsFromSettings: - allTokens.add(getTokensForChainId(chainId)) allTokens = deduplicate(allTokens) var prices = fetchPrices(networkSymbols, allTokens, arg.currency) @@ -216,55 +225,80 @@ const prepareTokensTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = var builtTokens: seq[WalletTokenDto] let groupedNetworks = groupNetworksBySymbol(arg.networks) + var enabledNetworkBalance = BalanceDto( + balance: 0.0, + currencyBalance: 0.0 + ) for networkNativeCurrencySymbol, networks in groupedNetworks.pairs: var balancesPerChain = initTable[int, BalanceDto]() for network in networks: let chainBalance = fetchNativeChainBalance(network.chainId, network.nativeCurrencyDecimals, address) balancesPerChain[network.chainId] = BalanceDto( balance: chainBalance, - currencyBalance: chainBalance * prices[network.nativeCurrencySymbol] + currencyBalance: chainBalance * prices[network.nativeCurrencySymbol], + chainId: network.chainId, + address: "0x0000000000000000000000000000000000000000" ) + if network.enabled: + enabledNetworkBalance.balance += balancesPerChain[network.chainId].balance + enabledNetworkBalance.currencyBalance += balancesPerChain[network.chainId].currencyBalance + let networkDto = getNetworkByCurrencySymbol(arg.networks, networkNativeCurrencySymbol) var totalTokenBalance: BalanceDto totalTokenBalance.balance = toSeq(balancesPerChain.values).map(x => x.balance).foldl(a + b) totalTokenBalance.currencyBalance = totalTokenBalance.balance * prices[networkDto.nativeCurrencySymbol] builtTokens.add(WalletTokenDto( name: networkDto.nativeCurrencyName, - address: "0x0000000000000000000000000000000000000000", symbol: networkDto.nativeCurrencySymbol, decimals: networkDto.nativeCurrencyDecimals, hasIcon: true, color: "blue", isCustom: false, totalBalance: totalTokenBalance, - balancesPerChain: balancesPerChain + enabledNetworkBalance: enabledNetworkBalance, + balancesPerChain: balancesPerChain, + visible: networkDto.enabled, ) ) let groupedTokens = groupTokensBySymbol(allTokens) + enabledNetworkBalance = BalanceDto( + balance: 0.0, + currencyBalance: 0.0 + ) for symbol, tokens in groupedTokens.pairs: var balancesPerChain = initTable[int, BalanceDto]() + var visible = false + for token in tokens: let balanceForToken = tokenBalances{address}{token.addressAsString()}.getStr let chainBalanceForToken = parsefloat(hex2Balance(balanceForToken, token.decimals)) balancesPerChain[token.chainId] = BalanceDto( balance: chainBalanceForToken, - currencyBalance: chainBalanceForToken * prices[token.symbol] + currencyBalance: chainBalanceForToken * prices[token.symbol], + chainId: token.chainId, + address: $token.address ) + if isNetworkEnabledForChainId(arg.networks, token.chainId): + visible = true + enabledNetworkBalance.balance += balancesPerChain[token.chainId].balance + enabledNetworkBalance.currencyBalance += balancesPerChain[token.chainId].currencyBalance + let tokenDto = getTokenForSymbol(allTokens, symbol) var totalTokenBalance: BalanceDto totalTokenBalance.balance = toSeq(balancesPerChain.values).map(x => x.balance).foldl(a + b) totalTokenBalance.currencyBalance = totalTokenBalance.balance * prices[tokenDto.symbol] builtTokens.add(WalletTokenDto( name: tokenDto.name, - address: $tokenDto.address, symbol: tokenDto.symbol, decimals: tokenDto.decimals, hasIcon: tokenDto.hasIcon, color: tokenDto.color, isCustom: tokenDto.isCustom, totalBalance: totalTokenBalance, - balancesPerChain: balancesPerChain + balancesPerChain: balancesPerChain, + enabledNetworkBalance: enabledNetworkBalance, + visible: visible ) ) @@ -272,6 +306,6 @@ const prepareTokensTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = for wtDto in builtTokens: tokensJarray.add(walletTokenDtoToJson(wtDto)) builtTokensPerAccount[address] = tokensJArray - + arg.finish(builtTokensPerAccount) diff --git a/src/app_service/service/wallet_account/dto.nim b/src/app_service/service/wallet_account/dto.nim index 8a608f1051..87fb9e6577 100644 --- a/src/app_service/service/wallet_account/dto.nim +++ b/src/app_service/service/wallet_account/dto.nim @@ -5,18 +5,21 @@ include ../../common/json_utils type BalanceDto* = object balance*: float64 currencyBalance*: float64 + address*: string + chainId*: int type WalletTokenDto* = object name*: string - address*: string symbol*: string decimals*: int hasIcon*: bool color*: string isCustom*: bool totalBalance*: BalanceDto + enabledNetworkBalance*: BalanceDto balancesPerChain*: Table[int, BalanceDto] + visible*: bool type WalletAccountDto* = ref object of RootObj @@ -79,20 +82,26 @@ proc toBalanceDto*(jsonObj: JsonNode): BalanceDto = result = BalanceDto() discard jsonObj.getProp("balance", result.balance) discard jsonObj.getProp("currencyBalance", result.currencyBalance) + discard jsonObj.getProp("address", result.address) + discard jsonObj.getProp("chainId", result.chainId) proc toWalletTokenDto*(jsonObj: JsonNode): WalletTokenDto = result = WalletTokenDto() discard jsonObj.getProp("name", result.name) - discard jsonObj.getProp("address", result.address) discard jsonObj.getProp("symbol", result.symbol) discard jsonObj.getProp("decimals", result.decimals) discard jsonObj.getProp("hasIcon", result.hasIcon) discard jsonObj.getProp("color", result.color) discard jsonObj.getProp("isCustom", result.isCustom) + discard jsonObj.getProp("visible", result.visible) var totalBalanceObj: JsonNode if(jsonObj.getProp("totalBalance", totalBalanceObj)): result.totalBalance = toBalanceDto(totalBalanceObj) + + var enabledNetworkBalanceObj: JsonNode + if(jsonObj.getProp("enabledNetworkBalance", enabledNetworkBalanceObj)): + result.enabledNetworkBalance = toBalanceDto(enabledNetworkBalanceObj) var balancesPerChainObj: JsonNode if(jsonObj.getProp("balancesPerChain", balancesPerChainObj)): @@ -106,12 +115,13 @@ proc walletTokenDtoToJson*(dto: WalletTokenDto): JsonNode = result = %* { "name": dto.name, - "address": dto.address, "symbol": dto.symbol, "decimals": dto.decimals, "hasIcon": dto.hasIcon, "color": dto.color, "isCustom": dto.isCustom, "totalBalance": %* dto.totalBalance, - "balancesPerChain": balancesPerChainJsonObj + "enabledNetworkBalance": %* dto.enabledNetworkBalance, + "balancesPerChain": balancesPerChainJsonObj, + "visible": dto.visible } \ No newline at end of file diff --git a/src/app_service/service/wallet_account/service.nim b/src/app_service/service/wallet_account/service.nim index 6782bf8e0d..8a7f157f54 100644 --- a/src/app_service/service/wallet_account/service.nim +++ b/src/app_service/service/wallet_account/service.nim @@ -394,7 +394,7 @@ QtObject: slot: "onAllTokensBuilt", walletAddresses: walletAddresses, currency: self.settingsService.getCurrency(), - networks: self.networkService.getEnabledNetworks() + networks: self.networkService.getNetworks() ) self.threadpool.start(arg) diff --git a/src/backend/backend.nim b/src/backend/backend.nim index 232e92c195..fbf8793dd4 100644 --- a/src/backend/backend.nim +++ b/src/backend/backend.nim @@ -96,9 +96,6 @@ rpc(discoverToken, "wallet"): rpc(getPendingTransactions, "wallet"): discard -rpc(getVisibleTokens, "wallet"): - chainIds: seq[int] - rpc(toggleVisibleToken, "wallet"): chainId: int address: string diff --git a/src/backend/core.nim b/src/backend/core.nim index 62f22166bd..992d8ac7fe 100644 --- a/src/backend/core.nim +++ b/src/backend/core.nim @@ -60,11 +60,11 @@ proc signMessage*(rpcParams: string): string = proc signTypedData*(data: string, address: string, password: string): string = return $status_go.signTypedData(data, address, password) -proc sendTransaction*(inputJSON: string, password: string): RpcResponse[JsonNode] +proc sendTransaction*(chainId: int, inputJSON: string, password: string): RpcResponse[JsonNode] {.raises: [RpcException, ValueError, Defect, SerializationError].} = try: var hashed_password = "0x" & $keccak_256.digest(password) - let rpcResponseRaw = status_go.sendTransaction(inputJSON, hashed_password) + let rpcResponseRaw = status_go.sendTransactionWithChainId(chainId, inputJSON, hashed_password) result = Json.decode(rpcResponseRaw, RpcResponse[JsonNode]) except RpcException as e: error "error sending tx", inputJSON, exception=e.msg diff --git a/src/backend/eth.nim b/src/backend/eth.nim index 7d1af8b7ac..7ae36b1275 100644 --- a/src/backend/eth.nim +++ b/src/backend/eth.nim @@ -14,22 +14,23 @@ proc getNativeChainBalance*(chainId: int, address: string): RpcResponse[JsonNode let payload = %* [address, "latest"] return core.callPrivateRPCWithChainId("eth_getBalance", chainId, payload) -proc sendTransaction*(transactionData: string, password: string): RpcResponse[JsonNode] {.raises: [Exception].} = - core.sendTransaction(transactionData, password) +proc sendTransaction*(chainId: int, transactionData: string, password: string): RpcResponse[JsonNode] {.raises: [Exception].} = + core.sendTransaction(chainId, transactionData, password) # This is the replacement of the `call` function proc doEthCall*(payload = %* []): RpcResponse[JsonNode] {.raises: [Exception].} = core.callPrivateRPC("eth_call", payload) -proc estimateGas*(payload = %* []): RpcResponse[JsonNode] {.raises: [Exception].} = - core.callPrivateRPC("eth_estimateGas", payload) +proc estimateGas*(chainId: int, payload = %* []): RpcResponse[JsonNode] {.raises: [Exception].} = + core.callPrivateRPCWithChainId("eth_estimateGas", chainId, payload) proc getEthAccounts*(): RpcResponse[JsonNode] {.raises: [Exception].} = return core.callPrivateRPC("eth_accounts") -proc getGasPrice*(payload = %* []): RpcResponse[JsonNode] {.raises: [Exception].} = - return core.callPrivateRPC("eth_gasPrice", payload) - proc suggestedFees*(chainId: int): RpcResponse[JsonNode] {.raises: [Exception].} = let payload = %* [chainId] - return core.callPrivateRPC("wallet_getSuggestedFees", payload) \ No newline at end of file + return core.callPrivateRPC("wallet_getSuggestedFees", payload) + +proc suggestedRoutes*(account: string, amount: float64, token: string): RpcResponse[JsonNode] {.raises: [Exception].} = + let payload = %* [account, amount, token] + return core.callPrivateRPC("wallet_getSuggestedRoutes", payload) \ No newline at end of file diff --git a/ui/StatusQ b/ui/StatusQ index 0b855bfb48..18d385cf2b 160000 --- a/ui/StatusQ +++ b/ui/StatusQ @@ -1 +1 @@ -Subproject commit 0b855bfb48159850decbed55b3427bd2c3f3ad96 +Subproject commit 18d385cf2b5f075b9ba18ce91465ea3847515aa5 diff --git a/ui/app/AppLayouts/Browser/BrowserLayout.qml b/ui/app/AppLayouts/Browser/BrowserLayout.qml index dd34c086e2..f8f470b1ae 100644 --- a/ui/app/AppLayouts/Browser/BrowserLayout.qml +++ b/ui/app/AppLayouts/Browser/BrowserLayout.qml @@ -59,6 +59,7 @@ Rectangle { anchors.centerIn: parent store: browserWindow.globalStore contactsStore: browserWindow.globalStore.profileSectionStore.contactsStore + chainId: browserWindow.globalStore.getChainIdForBrowser() } property Component signMessageModalComponent: SignMessageModal {} @@ -196,7 +197,7 @@ Rectangle { // TODO: WIP under PR https://github.com/status-im/status-desktop/pull/4274 let url = `${WalletStore.getEtherscanLink()}/${result}`; Global.displayToastMessage(qsTr("Transaction pending..."), - "", + qsTr("View on etherscan"), "", true, Constants.ephemeralNotificationType.normal, diff --git a/ui/app/AppLayouts/Browser/stores/RootStore.qml b/ui/app/AppLayouts/Browser/stores/RootStore.qml index c7c8a35255..fa51482fa5 100644 --- a/ui/app/AppLayouts/Browser/stores/RootStore.qml +++ b/ui/app/AppLayouts/Browser/stores/RootStore.qml @@ -78,4 +78,5 @@ QtObject { function copyToClipboard(text) { globalUtils.copyToClipboard(text) } + } diff --git a/ui/app/AppLayouts/Browser/stores/WalletStore.qml b/ui/app/AppLayouts/Browser/stores/WalletStore.qml index 677fbda1b8..564f907392 100644 --- a/ui/app/AppLayouts/Browser/stores/WalletStore.qml +++ b/ui/app/AppLayouts/Browser/stores/WalletStore.qml @@ -20,13 +20,4 @@ QtObject { walletSectionTransactions.setModel(address) } - function getGasPrice(){ - // Not Refactored Yet -// walletModel.gasView.getGasPrice() - } - - function isEIP1559Enabled() { - return walletSection.isEIP1559Enabled() - } - } diff --git a/ui/app/AppLayouts/Browser/views/WebProviderObj.qml b/ui/app/AppLayouts/Browser/views/WebProviderObj.qml index 119a8de966..ef28fe8070 100644 --- a/ui/app/AppLayouts/Browser/views/WebProviderObj.qml +++ b/ui/app/AppLayouts/Browser/views/WebProviderObj.qml @@ -94,12 +94,8 @@ QtObject { // TODO: use bignumber instead of floats trx.value = RootStore.getEth2Hex(parseFloat(value)) trx.gas = "0x" + parseInt(selectedGasLimit, 10).toString(16) - if (WalletStore.isEIP1559Enabled()) { - trx.maxPriorityFeePerGas = RootStore.getGwei2Hex(parseFloat(selectedTipLimit)) - trx.maxFeePerGas = RootStore.getGwei2Hex(parseFloat(selectedOverallLimit)) - } else { - trx.gasPrice = RootStore.getGwei2Hex(parseFloat(selectedGasPrice)) - } + trx.maxPriorityFeePerGas = RootStore.getGwei2Hex(parseFloat(selectedTipLimit)) + trx.maxFeePerGas = RootStore.getGwei2Hex(parseFloat(selectedOverallLimit)) request.payload.password = enteredPassword request.payload.params[0] = trx @@ -110,7 +106,6 @@ QtObject { } sendDialog.open(); - WalletStore.getGasPrice() } else if (requestType === Constants.web3SendAsyncReadOnly && ["eth_sign", "personal_sign", "eth_signTypedData", "eth_signTypedData_v3"].indexOf(request.payload.method) > -1) { const signDialog = createSignMessageModalComponent(request) signDialog.web3Response = web3Response diff --git a/ui/app/AppLayouts/Chat/popups/ChatCommandModal.qml b/ui/app/AppLayouts/Chat/popups/ChatCommandModal.qml index 5e2ad0c38d..3a14b897b7 100644 --- a/ui/app/AppLayouts/Chat/popups/ChatCommandModal.qml +++ b/ui/app/AppLayouts/Chat/popups/ChatCommandModal.qml @@ -177,7 +177,7 @@ StatusModal { if (stack.isLastGroup) { root.sendChatCommand(selectFromAccount.selectedAccount.address, txtAmount.selectedAmount, - txtAmount.selectedAsset.address, + txtAmount.selectedAsset.symbol, txtAmount.selectedAsset.decimals) return root.close() } diff --git a/ui/app/AppLayouts/Chat/stores/RootStore.qml b/ui/app/AppLayouts/Chat/stores/RootStore.qml index 38ca3344ea..1c633e7d02 100644 --- a/ui/app/AppLayouts/Chat/stores/RootStore.qml +++ b/ui/app/AppLayouts/Chat/stores/RootStore.qml @@ -132,8 +132,6 @@ QtObject { property string channelEmoji: chatCommunitySectionModule && chatCommunitySectionModule.emoji ? chatCommunitySectionModule.emoji : "" - property string gasPrice: profileSectionModule.ensUsernamesModule.gasPrice - property ListModel addToGroupContacts: ListModel {} property var walletSectionTransactionsInst: walletSectionTransactions @@ -513,18 +511,16 @@ QtObject { return profileSectionModule.ensUsernamesModule.getGasEthValue(gweiValue, gasLimit) } - function estimateGas(from_addr, to, assetAddress, value, data) { - return walletSectionTransactions.estimateGas(from_addr, to, assetAddress, value === "" ? "0.00" : value, data) + function estimateGas(from_addr, to, assetSymbol, value, chainId, data) { + return walletSectionTransactions.estimateGas(from_addr, to, assetSymbol, value === "" ? "0.00" : value, chainId, data) } - function transferEth(from, to, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, uuid) { - return walletSectionTransactions.transferEth(from, to, amount, gasLimit, gasPrice, tipLimit, - overallLimit, password, uuid); - } - - function transferTokens(from, to, address, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, uuid) { - return walletSectionTransactions.transferTokens(from, to, address, amount, gasLimit, - gasPrice, tipLimit, overallLimit, password, uuid); + function transfer(from, to, address, tokenSymbol, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, chainId, uuid, eip1559Enabled) { + return walletSectionTransactions.transfer( + from, to, address, tokenSymbol, amount, gasLimit, + gasPrice, tipLimit, overallLimit, password, chainId, uuid, + eip1559Enabled + ); } function getAccountNameByAddress(address) { @@ -540,15 +536,11 @@ QtObject { return walletSectionAccounts.getAccountAssetsByAddress() } - function fetchGasPrice() { - profileSectionModule.ensUsernamesModule.fetchGasPrice() + function suggestedFees(chainId) { + return JSON.parse(walletSectionTransactions.suggestedFees(chainId)) } - function isEIP1559Enabled() { - return walletSection.isEIP1559Enabled() - } - - function suggestedFees() { - return JSON.parse(walletSectionTransactions.suggestedFees()) + function suggestedRoutes(account, amount, token) { + return JSON.parse(walletSectionTransactions.suggestedRoutes(account, amount, token)).networks } } diff --git a/ui/app/AppLayouts/Chat/stores/StickersStore.qml b/ui/app/AppLayouts/Chat/stores/StickersStore.qml index 9f33d4f087..baa9c67279 100644 --- a/ui/app/AppLayouts/Chat/stores/StickersStore.qml +++ b/ui/app/AppLayouts/Chat/stores/StickersStore.qml @@ -6,16 +6,8 @@ QtObject { property var stickersModule - property string gasPrice: root.stickersModule ? stickersModule.gasPrice : "0" - property var walletAccounts: walletSectionAccounts.model - function fetchGasPrice() { - if(!root.stickersModule) - return "0" - stickersModule.fetchGasPrice() - } - function getSigningPhrase() { if(!root.stickersModule) return "" @@ -70,10 +62,14 @@ QtObject { return stickersModule.estimate(packId, selectedAccount, price, uuid) } - function buy(packId, address, price, gasLimit, gasPrice, tipLimit, overallLimit, password) { + function buy(packId, address, price, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) { if(!root.stickersModule) return "" - return stickersModule.buy(packId, address, price, gasLimit, gasPrice, tipLimit, overallLimit, password) + return stickersModule.buy(packId, address, price, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) + } + + function getChainIdForStickers() { + return stickersModule.getChainIdForStickers() } } diff --git a/ui/app/AppLayouts/Chat/views/ChatColumnView.qml b/ui/app/AppLayouts/Chat/views/ChatColumnView.qml index 87fa1e7aeb..164ad49541 100644 --- a/ui/app/AppLayouts/Chat/views/ChatColumnView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatColumnView.qml @@ -384,10 +384,6 @@ Item { anchors.centerIn: parent store: root.rootStore contactsStore: root.contactsStore - onOpened: { - // Not Refactored Yet -// root.rootStore.walletModelInst.gasView.getGasPrice() - } onClosed: { destroy() } diff --git a/ui/app/AppLayouts/Profile/stores/AdvancedStore.qml b/ui/app/AppLayouts/Profile/stores/AdvancedStore.qml index 6346a067e3..9ac3bc192c 100644 --- a/ui/app/AppLayouts/Profile/stores/AdvancedStore.qml +++ b/ui/app/AppLayouts/Profile/stores/AdvancedStore.qml @@ -9,6 +9,7 @@ QtObject { // Advanced Module Properties property string currentNetworkName: advancedModule? advancedModule.currentNetworkName : "" property string currentNetworkId: advancedModule? advancedModule.currentNetworkId : "" + property string currentChainId: advancedModule? advancedModule.currentChainId : 0 property string fleet: advancedModule? advancedModule.fleet : "" property string bloomLevel: advancedModule? advancedModule.bloomLevel : "" property bool wakuV2LightClientEnabled: advancedModule? advancedModule.wakuV2LightClientEnabled : false @@ -40,10 +41,13 @@ QtObject { function setGlobalNetworkId() { Global.currentNetworkId = currentNetworkId + Global.currentChainId = currentChainId } + Component.onCompleted: { setGlobalNetworkId() } + onCurrentNetworkIdChanged: { setGlobalNetworkId() } diff --git a/ui/app/AppLayouts/Profile/stores/EnsUsernamesStore.qml b/ui/app/AppLayouts/Profile/stores/EnsUsernamesStore.qml index 311ff1f7bb..df54afd16b 100644 --- a/ui/app/AppLayouts/Profile/stores/EnsUsernamesStore.qml +++ b/ui/app/AppLayouts/Profile/stores/EnsUsernamesStore.qml @@ -13,7 +13,6 @@ QtObject { property string preferredUsername: userProfile.preferredName property string username: userProfile.username - property string gasPrice: root.ensUsernamesModule ? ensUsernamesModule.gasPrice : "0" property var walletAccounts: walletSectionAccounts.model @@ -41,22 +40,16 @@ QtObject { ensUsernamesModule.fetchDetailsForEnsUsername(ensUsername) } - function fetchGasPrice() { - if(!root.ensUsernamesModule) - return "0" - ensUsernamesModule.fetchGasPrice() - } - function setPubKeyGasEstimate(ensUsername, address) { if(!root.ensUsernamesModule) return 0 return ensUsernamesModule.setPubKeyGasEstimate(ensUsername, address) } - function setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) { + function setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) { if(!root.ensUsernamesModule) return "" - return ensUsernamesModule.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) + return ensUsernamesModule.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled) } function getEtherscanLink() { @@ -105,10 +98,10 @@ QtObject { return ensUsernamesModule.registerEnsGasEstimate(ensUsername, address) } - function registerEns(ensUsername, address, gasLimit, gasPrice, tipLimit, overallLimit, password) { + function registerEns(ensUsername, address, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) { if(!root.ensUsernamesModule) return "" - return ensUsernamesModule.registerEns(ensUsername, address, gasLimit, gasPrice, tipLimit, overallLimit, password) + return ensUsernamesModule.registerEns(ensUsername, address, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) } function getEnsRegistry() { @@ -153,12 +146,18 @@ QtObject { return ensUsernamesModule.getStatusToken() } - function isEIP1559Enabled() { - return walletSection.isEIP1559Enabled() + function suggestedFees(chainId) { + return JSON.parse(walletSectionTransactions.suggestedFees(chainId)) } - function suggestedFees() { - return JSON.parse(walletSectionTransactions.suggestedFees()) + function getChainIdForEns() { + if(!root.ensUsernamesModule) + return "" + return ensUsernamesModule.getChainIdForEns() + } + + function suggestedRoutes(account, amount, token) { + return JSON.parse(walletSectionTransactions.suggestedRoutes(account, amount, token)).networks } } diff --git a/ui/app/AppLayouts/Profile/views/AdvancedView.qml b/ui/app/AppLayouts/Profile/views/AdvancedView.qml index 1488cdd74f..6282b84db9 100644 --- a/ui/app/AppLayouts/Profile/views/AdvancedView.qml +++ b/ui/app/AppLayouts/Profile/views/AdvancedView.qml @@ -518,7 +518,7 @@ SettingsContentBase { anchors.leftMargin: 0 anchors.rightMargin: 0 text: qsTr("Stickers/ENS on ropsten") - visible: root.advancedStore.currentNetworkId === Constants.networkRopsten + visible: !localAccountSensitiveSettings.isMultiNetworkEnabled && root.advancedStore.currentNetworkId === Constants.networkRopsten isSwitch: true switchChecked: localAccountSensitiveSettings.stickersEnsRopsten onClicked: { diff --git a/ui/app/AppLayouts/Profile/views/EnsDetailsView.qml b/ui/app/AppLayouts/Profile/views/EnsDetailsView.qml index 6383644b6b..a55c76da47 100644 --- a/ui/app/AppLayouts/Profile/views/EnsDetailsView.qml +++ b/ui/app/AppLayouts/Profile/views/EnsDetailsView.qml @@ -106,9 +106,7 @@ Item { ensUsernamesStore: root.ensUsernamesStore contactsStore: root.contactsStore ensUsername: root.username - onOpened: { - root.ensUsernamesStore.fetchGasPrice() - } + chainId: root.ensUsernamesStore.getChainIdForEns() title: qsTr("Connect username with your pubkey") onClosed: { destroy() diff --git a/ui/app/AppLayouts/Profile/views/EnsSearchView.qml b/ui/app/AppLayouts/Profile/views/EnsSearchView.qml index 3b74b5840d..1752527ef9 100644 --- a/ui/app/AppLayouts/Profile/views/EnsSearchView.qml +++ b/ui/app/AppLayouts/Profile/views/EnsSearchView.qml @@ -63,9 +63,7 @@ Item { StatusETHTransactionModal { ensUsernamesStore: root.ensUsernamesStore contactsStore: root.contactsStore - onOpened: { - root.ensUsernamesStore.fetchGasPrice() - } + chainId: root.ensUsernamesStore.getChainIdForEns() title: qsTr("Connect username with your pubkey") onClosed: { destroy() @@ -74,14 +72,15 @@ Item { if (ensUsername.text === "" || !selectedAccount) return 80000; return root.ensUsernamesStore.setPubKeyGasEstimate(ensUsername.text + (isStatus ? ".stateofus.eth" : "" ), selectedAccount.address) } - onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) { + onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password, eip1559Enabled) { return root.ensUsernamesStore.setPubKey(ensUsername.text + (isStatus ? ".stateofus.eth" : "" ), selectedAddress, gasLimit, gasPrice, "", "", - password) + password, + eip1559Enabled) } onSuccess: function(){ usernameUpdated(ensUsername.text); diff --git a/ui/app/AppLayouts/Profile/views/EnsTermsAndConditionsView.qml b/ui/app/AppLayouts/Profile/views/EnsTermsAndConditionsView.qml index 9a69a54527..822f21e062 100644 --- a/ui/app/AppLayouts/Profile/views/EnsTermsAndConditionsView.qml +++ b/ui/app/AppLayouts/Profile/views/EnsTermsAndConditionsView.qml @@ -51,12 +51,13 @@ Item { stickersStore: root.stickersStore asyncGasEstimateTarget: root.stickersStore.stickersModule assetPrice: "10" + chainId: root.ensUsernamesStore.getChainIdForEns() contractAddress: root.ensUsernamesStore.getEnsRegisteredAddress() estimateGasFunction: function(selectedAccount, uuid) { if (username === "" || !selectedAccount) return 380000; return root.ensUsernamesStore.registerEnsGasEstimate(username, selectedAccount.address) } - onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password) { + onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) { return root.ensUsernamesStore.registerEns( username, selectedAddress, @@ -64,7 +65,8 @@ Item { gasPrice, tipLimit, overallLimit, - password + password, + eip1559Enabled, ) } onSuccess: function(){ diff --git a/ui/app/AppLayouts/Profile/views/EnsView.qml b/ui/app/AppLayouts/Profile/views/EnsView.qml index d937e76c28..5a8012176e 100644 --- a/ui/app/AppLayouts/Profile/views/EnsView.qml +++ b/ui/app/AppLayouts/Profile/views/EnsView.qml @@ -325,7 +325,7 @@ Item { onTransactionWasSent: { let url = `${ensView.ensUsernamesStore.getEtherscanLink()}/${txResult}`; Global.displayToastMessage(qsTr("Transaction pending..."), - "", + qsTr("View on etherscan"), "", true, Constants.ephemeralNotificationType.normal, @@ -364,7 +364,7 @@ Item { let url = `${ensView.ensUsernamesStore.getEtherscanLink()}/${txHash}`; Global.displayToastMessage(qsTr("Transaction pending..."), - "", + qsTr("View on etherscan"), icon, false, ephType, diff --git a/ui/app/AppLayouts/stores/RootStore.qml b/ui/app/AppLayouts/stores/RootStore.qml index 022326be8b..8e0f485ab8 100644 --- a/ui/app/AppLayouts/stores/RootStore.qml +++ b/ui/app/AppLayouts/stores/RootStore.qml @@ -76,11 +76,9 @@ QtObject { property string currentCurrency: walletSection.currentCurrency property string signingPhrase: walletSection.signingPhrase - function estimateGas(from_addr, to, assetAddress, value, data) { - return walletSectionTransactions.estimateGas(from_addr, to, assetAddress, value, data) + function estimateGas(from_addr, to, assetSymbol, value, chainId, data) { + return walletSectionTransactions.estimateGas(from_addr, to, assetSymbol, value, chainId, data) } - // TODO change this to use a better store once it is moved out of the ENS module - property string gasPrice: profileSectionStore.ensUsernamesStore.gasPrice function getFiatValue(balance, cryptoSymbo, fiatSymbol) { return profileSectionStore.ensUsernamesStore.getFiatValue(balance, cryptoSymbo, fiatSymbol) } @@ -88,22 +86,29 @@ QtObject { return profileSectionStore.ensUsernamesStore.getGasEthValue(gweiValue, gasLimit) } - function transferEth(from, to, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, uuid) { - return walletSectionTransactions.transferEth(from, to, amount, gasLimit, gasPrice, tipLimit, - overallLimit, password, uuid); + + function transfer(from, to, tokenSymbol, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, chainId, uuid, eip1559Enabled) { + return walletSectionTransactions.transfer( + from, to, tokenSymbol, amount, gasLimit, + gasPrice, tipLimit, overallLimit, password, chainId, uuid, + eip1559Enabled + ); } - function transferTokens(from, to, address, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, uuid) { - return walletSectionTransactions.transferTokens(from, to, address, amount, gasLimit, - gasPrice, tipLimit, overallLimit, password, uuid); + function suggestedFees(chainId) { + return JSON.parse(walletSectionTransactions.suggestedFees(chainId)) } - function isEIP1559Enabled() { - return walletSection.isEIP1559Enabled() + function getChainIdForChat() { + return walletSectionTransactions.getChainIdForChat() } - function suggestedFees() { - return JSON.parse(walletSectionTransactions.suggestedFees()) + function getChainIdForBrowser() { + return walletSectionTransactions.getChainIdForBrowser() + } + + function suggestedRoutes(account, amount, token) { + return JSON.parse(walletSectionTransactions.suggestedRoutes(account, amount, token)).networks } function hex2Eth(value) { diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index 525c978e61..d09b731ff5 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -852,10 +852,6 @@ Item { anchors.centerIn: parent store: appMain.rootStore contactsStore: appMain.rootStore.profileSectionStore.contactsStore - onOpened: { - // Not Refactored Yet -// walletModel.gasView.getGasPrice() - } onClosed: { sendModal.closed() } diff --git a/ui/imports/shared/controls/AssetDelegate.qml b/ui/imports/shared/controls/AssetDelegate.qml index 68e1eb3c6a..ba9ef58658 100644 --- a/ui/imports/shared/controls/AssetDelegate.qml +++ b/ui/imports/shared/controls/AssetDelegate.qml @@ -14,7 +14,7 @@ Item { anchors.right: parent.right anchors.left: parent.left - visible: balance > 0 + visible: networkVisible && enabledNetworkBalance > 0 height: visible ? 40 + 2 * Style.current.padding : 0 @@ -52,7 +52,7 @@ Item { } StyledText { id: assetBalance - text: Utils.toLocaleString(balance, locale) + " " + symbol.toUpperCase() + text: Utils.toLocaleString(enabledNetworkBalance, locale) + " " + symbol.toUpperCase() anchors.top: assetInfoImage.top anchors.topMargin: 0 anchors.right: parent.right @@ -63,7 +63,7 @@ Item { StyledText { id: assetCurrencyBalance color: Style.current.secondaryText - text: Utils.toLocaleString(currencyBalance.toFixed(2), locale) + " " + assetDelegate.currency.toUpperCase() + text: Utils.toLocaleString(enabledNetworkCurrencyBalance.toFixed(2), locale) + " " + assetDelegate.currency.toUpperCase() anchors.right: parent.right anchors.rightMargin: 0 anchors.top: assetBalance.bottom diff --git a/ui/imports/shared/controls/GasSelector.qml b/ui/imports/shared/controls/GasSelector.qml index 5ad0e8cffd..b2a2c9dc91 100644 --- a/ui/imports/shared/controls/GasSelector.qml +++ b/ui/imports/shared/controls/GasSelector.qml @@ -14,12 +14,10 @@ Item { height: visible ? Style.current.smallPadding + prioritytext.height + (advancedMode ? advancedModeItemGroup.height : selectorButtons.height) : 0 - property double gasPrice: 0 - - property bool isEIP1559Enabled: true - - property var suggestedFees: "" + property var suggestedFees: ({ + eip1559Enabled: true + }) property var getGasGweiValue: function () {} property var getGasEthValue: function () {} property var getFiatValue: function () {} @@ -62,8 +60,10 @@ Item { // causes error on application load without this null check if (!inputGasPrice || !inputGasLimit) { return + } let ethValue = root.getGasEthValue(inputGasPrice.text, inputGasLimit.text) + let fiatValue = root.getFiatValue(ethValue, "ETH", root.defaultCurrency) selectedGasEthValue = ethValue selectedGasFiatValue = fiatValue @@ -75,7 +75,7 @@ Item { function checkLimits(){ - if(!isEIP1559Enabled) return; + if(!root.suggestedFees.eip1559Enabled) return; let inputTipLimit = parseFloat(inputPerGasTipLimit.text || "0.00") let inputOverallLimit = parseFloat(inputGasPrice.text || "0.00") @@ -105,9 +105,10 @@ Item { } - Component.onCompleted: { - updateGasEthValue() - checkLimits() + function checkOptimal() { + if (!optimalGasButton.gasRadioBtn.checked) { + optimalGasButton.gasRadioBtn.toggle() + } } function validate() { @@ -132,7 +133,7 @@ Item { inputGasPrice.validationError = root.noInputErrorMessage } - if (isEIP1559Enabled && noPerGasTip) { + if (root.suggestedFees.eip1559Enabled && noPerGasTip) { inputPerGasTipLimit.validationError = root.noInputErrorMessage } @@ -143,7 +144,7 @@ Item { inputGasPrice.validationError = invalidInputErrorMessage } - if (isEIP1559Enabled && isNaN(inputPerGasTipLimit.text)) { + if (root.suggestedFees.eip1559Enabled && isNaN(inputPerGasTipLimit.text)) { inputPerGasTipLimit.validationError = invalidInputErrorMessage } @@ -158,11 +159,10 @@ Item { if (inputPrice <= 0.00) { inputGasPrice.validationError = root.greaterThan0ErrorMessage } - if (isEIP1559Enabled && inputTipLimit <= 0.00) { + if (root.suggestedFees.eip1559Enabled && inputTipLimit <= 0.00) { inputPerGasTipLimit.validationError = root.greaterThan0ErrorMessage } - const isInputValid = inputGasLimit.validationError === "" && inputGasPrice.validationError === "" && (!isEIP1559Enabled || (isEIP1559Enabled && inputPerGasTipLimit.validationError === "")) - + const isInputValid = inputGasLimit.validationError === "" && inputGasPrice.validationError === "" && (!root.suggestedFees.eip1559Enabled || (root.suggestedFees.eip1559Enabled && inputPerGasTipLimit.validationError === "")) return isInputValid } @@ -171,8 +171,7 @@ Item { id: prioritytext anchors.top: parent.top anchors.left: parent.left - //% "Priority" - text: qsTrId("priority") + text: root.suggestedFees.eip1559Enabled ? qsTr("Priority") : qsTr("Gas Price") font.weight: Font.Medium font.pixelSize: 13 color: Style.current.textColor @@ -180,7 +179,7 @@ Item { StyledText { id: baseFeeText - visible: isEIP1559Enabled && advancedMode + visible: root.suggestedFees.eip1559Enabled && advancedMode anchors.top: parent.top anchors.left: prioritytext.right anchors.leftMargin: Style.current.smallPadding @@ -194,6 +193,7 @@ Item { id: buttonAdvanced anchors.verticalCenter: prioritytext.verticalCenter anchors.right: parent.right + visible: root.suggestedFees.eip1559Enabled text: advancedMode ? //% "Use suggestions" qsTrId("use-suggestions") : @@ -205,7 +205,7 @@ Item { Row { id: selectorButtons - visible: !advancedMode + visible: root.suggestedFees.eip1559Enabled && !advancedMode anchors.top: prioritytext.bottom anchors.topMargin: Style.current.halfPadding spacing: 11 @@ -216,10 +216,11 @@ Item { } GasSelectorButton { + id: lowGasButton buttonGroup: gasGroup text: qsTr("Low") price: { - if (!isEIP1559Enabled) return gasPrice; + if (!root.suggestedFees.eip1559Enabled) return root.suggestedFees.gasPrice; return formatDec(root.suggestedFees.maxFeePerGasL, 6) } gasLimit: inputGasLimit ? inputGasLimit.text : "" @@ -227,7 +228,7 @@ Item { getFiatValue: root.getFiatValue defaultCurrency: root.defaultCurrency onChecked: { - if (isEIP1559Enabled){ + if (root.suggestedFees.eip1559Enabled){ inputPerGasTipLimit.text = formatDec(root.suggestedFees.maxPriorityFeePerGas, 2); inputGasPrice.text = formatDec(root.suggestedFees.maxFeePerGasL, 2); } else { @@ -240,14 +241,13 @@ Item { GasSelectorButton { id: optimalGasButton buttonGroup: gasGroup - checkedByDefault: true //% "Optimal" text: qsTrId("optimal") price: { - if (!isEIP1559Enabled) { + if (!root.suggestedFees.eip1559Enabled) { // Setting the gas price field here because the binding didn't work - inputGasPrice.text = root.gasPrice - return root.gasPrice + inputGasPrice.text = root.suggestedFees.gasPrice + return root.suggestedFees.gasPrice } return formatDec(root.suggestedFees.maxFeePerGasM, 6) @@ -257,11 +257,11 @@ Item { getFiatValue: root.getFiatValue defaultCurrency: root.defaultCurrency onChecked: { - if (isEIP1559Enabled){ + if (root.suggestedFees.eip1559Enabled){ inputPerGasTipLimit.text = formatDec(root.suggestedFees.maxPriorityFeePerGas, 2); inputGasPrice.text = formatDec(root.suggestedFees.maxFeePerGasM, 2); } else { - inputGasPrice.text = root.gasPrice + inputGasPrice.text = root.suggestedFees.gasPrice } root.updateGasEthValue() root.checkLimits() @@ -269,10 +269,11 @@ Item { } GasSelectorButton { + id: highGasButton buttonGroup: gasGroup text: qsTr("High") price: { - if (!isEIP1559Enabled) return gasPrice; + if (!root.suggestedFees.eip1559Enabled) return root.suggestedFees.gasPrice; return formatDec(root.suggestedFees.maxFeePerGasH,6); } gasLimit: inputGasLimit ? inputGasLimit.text : "" @@ -280,7 +281,7 @@ Item { getFiatValue: root.getFiatValue defaultCurrency: root.defaultCurrency onChecked: { - if (isEIP1559Enabled){ + if (root.suggestedFees.eip1559Enabled){ inputPerGasTipLimit.text = formatDec(root.suggestedFees.maxPriorityFeePerGas, 2); inputGasPrice.text = formatDec(root.suggestedFees.maxFeePerGasH, 2); } else { @@ -296,7 +297,7 @@ Item { id: advancedModeItemGroup anchors.top: prioritytext.bottom anchors.topMargin: 14 - visible: root.advancedMode + visible: !root.suggestedFees.eip1559Enabled || root.advancedMode width: parent.width height: childrenRect.height @@ -309,7 +310,7 @@ Item { customHeight: 56 anchors.top: parent.top anchors.left: parent.left - anchors.right: isEIP1559Enabled ? inputPerGasTipLimit.left : inputGasPrice.left + anchors.right: root.suggestedFees.eip1559Enabled ? inputPerGasTipLimit.left : inputGasPrice.left anchors.rightMargin: Style.current.padding placeholderText: "21000" validator: IntValidator{ @@ -333,7 +334,7 @@ Item { anchors.right: inputGasPrice.left anchors.rightMargin: Style.current.padding anchors.left: undefined - visible: isEIP1559Enabled + visible: root.suggestedFees.eip1559Enabled width: 125 customHeight: 56 text: formatDec(root.suggestedFees.maxPriorityFeePerGas, 2); @@ -350,7 +351,7 @@ Item { color: Style.current.secondaryText //% "Gwei" text: qsTrId("gwei") - visible: isEIP1559Enabled + visible: root.suggestedFees.eip1559Enabled anchors.top: parent.top anchors.topMargin: 42 anchors.right: inputPerGasTipLimit.right @@ -405,6 +406,7 @@ Item { StyledText { id: maxPriorityFeeText anchors.left: parent.left + visible: root.suggestedFees.eip1559Enabled //% "Maximum priority fee: %1 ETH" text: { let v = selectedGasEthValue > 0.00009 ? selectedGasEthValue : @@ -420,6 +422,7 @@ Item { StyledText { id: maxPriorityFeeFiatText text: root.maxFiatFees + visible: root.suggestedFees.eip1559Enabled anchors.verticalCenter: maxPriorityFeeText.verticalCenter anchors.left: maxPriorityFeeText.right anchors.leftMargin: 6 @@ -432,6 +435,7 @@ Item { id: maxPriorityFeeDetailsText //% "Maximum overall price for the transaction. If the block base fee exceeds this, it will be included in a following block with a lower base fee." text: qsTrId("maximum-overall-price-for-the-transaction--if-the-block-base-fee-exceeds-this--it-will-be-included-in-a-following-block-with-a-lower-base-fee-") + visible: root.suggestedFees.eip1559Enabled width: parent.width anchors.top: maxPriorityFeeText.bottom anchors.topMargin: Style.current.smallPadding diff --git a/ui/imports/shared/controls/GasValidator.qml b/ui/imports/shared/controls/GasValidator.qml index a9842986d0..48bce5650a 100644 --- a/ui/imports/shared/controls/GasValidator.qml +++ b/ui/imports/shared/controls/GasValidator.qml @@ -19,12 +19,14 @@ Column { property double selectedAmount property var selectedAsset property double selectedGasEthValue + property var selectedNetwork property bool isValid: false onSelectedAccountChanged: validate() onSelectedAmountChanged: validate() onSelectedAssetChanged: validate() onSelectedGasEthValueChanged: validate() + onSelectedNetworkChanged: validate() function validate() { let isValid = true @@ -36,8 +38,10 @@ Column { if (selectedAsset && selectedAsset.symbol && selectedAsset.symbol.toUpperCase() === "ETH") { gasTotal += selectedAmount } - const currAcctGasAsset = Utils.findAssetBySymbol(selectedAccount.assets, "ETH") - if (currAcctGasAsset && currAcctGasAsset.value < gasTotal) { + const chainId = (selectedNetwork && selectedNetwork.chainId) || Global.currentChainId + + const currAcctGasAsset = Utils.findAssetByChainAndSymbol(chainId, selectedAccount.assets, "ETH") + if (currAcctGasAsset && currAcctGasAsset.totalBalance < gasTotal) { isValid = false } root.isValid = isValid diff --git a/ui/imports/shared/controls/chat/GasSelectorButton.qml b/ui/imports/shared/controls/chat/GasSelectorButton.qml index 7e5d28c8d1..cdd2900aac 100644 --- a/ui/imports/shared/controls/chat/GasSelectorButton.qml +++ b/ui/imports/shared/controls/chat/GasSelectorButton.qml @@ -20,6 +20,7 @@ Rectangle { property var getGasEthValue: function () {} property var getFiatValue: function () {} + property alias gasRadioBtn: gasRadioBtn function formatDec(num, dec){ return Math.round((num + Number.EPSILON) * Math.pow(10, dec)) / Math.pow(10, dec) diff --git a/ui/imports/shared/panels/BalanceValidator.qml b/ui/imports/shared/panels/BalanceValidator.qml index 6648da7fe3..37de650804 100644 --- a/ui/imports/shared/panels/BalanceValidator.qml +++ b/ui/imports/shared/panels/BalanceValidator.qml @@ -12,6 +12,7 @@ Column { visible: !isValid spacing: 5 + property int chainId property var account property double amount property var asset @@ -27,7 +28,7 @@ Column { if (!(account && account.assets && asset && amount >= 0)) { return root.isValid } - const currAcctAsset = Utils.findAssetBySymbol(account.assets, asset.symbol) + const currAcctAsset = Utils.findAssetByChainAndSymbol(root.chainId, account.assets, asset.symbol) if (currAcctAsset && currAcctAsset.value < amount) { isValid = false diff --git a/ui/imports/shared/popups/SendModal.qml b/ui/imports/shared/popups/SendModal.qml index 51d3f81ad7..f35add41db 100644 --- a/ui/imports/shared/popups/SendModal.qml +++ b/ui/imports/shared/popups/SendModal.qml @@ -39,32 +39,41 @@ StatusModal { function sendTransaction() { stack.currentGroup.isPending = true let success = false - if(advancedHeader.assetSelector.selectedAsset.address === "" || advancedHeader.assetSelector.selectedAsset.address === Constants.zeroAddress){ - success = popup.store.transferEth( - advancedHeader.accountSelector.selectedAccount.address, - advancedHeader.recipientSelector.selectedRecipient.address, - advancedHeader.amountToSendInput.text, - gasSelector.selectedGasLimit, - gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, - gasSelector.selectedTipLimit, - gasSelector.selectedOverallLimit, - transactionSigner.enteredPassword, - stack.uuid) - } else { - success = popup.store.transferTokens( - advancedHeader.accountSelector.selectedAccount.address, - advancedHeader.recipientSelector.selectedRecipient.address, - advancedHeader.assetSelector.selectedAsset.address, - advancedHeader.amountToSendInput.text, - gasSelector.selectedGasLimit, - gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, - gasSelector.selectedTipLimit, - gasSelector.selectedOverallLimit, - transactionSigner.enteredPassword, - stack.uuid) - } + success = popup.store.transfer( + advancedHeader.accountSelector.selectedAccount.address, + advancedHeader.recipientSelector.selectedRecipient.address, + advancedHeader.assetSelector.selectedAsset.symbol, + advancedHeader.amountToSendInput.text, + gasSelector.selectedGasLimit, + gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.selectedTipLimit, + gasSelector.selectedOverallLimit, + transactionSigner.enteredPassword, + networkSelector.selectedNetwork.chainId || Global.currentChainId, + stack.uuid, + gasSelector.suggestedFees.eip1559Enabled, + ) } + property var recalculateRoutesAndFees: Backpressure.debounce(popup, 600, function() { + if (!popup.store.isMultiNetworkEnabled) { + return + } + + networkSelector.suggestedRoutes = popup.store.suggestedRoutes( + advancedHeader.accountSelector.selectedAccount.address, advancedHeader.amountToSendInput.text, advancedHeader.assetSelector.selectedAsset.symbol + ) + if (networkSelector.suggestedRoutes.length) { + networkSelector.selectedNetwork = networkSelector.suggestedRoutes[0] + gasSelector.suggestedFees = popup.store.suggestedFees(networkSelector.suggestedRoutes[0].chainId) + gasSelector.checkOptimal() + gasSelector.visible = true + } else { + networkSelector.selectedNetwork = "" + gasSelector.visible = false + } + }) + width: 556 // To-Do as per design once the account selector become floating the heigth can be as defined in design as 595 height: 670 @@ -81,13 +90,22 @@ StatusModal { advancedHeader.recipientSelector.selectedType = RecipientSelector.Type.Contact advancedHeader.recipientSelector.readOnly = true advancedHeader.recipientSelector.selectedRecipient = popup.preSelectedRecipient + } if(popup.preSelectedAccount) { advancedHeader.accountSelector.selectedAccount = popup.preSelectedAccount } } + + if (popup.store.isMultiNetworkEnabled) { + popup.recalculateRoutesAndFees() + } else { + gasSelector.suggestedFees = popup.store.suggestedFees(Global.currentChainId) + gasSelector.checkOptimal() + } } + advancedHeaderComponent: SendModalHeader { store: popup.store contactsStore: popup.contactsStore @@ -95,6 +113,18 @@ StatusModal { if(popup.contentItem.currentGroup.isValid) gasSelector.estimateGas() } + + onAssetChanged: function() { + popup.recalculateRoutesAndFees() + } + + onSelectedAccountChanged: function() { + popup.recalculateRoutesAndFees() + } + + onAmountToSendChanged: function() { + popup.recalculateRoutesAndFees() + } } contentItem: TransactionStackView { @@ -115,7 +145,7 @@ StatusModal { ScrollBar.vertical.policy: ScrollBar.AlwaysOff ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - contentHeight: addressSelector.height + networkAndFeesSelector.height + gasSelector.height + gasValidator.height + contentHeight: addressSelector.height + networkSelector.height + gasSelector.height + gasValidator.height clip: true TabAddressSelectorView { @@ -130,45 +160,45 @@ StatusModal { } } - TabNetworkAndFees { - id: networkAndFeesSelector + NetworkSelector { + id: networkSelector anchors.top: addressSelector.bottom anchors.right: parent.right anchors.left: parent.left visible: popup.store.isMultiNetworkEnabled + onNetworkChanged: function(chainId) { + gasSelector.suggestedFees = popup.store.suggestedFees(chainId) + } } GasSelector { id: gasSelector - anchors.top: networkAndFeesSelector.visible ? networkAndFeesSelector.bottom : addressSelector.bottom - gasPrice: parseFloat(popup.store.gasPrice) + anchors.top: networkSelector.bottom getGasEthValue: popup.store.getGasEthValue getFiatValue: popup.store.getFiatValue defaultCurrency: popup.store.currentCurrency - isEIP1559Enabled: popup.store.isEIP1559Enabled() - suggestedFees: popup.store.suggestedFees() - - visible: !popup.store.isMultiNetworkEnabled width: stack.width property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { if (!(advancedHeader.accountSelector.selectedAccount && advancedHeader.accountSelector.selectedAccount.address && - advancedHeader.recipientSelector.selectedRecipient && advancedHeader.recipientSelector.selectedRecipient.address && - advancedHeader.assetSelector.selectedAsset && advancedHeader.assetSelector.selectedAsset.address && - advancedHeader.amountToSendInput.text)) { + advancedHeader.recipientSelector.selectedRecipient && advancedHeader.recipientSelector.selectedRecipient.address && + advancedHeader.assetSelector.selectedAsset && advancedHeader.assetSelector.selectedAsset.symbol && + advancedHeader.amountToSendInput.text)) { selectedGasLimit = 250000 defaultGasLimit = selectedGasLimit return } let gasEstimate = JSON.parse(popup.store.estimateGas( - advancedHeader.accountSelector.selectedAccount.address, - advancedHeader.recipientSelector.selectedRecipient.address, - advancedHeader.assetSelector.selectedAsset.address, - advancedHeader.amountToSendInput.text, - "")) + advancedHeader.accountSelector.selectedAccount.address, + advancedHeader.recipientSelector.selectedRecipient.address, + advancedHeader.assetSelector.selectedAsset.symbol, + advancedHeader.amountToSendInput.text, + networkSelector.selectedNetwork.chainId || Global.currentChainId, + "")) if (!gasEstimate.success) { + //% "Error estimating gas: %1" console.warn(qsTrId("error-estimating-gas---1").arg(gasEstimate.error.message)) return @@ -178,6 +208,7 @@ StatusModal { defaultGasLimit = selectedGasLimit }) } + GasValidator { id: gasValidator anchors.top: gasSelector.bottom @@ -186,6 +217,7 @@ StatusModal { : parseFloat(advancedHeader.amountToSendInput.text) selectedAsset: advancedHeader.assetSelector.selectedAsset selectedGasEthValue: gasSelector.selectedGasEthValue + selectedNetwork: networkSelector.selectedNetwork } } } @@ -217,7 +249,7 @@ StatusModal { return popup.sendTransaction() } - if(gasSelector.isEIP1559Enabled && popup.contentItem.currentGroup === group1 && gasSelector.advancedMode){ + if(gasSelector.suggestedFees.eip1559Enabled && popup.contentItem.currentGroup === group1 && gasSelector.advancedMode){ if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){ Global.openPopup(transactionSettingsConfirmationPopupComponent, { currentBaseFee: gasSelector.suggestedFees.baseFee, @@ -277,7 +309,7 @@ StatusModal { let url = `${popup.store.getEtherscanLink()}/${response.result}` Global.displayToastMessage(qsTr("Transaction pending..."), - "", + qsTr("View on etherscan"), "", true, Constants.ephemeralNotificationType.normal, diff --git a/ui/imports/shared/popups/SignTransactionModal.qml b/ui/imports/shared/popups/SignTransactionModal.qml index c5e22d983f..d1d6732742 100644 --- a/ui/imports/shared/popups/SignTransactionModal.qml +++ b/ui/imports/shared/popups/SignTransactionModal.qml @@ -33,37 +33,27 @@ StatusModal { property bool isARequest: false property string msgId: "" property string trxData: "" + property int chainId property alias transactionSigner: transactionSigner property var sendTransaction: function() { stack.currentGroup.isPending = true let success = false - if(root.selectedAsset.address === "" || root.selectedAsset.address === Constants.zeroAddress){ - success = root.store.transferEth( - selectFromAccount.selectedAccount.address, - selectRecipient.selectedRecipient.address, - root.selectedAmount, - gasSelector.selectedGasLimit, - gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, - gasSelector.selectedTipLimit, - gasSelector.selectedOverallLimit, - transactionSigner.enteredPassword, - stack.uuid) - } else { - success = root.store.transferTokens( - selectFromAccount.selectedAccount.address, - selectRecipient.selectedRecipient.address, - root.selectedAsset.address, - root.selectedAmount, - gasSelector.selectedGasLimit, - gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, - gasSelector.selectedTipLimit, - gasSelector.selectedOverallLimit, - transactionSigner.enteredPassword, - stack.uuid) - } - + success = root.store.transfer( + selectFromAccount.selectedAccount.address, + selectRecipient.selectedRecipient.address, + root.selectedAsset.symbol, + root.selectedAmount, + gasSelector.selectedGasLimit, + gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.selectedTipLimit, + gasSelector.selectedOverallLimit, + transactionSigner.enteredPassword, + root.chainId, + stack.uuid, + gasSelector.suggestedFees.eip1559Enabled, + ) // TODO remove this else once the thread and connection are back // if(!success){ // //% "Invalid transaction parameters" @@ -85,6 +75,11 @@ StatusModal { stack.pop(groupPreview, StackView.Immediate) } + onOpened: { + gasSelector.suggestedFees = root.store.suggestedFees(root.chainId) + gasSelector.checkOptimal() + } + contentItem: Item { width: root.width height: childrenRect.height @@ -127,6 +122,7 @@ StatusModal { //% "Choose account" label: qsTrId("choose-account") showBalanceForAssetSymbol: root.selectedAsset.symbol + chainId: root.chainId minRequiredAssetBalance: parseFloat(root.selectedAmount) onSelectedAccountChanged: if (isValid) { gasSelector.estimateGas() } } @@ -152,18 +148,15 @@ StatusModal { GasSelector { id: gasSelector anchors.topMargin: Style.current.padding - gasPrice: parseFloat(root.store.gasPrice) getGasEthValue: root.store.getGasEthValue getFiatValue: root.store.getFiatValue defaultCurrency: root.store.currentCurrency - isEIP1559Enabled: root.store.isEIP1559Enabled() - suggestedFees: root.store.suggestedFees() width: stack.width property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { if (!(selectFromAccount.selectedAccount && selectFromAccount.selectedAccount.address && selectRecipient.selectedRecipient && selectRecipient.selectedRecipient.address && - root.selectedAsset && root.selectedAsset.address && + root.selectedAsset && root.selectedAsset.symbol && root.selectedAmount)) { selectedGasLimit = 250000 defaultGasLimit = selectedGasLimit @@ -173,9 +166,11 @@ StatusModal { let gasEstimate = JSON.parse(root.store.estimateGas( selectFromAccount.selectedAccount.address, selectRecipient.selectedRecipient.address, - root.selectedAsset.address, + root.selectedAsset.symbol, root.selectedAmount, - trxData)) + root.chainId, + trxData + )) if (!gasEstimate.success) { let message = qsTr("Error estimating gas: %1").arg(gasEstimate.error.message) @@ -235,6 +230,7 @@ StatusModal { anchors.horizontalCenter: parent.horizontalCenter account: selectFromAccount.selectedAccount amount: !!root.selectedAmount ? parseFloat(root.selectedAmount) : 0.0 + chainId: root.chainId asset: root.selectedAsset } GasValidator { @@ -297,13 +293,13 @@ StatusModal { if (validity.isValid && !validity.isPending) { if (stack.isLastGroup) { return root.sendTransaction(gasSelector.selectedGasLimit, - gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice, gasSelector.selectedTipLimit, gasSelector.selectedOverallLimit, transactionSigner.enteredPassword) } - if(gasSelector.isEIP1559Enabled && stack.currentGroup === groupSelectGas && gasSelector.advancedMode){ + if(gasSelector.suggestedFees.eip1559Enabled && stack.currentGroup === groupSelectGas && gasSelector.advancedMode){ if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){ Global.openPopup(transactionSettingsConfirmationPopupComponent, { currentBaseFee: gasSelector.suggestedFees.baseFee, @@ -366,7 +362,7 @@ StatusModal { // Refactor this let url = "" //`${walletModel.utilsView.etherscanLink}/${response.result}` Global.displayToastMessage(qsTr("Transaction pending..."), - "", + qsTr("View on etherscan"), "", true, Constants.ephemeralNotificationType.normal, diff --git a/ui/imports/shared/status/StatusETHTransactionModal.qml b/ui/imports/shared/status/StatusETHTransactionModal.qml index d3a8086a9d..693ab08f0b 100644 --- a/ui/imports/shared/status/StatusETHTransactionModal.qml +++ b/ui/imports/shared/status/StatusETHTransactionModal.qml @@ -20,6 +20,7 @@ ModalPopup { property var contactsStore property string ensUsername + property int chainId readonly property var asset: {"name": "Ethereum", "symbol": "ETH"} title: qsTr("Contract interaction") @@ -35,10 +36,11 @@ ModalPopup { let responseStr = root.ensUsernamesStore.setPubKey(root.ensUsername, selectFromAccount.selectedAccount.address, gasSelector.selectedGasLimit, - gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice, gasSelector.selectedTipLimit, gasSelector.selectedOverallLimit, - transactionSigner.enteredPassword) + transactionSigner.enteredPassword, + gasSelector.suggestedFees.eip1559Enabled) let response = JSON.parse(responseStr) if (!response.success) { @@ -60,6 +62,11 @@ ModalPopup { } } + onOpened: { + gasSelector.suggestedFees = root.ensUsernamesStore.suggestedFees(root.chainId) + gasSelector.checkOptimal() + } + property MessageDialog sendingError: MessageDialog { id: sendingError //% "Error sending the transaction" @@ -96,6 +103,7 @@ ModalPopup { } currency: root.ensUsernamesStore.getCurrentCurrency() width: stack.width + chainId: root.chainId //% "Choose account" label: qsTrId("choose-account") showBalanceForAssetSymbol: "ETH" @@ -116,12 +124,9 @@ ModalPopup { visible: true anchors.top: selectFromAccount.bottom anchors.topMargin: Style.current.padding - gasPrice: parseFloat(root.ensUsernamesStore.gasPrice) getGasEthValue: root.ensUsernamesStore.getGasEthValue getFiatValue: root.ensUsernamesStore.getFiatValue defaultCurrency: root.ensUsernamesStore.getCurrentCurrency() - isEIP1559Enabled: root.ensUsernamesStore.isEIP1559Enabled() - suggestedFees: root.ensUsernamesStore.suggestedFees() property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount); @@ -217,7 +222,7 @@ ModalPopup { return root.sendTransaction() } - if(gasSelector.isEIP1559Enabled && stack.currentGroup === group3 && gasSelector.advancedMode){ + if(gasSelector.suggestedFees.eip1559Enabled && stack.currentGroup === group3 && gasSelector.advancedMode){ if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){ Global.openPopup(transactionSettingsConfirmationPopupComponent, { currentBaseFee: gasSelector.suggestedFees.baseFee, diff --git a/ui/imports/shared/status/StatusSNTTransactionModal.qml b/ui/imports/shared/status/StatusSNTTransactionModal.qml index 58be0093db..c946213042 100644 --- a/ui/imports/shared/status/StatusSNTTransactionModal.qml +++ b/ui/imports/shared/status/StatusSNTTransactionModal.qml @@ -22,13 +22,13 @@ ModalPopup { readonly property var asset: JSON.parse(root.stickersStore.getStatusToken()) property string assetPrice property string contractAddress + property int chainId property var estimateGasFunction: (function(userAddress, uuid) { return 0; }) - property var onSendTransaction: (function(userAddress, gasLimit, gasPrice, tipLimit, overallLimit, password){ return ""; }) + property var onSendTransaction: (function(userAddress, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled){ return ""; }) property var onSuccess: (function(){}) property var asyncGasEstimateTarget Component.onCompleted: { - root.stickersStore.fetchGasPrice(); gasSelector.estimateGas(); } @@ -56,10 +56,11 @@ ModalPopup { function sendTransaction() { let responseStr = onSendTransaction(selectFromAccount.selectedAccount.address, gasSelector.selectedGasLimit, - gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice, gasSelector.selectedTipLimit, gasSelector.selectedOverallLimit, - transactionSigner.enteredPassword); + transactionSigner.enteredPassword, + gasSelector.suggestedFees.eip1559Enabled); let response = JSON.parse(responseStr) if (!response.success) { @@ -76,6 +77,11 @@ ModalPopup { root.close(); } + onOpened: { + gasSelector.suggestedFees = root.store.suggestedFees(root.chainId) + gasSelector.checkOptimal() + } + TransactionStackView { id: stack height: parent.height @@ -111,6 +117,7 @@ ModalPopup { label: qsTrId("choose-account") showBalanceForAssetSymbol: root.asset.symbol minRequiredAssetBalance: root.assetPrice + chainId: root.chainId onSelectedAccountChanged: if (isValid) { gasSelector.estimateGas() } } RecipientSelector { @@ -134,12 +141,9 @@ ModalPopup { id: gasSelector anchors.top: selectFromAccount.bottom anchors.topMargin: Style.current.padding - gasPrice: parseFloat(root.stickersStore.gasPrice) getGasEthValue: root.stickersStore.getGasEthValue getFiatValue: root.stickersStore.getFiatValue defaultCurrency: root.stickersStore.getCurrentCurrency() - isEIP1559Enabled: root.store.isEIP1559Enabled() - suggestedFees: root.store.suggestedFees() width: stack.width property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { @@ -243,7 +247,7 @@ ModalPopup { return root.sendTransaction() } - if(gasSelector.isEIP1559Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){ + if(gasSelector.suggestedFees.eip1559Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){ if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){ Global.openPopup(transactionSettingsConfirmationPopupComponent, { currentBaseFee: gasSelector.suggestedFees.baseFee, diff --git a/ui/imports/shared/status/StatusStickerMarket.qml b/ui/imports/shared/status/StatusStickerMarket.qml index 62badecc9e..efd7435bf5 100644 --- a/ui/imports/shared/status/StatusStickerMarket.qml +++ b/ui/imports/shared/status/StatusStickerMarket.qml @@ -107,18 +107,20 @@ Item { contractAddress: root.store.stickersStore.getStickersMarketAddress() contactsStore: root.store.contactsStore assetPrice: price + chainId: root.store.stickersStore.getChainIdForStickers() estimateGasFunction: function(selectedAccount, uuid) { if (packId < 0 || !selectedAccount || !price) return 325000 return root.store.stickersStore.estimate(packId, selectedAccount.address, price, uuid) } - onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password) { + onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) { return root.store.stickersStore.buy(packId, selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, - password) + password, + eip1559Enabled) } onClosed: { destroy() diff --git a/ui/imports/shared/status/StatusStickerPackClickPopup.qml b/ui/imports/shared/status/StatusStickerPackClickPopup.qml index ecc43776f9..287e9aeaf2 100644 --- a/ui/imports/shared/status/StatusStickerPackClickPopup.qml +++ b/ui/imports/shared/status/StatusStickerPackClickPopup.qml @@ -68,18 +68,20 @@ ModalPopup { contactsStore: stickerPackDetailsPopup.store.contactsStore contractAddress: root.store.stickersStore.getStickersMarketAddress() assetPrice: price + chainId: root.store.stickersStore.getChainIdForStickers() estimateGasFunction: function(selectedAccount, uuid) { if (packId < 0 || !selectedAccount || !price) return 325000 return stickerPackDetailsPopup.store.stickersStore.estimate(packId, selectedAccount.address, price, uuid) } - onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password) { + onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password, eip1559Enabled) { return root.store.stickersStore.buy(packId, selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, - password) + password, + eip1559Enabled) } onClosed: { destroy() diff --git a/ui/imports/shared/views/NetworkSelector.qml b/ui/imports/shared/views/NetworkSelector.qml new file mode 100644 index 0000000000..2a272a175f --- /dev/null +++ b/ui/imports/shared/views/NetworkSelector.qml @@ -0,0 +1,130 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 +import QtQuick.Dialogs 1.3 + +import utils 1.0 +import shared.stores 1.0 + +import StatusQ.Controls 0.1 +import StatusQ.Popups 0.1 +import StatusQ.Components 0.1 +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + +import "../panels" +import "../controls" +import "../views" + +Item { + id: root + height: visible ? stackLayout.height + 2* Style.current.xlPadding : 0 + + signal networkChanged(int chainId) + + property var suggestedRoutes: "" + property var selectedNetwork: "" + + StackLayout { + id: stackLayout + anchors.top: parent.top + anchors.topMargin: Style.current.xlPadding + height: simpleLayout.height + width: parent.width + currentIndex: 0 + + ColumnLayout { + id: simpleLayout + Layout.fillWidth: true + spacing: 24 + Rectangle { + id: networksRect + radius: 13 + color: Theme.palette.indirectColor1 + Layout.fillWidth: true + Layout.preferredHeight: layout.height + 24 + ColumnLayout { + id: layout + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 16 + spacing: 20 + RowLayout { + spacing: 10 + StatusRoundIcon { + Layout.alignment: Qt.AlignTop + radius: 8 + icon.name: "flash" + } + ColumnLayout { + StatusBaseText { + Layout.maximumWidth: 410 + font.pixelSize: 15 + font.weight: Font.Medium + color: Theme.palette.directColor1 + //% "Networks" + text: qsTr("Networks") + wrapMode: Text.WordWrap + } + StatusBaseText { + Layout.maximumWidth: 410 + font.pixelSize: 15 + color: Theme.palette.baseColor1 + text: qsTr("Choose a network to use for the transaction") + wrapMode: Text.WordWrap + } + } + } + StatusBaseText { + visible: suggestedRoutes.length === 0 + font.pixelSize: 15 + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + color: Theme.palette.dangerColor1 + text: qsTr("No networks available") + wrapMode: Text.WordWrap + } + + Item { + Layout.fillWidth: true + height: 50 + ScrollView { + width: parent.width + contentWidth: row.width + contentHeight: row.height + 10 + ScrollBar.vertical.policy: ScrollBar.AlwaysOff + ScrollBar.horizontal.policy: ScrollBar.AlwaysOn + clip: true + Row { + id: row + spacing: 16 + Repeater { + id: repeater + model: suggestedRoutes + StatusListItem { + id: item + implicitWidth: 126 + title: modelData.chainName + subTitle: "" + image.source: Style.png("networks/" + modelData.chainName.toLowerCase()) + image.width: 32 + image.height: 32 + leftPadding: 5 + rightPadding: 5 + color: "transparent" + border.color: Style.current.primary + border.width: root.selectedNetwork.chainId === modelData.chainId ? 1 : 0 + onClicked: { + root.selectedNetwork = modelData + root.networkChanged(modelData.chainId) + } + } + } + } + } + } + } + } + } + } +} diff --git a/ui/imports/shared/views/SendModalHeader.qml b/ui/imports/shared/views/SendModalHeader.qml index 10b7abd3f9..b5c185d834 100644 --- a/ui/imports/shared/views/SendModalHeader.qml +++ b/ui/imports/shared/views/SendModalHeader.qml @@ -27,9 +27,13 @@ Rectangle { property var estimateGas: function() {} property bool isReady: amountToSendInput.valid && !amountToSendInput.pending && recipientSelector.isValid && !recipientSelector.isPending + signal assetChanged() + signal selectedAccountChanged() + signal amountToSendChanged() + QtObject { id: _internal - property string maxFiatBalance: Utils.stripTrailingZeros(parseFloat(assetSelector.selectedAsset.balance).toFixed(4)) + property string maxFiatBalance: Utils.stripTrailingZeros(parseFloat(assetSelector.selectedAsset.totalBalance).toFixed(4)) //% "Please enter a valid amount" property string sendAmountInputErrorMessage: qsTr("Please enter a valid amount") //% "Max:" @@ -86,6 +90,7 @@ Rectangle { } }) if (isValid) { estimateGas() } + header.selectedAccountChanged() } showAccountDetails: false selectField.select.height: 32 @@ -103,7 +108,7 @@ Rectangle { } StatusListItemTag { //% "No balances active" - title: assetSelector.selectedAsset.balance > 0 ? _internal.maxString + (assetSelector.selectedAsset ? _internal.maxFiatBalance : "0.00") : qsTr("No balances active") + title: assetSelector.selectedAsset.totalBalance > 0 ? _internal.maxString + (assetSelector.selectedAsset ? _internal.maxFiatBalance : "0.00") : qsTr("No balances active") closeButtonVisible: false titleText.font.pixelSize: 12 height: 22 @@ -139,6 +144,7 @@ Rectangle { txtFiatBalance.text = header.store.getFiatValue(amount, assetSelector.selectedAsset.symbol, header.store.currentCurrency) } estimateGas() + header.amountToSendChanged() } } StatusAssetSelector { @@ -161,6 +167,7 @@ Rectangle { } txtFiatBalance.text = header.store.getFiatValue(amountToSendInput.text, assetSelector.selectedAsset.symbol, header.store.currentCurrency) estimateGas() + header.assetChanged() } } } diff --git a/ui/imports/shared/views/TabNetworkAndFees.qml b/ui/imports/shared/views/TabNetworkAndFees.qml deleted file mode 100644 index b3e0325bb1..0000000000 --- a/ui/imports/shared/views/TabNetworkAndFees.qml +++ /dev/null @@ -1,208 +0,0 @@ -import QtQuick 2.13 -import QtQuick.Controls 2.13 -import QtQuick.Layouts 1.13 -import QtQuick.Dialogs 1.3 - -import utils 1.0 -import shared.stores 1.0 - -import StatusQ.Controls 0.1 -import StatusQ.Popups 0.1 -import StatusQ.Components 0.1 -import StatusQ.Core 0.1 -import StatusQ.Core.Theme 0.1 - -import "../panels" -import "../controls" -import "../views" - -Item { - id: root - height: visible ? stackLayout.height + modeSelectionTabBar.height + 2*Style.current.xlPadding: 0 - - signal contactSelected(string address, int type) - - StatusSwitchTabBar { - id: modeSelectionTabBar - anchors.horizontalCenter: parent.horizontalCenter - StatusSwitchTabButton { - //% "Simple" - text: qsTr("Simple") - } - StatusSwitchTabButton { - //% "Advanced" - text: qsTr("Advanced") - } - StatusSwitchTabButton { - //% "Custom" - text: qsTr("Custom") - } - } - - StackLayout { - id: stackLayout - anchors.top: modeSelectionTabBar.bottom - anchors.topMargin: Style.current.xlPadding - height: simpleLayout.height - width: parent.width - currentIndex: modeSelectionTabBar.currentIndex - - ColumnLayout { - id: simpleLayout - Layout.fillWidth: true - spacing: 24 - // To-do networks depends on multi networks and fee suggestions not available yet - Rectangle { - id: networksRect - radius: 13 - color: Theme.palette.indirectColor1 - Layout.fillWidth: true - Layout.preferredHeight: layout.height + 24 - ColumnLayout { - id: layout - anchors.top: parent.top - anchors.left: parent.left - anchors.margins: 16 - spacing: 20 - RowLayout { - spacing: 10 - StatusRoundIcon { - Layout.alignment: Qt.AlignTop - radius: 8 - icon.name: "flash" - } - ColumnLayout { - StatusBaseText { - Layout.maximumWidth: 410 - font.pixelSize: 15 - font.weight: Font.Medium - color: Theme.palette.directColor1 - //% "Networks" - text: qsTr("Networks") - wrapMode: Text.WordWrap - } - StatusBaseText { - Layout.maximumWidth: 410 - font.pixelSize: 15 - color: Theme.palette.baseColor1 - //% "The networks where the receipient will receive tokens. Amounts calculated automatically for the lowest cost." - text: qsTr("The networks where the receipient will receive tokens. Amounts calculated automatically for the lowest cost.") - wrapMode: Text.WordWrap - } - } - } - RowLayout { - spacing: 16 - Layout.leftMargin: 40 - Repeater { - model: 3 - StatusListItem { - implicitWidth: 126 - title: "PlaceHolder" + index - subTitle: "" - icon.isLetterIdenticon: true - icon.width: 32 - icon.height: 32 - leftPadding: 0 - rightPadding: 0 - color: "transparent" - } - } - } - } - } - - Rectangle { - id: feesRect - radius: 13 - color: Theme.palette.indirectColor1 - Layout.fillWidth: true - Layout.preferredHeight: feesLayout.height + 32 - RowLayout { - id: feesLayout - anchors.top: parent.top - anchors.left: parent.left - anchors.margins: 16 - spacing: 10 - StatusRoundIcon { - Layout.alignment: Qt.AlignTop - radius: 8 - icon.name: "fees" - } - ColumnLayout { - spacing: 12 - StatusBaseText { - Layout.maximumWidth: 410 - font.pixelSize: 15 - font.weight: Font.Medium - color: Theme.palette.directColor1 - //% "Fees" - text: qsTr("Fees") - wrapMode: Text.WordWrap - } - RowLayout { - spacing: 16 - Repeater { - model: 3 - delegate: - RadioDelegate { - id: control - checked: index === 0 - contentItem.visible: false - indicator.visible: false - background: Rectangle { - implicitWidth: 128 - implicitHeight: 78 - radius: 8 - color: control.checked ? Theme.palette.indirectColor1: Theme.palette.baseColor4 - border.width: control.checked - border.color: Theme.palette.primaryColor2 - ColumnLayout { - width: parent.width - anchors.top: parent.top - anchors.left: parent.left - anchors.margins: 8 - StatusBaseText { - font.pixelSize: 15 - color: Theme.palette.baseColor1 - //% "Slow" - text: qsTr("Slow") - wrapMode: Text.WordWrap - } - StatusBaseText { - font.pixelSize: 13 - color: Theme.palette.baseColor1 - text: "0.24 USD" - wrapMode: Text.WordWrap - } - StatusBaseText { - font.pixelSize: 13 - color: Theme.palette.baseColor1 - text: "~15 minutes" - wrapMode: Text.WordWrap - } - } - } - } - } - } - } - } - } - } - - StatusBaseText { - Layout.alignment: Qt.AlignCenter - font.pixelSize: 15 - color: Theme.palette.baseColor1 - text: "Not Implemented" - } - - StatusBaseText { - Layout.alignment: Qt.AlignCenter - font.pixelSize: 15 - color: Theme.palette.baseColor1 - text: "Not Implemented" - } - } -} diff --git a/ui/imports/shared/views/chat/AcceptTransactionView.qml b/ui/imports/shared/views/chat/AcceptTransactionView.qml index bff1af23f1..c682052a9c 100644 --- a/ui/imports/shared/views/chat/AcceptTransactionView.qml +++ b/ui/imports/shared/views/chat/AcceptTransactionView.qml @@ -105,7 +105,7 @@ Item { contactsStore: root.contactsStore msgId: messageId isARequest: true - onOpened: root.store.fetchGasPrice() + chainId: root.store.getChainIdForChat() onClosed: destroy() onOpenGasEstimateErrorPopup: { gasEstimateErrorPopup.confirmationText = message + qsTrId("--the-transaction-will-probably-fail-"); diff --git a/ui/imports/shared/views/chat/TransactionBubbleView.qml b/ui/imports/shared/views/chat/TransactionBubbleView.qml index 43e407f98d..c982fb458a 100644 --- a/ui/imports/shared/views/chat/TransactionBubbleView.qml +++ b/ui/imports/shared/views/chat/TransactionBubbleView.qml @@ -244,7 +244,7 @@ Item { selectedRecipient: root.selectedRecipient selectedFiatAmount: root.fiatValue selectedType: RecipientSelector.Type.Contact - onOpened: root.store.fetchGasPrice() + chainId: root.store.getChainIdForChat() onClosed: destroy() msgId: messageId } diff --git a/ui/imports/utils/Global.qml b/ui/imports/utils/Global.qml index 0a928d374e..3c223a4eba 100644 --- a/ui/imports/utils/Global.qml +++ b/ui/imports/utils/Global.qml @@ -16,8 +16,9 @@ QtObject { property var privacyModuleInst property bool profilePopupOpened: false property string currentNetworkId: "" - property bool networkGuarded: root.currentNetworkId === Constants.networkMainnet || - (root.currentNetworkId === Constants.networkRopsten && localAccountSensitiveSettings.stickersEnsRopsten) + property int currentChainId: 0 + property bool networkGuarded: localAccountSensitiveSettings.isMultiNetworkEnabled || (root.currentNetworkId === Constants.networkMainnet || + (root.currentNetworkId === Constants.networkRopsten && localAccountSensitiveSettings.stickersEnsRopsten)) signal openImagePopup(var image, var contextMenu) signal openLinkInBrowser(string link) diff --git a/ui/imports/utils/Utils.qml b/ui/imports/utils/Utils.qml index 2a5c9c1fdf..f970341f97 100644 --- a/ui/imports/utils/Utils.qml +++ b/ui/imports/utils/Utils.qml @@ -387,17 +387,17 @@ QtObject { return qsTrId("-1d").arg(diffDay) } - function findAssetBySymbol(assets, symbolToFind) { + function findAssetByChainAndSymbol(chainIdToFind, assets, symbolToFind) { for(var i=0; i