@pear-protocol/hyperliquid-sdk 0.0.60-beta → 0.0.60-beta-usdh-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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/useAuth.d.ts +4 -4
- package/dist/hooks/useMarketData.d.ts +9 -7
- package/dist/hooks/useWebData.d.ts +21 -3
- package/dist/index.d.ts +126 -45
- package/dist/index.js +709 -260
- package/dist/provider.d.ts +1 -1
- package/dist/store/historicalPriceDataStore.d.ts +1 -12
- package/dist/store/hyperliquidDataStore.d.ts +1 -25
- package/dist/store/marketDataStore.d.ts +1 -10
- package/dist/store/tokenSelectionMetadataStore.d.ts +2 -2
- package/dist/store/userDataStore.d.ts +1 -28
- package/dist/store/userSelection.d.ts +1 -1
- package/dist/types.d.ts +38 -3
- package/dist/utils/symbol-translator.d.ts +32 -3
- package/dist/utils/token-metadata-extractor.d.ts +9 -5
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import
|
|
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 !==
|
|
30
|
+
if (typeof window !== 'undefined') {
|
|
71
31
|
if (address) {
|
|
72
|
-
window.localStorage.setItem(
|
|
32
|
+
window.localStorage.setItem('address', address);
|
|
73
33
|
}
|
|
74
34
|
else {
|
|
75
|
-
window.localStorage.removeItem(
|
|
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
|
-
*
|
|
121
|
-
* @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"])
|
|
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
|
|
122
96
|
*/
|
|
123
|
-
function
|
|
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) {
|
|
124
120
|
var _a;
|
|
125
|
-
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(':');
|
|
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
|
-
|
|
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
|
-
|
|
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(() =>
|
|
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) =>
|
|
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
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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 = {
|
|
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
|
-
}, [
|
|
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
|
-
}, [
|
|
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
|
-
}, [
|
|
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 ||
|
|
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
|
-
}, [
|
|
929
|
+
}, [
|
|
930
|
+
isConnected,
|
|
931
|
+
selectedTokenSymbols,
|
|
932
|
+
candleInterval,
|
|
933
|
+
subscribedCandleTokens,
|
|
934
|
+
sendJsonMessage,
|
|
935
|
+
setCandleData,
|
|
936
|
+
]);
|
|
868
937
|
return {
|
|
869
938
|
isConnected,
|
|
870
939
|
lastError,
|
|
@@ -944,16 +1013,69 @@ const useAccountSummary = () => {
|
|
|
944
1013
|
return { data: calculated, isLoading };
|
|
945
1014
|
};
|
|
946
1015
|
|
|
1016
|
+
// Helper to find asset metadata from perpMetaAssets
|
|
1017
|
+
function findAssetMeta$2(coinName, perpMetaAssets) {
|
|
1018
|
+
var _a, _b, _c, _d;
|
|
1019
|
+
if (!perpMetaAssets) {
|
|
1020
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
1021
|
+
}
|
|
1022
|
+
// Try exact match first (for prefixed assets like "xyz:TSLA")
|
|
1023
|
+
const exactMatch = perpMetaAssets.find((a) => a.name === coinName);
|
|
1024
|
+
if (exactMatch) {
|
|
1025
|
+
return {
|
|
1026
|
+
collateralToken: (_a = exactMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
1027
|
+
marketPrefix: (_b = exactMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
// Try matching by base symbol (for non-prefixed names in data)
|
|
1031
|
+
const baseMatch = perpMetaAssets.find((a) => {
|
|
1032
|
+
const baseName = a.name.includes(':') ? a.name.split(':')[1] : a.name;
|
|
1033
|
+
return baseName === coinName;
|
|
1034
|
+
});
|
|
1035
|
+
if (baseMatch) {
|
|
1036
|
+
return {
|
|
1037
|
+
collateralToken: (_c = baseMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
1038
|
+
marketPrefix: (_d = baseMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
1042
|
+
}
|
|
1043
|
+
// Enrich trade history assets with market prefix and collateral token
|
|
1044
|
+
function enrichTradeHistoryAssets(assets, perpMetaAssets) {
|
|
1045
|
+
return assets.map((asset) => {
|
|
1046
|
+
const meta = findAssetMeta$2(asset.coin, perpMetaAssets);
|
|
1047
|
+
return {
|
|
1048
|
+
...asset,
|
|
1049
|
+
marketPrefix: meta.marketPrefix,
|
|
1050
|
+
collateralToken: meta.collateralToken,
|
|
1051
|
+
};
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
// Enrich all trade histories with market metadata
|
|
1055
|
+
function enrichTradeHistories(histories, perpMetaAssets) {
|
|
1056
|
+
return histories.map((history) => ({
|
|
1057
|
+
...history,
|
|
1058
|
+
closedLongAssets: enrichTradeHistoryAssets(history.closedLongAssets, perpMetaAssets),
|
|
1059
|
+
closedShortAssets: enrichTradeHistoryAssets(history.closedShortAssets, perpMetaAssets),
|
|
1060
|
+
}));
|
|
1061
|
+
}
|
|
947
1062
|
const useTradeHistories = () => {
|
|
948
1063
|
const context = useContext(PearHyperliquidContext);
|
|
949
1064
|
if (!context) {
|
|
950
1065
|
throw new Error('useTradeHistories must be used within a PearHyperliquidProvider');
|
|
951
1066
|
}
|
|
952
1067
|
const tradeHistories = useUserData((state) => state.tradeHistories);
|
|
1068
|
+
const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
953
1069
|
const isLoading = useMemo(() => {
|
|
954
1070
|
return tradeHistories === null && context.isConnected;
|
|
955
1071
|
}, [tradeHistories, context.isConnected]);
|
|
956
|
-
|
|
1072
|
+
// Enrich trade histories with market prefix and collateral token
|
|
1073
|
+
const enrichedTradeHistories = useMemo(() => {
|
|
1074
|
+
if (!tradeHistories)
|
|
1075
|
+
return null;
|
|
1076
|
+
return enrichTradeHistories(tradeHistories, perpMetaAssets);
|
|
1077
|
+
}, [tradeHistories, perpMetaAssets]);
|
|
1078
|
+
return { data: enrichedTradeHistories, isLoading };
|
|
957
1079
|
};
|
|
958
1080
|
/**
|
|
959
1081
|
* Hook to access open orders with loading state
|
|
@@ -986,21 +1108,51 @@ const useWebData = () => {
|
|
|
986
1108
|
const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
987
1109
|
const aggregatedClearinghouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
988
1110
|
const finalAtOICaps = useHyperliquidData((state) => state.finalAtOICaps);
|
|
989
|
-
const hip3Assets = useHyperliquidData((state) => state.
|
|
1111
|
+
const hip3Assets = useHyperliquidData((state) => state.hip3Assets);
|
|
1112
|
+
const hip3MarketPrefixes = useHyperliquidData((state) => state.hip3MarketPrefixes);
|
|
990
1113
|
let marketDataBySymbol = {};
|
|
991
1114
|
if (finalAssetContexts && perpMetaAssets) {
|
|
992
1115
|
const result = {};
|
|
1116
|
+
// Build a map of display name -> asset context index (for unique display names)
|
|
1117
|
+
const displayNameToContextIndex = new Map();
|
|
1118
|
+
const seenNames = new Set();
|
|
1119
|
+
let contextIndex = 0;
|
|
1120
|
+
// First pass: map unique display names to their context index
|
|
993
1121
|
for (let index = 0; index < perpMetaAssets.length; index++) {
|
|
994
1122
|
const name = perpMetaAssets[index].name;
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1123
|
+
if (!seenNames.has(name)) {
|
|
1124
|
+
seenNames.add(name);
|
|
1125
|
+
if (contextIndex < finalAssetContexts.length) {
|
|
1126
|
+
displayNameToContextIndex.set(name, contextIndex);
|
|
1127
|
+
contextIndex++;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
// Second pass: create nested entries for all market variants
|
|
1132
|
+
for (let index = 0; index < perpMetaAssets.length; index++) {
|
|
1133
|
+
const universeAsset = perpMetaAssets[index];
|
|
1134
|
+
const displayName = universeAsset.name;
|
|
1135
|
+
const marketPrefix = universeAsset.marketPrefix;
|
|
1136
|
+
const ctxIndex = displayNameToContextIndex.get(displayName);
|
|
1137
|
+
if (ctxIndex !== undefined) {
|
|
1138
|
+
const assetContext = finalAssetContexts[ctxIndex];
|
|
1139
|
+
// Initialize the symbol entry if it doesn't exist
|
|
1140
|
+
if (!result[displayName]) {
|
|
1141
|
+
result[displayName] = {};
|
|
1142
|
+
}
|
|
1143
|
+
// Use marketPrefix as key for HIP-3 assets, "default" for regular assets
|
|
1144
|
+
const variantKey = marketPrefix || 'default';
|
|
1145
|
+
result[displayName][variantKey] = {
|
|
1146
|
+
asset: assetContext,
|
|
1147
|
+
universe: universeAsset,
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
999
1150
|
}
|
|
1000
1151
|
marketDataBySymbol = result;
|
|
1001
1152
|
}
|
|
1002
1153
|
return {
|
|
1003
1154
|
hip3Assets,
|
|
1155
|
+
hip3MarketPrefixes,
|
|
1004
1156
|
clearinghouseState: aggregatedClearinghouseState,
|
|
1005
1157
|
perpsAtOpenInterestCap: finalAtOICaps,
|
|
1006
1158
|
marketDataBySymbol,
|
|
@@ -1015,30 +1167,60 @@ const useWebData = () => {
|
|
|
1015
1167
|
class TokenMetadataExtractor {
|
|
1016
1168
|
/**
|
|
1017
1169
|
* Extracts comprehensive token metadata
|
|
1018
|
-
* @param symbol - Token symbol
|
|
1170
|
+
* @param symbol - Token symbol (base symbol without prefix, e.g., "TSLA")
|
|
1019
1171
|
* @param perpMetaAssets - Aggregated universe assets (flattened across dexes)
|
|
1020
1172
|
* @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
|
|
1021
1173
|
* @param allMids - AllMids data containing current prices
|
|
1022
1174
|
* @param activeAssetData - Optional active asset data containing leverage information
|
|
1175
|
+
* @param marketPrefix - Optional market prefix (e.g., "xyz", "flx") for HIP3 multi-market assets
|
|
1023
1176
|
* @returns TokenMetadata or null if token not found
|
|
1024
1177
|
*/
|
|
1025
|
-
static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
|
|
1178
|
+
static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketPrefix) {
|
|
1026
1179
|
if (!perpMetaAssets || !finalAssetContexts || !allMids) {
|
|
1027
1180
|
return null;
|
|
1028
1181
|
}
|
|
1029
1182
|
// Find token index in aggregated universe
|
|
1030
|
-
|
|
1031
|
-
|
|
1183
|
+
// For HIP3 assets, match both name AND marketPrefix
|
|
1184
|
+
const universeIndex = perpMetaAssets.findIndex((asset) => {
|
|
1185
|
+
if (asset.name !== symbol)
|
|
1186
|
+
return false;
|
|
1187
|
+
// If marketPrefix is specified, match it; otherwise match assets without prefix
|
|
1188
|
+
if (marketPrefix) {
|
|
1189
|
+
return asset.marketPrefix === marketPrefix;
|
|
1190
|
+
}
|
|
1191
|
+
// No prefix specified - match non-HIP3 asset (no marketPrefix) or first matching asset
|
|
1192
|
+
return !asset.marketPrefix;
|
|
1193
|
+
});
|
|
1194
|
+
// If no exact match found and prefix was specified, try finding the specific market variant
|
|
1195
|
+
const finalIndex = universeIndex === -1 && marketPrefix
|
|
1196
|
+
? perpMetaAssets.findIndex((asset) => asset.name === symbol && asset.marketPrefix === marketPrefix)
|
|
1197
|
+
: universeIndex;
|
|
1198
|
+
// Fallback: if still not found and no prefix, find first matching by name (for backward compatibility)
|
|
1199
|
+
const resolvedIndex = finalIndex === -1
|
|
1200
|
+
? perpMetaAssets.findIndex((asset) => asset.name === symbol)
|
|
1201
|
+
: finalIndex;
|
|
1202
|
+
if (resolvedIndex === -1) {
|
|
1032
1203
|
return null;
|
|
1033
1204
|
}
|
|
1034
|
-
const universeAsset = perpMetaAssets[
|
|
1035
|
-
const assetCtx = finalAssetContexts[
|
|
1205
|
+
const universeAsset = perpMetaAssets[resolvedIndex];
|
|
1206
|
+
const assetCtx = finalAssetContexts[resolvedIndex];
|
|
1036
1207
|
if (!assetCtx) {
|
|
1037
1208
|
return null;
|
|
1038
1209
|
}
|
|
1039
|
-
// Get current price
|
|
1040
|
-
|
|
1041
|
-
const
|
|
1210
|
+
// Get current price - prefer assetCtx.midPx as it's already index-matched,
|
|
1211
|
+
// fall back to allMids lookup if midPx is null
|
|
1212
|
+
const prefixedKeyColon = marketPrefix ? `${marketPrefix}:${symbol}` : null;
|
|
1213
|
+
let currentPrice = 0;
|
|
1214
|
+
// Primary source: assetCtx.midPx (already properly indexed)
|
|
1215
|
+
if (assetCtx.midPx) {
|
|
1216
|
+
currentPrice = parseFloat(assetCtx.midPx);
|
|
1217
|
+
}
|
|
1218
|
+
// Fallback: allMids lookup with multiple key formats for HIP3 markets
|
|
1219
|
+
if (!currentPrice || isNaN(currentPrice)) {
|
|
1220
|
+
const currentPriceStr = (prefixedKeyColon && allMids.mids[prefixedKeyColon]) ||
|
|
1221
|
+
allMids.mids[symbol];
|
|
1222
|
+
currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
|
|
1223
|
+
}
|
|
1042
1224
|
// Get previous day price
|
|
1043
1225
|
const prevDayPrice = parseFloat(assetCtx.prevDayPx);
|
|
1044
1226
|
// Calculate 24h price change
|
|
@@ -1049,7 +1231,11 @@ class TokenMetadataExtractor {
|
|
|
1049
1231
|
const markPrice = parseFloat(assetCtx.markPx);
|
|
1050
1232
|
const oraclePrice = parseFloat(assetCtx.oraclePx);
|
|
1051
1233
|
// Extract leverage info from activeAssetData if available
|
|
1052
|
-
|
|
1234
|
+
// Try prefixed key first (e.g., "xyz:TSLA"), then fall back to plain symbol
|
|
1235
|
+
const activeDataKey = prefixedKeyColon && (activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[prefixedKeyColon])
|
|
1236
|
+
? prefixedKeyColon
|
|
1237
|
+
: symbol;
|
|
1238
|
+
const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[activeDataKey];
|
|
1053
1239
|
const leverage = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.leverage;
|
|
1054
1240
|
const maxTradeSzs = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.maxTradeSzs;
|
|
1055
1241
|
const availableToTrade = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.availableToTrade;
|
|
@@ -1067,21 +1253,27 @@ class TokenMetadataExtractor {
|
|
|
1067
1253
|
leverage,
|
|
1068
1254
|
maxTradeSzs,
|
|
1069
1255
|
availableToTrade,
|
|
1256
|
+
collateralToken: universeAsset.collateralToken,
|
|
1070
1257
|
};
|
|
1071
1258
|
}
|
|
1072
1259
|
/**
|
|
1073
1260
|
* Extracts metadata for multiple tokens
|
|
1074
|
-
* @param
|
|
1261
|
+
* @param tokens - Array of token objects with symbol and optional marketPrefix
|
|
1075
1262
|
* @param perpMetaAssets - Aggregated universe assets
|
|
1076
1263
|
* @param finalAssetContexts - Aggregated asset contexts
|
|
1077
1264
|
* @param allMids - AllMids data
|
|
1078
1265
|
* @param activeAssetData - Optional active asset data containing leverage information
|
|
1079
|
-
* @returns Record of
|
|
1266
|
+
* @returns Record of unique key to TokenMetadata. Key is "{prefix}:{symbol}" for HIP3 assets, or just "{symbol}" otherwise
|
|
1080
1267
|
*/
|
|
1081
|
-
static extractMultipleTokensMetadata(
|
|
1268
|
+
static extractMultipleTokensMetadata(tokens, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
|
|
1082
1269
|
const result = {};
|
|
1083
|
-
for (const
|
|
1084
|
-
|
|
1270
|
+
for (const token of tokens) {
|
|
1271
|
+
// Use a unique key that includes the prefix for HIP3 assets
|
|
1272
|
+
// This ensures xyz:TSLA and flx:TSLA get separate entries
|
|
1273
|
+
const resultKey = token.marketPrefix
|
|
1274
|
+
? `${token.marketPrefix}:${token.symbol}`
|
|
1275
|
+
: token.symbol;
|
|
1276
|
+
result[resultKey] = this.extractTokenMetadata(token.symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, token.marketPrefix);
|
|
1085
1277
|
}
|
|
1086
1278
|
return result;
|
|
1087
1279
|
}
|
|
@@ -1094,10 +1286,30 @@ class TokenMetadataExtractor {
|
|
|
1094
1286
|
static isTokenAvailable(symbol, perpMetaAssets) {
|
|
1095
1287
|
if (!perpMetaAssets)
|
|
1096
1288
|
return false;
|
|
1097
|
-
return perpMetaAssets.some(asset => asset.name === symbol);
|
|
1289
|
+
return perpMetaAssets.some((asset) => asset.name === symbol);
|
|
1098
1290
|
}
|
|
1099
1291
|
}
|
|
1100
1292
|
|
|
1293
|
+
/**
|
|
1294
|
+
* Parse a token string that may have a market prefix (e.g., "xyz:GOOGL" -> { prefix: "xyz", symbol: "GOOGL" })
|
|
1295
|
+
* This allows us to keep the full name (xyz:GOOGL) for URLs/tags while extracting just the symbol for SDK lookups.
|
|
1296
|
+
*/
|
|
1297
|
+
function parseTokenWithPrefix(token) {
|
|
1298
|
+
if (token.includes(':')) {
|
|
1299
|
+
const [prefix, ...rest] = token.split(':');
|
|
1300
|
+
const symbol = rest.join(':').toUpperCase();
|
|
1301
|
+
return {
|
|
1302
|
+
prefix: prefix.toLowerCase(),
|
|
1303
|
+
symbol,
|
|
1304
|
+
fullName: `${prefix.toLowerCase()}:${symbol}`,
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
return {
|
|
1308
|
+
prefix: null,
|
|
1309
|
+
symbol: token.toUpperCase(),
|
|
1310
|
+
fullName: token.toUpperCase(),
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1101
1313
|
const useTokenSelectionMetadataStore = create((set) => ({
|
|
1102
1314
|
isPriceDataReady: false,
|
|
1103
1315
|
isLoading: true,
|
|
@@ -1107,23 +1319,65 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1107
1319
|
weightedRatio24h: 1,
|
|
1108
1320
|
priceRatio: 1,
|
|
1109
1321
|
priceRatio24h: 1,
|
|
1110
|
-
openInterest:
|
|
1111
|
-
volume:
|
|
1322
|
+
openInterest: '0',
|
|
1323
|
+
volume: '0',
|
|
1112
1324
|
sumNetFunding: 0,
|
|
1113
1325
|
maxLeverage: 0,
|
|
1114
1326
|
minMargin: 0,
|
|
1115
1327
|
leverageMatched: true,
|
|
1116
|
-
recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens }) => {
|
|
1117
|
-
const isPriceDataReady = !!(perpMetaAssets &&
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1328
|
+
recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens, }) => {
|
|
1329
|
+
const isPriceDataReady = !!(perpMetaAssets &&
|
|
1330
|
+
finalAssetContexts &&
|
|
1331
|
+
allMids);
|
|
1332
|
+
// Parse tokens - handle prefixed tokens like "xyz:GOOGL" by extracting the symbol and market prefix
|
|
1333
|
+
// The full name (xyz:GOOGL) is kept as the metadata key for UI consistency
|
|
1334
|
+
const parsedLongTokens = longTokens.map((t) => ({
|
|
1335
|
+
...t,
|
|
1336
|
+
parsed: parseTokenWithPrefix(t.symbol),
|
|
1337
|
+
}));
|
|
1338
|
+
const parsedShortTokens = shortTokens.map((t) => ({
|
|
1339
|
+
...t,
|
|
1340
|
+
parsed: parseTokenWithPrefix(t.symbol),
|
|
1341
|
+
}));
|
|
1342
|
+
// Extract base symbols with their market prefixes for SDK lookups
|
|
1343
|
+
// This ensures xyz:TSLA and flx:TSLA get different market data
|
|
1344
|
+
const longTokensForLookup = parsedLongTokens.map((t) => ({
|
|
1345
|
+
symbol: t.parsed.symbol,
|
|
1346
|
+
marketPrefix: t.parsed.prefix,
|
|
1347
|
+
}));
|
|
1348
|
+
const shortTokensForLookup = parsedShortTokens.map((t) => ({
|
|
1349
|
+
symbol: t.parsed.symbol,
|
|
1350
|
+
marketPrefix: t.parsed.prefix,
|
|
1351
|
+
}));
|
|
1352
|
+
// Also extract just the base symbols (without prefix) for lookups that don't support prefixes
|
|
1353
|
+
const longBaseSymbols = longTokensForLookup.map((t) => t.symbol);
|
|
1354
|
+
const shortBaseSymbols = shortTokensForLookup.map((t) => t.symbol);
|
|
1355
|
+
// Get metadata using base symbols with market prefix for proper market differentiation
|
|
1356
|
+
const longBaseMetadata = isPriceDataReady
|
|
1357
|
+
? TokenMetadataExtractor.extractMultipleTokensMetadata(longTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
|
|
1123
1358
|
: {};
|
|
1124
|
-
const
|
|
1125
|
-
? TokenMetadataExtractor.extractMultipleTokensMetadata(
|
|
1359
|
+
const shortBaseMetadata = isPriceDataReady
|
|
1360
|
+
? TokenMetadataExtractor.extractMultipleTokensMetadata(shortTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
|
|
1126
1361
|
: {};
|
|
1362
|
+
// Re-map metadata using original full names (with prefix) as keys for UI consistency
|
|
1363
|
+
// The extractor now keys by "{prefix}:{symbol}" for prefixed tokens, which matches our parsed.fullName
|
|
1364
|
+
const longTokensMetadata = {};
|
|
1365
|
+
parsedLongTokens.forEach((t) => {
|
|
1366
|
+
var _a;
|
|
1367
|
+
// Use the full name (e.g., "xyz:TSLA") as the lookup key since extractor uses the same format
|
|
1368
|
+
const lookupKey = t.parsed.prefix
|
|
1369
|
+
? `${t.parsed.prefix}:${t.parsed.symbol}`
|
|
1370
|
+
: t.parsed.symbol;
|
|
1371
|
+
longTokensMetadata[t.symbol] = (_a = longBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
|
|
1372
|
+
});
|
|
1373
|
+
const shortTokensMetadata = {};
|
|
1374
|
+
parsedShortTokens.forEach((t) => {
|
|
1375
|
+
var _a;
|
|
1376
|
+
const lookupKey = t.parsed.prefix
|
|
1377
|
+
? `${t.parsed.prefix}:${t.parsed.symbol}`
|
|
1378
|
+
: t.parsed.symbol;
|
|
1379
|
+
shortTokensMetadata[t.symbol] = (_a = shortBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
|
|
1380
|
+
});
|
|
1127
1381
|
// Determine loading state
|
|
1128
1382
|
const allTokens = [...longTokens, ...shortTokens];
|
|
1129
1383
|
const isLoading = (() => {
|
|
@@ -1131,26 +1385,33 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1131
1385
|
return true;
|
|
1132
1386
|
if (allTokens.length === 0)
|
|
1133
1387
|
return false;
|
|
1134
|
-
const allMetadata = {
|
|
1388
|
+
const allMetadata = {
|
|
1389
|
+
...longTokensMetadata,
|
|
1390
|
+
...shortTokensMetadata,
|
|
1391
|
+
};
|
|
1135
1392
|
return allTokens.some((token) => !allMetadata[token.symbol]);
|
|
1136
1393
|
})();
|
|
1137
1394
|
// Open interest and volume (from market data for matching asset basket)
|
|
1395
|
+
// Use base symbols (without prefix) for matching against market data
|
|
1138
1396
|
const { openInterest, volume } = (() => {
|
|
1139
|
-
const empty = { openInterest:
|
|
1397
|
+
const empty = { openInterest: '0', volume: '0' };
|
|
1140
1398
|
if (!(marketData === null || marketData === void 0 ? void 0 : marketData.active) || (!longTokens.length && !shortTokens.length))
|
|
1141
1399
|
return empty;
|
|
1142
|
-
const selectedLong =
|
|
1143
|
-
const selectedShort =
|
|
1400
|
+
const selectedLong = longBaseSymbols.slice().sort();
|
|
1401
|
+
const selectedShort = shortBaseSymbols.slice().sort();
|
|
1144
1402
|
const match = marketData.active.find((item) => {
|
|
1145
1403
|
const longs = [...item.longAssets].sort();
|
|
1146
1404
|
const shorts = [...item.shortAssets].sort();
|
|
1147
|
-
if (longs.length !== selectedLong.length ||
|
|
1405
|
+
if (longs.length !== selectedLong.length ||
|
|
1406
|
+
shorts.length !== selectedShort.length)
|
|
1148
1407
|
return false;
|
|
1149
1408
|
const longsEqual = longs.every((s, i) => s.asset === selectedLong[i]);
|
|
1150
1409
|
const shortsEqual = shorts.every((s, i) => s.asset === selectedShort[i]);
|
|
1151
1410
|
return longsEqual && shortsEqual;
|
|
1152
1411
|
});
|
|
1153
|
-
return match
|
|
1412
|
+
return match
|
|
1413
|
+
? { openInterest: match.openInterest, volume: match.volume }
|
|
1414
|
+
: empty;
|
|
1154
1415
|
})();
|
|
1155
1416
|
// Price ratio (only when exactly one long and one short)
|
|
1156
1417
|
const { priceRatio, priceRatio24h } = (() => {
|
|
@@ -1230,17 +1491,27 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1230
1491
|
return totalFunding;
|
|
1231
1492
|
})();
|
|
1232
1493
|
// Max leverage (minimum across all tokens)
|
|
1494
|
+
// Use tokens with their market prefixes for proper lookup in perpMetaAssets
|
|
1233
1495
|
const maxLeverage = (() => {
|
|
1234
1496
|
if (!perpMetaAssets)
|
|
1235
1497
|
return 0;
|
|
1236
|
-
const
|
|
1237
|
-
|
|
1498
|
+
const allTokensForLookup = [
|
|
1499
|
+
...longTokensForLookup,
|
|
1500
|
+
...shortTokensForLookup,
|
|
1501
|
+
];
|
|
1502
|
+
if (allTokensForLookup.length === 0)
|
|
1238
1503
|
return 0;
|
|
1239
1504
|
let minLev = Infinity;
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1505
|
+
allTokensForLookup.forEach(({ symbol, marketPrefix }) => {
|
|
1506
|
+
// Match by both name AND marketPrefix for HIP3 assets
|
|
1507
|
+
const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol &&
|
|
1508
|
+
(marketPrefix
|
|
1509
|
+
? u.marketPrefix === marketPrefix
|
|
1510
|
+
: !u.marketPrefix));
|
|
1511
|
+
// Fallback to just matching by name if no exact match
|
|
1512
|
+
const fallbackUniverse = tokenUniverse || perpMetaAssets.find((u) => u.name === symbol);
|
|
1513
|
+
if (fallbackUniverse === null || fallbackUniverse === void 0 ? void 0 : fallbackUniverse.maxLeverage)
|
|
1514
|
+
minLev = Math.min(minLev, fallbackUniverse.maxLeverage);
|
|
1244
1515
|
});
|
|
1245
1516
|
return minLev === Infinity ? 0 : minLev;
|
|
1246
1517
|
})();
|
|
@@ -1252,7 +1523,10 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1252
1523
|
// Whether all tokens have matching leverage
|
|
1253
1524
|
const leverageMatched = (() => {
|
|
1254
1525
|
const allTokensArr = [...longTokens, ...shortTokens];
|
|
1255
|
-
const allMetadata = {
|
|
1526
|
+
const allMetadata = {
|
|
1527
|
+
...longTokensMetadata,
|
|
1528
|
+
...shortTokensMetadata,
|
|
1529
|
+
};
|
|
1256
1530
|
if (allTokensArr.length === 0)
|
|
1257
1531
|
return true;
|
|
1258
1532
|
const tokensWithLev = allTokensArr.filter((token) => { var _a; return (_a = allMetadata[token.symbol]) === null || _a === void 0 ? void 0 : _a.leverage; });
|
|
@@ -5437,8 +5711,8 @@ function addAuthInterceptors(params) {
|
|
|
5437
5711
|
/**
|
|
5438
5712
|
* Fetch historical candle data from HyperLiquid API
|
|
5439
5713
|
*/
|
|
5440
|
-
const fetchHistoricalCandles = async (coin, startTime, endTime, interval,
|
|
5441
|
-
const backendCoin = toBackendSymbol(coin,
|
|
5714
|
+
const fetchHistoricalCandles = async (coin, startTime, endTime, interval, hip3Assets) => {
|
|
5715
|
+
const backendCoin = toBackendSymbol(coin, hip3Assets);
|
|
5442
5716
|
const request = {
|
|
5443
5717
|
req: { coin: backendCoin, startTime, endTime, interval },
|
|
5444
5718
|
type: 'candleSnapshot',
|
|
@@ -5597,10 +5871,10 @@ const useHistoricalPriceData = () => {
|
|
|
5597
5871
|
setTokenLoading(token.symbol, true);
|
|
5598
5872
|
});
|
|
5599
5873
|
try {
|
|
5600
|
-
const
|
|
5874
|
+
const hip3Assets = useHyperliquidData.getState().hip3Assets;
|
|
5601
5875
|
const fetchPromises = tokensToFetch.map(async (token) => {
|
|
5602
5876
|
try {
|
|
5603
|
-
const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval,
|
|
5877
|
+
const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, hip3Assets);
|
|
5604
5878
|
addHistoricalPriceData(token.symbol, interval, response.data, { start: startTime, end: endTime });
|
|
5605
5879
|
return { symbol: token.symbol, candles: response.data, success: true };
|
|
5606
5880
|
}
|
|
@@ -6369,12 +6643,12 @@ function validatePositionSize(usdValue, longAssets, shortAssets) {
|
|
|
6369
6643
|
* Authorization is derived from headers (Axios defaults or browser localStorage fallback)
|
|
6370
6644
|
* @throws MinimumPositionSizeError if any asset has less than $11 USD value
|
|
6371
6645
|
*/
|
|
6372
|
-
async function createPosition(baseUrl, payload,
|
|
6646
|
+
async function createPosition(baseUrl, payload, hip3Assets) {
|
|
6373
6647
|
// Validate minimum asset size before creating position
|
|
6374
6648
|
validateMinimumAssetSize(payload.usdValue, payload.longAssets, payload.shortAssets);
|
|
6375
6649
|
const url = joinUrl(baseUrl, "/positions");
|
|
6376
6650
|
// 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,
|
|
6651
|
+
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6378
6652
|
const translatedPayload = {
|
|
6379
6653
|
...payload,
|
|
6380
6654
|
longAssets: mapAssets(payload.longAssets),
|
|
@@ -6473,9 +6747,9 @@ async function adjustPosition(baseUrl, positionId, payload) {
|
|
|
6473
6747
|
throw toApiError(error);
|
|
6474
6748
|
}
|
|
6475
6749
|
}
|
|
6476
|
-
async function adjustAdvancePosition(baseUrl, positionId, payload,
|
|
6750
|
+
async function adjustAdvancePosition(baseUrl, positionId, payload, hip3Assets) {
|
|
6477
6751
|
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,
|
|
6752
|
+
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6479
6753
|
const translatedPayload = (payload || []).map((item) => ({
|
|
6480
6754
|
longAssets: mapAssets(item.longAssets),
|
|
6481
6755
|
shortAssets: mapAssets(item.shortAssets),
|
|
@@ -6600,16 +6874,62 @@ const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
|
|
|
6600
6874
|
});
|
|
6601
6875
|
};
|
|
6602
6876
|
|
|
6877
|
+
// Helper to find asset metadata from perpMetaAssets
|
|
6878
|
+
function findAssetMeta$1(coinName, perpMetaAssets) {
|
|
6879
|
+
var _a, _b, _c, _d;
|
|
6880
|
+
if (!perpMetaAssets) {
|
|
6881
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
6882
|
+
}
|
|
6883
|
+
// Try exact match first (for prefixed assets like "xyz:TSLA")
|
|
6884
|
+
const exactMatch = perpMetaAssets.find((a) => a.name === coinName);
|
|
6885
|
+
if (exactMatch) {
|
|
6886
|
+
return {
|
|
6887
|
+
collateralToken: (_a = exactMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
6888
|
+
marketPrefix: (_b = exactMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
6889
|
+
};
|
|
6890
|
+
}
|
|
6891
|
+
// Try matching by base symbol (for non-prefixed names in data)
|
|
6892
|
+
const baseMatch = perpMetaAssets.find((a) => {
|
|
6893
|
+
const baseName = a.name.includes(':') ? a.name.split(':')[1] : a.name;
|
|
6894
|
+
return baseName === coinName;
|
|
6895
|
+
});
|
|
6896
|
+
if (baseMatch) {
|
|
6897
|
+
return {
|
|
6898
|
+
collateralToken: (_c = baseMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
6899
|
+
marketPrefix: (_d = baseMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
6900
|
+
};
|
|
6901
|
+
}
|
|
6902
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
6903
|
+
}
|
|
6904
|
+
// Enrich position assets with market prefix and collateral token
|
|
6905
|
+
function enrichPositionAssets(assets, perpMetaAssets) {
|
|
6906
|
+
return assets.map((asset) => {
|
|
6907
|
+
const meta = findAssetMeta$1(asset.coin, perpMetaAssets);
|
|
6908
|
+
return {
|
|
6909
|
+
...asset,
|
|
6910
|
+
marketPrefix: meta.marketPrefix,
|
|
6911
|
+
collateralToken: meta.collateralToken,
|
|
6912
|
+
};
|
|
6913
|
+
});
|
|
6914
|
+
}
|
|
6915
|
+
// Enrich all positions with market metadata
|
|
6916
|
+
function enrichPositions(positions, perpMetaAssets) {
|
|
6917
|
+
return positions.map((position) => ({
|
|
6918
|
+
...position,
|
|
6919
|
+
longAssets: enrichPositionAssets(position.longAssets, perpMetaAssets),
|
|
6920
|
+
shortAssets: enrichPositionAssets(position.shortAssets, perpMetaAssets),
|
|
6921
|
+
}));
|
|
6922
|
+
}
|
|
6603
6923
|
function usePosition() {
|
|
6604
6924
|
const context = useContext(PearHyperliquidContext);
|
|
6605
6925
|
if (!context) {
|
|
6606
6926
|
throw new Error('usePosition must be used within a PearHyperliquidProvider');
|
|
6607
6927
|
}
|
|
6608
6928
|
const { apiBaseUrl, isConnected } = context;
|
|
6609
|
-
const
|
|
6929
|
+
const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
|
|
6610
6930
|
// Create position API action
|
|
6611
6931
|
const createPosition$1 = async (payload) => {
|
|
6612
|
-
return createPosition(apiBaseUrl, payload,
|
|
6932
|
+
return createPosition(apiBaseUrl, payload, hip3Assets);
|
|
6613
6933
|
};
|
|
6614
6934
|
// Update TP/SL risk parameters for a position
|
|
6615
6935
|
const updateRiskParameters$1 = async (positionId, payload) => {
|
|
@@ -6629,21 +6949,33 @@ function usePosition() {
|
|
|
6629
6949
|
};
|
|
6630
6950
|
// Adjust to absolute target sizes per asset, optionally adding new assets
|
|
6631
6951
|
const adjustAdvancePosition$1 = async (positionId, payload) => {
|
|
6632
|
-
return adjustAdvancePosition(apiBaseUrl, positionId, payload,
|
|
6952
|
+
return adjustAdvancePosition(apiBaseUrl, positionId, payload, hip3Assets);
|
|
6633
6953
|
};
|
|
6634
6954
|
// Open positions using WS data, with derived values
|
|
6635
6955
|
const userOpenPositions = useUserData((state) => state.rawOpenPositions);
|
|
6636
6956
|
const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
6637
6957
|
const allMids = useHyperliquidData((state) => state.allMids);
|
|
6958
|
+
const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
6638
6959
|
const isLoading = useMemo(() => {
|
|
6639
6960
|
return userOpenPositions === null && isConnected;
|
|
6640
6961
|
}, [userOpenPositions, isConnected]);
|
|
6641
6962
|
const openPositions = useMemo(() => {
|
|
6642
6963
|
if (!userOpenPositions || !aggregatedClearingHouseState || !allMids)
|
|
6643
6964
|
return null;
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6965
|
+
const positions = buildPositionValue(userOpenPositions, aggregatedClearingHouseState, allMids);
|
|
6966
|
+
// Enrich with market prefix and collateral token
|
|
6967
|
+
return enrichPositions(positions, perpMetaAssets);
|
|
6968
|
+
}, [userOpenPositions, aggregatedClearingHouseState, allMids, perpMetaAssets]);
|
|
6969
|
+
return {
|
|
6970
|
+
createPosition: createPosition$1,
|
|
6971
|
+
updateRiskParameters: updateRiskParameters$1,
|
|
6972
|
+
closePosition: closePosition$1,
|
|
6973
|
+
closeAllPositions: closeAllPositions$1,
|
|
6974
|
+
adjustPosition: adjustPosition$1,
|
|
6975
|
+
adjustAdvancePosition: adjustAdvancePosition$1,
|
|
6976
|
+
openPositions,
|
|
6977
|
+
isLoading,
|
|
6978
|
+
};
|
|
6647
6979
|
}
|
|
6648
6980
|
|
|
6649
6981
|
async function adjustOrder(baseUrl, orderId, payload) {
|
|
@@ -6815,59 +7147,170 @@ function useNotifications() {
|
|
|
6815
7147
|
};
|
|
6816
7148
|
}
|
|
6817
7149
|
|
|
6818
|
-
//
|
|
7150
|
+
// Helper to find asset metadata from perpMetaAssets
|
|
7151
|
+
function findAssetMeta(assetName, perpMetaAssets) {
|
|
7152
|
+
var _a, _b, _c, _d;
|
|
7153
|
+
if (!perpMetaAssets) {
|
|
7154
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7155
|
+
}
|
|
7156
|
+
// Try exact match first (for prefixed assets like "xyz:TSLA")
|
|
7157
|
+
const exactMatch = perpMetaAssets.find((a) => a.name === assetName);
|
|
7158
|
+
if (exactMatch) {
|
|
7159
|
+
return {
|
|
7160
|
+
collateralToken: (_a = exactMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
7161
|
+
marketPrefix: (_b = exactMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
7162
|
+
};
|
|
7163
|
+
}
|
|
7164
|
+
// Try matching by base symbol (for non-prefixed names in data)
|
|
7165
|
+
const baseMatch = perpMetaAssets.find((a) => {
|
|
7166
|
+
const baseName = a.name.includes(':') ? a.name.split(':')[1] : a.name;
|
|
7167
|
+
return baseName === assetName;
|
|
7168
|
+
});
|
|
7169
|
+
if (baseMatch) {
|
|
7170
|
+
return {
|
|
7171
|
+
collateralToken: (_c = baseMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
7172
|
+
marketPrefix: (_d = baseMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
7173
|
+
};
|
|
7174
|
+
}
|
|
7175
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7176
|
+
}
|
|
7177
|
+
// Enrich a single asset with metadata
|
|
7178
|
+
function enrichAsset(asset, perpMetaAssets) {
|
|
7179
|
+
const meta = findAssetMeta(asset.asset, perpMetaAssets);
|
|
7180
|
+
return {
|
|
7181
|
+
...asset,
|
|
7182
|
+
collateralToken: meta.collateralToken,
|
|
7183
|
+
marketPrefix: meta.marketPrefix,
|
|
7184
|
+
};
|
|
7185
|
+
}
|
|
7186
|
+
// Enrich a basket item with collateral info
|
|
7187
|
+
function enrichBasketItem(item, perpMetaAssets) {
|
|
7188
|
+
const enrichedLongs = item.longAssets.map((a) => enrichAsset(a, perpMetaAssets));
|
|
7189
|
+
const enrichedShorts = item.shortAssets.map((a) => enrichAsset(a, perpMetaAssets));
|
|
7190
|
+
// Determine collateral type
|
|
7191
|
+
const allAssets = [...enrichedLongs, ...enrichedShorts];
|
|
7192
|
+
const hasUsdc = allAssets.some((a) => a.collateralToken === 'USDC');
|
|
7193
|
+
const hasUsdh = allAssets.some((a) => a.collateralToken === 'USDH');
|
|
7194
|
+
let collateralType = 'USDC';
|
|
7195
|
+
if (hasUsdc && hasUsdh) {
|
|
7196
|
+
collateralType = 'MIXED';
|
|
7197
|
+
}
|
|
7198
|
+
else if (hasUsdh) {
|
|
7199
|
+
collateralType = 'USDH';
|
|
7200
|
+
}
|
|
7201
|
+
return {
|
|
7202
|
+
...item,
|
|
7203
|
+
longAssets: enrichedLongs,
|
|
7204
|
+
shortAssets: enrichedShorts,
|
|
7205
|
+
collateralType,
|
|
7206
|
+
};
|
|
7207
|
+
}
|
|
7208
|
+
/**
|
|
7209
|
+
* Filter baskets by collateral type
|
|
7210
|
+
* - 'USDC': Only baskets where ALL assets use USDC (collateralType === 'USDC')
|
|
7211
|
+
* - 'USDH': Only baskets where ALL assets use USDH (collateralType === 'USDH')
|
|
7212
|
+
* - 'ALL' or undefined: No filtering, returns all baskets
|
|
7213
|
+
*/
|
|
7214
|
+
function filterByCollateral(baskets, filter) {
|
|
7215
|
+
if (!filter || filter === 'ALL') {
|
|
7216
|
+
return baskets;
|
|
7217
|
+
}
|
|
7218
|
+
return baskets.filter((basket) => {
|
|
7219
|
+
if (filter === 'USDC') {
|
|
7220
|
+
// Include baskets that are purely USDC or have USDC assets
|
|
7221
|
+
return (basket.collateralType === 'USDC' || basket.collateralType === 'MIXED');
|
|
7222
|
+
}
|
|
7223
|
+
if (filter === 'USDH') {
|
|
7224
|
+
// Include baskets that are purely USDH or have USDH assets
|
|
7225
|
+
return (basket.collateralType === 'USDH' || basket.collateralType === 'MIXED');
|
|
7226
|
+
}
|
|
7227
|
+
return true;
|
|
7228
|
+
});
|
|
7229
|
+
}
|
|
7230
|
+
// Base selector for the full market-data payload (raw from WS)
|
|
6819
7231
|
const useMarketDataPayload = () => {
|
|
6820
7232
|
return useMarketData((s) => s.marketData);
|
|
6821
7233
|
};
|
|
6822
|
-
// Full payload for 'market-data-all' channel
|
|
7234
|
+
// Full payload for 'market-data-all' channel (raw from WS)
|
|
6823
7235
|
const useMarketDataAllPayload = () => {
|
|
6824
7236
|
return useMarketData((s) => s.marketDataAll);
|
|
6825
7237
|
};
|
|
6826
|
-
//
|
|
6827
|
-
const
|
|
6828
|
-
|
|
7238
|
+
// Access perpMetaAssets for enrichment
|
|
7239
|
+
const usePerpMetaAssets = () => {
|
|
7240
|
+
return useHyperliquidData((s) => s.perpMetaAssets);
|
|
7241
|
+
};
|
|
7242
|
+
// Active baskets (with collateral and market prefix info)
|
|
7243
|
+
const useActiveBaskets = (collateralFilter) => {
|
|
6829
7244
|
const data = useMarketDataPayload();
|
|
6830
|
-
|
|
7245
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7246
|
+
return useMemo(() => {
|
|
7247
|
+
if (!(data === null || data === void 0 ? void 0 : data.active))
|
|
7248
|
+
return [];
|
|
7249
|
+
const enriched = data.active.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7250
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7251
|
+
}, [data, perpMetaAssets, collateralFilter]);
|
|
6831
7252
|
};
|
|
6832
|
-
// Top gainers (
|
|
6833
|
-
const useTopGainers = (limit) => {
|
|
7253
|
+
// Top gainers (with collateral and market prefix info)
|
|
7254
|
+
const useTopGainers = (limit, collateralFilter) => {
|
|
6834
7255
|
const data = useMarketDataPayload();
|
|
7256
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6835
7257
|
return useMemo(() => {
|
|
6836
7258
|
var _a;
|
|
6837
7259
|
const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
|
|
6838
|
-
|
|
6839
|
-
|
|
7260
|
+
const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
|
|
7261
|
+
const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7262
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7263
|
+
}, [data, perpMetaAssets, limit, collateralFilter]);
|
|
6840
7264
|
};
|
|
6841
|
-
// Top losers (
|
|
6842
|
-
const useTopLosers = (limit) => {
|
|
7265
|
+
// Top losers (with collateral and market prefix info)
|
|
7266
|
+
const useTopLosers = (limit, collateralFilter) => {
|
|
6843
7267
|
const data = useMarketDataPayload();
|
|
7268
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6844
7269
|
return useMemo(() => {
|
|
6845
7270
|
var _a;
|
|
6846
7271
|
const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
|
|
6847
|
-
|
|
6848
|
-
|
|
7272
|
+
const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
|
|
7273
|
+
const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7274
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7275
|
+
}, [data, perpMetaAssets, limit, collateralFilter]);
|
|
6849
7276
|
};
|
|
6850
|
-
// Highlighted baskets
|
|
6851
|
-
const useHighlightedBaskets = () => {
|
|
6852
|
-
var _a;
|
|
7277
|
+
// Highlighted baskets (with collateral and market prefix info)
|
|
7278
|
+
const useHighlightedBaskets = (collateralFilter) => {
|
|
6853
7279
|
const data = useMarketDataPayload();
|
|
6854
|
-
|
|
7280
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7281
|
+
return useMemo(() => {
|
|
7282
|
+
if (!(data === null || data === void 0 ? void 0 : data.highlighted))
|
|
7283
|
+
return [];
|
|
7284
|
+
const enriched = data.highlighted.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7285
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7286
|
+
}, [data, perpMetaAssets, collateralFilter]);
|
|
6855
7287
|
};
|
|
6856
|
-
// Watchlist baskets (
|
|
6857
|
-
const useWatchlistBaskets = () => {
|
|
6858
|
-
var _a;
|
|
7288
|
+
// Watchlist baskets (with collateral and market prefix info)
|
|
7289
|
+
const useWatchlistBaskets = (collateralFilter) => {
|
|
6859
7290
|
const data = useMarketDataPayload();
|
|
6860
|
-
|
|
7291
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7292
|
+
return useMemo(() => {
|
|
7293
|
+
if (!(data === null || data === void 0 ? void 0 : data.watchlist))
|
|
7294
|
+
return [];
|
|
7295
|
+
const enriched = data.watchlist.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7296
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7297
|
+
}, [data, perpMetaAssets, collateralFilter]);
|
|
6861
7298
|
};
|
|
6862
|
-
// All baskets (
|
|
6863
|
-
const useAllBaskets = () => {
|
|
6864
|
-
var _a;
|
|
7299
|
+
// All baskets (with collateral and market prefix info)
|
|
7300
|
+
const useAllBaskets = (collateralFilter) => {
|
|
6865
7301
|
const dataAll = useMarketDataAllPayload();
|
|
6866
|
-
|
|
7302
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7303
|
+
return useMemo(() => {
|
|
7304
|
+
if (!(dataAll === null || dataAll === void 0 ? void 0 : dataAll.all))
|
|
7305
|
+
return [];
|
|
7306
|
+
const enriched = dataAll.all.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7307
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7308
|
+
}, [dataAll, perpMetaAssets, collateralFilter]);
|
|
6867
7309
|
};
|
|
6868
7310
|
// Find a basket by its exact asset composition (order-insensitive)
|
|
6869
7311
|
const useFindBasket = (longs, shorts) => {
|
|
6870
7312
|
const data = useMarketDataPayload();
|
|
7313
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6871
7314
|
return useMemo(() => {
|
|
6872
7315
|
if (!data)
|
|
6873
7316
|
return undefined;
|
|
@@ -6881,17 +7324,28 @@ const useFindBasket = (longs, shorts) => {
|
|
|
6881
7324
|
: '';
|
|
6882
7325
|
const lKey = normalize(longs);
|
|
6883
7326
|
const sKey = normalize(shorts);
|
|
6884
|
-
const match = (item) => normalize(item.longAssets) === lKey &&
|
|
6885
|
-
|
|
6886
|
-
|
|
7327
|
+
const match = (item) => normalize(item.longAssets) === lKey &&
|
|
7328
|
+
normalize(item.shortAssets) === sKey;
|
|
7329
|
+
const found = data.active.find(match) || data.highlighted.find(match);
|
|
7330
|
+
return found
|
|
7331
|
+
? enrichBasketItem(found, perpMetaAssets)
|
|
7332
|
+
: undefined;
|
|
7333
|
+
}, [data, longs, shorts, perpMetaAssets]);
|
|
6887
7334
|
};
|
|
6888
7335
|
|
|
6889
|
-
async function toggleWatchlist(baseUrl, longAssets, shortAssets,
|
|
7336
|
+
async function toggleWatchlist(baseUrl, longAssets, shortAssets, hip3Assets) {
|
|
6890
7337
|
const url = joinUrl(baseUrl, '/watchlist');
|
|
6891
|
-
const mapAssets = (arr) => arr.map(a => ({ ...a, asset: toBackendSymbol(a.asset,
|
|
7338
|
+
const mapAssets = (arr) => arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6892
7339
|
try {
|
|
6893
|
-
const response = await apiClient.post(url, {
|
|
6894
|
-
|
|
7340
|
+
const response = await apiClient.post(url, {
|
|
7341
|
+
longAssets: mapAssets(longAssets),
|
|
7342
|
+
shortAssets: mapAssets(shortAssets),
|
|
7343
|
+
}, { headers: { 'Content-Type': 'application/json' } });
|
|
7344
|
+
return {
|
|
7345
|
+
data: response.data,
|
|
7346
|
+
status: response.status,
|
|
7347
|
+
headers: response.headers,
|
|
7348
|
+
};
|
|
6895
7349
|
}
|
|
6896
7350
|
catch (error) {
|
|
6897
7351
|
throw toApiError(error);
|
|
@@ -6903,11 +7357,11 @@ function useWatchlist() {
|
|
|
6903
7357
|
if (!context)
|
|
6904
7358
|
throw new Error('useWatchlist must be used within a PearHyperliquidProvider');
|
|
6905
7359
|
const { apiBaseUrl, isConnected } = context;
|
|
6906
|
-
const
|
|
7360
|
+
const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
|
|
6907
7361
|
const marketData = useMarketDataPayload();
|
|
6908
7362
|
const isLoading = useMemo(() => !marketData && isConnected, [marketData, isConnected]);
|
|
6909
7363
|
const toggle = async (longAssets, shortAssets) => {
|
|
6910
|
-
const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets,
|
|
7364
|
+
const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, hip3Assets);
|
|
6911
7365
|
// Server will push updated market-data over WS; nothing to set here
|
|
6912
7366
|
return resp;
|
|
6913
7367
|
};
|
|
@@ -7029,7 +7483,7 @@ async function logout(baseUrl, refreshTokenVal) {
|
|
|
7029
7483
|
function useAuth() {
|
|
7030
7484
|
const context = useContext(PearHyperliquidContext);
|
|
7031
7485
|
if (!context) {
|
|
7032
|
-
throw new Error(
|
|
7486
|
+
throw new Error('usePortfolio must be used within a PearHyperliquidProvider');
|
|
7033
7487
|
}
|
|
7034
7488
|
const { apiBaseUrl, clientId } = context;
|
|
7035
7489
|
const [isReady, setIsReady] = useState(false);
|
|
@@ -7039,46 +7493,26 @@ function useAuth() {
|
|
|
7039
7493
|
const setRefreshToken = useUserData((s) => s.setRefreshToken);
|
|
7040
7494
|
const isAuthenticated = useUserData((s) => s.isAuthenticated);
|
|
7041
7495
|
const setIsAuthenticated = useUserData((s) => s.setIsAuthenticated);
|
|
7042
|
-
const address = useUserData((s) => s.address);
|
|
7043
7496
|
const setAddress = useUserData((s) => s.setAddress);
|
|
7044
7497
|
useEffect(() => {
|
|
7045
|
-
if (typeof window ==
|
|
7498
|
+
if (typeof window == 'undefined') {
|
|
7046
7499
|
return;
|
|
7047
7500
|
}
|
|
7048
|
-
|
|
7049
|
-
const
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
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
|
-
]);
|
|
7501
|
+
const access = localStorage.getItem('accessToken');
|
|
7502
|
+
const refresh = localStorage.getItem('refreshToken');
|
|
7503
|
+
const addr = localStorage.getItem('address');
|
|
7504
|
+
setAccessToken(access);
|
|
7505
|
+
setRefreshToken(refresh);
|
|
7506
|
+
setAddress(addr);
|
|
7507
|
+
const authed = Boolean(access && addr);
|
|
7508
|
+
setIsAuthenticated(authed);
|
|
7509
|
+
setIsReady(true);
|
|
7510
|
+
}, [setAccessToken, setRefreshToken, setIsAuthenticated, setAddress]);
|
|
7071
7511
|
useEffect(() => {
|
|
7072
7512
|
const cleanup = addAuthInterceptors({
|
|
7073
7513
|
apiBaseUrl,
|
|
7074
7514
|
getAccessToken: () => {
|
|
7075
|
-
|
|
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);
|
|
7515
|
+
return typeof window !== 'undefined' ? window.localStorage.getItem('accessToken') : null;
|
|
7082
7516
|
},
|
|
7083
7517
|
refreshTokens: async () => {
|
|
7084
7518
|
const data = await refreshTokens();
|
|
@@ -7099,15 +7533,14 @@ function useAuth() {
|
|
|
7099
7533
|
async function loginWithSignedMessage(address, signature, timestamp) {
|
|
7100
7534
|
try {
|
|
7101
7535
|
const { data } = await authenticate(apiBaseUrl, {
|
|
7102
|
-
method:
|
|
7536
|
+
method: 'eip712',
|
|
7103
7537
|
address,
|
|
7104
7538
|
clientId,
|
|
7105
7539
|
details: { signature, timestamp },
|
|
7106
7540
|
});
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
window.localStorage.setItem(
|
|
7110
|
-
window.localStorage.setItem(refreshTokenKey, data.refreshToken);
|
|
7541
|
+
window.localStorage.setItem('accessToken', data.accessToken);
|
|
7542
|
+
window.localStorage.setItem('refreshToken', data.refreshToken);
|
|
7543
|
+
window.localStorage.setItem('address', address);
|
|
7111
7544
|
setAccessToken(data.accessToken);
|
|
7112
7545
|
setRefreshToken(data.refreshToken);
|
|
7113
7546
|
setAddress(address);
|
|
@@ -7119,16 +7552,10 @@ function useAuth() {
|
|
|
7119
7552
|
}
|
|
7120
7553
|
async function loginWithPrivyToken(address, appId, privyAccessToken) {
|
|
7121
7554
|
try {
|
|
7122
|
-
const { data } = await authenticateWithPrivy(apiBaseUrl, {
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
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);
|
|
7555
|
+
const { data } = await authenticateWithPrivy(apiBaseUrl, { address, clientId, appId, accessToken: privyAccessToken });
|
|
7556
|
+
window.localStorage.setItem('accessToken', data.accessToken);
|
|
7557
|
+
window.localStorage.setItem('refreshToken', data.refreshToken);
|
|
7558
|
+
window.localStorage.setItem('address', address);
|
|
7132
7559
|
setAccessToken(data.accessToken);
|
|
7133
7560
|
setRefreshToken(data.refreshToken);
|
|
7134
7561
|
setAddress(address);
|
|
@@ -7139,38 +7566,28 @@ function useAuth() {
|
|
|
7139
7566
|
}
|
|
7140
7567
|
}
|
|
7141
7568
|
async function refreshTokens() {
|
|
7142
|
-
const
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
const accessTokenKey = `${currentAddress}_accessToken`;
|
|
7149
|
-
const refreshTokenKey = `${currentAddress}_refreshToken`;
|
|
7150
|
-
window.localStorage.setItem(accessTokenKey, data.accessToken);
|
|
7151
|
-
window.localStorage.setItem(refreshTokenKey, data.refreshToken);
|
|
7569
|
+
const refresh = window.localStorage.getItem('refreshToken');
|
|
7570
|
+
if (!refresh)
|
|
7571
|
+
throw new Error('No refresh token');
|
|
7572
|
+
const { data } = await refreshToken(apiBaseUrl, refresh);
|
|
7573
|
+
window.localStorage.setItem('accessToken', data.accessToken);
|
|
7574
|
+
window.localStorage.setItem('refreshToken', data.refreshToken);
|
|
7152
7575
|
setAccessToken(data.accessToken);
|
|
7153
7576
|
setRefreshToken(data.refreshToken);
|
|
7154
7577
|
setIsAuthenticated(true);
|
|
7155
7578
|
return data;
|
|
7156
7579
|
}
|
|
7157
7580
|
async function logout$1() {
|
|
7158
|
-
const
|
|
7159
|
-
|
|
7160
|
-
if (currentRefresh) {
|
|
7581
|
+
const refresh = window.localStorage.getItem('refreshToken');
|
|
7582
|
+
if (refresh) {
|
|
7161
7583
|
try {
|
|
7162
|
-
await logout(apiBaseUrl,
|
|
7584
|
+
await logout(apiBaseUrl, refresh);
|
|
7163
7585
|
}
|
|
7164
|
-
catch (_a) {
|
|
7165
|
-
/* ignore */
|
|
7166
|
-
}
|
|
7167
|
-
}
|
|
7168
|
-
if (currentAddress) {
|
|
7169
|
-
const accessTokenKey = `${currentAddress}_accessToken`;
|
|
7170
|
-
const refreshTokenKey = `${currentAddress}_refreshToken`;
|
|
7171
|
-
window.localStorage.removeItem(accessTokenKey);
|
|
7172
|
-
window.localStorage.removeItem(refreshTokenKey);
|
|
7586
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
7173
7587
|
}
|
|
7588
|
+
window.localStorage.removeItem('accessToken');
|
|
7589
|
+
window.localStorage.removeItem('refreshToken');
|
|
7590
|
+
window.localStorage.removeItem('address');
|
|
7174
7591
|
setAccessToken(null);
|
|
7175
7592
|
setRefreshToken(null);
|
|
7176
7593
|
setAddress(null);
|
|
@@ -7193,12 +7610,13 @@ const PearHyperliquidContext = createContext(undefined);
|
|
|
7193
7610
|
/**
|
|
7194
7611
|
* React Provider for PearHyperliquidClient
|
|
7195
7612
|
*/
|
|
7196
|
-
const PearHyperliquidProvider = ({ children, apiBaseUrl =
|
|
7613
|
+
const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-v2.pearprotocol.io', clientId = 'PEARPROTOCOLUI', wsUrl = 'wss://hl-v2.pearprotocol.io/ws', }) => {
|
|
7197
7614
|
const address = useUserData((s) => s.address);
|
|
7198
7615
|
const setAddress = useUserData((s) => s.setAddress);
|
|
7199
7616
|
const perpsMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
7200
7617
|
const setPerpMetaAssets = useHyperliquidData((state) => state.setPerpMetaAssets);
|
|
7201
|
-
const
|
|
7618
|
+
const setHip3Assets = useHyperliquidData((state) => state.setHip3Assets);
|
|
7619
|
+
const setHip3MarketPrefixes = useHyperliquidData((state) => state.setHip3MarketPrefixes);
|
|
7202
7620
|
const websocketsEnabled = useMemo(() => Array.isArray(perpsMetaAssets) && perpsMetaAssets.length > 0, [perpsMetaAssets]);
|
|
7203
7621
|
const { isConnected, lastError } = useHyperliquidWebSocket({
|
|
7204
7622
|
wsUrl,
|
|
@@ -7213,28 +7631,62 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-v2.pearpro
|
|
|
7213
7631
|
if (perpsMetaAssets === null) {
|
|
7214
7632
|
fetchAllPerpMetas()
|
|
7215
7633
|
.then((res) => {
|
|
7216
|
-
|
|
7217
|
-
const
|
|
7218
|
-
const
|
|
7219
|
-
const cleanedPerpMetas =
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7634
|
+
// Maps for multi-market assets
|
|
7635
|
+
const assetToMarkets = new Map(); // TSLA -> ["xyz:TSLA", "flx:TSLA"]
|
|
7636
|
+
const marketPrefixes = new Map(); // "xyz:TSLA" -> "xyz"
|
|
7637
|
+
const cleanedPerpMetas = [];
|
|
7638
|
+
// Process each market group (different collateral tokens)
|
|
7639
|
+
res.data.forEach((item) => {
|
|
7640
|
+
// Convert numeric collateral token to human-readable name
|
|
7641
|
+
const collateralToken = item.collateralToken === 360 ? 'USDH' : 'USDC';
|
|
7642
|
+
item.universe.forEach((asset) => {
|
|
7643
|
+
var _a;
|
|
7644
|
+
const [maybePrefix, maybeMarket] = asset.name.split(':');
|
|
7645
|
+
if (maybeMarket) {
|
|
7646
|
+
// HIP-3 market with prefix (e.g., "xyz:TSLA")
|
|
7647
|
+
const prefix = maybePrefix.toLowerCase();
|
|
7648
|
+
const displayName = maybeMarket;
|
|
7649
|
+
const fullName = `${prefix}:${displayName}`;
|
|
7650
|
+
// Store full market name with prefix
|
|
7651
|
+
marketPrefixes.set(fullName, prefix);
|
|
7652
|
+
// Track all markets for this asset
|
|
7653
|
+
const existingMarkets = (_a = assetToMarkets.get(displayName)) !== null && _a !== void 0 ? _a : [];
|
|
7654
|
+
if (!existingMarkets.includes(fullName)) {
|
|
7655
|
+
assetToMarkets.set(displayName, [
|
|
7656
|
+
...existingMarkets,
|
|
7657
|
+
fullName,
|
|
7658
|
+
]);
|
|
7659
|
+
}
|
|
7660
|
+
// Add asset with all metadata INCLUDING collateral token for THIS specific market
|
|
7661
|
+
// Important: We keep ALL variants so each market+collateral combo is tracked
|
|
7662
|
+
cleanedPerpMetas.push({
|
|
7663
|
+
...asset,
|
|
7664
|
+
name: displayName, // Use display name for UI
|
|
7665
|
+
marketPrefix: prefix, // Which market (xyz, flx, etc)
|
|
7666
|
+
collateralToken, // "USDC" or "USDH"
|
|
7667
|
+
});
|
|
7668
|
+
}
|
|
7669
|
+
else {
|
|
7670
|
+
// Regular market without prefix
|
|
7671
|
+
cleanedPerpMetas.push({
|
|
7672
|
+
...asset,
|
|
7673
|
+
collateralToken, // "USDC" or "USDH"
|
|
7674
|
+
});
|
|
7675
|
+
}
|
|
7676
|
+
});
|
|
7231
7677
|
});
|
|
7232
|
-
|
|
7678
|
+
setHip3Assets(assetToMarkets);
|
|
7679
|
+
setHip3MarketPrefixes(marketPrefixes);
|
|
7233
7680
|
setPerpMetaAssets(cleanedPerpMetas);
|
|
7234
7681
|
})
|
|
7235
7682
|
.catch(() => { });
|
|
7236
7683
|
}
|
|
7237
|
-
}, [
|
|
7684
|
+
}, [
|
|
7685
|
+
perpsMetaAssets,
|
|
7686
|
+
setPerpMetaAssets,
|
|
7687
|
+
setHip3Assets,
|
|
7688
|
+
setHip3MarketPrefixes,
|
|
7689
|
+
]);
|
|
7238
7690
|
// Auth methods now sourced from useAuth hook
|
|
7239
7691
|
useAutoSyncFills({
|
|
7240
7692
|
baseUrl: apiBaseUrl,
|
|
@@ -7254,9 +7706,6 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-v2.pearpro
|
|
|
7254
7706
|
// HyperLiquid native WebSocket state
|
|
7255
7707
|
nativeIsConnected,
|
|
7256
7708
|
nativeLastError,
|
|
7257
|
-
// Address utilities
|
|
7258
|
-
address,
|
|
7259
|
-
setAddress,
|
|
7260
7709
|
}), [
|
|
7261
7710
|
apiBaseUrl,
|
|
7262
7711
|
wsUrl,
|
|
@@ -7276,7 +7725,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-v2.pearpro
|
|
|
7276
7725
|
function usePearHyperliquid() {
|
|
7277
7726
|
const ctx = useContext(PearHyperliquidContext);
|
|
7278
7727
|
if (!ctx)
|
|
7279
|
-
throw new Error(
|
|
7728
|
+
throw new Error('usePearHyperliquid must be used within a PearHyperliquidProvider');
|
|
7280
7729
|
return ctx;
|
|
7281
7730
|
}
|
|
7282
7731
|
|
|
@@ -7367,4 +7816,4 @@ function mapCandleIntervalToTradingViewInterval(interval) {
|
|
|
7367
7816
|
}
|
|
7368
7817
|
}
|
|
7369
7818
|
|
|
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 };
|
|
7819
|
+
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 };
|