From 94b84479a7bfa78f05b44ba55c3ca31f62de8d17 Mon Sep 17 00:00:00 2001 From: artemkolodko Date: Fri, 6 Dec 2024 11:55:06 +0000 Subject: [PATCH] Refactor competitions entity --- src/app.controller.ts | 10 ++-- src/app.service.ts | 31 ++++++---- src/entities/competition.entity.ts | 21 ++++++- src/entities/index.ts | 3 - src/entities/token.entity.ts | 15 +++-- src/entities/token.winner.entity.ts | 41 ------------- src/indexer/indexer.service.ts | 89 +++++++++++++++++------------ 7 files changed, 109 insertions(+), 101 deletions(-) delete mode 100644 src/entities/token.winner.entity.ts diff --git a/src/app.controller.ts b/src/app.controller.ts index 577ed04..401f0fb 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -58,11 +58,6 @@ export class AppController { return this.appService.getTokenBalances(dto) } - @Get('/winners') - getWinners(@Query() dto: GetTokenWinnersDto) { - return this.appService.getTokenWinners(dto) - } - @Get('/candles') async getCandles(@Query() dto: GetCandlesDto) { return await this.appService.getCandles(dto) @@ -97,6 +92,11 @@ export class AppController { return this.appService.getTrades(dto) } + @Get('/competitions') + getCompetitions() { + return this.appService.getCompetitions() + } + @Get('/tokenBurns') getTokenBurns(@Query() dto: GetTokenBurnsDto) { return this.appService.getTokenBurns(dto) diff --git a/src/app.service.ts b/src/app.service.ts index 16aa3bb..5be3953 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -1,6 +1,15 @@ import {Injectable, Logger} from '@nestjs/common'; import {DataSource, EntityManager, MoreThan} from "typeorm"; -import {Comment, LiquidityProvision, Token, TokenBalance, TokenBurn, TokenWinner, Trade, UserAccount} from "./entities"; +import { + Comment, + CompetitionEntity, + LiquidityProvision, + Token, + TokenBalance, + TokenBurn, + Trade, + UserAccount +} from "./entities"; import {AddCommentDto, GetCommentsDto} from "./dto/comment.dto"; import {GetTokenBalancesDto, GetTokenBurnsDto, GetTokensDto, GetTokenWinnersDto} from "./dto/token.dto"; import {GetCandlesDto, GetTradesDto} from "./dto/trade.dto"; @@ -77,16 +86,6 @@ export class AppService { }) } - async getTokenWinners(dto: GetTokenWinnersDto) { - return await this.dataSource.manager.find(TokenWinner, { - order: { - timestamp: 'desc' - }, - take: dto.limit, - skip: dto.offset, - }) - } - async getTrades(dto: GetTradesDto){ return await this.dataSource.manager.find(Trade, { where: { @@ -102,6 +101,16 @@ export class AppService { }) } + async getCompetitions() { + return await this.dataSource.manager.find(CompetitionEntity, { + relations: ['winnerToken'], + where: {}, + order: { + competitionId: 'desc' + } + }) + } + async getTokenBurns(dto: GetTokenBurnsDto){ return await this.dataSource.manager.find(TokenBurn, { where: { diff --git a/src/entities/competition.entity.ts b/src/entities/competition.entity.ts index 9d0b993..6aa5d35 100644 --- a/src/entities/competition.entity.ts +++ b/src/entities/competition.entity.ts @@ -2,9 +2,13 @@ import { Column, CreateDateColumn, Entity, + OneToOne, PrimaryGeneratedColumn, + JoinColumn } from 'typeorm'; import { ApiProperty } from '@nestjs/swagger'; +import {Token} from "./token.entity"; + @Entity({ name: 'competitions' }) export class CompetitionEntity { @ApiProperty() @@ -24,8 +28,21 @@ export class CompetitionEntity { competitionId: number; @ApiProperty() - @Column({ type: 'bigint' }) - timestamp: number; + @Column({ type: 'bigint', nullable: false }) + timestampStart: number; + + @ApiProperty() + @Column({ type: 'bigint', nullable: true }) + timestampEnd: number; + + @ApiProperty() + @Column('bool', { default: false }) + isCompleted: boolean; + + @ApiProperty() + @OneToOne((type) => Token, token => token.competition) + @JoinColumn() + winnerToken: Token | null @ApiProperty() @CreateDateColumn({ name: 'createdAt' }) diff --git a/src/entities/index.ts b/src/entities/index.ts index efb6438..dc7bdaa 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -4,7 +4,6 @@ import { IndexerState } from './indexer.state.entity'; import { Trade } from './trade.entity'; import { Comment } from './comment.entity'; import { TokenBalance } from './token.balances.entity'; -import { TokenWinner } from './token.winner.entity'; import { SignInRequestEntity } from './signin.entity'; import { TokenBurn } from './token.burn.entity'; import { LiquidityProvision } from './liquidity.provision.entity'; @@ -17,7 +16,6 @@ const entities = [ Trade, Comment, TokenBalance, - TokenWinner, SignInRequestEntity, TokenBurn, LiquidityProvision, @@ -31,7 +29,6 @@ export { Trade, Comment, TokenBalance, - TokenWinner, SignInRequestEntity, TokenBurn, LiquidityProvision, diff --git a/src/entities/token.entity.ts b/src/entities/token.entity.ts index 56082fd..87749aa 100644 --- a/src/entities/token.entity.ts +++ b/src/entities/token.entity.ts @@ -14,13 +14,14 @@ import {UserAccount} from "./user-account.entity"; import {TokenMetadata} from "../types"; import {Trade} from "./trade.entity"; import Decimal from "decimal.js"; +import {CompetitionEntity} from "./competition.entity"; class ColumnNumericTransformer { to(data: string): string { return data; } from(data: number): string { - return new Decimal(data).toFixed() + return data ? new Decimal(data).toFixed() : '0' } } @@ -57,9 +58,15 @@ export class Token { @Column({ type: 'json', nullable: true }) uriData: TokenMetadata | null; - @ApiProperty() - @Column({ type: 'integer' }) - competitionId: number; + // @ApiProperty() + // @Column({ type: 'integer' }) + // competitionId: number; + + @ManyToOne(() => CompetitionEntity, { + eager: true + }) + @JoinTable() + competition: CompetitionEntity @ApiProperty() @Column({ type: 'bigint' }) diff --git a/src/entities/token.winner.entity.ts b/src/entities/token.winner.entity.ts deleted file mode 100644 index 7346164..0000000 --- a/src/entities/token.winner.entity.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - Column, - CreateDateColumn, - Entity, - PrimaryGeneratedColumn, - ManyToOne -} from 'typeorm'; -import { ApiProperty } from '@nestjs/swagger'; -import {Token} from "./token.entity"; - -@Entity({ name: 'token_winners' }) -export class TokenWinner { - @ApiProperty() - @PrimaryGeneratedColumn('uuid') - id: string; - - @ManyToOne(() => Token, { - eager: true - }) - token: Token - - @ApiProperty() - @Column({ type: 'integer' }) - competitionId: number; - - @ApiProperty() - @Column({ type: 'bigint' }) - timestamp: number; - - @ApiProperty() - @Column() - txnHash: string; - - @ApiProperty() - @Column({ type: 'integer' }) - blockNumber: number; - - @ApiProperty() - @CreateDateColumn({ name: 'createdAt' }) - createdAt: Date; -} diff --git a/src/indexer/indexer.service.ts b/src/indexer/indexer.service.ts index c754bdc..bfc6af7 100644 --- a/src/indexer/indexer.service.ts +++ b/src/indexer/indexer.service.ts @@ -2,7 +2,7 @@ import {Injectable, Logger} from '@nestjs/common'; import {Contract, ContractAbi, EventLog, Web3} from "web3"; import {TokenMetadata, TradeType} from "../types"; import axios from "axios"; -import process from "process"; +import * as process from "process"; import { CompetitionEntity, IndexerState, @@ -10,7 +10,6 @@ import { Token, TokenBalance, TokenBurn, - TokenWinner, Trade } from "../entities"; import {ConfigService} from "@nestjs/config"; @@ -107,37 +106,27 @@ export class IndexerService { return } - const existedWinner = await transactionalEntityManager.findOne(TokenWinner, { - where: { - token: { - address: winnerAddress, - }, - competitionId, - timestamp - } + const token = await this.appService.getTokenByAddress(winnerAddress, transactionalEntityManager) + if(!token) { + this.logger.error(`Failed to add winner: winner token not found in database, winnerAddress=${winnerAddress}, exit`) + process.exit(1) + } + + const competition = await transactionalEntityManager.findOne(CompetitionEntity, { + where: { competitionId } }) + if(!competition) { + this.logger.error(`Failed to add winner: competition=${competitionId} not found in database, exit`) + process.exit(1) + } - if(!existedWinner) { - const token = await this.appService.getTokenByAddress(winnerAddress, transactionalEntityManager) - if(!token) { - this.logger.error(`Failed to add winner: winner token not found in database, winnerAddress=${winnerAddress}, exit`) - process.exit(1) - } + token.isWinner = true + await transactionalEntityManager.save(token) - token.isWinner = true + competition.winnerToken = token + await transactionalEntityManager.save(competition) - await transactionalEntityManager.save(token) - await transactionalEntityManager.insert(TokenWinner, { - token, - timestamp, - competitionId, - txnHash, - blockNumber - }) - this.logger.log(`Added new token winner=${winnerAddress}, competitionId=${competitionId}, timestamp=${timestamp}`) - } else { - this.logger.warn(`Token winner=${winnerAddress}, competitionId=${competitionId}, timestamp=${timestamp} already exists, skip`) - } + this.logger.log(`Added new token winner=${winnerAddress}, competitionId=${competitionId}, timestamp=${timestamp}`) } private async processCreateTokenEvent(event: EventLog, transactionalEntityManager: EntityManager) { @@ -170,17 +159,32 @@ export class IndexerService { } } + const competition = await transactionalEntityManager.findOne(CompetitionEntity, { + where: {}, + order: { + competitionId: 'DESC' + } + }) + if(!competition) { + this.logger.error(`Create token: current competition is missing in DB; exit`) + process.exit(1) + } + if(competition.isCompleted) { + this.logger.error(`Create token: current competition is completed, new competitions has not started yet; exit`) + process.exit(1) + } + await transactionalEntityManager.insert(Token, { txnHash, address: tokenAddress, blockNumber: Number(event.blockNumber), name, symbol, - competitionId, timestamp, user, uri, uriData, + competition }); this.logger.log(`Create token: address=${tokenAddress}, name=${name}, symbol=${symbol}, uri=${uri}, creator=${creatorAddress}, competitionId=${competitionId}, txnHash=${txnHash}`); } @@ -370,11 +374,26 @@ export class IndexerService { const values = event.returnValues const competitionId = Number(values['competitionId'] as bigint) const timestamp = Number(values['timestamp'] as bigint) + + const competitions = await this.appService.getCompetitions() + if(competitions.length > 0) { + const currentCompetition = competitions[0] + if(currentCompetition) { + currentCompetition.isCompleted = true + currentCompetition.timestampEnd = timestamp + await transactionalEntityManager.save(currentCompetition) + } + + } + await transactionalEntityManager.insert(CompetitionEntity, { txnHash, blockNumber: Number(event.blockNumber), competitionId, - timestamp, + timestampStart: timestamp, + timestampEnd: null, + isCompleted: false, + winnerToken: null, }); this.logger.log(`NewCompetitionStarted: competitionId=${competitionId}, timestamp=${timestamp}, txnHash=${txnHash}`); } @@ -413,6 +432,10 @@ export class IndexerService { } if(toBlock - fromBlock >= 1) { + const newCompetitionEvents = await this.tokenFactoryContract.getPastEvents('allEvents', { + fromBlock, toBlock, topics: [ this.web3.utils.sha3('NewCompetitionStarted(uint256,uint256)')], + }) as EventLog[]; + const setWinnerEvents = await this.tokenFactoryContract.getPastEvents('allEvents', { fromBlock, toBlock, topics: [ this.web3.utils.sha3('SetWinner(address,uint256,uint256)')], }) as EventLog[]; @@ -421,10 +444,6 @@ export class IndexerService { fromBlock, toBlock, topics: [ this.web3.utils.sha3('WinnerLiquidityAdded(address,address,address,address,uint256,uint128,uint256,uint256,uint256)')], }) as EventLog[]; - const newCompetitionEvents = await this.tokenFactoryContract.getPastEvents('allEvents', { - fromBlock, toBlock, topics: [ this.web3.utils.sha3('NewCompetitionStarted(uint256,uint256)')], - }) as EventLog[]; - const tokenCreatedEvents = await this.tokenFactoryContract.getPastEvents('allEvents', { fromBlock, toBlock,