diff --git a/src/endpoints/accounts/account.controller.ts b/src/endpoints/accounts/account.controller.ts index 8d77f4879..77bec37c7 100644 --- a/src/endpoints/accounts/account.controller.ts +++ b/src/endpoints/accounts/account.controller.ts @@ -240,6 +240,7 @@ export class AccountController { @ApiQuery({ name: 'from', description: 'Number of items to skip for the result set', required: false }) @ApiQuery({ name: 'size', description: 'Number of items to retrieve', required: false }) @ApiQuery({ name: 'type', description: 'Token type', required: false, enum: TokenType }) + @ApiQuery({ name: 'subType', description: 'Token sub type', required: false, enum: NftSubType }) @ApiQuery({ name: 'search', description: 'Search by collection identifier', required: false }) @ApiQuery({ name: 'name', description: 'Search by token name', required: false }) @ApiQuery({ name: 'identifier', description: 'Search by token identifier', required: false }) @@ -253,6 +254,7 @@ export class AccountController { @Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number, @Query('size', new DefaultValuePipe(25), ParseIntPipe) size: number, @Query('type', new ParseEnumPipe(TokenType)) type?: TokenType, + @Query('subType', new ParseEnumPipe(NftSubType)) subType?: NftSubType, @Query('search') search?: string, @Query('name') name?: string, @Query('identifier') identifier?: string, @@ -262,7 +264,7 @@ export class AccountController { @Query('mexPairType', new ParseEnumArrayPipe(MexPairType)) mexPairType?: MexPairType[], ): Promise { try { - return await this.tokenService.getTokensForAddress(address, new QueryPagination({ from, size }), new TokenFilter({ type, search, name, identifier, identifiers, includeMetaESDT, mexPairType })); + return await this.tokenService.getTokensForAddress(address, new QueryPagination({ from, size }), new TokenFilter({ type, subType, search, name, identifier, identifiers, includeMetaESDT, mexPairType })); } catch (error) { this.logger.error(`Error in getAccountTokens for address ${address}`); this.logger.error(error); diff --git a/src/endpoints/nfts/entities/nft.sub.type.ts b/src/endpoints/nfts/entities/nft.sub.type.ts index b0bfe15ea..d6f4a8304 100644 --- a/src/endpoints/nfts/entities/nft.sub.type.ts +++ b/src/endpoints/nfts/entities/nft.sub.type.ts @@ -8,6 +8,7 @@ export enum NftSubType { DynamicNonFungibleESDT = 'DynamicNonFungibleESDT', DynamicSemiFungibleESDT = 'DynamicSemiFungibleESDT', DynamicMetaESDT = 'DynamicMetaESDT', + None = '', } registerEnumType(NftSubType, { @@ -35,5 +36,8 @@ registerEnumType(NftSubType, { DynamicMetaESDT: { description: 'Dynamic meta ESDT NFT type.', }, + None: { + description: '', + }, }, }); diff --git a/src/endpoints/tokens/entities/token.filter.ts b/src/endpoints/tokens/entities/token.filter.ts index 0151edb82..064468c2d 100644 --- a/src/endpoints/tokens/entities/token.filter.ts +++ b/src/endpoints/tokens/entities/token.filter.ts @@ -3,6 +3,7 @@ import { TokenType } from "src/common/indexer/entities"; import { TokenSort } from "./token.sort"; import { MexPairType } from "src/endpoints/mex/entities/mex.pair.type"; import { TokenAssetsPriceSourceType } from "src/common/assets/entities/token.assets.price.source.type"; +import { NftSubType } from "../../nfts/entities/nft.sub.type"; export class TokenFilter { constructor(init?: Partial) { @@ -11,6 +12,8 @@ export class TokenFilter { type?: TokenType; + subType?: NftSubType; + search?: string; name?: string; diff --git a/src/endpoints/tokens/entities/token.ts b/src/endpoints/tokens/entities/token.ts index fc59d4b98..4ad21b516 100644 --- a/src/endpoints/tokens/entities/token.ts +++ b/src/endpoints/tokens/entities/token.ts @@ -4,6 +4,7 @@ import { TokenType } from "src/common/indexer/entities"; import { TokenAssets } from "../../../common/assets/entities/token.assets"; import { MexPairType } from "src/endpoints/mex/entities/mex.pair.type"; import { TokenOwnersHistory } from "./token.owner.history"; +import { NftSubType } from "../../nfts/entities/nft.sub.type"; export class Token { constructor(init?: Partial) { @@ -13,6 +14,9 @@ export class Token { @ApiProperty({ enum: TokenType }) type: TokenType = TokenType.FungibleESDT; + @ApiProperty({ enum: NftSubType }) + subType: NftSubType = NftSubType.None; + @ApiProperty({ type: String }) identifier: string = ''; diff --git a/src/endpoints/tokens/token.service.ts b/src/endpoints/tokens/token.service.ts index 1aa0eda8a..107f371c5 100644 --- a/src/endpoints/tokens/token.service.ts +++ b/src/endpoints/tokens/token.service.ts @@ -42,10 +42,13 @@ import { TransferService } from "../transfers/transfer.service"; import { MexPairService } from "../mex/mex.pair.service"; import { MexPairState } from "../mex/entities/mex.pair.state"; import { MexTokenType } from "../mex/entities/mex.token.type"; +import { NftSubType } from "../nfts/entities/nft.sub.type"; @Injectable() export class TokenService { private readonly logger = new OriginLogger(TokenService.name); + private readonly nftSubTypes = [NftSubType.DynamicNonFungibleESDT, NftSubType.DynamicMetaESDT, NftSubType.NonFungibleESDTv2, NftSubType.DynamicSemiFungibleESDT]; + constructor( private readonly esdtService: EsdtService, private readonly indexerService: IndexerService, @@ -113,7 +116,7 @@ export class TokenService { } async getTokens(queryPagination: QueryPagination, filter: TokenFilter): Promise { - const { from, size } = queryPagination; + const {from, size} = queryPagination; let tokens = await this.getFilteredTokens(filter); @@ -141,6 +144,10 @@ export class TokenService { tokens = tokens.filter(token => token.type === filter.type); } + if (filter.subType) { + tokens = tokens.filter(token => token.subType.toString() === filter.subType?.toString()); + } + if (filter.search) { const searchLower = filter.search.toLowerCase(); @@ -350,11 +357,11 @@ export class TokenService { if (TokenUtils.isNft(identifier)) { const nftData = await this.gatewayService.getAddressNft(address, identifier); - tokenWithBalance = new TokenDetailedWithBalance({ ...token, ...nftData }); + tokenWithBalance = new TokenDetailedWithBalance({...token, ...nftData}); } else { const esdtData = await this.gatewayService.getAddressEsdt(address, identifier); - tokenWithBalance = new TokenDetailedWithBalance({ ...token, ...esdtData }); + tokenWithBalance = new TokenDetailedWithBalance({...token, ...esdtData}); } // eslint-disable-next-line require-await @@ -398,6 +405,27 @@ export class TokenService { continue; } + if (esdt.type && this.nftSubTypes.includes(esdt.type)) { + switch (esdt.type as NftSubType) { + case NftSubType.DynamicNonFungibleESDT: + case NftSubType.NonFungibleESDTv2: + esdt.type = NftSubType.NonFungibleESDT; + esdt.subType = esdt.type; + break; + case NftSubType.DynamicMetaESDT: + esdt.type = NftType.MetaESDT; + esdt.subType = NftSubType.DynamicMetaESDT; + break; + case NftSubType.DynamicSemiFungibleESDT: + esdt.type = NftType.SemiFungibleESDT; + esdt.subType = NftSubType.DynamicSemiFungibleESDT; + break; + default: + esdt.subType = NftSubType.None; + break; + } + } + const tokenWithBalance = { ...token, ...esdt, @@ -658,8 +686,6 @@ export class TokenService { return result; } - - private async getLogo(identifier: string): Promise { const assets = await this.assetsService.getTokenAssets(identifier); if (!assets) { @@ -712,7 +738,7 @@ export class TokenService { return await this.cachingService.getOrSet( CacheInfo.AllEsdtTokens.key, async () => await this.getAllTokensRaw(), - CacheInfo.AllEsdtTokens.ttl + CacheInfo.AllEsdtTokens.ttl, ); } @@ -746,6 +772,7 @@ export class TokenService { for (const collection of collections) { tokens.push(new TokenDetailed({ type: TokenType.MetaESDT, + subType: collection.subType, identifier: collection.collection, name: collection.name, timestamp: collection.timestamp, @@ -921,9 +948,9 @@ export class TokenService { private async getTotalTransactions(token: TokenDetailed): Promise<{ count: number, lastUpdatedAt: number } | undefined> { try { - const count = await this.transactionService.getTransactionCount(new TransactionFilter({ tokens: [token.identifier, ...token.assets?.extraTokens ?? []] })); + const count = await this.transactionService.getTransactionCount(new TransactionFilter({tokens: [token.identifier, ...token.assets?.extraTokens ?? []]})); - return { count, lastUpdatedAt: new Date().getTimeInSeconds() }; + return {count, lastUpdatedAt: new Date().getTimeInSeconds()}; } catch (error) { this.logger.error(`An unhandled error occurred when getting transaction count for token '${token.identifier}'`); this.logger.error(error); diff --git a/src/test/unit/services/tokens.spec.ts b/src/test/unit/services/tokens.spec.ts index faa0f32bb..222e5ae15 100644 --- a/src/test/unit/services/tokens.spec.ts +++ b/src/test/unit/services/tokens.spec.ts @@ -717,6 +717,7 @@ describe('Token Service', () => { mockNftCollections.forEach(collection => { mockTokens.push(new TokenDetailed({ type: TokenType.MetaESDT, + subType: collection.subType, identifier: collection.collection, name: collection.name, timestamp: collection.timestamp,