@pear-protocol/hyperliquid-sdk 0.0.65 → 0.0.66-usdh-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clients/hyperliquid.d.ts +1 -1
- package/dist/clients/orders.d.ts +41 -0
- package/dist/clients/positions.d.ts +3 -2
- package/dist/clients/watchlist.d.ts +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useMarketData.d.ts +9 -7
- package/dist/hooks/useSpotBalances.d.ts +7 -0
- 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 +239 -41
- package/dist/index.js +1353 -272
- package/dist/provider.d.ts +1 -1
- package/dist/store/tokenSelectionMetadataStore.d.ts +1 -1
- package/dist/store/userSelection.d.ts +2 -1
- package/dist/types.d.ts +74 -20
- package/dist/utils/position-validator.d.ts +20 -0
- package/dist/utils/symbol-translator.d.ts +32 -3
- package/dist/utils/token-metadata-extractor.d.ts +9 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ const useUserData = create((set) => ({
|
|
|
23
23
|
twapDetails: null,
|
|
24
24
|
notifications: null,
|
|
25
25
|
userExtraAgents: null,
|
|
26
|
+
spotState: null,
|
|
26
27
|
setAccessToken: (token) => set({ accessToken: token }),
|
|
27
28
|
setRefreshToken: (token) => set({ refreshToken: token }),
|
|
28
29
|
setIsAuthenticated: (value) => set({ isAuthenticated: value }),
|
|
@@ -43,6 +44,7 @@ const useUserData = create((set) => ({
|
|
|
43
44
|
setAccountSummary: (value) => set({ accountSummary: value }),
|
|
44
45
|
setTwapDetails: (value) => set({ twapDetails: value }),
|
|
45
46
|
setNotifications: (value) => set({ notifications: value }),
|
|
47
|
+
setSpotState: (value) => set({ spotState: value }),
|
|
46
48
|
clean: () => set({
|
|
47
49
|
accessToken: null,
|
|
48
50
|
refreshToken: null,
|
|
@@ -54,6 +56,7 @@ const useUserData = create((set) => ({
|
|
|
54
56
|
accountSummary: null,
|
|
55
57
|
twapDetails: null,
|
|
56
58
|
notifications: null,
|
|
59
|
+
spotState: null,
|
|
57
60
|
}),
|
|
58
61
|
setUserExtraAgents: (value) => set({ userExtraAgents: value }),
|
|
59
62
|
}));
|
|
@@ -71,18 +74,71 @@ const useMarketData = create((set) => ({
|
|
|
71
74
|
* Convert a full/prefixed symbol (e.g., "xyz:XYZ100") to a display symbol (e.g., "XYZ100").
|
|
72
75
|
*/
|
|
73
76
|
function toDisplaySymbol(symbol) {
|
|
74
|
-
const parts = symbol.split(
|
|
77
|
+
const parts = symbol.split(':');
|
|
75
78
|
return parts.length > 1 ? parts.slice(-1)[0] : symbol;
|
|
76
79
|
}
|
|
77
80
|
/**
|
|
78
81
|
* Convert a display symbol back to backend form using a provided map.
|
|
79
82
|
* If mapping is missing, returns the original symbol.
|
|
80
|
-
*
|
|
81
|
-
* @param
|
|
83
|
+
* For multi-market assets, returns the first available market.
|
|
84
|
+
* @param displaySymbol e.g., "TSLA"
|
|
85
|
+
* @param hip3Assets map of display -> all full market names (e.g., "TSLA" -> ["xyz:TSLA", "flx:TSLA"])
|
|
82
86
|
*/
|
|
83
|
-
function toBackendSymbol(displaySymbol,
|
|
87
|
+
function toBackendSymbol(displaySymbol, hip3Assets) {
|
|
88
|
+
const markets = hip3Assets.get(displaySymbol);
|
|
89
|
+
// Return first market if available, otherwise return original symbol
|
|
90
|
+
return markets && markets.length > 0 ? markets[0] : displaySymbol;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Convert a display symbol to backend form for a specific market prefix.
|
|
94
|
+
* This is useful when an asset is available on multiple markets (e.g., xyz:TSLA and flx:TSLA).
|
|
95
|
+
* @param displaySymbol e.g., "TSLA"
|
|
96
|
+
* @param marketPrefix e.g., "xyz" or "flx"
|
|
97
|
+
* @param hip3Assets map of display -> all full market names
|
|
98
|
+
* @returns Full market name if found, null if prefix not specified for multi-market asset, otherwise displaySymbol with prefix
|
|
99
|
+
*/
|
|
100
|
+
function toBackendSymbolWithMarket(displaySymbol, marketPrefix, hip3Assets) {
|
|
101
|
+
const availableMarkets = hip3Assets.get(displaySymbol);
|
|
102
|
+
if (!availableMarkets || availableMarkets.length === 0) {
|
|
103
|
+
// Not a HIP-3 asset, return as-is or with prefix if provided
|
|
104
|
+
return marketPrefix ? `${marketPrefix}:${displaySymbol}` : displaySymbol;
|
|
105
|
+
}
|
|
106
|
+
if (marketPrefix) {
|
|
107
|
+
// Find the market with the specified prefix
|
|
108
|
+
const targetMarket = availableMarkets.find((market) => market.toLowerCase().startsWith(`${marketPrefix.toLowerCase()}:`));
|
|
109
|
+
if (targetMarket) {
|
|
110
|
+
return targetMarket;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// No prefix specified or not found, return null to force explicit market selection
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get all available markets for a display symbol.
|
|
118
|
+
* @param displaySymbol e.g., "TSLA"
|
|
119
|
+
* @param hip3Assets map of display -> all full market names
|
|
120
|
+
* @returns Array of full market names, e.g., ["xyz:TSLA", "flx:TSLA"]
|
|
121
|
+
*/
|
|
122
|
+
function getAvailableMarkets(displaySymbol, hip3Assets) {
|
|
84
123
|
var _a;
|
|
85
|
-
return (_a =
|
|
124
|
+
return (_a = hip3Assets.get(displaySymbol)) !== null && _a !== void 0 ? _a : [];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Extract the market prefix from a full market name.
|
|
128
|
+
* @param fullSymbol e.g., "xyz:TSLA"
|
|
129
|
+
* @returns The prefix (e.g., "xyz") or undefined if no prefix
|
|
130
|
+
*/
|
|
131
|
+
function getMarketPrefix(fullSymbol) {
|
|
132
|
+
const parts = fullSymbol.split(':');
|
|
133
|
+
return parts.length > 1 ? parts[0] : undefined;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if a symbol is a HIP-3 market (has a prefix).
|
|
137
|
+
* @param symbol e.g., "xyz:TSLA" or "TSLA"
|
|
138
|
+
* @returns true if the symbol has a market prefix
|
|
139
|
+
*/
|
|
140
|
+
function isHip3Market(symbol) {
|
|
141
|
+
return symbol.includes(':');
|
|
86
142
|
}
|
|
87
143
|
|
|
88
144
|
const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
@@ -99,7 +155,8 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
99
155
|
try {
|
|
100
156
|
const message = JSON.parse(event.data);
|
|
101
157
|
// Handle subscription responses (only if they don't have channel data)
|
|
102
|
-
if (('success' in message || 'error' in message) &&
|
|
158
|
+
if (('success' in message || 'error' in message) &&
|
|
159
|
+
!('channel' in message)) {
|
|
103
160
|
if (message.error) {
|
|
104
161
|
setLastError(message.error);
|
|
105
162
|
}
|
|
@@ -118,12 +175,21 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
118
175
|
switch (dataMessage.channel) {
|
|
119
176
|
case 'trade-histories':
|
|
120
177
|
{
|
|
178
|
+
const mapAsset = (a) => {
|
|
179
|
+
var _a, _b;
|
|
180
|
+
const extractedPrefix = getMarketPrefix(a.coin);
|
|
181
|
+
return {
|
|
182
|
+
...a,
|
|
183
|
+
coin: toDisplaySymbol(a.coin),
|
|
184
|
+
marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
|
|
185
|
+
};
|
|
186
|
+
};
|
|
121
187
|
const list = dataMessage.data.map((item) => {
|
|
122
188
|
var _a, _b;
|
|
123
189
|
return ({
|
|
124
190
|
...item,
|
|
125
|
-
closedLongAssets: item.closedLongAssets.map(
|
|
126
|
-
closedShortAssets: item.closedShortAssets.map(
|
|
191
|
+
closedLongAssets: item.closedLongAssets.map(mapAsset),
|
|
192
|
+
closedShortAssets: item.closedShortAssets.map(mapAsset),
|
|
127
193
|
positionLongAssets: (_a = item.positionLongAssets) === null || _a === void 0 ? void 0 : _a.map((a) => toDisplaySymbol(a)),
|
|
128
194
|
positionShortAssets: (_b = item.positionShortAssets) === null || _b === void 0 ? void 0 : _b.map((a) => toDisplaySymbol(a)),
|
|
129
195
|
});
|
|
@@ -133,10 +199,19 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
133
199
|
break;
|
|
134
200
|
case 'open-positions':
|
|
135
201
|
{
|
|
202
|
+
const enrichAsset = (a) => {
|
|
203
|
+
var _a, _b;
|
|
204
|
+
const extractedPrefix = getMarketPrefix(a.coin);
|
|
205
|
+
return {
|
|
206
|
+
...a,
|
|
207
|
+
coin: toDisplaySymbol(a.coin),
|
|
208
|
+
marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
|
|
209
|
+
};
|
|
210
|
+
};
|
|
136
211
|
const list = dataMessage.data.map((pos) => ({
|
|
137
212
|
...pos,
|
|
138
|
-
longAssets: pos.longAssets.map(
|
|
139
|
-
shortAssets: pos.shortAssets.map(
|
|
213
|
+
longAssets: pos.longAssets.map(enrichAsset),
|
|
214
|
+
shortAssets: pos.shortAssets.map(enrichAsset),
|
|
140
215
|
}));
|
|
141
216
|
setRawOpenPositions(list);
|
|
142
217
|
}
|
|
@@ -145,8 +220,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
145
220
|
{
|
|
146
221
|
const list = dataMessage.data.map((order) => ({
|
|
147
222
|
...order,
|
|
148
|
-
longAssets: order.longAssets.map((a) => ({
|
|
149
|
-
|
|
223
|
+
longAssets: order.longAssets.map((a) => ({
|
|
224
|
+
...a,
|
|
225
|
+
asset: toDisplaySymbol(a.asset),
|
|
226
|
+
})),
|
|
227
|
+
shortAssets: order.shortAssets.map((a) => ({
|
|
228
|
+
...a,
|
|
229
|
+
asset: toDisplaySymbol(a.asset),
|
|
230
|
+
})),
|
|
150
231
|
}));
|
|
151
232
|
setOpenOrders(list);
|
|
152
233
|
}
|
|
@@ -156,10 +237,20 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
156
237
|
break;
|
|
157
238
|
case 'twap-details':
|
|
158
239
|
{
|
|
240
|
+
const mapTwapAsset = (a) => {
|
|
241
|
+
var _a, _b, _c;
|
|
242
|
+
const extractedPrefix = getMarketPrefix(a.asset);
|
|
243
|
+
return {
|
|
244
|
+
...a,
|
|
245
|
+
asset: toDisplaySymbol(a.asset),
|
|
246
|
+
marketPrefix: (_b = (_a = a.marketPrefix) !== null && _a !== void 0 ? _a : extractedPrefix) !== null && _b !== void 0 ? _b : null,
|
|
247
|
+
collateralToken: (_c = a.collateralToken) !== null && _c !== void 0 ? _c : undefined,
|
|
248
|
+
};
|
|
249
|
+
};
|
|
159
250
|
const list = dataMessage.data.map((twap) => ({
|
|
160
251
|
...twap,
|
|
161
|
-
longAssets: twap.longAssets.map(
|
|
162
|
-
shortAssets: twap.shortAssets.map(
|
|
252
|
+
longAssets: twap.longAssets.map(mapTwapAsset),
|
|
253
|
+
shortAssets: twap.shortAssets.map(mapTwapAsset),
|
|
163
254
|
}));
|
|
164
255
|
setTwapDetails(list);
|
|
165
256
|
}
|
|
@@ -172,8 +263,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
172
263
|
const md = dataMessage.data;
|
|
173
264
|
const mapGroup = (g) => ({
|
|
174
265
|
...g,
|
|
175
|
-
longAssets: g.longAssets.map((a) => ({
|
|
176
|
-
|
|
266
|
+
longAssets: g.longAssets.map((a) => ({
|
|
267
|
+
...a,
|
|
268
|
+
asset: toDisplaySymbol(a.asset),
|
|
269
|
+
})),
|
|
270
|
+
shortAssets: g.shortAssets.map((a) => ({
|
|
271
|
+
...a,
|
|
272
|
+
asset: toDisplaySymbol(a.asset),
|
|
273
|
+
})),
|
|
177
274
|
});
|
|
178
275
|
const mapped = {
|
|
179
276
|
active: md.active.map(mapGroup),
|
|
@@ -191,7 +288,15 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
191
288
|
catch (error) {
|
|
192
289
|
setLastError(`Failed to parse message: ${error instanceof Error ? error.message : String(error)}`);
|
|
193
290
|
}
|
|
194
|
-
}, [
|
|
291
|
+
}, [
|
|
292
|
+
setTradeHistories,
|
|
293
|
+
setRawOpenPositions,
|
|
294
|
+
setOpenOrders,
|
|
295
|
+
setAccountSummary,
|
|
296
|
+
setTwapDetails,
|
|
297
|
+
setNotifications,
|
|
298
|
+
setMarketData,
|
|
299
|
+
]);
|
|
195
300
|
const connect = useCallback(() => {
|
|
196
301
|
if (!enabled || !wsUrl)
|
|
197
302
|
return;
|
|
@@ -260,7 +365,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
260
365
|
'open-orders',
|
|
261
366
|
'twap-details',
|
|
262
367
|
'fills-checkpoint',
|
|
263
|
-
'notifications'
|
|
368
|
+
'notifications',
|
|
264
369
|
];
|
|
265
370
|
const globalChannels = ['market-data'];
|
|
266
371
|
if (address && address !== lastSubscribedAddress) {
|
|
@@ -269,14 +374,14 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
269
374
|
sendMessage(JSON.stringify({
|
|
270
375
|
action: 'unsubscribe',
|
|
271
376
|
address: lastSubscribedAddress,
|
|
272
|
-
channels: addressSpecificChannels
|
|
377
|
+
channels: addressSpecificChannels,
|
|
273
378
|
}));
|
|
274
379
|
}
|
|
275
380
|
// Subscribe to all channels (global + address-specific)
|
|
276
381
|
sendMessage(JSON.stringify({
|
|
277
382
|
action: 'subscribe',
|
|
278
383
|
address: address,
|
|
279
|
-
channels: [...globalChannels, ...addressSpecificChannels]
|
|
384
|
+
channels: [...globalChannels, ...addressSpecificChannels],
|
|
280
385
|
}));
|
|
281
386
|
setLastSubscribedAddress(address);
|
|
282
387
|
setLastError(null);
|
|
@@ -286,7 +391,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
286
391
|
sendMessage(JSON.stringify({
|
|
287
392
|
action: 'unsubscribe',
|
|
288
393
|
address: lastSubscribedAddress,
|
|
289
|
-
channels: addressSpecificChannels
|
|
394
|
+
channels: addressSpecificChannels,
|
|
290
395
|
}));
|
|
291
396
|
setLastSubscribedAddress(null);
|
|
292
397
|
}
|
|
@@ -294,7 +399,7 @@ const useHyperliquidWebSocket = ({ wsUrl, address, enabled = true, }) => {
|
|
|
294
399
|
// If no address but connected, subscribe to global channels only
|
|
295
400
|
sendMessage(JSON.stringify({
|
|
296
401
|
action: 'subscribe',
|
|
297
|
-
channels: globalChannels
|
|
402
|
+
channels: globalChannels,
|
|
298
403
|
}));
|
|
299
404
|
}
|
|
300
405
|
}, [isConnected, address, lastSubscribedAddress, sendMessage]);
|
|
@@ -318,10 +423,12 @@ const useHyperliquidData = create((set, get) => ({
|
|
|
318
423
|
finalAtOICaps: null,
|
|
319
424
|
aggregatedClearingHouseState: null,
|
|
320
425
|
perpMetaAssets: null,
|
|
321
|
-
|
|
426
|
+
allPerpMetaAssets: null,
|
|
427
|
+
hip3Assets: new Map(),
|
|
428
|
+
hip3MarketPrefixes: new Map(),
|
|
322
429
|
setAllMids: (value) => set({ allMids: value }),
|
|
323
430
|
setActiveAssetData: (value) => set((state) => ({
|
|
324
|
-
activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value
|
|
431
|
+
activeAssetData: typeof value === 'function' ? value(state.activeAssetData) : value,
|
|
325
432
|
})),
|
|
326
433
|
deleteActiveAssetData: (key) => {
|
|
327
434
|
set((state) => {
|
|
@@ -356,15 +463,130 @@ const useHyperliquidData = create((set, get) => ({
|
|
|
356
463
|
activeAssetData: {
|
|
357
464
|
...state.activeAssetData,
|
|
358
465
|
[key]: value,
|
|
359
|
-
}
|
|
466
|
+
},
|
|
360
467
|
})),
|
|
361
468
|
setFinalAssetContexts: (value) => set({ finalAssetContexts: value }),
|
|
362
469
|
setFinalAtOICaps: (value) => set({ finalAtOICaps: value }),
|
|
363
470
|
setAggregatedClearingHouseState: (value) => set({ aggregatedClearingHouseState: value }),
|
|
364
471
|
setPerpMetaAssets: (value) => set({ perpMetaAssets: value }),
|
|
365
|
-
|
|
472
|
+
setAllPerpMetaAssets: (value) => set({ allPerpMetaAssets: value }),
|
|
473
|
+
setHip3Assets: (value) => set({ hip3Assets: value }),
|
|
474
|
+
setHip3MarketPrefixes: (value) => set({ hip3MarketPrefixes: value }),
|
|
366
475
|
}));
|
|
367
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Minimum USD value required per asset when creating a position
|
|
479
|
+
*/
|
|
480
|
+
const MINIMUM_ASSET_USD_VALUE = 11;
|
|
481
|
+
/**
|
|
482
|
+
* Maximum number of assets allowed per leg (long or short) in a position
|
|
483
|
+
*/
|
|
484
|
+
const MAX_ASSETS_PER_LEG = 15;
|
|
485
|
+
/**
|
|
486
|
+
* Validation error for minimum position size
|
|
487
|
+
*/
|
|
488
|
+
class MinimumPositionSizeError extends Error {
|
|
489
|
+
constructor(assetName, assetValue, minimumRequired) {
|
|
490
|
+
super(`Asset "${assetName}" has a USD value of $${assetValue.toFixed(2)}, which is below the minimum required value of $${minimumRequired.toFixed(2)}`);
|
|
491
|
+
this.assetName = assetName;
|
|
492
|
+
this.assetValue = assetValue;
|
|
493
|
+
this.minimumRequired = minimumRequired;
|
|
494
|
+
this.name = "MinimumPositionSizeError";
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Validation error for exceeding maximum assets per leg
|
|
499
|
+
*/
|
|
500
|
+
class MaxAssetsPerLegError extends Error {
|
|
501
|
+
constructor(leg, assetCount, maxAllowed) {
|
|
502
|
+
super(`Maximum ${maxAllowed} assets allowed per leg. Your ${leg} leg has ${assetCount} assets. Please reduce the number of assets to continue.`);
|
|
503
|
+
this.leg = leg;
|
|
504
|
+
this.assetCount = assetCount;
|
|
505
|
+
this.maxAllowed = maxAllowed;
|
|
506
|
+
this.name = "MaxAssetsPerLegError";
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Validates that each leg doesn't exceed the maximum number of assets
|
|
511
|
+
* @param longAssets Array of long assets
|
|
512
|
+
* @param shortAssets Array of short assets
|
|
513
|
+
* @throws MaxAssetsPerLegError if any leg exceeds the maximum allowed assets
|
|
514
|
+
*/
|
|
515
|
+
function validateMaxAssetsPerLeg(longAssets, shortAssets) {
|
|
516
|
+
const longCount = (longAssets === null || longAssets === void 0 ? void 0 : longAssets.length) || 0;
|
|
517
|
+
const shortCount = (shortAssets === null || shortAssets === void 0 ? void 0 : shortAssets.length) || 0;
|
|
518
|
+
if (longCount > MAX_ASSETS_PER_LEG) {
|
|
519
|
+
throw new MaxAssetsPerLegError("long", longCount, MAX_ASSETS_PER_LEG);
|
|
520
|
+
}
|
|
521
|
+
if (shortCount > MAX_ASSETS_PER_LEG) {
|
|
522
|
+
throw new MaxAssetsPerLegError("short", shortCount, MAX_ASSETS_PER_LEG);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Validates that each asset in a position has at least the minimum USD value
|
|
527
|
+
* @param usdValue Total USD value for the position
|
|
528
|
+
* @param longAssets Array of long assets with weights
|
|
529
|
+
* @param shortAssets Array of short assets with weights
|
|
530
|
+
* @throws MinimumPositionSizeError if any asset has less than the minimum USD value
|
|
531
|
+
*/
|
|
532
|
+
function validateMinimumAssetSize(usdValue, longAssets, shortAssets) {
|
|
533
|
+
var _a;
|
|
534
|
+
const allAssets = [...(longAssets || []), ...(shortAssets || [])];
|
|
535
|
+
if (allAssets.length === 0) {
|
|
536
|
+
return; // No assets to validate
|
|
537
|
+
}
|
|
538
|
+
// Calculate total weight
|
|
539
|
+
const totalWeight = allAssets.reduce((sum, asset) => { var _a; return sum + ((_a = asset.weight) !== null && _a !== void 0 ? _a : 0); }, 0);
|
|
540
|
+
// If weights are not provided or sum to 0, assume equal distribution
|
|
541
|
+
const hasWeights = totalWeight > 0;
|
|
542
|
+
const equalWeight = hasWeights ? 0 : 1 / allAssets.length;
|
|
543
|
+
// Validate each asset
|
|
544
|
+
for (const asset of allAssets) {
|
|
545
|
+
const weight = hasWeights ? (_a = asset.weight) !== null && _a !== void 0 ? _a : 0 : equalWeight;
|
|
546
|
+
const assetUsdValue = usdValue * weight;
|
|
547
|
+
if (assetUsdValue < MINIMUM_ASSET_USD_VALUE) {
|
|
548
|
+
throw new MinimumPositionSizeError(asset.asset, assetUsdValue, MINIMUM_ASSET_USD_VALUE);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Calculates the minimum USD value required for a position based on the number of assets
|
|
554
|
+
* @param longAssets Array of long assets
|
|
555
|
+
* @param shortAssets Array of short assets
|
|
556
|
+
* @returns The minimum total USD value required
|
|
557
|
+
*/
|
|
558
|
+
function calculateMinimumPositionValue(longAssets, shortAssets) {
|
|
559
|
+
const totalAssets = ((longAssets === null || longAssets === void 0 ? void 0 : longAssets.length) || 0) + ((shortAssets === null || shortAssets === void 0 ? void 0 : shortAssets.length) || 0);
|
|
560
|
+
if (totalAssets === 0) {
|
|
561
|
+
return 0;
|
|
562
|
+
}
|
|
563
|
+
return MINIMUM_ASSET_USD_VALUE * totalAssets;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Validates and provides a user-friendly error message with suggestions
|
|
567
|
+
* @param usdValue Total USD value for the position
|
|
568
|
+
* @param longAssets Array of long assets with weights
|
|
569
|
+
* @param shortAssets Array of short assets with weights
|
|
570
|
+
* @returns Validation result with success flag and optional error message
|
|
571
|
+
*/
|
|
572
|
+
function validatePositionSize(usdValue, longAssets, shortAssets) {
|
|
573
|
+
try {
|
|
574
|
+
validateMinimumAssetSize(usdValue, longAssets, shortAssets);
|
|
575
|
+
return { valid: true };
|
|
576
|
+
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
if (error instanceof MinimumPositionSizeError) {
|
|
579
|
+
const minimumRequired = calculateMinimumPositionValue(longAssets, shortAssets);
|
|
580
|
+
return {
|
|
581
|
+
valid: false,
|
|
582
|
+
error: error.message,
|
|
583
|
+
minimumRequired,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
throw error;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
368
590
|
const DEFAULT_STATE = {
|
|
369
591
|
longTokens: [
|
|
370
592
|
{ symbol: "HYPE", weight: 25 },
|
|
@@ -413,14 +635,23 @@ const useUserSelection$1 = create((set, get) => ({
|
|
|
413
635
|
}
|
|
414
636
|
});
|
|
415
637
|
},
|
|
638
|
+
canAddToken: (isLong) => {
|
|
639
|
+
const currentTokens = isLong ? get().longTokens : get().shortTokens;
|
|
640
|
+
return currentTokens.length < MAX_ASSETS_PER_LEG;
|
|
641
|
+
},
|
|
416
642
|
addToken: (isLong) => {
|
|
417
643
|
const currentTokens = isLong ? get().longTokens : get().shortTokens;
|
|
644
|
+
// Check if we've reached the maximum number of assets per leg
|
|
645
|
+
if (currentTokens.length >= MAX_ASSETS_PER_LEG) {
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
418
648
|
const newIndex = currentTokens.length;
|
|
419
649
|
set((prev) => ({
|
|
420
650
|
...prev,
|
|
421
651
|
selectorConfig: { isLong, index: newIndex },
|
|
422
652
|
openTokenSelector: true,
|
|
423
653
|
}));
|
|
654
|
+
return true;
|
|
424
655
|
},
|
|
425
656
|
removeToken: (isLong, index) => {
|
|
426
657
|
set((prev) => {
|
|
@@ -503,11 +734,12 @@ const useUserSelection$1 = create((set, get) => ({
|
|
|
503
734
|
}));
|
|
504
735
|
|
|
505
736
|
const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
506
|
-
const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState } = useHyperliquidData();
|
|
737
|
+
const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState, } = useHyperliquidData();
|
|
738
|
+
const { setSpotState } = useUserData();
|
|
507
739
|
const { candleInterval } = useUserSelection$1();
|
|
508
740
|
const longTokens = useUserSelection$1((s) => s.longTokens);
|
|
509
741
|
const shortTokens = useUserSelection$1((s) => s.shortTokens);
|
|
510
|
-
const selectedTokenSymbols = useMemo(() =>
|
|
742
|
+
const selectedTokenSymbols = useMemo(() => [...longTokens, ...shortTokens].map((t) => t.symbol), [longTokens, shortTokens]);
|
|
511
743
|
const [lastError, setLastError] = useState(null);
|
|
512
744
|
const [subscribedAddress, setSubscribedAddress] = useState(null);
|
|
513
745
|
const [subscribedTokens, setSubscribedTokens] = useState([]);
|
|
@@ -545,7 +777,21 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
545
777
|
case 'allDexsAssetCtxs':
|
|
546
778
|
{
|
|
547
779
|
const data = response.data;
|
|
548
|
-
|
|
780
|
+
// Filter out vntl and hyna to match perpMetaAssets filtering
|
|
781
|
+
const FILTERED_DEX_PREFIXES = ['vntl', 'hyna'];
|
|
782
|
+
const filtered = (data.ctxs || [])
|
|
783
|
+
.filter(([prefix]) => !FILTERED_DEX_PREFIXES.includes((prefix || '').toLowerCase()))
|
|
784
|
+
.sort((a, b) => {
|
|
785
|
+
// Sort to match perpMetaAssets order: default market first, then alphabetically
|
|
786
|
+
const prefixA = a[0] || '';
|
|
787
|
+
const prefixB = b[0] || '';
|
|
788
|
+
if (prefixA === '' && prefixB !== '')
|
|
789
|
+
return -1;
|
|
790
|
+
if (prefixA !== '' && prefixB === '')
|
|
791
|
+
return 1;
|
|
792
|
+
return prefixA.localeCompare(prefixB);
|
|
793
|
+
});
|
|
794
|
+
const finalAssetContexts = filtered.flatMap(([, ctxs]) => ctxs || []);
|
|
549
795
|
setFinalAssetContexts(finalAssetContexts);
|
|
550
796
|
}
|
|
551
797
|
break;
|
|
@@ -556,7 +802,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
556
802
|
.map(([, s]) => s)
|
|
557
803
|
.filter(Boolean);
|
|
558
804
|
const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || '0') || 0), 0);
|
|
559
|
-
const toStr = (n) =>
|
|
805
|
+
const toStr = (n) => Number.isFinite(n) ? n.toString() : '0';
|
|
560
806
|
const assetPositions = states.flatMap((s) => s.assetPositions || []);
|
|
561
807
|
const crossMaintenanceMarginUsed = toStr(sum(states.map((s) => s.crossMaintenanceMarginUsed)));
|
|
562
808
|
const crossMarginSummary = {
|
|
@@ -587,19 +833,42 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
587
833
|
case 'allMids':
|
|
588
834
|
{
|
|
589
835
|
const data = response.data;
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
|
|
836
|
+
// Keep BOTH normalized prefixed keys AND display symbol keys
|
|
837
|
+
// This ensures xyz:TSLA and flx:TSLA are stored separately,
|
|
838
|
+
// while also maintaining backward compatibility with non-prefixed lookups
|
|
839
|
+
const mids = {};
|
|
840
|
+
Object.entries(data.mids || {}).forEach(([k, v]) => {
|
|
841
|
+
// Normalize prefixed keys to lowercase prefix (e.g., "XYZ:TSLA" -> "xyz:TSLA")
|
|
842
|
+
// This matches how we look up tokens in the SDK
|
|
843
|
+
let normalizedKey = k;
|
|
844
|
+
if (k.includes(':')) {
|
|
845
|
+
const [prefix, ...rest] = k.split(':');
|
|
846
|
+
normalizedKey = `${prefix.toLowerCase()}:${rest.join(':')}`;
|
|
847
|
+
}
|
|
848
|
+
// Store with normalized key
|
|
849
|
+
mids[normalizedKey] = v;
|
|
850
|
+
// Also store with original key for backward compatibility
|
|
851
|
+
if (k !== normalizedKey) {
|
|
852
|
+
mids[k] = v;
|
|
853
|
+
}
|
|
854
|
+
// Also store with display symbol for backward compatibility
|
|
855
|
+
const displayKey = toDisplaySymbol(k);
|
|
856
|
+
// Only set display key if it doesn't already exist (avoid overwriting market-specific prices)
|
|
857
|
+
if (!(displayKey in mids)) {
|
|
858
|
+
mids[displayKey] = v;
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
setAllMids({ mids });
|
|
596
862
|
}
|
|
597
863
|
break;
|
|
598
864
|
case 'activeAssetData':
|
|
599
865
|
{
|
|
600
866
|
const assetData = response.data;
|
|
601
867
|
const symbol = toDisplaySymbol(assetData.coin);
|
|
602
|
-
const normalized = {
|
|
868
|
+
const normalized = {
|
|
869
|
+
...assetData,
|
|
870
|
+
coin: symbol,
|
|
871
|
+
};
|
|
603
872
|
upsertActiveAssetData(symbol, normalized);
|
|
604
873
|
}
|
|
605
874
|
break;
|
|
@@ -611,6 +880,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
611
880
|
addCandleData(symbol, normalized);
|
|
612
881
|
}
|
|
613
882
|
break;
|
|
883
|
+
case 'spotState':
|
|
884
|
+
{
|
|
885
|
+
const spotStateData = response.data;
|
|
886
|
+
if (spotStateData === null || spotStateData === void 0 ? void 0 : spotStateData.spotState) {
|
|
887
|
+
setSpotState(spotStateData.spotState);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
break;
|
|
614
891
|
default:
|
|
615
892
|
console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
|
|
616
893
|
}
|
|
@@ -621,7 +898,15 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
621
898
|
console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
|
|
622
899
|
setLastError(errorMessage);
|
|
623
900
|
}
|
|
624
|
-
}, [
|
|
901
|
+
}, [
|
|
902
|
+
setAllMids,
|
|
903
|
+
upsertActiveAssetData,
|
|
904
|
+
addCandleData,
|
|
905
|
+
setFinalAssetContexts,
|
|
906
|
+
setFinalAtOICaps,
|
|
907
|
+
setAggregatedClearingHouseState,
|
|
908
|
+
setSpotState,
|
|
909
|
+
]);
|
|
625
910
|
const connect = useCallback(() => {
|
|
626
911
|
if (!enabled)
|
|
627
912
|
return;
|
|
@@ -719,6 +1004,17 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
719
1004
|
},
|
|
720
1005
|
};
|
|
721
1006
|
sendJsonMessage(unsubscribeMessage);
|
|
1007
|
+
// Unsubscribe from spotState for previous address
|
|
1008
|
+
if (subscribedAddress !== DEFAULT_ADDRESS) {
|
|
1009
|
+
const unsubscribeSpotState = {
|
|
1010
|
+
method: 'unsubscribe',
|
|
1011
|
+
subscription: {
|
|
1012
|
+
type: 'spotState',
|
|
1013
|
+
user: subscribedAddress,
|
|
1014
|
+
},
|
|
1015
|
+
};
|
|
1016
|
+
sendJsonMessage(unsubscribeSpotState);
|
|
1017
|
+
}
|
|
722
1018
|
const unsubscribeAllDexsClearinghouseState = {
|
|
723
1019
|
method: 'unsubscribe',
|
|
724
1020
|
subscription: {
|
|
@@ -762,13 +1058,34 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
762
1058
|
sendJsonMessage(subscribeAllDexsClearinghouseState);
|
|
763
1059
|
sendJsonMessage(subscribeAllMids);
|
|
764
1060
|
sendJsonMessage(subscribeAllDexsAssetCtxs);
|
|
1061
|
+
// Subscribe to spotState for real-time spot balances (USDH, USDC, etc.)
|
|
1062
|
+
// Only subscribe if we have a real user address (not the default)
|
|
1063
|
+
if (userAddress !== DEFAULT_ADDRESS) {
|
|
1064
|
+
const subscribeSpotState = {
|
|
1065
|
+
method: 'subscribe',
|
|
1066
|
+
subscription: {
|
|
1067
|
+
type: 'spotState',
|
|
1068
|
+
user: userAddress,
|
|
1069
|
+
},
|
|
1070
|
+
};
|
|
1071
|
+
sendJsonMessage(subscribeSpotState);
|
|
1072
|
+
}
|
|
765
1073
|
setSubscribedAddress(userAddress);
|
|
766
1074
|
// Clear previous data when address changes
|
|
767
1075
|
if (subscribedAddress && subscribedAddress !== userAddress) {
|
|
768
1076
|
// clear aggregatedClearingHouseState
|
|
769
1077
|
setAggregatedClearingHouseState(null);
|
|
1078
|
+
// clear spotState
|
|
1079
|
+
setSpotState(null);
|
|
770
1080
|
}
|
|
771
|
-
}, [
|
|
1081
|
+
}, [
|
|
1082
|
+
isConnected,
|
|
1083
|
+
address,
|
|
1084
|
+
subscribedAddress,
|
|
1085
|
+
sendJsonMessage,
|
|
1086
|
+
setAggregatedClearingHouseState,
|
|
1087
|
+
setSpotState,
|
|
1088
|
+
]);
|
|
772
1089
|
// Handle token subscriptions for activeAssetData
|
|
773
1090
|
useEffect(() => {
|
|
774
1091
|
if (!isConnected || !address)
|
|
@@ -777,7 +1094,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
777
1094
|
const tokensToSubscribe = effectiveTokens.filter((token) => token && !subscribedTokens.includes(token));
|
|
778
1095
|
const tokensToUnsubscribe = subscribedTokens.filter((token) => !effectiveTokens.includes(token));
|
|
779
1096
|
// Unsubscribe from tokens no longer in the list
|
|
780
|
-
tokensToUnsubscribe.forEach(token => {
|
|
1097
|
+
tokensToUnsubscribe.forEach((token) => {
|
|
781
1098
|
const unsubscribeMessage = {
|
|
782
1099
|
method: 'unsubscribe',
|
|
783
1100
|
subscription: {
|
|
@@ -789,7 +1106,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
789
1106
|
sendJsonMessage(unsubscribeMessage);
|
|
790
1107
|
});
|
|
791
1108
|
// Subscribe to new tokens
|
|
792
|
-
tokensToSubscribe.forEach(token => {
|
|
1109
|
+
tokensToSubscribe.forEach((token) => {
|
|
793
1110
|
const subscribeMessage = {
|
|
794
1111
|
method: 'subscribe',
|
|
795
1112
|
subscription: {
|
|
@@ -804,7 +1121,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
804
1121
|
setSubscribedTokens(effectiveTokens.filter((token) => token));
|
|
805
1122
|
tokensToSubscribe.forEach((token) => deleteActiveAssetData(token));
|
|
806
1123
|
}
|
|
807
|
-
}, [
|
|
1124
|
+
}, [
|
|
1125
|
+
isConnected,
|
|
1126
|
+
address,
|
|
1127
|
+
selectedTokenSymbols,
|
|
1128
|
+
subscribedTokens,
|
|
1129
|
+
sendJsonMessage,
|
|
1130
|
+
setActiveAssetData,
|
|
1131
|
+
]);
|
|
808
1132
|
// Handle candle subscriptions for tokens and interval changes
|
|
809
1133
|
useEffect(() => {
|
|
810
1134
|
if (!isConnected)
|
|
@@ -813,7 +1137,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
813
1137
|
// Unsubscribe from previous candle subscriptions if interval changed
|
|
814
1138
|
const prevInterval = prevCandleIntervalRef.current;
|
|
815
1139
|
if (prevInterval && prevInterval !== candleInterval) {
|
|
816
|
-
subscribedCandleTokens.forEach(token => {
|
|
1140
|
+
subscribedCandleTokens.forEach((token) => {
|
|
817
1141
|
const unsubscribeMessage = {
|
|
818
1142
|
method: 'unsubscribe',
|
|
819
1143
|
subscription: {
|
|
@@ -854,12 +1178,21 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
854
1178
|
sendJsonMessage(subscribeMessage);
|
|
855
1179
|
});
|
|
856
1180
|
// Update subscribed state
|
|
857
|
-
if (tokensToSubscribe.length > 0 ||
|
|
1181
|
+
if (tokensToSubscribe.length > 0 ||
|
|
1182
|
+
tokensToUnsubscribe.length > 0 ||
|
|
1183
|
+
prevInterval !== candleInterval) {
|
|
858
1184
|
setSubscribedCandleTokens(effectiveTokens.filter((token) => token));
|
|
859
1185
|
prevCandleIntervalRef.current = candleInterval;
|
|
860
1186
|
tokensToUnsubscribe.forEach((token) => deleteCandleSymbol(token));
|
|
861
1187
|
}
|
|
862
|
-
}, [
|
|
1188
|
+
}, [
|
|
1189
|
+
isConnected,
|
|
1190
|
+
selectedTokenSymbols,
|
|
1191
|
+
candleInterval,
|
|
1192
|
+
subscribedCandleTokens,
|
|
1193
|
+
sendJsonMessage,
|
|
1194
|
+
setCandleData,
|
|
1195
|
+
]);
|
|
863
1196
|
return {
|
|
864
1197
|
isConnected,
|
|
865
1198
|
lastError,
|
|
@@ -948,20 +1281,112 @@ const useAccountSummary = () => {
|
|
|
948
1281
|
return { data: calculated, isLoading };
|
|
949
1282
|
};
|
|
950
1283
|
|
|
1284
|
+
function findAssetMeta$4(coinName, perpMetaAssets, knownPrefix, desiredCollateral) {
|
|
1285
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
1286
|
+
if (!perpMetaAssets) {
|
|
1287
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
1288
|
+
}
|
|
1289
|
+
if (desiredCollateral) {
|
|
1290
|
+
const collateralMatch = perpMetaAssets.find((a) => a.name === coinName && a.collateralToken === desiredCollateral);
|
|
1291
|
+
if (collateralMatch) {
|
|
1292
|
+
return {
|
|
1293
|
+
collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
1294
|
+
marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
if (coinName.includes(':')) {
|
|
1299
|
+
const [prefix, symbol] = coinName.split(':');
|
|
1300
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
1301
|
+
var _a;
|
|
1302
|
+
return a.name === symbol &&
|
|
1303
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
|
|
1304
|
+
});
|
|
1305
|
+
if (exactMatch) {
|
|
1306
|
+
return {
|
|
1307
|
+
collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
1308
|
+
marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
if (knownPrefix) {
|
|
1313
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
1314
|
+
var _a;
|
|
1315
|
+
return a.name === coinName &&
|
|
1316
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
|
|
1317
|
+
});
|
|
1318
|
+
if (exactMatch) {
|
|
1319
|
+
return {
|
|
1320
|
+
collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
|
|
1321
|
+
marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
const exactMatch = perpMetaAssets.find((a) => a.name === coinName && !a.marketPrefix);
|
|
1326
|
+
if (exactMatch) {
|
|
1327
|
+
return {
|
|
1328
|
+
collateralToken: (_g = exactMatch.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
|
|
1329
|
+
marketPrefix: (_h = exactMatch.marketPrefix) !== null && _h !== void 0 ? _h : null,
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
const hip3Matches = perpMetaAssets.filter((a) => a.name === coinName && a.marketPrefix);
|
|
1333
|
+
if (hip3Matches.length > 0) {
|
|
1334
|
+
if (desiredCollateral) {
|
|
1335
|
+
const collateralMatch = hip3Matches.find((a) => a.collateralToken === desiredCollateral);
|
|
1336
|
+
if (collateralMatch) {
|
|
1337
|
+
return {
|
|
1338
|
+
collateralToken: (_j = collateralMatch.collateralToken) !== null && _j !== void 0 ? _j : 'USDC',
|
|
1339
|
+
marketPrefix: (_k = collateralMatch.marketPrefix) !== null && _k !== void 0 ? _k : null,
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
const usdHMatch = hip3Matches.find((a) => a.collateralToken === 'USDH');
|
|
1344
|
+
const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Matches[0];
|
|
1345
|
+
return {
|
|
1346
|
+
collateralToken: (_l = chosen.collateralToken) !== null && _l !== void 0 ? _l : 'USDC',
|
|
1347
|
+
marketPrefix: (_m = chosen.marketPrefix) !== null && _m !== void 0 ? _m : null,
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
1351
|
+
}
|
|
1352
|
+
function enrichTradeHistoryAssets(assets, perpMetaAssets) {
|
|
1353
|
+
return assets.map((asset) => {
|
|
1354
|
+
var _a;
|
|
1355
|
+
if (asset.marketPrefix && asset.collateralToken) {
|
|
1356
|
+
return asset;
|
|
1357
|
+
}
|
|
1358
|
+
const meta = findAssetMeta$4(asset.coin, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
|
|
1359
|
+
return {
|
|
1360
|
+
...asset,
|
|
1361
|
+
marketPrefix: asset.marketPrefix || meta.marketPrefix,
|
|
1362
|
+
collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
|
|
1363
|
+
};
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
function enrichTradeHistories(histories, perpMetaAssets) {
|
|
1367
|
+
return histories.map((history) => ({
|
|
1368
|
+
...history,
|
|
1369
|
+
closedLongAssets: enrichTradeHistoryAssets(history.closedLongAssets, perpMetaAssets),
|
|
1370
|
+
closedShortAssets: enrichTradeHistoryAssets(history.closedShortAssets, perpMetaAssets),
|
|
1371
|
+
}));
|
|
1372
|
+
}
|
|
951
1373
|
const useTradeHistories = () => {
|
|
952
1374
|
const context = useContext(PearHyperliquidContext);
|
|
953
1375
|
if (!context) {
|
|
954
1376
|
throw new Error('useTradeHistories must be used within a PearHyperliquidProvider');
|
|
955
1377
|
}
|
|
956
1378
|
const tradeHistories = useUserData((state) => state.tradeHistories);
|
|
1379
|
+
const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
|
|
957
1380
|
const isLoading = useMemo(() => {
|
|
958
1381
|
return tradeHistories === null && context.isConnected;
|
|
959
1382
|
}, [tradeHistories, context.isConnected]);
|
|
960
|
-
|
|
1383
|
+
const enrichedTradeHistories = useMemo(() => {
|
|
1384
|
+
if (!tradeHistories)
|
|
1385
|
+
return null;
|
|
1386
|
+
return enrichTradeHistories(tradeHistories, allPerpMetaAssets);
|
|
1387
|
+
}, [tradeHistories, allPerpMetaAssets]);
|
|
1388
|
+
return { data: enrichedTradeHistories, isLoading };
|
|
961
1389
|
};
|
|
962
|
-
/**
|
|
963
|
-
* Hook to access open orders with loading state
|
|
964
|
-
*/
|
|
965
1390
|
const useOpenOrders = () => {
|
|
966
1391
|
const context = useContext(PearHyperliquidContext);
|
|
967
1392
|
if (!context) {
|
|
@@ -990,21 +1415,51 @@ const useWebData = () => {
|
|
|
990
1415
|
const perpMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
991
1416
|
const aggregatedClearinghouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
992
1417
|
const finalAtOICaps = useHyperliquidData((state) => state.finalAtOICaps);
|
|
993
|
-
const hip3Assets = useHyperliquidData((state) => state.
|
|
1418
|
+
const hip3Assets = useHyperliquidData((state) => state.hip3Assets);
|
|
1419
|
+
const hip3MarketPrefixes = useHyperliquidData((state) => state.hip3MarketPrefixes);
|
|
994
1420
|
let marketDataBySymbol = {};
|
|
995
1421
|
if (finalAssetContexts && perpMetaAssets) {
|
|
996
1422
|
const result = {};
|
|
1423
|
+
// Build a map of display name -> asset context index (for unique display names)
|
|
1424
|
+
const displayNameToContextIndex = new Map();
|
|
1425
|
+
const seenNames = new Set();
|
|
1426
|
+
let contextIndex = 0;
|
|
1427
|
+
// First pass: map unique display names to their context index
|
|
997
1428
|
for (let index = 0; index < perpMetaAssets.length; index++) {
|
|
998
1429
|
const name = perpMetaAssets[index].name;
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1430
|
+
if (!seenNames.has(name)) {
|
|
1431
|
+
seenNames.add(name);
|
|
1432
|
+
if (contextIndex < finalAssetContexts.length) {
|
|
1433
|
+
displayNameToContextIndex.set(name, contextIndex);
|
|
1434
|
+
contextIndex++;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
// Second pass: create nested entries for all market variants
|
|
1439
|
+
for (let index = 0; index < perpMetaAssets.length; index++) {
|
|
1440
|
+
const universeAsset = perpMetaAssets[index];
|
|
1441
|
+
const displayName = universeAsset.name;
|
|
1442
|
+
const marketPrefix = universeAsset.marketPrefix;
|
|
1443
|
+
const ctxIndex = displayNameToContextIndex.get(displayName);
|
|
1444
|
+
if (ctxIndex !== undefined) {
|
|
1445
|
+
const assetContext = finalAssetContexts[ctxIndex];
|
|
1446
|
+
// Initialize the symbol entry if it doesn't exist
|
|
1447
|
+
if (!result[displayName]) {
|
|
1448
|
+
result[displayName] = {};
|
|
1449
|
+
}
|
|
1450
|
+
// Use marketPrefix as key for HIP-3 assets, "default" for regular assets
|
|
1451
|
+
const variantKey = marketPrefix || 'default';
|
|
1452
|
+
result[displayName][variantKey] = {
|
|
1453
|
+
asset: assetContext,
|
|
1454
|
+
universe: universeAsset,
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1003
1457
|
}
|
|
1004
1458
|
marketDataBySymbol = result;
|
|
1005
1459
|
}
|
|
1006
1460
|
return {
|
|
1007
1461
|
hip3Assets,
|
|
1462
|
+
hip3MarketPrefixes,
|
|
1008
1463
|
clearinghouseState: aggregatedClearinghouseState,
|
|
1009
1464
|
perpsAtOpenInterestCap: finalAtOICaps,
|
|
1010
1465
|
marketDataBySymbol,
|
|
@@ -1019,19 +1474,30 @@ const useWebData = () => {
|
|
|
1019
1474
|
class TokenMetadataExtractor {
|
|
1020
1475
|
/**
|
|
1021
1476
|
* Extracts comprehensive token metadata
|
|
1022
|
-
* @param symbol - Token symbol
|
|
1477
|
+
* @param symbol - Token symbol (base symbol without prefix, e.g., "TSLA")
|
|
1023
1478
|
* @param perpMetaAssets - Aggregated universe assets (flattened across dexes)
|
|
1024
1479
|
* @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
|
|
1025
1480
|
* @param allMids - AllMids data containing current prices
|
|
1026
1481
|
* @param activeAssetData - Optional active asset data containing leverage information
|
|
1482
|
+
* @param marketPrefix - Optional market prefix (e.g., "xyz", "flx") for HIP3 multi-market assets
|
|
1027
1483
|
* @returns TokenMetadata or null if token not found
|
|
1028
1484
|
*/
|
|
1029
|
-
static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
|
|
1485
|
+
static extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketPrefix) {
|
|
1030
1486
|
if (!perpMetaAssets || !finalAssetContexts || !allMids) {
|
|
1031
1487
|
return null;
|
|
1032
1488
|
}
|
|
1033
1489
|
// Find token index in aggregated universe
|
|
1034
|
-
|
|
1490
|
+
// For HIP3 assets, match both name AND marketPrefix
|
|
1491
|
+
const universeIndex = perpMetaAssets.findIndex((asset) => {
|
|
1492
|
+
if (asset.name !== symbol)
|
|
1493
|
+
return false;
|
|
1494
|
+
// If marketPrefix is specified, match it; otherwise match assets without prefix
|
|
1495
|
+
if (marketPrefix) {
|
|
1496
|
+
return asset.marketPrefix === marketPrefix;
|
|
1497
|
+
}
|
|
1498
|
+
// No prefix specified - match non-HIP3 asset (no marketPrefix) or first matching asset
|
|
1499
|
+
return !asset.marketPrefix;
|
|
1500
|
+
});
|
|
1035
1501
|
if (universeIndex === -1) {
|
|
1036
1502
|
return null;
|
|
1037
1503
|
}
|
|
@@ -1040,9 +1506,20 @@ class TokenMetadataExtractor {
|
|
|
1040
1506
|
if (!assetCtx) {
|
|
1041
1507
|
return null;
|
|
1042
1508
|
}
|
|
1043
|
-
// Get current price
|
|
1044
|
-
|
|
1045
|
-
const
|
|
1509
|
+
// Get current price - prefer assetCtx.midPx as it's already index-matched,
|
|
1510
|
+
// fall back to allMids lookup if midPx is null
|
|
1511
|
+
const prefixedKeyColon = marketPrefix ? `${marketPrefix}:${symbol}` : null;
|
|
1512
|
+
let currentPrice = 0;
|
|
1513
|
+
// Primary source: assetCtx.midPx (already properly indexed)
|
|
1514
|
+
if (assetCtx.midPx) {
|
|
1515
|
+
currentPrice = parseFloat(assetCtx.midPx);
|
|
1516
|
+
}
|
|
1517
|
+
// Fallback: allMids lookup with multiple key formats for HIP3 markets
|
|
1518
|
+
if (!currentPrice || isNaN(currentPrice)) {
|
|
1519
|
+
const currentPriceStr = (prefixedKeyColon && allMids.mids[prefixedKeyColon]) ||
|
|
1520
|
+
allMids.mids[symbol];
|
|
1521
|
+
currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
|
|
1522
|
+
}
|
|
1046
1523
|
// Get previous day price
|
|
1047
1524
|
const prevDayPrice = parseFloat(assetCtx.prevDayPx);
|
|
1048
1525
|
// Calculate 24h price change
|
|
@@ -1053,7 +1530,11 @@ class TokenMetadataExtractor {
|
|
|
1053
1530
|
const markPrice = parseFloat(assetCtx.markPx);
|
|
1054
1531
|
const oraclePrice = parseFloat(assetCtx.oraclePx);
|
|
1055
1532
|
// Extract leverage info from activeAssetData if available
|
|
1056
|
-
|
|
1533
|
+
// Try prefixed key first (e.g., "xyz:TSLA"), then fall back to plain symbol
|
|
1534
|
+
const activeDataKey = prefixedKeyColon && (activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[prefixedKeyColon])
|
|
1535
|
+
? prefixedKeyColon
|
|
1536
|
+
: symbol;
|
|
1537
|
+
const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[activeDataKey];
|
|
1057
1538
|
const leverage = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.leverage;
|
|
1058
1539
|
const maxTradeSzs = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.maxTradeSzs;
|
|
1059
1540
|
const availableToTrade = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.availableToTrade;
|
|
@@ -1071,21 +1552,27 @@ class TokenMetadataExtractor {
|
|
|
1071
1552
|
leverage,
|
|
1072
1553
|
maxTradeSzs,
|
|
1073
1554
|
availableToTrade,
|
|
1555
|
+
collateralToken: universeAsset.collateralToken,
|
|
1074
1556
|
};
|
|
1075
1557
|
}
|
|
1076
1558
|
/**
|
|
1077
1559
|
* Extracts metadata for multiple tokens
|
|
1078
|
-
* @param
|
|
1560
|
+
* @param tokens - Array of token objects with symbol and optional marketPrefix
|
|
1079
1561
|
* @param perpMetaAssets - Aggregated universe assets
|
|
1080
1562
|
* @param finalAssetContexts - Aggregated asset contexts
|
|
1081
1563
|
* @param allMids - AllMids data
|
|
1082
1564
|
* @param activeAssetData - Optional active asset data containing leverage information
|
|
1083
|
-
* @returns Record of
|
|
1565
|
+
* @returns Record of unique key to TokenMetadata. Key is "{prefix}:{symbol}" for HIP3 assets, or just "{symbol}" otherwise
|
|
1084
1566
|
*/
|
|
1085
|
-
static extractMultipleTokensMetadata(
|
|
1567
|
+
static extractMultipleTokensMetadata(tokens, perpMetaAssets, finalAssetContexts, allMids, activeAssetData) {
|
|
1086
1568
|
const result = {};
|
|
1087
|
-
for (const
|
|
1088
|
-
|
|
1569
|
+
for (const token of tokens) {
|
|
1570
|
+
// Use a unique key that includes the prefix for HIP3 assets
|
|
1571
|
+
// This ensures xyz:TSLA and flx:TSLA get separate entries
|
|
1572
|
+
const resultKey = token.marketPrefix
|
|
1573
|
+
? `${token.marketPrefix}:${token.symbol}`
|
|
1574
|
+
: token.symbol;
|
|
1575
|
+
result[resultKey] = this.extractTokenMetadata(token.symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, token.marketPrefix);
|
|
1089
1576
|
}
|
|
1090
1577
|
return result;
|
|
1091
1578
|
}
|
|
@@ -1098,10 +1585,30 @@ class TokenMetadataExtractor {
|
|
|
1098
1585
|
static isTokenAvailable(symbol, perpMetaAssets) {
|
|
1099
1586
|
if (!perpMetaAssets)
|
|
1100
1587
|
return false;
|
|
1101
|
-
return perpMetaAssets.some(asset => asset.name === symbol);
|
|
1588
|
+
return perpMetaAssets.some((asset) => asset.name === symbol);
|
|
1102
1589
|
}
|
|
1103
1590
|
}
|
|
1104
1591
|
|
|
1592
|
+
/**
|
|
1593
|
+
* Parse a token string that may have a market prefix (e.g., "xyz:GOOGL" -> { prefix: "xyz", symbol: "GOOGL" })
|
|
1594
|
+
* This allows us to keep the full name (xyz:GOOGL) for URLs/tags while extracting just the symbol for SDK lookups.
|
|
1595
|
+
*/
|
|
1596
|
+
function parseTokenWithPrefix(token) {
|
|
1597
|
+
if (token.includes(':')) {
|
|
1598
|
+
const [prefix, ...rest] = token.split(':');
|
|
1599
|
+
const symbol = rest.join(':').toUpperCase();
|
|
1600
|
+
return {
|
|
1601
|
+
prefix: prefix.toLowerCase(),
|
|
1602
|
+
symbol,
|
|
1603
|
+
fullName: `${prefix.toLowerCase()}:${symbol}`,
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
return {
|
|
1607
|
+
prefix: null,
|
|
1608
|
+
symbol: token.toUpperCase(),
|
|
1609
|
+
fullName: token.toUpperCase(),
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1105
1612
|
const useTokenSelectionMetadataStore = create((set) => ({
|
|
1106
1613
|
isPriceDataReady: false,
|
|
1107
1614
|
isLoading: true,
|
|
@@ -1111,23 +1618,65 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1111
1618
|
weightedRatio24h: 1,
|
|
1112
1619
|
priceRatio: 1,
|
|
1113
1620
|
priceRatio24h: 1,
|
|
1114
|
-
openInterest:
|
|
1115
|
-
volume:
|
|
1621
|
+
openInterest: '0',
|
|
1622
|
+
volume: '0',
|
|
1116
1623
|
sumNetFunding: 0,
|
|
1117
1624
|
maxLeverage: 0,
|
|
1118
1625
|
minMargin: 0,
|
|
1119
1626
|
leverageMatched: true,
|
|
1120
|
-
recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens }) => {
|
|
1121
|
-
const isPriceDataReady = !!(perpMetaAssets &&
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1627
|
+
recompute: ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, marketData, longTokens, shortTokens, }) => {
|
|
1628
|
+
const isPriceDataReady = !!(perpMetaAssets &&
|
|
1629
|
+
finalAssetContexts &&
|
|
1630
|
+
allMids);
|
|
1631
|
+
// Parse tokens - handle prefixed tokens like "xyz:GOOGL" by extracting the symbol and market prefix
|
|
1632
|
+
// The full name (xyz:GOOGL) is kept as the metadata key for UI consistency
|
|
1633
|
+
const parsedLongTokens = longTokens.map((t) => ({
|
|
1634
|
+
...t,
|
|
1635
|
+
parsed: parseTokenWithPrefix(t.symbol),
|
|
1636
|
+
}));
|
|
1637
|
+
const parsedShortTokens = shortTokens.map((t) => ({
|
|
1638
|
+
...t,
|
|
1639
|
+
parsed: parseTokenWithPrefix(t.symbol),
|
|
1640
|
+
}));
|
|
1641
|
+
// Extract base symbols with their market prefixes for SDK lookups
|
|
1642
|
+
// This ensures xyz:TSLA and flx:TSLA get different market data
|
|
1643
|
+
const longTokensForLookup = parsedLongTokens.map((t) => ({
|
|
1644
|
+
symbol: t.parsed.symbol,
|
|
1645
|
+
marketPrefix: t.parsed.prefix,
|
|
1646
|
+
}));
|
|
1647
|
+
const shortTokensForLookup = parsedShortTokens.map((t) => ({
|
|
1648
|
+
symbol: t.parsed.symbol,
|
|
1649
|
+
marketPrefix: t.parsed.prefix,
|
|
1650
|
+
}));
|
|
1651
|
+
// Also extract just the base symbols (without prefix) for lookups that don't support prefixes
|
|
1652
|
+
const longBaseSymbols = longTokensForLookup.map((t) => t.symbol);
|
|
1653
|
+
const shortBaseSymbols = shortTokensForLookup.map((t) => t.symbol);
|
|
1654
|
+
// Get metadata using base symbols with market prefix for proper market differentiation
|
|
1655
|
+
const longBaseMetadata = isPriceDataReady
|
|
1656
|
+
? TokenMetadataExtractor.extractMultipleTokensMetadata(longTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
|
|
1127
1657
|
: {};
|
|
1128
|
-
const
|
|
1129
|
-
? TokenMetadataExtractor.extractMultipleTokensMetadata(
|
|
1658
|
+
const shortBaseMetadata = isPriceDataReady
|
|
1659
|
+
? TokenMetadataExtractor.extractMultipleTokensMetadata(shortTokensForLookup, perpMetaAssets, finalAssetContexts, allMids, activeAssetData)
|
|
1130
1660
|
: {};
|
|
1661
|
+
// Re-map metadata using original full names (with prefix) as keys for UI consistency
|
|
1662
|
+
// The extractor now keys by "{prefix}:{symbol}" for prefixed tokens, which matches our parsed.fullName
|
|
1663
|
+
const longTokensMetadata = {};
|
|
1664
|
+
parsedLongTokens.forEach((t) => {
|
|
1665
|
+
var _a;
|
|
1666
|
+
// Use the full name (e.g., "xyz:TSLA") as the lookup key since extractor uses the same format
|
|
1667
|
+
const lookupKey = t.parsed.prefix
|
|
1668
|
+
? `${t.parsed.prefix}:${t.parsed.symbol}`
|
|
1669
|
+
: t.parsed.symbol;
|
|
1670
|
+
longTokensMetadata[t.symbol] = (_a = longBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
|
|
1671
|
+
});
|
|
1672
|
+
const shortTokensMetadata = {};
|
|
1673
|
+
parsedShortTokens.forEach((t) => {
|
|
1674
|
+
var _a;
|
|
1675
|
+
const lookupKey = t.parsed.prefix
|
|
1676
|
+
? `${t.parsed.prefix}:${t.parsed.symbol}`
|
|
1677
|
+
: t.parsed.symbol;
|
|
1678
|
+
shortTokensMetadata[t.symbol] = (_a = shortBaseMetadata[lookupKey]) !== null && _a !== void 0 ? _a : null;
|
|
1679
|
+
});
|
|
1131
1680
|
// Determine loading state
|
|
1132
1681
|
const allTokens = [...longTokens, ...shortTokens];
|
|
1133
1682
|
const isLoading = (() => {
|
|
@@ -1135,26 +1684,33 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1135
1684
|
return true;
|
|
1136
1685
|
if (allTokens.length === 0)
|
|
1137
1686
|
return false;
|
|
1138
|
-
const allMetadata = {
|
|
1687
|
+
const allMetadata = {
|
|
1688
|
+
...longTokensMetadata,
|
|
1689
|
+
...shortTokensMetadata,
|
|
1690
|
+
};
|
|
1139
1691
|
return allTokens.some((token) => !allMetadata[token.symbol]);
|
|
1140
1692
|
})();
|
|
1141
1693
|
// Open interest and volume (from market data for matching asset basket)
|
|
1694
|
+
// Use base symbols (without prefix) for matching against market data
|
|
1142
1695
|
const { openInterest, volume } = (() => {
|
|
1143
|
-
const empty = { openInterest:
|
|
1696
|
+
const empty = { openInterest: '0', volume: '0' };
|
|
1144
1697
|
if (!(marketData === null || marketData === void 0 ? void 0 : marketData.active) || (!longTokens.length && !shortTokens.length))
|
|
1145
1698
|
return empty;
|
|
1146
|
-
const selectedLong =
|
|
1147
|
-
const selectedShort =
|
|
1699
|
+
const selectedLong = longBaseSymbols.slice().sort();
|
|
1700
|
+
const selectedShort = shortBaseSymbols.slice().sort();
|
|
1148
1701
|
const match = marketData.active.find((item) => {
|
|
1149
1702
|
const longs = [...item.longAssets].sort();
|
|
1150
1703
|
const shorts = [...item.shortAssets].sort();
|
|
1151
|
-
if (longs.length !== selectedLong.length ||
|
|
1704
|
+
if (longs.length !== selectedLong.length ||
|
|
1705
|
+
shorts.length !== selectedShort.length)
|
|
1152
1706
|
return false;
|
|
1153
1707
|
const longsEqual = longs.every((s, i) => s.asset === selectedLong[i]);
|
|
1154
1708
|
const shortsEqual = shorts.every((s, i) => s.asset === selectedShort[i]);
|
|
1155
1709
|
return longsEqual && shortsEqual;
|
|
1156
1710
|
});
|
|
1157
|
-
return match
|
|
1711
|
+
return match
|
|
1712
|
+
? { openInterest: match.openInterest, volume: match.volume }
|
|
1713
|
+
: empty;
|
|
1158
1714
|
})();
|
|
1159
1715
|
// Price ratio (only when exactly one long and one short)
|
|
1160
1716
|
const { priceRatio, priceRatio24h } = (() => {
|
|
@@ -1234,17 +1790,27 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1234
1790
|
return totalFunding;
|
|
1235
1791
|
})();
|
|
1236
1792
|
// Max leverage (minimum across all tokens)
|
|
1793
|
+
// Use tokens with their market prefixes for proper lookup in perpMetaAssets
|
|
1237
1794
|
const maxLeverage = (() => {
|
|
1238
1795
|
if (!perpMetaAssets)
|
|
1239
1796
|
return 0;
|
|
1240
|
-
const
|
|
1241
|
-
|
|
1797
|
+
const allTokensForLookup = [
|
|
1798
|
+
...longTokensForLookup,
|
|
1799
|
+
...shortTokensForLookup,
|
|
1800
|
+
];
|
|
1801
|
+
if (allTokensForLookup.length === 0)
|
|
1242
1802
|
return 0;
|
|
1243
1803
|
let minLev = Infinity;
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1804
|
+
allTokensForLookup.forEach(({ symbol, marketPrefix }) => {
|
|
1805
|
+
// Match by both name AND marketPrefix for HIP3 assets
|
|
1806
|
+
const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol &&
|
|
1807
|
+
(marketPrefix
|
|
1808
|
+
? u.marketPrefix === marketPrefix
|
|
1809
|
+
: !u.marketPrefix));
|
|
1810
|
+
// Fallback to just matching by name if no exact match
|
|
1811
|
+
const fallbackUniverse = tokenUniverse || perpMetaAssets.find((u) => u.name === symbol);
|
|
1812
|
+
if (fallbackUniverse === null || fallbackUniverse === void 0 ? void 0 : fallbackUniverse.maxLeverage)
|
|
1813
|
+
minLev = Math.min(minLev, fallbackUniverse.maxLeverage);
|
|
1248
1814
|
});
|
|
1249
1815
|
return minLev === Infinity ? 0 : minLev;
|
|
1250
1816
|
})();
|
|
@@ -1256,7 +1822,10 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1256
1822
|
// Whether all tokens have matching leverage
|
|
1257
1823
|
const leverageMatched = (() => {
|
|
1258
1824
|
const allTokensArr = [...longTokens, ...shortTokens];
|
|
1259
|
-
const allMetadata = {
|
|
1825
|
+
const allMetadata = {
|
|
1826
|
+
...longTokensMetadata,
|
|
1827
|
+
...shortTokensMetadata,
|
|
1828
|
+
};
|
|
1260
1829
|
if (allTokensArr.length === 0)
|
|
1261
1830
|
return true;
|
|
1262
1831
|
const tokensWithLev = allTokensArr.filter((token) => { var _a; return (_a = allMetadata[token.symbol]) === null || _a === void 0 ? void 0 : _a.leverage; });
|
|
@@ -5441,8 +6010,8 @@ function addAuthInterceptors(params) {
|
|
|
5441
6010
|
/**
|
|
5442
6011
|
* Fetch historical candle data from HyperLiquid API
|
|
5443
6012
|
*/
|
|
5444
|
-
const fetchHistoricalCandles = async (coin, startTime, endTime, interval,
|
|
5445
|
-
const backendCoin = toBackendSymbol(coin,
|
|
6013
|
+
const fetchHistoricalCandles = async (coin, startTime, endTime, interval, hip3Assets) => {
|
|
6014
|
+
const backendCoin = toBackendSymbol(coin, hip3Assets);
|
|
5446
6015
|
const request = {
|
|
5447
6016
|
req: { coin: backendCoin, startTime, endTime, interval },
|
|
5448
6017
|
type: 'candleSnapshot',
|
|
@@ -5601,10 +6170,10 @@ const useHistoricalPriceData = () => {
|
|
|
5601
6170
|
setTokenLoading(token.symbol, true);
|
|
5602
6171
|
});
|
|
5603
6172
|
try {
|
|
5604
|
-
const
|
|
6173
|
+
const hip3Assets = useHyperliquidData.getState().hip3Assets;
|
|
5605
6174
|
const fetchPromises = tokensToFetch.map(async (token) => {
|
|
5606
6175
|
try {
|
|
5607
|
-
const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval,
|
|
6176
|
+
const response = await fetchHistoricalCandles(token.symbol, startTime, endTime, interval, hip3Assets);
|
|
5608
6177
|
addHistoricalPriceData(token.symbol, interval, response.data, { start: startTime, end: endTime });
|
|
5609
6178
|
return { symbol: token.symbol, candles: response.data, success: true };
|
|
5610
6179
|
}
|
|
@@ -6288,97 +6857,19 @@ function useAutoSyncFills(options) {
|
|
|
6288
6857
|
}
|
|
6289
6858
|
|
|
6290
6859
|
/**
|
|
6291
|
-
*
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
* Validation error for minimum position size
|
|
6296
|
-
*/
|
|
6297
|
-
class MinimumPositionSizeError extends Error {
|
|
6298
|
-
constructor(assetName, assetValue, minimumRequired) {
|
|
6299
|
-
super(`Asset "${assetName}" has a USD value of $${assetValue.toFixed(2)}, which is below the minimum required value of $${minimumRequired.toFixed(2)}`);
|
|
6300
|
-
this.assetName = assetName;
|
|
6301
|
-
this.assetValue = assetValue;
|
|
6302
|
-
this.minimumRequired = minimumRequired;
|
|
6303
|
-
this.name = "MinimumPositionSizeError";
|
|
6304
|
-
}
|
|
6305
|
-
}
|
|
6306
|
-
/**
|
|
6307
|
-
* Validates that each asset in a position has at least the minimum USD value
|
|
6308
|
-
* @param usdValue Total USD value for the position
|
|
6309
|
-
* @param longAssets Array of long assets with weights
|
|
6310
|
-
* @param shortAssets Array of short assets with weights
|
|
6311
|
-
* @throws MinimumPositionSizeError if any asset has less than the minimum USD value
|
|
6860
|
+
* Create a position (MARKET/LIMIT/TWAP) using Pear Hyperliquid service
|
|
6861
|
+
* Authorization is derived from headers (Axios defaults or browser localStorage fallback)
|
|
6862
|
+
* @throws MinimumPositionSizeError if any asset has less than $11 USD value
|
|
6863
|
+
* @throws MaxAssetsPerLegError if any leg exceeds the maximum allowed assets (15)
|
|
6312
6864
|
*/
|
|
6313
|
-
function
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
if (allAssets.length === 0) {
|
|
6317
|
-
return; // No assets to validate
|
|
6318
|
-
}
|
|
6319
|
-
// Calculate total weight
|
|
6320
|
-
const totalWeight = allAssets.reduce((sum, asset) => { var _a; return sum + ((_a = asset.weight) !== null && _a !== void 0 ? _a : 0); }, 0);
|
|
6321
|
-
// If weights are not provided or sum to 0, assume equal distribution
|
|
6322
|
-
const hasWeights = totalWeight > 0;
|
|
6323
|
-
const equalWeight = hasWeights ? 0 : 1 / allAssets.length;
|
|
6324
|
-
// Validate each asset
|
|
6325
|
-
for (const asset of allAssets) {
|
|
6326
|
-
const weight = hasWeights ? (_a = asset.weight) !== null && _a !== void 0 ? _a : 0 : equalWeight;
|
|
6327
|
-
const assetUsdValue = usdValue * weight;
|
|
6328
|
-
if (assetUsdValue < MINIMUM_ASSET_USD_VALUE) {
|
|
6329
|
-
throw new MinimumPositionSizeError(asset.asset, assetUsdValue, MINIMUM_ASSET_USD_VALUE);
|
|
6330
|
-
}
|
|
6331
|
-
}
|
|
6332
|
-
}
|
|
6333
|
-
/**
|
|
6334
|
-
* Calculates the minimum USD value required for a position based on the number of assets
|
|
6335
|
-
* @param longAssets Array of long assets
|
|
6336
|
-
* @param shortAssets Array of short assets
|
|
6337
|
-
* @returns The minimum total USD value required
|
|
6338
|
-
*/
|
|
6339
|
-
function calculateMinimumPositionValue(longAssets, shortAssets) {
|
|
6340
|
-
const totalAssets = ((longAssets === null || longAssets === void 0 ? void 0 : longAssets.length) || 0) + ((shortAssets === null || shortAssets === void 0 ? void 0 : shortAssets.length) || 0);
|
|
6341
|
-
if (totalAssets === 0) {
|
|
6342
|
-
return 0;
|
|
6343
|
-
}
|
|
6344
|
-
return MINIMUM_ASSET_USD_VALUE * totalAssets;
|
|
6345
|
-
}
|
|
6346
|
-
/**
|
|
6347
|
-
* Validates and provides a user-friendly error message with suggestions
|
|
6348
|
-
* @param usdValue Total USD value for the position
|
|
6349
|
-
* @param longAssets Array of long assets with weights
|
|
6350
|
-
* @param shortAssets Array of short assets with weights
|
|
6351
|
-
* @returns Validation result with success flag and optional error message
|
|
6352
|
-
*/
|
|
6353
|
-
function validatePositionSize(usdValue, longAssets, shortAssets) {
|
|
6354
|
-
try {
|
|
6355
|
-
validateMinimumAssetSize(usdValue, longAssets, shortAssets);
|
|
6356
|
-
return { valid: true };
|
|
6357
|
-
}
|
|
6358
|
-
catch (error) {
|
|
6359
|
-
if (error instanceof MinimumPositionSizeError) {
|
|
6360
|
-
const minimumRequired = calculateMinimumPositionValue(longAssets, shortAssets);
|
|
6361
|
-
return {
|
|
6362
|
-
valid: false,
|
|
6363
|
-
error: error.message,
|
|
6364
|
-
minimumRequired,
|
|
6365
|
-
};
|
|
6366
|
-
}
|
|
6367
|
-
throw error;
|
|
6368
|
-
}
|
|
6369
|
-
}
|
|
6370
|
-
|
|
6371
|
-
/**
|
|
6372
|
-
* Create a position (MARKET/LIMIT/TWAP) using Pear Hyperliquid service
|
|
6373
|
-
* Authorization is derived from headers (Axios defaults or browser localStorage fallback)
|
|
6374
|
-
* @throws MinimumPositionSizeError if any asset has less than $11 USD value
|
|
6375
|
-
*/
|
|
6376
|
-
async function createPosition(baseUrl, payload, displayToFull) {
|
|
6865
|
+
async function createPosition(baseUrl, payload, hip3Assets) {
|
|
6866
|
+
// Validate maximum assets per leg before creating position
|
|
6867
|
+
validateMaxAssetsPerLeg(payload.longAssets, payload.shortAssets);
|
|
6377
6868
|
// Validate minimum asset size before creating position
|
|
6378
6869
|
validateMinimumAssetSize(payload.usdValue, payload.longAssets, payload.shortAssets);
|
|
6379
6870
|
const url = joinUrl(baseUrl, "/positions");
|
|
6380
6871
|
// Translate display symbols to backend format
|
|
6381
|
-
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset,
|
|
6872
|
+
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6382
6873
|
const translatedPayload = {
|
|
6383
6874
|
...payload,
|
|
6384
6875
|
longAssets: mapAssets(payload.longAssets),
|
|
@@ -6477,9 +6968,9 @@ async function adjustPosition(baseUrl, positionId, payload) {
|
|
|
6477
6968
|
throw toApiError(error);
|
|
6478
6969
|
}
|
|
6479
6970
|
}
|
|
6480
|
-
async function adjustAdvancePosition(baseUrl, positionId, payload,
|
|
6971
|
+
async function adjustAdvancePosition(baseUrl, positionId, payload, hip3Assets) {
|
|
6481
6972
|
const url = joinUrl(baseUrl, `/positions/${positionId}/adjust-advance`);
|
|
6482
|
-
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset,
|
|
6973
|
+
const mapAssets = (arr) => arr === null || arr === void 0 ? void 0 : arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6483
6974
|
const translatedPayload = (payload || []).map((item) => ({
|
|
6484
6975
|
longAssets: mapAssets(item.longAssets),
|
|
6485
6976
|
shortAssets: mapAssets(item.shortAssets),
|
|
@@ -6542,6 +7033,9 @@ const calculatePositionAsset = (asset, currentPrice, totalInitialPositionSize, l
|
|
|
6542
7033
|
entryPositionValue: entryNotional,
|
|
6543
7034
|
initialWeight: totalInitialPositionSize > 0 ? entryNotional / totalInitialPositionSize : 0,
|
|
6544
7035
|
fundingPaid: (_a = asset.fundingPaid) !== null && _a !== void 0 ? _a : 0,
|
|
7036
|
+
// Preserve market metadata from raw asset (if provided by backend)
|
|
7037
|
+
marketPrefix: asset.marketPrefix,
|
|
7038
|
+
collateralToken: asset.collateralToken,
|
|
6545
7039
|
};
|
|
6546
7040
|
};
|
|
6547
7041
|
const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
|
|
@@ -6604,57 +7098,151 @@ const buildPositionValue = (rawPositions, clearinghouseState, allMids) => {
|
|
|
6604
7098
|
});
|
|
6605
7099
|
};
|
|
6606
7100
|
|
|
7101
|
+
function findAssetMeta$3(coinName, perpMetaAssets, knownPrefix, desiredCollateral) {
|
|
7102
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
7103
|
+
if (!perpMetaAssets) {
|
|
7104
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7105
|
+
}
|
|
7106
|
+
if (desiredCollateral) {
|
|
7107
|
+
const collateralMatch = perpMetaAssets.find((a) => a.name === coinName && a.collateralToken === desiredCollateral);
|
|
7108
|
+
if (collateralMatch) {
|
|
7109
|
+
return {
|
|
7110
|
+
collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
7111
|
+
marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
7112
|
+
};
|
|
7113
|
+
}
|
|
7114
|
+
}
|
|
7115
|
+
if (coinName.includes(':')) {
|
|
7116
|
+
const [prefix, symbol] = coinName.split(':');
|
|
7117
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7118
|
+
var _a;
|
|
7119
|
+
return a.name === symbol &&
|
|
7120
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
|
|
7121
|
+
});
|
|
7122
|
+
if (exactMatch) {
|
|
7123
|
+
return {
|
|
7124
|
+
collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
7125
|
+
marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
7126
|
+
};
|
|
7127
|
+
}
|
|
7128
|
+
}
|
|
7129
|
+
if (knownPrefix) {
|
|
7130
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7131
|
+
var _a;
|
|
7132
|
+
return a.name === coinName &&
|
|
7133
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
|
|
7134
|
+
});
|
|
7135
|
+
if (exactMatch) {
|
|
7136
|
+
return {
|
|
7137
|
+
collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
|
|
7138
|
+
marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
|
|
7139
|
+
};
|
|
7140
|
+
}
|
|
7141
|
+
}
|
|
7142
|
+
const regularAsset = perpMetaAssets.find((a) => a.name === coinName && !a.marketPrefix);
|
|
7143
|
+
if (regularAsset) {
|
|
7144
|
+
return {
|
|
7145
|
+
collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
|
|
7146
|
+
marketPrefix: null,
|
|
7147
|
+
};
|
|
7148
|
+
}
|
|
7149
|
+
const hip3Asset = perpMetaAssets.find((a) => a.name === coinName && a.marketPrefix);
|
|
7150
|
+
if (hip3Asset) {
|
|
7151
|
+
return {
|
|
7152
|
+
collateralToken: (_h = hip3Asset.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
|
|
7153
|
+
marketPrefix: (_j = hip3Asset.marketPrefix) !== null && _j !== void 0 ? _j : null,
|
|
7154
|
+
};
|
|
7155
|
+
}
|
|
7156
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7157
|
+
}
|
|
7158
|
+
function enrichPositionAssets(assets, perpMetaAssets) {
|
|
7159
|
+
return assets.map((asset) => {
|
|
7160
|
+
var _a;
|
|
7161
|
+
if (asset.marketPrefix && asset.collateralToken) {
|
|
7162
|
+
return asset;
|
|
7163
|
+
}
|
|
7164
|
+
const meta = findAssetMeta$3(asset.coin, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
|
|
7165
|
+
return {
|
|
7166
|
+
...asset,
|
|
7167
|
+
marketPrefix: asset.marketPrefix || meta.marketPrefix,
|
|
7168
|
+
collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
|
|
7169
|
+
};
|
|
7170
|
+
});
|
|
7171
|
+
}
|
|
7172
|
+
function enrichPositions(positions, perpMetaAssets) {
|
|
7173
|
+
return positions.map((position) => ({
|
|
7174
|
+
...position,
|
|
7175
|
+
longAssets: enrichPositionAssets(position.longAssets, perpMetaAssets),
|
|
7176
|
+
shortAssets: enrichPositionAssets(position.shortAssets, perpMetaAssets),
|
|
7177
|
+
}));
|
|
7178
|
+
}
|
|
6607
7179
|
function usePosition() {
|
|
6608
7180
|
const context = useContext(PearHyperliquidContext);
|
|
6609
7181
|
if (!context) {
|
|
6610
7182
|
throw new Error('usePosition must be used within a PearHyperliquidProvider');
|
|
6611
7183
|
}
|
|
6612
7184
|
const { apiBaseUrl, isConnected } = context;
|
|
6613
|
-
const
|
|
6614
|
-
// Create position API action
|
|
7185
|
+
const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
|
|
6615
7186
|
const createPosition$1 = async (payload) => {
|
|
6616
|
-
return createPosition(apiBaseUrl, payload,
|
|
7187
|
+
return createPosition(apiBaseUrl, payload, hip3Assets);
|
|
6617
7188
|
};
|
|
6618
|
-
// Update TP/SL risk parameters for a position
|
|
6619
7189
|
const updateRiskParameters$1 = async (positionId, payload) => {
|
|
6620
7190
|
return updateRiskParameters(apiBaseUrl, positionId, payload);
|
|
6621
7191
|
};
|
|
6622
|
-
// Close a position (MARKET or TWAP)
|
|
6623
7192
|
const closePosition$1 = async (positionId, payload) => {
|
|
6624
7193
|
return closePosition(apiBaseUrl, positionId, payload);
|
|
6625
7194
|
};
|
|
6626
|
-
// Close all positions (MARKET or TWAP)
|
|
6627
7195
|
const closeAllPositions$1 = async (payload) => {
|
|
6628
7196
|
return closeAllPositions(apiBaseUrl, payload);
|
|
6629
7197
|
};
|
|
6630
|
-
// Adjust a position (REDUCE/INCREASE by %; MARKET or LIMIT)
|
|
6631
7198
|
const adjustPosition$1 = async (positionId, payload) => {
|
|
6632
7199
|
return adjustPosition(apiBaseUrl, positionId, payload);
|
|
6633
7200
|
};
|
|
6634
|
-
// Adjust to absolute target sizes per asset, optionally adding new assets
|
|
6635
7201
|
const adjustAdvancePosition$1 = async (positionId, payload) => {
|
|
6636
|
-
return adjustAdvancePosition(apiBaseUrl, positionId, payload,
|
|
7202
|
+
return adjustAdvancePosition(apiBaseUrl, positionId, payload, hip3Assets);
|
|
6637
7203
|
};
|
|
6638
|
-
// Open positions using WS data, with derived values
|
|
6639
7204
|
const userOpenPositions = useUserData((state) => state.rawOpenPositions);
|
|
6640
7205
|
const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
6641
7206
|
const allMids = useHyperliquidData((state) => state.allMids);
|
|
7207
|
+
const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
|
|
6642
7208
|
const isLoading = useMemo(() => {
|
|
6643
7209
|
return userOpenPositions === null && isConnected;
|
|
6644
7210
|
}, [userOpenPositions, isConnected]);
|
|
6645
7211
|
const openPositions = useMemo(() => {
|
|
6646
7212
|
if (!userOpenPositions || !aggregatedClearingHouseState || !allMids)
|
|
6647
7213
|
return null;
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
7214
|
+
const positions = buildPositionValue(userOpenPositions, aggregatedClearingHouseState, allMids);
|
|
7215
|
+
return enrichPositions(positions, allPerpMetaAssets);
|
|
7216
|
+
}, [
|
|
7217
|
+
userOpenPositions,
|
|
7218
|
+
aggregatedClearingHouseState,
|
|
7219
|
+
allMids,
|
|
7220
|
+
allPerpMetaAssets,
|
|
7221
|
+
]);
|
|
7222
|
+
return {
|
|
7223
|
+
createPosition: createPosition$1,
|
|
7224
|
+
updateRiskParameters: updateRiskParameters$1,
|
|
7225
|
+
closePosition: closePosition$1,
|
|
7226
|
+
closeAllPositions: closeAllPositions$1,
|
|
7227
|
+
adjustPosition: adjustPosition$1,
|
|
7228
|
+
adjustAdvancePosition: adjustAdvancePosition$1,
|
|
7229
|
+
openPositions,
|
|
7230
|
+
isLoading,
|
|
7231
|
+
};
|
|
6651
7232
|
}
|
|
6652
7233
|
|
|
6653
7234
|
async function adjustOrder(baseUrl, orderId, payload) {
|
|
6654
7235
|
const url = joinUrl(baseUrl, `/orders/${orderId}/adjust`);
|
|
6655
7236
|
try {
|
|
6656
|
-
const resp = await apiClient.put(url, payload, {
|
|
6657
|
-
|
|
7237
|
+
const resp = await apiClient.put(url, payload, {
|
|
7238
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7239
|
+
timeout: 60000,
|
|
7240
|
+
});
|
|
7241
|
+
return {
|
|
7242
|
+
data: resp.data,
|
|
7243
|
+
status: resp.status,
|
|
7244
|
+
headers: resp.headers,
|
|
7245
|
+
};
|
|
6658
7246
|
}
|
|
6659
7247
|
catch (error) {
|
|
6660
7248
|
throw toApiError(error);
|
|
@@ -6663,8 +7251,14 @@ async function adjustOrder(baseUrl, orderId, payload) {
|
|
|
6663
7251
|
async function cancelOrder(baseUrl, orderId) {
|
|
6664
7252
|
const url = joinUrl(baseUrl, `/orders/${orderId}/cancel`);
|
|
6665
7253
|
try {
|
|
6666
|
-
const resp = await apiClient.delete(url, {
|
|
6667
|
-
|
|
7254
|
+
const resp = await apiClient.delete(url, {
|
|
7255
|
+
timeout: 60000,
|
|
7256
|
+
});
|
|
7257
|
+
return {
|
|
7258
|
+
data: resp.data,
|
|
7259
|
+
status: resp.status,
|
|
7260
|
+
headers: resp.headers,
|
|
7261
|
+
};
|
|
6668
7262
|
}
|
|
6669
7263
|
catch (error) {
|
|
6670
7264
|
throw toApiError(error);
|
|
@@ -6674,19 +7268,129 @@ async function cancelTwapOrder(baseUrl, orderId) {
|
|
|
6674
7268
|
const url = joinUrl(baseUrl, `/orders/${orderId}/twap/cancel`);
|
|
6675
7269
|
try {
|
|
6676
7270
|
const resp = await apiClient.post(url, {}, { headers: { 'Content-Type': 'application/json' }, timeout: 60000 });
|
|
6677
|
-
return {
|
|
7271
|
+
return {
|
|
7272
|
+
data: resp.data,
|
|
7273
|
+
status: resp.status,
|
|
7274
|
+
headers: resp.headers,
|
|
7275
|
+
};
|
|
7276
|
+
}
|
|
7277
|
+
catch (error) {
|
|
7278
|
+
throw toApiError(error);
|
|
7279
|
+
}
|
|
7280
|
+
}
|
|
7281
|
+
/**
|
|
7282
|
+
* Execute a spot order (swap) using Pear Hyperliquid service
|
|
7283
|
+
* POST /orders/spot
|
|
7284
|
+
*/
|
|
7285
|
+
async function executeSpotOrder(baseUrl, payload) {
|
|
7286
|
+
const url = joinUrl(baseUrl, '/orders/spot');
|
|
7287
|
+
try {
|
|
7288
|
+
const resp = await apiClient.post(url, payload, {
|
|
7289
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7290
|
+
timeout: 60000,
|
|
7291
|
+
});
|
|
7292
|
+
return {
|
|
7293
|
+
data: resp.data,
|
|
7294
|
+
status: resp.status,
|
|
7295
|
+
headers: resp.headers,
|
|
7296
|
+
};
|
|
6678
7297
|
}
|
|
6679
7298
|
catch (error) {
|
|
6680
7299
|
throw toApiError(error);
|
|
6681
7300
|
}
|
|
6682
7301
|
}
|
|
6683
7302
|
|
|
7303
|
+
function findAssetMeta$2(assetName, perpMetaAssets, knownPrefix, desiredCollateral) {
|
|
7304
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
7305
|
+
if (!perpMetaAssets) {
|
|
7306
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7307
|
+
}
|
|
7308
|
+
if (desiredCollateral) {
|
|
7309
|
+
const collateralMatch = perpMetaAssets.find((a) => a.name === assetName && a.collateralToken === desiredCollateral);
|
|
7310
|
+
if (collateralMatch) {
|
|
7311
|
+
return {
|
|
7312
|
+
collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
7313
|
+
marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
7314
|
+
};
|
|
7315
|
+
}
|
|
7316
|
+
}
|
|
7317
|
+
if (assetName.includes(':')) {
|
|
7318
|
+
const [prefix, symbol] = assetName.split(':');
|
|
7319
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7320
|
+
var _a;
|
|
7321
|
+
return a.name === symbol &&
|
|
7322
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
|
|
7323
|
+
});
|
|
7324
|
+
if (exactMatch) {
|
|
7325
|
+
return {
|
|
7326
|
+
collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
7327
|
+
marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
7328
|
+
};
|
|
7329
|
+
}
|
|
7330
|
+
}
|
|
7331
|
+
if (knownPrefix) {
|
|
7332
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7333
|
+
var _a;
|
|
7334
|
+
return a.name === assetName &&
|
|
7335
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
|
|
7336
|
+
});
|
|
7337
|
+
if (exactMatch) {
|
|
7338
|
+
return {
|
|
7339
|
+
collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
|
|
7340
|
+
marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
|
|
7341
|
+
};
|
|
7342
|
+
}
|
|
7343
|
+
}
|
|
7344
|
+
const regularAsset = perpMetaAssets.find((a) => a.name === assetName && !a.marketPrefix);
|
|
7345
|
+
if (regularAsset) {
|
|
7346
|
+
return {
|
|
7347
|
+
collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
|
|
7348
|
+
marketPrefix: null,
|
|
7349
|
+
};
|
|
7350
|
+
}
|
|
7351
|
+
const hip3Assets = perpMetaAssets.filter((a) => a.name === assetName && a.marketPrefix);
|
|
7352
|
+
if (hip3Assets.length > 0) {
|
|
7353
|
+
if (desiredCollateral) {
|
|
7354
|
+
const collateralMatch = hip3Assets.find((a) => a.collateralToken === desiredCollateral);
|
|
7355
|
+
if (collateralMatch) {
|
|
7356
|
+
return {
|
|
7357
|
+
collateralToken: (_h = collateralMatch.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
|
|
7358
|
+
marketPrefix: (_j = collateralMatch.marketPrefix) !== null && _j !== void 0 ? _j : null,
|
|
7359
|
+
};
|
|
7360
|
+
}
|
|
7361
|
+
}
|
|
7362
|
+
const usdHMatch = hip3Assets.find((a) => a.collateralToken === 'USDH');
|
|
7363
|
+
const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Assets[0];
|
|
7364
|
+
return {
|
|
7365
|
+
collateralToken: (_k = chosen.collateralToken) !== null && _k !== void 0 ? _k : 'USDC',
|
|
7366
|
+
marketPrefix: (_l = chosen.marketPrefix) !== null && _l !== void 0 ? _l : null,
|
|
7367
|
+
};
|
|
7368
|
+
}
|
|
7369
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7370
|
+
}
|
|
7371
|
+
function enrichOrderAssets$1(assets, perpMetaAssets) {
|
|
7372
|
+
if (!assets)
|
|
7373
|
+
return [];
|
|
7374
|
+
return assets.map((asset) => {
|
|
7375
|
+
var _a;
|
|
7376
|
+
if (asset.marketPrefix && asset.collateralToken) {
|
|
7377
|
+
return asset;
|
|
7378
|
+
}
|
|
7379
|
+
const meta = findAssetMeta$2(asset.asset, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
|
|
7380
|
+
return {
|
|
7381
|
+
...asset,
|
|
7382
|
+
marketPrefix: asset.marketPrefix || meta.marketPrefix,
|
|
7383
|
+
collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
|
|
7384
|
+
};
|
|
7385
|
+
});
|
|
7386
|
+
}
|
|
6684
7387
|
function useOrders() {
|
|
6685
7388
|
const context = useContext(PearHyperliquidContext);
|
|
6686
7389
|
if (!context)
|
|
6687
7390
|
throw new Error('useOrders must be used within a PearHyperliquidProvider');
|
|
6688
7391
|
const { apiBaseUrl } = context;
|
|
6689
7392
|
const openOrders = useUserData((state) => state.openOrders);
|
|
7393
|
+
const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
|
|
6690
7394
|
const isLoading = useMemo(() => openOrders === null && context.isConnected, [openOrders, context.isConnected]);
|
|
6691
7395
|
const { openPositions } = usePosition();
|
|
6692
7396
|
const positionsById = useMemo(() => {
|
|
@@ -6705,19 +7409,27 @@ function useOrders() {
|
|
|
6705
7409
|
const isTpSl = ord.orderType === 'TP' || ord.orderType === 'SL';
|
|
6706
7410
|
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;
|
|
6707
7411
|
const pos = positionsById.get((_e = ord.positionId) !== null && _e !== void 0 ? _e : '');
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
7412
|
+
let enrichedOrd = {
|
|
7413
|
+
...ord,
|
|
7414
|
+
longAssets: enrichOrderAssets$1(ord.longAssets, allPerpMetaAssets),
|
|
7415
|
+
shortAssets: enrichOrderAssets$1(ord.shortAssets, allPerpMetaAssets),
|
|
7416
|
+
};
|
|
7417
|
+
if (isTpSl && !hasAssets && pos) {
|
|
7418
|
+
const mapAssets = (arr) => arr.map((a) => ({
|
|
7419
|
+
asset: a.coin,
|
|
7420
|
+
weight: a.initialWeight,
|
|
7421
|
+
marketPrefix: a.marketPrefix,
|
|
7422
|
+
collateralToken: a.collateralToken,
|
|
7423
|
+
}));
|
|
7424
|
+
enrichedOrd = {
|
|
7425
|
+
...enrichedOrd,
|
|
6714
7426
|
longAssets: mapAssets(pos.longAssets),
|
|
6715
7427
|
shortAssets: mapAssets(pos.shortAssets),
|
|
6716
7428
|
};
|
|
6717
7429
|
}
|
|
6718
|
-
return
|
|
7430
|
+
return enrichedOrd;
|
|
6719
7431
|
});
|
|
6720
|
-
}, [openOrders, positionsById]);
|
|
7432
|
+
}, [openOrders, positionsById, allPerpMetaAssets]);
|
|
6721
7433
|
const adjustOrder$1 = async (orderId, payload) => {
|
|
6722
7434
|
return adjustOrder(apiBaseUrl, orderId, payload);
|
|
6723
7435
|
};
|
|
@@ -6727,16 +7439,156 @@ function useOrders() {
|
|
|
6727
7439
|
const cancelTwapOrder$1 = async (orderId) => {
|
|
6728
7440
|
return cancelTwapOrder(apiBaseUrl, orderId);
|
|
6729
7441
|
};
|
|
6730
|
-
return {
|
|
7442
|
+
return {
|
|
7443
|
+
adjustOrder: adjustOrder$1,
|
|
7444
|
+
cancelOrder: cancelOrder$1,
|
|
7445
|
+
cancelTwapOrder: cancelTwapOrder$1,
|
|
7446
|
+
openOrders: enrichedOpenOrders,
|
|
7447
|
+
isLoading,
|
|
7448
|
+
};
|
|
6731
7449
|
}
|
|
6732
7450
|
|
|
7451
|
+
/**
|
|
7452
|
+
* Hook for executing spot orders (swaps) on Hyperliquid
|
|
7453
|
+
* Use this to swap between USDC and USDH or other spot assets
|
|
7454
|
+
*/
|
|
7455
|
+
function useSpotOrder() {
|
|
7456
|
+
const context = useContext(PearHyperliquidContext);
|
|
7457
|
+
if (!context) {
|
|
7458
|
+
throw new Error('useSpotOrder must be used within a PearHyperliquidProvider');
|
|
7459
|
+
}
|
|
7460
|
+
const { apiBaseUrl } = context;
|
|
7461
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
7462
|
+
const [error, setError] = useState(null);
|
|
7463
|
+
const resetError = useCallback(() => {
|
|
7464
|
+
setError(null);
|
|
7465
|
+
}, []);
|
|
7466
|
+
const executeSpotOrder$1 = useCallback(async (payload) => {
|
|
7467
|
+
setIsLoading(true);
|
|
7468
|
+
setError(null);
|
|
7469
|
+
try {
|
|
7470
|
+
const response = await executeSpotOrder(apiBaseUrl, payload);
|
|
7471
|
+
return response;
|
|
7472
|
+
}
|
|
7473
|
+
catch (err) {
|
|
7474
|
+
const apiError = err;
|
|
7475
|
+
setError(apiError);
|
|
7476
|
+
throw apiError;
|
|
7477
|
+
}
|
|
7478
|
+
finally {
|
|
7479
|
+
setIsLoading(false);
|
|
7480
|
+
}
|
|
7481
|
+
}, [apiBaseUrl]);
|
|
7482
|
+
return {
|
|
7483
|
+
executeSpotOrder: executeSpotOrder$1,
|
|
7484
|
+
isLoading,
|
|
7485
|
+
error,
|
|
7486
|
+
resetError,
|
|
7487
|
+
};
|
|
7488
|
+
}
|
|
7489
|
+
|
|
7490
|
+
function findAssetMeta$1(assetName, perpMetaAssets, knownPrefix, desiredCollateral) {
|
|
7491
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
7492
|
+
if (!perpMetaAssets) {
|
|
7493
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7494
|
+
}
|
|
7495
|
+
if (desiredCollateral) {
|
|
7496
|
+
const collateralMatch = perpMetaAssets.find((a) => a.name === assetName && a.collateralToken === desiredCollateral);
|
|
7497
|
+
if (collateralMatch) {
|
|
7498
|
+
return {
|
|
7499
|
+
collateralToken: (_a = collateralMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
7500
|
+
marketPrefix: (_b = collateralMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
7501
|
+
};
|
|
7502
|
+
}
|
|
7503
|
+
}
|
|
7504
|
+
if (assetName.includes(':')) {
|
|
7505
|
+
const [prefix, symbol] = assetName.split(':');
|
|
7506
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7507
|
+
var _a;
|
|
7508
|
+
return a.name === symbol &&
|
|
7509
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === prefix.toLowerCase();
|
|
7510
|
+
});
|
|
7511
|
+
if (exactMatch) {
|
|
7512
|
+
return {
|
|
7513
|
+
collateralToken: (_c = exactMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
7514
|
+
marketPrefix: (_d = exactMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
7515
|
+
};
|
|
7516
|
+
}
|
|
7517
|
+
}
|
|
7518
|
+
if (knownPrefix) {
|
|
7519
|
+
const exactMatch = perpMetaAssets.find((a) => {
|
|
7520
|
+
var _a;
|
|
7521
|
+
return a.name === assetName &&
|
|
7522
|
+
((_a = a.marketPrefix) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === knownPrefix.toLowerCase();
|
|
7523
|
+
});
|
|
7524
|
+
if (exactMatch) {
|
|
7525
|
+
return {
|
|
7526
|
+
collateralToken: (_e = exactMatch.collateralToken) !== null && _e !== void 0 ? _e : 'USDC',
|
|
7527
|
+
marketPrefix: (_f = exactMatch.marketPrefix) !== null && _f !== void 0 ? _f : null,
|
|
7528
|
+
};
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
const regularAsset = perpMetaAssets.find((a) => a.name === assetName && !a.marketPrefix);
|
|
7532
|
+
if (regularAsset) {
|
|
7533
|
+
return {
|
|
7534
|
+
collateralToken: (_g = regularAsset.collateralToken) !== null && _g !== void 0 ? _g : 'USDC',
|
|
7535
|
+
marketPrefix: null,
|
|
7536
|
+
};
|
|
7537
|
+
}
|
|
7538
|
+
const hip3Assets = perpMetaAssets.filter((a) => a.name === assetName && a.marketPrefix);
|
|
7539
|
+
if (hip3Assets.length > 0) {
|
|
7540
|
+
if (desiredCollateral) {
|
|
7541
|
+
const collateralMatch = hip3Assets.find((a) => a.collateralToken === desiredCollateral);
|
|
7542
|
+
if (collateralMatch) {
|
|
7543
|
+
return {
|
|
7544
|
+
collateralToken: (_h = collateralMatch.collateralToken) !== null && _h !== void 0 ? _h : 'USDC',
|
|
7545
|
+
marketPrefix: (_j = collateralMatch.marketPrefix) !== null && _j !== void 0 ? _j : null,
|
|
7546
|
+
};
|
|
7547
|
+
}
|
|
7548
|
+
}
|
|
7549
|
+
const usdHMatch = hip3Assets.find((a) => a.collateralToken === 'USDH');
|
|
7550
|
+
const chosen = usdHMatch !== null && usdHMatch !== void 0 ? usdHMatch : hip3Assets[0];
|
|
7551
|
+
return {
|
|
7552
|
+
collateralToken: (_k = chosen.collateralToken) !== null && _k !== void 0 ? _k : 'USDC',
|
|
7553
|
+
marketPrefix: (_l = chosen.marketPrefix) !== null && _l !== void 0 ? _l : null,
|
|
7554
|
+
};
|
|
7555
|
+
}
|
|
7556
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7557
|
+
}
|
|
7558
|
+
function enrichOrderAssets(assets, perpMetaAssets) {
|
|
7559
|
+
if (!assets)
|
|
7560
|
+
return [];
|
|
7561
|
+
return assets.map((asset) => {
|
|
7562
|
+
var _a;
|
|
7563
|
+
if (asset.marketPrefix && asset.collateralToken) {
|
|
7564
|
+
return asset;
|
|
7565
|
+
}
|
|
7566
|
+
const meta = findAssetMeta$1(asset.asset, perpMetaAssets, asset.marketPrefix, asset.collateralToken);
|
|
7567
|
+
return {
|
|
7568
|
+
...asset,
|
|
7569
|
+
marketPrefix: asset.marketPrefix || meta.marketPrefix,
|
|
7570
|
+
collateralToken: (_a = asset.collateralToken) !== null && _a !== void 0 ? _a : meta.collateralToken,
|
|
7571
|
+
};
|
|
7572
|
+
});
|
|
7573
|
+
}
|
|
7574
|
+
function enrichTwapOrders(orders, perpMetaAssets) {
|
|
7575
|
+
return orders.map((order) => ({
|
|
7576
|
+
...order,
|
|
7577
|
+
longAssets: enrichOrderAssets(order.longAssets, perpMetaAssets),
|
|
7578
|
+
shortAssets: enrichOrderAssets(order.shortAssets, perpMetaAssets),
|
|
7579
|
+
}));
|
|
7580
|
+
}
|
|
6733
7581
|
function useTwap() {
|
|
6734
|
-
const twapDetails = useUserData(state => state.twapDetails);
|
|
7582
|
+
const twapDetails = useUserData((state) => state.twapDetails);
|
|
7583
|
+
const allPerpMetaAssets = useHyperliquidData((state) => state.allPerpMetaAssets);
|
|
6735
7584
|
const context = useContext(PearHyperliquidContext);
|
|
6736
7585
|
if (!context)
|
|
6737
7586
|
throw new Error('useTwap must be used within a PearHyperliquidProvider');
|
|
6738
7587
|
const { apiBaseUrl } = context;
|
|
6739
|
-
const orders = useMemo(() =>
|
|
7588
|
+
const orders = useMemo(() => {
|
|
7589
|
+
const rawOrders = twapDetails !== null && twapDetails !== void 0 ? twapDetails : [];
|
|
7590
|
+
return enrichTwapOrders(rawOrders, allPerpMetaAssets);
|
|
7591
|
+
}, [twapDetails, allPerpMetaAssets]);
|
|
6740
7592
|
const cancelTwap$1 = async (orderId) => {
|
|
6741
7593
|
return cancelTwap(apiBaseUrl, orderId);
|
|
6742
7594
|
};
|
|
@@ -6819,59 +7671,170 @@ function useNotifications() {
|
|
|
6819
7671
|
};
|
|
6820
7672
|
}
|
|
6821
7673
|
|
|
6822
|
-
//
|
|
7674
|
+
// Helper to find asset metadata from perpMetaAssets
|
|
7675
|
+
function findAssetMeta(assetName, perpMetaAssets) {
|
|
7676
|
+
var _a, _b, _c, _d;
|
|
7677
|
+
if (!perpMetaAssets) {
|
|
7678
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7679
|
+
}
|
|
7680
|
+
// Try exact match first (for prefixed assets like "xyz:TSLA")
|
|
7681
|
+
const exactMatch = perpMetaAssets.find((a) => a.name === assetName);
|
|
7682
|
+
if (exactMatch) {
|
|
7683
|
+
return {
|
|
7684
|
+
collateralToken: (_a = exactMatch.collateralToken) !== null && _a !== void 0 ? _a : 'USDC',
|
|
7685
|
+
marketPrefix: (_b = exactMatch.marketPrefix) !== null && _b !== void 0 ? _b : null,
|
|
7686
|
+
};
|
|
7687
|
+
}
|
|
7688
|
+
// Try matching by base symbol (for non-prefixed names in data)
|
|
7689
|
+
const baseMatch = perpMetaAssets.find((a) => {
|
|
7690
|
+
const baseName = a.name.includes(':') ? a.name.split(':')[1] : a.name;
|
|
7691
|
+
return baseName === assetName;
|
|
7692
|
+
});
|
|
7693
|
+
if (baseMatch) {
|
|
7694
|
+
return {
|
|
7695
|
+
collateralToken: (_c = baseMatch.collateralToken) !== null && _c !== void 0 ? _c : 'USDC',
|
|
7696
|
+
marketPrefix: (_d = baseMatch.marketPrefix) !== null && _d !== void 0 ? _d : null,
|
|
7697
|
+
};
|
|
7698
|
+
}
|
|
7699
|
+
return { collateralToken: 'USDC', marketPrefix: null };
|
|
7700
|
+
}
|
|
7701
|
+
// Enrich a single asset with metadata
|
|
7702
|
+
function enrichAsset(asset, perpMetaAssets) {
|
|
7703
|
+
const meta = findAssetMeta(asset.asset, perpMetaAssets);
|
|
7704
|
+
return {
|
|
7705
|
+
...asset,
|
|
7706
|
+
collateralToken: meta.collateralToken,
|
|
7707
|
+
marketPrefix: meta.marketPrefix,
|
|
7708
|
+
};
|
|
7709
|
+
}
|
|
7710
|
+
// Enrich a basket item with collateral info
|
|
7711
|
+
function enrichBasketItem(item, perpMetaAssets) {
|
|
7712
|
+
const enrichedLongs = item.longAssets.map((a) => enrichAsset(a, perpMetaAssets));
|
|
7713
|
+
const enrichedShorts = item.shortAssets.map((a) => enrichAsset(a, perpMetaAssets));
|
|
7714
|
+
// Determine collateral type
|
|
7715
|
+
const allAssets = [...enrichedLongs, ...enrichedShorts];
|
|
7716
|
+
const hasUsdc = allAssets.some((a) => a.collateralToken === 'USDC');
|
|
7717
|
+
const hasUsdh = allAssets.some((a) => a.collateralToken === 'USDH');
|
|
7718
|
+
let collateralType = 'USDC';
|
|
7719
|
+
if (hasUsdc && hasUsdh) {
|
|
7720
|
+
collateralType = 'MIXED';
|
|
7721
|
+
}
|
|
7722
|
+
else if (hasUsdh) {
|
|
7723
|
+
collateralType = 'USDH';
|
|
7724
|
+
}
|
|
7725
|
+
return {
|
|
7726
|
+
...item,
|
|
7727
|
+
longAssets: enrichedLongs,
|
|
7728
|
+
shortAssets: enrichedShorts,
|
|
7729
|
+
collateralType,
|
|
7730
|
+
};
|
|
7731
|
+
}
|
|
7732
|
+
/**
|
|
7733
|
+
* Filter baskets by collateral type
|
|
7734
|
+
* - 'USDC': Only baskets where ALL assets use USDC (collateralType === 'USDC')
|
|
7735
|
+
* - 'USDH': Only baskets where ALL assets use USDH (collateralType === 'USDH')
|
|
7736
|
+
* - 'ALL' or undefined: No filtering, returns all baskets
|
|
7737
|
+
*/
|
|
7738
|
+
function filterByCollateral(baskets, filter) {
|
|
7739
|
+
if (!filter || filter === 'ALL') {
|
|
7740
|
+
return baskets;
|
|
7741
|
+
}
|
|
7742
|
+
return baskets.filter((basket) => {
|
|
7743
|
+
if (filter === 'USDC') {
|
|
7744
|
+
// Include baskets that are purely USDC or have USDC assets
|
|
7745
|
+
return (basket.collateralType === 'USDC' || basket.collateralType === 'MIXED');
|
|
7746
|
+
}
|
|
7747
|
+
if (filter === 'USDH') {
|
|
7748
|
+
// Include baskets that are purely USDH or have USDH assets
|
|
7749
|
+
return (basket.collateralType === 'USDH' || basket.collateralType === 'MIXED');
|
|
7750
|
+
}
|
|
7751
|
+
return true;
|
|
7752
|
+
});
|
|
7753
|
+
}
|
|
7754
|
+
// Base selector for the full market-data payload (raw from WS)
|
|
6823
7755
|
const useMarketDataPayload = () => {
|
|
6824
7756
|
return useMarketData((s) => s.marketData);
|
|
6825
7757
|
};
|
|
6826
|
-
// Full payload for 'market-data-all' channel
|
|
7758
|
+
// Full payload for 'market-data-all' channel (raw from WS)
|
|
6827
7759
|
const useMarketDataAllPayload = () => {
|
|
6828
7760
|
return useMarketData((s) => s.marketDataAll);
|
|
6829
7761
|
};
|
|
6830
|
-
//
|
|
6831
|
-
const
|
|
6832
|
-
|
|
7762
|
+
// Access perpMetaAssets for enrichment
|
|
7763
|
+
const usePerpMetaAssets = () => {
|
|
7764
|
+
return useHyperliquidData((s) => s.perpMetaAssets);
|
|
7765
|
+
};
|
|
7766
|
+
// Active baskets (with collateral and market prefix info)
|
|
7767
|
+
const useActiveBaskets = (collateralFilter) => {
|
|
6833
7768
|
const data = useMarketDataPayload();
|
|
6834
|
-
|
|
7769
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7770
|
+
return useMemo(() => {
|
|
7771
|
+
if (!(data === null || data === void 0 ? void 0 : data.active))
|
|
7772
|
+
return [];
|
|
7773
|
+
const enriched = data.active.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7774
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7775
|
+
}, [data, perpMetaAssets, collateralFilter]);
|
|
6835
7776
|
};
|
|
6836
|
-
// Top gainers (
|
|
6837
|
-
const useTopGainers = (limit) => {
|
|
7777
|
+
// Top gainers (with collateral and market prefix info)
|
|
7778
|
+
const useTopGainers = (limit, collateralFilter) => {
|
|
6838
7779
|
const data = useMarketDataPayload();
|
|
7780
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6839
7781
|
return useMemo(() => {
|
|
6840
7782
|
var _a;
|
|
6841
7783
|
const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
|
|
6842
|
-
|
|
6843
|
-
|
|
7784
|
+
const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
|
|
7785
|
+
const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7786
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7787
|
+
}, [data, perpMetaAssets, limit, collateralFilter]);
|
|
6844
7788
|
};
|
|
6845
|
-
// Top losers (
|
|
6846
|
-
const useTopLosers = (limit) => {
|
|
7789
|
+
// Top losers (with collateral and market prefix info)
|
|
7790
|
+
const useTopLosers = (limit, collateralFilter) => {
|
|
6847
7791
|
const data = useMarketDataPayload();
|
|
7792
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6848
7793
|
return useMemo(() => {
|
|
6849
7794
|
var _a;
|
|
6850
7795
|
const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
|
|
6851
|
-
|
|
6852
|
-
|
|
7796
|
+
const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
|
|
7797
|
+
const enriched = limited.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7798
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7799
|
+
}, [data, perpMetaAssets, limit, collateralFilter]);
|
|
6853
7800
|
};
|
|
6854
|
-
// Highlighted baskets
|
|
6855
|
-
const useHighlightedBaskets = () => {
|
|
6856
|
-
var _a;
|
|
7801
|
+
// Highlighted baskets (with collateral and market prefix info)
|
|
7802
|
+
const useHighlightedBaskets = (collateralFilter) => {
|
|
6857
7803
|
const data = useMarketDataPayload();
|
|
6858
|
-
|
|
7804
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7805
|
+
return useMemo(() => {
|
|
7806
|
+
if (!(data === null || data === void 0 ? void 0 : data.highlighted))
|
|
7807
|
+
return [];
|
|
7808
|
+
const enriched = data.highlighted.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7809
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7810
|
+
}, [data, perpMetaAssets, collateralFilter]);
|
|
6859
7811
|
};
|
|
6860
|
-
// Watchlist baskets (
|
|
6861
|
-
const useWatchlistBaskets = () => {
|
|
6862
|
-
var _a;
|
|
7812
|
+
// Watchlist baskets (with collateral and market prefix info)
|
|
7813
|
+
const useWatchlistBaskets = (collateralFilter) => {
|
|
6863
7814
|
const data = useMarketDataPayload();
|
|
6864
|
-
|
|
7815
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7816
|
+
return useMemo(() => {
|
|
7817
|
+
if (!(data === null || data === void 0 ? void 0 : data.watchlist))
|
|
7818
|
+
return [];
|
|
7819
|
+
const enriched = data.watchlist.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7820
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7821
|
+
}, [data, perpMetaAssets, collateralFilter]);
|
|
6865
7822
|
};
|
|
6866
|
-
// All baskets (
|
|
6867
|
-
const useAllBaskets = () => {
|
|
6868
|
-
var _a;
|
|
7823
|
+
// All baskets (with collateral and market prefix info)
|
|
7824
|
+
const useAllBaskets = (collateralFilter) => {
|
|
6869
7825
|
const dataAll = useMarketDataAllPayload();
|
|
6870
|
-
|
|
7826
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
7827
|
+
return useMemo(() => {
|
|
7828
|
+
if (!(dataAll === null || dataAll === void 0 ? void 0 : dataAll.all))
|
|
7829
|
+
return [];
|
|
7830
|
+
const enriched = dataAll.all.map((item) => enrichBasketItem(item, perpMetaAssets));
|
|
7831
|
+
return filterByCollateral(enriched, collateralFilter);
|
|
7832
|
+
}, [dataAll, perpMetaAssets, collateralFilter]);
|
|
6871
7833
|
};
|
|
6872
7834
|
// Find a basket by its exact asset composition (order-insensitive)
|
|
6873
7835
|
const useFindBasket = (longs, shorts) => {
|
|
6874
7836
|
const data = useMarketDataPayload();
|
|
7837
|
+
const perpMetaAssets = usePerpMetaAssets();
|
|
6875
7838
|
return useMemo(() => {
|
|
6876
7839
|
if (!data)
|
|
6877
7840
|
return undefined;
|
|
@@ -6885,17 +7848,28 @@ const useFindBasket = (longs, shorts) => {
|
|
|
6885
7848
|
: '';
|
|
6886
7849
|
const lKey = normalize(longs);
|
|
6887
7850
|
const sKey = normalize(shorts);
|
|
6888
|
-
const match = (item) => normalize(item.longAssets) === lKey &&
|
|
6889
|
-
|
|
6890
|
-
|
|
7851
|
+
const match = (item) => normalize(item.longAssets) === lKey &&
|
|
7852
|
+
normalize(item.shortAssets) === sKey;
|
|
7853
|
+
const found = data.active.find(match) || data.highlighted.find(match);
|
|
7854
|
+
return found
|
|
7855
|
+
? enrichBasketItem(found, perpMetaAssets)
|
|
7856
|
+
: undefined;
|
|
7857
|
+
}, [data, longs, shorts, perpMetaAssets]);
|
|
6891
7858
|
};
|
|
6892
7859
|
|
|
6893
|
-
async function toggleWatchlist(baseUrl, longAssets, shortAssets,
|
|
7860
|
+
async function toggleWatchlist(baseUrl, longAssets, shortAssets, hip3Assets) {
|
|
6894
7861
|
const url = joinUrl(baseUrl, '/watchlist');
|
|
6895
|
-
const mapAssets = (arr) => arr.map(a => ({ ...a, asset: toBackendSymbol(a.asset,
|
|
7862
|
+
const mapAssets = (arr) => arr.map((a) => ({ ...a, asset: toBackendSymbol(a.asset, hip3Assets) }));
|
|
6896
7863
|
try {
|
|
6897
|
-
const response = await apiClient.post(url, {
|
|
6898
|
-
|
|
7864
|
+
const response = await apiClient.post(url, {
|
|
7865
|
+
longAssets: mapAssets(longAssets),
|
|
7866
|
+
shortAssets: mapAssets(shortAssets),
|
|
7867
|
+
}, { headers: { 'Content-Type': 'application/json' } });
|
|
7868
|
+
return {
|
|
7869
|
+
data: response.data,
|
|
7870
|
+
status: response.status,
|
|
7871
|
+
headers: response.headers,
|
|
7872
|
+
};
|
|
6899
7873
|
}
|
|
6900
7874
|
catch (error) {
|
|
6901
7875
|
throw toApiError(error);
|
|
@@ -6907,11 +7881,11 @@ function useWatchlist() {
|
|
|
6907
7881
|
if (!context)
|
|
6908
7882
|
throw new Error('useWatchlist must be used within a PearHyperliquidProvider');
|
|
6909
7883
|
const { apiBaseUrl, isConnected } = context;
|
|
6910
|
-
const
|
|
7884
|
+
const hip3Assets = useHyperliquidData((s) => s.hip3Assets);
|
|
6911
7885
|
const marketData = useMarketDataPayload();
|
|
6912
7886
|
const isLoading = useMemo(() => !marketData && isConnected, [marketData, isConnected]);
|
|
6913
7887
|
const toggle = async (longAssets, shortAssets) => {
|
|
6914
|
-
const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets,
|
|
7888
|
+
const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets, hip3Assets);
|
|
6915
7889
|
// Server will push updated market-data over WS; nothing to set here
|
|
6916
7890
|
return resp;
|
|
6917
7891
|
};
|
|
@@ -7165,16 +8139,48 @@ function useAuth() {
|
|
|
7165
8139
|
};
|
|
7166
8140
|
}
|
|
7167
8141
|
|
|
8142
|
+
const useSpotBalances = () => {
|
|
8143
|
+
const spotState = useUserData((state) => state.spotState);
|
|
8144
|
+
return useMemo(() => {
|
|
8145
|
+
if (!spotState) {
|
|
8146
|
+
return {
|
|
8147
|
+
usdhBalance: undefined,
|
|
8148
|
+
spotUsdcBalance: undefined,
|
|
8149
|
+
isLoading: true,
|
|
8150
|
+
};
|
|
8151
|
+
}
|
|
8152
|
+
const balances = spotState.balances || [];
|
|
8153
|
+
let usdhBal = 0;
|
|
8154
|
+
let spotUsdcBal = 0;
|
|
8155
|
+
for (const balance of balances) {
|
|
8156
|
+
const total = parseFloat(balance.total || '0');
|
|
8157
|
+
if (balance.coin === 'USDH') {
|
|
8158
|
+
usdhBal = total;
|
|
8159
|
+
}
|
|
8160
|
+
if (balance.coin === 'USDC') {
|
|
8161
|
+
spotUsdcBal = total;
|
|
8162
|
+
}
|
|
8163
|
+
}
|
|
8164
|
+
return {
|
|
8165
|
+
usdhBalance: usdhBal,
|
|
8166
|
+
spotUsdcBalance: spotUsdcBal,
|
|
8167
|
+
isLoading: false,
|
|
8168
|
+
};
|
|
8169
|
+
}, [spotState]);
|
|
8170
|
+
};
|
|
8171
|
+
|
|
7168
8172
|
const PearHyperliquidContext = createContext(undefined);
|
|
7169
8173
|
/**
|
|
7170
8174
|
* React Provider for PearHyperliquidClient
|
|
7171
8175
|
*/
|
|
7172
|
-
const PearHyperliquidProvider = ({ children, apiBaseUrl =
|
|
8176
|
+
const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearprotocol.io', clientId = 'PEARPROTOCOLUI', wsUrl = 'wss://hl-ui.pearprotocol.io/ws', }) => {
|
|
7173
8177
|
const address = useUserData((s) => s.address);
|
|
7174
8178
|
const setAddress = useUserData((s) => s.setAddress);
|
|
7175
8179
|
const perpsMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
|
|
7176
8180
|
const setPerpMetaAssets = useHyperliquidData((state) => state.setPerpMetaAssets);
|
|
7177
|
-
const
|
|
8181
|
+
const setAllPerpMetaAssets = useHyperliquidData((state) => state.setAllPerpMetaAssets);
|
|
8182
|
+
const setHip3Assets = useHyperliquidData((state) => state.setHip3Assets);
|
|
8183
|
+
const setHip3MarketPrefixes = useHyperliquidData((state) => state.setHip3MarketPrefixes);
|
|
7178
8184
|
const websocketsEnabled = useMemo(() => Array.isArray(perpsMetaAssets) && perpsMetaAssets.length > 0, [perpsMetaAssets]);
|
|
7179
8185
|
const { isConnected, lastError } = useHyperliquidWebSocket({
|
|
7180
8186
|
wsUrl,
|
|
@@ -7189,32 +8195,107 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearpro
|
|
|
7189
8195
|
if (perpsMetaAssets === null) {
|
|
7190
8196
|
fetchAllPerpMetas()
|
|
7191
8197
|
.then((res) => {
|
|
7192
|
-
|
|
7193
|
-
const
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
const
|
|
7198
|
-
const
|
|
7199
|
-
|
|
7200
|
-
const
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
const
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
8198
|
+
const assetToMarkets = new Map();
|
|
8199
|
+
const marketPrefixes = new Map();
|
|
8200
|
+
const FILTERED_PREFIXES = ['vntl', 'hyna'];
|
|
8201
|
+
// Group assets by market prefix to match WebSocket flattening order
|
|
8202
|
+
// WebSocket sends in order: "", "flx", "hyna", "vntl", "xyz"
|
|
8203
|
+
const assetsByPrefix = new Map();
|
|
8204
|
+
const allAssetsByPrefix = new Map();
|
|
8205
|
+
res.data.forEach((item) => {
|
|
8206
|
+
const collateralToken = item.collateralToken === 360 ? 'USDH' : 'USDC';
|
|
8207
|
+
item.universe.forEach((asset) => {
|
|
8208
|
+
var _a;
|
|
8209
|
+
const [maybePrefix, maybeMarket] = asset.name.split(':');
|
|
8210
|
+
if (maybeMarket) {
|
|
8211
|
+
// HIP3 asset with market prefix
|
|
8212
|
+
const prefix = maybePrefix.toLowerCase();
|
|
8213
|
+
const displayName = maybeMarket;
|
|
8214
|
+
const fullName = `${prefix}:${displayName}`;
|
|
8215
|
+
marketPrefixes.set(fullName, prefix);
|
|
8216
|
+
if (!FILTERED_PREFIXES.includes(prefix)) {
|
|
8217
|
+
const existingMarkets = (_a = assetToMarkets.get(displayName)) !== null && _a !== void 0 ? _a : [];
|
|
8218
|
+
if (!existingMarkets.includes(fullName)) {
|
|
8219
|
+
assetToMarkets.set(displayName, [
|
|
8220
|
+
...existingMarkets,
|
|
8221
|
+
fullName,
|
|
8222
|
+
]);
|
|
8223
|
+
}
|
|
8224
|
+
}
|
|
8225
|
+
const assetWithMeta = {
|
|
8226
|
+
...asset,
|
|
8227
|
+
name: displayName,
|
|
8228
|
+
marketPrefix: prefix,
|
|
8229
|
+
collateralToken,
|
|
8230
|
+
};
|
|
8231
|
+
// Group by market prefix
|
|
8232
|
+
const allList = allAssetsByPrefix.get(prefix) || [];
|
|
8233
|
+
allList.push(assetWithMeta);
|
|
8234
|
+
allAssetsByPrefix.set(prefix, allList);
|
|
8235
|
+
if (!FILTERED_PREFIXES.includes(prefix)) {
|
|
8236
|
+
const cleanedList = assetsByPrefix.get(prefix) || [];
|
|
8237
|
+
cleanedList.push(assetWithMeta);
|
|
8238
|
+
assetsByPrefix.set(prefix, cleanedList);
|
|
8239
|
+
}
|
|
8240
|
+
}
|
|
8241
|
+
else {
|
|
8242
|
+
// Default market asset (no prefix)
|
|
8243
|
+
const assetWithMeta = {
|
|
8244
|
+
...asset,
|
|
8245
|
+
collateralToken,
|
|
8246
|
+
};
|
|
8247
|
+
// Add to default market group ("")
|
|
8248
|
+
const defaultList = assetsByPrefix.get('') || [];
|
|
8249
|
+
defaultList.push(assetWithMeta);
|
|
8250
|
+
assetsByPrefix.set('', defaultList);
|
|
8251
|
+
const allDefaultList = allAssetsByPrefix.get('') || [];
|
|
8252
|
+
allDefaultList.push(assetWithMeta);
|
|
8253
|
+
allAssetsByPrefix.set('', allDefaultList);
|
|
8254
|
+
}
|
|
8255
|
+
});
|
|
8256
|
+
});
|
|
8257
|
+
// Flatten in consistent order: default market first, then HIP3 markets alphabetically
|
|
8258
|
+
// This ensures both REST API and WebSocket data align properly
|
|
8259
|
+
const cleanedPrefixes = Array.from(assetsByPrefix.keys()).sort((a, b) => {
|
|
8260
|
+
// Empty prefix (default market) always comes first
|
|
8261
|
+
if (a === '' && b !== '')
|
|
8262
|
+
return -1;
|
|
8263
|
+
if (a !== '' && b === '')
|
|
8264
|
+
return 1;
|
|
8265
|
+
// HIP3 markets sorted alphabetically
|
|
8266
|
+
return a.localeCompare(b);
|
|
8267
|
+
});
|
|
8268
|
+
const allPrefixes = Array.from(allAssetsByPrefix.keys()).sort((a, b) => {
|
|
8269
|
+
if (a === '' && b !== '')
|
|
8270
|
+
return -1;
|
|
8271
|
+
if (a !== '' && b === '')
|
|
8272
|
+
return 1;
|
|
8273
|
+
return a.localeCompare(b);
|
|
7210
8274
|
});
|
|
7211
|
-
|
|
8275
|
+
const cleanedPerpMetas = [];
|
|
8276
|
+
const allPerpMetas = [];
|
|
8277
|
+
cleanedPrefixes.forEach((prefix) => {
|
|
8278
|
+
const assets = assetsByPrefix.get(prefix) || [];
|
|
8279
|
+
cleanedPerpMetas.push(...assets);
|
|
8280
|
+
});
|
|
8281
|
+
allPrefixes.forEach((prefix) => {
|
|
8282
|
+
const assets = allAssetsByPrefix.get(prefix) || [];
|
|
8283
|
+
allPerpMetas.push(...assets);
|
|
8284
|
+
});
|
|
8285
|
+
setHip3Assets(assetToMarkets);
|
|
8286
|
+
setHip3MarketPrefixes(marketPrefixes);
|
|
7212
8287
|
setPerpMetaAssets(cleanedPerpMetas);
|
|
8288
|
+
setAllPerpMetaAssets(allPerpMetas);
|
|
7213
8289
|
})
|
|
7214
8290
|
.catch(() => { });
|
|
7215
8291
|
}
|
|
7216
|
-
}, [
|
|
7217
|
-
|
|
8292
|
+
}, [
|
|
8293
|
+
perpsMetaAssets,
|
|
8294
|
+
setPerpMetaAssets,
|
|
8295
|
+
setAllPerpMetaAssets,
|
|
8296
|
+
setHip3Assets,
|
|
8297
|
+
setHip3MarketPrefixes,
|
|
8298
|
+
]);
|
|
7218
8299
|
useAutoSyncFills({
|
|
7219
8300
|
baseUrl: apiBaseUrl,
|
|
7220
8301
|
address,
|
|
@@ -7252,7 +8333,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearpro
|
|
|
7252
8333
|
function usePearHyperliquid() {
|
|
7253
8334
|
const ctx = useContext(PearHyperliquidContext);
|
|
7254
8335
|
if (!ctx)
|
|
7255
|
-
throw new Error(
|
|
8336
|
+
throw new Error('usePearHyperliquid must be used within a PearHyperliquidProvider');
|
|
7256
8337
|
return ctx;
|
|
7257
8338
|
}
|
|
7258
8339
|
|
|
@@ -7343,4 +8424,4 @@ function mapCandleIntervalToTradingViewInterval(interval) {
|
|
|
7343
8424
|
}
|
|
7344
8425
|
}
|
|
7345
8426
|
|
|
7346
|
-
export { AccountSummaryCalculator, ConflictDetector, MINIMUM_ASSET_USD_VALUE, MinimumPositionSizeError, PearHyperliquidProvider, TokenMetadataExtractor, adjustAdvancePosition, adjustOrder, adjustPosition, calculateMinimumPositionValue, calculateWeightedRatio, cancelOrder, cancelTwap, cancelTwapOrder, closeAllPositions, closePosition, computeBasketCandles, createCandleLookups, createPosition, getCompleteTimestamps, getPortfolio, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, toggleWatchlist, updateRiskParameters, useAccountSummary, useActiveBaskets, useAgentWallet, useAllBaskets, useAuth, useAutoSyncFills, useBasketCandles, useFindBasket, useHighlightedBaskets, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMarketData, useMarketDataAllPayload, useMarketDataPayload, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePortfolio, usePosition, useTokenSelectionMetadata, useTopGainers, useTopLosers, useTradeHistories, useTwap, useUserSelection, useWatchlist, useWatchlistBaskets, useWebData, validateMinimumAssetSize, validatePositionSize };
|
|
8427
|
+
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, updateRiskParameters, useAccountSummary, useActiveBaskets, useAgentWallet, useAllBaskets, useAuth, useAutoSyncFills, useBasketCandles, useFindBasket, useHighlightedBaskets, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMarketData, useMarketDataAllPayload, useMarketDataPayload, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePerpMetaAssets, usePortfolio, usePosition, useSpotBalances, useSpotOrder, useTokenSelectionMetadata, useTopGainers, useTopLosers, useTradeHistories, useTwap, useUserSelection, useWatchlist, useWatchlistBaskets, useWebData, validateMaxAssetsPerLeg, validateMinimumAssetSize, validatePositionSize };
|