diff --git a/indexer/common/src/handlers/index.ts b/indexer/common/src/handlers/index.ts index 5d7d50a..0b767de 100644 --- a/indexer/common/src/handlers/index.ts +++ b/indexer/common/src/handlers/index.ts @@ -1,7 +1,15 @@ +import { Event } from '../rpc'; + +export interface EventHandler { + name: string; + supports(event: Event): boolean; + handle(event: Event): Promise; +} + export { HandlerRegistry } from "./registry.js"; export type { EventHandler, HandlerFilter, HandlerResult, SorobanEventInput, -} from "./types.js"; +} from "./types.js"; \ No newline at end of file diff --git a/indexer/common/src/poller/README.md b/indexer/common/src/poller/README.md new file mode 100644 index 0000000..ed91eb9 --- /dev/null +++ b/indexer/common/src/poller/README.md @@ -0,0 +1,6 @@ +# Event Poller + +The Event Poller is responsible for continuously fetching Soroban events from the Stellar network and dispatching them to registered handlers. + +## Architecture + diff --git a/indexer/common/src/poller/index.ts b/indexer/common/src/poller/index.ts index ae30e14..c5fc01e 100644 --- a/indexer/common/src/poller/index.ts +++ b/indexer/common/src/poller/index.ts @@ -48,7 +48,6 @@ export class SorobanPoller { * Determines if an error is transient and should be retried. */ private isTransientError(error: unknown): boolean { - // Basic transient error checking - expand based on specific RPC error formats const message = error instanceof Error ? error.message : String(error); return ( message.includes("timeout") || @@ -72,21 +71,18 @@ export class SorobanPoller { updateCursor: (ledger: number) => Promise, ): Promise { try { - // 1. Fetch events with retry logic for RPC const events = await this.withRetry(() => fetchEvents(startLedger, endLedger)); - // 2. Process events sequentially for (const event of events) { - // If a handler fails, it throws, skipping the updateCursor step await processEvent(event); } - // 3. Update cursor ONLY if all events in the range succeeded await updateCursor(endLedger); return { success: true, lastProcessedLedger: endLedger }; } catch (error) { - // Return the error to surface it. Cursor is intentionally not advanced. return { success: false, error: error instanceof Error ? error : new Error(String(error)) }; } } } + +export * from './EventPoller'; \ No newline at end of file diff --git a/indexer/common/src/repositories/index.ts b/indexer/common/src/repositories/index.ts new file mode 100644 index 0000000..754694d --- /dev/null +++ b/indexer/common/src/repositories/index.ts @@ -0,0 +1,4 @@ +export interface CursorRepository { + getCursor(): Promise; + saveCursor(ledger: number): Promise; +} diff --git a/indexer/common/src/rpc/index.ts b/indexer/common/src/rpc/index.ts new file mode 100644 index 0000000..e4a3cfb --- /dev/null +++ b/indexer/common/src/rpc/index.ts @@ -0,0 +1,20 @@ +export interface Event { + id: string; + ledger: number; + contractId: string; + topics: string[]; + data: any; + timestamp: Date; +} + +export interface GetEventsParams { + startLedger: number; + limit?: number; + contractId?: string; + topics?: string[]; +} + +export interface SorobanRpc { + getEvents(params: GetEventsParams): Promise; + getLatestLedger(): Promise; +}