@pear-protocol/hyperliquid-sdk 0.0.9 → 0.0.10
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/useTrading.d.ts +20 -8
- package/dist/index.d.ts +38 -22
- package/dist/index.esm.js +144 -102
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +144 -102
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +2 -2
- package/dist/types.d.ts +16 -12
- 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,
|
|
@@ -3184,32 +3202,24 @@ class PositionProcessor {
|
|
|
3184
3202
|
};
|
|
3185
3203
|
}
|
|
3186
3204
|
calculateEntryRatio(syncResults) {
|
|
3205
|
+
var _a, _b;
|
|
3187
3206
|
const longResults = syncResults.filter(result => result.asset.side === PositionSide.LONG);
|
|
3188
3207
|
const shortResults = syncResults.filter(result => result.asset.side === PositionSide.SHORT);
|
|
3189
3208
|
if (longResults.length === 0 || shortResults.length === 0)
|
|
3190
3209
|
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;
|
|
3210
|
+
const longEntryPrice = ((_a = longResults[0]) === null || _a === void 0 ? void 0 : _a.asset.entryPrice) ? Number(longResults[0].asset.entryPrice) : 0;
|
|
3211
|
+
const shortEntryPrice = ((_b = shortResults[0]) === null || _b === void 0 ? void 0 : _b.asset.entryPrice) ? Number(shortResults[0].asset.entryPrice) : 0;
|
|
3212
|
+
return shortEntryPrice > 0 ? longEntryPrice / shortEntryPrice : 0;
|
|
3198
3213
|
}
|
|
3199
3214
|
calculateMarkRatio(syncResults) {
|
|
3215
|
+
var _a, _b;
|
|
3200
3216
|
const longResults = syncResults.filter(result => result.asset.side === PositionSide.LONG);
|
|
3201
3217
|
const shortResults = syncResults.filter(result => result.asset.side === PositionSide.SHORT);
|
|
3202
3218
|
if (longResults.length === 0 || shortResults.length === 0)
|
|
3203
3219
|
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;
|
|
3220
|
+
const longMarkPrice = ((_a = longResults[0]) === null || _a === void 0 ? void 0 : _a.asset.coin) ? this.getMarketPrice(longResults[0].asset.coin) : 0;
|
|
3221
|
+
const shortMarkPrice = ((_b = shortResults[0]) === null || _b === void 0 ? void 0 : _b.asset.coin) ? this.getMarketPrice(shortResults[0].asset.coin) : 0;
|
|
3222
|
+
return shortMarkPrice > 0 ? longMarkPrice / shortMarkPrice : 0;
|
|
3213
3223
|
}
|
|
3214
3224
|
calculateNetFundingFromSyncResults(syncResults) {
|
|
3215
3225
|
const netFunding = syncResults.reduce((sum, result) => {
|
|
@@ -3352,17 +3362,24 @@ const useCalculatedAccountSummary = (platformAccountSummary, platformOpenOrders,
|
|
|
3352
3362
|
};
|
|
3353
3363
|
|
|
3354
3364
|
/**
|
|
3355
|
-
* Hook to access trade histories
|
|
3365
|
+
* Hook to access trade histories with loading state
|
|
3356
3366
|
*/
|
|
3357
3367
|
const useTradeHistories = () => {
|
|
3358
3368
|
const context = require$$0.useContext(PearHyperliquidContext);
|
|
3359
3369
|
if (!context) {
|
|
3360
3370
|
throw new Error('useTradeHistories must be used within a PearHyperliquidProvider');
|
|
3361
3371
|
}
|
|
3362
|
-
|
|
3372
|
+
const isLoading = require$$0.useMemo(() => {
|
|
3373
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3374
|
+
return context.data.tradeHistories === null && context.isConnected;
|
|
3375
|
+
}, [context.data.tradeHistories, context.isConnected]);
|
|
3376
|
+
return {
|
|
3377
|
+
data: context.data.tradeHistories,
|
|
3378
|
+
isLoading
|
|
3379
|
+
};
|
|
3363
3380
|
};
|
|
3364
3381
|
/**
|
|
3365
|
-
* Hook to access open positions with real-time calculations
|
|
3382
|
+
* Hook to access open positions with real-time calculations and loading state
|
|
3366
3383
|
*/
|
|
3367
3384
|
const useOpenPositions = () => {
|
|
3368
3385
|
const context = require$$0.useContext(PearHyperliquidContext);
|
|
@@ -3371,20 +3388,38 @@ const useOpenPositions = () => {
|
|
|
3371
3388
|
}
|
|
3372
3389
|
// Use calculated positions that sync platform data with HyperLiquid real-time data
|
|
3373
3390
|
const calculatedPositions = useCalculatedOpenPositions(context.data.openPositions, context.webData2, context.allMids);
|
|
3374
|
-
|
|
3391
|
+
const isLoading = require$$0.useMemo(() => {
|
|
3392
|
+
// Loading is true initially and becomes false only when:
|
|
3393
|
+
// 1. WebSocket has open-positions data
|
|
3394
|
+
// 2. webData2 and allMids are ready (required for calculations)
|
|
3395
|
+
// 3. Connection is established
|
|
3396
|
+
return (context.isConnected &&
|
|
3397
|
+
(context.data.openPositions === null || !context.webData2 || !context.allMids));
|
|
3398
|
+
}, [context.data.openPositions, context.webData2, context.allMids, context.isConnected]);
|
|
3399
|
+
return {
|
|
3400
|
+
data: calculatedPositions,
|
|
3401
|
+
isLoading
|
|
3402
|
+
};
|
|
3375
3403
|
};
|
|
3376
3404
|
/**
|
|
3377
|
-
* Hook to access open orders
|
|
3405
|
+
* Hook to access open orders with loading state
|
|
3378
3406
|
*/
|
|
3379
3407
|
const useOpenOrders = () => {
|
|
3380
3408
|
const context = require$$0.useContext(PearHyperliquidContext);
|
|
3381
3409
|
if (!context) {
|
|
3382
3410
|
throw new Error('useOpenOrders must be used within a PearHyperliquidProvider');
|
|
3383
3411
|
}
|
|
3384
|
-
|
|
3412
|
+
const isLoading = require$$0.useMemo(() => {
|
|
3413
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3414
|
+
return context.data.openOrders === null && context.isConnected;
|
|
3415
|
+
}, [context.data.openOrders, context.isConnected]);
|
|
3416
|
+
return {
|
|
3417
|
+
data: context.data.openOrders,
|
|
3418
|
+
isLoading
|
|
3419
|
+
};
|
|
3385
3420
|
};
|
|
3386
3421
|
/**
|
|
3387
|
-
* Hook to access account summary with real-time calculations
|
|
3422
|
+
* Hook to access account summary with real-time calculations and loading state
|
|
3388
3423
|
*/
|
|
3389
3424
|
const useAccountSummary = () => {
|
|
3390
3425
|
var _a, _b, _c, _d;
|
|
@@ -3394,7 +3429,14 @@ const useAccountSummary = () => {
|
|
|
3394
3429
|
}
|
|
3395
3430
|
// Use calculated account summary that syncs platform data with HyperLiquid real-time data
|
|
3396
3431
|
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
|
-
|
|
3432
|
+
const isLoading = require$$0.useMemo(() => {
|
|
3433
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3434
|
+
return context.data.accountSummary === null && context.isConnected;
|
|
3435
|
+
}, [context.data.accountSummary, context.isConnected]);
|
|
3436
|
+
return {
|
|
3437
|
+
data: calculatedAccountSummary,
|
|
3438
|
+
isLoading
|
|
3439
|
+
};
|
|
3398
3440
|
};
|
|
3399
3441
|
|
|
3400
3442
|
exports.AccountSummaryCalculator = AccountSummaryCalculator;
|