diff --git a/src/common/gateway/entities/gateway.component.request.ts b/src/common/gateway/entities/gateway.component.request.ts index a3821082c..066b06904 100644 --- a/src/common/gateway/entities/gateway.component.request.ts +++ b/src/common/gateway/entities/gateway.component.request.ts @@ -5,6 +5,7 @@ export enum GatewayComponentRequest { networkEconomics = 'networkEconomics', networkTotalStaked = 'networkTotalStaked', addressDetails = 'addressDetails', + addressesBulk = 'addressesBulk', addressEsdt = 'addressEsdt', addressEsdtHistorical = 'addressEsdtHistorical', addressEsdtBalance = 'addressEsdtBalance', diff --git a/src/common/gateway/gateway.service.ts b/src/common/gateway/gateway.service.ts index 6888fbfb3..597ced989 100644 --- a/src/common/gateway/gateway.service.ts +++ b/src/common/gateway/gateway.service.ts @@ -95,6 +95,11 @@ export class GatewayService { return result; } + async getAccountsBulk(addresses: string[]): Promise { + const result = await this.create('address/bulk', GatewayComponentRequest.addressesBulk, addresses); + return result.accounts; + } + async getEsdtSupply(identifier: string): Promise { const result = await this.get(`network/esdt/supply/${identifier}`, GatewayComponentRequest.esdtSupply); return result; diff --git a/src/common/indexer/elastic/elastic.indexer.helper.ts b/src/common/indexer/elastic/elastic.indexer.helper.ts index be10ff0a9..adb19699c 100644 --- a/src/common/indexer/elastic/elastic.indexer.helper.ts +++ b/src/common/indexer/elastic/elastic.indexer.helper.ts @@ -18,6 +18,7 @@ import { SmartContractResultFilter } from "src/endpoints/sc-results/entities/sma import { ApplicationFilter } from "src/endpoints/applications/entities/application.filter"; import { NftType } from "../entities/nft.type"; import { EventsFilter } from "src/endpoints/events/entities/events.filter"; +import { ScriptQuery } from "./script.query"; @Injectable() export class ElasticIndexerHelper { @@ -93,7 +94,7 @@ export class ElasticIndexerHelper { QueryType.Nested('roles', [new MatchQuery('roles.ESDTRoleNFTUpdateAttributes', address)]), QueryType.Nested('roles', [new MatchQuery('roles.ESDTRoleNFTAddURI', address)]), QueryType.Nested('roles', [new MatchQuery('roles.ESDTTransferRole', address)]), - ] + ], )); } @@ -253,7 +254,7 @@ export class ElasticIndexerHelper { QueryType.Should([ QueryType.Match('nft_scamInfoType', 'scam'), QueryType.Match('nft_scamInfoType', 'potentialScam'), - ]) + ]), ); } @@ -309,12 +310,16 @@ export class ElasticIndexerHelper { smartContractResultConditions.push(QueryType.Match('sender', filter.address)); } + let mustNotQueries: AbstractQuery[] = [ + QueryType.Exists('canBeIgnored'), + ]; + if (filter.withRefunds) { + mustNotQueries = []; + } elasticQuery = elasticQuery.withCondition(QueryConditionOptions.should, QueryType.Must([ QueryType.Match('type', 'unsigned'), QueryType.Should(smartContractResultConditions), - ], [ - QueryType.Exists('canBeIgnored'), - ])) + ], mustNotQueries)) .withCondition(QueryConditionOptions.should, QueryType.Must([ QueryType.Should([QueryType.Match('type', 'normal')]), QueryType.Should([ @@ -429,7 +434,7 @@ export class ElasticIndexerHelper { [ QueryType.Match('currentOwner', address), ...rolesConditions, - ] + ], )) .withMustMatchCondition('token', filter.identifier) .withMustMatchCondition('currentOwner', filter.owner); @@ -507,9 +512,22 @@ export class ElasticIndexerHelper { } public buildTransactionFilterQuery(filter: TransactionFilter, address?: string): ElasticQuery { - let elasticQuery = ElasticQuery.create() - .withMustMatchCondition('type', 'normal') - .withMustMatchCondition('senderShard', filter.senderShard) + let elasticQuery = ElasticQuery.create(); + + if (!filter.withRelayedScresults) { + elasticQuery = elasticQuery.withMustMatchCondition('type', 'normal'); + } else { + elasticQuery = elasticQuery.withShouldCondition([ + QueryType.Match('type', 'normal'), + QueryType.Must([ + QueryType.Exists('relayerAddr'), + QueryType.Match('type', 'unsigned'), + new ScriptQuery(`doc['originalTxHash'].size() > 0 && doc['prevTxHash'].size() > 0 && doc['originalTxHash'].value == doc['prevTxHash'].value`), + ]), + ]); + } + + elasticQuery = elasticQuery.withMustMatchCondition('senderShard', filter.senderShard) .withMustMatchCondition('receiverShard', filter.receiverShard) .withMustMatchCondition('miniBlockHash', filter.miniBlockHash) .withMustMultiShouldCondition(filter.hashes, hash => QueryType.Match('_id', hash)) diff --git a/src/common/indexer/elastic/script.query.ts b/src/common/indexer/elastic/script.query.ts new file mode 100644 index 000000000..fa0b75ac3 --- /dev/null +++ b/src/common/indexer/elastic/script.query.ts @@ -0,0 +1,14 @@ +import { AbstractQuery } from "@multiversx/sdk-nestjs-elastic"; + +// TODO: remove this and use ScriptQuery from sdk-nestjs when PR #247 is merged +export class ScriptQuery extends AbstractQuery { + constructor( + private readonly source: string | undefined, + ) { + super(); + } + + getQuery(): any { + return { script: { script: { source: this.source, lang: 'painless' } } }; + } +} diff --git a/src/endpoints/accounts/account.controller.ts b/src/endpoints/accounts/account.controller.ts index 9af332110..e1387bb56 100644 --- a/src/endpoints/accounts/account.controller.ts +++ b/src/endpoints/accounts/account.controller.ts @@ -96,26 +96,29 @@ export class AccountController { @ApiQuery({ name: 'excludeTags', description: 'Exclude specific tags from result', required: false }) @ApiQuery({ name: 'hasAssets', description: 'Returns a list of accounts that have assets', required: false }) @ApiQuery({ name: 'search', description: 'Search by account address', required: false }) + @ApiQuery({ name: 'addresses', description: 'A comma-separated list of addresses to filter by', required: false, type: String }) getAccounts( @Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number, @Query("size", new DefaultValuePipe(25), ParseIntPipe) size: number, @Query("ownerAddress", ParseAddressPipe) ownerAddress?: string, @Query("name") name?: string, - @Query("tags", new ParseArrayPipe()) tags?: string[], + @Query("tags", ParseArrayPipe) tags?: string[], @Query('sort', new ParseEnumPipe(AccountSort)) sort?: AccountSort, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, - @Query("isSmartContract", new ParseBoolPipe) isSmartContract?: boolean, - @Query("withOwnerAssets", new ParseBoolPipe) withOwnerAssets?: boolean, - @Query("withDeployInfo", new ParseBoolPipe) withDeployInfo?: boolean, - @Query("withTxCount", new ParseBoolPipe) withTxCount?: boolean, - @Query("withScrCount", new ParseBoolPipe) withScrCount?: boolean, - @Query("excludeTags", new ParseArrayPipe) excludeTags?: string[], - @Query("hasAssets", new ParseBoolPipe) hasAssets?: boolean, + @Query("isSmartContract", ParseBoolPipe) isSmartContract?: boolean, + @Query("withOwnerAssets", ParseBoolPipe) withOwnerAssets?: boolean, + @Query("withDeployInfo", ParseBoolPipe) withDeployInfo?: boolean, + @Query("withTxCount", ParseBoolPipe) withTxCount?: boolean, + @Query("withScrCount", ParseBoolPipe) withScrCount?: boolean, + @Query("excludeTags", ParseArrayPipe) excludeTags?: string[], + @Query("hasAssets", ParseBoolPipe) hasAssets?: boolean, @Query("search") search?: string, + @Query("addresses", ParseAddressArrayPipe) addresses?: string[], ): Promise { const queryOptions = new AccountQueryOptions( { ownerAddress, + addresses, sort, order, isSmartContract, @@ -147,11 +150,11 @@ export class AccountController { @ApiQuery({ name: 'hasAssets', description: 'Returns a list of accounts that have assets', required: false }) async getAccountsCount( @Query("ownerAddress", ParseAddressPipe) ownerAddress?: string, - @Query("isSmartContract", new ParseBoolPipe) isSmartContract?: boolean, + @Query("isSmartContract", ParseBoolPipe) isSmartContract?: boolean, @Query("name") name?: string, - @Query("tags", new ParseArrayPipe()) tags?: string[], - @Query("excludeTags", new ParseArrayPipe) excludeTags?: string[], - @Query("hasAssets", new ParseBoolPipe) hasAssets?: boolean, + @Query("tags", ParseArrayPipe) tags?: string[], + @Query("excludeTags", ParseArrayPipe) excludeTags?: string[], + @Query("hasAssets", ParseBoolPipe) hasAssets?: boolean, ): Promise { return await this.accountService.getAccountsCount( new AccountQueryOptions( @@ -169,11 +172,11 @@ export class AccountController { @ApiExcludeEndpoint() async getAccountsCountAlternative( @Query("ownerAddress", ParseAddressPipe) ownerAddress?: string, - @Query("isSmartContract", new ParseBoolPipe) isSmartContract?: boolean, + @Query("isSmartContract", ParseBoolPipe) isSmartContract?: boolean, @Query("name") name?: string, - @Query("tags", new ParseArrayPipe()) tags?: string[], - @Query("excludeTags", new ParseArrayPipe) excludeTags?: string[], - @Query("hasAssets", new ParseBoolPipe) hasAssets?: boolean, + @Query("tags", ParseArrayPipe) tags?: string[], + @Query("excludeTags", ParseArrayPipe) excludeTags?: string[], + @Query("hasAssets", ParseBoolPipe) hasAssets?: boolean, ): Promise { return await this.accountService.getAccountsCount( new AccountQueryOptions( @@ -196,7 +199,7 @@ export class AccountController { @ApiOkResponse({ type: AccountDetailed }) async getAccountDetails( @Param('address', ParseAddressPipe) address: string, - @Query('withGuardianInfo', new ParseBoolPipe) withGuardianInfo?: boolean, + @Query('withGuardianInfo', ParseBoolPipe) withGuardianInfo?: boolean, @Query('fields', ParseArrayPipe) fields?: string[], @Query('timestamp', ParseIntPipe) _timestamp?: number, ): Promise { @@ -259,7 +262,7 @@ export class AccountController { @Query('name') name?: string, @Query('identifier') identifier?: string, @Query('identifiers', ParseArrayPipe) identifiers?: string[], - @Query('includeMetaESDT', new ParseBoolPipe) includeMetaESDT?: boolean, + @Query('includeMetaESDT', ParseBoolPipe) includeMetaESDT?: boolean, @Query('timestamp', ParseIntPipe) _timestamp?: number, @Query('mexPairType', new ParseEnumArrayPipe(MexPairType)) mexPairType?: MexPairType[], ): Promise { @@ -292,7 +295,7 @@ export class AccountController { @Query('name') name?: string, @Query('identifier') identifier?: string, @Query('identifiers', ParseArrayPipe) identifiers?: string[], - @Query('includeMetaESDT', new ParseBoolPipe) includeMetaESDT?: boolean, + @Query('includeMetaESDT', ParseBoolPipe) includeMetaESDT?: boolean, @Query('timestamp', ParseIntPipe) _timestamp?: number, @Query('mexPairType', new ParseEnumArrayPipe(MexPairType)) mexPairType?: MexPairType[], ): Promise { @@ -316,7 +319,7 @@ export class AccountController { @Query('name') name?: string, @Query('identifier') identifier?: string, @Query('identifiers', ParseArrayPipe) identifiers?: string[], - @Query('includeMetaESDT', new ParseBoolPipe) includeMetaESDT?: boolean, + @Query('includeMetaESDT', ParseBoolPipe) includeMetaESDT?: boolean, @Query('timestamp', ParseIntPipe) _timestamp?: number, @Query('mexPairType', new ParseEnumArrayPipe(MexPairType)) mexPairType?: MexPairType[], ): Promise { @@ -372,13 +375,13 @@ export class AccountController { @Query('type', new ParseEnumArrayPipe(NftType)) type?: NftType[], @Query('subType', new ParseEnumArrayPipe(NftSubType)) subType?: NftSubType[], @Query('owner', ParseAddressPipe) owner?: string, - @Query('canCreate', new ParseBoolPipe) canCreate?: boolean, - @Query('canBurn', new ParseBoolPipe) canBurn?: boolean, - @Query('canAddQuantity', new ParseBoolPipe) canAddQuantity?: boolean, - @Query('canUpdateAttributes', new ParseBoolPipe) canUpdateAttributes?: boolean, - @Query('canAddUri', new ParseBoolPipe) canAddUri?: boolean, - @Query('canTransferRole', new ParseBoolPipe) canTransferRole?: boolean, - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('canCreate', ParseBoolPipe) canCreate?: boolean, + @Query('canBurn', ParseBoolPipe) canBurn?: boolean, + @Query('canAddQuantity', ParseBoolPipe) canAddQuantity?: boolean, + @Query('canUpdateAttributes', ParseBoolPipe) canUpdateAttributes?: boolean, + @Query('canAddUri', ParseBoolPipe) canAddUri?: boolean, + @Query('canTransferRole', ParseBoolPipe) canTransferRole?: boolean, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, ): Promise { return await this.collectionService.getCollectionsWithRolesForAddress( address, @@ -414,10 +417,10 @@ export class AccountController { @Query('type', new ParseEnumArrayPipe(NftType)) type?: NftType[], @Query('subType', new ParseEnumArrayPipe(NftSubType)) subType?: NftSubType[], @Query('owner', ParseAddressPipe) owner?: string, - @Query('canCreate', new ParseBoolPipe) canCreate?: boolean, - @Query('canBurn', new ParseBoolPipe) canBurn?: boolean, - @Query('canAddQuantity', new ParseBoolPipe) canAddQuantity?: boolean, - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('canCreate', ParseBoolPipe) canCreate?: boolean, + @Query('canBurn', ParseBoolPipe) canBurn?: boolean, + @Query('canAddQuantity', ParseBoolPipe) canAddQuantity?: boolean, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, ): Promise { return await this.collectionService.getCollectionCountForAddressWithRoles(address, new CollectionFilter({ search, type, subType, owner, canCreate, canBurn, canAddQuantity, excludeMetaESDT })); } @@ -430,10 +433,10 @@ export class AccountController { @Query('type', new ParseEnumArrayPipe(NftType)) type?: NftType[], @Query('subType', new ParseEnumArrayPipe(NftSubType)) subType?: NftSubType[], @Query('owner', ParseAddressPipe) owner?: string, - @Query('canCreate', new ParseBoolPipe) canCreate?: boolean, - @Query('canBurn', new ParseBoolPipe) canBurn?: boolean, - @Query('canAddQuantity', new ParseBoolPipe) canAddQuantity?: boolean, - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('canCreate', ParseBoolPipe) canCreate?: boolean, + @Query('canBurn', ParseBoolPipe) canBurn?: boolean, + @Query('canAddQuantity', ParseBoolPipe) canAddQuantity?: boolean, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, ): Promise { return await this.collectionService.getCollectionCountForAddressWithRoles(address, new CollectionFilter({ search, type, subType, owner, canCreate, canBurn, canAddQuantity, excludeMetaESDT, @@ -471,9 +474,9 @@ export class AccountController { @Query('size', new DefaultValuePipe(25), ParseIntPipe) size: number, @Query('search') search?: string, @Query('owner', ParseAddressPipe) owner?: string, - @Query('canMint', new ParseBoolPipe) canMint?: boolean, - @Query('canBurn', new ParseBoolPipe) canBurn?: boolean, - @Query('includeMetaESDT', new ParseBoolPipe) includeMetaESDT?: boolean, + @Query('canMint', ParseBoolPipe) canMint?: boolean, + @Query('canBurn', ParseBoolPipe) canBurn?: boolean, + @Query('includeMetaESDT', ParseBoolPipe) includeMetaESDT?: boolean, ): Promise { return await this.tokenService.getTokensWithRolesForAddress(address, new TokenWithRolesFilter({ search, owner, canMint, canBurn, includeMetaESDT }), new QueryPagination({ from, size })); } @@ -490,9 +493,9 @@ export class AccountController { @Param('address', ParseAddressPipe) address: string, @Query('search') search?: string, @Query('owner', ParseAddressPipe) owner?: string, - @Query('canMint', new ParseBoolPipe) canMint?: boolean, - @Query('canBurn', new ParseBoolPipe) canBurn?: boolean, - @Query('includeMetaESDT', new ParseBoolPipe) includeMetaESDT?: boolean, + @Query('canMint', ParseBoolPipe) canMint?: boolean, + @Query('canBurn', ParseBoolPipe) canBurn?: boolean, + @Query('includeMetaESDT', ParseBoolPipe) includeMetaESDT?: boolean, ): Promise { return await this.tokenService.getTokensWithRolesForAddressCount(address, new TokenWithRolesFilter({ search, owner, canMint, canBurn, includeMetaESDT })); } @@ -503,9 +506,9 @@ export class AccountController { @Param('address', ParseAddressPipe) address: string, @Query('search') search?: string, @Query('owner', ParseAddressPipe) owner?: string, - @Query('canMint', new ParseBoolPipe) canMint?: boolean, - @Query('canBurn', new ParseBoolPipe) canBurn?: boolean, - @Query('includeMetaESDT', new ParseBoolPipe) includeMetaESDT?: boolean, + @Query('canMint', ParseBoolPipe) canMint?: boolean, + @Query('canBurn', ParseBoolPipe) canBurn?: boolean, + @Query('includeMetaESDT', ParseBoolPipe) includeMetaESDT?: boolean, ): Promise { return await this.tokenService.getTokensWithRolesForAddressCount(address, new TokenWithRolesFilter({ search, owner, canMint, canBurn, includeMetaESDT })); } @@ -541,7 +544,7 @@ export class AccountController { @Query('search') search?: string, @Query('type', new ParseEnumArrayPipe(NftType)) type?: NftType[], @Query('subType', new ParseEnumArrayPipe(NftSubType)) subType?: NftSubType[], - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, ): Promise { return await this.collectionService.getCollectionsForAddress( address, @@ -561,7 +564,7 @@ export class AccountController { @Query('search') search?: string, @Query('type', new ParseEnumArrayPipe(NftType)) type?: NftType[], @Query('subType', new ParseEnumArrayPipe(NftSubType)) subType?: NftSubType[], - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, ): Promise { return await this.collectionService.getCollectionCountForAddress(address, new CollectionFilter({ search, type, subType, excludeMetaESDT })); } @@ -573,7 +576,7 @@ export class AccountController { @Query('search') search?: string, @Query('type', new ParseEnumArrayPipe(NftType)) type?: NftType[], @Query('subType', new ParseEnumArrayPipe(NftSubType)) subType?: NftSubType[], - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, ): Promise { return await this.collectionService.getCollectionCountForAddress(address, new CollectionFilter({ search, type, subType, excludeMetaESDT })); } @@ -632,13 +635,13 @@ export class AccountController { @Query('name') name?: string, @Query('tags', ParseArrayPipe) tags?: string[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('hasUris', new ParseBoolPipe) hasUris?: boolean, - @Query('includeFlagged', new ParseBoolPipe) includeFlagged?: boolean, - @Query('withSupply', new ParseBoolPipe) withSupply?: boolean, + @Query('hasUris', ParseBoolPipe) hasUris?: boolean, + @Query('includeFlagged', ParseBoolPipe) includeFlagged?: boolean, + @Query('withSupply', ParseBoolPipe) withSupply?: boolean, @Query('source', new ParseEnumPipe(EsdtDataSource)) source?: EsdtDataSource, - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, @Query('fields', ParseArrayPipe) fields?: string[], - @Query('isScam', new ParseBoolPipe) isScam?: boolean, + @Query('isScam', ParseBoolPipe) isScam?: boolean, @Query('scamType', new ParseEnumPipe(ScamType)) scamType?: ScamType, @Query('timestamp', ParseIntPipe) _timestamp?: number, ): Promise { @@ -697,10 +700,10 @@ export class AccountController { @Query('name') name?: string, @Query('tags', ParseArrayPipe) tags?: string[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('hasUris', new ParseBoolPipe) hasUris?: boolean, - @Query('includeFlagged', new ParseBoolPipe) includeFlagged?: boolean, - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, - @Query('isScam', new ParseBoolPipe) isScam?: boolean, + @Query('hasUris', ParseBoolPipe) hasUris?: boolean, + @Query('includeFlagged', ParseBoolPipe) includeFlagged?: boolean, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('isScam', ParseBoolPipe) isScam?: boolean, @Query('scamType', new ParseEnumPipe(ScamType)) scamType?: ScamType, @Query('timestamp', ParseIntPipe) _timestamp?: number, ): Promise { @@ -736,10 +739,10 @@ export class AccountController { @Query('name') name?: string, @Query('tags', ParseArrayPipe) tags?: string[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('hasUris', new ParseBoolPipe) hasUris?: boolean, - @Query('includeFlagged', new ParseBoolPipe) includeFlagged?: boolean, - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, - @Query('isScam', new ParseBoolPipe) isScam?: boolean, + @Query('hasUris', ParseBoolPipe) hasUris?: boolean, + @Query('includeFlagged', ParseBoolPipe) includeFlagged?: boolean, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('isScam', ParseBoolPipe) isScam?: boolean, @Query('scamType', new ParseEnumPipe(ScamType)) scamType?: ScamType, @Query('timestamp', ParseIntPipe) _timestamp?: number, ): Promise { @@ -865,6 +868,7 @@ export class AccountController { @ApiQuery({ name: 'senderOrReceiver', description: 'One address that current address interacted with', required: false }) @ApiQuery({ name: 'isRelayed', description: 'Returns isRelayed transactions details', required: false, type: Boolean }) @ApiQuery({ name: 'withActionTransferValue', description: 'Returns value in USD and EGLD for transferred tokens within the action attribute', required: false }) + @ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean }) async getAccountTransactions( @Param('address', ParseAddressPipe) address: string, @Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number, @@ -883,19 +887,20 @@ export class AccountController { @Query('round', ParseIntPipe) round?: number, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, @Query('fields', ParseArrayPipe) fields?: string[], - @Query('withScResults', new ParseBoolPipe) withScResults?: boolean, - @Query('withOperations', new ParseBoolPipe) withOperations?: boolean, - @Query('withLogs', new ParseBoolPipe) withLogs?: boolean, - @Query('withScamInfo', new ParseBoolPipe) withScamInfo?: boolean, - @Query('withUsername', new ParseBoolPipe) withUsername?: boolean, - @Query('withBlockInfo', new ParseBoolPipe) withBlockInfo?: boolean, + @Query('withScResults', ParseBoolPipe) withScResults?: boolean, + @Query('withOperations', ParseBoolPipe) withOperations?: boolean, + @Query('withLogs', ParseBoolPipe) withLogs?: boolean, + @Query('withScamInfo', ParseBoolPipe) withScamInfo?: boolean, + @Query('withUsername', ParseBoolPipe) withUsername?: boolean, + @Query('withBlockInfo', ParseBoolPipe) withBlockInfo?: boolean, @Query('senderOrReceiver', ParseAddressPipe) senderOrReceiver?: string, - @Query('isRelayed', new ParseBoolPipe) isRelayed?: boolean, + @Query('isRelayed', ParseBoolPipe) isRelayed?: boolean, @Query('withActionTransferValue', ParseBoolPipe) withActionTransferValue?: boolean, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, ) { const options = TransactionQueryOptions.applyDefaultOptions(size, { withScResults, withOperations, withLogs, withScamInfo, withUsername, withBlockInfo, withActionTransferValue }); - return await this.transactionService.getTransactions(new TransactionFilter({ + const transactionFilter = new TransactionFilter({ sender, receivers: receiver, token, @@ -911,7 +916,10 @@ export class AccountController { senderOrReceiver, isRelayed, round, - }), new QueryPagination({ from, size }), options, address, fields); + withRelayedScresults, + }); + TransactionFilter.validate(transactionFilter, size); + return await this.transactionService.getTransactions(transactionFilter, new QueryPagination({ from, size }), options, address, fields); } @Get("/accounts/:address/transactions/count") @@ -931,6 +939,7 @@ export class AccountController { @ApiQuery({ name: 'round', description: 'Round number', required: false }) @ApiQuery({ name: 'senderOrReceiver', description: 'One address that current address interacted with', required: false }) @ApiQuery({ name: 'isRelayed', description: 'Returns isRelayed transactions details', required: false, type: Boolean }) + @ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean }) async getAccountTransactionsCount( @Param('address', ParseAddressPipe) address: string, @Query('sender', ParseAddressPipe) sender?: string, @@ -946,7 +955,9 @@ export class AccountController { @Query('after', ParseIntPipe) after?: number, @Query('round', ParseIntPipe) round?: number, @Query('senderOrReceiver', ParseAddressPipe) senderOrReceiver?: string, - @Query('isRelayed', new ParseBoolPipe) isRelayed?: boolean, + @Query('isRelayed', ParseBoolPipe) isRelayed?: boolean, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, + ): Promise { return await this.transactionService.getTransactionCount(new TransactionFilter({ @@ -964,6 +975,7 @@ export class AccountController { senderOrReceiver, isRelayed, round, + withRelayedScresults, }), address); } @@ -995,6 +1007,7 @@ export class AccountController { @ApiQuery({ name: 'withLogs', description: 'Return logs for transfers. When "withLogs" parameter is applied, complexity estimation is 200', required: false }) @ApiQuery({ name: 'withOperations', description: 'Return operations for transfers. When "withOperations" parameter is applied, complexity estimation is 200', required: false }) @ApiQuery({ name: 'withActionTransferValue', description: 'Returns value in USD and EGLD for transferred tokens within the action attribute', required: false }) + @ApiQuery({ name: 'withRefunds', description: 'Include refund transactions', required: false }) async getAccountTransfers( @Param('address', ParseAddressPipe) address: string, @Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number, @@ -1014,13 +1027,14 @@ export class AccountController { @Query('fields', ParseArrayPipe) fields?: string[], @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, @Query('relayer', ParseAddressPipe) relayer?: string, - @Query('withScamInfo', new ParseBoolPipe) withScamInfo?: boolean, - @Query('withUsername', new ParseBoolPipe) withUsername?: boolean, - @Query('withBlockInfo', new ParseBoolPipe) withBlockInfo?: boolean, + @Query('withScamInfo', ParseBoolPipe) withScamInfo?: boolean, + @Query('withUsername', ParseBoolPipe) withUsername?: boolean, + @Query('withBlockInfo', ParseBoolPipe) withBlockInfo?: boolean, @Query('senderOrReceiver', ParseAddressPipe) senderOrReceiver?: string, - @Query('withLogs', new ParseBoolPipe) withLogs?: boolean, - @Query('withOperations', new ParseBoolPipe) withOperations?: boolean, + @Query('withLogs', ParseBoolPipe) withLogs?: boolean, + @Query('withOperations', ParseBoolPipe) withOperations?: boolean, @Query('withActionTransferValue', ParseBoolPipe) withActionTransferValue?: boolean, + @Query('withRefunds', ParseBoolPipe) withRefunds?: boolean, ): Promise { const options = TransactionQueryOptions.applyDefaultOptions( size, { withScamInfo, withUsername, withBlockInfo, withOperations, withLogs, withActionTransferValue }); @@ -1042,6 +1056,7 @@ export class AccountController { senderOrReceiver, relayer, round, + withRefunds, }), new QueryPagination({ from, size }), options, @@ -1065,6 +1080,7 @@ export class AccountController { @ApiQuery({ name: 'after', description: 'After timestamp', required: false }) @ApiQuery({ name: 'round', description: 'Round number', required: false }) @ApiQuery({ name: 'senderOrReceiver', description: 'One address that current address interacted with', required: false }) + @ApiQuery({ name: 'withRefunds', description: 'Include refund transactions', required: false }) async getAccountTransfersCount( @Param('address', ParseAddressPipe) address: string, @Query('sender', ParseAddressArrayPipe) sender?: string[], @@ -1080,6 +1096,7 @@ export class AccountController { @Query('after', ParseIntPipe) after?: number, @Query('round', ParseIntPipe) round?: number, @Query('senderOrReceiver', ParseAddressPipe) senderOrReceiver?: string, + @Query('withRefunds', ParseBoolPipe) withRefunds?: boolean, ): Promise { return await this.transferService.getTransfersCount(new TransactionFilter({ address, @@ -1096,6 +1113,7 @@ export class AccountController { after, senderOrReceiver, round, + withRefunds, })); } @@ -1116,6 +1134,7 @@ export class AccountController { @Query('after', ParseIntPipe) after?: number, @Query('round', ParseIntPipe) round?: number, @Query('senderOrReceiver', ParseAddressPipe) senderOrReceiver?: string, + @Query('withRefunds', ParseBoolPipe) withRefunds?: boolean, ): Promise { return await this.transferService.getTransfersCount(new TransactionFilter({ address, @@ -1132,6 +1151,7 @@ export class AccountController { after, senderOrReceiver, round, + withRefunds, })); } diff --git a/src/endpoints/accounts/account.service.ts b/src/endpoints/accounts/account.service.ts index 26b3e7bad..b2fcd6462 100644 --- a/src/endpoints/accounts/account.service.ts +++ b/src/endpoints/accounts/account.service.ts @@ -328,6 +328,24 @@ export class AccountService { const verifiedAccounts = await this.cachingService.get(CacheInfo.VerifiedAccounts.key); + if (options.addresses && options.addresses.length > 0) { + const gatewayResponse: any = await this.gatewayService.getAccountsBulk(options.addresses); + const finalAccounts: Record = {}; + + for (const address in gatewayResponse) { + if (gatewayResponse.hasOwnProperty(address)) { + finalAccounts[address] = gatewayResponse[address] as AccountDetailed; + } + } + + for (const account of accounts) { + const gatewayAccount = finalAccounts[account.address]; + if (gatewayAccount) { + account.balance = gatewayAccount.balance; + } + } + } + for (const account of accounts) { account.shard = AddressUtils.computeShard(AddressUtils.bech32Decode(account.address), shardCount); account.assets = assets[account.address]; @@ -357,8 +375,6 @@ export class AccountService { if (verifiedAccounts && verifiedAccounts.includes(account.address)) { account.isVerified = true; } - - } return accounts; diff --git a/src/endpoints/accounts/entities/account.query.options.ts b/src/endpoints/accounts/entities/account.query.options.ts index 35b32bca7..91649d3c1 100644 --- a/src/endpoints/accounts/entities/account.query.options.ts +++ b/src/endpoints/accounts/entities/account.query.options.ts @@ -35,6 +35,10 @@ export class AccountQueryOptions { if (this.withScrCount && size > 25) { throw new BadRequestException('Size must be less than or equal to 25 when withScrCount is set'); } + + if (this.addresses && this.addresses.length > 25) { + throw new BadRequestException('Addresses array must contain 25 or fewer elements'); + } } isSet(): boolean { @@ -48,6 +52,7 @@ export class AccountQueryOptions { this.tags !== undefined || this.excludeTags !== undefined || this.hasAssets !== undefined || - this.search !== undefined; + this.search !== undefined || + this.addresses !== undefined; } } diff --git a/src/endpoints/collections/collection.controller.ts b/src/endpoints/collections/collection.controller.ts index 039ccd434..df5f2fa24 100644 --- a/src/endpoints/collections/collection.controller.ts +++ b/src/endpoints/collections/collection.controller.ts @@ -66,15 +66,15 @@ export class CollectionController { @Query('type', new ParseEnumArrayPipe(NftType)) type?: NftType[], @Query('subType', new ParseEnumArrayPipe(NftSubType)) subType?: NftSubType[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('before', new ParseIntPipe) before?: number, - @Query('after', new ParseIntPipe) after?: number, - @Query('canCreate', new ParseAddressPipe) canCreate?: string, - @Query('canBurn', new ParseAddressPipe) canBurn?: string, - @Query('canAddQuantity', new ParseAddressPipe) canAddQuantity?: string, - @Query('canUpdateAttributes', new ParseAddressPipe) canUpdateAttributes?: string, - @Query('canAddUri', new ParseAddressPipe) canAddUri?: string, - @Query('canTransferRole', new ParseAddressPipe) canTransferRole?: string, - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('before', ParseIntPipe) before?: number, + @Query('after', ParseIntPipe) after?: number, + @Query('canCreate', ParseAddressPipe) canCreate?: string, + @Query('canBurn', ParseAddressPipe) canBurn?: string, + @Query('canAddQuantity', ParseAddressPipe) canAddQuantity?: string, + @Query('canUpdateAttributes', ParseAddressPipe) canUpdateAttributes?: string, + @Query('canAddUri', ParseAddressPipe) canAddUri?: string, + @Query('canTransferRole', ParseAddressPipe) canTransferRole?: string, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, @Query('sort', new ParseEnumPipe(SortCollections)) sort?: SortCollections, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, ): Promise { @@ -117,15 +117,15 @@ export class CollectionController { @Query('type', new ParseEnumArrayPipe(NftType)) type?: NftType[], @Query('subType', new ParseEnumArrayPipe(NftSubType)) subType?: NftSubType[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('before', new ParseIntPipe) before?: number, - @Query('after', new ParseIntPipe) after?: number, - @Query('canCreate', new ParseAddressPipe) canCreate?: string, - @Query('canBurn', new ParseAddressPipe) canBurn?: string, - @Query('canAddQuantity', new ParseAddressPipe) canAddQuantity?: string, - @Query('canUpdateAttributes', new ParseAddressPipe) canUpdateAttributes?: string, - @Query('canAddUri', new ParseAddressPipe) canAddUri?: string, - @Query('canTransferRole', new ParseAddressPipe) canTransferRole?: string, - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('before', ParseIntPipe) before?: number, + @Query('after', ParseIntPipe) after?: number, + @Query('canCreate', ParseAddressPipe) canCreate?: string, + @Query('canBurn', ParseAddressPipe) canBurn?: string, + @Query('canAddQuantity', ParseAddressPipe) canAddQuantity?: string, + @Query('canUpdateAttributes', ParseAddressPipe) canUpdateAttributes?: string, + @Query('canAddUri', ParseAddressPipe) canAddUri?: string, + @Query('canTransferRole', ParseAddressPipe) canTransferRole?: string, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, ): Promise { return await this.collectionService.getNftCollectionCount(new CollectionFilter({ search, @@ -149,15 +149,15 @@ export class CollectionController { @Query('search') search?: string, @Query('type', new ParseEnumArrayPipe(NftType)) type?: NftType[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('before', new ParseIntPipe) before?: number, - @Query('after', new ParseIntPipe) after?: number, - @Query('canCreate', new ParseAddressPipe) canCreate?: string, - @Query('canBurn', new ParseAddressPipe) canBurn?: string, - @Query('canAddQuantity', new ParseAddressPipe) canAddQuantity?: string, - @Query('canUpdateAttributes', new ParseAddressPipe) canUpdateAttributes?: string, - @Query('canAddUri', new ParseAddressPipe) canAddUri?: string, - @Query('canTransferRole', new ParseAddressPipe) canTransferRole?: string, - @Query('excludeMetaESDT', new ParseBoolPipe) excludeMetaESDT?: boolean, + @Query('before', ParseIntPipe) before?: number, + @Query('after', ParseIntPipe) after?: number, + @Query('canCreate', ParseAddressPipe) canCreate?: string, + @Query('canBurn', ParseAddressPipe) canBurn?: string, + @Query('canAddQuantity', ParseAddressPipe) canAddQuantity?: string, + @Query('canUpdateAttributes', ParseAddressPipe) canUpdateAttributes?: string, + @Query('canAddUri', ParseAddressPipe) canAddUri?: string, + @Query('canTransferRole', ParseAddressPipe) canTransferRole?: string, + @Query('excludeMetaESDT', ParseBoolPipe) excludeMetaESDT?: boolean, ): Promise { return await this.collectionService.getNftCollectionCount(new CollectionFilter({ search, @@ -235,14 +235,14 @@ export class CollectionController { @Query('name') name?: string, @Query('tags', ParseArrayPipe) tags?: string[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('isWhitelistedStorage', new ParseBoolPipe) isWhitelistedStorage?: boolean, - @Query('hasUris', new ParseBoolPipe) hasUris?: boolean, - @Query('isNsfw', new ParseBoolPipe) isNsfw?: boolean, - @Query('traits', new ParseRecordPipe) traits?: Record, - @Query('nonceBefore', new ParseIntPipe) nonceBefore?: number, - @Query('nonceAfter', new ParseIntPipe) nonceAfter?: number, - @Query('withOwner', new ParseBoolPipe) withOwner?: boolean, - @Query('withSupply', new ParseBoolPipe) withSupply?: boolean, + @Query('isWhitelistedStorage', ParseBoolPipe) isWhitelistedStorage?: boolean, + @Query('hasUris', ParseBoolPipe) hasUris?: boolean, + @Query('isNsfw', ParseBoolPipe) isNsfw?: boolean, + @Query('traits', ParseRecordPipe) traits?: Record, + @Query('nonceBefore', ParseIntPipe) nonceBefore?: number, + @Query('nonceAfter', ParseIntPipe) nonceAfter?: number, + @Query('withOwner', ParseBoolPipe) withOwner?: boolean, + @Query('withSupply', ParseBoolPipe) withSupply?: boolean, @Query('sort', new ParseEnumPipe(SortCollectionNfts)) sort?: SortCollectionNfts, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, ): Promise { @@ -279,11 +279,11 @@ export class CollectionController { @Query('name') name?: string, @Query('tags', ParseArrayPipe) tags?: string[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('isWhitelistedStorage', new ParseBoolPipe) isWhitelistedStorage?: boolean, - @Query('hasUris', new ParseBoolPipe) hasUris?: boolean, - @Query('traits', new ParseRecordPipe) traits?: Record, - @Query('nonceBefore', new ParseIntPipe) nonceBefore?: number, - @Query('nonceAfter', new ParseIntPipe) nonceAfter?: number, + @Query('isWhitelistedStorage', ParseBoolPipe) isWhitelistedStorage?: boolean, + @Query('hasUris', ParseBoolPipe) hasUris?: boolean, + @Query('traits', ParseRecordPipe) traits?: Record, + @Query('nonceBefore', ParseIntPipe) nonceBefore?: number, + @Query('nonceAfter', ParseIntPipe) nonceAfter?: number, ): Promise { const isCollection = await this.collectionService.isCollection(collection); if (!isCollection) { @@ -336,6 +336,7 @@ export class CollectionController { @ApiQuery({ name: 'withLogs', description: 'Return logs for transactions', required: false, type: Boolean }) @ApiQuery({ name: 'withScamInfo', description: 'Returns scam information', required: false, type: Boolean }) @ApiQuery({ name: 'withUsername', description: 'Integrates username in assets for all addresses present in the transactions', required: false, type: Boolean }) + @ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean }) async getCollectionTransactions( @Param('collection', ParseCollectionPipe) identifier: string, @Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number, @@ -352,11 +353,12 @@ export class CollectionController { @Query('after', ParseIntPipe) after?: number, @Query('round', ParseIntPipe) round?: number, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, - @Query('withScResults', new ParseBoolPipe) withScResults?: boolean, - @Query('withOperations', new ParseBoolPipe) withOperations?: boolean, - @Query('withLogs', new ParseBoolPipe) withLogs?: boolean, - @Query('withScamInfo', new ParseBoolPipe) withScamInfo?: boolean, - @Query('withUsername', new ParseBoolPipe) withUsername?: boolean, + @Query('withScResults', ParseBoolPipe) withScResults?: boolean, + @Query('withOperations', ParseBoolPipe) withOperations?: boolean, + @Query('withLogs', ParseBoolPipe) withLogs?: boolean, + @Query('withScamInfo', ParseBoolPipe) withScamInfo?: boolean, + @Query('withUsername', ParseBoolPipe) withUsername?: boolean, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, ) { const options = TransactionQueryOptions.applyDefaultOptions(size, { withScResults, withOperations, withLogs, withScamInfo, withUsername }); @@ -365,7 +367,7 @@ export class CollectionController { throw new HttpException('Collection not found', HttpStatus.NOT_FOUND); } - return await this.transactionService.getTransactions(new TransactionFilter({ + const transactionFilter = new TransactionFilter({ sender, receivers: receiver, token: identifier, @@ -379,7 +381,11 @@ export class CollectionController { after, order, round, - }), new QueryPagination({ from, size }), options); + withRelayedScresults, + }); + TransactionFilter.validate(transactionFilter, size); + + return await this.transactionService.getTransactions(transactionFilter, new QueryPagination({ from, size }), options); } @Get("/collections/:collection/transactions/count") @@ -396,6 +402,7 @@ export class CollectionController { @ApiQuery({ name: 'before', description: 'Before timestamp', required: false }) @ApiQuery({ name: 'after', description: 'After timestamp', required: false }) @ApiQuery({ name: 'round', description: 'Filter by round number', required: false }) + @ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean }) async getCollectionTransactionsCount( @Param('collection', ParseCollectionPipe) identifier: string, @Query('sender', ParseAddressPipe) sender?: string, @@ -408,6 +415,7 @@ export class CollectionController { @Query('before', ParseIntPipe) before?: number, @Query('after', ParseIntPipe) after?: number, @Query('round', ParseIntPipe) round?: number, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, ) { const isCollection = await this.collectionService.isCollection(identifier); if (!isCollection) { @@ -426,6 +434,7 @@ export class CollectionController { before, after, round, + withRelayedScresults, })); } @@ -469,11 +478,11 @@ export class CollectionController { @Query('after', ParseIntPipe) after?: number, @Query('round', ParseIntPipe) round?: number, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, - @Query('withScResults', new ParseBoolPipe) withScResults?: boolean, - @Query('withOperations', new ParseBoolPipe) withOperations?: boolean, - @Query('withLogs', new ParseBoolPipe) withLogs?: boolean, - @Query('withScamInfo', new ParseBoolPipe) withScamInfo?: boolean, - @Query('withUsername', new ParseBoolPipe) withUsername?: boolean, + @Query('withScResults', ParseBoolPipe) withScResults?: boolean, + @Query('withOperations', ParseBoolPipe) withOperations?: boolean, + @Query('withLogs', ParseBoolPipe) withLogs?: boolean, + @Query('withScamInfo', ParseBoolPipe) withScamInfo?: boolean, + @Query('withUsername', ParseBoolPipe) withUsername?: boolean, ) { const options = TransactionQueryOptions.applyDefaultOptions(size, { withScResults, withOperations, withLogs, withScamInfo, withUsername }); diff --git a/src/endpoints/nfts/nft.controller.ts b/src/endpoints/nfts/nft.controller.ts index 539d1797e..50a495d24 100644 --- a/src/endpoints/nfts/nft.controller.ts +++ b/src/endpoints/nfts/nft.controller.ts @@ -68,16 +68,16 @@ export class NftController { @Query('name') name?: string, @Query('tags', ParseArrayPipe) tags?: string[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('isWhitelistedStorage', new ParseBoolPipe) isWhitelistedStorage?: boolean, - @Query('hasUris', new ParseBoolPipe) hasUris?: boolean, - @Query('isNsfw', new ParseBoolPipe) isNsfw?: boolean, - @Query('isScam', new ParseBoolPipe) isScam?: boolean, + @Query('isWhitelistedStorage', ParseBoolPipe) isWhitelistedStorage?: boolean, + @Query('hasUris', ParseBoolPipe) hasUris?: boolean, + @Query('isNsfw', ParseBoolPipe) isNsfw?: boolean, + @Query('isScam', ParseBoolPipe) isScam?: boolean, @Query('scamType', new ParseEnumPipe(ScamType)) scamType?: ScamType, - @Query('traits', new ParseRecordPipe) traits?: Record, - @Query('before', new ParseIntPipe) before?: number, - @Query('after', new ParseIntPipe) after?: number, - @Query('withOwner', new ParseBoolPipe) withOwner?: boolean, - @Query('withSupply', new ParseBoolPipe) withSupply?: boolean, + @Query('traits', ParseRecordPipe) traits?: Record, + @Query('before', ParseIntPipe) before?: number, + @Query('after', ParseIntPipe) after?: number, + @Query('withOwner', ParseBoolPipe) withOwner?: boolean, + @Query('withSupply', ParseBoolPipe) withSupply?: boolean, ): Promise { return await this.nftService.getNfts( new QueryPagination({ from, size }), @@ -134,13 +134,13 @@ export class NftController { @Query('name') name?: string, @Query('tags', ParseArrayPipe) tags?: string[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('isWhitelistedStorage', new ParseBoolPipe) isWhitelistedStorage?: boolean, - @Query('hasUris', new ParseBoolPipe) hasUris?: boolean, - @Query('isNsfw', new ParseBoolPipe) isNsfw?: boolean, - @Query('traits', new ParseRecordPipe) traits?: Record, - @Query('before', new ParseIntPipe) before?: number, - @Query('after', new ParseIntPipe) after?: number, - @Query('isScam', new ParseBoolPipe) isScam?: boolean, + @Query('isWhitelistedStorage', ParseBoolPipe) isWhitelistedStorage?: boolean, + @Query('hasUris', ParseBoolPipe) hasUris?: boolean, + @Query('isNsfw', ParseBoolPipe) isNsfw?: boolean, + @Query('traits', ParseRecordPipe) traits?: Record, + @Query('before', ParseIntPipe) before?: number, + @Query('after', ParseIntPipe) after?: number, + @Query('isScam', ParseBoolPipe) isScam?: boolean, @Query('scamType', new ParseEnumPipe(ScamType)) scamType?: ScamType, ): Promise { return await this.nftService.getNftCount( @@ -177,13 +177,13 @@ export class NftController { @Query('name') name?: string, @Query('tags', ParseArrayPipe) tags?: string[], @Query('creator', ParseAddressPipe) creator?: string, - @Query('isWhitelistedStorage', new ParseBoolPipe) isWhitelistedStorage?: boolean, - @Query('hasUris', new ParseBoolPipe) hasUris?: boolean, - @Query('isNsfw', new ParseBoolPipe) isNsfw?: boolean, - @Query('traits', new ParseRecordPipe) traits?: Record, - @Query('before', new ParseIntPipe) before?: number, - @Query('after', new ParseIntPipe) after?: number, - @Query('isScam', new ParseBoolPipe) isScam?: boolean, + @Query('isWhitelistedStorage', ParseBoolPipe) isWhitelistedStorage?: boolean, + @Query('hasUris', ParseBoolPipe) hasUris?: boolean, + @Query('isNsfw', ParseBoolPipe) isNsfw?: boolean, + @Query('traits', ParseRecordPipe) traits?: Record, + @Query('before', ParseIntPipe) before?: number, + @Query('after', ParseIntPipe) after?: number, + @Query('isScam', ParseBoolPipe) isScam?: boolean, @Query('scamType', new ParseEnumPipe(ScamType)) scamType?: ScamType, ): Promise { return await this.nftService.getNftCount(new NftFilter({ search, identifiers, type, subType, collection, collections, name, tags, creator, isWhitelistedStorage, hasUris, isNsfw, traits, before, after, isScam, scamType })); @@ -297,6 +297,7 @@ export class NftController { @ApiQuery({ name: 'withLogs', description: 'Return logs for transactions', required: false, type: Boolean }) @ApiQuery({ name: 'withScamInfo', description: 'Returns scam information', required: false, type: Boolean }) @ApiQuery({ name: 'withUsername', description: 'Integrates username in assets for all addresses present in the transactions', required: false, type: Boolean }) + @ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean }) async getNftTransactions( @Param('identifier', ParseNftPipe) identifier: string, @Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number, @@ -312,15 +313,17 @@ export class NftController { @Query('before', ParseIntPipe) before?: number, @Query('after', ParseIntPipe) after?: number, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, - @Query('withScResults', new ParseBoolPipe) withScResults?: boolean, - @Query('withOperations', new ParseBoolPipe) withOperations?: boolean, - @Query('withLogs', new ParseBoolPipe) withLogs?: boolean, - @Query('withScamInfo', new ParseBoolPipe) withScamInfo?: boolean, - @Query('withUsername', new ParseBoolPipe) withUsername?: boolean, + + @Query('withScResults', ParseBoolPipe) withScResults?: boolean, + @Query('withOperations', ParseBoolPipe) withOperations?: boolean, + @Query('withLogs', ParseBoolPipe) withLogs?: boolean, + @Query('withScamInfo', ParseBoolPipe) withScamInfo?: boolean, + @Query('withUsername', ParseBoolPipe) withUsername?: boolean, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, ) { const options = TransactionQueryOptions.applyDefaultOptions(size, { withScResults, withOperations, withLogs, withScamInfo, withUsername }); - return await this.transactionService.getTransactions(new TransactionFilter({ + const transactionFilter = new TransactionFilter({ sender, receivers: receiver, token: identifier, @@ -333,7 +336,11 @@ export class NftController { before, after, order, - }), new QueryPagination({ from, size }), options); + withRelayedScresults, + }); + TransactionFilter.validate(transactionFilter, size); + + return await this.transactionService.getTransactions(transactionFilter, new QueryPagination({ from, size }), options); } @Get("/nfts/:identifier/transactions/count") @@ -349,6 +356,7 @@ export class NftController { @ApiQuery({ name: 'status', description: 'Status of the transaction (success / pending / invalid / fail)', required: false, enum: TransactionStatus }) @ApiQuery({ name: 'before', description: 'Before timestamp', required: false }) @ApiQuery({ name: 'after', description: 'After timestamp', required: false }) + @ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean }) async getNftTransactionsCount( @Param('identifier', ParseNftPipe) identifier: string, @Query('sender', ParseAddressPipe) sender?: string, @@ -360,6 +368,7 @@ export class NftController { @Query('status', new ParseEnumPipe(TransactionStatus)) status?: TransactionStatus, @Query('before', ParseIntPipe) before?: number, @Query('after', ParseIntPipe) after?: number, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, ) { return await this.transactionService.getTransactionCount(new TransactionFilter({ @@ -373,6 +382,7 @@ export class NftController { status, before, after, + withRelayedScresults, })); } @@ -414,11 +424,11 @@ export class NftController { @Query('before', ParseIntPipe) before?: number, @Query('after', ParseIntPipe) after?: number, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, - @Query('withScResults', new ParseBoolPipe) withScResults?: boolean, - @Query('withOperations', new ParseBoolPipe) withOperations?: boolean, - @Query('withLogs', new ParseBoolPipe) withLogs?: boolean, - @Query('withScamInfo', new ParseBoolPipe) withScamInfo?: boolean, - @Query('withUsername', new ParseBoolPipe) withUsername?: boolean, + @Query('withScResults', ParseBoolPipe) withScResults?: boolean, + @Query('withOperations', ParseBoolPipe) withOperations?: boolean, + @Query('withLogs', ParseBoolPipe) withLogs?: boolean, + @Query('withScamInfo', ParseBoolPipe) withScamInfo?: boolean, + @Query('withUsername', ParseBoolPipe) withUsername?: boolean, ) { const options = TransactionQueryOptions.applyDefaultOptions(size, { withScResults, withOperations, withLogs, withScamInfo, withUsername }); diff --git a/src/endpoints/nfts/nft.service.ts b/src/endpoints/nfts/nft.service.ts index 1c357d8ef..e0af78f6b 100644 --- a/src/endpoints/nfts/nft.service.ts +++ b/src/endpoints/nfts/nft.service.ts @@ -31,6 +31,7 @@ import { NftRarities } from "./entities/nft.rarities"; import { SortCollectionNfts } from "../collections/entities/sort.collection.nfts"; import { TokenAssets } from "src/common/assets/entities/token.assets"; import { ScamInfo } from "src/common/entities/scam-info.dto"; +import { NftSubType } from "./entities/nft.sub.type"; @Injectable() export class NftService { @@ -217,7 +218,10 @@ export class NftService { return undefined; } - if (nft.type.in(NftType.SemiFungibleESDT, NftType.MetaESDT)) { + if (nft.type && nft.type.in( + NftType.SemiFungibleESDT, NftType.MetaESDT, + NftSubType.DynamicSemiFungibleESDT, NftSubType.DynamicMetaESDT + )) { await this.applySupply(nft); } diff --git a/src/endpoints/nodes/node.controller.ts b/src/endpoints/nodes/node.controller.ts index f00bea6bb..eb478981d 100644 --- a/src/endpoints/nodes/node.controller.ts +++ b/src/endpoints/nodes/node.controller.ts @@ -59,7 +59,7 @@ export class NodeController { @Query('fullHistory', ParseBoolPipe) fullHistory?: boolean, @Query('sort', new ParseEnumPipe(NodeSort)) sort?: NodeSort, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, - @Query('withIdentityInfo', new ParseBoolPipe) withIdentityInfo?: boolean, + @Query('withIdentityInfo', ParseBoolPipe) withIdentityInfo?: boolean, @Query('isQualified', ParseBoolPipe) isQualified?: boolean, @Query('isAuctioned', ParseBoolPipe) isAuctioned?: boolean, @Query('isAuctionDangerZone', ParseBoolPipe) isAuctionDangerZone?: boolean, diff --git a/src/endpoints/providers/provider.controller.ts b/src/endpoints/providers/provider.controller.ts index 03bc999ef..eb444c9ee 100644 --- a/src/endpoints/providers/provider.controller.ts +++ b/src/endpoints/providers/provider.controller.ts @@ -26,8 +26,8 @@ export class ProviderController { @Query('identity') identity?: string, @Query('owner', ParseAddressPipe) owner?: string, @Query('providers', ParseAddressArrayPipe) providers?: string[], - @Query('withIdentityInfo', new ParseBoolPipe) withIdentityInfo?: boolean, - @Query('withLatestInfo', new ParseBoolPipe) withLatestInfo?: boolean, + @Query('withIdentityInfo', ParseBoolPipe) withIdentityInfo?: boolean, + @Query('withLatestInfo', ParseBoolPipe) withLatestInfo?: boolean, ): Promise { const options = ProviderQueryOptions.applyDefaultOptions(owner, { withIdentityInfo, withLatestInfo }); diff --git a/src/endpoints/providers/provider.service.ts b/src/endpoints/providers/provider.service.ts index 7c76bcd6c..0cd9d3396 100644 --- a/src/endpoints/providers/provider.service.ts +++ b/src/endpoints/providers/provider.service.ts @@ -106,13 +106,19 @@ export class ProviderService { const nodesGroupedByProvider: { [key: string]: any[] } = nodes.groupBy(x => x.provider); - const providersDelegationData: DelegationData[] = await this.getDelegationProviders(); + const providersDelegationData = await this.getDelegationProviders(); + if (!Array.isArray(providersDelegationData)) { + return providers; + } providers.forEach((element) => { const providerAddress = element.provider; // Delegation details for provider - const delegationData: DelegationData | undefined = providersDelegationData.find((providerDelegationInfo: any) => providerDelegationInfo !== null && providerAddress === providerDelegationInfo.contract); + const delegationData = providersDelegationData.find((providerDelegationInfo: any) => + providerDelegationInfo !== null && providerAddress === providerDelegationInfo.contract + ); + if (delegationData) { if (delegationData.aprValue) { element.apr = parseFloat(delegationData.aprValue.toFixed(2)); diff --git a/src/endpoints/proxy/gateway.proxy.controller.ts b/src/endpoints/proxy/gateway.proxy.controller.ts index 48f9fb434..3b7e12c37 100644 --- a/src/endpoints/proxy/gateway.proxy.controller.ts +++ b/src/endpoints/proxy/gateway.proxy.controller.ts @@ -6,7 +6,7 @@ import { GatewayService } from "src/common/gateway/gateway.service"; import { Response, Request } from "express"; import { GatewayComponentRequest } from "src/common/gateway/entities/gateway.component.request"; import { PluginService } from "src/common/plugins/plugin.service"; -import { Constants, ParseAddressPipe, ParseBlockHashPipe, ParseBlsHashPipe, ParseIntPipe, ParseTransactionHashPipe } from "@multiversx/sdk-nestjs-common"; +import { Constants, ParseAddressPipe, ParseBlockHashPipe, ParseBlsHashPipe, ParseIntPipe, ParseTransactionHashPipe, ParseBoolPipe } from "@multiversx/sdk-nestjs-common"; import { CacheService, NoCache } from "@multiversx/sdk-nestjs-cache"; import { OriginLogger } from "@multiversx/sdk-nestjs-common"; import { DeepHistoryInterceptor } from "src/interceptors/deep-history.interceptor"; @@ -118,8 +118,12 @@ export class GatewayProxyController { } @Post('/transaction/simulate') - async transactionSimulate(@Body() body: any) { - return await this.gatewayPost('transaction/simulate', GatewayComponentRequest.simulateTransaction, body); + async transactionSimulate(@Query('checkSignature', ParseBoolPipe) checkSignature: boolean, @Body() body: any) { + let url = 'transaction/simulate'; + if (checkSignature !== undefined) { + url += `?checkSignature=${checkSignature}`; + } + return await this.gatewayPost(url, GatewayComponentRequest.simulateTransaction, body); } @Post('/transaction/send-multiple') diff --git a/src/endpoints/rounds/round.controller.ts b/src/endpoints/rounds/round.controller.ts index 783e0eaf3..9fefac936 100644 --- a/src/endpoints/rounds/round.controller.ts +++ b/src/endpoints/rounds/round.controller.ts @@ -26,8 +26,8 @@ export class RoundController { @Query("size", new DefaultValuePipe(25), ParseIntPipe) size: number, @Query("validator", ParseBlsHashPipe) validator?: string, @Query('condition', new ParseEnumPipe(QueryConditionOptions)) condition?: QueryConditionOptions, - @Query("shard", new ParseIntPipe) shard?: number, - @Query("epoch", new ParseIntPipe) epoch?: number, + @Query("shard", ParseIntPipe) shard?: number, + @Query("epoch", ParseIntPipe) epoch?: number, ): Promise { return this.roundService.getRounds(new RoundFilter({ from, size, condition, validator, shard, epoch })); } @@ -42,8 +42,8 @@ export class RoundController { getRoundCount( @Query("validator", ParseBlsHashPipe) validator?: string, @Query('condition', new ParseEnumPipe(QueryConditionOptions)) condition?: QueryConditionOptions, - @Query("shard", new ParseIntPipe) shard?: number, - @Query("epoch", new ParseIntPipe) epoch?: number, + @Query("shard", ParseIntPipe) shard?: number, + @Query("epoch", ParseIntPipe) epoch?: number, ): Promise { return this.roundService.getRoundCount(new RoundFilter({ condition, validator, shard, epoch })); } @@ -53,8 +53,8 @@ export class RoundController { getRoundCountAlternative( @Query("validator", ParseBlsHashPipe) validator?: string, @Query('condition', new ParseEnumPipe(QueryConditionOptions)) condition?: QueryConditionOptions, - @Query("shard", new ParseIntPipe) shard?: number, - @Query("epoch", new ParseIntPipe) epoch?: number, + @Query("shard", ParseIntPipe) shard?: number, + @Query("epoch", ParseIntPipe) epoch?: number, ): Promise { return this.roundService.getRoundCount(new RoundFilter({ condition, validator, shard, epoch })); } diff --git a/src/endpoints/tokens/token.controller.ts b/src/endpoints/tokens/token.controller.ts index 864a8576e..757a29db6 100644 --- a/src/endpoints/tokens/token.controller.ts +++ b/src/endpoints/tokens/token.controller.ts @@ -59,7 +59,7 @@ export class TokenController { @Query('identifiers', ParseArrayPipe) identifiers?: string[], @Query('sort', new ParseEnumPipe(TokenSort)) sort?: TokenSort, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, - @Query('includeMetaESDT', new ParseBoolPipe) includeMetaESDT?: boolean, + @Query('includeMetaESDT', ParseBoolPipe) includeMetaESDT?: boolean, @Query('mexPairType', new ParseEnumArrayPipe(MexPairType)) mexPairType?: MexPairType[], @Query('priceSource', new ParseEnumPipe(TokenAssetsPriceSourceType)) priceSource?: TokenAssetsPriceSourceType, ): Promise { @@ -87,7 +87,7 @@ export class TokenController { @Query('type', new ParseEnumPipe(TokenType)) type?: TokenType, @Query('identifier', ParseTokenPipe) identifier?: string, @Query('identifiers', ParseArrayPipe) identifiers?: string[], - @Query('includeMetaESDT', new ParseBoolPipe) includeMetaESDT?: boolean, + @Query('includeMetaESDT', ParseBoolPipe) includeMetaESDT?: boolean, @Query('mexPairType', new ParseEnumArrayPipe(MexPairType)) mexPairType?: MexPairType[], @Query('priceSource', new ParseEnumPipe(TokenAssetsPriceSourceType)) priceSource?: TokenAssetsPriceSourceType, ): Promise { @@ -102,7 +102,7 @@ export class TokenController { @Query('type', new ParseEnumPipe(TokenType)) type?: TokenType, @Query('identifier', ParseTokenPipe) identifier?: string, @Query('identifiers', ParseArrayPipe) identifiers?: string[], - @Query('includeMetaESDT', new ParseBoolPipe) includeMetaESDT?: boolean, + @Query('includeMetaESDT', ParseBoolPipe) includeMetaESDT?: boolean, @Query('mexPairType', new ParseEnumArrayPipe(MexPairType)) mexPairType?: MexPairType[], @Query('priceSource', new ParseEnumPipe(TokenAssetsPriceSourceType)) priceSource?: TokenAssetsPriceSourceType, ): Promise { @@ -116,7 +116,7 @@ export class TokenController { @ApiNotFoundResponse({ description: 'Token not found' }) async getToken( @Param('identifier', ParseTokenPipe) identifier: string, - @Query('denominated', new ParseBoolPipe) denominated?: boolean, + @Query('denominated', ParseBoolPipe) denominated?: boolean, ): Promise { const supplyOptions = { denominated }; const token = await this.tokenService.getToken(identifier, supplyOptions); @@ -134,7 +134,7 @@ export class TokenController { @ApiNotFoundResponse({ description: 'Token not found' }) async getTokenSupply( @Param('identifier', ParseTokenPipe) identifier: string, - @Query('denominated', new ParseBoolPipe) denominated?: boolean, + @Query('denominated', ParseBoolPipe) denominated?: boolean, ): Promise { const isToken = await this.tokenService.isToken(identifier); if (!isToken) { @@ -219,6 +219,7 @@ export class TokenController { @ApiQuery({ name: 'withUsername', description: 'Integrates username in assets for all addresses present in the transactions', required: false, type: Boolean }) @ApiQuery({ name: 'withBlockInfo', description: 'Returns sender / receiver block details', required: false, type: Boolean }) @ApiQuery({ name: 'withActionTransferValue', description: 'Returns value in USD and EGLD for transferred tokens within the action attribute', required: false }) + @ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean }) async getTokenTransactions( @Param('identifier', ParseTokenPipe) identifier: string, @Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number, @@ -236,13 +237,14 @@ export class TokenController { @Query('round', ParseIntPipe) round?: number, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, @Query('fields', ParseArrayPipe) fields?: string[], - @Query('withScResults', new ParseBoolPipe) withScResults?: boolean, - @Query('withOperations', new ParseBoolPipe) withOperations?: boolean, - @Query('withLogs', new ParseBoolPipe) withLogs?: boolean, - @Query('withScamInfo', new ParseBoolPipe) withScamInfo?: boolean, - @Query('withUsername', new ParseBoolPipe) withUsername?: boolean, - @Query('withBlockInfo', new ParseBoolPipe) withBlockInfo?: boolean, + @Query('withScResults', ParseBoolPipe) withScResults?: boolean, + @Query('withOperations', ParseBoolPipe) withOperations?: boolean, + @Query('withLogs', ParseBoolPipe) withLogs?: boolean, + @Query('withScamInfo', ParseBoolPipe) withScamInfo?: boolean, + @Query('withUsername', ParseBoolPipe) withUsername?: boolean, + @Query('withBlockInfo', ParseBoolPipe) withBlockInfo?: boolean, @Query('withActionTransferValue', ParseBoolPipe) withActionTransferValue?: boolean, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, ) { const options = TransactionQueryOptions.applyDefaultOptions(size, { withScResults, withOperations, withLogs, withScamInfo, withUsername, withBlockInfo, withActionTransferValue }); @@ -251,7 +253,7 @@ export class TokenController { throw new NotFoundException('Token not found'); } - return await this.transactionService.getTransactions(new TransactionFilter({ + const transactionFilter = new TransactionFilter({ sender, receivers: receiver, token: identifier, @@ -265,7 +267,12 @@ export class TokenController { after, order, round, - }), + withRelayedScresults, + }); + TransactionFilter.validate(transactionFilter, size); + + return await this.transactionService.getTransactions( + transactionFilter, new QueryPagination({ from, size }), options, undefined, @@ -287,6 +294,7 @@ export class TokenController { @ApiQuery({ name: 'before', description: 'Before timestamp', required: false }) @ApiQuery({ name: 'after', description: 'After timestamp', required: false }) @ApiQuery({ name: 'round', description: 'Filter by round number', required: false }) + @ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean }) async getTokenTransactionsCount( @Param('identifier', ParseTokenPipe) identifier: string, @Query('sender', ParseAddressPipe) sender?: string, @@ -299,6 +307,7 @@ export class TokenController { @Query('before', ParseIntPipe) before?: number, @Query('after', ParseIntPipe) after?: number, @Query('round', ParseIntPipe) round?: number, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, ) { const isToken = await this.tokenService.isToken(identifier); if (!isToken) { @@ -317,6 +326,7 @@ export class TokenController { before, after, round, + withRelayedScresults, })); } @@ -401,9 +411,9 @@ export class TokenController { @Query('round', ParseIntPipe) round?: number, @Query('fields', ParseArrayPipe) fields?: string[], @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, - @Query('withScamInfo', new ParseBoolPipe) withScamInfo?: boolean, - @Query('withUsername', new ParseBoolPipe) withUsername?: boolean, - @Query('withBlockInfo', new ParseBoolPipe) withBlockInfo?: boolean, + @Query('withScamInfo', ParseBoolPipe) withScamInfo?: boolean, + @Query('withUsername', ParseBoolPipe) withUsername?: boolean, + @Query('withBlockInfo', ParseBoolPipe) withBlockInfo?: boolean, @Query('withActionTransferValue', ParseBoolPipe) withActionTransferValue?: boolean, ): Promise { const isToken = await this.tokenService.isToken(identifier); diff --git a/src/endpoints/transactions/entities/transaction.filter.ts b/src/endpoints/transactions/entities/transaction.filter.ts index b39779eb0..5462a0822 100644 --- a/src/endpoints/transactions/entities/transaction.filter.ts +++ b/src/endpoints/transactions/entities/transaction.filter.ts @@ -2,12 +2,9 @@ import { QueryConditionOptions } from "@multiversx/sdk-nestjs-elastic"; import { SortOrder } from "src/common/entities/sort.order"; import { TransactionStatus } from "./transaction.status"; import { TransactionType } from "./transaction.type"; +import { BadRequestException } from "@nestjs/common"; export class TransactionFilter { - constructor(init?: Partial) { - Object.assign(this, init); - } - address?: string; sender?: string; senders?: string[] = []; @@ -29,4 +26,16 @@ export class TransactionFilter { isRelayed?: boolean; relayer?: string; round?: number; + withRefunds?: boolean; + withRelayedScresults?: boolean; + + constructor(init?: Partial) { + Object.assign(this, init); + } + + static validate(filter: TransactionFilter, size: number) { + if (filter.withRelayedScresults && size > 50) { + throw new BadRequestException('Size must be less than or equal to 50 when withRelayedScresults is set'); + } + } } diff --git a/src/endpoints/transactions/transaction.controller.ts b/src/endpoints/transactions/transaction.controller.ts index 4e2c415cf..90858a346 100644 --- a/src/endpoints/transactions/transaction.controller.ts +++ b/src/endpoints/transactions/transaction.controller.ts @@ -49,6 +49,7 @@ export class TransactionController { @ApiQuery({ name: 'withBlockInfo', description: 'Returns sender / receiver block details', required: false, type: Boolean }) @ApiQuery({ name: 'isRelayed', description: 'Returns relayed transactions details', required: false, type: Boolean }) @ApiQuery({ name: 'withActionTransferValue', description: 'Returns value in USD and EGLD for transferred tokens within the action attribute', required: false }) + @ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean }) getTransactions( @Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number, @Query('size', new DefaultValuePipe(25), ParseIntPipe) size: number, @@ -67,18 +68,19 @@ export class TransactionController { @Query('round', ParseIntPipe) round?: number, @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, @Query('fields', ParseArrayPipe) fields?: string[], - @Query('withScResults', new ParseBoolPipe) withScResults?: boolean, - @Query('withOperations', new ParseBoolPipe) withOperations?: boolean, - @Query('withLogs', new ParseBoolPipe) withLogs?: boolean, - @Query('withScamInfo', new ParseBoolPipe) withScamInfo?: boolean, - @Query('withUsername', new ParseBoolPipe) withUsername?: boolean, - @Query('withBlockInfo', new ParseBoolPipe) withBlockInfo?: boolean, - @Query('isRelayed', new ParseBoolPipe) isRelayed?: boolean, + @Query('withScResults', ParseBoolPipe) withScResults?: boolean, + @Query('withOperations', ParseBoolPipe) withOperations?: boolean, + @Query('withLogs', ParseBoolPipe) withLogs?: boolean, + @Query('withScamInfo', ParseBoolPipe) withScamInfo?: boolean, + @Query('withUsername', ParseBoolPipe) withUsername?: boolean, + @Query('withBlockInfo', ParseBoolPipe) withBlockInfo?: boolean, + @Query('isRelayed', ParseBoolPipe) isRelayed?: boolean, @Query('withActionTransferValue', ParseBoolPipe) withActionTransferValue?: boolean, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, ) { const options = TransactionQueryOptions.applyDefaultOptions(size, { withScResults, withOperations, withLogs, withScamInfo, withUsername, withBlockInfo, withActionTransferValue }); - return this.transactionService.getTransactions(new TransactionFilter({ + const transactionFilter = new TransactionFilter({ sender, receivers: receiver, token, @@ -94,7 +96,10 @@ export class TransactionController { order, isRelayed, round, - }), + withRelayedScresults: withRelayedScresults, + }); + TransactionFilter.validate(transactionFilter, size); + return this.transactionService.getTransactions(transactionFilter, new QueryPagination({ from, size }), options, undefined, @@ -119,6 +124,7 @@ export class TransactionController { @ApiQuery({ name: 'after', description: 'After timestamp', required: false }) @ApiQuery({ name: 'round', description: 'Round number', required: false }) @ApiQuery({ name: 'isRelayed', description: 'Returns relayed transactions details', required: false, type: Boolean }) + @ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean }) getTransactionCount( @Query('sender', ParseAddressAndMetachainPipe) sender?: string, @Query('receiver', ParseAddressArrayPipe) receiver?: string[], @@ -133,7 +139,8 @@ export class TransactionController { @Query('before', ParseIntPipe) before?: number, @Query('after', ParseIntPipe) after?: number, @Query('round', ParseIntPipe) round?: number, - @Query('isRelayed', new ParseBoolPipe) isRelayed?: boolean, + @Query('isRelayed', ParseBoolPipe) isRelayed?: boolean, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, ): Promise { return this.transactionService.getTransactionCount(new TransactionFilter({ sender, @@ -150,6 +157,7 @@ export class TransactionController { condition, isRelayed, round, + withRelayedScresults: withRelayedScresults, })); } @@ -168,8 +176,9 @@ export class TransactionController { @Query('condition') condition?: QueryConditionOptions, @Query('before', ParseIntPipe) before?: number, @Query('after', ParseIntPipe) after?: number, - @Query('round', new ParseIntPipe) round?: number, - @Query('isRelayed', new ParseBoolPipe) isRelayed?: boolean, + @Query('round', ParseIntPipe) round?: number, + @Query('isRelayed', ParseBoolPipe) isRelayed?: boolean, + @Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean, ): Promise { return this.transactionService.getTransactionCount(new TransactionFilter({ sender, @@ -186,6 +195,7 @@ export class TransactionController { condition, isRelayed, round, + withRelayedScresults: withRelayedScresults, })); } @@ -249,4 +259,3 @@ export class TransactionController { return await this.transactionService.decodeTransaction(transaction); } } - diff --git a/src/endpoints/transfers/transfer.controller.ts b/src/endpoints/transfers/transfer.controller.ts index 117be703e..1e723c463 100644 --- a/src/endpoints/transfers/transfer.controller.ts +++ b/src/endpoints/transfers/transfer.controller.ts @@ -46,6 +46,7 @@ export class TransferController { @ApiQuery({ name: 'withLogs', description: 'Return logs for transfers. When "withLogs" parameter is applied, complexity estimation is 200', required: false }) @ApiQuery({ name: 'withOperations', description: 'Return operations for transfers. When "withOperations" parameter is applied, complexity estimation is 200', required: false }) @ApiQuery({ name: 'withActionTransferValue', description: 'Returns value in USD and EGLD for transferred tokens within the action attribute', required: false }) + @ApiQuery({ name: 'withRefunds', description: 'Include refund transactions', required: false }) async getAccountTransfers( @Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number, @Query('size', new DefaultValuePipe(25), ParseIntPipe) size: number, @@ -64,13 +65,14 @@ export class TransferController { @Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder, @Query('fields', ParseArrayPipe) fields?: string[], @Query('relayer', ParseAddressPipe) relayer?: string, - @Query('isRelayed', new ParseBoolPipe) isRelayed?: boolean, - @Query('withScamInfo', new ParseBoolPipe) withScamInfo?: boolean, - @Query('withUsername', new ParseBoolPipe) withUsername?: boolean, - @Query('withBlockInfo', new ParseBoolPipe) withBlockInfo?: boolean, - @Query('withLogs', new ParseBoolPipe) withLogs?: boolean, - @Query('withOperations', new ParseBoolPipe) withOperations?: boolean, + @Query('isRelayed', ParseBoolPipe) isRelayed?: boolean, + @Query('withScamInfo', ParseBoolPipe) withScamInfo?: boolean, + @Query('withUsername', ParseBoolPipe) withUsername?: boolean, + @Query('withBlockInfo', ParseBoolPipe) withBlockInfo?: boolean, + @Query('withLogs', ParseBoolPipe) withLogs?: boolean, + @Query('withOperations', ParseBoolPipe) withOperations?: boolean, @Query('withActionTransferValue', ParseBoolPipe) withActionTransferValue?: boolean, + @Query('withRefunds', ParseBoolPipe) withRefunds?: boolean, ): Promise { const options = TransactionQueryOptions.applyDefaultOptions( size, new TransactionQueryOptions({ withScamInfo, withUsername, withBlockInfo, withLogs, withOperations, withActionTransferValue }), @@ -92,6 +94,7 @@ export class TransferController { relayer, isRelayed, round, + withRefunds, }), new QueryPagination({ from, size }), options, @@ -115,6 +118,7 @@ export class TransferController { @ApiQuery({ name: 'after', description: 'After timestamp', required: false }) @ApiQuery({ name: 'round', description: 'Round number', required: false }) @ApiQuery({ name: 'isRelayed', description: 'Returns relayed transactions details', required: false, type: Boolean }) + @ApiQuery({ name: 'withRefunds', description: 'Include refund transactions', required: false }) async getAccountTransfersCount( @Query('sender', ParseAddressArrayPipe) sender?: string[], @Query('receiver', ParseAddressArrayPipe) receiver?: string[], @@ -128,7 +132,8 @@ export class TransferController { @Query('before', ParseIntPipe) before?: number, @Query('after', ParseIntPipe) after?: number, @Query('round', ParseIntPipe) round?: number, - @Query('isRelayed', new ParseBoolPipe) isRelayed?: boolean, + @Query('isRelayed', ParseBoolPipe) isRelayed?: boolean, + @Query('withRefunds', ParseBoolPipe) withRefunds?: boolean, ): Promise { return await this.transferService.getTransfersCount(new TransactionFilter({ senders: sender, @@ -144,6 +149,7 @@ export class TransferController { after, isRelayed, round, + withRefunds, })); } @@ -162,6 +168,7 @@ export class TransferController { @Query('before', ParseIntPipe) before?: number, @Query('after', ParseIntPipe) after?: number, @Query('round', ParseIntPipe) round?: number, + @Query('withRefunds', ParseBoolPipe) withRefunds?: boolean, ): Promise { return await this.transferService.getTransfersCount(new TransactionFilter({ senders: sender, @@ -176,6 +183,7 @@ export class TransferController { before, after, round, + withRefunds, })); } } diff --git a/src/endpoints/usernames/username.controller.ts b/src/endpoints/usernames/username.controller.ts index b0aeadb05..3a1f242e4 100644 --- a/src/endpoints/usernames/username.controller.ts +++ b/src/endpoints/usernames/username.controller.ts @@ -22,7 +22,7 @@ export class UsernameController { async getUsernameDetails( @Res() res: any, @Param('username') username: string, - @Query('withGuardianInfo', new ParseBoolPipe) withGuardianInfo: boolean + @Query('withGuardianInfo', ParseBoolPipe) withGuardianInfo: boolean ): Promise { const address = await this.usernameService.getAddressForUsername(username); if (!address) {