fix(@desktop/wallet): properly handle tokenID 0 when filtering activity by collectibles

Fixes #13660
This commit is contained in:
Dario Gabriel Lipicar 2024-02-22 15:48:43 -03:00 committed by dlipicar
parent a27e447f11
commit d22ac0dfed
11 changed files with 103 additions and 80 deletions

View File

@ -9,6 +9,7 @@ import collectibles_model
import collectibles_item import collectibles_item
import events_handler import events_handler
import status import status
import utils
import web3/conversions import web3/conversions
@ -36,9 +37,6 @@ const FETCH_BATCH_COUNT_DEFAULT = 10
const FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT = 2000 const FETCH_RECIPIENTS_BATCH_COUNT_DEFAULT = 2000
const FETCH_COLLECTIBLES_BATCH_COUNT_DEFAULT = 2000 const FETCH_COLLECTIBLES_BATCH_COUNT_DEFAULT = 2000
type
CollectiblesToTokenConverter* = proc (id: string): backend_activity.Token
QtObject: QtObject:
type type
Controller* = ref object of QObject Controller* = ref object of QObject
@ -65,8 +63,6 @@ QtObject:
requestId: int32 requestId: int32
collectiblesToTokenConverter: CollectiblesToTokenConverter
proc setup(self: Controller) = proc setup(self: Controller) =
self.QObject.setup self.QObject.setup
@ -311,8 +307,7 @@ QtObject:
currencyService: currency_service.Service, currencyService: currency_service.Service,
tokenService: token_service.Service, tokenService: token_service.Service,
savedAddressService: saved_address_service.Service, savedAddressService: saved_address_service.Service,
events: EventEmitter, events: EventEmitter): Controller =
collectiblesConverter: CollectiblesToTokenConverter): Controller =
new(result, delete) new(result, delete)
result.requestId = requestId result.requestId = requestId
@ -334,8 +329,6 @@ QtObject:
result.allAddressesSelected = false result.allAddressesSelected = false
result.chainIds = @[] result.chainIds = @[]
result.collectiblesToTokenConverter = collectiblesConverter
result.setup() result.setup()
result.setupEventHandlers(events) result.setupEventHandlers(events)
@ -373,8 +366,12 @@ QtObject:
var collectibles = newSeq[backend_activity.Token]() var collectibles = newSeq[backend_activity.Token]()
for i in 0 ..< collectiblesJson.len: for i in 0 ..< collectiblesJson.len:
let uid = collectiblesJson[i].getStr() let uid = collectiblesJson[i].getStr()
let token = self.collectiblesToTokenConverter(uid) # TODO: We need the token type here, which is not part of the uid.
collectibles.add(token) # We currently don't support filtering ERC1155 tokens anyway, so it's not an issue.
# When we have a split model for all collectibles metadata, get the entry from there
# to get the token type. Perhaps also add an "UnknownCollectible" TokenType that includes
# both ERC721 and ERC1155?
collectibles.add(collectibleUidToActivityToken(uid, TokenType.ERC721))
self.currentActivityFilter.collectibles = collectibles self.currentActivityFilter.collectibles = collectibles

View File

@ -0,0 +1,20 @@
import stint, chronicles
import backend/collectibles_types as backend_collectibles
import backend/activity as backend_activity
import app_service/common/types
import web3/ethtypes as eth
proc collectibleUidToActivityToken*(uid: string, tokenType: TokenType): backend_activity.Token =
try:
let id = uid.toCollectibleUniqueID()
result.tokenType = tokenType
result.chainId = backend_activity.ChainId(id.contractID.chainID)
let contractAddress = id.contractID.address
if len(contractAddress) > 0:
var address: eth.Address
address = eth.fromHex(eth.Address, contractAddress)
result.address = some(address)
result.tokenId = some(backend_activity.TokenId("0x" & stint.toHex(id.tokenId)))
except:
error "Invalid collectible uid: ", uid

View File

