@pear-protocol/hyperliquid-sdk 0.1.0 → 0.1.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.
@@ -1,4 +1,4 @@
1
- import type { ApiResponse, CandleInterval, CandleData, ExternalFillDto, AllPerpMetasResponse, ExtraAgent, TwapSliceFillResponseItem } from '../types';
1
+ import type { ApiResponse, CandleInterval, CandleData, ExternalFillDto, AllPerpMetasResponse, ExtraAgent, TwapSliceFillResponseItem, PerpDexsResponse } from '../types';
2
2
  /**
3
3
  * Fetch historical candle data from HyperLiquid API
4
4
  */
@@ -23,3 +23,10 @@ export declare const fetchAllPerpMetas: () => Promise<ApiResponse<AllPerpMetasRe
23
23
  * Payload: { "type": "extraAgents", "user": "0x..." }
24
24
  */
25
25
  export declare const fetchExtraAgents: (user: string) => Promise<ApiResponse<ExtraAgent[]>>;
26
+ /**
27
+ * Fetch perp dexes from HyperLiquid API
28
+ * Endpoint: https://api.hyperliquid.xyz/info
29
+ * Payload: { "type": "perpDexs" }
30
+ * Returns array where index 0 is null (HYPERLIQUID), index 1+ are named DEXes
31
+ */
32
+ export declare const fetchPerpDexs: () => Promise<ApiResponse<PerpDexsResponse>>;
package/dist/index.d.ts CHANGED
@@ -599,8 +599,10 @@ interface AssetCtx {
599
599
  * Collateral token type
600
600
  * 0 = USDC
601
601
  * 360 = USDH
602
+ * 235 = USDE
603
+ * 268 = USDT
602
604
  */
603
- type CollateralToken = 'USDC' | 'USDH';
605
+ type CollateralToken = 'USDC' | 'USDH' | 'USDE' | 'USDT';
604
606
  /**
605
607
  * Universe asset metadata
606
608
  */
@@ -1483,26 +1485,6 @@ declare class ConflictDetector {
1483
1485
  * Extracts token metadata from aggregated WebData3 contexts and AllMids data
1484
1486
  */
1485
1487
  declare class TokenMetadataExtractor {
1486
- /**
1487
- * Extracts comprehensive token metadata
1488
- * @param symbol - Token symbol (e.g., "BTC", "TSLA")
1489
- * @param perpMetaAssets - Aggregated universe assets (flattened across dexes)
1490
- * @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
1491
- * @param allMids - AllMids data containing current prices
1492
- * @param activeAssetData - Optional active asset data containing leverage information
1493
- * @returns TokenMetadata or null if token not found
1494
- */
1495
- static extractTokenMetadata(symbol: string, perpMetaAssets: UniverseAsset[] | null, finalAssetContexts: WebData3AssetCtx[] | null, allMids: WsAllMidsData | null, activeAssetData?: Record<string, ActiveAssetData> | null, finalAtOICaps?: string[] | null): TokenMetadata | null;
1496
- /**
1497
- * Extracts metadata for multiple tokens
1498
- * @param tokens - Array of token strings (e.g., "BTC", "TSLA")
1499
- * @param perpMetaAssets - Aggregated universe assets
1500
- * @param finalAssetContexts - Aggregated asset contexts
1501
- * @param allMids - AllMids data
1502
- * @param activeAssetData - Optional active asset data containing leverage information
1503
- * @returns Record of token string to TokenMetadata (keys match input tokens exactly)
1504
- */
1505
- static extractMultipleTokensMetadata(tokens: string[], perpMetaAssets: UniverseAsset[] | null, finalAssetContexts: WebData3AssetCtx[] | null, allMids: WsAllMidsData | null, activeAssetData?: Record<string, ActiveAssetData> | null, finalAtOICaps?: string[] | null): Record<string, TokenMetadata | null>;
1506
1488
  /**
1507
1489
  * Checks if token data is available in aggregated universe assets
1508
1490
  * @param symbol - Token symbol
@@ -1510,6 +1492,17 @@ declare class TokenMetadataExtractor {
1510
1492
  * @returns boolean indicating if token exists in universe
1511
1493
  */
1512
1494
  static isTokenAvailable(symbol: string, perpMetaAssets: UniverseAsset[] | null): boolean;
1495
+ /**
1496
+ * Extracts token metadata using DEX-aware lookup (correctly matches perpMetas to assetContexts)
1497
+ * @param symbol - Token symbol (e.g., "BTC", "TSLA")
1498
+ * @param perpMetasByDex - Map of DEX name to UniverseAsset[]
1499
+ * @param assetContextsByDex - Map of DEX name to WebData3AssetCtx[]
1500
+ * @param allMids - AllMids data containing current prices
1501
+ * @param activeAssetData - Optional active asset data containing leverage information
1502
+ * @param finalAtOICaps - Optional array of symbols at OI caps
1503
+ * @returns TokenMetadata or null if token not found
1504
+ */
1505
+ static extractTokenMetadataByDex(symbol: string, perpMetasByDex: Map<string, UniverseAsset[]>, assetContextsByDex: Map<string, WebData3AssetCtx[]>, allMids: WsAllMidsData, activeAssetData?: Record<string, ActiveAssetData> | null, finalAtOICaps?: string[] | null): TokenMetadata | null;
1513
1506
  }
1514
1507
 
1515
1508
  type TokenMetadataBySymbol = Record<string, TokenMetadata | null>;
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 || 'hyperliquid',
319
+ marketName: prefix || '',
320
320
  };
321
321
  }
