traderclaw-v1 1.0.0
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 +296 -0
- package/bin/openclaw-trader.mjs +897 -0
- package/dist/chunk-3UQIQJPQ.js +144 -0
- package/dist/chunk-45WQGKBZ.js +369 -0
- package/dist/chunk-GHV6TKIC.js +217 -0
- package/dist/chunk-OIWH6XY6.js +64 -0
- package/dist/index.js +927 -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 +97 -0
- package/package.json +57 -0
- package/skills/social-intel/SKILL.md +305 -0
- package/skills/solana-trader/SKILL.md +1652 -0
- package/skills/solana-trader/bitquery-schema.md +303 -0
- package/skills/solana-trader/query-catalog.md +184 -0
- package/skills/solana-trader/websocket-streaming.md +265 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,927 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AlphaBuffer
|
|
3
|
+
} from "./chunk-3UQIQJPQ.js";
|
|
4
|
+
import {
|
|
5
|
+
AlphaStreamManager
|
|
6
|
+
} from "./chunk-GHV6TKIC.js";
|
|
7
|
+
import {
|
|
8
|
+
orchestratorRequest
|
|
9
|
+
} from "./chunk-OIWH6XY6.js";
|
|
10
|
+
import {
|
|
11
|
+
SessionManager
|
|
12
|
+
} from "./chunk-45WQGKBZ.js";
|
|
13
|
+
|
|
14
|
+
// index.ts
|
|
15
|
+
import { Type } from "@sinclair/typebox";
|
|
16
|
+
function parseConfig(raw) {
|
|
17
|
+
const obj = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
18
|
+
const orchestratorUrl = typeof obj.orchestratorUrl === "string" ? obj.orchestratorUrl : "";
|
|
19
|
+
const walletId = typeof obj.walletId === "string" ? obj.walletId : typeof obj.walletId === "number" ? String(obj.walletId) : "";
|
|
20
|
+
const apiKey = typeof obj.apiKey === "string" ? obj.apiKey : "";
|
|
21
|
+
const externalUserId = typeof obj.externalUserId === "string" ? obj.externalUserId : void 0;
|
|
22
|
+
const refreshToken = typeof obj.refreshToken === "string" ? obj.refreshToken : void 0;
|
|
23
|
+
const walletPublicKey = typeof obj.walletPublicKey === "string" ? obj.walletPublicKey : void 0;
|
|
24
|
+
const walletPrivateKey = typeof obj.walletPrivateKey === "string" ? obj.walletPrivateKey : void 0;
|
|
25
|
+
const apiTimeout = typeof obj.apiTimeout === "number" ? obj.apiTimeout : 3e4;
|
|
26
|
+
const agentId = typeof obj.agentId === "string" ? obj.agentId : void 0;
|
|
27
|
+
return { orchestratorUrl, walletId, apiKey, externalUserId, refreshToken, walletPublicKey, walletPrivateKey, apiTimeout, agentId };
|
|
28
|
+
}
|
|
29
|
+
var solanaTraderPlugin = {
|
|
30
|
+
id: "solana-trader",
|
|
31
|
+
name: "Solana Trader",
|
|
32
|
+
description: "Autonomous Solana memecoin trading agent \u2014 orchestrator integration",
|
|
33
|
+
register(api) {
|
|
34
|
+
const config = parseConfig(api.pluginConfig);
|
|
35
|
+
const { orchestratorUrl, walletId, apiKey, apiTimeout } = config;
|
|
36
|
+
if (!orchestratorUrl) {
|
|
37
|
+
api.logger.error("[solana-trader] orchestratorUrl is required in plugin config. Run: openclaw-trader setup");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!apiKey && !config.refreshToken) {
|
|
41
|
+
api.logger.error("[solana-trader] apiKey or refreshToken is required in plugin config. Run: openclaw-trader setup");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const sessionManager = new SessionManager({
|
|
45
|
+
baseUrl: orchestratorUrl,
|
|
46
|
+
apiKey: apiKey || "",
|
|
47
|
+
refreshToken: config.refreshToken,
|
|
48
|
+
walletPublicKey: config.walletPublicKey,
|
|
49
|
+
walletPrivateKey: config.walletPrivateKey,
|
|
50
|
+
clientLabel: "openclaw-plugin-runtime",
|
|
51
|
+
timeout: apiTimeout,
|
|
52
|
+
onTokensRotated: (tokens) => {
|
|
53
|
+
api.logger.info(
|
|
54
|
+
`[solana-trader] Session tokens rotated. New refreshToken: ${tokens.refreshToken.slice(0, 8)}... Update config with: openclaw-trader config set refreshToken ${tokens.refreshToken}`
|
|
55
|
+
);
|
|
56
|
+
},
|
|
57
|
+
logger: {
|
|
58
|
+
info: (msg) => api.logger.info(`[solana-trader] ${msg}`),
|
|
59
|
+
warn: (msg) => api.logger.warn(`[solana-trader] ${msg}`),
|
|
60
|
+
error: (msg) => api.logger.error(`[solana-trader] ${msg}`)
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
const onUnauthorized = async () => {
|
|
64
|
+
api.logger.warn("[solana-trader] Received 401 \u2014 refreshing session...");
|
|
65
|
+
return sessionManager.handleUnauthorized();
|
|
66
|
+
};
|
|
67
|
+
const post = async (path, body, extraHeaders) => {
|
|
68
|
+
const token = await sessionManager.getAccessToken();
|
|
69
|
+
return orchestratorRequest({
|
|
70
|
+
baseUrl: orchestratorUrl,
|
|
71
|
+
method: "POST",
|
|
72
|
+
path,
|
|
73
|
+
body: { walletId, ...body },
|
|
74
|
+
timeout: apiTimeout,
|
|
75
|
+
accessToken: token,
|
|
76
|
+
extraHeaders,
|
|
77
|
+
onUnauthorized
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
const get = async (path) => {
|
|
81
|
+
const token = await sessionManager.getAccessToken();
|
|
82
|
+
return orchestratorRequest({
|
|
83
|
+
baseUrl: orchestratorUrl,
|
|
84
|
+
method: "GET",
|
|
85
|
+
path,
|
|
86
|
+
timeout: apiTimeout,
|
|
87
|
+
accessToken: token,
|
|
88
|
+
onUnauthorized
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
const put = async (path, body) => {
|
|
92
|
+
const token = await sessionManager.getAccessToken();
|
|
93
|
+
return orchestratorRequest({
|
|
94
|
+
baseUrl: orchestratorUrl,
|
|
95
|
+
method: "PUT",
|
|
96
|
+
path,
|
|
97
|
+
body,
|
|
98
|
+
timeout: apiTimeout,
|
|
99
|
+
accessToken: token,
|
|
100
|
+
onUnauthorized
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
const del = async (path) => {
|
|
104
|
+
const token = await sessionManager.getAccessToken();
|
|
105
|
+
return orchestratorRequest({
|
|
106
|
+
baseUrl: orchestratorUrl,
|
|
107
|
+
method: "DELETE",
|
|
108
|
+
path,
|
|
109
|
+
timeout: apiTimeout,
|
|
110
|
+
accessToken: token,
|
|
111
|
+
onUnauthorized
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
const json = (data) => ({
|
|
115
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
116
|
+
});
|
|
117
|
+
const wrapExecute = (fn) => async (toolCallId, params) => {
|
|
118
|
+
try {
|
|
119
|
+
const result = await fn(toolCallId, params ?? {});
|
|
120
|
+
return json(result);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
api.registerTool({
|
|
126
|
+
name: "solana_scan_launches",
|
|
127
|
+
description: "Scan for new Solana token launches (Pump.fun, Raydium, PumpSwap). Returns recent launches with initial metrics. Watch for deployer patterns \u2014 same deployer launching multiple tokens is a serial rugger red flag.",
|
|
128
|
+
parameters: Type.Object({}),
|
|
129
|
+
execute: wrapExecute(async () => post("/api/scan/new-launches", {}))
|
|
130
|
+
});
|
|
131
|
+
api.registerTool({
|
|
132
|
+
name: "solana_scan_hot_pairs",
|
|
133
|
+
description: "Find Solana trading pairs with high volume and price acceleration. Returns hot pairs ranked by activity.",
|
|
134
|
+
parameters: Type.Object({}),
|
|
135
|
+
execute: wrapExecute(async () => post("/api/scan/hot-pairs", {}))
|
|
136
|
+
});
|
|
137
|
+
api.registerTool({
|
|
138
|
+
name: "solana_market_regime",
|
|
139
|
+
description: "Get the current Solana market regime (bullish/bearish/neutral) with aggregate metrics like total DEX volume and trending sectors.",
|
|
140
|
+
parameters: Type.Object({}),
|
|
141
|
+
execute: wrapExecute(async () => post("/api/market/regime", {}))
|
|
142
|
+
});
|
|
143
|
+
api.registerTool({
|
|
144
|
+
name: "solana_token_snapshot",
|
|
145
|
+
description: "Get a price/volume snapshot for a Solana token including current price, 24h OHLC, volume, and trade count.",
|
|
146
|
+
parameters: Type.Object({
|
|
147
|
+
tokenAddress: Type.String({ description: "Solana token mint address" })
|
|
148
|
+
}),
|
|
149
|
+
execute: wrapExecute(
|
|
150
|
+
async (_id, params) => post("/api/token/snapshot", { tokenAddress: params.tokenAddress })
|
|
151
|
+
)
|
|
152
|
+
});
|
|
153
|
+
api.registerTool({
|
|
154
|
+
name: "solana_token_holders",
|
|
155
|
+
description: "Get holder distribution for a Solana token \u2014 top 10 concentration, dev holdings percentage, total holder count.",
|
|
156
|
+
parameters: Type.Object({
|
|
157
|
+
tokenAddress: Type.String({ description: "Solana token mint address" })
|
|
158
|
+
}),
|
|
159
|
+
execute: wrapExecute(
|
|
160
|
+
async (_id, params) => post("/api/token/holders", { tokenAddress: params.tokenAddress })
|
|
161
|
+
)
|
|
162
|
+
});
|
|
163
|
+
api.registerTool({
|
|
164
|
+
name: "solana_token_flows",
|
|
165
|
+
description: "Get buy/sell flow data for a Solana token \u2014 pressure ratio, net flow, unique trader count.",
|
|
166
|
+
parameters: Type.Object({
|
|
167
|
+
tokenAddress: Type.String({ description: "Solana token mint address" })
|
|
168
|
+
}),
|
|
169
|
+
execute: wrapExecute(
|
|
170
|
+
async (_id, params) => post("/api/token/flows", { tokenAddress: params.tokenAddress })
|
|
171
|
+
)
|
|
172
|
+
});
|
|
173
|
+
api.registerTool({
|
|
174
|
+
name: "solana_token_liquidity",
|
|
175
|
+
description: "Get liquidity profile for a Solana token \u2014 pool depth in USD, locked liquidity percentage, DEX breakdown.",
|
|
176
|
+
parameters: Type.Object({
|
|
177
|
+
tokenAddress: Type.String({ description: "Solana token mint address" })
|
|
178
|
+
}),
|
|
179
|
+
execute: wrapExecute(
|
|
180
|
+
async (_id, params) => post("/api/token/liquidity", { tokenAddress: params.tokenAddress })
|
|
181
|
+
)
|
|
182
|
+
});
|
|
183
|
+
api.registerTool({
|
|
184
|
+
name: "solana_token_risk",
|
|
185
|
+
description: "Get composite risk assessment for a Solana token \u2014 checks mint authority, freeze authority, LP lock/burn status, deployer history, concentration, dev holdings, and honeypot indicators. Hard-skip tokens with active mint or freeze authority.",
|
|
186
|
+
parameters: Type.Object({
|
|
187
|
+
tokenAddress: Type.String({ description: "Solana token mint address" })
|
|
188
|
+
}),
|
|
189
|
+
execute: wrapExecute(
|
|
190
|
+
async (_id, params) => post("/api/token/risk", { tokenAddress: params.tokenAddress })
|
|
191
|
+
)
|
|
192
|
+
});
|
|
193
|
+
api.registerTool({
|
|
194
|
+
name: "solana_build_thesis",
|
|
195
|
+
description: "Build a complete thesis package for a token \u2014 assembles market data, your strategy weights, your prior trades on this token, journal stats, wallet context, and an advisory risk pre-screen. This is your full intelligence briefing before making a trade decision.",
|
|
196
|
+
parameters: Type.Object({
|
|
197
|
+
tokenAddress: Type.String({ description: "Solana token mint address" }),
|
|
198
|
+
maxSizeSol: Type.Optional(Type.Number({ description: "Advisory \u2014 max position size in SOL for risk pre-screen. Not in server schema; accepted but currently ignored." }))
|
|
199
|
+
}),
|
|
200
|
+
execute: wrapExecute(
|
|
201
|
+
async (_id, params) => post("/api/thesis/build", {
|
|
202
|
+
tokenAddress: params.tokenAddress,
|
|
203
|
+
maxSizeSol: params.maxSizeSol
|
|
204
|
+
})
|
|
205
|
+
)
|
|
206
|
+
});
|
|
207
|
+
api.registerTool({
|
|
208
|
+
name: "solana_trade_precheck",
|
|
209
|
+
description: "Pre-trade risk check \u2014 validates a proposed trade against risk rules, kill switch, entitlement limits, and on-chain conditions. Returns approved/denied with reasons and capped size. Always call this before executing a trade.",
|
|
210
|
+
parameters: Type.Object({
|
|
211
|
+
tokenAddress: Type.String({ description: "Solana token mint address" }),
|
|
212
|
+
side: Type.Union([Type.Literal("buy"), Type.Literal("sell")], { description: "Trade direction" }),
|
|
213
|
+
sizeSol: Type.Number({ description: "Intended position size in SOL" }),
|
|
214
|
+
slippageBps: Type.Optional(Type.Number({ description: "Slippage tolerance in basis points (e.g., 300 = 3%)" }))
|
|
215
|
+
}),
|
|
216
|
+
execute: wrapExecute(
|
|
217
|
+
async (_id, params) => post("/api/trade/precheck", {
|
|
218
|
+
tokenAddress: params.tokenAddress,
|
|
219
|
+
side: params.side,
|
|
220
|
+
sizeSol: params.sizeSol,
|
|
221
|
+
slippageBps: params.slippageBps
|
|
222
|
+
})
|
|
223
|
+
)
|
|
224
|
+
});
|
|
225
|
+
api.registerTool({
|
|
226
|
+
name: "solana_trade_execute",
|
|
227
|
+
description: "Execute a trade on Solana via the SpyFly bot. Enforces risk rules before proxying to on-chain execution. Returns trade ID, position ID, and transaction signature.",
|
|
228
|
+
parameters: Type.Object({
|
|
229
|
+
tokenAddress: Type.String({ description: "Solana token mint address" }),
|
|
230
|
+
side: Type.Union([Type.Literal("buy"), Type.Literal("sell")], { description: "Trade direction" }),
|
|
231
|
+
sizeSol: Type.Number({ description: "Position size in SOL" }),
|
|
232
|
+
symbol: Type.String({ description: "Token symbol (e.g., BONK, WIF)" }),
|
|
233
|
+
slippageBps: Type.Optional(Type.Number({ description: "Slippage in basis points (default: 300)" })),
|
|
234
|
+
slPct: Type.Optional(Type.Number({ description: "Stop-loss percentage (e.g., 15 = 15% below entry)" })),
|
|
235
|
+
tpLevels: Type.Optional(Type.Array(Type.Number(), { description: "Take-profit levels as percentages (e.g., [25, 50, 100])" })),
|
|
236
|
+
trailingStopPct: Type.Optional(Type.Number({ description: "Trailing stop percentage" })),
|
|
237
|
+
managementMode: Type.Optional(
|
|
238
|
+
Type.Union([Type.Literal("LOCAL_MANAGED"), Type.Literal("SERVER_MANAGED")], {
|
|
239
|
+
description: "Advisory only \u2014 server decides position mode internally. Sent for future compatibility."
|
|
240
|
+
})
|
|
241
|
+
),
|
|
242
|
+
idempotencyKey: Type.Optional(Type.String({ description: "Unique key to prevent duplicate executions (e.g., UUID). Server uses walletId + key for replay cache." }))
|
|
243
|
+
}),
|
|
244
|
+
execute: wrapExecute(async (_id, params) => {
|
|
245
|
+
const headers = {};
|
|
246
|
+
if (params.idempotencyKey) {
|
|
247
|
+
headers["x-idempotency-key"] = params.idempotencyKey;
|
|
248
|
+
}
|
|
249
|
+
return post("/api/trade/execute", {
|
|
250
|
+
tokenAddress: params.tokenAddress,
|
|
251
|
+
side: params.side,
|
|
252
|
+
sizeSol: params.sizeSol,
|
|
253
|
+
symbol: params.symbol,
|
|
254
|
+
slippageBps: params.slippageBps,
|
|
255
|
+
slPct: params.slPct,
|
|
256
|
+
tpLevels: params.tpLevels,
|
|
257
|
+
trailingStopPct: params.trailingStopPct,
|
|
258
|
+
managementMode: params.managementMode
|
|
259
|
+
}, Object.keys(headers).length > 0 ? headers : void 0);
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
api.registerTool({
|
|
263
|
+
name: "solana_trade_review",
|
|
264
|
+
description: "Submit a post-trade review with outcome and notes. Creates a memory entry linked to the trade for future learning. Be honest \u2014 your future strategy evolution depends on accurate reviews.",
|
|
265
|
+
parameters: Type.Object({
|
|
266
|
+
tradeId: Type.Optional(Type.String({ description: "Trade ID (UUID) to review" })),
|
|
267
|
+
tokenAddress: Type.Optional(Type.String({ description: "Token mint address for the reviewed trade" })),
|
|
268
|
+
outcome: Type.Union([Type.Literal("win"), Type.Literal("loss"), Type.Literal("neutral")], {
|
|
269
|
+
description: "Trade outcome"
|
|
270
|
+
}),
|
|
271
|
+
notes: Type.String({ description: "Detailed analysis: what worked, what didn't, key signals, lessons learned" }),
|
|
272
|
+
pnlSol: Type.Optional(Type.Number({ description: "Actual profit/loss in SOL" })),
|
|
273
|
+
tags: Type.Optional(Type.Array(Type.String(), { description: "Tags for categorization (e.g., ['momentum_win', 'late_entry'])" })),
|
|
274
|
+
strategyVersion: Type.Optional(Type.String({ description: "Strategy version at time of trade (e.g., 'v1.3.0')" }))
|
|
275
|
+
}),
|
|
276
|
+
execute: wrapExecute(
|
|
277
|
+
async (_id, params) => post("/api/trade/review", {
|
|
278
|
+
tradeId: params.tradeId,
|
|
279
|
+
tokenAddress: params.tokenAddress,
|
|
280
|
+
outcome: params.outcome,
|
|
281
|
+
notes: params.notes,
|
|
282
|
+
pnlSol: params.pnlSol,
|
|
283
|
+
tags: params.tags,
|
|
284
|
+
strategyVersion: params.strategyVersion
|
|
285
|
+
})
|
|
286
|
+
)
|
|
287
|
+
});
|
|
288
|
+
api.registerTool({
|
|
289
|
+
name: "solana_memory_write",
|
|
290
|
+
description: "Write a memory entry \u2014 journal observations, market insights, or trading lessons. These memories are searchable and appear in future thesis packages.",
|
|
291
|
+
parameters: Type.Object({
|
|
292
|
+
notes: Type.String({ description: "Observation or lesson to remember" }),
|
|
293
|
+
tags: Type.Optional(Type.Array(Type.String(), { description: "Tags for categorization (e.g., ['momentum', 'risk', 'regime'])" })),
|
|
294
|
+
tokenAddress: Type.Optional(Type.String({ description: "Associate with a specific token" })),
|
|
295
|
+
outcome: Type.Optional(Type.Union([Type.Literal("win"), Type.Literal("loss"), Type.Literal("neutral")], {
|
|
296
|
+
description: "Outcome if trade-related"
|
|
297
|
+
})),
|
|
298
|
+
strategyVersion: Type.Optional(Type.String({ description: "Strategy version at time of writing (e.g., 'v1.3.0')" }))
|
|
299
|
+
}),
|
|
300
|
+
execute: wrapExecute(
|
|
301
|
+
async (_id, params) => post("/api/memory/write", {
|
|
302
|
+
notes: params.notes,
|
|
303
|
+
tags: params.tags,
|
|
304
|
+
tokenAddress: params.tokenAddress,
|
|
305
|
+
outcome: params.outcome,
|
|
306
|
+
strategyVersion: params.strategyVersion
|
|
307
|
+
})
|
|
308
|
+
)
|
|
309
|
+
});
|
|
310
|
+
api.registerTool({
|
|
311
|
+
name: "solana_memory_search",
|
|
312
|
+
description: "Search your trading memory by text query. Returns matching journal entries, trade reviews, and observations.",
|
|
313
|
+
parameters: Type.Object({
|
|
314
|
+
query: Type.String({ description: "Search text (e.g., 'high concentration tokens' or 'momentum plays')" }),
|
|
315
|
+
limit: Type.Optional(Type.Number({ description: "Advisory \u2014 max results to return. Not honored by server; storage applies internal cap (~50)." }))
|
|
316
|
+
}),
|
|
317
|
+
execute: wrapExecute(
|
|
318
|
+
async (_id, params) => post("/api/memory/search", {
|
|
319
|
+
query: params.query,
|
|
320
|
+
limit: params.limit
|
|
321
|
+
})
|
|
322
|
+
)
|
|
323
|
+
});
|
|
324
|
+
api.registerTool({
|
|
325
|
+
name: "solana_memory_by_token",
|
|
326
|
+
description: "Get all your prior memory entries for a specific token \u2014 past trades, reviews, and observations. MANDATORY: always call this before re-entering any token you've previously traded. Required by risk rules.",
|
|
327
|
+
parameters: Type.Object({
|
|
328
|
+
tokenAddress: Type.String({ description: "Solana token mint address" })
|
|
329
|
+
}),
|
|
330
|
+
execute: wrapExecute(
|
|
331
|
+
async (_id, params) => post("/api/memory/by-token", {
|
|
332
|
+
tokenAddress: params.tokenAddress
|
|
333
|
+
})
|
|
334
|
+
)
|
|
335
|
+
});
|
|
336
|
+
api.registerTool({
|
|
337
|
+
name: "solana_journal_summary",
|
|
338
|
+
description: "Get a summary of your trading journal \u2014 win rate, total entries, recent notes, and performance over a time period.",
|
|
339
|
+
parameters: Type.Object({
|
|
340
|
+
days: Type.Optional(Type.Number({ description: "Look back period in days (default: 7)" }))
|
|
341
|
+
}),
|
|
342
|
+
execute: wrapExecute(async (_id, params) => {
|
|
343
|
+
let path = `/api/memory/journal-summary?walletId=${walletId}`;
|
|
344
|
+
if (params.days) path += `&lookbackDays=${params.days}`;
|
|
345
|
+
return get(path);
|
|
346
|
+
})
|
|
347
|
+
});
|
|
348
|
+
api.registerTool({
|
|
349
|
+
name: "solana_strategy_state",
|
|
350
|
+
description: "Read your current strategy state \u2014 feature weights and strategy version. These are YOUR learned preferences that evolve over time.",
|
|
351
|
+
parameters: Type.Object({}),
|
|
352
|
+
execute: wrapExecute(
|
|
353
|
+
async () => get(`/api/strategy/state?walletId=${walletId}`)
|
|
354
|
+
)
|
|
355
|
+
});
|
|
356
|
+
api.registerTool({
|
|
357
|
+
name: "solana_strategy_update",
|
|
358
|
+
description: "Update your strategy weights and/or operating mode. Weights reflect which market signals best predict winners. Server enforces guardrails: min 3 features, each weight 0.01\u20130.50, sum 0.95\u20131.05, max \xB10.20 delta per feature, semver format required, version must increment. Always increment strategyVersion.",
|
|
359
|
+
parameters: Type.Object({
|
|
360
|
+
featureWeights: Type.Record(Type.String(), Type.Number(), {
|
|
361
|
+
description: "Feature weight map (e.g., { volume_momentum: 0.25, buy_pressure: 0.20, ... }). Values should sum to ~1.0"
|
|
362
|
+
}),
|
|
363
|
+
strategyVersion: Type.String({ description: "New version string (e.g., 'v1.3.0'). Always increment from current." }),
|
|
364
|
+
mode: Type.Optional(
|
|
365
|
+
Type.Union([Type.Literal("HARDENED"), Type.Literal("DEGEN")], {
|
|
366
|
+
description: "Operating mode. HARDENED = survival-first, DEGEN = high-velocity. Default: HARDENED"
|
|
367
|
+
})
|
|
368
|
+
)
|
|
369
|
+
}),
|
|
370
|
+
execute: wrapExecute(
|
|
371
|
+
async (_id, params) => post("/api/strategy/update", {
|
|
372
|
+
featureWeights: params.featureWeights,
|
|
373
|
+
strategyVersion: params.strategyVersion,
|
|
374
|
+
mode: params.mode
|
|
375
|
+
})
|
|
376
|
+
)
|
|
377
|
+
});
|
|
378
|
+
api.registerTool({
|
|
379
|
+
name: "solana_killswitch",
|
|
380
|
+
description: "Toggle the emergency kill switch. When enabled, ALL trade execution is blocked. Use in emergencies: repeated losses, unusual market behavior, or security concerns.",
|
|
381
|
+
parameters: Type.Object({
|
|
382
|
+
enabled: Type.Boolean({ description: "true to activate (block all trades), false to deactivate" }),
|
|
383
|
+
mode: Type.Optional(
|
|
384
|
+
Type.Union([Type.Literal("TRADES_ONLY"), Type.Literal("TRADES_AND_STREAMS")], {
|
|
385
|
+
description: "TRADES_ONLY blocks execution; TRADES_AND_STREAMS blocks everything"
|
|
386
|
+
})
|
|
387
|
+
)
|
|
388
|
+
}),
|
|
389
|
+
execute: wrapExecute(
|
|
390
|
+
async (_id, params) => post("/api/killswitch", {
|
|
391
|
+
enabled: params.enabled,
|
|
392
|
+
mode: params.mode
|
|
393
|
+
})
|
|
394
|
+
)
|
|
395
|
+
});
|
|
396
|
+
api.registerTool({
|
|
397
|
+
name: "solana_killswitch_status",
|
|
398
|
+
description: "Check the current kill switch state \u2014 whether it's enabled and in what mode.",
|
|
399
|
+
parameters: Type.Object({}),
|
|
400
|
+
execute: wrapExecute(
|
|
401
|
+
async () => get(`/api/killswitch/status?walletId=${walletId}`)
|
|
402
|
+
)
|
|
403
|
+
});
|
|
404
|
+
api.registerTool({
|
|
405
|
+
name: "solana_capital_status",
|
|
406
|
+
description: "Get your current capital status \u2014 SOL balance, open position count, unrealized PnL, daily notional used, daily loss, and effective limits (adjusted by entitlements).",
|
|
407
|
+
parameters: Type.Object({}),
|
|
408
|
+
execute: wrapExecute(
|
|
409
|
+
async () => get(`/api/capital/status?walletId=${walletId}`)
|
|
410
|
+
)
|
|
411
|
+
});
|
|
412
|
+
api.registerTool({
|
|
413
|
+
name: "solana_positions",
|
|
414
|
+
description: "List your current trading positions with unrealized PnL, entry price, current price, stop-loss/take-profit settings, and management mode. Call at the START of every trading cycle for interrupt check. Also use to detect dead money (flat positions).",
|
|
415
|
+
parameters: Type.Object({
|
|
416
|
+
status: Type.Optional(Type.String({ description: "Filter by status: 'open', 'closed', or omit for all" }))
|
|
417
|
+
}),
|
|
418
|
+
execute: wrapExecute(async (_id, params) => {
|
|
419
|
+
let path = `/api/wallet/positions?walletId=${walletId}`;
|
|
420
|
+
if (params.status) path += `&status=${params.status}`;
|
|
421
|
+
return get(path);
|
|
422
|
+
})
|
|
423
|
+
});
|
|
424
|
+
api.registerTool({
|
|
425
|
+
name: "solana_funding_instructions",
|
|
426
|
+
description: "Get deposit instructions for funding your trading wallet with SOL.",
|
|
427
|
+
parameters: Type.Object({}),
|
|
428
|
+
execute: wrapExecute(
|
|
429
|
+
async () => get(`/api/funding/instructions?walletId=${walletId}`)
|
|
430
|
+
)
|
|
431
|
+
});
|
|
432
|
+
api.registerTool({
|
|
433
|
+
name: "solana_wallets",
|
|
434
|
+
description: "List all wallets associated with your account. Optionally refresh balances from on-chain.",
|
|
435
|
+
parameters: Type.Object({
|
|
436
|
+
refresh: Type.Optional(Type.Boolean({ description: "If true, refresh balances from on-chain before returning" }))
|
|
437
|
+
}),
|
|
438
|
+
execute: wrapExecute(async (_id, params) => {
|
|
439
|
+
let path = "/api/wallets";
|
|
440
|
+
if (params.refresh) path += "?refresh=true";
|
|
441
|
+
return get(path);
|
|
442
|
+
})
|
|
443
|
+
});
|
|
444
|
+
api.registerTool({
|
|
445
|
+
name: "solana_wallet_create",
|
|
446
|
+
description: "Create a new trading wallet. Returns the wallet ID and public key. Use this to provision additional wallets for strategy isolation or multi-wallet trading.",
|
|
447
|
+
parameters: Type.Object({
|
|
448
|
+
label: Type.Optional(Type.String({ description: "Human-readable label for the wallet (e.g., 'Degen Wallet')" })),
|
|
449
|
+
publicKey: Type.Optional(Type.String({ description: "Existing Solana public key to import (omit to generate new)" })),
|
|
450
|
+
chain: Type.Optional(Type.Union([Type.Literal("solana"), Type.Literal("bsc")], { description: "Blockchain (default: solana)" })),
|
|
451
|
+
ownerRef: Type.Optional(Type.String({ description: "Owner reference string" })),
|
|
452
|
+
includePrivateKey: Type.Optional(Type.Boolean({ description: "If true, return the private key in the response (only for newly generated wallets)" }))
|
|
453
|
+
}),
|
|
454
|
+
execute: wrapExecute(
|
|
455
|
+
async (_id, params) => post("/api/wallet/create", {
|
|
456
|
+
label: params.label,
|
|
457
|
+
publicKey: params.publicKey,
|
|
458
|
+
chain: params.chain,
|
|
459
|
+
ownerRef: params.ownerRef,
|
|
460
|
+
includePrivateKey: params.includePrivateKey
|
|
461
|
+
})
|
|
462
|
+
)
|
|
463
|
+
});
|
|
464
|
+
api.registerTool({
|
|
465
|
+
name: "solana_trades",
|
|
466
|
+
description: "List your trade history with pagination. Returns executed trades with details like token, side, size, PnL, and timestamp.",
|
|
467
|
+
parameters: Type.Object({
|
|
468
|
+
limit: Type.Optional(Type.Number({ description: "Max trades to return (1-200, default: 50)" })),
|
|
469
|
+
offset: Type.Optional(Type.Number({ description: "Offset for pagination (default: 0)" }))
|
|
470
|
+
}),
|
|
471
|
+
execute: wrapExecute(async (_id, params) => {
|
|
472
|
+
let path = `/api/trades?walletId=${walletId}`;
|
|
473
|
+
if (params.limit) path += `&limit=${params.limit}`;
|
|
474
|
+
if (params.offset) path += `&offset=${params.offset}`;
|
|
475
|
+
return get(path);
|
|
476
|
+
})
|
|
477
|
+
});
|
|
478
|
+
api.registerTool({
|
|
479
|
+
name: "solana_risk_denials",
|
|
480
|
+
description: "List recent risk denials \u2014 trades that were blocked by the policy engine. Review these to understand what setups trigger denials and avoid repeating wasted analysis.",
|
|
481
|
+
parameters: Type.Object({
|
|
482
|
+
limit: Type.Optional(Type.Number({ description: "Max denials to return (1-200, default: 50)" }))
|
|
483
|
+
}),
|
|
484
|
+
execute: wrapExecute(async (_id, params) => {
|
|
485
|
+
let path = `/api/risk-denials?walletId=${walletId}`;
|
|
486
|
+
if (params.limit) path += `&limit=${params.limit}`;
|
|
487
|
+
return get(path);
|
|
488
|
+
})
|
|
489
|
+
});
|
|
490
|
+
api.registerTool({
|
|
491
|
+
name: "solana_entitlement_costs",
|
|
492
|
+
description: "Get tier costs \u2014 what each tier (starter, pro, enterprise) costs and what capabilities it unlocks.",
|
|
493
|
+
parameters: Type.Object({}),
|
|
494
|
+
execute: wrapExecute(async () => get("/api/entitlements/costs"))
|
|
495
|
+
});
|
|
496
|
+
api.registerTool({
|
|
497
|
+
name: "solana_entitlement_plans",
|
|
498
|
+
description: "List available monthly entitlement plans that upgrade your trading limits (position size, daily notional, bandwidth). Shows price, duration, and limit boosts.",
|
|
499
|
+
parameters: Type.Object({}),
|
|
500
|
+
execute: wrapExecute(async () => get("/api/entitlements/plans"))
|
|
501
|
+
});
|
|
502
|
+
api.registerTool({
|
|
503
|
+
name: "solana_entitlement_current",
|
|
504
|
+
description: "Get your current entitlements \u2014 active tier, scope access, effective limits, and expiration details.",
|
|
505
|
+
parameters: Type.Object({}),
|
|
506
|
+
execute: wrapExecute(
|
|
507
|
+
async () => get(`/api/entitlements/current?walletId=${walletId}`)
|
|
508
|
+
)
|
|
509
|
+
});
|
|
510
|
+
api.registerTool({
|
|
511
|
+
name: "solana_entitlement_purchase",
|
|
512
|
+
description: "Purchase an entitlement plan to upgrade your trading limits. Deducts SOL from your wallet balance. Subject to spend guardrails (daily max, per-upgrade max, cooldown).",
|
|
513
|
+
parameters: Type.Object({
|
|
514
|
+
planCode: Type.String({ description: "Plan code to purchase (e.g., 'pro_trader', 'bandwidth_boost')" })
|
|
515
|
+
}),
|
|
516
|
+
execute: wrapExecute(
|
|
517
|
+
async (_id, params) => post("/api/entitlements/purchase", {
|
|
518
|
+
planCode: params.planCode
|
|
519
|
+
})
|
|
520
|
+
)
|
|
521
|
+
});
|
|
522
|
+
api.registerTool({
|
|
523
|
+
name: "solana_entitlement_upgrade",
|
|
524
|
+
description: "Upgrade your account tier (starter \u2192 pro \u2192 enterprise). Unlocks additional endpoints and capabilities. Pro tier is required for scanning, token analysis, and Bitquery tools.",
|
|
525
|
+
parameters: Type.Object({
|
|
526
|
+
targetTier: Type.Union([Type.Literal("starter"), Type.Literal("pro"), Type.Literal("enterprise")], {
|
|
527
|
+
description: "Target tier to upgrade to"
|
|
528
|
+
})
|
|
529
|
+
}),
|
|
530
|
+
execute: wrapExecute(
|
|
531
|
+
async (_id, params) => post("/api/entitlements/upgrade", {
|
|
532
|
+
targetTier: params.targetTier
|
|
533
|
+
})
|
|
534
|
+
)
|
|
535
|
+
});
|
|
536
|
+
api.registerTool({
|
|
537
|
+
name: "solana_bitquery_templates",
|
|
538
|
+
description: "List all available pre-built Bitquery query templates with descriptions and required variables. Call this first to discover what templates are available before using solana_bitquery_catalog. Returns 50+ templates organized by category covering Pump.fun, PumpSwap, Raydium, Jupiter, BonkSwap, and generic DEX analytics.",
|
|
539
|
+
parameters: Type.Object({}),
|
|
540
|
+
execute: wrapExecute(async () => ({
|
|
541
|
+
categories: {
|
|
542
|
+
pumpFunCreation: [
|
|
543
|
+
{ path: "pumpFunCreation.trackNewTokens", description: "Track newly created Pump.fun tokens", variables: { since: "DateTime!", limit: "Int!" } },
|
|
544
|
+
{ path: "pumpFunCreation.getCreationTimeAndDev", description: "Get creation time and dev address for token", variables: { token: "String!" } },
|
|
545
|
+
{ path: "pumpFunCreation.trackLaunchesRealtime", description: "Track new token launches in real-time via query polling", variables: { since: "DateTime!", limit: "Int!" } },
|
|
546
|
+
{ path: "pumpFunCreation.getTokensByCreatorAddress", description: "Get all Pump.fun tokens created by creator wallet", variables: { creator: "String!", limit: "Int!" } },
|
|
547
|
+
{ path: "pumpFunCreation.getTokensByCreatorHistorical", description: "Historical token creations by wallet", variables: { creator: "String!", since: "DateTime!", till: "DateTime!" } }
|
|
548
|
+
],
|
|
549
|
+
pumpFunMetadata: [
|
|
550
|
+
{ path: "pumpFunMetadata.tokenMetadataByAddress", description: "Get token metadata plus dev and creation time", variables: { token: "String!" } },
|
|
551
|
+
{ path: "pumpFunMetadata.trackMayhemModeRealtime", description: "Track Mayhem Mode enabled tokens in real-time", variables: { since: "DateTime!", limit: "Int!" } },
|
|
552
|
+
{ path: "pumpFunMetadata.currentMayhemModeStatus", description: "Check current Mayhem mode status for token", variables: { token: "String!" } },
|
|
553
|
+
{ path: "pumpFunMetadata.historicalMayhemModeStatus", description: "Historical mayhem mode changes for token", variables: { token: "String!", since: "DateTime!", till: "DateTime!" } },
|
|
554
|
+
{ path: "pumpFunMetadata.latestPrice", description: "Latest price for Pump.fun token", variables: { token: "String!" } }
|
|
555
|
+
],
|
|
556
|
+
pumpFunPriceMomentum: [
|
|
557
|
+
{ path: "pumpFunPriceMomentum.streamTokenPrice", description: "Price stream query for polling mode", variables: { token: "String!", since: "DateTime!" } },
|
|
558
|
+
{ path: "pumpFunPriceMomentum.top10PriceChange5m", description: "Top 10 by short-term price change", variables: { since: "DateTime!" } },
|
|
559
|
+
{ path: "pumpFunPriceMomentum.tokenOHLC", description: "OHLC data for Pump.fun token", variables: { token: "String!", since: "DateTime!" } },
|
|
560
|
+
{ path: "pumpFunPriceMomentum.athMarketCapWindow", description: "ATH market cap in window", variables: { token: "String!", since: "DateTime!", till: "DateTime!" } },
|
|
561
|
+
{ path: "pumpFunPriceMomentum.priceChangeDeltaFromMinutesAgo", description: "Price-change delta from X minutes back", variables: { token: "String!", since: "DateTime!" } }
|
|
562
|
+
],
|
|
563
|
+
pumpFunTradesLiquidity: [
|
|
564
|
+
{ path: "pumpFunTradesLiquidity.realtimeTrades", description: "Get real-time trades on Pump.fun", variables: { since: "DateTime!", limit: "Int!" } },
|
|
565
|
+
{ path: "pumpFunTradesLiquidity.latestTradesByToken", description: "Latest trades by token", variables: { token: "String!", limit: "Int!" } },
|
|
566
|
+
{ path: "pumpFunTradesLiquidity.tradingVolume", description: "Get trading volume for token", variables: { token: "String!", since: "DateTime!" } },
|
|
567
|
+
{ path: "pumpFunTradesLiquidity.detailedTradeStats", description: "Detailed trade stats (volume/buys/sells/makers/buyers/sellers)", variables: { token: "String!", since: "DateTime!" } },
|
|
568
|
+
{ path: "pumpFunTradesLiquidity.lastTradeBeforeMigration", description: "Last Pump.fun trade before migration to PumpSwap", variables: { token: "String!" } }
|
|
569
|
+
],
|
|
570
|
+
pumpFunHoldersRisk: [
|
|
571
|
+
{ path: "pumpFunHoldersRisk.first100Buyers", description: "Get first 100 buyers", variables: { token: "String!" } },
|
|
572
|
+
{ path: "pumpFunHoldersRisk.first100StillHolding", description: "Check whether first 100 buyers still hold", variables: { holders: "[String!]", token: "String!" } },
|
|
573
|
+
{ path: "pumpFunHoldersRisk.devHoldings", description: "Get developer holdings for token", variables: { devWallet: "String!", token: "String!" } },
|
|
574
|
+
{ path: "pumpFunHoldersRisk.topHoldersTopTradersTopCreators", description: "Get top holders/top traders/top creators", variables: { token: "String!", since: "DateTime!" } },
|
|
575
|
+
{ path: "pumpFunHoldersRisk.phishyAndMarketCapFilters", description: "Phishy check + market cap filter scaffolding", variables: { since: "DateTime!", minCap: "String!", maxCap: "String!" } }
|
|
576
|
+
],
|
|
577
|
+
pumpSwapPostMigration: [
|
|
578
|
+
{ path: "pumpSwapPostMigration.newPoolsRealtime", description: "Get newly created PumpSwap pools", variables: { since: "DateTime!", limit: "Int!" } },
|
|
579
|
+
{ path: "pumpSwapPostMigration.trackMigratedPools", description: "Track pools migrated to PumpSwap", variables: { since: "DateTime!", limit: "Int!" } },
|
|
580
|
+
{ path: "pumpSwapPostMigration.latestTrades", description: "Get latest trades on PumpSwap", variables: { since: "DateTime!", limit: "Int!" } },
|
|
581
|
+
{ path: "pumpSwapPostMigration.latestTradesByToken", description: "Latest PumpSwap trades for token", variables: { token: "String!", limit: "Int!" } },
|
|
582
|
+
{ path: "pumpSwapPostMigration.pumpSwapSubscriptionScaffold", description: "Query mirror for PumpSwap realtime subscription", variables: { since: "DateTime!" } }
|
|
583
|
+
],
|
|
584
|
+
pumpSwapPriceTrader: [
|
|
585
|
+
{ path: "pumpSwapPriceTrader.trackTokenPriceRealtime", description: "Track PumpSwap token price realtime", variables: { token: "String!", since: "DateTime!" } },
|
|
586
|
+
{ path: "pumpSwapPriceTrader.latestPrice", description: "Get latest price for PumpSwap token", variables: { token: "String!" } },
|
|
587
|
+
{ path: "pumpSwapPriceTrader.ohlc", description: "OHLC for PumpSwap token", variables: { token: "String!", since: "DateTime!" } },
|
|
588
|
+
{ path: "pumpSwapPriceTrader.latestTradesByTrader", description: "Get latest trades by trader", variables: { wallet: "String!", since: "DateTime!" } },
|
|
589
|
+
{ path: "pumpSwapPriceTrader.topTradersAndStats", description: "Top traders and token trade stats", variables: { token: "String!", since: "DateTime!" } }
|
|
590
|
+
],
|
|
591
|
+
launchpadsRaydiumLetsBonk: [
|
|
592
|
+
{ path: "launchpadsRaydiumLetsBonk.latestRaydiumLaunchpadPools", description: "Track latest pools created on Raydium Launchpad", variables: { since: "DateTime!", limit: "Int!" } },
|
|
593
|
+
{ path: "launchpadsRaydiumLetsBonk.trackMigrationsToRaydium", description: "Track migrations to Raydium DEX/CPMM across launchpads", variables: { since: "DateTime!", limit: "Int!" } },
|
|
594
|
+
{ path: "launchpadsRaydiumLetsBonk.bondingCurveProgress", description: "Compute bonding curve progress from latest pool/liquidity snapshot", variables: { token: "String!", since: "DateTime!" } },
|
|
595
|
+
{ path: "launchpadsRaydiumLetsBonk.tokensAbove95Progress", description: "Track launchpad tokens above 95% bonding curve progress", variables: { since: "DateTime!", limit: "Int!" } },
|
|
596
|
+
{ path: "launchpadsRaydiumLetsBonk.top100AboutToGraduate", description: "Top 100 launchpad tokens near migration", variables: { since: "DateTime!" } }
|
|
597
|
+
],
|
|
598
|
+
launchpadsTokenLevel: [
|
|
599
|
+
{ path: "launchpadsTokenLevel.latestLaunchpadTrades", description: "Get latest launchpad trades", variables: { since: "DateTime!", limit: "Int!" } },
|
|
600
|
+
{ path: "launchpadsTokenLevel.latestPriceForToken", description: "Get latest price for launchpad token", variables: { token: "String!" } },
|
|
601
|
+
{ path: "launchpadsTokenLevel.latestTradesByUser", description: "Get latest trades by user", variables: { wallet: "String!", since: "DateTime!" } },
|
|
602
|
+
{ path: "launchpadsTokenLevel.topBuyersAndSellers", description: "Get top buyers and top sellers for token", variables: { token: "String!", since: "DateTime!" } },
|
|
603
|
+
{ path: "launchpadsTokenLevel.ohlcPairAndLiquidity", description: "Get OHLC, pair address and latest liquidity", variables: { token: "String!", since: "DateTime!" } }
|
|
604
|
+
],
|
|
605
|
+
exchangeSpecific: [
|
|
606
|
+
{ path: "exchangeSpecific.raydiumSuite", description: "Raydium: pools, pair create time, latest price, trades, LP changes, OHLC", variables: { token: "String!", since: "DateTime!" } },
|
|
607
|
+
{ path: "exchangeSpecific.bonkSwapSuite", description: "BonkSwap: latest trades, top traders, trader feed, OHLC", variables: { token: "String!", wallet: "String!", since: "DateTime!" } },
|
|
608
|
+
{ path: "exchangeSpecific.jupiterSuite", description: "Jupiter swaps and order lifecycle query suite", variables: { since: "DateTime!" } },
|
|
609
|
+
{ path: "exchangeSpecific.jupiterStudioSuite", description: "Jupiter Studio token trades, prices, OHLC, launches, migrations", variables: { since: "DateTime!", token: "String" } }
|
|
610
|
+
],
|
|
611
|
+
genericDexAnalytics: [
|
|
612
|
+
{ path: "genericDexAnalytics.latestSolanaTrades", description: "Subscribe/query latest Solana trades", variables: { since: "DateTime!", limit: "Int!" } },
|
|
613
|
+
{ path: "genericDexAnalytics.priceVsWsolUsdMultiMarket", description: "Token price vs WSOL/USD and multi-market", variables: { token: "String!", since: "DateTime!" } },
|
|
614
|
+
{ path: "genericDexAnalytics.pressureTopsAndDexs", description: "Buy/sell pressure and top-bought/top-sold/pairs/dexs", variables: { since: "DateTime!", limit: "Int!" } },
|
|
615
|
+
{ path: "genericDexAnalytics.dexMarketsPairsTokenDetails", description: "DEX markets/pairs/token details", variables: { token: "String!", since: "DateTime!" } },
|
|
616
|
+
{ path: "genericDexAnalytics.ohlcHistoryAthTrendSearch", description: "OHLC history, ATH, first-24h, trend, search", variables: { token: "String!", since: "DateTime!" } }
|
|
617
|
+
]
|
|
618
|
+
},
|
|
619
|
+
subscriptions: [
|
|
620
|
+
{ key: "realtimeTokenPricesSolana", description: "Real-time token prices on Solana", variables: { token: "String!" } },
|
|
621
|
+
{ key: "ohlc1s", description: "1-second OHLC stream", variables: { token: "String!" } },
|
|
622
|
+
{ key: "dexPoolLiquidityChanges", description: "DEXPool liquidity changes stream", variables: { token: "String!" } },
|
|
623
|
+
{ key: "pumpFunTokenCreation", description: "Pump.fun token creation stream", variables: {} },
|
|
624
|
+
{ key: "pumpFunTrades", description: "Pump.fun trades stream", variables: { token: "String" } },
|
|
625
|
+
{ key: "pumpSwapTrades", description: "PumpSwap trades stream", variables: { token: "String" } },
|
|
626
|
+
{ key: "raydiumNewPools", description: "Raydium v4/Launchpad/CLMM new pools stream", variables: {} }
|
|
627
|
+
],
|
|
628
|
+
totalTemplates: 54,
|
|
629
|
+
totalSubscriptions: 7,
|
|
630
|
+
usage: "Use solana_bitquery_catalog with templatePath and variables to run any template. For custom queries, use solana_bitquery_query with raw GraphQL."
|
|
631
|
+
}))
|
|
632
|
+
});
|
|
633
|
+
api.registerTool({
|
|
634
|
+
name: "solana_bitquery_catalog",
|
|
635
|
+
description: "Run a pre-built Bitquery query template from the catalog. Use solana_bitquery_templates first to discover available templates. Templates cover Pump.fun creation/metadata/price/trades/holders, PumpSwap post-migration, launchpad analytics, exchange-specific suites (Raydium/Jupiter/BonkSwap), and generic DEX analytics. See query-catalog.md in the solana-trader skill for the full reference.",
|
|
636
|
+
parameters: Type.Object({
|
|
637
|
+
templatePath: Type.String({ description: "Template path in 'category.key' format (e.g., 'pumpFunHoldersRisk.first100Buyers')" }),
|
|
638
|
+
variables: Type.Object({}, { additionalProperties: true, description: "Variables required by the template (e.g., { token: 'MINT_ADDRESS', since: '2025-01-01T00:00:00Z' })" })
|
|
639
|
+
}),
|
|
640
|
+
execute: wrapExecute(
|
|
641
|
+
async (_id, params) => post("/api/bitquery/catalog", {
|
|
642
|
+
templatePath: params.templatePath,
|
|
643
|
+
variables: params.variables || {}
|
|
644
|
+
})
|
|
645
|
+
)
|
|
646
|
+
});
|
|
647
|
+
api.registerTool({
|
|
648
|
+
name: "solana_bitquery_query",
|
|
649
|
+
description: "Run a custom raw GraphQL query against the Bitquery v2 EAP endpoint for Solana on-chain data. Use this when no pre-built template fits your needs. IMPORTANT: Consult bitquery-schema.md in the solana-trader skill before writing queries \u2014 DEXTrades and DEXTradeByTokens have different field shapes and mixing them causes errors. The schema reference includes a decision guide, correct field paths, aggregate keys, and a common error fix map.",
|
|
650
|
+
parameters: Type.Object({
|
|
651
|
+
query: Type.String({ description: "Raw GraphQL query string (query or subscription operation)" }),
|
|
652
|
+
variables: Type.Optional(Type.Object({}, { additionalProperties: true, description: "GraphQL variables (e.g., { token: 'MINT_ADDRESS', since: '2025-01-01T00:00:00Z' })" }))
|
|
653
|
+
}),
|
|
654
|
+
execute: wrapExecute(
|
|
655
|
+
async (_id, params) => post("/api/bitquery/query", {
|
|
656
|
+
query: params.query,
|
|
657
|
+
variables: params.variables || {}
|
|
658
|
+
})
|
|
659
|
+
)
|
|
660
|
+
});
|
|
661
|
+
api.registerTool({
|
|
662
|
+
name: "solana_bitquery_subscribe",
|
|
663
|
+
description: "Subscribe to a managed real-time Bitquery data stream. The orchestrator manages the WebSocket connection and broadcasts events. Available templates: realtimeTokenPricesSolana, ohlc1s, dexPoolLiquidityChanges, pumpFunTokenCreation, pumpFunTrades, pumpSwapTrades, raydiumNewPools. Returns a subscriptionId for tracking. Pass agentId to enable event-to-agent forwarding \u2014 orchestrator delivers each event to your Gateway via /v1/responses in addition to normal WS delivery. Subscriptions expire after 24h and emit subscription_expiring/subscription_expired events. See websocket-streaming.md in the solana-trader skill for the full message contract and usage patterns.",
|
|
664
|
+
parameters: Type.Object({
|
|
665
|
+
templateKey: Type.String({ description: "Subscription template key (e.g., 'pumpFunTrades', 'ohlc1s', 'realtimeTokenPricesSolana')" }),
|
|
666
|
+
variables: Type.Optional(Type.Object({}, { additionalProperties: true, description: "Template variables (e.g., { token: 'MINT_ADDRESS' })" })),
|
|
667
|
+
agentId: Type.Optional(Type.String({ description: "Agent ID for event-to-agent forwarding (e.g., 'main'). When set, orchestrator forwards each stream event to your registered Gateway via /v1/responses." })),
|
|
668
|
+
subscriberType: Type.Optional(Type.Union([Type.Literal("agent"), Type.Literal("client")], { description: "Subscriber type. Inferred as 'agent' when agentId is present. Defaults to 'client'." }))
|
|
669
|
+
}),
|
|
670
|
+
execute: wrapExecute(async (_id, params) => {
|
|
671
|
+
const body = {
|
|
672
|
+
templateKey: params.templateKey,
|
|
673
|
+
variables: params.variables || {}
|
|
674
|
+
};
|
|
675
|
+
const effectiveAgentId = params.agentId || config.agentId;
|
|
676
|
+
if (effectiveAgentId) {
|
|
677
|
+
body.agentId = effectiveAgentId;
|
|
678
|
+
body.subscriberType = params.subscriberType || "agent";
|
|
679
|
+
} else if (params.subscriberType) {
|
|
680
|
+
body.subscriberType = params.subscriberType;
|
|
681
|
+
}
|
|
682
|
+
return post("/api/bitquery/subscribe", body);
|
|
683
|
+
})
|
|
684
|
+
});
|
|
685
|
+
api.registerTool({
|
|
686
|
+
name: "solana_bitquery_unsubscribe",
|
|
687
|
+
description: "Unsubscribe from a managed Bitquery data stream. Pass the subscriptionId returned by solana_bitquery_subscribe. Important: always use the server-returned subscriptionId, never generate your own.",
|
|
688
|
+
parameters: Type.Object({
|
|
689
|
+
subscriptionId: Type.String({ description: "Subscription ID returned by solana_bitquery_subscribe (e.g., 'bqs_abc123...')" })
|
|
690
|
+
}),
|
|
691
|
+
execute: wrapExecute(
|
|
692
|
+
async (_id, params) => post("/api/bitquery/unsubscribe", {
|
|
693
|
+
subscriptionId: params.subscriptionId
|
|
694
|
+
})
|
|
695
|
+
)
|
|
696
|
+
});
|
|
697
|
+
api.registerTool({
|
|
698
|
+
name: "solana_bitquery_subscriptions",
|
|
699
|
+
description: "List all active Bitquery subscriptions and bridge diagnostics. Returns connected clients, active streams, upstream connection status, and per-stream subscriber counts. Use for monitoring real-time data feed health.",
|
|
700
|
+
parameters: Type.Object({}),
|
|
701
|
+
execute: wrapExecute(
|
|
702
|
+
async () => get("/api/bitquery/subscriptions/active")
|
|
703
|
+
)
|
|
704
|
+
});
|
|
705
|
+
api.registerTool({
|
|
706
|
+
name: "solana_bitquery_subscription_reopen",
|
|
707
|
+
description: "Reopen an expired or expiring Bitquery subscription. Subscriptions have a 24h TTL and emit bitquery_subscription_expiring (30 min warning), bitquery_subscription_expired, and reconnect_required events. Call this to renew before or after expiry. The subscription_cleanup cron job handles this automatically, but manual reopen is available for critical subscriptions. Returns the new subscriptionId.",
|
|
708
|
+
parameters: Type.Object({
|
|
709
|
+
subscriptionId: Type.String({ description: "The expired or expiring subscription ID to reopen (e.g., 'bqs_abc123...')" }),
|
|
710
|
+
walletId: Type.Optional(Type.String({ description: "Wallet ID to reopen the subscription for. Defaults to the plugin's configured walletId." }))
|
|
711
|
+
}),
|
|
712
|
+
execute: wrapExecute(
|
|
713
|
+
async (_id, params) => post("/api/bitquery/subscriptions/reopen", {
|
|
714
|
+
subscriptionId: params.subscriptionId,
|
|
715
|
+
...params.walletId ? { walletId: params.walletId } : {}
|
|
716
|
+
})
|
|
717
|
+
)
|
|
718
|
+
});
|
|
719
|
+
api.registerTool({
|
|
720
|
+
name: "solana_gateway_credentials_set",
|
|
721
|
+
description: "Register or update your OpenClaw Gateway credentials with the orchestrator. This enables event-to-agent forwarding \u2014 when subscriptions include agentId, the orchestrator delivers each stream event to your Gateway via /v1/responses. Call this once during initial setup (Step 0). The gatewayBaseUrl is your self-hosted OpenClaw Gateway's public URL. The gatewayToken is the Bearer token for authenticating forwarded events.",
|
|
722
|
+
parameters: Type.Object({
|
|
723
|
+
gatewayBaseUrl: Type.String({ description: "Your OpenClaw Gateway's public HTTPS URL (e.g., 'https://gateway.example.com')" }),
|
|
724
|
+
gatewayToken: Type.String({ description: "Bearer token for authenticating forwarded events to your Gateway" }),
|
|
725
|
+
agentId: Type.Optional(Type.String({ description: "Agent ID to associate credentials with (default: 'main'). Omit to store as the default fallback." })),
|
|
726
|
+
active: Type.Optional(Type.Boolean({ description: "Whether forwarding is active (default: true)" }))
|
|
727
|
+
}),
|
|
728
|
+
execute: wrapExecute(async (_id, params) => {
|
|
729
|
+
const body = {
|
|
730
|
+
gatewayBaseUrl: params.gatewayBaseUrl,
|
|
731
|
+
gatewayToken: params.gatewayToken
|
|
732
|
+
};
|
|
733
|
+
if (params.agentId) body.agentId = params.agentId;
|
|
734
|
+
if (params.active !== void 0) body.active = params.active;
|
|
735
|
+
return put("/api/agents/gateway-credentials", body);
|
|
736
|
+
})
|
|
737
|
+
});
|
|
738
|
+
api.registerTool({
|
|
739
|
+
name: "solana_gateway_credentials_get",
|
|
740
|
+
description: "Get the currently registered Gateway credentials for event-to-agent forwarding. Returns the gatewayBaseUrl, agentId, active status, and masked token. Use to verify Gateway setup is correct.",
|
|
741
|
+
parameters: Type.Object({}),
|
|
742
|
+
execute: wrapExecute(
|
|
743
|
+
async () => get("/api/agents/gateway-credentials")
|
|
744
|
+
)
|
|
745
|
+
});
|
|
746
|
+
api.registerTool({
|
|
747
|
+
name: "solana_gateway_credentials_delete",
|
|
748
|
+
description: "Delete your registered Gateway credentials. This disables event-to-agent forwarding \u2014 subscriptions with agentId will no longer forward events to your Gateway. Only use if decommissioning the Gateway.",
|
|
749
|
+
parameters: Type.Object({}),
|
|
750
|
+
execute: wrapExecute(
|
|
751
|
+
async () => del("/api/agents/gateway-credentials")
|
|
752
|
+
)
|
|
753
|
+
});
|
|
754
|
+
api.registerTool({
|
|
755
|
+
name: "solana_agent_sessions",
|
|
756
|
+
description: "List active agent sessions registered with the orchestrator. Returns session IDs, agent IDs, connection status, and subscription counts. Use for diagnostics \u2014 verify your agent is properly registered and its subscriptions are forwarding events.",
|
|
757
|
+
parameters: Type.Object({}),
|
|
758
|
+
execute: wrapExecute(
|
|
759
|
+
async () => get("/api/agents/active")
|
|
760
|
+
)
|
|
761
|
+
});
|
|
762
|
+
const alphaBuffer = new AlphaBuffer();
|
|
763
|
+
const alphaStreamManager = new AlphaStreamManager({
|
|
764
|
+
wsUrl: orchestratorUrl.replace(/^http/, "ws").replace(/\/$/, "") + "/ws",
|
|
765
|
+
getAccessToken: () => sessionManager.getAccessToken(),
|
|
766
|
+
buffer: alphaBuffer,
|
|
767
|
+
agentId: config.agentId,
|
|
768
|
+
logger: {
|
|
769
|
+
info: (msg) => api.logger.info(`[solana-trader] ${msg}`),
|
|
770
|
+
warn: (msg) => api.logger.warn(`[solana-trader] ${msg}`),
|
|
771
|
+
error: (msg) => api.logger.error(`[solana-trader] ${msg}`)
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
api.registerTool({
|
|
775
|
+
name: "solana_alpha_subscribe",
|
|
776
|
+
description: "Subscribe to the SpyFly alpha signal stream via WebSocket. Starts receiving real-time alpha signals (TG/Discord channel calls) into the buffer. Call once on first heartbeat \u2014 stays connected with auto-reconnect. Pass agentId to enable event-to-agent forwarding \u2014 orchestrator delivers each alpha signal to your Gateway via /v1/responses in addition to buffering. Returns subscription status, tier, and premium access level.",
|
|
777
|
+
parameters: Type.Object({
|
|
778
|
+
agentId: Type.Optional(Type.String({ description: "Agent ID for event-to-agent forwarding (e.g., 'main'). Overrides plugin config agentId if provided." })),
|
|
779
|
+
subscriberType: Type.Optional(Type.String({ description: "Subscriber type: 'agent' (default when agentId is set) or 'user'. Controls how the orchestrator routes events." }))
|
|
780
|
+
}),
|
|
781
|
+
execute: wrapExecute(async (_id, params) => {
|
|
782
|
+
const effectiveAgentId = params.agentId || config.agentId;
|
|
783
|
+
if (effectiveAgentId && alphaStreamManager.getAgentId() !== effectiveAgentId) {
|
|
784
|
+
alphaStreamManager.setAgentId(effectiveAgentId);
|
|
785
|
+
}
|
|
786
|
+
const effectiveSubscriberType = params.subscriberType || (effectiveAgentId ? "agent" : void 0);
|
|
787
|
+
if (effectiveSubscriberType) {
|
|
788
|
+
alphaStreamManager.setSubscriberType(effectiveSubscriberType);
|
|
789
|
+
}
|
|
790
|
+
return alphaStreamManager.subscribe();
|
|
791
|
+
})
|
|
792
|
+
});
|
|
793
|
+
api.registerTool({
|
|
794
|
+
name: "solana_alpha_unsubscribe",
|
|
795
|
+
description: "Unsubscribe from the SpyFly alpha signal stream and disconnect WebSocket. Use when shutting down or if kill switch is activated with TRADES_AND_STREAMS mode.",
|
|
796
|
+
parameters: Type.Object({}),
|
|
797
|
+
execute: wrapExecute(async () => alphaStreamManager.unsubscribe())
|
|
798
|
+
});
|
|
799
|
+
api.registerTool({
|
|
800
|
+
name: "solana_alpha_signals",
|
|
801
|
+
description: "Get buffered alpha signals from the SpyFly stream. By default returns only unseen signals and marks them as seen. Use minScore to filter low-quality signals. Poll this every heartbeat cycle in Step 1.5b. Returns signals sorted by ingestion time (newest last).",
|
|
802
|
+
parameters: Type.Object({
|
|
803
|
+
minScore: Type.Optional(Type.Number({ description: "Minimum systemScore threshold (0-100). Signals below this are excluded." })),
|
|
804
|
+
chain: Type.Optional(Type.String({ description: "Filter by chain (e.g., 'solana'). BSC is already filtered at ingestion." })),
|
|
805
|
+
kinds: Type.Optional(Type.Array(Type.String(), { description: "Filter by signal kind: 'ca_drop', 'milestone', 'update', 'risk', 'exit'" })),
|
|
806
|
+
unseen: Type.Optional(Type.Boolean({ description: "If true (default), return only unseen signals and mark them as seen. Set false to get all buffered signals." }))
|
|
807
|
+
}),
|
|
808
|
+
execute: wrapExecute(async (_id, params) => {
|
|
809
|
+
const signals = alphaBuffer.getSignals({
|
|
810
|
+
minScore: params.minScore,
|
|
811
|
+
chain: params.chain,
|
|
812
|
+
kinds: params.kinds,
|
|
813
|
+
unseen: params.unseen !== void 0 ? params.unseen : true
|
|
814
|
+
});
|
|
815
|
+
return {
|
|
816
|
+
signals,
|
|
817
|
+
count: signals.length,
|
|
818
|
+
bufferSize: alphaBuffer.getBufferSize(),
|
|
819
|
+
subscribed: alphaStreamManager.isSubscribed(),
|
|
820
|
+
stats: alphaStreamManager.getStats()
|
|
821
|
+
};
|
|
822
|
+
})
|
|
823
|
+
});
|
|
824
|
+
api.registerTool({
|
|
825
|
+
name: "solana_alpha_history",
|
|
826
|
+
description: "Query historical alpha signal data via the SpyFly REST API (GET /api/pings). Returns up to 1 year of stored signals for source reputation analysis, post-downtime catch-up, and strategy learning. Tier-gated: starter=10, pro=50, enterprise=200 results. 99.99% of tokens are dead but source patterns are invaluable.",
|
|
827
|
+
parameters: Type.Object({
|
|
828
|
+
tokenAddress: Type.Optional(Type.String({ description: "Filter by token mint address" })),
|
|
829
|
+
channelId: Type.Optional(Type.String({ description: "Filter by source channel ID" })),
|
|
830
|
+
limit: Type.Optional(Type.Number({ description: "Max results (tier-capped: starter=10, pro=50, enterprise=200)" })),
|
|
831
|
+
days: Type.Optional(Type.Number({ description: "Look back period in days. Converted to then/now timestamp range." }))
|
|
832
|
+
}),
|
|
833
|
+
execute: wrapExecute(async (_id, params) => {
|
|
834
|
+
const queryParts = [];
|
|
835
|
+
if (params.limit) queryParts.push(`limit=${params.limit}`);
|
|
836
|
+
if (params.channelId) queryParts.push(`channelId=${params.channelId}`);
|
|
837
|
+
if (params.days) {
|
|
838
|
+
const now = Date.now();
|
|
839
|
+
const then = now - params.days * 24 * 60 * 60 * 1e3;
|
|
840
|
+
queryParts.push(`then=${then}`);
|
|
841
|
+
queryParts.push(`now=${now}`);
|
|
842
|
+
}
|
|
843
|
+
if (params.tokenAddress) queryParts.push(`tokenAddress=${params.tokenAddress}`);
|
|
844
|
+
const qs = queryParts.length > 0 ? `?${queryParts.join("&")}` : "";
|
|
845
|
+
return get(`/api/pings${qs}`);
|
|
846
|
+
})
|
|
847
|
+
});
|
|
848
|
+
api.registerTool({
|
|
849
|
+
name: "solana_alpha_sources",
|
|
850
|
+
description: "Get per-source statistics from the alpha signal buffer \u2014 signal count, average systemScore, and source type for each channel. Use for quick reputation checks during signal processing and to identify high-quality vs low-quality sources.",
|
|
851
|
+
parameters: Type.Object({}),
|
|
852
|
+
execute: wrapExecute(async () => ({
|
|
853
|
+
sources: alphaBuffer.getSourceStatsAll(),
|
|
854
|
+
bufferSize: alphaBuffer.getBufferSize(),
|
|
855
|
+
subscribed: alphaStreamManager.isSubscribed()
|
|
856
|
+
}))
|
|
857
|
+
});
|
|
858
|
+
api.registerTool({
|
|
859
|
+
name: "solana_system_status",
|
|
860
|
+
description: "Check orchestrator system health \u2014 uptime, connected services, database status, execution mode (mock/live), and upstream API connectivity.",
|
|
861
|
+
parameters: Type.Object({}),
|
|
862
|
+
execute: wrapExecute(async () => get("/api/system/status"))
|
|
863
|
+
});
|
|
864
|
+
api.registerService({
|
|
865
|
+
id: "solana-trader-session",
|
|
866
|
+
start: async () => {
|
|
867
|
+
try {
|
|
868
|
+
await sessionManager.initialize();
|
|
869
|
+
const info = sessionManager.getSessionInfo();
|
|
870
|
+
api.logger.info(
|
|
871
|
+
`[solana-trader] Session active. Tier: ${info.tier}, Scopes: ${info.scopes.join(", ")}`
|
|
872
|
+
);
|
|
873
|
+
} catch (err) {
|
|
874
|
+
api.logger.error(
|
|
875
|
+
`[solana-trader] Session initialization failed: ${err instanceof Error ? err.message : String(err)}`
|
|
876
|
+
);
|
|
877
|
+
api.logger.error(
|
|
878
|
+
"[solana-trader] Trading tools will fail until session is established. Run: openclaw-trader setup"
|
|
879
|
+
);
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
try {
|
|
883
|
+
const healthz = await orchestratorRequest({
|
|
884
|
+
baseUrl: orchestratorUrl,
|
|
885
|
+
method: "GET",
|
|
886
|
+
path: "/healthz",
|
|
887
|
+
timeout: 5e3,
|
|
888
|
+
accessToken: await sessionManager.getAccessToken()
|
|
889
|
+
});
|
|
890
|
+
api.logger.info(
|
|
891
|
+
`[solana-trader] Orchestrator healthz OK at ${orchestratorUrl}`
|
|
892
|
+
);
|
|
893
|
+
if (healthz && typeof healthz === "object") {
|
|
894
|
+
const h = healthz;
|
|
895
|
+
api.logger.info(
|
|
896
|
+
`[solana-trader] Mode: ${h.executionMode || "unknown"}, Upstream: ${h.upstreamConfigured ? "yes" : "no"}`
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
} catch (err) {
|
|
900
|
+
api.logger.warn(
|
|
901
|
+
`[solana-trader] /healthz unreachable at ${orchestratorUrl}: ${err instanceof Error ? err.message : String(err)}`
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
try {
|
|
905
|
+
const status = await get("/api/system/status");
|
|
906
|
+
api.logger.info(
|
|
907
|
+
`[solana-trader] Connected to orchestrator (walletId: ${walletId})`
|
|
908
|
+
);
|
|
909
|
+
if (status && typeof status === "object") {
|
|
910
|
+
api.logger.info(`[solana-trader] System status: ${JSON.stringify(status)}`);
|
|
911
|
+
}
|
|
912
|
+
} catch (err) {
|
|
913
|
+
api.logger.warn(
|
|
914
|
+
`[solana-trader] /api/system/status unreachable: ${err instanceof Error ? err.message : String(err)}`
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
api.logger.info(
|
|
920
|
+
`[solana-trader] Registered 49 trading tools for walletId ${walletId} (session auth mode)`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
var index_default = solanaTraderPlugin;
|
|
925
|
+
export {
|
|
926
|
+
index_default as default
|
|
927
|
+
};
|