@pear-protocol/symmio-client 0.2.6 → 0.2.7

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,7 +1,8 @@
1
1
  'use client';
2
- import { createContext, useContext, useState, useEffect, useMemo, useRef, useCallback } from 'react';
2
+ import { createContext, useContext, useEffect, useMemo, useRef, useCallback } from 'react';
3
3
  import { createSymmSDK, isAuthExpiredError, isNetworkError, isInsufficientMarginError, isRateLimitedError, isTimeoutError } from '@pear-protocol/symm-core';
4
4
  import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
5
+ import { create } from 'zustand';
5
6
  import { jsx } from 'react/jsx-runtime';
6
7
  import { isAddress, encodeFunctionData } from 'viem';
7
8
 
@@ -52,6 +53,10 @@ var symmKeys = {
52
53
  delegation: (account, target, selectors, chainId) => ["symm", "delegation", account, target, selectors, chainId],
53
54
  chartMetadata: (symbolsKey, positionKey) => ["symm", "chartMetadata", symbolsKey, positionKey]
54
55
  };
56
+ var useSymmWsStore = create((set) => ({
57
+ isConnected: false,
58
+ setConnected: (isConnected) => set({ isConnected })
59
+ }));
55
60
 
56
61
  // src/react/hooks/use-symm-ws.ts
