chore(@desktop/wallet): Wallet: explore streamlining "all tokens model" spread across different sections

closes 
This commit is contained in:
Khushboo Mehta 2023-10-20 10:19:48 +02:00 committed by Khushboo-dev-cpp
parent 5257d003d9
commit b315d8b9b8
38 changed files with 901 additions and 148 deletions

@ -593,7 +593,7 @@ proc buildTokensAndCollectiblesFromWallet(self: Module) =
key = token.symbol,
name = token.name,
symbol = token.symbol,
color = token.color,
color = "",
communityId = token.communityId,
image = "",
category = ord(TokenListItemCategory.General),

@ -252,7 +252,7 @@ method delete*[T](self: Module[T]) =
self.view.delete
self.viewVariant.delete
proc createTokenItem[T](self: Module[T], tokenDto: CommunityTokenDto) : TokenItem =
proc createTokenItem[T](self: Module[T], tokenDto: CommunityTokenDto) : token_item.TokenItem =
let network = self.controller.getNetwork(tokenDto.chainId)
let tokenOwners = self.controller.getCommunityTokenOwners(tokenDto.communityId, tokenDto.chainId, tokenDto.address)
let ownerAddressName = if len(tokenDto.deployer) > 0: self.controller.getCommunityTokenOwnerName(tokenDto.deployer) else: ""
@ -262,7 +262,7 @@ proc createTokenItem[T](self: Module[T], tokenDto: CommunityTokenDto) : TokenIte
let destructedAmount = self.controller.getRemoteDestructedAmount(tokenDto.chainId, tokenDto.address)
result = initTokenItem(tokenDto, network, tokenOwners, ownerAddressName, burnState, remoteDestructedAddresses, remainingSupply, destructedAmount)
proc createTokenItemImproved[T](self: Module[T], tokenDto: CommunityTokenDto, communityTokenJsonItems: JsonNode) : TokenItem =
proc createTokenItemImproved[T](self: Module[T], tokenDto: CommunityTokenDto, communityTokenJsonItems: JsonNode) : token_item.TokenItem =
# These 3 values come from local caches so they can be done sync
let network = self.controller.getNetwork(tokenDto.chainId)
let tokenOwners = self.controller.getCommunityTokenOwners(tokenDto.communityId, tokenDto.chainId, tokenDto.address)

@ -155,10 +155,13 @@ proc getPrice*(self: Controller, crypto: string, fiat: string): float64 =
proc getStatusToken*(self: Controller): string =
let token = self.ensService.getStatusToken()
if token == nil:
return $ %*{}
let jsonObj = %* {
"name": token.name,
"symbol": token.symbol,
"address": token.addressAsString()
"address": token.address
}
return $jsonObj

@ -165,10 +165,13 @@ proc getChainIdForStickers*(self: Controller): int =
proc getStatusToken*(self: Controller): string =
let token = self.stickerService.getStatusToken()
if token == nil:
return $ %*{}
let jsonObj = %* {
"name": token.name,
"symbol": token.symbol,
"address": token.addressAsString()
"address": token.address
}
return $jsonObj

@ -18,6 +18,7 @@ import app/core/signals/types
import backend/activity as backend_activity
import backend/backend as backend
import app_service/common/conversion
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
@ -361,7 +362,7 @@ QtObject:
assets.add(backend_activity.Token(
tokenType: tokenType,
chainId: backend_activity.ChainId(token.chainId),
address: some(token.address)
address: some(parseAddress(token.address))
))
self.currentActivityFilter.assets = assets

