This implementation adds comprehensive event history tracking for stream lifecycle actions (create, claim, cancel, start time updates).
Added stream_events table:
id: Auto-incrementing primary keystream_id: Foreign key to streams tableevent_type: Type of event (created, claimed, canceled, start_time_updated)timestamp: Unix timestamp when event occurredactor: Address of the account that triggered the eventamount: Amount involved (for created/claimed events)metadata: JSON metadata for additional context
Indexes on stream_id and timestamp for efficient queries.
Core functions:
recordEvent(): Store a new event in the databasegetStreamHistory(): Retrieve all events for a specific streamgetAllEvents(): Retrieve recent events across all streams
Background worker that:
- Polls Soroban RPC for contract events every 10 seconds
- Processes StreamCreated, StreamClaimed, and StreamCanceled events
- Stores events in the database with proper timestamps and metadata
- Tracks last processed ledger to avoid duplicates
- Handles errors gracefully with logging
Modified to record events when:
- Stream is created (with sender, amount, and metadata)
- Stream is canceled (with actor)
- Start time is updated (with old/new values in metadata)
New endpoint: GET /api/streams/:id/history
- Returns ordered list of lifecycle events for a stream
- Events sorted by timestamp ascending
- No authentication required (read-only)
Indexer initialization:
- Starts automatically on server startup if CONTRACT_ID is configured
- Polls every 10 seconds for new events
- Logs warnings if configuration is missing
Added:
StreamEventinterface matching backend event structuregetStreamHistory()function to fetch events for a stream
React component that:
- Displays stream events in chronological order
- Shows event icons (🎉 created, 💰 claimed, ❌ canceled, ⏰ updated)
- Formats timestamps in local time
- Handles loading and error states
- Auto-refreshes when stream ID changes
The indexer starts automatically when the server starts:
await initSoroban();
await syncStreams();
// Initialize and start event indexer
const rpcUrl = process.env.RPC_URL || "https://soroban-testnet.stellar.org:443";
const contractId = process.env.CONTRACT_ID;
const networkPassphrase = process.env.NETWORK_PASSPHRASE;
if (contractId) {
initIndexer(rpcUrl, contractId, networkPassphrase);
startIndexer(10000); // Poll every 10 seconds
}Use the StreamTimeline component:
import { StreamTimeline } from "./components/StreamTimeline";
function StreamDetails({ streamId }: { streamId: string }) {
return (
<div>
{/* Other stream details */}
<StreamTimeline streamId={streamId} />
</div>
);
}Or fetch events directly:
import { getStreamHistory } from "./services/api";
const events = await getStreamHistory("123");
console.log(events);- Triggered when a new stream is created
- Includes: sender, total amount, recipient, asset code, duration
- Triggered when tokens are claimed from a stream
- Includes: recipient, claimed amount
- Triggered when a stream is canceled
- Includes: sender who canceled
- Triggered when start time is modified (scheduled streams only)
- Includes: sender, old and new start times in metadata
- Events are stored in SQLite database (
data/streams.db) - Database uses WAL mode for better concurrency
- Events persist across backend restarts
- Indexer resumes from last processed ledger
To test the implementation:
- Start the backend:
cd backend
npm install
npm run dev- Create a stream via API
- Check the history endpoint:
curl http://localhost:3001/api/streams/1/history- Cancel or claim from the stream
- Verify new events appear in the history
- ✅ History endpoint returns ordered lifecycle events
- ✅ Events persist across backend restarts (SQLite database)
- ✅ Frontend can display stream timeline data (StreamTimeline component)
- ✅ Events emitted for create/cancel/claim actions
- ✅ Indexer worker stores event history automatically