@pear-protocol/hyperliquid-sdk 0.0.59 → 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/clients/hyperliquid.d.ts +1 -1
- package/dist/clients/positions.d.ts +2 -2
- package/dist/clients/watchlist.d.ts +1 -1
- package/dist/hooks/useMarketData.d.ts +9 -7
- package/dist/hooks/useWebData.d.ts +21 -3
- package/dist/index.d.ts +119 -21
- package/dist/index.js +626 -145
- package/dist/store/tokenSelectionMetadataStore.d.ts +1 -1
- package/dist/types.d.ts +36 -3
- package/dist/utils/symbol-translator.d.ts +32 -3
- package/dist/utils/token-metadata-extractor.d.ts +9 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -71,18 +71,71 @@ const useMarketData = create((set) => ({
|
|
|
71
71
|
* Convert a full/prefixed symbol (e.g., "xyz:XYZ100") to a display symbol (e.g., "XYZ100").
|
|
72
72
|
*/
|
|
73
73
|
function toDisplaySymbol(symbol) {
|
|
74
|
-
const parts = symbol.split(
|
|
74
|
+
const parts = symbol.split(':');
|
|
75
75
|
return parts.length > 1 ? parts.slice(-1)[0] : symbol;
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
78
|
* Convert a display symbol back to backend form using a provided map.
|
|
79
79
|
* If mapping is missing, returns the original symbol.
|
|
80
|
-
*
|
|
81
|
-
* @param
|
|
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"])
|
|
82
83
|
*/
|
|
83
|
-
function toBackendSymbol(displaySymbol,
|
|
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"]
|
|
118
|
+
*/
|
|
119
|
+
function getAvailableMarkets(displaySymbol, hip3Assets) {
|
|
84
120
|
var _a;
|
|
85
|
-
return (_a =
|
|
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(':');
|
|
86
139
|
}
|
|
87
140
|
|
|
88
141
|
const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
@@ -318,10 +371,11 @@ const useHyperliquidData = create((set, get) => ({
|
|
|
318
371
|
finalAtOICaps: null,
|
|
319
372
|
aggregatedClearingHouseState: null,
|
|
320
373
|
perpMetaAssets: null,
|
|
321
|
-
|
|
374
|
+
hip3Assets: new Map(),
|
|
375
|
+
hip3MarketPrefixes: new Map(),
|
|
322
376
|
setAllMids: (value) => set({ allMids: value }),
|
|
323
377
|
setActiveAssetData: (value) => set((state) => ({
|
|
324
|
-
activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value
|
|
378
|
+
activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value,
|
|
325
379
|
})),
|
|
326
380
|
deleteActiveAssetData: (key) => {
|
|
327
381
|
set((state) => {
|
|
@@ -356,13 +410,14 @@ const useHyperliquidData = create((set, get) => ({
|
|
|
356
410
|
activeAssetData: {
|
|
357
411
|
...state.activeAssetData,
|
|
358
412
|
[key]: value,
|
|
359
|
-
}
|
|
413
|
+
},
|
|
360
414
|
})),
|
|
361
415
|
setFinalAssetContexts: (value) => set({ finalAssetContexts: value }),
|
|
362
416
|
setFinalAtOICaps: (value) => set({ finalAtOICaps: value }),
|
|
363
417
|
setAggregatedClearingHouseState: (value) => set({ aggregatedClearingHouseState: value }),
|
|
364
418
|
setPerpMetaAssets: (value) => set({ perpMetaAssets: value }),
|
|
365
|
-
|
|
419
|
+
setHip3Assets: (value) => set({ hip3Assets: value }),
|
|
420
|
+
setHip3MarketPrefixes: (value) => set({ hip3MarketPrefixes: value }),
|
|
366
421
|
}));
|
|
367
422
|
|
|
368
423
|
const DEFAULT_STATE = {
|
|
@@ -503,11 +558,11 @@ const useUserSelection$1 = create((set, get) => ({
|
|
|
503
558
|
}));
|
|
504
559
|
|
|
505
560
|
const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
506
|
-
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();
|
|
507
562
|
const { candleInterval } = useUserSelection$1();
|
|
508
563
|
const longTokens = useUserSelection$1((s) => s.longTokens);
|
|
509
564
|
const shortTokens = useUserSelection$1((s) => s.shortTokens);
|
|
510
|
-
const selectedTokenSymbols = useMemo(() =>
|
|
565
|
+
const selectedTokenSymbols = useMemo(() => [...longTokens, ...shortTokens].map((t) => t.symbol), [longTokens, shortTokens]);
|
|
511
566
|
const [lastError, setLastError] = useState(null);
|
|
512
567
|
const [subscribedAddress, setSubscribedAddress] = useState(null);
|
|
513
568
|
const [subscribedTokens, setSubscribedTokens] = useState([]);
|
|
@@ -545,7 +600,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
545
600
|
.map((dex) => dex.clearinghouseState)
|
|
546
601
|
.filter(Boolean);
|
|
547
602
|
const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || '0') || 0), 0);
|
|
548
|
-
const toStr = (n) =>
|
|
603
|
+
const toStr = (n) => Number.isFinite(n) ? n.toString() : '0';
|
|
549
604
|
const assetPositions = states.flatMap((s) => s.assetPositions || []);
|
|
550
605
|
const crossMaintenanceMarginUsed = toStr(sum(states.map((s) => s.crossMaintenanceMarginUsed)));
|
|
551
606
|
const crossMarginSummary = {
|
|
@@ -578,17 +633,42 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
578
633
|
case 'allMids':
|
|
579
634
|
{
|
|
580
635
|
const data = response.data;
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
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 });
|
|
585
662
|
}
|
|
586
663
|
break;
|
|
587
664
|
case 'activeAssetData':
|
|
588
665
|
{
|
|
589
666
|
const assetData = response.data;
|
|
590
667
|
const symbol = toDisplaySymbol(assetData.coin);
|
|
591
|
-
const normalized = {
|
|
668
|
+
const normalized = {
|
|
669
|
+
...assetData,
|
|
670
|
+
coin: symbol,
|
|
671
|
+
};
|
|
592
672
|
upsertActiveAssetData(symbol, normalized);
|
|
593
673
|
}
|
|
594
674
|
break;
|
|
@@ -610,7 +690,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
610
690
|
console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
|
|
611
691
|
setLastError(errorMessage);
|
|
612
692
|
}
|
|
613
|
-
}, [
|
|
693
|
+
}, [
|
|
694
|
+
setAllMids,
|
|
695
|
+
upsertActiveAssetData,
|
|
696
|
+
addCandleData,
|
|
697
|
+
setFinalAssetContexts,
|
|
698
|
+
setFinalAtOICaps,
|
|
699
|
+
setAggregatedClearingHouseState,
|
|
700
|
+
]);
|
|
614
701
|
const connect = useCallback(() => {
|
|
615
702
|
if (!enabled)
|
|
616
703
|
return;
|
|
@@ -733,7 +820,13 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
733
820
|
// clear aggregatedClearingHouseState
|
|
734
821
|
setAggregatedClearingHouseState(null);
|
|
735
822
|
}
|
|
736
|
-
}, [
|
|
823
|
+
}, [
|
|
824
|
+
isConnected,
|
|
825
|
+
address,
|
|
826
|
+
subscribedAddress,
|
|
827
|
+
sendJsonMessage,
|
|
828
|
+
setAggregatedClearingHouseState,
|
|
829
|
+
]);
|
|
737
830
|
// Handle token subscriptions for activeAssetData
|
|
738
831
|
useEffect(() => {
|
|
739
832
|
if (!isConnected || !address)
|
|
@@ -742,7 +835,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
742
835
|
const tokensToSubscribe = effectiveTokens.filter((token) => token && !subscribedTokens.includes(token));
|
|
743
836
|
const tokensToUnsubscribe = subscribedTokens.filter((token) => !effectiveTokens.includes(token));
|
|
744
837
|
// Unsubscribe from tokens no longer in the list
|
|
745
|
-
tokensToUnsubscribe.forEach(token => {
|
|
838
|
+
tokensToUnsubscribe.forEach((token) => {
|
|
746
839
|
const unsubscribeMessage = {
|
|
747
840
|
method: 'unsubscribe',
|
|
748
841
|
subscription: {
|
|
@@ -754,7 +847,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
754
847
|
sendJsonMessage(unsubscribeMessage);
|
|
755
848
|
});
|
|
756
849
|
// Subscribe to new tokens
|
|
757
|
-
tokensToSubscribe.forEach(token => {
|
|
850
|
+
tokensToSubscribe.forEach((token) => {
|
|
758
851
|
const subscribeMessage = {
|
|
759
852
|
method: 'subscribe',
|
|
760
853
|
subscription: {
|
|
@@ -769,7 +862,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
769
862
|
setSubscribedTokens(effectiveTokens.filter((token) => token));
|
|
770
863
|
tokensToSubscribe.forEach((token) => deleteActiveAssetData(token));
|
|
771
864
|
}
|
|
772
|
-
}, [
|
|
865
|
+
}, [
|
|
866
|
+
isConnected,
|
|
867
|
+
address,
|
|
868
|
+
selectedTokenSymbols,
|
|
869
|
+
subscribedTokens,
|
|
870
|
+
sendJsonMessage,
|
|
871
|
+
setActiveAssetData,
|
|
872
|
+
]);
|
|
773
873
|
// Handle candle subscriptions for tokens and interval changes
|
|
774
874
|
useEffect(() => {
|
|
775
875
|
if (!isConnected)
|
|
@@ -778,7 +878,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
778
878
|
// Unsubscribe from previous candle subscriptions if interval changed
|
|
779
879
|
const prevInterval = prevCandleIntervalRef.current;
|
|
780
880
|
if (prevInterval && prevInterval !== candleInterval) {
|
|
781
|
-
subscribedCandleTokens.forEach(token => {
|
|
881
|
+
subscribedCandleTokens.forEach((token) => {
|
|
782
882
|
const unsubscribeMessage = {
|
|
783
883
|
method: 'unsubscribe',
|
|
784
884
|
subscription: {
|
|
@@ -819,12 +919,21 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
819
919
|
sendJsonMessage(subscribeMessage);
|
|
820
920
|
});
|
|
821
921
|
// Update subscribed state
|
|
822
|
-
if (tokensToSubscribe.length > 0 ||
|
|
922
|
+
if (tokensToSubscribe.length > 0 ||
|
|
923
|
+
tokensToUnsubscribe.length > 0 ||
|
|
924
|
+
prevInterval !== candleInterval) {
|
|
823
925
|
setSubscribedCandleTokens(effectiveTokens.filter((token) => token));
|
|
824
926
|
prevCandleIntervalRef.current = candleInterval;
|
|
825
927
|
tokensToUnsubscribe.forEach((token) => deleteCandleSymbol(token));
|
|
826
928
|
}
|
|
827
|
-
}, [
|
|
929
|
+
}, [
|
|
930
|
+
isConnected,
|
|
931
|
+
selectedTokenSymbols,
|
|
932
|
+
candleInterval,
|
|
933
|
+
subscribedCandleTokens,
|
|
934
|
+
sendJsonMessage,
|
|
935
|
+
setCandleData,
|
|
936
|
+
]);
|
|
828
937
|
return {
|
|
829
938
|
isConnected,
|
|
830
939
|
lastError,
|
|
@@ -946,21 +1055,51 @@ const useWebData = () => {
|
|
|
946
1055
|
const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
947
1056
|
const aggregatedClearinghouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
948
1057
|
const finalAtOICaps = useHyperliquidData((state) => state.finalAtOICaps);
|
|
949
|
-
const hip3Assets = useHyperliquidData((state) => state.
|
|
1058
|
+
const hip3Assets = useHyperliquidData((state) => state.hip3Assets);
|
|
1059
|
+
const hip3MarketPrefixes = useHyperliquidData((state) => state.hip3MarketPrefixes);
|
|
950
1060
|
let marketDataBySymbol = {};
|
|
951
1061
|
if (finalAssetContexts && perpMetaAssets) {
|
|
952
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
|
|
953
1068
|
for (let index = 0; index < perpMetaAssets.length; index++) {
|
|
954
1069
|
const name = perpMetaAssets[index].name;
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
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
|
+
}
|
|
959
1097
|
}
|
|
960
1098
|
marketDataBySymbol = result;
|
|
961
1099
|
}
|
|
962
1100
|
return {
|
|
963
1101
|
hip3Assets,
|
|
1102
|
+
hip3MarketPrefixes,
|
|
964
1103
|
clearinghouseState: aggregatedClearinghouseState,
|
|
965
1104
|
perpsAtOpenInterestCap: finalAtOICaps,
|
|
966
1105
|
marketDataBySymbol,
|
|
@@ -975,30 +1114,60 @@ const useWebData = () => {
|
|
|
975
1114
|
class TokenMetadataExtractor {
|
|
976
1115
|
/**
|
|
977
1116
|
* Extracts comprehensive token metadata
|
|
978
|
-
* @param symbol - Token symbol
|
|
1117
|
+
* @param symbol - Token symbol (base symbol without prefix, e.g., "TSLA")
|
|
979
1118
|
* @param perpMetaAssets - Aggregated universe assets (flattened across dexes)
|
|
980
1119
|
* @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
|
|
981
1120
|
* @param allMids - AllMids data containing current prices
|
|
982
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
|
|
983
1123
|
* @returns TokenMetadata or null if token not found
|
|
984
1124
|
*/
|
|
985
|
-
static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
|
|
1125
|
+
static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketPrefix) {
|
|
986
1126
|
if (!perpMetaAssets || !finalAssetContexts || !allMids) {
|
|
987
1127
|
return null;
|
|
988
1128
|
}
|
|
989
1129
|
// Find token index in aggregated universe
|
|
990
|
-
|
|
991
|
-
|
|
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) {
|
|
992
1150
|
return null;
|
|
993
1151
|
}
|
|
994
|
-
const universeAsset = perpMetaAssets[
|
|
995
|
-
const assetCtx = finalAssetContexts[
|
|
1152
|
+
const universeAsset = perpMetaAssets[resolvedIndex];
|
|
1153
|
+
const assetCtx = finalAssetContexts[resolvedIndex];
|
|
996
1154
|
if (!assetCtx) {
|
|
997
1155
|
return null;
|
|
998
1156
|
}
|
|
999
|
-
// Get current price
|
|
1000
|
-
|
|
1001
|
-
const
|
|
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
|
+
}
|
|
1002
1171
|
// Get previous day price
|
|
1003
1172
|
const prevDayPrice = parseFloat(assetCtx.prevDayPx);
|
|
1004
1173
|
// Calculate 24h price change
|
|
@@ -1009,7 +1178,11 @@ class TokenMetadataExtractor {
|
|
|
1009
1178
|
const markPrice = parseFloat(assetCtx.markPx);
|
|
1010
1179
|
const oraclePrice = parseFloat(assetCtx.oraclePx);
|
|
1011
1180
|
// Extract leverage info from activeAssetData if available
|
|
1012
|
-
|
|
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];
|
|
1013
1186
|
const leverage = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.leverage;
|
|
1014
1187
|
const maxTradeSzs = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.maxTradeSzs;
|
|
1015
1188
|
const availableToTrade = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.availableToTrade;
|
|
@@ -1027,21 +1200,27 @@ class TokenMetadataExtractor {
|
|
|
1027
1200
|
leverage,
|
|
1028
1201
|
maxTradeSzs,
|
|
1029
1202
|
availableToTrade,
|
|
1203
|
+
collateralToken: universeAsset.collateralToken,
|
|
1030
1204
|
};
|
|
1031
1205
|
}
|
|
1032
1206
|
/**
|
|
1033
1207
|
* Extracts metadata for multiple tokens
|
|
1034
|
-
* @param
|
|
1208
|
+
* @param tokens - Array of token objects with symbol and optional marketPrefix
|
|
1035
1209
|
* @param perpMetaAssets - Aggregated universe assets
|
|
1036
1210
|
* @param finalAssetContexts - Aggregated asset contexts
|
|
1037
1211
|
* @param allMids - AllMids data
|
|
1038
1212
|
* @param activeAssetData - Optional active asset data containing leverage information
|
|
1039
|
-
* @returns Record of
|
|
1213
|
+
* @returns Record of unique key to TokenMetadata. Key is "{prefix}:{symbol}" for HIP3 assets, or just "{symbol}" otherwise
|
|
1040
1214
|
*/
|
|
1041
|
-
static extractMultipleTokensMetadata(
|
|
1215
|
+
static extractMultipleTokensMetadata(tokens, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
|
|
1042
1216
|
const result = {};
|
|
1043
|
-
for (const
|
|
1044
|
-
|
|
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);
|
|
1045
1224
|
}
|
|
1046
1225
|
return result;
|
|
1047
1226
|
}
|
|
@@ -1054,10 +1233,30 @@ class TokenMetadataExtractor {
|
|
|
1054
1233
|
static isTokenAvailable(symbol, perpMetaAssets) {
|
|
1055
1234
|
if (!perpMetaAssets)
|
|
1056
1235
|
return false;
|
|
1057
|
-
return perpMetaAssets.some(asset => asset.name === symbol);
|
|
1236
|
+
return perpMetaAssets.some((asset) => asset.name === symbol);
|
|
1058
1237
|
}
|
|
1059
1238
|
}
|
|
1060
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
|
+
}
|
|
1061
1260
|
const useTokenSelectionMetadataStore = create((set) => ({
|
|
1062
1261
|
isPriceDataReady: false,
|
|
1063
1262
|
isLoading: true,
|
|
@@ -1067,23 +1266,65 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1067
1266
|
weightedRatio24h: 1,
|
|
1068
1267
|
priceRatio: 1,
|
|
1069
1268
|
priceRatio24h: 1,
|
|
1070
|
-
openInterest:
|
|
1071
|
-
volume:
|
|
1269
|
+
openInterest: '0',
|
|
1270
|
+
volume: '0',
|
|
1072
1271
|
sumNetFunding: 0,
|
|
1073
1272
|
maxLeverage: 0,
|
|
1074
1273
|
minMargin: 0,
|
|
1075
1274
|
leverageMatched: true,
|
|
1076
|
-
recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens }) => {
|
|
1077
|
-
const isPriceDataReady = !!(perpMetaAssets &&
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
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)
|
|
1083
1305
|
: {};
|
|
1084
|
-
const
|
|
1085
|
-
? TokenMetadataExtractor.extractMultipleTokensMetadata(
|
|
1306
|
+
const shortBaseMetadata = isPriceDataReady
|
|
1307
|
+
? TokenMetadataExtractor.extractMultipleTokensMetadata(shortTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
|
|
1086
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
|
+
});
|
|
1087
1328
|
// Determine loading state
|
|
1088
1329
|
const allTokens = [...longTokens, ...shortTokens];
|
|
1089
1330
|
const isLoading = (() => {
|
|
@@ -1091,26 +1332,33 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1091
1332
|
return true;
|
|
1092
1333
|
if (allTokens.length === 0)
|
|
1093
1334
|
return false;
|
|
1094
|
-
const allMetadata = {
|
|
1335
|
+
const allMetadata = {
|
|
1336
|
+
...longTokensMetadata,
|
|
1337
|
+
...shortTokensMetadata,
|
|
1338
|
+
};
|
|
1095
1339
|
return allTokens.some((token) => !allMetadata[token.symbol]);
|
|
1096
1340
|
})();
|
|
1097
1341
|
// Open interest and volume (from market data for matching asset basket)
|
|
1342
|
+
// Use base symbols (without prefix) for matching against market data
|
|
1098
1343
|
const { openInterest, volume } = (() => {
|
|
1099
|
-
const empty = { openInterest:
|
|
1344
|
+
const empty = { openInterest: '0', volume: '0' };
|
|
1100
1345
|
if (!(marketData === null || marketData === void 0 ? void 0 : marketData.active) || (!longTokens.length && !shortTokens.length))
|
|
1101
1346
|
return empty;
|
|
1102
|
-
const selectedLong =
|
|
1103
|
-
const selectedShort =
|
|
1347
|
+
const selectedLong = longBaseSymbols.slice().sort();
|
|
1348
|
+
const selectedShort = shortBaseSymbols.slice().sort();
|
|
1104
1349
|
const match = marketData.active.find((item) => {
|
|
1105
1350
|
const longs = [...item.longAssets].sort();
|
|
1106
1351
|
const shorts = [...item.shortAssets].sort();
|
|
1107
|
-
if (longs.length !== selectedLong.length ||
|
|
1352
|
+
if (longs.length !== selectedLong.length ||
|
|
1353
|
+
shorts.length !== selectedShort.length)
|
|
1108
1354
|
return false;
|
|
1109
1355
|
const longsEqual = longs.every((s, i) => s.asset === selectedLong[i]);
|
|
1110
1356
|
const shortsEqual = shorts.every((s, i) => s.asset === selectedShort[i]);
|
|
1111
1357
|
return longsEqual && shortsEqual;
|
|
1112
1358
|
});
|
|
1113
|
-
return match
|
|
1359
|
+
return match
|
|
1360
|
+
? { openInterest: match.openInterest, volume: match.volume }
|
|
1361
|
+
: empty;
|
|
1114
1362
|
})();
|
|
1115
1363
|
// Price ratio (only when exactly one long and one short)
|
|
1116
1364
|
const { priceRatio, priceRatio24h } = (() => {
|
|
@@ -1190,17 +1438,27 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1190
1438
|
return totalFunding;
|
|
1191
1439
|
})();
|
|
1192
1440
|
// Max leverage (minimum across all tokens)
|
|
1441
|
+
// Use tokens with their market prefixes for proper lookup in perpMetaAssets
|
|
1193
1442
|
const maxLeverage = (() => {
|
|
1194
1443
|
if (!perpMetaAssets)
|
|
1195
1444
|
return 0;
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1445
|
+
const allTokensForLookup = [
|
|
1446
|
+
...longTokensForLookup,
|
|
1447
|
+
...shortTokensForLookup,
|
|
1448
|
+
];
|
|
1449
|
+
if (allTokensForLookup.length === 0)
|
|
1198
1450
|
return 0;
|
|
1199
1451
|
let minLev = Infinity;
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
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);
|
|
1204
1462
|
});
|
|
1205
1463
|
return minLev === Infinity ? 0 : minLev;
|
|
1206
1464
|
})();
|
|
@@ -1212,7 +1470,10 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1212
1470
|
// Whether all tokens have matching leverage
|
|
1213
1471
|
const leverageMatched = (() => {
|
|
1214
1472
|
const allTokensArr = [...longTokens, ...shortTokens];
|
|
1215
|
-
const allMetadata = {
|
|
1473
|
+
const allMetadata = {
|
|
1474
|
+
...longTokensMetadata,
|
|
1475
|
+
...shortTokensMetadata,
|
|
1476
|
+
};
|
|
1216
1477
|
if (allTokensArr.length === 0)
|
|
1217
1478
|
return true;
|
|
1218
1479
|
const tokensWithLev = allTokensArr.filter((token) => { var _a; return (_a = allMetadata[token.symbol]) === null || _a === void 0 ? void 0 : _a.leverage; });
|
|
@@ -5397,8 +5658,8 @@ function addAuthInterceptors(params) {
|
|
|
5397
5658
|
/**
|
|
5398
5659
|
* Fetch historical candle data from HyperLiquid API
|
|
5399
5660
|
*/
|
|
5400
|
-
const fetchHistoricalCandles = async (coin, startTime, endTime, interval,
|
|
5401
|
-
const backendCoin = toBackendSymbol(coin,
|
|
5661
|
+
const fetchHistoricalCandles = async (coin, startTime, endTime, interval, hip3Assets) => {
|
|
5662
|
+
const backendCoin = toBackendSymbol(coin, hip3Assets);
|
|
5402
5663
|
const request = {
|
|
5403
5664
|
req: { coin: backendCoin, startTime, endTime, interval },
|
|
5404
5665
|
type: 'candleSnapshot',
|
|
@@ -5557,10 +5818,10 @@ const useHistoricalPriceData = () => {
|
|
|
5557
5818
|
setTokenLoading(token.symbol, true);
|
|
5558
5819
|
});
|
|
5559
5820
|
try {
|
|
5560
|
-
const
|
|
5821
|
+
const hip3Assets = useHyperliquidData.getState().hip3Assets;
|
|
5561
5822
|
const fetchPromises = tokensToFetch.map(async (token) => {
|
|
5562
5823
|
try {
|
|
5563
|
-
const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval,
|
|
5824
|
+
const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, hip3Assets);
|
|
5564
5825
|
addHistoricalPriceData(token.symbol, interval, response.data, { start: startTime, end: endTime });
|
|
5565
5826
|
return { symbol: token.symbol, candles: response.data, success: true };
|
|
5566
5827
|
}
|
|
@@ -6329,12 +6590,12 @@ function validatePositionSize(usdValue, longAssets, shortAssets) {
|
|
|
6329
6590
|
* Authorization is derived from headers (Axios defaults or browser localStorage fallback)
|
|
6330
6591
|
* @throws MinimumPositionSizeError if any asset has less than $11 USD value
|
|
6331
6592
|
*/
|
|
6332
|
-
async function createPosition(baseUrl, payload,
|
|
6593
|
+
async function createPosition(baseUrl, payload, hip3Assets) {
|
|
6333
6594
|
// Validate minimum asset size before creating position
|
|
6334
6595
|
validateMinimumAssetSize(payload.usdValue, payload.longAssets, payload.shortAssets);
|
|
6335
6596
|
const url = joinUrl(baseUrl, "/positions");
|
|
6336
6597
|
// Translate display symbols to backend format
|
|
6337
|
-
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset,
|
|
6598
|
+
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6338
6599
|
const translatedPayload = {
|
|
6339
6600
|
...payload,
|
|
6340
6601
|
longAssets: mapAssets(payload.longAssets),
|
|
@@ -6433,9 +6694,9 @@ async function adjustPosition(baseUrl, positionId, payload) {
|
|
|
6433
6694
|
throw toApiError(error);
|
|
6434
6695
|
}
|
|
6435
6696
|
}
|
|
6436
|
-
async function adjustAdvancePosition(baseUrl, positionId, payload,
|
|
6697
|
+
async function adjustAdvancePosition(baseUrl, positionId, payload, hip3Assets) {
|
|
6437
6698
|
const url = joinUrl(baseUrl, `/positions/${positionId}/adjust-advance`);
|
|
6438
|
-
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset,
|
|
6699
|
+
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6439
6700
|
const translatedPayload = (payload || []).map((item) => ({
|
|
6440
6701
|
longAssets: mapAssets(item.longAssets),
|
|
6441
6702
|
shortAssets: mapAssets(item.shortAssets),
|
|
@@ -6560,16 +6821,62 @@ const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
|
|
|
6560
6821
|
});
|
|
6561
6822
|
};
|
|
6562
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
|
+
}
|
|
6563
6870
|
function usePosition() {
|
|
6564
6871
|
const context = useContext(PearHyperliquidContext);
|
|
6565
6872
|
if (!context) {
|
|
6566
6873
|
throw new Error('usePosition must be used within a PearHyperliquidProvider');
|
|
6567
6874
|
}
|
|
6568
6875
|
const { apiBaseUrl, isConnected } = context;
|
|
6569
|
-
const
|
|
6876
|
+
const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
|
|
6570
6877
|
// Create position API action
|
|
6571
6878
|
const createPosition$1 = async (payload) => {
|
|
6572
|
-
return createPosition(apiBaseUrl, payload,
|
|
6879
|
+
return createPosition(apiBaseUrl, payload, hip3Assets);
|
|
6573
6880
|
};
|
|
6574
6881
|
// Update TP/SL risk parameters for a position
|
|
6575
6882
|
const updateRiskParameters$1 = async (positionId, payload) => {
|
|
@@ -6589,21 +6896,33 @@ function usePosition() {
|
|
|
6589
6896
|
};
|
|
6590
6897
|
// Adjust to absolute target sizes per asset, optionally adding new assets
|
|
6591
6898
|
const adjustAdvancePosition$1 = async (positionId, payload) => {
|
|
6592
|
-
return adjustAdvancePosition(apiBaseUrl, positionId, payload,
|
|
6899
|
+
return adjustAdvancePosition(apiBaseUrl, positionId, payload, hip3Assets);
|
|
6593
6900
|
};
|
|
6594
6901
|
// Open positions using WS data, with derived values
|
|
6595
6902
|
const userOpenPositions = useUserData((state) => state.rawOpenPositions);
|
|
6596
6903
|
const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
6597
6904
|
const allMids = useHyperliquidData((state) => state.allMids);
|
|
6905
|
+
const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
6598
6906
|
const isLoading = useMemo(() => {
|
|
6599
6907
|
return userOpenPositions === null && isConnected;
|
|
6600
6908
|
}, [userOpenPositions, isConnected]);
|
|
6601
6909
|
const openPositions = useMemo(() => {
|
|
6602
6910
|
if (!userOpenPositions || !aggregatedClearingHouseState || !allMids)
|
|
6603
6911
|
return null;
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
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
|
+
};
|
|
6607
6926
|
}
|
|
6608
6927
|
|
|
6609
6928
|
async function adjustOrder(baseUrl, orderId, payload) {
|
|
@@ -6775,59 +7094,170 @@ function useNotifications() {
|
|
|
6775
7094
|
};
|
|
6776
7095
|
}
|
|
6777
7096
|
|
|
6778
|
-
//
|
|
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)
|
|
6779
7178
|
const useMarketDataPayload = () => {
|
|
6780
7179
|
return useMarketData((s) => s.marketData);
|
|
6781
7180
|
};
|
|
6782
|
-
// Full payload for 'market-data-all' channel
|
|
7181
|
+
// Full payload for 'market-data-all' channel (raw from WS)
|
|
6783
7182
|
const useMarketDataAllPayload = () => {
|
|
6784
7183
|
return useMarketData((s) => s.marketDataAll);
|
|
6785
7184
|
};
|
|
6786
|
-
//
|
|
6787
|
-
const
|
|
6788
|
-
|
|
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) => {
|
|
6789
7191
|
const data = useMarketDataPayload();
|
|
6790
|
-
|
|
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]);
|
|
6791
7199
|
};
|
|
6792
|
-
// Top gainers (
|
|
6793
|
-
const useTopGainers = (limit) => {
|
|
7200
|
+
// Top gainers (with collateral and market prefix info)
|
|
7201
|
+
const useTopGainers = (limit, collateralFilter) => {
|
|
6794
7202
|
const data = useMarketDataPayload();
|
|
7203
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6795
7204
|
return useMemo(() => {
|
|
6796
7205
|
var _a;
|
|
6797
7206
|
const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
|
|
6798
|
-
|
|
6799
|
-
|
|
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]);
|
|
6800
7211
|
};
|
|
6801
|
-
// Top losers (
|
|
6802
|
-
const useTopLosers = (limit) => {
|
|
7212
|
+
// Top losers (with collateral and market prefix info)
|
|
7213
|
+
const useTopLosers = (limit, collateralFilter) => {
|
|
6803
7214
|
const data = useMarketDataPayload();
|
|
7215
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6804
7216
|
return useMemo(() => {
|
|
6805
7217
|
var _a;
|
|
6806
7218
|
const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
|
|
6807
|
-
|
|
6808
|
-
|
|
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]);
|
|
6809
7223
|
};
|
|
6810
|
-
// Highlighted baskets
|
|
6811
|
-
const useHighlightedBaskets = () => {
|
|
6812
|
-
var _a;
|
|
7224
|
+
// Highlighted baskets (with collateral and market prefix info)
|
|
7225
|
+
const useHighlightedBaskets = (collateralFilter) => {
|
|
6813
7226
|
const data = useMarketDataPayload();
|
|
6814
|
-
|
|
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]);
|
|
6815
7234
|
};
|
|
6816
|
-
// Watchlist baskets (
|
|
6817
|
-
const useWatchlistBaskets = () => {
|
|
6818
|
-
var _a;
|
|
7235
|
+
// Watchlist baskets (with collateral and market prefix info)
|
|
7236
|
+
const useWatchlistBaskets = (collateralFilter) => {
|
|
6819
7237
|
const data = useMarketDataPayload();
|
|
6820
|
-
|
|
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]);
|
|
6821
7245
|
};
|
|
6822
|
-
// All baskets (
|
|
6823
|
-
const useAllBaskets = () => {
|
|
6824
|
-
var _a;
|
|
7246
|
+
// All baskets (with collateral and market prefix info)
|
|
7247
|
+
const useAllBaskets = (collateralFilter) => {
|
|
6825
7248
|
const dataAll = useMarketDataAllPayload();
|
|
6826
|
-
|
|
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]);
|
|
6827
7256
|
};
|
|
6828
7257
|
// Find a basket by its exact asset composition (order-insensitive)
|
|
6829
7258
|
const useFindBasket = (longs, shorts) => {
|
|
6830
7259
|
const data = useMarketDataPayload();
|
|
7260
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6831
7261
|
return useMemo(() => {
|
|
6832
7262
|
if (!data)
|
|
6833
7263
|
return undefined;
|
|
@@ -6841,17 +7271,28 @@ const useFindBasket = (longs, shorts) => {
|
|
|
6841
7271
|
: '';
|
|
6842
7272
|
const lKey = normalize(longs);
|
|
6843
7273
|
const sKey = normalize(shorts);
|
|
6844
|
-
const match = (item) => normalize(item.longAssets) === lKey &&
|
|
6845
|
-
|
|
6846
|
-
|
|
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]);
|
|
6847
7281
|
};
|
|
6848
7282
|
|
|
6849
|
-
async function toggleWatchlist(baseUrl, longAssets, shortAssets,
|
|
7283
|
+
async function toggleWatchlist(baseUrl, longAssets, shortAssets, hip3Assets) {
|
|
6850
7284
|
const url = joinUrl(baseUrl, '/watchlist');
|
|
6851
|
-
const mapAssets = (arr) => arr.map(a => ({ ...a, asset: toBackendSymbol(a.asset,
|
|
7285
|
+
const mapAssets = (arr) => arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6852
7286
|
try {
|
|
6853
|
-
const response = await apiClient.post(url, {
|
|
6854
|
-
|
|
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
|
+
};
|
|
6855
7296
|
}
|
|
6856
7297
|
catch (error) {
|
|
6857
7298
|
throw toApiError(error);
|
|
@@ -6863,11 +7304,11 @@ function useWatchlist() {
|
|
|
6863
7304
|
if (!context)
|
|
6864
7305
|
throw new Error('useWatchlist must be used within a PearHyperliquidProvider');
|
|
6865
7306
|
const { apiBaseUrl, isConnected } = context;
|
|
6866
|
-
const
|
|
7307
|
+
const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
|
|
6867
7308
|
const marketData = useMarketDataPayload();
|
|
6868
7309
|
const isLoading = useMemo(() => !marketData && isConnected, [marketData, isConnected]);
|
|
6869
7310
|
const toggle = async (longAssets, shortAssets) => {
|
|
6870
|
-
const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets,
|
|
7311
|
+
const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, hip3Assets);
|
|
6871
7312
|
// Server will push updated market-data over WS; nothing to set here
|
|
6872
7313
|
return resp;
|
|
6873
7314
|
};
|
|
@@ -7121,7 +7562,8 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-v2.pearpro
|
|
|
7121
7562
|
const setAddress = useUserData((s) => s.setAddress);
|
|
7122
7563
|
const perpsMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
7123
7564
|
const setPerpMetaAssets = useHyperliquidData((state) => state.setPerpMetaAssets);
|
|
7124
|
-
const
|
|
7565
|
+
const setHip3Assets = useHyperliquidData((state) => state.setHip3Assets);
|
|
7566
|
+
const setHip3MarketPrefixes = useHyperliquidData((state) => state.setHip3MarketPrefixes);
|
|
7125
7567
|
const websocketsEnabled = useMemo(() => Array.isArray(perpsMetaAssets) && perpsMetaAssets.length > 0, [perpsMetaAssets]);
|
|
7126
7568
|
const { isConnected, lastError } = useHyperliquidWebSocket({
|
|
7127
7569
|
wsUrl,
|
|
@@ -7134,29 +7576,64 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-v2.pearpro
|
|
|
7134
7576
|
});
|
|
7135
7577
|
useEffect(() => {
|
|
7136
7578
|
if (perpsMetaAssets === null) {
|
|
7137
|
-
fetchAllPerpMetas()
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
const
|
|
7141
|
-
const
|
|
7142
|
-
const cleanedPerpMetas =
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7579
|
+
fetchAllPerpMetas()
|
|
7580
|
+
.then((res) => {
|
|
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
|
+
});
|
|
7154
7624
|
});
|
|
7155
|
-
|
|
7625
|
+
setHip3Assets(assetToMarkets);
|
|
7626
|
+
setHip3MarketPrefixes(marketPrefixes);
|
|
7156
7627
|
setPerpMetaAssets(cleanedPerpMetas);
|
|
7157
|
-
})
|
|
7628
|
+
})
|
|
7629
|
+
.catch(() => { });
|
|
7158
7630
|
}
|
|
7159
|
-
}, [
|
|
7631
|
+
}, [
|
|
7632
|
+
perpsMetaAssets,
|
|
7633
|
+
setPerpMetaAssets,
|
|
7634
|
+
setHip3Assets,
|
|
7635
|
+
setHip3MarketPrefixes,
|
|
7636
|
+
]);
|
|
7160
7637
|
// Auth methods now sourced from useAuth hook
|
|
7161
7638
|
useAutoSyncFills({
|
|
7162
7639
|
baseUrl: apiBaseUrl,
|
|
@@ -7177,10 +7654,14 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-v2.pearpro
|
|
|
7177
7654
|
nativeIsConnected,
|
|
7178
7655
|
nativeLastError,
|
|
7179
7656
|
}), [
|
|
7180
|
-
apiBaseUrl,
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7657
|
+
apiBaseUrl,
|
|
7658
|
+
wsUrl,
|
|
7659
|
+
address,
|
|
7660
|
+
setAddress,
|
|
7661
|
+
isConnected,
|
|
7662
|
+
lastError,
|
|
7663
|
+
nativeIsConnected,
|
|
7664
|
+
nativeLastError,
|
|
7184
7665
|
]);
|
|
7185
7666
|
return (jsx(PearHyperliquidContext.Provider, { value: contextValue, children: children }));
|
|
7186
7667
|
};
|
|
@@ -7282,4 +7763,4 @@ function mapCandleIntervalToTradingViewInterval(interval) {
|
|
|
7282
7763
|
}
|
|
7283
7764
|
}
|
|
7284
7765
|
|
|
7285
|
-
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 };
|