@pear-protocol/hyperliquid-sdk 0.0.73 → 0.0.75
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/clients/kalshi.d.ts +87 -0
- package/dist/clients/positions.d.ts +12 -20
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/useHyperliquidUserFills.d.ts +17 -0
- package/dist/hyperliquid-websocket.d.ts +2 -1
- package/dist/index.d.ts +299 -58
- package/dist/index.js +356 -154
- package/dist/types.d.ts +123 -17
- package/dist/utils/order-helpers.d.ts +57 -0
- package/package.json +1 -1
- package/dist/hooks/useAutoSyncFills.d.ts +0 -19
package/dist/index.js
CHANGED
|
@@ -775,7 +775,7 @@ const useUserSelection$1 = create((set, get) => ({
|
|
|
775
775
|
resetToDefaults: () => set((prev) => ({ ...prev, ...DEFAULT_STATE })),
|
|
776
776
|
}));
|
|
777
777
|
|
|
778
|
-
const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
778
|
+
const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }) => {
|
|
779
779
|
const { setAllMids, setActiveAssetData, upsertActiveAssetData, setCandleData, deleteCandleSymbol, deleteActiveAssetData, addCandleData, setFinalAssetContexts, setFinalAtOICaps, setAggregatedClearingHouseState, setRawClearinghouseStates, } = useHyperliquidData();
|
|
780
780
|
const { setSpotState } = useUserData();
|
|
781
781
|
const { candleInterval } = useUserSelection$1();
|
|
@@ -810,6 +810,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
810
810
|
if ('channel' in message && 'data' in message) {
|
|
811
811
|
const response = message;
|
|
812
812
|
switch (response.channel) {
|
|
813
|
+
case 'userFills':
|
|
814
|
+
{
|
|
815
|
+
const maybePromise = onUserFills === null || onUserFills === void 0 ? void 0 : onUserFills();
|
|
816
|
+
if (maybePromise instanceof Promise) {
|
|
817
|
+
maybePromise.catch((err) => console.error('[HyperLiquid WS] userFills callback error', err));
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
break;
|
|
813
821
|
case 'webData3':
|
|
814
822
|
const webData3 = response.data;
|
|
815
823
|
// finalAssetContexts now sourced from allDexsAssetCtxs channel
|
|
@@ -950,6 +958,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
950
958
|
setAggregatedClearingHouseState,
|
|
951
959
|
setRawClearinghouseStates,
|
|
952
960
|
setSpotState,
|
|
961
|
+
onUserFills,
|
|
953
962
|
]);
|
|
954
963
|
const connect = useCallback(() => {
|
|
955
964
|
if (!enabled)
|
|
@@ -1067,6 +1076,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
1067
1076
|
},
|
|
1068
1077
|
};
|
|
1069
1078
|
sendJsonMessage(unsubscribeAllDexsClearinghouseState);
|
|
1079
|
+
const unsubscribeUserFills = {
|
|
1080
|
+
method: 'unsubscribe',
|
|
1081
|
+
subscription: {
|
|
1082
|
+
type: 'userFills',
|
|
1083
|
+
user: subscribedAddress,
|
|
1084
|
+
},
|
|
1085
|
+
};
|
|
1086
|
+
sendJsonMessage(unsubscribeUserFills);
|
|
1070
1087
|
}
|
|
1071
1088
|
const subscribeWebData3 = {
|
|
1072
1089
|
method: "subscribe",
|
|
@@ -1098,10 +1115,18 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, }) => {
|
|
|
1098
1115
|
type: "allDexsAssetCtxs",
|
|
1099
1116
|
},
|
|
1100
1117
|
};
|
|
1118
|
+
const subscribeUserFills = {
|
|
1119
|
+
method: 'subscribe',
|
|
1120
|
+
subscription: {
|
|
1121
|
+
type: 'userFills',
|
|
1122
|
+
user: userAddress,
|
|
1123
|
+
},
|
|
1124
|
+
};
|
|
1101
1125
|
sendJsonMessage(subscribeWebData3);
|
|
1102
1126
|
sendJsonMessage(subscribeAllDexsClearinghouseState);
|
|
1103
1127
|
sendJsonMessage(subscribeAllMids);
|
|
1104
1128
|
sendJsonMessage(subscribeAllDexsAssetCtxs);
|
|
1129
|
+
sendJsonMessage(subscribeUserFills);
|
|
1105
1130
|
// Subscribe to spotState for real-time spot balances (USDH, USDC, etc.)
|
|
1106
1131
|
// Only subscribe if we have a real user address (not the default)
|
|
1107
1132
|
if (userAddress !== DEFAULT_ADDRESS) {
|
|
@@ -1514,6 +1539,20 @@ const useWebData = () => {
|
|
|
1514
1539
|
};
|
|
1515
1540
|
};
|
|
1516
1541
|
|
|
1542
|
+
/**
|
|
1543
|
+
* Check if two symbols match, handling kPEPE/KPEPE variations
|
|
1544
|
+
* Returns true if symbols match (case-insensitive for k-prefix tokens)
|
|
1545
|
+
*/
|
|
1546
|
+
function symbolsMatch(assetName, searchSymbol) {
|
|
1547
|
+
// Exact match
|
|
1548
|
+
if (assetName === searchSymbol)
|
|
1549
|
+
return true;
|
|
1550
|
+
// Try case-insensitive match for k-prefix tokens (kPEPE vs KPEPE)
|
|
1551
|
+
if (assetName.toUpperCase() === searchSymbol.toUpperCase()) {
|
|
1552
|
+
return true;
|
|
1553
|
+
}
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1517
1556
|
/**
|
|
1518
1557
|
* Extracts token metadata from aggregated WebData3 contexts and AllMids data
|
|
1519
1558
|
*/
|
|
@@ -1534,8 +1573,9 @@ class TokenMetadataExtractor {
|
|
|
1534
1573
|
}
|
|
1535
1574
|
// Find token index in aggregated universe
|
|
1536
1575
|
// For HIP3 assets, match both name AND marketPrefix
|
|
1576
|
+
// Uses symbolsMatch to handle kPEPE/KPEPE case variations
|
|
1537
1577
|
const universeIndex = perpMetaAssets.findIndex((asset) => {
|
|
1538
|
-
if (asset.name
|
|
1578
|
+
if (!symbolsMatch(asset.name, symbol))
|
|
1539
1579
|
return false;
|
|
1540
1580
|
// If marketPrefix is specified, match it; otherwise match assets without prefix
|
|
1541
1581
|
if (marketPrefix) {
|
|
@@ -1554,15 +1594,20 @@ class TokenMetadataExtractor {
|
|
|
1554
1594
|
}
|
|
1555
1595
|
// Get current price - prefer assetCtx.midPx as it's already index-matched,
|
|
1556
1596
|
// fall back to allMids lookup if midPx is null
|
|
1557
|
-
const
|
|
1597
|
+
const actualSymbol = universeAsset.name; // Use actual symbol from universe (handles kPEPE vs KPEPE)
|
|
1598
|
+
const prefixedKeyColon = marketPrefix
|
|
1599
|
+
? `${marketPrefix}:${actualSymbol}`
|
|
1600
|
+
: null;
|
|
1558
1601
|
let currentPrice = 0;
|
|
1559
1602
|
// Primary source: assetCtx.midPx (already properly indexed)
|
|
1560
1603
|
if (assetCtx.midPx) {
|
|
1561
1604
|
currentPrice = parseFloat(assetCtx.midPx);
|
|
1562
1605
|
}
|
|
1563
1606
|
// Fallback: allMids lookup with multiple key formats for HIP3 markets
|
|
1607
|
+
// Try actual symbol from universe first, then input symbol
|
|
1564
1608
|
if (!currentPrice || isNaN(currentPrice)) {
|
|
1565
1609
|
const currentPriceStr = (prefixedKeyColon && allMids.mids[prefixedKeyColon]) ||
|
|
1610
|
+
allMids.mids[actualSymbol] ||
|
|
1566
1611
|
allMids.mids[symbol];
|
|
1567
1612
|
currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
|
|
1568
1613
|
}
|
|
@@ -1576,10 +1621,12 @@ class TokenMetadataExtractor {
|
|
|
1576
1621
|
const markPrice = parseFloat(assetCtx.markPx);
|
|
1577
1622
|
const oraclePrice = parseFloat(assetCtx.oraclePx);
|
|
1578
1623
|
// Extract leverage info from activeAssetData if available
|
|
1579
|
-
// Try prefixed key first (e.g., "xyz:TSLA"), then
|
|
1624
|
+
// Try prefixed key first (e.g., "xyz:TSLA"), then actual symbol, then input symbol
|
|
1580
1625
|
const activeDataKey = prefixedKeyColon && (activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[prefixedKeyColon])
|
|
1581
1626
|
? prefixedKeyColon
|
|
1582
|
-
:
|
|
1627
|
+
: (activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[actualSymbol])
|
|
1628
|
+
? actualSymbol
|
|
1629
|
+
: symbol;
|
|
1583
1630
|
const tokenActiveData = activeAssetData === null || activeAssetData === void 0 ? void 0 : activeAssetData[activeDataKey];
|
|
1584
1631
|
const leverage = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.leverage;
|
|
1585
1632
|
const maxTradeSzs = tokenActiveData === null || tokenActiveData === void 0 ? void 0 : tokenActiveData.maxTradeSzs;
|
|
@@ -1631,7 +1678,7 @@ class TokenMetadataExtractor {
|
|
|
1631
1678
|
static isTokenAvailable(symbol, perpMetaAssets) {
|
|
1632
1679
|
if (!perpMetaAssets)
|
|
1633
1680
|
return false;
|
|
1634
|
-
return perpMetaAssets.some((asset) => asset.name
|
|
1681
|
+
return perpMetaAssets.some((asset) => symbolsMatch(asset.name, symbol));
|
|
1635
1682
|
}
|
|
1636
1683
|
}
|
|
1637
1684
|
|
|
@@ -6761,147 +6808,6 @@ function useAgentWallet() {
|
|
|
6761
6808
|
};
|
|
6762
6809
|
}
|
|
6763
6810
|
|
|
6764
|
-
/**
|
|
6765
|
-
* Sync external fills into Pear Hyperliquid service (POST /sync/fills)
|
|
6766
|
-
*/
|
|
6767
|
-
const syncFills = async (baseUrl, payload) => {
|
|
6768
|
-
const url = joinUrl(baseUrl, '/sync/fills');
|
|
6769
|
-
try {
|
|
6770
|
-
const response = await apiClient.post(url, payload, {
|
|
6771
|
-
headers: { 'Content-Type': 'application/json' },
|
|
6772
|
-
timeout: 30000,
|
|
6773
|
-
});
|
|
6774
|
-
return { data: response.data, status: response.status, headers: response.headers };
|
|
6775
|
-
}
|
|
6776
|
-
catch (error) {
|
|
6777
|
-
throw toApiError(error);
|
|
6778
|
-
}
|
|
6779
|
-
};
|
|
6780
|
-
/**
|
|
6781
|
-
* Convenience: fetch user fills from HyperLiquid, then sync them to Pear backend
|
|
6782
|
-
*/
|
|
6783
|
-
const syncUserFillsFromHyperliquid = async (baseUrl, user, aggregateByTime = true, lastSyncAt = null, assetPositions) => {
|
|
6784
|
-
const firstStartTime = lastSyncAt ? Number(lastSyncAt) + 1 : 0;
|
|
6785
|
-
const allFills = [];
|
|
6786
|
-
const seenTids = new Set();
|
|
6787
|
-
let startTime = firstStartTime;
|
|
6788
|
-
let batchSize = 0;
|
|
6789
|
-
do {
|
|
6790
|
-
const { data: batch } = await fetchUserFillsFromHyperliquid(user, startTime, aggregateByTime);
|
|
6791
|
-
batchSize = batch.length;
|
|
6792
|
-
for (const fill of batch) {
|
|
6793
|
-
const tid = fill.tid;
|
|
6794
|
-
if (tid === undefined)
|
|
6795
|
-
continue;
|
|
6796
|
-
if (!seenTids.has(tid)) {
|
|
6797
|
-
seenTids.add(tid);
|
|
6798
|
-
allFills.push(fill);
|
|
6799
|
-
}
|
|
6800
|
-
}
|
|
6801
|
-
if (batchSize === 2000) {
|
|
6802
|
-
const last = batch[batch.length - 1];
|
|
6803
|
-
startTime = last.time;
|
|
6804
|
-
}
|
|
6805
|
-
} while (batchSize === 2000);
|
|
6806
|
-
startTime = firstStartTime;
|
|
6807
|
-
batchSize = 0;
|
|
6808
|
-
do {
|
|
6809
|
-
const { data: twapBatch } = await fetchUserTwapSliceFillsByTime(user, startTime, aggregateByTime);
|
|
6810
|
-
batchSize = twapBatch.length;
|
|
6811
|
-
for (const item of twapBatch) {
|
|
6812
|
-
const fill = item.fill;
|
|
6813
|
-
const tid = fill.tid;
|
|
6814
|
-
if (tid === undefined)
|
|
6815
|
-
continue;
|
|
6816
|
-
if (!seenTids.has(tid)) {
|
|
6817
|
-
seenTids.add(tid);
|
|
6818
|
-
allFills.push(fill);
|
|
6819
|
-
}
|
|
6820
|
-
}
|
|
6821
|
-
if (batchSize === 2000) {
|
|
6822
|
-
const last = twapBatch[twapBatch.length - 1];
|
|
6823
|
-
startTime = last.fill.time;
|
|
6824
|
-
}
|
|
6825
|
-
} while (batchSize === 2000);
|
|
6826
|
-
const sortedFills = [...allFills].sort((a, b) => Number(a.time) - Number(b.time));
|
|
6827
|
-
return syncFills(baseUrl, { user, fills: sortedFills, assetPositions });
|
|
6828
|
-
};
|
|
6829
|
-
|
|
6830
|
-
/**
|
|
6831
|
-
* Listens to address changes and periodically syncs user fills
|
|
6832
|
-
* Defaults: aggregate=true, interval=60s
|
|
6833
|
-
*/
|
|
6834
|
-
function useAutoSyncFills(options) {
|
|
6835
|
-
const { baseUrl, address, intervalMs = 60000, aggregateByTime = true, } = options;
|
|
6836
|
-
const [lastRunAt, setLastRunAt] = useState(null);
|
|
6837
|
-
const [lastResult, setLastResult] = useState(null);
|
|
6838
|
-
const [error, setError] = useState(null);
|
|
6839
|
-
const [isSyncing, setIsSyncing] = useState(false);
|
|
6840
|
-
const mountedRef = useRef(true);
|
|
6841
|
-
const runningRef = useRef(null);
|
|
6842
|
-
const lastSyncedAt = useUserData((state) => { var _a; return (_a = state.accountSummary) === null || _a === void 0 ? void 0 : _a.lastSyncedAt; });
|
|
6843
|
-
const enabled = useUserData((state) => state.isAuthenticated);
|
|
6844
|
-
useEffect(() => {
|
|
6845
|
-
mountedRef.current = true;
|
|
6846
|
-
return () => { mountedRef.current = false; };
|
|
6847
|
-
}, []);
|
|
6848
|
-
const canRun = useMemo(() => {
|
|
6849
|
-
return Boolean(enabled && address && baseUrl);
|
|
6850
|
-
}, [enabled, address, baseUrl]);
|
|
6851
|
-
const doSync = useCallback(async () => {
|
|
6852
|
-
var _a;
|
|
6853
|
-
if (!canRun)
|
|
6854
|
-
return;
|
|
6855
|
-
if (runningRef.current)
|
|
6856
|
-
return;
|
|
6857
|
-
if (!useUserData.getState().accountSummary)
|
|
6858
|
-
return;
|
|
6859
|
-
if (!((_a = useHyperliquidData.getState().aggregatedClearingHouseState) === null || _a === void 0 ? void 0 : _a.assetPositions))
|
|
6860
|
-
return;
|
|
6861
|
-
setIsSyncing(true);
|
|
6862
|
-
setError(null);
|
|
6863
|
-
const promise = (async () => {
|
|
6864
|
-
var _a;
|
|
6865
|
-
try {
|
|
6866
|
-
const { data } = await syncUserFillsFromHyperliquid(baseUrl, address, aggregateByTime, lastSyncedAt, useHyperliquidData.getState().aggregatedClearingHouseState.assetPositions);
|
|
6867
|
-
if (!mountedRef.current)
|
|
6868
|
-
return;
|
|
6869
|
-
setLastResult(data);
|
|
6870
|
-
setLastRunAt(Date.now());
|
|
6871
|
-
}
|
|
6872
|
-
catch (e) {
|
|
6873
|
-
if (!mountedRef.current)
|
|
6874
|
-
return;
|
|
6875
|
-
setError((_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : 'Failed to sync user fills');
|
|
6876
|
-
}
|
|
6877
|
-
finally {
|
|
6878
|
-
if (mountedRef.current)
|
|
6879
|
-
setIsSyncing(false);
|
|
6880
|
-
runningRef.current = null;
|
|
6881
|
-
}
|
|
6882
|
-
})();
|
|
6883
|
-
runningRef.current = promise;
|
|
6884
|
-
await promise;
|
|
6885
|
-
}, [canRun, baseUrl, address, aggregateByTime, lastSyncedAt]);
|
|
6886
|
-
useEffect(() => {
|
|
6887
|
-
if (!canRun)
|
|
6888
|
-
return;
|
|
6889
|
-
// Fire immediately on address change/enable
|
|
6890
|
-
void doSync();
|
|
6891
|
-
const id = setInterval(() => {
|
|
6892
|
-
void doSync();
|
|
6893
|
-
}, Math.max(1000, intervalMs));
|
|
6894
|
-
return () => clearInterval(id);
|
|
6895
|
-
}, [canRun, doSync, intervalMs]);
|
|
6896
|
-
return {
|
|
6897
|
-
lastRunAt,
|
|
6898
|
-
lastResult,
|
|
6899
|
-
error,
|
|
6900
|
-
isSyncing,
|
|
6901
|
-
triggerSync: doSync,
|
|
6902
|
-
};
|
|
6903
|
-
}
|
|
6904
|
-
|
|
6905
6811
|
/**
|
|
6906
6812
|
* Create a position (MARKET/LIMIT/TWAP) using Pear Hyperliquid service
|
|
6907
6813
|
* Authorization is derived from headers (Axios defaults or browser localStorage fallback)
|
|
@@ -6920,6 +6826,9 @@ async function createPosition(baseUrl, payload, hip3Assets) {
|
|
|
6920
6826
|
...payload,
|
|
6921
6827
|
longAssets: mapAssets(payload.longAssets),
|
|
6922
6828
|
shortAssets: mapAssets(payload.shortAssets),
|
|
6829
|
+
assetName: payload.assetName
|
|
6830
|
+
? toBackendSymbol(payload.assetName, hip3Assets)
|
|
6831
|
+
: undefined,
|
|
6923
6832
|
};
|
|
6924
6833
|
try {
|
|
6925
6834
|
const resp = await apiClient.post(url, translatedPayload, {
|
|
@@ -8331,6 +8240,138 @@ const useAllUserBalances = () => {
|
|
|
8331
8240
|
]);
|
|
8332
8241
|
};
|
|
8333
8242
|
|
|
8243
|
+
/**
|
|
8244
|
+
* Sync external fills into Pear Hyperliquid service (POST /sync/fills)
|
|
8245
|
+
*/
|
|
8246
|
+
const syncFills = async (baseUrl, payload) => {
|
|
8247
|
+
const url = joinUrl(baseUrl, '/sync/fills');
|
|
8248
|
+
try {
|
|
8249
|
+
const response = await apiClient.post(url, payload, {
|
|
8250
|
+
headers: { 'Content-Type': 'application/json' },
|
|
8251
|
+
timeout: 30000,
|
|
8252
|
+
});
|
|
8253
|
+
return { data: response.data, status: response.status, headers: response.headers };
|
|
8254
|
+
}
|
|
8255
|
+
catch (error) {
|
|
8256
|
+
throw toApiError(error);
|
|
8257
|
+
}
|
|
8258
|
+
};
|
|
8259
|
+
/**
|
|
8260
|
+
* Convenience: fetch user fills from HyperLiquid, then sync them to Pear backend
|
|
8261
|
+
*/
|
|
8262
|
+
const syncUserFillsFromHyperliquid = async (baseUrl, user, aggregateByTime = true, lastSyncAt = null, assetPositions) => {
|
|
8263
|
+
const firstStartTime = lastSyncAt ? Number(lastSyncAt) + 1 : 0;
|
|
8264
|
+
const allFills = [];
|
|
8265
|
+
const seenTids = new Set();
|
|
8266
|
+
let startTime = firstStartTime;
|
|
8267
|
+
let batchSize = 0;
|
|
8268
|
+
do {
|
|
8269
|
+
const { data: batch } = await fetchUserFillsFromHyperliquid(user, startTime, aggregateByTime);
|
|
8270
|
+
batchSize = batch.length;
|
|
8271
|
+
for (const fill of batch) {
|
|
8272
|
+
const tid = fill.tid;
|
|
8273
|
+
if (tid === undefined)
|
|
8274
|
+
continue;
|
|
8275
|
+
if (!seenTids.has(tid)) {
|
|
8276
|
+
seenTids.add(tid);
|
|
8277
|
+
allFills.push(fill);
|
|
8278
|
+
}
|
|
8279
|
+
}
|
|
8280
|
+
if (batchSize === 2000) {
|
|
8281
|
+
const last = batch[batch.length - 1];
|
|
8282
|
+
startTime = last.time;
|
|
8283
|
+
}
|
|
8284
|
+
} while (batchSize === 2000);
|
|
8285
|
+
startTime = firstStartTime;
|
|
8286
|
+
batchSize = 0;
|
|
8287
|
+
do {
|
|
8288
|
+
const { data: twapBatch } = await fetchUserTwapSliceFillsByTime(user, startTime, aggregateByTime);
|
|
8289
|
+
batchSize = twapBatch.length;
|
|
8290
|
+
for (const item of twapBatch) {
|
|
8291
|
+
const fill = item.fill;
|
|
8292
|
+
const tid = fill.tid;
|
|
8293
|
+
if (tid === undefined)
|
|
8294
|
+
continue;
|
|
8295
|
+
if (!seenTids.has(tid)) {
|
|
8296
|
+
seenTids.add(tid);
|
|
8297
|
+
allFills.push(fill);
|
|
8298
|
+
}
|
|
8299
|
+
}
|
|
8300
|
+
if (batchSize === 2000) {
|
|
8301
|
+
const last = twapBatch[twapBatch.length - 1];
|
|
8302
|
+
startTime = last.fill.time;
|
|
8303
|
+
}
|
|
8304
|
+
} while (batchSize === 2000);
|
|
8305
|
+
const sortedFills = [...allFills].sort((a, b) => Number(a.time) - Number(b.time));
|
|
8306
|
+
return syncFills(baseUrl, { user, fills: sortedFills, assetPositions });
|
|
8307
|
+
};
|
|
8308
|
+
|
|
8309
|
+
/**
|
|
8310
|
+
* Provides a callback to sync user fills whenever the native WebSocket emits a userFills event.
|
|
8311
|
+
*/
|
|
8312
|
+
function useHyperliquidUserFills(options) {
|
|
8313
|
+
const { baseUrl, address: addressOverride, aggregateByTime = true } = options;
|
|
8314
|
+
const [lastRunAt, setLastRunAt] = useState(null);
|
|
8315
|
+
const [lastResult, setLastResult] = useState(null);
|
|
8316
|
+
const [error, setError] = useState(null);
|
|
8317
|
+
const [isSyncing, setIsSyncing] = useState(false);
|
|
8318
|
+
const mountedRef = useRef(true);
|
|
8319
|
+
const runningRef = useRef(null);
|
|
8320
|
+
const enabled = useUserData((state) => state.isAuthenticated);
|
|
8321
|
+
useEffect(() => {
|
|
8322
|
+
mountedRef.current = true;
|
|
8323
|
+
return () => {
|
|
8324
|
+
mountedRef.current = false;
|
|
8325
|
+
};
|
|
8326
|
+
}, []);
|
|
8327
|
+
const handleUserFillsEvent = useCallback(async () => {
|
|
8328
|
+
var _a;
|
|
8329
|
+
const userState = useUserData.getState();
|
|
8330
|
+
const currentAddress = userState.address || addressOverride;
|
|
8331
|
+
const lastSyncedAt = (_a = userState.accountSummary) === null || _a === void 0 ? void 0 : _a.lastSyncedAt;
|
|
8332
|
+
if (!(enabled && currentAddress && baseUrl))
|
|
8333
|
+
return;
|
|
8334
|
+
if (runningRef.current)
|
|
8335
|
+
return;
|
|
8336
|
+
if (!userState.accountSummary)
|
|
8337
|
+
return;
|
|
8338
|
+
const clearinghouseState = useHyperliquidData.getState().aggregatedClearingHouseState;
|
|
8339
|
+
if (!(clearinghouseState === null || clearinghouseState === void 0 ? void 0 : clearinghouseState.assetPositions))
|
|
8340
|
+
return;
|
|
8341
|
+
setIsSyncing(true);
|
|
8342
|
+
setError(null);
|
|
8343
|
+
const promise = (async () => {
|
|
8344
|
+
var _a;
|
|
8345
|
+
try {
|
|
8346
|
+
const { data } = await syncUserFillsFromHyperliquid(baseUrl, currentAddress, aggregateByTime, lastSyncedAt, clearinghouseState.assetPositions);
|
|
8347
|
+
if (!mountedRef.current)
|
|
8348
|
+
return;
|
|
8349
|
+
setLastResult(data);
|
|
8350
|
+
setLastRunAt(Date.now());
|
|
8351
|
+
}
|
|
8352
|
+
catch (e) {
|
|
8353
|
+
if (!mountedRef.current)
|
|
8354
|
+
return;
|
|
8355
|
+
setError((_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : 'Failed to sync user fills');
|
|
8356
|
+
}
|
|
8357
|
+
finally {
|
|
8358
|
+
if (mountedRef.current)
|
|
8359
|
+
setIsSyncing(false);
|
|
8360
|
+
runningRef.current = null;
|
|
8361
|
+
}
|
|
8362
|
+
})();
|
|
8363
|
+
runningRef.current = promise;
|
|
8364
|
+
await promise;
|
|
8365
|
+
}, [baseUrl, addressOverride, aggregateByTime, enabled]);
|
|
8366
|
+
return {
|
|
8367
|
+
lastRunAt,
|
|
8368
|
+
lastResult,
|
|
8369
|
+
error,
|
|
8370
|
+
isSyncing,
|
|
8371
|
+
handleUserFillsEvent,
|
|
8372
|
+
};
|
|
8373
|
+
}
|
|
8374
|
+
|
|
8334
8375
|
const PearHyperliquidContext = createContext(undefined);
|
|
8335
8376
|
/**
|
|
8336
8377
|
* React Provider for PearHyperliquidClient
|
|
@@ -8344,6 +8385,11 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
|
|
|
8344
8385
|
const setHip3Assets = useHyperliquidData((state) => state.setHip3Assets);
|
|
8345
8386
|
const setHip3MarketPrefixes = useHyperliquidData((state) => state.setHip3MarketPrefixes);
|
|
8346
8387
|
const websocketsEnabled = useMemo(() => Array.isArray(perpsMetaAssets) && perpsMetaAssets.length > 0, [perpsMetaAssets]);
|
|
8388
|
+
const { handleUserFillsEvent } = useHyperliquidUserFills({
|
|
8389
|
+
baseUrl: apiBaseUrl,
|
|
8390
|
+
address,
|
|
8391
|
+
aggregateByTime: true,
|
|
8392
|
+
});
|
|
8347
8393
|
const { isConnected, lastError } = useHyperliquidWebSocket({
|
|
8348
8394
|
wsUrl,
|
|
8349
8395
|
address,
|
|
@@ -8352,6 +8398,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
|
|
|
8352
8398
|
const { isConnected: nativeIsConnected, lastError: nativeLastError } = useHyperliquidNativeWebSocket({
|
|
8353
8399
|
address,
|
|
8354
8400
|
enabled: websocketsEnabled,
|
|
8401
|
+
onUserFills: handleUserFillsEvent,
|
|
8355
8402
|
});
|
|
8356
8403
|
useEffect(() => {
|
|
8357
8404
|
if (perpsMetaAssets === null) {
|
|
@@ -8458,12 +8505,6 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
|
|
|
8458
8505
|
setHip3Assets,
|
|
8459
8506
|
setHip3MarketPrefixes,
|
|
8460
8507
|
]);
|
|
8461
|
-
useAutoSyncFills({
|
|
8462
|
-
baseUrl: apiBaseUrl,
|
|
8463
|
-
address,
|
|
8464
|
-
intervalMs: 60000,
|
|
8465
|
-
aggregateByTime: true,
|
|
8466
|
-
});
|
|
8467
8508
|
const contextValue = useMemo(() => ({
|
|
8468
8509
|
// Config
|
|
8469
8510
|
clientId,
|
|
@@ -8499,6 +8540,42 @@ function usePearHyperliquid() {
|
|
|
8499
8540
|
return ctx;
|
|
8500
8541
|
}
|
|
8501
8542
|
|
|
8543
|
+
const KALSHI_API_BASE_URL = 'https://api.elections.kalshi.com/trade-api/v2';
|
|
8544
|
+
async function getKalshiMarkets(params) {
|
|
8545
|
+
const url = new URL(`${KALSHI_API_BASE_URL}/markets`);
|
|
8546
|
+
if (params) {
|
|
8547
|
+
if (params.limit !== undefined) {
|
|
8548
|
+
url.searchParams.set('limit', String(params.limit));
|
|
8549
|
+
}
|
|
8550
|
+
if (params.cursor) {
|
|
8551
|
+
url.searchParams.set('cursor', params.cursor);
|
|
8552
|
+
}
|
|
8553
|
+
if (params.tickers && params.tickers.length > 0) {
|
|
8554
|
+
url.searchParams.set('tickers', params.tickers.join(','));
|
|
8555
|
+
}
|
|
8556
|
+
if (params.event_ticker) {
|
|
8557
|
+
url.searchParams.set('event_ticker', params.event_ticker);
|
|
8558
|
+
}
|
|
8559
|
+
if (params.series_ticker) {
|
|
8560
|
+
url.searchParams.set('series_ticker', params.series_ticker);
|
|
8561
|
+
}
|
|
8562
|
+
if (params.status) {
|
|
8563
|
+
url.searchParams.set('status', params.status);
|
|
8564
|
+
}
|
|
8565
|
+
}
|
|
8566
|
+
try {
|
|
8567
|
+
const response = await axios$1.get(url.toString());
|
|
8568
|
+
return {
|
|
8569
|
+
data: response.data,
|
|
8570
|
+
status: response.status,
|
|
8571
|
+
headers: response.headers,
|
|
8572
|
+
};
|
|
8573
|
+
}
|
|
8574
|
+
catch (error) {
|
|
8575
|
+
throw toApiError(error);
|
|
8576
|
+
}
|
|
8577
|
+
}
|
|
8578
|
+
|
|
8502
8579
|
/**
|
|
8503
8580
|
* Detects conflicts between selected tokens and existing positions
|
|
8504
8581
|
*/
|
|
@@ -8586,4 +8663,129 @@ function mapCandleIntervalToTradingViewInterval(interval) {
|
|
|
8586
8663
|
}
|
|
8587
8664
|
}
|
|
8588
8665
|
|
|
8589
|
-
|
|
8666
|
+
/**
|
|
8667
|
+
* Helper functions to safely extract values from order parameters
|
|
8668
|
+
* based on the order type and parameter structure.
|
|
8669
|
+
*/
|
|
8670
|
+
/**
|
|
8671
|
+
* Get leverage from order parameters (available for Market, Trigger, Twap, Ladder orders)
|
|
8672
|
+
*/
|
|
8673
|
+
function getOrderLeverage(order) {
|
|
8674
|
+
const p = order.parameters;
|
|
8675
|
+
if ('leverage' in p && p.leverage !== undefined) {
|
|
8676
|
+
return p.leverage;
|
|
8677
|
+
}
|
|
8678
|
+
return undefined;
|
|
8679
|
+
}
|
|
8680
|
+
/**
|
|
8681
|
+
* Get USD value from order parameters (available for Market, Trigger, Twap, Ladder orders)
|
|
8682
|
+
*/
|
|
8683
|
+
function getOrderUsdValue(order) {
|
|
8684
|
+
const p = order.parameters;
|
|
8685
|
+
if ('usdValue' in p) {
|
|
8686
|
+
return p.usdValue;
|
|
8687
|
+
}
|
|
8688
|
+
return undefined;
|
|
8689
|
+
}
|
|
8690
|
+
/**
|
|
8691
|
+
* Get trigger value from order parameters (available for TP/SL and Trigger orders)
|
|
8692
|
+
*/
|
|
8693
|
+
function getOrderTriggerValue(order) {
|
|
8694
|
+
const p = order.parameters;
|
|
8695
|
+
if ('triggerValue' in p) {
|
|
8696
|
+
return p.triggerValue;
|
|
8697
|
+
}
|
|
8698
|
+
return undefined;
|
|
8699
|
+
}
|
|
8700
|
+
/**
|
|
8701
|
+
* Get TP/SL trigger type from order parameters (only for TP/SL orders)
|
|
8702
|
+
*/
|
|
8703
|
+
function getOrderTpSlTriggerType(order) {
|
|
8704
|
+
if (order.orderType === 'TP' || order.orderType === 'SL') {
|
|
8705
|
+
const p = order.parameters;
|
|
8706
|
+
return p.triggerType;
|
|
8707
|
+
}
|
|
8708
|
+
return undefined;
|
|
8709
|
+
}
|
|
8710
|
+
/**
|
|
8711
|
+
* Get trigger type from order parameters (for Trigger orders)
|
|
8712
|
+
*/
|
|
8713
|
+
function getOrderTriggerType(order) {
|
|
8714
|
+
if (order.orderType === 'TRIGGER') {
|
|
8715
|
+
const p = order.parameters;
|
|
8716
|
+
return p.triggerType;
|
|
8717
|
+
}
|
|
8718
|
+
return undefined;
|
|
8719
|
+
}
|
|
8720
|
+
/**
|
|
8721
|
+
* Get order direction from order parameters (for Trigger orders)
|
|
8722
|
+
*/
|
|
8723
|
+
function getOrderDirection(order) {
|
|
8724
|
+
if (order.orderType === 'TRIGGER') {
|
|
8725
|
+
const p = order.parameters;
|
|
8726
|
+
return p.direction;
|
|
8727
|
+
}
|
|
8728
|
+
return undefined;
|
|
8729
|
+
}
|
|
8730
|
+
/**
|
|
8731
|
+
* Get reduce only flag from order parameters (available for all order types)
|
|
8732
|
+
*/
|
|
8733
|
+
function getOrderReduceOnly(order) {
|
|
8734
|
+
var _a;
|
|
8735
|
+
const p = order.parameters;
|
|
8736
|
+
if ('reduceOnly' in p) {
|
|
8737
|
+
return (_a = p.reduceOnly) !== null && _a !== void 0 ? _a : false;
|
|
8738
|
+
}
|
|
8739
|
+
return false;
|
|
8740
|
+
}
|
|
8741
|
+
/**
|
|
8742
|
+
* Get TWAP duration from order parameters (only for TWAP orders)
|
|
8743
|
+
*/
|
|
8744
|
+
function getOrderTwapDuration(order) {
|
|
8745
|
+
if (order.orderType === 'TWAP') {
|
|
8746
|
+
const p = order.parameters;
|
|
8747
|
+
return p.duration;
|
|
8748
|
+
}
|
|
8749
|
+
return undefined;
|
|
8750
|
+
}
|
|
8751
|
+
/**
|
|
8752
|
+
* Get ladder config from order parameters (only for Ladder orders)
|
|
8753
|
+
*/
|
|
8754
|
+
function getOrderLadderConfig(order) {
|
|
8755
|
+
if (order.orderType === 'LADDER') {
|
|
8756
|
+
const p = order.parameters;
|
|
8757
|
+
return {
|
|
8758
|
+
ratioStart: p.ratioStart,
|
|
8759
|
+
ratioEnd: p.ratioEnd,
|
|
8760
|
+
numberOfLevels: p.numberOfLevels,
|
|
8761
|
+
};
|
|
8762
|
+
}
|
|
8763
|
+
return undefined;
|
|
8764
|
+
}
|
|
8765
|
+
/**
|
|
8766
|
+
* Check if the order is a BTC Dominance trigger order
|
|
8767
|
+
*/
|
|
8768
|
+
function isBtcDomOrder(order) {
|
|
8769
|
+
if (order.orderType === 'TRIGGER') {
|
|
8770
|
+
const p = order.parameters;
|
|
8771
|
+
return p.triggerType === 'BTC_DOM';
|
|
8772
|
+
}
|
|
8773
|
+
return false;
|
|
8774
|
+
}
|
|
8775
|
+
/**
|
|
8776
|
+
* Get trailing info from TP/SL order parameters
|
|
8777
|
+
*/
|
|
8778
|
+
function getOrderTrailingInfo(order) {
|
|
8779
|
+
var _a;
|
|
8780
|
+
if (order.orderType === 'TP' || order.orderType === 'SL') {
|
|
8781
|
+
const p = order.parameters;
|
|
8782
|
+
return {
|
|
8783
|
+
isTrailing: (_a = p.isTrailing) !== null && _a !== void 0 ? _a : false,
|
|
8784
|
+
trailingDeltaValue: p.trailingDeltaValue,
|
|
8785
|
+
trailingActivationValue: p.trailingActivationValue,
|
|
8786
|
+
};
|
|
8787
|
+
}
|
|
8788
|
+
return undefined;
|
|
8789
|
+
}
|
|
8790
|
+
|
|
8791
|
+
export { AccountSummaryCalculator, ConflictDetector, MAX_ASSETS_PER_LEG, MINIMUM_ASSET_USD_VALUE, MaxAssetsPerLegError, MinimumPositionSizeError, PearHyperliquidProvider, TokenMetadataExtractor, adjustAdvancePosition, adjustOrder, adjustPosition, calculateMinimumPositionValue, calculateWeightedRatio, cancelOrder, cancelTwap, cancelTwapOrder, closeAllPositions, closePosition, computeBasketCandles, createCandleLookups, createPosition, executeSpotOrder, getAvailableMarkets, getCompleteTimestamps, getKalshiMarkets, getMarketPrefix, getOrderDirection, getOrderLadderConfig, getOrderLeverage, getOrderReduceOnly, getOrderTpSlTriggerType, getOrderTrailingInfo, getOrderTriggerType, getOrderTriggerValue, getOrderTwapDuration, getOrderUsdValue, getPortfolio, isBtcDomOrder, isHip3Market, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, toBackendSymbol, toBackendSymbolWithMarket, toDisplaySymbol, toggleWatchlist, updateLeverage, updateRiskParameters, useAccountSummary, useActiveBaskets, useAgentWallet, useAllBaskets, useAllUserBalances, useAuth, useBasketCandles, useFindBasket, useHighlightedBaskets, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidNativeWebSocket, useHyperliquidUserFills, useHyperliquidWebSocket, useMarketData, useMarketDataAllPayload, useMarketDataPayload, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePerpMetaAssets, usePortfolio, usePosition, useSpotOrder, useTokenSelectionMetadata, useTopGainers, useTopLosers, useTradeHistories, useTwap, useUserSelection, useWatchlist, useWatchlistBaskets, useWebData, validateMaxAssetsPerLeg, validateMinimumAssetSize, validatePositionSize };
|