Skip to content

subhamg/swap-tracker

Repository files navigation

Solana Swap Tracker (Monorepo: NestJS + Next.js)

Apps

  • Backend: apps/backend (NestJS)
  • Web: apps/web (Next.js)

Requirements covered

  • Subscribes to Solana logs and parses swaps from raw pre/post token balances
  • Stores swaps in Postgres via TypeORM (Postgres driver)
  • WebSocket updates via Socket.IO
  • REST: GET /swaps/latest
  • Web UI: auto-updates last 10 swaps
  • Dockerfiles + docker-compose

Setup

Environment variables

Backend (apps/backend/.env):

# Required: Postgres connection
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/swaps

# Optional: API port (default 4000)
PORT=4000

# Solana RPC (HTTP). Use a provider with sufficient limits
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com

# Optional: Solana WS endpoint (for logs). If provider lacks logsSubscribe, leave empty
# SOLANA_WS_URL=wss://your-ws-provider

# Tuning for rate limits / retries
# Concurrent signature processing (default 1)
SOL_TX_CONCURRENCY=1
# Min delay between RPC calls in ms (default 250)
SOL_TX_MIN_DELAY_MS=250

Frontend (apps/web):

# Backend base URL used by the web app
NEXT_PUBLIC_API_URL=http://localhost:4000

# Explorer base for tx/user links (optional; default https://solscan.io)
# NEXT_PUBLIC_SOL_EXPLORER_BASE=https://solscan.io

Local (without Docker)

  1. Postgres running locally, create DB swaps and set apps/backend/.env:
    • DATABASE_URL=postgresql://postgres:postgres@localhost:5432/swaps
    • PORT=4000
    • SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
  2. Database schema:
    • Dev: synchronize: true will auto-create tables.
    • Prod: disable synchronize and run TypeORM migrations.
  3. Start backend and web:
    npm run dev:backend
    npm run dev:web
    Or concurrently from root if you install concurrently: npm run dev.

Docker Compose

docker compose up --build
  • Backend: http://localhost:4000
  • Web: http://localhost:3000

Compose uses:

  • Postgres 16 (user postgres, password postgres, db swaps)
  • Backend container with DATABASE_URL=postgresql://postgres:postgres@db:5432/swaps
  • Web container pointing NEXT_PUBLIC_API_URL at backend

You can override compose envs via .env.docker in the repo root (compose reads it automatically).

Notes on parsing

  • No high-level SDKs used for parsing swaps; we read getTransaction data and infer swaps using pre/post token account balances for the signer. This catches swaps executed via aggregators (e.g., Jupiter) on top of underlying DEX programs (Orca/Raydium), without Anchor parsers.
  • For production-grade accuracy, extend with per-DEX instruction decoding to map exact pools and fees.

Improvements (what we'd ship next)

  • Scaling to thousands TPS:
    • Use onLogs(programId) for targeted DEX IDs and multiple workers
    • Batch getTransaction via getBlock/getSignaturesForAddress and decode offline
    • Connection pooling for Postgres; write-ahead queue (e.g., Kafka/NATS)
  • Reorg handling:
    • Subscribe at finalized and reconcile differences; maintain seen-signatures by slot and re-validate on slot rollback
  • Retry and backfill:
    • Retry getTransaction failures with exponential backoff
    • Backfill recent slots on startup to cover downtime
  • Metrics and ops:
    • Prometheus metrics (queue size, swaps/sec, RPC latencies)
    • Structured logging and alerting
    • Health endpoints (liveness/readiness)
  • Data quality:
    • Decode instructions per protocol (Orca, Raydium) to get pool addresses and fee info
    • Map token mints to symbols via a curated token list

EVM Extension (sketch)

  • Architecture:
    • Add an EVM listener service subscribing to logs for swap events (UniswapV2/V3, etc.)
    • Normalize into shared Swap abstraction (chain, txHash, fromToken, toToken, amounts, blockNumber/time, user)
  • Tools:
    • ethers or viem for JSON-RPC
    • ABIs for routers/pools; multicall for efficiency
  • Shared layer design:
    • Interface-driven parser modules per chain/dex
    • Common storage schema with chain and logIndex

Bonus

  • REST endpoint: Implemented (GET /swaps/latest).
  • Min USD filter: Implemented (USDC-only baseline)
    • Client: number input on the page
    • Server: GET /swaps/latest?min_usd=1000 (filters USDC amounts)
    • Future: integrate price API (e.g., Jupiter) to compute USD for all tokens server-side
  • Show current slot/count: Implemented via /swaps/status + UI badge on the page
  • Telegram bot: Not included to keep scope focused. Could be added as a small Nest provider posting on new swaps.

How to verify bonus

  • Open the web UI and trigger a swap: the newest entry appears at the top, older entries drop off after 10.
  • Adjust the Min USD field: the list updates according to the threshold (USDC baseline).
  • Current slot badge updates roughly every 5s.
  • API endpoints:
    • GET /swaps/latest (optionally ?limit=10&min_usd=1000)
    • GET /swaps/status

Operations & Tuning

  • RPC provider:
    • If logsSubscribe is not supported, the service still works via polling fallback (tunable).
    • Prefer a provider with WS logs (paid tiers often required).
  • Rate limits (envs):
    • SOL_TX_CONCURRENCY (default 1): signature processing concurrency
    • SOL_TX_MIN_DELAY_MS (default 250): min gap between RPC calls
    • SOL_POLL_INTERVAL_MS (if polling enabled): signature polling cadence
  • WebSocket:
    • Backend Socket.IO path: /socket.io
    • Frontend will fall back to polling if WS upgrade fails.

Troubleshooting

  • Socket 404 on /socket.io: ensure backend is running on port 4000 and Socket.IO adapter is mounted; no reverse proxy on that port.
  • 429 Too Many Requests: lower SOL_TX_CONCURRENCY, increase SOL_TX_MIN_DELAY_MS, or use a higher-capacity RPC.
  • No logs with certain providers: set SOLANA_WS_URL to a WS endpoint that supports logs, or rely on polling.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages