@lifi/perps-sdk-provider-ondo 22.0.1 → 24.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 (61) hide show
  1. package/README.md +27 -2
  2. package/dist/cjs/OndoProvider.d.ts.map +1 -1
  3. package/dist/cjs/OndoProvider.js +41 -46
  4. package/dist/cjs/OndoProvider.js.map +1 -1
  5. package/dist/cjs/utils/index.d.ts +1 -1
  6. package/dist/cjs/utils/index.d.ts.map +1 -1
  7. package/dist/cjs/utils/index.js +3 -3
  8. package/dist/cjs/utils/index.js.map +1 -1
  9. package/dist/cjs/utils/liquidation.js +1 -1
  10. package/dist/cjs/utils/liquidation.js.map +1 -1
  11. package/dist/cjs/utils/orderFormatting.d.ts +3 -3
  12. package/dist/cjs/utils/orderFormatting.d.ts.map +1 -1
  13. package/dist/cjs/utils/orderFormatting.js +5 -5
  14. package/dist/cjs/utils/orderFormatting.js.map +1 -1
  15. package/dist/cjs/utils/withdrawableBalances.d.ts +1 -1
  16. package/dist/cjs/utils/withdrawableBalances.d.ts.map +1 -1
  17. package/dist/cjs/utils/withdrawableBalances.js +13 -13
  18. package/dist/cjs/utils/withdrawableBalances.js.map +1 -1
  19. package/dist/cjs/websocket/OndoWsProvider.d.ts +1 -1
  20. package/dist/cjs/websocket/OndoWsProvider.d.ts.map +1 -1
  21. package/dist/cjs/websocket/OndoWsProvider.js +2 -2
  22. package/dist/cjs/websocket/OndoWsProvider.js.map +1 -1
  23. package/dist/esm/OndoProvider.d.ts +4 -3
  24. package/dist/esm/OndoProvider.d.ts.map +1 -1
  25. package/dist/esm/OndoProvider.js +56 -52
  26. package/dist/esm/OndoProvider.js.map +1 -1
  27. package/dist/esm/utils/index.d.ts +1 -1
  28. package/dist/esm/utils/index.d.ts.map +1 -1
  29. package/dist/esm/utils/index.js +1 -1
  30. package/dist/esm/utils/index.js.map +1 -1
  31. package/dist/esm/utils/liquidation.js +1 -1
  32. package/dist/esm/utils/liquidation.js.map +1 -1
  33. package/dist/esm/utils/orderFormatting.d.ts +8 -8
  34. package/dist/esm/utils/orderFormatting.d.ts.map +1 -1
  35. package/dist/esm/utils/orderFormatting.js +9 -9
  36. package/dist/esm/utils/orderFormatting.js.map +1 -1
  37. package/dist/esm/utils/withdrawableBalances.d.ts +4 -4
  38. package/dist/esm/utils/withdrawableBalances.d.ts.map +1 -1
  39. package/dist/esm/utils/withdrawableBalances.js +16 -16
  40. package/dist/esm/utils/withdrawableBalances.js.map +1 -1
  41. package/dist/esm/websocket/OndoWsProvider.d.ts +8 -7
  42. package/dist/esm/websocket/OndoWsProvider.d.ts.map +1 -1
  43. package/dist/esm/websocket/OndoWsProvider.js +11 -10
  44. package/dist/esm/websocket/OndoWsProvider.js.map +1 -1
  45. package/dist/types/OndoProvider.d.ts +4 -3
  46. package/dist/types/OndoProvider.d.ts.map +1 -1
  47. package/dist/types/utils/index.d.ts +1 -1
  48. package/dist/types/utils/index.d.ts.map +1 -1
  49. package/dist/types/utils/orderFormatting.d.ts +8 -8
  50. package/dist/types/utils/orderFormatting.d.ts.map +1 -1
  51. package/dist/types/utils/withdrawableBalances.d.ts +4 -4
  52. package/dist/types/utils/withdrawableBalances.d.ts.map +1 -1
  53. package/dist/types/websocket/OndoWsProvider.d.ts +8 -7
  54. package/dist/types/websocket/OndoWsProvider.d.ts.map +1 -1
  55. package/package.json +3 -3
  56. package/src/OndoProvider.ts +86 -72
  57. package/src/utils/index.ts +1 -1
  58. package/src/utils/liquidation.ts +1 -1
  59. package/src/utils/orderFormatting.ts +19 -9
  60. package/src/utils/withdrawableBalances.ts +22 -20
  61. package/src/websocket/OndoWsProvider.ts +18 -15
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  ACTIVE_ORDER_STATUSES,
3
3
  type ActionSignerContribution,
