@lifi/perps-sdk-provider-ondo 17.0.0 → 18.0.0

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.
Files changed (75) hide show
  1. package/dist/cjs/OndoProvider.d.ts.map +1 -1
  2. package/dist/cjs/OndoProvider.js +22 -10
  3. package/dist/cjs/OndoProvider.js.map +1 -1
  4. package/dist/cjs/accountSummary.d.ts +2 -2
  5. package/dist/cjs/accountSummary.d.ts.map +1 -1
  6. package/dist/cjs/accountSummary.js +24 -2
  7. package/dist/cjs/accountSummary.js.map +1 -1
  8. package/dist/cjs/utils/collateralAsset.d.ts +3 -0
  9. package/dist/cjs/utils/collateralAsset.d.ts.map +1 -0
  10. package/dist/cjs/utils/collateralAsset.js +19 -0
  11. package/dist/cjs/utils/collateralAsset.js.map +1 -0
  12. package/dist/cjs/utils/decimal.d.ts +3 -0
  13. package/dist/cjs/utils/decimal.d.ts.map +1 -0
  14. package/dist/cjs/utils/decimal.js +22 -0
  15. package/dist/cjs/utils/decimal.js.map +1 -0
  16. package/dist/cjs/utils/index.d.ts +2 -0
  17. package/dist/cjs/utils/index.d.ts.map +1 -1
  18. package/dist/cjs/utils/index.js +5 -1
  19. package/dist/cjs/utils/index.js.map +1 -1
  20. package/dist/cjs/utils/withdrawableBalances.d.ts +4 -0
  21. package/dist/cjs/utils/withdrawableBalances.d.ts.map +1 -0
  22. package/dist/cjs/utils/withdrawableBalances.js +12 -0
  23. package/dist/cjs/utils/withdrawableBalances.js.map +1 -0
  24. package/dist/cjs/websocket/OndoWsProvider.d.ts +3 -1
  25. package/dist/cjs/websocket/OndoWsProvider.d.ts.map +1 -1
  26. package/dist/cjs/websocket/OndoWsProvider.js +28 -27
  27. package/dist/cjs/websocket/OndoWsProvider.js.map +1 -1
  28. package/dist/esm/OndoProvider.d.ts.map +1 -1
  29. package/dist/esm/OndoProvider.js +24 -12
  30. package/dist/esm/OndoProvider.js.map +1 -1
  31. package/dist/esm/accountSummary.d.ts +6 -7
  32. package/dist/esm/accountSummary.d.ts.map +1 -1
  33. package/dist/esm/accountSummary.js +29 -8
  34. package/dist/esm/accountSummary.js.map +1 -1
  35. package/dist/esm/utils/collateralAsset.d.ts +10 -0
  36. package/dist/esm/utils/collateralAsset.d.ts.map +1 -0
  37. package/dist/esm/utils/collateralAsset.js +22 -0
  38. package/dist/esm/utils/collateralAsset.js.map +1 -0
  39. package/dist/esm/utils/decimal.d.ts +10 -0
  40. package/dist/esm/utils/decimal.d.ts.map +1 -0
  41. package/dist/esm/utils/decimal.js +22 -0
  42. package/dist/esm/utils/decimal.js.map +1 -0
  43. package/dist/esm/utils/index.d.ts +2 -0
  44. package/dist/esm/utils/index.d.ts.map +1 -1
  45. package/dist/esm/utils/index.js +2 -0
  46. package/dist/esm/utils/index.js.map +1 -1
  47. package/dist/esm/utils/withdrawableBalances.d.ts +13 -0
  48. package/dist/esm/utils/withdrawableBalances.d.ts.map +1 -0
  49. package/dist/esm/utils/withdrawableBalances.js +17 -0
  50. package/dist/esm/utils/withdrawableBalances.js.map +1 -0
  51. package/dist/esm/websocket/OndoWsProvider.d.ts +15 -12
  52. package/dist/esm/websocket/OndoWsProvider.d.ts.map +1 -1
  53. package/dist/esm/websocket/OndoWsProvider.js +41 -39
  54. package/dist/esm/websocket/OndoWsProvider.js.map +1 -1
  55. package/dist/types/OndoProvider.d.ts.map +1 -1
  56. package/dist/types/accountSummary.d.ts +6 -7
  57. package/dist/types/accountSummary.d.ts.map +1 -1
  58. package/dist/types/utils/collateralAsset.d.ts +10 -0
  59. package/dist/types/utils/collateralAsset.d.ts.map +1 -0
  60. package/dist/types/utils/decimal.d.ts +10 -0
  61. package/dist/types/utils/decimal.d.ts.map +1 -0
  62. package/dist/types/utils/index.d.ts +2 -0
  63. package/dist/types/utils/index.d.ts.map +1 -1
  64. package/dist/types/utils/withdrawableBalances.d.ts +13 -0
  65. package/dist/types/utils/withdrawableBalances.d.ts.map +1 -0
  66. package/dist/types/websocket/OndoWsProvider.d.ts +15 -12
  67. package/dist/types/websocket/OndoWsProvider.d.ts.map +1 -1
  68. package/package.json +3 -3
  69. package/src/OndoProvider.ts +38 -19
  70. package/src/accountSummary.ts +37 -12
  71. package/src/utils/collateralAsset.ts +30 -0
  72. package/src/utils/decimal.ts +24 -0
  73. package/src/utils/index.ts +2 -0
  74. package/src/utils/withdrawableBalances.ts +25 -0
  75. package/src/websocket/OndoWsProvider.ts +53 -48
