@pear-protocol/symmio-client 0.1.5 → 0.1.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.
@@ -3,7 +3,7 @@ import { useAccount, usePublicClient, useWalletClient } from 'wagmi';
3
3
  import { createSymmSDK, isAuthExpiredError, isNetworkError, isInsufficientMarginError, isRateLimitedError, isTimeoutError } from '@pear-protocol/symm-core';
4
4
  import { encodeFunctionData, isAddress } from 'viem';
5
5
  import { jsx } from 'react/jsx-runtime';
6
- import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
6
+ import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
7
7
 
8
8
  // src/react/provider.tsx
9
9
 
@@ -27,6 +27,10 @@ var CLEARING_HOUSE_ADDRESS = {
27
27
  var SIGNATURE_STORE_ADDRESS = {
28
28
  [42161 /* ARBITRUM */]: "0x94eEa58De1C8945c342dB4bE9670301638E403e2"
29
29
  };
30
+ var DEFAULT_PARTY_B_ADDRESS = {
31
+ [42161 /* ARBITRUM */]: "0x00c069d68bc7420740460DBC3cc3fFF9b3742421",
32
+ [8453 /* BASE */]: "0x1EcAbF0Eba136920677C9575FAccee36f30592cf"
33
+ };
30
34
  function getAddress(addressMap, chainId, name) {
31
35
  const addr = addressMap[chainId];
32
36
  if (!addr || addr === "0x0000000000000000000000000000000000000000") {
@@ -23650,6 +23654,17 @@ async function revokeAccess(walletClient, publicClient, multiAccount, params) {
23650
23654
  chain: walletClient.chain
23651
23655
  });
23652
23656
  }
23657
+ async function hasDelegatedAccess(publicClient, multiAccount, params) {
23658
+ validateAddress(params.account, "subAccount");
23659
+ validateAddress(params.target, "target");
23660
+ const result = await publicClient.readContract({
23661
+ address: multiAccount,
23662
+ abi: MultiAccountABI,
23663
+ functionName: "delegatedAccesses",
23664
+ args: [params.account, params.target, params.selector]
23665
+ });
23666
+ return Boolean(result);
23667
+ }
23653
23668
 
23654
23669
  // src/abis/SignatureStore.ts
23655
23670
  var SignatureStoreABI = [
@@ -23954,6 +23969,51 @@ async function getOpenInstantCloses(chainId, account, accessToken) {
23954
23969
  return response.json();
23955
23970
  }
23956
23971
 
23972
+ // src/actions/stats.ts
23973
+ async function getPartyAStats(publicClient, symmioDiamond, partyA) {
23974
+ const result = await publicClient.readContract({
23975
+ address: symmioDiamond,
23976
+ abi: SymmioDiamondABI,
23977
+ functionName: "partyAStats",
23978
+ args: [partyA]
23979
+ });
23980
+ return {
23981
+ collateralBalance: result[1],
23982
+ allocatedBalance: result[2],
23983
+ availableBalance: result[3],
23984
+ lockedCVA: result[4],
23985
+ lockedLF: result[5],
23986
+ lockedPartyAMM: result[6],
23987
+ lockedPartyBMM: result[7],
23988
+ pendingLockedCVA: result[8],
23989
+ pendingLockedLF: result[9],
23990
+ pendingLockedPartyAMM: result[10],
23991
+ pendingLockedPartyBMM: result[11],
23992
+ positionsCount: Number(result[12]),
23993
+ pendingCount: Number(result[13]),
23994
+ nonces: 0
23995
+ };
23996
+ }
23997
+ function calculateAvailableForOrder(stats, upnl) {
23998
+ const {
23999
+ allocatedBalance,
24000
+ lockedCVA,
24001
+ lockedLF,
24002
+ lockedPartyAMM,
24003
+ pendingLockedCVA,
24004
+ pendingLockedLF,
24005
+ pendingLockedPartyAMM
24006
+ } = stats;
24007
+ const totalPendingLocked = pendingLockedCVA + pendingLockedLF + pendingLockedPartyAMM;
24008
+ if (upnl >= 0n) {
24009
+ const totalLocked = lockedCVA + lockedLF + lockedPartyAMM;
24010
+ return allocatedBalance + upnl - totalLocked - totalPendingLocked;
24011
+ }
24012
+ const absUpnl = -upnl;
24013
+ const consideringMm = absUpnl > lockedPartyAMM ? absUpnl : lockedPartyAMM;
24014
+ return allocatedBalance - lockedCVA - lockedLF - totalPendingLocked - consideringMm;
24015
+ }
24016
+
23957
24017
  // src/client.ts
23958
24018
  var SymmioSDK = class {
23959
24019
  chainId;
@@ -24100,7 +24160,9 @@ var SymmioSDK = class {
24100
24160
  /** Propose to revoke delegated access (starts cooldown). */
24101
24161
  proposeRevoke: (params) => proposeRevoke(wc, pc, ma, params),
24102
24162
  /** Revoke delegated access (after cooldown). */
24103
- revokeAccess: (params) => revokeAccess(wc, pc, ma, params)
24163
+ revokeAccess: (params) => revokeAccess(wc, pc, ma, params),
24164
+ /** Reads whether a selector is delegated to a target for a sub-account. */
24165
+ hasAccess: (params) => hasDelegatedAccess(pc, ma, params)
24104
24166
  };
24105
24167
  }
24106
24168
  // ─── Signature Module ──────────────────────────────────────────
@@ -24156,6 +24218,17 @@ var SymmioSDK = class {
24156
24218
  getOpenCloses: (account, accessToken) => getOpenInstantCloses(this.chainId, account, accessToken)
24157
24219
  };
24158
24220
  }
24221
+ // ─── Stats Module ───────────────────────────────────────────
24222
+ get stats() {
24223
+ const pc = this._publicClient;
24224
+ const sd = this._symmioDiamond;
24225
+ return {
24226
+ /** Reads partyA statistics (balances, locks) from the Diamond contract. */
24227
+ getPartyAStats: (partyA) => getPartyAStats(pc, sd, partyA),
24228
+ /** Calculates available margin for placing orders given uPNL. */
24229
+ calculateAvailableForOrder: (stats, upnl) => calculateAvailableForOrder(stats, upnl)
24230
+ };
24231
+ }
24159
24232
  // ─── Muon Signatures ──────────────────────────────────────────
24160
24233
  async getQuoteSig(partyA, symbolId) {
24161
24234
  return this.muon.getQuoteSig({
@@ -24330,8 +24403,8 @@ function SymmProvider({
24330
24403
  setAccessToken(null);
24331
24404
  return;
24332
24405
  }
24333
- setAccessToken(null);
24334
24406
  if (previousAddress && (addressChanged || chainChanged)) {
24407
+ setAccessToken(null);
24335
24408
  clearCachedToken(previousAddress, previousChainId);
24336
24409
  }
24337
24410
  }, [address, walletClient, chainId]);
@@ -24386,6 +24459,14 @@ function useSymmAuth() {
24386
24459
  };
24387
24460
  }
24388
24461
 
24462
+ // src/constants/selectors.ts
24463
+ var SEND_QUOTE_WITH_AFFILIATE_SELECTOR = "0x40f1310c";
24464
+ var CLOSE_QUOTE_SELECTOR = "0x501e891f";
24465
+ var ALL_TRADING_SELECTORS = [
24466
+ SEND_QUOTE_WITH_AFFILIATE_SELECTOR,
24467
+ CLOSE_QUOTE_SELECTOR
24468
+ ];
24469
+
24389
24470
  // src/react/query-keys.ts
24390
24471
  var symmKeys = {
24391
24472
  all: ["symm"],
@@ -24404,10 +24485,157 @@ var symmKeys = {
24404
24485
  fundingRates: (chainId) => ["symm", "fundingRates", chainId],
24405
24486
  portfolio: (address, chainId) => ["symm", "portfolio", address, chainId],
24406
24487
  notifications: (address, chainId) => ["symm", "notifications", address, chainId],
24407
- unreadCount: (address, chainId) => ["symm", "unreadCount", address, chainId]
24488
+ unreadCount: (address, chainId) => ["symm", "unreadCount", address, chainId],
24489
+ availableMargin: (address, chainId) => ["symm", "availableMargin", address, chainId],
24490
+ delegation: (account, target, selectors, chainId) => ["symm", "delegation", account, target, selectors, chainId],
24491
+ chartMetadata: (symbolsKey, positionKey) => ["symm", "chartMetadata", symbolsKey, positionKey]
24408
24492
  };
24409
24493
 
24410
- // src/react/hooks/use-symm-accounts.ts
24494
+ // src/react/hooks/use-symm-delegation.ts
24495
+ function useSymmDelegation(params) {
24496
+ const { symmioClient, chainId, address } = useSymmContext();
24497
+ const accountAddress = params?.accountAddress ?? address;
24498
+ const target = params?.target ?? DEFAULT_PARTY_B_ADDRESS[chainId];
24499
+ const selectors = params?.selectors ?? ALL_TRADING_SELECTORS;
24500
+ const enabled = (params?.enabled ?? true) && !!symmioClient && !!accountAddress && !!target && selectors.length > 0;
24501
+ return useQuery({
24502
+ queryKey: symmKeys.delegation(accountAddress, target, selectors, chainId),
24503
+ enabled,
24504
+ queryFn: async () => {
24505
+ if (!symmioClient) throw new Error("symmio client not available");
24506
+ if (!accountAddress) throw new Error("account address is required");
24507
+ if (!target) throw new Error("delegation target is not configured");
24508
+ const entries = await Promise.all(
24509
+ selectors.map(async (selector) => [
24510
+ selector,
24511
+ await symmioClient.delegation.hasAccess({
24512
+ account: accountAddress,
24513
+ target,
24514
+ selector
24515
+ })
24516
+ ])
24517
+ );
24518
+ const accessBySelector = Object.fromEntries(entries);
24519
+ return {
24520
+ hasAccess: selectors.every((selector) => accessBySelector[selector] === true),
24521
+ account: accountAddress,
24522
+ target,
24523
+ selectors,
24524
+ accessBySelector,
24525
+ openAccess: accessBySelector[SEND_QUOTE_WITH_AFFILIATE_SELECTOR] ?? void 0,
24526
+ closeAccess: accessBySelector[CLOSE_QUOTE_SELECTOR] ?? void 0
24527
+ };
24528
+ }
24529
+ });
24530
+ }
24531
+
24532
+ // src/react/cache.ts
24533
+ function invalidateBalances(qc) {
24534
+ qc.invalidateQueries({ queryKey: ["symm", "balances"] });
24535
+ qc.invalidateQueries({ queryKey: ["symm", "accountSummary"] });
24536
+ qc.invalidateQueries({ queryKey: ["symm", "portfolio"] });
24537
+ }
24538
+ function invalidatePositions(qc) {
24539
+ qc.invalidateQueries({ queryKey: ["symm", "positions"] });
24540
+ qc.invalidateQueries({ queryKey: ["symm", "openOrders"] });
24541
+ qc.invalidateQueries({ queryKey: ["symm", "tradeHistory"] });
24542
+ qc.invalidateQueries({ queryKey: ["symm", "portfolio"] });
24543
+ }
24544
+ function invalidateOrders(qc) {
24545
+ qc.invalidateQueries({ queryKey: ["symm", "openOrders"] });
24546
+ qc.invalidateQueries({ queryKey: ["symm", "tpslOrders"] });
24547
+ qc.invalidateQueries({ queryKey: ["symm", "twapOrders"] });
24548
+ }
24549
+
24550
+ // src/react/hooks/use-symm-instant-trade.ts
24551
+ function useSymmInstantTrade(params) {
24552
+ const {
24553
+ symmioClient,
24554
+ symmCoreClient,
24555
+ chainId,
24556
+ address,
24557
+ refreshAuth
24558
+ } = useSymmContext();
24559
+ const queryClient = useQueryClient();
24560
+ const defaultAccountAddress = params?.accountAddress ?? address;
24561
+ const defaultTarget = params?.target ?? DEFAULT_PARTY_B_ADDRESS[chainId];
24562
+ const defaultSelectors = params?.selectors ?? ALL_TRADING_SELECTORS;
24563
+ const delegation = useSymmDelegation({
24564
+ accountAddress: defaultAccountAddress,
24565
+ target: defaultTarget,
24566
+ selectors: defaultSelectors,
24567
+ enabled: params?.enabled
24568
+ });
24569
+ const ensureReady = useMutation({
24570
+ mutationFn: async (request) => {
24571
+ if (!symmioClient) throw new Error("symmio client not available");
24572
+ const accountAddress = request?.accountAddress ?? defaultAccountAddress;
24573
+ const target = request?.target ?? defaultTarget;
24574
+ const selectors = request?.selectors ?? defaultSelectors;
24575
+ if (!accountAddress) throw new Error("account address is required");
24576
+ if (!target) throw new Error("delegation target is not configured");
24577
+ if (selectors.length === 0) {
24578
+ throw new Error("at least one delegation selector is required");
24579
+ }
24580
+ const accessToken = await refreshAuth(accountAddress);
24581
+ if (!accessToken) {
24582
+ throw new Error("failed to refresh instant-trading auth");
24583
+ }
24584
+ const accessStates = await Promise.all(
24585
+ selectors.map(
24586
+ (selector) => symmioClient.delegation.hasAccess({
24587
+ account: accountAddress,
24588
+ target,
24589
+ selector
24590
+ })
24591
+ )
24592
+ );
24593
+ if (!accessStates.every(Boolean)) {
24594
+ await symmioClient.delegation.delegateAccess({
24595
+ account: accountAddress,
24596
+ target,
24597
+ selectors: [...selectors],
24598
+ activate: true
24599
+ });
24600
+ await queryClient.invalidateQueries({
24601
+ queryKey: symmKeys.delegation(accountAddress, target, selectors, chainId)
24602
+ });
24603
+ }
24604
+ return {
24605
+ accessToken,
24606
+ accountAddress,
24607
+ target,
24608
+ selectors
24609
+ };
24610
+ }
24611
+ });
24612
+ const execute = useMutation({
24613
+ mutationFn: async (request) => {
24614
+ if (!symmCoreClient) throw new Error("symm-core client not available");
24615
+ const setup = await ensureReady.mutateAsync({
24616
+ accountAddress: request.accountAddress,
24617
+ target: request.target,
24618
+ selectors: request.selectors
24619
+ });
24620
+ return request.action({
24621
+ symmCoreClient,
24622
+ accessToken: setup.accessToken,
24623
+ accountAddress: setup.accountAddress
24624
+ });
24625
+ },
24626
+ onSuccess: (_data, variables) => {
24627
+ if (variables.invalidatePositionsOnSuccess !== false) {
24628
+ invalidatePositions(queryClient);
24629
+ }
24630
+ }
24631
+ });
24632
+ return {
24633
+ delegation,
24634
+ ensureReady,
24635
+ execute
24636
+ };
24637
+ }
24638
+ var EMPTY_ACCOUNTS = [];
24411
24639
  function useSymmAccounts(userAddress) {
24412
24640
  const { symmioClient } = useSymmContext();
24413
24641
  const queryClient = useQueryClient();
@@ -24438,7 +24666,7 @@ function useSymmAccounts(userAddress) {
24438
24666
  }
24439
24667
  });
24440
24668
  return {
24441
- accounts: accountsQuery.data ?? [],
24669
+ accounts: accountsQuery.data ?? EMPTY_ACCOUNTS,
24442
24670
  count: accountsQuery.data?.length ?? 0,
24443
24671
  isLoading: accountsQuery.isLoading,
24444
24672
  error: accountsQuery.error,
@@ -24453,21 +24681,23 @@ function useSymmApproval(params) {
24453
24681
  const { owner, amount, spender, collateralToken } = params;
24454
24682
  const resolvedSpender = spender ?? symmioClient?.addresses.multiAccount;
24455
24683
  const resolvedToken = collateralToken ?? symmioClient?.addresses.collateral;
24684
+ const selectWithAmount = useCallback(
24685
+ (data) => ({
24686
+ ...data,
24687
+ state: data.allowance >= amount ? "APPROVED" /* APPROVED */ : "NOT_APPROVED" /* NOT_APPROVED */
24688
+ }),
24689
+ [amount]
24690
+ );
24456
24691
  const approvalQuery = useQuery({
24457
24692
  queryKey: symmKeys.approval(owner, resolvedSpender),
24458
24693
  queryFn: async () => {
24459
- const [state, allowance, balance] = await Promise.all([
24460
- symmioClient.approval.getState(
24461
- resolvedToken,
24462
- owner,
24463
- resolvedSpender,
24464
- amount
24465
- ),
24694
+ const [allowance, balance] = await Promise.all([
24466
24695
  symmioClient.approval.getAllowance(resolvedToken, owner, resolvedSpender),
24467
24696
  symmioClient.approval.getBalance(resolvedToken, owner)
24468
24697
  ]);
24469
- return { state, allowance, balance };
24698
+ return { allowance, balance };
24470
24699
  },
24700
+ select: selectWithAmount,
24471
24701
  enabled: !!symmioClient && !!owner && !!resolvedSpender && !!resolvedToken
24472
24702
  });
24473
24703
  const approve2 = useMutation({
@@ -24489,26 +24719,6 @@ function useSymmApproval(params) {
24489
24719
  approve: approve2
24490
24720
  };
24491
24721
  }
24492
-
24493
- // src/react/cache.ts
24494
- function invalidateBalances(qc) {
24495
- qc.invalidateQueries({ queryKey: ["symm", "balances"] });
24496
- qc.invalidateQueries({ queryKey: ["symm", "accountSummary"] });
24497
- qc.invalidateQueries({ queryKey: ["symm", "portfolio"] });
24498
- }
24499
- function invalidatePositions(qc) {
24500
- qc.invalidateQueries({ queryKey: ["symm", "positions"] });
24501
- qc.invalidateQueries({ queryKey: ["symm", "openOrders"] });
24502
- qc.invalidateQueries({ queryKey: ["symm", "tradeHistory"] });
24503
- qc.invalidateQueries({ queryKey: ["symm", "portfolio"] });
24504
- }
24505
- function invalidateOrders(qc) {
24506
- qc.invalidateQueries({ queryKey: ["symm", "openOrders"] });
24507
- qc.invalidateQueries({ queryKey: ["symm", "tpslOrders"] });
24508
- qc.invalidateQueries({ queryKey: ["symm", "twapOrders"] });
24509
- }
24510
-
24511
- // src/react/hooks/use-symm-deposit.ts
24512
24722
  function useSymmDeposit() {
24513
24723
  const { symmioClient } = useSymmContext();
24514
24724
  const queryClient = useQueryClient();
@@ -24628,6 +24838,32 @@ function useSymmSignature(userAddress) {
24628
24838
  signTerms
24629
24839
  };
24630
24840
  }
24841
+ function useSymmAvailableMargin(params) {
24842
+ const { symmioClient } = useSymmContext();
24843
+ const { accountAddress, upnl } = params;
24844
+ const resolvedUpnl = upnl ?? 0n;
24845
+ const selectWithUpnl = useCallback(
24846
+ (stats) => ({
24847
+ ...stats,
24848
+ upnl: resolvedUpnl,
24849
+ availableForOrder: calculateAvailableForOrder(stats, resolvedUpnl)
24850
+ }),
24851
+ [resolvedUpnl]
24852
+ );
24853
+ const query = useQuery({
24854
+ queryKey: symmKeys.availableMargin(accountAddress, symmioClient?.chainId),
24855
+ queryFn: () => symmioClient.stats.getPartyAStats(accountAddress),
24856
+ select: selectWithUpnl,
24857
+ enabled: !!symmioClient && !!accountAddress,
24858
+ staleTime: 1e4
24859
+ });
24860
+ return {
24861
+ availableForOrder: query.data?.availableForOrder ?? null,
24862
+ stats: query.data ?? null,
24863
+ isLoading: query.isLoading,
24864
+ refetch: query.refetch
24865
+ };
24866
+ }
24631
24867
  function useSymmBalances(params) {
24632
24868
  const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
24633
24869
  const { userAddress, multiAccountAddress } = params;
@@ -24837,37 +25073,37 @@ function useSymmMarkets(params) {
24837
25073
  refetch: query.refetch
24838
25074
  };
24839
25075
  }
25076
+ var EMPTY_MARKETS = [];
25077
+ var EMPTY_SYMBOLS = [];
25078
+ var EMPTY_MAP_BY_ID = /* @__PURE__ */ new Map();
25079
+ var EMPTY_MAP_BY_SYMBOL = /* @__PURE__ */ new Map();
24840
25080
  function useSymmHedgerMarkets(params) {
24841
25081
  const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
24842
25082
  const chainId = params?.chainId ?? ctxChainId;
24843
25083
  const searchText = params?.searchText?.trim();
24844
25084
  const isEnabled = params?.enabled ?? true;
25085
+ const { enabled: _, ...restParams } = params ?? {};
24845
25086
  const request = {
24846
- ...params,
25087
+ ...restParams,
24847
25088
  chainId,
24848
25089
  searchText: searchText || void 0
24849
25090
  };
24850
25091
  const query = useQuery({
24851
25092
  queryKey: symmKeys.hedgerMarkets(request),
24852
- queryFn: async () => {
24853
- const { enabled: _enabled, ...queryRequest } = request;
24854
- return symmCoreClient.markets.listSymmHedger(queryRequest);
24855
- },
25093
+ queryFn: () => symmCoreClient.markets.listSymmHedger(request),
24856
25094
  enabled: !!symmCoreClient && isEnabled,
24857
25095
  staleTime: 3e4
24858
25096
  });
24859
25097
  const data = query.data ?? null;
24860
- const emptyMap = /* @__PURE__ */ new Map();
24861
- const emptySymbolMap = /* @__PURE__ */ new Map();
24862
25098
  return {
24863
25099
  data,
24864
- markets: data?.markets ?? [],
24865
- rawMarkets: data?.rawMarkets ?? [],
24866
- filteredMarkets: data?.filteredMarkets ?? [],
24867
- allSymbols: data?.allSymbols ?? [],
24868
- filteredSymbols: data?.filteredSymbols ?? [],
24869
- marketsById: data?.marketsById ?? emptyMap,
24870
- marketsBySymbol: data?.marketsBySymbol ?? emptySymbolMap,
25100
+ markets: data?.markets ?? EMPTY_MARKETS,
25101
+ rawMarkets: data?.rawMarkets ?? EMPTY_MARKETS,
25102
+ filteredMarkets: data?.filteredMarkets ?? EMPTY_MARKETS,
25103
+ allSymbols: data?.allSymbols ?? EMPTY_SYMBOLS,
25104
+ filteredSymbols: data?.filteredSymbols ?? EMPTY_SYMBOLS,
25105
+ marketsById: data?.marketsById ?? EMPTY_MAP_BY_ID,
25106
+ marketsBySymbol: data?.marketsBySymbol ?? EMPTY_MAP_BY_SYMBOL,
24871
25107
  category: data?.category ?? params?.category ?? "all",
24872
25108
  resolvedSearchText: data?.searchText ?? searchText ?? "",
24873
25109
  isLoading: query.isLoading,
@@ -24959,13 +25195,15 @@ function useSymmNotifications(params) {
24959
25195
  refetch: notificationsQuery.refetch
24960
25196
  };
24961
25197
  }
25198
+ function asUnsubscribeFn(value) {
25199
+ return typeof value === "function" ? value : null;
25200
+ }
24962
25201
  function useSymmWs(params) {
24963
25202
  const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
24964
25203
  const queryClient = useQueryClient();
24965
25204
  const { accountAddress } = params;
24966
25205
  const chainId = params.chainId ?? ctxChainId;
24967
25206
  const [isConnected, setIsConnected] = useState(false);
24968
- const cleanupRef = useRef([]);
24969
25207
  useEffect(() => {
24970
25208
  if (!symmCoreClient || !accountAddress) {
24971
25209
  setIsConnected(false);
@@ -24973,61 +25211,75 @@ function useSymmWs(params) {
24973
25211
  }
24974
25212
  const ws = symmCoreClient.ws;
24975
25213
  const addr = accountAddress;
25214
+ const unsubscribers = [];
24976
25215
  const removeOnConnect = ws.onConnect(() => setIsConnected(true));
24977
25216
  const removeOnDisconnect = ws.onDisconnect(() => setIsConnected(false));
24978
- ws.subscribeToPositions(addr, chainId, () => {
25217
+ unsubscribers.push(removeOnConnect, removeOnDisconnect);
25218
+ const positionsUnsub = asUnsubscribeFn(ws.subscribeToPositions(addr, chainId, () => {
24979
25219
  queryClient.invalidateQueries({
24980
25220
  queryKey: symmKeys.positions(accountAddress, chainId)
24981
25221
  });
24982
- });
24983
- ws.subscribeToOpenOrders(addr, chainId, () => {
25222
+ }));
25223
+ if (positionsUnsub) unsubscribers.push(positionsUnsub);
25224
+ const openOrdersUnsub = asUnsubscribeFn(ws.subscribeToOpenOrders(addr, chainId, () => {
24984
25225
  queryClient.invalidateQueries({
24985
25226
  queryKey: symmKeys.openOrders(accountAddress, chainId)
24986
25227
  });
24987
- });
24988
- ws.subscribeToTrades(addr, chainId, () => {
25228
+ }));
25229
+ if (openOrdersUnsub) unsubscribers.push(openOrdersUnsub);
25230
+ const tradesUnsub = asUnsubscribeFn(ws.subscribeToTrades(addr, chainId, () => {
24989
25231
  queryClient.invalidateQueries({
24990
25232
  queryKey: symmKeys.tradeHistory(accountAddress, chainId)
24991
25233
  });
24992
- });
24993
- ws.subscribeToAccountSummary(addr, chainId, () => {
25234
+ }));
25235
+ if (tradesUnsub) unsubscribers.push(tradesUnsub);
25236
+ const accountSummaryUnsub = asUnsubscribeFn(ws.subscribeToAccountSummary(addr, chainId, () => {
24994
25237
  queryClient.invalidateQueries({
24995
25238
  queryKey: symmKeys.balances(accountAddress, chainId)
24996
25239
  });
24997
25240
  queryClient.invalidateQueries({
24998
25241
  queryKey: symmKeys.accountSummary(accountAddress, chainId)
24999
25242
  });
25000
- });
25001
- ws.subscribeToNotifications(addr, chainId, () => {
25243
+ }));
25244
+ if (accountSummaryUnsub) unsubscribers.push(accountSummaryUnsub);
25245
+ const notificationsUnsub = asUnsubscribeFn(ws.subscribeToNotifications(addr, chainId, () => {
25002
25246
  queryClient.invalidateQueries({
25003
25247
  queryKey: symmKeys.notifications(accountAddress, chainId)
25004
25248
  });
25005
25249
  queryClient.invalidateQueries({
25006
25250
  queryKey: symmKeys.unreadCount(accountAddress, chainId)
25007
25251
  });
25008
- });
25009
- ws.subscribeToTpsl(addr, chainId, () => {
25252
+ }));
25253
+ if (notificationsUnsub) unsubscribers.push(notificationsUnsub);
25254
+ const tpslUnsub = asUnsubscribeFn(ws.subscribeToTpsl(addr, chainId, () => {
25010
25255
  queryClient.invalidateQueries({
25011
25256
  queryKey: symmKeys.tpslOrders(accountAddress, chainId)
25012
25257
  });
25013
- });
25014
- ws.subscribeToTwapOrders(addr, chainId, () => {
25258
+ }));
25259
+ if (tpslUnsub) unsubscribers.push(tpslUnsub);
25260
+ const twapUnsub = asUnsubscribeFn(ws.subscribeToTwapOrders(addr, chainId, () => {
25015
25261
  queryClient.invalidateQueries({
25016
25262
  queryKey: symmKeys.twapOrders(accountAddress, chainId)
25017
25263
  });
25018
- });
25019
- ws.subscribeToExecutions(addr, chainId, () => {
25264
+ }));
25265
+ if (twapUnsub) unsubscribers.push(twapUnsub);
25266
+ const executionsUnsub = asUnsubscribeFn(ws.subscribeToExecutions(addr, chainId, () => {
25020
25267
  queryClient.invalidateQueries({
25021
25268
  queryKey: symmKeys.positions(accountAddress, chainId)
25022
25269
  });
25023
25270
  queryClient.invalidateQueries({
25024
25271
  queryKey: symmKeys.portfolio(accountAddress, chainId)
25025
25272
  });
25026
- });
25027
- cleanupRef.current = [removeOnConnect, removeOnDisconnect];
25273
+ }));
25274
+ if (executionsUnsub) unsubscribers.push(executionsUnsub);
25028
25275
  return () => {
25029
- cleanupRef.current.forEach((fn) => fn());
25030
- ws.unsubscribeAll();
25276
+ if (unsubscribers.length > 2) {
25277
+ unsubscribers.forEach((unsubscribe) => unsubscribe());
25278
+ } else {
25279
+ removeOnConnect();
25280
+ removeOnDisconnect();
25281
+ ws.unsubscribeAll();
25282
+ }
25031
25283
  };
25032
25284
  }, [symmCoreClient, accountAddress, chainId, queryClient]);
25033
25285
  return { isConnected };
@@ -25354,8 +25606,12 @@ function useSymmTokenSelectionMetadata(selection) {
25354
25606
  const isUnsupported = unsupportedSymbols.length > 0;
25355
25607
  const unavailableReason = isUnsupported ? `Binance market data is unavailable for ${unsupportedSymbols.join(", ")}.` : null;
25356
25608
  const symbolsKey = [...selectedSymbols].sort().join(",");
25609
+ const positionKey = [
25610
+ longTokens.map((token) => `${token.symbol}:${token.weight}`).join("|"),
25611
+ shortTokens.map((token) => `${token.symbol}:${token.weight}`).join("|")
25612
+ ].join("::");
25357
25613
  const query = useQuery({
25358
- queryKey: ["symm", "chart-metadata", symbolsKey],
25614
+ queryKey: symmKeys.chartMetadata(symbolsKey, positionKey),
25359
25615
  queryFn: async () => {
25360
25616
  const allSymbols = [
25361
25617
  ...longTokens.map((t) => ({ symbol: t.symbol, side: "long" })),
@@ -25790,7 +26046,6 @@ function useSymmChartCandles(selection) {
25790
26046
  low: longValues.l * shortValues.l,
25791
26047
  close: longValues.c * shortValues.c
25792
26048
  };
25793
- if (!bar) return;
25794
26049
  listenersRef.current.forEach((cb) => {
25795
26050
  try {
25796
26051
  cb(bar);
@@ -25815,11 +26070,14 @@ function useSymmChartCandles(selection) {
25815
26070
  }
25816
26071
  }, [emitRealtimeBar, isUnsupported, selectedSymbols]);
25817
26072
  useEffect(() => {
26073
+ if (listenersRef.current.size > 0) {
26074
+ setupWsSubscriptions();
26075
+ }
25818
26076
  return () => {
25819
26077
  wsUnsubsRef.current.forEach((unsub) => unsub());
25820
26078
  wsUnsubsRef.current = [];
25821
26079
  };
25822
- }, [longTokens, shortTokens]);
26080
+ }, [setupWsSubscriptions]);
25823
26081
  const fetchBasketCandles = useCallback(
25824
26082
  async (start, end, interval) => {
25825
26083
  const longSymbol = longTokens[0]?.symbol;
@@ -25985,10 +26243,8 @@ function useSymmPerformanceOverlays(selection) {
25985
26243
  weight: -token.weight
25986
26244
  }))
25987
26245
  ], [longTokens, shortTokens]);
25988
- const generateOverlaySymbols = useCallback(() => {
25989
- return overlays.map((o) => o.symbol);
25990
- }, [overlays]);
25991
- return { overlays, generateOverlaySymbols };
26246
+ const overlaySymbols = useMemo(() => overlays.map((o) => o.symbol), [overlays]);
26247
+ return { overlays, overlaySymbols };
25992
26248
  }
25993
26249
  function getSymmErrorMessage(error) {
25994
26250
  if (error instanceof SymmioSDKError) return error.message;
@@ -26002,6 +26258,6 @@ function getSymmErrorMessage(error) {
26002
26258
  return "An unexpected error occurred.";
26003
26259
  }
26004
26260
 
26005
- export { SymmProvider, getSymmErrorMessage, symmKeys, useSymmAccounts, useSymmApproval, useSymmAuth, useSymmBalances, useSymmChartCandles, useSymmChartSelection, useSymmCollateral, useSymmContext, useSymmCoreClient, useSymmDeposit, useSymmFunding, useSymmHedgerMarkets, useSymmMarkets, useSymmNotifications, useSymmOpenOrders, useSymmPerformanceOverlays, useSymmPortfolio, useSymmPositions, useSymmSignature, useSymmTokenSelectionMetadata, useSymmTpsl, useSymmTrade, useSymmTradeHistory, useSymmTwap, useSymmWithdraw, useSymmWs, useSymmioClient };
26261
+ export { SymmProvider, getSymmErrorMessage, symmKeys, useSymmAccounts, useSymmApproval, useSymmAuth, useSymmAvailableMargin, useSymmBalances, useSymmChartCandles, useSymmChartSelection, useSymmCollateral, useSymmContext, useSymmCoreClient, useSymmDelegation, useSymmDeposit, useSymmFunding, useSymmHedgerMarkets, useSymmInstantTrade, useSymmMarkets, useSymmNotifications, useSymmOpenOrders, useSymmPerformanceOverlays, useSymmPortfolio, useSymmPositions, useSymmSignature, useSymmTokenSelectionMetadata, useSymmTpsl, useSymmTrade, useSymmTradeHistory, useSymmTwap, useSymmWithdraw, useSymmWs, useSymmioClient };
26006
26262
  //# sourceMappingURL=index.mjs.map
26007
26263
  //# sourceMappingURL=index.mjs.map