@pear-protocol/hyperliquid-sdk 0.0.9 → 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.
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useTokenSelection.d.ts +33 -0
- package/dist/hooks/useTrading.d.ts +20 -8
- package/dist/index.d.ts +148 -23
- package/dist/index.esm.js +616 -105
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +617 -103
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +16 -2
- package/dist/types.d.ts +47 -12
- package/dist/utils/conflict-detector.d.ts +14 -0
- package/dist/utils/token-metadata-extractor.d.ts +29 -0
- package/dist/websocket.d.ts +2 -2
- package/package.json +1 -1
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
|
/**
|
|
@@ -2638,21 +2638,17 @@ const useHyperliquidWebSocket = ({ wsUrl, address }) => {
|
|
|
2638
2638
|
accountSummary: null,
|
|
2639
2639
|
});
|
|
2640
2640
|
const [lastError, setLastError] = useState(null);
|
|
2641
|
+
const [lastSubscribedAddress, setLastSubscribedAddress] = useState(null);
|
|
2641
2642
|
// WebSocket connection
|
|
2642
|
-
const {
|
|
2643
|
-
{
|
|
2643
|
+
const { readyState, sendMessage } = useWebSocket(wsUrl, {
|
|
2644
2644
|
shouldReconnect: () => true,
|
|
2645
2645
|
reconnectAttempts: 5,
|
|
2646
2646
|
reconnectInterval: 3000,
|
|
2647
|
-
|
|
2648
|
-
const isConnected = readyState === dist.ReadyState.OPEN;
|
|
2649
|
-
// Handle incoming WebSocket messages
|
|
2650
|
-
useEffect(() => {
|
|
2651
|
-
if (lastMessage !== null) {
|
|
2647
|
+
onMessage: (event) => {
|
|
2652
2648
|
try {
|
|
2653
|
-
const message = JSON.parse(
|
|
2654
|
-
// Handle subscription responses
|
|
2655
|
-
if ('success' in message || 'error' in message) {
|
|
2649
|
+
const message = JSON.parse(event.data);
|
|
2650
|
+
// Handle subscription responses (only if they don't have channel data)
|
|
2651
|
+
if (('success' in message || 'error' in message) && !('channel' in message)) {
|
|
2656
2652
|
if (message.error) {
|
|
2657
2653
|
setLastError(message.error);
|
|
2658
2654
|
}
|
|
@@ -2664,30 +2660,42 @@ const useHyperliquidWebSocket = ({ wsUrl, address }) => {
|
|
|
2664
2660
|
// Handle channel data messages
|
|
2665
2661
|
if ('channel' in message && 'data' in message) {
|
|
2666
2662
|
const dataMessage = message;
|
|
2663
|
+
// Validate data exists and is not null/undefined
|
|
2664
|
+
if (dataMessage.data === null || dataMessage.data === undefined) {
|
|
2665
|
+
return;
|
|
2666
|
+
}
|
|
2667
2667
|
switch (dataMessage.channel) {
|
|
2668
2668
|
case 'trade-histories':
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2669
|
+
if (Array.isArray(dataMessage.data)) {
|
|
2670
|
+
setData(prev => ({
|
|
2671
|
+
...prev,
|
|
2672
|
+
tradeHistories: dataMessage.data
|
|
2673
|
+
}));
|
|
2674
|
+
}
|
|
2673
2675
|
break;
|
|
2674
2676
|
case 'open-positions':
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2677
|
+
if (Array.isArray(dataMessage.data)) {
|
|
2678
|
+
setData(prev => ({
|
|
2679
|
+
...prev,
|
|
2680
|
+
openPositions: dataMessage.data
|
|
2681
|
+
}));
|
|
2682
|
+
}
|
|
2679
2683
|
break;
|
|
2680
2684
|
case 'open-orders':
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
+
if (Array.isArray(dataMessage.data)) {
|
|
2686
|
+
setData(prev => ({
|
|
2687
|
+
...prev,
|
|
2688
|
+
openOrders: dataMessage.data
|
|
2689
|
+
}));
|
|
2690
|
+
}
|
|
2685
2691
|
break;
|
|
2686
2692
|
case 'account-summary':
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2693
|
+
if (typeof dataMessage.data === 'object' && dataMessage.data !== null) {
|
|
2694
|
+
setData(prev => ({
|
|
2695
|
+
...prev,
|
|
2696
|
+
accountSummary: dataMessage.data
|
|
2697
|
+
}));
|
|
2698
|
+
}
|
|
2691
2699
|
break;
|
|
2692
2700
|
}
|
|
2693
2701
|
}
|
|
@@ -2696,34 +2704,47 @@ const useHyperliquidWebSocket = ({ wsUrl, address }) => {
|
|
|
2696
2704
|
setLastError(`Failed to parse message: ${error instanceof Error ? error.message : String(error)}`);
|
|
2697
2705
|
}
|
|
2698
2706
|
}
|
|
2699
|
-
}
|
|
2700
|
-
|
|
2707
|
+
});
|
|
2708
|
+
const isConnected = readyState === dist.ReadyState.OPEN;
|
|
2709
|
+
// Handle subscription management
|
|
2701
2710
|
useEffect(() => {
|
|
2702
|
-
if (isConnected && address) {
|
|
2711
|
+
if (isConnected && address && address !== lastSubscribedAddress) {
|
|
2712
|
+
// Unsubscribe from previous address if exists
|
|
2713
|
+
if (lastSubscribedAddress) {
|
|
2714
|
+
sendMessage(JSON.stringify({
|
|
2715
|
+
action: 'unsubscribe',
|
|
2716
|
+
address: lastSubscribedAddress
|
|
2717
|
+
}));
|
|
2718
|
+
}
|
|
2703
2719
|
// Subscribe to new address
|
|
2704
2720
|
sendMessage(JSON.stringify({
|
|
2705
2721
|
action: 'subscribe',
|
|
2706
2722
|
address: address
|
|
2707
2723
|
}));
|
|
2708
|
-
|
|
2709
|
-
setData({
|
|
2710
|
-
tradeHistories: null,
|
|
2711
|
-
openPositions: null,
|
|
2712
|
-
openOrders: null,
|
|
2713
|
-
accountSummary: null,
|
|
2714
|
-
});
|
|
2724
|
+
setLastSubscribedAddress(address);
|
|
2715
2725
|
setLastError(null);
|
|
2716
2726
|
}
|
|
2717
|
-
else if (isConnected && !address) {
|
|
2718
|
-
//
|
|
2727
|
+
else if (isConnected && !address && lastSubscribedAddress) {
|
|
2728
|
+
// Send unsubscribe action when address is removed
|
|
2729
|
+
sendMessage(JSON.stringify({
|
|
2730
|
+
action: 'unsubscribe',
|
|
2731
|
+
address: lastSubscribedAddress
|
|
2732
|
+
}));
|
|
2733
|
+
setLastSubscribedAddress(null);
|
|
2734
|
+
}
|
|
2735
|
+
}, [isConnected, address, lastSubscribedAddress, sendMessage]);
|
|
2736
|
+
// Clear data when address changes
|
|
2737
|
+
useEffect(() => {
|
|
2738
|
+
if (address !== lastSubscribedAddress) {
|
|
2719
2739
|
setData({
|
|
2720
2740
|
tradeHistories: null,
|
|
2721
2741
|
openPositions: null,
|
|
2722
2742
|
openOrders: null,
|
|
2723
2743
|
accountSummary: null,
|
|
2724
2744
|
});
|
|
2745
|
+
setLastError(null);
|
|
2725
2746
|
}
|
|
2726
|
-
}, [
|
|
2747
|
+
}, [address, lastSubscribedAddress]);
|
|
2727
2748
|
return {
|
|
2728
2749
|
data,
|
|
2729
2750
|
connectionStatus: readyState,
|
|
@@ -2739,7 +2760,7 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2739
2760
|
const [subscribedAddress, setSubscribedAddress] = useState(null);
|
|
2740
2761
|
const pingIntervalRef = useRef(null);
|
|
2741
2762
|
// WebSocket connection to HyperLiquid native API
|
|
2742
|
-
const {
|
|
2763
|
+
const { readyState, sendJsonMessage } = useWebSocket('wss://api.hyperliquid.xyz/ws', {
|
|
2743
2764
|
shouldReconnect: () => true,
|
|
2744
2765
|
reconnectAttempts: 5,
|
|
2745
2766
|
reconnectInterval: 3000,
|
|
@@ -2747,6 +2768,41 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2747
2768
|
onClose: () => { },
|
|
2748
2769
|
onError: (event) => console.error('[HyperLiquid WS] Connection error:', event),
|
|
2749
2770
|
onReconnectStop: () => console.error('[HyperLiquid WS] Reconnection stopped after 5 attempts'),
|
|
2771
|
+
onMessage: (event) => {
|
|
2772
|
+
try {
|
|
2773
|
+
const message = JSON.parse(event.data);
|
|
2774
|
+
// Handle subscription responses
|
|
2775
|
+
if ('success' in message || 'error' in message) {
|
|
2776
|
+
if (message.error) {
|
|
2777
|
+
console.error('[HyperLiquid WS] Subscription error:', message.error);
|
|
2778
|
+
setLastError(message.error);
|
|
2779
|
+
}
|
|
2780
|
+
else {
|
|
2781
|
+
setLastError(null);
|
|
2782
|
+
}
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
// Handle channel data messages
|
|
2786
|
+
if ('channel' in message && 'data' in message) {
|
|
2787
|
+
const response = message;
|
|
2788
|
+
switch (response.channel) {
|
|
2789
|
+
case 'webData2':
|
|
2790
|
+
setWebData2(response.data);
|
|
2791
|
+
break;
|
|
2792
|
+
case 'allMids':
|
|
2793
|
+
setAllMids(response.data);
|
|
2794
|
+
break;
|
|
2795
|
+
default:
|
|
2796
|
+
console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
catch (error) {
|
|
2801
|
+
const errorMessage = `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`;
|
|
2802
|
+
console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
|
|
2803
|
+
setLastError(errorMessage);
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2750
2806
|
});
|
|
2751
2807
|
const isConnected = readyState === dist.ReadyState.OPEN;
|
|
2752
2808
|
// Setup ping mechanism
|
|
@@ -2812,44 +2868,6 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2812
2868
|
setWebData2(null);
|
|
2813
2869
|
}
|
|
2814
2870
|
}, [isConnected, address, subscribedAddress, sendJsonMessage]);
|
|
2815
|
-
// Handle incoming WebSocket messages
|
|
2816
|
-
useEffect(() => {
|
|
2817
|
-
if (!lastMessage)
|
|
2818
|
-
return;
|
|
2819
|
-
try {
|
|
2820
|
-
const message = JSON.parse(lastMessage.data);
|
|
2821
|
-
// Handle subscription responses
|
|
2822
|
-
if ('success' in message || 'error' in message) {
|
|
2823
|
-
if (message.error) {
|
|
2824
|
-
console.error('[HyperLiquid WS] Subscription error:', message.error);
|
|
2825
|
-
setLastError(message.error);
|
|
2826
|
-
}
|
|
2827
|
-
else {
|
|
2828
|
-
setLastError(null);
|
|
2829
|
-
}
|
|
2830
|
-
return;
|
|
2831
|
-
}
|
|
2832
|
-
// Handle channel data messages
|
|
2833
|
-
if ('channel' in message && 'data' in message) {
|
|
2834
|
-
const response = message;
|
|
2835
|
-
switch (response.channel) {
|
|
2836
|
-
case 'webData2':
|
|
2837
|
-
setWebData2(response.data);
|
|
2838
|
-
break;
|
|
2839
|
-
case 'allMids':
|
|
2840
|
-
setAllMids(response.data);
|
|
2841
|
-
break;
|
|
2842
|
-
default:
|
|
2843
|
-
console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
|
|
2844
|
-
}
|
|
2845
|
-
}
|
|
2846
|
-
}
|
|
2847
|
-
catch (error) {
|
|
2848
|
-
const errorMessage = `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`;
|
|
2849
|
-
console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', lastMessage.data);
|
|
2850
|
-
setLastError(errorMessage);
|
|
2851
|
-
}
|
|
2852
|
-
}, [lastMessage]);
|
|
2853
2871
|
return {
|
|
2854
2872
|
webData2,
|
|
2855
2873
|
allMids,
|
|
@@ -2859,6 +2877,23 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2859
2877
|
};
|
|
2860
2878
|
};
|
|
2861
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
|
+
};
|
|
2862
2897
|
const PearHyperliquidContext = createContext(undefined);
|
|
2863
2898
|
/**
|
|
2864
2899
|
* React Provider for PearHyperliquidClient
|
|
@@ -2868,6 +2903,8 @@ const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/
|
|
|
2868
2903
|
const migrationSDK = useMemo(() => new PearMigrationSDK(client), [client]);
|
|
2869
2904
|
// Address state
|
|
2870
2905
|
const [address, setAddress] = useState(null);
|
|
2906
|
+
// Token selection state
|
|
2907
|
+
const [tokenSelection, setTokenSelection] = useState(DEFAULT_TOKEN_SELECTION_STATE);
|
|
2871
2908
|
// WebSocket connection and data (Pear API)
|
|
2872
2909
|
const { data, connectionStatus, isConnected, lastError } = useHyperliquidWebSocket({
|
|
2873
2910
|
wsUrl,
|
|
@@ -2897,7 +2934,10 @@ const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/
|
|
|
2897
2934
|
// HyperLiquid native WebSocket data
|
|
2898
2935
|
webData2,
|
|
2899
2936
|
allMids,
|
|
2900
|
-
|
|
2937
|
+
// Token selection state
|
|
2938
|
+
tokenSelection,
|
|
2939
|
+
setTokenSelection,
|
|
2940
|
+
}), [client, migrationSDK, address, connectionStatus, isConnected, data, lastError, nativeConnectionStatus, nativeIsConnected, nativeLastError, webData2, allMids, tokenSelection, setTokenSelection]);
|
|
2901
2941
|
return (jsxRuntimeExports.jsx(PearHyperliquidContext.Provider, { value: contextValue, children: children }));
|
|
2902
2942
|
};
|
|
2903
2943
|
/**
|
|
@@ -3180,32 +3220,24 @@ class PositionProcessor {
|
|
|
3180
3220
|
};
|
|
3181
3221
|
}
|
|
3182
3222
|
calculateEntryRatio(syncResults) {
|
|
3223
|
+
var _a, _b;
|
|
3183
3224
|
const longResults = syncResults.filter(result => result.asset.side === PositionSide.LONG);
|
|
3184
3225
|
const shortResults = syncResults.filter(result => result.asset.side === PositionSide.SHORT);
|
|
3185
3226
|
if (longResults.length === 0 || shortResults.length === 0)
|
|
3186
3227
|
return 0;
|
|
3187
|
-
const
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
const shortValue = shortResults.reduce((sum, result) => {
|
|
3191
|
-
return sum + (Number(result.asset.size || 0) * Number(result.asset.entryPrice || 0));
|
|
3192
|
-
}, 0);
|
|
3193
|
-
return shortValue > 0 ? longValue / shortValue : 0;
|
|
3228
|
+
const longEntryPrice = ((_a = longResults[0]) === null || _a === void 0 ? void 0 : _a.asset.entryPrice) ? Number(longResults[0].asset.entryPrice) : 0;
|
|
3229
|
+
const shortEntryPrice = ((_b = shortResults[0]) === null || _b === void 0 ? void 0 : _b.asset.entryPrice) ? Number(shortResults[0].asset.entryPrice) : 0;
|
|
3230
|
+
return shortEntryPrice > 0 ? longEntryPrice / shortEntryPrice : 0;
|
|
3194
3231
|
}
|
|
3195
3232
|
calculateMarkRatio(syncResults) {
|
|
3233
|
+
var _a, _b;
|
|
3196
3234
|
const longResults = syncResults.filter(result => result.asset.side === PositionSide.LONG);
|
|
3197
3235
|
const shortResults = syncResults.filter(result => result.asset.side === PositionSide.SHORT);
|
|
3198
3236
|
if (longResults.length === 0 || shortResults.length === 0)
|
|
3199
3237
|
return 0;
|
|
3200
|
-
const
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
}, 0);
|
|
3204
|
-
const shortValue = shortResults.reduce((sum, result) => {
|
|
3205
|
-
const currentPrice = this.getMarketPrice(result.asset.coin);
|
|
3206
|
-
return sum + (result.actualSize * currentPrice);
|
|
3207
|
-
}, 0);
|
|
3208
|
-
return shortValue > 0 ? longValue / shortValue : 0;
|
|
3238
|
+
const longMarkPrice = ((_a = longResults[0]) === null || _a === void 0 ? void 0 : _a.asset.coin) ? this.getMarketPrice(longResults[0].asset.coin) : 0;
|
|
3239
|
+
const shortMarkPrice = ((_b = shortResults[0]) === null || _b === void 0 ? void 0 : _b.asset.coin) ? this.getMarketPrice(shortResults[0].asset.coin) : 0;
|
|
3240
|
+
return shortMarkPrice > 0 ? longMarkPrice / shortMarkPrice : 0;
|
|
3209
3241
|
}
|
|
3210
3242
|
calculateNetFundingFromSyncResults(syncResults) {
|
|
3211
3243
|
const netFunding = syncResults.reduce((sum, result) => {
|
|
@@ -3348,17 +3380,24 @@ const useCalculatedAccountSummary = (platformAccountSummary, platformOpenOrders,
|
|
|
3348
3380
|
};
|
|
3349
3381
|
|
|
3350
3382
|
/**
|
|
3351
|
-
* Hook to access trade histories
|
|
3383
|
+
* Hook to access trade histories with loading state
|
|
3352
3384
|
*/
|
|
3353
3385
|
const useTradeHistories = () => {
|
|
3354
3386
|
const context = useContext(PearHyperliquidContext);
|
|
3355
3387
|
if (!context) {
|
|
3356
3388
|
throw new Error('useTradeHistories must be used within a PearHyperliquidProvider');
|
|
3357
3389
|
}
|
|
3358
|
-
|
|
3390
|
+
const isLoading = useMemo(() => {
|
|
3391
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3392
|
+
return context.data.tradeHistories === null && context.isConnected;
|
|
3393
|
+
}, [context.data.tradeHistories, context.isConnected]);
|
|
3394
|
+
return {
|
|
3395
|
+
data: context.data.tradeHistories,
|
|
3396
|
+
isLoading
|
|
3397
|
+
};
|
|
3359
3398
|
};
|
|
3360
3399
|
/**
|
|
3361
|
-
* Hook to access open positions with real-time calculations
|
|
3400
|
+
* Hook to access open positions with real-time calculations and loading state
|
|
3362
3401
|
*/
|
|
3363
3402
|
const useOpenPositions = () => {
|
|
3364
3403
|
const context = useContext(PearHyperliquidContext);
|
|
@@ -3367,20 +3406,38 @@ const useOpenPositions = () => {
|
|
|
3367
3406
|
}
|
|
3368
3407
|
// Use calculated positions that sync platform data with HyperLiquid real-time data
|
|
3369
3408
|
const calculatedPositions = useCalculatedOpenPositions(context.data.openPositions, context.webData2, context.allMids);
|
|
3370
|
-
|
|
3409
|
+
const isLoading = useMemo(() => {
|
|
3410
|
+
// Loading is true initially and becomes false only when:
|
|
3411
|
+
// 1. WebSocket has open-positions data
|
|
3412
|
+
// 2. webData2 and allMids are ready (required for calculations)
|
|
3413
|
+
// 3. Connection is established
|
|
3414
|
+
return (context.isConnected &&
|
|
3415
|
+
(context.data.openPositions === null || !context.webData2 || !context.allMids));
|
|
3416
|
+
}, [context.data.openPositions, context.webData2, context.allMids, context.isConnected]);
|
|
3417
|
+
return {
|
|
3418
|
+
data: calculatedPositions,
|
|
3419
|
+
isLoading
|
|
3420
|
+
};
|
|
3371
3421
|
};
|
|
3372
3422
|
/**
|
|
3373
|
-
* Hook to access open orders
|
|
3423
|
+
* Hook to access open orders with loading state
|
|
3374
3424
|
*/
|
|
3375
3425
|
const useOpenOrders = () => {
|
|
3376
3426
|
const context = useContext(PearHyperliquidContext);
|
|
3377
3427
|
if (!context) {
|
|
3378
3428
|
throw new Error('useOpenOrders must be used within a PearHyperliquidProvider');
|
|
3379
3429
|
}
|
|
3380
|
-
|
|
3430
|
+
const isLoading = useMemo(() => {
|
|
3431
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3432
|
+
return context.data.openOrders === null && context.isConnected;
|
|
3433
|
+
}, [context.data.openOrders, context.isConnected]);
|
|
3434
|
+
return {
|
|
3435
|
+
data: context.data.openOrders,
|
|
3436
|
+
isLoading
|
|
3437
|
+
};
|
|
3381
3438
|
};
|
|
3382
3439
|
/**
|
|
3383
|
-
* Hook to access account summary with real-time calculations
|
|
3440
|
+
* Hook to access account summary with real-time calculations and loading state
|
|
3384
3441
|
*/
|
|
3385
3442
|
const useAccountSummary = () => {
|
|
3386
3443
|
var _a, _b, _c, _d;
|
|
@@ -3390,8 +3447,462 @@ const useAccountSummary = () => {
|
|
|
3390
3447
|
}
|
|
3391
3448
|
// Use calculated account summary that syncs platform data with HyperLiquid real-time data
|
|
3392
3449
|
const calculatedAccountSummary = useCalculatedAccountSummary(context.data.accountSummary, context.data.openOrders, context.webData2, (_b = (_a = context.data.accountSummary) === null || _a === void 0 ? void 0 : _a.agentWallet) === null || _b === void 0 ? void 0 : _b.address, (_d = (_c = context.data.accountSummary) === null || _c === void 0 ? void 0 : _c.agentWallet) === null || _d === void 0 ? void 0 : _d.status);
|
|
3393
|
-
|
|
3450
|
+
const isLoading = useMemo(() => {
|
|
3451
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3452
|
+
return context.data.accountSummary === null && context.isConnected;
|
|
3453
|
+
}, [context.data.accountSummary, context.isConnected]);
|
|
3454
|
+
return {
|
|
3455
|
+
data: calculatedAccountSummary,
|
|
3456
|
+
isLoading
|
|
3457
|
+
};
|
|
3458
|
+
};
|
|
3459
|
+
|
|
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
|
+
};
|
|
3394
3851
|
};
|
|
3395
3852
|
|
|
3396
|
-
|
|
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 };
|
|
3397
3908
|
//# sourceMappingURL=index.esm.js.map
|