@pear-protocol/hyperliquid-sdk 0.1.15 → 0.1.16

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.
@@ -1,4 +1,4 @@
1
- import type { ApiResponse, GetEIP712MessageResponse, AuthenticateRequest, AuthenticateResponse, RefreshTokenResponse, LogoutResponse } from '../types';
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
  /**
@@ -1,4 +1,4 @@
1
- import type { GetEIP712MessageResponse } from "../types";
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 clearSession: () => void;
14
+ readonly setAddress: (address: string | null) => void;
13
15
  };
package/dist/index.d.ts CHANGED
@@ -1431,6 +1431,8 @@ declare function useAuth(): {
1431
1431
  readonly loginWithPrivyToken: (address: string, appId: string, privyAccessToken: string) => Promise<void>;
1432
1432
  readonly refreshTokens: () => Promise<RefreshTokenResponse>;
1433
1433
  readonly logout: () => Promise<void>;
1434
+ readonly clearSession: () => void;
1435
+ readonly setAddress: (address: string | null) => void;
1434
1436
  };
1435
1437
 
1436
1438
  interface MarginRequiredPerCollateral {
package/dist/index.js CHANGED
@@ -65,18 +65,27 @@ 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
- setAddress: (address) => set(() => {
73
- if (typeof window !== 'undefined') {
74
- if (address) {
75
- window.localStorage.setItem('address', address);
76
- }
77
- else {
78
- window.localStorage.removeItem('address');
79
- }
73
+ setIsReady: (value) => set({ isReady: value }),
74
+ setAddress: (address) => set((state) => {
75
+ const addressChanged = state.address !== null && state.address !== address;
76
+ if (addressChanged) {
77
+ return {
78
+ address,
79
+ spotState: null,
80
+ tradeHistories: null,
81
+ rawOpenPositions: null,
82
+ openOrders: null,
83
+ accountSummary: null,
84
+ twapDetails: null,
85
+ notifications: null,
86
+ userExtraAgents: null,
87
+ userAbstractionMode: null,
88
+ };
80
89
  }
81
90
  return { address };
82
91
  }),
@@ -88,10 +97,11 @@ const useUserData = create((set) => ({
88
97
  setNotifications: (value) => set({ notifications: value }),
89
98
  setSpotState: (value) => set({ spotState: value }),
90
99
  clean: () => set({
91
- accessToken: null,
92
- refreshToken: null,
93
- isAuthenticated: false,
94
- address: null,
100
+ // accessToken: null,
101
+ // refreshToken: null,
102
+ // isAuthenticated: false,
103
+ // isReady: false,
104
+ // address: null,
95
105
  tradeHistories: null,
96
106
  rawOpenPositions: null,
97
107
  openOrders: null,
@@ -551,7 +561,7 @@ const useHyperliquidData = create((set) => ({
551
561
  tokenMetadata: refreshTokenMetadata(state, { allMids: value }),
552
562
  })),
553
563
  setActiveAssetData: (value) => set((state) => {
554
- const activeAssetData = typeof value === 'function' ? value(state.activeAssetData) : value;
564
+ const activeAssetData = typeof value === "function" ? value(state.activeAssetData) : value;
555
565
  return {
556
566
  activeAssetData,
557
567
  tokenMetadata: refreshTokenMetadata(state, { activeAssetData }),
@@ -591,7 +601,10 @@ const useHyperliquidData = create((set) => ({
591
601
  setCandleData: (value) => set({ candleData: value }),
592
602
  upsertActiveAssetData: (key, value) => set((state) => {
593
603
  var _a;
594
- const activeAssetData = { ...((_a = state.activeAssetData) !== null && _a !== void 0 ? _a : {}), [key]: value };
604
+ const activeAssetData = {
605
+ ...((_a = state.activeAssetData) !== null && _a !== void 0 ? _a : {}),
606
+ [key]: value,
607
+ };
595
608
  return {
596
609
  activeAssetData,
597
610
  tokenMetadata: refreshTokenMetadata(state, { activeAssetData }, { symbols: [key] }),
@@ -626,6 +639,10 @@ const useHyperliquidData = create((set) => ({
626
639
  perpMetasByDex: state.perpMetasByDex,
627
640
  }),
628
641
  })),
642
+ clearUserData: () => set({
643
+ aggregatedClearingHouseState: null,
644
+ rawClearinghouseStates: null,
645
+ }),
629
646
  }));
630
647
 
631
648
  /**
@@ -917,9 +934,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
917
934
  try {
918
935
  const message = JSON.parse(event.data);
919
936
  // Handle subscription responses
920
- if ('success' in message || 'error' in message) {
937
+ if ("success" in message || "error" in message) {
921
938
  if (message.error) {
922
- console.error('[HyperLiquid WS] Subscription error:', message.error);
939
+ console.error("[HyperLiquid WS] Subscription error:", message.error);
923
940
  setLastError(message.error);
924
941
  }
925
942
  else {
@@ -928,44 +945,44 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
928
945
  return;
929
946
  }
930
947
  // Handle channel data messages
931
- if ('channel' in message && 'data' in message) {
948
+ if ("channel" in message && "data" in message) {
932
949
  const response = message;
933
950
  switch (response.channel) {
934
- case 'userFills':
951
+ case "userFills":
935
952
  {
936
953
  const maybePromise = (_a = onUserFillsRef.current) === null || _a === void 0 ? void 0 : _a.call(onUserFillsRef);
937
954
  if (maybePromise instanceof Promise) {
938
- maybePromise.catch((err) => console.error('[HyperLiquid WS] userFills callback error', err));
955
+ maybePromise.catch((err) => console.error("[HyperLiquid WS] userFills callback error", err));
939
956
  }
940
957
  }
941
958
  break;
942
- case 'webData3':
959
+ case "webData3":
943
960
  const webData3 = response.data;
944
961
  // finalAssetContexts now sourced from allDexsAssetCtxs channel
945
962
  const finalAtOICaps = webData3.perpDexStates.flatMap((dex) => dex.perpsAtOpenInterestCap);
946
963
  setFinalAtOICaps(finalAtOICaps);
947
964
  setUserAbstractionMode(webData3.userState.abstraction || null);
948
965
  break;
949
- case 'allDexsAssetCtxs':
966
+ case "allDexsAssetCtxs":
950
967
  {
951
968
  const data = response.data;
952
969
  // Store by DEX name, mapping '' to 'HYPERLIQUID'
953
970
  const assetContextsByDex = new Map();
954
971
  data.ctxs.forEach(([dexKey, ctxs]) => {
955
- const dexName = dexKey === '' ? 'HYPERLIQUID' : dexKey;
972
+ const dexName = dexKey === "" ? "HYPERLIQUID" : dexKey;
956
973
  assetContextsByDex.set(dexName, ctxs || []);
957
974
  });
958
975
  setAssetContextsByDex(assetContextsByDex);
959
976
  }
960
977
  break;
961
- case 'allDexsClearinghouseState':
978
+ case "allDexsClearinghouseState":
962
979
  {
963
980
  const data = response.data;
964
981
  const states = (data.clearinghouseStates || [])
965
982
  .map(([, s]) => s)
966
983
  .filter(Boolean);
967
- const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || '0') || 0), 0);
968
- const toStr = (n) => Number.isFinite(n) ? n.toString() : '0';
984
+ const sum = (values) => values.reduce((acc, v) => acc + (parseFloat(v || "0") || 0), 0);
985
+ const toStr = (n) => Number.isFinite(n) ? n.toString() : "0";
969
986
  const assetPositions = states.flatMap((s) => s.assetPositions || []);
970
987
  const crossMaintenanceMarginUsed = toStr(sum(states.map((s) => s.crossMaintenanceMarginUsed)));
971
988
  const crossMarginSummary = {
@@ -995,26 +1012,26 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
995
1012
  setClearinghouseStateReceived(true);
996
1013
  }
997
1014
  break;
998
- case 'allMids':
1015
+ case "allMids":
999
1016
  {
1000
1017
  const data = response.data;
1001
1018
  setAllMids(data);
1002
1019
  }
1003
1020
  break;
1004
- case 'activeAssetData':
1021
+ case "activeAssetData":
1005
1022
  {
1006
1023
  const assetData = response.data;
1007
1024
  upsertActiveAssetData(assetData.coin, assetData);
1008
1025
  }
1009
1026
  break;
1010
- case 'candle':
1027
+ case "candle":
1011
1028
  {
1012
1029
  const candleDataItem = response.data;
1013
- const symbol = candleDataItem.s || '';
1030
+ const symbol = candleDataItem.s || "";
1014
1031
  addCandleData(symbol, candleDataItem);
1015
1032
  }
1016
1033
  break;
1017
- case 'spotState':
1034
+ case "spotState":
1018
1035
  {
1019
1036
  const spotStateData = response.data;
1020
1037
  if (spotStateData === null || spotStateData === void 0 ? void 0 : spotStateData.spotState) {
@@ -1029,7 +1046,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1029
1046
  }
1030
1047
  catch (error) {
1031
1048
  const errorMessage = `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`;
1032
- console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
1049
+ console.error("[HyperLiquid WS] Parse error:", errorMessage, "Raw message:", event.data);
1033
1050
  setLastError(errorMessage);
1034
1051
  }
1035
1052
  }, [
@@ -1043,7 +1060,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1043
1060
  setSpotState,
1044
1061
  ]);
1045
1062
  const connect = useCallback(() => {
1046
- console.log('[HyperLiquid WS] connect() called, enabled:', enabled);
1063
+ console.log("[HyperLiquid WS] connect() called, enabled:", enabled);
1047
1064
  if (!enabled)
1048
1065
  return;
1049
1066
  // Clear any pending reconnect timeout
@@ -1059,10 +1076,10 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1059
1076
  console.log('[HyperLiquid WS] connect() returning early - socket already exists, readyState:', wsRef.current.readyState);
1060
1077
  return;
1061
1078
  }
1062
- console.log('[HyperLiquid WS] Creating new WebSocket connection');
1079
+ console.log("[HyperLiquid WS] Creating new WebSocket connection");
1063
1080
  manualCloseRef.current = false;
1064
1081
  setReadyState(ReadyState.CONNECTING);
1065
- const ws = new WebSocket('wss://api.hyperliquid.xyz/ws');
1082
+ const ws = new WebSocket("wss://api.hyperliquid.xyz/ws");
1066
1083
  wsRef.current = ws;
1067
1084
  ws.onopen = () => {
1068
1085
  reconnectAttemptsRef.current = 0;
@@ -1071,8 +1088,8 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1071
1088
  };
1072
1089
  ws.onmessage = handleMessage;
1073
1090
  ws.onerror = (event) => {
1074
- console.error('[HyperLiquid WS] Connection error:', event);
1075
- setLastError('WebSocket error');
1091
+ console.error("[HyperLiquid WS] Connection error:", event);
1092
+ setLastError("WebSocket error");
1076
1093
  };
1077
1094
  ws.onclose = () => {
1078
1095
  setReadyState(ReadyState.CLOSED);
@@ -1164,7 +1181,7 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1164
1181
  if (isConnected) {
1165
1182
  // Send ping every 30 seconds
1166
1183
  pingIntervalRef.current = setInterval(() => {
1167
- sendJsonMessage({ method: 'ping' });
1184
+ sendJsonMessage({ method: "ping" });
1168
1185
  }, 30000);
1169
1186
  }
1170
1187
  else {
@@ -1182,27 +1199,27 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1182
1199
  }, [isConnected, sendJsonMessage]);
1183
1200
  // Handle address subscription changes
1184
1201
  useEffect(() => {
1185
- const DEFAULT_ADDRESS = '0x0000000000000000000000000000000000000000';
1202
+ const DEFAULT_ADDRESS = "0x0000000000000000000000000000000000000000";
1186
1203
  const userAddress = (address || DEFAULT_ADDRESS).toLowerCase();
1187
1204
  const normalizedSubscribedAddress = (subscribedAddress === null || subscribedAddress === void 0 ? void 0 : subscribedAddress.toLowerCase()) || null;
1188
- console.log('[HyperLiquid WS] Address subscription effect running');
1189
- console.log('[HyperLiquid WS] address:', address, 'userAddress:', userAddress, 'subscribedAddress:', subscribedAddress, 'normalizedSubscribedAddress:', normalizedSubscribedAddress);
1190
- console.log('[HyperLiquid WS] isConnected:', isConnected);
1205
+ console.log("[HyperLiquid WS] Address subscription effect running");
1206
+ console.log("[HyperLiquid WS] address:", address, "userAddress:", userAddress, "subscribedAddress:", subscribedAddress, "normalizedSubscribedAddress:", normalizedSubscribedAddress);
1207
+ console.log("[HyperLiquid WS] isConnected:", isConnected);
1191
1208
  if (normalizedSubscribedAddress === userAddress) {
1192
- console.log('[HyperLiquid WS] Address unchanged, skipping subscription update');
1209
+ console.log("[HyperLiquid WS] Address unchanged, skipping subscription update");
1193
1210
  return;
1194
1211
  }
1195
1212
  if (!isConnected) {
1196
- console.log('[HyperLiquid WS] Not connected, skipping subscription update');
1213
+ console.log("[HyperLiquid WS] Not connected, skipping subscription update");
1197
1214
  return;
1198
1215
  }
1199
1216
  // Unsubscribe from previous address if exists
1200
1217
  if (subscribedAddress) {
1201
- console.log('[HyperLiquid WS] Unsubscribing from previous address:', subscribedAddress);
1218
+ console.log("[HyperLiquid WS] Unsubscribing from previous address:", subscribedAddress);
1202
1219
  const unsubscribeMessage = {
1203
- method: 'unsubscribe',
1220
+ method: "unsubscribe",
1204
1221
  subscription: {
1205
- type: 'webData3',
1222
+ type: "webData3",
1206
1223
  user: subscribedAddress,
1207
1224
  },
1208
1225
  };
@@ -1210,54 +1227,54 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1210
1227
  // Unsubscribe from spotState for previous address
1211
1228
  if (subscribedAddress !== DEFAULT_ADDRESS) {
1212
1229
  const unsubscribeSpotState = {
1213
- method: 'unsubscribe',
1230
+ method: "unsubscribe",
1214
1231
  subscription: {
1215
- type: 'spotState',
1232
+ type: "spotState",
1216
1233
  user: subscribedAddress,
1217
1234
  },
1218
1235
  };
1219
1236
  sendJsonMessage(unsubscribeSpotState);
1220
1237
  }
1221
1238
  const unsubscribeAllDexsClearinghouseState = {
1222
- method: 'unsubscribe',
1239
+ method: "unsubscribe",
1223
1240
  subscription: {
1224
- type: 'allDexsClearinghouseState',
1241
+ type: "allDexsClearinghouseState",
1225
1242
  user: subscribedAddress,
1226
1243
  },
1227
1244
  };
1228
1245
  sendJsonMessage(unsubscribeAllDexsClearinghouseState);
1229
1246
  const unsubscribeUserFills = {
1230
- method: 'unsubscribe',
1247
+ method: "unsubscribe",
1231
1248
  subscription: {
1232
- type: 'userFills',
1249
+ type: "userFills",
1233
1250
  user: subscribedAddress,
1234
1251
  },
1235
1252
  };
1236
1253
  sendJsonMessage(unsubscribeUserFills);
1237
1254
  }
1238
1255
  const subscribeWebData3 = {
1239
- method: 'subscribe',
1256
+ method: "subscribe",
1240
1257
  subscription: {
1241
- type: 'webData3',
1258
+ type: "webData3",
1242
1259
  user: userAddress,
1243
1260
  },
1244
1261
  };
1245
1262
  // Subscribe to allMids
1246
1263
  const subscribeAllMids = {
1247
- method: 'subscribe',
1264
+ method: "subscribe",
1248
1265
  subscription: {
1249
- type: 'allMids',
1250
- dex: 'ALL_DEXS',
1266
+ type: "allMids",
1267
+ dex: "ALL_DEXS",
1251
1268
  },
1252
1269
  };
1253
1270
  // Subscribe to allDexsAssetCtxs (no payload params, global feed)
1254
1271
  const subscribeAllDexsAssetCtxs = {
1255
- method: 'subscribe',
1272
+ method: "subscribe",
1256
1273
  subscription: {
1257
- type: 'allDexsAssetCtxs',
1274
+ type: "allDexsAssetCtxs",
1258
1275
  },
1259
1276
  };
1260
- console.log('[HyperLiquid WS] Subscribing to new address:', userAddress);
1277
+ console.log("[HyperLiquid WS] Subscribing to new address:", userAddress);
1261
1278
  sendJsonMessage(subscribeWebData3);
1262
1279
  sendJsonMessage(subscribeAllMids);
1263
1280
  sendJsonMessage(subscribeAllDexsAssetCtxs);
@@ -1265,9 +1282,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1265
1282
  // Only subscribe if we have a real user address (not the default)
1266
1283
  if (userAddress !== DEFAULT_ADDRESS.toLowerCase()) {
1267
1284
  const subscribeSpotState = {
1268
- method: 'subscribe',
1285
+ method: "subscribe",
1269
1286
  subscription: {
1270
- type: 'spotState',
1287
+ type: "spotState",
1271
1288
  user: userAddress,
1272
1289
  },
1273
1290
  };
@@ -1277,9 +1294,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1277
1294
  // Only subscribe if we have a real user address (not the default)
1278
1295
  if (userAddress !== DEFAULT_ADDRESS.toLowerCase()) {
1279
1296
  const subscribeAllDexsClearinghouseState = {
1280
- method: 'subscribe',
1297
+ method: "subscribe",
1281
1298
  subscription: {
1282
- type: 'allDexsClearinghouseState',
1299
+ type: "allDexsClearinghouseState",
1283
1300
  user: userAddress,
1284
1301
  },
1285
1302
  };
@@ -1313,9 +1330,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1313
1330
  !userSummary)
1314
1331
  return;
1315
1332
  const subscribeUserFills = {
1316
- method: 'subscribe',
1333
+ method: "subscribe",
1317
1334
  subscription: {
1318
- type: 'userFills',
1335
+ type: "userFills",
1319
1336
  user: subscribedAddress,
1320
1337
  },
1321
1338
  };
@@ -1337,9 +1354,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1337
1354
  // Unsubscribe from tokens no longer in the list
1338
1355
  tokensToUnsubscribe.forEach((token) => {
1339
1356
  const unsubscribeMessage = {
1340
- method: 'unsubscribe',
1357
+ method: "unsubscribe",
1341
1358
  subscription: {
1342
- type: 'activeAssetData',
1359
+ type: "activeAssetData",
1343
1360
  user: address,
1344
1361
  coin: token,
1345
1362
  },
@@ -1349,9 +1366,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1349
1366
  // Subscribe to new tokens
1350
1367
  tokensToSubscribe.forEach((token) => {
1351
1368
  const subscribeMessage = {
1352
- method: 'subscribe',
1369
+ method: "subscribe",
1353
1370
  subscription: {
1354
- type: 'activeAssetData',
1371
+ type: "activeAssetData",
1355
1372
  user: address,
1356
1373
  coin: token,
1357
1374
  },
@@ -1380,9 +1397,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1380
1397
  if (prevInterval && prevInterval !== candleInterval) {
1381
1398
  subscribedCandleTokens.forEach((token) => {
1382
1399
  const unsubscribeMessage = {
1383
- method: 'unsubscribe',
1400
+ method: "unsubscribe",
1384
1401
  subscription: {
1385
- type: 'candle',
1402
+ type: "candle",
1386
1403
  coin: token,
1387
1404
  interval: prevInterval,
1388
1405
  },
@@ -1397,9 +1414,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1397
1414
  // Unsubscribe from tokens no longer in the list
1398
1415
  tokensToUnsubscribe.forEach((token) => {
1399
1416
  const unsubscribeMessage = {
1400
- method: 'unsubscribe',
1417
+ method: "unsubscribe",
1401
1418
  subscription: {
1402
- type: 'candle',
1419
+ type: "candle",
1403
1420
  coin: token,
1404
1421
  interval: candleInterval,
1405
1422
  },
@@ -1409,9 +1426,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
1409
1426
  // Subscribe to new tokens
1410
1427
  tokensToSubscribe.forEach((token) => {
1411
1428
  const subscribeMessage = {
1412
- method: 'subscribe',
1429
+ method: "subscribe",
1413
1430
  subscription: {
1414
- type: 'candle',
1431
+ type: "candle",
1415
1432
  coin: token,
1416
1433
  interval: candleInterval,
1417
1434
  },
@@ -5942,10 +5959,10 @@ function toApiError(error) {
5942
5959
  var _a;
5943
5960
  const axiosError = error;
5944
5961
  const payload = (axiosError && axiosError.response ? axiosError.response.data : undefined);
5945
- const message = typeof payload === 'object' && payload && 'message' in payload
5962
+ const message = typeof payload === "object" && payload && "message" in payload
5946
5963
  ? String(payload.message)
5947
- : (axiosError === null || axiosError === void 0 ? void 0 : axiosError.message) || 'Request failed';
5948
- const errField = typeof payload === 'object' && payload && 'error' in payload
5964
+ : (axiosError === null || axiosError === void 0 ? void 0 : axiosError.message) || "Request failed";
5965
+ const errField = typeof payload === "object" && payload && "error" in payload
5949
5966
  ? String(payload.error)
5950
5967
  : undefined;
5951
5968
  return {
@@ -5955,8 +5972,8 @@ function toApiError(error) {
5955
5972
  };
5956
5973
  }
5957
5974
  function joinUrl(baseUrl, path) {
5958
- const cleanBase = baseUrl.replace(/\/$/, '');
5959
- const cleanPath = path.startsWith('/') ? path : `/${path}`;
5975
+ const cleanBase = baseUrl.replace(/\/$/, "");
5976
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
5960
5977
  return `${cleanBase}${cleanPath}`;
5961
5978
  }
5962
5979
  /**
@@ -5985,7 +6002,7 @@ function addAuthInterceptors(params) {
5985
6002
  pendingRequests = [];
5986
6003
  }
5987
6004
  const isOurApiUrl = (url) => Boolean(url && url.startsWith(apiBaseUrl));
5988
- const isRefreshUrl = (url) => Boolean(url && url.startsWith(joinUrl(apiBaseUrl, '/auth/refresh')));
6005
+ const isRefreshUrl = (url) => Boolean(url && url.startsWith(joinUrl(apiBaseUrl, "/auth/refresh")));
5989
6006
  const reqId = apiClient.interceptors.request.use((config) => {
5990
6007
  var _a;
5991
6008
  try {
@@ -5993,11 +6010,12 @@ function addAuthInterceptors(params) {
5993
6010
  const token = getAccessToken();
5994
6011
  if (token) {
5995
6012
  config.headers = (_a = config.headers) !== null && _a !== void 0 ? _a : {};
5996
- (config.headers)['Authorization'] = `Bearer ${token}`;
6013
+ config.headers["Authorization"] = `Bearer ${token}`;
5997
6014
  }
5998
6015
  }
5999
6016
  }
6000
- catch (_b) {
6017
+ catch (err) {
6018
+ console.error("[Auth Interceptor] Request interceptor error:", err);
6001
6019
  }
6002
6020
  return config;
6003
6021
  });
@@ -6008,22 +6026,36 @@ function addAuthInterceptors(params) {
6008
6026
  const url = originalRequest === null || originalRequest === void 0 ? void 0 : originalRequest.url;
6009
6027
  // If not our API or not 401, just reject
6010
6028
  if (!status || status !== 401 || !isOurApiUrl(url)) {
6029
+ if (status === 401) {
6030
+ console.warn("[Auth Interceptor] 401 received but URL check failed:", {
6031
+ url,
6032
+ apiBaseUrl,
6033
+ isOurApiUrl: isOurApiUrl(url),
6034
+ });
6035
+ }
6011
6036
  return Promise.reject(error);
6012
6037
  }
6038
+ console.log("[Auth Interceptor] 401 detected, attempting token refresh for URL:", url);
6013
6039
  // If the 401 is from refresh endpoint itself -> force logout
6014
6040
  if (isRefreshUrl(url)) {
6041
+ console.warn("[Auth Interceptor] Refresh endpoint returned 401, logging out");
6015
6042
  try {
6016
6043
  await logout();
6017
6044
  }
6018
- catch (_d) { }
6045
+ catch (err) {
6046
+ console.error("[Auth Interceptor] Logout failed:", err);
6047
+ }
6019
6048
  return Promise.reject(error);
6020
6049
  }
6021
6050
  // Prevent infinite loop
6022
6051
  if (originalRequest && originalRequest._retry) {
6052
+ console.warn("[Auth Interceptor] Request already retried, logging out");
6023
6053
  try {
6024
6054
  await logout();
6025
6055
  }
6026
- catch (_e) { }
6056
+ catch (err) {
6057
+ console.error("[Auth Interceptor] Logout failed:", err);
6058
+ }
6027
6059
  return Promise.reject(error);
6028
6060
  }
6029
6061
  // Mark so we don't retry twice
@@ -6037,31 +6069,45 @@ function addAuthInterceptors(params) {
6037
6069
  if (!newToken || !originalRequest)
6038
6070
  return reject(error);
6039
6071
  originalRequest.headers = (_a = originalRequest.headers) !== null && _a !== void 0 ? _a : {};
6040
- originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
6072
+ originalRequest.headers["Authorization"] =
6073
+ `Bearer ${newToken}`;
6041
6074
  resolve(apiClient.request(originalRequest));
6042
6075
  });
6043
6076
  });
6044
6077
  }
6045
6078
  isRefreshing = true;
6046
6079
  try {
6080
+ console.log("[Auth Interceptor] Refreshing tokens...");
6047
6081
  const refreshed = await refreshTokens();
6048
- const newAccessToken = (_b = (refreshed && (refreshed.accessToken || ((_a = refreshed.data) === null || _a === void 0 ? void 0 : _a.accessToken)))) !== null && _b !== void 0 ? _b : null;
6082
+ const newAccessToken = (_b = (refreshed &&
6083
+ (refreshed.accessToken || ((_a = refreshed.data) === null || _a === void 0 ? void 0 : _a.accessToken)))) !== null && _b !== void 0 ? _b : null;
6084
+ if (!newAccessToken) {
6085
+ console.error("[Auth Interceptor] Token refresh succeeded but no access token in response:", refreshed);
6086
+ }
6087
+ else {
6088
+ console.log("[Auth Interceptor] Token refresh successful");
6089
+ }
6049
6090
  resolvePendingRequests(newAccessToken);
6050
6091
  if (originalRequest) {
6051
6092
  originalRequest.headers = (_c = originalRequest.headers) !== null && _c !== void 0 ? _c : {};
6052
6093
  if (newAccessToken)
6053
- (originalRequest.headers)['Authorization'] = `Bearer ${newAccessToken}`;
6094
+ originalRequest.headers["Authorization"] =
6095
+ `Bearer ${newAccessToken}`;
6096
+ console.log("[Auth Interceptor] Retrying original request with new token");
6054
6097
  const resp = await apiClient.request(originalRequest);
6055
6098
  return resp;
6056
6099
  }
6057
6100
  return Promise.reject(error);
6058
6101
  }
6059
6102
  catch (refreshErr) {
6103
+ console.error("[Auth Interceptor] Token refresh failed:", refreshErr);
6060
6104
  resolvePendingRequests(null);
6061
6105
  try {
6062
6106
  await logout();
6063
6107
  }
6064
- catch (_f) { }
6108
+ catch (err) {
6109
+ console.error("[Auth Interceptor] Logout failed:", err);
6110
+ }
6065
6111
  return Promise.reject(refreshErr);
6066
6112
  }
6067
6113
  finally {
@@ -6072,11 +6118,15 @@ function addAuthInterceptors(params) {
6072
6118
  try {
6073
6119
  apiClient.interceptors.request.eject(reqId);
6074
6120
  }
6075
- catch (_a) { }
6121
+ catch (err) {
6122
+ console.error("[Auth Interceptor] Failed to eject request interceptor:", err);
6123
+ }
6076
6124
  try {
6077
6125
  apiClient.interceptors.response.eject(resId);
6078
6126
  }
6079
- catch (_b) { }
6127
+ catch (err) {
6128
+ console.error("[Auth Interceptor] Failed to eject response interceptor:", err);
6129
+ }
6080
6130
  };
6081
6131
  }
6082
6132
 
@@ -7593,20 +7643,34 @@ function usePortfolio() {
7593
7643
  }
7594
7644
 
7595
7645
  async function getEIP712Message(baseUrl, address, clientId) {
7596
- const url = joinUrl(baseUrl, '/auth/eip712-message');
7646
+ const url = joinUrl(baseUrl, "/auth/eip712-message");
7597
7647
  try {
7598
- const resp = await axios$1.get(url, { params: { address, clientId }, timeout: 30000 });
7599
- return { data: resp.data, status: resp.status, headers: resp.headers };
7648
+ const resp = await apiClient.get(url, {
7649
+ params: { address, clientId },
7650
+ timeout: 30000,
7651
+ });
7652
+ return {
7653
+ data: resp.data,
7654
+ status: resp.status,
7655
+ headers: resp.headers,
7656
+ };
7600
7657
  }
7601
7658
  catch (error) {
7602
7659
  throw toApiError(error);
7603
7660
  }
7604
7661
  }
7605
7662
  async function authenticate(baseUrl, body) {
7606
- const url = joinUrl(baseUrl, '/auth/login');
7663
+ const url = joinUrl(baseUrl, "/auth/login");
7607
7664
  try {
7608
- const resp = await axios$1.post(url, body, { headers: { 'Content-Type': 'application/json' }, timeout: 30000 });
7609
- return { data: resp.data, status: resp.status, headers: resp.headers };
7665
+ const resp = await apiClient.post(url, body, {
7666
+ headers: { "Content-Type": "application/json" },
7667
+ timeout: 30000,
7668
+ });
7669
+ return {
7670
+ data: resp.data,
7671
+ status: resp.status,
7672
+ headers: resp.headers,
7673
+ };
7610
7674
  }
7611
7675
  catch (error) {
7612
7676
  throw toApiError(error);
@@ -7617,7 +7681,7 @@ async function authenticate(baseUrl, body) {
7617
7681
  */
