diff --git a/src/app/modules/main/wallet_section/activity/controller.nim b/src/app/modules/main/wallet_section/activity/controller.nim index 4433b361dd..07678adb15 100644 --- a/src/app/modules/main/wallet_section/activity/controller.nim +++ b/src/app/modules/main/wallet_section/activity/controller.nim @@ -1,5 +1,5 @@ import NimQml, logging, std/json, sequtils, sugar, options -import tables +import tables, stint import model import entry @@ -15,6 +15,7 @@ import backend/activity as backend_activity import backend/backend as backend import backend/transactions +import app_service/service/currency/service as currency_service import app_service/service/transaction/service as transaction_service proc toRef*[T](obj: T): ref T = @@ -31,6 +32,7 @@ QtObject: recipientsModel: RecipientsModel transactionsModule: transactions_module.AccessInterface currentActivityFilter: backend_activity.ActivityFilter + currencyService: currency_service.Service events: EventEmitter @@ -59,6 +61,20 @@ QtObject: QtProperty[QVariant] recipientsModel: read = getRecipientsModel + proc buildMultiTransactionExtraData(self: Controller, metadata: backend_activity.ActivityEntry, item: MultiTransactionDto): ExtraData = + # TODO: Use symbols from backendEntry when they're available + result.inSymbol = item.toAsset + result.inAmount = self.currencyService.parseCurrencyValue(result.inSymbol, metadata.amountIn) + result.outSymbol = item.fromAsset + result.outAmount = self.currencyService.parseCurrencyValue(result.outSymbol, metadata.amountOut) + + proc buildTransactionExtraData(self: Controller, metadata: backend_activity.ActivityEntry, item: ref Item): ExtraData = + # TODO: Use symbols from backendEntry when they're available + result.inSymbol = item[].getSymbol() + result.inAmount = self.currencyService.parseCurrencyValue(result.inSymbol, metadata.amountIn) + result.outSymbol = item[].getSymbol() + result.outAmount = self.currencyService.parseCurrencyValue(result.outSymbol, metadata.amountOut) + proc backendToPresentation(self: Controller, backendEntities: seq[backend_activity.ActivityEntry]): seq[entry.ActivityEntry] = var multiTransactionsIds: seq[int] = @[] var transactionIdentities: seq[backend.TransactionIdentity] = @[] @@ -116,21 +132,27 @@ QtObject: of MultiTransaction: let id = multiTransactionsIds[mtIndex] if multiTransactions.hasKey(id): - result.add(entry.newMultiTransactionActivityEntry(multiTransactions[id], backendEntry)) + let mt = multiTransactions[id] + let extraData = self.buildMultiTransactionExtraData(backendEntry, mt) + result.add(entry.newMultiTransactionActivityEntry(mt, backendEntry, extraData)) else: error "failed to find multi transaction with id: ", id mtIndex += 1 of SimpleTransaction: let identity = transactionIdentities[tIndex] if transactions.hasKey(identity): - result.add(entry.newTransactionActivityEntry(transactions[identity], backendEntry, self.addresses)) + let tr = transactions[identity] + let extraData = self.buildTransactionExtraData(backendEntry, tr) + result.add(entry.newTransactionActivityEntry(tr, backendEntry, self.addresses, extraData)) else: error "failed to find transaction with identity: ", identity tIndex += 1 of PendingTransaction: let identity = pendingTransactionIdentities[ptIndex] if pendingTransactions.hasKey(identity): - result.add(entry.newTransactionActivityEntry(pendingTransactions[identity], backendEntry, self.addresses)) + let tr = pendingTransactions[identity] + let extraData = self.buildTransactionExtraData(backendEntry, tr) + result.add(entry.newTransactionActivityEntry(tr, backendEntry, self.addresses, extraData)) else: error "failed to find pending transaction with identity: ", identity ptIndex += 1 @@ -192,13 +214,14 @@ QtObject: self.currentActivityFilter.types = types - proc newController*(transactionsModule: transactions_module.AccessInterface, events: EventEmitter): Controller = + proc newController*(transactionsModule: transactions_module.AccessInterface, events: EventEmitter, currencyService: currency_service.Service): Controller = new(result, delete) result.model = newModel() result.recipientsModel = newRecipientsModel() result.transactionsModule = transactionsModule result.currentActivityFilter = backend_activity.getIncludeAllActivityFilter() result.events = events + result.currencyService = currencyService result.setup() let controller = result diff --git a/src/app/modules/main/wallet_section/activity/entry.nim b/src/app/modules/main/wallet_section/activity/entry.nim index 6e80c2ee2f..06662dd10d 100644 --- a/src/app/modules/main/wallet_section/activity/entry.nim +++ b/src/app/modules/main/wallet_section/activity/entry.nim @@ -4,6 +4,18 @@ import ../transactions/view import ../transactions/item import ./backend/transactions import backend/activity as backend +import ../../../shared_models/currency_amount + +# Additional data needed to build an Entry, which is +# not included in the metadata and needs to be +# fetched from a different source. +type + ExtraData* = object + inAmount*: float64 + outAmount*: float64 + # TODO: Fields below should come from the metadata + inSymbol*: string + outSymbol*: string # It is used to display an activity history entry in the QML UI # @@ -23,6 +35,7 @@ QtObject: activityType: backend.ActivityType metadata: backend.ActivityEntry + extradata: ExtraData proc setup(self: ActivityEntry) = self.QObject.setup @@ -30,20 +43,22 @@ QtObject: proc delete*(self: ActivityEntry) = self.QObject.delete - proc newMultiTransactionActivityEntry*(mt: MultiTransactionDto, metadata: backend.ActivityEntry): ActivityEntry = + proc newMultiTransactionActivityEntry*(mt: MultiTransactionDto, metadata: backend.ActivityEntry, extradata: ExtraData): ActivityEntry = new(result, delete) result.multi_transaction = mt result.transaction = nil result.isPending = false result.metadata = metadata + result.extradata = extradata result.setup() - proc newTransactionActivityEntry*(tr: ref Item, metadata: backend.ActivityEntry, fromAddresses: seq[string]): ActivityEntry = + proc newTransactionActivityEntry*(tr: ref Item, metadata: backend.ActivityEntry, fromAddresses: seq[string], extradata: ExtraData): ActivityEntry = new(result, delete) result.multi_transaction = nil result.transaction = tr result.isPending = metadata.payloadType == backend.PayloadType.PendingTransaction result.metadata = metadata + result.extradata = extradata result.activityType = backend.ActivityType.Send if tr != nil: for address in fromAddresses: @@ -104,38 +119,30 @@ QtObject: QtProperty[string] recipient: read = getRecipient - # TODO: use CurrencyAmount? - proc getFromAmount*(self: ActivityEntry): string {.slot.} = - if self.isMultiTransaction(): - return self.multi_transaction.fromAmount - error "getFromAmount: ActivityEntry is not a MultiTransaction" - return "0" + proc getInAmount*(self: ActivityEntry): float {.slot.} = + return float(self.extradata.inAmount) - QtProperty[string] fromAmount: - read = getFromAmount + QtProperty[float] inAmount: + read = getInAmount - proc getToAmount*(self: ActivityEntry): string {.slot.} = - if not self.isMultiTransaction(): - error "getToAmount: ActivityEntry is not a MultiTransaction" - return "0" + proc getOutAmount*(self: ActivityEntry): float {.slot.} = + return float(self.extradata.outAmount) - return self.multi_transaction.fromAmount + QtProperty[float] outAmount: + read = getOutAmount - QtProperty[string] toAmount: - read = getToAmount - proc getValue*(self: ActivityEntry): QVariant {.slot.} = - if self.isMultiTransaction(): - return newQVariant(0) + proc getInSymbol*(self: ActivityEntry): string {.slot.} = + return self.extradata.inSymbol - if self.transaction == nil: - error "getValue: ActivityEntry is not an transaction.Item" - return newQVariant(0) + QtProperty[string] inSymbol: + read = getInSymbol - return newQVariant(self.transaction[].getValue()) + proc getOutSymbol*(self: ActivityEntry): string {.slot.} = + return self.extradata.outSymbol - QtProperty[QVariant] value: - read = getValue + QtProperty[string] outSymbol: + read = getOutSymbol proc getTimestamp*(self: ActivityEntry): int {.slot.} = if self.isMultiTransaction(): @@ -161,15 +168,6 @@ QtObject: QtProperty[int] chainId: read = getChainId - proc getSymbol*(self: ActivityEntry): string {.slot.} = - if self.transaction == nil: - error "getSymbol: ActivityEntry is not an transaction.Item" - return "" - return self.transaction[].getSymbol() - - QtProperty[string] symbol: - read = getSymbol - proc getIsNFT*(self: ActivityEntry): bool {.slot.} = if self.transaction == nil: error "getIsNFT: ActivityEntry is not an transaction.Item" @@ -265,3 +263,52 @@ QtObject: QtProperty[string] nonce: read = getNonce + +# TODO: Replaced usage of these for in/out versions in the QML modules + proc getSymbol*(self: ActivityEntry): string {.slot.} = + if self.transaction == nil: + error "getSymbol: ActivityEntry is not an transaction.Item" + return "" + + if self.activityType == backend.ActivityType.Receive: + return self.getInSymbol() + + return self.getOutSymbol() + + QtProperty[string] symbol: + read = getSymbol + + proc getFromAmount*(self: ActivityEntry): float {.slot.} = + if self.isMultiTransaction(): + return self.getOutAmount() + error "getFromAmount: ActivityEntry is not a MultiTransaction" + return 0.0 + + QtProperty[float] fromAmount: + read = getFromAmount + + proc getToAmount*(self: ActivityEntry): float {.slot.} = + if self.isMultiTransaction(): + return self.getInAmount() + error "getToAmount: ActivityEntry is not a MultiTransaction" + return 0.0 + + QtProperty[float] toAmount: + read = getToAmount + + proc getValue*(self: ActivityEntry): float {.slot.} = + if self.isMultiTransaction(): + error "getToAmount: ActivityEntry is a MultiTransaction" + return 0.0 + + if self.activityType == backend.ActivityType.Receive: + return self.getInAmount() + + # For some reason status-go is categorizing every activity as Receive, + # inverting the In/Out fields for Send operations. Revert this when + # that gets fixed. + #return self.getOutAmount() + return self.getInAmount() + + QtProperty[float] value: + read = getValue \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/module.nim b/src/app/modules/main/wallet_section/module.nim index 64e1e20a7e..b8c484a202 100644 --- a/src/app/modules/main/wallet_section/module.nim +++ b/src/app/modules/main/wallet_section/module.nim @@ -101,7 +101,7 @@ proc newModule*( result.overviewModule = overview_module.newModule(result, events, walletAccountService, currencyService) result.networksModule = networks_module.newModule(result, events, networkService, walletAccountService, settingsService) result.networksService = networkService - result.activityController = activityc.newController(result.transactionsModule, events) + result.activityController = activityc.newController(result.transactionsModule, events, currencyService) result.filter = initFilter(result.controller, result.activityController) result.view = newView(result, result.activityController) diff --git a/src/app_service/service/currency/service.nim b/src/app_service/service/currency/service.nim index 784c93ffe4..93ee4b1d68 100644 --- a/src/app_service/service/currency/service.nim +++ b/src/app_service/service/currency/service.nim @@ -1,4 +1,4 @@ -import NimQml, chronicles, strutils, tables, json +import NimQml, chronicles, strutils, tables, json, stint import ../../../backend/backend as backend @@ -99,4 +99,23 @@ QtObject: proc getCurrencyFormat*(self: Service, symbol: string): CurrencyFormatDto = if not self.currencyFormatCache.hasKey(symbol): return newCurrencyFormatDto(symbol) - return self.currencyFormatCache[symbol] \ No newline at end of file + return self.currencyFormatCache[symbol] + + proc toFloat(amountInt: UInt256): float64 = + return float64(amountInt.truncate(uint64)) + + proc u256ToFloat(decimals: int, amountInt: UInt256): float64 = + if decimals == 0: + return amountInt.toFloat() + + # Convert to float at the end to avoid losing precision + let base = 10.to(UInt256) + let p = base.pow(decimals) + let i = amountInt.div(p) + let r = amountInt.mod(p) + + return i.toFloat() + r.toFloat() / p.toFloat() + + proc parseCurrencyValue*(self: Service, symbol: string, amountInt: UInt256): float64 = + let decimals = self.tokenService.getTokenDecimals(symbol) + return u256ToFloat(decimals, amountInt) diff --git a/src/backend/activity.nim b/src/backend/activity.nim index e80f367f6d..5d3f6c0e5e 100644 --- a/src/backend/activity.nim +++ b/src/backend/activity.nim @@ -1,6 +1,7 @@ import times, strformat, options import json, json_serialization import core, response_type +import stint from gen import rpc import backend import transactions @@ -148,6 +149,8 @@ type activityType*: MultiTransactionType activityStatus*: ActivityStatus tokenType*: TokenType + amountOut*: UInt256 + amountIn*: UInt256 # Mirrors services/wallet/activity/service.go ErrorCode ErrorCode* = enum @@ -174,7 +177,9 @@ proc fromJson*(e: JsonNode, T: typedesc[ActivityEntry]): ActivityEntry {.inline. else: none(TransactionIdentity), id: e["id"].getInt(), activityStatus: fromJson(e["activityStatus"], ActivityStatus), - timestamp: e["timestamp"].getInt() + timestamp: e["timestamp"].getInt(), + amountOut: stint.fromHex(UInt256, e["amountOut"].getStr()), + amountIn: stint.fromHex(UInt256, e["amountIn"].getStr()) ) proc `$`*(self: ActivityEntry): string = @@ -188,6 +193,8 @@ proc `$`*(self: ActivityEntry): string = activityType* {$self.activityType}, activityStatus* {$self.activityStatus}, tokenType* {$self.tokenType}, + amountOut* {$self.amountOut}, + amountIn* {$self.amountIn}, )""" proc fromJson*(e: JsonNode, T: typedesc[FilterResponse]): FilterResponse {.inline.} = diff --git a/storybook/pages/TransactionDetailViewPage.qml b/storybook/pages/TransactionDetailViewPage.qml index 3f8e8897a5..f334491b05 100644 --- a/storybook/pages/TransactionDetailViewPage.qml +++ b/storybook/pages/TransactionDetailViewPage.qml @@ -130,7 +130,7 @@ SplitView { } readonly property var totalFees: QtObject { - property real amount: (transactionData.value.amount / 15) * Math.pow(10, 9) + property real amount: (transactionData.value / 15) * Math.pow(10, 9) property string symbol: "Gwei" property int displayDecimals: 8 property bool stripTrailingZeroes: true diff --git a/ui/app/AppLayouts/Wallet/views/TransactionDetailView.qml b/ui/app/AppLayouts/Wallet/views/TransactionDetailView.qml index 3f0a681dd0..dda8b14b8c 100644 --- a/ui/app/AppLayouts/Wallet/views/TransactionDetailView.qml +++ b/ui/app/AppLayouts/Wallet/views/TransactionDetailView.qml @@ -52,10 +52,10 @@ Item { readonly property string swapSymbol: "" // TODO fill when swap data is implemented readonly property string symbol: root.isTransactionValid ? transaction.symbol : "" readonly property var multichainNetworks: [] // TODO fill icon for networks for multichain - readonly property double cryptoValue: root.isTransactionValid && transaction.value ? transaction.value.amount: 0.0 + readonly property double cryptoValue: root.isTransactionValid ? transaction.value : 0.0 readonly property double fiatValue: root.isTransactionValid ? RootStore.getFiatValue(cryptoValue, symbol, RootStore.currentCurrency): 0.0 readonly property string fiatValueFormatted: root.isTransactionValid ? RootStore.formatCurrencyAmount(d.fiatValue, RootStore.currentCurrency) : "" - readonly property string cryptoValueFormatted: root.isTransactionValid && transaction.value ? LocaleUtils.currencyAmountToLocaleString(transaction.value) : "" + readonly property string cryptoValueFormatted: root.isTransactionValid ? RootStore.formatCurrencyAmount(d.cryptoValue, symbol) : "" readonly property real feeEthValue: root.isTransactionValid && transaction.totalFees ? RootStore.getGasEthValue(transaction.totalFees.amount, 1) : 0 readonly property real feeFiatValue: root.isTransactionValid ? RootStore.getFiatValue(d.feeEthValue, "ETH", RootStore.currentCurrency) : 0 readonly property int transactionType: root.isTransactionValid ? transaction.txType : Constants.TransactionType.Send @@ -278,7 +278,7 @@ Item { TransactionContractTile { // Used to display contract address for any network address: root.isTransactionValid ? transaction.contract : "" - symbol: root.isTransactionValid && transaction.value ? transaction.value.symbol.toUpperCase() : "" + symbol: root.isTransactionValid ? d.symbol : "" networkName: d.networkFullName shortNetworkName: d.networkShortName } @@ -311,7 +311,7 @@ Item { case Constants.TransactionType.Swap: return d.swapSymbol case Constants.TransactionType.Bridge: - return transaction.value.symbol.toUpperCase() + return d.symbol default: return "" } diff --git a/ui/imports/shared/views/ActivityView.qml b/ui/imports/shared/views/ActivityView.qml index 3a6cd76e8f..ea7c5fd6a3 100644 --- a/ui/imports/shared/views/ActivityView.qml +++ b/ui/imports/shared/views/ActivityView.qml @@ -344,13 +344,14 @@ Control { spacing: 5 RowLayout { - Label { text: entry.isMultiTransaction ? entry.fromAmount : entry.amount } + Label { text: qsTr("in"); Layout.leftMargin: 5; Layout.rightMargin: 5 } + Label { text: entry.inAmount } + Label { text: qsTr("out"); Layout.leftMargin: 5; Layout.rightMargin: 5 } + Label { text: entry.outAmount } Label { text: qsTr("from"); Layout.leftMargin: 5; Layout.rightMargin: 5 } Label { text: entry.sender; Layout.maximumWidth: 200; elide: Text.ElideMiddle } Label { text: qsTr("to"); Layout.leftMargin: 5; Layout.rightMargin: 5 } Label { text: entry.recipient; Layout.maximumWidth: 200; elide: Text.ElideMiddle } - Label { text: qsTr("got"); Layout.leftMargin: 5; Layout.rightMargin: 5; visible: entry.isMultiTransaction } - Label { text: entry.toAmount; Layout.leftMargin: 5; Layout.rightMargin: 5; visible: entry.isMultiTransaction } RowLayout {} // Spacer } RowLayout { diff --git a/ui/imports/shared/views/HistoryView.qml b/ui/imports/shared/views/HistoryView.qml index f95d32e408..ddb4f6dcc3 100644 --- a/ui/imports/shared/views/HistoryView.qml +++ b/ui/imports/shared/views/HistoryView.qml @@ -275,7 +275,7 @@ ColumnLayout { width: ListView.view.width modelData: model.activityEntry currentCurrency: RootStore.currentCurrency - cryptoValue: isModelDataValid && modelData.value ? modelData.value.amount : 0.0 + cryptoValue: isModelDataValid ? modelData.value : 0.0 fiatValue: isModelDataValid ? RootStore.getFiatValue(cryptoValue, symbol, currentCurrency): 0.0 networkIcon: isModelDataValid ? RootStore.getNetworkIcon(modelData.chainId) : "" networkColor: isModelDataValid ? RootStore.getNetworkColor(modelData.chainId) : "" diff --git a/vendor/status-go b/vendor/status-go index 51e3d800bb..60b160997c 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit 51e3d800bb0f334e0d6f6167fd461ebefede458f +Subproject commit 60b160997c7f583c2f1e0ebbe5b995ca117e86f3