@ -129,7 +129,7 @@ proc newModule*(
result.assetsModule = assets_module.newModule(result, events, walletAccountService, networkService, tokenService, result.assetsModule = assets_module.newModule(result, events, walletAccountService, networkService, tokenService,
currencyService) currencyService)
result.sendModule = send_module.newModule(result, events, walletAccountService, networkService, currencyService, result.sendModule = send_module.newModule(result, events, walletAccountService, networkService, currencyService,
transactionService, keycardService) transactionService, keycardService)
result.savedAddressesModule = saved_addresses_module.newModule(result, events, savedAddressService) result.savedAddressesModule = saved_addresses_module.newModule(result, events, savedAddressService)
result.buySellCryptoModule = buy_sell_crypto_module.newModule(result, events, transactionService) result.buySellCryptoModule = buy_sell_crypto_module.newModule(result, events, transactionService)
result.overviewModule = overview_module.newModule(result, events, walletAccountService, currencyService) result.overviewModule = overview_module.newModule(result, events, walletAccountService, currencyService)
@ -137,12 +137,10 @@ proc newModule*(
result.networksService = networkService result.networksService = networkService
result.transactionService = transactionService result.transactionService = transactionService
let collectiblesToTokenConverter = proc(id: string): backend_activity.Token =
return allCollectiblesModule.getAllCollectiblesModel().getActivityToken(id)
result.activityController = activityc.newController(int32(ActivityID.History), currencyService, tokenService, result.activityController = activityc.newController(int32(ActivityID.History), currencyService, tokenService,
savedAddressService, events, collectiblesToTokenConverter) savedAddressService, events)
result.tmpActivityController = activityc.newController(int32(ActivityID.Temporary), currencyService, tokenService, result.tmpActivityController = activityc.newController(int32(ActivityID.Temporary), currencyService, tokenService,
savedAddressService, events, collectiblesToTokenConverter) savedAddressService, events)
result.collectibleDetailsController = collectible_detailsc.newController(int32(backend_collectibles.CollectiblesRequestID.WalletAccount), networkService, events) result.collectibleDetailsController = collectible_detailsc.newController(int32(backend_collectibles.CollectiblesRequestID.WalletAccount), networkService, events)
result.filter = initFilter(result.controller) result.filter = initFilter(result.controller)

View File

@ -59,9 +59,6 @@ QtObject:
generatedCollectionId:{self.generatedCollectionId} generatedCollectionId:{self.generatedCollectionId}
)""" )"""
proc getCollectibleUniqueID*(self: CollectiblesEntry): backend.CollectibleUniqueID =
return self.id
proc hasCollectibleData(self: CollectiblesEntry): bool = proc hasCollectibleData(self: CollectiblesEntry): bool =
return self.data != nil and isSome(self.data.collectibleData) return self.data != nil and isSome(self.data.collectibleData)
@ -108,18 +105,18 @@ QtObject:
read = getTokenIDAsString read = getTokenIDAsString
# Unique ID to identify collectible, generated by us # Unique ID to identify collectible, generated by us
proc getID*(self: CollectiblesEntry): string = proc getID*(self: CollectiblesEntry): backend.CollectibleUniqueID =
return self.id
proc getIDAsString*(self: CollectiblesEntry): string =
return self.generatedId return self.generatedId
proc generateId*(self: CollectiblesEntry): string =
return fmt"{self.getChainId}+{self.getContractAddress}+{self.getTokenID}"
# Unique ID to identify collection, generated by us # Unique ID to identify collection, generated by us
proc getCollectionID*(self: CollectiblesEntry): string = proc getCollectionID*(self: CollectiblesEntry): backend.ContractID =
return self.generatedCollectionId return self.id.contractID
proc generateCollectionId*(self: CollectiblesEntry): string = proc getCollectionIDAsString*(self: CollectiblesEntry): string =
return fmt"{self.getChainId}+{self.getContractAddress}" return self.generatedCollectionId
proc nameChanged*(self: CollectiblesEntry) {.signal.} proc nameChanged*(self: CollectiblesEntry) {.signal.}
proc getName*(self: CollectiblesEntry): string {.slot.} = proc getName*(self: CollectiblesEntry): string {.slot.} =
@ -341,8 +338,8 @@ QtObject:
result.id = data.id result.id = data.id
result.setData(data) result.setData(data)
result.extradata = extradata result.extradata = extradata
result.generatedId = result.generateId() result.generatedId = result.id.toString()
result.generatedCollectionId = result.generateCollectionId() result.generatedCollectionId = result.id.contractID.toString()
result.setup() result.setup()
proc newCollectibleDetailsBasicEntry*(id: backend.CollectibleUniqueID, extradata: ExtraData): CollectiblesEntry = proc newCollectibleDetailsBasicEntry*(id: backend.CollectibleUniqueID, extradata: ExtraData): CollectiblesEntry =
@ -351,8 +348,8 @@ QtObject:
result.extradata = extradata result.extradata = extradata
result.traits = newTraitModel() result.traits = newTraitModel()
result.ownership = newOwnershipModel() result.ownership = newOwnershipModel()
result.generatedId = result.generateId() result.generatedId = result.id.toString()
result.generatedCollectionId = result.generateCollectionId() result.generatedCollectionId = result.id.contractID.toString()
result.setup() result.setup()
proc newCollectibleDetailsEmptyEntry*(): CollectiblesEntry = proc newCollectibleDetailsEmptyEntry*(): CollectiblesEntry =

View File

@ -2,9 +2,7 @@ import NimQml, Tables, strutils, strformat, sequtils, stint, json
import logging import logging
import ./collectibles_entry import ./collectibles_entry
import web3/ethtypes as eth
import backend/collectibles as backend_collectibles import backend/collectibles as backend_collectibles
import backend/activity as backend_activity
import app_service/common/utils as common_utils import app_service/common/utils as common_utils
import app_service/common/types import app_service/common/types
@ -164,7 +162,7 @@ QtObject:
let item = self.items[index.row] let item = self.items[index.row]
case enumRole: case enumRole:
of CollectibleRole.Uid: of CollectibleRole.Uid:
result = newQVariant(item.getID()) result = newQVariant(item.getIDAsString())
of CollectibleRole.ChainId: of CollectibleRole.ChainId:
result = newQVariant(item.getChainID()) result = newQVariant(item.getChainID())
of CollectibleRole.ContractAddress: of CollectibleRole.ContractAddress:
@ -182,7 +180,7 @@ QtObject:
of CollectibleRole.BackgroundColor: of CollectibleRole.BackgroundColor:
result = newQVariant(item.getBackgroundColor()) result = newQVariant(item.getBackgroundColor())
of CollectibleRole.CollectionUid: of CollectibleRole.CollectionUid:
result = newQVariant(item.getCollectionID()) result = newQVariant(item.getCollectionIDAsString())
of CollectibleRole.CollectionName: of CollectibleRole.CollectionName:
result = newQVariant(item.getCollectionName()) result = newQVariant(item.getCollectionName())
of CollectibleRole.CollectionSlug: of CollectibleRole.CollectionSlug:
@ -205,7 +203,7 @@ QtObject:
return return
let item = self.items[index] let item = self.items[index]
case column: case column:
of "uid": result = item.getID() of "uid": result = item.getIDAsString()
of "chainId": result = $item.getChainID() of "chainId": result = $item.getChainID()
of "contractAddress": result = item.getContractAddress() of "contractAddress": result = item.getContractAddress()
of "tokenId": result = item.getTokenIDAsString() of "tokenId": result = item.getTokenIDAsString()
@ -214,7 +212,7 @@ QtObject:
of "mediaType": result = item.getMediaType() of "mediaType": result = item.getMediaType()
of "imageUrl": result = item.getImageURL() of "imageUrl": result = item.getImageURL()
of "backgroundColor": result = item.getBackgroundColor() of "backgroundColor": result = item.getBackgroundColor()
of "collectionUid": result = item.getCollectionID() of "collectionUid": result = item.getCollectionIDAsString()
of "collectionName": result = item.getCollectionName() of "collectionName": result = item.getCollectionName()
of "collectionSlug": result = item.getCollectionSlug() of "collectionSlug": result = item.getCollectionSlug()
of "isLoading": result = $false of "isLoading": result = $false
@ -271,12 +269,12 @@ QtObject:
var newTable = initTable[string, int](len(newItems)) var newTable = initTable[string, int](len(newItems))
for i in 0 ..< len(newItems): for i in 0 ..< len(newItems):
newTable.add(newItems[i].getID(), i) newTable.add(newItems[i].getIDAsString(), i)
# Needs to be built in sequential index order # Needs to be built in sequential index order
var oldIndicesToRemove: seq[int] = @[] var oldIndicesToRemove: seq[int] = @[]
for idx in 0 ..< len(self.items): for idx in 0 ..< len(self.items):
let uid = self.items[idx].getID() let uid = self.items[idx].getIDAsString()
if not newTable.hasKey(uid): if not newTable.hasKey(uid):
# Item in old list but not in new -> Must remove # Item in old list but not in new -> Must remove
oldIndicesToRemove.add(idx) oldIndicesToRemove.add(idx)
@ -303,7 +301,7 @@ QtObject:
proc getItemById*(self: Model, id: string): CollectiblesEntry = proc getItemById*(self: Model, id: string): CollectiblesEntry =
for item in self.items: for item in self.items:
if(cmpIgnoreCase(item.getID(), id) == 0): if(cmpIgnoreCase(item.getIDAsString(), id) == 0):
return item return item
return nil return nil
@ -343,45 +341,20 @@ QtObject:
proc getImageUrl*(self: Model, id: string): string {.slot.} = proc getImageUrl*(self: Model, id: string): string {.slot.} =
for item in self.items: for item in self.items:
if(cmpIgnoreCase(item.getId(), id) == 0): if(cmpIgnoreCase(item.getIDAsString(), id) == 0):
return item.getImageUrl() return item.getImageUrl()
return "" return ""
proc getName*(self: Model, id: string): string {.slot.} = proc getName*(self: Model, id: string): string {.slot.} =
for item in self.items: for item in self.items:
if(cmpIgnoreCase(item.getId(), id) == 0): if(cmpIgnoreCase(item.getIDAsString(), id) == 0):
return item.getName() return item.getName()
return "" return ""
proc getActivityToken*(self: Model, id: string): backend_activity.Token =
for item in self.items:
if(cmpIgnoreCase(item.getID(), id) == 0):
result.tokenType = TokenType.ERC721
result.chainId = backend_activity.ChainId(item.getChainID())
var contract = item.getContractAddress()
if len(contract) > 0:
var address: eth.Address
address = eth.fromHex(eth.Address, contract)
result.address = some(address)
var tokenId = item.getTokenID()
if tokenId > 0:
result.tokenId = some(backend_activity.TokenId("0x" & stint.toHex(tokenId)))
return result
# Fallback, use data from id
var parts = id.split("+")
if len(parts) == 3:
result.chainId = backend_activity.ChainId(parseInt(parts[0]))
result.address = some(eth.fromHex(eth.Address, parts[1]))
var tokenIdInt = u256(parseInt(parts[2]))
result.tokenId = some(backend_activity.TokenId("0x" & stint.toHex(tokenIdInt)))
return result
proc getUidForData*(self: Model, tokenId: string, tokenAddress: string, chainId: int): string {.slot.} = proc getUidForData*(self: Model, tokenId: string, tokenAddress: string, chainId: int): string {.slot.} =
for item in self.items: for item in self.items:
if(cmpIgnoreCase(item.getTokenIDAsString(), tokenId) == 0 and cmpIgnoreCase(item.getContractAddress(), tokenAddress) == 0): if(cmpIgnoreCase(item.getTokenIDAsString(), tokenId) == 0 and cmpIgnoreCase(item.getContractAddress(), tokenAddress) == 0) and item.getChainID() == chainId:
return item.getID() return item.getIDAsString()
# Fallback, create uid from data, because it still might not be fetched # Fallback, create uid from data, because it still might not be fetched
if chainId > 0 and len(tokenAddress) > 0 and len(tokenId) > 0: if chainId > 0 and len(tokenAddress) > 0 and len(tokenId) > 0:
return $chainId & "+" & tokenAddress & "+" & tokenId return $chainId & "+" & tokenAddress & "+" & tokenId

View File

@ -125,7 +125,7 @@ QtObject:
var collectiblesPerCollection = initTable[string, seq[flat_item.CollectiblesEntry]]() var collectiblesPerCollection = initTable[string, seq[flat_item.CollectiblesEntry]]()
for item in items: for item in items:
let collectionId = item.getCollectionID() let collectionId = item.getCollectionIDAsString()
if not collectiblesPerCollection.hasKey(collectionId): if not collectiblesPerCollection.hasKey(collectionId):
collectiblesPerCollection[collectionId] = @[] collectiblesPerCollection[collectionId] = @[]
collectiblesPerCollection[collectionId].add(item) collectiblesPerCollection[collectionId].add(item)

View File

@ -3,22 +3,22 @@ import ./collectibles_nested_item as nested_item
proc collectibleToCollectibleNestedItem*(flatItem: flat_item.CollectiblesEntry): nested_item.Item = proc collectibleToCollectibleNestedItem*(flatItem: flat_item.CollectiblesEntry): nested_item.Item =
return nested_item.initItem( return nested_item.initItem(
flatItem.getID(), flatItem.getIDAsString(),
flatItem.getChainID(), flatItem.getChainID(),
flatItem.getName(), flatItem.getName(),
flatItem.getImageURL(), flatItem.getImageURL(),
flatItem.getCollectionID(), flatItem.getCollectionIDAsString(),
flatItem.getCollectionName(), flatItem.getCollectionName(),
false false
) )
proc collectibleToCollectionNestedItem*(flatItem: flat_item.CollectiblesEntry): nested_item.Item = proc collectibleToCollectionNestedItem*(flatItem: flat_item.CollectiblesEntry): nested_item.Item =
return nested_item.initItem( return nested_item.initItem(
flatItem.getCollectionID(), flatItem.getCollectionIDAsString(),
flatItem.getChainID(), flatItem.getChainID(),
flatItem.getCollectionName(), flatItem.getCollectionName(),
flatItem.getCollectionImageURL(), flatItem.getCollectionImageURL(),
flatItem.getCollectionID(), flatItem.getCollectionIDAsString(),
flatItem.getCollectionName(), flatItem.getCollectionName(),
true true
) )

View File

@ -9,7 +9,6 @@ import events_handler
import app/core/eventemitter import app/core/eventemitter
import backend/collectibles as backend_collectibles import backend/collectibles as backend_collectibles
import backend/activity as backend_activity
import app_service/service/network/service as network_service import app_service/service/network/service as network_service
const FETCH_BATCH_COUNT_DEFAULT = 50 const FETCH_BATCH_COUNT_DEFAULT = 50
@ -302,9 +301,6 @@ QtObject:
self.resetModel() self.resetModel()
proc getActivityToken*(self: Controller, id: string): backend_activity.Token =
return self.model.getActivityToken(id)
proc getItemForData*(self: Controller, tokenId: string, tokenAddress: string, chainId: int): CollectiblesEntry = proc getItemForData*(self: Controller, tokenId: string, tokenAddress: string, chainId: int): CollectiblesEntry =
let uid = self.model.getUidForData(tokenId, tokenAddress, chainId) let uid = self.model.getUidForData(tokenId, tokenAddress, chainId)
return self.model.getItemById(uid) return self.model.getItemById(uid)

View File

@ -1,5 +1,5 @@
import json, strformat, json_serialization import json, strformat, json_serialization
import stint, Tables, options import stint, Tables, options, strutils
import community_tokens_types import community_tokens_types
include app_service/common/json_utils include app_service/common/json_utils
@ -126,6 +126,13 @@ proc fromJson*(t: JsonNode, T: typedesc[ref ContractID]): ref ContractID {.inlin
result = new(ContractID) result = new(ContractID)
result[] = fromJson(t, ContractID) result[] = fromJson(t, ContractID)
proc toString*(t: ContractID): string =
return fmt"{t.chainID}+{t.address}"
proc toContractID*(t: string): ContractID =
var parts = t.split("+")
return ContractID(chainID: parts[0].parseInt(), address: parts[1])
# CollectibleUniqueID # CollectibleUniqueID
proc `$`*(self: CollectibleUniqueID): string = proc `$`*(self: CollectibleUniqueID): string =
return fmt"""CollectibleUniqueID( return fmt"""CollectibleUniqueID(
@ -154,6 +161,19 @@ proc fromJson*(t: JsonNode, T: typedesc[ref CollectibleUniqueID]): ref Collectib
result = new(CollectibleUniqueID) result = new(CollectibleUniqueID)
result[] = fromJson(t, CollectibleUniqueID) result[] = fromJson(t, CollectibleUniqueID)
proc toString*(t: CollectibleUniqueID): string =
return fmt"{t.contractID.chainID}+{t.contractID.address}+{t.tokenID.toString()}"
proc toCollectibleUniqueID*(t: string): CollectibleUniqueID =
var parts = t.split("+")
return CollectibleUniqueID(
contractID: ContractID(
chainID: parts[0].parseInt(),
address: parts[1]
),
tokenID: stint.parse(parts[2], UInt256)
)
# CollectibleTrait # CollectibleTrait
proc `$`*(self: CollectibleTrait): string = proc `$`*(self: CollectibleTrait): string =
return fmt"""CollectibleTrait( return fmt"""CollectibleTrait(

View File

@ -41,3 +41,25 @@ suite "collectibles types":
check(oldCommOwners[0].name == newCommOwners[0].name) check(oldCommOwners[0].name == newCommOwners[0].name)
check(oldCommOwners[0].imageSource == newCommOwners[0].imageSource) check(oldCommOwners[0].imageSource == newCommOwners[0].imageSource)
check(oldCommOwners[0].collectibleOwner.address == newCommOwners[0].collectibleOwner.address) check(oldCommOwners[0].collectibleOwner.address == newCommOwners[0].collectibleOwner.address)
test "ContractID string conversion":
let oldContractID = ContractID(chainID: 321, address: "0x123")
let contractIDString = oldContractID.toString()
let newContractID = contractIDString.toContractID()
check(oldContractID == newContractID)
test "CollectibleUniqueID string conversion":
let oldUniqueID = CollectibleUniqueID(
contractID: ContractID(chainID: 321, address: "0x123"),
tokenId: stint.u256(23)
)
let uniqueIDString = oldUniqueID.toString()
let newUniqueID = uniqueIDString.toCollectibleUniqueID()
check(oldUniqueID == newUniqueID)

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 972a4f9df9fdd0464d02e014fc65a9de70753b3c Subproject commit 54ea0981a5e1496c1358ea9dcc8c99ffc2098c73