diff --git a/src/app/modules/main/wallet_section/collectibles/controller.nim b/src/app/modules/main/wallet_section/collectibles/controller.nim index babcddce5c..55f056efb6 100644 --- a/src/app/modules/main/wallet_section/collectibles/controller.nim +++ b/src/app/modules/main/wallet_section/collectibles/controller.nim @@ -41,19 +41,21 @@ proc newController*( proc delete*(self: Controller) = discard -proc refreshCollectibles(self: Controller, chainId: int, address: string) = +proc resetCollectibles(self: Controller, chainId: int, address: string) = let data = self.collectibleService.getOwnedCollectibles(chainId, address) - if not data.anyLoaded or data.lastLoadWasFromStart: - self.delegate.setCollectibles(chainId, address, data) - else: - self.delegate.appendCollectibles(chainId, address, data) + self.delegate.setCollectibles(chainId, address, data) + +proc processNewCollectibles(self: Controller, chainId: int, address: string) = + let data = self.collectibleService.getOwnedCollectibles(chainId, address) + self.delegate.appendCollectibles(chainId, address, data) + if not self.nodeService.isConnected() or not self.networkConnectionService.checkIfConnected(COLLECTIBLES): self.delegate.connectionToOpenSea(false) proc init*(self: Controller) = self.events.on(SIGNAL_OWNED_COLLECTIBLES_RESET) do(e:Args): let args = OwnedCollectiblesUpdateArgs(e) - self.refreshCollectibles(args.chainId, args.address) + self.resetCollectibles(args.chainId, args.address) self.events.on(SIGNAL_OWNED_COLLECTIBLES_UPDATE_STARTED) do(e:Args): let args = OwnedCollectiblesUpdateArgs(e) @@ -61,7 +63,7 @@ proc init*(self: Controller) = self.events.on(SIGNAL_OWNED_COLLECTIBLES_UPDATE_FINISHED) do(e:Args): let args = OwnedCollectiblesUpdateArgs(e) - self.refreshCollectibles(args.chainId, args.address) + self.processNewCollectibles(args.chainId, args.address) self.events.on(SIGNAL_REFRESH_COLLECTIBLES) do(e:Args): self.collectibleService.resetAllOwnedCollectibles() diff --git a/src/app/modules/main/wallet_section/collectibles/models/collectibles_model.nim b/src/app/modules/main/wallet_section/collectibles/models/collectibles_model.nim index 219cc8e1ab..f1b66d218d 100644 --- a/src/app/modules/main/wallet_section/collectibles/models/collectibles_model.nim +++ b/src/app/modules/main/wallet_section/collectibles/models/collectibles_model.nim @@ -29,6 +29,7 @@ QtObject: items: seq[Item] allCollectiblesLoaded: bool isFetching: bool + isError: bool proc addLoadingItems(self: Model) proc removeLoadingItems(self: Model) @@ -46,6 +47,7 @@ QtObject: result.items = @[] result.allCollectiblesLoaded = false result.isFetching = true + result.isError = false proc `$`*(self: Model): string = for i in 0 ..< self.items.len: @@ -74,6 +76,18 @@ QtObject: self.isFetching = value self.isFetchingChanged() + proc isErrorChanged(self: Model) {.signal.} + proc getIsError*(self: Model): bool {.slot.} = + self.isError + QtProperty[bool] isError: + read = getIsError + notify = isErrorChanged + proc setIsError*(self: Model, value: bool) = + if value == self.isError: + return + self.isError = value + self.isErrorChanged() + proc allCollectiblesLoadedChanged(self: Model) {.signal.} proc getAllCollectiblesLoaded*(self: Model): bool {.slot.} = self.allCollectiblesLoaded @@ -87,12 +101,11 @@ QtObject: self.allCollectiblesLoadedChanged() method canFetchMore*(self: Model, parent: QModelIndex): bool = - return not self.allCollectiblesLoaded and not self.isFetching + return not self.allCollectiblesLoaded and not self.isFetching and not self.isError proc requestFetch(self: Model) {.signal.} method fetchMore*(self: Model, parent: QModelIndex) = - if not self.isFetching: - self.requestFetch() + self.requestFetch() method rowCount*(self: Model, index: QModelIndex = nil): int = return self.items.len diff --git a/src/app/modules/main/wallet_section/collectibles/module.nim b/src/app/modules/main/wallet_section/collectibles/module.nim index b26b8a42be..f1b0f3c7a0 100644 --- a/src/app/modules/main/wallet_section/collectibles/module.nim +++ b/src/app/modules/main/wallet_section/collectibles/module.nim @@ -109,6 +109,8 @@ method onFetchStarted*(self: Module, chainId: int, address: string) = method setCollectibles*(self: Module, chainId: int, address: string, data: CollectiblesData) = if self.chainId == chainId and self.address == address: self.view.setIsFetching(data.isFetching) + self.view.setIsError(data.isError) + var newCollectibles = data.collectibles.map(oc => self.ownedCollectibleToItem(oc)) self.view.setCollectibles(newCollectibles) self.view.setAllLoaded(data.allLoaded) @@ -116,15 +118,17 @@ method setCollectibles*(self: Module, chainId: int, address: string, data: Colle method appendCollectibles*(self: Module, chainId: int, address: string, data: CollectiblesData) = if self.chainId == chainId and self.address == address: self.view.setIsFetching(data.isFetching) + self.view.setIsError(data.isError) - var ownedCollectiblesToAdd = newSeq[OwnedCollectible]() - for i in data.collectibles.len - data.lastLoadCount ..< data.collectibles.len: - ownedCollectiblesToAdd.add(data.collectibles[i]) + if not data.isError: + var ownedCollectiblesToAdd = newSeq[OwnedCollectible]() + for i in data.collectibles.len - data.lastLoadCount ..< data.collectibles.len: + ownedCollectiblesToAdd.add(data.collectibles[i]) - let newCollectibles = ownedCollectiblesToAdd.map(oc => self.ownedCollectibleToItem(oc)) + let newCollectibles = ownedCollectiblesToAdd.map(oc => self.ownedCollectibleToItem(oc)) - self.view.appendCollectibles(newCollectibles) - self.view.setAllLoaded(data.allLoaded) + self.view.appendCollectibles(newCollectibles) + self.view.setAllLoaded(data.allLoaded) method connectionToOpenSea*(self: Module, connected: bool) = self.view.connectionToOpenSea(connected) diff --git a/src/app/modules/main/wallet_section/collectibles/view.nim b/src/app/modules/main/wallet_section/collectibles/view.nim index 5526d7c408..aa477df609 100644 --- a/src/app/modules/main/wallet_section/collectibles/view.nim +++ b/src/app/modules/main/wallet_section/collectibles/view.nim @@ -34,6 +34,9 @@ QtObject: proc fetchMoreOwnedCollectibles*(self: View) {.slot.} = self.delegate.fetchOwnedCollectibles() + proc setIsError*(self: View, isError: bool) = + self.model.setIsError(isError) + proc setIsFetching*(self: View, isFetching: bool) = self.model.setIsFetching(isFetching) diff --git a/src/app_service/service/collectible/async_tasks.nim b/src/app_service/service/collectible/async_tasks.nim index 862873a6fd..3f5cd1c560 100644 --- a/src/app_service/service/collectible/async_tasks.nim +++ b/src/app_service/service/collectible/async_tasks.nim @@ -7,19 +7,25 @@ type const fetchOwnedCollectiblesTaskArg: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = let arg = decode[FetchOwnedCollectiblesTaskArg](argEncoded) - let output = %* { - "chainId": arg.chainId, - "address": arg.address, - "cursor": arg.cursor, - "collectibles": {"assets":nil,"next":"","previous":""} - } try: let response = collectibles.getOpenseaAssetsByOwnerWithCursor(arg.chainId, arg.address, arg.cursor, arg.limit) - output["collectibles"] = response.result + let output = %* { + "chainId": arg.chainId, + "address": arg.address, + "cursor": arg.cursor, + "collectibles": response.result, + "error": "" + } + arg.finish(output) except Exception as e: - let errDesription = e.msg - error "error fetchOwnedCollectiblesTaskArg: ", errDesription - arg.finish(output) + let output = %* { + "chainId": arg.chainId, + "address": arg.address, + "cursor": arg.cursor, + "collectibles": "", + "error": e.msg + } + arg.finish(output) type FetchOwnedCollectiblesFromContractAddressesTaskArg = ref object of QObjectTaskArg @@ -31,19 +37,25 @@ type const fetchOwnedCollectiblesFromContractAddressesTaskArg: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = let arg = decode[FetchOwnedCollectiblesFromContractAddressesTaskArg](argEncoded) - let output = %* { - "chainId": arg.chainId, - "address": arg.address, - "cursor": arg.cursor, - "collectibles": "" - } try: let response = collectibles.getOpenseaAssetsByOwnerAndContractAddressWithCursor(arg.chainId, arg.address, arg.contractAddresses, arg.cursor, arg.limit) - output["collectibles"] = response.result + let output = %* { + "chainId": arg.chainId, + "address": arg.address, + "cursor": arg.cursor, + "collectibles": response.result, + "error": "" + } + arg.finish(output) except Exception as e: - let errDesription = e.msg - error "error fetchOwnedCollectiblesFromContractAddressesTaskArg: ", errDesription - arg.finish(output) + let output = %* { + "chainId": arg.chainId, + "address": arg.address, + "cursor": arg.cursor, + "collectibles": "", + "error": e.msg + } + arg.finish(output) type FetchCollectiblesTaskArg = ref object of QObjectTaskArg @@ -53,14 +65,18 @@ type const fetchCollectiblesTaskArg: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = let arg = decode[FetchCollectiblesTaskArg](argEncoded) - let output = %* { - "chainId": arg.chainId, - "collectibles": "" - } try: let response = collectibles.getOpenseaAssetsByNFTUniqueID(arg.chainId, arg.ids, arg.limit) - output["collectibles"] = response.result + let output = %* { + "chainId": arg.chainId, + "collectibles": response.result, + "error": "" + } + arg.finish(output) except Exception as e: - let errDesription = e.msg - error "error fetchCollectiblesTaskArg: ", errDesription - arg.finish(output) + let output = %* { + "chainId": arg.chainId, + "collectibles": "", + "error": e.msg + } + arg.finish(output) diff --git a/src/app_service/service/collectible/service.nim b/src/app_service/service/collectible/service.nim index de0a6e80ba..b8237666af 100644 --- a/src/app_service/service/collectible/service.nim +++ b/src/app_service/service/collectible/service.nim @@ -5,6 +5,7 @@ import ../../../app/core/tasks/[qt, threadpool] import dto import ../network/service as network_service +import ../wallet_account/service as wallet_account_service import ../../../backend/collectibles as collectibles @@ -49,13 +50,20 @@ proc `$`*(self: OwnedCollectible): string = isFromWatchedContract:{self.isFromWatchedContract} )""" +type + State {.pure.} = enum + Init, + WatchedContractsLoading, + WatchedContractsLoaded, + ChunkLoading, + ChunkLoaded, + AllLoaded, + Error + type CollectiblesData* = ref object - isFetching*: bool - anyLoaded*: bool - allLoaded*: bool - lastLoadWasFromStart*: bool - lastLoadFromStartTimestamp*: DateTime + state*: State + initialLoadTimestamp*: DateTime lastLoadCount*: int previousCursor*: string nextCursor*: string @@ -64,24 +72,42 @@ type proc newCollectiblesData(): CollectiblesData = new(result) - result.isFetching = false - result.anyLoaded = false - result.allLoaded = false - result.lastLoadWasFromStart = false - result.lastLoadFromStartTimestamp = INVALID_TIMESTAMP.utc() + result.state = State.Init + result.initialLoadTimestamp = INVALID_TIMESTAMP.utc() result.lastLoadCount = 0 result.previousCursor = "" result.nextCursor = "" result.collectibles = @[] result.collectiblesFromWatchedContracts = @[] +proc isFetching*(self: CollectiblesData): bool = + result = case self.state: + of State.WatchedContractsLoading, State.ChunkLoading: + true + else: + false + +proc anyLoaded*(self: CollectiblesData): bool = + return self.initialLoadTimestamp != INVALID_TIMESTAMP.utc() + +proc isError*(self: CollectiblesData): bool = + result = case self.state: + of State.Error: + true + else: + false + +proc allLoaded*(self: CollectiblesData): bool = + result = case self.state: + of State.AllLoaded: + true + else: + false + proc `$`*(self: CollectiblesData): string = return fmt"""CollectiblesData( - isFetching:{self.isFetching}, - anyLoaded:{self.anyLoaded}, - allLoaded:{self.allLoaded}, - lastLoadWasFromStart:{self.lastLoadWasFromStart}, - lastLoadFromStartTimestamp:{self.lastLoadFromStartTimestamp}, + state:{self.state}, + initialLoadTimestamp:{self.initialLoadTimestamp}, lastLoadCount:{self.lastLoadCount}, previousCursor:{self.previousCursor}, nextCursor:{self.nextCursor}, @@ -110,6 +136,9 @@ type proc newChainsData(): ChainsData = result = newTable[int, AddressesData]() +proc contains*(self: ChainsData, chainId: int, address: string): bool = + return self.hasKey(chainId) and self[chainId].hasKey(address) + type CollectiblesResult = tuple[success: bool, collectibles: seq[CollectibleDto], collections: seq[CollectionDto], previousCursor: string, nextCursor: string] @@ -129,7 +158,8 @@ QtObject: # Forward declarations proc resetOwnedCollectibles*(self: Service, chainId: int, address: string) - proc resetAllOwnedCollectibles*(self: Service) + proc resetAllOwnedCollectibles(self: Service) + proc removeAddress(self: Service, address: string) proc delete*(self: Service) = self.QObject.delete @@ -155,6 +185,9 @@ QtObject: of "wallet-tick-reload": self.resetAllOwnedCollectibles() + self.events.on(SIGNAL_WALLET_ACCOUNT_DELETED) do(e:Args): + self.removeAddress(AccountDeleted(e).account.address) + # needs to be re-written once cache for colletibles works proc areCollectionsLoaded*(self: Service, address: string): bool = for chainId, adressesData in self.accountsOwnershipData: @@ -171,51 +204,58 @@ QtObject: if not chainData.hasKey(address): chainData[address] = newOwnershipData() - proc updateOwnedCollectibles(self: Service, chainId: int, address: string, previousCursor: string, nextCursor: string, collectibles: seq[CollectibleDto], isFromWatchedContract: bool) = + proc processOwnedCollectiblesError(self: Service, chainId: int, address: string) = let ownershipData = self.accountsOwnershipData[chainId][address] let collectiblesData = ownershipData.data - try: - if not (collectiblesData.nextCursor == previousCursor): - # Async response from an old fetch request, disregard - return - if not collectiblesData.anyLoaded: - collectiblesData.lastLoadWasFromStart = true - collectiblesData.lastLoadFromStartTimestamp = now() - else: - collectiblesData.lastLoadWasFromStart = false - - collectiblesData.anyLoaded = true + collectiblesData.lastLoadCount = 0 + collectiblesData.state = State.Error - if isFromWatchedContract: - # All fetched in one go, ignore cursors - collectiblesData.previousCursor = "" - collectiblesData.nextCursor = "" - collectiblesData.allLoaded = false + proc updateOwnedCollectibles(self: Service, chainId: int, address: string, previousCursor: string, nextCursor: string, collectibles: seq[CollectibleDto]) = + let ownershipData = self.accountsOwnershipData[chainId][address] + let collectiblesData = ownershipData.data + if not (collectiblesData.nextCursor == previousCursor): + # Async response from an old fetch request, disregard + return + var isFromWatchedContract = false + case collectiblesData.state: + of State.WatchedContractsLoading: + isFromWatchedContract = true + collectiblesData.state = State.WatchedContractsLoaded + # All fetched in one go, ignore cursors + collectiblesData.previousCursor = "" + collectiblesData.nextCursor = "" + of State.ChunkLoading: + if nextCursor == "": + collectiblesData.state = State.AllLoaded else: - collectiblesData.previousCursor = previousCursor - collectiblesData.nextCursor = nextCursor - collectiblesData.allLoaded = (nextCursor == "") + collectiblesData.state = State.ChunkLoaded + collectiblesData.previousCursor = previousCursor + collectiblesData.nextCursor = nextCursor + else: + let errDescription = collectiblesData.state + error "Invalid state", errDescription + self.processOwnedCollectiblesError(chainId, address) - var count = 0 - for collectible in collectibles: - let newId = UniqueID( - contractAddress: collectible.address, - tokenId: collectible.tokenId + if not collectiblesData.anyLoaded: + collectiblesData.initialLoadTimestamp = now() + + var count = 0 + for collectible in collectibles: + let newId = UniqueID( + contractAddress: collectible.address, + tokenId: collectible.tokenId + ) + if not collectiblesData.collectibles.any(c => newId == c.id): + let ownedCollectible = OwnedCollectible( + id: newId, + isFromWatchedContract: isFromWatchedContract ) - if not collectiblesData.collectibles.any(c => newId == c.id): - let ownedCollectible = OwnedCollectible( - id: newId, - isFromWatchedContract: isFromWatchedContract - ) - collectiblesData.collectibles.add(ownedCollectible) - if isFromWatchedContract: - collectiblesData.collectiblesFromWatchedContracts.add(ownedCollectible) + collectiblesData.collectibles.add(ownedCollectible) + if isFromWatchedContract: + collectiblesData.collectiblesFromWatchedContracts.add(ownedCollectible) - count = count + 1 - collectiblesData.lastLoadCount = count - except Exception as e: - let errDesription = e.msg - error "error: ", errDesription + count = count + 1 + collectiblesData.lastLoadCount = count proc updateCollectiblesCache*(self: Service, chainId: int, collectibles: seq[CollectibleDto], collections: seq[CollectionDto]) = if not self.collectibles.hasKey(chainId): @@ -289,18 +329,20 @@ QtObject: result.success = true proc onRxCollectibles(self: Service, response: string) {.slot.} = - try: - let responseObj = response.parseJson - if (responseObj.kind == JObject): - let chainIdJson = responseObj["chainId"] - if chainIdJson.kind == JInt: - let chainId = chainIdJson.getInt() - let (success, collectibles, collections, _, _) = processCollectiblesResult(responseObj) - if success: - self.updateCollectiblesCache(chainId, collectibles, collections) - except Exception as e: - let errDescription = e.msg - error "error onRxCollectibles: ", errDescription + let responseObj = response.parseJson + let chainIdJson = responseObj["chainId"] + let chainId = chainIdJson.getInt() + + let errorStr = responseObj["error"].getStr() + if errorStr != "": + error "error onRxCollectibles: ", errorStr + else: + let (success, collectibles, collections, _, _) = processCollectiblesResult(responseObj) + if success: + self.updateCollectiblesCache(chainId, collectibles, collections) + else: + let errDesription = "Could not get data from response" + error "error onRxCollectibles: ", errDesription proc fetchCollectibles*(self: Service, chainId: int, ids: seq[UniqueID]) = let arg = FetchCollectiblesTaskArg( @@ -318,24 +360,31 @@ QtObject: proc onRxOwnedCollectibles(self: Service, response: string) {.slot.} = var data = OwnedCollectiblesUpdateArgs() - try: - let responseObj = response.parseJson - if (responseObj.kind == JObject): - let chainIdJson = responseObj["chainId"] - let addressJson = responseObj["address"] - if (chainIdJson.kind == JInt and - addressJson.kind == JString): - data.chainId = chainIdJson.getInt() - data.address = addressJson.getStr() - let collectiblesData = self.accountsOwnershipData[data.chainId][data.address].data - collectiblesData.isFetching = false - let (success, collectibles, collections, prevCursor, nextCursor) = processCollectiblesResult(responseObj) - if success: - self.updateCollectiblesCache(data.chainId, collectibles, collections) - self.updateOwnedCollectibles(data.chainId, data.address, prevCursor, nextCursor, collectibles, false) - except Exception as e: - let errDescription = e.msg - error "error onRxOwnedCollectibles: ", errDescription + let responseObj = response.parseJson + let chainIdJson = responseObj["chainId"] + let addressJson = responseObj["address"] + data.chainId = chainIdJson.getInt() + data.address = addressJson.getStr() + + if not self.accountsOwnershipData.contains(data.chainId, data.address): + # Account was removed + return + + let collectiblesData = self.accountsOwnershipData[data.chainId][data.address].data + + let errorStr = responseObj["error"].getStr() + if errorStr != "": + self.processOwnedCollectiblesError(data.chainId, data.address) + error "error onRxOwnedCollectiblesFromWatchedContractAddresses: ", errorStr + else: + let (success, collectibles, collections, prevCursor, nextCursor) = processCollectiblesResult(responseObj) + if success: + self.updateCollectiblesCache(data.chainId, collectibles, collections) + self.updateOwnedCollectibles(data.chainId, data.address, prevCursor, nextCursor, collectibles) + else: + self.processOwnedCollectiblesError(data.chainId, data.address) + let errDesription = "Could not get data from response" + error "error onRxOwnedCollectibles: ", errDesription self.events.emit(SIGNAL_OWNED_COLLECTIBLES_UPDATE_FINISHED, data) proc fetchNextOwnedCollectiblesChunk(self: Service, chainId: int, address: string, limit: int = ownedCollectiblesFetchLimit) = @@ -359,24 +408,31 @@ QtObject: proc onRxOwnedCollectiblesFromWatchedContractAddresses(self: Service, response: string) {.slot.} = var data = OwnedCollectiblesUpdateArgs() - try: - let responseObj = response.parseJson - if (responseObj.kind == JObject): - let chainIdJson = responseObj["chainId"] - let addressJson = responseObj["address"] - if (chainIdJson.kind == JInt and - addressJson.kind == JString): - data.chainId = chainIdJson.getInt() - data.address = addressJson.getStr() - let collectiblesData = self.accountsOwnershipData[data.chainId][data.address].data - collectiblesData.isFetching = false - let (success, collectibles, collections, prevCursor, nextCursor) = processCollectiblesResult(responseObj) - if success: - self.updateCollectiblesCache(data.chainId, collectibles, collections) - self.updateOwnedCollectibles(data.chainId, data.address, prevCursor, nextCursor, collectibles, true) - except Exception as e: - let errDescription = e.msg - error "error onRxOwnedCollectiblesFromWatchedContractAddresses: ", errDescription + let responseObj = response.parseJson + let chainIdJson = responseObj["chainId"] + let addressJson = responseObj["address"] + data.chainId = chainIdJson.getInt() + data.address = addressJson.getStr() + + if not self.accountsOwnershipData.contains(data.chainId, data.address): + # Account was removed + return + + let collectiblesData = self.accountsOwnershipData[data.chainId][data.address].data + + let errorStr = responseObj["error"].getStr() + if errorStr != "": + self.processOwnedCollectiblesError(data.chainId, data.address) + error "error onRxOwnedCollectiblesFromWatchedContractAddresses: ", errorStr + else: + let (success, collectibles, collections, prevCursor, nextCursor) = processCollectiblesResult(responseObj) + if success: + self.updateCollectiblesCache(data.chainId, collectibles, collections) + self.updateOwnedCollectibles(data.chainId, data.address, prevCursor, nextCursor, collectibles) + else: + self.processOwnedCollectiblesError(data.chainId, data.address) + let errDesription = "Could not get data from response" + error "error onRxOwnedCollectiblesFromWatchedContractAddresses: ", errDesription self.events.emit(SIGNAL_OWNED_COLLECTIBLES_FROM_WATCHED_CONTRACTS_FETCHED, data) self.events.emit(SIGNAL_OWNED_COLLECTIBLES_UPDATE_FINISHED, data) @@ -402,24 +458,22 @@ QtObject: let watchedContractAddresses = ownershipData.watchedContractAddresses let collectiblesData = ownershipData.data - if collectiblesData.isFetching: + let state = collectiblesData.state + + if state == State.Init and len(watchedContractAddresses) > 0: + collectiblesData.state = State.WatchedContractsLoading + self.fetchOwnedCollectiblesFromWatchedContracts(chainId, address) + elif state == State.Init or state == State.WatchedContractsLoaded or state == State.ChunkLoaded: + collectiblesData.state = State.ChunkLoading + self.fetchNextOwnedCollectiblesChunk(chainId, address) + else: return - if collectiblesData.allLoaded: - return - - collectiblesData.isFetching = true var data = OwnedCollectiblesUpdateArgs() data.chainId = chainId data.address = address self.events.emit(SIGNAL_OWNED_COLLECTIBLES_UPDATE_STARTED, data) - # Full list of collectibles from watched contracts always get loaded first - if not collectiblesData.anyLoaded and len(watchedContractAddresses) > 0: - self.fetchOwnedCollectiblesFromWatchedContracts(chainId, address) - else: - self.fetchNextOwnedCollectiblesChunk(chainId, address) - proc resetOwnedCollectibles*(self: Service, chainId: int, address: string) = self.prepareOwnershipData(chainId, address) @@ -433,7 +487,11 @@ QtObject: self.fetchOwnedCollectibles(chainId, address) - proc resetAllOwnedCollectibles*(self: Service) = + proc resetAllOwnedCollectibles(self: Service) = for chainId, addressesData in self.accountsOwnershipData: for address, _ in addressesData: self.resetOwnedCollectibles(chainId, address) + + proc removeAddress(self: Service, address: string) = + for chainId, addressesData in self.accountsOwnershipData: + addressesData.del(address)