@pear-protocol/hyperliquid-sdk 0.0.60-beta → 0.0.60-beta-usdh

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
@@ -1,5 +1,6 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import React, { useState, useRef, useCallback, useEffect, useMemo, useContext, createContext } from 'react';
2
+ import { useState, useRef, useCallback, useEffect, useMemo, useContext, createContext } from 'react';
3
+ import { create } from 'zustand';
3
4
 
4
5
  // Browser-compatible WebSocket ready state enum (mirrors native values)
5
6
  var ReadyState;
@@ -10,47 +11,6 @@ var ReadyState;
10
11
  ReadyState[ReadyState["CLOSED"] = 3] = "CLOSED";
11
12
  })(ReadyState || (ReadyState = {}));
12
13
 
13
- const createStoreImpl = (createState) => {
14
- let state;
15
- const listeners = /* @__PURE__ */ new Set();
16
- const setState = (partial, replace) => {
17
- const nextState = typeof partial === "function" ? partial(state) : partial;
18
- if (!Object.is(nextState, state)) {
19
- const previousState = state;
20
- state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
21
- listeners.forEach((listener) => listener(state, previousState));
22
- }
23
- };
24
- const getState = () => state;
25
- const getInitialState = () => initialState;
26
- const subscribe = (listener) => {
27
- listeners.add(listener);
28
- return () => listeners.delete(listener);
29
- };
30
- const api = { setState, getState, getInitialState, subscribe };
31
- const initialState = state = createState(setState, getState, api);
32
- return api;
33
- };
34
- const createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);
35
-
36
- const identity = (arg) => arg;
37
- function useStore(api, selector = identity) {
38
- const slice = React.useSyncExternalStore(
39
- api.subscribe,
40
- React.useCallback(() => selector(api.getState()), [api, selector]),
41
- React.useCallback(() => selector(api.getInitialState()), [api, selector])
42
- );
43
- React.useDebugValue(slice);
44
- return slice;
45
- }
46
- const createImpl = (createState) => {
47
- const api = createStore(createState);
48
- const useBoundStore = (selector) => useStore(api, selector);
49
- Object.assign(useBoundStore, api);
50
- return useBoundStore;
51
- };
52
- const create = ((createState) => createState ? createImpl(createState) : createImpl);
53
-
54
14
  const useUserData = create((set) => ({
55
15
  accessToken: null,
56
16
  refreshToken: null,
@@ -67,12 +27,12 @@ const useUserData = create((set) => ({
67
27
  setRefreshToken: (token) => set({ refreshToken: token }),
68
28
  setIsAuthenticated: (value) => set({ isAuthenticated: value }),
69
29
  setAddress: (address) => set(() => {
70
- if (typeof window !== "undefined") {
30
+ if (typeof window !== 'undefined') {
71
31
  if (address) {
72
- window.localStorage.setItem("address", address);
32
+ window.localStorage.setItem('address', address);
73
33
  }
74
34
  else {
75
- window.localStorage.removeItem("address");
35
+ window.localStorage.removeItem('address');
76
36
  }
77
37
  }
78
38
  return { address };
@@ -111,18 +71,71 @@ const useMarketData = create((set) => ({
111
71
  * Convert a full/prefixed symbol (e.g., "xyz:XYZ100") to a display symbol (e.g., "XYZ100").
112
72
  */
113
73
  function toDisplaySymbol(symbol) {
114
- const parts = symbol.split(":");
74
+ const parts = symbol.split(':');
115
75
  return parts.length > 1 ? parts.slice(-1)[0] : symbol;
116
76
  }
117
77
  /**
118
78
  * Convert a display symbol back to backend form using a provided map.
119
79
  * If mapping is missing, returns the original symbol.
120
- * @param displaySymbol e.g., "XYZ100"
121
- * @param displayToFull map of display -> full (e.g., "XYZ100" -> "xyz:XYZ100")
80
+ * For multi-market assets, returns the first available market.
81
+ * @param displaySymbol e.g., "TSLA"
82
+ * @param hip3Assets map of display -> all full market names (e.g., "TSLA" -> ["xyz:TSLA", "flx:TSLA"])
83
+ */
84
+ function toBackendSymbol(displaySymbol, hip3Assets) {
85
+ const markets = hip3Assets.get(displaySymbol);
86
+ // Return first market if available, otherwise return original symbol
87
+ return markets && markets.length > 0 ? markets[0] : displaySymbol;
88
+ }
89
+ /**
90
+ * Convert a display symbol to backend form for a specific market prefix.
91
+ * This is useful when an asset is available on multiple markets (e.g., xyz:TSLA and flx:TSLA).
92
+ * @param displaySymbol e.g., "TSLA"
93
+ * @param marketPrefix e.g., "xyz" or "flx"
94
+ * @param hip3Assets map of display -> all full market names
95
+ * @returns Full market name if found, null if prefix not specified for multi-market asset, otherwise displaySymbol with prefix
96
+ */
97
+ function toBackendSymbolWithMarket(displaySymbol, marketPrefix, hip3Assets) {
98
+ const availableMarkets = hip3Assets.get(displaySymbol);
99
+ if (!availableMarkets || availableMarkets.length === 0) {
100
+ // Not a HIP-3 asset, return as-is or with prefix if provided
101
+ return marketPrefix ? `${marketPrefix}:${displaySymbol}` : displaySymbol;
102
+ }
103
+ if (marketPrefix) {
104
+ // Find the market with the specified prefix
105
+ const targetMarket = availableMarkets.find((market) => market.toLowerCase().startsWith(`${marketPrefix.toLowerCase()}:`));
106
+ if (targetMarket) {
107
+ return targetMarket;
108
+ }
109
+ }
110
+ // No prefix specified or not found, return null to force explicit market selection
111
+ return null;
112
+ }
113
+ /**
114
+ * Get all available markets for a display symbol.
115
+ * @param displaySymbol e.g., "TSLA"
116
+ * @param hip3Assets map of display -> all full market names
117
+ * @returns Array of full market names, e.g., ["xyz:TSLA", "flx:TSLA"]
122
118
  */
123
- function toBackendSymbol(displaySymbol, displayToFull) {
119
+ function getAvailableMarkets(displaySymbol, hip3Assets) {
124
120
  var _a;
125
- return (_a = displayToFull.get(displaySymbol)) !== null && _a !== void 0 ? _a : displaySymbol;
121
+ return (_a = hip3Assets.get(displaySymbol)) !== null && _a !== void 0 ? _a : [];
122
+ }
123
+ /**
124
+ * Extract the market prefix from a full market name.
125
+ * @param fullSymbol e.g., "xyz:TSLA"
126
+ * @returns The prefix (e.g., "xyz") or undefined if no prefix
127
+ */
128
+ function getMarketPrefix(fullSymbol) {
129
+ const parts = fullSymbol.split(':');
130
+ return parts.length > 1 ? parts[0] : undefined;
131
+ }
132
+ /**
133
+ * Check if a symbol is a HIP-3 market (has a prefix).
134
+ * @param symbol e.g., "xyz:TSLA" or "TSLA"
135
+ * @returns true if the symbol has a market prefix
136
+ */
137
+ function isHip3Market(symbol) {
138
+ return symbol.includes(':');
126
139
  }
127
140
 
128
141
  const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
@@ -358,10 +371,11 @@ const useHyperliquidData = create((set, get) => ({
358
371
  finalAtOICaps: null,
359
372
  aggregatedClearingHouseState: null,
360
373
  perpMetaAssets: null,
361
- hip3DisplayToFull: new Map(),
374
+ hip3Assets: new Map(),
375
+ hip3MarketPrefixes: new Map(),
362
376
  setAllMids: (value) => set({ allMids: value }),
363
377
  setActiveAssetData: (value) => set((state) => ({
364
- activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value
378
+ activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value,
365
379
  })),
366
380
  deleteActiveAssetData: (key) => {
367
381
  set((state) => {
@@ -396,13 +410,14 @@ const useHyperliquidData = create((set, get) => ({
396
410
  activeAssetData: {
397
411
  ...state.activeAssetData,
398
412
  [key]: value,
399
- }
413
+ },
400
414
  })),
401
415
  setFinalAssetContexts: (value) => set({ finalAssetContexts: value }),
402
416
  setFinalAtOICaps: (value) => set({ finalAtOICaps: value }),
403
417
  setAggregatedClearingHouseState: (value) => set({ aggregatedClearingHouseState: value }),
404
418
  setPerpMetaAssets: (value) => set({ perpMetaAssets: value }),
405
- setHip3DisplayToFull: (value) => set({ hip3DisplayToFull: value })
419
+ setHip3Assets: (value) => set({ hip3Assets: value }),
420
+ setHip3MarketPrefixes: (value) => set({ hip3MarketPrefixes: value }),
406
421
  }));
407
422
 
408
423
  const DEFAULT_STATE = {
@@ -543,11 +558,11 @@ const useUserSelection$1 = create((set, get) => ({
543
558
  }));
544
559
 
545
560
  const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
546
- const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState } = useHyperliquidData();
561
+ const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState, } = useHyperliquidData();
547
562
  const { candleInterval } = useUserSelection$1();
548
563
  const longTokens = useUserSelection$1((s) => s.longTokens);
549
564
  const shortTokens = useUserSelection$1((s) => s.shortTokens);
550
- const selectedTokenSymbols = useMemo(() => ([...longTokens, ...shortTokens].map((t) => t.symbol)), [longTokens, shortTokens]);
565
+ const selectedTokenSymbols = useMemo(() => [...longTokens, ...shortTokens].map((t) => t.symbol), [longTokens, shortTokens]);
551
566
  const [lastError, setLastError] = useState(null);
552
567
  const [subscribedAddress, setSubscribedAddress] = useState(null);
553
568
  const [subscribedTokens, setSubscribedTokens] = useState([]);
@@ -585,7 +600,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
585
600
  .map((dex) => dex.clearinghouseState)
586
601
  .filter(Boolean);
587
602
  const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || '0') || 0), 0);
588
- const toStr = (n) => (Number.isFinite(n) ? n.toString() : '0');
603
+ const toStr = (n) => Number.isFinite(n) ? n.toString() : '0';
589
604
  const assetPositions = states.flatMap((s) => s.assetPositions || []);
590
605
  const crossMaintenanceMarginUsed = toStr(sum(states.map((s) => s.crossMaintenanceMarginUsed)));
591
606
  const crossMarginSummary = {
@@ -618,17 +633,42 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
618
633
  case 'allMids':
619
634
  {
620
635
  const data = response.data;
621
- const remapped = {
622
- mids: Object.fromEntries(Object.entries(data.mids || {}).map(([k, v]) => [toDisplaySymbol(k), v]))
623
- };
624
- setAllMids(remapped);
636
+ // Keep BOTH normalized prefixed keys AND display symbol keys
637
+ // This ensures xyz:TSLA and flx:TSLA are stored separately,
638
+ // while also maintaining backward compatibility with non-prefixed lookups
639
+ const mids = {};
640
+ Object.entries(data.mids || {}).forEach(([k, v]) => {
641
+ // Normalize prefixed keys to lowercase prefix (e.g., "XYZ:TSLA" -> "xyz:TSLA")
642
+ // This matches how we look up tokens in the SDK
643
+ let normalizedKey = k;
644
+ if (k.includes(':')) {
645
+ const [prefix, ...rest] = k.split(':');
646
+ normalizedKey = `${prefix.toLowerCase()}:${rest.join(':')}`;
647
+ }
648
+ // Store with normalized key
649
+ mids[normalizedKey] = v;
650
+ // Also store with original key for backward compatibility
651
+ if (k !== normalizedKey) {
652
+ mids[k] = v;
653
+ }
654
+ // Also store with display symbol for backward compatibility
655
+ const displayKey = toDisplaySymbol(k);
656
+ // Only set display key if it doesn't already exist (avoid overwriting market-specific prices)
657
+ if (!(displayKey in mids)) {
658
+ mids[displayKey] = v;
659
+ }
660
+ });
661
+ setAllMids({ mids });
625
662
  }
626
663
  break;
627
664
  case 'activeAssetData':
628
665
  {
629
666
  const assetData = response.data;
630
667
  const symbol = toDisplaySymbol(assetData.coin);
631
- const normalized = { ...assetData, coin: symbol };
668
+ const normalized = {
669
+ ...assetData,
670
+ coin: symbol,
671
+ };
632
672
  upsertActiveAssetData(symbol, normalized);
633
673
  }
634
674
  break;
@@ -650,7 +690,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
650
690
  console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
651
691
  setLastError(errorMessage);
652
692
  }
653
- }, [setAllMids, upsertActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState]);
693
+ }, [
694
+ setAllMids,
695
+ upsertActiveAssetData,
696
+ addCandleData,
697
+ setFinalAssetContexts,
698
+ setFinalAtOICaps,
699
+ setAggregatedClearingHouseState,
700
+ ]);
654
701
  const connect = useCallback(() => {
655
702
  if (!enabled)
656
703
  return;
@@ -773,7 +820,13 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
773
820
  // clear aggregatedClearingHouseState
774
821
  setAggregatedClearingHouseState(null);
775
822
  }
776
- }, [isConnected, address, subscribedAddress, sendJsonMessage, setAggregatedClearingHouseState]);
823
+ }, [
824
+ isConnected,
825
+ address,
826
+ subscribedAddress,
827
+ sendJsonMessage,
828
+ setAggregatedClearingHouseState,
829
+ ]);
777
830
  // Handle token subscriptions for activeAssetData
778
831
  useEffect(() => {
779
832
  if (!isConnected || !address)
@@ -782,7 +835,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
782
835
  const tokensToSubscribe = effectiveTokens.filter((token) => token && !subscribedTokens.includes(token));
783
836
  const tokensToUnsubscribe = subscribedTokens.filter((token) => !effectiveTokens.includes(token));
784
837
  // Unsubscribe from tokens no longer in the list
785
- tokensToUnsubscribe.forEach(token => {
838
+ tokensToUnsubscribe.forEach((token) => {
786
839
  const unsubscribeMessage = {
787
840
  method: 'unsubscribe',
788
841
  subscription: {
@@ -794,7 +847,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
794
847
  sendJsonMessage(unsubscribeMessage);
795
848
  });
796
849
  // Subscribe to new tokens
797
- tokensToSubscribe.forEach(token => {
850
+ tokensToSubscribe.forEach((token) => {
798
851
  const subscribeMessage = {
799
852
  method: 'subscribe',
800
853
  subscription: {
@@ -809,7 +862,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
809
862
  setSubscribedTokens(effectiveTokens.filter((token) => token));
810
863
  tokensToSubscribe.forEach((token) => deleteActiveAssetData(token));
811
864
  }
812
- }, [isConnected, address, selectedTokenSymbols, subscribedTokens, sendJsonMessage, setActiveAssetData]);
865
+ }, [
866
+ isConnected,
867
+ address,
868
+ selectedTokenSymbols,
869
+ subscribedTokens,
870
+ sendJsonMessage,
871
+ setActiveAssetData,
872
+ ]);
813
873
  // Handle candle subscriptions for tokens and interval changes
814
874
  useEffect(() => {
815
875
  if (!isConnected)
@@ -818,7 +878,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
818
878
  // Unsubscribe from previous candle subscriptions if interval changed
819
879
  const prevInterval = prevCandleIntervalRef.current;
820
880
  if (prevInterval && prevInterval !== candleInterval) {
821
- subscribedCandleTokens.forEach(token => {
881
+ subscribedCandleTokens.forEach((token) => {
822
882
  const unsubscribeMessage = {
823
883
  method: 'unsubscribe',
824
884
  subscription: {
@@ -859,12 +919,21 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
859
919
  sendJsonMessage(subscribeMessage);
860
920
  });
861
921
  // Update subscribed state
862
- if (tokensToSubscribe.length > 0 || tokensToUnsubscribe.length > 0 || prevInterval !== candleInterval) {
922
+ if (tokensToSubscribe.length > 0 ||
923
+ tokensToUnsubscribe.length > 0 ||
924
+ prevInterval !== candleInterval) {
863
925
  setSubscribedCandleTokens(effectiveTokens.filter((token) => token));
864
926
  prevCandleIntervalRef.current = candleInterval;
865
927
  tokensToUnsubscribe.forEach((token) => deleteCandleSymbol(token));
866
928
  }
867
- }, [isConnected, selectedTokenSymbols, candleInterval, subscribedCandleTokens, sendJsonMessage, setCandleData]);
929
+ }, [
930
+ isConnected,
931
+ selectedTokenSymbols,
932
+ candleInterval,
933
+ subscribedCandleTokens,
934
+ sendJsonMessage,
935
+ setCandleData,
936
+ ]);
868
937
  return {
869
938
  isConnected,
870
939
  lastError,
@@ -986,21 +1055,51 @@ const useWebData = () => {
986
1055
  const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
987
1056
  const aggregatedClearinghouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
988
1057
  const finalAtOICaps = useHyperliquidData((state) => state.finalAtOICaps);
989
- const hip3Assets = useHyperliquidData((state) => state.hip3DisplayToFull);
1058
+ const hip3Assets = useHyperliquidData((state) => state.hip3Assets);
1059
+ const hip3MarketPrefixes = useHyperliquidData((state) => state.hip3MarketPrefixes);
990
1060
  let marketDataBySymbol = {};
991
1061
  if (finalAssetContexts && perpMetaAssets) {
992
1062
  const result = {};
1063
+ // Build a map of display name -> asset context index (for unique display names)
1064
+ const displayNameToContextIndex = new Map();
1065
+ const seenNames = new Set();
1066
+ let contextIndex = 0;
1067
+ // First pass: map unique display names to their context index
993
1068
  for (let index = 0; index < perpMetaAssets.length; index++) {
994
1069
  const name = perpMetaAssets[index].name;
995
- result[name] = {
996
- asset: finalAssetContexts[index],
997
- universe: perpMetaAssets[index],
998
- };
1070
+ if (!seenNames.has(name)) {
1071
+ seenNames.add(name);
1072
+ if (contextIndex < finalAssetContexts.length) {
1073
+ displayNameToContextIndex.set(name, contextIndex);
1074
+ contextIndex++;
1075
+ }
1076
+ }
1077
+ }
1078
+ // Second pass: create nested entries for all market variants
1079
+ for (let index = 0; index < perpMetaAssets.length; index++) {
1080
+ const universeAsset = perpMetaAssets[index];
1081
+ const displayName = universeAsset.name;
1082
+ const marketPrefix = universeAsset.marketPrefix;
1083
+ const ctxIndex = displayNameToContextIndex.get(displayName);
1084
+ if (ctxIndex !== undefined) {
1085
+ const assetContext = finalAssetContexts[ctxIndex];
1086
+ // Initialize the symbol entry if it doesn't exist
1087
+ if (!result[displayName]) {
1088
+ result[displayName] = {};
1089
+ }
1090
+ // Use marketPrefix as key for HIP-3 assets, "default" for regular assets
1091
+ const variantKey = marketPrefix || 'default';
1092
+ result[displayName][variantKey] = {
1093
+ asset: assetContext,
1094
+ universe: universeAsset,
1095
+ };
1096
+ }
999
1097
  }
1000
1098
  marketDataBySymbol = result;
1001
1099
  }
1002
1100
  return {
1003
1101
  hip3Assets,
1102
+ hip3MarketPrefixes,
1004
1103
  clearinghouseState: aggregatedClearinghouseState,
1005
1104
  perpsAtOpenInterestCap: finalAtOICaps,
1006
1105
  marketDataBySymbol,
@@ -1015,30 +1114,60 @@ const useWebData = () => {
1015
1114
  class TokenMetadataExtractor {
1016
1115
  /**
1017
1116
  * Extracts comprehensive token metadata
1018
- * @param symbol - Token symbol
1117
+ * @param symbol - Token symbol (base symbol without prefix, e.g., "TSLA")
1019
1118
  * @param perpMetaAssets - Aggregated universe assets (flattened across dexes)
1020
1119
  * @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
1021
1120
  * @param allMids - AllMids data containing current prices
1022
1121
  * @param activeAssetData - Optional active asset data containing leverage information
1122
+ * @param marketPrefix - Optional market prefix (e.g., "xyz", "flx") for HIP3 multi-market assets
1023
1123
  * @returns TokenMetadata or null if token not found
1024
1124
  */
1025
- static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1125
+ static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketPrefix) {
1026
1126
  if (!perpMetaAssets || !finalAssetContexts || !allMids) {
1027
1127
  return null;
1028
1128
  }
1029
1129
  // Find token index in aggregated universe
1030
- const universeIndex = perpMetaAssets.findIndex(asset => asset.name === symbol);
1031
- if (universeIndex === -1) {
1130
+ // For HIP3 assets, match both name AND marketPrefix
1131
+ const universeIndex = perpMetaAssets.findIndex((asset) => {
1132
+ if (asset.name !== symbol)
1133
+ return false;
1134
+ // If marketPrefix is specified, match it; otherwise match assets without prefix
1135
+ if (marketPrefix) {
1136
+ return asset.marketPrefix === marketPrefix;
1137
+ }
1138
+ // No prefix specified - match non-HIP3 asset (no marketPrefix) or first matching asset
1139
+ return !asset.marketPrefix;
1140
+ });
1141
+ // If no exact match found and prefix was specified, try finding the specific market variant
1142
+ const finalIndex = universeIndex === -1 && marketPrefix
1143
+ ? perpMetaAssets.findIndex((asset) => asset.name === symbol && asset.marketPrefix === marketPrefix)
1144
+ : universeIndex;
1145
+ // Fallback: if still not found and no prefix, find first matching by name (for backward compatibility)
1146
+ const resolvedIndex = finalIndex === -1
1147
+ ? perpMetaAssets.findIndex((asset) => asset.name === symbol)
1148
+ : finalIndex;
1149
+ if (resolvedIndex === -1) {
1032
1150
  return null;
1033
1151
  }
1034
- const universeAsset = perpMetaAssets[universeIndex];
1035
- const assetCtx = finalAssetContexts[universeIndex];
1152
+ const universeAsset = perpMetaAssets[resolvedIndex];
1153
+ const assetCtx = finalAssetContexts[resolvedIndex];
1036
1154
  if (!assetCtx) {
1037
1155
  return null;
1038
1156
  }
1039
- // Get current price from allMids
1040
- const currentPriceStr = allMids.mids[symbol];
1041
- const currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
1157
+ // Get current price - prefer assetCtx.midPx as it's already index-matched,
1158
+ // fall back to allMids lookup if midPx is null
1159
+ const prefixedKeyColon = marketPrefix ? `${marketPrefix}:${symbol}` : null;
1160
+ let currentPrice = 0;
1161
+ // Primary source: assetCtx.midPx (already properly indexed)
1162
+ if (assetCtx.midPx) {
1163
+ currentPrice = parseFloat(assetCtx.midPx);
1164
+ }
1165
+ // Fallback: allMids lookup with multiple key formats for HIP3 markets
1166
+ if (!currentPrice || isNaN(currentPrice)) {
1167
+ const currentPriceStr = (prefixedKeyColon && allMids.mids[prefixedKeyColon]) ||
1168
+ allMids.mids[symbol];
1169
+ currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
1170
+ }
1042
1171
  // Get previous day price
1043
1172
  const prevDayPrice = parseFloat(assetCtx.prevDayPx);
1044
1173
  // Calculate 24h price change
@@ -1049,7 +1178,11 @@ class TokenMetadataExtractor {
1049
1178
  const markPrice = parseFloat(assetCtx.markPx);
1050
1179
  const oraclePrice = parseFloat(assetCtx.oraclePx);
1051
1180
  // Extract leverage info from activeAssetData if available
1052
- const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[symbol];
1181
+ // Try prefixed key first (e.g., "xyz:TSLA"), then fall back to plain symbol
1182
+ const activeDataKey = prefixedKeyColon && (activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[prefixedKeyColon])
1183
+ ? prefixedKeyColon
1184
+ : symbol;
1185
+ const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[activeDataKey];
1053
1186
  const leverage = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.leverage;
1054
1187
  const maxTradeSzs = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.maxTradeSzs;
1055
1188
  const availableToTrade = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.availableToTrade;
@@ -1067,21 +1200,27 @@ class TokenMetadataExtractor {
1067
1200
  leverage,
1068
1201
  maxTradeSzs,
1069
1202
  availableToTrade,
1203
+ collateralToken: universeAsset.collateralToken,
1070
1204
  };
1071
1205
  }
1072
1206
  /**
1073
1207
  * Extracts metadata for multiple tokens
1074
- * @param symbols - Array of token symbols
1208
+ * @param tokens - Array of token objects with symbol and optional marketPrefix
1075
1209
  * @param perpMetaAssets - Aggregated universe assets
1076
1210
  * @param finalAssetContexts - Aggregated asset contexts
1077
1211
  * @param allMids - AllMids data
1078
1212
  * @param activeAssetData - Optional active asset data containing leverage information
1079
- * @returns Record of symbol to TokenMetadata
1213
+ * @returns Record of unique key to TokenMetadata. Key is "{prefix}:{symbol}" for HIP3 assets, or just "{symbol}" otherwise
1080
1214
  */
1081
- static extractMultipleTokensMetadata(symbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1215
+ static extractMultipleTokensMetadata(tokens, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
1082
1216
  const result = {};
1083
- for (const symbol of symbols) {
1084
- result[symbol] = this.extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData);
1217
+ for (const token of tokens) {
1218
+ // Use a unique key that includes the prefix for HIP3 assets
1219
+ // This ensures xyz:TSLA and flx:TSLA get separate entries
1220
+ const resultKey = token.marketPrefix
1221
+ ? `${token.marketPrefix}:${token.symbol}`
1222
+ : token.symbol;
1223
+ result[resultKey] = this.extractTokenMetadata(token.symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, token.marketPrefix);
1085
1224
  }
1086
1225
  return result;
1087
1226
  }
@@ -1094,10 +1233,30 @@ class TokenMetadataExtractor {
1094
1233
  static isTokenAvailable(symbol, perpMetaAssets) {
1095
1234
  if (!perpMetaAssets)
1096
1235
  return false;
1097
- return perpMetaAssets.some(asset => asset.name === symbol);
1236
+ return perpMetaAssets.some((asset) => asset.name === symbol);
1098
1237
  }
1099
1238
  }
1100
1239
 
1240
+ /**
1241
+ * Parse a token string that may have a market prefix (e.g., "xyz:GOOGL" -> { prefix: "xyz", symbol: "GOOGL" })
1242
+ * This allows us to keep the full name (xyz:GOOGL) for URLs/tags while extracting just the symbol for SDK lookups.
1243
+ */
1244
+ function parseTokenWithPrefix(token) {
1245
+ if (token.includes(':')) {
1246
+ const [prefix, ...rest] = token.split(':');
1247
+ const symbol = rest.join(':').toUpperCase();
1248
+ return {
1249
+ prefix: prefix.toLowerCase(),
1250
+ symbol,
1251
+ fullName: `${prefix.toLowerCase()}:${symbol}`,
1252
+ };
1253
+ }
1254
+ return {
1255
+ prefix: null,
1256
+ symbol: token.toUpperCase(),
1257
+ fullName: token.toUpperCase(),
1258
+ };
1259
+ }
1101
1260
  const useTokenSelectionMetadataStore = create((set) => ({
1102
1261
  isPriceDataReady: false,
1103
1262
  isLoading: true,
@@ -1107,23 +1266,65 @@ const useTokenSelectionMetadataStore = create((set) => ({
1107
1266
  weightedRatio24h: 1,
1108
1267
  priceRatio: 1,
1109
1268
  priceRatio24h: 1,
1110
- openInterest: "0",
1111
- volume: "0",
1269
+ openInterest: '0',
1270
+ volume: '0',
1112
1271
  sumNetFunding: 0,
1113
1272
  maxLeverage: 0,
1114
1273
  minMargin: 0,
1115
1274
  leverageMatched: true,
1116
- recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens }) => {
1117
- const isPriceDataReady = !!(perpMetaAssets && finalAssetContexts && allMids);
1118
- // Compute metadata when ready
1119
- const longSymbols = longTokens.map((t) => t.symbol);
1120
- const shortSymbols = shortTokens.map((t) => t.symbol);
1121
- const longTokensMetadata = isPriceDataReady
1122
- ? TokenMetadataExtractor.extractMultipleTokensMetadata(longSymbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1275
+ recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens, }) => {
1276
+ const isPriceDataReady = !!(perpMetaAssets &&
1277
+ finalAssetContexts &&
1278
+ allMids);
1279
+ // Parse tokens - handle prefixed tokens like "xyz:GOOGL" by extracting the symbol and market prefix
1280
+ // The full name (xyz:GOOGL) is kept as the metadata key for UI consistency
1281
+ const parsedLongTokens = longTokens.map((t) => ({
1282
+ ...t,
1283
+ parsed: parseTokenWithPrefix(t.symbol),
1284
+ }));
1285
+ const parsedShortTokens = shortTokens.map((t) => ({
1286
+ ...t,
1287
+ parsed: parseTokenWithPrefix(t.symbol),
1288
+ }));
1289
+ // Extract base symbols with their market prefixes for SDK lookups
1290
+ // This ensures xyz:TSLA and flx:TSLA get different market data
1291
+ const longTokensForLookup = parsedLongTokens.map((t) => ({
1292
+ symbol: t.parsed.symbol,
1293
+ marketPrefix: t.parsed.prefix,
1294
+ }));
1295
+ const shortTokensForLookup = parsedShortTokens.map((t) => ({
1296
+ symbol: t.parsed.symbol,
1297
+ marketPrefix: t.parsed.prefix,
1298
+ }));
1299
+ // Also extract just the base symbols (without prefix) for lookups that don't support prefixes
1300
+ const longBaseSymbols = longTokensForLookup.map((t) => t.symbol);
1301
+ const shortBaseSymbols = shortTokensForLookup.map((t) => t.symbol);
1302
+ // Get metadata using base symbols with market prefix for proper market differentiation
1303
+ const longBaseMetadata = isPriceDataReady
1304
+ ? TokenMetadataExtractor.extractMultipleTokensMetadata(longTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1123
1305
  : {};
1124
- const shortTokensMetadata = isPriceDataReady
1125
- ? TokenMetadataExtractor.extractMultipleTokensMetadata(shortSymbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1306
+ const shortBaseMetadata = isPriceDataReady
1307
+ ? TokenMetadataExtractor.extractMultipleTokensMetadata(shortTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
1126
1308
  : {};
1309
+ // Re-map metadata using original full names (with prefix) as keys for UI consistency
1310
+ // The extractor now keys by "{prefix}:{symbol}" for prefixed tokens, which matches our parsed.fullName
1311
+ const longTokensMetadata = {};
1312
+ parsedLongTokens.forEach((t) => {
1313
+ var _a;
1314
+ // Use the full name (e.g., "xyz:TSLA") as the lookup key since extractor uses the same format
1315
+ const lookupKey = t.parsed.prefix
1316
+ ? `${t.parsed.prefix}:${t.parsed.symbol}`
1317
+ : t.parsed.symbol;
1318
+ longTokensMetadata[t.symbol] = (_a = longBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
1319
+ });
1320
+ const shortTokensMetadata = {};
1321
+ parsedShortTokens.forEach((t) => {
1322
+ var _a;
1323
+ const lookupKey = t.parsed.prefix
1324
+ ? `${t.parsed.prefix}:${t.parsed.symbol}`
1325
+ : t.parsed.symbol;
1326
+ shortTokensMetadata[t.symbol] = (_a = shortBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
1327
+ });
1127
1328
  // Determine loading state
1128
1329
  const allTokens = [...longTokens, ...shortTokens];
1129
1330
  const isLoading = (() => {
@@ -1131,26 +1332,33 @@ const useTokenSelectionMetadataStore = create((set) => ({
1131
1332
  return true;
1132
1333
  if (allTokens.length === 0)
1133
1334
  return false;
1134
- const allMetadata = { ...longTokensMetadata, ...shortTokensMetadata };
1335
+ const allMetadata = {
1336
+ ...longTokensMetadata,
1337
+ ...shortTokensMetadata,
1338
+ };
1135
1339
  return allTokens.some((token) => !allMetadata[token.symbol]);
1136
1340
  })();
1137
1341
  // Open interest and volume (from market data for matching asset basket)
1342
+ // Use base symbols (without prefix) for matching against market data
1138
1343
  const { openInterest, volume } = (() => {
1139
- const empty = { openInterest: "0", volume: "0" };
1344
+ const empty = { openInterest: '0', volume: '0' };
1140
1345
  if (!(marketData === null || marketData === void 0 ? void 0 : marketData.active) || (!longTokens.length && !shortTokens.length))
1141
1346
  return empty;
1142
- const selectedLong = longTokens.map((t) => t.symbol).sort();
1143
- const selectedShort = shortTokens.map((t) => t.symbol).sort();
1347
+ const selectedLong = longBaseSymbols.slice().sort();
1348
+ const selectedShort = shortBaseSymbols.slice().sort();
1144
1349
  const match = marketData.active.find((item) => {
1145
1350
  const longs = [...item.longAssets].sort();
1146
1351
  const shorts = [...item.shortAssets].sort();
1147
- if (longs.length !== selectedLong.length || shorts.length !== selectedShort.length)
1352
+ if (longs.length !== selectedLong.length ||
1353
+ shorts.length !== selectedShort.length)
1148
1354
  return false;
1149
1355
  const longsEqual = longs.every((s, i) => s.asset === selectedLong[i]);
1150
1356
  const shortsEqual = shorts.every((s, i) => s.asset === selectedShort[i]);
1151
1357
  return longsEqual && shortsEqual;
1152
1358
  });
1153
- return match ? { openInterest: match.openInterest, volume: match.volume } : empty;
1359
+ return match
1360
+ ? { openInterest: match.openInterest, volume: match.volume }
1361
+ : empty;
1154
1362
  })();
1155
1363
  // Price ratio (only when exactly one long and one short)
1156
1364
  const { priceRatio, priceRatio24h } = (() => {
@@ -1230,17 +1438,27 @@ const useTokenSelectionMetadataStore = create((set) => ({
1230
1438
  return totalFunding;
1231
1439
  })();
1232
1440
  // Max leverage (minimum across all tokens)
1441
+ // Use tokens with their market prefixes for proper lookup in perpMetaAssets
1233
1442
  const maxLeverage = (() => {
1234
1443
  if (!perpMetaAssets)
1235
1444
  return 0;
1236
- const allTokenSymbols = [...longTokens, ...shortTokens].map((t) => t.symbol);
1237
- if (allTokenSymbols.length === 0)
1445
+ const allTokensForLookup = [
1446
+ ...longTokensForLookup,
1447
+ ...shortTokensForLookup,
1448
+ ];
1449
+ if (allTokensForLookup.length === 0)
1238
1450
  return 0;
1239
1451
  let minLev = Infinity;
1240
- allTokenSymbols.forEach((symbol) => {
1241
- const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol);
1242
- if (tokenUniverse === null || tokenUniverse === void 0 ? void 0 : tokenUniverse.maxLeverage)
1243
- minLev = Math.min(minLev, tokenUniverse.maxLeverage);
1452
+ allTokensForLookup.forEach(({ symbol, marketPrefix }) => {
1453
+ // Match by both name AND marketPrefix for HIP3 assets
1454
+ const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol &&
1455
+ (marketPrefix
1456
+ ? u.marketPrefix === marketPrefix
1457
+ : !u.marketPrefix));
1458
+ // Fallback to just matching by name if no exact match
1459
+ const fallbackUniverse = tokenUniverse || perpMetaAssets.find((u) => u.name === symbol);
1460
+ if (fallbackUniverse === null || fallbackUniverse === void 0 ? void 0 : fallbackUniverse.maxLeverage)
1461
+ minLev = Math.min(minLev, fallbackUniverse.maxLeverage);
1244
1462
  });
1245
1463
  return minLev === Infinity ? 0 : minLev;
1246
1464
  })();
@@ -1252,7 +1470,10 @@ const useTokenSelectionMetadataStore = create((set) => ({
1252
1470
  // Whether all tokens have matching leverage
1253
1471
  const leverageMatched = (() => {
1254
1472
  const allTokensArr = [...longTokens, ...shortTokens];
1255
- const allMetadata = { ...longTokensMetadata, ...shortTokensMetadata };
1473
+ const allMetadata = {
1474
+ ...longTokensMetadata,
1475
+ ...shortTokensMetadata,
1476
+ };
1256
1477
  if (allTokensArr.length === 0)
1257
1478
  return true;
1258
1479
  const tokensWithLev = allTokensArr.filter((token) => { var _a; return (_a = allMetadata[token.symbol]) === null || _a === void 0 ? void 0 : _a.leverage; });
@@ -5437,8 +5658,8 @@ function addAuthInterceptors(params) {
5437
5658
  /**
5438
5659
  * Fetch historical candle data from HyperLiquid API
5439
5660
  */
5440
- const fetchHistoricalCandles = async (coin, startTime, endTime, interval, displayToFull) => {
5441
- const backendCoin = toBackendSymbol(coin, displayToFull);
5661
+ const fetchHistoricalCandles = async (coin, startTime, endTime, interval, hip3Assets) => {
5662
+ const backendCoin = toBackendSymbol(coin, hip3Assets);
5442
5663
  const request = {
5443
5664
  req: { coin: backendCoin, startTime, endTime, interval },
5444
5665
  type: 'candleSnapshot',
@@ -5597,10 +5818,10 @@ const useHistoricalPriceData = () => {
5597
5818
  setTokenLoading(token.symbol, true);
5598
5819
  });
5599
5820
  try {
5600
- const displayToFull = useHyperliquidData.getState().hip3DisplayToFull;
5821
+ const hip3Assets = useHyperliquidData.getState().hip3Assets;
5601
5822
  const fetchPromises = tokensToFetch.map(async (token) => {
5602
5823
  try {
5603
- const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, displayToFull);
5824
+ const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, hip3Assets);
5604
5825
  addHistoricalPriceData(token.symbol, interval, response.data, { start: startTime, end: endTime });
5605
5826
  return { symbol: token.symbol, candles: response.data, success: true };
5606
5827
  }
@@ -6369,12 +6590,12 @@ function validatePositionSize(usdValue, longAssets, shortAssets) {
6369
6590
  * Authorization is derived from headers (Axios defaults or browser localStorage fallback)
6370
6591
  * @throws MinimumPositionSizeError if any asset has less than $11 USD value
6371
6592
  */
6372
- async function createPosition(baseUrl, payload, displayToFull) {
6593
+ async function createPosition(baseUrl, payload, hip3Assets) {
6373
6594
  // Validate minimum asset size before creating position
6374
6595
  validateMinimumAssetSize(payload.usdValue, payload.longAssets, payload.shortAssets);
6375
6596
  const url = joinUrl(baseUrl, "/positions");
6376
6597
  // Translate display symbols to backend format
6377
- const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, displayToFull) }));
6598
+ const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6378
6599
  const translatedPayload = {
6379
6600
  ...payload,
6380
6601
  longAssets: mapAssets(payload.longAssets),
@@ -6473,9 +6694,9 @@ async function adjustPosition(baseUrl, positionId, payload) {
6473
6694
  throw toApiError(error);
6474
6695
  }
6475
6696
  }
6476
- async function adjustAdvancePosition(baseUrl, positionId, payload, displayToFull) {
6697
+ async function adjustAdvancePosition(baseUrl, positionId, payload, hip3Assets) {
6477
6698
  const url = joinUrl(baseUrl, `/positions/${positionId}/adjust-advance`);
6478
- const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, displayToFull) }));
6699
+ const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6479
6700
  const translatedPayload = (payload || []).map((item) => ({
6480
6701
  longAssets: mapAssets(item.longAssets),
6481
6702
  shortAssets: mapAssets(item.shortAssets),
@@ -6600,16 +6821,62 @@ const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
6600
6821
  });
6601
6822
  };
6602
6823
 
6824
+ // Helper to find asset metadata from perpMetaAssets
6825
+ function findAssetMeta$1(coinName, perpMetaAssets) {
6826
+ var _a, _b, _c, _d;
6827
+ if (!perpMetaAssets) {
6828
+ return { collateralToken: 'USDC', marketPrefix: null };
6829
+ }
6830
+ // Try exact match first (for prefixed assets like "xyz:TSLA")
6831
+ const exactMatch = perpMetaAssets.find((a) => a.name === coinName);
6832
+ if (exactMatch) {
6833
+ return {
6834
+ collateralToken: (_a = exactMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
6835
+ marketPrefix: (_b = exactMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
6836
+ };
6837
+ }
6838
+ // Try matching by base symbol (for non-prefixed names in data)
6839
+ const baseMatch = perpMetaAssets.find((a) => {
6840
+ const baseName = a.name.includes(':') ? a.name.split(':')[1] : a.name;
6841
+ return baseName === coinName;
6842
+ });
6843
+ if (baseMatch) {
6844
+ return {
6845
+ collateralToken: (_c = baseMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
6846
+ marketPrefix: (_d = baseMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
6847
+ };
6848
+ }
6849
+ return { collateralToken: 'USDC', marketPrefix: null };
6850
+ }
6851
+ // Enrich position assets with market prefix and collateral token
6852
+ function enrichPositionAssets(assets, perpMetaAssets) {
6853
+ return assets.map((asset) => {
6854
+ const meta = findAssetMeta$1(asset.coin, perpMetaAssets);
6855
+ return {
6856
+ ...asset,
6857
+ marketPrefix: meta.marketPrefix,
6858
+ collateralToken: meta.collateralToken,
6859
+ };
6860
+ });
6861
+ }
6862
+ // Enrich all positions with market metadata
6863
+ function enrichPositions(positions, perpMetaAssets) {
6864
+ return positions.map((position) => ({
6865
+ ...position,
6866
+ longAssets: enrichPositionAssets(position.longAssets, perpMetaAssets),
6867
+ shortAssets: enrichPositionAssets(position.shortAssets, perpMetaAssets),
6868
+ }));
6869
+ }
6603
6870
  function usePosition() {
6604
6871
  const context = useContext(PearHyperliquidContext);
6605
6872
  if (!context) {
6606
6873
  throw new Error('usePosition must be used within a PearHyperliquidProvider');
6607
6874
  }
6608
6875
  const { apiBaseUrl, isConnected } = context;
6609
- const displayToFull = useHyperliquidData((s) => s.hip3DisplayToFull);
6876
+ const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
6610
6877
  // Create position API action
6611
6878
  const createPosition$1 = async (payload) => {
6612
- return createPosition(apiBaseUrl, payload, displayToFull);
6879
+ return createPosition(apiBaseUrl, payload, hip3Assets);
6613
6880
  };
6614
6881
  // Update TP/SL risk parameters for a position
6615
6882
  const updateRiskParameters$1 = async (positionId, payload) => {
@@ -6629,21 +6896,33 @@ function usePosition() {
6629
6896
  };
6630
6897
  // Adjust to absolute target sizes per asset, optionally adding new assets
6631
6898
  const adjustAdvancePosition$1 = async (positionId, payload) => {
6632
- return adjustAdvancePosition(apiBaseUrl, positionId, payload, displayToFull);
6899
+ return adjustAdvancePosition(apiBaseUrl, positionId, payload, hip3Assets);
6633
6900
  };
6634
6901
  // Open positions using WS data, with derived values
6635
6902
  const userOpenPositions = useUserData((state) => state.rawOpenPositions);
6636
6903
  const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
6637
6904
  const allMids = useHyperliquidData((state) => state.allMids);
6905
+ const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
6638
6906
  const isLoading = useMemo(() => {
6639
6907
  return userOpenPositions === null && isConnected;
6640
6908
  }, [userOpenPositions, isConnected]);
6641
6909
  const openPositions = useMemo(() => {
6642
6910
  if (!userOpenPositions || !aggregatedClearingHouseState || !allMids)
6643
6911
  return null;
6644
- return buildPositionValue(userOpenPositions, aggregatedClearingHouseState, allMids);
6645
- }, [userOpenPositions, aggregatedClearingHouseState, allMids]);
6646
- return { createPosition: createPosition$1, updateRiskParameters: updateRiskParameters$1, closePosition: closePosition$1, closeAllPositions: closeAllPositions$1, adjustPosition: adjustPosition$1, adjustAdvancePosition: adjustAdvancePosition$1, openPositions, isLoading };
6912
+ const positions = buildPositionValue(userOpenPositions, aggregatedClearingHouseState, allMids);
6913
+ // Enrich with market prefix and collateral token
6914
+ return enrichPositions(positions, perpMetaAssets);
6915
+ }, [userOpenPositions, aggregatedClearingHouseState, allMids, perpMetaAssets]);
6916
+ return {
6917
+ createPosition: createPosition$1,
6918
+ updateRiskParameters: updateRiskParameters$1,
6919
+ closePosition: closePosition$1,
6920
+ closeAllPositions: closeAllPositions$1,
6921
+ adjustPosition: adjustPosition$1,
6922
+ adjustAdvancePosition: adjustAdvancePosition$1,
6923
+ openPositions,
6924
+ isLoading,
6925
+ };
6647
6926
  }
6648
6927
 
6649
6928
  async function adjustOrder(baseUrl, orderId, payload) {
@@ -6815,59 +7094,170 @@ function useNotifications() {
6815
7094
  };
6816
7095
  }
6817
7096
 
6818
- // Base selector for the full market-data payload
7097
+ // Helper to find asset metadata from perpMetaAssets
7098
+ function findAssetMeta(assetName, perpMetaAssets) {
7099
+ var _a, _b, _c, _d;
7100
+ if (!perpMetaAssets) {
7101
+ return { collateralToken: 'USDC', marketPrefix: null };
7102
+ }
7103
+ // Try exact match first (for prefixed assets like "xyz:TSLA")
7104
+ const exactMatch = perpMetaAssets.find((a) => a.name === assetName);
7105
+ if (exactMatch) {
7106
+ return {
7107
+ collateralToken: (_a = exactMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
7108
+ marketPrefix: (_b = exactMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
7109
+ };
7110
+ }
7111
+ // Try matching by base symbol (for non-prefixed names in data)
7112
+ const baseMatch = perpMetaAssets.find((a) => {
7113
+ const baseName = a.name.includes(':') ? a.name.split(':')[1] : a.name;
7114
+ return baseName === assetName;
7115
+ });
7116
+ if (baseMatch) {
7117
+ return {
7118
+ collateralToken: (_c = baseMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
7119
+ marketPrefix: (_d = baseMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
7120
+ };
7121
+ }
7122
+ return { collateralToken: 'USDC', marketPrefix: null };
7123
+ }
7124
+ // Enrich a single asset with metadata
7125
+ function enrichAsset(asset, perpMetaAssets) {
7126
+ const meta = findAssetMeta(asset.asset, perpMetaAssets);
7127
+ return {
7128
+ ...asset,
7129
+ collateralToken: meta.collateralToken,
7130
+ marketPrefix: meta.marketPrefix,
7131
+ };
7132
+ }
7133
+ // Enrich a basket item with collateral info
7134
+ function enrichBasketItem(item, perpMetaAssets) {
7135
+ const enrichedLongs = item.longAssets.map((a) => enrichAsset(a, perpMetaAssets));
7136
+ const enrichedShorts = item.shortAssets.map((a) => enrichAsset(a, perpMetaAssets));
7137
+ // Determine collateral type
7138
+ const allAssets = [...enrichedLongs, ...enrichedShorts];
7139
+ const hasUsdc = allAssets.some((a) => a.collateralToken === 'USDC');
7140
+ const hasUsdh = allAssets.some((a) => a.collateralToken === 'USDH');
7141
+ let collateralType = 'USDC';
7142
+ if (hasUsdc && hasUsdh) {
7143
+ collateralType = 'MIXED';
7144
+ }
7145
+ else if (hasUsdh) {
7146
+ collateralType = 'USDH';
7147
+ }
7148
+ return {
7149
+ ...item,
7150
+ longAssets: enrichedLongs,
7151
+ shortAssets: enrichedShorts,
7152
+ collateralType,
7153
+ };
7154
+ }
7155
+ /**
7156
+ * Filter baskets by collateral type
7157
+ * - 'USDC': Only baskets where ALL assets use USDC (collateralType === 'USDC')
7158
+ * - 'USDH': Only baskets where ALL assets use USDH (collateralType === 'USDH')
7159
+ * - 'ALL' or undefined: No filtering, returns all baskets
7160
+ */
7161
+ function filterByCollateral(baskets, filter) {
7162
+ if (!filter || filter === 'ALL') {
7163
+ return baskets;
7164
+ }
7165
+ return baskets.filter((basket) => {
7166
+ if (filter === 'USDC') {
7167
+ // Include baskets that are purely USDC or have USDC assets
7168
+ return (basket.collateralType === 'USDC' || basket.collateralType === 'MIXED');
7169
+ }
7170
+ if (filter === 'USDH') {
7171
+ // Include baskets that are purely USDH or have USDH assets
7172
+ return (basket.collateralType === 'USDH' || basket.collateralType === 'MIXED');
7173
+ }
7174
+ return true;
7175
+ });
7176
+ }
7177
+ // Base selector for the full market-data payload (raw from WS)
6819
7178
  const useMarketDataPayload = () => {
6820
7179
  return useMarketData((s) => s.marketData);
6821
7180
  };
6822
- // Full payload for 'market-data-all' channel
7181
+ // Full payload for 'market-data-all' channel (raw from WS)
6823
7182
  const useMarketDataAllPayload = () => {
6824
7183
  return useMarketData((s) => s.marketDataAll);
6825
7184
  };
6826
- // Active baskets
6827
- const useActiveBaskets = () => {
6828
- var _a;
7185
+ // Access perpMetaAssets for enrichment
7186
+ const usePerpMetaAssets = () => {
7187
+ return useHyperliquidData((s) => s.perpMetaAssets);
7188
+ };
7189
+ // Active baskets (with collateral and market prefix info)
7190
+ const useActiveBaskets = (collateralFilter) => {
6829
7191
  const data = useMarketDataPayload();
6830
- return (_a = data === null || data === void 0 ? void 0 : data.active) !== null && _a !== void 0 ? _a : [];
7192
+ const perpMetaAssets = usePerpMetaAssets();
7193
+ return useMemo(() => {
7194
+ if (!(data === null || data === void 0 ? void 0 : data.active))
7195
+ return [];
7196
+ const enriched = data.active.map((item) => enrichBasketItem(item, perpMetaAssets));
7197
+ return filterByCollateral(enriched, collateralFilter);
7198
+ }, [data, perpMetaAssets, collateralFilter]);
6831
7199
  };
6832
- // Top gainers (optional limit override)
6833
- const useTopGainers = (limit) => {
7200
+ // Top gainers (with collateral and market prefix info)
7201
+ const useTopGainers = (limit, collateralFilter) => {
6834
7202
  const data = useMarketDataPayload();
7203
+ const perpMetaAssets = usePerpMetaAssets();
6835
7204
  return useMemo(() => {
6836
7205
  var _a;
6837
7206
  const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
6838
- return typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
6839
- }, [data, limit]);
7207
+ const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7208
+ const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
7209
+ return filterByCollateral(enriched, collateralFilter);
7210
+ }, [data, perpMetaAssets, limit, collateralFilter]);
6840
7211
  };
6841
- // Top losers (optional limit override)
6842
- const useTopLosers = (limit) => {
7212
+ // Top losers (with collateral and market prefix info)
7213
+ const useTopLosers = (limit, collateralFilter) => {
6843
7214
  const data = useMarketDataPayload();
7215
+ const perpMetaAssets = usePerpMetaAssets();
6844
7216
  return useMemo(() => {
6845
7217
  var _a;
6846
7218
  const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
6847
- return typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
6848
- }, [data, limit]);
7219
+ const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7220
+ const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
7221
+ return filterByCollateral(enriched, collateralFilter);
7222
+ }, [data, perpMetaAssets, limit, collateralFilter]);
6849
7223
  };
6850
- // Highlighted baskets
6851
- const useHighlightedBaskets = () => {
6852
- var _a;
7224
+ // Highlighted baskets (with collateral and market prefix info)
7225
+ const useHighlightedBaskets = (collateralFilter) => {
6853
7226
  const data = useMarketDataPayload();
6854
- return (_a = data === null || data === void 0 ? void 0 : data.highlighted) !== null && _a !== void 0 ? _a : [];
7227
+ const perpMetaAssets = usePerpMetaAssets();
7228
+ return useMemo(() => {
7229
+ if (!(data === null || data === void 0 ? void 0 : data.highlighted))
7230
+ return [];
7231
+ const enriched = data.highlighted.map((item) => enrichBasketItem(item, perpMetaAssets));
7232
+ return filterByCollateral(enriched, collateralFilter);
7233
+ }, [data, perpMetaAssets, collateralFilter]);
6855
7234
  };
6856
- // Watchlist baskets (from market-data payload when subscribed with address)
6857
- const useWatchlistBaskets = () => {
6858
- var _a;
7235
+ // Watchlist baskets (with collateral and market prefix info)
7236
+ const useWatchlistBaskets = (collateralFilter) => {
6859
7237
  const data = useMarketDataPayload();
6860
- return (_a = data === null || data === void 0 ? void 0 : data.watchlist) !== null && _a !== void 0 ? _a : [];
7238
+ const perpMetaAssets = usePerpMetaAssets();
7239
+ return useMemo(() => {
7240
+ if (!(data === null || data === void 0 ? void 0 : data.watchlist))
7241
+ return [];
7242
+ const enriched = data.watchlist.map((item) => enrichBasketItem(item, perpMetaAssets));
7243
+ return filterByCollateral(enriched, collateralFilter);
7244
+ }, [data, perpMetaAssets, collateralFilter]);
6861
7245
  };
6862
- // All baskets (from market-data-all)
6863
- const useAllBaskets = () => {
6864
- var _a;
7246
+ // All baskets (with collateral and market prefix info)
7247
+ const useAllBaskets = (collateralFilter) => {
6865
7248
  const dataAll = useMarketDataAllPayload();
6866
- return (_a = dataAll === null || dataAll === void 0 ? void 0 : dataAll.all) !== null && _a !== void 0 ? _a : [];
7249
+ const perpMetaAssets = usePerpMetaAssets();
7250
+ return useMemo(() => {
7251
+ if (!(dataAll === null || dataAll === void 0 ? void 0 : dataAll.all))
7252
+ return [];
7253
+ const enriched = dataAll.all.map((item) => enrichBasketItem(item, perpMetaAssets));
7254
+ return filterByCollateral(enriched, collateralFilter);
7255
+ }, [dataAll, perpMetaAssets, collateralFilter]);
6867
7256
  };
6868
7257
  // Find a basket by its exact asset composition (order-insensitive)
6869
7258
  const useFindBasket = (longs, shorts) => {
6870
7259
  const data = useMarketDataPayload();
7260
+ const perpMetaAssets = usePerpMetaAssets();
6871
7261
  return useMemo(() => {
6872
7262
  if (!data)
6873
7263
  return undefined;
@@ -6881,17 +7271,28 @@ const useFindBasket = (longs, shorts) => {
6881
7271
  : '';
6882
7272
  const lKey = normalize(longs);
6883
7273
  const sKey = normalize(shorts);
6884
- const match = (item) => normalize(item.longAssets) === lKey && normalize(item.shortAssets) === sKey;
6885
- return data.active.find(match) || data.highlighted.find(match);
6886
- }, [data, longs, shorts]);
7274
+ const match = (item) => normalize(item.longAssets) === lKey &&
7275
+ normalize(item.shortAssets) === sKey;
7276
+ const found = data.active.find(match) || data.highlighted.find(match);
7277
+ return found
7278
+ ? enrichBasketItem(found, perpMetaAssets)
7279
+ : undefined;
7280
+ }, [data, longs, shorts, perpMetaAssets]);
6887
7281
  };
6888
7282
 
6889
- async function toggleWatchlist(baseUrl, longAssets, shortAssets, displayToFull) {
7283
+ async function toggleWatchlist(baseUrl, longAssets, shortAssets, hip3Assets) {
6890
7284
  const url = joinUrl(baseUrl, '/watchlist');
6891
- const mapAssets = (arr) => arr.map(a => ({ ...a, asset: toBackendSymbol(a.asset, displayToFull) }));
7285
+ const mapAssets = (arr) => arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
6892
7286
  try {
6893
- const response = await apiClient.post(url, { longAssets: mapAssets(longAssets), shortAssets: mapAssets(shortAssets) }, { headers: { 'Content-Type': 'application/json' } });
6894
- return { data: response.data, status: response.status, headers: response.headers };
7287
+ const response = await apiClient.post(url, {
7288
+ longAssets: mapAssets(longAssets),
7289
+ shortAssets: mapAssets(shortAssets),
7290
+ }, { headers: { 'Content-Type': 'application/json' } });
7291
+ return {
7292
+ data: response.data,
7293
+ status: response.status,
7294
+ headers: response.headers,
7295
+ };
6895
7296
  }
6896
7297
  catch (error) {
6897
7298
  throw toApiError(error);
@@ -6903,11 +7304,11 @@ function useWatchlist() {
6903
7304
  if (!context)
6904
7305
  throw new Error('useWatchlist must be used within a PearHyperliquidProvider');
6905
7306
  const { apiBaseUrl, isConnected } = context;
6906
- const displayToFull = useHyperliquidData((s) => s.hip3DisplayToFull);
7307
+ const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
6907
7308
  const marketData = useMarketDataPayload();
6908
7309
  const isLoading = useMemo(() => !marketData && isConnected, [marketData, isConnected]);
6909
7310
  const toggle = async (longAssets, shortAssets) => {
6910
- const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, displayToFull);
7311
+ const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, hip3Assets);
6911
7312
  // Server will push updated market-data over WS; nothing to set here
6912
7313
  return resp;
6913
7314
  };
@@ -7029,7 +7430,7 @@ async function logout(baseUrl, refreshTokenVal) {
7029
7430
  function useAuth() {
7030
7431
  const context = useContext(PearHyperliquidContext);
7031
7432
  if (!context) {
7032
- throw new Error("usePortfolio must be used within a PearHyperliquidProvider");
7433
+ throw new Error('usePortfolio must be used within a PearHyperliquidProvider');
7033
7434
  }
7034
7435
  const { apiBaseUrl, clientId } = context;
7035
7436
  const [isReady, setIsReady] = useState(false);
@@ -7039,46 +7440,26 @@ function useAuth() {
7039
7440
  const setRefreshToken = useUserData((s) => s.setRefreshToken);
7040
7441
  const isAuthenticated = useUserData((s) => s.isAuthenticated);
7041
7442
  const setIsAuthenticated = useUserData((s) => s.setIsAuthenticated);
7042
- const address = useUserData((s) => s.address);
7043
7443
  const setAddress = useUserData((s) => s.setAddress);
7044
7444
  useEffect(() => {
7045
- if (typeof window == "undefined") {
7445
+ if (typeof window == 'undefined') {
7046
7446
  return;
7047
7447
  }
7048
- // Get the current address from state if it exists
7049
- const currentAddress = address;
7050
- if (currentAddress) {
7051
- // If we already have an address in state, use it to load the session
7052
- const accessTokenKey = `${currentAddress}_accessToken`;
7053
- const refreshTokenKey = `${currentAddress}_refreshToken`;
7054
- const storedAccessToken = localStorage.getItem(accessTokenKey);
7055
- const storedRefreshToken = localStorage.getItem(refreshTokenKey);
7056
- if (storedAccessToken && storedRefreshToken) {
7057
- setAccessToken(storedAccessToken);
7058
- setRefreshToken(storedRefreshToken);
7059
- setIsAuthenticated(true);
7060
- setIsReady(true);
7061
- return;
7062
- }
7063
- }
7064
- }, [
7065
- setAccessToken,
7066
- setRefreshToken,
7067
- setIsAuthenticated,
7068
- setAddress,
7069
- address,
7070
- ]);
7448
+ const access = localStorage.getItem('accessToken');
7449
+ const refresh = localStorage.getItem('refreshToken');
7450
+ const addr = localStorage.getItem('address');
7451
+ setAccessToken(access);
7452
+ setRefreshToken(refresh);
7453
+ setAddress(addr);
7454
+ const authed = Boolean(access && addr);
7455
+ setIsAuthenticated(authed);
7456
+ setIsReady(true);
7457
+ }, [setAccessToken, setRefreshToken, setIsAuthenticated, setAddress]);
7071
7458
  useEffect(() => {
7072
7459
  const cleanup = addAuthInterceptors({
7073
7460
  apiBaseUrl,
7074
7461
  getAccessToken: () => {
7075
- if (typeof window === "undefined")
7076
- return null;
7077
- const currentAddress = useUserData.getState().address;
7078
- if (!currentAddress)
7079
- return null;
7080
- const accessTokenKey = `${currentAddress}_accessToken`;
7081
- return localStorage.getItem(accessTokenKey);
7462
+ return typeof window !== 'undefined' ? window.localStorage.getItem('accessToken') : null;
7082
7463
  },
7083
7464
  refreshTokens: async () => {
7084
7465
  const data = await refreshTokens();
@@ -7099,15 +7480,14 @@ function useAuth() {
7099
7480
  async function loginWithSignedMessage(address, signature, timestamp) {
7100
7481
  try {
7101
7482
  const { data } = await authenticate(apiBaseUrl, {
7102
- method: "eip712",
7483
+ method: 'eip712',
7103
7484
  address,
7104
7485
  clientId,
7105
7486
  details: { signature, timestamp },
7106
7487
  });
7107
- const accessTokenKey = `${address}_accessToken`;
7108
- const refreshTokenKey = `${address}_refreshToken`;
7109
- window.localStorage.setItem(accessTokenKey, data.accessToken);
7110
- window.localStorage.setItem(refreshTokenKey, data.refreshToken);
7488
+ window.localStorage.setItem('accessToken', data.accessToken);
7489
+ window.localStorage.setItem('refreshToken', data.refreshToken);
7490
+ window.localStorage.setItem('address', address);
7111
7491
  setAccessToken(data.accessToken);
7112
7492
  setRefreshToken(data.refreshToken);
7113
7493
  setAddress(address);
@@ -7119,16 +7499,10 @@ function useAuth() {
7119
7499
  }
7120
7500
  async function loginWithPrivyToken(address, appId, privyAccessToken) {
7121
7501
  try {
7122
- const { data } = await authenticateWithPrivy(apiBaseUrl, {
7123
- address,
7124
- clientId,
7125
- appId,
7126
- accessToken: privyAccessToken,
7127
- });
7128
- const accessTokenKey = `${address}_accessToken`;
7129
- const refreshTokenKey = `${address}_refreshToken`;
7130
- window.localStorage.setItem(accessTokenKey, data.accessToken);
7131
- window.localStorage.setItem(refreshTokenKey, data.refreshToken);
7502
+ const { data } = await authenticateWithPrivy(apiBaseUrl, { address, clientId, appId, accessToken: privyAccessToken });
7503
+ window.localStorage.setItem('accessToken', data.accessToken);
7504
+ window.localStorage.setItem('refreshToken', data.refreshToken);
7505
+ window.localStorage.setItem('address', address);
7132
7506
  setAccessToken(data.accessToken);
7133
7507
  setRefreshToken(data.refreshToken);
7134
7508
  setAddress(address);
@@ -7139,38 +7513,28 @@ function useAuth() {
7139
7513
  }
7140
7514
  }
7141
7515
  async function refreshTokens() {
7142
- const currentAddress = address;
7143
- const currentRefresh = refreshToken$1;
7144
- if (!currentRefresh || !currentAddress)
7145
- throw new Error("No refresh token");
7146
- const { data } = await refreshToken(apiBaseUrl, currentRefresh);
7147
- // Update tokens in localStorage
7148
- const accessTokenKey = `${currentAddress}_accessToken`;
7149
- const refreshTokenKey = `${currentAddress}_refreshToken`;
7150
- window.localStorage.setItem(accessTokenKey, data.accessToken);
7151
- window.localStorage.setItem(refreshTokenKey, data.refreshToken);
7516
+ const refresh = window.localStorage.getItem('refreshToken');
7517
+ if (!refresh)
7518
+ throw new Error('No refresh token');
7519
+ const { data } = await refreshToken(apiBaseUrl, refresh);
7520
+ window.localStorage.setItem('accessToken', data.accessToken);
7521
+ window.localStorage.setItem('refreshToken', data.refreshToken);
7152
7522
  setAccessToken(data.accessToken);
7153
7523
  setRefreshToken(data.refreshToken);
7154
7524
  setIsAuthenticated(true);
7155
7525
  return data;
7156
7526
  }
7157
7527
  async function logout$1() {
7158
- const currentAddress = address;
7159
- const currentRefresh = refreshToken$1;
7160
- if (currentRefresh) {
7528
+ const refresh = window.localStorage.getItem('refreshToken');
7529
+ if (refresh) {
7161
7530
  try {
7162
- await logout(apiBaseUrl, currentRefresh);
7163
- }
7164
- catch (_a) {
7165
- /* ignore */
7531
+ await logout(apiBaseUrl, refresh);
7166
7532
  }
7533
+ catch ( /* ignore */_a) { /* ignore */ }
7167
7534
  }
7168
- if (currentAddress) {
7169
- const accessTokenKey = `${currentAddress}_accessToken`;
7170
- const refreshTokenKey = `${currentAddress}_refreshToken`;
7171
- window.localStorage.removeItem(accessTokenKey);
7172
- window.localStorage.removeItem(refreshTokenKey);
7173
- }
7535
+ window.localStorage.removeItem('accessToken');
7536
+ window.localStorage.removeItem('refreshToken');
7537
+ window.localStorage.removeItem('address');
7174
7538
  setAccessToken(null);
7175
7539
  setRefreshToken(null);
7176
7540
  setAddress(null);
@@ -7193,12 +7557,13 @@ const PearHyperliquidContext = createContext(undefined);
7193
7557
  /**
7194
7558
  * React Provider for PearHyperliquidClient
7195
7559
  */
7196
- const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-v2.pearprotocol.io", clientId = "PEARPROTOCOLUI", wsUrl = "wss://hl-v2.pearprotocol.io/ws", }) => {
7560
+ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-v2.pearprotocol.io', clientId = 'PEARPROTOCOLUI', wsUrl = 'wss://hl-v2.pearprotocol.io/ws', }) => {
7197
7561
  const address = useUserData((s) => s.address);
7198
7562
  const setAddress = useUserData((s) => s.setAddress);
7199
7563
  const perpsMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
7200
7564
  const setPerpMetaAssets = useHyperliquidData((state) => state.setPerpMetaAssets);
7201
- const setHip3DisplayToFull = useHyperliquidData((state) => state.setHip3DisplayToFull);
7565
+ const setHip3Assets = useHyperliquidData((state) => state.setHip3Assets);
7566
+ const setHip3MarketPrefixes = useHyperliquidData((state) => state.setHip3MarketPrefixes);
7202
7567
  const websocketsEnabled = useMemo(() => Array.isArray(perpsMetaAssets) && perpsMetaAssets.length > 0, [perpsMetaAssets]);
7203
7568
  const { isConnected, lastError } = useHyperliquidWebSocket({
7204
7569
  wsUrl,
@@ -7213,28 +7578,62 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-v2.pearpro
7213
7578
  if (perpsMetaAssets === null) {
7214
7579
  fetchAllPerpMetas()
7215
7580
  .then((res) => {
7216
- const aggregatedPerpMetas = res.data.flatMap((item) => item.universe);
7217
- const hip3Map = new Map();
7218
- const displayToFull = new Map();
7219
- const cleanedPerpMetas = aggregatedPerpMetas.map((asset) => {
7220
- var _a;
7221
- const [maybePrefix, maybeMarket] = asset.name.split(":");
7222
- if (maybeMarket) {
7223
- const prefix = maybePrefix.toLowerCase();
7224
- const market = maybeMarket;
7225
- const existing = (_a = hip3Map.get(prefix)) !== null && _a !== void 0 ? _a : [];
7226
- hip3Map.set(prefix, [...existing, market]);
7227
- displayToFull.set(market, `${prefix}:${market}`);
7228
- return { ...asset, name: market };
7229
- }
7230
- return asset;
7581
+ // Maps for multi-market assets
7582
+ const assetToMarkets = new Map(); // TSLA -> ["xyz:TSLA", "flx:TSLA"]
7583
+ const marketPrefixes = new Map(); // "xyz:TSLA" -> "xyz"
7584
+ const cleanedPerpMetas = [];
7585
+ // Process each market group (different collateral tokens)
7586
+ res.data.forEach((item) => {
7587
+ // Convert numeric collateral token to human-readable name
7588
+ const collateralToken = item.collateralToken === 360 ? 'USDH' : 'USDC';
7589
+ item.universe.forEach((asset) => {
7590
+ var _a;
7591
+ const [maybePrefix, maybeMarket] = asset.name.split(':');
7592
+ if (maybeMarket) {
7593
+ // HIP-3 market with prefix (e.g., "xyz:TSLA")
7594
+ const prefix = maybePrefix.toLowerCase();
7595
+ const displayName = maybeMarket;
7596
+ const fullName = `${prefix}:${displayName}`;
7597
+ // Store full market name with prefix
7598
+ marketPrefixes.set(fullName, prefix);
7599
+ // Track all markets for this asset
7600
+ const existingMarkets = (_a = assetToMarkets.get(displayName)) !== null && _a !== void 0 ? _a : [];
7601
+ if (!existingMarkets.includes(fullName)) {
7602
+ assetToMarkets.set(displayName, [
7603
+ ...existingMarkets,
7604
+ fullName,
7605
+ ]);
7606
+ }
7607
+ // Add asset with all metadata INCLUDING collateral token for THIS specific market
7608
+ // Important: We keep ALL variants so each market+collateral combo is tracked
7609
+ cleanedPerpMetas.push({
7610
+ ...asset,
7611
+ name: displayName, // Use display name for UI
7612
+ marketPrefix: prefix, // Which market (xyz, flx, etc)
7613
+ collateralToken, // "USDC" or "USDH"
7614
+ });
7615
+ }
7616
+ else {
7617
+ // Regular market without prefix
7618
+ cleanedPerpMetas.push({
7619
+ ...asset,
7620
+ collateralToken, // "USDC" or "USDH"
7621
+ });
7622
+ }
7623
+ });
7231
7624
  });
7232
- setHip3DisplayToFull(displayToFull);
7625
+ setHip3Assets(assetToMarkets);
7626
+ setHip3MarketPrefixes(marketPrefixes);
7233
7627
  setPerpMetaAssets(cleanedPerpMetas);
7234
7628
  })
7235
7629
  .catch(() => { });
7236
7630
  }
7237
- }, [perpsMetaAssets, setPerpMetaAssets, setHip3DisplayToFull]);
7631
+ }, [
7632
+ perpsMetaAssets,
7633
+ setPerpMetaAssets,
7634
+ setHip3Assets,
7635
+ setHip3MarketPrefixes,
7636
+ ]);
7238
7637
  // Auth methods now sourced from useAuth hook
7239
7638
  useAutoSyncFills({
7240
7639
  baseUrl: apiBaseUrl,
@@ -7254,9 +7653,6 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-v2.pearpro
7254
7653
  // HyperLiquid native WebSocket state
7255
7654
  nativeIsConnected,
7256
7655
  nativeLastError,
7257
- // Address utilities
7258
- address,
7259
- setAddress,
7260
7656
  }), [
7261
7657
  apiBaseUrl,
7262
7658
  wsUrl,
@@ -7276,7 +7672,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-v2.pearpro
7276
7672
  function usePearHyperliquid() {
7277
7673
  const ctx = useContext(PearHyperliquidContext);
7278
7674
  if (!ctx)
7279
- throw new Error("usePearHyperliquid must be used within a PearHyperliquidProvider");
7675
+ throw new Error('usePearHyperliquid must be used within a PearHyperliquidProvider');
7280
7676
  return ctx;
7281
7677
  }
7282
7678
 
@@ -7367,4 +7763,4 @@ function mapCandleIntervalToTradingViewInterval(interval) {
7367
7763
  }
7368
7764
  }
7369
7765
 
7370
- export { AccountSummaryCalculator, ConflictDetector, MINIMUM_ASSET_USD_VALUE, MinimumPositionSizeError, PearHyperliquidProvider, TokenMetadataExtractor, adjustAdvancePosition, adjustOrder, adjustPosition, calculateMinimumPositionValue, calculateWeightedRatio, cancelOrder, cancelTwap, cancelTwapOrder, closeAllPositions, closePosition, computeBasketCandles, createCandleLookups, createPosition, getCompleteTimestamps, getPortfolio, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, toggleWatchlist, updateRiskParameters, useAccountSummary, useActiveBaskets, useAgentWallet, useAllBaskets, useAuth, useAutoSyncFills, useBasketCandles, useFindBasket, useHighlightedBaskets, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMarketData, useMarketDataAllPayload, useMarketDataPayload, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePortfolio, usePosition, useTokenSelectionMetadata, useTopGainers, useTopLosers, useTradeHistories, useTwap, useUserSelection, useWatchlist, useWatchlistBaskets, useWebData, validateMinimumAssetSize, validatePositionSize };
7766
+ export { AccountSummaryCalculator, ConflictDetector, MINIMUM_ASSET_USD_VALUE, MinimumPositionSizeError, PearHyperliquidProvider, TokenMetadataExtractor, adjustAdvancePosition, adjustOrder, adjustPosition, calculateMinimumPositionValue, calculateWeightedRatio, cancelOrder, cancelTwap, cancelTwapOrder, closeAllPositions, closePosition, computeBasketCandles, createCandleLookups, createPosition, getAvailableMarkets, getCompleteTimestamps, getMarketPrefix, getPortfolio, isHip3Market, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, toBackendSymbol, toBackendSymbolWithMarket, toDisplaySymbol, toggleWatchlist, updateRiskParameters, useAccountSummary, useActiveBaskets, useAgentWallet, useAllBaskets, useAuth, useAutoSyncFills, useBasketCandles, useFindBasket, useHighlightedBaskets, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMarketData, useMarketDataAllPayload, useMarketDataPayload, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePerpMetaAssets, usePortfolio, usePosition, useTokenSelectionMetadata, useTopGainers, useTopLosers, useTradeHistories, useTwap, useUserSelection, useWatchlist, useWatchlistBaskets, useWebData, validateMinimumAssetSize, validatePositionSize };