opentool 0.8.22 → 0.8.24

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.
@@ -6,6 +6,29 @@ import { hexToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils';
6
6
  // src/adapters/hyperliquid/index.ts
7
7
 
8
8
  // src/store/index.ts
9
+ var STORE_EVENT_LEVELS = [
10
+ "decision",
11
+ "execution",
12
+ "lifecycle"
13
+ ];
14
+ var STORE_EVENT_LEVEL_SET = new Set(STORE_EVENT_LEVELS);
15
+ var MARKET_REQUIRED_ACTIONS = [
16
+ "swap",
17
+ "bridge",
18
+ "order",
19
+ "trade",
20
+ "lend",
21
+ "borrow",
22
+ "repay",
23
+ "stake",
24
+ "unstake",
25
+ "withdraw",
26
+ "provide_liquidity",
27
+ "remove_liquidity",
28
+ "claim"
29
+ ];
30
+ var MARKET_REQUIRED_ACTIONS_SET = new Set(MARKET_REQUIRED_ACTIONS);
31
+ var EXECUTION_ACTIONS_SET = new Set(MARKET_REQUIRED_ACTIONS);
9
32
  var StoreError = class extends Error {
10
33
  constructor(message, status, causeData) {
11
34
  super(message);
@@ -14,13 +37,57 @@ var StoreError = class extends Error {
14
37
  this.name = "StoreError";
15
38
  }
16
39
  };
40
+ var normalizeAction = (action) => {
41
+ const normalized = action?.trim().toLowerCase();
42
+ return normalized ? normalized : null;
43
+ };
44
+ var coerceEventLevel = (value) => {
45
+ if (typeof value !== "string") return null;
46
+ const normalized = value.trim().toLowerCase();
47
+ if (!normalized || !STORE_EVENT_LEVEL_SET.has(normalized)) return null;
48
+ return normalized;
49
+ };
17
50
  var requiresMarketIdentity = (input) => {
18
- const action = (input.action ?? "").toLowerCase();
19
- if (action === "order" || action === "swap" || action === "trade") return true;
20
- return false;
51
+ const action = normalizeAction(input.action);
52
+ if (!action) return false;
53
+ return MARKET_REQUIRED_ACTIONS_SET.has(action);
21
54
  };
22
55
  var hasMarketIdentity = (value) => {
23
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
56
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
57
+ const record = value;
58
+ const requiredKeys = ["market_type", "venue", "environment", "canonical_symbol"];
59
+ return requiredKeys.every((key) => {
60
+ const field = record[key];
61
+ return typeof field === "string" && field.trim().length > 0;
62
+ });
63
+ };
64
+ var resolveEventLevel = (input) => {
65
+ const direct = coerceEventLevel(input.eventLevel);
66
+ if (direct) return direct;
67
+ const metadataLevel = coerceEventLevel(input.metadata?.eventLevel);
68
+ if (metadataLevel) return metadataLevel;
69
+ const action = normalizeAction(input.action);
70
+ if (action && EXECUTION_ACTIONS_SET.has(action) && (input.metadata?.lifecycle === true || typeof input.metadata?.executionRef === "string" || typeof input.metadata?.parentExecutionRef === "string")) {
71
+ return "lifecycle";
72
+ }
73
+ if (action && EXECUTION_ACTIONS_SET.has(action) || hasMarketIdentity(input.market)) {
74
+ return "execution";
75
+ }
76
+ if (action) return "decision";
77
+ return null;
78
+ };
79
+ var normalizeStoreInput = (input) => {
80
+ const metadata = { ...input.metadata ?? {} };
81
+ const eventLevel = resolveEventLevel({ ...input, metadata });
82
+ if (eventLevel) {
83
+ metadata.eventLevel = eventLevel;
84
+ }
85
+ const hasMetadata = Object.keys(metadata).length > 0;
86
+ return {
87
+ ...input,
88
+ ...eventLevel ? { eventLevel } : {},
89
+ ...hasMetadata ? { metadata } : {}
90
+ };
24
91
  };
25
92
  function resolveConfig(options) {
26
93
  const baseUrl = options?.baseUrl ?? process.env.BASE_URL ?? "https://api.openpond.ai";
@@ -41,8 +108,26 @@ function resolveConfig(options) {
41
108
  return { baseUrl: normalizedBaseUrl, apiKey, fetchFn };
42
109
  }
43
110
  async function store(input, options) {
44
- if (requiresMarketIdentity(input) && !hasMarketIdentity(input.market)) {
45
- throw new StoreError("market is required for trade events");
111
+ const normalizedInput = normalizeStoreInput(input);
112
+ const eventLevel = normalizedInput.eventLevel;
113
+ const normalizedAction = normalizeAction(normalizedInput.action);
114
+ if (eventLevel === "execution" || eventLevel === "lifecycle") {
115
+ if (!normalizedAction || !EXECUTION_ACTIONS_SET.has(normalizedAction)) {
116
+ throw new StoreError(
117
+ `eventLevel "${eventLevel}" requires an execution action`
118
+ );
119
+ }
120
+ }
121
+ if (eventLevel === "execution" && !hasMarketIdentity(normalizedInput.market)) {
122
+ throw new StoreError(
123
+ `market is required for execution events. market must include market_type, venue, environment, canonical_symbol`
124
+ );
125
+ }
126
+ const shouldApplyLegacyMarketRule = eventLevel == null || eventLevel === "execution";
127
+ if (shouldApplyLegacyMarketRule && requiresMarketIdentity(normalizedInput) && !hasMarketIdentity(normalizedInput.market)) {
128
+ throw new StoreError(
129
+ `market is required for action "${normalizedInput.action}". market must include market_type, venue, environment, canonical_symbol`
130
+ );
46
131
  }
47
132
  const { baseUrl, apiKey, fetchFn } = resolveConfig(options);
48
133
  const url = `${baseUrl}/apps/positions/tx`;
@@ -54,7 +139,7 @@ async function store(input, options) {
54
139
  "content-type": "application/json",
55
140
  "openpond-api-key": apiKey
56
141
  },
57
- body: JSON.stringify(input)
142
+ body: JSON.stringify(normalizedInput)
58
143
  });
59
144
  } catch (error) {
60
145
  throw new StoreError("Failed to reach store endpoint", void 0, error);
@@ -120,6 +205,7 @@ var BUILDER_CODE = {
120
205
  fee: 100
121
206
  };
122
207
  var metaCache = /* @__PURE__ */ new Map();
208
+ var spotMetaCache = /* @__PURE__ */ new Map();
123
209
  var perpDexsCache = /* @__PURE__ */ new Map();
124
210
  var UNKNOWN_SYMBOL = "UNKNOWN";
125
211
  var extractDexPrefix = (value) => {
@@ -142,6 +228,14 @@ var normalizeHyperliquidBase = (value) => {
142
228
  if (!normalized || normalized === UNKNOWN_SYMBOL) return null;
143
229
  return normalized;
144
230
  };
231
+ var normalizeSpotTokenName = (value) => {
232
+ const raw = (value ?? "").trim().toUpperCase();
233
+ if (!raw) return "";
234
+ if (raw.endsWith("0") && raw.length > 1) {
235
+ return raw.slice(0, -1);
236
+ }
237
+ return raw;
238
+ };
145
239
  var parseHyperliquidPair = (value) => {
146
240
  if (!value) return null;
147
241
  const trimmed = value.trim();
@@ -288,6 +382,29 @@ async function getUniverse(args) {
288
382
  metaCache.set(cacheKey, { fetchedAt: Date.now(), universe: json.universe });
289
383
  return json.universe;
290
384
  }
385
+ async function getSpotMeta(args) {
386
+ const cacheKey = `${args.environment}:${args.baseUrl}`;
387
+ const cached = spotMetaCache.get(cacheKey);
388
+ if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
389
+ return { universe: cached.universe, tokens: cached.tokens };
390
+ }
391
+ const response = await args.fetcher(`${args.baseUrl}/info`, {
392
+ method: "POST",
393
+ headers: { "content-type": "application/json" },
394
+ body: JSON.stringify({ type: "spotMeta" })
395
+ });
396
+ const json = await response.json().catch(() => null);
397
+ if (!response.ok || !json?.universe) {
398
+ throw new HyperliquidApiError(
399
+ "Unable to load Hyperliquid spot metadata.",
400
+ json ?? { status: response.status }
401
+ );
402
+ }
403
+ const universe = json.universe ?? [];
404
+ const tokens = json.tokens ?? [];
405
+ spotMetaCache.set(cacheKey, { fetchedAt: Date.now(), universe, tokens });
406
+ return { universe, tokens };
407
+ }
291
408
  function resolveAssetIndex(symbol, universe) {
292
409
  const [raw] = symbol.split("-");
293
410
  const target = raw.trim();
@@ -331,11 +448,57 @@ async function resolveDexIndex(args) {
331
448
  }
332
449
  return index;
333
450
  }
451
+ function buildSpotTokenIndexMap(tokens) {
452
+ const map = /* @__PURE__ */ new Map();
453
+ for (const token of tokens) {
454
+ const name = normalizeSpotTokenName(token?.name);
455
+ const index = typeof token?.index === "number" && Number.isFinite(token.index) ? token.index : null;
456
+ if (!name || index == null) continue;
457
+ if (!map.has(name) || token?.isCanonical) {
458
+ map.set(name, index);
459
+ }
460
+ }
461
+ return map;
462
+ }
463
+ function resolveSpotTokenIndex(tokenMap, value) {
464
+ const normalized = normalizeSpotTokenName(value);
465
+ if (!normalized) return null;
466
+ const direct = tokenMap.get(normalized);
467
+ if (direct != null) return direct;
468
+ if (!normalized.startsWith("U")) {
469
+ const prefixed = tokenMap.get(`U${normalized}`);
470
+ if (prefixed != null) return prefixed;
471
+ }
472
+ return null;
473
+ }
474
+ function resolveSpotMarketIndex(args) {
475
+ for (let i = 0; i < args.universe.length; i += 1) {
476
+ const entry = args.universe[i];
477
+ const tokens = Array.isArray(entry?.tokens) ? entry.tokens : null;
478
+ const baseToken = tokens?.[0] ?? entry?.baseToken ?? null;
479
+ const quoteToken = tokens?.[1] ?? entry?.quoteToken ?? null;
480
+ if (baseToken === args.baseToken && quoteToken === args.quoteToken) {
481
+ if (typeof entry?.index === "number" && Number.isFinite(entry.index)) {
482
+ return entry.index;
483
+ }
484
+ return i;
485
+ }
486
+ }
487
+ return null;
488
+ }
334
489
  async function resolveHyperliquidAssetIndex(args) {
335
490
  const trimmed = args.symbol.trim();
336
491
  if (!trimmed) {
337
492
  throw new Error("Hyperliquid symbol must be a non-empty string.");
338
493
  }
494
+ if (trimmed.startsWith("@")) {
495
+ const rawIndex = trimmed.slice(1).trim();
496
+ const index = Number(rawIndex);
497
+ if (!Number.isFinite(index)) {
498
+ throw new Error(`Hyperliquid spot market index is invalid: ${trimmed}`);
499
+ }
500
+ return 1e4 + index;
501
+ }
339
502
  const separator = trimmed.indexOf(":");
340
503
  if (separator > 0) {
341
504
  const dex = trimmed.slice(0, separator).trim();
@@ -362,6 +525,29 @@ async function resolveHyperliquidAssetIndex(args) {
362
525
  }
363
526
  return 1e5 + dexIndex * 1e4 + assetIndex;
364
527
  }
528
+ const pair = parseHyperliquidPair(trimmed);
529
+ if (pair) {
530
+ const { universe: universe2, tokens } = await getSpotMeta({
531
+ baseUrl: args.baseUrl,
532
+ environment: args.environment,
533
+ fetcher: args.fetcher
534
+ });
535
+ const tokenMap = buildSpotTokenIndexMap(tokens);
536
+ const baseToken = resolveSpotTokenIndex(tokenMap, pair.base);
537
+ const quoteToken = resolveSpotTokenIndex(tokenMap, pair.quote);
538
+ if (baseToken == null || quoteToken == null) {
539
+ throw new Error(`Unknown Hyperliquid spot symbol: ${trimmed}`);
540
+ }
541
+ const marketIndex = resolveSpotMarketIndex({
542
+ universe: universe2,
543
+ baseToken,
544
+ quoteToken
545
+ });
546
+ if (marketIndex == null) {
547
+ throw new Error(`Unknown Hyperliquid spot symbol: ${trimmed}`);
548
+ }
549
+ return 1e4 + marketIndex;
550
+ }
365
551
  const universe = await getUniverse({
366
552
  baseUrl: args.baseUrl,
367
553
  environment: args.environment,