@pear-protocol/hyperliquid-sdk 0.0.72 → 0.0.73-beta-2
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/kalshi.d.ts +87 -0
- package/dist/clients/orders.d.ts +41 -0
- package/dist/clients/positions.d.ts +14 -22
- package/dist/clients/watchlist.d.ts +1 -1
- package/dist/hooks/index.d.ts +3 -1
- package/dist/hooks/useAllUserBalances.d.ts +9 -0
- package/dist/hooks/useAuth.d.ts +3 -3
- package/dist/hooks/useHyperliquidUserFills.d.ts +17 -0
- package/dist/hooks/useMarketData.d.ts +9 -7
- package/dist/hooks/useOrders.d.ts +2 -3
- package/dist/hooks/useSpotOrder.d.ts +13 -0
- package/dist/hooks/useTrading.d.ts +0 -3
- package/dist/hooks/useWebData.d.ts +21 -3
- package/dist/hyperliquid-websocket.d.ts +2 -1
- package/dist/index.d.ts +490 -95
- package/dist/index.js +1674 -354
- package/dist/provider.d.ts +1 -1
- package/dist/store/historicalPriceDataStore.d.ts +12 -1
- package/dist/store/hyperliquidDataStore.d.ts +31 -1
- package/dist/store/marketDataStore.d.ts +10 -1
- package/dist/store/tokenSelectionMetadataStore.d.ts +1 -1
- package/dist/store/userDataStore.d.ts +30 -1
- package/dist/store/userSelection.d.ts +1 -1
- package/dist/types.d.ts +147 -30
- package/dist/utils/order-helpers.d.ts +57 -0
- package/dist/utils/symbol-translator.d.ts +32 -3
- package/dist/utils/token-metadata-extractor.d.ts +9 -5
- package/package.json +3 -2
- package/dist/hooks/useAutoSyncFills.d.ts +0 -19
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { useState, useRef, useCallback, useEffect, useMemo, useContext, createContext } from 'react';
|
|
3
|
-
import { create } from 'zustand';
|
|
2
|
+
import React, { useState, useRef, useCallback, useEffect, useMemo, useContext, createContext } from 'react';
|
|
4
3
|
|
|
5
4
|
// Browser-compatible WebSocket ready state enum (mirrors native values)
|
|
6
5
|
var ReadyState;
|
|
@@ -11,6 +10,47 @@ var ReadyState;
|
|
|
11
10
|
ReadyState[ReadyState["CLOSED"] = 3] = "CLOSED";
|
|
12
11
|
})(ReadyState || (ReadyState = {}));
|
|
13
12
|
|
|
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
|
+
|
|
14
54
|
const useUserData = create((set) => ({
|
|
15
55
|
accessToken: null,
|
|
16
56
|
refreshToken: null,
|
|
@@ -23,6 +63,7 @@ const useUserData = create((set) => ({
|
|
|
23
63
|
twapDetails: null,
|
|
24
64
|
notifications: null,
|
|
25
65
|
userExtraAgents: null,
|
|
66
|
+
spotState: null,
|
|
26
67
|
setAccessToken: (token) => set({ accessToken: token }),
|
|
27
68
|
setRefreshToken: (token) => set({ refreshToken: token }),
|
|
28
69
|
setIsAuthenticated: (value) => set({ isAuthenticated: value }),
|
|
@@ -43,6 +84,7 @@ const useUserData = create((set) => ({
|
|
|
43
84
|
setAccountSummary: (value) => set({ accountSummary: value }),
|
|
44
85
|
setTwapDetails: (value) => set({ twapDetails: value }),
|
|
45
86
|
setNotifications: (value) => set({ notifications: value }),
|
|
87
|
+
setSpotState: (value) => set({ spotState: value }),
|
|
46
88
|
clean: () => set({
|
|
47
89
|
accessToken: null,
|
|
48
90
|
refreshToken: null,
|
|
@@ -54,6 +96,7 @@ const useUserData = create((set) => ({
|
|
|
54
96
|
accountSummary: null,
|
|
55
97
|
twapDetails: null,
|
|
56
98
|
notifications: null,
|
|
99
|
+
spotState: null,
|
|
57
100
|
}),
|
|
58
101
|
setUserExtraAgents: (value) => set({ userExtraAgents: value }),
|
|
59
102
|
}));
|
|
@@ -71,18 +114,71 @@ const useMarketData = create((set) => ({
|
|
|
71
114
|
* Convert a full/prefixed symbol (e.g., "xyz:XYZ100") to a display symbol (e.g., "XYZ100").
|
|
72
115
|
*/
|
|
73
116
|
function toDisplaySymbol(symbol) {
|
|
74
|
-
const parts = symbol.split(
|
|
117
|
+
const parts = symbol.split(':');
|
|
75
118
|
return parts.length > 1 ? parts.slice(-1)[0] : symbol;
|
|
76
119
|
}
|
|
77
120
|
/**
|
|
78
121
|
* Convert a display symbol back to backend form using a provided map.
|
|
79
122
|
* If mapping is missing, returns the original symbol.
|
|
80
|
-
*
|
|
81
|
-
* @param
|
|
123
|
+
* For multi-market assets, returns the first available market.
|
|
124
|
+
* @param displaySymbol e.g., "TSLA"
|
|
125
|
+
* @param hip3Assets map of display -> all full market names (e.g., "TSLA" -> ["xyz:TSLA", "flx:TSLA"])
|
|
126
|
+
*/
|
|
127
|
+
function toBackendSymbol(displaySymbol, hip3Assets) {
|
|
128
|
+
const markets = hip3Assets.get(displaySymbol);
|
|
129
|
+
// Return first market if available, otherwise return original symbol
|
|
130
|
+
return markets && markets.length > 0 ? markets[0] : displaySymbol;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Convert a display symbol to backend form for a specific market prefix.
|
|
134
|
+
* This is useful when an asset is available on multiple markets (e.g., xyz:TSLA and flx:TSLA).
|
|
135
|
+
* @param displaySymbol e.g., "TSLA"
|
|
136
|
+
* @param marketPrefix e.g., "xyz" or "flx"
|
|
137
|
+
* @param hip3Assets map of display -> all full market names
|
|
138
|
+
* @returns Full market name if found, null if prefix not specified for multi-market asset, otherwise displaySymbol with prefix
|
|
139
|
+
*/
|
|
140
|
+
function toBackendSymbolWithMarket(displaySymbol, marketPrefix, hip3Assets) {
|
|
141
|
+
const availableMarkets = hip3Assets.get(displaySymbol);
|
|
142
|
+
if (!availableMarkets || availableMarkets.length === 0) {
|
|
143
|
+
// Not a HIP-3 asset, return as-is or with prefix if provided
|
|
144
|
+
return marketPrefix ? `${marketPrefix}:${displaySymbol}` : displaySymbol;
|
|
145
|
+
}
|
|
146
|
+
if (marketPrefix) {
|
|
147
|
+
// Find the market with the specified prefix
|
|
148
|
+
const targetMarket = availableMarkets.find((market) => market.toLowerCase().startsWith(`${marketPrefix.toLowerCase()}:`));
|
|
149
|
+
if (targetMarket) {
|
|
150
|
+
return targetMarket;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// No prefix specified or not found, return null to force explicit market selection
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get all available markets for a display symbol.
|
|
158
|
+
* @param displaySymbol e.g., "TSLA"
|
|
159
|
+
* @param hip3Assets map of display -> all full market names
|
|
160
|
+
* @returns Array of full market names, e.g., ["xyz:TSLA", "flx:TSLA"]
|
|
82
161
|
*/
|
|
83
|
-
function
|
|
162
|
+
function getAvailableMarkets(displaySymbol, hip3Assets) {
|
|
84
163
|
var _a;
|
|
85
|
-
return (_a =
|
|
164
|
+
return (_a = hip3Assets.get(displaySymbol)) !== null && _a !== void 0 ? _a : [];
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Extract the market prefix from a full market name.
|
|
168
|
+
* @param fullSymbol e.g., "xyz:TSLA"
|
|
169
|
+
* @returns The prefix (e.g., "xyz") or undefined if no prefix
|
|
170
|
+
*/
|
|
171
|
+
function getMarketPrefix(fullSymbol) {
|
|
172
|
+
const parts = fullSymbol.split(':');
|
|
173
|
+
return parts.length > 1 ? parts[0] : undefined;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Check if a symbol is a HIP-3 market (has a prefix).
|
|
177
|
+
* @param symbol e.g., "xyz:TSLA" or "TSLA"
|
|
178
|
+
* @returns true if the symbol has a market prefix
|
|
179
|
+
*/
|
|
180
|
+
function isHip3Market(symbol) {
|
|
181
|
+
return symbol.includes(':');
|
|
86
182
|
}
|
|
87
183
|
|
|
88
184
|
const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
@@ -99,7 +195,8 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
99
195
|
try {
|
|
100
196
|
const message = JSON.parse(event.data);
|
|
101
197
|
// Handle subscription responses (only if they don't have channel data)
|
|
102
|
-
if (('success' in message || 'error' in message) &&
|
|
198
|
+
if (('success' in message || 'error' in message) &&
|
|
199
|
+
!('channel' in message)) {
|
|
103
200
|
if (message.error) {
|
|
104
201
|
setLastError(message.error);
|
|
105
202
|
}
|
|
@@ -118,12 +215,21 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
118
215
|
switch (dataMessage.channel) {
|
|
119
216
|
case 'trade-histories':
|
|
120
217
|
{
|
|
218
|
+
const mapAsset = (a) => {
|
|
219
|
+
var _a, _b;
|
|
220
|
+
const extractedPrefix = getMarketPrefix(a.coin);
|
|
221
|
+
return {
|
|
222
|
+
...a,
|
|
223
|
+
coin: toDisplaySymbol(a.coin),
|
|
224
|
+
marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
|
|
225
|
+
};
|
|
226
|
+
};
|
|
121
227
|
const list = dataMessage.data.map((item) => {
|
|
122
228
|
var _a, _b;
|
|
123
229
|
return ({
|
|
124
230
|
...item,
|
|
125
|
-
closedLongAssets: item.closedLongAssets.map(
|
|
126
|
-
closedShortAssets: item.closedShortAssets.map(
|
|
231
|
+
closedLongAssets: item.closedLongAssets.map(mapAsset),
|
|
232
|
+
closedShortAssets: item.closedShortAssets.map(mapAsset),
|
|
127
233
|
positionLongAssets: (_a = item.positionLongAssets) === null || _a === void 0 ? void 0 : _a.map((a) => toDisplaySymbol(a)),
|
|
128
234
|
positionShortAssets: (_b = item.positionShortAssets) === null || _b === void 0 ? void 0 : _b.map((a) => toDisplaySymbol(a)),
|
|
129
235
|
});
|
|
@@ -133,10 +239,19 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
133
239
|
break;
|
|
134
240
|
case 'open-positions':
|
|
135
241
|
{
|
|
242
|
+
const enrichAsset = (a) => {
|
|
243
|
+
var _a, _b;
|
|
244
|
+
const extractedPrefix = getMarketPrefix(a.coin);
|
|
245
|
+
return {
|
|
246
|
+
...a,
|
|
247
|
+
coin: toDisplaySymbol(a.coin),
|
|
248
|
+
marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
|
|
249
|
+
};
|
|
250
|
+
};
|
|
136
251
|
const list = dataMessage.data.map((pos) => ({
|
|
137
252
|
...pos,
|
|
138
|
-
longAssets: pos.longAssets.map(
|
|
139
|
-
shortAssets: pos.shortAssets.map(
|
|
253
|
+
longAssets: pos.longAssets.map(enrichAsset),
|
|
254
|
+
shortAssets: pos.shortAssets.map(enrichAsset),
|
|
140
255
|
}));
|
|
141
256
|
setRawOpenPositions(list);
|
|
142
257
|
}
|
|
@@ -145,8 +260,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
145
260
|
{
|
|
146
261
|
const list = dataMessage.data.map((order) => ({
|
|
147
262
|
...order,
|
|
148
|
-
longAssets: order.longAssets.map((a) => ({
|
|
149
|
-
|
|
263
|
+
longAssets: order.longAssets.map((a) => ({
|
|
264
|
+
...a,
|
|
265
|
+
asset: toDisplaySymbol(a.asset),
|
|
266
|
+
})),
|
|
267
|
+
shortAssets: order.shortAssets.map((a) => ({
|
|
268
|
+
...a,
|
|
269
|
+
asset: toDisplaySymbol(a.asset),
|
|
270
|
+
})),
|
|
150
271
|
}));
|
|
151
272
|
setOpenOrders(list);
|
|
152
273
|
}
|
|
@@ -156,10 +277,20 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
156
277
|
break;
|
|
157
278
|
case 'twap-details':
|
|
158
279
|
{
|
|
280
|
+
const mapTwapAsset = (a) => {
|
|
281
|
+
var _a, _b, _c;
|
|
282
|
+
const extractedPrefix = getMarketPrefix(a.asset);
|
|
283
|
+
return {
|
|
284
|
+
...a,
|
|
285
|
+
asset: toDisplaySymbol(a.asset),
|
|
286
|
+
marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
|
|
287
|
+
collateralToken: (_c = a.collateralToken) !== null && _c !== void 0 ? _c : undefined,
|
|
288
|
+
};
|
|
289
|
+
};
|
|
159
290
|
const list = dataMessage.data.map((twap) => ({
|
|
160
291
|
...twap,
|
|
161
|
-
longAssets: twap.longAssets.map(
|
|
162
|
-
shortAssets: twap.shortAssets.map(
|
|
292
|
+
longAssets: twap.longAssets.map(mapTwapAsset),
|
|
293
|
+
shortAssets: twap.shortAssets.map(mapTwapAsset),
|
|
163
294
|
}));
|
|
164
295
|
setTwapDetails(list);
|
|
165
296
|
}
|
|
@@ -172,8 +303,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
172
303
|
const md = dataMessage.data;
|
|
173
304
|
const mapGroup = (g) => ({
|
|
174
305
|
...g,
|
|
175
|
-
longAssets: g.longAssets.map((a) => ({
|
|
176
|
-
|
|
306
|
+
longAssets: g.longAssets.map((a) => ({
|
|
307
|
+
...a,
|
|
308
|
+
asset: toDisplaySymbol(a.asset),
|
|
309
|
+
})),
|
|
310
|
+
shortAssets: g.shortAssets.map((a) => ({
|
|
311
|
+
...a,
|
|
312
|
+
asset: toDisplaySymbol(a.asset),
|
|
313
|
+
})),
|
|
177
314
|
});
|
|
178
315
|
const mapped = {
|
|
179
316
|
active: md.active.map(mapGroup),
|
|
@@ -191,7 +328,15 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
191
328
|
catch (error) {
|
|
192
329
|
setLastError(`Failed to parse message: ${error instanceof Error ? error.message : String(error)}`);
|
|
193
330
|
}
|
|
194
|
-
}, [
|
|
331
|
+
}, [
|
|
332
|
+
setTradeHistories,
|
|
333
|
+
setRawOpenPositions,
|
|
334
|
+
setOpenOrders,
|
|
335
|
+
setAccountSummary,
|
|
336
|
+
setTwapDetails,
|
|
337
|
+
setNotifications,
|
|
338
|
+
setMarketData,
|
|
339
|
+
]);
|
|
195
340
|
const connect = useCallback(() => {
|
|
196
341
|
if (!enabled || !wsUrl)
|
|
197
342
|
return;
|
|
@@ -260,7 +405,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
260
405
|
'open-orders',
|
|
261
406
|
'twap-details',
|
|
262
407
|
'fills-checkpoint',
|
|
263
|
-
'notifications'
|
|
408
|
+
'notifications',
|
|
264
409
|
];
|
|
265
410
|
const globalChannels = ['market-data'];
|
|
266
411
|
if (address && address !== lastSubscribedAddress) {
|
|
@@ -269,14 +414,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
269
414
|
sendMessage(JSON.stringify({
|
|
270
415
|
action: 'unsubscribe',
|
|
271
416
|
address: lastSubscribedAddress,
|
|
272
|
-
channels: addressSpecificChannels
|
|
417
|
+
channels: addressSpecificChannels,
|
|
273
418
|
}));
|
|
274
419
|
}
|
|
275
420
|
// Subscribe to all channels (global + address-specific)
|
|
276
421
|
sendMessage(JSON.stringify({
|
|
277
422
|
action: 'subscribe',
|
|
278
423
|
address: address,
|
|
279
|
-
channels: [...globalChannels, ...addressSpecificChannels]
|
|
424
|
+
channels: [...globalChannels, ...addressSpecificChannels],
|
|
280
425
|
}));
|
|
281
426
|
setLastSubscribedAddress(address);
|
|
282
427
|
setLastError(null);
|
|
@@ -286,7 +431,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
286
431
|
sendMessage(JSON.stringify({
|
|
287
432
|
action: 'unsubscribe',
|
|
288
433
|
address: lastSubscribedAddress,
|
|
289
|
-
channels: addressSpecificChannels
|
|
434
|
+
channels: addressSpecificChannels,
|
|
290
435
|
}));
|
|
291
436
|
setLastSubscribedAddress(null);
|
|
292
437
|
}
|
|
@@ -294,7 +439,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
294
439
|
// If no address but connected, subscribe to global channels only
|
|
295
440
|
sendMessage(JSON.stringify({
|
|
296
441
|
action: 'subscribe',
|
|
297
|
-
channels: globalChannels
|
|
442
|
+
channels: globalChannels,
|
|
298
443
|
}));
|
|
299
444
|
}
|
|
300
445
|
}, [isConnected, address, lastSubscribedAddress, sendMessage]);
|
|
@@ -317,11 +462,14 @@ const useHyperliquidData = create((set, get) => ({
|
|
|
317
462
|
finalAssetContexts: null,
|
|
318
463
|
finalAtOICaps: null,
|
|
319
464
|
aggregatedClearingHouseState: null,
|
|
465
|
+
rawClearinghouseStates: null,
|
|
320
466
|
perpMetaAssets: null,
|
|
321
|
-
|
|
467
|
+
allPerpMetaAssets: null,
|
|
468
|
+
hip3Assets: new Map(),
|
|
469
|
+
hip3MarketPrefixes: new Map(),
|
|
322
470
|
setAllMids: (value) => set({ allMids: value }),
|
|
323
471
|
setActiveAssetData: (value) => set((state) => ({
|
|
324
|
-
activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value
|
|
472
|
+
activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value,
|
|
325
473
|
})),
|
|
326
474
|
deleteActiveAssetData: (key) => {
|
|
327
475
|
set((state) => {
|
|
@@ -356,13 +504,16 @@ const useHyperliquidData = create((set, get) => ({
|
|
|
356
504
|
activeAssetData: {
|
|
357
505
|
...state.activeAssetData,
|
|
358
506
|
[key]: value,
|
|
359
|
-
}
|
|
507
|
+
},
|
|
360
508
|
})),
|
|
361
509
|
setFinalAssetContexts: (value) => set({ finalAssetContexts: value }),
|
|
362
510
|
setFinalAtOICaps: (value) => set({ finalAtOICaps: value }),
|
|
363
511
|
setAggregatedClearingHouseState: (value) => set({ aggregatedClearingHouseState: value }),
|
|
512
|
+
setRawClearinghouseStates: (value) => set({ rawClearinghouseStates: value }),
|
|
364
513
|
setPerpMetaAssets: (value) => set({ perpMetaAssets: value }),
|
|
365
|
-
|
|
514
|
+
setAllPerpMetaAssets: (value) => set({ allPerpMetaAssets: value }),
|
|
515
|
+
setHip3Assets: (value) => set({ hip3Assets: value }),
|
|
516
|
+
setHip3MarketPrefixes: (value) => set({ hip3MarketPrefixes: value }),
|
|
366
517
|
}));
|
|
367
518
|
|
|
368
519
|
/**
|
|
@@ -624,8 +775,9 @@ const useUserSelection$1 = create((set, get) => ({
|
|
|
624
775
|
resetToDefaults: () => set((prev) => ({ ...prev, ...DEFAULT_STATE })),
|
|
625
776
|
}));
|
|
626
777
|
|
|
627
|
-
const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
628
|
-
const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState, } = useHyperliquidData();
|
|
778
|
+
const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }) => {
|
|
779
|
+
const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState, setRawClearinghouseStates, } = useHyperliquidData();
|
|
780
|
+
const { setSpotState } = useUserData();
|
|
629
781
|
const { candleInterval } = useUserSelection$1();
|
|
630
782
|
const longTokens = useUserSelection$1((s) => s.longTokens);
|
|
631
783
|
const shortTokens = useUserSelection$1((s) => s.shortTokens);
|
|
@@ -644,9 +796,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
644
796
|
try {
|
|
645
797
|
const message = JSON.parse(event.data);
|
|
646
798
|
// Handle subscription responses
|
|
647
|
-
if (
|
|
799
|
+
if ('success' in message || 'error' in message) {
|
|
648
800
|
if (message.error) {
|
|
649
|
-
console.error(
|
|
801
|
+
console.error('[HyperLiquid WS] Subscription error:', message.error);
|
|
650
802
|
setLastError(message.error);
|
|
651
803
|
}
|
|
652
804
|
else {
|
|
@@ -655,30 +807,52 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
655
807
|
return;
|
|
656
808
|
}
|
|
657
809
|
// Handle channel data messages
|
|
658
|
-
if (
|
|
810
|
+
if ('channel' in message && 'data' in message) {
|
|
659
811
|
const response = message;
|
|
660
812
|
switch (response.channel) {
|
|
661
|
-
case
|
|
813
|
+
case 'userFills':
|
|
814
|
+
{
|
|
815
|
+
const maybePromise = onUserFills === null || onUserFills === void 0 ? void 0 : onUserFills();
|
|
816
|
+
if (maybePromise instanceof Promise) {
|
|
817
|
+
maybePromise.catch((err) => console.error('[HyperLiquid WS] userFills callback error', err));
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
break;
|
|
821
|
+
case 'webData3':
|
|
662
822
|
const webData3 = response.data;
|
|
663
823
|
// finalAssetContexts now sourced from allDexsAssetCtxs channel
|
|
664
824
|
const finalAtOICaps = webData3.perpDexStates.flatMap((dex) => dex.perpsAtOpenInterestCap);
|
|
665
825
|
setFinalAtOICaps(finalAtOICaps);
|
|
666
826
|
break;
|
|
667
|
-
case
|
|
827
|
+
case 'allDexsAssetCtxs':
|
|
668
828
|
{
|
|
669
829
|
const data = response.data;
|
|
670
|
-
|
|
830
|
+
// Filter out hyna to match perpMetaAssets filtering
|
|
831
|
+
const FILTERED_DEX_PREFIXES = ['hyna'];
|
|
832
|
+
const filtered = (data.ctxs || [])
|
|
833
|
+
.filter(([prefix]) => !FILTERED_DEX_PREFIXES.includes((prefix || '').toLowerCase()))
|
|
834
|
+
.sort((a, b) => {
|
|
835
|
+
// Sort to match perpMetaAssets order: default market first, then alphabetically
|
|
836
|
+
const prefixA = a[0] || '';
|
|
837
|
+
const prefixB = b[0] || '';
|
|
838
|
+
if (prefixA === '' && prefixB !== '')
|
|
839
|
+
return -1;
|
|
840
|
+
if (prefixA !== '' && prefixB === '')
|
|
841
|
+
return 1;
|
|
842
|
+
return prefixA.localeCompare(prefixB);
|
|
843
|
+
});
|
|
844
|
+
const finalAssetContexts = filtered.flatMap(([, ctxs]) => ctxs || []);
|
|
671
845
|
setFinalAssetContexts(finalAssetContexts);
|
|
672
846
|
}
|
|
673
847
|
break;
|
|
674
|
-
case
|
|
848
|
+
case 'allDexsClearinghouseState':
|
|
675
849
|
{
|
|
676
850
|
const data = response.data;
|
|
677
851
|
const states = (data.clearinghouseStates || [])
|
|
678
852
|
.map(([, s]) => s)
|
|
679
853
|
.filter(Boolean);
|
|
680
|
-
const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v ||
|
|
681
|
-
const toStr = (n) => Number.isFinite(n) ? n.toString() :
|
|
854
|
+
const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || '0') || 0), 0);
|
|
855
|
+
const toStr = (n) => Number.isFinite(n) ? n.toString() : '0';
|
|
682
856
|
const assetPositions = states.flatMap((s) => s.assetPositions || []);
|
|
683
857
|
const crossMaintenanceMarginUsed = toStr(sum(states.map((s) => s.crossMaintenanceMarginUsed)));
|
|
684
858
|
const crossMarginSummary = {
|
|
@@ -704,22 +878,41 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
704
878
|
withdrawable,
|
|
705
879
|
};
|
|
706
880
|
setAggregatedClearingHouseState(aggregatedClearingHouseState);
|
|
881
|
+
setRawClearinghouseStates(data.clearinghouseStates || null);
|
|
707
882
|
}
|
|
708
883
|
break;
|
|
709
|
-
case
|
|
884
|
+
case 'allMids':
|
|
710
885
|
{
|
|
711
886
|
const data = response.data;
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
887
|
+
// Keep BOTH normalized prefixed keys AND display symbol keys
|
|
888
|
+
// This ensures xyz:TSLA and flx:TSLA are stored separately,
|
|
889
|
+
// while also maintaining backward compatibility with non-prefixed lookups
|
|
890
|
+
const mids = {};
|
|
891
|
+
Object.entries(data.mids || {}).forEach(([k, v]) => {
|
|
892
|
+
// Normalize prefixed keys to lowercase prefix (e.g., "XYZ:TSLA" -> "xyz:TSLA")
|
|
893
|
+
// This matches how we look up tokens in the SDK
|
|
894
|
+
let normalizedKey = k;
|
|
895
|
+
if (k.includes(':')) {
|
|
896
|
+
const [prefix, ...rest] = k.split(':');
|
|
897
|
+
normalizedKey = `${prefix.toLowerCase()}:${rest.join(':')}`;
|
|
898
|
+
}
|
|
899
|
+
// Store with normalized key
|
|
900
|
+
mids[normalizedKey] = v;
|
|
901
|
+
// Also store with original key for backward compatibility
|
|
902
|
+
if (k !== normalizedKey) {
|
|
903
|
+
mids[k] = v;
|
|
904
|
+
}
|
|
905
|
+
// Also store with display symbol for backward compatibility
|
|
906
|
+
const displayKey = toDisplaySymbol(k);
|
|
907
|
+
// Only set display key if it doesn't already exist (avoid overwriting market-specific prices)
|
|
908
|
+
if (!(displayKey in mids)) {
|
|
909
|
+
mids[displayKey] = v;
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
setAllMids({ mids });
|
|
720
913
|
}
|
|
721
914
|
break;
|
|
722
|
-
case
|
|
915
|
+
case 'activeAssetData':
|
|
723
916
|
{
|
|
724
917
|
const assetData = response.data;
|
|
725
918
|
const symbol = toDisplaySymbol(assetData.coin);
|
|
@@ -730,14 +923,22 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
730
923
|
upsertActiveAssetData(symbol, normalized);
|
|
731
924
|
}
|
|
732
925
|
break;
|
|
733
|
-
case
|
|
926
|
+
case 'candle':
|
|
734
927
|
{
|
|
735
928
|
const candleDataItem = response.data;
|
|
736
|
-
const symbol = toDisplaySymbol(candleDataItem.s ||
|
|
929
|
+
const symbol = toDisplaySymbol(candleDataItem.s || '');
|
|
737
930
|
const normalized = { ...candleDataItem, s: symbol };
|
|
738
931
|
addCandleData(symbol, normalized);
|
|
739
932
|
}
|
|
740
933
|
break;
|
|
934
|
+
case 'spotState':
|
|
935
|
+
{
|
|
936
|
+
const spotStateData = response.data;
|
|
937
|
+
if (spotStateData === null || spotStateData === void 0 ? void 0 : spotStateData.spotState) {
|
|
938
|
+
setSpotState(spotStateData.spotState);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
break;
|
|
741
942
|
default:
|
|
742
943
|
console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
|
|
743
944
|
}
|
|
@@ -745,7 +946,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
745
946
|
}
|
|
746
947
|
catch (error) {
|
|
747
948
|
const errorMessage = `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`;
|
|
748
|
-
console.error(
|
|
949
|
+
console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
|
|
749
950
|
setLastError(errorMessage);
|
|
750
951
|
}
|
|
751
952
|
}, [
|
|
@@ -755,6 +956,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
755
956
|
setFinalAssetContexts,
|
|
756
957
|
setFinalAtOICaps,
|
|
757
958
|
setAggregatedClearingHouseState,
|
|
959
|
+
setRawClearinghouseStates,
|
|
960
|
+
setSpotState,
|
|
961
|
+
onUserFills,
|
|
758
962
|
]);
|
|
759
963
|
const connect = useCallback(() => {
|
|
760
964
|
if (!enabled)
|
|
@@ -785,7 +989,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
785
989
|
if (!manualCloseRef.current && reconnectAttemptsRef.current < 5) {
|
|
786
990
|
reconnectAttemptsRef.current += 1;
|
|
787
991
|
if (reconnectAttemptsRef.current === 5) {
|
|
788
|
-
console.error(
|
|
992
|
+
console.error('[HyperLiquid WS] Reconnection stopped after 5 attempts');
|
|
789
993
|
}
|
|
790
994
|
setTimeout(() => connect(), 3000);
|
|
791
995
|
}
|
|
@@ -853,6 +1057,17 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
853
1057
|
},
|
|
854
1058
|
};
|
|
855
1059
|
sendJsonMessage(unsubscribeMessage);
|
|
1060
|
+
// Unsubscribe from spotState for previous address
|
|
1061
|
+
if (subscribedAddress !== DEFAULT_ADDRESS) {
|
|
1062
|
+
const unsubscribeSpotState = {
|
|
1063
|
+
method: 'unsubscribe',
|
|
1064
|
+
subscription: {
|
|
1065
|
+
type: 'spotState',
|
|
1066
|
+
user: subscribedAddress,
|
|
1067
|
+
},
|
|
1068
|
+
};
|
|
1069
|
+
sendJsonMessage(unsubscribeSpotState);
|
|
1070
|
+
}
|
|
856
1071
|
const unsubscribeAllDexsClearinghouseState = {
|
|
857
1072
|
method: "unsubscribe",
|
|
858
1073
|
subscription: {
|
|
@@ -861,6 +1076,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
861
1076
|
},
|
|
862
1077
|
};
|
|
863
1078
|
sendJsonMessage(unsubscribeAllDexsClearinghouseState);
|
|
1079
|
+
const unsubscribeUserFills = {
|
|
1080
|
+
method: 'unsubscribe',
|
|
1081
|
+
subscription: {
|
|
1082
|
+
type: 'userFills',
|
|
1083
|
+
user: subscribedAddress,
|
|
1084
|
+
},
|
|
1085
|
+
};
|
|
1086
|
+
sendJsonMessage(unsubscribeUserFills);
|
|
864
1087
|
}
|
|
865
1088
|
const subscribeWebData3 = {
|
|
866
1089
|
method: "subscribe",
|
|
@@ -892,15 +1115,38 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
892
1115
|
type: "allDexsAssetCtxs",
|
|
893
1116
|
},
|
|
894
1117
|
};
|
|
1118
|
+
const subscribeUserFills = {
|
|
1119
|
+
method: 'subscribe',
|
|
1120
|
+
subscription: {
|
|
1121
|
+
type: 'userFills',
|
|
1122
|
+
user: userAddress,
|
|
1123
|
+
},
|
|
1124
|
+
};
|
|
895
1125
|
sendJsonMessage(subscribeWebData3);
|
|
896
1126
|
sendJsonMessage(subscribeAllDexsClearinghouseState);
|
|
897
1127
|
sendJsonMessage(subscribeAllMids);
|
|
898
1128
|
sendJsonMessage(subscribeAllDexsAssetCtxs);
|
|
1129
|
+
sendJsonMessage(subscribeUserFills);
|
|
1130
|
+
// Subscribe to spotState for real-time spot balances (USDH, USDC, etc.)
|
|
1131
|
+
// Only subscribe if we have a real user address (not the default)
|
|
1132
|
+
if (userAddress !== DEFAULT_ADDRESS) {
|
|
1133
|
+
const subscribeSpotState = {
|
|
1134
|
+
method: 'subscribe',
|
|
1135
|
+
subscription: {
|
|
1136
|
+
type: 'spotState',
|
|
1137
|
+
user: userAddress,
|
|
1138
|
+
},
|
|
1139
|
+
};
|
|
1140
|
+
sendJsonMessage(subscribeSpotState);
|
|
1141
|
+
}
|
|
899
1142
|
setSubscribedAddress(userAddress);
|
|
900
1143
|
// Clear previous data when address changes
|
|
901
1144
|
if (subscribedAddress && subscribedAddress !== userAddress) {
|
|
902
1145
|
// clear aggregatedClearingHouseState
|
|
903
1146
|
setAggregatedClearingHouseState(null);
|
|
1147
|
+
setRawClearinghouseStates(null);
|
|
1148
|
+
// clear spotState
|
|
1149
|
+
setSpotState(null);
|
|
904
1150
|
}
|
|
905
1151
|
}, [
|
|
906
1152
|
isConnected,
|
|
@@ -908,6 +1154,8 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
908
1154
|
subscribedAddress,
|
|
909
1155
|
sendJsonMessage,
|
|
910
1156
|
setAggregatedClearingHouseState,
|
|
1157
|
+
setRawClearinghouseStates,
|
|
1158
|
+
setSpotState,
|
|
911
1159
|
]);
|
|
912
1160
|
// Handle token subscriptions for activeAssetData
|
|
913
1161
|
useEffect(() => {
|
|
@@ -1104,20 +1352,112 @@ const useAccountSummary = () => {
|
|
|
1104
1352
|
return { data: calculated, isLoading };
|
|
1105
1353
|
};
|
|
1106
1354
|
|
|
1355
|
+
function findAssetMeta$4(coinName, perpMetaAssets, knownPrefix, desiredCollateral) {
|
|
1356
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
1357
|
+
if (!perpMetaAssets) {
|
|
1358
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
1359
|
+
}
|
|
1360
|
+
if (desiredCollateral) {
|
|
1361
|
+
const collateralMatch = perpMetaAssets.find((a) => a.name === coinName && a.collateralToken === desiredCollateral);
|
|
1362
|
+
if (collateralMatch) {
|
|
1363
|
+
return {
|
|
1364
|
+
collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
1365
|
+
marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
if (coinName.includes(':')) {
|
|
1370
|
+
const [prefix, symbol] = coinName.split(':');
|
|
1371
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
1372
|
+
var _a;
|
|
1373
|
+
return a.name === symbol &&
|
|
1374
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
|
|
1375
|
+
});
|
|
1376
|
+
if (exactMatch) {
|
|
1377
|
+
return {
|
|
1378
|
+
collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
1379
|
+
marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
if (knownPrefix) {
|
|
1384
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
1385
|
+
var _a;
|
|
1386
|
+
return a.name === coinName &&
|
|
1387
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
|
|
1388
|
+
});
|
|
1389
|
+
if (exactMatch) {
|
|
1390
|
+
return {
|
|
1391
|
+
collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
|
|
1392
|
+
marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
const exactMatch = perpMetaAssets.find((a) => a.name === coinName && !a.marketPrefix);
|
|
1397
|
+
if (exactMatch) {
|
|
1398
|
+
return {
|
|
1399
|
+
collateralToken: (_g = exactMatch.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
|
|
1400
|
+
marketPrefix: (_h = exactMatch.marketPrefix) !== null && _h !== void 0 ? _h : null,
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
const hip3Matches = perpMetaAssets.filter((a) => a.name === coinName && a.marketPrefix);
|
|
1404
|
+
if (hip3Matches.length > 0) {
|
|
1405
|
+
if (desiredCollateral) {
|
|
1406
|
+
const collateralMatch = hip3Matches.find((a) => a.collateralToken === desiredCollateral);
|
|
1407
|
+
if (collateralMatch) {
|
|
1408
|
+
return {
|
|
1409
|
+
collateralToken: (_j = collateralMatch.collateralToken) !== null && _j !== void 0 ? _j : 'USDC',
|
|
1410
|
+
marketPrefix: (_k = collateralMatch.marketPrefix) !== null && _k !== void 0 ? _k : null,
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
const usdHMatch = hip3Matches.find((a) => a.collateralToken === 'USDH');
|
|
1415
|
+
const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Matches[0];
|
|
1416
|
+
return {
|
|
1417
|
+
collateralToken: (_l = chosen.collateralToken) !== null && _l !== void 0 ? _l : 'USDC',
|
|
1418
|
+
marketPrefix: (_m = chosen.marketPrefix) !== null && _m !== void 0 ? _m : null,
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
1422
|
+
}
|
|
1423
|
+
function enrichTradeHistoryAssets(assets, perpMetaAssets) {
|
|
1424
|
+
return assets.map((asset) => {
|
|
1425
|
+
var _a;
|
|
1426
|
+
if (asset.marketPrefix && asset.collateralToken) {
|
|
1427
|
+
return asset;
|
|
1428
|
+
}
|
|
1429
|
+
const meta = findAssetMeta$4(asset.coin, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
|
|
1430
|
+
return {
|
|
1431
|
+
...asset,
|
|
1432
|
+
marketPrefix: asset.marketPrefix || meta.marketPrefix,
|
|
1433
|
+
collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
|
|
1434
|
+
};
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
function enrichTradeHistories(histories, perpMetaAssets) {
|
|
1438
|
+
return histories.map((history) => ({
|
|
1439
|
+
...history,
|
|
1440
|
+
closedLongAssets: enrichTradeHistoryAssets(history.closedLongAssets, perpMetaAssets),
|
|
1441
|
+
closedShortAssets: enrichTradeHistoryAssets(history.closedShortAssets, perpMetaAssets),
|
|
1442
|
+
}));
|
|
1443
|
+
}
|
|
1107
1444
|
const useTradeHistories = () => {
|
|
1108
1445
|
const context = useContext(PearHyperliquidContext);
|
|
1109
1446
|
if (!context) {
|
|
1110
1447
|
throw new Error('useTradeHistories must be used within a PearHyperliquidProvider');
|
|
1111
1448
|
}
|
|
1112
1449
|
const tradeHistories = useUserData((state) => state.tradeHistories);
|
|
1450
|
+
const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
|
|
1113
1451
|
const isLoading = useMemo(() => {
|
|
1114
1452
|
return tradeHistories === null && context.isConnected;
|
|
1115
1453
|
}, [tradeHistories, context.isConnected]);
|
|
1116
|
-
|
|
1454
|
+
const enrichedTradeHistories = useMemo(() => {
|
|
1455
|
+
if (!tradeHistories)
|
|
1456
|
+
return null;
|
|
1457
|
+
return enrichTradeHistories(tradeHistories, allPerpMetaAssets);
|
|
1458
|
+
}, [tradeHistories, allPerpMetaAssets]);
|
|
1459
|
+
return { data: enrichedTradeHistories, isLoading };
|
|
1117
1460
|
};
|
|
1118
|
-
/**
|
|
1119
|
-
* Hook to access open orders with loading state
|
|
1120
|
-
*/
|
|
1121
1461
|
const useOpenOrders = () => {
|
|
1122
1462
|
const context = useContext(PearHyperliquidContext);
|
|
1123
1463
|
if (!context) {
|
|
@@ -1146,21 +1486,51 @@ const useWebData = () => {
|
|
|
1146
1486
|
const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
1147
1487
|
const aggregatedClearinghouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
1148
1488
|
const finalAtOICaps = useHyperliquidData((state) => state.finalAtOICaps);
|
|
1149
|
-
const hip3Assets = useHyperliquidData((state) => state.
|
|
1489
|
+
const hip3Assets = useHyperliquidData((state) => state.hip3Assets);
|
|
1490
|
+
const hip3MarketPrefixes = useHyperliquidData((state) => state.hip3MarketPrefixes);
|
|
1150
1491
|
let marketDataBySymbol = {};
|
|
1151
1492
|
if (finalAssetContexts && perpMetaAssets) {
|
|
1152
1493
|
const result = {};
|
|
1494
|
+
// Build a map of display name -> asset context index (for unique display names)
|
|
1495
|
+
const displayNameToContextIndex = new Map();
|
|
1496
|
+
const seenNames = new Set();
|
|
1497
|
+
let contextIndex = 0;
|
|
1498
|
+
// First pass: map unique display names to their context index
|
|
1153
1499
|
for (let index = 0; index < perpMetaAssets.length; index++) {
|
|
1154
1500
|
const name = perpMetaAssets[index].name;
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1501
|
+
if (!seenNames.has(name)) {
|
|
1502
|
+
seenNames.add(name);
|
|
1503
|
+
if (contextIndex < finalAssetContexts.length) {
|
|
1504
|
+
displayNameToContextIndex.set(name, contextIndex);
|
|
1505
|
+
contextIndex++;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
// Second pass: create nested entries for all market variants
|
|
1510
|
+
for (let index = 0; index < perpMetaAssets.length; index++) {
|
|
1511
|
+
const universeAsset = perpMetaAssets[index];
|
|
1512
|
+
const displayName = universeAsset.name;
|
|
1513
|
+
const marketPrefix = universeAsset.marketPrefix;
|
|
1514
|
+
const ctxIndex = displayNameToContextIndex.get(displayName);
|
|
1515
|
+
if (ctxIndex !== undefined) {
|
|
1516
|
+
const assetContext = finalAssetContexts[ctxIndex];
|
|
1517
|
+
// Initialize the symbol entry if it doesn't exist
|
|
1518
|
+
if (!result[displayName]) {
|
|
1519
|
+
result[displayName] = {};
|
|
1520
|
+
}
|
|
1521
|
+
// Use marketPrefix as key for HIP-3 assets, "default" for regular assets
|
|
1522
|
+
const variantKey = marketPrefix || 'default';
|
|
1523
|
+
result[displayName][variantKey] = {
|
|
1524
|
+
asset: assetContext,
|
|
1525
|
+
universe: universeAsset,
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1159
1528
|
}
|
|
1160
1529
|
marketDataBySymbol = result;
|
|
1161
1530
|
}
|
|
1162
1531
|
return {
|
|
1163
1532
|
hip3Assets,
|
|
1533
|
+
hip3MarketPrefixes,
|
|
1164
1534
|
clearinghouseState: aggregatedClearinghouseState,
|
|
1165
1535
|
perpsAtOpenInterestCap: finalAtOICaps,
|
|
1166
1536
|
marketDataBySymbol,
|
|
@@ -1175,19 +1545,30 @@ const useWebData = () => {
|
|
|
1175
1545
|
class TokenMetadataExtractor {
|
|
1176
1546
|
/**
|
|
1177
1547
|
* Extracts comprehensive token metadata
|
|
1178
|
-
* @param symbol - Token symbol
|
|
1548
|
+
* @param symbol - Token symbol (base symbol without prefix, e.g., "TSLA")
|
|
1179
1549
|
* @param perpMetaAssets - Aggregated universe assets (flattened across dexes)
|
|
1180
1550
|
* @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
|
|
1181
1551
|
* @param allMids - AllMids data containing current prices
|
|
1182
1552
|
* @param activeAssetData - Optional active asset data containing leverage information
|
|
1553
|
+
* @param marketPrefix - Optional market prefix (e.g., "xyz", "flx") for HIP3 multi-market assets
|
|
1183
1554
|
* @returns TokenMetadata or null if token not found
|
|
1184
1555
|
*/
|
|
1185
|
-
static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
|
|
1556
|
+
static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketPrefix) {
|
|
1186
1557
|
if (!perpMetaAssets || !finalAssetContexts || !allMids) {
|
|
1187
1558
|
return null;
|
|
1188
1559
|
}
|
|
1189
1560
|
// Find token index in aggregated universe
|
|
1190
|
-
|
|
1561
|
+
// For HIP3 assets, match both name AND marketPrefix
|
|
1562
|
+
const universeIndex = perpMetaAssets.findIndex((asset) => {
|
|
1563
|
+
if (asset.name !== symbol)
|
|
1564
|
+
return false;
|
|
1565
|
+
// If marketPrefix is specified, match it; otherwise match assets without prefix
|
|
1566
|
+
if (marketPrefix) {
|
|
1567
|
+
return asset.marketPrefix === marketPrefix;
|
|
1568
|
+
}
|
|
1569
|
+
// No prefix specified - match non-HIP3 asset (no marketPrefix) or first matching asset
|
|
1570
|
+
return !asset.marketPrefix;
|
|
1571
|
+
});
|
|
1191
1572
|
if (universeIndex === -1) {
|
|
1192
1573
|
return null;
|
|
1193
1574
|
}
|
|
@@ -1196,9 +1577,20 @@ class TokenMetadataExtractor {
|
|
|
1196
1577
|
if (!assetCtx) {
|
|
1197
1578
|
return null;
|
|
1198
1579
|
}
|
|
1199
|
-
// Get current price
|
|
1200
|
-
|
|
1201
|
-
const
|
|
1580
|
+
// Get current price - prefer assetCtx.midPx as it's already index-matched,
|
|
1581
|
+
// fall back to allMids lookup if midPx is null
|
|
1582
|
+
const prefixedKeyColon = marketPrefix ? `${marketPrefix}:${symbol}` : null;
|
|
1583
|
+
let currentPrice = 0;
|
|
1584
|
+
// Primary source: assetCtx.midPx (already properly indexed)
|
|
1585
|
+
if (assetCtx.midPx) {
|
|
1586
|
+
currentPrice = parseFloat(assetCtx.midPx);
|
|
1587
|
+
}
|
|
1588
|
+
// Fallback: allMids lookup with multiple key formats for HIP3 markets
|
|
1589
|
+
if (!currentPrice || isNaN(currentPrice)) {
|
|
1590
|
+
const currentPriceStr = (prefixedKeyColon && allMids.mids[prefixedKeyColon]) ||
|
|
1591
|
+
allMids.mids[symbol];
|
|
1592
|
+
currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
|
|
1593
|
+
}
|
|
1202
1594
|
// Get previous day price
|
|
1203
1595
|
const prevDayPrice = parseFloat(assetCtx.prevDayPx);
|
|
1204
1596
|
// Calculate 24h price change
|
|
@@ -1209,7 +1601,11 @@ class TokenMetadataExtractor {
|
|
|
1209
1601
|
const markPrice = parseFloat(assetCtx.markPx);
|
|
1210
1602
|
const oraclePrice = parseFloat(assetCtx.oraclePx);
|
|
1211
1603
|
// Extract leverage info from activeAssetData if available
|
|
1212
|
-
|
|
1604
|
+
// Try prefixed key first (e.g., "xyz:TSLA"), then fall back to plain symbol
|
|
1605
|
+
const activeDataKey = prefixedKeyColon && (activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[prefixedKeyColon])
|
|
1606
|
+
? prefixedKeyColon
|
|
1607
|
+
: symbol;
|
|
1608
|
+
const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[activeDataKey];
|
|
1213
1609
|
const leverage = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.leverage;
|
|
1214
1610
|
const maxTradeSzs = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.maxTradeSzs;
|
|
1215
1611
|
const availableToTrade = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.availableToTrade;
|
|
@@ -1227,21 +1623,27 @@ class TokenMetadataExtractor {
|
|
|
1227
1623
|
leverage,
|
|
1228
1624
|
maxTradeSzs,
|
|
1229
1625
|
availableToTrade,
|
|
1626
|
+
collateralToken: universeAsset.collateralToken,
|
|
1230
1627
|
};
|
|
1231
1628
|
}
|
|
1232
1629
|
/**
|
|
1233
1630
|
* Extracts metadata for multiple tokens
|
|
1234
|
-
* @param
|
|
1631
|
+
* @param tokens - Array of token objects with symbol and optional marketPrefix
|
|
1235
1632
|
* @param perpMetaAssets - Aggregated universe assets
|
|
1236
1633
|
* @param finalAssetContexts - Aggregated asset contexts
|
|
1237
1634
|
* @param allMids - AllMids data
|
|
1238
1635
|
* @param activeAssetData - Optional active asset data containing leverage information
|
|
1239
|
-
* @returns Record of
|
|
1636
|
+
* @returns Record of unique key to TokenMetadata. Key is "{prefix}:{symbol}" for HIP3 assets, or just "{symbol}" otherwise
|
|
1240
1637
|
*/
|
|
1241
|
-
static extractMultipleTokensMetadata(
|
|
1638
|
+
static extractMultipleTokensMetadata(tokens, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
|
|
1242
1639
|
const result = {};
|
|
1243
|
-
for (const
|
|
1244
|
-
|
|
1640
|
+
for (const token of tokens) {
|
|
1641
|
+
// Use a unique key that includes the prefix for HIP3 assets
|
|
1642
|
+
// This ensures xyz:TSLA and flx:TSLA get separate entries
|
|
1643
|
+
const resultKey = token.marketPrefix
|
|
1644
|
+
? `${token.marketPrefix}:${token.symbol}`
|
|
1645
|
+
: token.symbol;
|
|
1646
|
+
result[resultKey] = this.extractTokenMetadata(token.symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, token.marketPrefix);
|
|
1245
1647
|
}
|
|
1246
1648
|
return result;
|
|
1247
1649
|
}
|
|
@@ -1254,10 +1656,30 @@ class TokenMetadataExtractor {
|
|
|
1254
1656
|
static isTokenAvailable(symbol, perpMetaAssets) {
|
|
1255
1657
|
if (!perpMetaAssets)
|
|
1256
1658
|
return false;
|
|
1257
|
-
return perpMetaAssets.some(asset => asset.name === symbol);
|
|
1659
|
+
return perpMetaAssets.some((asset) => asset.name === symbol);
|
|
1258
1660
|
}
|
|
1259
1661
|
}
|
|
1260
1662
|
|
|
1663
|
+
/**
|
|
1664
|
+
* Parse a token string that may have a market prefix (e.g., "xyz:GOOGL" -> { prefix: "xyz", symbol: "GOOGL" })
|
|
1665
|
+
* This allows us to keep the full name (xyz:GOOGL) for URLs/tags while extracting just the symbol for SDK lookups.
|
|
1666
|
+
*/
|
|
1667
|
+
function parseTokenWithPrefix(token) {
|
|
1668
|
+
if (token.includes(":")) {
|
|
1669
|
+
const [prefix, ...rest] = token.split(":");
|
|
1670
|
+
const symbol = rest.join(":").toUpperCase();
|
|
1671
|
+
return {
|
|
1672
|
+
prefix: prefix.toLowerCase(),
|
|
1673
|
+
symbol,
|
|
1674
|
+
fullName: `${prefix.toLowerCase()}:${symbol}`,
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
return {
|
|
1678
|
+
prefix: null,
|
|
1679
|
+
symbol: token.toUpperCase(),
|
|
1680
|
+
fullName: token.toUpperCase(),
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1261
1683
|
const useTokenSelectionMetadataStore = create((set) => ({
|
|
1262
1684
|
isPriceDataReady: false,
|
|
1263
1685
|
isLoading: true,
|
|
@@ -1273,17 +1695,59 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1273
1695
|
maxLeverage: 0,
|
|
1274
1696
|
minMargin: 0,
|
|
1275
1697
|
leverageMatched: true,
|
|
1276
|
-
recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens }) => {
|
|
1277
|
-
const isPriceDataReady = !!(perpMetaAssets &&
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1698
|
+
recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens, }) => {
|
|
1699
|
+
const isPriceDataReady = !!(perpMetaAssets &&
|
|
1700
|
+
finalAssetContexts &&
|
|
1701
|
+
allMids);
|
|
1702
|
+
// Parse tokens - handle prefixed tokens like "xyz:GOOGL" by extracting the symbol and market prefix
|
|
1703
|
+
// The full name (xyz:GOOGL) is kept as the metadata key for UI consistency
|
|
1704
|
+
const parsedLongTokens = longTokens.map((t) => ({
|
|
1705
|
+
...t,
|
|
1706
|
+
parsed: parseTokenWithPrefix(t.symbol),
|
|
1707
|
+
}));
|
|
1708
|
+
const parsedShortTokens = shortTokens.map((t) => ({
|
|
1709
|
+
...t,
|
|
1710
|
+
parsed: parseTokenWithPrefix(t.symbol),
|
|
1711
|
+
}));
|
|
1712
|
+
// Extract base symbols with their market prefixes for SDK lookups
|
|
1713
|
+
// This ensures xyz:TSLA and flx:TSLA get different market data
|
|
1714
|
+
const longTokensForLookup = parsedLongTokens.map((t) => ({
|
|
1715
|
+
symbol: t.parsed.symbol,
|
|
1716
|
+
marketPrefix: t.parsed.prefix,
|
|
1717
|
+
}));
|
|
1718
|
+
const shortTokensForLookup = parsedShortTokens.map((t) => ({
|
|
1719
|
+
symbol: t.parsed.symbol,
|
|
1720
|
+
marketPrefix: t.parsed.prefix,
|
|
1721
|
+
}));
|
|
1722
|
+
// Also extract just the base symbols (without prefix) for lookups that don't support prefixes
|
|
1723
|
+
const longBaseSymbols = longTokensForLookup.map((t) => t.symbol);
|
|
1724
|
+
const shortBaseSymbols = shortTokensForLookup.map((t) => t.symbol);
|
|
1725
|
+
// Get metadata using base symbols with market prefix for proper market differentiation
|
|
1726
|
+
const longBaseMetadata = isPriceDataReady
|
|
1727
|
+
? TokenMetadataExtractor.extractMultipleTokensMetadata(longTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
|
|
1283
1728
|
: {};
|
|
1284
|
-
const
|
|
1285
|
-
? TokenMetadataExtractor.extractMultipleTokensMetadata(
|
|
1729
|
+
const shortBaseMetadata = isPriceDataReady
|
|
1730
|
+
? TokenMetadataExtractor.extractMultipleTokensMetadata(shortTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
|
|
1286
1731
|
: {};
|
|
1732
|
+
// Re-map metadata using original full names (with prefix) as keys for UI consistency
|
|
1733
|
+
// The extractor now keys by "{prefix}:{symbol}" for prefixed tokens, which matches our parsed.fullName
|
|
1734
|
+
const longTokensMetadata = {};
|
|
1735
|
+
parsedLongTokens.forEach((t) => {
|
|
1736
|
+
var _a;
|
|
1737
|
+
// Use the full name (e.g., "xyz:TSLA") as the lookup key since extractor uses the same format
|
|
1738
|
+
const lookupKey = t.parsed.prefix
|
|
1739
|
+
? `${t.parsed.prefix}:${t.parsed.symbol}`
|
|
1740
|
+
: t.parsed.symbol;
|
|
1741
|
+
longTokensMetadata[t.symbol] = (_a = longBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
|
|
1742
|
+
});
|
|
1743
|
+
const shortTokensMetadata = {};
|
|
1744
|
+
parsedShortTokens.forEach((t) => {
|
|
1745
|
+
var _a;
|
|
1746
|
+
const lookupKey = t.parsed.prefix
|
|
1747
|
+
? `${t.parsed.prefix}:${t.parsed.symbol}`
|
|
1748
|
+
: t.parsed.symbol;
|
|
1749
|
+
shortTokensMetadata[t.symbol] = (_a = shortBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
|
|
1750
|
+
});
|
|
1287
1751
|
// Determine loading state
|
|
1288
1752
|
const allTokens = [...longTokens, ...shortTokens];
|
|
1289
1753
|
const isLoading = (() => {
|
|
@@ -1291,26 +1755,33 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1291
1755
|
return true;
|
|
1292
1756
|
if (allTokens.length === 0)
|
|
1293
1757
|
return false;
|
|
1294
|
-
const allMetadata = {
|
|
1758
|
+
const allMetadata = {
|
|
1759
|
+
...longTokensMetadata,
|
|
1760
|
+
...shortTokensMetadata,
|
|
1761
|
+
};
|
|
1295
1762
|
return allTokens.some((token) => !allMetadata[token.symbol]);
|
|
1296
1763
|
})();
|
|
1297
1764
|
// Open interest and volume (from market data for matching asset basket)
|
|
1765
|
+
// Use base symbols (without prefix) for matching against market data
|
|
1298
1766
|
const { openInterest, volume } = (() => {
|
|
1299
1767
|
const empty = { openInterest: "0", volume: "0" };
|
|
1300
1768
|
if (!(marketData === null || marketData === void 0 ? void 0 : marketData.active) || (!longTokens.length && !shortTokens.length))
|
|
1301
1769
|
return empty;
|
|
1302
|
-
const selectedLong =
|
|
1303
|
-
const selectedShort =
|
|
1770
|
+
const selectedLong = longBaseSymbols.slice().sort();
|
|
1771
|
+
const selectedShort = shortBaseSymbols.slice().sort();
|
|
1304
1772
|
const match = marketData.active.find((item) => {
|
|
1305
1773
|
const longs = [...item.longAssets].sort();
|
|
1306
1774
|
const shorts = [...item.shortAssets].sort();
|
|
1307
|
-
if (longs.length !== selectedLong.length ||
|
|
1775
|
+
if (longs.length !== selectedLong.length ||
|
|
1776
|
+
shorts.length !== selectedShort.length)
|
|
1308
1777
|
return false;
|
|
1309
1778
|
const longsEqual = longs.every((s, i) => s.asset === selectedLong[i]);
|
|
1310
1779
|
const shortsEqual = shorts.every((s, i) => s.asset === selectedShort[i]);
|
|
1311
1780
|
return longsEqual && shortsEqual;
|
|
1312
1781
|
});
|
|
1313
|
-
return match
|
|
1782
|
+
return match
|
|
1783
|
+
? { openInterest: match.openInterest, volume: match.volume }
|
|
1784
|
+
: empty;
|
|
1314
1785
|
})();
|
|
1315
1786
|
// Price ratio (only when exactly one long and one short)
|
|
1316
1787
|
const { priceRatio, priceRatio24h } = (() => {
|
|
@@ -1390,17 +1861,27 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1390
1861
|
return totalFunding;
|
|
1391
1862
|
})();
|
|
1392
1863
|
// Max leverage (maximum across all tokens)
|
|
1864
|
+
// Use tokens with their market prefixes for proper lookup in perpMetaAssets
|
|
1393
1865
|
const maxLeverage = (() => {
|
|
1394
1866
|
if (!perpMetaAssets)
|
|
1395
1867
|
return 0;
|
|
1396
|
-
const
|
|
1397
|
-
|
|
1868
|
+
const allTokensForLookup = [
|
|
1869
|
+
...longTokensForLookup,
|
|
1870
|
+
...shortTokensForLookup,
|
|
1871
|
+
];
|
|
1872
|
+
if (allTokensForLookup.length === 0)
|
|
1398
1873
|
return 0;
|
|
1399
1874
|
let maxLev = 0;
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1875
|
+
allTokensForLookup.forEach(({ symbol, marketPrefix }) => {
|
|
1876
|
+
// Match by both name AND marketPrefix for HIP3 assets
|
|
1877
|
+
const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol &&
|
|
1878
|
+
(marketPrefix
|
|
1879
|
+
? u.marketPrefix === marketPrefix
|
|
1880
|
+
: !u.marketPrefix));
|
|
1881
|
+
// Fallback to just matching by name if no exact match
|
|
1882
|
+
const fallbackUniverse = tokenUniverse || perpMetaAssets.find((u) => u.name === symbol);
|
|
1883
|
+
if (fallbackUniverse === null || fallbackUniverse === void 0 ? void 0 : fallbackUniverse.maxLeverage)
|
|
1884
|
+
maxLev = Math.max(maxLev, fallbackUniverse.maxLeverage);
|
|
1404
1885
|
});
|
|
1405
1886
|
return maxLev;
|
|
1406
1887
|
})();
|
|
@@ -1412,7 +1893,10 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1412
1893
|
// Whether all tokens have matching leverage
|
|
1413
1894
|
const leverageMatched = (() => {
|
|
1414
1895
|
const allTokensArr = [...longTokens, ...shortTokens];
|
|
1415
|
-
const allMetadata = {
|
|
1896
|
+
const allMetadata = {
|
|
1897
|
+
...longTokensMetadata,
|
|
1898
|
+
...shortTokensMetadata,
|
|
1899
|
+
};
|
|
1416
1900
|
if (allTokensArr.length === 0)
|
|
1417
1901
|
return true;
|
|
1418
1902
|
const tokensWithLev = allTokensArr.filter((token) => { var _a; return (_a = allMetadata[token.symbol]) === null || _a === void 0 ? void 0 : _a.leverage; });
|
|
@@ -5597,8 +6081,8 @@ function addAuthInterceptors(params) {
|
|
|
5597
6081
|
/**
|
|
5598
6082
|
* Fetch historical candle data from HyperLiquid API
|
|
5599
6083
|
*/
|
|
5600
|
-
const fetchHistoricalCandles = async (coin, startTime, endTime, interval,
|
|
5601
|
-
const backendCoin = toBackendSymbol(coin,
|
|
6084
|
+
const fetchHistoricalCandles = async (coin, startTime, endTime, interval, hip3Assets) => {
|
|
6085
|
+
const backendCoin = toBackendSymbol(coin, hip3Assets);
|
|
5602
6086
|
const request = {
|
|
5603
6087
|
req: { coin: backendCoin, startTime, endTime, interval },
|
|
5604
6088
|
type: 'candleSnapshot',
|
|
@@ -5757,10 +6241,10 @@ const useHistoricalPriceData = () => {
|
|
|
5757
6241
|
setTokenLoading(token.symbol, true);
|
|
5758
6242
|
});
|
|
5759
6243
|
try {
|
|
5760
|
-
const
|
|
6244
|
+
const hip3Assets = useHyperliquidData.getState().hip3Assets;
|
|
5761
6245
|
const fetchPromises = tokensToFetch.map(async (token) => {
|
|
5762
6246
|
try {
|
|
5763
|
-
const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval,
|
|
6247
|
+
const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, hip3Assets);
|
|
5764
6248
|
addHistoricalPriceData(token.symbol, interval, response.data, { start: startTime, end: endTime });
|
|
5765
6249
|
return { symbol: token.symbol, candles: response.data, success: true };
|
|
5766
6250
|
}
|
|
@@ -6302,165 +6786,27 @@ function useAgentWallet() {
|
|
|
6302
6786
|
};
|
|
6303
6787
|
}
|
|
6304
6788
|
|
|
6305
|
-
/**
|
|
6306
|
-
* Sync external fills into Pear Hyperliquid service (POST /sync/fills)
|
|
6307
|
-
*/
|
|
6308
|
-
const syncFills = async (baseUrl, payload) => {
|
|
6309
|
-
const url = joinUrl(baseUrl, '/sync/fills');
|
|
6310
|
-
try {
|
|
6311
|
-
const response = await apiClient.post(url, payload, {
|
|
6312
|
-
headers: { 'Content-Type': 'application/json' },
|
|
6313
|
-
timeout: 30000,
|
|
6314
|
-
});
|
|
6315
|
-
return { data: response.data, status: response.status, headers: response.headers };
|
|
6316
|
-
}
|
|
6317
|
-
catch (error) {
|
|
6318
|
-
throw toApiError(error);
|
|
6319
|
-
}
|
|
6320
|
-
};
|
|
6321
|
-
/**
|
|
6322
|
-
* Convenience: fetch user fills from HyperLiquid, then sync them to Pear backend
|
|
6323
|
-
*/
|
|
6324
|
-
const syncUserFillsFromHyperliquid = async (baseUrl, user, aggregateByTime = true, lastSyncAt = null, assetPositions) => {
|
|
6325
|
-
const firstStartTime = lastSyncAt ? Number(lastSyncAt) + 1 : 0;
|
|
6326
|
-
const allFills = [];
|
|
6327
|
-
const seenTids = new Set();
|
|
6328
|
-
let startTime = firstStartTime;
|
|
6329
|
-
let batchSize = 0;
|
|
6330
|
-
do {
|
|
6331
|
-
const { data: batch } = await fetchUserFillsFromHyperliquid(user, startTime, aggregateByTime);
|
|
6332
|
-
batchSize = batch.length;
|
|
6333
|
-
for (const fill of batch) {
|
|
6334
|
-
const tid = fill.tid;
|
|
6335
|
-
if (tid === undefined)
|
|
6336
|
-
continue;
|
|
6337
|
-
if (!seenTids.has(tid)) {
|
|
6338
|
-
seenTids.add(tid);
|
|
6339
|
-
allFills.push(fill);
|
|
6340
|
-
}
|
|
6341
|
-
}
|
|
6342
|
-
if (batchSize === 2000) {
|
|
6343
|
-
const last = batch[batch.length - 1];
|
|
6344
|
-
startTime = last.time;
|
|
6345
|
-
}
|
|
6346
|
-
} while (batchSize === 2000);
|
|
6347
|
-
startTime = firstStartTime;
|
|
6348
|
-
batchSize = 0;
|
|
6349
|
-
do {
|
|
6350
|
-
const { data: twapBatch } = await fetchUserTwapSliceFillsByTime(user, startTime, aggregateByTime);
|
|
6351
|
-
batchSize = twapBatch.length;
|
|
6352
|
-
for (const item of twapBatch) {
|
|
6353
|
-
const fill = item.fill;
|
|
6354
|
-
const tid = fill.tid;
|
|
6355
|
-
if (tid === undefined)
|
|
6356
|
-
continue;
|
|
6357
|
-
if (!seenTids.has(tid)) {
|
|
6358
|
-
seenTids.add(tid);
|
|
6359
|
-
allFills.push(fill);
|
|
6360
|
-
}
|
|
6361
|
-
}
|
|
6362
|
-
if (batchSize === 2000) {
|
|
6363
|
-
const last = twapBatch[twapBatch.length - 1];
|
|
6364
|
-
startTime = last.fill.time;
|
|
6365
|
-
}
|
|
6366
|
-
} while (batchSize === 2000);
|
|
6367
|
-
const sortedFills = [...allFills].sort((a, b) => Number(a.time) - Number(b.time));
|
|
6368
|
-
return syncFills(baseUrl, { user, fills: sortedFills, assetPositions });
|
|
6369
|
-
};
|
|
6370
|
-
|
|
6371
|
-
/**
|
|
6372
|
-
* Listens to address changes and periodically syncs user fills
|
|
6373
|
-
* Defaults: aggregate=true, interval=60s
|
|
6374
|
-
*/
|
|
6375
|
-
function useAutoSyncFills(options) {
|
|
6376
|
-
const { baseUrl, address, intervalMs = 60000, aggregateByTime = true, } = options;
|
|
6377
|
-
const [lastRunAt, setLastRunAt] = useState(null);
|
|
6378
|
-
const [lastResult, setLastResult] = useState(null);
|
|
6379
|
-
const [error, setError] = useState(null);
|
|
6380
|
-
const [isSyncing, setIsSyncing] = useState(false);
|
|
6381
|
-
const mountedRef = useRef(true);
|
|
6382
|
-
const runningRef = useRef(null);
|
|
6383
|
-
const lastSyncedAt = useUserData((state) => { var _a; return (_a = state.accountSummary) === null || _a === void 0 ? void 0 : _a.lastSyncedAt; });
|
|
6384
|
-
const enabled = useUserData((state) => state.isAuthenticated);
|
|
6385
|
-
useEffect(() => {
|
|
6386
|
-
mountedRef.current = true;
|
|
6387
|
-
return () => { mountedRef.current = false; };
|
|
6388
|
-
}, []);
|
|
6389
|
-
const canRun = useMemo(() => {
|
|
6390
|
-
return Boolean(enabled && address && baseUrl);
|
|
6391
|
-
}, [enabled, address, baseUrl]);
|
|
6392
|
-
const doSync = useCallback(async () => {
|
|
6393
|
-
var _a;
|
|
6394
|
-
if (!canRun)
|
|
6395
|
-
return;
|
|
6396
|
-
if (runningRef.current)
|
|
6397
|
-
return;
|
|
6398
|
-
if (!useUserData.getState().accountSummary)
|
|
6399
|
-
return;
|
|
6400
|
-
if (!((_a = useHyperliquidData.getState().aggregatedClearingHouseState) === null || _a === void 0 ? void 0 : _a.assetPositions))
|
|
6401
|
-
return;
|
|
6402
|
-
setIsSyncing(true);
|
|
6403
|
-
setError(null);
|
|
6404
|
-
const promise = (async () => {
|
|
6405
|
-
var _a;
|
|
6406
|
-
try {
|
|
6407
|
-
const { data } = await syncUserFillsFromHyperliquid(baseUrl, address, aggregateByTime, lastSyncedAt, useHyperliquidData.getState().aggregatedClearingHouseState.assetPositions);
|
|
6408
|
-
if (!mountedRef.current)
|
|
6409
|
-
return;
|
|
6410
|
-
setLastResult(data);
|
|
6411
|
-
setLastRunAt(Date.now());
|
|
6412
|
-
}
|
|
6413
|
-
catch (e) {
|
|
6414
|
-
if (!mountedRef.current)
|
|
6415
|
-
return;
|
|
6416
|
-
setError((_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : 'Failed to sync user fills');
|
|
6417
|
-
}
|
|
6418
|
-
finally {
|
|
6419
|
-
if (mountedRef.current)
|
|
6420
|
-
setIsSyncing(false);
|
|
6421
|
-
runningRef.current = null;
|
|
6422
|
-
}
|
|
6423
|
-
})();
|
|
6424
|
-
runningRef.current = promise;
|
|
6425
|
-
await promise;
|
|
6426
|
-
}, [canRun, baseUrl, address, aggregateByTime, lastSyncedAt]);
|
|
6427
|
-
useEffect(() => {
|
|
6428
|
-
if (!canRun)
|
|
6429
|
-
return;
|
|
6430
|
-
// Fire immediately on address change/enable
|
|
6431
|
-
void doSync();
|
|
6432
|
-
const id = setInterval(() => {
|
|
6433
|
-
void doSync();
|
|
6434
|
-
}, Math.max(1000, intervalMs));
|
|
6435
|
-
return () => clearInterval(id);
|
|
6436
|
-
}, [canRun, doSync, intervalMs]);
|
|
6437
|
-
return {
|
|
6438
|
-
lastRunAt,
|
|
6439
|
-
lastResult,
|
|
6440
|
-
error,
|
|
6441
|
-
isSyncing,
|
|
6442
|
-
triggerSync: doSync,
|
|
6443
|
-
};
|
|
6444
|
-
}
|
|
6445
|
-
|
|
6446
6789
|
/**
|
|
6447
6790
|
* Create a position (MARKET/LIMIT/TWAP) using Pear Hyperliquid service
|
|
6448
6791
|
* Authorization is derived from headers (Axios defaults or browser localStorage fallback)
|
|
6449
6792
|
* @throws MinimumPositionSizeError if any asset has less than $11 USD value
|
|
6450
6793
|
* @throws MaxAssetsPerLegError if any leg exceeds the maximum allowed assets (15)
|
|
6451
6794
|
*/
|
|
6452
|
-
async function createPosition(baseUrl, payload,
|
|
6795
|
+
async function createPosition(baseUrl, payload, hip3Assets) {
|
|
6453
6796
|
// Validate maximum assets per leg before creating position
|
|
6454
6797
|
validateMaxAssetsPerLeg(payload.longAssets, payload.shortAssets);
|
|
6455
6798
|
// Validate minimum asset size before creating position
|
|
6456
6799
|
validateMinimumAssetSize(payload.usdValue, payload.longAssets, payload.shortAssets);
|
|
6457
6800
|
const url = joinUrl(baseUrl, "/positions");
|
|
6458
6801
|
// Translate display symbols to backend format
|
|
6459
|
-
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset,
|
|
6802
|
+
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6460
6803
|
const translatedPayload = {
|
|
6461
6804
|
...payload,
|
|
6462
6805
|
longAssets: mapAssets(payload.longAssets),
|
|
6463
6806
|
shortAssets: mapAssets(payload.shortAssets),
|
|
6807
|
+
assetName: payload.assetName
|
|
6808
|
+
? toBackendSymbol(payload.assetName, hip3Assets)
|
|
6809
|
+
: undefined,
|
|
6464
6810
|
};
|
|
6465
6811
|
try {
|
|
6466
6812
|
const resp = await apiClient.post(url, translatedPayload, {
|
|
@@ -6555,9 +6901,9 @@ async function adjustPosition(baseUrl, positionId, payload) {
|
|
|
6555
6901
|
throw toApiError(error);
|
|
6556
6902
|
}
|
|
6557
6903
|
}
|
|
6558
|
-
async function adjustAdvancePosition(baseUrl, positionId, payload,
|
|
6904
|
+
async function adjustAdvancePosition(baseUrl, positionId, payload, hip3Assets) {
|
|
6559
6905
|
const url = joinUrl(baseUrl, `/positions/${positionId}/adjust-advance`);
|
|
6560
|
-
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset,
|
|
6906
|
+
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6561
6907
|
const translatedPayload = (payload || []).map((item) => ({
|
|
6562
6908
|
longAssets: mapAssets(item.longAssets),
|
|
6563
6909
|
shortAssets: mapAssets(item.shortAssets),
|
|
@@ -6640,10 +6986,11 @@ const calculatePositionAsset = (asset, currentPrice, totalInitialPositionSize, l
|
|
|
6640
6986
|
positionValue: currentNotional,
|
|
6641
6987
|
unrealizedPnl: unrealizedPnl,
|
|
6642
6988
|
entryPositionValue: entryNotional,
|
|
6643
|
-
initialWeight: totalInitialPositionSize > 0
|
|
6644
|
-
? entryNotional / totalInitialPositionSize
|
|
6645
|
-
: 0,
|
|
6989
|
+
initialWeight: totalInitialPositionSize > 0 ? entryNotional / totalInitialPositionSize : 0,
|
|
6646
6990
|
fundingPaid: (_a = asset.fundingPaid) !== null && _a !== void 0 ? _a : 0,
|
|
6991
|
+
// Preserve market metadata from raw asset (if provided by backend)
|
|
6992
|
+
marketPrefix: asset.marketPrefix,
|
|
6993
|
+
collateralToken: asset.collateralToken,
|
|
6647
6994
|
};
|
|
6648
6995
|
};
|
|
6649
6996
|
const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
|
|
@@ -6732,36 +7079,108 @@ const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
|
|
|
6732
7079
|
});
|
|
6733
7080
|
};
|
|
6734
7081
|
|
|
6735
|
-
function
|
|
6736
|
-
|
|
6737
|
-
if (!
|
|
6738
|
-
|
|
7082
|
+
function findAssetMeta$3(coinName, perpMetaAssets, knownPrefix, desiredCollateral) {
|
|
7083
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
7084
|
+
if (!perpMetaAssets) {
|
|
7085
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
6739
7086
|
}
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
7087
|
+
if (desiredCollateral) {
|
|
7088
|
+
const collateralMatch = perpMetaAssets.find((a) => a.name === coinName && a.collateralToken === desiredCollateral);
|
|
7089
|
+
if (collateralMatch) {
|
|
7090
|
+
return {
|
|
7091
|
+
collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
7092
|
+
marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
7093
|
+
};
|
|
7094
|
+
}
|
|
7095
|
+
}
|
|
7096
|
+
if (coinName.includes(':')) {
|
|
7097
|
+
const [prefix, symbol] = coinName.split(':');
|
|
7098
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7099
|
+
var _a;
|
|
7100
|
+
return a.name === symbol &&
|
|
7101
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
|
|
7102
|
+
});
|
|
7103
|
+
if (exactMatch) {
|
|
7104
|
+
return {
|
|
7105
|
+
collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
7106
|
+
marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
7107
|
+
};
|
|
7108
|
+
}
|
|
7109
|
+
}
|
|
7110
|
+
if (knownPrefix) {
|
|
7111
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7112
|
+
var _a;
|
|
7113
|
+
return a.name === coinName &&
|
|
7114
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
|
|
7115
|
+
});
|
|
7116
|
+
if (exactMatch) {
|
|
7117
|
+
return {
|
|
7118
|
+
collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
|
|
7119
|
+
marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
|
|
7120
|
+
};
|
|
7121
|
+
}
|
|
7122
|
+
}
|
|
7123
|
+
const regularAsset = perpMetaAssets.find((a) => a.name === coinName && !a.marketPrefix);
|
|
7124
|
+
if (regularAsset) {
|
|
7125
|
+
return {
|
|
7126
|
+
collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
|
|
7127
|
+
marketPrefix: null,
|
|
7128
|
+
};
|
|
7129
|
+
}
|
|
7130
|
+
const hip3Asset = perpMetaAssets.find((a) => a.name === coinName && a.marketPrefix);
|
|
7131
|
+
if (hip3Asset) {
|
|
7132
|
+
return {
|
|
7133
|
+
collateralToken: (_h = hip3Asset.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
|
|
7134
|
+
marketPrefix: (_j = hip3Asset.marketPrefix) !== null && _j !== void 0 ? _j : null,
|
|
7135
|
+
};
|
|
7136
|
+
}
|
|
7137
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7138
|
+
}
|
|
7139
|
+
function enrichPositionAssets(assets, perpMetaAssets) {
|
|
7140
|
+
return assets.map((asset) => {
|
|
7141
|
+
var _a;
|
|
7142
|
+
if (asset.marketPrefix && asset.collateralToken) {
|
|
7143
|
+
return asset;
|
|
7144
|
+
}
|
|
7145
|
+
const meta = findAssetMeta$3(asset.coin, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
|
|
7146
|
+
return {
|
|
7147
|
+
...asset,
|
|
7148
|
+
marketPrefix: asset.marketPrefix || meta.marketPrefix,
|
|
7149
|
+
collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
|
|
7150
|
+
};
|
|
7151
|
+
});
|
|
7152
|
+
}
|
|
7153
|
+
function enrichPositions(positions, perpMetaAssets) {
|
|
7154
|
+
return positions.map((position) => ({
|
|
7155
|
+
...position,
|
|
7156
|
+
longAssets: enrichPositionAssets(position.longAssets, perpMetaAssets),
|
|
7157
|
+
shortAssets: enrichPositionAssets(position.shortAssets, perpMetaAssets),
|
|
7158
|
+
}));
|
|
7159
|
+
}
|
|
7160
|
+
function usePosition() {
|
|
7161
|
+
const context = useContext(PearHyperliquidContext);
|
|
7162
|
+
if (!context) {
|
|
7163
|
+
throw new Error('usePosition must be used within a PearHyperliquidProvider');
|
|
7164
|
+
}
|
|
7165
|
+
const { apiBaseUrl, isConnected } = context;
|
|
7166
|
+
const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
|
|
7167
|
+
const createPosition$1 = async (payload) => {
|
|
7168
|
+
return createPosition(apiBaseUrl, payload, hip3Assets);
|
|
7169
|
+
};
|
|
7170
|
+
const updateRiskParameters$1 = async (positionId, payload) => {
|
|
7171
|
+
return updateRiskParameters(apiBaseUrl, positionId, payload);
|
|
7172
|
+
};
|
|
7173
|
+
const closePosition$1 = async (positionId, payload) => {
|
|
6752
7174
|
return closePosition(apiBaseUrl, positionId, payload);
|
|
6753
7175
|
};
|
|
6754
|
-
// Close all positions (MARKET or TWAP)
|
|
6755
7176
|
const closeAllPositions$1 = async (payload) => {
|
|
6756
7177
|
return closeAllPositions(apiBaseUrl, payload);
|
|
6757
7178
|
};
|
|
6758
|
-
// Adjust a position (REDUCE/INCREASE by %; MARKET or LIMIT)
|
|
6759
7179
|
const adjustPosition$1 = async (positionId, payload) => {
|
|
6760
7180
|
return adjustPosition(apiBaseUrl, positionId, payload);
|
|
6761
7181
|
};
|
|
6762
|
-
// Adjust to absolute target sizes per asset, optionally adding new assets
|
|
6763
7182
|
const adjustAdvancePosition$1 = async (positionId, payload) => {
|
|
6764
|
-
return adjustAdvancePosition(apiBaseUrl, positionId, payload,
|
|
7183
|
+
return adjustAdvancePosition(apiBaseUrl, positionId, payload, hip3Assets);
|
|
6765
7184
|
};
|
|
6766
7185
|
const updateLeverage$1 = async (positionId, leverage) => {
|
|
6767
7186
|
return updateLeverage(apiBaseUrl, positionId, { leverage });
|
|
@@ -6770,22 +7189,46 @@ function usePosition() {
|
|
|
6770
7189
|
const userOpenPositions = useUserData((state) => state.rawOpenPositions);
|
|
6771
7190
|
const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
6772
7191
|
const allMids = useHyperliquidData((state) => state.allMids);
|
|
7192
|
+
const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
|
|
6773
7193
|
const isLoading = useMemo(() => {
|
|
6774
7194
|
return userOpenPositions === null && isConnected;
|
|
6775
7195
|
}, [userOpenPositions, isConnected]);
|
|
6776
7196
|
const openPositions = useMemo(() => {
|
|
6777
7197
|
if (!userOpenPositions || !aggregatedClearingHouseState || !allMids)
|
|
6778
7198
|
return null;
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
7199
|
+
const positions = buildPositionValue(userOpenPositions, aggregatedClearingHouseState, allMids);
|
|
7200
|
+
return enrichPositions(positions, allPerpMetaAssets);
|
|
7201
|
+
}, [
|
|
7202
|
+
userOpenPositions,
|
|
7203
|
+
aggregatedClearingHouseState,
|
|
7204
|
+
allMids,
|
|
7205
|
+
allPerpMetaAssets,
|
|
7206
|
+
]);
|
|
7207
|
+
return {
|
|
7208
|
+
createPosition: createPosition$1,
|
|
7209
|
+
updateRiskParameters: updateRiskParameters$1,
|
|
7210
|
+
closePosition: closePosition$1,
|
|
7211
|
+
closeAllPositions: closeAllPositions$1,
|
|
7212
|
+
adjustPosition: adjustPosition$1,
|
|
7213
|
+
adjustAdvancePosition: adjustAdvancePosition$1,
|
|
7214
|
+
updateLeverage: updateLeverage$1,
|
|
7215
|
+
openPositions,
|
|
7216
|
+
isLoading,
|
|
7217
|
+
};
|
|
6782
7218
|
}
|
|
6783
7219
|
|
|
6784
7220
|
async function adjustOrder(baseUrl, orderId, payload) {
|
|
6785
7221
|
const url = joinUrl(baseUrl, `/orders/${orderId}/adjust`);
|
|
6786
7222
|
try {
|
|
6787
|
-
const resp = await apiClient.put(url, payload, {
|
|
6788
|
-
|
|
7223
|
+
const resp = await apiClient.put(url, payload, {
|
|
7224
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7225
|
+
timeout: 60000,
|
|
7226
|
+
});
|
|
7227
|
+
return {
|
|
7228
|
+
data: resp.data,
|
|
7229
|
+
status: resp.status,
|
|
7230
|
+
headers: resp.headers,
|
|
7231
|
+
};
|
|
6789
7232
|
}
|
|
6790
7233
|
catch (error) {
|
|
6791
7234
|
throw toApiError(error);
|
|
@@ -6794,8 +7237,14 @@ async function adjustOrder(baseUrl, orderId, payload) {
|
|
|
6794
7237
|
async function cancelOrder(baseUrl, orderId) {
|
|
6795
7238
|
const url = joinUrl(baseUrl, `/orders/${orderId}/cancel`);
|
|
6796
7239
|
try {
|
|
6797
|
-
const resp = await apiClient.delete(url, {
|
|
6798
|
-
|
|
7240
|
+
const resp = await apiClient.delete(url, {
|
|
7241
|
+
timeout: 60000,
|
|
7242
|
+
});
|
|
7243
|
+
return {
|
|
7244
|
+
data: resp.data,
|
|
7245
|
+
status: resp.status,
|
|
7246
|
+
headers: resp.headers,
|
|
7247
|
+
};
|
|
6799
7248
|
}
|
|
6800
7249
|
catch (error) {
|
|
6801
7250
|
throw toApiError(error);
|
|
@@ -6805,19 +7254,165 @@ async function cancelTwapOrder(baseUrl, orderId) {
|
|
|
6805
7254
|
const url = joinUrl(baseUrl, `/orders/${orderId}/twap/cancel`);
|
|
6806
7255
|
try {
|
|
6807
7256
|
const resp = await apiClient.post(url, {}, { headers: { 'Content-Type': 'application/json' }, timeout: 60000 });
|
|
6808
|
-
return {
|
|
7257
|
+
return {
|
|
7258
|
+
data: resp.data,
|
|
7259
|
+
status: resp.status,
|
|
7260
|
+
headers: resp.headers,
|
|
7261
|
+
};
|
|
7262
|
+
}
|
|
7263
|
+
catch (error) {
|
|
7264
|
+
throw toApiError(error);
|
|
7265
|
+
}
|
|
7266
|
+
}
|
|
7267
|
+
/**
|
|
7268
|
+
* Execute a spot order (swap) using Pear Hyperliquid service
|
|
7269
|
+
* POST /orders/spot
|
|
7270
|
+
*/
|
|
7271
|
+
async function executeSpotOrder(baseUrl, payload) {
|
|
7272
|
+
const url = joinUrl(baseUrl, '/orders/spot');
|
|
7273
|
+
try {
|
|
7274
|
+
const resp = await apiClient.post(url, payload, {
|
|
7275
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7276
|
+
timeout: 60000,
|
|
7277
|
+
});
|
|
7278
|
+
return {
|
|
7279
|
+
data: resp.data,
|
|
7280
|
+
status: resp.status,
|
|
7281
|
+
headers: resp.headers,
|
|
7282
|
+
};
|
|
6809
7283
|
}
|
|
6810
7284
|
catch (error) {
|
|
6811
7285
|
throw toApiError(error);
|
|
6812
7286
|
}
|
|
6813
7287
|
}
|
|
6814
7288
|
|
|
7289
|
+
const KALSHI_API_BASE_URL = 'https://api.elections.kalshi.com/trade-api/v2';
|
|
7290
|
+
async function getKalshiMarkets(params) {
|
|
7291
|
+
const url = new URL(`${KALSHI_API_BASE_URL}/markets`);
|
|
7292
|
+
if (params) {
|
|
7293
|
+
if (params.limit !== undefined) {
|
|
7294
|
+
url.searchParams.set('limit', String(params.limit));
|
|
7295
|
+
}
|
|
7296
|
+
if (params.cursor) {
|
|
7297
|
+
url.searchParams.set('cursor', params.cursor);
|
|
7298
|
+
}
|
|
7299
|
+
if (params.tickers && params.tickers.length > 0) {
|
|
7300
|
+
url.searchParams.set('tickers', params.tickers.join(','));
|
|
7301
|
+
}
|
|
7302
|
+
if (params.event_ticker) {
|
|
7303
|
+
url.searchParams.set('event_ticker', params.event_ticker);
|
|
7304
|
+
}
|
|
7305
|
+
if (params.series_ticker) {
|
|
7306
|
+
url.searchParams.set('series_ticker', params.series_ticker);
|
|
7307
|
+
}
|
|
7308
|
+
if (params.status) {
|
|
7309
|
+
url.searchParams.set('status', params.status);
|
|
7310
|
+
}
|
|
7311
|
+
}
|
|
7312
|
+
try {
|
|
7313
|
+
const response = await axios$1.get(url.toString());
|
|
7314
|
+
return {
|
|
7315
|
+
data: response.data,
|
|
7316
|
+
status: response.status,
|
|
7317
|
+
headers: response.headers,
|
|
7318
|
+
};
|
|
7319
|
+
}
|
|
7320
|
+
catch (error) {
|
|
7321
|
+
throw toApiError(error);
|
|
7322
|
+
}
|
|
7323
|
+
}
|
|
7324
|
+
|
|
7325
|
+
function findAssetMeta$2(assetName, perpMetaAssets, knownPrefix, desiredCollateral) {
|
|
7326
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
7327
|
+
if (!perpMetaAssets) {
|
|
7328
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7329
|
+
}
|
|
7330
|
+
if (desiredCollateral) {
|
|
7331
|
+
const collateralMatch = perpMetaAssets.find((a) => a.name === assetName && a.collateralToken === desiredCollateral);
|
|
7332
|
+
if (collateralMatch) {
|
|
7333
|
+
return {
|
|
7334
|
+
collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
7335
|
+
marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
7336
|
+
};
|
|
7337
|
+
}
|
|
7338
|
+
}
|
|
7339
|
+
if (assetName.includes(':')) {
|
|
7340
|
+
const [prefix, symbol] = assetName.split(':');
|
|
7341
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7342
|
+
var _a;
|
|
7343
|
+
return a.name === symbol &&
|
|
7344
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
|
|
7345
|
+
});
|
|
7346
|
+
if (exactMatch) {
|
|
7347
|
+
return {
|
|
7348
|
+
collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
7349
|
+
marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
7350
|
+
};
|
|
7351
|
+
}
|
|
7352
|
+
}
|
|
7353
|
+
if (knownPrefix) {
|
|
7354
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7355
|
+
var _a;
|
|
7356
|
+
return a.name === assetName &&
|
|
7357
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
|
|
7358
|
+
});
|
|
7359
|
+
if (exactMatch) {
|
|
7360
|
+
return {
|
|
7361
|
+
collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
|
|
7362
|
+
marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
|
|
7363
|
+
};
|
|
7364
|
+
}
|
|
7365
|
+
}
|
|
7366
|
+
const regularAsset = perpMetaAssets.find((a) => a.name === assetName && !a.marketPrefix);
|
|
7367
|
+
if (regularAsset) {
|
|
7368
|
+
return {
|
|
7369
|
+
collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
|
|
7370
|
+
marketPrefix: null,
|
|
7371
|
+
};
|
|
7372
|
+
}
|
|
7373
|
+
const hip3Assets = perpMetaAssets.filter((a) => a.name === assetName && a.marketPrefix);
|
|
7374
|
+
if (hip3Assets.length > 0) {
|
|
7375
|
+
if (desiredCollateral) {
|
|
7376
|
+
const collateralMatch = hip3Assets.find((a) => a.collateralToken === desiredCollateral);
|
|
7377
|
+
if (collateralMatch) {
|
|
7378
|
+
return {
|
|
7379
|
+
collateralToken: (_h = collateralMatch.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
|
|
7380
|
+
marketPrefix: (_j = collateralMatch.marketPrefix) !== null && _j !== void 0 ? _j : null,
|
|
7381
|
+
};
|
|
7382
|
+
}
|
|
7383
|
+
}
|
|
7384
|
+
const usdHMatch = hip3Assets.find((a) => a.collateralToken === 'USDH');
|
|
7385
|
+
const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Assets[0];
|
|
7386
|
+
return {
|
|
7387
|
+
collateralToken: (_k = chosen.collateralToken) !== null && _k !== void 0 ? _k : 'USDC',
|
|
7388
|
+
marketPrefix: (_l = chosen.marketPrefix) !== null && _l !== void 0 ? _l : null,
|
|
7389
|
+
};
|
|
7390
|
+
}
|
|
7391
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7392
|
+
}
|
|
7393
|
+
function enrichOrderAssets$1(assets, perpMetaAssets) {
|
|
7394
|
+
if (!assets)
|
|
7395
|
+
return [];
|
|
7396
|
+
return assets.map((asset) => {
|
|
7397
|
+
var _a;
|
|
7398
|
+
if (asset.marketPrefix && asset.collateralToken) {
|
|
7399
|
+
return asset;
|
|
7400
|
+
}
|
|
7401
|
+
const meta = findAssetMeta$2(asset.asset, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
|
|
7402
|
+
return {
|
|
7403
|
+
...asset,
|
|
7404
|
+
marketPrefix: asset.marketPrefix || meta.marketPrefix,
|
|
7405
|
+
collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
|
|
7406
|
+
};
|
|
7407
|
+
});
|
|
7408
|
+
}
|
|
6815
7409
|
function useOrders() {
|
|
6816
7410
|
const context = useContext(PearHyperliquidContext);
|
|
6817
7411
|
if (!context)
|
|
6818
7412
|
throw new Error('useOrders must be used within a PearHyperliquidProvider');
|
|
6819
7413
|
const { apiBaseUrl } = context;
|
|
6820
7414
|
const openOrders = useUserData((state) => state.openOrders);
|
|
7415
|
+
const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
|
|
6821
7416
|
const isLoading = useMemo(() => openOrders === null && context.isConnected, [openOrders, context.isConnected]);
|
|
6822
7417
|
const { openPositions } = usePosition();
|
|
6823
7418
|
const positionsById = useMemo(() => {
|
|
@@ -6828,27 +7423,63 @@ function useOrders() {
|
|
|
6828
7423
|
}
|
|
6829
7424
|
return map;
|
|
6830
7425
|
}, [openPositions]);
|
|
7426
|
+
const [marketTitles, setMarketTitles] = useState({});
|
|
6831
7427
|
const enrichedOpenOrders = useMemo(() => {
|
|
6832
7428
|
if (!openOrders)
|
|
6833
7429
|
return null;
|
|
6834
|
-
|
|
7430
|
+
// Collect prediction market codes for fetching
|
|
7431
|
+
const predictionMarketCodes = [];
|
|
7432
|
+
const enrichedOrders = openOrders.map((ord) => {
|
|
6835
7433
|
var _a, _b, _c, _d, _e;
|
|
6836
7434
|
const isTpSl = ord.orderType === 'TP' || ord.orderType === 'SL';
|
|
6837
7435
|
const hasAssets = ((_b = (_a = ord.longAssets) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0 || ((_d = (_c = ord.shortAssets) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) > 0;
|
|
6838
7436
|
const pos = positionsById.get((_e = ord.positionId) !== null && _e !== void 0 ? _e : '');
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
7437
|
+
let enrichedOrd = {
|
|
7438
|
+
...ord,
|
|
7439
|
+
longAssets: enrichOrderAssets$1(ord.longAssets, allPerpMetaAssets),
|
|
7440
|
+
shortAssets: enrichOrderAssets$1(ord.shortAssets, allPerpMetaAssets),
|
|
7441
|
+
};
|
|
7442
|
+
if (isTpSl && !hasAssets && pos) {
|
|
7443
|
+
const mapAssets = (arr) => arr.map((a) => ({
|
|
7444
|
+
asset: a.coin,
|
|
7445
|
+
weight: a.initialWeight,
|
|
7446
|
+
marketPrefix: a.marketPrefix,
|
|
7447
|
+
collateralToken: a.collateralToken,
|
|
7448
|
+
}));
|
|
7449
|
+
enrichedOrd = {
|
|
7450
|
+
...enrichedOrd,
|
|
6845
7451
|
longAssets: mapAssets(pos.longAssets),
|
|
6846
7452
|
shortAssets: mapAssets(pos.shortAssets),
|
|
6847
7453
|
};
|
|
6848
7454
|
}
|
|
6849
|
-
|
|
7455
|
+
const params = ord.parameters;
|
|
7456
|
+
if ((params === null || params === void 0 ? void 0 : params.triggerType) === 'PREDICTION_MARKET_OUTCOME' && (params === null || params === void 0 ? void 0 : params.marketCode)) {
|
|
7457
|
+
const marketCode = params.marketCode;
|
|
7458
|
+
if (!predictionMarketCodes.includes(marketCode)) {
|
|
7459
|
+
predictionMarketCodes.push(marketCode);
|
|
7460
|
+
}
|
|
7461
|
+
enrichedOrd.marketTitle = marketTitles[marketCode];
|
|
7462
|
+
}
|
|
7463
|
+
return enrichedOrd;
|
|
6850
7464
|
});
|
|
6851
|
-
|
|
7465
|
+
const missingCodes = predictionMarketCodes.filter((code) => !marketTitles[code]);
|
|
7466
|
+
if (missingCodes.length > 0) {
|
|
7467
|
+
getKalshiMarkets({ tickers: missingCodes })
|
|
7468
|
+
.then((response) => {
|
|
7469
|
+
const newTitles = {};
|
|
7470
|
+
for (const market of response.data.markets) {
|
|
7471
|
+
newTitles[market.ticker] = market.title;
|
|
7472
|
+
}
|
|
7473
|
+
if (Object.keys(newTitles).length > 0) {
|
|
7474
|
+
setMarketTitles((prev) => ({ ...prev, ...newTitles }));
|
|
7475
|
+
}
|
|
7476
|
+
})
|
|
7477
|
+
.catch((err) => {
|
|
7478
|
+
console.error('Failed to fetch Kalshi market titles:', err);
|
|
7479
|
+
});
|
|
7480
|
+
}
|
|
7481
|
+
return enrichedOrders;
|
|
7482
|
+
}, [openOrders, positionsById, allPerpMetaAssets, marketTitles]);
|
|
6852
7483
|
const adjustOrder$1 = async (orderId, payload) => {
|
|
6853
7484
|
return adjustOrder(apiBaseUrl, orderId, payload);
|
|
6854
7485
|
};
|
|
@@ -6858,16 +7489,156 @@ function useOrders() {
|
|
|
6858
7489
|
const cancelTwapOrder$1 = async (orderId) => {
|
|
6859
7490
|
return cancelTwapOrder(apiBaseUrl, orderId);
|
|
6860
7491
|
};
|
|
6861
|
-
return {
|
|
7492
|
+
return {
|
|
7493
|
+
adjustOrder: adjustOrder$1,
|
|
7494
|
+
cancelOrder: cancelOrder$1,
|
|
7495
|
+
cancelTwapOrder: cancelTwapOrder$1,
|
|
7496
|
+
openOrders: enrichedOpenOrders,
|
|
7497
|
+
isLoading,
|
|
7498
|
+
};
|
|
7499
|
+
}
|
|
7500
|
+
|
|
7501
|
+
/**
|
|
7502
|
+
* Hook for executing spot orders (swaps) on Hyperliquid
|
|
7503
|
+
* Use this to swap between USDC and USDH or other spot assets
|
|
7504
|
+
*/
|
|
7505
|
+
function useSpotOrder() {
|
|
7506
|
+
const context = useContext(PearHyperliquidContext);
|
|
7507
|
+
if (!context) {
|
|
7508
|
+
throw new Error('useSpotOrder must be used within a PearHyperliquidProvider');
|
|
7509
|
+
}
|
|
7510
|
+
const { apiBaseUrl } = context;
|
|
7511
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
7512
|
+
const [error, setError] = useState(null);
|
|
7513
|
+
const resetError = useCallback(() => {
|
|
7514
|
+
setError(null);
|
|
7515
|
+
}, []);
|
|
7516
|
+
const executeSpotOrder$1 = useCallback(async (payload) => {
|
|
7517
|
+
setIsLoading(true);
|
|
7518
|
+
setError(null);
|
|
7519
|
+
try {
|
|
7520
|
+
const response = await executeSpotOrder(apiBaseUrl, payload);
|
|
7521
|
+
return response;
|
|
7522
|
+
}
|
|
7523
|
+
catch (err) {
|
|
7524
|
+
const apiError = err;
|
|
7525
|
+
setError(apiError);
|
|
7526
|
+
throw apiError;
|
|
7527
|
+
}
|
|
7528
|
+
finally {
|
|
7529
|
+
setIsLoading(false);
|
|
7530
|
+
}
|
|
7531
|
+
}, [apiBaseUrl]);
|
|
7532
|
+
return {
|
|
7533
|
+
executeSpotOrder: executeSpotOrder$1,
|
|
7534
|
+
isLoading,
|
|
7535
|
+
error,
|
|
7536
|
+
resetError,
|
|
7537
|
+
};
|
|
6862
7538
|
}
|
|
6863
7539
|
|
|
7540
|
+
function findAssetMeta$1(assetName, perpMetaAssets, knownPrefix, desiredCollateral) {
|
|
7541
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
7542
|
+
if (!perpMetaAssets) {
|
|
7543
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7544
|
+
}
|
|
7545
|
+
if (desiredCollateral) {
|
|
7546
|
+
const collateralMatch = perpMetaAssets.find((a) => a.name === assetName && a.collateralToken === desiredCollateral);
|
|
7547
|
+
if (collateralMatch) {
|
|
7548
|
+
return {
|
|
7549
|
+
collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
7550
|
+
marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
7551
|
+
};
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
if (assetName.includes(':')) {
|
|
7555
|
+
const [prefix, symbol] = assetName.split(':');
|
|
7556
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7557
|
+
var _a;
|
|
7558
|
+
return a.name === symbol &&
|
|
7559
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
|
|
7560
|
+
});
|
|
7561
|
+
if (exactMatch) {
|
|
7562
|
+
return {
|
|
7563
|
+
collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
7564
|
+
marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
7565
|
+
};
|
|
7566
|
+
}
|
|
7567
|
+
}
|
|
7568
|
+
if (knownPrefix) {
|
|
7569
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7570
|
+
var _a;
|
|
7571
|
+
return a.name === assetName &&
|
|
7572
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
|
|
7573
|
+
});
|
|
7574
|
+
if (exactMatch) {
|
|
7575
|
+
return {
|
|
7576
|
+
collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
|
|
7577
|
+
marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
|
|
7578
|
+
};
|
|
7579
|
+
}
|
|
7580
|
+
}
|
|
7581
|
+
const regularAsset = perpMetaAssets.find((a) => a.name === assetName && !a.marketPrefix);
|
|
7582
|
+
if (regularAsset) {
|
|
7583
|
+
return {
|
|
7584
|
+
collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
|
|
7585
|
+
marketPrefix: null,
|
|
7586
|
+
};
|
|
7587
|
+
}
|
|
7588
|
+
const hip3Assets = perpMetaAssets.filter((a) => a.name === assetName && a.marketPrefix);
|
|
7589
|
+
if (hip3Assets.length > 0) {
|
|
7590
|
+
if (desiredCollateral) {
|
|
7591
|
+
const collateralMatch = hip3Assets.find((a) => a.collateralToken === desiredCollateral);
|
|
7592
|
+
if (collateralMatch) {
|
|
7593
|
+
return {
|
|
7594
|
+
collateralToken: (_h = collateralMatch.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
|
|
7595
|
+
marketPrefix: (_j = collateralMatch.marketPrefix) !== null && _j !== void 0 ? _j : null,
|
|
7596
|
+
};
|
|
7597
|
+
}
|
|
7598
|
+
}
|
|
7599
|
+
const usdHMatch = hip3Assets.find((a) => a.collateralToken === 'USDH');
|
|
7600
|
+
const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Assets[0];
|
|
7601
|
+
return {
|
|
7602
|
+
collateralToken: (_k = chosen.collateralToken) !== null && _k !== void 0 ? _k : 'USDC',
|
|
7603
|
+
marketPrefix: (_l = chosen.marketPrefix) !== null && _l !== void 0 ? _l : null,
|
|
7604
|
+
};
|
|
7605
|
+
}
|
|
7606
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7607
|
+
}
|
|
7608
|
+
function enrichOrderAssets(assets, perpMetaAssets) {
|
|
7609
|
+
if (!assets)
|
|
7610
|
+
return [];
|
|
7611
|
+
return assets.map((asset) => {
|
|
7612
|
+
var _a;
|
|
7613
|
+
if (asset.marketPrefix && asset.collateralToken) {
|
|
7614
|
+
return asset;
|
|
7615
|
+
}
|
|
7616
|
+
const meta = findAssetMeta$1(asset.asset, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
|
|
7617
|
+
return {
|
|
7618
|
+
...asset,
|
|
7619
|
+
marketPrefix: asset.marketPrefix || meta.marketPrefix,
|
|
7620
|
+
collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
|
|
7621
|
+
};
|
|
7622
|
+
});
|
|
7623
|
+
}
|
|
7624
|
+
function enrichTwapOrders(orders, perpMetaAssets) {
|
|
7625
|
+
return orders.map((order) => ({
|
|
7626
|
+
...order,
|
|
7627
|
+
longAssets: enrichOrderAssets(order.longAssets, perpMetaAssets),
|
|
7628
|
+
shortAssets: enrichOrderAssets(order.shortAssets, perpMetaAssets),
|
|
7629
|
+
}));
|
|
7630
|
+
}
|
|
6864
7631
|
function useTwap() {
|
|
6865
|
-
const twapDetails = useUserData(state => state.twapDetails);
|
|
7632
|
+
const twapDetails = useUserData((state) => state.twapDetails);
|
|
7633
|
+
const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
|
|
6866
7634
|
const context = useContext(PearHyperliquidContext);
|
|
6867
7635
|
if (!context)
|
|
6868
7636
|
throw new Error('useTwap must be used within a PearHyperliquidProvider');
|
|
6869
7637
|
const { apiBaseUrl } = context;
|
|
6870
|
-
const orders = useMemo(() =>
|
|
7638
|
+
const orders = useMemo(() => {
|
|
7639
|
+
const rawOrders = twapDetails !== null && twapDetails !== void 0 ? twapDetails : [];
|
|
7640
|
+
return enrichTwapOrders(rawOrders, allPerpMetaAssets);
|
|
7641
|
+
}, [twapDetails, allPerpMetaAssets]);
|
|
6871
7642
|
const cancelTwap$1 = async (orderId) => {
|
|
6872
7643
|
return cancelTwap(apiBaseUrl, orderId);
|
|
6873
7644
|
};
|
|
@@ -6950,59 +7721,170 @@ function useNotifications() {
|
|
|
6950
7721
|
};
|
|
6951
7722
|
}
|
|
6952
7723
|
|
|
6953
|
-
//
|
|
7724
|
+
// Helper to find asset metadata from perpMetaAssets
|
|
7725
|
+
function findAssetMeta(assetName, perpMetaAssets) {
|
|
7726
|
+
var _a, _b, _c, _d;
|
|
7727
|
+
if (!perpMetaAssets) {
|
|
7728
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7729
|
+
}
|
|
7730
|
+
// Try exact match first (for prefixed assets like "xyz:TSLA")
|
|
7731
|
+
const exactMatch = perpMetaAssets.find((a) => a.name === assetName);
|
|
7732
|
+
if (exactMatch) {
|
|
7733
|
+
return {
|
|
7734
|
+
collateralToken: (_a = exactMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
7735
|
+
marketPrefix: (_b = exactMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
7736
|
+
};
|
|
7737
|
+
}
|
|
7738
|
+
// Try matching by base symbol (for non-prefixed names in data)
|
|
7739
|
+
const baseMatch = perpMetaAssets.find((a) => {
|
|
7740
|
+
const baseName = a.name.includes(':') ? a.name.split(':')[1] : a.name;
|
|
7741
|
+
return baseName === assetName;
|
|
7742
|
+
});
|
|
7743
|
+
if (baseMatch) {
|
|
7744
|
+
return {
|
|
7745
|
+
collateralToken: (_c = baseMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
7746
|
+
marketPrefix: (_d = baseMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
7747
|
+
};
|
|
7748
|
+
}
|
|
7749
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7750
|
+
}
|
|
7751
|
+
// Enrich a single asset with metadata
|
|
7752
|
+
function enrichAsset(asset, perpMetaAssets) {
|
|
7753
|
+
const meta = findAssetMeta(asset.asset, perpMetaAssets);
|
|
7754
|
+
return {
|
|
7755
|
+
...asset,
|
|
7756
|
+
collateralToken: meta.collateralToken,
|
|
7757
|
+
marketPrefix: meta.marketPrefix,
|
|
7758
|
+
};
|
|
7759
|
+
}
|
|
7760
|
+
// Enrich a basket item with collateral info
|
|
7761
|
+
function enrichBasketItem(item, perpMetaAssets) {
|
|
7762
|
+
const enrichedLongs = item.longAssets.map((a) => enrichAsset(a, perpMetaAssets));
|
|
7763
|
+
const enrichedShorts = item.shortAssets.map((a) => enrichAsset(a, perpMetaAssets));
|
|
7764
|
+
// Determine collateral type
|
|
7765
|
+
const allAssets = [...enrichedLongs, ...enrichedShorts];
|
|
7766
|
+
const hasUsdc = allAssets.some((a) => a.collateralToken === 'USDC');
|
|
7767
|
+
const hasUsdh = allAssets.some((a) => a.collateralToken === 'USDH');
|
|
7768
|
+
let collateralType = 'USDC';
|
|
7769
|
+
if (hasUsdc && hasUsdh) {
|
|
7770
|
+
collateralType = 'MIXED';
|
|
7771
|
+
}
|
|
7772
|
+
else if (hasUsdh) {
|
|
7773
|
+
collateralType = 'USDH';
|
|
7774
|
+
}
|
|
7775
|
+
return {
|
|
7776
|
+
...item,
|
|
7777
|
+
longAssets: enrichedLongs,
|
|
7778
|
+
shortAssets: enrichedShorts,
|
|
7779
|
+
collateralType,
|
|
7780
|
+
};
|
|
7781
|
+
}
|
|
7782
|
+
/**
|
|
7783
|
+
* Filter baskets by collateral type
|
|
7784
|
+
* - 'USDC': Only baskets where ALL assets use USDC (collateralType === 'USDC')
|
|
7785
|
+
* - 'USDH': Only baskets where ALL assets use USDH (collateralType === 'USDH')
|
|
7786
|
+
* - 'ALL' or undefined: No filtering, returns all baskets
|
|
7787
|
+
*/
|
|
7788
|
+
function filterByCollateral(baskets, filter) {
|
|
7789
|
+
if (!filter || filter === 'ALL') {
|
|
7790
|
+
return baskets;
|
|
7791
|
+
}
|
|
7792
|
+
return baskets.filter((basket) => {
|
|
7793
|
+
if (filter === 'USDC') {
|
|
7794
|
+
// Include baskets that are purely USDC or have USDC assets
|
|
7795
|
+
return (basket.collateralType === 'USDC' || basket.collateralType === 'MIXED');
|
|
7796
|
+
}
|
|
7797
|
+
if (filter === 'USDH') {
|
|
7798
|
+
// Include baskets that are purely USDH or have USDH assets
|
|
7799
|
+
return (basket.collateralType === 'USDH' || basket.collateralType === 'MIXED');
|
|
7800
|
+
}
|
|
7801
|
+
return true;
|
|
7802
|
+
});
|
|
7803
|
+
}
|
|
7804
|
+
// Base selector for the full market-data payload (raw from WS)
|
|
6954
7805
|
const useMarketDataPayload = () => {
|
|
6955
7806
|
return useMarketData((s) => s.marketData);
|
|
6956
7807
|
};
|
|
6957
|
-
// Full payload for 'market-data-all' channel
|
|
7808
|
+
// Full payload for 'market-data-all' channel (raw from WS)
|
|
6958
7809
|
const useMarketDataAllPayload = () => {
|
|
6959
7810
|
return useMarketData((s) => s.marketDataAll);
|
|
6960
7811
|
};
|
|
6961
|
-
//
|
|
6962
|
-
const
|
|
6963
|
-
|
|
7812
|
+
// Access perpMetaAssets for enrichment
|
|
7813
|
+
const usePerpMetaAssets = () => {
|
|
7814
|
+
return useHyperliquidData((s) => s.perpMetaAssets);
|
|
7815
|
+
};
|
|
7816
|
+
// Active baskets (with collateral and market prefix info)
|
|
7817
|
+
const useActiveBaskets = (collateralFilter) => {
|
|
6964
7818
|
const data = useMarketDataPayload();
|
|
6965
|
-
|
|
7819
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7820
|
+
return useMemo(() => {
|
|
7821
|
+
if (!(data === null || data === void 0 ? void 0 : data.active))
|
|
7822
|
+
return [];
|
|
7823
|
+
const enriched = data.active.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7824
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7825
|
+
}, [data, perpMetaAssets, collateralFilter]);
|
|
6966
7826
|
};
|
|
6967
|
-
// Top gainers (
|
|
6968
|
-
const useTopGainers = (limit) => {
|
|
7827
|
+
// Top gainers (with collateral and market prefix info)
|
|
7828
|
+
const useTopGainers = (limit, collateralFilter) => {
|
|
6969
7829
|
const data = useMarketDataPayload();
|
|
7830
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6970
7831
|
return useMemo(() => {
|
|
6971
7832
|
var _a;
|
|
6972
7833
|
const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
|
|
6973
|
-
|
|
6974
|
-
|
|
7834
|
+
const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
|
|
7835
|
+
const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7836
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7837
|
+
}, [data, perpMetaAssets, limit, collateralFilter]);
|
|
6975
7838
|
};
|
|
6976
|
-
// Top losers (
|
|
6977
|
-
const useTopLosers = (limit) => {
|
|
7839
|
+
// Top losers (with collateral and market prefix info)
|
|
7840
|
+
const useTopLosers = (limit, collateralFilter) => {
|
|
6978
7841
|
const data = useMarketDataPayload();
|
|
7842
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6979
7843
|
return useMemo(() => {
|
|
6980
7844
|
var _a;
|
|
6981
7845
|
const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
|
|
6982
|
-
|
|
6983
|
-
|
|
7846
|
+
const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
|
|
7847
|
+
const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7848
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7849
|
+
}, [data, perpMetaAssets, limit, collateralFilter]);
|
|
6984
7850
|
};
|
|
6985
|
-
// Highlighted baskets
|
|
6986
|
-
const useHighlightedBaskets = () => {
|
|
6987
|
-
var _a;
|
|
7851
|
+
// Highlighted baskets (with collateral and market prefix info)
|
|
7852
|
+
const useHighlightedBaskets = (collateralFilter) => {
|
|
6988
7853
|
const data = useMarketDataPayload();
|
|
6989
|
-
|
|
7854
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7855
|
+
return useMemo(() => {
|
|
7856
|
+
if (!(data === null || data === void 0 ? void 0 : data.highlighted))
|
|
7857
|
+
return [];
|
|
7858
|
+
const enriched = data.highlighted.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7859
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7860
|
+
}, [data, perpMetaAssets, collateralFilter]);
|
|
6990
7861
|
};
|
|
6991
|
-
// Watchlist baskets (
|
|
6992
|
-
const useWatchlistBaskets = () => {
|
|
6993
|
-
var _a;
|
|
7862
|
+
// Watchlist baskets (with collateral and market prefix info)
|
|
7863
|
+
const useWatchlistBaskets = (collateralFilter) => {
|
|
6994
7864
|
const data = useMarketDataPayload();
|
|
6995
|
-
|
|
7865
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7866
|
+
return useMemo(() => {
|
|
7867
|
+
if (!(data === null || data === void 0 ? void 0 : data.watchlist))
|
|
7868
|
+
return [];
|
|
7869
|
+
const enriched = data.watchlist.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7870
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7871
|
+
}, [data, perpMetaAssets, collateralFilter]);
|
|
6996
7872
|
};
|
|
6997
|
-
// All baskets (
|
|
6998
|
-
const useAllBaskets = () => {
|
|
6999
|
-
var _a;
|
|
7873
|
+
// All baskets (with collateral and market prefix info)
|
|
7874
|
+
const useAllBaskets = (collateralFilter) => {
|
|
7000
7875
|
const dataAll = useMarketDataAllPayload();
|
|
7001
|
-
|
|
7876
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7877
|
+
return useMemo(() => {
|
|
7878
|
+
if (!(dataAll === null || dataAll === void 0 ? void 0 : dataAll.all))
|
|
7879
|
+
return [];
|
|
7880
|
+
const enriched = dataAll.all.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7881
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7882
|
+
}, [dataAll, perpMetaAssets, collateralFilter]);
|
|
7002
7883
|
};
|
|
7003
7884
|
// Find a basket by its exact asset composition (order-insensitive)
|
|
7004
7885
|
const useFindBasket = (longs, shorts) => {
|
|
7005
7886
|
const data = useMarketDataPayload();
|
|
7887
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7006
7888
|
return useMemo(() => {
|
|
7007
7889
|
if (!data)
|
|
7008
7890
|
return undefined;
|
|
@@ -7016,17 +7898,28 @@ const useFindBasket = (longs, shorts) => {
|
|
|
7016
7898
|
: '';
|
|
7017
7899
|
const lKey = normalize(longs);
|
|
7018
7900
|
const sKey = normalize(shorts);
|
|
7019
|
-
const match = (item) => normalize(item.longAssets) === lKey &&
|
|
7020
|
-
|
|
7021
|
-
|
|
7901
|
+
const match = (item) => normalize(item.longAssets) === lKey &&
|
|
7902
|
+
normalize(item.shortAssets) === sKey;
|
|
7903
|
+
const found = data.active.find(match) || data.highlighted.find(match);
|
|
7904
|
+
return found
|
|
7905
|
+
? enrichBasketItem(found, perpMetaAssets)
|
|
7906
|
+
: undefined;
|
|
7907
|
+
}, [data, longs, shorts, perpMetaAssets]);
|
|
7022
7908
|
};
|
|
7023
7909
|
|
|
7024
|
-
async function toggleWatchlist(baseUrl, longAssets, shortAssets,
|
|
7910
|
+
async function toggleWatchlist(baseUrl, longAssets, shortAssets, hip3Assets) {
|
|
7025
7911
|
const url = joinUrl(baseUrl, '/watchlist');
|
|
7026
|
-
const mapAssets = (arr) => arr.map(a => ({ ...a, asset: toBackendSymbol(a.asset,
|
|
7912
|
+
const mapAssets = (arr) => arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
7027
7913
|
try {
|
|
7028
|
-
const response = await apiClient.post(url, {
|
|
7029
|
-
|
|
7914
|
+
const response = await apiClient.post(url, {
|
|
7915
|
+
longAssets: mapAssets(longAssets),
|
|
7916
|
+
shortAssets: mapAssets(shortAssets),
|
|
7917
|
+
}, { headers: { 'Content-Type': 'application/json' } });
|
|
7918
|
+
return {
|
|
7919
|
+
data: response.data,
|
|
7920
|
+
status: response.status,
|
|
7921
|
+
headers: response.headers,
|
|
7922
|
+
};
|
|
7030
7923
|
}
|
|
7031
7924
|
catch (error) {
|
|
7032
7925
|
throw toApiError(error);
|
|
@@ -7038,11 +7931,11 @@ function useWatchlist() {
|
|
|
7038
7931
|
if (!context)
|
|
7039
7932
|
throw new Error('useWatchlist must be used within a PearHyperliquidProvider');
|
|
7040
7933
|
const { apiBaseUrl, isConnected } = context;
|
|
7041
|
-
const
|
|
7934
|
+
const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
|
|
7042
7935
|
const marketData = useMarketDataPayload();
|
|
7043
7936
|
const isLoading = useMemo(() => !marketData && isConnected, [marketData, isConnected]);
|
|
7044
7937
|
const toggle = async (longAssets, shortAssets) => {
|
|
7045
|
-
const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets,
|
|
7938
|
+
const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, hip3Assets);
|
|
7046
7939
|
// Server will push updated market-data over WS; nothing to set here
|
|
7047
7940
|
return resp;
|
|
7048
7941
|
};
|
|
@@ -7296,17 +8189,249 @@ function useAuth() {
|
|
|
7296
8189
|
};
|
|
7297
8190
|
}
|
|
7298
8191
|
|
|
8192
|
+
const useAllUserBalances = () => {
|
|
8193
|
+
const spotState = useUserData((state) => state.spotState);
|
|
8194
|
+
const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
8195
|
+
const rawClearinghouseStates = useHyperliquidData((state) => state.rawClearinghouseStates);
|
|
8196
|
+
const activeAssetData = useHyperliquidData((state) => state.activeAssetData);
|
|
8197
|
+
const { longTokensMetadata, shortTokensMetadata } = useTokenSelectionMetadata();
|
|
8198
|
+
return useMemo(() => {
|
|
8199
|
+
const isLoading = !spotState || !aggregatedClearingHouseState;
|
|
8200
|
+
// Helper function to truncate to 2 decimal places without rounding
|
|
8201
|
+
const truncateToTwoDecimals = (value) => {
|
|
8202
|
+
return Math.floor(value * 100) / 100;
|
|
8203
|
+
};
|
|
8204
|
+
// Get spot balances from spotState
|
|
8205
|
+
let spotUsdcBal = undefined;
|
|
8206
|
+
let spotUsdhBal = undefined;
|
|
8207
|
+
if (spotState) {
|
|
8208
|
+
const balances = spotState.balances || [];
|
|
8209
|
+
for (const balance of balances) {
|
|
8210
|
+
const total = parseFloat(balance.total || '0');
|
|
8211
|
+
if (balance.coin === 'USDC') {
|
|
8212
|
+
spotUsdcBal = truncateToTwoDecimals(total);
|
|
8213
|
+
}
|
|
8214
|
+
if (balance.coin === 'USDH') {
|
|
8215
|
+
spotUsdhBal = truncateToTwoDecimals(total);
|
|
8216
|
+
}
|
|
8217
|
+
}
|
|
8218
|
+
}
|
|
8219
|
+
let availableToTradeUsdhFromAsset = 0;
|
|
8220
|
+
// This activeAssetData only contains data for SELECTED tokens (user's long and short Tokens)
|
|
8221
|
+
// It does NOT contain data for all tokens, so we cannot reliably use it for available to trade as used on hl trade page
|
|
8222
|
+
// so intead, we rely on rawClearinghouseStates which provides market-specific data
|
|
8223
|
+
// if (activeAssetData) {
|
|
8224
|
+
// Object.values(activeAssetData).forEach((assetData) => {
|
|
8225
|
+
// if (!assetData.availableToTrade) return;
|
|
8226
|
+
// const coinSymbol = assetData.coin;
|
|
8227
|
+
// const availableValue = truncateToTwoDecimals(
|
|
8228
|
+
// parseFloat(assetData.availableToTrade[0] || '0'),
|
|
8229
|
+
// );
|
|
8230
|
+
// // Determine collateral type based on market prefix
|
|
8231
|
+
// // HIP3 markets have prefix: "xyz:SYMBOL", "flx:SYMBOL", "vntl:SYMBOL", etc.
|
|
8232
|
+
// if (coinSymbol.includes(':')) {
|
|
8233
|
+
// const prefix = coinSymbol.split(':')[0];
|
|
8234
|
+
// if (prefix === 'xyz') {
|
|
8235
|
+
// // xyz markets use USDC
|
|
8236
|
+
// availableToTradeUsdcFromAsset = availableValue;
|
|
8237
|
+
// } else {
|
|
8238
|
+
// // flx, vntl, hyna and other markets use USDH
|
|
8239
|
+
// availableToTradeUsdhFromAsset = availableValue;
|
|
8240
|
+
// }
|
|
8241
|
+
// } else {
|
|
8242
|
+
// // Regular markets without prefix are automatically USDC
|
|
8243
|
+
// availableToTradeUsdcFromAsset = availableValue;
|
|
8244
|
+
// }
|
|
8245
|
+
// });
|
|
8246
|
+
// }
|
|
8247
|
+
// Calculate USDC available to trade
|
|
8248
|
+
// Priority 1: Use value from activeAssetData if available (> 0)
|
|
8249
|
+
// Priority 2: Calculate from USDC-specific clearinghouseState (empty prefix)
|
|
8250
|
+
let availableToTradeUsdcValue = undefined;
|
|
8251
|
+
if (rawClearinghouseStates) {
|
|
8252
|
+
// Find USDC market (empty prefix)
|
|
8253
|
+
const usdcMarket = rawClearinghouseStates.find(([prefix]) => prefix === '');
|
|
8254
|
+
const usdcState = usdcMarket === null || usdcMarket === void 0 ? void 0 : usdcMarket[1];
|
|
8255
|
+
if (usdcState === null || usdcState === void 0 ? void 0 : usdcState.marginSummary) {
|
|
8256
|
+
const accountValue = parseFloat(usdcState.marginSummary.accountValue || '0');
|
|
8257
|
+
const totalMarginUsed = parseFloat(usdcState.marginSummary.totalMarginUsed || '0');
|
|
8258
|
+
const calculatedValue = Math.max(0, accountValue - totalMarginUsed);
|
|
8259
|
+
availableToTradeUsdcValue = truncateToTwoDecimals(calculatedValue);
|
|
8260
|
+
}
|
|
8261
|
+
}
|
|
8262
|
+
// Calculate USDH available to trade
|
|
8263
|
+
// Priority 1: Use value from activeAssetData if available (> 0)
|
|
8264
|
+
// Priority 2: Use spot USDH balance
|
|
8265
|
+
const availableToTradeUsdhValue = availableToTradeUsdhFromAsset > 0
|
|
8266
|
+
? availableToTradeUsdhFromAsset
|
|
8267
|
+
: spotUsdhBal;
|
|
8268
|
+
return {
|
|
8269
|
+
spotUsdcBalance: spotUsdcBal,
|
|
8270
|
+
availableToTradeUsdc: availableToTradeUsdcValue,
|
|
8271
|
+
spotUsdhBalance: spotUsdhBal,
|
|
8272
|
+
availableToTradeUsdh: availableToTradeUsdhValue,
|
|
8273
|
+
isLoading,
|
|
8274
|
+
};
|
|
8275
|
+
}, [
|
|
8276
|
+
spotState,
|
|
8277
|
+
aggregatedClearingHouseState,
|
|
8278
|
+
rawClearinghouseStates,
|
|
8279
|
+
activeAssetData,
|
|
8280
|
+
longTokensMetadata,
|
|
8281
|
+
shortTokensMetadata,
|
|
8282
|
+
]);
|
|
8283
|
+
};
|
|
8284
|
+
|
|
8285
|
+
/**
|
|
8286
|
+
* Sync external fills into Pear Hyperliquid service (POST /sync/fills)
|
|
8287
|
+
*/
|
|
8288
|
+
const syncFills = async (baseUrl, payload) => {
|
|
8289
|
+
const url = joinUrl(baseUrl, '/sync/fills');
|
|
8290
|
+
try {
|
|
8291
|
+
const response = await apiClient.post(url, payload, {
|
|
8292
|
+
headers: { 'Content-Type': 'application/json' },
|
|
8293
|
+
timeout: 30000,
|
|
8294
|
+
});
|
|
8295
|
+
return { data: response.data, status: response.status, headers: response.headers };
|
|
8296
|
+
}
|
|
8297
|
+
catch (error) {
|
|
8298
|
+
throw toApiError(error);
|
|
8299
|
+
}
|
|
8300
|
+
};
|
|
8301
|
+
/**
|
|
8302
|
+
* Convenience: fetch user fills from HyperLiquid, then sync them to Pear backend
|
|
8303
|
+
*/
|
|
8304
|
+
const syncUserFillsFromHyperliquid = async (baseUrl, user, aggregateByTime = true, lastSyncAt = null, assetPositions) => {
|
|
8305
|
+
const firstStartTime = lastSyncAt ? Number(lastSyncAt) + 1 : 0;
|
|
8306
|
+
const allFills = [];
|
|
8307
|
+
const seenTids = new Set();
|
|
8308
|
+
let startTime = firstStartTime;
|
|
8309
|
+
let batchSize = 0;
|
|
8310
|
+
do {
|
|
8311
|
+
const { data: batch } = await fetchUserFillsFromHyperliquid(user, startTime, aggregateByTime);
|
|
8312
|
+
batchSize = batch.length;
|
|
8313
|
+
for (const fill of batch) {
|
|
8314
|
+
const tid = fill.tid;
|
|
8315
|
+
if (tid === undefined)
|
|
8316
|
+
continue;
|
|
8317
|
+
if (!seenTids.has(tid)) {
|
|
8318
|
+
seenTids.add(tid);
|
|
8319
|
+
allFills.push(fill);
|
|
8320
|
+
}
|
|
8321
|
+
}
|
|
8322
|
+
if (batchSize === 2000) {
|
|
8323
|
+
const last = batch[batch.length - 1];
|
|
8324
|
+
startTime = last.time;
|
|
8325
|
+
}
|
|
8326
|
+
} while (batchSize === 2000);
|
|
8327
|
+
startTime = firstStartTime;
|
|
8328
|
+
batchSize = 0;
|
|
8329
|
+
do {
|
|
8330
|
+
const { data: twapBatch } = await fetchUserTwapSliceFillsByTime(user, startTime, aggregateByTime);
|
|
8331
|
+
batchSize = twapBatch.length;
|
|
8332
|
+
for (const item of twapBatch) {
|
|
8333
|
+
const fill = item.fill;
|
|
8334
|
+
const tid = fill.tid;
|
|
8335
|
+
if (tid === undefined)
|
|
8336
|
+
continue;
|
|
8337
|
+
if (!seenTids.has(tid)) {
|
|
8338
|
+
seenTids.add(tid);
|
|
8339
|
+
allFills.push(fill);
|
|
8340
|
+
}
|
|
8341
|
+
}
|
|
8342
|
+
if (batchSize === 2000) {
|
|
8343
|
+
const last = twapBatch[twapBatch.length - 1];
|
|
8344
|
+
startTime = last.fill.time;
|
|
8345
|
+
}
|
|
8346
|
+
} while (batchSize === 2000);
|
|
8347
|
+
const sortedFills = [...allFills].sort((a, b) => Number(a.time) - Number(b.time));
|
|
8348
|
+
return syncFills(baseUrl, { user, fills: sortedFills, assetPositions });
|
|
8349
|
+
};
|
|
8350
|
+
|
|
8351
|
+
/**
|
|
8352
|
+
* Provides a callback to sync user fills whenever the native WebSocket emits a userFills event.
|
|
8353
|
+
*/
|
|
8354
|
+
function useHyperliquidUserFills(options) {
|
|
8355
|
+
const { baseUrl, address: addressOverride, aggregateByTime = true } = options;
|
|
8356
|
+
const [lastRunAt, setLastRunAt] = useState(null);
|
|
8357
|
+
const [lastResult, setLastResult] = useState(null);
|
|
8358
|
+
const [error, setError] = useState(null);
|
|
8359
|
+
const [isSyncing, setIsSyncing] = useState(false);
|
|
8360
|
+
const mountedRef = useRef(true);
|
|
8361
|
+
const runningRef = useRef(null);
|
|
8362
|
+
const enabled = useUserData((state) => state.isAuthenticated);
|
|
8363
|
+
useEffect(() => {
|
|
8364
|
+
mountedRef.current = true;
|
|
8365
|
+
return () => {
|
|
8366
|
+
mountedRef.current = false;
|
|
8367
|
+
};
|
|
8368
|
+
}, []);
|
|
8369
|
+
const handleUserFillsEvent = useCallback(async () => {
|
|
8370
|
+
var _a;
|
|
8371
|
+
const userState = useUserData.getState();
|
|
8372
|
+
const currentAddress = userState.address || addressOverride;
|
|
8373
|
+
const lastSyncedAt = (_a = userState.accountSummary) === null || _a === void 0 ? void 0 : _a.lastSyncedAt;
|
|
8374
|
+
if (!(enabled && currentAddress && baseUrl))
|
|
8375
|
+
return;
|
|
8376
|
+
if (runningRef.current)
|
|
8377
|
+
return;
|
|
8378
|
+
if (!userState.accountSummary)
|
|
8379
|
+
return;
|
|
8380
|
+
const clearinghouseState = useHyperliquidData.getState().aggregatedClearingHouseState;
|
|
8381
|
+
if (!(clearinghouseState === null || clearinghouseState === void 0 ? void 0 : clearinghouseState.assetPositions))
|
|
8382
|
+
return;
|
|
8383
|
+
setIsSyncing(true);
|
|
8384
|
+
setError(null);
|
|
8385
|
+
const promise = (async () => {
|
|
8386
|
+
var _a;
|
|
8387
|
+
try {
|
|
8388
|
+
const { data } = await syncUserFillsFromHyperliquid(baseUrl, currentAddress, aggregateByTime, lastSyncedAt, clearinghouseState.assetPositions);
|
|
8389
|
+
if (!mountedRef.current)
|
|
8390
|
+
return;
|
|
8391
|
+
setLastResult(data);
|
|
8392
|
+
setLastRunAt(Date.now());
|
|
8393
|
+
}
|
|
8394
|
+
catch (e) {
|
|
8395
|
+
if (!mountedRef.current)
|
|
8396
|
+
return;
|
|
8397
|
+
setError((_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : 'Failed to sync user fills');
|
|
8398
|
+
}
|
|
8399
|
+
finally {
|
|
8400
|
+
if (mountedRef.current)
|
|
8401
|
+
setIsSyncing(false);
|
|
8402
|
+
runningRef.current = null;
|
|
8403
|
+
}
|
|
8404
|
+
})();
|
|
8405
|
+
runningRef.current = promise;
|
|
8406
|
+
await promise;
|
|
8407
|
+
}, [baseUrl, addressOverride, aggregateByTime, enabled]);
|
|
8408
|
+
return {
|
|
8409
|
+
lastRunAt,
|
|
8410
|
+
lastResult,
|
|
8411
|
+
error,
|
|
8412
|
+
isSyncing,
|
|
8413
|
+
handleUserFillsEvent,
|
|
8414
|
+
};
|
|
8415
|
+
}
|
|
8416
|
+
|
|
7299
8417
|
const PearHyperliquidContext = createContext(undefined);
|
|
7300
8418
|
/**
|
|
7301
8419
|
* React Provider for PearHyperliquidClient
|
|
7302
8420
|
*/
|
|
7303
|
-
const PearHyperliquidProvider = ({ children, apiBaseUrl =
|
|
8421
|
+
const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearprotocol.io', clientId = 'PEARPROTOCOLUI', wsUrl = 'wss://hl-ui.pearprotocol.io/ws', }) => {
|
|
7304
8422
|
const address = useUserData((s) => s.address);
|
|
7305
8423
|
const setAddress = useUserData((s) => s.setAddress);
|
|
7306
8424
|
const perpsMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
7307
8425
|
const setPerpMetaAssets = useHyperliquidData((state) => state.setPerpMetaAssets);
|
|
7308
|
-
const
|
|
8426
|
+
const setAllPerpMetaAssets = useHyperliquidData((state) => state.setAllPerpMetaAssets);
|
|
8427
|
+
const setHip3Assets = useHyperliquidData((state) => state.setHip3Assets);
|
|
8428
|
+
const setHip3MarketPrefixes = useHyperliquidData((state) => state.setHip3MarketPrefixes);
|
|
7309
8429
|
const websocketsEnabled = useMemo(() => Array.isArray(perpsMetaAssets) && perpsMetaAssets.length > 0, [perpsMetaAssets]);
|
|
8430
|
+
const { handleUserFillsEvent } = useHyperliquidUserFills({
|
|
8431
|
+
baseUrl: apiBaseUrl,
|
|
8432
|
+
address,
|
|
8433
|
+
aggregateByTime: true,
|
|
8434
|
+
});
|
|
7310
8435
|
const { isConnected, lastError } = useHyperliquidWebSocket({
|
|
7311
8436
|
wsUrl,
|
|
7312
8437
|
address,
|
|
@@ -7315,43 +8440,113 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearpro
|
|
|
7315
8440
|
const { isConnected: nativeIsConnected, lastError: nativeLastError } = useHyperliquidNativeWebSocket({
|
|
7316
8441
|
address,
|
|
7317
8442
|
enabled: websocketsEnabled,
|
|
8443
|
+
onUserFills: handleUserFillsEvent,
|
|
7318
8444
|
});
|
|
7319
8445
|
useEffect(() => {
|
|
7320
8446
|
if (perpsMetaAssets === null) {
|
|
7321
8447
|
fetchAllPerpMetas()
|
|
7322
8448
|
.then((res) => {
|
|
7323
|
-
|
|
7324
|
-
const
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
const
|
|
7329
|
-
const
|
|
7330
|
-
|
|
7331
|
-
const
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
const
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
8449
|
+
const assetToMarkets = new Map();
|
|
8450
|
+
const marketPrefixes = new Map();
|
|
8451
|
+
const FILTERED_PREFIXES = ['hyna'];
|
|
8452
|
+
// Group assets by market prefix to match WebSocket flattening order
|
|
8453
|
+
// WebSocket sends in order: "", "flx", "hyna", "vntl", "xyz" (we filter out hyna)
|
|
8454
|
+
const assetsByPrefix = new Map();
|
|
8455
|
+
const allAssetsByPrefix = new Map();
|
|
8456
|
+
res.data.forEach((item) => {
|
|
8457
|
+
const collateralToken = item.collateralToken === 360 ? 'USDH' : 'USDC';
|
|
8458
|
+
item.universe.forEach((asset) => {
|
|
8459
|
+
var _a;
|
|
8460
|
+
const [maybePrefix, maybeMarket] = asset.name.split(':');
|
|
8461
|
+
if (maybeMarket) {
|
|
8462
|
+
// HIP3 asset with market prefix
|
|
8463
|
+
const prefix = maybePrefix.toLowerCase();
|
|
8464
|
+
const displayName = maybeMarket;
|
|
8465
|
+
const fullName = `${prefix}:${displayName}`;
|
|
8466
|
+
marketPrefixes.set(fullName, prefix);
|
|
8467
|
+
if (!FILTERED_PREFIXES.includes(prefix)) {
|
|
8468
|
+
const existingMarkets = (_a = assetToMarkets.get(displayName)) !== null && _a !== void 0 ? _a : [];
|
|
8469
|
+
if (!existingMarkets.includes(fullName)) {
|
|
8470
|
+
assetToMarkets.set(displayName, [
|
|
8471
|
+
...existingMarkets,
|
|
8472
|
+
fullName,
|
|
8473
|
+
]);
|
|
8474
|
+
}
|
|
8475
|
+
}
|
|
8476
|
+
const assetWithMeta = {
|
|
8477
|
+
...asset,
|
|
8478
|
+
name: displayName,
|
|
8479
|
+
marketPrefix: prefix,
|
|
8480
|
+
collateralToken,
|
|
8481
|
+
};
|
|
8482
|
+
// Group by market prefix
|
|
8483
|
+
const allList = allAssetsByPrefix.get(prefix) || [];
|
|
8484
|
+
allList.push(assetWithMeta);
|
|
8485
|
+
allAssetsByPrefix.set(prefix, allList);
|
|
8486
|
+
if (!FILTERED_PREFIXES.includes(prefix)) {
|
|
8487
|
+
const cleanedList = assetsByPrefix.get(prefix) || [];
|
|
8488
|
+
cleanedList.push(assetWithMeta);
|
|
8489
|
+
assetsByPrefix.set(prefix, cleanedList);
|
|
8490
|
+
}
|
|
8491
|
+
}
|
|
8492
|
+
else {
|
|
8493
|
+
// Default market asset (no prefix)
|
|
8494
|
+
const assetWithMeta = {
|
|
8495
|
+
...asset,
|
|
8496
|
+
collateralToken,
|
|
8497
|
+
};
|
|
8498
|
+
// Add to default market group ("")
|
|
8499
|
+
const defaultList = assetsByPrefix.get('') || [];
|
|
8500
|
+
defaultList.push(assetWithMeta);
|
|
8501
|
+
assetsByPrefix.set('', defaultList);
|
|
8502
|
+
const allDefaultList = allAssetsByPrefix.get('') || [];
|
|
8503
|
+
allDefaultList.push(assetWithMeta);
|
|
8504
|
+
allAssetsByPrefix.set('', allDefaultList);
|
|
8505
|
+
}
|
|
8506
|
+
});
|
|
7341
8507
|
});
|
|
7342
|
-
|
|
8508
|
+
// Flatten in consistent order: default market first, then HIP3 markets alphabetically
|
|
8509
|
+
// This ensures both REST API and WebSocket data align properly
|
|
8510
|
+
const cleanedPrefixes = Array.from(assetsByPrefix.keys()).sort((a, b) => {
|
|
8511
|
+
// Empty prefix (default market) always comes first
|
|
8512
|
+
if (a === '' && b !== '')
|
|
8513
|
+
return -1;
|
|
8514
|
+
if (a !== '' && b === '')
|
|
8515
|
+
return 1;
|
|
8516
|
+
// HIP3 markets sorted alphabetically
|
|
8517
|
+
return a.localeCompare(b);
|
|
8518
|
+
});
|
|
8519
|
+
const allPrefixes = Array.from(allAssetsByPrefix.keys()).sort((a, b) => {
|
|
8520
|
+
if (a === '' && b !== '')
|
|
8521
|
+
return -1;
|
|
8522
|
+
if (a !== '' && b === '')
|
|
8523
|
+
return 1;
|
|
8524
|
+
return a.localeCompare(b);
|
|
8525
|
+
});
|
|
8526
|
+
const cleanedPerpMetas = [];
|
|
8527
|
+
const allPerpMetas = [];
|
|
8528
|
+
cleanedPrefixes.forEach((prefix) => {
|
|
8529
|
+
const assets = assetsByPrefix.get(prefix) || [];
|
|
8530
|
+
cleanedPerpMetas.push(...assets);
|
|
8531
|
+
});
|
|
8532
|
+
allPrefixes.forEach((prefix) => {
|
|
8533
|
+
const assets = allAssetsByPrefix.get(prefix) || [];
|
|
8534
|
+
allPerpMetas.push(...assets);
|
|
8535
|
+
});
|
|
8536
|
+
setHip3Assets(assetToMarkets);
|
|
8537
|
+
setHip3MarketPrefixes(marketPrefixes);
|
|
7343
8538
|
setPerpMetaAssets(cleanedPerpMetas);
|
|
8539
|
+
setAllPerpMetaAssets(allPerpMetas);
|
|
7344
8540
|
})
|
|
7345
8541
|
.catch(() => { });
|
|
7346
8542
|
}
|
|
7347
|
-
}, [
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
});
|
|
8543
|
+
}, [
|
|
8544
|
+
perpsMetaAssets,
|
|
8545
|
+
setPerpMetaAssets,
|
|
8546
|
+
setAllPerpMetaAssets,
|
|
8547
|
+
setHip3Assets,
|
|
8548
|
+
setHip3MarketPrefixes,
|
|
8549
|
+
]);
|
|
7355
8550
|
const contextValue = useMemo(() => ({
|
|
7356
8551
|
// Config
|
|
7357
8552
|
clientId,
|
|
@@ -7383,7 +8578,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearpro
|
|
|
7383
8578
|
function usePearHyperliquid() {
|
|
7384
8579
|
const ctx = useContext(PearHyperliquidContext);
|
|
7385
8580
|
if (!ctx)
|
|
7386
|
-
throw new Error(
|
|
8581
|
+
throw new Error('usePearHyperliquid must be used within a PearHyperliquidProvider');
|
|
7387
8582
|
return ctx;
|
|
7388
8583
|
}
|
|
7389
8584
|
|
|
@@ -7474,4 +8669,129 @@ function mapCandleIntervalToTradingViewInterval(interval) {
|
|
|
7474
8669
|
}
|
|
7475
8670
|
}
|
|
7476
8671
|
|
|
7477
|
-
|
|
8672
|
+
/**
|
|
8673
|
+
* Helper functions to safely extract values from order parameters
|
|
8674
|
+
* based on the order type and parameter structure.
|
|
8675
|
+
*/
|
|
8676
|
+
/**
|
|
8677
|
+
* Get leverage from order parameters (available for Market, Trigger, Twap, Ladder orders)
|
|
8678
|
+
*/
|
|
8679
|
+
function getOrderLeverage(order) {
|
|
8680
|
+
const p = order.parameters;
|
|
8681
|
+
if ('leverage' in p && p.leverage !== undefined) {
|
|
8682
|
+
return p.leverage;
|
|
8683
|
+
}
|
|
8684
|
+
return undefined;
|
|
8685
|
+
}
|
|
8686
|
+
/**
|
|
8687
|
+
* Get USD value from order parameters (available for Market, Trigger, Twap, Ladder orders)
|
|
8688
|
+
*/
|
|
8689
|
+
function getOrderUsdValue(order) {
|
|
8690
|
+
const p = order.parameters;
|
|
8691
|
+
if ('usdValue' in p) {
|
|
8692
|
+
return p.usdValue;
|
|
8693
|
+
}
|
|
8694
|
+
return undefined;
|
|
8695
|
+
}
|
|
8696
|
+
/**
|
|
8697
|
+
* Get trigger value from order parameters (available for TP/SL and Trigger orders)
|
|
8698
|
+
*/
|
|
8699
|
+
function getOrderTriggerValue(order) {
|
|
8700
|
+
const p = order.parameters;
|
|
8701
|
+
if ('triggerValue' in p) {
|
|
8702
|
+
return p.triggerValue;
|
|
8703
|
+
}
|
|
8704
|
+
return undefined;
|
|
8705
|
+
}
|
|
8706
|
+
/**
|
|
8707
|
+
* Get TP/SL trigger type from order parameters (only for TP/SL orders)
|
|
8708
|
+
*/
|
|
8709
|
+
function getOrderTpSlTriggerType(order) {
|
|
8710
|
+
if (order.orderType === 'TP' || order.orderType === 'SL') {
|
|
8711
|
+
const p = order.parameters;
|
|
8712
|
+
return p.triggerType;
|
|
8713
|
+
}
|
|
8714
|
+
return undefined;
|
|
8715
|
+
}
|
|
8716
|
+
/**
|
|
8717
|
+
* Get trigger type from order parameters (for Trigger orders)
|
|
8718
|
+
*/
|
|
8719
|
+
function getOrderTriggerType(order) {
|
|
8720
|
+
if (order.orderType === 'TRIGGER') {
|
|
8721
|
+
const p = order.parameters;
|
|
8722
|
+
return p.triggerType;
|
|
8723
|
+
}
|
|
8724
|
+
return undefined;
|
|
8725
|
+
}
|
|
8726
|
+
/**
|
|
8727
|
+
* Get order direction from order parameters (for Trigger orders)
|
|
8728
|
+
*/
|
|
8729
|
+
function getOrderDirection(order) {
|
|
8730
|
+
if (order.orderType === 'TRIGGER') {
|
|
8731
|
+
const p = order.parameters;
|
|
8732
|
+
return p.direction;
|
|
8733
|
+
}
|
|
8734
|
+
return undefined;
|
|
8735
|
+
}
|
|
8736
|
+
/**
|
|
8737
|
+
* Get reduce only flag from order parameters (available for all order types)
|
|
8738
|
+
*/
|
|
8739
|
+
function getOrderReduceOnly(order) {
|
|
8740
|
+
var _a;
|
|
8741
|
+
const p = order.parameters;
|
|
8742
|
+
if ('reduceOnly' in p) {
|
|
8743
|
+
return (_a = p.reduceOnly) !== null && _a !== void 0 ? _a : false;
|
|
8744
|
+
}
|
|
8745
|
+
return false;
|
|
8746
|
+
}
|
|
8747
|
+
/**
|
|
8748
|
+
* Get TWAP duration from order parameters (only for TWAP orders)
|
|
8749
|
+
*/
|
|
8750
|
+
function getOrderTwapDuration(order) {
|
|
8751
|
+
if (order.orderType === 'TWAP') {
|
|
8752
|
+
const p = order.parameters;
|
|
8753
|
+
return p.duration;
|
|
8754
|
+
}
|
|
8755
|
+
return undefined;
|
|
8756
|
+
}
|
|
8757
|
+
/**
|
|
8758
|
+
* Get ladder config from order parameters (only for Ladder orders)
|
|
8759
|
+
*/
|
|
8760
|
+
function getOrderLadderConfig(order) {
|
|
8761
|
+
if (order.orderType === 'LADDER') {
|
|
8762
|
+
const p = order.parameters;
|
|
8763
|
+
return {
|
|
8764
|
+
ratioStart: p.ratioStart,
|
|
8765
|
+
ratioEnd: p.ratioEnd,
|
|
8766
|
+
numberOfLevels: p.numberOfLevels,
|
|
8767
|
+
};
|
|
8768
|
+
}
|
|
8769
|
+
return undefined;
|
|
8770
|
+
}
|
|
8771
|
+
/**
|
|
8772
|
+
* Check if the order is a BTC Dominance trigger order
|
|
8773
|
+
*/
|
|
8774
|
+
function isBtcDomOrder(order) {
|
|
8775
|
+
if (order.orderType === 'TRIGGER') {
|
|
8776
|
+
const p = order.parameters;
|
|
8777
|
+
return p.triggerType === 'BTC_DOM';
|
|
8778
|
+
}
|
|
8779
|
+
return false;
|
|
8780
|
+
}
|
|
8781
|
+
/**
|
|
8782
|
+
* Get trailing info from TP/SL order parameters
|
|
8783
|
+
*/
|
|
8784
|
+
function getOrderTrailingInfo(order) {
|
|
8785
|
+
var _a;
|
|
8786
|
+
if (order.orderType === 'TP' || order.orderType === 'SL') {
|
|
8787
|
+
const p = order.parameters;
|
|
8788
|
+
return {
|
|
8789
|
+
isTrailing: (_a = p.isTrailing) !== null && _a !== void 0 ? _a : false,
|
|
8790
|
+
trailingDeltaValue: p.trailingDeltaValue,
|
|
8791
|
+
trailingActivationValue: p.trailingActivationValue,
|
|
8792
|
+
};
|
|
8793
|
+
}
|
|
8794
|
+
return undefined;
|
|
8795
|
+
}
|
|
8796
|
+
|
|
8797
|
+
export { AccountSummaryCalculator, ConflictDetector, MAX_ASSETS_PER_LEG, MINIMUM_ASSET_USD_VALUE, MaxAssetsPerLegError, MinimumPositionSizeError, PearHyperliquidProvider, TokenMetadataExtractor, adjustAdvancePosition, adjustOrder, adjustPosition, calculateMinimumPositionValue, calculateWeightedRatio, cancelOrder, cancelTwap, cancelTwapOrder, closeAllPositions, closePosition, computeBasketCandles, createCandleLookups, createPosition, executeSpotOrder, getAvailableMarkets, getCompleteTimestamps, getKalshiMarkets, getMarketPrefix, getOrderDirection, getOrderLadderConfig, getOrderLeverage, getOrderReduceOnly, getOrderTpSlTriggerType, getOrderTrailingInfo, getOrderTriggerType, getOrderTriggerValue, getOrderTwapDuration, getOrderUsdValue, getPortfolio, isBtcDomOrder, isHip3Market, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, toBackendSymbol, toBackendSymbolWithMarket, toDisplaySymbol, toggleWatchlist, updateLeverage, updateRiskParameters, useAccountSummary, useActiveBaskets, useAgentWallet, useAllBaskets, useAllUserBalances, useAuth, useBasketCandles, useFindBasket, useHighlightedBaskets, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidNativeWebSocket, useHyperliquidUserFills, useHyperliquidWebSocket, useMarketData, useMarketDataAllPayload, useMarketDataPayload, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePerpMetaAssets, usePortfolio, usePosition, useSpotOrder, useTokenSelectionMetadata, useTopGainers, useTopLosers, useTradeHistories, useTwap, useUserSelection, useWatchlist, useWatchlistBaskets, useWebData, validateMaxAssetsPerLeg, validateMinimumAssetSize, validatePositionSize };
|