chore(wallet) remove transactions module

Bump Status GO to support session based activity requests

Transaction module was replaced by activity module,
this change removes the old code.

Details:

- Remove transaction.Item and use the DTO directly
- Replace the still using missing functionality
- Remove old way of fetching transactions in response to the history event
- Use activity filter to provide history for "recents" in Send.

Closes #11339
This commit is contained in:
Dario Gabriel Lipicar 2023-06-29 14:35:18 -03:00 committed by Stefan Dunca
parent 515028222e
commit bf4af3d7d4
36 changed files with 306 additions and 1516 deletions

View File

@ -1,4 +1,4 @@
import json
import json, options
import base
import signal_type
@ -13,19 +13,24 @@ type WalletSignal* = ref object of Signal
at*: int
chainID*: int
message*: string
requestId*: Option[int]
proc fromEvent*(T: type WalletSignal, jsonSignal: JsonNode): WalletSignal =
result = WalletSignal()
result.signalType = SignalType.Wallet
result.content = $jsonSignal
if jsonSignal["event"].kind != JNull:
result.eventType = jsonSignal["event"]["type"].getStr
result.blockNumber = jsonSignal["event"]{"blockNumber"}.getInt
result.erc20 = jsonSignal["event"]{"erc20"}.getBool
let event = jsonSignal["event"]
if event.kind != JNull:
result.eventType = event["type"].getStr
result.blockNumber = event{"blockNumber"}.getInt
result.erc20 = event{"erc20"}.getBool
result.accounts = @[]
if jsonSignal["event"]["accounts"].kind != JNull:
for account in jsonSignal["event"]["accounts"]:
if event["accounts"].kind != JNull:
for account in event["accounts"]:
result.accounts.add(account.getStr)
result.at = jsonSignal["event"]{"at"}.getInt
result.chainID = jsonSignal["event"]{"chainID"}.getInt
result.message = jsonSignal["event"]{"message"}.getStr
result.at = event{"at"}.getInt
result.chainID = event{"chainID"}.getInt
result.message = event{"message"}.getStr
const requestIdName = "requestId"
if event.contains(requestIdName):
result.requestId = some(event[requestIdName].getInt())

View File

