Real-time Solana swap-spike detector for meme-token discovery. The service ingests swap activity, applies token and volume filters, computes short-window anomaly signals, and emits Telegram alerts.
Status: Beta — production-usable for real-time monitoring, with known operational caveats and evolving interfaces.
- Ingests swap candidates from:
- Helius Enhanced Webhook (
POST /webhook) as the baseline ingestion path. - Optional Alchemy WebSocket + RPC enrichment path.
- Helius Enhanced Webhook (
- Normalizes and filters swaps:
- Stablecoin output mints are ignored.
- Minimum SOL input threshold is enforced when the input is native SOL.
- Market-cap threshold filter is applied for meme-token focus.
- Detects volume spikes using sharded rolling windows and EWMA baselines.
- Sends HTML-formatted alerts to Telegram.
+------------------------+
| Helius Webhook (POST) |
+-----------+------------+
|
v
+-----------+
| detector |
| filters |
+-----+-----+
|
v
+-----------+
| processor |
| shards + |
| EWMA |
+-----+-----+
|
v
+-----------+
| Telegram |
| alerts |
+-----------+
Optional side path:
Alchemy WS logsSubscribe -> ingestor (dedup/queue) -> RPC enrichment -> detector -> processor
core/cmd/main.go # service entrypoint, wiring, servers, shutdown
core/internal/handler/webhook.go # webhook auth + payload handling
core/internal/detector/detector.go # swap extraction + filtering
core/internal/processor/processor.go # sharded spike detection + alert queue
core/internal/alert/telegram.go # Telegram sink and message formatting
core/internal/metadata/fetcher.go # Jupiter + DexScreener token metadata
core/internal/filter/marketcap.go # market-cap filter
core/internal/ws/* # Alchemy WebSocket client + ingestor
core/internal/enrichment/* # Alchemy transaction enrichment workers
core/internal/types/* # Helius payload types
core/helpers.go # DEX program IDs + stablecoin mints
Dockerfile # multi-stage production image
.github/workflows/deploy-ec2.yml # CI deploy pipeline to EC2
- Go
1.25.x(module currently targetsgo 1.25.0). - Network egress to:
- Telegram Bot API
- Jupiter Tokens V2 API
- DexScreener API
- Optional: Alchemy WS/RPC endpoints
- A configured Helius webhook sender (external to this service).
Copy and set values appropriate to your environment:
# -----------------------------
# Required for baseline operation
# -----------------------------
WEBHOOK_SECRET=replace-with-shared-secret
TELEGRAM_BOT_TOKEN=123456789:replace-with-bot-token
TELEGRAM_CHAT_ID=-1001234567890
# -----------------------------
# Optional filters and metadata
# -----------------------------
# Max allowed token market cap (USD) for alerts.
# If omitted or invalid, service defaults to 200000.
MAX_MARKET_CAP_USD=200000
# Optional Jupiter API key for tokens/v2/search.
JUPITER_API_KEY=
# -----------------------------
# Optional Alchemy ingestion path
# Both must be set together to enable WS ingestion.
# -----------------------------
ALCHEMY_WS_URL=wss://solana-mainnet.g.alchemy.com/v2/replace-with-key
ALCHEMY_RPC_URL=https://solana-mainnet.g.alchemy.com/v2/replace-with-key| Variable | Required | Default | Purpose |
|---|---|---|---|
WEBHOOK_SECRET |
Yes | none | Authorization shared secret for /webhook. |
TELEGRAM_BOT_TOKEN |
Yes (if alerts enabled) | none | Bot token for Telegram sendMessage API. |
TELEGRAM_CHAT_ID |
Yes (if alerts enabled) | none | Destination chat/channel ID. |
MAX_MARKET_CAP_USD |
No | 200000 |
Max cap allowed by market-cap filter. |
JUPITER_API_KEY |
No | empty | Optional API key for Jupiter token search requests. |
ALCHEMY_WS_URL |
No | empty | Enables optional WS ingestion when paired with ALCHEMY_RPC_URL. |
ALCHEMY_RPC_URL |
No | empty | RPC endpoint used by enrichment workers. |
Configured in main.go and engine internals:
- Processor config:
Shards=16QueuePerShard=2048AlertQueue=1024WindowSec=60MinVolumeRaw=1_000_000_000MinTrades=8SpikeMultiple=3.0EWMAAlpha=0.2AlertCooldownSec=30
- Detector filters:
- Minimum SOL input threshold:
0.3 SOL(3e8lamports) when input is native SOL. - Stablecoin output mint suppression enabled.
- Market-cap filter enabled using
MAX_MARKET_CAP_USD.
- Minimum SOL input threshold:
go mod download# Option A: export env vars directly
export WEBHOOK_SECRET="replace-with-secret"
export TELEGRAM_BOT_TOKEN="replace-with-bot-token"
export TELEGRAM_CHAT_ID="replace-with-chat-id"
# Optional
export MAX_MARKET_CAP_USD="200000"
export JUPITER_API_KEY=""
export ALCHEMY_WS_URL=""
export ALCHEMY_RPC_URL=""
go run ./core/cmd/main.gogo build -o sol-whisperer ./core/cmd/main.godocker build -t sol-whisperer .docker run -d \
--name sol-whisperer \
--restart unless-stopped \
-p 8080:8080 \
-p 127.0.0.1:6060:6060 \
--env-file .env \
sol-whisperer- Path:
POST /webhook - Authorization header accepted values:
Authorization: <WEBHOOK_SECRET>Authorization: Bearer <WEBHOOK_SECRET>
# Expected: 405 Method Not Allowed (if using GET)
# This still proves HTTP server is alive.
curl -i http://localhost:8080/webhook
# Expected: 401 Unauthorized (POST without valid Authorization)
curl -i -X POST http://localhost:8080/webhookExposed on :6060 with standard endpoints:
/debug/pprof//debug/pprof/profile/debug/pprof/heap/debug/pprof/goroutine/debug/pprof/trace
Example:
go tool pprof http://localhost:6060/debug/pprof/profileWhen both ALCHEMY_WS_URL and ALCHEMY_RPC_URL are present:
- Service opens WS connection.
- Subscribes to tracked DEX programs via
logsSubscribewithprocessedcommitment. - Ingestor deduplicates signatures and queues enrichment tasks.
- Enrichment workers call
getTransactionover RPC. - Extracted swaps re-enter the same detector/processor pipeline.
If either variable is missing, service remains in webhook-only mode.
If logs show:
subscription error: Method 'logsSubscribe' not found (code=-32601)
this typically indicates Alchemy app/key capability mismatch for Solana PubSub methods, not necessarily a process crash. In this codebase, the Alchemy ingestion goroutine exits for that path, while the HTTP webhook server can continue running.
Telegram alert includes:
- Token symbol (if metadata resolved)
- SOL amount (if input was SOL)
- Mint
- Swapper
- Source
- Window/trade count/raw volume
- Baseline EWMA and spike ratio
- Signature
If metadata lookup fails, alert still sends without symbol enrichment.
Workflow file: .github/workflows/deploy-ec2.yml
Pipeline behavior:
- Build Docker image.
- Save and copy image tarball to EC2.
- Load image on EC2.
- Replace running container with:
-p 8080:8080-p 127.0.0.1:6060:6060--env-file /etc/environment
railway.toml builds and runs Go binary from core/cmd.
Cause: Route is defined as POST-only.
Action:
curl -i -X POST http://localhost:8080/webhookCause: Missing/invalid Authorization header.
Action: send exact secret or Bearer <secret>.
Cause: Provider/app capability issue for PubSub method.
Action:
- Verify Alchemy app tier/capabilities and key.
- Keep webhook ingestion active as baseline path.
Cause: expected context cancellation during graceful shutdown.
Action: treat as normal if it appears immediately after shutdown signal.
Cause: metadata lookup timeout or no upstream metadata.
Action:
- Verify network egress to Jupiter and DexScreener.
- Optionally set
JUPITER_API_KEY.
# 1) Pull deps
go mod download
# 2) Build fast
go build ./core/cmd
# 3) Run all tests (repository may currently have no *_test.go files)
go test ./...
# 4) Vet
go vet ./...- Keep webhook compatibility stable (
/webhook, auth behavior, response codes). - Preserve detector filter semantics unless intentionally changing alert policy.
- Treat queue and timeout changes as production-impacting; verify under load.
- For Alchemy path changes, validate behavior with and without WS/RPC env vars.
- Webhook route responds and auth works.
- Baseline webhook-only mode works with Alchemy vars unset.
- Optional Alchemy mode starts when both vars are set.
- Alerts still send and include expected fields.
- Graceful shutdown completes without hangs.
- Never commit real tokens/secrets.
- Restrict inbound access to
:8080to trusted webhook source(s). - Keep
:6060bound to localhost or behind restricted network controls. - Use TLS termination and authentication at ingress/reverse proxy.
- No persistence layer: processing state and dedup are in-memory.
- No built-in structured metrics exporter (currently log-based metrics snapshots).
- No unit/integration test suite is currently committed.
MIT