@pear-protocol/hyperliquid-sdk 0.0.73-beta.1 → 0.0.73

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
@@ -55,7 +55,6 @@ const useUserData = create((set) => ({
55
55
  accessToken: null,
56
56
  refreshToken: null,
57
57
  isAuthenticated: false,
58
- isReady: false,
59
58
  address: null,
60
59
  tradeHistories: null,
61
60
  rawOpenPositions: null,
@@ -64,38 +63,40 @@ const useUserData = create((set) => ({
64
63
  twapDetails: null,
65
64
  notifications: null,
66
65
  userExtraAgents: null,
66
+ spotState: null,
67
67
  setAccessToken: (token) => set({ accessToken: token }),
68
68
  setRefreshToken: (token) => set({ refreshToken: token }),
69
69
  setIsAuthenticated: (value) => set({ isAuthenticated: value }),
70
- setIsReady: (value) => set({ isReady: value }),
71
- setAddress: (address) => {
72
- // if (typeof window !== "undefined") {
73
- // if (address) {
74
- // window.localStorage.setItem("address", address);
75
- // } else {
76
- // window.localStorage.removeItem("address");
77
- // }
78
- // }
79
- set({ address });
80
- },
70
+ setAddress: (address) => set(() => {
71
+ if (typeof window !== 'undefined') {
72
+ if (address) {
73
+ window.localStorage.setItem('address', address);
74
+ }
75
+ else {
76
+ window.localStorage.removeItem('address');
77
+ }
78
+ }
79
+ return { address };
80
+ }),
81
81
  setTradeHistories: (value) => set({ tradeHistories: value }),
82
82
  setRawOpenPositions: (value) => set({ rawOpenPositions: value }),
83
83
  setOpenOrders: (value) => set({ openOrders: value }),
84
84
  setAccountSummary: (value) => set({ accountSummary: value }),
85
85
  setTwapDetails: (value) => set({ twapDetails: value }),
86
86
  setNotifications: (value) => set({ notifications: value }),
87
+ setSpotState: (value) => set({ spotState: value }),
87
88
  clean: () => set({
88
- // accessToken: null,
89
- // refreshToken: null,
90
- // isAuthenticated: false,
91
- // isReady: false,
92
- // address: null,
89
+ accessToken: null,
90
+ refreshToken: null,
91
+ isAuthenticated: false,
92
+ address: null,
93
93
  tradeHistories: null,
94
94
  rawOpenPositions: null,
95
95
  openOrders: null,
96
96
  accountSummary: null,
97
97
  twapDetails: null,
98
98
  notifications: null,
99
+ spotState: null,
99
100
  }),
100
101
  setUserExtraAgents: (value) => set({ userExtraAgents: value }),
101
102
  }));
@@ -113,18 +114,71 @@ const useMarketData = create((set) => ({
113
114
  * Convert a full/prefixed symbol (e.g., "xyz:XYZ100") to a display symbol (e.g., "XYZ100").
114
115
  */
115
116
  function toDisplaySymbol(symbol) {
116
- const parts = symbol.split(":");
117
+ const parts = symbol.split(':');
117
118
  return parts.length > 1 ? parts.slice(-1)[0] : symbol;
118
119
  }
119
120
  /**
120
121
  * Convert a display symbol back to backend form using a provided map.
121
122
  * If mapping is missing, returns the original symbol.
122
- * @param displaySymbol e.g., "XYZ100"
123
- * @param displayToFull map of display -> full (e.g., "XYZ100" -> "xyz:XYZ100")
123
+ * For multi-market assets, returns the first available market.
124
+ * @param displaySymbol e.g., "TSLA"
125
+ * @param hip3Assets map of display -> all full market names (e.g., "TSLA" -> ["xyz:TSLA", "flx:TSLA"])
126
+ */
127
+ function toBackendSymbol(displaySymbol, hip3Assets) {
128
+ const markets = hip3Assets.get(displaySymbol);
129
+ // Return first market if available, otherwise return original symbol
130
+ return markets && markets.length > 0 ? markets[0] : displaySymbol;
131
+ }
132
+ /**
133
+ * Convert a display symbol to backend form for a specific market prefix.
134
+ * This is useful when an asset is available on multiple markets (e.g., xyz:TSLA and flx:TSLA).
135
+ * @param displaySymbol e.g., "TSLA"
136
+ * @param marketPrefix e.g., "xyz" or "flx"
137
+ * @param hip3Assets map of display -> all full market names
138
+ * @returns Full market name if found, null if prefix not specified for multi-market asset, otherwise displaySymbol with prefix
139
+ */
140
+ function toBackendSymbolWithMarket(displaySymbol, marketPrefix, hip3Assets) {
141
+ const availableMarkets = hip3Assets.get(displaySymbol);
142
+ if (!availableMarkets || availableMarkets.length === 0) {
143
+ // Not a HIP-3 asset, return as-is or with prefix if provided
144
+ return marketPrefix ? `${marketPrefix}:${displaySymbol}` : displaySymbol;
145
+ }
146
+ if (marketPrefix) {
147
+ // Find the market with the specified prefix
148
+ const targetMarket = availableMarkets.find((market) => market.toLowerCase().startsWith(`${marketPrefix.toLowerCase()}:`));
149
+ if (targetMarket) {
150
+ return targetMarket;
151
+ }
152
+ }
153
+ // No prefix specified or not found, return null to force explicit market selection
154
+ return null;
155
+ }
156
+ /**
157
+ * Get all available markets for a display symbol.
158
+ * @param displaySymbol e.g., "TSLA"
159
+ * @param hip3Assets map of display -> all full market names
160
+ * @returns Array of full market names, e.g., ["xyz:TSLA", "flx:TSLA"]
124
161
  */
125
- function toBackendSymbol(displaySymbol, displayToFull) {
162
+ function getAvailableMarkets(displaySymbol, hip3Assets) {
126
163
  var _a;
127
- return (_a = displayToFull.get(displaySymbol)) !== null && _a !== void 0 ? _a : displaySymbol;
164
+ return (_a = hip3Assets.get(displaySymbol)) !== null && _a !== void 0 ? _a : [];
165
+ }
166
+ /**
167
+ * Extract the market prefix from a full market name.
168
+ * @param fullSymbol e.g., "xyz:TSLA"
169
+ * @returns The prefix (e.g., "xyz") or undefined if no prefix
170
+ */
171
+ function getMarketPrefix(fullSymbol) {
172
+ const parts = fullSymbol.split(':');
173
+ return parts.length > 1 ? parts[0] : undefined;
174
+ }
175
+ /**
176
+ * Check if a symbol is a HIP-3 market (has a prefix).
177
+ * @param symbol e.g., "xyz:TSLA" or "TSLA"
178
+ * @returns true if the symbol has a market prefix
179
+ */
180
+ function isHip3Market(symbol) {
181
+ return symbol.includes(':');
128
182
  }
129
183
 
130
184
  const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
@@ -141,7 +195,8 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
141
195
  try {
142
196
  const message = JSON.parse(event.data);
143
197
  // Handle subscription responses (only if they don't have channel data)
144
- if (('success' in message || 'error' in message) && !('channel' in message)) {
198
+ if (('success' in message || 'error' in message) &&
199
+ !('channel' in message)) {
145
200
  if (message.error) {
146
201
  setLastError(message.error);
147
202
  }
@@ -160,12 +215,21 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
160
215
  switch (dataMessage.channel) {
161
216
  case 'trade-histories':
162
217
  {
218
+ const mapAsset = (a) => {
219
+ var _a, _b;
220
+ const extractedPrefix = getMarketPrefix(a.coin);
221
+ return {
222
+ ...a,
223
+ coin: toDisplaySymbol(a.coin),
224
+ marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
225
+ };
226
+ };
163
227
  const list = dataMessage.data.map((item) => {
164
228
  var _a, _b;
165
229
  return ({
166
230
  ...item,
167
- closedLongAssets: item.closedLongAssets.map((a) => ({ ...a, coin: toDisplaySymbol(a.coin) })),
168
- closedShortAssets: item.closedShortAssets.map((a) => ({ ...a, coin: toDisplaySymbol(a.coin) })),
231
+ closedLongAssets: item.closedLongAssets.map(mapAsset),
232
+ closedShortAssets: item.closedShortAssets.map(mapAsset),
169
233
  positionLongAssets: (_a = item.positionLongAssets) === null || _a === void 0 ? void 0 : _a.map((a) => toDisplaySymbol(a)),
170
234
  positionShortAssets: (_b = item.positionShortAssets) === null || _b === void 0 ? void 0 : _b.map((a) => toDisplaySymbol(a)),
171
235
  });
@@ -175,10 +239,19 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
175
239
  break;
176
240
  case 'open-positions':
177
241
  {
242
+ const enrichAsset = (a) => {
243
+ var _a, _b;
244
+ const extractedPrefix = getMarketPrefix(a.coin);
245
+ return {
246
+ ...a,
247
+ coin: toDisplaySymbol(a.coin),
248
+ marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
249
+ };
250
+ };
178
251
  const list = dataMessage.data.map((pos) => ({
179
252
  ...pos,
180
- longAssets: pos.longAssets.map((a) => ({ ...a, coin: toDisplaySymbol(a.coin) })),
181
- shortAssets: pos.shortAssets.map((a) => ({ ...a, coin: toDisplaySymbol(a.coin) })),
253
+ longAssets: pos.longAssets.map(enrichAsset),
254
+ shortAssets: pos.shortAssets.map(enrichAsset),
182
255
  }));
183
256
  setRawOpenPositions(list);
184
257
  }
@@ -187,8 +260,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
187
260
  {
188
261
  const list = dataMessage.data.map((order) => ({
189
262
  ...order,
190
- longAssets: order.longAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
191
- shortAssets: order.shortAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
263
+ longAssets: order.longAssets.map((a) => ({
264
+ ...a,
265
+ asset: toDisplaySymbol(a.asset),
266
+ })),
267
+ shortAssets: order.shortAssets.map((a) => ({
268
+ ...a,
269
+ asset: toDisplaySymbol(a.asset),
270
+ })),
192
271
  }));
193
272
  setOpenOrders(list);
194
273
  }
@@ -198,10 +277,20 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
198
277
  break;
199
278
  case 'twap-details':
200
279
  {
280
+ const mapTwapAsset = (a) => {
281
+ var _a, _b, _c;
282
+ const extractedPrefix = getMarketPrefix(a.asset);
283
+ return {
284
+ ...a,
285
+ asset: toDisplaySymbol(a.asset),
286
+ marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
287
+ collateralToken: (_c = a.collateralToken) !== null && _c !== void 0 ? _c : undefined,
288
+ };
289
+ };
201
290
  const list = dataMessage.data.map((twap) => ({
202
291
  ...twap,
203
- longAssets: twap.longAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
204
- shortAssets: twap.shortAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
292
+ longAssets: twap.longAssets.map(mapTwapAsset),
293
+ shortAssets: twap.shortAssets.map(mapTwapAsset),
205
294
  }));
206
295
  setTwapDetails(list);
207
296
  }
@@ -214,8 +303,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
214
303
  const md = dataMessage.data;
215
304
  const mapGroup = (g) => ({
216
305
  ...g,
217
- longAssets: g.longAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
218
- shortAssets: g.shortAssets.map((a) => ({ ...a, asset: toDisplaySymbol(a.asset) })),
306
+ longAssets: g.longAssets.map((a) => ({
307
+ ...a,
308
+ asset: toDisplaySymbol(a.asset),
309
+ })),
310
+ shortAssets: g.shortAssets.map((a) => ({
311
+ ...a,
312
+ asset: toDisplaySymbol(a.asset),
313
+ })),
219
314
  });
220
315
  const mapped = {
221
316
  active: md.active.map(mapGroup),
@@ -233,7 +328,15 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
233
328
  catch (error) {
234
329
  setLastError(`Failed to parse message: ${error instanceof Error ? error.message : String(error)}`);
235
330
  }
236
- }, [setTradeHistories, setRawOpenPositions, setOpenOrders, setAccountSummary, setTwapDetails, setNotifications, setMarketData]);
331
+ }, [
332
+ setTradeHistories,
333
+ setRawOpenPositions,
334
+ setOpenOrders,
335
+ setAccountSummary,
336
+ setTwapDetails,
337
+ setNotifications,
338
+ setMarketData,
339
+ ]);
237
340
  const connect = useCallback(() => {
238
341
  if (!enabled || !wsUrl)
239
342
  return;
@@ -302,7 +405,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
302
405
  'open-orders',
303
406
  'twap-details',
304
407
  'fills-checkpoint',
305
- 'notifications'
408
+ 'notifications',
306
409
  ];
307
410
  const globalChannels = ['market-data'];
308
411
  if (address && address !== lastSubscribedAddress) {
@@ -311,14 +414,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
311
414
  sendMessage(JSON.stringify({
312
415
  action: 'unsubscribe',
313
416
  address: lastSubscribedAddress,
314
- channels: addressSpecificChannels
417
+ channels: addressSpecificChannels,
315
418
  }));
316
419
  }
317
420
  // Subscribe to all channels (global + address-specific)
318
421
  sendMessage(JSON.stringify({
319
422
  action: 'subscribe',
320
423
  address: address,
321
- channels: [...globalChannels, ...addressSpecificChannels]
424
+ channels: [...globalChannels, ...addressSpecificChannels],
322
425
  }));
323
426
  setLastSubscribedAddress(address);
324
427
  setLastError(null);
@@ -328,7 +431,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
328
431
  sendMessage(JSON.stringify({
329
432
  action: 'unsubscribe',
330
433
  address: lastSubscribedAddress,
331
- channels: addressSpecificChannels
434
+ channels: addressSpecificChannels,
332
435
  }));
333
436
  setLastSubscribedAddress(null);
334
437
  }
@@ -336,7 +439,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
336
439
  // If no address but connected, subscribe to global channels only
337
440
  sendMessage(JSON.stringify({
338
441
  action: 'subscribe',
339
- channels: globalChannels
442
+ channels: globalChannels,
340
443
  }));
341
444
  }
342
445
  }, [isConnected, address, lastSubscribedAddress, sendMessage]);
@@ -359,11 +462,14 @@ const useHyperliquidData = create((set, get) => ({
359
462
  finalAssetContexts: null,
360
463
  finalAtOICaps: null,
361
464
  aggregatedClearingHouseState: null,
465
+ rawClearinghouseStates: null,
362
466
  perpMetaAssets: null,
363
- hip3DisplayToFull: new Map(),
467
+ allPerpMetaAssets: null,
468
+ hip3Assets: new Map(),
469
+ hip3MarketPrefixes: new Map(),
364
470
  setAllMids: (value) => set({ allMids: value }),
365
471
  setActiveAssetData: (value) => set((state) => ({
366
- activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value
472
+ activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value,
367
473
  })),
368
474
  deleteActiveAssetData: (key) => {
369
475
  set((state) => {
@@ -398,13 +504,16 @@ const useHyperliquidData = create((set, get) => ({
398
504
  activeAssetData: {
399
505
  ...state.activeAssetData,
400
506
  [key]: value,
401
- }
507
+ },
402
508
  })),
403
509
  setFinalAssetContexts: (value) => set({ finalAssetContexts: value }),
404
510
  setFinalAtOICaps: (value) => set({ finalAtOICaps: value }),
405
511
  setAggregatedClearingHouseState: (value) => set({ aggregatedClearingHouseState: value }),
512
+ setRawClearinghouseStates: (value) => set({ rawClearinghouseStates: value }),
406
513
  setPerpMetaAssets: (value) => set({ perpMetaAssets: value }),
407
- setHip3DisplayToFull: (value) => set({ hip3DisplayToFull: value })
514
+ setAllPerpMetaAssets: (value) => set({ allPerpMetaAssets: value }),
515
+ setHip3Assets: (value) => set({ hip3Assets: value }),
516
+ setHip3MarketPrefixes: (value) => set({ hip3MarketPrefixes: value }),
408
517
  }));
409
518
 
410
519
  /**
@@ -667,7 +776,8 @@ const useUserSelection$1 = create((set, get) => ({
667
776
  }));
668
777
 
669
778
  const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
670
- const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState, } = useHyperliquidData();
779
+ const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState, setRawClearinghouseStates, } = useHyperliquidData();
780
+ const { setSpotState } = useUserData();
671
781
  const { candleInterval } = useUserSelection$1();
672
782
  const longTokens = useUserSelection$1((s) => s.longTokens);
673
783
  const shortTokens = useUserSelection$1((s) => s.shortTokens);
@@ -686,9 +796,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
686
796
  try {
687
797
  const message = JSON.parse(event.data);
688
798
  // Handle subscription responses
689
- if ("success" in message || "error" in message) {
799
+ if ('success' in message || 'error' in message) {
690
800
  if (message.error) {
691
- console.error("[HyperLiquid WS] Subscription error:", message.error);
801
+ console.error('[HyperLiquid WS] Subscription error:', message.error);
692
802
  setLastError(message.error);
693
803
  }
694
804
  else {
@@ -697,30 +807,44 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
697
807
  return;
698
808
  }
699
809
  // Handle channel data messages
700
- if ("channel" in message && "data" in message) {
810
+ if ('channel' in message && 'data' in message) {
701
811
  const response = message;
702
812
  switch (response.channel) {
703
- case "webData3":
813
+ case 'webData3':
704
814
  const webData3 = response.data;
705
815
  // finalAssetContexts now sourced from allDexsAssetCtxs channel
706
816
  const finalAtOICaps = webData3.perpDexStates.flatMap((dex) => dex.perpsAtOpenInterestCap);
707
817
  setFinalAtOICaps(finalAtOICaps);
708
818
  break;
709
- case "allDexsAssetCtxs":
819
+ case 'allDexsAssetCtxs':
710
820
  {
711
821
  const data = response.data;
712
- const finalAssetContexts = (data.ctxs || []).flatMap(([, ctxs]) => ctxs || []);
822
+ // Filter out hyna to match perpMetaAssets filtering
823
+ const FILTERED_DEX_PREFIXES = ['hyna'];
824
+ const filtered = (data.ctxs || [])
825
+ .filter(([prefix]) => !FILTERED_DEX_PREFIXES.includes((prefix || '').toLowerCase()))
826
+ .sort((a, b) => {
827
+ // Sort to match perpMetaAssets order: default market first, then alphabetically
828
+ const prefixA = a[0] || '';
829
+ const prefixB = b[0] || '';
830
+ if (prefixA === '' && prefixB !== '')
831
+ return -1;
832
+ if (prefixA !== '' && prefixB === '')
833
+ return 1;
834
+ return prefixA.localeCompare(prefixB);
835
+ });
836
+ const finalAssetContexts = filtered.flatMap(([, ctxs]) => ctxs || []);
713
837
  setFinalAssetContexts(finalAssetContexts);
714
838
  }
715
839
  break;
716
- case "allDexsClearinghouseState":
840
+ case 'allDexsClearinghouseState':
717
841
  {
718
842
  const data = response.data;
719
843
  const states = (data.clearinghouseStates || [])
720
844
  .map(([, s]) => s)
721
845
  .filter(Boolean);
722
- const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || "0") || 0), 0);
723
- const toStr = (n) => Number.isFinite(n) ? n.toString() : "0";
846
+ const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || '0') || 0), 0);
847
+ const toStr = (n) => Number.isFinite(n) ? n.toString() : '0';
724
848
  const assetPositions = states.flatMap((s) => s.assetPositions || []);
725
849
  const crossMaintenanceMarginUsed = toStr(sum(states.map((s) => s.crossMaintenanceMarginUsed)));
726
850
  const crossMarginSummary = {
@@ -746,22 +870,41 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
746
870
  withdrawable,
747
871
  };
748
872
  setAggregatedClearingHouseState(aggregatedClearingHouseState);
873
+ setRawClearinghouseStates(data.clearinghouseStates || null);
749
874
  }
750
875
  break;
751
- case "allMids":
876
+ case 'allMids':
752
877
  {
753
878
  const data = response.data;
754
- const remapped = {
755
- mids: Object.fromEntries(
756
- // only support non hip-3 and xyz market
757
- Object.entries(data.mids || {})
758
- .filter(([k, v]) => !k.includes(":") || k.includes("xyz:"))
759
- .map(([k, v]) => [toDisplaySymbol(k), v])),
760
- };
761
- setAllMids(remapped);
879
+ // Keep BOTH normalized prefixed keys AND display symbol keys
880
+ // This ensures xyz:TSLA and flx:TSLA are stored separately,
881
+ // while also maintaining backward compatibility with non-prefixed lookups
882
+ const mids = {};
883
+ Object.entries(data.mids || {}).forEach(([k, v]) => {
884
+ // Normalize prefixed keys to lowercase prefix (e.g., "XYZ:TSLA" -> "xyz:TSLA")
885
+ // This matches how we look up tokens in the SDK
886
+ let normalizedKey = k;
887
+ if (k.includes(':')) {
888
+ const [prefix, ...rest] = k.split(':');
889
+ normalizedKey = `${prefix.toLowerCase()}:${rest.join(':')}`;
890
+ }
891
+ // Store with normalized key
892
+ mids[normalizedKey] = v;
893
+ // Also store with original key for backward compatibility
894
+ if (k !== normalizedKey) {
895
+ mids[k] = v;
896
+ }
897
+ // Also store with display symbol for backward compatibility
898
+ const displayKey = toDisplaySymbol(k);
899
+ // Only set display key if it doesn't already exist (avoid overwriting market-specific prices)
900
+ if (!(displayKey in mids)) {
901
+ mids[displayKey] = v;
902
+ }
903
+ });
904
+ setAllMids({ mids });
762
905
  }
763
906
  break;
764
- case "activeAssetData":
907
+ case 'activeAssetData':
765
908
  {
766
909
  const assetData = response.data;
767
910
  const symbol = toDisplaySymbol(assetData.coin);
@@ -772,14 +915,22 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
772
915
  upsertActiveAssetData(symbol, normalized);
773
916
  }
774
917
  break;
775
- case "candle":
918
+ case 'candle':
776
919
  {
777
920
  const candleDataItem = response.data;
778
- const symbol = toDisplaySymbol(candleDataItem.s || "");
921
+ const symbol = toDisplaySymbol(candleDataItem.s || '');
779
922
  const normalized = { ...candleDataItem, s: symbol };
780
923
  addCandleData(symbol, normalized);
781
924
  }
782
925
  break;
926
+ case 'spotState':
927
+ {
928
+ const spotStateData = response.data;
929
+ if (spotStateData === null || spotStateData === void 0 ? void 0 : spotStateData.spotState) {
930
+ setSpotState(spotStateData.spotState);
931
+ }
932
+ }
933
+ break;
783
934
  default:
784
935
  console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
785
936
  }
@@ -787,7 +938,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
787
938
  }
788
939
  catch (error) {
789
940
  const errorMessage = `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`;
790
- console.error("[HyperLiquid WS] Parse error:", errorMessage, "Raw message:", event.data);
941
+ console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
791
942
  setLastError(errorMessage);
792
943
  }
793
944
  }, [
@@ -797,6 +948,8 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
797
948
  setFinalAssetContexts,
798
949
  setFinalAtOICaps,
799
950
  setAggregatedClearingHouseState,
951
+ setRawClearinghouseStates,
952
+ setSpotState,
800
953
  ]);
801
954
  const connect = useCallback(() => {
802
955
  if (!enabled)
@@ -827,7 +980,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
827
980
  if (!manualCloseRef.current && reconnectAttemptsRef.current < 5) {
828
981
  reconnectAttemptsRef.current += 1;
829
982
  if (reconnectAttemptsRef.current === 5) {
830
- console.error("[HyperLiquid WS] Reconnection stopped after 5 attempts");
983
+ console.error('[HyperLiquid WS] Reconnection stopped after 5 attempts');
831
984
  }
832
985
  setTimeout(() => connect(), 3000);
833
986
  }
@@ -895,6 +1048,17 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
895
1048
  },
896
1049
  };
897
1050
  sendJsonMessage(unsubscribeMessage);
1051
+ // Unsubscribe from spotState for previous address
1052
+ if (subscribedAddress !== DEFAULT_ADDRESS) {
1053
+ const unsubscribeSpotState = {
1054
+ method: 'unsubscribe',
1055
+ subscription: {
1056
+ type: 'spotState',
1057
+ user: subscribedAddress,
1058
+ },
1059
+ };
1060
+ sendJsonMessage(unsubscribeSpotState);
1061
+ }
898
1062
  const unsubscribeAllDexsClearinghouseState = {
899
1063
  method: "unsubscribe",
900
1064
  subscription: {
@@ -938,11 +1102,26 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
938
1102
  sendJsonMessage(subscribeAllDexsClearinghouseState);
939
1103
  sendJsonMessage(subscribeAllMids);
940
1104
  sendJsonMessage(subscribeAllDexsAssetCtxs);
1105
+ // Subscribe to spotState for real-time spot balances (USDH, USDC, etc.)
1106
+ // Only subscribe if we have a real user address (not the default)
1107
+ if (userAddress !== DEFAULT_ADDRESS) {
1108
+ const subscribeSpotState = {
1109
+ method: 'subscribe',
1110
+ subscription: {
1111
+ type: 'spotState',
1112
+ user: userAddress,
1113
+ },
1114
+ };
1115
+ sendJsonMessage(subscribeSpotState);
1116
+ }
941
1117
  setSubscribedAddress(userAddress);
942
1118
  // Clear previous data when address changes
943
1119
  if (subscribedAddress && subscribedAddress !== userAddress) {
944
1120
  // clear aggregatedClearingHouseState
945
1121
  setAggregatedClearingHouseState(null);
1122
+ setRawClearinghouseStates(null);
1123
+ // clear spotState
1124
+ setSpotState(null);
946
1125
  }
947
1126
  }, [
948
1127
  isConnected,
@@ -950,6 +1129,8 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
950
1129
  subscribedAddress,
951
1130
  sendJsonMessage,
952
1131
  setAggregatedClearingHouseState,
1132
+ setRawClearinghouseStates,
1133
+ setSpotState,
953
1134
  ]);
954
1135
  // Handle token subscriptions for activeAssetData
955
1136
  useEffect(() => {
@@ -1146,20 +1327,112 @@ const useAccountSummary = () => {
1146
1327
  return { data: calculated, isLoading };
1147
1328
  };
1148
1329
 
1330
+ function findAssetMeta$4(coinName, perpMetaAssets, knownPrefix, desiredCollateral) {
1331
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
1332
+ if (!perpMetaAssets) {
1333
+ return { collateralToken: 'USDC', marketPrefix: null };
1334
+ }
1335
+ if (desiredCollateral) {
1336
+ const collateralMatch = perpMetaAssets.find((a) => a.name === coinName && a.collateralToken === desiredCollateral);
1337
+ if (collateralMatch) {
1338
+ return {
1339
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
1340
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
1341
+ };
1342
+ }
1343
+ }
1344
+ if (coinName.includes(':')) {
1345
+ const [prefix, symbol] = coinName.split(':');
1346
+ const exactMatch = perpMetaAssets.find((a) => {
1347
+ var _a;
1348
+ return a.name === symbol &&
1349
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
1350
+ });
1351
+ if (exactMatch) {
1352
+ return {
1353
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
1354
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
1355
+ };
1356
+ }
1357
+ }
1358
+ if (knownPrefix) {
1359
+ const exactMatch = perpMetaAssets.find((a) => {
1360
+ var _a;
1361
+ return a.name === coinName &&
1362
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
1363
+ });
1364
+ if (exactMatch) {
1365
+ return {
1366
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
1367
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
1368
+ };
1369
+ }
1370
+ }
1371
+ const exactMatch = perpMetaAssets.find((a) => a.name === coinName && !a.marketPrefix);
1372
+ if (exactMatch) {
1373
+ return {
1374
+ collateralToken: (_g = exactMatch.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
1375
+ marketPrefix: (_h = exactMatch.marketPrefix) !== null && _h !== void 0 ? _h : null,
1376
+ };
1377
+ }
1378
+ const hip3Matches = perpMetaAssets.filter((a) => a.name === coinName && a.marketPrefix);
1379
+ if (hip3Matches.length > 0) {
1380
+ if (desiredCollateral) {
1381
+ const collateralMatch = hip3Matches.find((a) => a.collateralToken === desiredCollateral);
1382
+ if (collateralMatch) {
1383
+ return {
1384
+ collateralToken: (_j = collateralMatch.collateralToken) !== null && _j !== void 0 ? _j : 'USDC',
1385
+ marketPrefix: (_k = collateralMatch.marketPrefix) !== null && _k !== void 0 ? _k : null,
1386
+ };
1387
+ }
1388
+ }
1389
+ const usdHMatch = hip3Matches.find((a) => a.collateralToken === 'USDH');
1390
+ const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Matches[0];
1391
+ return {
1392
+ collateralToken: (_l = chosen.collateralToken) !== null && _l !== void 0 ? _l : 'USDC',
1393
+ marketPrefix: (_m = chosen.marketPrefix) !== null && _m !== void 0 ? _m : null,
1394
+ };
1395
+ }
1396
+ return { collateralToken: 'USDC', marketPrefix: null };
1397
+ }
1398
+ function enrichTradeHistoryAssets(assets, perpMetaAssets) {
1399
+ return assets.map((asset) => {
1400
+ var _a;
1401
+ if (asset.marketPrefix && asset.collateralToken) {
1402
+ return asset;
1403
+ }
1404
+ const meta = findAssetMeta$4(asset.coin, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
1405
+ return {
1406
+ ...asset,
1407
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
1408
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
1409
+ };
1410
+ });
1411
+ }
1412
+ function enrichTradeHistories(histories, perpMetaAssets) {
1413
+ return histories.map((history) => ({
1414
+ ...history,
1415
+ closedLongAssets: enrichTradeHistoryAssets(history.closedLongAssets, perpMetaAssets),
1416
+ closedShortAssets: enrichTradeHistoryAssets(history.closedShortAssets, perpMetaAssets),
1417
+ }));
1418
+ }
1149
1419
  const useTradeHistories = () => {
1150
1420
  const context = useContext(PearHyperliquidContext);
1151
1421
  if (!context) {
1152
1422
  throw new Error('useTradeHistories must be used within a PearHyperliquidProvider');
1153
1423
  }
1154
1424
  const tradeHistories = useUserData((state) => state.tradeHistories);
1425
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
1155
1426
  const isLoading = useMemo(() => {
1156
1427
  return tradeHistories === null && context.isConnected;
1157
1428
  }, [tradeHistories, context.isConnected]);
1158
- return { data: tradeHistories, isLoading };
1429
+ const enrichedTradeHistories = useMemo(() => {
1430
+ if (!tradeHistories)
1431
+ return null;
1432
+ return enrichTradeHistories(tradeHistories, allPerpMetaAssets);
1433
+ }, [tradeHistories, allPerpMetaAssets]);
1434
+ return { data: enrichedTradeHistories, isLoading };
1159
1435
  };
1160
- /**
1161
- * Hook to access open orders with loading state
1162
- */
1163
1436
  const useOpenOrders = () => {
1164
1437
  const context = useContext(PearHyperliquidContext);
1165
1438
  if (!context) {
@@ -1188,21 +1461,51 @@ const useWebData = () => {
1188
1461
  const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
1189
1462
  const aggregatedClearinghouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
1190
1463
  const finalAtOICaps = useHyperliquidData((state) => state.finalAtOICaps);
1191
- const hip3Assets = useHyperliquidData((state) => state.hip3DisplayToFull);
1464
+ const hip3Assets = useHyperliquidData((state) => state.hip3Assets);
1465
+ const hip3MarketPrefixes = useHyperliquidData((state) => state.hip3MarketPrefixes);
1192
1466
  let marketDataBySymbol = {};
1193
1467
  if (finalAssetContexts && perpMetaAssets) {
1194
1468
  const result = {};
1469
+ // Build a map of display name -> asset context index (for unique display names)
1470
+ const displayNameToContextIndex = new Map();
1471
+ const seenNames = new Set();
1472
+ let contextIndex = 0;
1473
+ // First pass: map unique display names to their context index
1195
1474
  for (let index = 0; index < perpMetaAssets.length; index++) {
1196
1475
  const name = perpMetaAssets[index].name;
1197
- result[name] = {
1198
- asset: finalAssetContexts[index],
1199
- universe: perpMetaAssets[index],
1200
- };
1476
+ if (!seenNames.has(name)) {
1477
+ seenNames.add(name);
1478
+ if (contextIndex < finalAssetContexts.length) {
1479
+ displayNameToContextIndex.set(name, contextIndex);
1480
+ contextIndex++;
1481
+ }
1482
+ }
1483
+ }
1484
+ // Second pass: create nested entries for all market variants
1485
+ for (let index = 0; index < perpMetaAssets.length; index++) {
1486
+ const universeAsset = perpMetaAssets[index];
1487
+ const displayName = universeAsset.name;
1488
+ const marketPrefix = universeAsset.marketPrefix;
1489
+ const ctxIndex = displayNameToContextIndex.get(displayName);
1490
+ if (ctxIndex !== undefined) {
1491
+ const assetContext = finalAssetContexts[ctxIndex];
1492
+ // Initialize the symbol entry if it doesn't exist
1493
+ if (!result[displayName]) {
1494
+ result[displayName] = {};
1495
+ }
1496
+ // Use marketPrefix as key for HIP-3 assets, "default" for regular assets
1497
+ const variantKey = marketPrefix || 'default';
1498
+ result[displayName][variantKey] = {
1499
+ asset: assetContext,
1500
+ universe: universeAsset,
1501
+ };
1502
+ }
1201
1503
  }
1202
1504
  marketDataBySymbol = result;
1203
1505
  }
1204
1506
  return {
1205
1507
  hip3Assets,
1508
+ hip3MarketPrefixes,
1206
1509
  clearinghouseState: aggregatedClearinghouseState,
1207
1510
  perpsAtOpenInterestCap: finalAtOICaps,
1208
1511
  marketDataBySymbol,
@@ -1217,19 +1520,30 @@ const useWebData = () => {
1217
1520
  class TokenMetadataExtractor {
1218
1521
  /**
1219
1522
  * Extracts comprehensive token metadata
1220
- * @param symbol - Token symbol
1523
+ * @param symbol - Token symbol (base symbol without prefix, e.g., "TSLA")
1221
1524
  * @param perpMetaAssets - Aggregated universe assets (flattened across dexes)
1222
1525
  * @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
1223
1526
  * @param allMids - AllMids data containing current prices
1224
1527
  * @param activeAssetData - Optional active asset data containing leverage information
1528
+ * @param marketPrefix - Optional market prefix (e.g., "xyz", "flx") for HIP3 multi-market assets
1225
1529
  * @returns TokenMetadata or null if token not found
1226
1530
  */
1227
- static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1531
+ static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketPrefix) {
1228
1532
  if (!perpMetaAssets || !finalAssetContexts || !allMids) {
1229
1533
  return null;
1230
1534
  }
1231
1535
  // Find token index in aggregated universe
1232
- const universeIndex = perpMetaAssets.findIndex(asset => asset.name === symbol);
1536
+ // For HIP3 assets, match both name AND marketPrefix
1537
+ const universeIndex = perpMetaAssets.findIndex((asset) => {
1538
+ if (asset.name !== symbol)
1539
+ return false;
1540
+ // If marketPrefix is specified, match it; otherwise match assets without prefix
1541
+ if (marketPrefix) {
1542
+ return asset.marketPrefix === marketPrefix;
1543
+ }
1544
+ // No prefix specified - match non-HIP3 asset (no marketPrefix) or first matching asset
1545
+ return !asset.marketPrefix;
1546
+ });
1233
1547
  if (universeIndex === -1) {
1234
1548
  return null;
1235
1549
  }
@@ -1238,9 +1552,20 @@ class TokenMetadataExtractor {
1238
1552
  if (!assetCtx) {
1239
1553
  return null;
1240
1554
  }
1241
- // Get current price from allMids
1242
- const currentPriceStr = allMids.mids[symbol];
1243
- const currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
1555
+ // Get current price - prefer assetCtx.midPx as it's already index-matched,
1556
+ // fall back to allMids lookup if midPx is null
1557
+ const prefixedKeyColon = marketPrefix ? `${marketPrefix}:${symbol}` : null;
1558
+ let currentPrice = 0;
1559
+ // Primary source: assetCtx.midPx (already properly indexed)
1560
+ if (assetCtx.midPx) {
1561
+ currentPrice = parseFloat(assetCtx.midPx);
1562
+ }
1563
+ // Fallback: allMids lookup with multiple key formats for HIP3 markets
1564
+ if (!currentPrice || isNaN(currentPrice)) {
1565
+ const currentPriceStr = (prefixedKeyColon && allMids.mids[prefixedKeyColon]) ||
1566
+ allMids.mids[symbol];
1567
+ currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
1568
+ }
1244
1569
  // Get previous day price
1245
1570
  const prevDayPrice = parseFloat(assetCtx.prevDayPx);
1246
1571
  // Calculate 24h price change
@@ -1251,7 +1576,11 @@ class TokenMetadataExtractor {
1251
1576
  const markPrice = parseFloat(assetCtx.markPx);
1252
1577
  const oraclePrice = parseFloat(assetCtx.oraclePx);
1253
1578
  // Extract leverage info from activeAssetData if available
1254
- const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[symbol];
1579
+ // Try prefixed key first (e.g., "xyz:TSLA"), then fall back to plain symbol
1580
+ const activeDataKey = prefixedKeyColon && (activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[prefixedKeyColon])
1581
+ ? prefixedKeyColon
1582
+ : symbol;
1583
+ const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[activeDataKey];
1255
1584
  const leverage = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.leverage;
1256
1585
  const maxTradeSzs = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.maxTradeSzs;
1257
1586
  const availableToTrade = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.availableToTrade;
@@ -1269,21 +1598,27 @@ class TokenMetadataExtractor {
1269
1598
  leverage,
1270
1599
  maxTradeSzs,
1271
1600
  availableToTrade,
1601
+ collateralToken: universeAsset.collateralToken,
1272
1602
  };
1273
1603
  }
1274
1604
  /**
1275
1605
  * Extracts metadata for multiple tokens
1276
- * @param symbols - Array of token symbols
1606
+ * @param tokens - Array of token objects with symbol and optional marketPrefix
1277
1607
  * @param perpMetaAssets - Aggregated universe assets
1278
1608
  * @param finalAssetContexts - Aggregated asset contexts
1279
1609
  * @param allMids - AllMids data
1280
1610
  * @param activeAssetData - Optional active asset data containing leverage information
1281
- * @returns Record of symbol to TokenMetadata
1611
+ * @returns Record of unique key to TokenMetadata. Key is "{prefix}:{symbol}" for HIP3 assets, or just "{symbol}" otherwise
1282
1612
  */
1283
- static extractMultipleTokensMetadata(symbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1613
+ static extractMultipleTokensMetadata(tokens, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1284
1614
  const result = {};
1285
- for (const symbol of symbols) {
1286
- result[symbol] = this.extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData);
1615
+ for (const token of tokens) {
1616
+ // Use a unique key that includes the prefix for HIP3 assets
1617
+ // This ensures xyz:TSLA and flx:TSLA get separate entries
1618
+ const resultKey = token.marketPrefix
1619
+ ? `${token.marketPrefix}:${token.symbol}`
1620
+ : token.symbol;
1621
+ result[resultKey] = this.extractTokenMetadata(token.symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, token.marketPrefix);
1287
1622
  }
1288
1623
  return result;
1289
1624
  }
@@ -1296,10 +1631,30 @@ class TokenMetadataExtractor {
1296
1631
  static isTokenAvailable(symbol, perpMetaAssets) {
1297
1632
  if (!perpMetaAssets)
1298
1633
  return false;
1299
- return perpMetaAssets.some(asset => asset.name === symbol);
1634
+ return perpMetaAssets.some((asset) => asset.name === symbol);
1300
1635
  }
1301
1636
  }
1302
1637
 
1638
+ /**
1639
+ * Parse a token string that may have a market prefix (e.g., "xyz:GOOGL" -> { prefix: "xyz", symbol: "GOOGL" })
1640
+ * This allows us to keep the full name (xyz:GOOGL) for URLs/tags while extracting just the symbol for SDK lookups.
1641
+ */
1642
+ function parseTokenWithPrefix(token) {
1643
+ if (token.includes(":")) {
1644
+ const [prefix, ...rest] = token.split(":");
1645
+ const symbol = rest.join(":").toUpperCase();
1646
+ return {
1647
+ prefix: prefix.toLowerCase(),
1648
+ symbol,
1649
+ fullName: `${prefix.toLowerCase()}:${symbol}`,
1650
+ };
1651
+ }
1652
+ return {
1653
+ prefix: null,
1654
+ symbol: token.toUpperCase(),
1655
+ fullName: token.toUpperCase(),
1656
+ };
1657
+ }
1303
1658
  const useTokenSelectionMetadataStore = create((set) => ({
1304
1659
  isPriceDataReady: false,
1305
1660
  isLoading: true,
@@ -1315,17 +1670,59 @@ const useTokenSelectionMetadataStore = create((set) => ({
1315
1670
  maxLeverage: 0,
1316
1671
  minMargin: 0,
1317
1672
  leverageMatched: true,
1318
- recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens }) => {
1319
- const isPriceDataReady = !!(perpMetaAssets && finalAssetContexts && allMids);
1320
- // Compute metadata when ready
1321
- const longSymbols = longTokens.map((t) => t.symbol);
1322
- const shortSymbols = shortTokens.map((t) => t.symbol);
1323
- const longTokensMetadata = isPriceDataReady
1324
- ? TokenMetadataExtractor.extractMultipleTokensMetadata(longSymbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1673
+ recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens, }) => {
1674
+ const isPriceDataReady = !!(perpMetaAssets &&
1675
+ finalAssetContexts &&
1676
+ allMids);
1677
+ // Parse tokens - handle prefixed tokens like "xyz:GOOGL" by extracting the symbol and market prefix
1678
+ // The full name (xyz:GOOGL) is kept as the metadata key for UI consistency
1679
+ const parsedLongTokens = longTokens.map((t) => ({
1680
+ ...t,
1681
+ parsed: parseTokenWithPrefix(t.symbol),
1682
+ }));
1683
+ const parsedShortTokens = shortTokens.map((t) => ({
1684
+ ...t,
1685
+ parsed: parseTokenWithPrefix(t.symbol),
1686
+ }));
1687
+ // Extract base symbols with their market prefixes for SDK lookups
1688
+ // This ensures xyz:TSLA and flx:TSLA get different market data
1689
+ const longTokensForLookup = parsedLongTokens.map((t) => ({
1690
+ symbol: t.parsed.symbol,
1691
+ marketPrefix: t.parsed.prefix,
1692
+ }));
1693
+ const shortTokensForLookup = parsedShortTokens.map((t) => ({
1694
+ symbol: t.parsed.symbol,
1695
+ marketPrefix: t.parsed.prefix,
1696
+ }));
1697
+ // Also extract just the base symbols (without prefix) for lookups that don't support prefixes
1698
+ const longBaseSymbols = longTokensForLookup.map((t) => t.symbol);
1699
+ const shortBaseSymbols = shortTokensForLookup.map((t) => t.symbol);
1700
+ // Get metadata using base symbols with market prefix for proper market differentiation
1701
+ const longBaseMetadata = isPriceDataReady
1702
+ ? TokenMetadataExtractor.extractMultipleTokensMetadata(longTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1325
1703
  : {};
1326
- const shortTokensMetadata = isPriceDataReady
1327
- ? TokenMetadataExtractor.extractMultipleTokensMetadata(shortSymbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1704
+ const shortBaseMetadata = isPriceDataReady
1705
+ ? TokenMetadataExtractor.extractMultipleTokensMetadata(shortTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1328
1706
  : {};
1707
+ // Re-map metadata using original full names (with prefix) as keys for UI consistency
1708
+ // The extractor now keys by "{prefix}:{symbol}" for prefixed tokens, which matches our parsed.fullName
1709
+ const longTokensMetadata = {};
1710
+ parsedLongTokens.forEach((t) => {
1711
+ var _a;
1712
+ // Use the full name (e.g., "xyz:TSLA") as the lookup key since extractor uses the same format
1713
+ const lookupKey = t.parsed.prefix
1714
+ ? `${t.parsed.prefix}:${t.parsed.symbol}`
1715
+ : t.parsed.symbol;
1716
+ longTokensMetadata[t.symbol] = (_a = longBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
1717
+ });
1718
+ const shortTokensMetadata = {};
1719
+ parsedShortTokens.forEach((t) => {
1720
+ var _a;
1721
+ const lookupKey = t.parsed.prefix
1722
+ ? `${t.parsed.prefix}:${t.parsed.symbol}`
1723
+ : t.parsed.symbol;
1724
+ shortTokensMetadata[t.symbol] = (_a = shortBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
1725
+ });
1329
1726
  // Determine loading state
1330
1727
  const allTokens = [...longTokens, ...shortTokens];
1331
1728
  const isLoading = (() => {
@@ -1333,26 +1730,33 @@ const useTokenSelectionMetadataStore = create((set) => ({
1333
1730
  return true;
1334
1731
  if (allTokens.length === 0)
1335
1732
  return false;
1336
- const allMetadata = { ...longTokensMetadata, ...shortTokensMetadata };
1733
+ const allMetadata = {
1734
+ ...longTokensMetadata,
1735
+ ...shortTokensMetadata,
1736
+ };
1337
1737
  return allTokens.some((token) => !allMetadata[token.symbol]);
1338
1738
  })();
1339
1739
  // Open interest and volume (from market data for matching asset basket)
1740
+ // Use base symbols (without prefix) for matching against market data
1340
1741
  const { openInterest, volume } = (() => {
1341
1742
  const empty = { openInterest: "0", volume: "0" };
1342
1743
  if (!(marketData === null || marketData === void 0 ? void 0 : marketData.active) || (!longTokens.length && !shortTokens.length))
1343
1744
  return empty;
1344
- const selectedLong = longTokens.map((t) => t.symbol).sort();
1345
- const selectedShort = shortTokens.map((t) => t.symbol).sort();
1745
+ const selectedLong = longBaseSymbols.slice().sort();
1746
+ const selectedShort = shortBaseSymbols.slice().sort();
1346
1747
  const match = marketData.active.find((item) => {
1347
1748
  const longs = [...item.longAssets].sort();
1348
1749
  const shorts = [...item.shortAssets].sort();
1349
- if (longs.length !== selectedLong.length || shorts.length !== selectedShort.length)
1750
+ if (longs.length !== selectedLong.length ||
1751
+ shorts.length !== selectedShort.length)
1350
1752
  return false;
1351
1753
  const longsEqual = longs.every((s, i) => s.asset === selectedLong[i]);
1352
1754
  const shortsEqual = shorts.every((s, i) => s.asset === selectedShort[i]);
1353
1755
  return longsEqual && shortsEqual;
1354
1756
  });
1355
- return match ? { openInterest: match.openInterest, volume: match.volume } : empty;
1757
+ return match
1758
+ ? { openInterest: match.openInterest, volume: match.volume }
1759
+ : empty;
1356
1760
  })();
1357
1761
  // Price ratio (only when exactly one long and one short)
1358
1762
  const { priceRatio, priceRatio24h } = (() => {
@@ -1432,17 +1836,27 @@ const useTokenSelectionMetadataStore = create((set) => ({
1432
1836
  return totalFunding;
1433
1837
  })();
1434
1838
  // Max leverage (maximum across all tokens)
1839
+ // Use tokens with their market prefixes for proper lookup in perpMetaAssets
1435
1840
  const maxLeverage = (() => {
1436
1841
  if (!perpMetaAssets)
1437
1842
  return 0;
1438
- const allTokenSymbols = [...longTokens, ...shortTokens].map((t) => t.symbol);
1439
- if (allTokenSymbols.length === 0)
1843
+ const allTokensForLookup = [
1844
+ ...longTokensForLookup,
1845
+ ...shortTokensForLookup,
1846
+ ];
1847
+ if (allTokensForLookup.length === 0)
1440
1848
  return 0;
1441
1849
  let maxLev = 0;
1442
- allTokenSymbols.forEach((symbol) => {
1443
- const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol);
1444
- if (tokenUniverse === null || tokenUniverse === void 0 ? void 0 : tokenUniverse.maxLeverage)
1445
- maxLev = Math.max(maxLev, tokenUniverse.maxLeverage);
1850
+ allTokensForLookup.forEach(({ symbol, marketPrefix }) => {
1851
+ // Match by both name AND marketPrefix for HIP3 assets
1852
+ const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol &&
1853
+ (marketPrefix
1854
+ ? u.marketPrefix === marketPrefix
1855
+ : !u.marketPrefix));
1856
+ // Fallback to just matching by name if no exact match
1857
+ const fallbackUniverse = tokenUniverse || perpMetaAssets.find((u) => u.name === symbol);
1858
+ if (fallbackUniverse === null || fallbackUniverse === void 0 ? void 0 : fallbackUniverse.maxLeverage)
1859
+ maxLev = Math.max(maxLev, fallbackUniverse.maxLeverage);
1446
1860
  });
1447
1861
  return maxLev;
1448
1862
  })();
@@ -1454,7 +1868,10 @@ const useTokenSelectionMetadataStore = create((set) => ({
1454
1868
  // Whether all tokens have matching leverage
1455
1869
  const leverageMatched = (() => {
1456
1870
  const allTokensArr = [...longTokens, ...shortTokens];
1457
- const allMetadata = { ...longTokensMetadata, ...shortTokensMetadata };
1871
+ const allMetadata = {
1872
+ ...longTokensMetadata,
1873
+ ...shortTokensMetadata,
1874
+ };
1458
1875
  if (allTokensArr.length === 0)
1459
1876
  return true;
1460
1877
  const tokensWithLev = allTokensArr.filter((token) => { var _a; return (_a = allMetadata[token.symbol]) === null || _a === void 0 ? void 0 : _a.leverage; });
@@ -5639,8 +6056,8 @@ function addAuthInterceptors(params) {
5639
6056
  /**
5640
6057
  * Fetch historical candle data from HyperLiquid API
5641
6058
  */
5642
- const fetchHistoricalCandles = async (coin, startTime, endTime, interval, displayToFull) => {
5643
- const backendCoin = toBackendSymbol(coin, displayToFull);
6059
+ const fetchHistoricalCandles = async (coin, startTime, endTime, interval, hip3Assets) => {
6060
+ const backendCoin = toBackendSymbol(coin, hip3Assets);
5644
6061
  const request = {
5645
6062
  req: { coin: backendCoin, startTime, endTime, interval },
5646
6063
  type: 'candleSnapshot',
@@ -5799,10 +6216,10 @@ const useHistoricalPriceData = () => {
5799
6216
  setTokenLoading(token.symbol, true);
5800
6217
  });
5801
6218
  try {
5802
- const displayToFull = useHyperliquidData.getState().hip3DisplayToFull;
6219
+ const hip3Assets = useHyperliquidData.getState().hip3Assets;
5803
6220
  const fetchPromises = tokensToFetch.map(async (token) => {
5804
6221
  try {
5805
- const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, displayToFull);
6222
+ const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, hip3Assets);
5806
6223
  addHistoricalPriceData(token.symbol, interval, response.data, { start: startTime, end: endTime });
5807
6224
  return { symbol: token.symbol, candles: response.data, success: true };
5808
6225
  }
@@ -6491,14 +6908,14 @@ function useAutoSyncFills(options) {
6491
6908
  * @throws MinimumPositionSizeError if any asset has less than $11 USD value
6492
6909
  * @throws MaxAssetsPerLegError if any leg exceeds the maximum allowed assets (15)
6493
6910
  */
6494
- async function createPosition(baseUrl, payload, displayToFull) {
6911
+ async function createPosition(baseUrl, payload, hip3Assets) {
6495
6912
  // Validate maximum assets per leg before creating position
6496
6913
  validateMaxAssetsPerLeg(payload.longAssets, payload.shortAssets);
6497
6914
  // Validate minimum asset size before creating position
6498
6915
  validateMinimumAssetSize(payload.usdValue, payload.longAssets, payload.shortAssets);
6499
6916
  const url = joinUrl(baseUrl, "/positions");
6500
6917
  // Translate display symbols to backend format
6501
- const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, displayToFull) }));
6918
+ const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6502
6919
  const translatedPayload = {
6503
6920
  ...payload,
6504
6921
  longAssets: mapAssets(payload.longAssets),
@@ -6597,9 +7014,9 @@ async function adjustPosition(baseUrl, positionId, payload) {
6597
7014
  throw toApiError(error);
6598
7015
  }
6599
7016
  }
6600
- async function adjustAdvancePosition(baseUrl, positionId, payload, displayToFull) {
7017
+ async function adjustAdvancePosition(baseUrl, positionId, payload, hip3Assets) {
6601
7018
  const url = joinUrl(baseUrl, `/positions/${positionId}/adjust-advance`);
6602
- const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, displayToFull) }));
7019
+ const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6603
7020
  const translatedPayload = (payload || []).map((item) => ({
6604
7021
  longAssets: mapAssets(item.longAssets),
6605
7022
  shortAssets: mapAssets(item.shortAssets),
@@ -6682,10 +7099,11 @@ const calculatePositionAsset = (asset, currentPrice, totalInitialPositionSize, l
6682
7099
  positionValue: currentNotional,
6683
7100
  unrealizedPnl: unrealizedPnl,
6684
7101
  entryPositionValue: entryNotional,
6685
- initialWeight: totalInitialPositionSize > 0
6686
- ? entryNotional / totalInitialPositionSize
6687
- : 0,
7102
+ initialWeight: totalInitialPositionSize > 0 ? entryNotional / totalInitialPositionSize : 0,
6688
7103
  fundingPaid: (_a = asset.fundingPaid) !== null && _a !== void 0 ? _a : 0,
7104
+ // Preserve market metadata from raw asset (if provided by backend)
7105
+ marketPrefix: asset.marketPrefix,
7106
+ collateralToken: asset.collateralToken,
6689
7107
  };
6690
7108
  };
6691
7109
  const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
@@ -6774,36 +7192,108 @@ const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
6774
7192
  });
6775
7193
  };
6776
7194
 
7195
+ function findAssetMeta$3(coinName, perpMetaAssets, knownPrefix, desiredCollateral) {
7196
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
7197
+ if (!perpMetaAssets) {
7198
+ return { collateralToken: 'USDC', marketPrefix: null };
7199
+ }
7200
+ if (desiredCollateral) {
7201
+ const collateralMatch = perpMetaAssets.find((a) => a.name === coinName && a.collateralToken === desiredCollateral);
7202
+ if (collateralMatch) {
7203
+ return {
7204
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7205
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7206
+ };
7207
+ }
7208
+ }
7209
+ if (coinName.includes(':')) {
7210
+ const [prefix, symbol] = coinName.split(':');
7211
+ const exactMatch = perpMetaAssets.find((a) => {
7212
+ var _a;
7213
+ return a.name === symbol &&
7214
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
7215
+ });
7216
+ if (exactMatch) {
7217
+ return {
7218
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7219
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7220
+ };
7221
+ }
7222
+ }
7223
+ if (knownPrefix) {
7224
+ const exactMatch = perpMetaAssets.find((a) => {
7225
+ var _a;
7226
+ return a.name === coinName &&
7227
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
7228
+ });
7229
+ if (exactMatch) {
7230
+ return {
7231
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
7232
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
7233
+ };
7234
+ }
7235
+ }
7236
+ const regularAsset = perpMetaAssets.find((a) => a.name === coinName && !a.marketPrefix);
7237
+ if (regularAsset) {
7238
+ return {
7239
+ collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
7240
+ marketPrefix: null,
7241
+ };
7242
+ }
7243
+ const hip3Asset = perpMetaAssets.find((a) => a.name === coinName && a.marketPrefix);
7244
+ if (hip3Asset) {
7245
+ return {
7246
+ collateralToken: (_h = hip3Asset.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
7247
+ marketPrefix: (_j = hip3Asset.marketPrefix) !== null && _j !== void 0 ? _j : null,
7248
+ };
7249
+ }
7250
+ return { collateralToken: 'USDC', marketPrefix: null };
7251
+ }
7252
+ function enrichPositionAssets(assets, perpMetaAssets) {
7253
+ return assets.map((asset) => {
7254
+ var _a;
7255
+ if (asset.marketPrefix && asset.collateralToken) {
7256
+ return asset;
7257
+ }
7258
+ const meta = findAssetMeta$3(asset.coin, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
7259
+ return {
7260
+ ...asset,
7261
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
7262
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
7263
+ };
7264
+ });
7265
+ }
7266
+ function enrichPositions(positions, perpMetaAssets) {
7267
+ return positions.map((position) => ({
7268
+ ...position,
7269
+ longAssets: enrichPositionAssets(position.longAssets, perpMetaAssets),
7270
+ shortAssets: enrichPositionAssets(position.shortAssets, perpMetaAssets),
7271
+ }));
7272
+ }
6777
7273
  function usePosition() {
6778
7274
  const context = useContext(PearHyperliquidContext);
6779
7275
  if (!context) {
6780
7276
  throw new Error('usePosition must be used within a PearHyperliquidProvider');
6781
7277
  }
6782
7278
  const { apiBaseUrl, isConnected } = context;
6783
- const displayToFull = useHyperliquidData((s) => s.hip3DisplayToFull);
6784
- // Create position API action
7279
+ const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
6785
7280
  const createPosition$1 = async (payload) => {
6786
- return createPosition(apiBaseUrl, payload, displayToFull);
7281
+ return createPosition(apiBaseUrl, payload, hip3Assets);
6787
7282
  };
6788
- // Update TP/SL risk parameters for a position
6789
7283
  const updateRiskParameters$1 = async (positionId, payload) => {
6790
7284
  return updateRiskParameters(apiBaseUrl, positionId, payload);
6791
7285
  };
6792
- // Close a position (MARKET or TWAP)
6793
7286
  const closePosition$1 = async (positionId, payload) => {
6794
7287
  return closePosition(apiBaseUrl, positionId, payload);
6795
7288
  };
6796
- // Close all positions (MARKET or TWAP)
6797
7289
  const closeAllPositions$1 = async (payload) => {
6798
7290
  return closeAllPositions(apiBaseUrl, payload);
6799
7291
  };
6800
- // Adjust a position (REDUCE/INCREASE by %; MARKET or LIMIT)
6801
7292
  const adjustPosition$1 = async (positionId, payload) => {
6802
7293
  return adjustPosition(apiBaseUrl, positionId, payload);
6803
7294
  };
6804
- // Adjust to absolute target sizes per asset, optionally adding new assets
6805
7295
  const adjustAdvancePosition$1 = async (positionId, payload) => {
6806
- return adjustAdvancePosition(apiBaseUrl, positionId, payload, displayToFull);
7296
+ return adjustAdvancePosition(apiBaseUrl, positionId, payload, hip3Assets);
6807
7297
  };
6808
7298
  const updateLeverage$1 = async (positionId, leverage) => {
6809
7299
  return updateLeverage(apiBaseUrl, positionId, { leverage });
@@ -6812,22 +7302,46 @@ function usePosition() {
6812
7302
  const userOpenPositions = useUserData((state) => state.rawOpenPositions);
6813
7303
  const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
6814
7304
  const allMids = useHyperliquidData((state) => state.allMids);
7305
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
6815
7306
  const isLoading = useMemo(() => {
6816
7307
  return userOpenPositions === null && isConnected;
6817
7308
  }, [userOpenPositions, isConnected]);
6818
7309
  const openPositions = useMemo(() => {
6819
7310
  if (!userOpenPositions || !aggregatedClearingHouseState || !allMids)
6820
7311
  return null;
6821
- return buildPositionValue(userOpenPositions, aggregatedClearingHouseState, allMids);
6822
- }, [userOpenPositions, aggregatedClearingHouseState, allMids]);
6823
- return { createPosition: createPosition$1, updateRiskParameters: updateRiskParameters$1, closePosition: closePosition$1, closeAllPositions: closeAllPositions$1, adjustPosition: adjustPosition$1, adjustAdvancePosition: adjustAdvancePosition$1, updateLeverage: updateLeverage$1, openPositions, isLoading };
7312
+ const positions = buildPositionValue(userOpenPositions, aggregatedClearingHouseState, allMids);
7313
+ return enrichPositions(positions, allPerpMetaAssets);
7314
+ }, [
7315
+ userOpenPositions,
7316
+ aggregatedClearingHouseState,
7317
+ allMids,
7318
+ allPerpMetaAssets,
7319
+ ]);
7320
+ return {
7321
+ createPosition: createPosition$1,
7322
+ updateRiskParameters: updateRiskParameters$1,
7323
+ closePosition: closePosition$1,
7324
+ closeAllPositions: closeAllPositions$1,
7325
+ adjustPosition: adjustPosition$1,
7326
+ adjustAdvancePosition: adjustAdvancePosition$1,
7327
+ updateLeverage: updateLeverage$1,
7328
+ openPositions,
7329
+ isLoading,
7330
+ };
6824
7331
  }
6825
7332
 
6826
7333
  async function adjustOrder(baseUrl, orderId, payload) {
6827
7334
  const url = joinUrl(baseUrl, `/orders/${orderId}/adjust`);
6828
7335
  try {
6829
- const resp = await apiClient.put(url, payload, { headers: { 'Content-Type': 'application/json' }, timeout: 60000 });
6830
- return { data: resp.data, status: resp.status, headers: resp.headers };
7336
+ const resp = await apiClient.put(url, payload, {
7337
+ headers: { 'Content-Type': 'application/json' },
7338
+ timeout: 60000,
7339
+ });
7340
+ return {
7341
+ data: resp.data,
7342
+ status: resp.status,
7343
+ headers: resp.headers,
7344
+ };
6831
7345
  }
6832
7346
  catch (error) {
6833
7347
  throw toApiError(error);
@@ -6836,8 +7350,14 @@ async function adjustOrder(baseUrl, orderId, payload) {
6836
7350
  async function cancelOrder(baseUrl, orderId) {
6837
7351
  const url = joinUrl(baseUrl, `/orders/${orderId}/cancel`);
6838
7352
  try {
6839
- const resp = await apiClient.delete(url, { timeout: 60000 });
6840
- return { data: resp.data, status: resp.status, headers: resp.headers };
7353
+ const resp = await apiClient.delete(url, {
7354
+ timeout: 60000,
7355
+ });
7356
+ return {
7357
+ data: resp.data,
7358
+ status: resp.status,
7359
+ headers: resp.headers,
7360
+ };
6841
7361
  }
6842
7362
  catch (error) {
6843
7363
  throw toApiError(error);
@@ -6847,19 +7367,129 @@ async function cancelTwapOrder(baseUrl, orderId) {
6847
7367
  const url = joinUrl(baseUrl, `/orders/${orderId}/twap/cancel`);
6848
7368
  try {
6849
7369
  const resp = await apiClient.post(url, {}, { headers: { 'Content-Type': 'application/json' }, timeout: 60000 });
6850
- return { data: resp.data, status: resp.status, headers: resp.headers };
7370
+ return {
7371
+ data: resp.data,
7372
+ status: resp.status,
7373
+ headers: resp.headers,
7374
+ };
7375
+ }
7376
+ catch (error) {
7377
+ throw toApiError(error);
7378
+ }
7379
+ }
7380
+ /**
7381
+ * Execute a spot order (swap) using Pear Hyperliquid service
7382
+ * POST /orders/spot
7383
+ */
7384
+ async function executeSpotOrder(baseUrl, payload) {
7385
+ const url = joinUrl(baseUrl, '/orders/spot');
7386
+ try {
7387
+ const resp = await apiClient.post(url, payload, {
7388
+ headers: { 'Content-Type': 'application/json' },
7389
+ timeout: 60000,
7390
+ });
7391
+ return {
7392
+ data: resp.data,
7393
+ status: resp.status,
7394
+ headers: resp.headers,
7395
+ };
6851
7396
  }
6852
7397
  catch (error) {
6853
7398
  throw toApiError(error);
6854
7399
  }
6855
7400
  }
6856
7401
 
7402
+ function findAssetMeta$2(assetName, perpMetaAssets, knownPrefix, desiredCollateral) {
7403
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
7404
+ if (!perpMetaAssets) {
7405
+ return { collateralToken: 'USDC', marketPrefix: null };
7406
+ }
7407
+ if (desiredCollateral) {
7408
+ const collateralMatch = perpMetaAssets.find((a) => a.name === assetName && a.collateralToken === desiredCollateral);
7409
+ if (collateralMatch) {
7410
+ return {
7411
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7412
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7413
+ };
7414
+ }
7415
+ }
7416
+ if (assetName.includes(':')) {
7417
+ const [prefix, symbol] = assetName.split(':');
7418
+ const exactMatch = perpMetaAssets.find((a) => {
7419
+ var _a;
7420
+ return a.name === symbol &&
7421
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
7422
+ });
7423
+ if (exactMatch) {
7424
+ return {
7425
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7426
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7427
+ };
7428
+ }
7429
+ }
7430
+ if (knownPrefix) {
7431
+ const exactMatch = perpMetaAssets.find((a) => {
7432
+ var _a;
7433
+ return a.name === assetName &&
7434
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
7435
+ });
7436
+ if (exactMatch) {
7437
+ return {
7438
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
7439
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
7440
+ };
7441
+ }
7442
+ }
7443
+ const regularAsset = perpMetaAssets.find((a) => a.name === assetName && !a.marketPrefix);
7444
+ if (regularAsset) {
7445
+ return {
7446
+ collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
7447
+ marketPrefix: null,
7448
+ };
7449
+ }
7450
+ const hip3Assets = perpMetaAssets.filter((a) => a.name === assetName && a.marketPrefix);
7451
+ if (hip3Assets.length > 0) {
7452
+ if (desiredCollateral) {
7453
+ const collateralMatch = hip3Assets.find((a) => a.collateralToken === desiredCollateral);
7454
+ if (collateralMatch) {
7455
+ return {
7456
+ collateralToken: (_h = collateralMatch.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
7457
+ marketPrefix: (_j = collateralMatch.marketPrefix) !== null && _j !== void 0 ? _j : null,
7458
+ };
7459
+ }
7460
+ }
7461
+ const usdHMatch = hip3Assets.find((a) => a.collateralToken === 'USDH');
7462
+ const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Assets[0];
7463
+ return {
7464
+ collateralToken: (_k = chosen.collateralToken) !== null && _k !== void 0 ? _k : 'USDC',
7465
+ marketPrefix: (_l = chosen.marketPrefix) !== null && _l !== void 0 ? _l : null,
7466
+ };
7467
+ }
7468
+ return { collateralToken: 'USDC', marketPrefix: null };
7469
+ }
7470
+ function enrichOrderAssets$1(assets, perpMetaAssets) {
7471
+ if (!assets)
7472
+ return [];
7473
+ return assets.map((asset) => {
7474
+ var _a;
7475
+ if (asset.marketPrefix && asset.collateralToken) {
7476
+ return asset;
7477
+ }
7478
+ const meta = findAssetMeta$2(asset.asset, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
7479
+ return {
7480
+ ...asset,
7481
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
7482
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
7483
+ };
7484
+ });
7485
+ }
6857
7486
  function useOrders() {
6858
7487
  const context = useContext(PearHyperliquidContext);
6859
7488
  if (!context)
6860
7489
  throw new Error('useOrders must be used within a PearHyperliquidProvider');
6861
7490
  const { apiBaseUrl } = context;
6862
7491
  const openOrders = useUserData((state) => state.openOrders);
7492
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
6863
7493
  const isLoading = useMemo(() => openOrders === null && context.isConnected, [openOrders, context.isConnected]);
6864
7494
  const { openPositions } = usePosition();
6865
7495
  const positionsById = useMemo(() => {
@@ -6878,19 +7508,27 @@ function useOrders() {
6878
7508
  const isTpSl = ord.orderType === 'TP' || ord.orderType === 'SL';
6879
7509
  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;
6880
7510
  const pos = positionsById.get((_e = ord.positionId) !== null && _e !== void 0 ? _e : '');
6881
- if (!isTpSl || !pos)
6882
- return ord;
6883
- const mapAssets = (arr) => arr.map((a) => ({ asset: a.coin, weight: a.initialWeight }));
6884
- if (!hasAssets) {
6885
- return {
6886
- ...ord,
7511
+ let enrichedOrd = {
7512
+ ...ord,
7513
+ longAssets: enrichOrderAssets$1(ord.longAssets, allPerpMetaAssets),
7514
+ shortAssets: enrichOrderAssets$1(ord.shortAssets, allPerpMetaAssets),
7515
+ };
7516
+ if (isTpSl && !hasAssets && pos) {
7517
+ const mapAssets = (arr) => arr.map((a) => ({
7518
+ asset: a.coin,
7519
+ weight: a.initialWeight,
7520
+ marketPrefix: a.marketPrefix,
7521
+ collateralToken: a.collateralToken,
7522
+ }));
7523
+ enrichedOrd = {
7524
+ ...enrichedOrd,
6887
7525
  longAssets: mapAssets(pos.longAssets),
6888
7526
  shortAssets: mapAssets(pos.shortAssets),
6889
7527
  };
6890
7528
  }
6891
- return ord;
7529
+ return enrichedOrd;
6892
7530
  });
6893
- }, [openOrders, positionsById]);
7531
+ }, [openOrders, positionsById, allPerpMetaAssets]);
6894
7532
  const adjustOrder$1 = async (orderId, payload) => {
6895
7533
  return adjustOrder(apiBaseUrl, orderId, payload);
6896
7534
  };
@@ -6900,16 +7538,156 @@ function useOrders() {
6900
7538
  const cancelTwapOrder$1 = async (orderId) => {
6901
7539
  return cancelTwapOrder(apiBaseUrl, orderId);
6902
7540
  };
6903
- return { adjustOrder: adjustOrder$1, cancelOrder: cancelOrder$1, cancelTwapOrder: cancelTwapOrder$1, openOrders: enrichedOpenOrders, isLoading };
7541
+ return {
7542
+ adjustOrder: adjustOrder$1,
7543
+ cancelOrder: cancelOrder$1,
7544
+ cancelTwapOrder: cancelTwapOrder$1,
7545
+ openOrders: enrichedOpenOrders,
7546
+ isLoading,
7547
+ };
6904
7548
  }
6905
7549
 
7550
+ /**
7551
+ * Hook for executing spot orders (swaps) on Hyperliquid
7552
+ * Use this to swap between USDC and USDH or other spot assets
7553
+ */
7554
+ function useSpotOrder() {
7555
+ const context = useContext(PearHyperliquidContext);
7556
+ if (!context) {
7557
+ throw new Error('useSpotOrder must be used within a PearHyperliquidProvider');
7558
+ }
7559
+ const { apiBaseUrl } = context;
7560
+ const [isLoading, setIsLoading] = useState(false);
7561
+ const [error, setError] = useState(null);
7562
+ const resetError = useCallback(() => {
7563
+ setError(null);
7564
+ }, []);
7565
+ const executeSpotOrder$1 = useCallback(async (payload) => {
7566
+ setIsLoading(true);
7567
+ setError(null);
7568
+ try {
7569
+ const response = await executeSpotOrder(apiBaseUrl, payload);
7570
+ return response;
7571
+ }
7572
+ catch (err) {
7573
+ const apiError = err;
7574
+ setError(apiError);
7575
+ throw apiError;
7576
+ }
7577
+ finally {
7578
+ setIsLoading(false);
7579
+ }
7580
+ }, [apiBaseUrl]);
7581
+ return {
7582
+ executeSpotOrder: executeSpotOrder$1,
7583
+ isLoading,
7584
+ error,
7585
+ resetError,
7586
+ };
7587
+ }
7588
+
7589
+ function findAssetMeta$1(assetName, perpMetaAssets, knownPrefix, desiredCollateral) {
7590
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
7591
+ if (!perpMetaAssets) {
7592
+ return { collateralToken: 'USDC', marketPrefix: null };
7593
+ }
7594
+ if (desiredCollateral) {
7595
+ const collateralMatch = perpMetaAssets.find((a) => a.name === assetName && a.collateralToken === desiredCollateral);
7596
+ if (collateralMatch) {
7597
+ return {
7598
+ collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7599
+ marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7600
+ };
7601
+ }
7602
+ }
7603
+ if (assetName.includes(':')) {
7604
+ const [prefix, symbol] = assetName.split(':');
7605
+ const exactMatch = perpMetaAssets.find((a) => {
7606
+ var _a;
7607
+ return a.name === symbol &&
7608
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
7609
+ });
7610
+ if (exactMatch) {
7611
+ return {
7612
+ collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7613
+ marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7614
+ };
7615
+ }
7616
+ }
7617
+ if (knownPrefix) {
7618
+ const exactMatch = perpMetaAssets.find((a) => {
7619
+ var _a;
7620
+ return a.name === assetName &&
7621
+ ((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
7622
+ });
7623
+ if (exactMatch) {
7624
+ return {
7625
+ collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
7626
+ marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
7627
+ };
7628
+ }
7629
+ }
7630
+ const regularAsset = perpMetaAssets.find((a) => a.name === assetName && !a.marketPrefix);
7631
+ if (regularAsset) {
7632
+ return {
7633
+ collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
7634
+ marketPrefix: null,
7635
+ };
7636
+ }
7637
+ const hip3Assets = perpMetaAssets.filter((a) => a.name === assetName && a.marketPrefix);
7638
+ if (hip3Assets.length > 0) {
7639
+ if (desiredCollateral) {
7640
+ const collateralMatch = hip3Assets.find((a) => a.collateralToken === desiredCollateral);
7641
+ if (collateralMatch) {
7642
+ return {
7643
+ collateralToken: (_h = collateralMatch.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
7644
+ marketPrefix: (_j = collateralMatch.marketPrefix) !== null && _j !== void 0 ? _j : null,
7645
+ };
7646
+ }
7647
+ }
7648
+ const usdHMatch = hip3Assets.find((a) => a.collateralToken === 'USDH');
7649
+ const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Assets[0];
7650
+ return {
7651
+ collateralToken: (_k = chosen.collateralToken) !== null && _k !== void 0 ? _k : 'USDC',
7652
+ marketPrefix: (_l = chosen.marketPrefix) !== null && _l !== void 0 ? _l : null,
7653
+ };
7654
+ }
7655
+ return { collateralToken: 'USDC', marketPrefix: null };
7656
+ }
7657
+ function enrichOrderAssets(assets, perpMetaAssets) {
7658
+ if (!assets)
7659
+ return [];
7660
+ return assets.map((asset) => {
7661
+ var _a;
7662
+ if (asset.marketPrefix && asset.collateralToken) {
7663
+ return asset;
7664
+ }
7665
+ const meta = findAssetMeta$1(asset.asset, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
7666
+ return {
7667
+ ...asset,
7668
+ marketPrefix: asset.marketPrefix || meta.marketPrefix,
7669
+ collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
7670
+ };
7671
+ });
7672
+ }
7673
+ function enrichTwapOrders(orders, perpMetaAssets) {
7674
+ return orders.map((order) => ({
7675
+ ...order,
7676
+ longAssets: enrichOrderAssets(order.longAssets, perpMetaAssets),
7677
+ shortAssets: enrichOrderAssets(order.shortAssets, perpMetaAssets),
7678
+ }));
7679
+ }
6906
7680
  function useTwap() {
6907
- const twapDetails = useUserData(state => state.twapDetails);
7681
+ const twapDetails = useUserData((state) => state.twapDetails);
7682
+ const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
6908
7683
  const context = useContext(PearHyperliquidContext);
6909
7684
  if (!context)
6910
7685
  throw new Error('useTwap must be used within a PearHyperliquidProvider');
6911
7686
  const { apiBaseUrl } = context;
6912
- const orders = useMemo(() => twapDetails !== null && twapDetails !== void 0 ? twapDetails : [], [twapDetails]);
7687
+ const orders = useMemo(() => {
7688
+ const rawOrders = twapDetails !== null && twapDetails !== void 0 ? twapDetails : [];
7689
+ return enrichTwapOrders(rawOrders, allPerpMetaAssets);
7690
+ }, [twapDetails, allPerpMetaAssets]);
6913
7691
  const cancelTwap$1 = async (orderId) => {
6914
7692
  return cancelTwap(apiBaseUrl, orderId);
6915
7693
  };
@@ -6992,59 +7770,170 @@ function useNotifications() {
6992
7770
  };
6993
7771
  }
6994
7772
 
6995
- // Base selector for the full market-data payload
7773
+ // Helper to find asset metadata from perpMetaAssets
7774
+ function findAssetMeta(assetName, perpMetaAssets) {
7775
+ var _a, _b, _c, _d;
7776
+ if (!perpMetaAssets) {
7777
+ return { collateralToken: 'USDC', marketPrefix: null };
7778
+ }
7779
+ // Try exact match first (for prefixed assets like "xyz:TSLA")
7780
+ const exactMatch = perpMetaAssets.find((a) => a.name === assetName);
7781
+ if (exactMatch) {
7782
+ return {
7783
+ collateralToken: (_a = exactMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7784
+ marketPrefix: (_b = exactMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7785
+ };
7786
+ }
7787
+ // Try matching by base symbol (for non-prefixed names in data)
7788
+ const baseMatch = perpMetaAssets.find((a) => {
7789
+ const baseName = a.name.includes(':') ? a.name.split(':')[1] : a.name;
7790
+ return baseName === assetName;
7791
+ });
7792
+ if (baseMatch) {
7793
+ return {
7794
+ collateralToken: (_c = baseMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7795
+ marketPrefix: (_d = baseMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7796
+ };
7797
+ }
7798
+ return { collateralToken: 'USDC', marketPrefix: null };
7799
+ }
7800
+ // Enrich a single asset with metadata
7801
+ function enrichAsset(asset, perpMetaAssets) {
7802
+ const meta = findAssetMeta(asset.asset, perpMetaAssets);
7803
+ return {
7804
+ ...asset,
7805
+ collateralToken: meta.collateralToken,
7806
+ marketPrefix: meta.marketPrefix,
7807
+ };
7808
+ }
7809
+ // Enrich a basket item with collateral info
7810
+ function enrichBasketItem(item, perpMetaAssets) {
7811
+ const enrichedLongs = item.longAssets.map((a) => enrichAsset(a, perpMetaAssets));
7812
+ const enrichedShorts = item.shortAssets.map((a) => enrichAsset(a, perpMetaAssets));
7813
+ // Determine collateral type
7814
+ const allAssets = [...enrichedLongs, ...enrichedShorts];
7815
+ const hasUsdc = allAssets.some((a) => a.collateralToken === 'USDC');
7816
+ const hasUsdh = allAssets.some((a) => a.collateralToken === 'USDH');
7817
+ let collateralType = 'USDC';
7818
+ if (hasUsdc && hasUsdh) {
7819
+ collateralType = 'MIXED';
7820
+ }
7821
+ else if (hasUsdh) {
7822
+ collateralType = 'USDH';
7823
+ }
7824
+ return {
7825
+ ...item,
7826
+ longAssets: enrichedLongs,
7827
+ shortAssets: enrichedShorts,
7828
+ collateralType,
7829
+ };
7830
+ }
7831
+ /**
7832
+ * Filter baskets by collateral type
7833
+ * - 'USDC': Only baskets where ALL assets use USDC (collateralType === 'USDC')
7834
+ * - 'USDH': Only baskets where ALL assets use USDH (collateralType === 'USDH')
7835
+ * - 'ALL' or undefined: No filtering, returns all baskets
7836
+ */
7837
+ function filterByCollateral(baskets, filter) {
7838
+ if (!filter || filter === 'ALL') {
7839
+ return baskets;
7840
+ }
7841
+ return baskets.filter((basket) => {
7842
+ if (filter === 'USDC') {
7843
+ // Include baskets that are purely USDC or have USDC assets
7844
+ return (basket.collateralType === 'USDC' || basket.collateralType === 'MIXED');
7845
+ }
7846
+ if (filter === 'USDH') {
7847
+ // Include baskets that are purely USDH or have USDH assets
7848
+ return (basket.collateralType === 'USDH' || basket.collateralType === 'MIXED');
7849
+ }
7850
+ return true;
7851
+ });
7852
+ }
7853
+ // Base selector for the full market-data payload (raw from WS)
6996
7854
  const useMarketDataPayload = () => {
6997
7855
  return useMarketData((s) => s.marketData);
6998
7856
  };
6999
- // Full payload for 'market-data-all' channel
7857
+ // Full payload for 'market-data-all' channel (raw from WS)
7000
7858
  const useMarketDataAllPayload = () => {
7001
7859
  return useMarketData((s) => s.marketDataAll);
7002
7860
  };
7003
- // Active baskets
7004
- const useActiveBaskets = () => {
7005
- var _a;
7861
+ // Access perpMetaAssets for enrichment
7862
+ const usePerpMetaAssets = () => {
7863
+ return useHyperliquidData((s) => s.perpMetaAssets);
7864
+ };
7865
+ // Active baskets (with collateral and market prefix info)
7866
+ const useActiveBaskets = (collateralFilter) => {
7006
7867
  const data = useMarketDataPayload();
7007
- return (_a = data === null || data === void 0 ? void 0 : data.active) !== null && _a !== void 0 ? _a : [];
7868
+ const perpMetaAssets = usePerpMetaAssets();
7869
+ return useMemo(() => {
7870
+ if (!(data === null || data === void 0 ? void 0 : data.active))
7871
+ return [];
7872
+ const enriched = data.active.map((item) => enrichBasketItem(item, perpMetaAssets));
7873
+ return filterByCollateral(enriched, collateralFilter);
7874
+ }, [data, perpMetaAssets, collateralFilter]);
7008
7875
  };
7009
- // Top gainers (optional limit override)
7010
- const useTopGainers = (limit) => {
7876
+ // Top gainers (with collateral and market prefix info)
7877
+ const useTopGainers = (limit, collateralFilter) => {
7011
7878
  const data = useMarketDataPayload();
7879
+ const perpMetaAssets = usePerpMetaAssets();
7012
7880
  return useMemo(() => {
7013
7881
  var _a;
7014
7882
  const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
7015
- return typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7016
- }, [data, limit]);
7883
+ const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7884
+ const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
7885
+ return filterByCollateral(enriched, collateralFilter);
7886
+ }, [data, perpMetaAssets, limit, collateralFilter]);
7017
7887
  };
7018
- // Top losers (optional limit override)
7019
- const useTopLosers = (limit) => {
7888
+ // Top losers (with collateral and market prefix info)
7889
+ const useTopLosers = (limit, collateralFilter) => {
7020
7890
  const data = useMarketDataPayload();
7891
+ const perpMetaAssets = usePerpMetaAssets();
7021
7892
  return useMemo(() => {
7022
7893
  var _a;
7023
7894
  const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
7024
- return typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7025
- }, [data, limit]);
7895
+ const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7896
+ const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
7897
+ return filterByCollateral(enriched, collateralFilter);
7898
+ }, [data, perpMetaAssets, limit, collateralFilter]);
7026
7899
  };
7027
- // Highlighted baskets
7028
- const useHighlightedBaskets = () => {
7029
- var _a;
7900
+ // Highlighted baskets (with collateral and market prefix info)
7901
+ const useHighlightedBaskets = (collateralFilter) => {
7030
7902
  const data = useMarketDataPayload();
7031
- return (_a = data === null || data === void 0 ? void 0 : data.highlighted) !== null && _a !== void 0 ? _a : [];
7903
+ const perpMetaAssets = usePerpMetaAssets();
7904
+ return useMemo(() => {
7905
+ if (!(data === null || data === void 0 ? void 0 : data.highlighted))
7906
+ return [];
7907
+ const enriched = data.highlighted.map((item) => enrichBasketItem(item, perpMetaAssets));
7908
+ return filterByCollateral(enriched, collateralFilter);
7909
+ }, [data, perpMetaAssets, collateralFilter]);
7032
7910
  };
7033
- // Watchlist baskets (from market-data payload when subscribed with address)
7034
- const useWatchlistBaskets = () => {
7035
- var _a;
7911
+ // Watchlist baskets (with collateral and market prefix info)
7912
+ const useWatchlistBaskets = (collateralFilter) => {
7036
7913
  const data = useMarketDataPayload();
7037
- return (_a = data === null || data === void 0 ? void 0 : data.watchlist) !== null && _a !== void 0 ? _a : [];
7914
+ const perpMetaAssets = usePerpMetaAssets();
7915
+ return useMemo(() => {
7916
+ if (!(data === null || data === void 0 ? void 0 : data.watchlist))
7917
+ return [];
7918
+ const enriched = data.watchlist.map((item) => enrichBasketItem(item, perpMetaAssets));
7919
+ return filterByCollateral(enriched, collateralFilter);
7920
+ }, [data, perpMetaAssets, collateralFilter]);
7038
7921
  };
7039
- // All baskets (from market-data-all)
7040
- const useAllBaskets = () => {
7041
- var _a;
7922
+ // All baskets (with collateral and market prefix info)
7923
+ const useAllBaskets = (collateralFilter) => {
7042
7924
  const dataAll = useMarketDataAllPayload();
7043
- return (_a = dataAll === null || dataAll === void 0 ? void 0 : dataAll.all) !== null && _a !== void 0 ? _a : [];
7925
+ const perpMetaAssets = usePerpMetaAssets();
7926
+ return useMemo(() => {
7927
+ if (!(dataAll === null || dataAll === void 0 ? void 0 : dataAll.all))
7928
+ return [];
7929
+ const enriched = dataAll.all.map((item) => enrichBasketItem(item, perpMetaAssets));
7930
+ return filterByCollateral(enriched, collateralFilter);
7931
+ }, [dataAll, perpMetaAssets, collateralFilter]);
7044
7932
  };
7045
7933
  // Find a basket by its exact asset composition (order-insensitive)
7046
7934
  const useFindBasket = (longs, shorts) => {
7047
7935
  const data = useMarketDataPayload();
7936
+ const perpMetaAssets = usePerpMetaAssets();
7048
7937
  return useMemo(() => {
7049
7938
  if (!data)
7050
7939
  return undefined;
@@ -7058,17 +7947,28 @@ const useFindBasket = (longs, shorts) => {
7058
7947
  : '';
7059
7948
  const lKey = normalize(longs);
7060
7949
  const sKey = normalize(shorts);
7061
- const match = (item) => normalize(item.longAssets) === lKey && normalize(item.shortAssets) === sKey;
7062
- return data.active.find(match) || data.highlighted.find(match);
7063
- }, [data, longs, shorts]);
7950
+ const match = (item) => normalize(item.longAssets) === lKey &&
7951
+ normalize(item.shortAssets) === sKey;
7952
+ const found = data.active.find(match) || data.highlighted.find(match);
7953
+ return found
7954
+ ? enrichBasketItem(found, perpMetaAssets)
7955
+ : undefined;
7956
+ }, [data, longs, shorts, perpMetaAssets]);
7064
7957
  };
7065
7958
 
7066
- async function toggleWatchlist(baseUrl, longAssets, shortAssets, displayToFull) {
7959
+ async function toggleWatchlist(baseUrl, longAssets, shortAssets, hip3Assets) {
7067
7960
  const url = joinUrl(baseUrl, '/watchlist');
7068
- const mapAssets = (arr) => arr.map(a => ({ ...a, asset: toBackendSymbol(a.asset, displayToFull) }));
7961
+ const mapAssets = (arr) => arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
7069
7962
  try {
7070
- const response = await apiClient.post(url, { longAssets: mapAssets(longAssets), shortAssets: mapAssets(shortAssets) }, { headers: { 'Content-Type': 'application/json' } });
7071
- return { data: response.data, status: response.status, headers: response.headers };
7963
+ const response = await apiClient.post(url, {
7964
+ longAssets: mapAssets(longAssets),
7965
+ shortAssets: mapAssets(shortAssets),
7966
+ }, { headers: { 'Content-Type': 'application/json' } });
7967
+ return {
7968
+ data: response.data,
7969
+ status: response.status,
7970
+ headers: response.headers,
7971
+ };
7072
7972
  }
7073
7973
  catch (error) {
7074
7974
  throw toApiError(error);
@@ -7080,11 +7980,11 @@ function useWatchlist() {
7080
7980
  if (!context)
7081
7981
  throw new Error('useWatchlist must be used within a PearHyperliquidProvider');
7082
7982
  const { apiBaseUrl, isConnected } = context;
7083
- const displayToFull = useHyperliquidData((s) => s.hip3DisplayToFull);
7983
+ const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
7084
7984
  const marketData = useMarketDataPayload();
7085
7985
  const isLoading = useMemo(() => !marketData && isConnected, [marketData, isConnected]);
7086
7986
  const toggle = async (longAssets, shortAssets) => {
7087
- const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, displayToFull);
7987
+ const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, hip3Assets);
7088
7988
  // Server will push updated market-data over WS; nothing to set here
7089
7989
  return resp;
7090
7990
  };
@@ -7206,46 +8106,38 @@ async function logout(baseUrl, refreshTokenVal) {
7206
8106
  function useAuth() {
7207
8107
  const context = useContext(PearHyperliquidContext);
7208
8108
  if (!context) {
7209
- throw new Error("useAuth must be used within a PearHyperliquidProvider");
8109
+ throw new Error("usePortfolio must be used within a PearHyperliquidProvider");
7210
8110
  }
7211
8111
  const { apiBaseUrl, clientId } = context;
8112
+ const [isReady, setIsReady] = useState(false);
7212
8113
  const accessToken = useUserData((s) => s.accessToken);
7213
8114
  const refreshToken$1 = useUserData((s) => s.refreshToken);
7214
- const isReady = useUserData((s) => s.isReady);
7215
- const isAuthenticated = useUserData((s) => s.isAuthenticated);
7216
- const address = useUserData((s) => s.address);
7217
8115
  const setAccessToken = useUserData((s) => s.setAccessToken);
7218
8116
  const setRefreshToken = useUserData((s) => s.setRefreshToken);
7219
- const setIsReady = useUserData((s) => s.setIsReady);
8117
+ const isAuthenticated = useUserData((s) => s.isAuthenticated);
7220
8118
  const setIsAuthenticated = useUserData((s) => s.setIsAuthenticated);
7221
8119
  const setAddress = useUserData((s) => s.setAddress);
7222
8120
  useEffect(() => {
7223
8121
  if (typeof window == "undefined") {
7224
8122
  return;
7225
8123
  }
7226
- if (address) {
7227
- // If we already have an address in state, use it to load the session
7228
- const accessTokenKey = `${address}_accessToken`;
7229
- const refreshTokenKey = `${address}_refreshToken`;
7230
- const storedAccessToken = localStorage.getItem(accessTokenKey);
7231
- const storedRefreshToken = localStorage.getItem(refreshTokenKey);
7232
- console.log({ storedAccessToken, storedRefreshToken });
7233
- if (storedAccessToken && storedRefreshToken) {
7234
- setAccessToken(storedAccessToken);
7235
- setRefreshToken(storedRefreshToken);
7236
- setIsAuthenticated(true);
7237
- }
7238
- }
8124
+ const access = localStorage.getItem("accessToken");
8125
+ const refresh = localStorage.getItem("refreshToken");
8126
+ const addr = localStorage.getItem("address");
8127
+ setAccessToken(access);
8128
+ setRefreshToken(refresh);
8129
+ setAddress(addr);
8130
+ const authed = Boolean(access && addr);
8131
+ setIsAuthenticated(authed);
7239
8132
  setIsReady(true);
7240
- }, [address]);
8133
+ }, [setAccessToken, setRefreshToken, setIsAuthenticated, setAddress]);
7241
8134
  useEffect(() => {
7242
8135
  const cleanup = addAuthInterceptors({
7243
8136
  apiBaseUrl,
7244
8137
  getAccessToken: () => {
7245
- if (typeof window === "undefined")
7246
- return null;
7247
- // Read from Zustand state as single source of truth
7248
- return useUserData.getState().accessToken;
8138
+ return typeof window !== "undefined"
8139
+ ? window.localStorage.getItem("accessToken")
8140
+ : null;
7249
8141
  },
7250
8142
  refreshTokens: async () => {
7251
8143
  const data = await refreshTokens();
@@ -7271,12 +8163,12 @@ function useAuth() {
7271
8163
  clientId,
7272
8164
  details: { signature, timestamp },
7273
8165
  });
7274
- const accessTokenKey = `${address}_accessToken`;
7275
- const refreshTokenKey = `${address}_refreshToken`;
7276
- window.localStorage.setItem(accessTokenKey, data.accessToken);
7277
- window.localStorage.setItem(refreshTokenKey, data.refreshToken);
8166
+ window.localStorage.setItem("accessToken", data.accessToken);
8167
+ window.localStorage.setItem("refreshToken", data.refreshToken);
8168
+ window.localStorage.setItem("address", address);
7278
8169
  setAccessToken(data.accessToken);
7279
8170
  setRefreshToken(data.refreshToken);
8171
+ setAddress(address);
7280
8172
  setIsAuthenticated(true);
7281
8173
  }
7282
8174
  catch (e) {
@@ -7291,12 +8183,12 @@ function useAuth() {
7291
8183
  appId,
7292
8184
  accessToken: privyAccessToken,
7293
8185
  });
7294
- const accessTokenKey = `${address}_accessToken`;
7295
- const refreshTokenKey = `${address}_refreshToken`;
7296
- window.localStorage.setItem(accessTokenKey, data.accessToken);
7297
- window.localStorage.setItem(refreshTokenKey, data.refreshToken);
8186
+ window.localStorage.setItem("accessToken", data.accessToken);
8187
+ window.localStorage.setItem("refreshToken", data.refreshToken);
8188
+ window.localStorage.setItem("address", address);
7298
8189
  setAccessToken(data.accessToken);
7299
8190
  setRefreshToken(data.refreshToken);
8191
+ setAddress(address);
7300
8192
  setIsAuthenticated(true);
7301
8193
  }
7302
8194
  catch (e) {
@@ -7304,38 +8196,30 @@ function useAuth() {
7304
8196
  }
7305
8197
  }
7306
8198
  async function refreshTokens() {
7307
- const currentAddress = address;
7308
- const currentRefresh = refreshToken$1;
7309
- if (!currentRefresh || !currentAddress)
8199
+ const refresh = window.localStorage.getItem("refreshToken");
8200
+ if (!refresh)
7310
8201
  throw new Error("No refresh token");
7311
- const { data } = await refreshToken(apiBaseUrl, currentRefresh);
7312
- // Update tokens in localStorage
7313
- const accessTokenKey = `${currentAddress}_accessToken`;
7314
- const refreshTokenKey = `${currentAddress}_refreshToken`;
7315
- window.localStorage.setItem(accessTokenKey, data.accessToken);
7316
- window.localStorage.setItem(refreshTokenKey, data.refreshToken);
8202
+ const { data } = await refreshToken(apiBaseUrl, refresh);
8203
+ window.localStorage.setItem("accessToken", data.accessToken);
8204
+ window.localStorage.setItem("refreshToken", data.refreshToken);
7317
8205
  setAccessToken(data.accessToken);
7318
8206
  setRefreshToken(data.refreshToken);
7319
8207
  setIsAuthenticated(true);
7320
8208
  return data;
7321
8209
  }
7322
8210
  async function logout$1() {
7323
- const currentAddress = address;
7324
- const currentRefresh = refreshToken$1;
7325
- if (currentRefresh) {
8211
+ const refresh = window.localStorage.getItem("refreshToken");
8212
+ if (refresh) {
7326
8213
  try {
7327
- await logout(apiBaseUrl, currentRefresh);
8214
+ await logout(apiBaseUrl, refresh);
7328
8215
  }
7329
8216
  catch (_a) {
7330
8217
  /* ignore */
7331
8218
  }
7332
8219
  }
7333
- if (currentAddress) {
7334
- const accessTokenKey = `${currentAddress}_accessToken`;
7335
- const refreshTokenKey = `${currentAddress}_refreshToken`;
7336
- window.localStorage.removeItem(accessTokenKey);
7337
- window.localStorage.removeItem(refreshTokenKey);
7338
- }
8220
+ window.localStorage.removeItem("accessToken");
8221
+ window.localStorage.removeItem("refreshToken");
8222
+ window.localStorage.removeItem("address");
7339
8223
  setAccessToken(null);
7340
8224
  setRefreshToken(null);
7341
8225
  setAddress(null);
@@ -7351,20 +8235,114 @@ function useAuth() {
7351
8235
  loginWithPrivyToken,
7352
8236
  refreshTokens,
7353
8237
  logout: logout$1,
7354
- setAddress,
7355
- address,
7356
8238
  };
7357
8239
  }
7358
8240
 
8241
+ const useAllUserBalances = () => {
8242
+ const spotState = useUserData((state) => state.spotState);
8243
+ const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
8244
+ const rawClearinghouseStates = useHyperliquidData((state) => state.rawClearinghouseStates);
8245
+ const activeAssetData = useHyperliquidData((state) => state.activeAssetData);
8246
+ const { longTokensMetadata, shortTokensMetadata } = useTokenSelectionMetadata();
8247
+ return useMemo(() => {
8248
+ const isLoading = !spotState || !aggregatedClearingHouseState;
8249
+ // Helper function to truncate to 2 decimal places without rounding
8250
+ const truncateToTwoDecimals = (value) => {
8251
+ return Math.floor(value * 100) / 100;
8252
+ };
8253
+ // Get spot balances from spotState
8254
+ let spotUsdcBal = undefined;
8255
+ let spotUsdhBal = undefined;
8256
+ if (spotState) {
8257
+ const balances = spotState.balances || [];
8258
+ for (const balance of balances) {
8259
+ const total = parseFloat(balance.total || '0');
8260
+ if (balance.coin === 'USDC') {
8261
+ spotUsdcBal = truncateToTwoDecimals(total);
8262
+ }
8263
+ if (balance.coin === 'USDH') {
8264
+ spotUsdhBal = truncateToTwoDecimals(total);
8265
+ }
8266
+ }
8267
+ }
8268
+ let availableToTradeUsdhFromAsset = 0;
8269
+ // This activeAssetData only contains data for SELECTED tokens (user's long and short Tokens)
8270
+ // It does NOT contain data for all tokens, so we cannot reliably use it for available to trade as used on hl trade page
8271
+ // so intead, we rely on rawClearinghouseStates which provides market-specific data
8272
+ // if (activeAssetData) {
8273
+ // Object.values(activeAssetData).forEach((assetData) => {
8274
+ // if (!assetData.availableToTrade) return;
8275
+ // const coinSymbol = assetData.coin;
8276
+ // const availableValue = truncateToTwoDecimals(
8277
+ // parseFloat(assetData.availableToTrade[0] || '0'),
8278
+ // );
8279
+ // // Determine collateral type based on market prefix
8280
+ // // HIP3 markets have prefix: "xyz:SYMBOL", "flx:SYMBOL", "vntl:SYMBOL", etc.
8281
+ // if (coinSymbol.includes(':')) {
8282
+ // const prefix = coinSymbol.split(':')[0];
8283
+ // if (prefix === 'xyz') {
8284
+ // // xyz markets use USDC
8285
+ // availableToTradeUsdcFromAsset = availableValue;
8286
+ // } else {
8287
+ // // flx, vntl, hyna and other markets use USDH
8288
+ // availableToTradeUsdhFromAsset = availableValue;
8289
+ // }
8290
+ // } else {
8291
+ // // Regular markets without prefix are automatically USDC
8292
+ // availableToTradeUsdcFromAsset = availableValue;
8293
+ // }
8294
+ // });
8295
+ // }
8296
+ // Calculate USDC available to trade
8297
+ // Priority 1: Use value from activeAssetData if available (> 0)
8298
+ // Priority 2: Calculate from USDC-specific clearinghouseState (empty prefix)
8299
+ let availableToTradeUsdcValue = undefined;
8300
+ if (rawClearinghouseStates) {
8301
+ // Find USDC market (empty prefix)
8302
+ const usdcMarket = rawClearinghouseStates.find(([prefix]) => prefix === '');
8303
+ const usdcState = usdcMarket === null || usdcMarket === void 0 ? void 0 : usdcMarket[1];
8304
+ if (usdcState === null || usdcState === void 0 ? void 0 : usdcState.marginSummary) {
8305
+ const accountValue = parseFloat(usdcState.marginSummary.accountValue || '0');
8306
+ const totalMarginUsed = parseFloat(usdcState.marginSummary.totalMarginUsed || '0');
8307
+ const calculatedValue = Math.max(0, accountValue - totalMarginUsed);
8308
+ availableToTradeUsdcValue = truncateToTwoDecimals(calculatedValue);
8309
+ }
8310
+ }
8311
+ // Calculate USDH available to trade
8312
+ // Priority 1: Use value from activeAssetData if available (> 0)
8313
+ // Priority 2: Use spot USDH balance
8314
+ const availableToTradeUsdhValue = availableToTradeUsdhFromAsset > 0
8315
+ ? availableToTradeUsdhFromAsset
8316
+ : spotUsdhBal;
8317
+ return {
8318
+ spotUsdcBalance: spotUsdcBal,
8319
+ availableToTradeUsdc: availableToTradeUsdcValue,
8320
+ spotUsdhBalance: spotUsdhBal,
8321
+ availableToTradeUsdh: availableToTradeUsdhValue,
8322
+ isLoading,
8323
+ };
8324
+ }, [
8325
+ spotState,
8326
+ aggregatedClearingHouseState,
8327
+ rawClearinghouseStates,
8328
+ activeAssetData,
8329
+ longTokensMetadata,
8330
+ shortTokensMetadata,
8331
+ ]);
8332
+ };
8333
+
7359
8334
  const PearHyperliquidContext = createContext(undefined);
7360
8335
  /**
7361
8336
  * React Provider for PearHyperliquidClient
7362
8337
  */
7363
- const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearprotocol.io", clientId = "PEARPROTOCOLUI", wsUrl = "wss://hl-ui.pearprotocol.io/ws", }) => {
8338
+ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearprotocol.io', clientId = 'PEARPROTOCOLUI', wsUrl = 'wss://hl-ui.pearprotocol.io/ws', }) => {
7364
8339
  const address = useUserData((s) => s.address);
8340
+ const setAddress = useUserData((s) => s.setAddress);
7365
8341
  const perpsMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
7366
8342
  const setPerpMetaAssets = useHyperliquidData((state) => state.setPerpMetaAssets);
7367
- const setHip3DisplayToFull = useHyperliquidData((state) => state.setHip3DisplayToFull);
8343
+ const setAllPerpMetaAssets = useHyperliquidData((state) => state.setAllPerpMetaAssets);
8344
+ const setHip3Assets = useHyperliquidData((state) => state.setHip3Assets);
8345
+ const setHip3MarketPrefixes = useHyperliquidData((state) => state.setHip3MarketPrefixes);
7368
8346
  const websocketsEnabled = useMemo(() => Array.isArray(perpsMetaAssets) && perpsMetaAssets.length > 0, [perpsMetaAssets]);
7369
8347
  const { isConnected, lastError } = useHyperliquidWebSocket({
7370
8348
  wsUrl,
@@ -7379,32 +8357,107 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearpro
7379
8357
  if (perpsMetaAssets === null) {
7380
8358
  fetchAllPerpMetas()
7381
8359
  .then((res) => {
7382
- // Only show HL and XYZ for now as other are using USDH collateral and need more work
7383
- const aggregatedPerpMetas = res.data
7384
- .slice(0, 2)
7385
- .flatMap((item) => item.universe);
7386
- const hip3Map = new Map();
7387
- const displayToFull = new Map();
7388
- const cleanedPerpMetas = aggregatedPerpMetas.map((asset) => {
7389
- var _a;
7390
- const [maybePrefix, maybeMarket] = asset.name.split(":");
7391
- if (maybeMarket) {
7392
- const prefix = maybePrefix.toLowerCase();
7393
- const market = maybeMarket;
7394
- const existing = (_a = hip3Map.get(prefix)) !== null && _a !== void 0 ? _a : [];
7395
- hip3Map.set(prefix, [...existing, market]);
7396
- displayToFull.set(market, `${prefix}:${market}`);
7397
- return { ...asset, name: market };
7398
- }
7399
- return asset;
8360
+ const assetToMarkets = new Map();
8361
+ const marketPrefixes = new Map();
8362
+ const FILTERED_PREFIXES = ['hyna'];
8363
+ // Group assets by market prefix to match WebSocket flattening order
8364
+ // WebSocket sends in order: "", "flx", "hyna", "vntl", "xyz" (we filter out hyna)
8365
+ const assetsByPrefix = new Map();
8366
+ const allAssetsByPrefix = new Map();
8367
+ res.data.forEach((item) => {
8368
+ const collateralToken = item.collateralToken === 360 ? 'USDH' : 'USDC';
8369
+ item.universe.forEach((asset) => {
8370
+ var _a;
8371
+ const [maybePrefix, maybeMarket] = asset.name.split(':');
8372
+ if (maybeMarket) {
8373
+ // HIP3 asset with market prefix
8374
+ const prefix = maybePrefix.toLowerCase();
8375
+ const displayName = maybeMarket;
8376
+ const fullName = `${prefix}:${displayName}`;
8377
+ marketPrefixes.set(fullName, prefix);
8378
+ if (!FILTERED_PREFIXES.includes(prefix)) {
8379
+ const existingMarkets = (_a = assetToMarkets.get(displayName)) !== null && _a !== void 0 ? _a : [];
8380
+ if (!existingMarkets.includes(fullName)) {
8381
+ assetToMarkets.set(displayName, [
8382
+ ...existingMarkets,
8383
+ fullName,
8384
+ ]);
8385
+ }
8386
+ }
8387
+ const assetWithMeta = {
8388
+ ...asset,
8389
+ name: displayName,
8390
+ marketPrefix: prefix,
8391
+ collateralToken,
8392
+ };
8393
+ // Group by market prefix
8394
+ const allList = allAssetsByPrefix.get(prefix) || [];
8395
+ allList.push(assetWithMeta);
8396
+ allAssetsByPrefix.set(prefix, allList);
8397
+ if (!FILTERED_PREFIXES.includes(prefix)) {
8398
+ const cleanedList = assetsByPrefix.get(prefix) || [];
8399
+ cleanedList.push(assetWithMeta);
8400
+ assetsByPrefix.set(prefix, cleanedList);
8401
+ }
8402
+ }
8403
+ else {
8404
+ // Default market asset (no prefix)
8405
+ const assetWithMeta = {
8406
+ ...asset,
8407
+ collateralToken,
8408
+ };
8409
+ // Add to default market group ("")
8410
+ const defaultList = assetsByPrefix.get('') || [];
8411
+ defaultList.push(assetWithMeta);
8412
+ assetsByPrefix.set('', defaultList);
8413
+ const allDefaultList = allAssetsByPrefix.get('') || [];
8414
+ allDefaultList.push(assetWithMeta);
8415
+ allAssetsByPrefix.set('', allDefaultList);
8416
+ }
8417
+ });
8418
+ });
8419
+ // Flatten in consistent order: default market first, then HIP3 markets alphabetically
8420
+ // This ensures both REST API and WebSocket data align properly
8421
+ const cleanedPrefixes = Array.from(assetsByPrefix.keys()).sort((a, b) => {
8422
+ // Empty prefix (default market) always comes first
8423
+ if (a === '' && b !== '')
8424
+ return -1;
8425
+ if (a !== '' && b === '')
8426
+ return 1;
8427
+ // HIP3 markets sorted alphabetically
8428
+ return a.localeCompare(b);
8429
+ });
8430
+ const allPrefixes = Array.from(allAssetsByPrefix.keys()).sort((a, b) => {
8431
+ if (a === '' && b !== '')
8432
+ return -1;
8433
+ if (a !== '' && b === '')
8434
+ return 1;
8435
+ return a.localeCompare(b);
8436
+ });
8437
+ const cleanedPerpMetas = [];
8438
+ const allPerpMetas = [];
8439
+ cleanedPrefixes.forEach((prefix) => {
8440
+ const assets = assetsByPrefix.get(prefix) || [];
8441
+ cleanedPerpMetas.push(...assets);
7400
8442
  });
7401
- setHip3DisplayToFull(displayToFull);
8443
+ allPrefixes.forEach((prefix) => {
8444
+ const assets = allAssetsByPrefix.get(prefix) || [];
8445
+ allPerpMetas.push(...assets);
8446
+ });
8447
+ setHip3Assets(assetToMarkets);
8448
+ setHip3MarketPrefixes(marketPrefixes);
7402
8449
  setPerpMetaAssets(cleanedPerpMetas);
8450
+ setAllPerpMetaAssets(allPerpMetas);
7403
8451
  })
7404
8452
  .catch(() => { });
7405
8453
  }
7406
- }, [perpsMetaAssets, setPerpMetaAssets, setHip3DisplayToFull]);
7407
- // Auth methods now sourced from useAuth hook
8454
+ }, [
8455
+ perpsMetaAssets,
8456
+ setPerpMetaAssets,
8457
+ setAllPerpMetaAssets,
8458
+ setHip3Assets,
8459
+ setHip3MarketPrefixes,
8460
+ ]);
7408
8461
  useAutoSyncFills({
7409
8462
  baseUrl: apiBaseUrl,
7410
8463
  address,
@@ -7426,6 +8479,8 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearpro
7426
8479
  }), [
7427
8480
  apiBaseUrl,
7428
8481
  wsUrl,
8482
+ address,
8483
+ setAddress,
7429
8484
  isConnected,
7430
8485
  lastError,
7431
8486
  nativeIsConnected,
@@ -7440,7 +8495,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearpro
7440
8495
  function usePearHyperliquid() {
7441
8496
  const ctx = useContext(PearHyperliquidContext);
7442
8497
  if (!ctx)
7443
- throw new Error("usePearHyperliquid must be used within a PearHyperliquidProvider");
8498
+ throw new Error('usePearHyperliquid must be used within a PearHyperliquidProvider');
7444
8499
  return ctx;
7445
8500
  }
7446
8501
 
@@ -7531,4 +8586,4 @@ function mapCandleIntervalToTradingViewInterval(interval) {
7531
8586
  }
7532
8587
  }
7533
8588
 
7534
- 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, getCompleteTimestamps, getPortfolio, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, toggleWatchlist, updateLeverage, 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, validateMaxAssetsPerLeg, validateMinimumAssetSize, validatePositionSize };
8589
+ 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, updateLeverage, updateRiskParameters, useAccountSummary, useActiveBaskets, useAgentWallet, useAllBaskets, useAllUserBalances, useAuth, useAutoSyncFills, useBasketCandles, useFindBasket, useHighlightedBaskets, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMarketData, useMarketDataAllPayload, useMarketDataPayload, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePerpMetaAssets, usePortfolio, usePosition, useSpotOrder, useTokenSelectionMetadata, useTopGainers, useTopLosers, useTradeHistories, useTwap, useUserSelection, useWatchlist, useWatchlistBaskets, useWebData, validateMaxAssetsPerLeg, validateMinimumAssetSize, validatePositionSize };