Skip to content

Commit

Permalink
feat: name service indexer
Browse files Browse the repository at this point in the history
  • Loading branch information
EjembiEmmanuel committed Dec 1, 2024
1 parent c0a5214 commit 76838ac
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 3 deletions.
5 changes: 5 additions & 0 deletions apps/nestjs-indexer/src/indexer/indexer.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,35 @@ import { TokenLaunchIndexer } from './token-launch.indexer';
import { DeployTokenIndexer } from './deploy-token.indexer';
import { BuyTokenIndexer } from './buy-token.indexer';
import { SellTokenIndexer } from './sell-token.indexer';
import { NameServiceIndexer } from './name-service.indexer';
import { TokenLaunchModule } from 'src/services/token-launch/token-launch.module';
import { DeployTokenModule } from 'src/services/deploy-token/deploy-token.module';
import { BuyTokenModule } from 'src/services/buy-token/buy-token.module';
import { SellTokenModule } from 'src/services/sell-token/sell-token.module';
import { NameServiceModule } from 'src/services/name-service/name-service.module';

@Module({
imports: [
TokenLaunchModule,
DeployTokenModule,
BuyTokenModule,
SellTokenModule,
NameServiceModule,
],
providers: [
TokenLaunchIndexer,
DeployTokenIndexer,
BuyTokenIndexer,
SellTokenIndexer,
NameServiceIndexer,
IndexerService,
],
exports: [
TokenLaunchIndexer,
DeployTokenIndexer,
BuyTokenIndexer,
SellTokenIndexer,
NameServiceIndexer,
],
})
export class IndexerModule {}
116 changes: 116 additions & 0 deletions apps/nestjs-indexer/src/indexer/name-service.indexer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { FieldElement, v1alpha2 as starknet } from '@apibara/starknet';
import { Inject, Injectable, Logger } from '@nestjs/common';
import { formatUnits } from 'viem';
import constants from 'src/common/constants';
import { uint256, validateAndParseAddress, hash, shortString } from 'starknet';
import { NameServiceService } from 'src/services/name-service/name-service.service';
import { IndexerService } from './indexer.service';
import { ContractAddress } from 'src/common/types';

@Injectable()
export class NameServiceIndexer {
private readonly logger = new Logger(NameServiceIndexer.name);
private readonly eventKeys: string[];

constructor(
@Inject(NameServiceService)
private readonly nameServiceService: NameServiceService,

@Inject(IndexerService)
private readonly indexerService: IndexerService,
) {
this.eventKeys = [
validateAndParseAddress(hash.getSelectorFromName('UsernameClaimed')),
];
}

async onModuleInit() {
this.indexerService.registerIndexer(
this.eventKeys,
this.handleEvents.bind(this),
);
}

private async handleEvents(
header: starknet.IBlockHeader,
event: starknet.IEvent,
transaction: starknet.ITransaction,
) {
this.logger.log('Received event');
const eventKey = validateAndParseAddress(FieldElement.toHex(event.keys[0]));

switch (eventKey) {
case validateAndParseAddress(hash.getSelectorFromName('UsernameClaimed')):
this.logger.log('Event name: CreateLaunch');
this.handleUsernameClaimedEvent(header, event, transaction);
break;
default:
this.logger.warn(`Unknown event type: ${eventKey}`);
}
}

private async handleUsernameClaimedEvent(
header: starknet.IBlockHeader,
event: starknet.IEvent,
transaction: starknet.ITransaction,
) {
const {
blockNumber,
blockHash: blockHashFelt,
timestamp: blockTimestamp,
} = header;

const blockHash = validateAndParseAddress(
`0x${FieldElement.toBigInt(blockHashFelt).toString(16)}`,
) as ContractAddress;

const transactionHashFelt = transaction.meta.hash;
const transactionHash = validateAndParseAddress(
`0x${FieldElement.toBigInt(transactionHashFelt).toString(16)}`,
) as ContractAddress;

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, addressFelt] = event.keys;

const address = validateAndParseAddress(
`0x${FieldElement.toBigInt(addressFelt).toString(16)}`,
) as ContractAddress;

const [usernameFelt, expiryFelt, paidLow, paidHigh, quoteTokenFelt] =
event.data;

const username = usernameFelt
? shortString.decodeShortString(
FieldElement.toBigInt(usernameFelt).toString(),
)
: '';

const expiry = new Date(Number(FieldElement.toBigInt(expiryFelt)) * 1000);

const paidRaw = uint256.uint256ToBN({
low: FieldElement.toBigInt(paidLow),
high: FieldElement.toBigInt(paidHigh),
});
const paid = formatUnits(paidRaw, constants.DECIMALS).toString();

const quoteToken = validateAndParseAddress(
`0x${FieldElement.toBigInt(quoteTokenFelt).toString(16)}`,
) as ContractAddress;

const data = {
transactionHash,
network: 'starknet-sepolia',
blockNumber: Number(blockNumber),
blockHash,
blockTimestamp: new Date(Number(blockTimestamp.seconds) * 1000),
ownerAddress: address,
expiry,
name: username,
username,
paid,
quoteToken: quoteToken,
};

await this.nameServiceService.create(data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './name-service.interface';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface NameService {
transactionHash: string;
network?: string;
blockHash?: string;
blockNumber?: number;
blockTimestamp?: Date;
ownerAddress?: string;
expiry?: Date;
username?: string;
name?: string;
symbol?: string;
paid?: string;
quoteAddress?: string;
cursor?: number;
timestamp?: Date;
createdAt?: Date;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { NameServiceService } from './name-service.service';

@Module({
imports: [],
providers: [NameServiceService],
exports: [NameServiceService],
})
export class NameServiceModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import { NameService } from './interfaces';

@Injectable()
export class NameServiceService {
private readonly logger = new Logger(NameServiceService.name);
constructor(private readonly prismaService: PrismaService) {}

async create(data: NameService) {
try {
const nameServiceRecord =
await this.prismaService.username_claimed.findUnique({
where: { transaction_hash: data.transactionHash },
});

if (nameServiceRecord) {
this.logger.warn(
`Record with transaction hash ${data.transactionHash} already exists`,
);
return;
}

await this.prismaService.username_claimed.create({
data: {
transaction_hash: data.transactionHash,
network: data.network,
block_hash: data.blockHash,
block_number: data.blockNumber,
block_timestamp: data.blockTimestamp,
owner_address: data.ownerAddress,
expiry: data.expiry,
username: data.username,
symbol: data.symbol,
paid: data.paid,
quote_address: data.quoteAddress,
time_stamp: data.timestamp,
},
});
} catch (error) {
this.logger.error(
`Error creating name service record: ${error.message}`,
error.stack,
);
}
}
}
6 changes: 3 additions & 3 deletions packages/indexer-prisma/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,13 @@ model username_claimed {
block_number BigInt?
block_timestamp DateTime? @db.Timestamp(6)
transaction_hash String @id
expiry String?
expiry DateTime? @db.Timestamp(6)
username String?
name String?
symbol String?
paid String?
quote_address String?
created_at DateTime? @default(now()) @db.Timestamp(6)
cursor BigInt? @map("_cursor")
time_stamp String?
time_stamp DateTime? @db.Timestamp(6)
created_at DateTime? @default(now()) @db.Timestamp(6)
}

0 comments on commit 76838ac

Please sign in to comment.