-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
00952b4
commit 2fdf497
Showing
6 changed files
with
276 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,20 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { IndexerService } from './indexer.service'; | ||
import { BuyTokenIndexer } from './buy-token.indexer'; | ||
import { TokenLaunchIndexer } from './token-launch.indexer'; | ||
import { BuyTokenModule } from 'src/services/buy-token/buy-token.module'; | ||
import { BuyTokenIndexer } from './buy-token.indexer'; | ||
import { SellTokenIndexer } from './sell-token.indexer'; | ||
import { TokenLaunchModule } from 'src/services/token-launch/token-launch.module'; | ||
import { BuyTokenModule } from 'src/services/buy-token/buy-token.module'; | ||
import { SellTokenModule } from 'src/services/sell-token/sell-token.module'; | ||
|
||
@Module({ | ||
imports: [TokenLaunchModule, BuyTokenModule], | ||
providers: [TokenLaunchIndexer, BuyTokenIndexer, IndexerService], | ||
exports: [TokenLaunchIndexer, BuyTokenIndexer], | ||
imports: [TokenLaunchModule, BuyTokenModule, SellTokenModule], | ||
providers: [ | ||
TokenLaunchIndexer, | ||
BuyTokenIndexer, | ||
SellTokenIndexer, | ||
IndexerService, | ||
], | ||
exports: [TokenLaunchIndexer, BuyTokenIndexer, SellTokenIndexer], | ||
}) | ||
export class IndexerModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
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 } from 'starknet'; | ||
import { SellTokenService } from 'src/services/sell-token/sell-token.service'; | ||
import { IndexerService } from './indexer.service'; | ||
import { ContractAddress } from 'src/common/types'; | ||
|
||
@Injectable() | ||
export class SellTokenIndexer { | ||
private readonly logger = new Logger(SellTokenIndexer.name); | ||
private readonly eventKeys: string[]; | ||
|
||
constructor( | ||
@Inject(SellTokenService) | ||
private readonly sellTokenService: SellTokenService, | ||
|
||
@Inject(IndexerService) | ||
private readonly indexerService: IndexerService, | ||
) { | ||
this.eventKeys = [ | ||
validateAndParseAddress(hash.getSelectorFromName('SellToken')), | ||
]; | ||
} | ||
|
||
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('SellToken')): | ||
this.logger.log('Event name: SellToken'); | ||
this.handleSellTokenEvent(header, event, transaction); | ||
break; | ||
default: | ||
this.logger.warn(`Unknown event type: ${eventKey}`); | ||
} | ||
} | ||
|
||
private async handleSellTokenEvent( | ||
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; | ||
|
||
const transferId = `${transactionHash}_${event.index}`; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const [_, callerFelt, tokenAddressFelt] = event.keys; | ||
|
||
const ownerAddress = validateAndParseAddress( | ||
`0x${FieldElement.toBigInt(callerFelt).toString(16)}`, | ||
) as ContractAddress; | ||
|
||
const tokenAddress = validateAndParseAddress( | ||
`0x${FieldElement.toBigInt(tokenAddressFelt).toString(16)}`, | ||
) as ContractAddress; | ||
|
||
const [ | ||
amountLow, | ||
amountHigh, | ||
priceLow, | ||
priceHigh, | ||
protocolFeeLow, | ||
protocolFeeHigh, | ||
lastPriceLow, | ||
lastPriceHigh, | ||
timestampFelt, | ||
quoteAmountLow, | ||
quoteAmountHigh, | ||
] = event.data; | ||
|
||
const amountRaw = uint256.uint256ToBN({ | ||
low: FieldElement.toBigInt(amountLow), | ||
high: FieldElement.toBigInt(amountHigh), | ||
}); | ||
const amount = formatUnits(amountRaw, constants.DECIMALS).toString(); | ||
|
||
const priceRaw = uint256.uint256ToBN({ | ||
low: FieldElement.toBigInt(priceLow), | ||
high: FieldElement.toBigInt(priceHigh), | ||
}); | ||
const price = formatUnits(priceRaw, constants.DECIMALS); | ||
|
||
const protocolFeeRaw = uint256.uint256ToBN({ | ||
low: FieldElement.toBigInt(protocolFeeLow), | ||
high: FieldElement.toBigInt(protocolFeeHigh), | ||
}); | ||
const protocolFee = formatUnits( | ||
protocolFeeRaw, | ||
constants.DECIMALS, | ||
).toString(); | ||
|
||
const lastPriceRaw = uint256.uint256ToBN({ | ||
low: FieldElement.toBigInt(lastPriceLow), | ||
high: FieldElement.toBigInt(lastPriceHigh), | ||
}); | ||
const lastPrice = formatUnits(lastPriceRaw, constants.DECIMALS).toString(); | ||
|
||
const quoteAmountRaw = uint256.uint256ToBN({ | ||
low: FieldElement.toBigInt(quoteAmountLow), | ||
high: FieldElement.toBigInt(quoteAmountHigh), | ||
}); | ||
const quoteAmount = formatUnits( | ||
quoteAmountRaw, | ||
constants.DECIMALS, | ||
).toString(); | ||
|
||
const timestamp = new Date( | ||
Number(FieldElement.toBigInt(timestampFelt)) * 1000, | ||
); | ||
|
||
const data = { | ||
transferId, | ||
network: 'starknet-sepolia', | ||
transactionHash, | ||
blockNumber: Number(blockNumber), | ||
blockHash, | ||
blockTimestamp: new Date(Number(blockTimestamp.seconds) * 1000), | ||
ownerAddress, | ||
memecoinAddress: tokenAddress, | ||
amount: Number(amount), | ||
price, | ||
protocolFee, | ||
lastPrice, | ||
quoteAmount, | ||
timestamp, | ||
transactionType: 'sell', | ||
}; | ||
|
||
await this.sellTokenService.create(data); | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
apps/nestjs-indexer/src/services/sell-token/interfaces/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './sell-token.interface'; |
17 changes: 17 additions & 0 deletions
17
apps/nestjs-indexer/src/services/sell-token/interfaces/sell-token.interface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
export interface SellToken { | ||
transferId: string; | ||
network: string; | ||
blockHash: string; | ||
blockNumber: number; | ||
blockTimestamp: Date; | ||
transactionHash: string; | ||
memecoinAddress: string; | ||
ownerAddress: string; | ||
lastPrice: string; | ||
quoteAmount: string; | ||
price: string; | ||
amount: number; | ||
protocolFee: string; | ||
timestamp: Date; | ||
transactionType: string; | ||
} |
9 changes: 9 additions & 0 deletions
9
apps/nestjs-indexer/src/services/sell-token/sell-token.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { SellTokenService } from './sell-token.service'; | ||
|
||
@Module({ | ||
imports: [], | ||
providers: [SellTokenService], | ||
exports: [SellTokenService], | ||
}) | ||
export class SellTokenModule {} |
78 changes: 78 additions & 0 deletions
78
apps/nestjs-indexer/src/services/sell-token/sell-token.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { Injectable, Logger } from '@nestjs/common'; | ||
import { PrismaService } from 'src/prisma/prisma.service'; | ||
import { SellToken } from './interfaces'; | ||
|
||
@Injectable() | ||
export class SellTokenService { | ||
private readonly logger = new Logger(SellTokenService.name); | ||
constructor(private readonly prismaService: PrismaService) {} | ||
|
||
async create(data: SellToken) { | ||
try { | ||
const sellTokenRecord = | ||
await this.prismaService.token_transactions.findUnique({ | ||
where: { transfer_id: data.transferId }, | ||
}); | ||
|
||
if (sellTokenRecord) { | ||
this.logger.warn( | ||
`Record with transfer ID ${data.transferId} already exists`, | ||
); | ||
return; | ||
} | ||
|
||
const tokenLaunchRecord = await this.prismaService.token_launch.findFirst( | ||
{ where: { memecoin_address: data.memecoinAddress } }, | ||
); | ||
|
||
if (!tokenLaunchRecord) { | ||
this.logger.warn( | ||
`Record with memecoin address ${data.memecoinAddress} doesn't exists`, | ||
); | ||
} else { | ||
const newSupply = | ||
Number(tokenLaunchRecord.current_supply ?? 0) + Number(data.amount); | ||
const newLiquidityRaised = | ||
Number(tokenLaunchRecord.liquidity_raised ?? 0) - | ||
Number(data.quoteAmount); | ||
const newTotalTokenHolded = | ||
Number(tokenLaunchRecord.total_token_holded ?? 0) - | ||
Number(data.amount); | ||
|
||
await this.prismaService.token_launch.update({ | ||
where: { transaction_hash: tokenLaunchRecord.transaction_hash }, | ||
data: { | ||
current_supply: newSupply.toString(), | ||
liquidity_raised: newLiquidityRaised.toString(), | ||
total_token_holded: newTotalTokenHolded.toString(), | ||
}, | ||
}); | ||
} | ||
|
||
await this.prismaService.token_transactions.create({ | ||
data: { | ||
transfer_id: data.transferId, | ||
network: data.network, | ||
block_hash: data.blockHash, | ||
block_number: data.blockNumber, | ||
block_timestamp: data.blockTimestamp, | ||
transaction_hash: data.transactionHash, | ||
memecoin_address: data.memecoinAddress, | ||
owner_address: data.ownerAddress, | ||
last_price: data.lastPrice, | ||
quote_amount: data.quoteAmount, | ||
price: data.price, | ||
amount: data.amount, | ||
protocol_fee: data.protocolFee, | ||
time_stamp: data.timestamp, | ||
transaction_type: data.transactionType, | ||
}, | ||
}); | ||
} catch (error) { | ||
this.logger.error( | ||
`Error creating buy token record: ${error.message}`, | ||
error.stack, | ||
); | ||
} | ||
} | ||
} |