@@ -0,0 +1,24 @@
1
+ import { PerpsError } from '@lifi/perps-sdk'
2
+ import { PerpsErrorCode } from '@lifi/perps-types'
3
+ import Big from 'big.js'
4
+ import { ONDO_PROVIDER_KEY } from '../constants.js'
5
+
6
+ /**
7
+ * Exact-decimal parse of an Ondo wire decimal string. `field` names the
8
+ * offending field on failure, so a venue contract breach surfaces as a
9
+ * `PerpsError` instead of an anonymous big.js throw.
10
+ *
11
+ * @throws {PerpsError} `SDKError` when `value` is not a decimal numeric string.
12
+ */
13
+ export const toWireBig = (value: string, field: string): Big => {
14
+ try {
15
+ return new Big(value)
16
+ } catch {
17
+ const error = new PerpsError(
18
+ PerpsErrorCode.SDKError,
19
+ `Ondo field \`${field}\` is not a valid decimal: '${value}'`
20
+ )
21
+ error.tool = ONDO_PROVIDER_KEY
22
+ throw error
23
+ }
24
+ }
@@ -16,6 +16,7 @@ export {
16
16
  type OndoRequestOptions,
17
17
  OndoSessionExpiredError,
18
18
  } from './apiClient.js'
19
+ export { requireOndoCollateralAsset } from './collateralAsset.js'
19
20
  export {
20
21
  buildOndoProvisionPayload,
21
22
  listOndoDepositAddress,
@@ -38,3 +39,4 @@ export { intervalFromBarSpan, mapInterval } from './ohlcvInterval.js'
38
39
  export { ondoAsset } from './ondoAsset.js'
39
40
  export { formatOrderPrice, formatOrderSize } from './orderFormatting.js'
40
41
  export { positionMarginConstraints } from './transferMargin.js'
42
+ export { ondoWithdrawableBalances } from './withdrawableBalances.js'
@@ -0,0 +1,25 @@
1
+ import type { ProviderWithdrawableBalance } from '@lifi/perps-sdk'
2
+ import type { OndoBalanceSummary } from '../types/wire.js'
3
+ import { toWireBig } from './decimal.js'
4
+
5
+ /**
6
+ * The single route an Ondo withdrawal draws on: `withdrawableMargin`, the
7
+ * venue's own figure for the margin the account can take out. A route with
8
+ * nothing left to draw carries no row, and the caller applies the per-asset
9
+ * venue minimum.
10
+ *
11
+ * @param assetId - `Asset.id` the row is keyed by.
12
+ * @public
13
+ */
14
+ export const ondoWithdrawableBalances = (
15
+ assetId: string,
16
+ balance: OndoBalanceSummary
17
+ ): ProviderWithdrawableBalance[] => {
18
+ const available = toWireBig(
19
+ balance.withdrawableMargin,
20
+ 'balance.withdrawableMargin'
21
+ )
22
+ return available.gt(0)
23
+ ? [{ assetId, route: 'perps', available: available.toFixed() }]
24
+ : []
25
+ }
@@ -1,4 +1,5 @@
1
1
  import {
2
+ DecodeChain,
2
3
  getMarketRegistry,
3
4
  localStorageAdapter,
4
5
  type MarketRegistry,
@@ -18,6 +19,7 @@ import {
18
19
  import type {
19
20
  MarketContext,
20
21
  MarketDisplay,
22
+ OndoAccountBalance,
21
23
  PerpsMarketDisplay,
22
24
  Subscription,
23
25
  } from '@lifi/perps-types'
@@ -131,13 +133,20 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
131
133
  private readonly authWireRefs = new Map<string, number>()
132
134
 
133
135
  /**
134
- * Running account-summary inputs while `accountSummary` is subscribed
135
- * (`undefined` otherwise). `walletBalance` is REST-seeded and refreshed on
136
- * fills; `marginUsed`/`unrealizedPnl` track the latest positions frame.
136
+ * Latest venue balance figures while `accountSummary` is subscribed
137
+ * (`undefined` otherwise). REST-seeded, then re-read on every fills and
138
+ * positions frame, which is what moves them.
137
139
  */
138
- private accountSummary:
139
- | { walletBalance: number; marginUsed: number; unrealizedPnl: number }
140
- | undefined
140
+ private accountSummary: OndoAccountBalance | undefined
141
+
142
+ /**
143
+ * Serializes the balance re-read: a single trade event emits a fills frame
144
+ * and a positions frame together, and two concurrent REST reads can resolve
145
+ * out of order. `'latest'` coalesces the backlog to one trailing refresh.
146
+ */
147
+ private readonly accountSummaryChain = new DecodeChain('latest', (err) =>
148
+ wsLog.handlerFailure(this.providerKey, err)
149
+ )
141
150
 
142
151
  /** Complete (mark-price-bearing) contexts, keyed by venue market symbol. */
143
152
  private contexts: Record<string, MarketContext> = {}
@@ -215,6 +224,8 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
215
224
  case 'accountSummary':
216
225
  case 'spotBalances':
217
226
  return `${sub.channel}:${sub.address.toLowerCase()}`
227
+ case 'availableToTrade':
228
+ throw new Error(`Ondo WS does not support channel: ${sub.channel}.`)
218
229
  }
219
230
  }
220
231
 
@@ -269,6 +280,7 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
269
280
  return () => {
270
281
  if (sub.channel === 'accountSummary') {
271
282
  this.accountSummary = undefined
283
+ this.accountSummaryChain.reset()
272
284
  }
273
285
  for (const [key, state] of wireSubs) {
274
286
  this.releaseWire(key, state, needsLogin)
@@ -484,6 +496,7 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
484
496
  ],
485
497
  ]
486
498
  case 'spotBalances':
499
+ case 'availableToTrade':
487
500
  return []
488
501
  }