7618
7682
  async function authenticateWithPrivy(baseUrl, params) {
7619
7683
  const body = {
7620
- method: 'privy_access_token',
7684
+ method: "privy_access_token",
7621
7685
  address: params.address,
7622
7686
  clientId: params.clientId,
7623
7687
  details: { appId: params.appId, accessToken: params.accessToken },
@@ -7625,62 +7689,124 @@ async function authenticateWithPrivy(baseUrl, params) {
7625
7689
  return authenticate(baseUrl, body);
7626
7690
  }
7627
7691
  async function refreshToken(baseUrl, refreshTokenVal) {
7628
- const url = joinUrl(baseUrl, '/auth/refresh');
7692
+ const url = joinUrl(baseUrl, "/auth/refresh");
7629
7693
  try {
7630
- const resp = await axios$1.post(url, { refreshToken: refreshTokenVal }, { headers: { 'Content-Type': 'application/json' }, timeout: 30000 });
7631
- return { data: resp.data, status: resp.status, headers: resp.headers };
7694
+ const resp = await apiClient.post(url, { refreshToken: refreshTokenVal }, { headers: { "Content-Type": "application/json" }, timeout: 30000 });
7695
+ return {
7696
+ data: resp.data,
7697
+ status: resp.status,
7698
+ headers: resp.headers,
7699
+ };
7632
7700
  }
7633
7701
  catch (error) {
7634
7702
  throw toApiError(error);
7635
7703
  }
7636
7704
  }
7637
7705
  async function logout(baseUrl, refreshTokenVal) {
7638
- const url = joinUrl(baseUrl, '/auth/logout');
7706
+ const url = joinUrl(baseUrl, "/auth/logout");
7639
7707
  try {
7640
- const resp = await axios$1.post(url, { refreshToken: refreshTokenVal }, { headers: { 'Content-Type': 'application/json' }, timeout: 30000 });
7641
- return { data: resp.data, status: resp.status, headers: resp.headers };
7708
+ const resp = await apiClient.post(url, { refreshToken: refreshTokenVal }, { headers: { "Content-Type": "application/json" }, timeout: 30000 });
7709
+ return {
7710
+ data: resp.data,
7711
+ status: resp.status,
7712
+ headers: resp.headers,
7713
+ };
7642
7714
  }
7643
7715
  catch (error) {
7644
7716
  throw toApiError(error);
7645
7717
  }
7646
7718
  }
7647
7719
 
7720
+ // Token expiration constants
7721
+ const ACCESS_TOKEN_BUFFER_MS = 5 * 60 * 1000; // Refresh 5 min before expiry
7722
+ const REFRESH_TOKEN_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days fallback
7723
+ function nowMs() {
7724
+ return Date.now();
7725
+ }
7726
+ function calcExpiresAt(expiresInSeconds) {
7727
+ return nowMs() + expiresInSeconds * 1000;
7728
+ }
7648
7729
  function useAuth() {
7649
7730
  const context = useContext(PearHyperliquidContext);
7650
7731
  if (!context) {
7651
- throw new Error("usePortfolio must be used within a PearHyperliquidProvider");
7732
+ throw new Error('useAuth must be used within a PearHyperliquidProvider');
7652
7733
  }
7653
7734
  const { apiBaseUrl, clientId } = context;
7654
- const [isReady, setIsReady] = useState(false);
7655
7735
  const accessToken = useUserData((s) => s.accessToken);
7656
7736
  const refreshToken$1 = useUserData((s) => s.refreshToken);
7737
+ const isReady = useUserData((s) => s.isReady);
7738
+ const isAuthenticated = useUserData((s) => s.isAuthenticated);
7657
7739
  const setAccessToken = useUserData((s) => s.setAccessToken);
7658
7740
  const setRefreshToken = useUserData((s) => s.setRefreshToken);
7659
- const isAuthenticated = useUserData((s) => s.isAuthenticated);
7741
+ const setIsReady = useUserData((s) => s.setIsReady);
7660
7742
  const setIsAuthenticated = useUserData((s) => s.setIsAuthenticated);
7661
7743
  const address = useUserData((s) => s.address);
7662
7744
  const setAddress = useUserData((s) => s.setAddress);
7745
+ // Ref to prevent concurrent refresh attempts
7746
+ const isRefreshingRef = useRef(false);
7663
7747
  useEffect(() => {
7664
- if (typeof window == "undefined") {
7748
+ if (typeof window == 'undefined') {
7665
7749
  return;
7666
7750
  }
7667
- const access = localStorage.getItem("accessToken");
7668
- const refresh = localStorage.getItem("refreshToken");
7669
- const addr = localStorage.getItem("address");
7670
- setAccessToken(access);
7671
- setRefreshToken(refresh);
7672
- setAddress(addr);
7673
- const authed = Boolean(access && addr);
7674
- setIsAuthenticated(authed);
7675
- setIsReady(true);
7676
- }, [setAccessToken, setRefreshToken, setIsAuthenticated, setAddress]);
7751
+ if (address) {
7752
+ const accessTokenKey = `${address}_accessToken`;
7753
+ const refreshTokenKey = `${address}_refreshToken`;
7754
+ const accessTokenExpiresAtKey = `${address}_accessTokenExpiresAt`;
7755
+ const refreshTokenExpiresAtKey = `${address}_refreshTokenExpiresAt`;
7756
+ const storedAccessToken = localStorage.getItem(accessTokenKey);
7757
+ const storedRefreshToken = localStorage.getItem(refreshTokenKey);
7758
+ const accessExpRaw = localStorage.getItem(accessTokenExpiresAtKey);
7759
+ const refreshExpRaw = localStorage.getItem(refreshTokenExpiresAtKey);
7760
+ const accessExp = accessExpRaw ? Number(accessExpRaw) : 0;
7761
+ const refreshExp = refreshExpRaw ? Number(refreshExpRaw) : 0;
7762
+ const now = nowMs();
7763
+ const accessValid = !!storedAccessToken && accessExp > now;
7764
+ const refreshValid = !!storedRefreshToken && refreshExp > now;
7765
+ if (accessValid && refreshValid) {
7766
+ // Both tokens are valid
7767
+ setAccessToken(storedAccessToken);
7768
+ setRefreshToken(storedRefreshToken);
7769
+ setIsAuthenticated(true);
7770
+ setIsReady(true);
7771
+ }
7772
+ else if (refreshValid) {
7773
+ // Access token expired but refresh still valid → refresh immediately
7774
+ setAccessToken(storedAccessToken);
7775
+ setRefreshToken(storedRefreshToken);
7776
+ (async () => {
7777
+ try {
7778
+ await refreshTokens();
7779
+ }
7780
+ catch (_a) {
7781
+ // Refresh failed → clear tokens
7782
+ setAccessToken(null);
7783
+ setRefreshToken(null);
7784
+ setIsAuthenticated(false);
7785
+ }
7786
+ setIsReady(true);
7787
+ })();
7788
+ return; // setIsReady will be called in the async block
7789
+ }
7790
+ else {
7791
+ // Refresh expired or no tokens → clear
7792
+ setAccessToken(null);
7793
+ setRefreshToken(null);
7794
+ setIsAuthenticated(false);
7795
+ setIsReady(true);
7796
+ }
7797
+ }
7798
+ else {
7799
+ setIsReady(true);
7800
+ }
7801
+ }, [address]);
7677
7802
  useEffect(() => {
7678
7803
  const cleanup = addAuthInterceptors({
7679
7804
  apiBaseUrl,
7680
7805
  getAccessToken: () => {
7681
- return typeof window !== "undefined"
7682
- ? window.localStorage.getItem("accessToken")
7683
- : null;
7806
+ if (typeof window === 'undefined')
7807
+ return null;
7808
+ // Read from Zustand state as single source of truth
7809
+ return useUserData.getState().accessToken;
7684
7810
  },
7685
7811
  refreshTokens: async () => {
7686
7812
  const data = await refreshTokens();
@@ -7694,6 +7820,55 @@ function useAuth() {
7694
7820
  cleanup();
7695
7821
  };
7696
7822
  }, [apiBaseUrl]);
7823
+ // Proactive refresh effect: refresh when app regains focus or timer fires
7824
+ useEffect(() => {
7825
+ if (typeof window === 'undefined' || !address || !refreshToken$1)
7826
+ return;
7827
+ const refreshIfNeeded = async () => {
7828
+ // Prevent concurrent refresh attempts
7829
+ if (isRefreshingRef.current)
7830
+ return;
7831
+ // Read fresh expiration values from localStorage (not stale closure)
7832
+ const accessExpRaw = localStorage.getItem(`${address}_accessTokenExpiresAt`);
7833
+ const refreshExpRaw = localStorage.getItem(`${address}_refreshTokenExpiresAt`);
7834
+ const accessExp = accessExpRaw ? Number(accessExpRaw) : 0;
7835
+ const refreshExp = refreshExpRaw ? Number(refreshExpRaw) : 0;
7836
+ const now = nowMs();
7837
+ // If refresh token is already expired, do nothing
7838
+ if (refreshExp <= now)
7839
+ return;
7840
+ // If access token is within buffer window, refresh
7841
+ if (accessExp - now <= ACCESS_TOKEN_BUFFER_MS) {
7842
+ isRefreshingRef.current = true;
7843
+ try {
7844
+ await refreshTokens();
7845
+ }
7846
+ catch (_a) {
7847
+ // Refresh failed, interceptor will handle logout on next API call
7848
+ }
7849
+ finally {
7850
+ isRefreshingRef.current = false;
7851
+ }
7852
+ }
7853
+ };
7854
+ const onVisibilityChange = () => {
7855
+ if (document.visibilityState === 'visible') {
7856
+ refreshIfNeeded();
7857
+ }
7858
+ };
7859
+ document.addEventListener('visibilitychange', onVisibilityChange);
7860
+ // Schedule timer for (accessExp - buffer)
7861
+ const accessExpRaw = localStorage.getItem(`${address}_accessTokenExpiresAt`);
7862
+ const accessExp = accessExpRaw ? Number(accessExpRaw) : 0;
7863
+ const delay = Math.max(0, accessExp - nowMs() - ACCESS_TOKEN_BUFFER_MS);
7864
+ const timer = window.setTimeout(() => {
7865
+ refreshIfNeeded();
7866
+ }, delay);
7867
+ return () => {
7868
+ document.removeEventListener('visibilitychange', onVisibilityChange);
7869
+ clearTimeout(timer);
7870
+ };
7871
+ }, [address, refreshToken$1]);
7697
7872
  async function getEip712(address) {
7698
7873
  const { data } = await getEIP712Message(apiBaseUrl, address, clientId);
7699
7874
  return data;
@@ -7701,17 +7876,21 @@ function useAuth() {
7701
7876
  async function loginWithSignedMessage(address, signature, timestamp) {
7702
7877
  try {
7703
7878
  const { data } = await authenticate(apiBaseUrl, {
7704
- method: "eip712",
7879
+ method: 'eip712',
7705
7880
  address,
7706
7881
  clientId,
7707
7882
  details: { signature, timestamp },
7708
7883
  });
7709
- window.localStorage.setItem("accessToken", data.accessToken);
7710
- window.localStorage.setItem("refreshToken", data.refreshToken);
7711
- window.localStorage.setItem("address", address);
7884
+ const accessTokenKey = `${address}_accessToken`;
7885
+ const refreshTokenKey = `${address}_refreshToken`;
7886
+ const accessTokenExpiresAtKey = `${address}_accessTokenExpiresAt`;
7887
+ const refreshTokenExpiresAtKey = `${address}_refreshTokenExpiresAt`;
7888
+ window.localStorage.setItem(accessTokenKey, data.accessToken);
7889
+ window.localStorage.setItem(refreshTokenKey, data.refreshToken);
7890
+ window.localStorage.setItem(accessTokenExpiresAtKey, String(calcExpiresAt(data.expiresIn)));
7891
+ window.localStorage.setItem(refreshTokenExpiresAtKey, String(nowMs() + REFRESH_TOKEN_TTL_MS));
7712
7892
  setAccessToken(data.accessToken);
7713
7893
  setRefreshToken(data.refreshToken);
7714
- setAddress(address);
7715
7894
  setIsAuthenticated(true);
7716
7895
  }
7717
7896
  catch (e) {
@@ -7726,12 +7905,16 @@ function useAuth() {
7726
7905
  appId,
7727
7906
  accessToken: privyAccessToken,
7728
7907
  });
7729
- window.localStorage.setItem("accessToken", data.accessToken);
7730
- window.localStorage.setItem("refreshToken", data.refreshToken);
7731
- window.localStorage.setItem("address", address);
7908
+ const accessTokenKey = `${address}_accessToken`;
7909
+ const refreshTokenKey = `${address}_refreshToken`;
7910
+ const accessTokenExpiresAtKey = `${address}_accessTokenExpiresAt`;
7911
+ const refreshTokenExpiresAtKey = `${address}_refreshTokenExpiresAt`;
7912
+ window.localStorage.setItem(accessTokenKey, data.accessToken);
7913
+ window.localStorage.setItem(refreshTokenKey, data.refreshToken);
7914
+ window.localStorage.setItem(accessTokenExpiresAtKey, String(calcExpiresAt(data.expiresIn)));
7915
+ window.localStorage.setItem(refreshTokenExpiresAtKey, String(nowMs() + REFRESH_TOKEN_TTL_MS));
7732
7916
  setAccessToken(data.accessToken);
7733
7917
  setRefreshToken(data.refreshToken);
7734
- setAddress(address);
7735
7918
  setIsAuthenticated(true);
7736
7919
  }
7737
7920
  catch (e) {
@@ -7739,35 +7922,56 @@ function useAuth() {
7739
7922
  }
7740
7923
  }
7741
7924
  async function refreshTokens() {
7742
- const refresh = window.localStorage.getItem("refreshToken");
7743
- if (!refresh)
7744
- throw new Error("No refresh token");
7745
- const { data } = await refreshToken(apiBaseUrl, refresh);
7746
- window.localStorage.setItem("accessToken", data.accessToken);
7747
- window.localStorage.setItem("refreshToken", data.refreshToken);
7925
+ const currentAddress = address;
7926
+ const currentRefresh = refreshToken$1;
7927
+ if (!currentRefresh || !currentAddress)
7928
+ throw new Error('No refresh token');
7929
+ const { data } = await refreshToken(apiBaseUrl, currentRefresh);
7930
+ // Update tokens in localStorage
7931
+ const accessTokenKey = `${currentAddress}_accessToken`;
7932
+ const refreshTokenKey = `${currentAddress}_refreshToken`;
7933
+ const accessTokenExpiresAtKey = `${currentAddress}_accessTokenExpiresAt`;
7934
+ const refreshTokenExpiresAtKey = `${currentAddress}_refreshTokenExpiresAt`;
7935
+ window.localStorage.setItem(accessTokenKey, data.accessToken);
7936
+ window.localStorage.setItem(refreshTokenKey, data.refreshToken);
7937
+ window.localStorage.setItem(accessTokenExpiresAtKey, String(calcExpiresAt(data.expiresIn)));
7938
+ window.localStorage.setItem(refreshTokenExpiresAtKey, String(nowMs() + REFRESH_TOKEN_TTL_MS));
7748
7939
  setAccessToken(data.accessToken);
7749
7940
  setRefreshToken(data.refreshToken);
7750
7941
  setIsAuthenticated(true);
7751
7942
  return data;
7752
7943
  }
7753
7944
  async function logout$1() {
7754
- const refresh = window.localStorage.getItem("refreshToken");
7755
- if (refresh) {
7945
+ const currentAddress = address;
7946
+ const currentRefresh = refreshToken$1;
7947
+ if (currentRefresh) {
7756
7948
  try {
7757
- await logout(apiBaseUrl, refresh);
7949
+ await logout(apiBaseUrl, currentRefresh);
7758
7950
  }
7759
7951
  catch (_a) {
7760
7952
  /* ignore */
7761
7953
  }
7762
7954
  }
7763
- window.localStorage.removeItem("accessToken");
7764
- window.localStorage.removeItem("refreshToken");
7765
- window.localStorage.removeItem("address");
7955
+ if (currentAddress) {
7956
+ const accessTokenKey = `${currentAddress}_accessToken`;
7957
+ const refreshTokenKey = `${currentAddress}_refreshToken`;
7958
+ const accessTokenExpiresAtKey = `${currentAddress}_accessTokenExpiresAt`;
7959
+ const refreshTokenExpiresAtKey = `${currentAddress}_refreshTokenExpiresAt`;
7960
+ window.localStorage.removeItem(accessTokenKey);
7961
+ window.localStorage.removeItem(refreshTokenKey);
7962
+ window.localStorage.removeItem(accessTokenExpiresAtKey);
7963
+ window.localStorage.removeItem(refreshTokenExpiresAtKey);
7964
+ }
7766
7965
  setAccessToken(null);
7767
7966
  setRefreshToken(null);
7768
7967
  setAddress(null);
7769
7968
  setIsAuthenticated(false);
7770
7969
  }
7970
+ function clearSession() {
7971
+ setAccessToken(null);
7972
+ setRefreshToken(null);
7973
+ setIsAuthenticated(false);
7974
+ }
7771
7975
  return {
7772
7976
  isReady,
7773
7977
  isAuthenticated,
@@ -7779,6 +7983,8 @@ function useAuth() {
7779
7983
  loginWithPrivyToken,
7780
7984
  refreshTokens,
7781
7985
  logout: logout$1,
7986
+ clearSession,
7987
+ setAddress,
7782
7988
  };
7783
7989
  }
7784
7990
 
@@ -8091,9 +8297,16 @@ const PearHyperliquidContext = createContext(undefined);
8091
8297
  /**
8092
8298
  * React Provider for PearHyperliquidClient
8093
8299
  */
8094
- const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearprotocol.io', clientId = 'PEARPROTOCOLUI', wsUrl = 'wss://hl-ui.pearprotocol.io/ws', }) => {
8300
+ const PearHyperliquidProvider = ({ children, apiBaseUrl = "https://hl-ui.pearprotocol.io", clientId = "PEARPROTOCOLUI", wsUrl = "wss://hl-ui.pearprotocol.io/ws", }) => {
8095
8301
  const address = useUserData((s) => s.address);
8096
- const setAddress = useUserData((s) => s.setAddress);
8302
+ const clearHyperliquidUserData = useHyperliquidData((state) => state.clearUserData);
8303
+ const prevAddressRef = useRef(null);
8304
+ useEffect(() => {
8305
+ if (prevAddressRef.current !== null && prevAddressRef.current !== address) {
8306
+ clearHyperliquidUserData();
8307
+ }
8308
+ prevAddressRef.current = address;
8309
+ }, [address, clearHyperliquidUserData]);
8097
8310
  const perpMetasByDex = useHyperliquidData((state) => state.perpMetasByDex);
8098
8311
  const setPerpDexs = useHyperliquidData((state) => state.setPerpDexs);
8099
8312
  const setPerpMetasByDex = useHyperliquidData((state) => state.setPerpMetasByDex);
@@ -8124,20 +8337,20 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
8124
8337
  perpMetas.forEach((item, perpIndex) => {
8125
8338
  var _a, _b;
8126
8339
  const dexName = perpIndex === 0
8127
- ? 'HYPERLIQUID'
8340
+ ? "HYPERLIQUID"
8128
8341
  : ((_b = (_a = perpDexs[perpIndex]) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : `DEX_${perpIndex}`);
8129
8342
  var collateralToken;
8130
8343
  if (item.collateralToken === 360) {
8131
- collateralToken = 'USDH';
8344
+ collateralToken = "USDH";
8132
8345
  }
8133
8346
  if (item.collateralToken === 0) {
8134
- collateralToken = 'USDC';
8347
+ collateralToken = "USDC";
8135
8348
  }
8136
8349
  if (item.collateralToken === 235) {
8137
- collateralToken = 'USDE';
8350
+ collateralToken = "USDE";
8138
8351
  }
8139
8352
  if (item.collateralToken === 268) {
8140
- collateralToken = 'USDT0';
8353
+ collateralToken = "USDT0";
8141
8354
  }
8142
8355
  const universeAssets = item.universe.map((asset) => ({
8143
8356
  ...asset,
@@ -8165,8 +8378,6 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
8165
8378
  }), [
8166
8379
  apiBaseUrl,
8167
8380
  wsUrl,
8168
- address,
8169
- setAddress,
8170
8381
  isConnected,
8171
8382
  lastError,
8172
8383
  nativeIsConnected,
@@ -8181,7 +8392,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
8181
8392
  function usePearHyperliquid() {
8182
8393
  const ctx = useContext(PearHyperliquidContext);
8183
8394
  if (!ctx)
8184
- throw new Error('usePearHyperliquid must be used within a PearHyperliquidProvider');
8395
+ throw new Error("usePearHyperliquid must be used within a PearHyperliquidProvider");
8185
8396
  return ctx;
8186
8397
  }
8187
8398
 
@@ -1,4 +1,4 @@
1
- import React, { ReactNode } from 'react';
1
+ import React, { ReactNode } from "react";
2
2
  export interface PearHyperliquidContextType {
3
3
  clientId: string;
4
4
  apiBaseUrl: string;
@@ -1,5 +1,5 @@
1
- import { ActiveAssetData, CandleChartData, CandleData, ClearinghouseState, UniverseAsset, WebData3AssetCtx, WsAllMidsData, PerpDexsResponse } from '../types';
2
- import { TokenMetadataBySymbol } from '../utils/token-metadata-extractor';
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>>;
@@ -1,5 +1,5 @@
1
- import type { AxiosInstance } from 'axios';
2
- import { ApiErrorResponse } from '../types';
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
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pear-protocol/hyperliquid-sdk",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "React SDK for Pear Protocol Hyperliquid API integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",