@pear-protocol/hyperliquid-sdk 0.0.64 → 0.0.66-usdh-1

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([]);
@@ -556,7 +788,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
556
788
  .map(([, s]) => s)
557
789
  .filter(Boolean);
558
790
  const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || '0') || 0), 0);
559
- const toStr = (n) => (Number.isFinite(n) ? n.toString() : '0');
791
+ const toStr = (n) => Number.isFinite(n) ? n.toString() : '0';
560
792
  const assetPositions = states.flatMap((s) => s.assetPositions || []);
561
793
  const crossMaintenanceMarginUsed = toStr(sum(states.map((s) => s.crossMaintenanceMarginUsed)));
562
794
  const crossMarginSummary = {
@@ -587,19 +819,42 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
587
819
  case 'allMids':
588
820
  {
589
821
  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);
822
+ // Keep BOTH normalized prefixed keys AND display symbol keys
823
+ // This ensures xyz:TSLA and flx:TSLA are stored separately,
824
+ // while also maintaining backward compatibility with non-prefixed lookups
825
+ const mids = {};
826
+ Object.entries(data.mids || {}).forEach(([k, v]) => {
827
+ // Normalize prefixed keys to lowercase prefix (e.g., "XYZ:TSLA" -> "xyz:TSLA")
828
+ // This matches how we look up tokens in the SDK
829
+ let normalizedKey = k;
830
+ if (k.includes(':')) {
831
+ const [prefix, ...rest] = k.split(':');
832
+ normalizedKey = `${prefix.toLowerCase()}:${rest.join(':')}`;
833
+ }
834
+ // Store with normalized key
835
+ mids[normalizedKey] = v;
836
+ // Also store with original key for backward compatibility
837
+ if (k !== normalizedKey) {
838
+ mids[k] = v;
839
+ }
840
+ // Also store with display symbol for backward compatibility
841
+ const displayKey = toDisplaySymbol(k);
842
+ // Only set display key if it doesn't already exist (avoid overwriting market-specific prices)
843
+ if (!(displayKey in mids)) {
844
+ mids[displayKey] = v;
845
+ }
846
+ });
847
+ setAllMids({ mids });
596
848
  }
597
849
  break;
598
850
  case 'activeAssetData':
599
851
  {
600
852
  const assetData = response.data;
601
853
  const symbol = toDisplaySymbol(assetData.coin);
602
- const normalized = { ...assetData, coin: symbol };
854
+ const normalized = {
855
+ ...assetData,
856
+ coin: symbol,
857
+ };
603
858
  upsertActiveAssetData(symbol, normalized);
604
859
  }
605
860
  break;
@@ -611,6 +866,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
611
866
  addCandleData(symbol, normalized);
612
867
  }
613
868
  break;
869
+ case 'spotState':
870
+ {
871
+ const spotStateData = response.data;
872
+ if (spotStateData === null || spotStateData === void 0 ? void 0 : spotStateData.spotState) {
873
+ setSpotState(spotStateData.spotState);
874
+ }
875
+ }
876
+ break;
614
877
  default:
615
878
  console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
616
879
  }
@@ -621,7 +884,15 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
621
884
  console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
622
885
  setLastError(errorMessage);
623
886
  }
624
- }, [setAllMids, upsertActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState]);
887
+ }, [
888
+ setAllMids,
889
+ upsertActiveAssetData,
890
+ addCandleData,
891
+ setFinalAssetContexts,
892
+ setFinalAtOICaps,
893
+ setAggregatedClearingHouseState,
894
+ setSpotState,
895
+ ]);
625
896
  const connect = useCallback(() => {
626
897
  if (!enabled)
627
898
  return;
@@ -719,6 +990,17 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
719
990
  },
720
991
  };
721
992
  sendJsonMessage(unsubscribeMessage);
993
+ // Unsubscribe from spotState for previous address
994
+ if (subscribedAddress !== DEFAULT_ADDRESS) {
995
+ const unsubscribeSpotState = {
996
+ method: 'unsubscribe',
997
+ subscription: {
998
+ type: 'spotState',
999
+ user: subscribedAddress,
1000
+ },
1001
+ };
1002
+ sendJsonMessage(unsubscribeSpotState);
1003
+ }
722
1004
  const unsubscribeAllDexsClearinghouseState = {
723
1005
  method: 'unsubscribe',
724
1006
  subscription: {
@@ -762,13 +1044,34 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
762
1044
  sendJsonMessage(subscribeAllDexsClearinghouseState);
763
1045
  sendJsonMessage(subscribeAllMids);
764
1046
  sendJsonMessage(subscribeAllDexsAssetCtxs);
1047
+ // Subscribe to spotState for real-time spot balances (USDH, USDC, etc.)
1048
+ // Only subscribe if we have a real user address (not the default)
1049
+ if (userAddress !== DEFAULT_ADDRESS) {
1050
+ const subscribeSpotState = {
1051
+ method: 'subscribe',
1052
+ subscription: {
1053
+ type: 'spotState',
1054
+ user: userAddress,
1055
+ },
1056
+ };
1057
+ sendJsonMessage(subscribeSpotState);
1058
+ }
765
1059
  setSubscribedAddress(userAddress);
766
1060
  // Clear previous data when address changes
767
1061
  if (subscribedAddress && subscribedAddress !== userAddress) {
768
1062
  // clear aggregatedClearingHouseState
769
1063
  setAggregatedClearingHouseState(null);
1064
+ // clear spotState
1065
+ setSpotState(null);
770
1066
  }
771
- }, [isConnected, address, subscribedAddress, sendJsonMessage, setAggregatedClearingHouseState]);
1067
+ }, [
1068
+ isConnected,
1069
+ address,
1070
+ subscribedAddress,
1071
+ sendJsonMessage,
1072
+ setAggregatedClearingHouseState,
1073
+ setSpotState,
1074
+ ]);
772
1075
  // Handle token subscriptions for activeAssetData
