@pear-protocol/hyperliquid-sdk 0.1.12 → 0.1.13-beta.2
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/auth.d.ts +1 -1
- package/dist/hooks/useAuth.d.ts +3 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +436 -166
- package/dist/provider.d.ts +1 -1
- package/dist/store/hyperliquidDataStore.d.ts +3 -2
- package/dist/store/userDataStore.d.ts +2 -0
- package/dist/utils/http.d.ts +2 -2
- package/package.json +1 -1
package/dist/clients/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiResponse, GetEIP712MessageResponse, AuthenticateRequest, AuthenticateResponse, RefreshTokenResponse, LogoutResponse } from
|
|
1
|
+
import type { ApiResponse, GetEIP712MessageResponse, AuthenticateRequest, AuthenticateResponse, RefreshTokenResponse, LogoutResponse } from "../types";
|
|
2
2
|
export declare function getEIP712Message(baseUrl: string, address: string, clientId: string): Promise<ApiResponse<GetEIP712MessageResponse>>;
|
|
3
3
|
export declare function authenticate(baseUrl: string, body: AuthenticateRequest): Promise<ApiResponse<AuthenticateResponse>>;
|
|
4
4
|
/**
|
package/dist/hooks/useAuth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GetEIP712MessageResponse } from
|
|
1
|
+
import type { GetEIP712MessageResponse } from '../types';
|
|
2
2
|
export declare function useAuth(): {
|
|
3
3
|
readonly isReady: boolean;
|
|
4
4
|
readonly isAuthenticated: boolean;
|
|
@@ -10,4 +10,6 @@ export declare function useAuth(): {
|
|
|
10
10
|
readonly loginWithPrivyToken: (address: string, appId: string, privyAccessToken: string) => Promise<void>;
|
|
11
11
|
readonly refreshTokens: () => Promise<import("../types").RefreshTokenResponse>;
|
|
12
12
|
readonly logout: () => Promise<void>;
|
|
13
|
+
readonly setAddress: (address: string | null) => void;
|
|
14
|
+
readonly clearSession: () => void;
|
|
13
15
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1429,6 +1429,8 @@ declare function useAuth(): {
|
|
|
1429
1429
|
readonly loginWithPrivyToken: (address: string, appId: string, privyAccessToken: string) => Promise<void>;
|
|
1430
1430
|
readonly refreshTokens: () => Promise<RefreshTokenResponse>;
|
|
1431
1431
|
readonly logout: () => Promise<void>;
|
|
1432
|
+
readonly setAddress: (address: string | null) => void;
|
|
1433
|
+
readonly clearSession: () => void;
|
|
1432
1434
|
};
|
|
1433
1435
|
|
|
1434
1436
|
interface MarginRequiredPerCollateral {
|
package/dist/index.js
CHANGED
|
@@ -65,18 +65,29 @@ const useUserData = create((set) => ({
|
|
|
65
65
|
userExtraAgents: null,
|
|
66
66
|
spotState: null,
|
|
67
67
|
userAbstractionMode: null,
|
|
68
|
+
isReady: false,
|
|
68
69
|
setUserAbstractionMode: (value) => set({ userAbstractionMode: value }),
|
|
69
70
|
setAccessToken: (token) => set({ accessToken: token }),
|
|
70
71
|
setRefreshToken: (token) => set({ refreshToken: token }),
|
|
71
72
|
setIsAuthenticated: (value) => set({ isAuthenticated: value }),
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
setIsReady: (value) => set({ isReady: value }),
|
|
74
|
+
setAddress: (address) => set((state) => {
|
|
75
|
+
// If address changed, clear user-specific data to prevent stale balances
|
|
76
|
+
const addressChanged = state.address !== null && state.address !== address;
|
|
77
|
+
if (addressChanged) {
|
|
78
|
+
return {
|
|
79
|
+
address,
|
|
80
|
+
// Clear user-specific data when address changes
|
|
81
|
+
spotState: null,
|
|
82
|
+
tradeHistories: null,
|
|
83
|
+
rawOpenPositions: null,
|
|
84
|
+
openOrders: null,
|
|
85
|
+
accountSummary: null,
|
|
86
|
+
twapDetails: null,
|
|
87
|
+
notifications: null,
|
|
88
|
+
userExtraAgents: null,
|
|
89
|
+
userAbstractionMode: null,
|
|
90
|
+
};
|
|
80
91
|
}
|
|
81
92
|
return { address };
|
|
82
93
|
}),
|
|
@@ -88,10 +99,6 @@ const useUserData = create((set) => ({
|
|
|
88
99
|
setNotifications: (value) => set({ notifications: value }),
|
|
89
100
|
setSpotState: (value) => set({ spotState: value }),
|
|
90
101
|
clean: () => set({
|
|
91
|
-
accessToken: null,
|
|
92
|
-
refreshToken: null,
|
|
93
|
-
isAuthenticated: false,
|
|
94
|
-
address: null,
|
|
95
102
|
tradeHistories: null,
|
|
96
103
|
rawOpenPositions: null,
|
|
97
104
|
openOrders: null,
|
|
@@ -384,19 +391,19 @@ class TokenMetadataExtractor {
|
|
|
384
391
|
if (!assetCtx) {
|
|
385
392
|
return null;
|
|
386
393
|
}
|
|
387
|
-
// Get current price - prefer
|
|
388
|
-
// fall back to
|
|
394
|
+
// Get current price - prefer allMids (real-time WebSocket data),
|
|
395
|
+
// fall back to assetCtx.midPx if not available
|
|
389
396
|
const actualSymbol = foundAsset.name;
|
|
390
397
|
let currentPrice = 0;
|
|
391
|
-
// Fallback: assetCtx.midPx (
|
|
392
|
-
if (!currentPrice || isNaN(currentPrice)) {
|
|
393
|
-
const currentPriceStr = allMids.mids[actualSymbol] || allMids.mids[symbol];
|
|
394
|
-
currentPrice = currentPriceStr ? parseFloat(currentPriceStr) : 0;
|
|
395
|
-
}
|
|
396
|
-
// Primary source: allMids lookup
|
|
398
|
+
// Fallback: assetCtx.midPx (from REST API, less frequent)
|
|
397
399
|
if (assetCtx.midPx) {
|
|
398
400
|
currentPrice = parseFloat(assetCtx.midPx);
|
|
399
401
|
}
|
|
402
|
+
// Primary source: allMids (real-time WebSocket data)
|
|
403
|
+
const currentPriceStr = allMids.mids[actualSymbol] || allMids.mids[symbol];
|
|
404
|
+
if (currentPriceStr) {
|
|
405
|
+
currentPrice = parseFloat(currentPriceStr);
|
|
406
|
+
}
|
|
400
407
|
// Get previous day price
|
|
401
408
|
const prevDayPrice = parseFloat(assetCtx.prevDayPx);
|
|
402
409
|
// Calculate 24h price change
|
|
@@ -551,7 +558,7 @@ const useHyperliquidData = create((set) => ({
|
|
|
551
558
|
tokenMetadata: refreshTokenMetadata(state, { allMids: value }),
|
|
552
559
|
})),
|
|
553
560
|
setActiveAssetData: (value) => set((state) => {
|
|
554
|
-
const activeAssetData = typeof value ===
|
|
561
|
+
const activeAssetData = typeof value === "function" ? value(state.activeAssetData) : value;
|
|
555
562
|
return {
|
|
556
563
|
activeAssetData,
|
|
557
564
|
tokenMetadata: refreshTokenMetadata(state, { activeAssetData }),
|
|
@@ -591,7 +598,10 @@ const useHyperliquidData = create((set) => ({
|
|
|
591
598
|
setCandleData: (value) => set({ candleData: value }),
|
|
592
599
|
upsertActiveAssetData: (key, value) => set((state) => {
|
|
593
600
|
var _a;
|
|
594
|
-
const activeAssetData = {
|
|
601
|
+
const activeAssetData = {
|
|
602
|
+
...((_a = state.activeAssetData) !== null && _a !== void 0 ? _a : {}),
|
|
603
|
+
[key]: value,
|
|
604
|
+
};
|
|
595
605
|
return {
|
|
596
606
|
activeAssetData,
|
|
597
607
|
tokenMetadata: refreshTokenMetadata(state, { activeAssetData }, { symbols: [key] }),
|
|
@@ -626,6 +636,11 @@ const useHyperliquidData = create((set) => ({
|
|
|
626
636
|
perpMetasByDex: state.perpMetasByDex,
|
|
627
637
|
}),
|
|
628
638
|
})),
|
|
639
|
+
// Clear user-specific data (called when wallet address changes)
|
|
640
|
+
clearUserData: () => set({
|
|
641
|
+
aggregatedClearingHouseState: null,
|
|
642
|
+
rawClearinghouseStates: null,
|
|
643
|
+
}),
|
|
629
644
|
}));
|
|
630
645
|
|
|
631
646
|
/**
|
|
@@ -906,6 +921,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
906
921
|
const reconnectAttemptsRef = useRef(0);
|
|
907
922
|
const manualCloseRef = useRef(false);
|
|
908
923
|
const onUserFillsRef = useRef(onUserFills);
|
|
924
|
+
const reconnectTimeoutRef = useRef(null);
|
|
909
925
|
const [readyState, setReadyState] = useState(ReadyState.CONNECTING);
|
|
910
926
|
// Keep the ref updated with the latest callback
|
|
911
927
|
useEffect(() => {
|
|
@@ -916,9 +932,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
916
932
|
try {
|
|
917
933
|
const message = JSON.parse(event.data);
|
|
918
934
|
// Handle subscription responses
|
|
919
|
-
if (
|
|
935
|
+
if ("success" in message || "error" in message) {
|
|
920
936
|
if (message.error) {
|
|
921
|
-
console.error(
|
|
937
|
+
console.error("[HyperLiquid WS] Subscription error:", message.error);
|
|
922
938
|
setLastError(message.error);
|
|
923
939
|
}
|
|
924
940
|
else {
|
|
@@ -927,44 +943,44 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
927
943
|
return;
|
|
928
944
|
}
|
|
929
945
|
// Handle channel data messages
|
|
930
|
-
if (
|
|
946
|
+
if ("channel" in message && "data" in message) {
|
|
931
947
|
const response = message;
|
|
932
948
|
switch (response.channel) {
|
|
933
|
-
case
|
|
949
|
+
case "userFills":
|
|
934
950
|
{
|
|
935
951
|
const maybePromise = (_a = onUserFillsRef.current) === null || _a === void 0 ? void 0 : _a.call(onUserFillsRef);
|
|
936
952
|
if (maybePromise instanceof Promise) {
|
|
937
|
-
maybePromise.catch((err) => console.error(
|
|
953
|
+
maybePromise.catch((err) => console.error("[HyperLiquid WS] userFills callback error", err));
|
|
938
954
|
}
|
|
939
955
|
}
|
|
940
956
|
break;
|
|
941
|
-
case
|
|
957
|
+
case "webData3":
|
|
942
958
|
const webData3 = response.data;
|
|
943
959
|
// finalAssetContexts now sourced from allDexsAssetCtxs channel
|
|
944
960
|
const finalAtOICaps = webData3.perpDexStates.flatMap((dex) => dex.perpsAtOpenInterestCap);
|
|
945
961
|
setFinalAtOICaps(finalAtOICaps);
|
|
946
962
|
setUserAbstractionMode(webData3.userState.abstraction || null);
|
|
947
963
|
break;
|
|
948
|
-
case
|
|
964
|
+
case "allDexsAssetCtxs":
|
|
949
965
|
{
|
|
950
966
|
const data = response.data;
|
|
951
967
|
// Store by DEX name, mapping '' to 'HYPERLIQUID'
|
|
952
968
|
const assetContextsByDex = new Map();
|
|
953
969
|
data.ctxs.forEach(([dexKey, ctxs]) => {
|
|
954
|
-
const dexName = dexKey ===
|
|
970
|
+
const dexName = dexKey === "" ? "HYPERLIQUID" : dexKey;
|
|
955
971
|
assetContextsByDex.set(dexName, ctxs || []);
|
|
956
972
|
});
|
|
957
973
|
setAssetContextsByDex(assetContextsByDex);
|
|
958
974
|
}
|
|
959
975
|
break;
|
|
960
|
-
case
|
|
976
|
+
case "allDexsClearinghouseState":
|
|
961
977
|
{
|
|
962
978
|
const data = response.data;
|
|
963
979
|
const states = (data.clearinghouseStates || [])
|
|
964
980
|
.map(([, s]) => s)
|
|
965
981
|
.filter(Boolean);
|
|
966
|
-
const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v ||
|
|
967
|
-
const toStr = (n) => Number.isFinite(n) ? n.toString() :
|
|
982
|
+
const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || "0") || 0), 0);
|
|
983
|
+
const toStr = (n) => Number.isFinite(n) ? n.toString() : "0";
|
|
968
984
|
const assetPositions = states.flatMap((s) => s.assetPositions || []);
|
|
969
985
|
const crossMaintenanceMarginUsed = toStr(sum(states.map((s) => s.crossMaintenanceMarginUsed)));
|
|
970
986
|
const crossMarginSummary = {
|
|
@@ -994,26 +1010,26 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
994
1010
|
setClearinghouseStateReceived(true);
|
|
995
1011
|
}
|
|
996
1012
|
break;
|
|
997
|
-
case
|
|
1013
|
+
case "allMids":
|
|
998
1014
|
{
|
|
999
1015
|
const data = response.data;
|
|
1000
1016
|
setAllMids(data);
|
|
1001
1017
|
}
|
|
1002
1018
|
break;
|
|
1003
|
-
case
|
|
1019
|
+
case "activeAssetData":
|
|
1004
1020
|
{
|
|
1005
1021
|
const assetData = response.data;
|
|
1006
1022
|
upsertActiveAssetData(assetData.coin, assetData);
|
|
1007
1023
|
}
|
|
1008
1024
|
break;
|
|
1009
|
-
case
|
|
1025
|
+
case "candle":
|
|
1010
1026
|
{
|
|
1011
1027
|
const candleDataItem = response.data;
|
|
1012
|
-
const symbol = candleDataItem.s ||
|
|
1028
|
+
const symbol = candleDataItem.s || "";
|
|
1013
1029
|
addCandleData(symbol, candleDataItem);
|
|
1014
1030
|
}
|
|
1015
1031
|
break;
|
|
1016
|
-
case
|
|
1032
|
+
case "spotState":
|
|
1017
1033
|
{
|
|
1018
1034
|
const spotStateData = response.data;
|
|
1019
1035
|
if (spotStateData === null || spotStateData === void 0 ? void 0 : spotStateData.spotState) {
|
|
@@ -1028,7 +1044,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1028
1044
|
}
|
|
1029
1045
|
catch (error) {
|
|
1030
1046
|
const errorMessage = `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`;
|
|
1031
|
-
console.error(
|
|
1047
|
+
console.error("[HyperLiquid WS] Parse error:", errorMessage, "Raw message:", event.data);
|
|
1032
1048
|
setLastError(errorMessage);
|
|
1033
1049
|
}
|
|
1034
1050
|
}, [
|
|
@@ -1042,9 +1058,14 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1042
1058
|
setSpotState,
|
|
1043
1059
|
]);
|
|
1044
1060
|
const connect = useCallback(() => {
|
|
1045
|
-
console.log(
|
|
1061
|
+
console.log("[HyperLiquid WS] connect() called, enabled:", enabled);
|
|
1046
1062
|
if (!enabled)
|
|
1047
1063
|
return;
|
|
1064
|
+
// Clear any pending reconnect timeout
|
|
1065
|
+
if (reconnectTimeoutRef.current) {
|
|
1066
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
1067
|
+
reconnectTimeoutRef.current = null;
|
|
1068
|
+
}
|
|
1048
1069
|
try {
|
|
1049
1070
|
// Avoid opening multiple sockets if one is already active or connecting
|
|
1050
1071
|
if (wsRef.current &&
|
|
@@ -1053,10 +1074,10 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1053
1074
|
console.log('[HyperLiquid WS] connect() returning early - socket already exists, readyState:', wsRef.current.readyState);
|
|
1054
1075
|
return;
|
|
1055
1076
|
}
|
|
1056
|
-
console.log(
|
|
1077
|
+
console.log("[HyperLiquid WS] Creating new WebSocket connection");
|
|
1057
1078
|
manualCloseRef.current = false;
|
|
1058
1079
|
setReadyState(ReadyState.CONNECTING);
|
|
1059
|
-
const ws = new WebSocket(
|
|
1080
|
+
const ws = new WebSocket("wss://api.hyperliquid.xyz/ws");
|
|
1060
1081
|
wsRef.current = ws;
|
|
1061
1082
|
ws.onopen = () => {
|
|
1062
1083
|
reconnectAttemptsRef.current = 0;
|
|
@@ -1065,17 +1086,22 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1065
1086
|
};
|
|
1066
1087
|
ws.onmessage = handleMessage;
|
|
1067
1088
|
ws.onerror = (event) => {
|
|
1068
|
-
console.error(
|
|
1069
|
-
setLastError(
|
|
1089
|
+
console.error("[HyperLiquid WS] Connection error:", event);
|
|
1090
|
+
setLastError("WebSocket error");
|
|
1070
1091
|
};
|
|
1071
1092
|
ws.onclose = () => {
|
|
1072
1093
|
setReadyState(ReadyState.CLOSED);
|
|
1073
|
-
|
|
1094
|
+
// Reset subscription state so effects will resubscribe on reconnect
|
|
1095
|
+
setSubscribedAddress(null);
|
|
1096
|
+
setSubscribedTokens([]);
|
|
1097
|
+
setSubscribedCandleTokens([]);
|
|
1098
|
+
setClearinghouseStateReceived(false);
|
|
1099
|
+
if (!manualCloseRef.current) {
|
|
1074
1100
|
reconnectAttemptsRef.current += 1;
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
}
|
|
1078
|
-
setTimeout(() => connect(),
|
|
1101
|
+
// Exponential backoff: 1s, 2s, 4s, 8s, 16s, 30s (max), then stay at 30s
|
|
1102
|
+
const delay = Math.min(1000 * Math.pow(2, reconnectAttemptsRef.current - 1), 30000);
|
|
1103
|
+
console.log(`[HyperLiquid WS] Reconnecting in ${delay}ms (attempt ${reconnectAttemptsRef.current})`);
|
|
1104
|
+
reconnectTimeoutRef.current = setTimeout(() => connect(), delay);
|
|
1079
1105
|
}
|
|
1080
1106
|
};
|
|
1081
1107
|
}
|
|
@@ -1086,9 +1112,53 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1086
1112
|
useEffect(() => {
|
|
1087
1113
|
console.log('[HyperLiquid WS] Connection effect running - calling connect()');
|
|
1088
1114
|
connect();
|
|
1115
|
+
// Handle online/offline events to reconnect when internet is restored
|
|
1116
|
+
const handleOnline = () => {
|
|
1117
|
+
console.log('[HyperLiquid WS] Browser went online, attempting reconnect');
|
|
1118
|
+
// Reset reconnect attempts when internet comes back
|
|
1119
|
+
reconnectAttemptsRef.current = 0;
|
|
1120
|
+
// Clear any pending reconnect timeout
|
|
1121
|
+
if (reconnectTimeoutRef.current) {
|
|
1122
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
1123
|
+
reconnectTimeoutRef.current = null;
|
|
1124
|
+
}
|
|
1125
|
+
// Reset subscription state so effects will resubscribe on reconnect
|
|
1126
|
+
setSubscribedAddress(null);
|
|
1127
|
+
setSubscribedTokens([]);
|
|
1128
|
+
setSubscribedCandleTokens([]);
|
|
1129
|
+
setClearinghouseStateReceived(false);
|
|
1130
|
+
// Close existing socket if in a bad state
|
|
1131
|
+
if (wsRef.current &&
|
|
1132
|
+
wsRef.current.readyState !== WebSocket.OPEN &&
|
|
1133
|
+
wsRef.current.readyState !== WebSocket.CONNECTING) {
|
|
1134
|
+
try {
|
|
1135
|
+
wsRef.current.close();
|
|
1136
|
+
}
|
|
1137
|
+
catch (_a) { }
|
|
1138
|
+
wsRef.current = null;
|
|
1139
|
+
}
|
|
1140
|
+
// Attempt to reconnect
|
|
1141
|
+
connect();
|
|
1142
|
+
};
|
|
1143
|
+
const handleOffline = () => {
|
|
1144
|
+
console.log('[HyperLiquid WS] Browser went offline');
|
|
1145
|
+
// Clear pending reconnect timeout since we're offline
|
|
1146
|
+
if (reconnectTimeoutRef.current) {
|
|
1147
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
1148
|
+
reconnectTimeoutRef.current = null;
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
window.addEventListener('online', handleOnline);
|
|
1152
|
+
window.addEventListener('offline', handleOffline);
|
|
1089
1153
|
return () => {
|
|
1090
1154
|
console.log('[HyperLiquid WS] Connection effect cleanup - closing existing connection');
|
|
1155
|
+
window.removeEventListener('online', handleOnline);
|
|
1156
|
+
window.removeEventListener('offline', handleOffline);
|
|
1091
1157
|
manualCloseRef.current = true;
|
|
1158
|
+
if (reconnectTimeoutRef.current) {
|
|
1159
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
1160
|
+
reconnectTimeoutRef.current = null;
|
|
1161
|
+
}
|
|
1092
1162
|
if (wsRef.current) {
|
|
1093
1163
|
try {
|
|
1094
1164
|
wsRef.current.close();
|
|
@@ -1109,7 +1179,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1109
1179
|
if (isConnected) {
|
|
1110
1180
|
// Send ping every 30 seconds
|
|
1111
1181
|
pingIntervalRef.current = setInterval(() => {
|
|
1112
|
-
sendJsonMessage({ method:
|
|
1182
|
+
sendJsonMessage({ method: "ping" });
|
|
1113
1183
|
}, 30000);
|
|
1114
1184
|
}
|
|
1115
1185
|
else {
|
|
@@ -1127,27 +1197,27 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1127
1197
|
}, [isConnected, sendJsonMessage]);
|
|
1128
1198
|
// Handle address subscription changes
|
|
1129
1199
|
useEffect(() => {
|
|
1130
|
-
const DEFAULT_ADDRESS =
|
|
1200
|
+
const DEFAULT_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
1131
1201
|
const userAddress = (address || DEFAULT_ADDRESS).toLowerCase();
|
|
1132
1202
|
const normalizedSubscribedAddress = (subscribedAddress === null || subscribedAddress === void 0 ? void 0 : subscribedAddress.toLowerCase()) || null;
|
|
1133
|
-
console.log(
|
|
1134
|
-
console.log(
|
|
1135
|
-
console.log(
|
|
1203
|
+
console.log("[HyperLiquid WS] Address subscription effect running");
|
|
1204
|
+
console.log("[HyperLiquid WS] address:", address, "userAddress:", userAddress, "subscribedAddress:", subscribedAddress, "normalizedSubscribedAddress:", normalizedSubscribedAddress);
|
|
1205
|
+
console.log("[HyperLiquid WS] isConnected:", isConnected);
|
|
1136
1206
|
if (normalizedSubscribedAddress === userAddress) {
|
|
1137
|
-
console.log(
|
|
1207
|
+
console.log("[HyperLiquid WS] Address unchanged, skipping subscription update");
|
|
1138
1208
|
return;
|
|
1139
1209
|
}
|
|
1140
1210
|
if (!isConnected) {
|
|
1141
|
-
console.log(
|
|
1211
|
+
console.log("[HyperLiquid WS] Not connected, skipping subscription update");
|
|
1142
1212
|
return;
|
|
1143
1213
|
}
|
|
1144
1214
|
// Unsubscribe from previous address if exists
|
|
1145
1215
|
if (subscribedAddress) {
|
|
1146
|
-
console.log(
|
|
1216
|
+
console.log("[HyperLiquid WS] Unsubscribing from previous address:", subscribedAddress);
|
|
1147
1217
|
const unsubscribeMessage = {
|
|
1148
|
-
method:
|
|
1218
|
+
method: "unsubscribe",
|
|
1149
1219
|
subscription: {
|
|
1150
|
-
type:
|
|
1220
|
+
type: "webData3",
|
|
1151
1221
|
user: subscribedAddress,
|
|
1152
1222
|
},
|
|
1153
1223
|
};
|
|
@@ -1155,54 +1225,54 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1155
1225
|
// Unsubscribe from spotState for previous address
|
|
1156
1226
|
if (subscribedAddress !== DEFAULT_ADDRESS) {
|
|
1157
1227
|
const unsubscribeSpotState = {
|
|
1158
|
-
method:
|
|
1228
|
+
method: "unsubscribe",
|
|
1159
1229
|
subscription: {
|
|
1160
|
-
type:
|
|
1230
|
+
type: "spotState",
|
|
1161
1231
|
user: subscribedAddress,
|
|
1162
1232
|
},
|
|
1163
1233
|
};
|
|
1164
1234
|
sendJsonMessage(unsubscribeSpotState);
|
|
1165
1235
|
}
|
|
1166
1236
|
const unsubscribeAllDexsClearinghouseState = {
|
|
1167
|
-
method:
|
|
1237
|
+
method: "unsubscribe",
|
|
1168
1238
|
subscription: {
|
|
1169
|
-
type:
|
|
1239
|
+
type: "allDexsClearinghouseState",
|
|
1170
1240
|
user: subscribedAddress,
|
|
1171
1241
|
},
|
|
1172
1242
|
};
|
|
1173
1243
|
sendJsonMessage(unsubscribeAllDexsClearinghouseState);
|
|
1174
1244
|
const unsubscribeUserFills = {
|
|
1175
|
-
method:
|
|
1245
|
+
method: "unsubscribe",
|
|
1176
1246
|
subscription: {
|
|
1177
|
-
type:
|
|
1247
|
+
type: "userFills",
|
|
1178
1248
|
user: subscribedAddress,
|
|
1179
1249
|
},
|
|
1180
1250
|
};
|
|
1181
1251
|
sendJsonMessage(unsubscribeUserFills);
|
|
1182
1252
|
}
|
|
1183
1253
|
const subscribeWebData3 = {
|
|
1184
|
-
method:
|
|
1254
|
+
method: "subscribe",
|
|
1185
1255
|
subscription: {
|
|
1186
|
-
type:
|
|
1256
|
+
type: "webData3",
|
|
1187
1257
|
user: userAddress,
|
|
1188
1258
|
},
|
|
1189
1259
|
};
|
|
1190
1260
|
// Subscribe to allMids
|
|
1191
1261
|
const subscribeAllMids = {
|
|
1192
|
-
method:
|
|
1262
|
+
method: "subscribe",
|
|
1193
1263
|
subscription: {
|
|
1194
|
-
type:
|
|
1195
|
-
dex:
|
|
1264
|
+
type: "allMids",
|
|
1265
|
+
dex: "ALL_DEXS",
|
|
1196
1266
|
},
|
|
1197
1267
|
};
|
|
1198
1268
|
// Subscribe to allDexsAssetCtxs (no payload params, global feed)
|
|
1199
1269
|
const subscribeAllDexsAssetCtxs = {
|
|
1200
|
-
method:
|
|
1270
|
+
method: "subscribe",
|
|
1201
1271
|
subscription: {
|
|
1202
|
-
type:
|
|
1272
|
+
type: "allDexsAssetCtxs",
|
|
1203
1273
|
},
|
|
1204
1274
|
};
|
|
1205
|
-
console.log(
|
|
1275
|
+
console.log("[HyperLiquid WS] Subscribing to new address:", userAddress);
|
|
1206
1276
|
sendJsonMessage(subscribeWebData3);
|
|
1207
1277
|
sendJsonMessage(subscribeAllMids);
|
|
1208
1278
|
sendJsonMessage(subscribeAllDexsAssetCtxs);
|
|
@@ -1210,9 +1280,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1210
1280
|
// Only subscribe if we have a real user address (not the default)
|
|
1211
1281
|
if (userAddress !== DEFAULT_ADDRESS.toLowerCase()) {
|
|
1212
1282
|
const subscribeSpotState = {
|
|
1213
|
-
method:
|
|
1283
|
+
method: "subscribe",
|
|
1214
1284
|
subscription: {
|
|
1215
|
-
type:
|
|
1285
|
+
type: "spotState",
|
|
1216
1286
|
user: userAddress,
|
|
1217
1287
|
},
|
|
1218
1288
|
};
|
|
@@ -1222,9 +1292,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1222
1292
|
// Only subscribe if we have a real user address (not the default)
|
|
1223
1293
|
if (userAddress !== DEFAULT_ADDRESS.toLowerCase()) {
|
|
1224
1294
|
const subscribeAllDexsClearinghouseState = {
|
|
1225
|
-
method:
|
|
1295
|
+
method: "subscribe",
|
|
1226
1296
|
subscription: {
|
|
1227
|
-
type:
|
|
1297
|
+
type: "allDexsClearinghouseState",
|
|
1228
1298
|
user: userAddress,
|
|
1229
1299
|
},
|
|
1230
1300
|
};
|
|
@@ -1258,9 +1328,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1258
1328
|
!userSummary)
|
|
1259
1329
|
return;
|
|
1260
1330
|
const subscribeUserFills = {
|
|
1261
|
-
method:
|
|
1331
|
+
method: "subscribe",
|
|
1262
1332
|
subscription: {
|
|
1263
|
-
type:
|
|
1333
|
+
type: "userFills",
|
|
1264
1334
|
user: subscribedAddress,
|
|
1265
1335
|
},
|
|
1266
1336
|
};
|
|
@@ -1282,9 +1352,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1282
1352
|
// Unsubscribe from tokens no longer in the list
|
|
1283
1353
|
tokensToUnsubscribe.forEach((token) => {
|
|
1284
1354
|
const unsubscribeMessage = {
|
|
1285
|
-
method:
|
|
1355
|
+
method: "unsubscribe",
|
|
1286
1356
|
subscription: {
|
|
1287
|
-
type:
|
|
1357
|
+
type: "activeAssetData",
|
|
1288
1358
|
user: address,
|
|
1289
1359
|
coin: token,
|
|
1290
1360
|
},
|
|
@@ -1294,9 +1364,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1294
1364
|
// Subscribe to new tokens
|
|
1295
1365
|
tokensToSubscribe.forEach((token) => {
|
|
1296
1366
|
const subscribeMessage = {
|
|
1297
|
-
method:
|
|
1367
|
+
method: "subscribe",
|
|
1298
1368
|
subscription: {
|
|
1299
|
-
type:
|
|
1369
|
+
type: "activeAssetData",
|
|
1300
1370
|
user: address,
|
|
1301
1371
|
coin: token,
|
|
1302
1372
|
},
|
|
@@ -1325,9 +1395,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1325
1395
|
if (prevInterval && prevInterval !== candleInterval) {
|
|
1326
1396
|
subscribedCandleTokens.forEach((token) => {
|
|
1327
1397
|
const unsubscribeMessage = {
|
|
1328
|
-
method:
|
|
1398
|
+
method: "unsubscribe",
|
|
1329
1399
|
subscription: {
|
|
1330
|
-
type:
|
|
1400
|
+
type: "candle",
|
|
1331
1401
|
coin: token,
|
|
1332
1402
|
interval: prevInterval,
|
|
1333
1403
|
},
|
|
@@ -1342,9 +1412,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1342
1412
|
// Unsubscribe from tokens no longer in the list
|
|
1343
1413
|
tokensToUnsubscribe.forEach((token) => {
|
|
1344
1414
|
const unsubscribeMessage = {
|
|
1345
|
-
method:
|
|
1415
|
+
method: "unsubscribe",
|
|
1346
1416
|
subscription: {
|
|
1347
|
-
type:
|
|
1417
|
+
type: "candle",
|
|
1348
1418
|
coin: token,
|
|
1349
1419
|
interval: candleInterval,
|
|
1350
1420
|
},
|
|
@@ -1354,9 +1424,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
|
|
|
1354
1424
|
// Subscribe to new tokens
|
|
1355
1425
|
tokensToSubscribe.forEach((token) => {
|
|
1356
1426
|
const subscribeMessage = {
|
|
1357
|
-
method:
|
|
1427
|
+
method: "subscribe",
|
|
1358
1428
|
subscription: {
|
|
1359
|
-
type:
|
|
1429
|
+
type: "candle",
|
|
1360
1430
|
coin: token,
|
|
1361
1431
|
interval: candleInterval,
|
|
1362
1432
|
},
|
|
@@ -5871,10 +5941,10 @@ function toApiError(error) {
|
|
|
5871
5941
|
var _a;
|
|
5872
5942
|
const axiosError = error;
|
|
5873
5943
|
const payload = (axiosError && axiosError.response ? axiosError.response.data : undefined);
|
|
5874
|
-
const message = typeof payload ===
|
|
5944
|
+
const message = typeof payload === "object" && payload && "message" in payload
|
|
5875
5945
|
? String(payload.message)
|
|
5876
|
-
: (axiosError === null || axiosError === void 0 ? void 0 : axiosError.message) ||
|
|
5877
|
-
const errField = typeof payload ===
|
|
5946
|
+
: (axiosError === null || axiosError === void 0 ? void 0 : axiosError.message) || "Request failed";
|
|
5947
|
+
const errField = typeof payload === "object" && payload && "error" in payload
|
|
5878
5948
|
? String(payload.error)
|
|
5879
5949
|
: undefined;
|
|
5880
5950
|
return {
|
|
@@ -5884,8 +5954,8 @@ function toApiError(error) {
|
|
|
5884
5954
|
};
|
|
5885
5955
|
}
|
|
5886
5956
|
function joinUrl(baseUrl, path) {
|
|
5887
|
-
const cleanBase = baseUrl.replace(/\/$/,
|
|
5888
|
-
const cleanPath = path.startsWith(
|
|
5957
|
+
const cleanBase = baseUrl.replace(/\/$/, "");
|
|
5958
|
+
const cleanPath = path.startsWith("/") ? path : `/${path}`;
|
|
5889
5959
|
return `${cleanBase}${cleanPath}`;
|
|
5890
5960
|
}
|
|
5891
5961
|
/**
|
|
@@ -5914,7 +5984,7 @@ function addAuthInterceptors(params) {
|
|
|
5914
5984
|
pendingRequests = [];
|
|
5915
5985
|
}
|
|
5916
5986
|
const isOurApiUrl = (url) => Boolean(url && url.startsWith(apiBaseUrl));
|
|
5917
|
-
const isRefreshUrl = (url) => Boolean(url && url.startsWith(joinUrl(apiBaseUrl,
|
|
5987
|
+
const isRefreshUrl = (url) => Boolean(url && url.startsWith(joinUrl(apiBaseUrl, "/auth/refresh")));
|
|
5918
5988
|
const reqId = apiClient.interceptors.request.use((config) => {
|
|
5919
5989
|
var _a;
|
|
5920
5990
|
try {
|
|
@@ -5922,11 +5992,12 @@ function addAuthInterceptors(params) {
|
|
|
5922
5992
|
const token = getAccessToken();
|
|
5923
5993
|
if (token) {
|
|
5924
5994
|
config.headers = (_a = config.headers) !== null && _a !== void 0 ? _a : {};
|
|
5925
|
-
|
|
5995
|
+
config.headers["Authorization"] = `Bearer ${token}`;
|
|
5926
5996
|
}
|
|
5927
5997
|
}
|
|
5928
5998
|
}
|
|
5929
|
-
catch (
|
|
5999
|
+
catch (err) {
|
|
6000
|
+
console.error("[Auth Interceptor] Request interceptor error:", err);
|
|
5930
6001
|
}
|
|
5931
6002
|
return config;
|
|
5932
6003
|
});
|
|
@@ -5937,22 +6008,36 @@ function addAuthInterceptors(params) {
|
|
|
5937
6008
|
const url = originalRequest === null || originalRequest === void 0 ? void 0 : originalRequest.url;
|
|
5938
6009
|
// If not our API or not 401, just reject
|
|
5939
6010
|
if (!status || status !== 401 || !isOurApiUrl(url)) {
|
|
6011
|
+
if (status === 401) {
|
|
6012
|
+
console.warn("[Auth Interceptor] 401 received but URL check failed:", {
|
|
6013
|
+
url,
|
|
6014
|
+
apiBaseUrl,
|
|
6015
|
+
isOurApiUrl: isOurApiUrl(url),
|
|
6016
|
+
});
|
|
6017
|
+
}
|
|
5940
6018
|
return Promise.reject(error);
|
|
5941
6019
|
}
|
|
6020
|
+
console.log("[Auth Interceptor] 401 detected, attempting token refresh for URL:", url);
|
|
5942
6021
|
// If the 401 is from refresh endpoint itself -> force logout
|
|
5943
6022
|
if (isRefreshUrl(url)) {
|
|
6023
|
+
console.warn("[Auth Interceptor] Refresh endpoint returned 401, logging out");
|
|
5944
6024
|
try {
|
|
5945
6025
|
await logout();
|
|
5946
6026
|
}
|
|
5947
|
-
catch (
|
|
6027
|
+
catch (err) {
|
|
6028
|
+
console.error("[Auth Interceptor] Logout failed:", err);
|
|
6029
|
+
}
|
|
5948
6030
|
return Promise.reject(error);
|
|
5949
6031
|
}
|
|
5950
6032
|
// Prevent infinite loop
|
|
5951
6033
|
if (originalRequest && originalRequest._retry) {
|
|
6034
|
+
console.warn("[Auth Interceptor] Request already retried, logging out");
|
|
5952
6035
|
try {
|
|
5953
6036
|
await logout();
|
|
5954
6037
|
}
|
|
5955
|
-
catch (
|
|
6038
|
+
catch (err) {
|
|
6039
|
+
console.error("[Auth Interceptor] Logout failed:", err);
|
|
6040
|
+
}
|
|
5956
6041
|
return Promise.reject(error);
|
|
5957
6042
|
}
|
|
5958
6043
|
// Mark so we don't retry twice
|
|
@@ -5966,31 +6051,45 @@ function addAuthInterceptors(params) {
|
|
|
5966
6051
|
if (!newToken || !originalRequest)
|
|
5967
6052
|
return reject(error);
|
|
5968
6053
|
originalRequest.headers = (_a = originalRequest.headers) !== null && _a !== void 0 ? _a : {};
|
|
5969
|
-
originalRequest.headers[
|
|
6054
|
+
originalRequest.headers["Authorization"] =
|
|
6055
|
+
`Bearer ${newToken}`;
|
|
5970
6056
|
resolve(apiClient.request(originalRequest));
|
|
5971
6057
|
});
|
|
5972
6058
|
});
|
|
5973
6059
|
}
|
|
5974
6060
|
isRefreshing = true;
|
|
5975
6061
|
try {
|
|
6062
|
+
console.log("[Auth Interceptor] Refreshing tokens...");
|
|
5976
6063
|
const refreshed = await refreshTokens();
|
|
5977
|
-
const newAccessToken = (_b = (refreshed &&
|
|
6064
|
+
const newAccessToken = (_b = (refreshed &&
|
|
6065
|
+
(refreshed.accessToken || ((_a = refreshed.data) === null || _a === void 0 ? void 0 : _a.accessToken)))) !== null && _b !== void 0 ? _b : null;
|
|
6066
|
+
if (!newAccessToken) {
|
|
6067
|
+
console.error("[Auth Interceptor] Token refresh succeeded but no access token in response:", refreshed);
|
|
6068
|
+
}
|
|
6069
|
+
else {
|
|
6070
|
+
console.log("[Auth Interceptor] Token refresh successful");
|
|
6071
|
+
}
|
|
5978
6072
|
resolvePendingRequests(newAccessToken);
|
|
5979
6073
|
if (originalRequest) {
|
|
5980
6074
|
originalRequest.headers = (_c = originalRequest.headers) !== null && _c !== void 0 ? _c : {};
|
|
5981
6075
|
if (newAccessToken)
|
|
5982
|
-
|
|
6076
|
+
originalRequest.headers["Authorization"] =
|
|
6077
|
+
`Bearer ${newAccessToken}`;
|
|
6078
|
+
console.log("[Auth Interceptor] Retrying original request with new token");
|
|
5983
6079
|
const resp = await apiClient.request(originalRequest);
|
|
5984
6080
|
return resp;
|
|
5985
6081
|
}
|
|
5986
6082
|
return Promise.reject(error);
|
|
5987
6083
|
}
|
|
5988
6084
|
catch (refreshErr) {
|
|
6085
|
+
console.error("[Auth Interceptor] Token refresh failed:", refreshErr);
|
|
5989
6086
|
resolvePendingRequests(null);
|
|
5990
6087
|
try {
|
|
5991
6088
|
await logout();
|
|
5992
6089
|
}
|
|
5993
|
-
catch (
|
|
6090
|
+
catch (err) {
|
|
6091
|
+
console.error("[Auth Interceptor] Logout failed:", err);
|
|
6092
|
+
}
|
|
5994
6093
|
return Promise.reject(refreshErr);
|
|
5995
6094
|
}
|
|
5996
6095
|
finally {
|
|
@@ -6001,11 +6100,15 @@ function addAuthInterceptors(params) {
|
|
|
6001
6100
|
try {
|
|
6002
6101
|
apiClient.interceptors.request.eject(reqId);
|
|
6003
6102
|
}
|
|
6004
|
-
catch (
|
|
6103
|
+
catch (err) {
|
|
6104
|
+
console.error("[Auth Interceptor] Failed to eject request interceptor:", err);
|
|
6105
|
+
}
|
|
6005
6106
|
try {
|
|
6006
6107
|
apiClient.interceptors.response.eject(resId);
|
|
6007
6108
|
}
|
|
6008
|
-
catch (
|
|
6109
|
+
catch (err) {
|
|
6110
|
+
console.error("[Auth Interceptor] Failed to eject response interceptor:", err);
|
|
6111
|
+
}
|
|
6009
6112
|
};
|
|
6010
6113
|
}
|
|
6011
6114
|
|
|
@@ -7511,20 +7614,34 @@ function usePortfolio() {
|
|
|
7511
7614
|
}
|
|
7512
7615
|
|
|
7513
7616
|
async function getEIP712Message(baseUrl, address, clientId) {
|
|
7514
|
-
const url = joinUrl(baseUrl,
|
|
7617
|
+
const url = joinUrl(baseUrl, "/auth/eip712-message");
|
|
7515
7618
|
try {
|
|
7516
|
-
const resp = await
|
|
7517
|
-
|
|
7619
|
+
const resp = await apiClient.get(url, {
|
|
7620
|
+
params: { address, clientId },
|
|
7621
|
+
timeout: 30000,
|
|
7622
|
+
});
|
|
7623
|
+
return {
|
|
7624
|
+
data: resp.data,
|
|
7625
|
+
status: resp.status,
|
|
7626
|
+
headers: resp.headers,
|
|
7627
|
+
};
|
|
7518
7628
|
}
|
|
7519
7629
|
catch (error) {
|
|
7520
7630
|
throw toApiError(error);
|
|
7521
7631
|
}
|
|
7522
7632
|
}
|
|
7523
7633
|
async function authenticate(baseUrl, body) {
|
|
7524
|
-
const url = joinUrl(baseUrl,
|
|
7634
|
+
const url = joinUrl(baseUrl, "/auth/login");
|
|
7525
7635
|
try {
|
|
7526
|
-
const resp = await
|
|
7527
|
-
|
|
7636
|
+
const resp = await apiClient.post(url, body, {
|
|
7637
|
+
headers: { "Content-Type": "application/json" },
|
|
7638
|
+
timeout: 30000,
|
|
7639
|
+
});
|
|
7640
|
+
return {
|
|
7641
|
+
data: resp.data,
|
|
7642
|
+
status: resp.status,
|
|
7643
|
+
headers: resp.headers,
|
|
7644
|
+
};
|
|
7528
7645
|
}
|
|
7529
7646
|
catch (error) {
|
|
7530
7647
|
throw toApiError(error);
|
|
@@ -7535,7 +7652,7 @@ async function authenticate(baseUrl, body) {
|
|
|
7535
7652
|
*/
|
|
7536
7653
|
async function authenticateWithPrivy(baseUrl, params) {
|
|
7537
7654
|
const body = {
|
|
7538
|
-
method:
|
|
7655
|
+
method: "privy_access_token",
|
|
7539
7656
|
address: params.address,
|
|
7540
7657
|
clientId: params.clientId,
|
|
7541
7658
|
details: { appId: params.appId, accessToken: params.accessToken },
|
|
@@ -7543,62 +7660,124 @@ async function authenticateWithPrivy(baseUrl, params) {
|
|
|
7543
7660
|
return authenticate(baseUrl, body);
|
|
7544
7661
|
}
|
|
7545
7662
|
async function refreshToken(baseUrl, refreshTokenVal) {
|
|
7546
|
-
const url = joinUrl(baseUrl,
|
|
7663
|
+
const url = joinUrl(baseUrl, "/auth/refresh");
|
|
7547
7664
|
try {
|
|
7548
|
-
const resp = await
|
|
7549
|
-
return {
|
|
7665
|
+
const resp = await apiClient.post(url, { refreshToken: refreshTokenVal }, { headers: { "Content-Type": "application/json" }, timeout: 30000 });
|
|
7666
|
+
return {
|
|
7667
|
+
data: resp.data,
|
|
7668
|
+
status: resp.status,
|
|
7669
|
+
headers: resp.headers,
|
|
7670
|
+
};
|
|
7550
7671
|
}
|
|
7551
7672
|
catch (error) {
|
|
7552
7673
|
throw toApiError(error);
|
|
7553
7674
|
}
|
|
7554
7675
|
}
|
|
7555
7676
|
async function logout(baseUrl, refreshTokenVal) {
|
|
7556
|
-
const url = joinUrl(baseUrl,
|
|
7677
|
+
const url = joinUrl(baseUrl, "/auth/logout");
|
|
7557
7678
|
try {
|
|
7558
|
-
const resp = await
|
|
7559
|
-
return {
|
|
7679
|
+
const resp = await apiClient.post(url, { refreshToken: refreshTokenVal }, { headers: { "Content-Type": "application/json" }, timeout: 30000 });
|
|
7680
|
+
return {
|
|
7681
|
+
data: resp.data,
|
|
7682
|
+
status: resp.status,
|
|
7683
|
+
headers: resp.headers,
|
|
7684
|
+
};
|
|
7560
7685
|
}
|
|
7561
7686
|
catch (error) {
|
|
7562
7687
|
throw toApiError(error);
|
|
7563
7688
|
}
|
|
7564
7689
|
}
|
|
7565
7690
|
|
|
7691
|
+
// Token expiration constants
|
|
7692
|
+
const ACCESS_TOKEN_BUFFER_MS = 5 * 60 * 1000; // Refresh 5 min before expiry
|
|
7693
|
+
const REFRESH_TOKEN_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days fallback
|
|
7694
|
+
function nowMs() {
|
|
7695
|
+
return Date.now();
|
|
7696
|
+
}
|
|
7697
|
+
function calcExpiresAt(expiresInSeconds) {
|
|
7698
|
+
return nowMs() + expiresInSeconds * 1000;
|
|
7699
|
+
}
|
|
7566
7700
|
function useAuth() {
|
|
7567
7701
|
const context = useContext(PearHyperliquidContext);
|
|
7568
7702
|
if (!context) {
|
|
7569
|
-
throw new Error(
|
|
7703
|
+
throw new Error('useAuth must be used within a PearHyperliquidProvider');
|
|
7570
7704
|
}
|
|
7571
7705
|
const { apiBaseUrl, clientId } = context;
|
|
7572
|
-
const [isReady, setIsReady] = useState(false);
|
|
7573
7706
|
const accessToken = useUserData((s) => s.accessToken);
|
|
7574
7707
|
const refreshToken$1 = useUserData((s) => s.refreshToken);
|
|
7708
|
+
const isReady = useUserData((s) => s.isReady);
|
|
7709
|
+
const isAuthenticated = useUserData((s) => s.isAuthenticated);
|
|
7575
7710
|
const setAccessToken = useUserData((s) => s.setAccessToken);
|
|
7576
7711
|
const setRefreshToken = useUserData((s) => s.setRefreshToken);
|
|
7577
|
-
const
|
|
7712
|
+
const setIsReady = useUserData((s) => s.setIsReady);
|
|
7578
7713
|
const setIsAuthenticated = useUserData((s) => s.setIsAuthenticated);
|
|
7579
7714
|
const address = useUserData((s) => s.address);
|
|
7580
7715
|
const setAddress = useUserData((s) => s.setAddress);
|
|
7716
|
+
// Ref to prevent concurrent refresh attempts
|
|
7717
|
+
const isRefreshingRef = useRef(false);
|
|
7581
7718
|
useEffect(() => {
|
|
7582
|
-
if (typeof window ==
|
|
7719
|
+
if (typeof window == 'undefined') {
|
|
7583
7720
|
return;
|
|
7584
7721
|
}
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7722
|
+
if (address) {
|
|
7723
|
+
const accessTokenKey = `${address}_accessToken`;
|
|
7724
|
+
const refreshTokenKey = `${address}_refreshToken`;
|
|
7725
|
+
const accessTokenExpiresAtKey = `${address}_accessTokenExpiresAt`;
|
|
7726
|
+
const refreshTokenExpiresAtKey = `${address}_refreshTokenExpiresAt`;
|
|
7727
|
+
const storedAccessToken = localStorage.getItem(accessTokenKey);
|
|
7728
|
+
const storedRefreshToken = localStorage.getItem(refreshTokenKey);
|
|
7729
|
+
const accessExpRaw = localStorage.getItem(accessTokenExpiresAtKey);
|
|
7730
|
+
const refreshExpRaw = localStorage.getItem(refreshTokenExpiresAtKey);
|
|
7731
|
+
const accessExp = accessExpRaw ? Number(accessExpRaw) : 0;
|
|
7732
|
+
const refreshExp = refreshExpRaw ? Number(refreshExpRaw) : 0;
|
|
7733
|
+
const now = nowMs();
|
|
7734
|
+
const accessValid = !!storedAccessToken && accessExp > now;
|
|
7735
|
+
const refreshValid = !!storedRefreshToken && refreshExp > now;
|
|
7736
|
+
if (accessValid && refreshValid) {
|
|
7737
|
+
// Both tokens are valid
|
|
7738
|
+
setAccessToken(storedAccessToken);
|
|
7739
|
+
setRefreshToken(storedRefreshToken);
|
|
7740
|
+
setIsAuthenticated(true);
|
|
7741
|
+
setIsReady(true);
|
|
7742
|
+
}
|
|
7743
|
+
else if (refreshValid) {
|
|
7744
|
+
// Access token expired but refresh still valid → refresh immediately
|
|
7745
|
+
setAccessToken(storedAccessToken);
|
|
7746
|
+
setRefreshToken(storedRefreshToken);
|
|
7747
|
+
(async () => {
|
|
7748
|
+
try {
|
|
7749
|
+
await refreshTokens();
|
|
7750
|
+
}
|
|
7751
|
+
catch (_a) {
|
|
7752
|
+
// Refresh failed → clear tokens
|
|
7753
|
+
setAccessToken(null);
|
|
7754
|
+
setRefreshToken(null);
|
|
7755
|
+
setIsAuthenticated(false);
|
|
7756
|
+
}
|
|
7757
|
+
setIsReady(true);
|
|
7758
|
+
})();
|
|
7759
|
+
return; // setIsReady will be called in the async block
|
|
7760
|
+
}
|
|
7761
|
+
else {
|
|
7762
|
+
// Refresh expired or no tokens → clear
|
|
7763
|
+
setAccessToken(null);
|
|
7764
|
+
setRefreshToken(null);
|
|
7765
|
+
setIsAuthenticated(false);
|
|
7766
|
+
setIsReady(true);
|
|
7767
|
+
}
|
|
7768
|
+
}
|
|
7769
|
+
else {
|
|
7770
|
+
setIsReady(true);
|
|
7771
|
+
}
|
|
7772
|
+
}, [address]);
|
|
7595
7773
|
useEffect(() => {
|
|
7596
7774
|
const cleanup = addAuthInterceptors({
|
|
7597
7775
|
apiBaseUrl,
|
|
7598
7776
|
getAccessToken: () => {
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7777
|
+
if (typeof window === 'undefined')
|
|
7778
|
+
return null;
|
|
7779
|
+
// Read from Zustand state as single source of truth
|
|
7780
|
+
return useUserData.getState().accessToken;
|
|
7602
7781
|
},
|
|
7603
7782
|
refreshTokens: async () => {
|
|
7604
7783
|
const data = await refreshTokens();
|
|
@@ -7612,6 +7791,55 @@ function useAuth() {
|
|
|
7612
7791
|
cleanup();
|
|
7613
7792
|
};
|
|
7614
7793
|
}, [apiBaseUrl]);
|
|
7794
|
+
// Proactive refresh effect: refresh when app regains focus or timer fires
|
|
7795
|
+
useEffect(() => {
|
|
7796
|
+
if (typeof window === 'undefined' || !address || !refreshToken$1)
|
|
7797
|
+
return;
|
|
7798
|
+
const refreshIfNeeded = async () => {
|
|
7799
|
+
// Prevent concurrent refresh attempts
|
|
7800
|
+
if (isRefreshingRef.current)
|
|
7801
|
+
return;
|
|
7802
|
+
// Read fresh expiration values from localStorage (not stale closure)
|
|
7803
|
+
const accessExpRaw = localStorage.getItem(`${address}_accessTokenExpiresAt`);
|
|
7804
|
+
const refreshExpRaw = localStorage.getItem(`${address}_refreshTokenExpiresAt`);
|
|
7805
|
+
const accessExp = accessExpRaw ? Number(accessExpRaw) : 0;
|
|
7806
|
+
const refreshExp = refreshExpRaw ? Number(refreshExpRaw) : 0;
|
|
7807
|
+
const now = nowMs();
|
|
7808
|
+
// If refresh token is already expired, do nothing
|
|
7809
|
+
if (refreshExp <= now)
|
|
7810
|
+
return;
|
|
7811
|
+
// If access token is within buffer window, refresh
|
|
7812
|
+
if (accessExp - now <= ACCESS_TOKEN_BUFFER_MS) {
|
|
7813
|
+
isRefreshingRef.current = true;
|
|
7814
|
+
try {
|
|
7815
|
+
await refreshTokens();
|
|
7816
|
+
}
|
|
7817
|
+
catch (_a) {
|
|
7818
|
+
// Refresh failed, interceptor will handle logout on next API call
|
|
7819
|
+
}
|
|
7820
|
+
finally {
|
|
7821
|
+
isRefreshingRef.current = false;
|
|
7822
|
+
}
|
|
7823
|
+
}
|
|
7824
|
+
};
|
|
7825
|
+
const onVisibilityChange = () => {
|
|
7826
|
+
if (document.visibilityState === 'visible') {
|
|
7827
|
+
refreshIfNeeded();
|
|
7828
|
+
}
|
|
7829
|
+
};
|
|
7830
|
+
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
7831
|
+
// Schedule timer for (accessExp - buffer)
|
|
7832
|
+
const accessExpRaw = localStorage.getItem(`${address}_accessTokenExpiresAt`);
|
|
7833
|
+
const accessExp = accessExpRaw ? Number(accessExpRaw) : 0;
|
|
7834
|
+
const delay = Math.max(0, accessExp - nowMs() - ACCESS_TOKEN_BUFFER_MS);
|
|
7835
|
+
const timer = window.setTimeout(() => {
|
|
7836
|
+
refreshIfNeeded();
|
|
7837
|
+
}, delay);
|
|
7838
|
+
return () => {
|
|
7839
|
+
document.removeEventListener('visibilitychange', onVisibilityChange);
|
|
7840
|
+
clearTimeout(timer);
|
|
7841
|
+
};
|
|
7842
|
+
}, [address, refreshToken$1]);
|
|
7615
7843
|
async function getEip712(address) {
|
|
7616
7844
|
const { data } = await getEIP712Message(apiBaseUrl, address, clientId);
|
|
7617
7845
|
return data;
|
|
@@ -7619,17 +7847,21 @@ function useAuth() {
|
|
|
7619
7847
|
async function loginWithSignedMessage(address, signature, timestamp) {
|
|
7620
7848
|
try {
|
|
7621
7849
|
const { data } = await authenticate(apiBaseUrl, {
|
|
7622
|
-
method:
|
|
7850
|
+
method: 'eip712',
|
|
7623
7851
|
address,
|
|
7624
7852
|
clientId,
|
|
7625
7853
|
details: { signature, timestamp },
|
|
7626
7854
|
});
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7855
|
+
const accessTokenKey = `${address}_accessToken`;
|
|
7856
|
+
const refreshTokenKey = `${address}_refreshToken`;
|
|
7857
|
+
const accessTokenExpiresAtKey = `${address}_accessTokenExpiresAt`;
|
|
7858
|
+
const refreshTokenExpiresAtKey = `${address}_refreshTokenExpiresAt`;
|
|
7859
|
+
window.localStorage.setItem(accessTokenKey, data.accessToken);
|
|
7860
|
+
window.localStorage.setItem(refreshTokenKey, data.refreshToken);
|
|
7861
|
+
window.localStorage.setItem(accessTokenExpiresAtKey, String(calcExpiresAt(data.expiresIn)));
|
|
7862
|
+
window.localStorage.setItem(refreshTokenExpiresAtKey, String(nowMs() + REFRESH_TOKEN_TTL_MS));
|
|
7630
7863
|
setAccessToken(data.accessToken);
|
|
7631
7864
|
setRefreshToken(data.refreshToken);
|
|
7632
|
-
setAddress(address);
|
|
7633
7865
|
setIsAuthenticated(true);
|
|
7634
7866
|
}
|
|
7635
7867
|
catch (e) {
|
|
@@ -7644,12 +7876,16 @@ function useAuth() {
|
|
|
7644
7876
|
appId,
|
|
7645
7877
|
accessToken: privyAccessToken,
|
|
7646
7878
|
});
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7879
|
+
const accessTokenKey = `${address}_accessToken`;
|
|
7880
|
+
const refreshTokenKey = `${address}_refreshToken`;
|
|
7881
|
+
const accessTokenExpiresAtKey = `${address}_accessTokenExpiresAt`;
|
|
7882
|
+
const refreshTokenExpiresAtKey = `${address}_refreshTokenExpiresAt`;
|
|
7883
|
+
window.localStorage.setItem(accessTokenKey, data.accessToken);
|
|
7884
|
+
window.localStorage.setItem(refreshTokenKey, data.refreshToken);
|
|
7885
|
+
window.localStorage.setItem(accessTokenExpiresAtKey, String(calcExpiresAt(data.expiresIn)));
|
|
7886
|
+
window.localStorage.setItem(refreshTokenExpiresAtKey, String(nowMs() + REFRESH_TOKEN_TTL_MS));
|
|
7650
7887
|
setAccessToken(data.accessToken);
|
|
7651
7888
|
setRefreshToken(data.refreshToken);
|
|
7652
|
-
setAddress(address);
|
|
7653
7889
|
setIsAuthenticated(true);
|
|
7654
7890
|
}
|
|
7655
7891
|
catch (e) {
|
|
@@ -7657,35 +7893,61 @@ function useAuth() {
|
|
|
7657
7893
|
}
|
|
7658
7894
|
}
|
|
7659
7895
|
async function refreshTokens() {
|
|
7660
|
-
const
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7896
|
+
const currentAddress = address;
|
|
7897
|
+
const currentRefresh = refreshToken$1;
|
|
7898
|
+
if (!currentRefresh || !currentAddress)
|
|
7899
|
+
throw new Error('No refresh token');
|
|
7900
|
+
const { data } = await refreshToken(apiBaseUrl, currentRefresh);
|
|
7901
|
+
// Update tokens in localStorage
|
|
7902
|
+
const accessTokenKey = `${currentAddress}_accessToken`;
|
|
7903
|
+
const refreshTokenKey = `${currentAddress}_refreshToken`;
|
|
7904
|
+
const accessTokenExpiresAtKey = `${currentAddress}_accessTokenExpiresAt`;
|
|
7905
|
+
const refreshTokenExpiresAtKey = `${currentAddress}_refreshTokenExpiresAt`;
|
|
7906
|
+
window.localStorage.setItem(accessTokenKey, data.accessToken);
|
|
7907
|
+
window.localStorage.setItem(refreshTokenKey, data.refreshToken);
|
|
7908
|
+
window.localStorage.setItem(accessTokenExpiresAtKey, String(calcExpiresAt(data.expiresIn)));
|
|
7909
|
+
window.localStorage.setItem(refreshTokenExpiresAtKey, String(nowMs() + REFRESH_TOKEN_TTL_MS));
|
|
7666
7910
|
setAccessToken(data.accessToken);
|
|
7667
7911
|
setRefreshToken(data.refreshToken);
|
|
7668
7912
|
setIsAuthenticated(true);
|
|
7669
7913
|
return data;
|
|
7670
7914
|
}
|
|
7671
7915
|
async function logout$1() {
|
|
7672
|
-
const
|
|
7673
|
-
|
|
7916
|
+
const currentAddress = address;
|
|
7917
|
+
const currentRefresh = refreshToken$1;
|
|
7918
|
+
if (currentRefresh) {
|
|
7674
7919
|
try {
|
|
7675
|
-
await logout(apiBaseUrl,
|
|
7920
|
+
await logout(apiBaseUrl, currentRefresh);
|
|
7676
7921
|
}
|
|
7677
7922
|
catch (_a) {
|
|
7678
7923
|
/* ignore */
|
|
7679
7924
|
}
|
|
7680
7925
|
}
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7926
|
+
if (currentAddress) {
|
|
7927
|
+
const accessTokenKey = `${currentAddress}_accessToken`;
|
|
7928
|
+
const refreshTokenKey = `${currentAddress}_refreshToken`;
|
|
7929
|
+
const accessTokenExpiresAtKey = `${currentAddress}_accessTokenExpiresAt`;
|
|
7930
|
+
const refreshTokenExpiresAtKey = `${currentAddress}_refreshTokenExpiresAt`;
|
|
7931
|
+
window.localStorage.removeItem(accessTokenKey);
|
|
7932
|
+
window.localStorage.removeItem(refreshTokenKey);
|
|
7933
|
+
window.localStorage.removeItem(accessTokenExpiresAtKey);
|
|
7934
|
+
window.localStorage.removeItem(refreshTokenExpiresAtKey);
|
|
7935
|
+
}
|
|
7684
7936
|
setAccessToken(null);
|
|
7685
7937
|
setRefreshToken(null);
|
|
7686
7938
|
setAddress(null);
|
|
7687
7939
|
setIsAuthenticated(false);
|
|
7688
7940
|
}
|
|
7941
|
+
/**
|
|
7942
|
+
* Clear the current session without logging out from the API.
|
|
7943
|
+
* Useful when switching wallets to clear stale in-memory auth state.
|
|
7944
|
+
* Note: setAddress will clear user-specific data (spotState, etc.) when called
|
|
7945
|
+
*/
|
|
7946
|
+
function clearSession() {
|
|
7947
|
+
setAccessToken(null);
|
|
7948
|
+
setRefreshToken(null);
|
|
7949
|
+
setIsAuthenticated(false);
|
|
7950
|
+
}
|
|
7689
7951
|
return {
|
|
7690
7952
|
isReady,
|
|
7691
7953
|
isAuthenticated,
|
|
@@ -7697,6 +7959,8 @@ function useAuth() {
|
|
|
7697
7959
|
loginWithPrivyToken,
|
|
7698
7960
|
refreshTokens,
|
|
7699
7961
|
logout: logout$1,
|
|
7962
|
+
setAddress,
|
|
7963
|
+
clearSession,
|
|
7700
7964
|
};
|
|
7701
7965
|
}
|
|
7702
7966
|
|
|
@@ -8009,9 +8273,17 @@ const PearHyperliquidContext = createContext(undefined);
|
|
|
8009
8273
|
/**
|
|
8010
8274
|
* React Provider for PearHyperliquidClient
|
|
8011
8275
|
*/
|
|
8012
|
-
const PearHyperliquidProvider = ({ children, apiBaseUrl =
|
|
8276
|
+
const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearprotocol.io", clientId = "PEARPROTOCOLUI", wsUrl = "wss://hl-ui.pearprotocol.io/ws", }) => {
|
|
8013
8277
|
const address = useUserData((s) => s.address);
|
|
8014
|
-
const
|
|
8278
|
+
const clearHyperliquidUserData = useHyperliquidData((state) => state.clearUserData);
|
|
8279
|
+
const prevAddressRef = useRef(null);
|
|
8280
|
+
// Clear user-specific data when address changes
|
|
8281
|
+
useEffect(() => {
|
|
8282
|
+
if (prevAddressRef.current !== null && prevAddressRef.current !== address) {
|
|
8283
|
+
clearHyperliquidUserData();
|
|
8284
|
+
}
|
|
8285
|
+
prevAddressRef.current = address;
|
|
8286
|
+
}, [address, clearHyperliquidUserData]);
|
|
8015
8287
|
const perpMetasByDex = useHyperliquidData((state) => state.perpMetasByDex);
|
|
8016
8288
|
const setPerpDexs = useHyperliquidData((state) => state.setPerpDexs);
|
|
8017
8289
|
const setPerpMetasByDex = useHyperliquidData((state) => state.setPerpMetasByDex);
|
|
@@ -8042,20 +8314,20 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
|
|
|
8042
8314
|
perpMetas.forEach((item, perpIndex) => {
|
|
8043
8315
|
var _a, _b;
|
|
8044
8316
|
const dexName = perpIndex === 0
|
|
8045
|
-
?
|
|
8317
|
+
? "HYPERLIQUID"
|
|
8046
8318
|
: ((_b = (_a = perpDexs[perpIndex]) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : `DEX_${perpIndex}`);
|
|
8047
8319
|
var collateralToken;
|
|
8048
8320
|
if (item.collateralToken === 360) {
|
|
8049
|
-
collateralToken =
|
|
8321
|
+
collateralToken = "USDH";
|
|
8050
8322
|
}
|
|
8051
8323
|
if (item.collateralToken === 0) {
|
|
8052
|
-
collateralToken =
|
|
8324
|
+
collateralToken = "USDC";
|
|
8053
8325
|
}
|
|
8054
8326
|
if (item.collateralToken === 235) {
|
|
8055
|
-
collateralToken =
|
|
8327
|
+
collateralToken = "USDE";
|
|
8056
8328
|
}
|
|
8057
8329
|
if (item.collateralToken === 268) {
|
|
8058
|
-
collateralToken =
|
|
8330
|
+
collateralToken = "USDT0";
|
|
8059
8331
|
}
|
|
8060
8332
|
const universeAssets = item.universe.map((asset) => ({
|
|
8061
8333
|
...asset,
|
|
@@ -8083,8 +8355,6 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
|
|
|
8083
8355
|
}), [
|
|
8084
8356
|
apiBaseUrl,
|
|
8085
8357
|
wsUrl,
|
|
8086
|
-
address,
|
|
8087
|
-
setAddress,
|
|
8088
8358
|
isConnected,
|
|
8089
8359
|
lastError,
|
|
8090
8360
|
nativeIsConnected,
|
|
@@ -8099,7 +8369,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
|
|
|
8099
8369
|
function usePearHyperliquid() {
|
|
8100
8370
|
const ctx = useContext(PearHyperliquidContext);
|
|
8101
8371
|
if (!ctx)
|
|
8102
|
-
throw new Error(
|
|
8372
|
+
throw new Error("usePearHyperliquid must be used within a PearHyperliquidProvider");
|
|
8103
8373
|
return ctx;
|
|
8104
8374
|
}
|
|
8105
8375
|
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ActiveAssetData, CandleChartData, CandleData, ClearinghouseState, UniverseAsset, WebData3AssetCtx, WsAllMidsData, PerpDexsResponse } from
|
|
2
|
-
import { TokenMetadataBySymbol } from
|
|
1
|
+
import { ActiveAssetData, CandleChartData, CandleData, ClearinghouseState, UniverseAsset, WebData3AssetCtx, WsAllMidsData, PerpDexsResponse } from "../types";
|
|
2
|
+
import { TokenMetadataBySymbol } from "../utils/token-metadata-extractor";
|
|
3
3
|
interface HyperliquidDataState {
|
|
4
4
|
allMids: WsAllMidsData | null;
|
|
5
5
|
activeAssetData: Record<string, ActiveAssetData> | null;
|
|
@@ -28,6 +28,7 @@ interface HyperliquidDataState {
|
|
|
28
28
|
setPerpDexs: (value: PerpDexsResponse | null) => void;
|
|
29
29
|
setPerpMetasByDex: (value: Map<string, UniverseAsset[]> | null) => void;
|
|
30
30
|
setAssetContextsByDex: (value: Map<string, WebData3AssetCtx[]> | null) => void;
|
|
31
|
+
clearUserData: () => void;
|
|
31
32
|
}
|
|
32
33
|
export declare const useHyperliquidData: import("zustand").UseBoundStore<import("zustand").StoreApi<HyperliquidDataState>>;
|
|
33
34
|
export {};
|
|
@@ -3,6 +3,7 @@ interface UserDataState {
|
|
|
3
3
|
accessToken: string | null;
|
|
4
4
|
refreshToken: string | null;
|
|
5
5
|
isAuthenticated: boolean;
|
|
6
|
+
isReady: boolean;
|
|
6
7
|
address: string | null;
|
|
7
8
|
tradeHistories: TradeHistoryDataDto[] | null;
|
|
8
9
|
rawOpenPositions: RawPositionDto[] | null;
|
|
@@ -26,6 +27,7 @@ interface UserDataState {
|
|
|
26
27
|
setNotifications: (value: NotificationDto[] | null) => void;
|
|
27
28
|
setSpotState: (value: SpotState | null) => void;
|
|
28
29
|
setUserAbstractionMode: (value: UserAbstraction | null) => void;
|
|
30
|
+
setIsReady: (value: boolean) => void;
|
|
29
31
|
clean: () => void;
|
|
30
32
|
}
|
|
31
33
|
export declare const useUserData: import("zustand").UseBoundStore<import("zustand").StoreApi<UserDataState>>;
|
package/dist/utils/http.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AxiosInstance } from
|
|
2
|
-
import { ApiErrorResponse } from
|
|
1
|
+
import type { AxiosInstance } from "axios";
|
|
2
|
+
import { ApiErrorResponse } from "../types";
|
|
3
3
|
export declare function toApiError(error: unknown): ApiErrorResponse;
|
|
4
4
|
export declare function joinUrl(baseUrl: string, path: string): string;
|
|
5
5
|
/**
|