This document summarizes the implementation of four major features for StellarStream.
Allows users to generate shareable payment links that embed stream parameters (amount, asset, duration) into a short UUID-based URL.
Database Schema:
- New
InvoiceLinkmodel in Prisma schema - Fields:
slug(unique short UUID),sender,receiver,amount,tokenAddress,duration,description,pdfUrl,xdrParams,status,expiresAt - Indexes on
slug,sender,status,createdAtfor fast lookups
Service Layer:
InvoiceLinkService(backend/src/services/invoice-link.service.ts)createInvoiceLink()- Generate draft link with encoded XDR parametersgetInvoiceLinkBySlug()- Retrieve link by slug (public endpoint)updateInvoiceLinkStatus()- Track link lifecycle (DRAFT → SIGNED → COMPLETED)listInvoiceLinks()- List all links for a senderdeleteInvoiceLink()- Remove a link
API Endpoints:
POST /api/v1/invoice-links- Create new invoice link (requires auth)GET /api/v1/invoice-links/:slug- Retrieve link by slug (public)GET /api/v1/invoice-links- List sender's links (requires auth)PATCH /api/v1/invoice-links/:id/status- Update statusDELETE /api/v1/invoice-links/:id- Delete link
XDR Encoding:
- Stream parameters encoded as base64-encoded JSON
- Includes: receiver, amount, tokenAddress, duration, startTime
- Frontend decodes and uses for one-click stream signing
Usage Example:
# Create invoice link
curl -X POST http://localhost:3000/api/v1/invoice-links \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"receiver": "GXXXXXX...",
"amount": "1000000000",
"tokenAddress": "CUSDC...",
"duration": 2592000,
"description": "Monthly retainer",
"pdfUrl": "https://example.com/invoice.pdf"
}'
# Response includes shareUrl: http://localhost:5173/invoice/a1b2c3d4Lightweight TypeScript library wrapping both smart contract calls and Warp API endpoints for external developers.
Package Structure:
- Location:
/sdk(new workspace package) - Build tool:
tsupfor ESM bundling - Exports:
Nebulaclass + shared types
Core Components:
-
Types (
sdk/src/types.ts)Stream- Stream state interfaceStreamStatus- Enum (ACTIVE, PAUSED, COMPLETED, CANCELED, ARCHIVED)StreamEvent- Event log interfaceStreamHistory- Combined stream + eventsYieldData- Yield accrual dataCreateStreamParams,WithdrawParams,CancelStreamParams
-
Client (
sdk/src/client.ts)NebulaClientclass - HTTP client wrapper- Methods for all stream operations
- Axios-based with configurable base URL
-
Main Export (
sdk/src/index.ts)Nebulastatic class - Main SDK entry pointinitialize(baseUrl)- Setup SDKsetAuthToken(token)- Set auth- Static methods:
createStream(),getStream(),getStreams(),withdrawFromStream(),cancelStream(),getStreamHistory(),getYieldData(),calculateYield(),getStreamEvents(),getStats(),searchStreams()
Usage Example:
import { Nebula, StreamStatus } from '@stellarstream/nebula-sdk';
// Initialize
Nebula.initialize('https://api.stellarstream.com/api/v1');
Nebula.setAuthToken('your-jwt-token');
// Create stream
const stream = await Nebula.createStream({
receiver: 'GXXXXXX...',
amount: '1000000000',
tokenAddress: 'CUSDC...',
duration: 2592000,
yieldEnabled: true,
});
// Get stream history
const history = await Nebula.getStreamHistory(stream.id);
// Calculate yield
const yield = await Nebula.calculateYield(stream.id, 30);Build & Distribution:
cd sdk
npm run build # Outputs to dist/
npm publish # Publish to npm registryAllows manual or automated catch-up by scanning historical ledgers when the indexer goes offline.
Service Layer:
HistoricalSyncService(backend/src/services/historical-sync.service.ts)syncFromSorobanRpc(fromLedger, toLedger)- Primary sync methodsyncFromHorizon(fromLedger, toLedger)- Fallback methodfetchSorobanEvents(ledger)- Query Soroban RPCfetchHorizonTransactions(fromLedger, toLedger)- Query HorizonparseHorizonTransaction(tx)- Extract contract events from XDRupsertEvents(events)- Deduplication via unique constraint on(txHash, eventIndex)getSyncState()/updateSyncState()- Track last synced ledger
CLI Script:
- Location:
backend/src/scripts/sync-historical-ledgers.ts - Executable via:
npm run sync -- --from=LEDGER_START --to=LEDGER_END [--horizon]
Deduplication Strategy:
- Uses Prisma
upsertwith unique constraint onContractEvent(txHash, eventIndex) - Prevents duplicate events if sync is interrupted and restarted
- Idempotent: safe to run multiple times
Fallback Logic:
- Try Soroban RPC first (faster, more recent data)
- If unavailable, fallback to Horizon (slower, but always available)
- Parse XDR from Horizon transaction metadata
Usage Examples:
# Sync ledgers 50000-51000 from Soroban RPC
npm run sync -- --from=50000 --to=51000
# Sync from Horizon (fallback)
npm run sync -- --from=50000 --to=51000 --horizon
# Sync last 1000 ledgers
CURRENT=$(curl -s https://horizon.stellar.org | jq '.history_latest_ledger')
npm run sync -- --from=$((CURRENT-1000)) --to=$CURRENTDatabase Updates:
- Synced events stored in
ContractEventtable SyncStatetable trackslastLedgerSequence- Enables incremental syncs:
npm run sync -- --from=$(lastLedgerSequence+1) --to=CURRENT
Ensures backend process resilience with cluster mode, automatic restarts, and log rotation.
PM2 Configuration:
- File:
backend/ecosystem.config.cjs - Two apps configured:
- stellarstream-api - Main API server
- Cluster mode with
instances: "max"(auto-detect CPU cores) - Max memory: 500MB (auto-restart on spike)
- Error/output logs with date formatting
- Graceful shutdown (5s timeout)
- Cluster mode with
- stellarstream-indexer - Event indexer
- Fork mode (single instance)
- Max memory: 300MB
- Separate log files
- stellarstream-api - Main API server
Log Rotation:
- Module:
pm2-logrotate - Configuration:
- Max file size: 100MB
- Retention: 7 days
- Compression: enabled
- Date format:
YYYY-MM-DD_HH-mm-ss
Health Monitoring:
- Service:
PM2HealthMonitor(backend/src/services/pm2-health-monitor.service.ts) - Checks every 30 seconds:
- Memory usage (restart if > 500MB)
- CPU usage (alert if > 90%)
- Process status
- Graceful restart on memory spike
Setup Script:
- Location:
backend/scripts/setup-pm2.sh - Installs PM2 globally
- Installs pm2-logrotate
- Configures log rotation
- Builds project
- Starts services
- Enables auto-start on system boot
Usage:
# Initial setup
bash backend/scripts/setup-pm2.sh
# View status
pm2 status
# View logs
pm2 logs stellarstream-api --lines 100 --follow
# Monitor resources
pm2 monit
# Restart all
pm2 restart all
# Stop all
pm2 stop all
# Delete all
pm2 delete allAuto-Start on Boot:
# After setup, run:
pm2 startup
pm2 save
# To disable:
pm2 unstartup- Database migrations created
- Prisma schema updated
- Service layers implemented
- API routes created and integrated
- SDK package scaffolded
- CLI scripts created
- PM2 configuration ready
- Documentation complete
-
Invoice Links:
- Create link, verify slug generation
- Retrieve by slug, verify XDR encoding
- Test expiration logic
- Test status transitions
-
SDK:
- Test all methods with mock backend
- Verify type exports
- Test error handling
- Build and publish to npm
-
Historical Sync:
- Sync small ledger range, verify deduplication
- Test Horizon fallback
- Verify sync state tracking
- Test idempotency (run twice, same result)
-
PM2:
- Start services, verify cluster mode
- Monitor memory, trigger restart
- Check log rotation
- Verify auto-start on reboot
- Run database migrations:
npm run db:migrate - Install SDK dependencies:
cd sdk && npm install - Build SDK:
npm run build - Setup PM2:
bash backend/scripts/setup-pm2.sh - Test invoice link endpoints
- Test historical sync:
npm run sync -- --from=50000 --to=50100