Skip to content

Commit 905bcbd

Browse files
committed
Merge branch 'refactor_indexer_logic'
# Conflicts: # src/app.controller.ts
2 parents 4a62930 + 3bb31cc commit 905bcbd

File tree

6 files changed

+61
-53
lines changed

6 files changed

+61
-53
lines changed

src/app.controller.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@ import {
66
Logger,
77
NotFoundException,
88
Post,
9-
Query, UploadedFile, UseInterceptors,
10-
Headers, UseGuards, Request, ParseFilePipe, MaxFileSizeValidator, FileTypeValidator
9+
Query,
10+
UploadedFile,
11+
UseInterceptors,
12+
UseGuards,
13+
Request,
14+
ParseFilePipe,
15+
MaxFileSizeValidator,
16+
FileTypeValidator
1117
} from '@nestjs/common';
1218
import {ApiBearerAuth, ApiTags} from '@nestjs/swagger';
1319
import { ConfigService } from '@nestjs/config';
@@ -26,6 +32,7 @@ import {plainToInstance} from "class-transformer";
2632
import {JwtUserAccount} from "./entities/user-account.entity";
2733
import {GetWinnerLiquidityProvisionsDto} from "./dto/winner.liquidity.dto";
2834
import {CacheTTL} from "@nestjs/common/cache";
35+
import {IndexerService} from "./indexer/indexer.service";
2936
import {GetCompetitionsDto} from "./dto/competition.dto";
3037

3138
@SkipThrottle()
@@ -37,16 +44,20 @@ export class AppController {
3744
private readonly configService: ConfigService,
3845
private readonly appService: AppService,
3946
private readonly userService: UserService,
40-
private readonly gCloudService: GcloudService
47+
private readonly gCloudService: GcloudService,
48+
private readonly indexerService: IndexerService,
4149
) {}
4250
@Get('/version')
4351
getVersion() {
4452
return this.configService.get('version');
4553
}
4654

