@pear-protocol/hyperliquid-sdk 0.0.65 → 0.0.66-usdh-2

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/index.js CHANGED
@@ -23,6 +23,7 @@ const useUserData = create((set) => ({
23
23
  twapDetails: null,
24
24
  notifications: null,
25
25
  userExtraAgents: null,
26
+ spotState: null,
26
27
  setAccessToken: (token) => set({ accessToken: token }),
27
28
  setRefreshToken: (token) => set({ refreshToken: token }),
28
29
  setIsAuthenticated: (value) => set({ isAuthenticated: value }),
@@ -43,6 +44,7 @@ const useUserData = create((set) => ({
43
44
  setAccountSummary: (value) => set({ accountSummary: value }),
44
45
  setTwapDetails: (value) => set({ twapDetails: value }),
45
46
  setNotifications: (value) => set({ notifications: value }),
47
+ setSpotState: (value) => set({ spotState: value }),
46
48
  clean: () => set({
47
49
  accessToken: null,
48
50
  refreshToken: null,
@@ -54,6 +56,7 @@ const useUserData = create((set) => ({
54
56
  accountSummary: null,
55
57
  twapDetails: null,
56
58
  notifications: null,
59
+ spotState: null,
57
60
  }),
58
61
  setUserExtraAgents: (value) => set({ userExtraAgents: value }),
59
62
  }));
@@ -71,18 +74,71 @@ const useMarketData = create((set) => ({
71
74
  * Convert a full/prefixed symbol (e.g., "xyz:XYZ100") to a display symbol (e.g., "XYZ100").
72
75
  */
73
76
  function toDisplaySymbol(symbol) {
74
- const parts = symbol.split(":");
77
+ const parts = symbol.split(':');
75
78
  return parts.length > 1 ? parts.slice(-1)[0] : symbol;
76
79
  }
77
80
  /**
78
81
  * Convert a display symbol back to backend form using a provided map.
79
82
  * If mapping is missing, returns the original symbol.
80
- * @param displaySymbol e.g., "XYZ100"
81
- * @param displayToFull map of display -> full (e.g., "XYZ100" -> "xyz:XYZ100")
83
+ * For multi-market assets, returns the first available market.
84
+ * @param displaySymbol e.g., "TSLA"
85
+ * @param hip3Assets map of display -> all full market names (e.g., "TSLA" -> ["xyz:TSLA", "flx:TSLA"])
82
86
  */
83
- function toBackendSymbol(displaySymbol, displayToFull) {
87
+ function toBackendSymbol(displaySymbol, hip3Assets) {
88
+ const markets = hip3Assets.get(displaySymbol);
89
+ // Return first market if available, otherwise return original symbol
90
+ return markets && markets.length > 0 ? markets[0] : displaySymbol;
91
+ }
92
+ /**
93
+ * Convert a display symbol to backend form for a specific market prefix.
94
+ * This is useful when an asset is available on multiple markets (e.g., xyz:TSLA and flx:TSLA).
95
+ * @param displaySymbol e.g., "TSLA"
96
+ * @param marketPrefix e.g., "xyz" or "flx"
97
+ * @param hip3Assets map of display -> all full market names
98
+ * @returns Full market name if found, null if prefix not specified for multi-market asset, otherwise displaySymbol with prefix
99
+ */
100
+ function toBackendSymbolWithMarket(displaySymbol, marketPrefix, hip3Assets) {
101
+ const availableMarkets = hip3Assets.get(displaySymbol);
102
+ if (!availableMarkets || availableMarkets.length === 0) {
103
+ // Not a HIP-3 asset, return as-is or with prefix if provided
104
+ return marketPrefix ? `${marketPrefix}:${displaySymbol}` : displaySymbol;
105
+ }
106
+ if (marketPrefix) {
107
+ // Find the market with the specified prefix
108
+ const targetMarket = availableMarkets.find((market) => market.toLowerCase().startsWith(`${marketPrefix.toLowerCase()}:`));
109
+ if (targetMarket) {
110
+ return targetMarket;
111
+ }
112
+ }
113
+ // No prefix specified or not found, return null to force explicit market selection
114
+ return null;
115
+ }
116
+ /**
117
+ * Get all available markets for a display symbol.
118
+ * @param displaySymbol e.g., "TSLA"
119
+ * @param hip3Assets map of display -> all full market names
120
+ * @returns Array of full market names, e.g., ["xyz:TSLA", "flx:TSLA"]
121
+ */
122
+ function getAvailableMarkets(displaySymbol, hip3Assets) {
84
123
  var _a;
85
- return (_a = displayToFull.get(displaySymbol)) !== null && _a !== void 0 ? _a : displaySymbol;
124
+ return (_a = hip3Assets.get(displaySymbol)) !== null && _a !== void 0 ? _a : [];
125
+ }
126
+ /**
127
+ * Extract the market prefix from a full market name.
128
+ * @param fullSymbol e.g., "xyz:TSLA"
129
+ * @returns The prefix (e.g., "xyz") or undefined if no prefix
130
+ */
131
+ function getMarketPrefix(fullSymbol) {
132
+ const parts = fullSymbol.split(':');
133
+ return parts.length > 1 ? parts[0] : undefined;
134
+ }
135
+ /**
136
+ * Check if a symbol is a HIP-3 market (has a prefix).
137
+ * @param symbol e.g., "xyz:TSLA" or "TSLA"
138
+ * @returns true if the symbol has a market prefix
139
+ */
140
+ function isHip3Market(symbol) {
141
+ return symbol.includes(':');
86
142
  }
87
143
 
88
144
  const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
@@ -99,7 +155,8 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
99
155
  try {
100
156
  const message = JSON.parse(event.data);
101
157
  // Handle subscription responses (only if they don't have channel data)
102
- if (('success' in message || 'error' in message) && !('channel' in message)) {
158
+ if (('success' in message || 'error' in message) &&
159
+ !('channel' in message)) {
103
160
  if (message.error) {
104
161
  setLastError(message.error);
105
162
  }
@@ -118,12 +175,21 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
118
175
  switch (dataMessage.channel) {
119
176
  case 'trade-histories':
120
177
  {
178
+ const mapAsset = (a) => {
179
+ var _a, _b;
180
+ const extractedPrefix = getMarketPrefix(a.coin);
181
+ return {
182
+ ...a,
183
+ coin: toDisplaySymbol(a.coin),
184
+ marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
185
+ };
186
+ };
121
187
  const list = dataMessage.data.map((item) => {
122
188
  var _a, _b;
123
189
  return ({
124
190
  ...item,
125
- closedLongAssets: item.closedLongAssets.map((a) => ({ ...a, coin: toDisplaySymbol(a.coin) })),
126
- closedShortAssets: item.closedShortAssets.map((a) => ({ ...a, coin: toDisplaySymbol(a.coin) })),
191
+ closedLongAssets: item.closedLongAssets.map(mapAsset),
192
+ closedShortAssets: item.closedShortAssets.map(mapAsset),
127
193
  positionLongAssets: (_a = item.positionLongAssets) === null || _a === void 0 ? void 0 : _a.map((a) => toDisplaySymbol(a)),
128
194
  positionShortAssets: (_b = item.positionShortAssets) === null || _b === void 0 ? void 0 : _b.map((a) => toDisplaySymbol(a)),
129
195
  });
@@ -133,10 +199,19 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
133
199
  break;
134
200
  case 'open-positions':
135
201
  {
202
+ const enrichAsset = (a) => {
203
+ var _a, _b;
204
+ const extractedPrefix = getMarketPrefix(a.coin);
205
+ return {
206
+ ...a,
207
+ coin: toDisplaySymbol(a.coin),
208
+ marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
209
+ };
210
+ };
136
211
  const list = dataMessage.data.map((pos) => ({
137
212
  ...pos,
138
- longAssets: pos.longAssets.map((a) => ({ ...a, coin: toDisplaySymbol(a.coin) })),
139
- shortAssets: pos.shortAssets.map((a) => ({ ...a, coin: toDisplaySymbol(a.coin) })),
213
+ longAssets: pos.longAssets.map(enrichAsset),
214
+ shortAssets: pos.shortAssets.map(enrichAsset),
140
215
  }));
141
216
  setRawOpenPositions(list);
142
217
  }
@@ -145,8 +220,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
145
220
  {
146
221
  const list = dataMessage.data.map((order) => ({
147
222
  ...order,
148
- longAssets: order.longAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
149
- shortAssets: order.shortAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
223
+ longAssets: order.longAssets.map((a) => ({
224
+ ...a,
225
+ asset: toDisplaySymbol(a.asset),
226
+ })),
227
+ shortAssets: order.shortAssets.map((a) => ({
228
+ ...a,
229
+ asset: toDisplaySymbol(a.asset),
230
+ })),
150
231
  }));
151
232
  setOpenOrders(list);
152
233
  }
@@ -156,10 +237,20 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
156
237
  break;
157
238
  case 'twap-details':
158
239
  {
240
+ const mapTwapAsset = (a) => {
241
+ var _a, _b, _c;
242
+ const extractedPrefix = getMarketPrefix(a.asset);
243
+ return {
244
+ ...a,
245
+ asset: toDisplaySymbol(a.asset),
246
+ marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
247
+ collateralToken: (_c = a.collateralToken) !== null && _c !== void 0 ? _c : undefined,
248
+ };
249
+ };
159
250
  const list = dataMessage.data.map((twap) => ({
160
251
  ...twap,
161
- longAssets: twap.longAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
162
- shortAssets: twap.shortAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
252
+ longAssets: twap.longAssets.map(mapTwapAsset),
253
+ shortAssets: twap.shortAssets.map(mapTwapAsset),
163
254
  }));
164
255
  setTwapDetails(list);
165
256
  }
@@ -172,8 +263,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
172
263
  const md = dataMessage.data;
173
264
  const mapGroup = (g) => ({
174
265
  ...g,
175
- longAssets: g.longAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
176
- shortAssets: g.shortAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
266
+ longAssets: g.longAssets.map((a) => ({
267
+ ...a,
268
+ asset: toDisplaySymbol(a.asset),
269
+ })),
270
+ shortAssets: g.shortAssets.map((a) => ({
271
+ ...a,
272
+ asset: toDisplaySymbol(a.asset),
273
+ })),
177
274
  });
178
275
  const mapped = {
179
276
  active: md.active.map(mapGroup),
@@ -191,7 +288,15 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
191
288
  catch (error) {
192
289
  setLastError(`Failed to parse message: ${error instanceof Error ? error.message : String(error)}`);
193
290
  }
194
- }, [setTradeHistories, setRawOpenPositions, setOpenOrders, setAccountSummary, setTwapDetails, setNotifications, setMarketData]);
291
+ }, [
292
+ setTradeHistories,
293
+ setRawOpenPositions,
294
+ setOpenOrders,
295
+ setAccountSummary,
296
+ setTwapDetails,
297
+ setNotifications,
298
+ setMarketData,
299
+ ]);
195
300
  const connect = useCallback(() => {
196
301
  if (!enabled || !wsUrl)
197
302
  return;
@@ -260,7 +365,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
260
365
  'open-orders',
261
366
  'twap-details',
262
367
  'fills-checkpoint',
263
- 'notifications'
368
+ 'notifications',
264
369
  ];
265
370
  const globalChannels = ['market-data'];
266
371
  if (address && address !== lastSubscribedAddress) {
@@ -269,14 +374,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
269
374
  sendMessage(JSON.stringify({
270
375
  action: 'unsubscribe',
271
376
  address: lastSubscribedAddress,
272
- channels: addressSpecificChannels
377
+ channels: addressSpecificChannels,
273
378
  }));
274
379
  }
275
380
  // Subscribe to all channels (global + address-specific)
276
381
  sendMessage(JSON.stringify({
277
382
  action: 'subscribe',
278
383
  address: address,
279
- channels: [...globalChannels, ...addressSpecificChannels]
384
+ channels: [...globalChannels, ...addressSpecificChannels],
280
385
  }));
281
386
  setLastSubscribedAddress(address);
282
387
  setLastError(null);
@@ -286,7 +391,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
286
391
  sendMessage(JSON.stringify({
287
392
  action: 'unsubscribe',
288
393
  address: lastSubscribedAddress,
289
- channels: addressSpecificChannels
394
+ channels: addressSpecificChannels,
290
395
  }));
291
396
  setLastSubscribedAddress(null);
292
397
  }
@@ -294,7 +399,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
294
399
  // If no address but connected, subscribe to global channels only
295
400
  sendMessage(JSON.stringify({
296
401
  action: 'subscribe',
297
- channels: globalChannels
402
+ channels: globalChannels,
298
403
  }));
299
404
  }
300
405
  }, [isConnected, address, lastSubscribedAddress, sendMessage]);
@@ -318,10 +423,12 @@ const useHyperliquidData = create((set, get) => ({
318
423
  finalAtOICaps: null,
319
424
  aggregatedClearingHouseState: null,
320
425
  perpMetaAssets: null,
321
- hip3DisplayToFull: new Map(),
426
+ allPerpMetaAssets: null,
427
+ hip3Assets: new Map(),
428
+ hip3MarketPrefixes: new Map(),
322
429
  setAllMids: (value) => set({ allMids: value }),
323
430
  setActiveAssetData: (value) => set((state) => ({
324
- activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value
431
+ activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value,
325
432
  })),
326
433
  deleteActiveAssetData: (key) => {
327
434
  set((state) => {
@@ -356,15 +463,130 @@ const useHyperliquidData = create((set, get) => ({
356
463
  activeAssetData: {
357
464
  ...state.activeAssetData,
358
465
  [key]: value,
359
- }
466
+ },
360
467
  })),
361
468
  setFinalAssetContexts: (value) => set({ finalAssetContexts: value }),
362
469
  setFinalAtOICaps: (value) => set({ finalAtOICaps: value }),
363
470
  setAggregatedClearingHouseState: (value) => set({ aggregatedClearingHouseState: value }),
364
471
  setPerpMetaAssets: (value) => set({ perpMetaAssets: value }),
365
- setHip3DisplayToFull: (value) => set({ hip3DisplayToFull: value })
472
+ setAllPerpMetaAssets: (value) => set({ allPerpMetaAssets: value }),
473
+ setHip3Assets: (value) => set({ hip3Assets: value }),
474
+ setHip3MarketPrefixes: (value) => set({ hip3MarketPrefixes: value }),
366
475
  }));
367
476
 
477
+ /**
478
+ * Minimum USD value required per asset when creating a position
479
+ */
480
+ const MINIMUM_ASSET_USD_VALUE = 11;
481
+ /**
482
+ * Maximum number of assets allowed per leg (long or short) in a position
483
+ */
484
+ const MAX_ASSETS_PER_LEG = 15;
485
+ /**
486
+ * Validation error for minimum position size
487
+ */
488
+ class MinimumPositionSizeError extends Error {
489
+ constructor(assetName, assetValue, minimumRequired) {
490
+ super(`Asset "${assetName}" has a USD value of $${assetValue.toFixed(2)}, which is below the minimum required value of $${minimumRequired.toFixed(2)}`);
491
+ this.assetName = assetName;
492
+ this.assetValue = assetValue;
493
+ this.minimumRequired = minimumRequired;
494
+ this.name = "MinimumPositionSizeError";
495
+ }
496
+ }
497
+ /**
498
+ * Validation error for exceeding maximum assets per leg
499
+ */
500
+ class MaxAssetsPerLegError extends Error {
501
+ constructor(leg, assetCount, maxAllowed) {
502
+ super(`Maximum ${maxAllowed} assets allowed per leg. Your ${leg} leg has ${assetCount} assets. Please reduce the number of assets to continue.`);
503
+ this.leg = leg;
504
+ this.assetCount = assetCount;
505
+ this.maxAllowed = maxAllowed;
506
+ this.name = "MaxAssetsPerLegError";
507
+ }
508
+ }
509
+ /**
510
+ * Validates that each leg doesn't exceed the maximum number of assets
511
+ * @param longAssets Array of long assets
512
+ * @param shortAssets Array of short assets
513
+ * @throws MaxAssetsPerLegError if any leg exceeds the maximum allowed assets
514
+ */
515
+ function validateMaxAssetsPerLeg(longAssets, shortAssets) {
516
+ const longCount = (longAssets === null || longAssets === void 0 ? void 0 : longAssets.length) || 0;
517
+ const shortCount = (shortAssets === null || shortAssets === void 0 ? void 0 : shortAssets.length) || 0;
518
+ if (longCount > MAX_ASSETS_PER_LEG) {
519
+ throw new MaxAssetsPerLegError("long", longCount, MAX_ASSETS_PER_LEG);
520
+ }
521
+ if (shortCount > MAX_ASSETS_PER_LEG) {
522
+ throw new MaxAssetsPerLegError("short", shortCount, MAX_ASSETS_PER_LEG);
523
+ }
524
+ }
525
+ /**
526
+ * Validates that each asset in a position has at least the minimum USD value
527
+ * @param usdValue Total USD value for the position
528
+ * @param longAssets Array of long assets with weights
529
+ * @param shortAssets Array of short assets with weights
530
+ * @throws MinimumPositionSizeError if any asset has less than the minimum USD value
531
+ */
532
+ function validateMinimumAssetSize(usdValue, longAssets, shortAssets) {
533
+ var _a;
534
+ const allAssets = [...(longAssets || []), ...(shortAssets || [])];
535
+ if (allAssets.length === 0) {
536
+ return; // No assets to validate
537
+ }
538
+ // Calculate total weight
539
+ const totalWeight = allAssets.reduce((sum, asset) => { var _a; return sum + ((_a = asset.weight) !== null && _a !== void 0 ? _a : 0); }, 0);
540
+ // If weights are not provided or sum to 0, assume equal distribution
541
+ const hasWeights = totalWeight > 0;
542
+ const equalWeight = hasWeights ? 0 : 1 / allAssets.length;
543
+ // Validate each asset
544
+ for (const asset of allAssets) {
545
+ const weight = hasWeights ? (_a = asset.weight) !== null && _a !== void 0 ? _a : 0 : equalWeight;
546
+ const assetUsdValue = usdValue * weight;
547
+ if (assetUsdValue < MINIMUM_ASSET_USD_VALUE) {
548
+ throw new MinimumPositionSizeError(asset.asset, assetUsdValue, MINIMUM_ASSET_USD_VALUE);
549
+ }
550
+ }
551
+ }
552
+ /**
553
+ * Calculates the minimum USD value required for a position based on the number of assets
554
+ * @param longAssets Array of long assets
555
+ * @param shortAssets Array of short assets
556
+ * @returns The minimum total USD value required
557
+ */
558
+ function calculateMinimumPositionValue(longAssets, shortAssets) {
559
+ const totalAssets = ((longAssets === null || longAssets === void 0 ? void 0 : longAssets.length) || 0) + ((shortAssets === null || shortAssets === void 0 ? void 0 : shortAssets.length) || 0);
560
+ if (totalAssets === 0) {
561
+ return 0;
562
+ }
563
+ return MINIMUM_ASSET_USD_VALUE * totalAssets;
564
+ }
565
+ /**
566
+ * Validates and provides a user-friendly error message with suggestions
567
+ * @param usdValue Total USD value for the position
568
+ * @param longAssets Array of long assets with weights
569
+ * @param shortAssets Array of short assets with weights
570
+ * @returns Validation result with success flag and optional error message
571
+ */
572
+ function validatePositionSize(usdValue, longAssets, shortAssets) {
573
+ try {
574
+ validateMinimumAssetSize(usdValue, longAssets, shortAssets);
575
+ return { valid: true };
576
+ }
577
+ catch (error) {
578
+ if (error instanceof MinimumPositionSizeError) {
579
+ const minimumRequired = calculateMinimumPositionValue(longAssets, shortAssets);
580
+ return {
581
+ valid: false,
582
+ error: error.message,
583
+ minimumRequired,
584
+ };
585
+ }
586
+ throw error;
587
+ }
588
+ }
589
+
368
590
  const DEFAULT_STATE = {
369
591
  longTokens: [
370
592
  { symbol: "HYPE", weight: 25 },
@@ -413,14 +635,23 @@ const useUserSelection$1 = create((set, get) => ({
413
635
  }
414
636
  });
415
637
  },
638
+ canAddToken: (isLong) => {
639
+ const currentTokens = isLong ? get().longTokens : get().shortTokens;
640
+ return currentTokens.length < MAX_ASSETS_PER_LEG;
641
+ },
416
642
  addToken: (isLong) => {
417
643
  const currentTokens = isLong ? get().longTokens : get().shortTokens;
644
+ // Check if we've reached the maximum number of assets per leg
645
+ if (currentTokens.length >= MAX_ASSETS_PER_LEG) {
646
+ return false;
647
+ }
418
648
  const newIndex = currentTokens.length;
419
649
  set((prev) => ({
420
650
  ...prev,
421
651
  selectorConfig: { isLong, index: newIndex },
422
652
  openTokenSelector: true,
423
653
  }));
654
+ return true;
424
655
  },
425
656
  removeToken: (isLong, index) => {
426
657
  set((prev) => {
@@ -503,11 +734,12 @@ const useUserSelection$1 = create((set, get) => ({
503
734
  }));
504
735
 
505
736
  const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
506
- const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState } = useHyperliquidData();
737
+ const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState, } = useHyperliquidData();
738
+ const { setSpotState } = useUserData();
507
739
  const { candleInterval } = useUserSelection$1();
508
740
  const longTokens = useUserSelection$1((s) => s.longTokens);
509
741
  const shortTokens = useUserSelection$1((s) => s.shortTokens);
510
- const selectedTokenSymbols = useMemo(() => ([...longTokens, ...shortTokens].map((t) => t.symbol)), [longTokens, shortTokens]);
742
+ const selectedTokenSymbols = useMemo(() => [...longTokens, ...shortTokens].map((t) => t.symbol), [longTokens, shortTokens]);
511
743
  const [lastError, setLastError] = useState(null);
512
744
  const [subscribedAddress, setSubscribedAddress] = useState(null);
513
745
  const [subscribedTokens, setSubscribedTokens] = useState([]);
@@ -545,7 +777,21 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
545
777
  case 'allDexsAssetCtxs':
546
778
  {
547
779
  const data = response.data;
548
- const finalAssetContexts = (data.ctxs || []).flatMap(([, ctxs]) => ctxs || []);
780
+ // Filter out vntl and hyna to match perpMetaAssets filtering
781
+ const FILTERED_DEX_PREFIXES = ['vntl', 'hyna'];
782
+ const filtered = (data.ctxs || [])
783
+ .filter(([prefix]) => !FILTERED_DEX_PREFIXES.includes((prefix || '').toLowerCase()))
784
+ .sort((a, b) => {
785
+ // Sort to match perpMetaAssets order: default market first, then alphabetically
786
+ const prefixA = a[0] || '';
787
+ const prefixB = b[0] || '';
788
+ if (prefixA === '' && prefixB !== '')
789
+ return -1;
790
+ if (prefixA !== '' && prefixB === '')
791
+ return 1;
792
+ return prefixA.localeCompare(prefixB);
793
+ });
794
+ const finalAssetContexts = filtered.flatMap(([, ctxs]) => ctxs || []);
549
795
  setFinalAssetContexts(finalAssetContexts);
550
796
  }
551
797
  break;
@@ -556,7 +802,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
556
802
  .map(([, s]) => s)
557
803
  .filter(Boolean);
558
804
  const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || '0') || 0), 0);
559
- const toStr = (n) => (Number.isFinite(n) ? n.toString() : '0');
805
+ const toStr = (n) => Number.isFinite(n) ? n.toString() : '0';
560
806
  const assetPositions = states.flatMap((s) => s.assetPositions || []);
561
807
  const crossMaintenanceMarginUsed = toStr(sum(states.map((s) => s.crossMaintenanceMarginUsed)));
562
808
  const crossMarginSummary = {
@@ -587,19 +833,42 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
587
833
  case 'allMids':
588
834
  {
589
835
  const data = response.data;
590
- const remapped = {
591
- mids: Object.fromEntries(
592
- // only support non hip-3 and xyz market
593
- Object.entries(data.mids || {}).filter(([k, v]) => !k.includes(':') || k.includes('xyz:')).map(([k, v]) => [toDisplaySymbol(k), v]))
594
- };
595
- setAllMids(remapped);
836
+ // Keep BOTH normalized prefixed keys AND display symbol keys
837
+ // This ensures xyz:TSLA and flx:TSLA are stored separately,
838
+ // while also maintaining backward compatibility with non-prefixed lookups
839
+ const mids = {};
840
+ Object.entries(data.mids || {}).forEach(([k, v]) => {
841
+ // Normalize prefixed keys to lowercase prefix (e.g., "XYZ:TSLA" -> "xyz:TSLA")
842
+ // This matches how we look up tokens in the SDK
843
+ let normalizedKey = k;
844
+ if (k.includes(':')) {
845
+ const [prefix, ...rest] = k.split(':');
846
+ normalizedKey = `${prefix.toLowerCase()}:${rest.join(':')}`;
847
+ }
848
+ // Store with normalized key
849
+ mids[normalizedKey] = v;
850
+ // Also store with original key for backward compatibility
851
+ if (k !== normalizedKey) {
852
+ mids[k] = v;
853
+ }
854
+ // Also store with display symbol for backward compatibility
855
+ const displayKey = toDisplaySymbol(k);
856
+ // Only set display key if it doesn't already exist (avoid overwriting market-specific prices)
857
+ if (!(displayKey in mids)) {
858
+ mids[displayKey] = v;
859
+ }
860
+ });
861
+ setAllMids({ mids });
596
862
  }
597
863
  break;
598
864
  case 'activeAssetData':
599
865
  {
600
866
  const assetData = response.data;
601
867
  const symbol = toDisplaySymbol(assetData.coin);
602
- const normalized = { ...assetData, coin: symbol };
868
+ const normalized = {
869
+ ...assetData,
870
+ coin: symbol,
871
+ };
603
872
  upsertActiveAssetData(symbol, normalized);
604
873
  }
605
874
  break;
@@ -611,6 +880,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
611
880
  addCandleData(symbol, normalized);
612
881
  }
613
882
  break;
883
+ case 'spotState':
884
+ {
885
+ const spotStateData = response.data;
886
+ if (spotStateData === null || spotStateData === void 0 ? void 0 : spotStateData.spotState) {
887
+ setSpotState(spotStateData.spotState);
888
+ }
889
+ }
890
+ break;
614
891
  default:
615
892
  console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
616
893
  }
@@ -621,7 +898,15 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
621
898
  console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
622
899
  setLastError(errorMessage);
623
900
  }
624
- }, [setAllMids, upsertActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState]);
901
+ }, [
902
+ setAllMids,
903
+ upsertActiveAssetData,
904
+ addCandleData,
905
+ setFinalAssetContexts,
906
+ setFinalAtOICaps,
907
+ setAggregatedClearingHouseState,
908
+ setSpotState,
909
+ ]);
625
910
  const connect = useCallback(() => {
626
911
  if (!enabled)
627
912
  return;
@@ -719,6 +1004,17 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
719
1004
  },
720
1005
  };
721
1006
  sendJsonMessage(unsubscribeMessage);
1007
+ // Unsubscribe from spotState for previous address
1008
+ if (subscribedAddress !== DEFAULT_ADDRESS) {
1009
+ const unsubscribeSpotState = {
1010
+ method: 'unsubscribe',
1011
+ subscription: {
1012
+ type: 'spotState',
1013
+ user: subscribedAddress,
1014
+ },
1015
+ };
1016
+ sendJsonMessage(unsubscribeSpotState);
1017
+ }
722
1018
  const unsubscribeAllDexsClearinghouseState = {
723
1019
  method: 'unsubscribe',
724
1020
  subscription: {
@@ -762,13 +1058,34 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
762
1058
  sendJsonMessage(subscribeAllDexsClearinghouseState);
763
1059
  sendJsonMessage(subscribeAllMids);
764
1060
  sendJsonMessage(subscribeAllDexsAssetCtxs);
1061
+ // Subscribe to spotState for real-time spot balances (USDH, USDC, etc.)
1062
+ // Only subscribe if we have a real user address (not the default)
1063
+ if (userAddress !== DEFAULT_ADDRESS) {
1064
+ const subscribeSpotState = {
1065
+ method: 'subscribe',
1066
+ subscription: {
1067
+ type: 'spotState',
1068
+ user: userAddress,
1069
+ },
1070
+ };
1071
+ sendJsonMessage(subscribeSpotState);
1072
+ }
765
1073
  setSubscribedAddress(userAddress);
766
1074
  // Clear previous data when address changes
767
1075
  if (subscribedAddress && subscribedAddress !== userAddress) {
768
1076
  // clear aggregatedClearingHouseState
769
1077
  setAggregatedClearingHouseState(null);
1078
+ // clear spotState
1079
+ setSpotState(null);
770
1080
  }
771
- }, [isConnected, address, subscribedAddress, sendJsonMessage, setAggregatedClearingHouseState]);
1081
+ }, [
1082
+ isConnected,
1083
+ address,
1084
+ subscribedAddress,
1085
+ sendJsonMessage,
1086
+ setAggregatedClearingHouseState,
1087
+ setSpotState,
1088
+ ]);
772
1089
  // Handle token subscriptions for activeAssetData
773
1090
  useEffect(() => {
774
1091
  if (!isConnected || !address)
@@ -777,7 +1094,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
777
1094
  const tokensToSubscribe = effectiveTokens.filter((token) => token && !subscribedTokens.includes(token));
778
1095
  const tokensToUnsubscribe = subscribedTokens.filter((token) => !effectiveTokens.includes(token));
779
1096
  // Unsubscribe from tokens no longer in the list
780
- tokensToUnsubscribe.forEach(token => {
1097
+ tokensToUnsubscribe.forEach((token) => {
781
1098
  const unsubscribeMessage = {
782
1099
  method: 'unsubscribe',
783
1100
  subscription: {
@@ -789,7 +1106,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
789
1106
  sendJsonMessage(unsubscribeMessage);
790
1107
  });
791
1108
  // Subscribe to new tokens
792
- tokensToSubscribe.forEach(token => {
1109
+ tokensToSubscribe.forEach((token) => {
793
1110
  const subscribeMessage = {
794
1111
  method: 'subscribe',
795
1112
  subscription: {
@@ -804,7 +1121,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
804
1121
  setSubscribedTokens(effectiveTokens.filter((token) => token));
805
1122
  tokensToSubscribe.forEach((token) => deleteActiveAssetData(token));
806
1123
  }
807
- }, [isConnected, address, selectedTokenSymbols, subscribedTokens, sendJsonMessage, setActiveAssetData]);
1124
+ }, [
1125
+ isConnected,
1126
+ address,
1127
+ selectedTokenSymbols,
1128
+ subscribedTokens,
1129
+ sendJsonMessage,
1130
+ setActiveAssetData,
1131
+ ]);
808
1132
  // Handle candle subscriptions for tokens and interval changes
809
1133
  useEffect(() => {
810
1134
  if (!isConnected)
@@ -813,7 +1137,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
813
1137
  // Unsubscribe from previous candle subscriptions if interval changed
814
1138
  const prevInterval = prevCandleIntervalRef.current;
815
1139
  if (prevInterval && prevInterval !== candleInterval) {
816
- subscribedCandleTokens.forEach(token => {
1140
+ subscribedCandleTokens.forEach((token) => {
817
1141
  const unsubscribeMessage = {
818
1142
  method: 'unsubscribe',
819
1143
  subscription: {
@@ -854,12 +1178,21 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
854
1178
  sendJsonMessage(subscribeMessage);
855
1179
  });
856
1180
  // Update subscribed state
857
- if (tokensToSubscribe.length > 0 || tokensToUnsubscribe.length > 0 || prevInterval !== candleInterval) {
1181
+ if (tokensToSubscribe.length > 0 ||
1182
+ tokensToUnsubscribe.length > 0 ||
1183
+ prevInterval !== candleInterval) {
858
1184
  setSubscribedCandleTokens(effectiveTokens.filter((token) => token));
859
1185
  prevCandleIntervalRef.current = candleInterval;
860
1186
  tokensToUnsubscribe.forEach((token) => deleteCandleSymbol(token));
861
1187
  }
862
- }, [isConnected, selectedTokenSymbols, candleInterval, subscribedCandleTokens, sendJsonMessage, setCandleData]);
1188
+ }, [
1189
+ isConnected,
1190
+ selectedTokenSymbols,
1191
+ candleInterval,
1192
+ subscribedCandleTokens,
1193
+ sendJsonMessage,
1194
+ setCandleData,
1195
+ ]);
863
1196
  return {
864
1197
  isConnected,
865
1198
  lastError,
@@ -948,20 +1281,112 @@ const useAccountSummary = () => {
948
1281
  return { data: calculated, isLoading };
949
1282
  };
950
1283
 
1284
+ function findAssetMeta$4(coinName, perpMetaAssets, knownPrefix, desiredCollateral) {
1285
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
1286
+ if (!perpMetaAssets) {
1287
+ return { collateralToken: 'USDC', marketPrefix: null };
1288
+ }
1289
+ if (desiredCollateral) {
1290
+ const collateralMatch = perpMetaAssets.find((a) => a.name === coinName && a.collateralToken === desiredCollateral);
1291
+ if (collateralMatch) {
1292
+ return {
1293
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
1294
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
1295
+ };
1296
+ }
1297
+ }
1298
+ if (coinName.includes(':')) {
1299
+ const [prefix, symbol] = coinName.split(':');
1300
+ const exactMatch = perpMetaAssets.find((a) => {
1301
+ var _a;
1302
+ return a.name === symbol &&
1303
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
1304
+ });
1305
+ if (exactMatch) {
1306
+ return {
1307
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
1308
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
1309
+ };
1310
+ }
1311
+ }
1312
+ if (knownPrefix) {
1313
+ const exactMatch = perpMetaAssets.find((a) => {
1314
+ var _a;
1315
+ return a.name === coinName &&
1316
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
1317
+ });
1318
+ if (exactMatch) {
1319
+ return {
1320
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
1321
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
1322
+ };
1323
+ }
1324
+ }
1325
+ const exactMatch = perpMetaAssets.find((a) => a.name === coinName && !a.marketPrefix);
1326
+ if (exactMatch) {
1327
+ return {
1328
+ collateralToken: (_g = exactMatch.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
1329
+ marketPrefix: (_h = exactMatch.marketPrefix) !== null && _h !== void 0 ? _h : null,
1330
+ };
1331
+ }
1332
+ const hip3Matches = perpMetaAssets.filter((a) => a.name === coinName && a.marketPrefix);
1333
+ if (hip3Matches.length > 0) {
1334
+ if (desiredCollateral) {
1335
+ const collateralMatch = hip3Matches.find((a) => a.collateralToken === desiredCollateral);
1336
+ if (collateralMatch) {
1337
+ return {
1338
+ collateralToken: (_j = collateralMatch.collateralToken) !== null && _j !== void 0 ? _j : 'USDC',
1339
+ marketPrefix: (_k = collateralMatch.marketPrefix) !== null && _k !== void 0 ? _k : null,
1340
+ };
1341
+ }
1342
+ }
1343
+ const usdHMatch = hip3Matches.find((a) => a.collateralToken === 'USDH');
1344
+ const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Matches[0];
1345
+ return {
1346
+ collateralToken: (_l = chosen.collateralToken) !== null && _l !== void 0 ? _l : 'USDC',
1347
+ marketPrefix: (_m = chosen.marketPrefix) !== null && _m !== void 0 ? _m : null,
1348
+ };
1349
+ }
1350
+ return { collateralToken: 'USDC', marketPrefix: null };
1351
+ }
1352
+ function enrichTradeHistoryAssets(assets, perpMetaAssets) {
1353
+ return assets.map((asset) => {
1354
+ var _a;
1355
+ if (asset.marketPrefix && asset.collateralToken) {
1356
+ return asset;
1357
+ }
1358
+ const meta = findAssetMeta$4(asset.coin, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
1359
+ return {
1360
+ ...asset,
1361
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
1362
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
1363
+ };
1364
+ });
1365
+ }
1366
+ function enrichTradeHistories(histories, perpMetaAssets) {
1367
+ return histories.map((history) => ({
1368
+ ...history,
1369
+ closedLongAssets: enrichTradeHistoryAssets(history.closedLongAssets, perpMetaAssets),
1370
+ closedShortAssets: enrichTradeHistoryAssets(history.closedShortAssets, perpMetaAssets),
1371
+ }));
1372
+ }
951
1373
  const useTradeHistories = () => {
952
1374
  const context = useContext(PearHyperliquidContext);
953
1375
  if (!context) {
954
1376
  throw new Error('useTradeHistories must be used within a PearHyperliquidProvider');
955
1377
  }
956
1378
  const tradeHistories = useUserData((state) => state.tradeHistories);
1379
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
957
1380
  const isLoading = useMemo(() => {
958
1381
  return tradeHistories === null && context.isConnected;
959
1382
  }, [tradeHistories, context.isConnected]);
960
- return { data: tradeHistories, isLoading };
1383
+ const enrichedTradeHistories = useMemo(() => {
1384
+ if (!tradeHistories)
1385
+ return null;
1386
+ return enrichTradeHistories(tradeHistories, allPerpMetaAssets);
1387
+ }, [tradeHistories, allPerpMetaAssets]);
1388
+ return { data: enrichedTradeHistories, isLoading };
961
1389
  };
962
- /**
963
- * Hook to access open orders with loading state
964
- */
965
1390
  const useOpenOrders = () => {
966
1391
  const context = useContext(PearHyperliquidContext);
967
1392
  if (!context) {
@@ -990,21 +1415,51 @@ const useWebData = () => {
990
1415
  const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
991
1416
  const aggregatedClearinghouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
992
1417
  const finalAtOICaps = useHyperliquidData((state) => state.finalAtOICaps);
993
- const hip3Assets = useHyperliquidData((state) => state.hip3DisplayToFull);
1418
+ const hip3Assets = useHyperliquidData((state) => state.hip3Assets);
1419
+ const hip3MarketPrefixes = useHyperliquidData((state) => state.hip3MarketPrefixes);
994
1420
  let marketDataBySymbol = {};
995
1421
  if (finalAssetContexts && perpMetaAssets) {
996
1422
  const result = {};
1423
+ // Build a map of display name -> asset context index (for unique display names)
1424
+ const displayNameToContextIndex = new Map();
1425
+ const seenNames = new Set();
1426
+ let contextIndex = 0;
1427
+ // First pass: map unique display names to their context index
997
1428
  for (let index = 0; index < perpMetaAssets.length; index++) {
998
1429
  const name = perpMetaAssets[index].name;
999
- result[name] = {
1000
- asset: finalAssetContexts[index],
1001
- universe: perpMetaAssets[index],
1002
- };
1430
+ if (!seenNames.has(name)) {
1431
+ seenNames.add(name);
1432
+ if (contextIndex < finalAssetContexts.length) {
1433
+ displayNameToContextIndex.set(name, contextIndex);
1434
+ contextIndex++;
1435
+ }
1436
+ }
1437
+ }
1438
+ // Second pass: create nested entries for all market variants
1439
+ for (let index = 0; index < perpMetaAssets.length; index++) {
1440
+ const universeAsset = perpMetaAssets[index];
1441
+ const displayName = universeAsset.name;
1442
+ const marketPrefix = universeAsset.marketPrefix;
1443
+ const ctxIndex = displayNameToContextIndex.get(displayName);
1444
+ if (ctxIndex !== undefined) {
1445
+ const assetContext = finalAssetContexts[ctxIndex];
1446
+ // Initialize the symbol entry if it doesn't exist
1447
+ if (!result[displayName]) {
1448
+ result[displayName] = {};
1449
+ }
1450
+ // Use marketPrefix as key for HIP-3 assets, "default" for regular assets
1451
+ const variantKey = marketPrefix || 'default';
1452
+ result[displayName][variantKey] = {
1453
+ asset: assetContext,
1454
+ universe: universeAsset,
1455
+ };
1456
+ }
1003
1457
  }
1004
1458
  marketDataBySymbol = result;
1005
1459
  }
1006
1460
  return {
1007
1461
  hip3Assets,
1462
+ hip3MarketPrefixes,
1008
1463
  clearinghouseState: aggregatedClearinghouseState,
1009
1464
  perpsAtOpenInterestCap: finalAtOICaps,
1010
1465
  marketDataBySymbol,
@@ -1019,19 +1474,30 @@ const useWebData = () => {
1019
1474
  class TokenMetadataExtractor {
1020
1475
  /**
1021
1476
  * Extracts comprehensive token metadata
1022
- * @param symbol - Token symbol
1477
+ * @param symbol - Token symbol (base symbol without prefix, e.g., "TSLA")
1023
1478
  * @param perpMetaAssets - Aggregated universe assets (flattened across dexes)
1024
1479
  * @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
1025
1480
  * @param allMids - AllMids data containing current prices
1026
1481
  * @param activeAssetData - Optional active asset data containing leverage information
1482
+ * @param marketPrefix - Optional market prefix (e.g., "xyz", "flx") for HIP3 multi-market assets
1027
1483
  * @returns TokenMetadata or null if token not found
1028
1484
  */
1029
- static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1485
+ static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketPrefix) {
1030
1486
  if (!perpMetaAssets || !finalAssetContexts || !allMids) {
1031
1487
  return null;
1032
1488
  }
1033
1489
  // Find token index in aggregated universe
1034
- const universeIndex = perpMetaAssets.findIndex(asset => asset.name === symbol);
1490
+ // For HIP3 assets, match both name AND marketPrefix
1491
+ const universeIndex = perpMetaAssets.findIndex((asset) => {
1492
+ if (asset.name !== symbol)
1493
+ return false;
1494
+ // If marketPrefix is specified, match it; otherwise match assets without prefix
1495
+ if (marketPrefix) {
1496
+ return asset.marketPrefix === marketPrefix;
1497
+ }
1498
+ // No prefix specified - match non-HIP3 asset (no marketPrefix) or first matching asset
1499
+ return !asset.marketPrefix;
1500
+ });
1035
1501
  if (universeIndex === -1) {
1036
1502
  return null;
1037
1503
  }
@@ -1040,9 +1506,20 @@ class TokenMetadataExtractor {
1040
1506
  if (!assetCtx) {
1041
1507
  return null;
1042
1508
  }
1043
- // Get current price from allMids
1044
- const currentPriceStr = allMids.mids[symbol];
1045
- const currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
1509
+ // Get current price - prefer assetCtx.midPx as it's already index-matched,
1510
+ // fall back to allMids lookup if midPx is null
1511
+ const prefixedKeyColon = marketPrefix ? `${marketPrefix}:${symbol}` : null;
1512
+ let currentPrice = 0;
1513
+ // Primary source: assetCtx.midPx (already properly indexed)
1514
+ if (assetCtx.midPx) {
1515
+ currentPrice = parseFloat(assetCtx.midPx);
1516
+ }
1517
+ // Fallback: allMids lookup with multiple key formats for HIP3 markets
1518
+ if (!currentPrice || isNaN(currentPrice)) {
1519
+ const currentPriceStr = (prefixedKeyColon && allMids.mids[prefixedKeyColon]) ||
1520
+ allMids.mids[symbol];
1521
+ currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
1522
+ }
1046
1523
  // Get previous day price
1047
1524
  const prevDayPrice = parseFloat(assetCtx.prevDayPx);
1048
1525
  // Calculate 24h price change
@@ -1053,7 +1530,11 @@ class TokenMetadataExtractor {
1053
1530
  const markPrice = parseFloat(assetCtx.markPx);
1054
1531
  const oraclePrice = parseFloat(assetCtx.oraclePx);
1055
1532
  // Extract leverage info from activeAssetData if available
1056
- const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[symbol];
1533
+ // Try prefixed key first (e.g., "xyz:TSLA"), then fall back to plain symbol
1534
+ const activeDataKey = prefixedKeyColon && (activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[prefixedKeyColon])
1535
+ ? prefixedKeyColon
1536
+ : symbol;
1537
+ const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[activeDataKey];
1057
1538
  const leverage = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.leverage;
1058
1539
  const maxTradeSzs = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.maxTradeSzs;
1059
1540
  const availableToTrade = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.availableToTrade;
@@ -1071,21 +1552,27 @@ class TokenMetadataExtractor {
1071
1552
  leverage,
1072
1553
  maxTradeSzs,
1073
1554
  availableToTrade,
1555
+ collateralToken: universeAsset.collateralToken,
1074
1556
  };
1075
1557
  }
1076
1558
  /**
1077
1559
  * Extracts metadata for multiple tokens
1078
- * @param symbols - Array of token symbols
1560
+ * @param tokens - Array of token objects with symbol and optional marketPrefix
1079
1561
  * @param perpMetaAssets - Aggregated universe assets
1080
1562
  * @param finalAssetContexts - Aggregated asset contexts
1081
1563
  * @param allMids - AllMids data
1082
1564
  * @param activeAssetData - Optional active asset data containing leverage information
1083
- * @returns Record of symbol to TokenMetadata
1565
+ * @returns Record of unique key to TokenMetadata. Key is "{prefix}:{symbol}" for HIP3 assets, or just "{symbol}" otherwise
1084
1566
  */
1085
- static extractMultipleTokensMetadata(symbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1567
+ static extractMultipleTokensMetadata(tokens, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1086
1568
  const result = {};
1087
- for (const symbol of symbols) {
1088
- result[symbol] = this.extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData);
1569
+ for (const token of tokens) {
1570
+ // Use a unique key that includes the prefix for HIP3 assets
1571
+ // This ensures xyz:TSLA and flx:TSLA get separate entries
1572
+ const resultKey = token.marketPrefix
1573
+ ? `${token.marketPrefix}:${token.symbol}`
1574
+ : token.symbol;
1575
+ result[resultKey] = this.extractTokenMetadata(token.symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, token.marketPrefix);
1089
1576
  }
1090
1577
  return result;
1091
1578
  }
@@ -1098,10 +1585,30 @@ class TokenMetadataExtractor {
1098
1585
  static isTokenAvailable(symbol, perpMetaAssets) {
1099
1586
  if (!perpMetaAssets)
1100
1587
  return false;
1101
- return perpMetaAssets.some(asset => asset.name === symbol);
1588
+ return perpMetaAssets.some((asset) => asset.name === symbol);
1102
1589
  }
1103
1590
  }
1104
1591
 
1592
+ /**
1593
+ * Parse a token string that may have a market prefix (e.g., "xyz:GOOGL" -> { prefix: "xyz", symbol: "GOOGL" })
1594
+ * This allows us to keep the full name (xyz:GOOGL) for URLs/tags while extracting just the symbol for SDK lookups.
1595
+ */
1596
+ function parseTokenWithPrefix(token) {
1597
+ if (token.includes(':')) {
1598
+ const [prefix, ...rest] = token.split(':');
1599
+ const symbol = rest.join(':').toUpperCase();
1600
+ return {
1601
+ prefix: prefix.toLowerCase(),
1602
+ symbol,
1603
+ fullName: `${prefix.toLowerCase()}:${symbol}`,
1604
+ };
1605
+ }
1606
+ return {
1607
+ prefix: null,
1608
+ symbol: token.toUpperCase(),
1609
+ fullName: token.toUpperCase(),
1610
+ };
1611
+ }
1105
1612
  const useTokenSelectionMetadataStore = create((set) => ({
1106
1613
  isPriceDataReady: false,
1107
1614
  isLoading: true,
@@ -1111,23 +1618,65 @@ const useTokenSelectionMetadataStore = create((set) => ({
1111
1618
  weightedRatio24h: 1,
1112
1619
  priceRatio: 1,
1113
1620
  priceRatio24h: 1,
1114
- openInterest: "0",
1115
- volume: "0",
1621
+ openInterest: '0',
1622
+ volume: '0',
1116
1623
  sumNetFunding: 0,
1117
1624
  maxLeverage: 0,
1118
1625
  minMargin: 0,
1119
1626
  leverageMatched: true,
1120
- recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens }) => {
1121
- const isPriceDataReady = !!(perpMetaAssets && finalAssetContexts && allMids);
1122
- // Compute metadata when ready
1123
- const longSymbols = longTokens.map((t) => t.symbol);
1124
- const shortSymbols = shortTokens.map((t) => t.symbol);
1125
- const longTokensMetadata = isPriceDataReady
1126
- ? TokenMetadataExtractor.extractMultipleTokensMetadata(longSymbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1627
+ recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens, }) => {
1628
+ const isPriceDataReady = !!(perpMetaAssets &&
1629
+ finalAssetContexts &&
1630
+ allMids);
1631
+ // Parse tokens - handle prefixed tokens like "xyz:GOOGL" by extracting the symbol and market prefix
1632
+ // The full name (xyz:GOOGL) is kept as the metadata key for UI consistency
1633
+ const parsedLongTokens = longTokens.map((t) => ({
1634
+ ...t,
1635
+ parsed: parseTokenWithPrefix(t.symbol),
1636
+ }));
1637
+ const parsedShortTokens = shortTokens.map((t) => ({
1638
+ ...t,
1639
+ parsed: parseTokenWithPrefix(t.symbol),
1640
+ }));
1641
+ // Extract base symbols with their market prefixes for SDK lookups
1642
+ // This ensures xyz:TSLA and flx:TSLA get different market data
1643
+ const longTokensForLookup = parsedLongTokens.map((t) => ({
1644
+ symbol: t.parsed.symbol,
1645
+ marketPrefix: t.parsed.prefix,
1646
+ }));
1647
+ const shortTokensForLookup = parsedShortTokens.map((t) => ({
1648
+ symbol: t.parsed.symbol,
1649
+ marketPrefix: t.parsed.prefix,
1650
+ }));
1651
+ // Also extract just the base symbols (without prefix) for lookups that don't support prefixes
1652
+ const longBaseSymbols = longTokensForLookup.map((t) => t.symbol);
1653
+ const shortBaseSymbols = shortTokensForLookup.map((t) => t.symbol);
1654
+ // Get metadata using base symbols with market prefix for proper market differentiation
1655
+ const longBaseMetadata = isPriceDataReady
1656
+ ? TokenMetadataExtractor.extractMultipleTokensMetadata(longTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1127
1657
  : {};
1128
- const shortTokensMetadata = isPriceDataReady
1129
- ? TokenMetadataExtractor.extractMultipleTokensMetadata(shortSymbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1658
+ const shortBaseMetadata = isPriceDataReady
1659
+ ? TokenMetadataExtractor.extractMultipleTokensMetadata(shortTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1130
1660
  : {};
1661
+ // Re-map metadata using original full names (with prefix) as keys for UI consistency
1662
+ // The extractor now keys by "{prefix}:{symbol}" for prefixed tokens, which matches our parsed.fullName
1663
+ const longTokensMetadata = {};
1664
+ parsedLongTokens.forEach((t) => {
1665
+ var _a;
1666
+ // Use the full name (e.g., "xyz:TSLA") as the lookup key since extractor uses the same format
1667
+ const lookupKey = t.parsed.prefix
1668
+ ? `${t.parsed.prefix}:${t.parsed.symbol}`
1669
+ : t.parsed.symbol;
1670
+ longTokensMetadata[t.symbol] = (_a = longBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
1671
+ });
1672
+ const shortTokensMetadata = {};
1673
+ parsedShortTokens.forEach((t) => {
1674
+ var _a;
1675
+ const lookupKey = t.parsed.prefix
1676
+ ? `${t.parsed.prefix}:${t.parsed.symbol}`
1677
+ : t.parsed.symbol;
1678
+ shortTokensMetadata[t.symbol] = (_a = shortBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
1679
+ });
1131
1680
  // Determine loading state
1132
1681
  const allTokens = [...longTokens, ...shortTokens];
1133
1682
  const isLoading = (() => {
@@ -1135,26 +1684,33 @@ const useTokenSelectionMetadataStore = create((set) => ({
1135
1684
  return true;
1136
1685
  if (allTokens.length === 0)
1137
1686
  return false;
1138
- const allMetadata = { ...longTokensMetadata, ...shortTokensMetadata };
1687
+ const allMetadata = {
1688
+ ...longTokensMetadata,
1689
+ ...shortTokensMetadata,
1690
+ };
1139
1691
  return allTokens.some((token) => !allMetadata[token.symbol]);
1140
1692
  })();
1141
1693
  // Open interest and volume (from market data for matching asset basket)
1694
+ // Use base symbols (without prefix) for matching against market data
1142
1695
  const { openInterest, volume } = (() => {
1143
- const empty = { openInterest: "0", volume: "0" };
1696
+ const empty = { openInterest: '0', volume: '0' };
1144
1697
  if (!(marketData === null || marketData === void 0 ? void 0 : marketData.active) || (!longTokens.length && !shortTokens.length))
1145
1698
  return empty;
1146
- const selectedLong = longTokens.map((t) => t.symbol).sort();
1147
- const selectedShort = shortTokens.map((t) => t.symbol).sort();
1699
+ const selectedLong = longBaseSymbols.slice().sort();
1700
+ const selectedShort = shortBaseSymbols.slice().sort();
1148
1701
  const match = marketData.active.find((item) => {
1149
1702
  const longs = [...item.longAssets].sort();
1150
1703
  const shorts = [...item.shortAssets].sort();
1151
- if (longs.length !== selectedLong.length || shorts.length !== selectedShort.length)
1704
+ if (longs.length !== selectedLong.length ||
1705
+ shorts.length !== selectedShort.length)
1152
1706
  return false;
1153
1707
  const longsEqual = longs.every((s, i) => s.asset === selectedLong[i]);
1154
1708
  const shortsEqual = shorts.every((s, i) => s.asset === selectedShort[i]);
1155
1709
  return longsEqual && shortsEqual;
1156
1710
  });
1157
- return match ? { openInterest: match.openInterest, volume: match.volume } : empty;
1711
+ return match
1712
+ ? { openInterest: match.openInterest, volume: match.volume }
1713
+ : empty;
1158
1714
  })();
1159
1715
  // Price ratio (only when exactly one long and one short)
1160
1716
  const { priceRatio, priceRatio24h } = (() => {
@@ -1234,17 +1790,27 @@ const useTokenSelectionMetadataStore = create((set) => ({
1234
1790
  return totalFunding;
1235
1791
  })();
1236
1792
  // Max leverage (minimum across all tokens)
1793
+ // Use tokens with their market prefixes for proper lookup in perpMetaAssets
1237
1794
  const maxLeverage = (() => {
1238
1795
  if (!perpMetaAssets)
1239
1796
  return 0;
1240
- const allTokenSymbols = [...longTokens, ...shortTokens].map((t) => t.symbol);
1241
- if (allTokenSymbols.length === 0)
1797
+ const allTokensForLookup = [
1798
+ ...longTokensForLookup,
1799
+ ...shortTokensForLookup,
1800
+ ];
1801
+ if (allTokensForLookup.length === 0)
1242
1802
  return 0;
1243
1803
  let minLev = Infinity;
1244
- allTokenSymbols.forEach((symbol) => {
1245
- const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol);
1246
- if (tokenUniverse === null || tokenUniverse === void 0 ? void 0 : tokenUniverse.maxLeverage)
1247
- minLev = Math.min(minLev, tokenUniverse.maxLeverage);
1804
+ allTokensForLookup.forEach(({ symbol, marketPrefix }) => {
1805
+ // Match by both name AND marketPrefix for HIP3 assets
1806
+ const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol &&
1807
+ (marketPrefix
1808
+ ? u.marketPrefix === marketPrefix
1809
+ : !u.marketPrefix));
1810
+ // Fallback to just matching by name if no exact match
1811
+ const fallbackUniverse = tokenUniverse || perpMetaAssets.find((u) => u.name === symbol);
1812
+ if (fallbackUniverse === null || fallbackUniverse === void 0 ? void 0 : fallbackUniverse.maxLeverage)
1813
+ minLev = Math.min(minLev, fallbackUniverse.maxLeverage);
1248
1814
  });
1249
1815
  return minLev === Infinity ? 0 : minLev;
1250
1816
  })();
@@ -1256,7 +1822,10 @@ const useTokenSelectionMetadataStore = create((set) => ({
1256
1822
  // Whether all tokens have matching leverage
1257
1823
  const leverageMatched = (() => {
1258
1824
  const allTokensArr = [...longTokens, ...shortTokens];
1259
- const allMetadata = { ...longTokensMetadata, ...shortTokensMetadata };
1825
+ const allMetadata = {
1826
+ ...longTokensMetadata,
1827
+ ...shortTokensMetadata,
1828
+ };
1260
1829
  if (allTokensArr.length === 0)
1261
1830
  return true;
1262
1831
  const tokensWithLev = allTokensArr.filter((token) => { var _a; return (_a = allMetadata[token.symbol]) === null || _a === void 0 ? void 0 : _a.leverage; });
@@ -5441,8 +6010,8 @@ function addAuthInterceptors(params) {
5441
6010
  /**
5442
6011
  * Fetch historical candle data from HyperLiquid API
5443
6012
  */
5444
- const fetchHistoricalCandles = async (coin, startTime, endTime, interval, displayToFull) => {
5445
- const backendCoin = toBackendSymbol(coin, displayToFull);
6013
+ const fetchHistoricalCandles = async (coin, startTime, endTime, interval, hip3Assets) => {
6014
+ const backendCoin = toBackendSymbol(coin, hip3Assets);
5446
6015
  const request = {
5447
6016
  req: { coin: backendCoin, startTime, endTime, interval },
5448
6017
  type: 'candleSnapshot',
@@ -5601,10 +6170,10 @@ const useHistoricalPriceData = () => {
5601
6170
  setTokenLoading(token.symbol, true);
5602
6171
  });
5603
6172
  try {
5604
- const displayToFull = useHyperliquidData.getState().hip3DisplayToFull;
6173
+ const hip3Assets = useHyperliquidData.getState().hip3Assets;
5605
6174
  const fetchPromises = tokensToFetch.map(async (token) => {
5606
6175
  try {
5607
- const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, displayToFull);
6176
+ const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, hip3Assets);
5608
6177
  addHistoricalPriceData(token.symbol, interval, response.data, { start: startTime, end: endTime });
5609
6178
  return { symbol: token.symbol, candles: response.data, success: true };
5610
6179
  }
@@ -6288,97 +6857,19 @@ function useAutoSyncFills(options) {
6288
6857
  }
6289
6858
 
6290
6859
  /**
6291
- * Minimum USD value required per asset when creating a position
6292
- */
6293
- const MINIMUM_ASSET_USD_VALUE = 11;
6294
- /**
6295
- * Validation error for minimum position size
6296
- */
6297
- class MinimumPositionSizeError extends Error {
6298
- constructor(assetName, assetValue, minimumRequired) {
6299
- super(`Asset "${assetName}" has a USD value of $${assetValue.toFixed(2)}, which is below the minimum required value of $${minimumRequired.toFixed(2)}`);
6300
- this.assetName = assetName;
6301
- this.assetValue = assetValue;
6302
- this.minimumRequired = minimumRequired;
6303
- this.name = "MinimumPositionSizeError";
6304
- }
6305
- }
6306
- /**
6307
- * Validates that each asset in a position has at least the minimum USD value
6308
- * @param usdValue Total USD value for the position
6309
- * @param longAssets Array of long assets with weights
6310
- * @param shortAssets Array of short assets with weights
6311
- * @throws MinimumPositionSizeError if any asset has less than the minimum USD value
6860
+ * Create a position (MARKET/LIMIT/TWAP) using Pear Hyperliquid service
6861
+ * Authorization is derived from headers (Axios defaults or browser localStorage fallback)
6862
+ * @throws MinimumPositionSizeError if any asset has less than $11 USD value
6863
+ * @throws MaxAssetsPerLegError if any leg exceeds the maximum allowed assets (15)
6312
6864
  */
6313
- function validateMinimumAssetSize(usdValue, longAssets, shortAssets) {
6314
- var _a;
6315
- const allAssets = [...(longAssets || []), ...(shortAssets || [])];
6316
- if (allAssets.length === 0) {
6317
- return; // No assets to validate
6318
- }
6319
- // Calculate total weight
6320
- const totalWeight = allAssets.reduce((sum, asset) => { var _a; return sum + ((_a = asset.weight) !== null && _a !== void 0 ? _a : 0); }, 0);
6321
- // If weights are not provided or sum to 0, assume equal distribution
6322
- const hasWeights = totalWeight > 0;
6323
- const equalWeight = hasWeights ? 0 : 1 / allAssets.length;
6324
- // Validate each asset
6325
- for (const asset of allAssets) {
6326
- const weight = hasWeights ? (_a = asset.weight) !== null && _a !== void 0 ? _a : 0 : equalWeight;
6327
- const assetUsdValue = usdValue * weight;
6328
- if (assetUsdValue < MINIMUM_ASSET_USD_VALUE) {
6329
- throw new MinimumPositionSizeError(asset.asset, assetUsdValue, MINIMUM_ASSET_USD_VALUE);
6330
- }
6331
- }
6332
- }
6333
- /**
6334
- * Calculates the minimum USD value required for a position based on the number of assets
6335
- * @param longAssets Array of long assets
6336
- * @param shortAssets Array of short assets
6337
- * @returns The minimum total USD value required
6338
- */
6339
- function calculateMinimumPositionValue(longAssets, shortAssets) {
6340
- const totalAssets = ((longAssets === null || longAssets === void 0 ? void 0 : longAssets.length) || 0) + ((shortAssets === null || shortAssets === void 0 ? void 0 : shortAssets.length) || 0);
6341
- if (totalAssets === 0) {
6342
- return 0;
6343
- }
6344
- return MINIMUM_ASSET_USD_VALUE * totalAssets;
6345
- }
6346
- /**
6347
- * Validates and provides a user-friendly error message with suggestions
6348
- * @param usdValue Total USD value for the position
6349
- * @param longAssets Array of long assets with weights
6350
- * @param shortAssets Array of short assets with weights
6351
- * @returns Validation result with success flag and optional error message
6352
- */
6353
- function validatePositionSize(usdValue, longAssets, shortAssets) {
6354
- try {
6355
- validateMinimumAssetSize(usdValue, longAssets, shortAssets);
6356
- return { valid: true };
6357
- }
6358
- catch (error) {
6359
- if (error instanceof MinimumPositionSizeError) {
6360
- const minimumRequired = calculateMinimumPositionValue(longAssets, shortAssets);
6361
- return {
6362
- valid: false,
6363
- error: error.message,
6364
- minimumRequired,
6365
- };
6366
- }
6367
- throw error;
6368
- }
6369
- }
6370
-
6371
- /**
6372
- * Create a position (MARKET/LIMIT/TWAP) using Pear Hyperliquid service
6373
- * Authorization is derived from headers (Axios defaults or browser localStorage fallback)
6374
- * @throws MinimumPositionSizeError if any asset has less than $11 USD value
6375
- */
6376
- async function createPosition(baseUrl, payload, displayToFull) {
6865
+ async function createPosition(baseUrl, payload, hip3Assets) {
6866
+ // Validate maximum assets per leg before creating position
6867
+ validateMaxAssetsPerLeg(payload.longAssets, payload.shortAssets);
6377
6868
  // Validate minimum asset size before creating position
6378
6869
  validateMinimumAssetSize(payload.usdValue, payload.longAssets, payload.shortAssets);
6379
6870
  const url = joinUrl(baseUrl, "/positions");
6380
6871
  // Translate display symbols to backend format
6381
- const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, displayToFull) }));
6872
+ const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6382
6873
  const translatedPayload = {
6383
6874
  ...payload,
6384
6875
  longAssets: mapAssets(payload.longAssets),
@@ -6477,9 +6968,9 @@ async function adjustPosition(baseUrl, positionId, payload) {
6477
6968
  throw toApiError(error);
6478
6969
  }
6479
6970
  }
6480
- async function adjustAdvancePosition(baseUrl, positionId, payload, displayToFull) {
6971
+ async function adjustAdvancePosition(baseUrl, positionId, payload, hip3Assets) {
6481
6972
  const url = joinUrl(baseUrl, `/positions/${positionId}/adjust-advance`);
6482
- const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, displayToFull) }));
6973
+ const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6483
6974
  const translatedPayload = (payload || []).map((item) => ({
6484
6975
  longAssets: mapAssets(item.longAssets),
6485
6976
  shortAssets: mapAssets(item.shortAssets),
@@ -6542,6 +7033,9 @@ const calculatePositionAsset = (asset, currentPrice, totalInitialPositionSize, l
6542
7033
  entryPositionValue: entryNotional,
6543
7034
  initialWeight: totalInitialPositionSize > 0 ? entryNotional / totalInitialPositionSize : 0,
6544
7035
  fundingPaid: (_a = asset.fundingPaid) !== null && _a !== void 0 ? _a : 0,
7036
+ // Preserve market metadata from raw asset (if provided by backend)
7037
+ marketPrefix: asset.marketPrefix,
7038
+ collateralToken: asset.collateralToken,
6545
7039
  };
6546
7040
  };
6547
7041
  const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
@@ -6604,57 +7098,151 @@ const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
6604
7098
  });
6605
7099
  };
6606
7100
 
7101
+ function findAssetMeta$3(coinName, perpMetaAssets, knownPrefix, desiredCollateral) {
7102
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
7103
+ if (!perpMetaAssets) {
7104
+ return { collateralToken: 'USDC', marketPrefix: null };
7105
+ }
7106
+ if (desiredCollateral) {
7107
+ const collateralMatch = perpMetaAssets.find((a) => a.name === coinName && a.collateralToken === desiredCollateral);
7108
+ if (collateralMatch) {
7109
+ return {
7110
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7111
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7112
+ };
7113
+ }
7114
+ }
7115
+ if (coinName.includes(':')) {
7116
+ const [prefix, symbol] = coinName.split(':');
7117
+ const exactMatch = perpMetaAssets.find((a) => {
7118
+ var _a;
7119
+ return a.name === symbol &&
7120
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
7121
+ });
7122
+ if (exactMatch) {
7123
+ return {
7124
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7125
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7126
+ };
7127
+ }
7128
+ }
7129
+ if (knownPrefix) {
7130
+ const exactMatch = perpMetaAssets.find((a) => {
7131
+ var _a;
7132
+ return a.name === coinName &&
7133
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
7134
+ });
7135
+ if (exactMatch) {
7136
+ return {
7137
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
7138
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
7139
+ };
7140
+ }
7141
+ }
7142
+ const regularAsset = perpMetaAssets.find((a) => a.name === coinName && !a.marketPrefix);
7143
+ if (regularAsset) {
7144
+ return {
7145
+ collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
7146
+ marketPrefix: null,
7147
+ };
7148
+ }
7149
+ const hip3Asset = perpMetaAssets.find((a) => a.name === coinName && a.marketPrefix);
7150
+ if (hip3Asset) {
7151
+ return {
7152
+ collateralToken: (_h = hip3Asset.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
7153
+ marketPrefix: (_j = hip3Asset.marketPrefix) !== null && _j !== void 0 ? _j : null,
7154
+ };
7155
+ }
7156
+ return { collateralToken: 'USDC', marketPrefix: null };
7157
+ }
7158
+ function enrichPositionAssets(assets, perpMetaAssets) {
7159
+ return assets.map((asset) => {
7160
+ var _a;
7161
+ if (asset.marketPrefix && asset.collateralToken) {
7162
+ return asset;
7163
+ }
7164
+ const meta = findAssetMeta$3(asset.coin, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
7165
+ return {
7166
+ ...asset,
7167
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
7168
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
7169
+ };
7170
+ });
7171
+ }
7172
+ function enrichPositions(positions, perpMetaAssets) {
7173
+ return positions.map((position) => ({
7174
+ ...position,
7175
+ longAssets: enrichPositionAssets(position.longAssets, perpMetaAssets),
7176
+ shortAssets: enrichPositionAssets(position.shortAssets, perpMetaAssets),
7177
+ }));
7178
+ }
6607
7179
  function usePosition() {
6608
7180
  const context = useContext(PearHyperliquidContext);
6609
7181
  if (!context) {
6610
7182
  throw new Error('usePosition must be used within a PearHyperliquidProvider');
6611
7183
  }
6612
7184
  const { apiBaseUrl, isConnected } = context;
6613
- const displayToFull = useHyperliquidData((s) => s.hip3DisplayToFull);
6614
- // Create position API action
7185
+ const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
6615
7186
  const createPosition$1 = async (payload) => {
6616
- return createPosition(apiBaseUrl, payload, displayToFull);
7187
+ return createPosition(apiBaseUrl, payload, hip3Assets);
6617
7188
  };
6618
- // Update TP/SL risk parameters for a position
6619
7189
  const updateRiskParameters$1 = async (positionId, payload) => {
6620
7190
  return updateRiskParameters(apiBaseUrl, positionId, payload);
6621
7191
  };
6622
- // Close a position (MARKET or TWAP)
6623
7192
  const closePosition$1 = async (positionId, payload) => {
6624
7193
  return closePosition(apiBaseUrl, positionId, payload);
6625
7194
  };
6626
- // Close all positions (MARKET or TWAP)
6627
7195
  const closeAllPositions$1 = async (payload) => {
6628
7196
  return closeAllPositions(apiBaseUrl, payload);
6629
7197
  };
6630
- // Adjust a position (REDUCE/INCREASE by %; MARKET or LIMIT)
6631
7198
  const adjustPosition$1 = async (positionId, payload) => {
6632
7199
  return adjustPosition(apiBaseUrl, positionId, payload);
6633
7200
  };
6634
- // Adjust to absolute target sizes per asset, optionally adding new assets
6635
7201
  const adjustAdvancePosition$1 = async (positionId, payload) => {
6636
- return adjustAdvancePosition(apiBaseUrl, positionId, payload, displayToFull);
7202
+ return adjustAdvancePosition(apiBaseUrl, positionId, payload, hip3Assets);
6637
7203
  };
6638
- // Open positions using WS data, with derived values
6639
7204
  const userOpenPositions = useUserData((state) => state.rawOpenPositions);
6640
7205
  const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
6641
7206
  const allMids = useHyperliquidData((state) => state.allMids);
7207
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
6642
7208
  const isLoading = useMemo(() => {
6643
7209
  return userOpenPositions === null && isConnected;
6644
7210
  }, [userOpenPositions, isConnected]);
6645
7211
  const openPositions = useMemo(() => {
6646
7212
  if (!userOpenPositions || !aggregatedClearingHouseState || !allMids)
6647
7213
  return null;
6648
- return buildPositionValue(userOpenPositions, aggregatedClearingHouseState, allMids);
6649
- }, [userOpenPositions, aggregatedClearingHouseState, allMids]);
6650
- return { createPosition: createPosition$1, updateRiskParameters: updateRiskParameters$1, closePosition: closePosition$1, closeAllPositions: closeAllPositions$1, adjustPosition: adjustPosition$1, adjustAdvancePosition: adjustAdvancePosition$1, openPositions, isLoading };
7214
+ const positions = buildPositionValue(userOpenPositions, aggregatedClearingHouseState, allMids);
7215
+ return enrichPositions(positions, allPerpMetaAssets);
7216
+ }, [
7217
+ userOpenPositions,
7218
+ aggregatedClearingHouseState,
7219
+ allMids,
7220
+ allPerpMetaAssets,
7221
+ ]);
7222
+ return {
7223
+ createPosition: createPosition$1,
7224
+ updateRiskParameters: updateRiskParameters$1,
7225
+ closePosition: closePosition$1,
7226
+ closeAllPositions: closeAllPositions$1,
7227
+ adjustPosition: adjustPosition$1,
7228
+ adjustAdvancePosition: adjustAdvancePosition$1,
7229
+ openPositions,
7230
+ isLoading,
7231
+ };
6651
7232
  }
6652
7233
 
6653
7234
  async function adjustOrder(baseUrl, orderId, payload) {
6654
7235
  const url = joinUrl(baseUrl, `/orders/${orderId}/adjust`);
6655
7236
  try {
6656
- const resp = await apiClient.put(url, payload, { headers: { 'Content-Type': 'application/json' }, timeout: 60000 });
6657
- return { data: resp.data, status: resp.status, headers: resp.headers };
7237
+ const resp = await apiClient.put(url, payload, {
7238
+ headers: { 'Content-Type': 'application/json' },
7239
+ timeout: 60000,
7240
+ });
7241
+ return {
7242
+ data: resp.data,
7243
+ status: resp.status,
7244
+ headers: resp.headers,
7245
+ };
6658
7246
  }
6659
7247
  catch (error) {
6660
7248
  throw toApiError(error);
@@ -6663,8 +7251,14 @@ async function adjustOrder(baseUrl, orderId, payload) {
6663
7251
  async function cancelOrder(baseUrl, orderId) {
6664
7252
  const url = joinUrl(baseUrl, `/orders/${orderId}/cancel`);
6665
7253
  try {
6666
- const resp = await apiClient.delete(url, { timeout: 60000 });
6667
- return { data: resp.data, status: resp.status, headers: resp.headers };
7254
+ const resp = await apiClient.delete(url, {
7255
+ timeout: 60000,
7256
+ });
7257
+ return {
7258
+ data: resp.data,
7259
+ status: resp.status,
7260
+ headers: resp.headers,
7261
+ };
6668
7262
  }
6669
7263
  catch (error) {
6670
7264
  throw toApiError(error);
@@ -6674,19 +7268,129 @@ async function cancelTwapOrder(baseUrl, orderId) {
6674
7268
  const url = joinUrl(baseUrl, `/orders/${orderId}/twap/cancel`);
6675
7269
  try {
6676
7270
  const resp = await apiClient.post(url, {}, { headers: { 'Content-Type': 'application/json' }, timeout: 60000 });
6677
- return { data: resp.data, status: resp.status, headers: resp.headers };
7271
+ return {
7272
+ data: resp.data,
7273
+ status: resp.status,
7274
+ headers: resp.headers,
7275
+ };
7276
+ }
7277
+ catch (error) {
7278
+ throw toApiError(error);
7279
+ }
7280
+ }
7281
+ /**
7282
+ * Execute a spot order (swap) using Pear Hyperliquid service
7283
+ * POST /orders/spot
7284
+ */
7285
+ async function executeSpotOrder(baseUrl, payload) {
7286
+ const url = joinUrl(baseUrl, '/orders/spot');
7287
+ try {
7288
+ const resp = await apiClient.post(url, payload, {
7289
+ headers: { 'Content-Type': 'application/json' },
7290
+ timeout: 60000,
7291
+ });
7292
+ return {
7293
+ data: resp.data,
7294
+ status: resp.status,
7295
+ headers: resp.headers,
7296
+ };
6678
7297
  }
6679
7298
  catch (error) {
6680
7299
  throw toApiError(error);
6681
7300
  }
6682
7301
  }
6683
7302
 
7303
+ function findAssetMeta$2(assetName, perpMetaAssets, knownPrefix, desiredCollateral) {
7304
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
7305
+ if (!perpMetaAssets) {
7306
+ return { collateralToken: 'USDC', marketPrefix: null };
7307
+ }
7308
+ if (desiredCollateral) {
7309
+ const collateralMatch = perpMetaAssets.find((a) => a.name === assetName && a.collateralToken === desiredCollateral);
7310
+ if (collateralMatch) {
7311
+ return {
7312
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7313
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7314
+ };
7315
+ }
7316
+ }
7317
+ if (assetName.includes(':')) {
7318
+ const [prefix, symbol] = assetName.split(':');
7319
+ const exactMatch = perpMetaAssets.find((a) => {
7320
+ var _a;
7321
+ return a.name === symbol &&
7322
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
7323
+ });
7324
+ if (exactMatch) {
7325
+ return {
7326
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7327
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7328
+ };
7329
+ }
7330
+ }
7331
+ if (knownPrefix) {
7332
+ const exactMatch = perpMetaAssets.find((a) => {
7333
+ var _a;
7334
+ return a.name === assetName &&
7335
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
7336
+ });
7337
+ if (exactMatch) {
7338
+ return {
7339
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
7340
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
7341
+ };
7342
+ }
7343
+ }
7344
+ const regularAsset = perpMetaAssets.find((a) => a.name === assetName && !a.marketPrefix);
7345
+ if (regularAsset) {
7346
+ return {
7347
+ collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
7348
+ marketPrefix: null,
7349
+ };
7350
+ }
7351
+ const hip3Assets = perpMetaAssets.filter((a) => a.name === assetName && a.marketPrefix);
7352
+ if (hip3Assets.length > 0) {
7353
+ if (desiredCollateral) {
7354
+ const collateralMatch = hip3Assets.find((a) => a.collateralToken === desiredCollateral);
7355
+ if (collateralMatch) {
7356
+ return {
7357
+ collateralToken: (_h = collateralMatch.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
7358
+ marketPrefix: (_j = collateralMatch.marketPrefix) !== null && _j !== void 0 ? _j : null,
7359
+ };
7360
+ }
7361
+ }
7362
+ const usdHMatch = hip3Assets.find((a) => a.collateralToken === 'USDH');
7363
+ const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Assets[0];
7364
+ return {
7365
+ collateralToken: (_k = chosen.collateralToken) !== null && _k !== void 0 ? _k : 'USDC',
7366
+ marketPrefix: (_l = chosen.marketPrefix) !== null && _l !== void 0 ? _l : null,
7367
+ };
7368
+ }
7369
+ return { collateralToken: 'USDC', marketPrefix: null };
7370
+ }
7371
+ function enrichOrderAssets$1(assets, perpMetaAssets) {
7372
+ if (!assets)
7373
+ return [];
7374
+ return assets.map((asset) => {
7375
+ var _a;
7376
+ if (asset.marketPrefix && asset.collateralToken) {
7377
+ return asset;
7378
+ }
7379
+ const meta = findAssetMeta$2(asset.asset, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
7380
+ return {
7381
+ ...asset,
7382
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
7383
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
7384
+ };
7385
+ });
7386
+ }
6684
7387
  function useOrders() {
6685
7388
  const context = useContext(PearHyperliquidContext);
6686
7389
  if (!context)
6687
7390
  throw new Error('useOrders must be used within a PearHyperliquidProvider');
6688
7391
  const { apiBaseUrl } = context;
6689
7392
  const openOrders = useUserData((state) => state.openOrders);
7393
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
6690
7394
  const isLoading = useMemo(() => openOrders === null && context.isConnected, [openOrders, context.isConnected]);
6691
7395
  const { openPositions } = usePosition();
6692
7396
  const positionsById = useMemo(() => {
@@ -6705,19 +7409,27 @@ function useOrders() {
6705
7409
  const isTpSl = ord.orderType === 'TP' || ord.orderType === 'SL';
6706
7410
  const hasAssets = ((_b = (_a = ord.longAssets) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0 || ((_d = (_c = ord.shortAssets) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) > 0;
6707
7411
  const pos = positionsById.get((_e = ord.positionId) !== null && _e !== void 0 ? _e : '');
6708
- if (!isTpSl || !pos)
6709
- return ord;
6710
- const mapAssets = (arr) => arr.map((a) => ({ asset: a.coin, weight: a.initialWeight }));
6711
- if (!hasAssets) {
6712
- return {
6713
- ...ord,
7412
+ let enrichedOrd = {
7413
+ ...ord,
7414
+ longAssets: enrichOrderAssets$1(ord.longAssets, allPerpMetaAssets),
7415
+ shortAssets: enrichOrderAssets$1(ord.shortAssets, allPerpMetaAssets),
7416
+ };
7417
+ if (isTpSl && !hasAssets && pos) {
7418
+ const mapAssets = (arr) => arr.map((a) => ({
7419
+ asset: a.coin,
7420
+ weight: a.initialWeight,
7421
+ marketPrefix: a.marketPrefix,
7422
+ collateralToken: a.collateralToken,
7423
+ }));
7424
+ enrichedOrd = {
7425
+ ...enrichedOrd,
6714
7426
  longAssets: mapAssets(pos.longAssets),
6715
7427
  shortAssets: mapAssets(pos.shortAssets),
6716
7428
  };
6717
7429
  }
6718
- return ord;
7430
+ return enrichedOrd;
6719
7431
  });
6720
- }, [openOrders, positionsById]);
7432
+ }, [openOrders, positionsById, allPerpMetaAssets]);
6721
7433
  const adjustOrder$1 = async (orderId, payload) => {
6722
7434
  return adjustOrder(apiBaseUrl, orderId, payload);
6723
7435
  };
@@ -6727,16 +7439,156 @@ function useOrders() {
6727
7439
  const cancelTwapOrder$1 = async (orderId) => {
6728
7440
  return cancelTwapOrder(apiBaseUrl, orderId);
6729
7441
  };
6730
- return { adjustOrder: adjustOrder$1, cancelOrder: cancelOrder$1, cancelTwapOrder: cancelTwapOrder$1, openOrders: enrichedOpenOrders, isLoading };
7442
+ return {
7443
+ adjustOrder: adjustOrder$1,
7444
+ cancelOrder: cancelOrder$1,
7445
+ cancelTwapOrder: cancelTwapOrder$1,
7446
+ openOrders: enrichedOpenOrders,
7447
+ isLoading,
7448
+ };
6731
7449
  }
6732
7450
 
7451
+ /**
7452
+ * Hook for executing spot orders (swaps) on Hyperliquid
7453
+ * Use this to swap between USDC and USDH or other spot assets
7454
+ */
7455
+ function useSpotOrder() {
7456
+ const context = useContext(PearHyperliquidContext);
7457
+ if (!context) {
7458
+ throw new Error('useSpotOrder must be used within a PearHyperliquidProvider');
7459
+ }
7460
+ const { apiBaseUrl } = context;
7461
+ const [isLoading, setIsLoading] = useState(false);
7462
+ const [error, setError] = useState(null);
7463
+ const resetError = useCallback(() => {
7464
+ setError(null);
7465
+ }, []);
7466
+ const executeSpotOrder$1 = useCallback(async (payload) => {
7467
+ setIsLoading(true);
7468
+ setError(null);
7469
+ try {
7470
+ const response = await executeSpotOrder(apiBaseUrl, payload);
7471
+ return response;
7472
+ }
7473
+ catch (err) {
7474
+ const apiError = err;
7475
+ setError(apiError);
7476
+ throw apiError;
7477
+ }
7478
+ finally {
7479
+ setIsLoading(false);
7480
+ }
7481
+ }, [apiBaseUrl]);
7482
+ return {
7483
+ executeSpotOrder: executeSpotOrder$1,
7484
+ isLoading,
7485
+ error,
7486
+ resetError,
7487
+ };
7488
+ }
7489
+
7490
+ function findAssetMeta$1(assetName, perpMetaAssets, knownPrefix, desiredCollateral) {
7491
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
7492
+ if (!perpMetaAssets) {
7493
+ return { collateralToken: 'USDC', marketPrefix: null };
7494
+ }
7495
+ if (desiredCollateral) {
7496
+ const collateralMatch = perpMetaAssets.find((a) => a.name === assetName && a.collateralToken === desiredCollateral);
7497
+ if (collateralMatch) {
7498
+ return {
7499
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7500
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7501
+ };
7502
+ }
7503
+ }
7504
+ if (assetName.includes(':')) {
7505
+ const [prefix, symbol] = assetName.split(':');
7506
+ const exactMatch = perpMetaAssets.find((a) => {
7507
+ var _a;
7508
+ return a.name === symbol &&
7509
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
7510
+ });
7511
+ if (exactMatch) {
7512
+ return {
7513
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7514
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7515
+ };
7516
+ }
7517
+ }
7518
+ if (knownPrefix) {
7519
+ const exactMatch = perpMetaAssets.find((a) => {
7520
+ var _a;
7521
+ return a.name === assetName &&
7522
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
7523
+ });
7524
+ if (exactMatch) {
7525
+ return {
7526
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
7527
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
7528
+ };
7529
+ }
7530
+ }
7531
+ const regularAsset = perpMetaAssets.find((a) => a.name === assetName && !a.marketPrefix);
7532
+ if (regularAsset) {
7533
+ return {
7534
+ collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
7535
+ marketPrefix: null,
7536
+ };
7537
+ }
7538
+ const hip3Assets = perpMetaAssets.filter((a) => a.name === assetName && a.marketPrefix);
7539
+ if (hip3Assets.length > 0) {
7540
+ if (desiredCollateral) {
7541
+ const collateralMatch = hip3Assets.find((a) => a.collateralToken === desiredCollateral);
7542
+ if (collateralMatch) {
7543
+ return {
7544
+ collateralToken: (_h = collateralMatch.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
7545
+ marketPrefix: (_j = collateralMatch.marketPrefix) !== null && _j !== void 0 ? _j : null,
7546
+ };
7547
+ }
7548
+ }
7549
+ const usdHMatch = hip3Assets.find((a) => a.collateralToken === 'USDH');
7550
+ const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Assets[0];
7551
+ return {
7552
+ collateralToken: (_k = chosen.collateralToken) !== null && _k !== void 0 ? _k : 'USDC',
7553
+ marketPrefix: (_l = chosen.marketPrefix) !== null && _l !== void 0 ? _l : null,
7554
+ };
7555
+ }
7556
+ return { collateralToken: 'USDC', marketPrefix: null };
7557
+ }
7558
+ function enrichOrderAssets(assets, perpMetaAssets) {
7559
+ if (!assets)
7560
+ return [];
7561
+ return assets.map((asset) => {
7562
+ var _a;
7563
+ if (asset.marketPrefix && asset.collateralToken) {
7564
+ return asset;
7565
+ }
7566
+ const meta = findAssetMeta$1(asset.asset, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
7567
+ return {
7568
+ ...asset,
7569
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
7570
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
7571
+ };
7572
+ });
7573
+ }
7574
+ function enrichTwapOrders(orders, perpMetaAssets) {
7575
+ return orders.map((order) => ({
7576
+ ...order,
7577
+ longAssets: enrichOrderAssets(order.longAssets, perpMetaAssets),
7578
+ shortAssets: enrichOrderAssets(order.shortAssets, perpMetaAssets),
7579
+ }));
7580
+ }
6733
7581
  function useTwap() {
6734
- const twapDetails = useUserData(state => state.twapDetails);
7582
+ const twapDetails = useUserData((state) => state.twapDetails);
7583
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
6735
7584
  const context = useContext(PearHyperliquidContext);
6736
7585
  if (!context)
6737
7586
  throw new Error('useTwap must be used within a PearHyperliquidProvider');
6738
7587
  const { apiBaseUrl } = context;
6739
- const orders = useMemo(() => twapDetails !== null && twapDetails !== void 0 ? twapDetails : [], [twapDetails]);
7588
+ const orders = useMemo(() => {
7589
+ const rawOrders = twapDetails !== null && twapDetails !== void 0 ? twapDetails : [];
7590
+ return enrichTwapOrders(rawOrders, allPerpMetaAssets);
7591
+ }, [twapDetails, allPerpMetaAssets]);
6740
7592
  const cancelTwap$1 = async (orderId) => {
6741
7593
  return cancelTwap(apiBaseUrl, orderId);
6742
7594
  };
@@ -6819,59 +7671,170 @@ function useNotifications() {
6819
7671
  };
6820
7672
  }
6821
7673
 
6822
- // Base selector for the full market-data payload
7674
+ // Helper to find asset metadata from perpMetaAssets
7675
+ function findAssetMeta(assetName, perpMetaAssets) {
7676
+ var _a, _b, _c, _d;
7677
+ if (!perpMetaAssets) {
7678
+ return { collateralToken: 'USDC', marketPrefix: null };
7679
+ }
7680
+ // Try exact match first (for prefixed assets like "xyz:TSLA")
7681
+ const exactMatch = perpMetaAssets.find((a) => a.name === assetName);
7682
+ if (exactMatch) {
7683
+ return {
7684
+ collateralToken: (_a = exactMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7685
+ marketPrefix: (_b = exactMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7686
+ };
7687
+ }
7688
+ // Try matching by base symbol (for non-prefixed names in data)
7689
+ const baseMatch = perpMetaAssets.find((a) => {
7690
+ const baseName = a.name.includes(':') ? a.name.split(':')[1] : a.name;
7691
+ return baseName === assetName;
7692
+ });
7693
+ if (baseMatch) {
7694
+ return {
7695
+ collateralToken: (_c = baseMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7696
+ marketPrefix: (_d = baseMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7697
+ };
7698
+ }
7699
+ return { collateralToken: 'USDC', marketPrefix: null };
7700
+ }
7701
+ // Enrich a single asset with metadata
7702
+ function enrichAsset(asset, perpMetaAssets) {
7703
+ const meta = findAssetMeta(asset.asset, perpMetaAssets);
7704
+ return {
7705
+ ...asset,
7706
+ collateralToken: meta.collateralToken,
7707
+ marketPrefix: meta.marketPrefix,
7708
+ };
7709
+ }
7710
+ // Enrich a basket item with collateral info
7711
+ function enrichBasketItem(item, perpMetaAssets) {
7712
+ const enrichedLongs = item.longAssets.map((a) => enrichAsset(a, perpMetaAssets));
7713
+ const enrichedShorts = item.shortAssets.map((a) => enrichAsset(a, perpMetaAssets));
7714
+ // Determine collateral type
7715
+ const allAssets = [...enrichedLongs, ...enrichedShorts];
7716
+ const hasUsdc = allAssets.some((a) => a.collateralToken === 'USDC');
7717
+ const hasUsdh = allAssets.some((a) => a.collateralToken === 'USDH');
7718
+ let collateralType = 'USDC';
7719
+ if (hasUsdc && hasUsdh) {
7720
+ collateralType = 'MIXED';
7721
+ }
7722
+ else if (hasUsdh) {
7723
+ collateralType = 'USDH';
7724
+ }
7725
+ return {
7726
+ ...item,
7727
+ longAssets: enrichedLongs,
7728
+ shortAssets: enrichedShorts,
7729
+ collateralType,
7730
+ };
7731
+ }
7732
+ /**
7733
+ * Filter baskets by collateral type
7734
+ * - 'USDC': Only baskets where ALL assets use USDC (collateralType === 'USDC')
7735
+ * - 'USDH': Only baskets where ALL assets use USDH (collateralType === 'USDH')
7736
+ * - 'ALL' or undefined: No filtering, returns all baskets
7737
+ */
7738
+ function filterByCollateral(baskets, filter) {
7739
+ if (!filter || filter === 'ALL') {
7740
+ return baskets;
7741
+ }
7742
+ return baskets.filter((basket) => {
7743
+ if (filter === 'USDC') {
7744
+ // Include baskets that are purely USDC or have USDC assets
7745
+ return (basket.collateralType === 'USDC' || basket.collateralType === 'MIXED');
7746
+ }
7747
+ if (filter === 'USDH') {
7748
+ // Include baskets that are purely USDH or have USDH assets
7749
+ return (basket.collateralType === 'USDH' || basket.collateralType === 'MIXED');
7750
+ }
7751
+ return true;
7752
+ });
7753
+ }
7754
+ // Base selector for the full market-data payload (raw from WS)
6823
7755
  const useMarketDataPayload = () => {
6824
7756
  return useMarketData((s) => s.marketData);
6825
7757
  };
6826
- // Full payload for 'market-data-all' channel
7758
+ // Full payload for 'market-data-all' channel (raw from WS)
6827
7759
  const useMarketDataAllPayload = () => {
6828
7760
  return useMarketData((s) => s.marketDataAll);
6829
7761
  };
6830
- // Active baskets
6831
- const useActiveBaskets = () => {
6832
- var _a;
7762
+ // Access perpMetaAssets for enrichment
7763
+ const usePerpMetaAssets = () => {
7764
+ return useHyperliquidData((s) => s.perpMetaAssets);
7765
+ };
7766
+ // Active baskets (with collateral and market prefix info)
7767
+ const useActiveBaskets = (collateralFilter) => {
6833
7768
  const data = useMarketDataPayload();
6834
- return (_a = data === null || data === void 0 ? void 0 : data.active) !== null && _a !== void 0 ? _a : [];
7769
+ const perpMetaAssets = usePerpMetaAssets();
7770
+ return useMemo(() => {
7771
+ if (!(data === null || data === void 0 ? void 0 : data.active))
7772
+ return [];
7773
+ const enriched = data.active.map((item) => enrichBasketItem(item, perpMetaAssets));
7774
+ return filterByCollateral(enriched, collateralFilter);
7775
+ }, [data, perpMetaAssets, collateralFilter]);
6835
7776
  };
6836
- // Top gainers (optional limit override)
6837
- const useTopGainers = (limit) => {
7777
+ // Top gainers (with collateral and market prefix info)
7778
+ const useTopGainers = (limit, collateralFilter) => {
6838
7779
  const data = useMarketDataPayload();
7780
+ const perpMetaAssets = usePerpMetaAssets();
6839
7781
  return useMemo(() => {
6840
7782
  var _a;
6841
7783
  const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
6842
- return typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
6843
- }, [data, limit]);
7784
+ const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7785
+ const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
7786
+ return filterByCollateral(enriched, collateralFilter);
7787
+ }, [data, perpMetaAssets, limit, collateralFilter]);
6844
7788
  };
6845
- // Top losers (optional limit override)
6846
- const useTopLosers = (limit) => {
7789
+ // Top losers (with collateral and market prefix info)
7790
+ const useTopLosers = (limit, collateralFilter) => {
6847
7791
  const data = useMarketDataPayload();
7792
+ const perpMetaAssets = usePerpMetaAssets();
6848
7793
  return useMemo(() => {
6849
7794
  var _a;
6850
7795
  const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
6851
- return typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
6852
- }, [data, limit]);
7796
+ const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7797
+ const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
7798
+ return filterByCollateral(enriched, collateralFilter);
7799
+ }, [data, perpMetaAssets, limit, collateralFilter]);
6853
7800
  };
6854
- // Highlighted baskets
6855
- const useHighlightedBaskets = () => {
6856
- var _a;
7801
+ // Highlighted baskets (with collateral and market prefix info)
7802
+ const useHighlightedBaskets = (collateralFilter) => {
6857
7803
  const data = useMarketDataPayload();
6858
- return (_a = data === null || data === void 0 ? void 0 : data.highlighted) !== null && _a !== void 0 ? _a : [];
7804
+ const perpMetaAssets = usePerpMetaAssets();
7805
+ return useMemo(() => {
7806
+ if (!(data === null || data === void 0 ? void 0 : data.highlighted))
7807
+ return [];
7808
+ const enriched = data.highlighted.map((item) => enrichBasketItem(item, perpMetaAssets));
7809
+ return filterByCollateral(enriched, collateralFilter);
7810
+ }, [data, perpMetaAssets, collateralFilter]);
6859
7811
  };
6860
- // Watchlist baskets (from market-data payload when subscribed with address)
6861
- const useWatchlistBaskets = () => {
6862
- var _a;
7812
+ // Watchlist baskets (with collateral and market prefix info)
7813
+ const useWatchlistBaskets = (collateralFilter) => {
6863
7814
  const data = useMarketDataPayload();
6864
- return (_a = data === null || data === void 0 ? void 0 : data.watchlist) !== null && _a !== void 0 ? _a : [];
7815
+ const perpMetaAssets = usePerpMetaAssets();
7816
+ return useMemo(() => {
7817
+ if (!(data === null || data === void 0 ? void 0 : data.watchlist))
7818
+ return [];
7819
+ const enriched = data.watchlist.map((item) => enrichBasketItem(item, perpMetaAssets));
7820
+ return filterByCollateral(enriched, collateralFilter);
7821
+ }, [data, perpMetaAssets, collateralFilter]);
6865
7822
  };
6866
- // All baskets (from market-data-all)
6867
- const useAllBaskets = () => {
6868
- var _a;
7823
+ // All baskets (with collateral and market prefix info)
7824
+ const useAllBaskets = (collateralFilter) => {
6869
7825
  const dataAll = useMarketDataAllPayload();
6870
- return (_a = dataAll === null || dataAll === void 0 ? void 0 : dataAll.all) !== null && _a !== void 0 ? _a : [];
7826
+ const perpMetaAssets = usePerpMetaAssets();
7827
+ return useMemo(() => {
7828
+ if (!(dataAll === null || dataAll === void 0 ? void 0 : dataAll.all))
7829
+ return [];
7830
+ const enriched = dataAll.all.map((item) => enrichBasketItem(item, perpMetaAssets));
7831
+ return filterByCollateral(enriched, collateralFilter);
7832
+ }, [dataAll, perpMetaAssets, collateralFilter]);
6871
7833
  };
6872
7834
  // Find a basket by its exact asset composition (order-insensitive)
6873
7835
  const useFindBasket = (longs, shorts) => {
6874
7836
  const data = useMarketDataPayload();
7837
+ const perpMetaAssets = usePerpMetaAssets();
6875
7838
  return useMemo(() => {
6876
7839
  if (!data)
6877
7840
  return undefined;
@@ -6885,17 +7848,28 @@ const useFindBasket = (longs, shorts) => {
6885
7848
  : '';
6886
7849
  const lKey = normalize(longs);
6887
7850
  const sKey = normalize(shorts);
6888
- const match = (item) => normalize(item.longAssets) === lKey && normalize(item.shortAssets) === sKey;
6889
- return data.active.find(match) || data.highlighted.find(match);
6890
- }, [data, longs, shorts]);
7851
+ const match = (item) => normalize(item.longAssets) === lKey &&
7852
+ normalize(item.shortAssets) === sKey;
7853
+ const found = data.active.find(match) || data.highlighted.find(match);
7854
+ return found
7855
+ ? enrichBasketItem(found, perpMetaAssets)
7856
+ : undefined;
7857
+ }, [data, longs, shorts, perpMetaAssets]);
6891
7858
  };
6892
7859
 
6893
- async function toggleWatchlist(baseUrl, longAssets, shortAssets, displayToFull) {
7860
+ async function toggleWatchlist(baseUrl, longAssets, shortAssets, hip3Assets) {
6894
7861
  const url = joinUrl(baseUrl, '/watchlist');
6895
- const mapAssets = (arr) => arr.map(a => ({ ...a, asset: toBackendSymbol(a.asset, displayToFull) }));
7862
+ const mapAssets = (arr) => arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6896
7863
  try {
6897
- const response = await apiClient.post(url, { longAssets: mapAssets(longAssets), shortAssets: mapAssets(shortAssets) }, { headers: { 'Content-Type': 'application/json' } });
6898
- return { data: response.data, status: response.status, headers: response.headers };
7864
+ const response = await apiClient.post(url, {
7865
+ longAssets: mapAssets(longAssets),
7866
+ shortAssets: mapAssets(shortAssets),
7867
+ }, { headers: { 'Content-Type': 'application/json' } });
7868
+ return {
7869
+ data: response.data,
7870
+ status: response.status,
7871
+ headers: response.headers,
7872
+ };
6899
7873
  }
6900
7874
  catch (error) {
6901
7875
  throw toApiError(error);
@@ -6907,11 +7881,11 @@ function useWatchlist() {
6907
7881
  if (!context)
6908
7882
  throw new Error('useWatchlist must be used within a PearHyperliquidProvider');
6909
7883
  const { apiBaseUrl, isConnected } = context;
6910
- const displayToFull = useHyperliquidData((s) => s.hip3DisplayToFull);
7884
+ const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
6911
7885
  const marketData = useMarketDataPayload();
6912
7886
  const isLoading = useMemo(() => !marketData && isConnected, [marketData, isConnected]);
6913
7887
  const toggle = async (longAssets, shortAssets) => {
6914
- const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, displayToFull);
7888
+ const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, hip3Assets);
6915
7889
  // Server will push updated market-data over WS; nothing to set here
6916
7890
  return resp;
6917
7891
  };
@@ -7165,16 +8139,48 @@ function useAuth() {
7165
8139
  };
7166
8140
  }
7167
8141
 
8142
+ const useSpotBalances = () => {
8143
+ const spotState = useUserData((state) => state.spotState);
8144
+ return useMemo(() => {
8145
+ if (!spotState) {
8146
+ return {
8147
+ usdhBalance: undefined,
8148
+ spotUsdcBalance: undefined,
8149
+ isLoading: true,
8150
+ };
8151
+ }
8152
+ const balances = spotState.balances || [];
8153
+ let usdhBal = 0;
8154
+ let spotUsdcBal = 0;
8155
+ for (const balance of balances) {
8156
+ const total = parseFloat(balance.total || '0');
8157
+ if (balance.coin === 'USDH') {
8158
+ usdhBal = total;
8159
+ }
8160
+ if (balance.coin === 'USDC') {
8161
+ spotUsdcBal = total;
8162
+ }
8163
+ }
8164
+ return {
8165
+ usdhBalance: usdhBal,
8166
+ spotUsdcBalance: spotUsdcBal,
8167
+ isLoading: false,
8168
+ };
8169
+ }, [spotState]);
8170
+ };
8171
+
7168
8172
  const PearHyperliquidContext = createContext(undefined);
7169
8173
  /**
7170
8174
  * React Provider for PearHyperliquidClient
7171
8175
  */
7172
- const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearprotocol.io", clientId = "PEARPROTOCOLUI", wsUrl = "wss://hl-ui.pearprotocol.io/ws", }) => {
8176
+ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearprotocol.io', clientId = 'PEARPROTOCOLUI', wsUrl = 'wss://hl-ui.pearprotocol.io/ws', }) => {
7173
8177
  const address = useUserData((s) => s.address);
7174
8178
  const setAddress = useUserData((s) => s.setAddress);
7175
8179
  const perpsMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
7176
8180
  const setPerpMetaAssets = useHyperliquidData((state) => state.setPerpMetaAssets);
7177
- const setHip3DisplayToFull = useHyperliquidData((state) => state.setHip3DisplayToFull);
8181
+ const setAllPerpMetaAssets = useHyperliquidData((state) => state.setAllPerpMetaAssets);
8182
+ const setHip3Assets = useHyperliquidData((state) => state.setHip3Assets);
8183
+ const setHip3MarketPrefixes = useHyperliquidData((state) => state.setHip3MarketPrefixes);
7178
8184
  const websocketsEnabled = useMemo(() => Array.isArray(perpsMetaAssets) && perpsMetaAssets.length > 0, [perpsMetaAssets]);
7179
8185
  const { isConnected, lastError } = useHyperliquidWebSocket({
7180
8186
  wsUrl,
@@ -7189,32 +8195,107 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearpro
7189
8195
  if (perpsMetaAssets === null) {
7190
8196
  fetchAllPerpMetas()
7191
8197
  .then((res) => {
7192
- // Only show HL and XYZ for now as other are using USDH collateral and need more work
7193
- const aggregatedPerpMetas = res.data
7194
- .slice(0, 2)
7195
- .flatMap((item) => item.universe);
7196
- const hip3Map = new Map();
7197
- const displayToFull = new Map();
7198
- const cleanedPerpMetas = aggregatedPerpMetas.map((asset) => {
7199
- var _a;
7200
- const [maybePrefix, maybeMarket] = asset.name.split(":");
7201
- if (maybeMarket) {
7202
- const prefix = maybePrefix.toLowerCase();
7203
- const market = maybeMarket;
7204
- const existing = (_a = hip3Map.get(prefix)) !== null && _a !== void 0 ? _a : [];
7205
- hip3Map.set(prefix, [...existing, market]);
7206
- displayToFull.set(market, `${prefix}:${market}`);
7207
- return { ...asset, name: market };
7208
- }
7209
- return asset;
8198
+ const assetToMarkets = new Map();
8199
+ const marketPrefixes = new Map();
8200
+ const FILTERED_PREFIXES = ['vntl', 'hyna'];
8201
+ // Group assets by market prefix to match WebSocket flattening order
8202
+ // WebSocket sends in order: "", "flx", "hyna", "vntl", "xyz"
8203
+ const assetsByPrefix = new Map();
8204
+ const allAssetsByPrefix = new Map();
8205
+ res.data.forEach((item) => {
8206
+ const collateralToken = item.collateralToken === 360 ? 'USDH' : 'USDC';
8207
+ item.universe.forEach((asset) => {
8208
+ var _a;
8209
+ const [maybePrefix, maybeMarket] = asset.name.split(':');
8210
+ if (maybeMarket) {
8211
+ // HIP3 asset with market prefix
8212
+ const prefix = maybePrefix.toLowerCase();
8213
+ const displayName = maybeMarket;
8214
+ const fullName = `${prefix}:${displayName}`;
8215
+ marketPrefixes.set(fullName, prefix);
8216
+ if (!FILTERED_PREFIXES.includes(prefix)) {
8217
+ const existingMarkets = (_a = assetToMarkets.get(displayName)) !== null && _a !== void 0 ? _a : [];
8218
+ if (!existingMarkets.includes(fullName)) {
8219
+ assetToMarkets.set(displayName, [
8220
+ ...existingMarkets,
8221
+ fullName,
8222
+ ]);
8223
+ }
8224
+ }
8225
+ const assetWithMeta = {
8226
+ ...asset,
8227
+ name: displayName,
8228
+ marketPrefix: prefix,
8229
+ collateralToken,
8230
+ };
8231
+ // Group by market prefix
8232
+ const allList = allAssetsByPrefix.get(prefix) || [];
8233
+ allList.push(assetWithMeta);
8234
+ allAssetsByPrefix.set(prefix, allList);
8235
+ if (!FILTERED_PREFIXES.includes(prefix)) {
8236
+ const cleanedList = assetsByPrefix.get(prefix) || [];
8237
+ cleanedList.push(assetWithMeta);
8238
+ assetsByPrefix.set(prefix, cleanedList);
8239
+ }
8240
+ }
8241
+ else {
8242
+ // Default market asset (no prefix)
8243
+ const assetWithMeta = {
8244
+ ...asset,
8245
+ collateralToken,
8246
+ };
8247
+ // Add to default market group ("")
8248
+ const defaultList = assetsByPrefix.get('') || [];
8249
+ defaultList.push(assetWithMeta);
8250
+ assetsByPrefix.set('', defaultList);
8251
+ const allDefaultList = allAssetsByPrefix.get('') || [];
8252
+ allDefaultList.push(assetWithMeta);
8253
+ allAssetsByPrefix.set('', allDefaultList);
8254
+ }
8255
+ });
8256
+ });
8257
+ // Flatten in consistent order: default market first, then HIP3 markets alphabetically
8258
+ // This ensures both REST API and WebSocket data align properly
8259
+ const cleanedPrefixes = Array.from(assetsByPrefix.keys()).sort((a, b) => {
8260
+ // Empty prefix (default market) always comes first
8261
+ if (a === '' && b !== '')
8262
+ return -1;
8263
+ if (a !== '' && b === '')
8264
+ return 1;
8265
+ // HIP3 markets sorted alphabetically
8266
+ return a.localeCompare(b);
8267
+ });
8268
+ const allPrefixes = Array.from(allAssetsByPrefix.keys()).sort((a, b) => {
8269
+ if (a === '' && b !== '')
8270
+ return -1;
8271
+ if (a !== '' && b === '')
8272
+ return 1;
8273
+ return a.localeCompare(b);
7210
8274
  });
7211
- setHip3DisplayToFull(displayToFull);
8275
+ const cleanedPerpMetas = [];
8276
+ const allPerpMetas = [];
8277
+ cleanedPrefixes.forEach((prefix) => {
8278
+ const assets = assetsByPrefix.get(prefix) || [];
8279
+ cleanedPerpMetas.push(...assets);
8280
+ });
8281
+ allPrefixes.forEach((prefix) => {
8282
+ const assets = allAssetsByPrefix.get(prefix) || [];
8283
+ allPerpMetas.push(...assets);
8284
+ });
8285
+ setHip3Assets(assetToMarkets);
8286
+ setHip3MarketPrefixes(marketPrefixes);
7212
8287
  setPerpMetaAssets(cleanedPerpMetas);
8288
+ setAllPerpMetaAssets(allPerpMetas);
7213
8289
  })
7214
8290
  .catch(() => { });
7215
8291
  }
7216
- }, [perpsMetaAssets, setPerpMetaAssets, setHip3DisplayToFull]);
7217
- // Auth methods now sourced from useAuth hook
8292
+ }, [
8293
+ perpsMetaAssets,
8294
+ setPerpMetaAssets,
8295
+ setAllPerpMetaAssets,
8296
+ setHip3Assets,
8297
+ setHip3MarketPrefixes,
8298
+ ]);
7218
8299
  useAutoSyncFills({
7219
8300
  baseUrl: apiBaseUrl,
7220
8301
  address,
@@ -7252,7 +8333,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearpro
7252
8333
  function usePearHyperliquid() {
7253
8334
  const ctx = useContext(PearHyperliquidContext);
7254
8335
  if (!ctx)
7255
- throw new Error("usePearHyperliquid must be used within a PearHyperliquidProvider");
8336
+ throw new Error('usePearHyperliquid must be used within a PearHyperliquidProvider');
7256
8337
  return ctx;
7257
8338
  }
7258
8339
 
@@ -7343,4 +8424,4 @@ function mapCandleIntervalToTradingViewInterval(interval) {
7343
8424
  }
7344
8425
  }
7345
8426
 
7346
- export { AccountSummaryCalculator, ConflictDetector, MINIMUM_ASSET_USD_VALUE, MinimumPositionSizeError, PearHyperliquidProvider, TokenMetadataExtractor, adjustAdvancePosition, adjustOrder, adjustPosition, calculateMinimumPositionValue, calculateWeightedRatio, cancelOrder, cancelTwap, cancelTwapOrder, closeAllPositions, closePosition, computeBasketCandles, createCandleLookups, createPosition, getCompleteTimestamps, getPortfolio, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, toggleWatchlist, updateRiskParameters, useAccountSummary, useActiveBaskets, useAgentWallet, useAllBaskets, useAuth, useAutoSyncFills, useBasketCandles, useFindBasket, useHighlightedBaskets, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMarketData, useMarketDataAllPayload, useMarketDataPayload, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePortfolio, usePosition, useTokenSelectionMetadata, useTopGainers, useTopLosers, useTradeHistories, useTwap, useUserSelection, useWatchlist, useWatchlistBaskets, useWebData, validateMinimumAssetSize, validatePositionSize };
8427
+ export { AccountSummaryCalculator, ConflictDetector, MAX_ASSETS_PER_LEG, MINIMUM_ASSET_USD_VALUE, MaxAssetsPerLegError, MinimumPositionSizeError, PearHyperliquidProvider, TokenMetadataExtractor, adjustAdvancePosition, adjustOrder, adjustPosition, calculateMinimumPositionValue, calculateWeightedRatio, cancelOrder, cancelTwap, cancelTwapOrder, closeAllPositions, closePosition, computeBasketCandles, createCandleLookups, createPosition, executeSpotOrder, getAvailableMarkets, getCompleteTimestamps, getMarketPrefix, getPortfolio, isHip3Market, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, toBackendSymbol, toBackendSymbolWithMarket, toDisplaySymbol, toggleWatchlist, updateRiskParameters, useAccountSummary, useActiveBaskets, useAgentWallet, useAllBaskets, useAuth, useAutoSyncFills, useBasketCandles, useFindBasket, useHighlightedBaskets, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMarketData, useMarketDataAllPayload, useMarketDataPayload, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePerpMetaAssets, usePortfolio, usePosition, useSpotBalances, useSpotOrder, useTokenSelectionMetadata, useTopGainers, useTopLosers, useTradeHistories, useTwap, useUserSelection, useWatchlist, useWatchlistBaskets, useWebData, validateMaxAssetsPerLeg, validateMinimumAssetSize, validatePositionSize };