@ -10,9 +10,7 @@ import status
import web3/conversions
import app/core/eventemitter
import ../transactions/item
import ../transactions/module as transactions_module
import app/core/signals/types
import backend/activity as backend_activity
import backend/backend as backend
@ -22,6 +20,11 @@ import app_service/service/currency/service as currency_service
import app_service/service/transaction/service as transaction_service
import app_service/service/token/service as token_service
import app/modules/shared/wallet_utils
import app/modules/shared_models/currency_amount
import app_service/service/transaction/dto
proc toRef*[T](obj: T): ref T =
new(result)
result[] = obj
@ -35,7 +38,6 @@ QtObject:
Controller* = ref object of QObject
model: Model
recipientsModel: RecipientsModel
transactionsModule: transactions_module.AccessInterface
currentActivityFilter: backend_activity.ActivityFilter
currencyService: currency_service.Service
tokenService: token_service.Service
@ -50,6 +52,8 @@ QtObject:
# call updateAssetsIdentities after updating chainIds
chainIds: seq[int]
requestId: int32
proc setup(self: Controller) =
self.QObject.setup
@ -75,20 +79,30 @@ QtObject:
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 =
proc buildTransactionExtraData(self: Controller, metadata: backend_activity.ActivityEntry, item: ref TransactionDto): ExtraData =
# TODO: Use symbols from backendEntry when they're available
result.inSymbol = item[].getSymbol()
result.inSymbol = item[].symbol
result.inAmount = self.currencyService.parseCurrencyValue(result.inSymbol, metadata.amountIn)
result.outSymbol = item[].getSymbol()
result.outSymbol = item[].symbol
result.outAmount = self.currencyService.parseCurrencyValue(result.outSymbol, metadata.amountOut)
proc getResolvedSymbol(self: Controller, transaction: TransactionDto): string =
if transaction.symbol != "":
result = transaction.symbol
else:
let contractSymbol = self.tokenService.findTokenSymbolByAddress(transaction.contract)
if contractSymbol != "":
result = contractSymbol
else:
result = "ETH"
proc backendToPresentation(self: Controller, backendEntities: seq[backend_activity.ActivityEntry]): seq[entry.ActivityEntry] =
var multiTransactionsIds: seq[int] = @[]
var transactionIdentities: seq[backend.TransactionIdentity] = @[]
var pendingTransactionIdentities: seq[backend.TransactionIdentity] = @[]
# Extract metadata required to fetch details
# TODO: temporary here to show the working API. Details for each entry will be done as required
# TODO: see #11598. Temporary here to show the working API. Details for each entry will be done as required
# on a detail request from UI after metadata is extended to include the required info
for backendEntry in backendEntities:
case backendEntry.payloadType:
@ -105,7 +119,7 @@ QtObject:
for mt in mts:
multiTransactions[mt.id] = mt
var transactions = initTable[TransactionIdentity, ref Item]()
var transactions = initTable[TransactionIdentity, ref TransactionDto]()
if len(transactionIdentities) > 0:
let response = backend.getTransfersForIdentities(transactionIdentities)
let res = response.result
@ -113,11 +127,10 @@ QtObject:
error "failed fetching transaction details; err: ", response.error, ", kind: ", res.kind, ", res.len: ", res.len
let transactionsDtos = res.getElems().map(x => x.toTransactionDto())
let trItems = self.transactionsModule.transactionsToItems(transactionsDtos, @[])
for item in trItems:
transactions[TransactionIdentity(chainId: item.getChainId(), hash: item.getId(), address: item.getAddress())] = toRef(item)
for dto in transactionsDtos:
transactions[TransactionIdentity(chainId: dto.chainId, hash: dto.id, address: dto.address)] = toRef(dto)
var pendingTransactions = initTable[TransactionIdentity, ref Item]()
var pendingTransactions = initTable[TransactionIdentity, ref TransactionDto]()
if len(pendingTransactionIdentities) > 0:
let response = backend.getPendingTransactionsForIdentities(pendingTransactionIdentities)
let res = response.result
@ -125,12 +138,16 @@ QtObject:
error "failed fetching pending transactions details; err: ", response.error, ", kind: ", res.kind, ", res.len: ", res.len
let pendingTransactionsDtos = res.getElems().map(x => x.toPendingTransactionDto())
let trItems = self.transactionsModule.transactionsToItems(pendingTransactionsDtos, @[])
for item in trItems:
pendingTransactions[TransactionIdentity(chainId: item.getChainId(), hash: item.getId(), address: item.getAddress())] = toRef(item)
for dto in pendingTransactionsDtos:
pendingTransactions[TransactionIdentity(chainId: dto.chainId, hash: dto.id, address: dto.address)] = toRef(dto)
# Merge detailed transaction info in order
result = newSeqOfCap[entry.ActivityEntry](multiTransactions.len + transactions.len + pendingTransactions.len)
let amountToCurrencyConvertor = proc(amount: UInt256, symbol: string): CurrencyAmount =
return currencyAmountToItem(self.currencyService.parseCurrencyValue(symbol, amount),
self.currencyService.getCurrencyFormat(symbol))
var mtIndex = 0
var tIndex = 0
var ptIndex = 0
@ -141,7 +158,7 @@ QtObject:
if multiTransactions.hasKey(id):
let mt = multiTransactions[id]
let extraData = self.buildMultiTransactionExtraData(backendEntry, mt)
result.add(entry.newMultiTransactionActivityEntry(mt, backendEntry, extraData))
result.add(entry.newMultiTransactionActivityEntry(mt, backendEntry, extraData, amountToCurrencyConvertor))
else:
error "failed to find multi transaction with id: ", id
mtIndex += 1
@ -149,8 +166,9 @@ QtObject:
let identity = transactionIdentities[tIndex]
if transactions.hasKey(identity):
let tr = transactions[identity]
tr.symbol = self.getResolvedSymbol(tr[])
let extraData = self.buildTransactionExtraData(backendEntry, tr)
result.add(entry.newTransactionActivityEntry(tr, backendEntry, self.addresses, extraData))
result.add(entry.newTransactionActivityEntry(tr, backendEntry, self.addresses, extraData, amountToCurrencyConvertor))
else:
error "failed to find transaction with identity: ", identity
tIndex += 1
@ -158,8 +176,9 @@ QtObject:
let identity = pendingTransactionIdentities[ptIndex]
if pendingTransactions.hasKey(identity):
let tr = pendingTransactions[identity]
tr.symbol = self.getResolvedSymbol(tr[])
let extraData = self.buildTransactionExtraData(backendEntry, tr)
result.add(entry.newTransactionActivityEntry(tr, backendEntry, self.addresses, extraData))
result.add(entry.newTransactionActivityEntry(tr, backendEntry, self.addresses, extraData, amountToCurrencyConvertor))
else:
error "failed to find pending transaction with identity: ", identity
ptIndex += 1
@ -189,7 +208,7 @@ QtObject:
self.eventsHandler.updateSubscribedChainIDs(self.chainIds)
self.status.setNewDataAvailable(false)
let response = backend_activity.filterActivityAsync(self.addresses, seq[backend_activity.ChainId](self.chainIds), self.currentActivityFilter, 0, FETCH_BATCH_COUNT_DEFAULT)
let response = backend_activity.filterActivityAsync(self.requestId, self.addresses, seq[backend_activity.ChainId](self.chainIds), self.currentActivityFilter, 0, FETCH_BATCH_COUNT_DEFAULT)
if response.error != nil:
error "error fetching activity entries: ", response.error
self.status.setLoadingData(false)
@ -198,7 +217,7 @@ QtObject:
proc loadMoreItems(self: Controller) {.slot.} =
self.status.setLoadingData(true)
let response = backend_activity.filterActivityAsync(self.addresses, seq[backend_activity.ChainId](self.chainIds), self.currentActivityFilter, self.model.getCount(), FETCH_BATCH_COUNT_DEFAULT)
let response = backend_activity.filterActivityAsync(self.requestId, self.addresses, seq[backend_activity.ChainId](self.chainIds), self.currentActivityFilter, self.model.getCount(), FETCH_BATCH_COUNT_DEFAULT)
if response.error != nil:
self.status.setLoadingData(false)
error "error fetching activity entries: ", response.error
@ -224,7 +243,7 @@ QtObject:
proc updateStartTimestamp*(self: Controller) {.slot.} =
self.status.setLoadingStartTimestamp(true)
let resJson = backend_activity.getOldestActivityTimestampAsync(self.addresses)
let resJson = backend_activity.getOldestActivityTimestampAsync(self.requestId, self.addresses)
if resJson.error != nil:
self.status.setLoadingStartTimestamp(false)
error "error requesting oldest activity timestamp: ", resJson.error
@ -261,19 +280,19 @@ QtObject:
self.status.setNewDataAvailable(true)
)
proc newController*(transactionsModule: transactions_module.AccessInterface,
proc newController*(requestId: int32,
currencyService: currency_service.Service,
tokenService: token_service.Service,
events: EventEmitter): Controller =
new(result, delete)
result.requestId = requestId
result.model = newModel()
result.recipientsModel = newRecipientsModel()
result.transactionsModule = transactionsModule
result.tokenService = tokenService
result.currentActivityFilter = backend_activity.getIncludeAllActivityFilter()
result.eventsHandler = newEventsHandler(events)
result.eventsHandler = newEventsHandler(result.requestId, events)
result.status = newStatus()
result.currencyService = currencyService
@ -345,20 +364,37 @@ QtObject:
proc setFilterAddresses*(self: Controller, addresses: seq[string]) =
self.addresses = addresses
self.status.setIsFilterDirty(true)
self.updateStartTimestamp()
proc setFilterAddressesJson*(self: Controller, jsonArray: string) {.slot.} =
let addressesJson = parseJson(jsonArray)
if addressesJson.kind != JArray:
error "invalid array of json strings"
return
var addresses = newSeq[string]()
for i in 0 ..< addressesJson.len:
if addressesJson[i].kind != JString:
error "not string entry in the json adday for index ", i
return
addresses.add(addressesJson[i].getStr())
self.setFilterAddresses(addresses)
proc setFilterToAddresses*(self: Controller, addresses: seq[string]) =
self.currentActivityFilter.counterpartyAddresses = addresses
proc setFilterChains*(self: Controller, chainIds: seq[int]) =
self.chainIds = chainIds
self.status.setIsFilterDirty(true)
self.updateAssetsIdentities()
proc updateRecipientsModel*(self: Controller) {.slot.} =
self.status.setLoadingRecipients(true)
let res = backend_activity.getRecipientsAsync(0, FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT)
let res = backend_activity.getRecipientsAsync(self.requestId, 0, FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT)
if res.error != nil or res.result.kind != JBool:
self.status.setLoadingRecipients(false)
error "error fetching recipients: ", res.error, "; kind ", res.result.kind
@ -370,7 +406,7 @@ QtObject:
proc loadMoreRecipients(self: Controller) {.slot.} =
self.status.setLoadingRecipients(true)
let res = backend_activity.getRecipientsAsync(self.recipientsModel.getCount(), FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT)
let res = backend_activity.getRecipientsAsync(self.requestId, self.recipientsModel.getCount(), FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT)
if res.error != nil:
self.status.setLoadingRecipients(false)
error "error fetching more recipient entries: ", res.error
@ -393,5 +429,4 @@ QtObject:
if (self.addresses == addresses and self.chainIds == chainIds):
return
self.setFilterAddresses(addresses)
self.setFilterChains(chainIds)
self.status.setIsFilterDirty(true)
self.setFilterChains(chainIds)

View File

@ -1,10 +1,14 @@
import NimQml, json, strformat, sequtils, strutils, logging, stint, options
import NimQml, json, strformat, sequtils, strutils, logging, stint
import ../transactions/view
import ../transactions/item
import ./backend/transactions
import backend/transactions
import backend/activity as backend
import ../../../shared_models/currency_amount
import app/modules/shared_models/currency_amount
import app/global/global_singleton
import app_service/service/transaction/dto
import app_service/service/currency/dto as currency
import app_service/service/currency/service
import web3/ethtypes as eth
@ -15,13 +19,16 @@ type
ExtraData* = object
inAmount*: float64
outAmount*: float64
# TODO: Fields below should come from the metadata
# TODO: Fields below should come from the metadata. see #11597
inSymbol*: string
outSymbol*: string
AmountToCurrencyConvertor* = proc (amount: UInt256, symbol: string): CurrencyAmount
# It is used to display an activity history entry in the QML UI
#
# TODO remove this legacy after the NFT is served async; see #11598
# TODO add all required metadata from filtering; see #11597
#
# Looking into going away from carying the whole detailed data and just keep the required data for the UI
# and request the detailed data on demand
@ -30,36 +37,59 @@ type
QtObject:
type
ActivityEntry* = ref object of QObject
# TODO: these should be removed
# TODO: these should be removed; see #11598
multi_transaction: MultiTransactionDto
transaction: ref Item
transaction: ref TransactionDto
isPending: bool
valueConvertor: AmountToCurrencyConvertor
metadata: backend.ActivityEntry
extradata: ExtraData
totalFees: CurrencyAmount
maxTotalFees: CurrencyAmount
amountCurrency: CurrencyAmount
noAmount: CurrencyAmount
proc setup(self: ActivityEntry) =
self.QObject.setup
proc delete*(self: ActivityEntry) =
self.QObject.delete
proc newMultiTransactionActivityEntry*(mt: MultiTransactionDto, metadata: backend.ActivityEntry, extradata: ExtraData): ActivityEntry =
proc newMultiTransactionActivityEntry*(mt: MultiTransactionDto, metadata: backend.ActivityEntry, extradata: ExtraData, valueConvertor: AmountToCurrencyConvertor): ActivityEntry =
new(result, delete)
result.multi_transaction = mt
result.transaction = nil
result.isPending = false
result.valueConvertor = valueConvertor
result.metadata = metadata
result.extradata = extradata
result.noAmount = newCurrencyAmount()
result.amountCurrency = valueConvertor(
if metadata.activityType == backend.ActivityType.Receive: metadata.amountIn else: metadata.amountOut,
if metadata.activityType == backend.ActivityType.Receive: mt.toAsset else: mt.fromAsset,
)
result.setup()
proc newTransactionActivityEntry*(tr: ref Item, metadata: backend.ActivityEntry, fromAddresses: seq[string], extradata: ExtraData): ActivityEntry =
proc newTransactionActivityEntry*(tr: ref TransactionDto, metadata: backend.ActivityEntry, fromAddresses: seq[string], extradata: ExtraData, valueConvertor: AmountToCurrencyConvertor): ActivityEntry =
new(result, delete)
result.multi_transaction = nil
result.transaction = tr
result.isPending = metadata.payloadType == backend.PayloadType.PendingTransaction
result.valueConvertor = valueConvertor
result.metadata = metadata
result.extradata = extradata
result.totalFees = valueConvertor(stint.fromHex(UInt256, tr.totalFees), "Gwei")
result.maxTotalFees = valueConvertor(stint.fromHex(UInt256, tr.maxTotalFees), "Gwei")
result.amountCurrency = valueConvertor(
if metadata.activityType == backend.ActivityType.Receive: metadata.amountIn else: metadata.amountOut,
tr.symbol
)
result.noAmount = newCurrencyAmount()
result.setup()
proc isMultiTransaction*(self: ActivityEntry): bool {.slot.} =
@ -89,11 +119,6 @@ QtObject:
raise newException(Defect, "ActivityEntry is not a MultiTransaction")
return self.multi_transaction
proc getTransaction*(self: ActivityEntry, pending: bool): ref Item =
if self.isMultiTransaction() or self.isPending != pending:
raise newException(Defect, "ActivityEntry is not a " & (if pending: "pending" else: "") & " Transaction")
return self.transaction
proc getSender*(self: ActivityEntry): string {.slot.} =
return if self.metadata.sender.isSome(): "0x" & self.metadata.sender.unsafeGet().toHex() else: ""
@ -153,24 +178,20 @@ QtObject:
proc getIsNFT*(self: ActivityEntry): bool {.slot.} =
return self.metadata.transferType.isSome() and self.metadata.transferType.unsafeGet() == TransferType.Erc721
QtProperty[int] isNFT:
QtProperty[bool] isNFT:
read = getIsNFT
proc getNFTName*(self: ActivityEntry): string {.slot.} =
if self.transaction == nil:
error "getNFTName: ActivityEntry is not an transaction.Item"
return ""
return self.transaction[].getNFTName()
# TODO: complete this async #11597
return ""
# TODO: lazy load this in activity history service. See #11597
QtProperty[string] nftName:
read = getNFTName
proc getNFTImageURL*(self: ActivityEntry): string {.slot.} =
if self.transaction == nil:
error "getNFTImageURL: ActivityEntry is not an transaction.Item"
return ""
return self.transaction[].getNFTImageURL()
# TODO: complete this async #11597
return ""
# TODO: lazy load this in activity history service. See #11597
QtProperty[string] nftImageURL:
@ -178,9 +199,9 @@ QtObject:
proc getTotalFees*(self: ActivityEntry): QVariant {.slot.} =
if self.transaction == nil:
error "getTotalFees: ActivityEntry is not an transaction.Item"
return newQVariant(newCurrencyAmount())
return newQVariant(self.transaction[].getTotalFees())
error "getTotalFees: ActivityEntry is not an transaction entry"
return newQVariant(self.noAmount)
return newQVariant(self.totalFees)
# TODO: lazy load this in activity history service. See #11597
QtProperty[QVariant] totalFees:
@ -188,9 +209,9 @@ QtObject:
proc getMaxTotalFees*(self: ActivityEntry): QVariant {.slot.} =
if self.transaction == nil:
error "getMaxTotalFees: ActivityEntry is not an transaction.Item"
return newQVariant(newCurrencyAmount())
return newQVariant(self.transaction[].getMaxTotalFees())
error "getMaxTotalFees: ActivityEntry is not an transaction entry"
return newQVariant(self.noAmount)
return newQVariant(self.maxTotalFees)
# TODO: used only in details, move it to a entry_details.nim. See #11598
QtProperty[QVariant] maxTotalFees:
@ -198,9 +219,9 @@ QtObject:
proc getInput*(self: ActivityEntry): string {.slot.} =
if self.transaction == nil:
error "getInput: ActivityEntry is not an transaction.Item"
error "getInput: ActivityEntry is not an transactio entry"
return ""
return self.transaction[].getInput()
return self.transaction[].input
# TODO: used only in details, move it to a entry_details.nim. See #11598
QtProperty[string] input:
@ -214,9 +235,9 @@ QtObject:
proc getType*(self: ActivityEntry): string {.slot.} =
if self.transaction == nil:
error "getType: ActivityEntry is not an transaction.Item"
error "getType: ActivityEntry is not an transaction entry"
return ""
return self.transaction[].getType()
return self.transaction[].typeValue
# TODO: used only in details, move it to a entry_details.nim. See #11598
QtProperty[string] type:
@ -230,9 +251,9 @@ QtObject:
proc getTxHash*(self: ActivityEntry): string {.slot.} =
if self.transaction == nil:
error "getTxHash: ActivityEntry is not an transaction.Item"
error "getTxHash: ActivityEntry is not an transaction entry"
return ""
return self.transaction[].getTxHash()
return self.transaction[].txHash
# TODO: used only in details, move it to a entry_details.nim. See #11598
QtProperty[string] txHash:
@ -253,18 +274,18 @@ QtObject:
# TODO: used only in details, move it to a entry_details.nim. See #11598
proc getNonce*(self: ActivityEntry): string {.slot.} =
if self.transaction == nil:
error "getNonce: ActivityEntry is not an transaction.Item"
error "getNonce: ActivityEntry is not an transaction entry"
return ""
return $self.transaction[].getNonce()
return $self.transaction[].nonce
QtProperty[string] nonce:
read = getNonce
proc getBlockNumber*(self: ActivityEntry): string {.slot.} =
if self.transaction == nil:
error "getBlockNumber: ActivityEntry is not an transaction.Item"
error "getBlockNumber: ActivityEntry is not an transaction entry"
return ""
return $self.transaction[].getBlockNumber()
return $self.transaction[].blockNumber
# TODO: used only in details, move it to a entry_details.nim. See #11598
QtProperty[string] blockNumber:
@ -289,3 +310,9 @@ QtObject:
QtProperty[float] amount:
read = getAmount
proc getAmountCurrency*(self: ActivityEntry): QVariant {.slot.} =
return newQVariant(self.amountCurrency)
QtProperty[QVariant] amountCurrency:
read = getAmountCurrency

View File

@ -1,11 +1,7 @@
import NimQml, logging, std/json, sequtils, strutils
import NimQml, logging, std/json, sequtils, strutils, options
import tables, stint, sets
import model
import entry
import recipients_model
import web3/conversions
import app/core/eventemitter
import app/core/signals/types
@ -30,6 +26,8 @@ QtObject:
subscribedChainIDs: HashSet[int]
newDataAvailableFn: proc()
requestId: int
proc setup(self: EventsHandler) =
self.QObject.setup
@ -51,6 +49,9 @@ QtObject:
proc handleApiEvents(self: EventsHandler, e: Args) =
var data = WalletSignal(e)
if data.requestId.isSome and data.requestId.get() != self.requestId:
return
if self.walletEventHandlers.hasKey(data.eventType):
let callback = self.walletEventHandlers[data.eventType]
callback(data)
@ -63,8 +64,6 @@ QtObject:
return
let callback = self.eventHandlers[data.eventType]
callback(responseJson)
else:
discard
proc setupWalletEventHandlers(self: EventsHandler) =
let newDataAvailableCallback = proc (data: WalletSignal) =
@ -100,14 +99,17 @@ QtObject:
self.walletEventHandlers[EventPendingTransactionUpdate] = newDataAvailableCallback
self.walletEventHandlers[EventMTTransactionUpdate] = newDataAvailableCallback
proc newEventsHandler*(events: EventEmitter): EventsHandler =
proc newEventsHandler*(requestId: int, events: EventEmitter): EventsHandler =
new(result, delete)
result.events = events
result.eventHandlers = initTable[string, EventCallbackProc]()
result.subscribedAddresses = initHashSet[string]()
result.subscribedChainIDs = initHashSet[int]()
result.requestId = requestId
result.setup()
result.setupWalletEventHandlers()

View File

@ -2,10 +2,7 @@ import NimQml, Tables, strutils, strformat, sequtils, logging
import ./entry
# TODO - DEV: remove these imports and associated code after all the metadata is returned by the filter API
import app_service/service/transaction/dto
import app/modules/shared_models/currency_amount
import ../transactions/item as transaction
type
ModelRole {.pure.} = enum

View File

@ -1,4 +1,4 @@
import NimQml, Tables, strutils, strformat, sequtils, logging
import NimQml, Tables, strutils, sequtils, logging
type
ModelRole {.pure.} = enum

View File

@ -1,7 +1,5 @@
import NimQml, std/json, sequtils, strutils, times
import tables, stint, sets, atomics
import web3/conversions
import stint, atomics
import app/core/signals/types
@ -119,6 +117,9 @@ QtObject:
proc isFilterDirtyChanged*(self: Status) {.signal.}
proc setIsFilterDirty*(self: Status, value: bool) =
if self.isFilterDirty == value:
return
self.isFilterDirty = value
self.isFilterDirtyChanged()

View File

@ -89,4 +89,13 @@ method destroyAddAccountPopup*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getNetworkLayer*(self: AccessInterface, chainId: int): string {.base.} =
raise newException(ValueError, "No implementation available")
raise newException(ValueError, "No implementation available")
method getChainIdForChat*(self: AccessInterface): int {.base.} =
raise newException(ValueError, "No implementation available")
method getLatestBlockNumber*(self: AccessInterface, chainId: int): string {.base.} =
raise newException(ValueError, "No implementation available")
method fetchDecodedTxData*(self: AccessInterface, txHash: string, data: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -7,7 +7,6 @@ import ../io_interface as delegate_interface
import ./accounts/module as accounts_module
import ./all_tokens/module as all_tokens_module
import ./assets/module as assets_module
import ./transactions/module as transactions_module
import ./saved_addresses/module as saved_addresses_module
import ./buy_sell_crypto/module as buy_sell_crypto_module
import ./networks/module as networks_module
@ -39,6 +38,10 @@ logScope:
export io_interface
type
ActivityID = enum
History
Temporary
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
events: EventEmitter
@ -51,13 +54,13 @@ type
allTokensModule: all_tokens_module.AccessInterface
assetsModule: assets_module.AccessInterface
sendModule: send_module.AccessInterface
transactionsModule: transactions_module.AccessInterface
savedAddressesModule: saved_addresses_module.AccessInterface
buySellCryptoModule: buy_sell_crypto_module.AccessInterface
addAccountModule: add_account_module.AccessInterface
overviewModule: overview_module.AccessInterface
networksModule: networks_module.AccessInterface
networksService: network_service.Service
transactionService: transaction_service.Service
keycardService: keycard_service.Service
accountsService: accounts_service.Service
walletAccountService: wallet_account_service.Service
@ -65,6 +68,8 @@ type
activityController: activityc.Controller
collectiblesController: collectiblesc.Controller
collectibleDetailsController: collectible_detailsc.Controller
# instance to be used in temporary, short-lived, workflows (e.g. send popup)
tmpActivityController: activityc.Controller
proc newModule*(
delegate: delegate_interface.AccessInterface,
@ -93,32 +98,33 @@ proc newModule*(
result.accountsModule = accounts_module.newModule(result, events, walletAccountService, networkService, currencyService)
result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService)
result.assetsModule = assets_module.newModule(result, events, walletAccountService, networkService, tokenService, currencyService)
result.transactionsModule = transactions_module.newModule(result, events, transactionService, walletAccountService, networkService, currencyService)
result.sendModule = send_module.newModule(result, events, walletAccountService, networkService, currencyService, transactionService)
result.savedAddressesModule = saved_addresses_module.newModule(result, events, savedAddressService)
result.buySellCryptoModule = buy_sell_crypto_module.newModule(result, events, transactionService)
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, currencyService, tokenService, events)
result.transactionService = transactionService
result.activityController = activityc.newController(int32(ActivityID.History), currencyService, tokenService, events)
result.tmpActivityController = activityc.newController(int32(ActivityID.Temporary), currencyService, tokenService, events)
result.collectiblesController = collectiblesc.newController(events)
result.collectibleDetailsController = collectible_detailsc.newController(networkService, events)
result.filter = initFilter(result.controller)
result.view = newView(result, result.activityController, result.collectiblesController, result.collectibleDetailsController)
result.view = newView(result, result.activityController, result.tmpActivityController, result.collectiblesController, result.collectibleDetailsController)
method delete*(self: Module) =
self.accountsModule.delete
self.allTokensModule.delete
self.assetsModule.delete
self.transactionsModule.delete
self.savedAddressesModule.delete
self.buySellCryptoModule.delete
self.sendModule.delete
self.controller.delete
self.view.delete
self.activityController.delete
self.tmpActivityController.delete
self.collectiblesController.delete
self.collectibleDetailsController.delete
@ -145,7 +151,6 @@ method notifyFilterChanged(self: Module) =
let includeWatchOnly = self.controller.isIncludeWatchOnlyAccount()
self.overviewModule.filterChanged(self.filter.addresses, self.filter.chainIds, includeWatchOnly, self.filter.allAddresses)
self.assetsModule.filterChanged(self.filter.addresses, self.filter.chainIds)
self.transactionsModule.filterChanged(self.filter.addresses, self.filter.chainIds)
self.accountsModule.filterChanged(self.filter.addresses, self.filter.chainIds)
self.sendModule.filterChanged(self.filter.addresses, self.filter.chainIds)
self.activityController.globalFilterChanged(self.filter.addresses, self.filter.chainIds)
@ -211,13 +216,17 @@ method load*(self: Module) =
self.filter.includeWatchOnlyToggled()
self.notifyFilterChanged()
self.setTotalCurrencyBalance()
self.events.on(SIGNAL_HISTORY_NON_ARCHIVAL_NODE) do (e:Args):
self.view.setIsNonArchivalNode(true)
self.events.on(SIGNAL_TRANSACTION_DECODED) do(e: Args):
let args = TransactionDecodedArgs(e)
self.view.txDecoded(args.txHash, args.dataDecoded)
self.controller.init()
self.view.load()
self.accountsModule.load()
self.allTokensModule.load()
self.assetsModule.load()
self.transactionsModule.load()
self.savedAddressesModule.load()
self.buySellCryptoModule.load()
self.overviewModule.load()
@ -237,9 +246,6 @@ proc checkIfModuleDidLoad(self: Module) =
if(not self.assetsModule.isLoaded()):
return
if(not self.transactionsModule.isLoaded()):
return
if(not self.savedAddressesModule.isLoaded()):
return
@ -326,4 +332,13 @@ method onAddAccountModuleLoaded*(self: Module) =
self.view.emitDisplayAddAccountPopup()
method getNetworkLayer*(self: Module, chainId: int): string =
return self.networksModule.getNetworkLayer(chainId)
return self.networksModule.getNetworkLayer(chainId)
method getChainIdForChat*(self: Module): int =
return self.networksService.getNetworkForChat().chainId
method getLatestBlockNumber*(self: Module, chainId: int): string =
return self.transactionService.getLatestBlockNumber(chainId)
method fetchDecodedTxData*(self: Module, txHash: string, data: string) =
self.transactionService.fetchDecodedTxData(txHash, data)

View File

@ -1,128 +0,0 @@
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 ../../../../../app_service/service/currency/service as currency_service
import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module
import ../../../../core/[main]
import ../../../../core/tasks/[qt, threadpool]
import ./backend/transactions
type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
events: EventEmitter
transactionService: transaction_service.Service
networkService: network_service.Service
walletAccountService: wallet_account_service.Service
currencyService: currency_service.Service
# Forward declaration
proc loadTransactions*(self: Controller, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false)
proc getWalletAccounts*(self: Controller): seq[WalletAccountDto]
proc newController*(
delegate: io_interface.AccessInterface,
events: EventEmitter,
transactionService: transaction_service.Service,
walletAccountService: wallet_account_service.Service,
networkService: network_service.Service,
currencyService: currency_service.Service,
): Controller =
result = Controller()
result.events = events
result.delegate = delegate
result.transactionService = transactionService
result.walletAccountService = walletAccountService
result.networkService = networkService
result.currencyService = currencyService
proc delete*(self: Controller) =
discard
proc init*(self: Controller) =
self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args):
self.delegate.transactionWasSent(TransactionSentArgs(e).result)
self.events.on(SIGNAL_HISTORY_FETCHING) do (e:Args):
let args = HistoryArgs(e)
self.delegate.setHistoryFetchState(args.addresses, isFetching = true)
self.events.on(SIGNAL_HISTORY_READY) do (e:Args):
let args = HistoryArgs(e)
self.delegate.setHistoryFetchState(args.addresses, isFetching = true)
self.events.on(SIGNAL_HISTORY_NON_ARCHIVAL_NODE) do (e:Args):
let accounts = self.getWalletAccounts()
let addresses = accounts.map(account => account.address)
self.delegate.setHistoryFetchState(addresses, isFetching = false)
self.delegate.setIsNonArchivalNode(true)
self.events.on(SIGNAL_HISTORY_ERROR) do (e:Args):
let accounts = self.getWalletAccounts()
let addresses = accounts.map(account => account.address)
self.delegate.setHistoryFetchState(addresses, isFetching = false, hasMore = false)
self.events.on(SIGNAL_TRANSACTIONS_LOADED) do(e:Args):
let args = TransactionsLoadedArgs(e)
self.delegate.setHistoryFetchState(@[args.address], isFetching = false)
self.delegate.setTrxHistoryResult(args.transactions, args.collectibles, args.address, args.wasFetchMore)
self.events.on(SIGNAL_TRANSACTION_LOADING_COMPLETED_FOR_ALL_NETWORKS) do(e:Args):
let args = TransactionsLoadedArgs(e)
self.delegate.setHistoryFetchState(args.address, args.allTxLoaded, isFetching = false)
self.events.on(SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED) do(e:Args):
self.delegate.refreshTransactions()
self.events.on(SIGNAL_CURRENCY_FORMATS_UPDATED) do(e:Args):
# TODO: Rebuild Transaction items
discard
self.events.on(SIGNAL_TRANSACTION_DECODED) do(e: Args):
let args = TransactionDecodedArgs(e)
self.delegate.txDecoded(args.txHash, args.dataDecoded)
proc watchPendingTransactions*(self: Controller): seq[TransactionDto] =
return self.transactionService.watchPendingTransactions()
proc getPendingTransactions*(self: Controller): seq[TransactionDto] =
return self.transactionService.getPendingTransactions()
proc getWalletAccounts*(self: Controller): seq[WalletAccountDto] =
self.walletAccountService.getWalletAccounts()
proc getWalletAccountByAddress*(self: Controller, address: string): WalletAccountDto =
return self.walletAccountService.getAccountByAddress(address)
proc loadTransactions*(self: Controller, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false) =
self.transactionService.loadTransactions(address, toBlock, limit, loadMore)
proc getAllTransactions*(self: Controller, address: string): seq[TransactionDto] =
return self.transactionService.getAllTransactions(address)
proc getChainIdForChat*(self: Controller): int =
return self.networkService.getNetworkForChat().chainId
proc getChainIdForBrowser*(self: Controller): int =
return self.networkService.getNetworkForBrowser().chainId
proc getLatestBlockNumber*(self: Controller, chainId: int): string =
return self.transactionService.getLatestBlockNumber(chainId)
proc getEnabledChainIds*(self: Controller): seq[int] =
return self.networkService.getNetworks().filter(n => n.enabled).map(n => n.chainId)
proc getCurrencyFormat*(self: Controller, symbol: string): CurrencyFormatDto =
return self.currencyService.getCurrencyFormat(symbol)
proc findTokenSymbolByAddress*(self: Controller, address: string): string =
return self.walletAccountService.findTokenSymbolByAddress(address)
proc getMultiTransactions*(self: Controller, transactionIDs: seq[int]): seq[MultiTransactionDto] =
return transaction_service.getMultiTransactions(transactionIDs)
proc fetchDecodedTxData*(self: Controller, txHash: string, data: string) =
self.transactionService.fetchDecodedTxData(txHash, data)

View File

@ -1,75 +0,0 @@
import ../../../../../app_service/service/wallet_account/dto as WalletDto
import ../../../../../app_service/service/transaction/dto
export TransactionDto, CollectibleDto
import ./item
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for any input/interaction with this module.
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method isLoaded*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method filterChanged*(self: AccessInterface, addresses: seq[string], chainIds: seq[int]) {.base.} =
raise newException(ValueError, "No implementation available")
method getWalletAccounts*(self: AccessInterface): seq[WalletAccountDto] {.base.} =
raise newException(ValueError, "No implementation available")
method loadTransactions*(self: AccessInterface, address: string, toBlock: string, limit: int, loadMore: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method setTrxHistoryResult*(self: AccessInterface, transactions: seq[TransactionDto], collectibles: seq[CollectibleDto], address: string, wasFetchMore: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method setHistoryFetchState*(self: AccessInterface, addresses: seq[string], isFetching: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method setHistoryFetchState*(self: AccessInterface, addresses: seq[string], isFetching: bool, hasMore: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method setHistoryFetchState*(self: AccessInterface, address: string, allTxLoaded: bool, isFetching: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method setIsNonArchivalNode*(self: AccessInterface, isNonArchivalNode: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method transactionWasSent*(self: AccessInterface, result: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getChainIdForChat*(self: AccessInterface): int {.base.} =
raise newException(ValueError, "No implementation available")
method getChainIdForBrowser*(self: AccessInterface): int {.base.} =
raise newException(ValueError, "No implementation available")
method refreshTransactions*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method fetchDecodedTxData*(self: AccessInterface, txHash: string, data: string) {.base.} =
raise newException(ValueError, "No implementation available")
method txDecoded*(self: AccessInterface, txHash: string, dataDecoded: string) {.base.} =
raise newException(ValueError, "No implementation available")
# View Delegate Interface
# Delegate for the view must be declared here due to use of QtObject and multi
# inheritance, which is not well supported in Nim.
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getLatestBlockNumber*(self: AccessInterface, chainId: int): string {.base.} =
raise newException(ValueError, "No implementation available")
method transactionsToItems*(self: AccessInterface, transactions: seq[TransactionDto], collectibles: seq[CollectibleDto]): seq[Item] {.base.} =
raise newException(ValueError, "No implementation available")
method getNetworkLayer*(self: AccessInterface, chainId: int): string {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,299 +0,0 @@
import strformat, stint
import ../../../shared_models/currency_amount
type
Item* = object
id: string
txType: string
address: string
blockNumber: string
blockHash: string
timestamp: int
gasPrice: CurrencyAmount
gasLimit: int
gasUsed: int
nonce: string
txStatus: string
fro: string
to: string
contract: string
chainId: int
maxFeePerGas: CurrencyAmount
maxPriorityFeePerGas: CurrencyAmount
input: string
txHash: string
multiTransactionID: int
isTimeStamp: bool
isNFT: bool
baseGasFees: CurrencyAmount
totalFees: CurrencyAmount
maxTotalFees: CurrencyAmount
loadingTransaction: bool
# Applies only to isNFT == false
value: CurrencyAmount
symbol: string
# Applies only to isNFT == true
tokenID: UInt256
nftName: string
nftImageUrl: string
proc initItem*(
id: string,
txType: string,
address: string,
blockNumber: string,
blockHash: string,
timestamp: int,
gasPrice: CurrencyAmount,
gasLimit: int,
gasUsed: int,
nonce: string,
txStatus: string,
value: CurrencyAmount,
fro: string,
to: string,
contract: string,
chainId: int,
maxFeePerGas: CurrencyAmount,
maxPriorityFeePerGas: CurrencyAmount,
input: string,
txHash: string,
multiTransactionID: int,
isTimeStamp: bool,
baseGasFees: CurrencyAmount,
totalFees: CurrencyAmount,
maxTotalFees: CurrencyAmount,
symbol: string,
loadingTransaction: bool = false
): Item =
result.id = id
result.txType = txType
result.address = address
result.blockNumber = blockNumber
result.blockHash = blockHash
result.timestamp = timestamp
result.gasPrice = gasPrice
result.gasLimit = gasLimit
result.gasUsed = gasUsed
result.nonce = nonce
result.txStatus = txStatus
result.value = value
result.fro = fro
result.to = to
result.contract = contract
result.chainId = chainId
result.maxFeePerGas = maxFeePerGas
result.maxPriorityFeePerGas = maxPriorityFeePerGas
result.input = input
result.txHash = txHash
result.multiTransactionID = multiTransactionID
result.isTimeStamp = isTimeStamp
result.isNFT = false
result.baseGasFees = baseGasFees
result.totalFees = totalFees
result.maxTotalFees = maxTotalFees
result.symbol = symbol
result.loadingTransaction = loadingTransaction
proc initNFTItem*(
id: string,
txType: string,
address: string,
blockNumber: string,
blockHash: string,
timestamp: int,
gasPrice: CurrencyAmount,
gasLimit: int,
gasUsed: int,
nonce: string,
txStatus: string,
fro: string,
to: string,
contract: string,
chainId: int,
maxFeePerGas: CurrencyAmount,
maxPriorityFeePerGas: CurrencyAmount,
input: string,
txHash: string,
multiTransactionID: int,
baseGasFees: CurrencyAmount,
totalFees: CurrencyAmount,
maxTotalFees: CurrencyAmount,
tokenID: UInt256,
nftName: string,
nftImageUrl: string,
loadingTransaction: bool = false
): Item =
result.id = id
result.txType = txType
result.address = address
result.blockNumber = blockNumber
result.blockHash = blockHash
result.timestamp = timestamp
result.gasPrice = gasPrice
result.gasLimit = gasLimit
result.gasUsed = gasUsed
result.nonce = nonce
result.txStatus = txStatus
result.value = newCurrencyAmount()
result.fro = fro
result.to = to
result.contract = contract
result.chainId = chainId
result.maxFeePerGas = maxFeePerGas
result.maxPriorityFeePerGas = maxPriorityFeePerGas
result.input = input
result.txHash = txHash
result.multiTransactionID = multiTransactionID
result.isTimeStamp = false
result.isNFT = true
result.baseGasFees = baseGasFees
result.totalFees = totalFees
result.maxTotalFees = maxTotalFees
result.loadingTransaction = loadingTransaction
result.tokenID = tokenID
result.nftName = nftName
result.nftImageUrl = nftImageUrl
proc initLoadingItem*(): Item =
result.timestamp = 0
result.gasPrice = newCurrencyAmount()
result.value = newCurrencyAmount()
result.chainId = 0
result.maxFeePerGas = newCurrencyAmount()
result.maxPriorityFeePerGas = newCurrencyAmount()
result.multiTransactionID = 0
result.isTimeStamp = false
result.baseGasFees = newCurrencyAmount()
result.totalFees = newCurrencyAmount()
result.maxTotalFees = newCurrencyAmount()
result.loadingTransaction = true
proc `$`*(self: Item): string =
result = fmt"""TransactionsItem(
id: {self.id},
txType: {self.txType},
address: {self.address},
blockNumber: {self.blockNumber},
blockHash: {self.blockHash},
timestamp: {self.timestamp},
gasPrice: {self.gasPrice},
gasLimit: {self.gasLimit},
gasUsed: {self.gasUsed},
nonce: {self.nonce},
txStatus: {self.txStatus},
value: {self.value},
fro: {self.fro},
to: {self.to},
contract: {self.contract},
chainId: {self.chainId},
maxFeePerGas: {self.maxFeePerGas},
maxPriorityFeePerGas: {self.maxPriorityFeePerGas},
input: {self.input},
txHash: {self.txHash},
multiTransactionID: {self.multiTransactionID},
isTimeStamp: {self.isTimeStamp},
isNFT: {self.isNFT},
baseGasFees: {self.baseGasFees},
totalFees: {self.totalFees},
maxTotalFees: {self.maxTotalFees},
symbol: {self.symbol},
loadingTransaction: {self.loadingTransaction},
tokenID: {self.tokenID},
nftName: {self.nftName},
nftImageUrl: {self.nftImageUrl},
]"""
proc getId*(self: Item): string =
return self.id
proc getType*(self: Item): string =
return self.txType
proc getAddress*(self: Item): string =
return self.address
proc getBlockNumber*(self: Item): string =
return self.blockNumber
proc getBlockHash*(self: Item): string =
return self.blockHash
proc getTimestamp*(self: Item): int =
return self.timestamp
proc getGasPrice*(self: Item): CurrencyAmount =
return self.gasPrice
proc getGasLimit*(self: Item): int =
return self.gasLimit
proc getGasUsed*(self: Item): int =
return self.gasUsed
proc getNonce*(self: Item): string =
return self.nonce
proc getTxStatus*(self: Item): string =
return self.txStatus
proc getValue*(self: Item): CurrencyAmount =
return self.value
# TODO: fix naming
proc getfrom*(self: Item): string =
return self.fro
proc getTo*(self: Item): string =
return self.to
proc getContract*(self: Item): string =
return self.contract
proc getChainId*(self: Item): int =
return self.chainId
proc getMaxFeePerGas*(self: Item): CurrencyAmount =
return self.maxFeePerGas
proc getMaxPriorityFeePerGas*(self: Item): CurrencyAmount =
return self.maxPriorityFeePerGas
proc getInput*(self: Item): string =
return self.input
proc getTxHash*(self: Item): string =
return self.txHash
proc getMultiTransactionID*(self: Item): int =
return self.multiTransactionID
proc getIsTimeStamp*(self: Item): bool =
return self.isTimeStamp
proc getIsNFT*(self: Item): bool =
return self.isNFT
proc getBaseGasFees*(self: Item): CurrencyAmount =
return self.baseGasFees
proc getTotalFees*(self: Item): CurrencyAmount =
return self.totalFees
proc getMaxTotalFees*(self: Item): CurrencyAmount =
return self.maxTotalFees
proc getSymbol*(self: Item): string =
return self.symbol
proc getLoadingTransaction*(self: Item): bool =
return self.loadingTransaction
proc getTokenID*(self: Item): UInt256 =
return self.tokenID
proc getNFTName*(self: Item): string =
return self.nftName
proc getNFTImageURL*(self: Item): string =
return self.nftImageUrl

View File

@ -1,270 +0,0 @@
import NimQml, Tables, strutils, strformat, sequtils, tables, sugar, algorithm, std/[times, os], stint, parseutils
import ./item
import ../../../../../app_service/service/eth/utils as eth_service_utils
type
ModelRole {.pure.} = enum
Id = UserRole + 1,
Type
Address
BlockNumber
BlockHash
Timestamp
GasPrice
GasLimit
GasUsed
Nonce
TxStatus
From
To
Contract
ChainID
MaxFeePerGas
MaxPriorityFeePerGas
Input
TxHash
MultiTransactionID
IsTimeStamp
IsNFT
BaseGasFees
TotalFees
MaxTotalFees
LoadingTransaction
# Applies only to IsNFT == false
Value
Symbol
# Applies only to IsNFT == true
TokenID
NFTName
NFTImageURL
QtObject:
type
Model* = ref object of QAbstractListModel
items: seq[Item]
hasMore: bool
proc delete(self: Model) =
self.items = @[]
self.QAbstractListModel.delete
proc setup(self: Model) =
self.QAbstractListModel.setup
proc newModel*(): Model =
new(result, delete)
result.setup
result.hasMore = true
proc `$`*(self: Model): string =
for i in 0 ..< self.items.len:
result &= fmt"""[{i}]:({$self.items[i]})"""
proc countChanged(self: Model) {.signal.}
proc getCount*(self: Model): int {.slot.} =
self.items.len
QtProperty[int] count:
read = getCount
notify = countChanged
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: Model): Table[int, string] =
{
ModelRole.Id.int:"id",
ModelRole.Type.int:"type",
ModelRole.Address.int:"address",
ModelRole.BlockNumber.int:"blockNumber",
ModelRole.BlockHash.int:"blockHash",
ModelRole.Timestamp.int:"timestamp",
ModelRole.GasPrice.int:"gasPrice",
ModelRole.GasLimit.int:"gasLimit",
ModelRole.GasUsed.int:"gasUsed",
ModelRole.Nonce.int:"nonce",
ModelRole.TxStatus.int:"txStatus",
ModelRole.Value.int:"value",
ModelRole.From.int:"from",
ModelRole.To.int:"to",
ModelRole.Contract.int:"contract",
ModelRole.ChainID.int:"chainId",
ModelRole.MaxFeePerGas.int:"maxFeePerGas",
ModelRole.MaxPriorityFeePerGas.int:"maxPriorityFeePerGas",
ModelRole.Input.int:"input",
ModelRole.TxHash.int:"txHash",
ModelRole.MultiTransactionID.int:"multiTransactionID",
ModelRole.IsTimeStamp.int: "isTimeStamp",
ModelRole.IsNFT.int: "isNFT",
ModelRole.BaseGasFees.int: "baseGasFees",
ModelRole.TotalFees.int: "totalFees",
ModelRole.MaxTotalFees.int: "maxTotalFees",
ModelRole.Symbol.int: "symbol",
ModelRole.LoadingTransaction.int: "loadingTransaction",
ModelRole.TokenID.int: "tokenID",
ModelRole.NFTName.int: "nftName",
ModelRole.NFTImageURL.int: "nftImageUrl"
}.toTable
method data(self: Model, 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.Id:
result = newQVariant(item.getId())
of ModelRole.Type:
result = newQVariant(item.getType())
of ModelRole.Address:
result = newQVariant(item.getAddress())
of ModelRole.BlockNumber:
result = newQVariant(item.getBlockNumber())
of ModelRole.BlockHash:
result = newQVariant(item.getBlockHash())
of ModelRole.Timestamp:
result = newQVariant(item.getTimestamp())
of ModelRole.GasPrice:
result = newQVariant(item.getGasPrice())
of ModelRole.GasLimit:
result = newQVariant(item.getGasLimit())
of ModelRole.GasUsed:
result = newQVariant(item.getGasUsed())
of ModelRole.Nonce:
result = newQVariant(item.getNonce())
of ModelRole.TxStatus:
result = newQVariant(item.getTxStatus())
of ModelRole.Value:
result = newQVariant(item.getValue())
of ModelRole.From:
result = newQVariant(item.getFrom())
of ModelRole.To:
result = newQVariant(item.getTo())
of ModelRole.Contract:
result = newQVariant(item.getContract())
of ModelRole.ChainID:
result = newQVariant(item.getChainId())
of ModelRole.MaxFeePerGas:
result = newQVariant(item.getMaxFeePerGas())
of ModelRole.MaxPriorityFeePerGas:
result = newQVariant(item.getMaxPriorityFeePerGas())
of ModelRole.Input:
result = newQVariant(item.getInput())
of ModelRole.TxHash:
result = newQVariant(item.getTxHash())
of ModelRole.MultiTransactionID:
result = newQVariant(item.getMultiTransactionID())
of ModelRole.IsTimeStamp:
result = newQVariant(item.getIsTimeStamp())
of ModelRole.IsNFT:
result = newQVariant(item.getIsNFT())
of ModelRole.BaseGasFees:
result = newQVariant(item.getBaseGasFees())
of ModelRole.TotalFees:
result = newQVariant(item.getTotalFees())
of ModelRole.MaxTotalFees:
result = newQVariant(item.getMaxTotalFees())
of ModelRole.Symbol:
result = newQVariant(item.getSymbol())
of ModelRole.LoadingTransaction:
result = newQVariant(item.getLoadingTransaction())
of ModelRole.TokenID:
result = newQVariant(item.getTokenID().toString())
of ModelRole.NFTName:
result = newQVariant(item.getNFTName())
of ModelRole.NFTImageURL:
result = newQVariant(item.getNFTImageURL())
proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel()
self.items = items
self.endResetModel()
self.countChanged()
proc resetItems*(self: Model) =
self.beginResetModel()
self.items = @[]
self.endResetModel()
self.countChanged()
proc getLastTxBlockNumber*(self: Model): string {.slot.} =
if (self.items.len == 0):
return "0x0"
return self.items[^1].getBlockNumber()
proc hasMoreChanged*(self: Model) {.signal.}
proc getHasMore*(self: Model): bool {.slot.} =
return self.hasMore
proc setHasMore*(self: Model, hasMore: bool) {.slot.} =
self.hasMore = hasMore
self.hasMoreChanged()
QtProperty[bool] hasMore:
read = getHasMore
write = setHasMore
notify = hasMoreChanged
proc cmpTransactions*(x, y: Item): int =
# Sort proc to compare transactions from a single account.
# Compares first by block number, then by nonce
if x.getBlockNumber().isEmptyOrWhitespace or y.getBlockNumber().isEmptyOrWhitespace :
return cmp(x.getTimestamp(), y.getTimestamp())
result = cmp(x.getBlockNumber().parseHexInt, y.getBlockNumber().parseHexInt)
if result == 0:
result = cmp(x.getNonce(), y.getNonce())
proc addNewTransactions*(self: Model, transactions: seq[Item], wasFetchMore: bool) =
if transactions.len == 0:
return
var txs = transactions
# Reset the model if empty
if self.items.len == 0:
eth_service_utils.deduplicate(txs, tx => tx.getTxHash())
self.setItems(txs)
return
# Concatenate existing and new, filter out duplicates
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
var newItems = concat(self.items, txs)
eth_service_utils.deduplicate(newItems, tx => tx.getTxHash())
# Though we claim that we insert rows to preserve listview indices without
# model reset, we actually reset the list, but since old items order is not changed
# by deduplicate() call, the order is preserved and only new items are added
# to the end. For model it looks like we inserted.
# Unsorted, sorting is done on UI side
self.beginInsertRows(parentModelIndex, self.items.len, newItems.len - 1)
self.items = newItems
self.endInsertRows()
self.countChanged()
proc addPageSizeBuffer*(self: Model, pageSize: int) =
if pageSize > 0:
self.beginInsertRows(newQModelIndex(), self.items.len, self.items.len + pageSize - 1)
for i in 0 ..< pageSize:
self.items.add(initLoadingItem())
self.endInsertRows()
self.countChanged()
proc removePageSizeBuffer*(self: Model) =
for i in 0 ..< self.items.len:
if self.items[i].getLoadingTransaction():
self.beginRemoveRows(newQModelIndex(), i, self.items.len-1)
self.items.delete(i, self.items.len-1)
self.endRemoveRows()
self.countChanged()
return

View File

@ -1,142 +0,0 @@
import NimQml, stint, json, sequtils, sugar
import ./io_interface, ./view, ./controller, ./item, ./utils, ./multi_transaction_item
import ../io_interface as delegate_interface
import ../../../../global/global_singleton
import ../../../../core/eventemitter
import ../../../../../app_service/common/wallet_constants
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
import ../../../../../app_service/service/currency/service as currency_service
export io_interface
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
view: View
controller: Controller
moduleLoaded: bool
# Forward declarations
method loadTransactions*(self: Module, address: string, toBlock: string = "0x0", limit: int = 20, loadMore: bool = false)
proc newModule*(
delegate: delegate_interface.AccessInterface,
events: EventEmitter,
transactionService: transaction_service.Service,
walletAccountService: wallet_account_service.Service,
networkService: network_service.Service,
currencyService: currency_service.Service,
): Module =
result = Module()
result.delegate = delegate
result.view = newView(result)
result.controller = controller.newController(result, events, transactionService, walletAccountService, networkService, currencyService)
result.moduleLoaded = false
method delete*(self: Module) =
self.view.delete
self.controller.delete
method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("walletSectionTransactions", newQVariant(self.view))
self.controller.init()
self.view.load()
method isLoaded*(self: Module): bool =
return self.moduleLoaded
proc getResolvedSymbol*(self: Module, transaction: TransactionDto): string =
if transaction.symbol != "":
result = transaction.symbol
else:
let contractSymbol = self.controller.findTokenSymbolByAddress(transaction.contract)
if contractSymbol != "":
result = contractSymbol
elif transaction.typeValue == "erc20":
result = ""
else:
result = "ETH"
method transactionsToItems*(self: Module, transactions: seq[TransactionDto], collectibles: seq[CollectibleDto]): seq[Item] =
let gweiFormat = self.controller.getCurrencyFormat("Gwei")
let ethFormat = self.controller.getCurrencyFormat("ETH")
transactions.map(t => (block:
let resolvedSymbol = self.getResolvedSymbol(t)
return transactionToItem(t, resolvedSymbol, self.controller.getCurrencyFormat(resolvedSymbol), ethFormat, gweiFormat)
))
proc setPendingTx(self: Module) =
self.view.setPendingTx(self.transactionsToItems(self.controller.watchPendingTransactions(), @[]))
method setEnabledChainIds*(self: Module) =
let enabledChainIds = self.controller.getEnabledChainIds()
self.view.setEnabledChainIds(enabledChainIds)
method refreshTransactions*(self: Module) =
self.setEnabledChainIds()
self.view.resetTrxHistory()
self.view.setPendingTx(self.transactionsToItems(self.controller.getPendingTransactions(), @[]))
for account in self.controller.getWalletAccounts():
let transactions = self.controller.getAllTransactions(account.address)
self.view.setTrxHistoryResult(self.transactionsToItems(transactions, @[]), account.address, wasFetchMore=false)
method viewDidLoad*(self: Module) =
let accounts = self.controller.getWalletAccounts()
self.moduleLoaded = true
self.delegate.transactionsModuleDidLoad()
self.setEnabledChainIds()
self.setPendingTx()
method filterChanged*(self: Module, addresses: seq[string], chainIds: seq[int]) =
let walletAccount = self.controller.getWalletAccountByAddress(addresses[0])
self.view.switchAccount(walletAccount)
method loadTransactions*(self: Module, address: string, toBlock: string = "0x0", limit: int = 20, loadMore: bool = false) =
let toBlockParsed = stint.fromHex(Uint256, toBlock)
let txLimit = if toBlock == "0x0":
limit
else:
limit + 1
self.controller.loadTransactions(address, toBlockParsed, txLimit, loadMore)
method setTrxHistoryResult*(self: Module, transactions: seq[TransactionDto], collectibles: seq[CollectibleDto], address: string, wasFetchMore: bool) =
self.view.setTrxHistoryResult(self.transactionsToItems(transactions, collectibles), address, wasFetchMore)
method setHistoryFetchState*(self: Module, addresses: seq[string], isFetching: bool) =
self.view.setHistoryFetchStateForAccounts(addresses, isFetching)
method setHistoryFetchState*(self: Module, addresses: seq[string], isFetching: bool, hasMore: bool) =
self.view.setHistoryFetchStateForAccounts(addresses, isFetching, hasMore)
method setHistoryFetchState*(self: Module, address: string, allTxLoaded: bool, isFetching: bool) =
self.view.setHistoryFetchState(address, allTxLoaded, isFetching)
method setIsNonArchivalNode*(self: Module, isNonArchivalNode: bool) =
self.view.setIsNonArchivalNode(isNonArchivalNode)
method getChainIdForChat*(self: Module): int =
return self.controller.getChainIdForChat()
method getChainIdForBrowser*(self: Module): int =
return self.controller.getChainIdForBrowser()
method getLatestBlockNumber*(self: Module, chainId: int): string =
return self.controller.getLatestBlockNumber(chainId)
method transactionWasSent*(self: Module, result: string) =
self.view.setPendingTx(self.transactionsToItems(self.controller.getPendingTransactions(), @[]))
method fetchDecodedTxData*(self: Module, txHash: string, data: string) =
self.controller.fetchDecodedTxData(txHash, data)
method txDecoded*(self: Module, txHash: string, dataDecoded: string) =
self.view.txDecoded(txHash, dataDecoded)
method getNetworkLayer*(self: Module, chainId: int): string =
return self.delegate.getNetworkLayer(chainId)

View File

@ -1,66 +0,0 @@
import strutils, stint
import ../../../../global/global_singleton
import ../../../../../app_service/service/transaction/dto
import ../../../../../app_service/service/currency/dto as currency_dto
import ../../../shared/wallet_utils
import ../../../shared_models/currency_amount
import ./item
import ./multi_transaction_item
import ./backend/transactions
proc hex2GweiCurrencyAmount(hexValueStr: string, gweiFormat: CurrencyFormatDto): CurrencyAmount =
let value = parseFloat(singletonInstance.utils.hex2Gwei(hexValueStr))
return currencyAmountToItem(value, gweiFormat)
proc hex2EthCurrencyAmount(hexValueStr: string, ethFormat: CurrencyFormatDto): CurrencyAmount =
let value = parseFloat(singletonInstance.utils.hex2Eth(hexValueStr))
return currencyAmountToItem(value, ethFormat)
proc hex2TokenCurrencyAmount(hexValueStr: string, tokenFormat: CurrencyFormatDto): CurrencyAmount =
let value = parseFloat(singletonInstance.utils.hex2Eth(hexValueStr))
return currencyAmountToItem(value, tokenFormat)
proc transactionToItem*(t: TransactionDto, resolvedSymbol: string, tokenFormat: CurrencyFormatDto, ethFormat: CurrencyFormatDto, gweiFormat: CurrencyFormatDto): Item =
return initItem(
t.id,
t.typeValue,
t.address,
t.blockNumber,
t.blockHash,
toInt(t.timestamp),
hex2EthCurrencyAmount(t.gasPrice, ethFormat),
parseInt(singletonInstance.utils.hex2Dec(t.gasLimit)),
parseInt(singletonInstance.utils.hex2Dec(t.gasUsed)),
t.nonce,
t.txStatus,
hex2TokenCurrencyAmount(t.value, tokenFormat),
t.fromAddress,
t.to,
t.contract,
t.chainId,
hex2GweiCurrencyAmount(t.maxFeePerGas, gweiFormat),
hex2GweiCurrencyAmount(t.maxPriorityFeePerGas, gweiFormat),
t.input,
t.txHash,
t.multiTransactionID,
false,
hex2GweiCurrencyAmount(t.baseGasFees, gweiFormat),
hex2GweiCurrencyAmount(t.totalFees, gweiFormat),
hex2GweiCurrencyAmount(t.maxTotalFees, gweiFormat),
resolvedSymbol
)
proc multiTransactionToItem*(t: MultiTransactionDto): MultiTransactionItem =
return initMultiTransactionItem(
t.id,
t.timestamp,
t.fromAddress,
t.toAddress,
t.fromAsset,
t.toAsset,
t.fromAmount,
t.multiTxType
)

View File

@ -1,200 +0,0 @@
import NimQml, tables, stint, json, strformat, sequtils, strutils, sugar, times, math
import ./item
import ./model
import ./io_interface
import ../../../../global/global_singleton
import ../../../../../app_service/common/conversion as common_conversion
import ../../../../../app_service/service/wallet_account/dto
type
LatestBlockData* = ref object of RootObj
blockNumber*: int
datetime*: DateTime
isLayer1*: bool
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
models: Table[string, Model]
model: Model
modelVariant: QVariant
fetchingHistoryState: Table[string, bool]
enabledChainIds: seq[int]
isNonArchivalNode: bool
tempAddress: string
latestBlockNumbers: Table[int, LatestBlockData]
proc delete*(self: View) =
self.model.delete
self.modelVariant.delete
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.delegate = delegate
result.model = newModel()
result.modelVariant = newQVariant(result.model)
proc load*(self: View) =
self.delegate.viewDidLoad()
proc modelChanged*(self: View) {.signal.}
proc getModel(self: View): QVariant {.slot.} =
return self.modelVariant
QtProperty[QVariant] model:
read = getModel
notify = modelChanged
proc setItems*(self: View, items: seq[Item]) =
self.model.setItems(items)
proc historyWasFetched*(self: View) {.signal.}
proc loadingTrxHistoryChanged*(self: View, isLoading: bool, address: string) {.signal.}
proc setHistoryFetchState*(self: View, address: string, allTxLoaded: bool, isFetching: bool) =
if self.models.hasKey(address):
if not isFetching:
self.models[address].removePageSizeBuffer()
elif isFetching and self.models[address].getCount() == 0:
self.models[address].addPageSizeBuffer(20)
self.models[address].setHasMore(not allTxLoaded)
self.fetchingHistoryState[address] = isFetching
self.loadingTrxHistoryChanged(isFetching, address)
proc isFetchingHistory*(self: View, address: string): bool {.slot.} =
if self.fetchingHistoryState.hasKey(address):
return self.fetchingHistoryState[address]
return true
proc isHistoryFetched*(self: View, address: string): bool {.slot.} =
return self.model.getCount() > 0
proc loadTransactionsForAccount*(self: View, address: string, toBlock: string = "0x0", limit: int = 20, loadMore: bool = false) {.slot.} =
if self.models.hasKey(address):
self.setHistoryFetchState(address, allTxLoaded=not self.models[address].getHasMore(), isFetching=true)
self.models[address].addPageSizeBuffer(limit)
self.delegate.loadTransactions(address, toBlock, limit, loadMore)
proc resetTrxHistory*(self: View) =
for address in self.models.keys:
self.models[address].resetItems()
proc setTrxHistoryResult*(self: View, transactions: seq[Item], address: string, wasFetchMore: bool) =
var toAddTransactions: seq[Item] = @[]
for tx in transactions:
if not self.enabledChainIds.contains(tx.getChainId()):
continue
toAddTransactions.add(tx)
if not self.models.hasKey(address):
self.models[address] = newModel()
self.models[address].removePageSizeBuffer()
self.models[address].addNewTransactions(toAddTransactions, wasFetchMore)
if self.fetchingHistoryState.hasKey(address) and self.fetchingHistoryState[address] and wasFetchMore:
self.models[address].addPageSizeBuffer(toAddTransactions.len)
proc setHistoryFetchStateForAccounts*(self: View, addresses: seq[string], isFetching: bool) =
for address in addresses:
if self.models.hasKey(address):
self.setHistoryFetchState(address, allTxLoaded = not self.models[address].getHasMore(), isFetching)
else:
self.setHistoryFetchState(address, allTxLoaded = false, isFetching)
proc setHistoryFetchStateForAccounts*(self: View, addresses: seq[string], isFetching: bool, hasMore: bool) =
for address in addresses:
self.setHistoryFetchState(address, allTxLoaded = not hasMore, isFetching)
proc setModel*(self: View, address: string) {.slot.} =
if not self.models.hasKey(address):
self.models[address] = newModel()
self.model = self.models[address]
self.modelVariant = newQVariant(self.model)
self.modelChanged()
proc switchAccount*(self: View, walletAccount: WalletAccountDto) =
self.setModel(walletAccount.address)
proc getIsNonArchivalNode(self: View): QVariant {.slot.} =
return newQVariant(self.isNonArchivalNode)
proc setEnabledChainIds*(self: View, chainIds: seq[int]) =
self.enabledChainIds = chainIds
proc isNonArchivalNodeChanged(self: View) {.signal.}
proc setIsNonArchivalNode*(self: View, isNonArchivalNode: bool) =
self.isNonArchivalNode = isNonArchivalNode
self.isNonArchivalNodeChanged()
QtProperty[QVariant] isNonArchivalNode:
read = getIsNonArchivalNode
notify = isNonArchivalNodeChanged
proc getChainIdForChat*(self: View): int {.slot.} =
return self.delegate.getChainIdForChat()
proc getChainIdForBrowser*(self: View): int {.slot.} =
return self.delegate.getChainIdForBrowser()
proc getLatestBlockNumber*(self: View, chainId: int): int {.slot.} =
if self.latestBlockNumbers.hasKey(chainId):
let blockData = self.latestBlockNumbers[chainId]
let timeDiff = (now() - blockData.datetime)
var multiplier = -1.0
if blockData.isLayer1:
multiplier = 0.083 # 1 every 12 seconds
else:
case chainId:
of 10: # Optimism
multiplier = 0.5 # 1 every 2 second
of 42161: # Arbitrum
multiplier = 3.96 # around 4 every 1 second
else:
multiplier = -1.0
if multiplier >= 0.0 and timeDiff < initDuration(minutes = 10):
let blockNumDiff = inSeconds(timeDiff).float64 * multiplier
return (blockData.blockNumber + (int)round(blockNumDiff))
let latestBlockNumber = self.delegate.getLatestBlockNumber(chainId)
let latestBlockNumberNumber = parseInt(singletonInstance.utils.hex2Dec(latestBlockNumber))
self.latestBlockNumbers[chainId] = LatestBlockData(
blockNumber: latestBlockNumberNumber,
datetime: now(),
isLayer1: self.delegate.getNetworkLayer(chainId) == "1"
)
return latestBlockNumberNumber
proc setPendingTx*(self: View, pendingTx: seq[Item]) =
for tx in pendingTx:
if not self.enabledChainIds.contains(tx.getChainId()):
continue
let fromAddress = tx.getfrom()
if not self.models.hasKey(fromAddress):
self.models[fromAddress] = newModel()
self.models[fromAddress].addNewTransactions(@[tx], wasFetchMore=false)
proc prepareTransactionsForAddress*(self: View, address: string) {.slot.} =
self.tempAddress = address
proc getTransactions*(self: View): QVariant {.slot.} =
if self.models.hasKey(self.tempAddress):
return newQVariant(self.models[self.tempAddress])
else:
return newQVariant()
proc fetchDecodedTxData*(self: View, txHash: string, data: string) {.slot.} =
self.delegate.fetchDecodedTxData(txHash, data)
proc txDecoded*(self: View, txHash: string, dataDecoded: string) {.signal.}

View File

@ -16,8 +16,10 @@ QtObject:
tmpAmount: float # shouldn't be used anywhere except in prepare*/getPrepared* procs
tmpSymbol: string # shouldn't be used anywhere except in prepare*/getPrepared* procs
activityController: activityc.Controller
tmpActivityController: activityc.Controller
collectiblesController: collectiblesc.Controller
collectibleDetailsController: collectible_detailsc.Controller
isNonArchivalNode: bool
proc setup(self: View) =
self.QObject.setup
@ -25,10 +27,11 @@ QtObject:
proc delete*(self: View) =
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface, activityController: activityc.Controller, collectiblesController: collectiblesc.Controller, collectibleDetailsController: collectible_detailsc.Controller): View =
proc newView*(delegate: io_interface.AccessInterface, activityController: activityc.Controller, tmpActivityController: activityc.Controller, collectiblesController: collectiblesc.Controller, collectibleDetailsController: collectible_detailsc.Controller): View =
new(result, delete)
result.delegate = delegate
result.activityController = activityController
result.tmpActivityController = tmpActivityController
result.collectiblesController = collectiblesController
result.collectibleDetailsController = collectibleDetailsController
result.setup()
@ -135,3 +138,32 @@ QtObject:
return newQVariant(self.collectibleDetailsController)
QtProperty[QVariant] collectibleDetailsController:
read = getCollectibleDetailsController
proc getTmpActivityController(self: View): QVariant {.slot.} =
return newQVariant(self.tmpActivityController)
QtProperty[QVariant] tmpActivityController:
read = getTmpActivityController
proc getChainIdForChat*(self: View): int {.slot.} =
return self.delegate.getChainIdForChat()
proc getLatestBlockNumber*(self: View, chainId: int): string {.slot.} =
return self.delegate.getLatestBlockNumber(chainId)
proc fetchDecodedTxData*(self: View, txHash: string, data: string) =
self.delegate.fetchDecodedTxData(txHash, data)
proc getIsNonArchivalNode(self: View): bool {.slot.} =
return self.isNonArchivalNode
proc isNonArchivalNodeChanged(self: View) {.signal.}
proc setIsNonArchivalNode*(self: View, isNonArchivalNode: bool) =
self.isNonArchivalNode = isNonArchivalNode
self.isNonArchivalNodeChanged()
QtProperty[bool] isNonArchivalNode:
read = getIsNonArchivalNode
notify = isNonArchivalNodeChanged
proc txDecoded*(self: View, txHash: string, dataDecoded: string) {.signal.}

View File

@ -49,7 +49,7 @@ QtObject:
result.threadpool = threadpool
result.tokenService = tokenService
result.settingsService = settingsService
proc init*(self: Service) =
self.events.on(SignalType.Wallet.event) do(e:Args):
var data = WalletSignal(e)

View File

@ -12,51 +12,6 @@ proc sortAsc[T](t1, t2: T): int =
elif (t1.fromNetwork.chainId < t2.fromNetwork.chainId): return -1
else: return 0
type
LoadTransactionsTaskArg* = ref object of QObjectTaskArg
chainId: int
address: string
toBlock: Uint256
limit: int
collectiblesLimit: int
loadMore: bool
allTxLoaded: bool
const loadTransactionsTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[LoadTransactionsTaskArg](argEncoded)
let output = %* {
"address": arg.address,
"chainId": arg.chainId,
"history": "",
"collectibles": "",
"loadMore": arg.loadMore,
"allTxLoaded": ""
}
try:
let limitAsHex = "0x" & eth_utils.stripLeadingZeros(arg.limit.toHex)
let response = transactions.getTransfersByAddress(arg.chainId, arg.address, arg.toBlock, limitAsHex, arg.loadMore).result
output["history"] = response
output["allTxLoaded"] = %(response.getElems().len < arg.limit)
# Fetch collectibles for transactions
var uniqueIds: seq[collectibles.CollectibleUniqueID] = @[]
for txJson in response.getElems():
let tx = txJson.toTransactionDto()
if tx.typeValue == ERC721_TRANSACTION_TYPE:
let nftId = collectibles.CollectibleUniqueID(
chainID: arg.chainId,
contractAddress: tx.contract,
tokenID: tx.tokenId
)
if not uniqueIds.any(x => (x == nftId)):
uniqueIds.add(nftId)
except Exception as e:
let errDesription = e.msg
error "error loadTransactionsTask: ", errDesription
arg.finish(output)
type
GetSuggestedRoutesTaskArg* = ref object of QObjectTaskArg
account: string

View File

@ -24,11 +24,6 @@ type
proc event*(self:PendingTransactionTypeDto):string =
result = "transaction:" & $self
# Deprecated type, to be removed along with transaction service/module
type
CollectibleDto* = ref object of RootObj
dummy: string
type
TransactionDto* = ref object of RootObj
id*: string

View File

@ -37,13 +37,8 @@ include ../../common/json_utils
const collectiblesLimit = 200
# Signals which may be emitted by this service:
const SIGNAL_TRANSACTIONS_LOADED* = "transactionsLoaded"
const SIGNAL_TRANSACTION_SENT* = "transactionSent"
const SIGNAL_SUGGESTED_ROUTES_READY* = "suggestedRoutesReady"
const SIGNAL_TRANSACTION_LOADING_COMPLETED_FOR_ALL_NETWORKS* = "transactionsLoadingCompleteForAllNetworks"
# TODO: soon to be removed with old transactions module
const SIGNAL_HISTORY_FETCHING* = "historyFetching"
const SIGNAL_HISTORY_READY* = "historyReady"
const SIGNAL_HISTORY_NON_ARCHIVAL_NODE* = "historyNonArchivalNode"
const SIGNAL_HISTORY_ERROR* = "historyError"
const SIGNAL_CRYPTO_SERVICES_READY* = "cryptoServicesReady"
@ -75,20 +70,6 @@ proc `$`*(self: TransactionMinedArgs): string =
data: {self.data},
]"""
type
HistoryArgs* = ref object of Args
addresses*: seq[string]
type
TransactionsLoadedArgs* = ref object of Args
transactions*: seq[TransactionDto]
collectibles*: seq[CollectibleDto]
address*: string
wasFetchMore*: bool
allTxLoaded*: bool
tempLoadingTx*: int
type
TransactionSentArgs* = ref object of Args
result*: string
@ -112,12 +93,6 @@ QtObject:
networkService: network_service.Service
settingsService: settings_service.Service
tokenService: token_service.Service
txCounter: Table[string, seq[int]]
allTxLoaded: Table[string, bool]
allTransactions: Table[string, Table[string, TransactionDto]]
# Forward declaration
proc loadTransactions*(self: Service, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false)
proc delete*(self: Service) =
self.QObject.delete
@ -136,18 +111,11 @@ QtObject:
result.networkService = networkService
result.settingsService = settingsService
result.tokenService = tokenService
result.txCounter = initTable[string, seq[int]]()
result.allTxLoaded = initTable[string, bool]()
result.allTransactions = initTable[string, Table[string, TransactionDto]]()
proc init*(self: Service) =
self.events.on(SignalType.Wallet.event) do(e:Args):
var data = WalletSignal(e)
case data.eventType:
of transactions.EventRecentHistoryReady:
for account in data.accounts:
self.loadTransactions(account, stint.fromHex(Uint256, "0x0"))
self.events.emit(SIGNAL_HISTORY_READY, HistoryArgs(addresses: data.accounts))
of transactions.EventNonArchivalNodeDetected:
self.events.emit(SIGNAL_HISTORY_NON_ARCHIVAL_NODE, Args())
of transactions.EventFetchingHistoryError:
@ -170,12 +138,6 @@ QtObject:
let allPendingTransactions = self.getPendingTransactions()
return allPendingTransactions.filter(x => x.typeValue == $transactionType)
proc getAllTransactions*(self: Service, address: string): seq[TransactionDto] =
if not self.allTransactions.hasKey(address):
return @[]
return toSeq(self.allTransactions[address].values)
proc watchTransactionResult*(self: Service, watchTxResult: string) {.slot.} =
let watchTxResult = parseJson(watchTxResult)
let success = watchTxResult["isSuccessfull"].getBool
@ -237,65 +199,6 @@ QtObject:
self.watchTransaction(tx.txHash, tx.fromAddress, tx.to, tx.typeValue, tx.input, tx.chainId, track = false)
return pendingTransactions
proc onTransactionsLoaded*(self: Service, historyJSON: string) {.slot.} =
let historyData = parseJson(historyJSON)
let address = historyData["address"].getStr
let chainID = historyData["chainId"].getInt
let wasFetchMore = historyData["loadMore"].getBool
let allTxLoaded = historyData["allTxLoaded"].getBool
var transactions: seq[TransactionDto] = @[]
var collectibles: seq[CollectibleDto] = @[]
for tx in historyData["history"].getElems():
let dto = tx.toTransactionDto()
self.allTransactions.mgetOrPut(address, initTable[string, TransactionDto]())[dto.txHash] = dto
transactions.add(dto)
if self.allTxLoaded.hasKey(address):
self.allTxLoaded[address] = self.allTxLoaded[address] and allTxLoaded
else:
self.allTxLoaded[address] = allTxLoaded
# emit event
self.events.emit(SIGNAL_TRANSACTIONS_LOADED, TransactionsLoadedArgs(
transactions: transactions,
collectibles: collectibles,
address: address,
wasFetchMore: wasFetchMore
))
# when requests for all networks are completed then set loading state as completed
if self.txCounter.hasKey(address) and self.allTxLoaded.hasKey(address) :
var chainIDs = self.txCounter[address]
chainIDs.del(chainIDs.find(chainID))
self.txCounter[address] = chainIDs
if self.txCounter[address].len == 0:
self.txCounter.del(address)
self.events.emit(SIGNAL_TRANSACTION_LOADING_COMPLETED_FOR_ALL_NETWORKS, TransactionsLoadedArgs(address: address, allTxLoaded: self.allTxLoaded[address]))
proc loadTransactions*(self: Service, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false) =
let networks = self.networkService.getNetworks()
self.allTxLoaded.del(address)
if not self.txCounter.hasKey(address):
var networkChains: seq[int] = @[]
self.txCounter[address] = networkChains
for network in networks:
networkChains.add(network.chainId)
let arg = LoadTransactionsTaskArg(
address: address,
tptr: cast[ByteAddress](loadTransactionsTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onTransactionsLoaded",
toBlock: toBlock,
limit: limit,
collectiblesLimit: collectiblesLimit,
loadMore: loadMore,
chainId: network.chainId,
)
self.txCounter[address] = networkChains
self.threadpool.start(arg)
proc createApprovalPath*(self: Service, route: TransactionPathDto, from_addr: string, toAddress: Address, gasFees: string): TransactionBridgeDto =
var txData = TransactionDataDto()
let approve = Approve(

View File

@ -358,6 +358,7 @@ proc fromJson*(e: JsonNode, T: typedesc[FilterResponse]): FilterResponse {.inlin
)
rpc(filterActivityAsync, "wallet"):
requestId: int32
addresses: seq[string]
chainIds: seq[ChainId]
filter: ActivityFilter
@ -388,6 +389,7 @@ proc fromJson*(e: JsonNode, T: typedesc[GetRecipientsResponse]): GetRecipientsRe
)
rpc(getRecipientsAsync, "wallet"):
requestId: int32
offset: int
limit: int
@ -403,4 +405,5 @@ proc fromJson*(e: JsonNode, T: typedesc[GetOldestTimestampResponse]): GetOldestT
)
rpc(getOldestActivityTimestampAsync, "wallet"):
requestId: int32
addresses: seq[string]

View File

@ -38,7 +38,6 @@ SplitView {
RootStore.getGasEthValue = (gasAmount, gasPrice) => { return (gasAmount * Math.pow(10, -9)).toPrecision(5) }
RootStore.getNetworkLayer = (chainId) => { return 1 }
RootStore.currentCurrency = "USD"
RootStore.history = historyMockup
root.rootStoreReady = true
}

View File

@ -16,7 +16,6 @@ QtObject {
function switchAccountByAddress(address) {
browserSectionCurrentAccount.switchAccountByAddress(address)
walletSectionTransactions.setModel(address)
}
}

View File

@ -384,6 +384,14 @@ QtObject {
communitiesModuleInst.requestToJoinCommunityWithAuthenticationWithSharedAddresses(communityId, ensName, JSON.stringify(addressesToShare), airdropAddress)
}
function getChainIdForChat() {
return walletSection.getChainIdForChat()
}
function getLatestBlockNumber(chainId) {
return walletSection.getChainIdForSend(chainId)
}
function userCanJoin(id) {
return communitiesModuleInst.userCanJoin(id)
}

View File

@ -26,6 +26,7 @@ QtObject {
property var walletSectionInst: walletSection
property var totalCurrencyBalance: walletSection.totalCurrencyBalance
property var activityController: walletSection.activityController
property var tmpActivityController: walletSection.tmpActivityController
property string signingPhrase: walletSection.signingPhrase
property string mnemonicBackedUp: walletSection.isMnemonicBackedUp
@ -143,7 +144,7 @@ QtObject {
}
function getLatestBlockNumber(chainId) {
return walletSectionTransactions.getLatestBlockNumber(chainId)
return walletSection.getLatestBlockNumber(chainId)
}
function setFilterAddress(address) {

View File

@ -33,10 +33,10 @@ Item {
onTransactionChanged: {
d.decodedInputData = ""
if (!transaction || !transaction.input || !RootStore.history)
if (!transaction || !transaction.input)
return
d.loadingInputDate = true
RootStore.history.fetchDecodedTxData(transaction.txHash, transaction.input)
walletSection.fetchDecodedTxData(transaction.txHash, transaction.input)
}
QtObject {
@ -92,7 +92,7 @@ Item {
}
Connections {
target: RootStore.history
target: RootStore.walletSectionInst
function onTxDecoded(txHash: string, dataDecoded: string) {
if (!root.isTransactionValid || txHash !== root.transaction.txHash)
return

View File

@ -169,14 +169,6 @@ QtObject {
return walletSectionSendInst.getEstimatedTime(chainId, maxFeePerGas)
}
function getChainIdForChat() {
return walletSectionTransactions.getChainIdForChat()
}
function getChainIdForBrowser() {
return walletSectionTransactions.getChainIdForBrowser()
}
function hex2Eth(value) {
return globalUtils.hex2Eth(value)
}

View File

@ -5,10 +5,6 @@ import utils 1.0
QtObject {
id: root
// property var utilsModelInst: !!utilsModel ? utilsModel : null
// property var chatsModelInst: !!chatsModel ?chatsModel : null
// property var walletModelInst: !!walletModel ? walletModel : null
// property var profileModelInst: !!profileModel ? profileModel : null
property var profileSectionModuleInst: profileSectionModule
property var privacyModule: profileSectionModuleInst.privacyModule
@ -25,29 +21,20 @@ QtObject {
property bool isTenorWarningAccepted: !!accountSensitiveSettings ? accountSensitiveSettings.isTenorWarningAccepted : false
property bool displayChatImages: !!accountSensitiveSettings ? accountSensitiveSettings.displayChatImages : false
// property string signingPhrase: !!walletModelInst ? walletModelInst.utilsView.signingPhrase : ""
// property string gasPrice: !!walletModelInst ? walletModelInst.gasView.gasPrice : "0"
// property string gasEthValue: !!walletModelInst ? walletModelInst.gasView.getGasEthValue : "0"
property CurrenciesStore currencyStore: CurrenciesStore {}
property string currentCurrency: Global.appIsReady? walletSection.currentCurrency : ""
// property string defaultCurrency: !!walletModelInst ? walletModelInst.balanceView.defaultCurrency : "0"
// property string fiatValue: !!walletModelInst ? walletModelInst.balanceView.getFiatValue : "0"
// property string cryptoValue: !!walletModelInst ? walletModelInst.balanceView.getCryptoValue : "0"
property string currentCurrency: Global.appIsReady? walletSectionInst.currentCurrency : ""
property var history: typeof walletSectionTransactions !== "undefined" ? walletSectionTransactions
: null
property var historyTransactions: Global.appIsReady? walletSection.activityController.model : null
readonly property bool loadingHistoryTransactions: Global.appIsReady && walletSection.activityController.status.loadingData
readonly property bool newDataAvailable: Global.appIsReady && walletSection.activityController.status.newDataAvailable
readonly property var transactionActivityStatus: Global.appIsReady ? walletSection.activityController.status : null
readonly property var transactionActivityStatus: Global.appIsReady ? walletSectionInst.activityController.status : null
property var historyTransactions: Global.appIsReady? walletSectionInst.activityController.model : null
readonly property bool loadingHistoryTransactions: Global.appIsReady && walletSectionInst.activityController.status.loadingData
readonly property bool newDataAvailable: Global.appIsReady && walletSectionInst.activityController.status.newDataAvailable
property bool isNonArchivalNode: Global.appIsReady && walletSectionInst.isNonArchivalNode
property bool isNonArchivalNode: history ? history.isNonArchivalNode
: false
property var marketValueStore: TokenMarketValuesStore{}
function resetFilter() {
walletSection.activityController.updateFilter()
walletSectionInst.activityController.updateFilter()
}
function getNetworkColor(chainId) {
@ -185,12 +172,12 @@ QtObject {
|| !RootStore.historyTransactions.hasMore
|| loadingHistoryTransactions)
return
walletSection.activityController.loadMoreItems()
walletSectionInst.activityController.loadMoreItems()
}
function updateTransactionFilter() {
if (transactionActivityStatus.isFilterDirty)
walletSection.activityController.updateFilter()
walletSectionInst.activityController.updateFilter()
}
function hex2Eth(value) {

View File

@ -14,16 +14,18 @@ QtObject {
property var contactStore: profileSectionStore.contactsStore
property var mainModuleInst: mainModule
property var walletSectionTransactionsInst: walletSectionTransactions
property var walletSectionSendInst: walletSectionSend
property var walletSectionInst: walletSection
property string currentCurrency: walletSection.currentCurrency
property var tmpActivityController: walletSectionInst.tmpActivityController
property string currentCurrency: walletSectionInst.currentCurrency
property var allNetworks: networksModule.all
property var overview: walletSectionOverview
property var accounts: walletSectionSendInst.accounts
property var senderAccounts: walletSectionSendInst.senderAccounts
property var selectedSenderAccount: walletSectionSendInst.selectedSenderAccount
property string signingPhrase: walletSection.signingPhrase
property string signingPhrase: walletSectionInst.signingPhrase
property var savedAddressesModel: SortFilterProxyModel {
sourceModel: walletSectionSavedAddresses.model
filters: [
@ -86,14 +88,6 @@ QtObject {
return walletSectionSendInst.getEstimatedTime(chainId, maxFeePerGas)
}
function getChainIdForChat() {
return walletSectionTransactions.getChainIdForChat()
}
function getChainIdForBrowser() {
return walletSectionTransactions.getChainIdForBrowser()
}
function suggestedRoutes(account, amount, token, disabledFromChainIDs, disabledToChainIDs, preferredChainIds, sendType, lockedInAmounts) {
walletSectionSendInst.suggestedRoutes(account, amount, token, disabledFromChainIDs, disabledToChainIDs, preferredChainIds, sendType, JSON.stringify(lockedInAmounts))
}
@ -288,14 +282,6 @@ QtObject {
return {}
}
function prepareTransactionsForAddress(address) {
walletSectionTransactions.prepareTransactionsForAddress(address)
}
function getTransactions() {
return walletSectionTransactions.getTransactions()
}
function getAllNetworksSupportedPrefix() {
return networksModule.getAllNetworksSupportedPrefix()
}

View File

@ -46,8 +46,8 @@ Loader {
break
}
case TabAddressSelectorView.Type.RecentsAddress: {
let isIncoming = root.selectedRecipient.to === root.selectedRecipient.address
root.addressText = isIncoming ? root.selectedRecipient.from : root.selectedRecipient.to
let isIncoming = root.selectedRecipient.txType === Constants.TransactionType.Receive
root.addressText = isIncoming ? root.selectedRecipient.sender : root.selectedRecipient.recipient
root.item.input.text = root.addressText
return
}

View File

@ -15,6 +15,8 @@ import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import utils 1.0
import "../panels"
import "../controls"
import "../views"
@ -118,7 +120,7 @@ Item {
Rectangle {
id: myAccountsRect
Layout.maximumWidth: parent.width
Layout.maximumHeight : myAccounts.height
Layout.maximumHeight: myAccounts.height
color: Theme.palette.indirectColor1
radius: 8
@ -147,11 +149,34 @@ Item {
}
Rectangle {
id: recentsRect
Layout.maximumWidth: parent.width
Layout.maximumHeight : recents.height
color: Theme.palette.indirectColor1
radius: 8
onVisibleChanged: {
if (visible) {
updateRecentsActivity()
}
}
Connections {
target: root
function onSelectedAccountChanged() {
if (visible) {
recentsRect.updateRecentsActivity()
}
}
}
function updateRecentsActivity() {
if(root.selectedAccount) {
root.store.tmpActivityController.setFilterAddressesJson(JSON.stringify([root.selectedAccount.address]))
}
root.store.tmpActivityController.updateFilter()
}
StatusListView {
id: recents
anchors.horizontalCenter: parent.horizontalCenter
@ -172,44 +197,38 @@ Item {
delegate: StatusListItem {
id: listItem
property bool isIncoming: root.selectedAccount ? to === root.selectedAccount.address : false
property var entry: activityEntry
property bool isIncoming: entry.txType === Constants.TransactionType.Receive
implicitWidth: ListView.view.width
height: visible ? 64 : 0
title: loading ? Constants.dummyText : isIncoming ? StatusQUtils.Utils.elideText(from,6,4) : StatusQUtils.Utils.elideText(to,6,4)
subTitle: LocaleUtils.getTimeDifference(new Date(parseInt(timestamp) * 1000), new Date())
title: isIncoming ? StatusQUtils.Utils.elideText(entry.sender,6,4) : StatusQUtils.Utils.elideText(entry.recipient,6,4)
subTitle: LocaleUtils.getTimeDifference(new Date(parseInt(entry.timestamp) * 1000), new Date())
statusListItemTitle.elide: Text.ElideMiddle
statusListItemTitle.wrapMode: Text.NoWrap
radius: 0
color: sensor.containsMouse || highlighted ? Theme.palette.baseColor2 : "transparent"
statusListItemComponentsSlot.spacing: 5
loading: loadingTransaction
components: [
StatusIcon {
id: transferIcon
height: 15
width: 15
color: isIncoming ? Style.current.success : Style.current.danger
icon: isIncoming ? "arrow-down" : "arrow-up"
color: listItem.isIncoming ? Style.current.success : Style.current.danger
icon: listItem.isIncoming ? "arrow-down" : "arrow-up"
rotation: 45
visible: !listItem.loading
},
StatusTextWithLoadingState {
id: contactsLabel
loading: listItem.loading
font.pixelSize: 15
customColor: Theme.palette.directColor1
text: loading ? Constants.dummyText : LocaleUtils.currencyAmountToLocaleString(value)
text: LocaleUtils.currencyAmountToLocaleString(entry.amountCurrency)
}
]
onClicked: recipientSelected(model, TabAddressSelectorView.Type.RecentsAddress)
onClicked: recipientSelected(entry, TabAddressSelectorView.Type.RecentsAddress)
}
model: {
if(root.selectedAccount) {
root.store.prepareTransactionsForAddress(root.selectedAccount.address)
return root.store.getTransactions()
}
}
model: root.store.tmpActivityController.model
}
}
}

View File

@ -13,7 +13,7 @@ Item {
width: parent.width
height: childrenRect.height
property var store
property var store // Expected ui/app/AppLayouts/Chat/stores/RootStore.qml
property var contactsStore
property var token

View File

@ -16,7 +16,7 @@ Item {
width: rectangleBubble.width
height: rectangleBubble.height
property var store
property var store // expected ui/app/AppLayouts/Chat/stores/RootStore.qml
property var contactsStore
property var transactionParams

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit cbb845b574fca248307778d960c993a73df78b98
Subproject commit eb8d74e1aed94930241c9fb466805b09bd45810d