From 0d21ed26175dd95136dad3e187b33dc325e63669 Mon Sep 17 00:00:00 2001 From: Michal Iskierko Date: Tue, 28 Nov 2023 12:13:09 +0100 Subject: [PATCH] fix(@desktop/communities): Keep community tokens in cache. Fix #12547 --- .../modules/main/communities/controller.nim | 9 +- .../modules/main/communities/io_interface.nim | 3 - src/app/modules/main/communities/module.nim | 10 +- .../main/communities/tokens/controller.nim | 2 + src/app_service/service/community/service.nim | 11 +++ .../service/community_tokens/service.nim | 91 +++++++++++-------- 6 files changed, 74 insertions(+), 52 deletions(-) diff --git a/src/app/modules/main/communities/controller.nim b/src/app/modules/main/communities/controller.nim index a5bb70e569..1071aa21dd 100644 --- a/src/app/modules/main/communities/controller.nim +++ b/src/app/modules/main/communities/controller.nim @@ -163,11 +163,6 @@ proc init*(self: Controller) = let args = CommunitiesArgs(e) self.delegate.curatedCommunitiesLoaded(args.communities) - # We use once here because we only need it to generate the original list of tokens from communities - self.events.once(SIGNAL_ALL_COMMUNITY_TOKENS_LOADED) do(e: Args): - let args = CommunityTokensArgs(e) - self.delegate.onAllCommunityTokensLoaded(args.communityTokens) - self.events.on(SIGNAL_COMMUNITY_TOKEN_METADATA_ADDED) do(e: Args): let args = CommunityTokenMetadataArgs(e) self.delegate.onCommunityTokenMetadataAdded(args.communityId, args.tokenMetadata) @@ -368,8 +363,8 @@ proc requestCancelDiscordChannelImport*(self: Controller, discordChannelId: stri proc getCommunityTokens*(self: Controller, communityId: string): seq[CommunityTokenDto] = self.communityTokensService.getCommunityTokens(communityId) -proc getAllCommunityTokensAsync*(self: Controller) = - self.communityTokensService.getAllCommunityTokensAsync() +proc getAllCommunityTokens*(self: Controller): seq[CommunityTokenDto] = + self.communityTokensService.getAllCommunityTokens() proc getNetwork*(self:Controller, chainId: int): NetworkDto = self.networksService.getNetwork(chainId) diff --git a/src/app/modules/main/communities/io_interface.nim b/src/app/modules/main/communities/io_interface.nim index a40534af28..7330f52293 100644 --- a/src/app/modules/main/communities/io_interface.nim +++ b/src/app/modules/main/communities/io_interface.nim @@ -257,8 +257,5 @@ method onCommunityMemberRevealedAccountsLoaded*(self: AccessInterface, community revealedAccounts: seq[RevealedAccount]) {.base.} = raise newException(ValueError, "No implementation available") -method onAllCommunityTokensLoaded*(self: AccessInterface, communityTokens: seq[CommunityTokenDto]) {.base.} = - raise newException(ValueError, "No implementation available") - method removeCommunityChat*(self: AccessInterface, communityId: string, channelId: string) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/communities/module.nim b/src/app/modules/main/communities/module.nim index 62ad5bea5b..de36945982 100644 --- a/src/app/modules/main/communities/module.nim +++ b/src/app/modules/main/communities/module.nim @@ -95,6 +95,7 @@ type method setCommunityTags*(self: Module, communityTags: string) method setAllCommunities*(self: Module, communities: seq[CommunityDto]) method setCuratedCommunities*(self: Module, curatedCommunities: seq[CommunityDto]) +proc buildTokensAndCollectiblesFromCommunities(self: Module) proc newModule*( delegate: delegate_interface.AccessInterface, @@ -158,8 +159,7 @@ method viewDidLoad*(self: Module) = method communityDataLoaded*(self: Module) = self.setCommunityTags(self.controller.getCommunityTags()) self.setAllCommunities(self.controller.getAllCommunities()) - # Get all community tokens to construct the original list of collectibles and assets from communities - self.controller.getAllCommunityTokensAsync() + self.buildTokensAndCollectiblesFromCommunities() method onActivated*(self: Module) = self.controller.asyncLoadCuratedCommunities() @@ -555,10 +555,11 @@ proc createCommunityTokenItem(self: Module, token: CommunityTokensMetadataDto, c infiniteSupply, ) -proc buildTokensAndCollectiblesFromCommunities(self: Module, communityTokens: seq[CommunityTokenDto]) = +proc buildTokensAndCollectiblesFromCommunities(self: Module) = var tokenListItems: seq[TokenListItem] var collectiblesListItems: seq[TokenListItem] + let communityTokens = self.controller.getAllCommunityTokens() let communities = self.controller.getAllCommunities() for community in communities: if not community.isOwner and not community.isTokenMaster: @@ -614,9 +615,6 @@ proc buildTokensAndCollectiblesFromWallet(self: Module) = method onWalletAccountTokensRebuilt*(self: Module) = self.buildTokensAndCollectiblesFromWallet() -method onAllCommunityTokensLoaded*(self: Module, communityTokens: seq[CommunityTokenDto]) = - self.buildTokensAndCollectiblesFromCommunities(communityTokens) - method onCommunityTokenMetadataAdded*(self: Module, communityId: string, tokenMetadata: CommunityTokensMetadataDto) = let communityTokens = self.controller.getCommunityTokens(communityId) var tokenListItem: TokenListItem diff --git a/src/app/modules/main/communities/tokens/controller.nim b/src/app/modules/main/communities/tokens/controller.nim index 49c05c5b47..6c153bf8f6 100644 --- a/src/app/modules/main/communities/tokens/controller.nim +++ b/src/app/modules/main/communities/tokens/controller.nim @@ -101,6 +101,8 @@ proc init*(self: Controller) = self.events.on(SIGNAL_OWNER_TOKEN_SENT) do(e: Args): let args = OwnerTokenSentArgs(e) self.communityTokensModule.onSendOwnerTokenStateChanged(args.chainId, args.txHash, args.tokenName, args.status) + self.events.on(SIGNAL_COMMUNITY_TOKENS_CHANGED) do(e:Args): + self.communityTokensService.getAllCommunityTokensAsync() proc deployContract*(self: Controller, communityId: string, addressFrom: string, password: string, deploymentParams: DeploymentParameters, tokenMetadata: CommunityTokensMetadataDto, tokenImageCropInfoJson: string, chainId: int) = self.communityTokensService.deployContract(communityId, addressFrom, password, deploymentParams, tokenMetadata, tokenImageCropInfoJson, chainId) diff --git a/src/app_service/service/community/service.nim b/src/app_service/service/community/service.nim index 7f7311538c..b7e9428525 100644 --- a/src/app_service/service/community/service.nim +++ b/src/app_service/service/community/service.nim @@ -247,6 +247,8 @@ const SIGNAL_COMMUNITY_LOST_OWNERSHIP* = "communityLostOwnership" const SIGNAL_COMMUNITY_SHARD_SET* = "communityShardSet" const SIGNAL_COMMUNITY_SHARD_SET_FAILED* = "communityShardSetFailed" +const SIGNAL_COMMUNITY_TOKENS_CHANGED* = "communityTokensChanged" + QtObject: type Service* = ref object of QObject @@ -507,6 +509,12 @@ QtObject: self.events.emit(SIGNAL_COMMUNITY_MEMBERS_CHANGED, CommunityMembersArgs(communityId: community.id, members: community.members)) + proc communityTokensChanged(self: Service, community: CommunityDto, prev_community: CommunityDto): bool = + let communityTokens = community.communityTokensMetadata + let prevCommunityTokens = prev_community.communityTokensMetadata + # checking length is sufficient - communityTokensMetadata list can only extend + return communityTokens.len != prevCommunityTokens.len + proc handleCommunityUpdates(self: Service, communities: seq[CommunityDto], updatedChats: seq[ChatDto], removedChats: seq[string]) = try: let myPublicKey = singletonInstance.userProfile.getPubKey() @@ -527,6 +535,9 @@ QtObject: let response = tokens_backend.registerLostOwnershipNotification(community.id) checkAndEmitACNotificationsFromResponse(self.events, response.result{"activityCenterNotifications"}) + if self.communityTokensChanged(community, prev_community): + self.events.emit(SIGNAL_COMMUNITY_TOKENS_CHANGED, nil) + # If there's settings without `id` it means the original # signal didn't include actual communitySettings, hence we # assign the settings we already have, otherwise we risk our diff --git a/src/app_service/service/community_tokens/service.nim b/src/app_service/service/community_tokens/service.nim index d76c2bde5a..219b1d7a54 100644 --- a/src/app_service/service/community_tokens/service.nim +++ b/src/app_service/service/community_tokens/service.nim @@ -221,7 +221,6 @@ const SIGNAL_COMMUNITY_TOKEN_REMOVED* = "communityTokens-communityTokenRemoved" const SIGNAL_OWNER_TOKEN_DEPLOY_STATUS* = "communityTokens-ownerTokenDeployStatus" const SIGNAL_OWNER_TOKEN_DEPLOYMENT_STARTED* = "communityTokens-ownerTokenDeploymentStarted" const SIGNAL_COMMUNITY_TOKENS_DETAILS_LOADED* = "communityTokens-communityTokenDetailsLoaded" -const SIGNAL_ALL_COMMUNITY_TOKENS_LOADED* = "communityTokens-allCommunityTokensLoaded" const SIGNAL_OWNER_TOKEN_RECEIVED* = "communityTokens-ownerTokenReceived" const SIGNAL_SET_SIGNER_STATUS* = "communityTokens-setSignerStatus" const SIGNAL_FINALISE_OWNERSHIP_STATUS* = "communityTokens-finaliseOwnershipStatus" @@ -250,7 +249,10 @@ QtObject: tempGasTable: Table[ContractTuple, int] # gas per contract, filled during gas computation, used during operation (deployment, mint, burn) tempTokensAndAmounts: seq[CommunityTokenAndAmount] + communityTokensCache: seq[CommunityTokenDto] + # Forward declaration + proc getAllCommunityTokensAsync*(self: Service) proc fetchAllTokenOwners*(self: Service) proc getCommunityTokenOwners*(self: Service, communityId: string, chainId: int, contractAddress: string): seq[CommunityCollectibleOwner] proc getCommunityToken*(self: Service, chainId: int, address: string): CommunityTokenDto @@ -371,7 +373,42 @@ QtObject: except Exception as e: error "Error processing set signer transaction", msg=e.msg + # cache functions + proc saveCommunityTokenAndUpdateCache(self: Service, tokenToSave: CommunityTokenDto, croppedImageJson: string): CommunityTokenDto = + let savedTokenJson = tokens_backend.saveCommunityToken(tokenToSave, croppedImageJson) + let savedToken = savedTokenJson.result.toCommunityTokenDto() + self.communityTokensCache.add(savedToken) + return savedToken + + proc updateCommunityTokenSupplyAndUpdateCache(self: Service, chainId: int, contractAddress: string, supply: Uint256) = + discard updateCommunityTokenSupply(chainId, contractAddress, supply) + for i in 0..self.communityTokensCache.len-1: + if self.communityTokensCache[i].chainId == chainId and self.communityTokensCache[i].address == contractAddress: + self.communityTokensCache[i].supply = supply + return + + proc updateCommunityTokenStateAndUpdateCache(self: Service, chainId: int, contractAddress: string, deployState: DeployState) = + discard updateCommunityTokenState(chainId, contractAddress, deployState) + for i in 0..self.communityTokensCache.len-1: + if self.communityTokensCache[i].chainId == chainId and self.communityTokensCache[i].address == contractAddress: + self.communityTokensCache[i].deployState = deployState + return + + proc updateCommunityTokenAddressAndUpdateCache(self: Service, chainId: int, contractAddress: string, newAddress: string) = + discard updateCommunityTokenAddress(chainId, contractAddress, newAddress) + for i in 0..self.communityTokensCache.len-1: + if self.communityTokensCache[i].chainId == chainId and self.communityTokensCache[i].address == contractAddress: + self.communityTokensCache[i].address = newAddress + return + + proc removeCommunityTokenAndUpdateCache(self: Service, chainId: int, contractAddress: string) = + discard tokens_backend.removeCommunityToken(chainId, contractAddress) + self.communityTokensCache = self.communityTokensCache.filter(x => ((x.chainId != chainId) or (x.address != contractAddress))) + + # end of cache functions + proc init*(self: Service) = + self.getAllCommunityTokensAsync() self.fetchAllTokenOwners() self.tokenOwnersTimer.start() @@ -394,7 +431,7 @@ QtObject: if not receivedData.success: error "Community contract not deployed", chainId=tokenDto.chainId, address=tokenDto.address try: - discard updateCommunityTokenState(tokenDto.chainId, tokenDto.address, deployState) #update db state + self.updateCommunityTokenStateAndUpdateCache(tokenDto.chainId, tokenDto.address, deployState) # now add community token to community and publish update let response = tokens_backend.addCommunityToken(tokenDto.communityId, tokenDto.chainId, tokenDto.address) if response.error != nil: @@ -435,12 +472,12 @@ QtObject: debug "Minted master token contract address:", masterContractAddress # update master token address - discard updateCommunityTokenAddress(ownerTransactionDetails.masterToken.chainId, ownerTransactionDetails.masterToken.address, masterContractAddress) + self.updateCommunityTokenAddressAndUpdateCache(ownerTransactionDetails.masterToken.chainId, ownerTransactionDetails.masterToken.address, masterContractAddress) # update owner token address - discard updateCommunityTokenAddress(ownerTransactionDetails.ownerToken.chainId, ownerTransactionDetails.ownerToken.address, ownerContractAddress) + self.updateCommunityTokenAddressAndUpdateCache(ownerTransactionDetails.ownerToken.chainId, ownerTransactionDetails.ownerToken.address, ownerContractAddress) #update db state for owner and master token - discard updateCommunityTokenState(ownerTransactionDetails.ownerToken.chainId, ownerContractAddress, deployState) - discard updateCommunityTokenState(ownerTransactionDetails.masterToken.chainId, masterContractAddress, deployState) + self.updateCommunityTokenStateAndUpdateCache(ownerTransactionDetails.ownerToken.chainId, ownerContractAddress, deployState) + self.updateCommunityTokenStateAndUpdateCache(ownerTransactionDetails.masterToken.chainId, masterContractAddress, deployState) # now add owner token to community and publish update var response = tokens_backend.addCommunityToken(ownerTransactionDetails.communityId, ownerTransactionDetails.ownerToken.chainId, ownerContractAddress) if response.error != nil: @@ -472,7 +509,7 @@ QtObject: let data = AirdropArgs(communityToken: tokenDto, transactionHash: receivedData.transactionHash, status: transactionStatus) self.events.emit(SIGNAL_AIRDROP_STATUS, data) - # update owners list if burn was successfull + # update owners list if airdrop was successfull if receivedData.success: self.tempTokenOwnersToFetch = tokenDto self.tokenOwners1SecTimer.start() @@ -488,7 +525,7 @@ QtObject: let data = RemoteDestructArgs(communityToken: tokenDto, transactionHash: receivedData.transactionHash, status: transactionStatus, remoteDestructAddresses: @[]) self.events.emit(SIGNAL_REMOTE_DESTRUCT_STATUS, data) - # update owners list if burn was successfull + # update owners list if remote destruct was successfull if receivedData.success: self.tempTokenOwnersToFetch = tokenDto self.tokenOwners1SecTimer.start() @@ -502,7 +539,7 @@ QtObject: let transactionStatus = if receivedData.success: ContractTransactionStatus.Completed else: ContractTransactionStatus.Failed if receivedData.success: try: - discard updateCommunityTokenSupply(tokenDto.chainId, tokenDto.address, tokenDto.supply) #update db state + self.updateCommunityTokenSupplyAndUpdateCache(tokenDto.chainId, tokenDto.address, tokenDto.supply) except RpcException: error "Error updating collectibles supply", message = getCurrentExceptionMsg() let data = RemoteDestructArgs(communityToken: tokenDto, transactionHash: receivedData.transactionHash, status: transactionStatus) @@ -545,9 +582,7 @@ QtObject: var croppedImage = croppedImageJson.parseJson croppedImage{"imagePath"} = newJString(singletonInstance.utils.formatImagePath(croppedImage["imagePath"].getStr)) - # save token to db - let communityTokenJson = tokens_backend.saveCommunityToken(communityToken, $croppedImage) - let addedCommunityToken = communityTokenJson.result.toCommunityTokenDto() + let addedCommunityToken = self.saveCommunityTokenAndUpdateCache(communityToken, $croppedImage) let data = CommunityTokenDeploymentArgs(communityToken: addedCommunityToken, transactionHash: transactionHash) self.events.emit(SIGNAL_COMMUNITY_TOKEN_DEPLOYMENT_STARTED, data) @@ -597,11 +632,8 @@ QtObject: ownerToken.image = croppedImage{"imagePath"}.getStr masterToken.image = croppedImage{"imagePath"}.getStr - let ownerTokenJson = tokens_backend.saveCommunityToken(ownerToken, "") - let addedOwnerToken = ownerTokenJson.result.toCommunityTokenDto() - - let masterTokenJson = tokens_backend.saveCommunityToken(masterToken, "") - let addedMasterToken = masterTokenJson.result.toCommunityTokenDto() + let addedOwnerToken = self.saveCommunityTokenAndUpdateCache(ownerToken, "") + let addedMasterToken = self.saveCommunityTokenAndUpdateCache(masterToken, "") let data = OwnerTokenDeploymentArgs(ownerToken: addedOwnerToken, masterToken: addedMasterToken, transactionHash: transactionHash) self.events.emit(SIGNAL_OWNER_TOKEN_DEPLOYMENT_STARTED, data) @@ -655,18 +687,10 @@ QtObject: self.events.emit(SIGNAL_COMMUNITY_TOKEN_DEPLOY_STATUS, data) proc getCommunityTokens*(self: Service, communityId: string): seq[CommunityTokenDto] = - try: - let response = tokens_backend.getCommunityTokens(communityId) - return parseCommunityTokens(response) - except RpcException: - error "Error getting community tokens", message = getCurrentExceptionMsg() + return self.communityTokensCache.filter(x => (x.communityId == communityId)) proc getAllCommunityTokens*(self: Service): seq[CommunityTokenDto] = - try: - let response = tokens_backend.getAllCommunityTokens() - return parseCommunityTokens(response) - except RpcException: - error "Error getting all community tokens", message = getCurrentExceptionMsg() + return self.communityTokensCache proc getCommunityTokensDetailsAsync*(self: Service, communityId: string) = let arg = GetCommunityTokensDetailsArg( @@ -707,22 +731,17 @@ QtObject: proc onGotAllCommunityTokens*(self:Service, response: string) {.slot.} = try: let responseJson = parseJson(response) - let communityTokens = map(responseJson["response"]["result"].getElems(), + self.communityTokensCache = map(responseJson["response"]["result"].getElems(), proc(x: JsonNode): CommunityTokenDto = x.toCommunityTokenDto()) - self.events.emit(SIGNAL_ALL_COMMUNITY_TOKENS_LOADED, CommunityTokensArgs(communityTokens: communityTokens)) except RpcException as e: error "Error getting all community tokens async", message = e.msg proc removeCommunityToken*(self: Service, communityId: string, chainId: int, address: string) = try: - let response = tokens_backend.removeCommunityToken(chainId, address) - if response.error != nil: - let error = Json.decode($response.error, RpcError) - raise newException(RpcException, "error removing community token: " & error.message) + self.removeCommunityTokenAndUpdateCache(chainId, address) self.events.emit(SIGNAL_COMMUNITY_TOKEN_REMOVED, CommunityTokenRemovedArgs(communityId: communityId, contractAddress: address, chainId: chainId)) - - except RpcException as e: - error "Error removing community token", message = getCurrentExceptionMsg() + except Exception as e: + error "Error removing community token", message = e.msg self.events.emit(SIGNAL_REMOVE_COMMUNITY_TOKEN_FAILED, Args()) proc getCommunityTokenBySymbol*(self: Service, communityId: string, symbol: string): CommunityTokenDto =