solana-traderclaw 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +516 -0
- package/bin/gateway-persistence-linux.mjs +275 -0
- package/bin/installer-step-engine.mjs +1422 -0
- package/bin/llm-model-preference.mjs +136 -0
- package/bin/openclaw-trader.mjs +2624 -0
- package/bin/traderclaw.cjs +13 -0
- package/config/gateway-v1.json5 +121 -0
- package/dist/chunk-3UQIQJPQ.js +144 -0
- package/dist/chunk-3YPZOXWE.js +238 -0
- package/dist/chunk-RQZVD6TH.js +361 -0
- package/dist/chunk-T4YWGIIR.js +64 -0
- package/dist/index.js +2883 -0
- package/dist/src/alpha-buffer.js +6 -0
- package/dist/src/alpha-ws.js +6 -0
- package/dist/src/http-client.js +6 -0
- package/dist/src/session-manager.js +6 -0
- package/openclaw.plugin.json +104 -0
- package/package.json +60 -0
- package/skills/solana-trader/HEARTBEAT.md +51 -0
- package/skills/solana-trader/SKILL.md +2739 -0
- package/skills/solana-trader/bitquery-schema.md +303 -0
- package/skills/solana-trader/query-catalog.md +184 -0
- package/skills/solana-trader/refs/x-credentials.md +99 -0
- package/skills/solana-trader/websocket-streaming.md +265 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# WebSocket + Bitquery Streaming Reference
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Use this guide when working with OpenClaw WebSocket behavior, managed Bitquery subscriptions, policy limits, and diagnostics endpoints.
|
|
6
|
+
|
|
7
|
+
This document covers:
|
|
8
|
+
- Where WebSocket traffic enters the app
|
|
9
|
+
- How Bitquery subscriptions are multiplexed
|
|
10
|
+
- What external clients are allowed to do
|
|
11
|
+
- How limits and metering are enforced
|
|
12
|
+
- How to validate behavior quickly
|
|
13
|
+
|
|
14
|
+
This is a companion reference to `SKILL.md`, similar to `bitquery-schema.md` and `query-catalog.md`.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## High-Level Architecture
|
|
19
|
+
|
|
20
|
+
### Local WebSocket Entrypoint
|
|
21
|
+
|
|
22
|
+
- Path: `/ws`
|
|
23
|
+
- File: `server/websocket.ts`
|
|
24
|
+
- Manager: `OpenClawWebSocketManager`
|
|
25
|
+
|
|
26
|
+
Clients connect to `/ws` and can subscribe to:
|
|
27
|
+
- Internal OpenClaw channels (`trades`, `positions`, etc.)
|
|
28
|
+
- Managed Bitquery channels via `bitquery_subscribe`
|
|
29
|
+
|
|
30
|
+
### Upstream Bitquery Bridge
|
|
31
|
+
|
|
32
|
+
- File: `server/services/bitquery-ws-bridge.ts`
|
|
33
|
+
- Class: `BitqueryWsBridge`
|
|
34
|
+
- Upstream URL: `wss://streaming.bitquery.io/graphql`
|
|
35
|
+
- Protocol: `graphql-transport-ws`
|
|
36
|
+
|
|
37
|
+
The bridge keeps one upstream subscription per `templateKey + variables hash` and fans out events to many local clients.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Bootstrap Wiring
|
|
42
|
+
|
|
43
|
+
The WebSocket manager is created in `server/websocket.ts` and wired into the Express server in `server/index.ts`. The `BitqueryWsBridge` is instantiated and injected into the WebSocket manager during configuration.
|
|
44
|
+
|
|
45
|
+
If WebSocket behavior is missing, verify this wiring first.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Message Contract (Client <-> Server)
|
|
50
|
+
|
|
51
|
+
### Server -> Client Messages
|
|
52
|
+
|
|
53
|
+
| Type | Description | Payload |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `connected` | Sent on initial WebSocket connection | `{ type: "connected" }` |
|
|
56
|
+
| `authenticated` | Sent after successful `auth` message | `{ type: "authenticated" }` |
|
|
57
|
+
| `subscribed` | Ack for internal channel subscription | `{ type: "subscribed", channels: string[] }` |
|
|
58
|
+
| `bitquery_subscribed` | Ack for managed Bitquery subscription | `{ type: "bitquery_subscribed", subscriptionId: string, templateKey: string }` |
|
|
59
|
+
| `bitquery_event` | Upstream Bitquery data event relayed to client | `{ type: "bitquery_event", subscriptionId: string, data: object }` |
|
|
60
|
+
| `bitquery_error` | Error from upstream Bitquery subscription | `{ type: "bitquery_error", subscriptionId: string, error: object }` |
|
|
61
|
+
| `bitquery_complete` | Upstream subscription completed | `{ type: "bitquery_complete", subscriptionId: string }` |
|
|
62
|
+
| `bitquery_unsubscribed` | Ack for unsubscribe request | `{ type: "bitquery_unsubscribed", subscriptionId: string }` |
|
|
63
|
+
| `error` | General error message | `{ type: "error", message: string, code?: string }` |
|
|
64
|
+
| `pong` | Response to client `ping` | `{ type: "pong" }` |
|
|
65
|
+
|
|
66
|
+
### Client -> Server Messages
|
|
67
|
+
|
|
68
|
+
| Type | Description | Payload |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `auth` | Authenticate the WebSocket connection | `{ type: "auth", accessToken: string }` |
|
|
71
|
+
| `subscribe` | Subscribe to internal channels | `{ type: "subscribe", channels: string[] }` |
|
|
72
|
+
| `bitquery_subscribe` | Subscribe to a managed Bitquery stream | `{ type: "bitquery_subscribe", templateKey: string, walletId: string, variables: object }` |
|
|
73
|
+
| `bitquery_unsubscribe` | Unsubscribe from a managed Bitquery stream | `{ type: "bitquery_unsubscribe", subscriptionId: string }` |
|
|
74
|
+
| `ping` | Keepalive ping | `{ type: "ping" }` |
|
|
75
|
+
|
|
76
|
+
**Important:** Do not generate your own `subscriptionId` values. Always use the `subscriptionId` returned by the server in the `bitquery_subscribed` ack message.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Auth + Policy Enforcement
|
|
81
|
+
|
|
82
|
+
### WebSocket Authentication
|
|
83
|
+
|
|
84
|
+
1. Client sends `{ type: "auth", accessToken: "..." }` after connecting
|
|
85
|
+
2. Server validates the access token
|
|
86
|
+
3. Session must be active
|
|
87
|
+
4. Client must exist in storage
|
|
88
|
+
5. On success, server sends `{ type: "authenticated" }`
|
|
89
|
+
6. Until authenticated, only `auth` and `ping` messages are accepted
|
|
90
|
+
|
|
91
|
+
### Bitquery Subscription Policy Checks
|
|
92
|
+
|
|
93
|
+
For every `bitquery_subscribe` request, the server enforces the following checks in order:
|
|
94
|
+
|
|
95
|
+
1. **Template allowlist** — `templateKey` must match a known subscription template (see `query-catalog.md` Subscriptions section)
|
|
96
|
+
2. **Tier + scope access** — Client must have `bitquery:catalog` scope and appropriate tier access
|
|
97
|
+
3. **Funded wallet gate** — The `walletId` must reference a funded wallet with sufficient SOL balance
|
|
98
|
+
4. **Usage/metering** — RPS, bandwidth, subscription counts, and advanced filter limits are checked
|
|
99
|
+
5. **Per-client subscription cap** — Maximum active subscriptions per client (default: 20, configurable via `OPENCLAW_WS_MAX_SUBS_PER_CLIENT`)
|
|
100
|
+
|
|
101
|
+
If any check fails, the server sends an `error` message with a descriptive code and message.
|
|
102
|
+
|
|
103
|
+
### Funded Wallet Source of Truth
|
|
104
|
+
|
|
105
|
+
Do not trust stale database balance alone. The current check uses:
|
|
106
|
+
- Live balance refresh from on-chain data
|
|
107
|
+
- Fallback to direct RPC wallet balance query when cached data is stale
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Subscription Lifecycle
|
|
112
|
+
|
|
113
|
+
### Subscribe Flow
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
Client Server Bitquery Upstream
|
|
117
|
+
| | |
|
|
118
|
+
|-- bitquery_subscribe -------->| |
|
|
119
|
+
| { templateKey, walletId, | |
|
|
120
|
+
| variables } | |
|
|
121
|
+
| |-- policy checks ------------->|
|
|
122
|
+
| | |
|
|
123
|
+
| |-- subscribe (if new stream) ->|
|
|
124
|
+
| | (reuse if same templateKey |
|
|
125
|
+
| | + variables hash exists) |
|
|
126
|
+
| | |
|
|
127
|
+
|<- bitquery_subscribed --------| |
|
|
128
|
+
| { subscriptionId, | |
|
|
129
|
+
| templateKey } | |
|
|
130
|
+
| | |
|
|
131
|
+
|<- bitquery_event -------------|<-- data event ----------------|
|
|
132
|
+
| { subscriptionId, data } | (fanned out to all |
|
|
133
|
+
| | subscribers on this stream)|
|
|
134
|
+
| | |
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Unsubscribe Flow
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
Client Server Bitquery Upstream
|
|
141
|
+
| | |
|
|
142
|
+
|-- bitquery_unsubscribe ------>| |
|
|
143
|
+
| { subscriptionId } | |
|
|
144
|
+
| |-- remove client from stream ->|
|
|
145
|
+
| | (if last subscriber, |
|
|
146
|
+
| | close upstream sub) |
|
|
147
|
+
| | |
|
|
148
|
+
|<- bitquery_unsubscribed ------| |
|
|
149
|
+
| { subscriptionId } | |
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Multiplexing
|
|
153
|
+
|
|
154
|
+
The bridge maintains one upstream WebSocket subscription per unique `templateKey + variables hash`. When multiple clients subscribe to the same stream:
|
|
155
|
+
|
|
156
|
+
- Only one upstream connection is made
|
|
157
|
+
- Events are fanned out to all local subscribers
|
|
158
|
+
- When a client unsubscribes, they are removed from the fan-out list
|
|
159
|
+
- When the last subscriber disconnects, the upstream subscription is closed
|
|
160
|
+
|
|
161
|
+
### Client Disconnect Cleanup
|
|
162
|
+
|
|
163
|
+
When a WebSocket client disconnects:
|
|
164
|
+
- All of their active Bitquery subscriptions are automatically cleaned up
|
|
165
|
+
- If they were the last subscriber on any stream, those upstream subscriptions are closed
|
|
166
|
+
- Internal channel subscriptions are also removed
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Available Subscription Templates
|
|
171
|
+
|
|
172
|
+
These are the managed subscription template keys. See `query-catalog.md` for full details.
|
|
173
|
+
|
|
174
|
+
| Template Key | Description | Variables |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `realtimeTokenPricesSolana` | Real-time token prices on Solana | `token: String!` |
|
|
177
|
+
| `ohlc1s` | 1-second OHLC stream | `token: String!` |
|
|
178
|
+
| `dexPoolLiquidityChanges` | DEXPool liquidity changes stream | `token: String!` |
|
|
179
|
+
| `pumpFunTokenCreation` | Pump.fun token creation stream | (none) |
|
|
180
|
+
| `pumpFunTrades` | Pump.fun trades stream | `token: String` |
|
|
181
|
+
| `pumpSwapTrades` | PumpSwap trades stream | `token: String` |
|
|
182
|
+
| `raydiumNewPools` | Raydium v4/Launchpad/CLMM new pools stream | (none) |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Raw Query Endpoint Guard
|
|
187
|
+
|
|
188
|
+
`POST /api/bitquery/query` behavior:
|
|
189
|
+
- Regular raw GraphQL queries are still allowed (subject to existing raw scope/tier checks)
|
|
190
|
+
- If operation type is `subscription` AND the query matches a managed template operation, the request is rejected with:
|
|
191
|
+
- Code: `BITQUERY_SUBSCRIPTION_MANAGED_ONLY`
|
|
192
|
+
- Message: `"Use /ws with bitquery_subscribe for managed subscriptions"`
|
|
193
|
+
|
|
194
|
+
This prevents clients from bypassing the managed subscription lifecycle (and its policy enforcement) by sending raw subscription queries through the REST endpoint.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Diagnostics Endpoint
|
|
199
|
+
|
|
200
|
+
`GET /api/bitquery/subscriptions/active`
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
- Connected WebSocket client count
|
|
204
|
+
- Clients with active Bitquery subscriptions
|
|
205
|
+
- Bridge diagnostics:
|
|
206
|
+
- `upstreamConnected` — whether the upstream Bitquery WebSocket is connected
|
|
207
|
+
- `activeStreams` — number of active multiplexed streams
|
|
208
|
+
- Per-stream entries: `templateKey`, `streamKey`, `subscriberCount`, `lastEventAt`
|
|
209
|
+
|
|
210
|
+
Access is policy-protected with `bitquery:catalog` scope.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Agent Tool Integration
|
|
215
|
+
|
|
216
|
+
Three plugin tools are available for the agent to manage subscriptions programmatically (the agent operates via stateless HTTP, so the orchestrator manages WebSocket connections on its behalf):
|
|
217
|
+
|
|
218
|
+
| Tool | Description | Parameters |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| `solana_bitquery_subscribe` | Subscribe to a managed Bitquery stream | `templateKey: string`, `variables: object` |
|
|
221
|
+
| `solana_bitquery_unsubscribe` | Unsubscribe from a stream | `subscriptionId: string` |
|
|
222
|
+
| `solana_bitquery_subscriptions` | List active Bitquery subscriptions | (none) |
|
|
223
|
+
|
|
224
|
+
The orchestrator creates and manages the WebSocket subscription on behalf of the plugin. Events from the subscription feed into the orchestrator's broadcast channels and can be consumed by the agent through subsequent tool calls or polling.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Trading Use Cases
|
|
229
|
+
|
|
230
|
+
### New Launch Detection
|
|
231
|
+
- Use `pumpFunTokenCreation` subscription for real-time alerts on new Pump.fun token launches
|
|
232
|
+
- Replaces polling `pumpFunCreation.trackNewTokens` for lower latency
|
|
233
|
+
- Combine with `raydiumNewPools` for cross-launchpad coverage
|
|
234
|
+
|
|
235
|
+
### Active Position Monitoring
|
|
236
|
+
- Use `pumpFunTrades` or `pumpSwapTrades` with `{ token: "MINT_ADDRESS" }` to monitor trades on tokens you hold
|
|
237
|
+
- Enables real-time flow detection (large sells, whale accumulation) without polling
|
|
238
|
+
- Useful for Step 7 (Monitor) — detect momentum collapse or flow reversal immediately
|
|
239
|
+
|
|
240
|
+
### Real-Time Price Tracking
|
|
241
|
+
- Use `ohlc1s` with `{ token: "MINT_ADDRESS" }` for 1-second OHLC candles
|
|
242
|
+
- Use `realtimeTokenPricesSolana` for simpler price-only stream
|
|
243
|
+
- Enables micro-timing entries (Step 4.2) — watch for pullbacks within uptrends in real-time
|
|
244
|
+
|
|
245
|
+
### Liquidity Monitoring
|
|
246
|
+
- Use `dexPoolLiquidityChanges` to detect LP drains or additions in real-time
|
|
247
|
+
- Critical for anti-rug detection on FRESH and EMERGING tokens
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Known Gotchas
|
|
252
|
+
|
|
253
|
+
1. **Wallet not funded errors** — If most templates fail with "wallet not funded", verify the live on-chain balance, not only the cached database value.
|
|
254
|
+
|
|
255
|
+
2. **Wrong process listening** — If WebSocket connections time out for all templates, check whether the API process you're connecting to is actually the one listening on the expected port.
|
|
256
|
+
|
|
257
|
+
3. **Single template failures** — If one template fails while others pass, it is usually a schema mismatch inside that template's GraphQL query, not a WebSocket transport problem.
|
|
258
|
+
|
|
259
|
+
4. **Environment variable typo** — `BITQUERY_API_KEYBOT` typo exists in some env naming. The bridge supports both the typo and corrected env names (`BITQUERY_API_KEY`) for compatibility.
|
|
260
|
+
|
|
261
|
+
5. **Subscription ID ownership** — Never fabricate `subscriptionId` values. Always use the ID returned by the server in the `bitquery_subscribed` ack. Using invalid IDs for unsubscribe will result in an error.
|
|
262
|
+
|
|
263
|
+
6. **Unauthenticated messages** — Sending `bitquery_subscribe` before completing `auth` will result in an error. Always authenticate first.
|
|
264
|
+
|
|
265
|
+
7. **Subscription cap** — Default per-client cap is 20 active subscriptions. Exceeding this returns an error. Unsubscribe from unused streams before creating new ones.
|