@elizaos/plugin-hyperliquid-app 2.0.3-beta.5 → 2.0.3-beta.7
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/dist/HyperliquidAppView.d.ts +4 -0
- package/dist/HyperliquidAppView.d.ts.map +1 -0
- package/dist/HyperliquidAppView.interact.d.ts +10 -0
- package/dist/HyperliquidAppView.interact.d.ts.map +1 -0
- package/dist/HyperliquidAppView.interact.js +70 -0
- package/dist/HyperliquidAppView.interact.js.map +1 -0
- package/dist/HyperliquidAppView.js +199 -0
- package/dist/HyperliquidAppView.js.map +1 -0
- package/dist/HyperliquidPositionsPanel.d.ts +12 -0
- package/dist/HyperliquidPositionsPanel.d.ts.map +1 -0
- package/dist/HyperliquidPositionsPanel.js +194 -0
- package/dist/HyperliquidPositionsPanel.js.map +1 -0
- package/dist/HyperliquidView.d.ts +14 -0
- package/dist/HyperliquidView.d.ts.map +1 -0
- package/dist/HyperliquidView.js +56 -0
- package/dist/HyperliquidView.js.map +1 -0
- package/dist/__fixtures__/contract.d.ts +5 -0
- package/dist/__fixtures__/contract.d.ts.map +1 -0
- package/dist/__fixtures__/contract.js +87 -0
- package/dist/__fixtures__/contract.js.map +1 -0
- package/dist/actions/perpetual-market.d.ts +38 -0
- package/dist/actions/perpetual-market.d.ts.map +1 -0
- package/dist/actions/perpetual-market.js +653 -0
- package/dist/actions/perpetual-market.js.map +1 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +15 -0
- package/dist/client.js.map +1 -0
- package/dist/components/HyperliquidSpatialView.d.ts +46 -0
- package/dist/components/HyperliquidSpatialView.d.ts.map +1 -0
- package/dist/components/HyperliquidSpatialView.js +214 -0
- package/dist/components/HyperliquidSpatialView.js.map +1 -0
- package/dist/hyperliquid-app-view-bundle.d.ts +3 -0
- package/dist/hyperliquid-app-view-bundle.d.ts.map +1 -0
- package/dist/hyperliquid-app-view-bundle.js +7 -0
- package/dist/hyperliquid-app-view-bundle.js.map +1 -0
- package/dist/hyperliquid-app.d.ts +4 -0
- package/dist/hyperliquid-app.d.ts.map +1 -0
- package/dist/hyperliquid-app.js +18 -0
- package/dist/hyperliquid-app.js.map +1 -0
- package/dist/hyperliquid-contracts.d.ts +152 -0
- package/dist/hyperliquid-contracts.d.ts.map +1 -0
- package/dist/hyperliquid-contracts.js +17 -0
- package/dist/hyperliquid-contracts.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +130 -0
- package/dist/plugin.js.map +1 -0
- package/dist/register-routes.d.ts +2 -0
- package/dist/register-routes.d.ts.map +1 -0
- package/dist/register-routes.js +6 -0
- package/dist/register-routes.js.map +1 -0
- package/dist/register-terminal-view.d.ts +15 -0
- package/dist/register-terminal-view.d.ts.map +1 -0
- package/dist/register-terminal-view.js +34 -0
- package/dist/register-terminal-view.js.map +1 -0
- package/dist/register.d.ts +2 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +17 -0
- package/dist/register.js.map +1 -0
- package/dist/routes.d.ts +25 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +485 -0
- package/dist/routes.js.map +1 -0
- package/dist/ui.d.ts +6 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +12 -0
- package/dist/ui.js.map +1 -0
- package/dist/useHyperliquidState.d.ts +20 -0
- package/dist/useHyperliquidState.d.ts.map +1 -0
- package/dist/useHyperliquidState.js +72 -0
- package/dist/useHyperliquidState.js.map +1 -0
- package/dist/views/bundle.js +463 -0
- package/dist/views/bundle.js.map +1 -0
- package/package.json +6 -6
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
import { Service } from "@elizaos/core";
|
|
2
|
+
import { resolveApiToken, resolveDesktopApiPort } from "@elizaos/shared";
|
|
3
|
+
const ACTION_TIMEOUT_MS = 15e3;
|
|
4
|
+
const PERPETUAL_MARKET_SERVICE_TYPE = "perpetual-market";
|
|
5
|
+
const HYPERLIQUID_CONTEXTS = ["finance", "crypto", "trading"];
|
|
6
|
+
const HYPERLIQUID_ACTION_CONTEXTS = [
|
|
7
|
+
...HYPERLIQUID_CONTEXTS,
|
|
8
|
+
"payments"
|
|
9
|
+
];
|
|
10
|
+
const PERPETUAL_MARKET_ACTION_NAME = "PERPETUAL_MARKET";
|
|
11
|
+
const HYPERLIQUID_READ_COMPAT_NAME = "HYPERLIQUID_READ";
|
|
12
|
+
const HYPERLIQUID_PLACE_ORDER_COMPAT_NAME = "HYPERLIQUID_PLACE_ORDER";
|
|
13
|
+
function toCallbackData(data) {
|
|
14
|
+
return data;
|
|
15
|
+
}
|
|
16
|
+
const HYPERLIQUID_READ_KEYWORDS = [
|
|
17
|
+
"hyperliquid",
|
|
18
|
+
"perp",
|
|
19
|
+
"perps",
|
|
20
|
+
"perpetual",
|
|
21
|
+
"perpetuals",
|
|
22
|
+
"funding",
|
|
23
|
+
"funding rate",
|
|
24
|
+
"futures",
|
|
25
|
+
"leverage",
|
|
26
|
+
"liquidation",
|
|
27
|
+
"perp\xE9tuel",
|
|
28
|
+
"perpetuo",
|
|
29
|
+
"perpetuelle",
|
|
30
|
+
"ewig",
|
|
31
|
+
"\u6C38\u7D9A",
|
|
32
|
+
"\u6C38\u4E45",
|
|
33
|
+
"\u6C38\u7EED",
|
|
34
|
+
"\uC120\uBB3C",
|
|
35
|
+
"\uBB34\uAE30\uD55C"
|
|
36
|
+
];
|
|
37
|
+
const HYPERLIQUID_TRADE_KEYWORDS = [
|
|
38
|
+
...HYPERLIQUID_READ_KEYWORDS,
|
|
39
|
+
"buy",
|
|
40
|
+
"sell",
|
|
41
|
+
"trade",
|
|
42
|
+
"order",
|
|
43
|
+
"long",
|
|
44
|
+
"short",
|
|
45
|
+
"open position",
|
|
46
|
+
"close position",
|
|
47
|
+
"comprar",
|
|
48
|
+
"vender",
|
|
49
|
+
"orden",
|
|
50
|
+
"acheter",
|
|
51
|
+
"vendre",
|
|
52
|
+
"ordre",
|
|
53
|
+
"kaufen",
|
|
54
|
+
"verkaufen",
|
|
55
|
+
"auftrag",
|
|
56
|
+
"comprare",
|
|
57
|
+
"vendere",
|
|
58
|
+
"\u6CE8\u6587",
|
|
59
|
+
"\u8CB7\u3046",
|
|
60
|
+
"\u58F2\u308B",
|
|
61
|
+
"\u4E70\u5165",
|
|
62
|
+
"\u5356\u51FA",
|
|
63
|
+
"\u8BA2\u5355",
|
|
64
|
+
"\uB9E4\uC218",
|
|
65
|
+
"\uB9E4\uB3C4"
|
|
66
|
+
];
|
|
67
|
+
const READ_KINDS = [
|
|
68
|
+
"status",
|
|
69
|
+
"markets",
|
|
70
|
+
"market",
|
|
71
|
+
"positions",
|
|
72
|
+
"funding"
|
|
73
|
+
];
|
|
74
|
+
const HYPERLIQUID_OPS = ["read", "place_order"];
|
|
75
|
+
const HYPERLIQUID_READ_COMPAT_SIMILES = [
|
|
76
|
+
"HYPERLIQUID",
|
|
77
|
+
"PERP_MARKET",
|
|
78
|
+
HYPERLIQUID_READ_COMPAT_NAME,
|
|
79
|
+
"HYPERLIQUID_STATUS",
|
|
80
|
+
"HYPERLIQUID_READINESS",
|
|
81
|
+
"HYPERLIQUID_HEALTH",
|
|
82
|
+
"HYPERLIQUID_GET_MARKETS",
|
|
83
|
+
"HYPERLIQUID_MARKETS",
|
|
84
|
+
"HYPERLIQUID_GET_MARKET",
|
|
85
|
+
"HYPERLIQUID_MARKET",
|
|
86
|
+
"HYPERLIQUID_GET_POSITIONS",
|
|
87
|
+
"HYPERLIQUID_POSITIONS",
|
|
88
|
+
"HYPERLIQUID_FUNDING"
|
|
89
|
+
];
|
|
90
|
+
const HYPERLIQUID_PLACE_ORDER_COMPAT_SIMILES = [
|
|
91
|
+
HYPERLIQUID_PLACE_ORDER_COMPAT_NAME,
|
|
92
|
+
"HYPERLIQUID_TRADE",
|
|
93
|
+
"HYPERLIQUID_BUY",
|
|
94
|
+
"HYPERLIQUID_SELL",
|
|
95
|
+
"HYPERLIQUID_LONG",
|
|
96
|
+
"HYPERLIQUID_SHORT",
|
|
97
|
+
// HyperliquidBench Rust plan-step kinds (packages/benchmarks/HyperliquidBench/types.py)
|
|
98
|
+
// — keep these as similes so retrieval/fine-tune transfer covers the bench's vocabulary.
|
|
99
|
+
"HYPERLIQUID_PERP_ORDERS",
|
|
100
|
+
"HYPERLIQUID_CANCEL_LAST",
|
|
101
|
+
"HYPERLIQUID_CANCEL_OIDS",
|
|
102
|
+
"HYPERLIQUID_CANCEL_ALL",
|
|
103
|
+
"HYPERLIQUID_USD_CLASS_TRANSFER",
|
|
104
|
+
"HYPERLIQUID_SET_LEVERAGE"
|
|
105
|
+
];
|
|
106
|
+
const HYPERLIQUID_READ_OP_ALIASES = /* @__PURE__ */ new Set([
|
|
107
|
+
...READ_KINDS,
|
|
108
|
+
...HYPERLIQUID_READ_COMPAT_SIMILES.map((name) => name.toLowerCase())
|
|
109
|
+
]);
|
|
110
|
+
const HYPERLIQUID_PLACE_ORDER_OP_ALIASES = /* @__PURE__ */ new Set([
|
|
111
|
+
...HYPERLIQUID_PLACE_ORDER_COMPAT_SIMILES.map((name) => name.toLowerCase()),
|
|
112
|
+
"trade",
|
|
113
|
+
"order",
|
|
114
|
+
"buy",
|
|
115
|
+
"sell",
|
|
116
|
+
"long",
|
|
117
|
+
"short"
|
|
118
|
+
]);
|
|
119
|
+
const PLACE_ORDER_DISABLED_REASON = "Signed Hyperliquid exchange execution is disabled in the native app. Use the Hyperliquid UI or a dedicated signer to place orders.";
|
|
120
|
+
function getApiBase() {
|
|
121
|
+
return `http://127.0.0.1:${resolveDesktopApiPort(process.env)}`;
|
|
122
|
+
}
|
|
123
|
+
function buildAuthHeaders() {
|
|
124
|
+
const token = resolveApiToken(process.env);
|
|
125
|
+
if (!token) return {};
|
|
126
|
+
return {
|
|
127
|
+
Authorization: /^Bearer\s+/i.test(token) ? token : `Bearer ${token}`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function readParam(options, key) {
|
|
131
|
+
const maybeOptions = options;
|
|
132
|
+
if (maybeOptions?.parameters && key in maybeOptions.parameters) {
|
|
133
|
+
return maybeOptions.parameters[key];
|
|
134
|
+
}
|
|
135
|
+
return options?.[key];
|
|
136
|
+
}
|
|
137
|
+
function readStringParam(options, key) {
|
|
138
|
+
const value = readParam(options, key);
|
|
139
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
140
|
+
}
|
|
141
|
+
function readKind(options) {
|
|
142
|
+
const raw = readStringParam(options, "kind");
|
|
143
|
+
if (!raw) return null;
|
|
144
|
+
const normalized = raw.toLowerCase();
|
|
145
|
+
return READ_KINDS.includes(normalized) ? normalized : null;
|
|
146
|
+
}
|
|
147
|
+
function normalizeOp(value) {
|
|
148
|
+
if (typeof value !== "string") return null;
|
|
149
|
+
const normalized = value.trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
150
|
+
if (HYPERLIQUID_OPS.includes(normalized)) {
|
|
151
|
+
return normalized;
|
|
152
|
+
}
|
|
153
|
+
if (HYPERLIQUID_READ_OP_ALIASES.has(normalized)) {
|
|
154
|
+
return "read";
|
|
155
|
+
}
|
|
156
|
+
if (HYPERLIQUID_PLACE_ORDER_OP_ALIASES.has(normalized)) {
|
|
157
|
+
return "place_order";
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
function readOp(options) {
|
|
162
|
+
const rawOp = readStringParam(options, "action") ?? readStringParam(options, "subaction") ?? readStringParam(options, "op") ?? readStringParam(options, "operation") ?? readStringParam(options, "name");
|
|
163
|
+
const explicit = normalizeOp(rawOp);
|
|
164
|
+
if (explicit) return explicit;
|
|
165
|
+
if (readKind(options)) return "read";
|
|
166
|
+
if (readStringParam(options, "side") || readStringParam(options, "coin") || readStringParam(options, "asset") || readParam(options, "size") !== void 0) {
|
|
167
|
+
return "place_order";
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
function hasSelectedContext(state, contexts = HYPERLIQUID_ACTION_CONTEXTS) {
|
|
172
|
+
const selected = /* @__PURE__ */ new Set();
|
|
173
|
+
const collect = (value) => {
|
|
174
|
+
if (!Array.isArray(value)) return;
|
|
175
|
+
for (const item of value) {
|
|
176
|
+
if (typeof item === "string") selected.add(item);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
collect(
|
|
180
|
+
state?.values?.selectedContexts
|
|
181
|
+
);
|
|
182
|
+
collect(
|
|
183
|
+
state?.data?.selectedContexts
|
|
184
|
+
);
|
|
185
|
+
const contextObject = state?.data?.contextObject;
|
|
186
|
+
collect(contextObject?.trajectoryPrefix?.selectedContexts);
|
|
187
|
+
collect(contextObject?.metadata?.selectedContexts);
|
|
188
|
+
return contexts.some((context) => selected.has(context));
|
|
189
|
+
}
|
|
190
|
+
function hasKeywordIntent(message, state, keywords) {
|
|
191
|
+
const text = [
|
|
192
|
+
typeof message.content?.text === "string" ? message.content.text : "",
|
|
193
|
+
typeof state?.values?.recentMessages === "string" ? state.values.recentMessages : ""
|
|
194
|
+
].join("\n").toLowerCase();
|
|
195
|
+
return keywords.some((keyword) => text.includes(keyword.toLowerCase()));
|
|
196
|
+
}
|
|
197
|
+
async function fetchHyperliquidJson(path) {
|
|
198
|
+
const response = await fetch(`${getApiBase()}${path}`, {
|
|
199
|
+
headers: { accept: "application/json", ...buildAuthHeaders() },
|
|
200
|
+
signal: AbortSignal.timeout(ACTION_TIMEOUT_MS)
|
|
201
|
+
});
|
|
202
|
+
const payload = await response.json().catch(() => null);
|
|
203
|
+
if (!response.ok) {
|
|
204
|
+
const message = payload && typeof payload === "object" && "error" in payload ? String(payload.error) : `Hyperliquid API request failed with ${response.status}`;
|
|
205
|
+
throw new Error(message);
|
|
206
|
+
}
|
|
207
|
+
return payload;
|
|
208
|
+
}
|
|
209
|
+
async function emit(callback, text, data) {
|
|
210
|
+
if (callback) {
|
|
211
|
+
await callback({
|
|
212
|
+
text,
|
|
213
|
+
actions: [PERPETUAL_MARKET_ACTION_NAME],
|
|
214
|
+
data: toCallbackData(data)
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
success: true,
|
|
219
|
+
text,
|
|
220
|
+
data: { actionName: PERPETUAL_MARKET_ACTION_NAME, ...data }
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
async function emitFailure(callback, text, error, data) {
|
|
224
|
+
if (callback) {
|
|
225
|
+
await callback({
|
|
226
|
+
text,
|
|
227
|
+
actions: [PERPETUAL_MARKET_ACTION_NAME],
|
|
228
|
+
data: toCallbackData(data)
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return { success: false, text, error, data };
|
|
232
|
+
}
|
|
233
|
+
function marketLine(market) {
|
|
234
|
+
const leverage = market.maxLeverage !== null ? ` maxLeverage ${market.maxLeverage}x` : "";
|
|
235
|
+
const isolated = market.onlyIsolated ? " isolated-only" : "";
|
|
236
|
+
return `- ${market.name}${leverage}${isolated}`;
|
|
237
|
+
}
|
|
238
|
+
function formatMarkets(markets) {
|
|
239
|
+
if (markets.length === 0) return "No active Hyperliquid markets found.";
|
|
240
|
+
const active = markets.filter((m) => !m.isDelisted);
|
|
241
|
+
return `Hyperliquid perpetual markets (${active.length} active):
|
|
242
|
+
${active.slice(0, 20).map(marketLine).join("\n")}`;
|
|
243
|
+
}
|
|
244
|
+
function formatMarket(market) {
|
|
245
|
+
if (!market) return "No matching Hyperliquid market found.";
|
|
246
|
+
return [
|
|
247
|
+
`Hyperliquid ${market.name} perpetual`,
|
|
248
|
+
`Status: ${market.isDelisted ? "delisted" : "active"}`,
|
|
249
|
+
`Size decimals: ${market.szDecimals}`,
|
|
250
|
+
`Max leverage: ${market.maxLeverage ?? "n/a"}`,
|
|
251
|
+
`Isolated only: ${market.onlyIsolated ? "yes" : "no"}`
|
|
252
|
+
].join("\n");
|
|
253
|
+
}
|
|
254
|
+
function fundingLine(rate) {
|
|
255
|
+
const premium = rate.premium ? ` premium ${rate.premium}` : "";
|
|
256
|
+
const openInterest = rate.openInterest ? ` OI ${rate.openInterest}` : "";
|
|
257
|
+
const mark = rate.markPx ? ` mark ${rate.markPx}` : "";
|
|
258
|
+
return `- ${rate.coin}: funding ${rate.funding}${premium}${openInterest}${mark}`;
|
|
259
|
+
}
|
|
260
|
+
function formatFundingRates(rates) {
|
|
261
|
+
if (rates.length === 0) return "No Hyperliquid funding rates found.";
|
|
262
|
+
return `Hyperliquid current funding rates:
|
|
263
|
+
${rates.slice(0, 20).map(fundingLine).join("\n")}`;
|
|
264
|
+
}
|
|
265
|
+
async function handleStatus(callback) {
|
|
266
|
+
const status = await fetchHyperliquidJson(
|
|
267
|
+
"/api/hyperliquid/status"
|
|
268
|
+
);
|
|
269
|
+
const text = [
|
|
270
|
+
`Hyperliquid public reads: ${status.publicReadReady ? "ready" : "not ready"}`,
|
|
271
|
+
`Account reads: ${status.readiness.accountReads ? "ready" : "not ready"}`,
|
|
272
|
+
`Signer: ${status.signerReady ? "ready" : "not ready"}`,
|
|
273
|
+
`Execution: disabled`,
|
|
274
|
+
status.executionBlockedReason ? `Reason: ${status.executionBlockedReason}` : null,
|
|
275
|
+
`Credential mode: ${status.credentialMode}`,
|
|
276
|
+
status.accountAddress ? `Account: ${status.accountAddress}` : null
|
|
277
|
+
].filter((line) => Boolean(line)).join("\n");
|
|
278
|
+
return emit(callback, text, {
|
|
279
|
+
op: "read",
|
|
280
|
+
compatActionName: HYPERLIQUID_READ_COMPAT_NAME,
|
|
281
|
+
kind: "status",
|
|
282
|
+
status
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
async function handleMarkets(callback) {
|
|
286
|
+
const response = await fetchHyperliquidJson(
|
|
287
|
+
"/api/hyperliquid/markets"
|
|
288
|
+
);
|
|
289
|
+
return emit(callback, formatMarkets(response.markets), {
|
|
290
|
+
op: "read",
|
|
291
|
+
compatActionName: HYPERLIQUID_READ_COMPAT_NAME,
|
|
292
|
+
kind: "markets",
|
|
293
|
+
markets: response.markets,
|
|
294
|
+
source: response.source,
|
|
295
|
+
fetchedAt: response.fetchedAt
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
async function handleMarket(options, callback) {
|
|
299
|
+
const coin = readStringParam(options, "coin") ?? readStringParam(options, "asset") ?? readStringParam(options, "name") ?? readStringParam(options, "symbol");
|
|
300
|
+
if (!coin) {
|
|
301
|
+
const text = "Provide a Hyperliquid coin/asset symbol (e.g. BTC, ETH, SOL).";
|
|
302
|
+
return emitFailure(callback, text, "missing_market_identifier", {
|
|
303
|
+
actionName: PERPETUAL_MARKET_ACTION_NAME,
|
|
304
|
+
op: "read",
|
|
305
|
+
compatActionName: HYPERLIQUID_READ_COMPAT_NAME,
|
|
306
|
+
kind: "market"
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
const response = await fetchHyperliquidJson(
|
|
310
|
+
"/api/hyperliquid/markets"
|
|
311
|
+
);
|
|
312
|
+
const target = coin.toUpperCase();
|
|
313
|
+
const market = response.markets.find((m) => m.name.toUpperCase() === target) ?? null;
|
|
314
|
+
return emit(callback, formatMarket(market), {
|
|
315
|
+
op: "read",
|
|
316
|
+
compatActionName: HYPERLIQUID_READ_COMPAT_NAME,
|
|
317
|
+
kind: "market",
|
|
318
|
+
market,
|
|
319
|
+
source: response.source,
|
|
320
|
+
fetchedAt: response.fetchedAt
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
async function handlePositions(callback) {
|
|
324
|
+
const response = await fetchHyperliquidJson(
|
|
325
|
+
"/api/hyperliquid/positions"
|
|
326
|
+
);
|
|
327
|
+
if (!response.accountAddress) {
|
|
328
|
+
const text2 = response.readBlockedReason ? `Hyperliquid positions unavailable: ${response.readBlockedReason}` : "Hyperliquid positions unavailable: no account address configured.";
|
|
329
|
+
return emit(callback, text2, {
|
|
330
|
+
op: "read",
|
|
331
|
+
compatActionName: HYPERLIQUID_READ_COMPAT_NAME,
|
|
332
|
+
kind: "positions",
|
|
333
|
+
accountAddress: null,
|
|
334
|
+
positions: [],
|
|
335
|
+
readBlockedReason: response.readBlockedReason
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
const text = response.positions.length === 0 ? `No Hyperliquid positions for ${response.accountAddress}.` : `Hyperliquid positions for ${response.accountAddress}:
|
|
339
|
+
${response.positions.slice(0, 12).map(
|
|
340
|
+
(position) => `- ${position.coin}: size ${position.size}` + (position.entryPx ? ` entry ${position.entryPx}` : "") + (position.unrealizedPnl ? ` uPnL ${position.unrealizedPnl}` : "") + (position.leverageValue !== null ? ` ${position.leverageType ?? "leverage"} ${position.leverageValue}x` : "")
|
|
341
|
+
).join("\n")}`;
|
|
342
|
+
return emit(callback, text, {
|
|
343
|
+
op: "read",
|
|
344
|
+
compatActionName: HYPERLIQUID_READ_COMPAT_NAME,
|
|
345
|
+
kind: "positions",
|
|
346
|
+
accountAddress: response.accountAddress,
|
|
347
|
+
positions: response.positions,
|
|
348
|
+
fetchedAt: response.fetchedAt
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
async function handleFunding(options, callback) {
|
|
352
|
+
const coin = readStringParam(options, "coin") ?? readStringParam(options, "asset") ?? readStringParam(options, "symbol");
|
|
353
|
+
const response = await fetchHyperliquidJson(
|
|
354
|
+
"/api/hyperliquid/funding"
|
|
355
|
+
);
|
|
356
|
+
const rates = coin ? response.rates.filter(
|
|
357
|
+
(rate) => rate.coin.toUpperCase() === coin.toUpperCase()
|
|
358
|
+
) : response.rates;
|
|
359
|
+
return emit(callback, formatFundingRates(rates), {
|
|
360
|
+
op: "read",
|
|
361
|
+
compatActionName: HYPERLIQUID_READ_COMPAT_NAME,
|
|
362
|
+
kind: "funding",
|
|
363
|
+
rates,
|
|
364
|
+
source: response.source,
|
|
365
|
+
fetchedAt: response.fetchedAt,
|
|
366
|
+
...coin ? { coin } : {}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
async function handleReadOperation(options, callback) {
|
|
370
|
+
const kind = readKind(options);
|
|
371
|
+
if (!kind) {
|
|
372
|
+
const text = "Provide kind: status | markets | market | positions | funding.";
|
|
373
|
+
return emitFailure(callback, text, "missing_or_invalid_kind", {
|
|
374
|
+
actionName: PERPETUAL_MARKET_ACTION_NAME,
|
|
375
|
+
op: "read",
|
|
376
|
+
compatActionName: HYPERLIQUID_READ_COMPAT_NAME,
|
|
377
|
+
availableKinds: [...READ_KINDS]
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
switch (kind) {
|
|
382
|
+
case "status":
|
|
383
|
+
return await handleStatus(callback);
|
|
384
|
+
case "markets":
|
|
385
|
+
return await handleMarkets(callback);
|
|
386
|
+
case "market":
|
|
387
|
+
return await handleMarket(options, callback);
|
|
388
|
+
case "positions":
|
|
389
|
+
return await handlePositions(callback);
|
|
390
|
+
case "funding":
|
|
391
|
+
return await handleFunding(options, callback);
|
|
392
|
+
}
|
|
393
|
+
} catch (error) {
|
|
394
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
395
|
+
return emitFailure(callback, text, text, {
|
|
396
|
+
actionName: PERPETUAL_MARKET_ACTION_NAME,
|
|
397
|
+
op: "read",
|
|
398
|
+
compatActionName: HYPERLIQUID_READ_COMPAT_NAME,
|
|
399
|
+
kind
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async function handlePlaceOrderOperation(callback) {
|
|
404
|
+
let status = null;
|
|
405
|
+
try {
|
|
406
|
+
status = await fetchHyperliquidJson(
|
|
407
|
+
"/api/hyperliquid/status"
|
|
408
|
+
);
|
|
409
|
+
} catch {
|
|
410
|
+
status = null;
|
|
411
|
+
}
|
|
412
|
+
const reason = status?.executionBlockedReason ?? PLACE_ORDER_DISABLED_REASON;
|
|
413
|
+
const text = `Hyperliquid order placement is disabled.
|
|
414
|
+
Reason: ${reason}`;
|
|
415
|
+
return {
|
|
416
|
+
...await emit(callback, text, {
|
|
417
|
+
op: "place_order",
|
|
418
|
+
compatActionName: HYPERLIQUID_PLACE_ORDER_COMPAT_NAME,
|
|
419
|
+
trading: {
|
|
420
|
+
enabled: false,
|
|
421
|
+
reason,
|
|
422
|
+
credentialMode: status?.credentialMode ?? "none",
|
|
423
|
+
signerReady: status?.signerReady ?? false
|
|
424
|
+
}
|
|
425
|
+
}),
|
|
426
|
+
success: false,
|
|
427
|
+
error: reason
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
async function handleOrders(callback) {
|
|
431
|
+
return await emit(
|
|
432
|
+
callback,
|
|
433
|
+
"Hyperliquid open-order reads (kind=orders) are not exposed in this read action; use kind=positions for held perps or the Hyperliquid UI for working orders.",
|
|
434
|
+
{
|
|
435
|
+
op: "read",
|
|
436
|
+
compatActionName: HYPERLIQUID_READ_COMPAT_NAME,
|
|
437
|
+
kind: "orders",
|
|
438
|
+
notExposed: true
|
|
439
|
+
}
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
void handleOrders;
|
|
443
|
+
void {};
|
|
444
|
+
function normalizeProviderKey(value) {
|
|
445
|
+
return value.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
446
|
+
}
|
|
447
|
+
function readTarget(options) {
|
|
448
|
+
return readStringParam(options, "target") ?? readStringParam(options, "provider") ?? "hyperliquid";
|
|
449
|
+
}
|
|
450
|
+
function createHyperliquidProvider() {
|
|
451
|
+
return {
|
|
452
|
+
name: "hyperliquid",
|
|
453
|
+
aliases: ["hl", "hyperliquid-perps"],
|
|
454
|
+
supportedSubactions: ["read", "place_order"],
|
|
455
|
+
description: "Hyperliquid perpetual market discovery, position reads, and execution readiness.",
|
|
456
|
+
execute: async ({ options, op, callback }) => {
|
|
457
|
+
switch (op) {
|
|
458
|
+
case "read":
|
|
459
|
+
return await handleReadOperation(options, callback);
|
|
460
|
+
case "place_order":
|
|
461
|
+
return await handlePlaceOrderOperation(callback);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
class PerpetualMarketService extends Service {
|
|
467
|
+
static serviceType = PERPETUAL_MARKET_SERVICE_TYPE;
|
|
468
|
+
capabilityDescription = "Perpetual market provider registry; currently registers Hyperliquid";
|
|
469
|
+
providers = /* @__PURE__ */ new Map();
|
|
470
|
+
aliases = /* @__PURE__ */ new Map();
|
|
471
|
+
static async start(runtime) {
|
|
472
|
+
const service = new PerpetualMarketService(runtime);
|
|
473
|
+
service.registerProvider(createHyperliquidProvider());
|
|
474
|
+
return service;
|
|
475
|
+
}
|
|
476
|
+
registerProvider(provider) {
|
|
477
|
+
const key = normalizeProviderKey(provider.name);
|
|
478
|
+
this.providers.set(key, provider);
|
|
479
|
+
for (const alias of [provider.name, ...provider.aliases]) {
|
|
480
|
+
this.aliases.set(normalizeProviderKey(alias), key);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
listProviders() {
|
|
484
|
+
return [...this.providers.values()].map((provider) => ({
|
|
485
|
+
name: provider.name,
|
|
486
|
+
aliases: [...provider.aliases],
|
|
487
|
+
supportedSubactions: [...provider.supportedSubactions],
|
|
488
|
+
description: provider.description
|
|
489
|
+
}));
|
|
490
|
+
}
|
|
491
|
+
async route(args) {
|
|
492
|
+
const target = args.target ?? "hyperliquid";
|
|
493
|
+
const key = this.aliases.get(normalizeProviderKey(target));
|
|
494
|
+
const provider = key ? this.providers.get(key) : void 0;
|
|
495
|
+
if (!provider) {
|
|
496
|
+
const text = `Unsupported perpetual market provider "${target}".`;
|
|
497
|
+
const data = {
|
|
498
|
+
actionName: PERPETUAL_MARKET_ACTION_NAME,
|
|
499
|
+
error: "UNSUPPORTED_PROVIDER",
|
|
500
|
+
providers: this.listProviders()
|
|
501
|
+
};
|
|
502
|
+
await args.callback?.({
|
|
503
|
+
text,
|
|
504
|
+
actions: [PERPETUAL_MARKET_ACTION_NAME],
|
|
505
|
+
data: toCallbackData(data)
|
|
506
|
+
});
|
|
507
|
+
return {
|
|
508
|
+
success: false,
|
|
509
|
+
text,
|
|
510
|
+
error: "UNSUPPORTED_PROVIDER",
|
|
511
|
+
data
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
if (!provider.supportedSubactions.includes(args.op)) {
|
|
515
|
+
const text = `${provider.name} does not support ${args.op}.`;
|
|
516
|
+
await args.callback?.({
|
|
517
|
+
text,
|
|
518
|
+
actions: [PERPETUAL_MARKET_ACTION_NAME],
|
|
519
|
+
data: {
|
|
520
|
+
actionName: PERPETUAL_MARKET_ACTION_NAME,
|
|
521
|
+
error: "UNSUPPORTED_SUBACTION",
|
|
522
|
+
provider: provider.name
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
return {
|
|
526
|
+
success: false,
|
|
527
|
+
text,
|
|
528
|
+
error: "UNSUPPORTED_SUBACTION",
|
|
529
|
+
data: {
|
|
530
|
+
actionName: PERPETUAL_MARKET_ACTION_NAME,
|
|
531
|
+
provider: provider.name
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
const result = await provider.execute(args);
|
|
536
|
+
return {
|
|
537
|
+
...result,
|
|
538
|
+
data: {
|
|
539
|
+
...result.data ?? {},
|
|
540
|
+
actionName: PERPETUAL_MARKET_ACTION_NAME,
|
|
541
|
+
target: provider.name,
|
|
542
|
+
supportedProviders: this.listProviders()
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
async stop() {
|
|
547
|
+
this.providers.clear();
|
|
548
|
+
this.aliases.clear();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
const perpetualMarketAction = {
|
|
552
|
+
name: "PERPETUAL_MARKET",
|
|
553
|
+
contexts: [...HYPERLIQUID_ACTION_CONTEXTS],
|
|
554
|
+
contextGate: { anyOf: [...HYPERLIQUID_ACTION_CONTEXTS] },
|
|
555
|
+
roleGate: { minRole: "USER" },
|
|
556
|
+
similes: [
|
|
557
|
+
...HYPERLIQUID_READ_COMPAT_SIMILES,
|
|
558
|
+
...HYPERLIQUID_PLACE_ORDER_COMPAT_SIMILES
|
|
559
|
+
],
|
|
560
|
+
description: "Use registered perpetual market providers. target selects the provider; Hyperliquid is registered today. action=read reads public state with kind: status, markets, market, positions, or funding. action=place_order reports trading readiness; signed order placement is disabled in this read-only app.",
|
|
561
|
+
descriptionCompressed: "Perpetual market router: target hyperliquid; action read or place_order.",
|
|
562
|
+
parameters: [
|
|
563
|
+
{
|
|
564
|
+
name: "target",
|
|
565
|
+
description: "Perpetual market provider.",
|
|
566
|
+
required: false,
|
|
567
|
+
schema: {
|
|
568
|
+
type: "string",
|
|
569
|
+
enum: ["hyperliquid"],
|
|
570
|
+
default: "hyperliquid"
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
name: "action",
|
|
575
|
+
description: "Perpetual market operation: read or place_order.",
|
|
576
|
+
required: false,
|
|
577
|
+
schema: { type: "string", enum: ["read", "place_order"] }
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
name: "subaction",
|
|
581
|
+
description: "Alias for action (read | place_order | place-order).",
|
|
582
|
+
required: false,
|
|
583
|
+
schema: { type: "string", enum: ["read", "place_order", "place-order"] }
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
name: "kind",
|
|
587
|
+
description: "read only: status | markets | market | positions | funding.",
|
|
588
|
+
required: false,
|
|
589
|
+
schema: {
|
|
590
|
+
type: "string",
|
|
591
|
+
enum: ["status", "markets", "market", "positions", "funding"]
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
name: "coin",
|
|
596
|
+
description: "market only: Hyperliquid coin/asset symbol (e.g. BTC).",
|
|
597
|
+
required: false,
|
|
598
|
+
schema: { type: "string" }
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
name: "side",
|
|
602
|
+
description: "place_order only: intended side, buy or sell.",
|
|
603
|
+
required: false,
|
|
604
|
+
schema: { type: "string", enum: ["buy", "sell"] }
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
name: "asset",
|
|
608
|
+
description: "place_order only: Hyperliquid asset symbol.",
|
|
609
|
+
required: false,
|
|
610
|
+
schema: { type: "string" }
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
name: "size",
|
|
614
|
+
description: "place_order only: intended order size.",
|
|
615
|
+
required: false,
|
|
616
|
+
schema: { type: "number" }
|
|
617
|
+
}
|
|
618
|
+
],
|
|
619
|
+
validate: async (_runtime, message, state) => hasSelectedContext(state) || hasKeywordIntent(message, state, HYPERLIQUID_READ_KEYWORDS) || hasKeywordIntent(message, state, HYPERLIQUID_TRADE_KEYWORDS),
|
|
620
|
+
handler: async (runtime, _message, _state, options, callback) => {
|
|
621
|
+
const op = readOp(options);
|
|
622
|
+
if (!op) {
|
|
623
|
+
const text = "Provide action: read | place_order. For read, also provide kind: status | markets | market | positions | funding.";
|
|
624
|
+
return emitFailure(callback, text, "missing_or_invalid_op", {
|
|
625
|
+
actionName: PERPETUAL_MARKET_ACTION_NAME,
|
|
626
|
+
availableActions: [...HYPERLIQUID_OPS]
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
const service = runtime.getService(
|
|
630
|
+
PERPETUAL_MARKET_SERVICE_TYPE
|
|
631
|
+
);
|
|
632
|
+
if (!service || typeof service.route !== "function") {
|
|
633
|
+
const text = "Perpetual market service is not available.";
|
|
634
|
+
return emitFailure(callback, text, "service_unavailable", {
|
|
635
|
+
actionName: PERPETUAL_MARKET_ACTION_NAME
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
return service.route({
|
|
639
|
+
target: readTarget(options),
|
|
640
|
+
op,
|
|
641
|
+
options,
|
|
642
|
+
callback
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
const hyperliquidActions = [perpetualMarketAction];
|
|
647
|
+
export {
|
|
648
|
+
PERPETUAL_MARKET_SERVICE_TYPE,
|
|
649
|
+
PerpetualMarketService,
|
|
650
|
+
hyperliquidActions,
|
|
651
|
+
perpetualMarketAction
|
|
652
|
+
};
|
|
653
|
+
//# sourceMappingURL=perpetual-market.js.map
|