Skip to content

feat: index Soroban contract events into Postgres within 2s - #22

Open
testersweb wants to merge 1 commit into
LightForgeHub:mainfrom
testersweb:feat/stellar-rpc-event-indexer
Open

feat: index Soroban contract events into Postgres within 2s#22
testersweb wants to merge 1 commit into
LightForgeHub:mainfrom
testersweb:feat/stellar-rpc-event-indexer

Conversation

@testersweb

Copy link
Copy Markdown
Contributor

Summary

Index Stellar/Soroban contract events into PostgreSQL so expert earnings and session history can be queried off-chain as soon as they are visible to RPC.

The indexer polls getEvents filtered by contract ID, decodes Soroban XDR topics/data (including PaymentStreamed), and writes Transaction / session rows via Prisma in the same poll tick. Default poll interval is 1000ms, which satisfies the 2 second freshness acceptance criterion.

What changed

  • backend/src/indexer.ts — 1s poll loop (invalid/<100ms intervals fall back to 1000ms); logs combined ingest + recovery processing counts.
  • backend/src/sorobanIndexer.tsgetEvents with contract ID filters, cursor pagination, exact topic classification, XDR decode, ledger/cursor persistence.
  • backend/src/eventListener.tsPAYMENT_STREAMED handler: insert PAYMENT_RELEASED transaction, keep session ACTIVE; ledger time from ledgerClosedAt; ingest handles unique-constraint races (P2002).
  • backend/prisma/schema.prisma + migration — IndexerState.lastCursor for RPC pagination.
  • docker-compose.yml / backend/.env.exampleINDEXER_INTERVAL_MS=1000, SOROBAN_RPC_URL, ESCROW_CONTRACT_ID.
  • Tests for decode, exact classification, contract filters, cursor resume, and PaymentStreamed → DB.

Key implementation details

  • getEvents uses either startLedger or cursor, matching Stellar RPC / @stellar/stellar-sdk v16 (the two must not be mixed).
  • Contract IDs come from ESCROW_CONTRACT_ID, DISPUTE_CONTRACT_ID, SOROBAN_CONTRACT_IDS, or constructor options. Filters respect the 5 IDs per filter / 5 filters limits.
  • Topic names are classified with an exact normalized map (PaymentStreamedPAYMENT_STREAMED). Generic "payment" is not treated as session completion.
  • PaymentStreamed records earnings without setting session status to COMPLETED.
  • Cursor is stored on IndexerState. lastLedger is only fast-forwarded to RPC latestLedger on an empty page so truncated scans are not skipped.
  • Event ingest keys EventLog / Transaction by the Stellar txHash.
  • If no contract IDs are configured, the indexer warns and matches all contract events.

Test plan

  • cd backend && npx prisma generate && npx tsc --noEmit
  • cd backend && npm test
  • Confirm PaymentStreamed decode does not classify as SESSION_COMPLETED
  • Confirm poll writes a Transaction (PAYMENT_RELEASED) and session stays ACTIVE
  • Confirm getEvents is called with filters[].contractIds
  • Confirm a saved cursor is reused (no startLedger on the next request)
  • Apply migration: npx prisma migrate deploy (adds IndexerState.lastCursor)
  • Set ESCROW_CONTRACT_ID (and SOROBAN_RPC_URL) before running the indexer in a real environment
  • Smoke: emit PaymentStreamed on testnet and confirm the row appears in Postgres within ~2s of RPC visibility

Testing / verification performed

  • npx prisma generate — success
  • npx tsc --noEmit and npx tsc --noEmit -p tsconfig.test.json — success
  • npm test128 passed (coverage thresholds met)

Notes / considerations

  • The 2s SLA is RPC-visible → Postgres. Stellar ledger close time is separate.
  • EventLog.txHash is unique per Stellar transaction. If one tx emits multiple contract events, only the first ingested row is kept.
  • Production should set ESCROW_CONTRACT_ID; an empty value logs a warning and indexes all contract events.

Related Issue

Closes #19

Poll Stellar RPC getEvents by contract ID on a 1s interval, decode
PaymentStreamed XDR payloads, and persist transactions without
completing the session so expert earnings can be queried off-chain.
@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fddec0aa-2255-46d6-b05e-c10543791795


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Soroban Event Stream Consumer for Real-Time Payment Indexing

1 participant