@pear-protocol/hyperliquid-sdk 0.0.10 → 0.0.11

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.
@@ -2,3 +2,5 @@ export { useAddress } from './useAddress';
2
2
  export { useTradeHistories, useOpenPositions, useOpenOrders, useAccountSummary } from './useTrading';
3
3
  export { useCalculatedOpenPositions } from './useCalculatedPositions';
4
4
  export { useCalculatedAccountSummary } from './useCalculatedAccountSummary';
5
+ export { useTokenSelection } from './useTokenSelection';
6
+ export type { TokenSelectorConfig, UseTokenSelectionReturn } from './useTokenSelection';
@@ -0,0 +1,33 @@
1
+ import type { TokenSelection, TokenConflict } from '../types';
2
+ import { TokenSelectorConfig } from '../provider';
3
+ export type { TokenSelectorConfig };
4
+ export interface UseTokenSelectionReturn {
5
+ longTokens: TokenSelection[];
6
+ shortTokens: TokenSelection[];
7
+ openTokenSelector: boolean;
8
+ selectorConfig: TokenSelectorConfig | null;
9
+ openConflictModal: boolean;
10
+ conflicts: TokenConflict[];
11
+ isLoading: boolean;
12
+ isPriceDataReady: boolean;
13
+ weightedRatio: number;
14
+ weightedRatio24h: number;
15
+ sumNetFunding: number;
16
+ maxLeverage: number;
17
+ minMargin: number;
18
+ setLongTokens: (tokens: TokenSelection[]) => void;
19
+ setShortTokens: (tokens: TokenSelection[]) => void;
20
+ updateTokenWeight: (isLong: boolean, index: number, newWeight: number) => void;
21
+ addToken: (isLong: boolean) => void;
22
+ removeToken: (isLong: boolean, index: number) => void;
23
+ handleTokenSelect: (selectedToken: string) => void;
24
+ setOpenTokenSelector: (open: boolean) => void;
25
+ setSelectorConfig: (config: TokenSelectorConfig | null) => void;
26
+ setOpenConflictModal: (open: boolean) => void;
27
+ setConflicts: (conflicts: TokenConflict[]) => void;
28
+ resetToDefaults: () => void;
29
+ }
30
+ /**
31
+ * Hook to access token selection state using provider's WebSocket data
32
+ */
33
+ export declare const useTokenSelection: () => UseTokenSelectionReturn;
package/dist/index.d.ts CHANGED
@@ -542,6 +542,37 @@ interface RawPositionDto {
542
542
  longAssets: RawAssetDto[];
543
543
  shortAssets: RawAssetDto[];
544
544
  }
545
+ /**
546
+ * Token metadata from WebData2 and AllMids
547
+ */
548
+ interface TokenMetadata {
549
+ currentPrice: number;
550
+ prevDayPrice: number;
551
+ priceChange24h: number;
552
+ priceChange24hPercent: number;
553
+ netFunding: number;
554
+ maxLeverage: number;
555
+ markPrice: number;
556
+ oraclePrice: number;
557
+ openInterest: string;
558
+ dayVolume: string;
559
+ }
560
+ /**
561
+ * Enhanced token selection with weight and metadata for basket trading
562
+ */
563
+ interface TokenSelection {
564
+ symbol: string;
565
+ weight: number;
566
+ metadata?: TokenMetadata;
567
+ }
568
+ /**
569
+ * Token conflict information for position conflicts
570
+ */
571
+ interface TokenConflict {
572
+ symbol: string;
573
+ conflictType: 'long' | 'short';
574
+ conflictMessage: string;
575
+ }
545
576
 
546
577
  /**
547
578
  * Main SDK client for Pear Protocol Hyperliquid API integration
@@ -644,6 +675,10 @@ declare class PearMigrationSDK {
644
675
  getBaseUrl(): string;
645
676
  }
646
677
 
678
+ interface TokenSelectorConfig {
679
+ isLong: boolean;
680
+ index: number;
681
+ }
647
682
  interface PearHyperliquidProviderProps {
648
683
  config: PearHyperliquidConfig;
649
684
  /**
@@ -715,6 +750,37 @@ declare const useCalculatedOpenPositions: (platformPositions: RawPositionDto[] |
715
750
  */
716
751
  declare const useCalculatedAccountSummary: (platformAccountSummary: AccountSummaryResponseDto | null, platformOpenOrders: OpenLimitOrderDto[] | null, webData2: WebData2Response | null, agentWalletAddress?: string, agentWalletStatus?: string) => AccountSummaryResponseDto | null;
717
752
 
