From 09d5423ab931723a3d17dd2e4007cdc1b7a46f64 Mon Sep 17 00:00:00 2001 From: hoangtv49 <92872128+hoangtv49@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:45:15 +0700 Subject: [PATCH 1/2] Baseline/euphoria 20231211 (#1057) * [Serenity][Manage Connected Chain] (#1042) * remove influxdb (#1044) * add sync module (#1045) * [Serenity][Optimize export csv] (#1055) * feat: update export cw20 transfer (#1053) Co-authored-by: Duc Nguyen * revamp: optimize export csv cw721 (#1054) --------- Co-authored-by: Duc Nguyen * refactor table database (#1056) --------- Co-authored-by: Duc Nguyen <119287881+nthduc95@users.noreply.github.com> Co-authored-by: Duc Nguyen (cherry picked from commit d4c8abbe9d0cbd82abab6076f243f9f4c36e60c3) --- docker-compose.yml | 16 - package.json | 1 - src/app.module.ts | 3 +- .../chain-info/chain-info.controller.ts | 100 ++++++ .../chain-info/chain-info.module.ts | 14 + .../chain-info/chain-info.service.ts | 61 ++++ .../chain-info/dto/chain-info-response.dto.ts | 10 + .../chain-info/dto/create-chain-info.dto.ts | 18 + .../chain-info/dto/update-chain-info.dto.ts | 7 + .../validator/is-unique.validator.ts | 54 +++ src/components/components.module.ts | 8 +- .../controllers/cw20-token.controller.ts | 121 ++++++- .../cw20-token/cw20-token.module.ts | 17 +- .../cw20-token/dtos/create-cw20-token.dto.ts | 40 +++ .../cw20-token/dtos/create-ibc.dto.ts | 39 ++ .../dtos/cw20-token-market-params.dto.ts | 7 + .../dtos/cw20-token-response.dto.ts | 37 ++ .../cw20-token/dtos/ibc-response.dto.ts | 46 +++ .../cw20-token/dtos/update-cw20-token.dto.ts | 7 + .../cw20-token/dtos/update-ibc.dto.ts | 22 ++ .../repositories/token-markets.repository.ts | 44 +-- .../cw20-token/services/cw20-token.service.ts | 60 +++- .../is-unique-many-column.validator.ts | 58 +++ .../validators/is-unique.validator.ts | 55 +++ .../export-csv/services/export-csv.service.ts | 1 - .../metric/controllers/metric.controller.ts | 74 ---- .../metric/dtos/cw20-metric-params.dto.ts | 37 -- .../metric/dtos/metric-condition.dto.ts | 9 - .../metric/dtos/metric-output.dto.ts | 9 - .../metric/dtos/metric-params.dto.ts | 22 -- .../metric/dtos/metric-status-output.dto.ts | 12 - .../dtos/metric-transaction-output.dto.ts | 9 - .../metric/dtos/token-output.dto.ts | 9 - src/components/metric/metric.module.ts | 14 - .../metric/services/influxdb-client.ts | 338 ------------------ .../metric/services/metric.service.ts | 241 ------------- src/components/metric/utils/enum.ts | 27 -- src/components/metric/utils/utils.ts | 137 ------- .../queues/cw4973/cw4973.processor.ts | 16 +- src/components/queues/queues.module.ts | 4 +- .../queues/token/token.processor.ts | 117 +----- ...1052101011-add-chain-id-to-token-market.ts | 19 + ...278720-remove-code-id-from-token-market.ts | 19 + ...-unique-chain-id-denom-to-token-markets.ts | 19 + .../1701245216668-create-chain-info.ts | 21 ++ .../1701678792982-refactor-table-database.ts | 27 ++ src/shared/configs/configuration.ts | 12 - src/shared/configs/module-options.ts | 4 - src/shared/constants/common.ts | 122 ++++--- .../entities/block-sync-error.entity.ts | 12 - src/shared/entities/block.entity.ts | 61 ---- src/shared/entities/chain-info.entity.ts | 15 + .../entities/cw20-token-owner.entity.ts | 18 - src/shared/entities/delegation.entity.ts | 25 -- .../entities/delegator-reward.entity.ts | 18 - .../entities/deployment-requests.entity.ts | 130 ------- src/shared/entities/missed-block.entity.ts | 13 - src/shared/entities/proposal-vote.entity.ts | 18 - .../entities/smart-contract-code.entity.ts | 54 --- src/shared/entities/smart-contract.entity.ts | 96 ----- src/shared/entities/tag.entity.ts | 18 - src/shared/entities/token-markets.entity.ts | 8 +- src/shared/entities/transaction.entity.ts | 32 -- src/shared/entities/validator.entity.ts | 84 ----- .../entities/verify-code-step.entity.ts | 23 -- .../entities/verify-item-check.entity.ts | 11 - src/shared/helpers/transaction.helper.ts | 105 ++---- src/shared/index.ts | 5 - 68 files changed, 1009 insertions(+), 1901 deletions(-) create mode 100644 src/components/chain-info/chain-info.controller.ts create mode 100644 src/components/chain-info/chain-info.module.ts create mode 100644 src/components/chain-info/chain-info.service.ts create mode 100644 src/components/chain-info/dto/chain-info-response.dto.ts create mode 100644 src/components/chain-info/dto/create-chain-info.dto.ts create mode 100644 src/components/chain-info/dto/update-chain-info.dto.ts create mode 100644 src/components/chain-info/validator/is-unique.validator.ts create mode 100644 src/components/cw20-token/dtos/create-cw20-token.dto.ts create mode 100644 src/components/cw20-token/dtos/create-ibc.dto.ts create mode 100644 src/components/cw20-token/dtos/cw20-token-response.dto.ts create mode 100644 src/components/cw20-token/dtos/ibc-response.dto.ts create mode 100644 src/components/cw20-token/dtos/update-cw20-token.dto.ts create mode 100644 src/components/cw20-token/dtos/update-ibc.dto.ts create mode 100644 src/components/cw20-token/validators/is-unique-many-column.validator.ts create mode 100644 src/components/cw20-token/validators/is-unique.validator.ts delete mode 100644 src/components/metric/controllers/metric.controller.ts delete mode 100644 src/components/metric/dtos/cw20-metric-params.dto.ts delete mode 100644 src/components/metric/dtos/metric-condition.dto.ts delete mode 100644 src/components/metric/dtos/metric-output.dto.ts delete mode 100644 src/components/metric/dtos/metric-params.dto.ts delete mode 100644 src/components/metric/dtos/metric-status-output.dto.ts delete mode 100644 src/components/metric/dtos/metric-transaction-output.dto.ts delete mode 100644 src/components/metric/dtos/token-output.dto.ts delete mode 100644 src/components/metric/metric.module.ts delete mode 100644 src/components/metric/services/influxdb-client.ts delete mode 100644 src/components/metric/services/metric.service.ts delete mode 100644 src/components/metric/utils/enum.ts delete mode 100644 src/components/metric/utils/utils.ts create mode 100644 src/migrations/1701052101011-add-chain-id-to-token-market.ts create mode 100644 src/migrations/1701144278720-remove-code-id-from-token-market.ts create mode 100644 src/migrations/1701145590378-add-unique-chain-id-denom-to-token-markets.ts create mode 100644 src/migrations/1701245216668-create-chain-info.ts create mode 100644 src/migrations/1701678792982-refactor-table-database.ts delete mode 100644 src/shared/entities/block-sync-error.entity.ts delete mode 100644 src/shared/entities/block.entity.ts create mode 100644 src/shared/entities/chain-info.entity.ts delete mode 100644 src/shared/entities/cw20-token-owner.entity.ts delete mode 100644 src/shared/entities/delegation.entity.ts delete mode 100644 src/shared/entities/delegator-reward.entity.ts delete mode 100644 src/shared/entities/deployment-requests.entity.ts delete mode 100644 src/shared/entities/missed-block.entity.ts delete mode 100644 src/shared/entities/proposal-vote.entity.ts delete mode 100644 src/shared/entities/smart-contract-code.entity.ts delete mode 100644 src/shared/entities/smart-contract.entity.ts delete mode 100644 src/shared/entities/tag.entity.ts delete mode 100644 src/shared/entities/transaction.entity.ts delete mode 100644 src/shared/entities/validator.entity.ts delete mode 100644 src/shared/entities/verify-code-step.entity.ts delete mode 100644 src/shared/entities/verify-item-check.entity.ts diff --git a/docker-compose.yml b/docker-compose.yml index a531c69c..b7e23ebb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,26 +39,10 @@ services: - "6379:6379" networks: - dev_network - influxdb: - image: influxdb:2.1.1 - environment: - - DOCKER_INFLUXDB_INIT_MODE=setup - - DOCKER_INFLUXDB_INIT_USERNAME=admin - - DOCKER_INFLUXDB_INIT_PASSWORD=adminAurascan - - DOCKER_INFLUXDB_INIT_ORG=aura_dev - - DOCKER_INFLUXDB_INIT_BUCKET=aurascan - - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=aurascan-example-token - volumes: - - influxdb_data:/var/lib/influxdb - ports: - - '8086:8086' - networks: - - dev_network volumes: db_data: redis_data: - influxdb_data: networks: dev_network: diff --git a/package.json b/package.json index 0b15db24..100a0fdd 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "@cosmjs/cosmwasm-stargate": "^0.29.4", "@cosmjs/crypto": "^0.29.4", "@cosmjs/encoding": "^0.29.4", - "@influxdata/influxdb-client": "^1.31.0", "@json2csv/plainjs": "^6.1.3", "@nestjs-modules/mailer": "^1.8.1", "@nestjs/axios": "0.0.3", diff --git a/src/app.module.ts b/src/app.module.ts index 5324a810..32cce526 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -8,7 +8,6 @@ import { AccountModule } from './components/account/account.module'; import { ServiceUtil } from './shared/utils/service.util'; import { ContractModule } from './components/contract/contract.module'; import { Cw20TokenModule } from './components/cw20-token/cw20-token.module'; -import { MetricService } from './components/metric/services/metric.service'; import { SoulboundTokenModule } from './components/soulbound-token/soulbound-token.module'; import { AuthModule } from './auth/auth.module'; import { MailModule } from './components/mail/mail.module'; @@ -50,6 +49,6 @@ import { WatchListModule } from './components/watch-list/watch-list.module'; inject: [ConfigService], }), ], - providers: [ServiceUtil, MetricService], + providers: [ServiceUtil], }) export class AppModule {} diff --git a/src/components/chain-info/chain-info.controller.ts b/src/components/chain-info/chain-info.controller.ts new file mode 100644 index 00000000..9e00bb6a --- /dev/null +++ b/src/components/chain-info/chain-info.controller.ts @@ -0,0 +1,100 @@ +import { + Controller, + Get, + Post, + Body, + Param, + Delete, + Put, + UseGuards, + HttpCode, + HttpStatus, +} from '@nestjs/common'; +import { ChainInfoService } from './chain-info.service'; +import { CreateChainInfoDto } from './dto/create-chain-info.dto'; +import { UpdateChainInfoDto } from './dto/update-chain-info.dto'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiCreatedResponse, + ApiForbiddenResponse, + ApiNoContentResponse, + ApiOkResponse, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; +import { Roles } from '../../auth/role/roles.decorator'; +import { RoleGuard } from '../../auth/role/roles.guard'; +import { MESSAGES, SwaggerBaseApiResponse, USER_ROLE } from '../../shared'; +import { JwtAuthGuard } from '../../auth/jwt/jwt-auth.guard'; +import { ChainInfoResponseDto } from './dto/chain-info-response.dto'; +import { ChainInfo } from '../../shared/entities/chain-info.entity'; + +@ApiUnauthorizedResponse({ + description: MESSAGES.ERROR.NOT_PERMISSION, +}) +@ApiForbiddenResponse({ + description: MESSAGES.ERROR.NOT_PERMISSION, +}) +@ApiBadRequestResponse({ + description: MESSAGES.ERROR.BAD_REQUEST, +}) +@ApiTags('chain-info') +@Controller() +export class ChainInfoController { + constructor(private readonly chainInfoService: ChainInfoService) {} + + @Post('admin/chain-info') + @UseGuards(JwtAuthGuard, RoleGuard) + @Roles(USER_ROLE.ADMIN) + @ApiBearerAuth() + @ApiCreatedResponse({ + type: ChainInfoResponseDto, + }) + async create(@Body() createChainInfoDto: CreateChainInfoDto) { + return this.chainInfoService.create(createChainInfoDto); + } + + @Get('chain-info') + @ApiOkResponse({ + type: SwaggerBaseApiResponse(ChainInfoResponseDto), + }) + @HttpCode(HttpStatus.OK) + async findAll(): Promise<{ data: ChainInfo[]; meta: { count: number } }> { + return await this.chainInfoService.findAll(); + } + + @Get('chain-info/:id') + @ApiOkResponse({ + type: ChainInfoResponseDto, + }) + async findOne(@Param('id') id: string): Promise { + return this.chainInfoService.findOne(+id); + } + + @Put('admin/chain-info/:id') + @UseGuards(JwtAuthGuard, RoleGuard) + @Roles(USER_ROLE.ADMIN) + @ApiBearerAuth() + @ApiOkResponse({ + type: ChainInfoResponseDto, + }) + async update( + @Param('id') id: string, + @Body() updateChainInfoDto: UpdateChainInfoDto, + ): Promise { + return this.chainInfoService.update(+id, updateChainInfoDto); + } + + @Delete('admin/chain-info/:id') + @UseGuards(JwtAuthGuard, RoleGuard) + @Roles(USER_ROLE.ADMIN) + @ApiBearerAuth() + @ApiNoContentResponse({ + description: 'Delete successfully.', + }) + @HttpCode(HttpStatus.NO_CONTENT) + async remove(@Param('id') id: string): Promise { + return this.chainInfoService.remove(+id); + } +} diff --git a/src/components/chain-info/chain-info.module.ts b/src/components/chain-info/chain-info.module.ts new file mode 100644 index 00000000..29237f6a --- /dev/null +++ b/src/components/chain-info/chain-info.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { ChainInfoService } from './chain-info.service'; +import { ChainInfoController } from './chain-info.controller'; +import { ChainInfo } from '../../shared/entities/chain-info.entity'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { IsUniqueConstraint } from './validator/is-unique.validator'; +import { UserModule } from '../user/user.module'; + +@Module({ + imports: [TypeOrmModule.forFeature([ChainInfo]), UserModule], + controllers: [ChainInfoController], + providers: [ChainInfoService, IsUniqueConstraint], +}) +export class ChainInfoModule {} diff --git a/src/components/chain-info/chain-info.service.ts b/src/components/chain-info/chain-info.service.ts new file mode 100644 index 00000000..33b7801d --- /dev/null +++ b/src/components/chain-info/chain-info.service.ts @@ -0,0 +1,61 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { CreateChainInfoDto } from './dto/create-chain-info.dto'; +import { UpdateChainInfoDto } from './dto/update-chain-info.dto'; +import { InjectRepository } from '@nestjs/typeorm'; +import { ChainInfo } from '../../shared/entities/chain-info.entity'; +import { Repository } from 'typeorm'; + +@Injectable() +export class ChainInfoService { + constructor( + @InjectRepository(ChainInfo) + private readonly chainInfoRepository: Repository, + ) {} + + async create(createChainInfoDto: CreateChainInfoDto): Promise { + return this.chainInfoRepository.save(createChainInfoDto); + } + + async findAll(): Promise<{ data: ChainInfo[]; meta: { count: number } }> { + const [data, count] = await this.chainInfoRepository.findAndCount({ + order: { updated_at: 'DESC', created_at: 'DESC' }, + }); + return { data: data || [], meta: { count: count || 0 } }; + } + + async findOne(id: number): Promise { + const chainInfo = await this.chainInfoRepository.findOne(id); + + if (!chainInfo) { + throw new NotFoundException('Chain not found.'); + } + + return chainInfo; + } + + async update( + id: number, + updateChainInfoDto: UpdateChainInfoDto, + ): Promise { + const currentChainInfo = await this.chainInfoRepository.findOne(id); + + if (!currentChainInfo) { + throw new NotFoundException('Chain not found.'); + } + + updateChainInfoDto.id = id; + this.chainInfoRepository.merge(currentChainInfo, updateChainInfoDto); + + return this.chainInfoRepository.save(currentChainInfo); + } + + async remove(id: number): Promise { + const chainInfo = await this.chainInfoRepository.findOne(id); + + if (!chainInfo) { + throw new NotFoundException('Chain not found.'); + } + + this.chainInfoRepository.delete(id); + } +} diff --git a/src/components/chain-info/dto/chain-info-response.dto.ts b/src/components/chain-info/dto/chain-info-response.dto.ts new file mode 100644 index 00000000..04ec0202 --- /dev/null +++ b/src/components/chain-info/dto/chain-info-response.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { UpdateChainInfoDto } from './update-chain-info.dto'; + +export class ChainInfoResponseDto extends PartialType(UpdateChainInfoDto) { + @ApiProperty() + created_at: Date; + + @ApiProperty() + updated_at: Date; +} diff --git a/src/components/chain-info/dto/create-chain-info.dto.ts b/src/components/chain-info/dto/create-chain-info.dto.ts new file mode 100644 index 00000000..1cfd71c0 --- /dev/null +++ b/src/components/chain-info/dto/create-chain-info.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; +import { IsUnique } from '../validator/is-unique.validator'; + +export class CreateChainInfoDto { + @ApiProperty() + @IsString() + @IsUnique(['chainId'], { message: 'Chain id must be unique.' }) + chainId: string; + + @ApiProperty() + @IsString() + chainName: string; + + @ApiProperty() + @IsString() + chainImage: string; +} diff --git a/src/components/chain-info/dto/update-chain-info.dto.ts b/src/components/chain-info/dto/update-chain-info.dto.ts new file mode 100644 index 00000000..1e172f1a --- /dev/null +++ b/src/components/chain-info/dto/update-chain-info.dto.ts @@ -0,0 +1,7 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { CreateChainInfoDto } from './create-chain-info.dto'; + +export class UpdateChainInfoDto extends PartialType(CreateChainInfoDto) { + @ApiProperty() + id: number; +} diff --git a/src/components/chain-info/validator/is-unique.validator.ts b/src/components/chain-info/validator/is-unique.validator.ts new file mode 100644 index 00000000..9dfae80a --- /dev/null +++ b/src/components/chain-info/validator/is-unique.validator.ts @@ -0,0 +1,54 @@ +import { + registerDecorator, + ValidationOptions, + ValidatorConstraint, + ValidatorConstraintInterface, + ValidationArguments, +} from 'class-validator'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Not, Repository } from 'typeorm'; +import { ChainInfo } from '../../../shared/entities/chain-info.entity'; + +@Injectable() +@ValidatorConstraint({ name: 'isUnique', async: true }) +export class IsUniqueConstraint implements ValidatorConstraintInterface { + constructor( + @InjectRepository(ChainInfo) + private chainInfoRepository: Repository, + ) {} + + async validate(value: any, args: ValidationArguments) { + const entity = args.object as any; + const propertiesName = args.constraints[0]; + + const whereCondition = propertiesName.reduce( + (where, propertyName) => { + where[propertyName] = args.object[propertyName]; + return where; + }, + { id: Not(Number(entity?.id) || 0) }, + ); + const existingChainInfo = await this.chainInfoRepository.findOne({ + where: whereCondition, + }); + + return !existingChainInfo; + } +} + +export function IsUnique( + properties: string[], + validationOptions?: ValidationOptions, +) { + return function (object: any, propertyName: string) { + registerDecorator({ + name: 'isUnique', + target: object.constructor, + propertyName: propertyName, + constraints: [properties], + options: validationOptions, + validator: IsUniqueConstraint, + }); + }; +} diff --git a/src/components/components.module.ts b/src/components/components.module.ts index f9d53381..755be5ac 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -1,9 +1,13 @@ import { Module } from '@nestjs/common'; import { ScheduleModule } from '@nestjs/schedule'; import { SharedModule } from '../shared/shared.module'; -import { MetricModule } from './metric/metric.module'; +import { ChainInfoModule } from './chain-info/chain-info.module'; @Module({ - imports: [SharedModule, MetricModule, ScheduleModule.forRoot()], + imports: [ + SharedModule, + ScheduleModule.forRoot(), + ChainInfoModule, + ], }) export class ComponentsModule {} diff --git a/src/components/cw20-token/controllers/cw20-token.controller.ts b/src/components/cw20-token/controllers/cw20-token.controller.ts index 1e53f49a..ea328764 100644 --- a/src/components/cw20-token/controllers/cw20-token.controller.ts +++ b/src/components/cw20-token/controllers/cw20-token.controller.ts @@ -3,26 +3,59 @@ import { CacheInterceptor, ClassSerializerInterceptor, Controller, + Delete, Get, + HttpCode, HttpStatus, Param, Post, + Put, Query, + UseGuards, UseInterceptors, } from '@nestjs/common'; -import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiCreatedResponse, + ApiForbiddenResponse, + ApiOkResponse, + ApiOperation, + ApiResponse, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; import { AkcLogger, + MESSAGES, ReqContext, RequestContext, TokenMarkets, + USER_ROLE, } from '../../../shared'; -import { Cw20TokenByOwnerParamsDto } from '../dtos/cw20-token-by-owner-params.dto'; import { Cw20TokenService } from '../services/cw20-token.service'; import { Cw20TokenMarketParamsDto } from '../dtos/cw20-token-market-params.dto'; +import { JwtAuthGuard } from '../../../auth/jwt/jwt-auth.guard'; +import { Roles } from '../../../auth/role/roles.decorator'; +import { RoleGuard } from '../../../auth/role/roles.guard'; +import { CreateCw20TokenDto } from '../dtos/create-cw20-token.dto'; +import { UpdateCw20TokenDto } from '../dtos/update-cw20-token.dto'; +import { Cw20TokenResponseDto } from '../dtos/cw20-token-response.dto'; +import { CreateIbcDto } from '../dtos/create-ibc.dto'; +import { UpdateIbcDto } from '../dtos/update-ibc.dto'; +import { IbcResponseDto } from '../dtos/ibc-response.dto'; +@ApiUnauthorizedResponse({ + description: MESSAGES.ERROR.NOT_PERMISSION, +}) +@ApiForbiddenResponse({ + description: MESSAGES.ERROR.NOT_PERMISSION, +}) +@ApiBadRequestResponse({ + description: MESSAGES.ERROR.BAD_REQUEST, +}) @ApiTags('cw20-tokens') -@Controller('cw20-tokens') +@Controller() export class Cw20TokenController { constructor( private readonly cw20TokenService: Cw20TokenService, @@ -31,7 +64,7 @@ export class Cw20TokenController { this.logger.setContext(Cw20TokenController.name); } - @Get('get-by-owner/:owner') + @Get('cw20-tokens/get-by-owner/:owner') @ApiOperation({ summary: 'Get list cw20 tokens by owner' }) @ApiResponse({ status: HttpStatus.OK }) @UseInterceptors(ClassSerializerInterceptor) @@ -49,7 +82,7 @@ export class Cw20TokenController { return { data: tokens, meta: { count } }; } - @Get('price/:id') + @Get('cw20-tokens/price/:id') @ApiOperation({ summary: 'Get price of cw20/cw721 token by id' }) @ApiResponse({ status: HttpStatus.OK }) @UseInterceptors(ClassSerializerInterceptor) @@ -63,7 +96,7 @@ export class Cw20TokenController { return { data: price, meta: {} }; } - @Get('total-asset/:accountAddress') + @Get('cw20-tokens/total-asset/:accountAddress') @ApiOperation({ summary: 'Get total asset of coins and tokens' }) @ApiResponse({ status: HttpStatus.OK }) @UseInterceptors(ClassSerializerInterceptor) @@ -83,7 +116,7 @@ export class Cw20TokenController { return { data: price, meta: {} }; } - @Get('token-market') + @Get('cw20-tokens/token-market') @ApiOperation({ summary: 'Get token market of cw20 token by contract address', }) @@ -97,4 +130,78 @@ export class Cw20TokenController { this.logger.log(ctx, `${this.getPriceById.name} was called!`); return await this.cw20TokenService.getTokenMarket(ctx, query); } + + @Post('admin/cw20-tokens') + @UseGuards(JwtAuthGuard, RoleGuard) + @Roles(USER_ROLE.ADMIN) + @ApiBearerAuth() + @ApiCreatedResponse({ + description: 'Return a cw20 token.', + type: Cw20TokenResponseDto, + }) + async create( + @Body() createCw20TokenDto: CreateCw20TokenDto, + ): Promise { + return await this.cw20TokenService.create( + createCw20TokenDto, + Cw20TokenResponseDto, + ); + } + + @Put('admin/cw20-tokens/:id') + @UseGuards(JwtAuthGuard, RoleGuard) + @Roles(USER_ROLE.ADMIN) + @ApiBearerAuth() + @ApiOkResponse({ + description: 'Return a cw20 token.', + type: Cw20TokenResponseDto, + }) + async update( + @Body() updateCw20TokenDto: UpdateCw20TokenDto, + @Param('id') id: number, + ): Promise { + return await this.cw20TokenService.update( + id, + updateCw20TokenDto, + Cw20TokenResponseDto, + ); + } + + @Delete('admin/tokens-market/:id') + @UseGuards(JwtAuthGuard, RoleGuard) + @Roles(USER_ROLE.ADMIN) + @ApiBearerAuth() + @HttpCode(HttpStatus.NO_CONTENT) + async remove(@Param('id') id: number): Promise { + await this.cw20TokenService.remove(id); + } + + @Post('admin/ibc') + @UseGuards(JwtAuthGuard, RoleGuard) + @Roles(USER_ROLE.ADMIN) + @ApiBearerAuth() + @ApiCreatedResponse({ + description: 'Return an ibc token.', + type: IbcResponseDto, + }) + async createIbc( + @Body() createIbcDto: CreateIbcDto, + ): Promise { + return await this.cw20TokenService.create(createIbcDto, IbcResponseDto); + } + + @Put('admin/ibc/:id') + @UseGuards(JwtAuthGuard, RoleGuard) + @Roles(USER_ROLE.ADMIN) + @ApiBearerAuth() + @ApiOkResponse({ + description: 'Return an ibc token.', + type: IbcResponseDto, + }) + async updateIbc( + @Body() updateIbcDto: UpdateIbcDto, + @Param('id') id: number, + ): Promise { + return await this.cw20TokenService.update(id, updateIbcDto, IbcResponseDto); + } } diff --git a/src/components/cw20-token/cw20-token.module.ts b/src/components/cw20-token/cw20-token.module.ts index 8e98e7f9..3320aa69 100644 --- a/src/components/cw20-token/cw20-token.module.ts +++ b/src/components/cw20-token/cw20-token.module.ts @@ -3,21 +3,32 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ServiceUtil } from '../../shared/utils/service.util'; -import { SharedModule } from '../../shared'; +import { SharedModule, TokenMarkets } from '../../shared'; import { Cw20TokenController } from './controllers/cw20-token.controller'; import { Cw20TokenService } from './services/cw20-token.service'; import { RedisUtil } from '../../shared/utils/redis.util'; import { AccountService } from '../account/services/account.service'; import { TokenMarketsRepository } from './repositories/token-markets.repository'; +import { UserModule } from '../user/user.module'; +import { IsUniqueConstraint } from './validators/is-unique.validator'; +import { IsUniqueManyColumnConstraint } from './validators/is-unique-many-column.validator'; @Module({ imports: [ SharedModule, - TypeOrmModule.forFeature([TokenMarketsRepository]), + TypeOrmModule.forFeature([TokenMarkets, TokenMarketsRepository]), ConfigModule, HttpModule, + UserModule, + ], + providers: [ + Cw20TokenService, + ServiceUtil, + RedisUtil, + AccountService, + IsUniqueConstraint, + IsUniqueManyColumnConstraint, ], - providers: [Cw20TokenService, ServiceUtil, RedisUtil, AccountService], controllers: [Cw20TokenController], exports: [Cw20TokenService], }) diff --git a/src/components/cw20-token/dtos/create-cw20-token.dto.ts b/src/components/cw20-token/dtos/create-cw20-token.dto.ts new file mode 100644 index 00000000..2f30dafa --- /dev/null +++ b/src/components/cw20-token/dtos/create-cw20-token.dto.ts @@ -0,0 +1,40 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsValidBench32Address } from '../../watch-list/validators/validate-address'; +import { IsUnique } from '../validators/is-unique.validator'; + +export class CreateCw20TokenDto { + @ApiProperty() + @IsValidBench32Address('contract_address') + @IsUnique('contract_address') + contract_address: string; + + @ApiProperty() + coin_id: string; + + @ApiProperty() + symbol: string; + + @ApiProperty() + name: string; + + @ApiProperty() + image: string; + + @ApiProperty() + verify_status: string; + + @ApiProperty() + verify_text: string; + + @ApiProperty() + denom: string; + + @ApiProperty() + description: string; + + @ApiProperty() + decimal: number; + + @ApiProperty() + chain_id: string; +} diff --git a/src/components/cw20-token/dtos/create-ibc.dto.ts b/src/components/cw20-token/dtos/create-ibc.dto.ts new file mode 100644 index 00000000..caf83a88 --- /dev/null +++ b/src/components/cw20-token/dtos/create-ibc.dto.ts @@ -0,0 +1,39 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Expose } from 'class-transformer'; +import { IsNumber, IsString } from 'class-validator'; +import { IsUniqueManyColumn } from '../validators/is-unique-many-column.validator'; + +@Expose() +export class CreateIbcDto { + @IsUniqueManyColumn(['chain_id', 'denom'], { + message: 'chain_id and denom must be unique.', + }) + @ApiProperty() + chain_id: string; + + @ApiProperty() + @IsString() + denom: string; + + @ApiProperty() + coin_id: string; + + @ApiProperty() + name: string; + + @ApiProperty() + symbol: string; + + @ApiProperty() + image: string; + + @ApiProperty() + @IsNumber() + decimal: number; + + @ApiProperty() + verify_status: string; + + @ApiProperty() + verify_text: string; +} diff --git a/src/components/cw20-token/dtos/cw20-token-market-params.dto.ts b/src/components/cw20-token/dtos/cw20-token-market-params.dto.ts index 39105c95..d2f73efb 100644 --- a/src/components/cw20-token/dtos/cw20-token-market-params.dto.ts +++ b/src/components/cw20-token/dtos/cw20-token-market-params.dto.ts @@ -7,4 +7,11 @@ export class Cw20TokenMarketParamsDto { default: '', }) contractAddress: string; + + @ApiPropertyOptional({ + description: `Optional get ibc token or all.`, + type: Boolean, + default: false, + }) + onlyIbc: string; } diff --git a/src/components/cw20-token/dtos/cw20-token-response.dto.ts b/src/components/cw20-token/dtos/cw20-token-response.dto.ts new file mode 100644 index 00000000..10a2f330 --- /dev/null +++ b/src/components/cw20-token/dtos/cw20-token-response.dto.ts @@ -0,0 +1,37 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { UpdateCw20TokenDto } from './update-cw20-token.dto'; + +export class Cw20TokenResponseDto extends PartialType(UpdateCw20TokenDto) { + @ApiProperty() + max_supply: number; + + @ApiProperty() + current_price: number; + + @ApiProperty() + price_change_percentage_24h: number; + + @ApiProperty() + total_volume: number; + + @ApiProperty() + circulating_market_cap: number; + + @ApiProperty() + circulating_supply: number; + + @ApiProperty() + market_cap: number; + + @ApiProperty() + fully_diluted_valuation: number; + + @ApiProperty() + created_at: Date; + + @ApiProperty() + updated_at: Date; + + @ApiProperty() + id: number; +} diff --git a/src/components/cw20-token/dtos/ibc-response.dto.ts b/src/components/cw20-token/dtos/ibc-response.dto.ts new file mode 100644 index 00000000..71521f87 --- /dev/null +++ b/src/components/cw20-token/dtos/ibc-response.dto.ts @@ -0,0 +1,46 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { UpdateIbcDto } from './update-ibc.dto'; +import { Exclude, Expose } from 'class-transformer'; +export class IbcResponseDto extends PartialType(UpdateIbcDto) { + @Expose() + @ApiProperty() + id: number; + + @Expose() + @ApiProperty() + created_at: Date; + + @Expose() + @ApiProperty() + updated_at: Date; + + @Exclude() + max_supply; + + @Exclude() + current_price; + + @Exclude() + price_change_percentage_24h; + + @Exclude() + total_volume; + + @Exclude() + circulating_supply; + + @Exclude() + circulating_market_cap; + + @Exclude() + market_cap; + + @Exclude() + fully_diluted_valuation; + + @Exclude() + contract_address; + + @Exclude() + description; +} diff --git a/src/components/cw20-token/dtos/update-cw20-token.dto.ts b/src/components/cw20-token/dtos/update-cw20-token.dto.ts new file mode 100644 index 00000000..227ee1e3 --- /dev/null +++ b/src/components/cw20-token/dtos/update-cw20-token.dto.ts @@ -0,0 +1,7 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { CreateCw20TokenDto } from './create-cw20-token.dto'; + +export class UpdateCw20TokenDto extends PartialType(CreateCw20TokenDto) { + @ApiProperty() + id: number; +} diff --git a/src/components/cw20-token/dtos/update-ibc.dto.ts b/src/components/cw20-token/dtos/update-ibc.dto.ts new file mode 100644 index 00000000..288acb78 --- /dev/null +++ b/src/components/cw20-token/dtos/update-ibc.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; +import { CreateIbcDto } from './create-ibc.dto'; +import { Expose } from 'class-transformer'; + +export class UpdateIbcDto extends PartialType(CreateIbcDto) { + @Expose() + @ApiProperty() + id: number; + + @Expose() + @ApiProperty() + @IsString() + @IsOptional() + denom: string; + + @Expose() + @ApiProperty() + @IsNumber() + @IsOptional() + decimal: number; +} diff --git a/src/components/cw20-token/repositories/token-markets.repository.ts b/src/components/cw20-token/repositories/token-markets.repository.ts index 9014cebe..e942edf6 100644 --- a/src/components/cw20-token/repositories/token-markets.repository.ts +++ b/src/components/cw20-token/repositories/token-markets.repository.ts @@ -1,9 +1,7 @@ import { Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { EntityRepository, ObjectLiteral, Repository } from 'typeorm'; -import { CONTRACT_CODE_RESULT, TokenMarkets } from '../../../shared'; -import { SmartContractCode } from '../../../shared/entities/smart-contract-code.entity'; -import { Cw20TokenParamsDto } from '../dtos/cw20-token-params.dto'; +import { TokenMarkets } from '../../../shared'; @EntityRepository(TokenMarkets) export class TokenMarketsRepository extends Repository { @@ -18,49 +16,12 @@ export class TokenMarketsRepository extends Repository { ); } - async getCw20TokenMarkets(request: Cw20TokenParamsDto) { - const sqlSelect = `tm.*`; - - const queryBuilder = this.createQueryBuilder('tm') - .select(sqlSelect) - .innerJoin( - SmartContractCode, - 'smc', - `smc.code_id = tm.code_id AND smc.result='${CONTRACT_CODE_RESULT.CORRECT}' `, - ) - .where(`tm.coin_id NOT IN ('bitcoin','aura-network')`) - .andWhere( - '(LOWER(tm.name) LIKE :keyword OR LOWER(tm.contract_address) LIKE :keyword)', - { - keyword: `%${(request.keyword || '').toLowerCase()}%`, - }, - ) - - .limit(request.limit) - .offset(request.offset) - .orderBy( - request?.sort_column && request?.sort_order - ? { - [`${request.sort_column}`]: - request.sort_order.toLowerCase() === 'asc' ? 'ASC' : 'DESC', - updated_at: 'DESC', - } - : { circulating_market_cap: 'DESC', updated_at: 'DESC' }, - ); - - const list = await queryBuilder.getRawMany(); - const count = await queryBuilder.getCount(); - - return { list, count }; - } - async countCw20TokensHavingCoinId() { const sqlSelect = `tm.contract_address, tm.coin_id`; const queryBuilder = this.createQueryBuilder('tm') .select(sqlSelect) - .where("tm.coin_id <> '' ") - .andWhere("tm.coin_id <> 'aura-network' "); + .where("tm.coin_id <> '' "); return await queryBuilder.getCount(); } @@ -71,7 +32,6 @@ export class TokenMarketsRepository extends Repository { const queryBuilder = this.createQueryBuilder('tm') .select(sqlSelect) .where("tm.coin_id <> '' ") - .andWhere("tm.coin_id <> 'aura-network' ") .limit(limit) .offset(pageIndex * limit); diff --git a/src/components/cw20-token/services/cw20-token.service.ts b/src/components/cw20-token/services/cw20-token.service.ts index 4a14df27..38e7dc9c 100644 --- a/src/components/cw20-token/services/cw20-token.service.ts +++ b/src/components/cw20-token/services/cw20-token.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { IsNull, Not } from 'typeorm'; import * as util from 'util'; import { AccountService } from '../../../components/account/services/account.service'; @@ -15,6 +15,13 @@ import { ServiceUtil } from '../../../shared/utils/service.util'; import { AssetDto } from '../dtos/asset.dto'; import { TokenMarketsRepository } from '../repositories/token-markets.repository'; import { Cw20TokenMarketParamsDto } from '../dtos/cw20-token-market-params.dto'; +import { CreateCw20TokenDto } from '../dtos/create-cw20-token.dto'; +import { UpdateCw20TokenDto } from '../dtos/update-cw20-token.dto'; +import { CreateIbcDto } from '../dtos/create-ibc.dto'; +import { plainToClass } from 'class-transformer'; +import { Cw20TokenResponseDto } from '../dtos/cw20-token-response.dto'; +import { IbcResponseDto } from '../dtos/ibc-response.dto'; +import { UpdateIbcDto } from '../dtos/update-ibc.dto'; @Injectable() export class Cw20TokenService { @@ -179,6 +186,10 @@ export class Cw20TokenService { { denom: query.contractAddress }, ], }); + } else if (query.onlyIbc === 'true') { + return await this.tokenMarketsRepository.find({ + where: { denom: Not(IsNull()) }, + }); } else { return await this.tokenMarketsRepository.find(); } @@ -344,4 +355,51 @@ export class Cw20TokenService { } return result; } + + async create( + createTokenMarketsDto: CreateCw20TokenDto | CreateIbcDto, + returnType: typeof Cw20TokenResponseDto | typeof IbcResponseDto, + ): Promise { + const newTokenMarket = await this.tokenMarketsRepository.save( + createTokenMarketsDto, + ); + + return plainToClass(returnType, newTokenMarket); + } + + async update( + id: number, + updateTokenMarketsDto: UpdateCw20TokenDto | UpdateIbcDto, + returnType: typeof Cw20TokenResponseDto | typeof IbcResponseDto, + ): Promise { + const foundTokenMarkets = await this.tokenMarketsRepository.findOne({ + where: { id }, + }); + + if (foundTokenMarkets) { + updateTokenMarketsDto.id = foundTokenMarkets.id; + + await this.tokenMarketsRepository.merge( + foundTokenMarkets, + updateTokenMarketsDto, + ); + await this.tokenMarketsRepository.save(foundTokenMarkets); + + return plainToClass(returnType, foundTokenMarkets); + } else { + throw new NotFoundException(); + } + } + + async remove(id: number): Promise { + const foundTokenMarkets = await this.tokenMarketsRepository.findOne({ + where: { id }, + }); + + if (!foundTokenMarkets) { + throw new NotFoundException(); + } + + await this.tokenMarketsRepository.delete(id); + } } diff --git a/src/components/cw20-token/validators/is-unique-many-column.validator.ts b/src/components/cw20-token/validators/is-unique-many-column.validator.ts new file mode 100644 index 00000000..e3807feb --- /dev/null +++ b/src/components/cw20-token/validators/is-unique-many-column.validator.ts @@ -0,0 +1,58 @@ +import { + registerDecorator, + ValidationOptions, + ValidatorConstraint, + ValidatorConstraintInterface, + ValidationArguments, +} from 'class-validator'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { TokenMarkets } from '../../../shared/entities/token-markets.entity'; +import { TokenMarketsRepository } from '../repositories/token-markets.repository'; +import { Not } from 'typeorm'; + +@Injectable() +@ValidatorConstraint({ name: 'isUniqueManyColumn', async: true }) +export class IsUniqueManyColumnConstraint + implements ValidatorConstraintInterface +{ + constructor( + @InjectRepository(TokenMarkets) + private tokenMarketsRepository: TokenMarketsRepository, + ) {} + + async validate(value: any, args: ValidationArguments) { + const entity = args.object as any; + const propertiesName = args.constraints[0]; + + const whereCondition = propertiesName.reduce( + (where, propertyName) => { + where[propertyName] = args.object[propertyName]; + return where; + }, + { id: Not(Number(entity?.id) || 0) }, + ); + + const existingTokenMarkets = await this.tokenMarketsRepository.findOne({ + where: whereCondition, + }); + + return !existingTokenMarkets; + } +} + +export function IsUniqueManyColumn( + property: string[], + validationOptions?: ValidationOptions, +) { + return function (object: any, propertyName: string) { + registerDecorator({ + name: 'isUnique', + target: object.constructor, + propertyName: propertyName, + constraints: [property], + options: validationOptions, + validator: IsUniqueManyColumnConstraint, + }); + }; +} diff --git a/src/components/cw20-token/validators/is-unique.validator.ts b/src/components/cw20-token/validators/is-unique.validator.ts new file mode 100644 index 00000000..6896a0bf --- /dev/null +++ b/src/components/cw20-token/validators/is-unique.validator.ts @@ -0,0 +1,55 @@ +import { + registerDecorator, + ValidationOptions, + ValidatorConstraint, + ValidatorConstraintInterface, + ValidationArguments, +} from 'class-validator'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Not } from 'typeorm'; +import { TokenMarkets } from '../../../shared/entities/token-markets.entity'; +import { TokenMarketsRepository } from '../repositories/token-markets.repository'; + +@Injectable() +@ValidatorConstraint({ name: 'isUnique', async: true }) +export class IsUniqueConstraint implements ValidatorConstraintInterface { + constructor( + @InjectRepository(TokenMarkets) + private tokenMarketsRepository: TokenMarketsRepository, + ) {} + + async validate(value: any, args: ValidationArguments) { + const [relatedPropertyName] = args.constraints; + const entity = args.object as any; + const existingTokenMarkets = await this.tokenMarketsRepository.findOne({ + where: { + [relatedPropertyName]: value, + id: Not(Number(entity?.id) || 0), + }, + }); + + return !existingTokenMarkets; + } + + defaultMessage(args: ValidationArguments) { + const [relatedPropertyName] = args.constraints; + return `${relatedPropertyName} must be unique.`; + } +} + +export function IsUnique( + property: string, + validationOptions?: ValidationOptions, +) { + return function (object: any, propertyName: string) { + registerDecorator({ + name: 'isUnique', + target: object.constructor, + propertyName: propertyName, + constraints: [property], + options: validationOptions, + validator: IsUniqueConstraint, + }); + }; +} diff --git a/src/components/export-csv/services/export-csv.service.ts b/src/components/export-csv/services/export-csv.service.ts index 37829baa..08a8d35a 100644 --- a/src/components/export-csv/services/export-csv.service.ts +++ b/src/components/export-csv/services/export-csv.service.ts @@ -325,7 +325,6 @@ export class ExportCsvService { endTime: payload.dataRangeType === RANGE_EXPORT.Date ? payload.max : null, actionIn: ['mint', 'burn', 'transfer_nft', 'send_nft'], - neqCw4973: 'crates.io:cw4973', }, operationName: INDEXER_API_V2.OPERATION_NAME.TX_NFT_TRANSFER, }; diff --git a/src/components/metric/controllers/metric.controller.ts b/src/components/metric/controllers/metric.controller.ts deleted file mode 100644 index 7dc30af8..00000000 --- a/src/components/metric/controllers/metric.controller.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { - ClassSerializerInterceptor, - Controller, - Get, - HttpStatus, - Query, - UseInterceptors, -} from '@nestjs/common'; -import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; -import { - AkcLogger, - BaseApiResponse, - ReqContext, - RequestContext, - SwaggerBaseApiResponse, -} from '../../../shared'; -import { Cw20MetricParamsDto } from '../dtos/cw20-metric-params.dto'; -import { MetricOutput } from '../dtos/metric-output.dto'; -import { TokenOutput } from '../dtos/token-output.dto'; -import { MetricService } from '../services/metric.service'; - -@ApiTags('metrics') -@Controller('metrics') -export class MetricController { - constructor( - private readonly metricService: MetricService, - private readonly logger: AkcLogger, - ) { - this.logger.setContext(MetricController.name); - } - - @Get('token') - @ApiOperation({ summary: 'Get token by coin id' }) - @ApiResponse({ - status: HttpStatus.OK, - type: SwaggerBaseApiResponse(MetricOutput), - }) - @UseInterceptors(ClassSerializerInterceptor) - async getTokenInfoMetric( - @ReqContext() ctx: RequestContext, - @Query() query: Cw20MetricParamsDto, - ): Promise> { - this.logger.log(ctx, `${this.getTokenInfoMetric.name} was called!`); - - const metrics = await this.metricService.getTokenInfo( - ctx, - query.min, - query.max, - query.rangeType, - query.step, - query.coinId, - ); - - return { data: metrics, meta: null }; - } - - @Get('token-market') - @ApiOperation({ summary: 'Get market info of token' }) - @ApiResponse({ - status: HttpStatus.OK, - type: SwaggerBaseApiResponse(TokenOutput), - }) - @UseInterceptors(ClassSerializerInterceptor) - async getTokenMarketInfoMetric( - @ReqContext() ctx: RequestContext, - @Query('coinId') coinId: string, - ): Promise> { - this.logger.log(ctx, `${this.getTokenMarketInfoMetric.name} was called!`); - - const metric = await this.metricService.getTokenMarketInfo(ctx, coinId); - - return { data: metric, meta: null }; - } -} diff --git a/src/components/metric/dtos/cw20-metric-params.dto.ts b/src/components/metric/dtos/cw20-metric-params.dto.ts deleted file mode 100644 index 6f89da9c..00000000 --- a/src/components/metric/dtos/cw20-metric-params.dto.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsEnum, IsNotEmpty, IsNumber } from 'class-validator'; -import { RangeType } from '../utils/enum'; - -export class Cw20MetricParamsDto { - @ApiPropertyOptional({ - enum: RangeType, - description: 'Optional, defaults to minute', - type: RangeType, - example: RangeType.minute, - default: RangeType.minute, - }) - @IsEnum(RangeType) - @ApiProperty() - readonly rangeType: RangeType = RangeType.minute; - - @ApiPropertyOptional() - @Transform(({ value }) => Number(value)) - step: number; - - @ApiProperty() - @IsNotEmpty() - coinId: string; - - @ApiProperty() - @IsNotEmpty() - @IsNumber() - @Transform(({ value }) => Number(value)) - min: number; - - @ApiProperty() - @IsNotEmpty() - @IsNumber() - @Transform(({ value }) => Number(value)) - max: number; -} diff --git a/src/components/metric/dtos/metric-condition.dto.ts b/src/components/metric/dtos/metric-condition.dto.ts deleted file mode 100644 index 80eec466..00000000 --- a/src/components/metric/dtos/metric-condition.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { TypeDate } from '../utils/enum'; - -export class MetricConditionDto { - fluxType: string; - formatDate: string; - amount: number; - step = 1; - type: TypeDate; -} diff --git a/src/components/metric/dtos/metric-output.dto.ts b/src/components/metric/dtos/metric-output.dto.ts deleted file mode 100644 index 3744eed0..00000000 --- a/src/components/metric/dtos/metric-output.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Expose } from 'class-transformer'; - -export class MetricOutput { - @Expose() - total: string; - - @Expose() - timestamp: string; -} diff --git a/src/components/metric/dtos/metric-params.dto.ts b/src/components/metric/dtos/metric-params.dto.ts deleted file mode 100644 index 093e0a8e..00000000 --- a/src/components/metric/dtos/metric-params.dto.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsEnum } from 'class-validator'; -import { Range } from '../utils/enum'; - -export class MetricParamsDto { - @ApiPropertyOptional({ - enum: Range, - description: 'Optional, defaults to 24h', - type: Range, - example: Range.day, - default: Range.hour, - }) - @IsEnum(Range) - @ApiProperty() - readonly range: Range = Range.hour; - - @ApiPropertyOptional({ - description: 'Optional, defaults to 0s', - default: 0, - }) - timezone = 0; -} diff --git a/src/components/metric/dtos/metric-status-output.dto.ts b/src/components/metric/dtos/metric-status-output.dto.ts deleted file mode 100644 index 76502a61..00000000 --- a/src/components/metric/dtos/metric-status-output.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Expose } from 'class-transformer'; - -export class MetricStatusOutput { - @Expose() - block_height: string; - - @Expose() - total_txs_num: string; - - @Expose() - total_validator_num: string; -} diff --git a/src/components/metric/dtos/metric-transaction-output.dto.ts b/src/components/metric/dtos/metric-transaction-output.dto.ts deleted file mode 100644 index f91085a8..00000000 --- a/src/components/metric/dtos/metric-transaction-output.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Expose } from 'class-transformer'; - -export class MetricTransactionOutput { - @Expose() - total: string; - - @Expose() - timestamp: string; -} diff --git a/src/components/metric/dtos/token-output.dto.ts b/src/components/metric/dtos/token-output.dto.ts deleted file mode 100644 index 29ccb309..00000000 --- a/src/components/metric/dtos/token-output.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -export class TokenOutput { - coinId = ''; - current_price = 0; - max_supply = 0; - total_volume = 0; - market_cap = 0; - timestamp = ''; - price_change_percentage_24h = 0; -} diff --git a/src/components/metric/metric.module.ts b/src/components/metric/metric.module.ts deleted file mode 100644 index 9cb20910..00000000 --- a/src/components/metric/metric.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { SharedModule } from '../../shared'; - -import { MetricController } from './controllers/metric.controller'; -import { MetricService } from './services/metric.service'; - -@Module({ - imports: [SharedModule, ConfigModule], - providers: [MetricService], - controllers: [MetricController], - exports: [MetricService], -}) -export class MetricModule {} diff --git a/src/components/metric/services/influxdb-client.ts b/src/components/metric/services/influxdb-client.ts deleted file mode 100644 index ba04774b..00000000 --- a/src/components/metric/services/influxdb-client.ts +++ /dev/null @@ -1,338 +0,0 @@ -import { - InfluxDB, - Point, - QueryApi, - WriteApi, -} from '@influxdata/influxdb-client'; -import { TokenOutput } from '../dtos/token-output.dto'; -import { TokenMarkets } from '../../../shared'; - -export class InfluxDBClient { - private client: InfluxDB; - private queryApi: QueryApi; - private writeApi: WriteApi; - - constructor( - public bucket: string, - public org: string, - public url: string, - public token: string, - ) { - this.client = new InfluxDB({ url, token, timeout: 60000 }); - } - - initQueryApi(): void { - this.queryApi = this.client.getQueryApi(this.org); - } - - initWriteApi(): void { - this.writeApi = this.client.getWriteApi(this.org, this.bucket); - return; - } - - queryData(measurement, statTime, step) { - const results: { - count: string; - timestamp: string; - }[] = []; - const query = `from(bucket: "${this.bucket}") |> range(start: ${statTime}) |> filter(fn: (r) => r._measurement == "${measurement}") |> window(every: ${step}) |> count()`; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const output = new Promise((resolve, reject) => { - this.queryApi.queryRows(query, { - next(row, tableMeta) { - const o = tableMeta.toObject(row); - results.push({ - timestamp: o._start, - count: String(o._value), - }); - }, - error(error) { - console.error(error); - console.log('Finished ERROR'); - return resolve(results); - }, - complete() { - console.log('Finished SUCCESS'); - return resolve(results); - }, - }); - }); - return output; - } - - /** - * Sum data by column - * @param measurement - * @param statTime - * @param step - * @param column - * @returns - */ - sumData(measurement: string, start: string, step: string, column: string) { - const query = ` from(bucket: "${this.bucket}") |> range(start: ${start}) |> filter(fn: (r) => r._measurement == "${measurement}") |> filter(fn: (r) => r["_field"] == "${column}") |> window(every: ${step}) |> sum()`; - return this.bindingData(query); - } - - /** - * Sum data by column with timezone (in hour) - * @param measurement - * @param statTime - * @param step - * @param column - * @param offsetInHours - * @returns - */ - sumDataWithTimezoneOffset( - measurement: string, - start: string, - step: string, - column: string, - withTimezone = true, - offsetInHours = 0, - ) { - if (withTimezone) { - const query = ` - from(bucket: "${this.bucket}") - |> range(start: ${start}) - |> filter(fn: (r) => r._measurement == "${measurement}") - |> filter(fn: (r) => r["_field"] == "${column}") - |> aggregateWindow(every: 1h, timeSrc: "_start", fn: sum) - |> timeShift(duration: ${-offsetInHours}h) - |> window(every: ${step}, createEmpty: true) - |> timeShift(duration: ${offsetInHours}h) - |> sum()`; - return this.bindingData(query); - } - const query = ` - from(bucket: "${this.bucket}") - |> range(start: ${start}) - |> filter(fn: (r) => r._measurement == "${measurement}") - |> filter(fn: (r) => r["_field"] == "${column}") - |> window(every: ${step}, createEmpty: true) - |> sum()`; - return this.bindingData(query); - } - - /** - * Get number transactions - * @param start - * @returns - */ - getNumberTransactions(start: string) { - const query = ` from(bucket: "${this.bucket}") - |> range(start: ${start}) - |> filter(fn: (r) => r._measurement == "blocks_measurement") - |> filter(fn: (r) => r["_field"] == "num_txs") - |> group(columns: ["_measurement"]) - |> sum()`; - return this.bindingData(query); - } - - /** - * Convert result's Influx - * @param query - * @returns - */ - private bindingData(query: string): Promise { - const results: { - total: string; - timestamp: string; - }[] = []; - - const output = new Promise((resolve) => { - this.queryApi.queryRows(query, { - next(row, tableMeta) { - const o = tableMeta.toObject(row); - const date = new Date(o._stop); - date.setMinutes(0, 0, 0); - results.push({ - timestamp: o._start, - total: String(o._value), - }); - }, - error(error) { - console.error(error); - console.log('Finished ERROR'); - return resolve(results); - }, - complete() { - console.log('Finished SUCCESS'); - return resolve(results); - }, - }); - }); - return output; - } - - /** - * Get market info of token - * @param measurement - * @param start - * @param step - * @param coinId - * @returns - */ - getTokenMarketInfo( - measurement: string, - start: string, - step: string, - coinId: string, - ): Promise { - const results: Array = []; - const query = ` - import "date" - from(bucket: "${this.bucket}") - |> range(start: ${start}) - |> filter(fn: (r) => r._measurement == "${measurement}") - |> filter(fn: (r) => r["token_id"]== "${coinId}") - |> last(column: "_start") - |> pivot(rowKey:["_time"], columnKey:["_field"], valueColumn:"_value") - |> drop(columns:["_value"]) - |> last(column: "_start") - |> map(fn: (r) => ({ - coinId: r.coinId, - current_price: r.current_price, - total_volume: r.total_volume, - market_cap: r.market_cap, - price_change_percentage_24h: r.price_change_percentage_24h, - time: date.truncate(t: r._start, unit: ${step}) - }))`; - const output = new Promise((resolve) => { - this.queryApi.queryRows(query, { - next(row, tableMeta) { - const output = tableMeta.toObject(row); - const tokenOutput = new TokenOutput(); - tokenOutput.timestamp = output.time; - tokenOutput.coinId = String(output.coinId); - tokenOutput.current_price = Number(output.current_price) || 0; - tokenOutput.total_volume = Number(output.total_volume) || 0; - tokenOutput.market_cap = Number(output.market_cap) || 0; - tokenOutput.price_change_percentage_24h = - Number(output.price_change_percentage_24h) || 0; - results.push(tokenOutput); - }, - error(error) { - console.error(error); - console.log('Finished ERROR'); - return resolve(results); - }, - complete() { - console.log('Finished SUCCESS'); - return resolve(results); - }, - }); - }); - return output; - } - - /** - * Get token info - * @param measurement - * @param start - * @param step - * @param coinId - * @returns - */ - getTokenInfo( - measurement: string, - start: string, - stop: string, - step: string, - coinId: string, - ): Promise { - const results: Array = []; - const query = ` - import "date" - from(bucket: "${this.bucket}") - |> range(start:${start}, stop:${stop}) - |> filter(fn: (r) => r._measurement == "${measurement}") - |> filter(fn: (r) => r["token_id"]== "${coinId}") - |> filter(fn: (r) => r["_field"]== "coinId" - or r["_field"]== "current_price" - or r["_field"]== "price_change_percentage_24h" - or r["_field"]== "total_volume" - ) - |> pivot(rowKey:["_time"], columnKey:["_field"], valueColumn:"_value") - |> drop(columns:["_value"]) - |> window(every: ${step}, createEmpty: true, timeColumn: "_time") - |> last(column: "_start") - |> map(fn: (r) => ({ - coinId: r.coinId, - current_price: r.current_price, - price_change_percentage_24h: r.price_change_percentage_24h, - total_volume: r.total_volume, - time: date.truncate(t: r._start, unit: ${step}) - }))`; - - console.log(query); - - const output = new Promise((resolve) => { - this.queryApi.queryRows(query, { - next(row, tableMeta) { - const output = tableMeta.toObject(row); - const tokenOutput = new TokenOutput(); - tokenOutput.timestamp = output.time; - tokenOutput.coinId = String(output.coinId); - tokenOutput.current_price = Number(output.current_price) || 0; - tokenOutput.total_volume = Number(output.total_volume) || 0; - tokenOutput.price_change_percentage_24h = - Number(output.price_change_percentage_24h) || 0; - results.push(tokenOutput); - }, - error(error) { - console.error(error); - console.log('Finished ERROR'); - return resolve(results); - }, - complete() { - console.log('Finished SUCCESS'); - return resolve(results); - }, - }); - }); - return output; - } - - async writeBlockTokenPriceAndVolume(tokens: TokenMarkets[]) { - const points: Array = []; - tokens.forEach((token) => { - const point = new Point('token_cw20_measurement') - .tag('token_id', token.coin_id) - .stringField('coinId', token.coin_id) - .stringField('type', 'CW20') - .stringField('last_updated', token.updated_at) - .floatField('current_price', token.current_price) - .floatField( - 'price_change_percentage_24h', - token.price_change_percentage_24h, - ) - .floatField('total_volume', token.total_volume) - .floatField('circulating_supply', token.circulating_supply) - .floatField('circulating_market_cap', token.circulating_market_cap) - .floatField('max_supply', token.max_supply) - .floatField('market_cap', token.market_cap) - // .intField('current_holder', token.current_holder) - // .floatField('percent_hold', token.holder_change_percentage_24h) - .timestamp(this.convertDate(token.updated_at)); - points.push(point); - }); - - if (points.length > 0) { - this.writeApi.writePoints(points); - await this.writeApi.flush(); - } - } - - /** - * convertDate - * @param timestamp - * @returns - */ - convertDate(timestamp: any): Date { - const strTime = String(timestamp); - const idx = strTime.lastIndexOf('.'); - const dateConvert = - idx > -1 ? strTime.substring(0, idx) + '.000Z' : strTime; - return new Date(dateConvert); - } -} diff --git a/src/components/metric/services/metric.service.ts b/src/components/metric/services/metric.service.ts deleted file mode 100644 index e6448b51..00000000 --- a/src/components/metric/services/metric.service.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import * as moment from 'moment'; -import { AkcLogger, RequestContext } from '../../../shared'; -import { MetricOutput } from '../dtos/metric-output.dto'; -import { TokenOutput } from '../dtos/token-output.dto'; -import { Range, RangeType } from '../utils/enum'; -import { - buildCondition, - generateSeries, - mergeByProperty, -} from '../utils/utils'; -import { InfluxDBClient } from './influxdb-client'; - -@Injectable() -export class MetricService { - influxDbClient: InfluxDBClient; - - constructor( - private readonly logger: AkcLogger, - private configService: ConfigService, - ) { - this.logger.setContext(MetricService.name); - this.connectInfluxDB(); - } - - /** - * Get token info - * @param ctx - * @param coinId - * @param range - * @returns - */ - async getTokenInfo( - ctx: RequestContext, - min: number, - max: number, - rangeType: RangeType, - step: number, - coinId: string, - ): Promise { - try { - this.logger.log(ctx, `${this.getTokenInfo.name} was called!`); - // const { step, fluxType, amount } = buildCondition(range); - const queryStep = `${step || 1}${rangeType}`; - const minDate = new Date(min), - maxDate = new Date(max); - - const { start, stop } = this.createRange(minDate, maxDate, rangeType); - - this.logger.log( - ctx, - `${this.getTokenInfo.name} call method from influxdb, start: ${start}, stop: ${stop}`, - ); - const output = (await this.influxDbClient.getTokenInfo( - 'token_cw20_measurement', - start, - stop, - queryStep, - coinId, - )) as TokenOutput[]; - - this.logger.log(ctx, `${this.getTokenInfo.name} generation data!`); - const metricData: TokenOutput[] = []; - if (rangeType === RangeType.minute) { - const length = output?.length || 0; - for (let i = 0; i < length; i++) { - const item = output[i]; - let tokenOutput = new TokenOutput(); - tokenOutput = { ...item }; - metricData.push(tokenOutput); - const currentTime = maxDate; - currentTime.setSeconds(0, 0); - if (new Date(item.timestamp) < currentTime && i == length - 1) { - const cloneItem = { ...item }; - cloneItem.timestamp = - moment(currentTime).utc().format('YYYY-MM-DDTHH:mm:00.00') + 'Z'; - metricData.push(cloneItem); - } - } - } else { - if (output.length > 0) { - metricData.push(...output); - } - } - - this.logger.log(ctx, `${this.getTokenInfo.name} end call!`); - return metricData; - } catch (err) { - this.logger.log(ctx, `${this.getTokenInfo.name} has error: ${err.stack}`); - this.reconnectInfluxdb(err); - throw err; - } - } - - async getTokenMarketInfo(ctx: RequestContext, coinId: string) { - try { - const output = (await this.influxDbClient.getTokenMarketInfo( - 'token_cw20_measurement', - '-1h', - '1h', - coinId, - )) as TokenOutput[]; - - let metricData = new TokenOutput(); - if (output.length > 0) { - metricData = { ...output[0] }; - } - - return metricData; - } catch (err) { - this.logger.log( - ctx, - `${this.getTokenMarketInfo.name} has error: ${err.stack}`, - ); - this.reconnectInfluxdb(err); - throw err; - } - } - - private async queryInfluxDb( - range: Range, - measurement: string, - ): Promise { - const { amount, step, fluxType } = buildCondition(range); - const startTime = `-${amount}${fluxType}`; - const queryStep = `${step}${fluxType}`; - const data = (await this.influxDbClient.queryData( - measurement, - startTime, - queryStep, - )) as MetricOutput[]; - const series = generateSeries(new Date(), range); - return mergeByProperty(data, series); - } - - /** - * Get the number of transactions - * @returns MetricOutput[] - */ - async getNumberTransactions() { - const start: string = this.configService.get('deploymentDate'); - return (await this.influxDbClient.getNumberTransactions( - start, - )) as MetricOutput[]; - } - - /** - * Create range to get data from influxdb - * @param date - * @param format - * @param amount - * @param range - * @returns - */ - createRange(minDate: Date, maxDate: Date, rangeType: RangeType) { - let start = '', - formatDate = ''; - - const utcDate = moment(minDate).utc(); - const stop = maxDate.toISOString(); - const compareDate = moment(maxDate).utc(); - switch (rangeType) { - case RangeType.day: - compareDate.add(-730, 'd'); // Value of 2 years - formatDate = 'YYYY-MM-DDT00:00:00.000'; - break; - case RangeType.month: - compareDate.add(-60, 'M'); // Value of 5 year - formatDate = 'YYYY-MM-01T00:00:00.000'; - break; - case RangeType.hour: - compareDate.add(-1440, 'h'); // Value of 60 days - formatDate = 'YYYY-MM-DDTHH:00:00.000'; - break; - default: - compareDate.add(-2880, 'm'); // Value of 48 hours - formatDate = 'YYYY-MM-DDTHH:mm:00.000'; - break; - } - if (utcDate.toDate() > compareDate.toDate()) { - start = utcDate.format(formatDate); - } else { - start = compareDate.format(formatDate); - } - - return { start: start + 'Z', stop }; - } - - /** - * Determine the last date to get data from influxdb - * @param date - * @param range - * @returns - */ - getLastDate(date: Date, rangeType: RangeType) { - const lastDate = new Date(date.toISOString()); - const minute = 59, - second = 59; - switch (rangeType) { - case RangeType.month: - lastDate.setMonth(lastDate.getMonth() - 1); - break; - case RangeType.day: - lastDate.setDate(lastDate.getDate() - 1); - lastDate.setHours(23, minute, second); - break; - case RangeType.hour: - lastDate.setHours(lastDate.getHours() - 1, minute, second); - break; - default: - lastDate.setMinutes(lastDate.getMinutes() - 3, second); - break; - } - return lastDate; - } - - /** - * Setting connection to Influxdb - */ - connectInfluxDB() { - this.influxDbClient = new InfluxDBClient( - this.configService.get('influxdb.bucket'), - this.configService.get('influxdb.org'), - this.configService.get('influxdb.url'), - this.configService.get('influxdb.token'), - ); - this.influxDbClient.initQueryApi(); - } - - /** - * Reconnect Influxdb - * @param error - */ - reconnectInfluxdb(error: any) { - const errorCode = error?.code || ''; - if (errorCode === 'ECONNREFUSED' || errorCode === 'ETIMEDOUT') { - this.connectInfluxDB(); - } - } -} diff --git a/src/components/metric/utils/enum.ts b/src/components/metric/utils/enum.ts deleted file mode 100644 index 1dafe35b..00000000 --- a/src/components/metric/utils/enum.ts +++ /dev/null @@ -1,27 +0,0 @@ -export enum TypeDate { - month = 'month', - day = 'day', - hour = 'hour', - minute = 'minute', -} - -export enum Range { - month = '12M', - day = '30d', - hour = '24h', - minute = '60m', -} - -export enum RangeType { - month = 'mo', - day = 'd', - hour = 'h', - minute = 'm', -} - -export enum FormatDate { - month = 'YYYY-MM-01T00:00:00.000', - day = 'YYYY-MM-DDT00:00:00.000', - hour = 'YYYY-MM-DDTHH:00:00.000', - minute = 'YYYY-MM-DDTHH:mm:00.000', -} diff --git a/src/components/metric/utils/utils.ts b/src/components/metric/utils/utils.ts deleted file mode 100644 index 13ffcfdf..00000000 --- a/src/components/metric/utils/utils.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { MetricConditionDto } from '../dtos/metric-condition.dto'; -import { MetricOutput } from '../dtos/metric-output.dto'; -import { Range, TypeDate } from './enum'; - -const makeData = (date: Date): MetricOutput => { - const data = new MetricOutput(); - data.total = '0'; - data.timestamp = date.toISOString().split('.')[0] + 'Z'; - return data; -}; - -export function makeupData( - metricData: MetricOutput[], - length: number, -): MetricOutput[] { - const _metricData = metricData.filter((i) => { - return i.timestamp.substring(i.timestamp.lastIndexOf(':')) === ':00Z'; - }); - _metricData.length > length && _metricData.shift(); - - return _metricData.map((i) => { - const total = Number(i.total) && Number(i.total) >= 0 ? Number(i.total) : 0; - - return { - total: `${total}`, - timestamp: i.timestamp, - }; - }); -} - -export function generateSeries( - now: Date, - range: Range, - hours = 0, -): MetricOutput[] { - const series: MetricOutput[] = []; - const past = new Date(now); - const condition = buildCondition(range); - switch (condition.type) { - case TypeDate.month: { - past.setMonth(now.getMonth() - condition.amount); - for (let i = 1; i <= condition.amount; i++) { - const date = new Date( - new Date(past.getFullYear(), past.getMonth() + i, 1), - ); - // date.setDate(date.getDate() + 1); - date.setHours(hours, 0, 0, 0); - series.push(makeData(date)); - } - break; - } - case TypeDate.day: { - past.setDate(now.getDate() - condition.amount); - const date = new Date(new Date(past).setHours(hours, 0, 0, 0)); - for (let i = 1; i <= condition.amount; i++) { - date.setDate(date.getDate() + condition.step); - series.push(makeData(date)); - } - break; - } - case TypeDate.hour: { - past.setHours(now.getHours() - condition.amount + 1); - for ( - let date = new Date(new Date(past).setUTCMinutes(0, 0, 0)); - date <= now; - date.setHours(date.getHours() + condition.step) - ) { - series.push(makeData(date)); - } - break; - } - case TypeDate.minute: { - past.setMinutes(now.getMinutes() - condition.amount + 1); - for ( - let date = new Date(new Date(past).setUTCSeconds(0, 0)); - date <= now; - date.setMinutes(date.getMinutes() + condition.step) - ) { - series.push(makeData(date)); - } - break; - } - } - - return series; -} - -export function mergeByProperty( - metric: MetricOutput[], - series: MetricOutput[], -): MetricOutput[] { - return series.map((seriesElement) => { - return ( - metric.find((e) => { - return seriesElement.timestamp === e.timestamp; - }) || seriesElement - ); - // sourceElement = targetElement || sourceElement; - // return sourceElement; - }); -} - -export function buildCondition(range: Range): MetricConditionDto { - const condition = new MetricConditionDto(); - switch (range) { - case Range.month: - condition.formatDate = '%Y-%m-01T00:00:00.000Z'; - condition.amount = 12; - condition.step = 1; - condition.type = TypeDate.month; - condition.fluxType = 'mo'; - break; - case Range.day: - condition.formatDate = '%Y-%m-%dT00:00:00.000Z'; - condition.amount = 30; - condition.step = 1; - condition.type = TypeDate.day; - condition.fluxType = 'd'; - break; - case Range.minute: - condition.formatDate = '%Y-%m-%dT%H:%i:00.000Z'; - condition.amount = 60; - condition.step = 1; - condition.type = TypeDate.minute; - condition.fluxType = 'm'; - break; - case Range.hour: - default: - condition.formatDate = '%Y-%m-%dT%H:00:00.000Z'; - condition.amount = 24; - condition.step = 1; - condition.type = TypeDate.hour; - condition.fluxType = 'h'; - break; - } - return condition; -} diff --git a/src/components/queues/cw4973/cw4973.processor.ts b/src/components/queues/cw4973/cw4973.processor.ts index 2f620794..d4d185cb 100644 --- a/src/components/queues/cw4973/cw4973.processor.ts +++ b/src/components/queues/cw4973/cw4973.processor.ts @@ -51,18 +51,18 @@ export class CW4973Processor { ); this.indexerChainId = this.configService.get('indexer.chainId'); - // this.cw4973Queue.add( - // QUEUES.CW4973.JOBS.SYNC_4973_STATUS, - // {}, - // { - // repeat: { cron: CronExpression.EVERY_10_SECONDS }, - // }, - // ); + this.cw4973Queue.add( + QUEUES.CW4973.JOBS.SYNC_4973_STATUS, + {}, + { + repeat: { cron: CronExpression.EVERY_10_SECONDS }, + }, + ); this.chainDB = configService.get('indexerV2.chainDB'); } - // @Process(QUEUES.CW4973.JOBS.SYNC_4973_STATUS) + @Process(QUEUES.CW4973.JOBS.SYNC_4973_STATUS) async handleJobSyncCw4973Status() { const currentCw4973Height = await this.syncPointRepos.findOne({ where: { diff --git a/src/components/queues/queues.module.ts b/src/components/queues/queues.module.ts index 8f317eec..cca6d024 100644 --- a/src/components/queues/queues.module.ts +++ b/src/components/queues/queues.module.ts @@ -26,8 +26,8 @@ import { NotificationModule } from './notification/notification.module'; inject: [ConfigService], }), SendMailModule, - // TokenModule, - // CW4973QueueModule, + TokenModule, + CW4973QueueModule, NotificationModule, ], }) diff --git a/src/components/queues/token/token.processor.ts b/src/components/queues/token/token.processor.ts index 7fd04c4e..9eccfbab 100644 --- a/src/components/queues/token/token.processor.ts +++ b/src/components/queues/token/token.processor.ts @@ -3,24 +3,20 @@ import { TokenMarketsRepository } from '../../cw20-token/repositories/token-mark import { Logger } from '@nestjs/common'; import { Queue } from 'bull'; import { - AURA_INFO, COINGECKO_API, COIN_MARKET_CAP, COIN_MARKET_CAP_API, - GECKOTERMINAL_API, QUEUES, TokenMarkets, } from '../../../shared'; import * as appConfig from '../../../shared/configs/configuration'; import * as util from 'util'; -import { InfluxDBClient } from '../../metric/services/influxdb-client'; import { ServiceUtil } from '../../../shared/utils/service.util'; import { In } from 'typeorm'; @Processor(QUEUES.TOKEN.QUEUE_NAME) export class TokenProcessor { private readonly logger = new Logger(TokenProcessor.name); - private influxDbClient: InfluxDBClient; private appParams: any; constructor( @@ -32,74 +28,16 @@ export class TokenProcessor { '============== Constructor Token Price Processor Service ==============', ); this.appParams = appConfig.default(); - - // this.tokenQueue.add( - // QUEUES.TOKEN.JOB_SYNC_TOKEN_PRICE, - // {}, - // { - // repeat: { cron: this.appParams.priceTimeSync }, - // }, - // ); - - // this.tokenQueue.add( - // QUEUES.TOKEN.JOB_SYNC_CW20_PRICE, - // {}, - // { - // repeat: { cron: this.appParams.priceTimeSync }, - // }, - // ); - - // Connect influxdb - // this.connectInfluxdb(); - } - - // @Process(QUEUES.TOKEN.JOB_SYNC_TOKEN_PRICE) - async syncAuraTokenPrice(): Promise { - try { - const geckoTerminal = this.appParams.geckoterminal; - - const token = await this.tokenMarketsRepository.findOne({ - where: { - coin_id: AURA_INFO.COIN_ID, - }, - }); - - const para = `${util.format( - GECKOTERMINAL_API.GET_TOKEN_PRICE, - geckoTerminal.pool, - geckoTerminal.coinAddress, - )}`; - - const response = await this.serviceUtil.getDataAPI( - geckoTerminal.api, - para, - '', - ); - if (response?.data?.attributes && token) { - const attributes = response.data.attributes; - token.current_price = attributes?.base_token_price_usd; - token.fully_diluted_valuation = attributes?.fdv_usd; - token.market_cap = attributes?.market_cap_usd; - token.price_change_percentage_24h = - attributes?.price_change_percentage.h24; - token.total_volume = attributes?.volume_usd.h24; - - await this.tokenMarketsRepository.save(token); - - const coinMarkets: TokenMarkets[] = []; - coinMarkets.push(token); - this.logger.log(`============== Write data to Influxdb ==============`); - await this.influxDbClient.writeBlockTokenPriceAndVolume(coinMarkets); - this.logger.log( - `============== Write data to Influxdb successfully ==============`, - ); - } - } catch (err) { - this.logger.error(`sync-aura-token has error: ${err.message}`, err.stack); - } + this.tokenQueue.add( + QUEUES.TOKEN.JOB_SYNC_CW20_PRICE, + {}, + { + repeat: { cron: this.appParams.priceTimeSync }, + }, + ); } - // @Process(QUEUES.TOKEN.JOB_SYNC_CW20_PRICE) + @Process(QUEUES.TOKEN.JOB_SYNC_CW20_PRICE) async syncCW20TokenPrice(): Promise { const numberCW20Tokens = await this.tokenMarketsRepository.countCw20TokensHavingCoinId(); @@ -121,28 +59,6 @@ export class TokenProcessor { } } - connectInfluxdb() { - this.logger.log( - `============== call connectInfluxdb method ==============`, - ); - try { - this.influxDbClient = new InfluxDBClient( - this.appParams.influxdb.bucket, - this.appParams.influxdb.org, - this.appParams.influxdb.url, - this.appParams.influxdb.token, - ); - if (this.influxDbClient) { - this.influxDbClient.initWriteApi(); - } - } catch (err) { - this.logger.log( - `call connectInfluxdb method has error: ${err.message}`, - err.stack, - ); - } - } - async handleSyncPriceVolume(listTokens: string[]): Promise { try { if (this.appParams.priceHostSync === COIN_MARKET_CAP) { @@ -152,11 +68,6 @@ export class TokenProcessor { } } catch (err) { this.logger.log(`sync-price-volume has error: ${err.message}`, err.stack); - // Reconnect influxDb - const errorCode = err?.code || ''; - if (errorCode === 'ECONNREFUSED' || errorCode === 'ETIMEDOUT') { - this.connectInfluxdb(); - } } } @@ -202,12 +113,6 @@ export class TokenProcessor { } if (coinMarkets.length > 0) { await this.tokenMarketsRepository.save(coinMarkets); - - this.logger.log(`============== Write data to Influxdb ==============`); - await this.influxDbClient.writeBlockTokenPriceAndVolume(coinMarkets); - this.logger.log( - `============== Write data to Influxdb successfully ==============`, - ); } } @@ -244,12 +149,6 @@ export class TokenProcessor { } if (coinMarkets.length > 0) { await this.tokenMarketsRepository.save(coinMarkets); - - this.logger.log(`============== Write data to Influxdb ==============`); - await this.influxDbClient.writeBlockTokenPriceAndVolume(coinMarkets); - this.logger.log( - `============== Write data to Influxdb successfully ==============`, - ); } } diff --git a/src/migrations/1701052101011-add-chain-id-to-token-market.ts b/src/migrations/1701052101011-add-chain-id-to-token-market.ts new file mode 100644 index 00000000..e22c769e --- /dev/null +++ b/src/migrations/1701052101011-add-chain-id-to-token-market.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class addChainIdToTokenMarket1701052101011 + implements MigrationInterface +{ + name = 'addChainIdToTokenMarket1701052101011'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`token_markets\` ADD \`chain_id\` varchar(255) NULL`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`token_markets\` DROP COLUMN \`chain_id\``, + ); + } +} diff --git a/src/migrations/1701144278720-remove-code-id-from-token-market.ts b/src/migrations/1701144278720-remove-code-id-from-token-market.ts new file mode 100644 index 00000000..6895b340 --- /dev/null +++ b/src/migrations/1701144278720-remove-code-id-from-token-market.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class removeCodeIdFromTokenMarket1701144278720 + implements MigrationInterface +{ + name = 'removeCodeIdFromTokenMarket1701144278720'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`token_markets\` DROP COLUMN \`code_id\``, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`token_markets\` ADD \`code_id\` int NOT NULL DEFAULT '0'`, + ); + } +} diff --git a/src/migrations/1701145590378-add-unique-chain-id-denom-to-token-markets.ts b/src/migrations/1701145590378-add-unique-chain-id-denom-to-token-markets.ts new file mode 100644 index 00000000..91b3d3e6 --- /dev/null +++ b/src/migrations/1701145590378-add-unique-chain-id-denom-to-token-markets.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class addUniqueChainIdDenomToTokenMarkets1701145590378 + implements MigrationInterface +{ + name = 'addUniqueChainIdDenomToTokenMarkets1701145590378'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_ee5edec6c978e77465b00cf4e9\` ON \`token_markets\` (\`chain_id\`, \`denom\`)`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DROP INDEX \`IDX_ee5edec6c978e77465b00cf4e9\` ON \`token_markets\``, + ); + } +} diff --git a/src/migrations/1701245216668-create-chain-info.ts b/src/migrations/1701245216668-create-chain-info.ts new file mode 100644 index 00000000..93714de0 --- /dev/null +++ b/src/migrations/1701245216668-create-chain-info.ts @@ -0,0 +1,21 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class createChainInfo1701245216668 implements MigrationInterface { + name = 'createChainInfo1701245216668'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE \`chain_info\` (\`created_at\` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + \`updated_at\` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + \`id\` int NOT NULL AUTO_INCREMENT, + \`chain_id\` varchar(255) NOT NULL, + \`chain_name\` varchar(255) NOT NULL, + \`chain_image\` varchar(255) NOT NULL, + UNIQUE INDEX \`IDX_ab5accf171432c687f0cf91edf\` (\`chain_id\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE \`chain_info\``); + } +} diff --git a/src/migrations/1701678792982-refactor-table-database.ts b/src/migrations/1701678792982-refactor-table-database.ts new file mode 100644 index 00000000..6e66238f --- /dev/null +++ b/src/migrations/1701678792982-refactor-table-database.ts @@ -0,0 +1,27 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class refactorTableDatabase1701678792982 implements MigrationInterface { + name = 'refactorTableDatabase1701678792982'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE \`block_sync_error\``); + await queryRunner.query(`DROP TABLE \`blocks\``); + await queryRunner.query(`DROP TABLE \`cw20_token_owners\``); + await queryRunner.query(`DROP TABLE \`delegations\``); + await queryRunner.query(`DROP TABLE \`delegator_rewards\``); + await queryRunner.query(`DROP TABLE \`deployment_requests\``); + await queryRunner.query(`DROP TABLE \`missed_block\``); + await queryRunner.query(`DROP TABLE \`proposal_votes\``); + await queryRunner.query(`DROP TABLE \`smart_contract_codes\``); + await queryRunner.query(`DROP TABLE \`smart_contracts\``); + await queryRunner.query(`DROP TABLE \`tags\``); + await queryRunner.query(`DROP TABLE \`transactions\``); + await queryRunner.query(`DROP TABLE \`validators\``); + await queryRunner.query(`DROP TABLE \`verify_code_step\``); + await queryRunner.query(`DROP TABLE \`verify_item_check\``); + } + + public async down(queryRunner: QueryRunner): Promise { + // No action + } +} diff --git a/src/shared/configs/configuration.ts b/src/shared/configs/configuration.ts index 9087593e..9e26851c 100644 --- a/src/shared/configs/configuration.ts +++ b/src/shared/configs/configuration.ts @@ -15,12 +15,6 @@ export default () => ({ rpc: process.env.RPC, api: process.env.API, }, - influxdb: { - token: process.env.INFLUXDB_TOKEN, - url: process.env.INFLUXDB_URL, - bucket: process.env.INFLUXDB_BUCKET, - org: process.env.INFLUXDB_ORG, - }, cacheManagement: { useRedis: process.env.USE_REDIS, ttl: process.env.TTL, @@ -84,12 +78,6 @@ export default () => ({ }, priceHostSync: process.env.PRICE_HOST_SYNC || 'COINGECKO', priceTimeSync: process.env.PRICE_TIME_SYNC || '0 */3 * * * *', - geckoterminal: { - api: process.env.GECKOTERMINAL_API, - pool: process.env.GECKOTERMINAL_POOL || 'bsc', - coinAddress: - process.env.COIN_ADDRESS || '0x9f1a332c0657ce3f90666ad38dbe2e92793abf5c', - }, coingecko: { api: process.env.COINGECKO_API, maxRequest: Number(process.env.MAX_REQUEST) || 250, diff --git a/src/shared/configs/module-options.ts b/src/shared/configs/module-options.ts index eb7ac2df..22ec3cf2 100644 --- a/src/shared/configs/module-options.ts +++ b/src/shared/configs/module-options.ts @@ -17,9 +17,5 @@ export const configModuleOptions: ConfigModuleOptions = { RPC: Joi.string().required(), API: Joi.string().required(), START_HEIGHT: Joi.number(), - INFLUXDB_TOKEN: Joi.string().required(), - INFLUXDB_URL: Joi.string().required(), - INFLUXDB_BUCKET: Joi.string().required(), - INFLUXDB_ORG: Joi.string().required(), }), }; diff --git a/src/shared/constants/common.ts b/src/shared/constants/common.ts index 7f158fbf..ae960622 100644 --- a/src/shared/constants/common.ts +++ b/src/shared/constants/common.ts @@ -92,77 +92,85 @@ export const INDEXER_API_V2 = { } } }`, - TX_TOKEN_TRANSFER: `query Cw20TXMultilCondition($receiver: String = null, $sender: String = null, $contractAddr: String = null, $startTime: timestamptz = null, $endTime: timestamptz = null, $listTxMsgType: [String!] = null, $listTxMsgTypeNotIn: [String!] = null, $heightGT: Int = null, $heightLT: Int = null, $limit: Int = null, $txHash: String = null, $actionIn: [String!] = null, $actionNotIn: [String!] = null) { + TX_TOKEN_TRANSFER: `query Cw20TXMultilCondition($receiver: String = null, $sender: String = null, $heightGT: Int = null, $heightLT: Int = null, $limit: Int = 100, $actionIn: [String!] = null, $startTime: timestamptz = null, $endTime: timestamptz = null) { ${process.env.INDEXER_V2_DB} { - transaction(where: {events: {smart_contract_events: {cw20_activities: {_or: [{to: {_eq: $receiver}}, {from: {_eq: $sender}}], action: {_in: $actionIn, _nin: $actionNotIn}}, smart_contract: {address: {_eq: $contractAddr}}}}, timestamp: {_gte: $startTime, _lte: $endTime}, _and: {height: {_gt: $heightGT, _lt: $heightLT}, hash: {_eq: $txHash}}, transaction_messages: {type: {_in: $listTxMsgType, _nin: $listTxMsgTypeNotIn}}}, order_by: {height: desc}, limit: $limit) { - gas_used - hash - height - timestamp - code - transaction_messages { - content - type + transaction: cw20_activity(where: {_or: [{to: {_eq: $receiver}}, {from: {_eq: $sender}}], cw20_contract: {}, action: {_in: $actionIn}, height: {_gt: $heightGT, _lt: $heightLT}, tx: {timestamp: {_lte: $endTime, _gte: $startTime}}}, order_by: {height: desc}, limit: $limit) { + action + amount + from + to + sender + cw20_contract { + smart_contract { + address + } + decimal + symbol } - events(where: {smart_contract_events: {cw20_activities: {_or: [{to: {_eq: $receiver}}, {from: {_eq: $sender}}], id: {_is_null: false}}}}) { - smart_contract_events { - cw20_activities { - amount - action - from - to - sender - } - smart_contract { - address - cw20_contract { - symbol - decimal - } - } + tx { + hash + height + timestamp + code + transaction_messages { + type + content } } } } }`, - TX_NFT_TRANSFER: `query Cw721TXMultilCondition($receiver: String = null, $sender: String = null, $startTime: timestamptz = null, $endTime: timestamptz = null, $listTxMsgType: [String!] = null, $listTxMsgTypeNotIn: [String!] = null, $heightGT: Int = null, $heightLT: Int = null, $limit: Int = null, $contractAddr: String = null, $tokenId: String = null, $txHash: String = null, $neqCw4973: String, $eqCw4973: String = null, $actionIn: [String!] = null, $actionNotIn: [String!] = null) { + TX_NFT_TRANSFER: `query Cw721TXMultilCondition( + $receiver: String = null + $sender: String = null + $heightGT: Int = null + $heightLT: Int = null + $limit: Int = 100 + $actionIn: [String!] = null + $startTime: timestamptz = null + $endTime: timestamptz = null + ) { ${process.env.INDEXER_V2_DB} { - transaction(where: {events: {smart_contract_events: {cw721_activity: {_or: [{to: {_eq: $receiver}}, {from: {_eq: $sender}}], cw721_token: {token_id: {_eq: $tokenId}}, action: {_in: $actionIn, _nin: $actionNotIn}}, smart_contract: {name: {_neq: $neqCw4973, _eq: $eqCw4973}, address: {_eq: $contractAddr}}}}, timestamp: {_gte: $startTime, _lte: $endTime}, _and: {height: {_gt: $heightGT, _lt: $heightLT}, hash: {_eq: $txHash}}, transaction_messages: {type: {_in: $listTxMsgType, _nin: $listTxMsgTypeNotIn}}}, order_by: {height: desc}, limit: $limit) { - gas_used - hash - height - timestamp - code - transaction_messages { - content - type + transaction: cw721_activity( + where: { + _or: [{ to: { _eq: $receiver } }, { from: { _eq: $sender } }] + cw721_contract: { + smart_contract: { name: { _neq: "crates.io:cw4973" } } + } + action: { _in: $actionIn } + height: { _gt: $heightGT, _lt: $heightLT } + tx: { timestamp: { _lte: $endTime, _gte: $startTime } } } - events(where: {smart_contract_events: {cw721_activity: {id: {_is_null: false}, _or: [{to: {_eq: $receiver}}, {from: {_eq: $sender}}], cw721_token: {token_id: {_eq: $tokenId}}}}}) { - smart_contract_events { - cw721_activity { - action - from - to - sender - cw721_token { - token_id - } - cw721_contract { - smart_contract { - address - } - } - } + order_by: { height: desc } + limit: $limit + ) { + action + from + to + sender + cw721_contract { + smart_contract { + address } + symbol + } + cw721_token { + token_id } - event_attribute_index(where: {composite_key: {_eq: "wasm.spender"}}) { - composite_key - key - value + tx { + hash + height + timestamp + code + transaction_messages { + type + content + } } } } - }`, + } + `, EXECUTED_NOTIFICATION: `query ExecutedNotification($heightGT: Int, $heightLT: Int) { ${process.env.INDEXER_V2_DB} { executed: transaction(where: {height: {_gt: $heightGT, _lt: $heightLT}, code: {_eq: 0}}, order_by: {height: desc}, limit: 100) { diff --git a/src/shared/entities/block-sync-error.entity.ts b/src/shared/entities/block-sync-error.entity.ts deleted file mode 100644 index 1d5acff4..00000000 --- a/src/shared/entities/block-sync-error.entity.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Column, Entity, PrimaryGeneratedColumn, Unique } from 'typeorm'; -import { BaseEntity } from './base/base.entity'; - -@Entity('block_sync_error') -@Unique(['height']) -export class BlockSyncError extends BaseEntity { - @PrimaryGeneratedColumn('increment', { type: 'bigint' }) - id: number; - - @Column() - height: number; -} diff --git a/src/shared/entities/block.entity.ts b/src/shared/entities/block.entity.ts deleted file mode 100644 index 98cc2f29..00000000 --- a/src/shared/entities/block.entity.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Column, Entity, Unique } from 'typeorm'; - -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('blocks') -export class Block extends BaseEntityIncrementId { - @Unique('block_hash', ['block_hash']) - @Column() - block_hash: string; - - @Column({ default: '' }) - chainid: string; - - @Column() - height: number; - - @Column({ default: '' }) - identity: string; - - @Column({ default: '' }) - moniker: string; - - @Column({ default: 0 }) - num_signatures: number; - - @Column({ default: 0 }) - num_txs: number; - - @Column({ default: '' }) - operator_address: string; - - @Column({ default: '' }) - proposer: string; - - @Column() - timestamp: Date; - - @Column({ - name: 'gas_used', - default: 0, - type: 'bigint', - }) - gas_used: number; - - @Column({ - name: 'gas_wanted', - type: 'bigint', - default: 0, - }) - gas_wanted: number; - - @Column({ default: 0 }) - round: number; - - @Column({ - name: 'json_data', - type: 'json', - nullable: true, - }) - json_data: any; -} diff --git a/src/shared/entities/chain-info.entity.ts b/src/shared/entities/chain-info.entity.ts new file mode 100644 index 00000000..6ecff60e --- /dev/null +++ b/src/shared/entities/chain-info.entity.ts @@ -0,0 +1,15 @@ +import { Column, Entity, Unique } from 'typeorm'; +import { BaseEntityIncrementId } from './base/base.entity'; + +@Entity('chain_info') +@Unique(['chainId']) +export class ChainInfo extends BaseEntityIncrementId { + @Column({ name: 'chain_id', nullable: false }) + chainId: string; + + @Column({ name: 'chain_name', nullable: false }) + chainName: string; + + @Column({ name: 'chain_image', nullable: false }) + chainImage: string; +} diff --git a/src/shared/entities/cw20-token-owner.entity.ts b/src/shared/entities/cw20-token-owner.entity.ts deleted file mode 100644 index bdd9dcaa..00000000 --- a/src/shared/entities/cw20-token-owner.entity.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Column, Entity, Unique } from 'typeorm'; -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('cw20_token_owners') -@Unique(['contract_address', 'owner']) -export class Cw20TokenOwner extends BaseEntityIncrementId { - @Column() - contract_address: string; - - @Column() - owner: string; - - @Column() - balance: number; - - @Column() - percent_hold: number; -} diff --git a/src/shared/entities/delegation.entity.ts b/src/shared/entities/delegation.entity.ts deleted file mode 100644 index 2018f701..00000000 --- a/src/shared/entities/delegation.entity.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Column, Entity, Unique } from 'typeorm'; - -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('delegations') -@Unique(['tx_hash', 'delegator_address', 'validator_address']) -export class Delegation extends BaseEntityIncrementId { - @Column() - delegator_address: string; - - @Column({ default: '' }) - validator_address: string; - - @Column({ default: '' }) - shares: string; - - @Column({ type: 'float' }) - amount: number; - - @Column() - tx_hash: string; - - @Column() - type: string; -} diff --git a/src/shared/entities/delegator-reward.entity.ts b/src/shared/entities/delegator-reward.entity.ts deleted file mode 100644 index 79523f55..00000000 --- a/src/shared/entities/delegator-reward.entity.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Column, Entity, Unique } from 'typeorm'; -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('delegator_rewards') -@Unique(['tx_hash', 'delegator_address', 'validator_address']) -export class DelegatorReward extends BaseEntityIncrementId { - @Column() - delegator_address: string; - - @Column() - validator_address: string; - - @Column() - amount: number; - - @Column() - tx_hash: string; -} diff --git a/src/shared/entities/deployment-requests.entity.ts b/src/shared/entities/deployment-requests.entity.ts deleted file mode 100644 index be5057c9..00000000 --- a/src/shared/entities/deployment-requests.entity.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { Column, Entity } from 'typeorm'; -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('deployment_requests') -export class DeploymentRequests extends BaseEntityIncrementId { - @Column() - request_id: number; - - @Column() - requester_address: string; - - @Column() - name: string; - - @Column() - email: string; - - @Column({ type: 'text' }) - contract_description: string; - - @Column() - project_name: string; - - @Column({ type: 'text' }) - official_project_website: string; - - @Column() - official_project_email: string; - - @Column({ - nullable: true, - }) - project_sector: string; - - @Column({ - nullable: true, - }) - whitepaper: string; - - @Column({ - nullable: true, - }) - github: string; - - @Column({ - nullable: true, - }) - telegram: string; - - @Column({ - nullable: true, - }) - wechat: string; - - @Column({ - nullable: true, - }) - linkedin: string; - - @Column({ - nullable: true, - }) - discord: string; - - @Column({ - nullable: true, - }) - medium: string; - - @Column({ - nullable: true, - }) - reddit: string; - - @Column({ - nullable: true, - }) - slack: string; - - @Column({ - nullable: true, - }) - facebook: string; - - @Column({ - nullable: true, - }) - twitter: string; - - @Column({ - nullable: true, - }) - bitcointalk: string; - - @Column() - euphoria_code_id: number; - - @Column() - mainnet_code_id: number; - - @Column() - contract_hash: string; - - @Column() - url: string; - - @Column({ type: 'text' }) - instantiate_msg_schema: string; - - @Column({ type: 'text' }) - query_msg_schema: string; - - @Column({ type: 'text' }) - execute_msg_schema: string; - - @Column() - compiler_version: string; - - @Column() - s3_location: string; - - @Column() - status: string; - - @Column({ - nullable: true, - type: 'text', - }) - reason: string; -} diff --git a/src/shared/entities/missed-block.entity.ts b/src/shared/entities/missed-block.entity.ts deleted file mode 100644 index a407409c..00000000 --- a/src/shared/entities/missed-block.entity.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -@Entity('missed_block') -export class MissedBlock { - @PrimaryColumn() - validator_address: string; - - @PrimaryColumn() - height: number; - - @Column() - timestamp: Date; -} diff --git a/src/shared/entities/proposal-vote.entity.ts b/src/shared/entities/proposal-vote.entity.ts deleted file mode 100644 index f2b98288..00000000 --- a/src/shared/entities/proposal-vote.entity.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Column, Entity, Unique } from 'typeorm'; -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('proposal_votes') -@Unique(['proposal_id', 'voter']) -export class ProposalVote extends BaseEntityIncrementId { - @Column() - proposal_id: number; - - @Column() - voter: string; - - @Column() - tx_hash: string; - - @Column() - option: string; -} diff --git a/src/shared/entities/smart-contract-code.entity.ts b/src/shared/entities/smart-contract-code.entity.ts deleted file mode 100644 index a2d7416f..00000000 --- a/src/shared/entities/smart-contract-code.entity.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Column, Entity } from 'typeorm'; -import { CONTRACT_STATUS } from '../constants/common'; -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('smart_contract_codes') -export class SmartContractCode extends BaseEntityIncrementId { - @Column() - code_id: number; - - @Column() - type: string; - - @Column() - result: string; - - @Column() - creator: string; - - @Column({ name: 'tx_hash', nullable: true }) - tx_hash: string; - - @Column({ name: 'instantiate_msg_schema', type: 'text', nullable: true }) - instantiate_msg_schema: string; - - @Column({ name: 'query_msg_schema', type: 'text', nullable: true }) - query_msg_schema: string; - - @Column({ name: 'execute_msg_schema', type: 'text', nullable: true }) - execute_msg_schema: string; - - @Column({ name: 'contract_hash', nullable: true }) - contract_hash: string; - - @Column({ name: 's3_location', nullable: true }) - s3_location: string; - - @Column({ - name: 'contract_verification', - default: CONTRACT_STATUS.UNVERIFIED, - }) - contract_verification: string; - - @Column({ name: 'compiler_version', nullable: true }) - compiler_version: string; - - @Column({ name: 'url', nullable: true }) - url: string; - - @Column({ - type: 'timestamp', - nullable: true, - }) - verified_at: Date; -} diff --git a/src/shared/entities/smart-contract.entity.ts b/src/shared/entities/smart-contract.entity.ts deleted file mode 100644 index 08893b3b..00000000 --- a/src/shared/entities/smart-contract.entity.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Column, Entity, Index } from 'typeorm'; -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('smart_contracts') -export class SmartContract extends BaseEntityIncrementId { - @Column({ - type: 'timestamp', - }) - verified_at: Date; - - @Column() - height: number; - - @Column() - code_id: number; - - @Column() - contract_name: string; - - @Column() - contract_address: string; - - @Column() - creator_address: string; - - @Column() - contract_hash: string; - - @Column() - tx_hash: string; - - @Column() - url: string; - - @Column({ type: 'text' }) - instantiate_msg_schema: string; - - @Column({ type: 'text' }) - query_msg_schema: string; - - @Column({ type: 'text' }) - execute_msg_schema: string; - - @Column() - contract_match: string; - - @Column() - contract_verification: string; - - @Column() - compiler_version: string; - - @Column() - s3_location: string; - - @Column() - reference_code_id: number; - - @Column() - mainnet_upload_status: string; - - @Column() - token_name: string; - - @Column() - token_symbol: string; - - @Column() - num_tokens: number; - - @Column({ - nullable: true, - }) - project_name: string; - - @Column({ - nullable: true, - }) - request_id: number; - - @Column({ name: 'image' }) - image: string; - - @Column({ name: 'description', type: 'text' }) - description: string; - - @Column({ name: 'minter_address', nullable: true }) - @Index({ unique: false }) - minter_address: string; - - @Column() - decimals: number; - - @Column() - total_tx: number; -} diff --git a/src/shared/entities/tag.entity.ts b/src/shared/entities/tag.entity.ts deleted file mode 100644 index 647a8937..00000000 --- a/src/shared/entities/tag.entity.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Column, Entity, Unique } from 'typeorm'; -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('tags') -@Unique(['account_address', 'contract_address']) -export class Tag extends BaseEntityIncrementId { - @Column({ update: false }) - account_address: string; - - @Column({ update: false }) - contract_address: string; - - @Column() - tag: string; - - @Column() - note: string; -} diff --git a/src/shared/entities/token-markets.entity.ts b/src/shared/entities/token-markets.entity.ts index b7365a12..93d26628 100644 --- a/src/shared/entities/token-markets.entity.ts +++ b/src/shared/entities/token-markets.entity.ts @@ -3,6 +3,7 @@ import { BaseEntityIncrementId } from './base/base.entity'; @Entity('token_markets') @Unique(['contract_address']) +@Index(['chain_id', 'denom'], { unique: true }) export class TokenMarkets extends BaseEntityIncrementId { @Column({ name: 'contract_address' }) @Index() @@ -11,10 +12,6 @@ export class TokenMarkets extends BaseEntityIncrementId { @Column({ name: 'coin_id' }) coin_id: string; - @Column({ name: 'code_id' }) - @Index({ unique: false }) - code_id: number; - @Column() symbol: string; @@ -108,4 +105,7 @@ export class TokenMarkets extends BaseEntityIncrementId { @Column({ name: 'decimal', default: 0 }) decimal: number; + + @Column({ name: 'chain_id', nullable: true }) + chain_id: string; } diff --git a/src/shared/entities/transaction.entity.ts b/src/shared/entities/transaction.entity.ts deleted file mode 100644 index c90e55be..00000000 --- a/src/shared/entities/transaction.entity.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; -import { BaseEntity } from './base/base.entity'; - -@Entity('transactions') -export class Transaction extends BaseEntity { - @PrimaryColumn({ type: 'varchar', name: 'tx_hash' }) - tx_hash: string; - - @Column({ type: 'int', name: 'height' }) - height: number; - - @Column({ type: 'varchar', name: 'type' }) - type: string; - - @Column({ type: 'varchar', name: 'contract_address' }) - contract_address: string; - - @Column({ type: 'varchar', name: 'from_address' }) - from_address: string; - - @Column({ type: 'varchar', name: 'to_address' }) - to_address: string; - - @Column({ type: 'decimal', name: 'amount' }) - amount: string; - - @Column({ type: 'decimal', name: 'fee' }) - fee: string; - - @Column({ type: 'datetime', name: 'timestamp' }) - timestamp: Date; -} diff --git a/src/shared/entities/validator.entity.ts b/src/shared/entities/validator.entity.ts deleted file mode 100644 index 5057880c..00000000 --- a/src/shared/entities/validator.entity.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -import { BaseEntity } from './base/base.entity'; - -@Entity('validators') -export class Validator extends BaseEntity { - @PrimaryColumn({ name: 'operator_address', type: 'varchar' }) - operator_address: string; - - @Column({ default: '' }) - acc_address: string; - - @Column({ default: '' }) - cons_address: string; - - @Column({ default: '' }) - cons_pub_key: string; - - @Column({ default: '' }) - title: string; - - @Column({ default: false }) - jailed: boolean; - - @Column({ type: 'text' }) - commission: string; - - @Column({ type: 'text' }) - max_commission: string; - - @Column({ type: 'text' }) - max_change_rate: string; - - @Column({ default: 0 }) - min_self_delegation: number; - - @Column({ type: 'text' }) - delegator_shares: string; - - @Column({ type: 'double' }) - power: number; - - @Column({ default: '' }) - percent_power: string; - - @Column({ type: 'double' }) - self_bonded: number; - - @Column({ default: '' }) - percent_self_bonded: string; - - @Column({ default: '' }) - website: string; - - @Column({ nullable: true, type: 'text' }) - details: string; - - @Column({ default: '' }) - identity: string; - - @Column({ default: '' }) - unbonding_height: string; - - @Column() - unbonding_time: Date; - - @Column() - update_time: Date; - - @Column({ default: 0 }) - up_time: string; - - @Column({ default: 0 }) - status: number; - - @Column({ name: 'image_url', nullable: true, type: 'nvarchar' }) - image_url: string; - - @Column({ name: 'voting_power_level', nullable: true }) - voting_power_level: string; - - @Column({ name: 'bonded_height', default: 1 }) - bonded_height: number; -} diff --git a/src/shared/entities/verify-code-step.entity.ts b/src/shared/entities/verify-code-step.entity.ts deleted file mode 100644 index 03775503..00000000 --- a/src/shared/entities/verify-code-step.entity.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Column, Entity, Unique } from 'typeorm'; -import { VERIFY_CODE_RESULT } from '../constants/common'; -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('verify_code_step') -@Unique(['code_id', 'check_id']) -export class VerifyCodeStep extends BaseEntityIncrementId { - @Column() - code_id: number; - - @Column() - check_id: number; - - @Column({ - type: 'enum', - enum: VERIFY_CODE_RESULT, - default: VERIFY_CODE_RESULT.PENDING, - }) - result: VERIFY_CODE_RESULT; - - @Column({ name: 'msg_code', nullable: true }) - msg_code: string; -} diff --git a/src/shared/entities/verify-item-check.entity.ts b/src/shared/entities/verify-item-check.entity.ts deleted file mode 100644 index 215fb59d..00000000 --- a/src/shared/entities/verify-item-check.entity.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Column, Entity } from 'typeorm'; -import { BaseEntityIncrementId } from './base/base.entity'; - -@Entity('verify_item_check') -export class VerifyItemCheck extends BaseEntityIncrementId { - @Column() - check_name: string; - - @Column() - group_stage: number; -} diff --git a/src/shared/helpers/transaction.helper.ts b/src/shared/helpers/transaction.helper.ts index 85c0945a..1f80cd5f 100644 --- a/src/shared/helpers/transaction.helper.ts +++ b/src/shared/helpers/transaction.helper.ts @@ -20,8 +20,10 @@ export class TransactionHelper { ) { const txs = _.get(data, 'transaction')?.map((element) => { const code = _.get(element, 'code'); - const tx_hash = _.get(element, 'hash'); - const lstTypeTemp = _.get(element, 'transaction_messages'); + const tx_hash = _.get(element, 'hash') || _.get(element, 'tx.hash'); + const lstTypeTemp = + _.get(element, 'transaction_messages') || + _.get(element, 'tx.transaction_messages'); let type; if (lstTypeTemp) { if (lstTypeTemp[0]['type'] === TRANSACTION_TYPE_ENUM.GetReward) { @@ -51,7 +53,8 @@ export class TransactionHelper { coinInfo.coinDecimals, ).toFixed(coinInfo.coinDecimals); const height = _.get(element, 'height'); - const timestamp = _.get(element, 'timestamp'); + const timestamp = + _.get(element, 'timestamp') || _.get(element, 'tx.timestamp'); const limit = 5; let fromAddress; let toAddress; @@ -104,79 +107,35 @@ export class TransactionHelper { arrEvent = arrTemp; break; case TYPE_EXPORT.FtsTxs: - arrEvent = _.get(element, 'events')?.map((item) => { - const { type, action } = this.getTypeTx(element); - const fromAddress = - _.get(item, 'smart_contract_events[0].cw20_activities[0].from') || - NULL_ADDRESS; - const toAddress = - _.get(item, 'smart_contract_events[0].cw20_activities[0].to') || - NULL_ADDRESS; - const denom = _.get( - item, - 'smart_contract_events[0].smart_contract.cw20_contract.symbol', - ); - const amountTemp = _.get( - item, - 'smart_contract_events[0].cw20_activities[0].amount', - ); - const decimal = _.get( - item, - 'smart_contract_events[0].smart_contract.cw20_contract.decimal', - ); - const amount = this.balanceOf(amountTemp || 0, +decimal); - const contractAddress = _.get( - item, - 'smart_contract_events[0].smart_contract.address', - ); - return { - type, - fromAddress, - toAddress, - amount, - denom, - contractAddress, - action, - amountTemp, + const typeAndAction = this.getTypeTx(element.tx); + const decimal = element.cw20_contract?.decimal; + + arrEvent = [ + { + type: typeAndAction.type, + fromAddress: element.from || NULL_ADDRESS, + toAddress: element.to || NULL_ADDRESS, + amount: this.balanceOf(element.amount || 0, +decimal), + denom: element.cw20_contract?.symbol, + contractAddress: element.cw20_contract?.smart_contract?.address, + action: typeAndAction.action, + amountTemp: element.amount, decimal, - }; - }); + }, + ]; break; case TYPE_EXPORT.NftTxs: - arrEvent = _.get(element, 'events')?.map((item) => { - const { type, action } = this.getTypeTx(element); - const fromAddress = - _.get(item, 'smart_contract_events[0].cw721_activity.from') || - NULL_ADDRESS; - let toAddress = - _.get(item, 'smart_contract_events[0].cw721_activity.to') || - _.get( - item, - 'smart_contract_events[0].cw721_activity.cw721_contract.smart_contract.address', - ) || - NULL_ADDRESS; - if (action === 'burn') { - toAddress = NULL_ADDRESS; - } + const nftTypeAndAction = this.getTypeTx(element.tx); - const contractAddress = _.get( - item, - 'smart_contract_events[0].cw721_activity.cw721_contract.smart_contract.address', - ); - const tokenId = _.get( - item, - 'smart_contract_events[0].cw721_activity.cw721_token.token_id', - ); - const eventAttr = element.event_attribute_index; - return { - type, - fromAddress, - toAddress, - tokenId, - contractAddress, - eventAttr, - }; - }); + arrEvent = [ + { + type: nftTypeAndAction.type, + fromAddress: element.from || NULL_ADDRESS, + toAddress: element.to || NULL_ADDRESS, + tokenId: element.cw721_token.token_id, + contractAddress: element.cw721_contract?.smart_contract?.address, + }, + ]; break; } @@ -192,7 +151,6 @@ export class TransactionHelper { tokenId = arrEvent[0]?.tokenId; contractAddress = arrEvent[0]?.contractAddress; action = arrEvent[0]?.action; - eventAttr = arrEvent[0]?.eventAttr; } return { @@ -212,7 +170,6 @@ export class TransactionHelper { arrEvent, limit, action, - eventAttr, lstTypeTemp, lstType, }; diff --git a/src/shared/index.ts b/src/shared/index.ts index dfd0fd3a..eaf8fb28 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -17,11 +17,6 @@ export * from './dtos/pagination-params.dto'; export * from './auth/auth-token-output.dto'; export * from './auth/constants/role.constant'; -export * from './entities/block.entity'; -export * from './entities/transaction.entity'; export * from './entities/sync-status.entity'; -export * from './entities/validator.entity'; -export * from './entities/delegation.entity'; -export * from './entities/missed-block.entity'; export * from './entities/token-markets.entity'; export * from './entities/soulbound-token.entity'; From 2db2441a7cd362823732938a5432b205492a4219 Mon Sep 17 00:00:00 2001 From: Duc Nguyen <119287881+nthduc95@users.noreply.github.com> Date: Mon, 25 Dec 2023 09:46:14 +0700 Subject: [PATCH 2/2] [IBC][EUPHORIA] Baseline/euphoria 20231225 (#1063) (cherry picked from commit f83c9726f53dbfe95771610a30180440acc14b61) --- .../export-csv/export-csv.module.ts | 7 ++++- .../export-csv/services/export-csv.service.ts | 27 ++++++++++++++--- .../notification/notification.module.ts | 2 ++ .../notification/utils/notification.util.ts | 24 ++++++++++----- src/shared/helpers/transaction.helper.ts | 29 ++----------------- 5 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/components/export-csv/export-csv.module.ts b/src/components/export-csv/export-csv.module.ts index a5eca9b2..818cbe2f 100644 --- a/src/components/export-csv/export-csv.module.ts +++ b/src/components/export-csv/export-csv.module.ts @@ -10,6 +10,7 @@ import { PrivateNameTagRepository } from '../private-name-tag/repositories/priva import { EncryptionService } from '../encryption/encryption.service'; import { CipherKey } from '../../shared/entities/cipher-key.entity'; import { UserModule } from '../user/user.module'; +import { TokenMarketsRepository } from '../cw20-token/repositories/token-markets.repository'; @Module({ imports: [ @@ -17,7 +18,11 @@ import { UserModule } from '../user/user.module'; HttpModule, ConfigModule, UserModule, - TypeOrmModule.forFeature([PrivateNameTagRepository, CipherKey]), + TypeOrmModule.forFeature([ + PrivateNameTagRepository, + CipherKey, + TokenMarketsRepository, + ]), ], providers: [ExportCsvService, EncryptionService, ServiceUtil], controllers: [ExportCsvController], diff --git a/src/components/export-csv/services/export-csv.service.ts b/src/components/export-csv/services/export-csv.service.ts index 08a8d35a..76f9e4bf 100644 --- a/src/components/export-csv/services/export-csv.service.ts +++ b/src/components/export-csv/services/export-csv.service.ts @@ -20,6 +20,8 @@ import { import { ExportCsvParamDto } from '../dtos/export-csv-param.dto'; import { PrivateNameTagRepository } from '../../private-name-tag/repositories/private-name-tag.repository'; import { EncryptionService } from '../../encryption/encryption.service'; +import { IsNull, Not } from 'typeorm'; +import { TokenMarketsRepository } from '../../cw20-token/repositories/token-markets.repository'; @Injectable() export class ExportCsvService { @@ -32,6 +34,7 @@ export class ExportCsvService { private httpService: HttpService, private privateNameTagRepository: PrivateNameTagRepository, private encryptionService: EncryptionService, + private tokenMarketsRepository: TokenMarketsRepository, ) { this.logger.setContext(ExportCsvService.name); this.config = appConfig.default(); @@ -96,12 +99,16 @@ export class ExportCsvService { this.httpService.get(this.config.configUrl), ).then((rs) => rs.data); + const coinConfig = await this.tokenMarketsRepository.find({ + where: { denom: Not(IsNull()) }, + }); + const txs = TransactionHelper.convertDataAccountTransaction( response, envConfig?.chainConfig?.chain_info?.currencies[0], payload.dataType, payload.address, - envConfig?.chainConfig?.coins, + coinConfig, ); const fields = TX_HEADER.EXECUTED; @@ -153,12 +160,16 @@ export class ExportCsvService { this.httpService.get(this.config.configUrl), ).then((rs) => rs.data); + const coinConfig = await this.tokenMarketsRepository.find({ + where: { denom: Not(IsNull()) }, + }); + const txs = TransactionHelper.convertDataAccountTransaction( response, envConfig?.chainConfig?.chain_info?.currencies[0], payload.dataType, payload.address, - envConfig?.chainConfig?.coins, + coinConfig, ); let lstPrivateName; @@ -248,12 +259,16 @@ export class ExportCsvService { this.httpService.get(this.config.configUrl), ).then((rs) => rs.data); + const coinConfig = await this.tokenMarketsRepository.find({ + where: { denom: Not(IsNull()) }, + }); + const txs = TransactionHelper.convertDataAccountTransaction( response, envConfig?.chainConfig?.chain_info?.currencies[0], payload.dataType, payload.address, - envConfig?.chainConfig?.coins, + coinConfig, ); let lstPrivateName; @@ -335,12 +350,16 @@ export class ExportCsvService { this.httpService.get(this.config.configUrl), ).then((rs) => rs.data); + const coinConfig = await this.tokenMarketsRepository.find({ + where: { denom: Not(IsNull()) }, + }); + const txs = TransactionHelper.convertDataAccountTransaction( response, envConfig?.chainConfig?.chain_info?.currencies[0], payload.dataType, payload.address, - envConfig?.chainConfig?.coins, + coinConfig, ); let lstPrivateName; diff --git a/src/components/queues/notification/notification.module.ts b/src/components/queues/notification/notification.module.ts index 705f8920..9361f06a 100644 --- a/src/components/queues/notification/notification.module.ts +++ b/src/components/queues/notification/notification.module.ts @@ -17,6 +17,7 @@ import { UserActivity } from '../../../shared/entities/user-activity.entity'; import { NotificationRepository } from './repositories/notification.repository'; import { WatchList } from '../../../shared/entities/watch-list.entity'; import { User } from '../../../shared/entities/user.entity'; +import { TokenMarketsRepository } from '../../cw20-token/repositories/token-markets.repository'; @Module({ imports: [ @@ -28,6 +29,7 @@ import { User } from '../../../shared/entities/user.entity'; PublicNameTagRepository, NotificationTokenRepository, NotificationRepository, + TokenMarketsRepository, SyncPointRepository, SyncStatus, CipherKey, diff --git a/src/components/queues/notification/utils/notification.util.ts b/src/components/queues/notification/utils/notification.util.ts index 23f7a6e8..ffa3c89f 100644 --- a/src/components/queues/notification/utils/notification.util.ts +++ b/src/components/queues/notification/utils/notification.util.ts @@ -11,11 +11,16 @@ import { TransactionHelper } from '../../../../shared/helpers/transaction.helper import { NOTIFICATION } from '../../../../shared'; import { WatchList } from '../../../../shared/entities/watch-list.entity'; import { TRANSACTION_TYPE_ENUM } from '../../../../shared/constants/transaction'; +import { TokenMarketsRepository } from '../../../cw20-token/repositories/token-markets.repository'; +import { IsNull, Not } from 'typeorm'; @Injectable() export class NotificationUtil { private config; - constructor(private httpService: HttpService) { + constructor( + private httpService: HttpService, + private tokenMarketsRepository: TokenMarketsRepository, + ) { this.config = appConfig.default(); } @@ -306,17 +311,22 @@ export class NotificationUtil { const envConfig = await lastValueFrom( this.httpService.get(this.config.configUrl), ).then((rs) => rs.data); - - const coinConfig = envConfig?.chainConfig?.coins; + const coinConfig = await this.tokenMarketsRepository.find({ + where: { denom: Not(IsNull()) }, + }); const coinInfo = envConfig?.chainConfig?.chain_info?.currencies[0]; const listTx = []; data?.forEach((tx) => { tx.coin_transfers?.forEach((coin) => { const dataIBC = coinConfig.find((k) => k.denom === coin.denom) || {}; + // Get denom ibc in config + const denomIBC = + dataIBC['symbol']?.indexOf('ibc') === -1 + ? 'ibc/' + dataIBC['symbol'] + : dataIBC['symbol']; + // Get denom ibc not find in config or denom is native const denom = - dataIBC['display']?.indexOf('ibc') === -1 - ? 'ibc/' + dataIBC['display'] - : dataIBC['display']; + coin.denom?.indexOf('ibc') === -1 ? coinInfo.coinDenom : coin.denom; listTx.push({ tx_hash: tx.hash, @@ -332,7 +342,7 @@ export class NotificationUtil { dataIBC['decimal'] || coinInfo.coinDecimals, ), image: dataIBC['logo'] || '', - denom: denom || coinInfo.coinDenom, + denom: denomIBC || denom, }); }); }); diff --git a/src/shared/helpers/transaction.helper.ts b/src/shared/helpers/transaction.helper.ts index 1f80cd5f..b95233df 100644 --- a/src/shared/helpers/transaction.helper.ts +++ b/src/shared/helpers/transaction.helper.ts @@ -62,7 +62,6 @@ export class TransactionHelper { let tokenId; let contractAddress; let action; - let eventAttr; switch (modeQuery) { case TYPE_EXPORT.ExecutedTxs: @@ -75,9 +74,9 @@ export class TransactionHelper { coinConfig.find((k) => k.denom === coin.denom) || {}; // Get denom ibc in config const denomIBC = - dataIBC['display']?.indexOf('ibc') === -1 - ? 'ibc/' + dataIBC['display'] - : dataIBC['display']; + dataIBC['symbol']?.indexOf('ibc') === -1 + ? 'ibc/' + dataIBC['symbol'] + : dataIBC['symbol']; // Get denom ibc not find in config or denom is native const denom = coin.denom?.indexOf('ibc') === -1 @@ -243,26 +242,4 @@ export class TransactionHelper { } return result; } - - static getDataIBC(value, coinConfig) { - let result = {}; - let temp; - if (value.indexOf('ibc') >= 0) { - try { - if (!value.startsWith('ibc')) { - const match = value?.match(/\d+/g); - const temp = match?.length > 0 ? match[0] : 0; - value = value?.replace(temp, ''); - } - } catch {} - result = { display: value, decimals: 6 }; - temp = value.slice(value.indexOf('ibc')); - result = coinConfig.find((k) => k.denom === temp) || {}; - result['display'] = result['display'] || value; - } else { - result = { display: temp, decimals: 6 }; - } - result['denom'] = result['denom'] || temp; - return result; - } }