57
62
  function asUnsubscribeFn(value) {
@@ -59,18 +64,19 @@ function asUnsubscribeFn(value) {
59
64
  }
60
65
  function useSymmWs(params) {
61
66
  const queryClient = useQueryClient();
67
+ const isConnected = useSymmWsStore((state) => state.isConnected);
68
+ const setConnected = useSymmWsStore((state) => state.setConnected);
62
69
  const { symmCoreClient, accountAddress, chainId } = params;
63
- const [isConnected, setIsConnected] = useState(false);
64
70
  useEffect(() => {
65
71
  if (!symmCoreClient || !accountAddress) {
66
- setIsConnected(false);
72
+ setConnected(false);
67
73
  return;
68
74
  }
69
75
  const ws = symmCoreClient.ws;
70
76
  const addr = accountAddress;
71
77
  const unsubscribers = [];
72
- const removeOnConnect = ws.onConnect(() => setIsConnected(true));
73
- const removeOnDisconnect = ws.onDisconnect(() => setIsConnected(false));
78
+ const removeOnConnect = ws.onConnect(() => setConnected(true));
79
+ const removeOnDisconnect = ws.onDisconnect(() => setConnected(false));
74
80
  unsubscribers.push(removeOnConnect, removeOnDisconnect);
75
81
  const positionsUnsub = asUnsubscribeFn(ws.subscribeToPositions(addr, chainId, () => {
76
82
  queryClient.invalidateQueries({
@@ -141,7 +147,7 @@ function useSymmWs(params) {
141
147
  ws.unsubscribeAll();
142
148
  }
143
149
  };
144
- }, [symmCoreClient, accountAddress, chainId, queryClient]);
150
+ }, [symmCoreClient, accountAddress, chainId, queryClient, setConnected]);
145
151
  return { isConnected };
146
152
  }
147
153
  var noopRefreshAuth = async () => null;
@@ -369,20 +375,60 @@ async function fetchAccessToken(walletClient, signerAddress, accountAddress, cha
369
375
  writeStoredToken(accountAddress, chainId, cachedToken);
370
376
  return accessToken;
371
377
  }
378
+ function symmAuthTokenKey(address, chainId) {
379
+ return `${address.toLowerCase()}:${chainId}`;
380
+ }
381
+ var useSymmAuthStore = create((set, get) => ({
382
+ tokensByKey: {},
383
+ setToken: (address, chainId, token) => {
384
+ const key = symmAuthTokenKey(address, chainId);
385
+ set((state) => ({
386
+ tokensByKey: {
387
+ ...state.tokensByKey,
388
+ [key]: token
389
+ }
390
+ }));
391
+ },
392
+ getToken: (address, chainId) => {
393
+ const key = symmAuthTokenKey(address, chainId);
394
+ return get().tokensByKey[key] ?? null;
395
+ },
396
+ clearToken: (address, chainId) => {
397
+ const key = symmAuthTokenKey(address, chainId);
398
+ set((state) => {
399
+ if (!(key in state.tokensByKey)) return state;
400
+ const next = { ...state.tokensByKey };
401
+ delete next[key];
402
+ return { tokensByKey: next };
403
+ });
404
+ },
405
+ clearAll: () => {
406
+ set({ tokensByKey: {} });
407
+ }
408
+ }));
372
409
 
373
410
  // src/react/hooks/use-symm-auth.ts
374
411
  function useSymmAuth(params) {
375
412
  const { address, chainId, walletClient, siweDomain } = params;
376
- const [accessToken, setAccessToken] = useState(null);
413
+ const accessToken = useSymmAuthStore(
414
+ (state) => address ? state.tokensByKey[symmAuthTokenKey(address, chainId)] ?? null : null
415
+ );
416
+ const setToken = useSymmAuthStore((state) => state.setToken);
417
+ const getToken = useSymmAuthStore((state) => state.getToken);
418
+ const clearToken = useSymmAuthStore((state) => state.clearToken);
377
419
  const previousAddressRef = useRef(address);
378
420
  const previousChainIdRef = useRef(chainId);
379
421
  const refreshAuth = useCallback(
380
422
  async (accountAddress) => {
381
423
  if (!walletClient || !address) return null;
382
424
  const resolvedAccountAddress = accountAddress ?? address;
425
+ const inMemory = getToken(resolvedAccountAddress, chainId);
426
+ if (inMemory) {
427
+ return inMemory;
428
+ }
383
429
  const cached = getCachedToken(resolvedAccountAddress, chainId);
384
430
  if (cached) {
385
- setAccessToken(cached);
431
+ setToken(resolvedAccountAddress, chainId, cached);
386
432
  return cached;
387
433
  }
388
434
  try {
@@ -393,21 +439,21 @@ function useSymmAuth(params) {
393
439
  chainId,
394
440
  siweDomain
395
441
  );
396
- setAccessToken(token);
442
+ setToken(resolvedAccountAddress, chainId, token);
397
443
  return token;
398
444
  } catch {
399
- setAccessToken(null);
445
+ clearToken(resolvedAccountAddress, chainId);
400
446
  return null;
401
447
  }
402
448
  },
403
- [walletClient, address, chainId, siweDomain]
449
+ [walletClient, address, chainId, siweDomain, getToken, setToken, clearToken]
404
450
  );
405
451
  const clearAuth = useCallback(() => {
406
452
  if (address) {
407
453
  clearCachedToken(address, chainId);
454
+ clearToken(address, chainId);
408
455
  }
409
- setAccessToken(null);
410
- }, [address, chainId]);
456
+ }, [address, chainId, clearToken]);
411
457
  useEffect(() => {
412
458
  const previousAddress = previousAddressRef.current;
413
459
  const previousChainId = previousChainIdRef.current;
@@ -416,15 +462,19 @@ function useSymmAuth(params) {
416
462
  previousAddressRef.current = address;
417
463
  previousChainIdRef.current = chainId;
418
464
  if (!address || !walletClient) {
419
- setAccessToken(null);
420
465
  return;
421
466
  }
422
467
  if (previousAddress && (addressChanged || chainChanged)) {
423
468
  clearCachedToken(previousAddress, previousChainId);
469
+ clearToken(previousAddress, previousChainId);
424
470
  }
425
471
  const cached = getCachedToken(address, chainId);
426
- setAccessToken(cached);
427
- }, [address, walletClient, chainId]);
472
+ if (cached) {
473
+ setToken(address, chainId, cached);
474
+ } else {
475
+ clearToken(address, chainId);
476
+ }
477
+ }, [address, walletClient, chainId, setToken, clearToken]);
428
478
  return {
429
479
  accessToken,
430
480
  authToken: accessToken,
@@ -1038,7 +1088,7 @@ function useSymmInstantTrade(params) {
1038
1088
  if (selectors.length === 0) {
1039
1089
  throw new Error("at least one delegation selector is required");
1040
1090
  }
1041
- const accessToken = await refreshAuth(accountAddress);
1091
+ const accessToken = useSymmAuthStore.getState().getToken(accountAddress, chainId) ?? await refreshAuth(accountAddress);
1042
1092
  if (!accessToken) {
1043
1093
  throw new Error("failed to refresh instant-trading auth");
1044
1094
  }
@@ -1437,7 +1487,7 @@ function useSymmCancelClose() {
1437
1487
  if (!symmCoreClient) {
1438
1488
  throw new Error("symm-core client not available");
1439
1489
  }
1440
- const resolvedAuthToken = authToken ?? ctxAuthToken ?? (accountAddress ? await refreshAuth(accountAddress) : null);
1490
+ const resolvedAuthToken = authToken ?? (accountAddress ? useSymmAuthStore.getState().getToken(accountAddress, chainId) : null) ?? ctxAuthToken ?? (accountAddress ? await refreshAuth(accountAddress) : null);
1441
1491
  if (!resolvedAuthToken) {
1442
1492
  throw new Error("auth token is required to cancel a pending close");
1443
1493
  }
@@ -25198,7 +25248,7 @@ function useSymmPendingInstantOpens(params) {
25198
25248
  ...params.query,
25199
25249
  queryKey: symmKeys.pendingInstantOpens(accountAddress, chainId),
25200
25250
  queryFn: async () => {
25201
- const authToken = providedAuthToken ?? ctxAuthToken ?? await refreshAuth(accountAddress);
25251
+ const authToken = providedAuthToken ?? useSymmAuthStore.getState().getToken(accountAddress, chainId) ?? ctxAuthToken ?? await refreshAuth(accountAddress);
25202
25252
  if (!authToken) {
25203
25253
  throw new Error("failed to acquire auth token for pending instant opens");
25204
25254
  }
@@ -25710,6 +25760,74 @@ function getBinanceWsManager() {
25710
25760
  return _instance;
25711
25761
  }
25712
25762
 
25763
+ // src/react/stores/use-binance-mark-price-store.ts
25764
+ var refCounts = /* @__PURE__ */ new Map();
25765
+ var streamUnsubs = /* @__PURE__ */ new Map();
25766
+ var streamSymbols = /* @__PURE__ */ new Map();
25767
+ function normalizeBinanceSymbol(symbol) {
25768
+ return symbol.toUpperCase().trim();
25769
+ }
25770
+ function getNextRefCount(binanceSymbol) {
25771
+ return (refCounts.get(binanceSymbol) ?? 0) + 1;
25772
+ }
25773
+ function getPrevRefCount(binanceSymbol) {
25774
+ return Math.max(0, (refCounts.get(binanceSymbol) ?? 0) - 1);
25775
+ }
25776
+ var useBinanceMarkPriceStore = create((set) => ({
25777
+ markPrices: {},
25778
+ subscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
25779
+ const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
25780
+ const nextRefCount = getNextRefCount(binanceSymbol);
25781
+ refCounts.set(binanceSymbol, nextRefCount);
25782
+ const symbols = streamSymbols.get(binanceSymbol) ?? /* @__PURE__ */ new Set();
25783
+ symbols.add(symmSymbol);
25784
+ streamSymbols.set(binanceSymbol, symbols);
25785
+ if (nextRefCount === 1) {
25786
+ const wsManager = getBinanceWsManager();
25787
+ const unsubscribe = wsManager.subscribeMarkPrice(binanceSymbol, (data) => {
25788
+ const canonicalSymbol = normalizeBinanceSymbol(data.symbol);
25789
+ const mappedSymbols = streamSymbols.get(canonicalSymbol);
25790
+ if (!mappedSymbols || mappedSymbols.size === 0) return;
25791
+ set((state) => {
25792
+ const nextMarkPrices = { ...state.markPrices };
25793
+ mappedSymbols.forEach((mappedSymbol) => {
25794
+ nextMarkPrices[mappedSymbol] = data.markPrice;
25795
+ });
25796
+ return { markPrices: nextMarkPrices };
25797
+ });
25798
+ });
25799
+ streamUnsubs.set(binanceSymbol, unsubscribe);
25800
+ }
25801
+ },
25802
+ unsubscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
25803
+ const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
25804
+ const symbols = streamSymbols.get(binanceSymbol);
25805
+ if (symbols) {
25806
+ symbols.delete(symmSymbol);
25807
+ if (symbols.size === 0) {
25808
+ streamSymbols.delete(binanceSymbol);
25809
+ } else {
25810
+ streamSymbols.set(binanceSymbol, symbols);
25811
+ }
25812
+ }
25813
+ const nextRefCount = getPrevRefCount(binanceSymbol);
25814
+ if (nextRefCount === 0) {
25815
+ const unsubscribe = streamUnsubs.get(binanceSymbol);
25816
+ if (unsubscribe) unsubscribe();
25817
+ streamUnsubs.delete(binanceSymbol);
25818
+ refCounts.delete(binanceSymbol);
25819
+ } else {
25820
+ refCounts.set(binanceSymbol, nextRefCount);
25821
+ }
25822
+ set((state) => {
25823
+ if (state.markPrices[symmSymbol] == null) return state;
25824
+ const nextMarkPrices = { ...state.markPrices };
25825
+ delete nextMarkPrices[symmSymbol];
25826
+ return { markPrices: nextMarkPrices };
25827
+ });
25828
+ }
25829
+ }));
25830
+
25713
25831
  // src/react/hooks/use-symm-token-selection-metadata.ts
25714
25832
  async function fetchTickerSnapshot(symbol) {
25715
25833
  const resolution = resolveBinanceSymbol(symbol);
@@ -25751,9 +25869,9 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
25751
25869
  ),
25752
25870
  [selectedSymbols]
25753
25871
  );
25754
- const [liveMarkPrices, setLiveMarkPrices] = useState(
25755
- {}
25756
- );
25872
+ const liveMarkPrices = useBinanceMarkPriceStore((state) => state.markPrices);
25873
+ const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);
25874
+ const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);
25757
25875
  const query = useQuery({
25758
25876
  queryKey: ["symm", "chart-metadata", symbolsKey],
25759
25877
  queryFn: async () => {
@@ -25781,36 +25899,25 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
25781
25899
  });
25782
25900
  useEffect(() => {
25783
25901
  if (!enabled || isUnsupported || selectedSymbols.length === 0) {
25784
- setLiveMarkPrices({});
25785
25902
  return;
25786
25903
  }
25787
- setLiveMarkPrices((current) => {
25788
- const next = {};
25789
- for (const symbol of selectedSymbols) {
25790
- if (current[symbol] != null) {
25791
- next[symbol] = current[symbol];
25792
- }
25793
- }
25794
- return next;
25795
- });
25796
- const wsManager = getBinanceWsManager();
25797
- const unsubscribes = Array.from(symbolToBinanceMap.entries()).map(
25798
- ([symbol, binanceSymbol]) => wsManager.subscribeMarkPrice(binanceSymbol, (data) => {
25799
- setLiveMarkPrices((current) => {
25800
- if (current[symbol] === data.markPrice) return current;
25801
- return {
25802
- ...current,
25803
- [symbol]: data.markPrice
25804
- };
25805
- });
25806
- })
25904
+ const symbolEntries = Array.from(symbolToBinanceMap.entries());
25905
+ symbolEntries.forEach(
25906
+ ([symbol, binanceSymbol]) => subscribeSymbol(symbol, binanceSymbol)
25807
25907
  );
25808
25908
  return () => {
25809
- for (const unsubscribe of unsubscribes) {
25810
- unsubscribe();
25811
- }
25909
+ symbolEntries.forEach(
25910
+ ([symbol, binanceSymbol]) => unsubscribeSymbol(symbol, binanceSymbol)
25911
+ );
25812
25912
  };
25813
- }, [enabled, isUnsupported, selectedSymbols, symbolToBinanceMap]);
25913
+ }, [
25914
+ enabled,
25915
+ isUnsupported,
25916
+ selectedSymbols.length,
25917
+ symbolToBinanceMap,
25918
+ subscribeSymbol,
25919
+ unsubscribeSymbol
25920
+ ]);
25814
25921
  return useMemo(() => {
25815
25922
  const tickerSnapshots = query.data?.tickerSnapshots ?? {};
25816
25923
  const longMeta = {};
@@ -26250,6 +26357,6 @@ function getSymmErrorMessage(error) {
26250
26357
  return "An unexpected error occurred.";
26251
26358
  }
26252
26359
 
26253
- export { SymmProvider, getSymmErrorMessage, symmKeys, useSymmAccountData, useSymmAccountSummary, useSymmAccounts, useSymmAccountsApi, useSymmAccountsLength, useSymmAccountsWithPositions, useSymmApproval, useSymmAuth, useSymmAvailableMargin, useSymmBalances, useSymmCancelClose, useSymmChartCandles, useSymmChartSelection, useSymmCloseOrder, useSymmCollateral, useSymmContext, useSymmCoreClient, useSymmDelegation, useSymmDeposit, useSymmFunding, useSymmFundingHistory, useSymmFundingPayments, useSymmHedgerMarketById, useSymmHedgerMarketBySymbol, useSymmHedgerMarkets, useSymmInstantTrade, useSymmLockedParams, useSymmMarkets, useSymmNotifications, useSymmOpenOrders, useSymmPendingIds, useSymmPendingInstantOpens, useSymmPerformanceOverlays, useSymmPortfolio, useSymmPositions, useSymmSignature, useSymmTokenSelectionMetadata, useSymmTpsl, useSymmTpslOrders, useSymmTrade, useSymmTradeHistory, useSymmTriggerConfig, useSymmTriggerOrders, useSymmTwap, useSymmTwapOrder, useSymmWithdraw, useSymmWs };
26360
+ export { SymmProvider, getSymmErrorMessage, symmKeys, useBinanceMarkPriceStore, useSymmAccountData, useSymmAccountSummary, useSymmAccounts, useSymmAccountsApi, useSymmAccountsLength, useSymmAccountsWithPositions, useSymmApproval, useSymmAuth, useSymmAuthStore, useSymmAvailableMargin, useSymmBalances, useSymmCancelClose, useSymmChartCandles, useSymmChartSelection, useSymmCloseOrder, useSymmCollateral, useSymmContext, useSymmCoreClient, useSymmDelegation, useSymmDeposit, useSymmFunding, useSymmFundingHistory, useSymmFundingPayments, useSymmHedgerMarketById, useSymmHedgerMarketBySymbol, useSymmHedgerMarkets, useSymmInstantTrade, useSymmLockedParams, useSymmMarkets, useSymmNotifications, useSymmOpenOrders, useSymmPendingIds, useSymmPendingInstantOpens, useSymmPerformanceOverlays, useSymmPortfolio, useSymmPositions, useSymmSignature, useSymmTokenSelectionMetadata, useSymmTpsl, useSymmTpslOrders, useSymmTrade, useSymmTradeHistory, useSymmTriggerConfig, useSymmTriggerOrders, useSymmTwap, useSymmTwapOrder, useSymmWithdraw, useSymmWs, useSymmWsStore };
26254
26361
  //# sourceMappingURL=index.mjs.map
26255
26362
  //# sourceMappingURL=index.mjs.map