Skip to content

[Feature] Build Contract Event Listener Service for Real-Time On-Chain Data Sync #27

Description

@KarenZita01

Description

The EquipChain platform relies on smart contracts deployed on the Stellar/Soroban network to manage meter readings, billing, and protocol configuration. To keep the backend state synchronized with on-chain data, a contract event listener service is required that continuously monitors the Soroban blockchain for events emitted by EquipChain smart contracts and processes them in real-time.

The event listener must support: Event Polling — periodically query the Soroban RPC endpoint for contract events using getEvents() with filters for the EquipChain contract ID, event types, and a starting ledger sequence; Event Processing — for each event received, parse the event data (values are serialized as SCVals — Soroban Contract Values), transform to JSON, and dispatch to the appropriate handler based on event type; Cursor Management — track the last processed ledger sequence and event cursor so the listener can resume from where it left off after restarts; Error Handling — handle RPC errors gracefully, implement exponential backoff for polling failures, and alert on prolonged sync failures; Performance — support multiple polling intervals (real-time: every 5 seconds, near-real-time: every 30 seconds) depending on event criticality.

Event types to handle: meter_reading_submitted (update local meter reading store), config_updated (update local protocol config cache), user_role_changed (update user permissions), billing_cycle_completed (trigger report generation), system_alert (dispatch to WebSocket broadcast and admin notification).

The listener should run as a background service within the Node.js process (or as a separate process for scalability), updating local state via the repository layer (Issue #22).

Technical Context & Impact

Step-by-Step Implementation Guide

  1. Create Event Handlers: Write src/services/eventHandlers.js with handler functions for each event type: handleMeterReadingSubmitted(event), handleConfigUpdated(event), handleUserRoleChanged(event), handleBillingCycleCompleted(event), handleSystemAlert(event). Each handler parses the SCVal payload, transforms to JSON, and updates the corresponding repository.
  2. Create Event Listener Service: Write src/services/eventListener.js exporting class EventListener. Constructor accepts Soroban server (Issue [Feature] Integrate Soroban SDK for Direct Blockchain Data Access and Contract Interaction #3), contract ID, and polling interval. Implement start() which begins a polling loop using setInterval. In each iteration: call server.getEvents() with filters, process new events, update cursor. Implement stop() to cleanly shut down the loop. Store cursor in Redis (Issue [Performance] Add Redis Caching Layer for Frequent Blockchain Data Queries #12) for persistence across restarts.
  3. Implement Cursor Management: On each poll, retrieve the last processed cursor from Redis. Use it as the cursor parameter in getEvents(). After processing, save the new cursor. On first start with no cursor, start from the current ledger (or a configurable lookback period).
  4. Integrate with Application: In src/index.js, after the server starts, instantiate and start the event listener. Register a graceful shutdown handler that stops the listener on SIGTERM/SIGINT. Log listener status changes (started, stopped, sync progress).
  5. Add Health Check Integration: Update the health endpoint (GET /api/health) to include event listener status: lastPolledLedger, lastEventTimestamp, eventsProcessed, status (syncing, synced, error).
  6. Write Tests: Create tests/unit/eventListener.test.js with mocked Soroban getEvents() responses. Test: event polling, cursor advancement, handler dispatch, error recovery (RPC failure → retry). Create tests/unit/eventHandlers.test.js testing SCVal parsing and repository updates.

Verification & Testing Steps

  1. Start the server with a connection to Soroban testnet (or mock). Verify the event listener starts automatically and logs: "Event listener started, polling every 5s".
  2. Submit a transaction on the Soroban testnet that emits a known event type — verify the listener picks it up within the polling interval and the handler processes it (check logs for "Processing event: meter_reading_submitted").
  3. Simulate an RPC failure by temporarily stopping the Soroban mock — verify the listener logs a warning, retries with backoff, and recovers when the RPC endpoint becomes available.
  4. Restart the server — verify the listener resumes from the last saved cursor (no duplicate processing of previously handled events).
  5. Check the health endpoint — verify it shows the event listener status with correct eventsProcessed count and lastPolledLedger.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions