@lifi/perps-sdk-provider-ondo 17.0.0 → 19.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 (95) hide show
  1. package/README.md +5 -0
  2. package/dist/cjs/OndoProvider.d.ts.map +1 -1
  3. package/dist/cjs/OndoProvider.js +27 -11
  4. package/dist/cjs/OndoProvider.js.map +1 -1
  5. package/dist/cjs/accountSummary.d.ts +2 -2
  6. package/dist/cjs/accountSummary.d.ts.map +1 -1
  7. package/dist/cjs/accountSummary.js +24 -2
  8. package/dist/cjs/accountSummary.js.map +1 -1
  9. package/dist/cjs/utils/collateralAsset.d.ts +3 -0
  10. package/dist/cjs/utils/collateralAsset.d.ts.map +1 -0
  11. package/dist/cjs/utils/collateralAsset.js +19 -0
  12. package/dist/cjs/utils/collateralAsset.js.map +1 -0
  13. package/dist/cjs/utils/decimal.d.ts +3 -0
  14. package/dist/cjs/utils/decimal.d.ts.map +1 -0
  15. package/dist/cjs/utils/decimal.js +22 -0
  16. package/dist/cjs/utils/decimal.js.map +1 -0
  17. package/dist/cjs/utils/index.d.ts +3 -1
  18. package/dist/cjs/utils/index.d.ts.map +1 -1
  19. package/dist/cjs/utils/index.js +6 -2
  20. package/dist/cjs/utils/index.js.map +1 -1
  21. package/dist/cjs/utils/mapOrder.d.ts.map +1 -1
  22. package/dist/cjs/utils/mapOrder.js +2 -0
  23. package/dist/cjs/utils/mapOrder.js.map +1 -1
  24. package/dist/cjs/utils/transferMargin.d.ts +1 -1
  25. package/dist/cjs/utils/transferMargin.d.ts.map +1 -1
  26. package/dist/cjs/utils/transferMargin.js +2 -2
  27. package/dist/cjs/utils/transferMargin.js.map +1 -1
  28. package/dist/cjs/utils/withdrawableBalances.d.ts +4 -0
  29. package/dist/cjs/utils/withdrawableBalances.d.ts.map +1 -0
  30. package/dist/cjs/utils/withdrawableBalances.js +24 -0
  31. package/dist/cjs/utils/withdrawableBalances.js.map +1 -0
  32. package/dist/cjs/websocket/OndoWsProvider.d.ts +3 -1
  33. package/dist/cjs/websocket/OndoWsProvider.d.ts.map +1 -1
  34. package/dist/cjs/websocket/OndoWsProvider.js +28 -27
  35. package/dist/cjs/websocket/OndoWsProvider.js.map +1 -1
  36. package/dist/esm/OndoProvider.d.ts.map +1 -1
  37. package/dist/esm/OndoProvider.js +29 -13
  38. package/dist/esm/OndoProvider.js.map +1 -1
  39. package/dist/esm/accountSummary.d.ts +6 -7
  40. package/dist/esm/accountSummary.d.ts.map +1 -1
  41. package/dist/esm/accountSummary.js +29 -8
  42. package/dist/esm/accountSummary.js.map +1 -1
  43. package/dist/esm/utils/collateralAsset.d.ts +10 -0
  44. package/dist/esm/utils/collateralAsset.d.ts.map +1 -0
  45. package/dist/esm/utils/collateralAsset.js +22 -0
  46. package/dist/esm/utils/collateralAsset.js.map +1 -0
  47. package/dist/esm/utils/decimal.d.ts +10 -0
  48. package/dist/esm/utils/decimal.d.ts.map +1 -0
  49. package/dist/esm/utils/decimal.js +22 -0
  50. package/dist/esm/utils/decimal.js.map +1 -0
  51. package/dist/esm/utils/index.d.ts +3 -1
  52. package/dist/esm/utils/index.d.ts.map +1 -1
  53. package/dist/esm/utils/index.js +3 -1
  54. package/dist/esm/utils/index.js.map +1 -1
  55. package/dist/esm/utils/mapOrder.d.ts.map +1 -1
  56. package/dist/esm/utils/mapOrder.js +2 -0
  57. package/dist/esm/utils/mapOrder.js.map +1 -1
  58. package/dist/esm/utils/transferMargin.d.ts +1 -1
  59. package/dist/esm/utils/transferMargin.d.ts.map +1 -1
  60. package/dist/esm/utils/transferMargin.js +1 -1
  61. package/dist/esm/utils/transferMargin.js.map +1 -1
  62. package/dist/esm/utils/withdrawableBalances.d.ts +16 -0
  63. package/dist/esm/utils/withdrawableBalances.d.ts.map +1 -0
  64. package/dist/esm/utils/withdrawableBalances.js +32 -0
  65. package/dist/esm/utils/withdrawableBalances.js.map +1 -0
  66. package/dist/esm/websocket/OndoWsProvider.d.ts +15 -12
  67. package/dist/esm/websocket/OndoWsProvider.d.ts.map +1 -1
  68. package/dist/esm/websocket/OndoWsProvider.js +41 -39
  69. package/dist/esm/websocket/OndoWsProvider.js.map +1 -1
  70. package/dist/types/OndoProvider.d.ts.map +1 -1
  71. package/dist/types/accountSummary.d.ts +6 -7
  72. package/dist/types/accountSummary.d.ts.map +1 -1
  73. package/dist/types/utils/collateralAsset.d.ts +10 -0
  74. package/dist/types/utils/collateralAsset.d.ts.map +1 -0
  75. package/dist/types/utils/decimal.d.ts +10 -0
  76. package/dist/types/utils/decimal.d.ts.map +1 -0
  77. package/dist/types/utils/index.d.ts +3 -1
  78. package/dist/types/utils/index.d.ts.map +1 -1
  79. package/dist/types/utils/mapOrder.d.ts.map +1 -1
  80. package/dist/types/utils/transferMargin.d.ts +1 -1
  81. package/dist/types/utils/transferMargin.d.ts.map +1 -1
  82. package/dist/types/utils/withdrawableBalances.d.ts +16 -0
  83. package/dist/types/utils/withdrawableBalances.d.ts.map +1 -0
  84. package/dist/types/websocket/OndoWsProvider.d.ts +15 -12
  85. package/dist/types/websocket/OndoWsProvider.d.ts.map +1 -1
  86. package/package.json +3 -3
  87. package/src/OndoProvider.ts +45 -21
  88. package/src/accountSummary.ts +37 -12
  89. package/src/utils/collateralAsset.ts +30 -0
  90. package/src/utils/decimal.ts +24 -0
  91. package/src/utils/index.ts +3 -1
  92. package/src/utils/mapOrder.ts +2 -0
  93. package/src/utils/transferMargin.ts +1 -1
  94. package/src/utils/withdrawableBalances.ts +44 -0
  95. package/src/websocket/OndoWsProvider.ts +53 -48
@@ -47,11 +47,17 @@ export declare class OndoWsProvider extends WsProviderBase<SubState> {
47
47
  */
48
48
  private readonly authWireRefs;
49
49
  /**
50
- * Running account-summary inputs while `accountSummary` is subscribed
51
- * (`undefined` otherwise). `walletBalance` is REST-seeded and refreshed on
52
- * fills; `marginUsed`/`unrealizedPnl` track the latest positions frame.
50
+ * Latest venue balance figures while `accountSummary` is subscribed
51
+ * (`undefined` otherwise). REST-seeded, then re-read on every fills and
52
+ * positions frame, which is what moves them.
53
53
  */
54
54
  private accountSummary;
55
+ /**
56
+ * Serializes the balance re-read: a single trade event emits a fills frame
57
+ * and a positions frame together, and two concurrent REST reads can resolve
58
+ * out of order. `'latest'` coalesces the backlog to one trailing refresh.
59
+ */
60
+ private readonly accountSummaryChain;
55
61
  /** Complete (mark-price-bearing) contexts, keyed by venue market symbol. */
56
62
  private contexts;
57
63
  /** Funding seen before the market's first mark price. */
@@ -104,19 +110,16 @@ export declare class OndoWsProvider extends WsProviderBase<SubState> {
104
110
  private ensureLogin;
105
111
  private restClient;
106
112
  /**
107
- * Seed the account-summary inputs from the REST balance so the first emit is
108
- * complete before any positions frame lands. Throws {@link
113
+ * Seed the account-summary figures from the REST balance so the first emit
114
+ * is complete before any positions frame lands. Throws {@link
109
115
  * OndoSessionExpiredError} when the SIWE session is missing, matching the
110
116
  * other authenticated channels.
111
117
  */
112
118
  private seedAccountSummary;
113
- /** Re-pull the wallet balance (moved by the fill) and re-emit the summary. */
114
- private refreshAccountSummaryBalance;
115
- /**
116
- * Emit the current account summary. Gross semantics: `walletBalance` is the
117
- * collateral, the positions carry unrealized PnL, so it counts toward buying
118
- * power (`availableMargin = walletBalance + unrealizedPnl − marginUsed`).
119
- */
119
+ /** Re-read the venue balance moved by the frame and re-emit the summary. */
120
+ private refreshAccountSummary;
121
+ private readBalance;
122
+ /** Emit the current account summary from the venue balance figures. */
120
123
  private emitAccountSummary;
121
124
  protected handleMessage(raw: string): void;
122
125
  private handleBooks;
@@ -1 +1 @@
1
- {"version":3,"file":"OndoWsProvider.d.ts","sourceRoot":"","sources":["../../../src/websocket/OndoWsProvider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OndoWsProvider.d.ts","sourceRoot":"","sources":["../../../src/websocket/OndoWsProvider.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/perps-sdk-provider-ondo",
3
- "version": "17.0.0",
3
+ "version": "19.0.0",
4
4
  "description": "Ondo Perps provider plugin for @lifi/perps-sdk",
5
5
  "homepage": "https://github.com/lifinance/perps-sdk",
6
6
  "bugs": {
@@ -44,10 +44,10 @@
44
44
  "dependencies": {
45
45
  "big.js": "^7.0.1",
46
46
  "viem": "^2.55.18",
47
- "@lifi/perps-types": "^14.0.0"
47
+ "@lifi/perps-types": "^16.0.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@lifi/perps-sdk": "^15.0.0"
50
+ "@lifi/perps-sdk": "^17.0.0"
51
51
  },
52
52
  "publishConfig": {
53
53
  "access": "public"
@@ -20,6 +20,8 @@ import {
20
20
  type ProviderGetPortfolioHistoryParams,
21
21
  type ProviderGetPositionsParams,
22
22
  type ProviderGetQuoteParams,
23
+ type ProviderGetWithdrawableBalancesParams,
24
+ type ProviderWithdrawableBalance,
23
25
  paginateActivity,
24
26
  resolveQuote,
25
27
  resolveRetryPolicy,
@@ -111,7 +113,9 @@ import {
111
113
  mapOpenPositions,
112
114
  mapOrder,
113
115
  mapWithdrawalActivity,
114
- positionMarginConstraints,
116
+ ondoWithdrawableBalances,
117
+ positionRemovableMargin,
118
+ requireOndoCollateralAsset,
115
119
  } from './utils/index.js'
116
120
  import { mapPortfolioHistory } from './utils/mapPortfolioHistory.js'
117
121
  import {
@@ -323,19 +327,7 @@ export const ondoProvider = (
323
327
  marketRegistry().sync(),
324
328
  ])
325
329
 
326
- const collateralAsset = providers
327
- .find((provider) => provider.key === ONDO_PROVIDER_KEY)
328
- ?.categories.find(
329
- (category) => category.id === ONDO_PROVIDER_KEY
330
- )?.quoteAsset
331
- if (collateralAsset === null || collateralAsset === undefined) {
332
- const error = new PerpsError(
333
- PerpsErrorCode.SDKError,
334
- 'Ondo provider metadata is missing its collateral asset'
335
- )
336
- error.tool = ONDO_PROVIDER_KEY
337
- throw error
338
- }
330
+ const collateralAsset = requireOndoCollateralAsset(providers)
339
331
 
340
332
  const positions: Position[] = mapOpenPositions(
341
333
  rawPositions ?? [],
@@ -343,7 +335,7 @@ export const ondoProvider = (
343
335
  )
344
336
 
345
337
  // The backend owns the collateral identity; the venue supplies its
346
- // gross wallet balance (locked margin in, unrealized PnL out).
338
+ // wallet balance (locked margin in, unrealized PnL out).
347
339
  return {
348
340
  provider: ONDO_PROVIDER_KEY,
349
341
  address: params.address,
@@ -373,12 +365,47 @@ export const ondoProvider = (
373
365
  apiKeyRegistered,
374
366
  referralSet: referral !== null && referral !== undefined,
375
367
  depositAddress,
368
+ balance: {
369
+ walletBalance: balance.walletBalance,
370
+ unrealizedPnl: balance.unrealizedPnl,
371
+ marginBalance: balance.marginBalance,
372
+ usedMargin: balance.usedMargin,
373
+ availableMargin: balance.availableMargin,
374
+ withdrawableMargin: balance.withdrawableMargin,
375
+ },
376
376
  },
377
377
  }
378
378
  }
379
379
  )
380
380
  },
381
381
 
382
+ async getWithdrawableBalances(
383
+ params: ProviderGetWithdrawableBalancesParams,
384
+ opts?: SDKRequestOptions
385
+ ): Promise<ProviderWithdrawableBalance[]> {
386
+ return withSession(
387
+ params.address,
388
+ (): ProviderWithdrawableBalance[] => [],
389
+ async (token) => {
390
+ const client = apiClient(opts)
391
+ const [{ providers }, balance, account] = await Promise.all([
392
+ getProviders(requireClient(), opts),
393
+ client.get<OndoBalanceSummary>('/v1/perps/balance', {
394
+ authToken: token.token,
395
+ }),
396
+ client.get<OndoAccountInfo>('/v1/account', {
397
+ authToken: token.token,
398
+ }),
399
+ ])
400
+ return ondoWithdrawableBalances(
401
+ requireOndoCollateralAsset(providers).id,
402
+ balance,
403
+ account.withdrawalFeeUSD
404
+ )
405
+ }
406
+ )
407
+ },
408
+
382
409
  async accountExists(
383
410
  params: ProviderAccountExistsParams,
384
411
  opts?: SDKRequestOptions
@@ -899,11 +926,8 @@ export const ondoProvider = (
899
926
  )
900
927
  },
901
928
 
902
- getAccountSummary(
903
- account: AccountResponse,
904
- positions: Position[]
905
- ): AccountSummary {
906
- return getAccountSummary(account, positions)
929
+ getAccountSummary(account: AccountResponse): AccountSummary {
930
+ return getAccountSummary(account)
907
931
  },
908
932
 
909
933
  formatOrderPrice,
@@ -912,7 +936,7 @@ export const ondoProvider = (
912
936
 
913
937
  estimateLiquidationPrice,
914
938
 
915
- positionMarginConstraints,
939
+ positionRemovableMargin,
916
940
 
917
941
  projectConfig(
918
942
  config: AccountConfig,
@@ -1,22 +1,47 @@
1
- import { summarizeAccount } from '@lifi/perps-sdk'
1
+ import { PerpsError } from '@lifi/perps-sdk'
2
2
  import type {
3
3
  AccountResponse,
4
4
  AccountSummary,
5
- Position,
5
+ OndoAccountBalance,
6
6
  } from '@lifi/perps-types'
7
+ import { PerpsErrorCode } from '@lifi/perps-types'
8
+
9
+ const ZERO_SUMMARY: AccountSummary = {
10
+ portfolioValue: '0',
11
+ availableMargin: '0',
12
+ marginUsed: '0',
13
+ unrealizedPnl: '0',
14
+ }
15
+
16
+ const ondoBalance = (
17
+ account: AccountResponse
18
+ ): OndoAccountBalance | undefined => {
19
+ if (account.config.provider !== 'ondo') {
20
+ throw new PerpsError(
21
+ PerpsErrorCode.SDKError,
22
+ `Ondo account summary received a '${account.config.provider}' account config`
23
+ )
24
+ }
25
+ return account.config.balance
26
+ }
7
27
 
8
28
  /**
9
- * Roll an Ondo {@link AccountResponse} up into an {@link AccountSummary}.
10
- * Ondo's collateral row holds `walletBalance`, which includes locked margin
11
- * but not unrealized PnL (the positions carry it) — gross semantics: the
12
- * venue counts unrealized PnL toward buying power
13
- * (`availableMargin = marginBalance − usedMargin`).
29
+ * Roll an Ondo account up into an {@link AccountSummary}, from the venue
30
+ * balance figures the response carries. A logged-out account reads no venue
31
+ * balance and summarizes as zero.
14
32
  *
33
+ * @throws {PerpsError} `SDKError` when the account is not an Ondo one.
15
34
  * @public
16
35
  */
17
- export function getAccountSummary(
18
- account: AccountResponse,
19
- positions: Position[]
20
- ): AccountSummary {
21
- return summarizeAccount(account, positions, 'gross')
36
+ export function getAccountSummary(account: AccountResponse): AccountSummary {
37
+ const balance = ondoBalance(account)
38
+ if (balance === undefined) {
39
+ return ZERO_SUMMARY
40
+ }
41
+ return {
42
+ portfolioValue: balance.marginBalance,
43
+ availableMargin: balance.availableMargin,
44
+ marginUsed: balance.usedMargin,
45
+ unrealizedPnl: balance.unrealizedPnl,
46
+ }
22
47
  }
@@ -0,0 +1,30 @@
1
+ import { PerpsError } from '@lifi/perps-sdk'
2
+ import type { Asset, Provider } from '@lifi/perps-types'
3
+ import { PerpsErrorCode } from '@lifi/perps-types'
4
+ import { ONDO_PROVIDER_KEY } from '../constants.js'
5
+
6
+ /**
7
+ * The collateral asset the backend publishes for Ondo's single category. The
8
+ * backend owns the collateral identity, so the venue payload never names it.
9
+ *
10
+ * @throws {PerpsError} `SDKError` when the provider metadata carries none.
11
+ * @public
12
+ */
13
+ export const requireOndoCollateralAsset = (
14
+ providers: readonly Provider[]
15
+ ): Asset => {
16
+ const asset = providers
17
+ .find((provider) => provider.key === ONDO_PROVIDER_KEY)
18
+ ?.categories.find(
19
+ (category) => category.id === ONDO_PROVIDER_KEY
20
+ )?.quoteAsset
21
+ if (asset === null || asset === undefined) {
22
+ const error = new PerpsError(
23
+ PerpsErrorCode.SDKError,
24
+ 'Ondo provider metadata is missing its collateral asset'
25
+ )
26
+ error.tool = ONDO_PROVIDER_KEY
27
+ throw error
28
+ }
29
+ return asset
30
+ }
@@ -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,
@@ -37,4 +38,5 @@ export { mapOpenPositions, mapPosition } from './mapPosition.js'
37
38
  export { intervalFromBarSpan, mapInterval } from './ohlcvInterval.js'
38
39
  export { ondoAsset } from './ondoAsset.js'
39
40
  export { formatOrderPrice, formatOrderSize } from './orderFormatting.js'
40
- export { positionMarginConstraints } from './transferMargin.js'
41
+ export { positionRemovableMargin } from './transferMargin.js'
42
+ export { ondoWithdrawableBalances } from './withdrawableBalances.js'
@@ -22,6 +22,8 @@ export const mapOrderStatus = (status: string): OrderStatus => {
22
22
  case 'open':
23
23
  case 'untriggered':
24
24
  return OrderStatus.OPEN
25
+ case 'pending':
26
+ return OrderStatus.PENDING
25
27
  case 'fullyfilled':
26
28
  return OrderStatus.FILLED
27
29
  case 'canceled':
@@ -7,6 +7,6 @@ import type { Position } from '@lifi/perps-types'
7
7
  * @see https://docs.ondo.finance/ondo-global-markets/perpetuals/trading/margin
8
8
  * @public
9
9
  */
10
- export function positionMarginConstraints(_position: Position): undefined {
10
+ export function positionRemovableMargin(_position: Position): undefined {
11
11
  return undefined
12
12
  }
@@ -0,0 +1,44 @@
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
+ * @param withdrawalFeeUsd - The account's `withdrawalFeeUSD`. Ondo collateral
13
+ * is USDC, so the USD fee is the row fee 1:1 in collateral units. Absent
14
+ * leaves the row without a fee.
15
+ * @public
16
+ */
17
+ export const ondoWithdrawableBalances = (
18
+ assetId: string,
19
+ balance: OndoBalanceSummary,
20
+ withdrawalFeeUsd?: string
21
+ ): ProviderWithdrawableBalance[] => {
22
+ const available = toWireBig(
23
+ balance.withdrawableMargin,
24
+ 'balance.withdrawableMargin'
25
+ )
26
+ if (!available.gt(0)) {
27
+ return []
28
+ }
29
+ return [
30
+ {
31
+ assetId,
32
+ route: 'perps',
33
+ available: available.toFixed(),
34
+ ...(withdrawalFeeUsd === undefined
35
+ ? {}
36
+ : {
37
+ withdrawalFee: toWireBig(
38
+ withdrawalFeeUsd,
39
+ 'account.withdrawalFeeUSD'
40
+ ).toFixed(),
41
+ }),
42
+ },
43
+ ]
44
+ }
@@ -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
  }