@pear-protocol/hyperliquid-sdk 0.1.0 → 0.1.3
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 +8 -1
- package/dist/clients/positions.d.ts +1 -44
- package/dist/hooks/useAgentWallet.d.ts +1 -1
- package/dist/hooks/useAllUserBalances.d.ts +20 -6
- package/dist/hooks/useMarketData.d.ts +14 -8
- package/dist/hooks/usePosition.d.ts +2 -2
- package/dist/hooks/useTokenSelectionMetadata.d.ts +1 -1
- package/dist/hooks/useUserSelection.d.ts +16 -1
- package/dist/index.d.ts +204 -142
- package/dist/index.js +454 -384
- package/dist/store/hyperliquidDataStore.d.ts +8 -2
- package/dist/store/tokenSelectionMetadataStore.d.ts +2 -3
- package/dist/types.d.ts +65 -2
- package/dist/utils/position-validator.d.ts +1 -1
- package/dist/utils/token-metadata-extractor.d.ts +35 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -316,10 +316,10 @@ const getMarketInfoFromSymbol = (symbol) => {
|
|
|
316
316
|
const name = symbol.slice(separatorIndex + 1);
|
|
317
317
|
return {
|
|
318
318
|
symbolName: name || symbol,
|
|
319
|
-
marketName: prefix || '
|
|
319
|
+
marketName: prefix || '',
|
|
320
320
|
};
|
|
321
321
|
}
|
|
322
|
-
return { symbolName: symbol, marketName: '
|
|
322
|
+
return { symbolName: symbol, marketName: '' };
|
|
323
323
|
};
|
|
324
324
|
|
|
325
325
|
/**
|
|
@@ -341,40 +341,60 @@ function symbolsMatch(assetName, searchSymbol) {
|
|
|
341
341
|
*/
|
|
342
342
|
class TokenMetadataExtractor {
|
|
343
343
|
/**
|
|
344
|
-
*
|
|
344
|
+
* Checks if token data is available in aggregated universe assets
|
|
345
|
+
* @param symbol - Token symbol
|
|
346
|
+
* @param perpMetaAssets - Aggregated universe assets
|
|
347
|
+
* @returns boolean indicating if token exists in universe
|
|
348
|
+
*/
|
|
349
|
+
static isTokenAvailable(symbol, perpMetaAssets) {
|
|
350
|
+
if (!perpMetaAssets)
|
|
351
|
+
return false;
|
|
352
|
+
return perpMetaAssets.some((asset) => symbolsMatch(asset.name, symbol));
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Extracts token metadata using DEX-aware lookup (correctly matches perpMetas to assetContexts)
|
|
345
356
|
* @param symbol - Token symbol (e.g., "BTC", "TSLA")
|
|
346
|
-
* @param
|
|
347
|
-
* @param
|
|
357
|
+
* @param perpMetasByDex - Map of DEX name to UniverseAsset[]
|
|
358
|
+
* @param assetContextsByDex - Map of DEX name to WebData3AssetCtx[]
|
|
348
359
|
* @param allMids - AllMids data containing current prices
|
|
349
360
|
* @param activeAssetData - Optional active asset data containing leverage information
|
|
361
|
+
* @param finalAtOICaps - Optional array of symbols at OI caps
|
|
350
362
|
* @returns TokenMetadata or null if token not found
|
|
351
363
|
*/
|
|
352
|
-
static
|
|
353
|
-
|
|
354
|
-
|
|
364
|
+
static extractTokenMetadataByDex(symbol, perpMetasByDex, assetContextsByDex, allMids, activeAssetData, finalAtOICaps) {
|
|
365
|
+
let foundDexName = null;
|
|
366
|
+
let foundAssetIndex = -1;
|
|
367
|
+
let foundAsset = null;
|
|
368
|
+
for (const [dexName, assets] of perpMetasByDex) {
|
|
369
|
+
const assetIndex = assets.findIndex((asset) => symbolsMatch(asset.name, symbol));
|
|
370
|
+
if (assetIndex !== -1) {
|
|
371
|
+
foundDexName = dexName;
|
|
372
|
+
foundAssetIndex = assetIndex;
|
|
373
|
+
foundAsset = assets[assetIndex];
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
355
376
|
}
|
|
356
|
-
|
|
357
|
-
if (universeIndex === -1) {
|
|
377
|
+
if (!foundDexName || foundAssetIndex === -1 || !foundAsset) {
|
|
358
378
|
return null;
|
|
359
379
|
}
|
|
360
|
-
const
|
|
361
|
-
const assetCtx =
|
|
380
|
+
const dexContexts = assetContextsByDex.get(foundDexName);
|
|
381
|
+
const assetCtx = dexContexts === null || dexContexts === void 0 ? void 0 : dexContexts[foundAssetIndex];
|
|
362
382
|
if (!assetCtx) {
|
|
363
383
|
return null;
|
|
364
384
|
}
|
|
365
385
|
// Get current price - prefer assetCtx.midPx as it's already index-matched,
|
|
366
386
|
// fall back to allMids lookup if midPx is null
|
|
367
|
-
const actualSymbol =
|
|
387
|
+
const actualSymbol = foundAsset.name;
|
|
368
388
|
let currentPrice = 0;
|
|
369
|
-
//
|
|
370
|
-
if (assetCtx.midPx) {
|
|
371
|
-
currentPrice = parseFloat(assetCtx.midPx);
|
|
372
|
-
}
|
|
373
|
-
// Fallback: allMids lookup
|
|
389
|
+
// Fallback: assetCtx.midPx (already properly indexed)
|
|
374
390
|
if (!currentPrice || isNaN(currentPrice)) {
|
|
375
391
|
const currentPriceStr = allMids.mids[actualSymbol] || allMids.mids[symbol];
|
|
376
392
|
currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
|
|
377
393
|
}
|
|
394
|
+
// Primary source: allMids lookup
|
|
395
|
+
if (assetCtx.midPx) {
|
|
396
|
+
currentPrice = parseFloat(assetCtx.midPx);
|
|
397
|
+
}
|
|
378
398
|
// Get previous day price
|
|
379
399
|
const prevDayPrice = parseFloat(assetCtx.prevDayPx);
|
|
380
400
|
// Calculate 24h price change
|
|
@@ -390,20 +410,17 @@ class TokenMetadataExtractor {
|
|
|
390
410
|
const maxTradeSzs = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.maxTradeSzs;
|
|
391
411
|
const availableToTrade = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.availableToTrade;
|
|
392
412
|
const { symbolName, marketName } = getMarketInfoFromSymbol(actualSymbol);
|
|
393
|
-
const assetName = symbolName;
|
|
394
413
|
return {
|
|
395
|
-
assetName,
|
|
414
|
+
assetName: foundAsset.name,
|
|
396
415
|
symbolName,
|
|
397
416
|
marketName,
|
|
398
|
-
isAtOiCaps: finalAtOICaps
|
|
399
|
-
? finalAtOICaps.includes(symbol)
|
|
400
|
-
: false,
|
|
417
|
+
isAtOiCaps: finalAtOICaps ? finalAtOICaps.includes(symbol) : false,
|
|
401
418
|
currentPrice,
|
|
402
419
|
prevDayPrice,
|
|
403
420
|
priceChange24h,
|
|
404
421
|
priceChange24hPercent,
|
|
405
422
|
netFunding,
|
|
406
|
-
maxLeverage:
|
|
423
|
+
maxLeverage: foundAsset.maxLeverage,
|
|
407
424
|
markPrice,
|
|
408
425
|
oraclePrice,
|
|
409
426
|
openInterest: assetCtx.openInterest,
|
|
@@ -411,54 +428,24 @@ class TokenMetadataExtractor {
|
|
|
411
428
|
leverage,
|
|
412
429
|
maxTradeSzs,
|
|
413
430
|
availableToTrade,
|
|
414
|
-
collateralToken:
|
|
431
|
+
collateralToken: foundAsset.collateralToken,
|
|
415
432
|
};
|
|
416
433
|
}
|
|
417
|
-
/**
|
|
418
|
-
* Extracts metadata for multiple tokens
|
|
419
|
-
* @param tokens - Array of token strings (e.g., "BTC", "TSLA")
|
|
420
|
-
* @param perpMetaAssets - Aggregated universe assets
|
|
421
|
-
* @param finalAssetContexts - Aggregated asset contexts
|
|
422
|
-
* @param allMids - AllMids data
|
|
423
|
-
* @param activeAssetData - Optional active asset data containing leverage information
|
|
424
|
-
* @returns Record of token string to TokenMetadata (keys match input tokens exactly)
|
|
425
|
-
*/
|
|
426
|
-
static extractMultipleTokensMetadata(tokens, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, finalAtOICaps) {
|
|
427
|
-
const result = {};
|
|
428
|
-
for (const token of tokens) {
|
|
429
|
-
result[token] = this.extractTokenMetadata(token, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, finalAtOICaps);
|
|
430
|
-
}
|
|
431
|
-
return result;
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* Checks if token data is available in aggregated universe assets
|
|
435
|
-
* @param symbol - Token symbol
|
|
436
|
-
* @param perpMetaAssets - Aggregated universe assets
|
|
437
|
-
* @returns boolean indicating if token exists in universe
|
|
438
|
-
*/
|
|
439
|
-
static isTokenAvailable(symbol, perpMetaAssets) {
|
|
440
|
-
if (!perpMetaAssets)
|
|
441
|
-
return false;
|
|
442
|
-
return perpMetaAssets.some((asset) => symbolsMatch(asset.name, symbol));
|
|
443
|
-
}
|
|
444
434
|
}
|
|
445
|
-
|
|
435
|
+
// Helper functions for token metadata
|
|
446
436
|
const buildOiCapSet = (finalAtOICaps) => new Set((finalAtOICaps !== null && finalAtOICaps !== void 0 ? finalAtOICaps : [])
|
|
447
437
|
.filter(Boolean)
|
|
448
438
|
.map((value) => value.toUpperCase()));
|
|
449
|
-
const isAtOiCaps = (
|
|
450
|
-
oiCapSet.has(symbolName.toUpperCase());
|
|
439
|
+
const isAtOiCaps = (assetName, oiCapSet) => oiCapSet.has(assetName.toUpperCase());
|
|
451
440
|
const applyMetadataContext = (symbol, metadata, oiCapSet) => {
|
|
452
441
|
if (!metadata)
|
|
453
442
|
return null;
|
|
454
443
|
const { symbolName, marketName } = getMarketInfoFromSymbol(symbol);
|
|
455
|
-
const assetName = symbolName;
|
|
456
444
|
return {
|
|
457
445
|
...metadata,
|
|
458
|
-
assetName,
|
|
459
446
|
symbolName,
|
|
460
447
|
marketName,
|
|
461
|
-
isAtOiCaps: isAtOiCaps(symbol,
|
|
448
|
+
isAtOiCaps: isAtOiCaps(symbol, oiCapSet),
|
|
462
449
|
};
|
|
463
450
|
};
|
|
464
451
|
const applyOiCapsToMetadataMap = (tokenMetadata, finalAtOICaps) => {
|
|
@@ -472,30 +459,79 @@ const applyOiCapsToMetadataMap = (tokenMetadata, finalAtOICaps) => {
|
|
|
472
459
|
});
|
|
473
460
|
return next;
|
|
474
461
|
};
|
|
475
|
-
const
|
|
476
|
-
if (
|
|
462
|
+
const shouldSkipToken = (asset, oiCapSet) => {
|
|
463
|
+
if (asset.isDelisted)
|
|
464
|
+
return true;
|
|
465
|
+
if (isAtOiCaps(asset.name, oiCapSet))
|
|
466
|
+
return true;
|
|
467
|
+
return false;
|
|
468
|
+
};
|
|
469
|
+
const buildTokenMetadataMap = ({ perpMetasByDex, assetContextsByDex, allMids, activeAssetData, finalAtOICaps, }) => {
|
|
470
|
+
if (!perpMetasByDex || !assetContextsByDex || !allMids) {
|
|
477
471
|
return {};
|
|
478
472
|
}
|
|
479
473
|
const oiCapSet = buildOiCapSet(finalAtOICaps);
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
474
|
+
const metadataMap = {};
|
|
475
|
+
// Iterate through all DEXes and their assets
|
|
476
|
+
for (const [, assets] of perpMetasByDex) {
|
|
477
|
+
for (const asset of assets) {
|
|
478
|
+
if (shouldSkipToken(asset, oiCapSet)) {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
const symbol = asset.name;
|
|
482
|
+
const metadata = TokenMetadataExtractor.extractTokenMetadataByDex(symbol, perpMetasByDex, assetContextsByDex, allMids, activeAssetData, finalAtOICaps);
|
|
483
|
+
metadataMap[symbol] = applyMetadataContext(symbol, metadata, oiCapSet);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
485
486
|
return metadataMap;
|
|
486
487
|
};
|
|
487
|
-
const updateTokenMetadataForSymbols = (prev, symbols, {
|
|
488
|
-
if (!
|
|
488
|
+
const updateTokenMetadataForSymbols = (prev, symbols, { perpMetasByDex, assetContextsByDex, allMids, activeAssetData, finalAtOICaps, }) => {
|
|
489
|
+
if (!perpMetasByDex || !assetContextsByDex || !allMids) {
|
|
489
490
|
return prev;
|
|
490
491
|
}
|
|
491
492
|
const oiCapSet = buildOiCapSet(finalAtOICaps);
|
|
492
493
|
const next = { ...prev };
|
|
493
494
|
symbols.forEach((symbol) => {
|
|
494
|
-
|
|
495
|
+
// Find asset in any DEX
|
|
496
|
+
let foundAsset;
|
|
497
|
+
for (const [, assets] of perpMetasByDex) {
|
|
498
|
+
foundAsset = assets.find((a) => a.name === symbol);
|
|
499
|
+
if (foundAsset)
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
if (foundAsset && shouldSkipToken(foundAsset, oiCapSet)) {
|
|
503
|
+
delete next[symbol];
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
next[symbol] = applyMetadataContext(symbol, TokenMetadataExtractor.extractTokenMetadataByDex(symbol, perpMetasByDex, assetContextsByDex, allMids, activeAssetData, finalAtOICaps), oiCapSet);
|
|
495
507
|
});
|
|
496
508
|
return next;
|
|
497
509
|
};
|
|
498
|
-
const
|
|
510
|
+
const refreshTokenMetadata = (state, overrides, options) => {
|
|
511
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
512
|
+
const inputs = {
|
|
513
|
+
perpMetaAssets: (_a = overrides.perpMetaAssets) !== null && _a !== void 0 ? _a : state.perpMetaAssets,
|
|
514
|
+
finalAssetContexts: (_b = overrides.finalAssetContexts) !== null && _b !== void 0 ? _b : state.finalAssetContexts,
|
|
515
|
+
allMids: (_c = overrides.allMids) !== null && _c !== void 0 ? _c : state.allMids,
|
|
516
|
+
activeAssetData: (_d = overrides.activeAssetData) !== null && _d !== void 0 ? _d : state.activeAssetData,
|
|
517
|
+
finalAtOICaps: (_e = overrides.finalAtOICaps) !== null && _e !== void 0 ? _e : state.finalAtOICaps,
|
|
518
|
+
// DEX-aware inputs
|
|
519
|
+
perpMetasByDex: (_f = overrides.perpMetasByDex) !== null && _f !== void 0 ? _f : state.perpMetasByDex,
|
|
520
|
+
assetContextsByDex: (_g = overrides.assetContextsByDex) !== null && _g !== void 0 ? _g : state.assetContextsByDex,
|
|
521
|
+
};
|
|
522
|
+
if (!inputs.perpMetasByDex || !inputs.assetContextsByDex || !inputs.allMids) {
|
|
523
|
+
return state.tokenMetadata;
|
|
524
|
+
}
|
|
525
|
+
if (options === null || options === void 0 ? void 0 : options.oiCapsOnly) {
|
|
526
|
+
return applyOiCapsToMetadataMap(state.tokenMetadata, inputs.finalAtOICaps);
|
|
527
|
+
}
|
|
528
|
+
if ((_h = options === null || options === void 0 ? void 0 : options.symbols) === null || _h === void 0 ? void 0 : _h.length) {
|
|
529
|
+
return updateTokenMetadataForSymbols(state.tokenMetadata, options.symbols, inputs);
|
|
530
|
+
}
|
|
531
|
+
return buildTokenMetadataMap(inputs);
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
const useHyperliquidData = create((set) => ({
|
|
499
535
|
allMids: null,
|
|
500
536
|
activeAssetData: null,
|
|
501
537
|
candleData: null,
|
|
@@ -505,45 +541,30 @@ const useHyperliquidData = create((set, get) => ({
|
|
|
505
541
|
rawClearinghouseStates: null,
|
|
506
542
|
perpMetaAssets: null,
|
|
507
543
|
tokenMetadata: {},
|
|
544
|
+
perpDexs: null,
|
|
545
|
+
perpMetasByDex: null,
|
|
546
|
+
assetContextsByDex: null,
|
|
508
547
|
setAllMids: (value) => set((state) => ({
|
|
509
548
|
allMids: value,
|
|
510
|
-
tokenMetadata:
|
|
511
|
-
perpMetaAssets: state.perpMetaAssets,
|
|
512
|
-
finalAssetContexts: state.finalAssetContexts,
|
|
513
|
-
allMids: value,
|
|
514
|
-
activeAssetData: state.activeAssetData,
|
|
515
|
-
finalAtOICaps: state.finalAtOICaps,
|
|
516
|
-
}),
|
|
549
|
+
tokenMetadata: refreshTokenMetadata(state, { allMids: value }),
|
|
517
550
|
})),
|
|
518
551
|
setActiveAssetData: (value) => set((state) => {
|
|
519
552
|
const activeAssetData = typeof value === 'function' ? value(state.activeAssetData) : value;
|
|
520
553
|
return {
|
|
521
554
|
activeAssetData,
|
|
522
|
-
tokenMetadata:
|
|
523
|
-
perpMetaAssets: state.perpMetaAssets,
|
|
524
|
-
finalAssetContexts: state.finalAssetContexts,
|
|
525
|
-
allMids: state.allMids,
|
|
526
|
-
activeAssetData,
|
|
527
|
-
finalAtOICaps: state.finalAtOICaps,
|
|
528
|
-
}),
|
|
555
|
+
tokenMetadata: refreshTokenMetadata(state, { activeAssetData }),
|
|
529
556
|
};
|
|
530
557
|
}),
|
|
531
558
|
deleteActiveAssetData: (key) => {
|
|
532
559
|
set((state) => {
|
|
533
560
|
if (!state.activeAssetData || !(key in state.activeAssetData)) {
|
|
534
|
-
return state;
|
|
561
|
+
return state;
|
|
535
562
|
}
|
|
536
|
-
const
|
|
537
|
-
delete
|
|
563
|
+
const activeAssetData = { ...state.activeAssetData };
|
|
564
|
+
delete activeAssetData[key];
|
|
538
565
|
return {
|
|
539
|
-
activeAssetData
|
|
540
|
-
tokenMetadata:
|
|
541
|
-
perpMetaAssets: state.perpMetaAssets,
|
|
542
|
-
finalAssetContexts: state.finalAssetContexts,
|
|
543
|
-
allMids: state.allMids,
|
|
544
|
-
activeAssetData: updated,
|
|
545
|
-
finalAtOICaps: state.finalAtOICaps,
|
|
546
|
-
}),
|
|
566
|
+
activeAssetData,
|
|
567
|
+
tokenMetadata: refreshTokenMetadata(state, { activeAssetData }, { symbols: [key] }),
|
|
547
568
|
};
|
|
548
569
|
});
|
|
549
570
|
},
|
|
@@ -568,47 +589,41 @@ const useHyperliquidData = create((set, get) => ({
|
|
|
568
589
|
setCandleData: (value) => set({ candleData: value }),
|
|
569
590
|
upsertActiveAssetData: (key, value) => set((state) => {
|
|
570
591
|
var _a;
|
|
571
|
-
const activeAssetData = {
|
|
572
|
-
...((_a = state.activeAssetData) !== null && _a !== void 0 ? _a : {}),
|
|
573
|
-
[key]: value,
|
|
574
|
-
};
|
|
592
|
+
const activeAssetData = { ...((_a = state.activeAssetData) !== null && _a !== void 0 ? _a : {}), [key]: value };
|
|
575
593
|
return {
|
|
576
594
|
activeAssetData,
|
|
577
|
-
tokenMetadata:
|
|
578
|
-
perpMetaAssets: state.perpMetaAssets,
|
|
579
|
-
finalAssetContexts: state.finalAssetContexts,
|
|
580
|
-
allMids: state.allMids,
|
|
581
|
-
activeAssetData,
|
|
582
|
-
finalAtOICaps: state.finalAtOICaps,
|
|
583
|
-
}),
|
|
595
|
+
tokenMetadata: refreshTokenMetadata(state, { activeAssetData }, { symbols: [key] }),
|
|
584
596
|
};
|
|
585
597
|
}),
|
|
586
598
|
setFinalAssetContexts: (value) => set((state) => ({
|
|
587
599
|
finalAssetContexts: value,
|
|
588
|
-
tokenMetadata:
|
|
589
|
-
perpMetaAssets: state.perpMetaAssets,
|
|
590
|
-
finalAssetContexts: value,
|
|
591
|
-
allMids: state.allMids,
|
|
592
|
-
activeAssetData: state.activeAssetData,
|
|
593
|
-
finalAtOICaps: state.finalAtOICaps,
|
|
594
|
-
}),
|
|
600
|
+
tokenMetadata: refreshTokenMetadata(state, { finalAssetContexts: value }),
|
|
595
601
|
})),
|
|
596
602
|
setFinalAtOICaps: (value) => set((state) => ({
|
|
597
603
|
finalAtOICaps: value,
|
|
598
|
-
tokenMetadata:
|
|
604
|
+
tokenMetadata: refreshTokenMetadata(state, { finalAtOICaps: value }, { oiCapsOnly: true }),
|
|
599
605
|
})),
|
|
600
606
|
setAggregatedClearingHouseState: (value) => set({ aggregatedClearingHouseState: value }),
|
|
601
607
|
setRawClearinghouseStates: (value) => set({ rawClearinghouseStates: value }),
|
|
602
608
|
setPerpMetaAssets: (value) => set((state) => ({
|
|
603
609
|
perpMetaAssets: value,
|
|
604
|
-
tokenMetadata:
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
+
tokenMetadata: refreshTokenMetadata(state, { perpMetaAssets: value }),
|
|
611
|
+
})),
|
|
612
|
+
setPerpDexs: (value) => set({ perpDexs: value }),
|
|
613
|
+
setPerpMetasByDex: (value) => set((state) => ({
|
|
614
|
+
perpMetasByDex: value,
|
|
615
|
+
tokenMetadata: refreshTokenMetadata(state, {
|
|
616
|
+
perpMetasByDex: value,
|
|
617
|
+
assetContextsByDex: state.assetContextsByDex,
|
|
618
|
+
}),
|
|
619
|
+
})),
|
|
620
|
+
setAssetContextsByDex: (value) => set((state) => ({
|
|
621
|
+
assetContextsByDex: value,
|
|
622
|
+
tokenMetadata: refreshTokenMetadata(state, {
|
|
623
|
+
assetContextsByDex: value,
|
|
624
|
+
perpMetasByDex: state.perpMetasByDex,
|
|
610
625
|
}),
|
|
611
|
-
}))
|
|
626
|
+
})),
|
|
612
627
|
}));
|
|
613
628
|
|
|
614
629
|
/**
|
|
@@ -871,7 +886,7 @@ const useUserSelection$1 = create((set, get) => ({
|
|
|
871
886
|
}));
|
|
872
887
|
|
|
873
888
|
const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }) => {
|
|
874
|
-
const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData,
|
|
889
|
+
const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAtOICaps, setAggregatedClearingHouseState, setRawClearinghouseStates, setAssetContextsByDex, } = useHyperliquidData();
|
|
875
890
|
const { setSpotState } = useUserData();
|
|
876
891
|
const { candleInterval } = useUserSelection$1();
|
|
877
892
|
const userSummary = useUserData((state) => state.accountSummary);
|
|
@@ -924,22 +939,13 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
924
939
|
case 'allDexsAssetCtxs':
|
|
925
940
|
{
|
|
926
941
|
const data = response.data;
|
|
927
|
-
//
|
|
928
|
-
const
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
.
|
|
932
|
-
// Sort to match perpMetaAssets order: default market first, then alphabetically
|
|
933
|
-
const prefixA = a[0] || '';
|
|
934
|
-
const prefixB = b[0] || '';
|
|
935
|
-
if (prefixA === '' && prefixB !== '')
|
|
936
|
-
return -1;
|
|
937
|
-
if (prefixA !== '' && prefixB === '')
|
|
938
|
-
return 1;
|
|
939
|
-
return prefixA.localeCompare(prefixB);
|
|
942
|
+
// Store by DEX name, mapping '' to 'HYPERLIQUID'
|
|
943
|
+
const assetContextsByDex = new Map();
|
|
944
|
+
data.ctxs.forEach(([dexKey, ctxs]) => {
|
|
945
|
+
const dexName = dexKey === '' ? 'HYPERLIQUID' : dexKey;
|
|
946
|
+
assetContextsByDex.set(dexName, ctxs || []);
|
|
940
947
|
});
|
|
941
|
-
|
|
942
|
-
setFinalAssetContexts(finalAssetContexts);
|
|
948
|
+
setAssetContextsByDex(assetContextsByDex);
|
|
943
949
|
}
|
|
944
950
|
break;
|
|
945
951
|
case 'allDexsClearinghouseState':
|
|
@@ -1020,10 +1026,10 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1020
1026
|
setAllMids,
|
|
1021
1027
|
upsertActiveAssetData,
|
|
1022
1028
|
addCandleData,
|
|
1023
|
-
setFinalAssetContexts,
|
|
1024
1029
|
setFinalAtOICaps,
|
|
1025
1030
|
setAggregatedClearingHouseState,
|
|
1026
1031
|
setRawClearinghouseStates,
|
|
1032
|
+
setAssetContextsByDex,
|
|
1027
1033
|
setSpotState,
|
|
1028
1034
|
onUserFills,
|
|
1029
1035
|
]);
|
|
@@ -1507,7 +1513,36 @@ const useOpenOrders = () => {
|
|
|
1507
1513
|
};
|
|
1508
1514
|
|
|
1509
1515
|
const useUserSelection = () => {
|
|
1510
|
-
|
|
1516
|
+
const longTokens = useUserSelection$1((s) => s.longTokens);
|
|
1517
|
+
const shortTokens = useUserSelection$1((s) => s.shortTokens);
|
|
1518
|
+
const candleInterval = useUserSelection$1((s) => s.candleInterval);
|
|
1519
|
+
const openTokenSelector = useUserSelection$1((s) => s.openTokenSelector);
|
|
1520
|
+
const selectorConfig = useUserSelection$1((s) => s.selectorConfig);
|
|
1521
|
+
const openConflictModal = useUserSelection$1((s) => s.openConflictModal);
|
|
1522
|
+
const setLongTokens = useUserSelection$1((s) => s.setLongTokens);
|
|
1523
|
+
const setShortTokens = useUserSelection$1((s) => s.setShortTokens);
|
|
1524
|
+
const setCandleInterval = useUserSelection$1((s) => s.setCandleInterval);
|
|
1525
|
+
const setTokenSelections = useUserSelection$1((s) => s.setTokenSelections);
|
|
1526
|
+
const setOpenTokenSelector = useUserSelection$1((s) => s.setOpenTokenSelector);
|
|
1527
|
+
const setSelectorConfig = useUserSelection$1((s) => s.setSelectorConfig);
|
|
1528
|
+
const setOpenConflictModal = useUserSelection$1((s) => s.setOpenConflictModal);
|
|
1529
|
+
const addToken = useUserSelection$1((s) => s.addToken);
|
|
1530
|
+
return {
|
|
1531
|
+
longTokens,
|
|
1532
|
+
shortTokens,
|
|
1533
|
+
candleInterval,
|
|
1534
|
+
openTokenSelector,
|
|
1535
|
+
selectorConfig,
|
|
1536
|
+
openConflictModal,
|
|
1537
|
+
setLongTokens,
|
|
1538
|
+
setShortTokens,
|
|
1539
|
+
setCandleInterval,
|
|
1540
|
+
setTokenSelections,
|
|
1541
|
+
setOpenTokenSelector,
|
|
1542
|
+
setSelectorConfig,
|
|
1543
|
+
setOpenConflictModal,
|
|
1544
|
+
addToken,
|
|
1545
|
+
};
|
|
1511
1546
|
};
|
|
1512
1547
|
|
|
1513
1548
|
const useTokenSelectionMetadataStore = create((set) => ({
|
|
@@ -1523,11 +1558,10 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1523
1558
|
volume: "0",
|
|
1524
1559
|
sumNetFunding: 0,
|
|
1525
1560
|
maxLeverage: 0,
|
|
1526
|
-
|
|
1561
|
+
minSize: {},
|
|
1527
1562
|
leverageMatched: true,
|
|
1528
|
-
recompute: ({
|
|
1563
|
+
recompute: ({ tokenMetadata, marketData, longTokens, shortTokens, }) => {
|
|
1529
1564
|
const isPriceDataReady = Object.keys(tokenMetadata).length > 0;
|
|
1530
|
-
// Get token symbols for lookups
|
|
1531
1565
|
const longSymbols = longTokens.map((t) => t.symbol);
|
|
1532
1566
|
const shortSymbols = shortTokens.map((t) => t.symbol);
|
|
1533
1567
|
// Get metadata
|
|
@@ -1650,24 +1684,37 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1650
1684
|
})();
|
|
1651
1685
|
// Max leverage (maximum across all tokens)
|
|
1652
1686
|
const maxLeverage = (() => {
|
|
1653
|
-
if (!
|
|
1687
|
+
if (!tokenMetadata)
|
|
1654
1688
|
return 0;
|
|
1655
1689
|
const allSymbols = [...longSymbols, ...shortSymbols];
|
|
1656
1690
|
if (allSymbols.length === 0)
|
|
1657
1691
|
return 0;
|
|
1658
1692
|
let maxLev = 0;
|
|
1659
1693
|
allSymbols.forEach((symbol) => {
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1694
|
+
var _a;
|
|
1695
|
+
const tokenDetail = tokenMetadata[symbol];
|
|
1696
|
+
if ((_a = tokenDetail === null || tokenDetail === void 0 ? void 0 : tokenDetail.leverage) === null || _a === void 0 ? void 0 : _a.value) {
|
|
1697
|
+
maxLev = Math.max(maxLev, tokenDetail.leverage.value);
|
|
1663
1698
|
}
|
|
1664
1699
|
});
|
|
1665
1700
|
return maxLev;
|
|
1666
1701
|
})();
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
const
|
|
1670
|
-
|
|
1702
|
+
const minSize = (() => {
|
|
1703
|
+
const allSymbols = [...longSymbols, ...shortSymbols];
|
|
1704
|
+
const collateralCounts = {};
|
|
1705
|
+
allSymbols.forEach((symbol) => {
|
|
1706
|
+
var _a;
|
|
1707
|
+
const collateralToken = (_a = tokenMetadata[symbol]) === null || _a === void 0 ? void 0 : _a.collateralToken;
|
|
1708
|
+
if (collateralToken) {
|
|
1709
|
+
collateralCounts[collateralToken] =
|
|
1710
|
+
(collateralCounts[collateralToken] || 0) + 1;
|
|
1711
|
+
}
|
|
1712
|
+
});
|
|
1713
|
+
const minSizeMap = {};
|
|
1714
|
+
Object.entries(collateralCounts).forEach(([collateral, count]) => {
|
|
1715
|
+
minSizeMap[collateral] = 11 * count;
|
|
1716
|
+
});
|
|
1717
|
+
return minSizeMap;
|
|
1671
1718
|
})();
|
|
1672
1719
|
// Whether all tokens have matching leverage
|
|
1673
1720
|
const leverageMatched = (() => {
|
|
@@ -1702,7 +1749,7 @@ const useTokenSelectionMetadataStore = create((set) => ({
|
|
|
1702
1749
|
volume,
|
|
1703
1750
|
sumNetFunding,
|
|
1704
1751
|
maxLeverage,
|
|
1705
|
-
|
|
1752
|
+
minSize,
|
|
1706
1753
|
leverageMatched,
|
|
1707
1754
|
});
|
|
1708
1755
|
},
|
|
@@ -1717,11 +1764,10 @@ const useTokenSelectionMetadata = () => {
|
|
|
1717
1764
|
const tokenMetadata = useHyperliquidData((state) => state.tokenMetadata);
|
|
1718
1765
|
const marketData = useMarketData((state) => state.marketData);
|
|
1719
1766
|
const { longTokens, shortTokens } = useUserSelection$1();
|
|
1720
|
-
const { isLoading, isPriceDataReady, longTokensMetadata, shortTokensMetadata, weightedRatio, weightedRatio24h, priceRatio, priceRatio24h, openInterest, volume, sumNetFunding, maxLeverage,
|
|
1767
|
+
const { isLoading, isPriceDataReady, longTokensMetadata, shortTokensMetadata, weightedRatio, weightedRatio24h, priceRatio, priceRatio24h, openInterest, volume, sumNetFunding, maxLeverage, minSize, leverageMatched, recompute, } = useTokenSelectionMetadataStore();
|
|
1721
1768
|
// Recompute derived metadata when inputs change
|
|
1722
1769
|
useEffect(() => {
|
|
1723
1770
|
recompute({
|
|
1724
|
-
perpMetaAssets,
|
|
1725
1771
|
tokenMetadata,
|
|
1726
1772
|
marketData: marketData || null,
|
|
1727
1773
|
longTokens,
|
|
@@ -1745,8 +1791,8 @@ const useTokenSelectionMetadata = () => {
|
|
|
1745
1791
|
volume,
|
|
1746
1792
|
sumNetFunding,
|
|
1747
1793
|
maxLeverage,
|
|
1748
|
-
minMargin,
|
|
1749
1794
|
leverageMatched,
|
|
1795
|
+
minSize,
|
|
1750
1796
|
};
|
|
1751
1797
|
};
|
|
1752
1798
|
|
|
@@ -5935,6 +5981,26 @@ const fetchExtraAgents = async (user) => {
|
|
|
5935
5981
|
throw toApiError(error);
|
|
5936
5982
|
}
|
|
5937
5983
|
};
|
|
5984
|
+
/**
|
|
5985
|
+
* Fetch perp dexes from HyperLiquid API
|
|
5986
|
+
* Endpoint: https://api.hyperliquid.xyz/info
|
|
5987
|
+
* Payload: { "type": "perpDexs" }
|
|
5988
|
+
* Returns array where index 0 is null (HYPERLIQUID), index 1+ are named DEXes
|
|
5989
|
+
*/
|
|
5990
|
+
const fetchPerpDexs = async () => {
|
|
5991
|
+
const request = { type: 'perpDexs' };
|
|
5992
|
+
try {
|
|
5993
|
+
const response = await axios$1.post('https://api.hyperliquid.xyz/info', request, { headers: { 'Content-Type': 'application/json' } });
|
|
5994
|
+
return {
|
|
5995
|
+
data: response.data,
|
|
5996
|
+
status: response.status,
|
|
5997
|
+
headers: response.headers,
|
|
5998
|
+
};
|
|
5999
|
+
}
|
|
6000
|
+
catch (error) {
|
|
6001
|
+
throw toApiError(error);
|
|
6002
|
+
}
|
|
6003
|
+
};
|
|
5938
6004
|
|
|
5939
6005
|
const useHistoricalPriceData = () => {
|
|
5940
6006
|
const context = useContext(PearHyperliquidContext);
|
|
@@ -6496,7 +6562,6 @@ const usePerformanceOverlays = () => {
|
|
|
6496
6562
|
overlays.forEach(overlay => {
|
|
6497
6563
|
symbols.push(overlay.symbol);
|
|
6498
6564
|
});
|
|
6499
|
-
console.log("final symbols", symbols);
|
|
6500
6565
|
return symbols;
|
|
6501
6566
|
}, [overlays]);
|
|
6502
6567
|
return {
|
|
@@ -7172,132 +7237,62 @@ function useNotifications() {
|
|
|
7172
7237
|
};
|
|
7173
7238
|
}
|
|
7174
7239
|
|
|
7175
|
-
|
|
7176
|
-
function enrichBasketItem(item) {
|
|
7177
|
-
const enrichedLongs = item.longAssets.map((a) => ({
|
|
7178
|
-
...a,
|
|
7179
|
-
}));
|
|
7180
|
-
const enrichedShorts = item.shortAssets.map((a) => ({
|
|
7181
|
-
...a,
|
|
7182
|
-
}));
|
|
7183
|
-
// // Determine collateral type
|
|
7184
|
-
// const allAssets = [...enrichedLongs, ...enrichedShorts];
|
|
7185
|
-
// const hasUsdc = allAssets.some((a) => a.collateralToken === 'USDC');
|
|
7186
|
-
// const hasUsdh = allAssets.some((a) => a.collateralToken === 'USDH');
|
|
7187
|
-
// let collateralType: 'USDC' | 'USDH' | 'MIXED' = 'USDC';
|
|
7188
|
-
// if (hasUsdc && hasUsdh) {
|
|
7189
|
-
// collateralType = 'MIXED';
|
|
7190
|
-
// } else if (hasUsdh) {
|
|
7191
|
-
// collateralType = 'USDH';
|
|
7192
|
-
// }
|
|
7240
|
+
function enrichBasketWithMetadata(basket, tokenMetadata) {
|
|
7193
7241
|
return {
|
|
7194
|
-
...
|
|
7195
|
-
longAssets:
|
|
7196
|
-
|
|
7197
|
-
|
|
7242
|
+
...basket,
|
|
7243
|
+
longAssets: basket.longAssets.map((asset) => {
|
|
7244
|
+
var _a;
|
|
7245
|
+
return ({
|
|
7246
|
+
...asset,
|
|
7247
|
+
metadata: (_a = tokenMetadata[asset.asset]) !== null && _a !== void 0 ? _a : null,
|
|
7248
|
+
});
|
|
7249
|
+
}),
|
|
7250
|
+
shortAssets: basket.shortAssets.map((asset) => {
|
|
7251
|
+
var _a;
|
|
7252
|
+
return ({
|
|
7253
|
+
...asset,
|
|
7254
|
+
metadata: (_a = tokenMetadata[asset.asset]) !== null && _a !== void 0 ? _a : null,
|
|
7255
|
+
});
|
|
7256
|
+
}),
|
|
7198
7257
|
};
|
|
7199
7258
|
}
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
* - 'USDC': Only baskets where ALL assets use USDC (collateralType === 'USDC')
|
|
7203
|
-
* - 'USDH': Only baskets where ALL assets use USDH (collateralType === 'USDH')
|
|
7204
|
-
* - 'ALL' or undefined: No filtering, returns all baskets
|
|
7205
|
-
*/
|
|
7206
|
-
function filterByCollateral(baskets, filter) {
|
|
7207
|
-
if (!filter || filter === 'ALL') {
|
|
7208
|
-
return baskets;
|
|
7209
|
-
}
|
|
7210
|
-
return baskets.filter((basket) => {
|
|
7211
|
-
if (filter === 'USDC') {
|
|
7212
|
-
// Include baskets that are purely USDC or have USDC assets
|
|
7213
|
-
return (basket.collateralType === 'USDC' || basket.collateralType === 'MIXED');
|
|
7214
|
-
}
|
|
7215
|
-
if (filter === 'USDH') {
|
|
7216
|
-
// Include baskets that are purely USDH or have USDH assets
|
|
7217
|
-
return (basket.collateralType === 'USDH' || basket.collateralType === 'MIXED');
|
|
7218
|
-
}
|
|
7219
|
-
return true;
|
|
7220
|
-
});
|
|
7259
|
+
function enrichBasketsWithMetadata(baskets, tokenMetadata) {
|
|
7260
|
+
return baskets.map((basket) => enrichBasketWithMetadata(basket, tokenMetadata));
|
|
7221
7261
|
}
|
|
7222
|
-
|
|
7223
|
-
const
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
// Active baskets
|
|
7227
|
-
const useActiveBaskets = (collateralFilter) => {
|
|
7228
|
-
const data = useMarketDataPayload();
|
|
7229
|
-
return useMemo(() => {
|
|
7230
|
-
if (!(data === null || data === void 0 ? void 0 : data.active))
|
|
7231
|
-
return [];
|
|
7232
|
-
const enriched = data.active.map((item) => enrichBasketItem(item));
|
|
7233
|
-
return filterByCollateral(enriched, collateralFilter);
|
|
7234
|
-
}, [data, collateralFilter]);
|
|
7235
|
-
};
|
|
7236
|
-
// Top gainers
|
|
7237
|
-
const useTopGainers = (limit, collateralFilter) => {
|
|
7238
|
-
const data = useMarketDataPayload();
|
|
7239
|
-
return useMemo(() => {
|
|
7240
|
-
var _a;
|
|
7241
|
-
const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
|
|
7242
|
-
const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
|
|
7243
|
-
const enriched = limited.map((item) => enrichBasketItem(item));
|
|
7244
|
-
return filterByCollateral(enriched, collateralFilter);
|
|
7245
|
-
}, [data, limit, collateralFilter]);
|
|
7246
|
-
};
|
|
7247
|
-
// Top losers
|
|
7248
|
-
const useTopLosers = (limit, collateralFilter) => {
|
|
7249
|
-
const data = useMarketDataPayload();
|
|
7250
|
-
return useMemo(() => {
|
|
7251
|
-
var _a;
|
|
7252
|
-
const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
|
|
7253
|
-
const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
|
|
7254
|
-
const enriched = limited.map((item) => enrichBasketItem(item));
|
|
7255
|
-
return filterByCollateral(enriched, collateralFilter);
|
|
7256
|
-
}, [data, limit, collateralFilter]);
|
|
7257
|
-
};
|
|
7258
|
-
// Highlighted baskets
|
|
7259
|
-
const useHighlightedBaskets = (collateralFilter) => {
|
|
7260
|
-
const data = useMarketDataPayload();
|
|
7262
|
+
const useMarketDataHook = (options = {}) => {
|
|
7263
|
+
const { topGainersLimit, topLosersLimit } = options;
|
|
7264
|
+
const data = useMarketData((s) => s.marketData);
|
|
7265
|
+
const tokenMetadata = useHyperliquidData((s) => s.tokenMetadata);
|
|
7261
7266
|
return useMemo(() => {
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
const
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
: '';
|
|
7292
|
-
const lKey = normalize(longs);
|
|
7293
|
-
const sKey = normalize(shorts);
|
|
7294
|
-
const match = (item) => normalize(item.longAssets) === lKey &&
|
|
7295
|
-
normalize(item.shortAssets) === sKey;
|
|
7296
|
-
const found = data.active.find(match) || data.highlighted.find(match);
|
|
7297
|
-
return found
|
|
7298
|
-
? enrichBasketItem(found)
|
|
7299
|
-
: undefined;
|
|
7300
|
-
}, [data, longs, shorts]);
|
|
7267
|
+
var _a, _b, _c;
|
|
7268
|
+
const activeBaskets = enrichBasketsWithMetadata((_a = data === null || data === void 0 ? void 0 : data.active) !== null && _a !== void 0 ? _a : [], tokenMetadata);
|
|
7269
|
+
const topGainers = (() => {
|
|
7270
|
+
var _a;
|
|
7271
|
+
const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
|
|
7272
|
+
const limited = typeof topGainersLimit === 'number'
|
|
7273
|
+
? list.slice(0, Math.max(0, topGainersLimit))
|
|
7274
|
+
: list;
|
|
7275
|
+
return enrichBasketsWithMetadata(limited, tokenMetadata);
|
|
7276
|
+
})();
|
|
7277
|
+
const topLosers = (() => {
|
|
7278
|
+
var _a;
|
|
7279
|
+
const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
|
|
7280
|
+
const limited = typeof topLosersLimit === 'number'
|
|
7281
|
+
? list.slice(0, Math.max(0, topLosersLimit))
|
|
7282
|
+
: list;
|
|
7283
|
+
return enrichBasketsWithMetadata(limited, tokenMetadata);
|
|
7284
|
+
})();
|
|
7285
|
+
const highlightedBaskets = enrichBasketsWithMetadata((_b = data === null || data === void 0 ? void 0 : data.highlighted) !== null && _b !== void 0 ? _b : [], tokenMetadata);
|
|
7286
|
+
const watchlistBaskets = enrichBasketsWithMetadata((_c = data === null || data === void 0 ? void 0 : data.watchlist) !== null && _c !== void 0 ? _c : [], tokenMetadata);
|
|
7287
|
+
return {
|
|
7288
|
+
marketData: data,
|
|
7289
|
+
activeBaskets,
|
|
7290
|
+
topGainers,
|
|
7291
|
+
topLosers,
|
|
7292
|
+
highlightedBaskets,
|
|
7293
|
+
watchlistBaskets,
|
|
7294
|
+
};
|
|
7295
|
+
}, [data, tokenMetadata, topGainersLimit, topLosersLimit]);
|
|
7301
7296
|
};
|
|
7302
7297
|
|
|
7303
7298
|
async function toggleWatchlist(baseUrl, longAssets, shortAssets) {
|
|
@@ -7323,7 +7318,7 @@ function useWatchlist() {
|
|
|
7323
7318
|
if (!context)
|
|
7324
7319
|
throw new Error('useWatchlist must be used within a PearHyperliquidProvider');
|
|
7325
7320
|
const { apiBaseUrl, isConnected } = context;
|
|
7326
|
-
const marketData =
|
|
7321
|
+
const marketData = useMarketData(s => s.marketData);
|
|
7327
7322
|
const isLoading = useMemo(() => !marketData && isConnected, [marketData, isConnected]);
|
|
7328
7323
|
const toggle = async (longAssets, shortAssets) => {
|
|
7329
7324
|
const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets);
|
|
@@ -7582,104 +7577,174 @@ function useAuth() {
|
|
|
7582
7577
|
|
|
7583
7578
|
const useAllUserBalances = () => {
|
|
7584
7579
|
const spotState = useUserData((state) => state.spotState);
|
|
7585
|
-
const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
7586
|
-
const rawClearinghouseStates = useHyperliquidData((state) => state.rawClearinghouseStates);
|
|
7587
7580
|
const { longTokensMetadata, shortTokensMetadata } = useTokenSelectionMetadata();
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
const
|
|
7592
|
-
|
|
7593
|
-
};
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
let spotUsdhBal = undefined;
|
|
7597
|
-
if (spotState) {
|
|
7598
|
-
const balances = spotState.balances || [];
|
|
7599
|
-
for (const balance of balances) {
|
|
7581
|
+
const { longTokens, shortTokens } = useUserSelection$1();
|
|
7582
|
+
const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
|
|
7583
|
+
const { spotBalances, availableToTrades, isLoading } = useMemo(() => {
|
|
7584
|
+
const isLoading = !spotState;
|
|
7585
|
+
const spotBalances = {};
|
|
7586
|
+
const availableToTrades = {};
|
|
7587
|
+
if (spotState === null || spotState === void 0 ? void 0 : spotState.balances) {
|
|
7588
|
+
for (const balance of spotState.balances) {
|
|
7600
7589
|
const total = parseFloat(balance.total || '0');
|
|
7601
|
-
|
|
7602
|
-
spotUsdcBal = truncateToTwoDecimals(total);
|
|
7603
|
-
}
|
|
7604
|
-
if (balance.coin === 'USDH') {
|
|
7605
|
-
spotUsdhBal = truncateToTwoDecimals(total);
|
|
7606
|
-
}
|
|
7590
|
+
spotBalances[balance.coin] = total;
|
|
7607
7591
|
}
|
|
7608
7592
|
}
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7593
|
+
const allMetadataWithSource = [
|
|
7594
|
+
...Object.values(longTokensMetadata).map((m) => ({
|
|
7595
|
+
metadata: m,
|
|
7596
|
+
isLong: true,
|
|
7597
|
+
})),
|
|
7598
|
+
...Object.values(shortTokensMetadata).map((m) => ({
|
|
7599
|
+
metadata: m,
|
|
7600
|
+
isLong: false,
|
|
7601
|
+
})),
|
|
7618
7602
|
];
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
if ((
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
}
|
|
7629
|
-
if ((metadata === null || metadata === void 0 ? void 0 : metadata.collateralToken) === 'USDC') {
|
|
7630
|
-
availableToTradeUsdcFromAsset = Math.max(availableToTradeUsdcFromAsset, availableValue);
|
|
7631
|
-
return;
|
|
7632
|
-
}
|
|
7633
|
-
if (symbol.includes(':')) {
|
|
7634
|
-
const prefix = symbol.split(':')[0];
|
|
7635
|
-
if (prefix === 'xyz') {
|
|
7636
|
-
availableToTradeUsdcFromAsset = Math.max(availableToTradeUsdcFromAsset, availableValue);
|
|
7637
|
-
}
|
|
7638
|
-
else {
|
|
7639
|
-
availableToTradeUsdhFromAsset = Math.max(availableToTradeUsdhFromAsset, availableValue);
|
|
7640
|
-
}
|
|
7641
|
-
return;
|
|
7603
|
+
for (const { metadata, isLong } of allMetadataWithSource) {
|
|
7604
|
+
if (!(metadata === null || metadata === void 0 ? void 0 : metadata.collateralToken) || !(metadata === null || metadata === void 0 ? void 0 : metadata.availableToTrade))
|
|
7605
|
+
continue;
|
|
7606
|
+
const collateralCoin = metadata.collateralToken;
|
|
7607
|
+
const availableToTrade = metadata.availableToTrade;
|
|
7608
|
+
let value = parseFloat(availableToTrade[isLong ? 0 : 1] || '0');
|
|
7609
|
+
if (!(collateralCoin in availableToTrades) ||
|
|
7610
|
+
value < availableToTrades[collateralCoin]) {
|
|
7611
|
+
availableToTrades[collateralCoin] = value;
|
|
7642
7612
|
}
|
|
7643
|
-
availableToTradeUsdcFromAsset = Math.max(availableToTradeUsdcFromAsset, availableValue);
|
|
7644
|
-
});
|
|
7645
|
-
// Calculate USDC available to trade
|
|
7646
|
-
// Priority 1: Use value from activeAssetData if available (> 0)
|
|
7647
|
-
// Priority 2: Calculate from USDC-specific clearinghouseState (empty prefix)
|
|
7648
|
-
let availableToTradeUsdcValue = undefined;
|
|
7649
|
-
if (availableToTradeUsdcFromAsset > 0) {
|
|
7650
|
-
availableToTradeUsdcValue = availableToTradeUsdcFromAsset;
|
|
7651
7613
|
}
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
const usdcMarket = rawClearinghouseStates.find(([prefix]) => prefix === '');
|
|
7655
|
-
const usdcState = usdcMarket === null || usdcMarket === void 0 ? void 0 : usdcMarket[1];
|
|
7656
|
-
if (usdcState === null || usdcState === void 0 ? void 0 : usdcState.marginSummary) {
|
|
7657
|
-
const accountValue = parseFloat(usdcState.marginSummary.accountValue || '0');
|
|
7658
|
-
const totalMarginUsed = parseFloat(usdcState.marginSummary.totalMarginUsed || '0');
|
|
7659
|
-
const calculatedValue = Math.max(0, accountValue - totalMarginUsed);
|
|
7660
|
-
availableToTradeUsdcValue = truncateToTwoDecimals(calculatedValue);
|
|
7661
|
-
}
|
|
7614
|
+
if (!availableToTrades['USDC']) {
|
|
7615
|
+
availableToTrades['USDC'] = parseFloat((aggregatedClearingHouseState === null || aggregatedClearingHouseState === void 0 ? void 0 : aggregatedClearingHouseState.marginSummary.totalRawUsd) || '0');
|
|
7662
7616
|
}
|
|
7663
|
-
// Calculate USDH available to trade
|
|
7664
|
-
// Priority 1: Use value from activeAssetData if available (> 0)
|
|
7665
|
-
// Priority 2: Use spot USDH balance
|
|
7666
|
-
const availableToTradeUsdhValue = availableToTradeUsdhFromAsset > 0
|
|
7667
|
-
? availableToTradeUsdhFromAsset
|
|
7668
|
-
: spotUsdhBal;
|
|
7669
7617
|
return {
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
spotUsdhBalance: spotUsdhBal,
|
|
7673
|
-
availableToTradeUsdh: availableToTradeUsdhValue,
|
|
7618
|
+
spotBalances,
|
|
7619
|
+
availableToTrades,
|
|
7674
7620
|
isLoading,
|
|
7675
7621
|
};
|
|
7622
|
+
}, [spotState, longTokensMetadata, shortTokensMetadata, aggregatedClearingHouseState]);
|
|
7623
|
+
/**
|
|
7624
|
+
* Calculate margin required for every collateral token based on asset leverages and size.
|
|
7625
|
+
* Returns margin required per collateral and whether there's sufficient margin.
|
|
7626
|
+
*/
|
|
7627
|
+
const getMarginRequired = useCallback((assetsLeverage, size) => {
|
|
7628
|
+
const sizeValue = parseFloat(String(size)) || 0;
|
|
7629
|
+
// Group tokens by collateral type and calculate margin required
|
|
7630
|
+
const marginByCollateral = {};
|
|
7631
|
+
// Process all tokens (long and short)
|
|
7632
|
+
const allTokensWithMetadata = [
|
|
7633
|
+
...longTokens.map((t) => ({
|
|
7634
|
+
...t,
|
|
7635
|
+
metadata: longTokensMetadata[t.symbol],
|
|
7636
|
+
})),
|
|
7637
|
+
...shortTokens.map((t) => ({
|
|
7638
|
+
...t,
|
|
7639
|
+
metadata: shortTokensMetadata[t.symbol],
|
|
7640
|
+
})),
|
|
7641
|
+
];
|
|
7642
|
+
let totalMarginRequired = 0;
|
|
7643
|
+
allTokensWithMetadata.forEach((token) => {
|
|
7644
|
+
var _a;
|
|
7645
|
+
const weight = token.weight || 0;
|
|
7646
|
+
const assetSize = (sizeValue * weight) / 100;
|
|
7647
|
+
const assetLeverage = assetsLeverage[token.symbol] || 1;
|
|
7648
|
+
const assetMargin = assetLeverage > 0 ? assetSize / assetLeverage : 0;
|
|
7649
|
+
// Get collateral type from metadata, default to USDC
|
|
7650
|
+
const collateralToken = ((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.collateralToken) || 'USDC';
|
|
7651
|
+
if (!marginByCollateral[collateralToken]) {
|
|
7652
|
+
marginByCollateral[collateralToken] = 0;
|
|
7653
|
+
}
|
|
7654
|
+
marginByCollateral[collateralToken] += assetMargin;
|
|
7655
|
+
totalMarginRequired += assetMargin;
|
|
7656
|
+
});
|
|
7657
|
+
const perCollateral = Object.entries(marginByCollateral).map(([collateral, marginRequired]) => {
|
|
7658
|
+
var _a;
|
|
7659
|
+
const collateralToken = collateral;
|
|
7660
|
+
const availableBalance = (_a = availableToTrades[collateralToken]) !== null && _a !== void 0 ? _a : 0;
|
|
7661
|
+
const hasEnough = marginRequired <= availableBalance;
|
|
7662
|
+
const shortfall = Math.max(0, marginRequired - availableBalance);
|
|
7663
|
+
return {
|
|
7664
|
+
collateral: collateralToken,
|
|
7665
|
+
marginRequired: Number(marginRequired.toFixed(2)),
|
|
7666
|
+
availableBalance: Number(availableBalance.toFixed(2)),
|
|
7667
|
+
hasEnough,
|
|
7668
|
+
shortfall: Number(shortfall.toFixed(2)),
|
|
7669
|
+
};
|
|
7670
|
+
});
|
|
7671
|
+
const hasEnoughTotal = perCollateral.every((c) => c.hasEnough);
|
|
7672
|
+
return {
|
|
7673
|
+
totalMarginRequired: Number(totalMarginRequired.toFixed(2)),
|
|
7674
|
+
orderValue: sizeValue,
|
|
7675
|
+
perCollateral,
|
|
7676
|
+
hasEnoughTotal,
|
|
7677
|
+
};
|
|
7676
7678
|
}, [
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
rawClearinghouseStates,
|
|
7679
|
+
longTokens,
|
|
7680
|
+
shortTokens,
|
|
7680
7681
|
longTokensMetadata,
|
|
7681
7682
|
shortTokensMetadata,
|
|
7683
|
+
spotBalances,
|
|
7684
|
+
availableToTrades,
|
|
7682
7685
|
]);
|
|
7686
|
+
/**
|
|
7687
|
+
* Calculate the maximum order size ($) based on available balances and asset leverages.
|
|
7688
|
+
* Returns the overall maximum order size constrained by all collateral types.
|
|
7689
|
+
*/
|
|
7690
|
+
const getMaxSize = useCallback((assetsLeverage) => {
|
|
7691
|
+
const marginFactorByCollateral = {};
|
|
7692
|
+
const allTokensWithMetadata = [
|
|
7693
|
+
...longTokens.map((t) => ({
|
|
7694
|
+
...t,
|
|
7695
|
+
metadata: longTokensMetadata[t.symbol],
|
|
7696
|
+
})),
|
|
7697
|
+
...shortTokens.map((t) => ({
|
|
7698
|
+
...t,
|
|
7699
|
+
metadata: shortTokensMetadata[t.symbol],
|
|
7700
|
+
})),
|
|
7701
|
+
];
|
|
7702
|
+
// Calculate the margin factor for each collateral type
|
|
7703
|
+
// marginFactor = sum of (weight / leverage) for all assets using that collateral
|
|
7704
|
+
allTokensWithMetadata.forEach((token) => {
|
|
7705
|
+
var _a;
|
|
7706
|
+
const weight = token.weight || 0;
|
|
7707
|
+
const assetLeverage = assetsLeverage[token.symbol] || 1;
|
|
7708
|
+
const collateralToken = ((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.collateralToken) || 'USDC';
|
|
7709
|
+
// marginFactor represents how much margin is needed per $1 of order size
|
|
7710
|
+
const marginFactor = assetLeverage > 0 ? weight / (100 * assetLeverage) : 0;
|
|
7711
|
+
if (!marginFactorByCollateral[collateralToken]) {
|
|
7712
|
+
marginFactorByCollateral[collateralToken] = 0;
|
|
7713
|
+
}
|
|
7714
|
+
marginFactorByCollateral[collateralToken] += marginFactor;
|
|
7715
|
+
});
|
|
7716
|
+
// Calculate max size for each collateral type
|
|
7717
|
+
// maxSize = availableBalance / marginFactor
|
|
7718
|
+
// Each maxSize represents the total order size that collateral can support
|
|
7719
|
+
const maxSizePerCollateral = [];
|
|
7720
|
+
Object.entries(marginFactorByCollateral).forEach(([collateral, marginFactor]) => {
|
|
7721
|
+
var _a;
|
|
7722
|
+
const collateralToken = collateral;
|
|
7723
|
+
const availableBalance = (_a = availableToTrades[collateralToken]) !== null && _a !== void 0 ? _a : 0;
|
|
7724
|
+
if (marginFactor > 0) {
|
|
7725
|
+
const maxSize = availableBalance / marginFactor;
|
|
7726
|
+
maxSizePerCollateral.push(maxSize);
|
|
7727
|
+
}
|
|
7728
|
+
});
|
|
7729
|
+
if (maxSizePerCollateral.length === 0) {
|
|
7730
|
+
return 0;
|
|
7731
|
+
}
|
|
7732
|
+
const overallMaxSize = Math.min(...maxSizePerCollateral);
|
|
7733
|
+
return Number(overallMaxSize.toFixed(2));
|
|
7734
|
+
}, [
|
|
7735
|
+
longTokens,
|
|
7736
|
+
shortTokens,
|
|
7737
|
+
longTokensMetadata,
|
|
7738
|
+
shortTokensMetadata,
|
|
7739
|
+
availableToTrades,
|
|
7740
|
+
]);
|
|
7741
|
+
return {
|
|
7742
|
+
spotBalances,
|
|
7743
|
+
availableToTrades,
|
|
7744
|
+
isLoading,
|
|
7745
|
+
getMarginRequired,
|
|
7746
|
+
getMaxSize,
|
|
7747
|
+
};
|
|
7683
7748
|
};
|
|
7684
7749
|
|
|
7685
7750
|
/**
|
|
@@ -7821,9 +7886,10 @@ const PearHyperliquidContext = createContext(undefined);
|
|
|
7821
7886
|
const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearprotocol.io', clientId = 'PEARPROTOCOLUI', wsUrl = 'wss://hl-ui.pearprotocol.io/ws', }) => {
|
|
7822
7887
|
const address = useUserData((s) => s.address);
|
|
7823
7888
|
const setAddress = useUserData((s) => s.setAddress);
|
|
7824
|
-
const
|
|
7825
|
-
const
|
|
7826
|
-
const
|
|
7889
|
+
const perpMetasByDex = useHyperliquidData((state) => state.perpMetasByDex);
|
|
7890
|
+
const setPerpDexs = useHyperliquidData((state) => state.setPerpDexs);
|
|
7891
|
+
const setPerpMetasByDex = useHyperliquidData((state) => state.setPerpMetasByDex);
|
|
7892
|
+
const websocketsEnabled = useMemo(() => perpMetasByDex !== null && perpMetasByDex.size > 0, [perpMetasByDex]);
|
|
7827
7893
|
const { handleUserFillsEvent } = useHyperliquidUserFills({
|
|
7828
7894
|
baseUrl: apiBaseUrl,
|
|
7829
7895
|
address,
|
|
@@ -7840,15 +7906,18 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
|
|
|
7840
7906
|
onUserFills: handleUserFillsEvent,
|
|
7841
7907
|
});
|
|
7842
7908
|
useEffect(() => {
|
|
7843
|
-
if (
|
|
7844
|
-
fetchAllPerpMetas()
|
|
7845
|
-
.then((
|
|
7846
|
-
const
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7909
|
+
if (perpMetasByDex === null) {
|
|
7910
|
+
Promise.all([fetchPerpDexs(), fetchAllPerpMetas()])
|
|
7911
|
+
.then(([perpDexsRes, perpMetasRes]) => {
|
|
7912
|
+
const perpDexs = perpDexsRes.data;
|
|
7913
|
+
const perpMetas = perpMetasRes.data;
|
|
7914
|
+
setPerpDexs(perpDexs);
|
|
7915
|
+
const metasByDex = new Map();
|
|
7916
|
+
perpMetas.forEach((item, perpIndex) => {
|
|
7917
|
+
var _a, _b;
|
|
7918
|
+
const dexName = perpIndex === 0
|
|
7919
|
+
? 'HYPERLIQUID'
|
|
7920
|
+
: ((_b = (_a = perpDexs[perpIndex]) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : `DEX_${perpIndex}`);
|
|
7852
7921
|
var collateralToken;
|
|
7853
7922
|
if (item.collateralToken === 360) {
|
|
7854
7923
|
collateralToken = 'USDH';
|
|
@@ -7856,22 +7925,23 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
|
|
|
7856
7925
|
if (item.collateralToken === 0) {
|
|
7857
7926
|
collateralToken = 'USDC';
|
|
7858
7927
|
}
|
|
7859
|
-
item.
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7928
|
+
if (item.collateralToken === 235) {
|
|
7929
|
+
collateralToken = 'USDE';
|
|
7930
|
+
}
|
|
7931
|
+
if (item.collateralToken === 268) {
|
|
7932
|
+
collateralToken = 'USDT0';
|
|
7933
|
+
}
|
|
7934
|
+
const universeAssets = item.universe.map((asset) => ({
|
|
7935
|
+
...asset,
|
|
7936
|
+
collateralToken,
|
|
7937
|
+
}));
|
|
7938
|
+
metasByDex.set(dexName, universeAssets);
|
|
7866
7939
|
});
|
|
7867
|
-
|
|
7940
|
+
setPerpMetasByDex(metasByDex);
|
|
7868
7941
|
})
|
|
7869
7942
|
.catch(() => { });
|
|
7870
7943
|
}
|
|
7871
|
-
}, [
|
|
7872
|
-
perpsMetaAssets,
|
|
7873
|
-
setPerpMetaAssets,
|
|
7874
|
-
]);
|
|
7944
|
+
}, [perpMetasByDex, setPerpDexs, setPerpMetasByDex]);
|
|
7875
7945
|
const contextValue = useMemo(() => ({
|
|
7876
7946
|
// Config
|
|
7877
7947
|
clientId,
|
|
@@ -8155,4 +8225,4 @@ function getOrderTrailingInfo(order) {
|
|
|
8155
8225
|
return undefined;
|
|
8156
8226
|
}
|
|
8157
8227
|
|
|
8158
|
-
export {
|
|
8228
|
+
export { ConflictDetector, MAX_ASSETS_PER_LEG, MINIMUM_ASSET_USD_VALUE, MaxAssetsPerLegError, MinimumPositionSizeError, PearHyperliquidProvider, adjustAdvancePosition, adjustOrder, adjustPosition, calculateMinimumPositionValue, calculateWeightedRatio, cancelOrder, cancelTwap, cancelTwapOrder, closeAllPositions, closePosition, computeBasketCandles, createCandleLookups, createPosition, executeSpotOrder, getCompleteTimestamps, getKalshiMarkets, getOrderDirection, getOrderLadderConfig, getOrderLeverage, getOrderReduceOnly, getOrderTpSlTriggerType, getOrderTrailingInfo, getOrderTriggerType, getOrderTriggerValue, getOrderTwapDuration, getOrderUsdValue, getPortfolio, isBtcDomOrder, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, toggleWatchlist, updateLeverage, updateRiskParameters, useAccountSummary, useAgentWallet, useAllUserBalances, useAuth, useBasketCandles, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidUserFills, useMarket, useMarketData, useMarketDataHook, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePortfolio, usePosition, useSpotOrder, useTokenSelectionMetadata, useTradeHistories, useTwap, useUserSelection, useWatchlist, validateMaxAssetsPerLeg, validateMinimumAssetSize, validatePositionSize };
|