@lifi/perps-sdk-provider-ondo 16.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 (86) 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/mapActivity.d.ts +1 -1
  21. package/dist/cjs/utils/mapActivity.d.ts.map +1 -1
  22. package/dist/cjs/utils/mapActivity.js +29 -17
  23. package/dist/cjs/utils/mapActivity.js.map +1 -1
  24. package/dist/cjs/utils/withdrawableBalances.d.ts +4 -0
  25. package/dist/cjs/utils/withdrawableBalances.d.ts.map +1 -0
  26. package/dist/cjs/utils/withdrawableBalances.js +12 -0
  27. package/dist/cjs/utils/withdrawableBalances.js.map +1 -0
  28. package/dist/cjs/websocket/OndoWsProvider.d.ts +3 -1
  29. package/dist/cjs/websocket/OndoWsProvider.d.ts.map +1 -1
  30. package/dist/cjs/websocket/OndoWsProvider.js +28 -27
  31. package/dist/cjs/websocket/OndoWsProvider.js.map +1 -1
  32. package/dist/esm/OndoProvider.d.ts.map +1 -1
  33. package/dist/esm/OndoProvider.js +24 -12
  34. package/dist/esm/OndoProvider.js.map +1 -1
  35. package/dist/esm/accountSummary.d.ts +6 -7
  36. package/dist/esm/accountSummary.d.ts.map +1 -1
  37. package/dist/esm/accountSummary.js +29 -8
  38. package/dist/esm/accountSummary.js.map +1 -1
  39. package/dist/esm/utils/collateralAsset.d.ts +10 -0
  40. package/dist/esm/utils/collateralAsset.d.ts.map +1 -0
  41. package/dist/esm/utils/collateralAsset.js +22 -0
  42. package/dist/esm/utils/collateralAsset.js.map +1 -0
  43. package/dist/esm/utils/decimal.d.ts +10 -0
  44. package/dist/esm/utils/decimal.d.ts.map +1 -0
  45. package/dist/esm/utils/decimal.js +22 -0
  46. package/dist/esm/utils/decimal.js.map +1 -0
  47. package/dist/esm/utils/index.d.ts +2 -0
  48. package/dist/esm/utils/index.d.ts.map +1 -1
  49. package/dist/esm/utils/index.js +2 -0
  50. package/dist/esm/utils/index.js.map +1 -1
  51. package/dist/esm/utils/mapActivity.d.ts +1 -1
  52. package/dist/esm/utils/mapActivity.d.ts.map +1 -1
  53. package/dist/esm/utils/mapActivity.js +34 -17
  54. package/dist/esm/utils/mapActivity.js.map +1 -1
  55. package/dist/esm/utils/withdrawableBalances.d.ts +13 -0
  56. package/dist/esm/utils/withdrawableBalances.d.ts.map +1 -0
  57. package/dist/esm/utils/withdrawableBalances.js +17 -0
  58. package/dist/esm/utils/withdrawableBalances.js.map +1 -0
  59. package/dist/esm/websocket/OndoWsProvider.d.ts +15 -12
  60. package/dist/esm/websocket/OndoWsProvider.d.ts.map +1 -1
  61. package/dist/esm/websocket/OndoWsProvider.js +41 -39
  62. package/dist/esm/websocket/OndoWsProvider.js.map +1 -1
  63. package/dist/types/OndoProvider.d.ts.map +1 -1
  64. package/dist/types/accountSummary.d.ts +6 -7
  65. package/dist/types/accountSummary.d.ts.map +1 -1
  66. package/dist/types/utils/collateralAsset.d.ts +10 -0
  67. package/dist/types/utils/collateralAsset.d.ts.map +1 -0
  68. package/dist/types/utils/decimal.d.ts +10 -0
  69. package/dist/types/utils/decimal.d.ts.map +1 -0
  70. package/dist/types/utils/index.d.ts +2 -0
  71. package/dist/types/utils/index.d.ts.map +1 -1
  72. package/dist/types/utils/mapActivity.d.ts +1 -1
  73. package/dist/types/utils/mapActivity.d.ts.map +1 -1
  74. package/dist/types/utils/withdrawableBalances.d.ts +13 -0
  75. package/dist/types/utils/withdrawableBalances.d.ts.map +1 -0
  76. package/dist/types/websocket/OndoWsProvider.d.ts +15 -12
  77. package/dist/types/websocket/OndoWsProvider.d.ts.map +1 -1
  78. package/package.json +3 -3
  79. package/src/OndoProvider.ts +38 -19
  80. package/src/accountSummary.ts +37 -12
  81. package/src/utils/collateralAsset.ts +30 -0
  82. package/src/utils/decimal.ts +24 -0
  83. package/src/utils/index.ts +2 -0
  84. package/src/utils/mapActivity.ts +43 -18
  85. package/src/utils/withdrawableBalances.ts +25 -0
  86. package/src/websocket/OndoWsProvider.ts +53 -48
