diff --git a/src/stellar/soroban-event-indexer.service.ts b/src/stellar/soroban-event-indexer.service.ts index 38ecf2b2..17f6be5f 100644 --- a/src/stellar/soroban-event-indexer.service.ts +++ b/src/stellar/soroban-event-indexer.service.ts @@ -62,42 +62,44 @@ export class SorobanEventIndexer { this.polling = true; try { await this.cronJobService.run('soroban-event-indexer', async () => { - const lastLedger = (await this.cache.get(CURSOR_KEY)) ?? 0; - let startLedger = lastLedger > 0 ? lastLedger + 1 : undefined; - let latestSeenLedger = lastLedger; - let pageCursor: string | undefined; - let eventCount = 0; - - while (true) { - const { events, latestLedger } = await this.getEvents(startLedger, pageCursor); - latestSeenLedger = Math.max(latestSeenLedger, latestLedger); - - if (!events.length) break; - - for (const event of events) { - try { - const dto = this.parseEvent(event); - if (!dto) continue; - await this.dispatch(dto); - eventCount++; - latestSeenLedger = Math.max(latestSeenLedger, event.ledger ?? latestSeenLedger); - } catch (error) { - await this.enqueueDeadLetter(event, error as Error); + const lastLedger = (await this.cache.get(CURSOR_KEY)) ?? 0; + let startLedger = lastLedger > 0 ? lastLedger + 1 : undefined; + let cursorLedger = lastLedger; + let pageCursor: string | undefined; + let eventCount = 0; + + while (true) { + const { events, latestLedger } = await this.getEvents(startLedger, pageCursor); + + if (!events.length) { + cursorLedger = Math.max(cursorLedger, latestLedger); + break; } - } - if (events.length < PAGE_LIMIT) break; - pageCursor = events[events.length - 1]?.pagingToken; - startLedger = undefined; - } + for (const event of events) { + try { + const dto = this.parseEvent(event); + if (!dto) continue; + await this.dispatch(dto); + eventCount++; + cursorLedger = Math.max(cursorLedger, event.ledger ?? cursorLedger); + } catch (error) { + await this.enqueueDeadLetter(event, error as Error); + } + } - if (latestSeenLedger > lastLedger) { - await this.cache.set(CURSOR_KEY, latestSeenLedger, { - ttlSeconds: CURSOR_TTL_SECONDS, - }); - } + if (events.length < PAGE_LIMIT) break; + pageCursor = events[events.length - 1]?.pagingToken; + startLedger = undefined; + } + + if (cursorLedger > lastLedger) { + await this.cache.set(CURSOR_KEY, cursorLedger, { + ttlSeconds: CURSOR_TTL_SECONDS, + }); + } - return eventCount; + return eventCount; }); } finally { this.polling = false; diff --git a/src/stellar/stellar-monitor.service.ts b/src/stellar/stellar-monitor.service.ts index ec4c3006..26948090 100644 --- a/src/stellar/stellar-monitor.service.ts +++ b/src/stellar/stellar-monitor.service.ts @@ -228,14 +228,25 @@ export class StellarMonitorService implements OnModuleInit { .getMany(); for (const payment of expired) { - await this.stellar.invokeContract('expire', [payment.id]); - payment.status = PaymentStatus.EXPIRED; - await this.paymentsRepo.save(payment); + try { + await this.stellar.invokeContract('expire', [payment.id]); + payment.status = PaymentStatus.EXPIRED; + await this.paymentsRepo.save(payment); - await this.webhooks.dispatch(payment.merchantId, 'payment.expired', { - paymentId: payment.id, - reference: payment.reference, - }); + await this.webhooks.dispatch(payment.merchantId, 'payment.expired', { + paymentId: payment.id, + reference: payment.reference, + }); + } catch (err) { + this.logger.error(`Failed to expire payment ${payment.id}: ${err.message}`); + await this.adminAlerts.raise({ + type: AdminAlertType.STELLAR_MONITOR, + dedupeKey: `stellar-monitor.expire:${payment.id}`, + message: `Failed to expire payment ${payment.reference}: ${err.message}`, + metadata: { paymentId: payment.id }, + thresholdValue: 1, + }); + } } }