4755
@Get('/status')
48-
getStatus() {
49-
return 'OK';
56+
async getStatus() {
57+
const latestIndexedBlock = await this.indexerService.getLatestIndexedBlockNumber();
58+
return {
59+
latestIndexedBlock,
60+
}
5061
}
5162

5263
@CacheTTL(200)
@@ -130,7 +141,6 @@ export class AppController {
130141
]
131142
})
132143
) file: Express.Multer.File,
133-
@Headers() headers
134144
) {
135145
if(!req.user) {
136146
throw new BadRequestException('InvalidJWT')

src/config/index.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,6 @@ import {existsSync, readFileSync} from "fs";
55
const parseStringArray = (value: string) =>
66
value.split(',').map(item => item.trim().toLowerCase()).filter(_ => _)
77

8-
const readTestPrivateKey = () => {
9-
const filePath = pathResolve(__dirname, '..', '../keypair/private.pem')
10-
if(existsSync(filePath)) {
11-
return readFileSync(pathResolve(__dirname, '..', '../keypair/private.pem'))
12-
}
13-
return ''
14-
}
15-
16-
const readTestPublicKey = () => {
17-
const filePath = pathResolve(__dirname, '..', '../keypair/public.pem')
18-
if(existsSync(filePath)) {
19-
return readFileSync(filePath)
20-
}
21-
return ''
22-
}
23-
248
const getGoogleCloudConfig = () => {
259
return {
2610
"type": "service_account",

src/entities/token.entity.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,6 @@ export class Token {
5858
@Column({ type: 'json', nullable: true })
5959
uriData: TokenMetadata | null;
6060

61-
// @ApiProperty()
62-
// @Column({ type: 'integer' })
63-
// competitionId: number;
64-
6561
@ManyToOne(() => CompetitionEntity, {
6662
eager: true
6763
})

src/gcloud/gcloud.service.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import {Injectable, Logger} from '@nestjs/common';
1+
import {Injectable} from '@nestjs/common';
22
import {ConfigService} from "@nestjs/config";
33
import {Storage} from "@google-cloud/storage";
44
import {AddTokenMetadataDto} from "../dto/metadata.dto";
5+
import {JWTInput} from "google-auth-library/build/src/auth/credentials";
56

67
@Injectable()
78
export class GcloudService {
8-
private readonly logger = new Logger(GcloudService.name);
99
private readonly storage: Storage;
1010

1111
constructor(private readonly configService: ConfigService) {
12+
const googleCloudConfig = this.configService.get<JWTInput>('GOOGLE_CLOUD_CONFIG')
1213
this.storage = new Storage({
13-
projectId: 'pumpfun-440412',
14-
// keyFilename: serviceKey,
15-
credentials: this.configService.get('GOOGLE_CLOUD_CONFIG')
14+
projectId: googleCloudConfig.project_id,
15+
credentials: googleCloudConfig
1616
})
1717
}
1818

src/indexer/indexer.service.ts

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Injectable, Logger} from '@nestjs/common';
22
import {Contract, ContractAbi, EventLog, Web3} from "web3";
3-
import {TokenMetadata, TradeType} from "../types";
3+
import {ProtocolEvent, TokenMetadata, TradeType} from "../types";
44
import axios from "axios";
55
import * as process from "process";
66
import {
@@ -27,7 +27,8 @@ export class IndexerService {
2727
private readonly web3: Web3
2828
private readonly accountAddress: string
2929
private readonly tokenFactoryContract: Contract<ContractAbi>
30-
private readonly blocksIndexingRange = 1000
30+
private readonly maxBlocksRange = 1000
31+
private readonly maxBlocksBatchSize = 5
3132

3233
constructor(
3334
private configService: ConfigService,
@@ -37,32 +38,30 @@ export class IndexerService {
3738
) {
3839
const rpcUrl = configService.get('RPC_URL')
3940
const contractAddress = configService.get('TOKEN_FACTORY_ADDRESS')
40-
const initialBlockNumber = configService.get('INDEXER_INITIAL_BLOCK_NUMBER')
41+
const privateKey = configService.get('SERVICE_PRIVATE_KEY')
4142

4243
if(!contractAddress) {
4344
this.logger.error(`[TOKEN_FACTORY_ADDRESS] is missing but required, exit`)
4445
process.exit(1)
4546
}
4647

47-
if(!initialBlockNumber) {
48-
this.logger.error(`[INDEXER_INITIAL_BLOCK_NUMBER] is missing but required, exit`)
48+
if(!privateKey) {
49+
this.logger.error(`[SERVICE_PRIVATE_KEY] is missing but required, exit`)
4950
process.exit(1)
5051
}
5152

5253
this.logger.log(`Starting app service, RPC_URL=${
5354
rpcUrl
5455
}, TOKEN_FACTORY_ADDRESS=${
5556
contractAddress
56-
}, INDEXER_INITIAL_BLOCK_NUMBER=${
57-
initialBlockNumber
5857
}`)
5958

6059
this.web3 = new Web3(rpcUrl);
61-
const account = this.web3.eth.accounts.privateKeyToAccount(configService.get('SERVICE_PRIVATE_KEY'))
60+
const account = this.web3.eth.accounts.privateKeyToAccount(privateKey)
6261
this.accountAddress = account.address
6362
this.web3.eth.accounts.wallet.add(account);
64-
this.logger.log(`Service account address=${account.address}`)
6563
this.tokenFactoryContract = new this.web3.eth.Contract(TokenFactoryABI, contractAddress);
64+
this.logger.log(`Service account address=${account.address}`)
6665
this.bootstrap().then(
6766
() => {
6867
this.eventsTrackingLoop()
@@ -410,7 +409,7 @@ export class IndexerService {
410409
this.logger.log(`NewCompetitionStarted: competitionId=${competitionId}, timestamp=${timestamp}, txnHash=${txnHash}`);
411410
}
412411

413-
private async getLatestIndexedBlockNumber() {
412+
public async getLatestIndexedBlockNumber() {
414413
const indexerState = await this.dataSource.manager.findOne(IndexerState, {
415414
where: {},
416415
})
@@ -478,23 +477,14 @@ export class IndexerService {
478477
})
479478
]) as EventLog[][]
480479

481-
// concat and sort all events by block number and transaction index
482-
const protocolEvents: { data: EventLog; type: string }[] = tokenCreatedEvents
480+
return tokenCreatedEvents
483481
.map(data => ({ type: 'create_token', data }))
484482
.concat(...buyEvents.map(data => ({ type: 'buy', data })))
485483
.concat(...sellEvents.map(data => ({ type: 'sell', data })))
486484
.concat(...setWinnerEvents.map(data => ({ type: 'set_winner', data })))
487485
.concat(...burnAndSetWinnerEvents.map(data => ({ type: 'burn_token_and_set_winner', data })))
488486
.concat(...winnerLiquidityEvents.map(data => ({ type: 'winner_liquidity', data })))
489487
.concat(...newCompetitionEvents.map(data => ({ type: 'new_competition', data })))
490-
.sort((a, b) => {
491-
const blockNumberDiff = Number(a.data.blockNumber) - Number(b.data.blockNumber)
492-
if(blockNumberDiff !== 0) {
493-
return blockNumberDiff
494-
}
495-
return Number(a.data.transactionIndex) - Number(b.data.transactionIndex)
496-
})
497-
return protocolEvents
498488
}
499489

500490
async eventsTrackingLoop() {
@@ -506,13 +496,34 @@ export class IndexerService {
506496

507497
try {
508498
const blockchainBlockNumber = +(String(await this.web3.eth.getBlockNumber()))
509-
toBlock = fromBlock + this.blocksIndexingRange - 1
499+
toBlock = fromBlock + this.maxBlocksRange * this.maxBlocksBatchSize - 1
510500
if(toBlock > blockchainBlockNumber) {
511501
toBlock = blockchainBlockNumber
512502
}
513503

514504
if(toBlock - fromBlock >= 1) {
515-
const protocolEvents = await this.getEventsFromBlocksRange(fromBlock, toBlock)
505+
const delta = toBlock - fromBlock
506+
const numberOfBatches = Math.ceil(delta / this.maxBlocksRange)
507+
508+
const protocolEventsBatch = await Promise.all(
509+
new Array(numberOfBatches)
510+
.fill(null)
511+
.map(async (_, index, arr) => {
512+
const batchFromBlock = fromBlock + index * this.maxBlocksRange
513+
const batchToBlock = Math.min(batchFromBlock + this.maxBlocksRange, toBlock)
514+
return await this.getEventsFromBlocksRange(batchFromBlock, batchToBlock)
515+
})
516+
)
517+
518+
const protocolEvents = protocolEventsBatch
519+
.flat()
520+
.sort((a, b) => {
521+
const blockNumberDiff = Number(a.data.blockNumber) - Number(b.data.blockNumber)
522+
if(blockNumberDiff !== 0) {
523+
return blockNumberDiff
524+
}
525+
return Number(a.data.transactionIndex) - Number(b.data.transactionIndex)
526+
})
516527

517528
await this.dataSource.manager.transaction(async (transactionalEntityManager) => {
518529
for(const protocolEvent of protocolEvents) {

src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,10 @@ export interface Candle {
2727
volume: string
2828
time: string
2929
}
30+
31+
export interface ProtocolEvent {
32+
data: EventLog
33+
type: ProtocolEventType
34+
}
35+
36+
export type ProtocolEventType = 'create_token' | 'buy' | 'sell' | 'set_winner' | 'burn_token_and_set_winner' | 'winner_liquidity' | 'new_competition'

0 commit comments

Comments
 (0)