diff --git a/packages/indexer/src/data-indexing/service/CCTPIndexerDataHandler.ts b/packages/indexer/src/data-indexing/service/CCTPIndexerDataHandler.ts index 90319613..e58254c0 100644 --- a/packages/indexer/src/data-indexing/service/CCTPIndexerDataHandler.ts +++ b/packages/indexer/src/data-indexing/service/CCTPIndexerDataHandler.ts @@ -40,9 +40,13 @@ import { } from "../adapter/cctp-v2/service"; import { entities, SaveQueryResult } from "@repo/indexer-database"; import { - formatAndSaveSimpleTransferFlowCompletedEvents, - getSimpleTransferFlowCompletedEventsFromTransactionReceipts, + formatFallbackHyperEVMFlowCompletedEvent, + formatSimpleTransferFlowCompletedEvent, } from "./hyperEvmExecutor"; +import { + formatAndSaveEvents, + getEventsFromTransactionReceipts, +} from "./eventProcessing"; export type EvmBurnEventsPair = { depositForBurn: DepositForBurnEvent; @@ -246,16 +250,17 @@ export class CCTPIndexerDataHandler implements IndexerDataHandler { ), ]); - const messageSentEvents = this.getMessageSentEventsFromTransactionReceipts( + const messageSentEvents = getEventsFromTransactionReceipts( filteredDepositForBurnTxReceipts, messageTransmitterAddress, + EventDecoder.decodeCCTPMessageSentEvents, ); - const mintAndWithdrawEvents = - this.getMintAndWithdrawEventsFromTransactionReceipts( - filteredMessageReceivedTxReceipts, - tokenMessengerAddress, - ); + const mintAndWithdrawEvents = getEventsFromTransactionReceipts( + filteredMessageReceivedTxReceipts, + tokenMessengerAddress, + EventDecoder.decodeCCTPMintAndWithdrawEvents, + ); const burnEvents = await this.matchDepositForBurnWithMessageSentEvents( filteredDepositForBurnEvents, @@ -289,23 +294,23 @@ export class CCTPIndexerDataHandler implements IndexerDataHandler { let fallbackHyperEVMFlowCompletedEvents: FallbackHyperEVMFlowCompletedLog[] = []; if (sponsoredCCTPDstPeripheryAddress) { - simpleTransferFlowCompletedEvents = - getSimpleTransferFlowCompletedEventsFromTransactionReceipts( - filteredMessageReceivedTxReceipts, - sponsoredCCTPDstPeripheryAddress, - ); + simpleTransferFlowCompletedEvents = getEventsFromTransactionReceipts( + filteredMessageReceivedTxReceipts, + sponsoredCCTPDstPeripheryAddress, + EventDecoder.decodeSimpleTransferFlowCompletedEvents, + ); - arbitraryActionsExecutedEvents = - this.getArbitraryActionsExecutedEventsFromTransactionReceipts( - filteredMessageReceivedTxReceipts, - sponsoredCCTPDstPeripheryAddress, - ); + arbitraryActionsExecutedEvents = getEventsFromTransactionReceipts( + filteredMessageReceivedTxReceipts, + sponsoredCCTPDstPeripheryAddress, + EventDecoder.decodeArbitraryActionsExecutedEvents, + ); - fallbackHyperEVMFlowCompletedEvents = - this.getFallbackHyperEVMFlowCompletedEventsFromTransactionReceipts( - filteredMessageReceivedTxReceipts, - sponsoredCCTPDstPeripheryAddress, - ); + fallbackHyperEVMFlowCompletedEvents = getEventsFromTransactionReceipts( + filteredMessageReceivedTxReceipts, + sponsoredCCTPDstPeripheryAddress, + EventDecoder.decodeFallbackHyperEVMFlowCompletedEvents, + ); } this.runChecks(burnEvents, mintEvents); @@ -408,52 +413,6 @@ export class CCTPIndexerDataHandler implements IndexerDataHandler { } } - private getMessageSentEventsFromTransactionReceipts( - transactionReceipts: Record, - messageTransmitterAddress: string, - ) { - const events: MessageSentLog[] = []; - - for (const txHash of Object.keys(transactionReceipts)) { - const transactionReceipt = transactionReceipts[ - txHash - ] as providers.TransactionReceipt; - const messageSentEvents: MessageSentLog[] = - EventDecoder.decodeCCTPMessageSentEvents( - transactionReceipt, - messageTransmitterAddress, - ); - if (messageSentEvents.length > 0) { - events.push(...messageSentEvents); - } - } - - return events; - } - - private getMintAndWithdrawEventsFromTransactionReceipts( - transactionReceipts: Record, - tokenMessengerAddress: string, - ) { - const events: MintAndWithdrawLog[] = []; - - for (const txHash of Object.keys(transactionReceipts)) { - const transactionReceipt = transactionReceipts[ - txHash - ] as providers.TransactionReceipt; - const mintAndWithdrawEvents: MintAndWithdrawLog[] = - EventDecoder.decodeCCTPMintAndWithdrawEvents( - transactionReceipt, - tokenMessengerAddress, - ); - if (mintAndWithdrawEvents.length > 0) { - events.push(...mintAndWithdrawEvents); - } - } - - return events; - } - private getSponsoredDepositForBurnEventsFromTransactionReceipts( transactionReceipts: Record, sponsoredCCTPSrcPeripheryAddress: string, @@ -512,50 +471,6 @@ export class CCTPIndexerDataHandler implements IndexerDataHandler { return events; } - private getArbitraryActionsExecutedEventsFromTransactionReceipts( - transactionReceipts: Record, - arbitraryEvmFlowExecutorAddress: string, - ) { - const events: ArbitraryActionsExecutedLog[] = []; - for (const txHash of Object.keys(transactionReceipts)) { - const transactionReceipt = transactionReceipts[ - txHash - ] as providers.TransactionReceipt; - const arbitraryActionsExecutedEvents: ArbitraryActionsExecutedLog[] = - EventDecoder.decodeArbitraryActionsExecutedEvents( - transactionReceipt, - arbitraryEvmFlowExecutorAddress, - ); - if (arbitraryActionsExecutedEvents.length > 0) { - events.push(...arbitraryActionsExecutedEvents); - } - } - - return events; - } - - private getFallbackHyperEVMFlowCompletedEventsFromTransactionReceipts( - transactionReceipts: Record, - arbitraryEvmFlowExecutorAddress: string, - ) { - const events: FallbackHyperEVMFlowCompletedLog[] = []; - for (const txHash of Object.keys(transactionReceipts)) { - const transactionReceipt = transactionReceipts[ - txHash - ] as providers.TransactionReceipt; - const fallbackHyperEVMFlowCompletedEvents: FallbackHyperEVMFlowCompletedLog[] = - EventDecoder.decodeFallbackHyperEVMFlowCompletedEvents( - transactionReceipt, - arbitraryEvmFlowExecutorAddress, - ); - if (fallbackHyperEVMFlowCompletedEvents.length > 0) { - events.push(...fallbackHyperEVMFlowCompletedEvents); - } - } - - return events; - } - private async getTransactionsReceipts(uniqueTransactionHashes: string[]) { const transactionReceipts = await Promise.all( uniqueTransactionHashes.map(async (txHash) => { @@ -630,7 +545,12 @@ export class CCTPIndexerDataHandler implements IndexerDataHandler { const chainAgnosticSponsoredBurnEvents = sponsoredBurnEvents.map((event) => this.convertSponsoredDepositForBurnToChainAgnostic(event), ); - + const primaryKeyColumns = [ + "chainId", + "blockNumber", + "transactionHash", + "logIndex", + ]; const [ savedBurnEvents, savedMintEvents, @@ -657,12 +577,15 @@ export class CCTPIndexerDataHandler implements IndexerDataHandler { this.chainId, blocksTimestamps, ), - formatAndSaveSimpleTransferFlowCompletedEvents( + formatAndSaveEvents( this.cctpRepository, simpleTransferFlowCompletedEvents, lastFinalisedBlock, this.chainId, blocksTimestamps, + formatSimpleTransferFlowCompletedEvent, + entities.SimpleTransferFlowCompleted, + primaryKeyColumns as (keyof entities.SimpleTransferFlowCompleted)[], ), this.cctpRepository.formatAndSaveArbitraryActionsExecutedEvents( arbitraryActionsExecutedEvents, @@ -670,11 +593,15 @@ export class CCTPIndexerDataHandler implements IndexerDataHandler { this.chainId, blocksTimestamps, ), - this.cctpRepository.formatAndSaveFallbackHyperEVMFlowCompletedEvents( + formatAndSaveEvents( + this.cctpRepository, fallbackHyperEVMFlowCompletedEvents, lastFinalisedBlock, this.chainId, blocksTimestamps, + formatFallbackHyperEVMFlowCompletedEvent, + entities.FallbackHyperEVMFlowCompleted, + primaryKeyColumns as (keyof entities.FallbackHyperEVMFlowCompleted)[], ), ]); diff --git a/packages/indexer/src/data-indexing/service/OFTIndexerDataHandler.ts b/packages/indexer/src/data-indexing/service/OFTIndexerDataHandler.ts index ce7e7a17..14518bb7 100644 --- a/packages/indexer/src/data-indexing/service/OFTIndexerDataHandler.ts +++ b/packages/indexer/src/data-indexing/service/OFTIndexerDataHandler.ts @@ -4,7 +4,11 @@ import * as across from "@across-protocol/sdk"; import { entities, SaveQueryResult } from "@repo/indexer-database"; -import { BlockRange, SimpleTransferFlowCompletedLog } from "../model"; +import { + BlockRange, + FallbackHyperEVMFlowCompletedLog, + SimpleTransferFlowCompletedLog, +} from "../model"; import { IndexerDataHandler } from "./IndexerDataHandler"; import { O_ADAPTER_UPGRADEABLE_ABI } from "../adapter/oft/abis"; import { @@ -21,8 +25,12 @@ import { import { EventDecoder } from "../../web3/EventDecoder"; import { fetchEvents } from "../../utils/contractUtils"; import { - formatAndSaveSimpleTransferFlowCompletedEvents, - getSimpleTransferFlowCompletedEventsFromTransactionReceipts, + formatAndSaveEvents, + getEventsFromTransactionReceipts, +} from "./eventProcessing"; +import { + formatFallbackHyperEVMFlowCompletedEvent, + formatSimpleTransferFlowCompletedEvent, } from "./hyperEvmExecutor"; import { CHAIN_IDs } from "@across-protocol/constants"; @@ -31,6 +39,7 @@ export type FetchEventsResult = { oftReceivedEvents: OFTReceivedEvent[]; sponsoredOFTSendEvents: SponsoredOFTSendLog[]; simpleTransferFlowCompletedEvents: SimpleTransferFlowCompletedLog[]; + fallbackHyperEVMFlowCompletedEvents: FallbackHyperEVMFlowCompletedLog[]; blocks: Record; }; export type StoreEventsResult = { @@ -38,6 +47,7 @@ export type StoreEventsResult = { oftReceivedEvents: SaveQueryResult[]; sponsoredOFTSendEvents: SaveQueryResult[]; simpleTransferFlowCompletedEvents: SaveQueryResult[]; + fallbackHyperEVMFlowCompletedEvents: SaveQueryResult[]; }; // Taken from https://hyperevmscan.io/tx/0xf72cfb2c0a9f781057cd4f7beca6fc6bd9290f1d73adef1142b8ac1b0ed7186c#eventlog#37 @@ -157,8 +167,6 @@ export class OFTIndexerDataHandler implements IndexerDataHandler { oftSentTransactions, oftSentEvents, ); - blockHashes.push(...filteredOftSentEvents.map((event) => event.blockHash)); - const filteredOftReceivedEvents = await this.filterTransactionsForSupportedEndpointIds(oftReceivedEvents); const filteredOftSentTransactionReceipts = @@ -166,19 +174,22 @@ export class OFTIndexerDataHandler implements IndexerDataHandler { ...new Set(filteredOftSentEvents.map((event) => event.transactionHash)), ]); blockHashes.push( + ...filteredOftSentEvents.map((event) => event.blockHash), ...filteredOftReceivedEvents.map((event) => event.blockHash), ); let sponsoredOFTSendEvents: SponsoredOFTSendLog[] = []; if (sponsoredOFTSrcPeripheryAddress) { - sponsoredOFTSendEvents = - this.getSponsoredOFTSendEventsFromTransactionReceipts( - filteredOftSentTransactionReceipts, - sponsoredOFTSrcPeripheryAddress, - ); + sponsoredOFTSendEvents = getEventsFromTransactionReceipts( + filteredOftSentTransactionReceipts, + sponsoredOFTSrcPeripheryAddress, + EventDecoder.decodeOFTSponsoredSendEvents, + ); } - const simpleTransferFlowCompletedEvents: SimpleTransferFlowCompletedLog[] = + let simpleTransferFlowCompletedEvents: SimpleTransferFlowCompletedLog[] = + []; + let fallbackHyperEVMFlowCompletedEvents: FallbackHyperEVMFlowCompletedLog[] = []; const composeDeliveredEvents = await fetchEvents( this.provider, @@ -192,13 +203,22 @@ export class OFTIndexerDataHandler implements IndexerDataHandler { const transactionReceipts = await this.getTransactionsReceipts( composeDeliveredEvents.map((event) => event.transactionHash), ); - const events = - getSimpleTransferFlowCompletedEventsFromTransactionReceipts( - transactionReceipts, - dstOftHandlerAddress, - ); - simpleTransferFlowCompletedEvents.push(...events); - blockHashes.push(...events.map((event) => event.blockHash)); + simpleTransferFlowCompletedEvents = getEventsFromTransactionReceipts( + transactionReceipts, + dstOftHandlerAddress, + EventDecoder.decodeSimpleTransferFlowCompletedEvents, + ); + fallbackHyperEVMFlowCompletedEvents = getEventsFromTransactionReceipts( + transactionReceipts, + dstOftHandlerAddress, + EventDecoder.decodeFallbackHyperEVMFlowCompletedEvents, + ); + blockHashes.push( + ...simpleTransferFlowCompletedEvents.map((event) => event.blockHash), + ...fallbackHyperEVMFlowCompletedEvents.map( + (event) => event.blockHash, + ), + ); } } @@ -220,6 +240,7 @@ export class OFTIndexerDataHandler implements IndexerDataHandler { oftReceivedEvents: filteredOftReceivedEvents, sponsoredOFTSendEvents, simpleTransferFlowCompletedEvents, + fallbackHyperEVMFlowCompletedEvents, blocks, }; } @@ -235,6 +256,7 @@ export class OFTIndexerDataHandler implements IndexerDataHandler { oftSentEvents, sponsoredOFTSendEvents, simpleTransferFlowCompletedEvents, + fallbackHyperEVMFlowCompletedEvents, } = events; const blocksTimestamps = this.getBlocksTimestamps(blocks); const [ @@ -242,6 +264,7 @@ export class OFTIndexerDataHandler implements IndexerDataHandler { savedOftReceivedEvents, savedSponsoredOFTSendEvents, savedSimpleTransferFlowCompletedEvents, + savedFallbackHyperEVMFlowCompletedEvents, ] = await Promise.all([ this.oftRepository.formatAndSaveOftSentEvents( oftSentEvents, @@ -263,12 +286,25 @@ export class OFTIndexerDataHandler implements IndexerDataHandler { this.chainId, blocksTimestamps, ), - formatAndSaveSimpleTransferFlowCompletedEvents( + formatAndSaveEvents( this.oftRepository, simpleTransferFlowCompletedEvents, lastFinalisedBlock, this.chainId, blocksTimestamps, + formatSimpleTransferFlowCompletedEvent, + entities.SimpleTransferFlowCompleted, + ["chainId", "blockNumber", "transactionHash", "logIndex"], + ), + formatAndSaveEvents( + this.oftRepository, + fallbackHyperEVMFlowCompletedEvents, + lastFinalisedBlock, + this.chainId, + blocksTimestamps, + formatFallbackHyperEVMFlowCompletedEvent, + entities.FallbackHyperEVMFlowCompleted, + ["chainId", "blockNumber", "transactionHash", "logIndex"], ), ]); @@ -277,6 +313,8 @@ export class OFTIndexerDataHandler implements IndexerDataHandler { oftReceivedEvents: savedOftReceivedEvents, sponsoredOFTSendEvents: savedSponsoredOFTSendEvents, simpleTransferFlowCompletedEvents: savedSimpleTransferFlowCompletedEvents, + fallbackHyperEVMFlowCompletedEvents: + savedFallbackHyperEVMFlowCompletedEvents, }; } @@ -312,26 +350,6 @@ export class OFTIndexerDataHandler implements IndexerDataHandler { return transactionReceiptsMap; } - private getSponsoredOFTSendEventsFromTransactionReceipts( - transactionReceipts: Record, - sponsoredOFTSrcPeripheryAddress: string, - ) { - const events: SponsoredOFTSendLog[] = []; - for (const txHash of Object.keys(transactionReceipts)) { - const transactionReceipt = transactionReceipts[ - txHash - ] as providers.TransactionReceipt; - const sponsoredOFTSendEvents = EventDecoder.decodeOFTSponsoredSendEvents( - transactionReceipt, - sponsoredOFTSrcPeripheryAddress, - ); - if (sponsoredOFTSendEvents.length > 0) { - events.push(...sponsoredOFTSendEvents); - } - } - return events; - } - private async filterTransactionsForSupportedEndpointIds( oftReceivedEvents: OFTReceivedEvent[], ) { diff --git a/packages/indexer/src/data-indexing/service/eventProcessing.ts b/packages/indexer/src/data-indexing/service/eventProcessing.ts new file mode 100644 index 00000000..11aa14ca --- /dev/null +++ b/packages/indexer/src/data-indexing/service/eventProcessing.ts @@ -0,0 +1,93 @@ +import { SaveQueryResult } from "@repo/indexer-database"; +import * as across from "@across-protocol/sdk"; +import { utils } from "@repo/indexer-database"; +import { ObjectLiteral } from "typeorm"; +import { providers } from "ethers"; + +/** + * Formats and saves a batch of blockchain events to the database using a provided formatting function. + * This generic function is designed to handle different types of events by accepting a specific formatting function for each event type. + * It maps the raw event data to the database entity format, marks them as finalized if they are within the finalized block range, + * and then saves them to the database in batches. + * + * @param repository The repository for database operations, specifically for saving blockchain events. + * @param events An array of events to be processed. + * @param lastFinalisedBlock The last block number that is considered finalized. + * @param chainId The ID of the chain where these events were emitted. + * @param blockDates A record mapping block numbers to their corresponding `Date` objects. + * @param formatEvent A function that takes an event and returns a partial entity. + * @param entity The entity to save the events to. + * @param primaryKeyColumns The primary key columns of the entity. + * @param chunkSize The number of events to save in a single batch. Defaults to 100. + * @returns A promise that resolves to an array of `SaveQueryResult` for the saved events. + */ +export async function formatAndSaveEvents( + repository: utils.BlockchainEventRepository, + events: T[], + lastFinalisedBlock: number, + chainId: number, + blockDates: Record, + formatEvent: ( + event: T, + finalised: boolean, + blockTimestamp: Date, + chainId: number, + ) => Partial, + entity: new () => TEntity, + primaryKeyColumns: (keyof TEntity)[], + chunkSize = 100, +): Promise[]> { + const formattedEvents = events.map((event: any) => { + const finalised = event.blockNumber <= lastFinalisedBlock; + const blockTimestamp = blockDates[event.blockNumber]!; + return formatEvent(event, finalised, blockTimestamp, chainId); + }); + + const chunkedEvents = across.utils.chunk(formattedEvents, chunkSize); + const savedEvents = await Promise.all( + chunkedEvents.map((eventsChunk) => + repository.saveAndHandleFinalisationBatch( + entity, + eventsChunk, + primaryKeyColumns as string[], + [], + ), + ), + ); + const result = savedEvents.flat(); + return result; +} + +/** + * Decodes and extracts events from a collection of transaction receipts using a provided decoding function. + * This generic function iterates over transaction receipts, decodes logs, and filters for events + * emitted by a specified contract address. + * + * @param transactionReceipts A record of transaction receipts, indexed by their transaction hash. + * @param contractAddress The address of the contract to filter events from. + * @param decodeEvents A function that takes a transaction receipt and contract address and returns an array of decoded events. + * @returns An array of decoded event objects. + */ +export function getEventsFromTransactionReceipts( + transactionReceipts: Record, + contractAddress: string, + decodeEvents: ( + receipt: providers.TransactionReceipt, + contractAddress?: string, + ) => T[], +): T[] { + const events: T[] = []; + for (const txHash of Object.keys(transactionReceipts)) { + const transactionReceipt = transactionReceipts[ + txHash + ] as providers.TransactionReceipt; + const decodedEvents: T[] = decodeEvents( + transactionReceipt, + contractAddress, + ); + if (decodedEvents.length > 0) { + events.push(...decodedEvents); + } + } + return events; +} diff --git a/packages/indexer/src/data-indexing/service/hyperEvmExecutor.ts b/packages/indexer/src/data-indexing/service/hyperEvmExecutor.ts index da91ceb1..ab7450a1 100644 --- a/packages/indexer/src/data-indexing/service/hyperEvmExecutor.ts +++ b/packages/indexer/src/data-indexing/service/hyperEvmExecutor.ts @@ -1,92 +1,65 @@ -import { ethers, providers } from "ethers"; -import { SimpleTransferFlowCompletedLog } from "../model"; -import { EventDecoder } from "../../web3/EventDecoder"; -import { entities, SaveQueryResult } from "@repo/indexer-database"; -import * as across from "@across-protocol/sdk"; -import { BlockchainEventRepository } from "../../../../indexer-database/dist/src/utils"; +import { + FallbackHyperEVMFlowCompletedLog, + SimpleTransferFlowCompletedLog, +} from "../model"; +import { entities } from "@repo/indexer-database"; /** - * Decodes and extracts `SimpleTransferFlowCompleted` events from a collection of transaction receipts. - * This function iterates over transaction receipts, decodes logs, and filters for `SimpleTransferFlowCompleted` events - * emitted by a specified HyperEVM executor contract. - * - * @param transactionReceipts A record of transaction receipts, indexed by their transaction hash. - * @param contractAddress The address of the HyperEVM executor contract to filter events from. - * @returns An array of decoded `SimpleTransferFlowCompletedLog` objects. + * @constant formatSimpleTransferFlowCompletedEvent + * Formats a `SimpleTransferFlowCompletedLog` event into a partial `SimpleTransferFlowCompleted` entity. + * @param event The `SimpleTransferFlowCompletedLog` event to format. + * @param finalised A boolean indicating if the event is finalized. + * @param blockTimestamp The timestamp of the block where the event was emitted. + * @param chainId The ID of the chain where the event was emitted. + * @returns A partial `SimpleTransferFlowCompleted` entity. */ -export function getSimpleTransferFlowCompletedEventsFromTransactionReceipts( - transactionReceipts: Record, - contractAddress: string, -) { - const events: SimpleTransferFlowCompletedLog[] = []; - for (const txHash of Object.keys(transactionReceipts)) { - const transactionReceipt = transactionReceipts[ - txHash - ] as providers.TransactionReceipt; - const simpleTransferFlowCompletedEvents: SimpleTransferFlowCompletedLog[] = - EventDecoder.decodeSimpleTransferFlowCompletedEvents( - transactionReceipt, - contractAddress, - ); - if (simpleTransferFlowCompletedEvents.length > 0) { - events.push(...simpleTransferFlowCompletedEvents); - } - } - - return events; -} +export const formatSimpleTransferFlowCompletedEvent = ( + event: SimpleTransferFlowCompletedLog, + finalised: boolean, + blockTimestamp: Date, + chainId: number, +): Partial => ({ + blockNumber: event.blockNumber, + logIndex: event.logIndex, + transactionHash: event.transactionHash, + transactionIndex: event.transactionIndex, + blockTimestamp: blockTimestamp, + chainId: chainId.toString(), + quoteNonce: event.args.quoteNonce, + finalRecipient: event.args.finalRecipient, + finalToken: event.args.finalToken.toString(), + evmAmountIn: event.args.evmAmountIn.toString(), + bridgingFeesIncurred: event.args.bridgingFeesIncurred.toString(), + evmAmountSponsored: event.args.evmAmountSponsored.toString(), + finalised, +}); /** - * Formats and saves `SimpleTransferFlowCompleted` events to the database. - * This function maps the raw event data to the database entity format, marks them as finalized if they are within the finalized block range, - * and then saves them to the database in batches. - * - * @param repository The repository for database operations, specifically for saving blockchain events. - * @param simpleTransferFlowCompletedEvents An array of `SimpleTransferFlowCompletedLog` events to be processed. - * @param lastFinalisedBlock The last block number that is considered finalized. - * @param chainId The ID of the chain where these events were emitted. - * @param blockDates A record mapping block numbers to their corresponding `Date` objects. - * @param chunkSize The number of events to save in a single batch. Defaults to 100. - * @returns A promise that resolves to an array of `SaveQueryResult` for the saved events. + * @constant formatFallbackHyperEVMFlowCompletedEvent + * Formats a `FallbackHyperEVMFlowCompletedLog` event into a partial `FallbackHyperEVMFlowCompleted` entity. + * @param event The `FallbackHyperEVMFlowCompletedLog` event to format. + * @param finalised A boolean indicating if the event is finalized. + * @param blockTimestamp The timestamp of the block where the event was emitted. + * @param chainId The ID of the chain where the event was emitted. + * @returns A partial `FallbackHyperEVMFlowCompleted` entity. */ -export async function formatAndSaveSimpleTransferFlowCompletedEvents( - repository: BlockchainEventRepository, - simpleTransferFlowCompletedEvents: SimpleTransferFlowCompletedLog[], - lastFinalisedBlock: number, +export const formatFallbackHyperEVMFlowCompletedEvent = ( + event: FallbackHyperEVMFlowCompletedLog, + finalised: boolean, + blockTimestamp: Date, chainId: number, - blockDates: Record, - chunkSize = 100, -) { - const formattedEvents: Partial[] = - simpleTransferFlowCompletedEvents.map((event) => { - return { - blockNumber: event.blockNumber, - logIndex: event.logIndex, - transactionHash: event.transactionHash, - transactionIndex: event.transactionIndex, - blockTimestamp: blockDates[event.blockNumber]!, - chainId: chainId.toString(), - quoteNonce: event.args.quoteNonce, - finalRecipient: event.args.finalRecipient, - finalToken: event.args.finalToken.toString(), - evmAmountIn: event.args.evmAmountIn.toString(), - bridgingFeesIncurred: event.args.bridgingFeesIncurred.toString(), - evmAmountSponsored: event.args.evmAmountSponsored.toString(), - finalised: event.blockNumber <= lastFinalisedBlock, - }; - }); - - const chunkedEvents = across.utils.chunk(formattedEvents, chunkSize); - const savedEvents = await Promise.all( - chunkedEvents.map((eventsChunk) => - repository.saveAndHandleFinalisationBatch( - entities.SimpleTransferFlowCompleted, - eventsChunk, - ["chainId", "blockNumber", "transactionHash", "logIndex"], - [], - ), - ), - ); - const result = savedEvents.flat(); - return result; -} +): Partial => ({ + blockNumber: event.blockNumber, + logIndex: event.logIndex, + transactionHash: event.transactionHash, + transactionIndex: event.transactionIndex, + blockTimestamp: blockTimestamp, + chainId: chainId.toString(), + quoteNonce: event.args.quoteNonce, + finalRecipient: event.args.finalRecipient, + finalToken: event.args.finalToken.toString(), + evmAmountIn: event.args.evmAmountIn.toString(), + bridgingFeesIncurred: event.args.bridgingFeesIncurred.toString(), + evmAmountSponsored: event.args.evmAmountSponsored.toString(), + finalised, +}); diff --git a/packages/indexer/src/data-indexing/tests/OFTIndexerDataHandler.integration.test.ts b/packages/indexer/src/data-indexing/tests/OFTIndexerDataHandler.integration.test.ts index cb50353f..74b887e3 100644 --- a/packages/indexer/src/data-indexing/tests/OFTIndexerDataHandler.integration.test.ts +++ b/packages/indexer/src/data-indexing/tests/OFTIndexerDataHandler.integration.test.ts @@ -110,4 +110,41 @@ describe("OFTIndexerDataHandler", () => { expect(savedEvent!.bridgingFeesIncurred.toString()).to.equal("0"); expect(savedEvent!.evmAmountSponsored.toString()).to.equal("0"); }).timeout(20000); + + it("should process a block range and store FallbackHyperEVMFlowCompleted event for OFT", async () => { + const transactionHash = + "0x05ccdbd44e8ffbed8f057762f40dee73fb218049347705d88f839dfe3c368c52"; + const blockNumber = 17917691; + const blockRange: BlockRange = { + from: blockNumber, + to: blockNumber, + }; + setupTestForChainId(CHAIN_IDs.HYPEREVM); + // We need to stub the filterTransactionsFromSwapApi method to avoid filtering out our test transaction + sinon.stub(handler as any, "filterTransactionsFromSwapApi").resolvesArg(1); + await handler.processBlockRange(blockRange, blockNumber - 1); + + const fallbackHyperEVMFlowCompletedRepo = dataSource.getRepository( + entities.FallbackHyperEVMFlowCompleted, + ); + const savedEvent = await fallbackHyperEVMFlowCompletedRepo.findOne({ + where: { transactionHash: transactionHash }, + }); + + expect(savedEvent).to.exist; + expect(savedEvent!.transactionHash).to.equal(transactionHash); + expect(savedEvent!.blockNumber).to.equal(blockNumber); + expect(savedEvent!.quoteNonce).to.equal( + "0x0000000000000000000000000000000000000000000000000000000069041bd4", + ); + expect(savedEvent!.finalRecipient).to.equal( + "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", + ); + expect(savedEvent!.finalToken).to.equal( + "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", + ); + expect(savedEvent!.evmAmountIn.toString()).to.equal("1005000"); + expect(savedEvent!.bridgingFeesIncurred.toString()).to.equal("0"); + expect(savedEvent!.evmAmountSponsored.toString()).to.equal("0"); + }).timeout(20000); }); diff --git a/packages/indexer/src/database/OftRepository.ts b/packages/indexer/src/database/OftRepository.ts index b1948999..7c4b5a5b 100644 --- a/packages/indexer/src/database/OftRepository.ts +++ b/packages/indexer/src/database/OftRepository.ts @@ -29,6 +29,7 @@ export class OftRepository extends dbUtils.BlockchainEventRepository { oftSentEvents, oftReceivedEvents, simpleTransferFlowCompletedEvents, + fallbackHyperEVMFlowCompletedEvents, ] = await Promise.all([ this.deleteUnfinalisedEvents( chainId, @@ -48,12 +49,19 @@ export class OftRepository extends dbUtils.BlockchainEventRepository { lastFinalisedBlock, entities.SimpleTransferFlowCompleted, ), + this.deleteUnfinalisedEvents( + chainId, + chainIdColumn, + lastFinalisedBlock, + entities.FallbackHyperEVMFlowCompleted, + ), ]); return { oftSentEvents, oftReceivedEvents, simpleTransferFlowCompletedEvents, + fallbackHyperEVMFlowCompletedEvents, }; } diff --git a/packages/indexer/src/web3/EventDecoder.ts b/packages/indexer/src/web3/EventDecoder.ts index c84684d8..b400c0a1 100644 --- a/packages/indexer/src/web3/EventDecoder.ts +++ b/packages/indexer/src/web3/EventDecoder.ts @@ -102,7 +102,7 @@ export class EventDecoder { const eventTopic = "0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036"; const eventAbi = ["event MessageSent (bytes message)"]; - let events: MessageSentLog[] = this.decodeTransactionReceiptLogs( + let events: MessageSentLog[] = EventDecoder.decodeTransactionReceiptLogs( receipt, eventTopic, eventAbi, @@ -123,11 +123,8 @@ export class EventDecoder { const eventAbi = [ "event MintAndWithdraw(address indexed mintRecipient, uint256 amount, address indexed mintToken, uint256 feeCollected)", ]; - let events: MintAndWithdrawLog[] = this.decodeTransactionReceiptLogs( - receipt, - eventTopic, - eventAbi, - ); + let events: MintAndWithdrawLog[] = + EventDecoder.decodeTransactionReceiptLogs(receipt, eventTopic, eventAbi); if (contractAddress) { events = events.filter((event) => event.address === contractAddress); } @@ -148,7 +145,7 @@ export class EventDecoder { ]; let events: SponsoredDepositForBurnLog[] = - this.decodeTransactionReceiptLogs(receipt, eventTopic, eventAbi); + EventDecoder.decodeTransactionReceiptLogs(receipt, eventTopic, eventAbi); if (contractAddress) { events = events.filter((event) => event.address === contractAddress); } @@ -167,11 +164,8 @@ export class EventDecoder { "event SponsoredOFTSend(bytes32 indexed quoteNonce, address indexed originSender, bytes32 indexed finalRecipient, bytes32 destinationHandler, uint256 quoteDeadline, uint256 maxBpsToSponsor, uint256 maxUserSlippageBps, bytes32 finalToken, bytes sig)", ]; - let events: SponsoredOFTSendLog[] = this.decodeTransactionReceiptLogs( - receipt, - eventTopic, - eventAbi, - ); + let events: SponsoredOFTSendLog[] = + EventDecoder.decodeTransactionReceiptLogs(receipt, eventTopic, eventAbi); if (contractAddress) { events = events.filter((event) => event.address === contractAddress); } @@ -200,7 +194,7 @@ export class EventDecoder { "event SimpleTransferFlowCompleted(bytes32 indexed quoteNonce,address indexed finalRecipient,address indexed finalToken,uint256 evmAmountIn,uint256 bridgingFeesIncurred,uint256 evmAmountSponsored)", ]; let events: SimpleTransferFlowCompletedLog[] = - this.decodeTransactionReceiptLogs(receipt, eventTopic, eventAbi); + EventDecoder.decodeTransactionReceiptLogs(receipt, eventTopic, eventAbi); if (contractAddress) { events = events.filter((event) => event.address === contractAddress); } @@ -217,7 +211,7 @@ export class EventDecoder { const eventAbi = [ "event ArbitraryActionsExecuted(bytes32 indexed quoteNonce, address indexed initialToken, uint256 initialAmount, address indexed finalToken, uint256 finalAmount)", ]; - let events: any[] = this.decodeTransactionReceiptLogs( + let events: any[] = EventDecoder.decodeTransactionReceiptLogs( receipt, eventTopic, eventAbi, @@ -238,7 +232,7 @@ export class EventDecoder { const eventAbi = [ "event FallbackHyperEVMFlowCompleted(bytes32 indexed quoteNonce, address indexed finalRecipient, address indexed finalToken, uint256 evmAmountIn, uint256 bridgingFeesIncurred, uint256 evmAmountSponsored)", ]; - let events: any[] = this.decodeTransactionReceiptLogs( + let events: any[] = EventDecoder.decodeTransactionReceiptLogs( receipt, eventTopic, eventAbi,