@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.js
CHANGED
|
@@ -2642,21 +2642,17 @@ const useHyperliquidWebSocket = ({ wsUrl, address }) => {
|
|
|
2642
2642
|
accountSummary: null,
|
|
2643
2643
|
});
|
|
2644
2644
|
const [lastError, setLastError] = require$$0.useState(null);
|
|
2645
|
+
const [lastSubscribedAddress, setLastSubscribedAddress] = require$$0.useState(null);
|
|
2645
2646
|
// WebSocket connection
|
|
2646
|
-
const {
|
|
2647
|
-
{
|
|
2647
|
+
const { readyState, sendMessage } = useWebSocket(wsUrl, {
|
|
2648
2648
|
shouldReconnect: () => true,
|
|
2649
2649
|
reconnectAttempts: 5,
|
|
2650
2650
|
reconnectInterval: 3000,
|
|
2651
|
-
|
|
2652
|
-
const isConnected = readyState === dist.ReadyState.OPEN;
|
|
2653
|
-
// Handle incoming WebSocket messages
|
|
2654
|
-
require$$0.useEffect(() => {
|
|
2655
|
-
if (lastMessage !== null) {
|
|
2651
|
+
onMessage: (event) => {
|
|
2656
2652
|
try {
|
|
2657
|
-
const message = JSON.parse(
|
|
2658
|
-
// Handle subscription responses
|
|
2659
|
-
if ('success' in message || 'error' in message) {
|
|
2653
|
+
const message = JSON.parse(event.data);
|
|
2654
|
+
// Handle subscription responses (only if they don't have channel data)
|
|
2655
|
+
if (('success' in message || 'error' in message) && !('channel' in message)) {
|
|
2660
2656
|
if (message.error) {
|
|
2661
2657
|
setLastError(message.error);
|
|
2662
2658
|
}
|
|
@@ -2668,30 +2664,42 @@ const useHyperliquidWebSocket = ({ wsUrl, address }) => {
|
|
|
2668
2664
|
// Handle channel data messages
|
|
2669
2665
|
if ('channel' in message && 'data' in message) {
|
|
2670
2666
|
const dataMessage = message;
|
|
2667
|
+
// Validate data exists and is not null/undefined
|
|
2668
|
+
if (dataMessage.data === null || dataMessage.data === undefined) {
|
|
2669
|
+
return;
|
|
2670
|
+
}
|
|
2671
2671
|
switch (dataMessage.channel) {
|
|
2672
2672
|
case 'trade-histories':
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2673
|
+
if (Array.isArray(dataMessage.data)) {
|
|
2674
|
+
setData(prev => ({
|
|
2675
|
+
...prev,
|
|
2676
|
+
tradeHistories: dataMessage.data
|
|
2677
|
+
}));
|
|
2678
|
+
}
|
|
2677
2679
|
break;
|
|
2678
2680
|
case 'open-positions':
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2681
|
+
if (Array.isArray(dataMessage.data)) {
|
|
2682
|
+
setData(prev => ({
|
|
2683
|
+
...prev,
|
|
2684
|
+
openPositions: dataMessage.data
|
|
2685
|
+
}));
|
|
2686
|
+
}
|
|
2683
2687
|
break;
|
|
2684
2688
|
case 'open-orders':
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
+
if (Array.isArray(dataMessage.data)) {
|
|
2690
|
+
setData(prev => ({
|
|
2691
|
+
...prev,
|
|
2692
|
+
openOrders: dataMessage.data
|
|
2693
|
+
}));
|
|
2694
|
+
}
|
|
2689
2695
|
break;
|
|
2690
2696
|
case 'account-summary':
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2697
|
+
if (typeof dataMessage.data === 'object' && dataMessage.data !== null) {
|
|
2698
|
+
setData(prev => ({
|
|
2699
|
+
...prev,
|
|
2700
|
+
accountSummary: dataMessage.data
|
|
2701
|
+
}));
|
|
2702
|
+
}
|
|
2695
2703
|
break;
|
|
2696
2704
|
}
|
|
2697
2705
|
}
|
|
@@ -2700,34 +2708,47 @@ const useHyperliquidWebSocket = ({ wsUrl, address }) => {
|
|
|
2700
2708
|
setLastError(`Failed to parse message: ${error instanceof Error ? error.message : String(error)}`);
|
|
2701
2709
|
}
|
|
2702
2710
|
}
|
|
2703
|
-
}
|
|
2704
|
-
|
|
2711
|
+
});
|
|
2712
|
+
const isConnected = readyState === dist.ReadyState.OPEN;
|
|
2713
|
+
// Handle subscription management
|
|
2705
2714
|
require$$0.useEffect(() => {
|
|
2706
|
-
if (isConnected && address) {
|
|
2715
|
+
if (isConnected && address && address !== lastSubscribedAddress) {
|
|
2716
|
+
// Unsubscribe from previous address if exists
|
|
2717
|
+
if (lastSubscribedAddress) {
|
|
2718
|
+
sendMessage(JSON.stringify({
|
|
2719
|
+
action: 'unsubscribe',
|
|
2720
|
+
address: lastSubscribedAddress
|
|
2721
|
+
}));
|
|
2722
|
+
}
|
|
2707
2723
|
// Subscribe to new address
|
|
2708
2724
|
sendMessage(JSON.stringify({
|
|
2709
2725
|
action: 'subscribe',
|
|
2710
2726
|
address: address
|
|
2711
2727
|
}));
|
|
2712
|
-
|
|
2713
|
-
setData({
|
|
2714
|
-
tradeHistories: null,
|
|
2715
|
-
openPositions: null,
|
|
2716
|
-
openOrders: null,
|
|
2717
|
-
accountSummary: null,
|
|
2718
|
-
});
|
|
2728
|
+
setLastSubscribedAddress(address);
|
|
2719
2729
|
setLastError(null);
|
|
2720
2730
|
}
|
|
2721
|
-
else if (isConnected && !address) {
|
|
2722
|
-
//
|
|
2731
|
+
else if (isConnected && !address && lastSubscribedAddress) {
|
|
2732
|
+
// Send unsubscribe action when address is removed
|
|
2733
|
+
sendMessage(JSON.stringify({
|
|
2734
|
+
action: 'unsubscribe',
|
|
2735
|
+
address: lastSubscribedAddress
|
|
2736
|
+
}));
|
|
2737
|
+
setLastSubscribedAddress(null);
|
|
2738
|
+
}
|
|
2739
|
+
}, [isConnected, address, lastSubscribedAddress, sendMessage]);
|
|
2740
|
+
// Clear data when address changes
|
|
2741
|
+
require$$0.useEffect(() => {
|
|
2742
|
+
if (address !== lastSubscribedAddress) {
|
|
2723
2743
|
setData({
|
|
2724
2744
|
tradeHistories: null,
|
|
2725
2745
|
openPositions: null,
|
|
2726
2746
|
openOrders: null,
|
|
2727
2747
|
accountSummary: null,
|
|
2728
2748
|
});
|
|
2749
|
+
setLastError(null);
|
|
2729
2750
|
}
|
|
2730
|
-
}, [
|
|
2751
|
+
}, [address, lastSubscribedAddress]);
|
|
2731
2752
|
return {
|
|
2732
2753
|
data,
|
|
2733
2754
|
connectionStatus: readyState,
|
|
@@ -2743,7 +2764,7 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2743
2764
|
const [subscribedAddress, setSubscribedAddress] = require$$0.useState(null);
|
|
2744
2765
|
const pingIntervalRef = require$$0.useRef(null);
|
|
2745
2766
|
// WebSocket connection to HyperLiquid native API
|
|
2746
|
-
const {
|
|
2767
|
+
const { readyState, sendJsonMessage } = useWebSocket('wss://api.hyperliquid.xyz/ws', {
|
|
2747
2768
|
shouldReconnect: () => true,
|
|
2748
2769
|
reconnectAttempts: 5,
|
|
2749
2770
|
reconnectInterval: 3000,
|
|
@@ -2751,6 +2772,41 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2751
2772
|
onClose: () => { },
|
|
2752
2773
|
onError: (event) => console.error('[HyperLiquid WS] Connection error:', event),
|
|
2753
2774
|
onReconnectStop: () => console.error('[HyperLiquid WS] Reconnection stopped after 5 attempts'),
|
|
2775
|
+
onMessage: (event) => {
|
|
2776
|
+
try {
|
|
2777
|
+
const message = JSON.parse(event.data);
|
|
2778
|
+
// Handle subscription responses
|
|
2779
|
+
if ('success' in message || 'error' in message) {
|
|
2780
|
+
if (message.error) {
|
|
2781
|
+
console.error('[HyperLiquid WS] Subscription error:', message.error);
|
|
2782
|
+
setLastError(message.error);
|
|
2783
|
+
}
|
|
2784
|
+
else {
|
|
2785
|
+
setLastError(null);
|
|
2786
|
+
}
|
|
2787
|
+
return;
|
|
2788
|
+
}
|
|
2789
|
+
// Handle channel data messages
|
|
2790
|
+
if ('channel' in message && 'data' in message) {
|
|
2791
|
+
const response = message;
|
|
2792
|
+
switch (response.channel) {
|
|
2793
|
+
case 'webData2':
|
|
2794
|
+
setWebData2(response.data);
|
|
2795
|
+
break;
|
|
2796
|
+
case 'allMids':
|
|
2797
|
+
setAllMids(response.data);
|
|
2798
|
+
break;
|
|
2799
|
+
default:
|
|
2800
|
+
console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
catch (error) {
|
|
2805
|
+
const errorMessage = `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`;
|
|
2806
|
+
console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
|
|
2807
|
+
setLastError(errorMessage);
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2754
2810
|
});
|
|
2755
2811
|
const isConnected = readyState === dist.ReadyState.OPEN;
|
|
2756
2812
|
// Setup ping mechanism
|
|
@@ -2816,44 +2872,6 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2816
2872
|
setWebData2(null);
|
|
2817
2873
|
}
|
|
2818
2874
|
}, [isConnected, address, subscribedAddress, sendJsonMessage]);
|
|
2819
|
-
// Handle incoming WebSocket messages
|
|
2820
|
-
require$$0.useEffect(() => {
|
|
2821
|
-
if (!lastMessage)
|
|
2822
|
-
return;
|
|
2823
|
-
try {
|
|
2824
|
-
const message = JSON.parse(lastMessage.data);
|
|
2825
|
-
// Handle subscription responses
|
|
2826
|
-
if ('success' in message || 'error' in message) {
|
|
2827
|
-
if (message.error) {
|
|
2828
|
-
console.error('[HyperLiquid WS] Subscription error:', message.error);
|
|
2829
|
-
setLastError(message.error);
|
|
2830
|
-
}
|
|
2831
|
-
else {
|
|
2832
|
-
setLastError(null);
|
|
2833
|
-
}
|
|
2834
|
-
return;
|
|
2835
|
-
}
|
|
2836
|
-
// Handle channel data messages
|
|
2837
|
-
if ('channel' in message && 'data' in message) {
|
|
2838
|
-
const response = message;
|
|
2839
|
-
switch (response.channel) {
|
|
2840
|
-
case 'webData2':
|
|
2841
|
-
setWebData2(response.data);
|
|
2842
|
-
break;
|
|
2843
|
-
case 'allMids':
|
|
2844
|
-
setAllMids(response.data);
|
|
2845
|
-
break;
|
|
2846
|
-
default:
|
|
2847
|
-
console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
|
|
2848
|
-
}
|
|
2849
|
-
}
|
|
2850
|
-
}
|
|
2851
|
-
catch (error) {
|
|
2852
|
-
const errorMessage = `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`;
|
|
2853
|
-
console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', lastMessage.data);
|
|
2854
|
-
setLastError(errorMessage);
|
|
2855
|
-
}
|
|
2856
|
-
}, [lastMessage]);
|
|
2857
2875
|
return {
|
|
2858
2876
|
webData2,
|
|
2859
2877
|
allMids,
|
|
@@ -2863,6 +2881,23 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2863
2881
|
};
|
|
2864
2882
|
};
|
|
2865
2883
|
|
|
2884
|
+
const DEFAULT_TOKEN_SELECTION_STATE = {
|
|
2885
|
+
longTokens: [
|
|
2886
|
+
{ symbol: 'HYPE', weight: 25 },
|
|
2887
|
+
{ symbol: 'BTC', weight: 25 }
|
|
2888
|
+
],
|
|
2889
|
+
shortTokens: [
|
|
2890
|
+
{ symbol: 'AVAX', weight: 10 },
|
|
2891
|
+
{ symbol: 'SEI', weight: 10 },
|
|
2892
|
+
{ symbol: 'ADA', weight: 10 },
|
|
2893
|
+
{ symbol: 'TRUMP', weight: 10 },
|
|
2894
|
+
{ symbol: 'SUI', weight: 10 }
|
|
2895
|
+
],
|
|
2896
|
+
openTokenSelector: false,
|
|
2897
|
+
selectorConfig: null,
|
|
2898
|
+
openConflictModal: false,
|
|
2899
|
+
conflicts: [],
|
|
2900
|
+
};
|
|
2866
2901
|
const PearHyperliquidContext = require$$0.createContext(undefined);
|
|
2867
2902
|
/**
|
|
2868
2903
|
* React Provider for PearHyperliquidClient
|
|
@@ -2872,6 +2907,8 @@ const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/
|
|
|
2872
2907
|
const migrationSDK = require$$0.useMemo(() => new PearMigrationSDK(client), [client]);
|
|
2873
2908
|
// Address state
|
|
2874
2909
|
const [address, setAddress] = require$$0.useState(null);
|
|
2910
|
+
// Token selection state
|
|
2911
|
+
const [tokenSelection, setTokenSelection] = require$$0.useState(DEFAULT_TOKEN_SELECTION_STATE);
|
|
2875
2912
|
// WebSocket connection and data (Pear API)
|
|
2876
2913
|
const { data, connectionStatus, isConnected, lastError } = useHyperliquidWebSocket({
|
|
2877
2914
|
wsUrl,
|
|
@@ -2901,7 +2938,10 @@ const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/
|
|
|
2901
2938
|
// HyperLiquid native WebSocket data
|
|
2902
2939
|
webData2,
|
|
2903
2940
|
allMids,
|
|
2904
|
-
|
|
2941
|
+
// Token selection state
|
|
2942
|
+
tokenSelection,
|
|
2943
|
+
setTokenSelection,
|
|
2944
|
+
}), [client, migrationSDK, address, connectionStatus, isConnected, data, lastError, nativeConnectionStatus, nativeIsConnected, nativeLastError, webData2, allMids, tokenSelection, setTokenSelection]);
|
|
2905
2945
|
return (jsxRuntimeExports.jsx(PearHyperliquidContext.Provider, { value: contextValue, children: children }));
|
|
2906
2946
|
};
|
|
2907
2947
|
/**
|
|
@@ -3184,32 +3224,24 @@ class PositionProcessor {
|
|
|
3184
3224
|
};
|
|
3185
3225
|
}
|
|
3186
3226
|
calculateEntryRatio(syncResults) {
|
|
3227
|
+
var _a, _b;
|
|
3187
3228
|
const longResults = syncResults.filter(result => result.asset.side === PositionSide.LONG);
|
|
3188
3229
|
const shortResults = syncResults.filter(result => result.asset.side === PositionSide.SHORT);
|
|
3189
3230
|
if (longResults.length === 0 || shortResults.length === 0)
|
|
3190
3231
|
return 0;
|
|
3191
|
-
const
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
const shortValue = shortResults.reduce((sum, result) => {
|
|
3195
|
-
return sum + (Number(result.asset.size || 0) * Number(result.asset.entryPrice || 0));
|
|
3196
|
-
}, 0);
|
|
3197
|
-
return shortValue > 0 ? longValue / shortValue : 0;
|
|
3232
|
+
const longEntryPrice = ((_a = longResults[0]) === null || _a === void 0 ? void 0 : _a.asset.entryPrice) ? Number(longResults[0].asset.entryPrice) : 0;
|
|
3233
|
+
const shortEntryPrice = ((_b = shortResults[0]) === null || _b === void 0 ? void 0 : _b.asset.entryPrice) ? Number(shortResults[0].asset.entryPrice) : 0;
|
|
3234
|
+
return shortEntryPrice > 0 ? longEntryPrice / shortEntryPrice : 0;
|
|
3198
3235
|
}
|
|
3199
3236
|
calculateMarkRatio(syncResults) {
|
|
3237
|
+
var _a, _b;
|
|
3200
3238
|
const longResults = syncResults.filter(result => result.asset.side === PositionSide.LONG);
|
|
3201
3239
|
const shortResults = syncResults.filter(result => result.asset.side === PositionSide.SHORT);
|
|
3202
3240
|
if (longResults.length === 0 || shortResults.length === 0)
|
|
3203
3241
|
return 0;
|
|
3204
|
-
const
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
}, 0);
|
|
3208
|
-
const shortValue = shortResults.reduce((sum, result) => {
|
|
3209
|
-
const currentPrice = this.getMarketPrice(result.asset.coin);
|
|
3210
|
-
return sum + (result.actualSize * currentPrice);
|
|
3211
|
-
}, 0);
|
|
3212
|
-
return shortValue > 0 ? longValue / shortValue : 0;
|
|
3242
|
+
const longMarkPrice = ((_a = longResults[0]) === null || _a === void 0 ? void 0 : _a.asset.coin) ? this.getMarketPrice(longResults[0].asset.coin) : 0;
|
|
3243
|
+
const shortMarkPrice = ((_b = shortResults[0]) === null || _b === void 0 ? void 0 : _b.asset.coin) ? this.getMarketPrice(shortResults[0].asset.coin) : 0;
|
|
3244
|
+
return shortMarkPrice > 0 ? longMarkPrice / shortMarkPrice : 0;
|
|
3213
3245
|
}
|
|
3214
3246
|
calculateNetFundingFromSyncResults(syncResults) {
|
|
3215
3247
|
const netFunding = syncResults.reduce((sum, result) => {
|
|
@@ -3352,17 +3384,24 @@ const useCalculatedAccountSummary = (platformAccountSummary, platformOpenOrders,
|
|
|
3352
3384
|
};
|
|
3353
3385
|
|
|
3354
3386
|
/**
|
|
3355
|
-
* Hook to access trade histories
|
|
3387
|
+
* Hook to access trade histories with loading state
|
|
3356
3388
|
*/
|
|
3357
3389
|
const useTradeHistories = () => {
|
|
3358
3390
|
const context = require$$0.useContext(PearHyperliquidContext);
|
|
3359
3391
|
if (!context) {
|
|
3360
3392
|
throw new Error('useTradeHistories must be used within a PearHyperliquidProvider');
|
|
3361
3393
|
}
|
|
3362
|
-
|
|
3394
|
+
const isLoading = require$$0.useMemo(() => {
|
|
3395
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3396
|
+
return context.data.tradeHistories === null && context.isConnected;
|
|
3397
|
+
}, [context.data.tradeHistories, context.isConnected]);
|
|
3398
|
+
return {
|
|
3399
|
+
data: context.data.tradeHistories,
|
|
3400
|
+
isLoading
|
|
3401
|
+
};
|
|
3363
3402
|
};
|
|
3364
3403
|
/**
|
|
3365
|
-
* Hook to access open positions with real-time calculations
|
|
3404
|
+
* Hook to access open positions with real-time calculations and loading state
|
|
3366
3405
|
*/
|
|
3367
3406
|
const useOpenPositions = () => {
|
|
3368
3407
|
const context = require$$0.useContext(PearHyperliquidContext);
|
|
@@ -3371,20 +3410,38 @@ const useOpenPositions = () => {
|
|
|
3371
3410
|
}
|
|
3372
3411
|
// Use calculated positions that sync platform data with HyperLiquid real-time data
|
|
3373
3412
|
const calculatedPositions = useCalculatedOpenPositions(context.data.openPositions, context.webData2, context.allMids);
|
|
3374
|
-
|
|
3413
|
+
const isLoading = require$$0.useMemo(() => {
|
|
3414
|
+
// Loading is true initially and becomes false only when:
|
|
3415
|
+
// 1. WebSocket has open-positions data
|
|
3416
|
+
// 2. webData2 and allMids are ready (required for calculations)
|
|
3417
|
+
// 3. Connection is established
|
|
3418
|
+
return (context.isConnected &&
|
|
3419
|
+
(context.data.openPositions === null || !context.webData2 || !context.allMids));
|
|
3420
|
+
}, [context.data.openPositions, context.webData2, context.allMids, context.isConnected]);
|
|
3421
|
+
return {
|
|
3422
|
+
data: calculatedPositions,
|
|
3423
|
+
isLoading
|
|
3424
|
+
};
|
|
3375
3425
|
};
|
|
3376
3426
|
/**
|
|
3377
|
-
* Hook to access open orders
|
|
3427
|
+
* Hook to access open orders with loading state
|
|
3378
3428
|
*/
|
|
3379
3429
|
const useOpenOrders = () => {
|
|
3380
3430
|
const context = require$$0.useContext(PearHyperliquidContext);
|
|
3381
3431
|
if (!context) {
|
|
3382
3432
|
throw new Error('useOpenOrders must be used within a PearHyperliquidProvider');
|
|
3383
3433
|
}
|
|
3384
|
-
|
|
3434
|
+
const isLoading = require$$0.useMemo(() => {
|
|
3435
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3436
|
+
return context.data.openOrders === null && context.isConnected;
|
|
3437
|
+
}, [context.data.openOrders, context.isConnected]);
|
|
3438
|
+
return {
|
|
3439
|
+
data: context.data.openOrders,
|
|
3440
|
+
isLoading
|
|
3441
|
+
};
|
|
3385
3442
|
};
|
|
3386
3443
|
/**
|
|
3387
|
-
* Hook to access account summary with real-time calculations
|
|
3444
|
+
* Hook to access account summary with real-time calculations and loading state
|
|
3388
3445
|
*/
|
|
3389
3446
|
const useAccountSummary = () => {
|
|
3390
3447
|
var _a, _b, _c, _d;
|
|
@@ -3394,13 +3451,469 @@ const useAccountSummary = () => {
|
|
|
3394
3451
|
}
|
|
3395
3452
|
// Use calculated account summary that syncs platform data with HyperLiquid real-time data
|
|
3396
3453
|
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);
|
|
3397
|
-
|
|
3454
|
+
const isLoading = require$$0.useMemo(() => {
|
|
3455
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3456
|
+
return context.data.accountSummary === null && context.isConnected;
|
|
3457
|
+
}, [context.data.accountSummary, context.isConnected]);
|
|
3458
|
+
return {
|
|
3459
|
+
data: calculatedAccountSummary,
|
|
3460
|
+
isLoading
|
|
3461
|
+
};
|
|
3462
|
+
};
|
|
3463
|
+
|
|
3464
|
+
/**
|
|
3465
|
+
* Extracts token metadata from WebData2 and AllMids data
|
|
3466
|
+
*/
|
|
3467
|
+
class TokenMetadataExtractor {
|
|
3468
|
+
/**
|
|
3469
|
+
* Extracts comprehensive token metadata
|
|
3470
|
+
* @param symbol - Token symbol
|
|
3471
|
+
* @param webData2 - WebData2 response containing asset context and universe data
|
|
3472
|
+
* @param allMids - AllMids data containing current prices
|
|
3473
|
+
* @returns TokenMetadata or null if token not found
|
|
3474
|
+
*/
|
|
3475
|
+
static extractTokenMetadata(symbol, webData2, allMids) {
|
|
3476
|
+
if (!webData2 || !allMids) {
|
|
3477
|
+
return null;
|
|
3478
|
+
}
|
|
3479
|
+
// Find token index in universe
|
|
3480
|
+
const universeIndex = webData2.meta.universe.findIndex(asset => asset.name === symbol);
|
|
3481
|
+
if (universeIndex === -1) {
|
|
3482
|
+
return null;
|
|
3483
|
+
}
|
|
3484
|
+
const universeAsset = webData2.meta.universe[universeIndex];
|
|
3485
|
+
const assetCtx = webData2.assetCtxs[universeIndex];
|
|
3486
|
+
if (!assetCtx) {
|
|
3487
|
+
return null;
|
|
3488
|
+
}
|
|
3489
|
+
// Get current price from allMids
|
|
3490
|
+
const currentPriceStr = allMids.mids[symbol];
|
|
3491
|
+
const currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
|
|
3492
|
+
// Get previous day price
|
|
3493
|
+
const prevDayPrice = parseFloat(assetCtx.prevDayPx);
|
|
3494
|
+
// Calculate 24h price change
|
|
3495
|
+
const priceChange24h = currentPrice - prevDayPrice;
|
|
3496
|
+
const priceChange24hPercent = prevDayPrice !== 0 ? (priceChange24h / prevDayPrice) * 100 : 0;
|
|
3497
|
+
// Parse other metadata
|
|
3498
|
+
const netFunding = parseFloat(assetCtx.funding);
|
|
3499
|
+
const markPrice = parseFloat(assetCtx.markPx);
|
|
3500
|
+
const oraclePrice = parseFloat(assetCtx.oraclePx);
|
|
3501
|
+
return {
|
|
3502
|
+
currentPrice,
|
|
3503
|
+
prevDayPrice,
|
|
3504
|
+
priceChange24h,
|
|
3505
|
+
priceChange24hPercent,
|
|
3506
|
+
netFunding,
|
|
3507
|
+
maxLeverage: universeAsset.maxLeverage,
|
|
3508
|
+
markPrice,
|
|
3509
|
+
oraclePrice,
|
|
3510
|
+
openInterest: assetCtx.openInterest,
|
|
3511
|
+
dayVolume: assetCtx.dayNtlVlm,
|
|
3512
|
+
};
|
|
3513
|
+
}
|
|
3514
|
+
/**
|
|
3515
|
+
* Extracts metadata for multiple tokens
|
|
3516
|
+
* @param symbols - Array of token symbols
|
|
3517
|
+
* @param webData2 - WebData2 response
|
|
3518
|
+
* @param allMids - AllMids data
|
|
3519
|
+
* @returns Record of symbol to TokenMetadata
|
|
3520
|
+
*/
|
|
3521
|
+
static extractMultipleTokensMetadata(symbols, webData2, allMids) {
|
|
3522
|
+
const result = {};
|
|
3523
|
+
for (const symbol of symbols) {
|
|
3524
|
+
result[symbol] = this.extractTokenMetadata(symbol, webData2, allMids);
|
|
3525
|
+
}
|
|
3526
|
+
return result;
|
|
3527
|
+
}
|
|
3528
|
+
/**
|
|
3529
|
+
* Checks if token data is available in WebData2
|
|
3530
|
+
* @param symbol - Token symbol
|
|
3531
|
+
* @param webData2 - WebData2 response
|
|
3532
|
+
* @returns boolean indicating if token exists in universe
|
|
3533
|
+
*/
|
|
3534
|
+
static isTokenAvailable(symbol, webData2) {
|
|
3535
|
+
if (!webData2)
|
|
3536
|
+
return false;
|
|
3537
|
+
return webData2.meta.universe.some(asset => asset.name === symbol);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
/**
|
|
3542
|
+
* Hook to access token selection state using provider's WebSocket data
|
|
3543
|
+
*/
|
|
3544
|
+
const useTokenSelection = () => {
|
|
3545
|
+
var _a;
|
|
3546
|
+
const context = require$$0.useContext(PearHyperliquidContext);
|
|
3547
|
+
if (!context) {
|
|
3548
|
+
throw new Error('useTokenSelection must be used within PearHyperliquidProvider');
|
|
3549
|
+
}
|
|
3550
|
+
const { webData2, allMids, tokenSelection, setTokenSelection } = context;
|
|
3551
|
+
// Loading states
|
|
3552
|
+
const isPriceDataReady = require$$0.useMemo(() => {
|
|
3553
|
+
return !!(webData2 && allMids);
|
|
3554
|
+
}, [webData2, allMids]);
|
|
3555
|
+
const isLoading = require$$0.useMemo(() => {
|
|
3556
|
+
// Check if any tokens have missing metadata when price data is available
|
|
3557
|
+
if (!isPriceDataReady)
|
|
3558
|
+
return true;
|
|
3559
|
+
const allTokens = [...tokenSelection.longTokens, ...tokenSelection.shortTokens];
|
|
3560
|
+
if (allTokens.length === 0)
|
|
3561
|
+
return false;
|
|
3562
|
+
// Loading if any token is missing metadata
|
|
3563
|
+
return allTokens.some(token => !token.metadata);
|
|
3564
|
+
}, [isPriceDataReady, tokenSelection.longTokens, tokenSelection.shortTokens]);
|
|
3565
|
+
// Auto-update token metadata when WebSocket data changes
|
|
3566
|
+
require$$0.useEffect(() => {
|
|
3567
|
+
if (!webData2 || !allMids)
|
|
3568
|
+
return;
|
|
3569
|
+
const allTokenSymbols = [...tokenSelection.longTokens, ...tokenSelection.shortTokens].map(t => t.symbol);
|
|
3570
|
+
if (allTokenSymbols.length === 0)
|
|
3571
|
+
return;
|
|
3572
|
+
const metadataMap = TokenMetadataExtractor.extractMultipleTokensMetadata(allTokenSymbols, webData2, allMids);
|
|
3573
|
+
setTokenSelection(prev => ({
|
|
3574
|
+
...prev,
|
|
3575
|
+
longTokens: prev.longTokens.map(token => ({
|
|
3576
|
+
...token,
|
|
3577
|
+
metadata: metadataMap[token.symbol] || undefined
|
|
3578
|
+
})),
|
|
3579
|
+
shortTokens: prev.shortTokens.map(token => ({
|
|
3580
|
+
...token,
|
|
3581
|
+
metadata: metadataMap[token.symbol] || undefined
|
|
3582
|
+
}))
|
|
3583
|
+
}));
|
|
3584
|
+
}, [webData2, allMids, setTokenSelection]);
|
|
3585
|
+
// Calculate weighted ratio: LONG^WEIGHT * SHORT^-WEIGHT
|
|
3586
|
+
const weightedRatio = require$$0.useMemo(() => {
|
|
3587
|
+
let longProduct = 1;
|
|
3588
|
+
let shortProduct = 1;
|
|
3589
|
+
// Calculate long side product: PRICE^(WEIGHT/100)
|
|
3590
|
+
tokenSelection.longTokens.forEach(token => {
|
|
3591
|
+
var _a;
|
|
3592
|
+
if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.currentPrice) && token.weight > 0) {
|
|
3593
|
+
const weightFactor = token.weight / 100;
|
|
3594
|
+
longProduct *= Math.pow(token.metadata.currentPrice, weightFactor);
|
|
3595
|
+
}
|
|
3596
|
+
});
|
|
3597
|
+
// Calculate short side product: PRICE^-(WEIGHT/100)
|
|
3598
|
+
tokenSelection.shortTokens.forEach(token => {
|
|
3599
|
+
var _a;
|
|
3600
|
+
if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.currentPrice) && token.weight > 0) {
|
|
3601
|
+
const weightFactor = token.weight / 100;
|
|
3602
|
+
shortProduct *= Math.pow(token.metadata.currentPrice, -weightFactor);
|
|
3603
|
+
}
|
|
3604
|
+
});
|
|
3605
|
+
return longProduct * shortProduct;
|
|
3606
|
+
}, [tokenSelection.longTokens, tokenSelection.shortTokens]);
|
|
3607
|
+
// Calculate 24h weighted ratio using previous day prices
|
|
3608
|
+
const weightedRatio24h = require$$0.useMemo(() => {
|
|
3609
|
+
let longProduct = 1;
|
|
3610
|
+
let shortProduct = 1;
|
|
3611
|
+
// Calculate long side product: PREV_PRICE^(WEIGHT/100)
|
|
3612
|
+
tokenSelection.longTokens.forEach(token => {
|
|
3613
|
+
var _a;
|
|
3614
|
+
if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.prevDayPrice) && token.weight > 0) {
|
|
3615
|
+
const weightFactor = token.weight / 100;
|
|
3616
|
+
longProduct *= Math.pow(token.metadata.prevDayPrice, weightFactor);
|
|
3617
|
+
}
|
|
3618
|
+
});
|
|
3619
|
+
// Calculate short side product: PREV_PRICE^-(WEIGHT/100)
|
|
3620
|
+
tokenSelection.shortTokens.forEach(token => {
|
|
3621
|
+
var _a;
|
|
3622
|
+
if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.prevDayPrice) && token.weight > 0) {
|
|
3623
|
+
const weightFactor = token.weight / 100;
|
|
3624
|
+
shortProduct *= Math.pow(token.metadata.prevDayPrice, -weightFactor);
|
|
3625
|
+
}
|
|
3626
|
+
});
|
|
3627
|
+
return longProduct * shortProduct;
|
|
3628
|
+
}, [tokenSelection.longTokens, tokenSelection.shortTokens]);
|
|
3629
|
+
// Calculate sum of weighted net funding
|
|
3630
|
+
const sumNetFunding = require$$0.useMemo(() => {
|
|
3631
|
+
let totalFunding = 0;
|
|
3632
|
+
// Add long funding weighted by allocation
|
|
3633
|
+
tokenSelection.longTokens.forEach(token => {
|
|
3634
|
+
var _a;
|
|
3635
|
+
if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.netFunding) && token.weight > 0) {
|
|
3636
|
+
const weightFactor = token.weight / 100;
|
|
3637
|
+
totalFunding += token.metadata.netFunding * weightFactor;
|
|
3638
|
+
}
|
|
3639
|
+
});
|
|
3640
|
+
// Subtract short funding weighted by allocation (since we're short)
|
|
3641
|
+
tokenSelection.shortTokens.forEach(token => {
|
|
3642
|
+
var _a;
|
|
3643
|
+
if (((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.netFunding) && token.weight > 0) {
|
|
3644
|
+
const weightFactor = token.weight / 100;
|
|
3645
|
+
totalFunding -= token.metadata.netFunding * weightFactor;
|
|
3646
|
+
}
|
|
3647
|
+
});
|
|
3648
|
+
return totalFunding;
|
|
3649
|
+
}, [tokenSelection.longTokens, tokenSelection.shortTokens]);
|
|
3650
|
+
// Calculate max leverage (minimum of all token maxLeverages)
|
|
3651
|
+
const maxLeverage = require$$0.useMemo(() => {
|
|
3652
|
+
var _a;
|
|
3653
|
+
if (!((_a = webData2 === null || webData2 === void 0 ? void 0 : webData2.meta) === null || _a === void 0 ? void 0 : _a.universe))
|
|
3654
|
+
return 0;
|
|
3655
|
+
const allTokenSymbols = [...tokenSelection.longTokens, ...tokenSelection.shortTokens].map(t => t.symbol);
|
|
3656
|
+
if (allTokenSymbols.length === 0)
|
|
3657
|
+
return 0;
|
|
3658
|
+
let minLeverage = Infinity;
|
|
3659
|
+
allTokenSymbols.forEach(symbol => {
|
|
3660
|
+
const tokenUniverse = webData2.meta.universe.find(u => u.name === symbol);
|
|
3661
|
+
if (tokenUniverse === null || tokenUniverse === void 0 ? void 0 : tokenUniverse.maxLeverage) {
|
|
3662
|
+
minLeverage = Math.min(minLeverage, tokenUniverse.maxLeverage);
|
|
3663
|
+
}
|
|
3664
|
+
});
|
|
3665
|
+
return minLeverage === Infinity ? 0 : minLeverage;
|
|
3666
|
+
}, [(_a = webData2 === null || webData2 === void 0 ? void 0 : webData2.meta) === null || _a === void 0 ? void 0 : _a.universe, tokenSelection.longTokens, tokenSelection.shortTokens]);
|
|
3667
|
+
// Calculate minimum margin (10 * total number of tokens)
|
|
3668
|
+
const minMargin = require$$0.useMemo(() => {
|
|
3669
|
+
const totalTokenCount = tokenSelection.longTokens.length + tokenSelection.shortTokens.length;
|
|
3670
|
+
return 10 * totalTokenCount;
|
|
3671
|
+
}, [tokenSelection.longTokens.length, tokenSelection.shortTokens.length]);
|
|
3672
|
+
// Actions
|
|
3673
|
+
const setLongTokens = require$$0.useCallback((tokens) => {
|
|
3674
|
+
setTokenSelection(prev => ({ ...prev, longTokens: tokens }));
|
|
3675
|
+
}, [setTokenSelection]);
|
|
3676
|
+
const setShortTokens = require$$0.useCallback((tokens) => {
|
|
3677
|
+
setTokenSelection(prev => ({ ...prev, shortTokens: tokens }));
|
|
3678
|
+
}, [setTokenSelection]);
|
|
3679
|
+
const updateTokenWeight = require$$0.useCallback((isLong, index, newWeight) => {
|
|
3680
|
+
const clampedWeight = Math.max(1, Math.min(100, newWeight));
|
|
3681
|
+
setTokenSelection(prev => {
|
|
3682
|
+
var _a, _b;
|
|
3683
|
+
const currentLongTotal = prev.longTokens.reduce((sum, token) => sum + token.weight, 0);
|
|
3684
|
+
const currentShortTotal = prev.shortTokens.reduce((sum, token) => sum + token.weight, 0);
|
|
3685
|
+
if (isLong) {
|
|
3686
|
+
const oldWeight = ((_a = prev.longTokens[index]) === null || _a === void 0 ? void 0 : _a.weight) || 0;
|
|
3687
|
+
const weightDiff = clampedWeight - oldWeight;
|
|
3688
|
+
const newLongTotal = currentLongTotal + weightDiff;
|
|
3689
|
+
if (newLongTotal + currentShortTotal > 100) {
|
|
3690
|
+
const maxAllowedWeight = Math.max(1, 100 - currentShortTotal - (currentLongTotal - oldWeight));
|
|
3691
|
+
const updated = [...prev.longTokens];
|
|
3692
|
+
updated[index] = { ...updated[index], weight: maxAllowedWeight };
|
|
3693
|
+
return { ...prev, longTokens: updated };
|
|
3694
|
+
}
|
|
3695
|
+
else {
|
|
3696
|
+
const updated = [...prev.longTokens];
|
|
3697
|
+
updated[index] = { ...updated[index], weight: clampedWeight };
|
|
3698
|
+
return { ...prev, longTokens: updated };
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
else {
|
|
3702
|
+
const oldWeight = ((_b = prev.shortTokens[index]) === null || _b === void 0 ? void 0 : _b.weight) || 0;
|
|
3703
|
+
const weightDiff = clampedWeight - oldWeight;
|
|
3704
|
+
const newShortTotal = currentShortTotal + weightDiff;
|
|
3705
|
+
if (currentLongTotal + newShortTotal > 100) {
|
|
3706
|
+
const maxAllowedWeight = Math.max(1, 100 - currentLongTotal - (currentShortTotal - oldWeight));
|
|
3707
|
+
const updated = [...prev.shortTokens];
|
|
3708
|
+
updated[index] = { ...updated[index], weight: maxAllowedWeight };
|
|
3709
|
+
return { ...prev, shortTokens: updated };
|
|
3710
|
+
}
|
|
3711
|
+
else {
|
|
3712
|
+
const updated = [...prev.shortTokens];
|
|
3713
|
+
updated[index] = { ...updated[index], weight: clampedWeight };
|
|
3714
|
+
return { ...prev, shortTokens: updated };
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
});
|
|
3718
|
+
}, [setTokenSelection]);
|
|
3719
|
+
const addToken = require$$0.useCallback((isLong) => {
|
|
3720
|
+
const currentTokens = isLong ? tokenSelection.longTokens : tokenSelection.shortTokens;
|
|
3721
|
+
const newIndex = currentTokens.length;
|
|
3722
|
+
setTokenSelection(prev => ({
|
|
3723
|
+
...prev,
|
|
3724
|
+
selectorConfig: { isLong, index: newIndex },
|
|
3725
|
+
openTokenSelector: true,
|
|
3726
|
+
}));
|
|
3727
|
+
}, [tokenSelection.longTokens, tokenSelection.shortTokens, setTokenSelection]);
|
|
3728
|
+
const removeToken = require$$0.useCallback((isLong, index) => {
|
|
3729
|
+
setTokenSelection(prev => {
|
|
3730
|
+
if (isLong) {
|
|
3731
|
+
const updated = prev.longTokens.filter((_, i) => i !== index);
|
|
3732
|
+
return { ...prev, longTokens: updated };
|
|
3733
|
+
}
|
|
3734
|
+
else {
|
|
3735
|
+
const updated = prev.shortTokens.filter((_, i) => i !== index);
|
|
3736
|
+
return { ...prev, shortTokens: updated };
|
|
3737
|
+
}
|
|
3738
|
+
});
|
|
3739
|
+
}, [setTokenSelection]);
|
|
3740
|
+
const setOpenTokenSelector = require$$0.useCallback((open) => {
|
|
3741
|
+
setTokenSelection(prev => ({ ...prev, openTokenSelector: open }));
|
|
3742
|
+
}, [setTokenSelection]);
|
|
3743
|
+
const setSelectorConfig = require$$0.useCallback((config) => {
|
|
3744
|
+
setTokenSelection(prev => ({ ...prev, selectorConfig: config }));
|
|
3745
|
+
}, [setTokenSelection]);
|
|
3746
|
+
const setOpenConflictModal = require$$0.useCallback((open) => {
|
|
3747
|
+
setTokenSelection(prev => ({ ...prev, openConflictModal: open }));
|
|
3748
|
+
}, [setTokenSelection]);
|
|
3749
|
+
const setConflicts = require$$0.useCallback((conflicts) => {
|
|
3750
|
+
setTokenSelection(prev => ({ ...prev, conflicts }));
|
|
3751
|
+
}, [setTokenSelection]);
|
|
3752
|
+
const resetToDefaults = require$$0.useCallback(() => {
|
|
3753
|
+
setTokenSelection(prev => ({
|
|
3754
|
+
...prev,
|
|
3755
|
+
longTokens: [
|
|
3756
|
+
{ symbol: 'HYPE', weight: 25 },
|
|
3757
|
+
{ symbol: 'BTC', weight: 25 }
|
|
3758
|
+
],
|
|
3759
|
+
shortTokens: [
|
|
3760
|
+
{ symbol: 'AVAX', weight: 10 },
|
|
3761
|
+
{ symbol: 'SEI', weight: 10 },
|
|
3762
|
+
{ symbol: 'ADA', weight: 10 },
|
|
3763
|
+
{ symbol: 'TRUMP', weight: 10 },
|
|
3764
|
+
{ symbol: 'SUI', weight: 10 }
|
|
3765
|
+
],
|
|
3766
|
+
openTokenSelector: false,
|
|
3767
|
+
selectorConfig: null,
|
|
3768
|
+
openConflictModal: false,
|
|
3769
|
+
}));
|
|
3770
|
+
}, [setTokenSelection]);
|
|
3771
|
+
const handleTokenSelect = require$$0.useCallback((selectedToken) => {
|
|
3772
|
+
if (!tokenSelection.selectorConfig)
|
|
3773
|
+
return;
|
|
3774
|
+
const { isLong, index } = tokenSelection.selectorConfig;
|
|
3775
|
+
const existingTokens = isLong ? tokenSelection.longTokens : tokenSelection.shortTokens;
|
|
3776
|
+
// Check if token already exists
|
|
3777
|
+
if (existingTokens.some(t => t.symbol === selectedToken))
|
|
3778
|
+
return;
|
|
3779
|
+
// Get metadata for new token
|
|
3780
|
+
const metadata = TokenMetadataExtractor.extractTokenMetadata(selectedToken, webData2, allMids);
|
|
3781
|
+
setTokenSelection(prev => {
|
|
3782
|
+
if (index >= existingTokens.length) {
|
|
3783
|
+
// Adding new token - calculate safe weight
|
|
3784
|
+
const currentLongTotal = prev.longTokens.reduce((sum, token) => sum + token.weight, 0);
|
|
3785
|
+
const currentShortTotal = prev.shortTokens.reduce((sum, token) => sum + token.weight, 0);
|
|
3786
|
+
const currentTotal = currentLongTotal + currentShortTotal;
|
|
3787
|
+
const maxAvailableWeight = Math.max(1, 100 - currentTotal);
|
|
3788
|
+
const safeWeight = Math.min(20, maxAvailableWeight);
|
|
3789
|
+
const newToken = {
|
|
3790
|
+
symbol: selectedToken,
|
|
3791
|
+
weight: safeWeight,
|
|
3792
|
+
metadata: metadata || undefined
|
|
3793
|
+
};
|
|
3794
|
+
if (isLong) {
|
|
3795
|
+
return { ...prev, longTokens: [...prev.longTokens, newToken] };
|
|
3796
|
+
}
|
|
3797
|
+
else {
|
|
3798
|
+
return { ...prev, shortTokens: [...prev.shortTokens, newToken] };
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
else {
|
|
3802
|
+
// Replacing existing token
|
|
3803
|
+
if (isLong) {
|
|
3804
|
+
const updated = [...prev.longTokens];
|
|
3805
|
+
updated[index] = {
|
|
3806
|
+
...updated[index],
|
|
3807
|
+
symbol: selectedToken,
|
|
3808
|
+
metadata: metadata || undefined
|
|
3809
|
+
};
|
|
3810
|
+
return { ...prev, longTokens: updated };
|
|
3811
|
+
}
|
|
3812
|
+
else {
|
|
3813
|
+
const updated = [...prev.shortTokens];
|
|
3814
|
+
updated[index] = {
|
|
3815
|
+
...updated[index],
|
|
3816
|
+
symbol: selectedToken,
|
|
3817
|
+
metadata: metadata || undefined
|
|
3818
|
+
};
|
|
3819
|
+
return { ...prev, shortTokens: updated };
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
});
|
|
3823
|
+
}, [tokenSelection.selectorConfig, tokenSelection.longTokens, tokenSelection.shortTokens, webData2, allMids, setTokenSelection]);
|
|
3824
|
+
// updateTokenMetadata is now handled automatically by the useEffect above
|
|
3825
|
+
return {
|
|
3826
|
+
// State
|
|
3827
|
+
longTokens: tokenSelection.longTokens,
|
|
3828
|
+
shortTokens: tokenSelection.shortTokens,
|
|
3829
|
+
openTokenSelector: tokenSelection.openTokenSelector,
|
|
3830
|
+
selectorConfig: tokenSelection.selectorConfig,
|
|
3831
|
+
openConflictModal: tokenSelection.openConflictModal,
|
|
3832
|
+
conflicts: tokenSelection.conflicts,
|
|
3833
|
+
// Loading states
|
|
3834
|
+
isLoading,
|
|
3835
|
+
isPriceDataReady,
|
|
3836
|
+
// Calculated values
|
|
3837
|
+
weightedRatio,
|
|
3838
|
+
weightedRatio24h,
|
|
3839
|
+
sumNetFunding,
|
|
3840
|
+
maxLeverage,
|
|
3841
|
+
minMargin,
|
|
3842
|
+
// Actions
|
|
3843
|
+
setLongTokens,
|
|
3844
|
+
setShortTokens,
|
|
3845
|
+
updateTokenWeight,
|
|
3846
|
+
addToken,
|
|
3847
|
+
removeToken,
|
|
3848
|
+
handleTokenSelect,
|
|
3849
|
+
setOpenTokenSelector,
|
|
3850
|
+
setSelectorConfig,
|
|
3851
|
+
setOpenConflictModal,
|
|
3852
|
+
setConflicts,
|
|
3853
|
+
resetToDefaults,
|
|
3854
|
+
};
|
|
3398
3855
|
};
|
|
3399
3856
|
|
|
3857
|
+
/**
|
|
3858
|
+
* Detects conflicts between selected tokens and existing positions
|
|
3859
|
+
*/
|
|
3860
|
+
class ConflictDetector {
|
|
3861
|
+
/**
|
|
3862
|
+
* Detects conflicts between token selections and open positions
|
|
3863
|
+
* @param longTokens - Selected long tokens
|
|
3864
|
+
* @param shortTokens - Selected short tokens
|
|
3865
|
+
* @param openPositions - Current open positions from API
|
|
3866
|
+
* @returns Array of detected conflicts
|
|
3867
|
+
*/
|
|
3868
|
+
static detectConflicts(longTokens, shortTokens, openPositions) {
|
|
3869
|
+
const detectedConflicts = [];
|
|
3870
|
+
if (!openPositions) {
|
|
3871
|
+
return detectedConflicts;
|
|
3872
|
+
}
|
|
3873
|
+
// Check long tokens against existing short positions
|
|
3874
|
+
longTokens.forEach((token) => {
|
|
3875
|
+
const existingShortPosition = openPositions.find((pos) => {
|
|
3876
|
+
var _a;
|
|
3877
|
+
// Check multiple possible property names and side values
|
|
3878
|
+
const symbol = pos.coin || pos.symbol || pos.asset;
|
|
3879
|
+
const side = (_a = pos.side) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
3880
|
+
return symbol === token.symbol && (side === 'short' || side === 'sell');
|
|
3881
|
+
});
|
|
3882
|
+
if (existingShortPosition) {
|
|
3883
|
+
detectedConflicts.push({
|
|
3884
|
+
symbol: token.symbol,
|
|
3885
|
+
conflictType: 'short',
|
|
3886
|
+
conflictMessage: `${token.symbol} Long conflicts with existing ${token.symbol} Short position`
|
|
3887
|
+
});
|
|
3888
|
+
}
|
|
3889
|
+
});
|
|
3890
|
+
// Check short tokens against existing long positions
|
|
3891
|
+
shortTokens.forEach((token) => {
|
|
3892
|
+
const existingLongPosition = openPositions.find((pos) => {
|
|
3893
|
+
var _a;
|
|
3894
|
+
// Check multiple possible property names and side values
|
|
3895
|
+
const symbol = pos.coin || pos.symbol || pos.asset;
|
|
3896
|
+
const side = (_a = pos.side) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
3897
|
+
return symbol === token.symbol && (side === 'long' || side === 'buy');
|
|
3898
|
+
});
|
|
3899
|
+
if (existingLongPosition) {
|
|
3900
|
+
detectedConflicts.push({
|
|
3901
|
+
symbol: token.symbol,
|
|
3902
|
+
conflictType: 'long',
|
|
3903
|
+
conflictMessage: `${token.symbol} Short conflicts with existing ${token.symbol} Long position`
|
|
3904
|
+
});
|
|
3905
|
+
}
|
|
3906
|
+
});
|
|
3907
|
+
return detectedConflicts;
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
|
|
3400
3911
|
exports.AccountSummaryCalculator = AccountSummaryCalculator;
|
|
3912
|
+
exports.ConflictDetector = ConflictDetector;
|
|
3401
3913
|
exports.PearHyperliquidClient = PearHyperliquidClient;
|
|
3402
3914
|
exports.PearHyperliquidProvider = PearHyperliquidProvider;
|
|
3403
3915
|
exports.PearMigrationSDK = PearMigrationSDK;
|
|
3916
|
+
exports.TokenMetadataExtractor = TokenMetadataExtractor;
|
|
3404
3917
|
exports.default = PearHyperliquidClient;
|
|
3405
3918
|
exports.useAccountSummary = useAccountSummary;
|
|
3406
3919
|
exports.useAddress = useAddress;
|
|
@@ -3412,5 +3925,6 @@ exports.useMigrationSDK = useMigrationSDK;
|
|
|
3412
3925
|
exports.useOpenOrders = useOpenOrders;
|
|
3413
3926
|
exports.useOpenPositions = useOpenPositions;
|
|
3414
3927
|
exports.usePearHyperliquidClient = usePearHyperliquidClient;
|
|
3928
|
+
exports.useTokenSelection = useTokenSelection;
|
|
3415
3929
|
exports.useTradeHistories = useTradeHistories;
|
|
3416
3930
|
//# sourceMappingURL=index.js.map
|