@pear-protocol/symmio-client 0.3.12 → 0.3.14

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.
@@ -72,6 +72,9 @@ function toBinanceSymbol(symmSymbol) {
72
72
  // src/utils/binance-ws.ts
73
73
  var BINANCE_WS_URL = "wss://fstream.binance.com/market/ws";
74
74
  var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
75
+ var IDLE_CLOSE_DELAY_MS = 3e4;
76
+ var STALE_CONNECTION_MS = 3e4;
77
+ var STALE_CHECK_INTERVAL_MS = 1e4;
75
78
  var STABLE_QUOTES = ["USDT0", "USDT", "USDC", "USDE", "USDH", "USD"];
76
79
  function normalizeBaseSymbol(symbol) {
77
80
  const normalized = symbol.toUpperCase().trim();
@@ -121,8 +124,11 @@ var BinanceWsManager = class {
121
124
  streams = /* @__PURE__ */ new Map();
122
125
  reconnectAttempt = 0;
123
126
  reconnectTimer = null;
127
+ idleCloseTimer = null;
128
+ staleCheckTimer = null;
124
129
  intentionalClose = false;
125
- pendingSubscribes = [];
130
+ pendingSubscribes = /* @__PURE__ */ new Set();
131
+ lastMessageAt = 0;
126
132
  idCounter = 0;
127
133
  /**
128
134
  * Subscribe to a kline stream. Returns an unsubscribe function.
@@ -195,10 +201,10 @@ var BinanceWsManager = class {
195
201
  */
196
202
  destroy() {
197
203
  this.intentionalClose = true;
198
- if (this.reconnectTimer) {
199
- clearTimeout(this.reconnectTimer);
200
- this.reconnectTimer = null;
201
- }
204
+ this.clearReconnectTimer();
205
+ this.clearIdleCloseTimer();
206
+ this.clearStaleCheckTimer();
207
+ this.pendingSubscribes.clear();
202
208
  if (this.ws) {
203
209
  this.ws.close();
204
210
  this.ws = null;
@@ -218,6 +224,7 @@ var BinanceWsManager = class {
218
224
  }
219
225
  sub.callbacks.set(id, cb);
220
226
  if (isNew) {
227
+ this.clearIdleCloseTimer();
221
228
  this.ensureConnected();
222
229
  this.sendSubscribe([streamName]);
223
230
  }
@@ -229,18 +236,14 @@ var BinanceWsManager = class {
229
236
  if (sub.callbacks.size === 0) {
230
237
  this.streams.delete(streamName);
231
238
  this.sendUnsubscribe([streamName]);
232
- if (this.streams.size === 0 && this.ws) {
233
- this.intentionalClose = true;
234
- this.ws.close();
235
- this.ws = null;
236
- this.intentionalClose = false;
237
- }
239
+ this.scheduleIdleClose();
238
240
  }
239
241
  }
240
242
  ensureConnected() {
241
243
  if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
242
244
  return;
243
245
  }
246
+ this.clearReconnectTimer();
244
247
  this.connect();
245
248
  }
246
249
  connect() {
@@ -249,23 +252,27 @@ var BinanceWsManager = class {
249
252
  this.ws = new WebSocket(BINANCE_WS_URL);
250
253
  this.ws.onopen = () => {
251
254
  this.reconnectAttempt = 0;
252
- const activeStreams = Array.from(this.streams.keys());
255
+ this.lastMessageAt = Date.now();
256
+ this.startStaleCheck();
257
+ const activeStreams = Array.from(
258
+ /* @__PURE__ */ new Set([...this.streams.keys(), ...this.pendingSubscribes])
259
+ );
260
+ this.pendingSubscribes.clear();
253
261
  if (activeStreams.length > 0) {
254
262
  this.sendSubscribe(activeStreams);
255
263
  }
256
- if (this.pendingSubscribes.length > 0) {
257
- this.sendSubscribe(this.pendingSubscribes);
258
- this.pendingSubscribes = [];
259
- }
260
264
  };
261
265
  this.ws.onmessage = (event) => {
262
266
  try {
263
267
  const data = JSON.parse(event.data);
268
+ this.lastMessageAt = Date.now();
264
269
  this.handleMessage(data);
265
270
  } catch {
266
271
  }
267
272
  };
268
273
  this.ws.onclose = () => {
274
+ this.ws = null;
275
+ this.clearStaleCheckTimer();
269
276
  if (this.intentionalClose) return;
270
277
  this.scheduleReconnect();
271
278
  };
@@ -299,25 +306,27 @@ var BinanceWsManager = class {
299
306
  }
300
307
  sendSubscribe(streams) {
301
308
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
302
- this.pendingSubscribes.push(...streams);
309
+ streams.forEach((stream) => this.pendingSubscribes.add(stream));
303
310
  return;
304
311
  }
312
+ const params = Array.from(new Set(streams));
313
+ if (params.length === 0) return;
305
314
  this.ws.send(JSON.stringify({
306
315
  method: "SUBSCRIBE",
307
- params: streams,
316
+ params,
308
317
  id: Date.now()
309
318
  }));
310
319
  }
311
320
  sendUnsubscribe(streams) {
312
321
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
313
- this.pendingSubscribes = this.pendingSubscribes.filter(
314
- (s) => !streams.includes(s)
315
- );
322
+ streams.forEach((stream) => this.pendingSubscribes.delete(stream));
316
323
  return;
317
324
  }
325
+ const params = Array.from(new Set(streams));
326
+ if (params.length === 0) return;
318
327
  this.ws.send(JSON.stringify({
319
328
  method: "UNSUBSCRIBE",
320
- params: streams,
329
+ params,
321
330
  id: Date.now()
322
331
  }));
323
332
  }
@@ -331,6 +340,44 @@ var BinanceWsManager = class {
331
340
  this.connect();
332
341
  }, delay);
333
342
  }
343
+ scheduleIdleClose() {
344
+ if (this.streams.size > 0 || this.idleCloseTimer) return;
345
+ this.clearReconnectTimer();
346
+ if (!this.ws) return;
347
+ this.idleCloseTimer = setTimeout(() => {
348
+ this.idleCloseTimer = null;
349
+ if (this.streams.size > 0 || !this.ws) return;
350
+ this.intentionalClose = true;
351
+ this.ws.close();
352
+ this.ws = null;
353
+ this.intentionalClose = false;
354
+ this.clearStaleCheckTimer();
355
+ }, IDLE_CLOSE_DELAY_MS);
356
+ }
357
+ startStaleCheck() {
358
+ this.clearStaleCheckTimer();
359
+ this.staleCheckTimer = setInterval(() => {
360
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
361
+ if (this.streams.size === 0) return;
362
+ if (Date.now() - this.lastMessageAt < STALE_CONNECTION_MS) return;
363
+ this.ws.close();
364
+ }, STALE_CHECK_INTERVAL_MS);
365
+ }
366
+ clearReconnectTimer() {
367
+ if (!this.reconnectTimer) return;
368
+ clearTimeout(this.reconnectTimer);
369
+ this.reconnectTimer = null;
370
+ }
371
+ clearIdleCloseTimer() {
372
+ if (!this.idleCloseTimer) return;
373
+ clearTimeout(this.idleCloseTimer);
374
+ this.idleCloseTimer = null;
375
+ }
376
+ clearStaleCheckTimer() {
377
+ if (!this.staleCheckTimer) return;
378
+ clearInterval(this.staleCheckTimer);
379
+ this.staleCheckTimer = null;
380
+ }
334
381
  };
335
382
  var _instance = null;
336
383
  function getBinanceWsManager() {
@@ -361,6 +408,28 @@ function getNextRefCount(binanceSymbol) {
361
408
  function getPrevRefCount(binanceSymbol) {
362
409
  return Math.max(0, (refCounts.get(binanceSymbol) ?? 0) - 1);
363
410
  }
411
+ function addMappedSymbol(binanceSymbol, symmSymbol) {
412
+ const symbols = streamSymbols.get(binanceSymbol) ?? /* @__PURE__ */ new Map();
413
+ symbols.set(symmSymbol, (symbols.get(symmSymbol) ?? 0) + 1);
414
+ streamSymbols.set(binanceSymbol, symbols);
415
+ }
416
+ function removeMappedSymbol(binanceSymbol, symmSymbol) {
417
+ const symbols = streamSymbols.get(binanceSymbol);
418
+ if (!symbols) return false;
419
+ const currentCount = symbols.get(symmSymbol) ?? 0;
420
+ if (currentCount <= 0) return false;
421
+ if (currentCount === 1) {
422
+ symbols.delete(symmSymbol);
423
+ } else {
424
+ symbols.set(symmSymbol, currentCount - 1);
425
+ }
426
+ if (symbols.size === 0) {
427
+ streamSymbols.delete(binanceSymbol);
428
+ } else {
429
+ streamSymbols.set(binanceSymbol, symbols);
430
+ }
431
+ return true;
432
+ }
364
433
  var useBinanceMarkPriceStore = zustand.create((set) => ({
365
434
  markPrices: {},
366
435
  fundingRates: {},
@@ -369,9 +438,7 @@ var useBinanceMarkPriceStore = zustand.create((set) => ({
369
438
  const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
370
439
  const nextRefCount = getNextRefCount(binanceSymbol);
371
440
  refCounts.set(binanceSymbol, nextRefCount);
372
- const symbols = streamSymbols.get(binanceSymbol) ?? /* @__PURE__ */ new Set();
373
- symbols.add(symmSymbol);
374
- streamSymbols.set(binanceSymbol, symbols);
441
+ addMappedSymbol(binanceSymbol, symmSymbol);
375
442
  if (allMarkPricesRefCount === 0) {
376
443
  const wsManager = getBinanceWsManager();
377
444
  allMarkPricesUnsubscribe = wsManager.subscribeAllMarkPrices((entries) => {
@@ -386,7 +453,7 @@ var useBinanceMarkPriceStore = zustand.create((set) => ({
386
453
  nextMarkPrices ??= { ...state.markPrices };
387
454
  nextFundingRates ??= { ...state.fundingRates };
388
455
  nextFundingTimes ??= { ...state.nextFundingTimes };
389
- mappedSymbols.forEach((mappedSymbol) => {
456
+ mappedSymbols.forEach((_count, mappedSymbol) => {
390
457
  nextMarkPrices[mappedSymbol] = entry.markPrice;
391
458
  nextFundingRates[mappedSymbol] = entry.fundingRate;
392
459
  nextFundingTimes[mappedSymbol] = entry.nextFundingTime;
@@ -407,14 +474,9 @@ var useBinanceMarkPriceStore = zustand.create((set) => ({
407
474
  },
408
475
  unsubscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
409
476
  const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
410
- const symbols = streamSymbols.get(binanceSymbol);
411
- if (symbols) {
412
- symbols.delete(symmSymbol);
413
- if (symbols.size === 0) {
414
- streamSymbols.delete(binanceSymbol);
415
- } else {
416
- streamSymbols.set(binanceSymbol, symbols);
417
- }
477
+ const removedSubscription = removeMappedSymbol(binanceSymbol, symmSymbol);
478
+ if (!removedSubscription) {
479
+ return;
418
480
  }
419
481
  const nextRefCount = getPrevRefCount(binanceSymbol);
420
482
  if (nextRefCount === 0) {
@@ -428,6 +490,10 @@ var useBinanceMarkPriceStore = zustand.create((set) => ({
428
490
  allMarkPricesUnsubscribe = null;
429
491
  }
430
492
  set((state) => {
493
+ const hasMappedSymbol = Boolean(streamSymbols.get(binanceSymbol)?.has(symmSymbol));
494
+ if (hasMappedSymbol) {
495
+ return state;
496
+ }
431
497
  if (state.markPrices[symmSymbol] == null && state.fundingRates[symmSymbol] == null && state.nextFundingTimes[symmSymbol] == null) {
432
498
  return state;
433
499
  }
@@ -512,7 +578,7 @@ var symmKeys = {
512
578
  signature: (address, chainId) => ["symm", "signature", address, chainId],
513
579
  auth: (accountAddress, chainId, signerAddress) => ["symm", "auth", accountAddress, chainId, signerAddress],
514
580
  approval: (owner, spender, chainId, token) => ["symm", "approval", owner, spender, chainId, token],
515
- balances: (address, chainId) => ["symm", "balances", address, chainId],
581
+ balances: (address, chainId, multiAccountAddress) => multiAccountAddress === void 0 ? ["symm", "balances", address, chainId] : ["symm", "balances", address, chainId, multiAccountAddress],
516
582
  positions: (params) => ["symm", "positions", params],
517
583
  openOrders: (params) => ["symm", "openOrders", params],
518
584
  tradeHistory: (params) => ["symm", "tradeHistory", params],
@@ -545,8 +611,33 @@ var useSymmWsStore = zustand.create((set) => ({
545
611
  }));
546
612
 
547
613
  // src/react/hooks/use-symm-ws.ts
548
- function asUnsubscribeFn(value) {
549
- return typeof value === "function" ? value : null;
614
+ var wsOwnerCounts = /* @__PURE__ */ new WeakMap();
615
+ var wsConnectPromises = /* @__PURE__ */ new WeakMap();
616
+ function addWsOwner(ws) {
617
+ wsOwnerCounts.set(ws, (wsOwnerCounts.get(ws) ?? 0) + 1);
618
+ }
619
+ function removeWsOwner(ws) {
620
+ const nextCount = Math.max(0, (wsOwnerCounts.get(ws) ?? 0) - 1);
621
+ if (nextCount === 0) {
622
+ wsOwnerCounts.delete(ws);
623
+ } else {
624
+ wsOwnerCounts.set(ws, nextCount);
625
+ }
626
+ return nextCount;
627
+ }
628
+ function connectShared(ws) {
629
+ if (ws.isConnected()) {
630
+ return Promise.resolve();
631
+ }
632
+ const existing = wsConnectPromises.get(ws);
633
+ if (existing) {
634
+ return existing;
635
+ }
636
+ const promise = ws.connect().finally(() => {
637
+ wsConnectPromises.delete(ws);
638
+ });
639
+ wsConnectPromises.set(ws, promise);
640
+ return promise;
550
641
  }
551
642
  function useSymmWs(params = {}) {
552
643
  const ctx = react.useContext(SymmContext);
@@ -563,114 +654,76 @@ function useSymmWs(params = {}) {
563
654
  }
564
655
  const ws = symmCoreClient.ws;
565
656
  const addr = accountAddress;
566
- const unsubscribers = [];
567
657
  let cancelled = false;
568
- const removeOnConnect = ws.onConnect(() => {
569
- setConnected(true);
658
+ addWsOwner(ws);
659
+ const removeOnConnect = ws.onConnect(() => setConnected(true));
660
+ const removeOnDisconnect = ws.onDisconnect(() => setConnected(false));
661
+ const subscriptions = [];
662
+ const addSubscription = (channel, handler) => {
663
+ ws.subscribe(channel, addr, chainId, handler);
664
+ subscriptions.push({ channel, handler });
665
+ };
666
+ addSubscription("positions", () => {
667
+ queryClient.invalidateQueries({ queryKey: symmKeys.positionsRoot });
570
668
  });
571
- const removeOnDisconnect = ws.onDisconnect(() => {
572
- setConnected(false);
669
+ addSubscription("open-orders", () => {
670
+ queryClient.invalidateQueries({ queryKey: symmKeys.openOrdersRoot });
573
671
  });
574
- const removeOnError = asUnsubscribeFn(ws.onError?.(() => {
575
- }));
576
- const removeOnWelcome = asUnsubscribeFn(ws.onWelcome?.(() => {
577
- }));
578
- unsubscribers.push(removeOnConnect, removeOnDisconnect);
579
- if (removeOnError) unsubscribers.push(removeOnError);
580
- if (removeOnWelcome) unsubscribers.push(removeOnWelcome);
581
- const positionsUnsub = asUnsubscribeFn(
582
- ws.subscribeToPositions(addr, chainId, () => {
583
- queryClient.invalidateQueries({
584
- queryKey: symmKeys.positionsRoot
585
- });
586
- })
587
- );
588
- if (positionsUnsub) unsubscribers.push(positionsUnsub);
589
- const openOrdersUnsub = asUnsubscribeFn(
590
- ws.subscribeToOpenOrders(addr, chainId, () => {
591
- queryClient.invalidateQueries({
592
- queryKey: symmKeys.openOrdersRoot
593
- });
594
- })
595
- );
596
- if (openOrdersUnsub) unsubscribers.push(openOrdersUnsub);
597
- const tradesUnsub = asUnsubscribeFn(
598
- ws.subscribeToTrades(addr, chainId, () => {
599
- queryClient.invalidateQueries({
600
- queryKey: symmKeys.tradeHistoryRoot
601
- });
602
- })
603
- );
604
- if (tradesUnsub) unsubscribers.push(tradesUnsub);
605
- const accountSummaryUnsub = asUnsubscribeFn(
606
- ws.subscribeToAccountSummary(addr, chainId, () => {
607
- queryClient.invalidateQueries({
608
- queryKey: symmKeys.balances(accountAddress, chainId)
609
- });
610
- queryClient.invalidateQueries({
611
- queryKey: symmKeys.accountSummary(accountAddress, chainId)
612
- });
613
- })
614
- );
615
- if (accountSummaryUnsub) unsubscribers.push(accountSummaryUnsub);
616
- const notificationsUnsub = asUnsubscribeFn(
617
- ws.subscribeToNotifications(addr, chainId, () => {
618
- queryClient.invalidateQueries({
619
- queryKey: symmKeys.notifications({ accountAddress, chainId })
620
- });
621
- queryClient.invalidateQueries({
622
- queryKey: symmKeys.unreadCount({ accountAddress, chainId })
623
- });
624
- })
625
- );
626
- if (notificationsUnsub) unsubscribers.push(notificationsUnsub);
627
- const tpslUnsub = asUnsubscribeFn(
628
- ws.subscribeToTpsl(addr, chainId, () => {
629
- queryClient.invalidateQueries({ queryKey: symmKeys.tpslOrdersRoot });
630
- queryClient.invalidateQueries({ queryKey: symmKeys.openOrdersRoot });
631
- })
632
- );
633
- if (tpslUnsub) unsubscribers.push(tpslUnsub);
634
- const twapUnsub = asUnsubscribeFn(
635
- ws.subscribeToTwapOrders(addr, chainId, () => {
636
- queryClient.invalidateQueries({ queryKey: symmKeys.twapOrdersRoot });
637
- queryClient.invalidateQueries({ queryKey: symmKeys.openOrdersRoot });
638
- })
639
- );
640
- if (twapUnsub) unsubscribers.push(twapUnsub);
641
- const triggerOrdersUnsub = asUnsubscribeFn(
642
- ws.subscribeToTriggerOrders(addr, chainId, () => {
643
- queryClient.invalidateQueries({ queryKey: symmKeys.triggerOrdersRoot });
644
- queryClient.invalidateQueries({ queryKey: symmKeys.openOrdersRoot });
645
- })
646
- );
647
- if (triggerOrdersUnsub) unsubscribers.push(triggerOrdersUnsub);
648
- const executionsUnsub = asUnsubscribeFn(
649
- ws.subscribeToExecutions(addr, chainId, () => {
650
- queryClient.invalidateQueries({
651
- queryKey: symmKeys.positionsRoot
652
- });
653
- queryClient.invalidateQueries({
654
- queryKey: symmKeys.portfolioRoot
655
- });
656
- })
657
- );
658
- if (executionsUnsub) unsubscribers.push(executionsUnsub);
659
- void ws.connect().then(() => {
660
- if (cancelled) {
661
- return;
672
+ addSubscription("trades", () => {
673
+ queryClient.invalidateQueries({ queryKey: symmKeys.tradeHistoryRoot });
674
+ });
675
+ addSubscription("account-summary", () => {
676
+ queryClient.invalidateQueries({
677
+ queryKey: symmKeys.balances(accountAddress, chainId)
678
+ });
679
+ queryClient.invalidateQueries({
680
+ queryKey: symmKeys.accountSummary(accountAddress, chainId)
681
+ });
682
+ });
683
+ addSubscription("notifications", () => {
684
+ queryClient.invalidateQueries({
685
+ queryKey: symmKeys.notifications({ accountAddress, chainId })
686
+ });
687
+ queryClient.invalidateQueries({
688
+ queryKey: symmKeys.unreadCount({ accountAddress, chainId })
689
+ });
690
+ });
691
+ addSubscription("tpsl", () => {
692
+ queryClient.invalidateQueries({ queryKey: symmKeys.tpslOrdersRoot });
693
+ queryClient.invalidateQueries({ queryKey: symmKeys.openOrdersRoot });
694
+ });
695
+ addSubscription("twap-orders", () => {
696
+ queryClient.invalidateQueries({ queryKey: symmKeys.twapOrdersRoot });
697
+ queryClient.invalidateQueries({ queryKey: symmKeys.openOrdersRoot });
698
+ });
699
+ addSubscription("trigger-orders", () => {
700
+ queryClient.invalidateQueries({ queryKey: symmKeys.triggerOrdersRoot });
701
+ queryClient.invalidateQueries({ queryKey: symmKeys.openOrdersRoot });
702
+ });
703
+ addSubscription("executions", () => {
704
+ queryClient.invalidateQueries({ queryKey: symmKeys.positionsRoot });
705
+ queryClient.invalidateQueries({ queryKey: symmKeys.portfolioRoot });
706
+ });
707
+ void connectShared(ws).then(() => {
708
+ if (cancelled) return;
709
+ if (ws.isConnected()) {
710
+ setConnected(true);
662
711
  }
663
712
  }).catch(() => {
664
- if (cancelled) {
665
- return;
666
- }
713
+ if (cancelled) return;
667
714
  setConnected(false);
668
715
  });
669
716
  return () => {
670
717
  cancelled = true;
671
- unsubscribers.forEach((unsubscribe) => unsubscribe());
672
- ws.disconnect();
673
- setConnected(false);
718
+ removeOnConnect();
719
+ removeOnDisconnect();
720
+ subscriptions.forEach(({ channel, handler }) => {
721
+ ws.unsubscribe(channel, addr, chainId, handler);
722
+ });
723
+ if (removeWsOwner(ws) === 0) {
724
+ ws.disconnect();
725
+ setConnected(false);
726
+ }
674
727
  };
675
728
  }, [symmCoreClient, accountAddress, chainId, queryClient, setConnected]);
676
729
  return { isConnected };
@@ -25589,7 +25642,7 @@ function useSymmBalances(params) {
25589
25642
  const internalEnabled = !!symmCoreClient && !!userAddress;
25590
25643
  return reactQuery.useQuery({
25591
25644
  ...params.query,
25592
- queryKey: symmKeys.balances(userAddress, chainId),
25645
+ queryKey: symmKeys.balances(userAddress, chainId, multiAccountAddress),
25593
25646
  queryFn: () => symmCoreClient.accounts.getBalanceInfo({
25594
25647
  userAddress,
25595
25648
  chainId,
@@ -25598,6 +25651,242 @@ function useSymmBalances(params) {
25598
25651
  enabled: internalEnabled && (params.query?.enabled ?? true)
25599
25652
  });
25600
25653
  }
25654
+
25655
+ // src/utils/account-overview.ts
25656
+ var BALANCE_INFO_KEYS = [
25657
+ "allocatedBalance",
25658
+ "allocated_balance",
25659
+ "cva",
25660
+ "lf",
25661
+ "partyAmm",
25662
+ "partyAMM",
25663
+ "party_a_mm",
25664
+ "mm",
25665
+ "lockedPartyAMM",
25666
+ "upnl"
25667
+ ];
25668
+ function isRecord(value) {
25669
+ return typeof value === "object" && value !== null;
25670
+ }
25671
+ function hasBalanceInfoShape(value) {
25672
+ return isRecord(value) && BALANCE_INFO_KEYS.some((key) => key in value);
25673
+ }
25674
+ function readValue(source, keys) {
25675
+ for (const key of keys) {
25676
+ if (key in source) {
25677
+ return source[key];
25678
+ }
25679
+ }
25680
+ return void 0;
25681
+ }
25682
+ function toNumber(value) {
25683
+ if (value === null || value === void 0 || value === "") {
25684
+ return 0;
25685
+ }
25686
+ if (typeof value === "bigint") {
25687
+ return Number(value);
25688
+ }
25689
+ const parsed = Number(value);
25690
+ return Number.isFinite(parsed) ? parsed : 0;
25691
+ }
25692
+ function toDisplayString(value) {
25693
+ if (Object.is(value, -0) || Math.abs(value) < Number.EPSILON) {
25694
+ return "0";
25695
+ }
25696
+ return String(value);
25697
+ }
25698
+ function readNumber(source, keys) {
25699
+ return toNumber(readValue(source, keys));
25700
+ }
25701
+ function readTimestamp(source) {
25702
+ const timestamp = readValue(source, ["timestamp"]);
25703
+ if (timestamp === void 0 || timestamp === null) {
25704
+ return void 0;
25705
+ }
25706
+ const parsed = Number(timestamp);
25707
+ return Number.isFinite(parsed) ? parsed : void 0;
25708
+ }
25709
+ function getBalanceInfoContainer(response) {
25710
+ if (!isRecord(response)) {
25711
+ return void 0;
25712
+ }
25713
+ if (hasBalanceInfoShape(response)) {
25714
+ return response;
25715
+ }
25716
+ const data = response.data;
25717
+ if (isRecord(data)) {
25718
+ return data;
25719
+ }
25720
+ return response;
25721
+ }
25722
+ function getSymmAccountBalanceInfo(response, accountAddress) {
25723
+ const container = getBalanceInfoContainer(response);
25724
+ if (!container) {
25725
+ return void 0;
25726
+ }
25727
+ if (hasBalanceInfoShape(container)) {
25728
+ return container;
25729
+ }
25730
+ if (accountAddress) {
25731
+ const normalizedAddress = accountAddress.toLowerCase();
25732
+ const direct = container[accountAddress] ?? container[normalizedAddress];
25733
+ if (isRecord(direct) && hasBalanceInfoShape(direct)) {
25734
+ return direct;
25735
+ }
25736
+ const matchingEntry = Object.entries(container).find(
25737
+ ([key]) => key.toLowerCase() === normalizedAddress
25738
+ );
25739
+ if (isRecord(matchingEntry?.[1]) && hasBalanceInfoShape(matchingEntry[1])) {
25740
+ return matchingEntry[1];
25741
+ }
25742
+ }
25743
+ const values = Object.values(container);
25744
+ if (values.length === 1 && isRecord(values[0]) && hasBalanceInfoShape(values[0])) {
25745
+ return values[0];
25746
+ }
25747
+ return void 0;
25748
+ }
25749
+ function computeSymmAccountOverview({
25750
+ balanceInfo,
25751
+ upnl: upnlOverride,
25752
+ netDeposited
25753
+ }) {
25754
+ const allocatedBalance = readNumber(balanceInfo, [
25755
+ "allocatedBalance",
25756
+ "allocated_balance"
25757
+ ]);
25758
+ const cva = readNumber(balanceInfo, ["cva", "lockedCVA", "locked_cva"]);
25759
+ const lf = readNumber(balanceInfo, ["lf", "lockedLF", "locked_lf"]);
25760
+ const partyAmm = readNumber(balanceInfo, [
25761
+ "partyAmm",
25762
+ "partyAMM",
25763
+ "party_a_mm",
25764
+ "mm",
25765
+ "lockedPartyAMM",
25766
+ "locked_party_a_mm"
25767
+ ]);
25768
+ const pendingCva = readNumber(balanceInfo, [
25769
+ "pendingCva",
25770
+ "pending_cva",
25771
+ "pendingLockedCVA",
25772
+ "pending_locked_cva"
25773
+ ]);
25774
+ const pendingLf = readNumber(balanceInfo, [
25775
+ "pendingLf",
25776
+ "pending_lf",
25777
+ "pendingLockedLF",
25778
+ "pending_locked_lf"
25779
+ ]);
25780
+ const pendingPartyAmm = readNumber(balanceInfo, [
25781
+ "pendingPartyAmm",
25782
+ "pendingPartyAMM",
25783
+ "pending_party_a_mm",
25784
+ "pendingMm",
25785
+ "pending_mm",
25786
+ "pendingLockedPartyAMM",
25787
+ "pending_locked_party_a_mm"
25788
+ ]);
25789
+ const upnl = upnlOverride === void 0 ? readNumber(balanceInfo, ["upnl"]) : toNumber(upnlOverride);
25790
+ const computedTotalLocked = cva + lf + partyAmm;
25791
+ const totalLocked = readValue(
25792
+ balanceInfo,
25793
+ ["totalLocked", "total_locked"]
25794
+ ) === void 0 ? computedTotalLocked : readNumber(balanceInfo, ["totalLocked", "total_locked"]);
25795
+ const totalPendingLocked = readValue(balanceInfo, [
25796
+ "totalPendingLocked",
25797
+ "total_pending_locked"
25798
+ ]) === void 0 ? pendingCva + pendingLf + pendingPartyAmm : readNumber(balanceInfo, ["totalPendingLocked", "total_pending_locked"]);
25799
+ const maintenanceMargin = cva + lf;
25800
+ const equity = allocatedBalance + upnl;
25801
+ const availableForOrder = upnl >= 0 ? allocatedBalance + upnl - totalLocked - totalPendingLocked : allocatedBalance - cva - lf - totalPendingLocked - Math.max(Math.abs(upnl), partyAmm);
25802
+ const availableBalance = readNumber(balanceInfo, [
25803
+ "availableBalance",
25804
+ "available_balance"
25805
+ ]);
25806
+ const notional = readNumber(balanceInfo, ["notional"]);
25807
+ return {
25808
+ allocatedBalance: toDisplayString(allocatedBalance),
25809
+ upnl: toDisplayString(upnl),
25810
+ equity: toDisplayString(equity),
25811
+ totalAccountValue: toDisplayString(equity),
25812
+ maintenanceMargin: toDisplayString(maintenanceMargin),
25813
+ totalLocked: toDisplayString(totalLocked),
25814
+ marginUsed: toDisplayString(computedTotalLocked),
25815
+ totalPendingLocked: toDisplayString(totalPendingLocked),
25816
+ availableForOrder: toDisplayString(availableForOrder),
25817
+ availableMargin: toDisplayString(availableForOrder),
25818
+ availableBalance: toDisplayString(availableBalance),
25819
+ netDeposited: toDisplayString(toNumber(netDeposited)),
25820
+ notional: toDisplayString(notional),
25821
+ timestamp: readTimestamp(balanceInfo)
25822
+ };
25823
+ }
25824
+ function computeSymmNetDeposited(totals, decimals = 6) {
25825
+ if (!totals) {
25826
+ return "0";
25827
+ }
25828
+ const deposit2 = parseRawCollateralUnits(totals.deposit);
25829
+ const withdraw2 = parseRawCollateralUnits(totals.withdraw);
25830
+ const value = deposit2 - withdraw2;
25831
+ const negative = value < 0n;
25832
+ const absolute = negative ? -value : value;
25833
+ const scale = 10n ** BigInt(decimals);
25834
+ const whole = absolute / scale;
25835
+ const fraction = absolute % scale;
25836
+ const fractionText = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
25837
+ const amount = fractionText ? `${whole}.${fractionText}` : whole.toString();
25838
+ return negative ? `-${amount}` : amount;
25839
+ }
25840
+ function parseRawCollateralUnits(value) {
25841
+ if (value === null || value === void 0 || value === "") {
25842
+ return 0n;
25843
+ }
25844
+ if (typeof value === "bigint") {
25845
+ return value;
25846
+ }
25847
+ if (typeof value === "number") {
25848
+ if (!Number.isSafeInteger(value)) {
25849
+ throw new Error("Raw collateral unit numbers must be safe integers");
25850
+ }
25851
+ return BigInt(value);
25852
+ }
25853
+ if (!/^-?\d+$/.test(value)) {
25854
+ throw new Error("Raw collateral unit strings must be base-10 integers");
25855
+ }
25856
+ return BigInt(value);
25857
+ }
25858
+
25859
+ // src/react/hooks/use-symm-account-overview.ts
25860
+ function useSymmAccountOverview(params) {
25861
+ const {
25862
+ userAddress,
25863
+ accountAddress,
25864
+ multiAccountAddress,
25865
+ upnl,
25866
+ netDeposited
25867
+ } = params;
25868
+ const query = useSymmBalances({
25869
+ userAddress,
25870
+ multiAccountAddress,
25871
+ chainId: params.chainId,
25872
+ query: params.query
25873
+ });
25874
+ const data = react.useMemo(() => {
25875
+ if (query.data === void 0) {
25876
+ return void 0;
25877
+ }
25878
+ const balanceInfo = getSymmAccountBalanceInfo(
25879
+ query.data,
25880
+ accountAddress ?? userAddress
25881
+ );
25882
+ return {
25883
+ response: query.data,
25884
+ balanceInfo,
25885
+ overview: balanceInfo ? computeSymmAccountOverview({ balanceInfo, upnl, netDeposited }) : void 0
25886
+ };
25887
+ }, [accountAddress, netDeposited, query.data, upnl, userAddress]);
25888
+ return { ...query, data };
25889
+ }
25601
25890
  function useSymmOpenBasketMutation(options) {
25602
25891
  const { symmCoreClient } = useSymmContext();
25603
25892
  const queryClient = reactQuery.useQueryClient();
@@ -27171,10 +27460,14 @@ function getSymmErrorMessage(error) {
27171
27460
  }
27172
27461
 
27173
27462
  exports.SymmProvider = SymmProvider;
27463
+ exports.computeSymmAccountOverview = computeSymmAccountOverview;
27464
+ exports.computeSymmNetDeposited = computeSymmNetDeposited;
27465
+ exports.getSymmAccountBalanceInfo = getSymmAccountBalanceInfo;
27174
27466
  exports.getSymmErrorMessage = getSymmErrorMessage;
27175
27467
  exports.symmKeys = symmKeys;
27176
27468
  exports.useBinanceMarkPriceStore = useBinanceMarkPriceStore;
27177
27469
  exports.useSymmAccountData = useSymmAccountData;
27470
+ exports.useSymmAccountOverview = useSymmAccountOverview;
27178
27471
  exports.useSymmAccountSummary = useSymmAccountSummary;
27179
27472
  exports.useSymmAccountsApi = useSymmAccountsApi;
27180
27473
  exports.useSymmAccountsLength = useSymmAccountsLength;