489
502
  }
@@ -530,62 +543,60 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
530
543
  }
531
544
 
532
545
  /**
533
- * Seed the account-summary inputs from the REST balance so the first emit is
534
- * complete before any positions frame lands. Throws {@link
546
+ * Seed the account-summary figures from the REST balance so the first emit
547
+ * is complete before any positions frame lands. Throws {@link
535
548
  * OndoSessionExpiredError} when the SIWE session is missing, matching the
536
549
  * other authenticated channels.
537
550
  */
538
551
  private async seedAccountSummary(address: Address): Promise<void> {
539
552
  const token = await this.requireSession(address)
540
- const balance = await this.restClient().get<OndoBalanceSummary>(
541
- '/v1/perps/balance',
542
- { authToken: token.token }
543
- )
544
- this.accountSummary = {
545
- walletBalance: Number.parseFloat(balance.walletBalance) || 0,
546
- marginUsed: Number.parseFloat(balance.usedMargin) || 0,
547
- unrealizedPnl: Number.parseFloat(balance.unrealizedPnl) || 0,
548
- }
553
+ this.accountSummary = await this.readBalance(token.token)
549
554
  this.emitAccountSummary()
550
555
  }
551
556
 
552
- /** Re-pull the wallet balance (moved by the fill) and re-emit the summary. */
553
- private async refreshAccountSummaryBalance(address: Address): Promise<void> {
557
+ /** Re-read the venue balance moved by the frame and re-emit the summary. */
558
+ private async refreshAccountSummary(address: Address): Promise<void> {
554
559
  const token = await this.tokenStore.get(address)
555
560
  if (token === null || this.accountSummary === undefined) {
556
561
  return
557
562
  }
558
- const balance = await this.restClient().get<OndoBalanceSummary>(
559
- '/v1/perps/balance',
560
- { authToken: token.token }
561
- )
563
+ const balance = await this.readBalance(token.token)
562
564
  if (this.accountSummary === undefined) {
563
565
  return
564
566
  }
565
- this.accountSummary.walletBalance =
566
- Number.parseFloat(balance.walletBalance) || 0
567
+ this.accountSummary = balance
567
568
  this.emitAccountSummary()
568
569
  }
569
570
 
570
- /**
571
- * Emit the current account summary. Gross semantics: `walletBalance` is the
572
- * collateral, the positions carry unrealized PnL, so it counts toward buying
573
- * power (`availableMargin = walletBalance + unrealizedPnl − marginUsed`).
574
- */
571
+ private async readBalance(authToken: string): Promise<OndoAccountBalance> {
572
+ const balance = await this.restClient().get<OndoBalanceSummary>(
573
+ '/v1/perps/balance',
574
+ { authToken }
575
+ )
576
+ return {
577
+ walletBalance: balance.walletBalance,
578
+ unrealizedPnl: balance.unrealizedPnl,
579
+ marginBalance: balance.marginBalance,
580
+ usedMargin: balance.usedMargin,
581
+ availableMargin: balance.availableMargin,
582
+ withdrawableMargin: balance.withdrawableMargin,
583
+ }
584
+ }
585
+
586
+ /** Emit the current account summary from the venue balance figures. */
575
587
  private emitAccountSummary(): void {
576
- const summary = this.accountSummary
588
+ const balance = this.accountSummary
577
589
  const address = this.accountAddress
578
- if (summary === undefined || address === undefined) {
590
+ if (balance === undefined || address === undefined) {
579
591
  return
580
592
  }
581
- const portfolioValue = summary.walletBalance + summary.unrealizedPnl
582
593
  this.emit(`accountSummary:${address}`, {
583
594
  channel: 'accountSummary',
584
595
  data: {
585
- portfolioValue: portfolioValue.toString(),
586
- availableMargin: (portfolioValue - summary.marginUsed).toString(),
587
- marginUsed: summary.marginUsed.toString(),
588
- unrealizedPnl: summary.unrealizedPnl.toString(),
596
+ portfolioValue: balance.marginBalance,
597
+ availableMargin: balance.availableMargin,
598
+ marginUsed: balance.usedMargin,
599
+ unrealizedPnl: balance.unrealizedPnl,
589
600
  },
590
601
  })
591
602
  }
@@ -792,8 +803,8 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
792
803
  }
793
804
  this.emit(`fills:${address}`, { channel: 'fills', data: mapped })
794
805
  if (this.accountSummary !== undefined) {
795
- this.refreshAccountSummaryBalance(address as Address).catch((err) =>
796
- wsLog.handlerFailure(this.providerKey, err)
806
+ this.accountSummaryChain.push(() =>
807
+ this.refreshAccountSummary(address as Address)
797
808
  )
798
809
  }
799
810
  }
@@ -818,15 +829,9 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
818
829
  data: mapped,
819
830
  })
820
831
  if (this.accountSummary !== undefined) {
821
- let marginUsed = 0
822
- let unrealizedPnl = 0
823
- for (const position of positions) {
824
- marginUsed += Number.parseFloat(position.usedMargin) || 0
825
- unrealizedPnl += Number.parseFloat(position.unrealizedPnl) || 0
826
- }
827
- this.accountSummary.marginUsed = marginUsed
828
- this.accountSummary.unrealizedPnl = unrealizedPnl
829
- this.emitAccountSummary()
832
+ this.accountSummaryChain.push(() =>
833
+ this.refreshAccountSummary(address as Address)
834
+ )
830
835
  }
831
836
  }
832
837
  }