Skip to content

feat(api): add WebSocket transfer subscription endpoint#42

Merged
Miracle656 merged 2 commits intoMiracle656:mainfrom
0xDeon:feat/websocket-transfer-subscriptions
Apr 23, 2026
Merged

feat(api): add WebSocket transfer subscription endpoint#42
Miracle656 merged 2 commits intoMiracle656:mainfrom
0xDeon:feat/websocket-transfer-subscriptions

Conversation

@0xDeon
Copy link
Copy Markdown
Contributor

@0xDeon 0xDeon commented Apr 22, 2026

Summary

Closes #33

Real-time push notifications for token transfers via WebSocket. Clients subscribe to an address and receive a JSON payload for every new transfer that is indexed where that address appears as sender or recipient.

What was implemented

New files

File Purpose
src/events.ts Singleton EventEmitter that publishes transfer:new events. setMaxListeners(0) prevents Node.js warnings with many concurrent subscribers.
src/ws.ts WebSocket server using noServer: true + manual upgrade event handling. Parses address from URL, registers a per-socket handler, forwards matching events, removes handler on close/error to prevent memory leaks.

Modified files

File Change
src/indexer.ts Calls emitTransfer(record) for every record after a successful upsertTransfers batch.
src/index.ts Replaces app.listen() with http.createServer(app) so the HTTP server can handle WebSocket upgrade requests on the same port. Calls attachWebSocketServer(server).

Endpoint

ws://host/subscribe/:address
  • :address — any Stellar address (sender or recipient)
  • Server pushes JSON on every new matching transfer; payload is identical to the REST /transfers response shape, including displayAmount
  • Non-matching /subscribe/... paths receive an HTTP 404 and the socket is destroyed
  • Connecting to an address with no incoming/outgoing traffic works fine — client simply waits until a matching event is indexed

Payload shape (example)

{
  "contractId": "CABC...",
  "eventType": "transfer",
  "fromAddress": "GBOB...",
  "toAddress": "GALICE...",
  "amount": "10000000",
  "displayAmount": "1.0000000",
  "ledger": 12345,
  "ledgerClosedAt": "2025-01-01T00:00:00.000Z",
  "txHash": "abc123",
  "eventId": "evt-001"
}

How it was tested

Build verification:

npm run build   # → zero TypeScript errors

Manual test steps:

  1. Start the server: npm run dev
  2. Open a WebSocket client (e.g. wscat):
    wscat -c ws://localhost:3000/subscribe/GALICE...
  3. Trigger an indexed transfer to/from that address
  4. Observe JSON payload pushed in real-time to the client
  5. Disconnect the client and confirm no memory leak (listener count stays bounded)

Memory safety check:
Connect and disconnect many clients rapidly — the listener count on transferEmitter returns to 0 after all sockets close because transferEmitter.off(handler) is called in both the close and error handlers.

Closes Miracle656#33

- src/events.ts: singleton EventEmitter that publishes `transfer:new` events;
  setMaxListeners(0) prevents false positives with many concurrent subscribers
- src/ws.ts: WebSocket server attached via noServer + manual upgrade handler;
  endpoint is ws://host/subscribe/:address; only events where the watched
  address is sender or recipient are forwarded; each handler is cleaned up on
  socket close/error to prevent memory leaks; payload mirrors REST shape and
  includes displayAmount
- src/indexer.ts: emits one event per record after a successful DB upsert batch
- src/index.ts: replaces app.listen() with http.createServer() so the same
  port handles both REST and WebSocket upgrade requests
@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Apr 22, 2026

@0xDeon Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@0xDeon 0xDeon changed the title feat(api): add WebSocket transfer subscription endpoint (#33) feat(api): add WebSocket transfer subscription endpoint Apr 22, 2026
@Miracle656 Miracle656 merged commit cf6630b into Miracle656:main Apr 23, 2026
1 of 2 checks passed
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.

Add WebSocket transfer subscriptions

2 participants