@ -0,0 +1,55 @@
import NimQml, Tables, strutils
import ./io_interface
type
ModelRole {.pure.} = enum
ChainId = UserRole + 1
Address
QtObject:
type AddressPerChainModel* = ref object of QAbstractListModel
delegate: io_interface.TokenBySymbolModelDataSource
index: int
proc setup(self: AddressPerChainModel) =
self.QAbstractListModel.setup
self.index = 0
proc delete(self: AddressPerChainModel) =
self.QAbstractListModel.delete
proc newAddressPerChainModel*(delegate: io_interface.TokenBySymbolModelDataSource, index: int): AddressPerChainModel =
new(result, delete)
result.setup
result.delegate = delegate
result.index = index
method rowCount(self: AddressPerChainModel, index: QModelIndex = nil): int =
return self.delegate.getTokenBySymbolList()[self.index].addressPerChainId.len
proc countChanged(self: AddressPerChainModel) {.signal.}
proc getCount(self: AddressPerChainModel): int {.slot.} =
return self.rowCount()
QtProperty[int] count:
read = getCount
notify = countChanged
method roleNames(self: AddressPerChainModel): Table[int, string] =
{
ModelRole.ChainId.int:"chainId",
ModelRole.Address.int:"address",
}.toTable
method data(self: AddressPerChainModel, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.rowCount():
return
let item = self.delegate.getTokenBySymbolList()[self.index].addressPerChainId[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.ChainId:
result = newQVariant(item.chainId)
of ModelRole.Address:
result = newQVariant(item.address)

@ -44,4 +44,13 @@ method getHistoricalDataForToken*(self: Controller, symbol: string, currency: st
self.tokenService.getHistoricalDataForToken(symbol, currency, range)
method fetchHistoricalBalanceForTokenAsJson*(self: Controller, address: string, tokenSymbol: string, currencySymbol: string, timeIntervalEnum: int) =
self.tokenService.fetchHistoricalBalanceForTokenAsJson(address, tokenSymbol, currencySymbol, BalanceHistoryTimeInterval(timeIntervalEnum))
self.tokenService.fetchHistoricalBalanceForTokenAsJson(address, tokenSymbol, currencySymbol, BalanceHistoryTimeInterval(timeIntervalEnum))
proc getSourcesOfTokensList*(self: Controller): var seq[SupportedSourcesItem] =
return self.tokenService.getSourcesOfTokensList()
proc getFlatTokensList*(self: Controller): var seq[TokenItem] =
return self.tokenService.getFlatTokensList()
proc getTokenBySymbolList*(self: Controller): var seq[TokenBySymbolItem] =
return self.tokenService.getTokenBySymbolList()

@ -0,0 +1,116 @@
import NimQml, Tables, strutils
import ./io_interface
const SOURCES_DELIMITER = ";"
type
ModelRole {.pure.} = enum
# The key is built as a concatenation of chainId and address
# to create a unique key for each element in the flat tokens list
Key = UserRole + 1
Name
Symbol
# uniswap/status/custom seq[string]
# returned as a string as nim doesnt support returning a StringList
# using join api and semicolon (;) as a delimiter
Sources
ChainId
Address
Decimals
Image
# Native, Erc20, Erc721
Type
# only be valid if source is custom
CommunityId
# everything below should be lazy loaded
Description
# properties below this are optional and may not exist in case of community minted assets
# built from chainId and address using networks service
WebsiteUrl
MarketValues
QtObject:
type FlatTokensModel* = ref object of QAbstractListModel
delegate: io_interface.FlatTokenModelDataSource
proc setup(self: FlatTokensModel) =
self.QAbstractListModel.setup
proc delete(self: FlatTokensModel) =
self.QAbstractListModel.delete
proc newFlatTokensModel*(delegate: io_interface.FlatTokenModelDataSource): FlatTokensModel =
new(result, delete)
result.setup
result.delegate = delegate
method rowCount(self: FlatTokensModel, index: QModelIndex = nil): int =
return self.delegate.getFlatTokensList().len
proc countChanged(self: FlatTokensModel) {.signal.}
proc getCount(self: FlatTokensModel): int {.slot.} =
return self.rowCount()
QtProperty[int] count:
read = getCount
notify = countChanged
method roleNames(self: FlatTokensModel): Table[int, string] =
{
ModelRole.Key.int:"key",
ModelRole.Name.int:"name",
ModelRole.Symbol.int:"symbol",
ModelRole.Sources.int:"sources",
ModelRole.ChainId.int:"chainId",
ModelRole.Address.int:"address",
ModelRole.Decimals.int:"decimals",
ModelRole.Image.int:"image",
ModelRole.Type.int:"type",
ModelRole.CommunityId.int:"communityId",
ModelRole.Description.int:"description",
ModelRole.WebsiteUrl.int:"websiteUrl",
ModelRole.MarketValues.int:"marketValues",
}.toTable
method data(self: FlatTokensModel, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.rowCount():
return
# the only way to read items from service is by this single method getFlatTokensList
let item = self.delegate.getFlatTokensList()[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.Key:
result = newQVariant(item.key)
of ModelRole.Name:
result = newQVariant(item.name)
of ModelRole.Symbol:
result = newQVariant(item.symbol)
of ModelRole.Sources:
result = newQVariant(SOURCES_DELIMITER & item.sources.join(SOURCES_DELIMITER) & SOURCES_DELIMITER)
of ModelRole.ChainId:
result = newQVariant(item.chainId)
of ModelRole.Address:
result = newQVariant(item.address)
of ModelRole.Decimals:
result = newQVariant(item.decimals)
of ModelRole.Image:
result = newQVariant(item.image)
of ModelRole.Type:
result = newQVariant(ord(item.`type`))
of ModelRole.CommunityId:
result = newQVariant(item.communityId)
# ToDo fetching of market values not done yet
of ModelRole.Description:
result = newQVariant("")
of ModelRole.WebsiteUrl:
result = newQVariant("")
of ModelRole.MarketValues:
result = newQVariant("")
proc modelsAboutToUpdate*(self: FlatTokensModel) =
self.beginResetModel()
proc modelsUpdated*(self: FlatTokensModel) =
self.endResetModel()

@ -1,3 +1,17 @@
import app_service/service/token/service_items
type
SourcesOfTokensModelDataSource* = tuple[
getSourcesOfTokensList: proc(): var seq[SupportedSourcesItem]
]
type
FlatTokenModelDataSource* = tuple[
getFlatTokensList: proc(): var seq[TokenItem]
]
type
TokenBySymbolModelDataSource* = tuple[
getTokenBySymbolList: proc(): var seq[TokenBySymbolItem]
]
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for any input/interaction with this module.
@ -26,6 +40,15 @@ method fetchHistoricalBalanceForTokenAsJson*(self: AccessInterface, address: str
method tokenBalanceHistoryDataResolved*(self: AccessInterface, balanceHistoryJson: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getSourcesOfTokensModelDataSource*(self: AccessInterface): SourcesOfTokensModelDataSource {.base.} =
raise newException(ValueError, "No implementation available")
method getFlatTokenModelDataSource*(self: AccessInterface): FlatTokenModelDataSource {.base.} =
raise newException(ValueError, "No implementation available")
method getTokenBySymbolModelDataSource*(self: AccessInterface): TokenBySymbolModelDataSource {.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.

@ -39,6 +39,12 @@ method delete*(self: Module) =
method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("walletSectionAllTokens", newQVariant(self.view))
# Passing on the events for changes in model to abstract model
self.events.on(SIGNAL_TOKENS_LIST_ABOUT_TO_BE_UPDATED) do(e: Args):
self.view.modelsAboutToUpdate()
self.events.on(SIGNAL_TOKENS_LIST_UPDATED) do(e: Args):
self.view.modelsUpdated()
self.controller.init()
self.view.load()
@ -62,9 +68,34 @@ method getHistoricalDataForToken*(self: Module, symbol: string, currency: string
method tokenHistoricalDataResolved*(self: Module, tokenDetails: string) =
self.view.setTokenHistoricalDataReady(tokenDetails)
method fetchHistoricalBalanceForTokenAsJson*(self: Module, address: string, tokenSymbol: string, currencySymbol: string, timeIntervalEnum: int) =
self.controller.fetchHistoricalBalanceForTokenAsJson(address, tokenSymbol, currencySymbol,timeIntervalEnum)
method tokenBalanceHistoryDataResolved*(self: Module, balanceHistoryJson: string) =
self.view.setTokenBalanceHistoryDataReady(balanceHistoryJson)
method getFlatTokensList*(self: Module): var seq[TokenItem] =
return self.controller.getFlatTokensList()
method getTokenBySymbolList*(self: Module): var seq[TokenBySymbolItem] =
return self.controller.getTokenBySymbolList()
method getSourcesOfTokensList*(self: Module): var seq[SupportedSourcesItem] =
return self.controller.getSourcesOfTokensList()
# Interfaces for getting lists from the service files into the abstract models
method getSourcesOfTokensModelDataSource*(self: Module): SourcesOfTokensModelDataSource =
return (
getSourcesOfTokensList: proc(): var seq[SupportedSourcesItem] = self.getSourcesOfTokensList()
)
method getFlatTokenModelDataSource*(self: Module): FlatTokenModelDataSource =
return (
getFlatTokensList: proc(): var seq[TokenItem] = self.getFlatTokensList()
)
method getTokenBySymbolModelDataSource*(self: Module): TokenBySymbolModelDataSource =
return (
getTokenBySymbolList: proc(): var seq[TokenBySymbolItem] = self.getTokenBySymbolList()
)

@ -0,0 +1,76 @@
import NimQml, Tables
import ./io_interface
type
ModelRole {.pure.} = enum
# key = name
Key = UserRole + 1
Name
UpdatedAt
Source
Version
TokensCount
QtObject:
type SourcesOfTokensModel* = ref object of QAbstractListModel
delegate: io_interface.SourcesOfTokensModelDataSource
proc setup(self: SourcesOfTokensModel) =
self.QAbstractListModel.setup
proc delete(self: SourcesOfTokensModel) =
self.QAbstractListModel.delete
proc newSourcesOfTokensModel*(delegate: io_interface.SourcesOfTokensModelDataSource): SourcesOfTokensModel =
new(result, delete)
result.setup
result.delegate = delegate
method rowCount(self: SourcesOfTokensModel, index: QModelIndex = nil): int =
return self.delegate.getSourcesOfTokensList().len
proc countChanged(self: SourcesOfTokensModel) {.signal.}
proc getCount(self: SourcesOfTokensModel): int {.slot.} =
return self.rowCount()
QtProperty[int] count:
read = getCount
notify = countChanged
method roleNames(self: SourcesOfTokensModel): Table[int, string] =
{
ModelRole.Key.int:"key",
ModelRole.Name.int:"name",
ModelRole.UpdatedAt.int:"updatedAt",
ModelRole.Source.int:"source",
ModelRole.Version.int:"version",
ModelRole.TokensCount.int:"tokensCount",
}.toTable
method data(self: SourcesOfTokensModel, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.rowCount():
return
let item = self.delegate.getSourcesOfTokensList()[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.Key:
result = newQVariant(item.name)
of ModelRole.Name:
result = newQVariant(item.name)
of ModelRole.UpdatedAt:
result = newQVariant(item.updatedAt)
of ModelRole.Source:
result = newQVariant(item.source)
of ModelRole.Version:
result = newQVariant(item.version)
of ModelRole.TokensCount:
result = newQVariant(item.tokensCount)
proc modelsAboutToUpdate*(self: SourcesOfTokensModel) =
self.beginResetModel()
proc modelsUpdated*(self: SourcesOfTokensModel) =
self.endResetModel()

@ -0,0 +1,117 @@
import NimQml, Tables, strutils
import ./io_interface, ./address_per_chain_model
const SOURCES_DELIMITER = ";"
type
ModelRole {.pure.} = enum
# The key is "symbol" in case it is not a community token
# and in case of the community token it will be the token "address"
Key = UserRole + 1
Name
Symbol
# uniswap/status/custom seq[string]
# returned as a string as nim doesnt support returning a StringList
# using join api and semicolon (;) as a delimiter
Sources
AddressPerChain
Decimals
Image
# Native, Erc20, Erc721
Type
# only be valid if source is custom
CommunityId
# everything below should be lazy loaded
Description
# properties below this are optional and may not exist in case of community minted assets
# built from chainId and address using networks service
WebsiteUrl
MarketValues
QtObject:
type TokensBySymbolModel* = ref object of QAbstractListModel
delegate: io_interface.TokenBySymbolModelDataSource
addressPerChainModel: seq[AddressPerChainModel]
proc setup(self: TokensBySymbolModel) =
self.QAbstractListModel.setup
self.addressPerChainModel = @[]
proc delete(self: TokensBySymbolModel) =
self.QAbstractListModel.delete
proc newTokensBySymbolModel*(delegate: io_interface.TokenBySymbolModelDataSource): TokensBySymbolModel =
new(result, delete)
result.setup
result.delegate = delegate
method rowCount(self: TokensBySymbolModel, index: QModelIndex = nil): int =
return self.delegate.getTokenBySymbolList().len
proc countChanged(self: TokensBySymbolModel) {.signal.}
proc getCount(self: TokensBySymbolModel): int {.slot.} =
return self.rowCount()
QtProperty[int] count:
read = getCount
notify = countChanged
method roleNames(self: TokensBySymbolModel): Table[int, string] =
{
ModelRole.Key.int:"key",
ModelRole.Name.int:"name",
ModelRole.Symbol.int:"symbol",
ModelRole.Sources.int:"sources",
ModelRole.AddressPerChain.int:"addressPerChain",
ModelRole.Decimals.int:"decimals",
ModelRole.Image.int:"image",
ModelRole.Type.int:"type",
ModelRole.CommunityId.int:"communityId",
ModelRole.Description.int:"description",
ModelRole.WebsiteUrl.int:"websiteUrl",
ModelRole.MarketValues.int:"marketValues",
}.toTable
method data(self: TokensBySymbolModel, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.rowCount():
return
# the only way to read items from service is by this single method getTokenBySymbolList
let item = self.delegate.getTokenBySymbolList()[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.Key:
result = newQVariant(item.key)
of ModelRole.Name:
result = newQVariant(item.name)
of ModelRole.Symbol:
result = newQVariant(item.symbol)
of ModelRole.Sources:
result = newQVariant(SOURCES_DELIMITER & item.sources.join(SOURCES_DELIMITER) & SOURCES_DELIMITER)
of ModelRole.AddressPerChain:
result = newQVariant(self.addressPerChainModel[index.row])
of ModelRole.Decimals:
result = newQVariant(item.decimals)
of ModelRole.Image:
result = newQVariant(item.image)
of ModelRole.Type:
result = newQVariant(ord(item.`type`))
of ModelRole.CommunityId:
result = newQVariant(item.communityId)
# ToDo fetching of market values not done yet
of ModelRole.Description:
result = newQVariant("")
of ModelRole.WebsiteUrl:
result = newQVariant("")
of ModelRole.MarketValues:
result = newQVariant("")
proc modelsAboutToUpdate*(self: TokensBySymbolModel) =
self.beginResetModel()
proc modelsUpdated*(self: TokensBySymbolModel) =
self.addressPerChainModel = @[]
for index in countup(0, self.delegate.getTokenBySymbolList().len):
self.addressPerChainModel.add(newAddressPerChainModel(self.delegate,index))
self.endResetModel()

@ -1,6 +1,6 @@
import NimQml, sequtils, sugar, strutils
import ./io_interface
import ./io_interface, ./sources_of_tokens_model, ./flat_tokens_model, ./token_by_symbol_model
QtObject:
type
@ -8,6 +8,16 @@ QtObject:
delegate: io_interface.AccessInterface
marketHistoryIsLoading: bool
balanceHistoryIsLoading: bool
# This contains the different sources for the tokens list
# ex. uniswap list, status tokens list
sourcesOfTokensModel: SourcesOfTokensModel
# this list contains the complete list of tokens with separate
# entry per token which has a unique address + network pair */
flatTokensModel: FlatTokensModel
# this list contains list of tokens grouped by symbol
# EXCEPTION: We may have different entries for the same symbol in case
# of symbol clash when minting community tokens
tokensBySymbolModel: TokensBySymbolModel
proc delete*(self: View) =
self.QObject.delete
@ -18,36 +28,33 @@ QtObject:
result.delegate = delegate
result.marketHistoryIsLoading = false
result.balanceHistoryIsLoading = false
result.sourcesOfTokensModel = newSourcesOfTokensModel(delegate.getSourcesOfTokensModelDataSource())
result.flatTokensModel = newFlatTokensModel(delegate.getFlatTokenModelDataSource())
result.tokensBySymbolModel = newTokensBySymbolModel(delegate.getTokenBySymbolModelDataSource())
proc load*(self: View) =
self.delegate.viewDidLoad()
proc marketHistoryIsLoadingChanged*(self: View) {.signal.}
proc getMarketHistoryIsLoading(self: View): QVariant {.slot.} =
return newQVariant(self.marketHistoryIsLoading)
proc setMarketHistoryIsLoading(self: View, isLoading: bool) =
if self.marketHistoryIsLoading == isLoading:
return
self.marketHistoryIsLoading = isLoading
self.marketHistoryIsLoadingChanged()
QtProperty[QVariant] marketHistoryIsLoading:
read = getMarketHistoryIsLoading
notify = marketHistoryIsLoadingChanged
proc balanceHistoryIsLoadingChanged*(self: View) {.signal.}
proc getBalanceHistoryIsLoading(self: View): QVariant {.slot.} =
return newQVariant(self.balanceHistoryIsLoading)
proc setBalanceHistoryIsLoading(self: View, isLoading: bool) =
if self.balanceHistoryIsLoading == isLoading:
return
self.balanceHistoryIsLoading = isLoading
self.balanceHistoryIsLoadingChanged()
QtProperty[QVariant] balanceHistoryIsLoading:
read = getBalanceHistoryIsLoading
notify = balanceHistoryIsLoadingChanged
@ -74,3 +81,34 @@ QtObject:
proc setTokenBalanceHistoryDataReady*(self: View, balanceHistoryJson: string) =
self.setBalanceHistoryIsLoading(false)
self.tokenBalanceHistoryDataReady(balanceHistoryJson)
proc sourcesOfTokensModelChanged*(self: View) {.signal.}
proc getSourcesOfTokensModel(self: View): QVariant {.slot.} =
return newQVariant(self.sourcesOfTokensModel)
QtProperty[QVariant] sourcesOfTokensModel:
read = getSourcesOfTokensModel
notify = sourcesOfTokensModelChanged
proc flatTokensModelChanged*(self: View) {.signal.}
proc getFlatTokensModel(self: View): QVariant {.slot.} =
return newQVariant(self.flatTokensModel)
QtProperty[QVariant] flatTokensModel:
read = getFlatTokensModel
notify = flatTokensModelChanged
proc tokensBySymbolModelChanged*(self: View) {.signal.}
proc getTokensBySymbolModel(self: View): QVariant {.slot.} =
return newQVariant(self.tokensBySymbolModel)
QtProperty[QVariant] tokensBySymbolModel:
read = getTokensBySymbolModel
notify = tokensBySymbolModelChanged
proc modelsAboutToUpdate*(self: View) =
self.sourcesOfTokensModel.modelsAboutToUpdate()
self.flatTokensModel.modelsAboutToUpdate()
self.tokensBySymbolModel.modelsAboutToUpdate()
proc modelsUpdated*(self: View) =
self.sourcesOfTokensModel.modelsUpdated()
self.flatTokensModel.modelsUpdated()
self.tokensBySymbolModel.modelsUpdated()

@ -34,11 +34,11 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED) do(e: Args):
self.delegate.refreshNetworks()
proc getNetworks*(self: Controller): seq[NetworkDto] =
return self.networkService.getNetworks()
proc getFlatNetworks*(self: Controller): seq[NetworkDto] =
return self.networkService.getFlatNetworks()
proc setNetworksState*(self: Controller, chainIds: seq[int], enabled: bool) =
self.walletAccountService.setNetworksState(chainIds, enabled)
proc areTestNetworksEnabled*(self: Controller): bool =
return self.settingsService.areTestNetworksEnabled()
return self.settingsService.areTestNetworksEnabled()

@ -1,5 +1,6 @@
import NimQml, Tables, strutils, strformat, sequtils, sugar
import app_service/service/network/types
import ./item
const EXPLORER_TX_PREFIX* = "/tx/"
@ -193,7 +194,7 @@ QtObject:
proc getLayer1Network*(self: Model, testNet: bool): int =
for item in self.items:
if item.getLayer() == 1 and item.getIsTest() == testNet:
if item.getLayer() == NETWORK_LAYER_1 and item.getIsTest() == testNet:
return item.getChainId()
return 0

@ -40,12 +40,12 @@ method delete*(self: Module) =
method refreshNetworks*(self: Module) =
self.view.setAreTestNetworksEnabled(self.controller.areTestNetworksEnabled())
self.view.setItems(self.controller.getNetworks())
self.view.setItems(self.controller.getFlatNetworks())
method load*(self: Module) =
self.controller.init()
self.view.setAreTestNetworksEnabled(self.controller.areTestNetworksEnabled())
self.view.load(self.controller.getNetworks())
self.view.load()
self.refreshNetworks()
method isLoaded*(self: Module): bool =
return self.moduleLoaded
@ -64,4 +64,4 @@ method setNetworksState*(self: Module, chainIds: seq[int], enabled: bool) =
self.controller.setNetworksState(chainIds, enabled)
method getNetworkLayer*(self: Module, chainId: int): string =
return self.view.getNetworkLayer(chainId)
return self.view.getNetworkLayer(chainId)

@ -1,7 +1,6 @@
import Tables, NimQml, sequtils, sugar
import ../../../../../app_service/service/network/dto
import app_service/service/network/[dto, types]
import ./io_interface
import ./model
import ./item
@ -17,6 +16,7 @@ QtObject:
enabled: Model
layer1: Model
layer2: Model
flatNetworks: Model
areTestNetworksEnabled: bool
proc setup(self: View) =
@ -32,6 +32,7 @@ QtObject:
result.layer1 = newModel()
result.layer2 = newModel()
result.enabled = newModel()
result.flatNetworks = newModel()
result.setup()
proc areTestNetworksEnabledChanged*(self: View) {.signal.}
@ -74,6 +75,14 @@ QtObject:
read = getLayer2
notify = layer2Changed
proc flatNetworksChanged*(self: View) {.signal.}
proc getFlatNetworks(self: View): QVariant {.slot.} =
return newQVariant(self.flatNetworks)
QtProperty[QVariant] flatNetworks:
read = getFlatNetworks
notify = flatNetworksChanged
proc enabledChanged*(self: View) {.signal.}
proc getEnabled(self: View): QVariant {.slot.} =
@ -105,18 +114,19 @@ QtObject:
networkEnabledToUxEnabledState(n.enabled, allEnabled)
))
self.all.setItems(items)
self.layer1.setItems(items.filter(i => i.getLayer() == 1))
self.layer2.setItems(items.filter(i => i.getLayer() == 2))
self.enabled.setItems(items.filter(i => i.getIsEnabled()))
let filteredItems = items.filter(i => i.getIsTest() == self.areTestNetworksEnabled)
self.flatNetworks.setItems(items)
self.all.setItems(filteredItems)
self.layer1.setItems(filteredItems.filter(i => i.getLayer() == NETWORK_LAYER_1))
self.layer2.setItems(filteredItems.filter(i => i.getLayer() == NETWORK_LAYER_2))
self.enabled.setItems(filteredItems.filter(i => i.getIsEnabled()))
self.allChanged()
self.layer1Changed()
self.layer2Changed()
self.enabledChanged()
proc load*(self: View, networks: seq[NetworkDto]) =
self.setItems(networks)
proc load*(self: View) =
self.delegate.viewDidLoad()
proc toggleNetwork*(self: View, chainId: int) {.slot.} =

@ -1,5 +1,6 @@
import NimQml, Tables, strutils, strformat, sequtils, sugar, json, stint
import app_service/service/network/types
import app/modules/shared_models/currency_amount
import ./network_item, ./suggested_route_item
@ -322,6 +323,6 @@ QtObject:
proc getLayer1Network*(self: NetworkModel): int =
for item in self.items:
if item.getLayer() == 1:
if item.getLayer() == NETWORK_LAYER_1:
return item.getChainId()
return 0

@ -82,4 +82,11 @@ type Shard* = object
proc initShard*(cluster: int = -1, index: int = -1): Shard =
result.cluster = cluster
result.index = index
result.index = index
# ToDo: Will be streamlined to single TokenType under https://github.com/status-im/status-desktop/pull/12654/files
type NewTokenType* {.pure.} = enum
Native = 0
ERC20 = 1,
ERC721 = 2,
ERC1155

@ -414,8 +414,8 @@ QtObject:
proc getSNTBalance*(self: Service): string =
let token = self.getStatusToken()
let account = self.walletAccountService.getWalletAccount(0).address
let balances = status_go_backend.getTokensBalancesForChainIDs(@[self.getChainId()], @[account], @[token.addressAsString()]).result
return ens_utils.hex2Token(balances{account}{token.addressAsString()}.getStr, token.decimals)
let balances = status_go_backend.getTokensBalancesForChainIDs(@[self.getChainId()], @[account], @[token.address]).result
return ens_utils.hex2Token(balances{account}{token.address}.getStr, token.decimals)
proc resourceUrl*(self: Service, username: string): (string, string, string) =
try:

@ -419,11 +419,11 @@ QtObject:
proc getTransactionDetails*(self: Service, message: MessageDto): (string, string) =
let networksDto = self.networkService.getNetworks()
var token = newTokenDto(networksDto[0].nativeCurrencyName, networksDto[0].chainId, parseAddress(ZERO_ADDRESS), networksDto[0].nativeCurrencySymbol, networksDto[0].nativeCurrencyDecimals, true)
var token = self.tokenService.findTokenByAddress(networksDto[0].chainId, ZERO_ADDRESS)
if message.transactionParameters.contract != "":
for networkDto in networksDto:
let tokenFound = self.tokenService.findTokenByAddress(networkDto, parseAddress(message.transactionParameters.contract))
let tokenFound = self.tokenService.findTokenByAddress(networkDto.chainId, message.transactionParameters.contract)
if tokenFound == nil:
continue

@ -58,6 +58,14 @@ proc resetNetworks*(self: Service) =
proc getCombinedNetworks*(self: Service): seq[CombinedNetworkDto] =
return self.fetchNetworks()
# TODO:: update the networks service to unify the model exposed from this service
# We currently have 3 types: combined, test/mainet and flat and probably can be optimized
# follow up task https://github.com/status-im/status-desktop/issues/12717
proc getFlatNetworks*(self: Service): seq[NetworkDto] =
for network in self.fetchNetworks():
result.add(network.test)
result.add(network.prod)
proc getNetworks*(self: Service): seq[NetworkDto] =
let testNetworksEnabled = self.settingsService.areTestNetworksEnabled()
@ -67,6 +75,11 @@ proc getNetworks*(self: Service): seq[NetworkDto] =
else:
result.add(network.prod)
proc getAllNetworkChainIds*(self: Service): seq[int] =
for network in self.fetchNetworks():
result.add(network.test.chainId)
result.add(network.prod.chainId)
proc upsertNetwork*(self: Service, network: NetworkDto): bool =
let response = backend.addEthereumChain(backend.Network(
chainId: network.chainId,

@ -7,7 +7,11 @@ const Optimism = 10
const Poa = 99
const XDai = 100
const NETWORK_LAYER_1 = 1
const NETWORK_LAYER_2 = 2
export Mainnet, Ropsten, Rinkeby, Goerli, Optimism, Poa, XDai, Sepolia
export NETWORK_LAYER_1, NETWORK_LAYER_2
type
NetworkType* {.pure.} = enum
@ -48,4 +52,4 @@ proc toChainId*(self: NetworkType): int =
of NetworkType.Sepolia: result = Sepolia
of NetworkType.XDai: result = XDai
of NetworkType.Poa: result = 99
of NetworkType.Other: result = -1
of NetworkType.Other: result = -1

@ -451,5 +451,5 @@ QtObject:
let account = self.walletAccountService.getWalletAccount(0).address
let network = self.networkService.getNetworkForStickers()
let balances = status_go_backend.getTokensBalancesForChainIDs(@[network.chainId], @[account], @[token.addressAsString()]).result
return ens_utils.hex2Token(balances{account}{token.addressAsString()}.getStr, token.decimals)
let balances = status_go_backend.getTokensBalancesForChainIDs(@[network.chainId], @[account], @[token.address]).result
return ens_utils.hex2Token(balances{account}{token.address}.getStr, token.decimals)

@ -1,8 +1,7 @@
import times
include ../../common/json_utils
import backend/backend as backend
import ../../../backend/backend as backend
import ./dto
include app_service/common/json_utils
#################################################
# Async load transactions
#################################################
@ -10,6 +9,26 @@ import ./dto
const DAYS_IN_WEEK = 7
const HOURS_IN_DAY = 24
type
GetTokenListTaskArg = ref object of QObjectTaskArg
const getSupportedTokenList*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[GetTokenListTaskArg](argEncoded)
var response: RpcResponse[JsonNode]
try:
response = backend.getTokenList()
let output = %* {
"supportedTokensJson": response,
"error": ""
}
arg.finish(output)
except Exception as e:
let output = %* {
"supportedTokensJson": response,
"error": e.msg
}
arg.finish(output)
type
GetTokenHistoricalDataTaskArg = ref object of QObjectTaskArg
symbol: string
@ -98,4 +117,4 @@ const getTokenBalanceHistoryDataTask*: Task = proc(argEncoded: string) {.gcsafe,
"timeInterval": int(arg.timeInterval),
"error": e.msg,
}
arg.finish(output)
arg.finish(output)

@ -1,64 +1,67 @@
import json
import json, strformat
include ../../common/json_utils
include app_service/common/json_utils
import
web3/ethtypes, json_serialization
from web3/conversions import `$`
import json_serialization
# TODO: remove once this is moved to wallet_accounts service
const WEEKLY_TIME_RANGE* = 0
const MONTHLY_TIME_RANGE* = 1
const HALF_YEARLY_TIME_RANGE* = 2
const YEARLY_TIME_RANGE* = 3
const ALL_TIME_RANGE* = 4
# Only contains DTO used for deserialisation of data from go lib
type
TokenDto* = ref object of RootObj
name*: string
chainId*: int
address*: Address
symbol*: string
decimals*: int
hasIcon* {.dontSerialize.}: bool
color*: string
isCustom* {.dontSerialize.}: bool
isVisible* {.dontSerialize.}: bool
communityId*: string
address* {.serializedFieldName("address").}: string
name* {.serializedFieldName("name").}: string
symbol* {.serializedFieldName("symbol").}: string
decimals* {.serializedFieldName("decimals").}: int
chainID* {.serializedFieldName("chainId").}: int
communityID* {.serializedFieldName("communityId").}: string
proc `$`*(self: TokenDto): string =
result = fmt"""TokenDto[
address: {self.address},
name: {self.name},
symbol: {self.symbol},
decimals: {self.decimals},
chainID: {self.chainID},
communityID: {self.communityID}
]"""
# TODO: Remove after https://github.com/status-im/status-desktop/issues/12513
proc newTokenDto*(
address: string,
name: string,
chainId: int,
address: Address,
symbol: string,
decimals: int,
hasIcon: bool,
isCustom: bool = false,
chainId: int,
communityId: string = ""
): TokenDto =
return TokenDto(
name: name,
chainId: chainId,
address: address,
name: name,
symbol: symbol,
decimals: decimals,
hasIcon: hasIcon,
communityId: communityId,
isCustom: isCustom
chainId: chainId,
communityId: communityId
)
proc toTokenDto*(jsonObj: JsonNode, isVisible: bool, hasIcon: bool = false, isCustom: bool = true): TokenDto =
result = TokenDto()
result.isCustom = isCustom
result.hasIcon = hasIcon
type TokenSourceDto* = ref object of RootObj
name* {.serializedFieldName("name").}: string
tokens* {.serializedFieldName("tokens").}: seq[TokenDto]
updatedAt* {.serializedFieldName("updatedAt").}: int64
source* {.serializedFieldName("source").}: string
version* {.serializedFieldName("version").}: string
discard jsonObj.getProp("name", result.name)
discard jsonObj.getProp("chainId", result.chainId)
discard jsonObj.getProp("address", result.address)
discard jsonObj.getProp("symbol", result.symbol)
discard jsonObj.getProp("decimals", result.decimals)
discard jsonObj.getProp("color", result.color)
discard jsonObj.getProp("communityId", result.communityId)
result.isVisible = isVisible
proc addressAsString*(self: TokenDto): string =
return $self.address
proc `$`*(self: TokenSourceDto): string =
result = fmt"""TokenSourceDto[
name: {self.name},
tokens: {self.tokens},
updatedAt: {self.updatedAt},
source: {self.source},
version: {self.version}
]"""

@ -1,20 +1,19 @@
import NimQml, Tables, json, sequtils, chronicles, strutils
import NimQml, Tables, json, sequtils, chronicles, strutils, strformat, algorithm
import web3/ethtypes
from web3/conversions import `$`
import ../../../backend/backend as backend
import backend/backend as backend
import ../network/service as network_service
import ../wallet_account/dto/account_dto as wallet_account_dto
import ../../../app/global/global_singleton
import app_service/service/network/service as network_service
import app_service/service/wallet_account/dto/account_dto
import ../../../app/core/eventemitter
import ../../../app/core/tasks/[qt, threadpool]
import ../../common/cache
import app/core/eventemitter
import app/core/tasks/[qt, threadpool]
import app_service/common/cache
import ../../../constants as main_constants
import ./dto
import ./dto, ./service_items
export dto
export dto, service_items
logScope:
topics = "token-service"
@ -32,6 +31,8 @@ const CRYPTO_SUB_UNITS_TO_FACTOR = {
# Signals which may be emitted by this service:
const SIGNAL_TOKEN_HISTORICAL_DATA_LOADED* = "tokenHistoricalDataLoaded"
const SIGNAL_BALANCE_HISTORY_DATA_READY* = "tokenBalanceHistoryDataReady"
const SIGNAL_TOKENS_LIST_UPDATED* = "tokensListUpdated"
const SIGNAL_TOKENS_LIST_ABOUT_TO_BE_UPDATED* = "tokensListAboutToBeUpdated"
type
TokenHistoricalDataArgs* = ref object of Args
@ -51,11 +52,22 @@ QtObject:
events: EventEmitter
threadpool: ThreadPool
networkService: network_service.Service
# TODO: remove these once community usage of this service is removed etc...
tokens: Table[int, seq[TokenDto]]
tokenList: seq[TokenDto]
tokensToAddressesMap: Table[string, TokenData]
priceCache: TimedCache[float64]
sourcesOfTokensList: seq[SupportedSourcesItem]
flatTokenList: seq[TokenItem]
tokenBySymbolList: seq[TokenBySymbolItem]
# TODO: Table[symbol, TokenDetails] fetched from cryptocompare
# will be done under https://github.com/status-im/status-desktop/issues/12668
tokenDetailsList: Table[string, TokenDetails]
proc updateCachedTokenPrice(self: Service, crypto: string, fiat: string, price: float64)
proc jsonToPricesMap(node: JsonNode): Table[string, Table[string, float64]]
@ -77,10 +89,106 @@ QtObject:
result.tokenList = @[]
result.tokensToAddressesMap = initTable[string, TokenData]()
proc loadData*(self: Service) =
if(not main_constants.WALLET_ENABLED):
return
result.sourcesOfTokensList = @[]
result.flatTokenList = @[]
result.tokenBySymbolList = @[]
result.tokenDetailsList = initTable[string, TokenDetails]()
# Callback to process the response of getSupportedTokensList call
proc supportedTokensListRetrieved(self: Service, response: string) {.slot.} =
# this is emited so that the models can know that the seq it depends on has been updated
defer: self.events.emit(SIGNAL_TOKENS_LIST_UPDATED, Args())
let parsedJson = response.parseJson
var errorString: string
var supportedTokensJson, tokensResult: JsonNode
discard parsedJson.getProp("supportedTokensJson", supportedTokensJson)
discard parsedJson.getProp("supportedTokensJson", errorString)
discard supportedTokensJson.getProp("result", tokensResult)
if not errorString.isEmptyOrWhitespace:
raise newException(Exception, "Error getting supported tokens list: " & errorString)
let sourcesList = if tokensResult.isNil or tokensResult.kind == JNull: @[]
else: Json.decode($tokensResult, seq[TokenSourceDto], allowUnknownFields = true)
let supportedNetworkChains = self.networkService.getAllNetworkChainIds()
var flatTokensList: Table[string, TokenItem] = initTable[string, TokenItem]()
var tokenBySymbolList: Table[string, TokenBySymbolItem] = initTable[string, TokenBySymbolItem]()
for s in sourcesList:
let newSource = SupportedSourcesItem(name: s.name, updatedAt: s.updatedAt, source: s.source, version: s.version, tokensCount: s.tokens.len)
self.sourcesOfTokensList.add(newSource)
for token in s.tokens:
# Remove tokens that are not on list of supported status networks
if supportedNetworkChains.contains(token.chainID):
# logic for building flat tokens list
let unique_key = $token.chainID & token.address
if flatTokensList.hasKey(unique_key):
flatTokensList[unique_key].sources.add(s.name)
else:
let tokenType = if s.name == "native" : NewTokenType.Native
else: NewTokenType.ERC20
flatTokensList[unique_key] = TokenItem(
key: unique_key,
name: token.name,
symbol: token.symbol,
sources: @[s.name],
chainID: token.chainID,
address: token.address,
decimals: token.decimals,
image: "",
`type`: tokenType,
communityId: token.communityID)
# logic for building tokens by symbol list
# In case the token is not a community token the unique key is symbol
# In case this is a community token the only param reliably unique is its address
# as there is always a rare case that a user can create two or more community token
# with same symbol and cannot be avoided
let token_by_symbol_key = if token.communityID.isEmptyOrWhitespace: token.symbol
else: token.address
if tokenBySymbolList.hasKey(token_by_symbol_key):
if not tokenBySymbolList[token_by_symbol_key].sources.contains(s.name):
tokenBySymbolList[token_by_symbol_key].sources.add(s.name)
# this logic is to check if an entry for same chainId as been made already,
# in that case we simply add it to address per chain
var addedChains: seq[int] = @[]
for addressPerChain in tokenBySymbolList[token_by_symbol_key].addressPerChainId:
addedChains.add(addressPerChain.chainId)
if not addedChains.contains(token.chainID):
tokenBySymbolList[token_by_symbol_key].addressPerChainId.add(AddressPerChain(chainId: token.chainID, address: token.address))
else:
let tokenType = if s.name == "native": NewTokenType.Native
else: NewTokenType.ERC20
tokenBySymbolList[token_by_symbol_key] = TokenBySymbolItem(
key: token_by_symbol_key,
name: token.name,
symbol: token.symbol,
sources: @[s.name],
addressPerChainId: @[AddressPerChain(chainId: token.chainID, address: token.address)],
decimals: token.decimals,
image: "",
`type`: tokenType,
communityId: token.communityID)
self.flatTokenList = toSeq(flatTokensList.values)
self.flatTokenList.sort(cmpTokenItem)
self.tokenBySymbolList = toSeq(tokenBySymbolList.values)
self.tokenBySymbolList.sort(cmpTokenBySymbolItem)
proc getSupportedTokensList(self: Service) =
# this is emited so that the models can know that an update is about to happen
self.events.emit(SIGNAL_TOKENS_LIST_ABOUT_TO_BE_UPDATED, Args())
let arg = GetTokenListTaskArg(
tptr: cast[ByteAddress](getSupportedTokenList),
vptr: cast[ByteAddress](self.vptr),
slot: "supportedTokensListRetrieved",
)
self.threadpool.start(arg)
# TODO: Remove after https://github.com/status-im/status-desktop/issues/12513
proc loadData*(self: Service) =
try:
let networks = self.networkService.getNetworks()
@ -95,21 +203,18 @@ QtObject:
if found:
continue
let responseTokens = backend.getTokens(network.chainId)
let default_tokens = map(
responseTokens.result.getElems(),
proc(x: JsonNode): TokenDto = x.toTokenDto(network.enabled, hasIcon=true, isCustom=false)
)
let default_tokens = Json.decode($responseTokens.result, seq[TokenDto], allowUnknownFields = true)
self.tokens[network.chainId] = default_tokens.filter(
proc(x: TokenDto): bool = x.chainId == network.chainId
)
let nativeToken = newTokenDto(
address = "0x0000000000000000000000000000000000000000",
name = network.nativeCurrencyName,
chainId = network.chainId,
address = Address.fromHex("0x0000000000000000000000000000000000000000"),
symbol = network.nativeCurrencySymbol,
decimals = network.nativeCurrencyDecimals,
hasIcon = false
chainId = network.chainId,
communityID = ""
)
if not self.tokensToAddressesMap.hasKey(network.nativeCurrencySymbol):
@ -137,8 +242,23 @@ QtObject:
error "Tokens init error", errDesription = e.msg
proc init*(self: Service) =
if(not main_constants.WALLET_ENABLED):
return
self.loadData()
self.getSupportedTokensList()
# ToDo: on self.events.on(SignalType.Message.event) do(e: Args):
# update and populate internal list and then emit signal
proc getSourcesOfTokensList*(self: Service): var seq[SupportedSourcesItem] =
return self.sourcesOfTokensList
proc getFlatTokensList*(self: Service): var seq[TokenItem] =
return self.flatTokenList
proc getTokenBySymbolList*(self: Service): var seq[TokenBySymbolItem] =
return self.tokenBySymbolList
# TODO: Remove after https://github.com/status-im/status-desktop/issues/12513
proc getTokenList*(self: Service): seq[TokenDto] =
return self.tokenList
@ -160,10 +280,11 @@ QtObject:
if token.symbol == symbol:
return token
proc findTokenByAddress*(self: Service, network: NetworkDto, address: Address): TokenDto =
if not self.tokens.hasKey(network.chainId):
# TODO: Shouldnt be needed after accounts assets are restructured
proc findTokenByAddress*(self: Service, networkChainId: int, address: string): TokenDto =
if not self.tokens.hasKey(networkChainId):
return
for token in self.tokens[network.chainId]:
for token in self.tokens[networkChainId]:
if token.address == address:
return token
@ -179,7 +300,7 @@ QtObject:
for _, tokens in self.tokens:
for token in tokens:
if token.address == hexAddressValue:
if token.address == $hexAddressValue:
return token.symbol
return ""
@ -264,7 +385,9 @@ QtObject:
)
self.threadpool.start(arg)
# Callback to process the response of fetchHistoricalBalanceForTokenAsJson call
# TODO: The below two APIS are not linked with generic tokens list but with assets per account and should perhaps be moved to
# wallet_account->token_service.nim and clean up rest of the code too. Callback to process the response of
# fetchHistoricalBalanceForTokenAsJson call
proc tokenBalanceHistoryDataResolved*(self: Service, response: string) {.slot.} =
let responseObj = response.parseJson
if (responseObj.kind != JObject):
@ -291,7 +414,7 @@ QtObject:
chainIds.add(network.chainId)
if chainIds.len == 0:
error "faild to find a network with the symbol", tokenSymbol
error "failed to find a network with the symbol", tokenSymbol
return
let arg = GetTokenBalanceHistoryDataTaskArg(

@ -0,0 +1,93 @@
import strformat, Tables
import app_service/common/types as common_types
#TODO: remove dependency
import ../wallet_account/dto/token_dto
# This file holds the data types used by models internally
type SupportedSourcesItem* = ref object of RootObj
name*: string
updatedAt* : int64
source*: string
version*: string
# Needed to show upfront on ui count of tokens on each list
tokensCount*: int
proc `$`*(self: SupportedSourcesItem): string =
result = fmt"""SupportedSourcesItem[
name: {self.name},
updatedAt: {self.updatedAt},
source: {self.source},
version: {self.version},
tokensCount: {self.tokensCount}
]"""
type
TokenItem* = ref object of RootObj
# key is created using chainId and Address
key*: string
name*: string
symbol*: string
# uniswap/status/custom seq[string]
sources*: seq[string]
chainID*: int
address*: string
decimals*: int
# will remain empty until backend provides us this data
image*: string
`type`*: common_types.NewTokenType
communityId*: string
proc `$`*(self: TokenItem): string =
result = fmt"""TokenItem[
key: {self.key},
name: {self.name},
symbol: {self.symbol},
sources: {self.sources},
chainID: {self.chainID},
address: {self.address},
decimals: {self.decimals},
image: {self.image},
`type`: {self.`type`},
communityId: {self.communityId}
]"""
type AddressPerChain* = ref object of RootObj
chainId*: int
address*: string
proc `$`*(self: AddressPerChain): string =
result = fmt"""AddressPerChain[
chainId: {self.chainId},
address: {self.address}
]"""
type
TokenBySymbolItem* = ref object of TokenItem
addressPerChainId*: seq[AddressPerChain]
proc `$`*(self: TokenBySymbolItem): string =
result = fmt"""TokenBySymbolItem[
key: {self.key},
name: {self.name},
symbol: {self.symbol},
sources: {self.sources},
addressPerChainId: {self.addressPerChainId},
decimals: {self.decimals},
image: {self.image},
`type`: {self.`type`},
communityId: {self.communityId}
]"""
# In case of community tokens only the description will be available
type TokenDetails* = ref object of RootObj
description*: string
websiteUrl*: string
marketValues*: TokenMarketValuesDto
proc cmpTokenItem*(x, y: TokenItem): int =
cmp(x.name, y.name)
proc cmpTokenBySymbolItem*(x, y: TokenBySymbolItem): int =
cmp(x.name, y.name)

@ -341,7 +341,7 @@ QtObject:
else:
let network = self.networkService.getNetwork(chainID)
let token = self.tokenService.findTokenBySymbol(network.chainId, tokenSym)
toAddress = token.address
toAddress = parseAddress(token.address)
let transfer = Transfer(
to: parseAddress(to_addr),

@ -95,6 +95,9 @@ rpc(checkConnected, "wallet"):
rpc(getTokens, "wallet"):
chainId: int
rpc(getTokenList, "wallet"):
discard
rpc(getTokensBalancesForChainIDs, "wallet"):
chainIds: seq[int]
accounts: seq[string]

@ -31,10 +31,6 @@ SplitView {
ExpressionRole {
name: "explorerUrl"
expression: { return "https://status.im/" }
},
ExpressionRole {
name: "jsArraySources"
expression: model.sources.split(";")
}
]
}

@ -29,10 +29,6 @@ SplitView {
ExpressionRole {
name: "explorerUrl"
expression: { return "https://status.im/" }
},
ExpressionRole {
name: "jsArraySources"
expression: model.sources.split(";")
}
]
}
@ -89,8 +85,9 @@ SplitView {
sourceModel: root.tokensProxyModel
// Filter by source
filters: ExpressionFilter {
expression: model.jsArraySources.includes(keyFilter.value)
filters: RegExpFilter {
roleName: "sources"
pattern: "\;" + keyFilter.value + "\;"
}
}

@ -13,7 +13,7 @@ ListModel {
key: "0",
name: "Unisocks",
symbol: "SOCKS",
sources: uniswap + ";" + status,
sources: ";" + uniswap + ";" + status + ";",
chainId: NetworksModel.ethNet,
address: "0x0000000000000000000000000000000000000123",
decimals: "18",
@ -27,7 +27,7 @@ ListModel {
key: "1",
name: "Unisocks",
symbol: "SOCKS",
sources: uniswap + ";" + status,
sources: ";" + uniswap + ";" + status + ";",
chainId: NetworksModel.optimismNet,
address: "0x00000000000000000000000000000000000ade21",
decimals: "18",
@ -41,7 +41,7 @@ ListModel {
key: "2",
name: "Ox",
symbol: "ZRX",
sources: uniswap + ";" + status,
sources: ";" + uniswap + ";" + status + ";",
chainId: NetworksModel.ethNet,
address: "0x1230000000000000000000000000000000000123",
decimals: "18",
@ -55,7 +55,7 @@ ListModel {
key: "3",
name: "1inch",
symbol: "1INCH",
sources: uniswap + ";" + status,
sources: ";" + uniswap + ";" + status + ";",
chainId: NetworksModel.ethNet,
address: "0x4321000000000000000000000000000000000123",
decimals: "18",
@ -69,7 +69,7 @@ ListModel {
key: "4",
name: "Aave",
symbol: "AAVE",
sources: uniswap + ";" + status,
sources: ";" + uniswap + ";" + status + ";",
chainId: NetworksModel.arbitrumNet,
address: "0x6543000000000000000000000000000000000123",
decimals: "18",
@ -83,7 +83,7 @@ ListModel {
key: "5",
name: "Amp",
symbol: "AMP",
sources: uniswap,
sources: ";" + uniswap + ";",
chainId: NetworksModel.arbitrumNet,
address: "0x6543700000000000000000000000000000000123",
decimals: "18",
@ -97,7 +97,7 @@ ListModel {
key: "6",
name: "Dai",
symbol: "DAI",
sources: uniswap,
sources: ";" + uniswap + ";",
chainId: NetworksModel.optimismNet,
address: "0xabc2000000000000000000000000000000000123",
decimals: "18",
@ -111,7 +111,7 @@ ListModel {
key: "7",
name: "snt",
symbol: "SNT",
sources: status,
sources: ";" + status + ";",
chainId: NetworksModel.optimismNet,
address: "0xbbc2000000000000000000000000000000000123",
decimals: "18",
@ -125,7 +125,7 @@ ListModel {
key: "8",
name: "snt",
symbol: "SNT",
sources: status,
sources: ";" + status + ";",
chainId: NetworksModel.ethNet,
address: "0xbbc200000000000000000000000000000000abcd",
decimals: "18",

@ -94,8 +94,9 @@ StatusListView {
sourceModel: root.tokensListModel
// Filter by source
filters: ExpressionFilter {
expression: (model.jsArraySources).includes(keyFilter.value)
filters: RegExpFilter {
roleName: "sources"
pattern: "\;" + keyFilter.value + "\;"
}
}

@ -22,7 +22,7 @@ StatusDialog {
required property int sourceUpdatedAt
required property string sourceVersion
required property int tokensCount
required property var tokensListModel // Expected roles: name, symbol, image, chainName, explorerUrl
required property var tokensListModel // Expected roles: name, symbol, image, chainName, explorerUrl, isTest
signal linkClicked(string link)
@ -45,7 +45,6 @@ StatusDialog {
bottomMargin: Style.current.padding
implicitHeight: contentHeight
model: root.tokensListModel
header: ColumnLayout {
spacing: 20
width: list.width
@ -60,6 +59,10 @@ StatusDialog {
CustomHeaderDelegate {}
}
delegate: CustomDelegate {}
/* This late binding has been added here because without it all
the items in the list get initialised before the popup is launched
creating a delay */
Component.onCompleted: model = Qt.binding(() => root.tokensListModel)
}
header: StatusDialogHeader {
@ -102,6 +105,7 @@ StatusDialog {
Layout.fillWidth: true
text: textBlock.text
elide: Text.ElideRight
color: Theme.palette.baseColor1
}
}
@ -118,7 +122,7 @@ StatusDialog {
textColor: Theme.palette.baseColor1
textHoverColor: Theme.palette.directColor1
icon.name: "external-link"
onClicked: root.linkClicked(root.sourceUrl)
onClicked: root.linkClicked(link)
}
component CustomSourceInfoComponent: ColumnLayout {
@ -236,7 +240,7 @@ StatusDialog {
StatusBaseText {
Layout.fillWidth: true
text: model.chainName
text: model.chainName + (model.isTest? " " + qsTr("(Test)") : "")
elide: Text.ElideMiddle
color: Theme.palette.baseColor1
}

@ -9,13 +9,13 @@ QtObject {
id: root
/* PRIVATE: Modules used to get data from backend */
readonly property var _walletModule: walletSectionNewModule
readonly property var _allTokensModule: walletSectionAllTokens
readonly property var _networksModule: networksModule
/* This contains the different sources for the tokens list
ex. uniswap list, status tokens list */
readonly property var sourcesOfTokensModel: SortFilterProxyModel {
sourceModel: root._walletModule.sourcesOfTokensModel
sourceModel: root._allTokensModule.sourcesOfTokensModel
proxyRoles: ExpressionRole {
function sourceImage(sourceKey) {
return Constants.getSupportedTokenSourceImage(sourceKey)
@ -23,16 +23,26 @@ QtObject {
name: "image"
expression: sourceImage(model.key)
}
filters: AnyOf {
ValueFilter {
roleName: "key"
value: Constants.supportedTokenSources.uniswap
}
ValueFilter {
roleName: "key"
value: Constants.supportedTokenSources.status
}
}
}
/* This list contains the complete list of tokens with separate
entry per token which has a unique [address + network] pair */
readonly property var flatTokensModel: root._walletModule.flatTokensModel
readonly property var flatTokensModel: root._allTokensModule.flatTokensModel
/* PRIVATE: This model just combines tokens and network information in one */
readonly property LeftJoinModel _joinFlatTokensModel : LeftJoinModel {
leftModel: root.flatTokensModel
rightModel: root._networksModule.all
rightModel: root._networksModule.flatNetworks
joinRole: "chainId"
}
@ -46,7 +56,7 @@ QtObject {
proxyRoles: [
ExpressionRole {
name: "explorerUrl"
expression: { return model.blockExplorerURL + "/" + model.address } // TO REVIEW the correct composition!!
expression: model.blockExplorerURL + "/token/" + model.address
},
ExpressionRole {
function tokenIcon(symbol) {
@ -54,10 +64,6 @@ QtObject {
}
name: "image"
expression: tokenIcon(model.symbol)
},
ExpressionRole {
name: "jsArraySources"
expression: model.sources.split(";")
}
]
}
@ -68,7 +74,7 @@ QtObject {
there will be one entry per address + network pair */
// TODO in #12513
readonly property var tokensBySymbolModel: SortFilterProxyModel {
sourceModel: root._walletModule.tokensBySymbolModel
sourceModel: root._allTokensModule.tokensBySymbolModel
proxyRoles: [
ExpressionRole {
function tokenIcon(symbol) {

@ -789,8 +789,8 @@ QtObject {
}
readonly property QtObject supportedTokenSources: QtObject {
readonly property string uniswap: "uniswap"
readonly property string status: "status"
readonly property string uniswap: "Uniswap Labs Default Token List"
readonly property string status: "Status Token List"
readonly property string custom: "custom"
}

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit ecbacb0a7fd5e4e9ee15dc8a6aa4209a582c4162
Subproject commit 5381ec4a767750034722c6611686f69215110366