753
+ interface UseTokenSelectionReturn {
754
+ longTokens: TokenSelection[];
755
+ shortTokens: TokenSelection[];
756
+ openTokenSelector: boolean;
757
+ selectorConfig: TokenSelectorConfig | null;
758
+ openConflictModal: boolean;
759
+ conflicts: TokenConflict[];
760
+ isLoading: boolean;
761
+ isPriceDataReady: boolean;
762
+ weightedRatio: number;
763
+ weightedRatio24h: number;
764
+ sumNetFunding: number;
765
+ maxLeverage: number;
766
+ minMargin: number;
767
+ setLongTokens: (tokens: TokenSelection[]) => void;
768
+ setShortTokens: (tokens: TokenSelection[]) => void;
769
+ updateTokenWeight: (isLong: boolean, index: number, newWeight: number) => void;
770
+ addToken: (isLong: boolean) => void;
771
+ removeToken: (isLong: boolean, index: number) => void;
772
+ handleTokenSelect: (selectedToken: string) => void;
773
+ setOpenTokenSelector: (open: boolean) => void;
774
+ setSelectorConfig: (config: TokenSelectorConfig | null) => void;
775
+ setOpenConflictModal: (open: boolean) => void;
776
+ setConflicts: (conflicts: TokenConflict[]) => void;
777
+ resetToDefaults: () => void;
778
+ }
779
+ /**
780
+ * Hook to access token selection state using provider's WebSocket data
781
+ */
782
+ declare const useTokenSelection: () => UseTokenSelectionReturn;
783
+
718
784
  interface WebSocketData {
719
785
  tradeHistories: TradeHistoryDataDto[] | null;
720
786
  openPositions: RawPositionDto[] | null;
@@ -786,5 +852,48 @@ declare class AccountSummaryCalculator {
786
852
  hasRealTimeData(): boolean;
787
853
  }
788
854
 
789
- export { AccountSummaryCalculator, PearHyperliquidClient, PearHyperliquidProvider, PearMigrationSDK, PearHyperliquidClient as default, useAccountSummary, useAddress, useCalculatedAccountSummary, useCalculatedOpenPositions, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMigrationSDK, useOpenOrders, useOpenPositions, usePearHyperliquidClient, useTradeHistories };
790
- export type { AccountSummaryResponseDto, AgentWalletDto, ApiErrorResponse, ApiResponse, AssetCtx, AssetInformationDetail, AssetPosition, BalanceSummaryDto, CrossMarginSummaryDto, CumFundingDto, MarginSummaryDto, MigrationHookState, MigrationHooks, OpenLimitOrderDto, OpenOrderV1Dto, OpenPositionDto, OpenPositionV1Dto, OrderAssetDto, OrderStatus, PearHyperliquidConfig, PnlDto, PositionAssetDetailDto, PositionSideDto, PositionSyncStatus, RawValueDto, SyncOpenOrderDto, SyncOpenOrderResponseDto, SyncOpenPositionDto, SyncOpenPositionResponseDto, SyncTradeHistoryDto, SyncTradeHistoryResponseDto, TpSlDto, TradeHistoryAssetDataDto, TradeHistoryDataDto, TradeHistoryV1Dto, UniverseAsset, WebData2Response, WebSocketChannel, WebSocketConnectionState, WebSocketDataMessage, WebSocketMessage, WebSocketResponse, WebSocketSubscribeMessage, WsAllMidsData };
855
+ /**
856
+ * Detects conflicts between selected tokens and existing positions
857
+ */
858
+ declare class ConflictDetector {
859
+ /**
860
+ * Detects conflicts between token selections and open positions
861
+ * @param longTokens - Selected long tokens
862
+ * @param shortTokens - Selected short tokens
863
+ * @param openPositions - Current open positions from API
864
+ * @returns Array of detected conflicts
865
+ */
866
+ static detectConflicts(longTokens: TokenSelection[], shortTokens: TokenSelection[], openPositions: any[] | null): TokenConflict[];
867
+ }
868
+
869
+ /**
870
+ * Extracts token metadata from WebData2 and AllMids data
871
+ */
872
+ declare class TokenMetadataExtractor {
873
+ /**
874
+ * Extracts comprehensive token metadata
875
+ * @param symbol - Token symbol
876
+ * @param webData2 - WebData2 response containing asset context and universe data
877
+ * @param allMids - AllMids data containing current prices
878
+ * @returns TokenMetadata or null if token not found
879
+ */
880
+ static extractTokenMetadata(symbol: string, webData2: WebData2Response | null, allMids: WsAllMidsData | null): TokenMetadata | null;
881
+ /**
882
+ * Extracts metadata for multiple tokens
883
+ * @param symbols - Array of token symbols
884
+ * @param webData2 - WebData2 response
885
+ * @param allMids - AllMids data
886
+ * @returns Record of symbol to TokenMetadata
887
+ */
888
+ static extractMultipleTokensMetadata(symbols: string[], webData2: WebData2Response | null, allMids: WsAllMidsData | null): Record<string, TokenMetadata | null>;
889
+ /**
890
+ * Checks if token data is available in WebData2
891
+ * @param symbol - Token symbol
892
+ * @param webData2 - WebData2 response
893
+ * @returns boolean indicating if token exists in universe
894
+ */
895
+ static isTokenAvailable(symbol: string, webData2: WebData2Response | null): boolean;
896
+ }
897
+
898
+ export { AccountSummaryCalculator, ConflictDetector, PearHyperliquidClient, PearHyperliquidProvider, PearMigrationSDK, TokenMetadataExtractor, PearHyperliquidClient as default, useAccountSummary, useAddress, useCalculatedAccountSummary, useCalculatedOpenPositions, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMigrationSDK, useOpenOrders, useOpenPositions, usePearHyperliquidClient, useTokenSelection, useTradeHistories };
899
+ export type { AccountSummaryResponseDto, AgentWalletDto, ApiErrorResponse, ApiResponse, AssetCtx, AssetInformationDetail, AssetPosition, BalanceSummaryDto, CrossMarginSummaryDto, CumFundingDto, MarginSummaryDto, MigrationHookState, MigrationHooks, OpenLimitOrderDto, OpenOrderV1Dto, OpenPositionDto, OpenPositionV1Dto, OrderAssetDto, OrderStatus, PearHyperliquidConfig, PnlDto, PositionAssetDetailDto, PositionSideDto, PositionSyncStatus, RawValueDto, SyncOpenOrderDto, SyncOpenOrderResponseDto, SyncOpenPositionDto, SyncOpenPositionResponseDto, SyncTradeHistoryDto, SyncTradeHistoryResponseDto, TokenConflict, TokenMetadata, TokenSelection, TokenSelectorConfig, TpSlDto, TradeHistoryAssetDataDto, TradeHistoryDataDto, TradeHistoryV1Dto, UniverseAsset, UseTokenSelectionReturn, WebData2Response, WebSocketChannel, WebSocketConnectionState, WebSocketDataMessage, WebSocketMessage, WebSocketResponse, WebSocketSubscribeMessage, WsAllMidsData };
package/dist/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import axios from 'axios';
2
- import require$$0, { useState, useEffect, useRef, createContext, useMemo, useContext } from 'react';
2
+ import require$$0, { useState, useEffect, useRef, createContext, useMemo, useContext, useCallback } from 'react';
3
3
  import require$$1 from 'react-dom';
4
4
 
5
5
  /**
@@ -2877,6 +2877,23 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
2877
2877
  };
2878
2878
  };
2879
2879
 
2880
+ const DEFAULT_TOKEN_SELECTION_STATE = {
2881
+ longTokens: [
2882
+ { symbol: 'HYPE', weight: 25 },
2883
+ { symbol: 'BTC', weight: 25 }
2884
+ ],
2885
+ shortTokens: [
2886
+ { symbol: 'AVAX', weight: 10 },
2887
+ { symbol: 'SEI', weight: 10 },
2888
+ { symbol: 'ADA', weight: 10 },
2889
+ { symbol: 'TRUMP', weight: 10 },
2890
+ { symbol: 'SUI', weight: 10 }
2891
+ ],
2892
+ openTokenSelector: false,
2893
+ selectorConfig: null,
2894
+ openConflictModal: false,
2895
+ conflicts: [],
2896
+ };
2880
2897
  const PearHyperliquidContext = createContext(undefined);
2881
2898
  /**
2882
2899
  * React Provider for PearHyperliquidClient
@@ -2886,6 +2903,8 @@ const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/
2886
2903
  const migrationSDK = useMemo(() => new PearMigrationSDK(client), [client]);
2887
2904
  // Address state
2888
2905
  const [address, setAddress] = useState(null);
2906
+ // Token selection state
2907
+ const [tokenSelection, setTokenSelection] = useState(DEFAULT_TOKEN_SELECTION_STATE);
2889
2908
  // WebSocket connection and data (Pear API)
2890
2909
  const { data, connectionStatus, isConnected, lastError } = useHyperliquidWebSocket({
2891
2910
  wsUrl,
@@ -2915,7 +2934,10 @@ const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/
2915
2934
  // HyperLiquid native WebSocket data
2916
2935
  webData2,
2917
2936
  allMids,
2918
- }), [client, migrationSDK, address, connectionStatus, isConnected, data, lastError, nativeConnectionStatus, nativeIsConnected, nativeLastError, webData2, allMids]);
2937
+ // Token selection state
2938
+ tokenSelection,
2939
+ setTokenSelection,
2940
+ }), [client, migrationSDK, address, connectionStatus, isConnected, data, lastError, nativeConnectionStatus, nativeIsConnected, nativeLastError, webData2, allMids, tokenSelection, setTokenSelection]);
2919
2941
  return (jsxRuntimeExports.jsx(PearHyperliquidContext.Provider, { value: contextValue, children: children }));
2920
2942
  };
2921
2943
  /**
@@ -3435,5 +3457,452 @@ const useAccountSummary = () => {
3435
3457
  };
3436
3458
  };
3437
3459
 
3438
- export { AccountSummaryCalculator, PearHyperliquidClient, PearHyperliquidProvider, PearMigrationSDK, PearHyperliquidClient as default, useAccountSummary, useAddress, useCalculatedAccountSummary, useCalculatedOpenPositions, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMigrationSDK, useOpenOrders, useOpenPositions, usePearHyperliquidClient, useTradeHistories };
3460
+ /**
3461
+ * Extracts token metadata from WebData2 and AllMids data
3462
+ */
3463
+ class TokenMetadataExtractor {
3464
+ /**
3465
+ * Extracts comprehensive token metadata
3466
+ * @param symbol - Token symbol
3467
+ * @param webData2 - WebData2 response containing asset context and universe data
3468
+ * @param allMids - AllMids data containing current prices
3469
+ * @returns TokenMetadata or null if token not found
3470
+ */
3471
+ static extractTokenMetadata(symbol, webData2, allMids) {
3472
+ if (!webData2 || !allMids) {
3473
+ return null;
3474
+ }
3475
+ // Find token index in universe
3476
+ const universeIndex = webData2.meta.universe.findIndex(asset => asset.name === symbol);
3477
+ if (universeIndex === -1) {
3478
+ return null;
3479
+ }
3480
+ const universeAsset = webData2.meta.universe[universeIndex];
3481
+ const assetCtx = webData2.assetCtxs[universeIndex];
3482
+ if (!assetCtx) {
3483
+ return null;
3484
+ }
3485
+ // Get current price from allMids
3486
+ const currentPriceStr = allMids.mids[symbol];
3487
+ const currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
3488
+ // Get previous day price
3489
+ const prevDayPrice = parseFloat(assetCtx.prevDayPx);
3490
+ // Calculate 24h price change
3491
+ const priceChange24h = currentPrice - prevDayPrice;
3492
+ const priceChange24hPercent = prevDayPrice !== 0 ? (priceChange24h / prevDayPrice) * 100 : 0;
3493
+ // Parse other metadata
3494
+ const netFunding = parseFloat(assetCtx.funding);
3495
+ const markPrice = parseFloat(assetCtx.markPx);
3496
+ const oraclePrice = parseFloat(assetCtx.oraclePx);
3497
+ return {
3498
+ currentPrice,
3499
+ prevDayPrice,
3500
+ priceChange24h,
3501
+ priceChange24hPercent,
3502
+ netFunding,
3503
+ maxLeverage: universeAsset.maxLeverage,
3504
+ markPrice,
3505
+ oraclePrice,
3506
+ openInterest: assetCtx.openInterest,
3507
+ dayVolume: assetCtx.dayNtlVlm,
3508
+ };
3509
+ }
3510
+ /**
3511
+ * Extracts metadata for multiple tokens
3512
+ * @param symbols - Array of token symbols
3513
+ * @param webData2 - WebData2 response
3514
+ * @param allMids - AllMids data
3515
+ * @returns Record of symbol to TokenMetadata
3516
+ */
3517
+ static extractMultipleTokensMetadata(symbols, webData2, allMids) {
3518
+ const result = {};
3519
+ for (const symbol of symbols) {
3520
+ result[symbol] = this.extractTokenMetadata(symbol, webData2, allMids);
3521
+ }
3522
+ return result;
3523
+ }
3524
+ /**
3525
+ * Checks if token data is available in WebData2
3526
+ * @param symbol - Token symbol
3527
+ * @param webData2 - WebData2 response
3528
+ * @returns boolean indicating if token exists in universe
3529
+ */
3530
+ static isTokenAvailable(symbol, webData2) {
3531
+ if (!webData2)
3532
+ return false;
3533
+ return webData2.meta.universe.some(asset => asset.name === symbol);
3534
+ }
3535
+ }
3536
+
3537
+ /**
3538
+ * Hook to access token selection state using provider's WebSocket data
3539
+ */
3540
+ const useTokenSelection = () => {
3541
+ var _a;
3542
+ const context = useContext(PearHyperliquidContext);
3543
+ if (!context) {
3544
+ throw new Error('useTokenSelection must be used within PearHyperliquidProvider');
3545
+ }
3546
+ const { webData2, allMids, tokenSelection, setTokenSelection } = context;
3547
+ // Loading states
3548
+ const isPriceDataReady = useMemo(() => {
3549
+ return !!(webData2 && allMids);
3550
+ }, [webData2, allMids]);
3551
+ const isLoading = useMemo(() => {
3552
+ // Check if any tokens have missing metadata when price data is available
3553
+ if (!isPriceDataReady)
3554
+ return true;
3555
+ const allTokens = [...tokenSelection.longTokens, ...tokenSelection.shortTokens];
3556
+ if (allTokens.length === 0)
3557
+ return false;
3558
+ // Loading if any token is missing metadata
3559
+ return allTokens.some(token => !token.metadata);
3560
+ }, [isPriceDataReady, tokenSelection.longTokens, tokenSelection.shortTokens]);
3561
+ // Auto-update token metadata when WebSocket data changes
3562
+ useEffect(() => {
3563
+ if (!webData2 || !allMids)
3564
+ return;
3565
+ const allTokenSymbols = [...tokenSelection.longTokens, ...tokenSelection.shortTokens].map(t => t.symbol);
3566
+ if (allTokenSymbols.length === 0)
3567
+ return;
3568
+ const metadataMap = TokenMetadataExtractor.extractMultipleTokensMetadata(allTokenSymbols, webData2, allMids);
3569
+ setTokenSelection(prev => ({
3570
+ ...prev,
3571
+ longTokens: prev.longTokens.map(token => ({
3572
+ ...token,
3573
+ metadata: metadataMap[token.symbol] || undefined
3574
+ })),
3575
+ shortTokens: prev.shortTokens.map(token => ({
3576
+ ...token,
3577
+ metadata: metadataMap[token.symbol] || undefined
3578
+ }))
3579
+ }));
3580
+ }, [webData2, allMids, setTokenSelection]);
3581
+ // Calculate weighted ratio: LONG^WEIGHT * SHORT^-WEIGHT
3582
+ const weightedRatio = useMemo(() => {
3583
+ let longProduct = 1;
3584
+ let shortProduct = 1;
3585
+ // Calculate long side product: PRICE^(WEIGHT/100)
3586
+ tokenSelection.longTokens.forEach(token => {
3587
+ var _a;
3588
+ if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.currentPrice) && token.weight > 0) {
3589
+ const weightFactor = token.weight / 100;
3590
+ longProduct *= Math.pow(token.metadata.currentPrice, weightFactor);
3591
+ }
3592
+ });
3593
+ // Calculate short side product: PRICE^-(WEIGHT/100)
3594
+ tokenSelection.shortTokens.forEach(token => {
3595
+ var _a;
3596
+ if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.currentPrice) && token.weight > 0) {
3597
+ const weightFactor = token.weight / 100;
3598
+ shortProduct *= Math.pow(token.metadata.currentPrice, -weightFactor);
3599
+ }
3600
+ });
3601
+ return longProduct * shortProduct;
3602
+ }, [tokenSelection.longTokens, tokenSelection.shortTokens]);
3603
+ // Calculate 24h weighted ratio using previous day prices
3604
+ const weightedRatio24h = useMemo(() => {
3605
+ let longProduct = 1;
3606
+ let shortProduct = 1;
3607
+ // Calculate long side product: PREV_PRICE^(WEIGHT/100)
3608
+ tokenSelection.longTokens.forEach(token => {
3609
+ var _a;
3610
+ if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.prevDayPrice) && token.weight > 0) {
3611
+ const weightFactor = token.weight / 100;
3612
+ longProduct *= Math.pow(token.metadata.prevDayPrice, weightFactor);
3613
+ }
3614
+ });
3615
+ // Calculate short side product: PREV_PRICE^-(WEIGHT/100)
3616
+ tokenSelection.shortTokens.forEach(token => {
3617
+ var _a;
3618
+ if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.prevDayPrice) && token.weight > 0) {
3619
+ const weightFactor = token.weight / 100;
3620
+ shortProduct *= Math.pow(token.metadata.prevDayPrice, -weightFactor);
3621
+ }
3622
+ });
3623
+ return longProduct * shortProduct;
3624
+ }, [tokenSelection.longTokens, tokenSelection.shortTokens]);
3625
+ // Calculate sum of weighted net funding
3626
+ const sumNetFunding = useMemo(() => {
3627
+ let totalFunding = 0;
3628
+ // Add long funding weighted by allocation
3629
+ tokenSelection.longTokens.forEach(token => {
3630
+ var _a;
3631
+ if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.netFunding) && token.weight > 0) {
3632
+ const weightFactor = token.weight / 100;
3633
+ totalFunding += token.metadata.netFunding * weightFactor;
3634
+ }
3635
+ });
3636
+ // Subtract short funding weighted by allocation (since we're short)
3637
+ tokenSelection.shortTokens.forEach(token => {
3638
+ var _a;
3639
+ if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.netFunding) && token.weight > 0) {
3640
+ const weightFactor = token.weight / 100;
3641
+ totalFunding -= token.metadata.netFunding * weightFactor;
3642
+ }
3643
+ });
3644
+ return totalFunding;
3645
+ }, [tokenSelection.longTokens, tokenSelection.shortTokens]);
3646
+ // Calculate max leverage (minimum of all token maxLeverages)
3647
+ const maxLeverage = useMemo(() => {
3648
+ var _a;
3649
+ if (!((_a = webData2 === null || webData2 === void 0 ? void 0 : webData2.meta) === null || _a === void 0 ? void 0 : _a.universe))
3650
+ return 0;
3651
+ const allTokenSymbols = [...tokenSelection.longTokens, ...tokenSelection.shortTokens].map(t => t.symbol);
3652
+ if (allTokenSymbols.length === 0)
3653
+ return 0;
3654
+ let minLeverage = Infinity;
3655
+ allTokenSymbols.forEach(symbol => {
3656
+ const tokenUniverse = webData2.meta.universe.find(u => u.name === symbol);
3657
+ if (tokenUniverse === null || tokenUniverse === void 0 ? void 0 : tokenUniverse.maxLeverage) {
3658
+ minLeverage = Math.min(minLeverage, tokenUniverse.maxLeverage);
3659
+ }
3660
+ });
3661
+ return minLeverage === Infinity ? 0 : minLeverage;
3662
+ }, [(_a = webData2 === null || webData2 === void 0 ? void 0 : webData2.meta) === null || _a === void 0 ? void 0 : _a.universe, tokenSelection.longTokens, tokenSelection.shortTokens]);
3663
+ // Calculate minimum margin (10 * total number of tokens)
3664
+ const minMargin = useMemo(() => {
3665
+ const totalTokenCount = tokenSelection.longTokens.length + tokenSelection.shortTokens.length;
3666
+ return 10 * totalTokenCount;
3667
+ }, [tokenSelection.longTokens.length, tokenSelection.shortTokens.length]);
3668
+ // Actions
3669
+ const setLongTokens = useCallback((tokens) => {
3670
+ setTokenSelection(prev => ({ ...prev, longTokens: tokens }));
3671
+ }, [setTokenSelection]);
3672
+ const setShortTokens = useCallback((tokens) => {
3673
+ setTokenSelection(prev => ({ ...prev, shortTokens: tokens }));
3674
+ }, [setTokenSelection]);
3675
+ const updateTokenWeight = useCallback((isLong, index, newWeight) => {
3676
+ const clampedWeight = Math.max(1, Math.min(100, newWeight));
3677
+ setTokenSelection(prev => {
3678
+ var _a, _b;
3679
+ const currentLongTotal = prev.longTokens.reduce((sum, token) => sum + token.weight, 0);
3680
+ const currentShortTotal = prev.shortTokens.reduce((sum, token) => sum + token.weight, 0);
3681
+ if (isLong) {
3682
+ const oldWeight = ((_a = prev.longTokens[index]) === null || _a === void 0 ? void 0 : _a.weight) || 0;
3683
+ const weightDiff = clampedWeight - oldWeight;
3684
+ const newLongTotal = currentLongTotal + weightDiff;
3685
+ if (newLongTotal + currentShortTotal > 100) {
3686
+ const maxAllowedWeight = Math.max(1, 100 - currentShortTotal - (currentLongTotal - oldWeight));
3687
+ const updated = [...prev.longTokens];
3688
+ updated[index] = { ...updated[index], weight: maxAllowedWeight };
3689
+ return { ...prev, longTokens: updated };
3690
+ }
3691
+ else {
3692
+ const updated = [...prev.longTokens];
3693
+ updated[index] = { ...updated[index], weight: clampedWeight };
3694
+ return { ...prev, longTokens: updated };
3695
+ }
3696
+ }
3697
+ else {
3698
+ const oldWeight = ((_b = prev.shortTokens[index]) === null || _b === void 0 ? void 0 : _b.weight) || 0;
3699
+ const weightDiff = clampedWeight - oldWeight;
3700
+ const newShortTotal = currentShortTotal + weightDiff;
3701
+ if (currentLongTotal + newShortTotal > 100) {
3702
+ const maxAllowedWeight = Math.max(1, 100 - currentLongTotal - (currentShortTotal - oldWeight));
3703
+ const updated = [...prev.shortTokens];
3704
+ updated[index] = { ...updated[index], weight: maxAllowedWeight };
3705
+ return { ...prev, shortTokens: updated };
3706
+ }
3707
+ else {
3708
+ const updated = [...prev.shortTokens];
3709
+ updated[index] = { ...updated[index], weight: clampedWeight };
3710
+ return { ...prev, shortTokens: updated };
3711
+ }
3712
+ }
3713
+ });
3714
+ }, [setTokenSelection]);
3715
+ const addToken = useCallback((isLong) => {
3716
+ const currentTokens = isLong ? tokenSelection.longTokens : tokenSelection.shortTokens;
3717
+ const newIndex = currentTokens.length;
3718
+ setTokenSelection(prev => ({
3719
+ ...prev,
3720
+ selectorConfig: { isLong, index: newIndex },
3721
+ openTokenSelector: true,
3722
+ }));
3723
+ }, [tokenSelection.longTokens, tokenSelection.shortTokens, setTokenSelection]);
3724
+ const removeToken = useCallback((isLong, index) => {
3725
+ setTokenSelection(prev => {
3726
+ if (isLong) {
3727
+ const updated = prev.longTokens.filter((_, i) => i !== index);
3728
+ return { ...prev, longTokens: updated };
3729
+ }
3730
+ else {
3731
+ const updated = prev.shortTokens.filter((_, i) => i !== index);
3732
+ return { ...prev, shortTokens: updated };
3733
+ }
3734
+ });
3735
+ }, [setTokenSelection]);
3736
+ const setOpenTokenSelector = useCallback((open) => {
3737
+ setTokenSelection(prev => ({ ...prev, openTokenSelector: open }));
3738
+ }, [setTokenSelection]);
3739
+ const setSelectorConfig = useCallback((config) => {
3740
+ setTokenSelection(prev => ({ ...prev, selectorConfig: config }));
3741
+ }, [setTokenSelection]);
3742
+ const setOpenConflictModal = useCallback((open) => {
3743
+ setTokenSelection(prev => ({ ...prev, openConflictModal: open }));
3744
+ }, [setTokenSelection]);
3745
+ const setConflicts = useCallback((conflicts) => {
3746
+ setTokenSelection(prev => ({ ...prev, conflicts }));
3747
+ }, [setTokenSelection]);
3748
+ const resetToDefaults = useCallback(() => {
3749
+ setTokenSelection(prev => ({
3750
+ ...prev,
3751
+ longTokens: [
3752
+ { symbol: 'HYPE', weight: 25 },
3753
+ { symbol: 'BTC', weight: 25 }
3754
+ ],
3755
+ shortTokens: [
3756
+ { symbol: 'AVAX', weight: 10 },
3757
+ { symbol: 'SEI', weight: 10 },
3758
+ { symbol: 'ADA', weight: 10 },
3759
+ { symbol: 'TRUMP', weight: 10 },
3760
+ { symbol: 'SUI', weight: 10 }
3761
+ ],
3762
+ openTokenSelector: false,
3763
+ selectorConfig: null,
3764
+ openConflictModal: false,
3765
+ }));
3766
+ }, [setTokenSelection]);
3767
+ const handleTokenSelect = useCallback((selectedToken) => {
3768
+ if (!tokenSelection.selectorConfig)
3769
+ return;
3770
+ const { isLong, index } = tokenSelection.selectorConfig;
3771
+ const existingTokens = isLong ? tokenSelection.longTokens : tokenSelection.shortTokens;
3772
+ // Check if token already exists
3773
+ if (existingTokens.some(t => t.symbol === selectedToken))
3774
+ return;
3775
+ // Get metadata for new token
3776
+ const metadata = TokenMetadataExtractor.extractTokenMetadata(selectedToken, webData2, allMids);
3777
+ setTokenSelection(prev => {
3778
+ if (index >= existingTokens.length) {
3779
+ // Adding new token - calculate safe weight
3780
+ const currentLongTotal = prev.longTokens.reduce((sum, token) => sum + token.weight, 0);
3781
+ const currentShortTotal = prev.shortTokens.reduce((sum, token) => sum + token.weight, 0);
3782
+ const currentTotal = currentLongTotal + currentShortTotal;
3783
+ const maxAvailableWeight = Math.max(1, 100 - currentTotal);
3784
+ const safeWeight = Math.min(20, maxAvailableWeight);
3785
+ const newToken = {
3786
+ symbol: selectedToken,
3787
+ weight: safeWeight,
3788
+ metadata: metadata || undefined
3789
+ };
3790
+ if (isLong) {
3791
+ return { ...prev, longTokens: [...prev.longTokens, newToken] };
3792
+ }
3793
+ else {
3794
+ return { ...prev, shortTokens: [...prev.shortTokens, newToken] };
3795
+ }
3796
+ }
3797
+ else {
3798
+ // Replacing existing token
3799
+ if (isLong) {
3800
+ const updated = [...prev.longTokens];
3801
+ updated[index] = {
3802
+ ...updated[index],
3803
+ symbol: selectedToken,
3804
+ metadata: metadata || undefined
3805
+ };
3806
+ return { ...prev, longTokens: updated };
3807
+ }
3808
+ else {
3809
+ const updated = [...prev.shortTokens];
3810
+ updated[index] = {
3811
+ ...updated[index],
3812
+ symbol: selectedToken,
3813
+ metadata: metadata || undefined
3814
+ };
3815
+ return { ...prev, shortTokens: updated };
3816
+ }
3817
+ }
3818
+ });
3819
+ }, [tokenSelection.selectorConfig, tokenSelection.longTokens, tokenSelection.shortTokens, webData2, allMids, setTokenSelection]);
3820
+ // updateTokenMetadata is now handled automatically by the useEffect above
3821
+ return {
3822
+ // State
3823
+ longTokens: tokenSelection.longTokens,
3824
+ shortTokens: tokenSelection.shortTokens,
3825
+ openTokenSelector: tokenSelection.openTokenSelector,
3826
+ selectorConfig: tokenSelection.selectorConfig,
3827
+ openConflictModal: tokenSelection.openConflictModal,
3828
+ conflicts: tokenSelection.conflicts,
3829
+ // Loading states
3830
+ isLoading,
3831
+ isPriceDataReady,
3832
+ // Calculated values
3833
+ weightedRatio,
3834
+ weightedRatio24h,
3835
+ sumNetFunding,
3836
+ maxLeverage,
3837
+ minMargin,
3838
+ // Actions
3839
+ setLongTokens,
3840
+ setShortTokens,
3841
+ updateTokenWeight,
3842
+ addToken,
3843
+ removeToken,
3844
+ handleTokenSelect,
3845
+ setOpenTokenSelector,
3846
+ setSelectorConfig,
3847
+ setOpenConflictModal,
3848
+ setConflicts,
3849
+ resetToDefaults,
3850
+ };
3851
+ };
3852
+
3853
+ /**
3854
+ * Detects conflicts between selected tokens and existing positions
3855
+ */
3856
+ class ConflictDetector {
3857
+ /**
3858
+ * Detects conflicts between token selections and open positions
3859
+ * @param longTokens - Selected long tokens
3860
+ * @param shortTokens - Selected short tokens
3861
+ * @param openPositions - Current open positions from API
3862
+ * @returns Array of detected conflicts
3863
+ */
3864
+ static detectConflicts(longTokens, shortTokens, openPositions) {
3865
+ const detectedConflicts = [];
3866
+ if (!openPositions) {
3867
+ return detectedConflicts;
3868
+ }
3869
+ // Check long tokens against existing short positions
3870
+ longTokens.forEach((token) => {
3871
+ const existingShortPosition = openPositions.find((pos) => {
3872
+ var _a;
3873
+ // Check multiple possible property names and side values
3874
+ const symbol = pos.coin || pos.symbol || pos.asset;
3875
+ const side = (_a = pos.side) === null || _a === void 0 ? void 0 : _a.toLowerCase();
3876
+ return symbol === token.symbol && (side === 'short' || side === 'sell');
3877
+ });
3878
+ if (existingShortPosition) {
3879
+ detectedConflicts.push({
3880
+ symbol: token.symbol,
3881
+ conflictType: 'short',
3882
+ conflictMessage: `${token.symbol} Long conflicts with existing ${token.symbol} Short position`
3883
+ });
3884
+ }
3885
+ });
3886
+ // Check short tokens against existing long positions
3887
+ shortTokens.forEach((token) => {
3888
+ const existingLongPosition = openPositions.find((pos) => {
3889
+ var _a;
3890
+ // Check multiple possible property names and side values
3891
+ const symbol = pos.coin || pos.symbol || pos.asset;
3892
+ const side = (_a = pos.side) === null || _a === void 0 ? void 0 : _a.toLowerCase();
3893
+ return symbol === token.symbol && (side === 'long' || side === 'buy');
3894
+ });
3895
+ if (existingLongPosition) {
3896
+ detectedConflicts.push({
3897
+ symbol: token.symbol,
3898
+ conflictType: 'long',
3899
+ conflictMessage: `${token.symbol} Short conflicts with existing ${token.symbol} Long position`
3900
+ });
3901
+ }
3902
+ });
3903
+ return detectedConflicts;
3904
+ }
3905
+ }
3906
+
3907
+ export { AccountSummaryCalculator, ConflictDetector, PearHyperliquidClient, PearHyperliquidProvider, PearMigrationSDK, TokenMetadataExtractor, PearHyperliquidClient as default, useAccountSummary, useAddress, useCalculatedAccountSummary, useCalculatedOpenPositions, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMigrationSDK, useOpenOrders, useOpenPositions, usePearHyperliquidClient, useTokenSelection, useTradeHistories };
3439
3908
  //# sourceMappingURL=index.esm.js.map