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