From 81c33fe2af58cf8ebff1cf479518e082316a77ad Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Wed, 20 Jan 2021 16:04:23 -0400 Subject: [PATCH] Do initial balance fetch async --- src/app/wallet/core.nim | 5 ++- src/app/wallet/view.nim | 51 +++++++++++++++++------ src/app/wallet/views/account_item.nim | 16 +++++-- src/app/wallet/views/account_list.nim | 13 +++--- src/status/libstatus/wallet.nim | 2 +- src/status/wallet.nim | 28 +++++++++---- src/status/wallet/account.nim | 6 ++- src/status/wallet/balance_manager.nim | 13 +++++- ui/app/AppLayouts/Wallet/LeftTab.qml | 2 +- ui/app/AppLayouts/Wallet/WalletHeader.qml | 44 ++++++++++--------- 10 files changed, 124 insertions(+), 56 deletions(-) diff --git a/src/app/wallet/core.nim b/src/app/wallet/core.nim index 9e1a0c168a..436bf97830 100644 --- a/src/app/wallet/core.nim +++ b/src/app/wallet/core.nim @@ -30,8 +30,8 @@ proc init*(self: WalletController) = var accounts = self.status.wallet.accounts for account in accounts: self.view.addAccountToList(account) - self.view.setTotalFiatBalance(self.status.wallet.getTotalFiatBalance()) + self.view.initBalances() self.view.setDappBrowserAddress() self.status.events.on("accountsUpdated") do(e: Args): @@ -40,6 +40,7 @@ proc init*(self: WalletController) = self.status.events.on("newAccountAdded") do(e: Args): var account = WalletTypes.AccountArgs(e) self.view.accounts.addAccountToList(account.account) + self.view.updateView() self.status.events.on("assetChanged") do(e: Args): self.view.updateView() @@ -54,6 +55,8 @@ proc init*(self: WalletController) = for acc in data.accounts: self.status.wallet.updateAccount(acc) self.status.wallet.checkPendingTransactions(acc, data.blockNumber) + self.view.updateView() + # TODO: show notification of "recent-history-fetching": self.view.setHistoryFetchState(data.accounts, true) diff --git a/src/app/wallet/view.nim b/src/app/wallet/view.nim index a46877a5ba..804c5a6416 100644 --- a/src/app/wallet/view.nim +++ b/src/app/wallet/view.nim @@ -1,4 +1,4 @@ -import NimQml, Tables, strformat, strutils, chronicles, json, std/wrapnils, parseUtils, stint, tables, json_serialization +import NimQml, Tables, strformat, strutils, chronicles, sequtils, json, std/wrapnils, parseUtils, stint, tables, json_serialization import ../../status/[status, wallet, threads] import ../../status/wallet/collectibles as status_collectibles import ../../status/libstatus/accounts/constants @@ -348,7 +348,11 @@ QtObject: self.accountListChanged() self.currentAccountChanged() + proc addCustomToken*(self: WalletView, address: string, name: string, symbol: string, decimals: string) {.slot.} = + self.status.wallet.addCustomToken(symbol, true, address, name, parseInt(decimals), "") + proc updateView*(self: WalletView) = + self.setTotalFiatBalance(self.status.wallet.getTotalFiatBalance()) self.totalFiatBalanceChanged() self.currentAccount.assetList.setNewData(self.currentAccount.account.assetList) self.currentAccountChanged() @@ -356,8 +360,27 @@ QtObject: self.accounts.forceUpdate() self.setCurrentAssetList(self.currentAccount.account.assetList) - proc addCustomToken*(self: WalletView, address: string, name: string, symbol: string, decimals: string) {.slot.} = - self.status.wallet.addCustomToken(symbol, true, address, name, parseInt(decimals), "") + proc initBalances*(self: WalletView) = + for acc in self.status.wallet.accounts: + let accountAddress = acc.address + let tokenList = acc.assetList.filter(proc(x:Asset): bool = x.address != "").map(proc(x: Asset): string = x.address) + spawnAndSend(self, "getAccountBalanceSuccess") do: + var tokenBalances = initTable[string, string]() + for token in tokenList: + tokenBalances[token] = getTokenBalance(token, accountAddress) + $ %* { + "address": accountAddress, + "eth": getEthBalance(accountAddress), + "tokens": tokenBalances + } + + proc getAccountBalanceSuccess*(self: WalletView, jsonResponse: string) {.slot.} = + let jsonObj = jsonResponse.parseJson() + self.status.wallet.update(jsonObj["address"].getStr(), jsonObj["eth"].getStr(), jsonObj["tokens"]) + self.setTotalFiatBalance(self.status.wallet.getTotalFiatBalance()) + self.accounts.forceUpdate() + self.currentAccountChanged() + proc loadCollectiblesForAccount*(self: WalletView, address: string, currentCollectiblesList: seq[CollectibleList]) = if (currentCollectiblesList.len > 0): @@ -523,17 +546,8 @@ QtObject: QtProperty[QVariant] customTokenList: read = getCustomTokenList - proc historyWasFetched*(self: WalletView) {.signal.} - proc setHistoryFetchState*(self: WalletView, accounts: seq[string], isFetching: bool) = - for acc in accounts: - self.fetchingHistoryState[acc] = isFetching - if not isFetching: self.historyWasFetched() - proc isFetchingHistory*(self: WalletView, address: string): bool {.slot.} = - if self.fetchingHistoryState.hasKey(address): - return self.fetchingHistoryState[address] - return true proc isKnownTokenContract*(self: WalletView, address: string): bool {.slot.} = return self.status.wallet.getKnownTokenContract(parseAddress(address)) != nil @@ -548,6 +562,19 @@ QtObject: return """{"error":"Unknown token address"}"""; + proc historyWasFetched*(self: WalletView) {.signal.} + + proc setHistoryFetchState*(self: WalletView, accounts: seq[string], isFetching: bool) = + for acc in accounts: + self.fetchingHistoryState[acc] = isFetching + if not isFetching: self.historyWasFetched() + + proc isFetchingHistory*(self: WalletView, address: string): bool {.slot.} = + if self.fetchingHistoryState.hasKey(address): + return self.fetchingHistoryState[address] + return true + + proc isHistoryFetched*(self: WalletView, address: string): bool {.slot.} = return self.currentTransactions.rowCount() > 0 diff --git a/src/app/wallet/views/account_item.nim b/src/app/wallet/views/account_item.nim index 732a099745..6d7a5b73d6 100644 --- a/src/app/wallet/views/account_item.nim +++ b/src/app/wallet/views/account_item.nim @@ -1,4 +1,4 @@ -import NimQml, std/wrapnils, strformat +import NimQml, std/wrapnils, strformat, options from ../../../status/wallet import WalletAccount import ./asset_list @@ -36,11 +36,21 @@ QtObject: QtProperty[string] iconColor: read = iconColor - proc balance*(self: AccountItemView): string {.slot.} = result = ?.self.account.balance + proc balance*(self: AccountItemView): string {.slot.} = + if ?.self.account.balance.isSome: + result = ?.self.account.balance.get() + else: + result = "" + QtProperty[string] balance: read = balance - proc fiatBalance*(self: AccountItemView): string {.slot.} = result = fmt"{?.self.account.realFiatBalance:>.2f}" + proc fiatBalance*(self: AccountItemView): string {.slot.} = + if ?.self.account.realFiatBalance.isSome: + result = fmt"{?.self.account.realFiatBalance.get():>.2f}" + else: + result = "" + QtProperty[string] fiatBalance: read = fiatBalance diff --git a/src/app/wallet/views/account_list.nim b/src/app/wallet/views/account_list.nim index b6eb51b252..ec5ba6deee 100644 --- a/src/app/wallet/views/account_list.nim +++ b/src/app/wallet/views/account_list.nim @@ -17,6 +17,7 @@ type Assets = UserRole + 6 WalletType = UserRole + 7 Wallet = UserRole + 8 + Loading = UserRole + 9 QtObject: type AccountList* = ref object of QAbstractListModel @@ -45,10 +46,10 @@ QtObject: of "name": result = account.name of "address": result = account.address of "iconColor": result = account.iconColor - of "balance": result = account.balance + of "balance": result = if account.balance.isSome(): account.balance.get() else: "..." of "path": result = account.path of "walletType": result = account.walletType - of "fiatBalance": result = fmt"{account.realFiatBalance:>.2f}" + of "fiatBalance": result = if account.realFiatBalance.isSome(): fmt"{account.realFiatBalance.get():>.2f}" else: "..." proc getAccountindexByAddress*(self: AccountList, address: string): int = var i = 0 @@ -91,11 +92,12 @@ QtObject: of AccountRoles.Name: result = newQVariant(account.name) of AccountRoles.Address: result = newQVariant(account.address) of AccountRoles.Color: result = newQVariant(account.iconColor) - of AccountRoles.Balance: result = newQVariant(account.balance) - of AccountRoles.FiatBalance: result = newQVariant(fmt"{account.realFiatBalance:>.2f}") + of AccountRoles.Balance: result = newQVariant(if account.balance.isSome(): account.balance.get() else: "...") + of AccountRoles.FiatBalance: result = newQVariant(if account.realFiatBalance.isSome(): fmt"{account.realFiatBalance.get():>.2f}" else: "...") of AccountRoles.Assets: result = newQVariant(accountView.assets) of AccountRoles.WalletType: result = newQVariant(account.walletType) of AccountRoles.Wallet: result = newQVariant(account.wallet) + of AccountRoles.Loading: result = newQVariant(if account.balance.isSome() and account.realFiatBalance.isSome(): false else: true) method roleNames(self: AccountList): Table[int, string] = { AccountRoles.Name.int:"name", @@ -105,7 +107,8 @@ QtObject: AccountRoles.FiatBalance.int:"fiatBalance", AccountRoles.Assets.int:"assets", AccountRoles.Wallet.int:"isWallet", - AccountRoles.WalletType.int:"walletType" }.toTable + AccountRoles.WalletType.int:"walletType", + AccountRoles.Loading.int:"isLoading" }.toTable proc addAccountToList*(self: AccountList, account: WalletAccount) = if account.iconColor == "": diff --git a/src/status/libstatus/wallet.nim b/src/status/libstatus/wallet.nim index 4672d8938c..e8e4167b6c 100644 --- a/src/status/libstatus/wallet.nim +++ b/src/status/libstatus/wallet.nim @@ -84,7 +84,7 @@ proc validateMnemonic*(mnemonic: string): string = result = $nim_status.validateMnemonic(mnemonic) proc startWallet*() = - discard nim_status.startWallet() + discard nim_status.startWallet(true) proc hex2Token*(input: string, decimals: int): string = var value = fromHex(Stuint[256], input) diff --git a/src/status/wallet.nim b/src/status/wallet.nim index e171f81abf..9f8ad40c5c 100644 --- a/src/status/wallet.nim +++ b/src/status/wallet.nim @@ -15,6 +15,7 @@ from libstatus/utils as libstatus_utils import eth2Wei, gwei2Wei, first, toUInt6 import wallet/[balance_manager, account, collectibles] import transactions import ../eventemitter +import options export account, collectibles export Transaction @@ -188,14 +189,23 @@ proc generateAccountConfiguredAssets*(self: WalletModel, accountAddress: string) proc populateAccount*(self: WalletModel, walletAccount: var WalletAccount, balance: string, refreshCache: bool = false) = var assets: seq[Asset] = self.generateAccountConfiguredAssets(walletAccount.address) - walletAccount.balance = fmt"{balance} {self.defaultCurrency}" + walletAccount.balance = none[string]() walletAccount.assetList = assets - walletAccount.realFiatBalance = 0.0 - updateBalance(walletAccount, self.getDefaultCurrency(), refreshCache) + walletAccount.realFiatBalance = none[float]() + +proc update*(self: WalletModel, address: string, ethBalance: string, tokens: JsonNode) = + for account in self.accounts: + if account.address != address: continue + storeBalances(account, ethBalance, tokens) + updateBalance(account, self.getDefaultCurrency(), false) + +proc getEthBalance*(address: string): string = + var balance = getBalance(address) + result = hex2token(balance, 18) proc newAccount*(self: WalletModel, walletType: string, derivationPath: string, name: string, address: string, iconColor: string, balance: string, publicKey: string): WalletAccount = var assets: seq[Asset] = self.generateAccountConfiguredAssets(address) - var account = WalletAccount(name: name, path: derivationPath, walletType: walletType, address: address, iconColor: iconColor, balance: fmt"{balance} {self.defaultCurrency}", assetList: assets, realFiatBalance: 0.0, publicKey: publicKey) + var account = WalletAccount(name: name, path: derivationPath, walletType: walletType, address: address, iconColor: iconColor, balance: none[string](), assetList: assets, realFiatBalance: none[float](), publicKey: publicKey) updateBalance(account, self.getDefaultCurrency()) account @@ -204,19 +214,18 @@ proc initAccounts*(self: WalletModel) = let accounts = status_wallet.getWalletAccounts() for account in accounts: var acc = WalletAccount(account) - - self.populateAccount(acc, "") + self.populateAccount(acc, "") self.accounts.add(acc) proc updateAccount*(self: WalletModel, address: string) = for acc in self.accounts.mitems: if acc.address == address: self.populateAccount(acc, "", true) + updateBalance(acc, self.getDefaultCurrency(), true) self.events.emit("accountsUpdated", Args()) proc getTotalFiatBalance*(self: WalletModel): string = - if self.totalBalance == 0.0: - self.calculateTotalFiatBalance() + self.calculateTotalFiatBalance() fmt"{self.totalBalance:.2f}" proc convertValue*(self: WalletModel, balance: string, fromCurrency: string, toCurrency: string): float = @@ -225,7 +234,8 @@ proc convertValue*(self: WalletModel, balance: string, fromCurrency: string, toC proc calculateTotalFiatBalance*(self: WalletModel) = self.totalBalance = 0.0 for account in self.accounts: - self.totalBalance += account.realFiatBalance + if account.realFiatBalance.isSome: + self.totalBalance += account.realFiatBalance.get() proc addNewGeneratedAccount(self: WalletModel, generatedAccount: GeneratedAccount, password: string, accountName: string, color: string, accountType: string, isADerivedAccount = true, walletIndex: int = 0): string = try: diff --git a/src/status/wallet/account.nim b/src/status/wallet/account.nim index dca570d53d..4a11648ca7 100644 --- a/src/status/wallet/account.nim +++ b/src/status/wallet/account.nim @@ -1,5 +1,6 @@ from ../../eventemitter import Args import ../libstatus/types +import options type CollectibleList* = ref object collectibleType*, collectiblesJSON*, error*: string @@ -15,8 +16,9 @@ type Asset* = ref object name*, symbol*, value*, fiatBalanceDisplay*, fiatBalance*, accountAddress*, address*: string type WalletAccount* = ref object - name*, address*, iconColor*, balance*, path*, walletType*, publicKey*: string - realFiatBalance*: float + name*, address*, iconColor*, path*, walletType*, publicKey*: string + balance*: Option[string] + realFiatBalance*: Option[float] assetList*: seq[Asset] wallet*, chat*: bool collectiblesLists*: seq[CollectibleList] diff --git a/src/status/wallet/balance_manager.nim b/src/status/wallet/balance_manager.nim index 7d930ff741..351a0a19fa 100644 --- a/src/status/wallet/balance_manager.nim +++ b/src/status/wallet/balance_manager.nim @@ -4,6 +4,7 @@ import ../libstatus/tokens as status_tokens import ../libstatus/types as status_types import ../utils/cache import account +import options logScope: topics = "balance-manager" @@ -69,9 +70,17 @@ proc updateBalance*(account: WalletAccount, currency: string, refreshCache: bool let eth_balance = getBalance("ETH", account.address, "", refreshCache) let usd_balance = convertValue(eth_balance, "ETH", currency) var totalAccountBalance = usd_balance - account.realFiatBalance = totalAccountBalance - account.balance = fmt"{totalAccountBalance:.2f} {currency}" + account.realFiatBalance = some(totalAccountBalance) + account.balance = some(fmt"{totalAccountBalance:.2f} {currency}") for asset in account.assetList: updateBalance(asset, currency, refreshCache) except RpcException: error "Error in updateBalance", message = getCurrentExceptionMsg() + +proc storeBalances*(account: WalletAccount, ethBalance = "0", tokenBalance: JsonNode) = + let ethCacheKey = fmt"ETH-{account.address}-" + balanceManager.tokenBalances.cacheValue(ethCacheKey, ethBalance) + for asset in account.assetList: + if tokenBalance.hasKey(asset.address): + let cacheKey = fmt"{asset.symbol}-{account.address}-{asset.address}" + balanceManager.tokenBalances.cacheValue(cacheKey, tokenBalance{asset.address}.getStr()) diff --git a/ui/app/AppLayouts/Wallet/LeftTab.qml b/ui/app/AppLayouts/Wallet/LeftTab.qml index 3c747bfc50..b628249574 100644 --- a/ui/app/AppLayouts/Wallet/LeftTab.qml +++ b/ui/app/AppLayouts/Wallet/LeftTab.qml @@ -134,7 +134,7 @@ Item { } StyledText { id: walletBalance - text: Utils.toLocaleString(fiatBalance, appSettings.locale) + " " + walletModel.defaultCurrency.toUpperCase() + text: isLoading ? "..." : Utils.toLocaleString(fiatBalance, appSettings.locale) + " " + walletModel.defaultCurrency.toUpperCase() anchors.top: parent.top anchors.topMargin: Style.current.smallPadding anchors.right: parent.right diff --git a/ui/app/AppLayouts/Wallet/WalletHeader.qml b/ui/app/AppLayouts/Wallet/WalletHeader.qml index 144c209110..7ebeb7c5c3 100644 --- a/ui/app/AppLayouts/Wallet/WalletHeader.qml +++ b/ui/app/AppLayouts/Wallet/WalletHeader.qml @@ -63,25 +63,33 @@ Item { anchors.topMargin: 0 } - ReceiveModal{ - id: receiveModal - selectedAccount: currentAccount + Component { + id: receiveModalComponent + ReceiveModal{ + selectedAccount: currentAccount + } } - SetCurrencyModal{ - id: setCurrencyModal + Component { + id: setCurrencyModalComponent + SetCurrencyModal{ + } } - TokenSettingsModal{ - id: tokenSettingsModal + Component { + id: tokenSettingsModalComponent + TokenSettingsModal{ + } } - AccountSettingsModal { - id: accountSettingsModal - changeSelectedAccount: walletHeader.changeSelectedAccount + Component { + id: accountSettingsModalComponent + AccountSettingsModal{ + changeSelectedAccount: walletHeader.changeSelectedAccount + } } - AddCustomTokenModal { + AddCustomTokenModal{ id: addCustomTokenModal } @@ -101,7 +109,7 @@ Item { imageSource: "../../img/send.svg" //% "Send" text: qsTrId("command-button-send") - onClicked: function () { + onClicked: function() { sendModal.open() } } @@ -113,7 +121,7 @@ Item { //% "Receive" text: qsTrId("receive") onClicked: function () { - receiveModal.open() + openPopup(receiveModalComponent); } anchors.left: sendBtn.right anchors.leftMargin: walletMenu.btnOuterMargin @@ -141,9 +149,7 @@ Item { icon.source: "../../img/manage-wallet.svg" icon.width: 16 icon.height: 16 - onTriggered: { - accountSettingsModal.open() - } + onTriggered: openPopup(accountSettingsModalComponent) } Action { text: qsTr("Manage Assets") @@ -151,7 +157,7 @@ Item { icon.width: 16 icon.height: 16 onTriggered: { - tokenSettingsModal.open() + openPopup(tokenSettingsModalComponent) walletModel.loadCustomTokens() } } @@ -161,9 +167,7 @@ Item { icon.source: "../../img/currency.svg" icon.width: 16 icon.height: 16 - onTriggered: { - setCurrencyModal.open() - } + onTriggered: openPopup(setCurrencyModalComponent) } } }