@@ -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": "16.0.0",
3
+ "version": "18.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": "^13.0.0"
47
+ "@lifi/perps-types": "^15.0.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@lifi/perps-sdk": "^14.0.0"
50
+ "@lifi/perps-sdk": "^16.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,
116
+ ondoWithdrawableBalances,
114
117
  positionMarginConstraints,
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,42 @@ 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 [{ providers }, balance] = await Promise.all([
391
+ getProviders(requireClient(), opts),
392
+ apiClient(opts).get<OndoBalanceSummary>('/v1/perps/balance', {
393
+ authToken: token.token,
394
+ }),
395
+ ])
396
+ return ondoWithdrawableBalances(
397
+ requireOndoCollateralAsset(providers).id,
398
+ balance
399
+ )
400
+ }
401
+ )
402
+ },
403
+
382
404
  async accountExists(
383
405
  params: ProviderAccountExistsParams,
384
406
  opts?: SDKRequestOptions
@@ -899,11 +921,8 @@ export const ondoProvider = (
899
921
  )
900
922
  },
901
923
 
902
- getAccountSummary(
903
- account: AccountResponse,
904
- positions: Position[]
905
- ): AccountSummary {
906
- return getAccountSummary(account, positions)
924
+ getAccountSummary(account: AccountResponse): AccountSummary {
925
+ return getAccountSummary(account)
907
926
  },
908
927
 
909
928
  formatOrderPrice,
@@ -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,
@@ -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'
@@ -1,4 +1,8 @@
1
- import type { AssetRegistry } from '@lifi/perps-sdk'
1
+ import {
2
+ type AssetRegistry,
3
+ ExplorerChainId,
4
+ explorerTxUrl,
5
+ } from '@lifi/perps-sdk'
2
6
  import type {
3
7
  DepositActivity,
4
8
  FundingActivity,
@@ -91,24 +95,46 @@ const depositId = (deposit: OndoWalletDeposit): string => {
91
95
  : `deposit:${deposit.txid}:${deposit.logIndex}`
92
96
  }
93
97
 
98
+ /**
99
+ * Explorer chain per Ondo wire `chainId`, whose values Ondo's REST spec
100
+ * enumerates. Ondo also settles on Bitcoin, Solana and testnets, and a row on
101
+ * one of those resolves to no explorer link.
102
+ */
103
+ const EXPLORER_CHAIN_BY_ONDO_CHAIN_ID: Record<string, ExplorerChainId> = {
104
+ 'eth-mainnet': ExplorerChainId.ETHEREUM,
105
+ 'avax-c-chain': ExplorerChainId.AVALANCHE_C_CHAIN,
106
+ 'bsc-mainnet': ExplorerChainId.BNB_SMART_CHAIN,
107
+ }
108
+
109
+ const ondoExplorerLink = (
110
+ chainId: string,
111
+ txid: string
112
+ ): string | undefined => {
113
+ const explorerChainId = EXPLORER_CHAIN_BY_ONDO_CHAIN_ID[chainId]
114
+ return explorerChainId === undefined
115
+ ? undefined
116
+ : explorerTxUrl(explorerChainId, txid)
117
+ }
118
+
94
119
  /** Map a wallet deposit with registry asset identity and its on-chain transaction. @public */
95
120
  export const mapDepositActivity = (
96
121
  deposit: OndoWalletDeposit,
97
122
  assetRegistry: AssetRegistry
98
- ): DepositActivity => ({
99
- id: depositId(deposit),
100
- provider: ONDO_PROVIDER_KEY,
101
- timestamp: new Date(deposit.time).toISOString(),
102
- type: ActivityType.DEPOSIT,
103
- asset: assetRegistry.require(deposit.coin),
104
- amount: deposit.size,
105
- ...(deposit.fromAddress === ''
106
- ? {}
107
- : { counterpartyAddress: deposit.fromAddress }),
108
- ...(deposit.txid === ''
109
- ? {}
110
- : { explorerLink: `https://scan.li.fi/tx/${deposit.txid}` }),
111
- })
123
+ ): DepositActivity => {
124
+ const explorerLink = ondoExplorerLink(deposit.chainId, deposit.txid)
125
+ return {
126
+ id: depositId(deposit),
127
+ provider: ONDO_PROVIDER_KEY,
128
+ timestamp: new Date(deposit.time).toISOString(),
129
+ type: ActivityType.DEPOSIT,
130
+ asset: assetRegistry.require(deposit.coin),
131
+ amount: deposit.size,
132
+ ...(deposit.fromAddress === ''
133
+ ? {}
134
+ : { counterpartyAddress: deposit.fromAddress }),
135
+ ...(explorerLink === undefined ? {} : { explorerLink }),
136
+ }
137
+ }
112
138
 
113
139
  const SETTLING_WITHDRAWAL_STATUSES = new Set<string>([
114
140
  'complete',
@@ -133,6 +159,7 @@ export const mapWithdrawalActivity = (
133
159
  if (!SETTLING_WITHDRAWAL_STATUSES.has(withdrawal.status)) {
134
160
  return null
135
161
  }
162
+ const explorerLink = ondoExplorerLink(withdrawal.chainId, withdrawal.txid)
136
163
  return {
137
164
  id: withdrawal.withdrawal_id,
138
165
  provider: ONDO_PROVIDER_KEY,
@@ -149,8 +176,6 @@ export const mapWithdrawalActivity = (
149
176
  asset: ONDO_WITHDRAWAL_FEE_SYMBOL,
150
177
  },
151
178
  }),
152
- ...(withdrawal.txid === ''
153
- ? {}
154
- : { explorerLink: `https://scan.li.fi/tx/${withdrawal.txid}` }),
179
+ ...(explorerLink === undefined ? {} : { explorerLink }),
155
180
  }
156
181
  }
@@ -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
  }