Production-oriented baseline implementation of the system described in system-design.md.
- PostgreSQL as the default database
- Drizzle ORM for all data access
- Drizzle migrations for schema lifecycle
- Multi-chain scan flow (EVM / UTXO / Solana / TON / Sui)
- Pre-seeded chain presets: Ethereum, Optimism, Arbitrum One, Base, Polygon, BNB Chain, Avalanche C-Chain, Blast, Gnosis, HyperEVM, Monad, Bitcoin, Solana, TON, Sui
- Pre-seeded production token presets: EVM native + major ERC20/BEP20 stablecoins, Solana native + SPL stablecoins, and native assets for UTXO/TON/Sui
- UTXO sweep flow with signed raw transaction broadcast (WIF or mnemonic-derived key)
- Batched EVM scan via Multicall3 with fallback single calls
- Incremental scan skip when latest chain height has not advanced
- RPC pool protections: endpoint RPS token bucket + failure circuit breaker + cooldown
- Single application process model:
- one process starts API + scheduler + (BullMQ mode) worker consumers
- database migrations run automatically at startup
- startup flow logs each phase (DB -> context -> server -> listener)
- HTTP API (Fastify) with API key + JWT authentication
- Role-based access control (
read/operator/admin) - Risk controller with score updates and restore endpoint
- Audit logs for operational actions
- Alert channels (webhook/slack/telegram/email placeholder)
- Business event alerts wired for sweep success/failure, account risk status transitions, and full-chain RPC outages
- API protection with IP allowlist and in-memory rate limiting
- Confirmation header guard for destructive admin operations
- BullMQ scheduler + Redis (default and only scheduler mode)
- Chain-sharded scan and sweep queues in BullMQ mode
- Exponential backoff retries + DLQ fallback for failed queue jobs
- Prometheus metrics endpoint (
/metrics) - EVM support (native + ERC-20)
- UTXO support (native sweep)
- Solana support (native + SPL scan/sweep)
- TON support (native scan/sweep)
- Sui support (native scan/sweep)
- Install dependencies:
npm install- Create env file:
cp .env.example .env- Ensure PostgreSQL is running and
DATABASE_URLpoints to a reachable database.
Optional local PostgreSQL via Docker:
docker compose -f docker/docker-compose.yml up -dThis compose file also starts Redis for BullMQ.
Build application container image:
docker build -f docker/Dockerfile .- Optional: generate new migration files when schema changes:
npm run db:generate- Configure BullMQ scheduler/worker settings:
# .env
SCAN_BATCH_SIZE=500
SCAN_RPC_CONCURRENCY=20
RPC_FAILURE_THRESHOLD=5
RPC_COOLDOWN_MS=300000
REDIS_URL=redis://127.0.0.1:6379
SCAN_QUEUE_NAME=sweeper-scan
SWEEP_QUEUE_NAME=sweeper-sweep
DLQ_QUEUE_NAME=sweeper-dlq
BULLMQ_SCAN_ATTEMPTS=5
BULLMQ_SWEEP_ATTEMPTS=5
BULLMQ_BACKOFF_MS=1000
BULLMQ_SCAN_WORKER_CONCURRENCY=1
BULLMQ_SWEEP_WORKER_CONCURRENCY=5Start the complete application:
npm run devProduction start (after build):
npm run build
npm run startThe same process runs API + BullMQ scheduler + BullMQ workers automatically.
Queue model in BullMQ mode:
- scan queue per chain:
<SCAN_QUEUE_NAME>-<chainId> - sweep queue per chain:
<SWEEP_QUEUE_NAME>-<chainId> - shared DLQ:
<DLQ_QUEUE_NAME>
The API is part of the same application process started by npm run dev / npm run start.
Swagger docs:
- UI:
GET /docs - OpenAPI JSON:
GET /docs/json
Use either API key (x-api-key) or JWT (Authorization: Bearer <token>).
API key example (default in .env.example):
curl -H "x-api-key: change-me-in-production" http://127.0.0.1:3000/api/v1/scan/statusIssue a JWT (admin role required):
curl -X POST \
-H "x-api-key: change-me-in-production" \
-H "content-type: application/json" \
-d '{"subject":"ops-bot","role":"operator","expiresIn":"1h"}' \
http://127.0.0.1:3000/api/v1/auth/tokenMain routes:
GET /healthGET /health/readyGET /health/liveGET /metricsGET /api/v1/auth/mePOST /api/v1/auth/tokenGET/POST /api/v1/chainsGET/PATCH/DELETE /api/v1/chains/:idGET /api/v1/chains/:id/healthGET/POST /api/v1/tokensGET/PATCH/DELETE /api/v1/tokens/:idGET/POST /api/v1/accountsGET/DELETE /api/v1/accounts/:idGET /api/v1/accounts/:id/addressesGET /api/v1/accounts/:id/riskPOST /api/v1/accounts/:id/restoreGET /api/v1/scan/statusGET /api/v1/sweepsGET /api/v1/sweeps/:idPOST /api/v1/sweeps/triggerGET /api/v1/sweeps/statsGET/POST /api/v1/alerts/channelsPATCH/DELETE /api/v1/alerts/channels/:idPOST /api/v1/alerts/testGET /api/v1/audit/logs
Role requirements:
read: read-only endpoints (GETstatus/list/risk)operator: operational writes (POSTchain/token/account/sweep)admin: security-sensitive writes (POST /api/v1/auth/token, restore/delete actions, audit log access)
Additional API controls:
- IP allowlist via
API_IP_ALLOWLIST(*to allow all) - in-memory rate limiting via
API_RATE_LIMIT_WINDOW_MSandAPI_RATE_LIMIT_MAX - destructive admin endpoints require
x-confirm-action: <ADMIN_CONFIRM_TOKEN>
Scheduler behavior:
- BullMQ is the only scheduler mode
- scheduler starts automatically during application startup and begins periodic scans immediately
- failed jobs retry with exponential backoff; exhausted retries are copied to DLQ
- Solana account import/sweep currently supports
private_keyonly. - TON account import/sweep currently supports
private_keyas the default path. - Sui currently supports native balance scan/sweep and private-key based account import.
- Default chain presets are created with
enabled=trueand placeholder collector addresses; update collector addresses before production use. - Default scheduler configuration is seeded with BullMQ mode +
DEFAULT_SCAN_INTERVAL_MSand auto-start enabled. - Default alert channels are seeded in enabled mode (
ops-webhook-critical,ops-slack-warning,ops-telegram-critical,ops-email-critical).