773
1076
  useEffect(() => {
774
1077
  if (!isConnected || !address)
@@ -777,7 +1080,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
777
1080
  const tokensToSubscribe = effectiveTokens.filter((token) => token && !subscribedTokens.includes(token));
778
1081
  const tokensToUnsubscribe = subscribedTokens.filter((token) => !effectiveTokens.includes(token));
779
1082
  // Unsubscribe from tokens no longer in the list
780
- tokensToUnsubscribe.forEach(token => {
1083
+ tokensToUnsubscribe.forEach((token) => {
781
1084
  const unsubscribeMessage = {
782
1085
  method: 'unsubscribe',
783
1086
  subscription: {
@@ -789,7 +1092,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
789
1092
  sendJsonMessage(unsubscribeMessage);
790
1093
  });
791
1094
  // Subscribe to new tokens
792
- tokensToSubscribe.forEach(token => {
1095
+ tokensToSubscribe.forEach((token) => {
793
1096
  const subscribeMessage = {
794
1097
  method: 'subscribe',
795
1098
  subscription: {
@@ -804,7 +1107,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
804
1107
  setSubscribedTokens(effectiveTokens.filter((token) => token));
805
1108
  tokensToSubscribe.forEach((token) => deleteActiveAssetData(token));
806
1109
  }
807
- }, [isConnected, address, selectedTokenSymbols, subscribedTokens, sendJsonMessage, setActiveAssetData]);
1110
+ }, [
1111
+ isConnected,
1112
+ address,
1113
+ selectedTokenSymbols,
1114
+ subscribedTokens,
1115
+ sendJsonMessage,
1116
+ setActiveAssetData,
1117
+ ]);
808
1118
  // Handle candle subscriptions for tokens and interval changes
809
1119
  useEffect(() => {
810
1120
  if (!isConnected)
@@ -813,7 +1123,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
813
1123
  // Unsubscribe from previous candle subscriptions if interval changed
814
1124
  const prevInterval = prevCandleIntervalRef.current;
815
1125
  if (prevInterval && prevInterval !== candleInterval) {
816
- subscribedCandleTokens.forEach(token => {
1126
+ subscribedCandleTokens.forEach((token) => {
817
1127
  const unsubscribeMessage = {
818
1128
  method: 'unsubscribe',
819
1129
  subscription: {
@@ -854,12 +1164,21 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
854
1164
  sendJsonMessage(subscribeMessage);
855
1165
  });
856
1166
  // Update subscribed state
857
- if (tokensToSubscribe.length > 0 || tokensToUnsubscribe.length > 0 || prevInterval !== candleInterval) {
1167
+ if (tokensToSubscribe.length > 0 ||
1168
+ tokensToUnsubscribe.length > 0 ||
1169
+ prevInterval !== candleInterval) {
858
1170
  setSubscribedCandleTokens(effectiveTokens.filter((token) => token));
859
1171
  prevCandleIntervalRef.current = candleInterval;
860
1172
  tokensToUnsubscribe.forEach((token) => deleteCandleSymbol(token));
861
1173
  }
862
- }, [isConnected, selectedTokenSymbols, candleInterval, subscribedCandleTokens, sendJsonMessage, setCandleData]);
1174
+ }, [
1175
+ isConnected,
1176
+ selectedTokenSymbols,
1177
+ candleInterval,
1178
+ subscribedCandleTokens,
1179
+ sendJsonMessage,
1180
+ setCandleData,
1181
+ ]);
863
1182
  return {
864
1183
  isConnected,
865
1184
  lastError,
@@ -948,20 +1267,112 @@ const useAccountSummary = () => {
948
1267
  return { data: calculated, isLoading };
949
1268
  };
950
1269
 
1270
+ function findAssetMeta$4(coinName, perpMetaAssets, knownPrefix, desiredCollateral) {
1271
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
1272
+ if (!perpMetaAssets) {
1273
+ return { collateralToken: 'USDC', marketPrefix: null };
1274
+ }
1275
+ if (desiredCollateral) {
1276
+ const collateralMatch = perpMetaAssets.find((a) => a.name === coinName && a.collateralToken === desiredCollateral);
1277
+ if (collateralMatch) {
1278
+ return {
1279
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
1280
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
1281
+ };
1282
+ }
1283
+ }
1284
+ if (coinName.includes(':')) {
1285
+ const [prefix, symbol] = coinName.split(':');
1286
+ const exactMatch = perpMetaAssets.find((a) => {
1287
+ var _a;
1288
+ return a.name === symbol &&
1289
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
1290
+ });
1291
+ if (exactMatch) {
1292
+ return {
1293
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
1294
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
1295
+ };
1296
+ }
1297
+ }
1298
+ if (knownPrefix) {
1299
+ const exactMatch = perpMetaAssets.find((a) => {
1300
+ var _a;
1301
+ return a.name === coinName &&
1302
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
1303
+ });
1304
+ if (exactMatch) {
1305
+ return {
1306
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
1307
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
1308
+ };
1309
+ }
1310
+ }
1311
+ const exactMatch = perpMetaAssets.find((a) => a.name === coinName && !a.marketPrefix);
1312
+ if (exactMatch) {
1313
+ return {
1314
+ collateralToken: (_g = exactMatch.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
1315
+ marketPrefix: (_h = exactMatch.marketPrefix) !== null && _h !== void 0 ? _h : null,
1316
+ };
1317
+ }
1318
+ const hip3Matches = perpMetaAssets.filter((a) => a.name === coinName && a.marketPrefix);
1319
+ if (hip3Matches.length > 0) {
1320
+ if (desiredCollateral) {
1321
+ const collateralMatch = hip3Matches.find((a) => a.collateralToken === desiredCollateral);
1322
+ if (collateralMatch) {
1323
+ return {
1324
+ collateralToken: (_j = collateralMatch.collateralToken) !== null && _j !== void 0 ? _j : 'USDC',
1325
+ marketPrefix: (_k = collateralMatch.marketPrefix) !== null && _k !== void 0 ? _k : null,
1326
+ };
1327
+ }
1328
+ }
1329
+ const usdHMatch = hip3Matches.find((a) => a.collateralToken === 'USDH');
1330
+ const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Matches[0];
1331
+ return {
1332
+ collateralToken: (_l = chosen.collateralToken) !== null && _l !== void 0 ? _l : 'USDC',
1333
+ marketPrefix: (_m = chosen.marketPrefix) !== null && _m !== void 0 ? _m : null,
1334
+ };
1335
+ }
1336
+ return { collateralToken: 'USDC', marketPrefix: null };
1337
+ }
1338
+ function enrichTradeHistoryAssets(assets, perpMetaAssets) {
1339
+ return assets.map((asset) => {
1340
+ var _a;
1341
+ if (asset.marketPrefix && asset.collateralToken) {
1342
+ return asset;
1343
+ }
1344
+ const meta = findAssetMeta$4(asset.coin, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
1345
+ return {
1346
+ ...asset,
1347
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
1348
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
1349
+ };
1350
+ });
1351
+ }
1352
+ function enrichTradeHistories(histories, perpMetaAssets) {
1353
+ return histories.map((history) => ({
1354
+ ...history,
1355
+ closedLongAssets: enrichTradeHistoryAssets(history.closedLongAssets, perpMetaAssets),
1356
+ closedShortAssets: enrichTradeHistoryAssets(history.closedShortAssets, perpMetaAssets),
1357
+ }));
1358
+ }
951
1359
  const useTradeHistories = () => {
952
1360
  const context = useContext(PearHyperliquidContext);
953
1361
  if (!context) {
954
1362
  throw new Error('useTradeHistories must be used within a PearHyperliquidProvider');
955
1363
  }
956
1364
  const tradeHistories = useUserData((state) => state.tradeHistories);
1365
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
957
1366
  const isLoading = useMemo(() => {
958
1367
  return tradeHistories === null && context.isConnected;
959
1368
  }, [tradeHistories, context.isConnected]);
960
- return { data: tradeHistories, isLoading };
1369
+ const enrichedTradeHistories = useMemo(() => {
1370
+ if (!tradeHistories)
1371
+ return null;
1372
+ return enrichTradeHistories(tradeHistories, allPerpMetaAssets);
1373
+ }, [tradeHistories, allPerpMetaAssets]);
1374
+ return { data: enrichedTradeHistories, isLoading };
961
1375
  };
962
- /**
963
- * Hook to access open orders with loading state
964
- */
965
1376
  const useOpenOrders = () => {
966
1377
  const context = useContext(PearHyperliquidContext);
967
1378
  if (!context) {
@@ -990,21 +1401,51 @@ const useWebData = () => {
990
1401
  const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
991
1402
  const aggregatedClearinghouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
992
1403
  const finalAtOICaps = useHyperliquidData((state) => state.finalAtOICaps);
993
- const hip3Assets = useHyperliquidData((state) => state.hip3DisplayToFull);
1404
+ const hip3Assets = useHyperliquidData((state) => state.hip3Assets);
1405
+ const hip3MarketPrefixes = useHyperliquidData((state) => state.hip3MarketPrefixes);
994
1406
  let marketDataBySymbol = {};
995
1407
  if (finalAssetContexts && perpMetaAssets) {
996
1408
  const result = {};
1409
+ // Build a map of display name -> asset context index (for unique display names)
1410
+ const displayNameToContextIndex = new Map();
1411
+ const seenNames = new Set();
1412
+ let contextIndex = 0;
1413
+ // First pass: map unique display names to their context index
997
1414
  for (let index = 0; index < perpMetaAssets.length; index++) {
998
1415
  const name = perpMetaAssets[index].name;
999
- result[name] = {
1000
- asset: finalAssetContexts[index],
1001
- universe: perpMetaAssets[index],
1002
- };
1416
+ if (!seenNames.has(name)) {
1417
+ seenNames.add(name);
1418
+ if (contextIndex < finalAssetContexts.length) {
1419
+ displayNameToContextIndex.set(name, contextIndex);
1420
+ contextIndex++;
1421
+ }
1422
+ }
1423
+ }
1424
+ // Second pass: create nested entries for all market variants
1425
+ for (let index = 0; index < perpMetaAssets.length; index++) {
1426
+ const universeAsset = perpMetaAssets[index];
1427
+ const displayName = universeAsset.name;
1428
+ const marketPrefix = universeAsset.marketPrefix;
1429
+ const ctxIndex = displayNameToContextIndex.get(displayName);
1430
+ if (ctxIndex !== undefined) {
1431
+ const assetContext = finalAssetContexts[ctxIndex];
1432
+ // Initialize the symbol entry if it doesn't exist
1433
+ if (!result[displayName]) {
1434
+ result[displayName] = {};
1435
+ }
1436
+ // Use marketPrefix as key for HIP-3 assets, "default" for regular assets
1437
+ const variantKey = marketPrefix || 'default';
1438
+ result[displayName][variantKey] = {
1439
+ asset: assetContext,
1440
+ universe: universeAsset,
1441
+ };
1442
+ }
1003
1443
  }
1004
1444
  marketDataBySymbol = result;
1005
1445
  }
1006
1446
  return {
1007
1447
  hip3Assets,
1448
+ hip3MarketPrefixes,
1008
1449
  clearinghouseState: aggregatedClearinghouseState,
1009
1450
  perpsAtOpenInterestCap: finalAtOICaps,
1010
1451
  marketDataBySymbol,
@@ -1019,30 +1460,60 @@ const useWebData = () => {
1019
1460
  class TokenMetadataExtractor {
1020
1461
  /**
1021
1462
  * Extracts comprehensive token metadata
1022
- * @param symbol - Token symbol
1463
+ * @param symbol - Token symbol (base symbol without prefix, e.g., "TSLA")
1023
1464
  * @param perpMetaAssets - Aggregated universe assets (flattened across dexes)
1024
1465
  * @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
1025
1466
  * @param allMids - AllMids data containing current prices
1026
1467
  * @param activeAssetData - Optional active asset data containing leverage information
1468
+ * @param marketPrefix - Optional market prefix (e.g., "xyz", "flx") for HIP3 multi-market assets
1027
1469
  * @returns TokenMetadata or null if token not found
1028
1470
  */
1029
- static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1471
+ static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketPrefix) {
1030
1472
  if (!perpMetaAssets || !finalAssetContexts || !allMids) {
1031
1473
  return null;
1032
1474
  }
1033
1475
  // Find token index in aggregated universe
1034
- const universeIndex = perpMetaAssets.findIndex(asset => asset.name === symbol);
1035
- if (universeIndex === -1) {
1476
+ // For HIP3 assets, match both name AND marketPrefix
1477
+ const universeIndex = perpMetaAssets.findIndex((asset) => {
1478
+ if (asset.name !== symbol)
1479
+ return false;
1480
+ // If marketPrefix is specified, match it; otherwise match assets without prefix
1481
+ if (marketPrefix) {
1482
+ return asset.marketPrefix === marketPrefix;
1483
+ }
1484
+ // No prefix specified - match non-HIP3 asset (no marketPrefix) or first matching asset
1485
+ return !asset.marketPrefix;
1486
+ });
1487
+ // If no exact match found and prefix was specified, try finding the specific market variant
1488
+ const finalIndex = universeIndex === -1 && marketPrefix
1489
+ ? perpMetaAssets.findIndex((asset) => asset.name === symbol && asset.marketPrefix === marketPrefix)
1490
+ : universeIndex;
1491
+ // Fallback: if still not found and no prefix, find first matching by name (for backward compatibility)
1492
+ const resolvedIndex = finalIndex === -1
1493
+ ? perpMetaAssets.findIndex((asset) => asset.name === symbol)
1494
+ : finalIndex;
1495
+ if (resolvedIndex === -1) {
1036
1496
  return null;
1037
1497
  }
1038
- const universeAsset = perpMetaAssets[universeIndex];
1039
- const assetCtx = finalAssetContexts[universeIndex];
1498
+ const universeAsset = perpMetaAssets[resolvedIndex];
1499
+ const assetCtx = finalAssetContexts[resolvedIndex];
1040
1500
  if (!assetCtx) {
1041
1501
  return null;
1042
1502
  }
1043
- // Get current price from allMids
1044
- const currentPriceStr = allMids.mids[symbol];
1045
- const currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
1503
+ // Get current price - prefer assetCtx.midPx as it's already index-matched,
1504
+ // fall back to allMids lookup if midPx is null
1505
+ const prefixedKeyColon = marketPrefix ? `${marketPrefix}:${symbol}` : null;
1506
+ let currentPrice = 0;
1507
+ // Primary source: assetCtx.midPx (already properly indexed)
1508
+ if (assetCtx.midPx) {
1509
+ currentPrice = parseFloat(assetCtx.midPx);
1510
+ }
1511
+ // Fallback: allMids lookup with multiple key formats for HIP3 markets
1512
+ if (!currentPrice || isNaN(currentPrice)) {
1513
+ const currentPriceStr = (prefixedKeyColon && allMids.mids[prefixedKeyColon]) ||
1514
+ allMids.mids[symbol];
1515
+ currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
1516
+ }
1046
1517
  // Get previous day price
1047
1518
  const prevDayPrice = parseFloat(assetCtx.prevDayPx);
1048
1519
  // Calculate 24h price change
@@ -1053,7 +1524,11 @@ class TokenMetadataExtractor {
1053
1524
  const markPrice = parseFloat(assetCtx.markPx);
1054
1525
  const oraclePrice = parseFloat(assetCtx.oraclePx);
1055
1526
  // Extract leverage info from activeAssetData if available
1056
- const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[symbol];
1527
+ // Try prefixed key first (e.g., "xyz:TSLA"), then fall back to plain symbol
1528
+ const activeDataKey = prefixedKeyColon && (activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[prefixedKeyColon])
1529
+ ? prefixedKeyColon
1530
+ : symbol;
1531
+ const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[activeDataKey];
1057
1532
  const leverage = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.leverage;
1058
1533
  const maxTradeSzs = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.maxTradeSzs;
1059
1534
  const availableToTrade = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.availableToTrade;
@@ -1071,21 +1546,27 @@ class TokenMetadataExtractor {
1071
1546
  leverage,
1072
1547
  maxTradeSzs,
1073
1548
  availableToTrade,
1549
+ collateralToken: universeAsset.collateralToken,
1074
1550
  };
1075
1551
  }
1076
1552
  /**
1077
1553
  * Extracts metadata for multiple tokens
1078
- * @param symbols - Array of token symbols
1554
+ * @param tokens - Array of token objects with symbol and optional marketPrefix
1079
1555
  * @param perpMetaAssets - Aggregated universe assets
1080
1556
  * @param finalAssetContexts - Aggregated asset contexts
1081
1557
  * @param allMids - AllMids data
1082
1558
  * @param activeAssetData - Optional active asset data containing leverage information
1083
- * @returns Record of symbol to TokenMetadata
1559
+ * @returns Record of unique key to TokenMetadata. Key is "{prefix}:{symbol}" for HIP3 assets, or just "{symbol}" otherwise
1084
1560
  */
1085
- static extractMultipleTokensMetadata(symbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1561
+ static extractMultipleTokensMetadata(tokens, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1086
1562
  const result = {};
1087
- for (const symbol of symbols) {
1088
- result[symbol] = this.extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData);
1563
+ for (const token of tokens) {
1564
+ // Use a unique key that includes the prefix for HIP3 assets
1565
+ // This ensures xyz:TSLA and flx:TSLA get separate entries
1566
+ const resultKey = token.marketPrefix
1567
+ ? `${token.marketPrefix}:${token.symbol}`
1568
+ : token.symbol;
1569
+ result[resultKey] = this.extractTokenMetadata(token.symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, token.marketPrefix);
1089
1570
  }
1090
1571
  return result;
1091
1572
  }
@@ -1098,10 +1579,30 @@ class TokenMetadataExtractor {
1098
1579
  static isTokenAvailable(symbol, perpMetaAssets) {
1099
1580
  if (!perpMetaAssets)
1100
1581
  return false;
1101
- return perpMetaAssets.some(asset => asset.name === symbol);
1582
+ return perpMetaAssets.some((asset) => asset.name === symbol);
1102
1583
  }
1103
1584
  }
1104
1585
 
1586
+ /**
1587
+ * Parse a token string that may have a market prefix (e.g., "xyz:GOOGL" -> { prefix: "xyz", symbol: "GOOGL" })
1588
+ * This allows us to keep the full name (xyz:GOOGL) for URLs/tags while extracting just the symbol for SDK lookups.
1589
+ */
1590
+ function parseTokenWithPrefix(token) {
1591
+ if (token.includes(':')) {
1592
+ const [prefix, ...rest] = token.split(':');
1593
+ const symbol = rest.join(':').toUpperCase();
1594
+ return {
1595
+ prefix: prefix.toLowerCase(),
1596
+ symbol,
1597
+ fullName: `${prefix.toLowerCase()}:${symbol}`,
1598
+ };
1599
+ }
1600
+ return {
1601
+ prefix: null,
1602
+ symbol: token.toUpperCase(),
1603
+ fullName: token.toUpperCase(),
1604
+ };
1605
+ }
1105
1606
  const useTokenSelectionMetadataStore = create((set) => ({
1106
1607
  isPriceDataReady: false,
1107
1608
  isLoading: true,
@@ -1111,23 +1612,65 @@ const useTokenSelectionMetadataStore = create((set) => ({
1111
1612
  weightedRatio24h: 1,
1112
1613
  priceRatio: 1,
1113
1614
  priceRatio24h: 1,
1114
- openInterest: "0",
1115
- volume: "0",
1615
+ openInterest: '0',
1616
+ volume: '0',
1116
1617
  sumNetFunding: 0,
1117
1618
  maxLeverage: 0,
1118
1619
  minMargin: 0,
1119
1620
  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)
1621
+ recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens, }) => {
1622
+ const isPriceDataReady = !!(perpMetaAssets &&
1623
+ finalAssetContexts &&
1624
+ allMids);
1625
+ // Parse tokens - handle prefixed tokens like "xyz:GOOGL" by extracting the symbol and market prefix
1626
+ // The full name (xyz:GOOGL) is kept as the metadata key for UI consistency
1627
+ const parsedLongTokens = longTokens.map((t) => ({
1628
+ ...t,
1629
+ parsed: parseTokenWithPrefix(t.symbol),
1630
+ }));
1631
+ const parsedShortTokens = shortTokens.map((t) => ({
1632
+ ...t,
1633
+ parsed: parseTokenWithPrefix(t.symbol),
1634
+ }));
1635
+ // Extract base symbols with their market prefixes for SDK lookups
1636
+ // This ensures xyz:TSLA and flx:TSLA get different market data
1637
+ const longTokensForLookup = parsedLongTokens.map((t) => ({
1638
+ symbol: t.parsed.symbol,
1639
+ marketPrefix: t.parsed.prefix,
1640
+ }));
1641
+ const shortTokensForLookup = parsedShortTokens.map((t) => ({
1642
+ symbol: t.parsed.symbol,
1643
+ marketPrefix: t.parsed.prefix,
1644
+ }));
1645
+ // Also extract just the base symbols (without prefix) for lookups that don't support prefixes
1646
+ const longBaseSymbols = longTokensForLookup.map((t) => t.symbol);
1647
+ const shortBaseSymbols = shortTokensForLookup.map((t) => t.symbol);
1648
+ // Get metadata using base symbols with market prefix for proper market differentiation
1649
+ const longBaseMetadata = isPriceDataReady
1650
+ ? TokenMetadataExtractor.extractMultipleTokensMetadata(longTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1127
1651
  : {};
1128
- const shortTokensMetadata = isPriceDataReady
1129
- ? TokenMetadataExtractor.extractMultipleTokensMetadata(shortSymbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1652
+ const shortBaseMetadata = isPriceDataReady
1653
+ ? TokenMetadataExtractor.extractMultipleTokensMetadata(shortTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1130
1654
  : {};
1655
+ // Re-map metadata using original full names (with prefix) as keys for UI consistency
1656
+ // The extractor now keys by "{prefix}:{symbol}" for prefixed tokens, which matches our parsed.fullName
1657
+ const longTokensMetadata = {};
1658
+ parsedLongTokens.forEach((t) => {
1659
+ var _a;
1660
+ // Use the full name (e.g., "xyz:TSLA") as the lookup key since extractor uses the same format
1661
+ const lookupKey = t.parsed.prefix
1662
+ ? `${t.parsed.prefix}:${t.parsed.symbol}`
1663
+ : t.parsed.symbol;
1664
+ longTokensMetadata[t.symbol] = (_a = longBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
1665
+ });
1666
+ const shortTokensMetadata = {};
1667
+ parsedShortTokens.forEach((t) => {
1668
+ var _a;
1669
+ const lookupKey = t.parsed.prefix
1670
+ ? `${t.parsed.prefix}:${t.parsed.symbol}`
1671
+ : t.parsed.symbol;
1672
+ shortTokensMetadata[t.symbol] = (_a = shortBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
1673
+ });
1131
1674
  // Determine loading state
1132
1675
  const allTokens = [...longTokens, ...shortTokens];
1133
1676
  const isLoading = (() => {
@@ -1135,26 +1678,33 @@ const useTokenSelectionMetadataStore = create((set) => ({
1135
1678
  return true;
1136
1679
  if (allTokens.length === 0)
1137
1680
  return false;
1138
- const allMetadata = { ...longTokensMetadata, ...shortTokensMetadata };
1681
+ const allMetadata = {
1682
+ ...longTokensMetadata,
1683
+ ...shortTokensMetadata,
1684
+ };
1139
1685
  return allTokens.some((token) => !allMetadata[token.symbol]);
1140
1686
  })();
1141
1687
  // Open interest and volume (from market data for matching asset basket)
1688
+ // Use base symbols (without prefix) for matching against market data
1142
1689
  const { openInterest, volume } = (() => {
1143
- const empty = { openInterest: "0", volume: "0" };
1690
+ const empty = { openInterest: '0', volume: '0' };
1144
1691
  if (!(marketData === null || marketData === void 0 ? void 0 : marketData.active) || (!longTokens.length && !shortTokens.length))
1145
1692
  return empty;
1146
- const selectedLong = longTokens.map((t) => t.symbol).sort();
1147
- const selectedShort = shortTokens.map((t) => t.symbol).sort();
1693
+ const selectedLong = longBaseSymbols.slice().sort();
1694
+ const selectedShort = shortBaseSymbols.slice().sort();
1148
1695
  const match = marketData.active.find((item) => {
1149
1696
  const longs = [...item.longAssets].sort();
1150
1697
  const shorts = [...item.shortAssets].sort();
1151
- if (longs.length !== selectedLong.length || shorts.length !== selectedShort.length)
1698
+ if (longs.length !== selectedLong.length ||
1699
+ shorts.length !== selectedShort.length)
1152
1700
  return false;
1153
1701
  const longsEqual = longs.every((s, i) => s.asset === selectedLong[i]);
1154
1702
  const shortsEqual = shorts.every((s, i) => s.asset === selectedShort[i]);
1155
1703
  return longsEqual && shortsEqual;
1156
1704
  });
1157
- return match ? { openInterest: match.openInterest, volume: match.volume } : empty;
1705
+ return match
1706
+ ? { openInterest: match.openInterest, volume: match.volume }
1707
+ : empty;
1158
1708
  })();
1159
1709
  // Price ratio (only when exactly one long and one short)
1160
1710
  const { priceRatio, priceRatio24h } = (() => {
@@ -1234,17 +1784,27 @@ const useTokenSelectionMetadataStore = create((set) => ({
1234
1784
  return totalFunding;
1235
1785
  })();
1236
1786
  // Max leverage (minimum across all tokens)
1787
+ // Use tokens with their market prefixes for proper lookup in perpMetaAssets
1237
1788
  const maxLeverage = (() => {
1238
1789
  if (!perpMetaAssets)
1239
1790
  return 0;
1240
- const allTokenSymbols = [...longTokens, ...shortTokens].map((t) => t.symbol);
1241
- if (allTokenSymbols.length === 0)
1791
+ const allTokensForLookup = [
1792
+ ...longTokensForLookup,
1793
+ ...shortTokensForLookup,
1794
+ ];
1795
+ if (allTokensForLookup.length === 0)
1242
1796
  return 0;
1243
1797
  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);
1798
+ allTokensForLookup.forEach(({ symbol, marketPrefix }) => {
1799
+ // Match by both name AND marketPrefix for HIP3 assets
1800
+ const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol &&
1801
+ (marketPrefix
1802
+ ? u.marketPrefix === marketPrefix
1803
+ : !u.marketPrefix));
1804
+ // Fallback to just matching by name if no exact match
1805
+ const fallbackUniverse = tokenUniverse || perpMetaAssets.find((u) => u.name === symbol);
1806
+ if (fallbackUniverse === null || fallbackUniverse === void 0 ? void 0 : fallbackUniverse.maxLeverage)
1807
+ minLev = Math.min(minLev, fallbackUniverse.maxLeverage);
1248
1808
  });
1249
1809
  return minLev === Infinity ? 0 : minLev;
1250
1810
  })();
@@ -1256,7 +1816,10 @@ const useTokenSelectionMetadataStore = create((set) => ({
1256
1816
  // Whether all tokens have matching leverage
1257
1817
  const leverageMatched = (() => {
1258
1818
  const allTokensArr = [...longTokens, ...shortTokens];
1259
- const allMetadata = { ...longTokensMetadata, ...shortTokensMetadata };
1819
+ const allMetadata = {
1820
+ ...longTokensMetadata,
1821
+ ...shortTokensMetadata,
1822
+ };
1260
1823
  if (allTokensArr.length === 0)
1261
1824
  return true;
1262
1825
  const tokensWithLev = allTokensArr.filter((token) => { var _a; return (_a = allMetadata[token.symbol]) === null || _a === void 0 ? void 0 : _a.leverage; });
@@ -5441,8 +6004,8 @@ function addAuthInterceptors(params) {
5441
6004
  /**
5442
6005
  * Fetch historical candle data from HyperLiquid API
5443
6006
  */
5444
- const fetchHistoricalCandles = async (coin, startTime, endTime, interval, displayToFull) => {
5445
- const backendCoin = toBackendSymbol(coin, displayToFull);
6007
+ const fetchHistoricalCandles = async (coin, startTime, endTime, interval, hip3Assets) => {
6008
+ const backendCoin = toBackendSymbol(coin, hip3Assets);
5446
6009
  const request = {
5447
6010
  req: { coin: backendCoin, startTime, endTime, interval },
5448
6011
  type: 'candleSnapshot',
@@ -5601,10 +6164,10 @@ const useHistoricalPriceData = () => {
5601
6164
  setTokenLoading(token.symbol, true);
5602
6165
  });
5603
6166
  try {
5604
- const displayToFull = useHyperliquidData.getState().hip3DisplayToFull;
6167
+ const hip3Assets = useHyperliquidData.getState().hip3Assets;
5605
6168
  const fetchPromises = tokensToFetch.map(async (token) => {
5606
6169
  try {
5607
- const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, displayToFull);
6170
+ const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, hip3Assets);
5608
6171
  addHistoricalPriceData(token.symbol, interval, response.data, { start: startTime, end: endTime });
5609
6172
  return { symbol: token.symbol, candles: response.data, success: true };
5610
6173
  }
@@ -6288,97 +6851,19 @@ function useAutoSyncFills(options) {
6288
6851
  }
6289
6852
 
6290
6853
  /**
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
6854
+ * Create a position (MARKET/LIMIT/TWAP) using Pear Hyperliquid service
6855
+ * Authorization is derived from headers (Axios defaults or browser localStorage fallback)
6856
+ * @throws MinimumPositionSizeError if any asset has less than $11 USD value
6857
+ * @throws MaxAssetsPerLegError if any leg exceeds the maximum allowed assets (15)
6312
6858
  */
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) {
6859
+ async function createPosition(baseUrl, payload, hip3Assets) {
6860
+ // Validate maximum assets per leg before creating position
6861
+ validateMaxAssetsPerLeg(payload.longAssets, payload.shortAssets);
6377
6862
  // Validate minimum asset size before creating position
6378
6863
  validateMinimumAssetSize(payload.usdValue, payload.longAssets, payload.shortAssets);
6379
6864
  const url = joinUrl(baseUrl, "/positions");
6380
6865
  // 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) }));
6866
+ const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6382
6867
  const translatedPayload = {
6383
6868
  ...payload,
6384
6869
  longAssets: mapAssets(payload.longAssets),
@@ -6477,9 +6962,9 @@ async function adjustPosition(baseUrl, positionId, payload) {
6477
6962
  throw toApiError(error);
6478
6963
  }
6479
6964
  }
6480
- async function adjustAdvancePosition(baseUrl, positionId, payload, displayToFull) {
6965
+ async function adjustAdvancePosition(baseUrl, positionId, payload, hip3Assets) {
6481
6966
  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) }));
6967
+ const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6483
6968
  const translatedPayload = (payload || []).map((item) => ({
6484
6969
  longAssets: mapAssets(item.longAssets),
6485
6970
  shortAssets: mapAssets(item.shortAssets),
@@ -6542,6 +7027,9 @@ const calculatePositionAsset = (asset, currentPrice, totalInitialPositionSize, l
6542
7027
  entryPositionValue: entryNotional,
6543
7028
  initialWeight: totalInitialPositionSize > 0 ? entryNotional / totalInitialPositionSize : 0,
6544
7029
  fundingPaid: (_a = asset.fundingPaid) !== null && _a !== void 0 ? _a : 0,
7030
+ // Preserve market metadata from raw asset (if provided by backend)
7031
+ marketPrefix: asset.marketPrefix,
7032
+ collateralToken: asset.collateralToken,
6545
7033
  };
6546
7034
  };
6547
7035
  const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
@@ -6604,57 +7092,151 @@ const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
6604
7092
  });
6605
7093
  };
6606
7094
 
7095
+ function findAssetMeta$3(coinName, perpMetaAssets, knownPrefix, desiredCollateral) {
7096
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
7097
+ if (!perpMetaAssets) {
7098
+ return { collateralToken: 'USDC', marketPrefix: null };
7099
+ }
7100
+ if (desiredCollateral) {
7101
+ const collateralMatch = perpMetaAssets.find((a) => a.name === coinName && a.collateralToken === desiredCollateral);
7102
+ if (collateralMatch) {
7103
+ return {
7104
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7105
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7106
+ };
7107
+ }
7108
+ }
7109
+ if (coinName.includes(':')) {
7110
+ const [prefix, symbol] = coinName.split(':');
7111
+ const exactMatch = perpMetaAssets.find((a) => {
7112
+ var _a;
7113
+ return a.name === symbol &&
7114
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
7115
+ });
7116
+ if (exactMatch) {
7117
+ return {
7118
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7119
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7120
+ };
7121
+ }
7122
+ }
7123
+ if (knownPrefix) {
7124
+ const exactMatch = perpMetaAssets.find((a) => {
7125
+ var _a;
7126
+ return a.name === coinName &&
7127
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
7128
+ });
7129
+ if (exactMatch) {
7130
+ return {
7131
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
7132
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
7133
+ };
7134
+ }
7135
+ }
7136
+ const regularAsset = perpMetaAssets.find((a) => a.name === coinName && !a.marketPrefix);
7137
+ if (regularAsset) {
7138
+ return {
7139
+ collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
7140
+ marketPrefix: null,
7141
+ };
7142
+ }
7143
+ const hip3Asset = perpMetaAssets.find((a) => a.name === coinName && a.marketPrefix);
7144
+ if (hip3Asset) {
7145
+ return {
7146
+ collateralToken: (_h = hip3Asset.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
7147
+ marketPrefix: (_j = hip3Asset.marketPrefix) !== null && _j !== void 0 ? _j : null,
7148
+ };
7149
+ }
7150
+ return { collateralToken: 'USDC', marketPrefix: null };
7151
+ }
7152
+ function enrichPositionAssets(assets, perpMetaAssets) {
7153
+ return assets.map((asset) => {
7154
+ var _a;
7155
+ if (asset.marketPrefix && asset.collateralToken) {
7156
+ return asset;
7157
+ }
7158
+ const meta = findAssetMeta$3(asset.coin, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
7159
+ return {
7160
+ ...asset,
7161
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
7162
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
7163
+ };
7164
+ });
7165
+ }
7166
+ function enrichPositions(positions, perpMetaAssets) {
7167
+ return positions.map((position) => ({
7168
+ ...position,
7169
+ longAssets: enrichPositionAssets(position.longAssets, perpMetaAssets),
7170
+ shortAssets: enrichPositionAssets(position.shortAssets, perpMetaAssets),
7171
+ }));
7172
+ }
6607
7173
  function usePosition() {
6608
7174
  const context = useContext(PearHyperliquidContext);
6609
7175
  if (!context) {
6610
7176
  throw new Error('usePosition must be used within a PearHyperliquidProvider');
6611
7177
  }
6612
7178
  const { apiBaseUrl, isConnected } = context;
6613
- const displayToFull = useHyperliquidData((s) => s.hip3DisplayToFull);
6614
- // Create position API action
7179
+ const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
6615
7180
  const createPosition$1 = async (payload) => {
6616
- return createPosition(apiBaseUrl, payload, displayToFull);
7181
+ return createPosition(apiBaseUrl, payload, hip3Assets);
6617
7182
  };
6618
- // Update TP/SL risk parameters for a position
6619
7183
  const updateRiskParameters$1 = async (positionId, payload) => {
6620
7184
  return updateRiskParameters(apiBaseUrl, positionId, payload);
6621
7185
  };
6622
- // Close a position (MARKET or TWAP)
6623
7186
  const closePosition$1 = async (positionId, payload) => {
6624
7187
  return closePosition(apiBaseUrl, positionId, payload);
6625
7188
  };
6626
- // Close all positions (MARKET or TWAP)
6627
7189
  const closeAllPositions$1 = async (payload) => {
6628
7190
  return closeAllPositions(apiBaseUrl, payload);
6629
7191
  };
6630
- // Adjust a position (REDUCE/INCREASE by %; MARKET or LIMIT)
6631
7192
  const adjustPosition$1 = async (positionId, payload) => {
6632
7193
  return adjustPosition(apiBaseUrl, positionId, payload);
6633
7194
  };
6634
- // Adjust to absolute target sizes per asset, optionally adding new assets
6635
7195
  const adjustAdvancePosition$1 = async (positionId, payload) => {
6636
- return adjustAdvancePosition(apiBaseUrl, positionId, payload, displayToFull);
7196
+ return adjustAdvancePosition(apiBaseUrl, positionId, payload, hip3Assets);
6637
7197
  };
6638
- // Open positions using WS data, with derived values
6639
7198
  const userOpenPositions = useUserData((state) => state.rawOpenPositions);
6640
7199
  const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
6641
7200
  const allMids = useHyperliquidData((state) => state.allMids);
7201
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
6642
7202
  const isLoading = useMemo(() => {
6643
7203
  return userOpenPositions === null && isConnected;
6644
7204
  }, [userOpenPositions, isConnected]);
6645
7205
  const openPositions = useMemo(() => {
6646
7206
  if (!userOpenPositions || !aggregatedClearingHouseState || !allMids)
6647
7207
  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 };
7208
+ const positions = buildPositionValue(userOpenPositions, aggregatedClearingHouseState, allMids);
7209
+ return enrichPositions(positions, allPerpMetaAssets);
7210
+ }, [
7211
+ userOpenPositions,
7212
+ aggregatedClearingHouseState,
7213
+ allMids,
7214
+ allPerpMetaAssets,
7215
+ ]);
7216
+ return {
7217
+ createPosition: createPosition$1,
7218
+ updateRiskParameters: updateRiskParameters$1,
7219
+ closePosition: closePosition$1,
7220
+ closeAllPositions: closeAllPositions$1,
7221
+ adjustPosition: adjustPosition$1,
7222
+ adjustAdvancePosition: adjustAdvancePosition$1,
7223
+ openPositions,
7224
+ isLoading,
7225
+ };
6651
7226
  }
6652
7227
 
6653
7228
  async function adjustOrder(baseUrl, orderId, payload) {
6654
7229
  const url = joinUrl(baseUrl, `/orders/${orderId}/adjust`);
6655
7230
  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 };
7231
+ const resp = await apiClient.put(url, payload, {
7232
+ headers: { 'Content-Type': 'application/json' },
7233
+ timeout: 60000,
7234
+ });
7235
+ return {
7236
+ data: resp.data,
7237
+ status: resp.status,
7238
+ headers: resp.headers,
7239
+ };
6658
7240
  }
6659
7241
  catch (error) {
6660
7242
  throw toApiError(error);
@@ -6663,8 +7245,14 @@ async function adjustOrder(baseUrl, orderId, payload) {
6663
7245
  async function cancelOrder(baseUrl, orderId) {
6664
7246
  const url = joinUrl(baseUrl, `/orders/${orderId}/cancel`);
6665
7247
  try {
6666
- const resp = await apiClient.delete(url, { timeout: 60000 });
6667
- return { data: resp.data, status: resp.status, headers: resp.headers };
7248
+ const resp = await apiClient.delete(url, {
7249
+ timeout: 60000,
7250
+ });
7251
+ return {
7252
+ data: resp.data,
7253
+ status: resp.status,
7254
+ headers: resp.headers,
7255
+ };
6668
7256
  }
6669
7257
  catch (error) {
6670
7258
  throw toApiError(error);
@@ -6674,19 +7262,129 @@ async function cancelTwapOrder(baseUrl, orderId) {
6674
7262
  const url = joinUrl(baseUrl, `/orders/${orderId}/twap/cancel`);
6675
7263
  try {
6676
7264
  const resp = await apiClient.post(url, {}, { headers: { 'Content-Type': 'application/json' }, timeout: 60000 });
6677
- return { data: resp.data, status: resp.status, headers: resp.headers };
7265
+ return {
7266
+ data: resp.data,
7267
+ status: resp.status,
7268
+ headers: resp.headers,
7269
+ };
7270
+ }
7271
+ catch (error) {
7272
+ throw toApiError(error);
7273
+ }
7274
+ }
7275
+ /**
7276
+ * Execute a spot order (swap) using Pear Hyperliquid service
7277
+ * POST /orders/spot
7278
+ */
7279
+ async function executeSpotOrder(baseUrl, payload) {
7280
+ const url = joinUrl(baseUrl, '/orders/spot');
7281
+ try {
7282
+ const resp = await apiClient.post(url, payload, {
7283
+ headers: { 'Content-Type': 'application/json' },
7284
+ timeout: 60000,
7285
+ });
7286
+ return {
7287
+ data: resp.data,
7288
+ status: resp.status,
7289
+ headers: resp.headers,
7290
+ };
6678
7291
  }
6679
7292
  catch (error) {
6680
7293
  throw toApiError(error);
6681
7294
  }
6682
7295
  }
6683
7296
 
7297
+ function findAssetMeta$2(assetName, perpMetaAssets, knownPrefix, desiredCollateral) {
7298
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
7299
+ if (!perpMetaAssets) {
7300
+ return { collateralToken: 'USDC', marketPrefix: null };
7301
+ }
7302
+ if (desiredCollateral) {
7303
+ const collateralMatch = perpMetaAssets.find((a) => a.name === assetName && a.collateralToken === desiredCollateral);
7304
+ if (collateralMatch) {
7305
+ return {
7306
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7307
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7308
+ };
7309
+ }
7310
+ }
7311
+ if (assetName.includes(':')) {
7312
+ const [prefix, symbol] = assetName.split(':');
7313
+ const exactMatch = perpMetaAssets.find((a) => {
7314
+ var _a;
7315
+ return a.name === symbol &&
7316
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
7317
+ });
7318
+ if (exactMatch) {
7319
+ return {
7320
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7321
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7322
+ };
7323
+ }
7324
+ }
7325
+ if (knownPrefix) {
7326
+ const exactMatch = perpMetaAssets.find((a) => {
7327
+ var _a;
7328
+ return a.name === assetName &&
7329
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
7330
+ });
7331
+ if (exactMatch) {
7332
+ return {
7333
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
7334
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
7335
+ };
7336
+ }
7337
+ }
7338
+ const regularAsset = perpMetaAssets.find((a) => a.name === assetName && !a.marketPrefix);
7339
+ if (regularAsset) {
7340
+ return {
7341
+ collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
7342
+ marketPrefix: null,
7343
+ };
7344
+ }
7345
+ const hip3Assets = perpMetaAssets.filter((a) => a.name === assetName && a.marketPrefix);
7346
+ if (hip3Assets.length > 0) {
7347
+ if (desiredCollateral) {
7348
+ const collateralMatch = hip3Assets.find((a) => a.collateralToken === desiredCollateral);
7349
+ if (collateralMatch) {
7350
+ return {
7351
+ collateralToken: (_h = collateralMatch.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
7352
+ marketPrefix: (_j = collateralMatch.marketPrefix) !== null && _j !== void 0 ? _j : null,
7353
+ };
7354
+ }
7355
+ }
7356
+ const usdHMatch = hip3Assets.find((a) => a.collateralToken === 'USDH');
7357
+ const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Assets[0];
7358
+ return {
7359
+ collateralToken: (_k = chosen.collateralToken) !== null && _k !== void 0 ? _k : 'USDC',
7360
+ marketPrefix: (_l = chosen.marketPrefix) !== null && _l !== void 0 ? _l : null,
7361
+ };
7362
+ }
7363
+ return { collateralToken: 'USDC', marketPrefix: null };
7364
+ }
7365
+ function enrichOrderAssets$1(assets, perpMetaAssets) {
7366
+ if (!assets)
7367
+ return [];
7368
+ return assets.map((asset) => {
7369
+ var _a;
7370
+ if (asset.marketPrefix && asset.collateralToken) {
7371
+ return asset;
7372
+ }
7373
+ const meta = findAssetMeta$2(asset.asset, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
7374
+ return {
7375
+ ...asset,
7376
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
7377
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
7378
+ };
7379
+ });
7380
+ }
6684
7381
  function useOrders() {
6685
7382
  const context = useContext(PearHyperliquidContext);
6686
7383
  if (!context)
6687
7384
  throw new Error('useOrders must be used within a PearHyperliquidProvider');
6688
7385
  const { apiBaseUrl } = context;
6689
7386
  const openOrders = useUserData((state) => state.openOrders);
7387
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
6690
7388
  const isLoading = useMemo(() => openOrders === null && context.isConnected, [openOrders, context.isConnected]);
6691
7389
  const { openPositions } = usePosition();
6692
7390
  const positionsById = useMemo(() => {
@@ -6705,19 +7403,27 @@ function useOrders() {
6705
7403
  const isTpSl = ord.orderType === 'TP' || ord.orderType === 'SL';
6706
7404
  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
7405
  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,
7406
+ let enrichedOrd = {
7407
+ ...ord,
7408
+ longAssets: enrichOrderAssets$1(ord.longAssets, allPerpMetaAssets),
7409
+ shortAssets: enrichOrderAssets$1(ord.shortAssets, allPerpMetaAssets),
7410
+ };
7411
+ if (isTpSl && !hasAssets && pos) {
7412
+ const mapAssets = (arr) => arr.map((a) => ({
7413
+ asset: a.coin,
7414
+ weight: a.initialWeight,
7415
+ marketPrefix: a.marketPrefix,
7416
+ collateralToken: a.collateralToken,
7417
+ }));
7418
+ enrichedOrd = {
7419
+ ...enrichedOrd,
6714
7420
  longAssets: mapAssets(pos.longAssets),
6715
7421
  shortAssets: mapAssets(pos.shortAssets),
6716
7422
  };
6717
7423
  }
6718
- return ord;
7424
+ return enrichedOrd;
6719
7425
  });
6720
- }, [openOrders, positionsById]);
7426
+ }, [openOrders, positionsById, allPerpMetaAssets]);
6721
7427
  const adjustOrder$1 = async (orderId, payload) => {
6722
7428
  return adjustOrder(apiBaseUrl, orderId, payload);
6723
7429
  };
@@ -6727,16 +7433,156 @@ function useOrders() {
6727
7433
  const cancelTwapOrder$1 = async (orderId) => {
6728
7434
  return cancelTwapOrder(apiBaseUrl, orderId);
6729
7435
  };
6730
- return { adjustOrder: adjustOrder$1, cancelOrder: cancelOrder$1, cancelTwapOrder: cancelTwapOrder$1, openOrders: enrichedOpenOrders, isLoading };
7436
+ return {
7437
+ adjustOrder: adjustOrder$1,
7438
+ cancelOrder: cancelOrder$1,
7439
+ cancelTwapOrder: cancelTwapOrder$1,
7440
+ openOrders: enrichedOpenOrders,
7441
+ isLoading,
7442
+ };
6731
7443
  }
6732
7444
 
7445
+ /**
7446
+ * Hook for executing spot orders (swaps) on Hyperliquid
7447
+ * Use this to swap between USDC and USDH or other spot assets
7448
+ */
7449
+ function useSpotOrder() {
7450
+ const context = useContext(PearHyperliquidContext);
7451
+ if (!context) {
7452
+ throw new Error('useSpotOrder must be used within a PearHyperliquidProvider');
7453
+ }
7454
+ const { apiBaseUrl } = context;
7455
+ const [isLoading, setIsLoading] = useState(false);
7456
+ const [error, setError] = useState(null);
7457
+ const resetError = useCallback(() => {
7458
+ setError(null);
7459
+ }, []);
7460
+ const executeSpotOrder$1 = useCallback(async (payload) => {
7461
+ setIsLoading(true);
7462
+ setError(null);
7463
+ try {
7464
+ const response = await executeSpotOrder(apiBaseUrl, payload);
7465
+ return response;
7466
+ }
7467
+ catch (err) {
7468
+ const apiError = err;
7469
+ setError(apiError);
7470
+ throw apiError;
7471
+ }
7472
+ finally {
7473
+ setIsLoading(false);
7474
+ }
7475
+ }, [apiBaseUrl]);
7476
+ return {
7477
+ executeSpotOrder: executeSpotOrder$1,
7478
+ isLoading,
7479
+ error,
7480
+ resetError,
7481
+ };
7482
+ }
7483
+
7484
+ function findAssetMeta$1(assetName, perpMetaAssets, knownPrefix, desiredCollateral) {
7485
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
7486
+ if (!perpMetaAssets) {
7487
+ return { collateralToken: 'USDC', marketPrefix: null };
7488
+ }
7489
+ if (desiredCollateral) {
7490
+ const collateralMatch = perpMetaAssets.find((a) => a.name === assetName && a.collateralToken === desiredCollateral);
7491
+ if (collateralMatch) {
7492
+ return {
7493
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7494
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7495
+ };
7496
+ }
7497
+ }
7498
+ if (assetName.includes(':')) {
7499
+ const [prefix, symbol] = assetName.split(':');
7500
+ const exactMatch = perpMetaAssets.find((a) => {
7501
+ var _a;
7502
+ return a.name === symbol &&
7503
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
7504
+ });
7505
+ if (exactMatch) {
7506
+ return {
7507
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7508
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7509
+ };
7510
+ }
7511
+ }
7512
+ if (knownPrefix) {
7513
+ const exactMatch = perpMetaAssets.find((a) => {
7514
+ var _a;
7515
+ return a.name === assetName &&
7516
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
7517
+ });
7518
+ if (exactMatch) {
7519
+ return {
7520
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
7521
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
7522
+ };
7523
+ }
7524
+ }
7525
+ const regularAsset = perpMetaAssets.find((a) => a.name === assetName && !a.marketPrefix);
7526
+ if (regularAsset) {
7527
+ return {
7528
+ collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
7529
+ marketPrefix: null,
7530
+ };
7531
+ }
7532
+ const hip3Assets = perpMetaAssets.filter((a) => a.name === assetName && a.marketPrefix);
7533
+ if (hip3Assets.length > 0) {
7534
+ if (desiredCollateral) {
7535
+ const collateralMatch = hip3Assets.find((a) => a.collateralToken === desiredCollateral);
7536
+ if (collateralMatch) {
7537
+ return {
7538
+ collateralToken: (_h = collateralMatch.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
7539
+ marketPrefix: (_j = collateralMatch.marketPrefix) !== null && _j !== void 0 ? _j : null,
7540
+ };
7541
+ }
7542
+ }
7543
+ const usdHMatch = hip3Assets.find((a) => a.collateralToken === 'USDH');
7544
+ const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Assets[0];
7545
+ return {
7546
+ collateralToken: (_k = chosen.collateralToken) !== null && _k !== void 0 ? _k : 'USDC',
7547
+ marketPrefix: (_l = chosen.marketPrefix) !== null && _l !== void 0 ? _l : null,
7548
+ };
7549
+ }
7550
+ return { collateralToken: 'USDC', marketPrefix: null };
7551
+ }
7552
+ function enrichOrderAssets(assets, perpMetaAssets) {
7553
+ if (!assets)
7554
+ return [];
7555
+ return assets.map((asset) => {
7556
+ var _a;
7557
+ if (asset.marketPrefix && asset.collateralToken) {
7558
+ return asset;
7559
+ }
7560
+ const meta = findAssetMeta$1(asset.asset, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
7561
+ return {
7562
+ ...asset,
7563
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
7564
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
7565
+ };
7566
+ });
7567
+ }
7568
+ function enrichTwapOrders(orders, perpMetaAssets) {
7569
+ return orders.map((order) => ({
7570
+ ...order,
7571
+ longAssets: enrichOrderAssets(order.longAssets, perpMetaAssets),
7572
+ shortAssets: enrichOrderAssets(order.shortAssets, perpMetaAssets),
7573
+ }));
7574
+ }
6733
7575
  function useTwap() {
6734
- const twapDetails = useUserData(state => state.twapDetails);
7576
+ const twapDetails = useUserData((state) => state.twapDetails);
7577
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
6735
7578
  const context = useContext(PearHyperliquidContext);
6736
7579
  if (!context)
6737
7580
  throw new Error('useTwap must be used within a PearHyperliquidProvider');
6738
7581
  const { apiBaseUrl } = context;
6739
- const orders = useMemo(() => twapDetails !== null && twapDetails !== void 0 ? twapDetails : [], [twapDetails]);
7582
+ const orders = useMemo(() => {
7583
+ const rawOrders = twapDetails !== null && twapDetails !== void 0 ? twapDetails : [];
7584
+ return enrichTwapOrders(rawOrders, allPerpMetaAssets);
7585
+ }, [twapDetails, allPerpMetaAssets]);
6740
7586
  const cancelTwap$1 = async (orderId) => {
6741
7587
  return cancelTwap(apiBaseUrl, orderId);
6742
7588
  };
@@ -6819,59 +7665,170 @@ function useNotifications() {
6819
7665
  };
6820
7666
  }
6821
7667
 
6822
- // Base selector for the full market-data payload
7668
+ // Helper to find asset metadata from perpMetaAssets
7669
+ function findAssetMeta(assetName, perpMetaAssets) {
7670
+ var _a, _b, _c, _d;
7671
+ if (!perpMetaAssets) {
7672
+ return { collateralToken: 'USDC', marketPrefix: null };
7673
+ }
7674
+ // Try exact match first (for prefixed assets like "xyz:TSLA")
7675
+ const exactMatch = perpMetaAssets.find((a) => a.name === assetName);
7676
+ if (exactMatch) {
7677
+ return {
7678
+ collateralToken: (_a = exactMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7679
+ marketPrefix: (_b = exactMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7680
+ };
7681
+ }
7682
+ // Try matching by base symbol (for non-prefixed names in data)
7683
+ const baseMatch = perpMetaAssets.find((a) => {
7684
+ const baseName = a.name.includes(':') ? a.name.split(':')[1] : a.name;
7685
+ return baseName === assetName;
7686
+ });
7687
+ if (baseMatch) {
7688
+ return {
7689
+ collateralToken: (_c = baseMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7690
+ marketPrefix: (_d = baseMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7691
+ };
7692
+ }
7693
+ return { collateralToken: 'USDC', marketPrefix: null };
7694
+ }
7695
+ // Enrich a single asset with metadata
7696
+ function enrichAsset(asset, perpMetaAssets) {
7697
+ const meta = findAssetMeta(asset.asset, perpMetaAssets);
7698
+ return {
7699
+ ...asset,
7700
+ collateralToken: meta.collateralToken,
7701
+ marketPrefix: meta.marketPrefix,
7702
+ };
7703
+ }
7704
+ // Enrich a basket item with collateral info
7705
+ function enrichBasketItem(item, perpMetaAssets) {
7706
+ const enrichedLongs = item.longAssets.map((a) => enrichAsset(a, perpMetaAssets));
7707
+ const enrichedShorts = item.shortAssets.map((a) => enrichAsset(a, perpMetaAssets));
7708
+ // Determine collateral type
7709
+ const allAssets = [...enrichedLongs, ...enrichedShorts];
7710
+ const hasUsdc = allAssets.some((a) => a.collateralToken === 'USDC');
7711
+ const hasUsdh = allAssets.some((a) => a.collateralToken === 'USDH');
7712
+ let collateralType = 'USDC';
7713
+ if (hasUsdc && hasUsdh) {
7714
+ collateralType = 'MIXED';
7715
+ }
7716
+ else if (hasUsdh) {
7717
+ collateralType = 'USDH';
7718
+ }
7719
+ return {
7720
+ ...item,
7721
+ longAssets: enrichedLongs,
7722
+ shortAssets: enrichedShorts,
7723
+ collateralType,
7724
+ };
7725
+ }
7726
+ /**
7727
+ * Filter baskets by collateral type
7728
+ * - 'USDC': Only baskets where ALL assets use USDC (collateralType === 'USDC')
7729
+ * - 'USDH': Only baskets where ALL assets use USDH (collateralType === 'USDH')
7730
+ * - 'ALL' or undefined: No filtering, returns all baskets
7731
+ */
7732
+ function filterByCollateral(baskets, filter) {
7733
+ if (!filter || filter === 'ALL') {
7734
+ return baskets;
7735
+ }
7736
+ return baskets.filter((basket) => {
7737
+ if (filter === 'USDC') {
7738
+ // Include baskets that are purely USDC or have USDC assets
7739
+ return (basket.collateralType === 'USDC' || basket.collateralType === 'MIXED');
7740
+ }
7741
+ if (filter === 'USDH') {
7742
+ // Include baskets that are purely USDH or have USDH assets
7743
+ return (basket.collateralType === 'USDH' || basket.collateralType === 'MIXED');
7744
+ }
7745
+ return true;
7746
+ });
7747
+ }
7748
+ // Base selector for the full market-data payload (raw from WS)
6823
7749
  const useMarketDataPayload = () => {
6824
7750
  return useMarketData((s) => s.marketData);
6825
7751
  };
6826
- // Full payload for 'market-data-all' channel
7752
+ // Full payload for 'market-data-all' channel (raw from WS)
6827
7753
  const useMarketDataAllPayload = () => {
6828
7754
  return useMarketData((s) => s.marketDataAll);
6829
7755
  };
6830
- // Active baskets
6831
- const useActiveBaskets = () => {
6832
- var _a;
7756
+ // Access perpMetaAssets for enrichment
7757
+ const usePerpMetaAssets = () => {
7758
+ return useHyperliquidData((s) => s.perpMetaAssets);
7759
+ };
7760
+ // Active baskets (with collateral and market prefix info)
7761
+ const useActiveBaskets = (collateralFilter) => {
6833
7762
  const data = useMarketDataPayload();
6834
- return (_a = data === null || data === void 0 ? void 0 : data.active) !== null && _a !== void 0 ? _a : [];
7763
+ const perpMetaAssets = usePerpMetaAssets();
7764
+ return useMemo(() => {
7765
+ if (!(data === null || data === void 0 ? void 0 : data.active))
7766
+ return [];
7767
+ const enriched = data.active.map((item) => enrichBasketItem(item, perpMetaAssets));
7768
+ return filterByCollateral(enriched, collateralFilter);
7769
+ }, [data, perpMetaAssets, collateralFilter]);
6835
7770
  };
6836
- // Top gainers (optional limit override)
6837
- const useTopGainers = (limit) => {
7771
+ // Top gainers (with collateral and market prefix info)
7772
+ const useTopGainers = (limit, collateralFilter) => {
6838
7773
  const data = useMarketDataPayload();
7774
+ const perpMetaAssets = usePerpMetaAssets();
6839
7775
  return useMemo(() => {
6840
7776
  var _a;
6841
7777
  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]);
7778
+ const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7779
+ const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
7780
+ return filterByCollateral(enriched, collateralFilter);
7781
+ }, [data, perpMetaAssets, limit, collateralFilter]);
6844
7782
  };
6845
- // Top losers (optional limit override)
6846
- const useTopLosers = (limit) => {
7783
+ // Top losers (with collateral and market prefix info)
7784
+ const useTopLosers = (limit, collateralFilter) => {
6847
7785
  const data = useMarketDataPayload();
7786
+ const perpMetaAssets = usePerpMetaAssets();
6848
7787
  return useMemo(() => {
6849
7788
  var _a;
6850
7789
  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]);
7790
+ const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7791
+ const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
7792
+ return filterByCollateral(enriched, collateralFilter);
7793
+ }, [data, perpMetaAssets, limit, collateralFilter]);
6853
7794
  };
6854
- // Highlighted baskets
6855
- const useHighlightedBaskets = () => {
6856
- var _a;
7795
+ // Highlighted baskets (with collateral and market prefix info)
7796
+ const useHighlightedBaskets = (collateralFilter) => {
6857
7797
  const data = useMarketDataPayload();
6858
- return (_a = data === null || data === void 0 ? void 0 : data.highlighted) !== null && _a !== void 0 ? _a : [];
7798
+ const perpMetaAssets = usePerpMetaAssets();
7799
+ return useMemo(() => {
7800
+ if (!(data === null || data === void 0 ? void 0 : data.highlighted))
7801
+ return [];
7802
+ const enriched = data.highlighted.map((item) => enrichBasketItem(item, perpMetaAssets));
7803
+ return filterByCollateral(enriched, collateralFilter);
7804
+ }, [data, perpMetaAssets, collateralFilter]);
6859
7805
  };
6860
- // Watchlist baskets (from market-data payload when subscribed with address)
6861
- const useWatchlistBaskets = () => {
6862
- var _a;
7806
+ // Watchlist baskets (with collateral and market prefix info)
7807
+ const useWatchlistBaskets = (collateralFilter) => {
6863
7808
  const data = useMarketDataPayload();
6864
- return (_a = data === null || data === void 0 ? void 0 : data.watchlist) !== null && _a !== void 0 ? _a : [];
7809
+ const perpMetaAssets = usePerpMetaAssets();
7810
+ return useMemo(() => {
7811
+ if (!(data === null || data === void 0 ? void 0 : data.watchlist))
7812
+ return [];
7813
+ const enriched = data.watchlist.map((item) => enrichBasketItem(item, perpMetaAssets));
7814
+ return filterByCollateral(enriched, collateralFilter);
7815
+ }, [data, perpMetaAssets, collateralFilter]);
6865
7816
  };
6866
- // All baskets (from market-data-all)
6867
- const useAllBaskets = () => {
6868
- var _a;
7817
+ // All baskets (with collateral and market prefix info)
7818
+ const useAllBaskets = (collateralFilter) => {
6869
7819
  const dataAll = useMarketDataAllPayload();
6870
- return (_a = dataAll === null || dataAll === void 0 ? void 0 : dataAll.all) !== null && _a !== void 0 ? _a : [];
7820
+ const perpMetaAssets = usePerpMetaAssets();
7821
+ return useMemo(() => {
7822
+ if (!(dataAll === null || dataAll === void 0 ? void 0 : dataAll.all))
7823
+ return [];
7824
+ const enriched = dataAll.all.map((item) => enrichBasketItem(item, perpMetaAssets));
7825
+ return filterByCollateral(enriched, collateralFilter);
7826
+ }, [dataAll, perpMetaAssets, collateralFilter]);
6871
7827
  };
6872
7828
  // Find a basket by its exact asset composition (order-insensitive)
6873
7829
  const useFindBasket = (longs, shorts) => {
6874
7830
  const data = useMarketDataPayload();
7831
+ const perpMetaAssets = usePerpMetaAssets();
6875
7832
  return useMemo(() => {
6876
7833
  if (!data)
6877
7834
  return undefined;
@@ -6885,17 +7842,28 @@ const useFindBasket = (longs, shorts) => {
6885
7842
  : '';
6886
7843
  const lKey = normalize(longs);
6887
7844
  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]);
7845
+ const match = (item) => normalize(item.longAssets) === lKey &&
7846
+ normalize(item.shortAssets) === sKey;
7847
+ const found = data.active.find(match) || data.highlighted.find(match);
7848
+ return found
7849
+ ? enrichBasketItem(found, perpMetaAssets)
7850
+ : undefined;
7851
+ }, [data, longs, shorts, perpMetaAssets]);
6891
7852
  };
6892
7853
 
6893
- async function toggleWatchlist(baseUrl, longAssets, shortAssets, displayToFull) {
7854
+ async function toggleWatchlist(baseUrl, longAssets, shortAssets, hip3Assets) {
6894
7855
  const url = joinUrl(baseUrl, '/watchlist');
6895
- const mapAssets = (arr) => arr.map(a => ({ ...a, asset: toBackendSymbol(a.asset, displayToFull) }));
7856
+ const mapAssets = (arr) => arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6896
7857
  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 };
7858
+ const response = await apiClient.post(url, {
7859
+ longAssets: mapAssets(longAssets),
7860
+ shortAssets: mapAssets(shortAssets),
7861
+ }, { headers: { 'Content-Type': 'application/json' } });
7862
+ return {
7863
+ data: response.data,
7864
+ status: response.status,
7865
+ headers: response.headers,
7866
+ };
6899
7867
  }
6900
7868
  catch (error) {
6901
7869
  throw toApiError(error);
@@ -6907,11 +7875,11 @@ function useWatchlist() {
6907
7875
  if (!context)
6908
7876
  throw new Error('useWatchlist must be used within a PearHyperliquidProvider');
6909
7877
  const { apiBaseUrl, isConnected } = context;
6910
- const displayToFull = useHyperliquidData((s) => s.hip3DisplayToFull);
7878
+ const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
6911
7879
  const marketData = useMarketDataPayload();
6912
7880
  const isLoading = useMemo(() => !marketData && isConnected, [marketData, isConnected]);
6913
7881
  const toggle = async (longAssets, shortAssets) => {
6914
- const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, displayToFull);
7882
+ const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, hip3Assets);
6915
7883
  // Server will push updated market-data over WS; nothing to set here
6916
7884
  return resp;
6917
7885
  };
@@ -7165,16 +8133,48 @@ function useAuth() {
7165
8133
  };
7166
8134
  }
7167
8135
 
8136
+ const useSpotBalances = () => {
8137
+ const spotState = useUserData((state) => state.spotState);
8138
+ return useMemo(() => {
8139
+ if (!spotState) {
8140
+ return {
8141
+ usdhBalance: undefined,
8142
+ spotUsdcBalance: undefined,
8143
+ isLoading: true,
8144
+ };
8145
+ }
8146
+ const balances = spotState.balances || [];
8147
+ let usdhBal = 0;
8148
+ let spotUsdcBal = 0;
8149
+ for (const balance of balances) {
8150
+ const total = parseFloat(balance.total || '0');
8151
+ if (balance.coin === 'USDH') {
8152
+ usdhBal = total;
8153
+ }
8154
+ if (balance.coin === 'USDC') {
8155
+ spotUsdcBal = total;
8156
+ }
8157
+ }
8158
+ return {
8159
+ usdhBalance: usdhBal,
8160
+ spotUsdcBalance: spotUsdcBal,
8161
+ isLoading: false,
8162
+ };
8163
+ }, [spotState]);
8164
+ };
8165
+
7168
8166
  const PearHyperliquidContext = createContext(undefined);
7169
8167
  /**
7170
8168
  * React Provider for PearHyperliquidClient
7171
8169
  */
7172
- const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-v2.pearprotocol.io", clientId = "PEARPROTOCOLUI", wsUrl = "wss://hl-v2.pearprotocol.io/ws", }) => {
8170
+ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearprotocol.io', clientId = 'PEARPROTOCOLUI', wsUrl = 'wss://hl-ui.pearprotocol.io/ws', }) => {
7173
8171
  const address = useUserData((s) => s.address);
7174
8172
  const setAddress = useUserData((s) => s.setAddress);
7175
8173
  const perpsMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
7176
8174
  const setPerpMetaAssets = useHyperliquidData((state) => state.setPerpMetaAssets);
7177
- const setHip3DisplayToFull = useHyperliquidData((state) => state.setHip3DisplayToFull);
8175
+ const setAllPerpMetaAssets = useHyperliquidData((state) => state.setAllPerpMetaAssets);
8176
+ const setHip3Assets = useHyperliquidData((state) => state.setHip3Assets);
8177
+ const setHip3MarketPrefixes = useHyperliquidData((state) => state.setHip3MarketPrefixes);
7178
8178
  const websocketsEnabled = useMemo(() => Array.isArray(perpsMetaAssets) && perpsMetaAssets.length > 0, [perpsMetaAssets]);
7179
8179
  const { isConnected, lastError } = useHyperliquidWebSocket({
7180
8180
  wsUrl,
@@ -7189,32 +8189,65 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-v2.pearpro
7189
8189
  if (perpsMetaAssets === null) {
7190
8190
  fetchAllPerpMetas()
7191
8191
  .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;
8192
+ const assetToMarkets = new Map();
8193
+ const marketPrefixes = new Map();
8194
+ const cleanedPerpMetas = [];
8195
+ const allPerpMetas = [];
8196
+ const FILTERED_PREFIXES = ['vntl', 'hyna'];
8197
+ res.data.forEach((item) => {
8198
+ const collateralToken = item.collateralToken === 360 ? 'USDH' : 'USDC';
8199
+ item.universe.forEach((asset) => {
8200
+ var _a;
8201
+ const [maybePrefix, maybeMarket] = asset.name.split(':');
8202
+ if (maybeMarket) {
8203
+ const prefix = maybePrefix.toLowerCase();
8204
+ const displayName = maybeMarket;
8205
+ const fullName = `${prefix}:${displayName}`;
8206
+ marketPrefixes.set(fullName, prefix);
8207
+ if (!FILTERED_PREFIXES.includes(prefix)) {
8208
+ const existingMarkets = (_a = assetToMarkets.get(displayName)) !== null && _a !== void 0 ? _a : [];
8209
+ if (!existingMarkets.includes(fullName)) {
8210
+ assetToMarkets.set(displayName, [
8211
+ ...existingMarkets,
8212
+ fullName,
8213
+ ]);
8214
+ }
8215
+ }
8216
+ const assetWithMeta = {
8217
+ ...asset,
8218
+ name: displayName,
8219
+ marketPrefix: prefix,
8220
+ collateralToken,
8221
+ };
8222
+ allPerpMetas.push(assetWithMeta);
8223
+ if (!FILTERED_PREFIXES.includes(prefix)) {
8224
+ cleanedPerpMetas.push(assetWithMeta);
8225
+ }
8226
+ }
8227
+ else {
8228
+ const assetWithMeta = {
8229
+ ...asset,
8230
+ collateralToken,
8231
+ };
8232
+ cleanedPerpMetas.push(assetWithMeta);
8233
+ allPerpMetas.push(assetWithMeta);
8234
+ }
8235
+ });
7210
8236
  });
7211
- setHip3DisplayToFull(displayToFull);
8237
+ setHip3Assets(assetToMarkets);
8238
+ setHip3MarketPrefixes(marketPrefixes);
7212
8239
  setPerpMetaAssets(cleanedPerpMetas);
8240
+ setAllPerpMetaAssets(allPerpMetas);
7213
8241
  })
7214
8242
  .catch(() => { });
7215
8243
  }
7216
- }, [perpsMetaAssets, setPerpMetaAssets, setHip3DisplayToFull]);
7217
- // Auth methods now sourced from useAuth hook
8244
+ }, [
8245
+ perpsMetaAssets,
8246
+ setPerpMetaAssets,
8247
+ setAllPerpMetaAssets,
8248
+ setHip3Assets,
8249
+ setHip3MarketPrefixes,
8250
+ ]);
7218
8251
  useAutoSyncFills({
7219
8252
  baseUrl: apiBaseUrl,
7220
8253
  address,
@@ -7252,7 +8285,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-v2.pearpro
7252
8285
  function usePearHyperliquid() {
7253
8286
  const ctx = useContext(PearHyperliquidContext);
7254
8287
  if (!ctx)
7255
- throw new Error("usePearHyperliquid must be used within a PearHyperliquidProvider");
8288
+ throw new Error('usePearHyperliquid must be used within a PearHyperliquidProvider');
7256
8289
  return ctx;
7257
8290
  }
7258
8291
 
@@ -7343,4 +8376,4 @@ function mapCandleIntervalToTradingViewInterval(interval) {
7343
8376
  }
7344
8377
  }
7345
8378
 
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 };
8379
+ 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 };