4
+ calculateTransferable,
4
5
  createWarnOnce,
5
6
  type DepositFlow,
6
7
  ETHEREUM_USDC,
@@ -8,8 +9,6 @@ import {
8
9
  getMarketRegistry,
9
10
  getProviders,
10
11
  localStorageAdapter,
11
- maxOf,
12
- minOf,
13
12
  PerpsError,
14
13
  type PerpsProviderPlugin,
15
14
  type PerpsSDKClient,
@@ -74,7 +73,6 @@ import {
74
73
  MarginMode,
75
74
  PerpsErrorCode,
76
75
  } from '@lifi/perps-types'
77
- import Big from 'big.js'
78
76
  import { type Address, getAddress } from 'viem'
79
77
  import { projectOndoConfigSettings } from './accountConfig.js'
80
78
  import { getAccountSummary } from './accountSummary.js'
@@ -123,8 +121,6 @@ import {
123
121
  import { toWireBig } from './utils/decimal.js'
124
122
  import {
125
123
  estimateLiquidationPrice,
126
- formatOrderPrice,
127
- formatOrderSize,
128
124
  listOndoDepositAddress,
129
125
  mapDepositActivity,
130
126
  mapFill,
@@ -137,6 +133,8 @@ import {
137
133
  ondoWithdrawableBalances,
138
134
  positionRemovableMargin,
139
135
  requireOndoCollateralAsset,
136
+ snapOrderPrice,
137
+ snapOrderSize,
140
138
  } from './utils/index.js'
141
139
  import { mapPortfolioHistory } from './utils/mapPortfolioHistory.js'
142
140
  import {
@@ -164,9 +162,10 @@ export interface OndoProviderOptions {
164
162
  *
165
163
  * Per-user reads call Ondo's REST API directly — every one requires the
166
164
  * session JWT obtained through the SIWE login and stored in the token store.
167
- * Without a stored token the reads degrade gracefully (empty pages,
168
- * `loggedIn: false` config) instead of failing; `getOrder` throws because an
169
- * empty result would be indistinguishable from "order not found".
165
+ * A data read throws `PerpsErrorCode.SetupRequired` when no session is stored
166
+ * and `PerpsErrorCode.Unauthorized` when Ondo rejects the stored session. A
167
+ * status read (`getAccount`, `accountExists`, `getDepositFlow`,
168
+ * `getWithdrawFlow`) returns the logged-out state instead.
170
169
  *
171
170
  * @example
172
171
  * ```ts
@@ -274,30 +273,69 @@ export const ondoProvider = (
274
273
  config: emptyConfig,
275
274
  })
276
275
 
277
- // Runs an authenticated read against a live session. An absent local token
278
- // and a server-revoked one (surfaced mid-call as `OndoSessionExpiredError`)
279
- // both evict any stale token and fall back to `loggedOut`, so a rotated
280
- // session never soft-locks the UI behind a token that looks valid locally.
276
+ // Runs an authenticated read against a live session. `noSession` answers
277
+ // when no token is stored; `rejected` answers after the venue rejects the
278
+ // stored token mid-call (`OndoSessionExpiredError`), which is evicted first
279
+ // so a rotated session never soft-locks the UI behind a token that looks
280
+ // valid locally.
281
281
  const withSession = async <T>(
282
282
  address: Address,
283
- loggedOut: () => T,
283
+ fallback: {
284
+ noSession: () => T
285
+ rejected: (cause: OndoSessionExpiredError) => T
286
+ },
284
287
  fn: (token: OndoAuthToken) => Promise<T>
285
288
  ): Promise<T> => {
286
289
  const token = await tokenStore.get(address)
287
290
  if (token === null) {
288
- return loggedOut()
291
+ return fallback.noSession()
289
292
  }
290
293
  try {
291
294
  return await fn(token)
292
295
  } catch (err) {
293
296
  if (err instanceof OndoSessionExpiredError) {
294
297
  await tokenStore.remove(address)
295
- return loggedOut()
298
+ return fallback.rejected(err)
296
299
  }
297
300
  throw err
298
301
  }
299
302
  }
300
303
 
304
+ const sessionRequired = (read: string) => (): never => {
305
+ const error = new PerpsError(
306
+ PerpsErrorCode.SetupRequired,
307
+ `Ondo ${read} requires a session token. Run the SIWE login first.`
308
+ )
309
+ error.tool = ONDO_PROVIDER_KEY
310
+ throw error
311
+ }
312
+
313
+ const sessionRejected =
314
+ (read: string) =>
315
+ (cause: OndoSessionExpiredError): never => {
316
+ const error = new PerpsError(
317
+ PerpsErrorCode.Unauthorized,
318
+ `Ondo ${read} failed: the venue rejected the session; sign in again.`
319
+ )
320
+ error.tool = ONDO_PROVIDER_KEY
321
+ error.cause = cause
322
+ throw error
323
+ }
324
+
325
+ // A data read throws the account's state: an empty result would be
326
+ // indistinguishable from an account with no data.
327
+ const dataRead = (read: string) => ({
328
+ noSession: sessionRequired(read),
329
+ rejected: sessionRejected(read),
330
+ })
331
+
332
+ // A status read reports the logged-out state whether the session is absent
333
+ // or was rejected.
334
+ const statusRead = <T>(loggedOut: () => T) => ({
335
+ noSession: loggedOut,
336
+ rejected: loggedOut,
337
+ })
338
+
301
339
  return {
302
340
  type: ONDO_PROVIDER_KEY,
303
341
 
@@ -313,13 +351,13 @@ export const ondoProvider = (
313
351
  const apiKeyRegistered = apiKey !== null && hasOndoApiKeyScopes(apiKey)
314
352
  return withSession(
315
353
  params.address,
316
- () => {
354
+ statusRead(() => {
317
355
  const account = loggedOutAccount(params.address)
318
356
  return {
319
357
  ...account,
320
358
  config: { ...emptyConfig, apiKeyRegistered },
321
359
  }
322
- },
360
+ }),
323
361
  async (token) => {
324
362
  const client = apiClient(opts)
325
363
  const [
@@ -373,16 +411,13 @@ export const ondoProvider = (
373
411
  units: balance.walletBalance,
374
412
  valueUsd: balance.walletBalance,
375
413
  price: '1',
376
- transferable: minOf(
377
- maxOf(
378
- toWireBig(
379
- balance.withdrawableMargin,
380
- 'balance.withdrawableMargin'
381
- ),
382
- new Big(0)
383
- ),
384
- walletBalance
385
- ).toFixed(),
414
+ transferable: calculateTransferable(
415
+ toWireBig(
416
+ balance.withdrawableMargin,
417
+ 'balance.withdrawableMargin'
418
+ ).toFixed(),
419
+ walletBalance.toFixed()
420
+ ),
386
421
  },
387
422
  ]
388
423
  : [],
@@ -420,7 +455,7 @@ export const ondoProvider = (
420
455
  ): Promise<ProviderWithdrawableBalance[]> {
421
456
  return withSession(
422
457
  params.address,
423
- (): ProviderWithdrawableBalance[] => [],
458
+ dataRead('withdrawable balances read'),
424
459
  async (token) => {
425
460
  const client = apiClient(opts)
426
461
  const [{ providers }, balance, account] = await Promise.all([
@@ -457,11 +492,7 @@ export const ondoProvider = (
457
492
  }
458
493
  return withSession(
459
494
  params.address,
460
- () => {
461
- throw new OndoSessionExpiredError(
462
- `No valid Ondo session token stored for ${params.address}. Run the SIWE login first.`
463
- )
464
- },
495
+ dataRead('market settings read'),
465
496
  async (token) => {
466
497
  const leverages = await apiClient(opts).get<OndoLeverage[]>(
467
498
  '/v1/perps/leverage',
@@ -496,7 +527,7 @@ export const ondoProvider = (
496
527
  ): Promise<AvailableToTrade | undefined> {
497
528
  return withSession(
498
529
  params.address,
499
- () => undefined,
530
+ dataRead('available-to-trade read'),
500
531
  async (token) => {
501
532
  const registry = marketRegistry()
502
533
  await registry.sync()
@@ -578,7 +609,7 @@ export const ondoProvider = (
578
609
  ): Promise<boolean> {
579
610
  return withSession(
580
611
  params.address,
581
- () => false,
612
+ statusRead(() => false),
582
613
  async (token) => {
583
614
  await apiClient(opts).get('/v1/account', { authToken: token.token })
584
615
  return true
@@ -595,10 +626,12 @@ export const ondoProvider = (
595
626
  // before a route has a recipient.
596
627
  return withSession<DepositFlow>(
597
628
  params.address,
598
- () => ({
599
- kind: 'setupRequired',
600
- setup: [ActionType.SIWE_LOGIN, ActionType.CREATE_DEPOSIT_ADDRESS],
601
- }),
629
+ statusRead(
630
+ (): DepositFlow => ({
631
+ kind: 'setupRequired',
632
+ setup: [ActionType.SIWE_LOGIN, ActionType.CREATE_DEPOSIT_ADDRESS],
633
+ })
634
+ ),
602
635
  async (token) => {
603
636
  const depositAddress = await listOndoDepositAddress(
604
637
  apiClient(opts),
@@ -627,10 +660,12 @@ export const ondoProvider = (
627
660
  // offers one destination: the login address.
628
661
  return withSession<WithdrawFlow>(
629
662
  params.address,
630
- () => ({
631
- kind: 'setupRequired',
632
- setup: [ActionType.SIWE_LOGIN, ActionType.ADD_WITHDRAWAL_ADDRESS],
633
- }),
663
+ statusRead(
664
+ (): WithdrawFlow => ({
665
+ kind: 'setupRequired',
666
+ setup: [ActionType.SIWE_LOGIN, ActionType.ADD_WITHDRAWAL_ADDRESS],
667
+ })
668
+ ),
634
669
  async (token) => {
635
670
  const { addressBook } = await apiClient(
636
671
  opts
@@ -658,11 +693,7 @@ export const ondoProvider = (
658
693
  ): Promise<PositionsResponse> {
659
694
  return withSession<PositionsResponse>(
660
695
  params.address,
661
- () => ({
662
- provider: ONDO_PROVIDER_KEY,
663
- positions: [],
664
- pagination: { limit: params.limit ?? 0, hasMore: false },
665
- }),
696
+ dataRead('positions read'),
666
697
  async (token) => {
667
698
  const client = apiClient(opts)
668
699
  const [rawPositions] = await Promise.all([
@@ -698,11 +729,7 @@ export const ondoProvider = (
698
729
  ): Promise<OrdersResponse> {
699
730
  return withSession<OrdersResponse>(
700
731
  params.address,
701
- () => ({
702
- provider: ONDO_PROVIDER_KEY,
703
- orders: [],
704
- pagination: { limit: params.limit ?? 0, hasMore: false },
705
- }),
732
+ dataRead('orders read'),
706
733
  async (token) => {
707
734
  const statuses =
708
735
  params.statuses === undefined
@@ -865,12 +892,7 @@ export const ondoProvider = (
865
892
  ): Promise<Order> {
866
893
  return withSession(
867
894
  params.address,
868
- (): Order => {
869
- throw new PerpsError(
870
- PerpsErrorCode.SDKError,
871
- 'Ondo order lookup requires a session token. Run the SIWE login first.'
872
- )
873
- },
895
+ dataRead('order lookup'),
874
896
  async (token) => {
875
897
  const client = apiClient(opts)
876
898
  const [order] = await Promise.all([
@@ -904,11 +926,7 @@ export const ondoProvider = (
904
926
  ): Promise<FillsResponse> {
905
927
  return withSession<FillsResponse>(
906
928
  params.address,
907
- () => ({
908
- provider: ONDO_PROVIDER_KEY,
909
- items: [],
910
- pagination: { limit: params.limit ?? 0, hasMore: false },
911
- }),
929
+ dataRead('fills read'),
912
930
  async (token) => {
913
931
  const client = apiClient(opts)
914
932
  const queryParams: ApiParams = {}
@@ -951,11 +969,7 @@ export const ondoProvider = (
951
969
  ): Promise<ActivitiesResponse> {
952
970
  return withSession<ActivitiesResponse>(
953
971
  params.address,
954
- () => ({
955
- provider: ONDO_PROVIDER_KEY,
956
- items: [],
957
- pagination: { limit: params.limit ?? 0, hasMore: false },
958
- }),
972
+ dataRead('activity read'),
959
973
  async (token) => {
960
974
  const inputCursor = decodeActivityCursor(params.cursor)
961
975
  const client = apiClient(opts)
@@ -1095,7 +1109,7 @@ export const ondoProvider = (
1095
1109
  ): Promise<PortfolioHistoryResponse> {
1096
1110
  return withSession<PortfolioHistoryResponse>(
1097
1111
  params.address,
1098
- () => ({ range: params.range, points: [] }),
1112
+ dataRead('portfolio history read'),
1099
1113
  async (token) => {
1100
1114
  const client = apiClient(opts)
1101
1115
  const [graph, summary] = await Promise.all([
@@ -1129,9 +1143,9 @@ export const ondoProvider = (
1129
1143
  return getAccountSummary(account)
1130
1144
  },
1131
1145
 
1132
- formatOrderPrice,
1146
+ snapOrderPrice,
1133
1147
 
1134
- formatOrderSize,
1148
+ snapOrderSize,
1135
1149
 
1136
1150
  estimateLiquidationPrice,
1137
1151
 
@@ -38,6 +38,6 @@ export { mapOrder, mapOrderStatus, mapOrderUpdates } from './mapOrder.js'
38
38
  export { mapOpenPositions, mapPosition } from './mapPosition.js'
39
39
  export { intervalFromBarSpan, mapInterval } from './ohlcvInterval.js'
40
40
  export { ondoAsset } from './ondoAsset.js'
41
- export { formatOrderPrice, formatOrderSize } from './orderFormatting.js'
41
+ export { snapOrderPrice, snapOrderSize } from './orderFormatting.js'
42
42
  export { positionRemovableMargin } from './transferMargin.js'
43
43
  export { ondoWithdrawableBalances } from './withdrawableBalances.js'
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import {
10
- estimateIsolatedLiquidationPrice,
10
+ estimateLiquidationPrice as estimateIsolatedLiquidationPrice,
11
11
  type LiquidationEstimateParams,
12
12
  } from '@lifi/perps-sdk'
13
13
  import type { PerpsMarket } from '@lifi/perps-types'
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ondo order size and price formatting for exchange submission.
2
+ * Ondo order size and price snapping for exchange submission.
3
3
  *
4
4
  * Ondo's tick and lot grids are exact contract increments (`quoteIncrement` /
5
5
  * `baseIncrement`) that need not be powers of ten. When the generic
@@ -10,7 +10,11 @@
10
10
  */
11
11
 
12
12
  import { PerpsError } from '@lifi/perps-sdk'
13
- import { type Market, PerpsErrorCode } from '@lifi/perps-types'
13
+ import {
14
+ type DecimalString,
15
+ type Market,
16
+ PerpsErrorCode,
17
+ } from '@lifi/perps-types'
14
18
  import Big from 'big.js'
15
19
 
16
20
  const powerOfTenIncrement = (decimals: number): Big => new Big(`1e-${decimals}`)
@@ -25,21 +29,24 @@ const priceGrid = (market: Market): Big => {
25
29
  throw new PerpsError(
26
30
  PerpsErrorCode.ValidationError,
27
31
  `Market '${market.id}' carries no price grid; Ondo order prices cannot ` +
28
- `be formatted without the market's tick increment.`
32
+ `be snapped without the market's tick increment.`
29
33
  )
30
34
  }
31
35
 
32
36
  /**
33
- * Format a price onto an Ondo market's tick grid: snapped half-up to the
34
- * nearest `priceIncrement` (or the grid implied by `priceDecimals`), trailing
35
- * zeros stripped.
37
+ * Snap a price onto an Ondo market's tick grid: half-up to the nearest
38
+ * `priceIncrement` (or the grid implied by `priceDecimals`), trailing zeros
39
+ * stripped.
36
40
  *
37
41
  * @throws {PerpsError} `ValidationError` when the market carries neither
38
42
  * `priceIncrement` nor `priceDecimals` — without the venue's tick grid no
39
43
  * correct price can be produced.
40
44
  * @public
41
45
  */
42
- export function formatOrderPrice(market: Market, price: number): string {
46
+ export function snapOrderPrice(
47
+ market: Market,
48
+ price: DecimalString
49
+ ): DecimalString {
43
50
  const increment = priceGrid(market)
44
51
  const snapped = new Big(price)
45
52
  .div(increment)
@@ -49,7 +56,7 @@ export function formatOrderPrice(market: Market, price: number): string {
49
56
  }
50
57
 
51
58
  /**
52
- * Format a size onto an Ondo market's lot grid: truncated toward zero (never
59
+ * Snap a size onto an Ondo market's lot grid: truncated toward zero (never
53
60
  * rounded up, so the size cannot exceed the user's balance) to the market's
54
61
  * `sizeIncrement` (or the grid implied by `szDecimals`), trailing zeros
55
62
  * stripped.
@@ -57,7 +64,10 @@ export function formatOrderPrice(market: Market, price: number): string {
57
64
  * @param size - Size in base-asset units as a non-negative magnitude.
58
65
  * @public
59
66
  */
60
- export function formatOrderSize(market: Market, size: number): string {
67
+ export function snapOrderSize(
68
+ market: Market,
69
+ size: DecimalString
70
+ ): DecimalString {
61
71
  const increment =
62
72
  market.sizeIncrement !== undefined
63
73
  ? new Big(market.sizeIncrement)
@@ -1,4 +1,7 @@
1
- import type { ProviderWithdrawableBalance } from '@lifi/perps-sdk'
1
+ import {
2
+ calculateWithdrawMax,
3
+ type ProviderWithdrawableBalance,
4
+ } from '@lifi/perps-sdk'
2
5
  import type { OndoBalanceSummary } from '../types/wire.js'
3
6
  import { toWireBig } from './decimal.js'
4
7
 
@@ -10,9 +13,9 @@ import { toWireBig } from './decimal.js'
10
13
  *
11
14
  * @param assetId - `Asset.id` the row is keyed by.
12
15
  * @param withdrawalFeeUsd - The account's `withdrawalFeeUSD`. Ondo collateral
13
- * is USDC, so the USD fee is the row fee 1:1 in collateral units. Ondo
14
- * charges it on top of the requested amount. Absent leaves the row without a
15
- * fee.
16
+ * is USDC, so the USD fee is the row fee 1:1 in collateral units. Ondo
17
+ * charges it on top of the requested amount. Absent leaves the row without a
18
+ * fee.
16
19
  * @public
17
20
  */
18
21
  export const ondoWithdrawableBalances = (
@@ -27,20 +30,19 @@ export const ondoWithdrawableBalances = (
27
30
  if (!available.gt(0)) {
28
31
  return []
29
32
  }
30
- return [
31
- {
32
- assetId,
33
- route: 'perps',
34
- available: available.toFixed(),
35
- ...(withdrawalFeeUsd === undefined
36
- ? {}
37
- : {
38
- withdrawalFee: toWireBig(
39
- withdrawalFeeUsd,
40
- 'account.withdrawalFeeUSD'
41
- ).toFixed(),
42
- isFeeDeducted: false,
43
- }),
44
- },
45
- ]
33
+ const row = {
34
+ assetId,
35
+ route: 'perps' as const,
36
+ available: available.toFixed(),
37
+ ...(withdrawalFeeUsd === undefined
38
+ ? {}
39
+ : {
40
+ withdrawalFee: toWireBig(
41
+ withdrawalFeeUsd,
42
+ 'account.withdrawalFeeUSD'
43
+ ).toFixed(),
44
+ isFeeDeducted: false,
45
+ }),
46
+ }
47
+ return [{ ...row, max: calculateWithdrawMax(row) }]
46
48
  }
@@ -3,6 +3,7 @@ import {
3
3
  getMarketRegistry,
4
4
  localStorageAdapter,
5
5
  type MarketRegistry,
6
+ PerpsError,
6
7
  type PerpsSDKClient,
7
8
  type ProviderGetQuoteParams,
8
9
  type QuoteListener,
@@ -16,12 +17,13 @@ import {
16
17
  type WsProviderFactoryParams,
17
18
  wsLog,
18
19
  } from '@lifi/perps-sdk'
19
- import type {
20
- MarketContext,
21
- MarketDisplay,
22
- OndoAccountBalance,
23
- PerpsMarketDisplay,
24
- Subscription,
20
+ import {
21
+ type MarketContext,
22
+ type MarketDisplay,
23
+ type OndoAccountBalance,
24
+ PerpsErrorCode,
25
+ type PerpsMarketDisplay,
26
+ type Subscription,
25
27
  } from '@lifi/perps-types'
26
28
  import type { Address } from 'viem'
27
29
  import { OndoTokenStore } from '../auth/OndoTokenStore.js'
@@ -44,7 +46,6 @@ import type {
44
46
  OndoWsMessage,
45
47
  OndoWsTrade,
46
48
  } from '../types/index.js'
47
- import { OndoSessionExpiredError } from '../utils/apiClient.js'
48
49
  import {
49
50
  mapFill,
50
51
  mapOrderUpdates,
@@ -65,7 +66,7 @@ import { intervalFromBarSpan, mapInterval } from '../utils/ohlcvInterval.js'
65
66
  // connection, sent before the first private subscribe; the venue rejects a
66
67
  // second login on the same connection. The JWT comes from the SIWE session in
67
68
  // the token store — absent or expired reads back as null and surfaces as
68
- // `OndoSessionExpiredError` so callers re-run the SIWE login. A dropped
69
+ // `PerpsError` with `SetupRequired` so callers run the SIWE login. A dropped
69
70
  // socket forgets the login, so the guard resets on every close.
70
71
 
71
72
  const ONDO_AUTH_CHANNEL = {
@@ -580,14 +581,16 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
580
581
  }
581
582
 
582
583
  /**
583
- * The stored SIWE session for `address`, or {@link OndoSessionExpiredError}
584
- * when it is absent or expired. Shared by the subscribe-time guard, the
585
- * login op and the account-summary seed so all three report one message.
584
+ * The stored SIWE session for `address`, or {@link PerpsError} with
585
+ * `SetupRequired` when it is absent or expired. Shared by the subscribe-time
586
+ * guard, the login op and the account-summary seed so all three report one
587
+ * message.
586
588
  */
587
589
  private async requireSession(address: Address): Promise<OndoAuthToken> {
588
590
  const token = await this.tokenStore.get(address)
589
591
  if (token === null) {
590
- throw new OndoSessionExpiredError(
592
+ throw new PerpsError(
593
+ PerpsErrorCode.SetupRequired,
591
594
  `No Ondo session for ${address}. Run the SIWE login before subscribing to account channels.`
592
595
  )
593
596
  }
@@ -628,9 +631,9 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
628
631
 
629
632
  /**
630
633
  * Seed the account-summary figures from the REST balance so the first emit
631
- * is complete before any positions frame lands. Throws {@link
632
- * OndoSessionExpiredError} when the SIWE session is missing, matching the
633
- * other authenticated channels.
634
+ * is complete before any positions frame lands. Throws {@link PerpsError}
635
+ * with `SetupRequired` when the SIWE session is missing, matching the other
636
+ * authenticated channels.
634
637
  */
635
638
  private async seedAccountSummary(
636
639
  address: Address,