322
- return { symbolName: symbol, marketName: 'hyperliquid' };
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
- * Extracts comprehensive token metadata
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 perpMetaAssets - Aggregated universe assets (flattened across dexes)
347
- * @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
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 extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, finalAtOICaps) {
353
- if (!perpMetaAssets || !finalAssetContexts || !allMids) {
354
- return null;
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
- const universeIndex = perpMetaAssets.findIndex((asset) => symbolsMatch(asset.name, symbol));
357
- if (universeIndex === -1) {
377
+ if (!foundDexName || foundAssetIndex === -1 || !foundAsset) {
358
378
  return null;
359
379
  }
360
- const universeAsset = perpMetaAssets[universeIndex];
361
- const assetCtx = finalAssetContexts[universeIndex];
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 = universeAsset.name;
387
+ const actualSymbol = foundAsset.name;
368
388
  let currentPrice = 0;
369
- // Primary source: assetCtx.midPx (already properly indexed)
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: universeAsset.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: universeAsset.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 = (symbol, symbolName, oiCapSet) => oiCapSet.has(symbol.toUpperCase()) ||
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, symbolName, oiCapSet),
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 buildTokenMetadataMap = ({ perpMetaAssets, finalAssetContexts, allMids, activeAssetData, finalAtOICaps, }) => {
476
- if (!perpMetaAssets || !finalAssetContexts || !allMids) {
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 symbols = perpMetaAssets.map((asset) => asset.name);
481
- const metadataMap = TokenMetadataExtractor.extractMultipleTokensMetadata(symbols, perpMetaAssets, finalAssetContexts, allMids, activeAssetData, finalAtOICaps);
482
- symbols.forEach((symbol) => {
483
- metadataMap[symbol] = applyMetadataContext(symbol, metadataMap[symbol], oiCapSet);
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, { perpMetaAssets, finalAssetContexts, allMids, activeAssetData, finalAtOICaps, }) => {
488
- if (!perpMetaAssets || !finalAssetContexts || !allMids) {
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
- next[symbol] = applyMetadataContext(symbol, TokenMetadataExtractor.extractTokenMetadata(symbol, perpMetaAssets, finalAssetContexts, allMids, activeAssetData), oiCapSet);
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 useHyperliquidData = create((set, get) => ({
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: buildTokenMetadataMap({
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: buildTokenMetadataMap({
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; // No change if key doesn't exist
561
+ return state;
535
562
  }
536
- const updated = { ...state.activeAssetData };
537
- delete updated[key];
563
+ const activeAssetData = { ...state.activeAssetData };
564
+ delete activeAssetData[key];
538
565
  return {
539
- activeAssetData: updated,
540
- tokenMetadata: updateTokenMetadataForSymbols(state.tokenMetadata, [key], {
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: updateTokenMetadataForSymbols(state.tokenMetadata, [key], {
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: buildTokenMetadataMap({
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: applyOiCapsToMetadataMap(state.tokenMetadata, value),
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: buildTokenMetadataMap({
605
- perpMetaAssets: value,
606
- finalAssetContexts: state.finalAssetContexts,
607
- allMids: state.allMids,
608
- activeAssetData: state.activeAssetData,
609
- finalAtOICaps: state.finalAtOICaps,
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,
610
618
  }),
611
- }))
619
+ })),
620
+ setAssetContextsByDex: (value) => set((state) => ({
621
+ assetContextsByDex: value,
622
+ tokenMetadata: refreshTokenMetadata(state, {
623
+ assetContextsByDex: value,
624
+ perpMetasByDex: state.perpMetasByDex,
625
+ }),
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, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState, setRawClearinghouseStates, } = useHyperliquidData();
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
- // Filter out hyna to match perpMetaAssets filtering
928
- const FILTERED_DEX_PREFIXES = ['hyna'];
929
- const filtered = (data.ctxs || [])
930
- .filter(([prefix]) => !FILTERED_DEX_PREFIXES.includes((prefix || '').toLowerCase()))
931
- .sort((a, b) => {
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
- const finalAssetContexts = filtered.flatMap(([, ctxs]) => ctxs || []);
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
  ]);
@@ -5935,6 +5941,26 @@ const fetchExtraAgents = async (user) => {
5935
5941
  throw toApiError(error);
5936
5942
  }
5937
5943
  };
5944
+ /**
5945
+ * Fetch perp dexes from HyperLiquid API
5946
+ * Endpoint: https://api.hyperliquid.xyz/info
5947
+ * Payload: { "type": "perpDexs" }
5948
+ * Returns array where index 0 is null (HYPERLIQUID), index 1+ are named DEXes
5949
+ */
5950
+ const fetchPerpDexs = async () => {
5951
+ const request = { type: 'perpDexs' };
5952
+ try {
5953
+ const response = await axios$1.post('https://api.hyperliquid.xyz/info', request, { headers: { 'Content-Type': 'application/json' } });
5954
+ return {
5955
+ data: response.data,
5956
+ status: response.status,
5957
+ headers: response.headers,
5958
+ };
5959
+ }
5960
+ catch (error) {
5961
+ throw toApiError(error);
5962
+ }
5963
+ };
5938
5964
 
5939
5965
  const useHistoricalPriceData = () => {
5940
5966
  const context = useContext(PearHyperliquidContext);
@@ -6496,7 +6522,6 @@ const usePerformanceOverlays = () => {
6496
6522
  overlays.forEach(overlay => {
6497
6523
  symbols.push(overlay.symbol);
6498
6524
  });
6499
- console.log("final symbols", symbols);
6500
6525
  return symbols;
6501
6526
  }, [overlays]);
6502
6527
  return {
@@ -7180,16 +7205,6 @@ function enrichBasketItem(item) {
7180
7205
  const enrichedShorts = item.shortAssets.map((a) => ({
7181
7206
  ...a,
7182
7207
  }));
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
- // }
7193
7208
  return {
7194
7209
  ...item,
7195
7210
  longAssets: enrichedLongs,
@@ -7821,9 +7836,10 @@ const PearHyperliquidContext = createContext(undefined);
7821
7836
  const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearprotocol.io', clientId = 'PEARPROTOCOLUI', wsUrl = 'wss://hl-ui.pearprotocol.io/ws', }) => {
7822
7837
  const address = useUserData((s) => s.address);
7823
7838
  const setAddress = useUserData((s) => s.setAddress);
7824
- const perpsMetaAssets = useHyperliquidData((state) => state.perpMetaAssets);
7825
- const setPerpMetaAssets = useHyperliquidData((state) => state.setPerpMetaAssets);
7826
- const websocketsEnabled = useMemo(() => Array.isArray(perpsMetaAssets) && perpsMetaAssets.length > 0, [perpsMetaAssets]);
7839
+ const perpMetasByDex = useHyperliquidData((state) => state.perpMetasByDex);
7840
+ const setPerpDexs = useHyperliquidData((state) => state.setPerpDexs);
7841
+ const setPerpMetasByDex = useHyperliquidData((state) => state.setPerpMetasByDex);
7842
+ const websocketsEnabled = useMemo(() => perpMetasByDex !== null && perpMetasByDex.size > 0, [perpMetasByDex]);
7827
7843
  const { handleUserFillsEvent } = useHyperliquidUserFills({
7828
7844
  baseUrl: apiBaseUrl,
7829
7845
  address,
@@ -7840,15 +7856,18 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
7840
7856
  onUserFills: handleUserFillsEvent,
7841
7857
  });
7842
7858
  useEffect(() => {
7843
- if (perpsMetaAssets === null) {
7844
- fetchAllPerpMetas()
7845
- .then((res) => {
7846
- const allPerpMetas = [];
7847
- res.data.forEach((item) => {
7848
- // Only include USDC and USDH collateral tokens
7849
- if (item.collateralToken !== 360 && item.collateralToken !== 0) {
7850
- return;
7851
- }
7859
+ if (perpMetasByDex === null) {
7860
+ Promise.all([fetchPerpDexs(), fetchAllPerpMetas()])
7861
+ .then(([perpDexsRes, perpMetasRes]) => {
7862
+ const perpDexs = perpDexsRes.data;
7863
+ const perpMetas = perpMetasRes.data;
7864
+ setPerpDexs(perpDexs);
7865
+ const metasByDex = new Map();
7866
+ perpMetas.forEach((item, perpIndex) => {
7867
+ var _a, _b;
7868
+ const dexName = perpIndex === 0
7869
+ ? 'HYPERLIQUID'
7870
+ : ((_b = (_a = perpDexs[perpIndex]) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : `DEX_${perpIndex}`);
7852
7871
  var collateralToken;
7853
7872
  if (item.collateralToken === 360) {
7854
7873
  collateralToken = 'USDH';
@@ -7856,22 +7875,23 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
7856
7875
  if (item.collateralToken === 0) {
7857
7876
  collateralToken = 'USDC';
7858
7877
  }
7859
- item.universe.forEach((asset) => {
7860
- const assetWithMeta = {
7861
- ...asset,
7862
- collateralToken,
7863
- };
7864
- allPerpMetas.push(assetWithMeta);
7865
- });
7878
+ if (item.collateralToken === 235) {
7879
+ collateralToken = 'USDE';
7880
+ }
7881
+ if (item.collateralToken === 268) {
7882
+ collateralToken = 'USDT';
7883
+ }
7884
+ const universeAssets = item.universe.map((asset) => ({
7885
+ ...asset,
7886
+ collateralToken,
7887
+ }));
7888
+ metasByDex.set(dexName, universeAssets);
7866
7889
  });
7867
- setPerpMetaAssets(allPerpMetas);
7890
+ setPerpMetasByDex(metasByDex);
7868
7891
  })
7869
7892
  .catch(() => { });
7870
7893
  }
7871
- }, [
7872
- perpsMetaAssets,
7873
- setPerpMetaAssets,
7874
- ]);
7894
+ }, [perpMetasByDex, setPerpDexs, setPerpMetasByDex]);
7875
7895
  const contextValue = useMemo(() => ({
7876
7896
  // Config
7877
7897
  clientId,
@@ -1,5 +1,5 @@
1
- import { ActiveAssetData, CandleChartData, CandleData, ClearinghouseState, TokenMetadata, UniverseAsset, WebData3AssetCtx, WsAllMidsData } from '../types';
2
- type TokenMetadataBySymbol = Record<string, TokenMetadata | null>;
1
+ import { ActiveAssetData, CandleChartData, CandleData, ClearinghouseState, UniverseAsset, WebData3AssetCtx, WsAllMidsData, PerpDexsResponse } from '../types';
2
+ import { TokenMetadataBySymbol } from '../utils/token-metadata-extractor';
3
3
  interface HyperliquidDataState {
4
4
  allMids: WsAllMidsData | null;
5
5
  activeAssetData: Record<string, ActiveAssetData> | null;
@@ -10,6 +10,9 @@ interface HyperliquidDataState {
10
10
  rawClearinghouseStates: [string, ClearinghouseState][] | null;
11
11
  perpMetaAssets: UniverseAsset[] | null;
12
12
  tokenMetadata: TokenMetadataBySymbol;
13
+ perpDexs: PerpDexsResponse | null;
14
+ perpMetasByDex: Map<string, UniverseAsset[]> | null;
15
+ assetContextsByDex: Map<string, WebData3AssetCtx[]> | null;
13
16
  setAllMids: (value: WsAllMidsData | null) => void;
14
17
  setActiveAssetData: (value: Record<string, ActiveAssetData> | null | ((prev: Record<string, ActiveAssetData> | null) => Record<string, ActiveAssetData> | null)) => void;
15
18
  deleteActiveAssetData: (key: string) => void;
@@ -22,6 +25,9 @@ interface HyperliquidDataState {
22
25
  setAggregatedClearingHouseState: (value: ClearinghouseState | null) => void;
23
26
  setRawClearinghouseStates: (value: [string, ClearinghouseState][] | null) => void;
24
27
  setPerpMetaAssets: (value: UniverseAsset[] | null) => void;
28
+ setPerpDexs: (value: PerpDexsResponse | null) => void;
29
+ setPerpMetasByDex: (value: Map<string, UniverseAsset[]> | null) => void;
30
+ setAssetContextsByDex: (value: Map<string, WebData3AssetCtx[]> | null) => void;
25
31
  }
26
32
  export declare const useHyperliquidData: import("zustand").UseBoundStore<import("zustand").StoreApi<HyperliquidDataState>>;
27
33
  export {};
package/dist/types.d.ts CHANGED
@@ -628,8 +628,10 @@ export interface AssetCtx {
628
628
  * Collateral token type
629
629
  * 0 = USDC
630
630
  * 360 = USDH
631
+ * 235 = USDE
632
+ * 268 = USDT
631
633
  */
632
- export type CollateralToken = 'USDC' | 'USDH';
634
+ export type CollateralToken = 'USDC' | 'USDH' | 'USDE' | 'USDT';
633
635
  /**
634
636
  * Universe asset metadata
635
637
  */
@@ -884,3 +886,16 @@ export interface TokenEntry {
884
886
  symbol: string;
885
887
  data: AssetMarketData;
886
888
  }
889
+ export interface PerpDex {
890
+ name: string;
891
+ fullName: string;
892
+ deployer: string;
893
+ oracleUpdater: string | null;
894
+ feeRecipient: string | null;
895
+ deployerFeeScale: string;
896
+ assetToStreamingOiCap: [string, string][];
897
+ assetToFundingMultiplier: [string, string][];
898
+ subDeployers: [string, string[]][];
899
+ lastDeployerFeeScaleChangeTime: string;
900
+ }
901
+ export type PerpDexsResponse = (PerpDex | null)[];
@@ -3,26 +3,6 @@ import type { WsAllMidsData, TokenMetadata, ActiveAssetData, UniverseAsset, WebD
3
3
  * Extracts token metadata from aggregated WebData3 contexts and AllMids data
4
4
  */
5
5
  export declare class TokenMetadataExtractor {
6
- /**
7
- * Extracts comprehensive token metadata
8
- * @param symbol - Token symbol (e.g., "BTC", "TSLA")
9
- * @param perpMetaAssets - Aggregated universe assets (flattened across dexes)
10
- * @param finalAssetContexts - Aggregated asset contexts (flattened across dexes)
11
- * @param allMids - AllMids data containing current prices
12
- * @param activeAssetData - Optional active asset data containing leverage information
13
- * @returns TokenMetadata or null if token not found
14
- */
15
- static extractTokenMetadata(symbol: string, perpMetaAssets: UniverseAsset[] | null, finalAssetContexts: WebData3AssetCtx[] | null, allMids: WsAllMidsData | null, activeAssetData?: Record<string, ActiveAssetData> | null, finalAtOICaps?: string[] | null): TokenMetadata | null;
16
- /**
17
- * Extracts metadata for multiple tokens
18
- * @param tokens - Array of token strings (e.g., "BTC", "TSLA")
19
- * @param perpMetaAssets - Aggregated universe assets
20
- * @param finalAssetContexts - Aggregated asset contexts
21
- * @param allMids - AllMids data
22
- * @param activeAssetData - Optional active asset data containing leverage information
23
- * @returns Record of token string to TokenMetadata (keys match input tokens exactly)
24
- */
25
- static extractMultipleTokensMetadata(tokens: string[], perpMetaAssets: UniverseAsset[] | null, finalAssetContexts: WebData3AssetCtx[] | null, allMids: WsAllMidsData | null, activeAssetData?: Record<string, ActiveAssetData> | null, finalAtOICaps?: string[] | null): Record<string, TokenMetadata | null>;
26
6
  /**
27
7
  * Checks if token data is available in aggregated universe assets
28
8
  * @param symbol - Token symbol
@@ -30,4 +10,39 @@ export declare class TokenMetadataExtractor {
30
10
  * @returns boolean indicating if token exists in universe
31
11
  */
32
12
  static isTokenAvailable(symbol: string, perpMetaAssets: UniverseAsset[] | null): boolean;
13
+ /**
14
+ * Extracts token metadata using DEX-aware lookup (correctly matches perpMetas to assetContexts)
15
+ * @param symbol - Token symbol (e.g., "BTC", "TSLA")
16
+ * @param perpMetasByDex - Map of DEX name to UniverseAsset[]
17
+ * @param assetContextsByDex - Map of DEX name to WebData3AssetCtx[]
18
+ * @param allMids - AllMids data containing current prices
19
+ * @param activeAssetData - Optional active asset data containing leverage information
20
+ * @param finalAtOICaps - Optional array of symbols at OI caps
21
+ * @returns TokenMetadata or null if token not found
22
+ */
23
+ static extractTokenMetadataByDex(symbol: string, perpMetasByDex: Map<string, UniverseAsset[]>, assetContextsByDex: Map<string, WebData3AssetCtx[]>, allMids: WsAllMidsData, activeAssetData?: Record<string, ActiveAssetData> | null, finalAtOICaps?: string[] | null): TokenMetadata | null;
33
24
  }
25
+ export type TokenMetadataBySymbol = Record<string, TokenMetadata | null>;
26
+ export type TokenMetadataInputs = {
27
+ perpMetaAssets: UniverseAsset[] | null;
28
+ finalAssetContexts: WebData3AssetCtx[] | null;
29
+ allMids: WsAllMidsData | null;
30
+ activeAssetData: Record<string, ActiveAssetData> | null;
31
+ finalAtOICaps: string[] | null;
32
+ perpMetasByDex?: Map<string, UniverseAsset[]> | null;
33
+ assetContextsByDex?: Map<string, WebData3AssetCtx[]> | null;
34
+ };
35
+ export type RefreshTokenMetadataOptions = {
36
+ symbols?: string[];
37
+ oiCapsOnly?: boolean;
38
+ };
39
+ export declare const refreshTokenMetadata: (state: {
40
+ perpMetaAssets: UniverseAsset[] | null;
41
+ finalAssetContexts: WebData3AssetCtx[] | null;
42
+ allMids: WsAllMidsData | null;
43
+ activeAssetData: Record<string, ActiveAssetData> | null;
44
+ finalAtOICaps: string[] | null;
45
+ tokenMetadata: TokenMetadataBySymbol;
46
+ perpMetasByDex?: Map<string, UniverseAsset[]> | null;
47
+ assetContextsByDex?: Map<string, WebData3AssetCtx[]> | null;
48
+ }, overrides: Partial<TokenMetadataInputs>, options?: RefreshTokenMetadataOptions) => TokenMetadataBySymbol;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pear-protocol/hyperliquid-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "React SDK for Pear Protocol Hyperliquid API integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",