From 85008cc8b667e7def6f24772fca6f832bc3cf6d8 Mon Sep 17 00:00:00 2001 From: Hayden Briese Date: Fri, 16 Aug 2024 16:49:34 +1000 Subject: [PATCH] fix(api): correctly attach events to confirmed result --- api/src/feat/events/events.service.ts | 35 ++++++++++++++----- .../feat/system-txs/confirmations.worker.ts | 29 ++++++++------- api/src/feat/transfers/transfers.events.ts | 2 -- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/api/src/feat/events/events.service.ts b/api/src/feat/events/events.service.ts index 8aef06fc3..16ec65731 100644 --- a/api/src/feat/events/events.service.ts +++ b/api/src/feat/events/events.service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@nestjs/common'; import { Chain } from 'chains'; -import { Hex, UUID } from 'lib'; +import { asUUID, Hex, UUID } from 'lib'; import { AbiEvent, encodeEventTopics, parseEventLogs, Log as ViemLog } from 'viem'; import { Receipt } from '../system-txs/confirmations.worker'; import { NetworksService } from '~/core/networks'; +import { DatabaseService } from '~/core/database'; +import e from '~/edgeql-js'; export type Log< TAbiEvent extends AbiEvent | undefined = undefined, @@ -27,7 +29,6 @@ export interface ProcessOptimisticParams { export interface ProcessConfirmedParams { logs: Log[]; chain: Chain; - result?: UUID; receipt?: Receipt; } @@ -56,7 +57,10 @@ export class EventsService { optimisticAbi: AbiEvent[] = []; confirmedAbi: AbiEvent[] = []; - constructor(private networks: NetworksService) {} + constructor( + private db: DatabaseService, + private networks: NetworksService, + ) {} onOptimistic( event: TAbiEvent, @@ -105,15 +109,30 @@ export class EventsService { ); } - async processConfirmed({ chain, logs, result, receipt }: ProcessConfirmedParams) { + async processConfirmed({ chain, logs, receipt }: ProcessConfirmedParams) { const parsedLogs = parseEventLogs({ logs, abi: this.confirmedAbi, strict: true }); + const getResult = async () => { + const id = + receipt && + (await this.db.queryWith2( + { hash: e.Bytes32 }, + { hash: receipt.transactionHash }, + ({ hash }) => + e.select(e.SystemTx, () => ({ filter_single: { hash }, result: true })).result.id, + )); + + return (id && asUUID(id)) || null; + }; + const network = this.networks.get(chain); - const blocks = await Promise.all( - [...new Set(logs.map((log) => log.blockNumber))].map((blockNumber) => + const uniqueBlockNumbers = [...new Set(logs.map((log) => log.blockNumber))]; + const [result, ...blocks] = await Promise.all([ + getResult(), + ...uniqueBlockNumbers.map((blockNumber) => network.getBlock({ blockNumber, includeTransactions: false }), ), - ); + ]); await Promise.all( parsedLogs @@ -129,7 +148,7 @@ export class EventsService { timestamp, logIndex: log.logIndex, log, - result: result ?? null, + result, receipt, }), ); diff --git a/api/src/feat/system-txs/confirmations.worker.ts b/api/src/feat/system-txs/confirmations.worker.ts index 838b9b0b7..8faada6ca 100644 --- a/api/src/feat/system-txs/confirmations.worker.ts +++ b/api/src/feat/system-txs/confirmations.worker.ts @@ -40,7 +40,7 @@ export class ConfirmationsWorker extends Worker { async process(job: TypedJob) { const { chain } = job.data; - const transaction = isHex(job.data.transaction) + const hash = isHex(job.data.transaction) ? job.data.transaction : await (async () => { const v = @@ -49,24 +49,23 @@ export class ConfirmationsWorker extends Worker { return isHex(v) ? v : undefined; })(); - if (!transaction) return; + if (!hash) return; - await job.updateData({ ...job.data, transaction }); + await job.updateData({ ...job.data, transaction: hash }); - const network = this.networks.get(chain); - const receipt = await network.waitForTransactionReceipt({ - hash: transaction, + const receipt = await this.networks.get(chain).waitForTransactionReceipt({ + hash, timeout: 60_000, - pollingInterval: 1_000, + pollingInterval: 500, }); - await Promise.all([ - ...this.listeners.map((listener) => listener({ chain, receipt })), - this.events.processConfirmed({ - chain, - logs: receipt.logs as unknown as Log[], - receipt, - }), - ]); + // Execute listeners prior to events to ensure result is available + await Promise.all(this.listeners.map((listener) => listener({ chain, receipt }))); + + await this.events.processConfirmed({ + chain, + logs: receipt.logs as unknown as Log[], + receipt, + }); } } diff --git a/api/src/feat/transfers/transfers.events.ts b/api/src/feat/transfers/transfers.events.ts index 9c83f0dc4..0d4ec650a 100644 --- a/api/src/feat/transfers/transfers.events.ts +++ b/api/src/feat/transfers/transfers.events.ts @@ -5,7 +5,6 @@ import { EventsService, OptimisticEvent, ConfirmedEvent } from '../events/events import { and, DatabaseService } from '~/core/database'; import e from '~/edgeql-js'; import { selectAccount } from '../accounts/accounts.util'; -import { NetworksService } from '~/core/networks/networks.service'; import { uuid } from 'edgedb/dist/codecs/ifaces'; import { EventPayload, PubsubService } from '~/core/pubsub/pubsub.service'; import { getAbiItem } from 'viem'; @@ -34,7 +33,6 @@ export class TransfersEvents { constructor( private db: DatabaseService, private events: EventsService, - private networks: NetworksService, private pubsub: PubsubService, private accountsCache: AccountsCacheService, private expo: ExpoService,