import type { NftEntity, TokenEntity } from "../types/db.types"; import { getAssetMetadata } from "../utils/asset"; import { currentTimestamp, tokenIdToHex } from "../utils/common"; import type { WalletDB } from "../persistence/wallet-db"; import type { WalletCache } from "./cache"; import type { AssetInfo, Balance } from "../types/wallet.types"; export class AssetService { private readonly walletDb: WalletDB; private readonly walletCache: WalletCache; constructor(walletDb: WalletDB, walletCache: WalletCache) { this.walletDb = walletDb; this.walletCache = walletCache; } public async getAssetInfo(assetId: string): Promise { try { if (this.walletCache.hasToken(assetId)) { const token = await this.walletCache.getToken(assetId); return { type: "token", parent: token!.parent, token: token!.token, tokenIdHex: token!.tokenIdHex, data: { name: token!.name, ticker: token!.ticker, decimals: token!.decimals, iconUrl: token!.iconUrl } } } if (this.walletCache.hasNft(assetId)) { const nft = await this.walletCache.getNft(assetId); return { type: "nft", parent: nft!.parent, token: nft!.token, tokenIdHex: nft!.tokenIdHex, data: { name: nft!.name, series: nft!.series, collection: nft!.collection, author: nft!.author, public: nft!.public, front: nft!.front, back: nft!.back } } } return await getAssetMetadata(assetId); } catch (e) { console.error(e); return undefined; } } public async handleNftReceive(accountId: number, tokenIdHex: string, time: number): Promise { const nft = await this.getAssetInfo(tokenIdHex); if (nft && nft.type == 'nft') { const nftEntity: NftEntity = { token: nft.token, tokenIdHex: nft.tokenIdHex, parent: nft.parent, ...nft.data } await this.walletDb.saveNft(accountId, nftEntity, time); } } public async syncTokens(accountId: number, tokenBalances: Record): Promise { for (const tokenId in tokenBalances) { const balance = tokenBalances[tokenId]; if (balance && BigInt(balance.confirmed) + BigInt(balance.unconfirmed) > 0n) { const token = await this.getAssetInfo(tokenId); if (token && token.type == 'token') { const tokenEntity: TokenEntity = { token: token.token, tokenIdHex: token.tokenIdHex, parent: token.parent, ...token.data }; await this.walletDb.saveToken(accountId, tokenEntity); } } } } public async syncNfts(accountId: number, tokensBalance: Record): Promise { const currentAssets = Object.keys(tokensBalance); const internalAssets = await this.walletDb.getAssets(accountId, "nft"); const staleAssets = internalAssets?.filter(a => !currentAssets.includes(a.tokenIdHex)) ?? []; for (const asset of staleAssets) { await this.deleteNft(accountId, asset.tokenIdHex); } for (const [token, balance] of Object.entries(tokensBalance)) { const tokenId = tokenIdToHex(token); const hasBalance = balance && BigInt(balance.confirmed) + BigInt(balance.unconfirmed) > 0n; const isAssetExist = internalAssets?.some(a => a.tokenIdHex == tokenId); if ((!hasBalance && !isAssetExist) || (hasBalance && isAssetExist)) { continue; } if (!hasBalance && isAssetExist) { await this.deleteNft(accountId, tokenId); } else { await this.handleNftReceive(accountId, tokenId, currentTimestamp()); } } } public async deleteToken(accountId: number, tokenId: string): Promise { await this.walletDb.deleteToken(accountId, tokenId); this.walletCache.removeToken(tokenId); } public async deleteNft(accountId: number, tokenId: string): Promise { await this.walletDb.deleteNft(accountId, tokenId); this.walletCache.removeNft(tokenId); } }