@lifi/perps-sdk 14.0.0 → 16.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 (89) hide show
  1. package/README.md +27 -0
  2. package/dist/cjs/client/PerpsClient.d.ts +6 -1
  3. package/dist/cjs/client/PerpsClient.d.ts.map +1 -1
  4. package/dist/cjs/client/PerpsClient.js +43 -8
  5. package/dist/cjs/client/PerpsClient.js.map +1 -1
  6. package/dist/cjs/index.d.ts +4 -3
  7. package/dist/cjs/index.d.ts.map +1 -1
  8. package/dist/cjs/index.js +6 -5
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/registry/assetRegistry.d.ts +2 -1
  11. package/dist/cjs/registry/assetRegistry.d.ts.map +1 -1
  12. package/dist/cjs/registry/assetRegistry.js +11 -1
  13. package/dist/cjs/registry/assetRegistry.js.map +1 -1
  14. package/dist/cjs/types/provider.d.ts +6 -1
  15. package/dist/cjs/types/provider.d.ts.map +1 -1
  16. package/dist/cjs/utils/explorer.d.ts +3 -0
  17. package/dist/cjs/utils/explorer.d.ts.map +1 -1
  18. package/dist/cjs/utils/explorer.js +6 -0
  19. package/dist/cjs/utils/explorer.js.map +1 -1
  20. package/dist/cjs/utils/orderClassification.d.ts.map +1 -1
  21. package/dist/cjs/utils/orderClassification.js +1 -0
  22. package/dist/cjs/utils/orderClassification.js.map +1 -1
  23. package/dist/cjs/version.d.ts +1 -1
  24. package/dist/cjs/version.js +1 -1
  25. package/dist/cjs/websocket/decodeChain.d.ts +11 -0
  26. package/dist/cjs/websocket/decodeChain.d.ts.map +1 -0
  27. package/dist/cjs/websocket/decodeChain.js +37 -0
  28. package/dist/cjs/websocket/decodeChain.js.map +1 -0
  29. package/dist/esm/client/PerpsClient.d.ts +30 -5
  30. package/dist/esm/client/PerpsClient.d.ts.map +1 -1
  31. package/dist/esm/client/PerpsClient.js +77 -13
  32. package/dist/esm/client/PerpsClient.js.map +1 -1
  33. package/dist/esm/index.d.ts +4 -3
  34. package/dist/esm/index.d.ts.map +1 -1
  35. package/dist/esm/index.js +2 -2
  36. package/dist/esm/index.js.map +1 -1
  37. package/dist/esm/registry/assetRegistry.d.ts +7 -1
  38. package/dist/esm/registry/assetRegistry.d.ts.map +1 -1
  39. package/dist/esm/registry/assetRegistry.js +14 -0
  40. package/dist/esm/registry/assetRegistry.js.map +1 -1
  41. package/dist/esm/types/provider.d.ts +20 -1
  42. package/dist/esm/types/provider.d.ts.map +1 -1
  43. package/dist/esm/utils/explorer.d.ts +3 -0
  44. package/dist/esm/utils/explorer.d.ts.map +1 -1
  45. package/dist/esm/utils/explorer.js +6 -0
  46. package/dist/esm/utils/explorer.js.map +1 -1
  47. package/dist/esm/utils/orderClassification.d.ts.map +1 -1
  48. package/dist/esm/utils/orderClassification.js +1 -0
  49. package/dist/esm/utils/orderClassification.js.map +1 -1
  50. package/dist/esm/version.d.ts +1 -1
  51. package/dist/esm/version.js +1 -1
  52. package/dist/esm/websocket/decodeChain.d.ts +30 -0
  53. package/dist/esm/websocket/decodeChain.d.ts.map +1 -0
  54. package/dist/esm/websocket/decodeChain.js +44 -0
  55. package/dist/esm/websocket/decodeChain.js.map +1 -0
  56. package/dist/types/client/PerpsClient.d.ts +30 -5
  57. package/dist/types/client/PerpsClient.d.ts.map +1 -1
  58. package/dist/types/index.d.ts +4 -3
  59. package/dist/types/index.d.ts.map +1 -1
  60. package/dist/types/registry/assetRegistry.d.ts +7 -1
  61. package/dist/types/registry/assetRegistry.d.ts.map +1 -1
  62. package/dist/types/types/provider.d.ts +20 -1
  63. package/dist/types/types/provider.d.ts.map +1 -1
  64. package/dist/types/utils/explorer.d.ts +3 -0
  65. package/dist/types/utils/explorer.d.ts.map +1 -1
  66. package/dist/types/utils/orderClassification.d.ts.map +1 -1
  67. package/dist/types/version.d.ts +1 -1
  68. package/dist/types/websocket/decodeChain.d.ts +30 -0
  69. package/dist/types/websocket/decodeChain.d.ts.map +1 -0
  70. package/package.json +2 -2
  71. package/src/client/PerpsClient.ts +111 -18
  72. package/src/index.ts +8 -5
  73. package/src/registry/assetRegistry.ts +16 -1
  74. package/src/types/provider.ts +25 -0
  75. package/src/utils/explorer.ts +6 -0
  76. package/src/utils/orderClassification.ts +1 -0
  77. package/src/version.ts +1 -1
  78. package/src/websocket/decodeChain.ts +54 -0
  79. package/dist/cjs/utils/accountSummary.d.ts +0 -4
  80. package/dist/cjs/utils/accountSummary.d.ts.map +0 -1
  81. package/dist/cjs/utils/accountSummary.js +0 -35
  82. package/dist/cjs/utils/accountSummary.js.map +0 -1
  83. package/dist/esm/utils/accountSummary.d.ts +0 -32
  84. package/dist/esm/utils/accountSummary.d.ts.map +0 -1
  85. package/dist/esm/utils/accountSummary.js +0 -46
  86. package/dist/esm/utils/accountSummary.js.map +0 -1
  87. package/dist/types/utils/accountSummary.d.ts +0 -32
  88. package/dist/types/utils/accountSummary.d.ts.map +0 -1
  89. package/src/utils/accountSummary.ts +0 -90
@@ -4,6 +4,7 @@ import type {
4
4
  ActionParamsMap,
5
5
  ActionResult,
6
6
  ActionStep,
7
+ AvailableToTrade,
7
8
  CreateActionResponse,
8
9
  ExecuteActionResponse,
9
10
  MarketRef,
@@ -28,7 +29,8 @@ import {
28
29
  import Big from 'big.js'
29
30
  import type { Address } from 'viem'
30
31
  import { PerpsError } from '../errors/PerpsError.js'
31
- import { getAssetRegistry } from '../registry/assetRegistry.js'
32
+ import { getAssetRegistry, toAssetDisplay } from '../registry/assetRegistry.js'
33
+ import { getMarketRegistry } from '../registry/marketRegistry.js'
32
34
  import { createAction } from '../services/createAction.js'
33
35
  import { executeAction } from '../services/executeAction.js'
34
36
  import { getAccount as fetchAccount } from '../services/getAccount.js'
@@ -109,6 +111,32 @@ function findActionDescriptor(
109
111
  return descriptor
110
112
  }
111
113
 
114
+ /**
115
+ * Split an ordered `createAction` batch into consecutive runs of steps that
116
+ * share one descriptor. A batch is a chain and may mix descriptors — a
117
+ * Hyperliquid `placeOrder` can arrive with a USER-signed builder-fee approval
118
+ * ahead of the SDK-signed order — and each run must be signed by its own
119
+ * descriptor's signer.
120
+ *
121
+ * @throws {PerpsError} When a step's action is not declared by the provider.
122
+ */
123
+ function groupStepsByDescriptor(
124
+ metadata: Provider,
125
+ steps: ActionStep[]
126
+ ): { descriptor: ProviderAction; steps: ActionStep[] }[] {
127
+ const groups: { descriptor: ProviderAction; steps: ActionStep[] }[] = []
128
+ for (const step of steps) {
129
+ const descriptor = findActionDescriptor(metadata, step.action)
130
+ const current = groups.at(-1)
131
+ if (current !== undefined && current.descriptor.type === descriptor.type) {
132
+ current.steps.push(step)
133
+ continue
134
+ }
135
+ groups.push({ descriptor, steps: [step] })
136
+ }
137
+ return groups
138
+ }
139
+
112
140
  /**
113
141
  * The primary high-level perps API: wraps a {@link PerpsSDKClient} and owns the
114
142
  * end-to-end signing pipeline for provider setup, orders, and account-level
@@ -241,15 +269,21 @@ export class PerpsClient {
241
269
  }
242
270
 
243
271
  /**
244
- * Delegate signing of `actions` to the provider plugin. The plugin owns
245
- * every signing arm and branches on the descriptor's `signers` internally,
246
- * reading the end-user's wallet from the {@link SignActionsContext} when an
247
- * arm signs as the user.
272
+ * Delegate signing of `actions` to the provider plugin, one call per
273
+ * consecutive run of steps sharing a descriptor. Each call carries that
274
+ * descriptor's `signingMethod` and `signers`, so a mixed batch signs every
275
+ * step with the signer its own descriptor declares; the signed steps come
276
+ * back in the batch's original order. The plugin owns every signing arm and
277
+ * branches on `signers` internally, reading the end-user's wallet from the
278
+ * {@link SignActionsContext} when an arm signs as the user.
279
+ *
280
+ * @throws {PerpsError} When a step's action is not declared by the provider,
281
+ * or the plugin implements no `signActions`.
248
282
  */
249
283
  private async delegateSignActions(
250
284
  provider: string,
251
285
  address: Address,
252
- descriptor: ProviderAction,
286
+ metadata: Provider,
253
287
  actions: ActionStep[],
254
288
  onProgress?: (progress: SignActionProgress) => void
255
289
  ): Promise<SignedActionStep[]> {
@@ -257,17 +291,25 @@ export class PerpsClient {
257
291
  if (typeof plugin.signActions !== 'function') {
258
292
  throw new PerpsError(
259
293
  PerpsErrorCode.SDKError,
260
- `Provider '${provider}' does not implement signActions for ` +
261
- `signingMethod '${descriptor.signingMethod}'.`
294
+ `Provider '${provider}' does not implement signActions.`
262
295
  )
263
296
  }
264
- const userWallet = await this.resolveSigningWallet(descriptor, actions)
265
- return plugin.signActions(
266
- descriptor.signingMethod,
267
- actions,
268
- address,
269
- this.buildSignActionsContext(descriptor, userWallet, onProgress)
270
- )
297
+ const signed: SignedActionStep[] = []
298
+ for (const group of groupStepsByDescriptor(metadata, actions)) {
299
+ const userWallet = await this.resolveSigningWallet(
300
+ group.descriptor,
301
+ group.steps
302
+ )
303
+ signed.push(
304
+ ...(await plugin.signActions(
305
+ group.descriptor.signingMethod,
306
+ group.steps,
307
+ address,
308
+ this.buildSignActionsContext(group.descriptor, userWallet, onProgress)
309
+ ))
310
+ )
311
+ }
312
+ return signed
271
313
  }
272
314
 
273
315
  /**
@@ -409,11 +451,10 @@ export class PerpsClient {
409
451
  step: ActionStep
410
452
  ): Promise<SignedActionStep | undefined> {
411
453
  const metadata = await this.getProviderMetadata(provider)
412
- const descriptor = findActionDescriptor(metadata, step.action)
413
454
  const [signed] = await this.delegateSignActions(
414
455
  provider,
415
456
  address,
416
- descriptor,
457
+ metadata,
417
458
  [step]
418
459
  )
419
460
  return signed
@@ -498,6 +539,58 @@ export class PerpsClient {
498
539
  })
499
540
  }
500
541
 
542
+ /**
543
+ * The amounts `params.address` can still buy and sell on one market, in
544
+ * that market's margin asset. The order panel reads this per-market figure;
545
+ * account displays read the account-scoped
546
+ * {@link AccountSummary.availableMargin} instead.
547
+ *
548
+ * Providers that read a per-market figure answer it directly. For every
549
+ * other provider this falls back to the account summary, so both sides
550
+ * equal `availableMargin` and the asset is the market's quote asset.
551
+ *
552
+ * @throws {PerpsError} When the provider plugin is not registered, or the
553
+ * market is unknown to the provider's market registry.
554
+ * @public
555
+ */
556
+ async getAvailableToTrade(
557
+ params: {
558
+ provider: string
559
+ address: Address
560
+ marketId: string
561
+ },
562
+ options?: SDKRequestOptions
563
+ ): Promise<AvailableToTrade> {
564
+ const plugin = this.requireProvider(params.provider)
565
+ const perMarket = await plugin.getAvailableToTrade?.(
566
+ { address: params.address, marketId: params.marketId },
567
+ options
568
+ )
569
+ if (perMarket !== undefined) {
570
+ return perMarket
571
+ }
572
+
573
+ const registry = getMarketRegistry(this.sdkClient, params.provider)
574
+ await registry.sync()
575
+ const market = registry.require(params.marketId)
576
+ const account = await fetchAccount(
577
+ this.sdkClient,
578
+ { provider: params.provider, address: params.address },
579
+ options
580
+ )
581
+ const { availableMargin } = plugin.getAccountSummary(
582
+ account,
583
+ account.positions
584
+ )
585
+ return {
586
+ providerId: market.providerId,
587
+ marketId: market.id,
588
+ asset: toAssetDisplay(market.quoteAsset),
589
+ buy: availableMargin,
590
+ sell: availableMargin,
591
+ }
592
+ }
593
+
501
594
  /**
502
595
  * Resolve the exact venue-owned margin requirements for `position`.
503
596
  * Returns `undefined` when the position has no individual margin adjustment.
@@ -1218,7 +1311,7 @@ export class PerpsClient {
1218
1311
  const signedActions = await this.delegateSignActions(
1219
1312
  provider,
1220
1313
  address,
1221
- descriptor,
1314
+ metadata,
1222
1315
  actions,
1223
1316
  onProgress
1224
1317
  )
package/src/index.ts CHANGED
@@ -19,7 +19,11 @@ export { PerpsClient } from './client/PerpsClient.js'
19
19
  export { PerpsErrorMessage } from './errors/constants.js'
20
20
  export { PerpsError } from './errors/PerpsError.js'
21
21
  // Registries
22
- export { AssetRegistry, getAssetRegistry } from './registry/assetRegistry.js'
22
+ export {
23
+ AssetRegistry,
24
+ getAssetRegistry,
25
+ toAssetDisplay,
26
+ } from './registry/assetRegistry.js'
23
27
  export type { DefaultMarketProvider } from './registry/marketRegistry.js'
24
28
  export {
25
29
  DEFAULT_MARKET_ID,
@@ -152,6 +156,7 @@ export type {
152
156
  ProviderAccountExistsParams,
153
157
  ProviderGetAccountParams,
154
158
  ProviderGetActivityParams,
159
+ ProviderGetAvailableToTradeParams,
155
160
  ProviderGetDepositFlowParams,
156
161
  ProviderGetFillsParams,
157
162
  ProviderGetMarketSettingsParams,
@@ -170,10 +175,6 @@ export type {
170
175
  WithdrawableBalance,
171
176
  WithdrawalRoute,
172
177
  } from './types/withdrawal.js'
173
- export {
174
- type CollateralSemantics,
175
- summarizeAccount,
176
- } from './utils/accountSummary.js'
177
178
  export { paginateActivity } from './utils/activityPaging.js'
178
179
  export type { ExpectedPnl } from './utils/calculations.js'
179
180
  export {
@@ -259,6 +260,8 @@ export { createWarnOnce } from './utils/warnOnce.js'
259
260
  // Version
260
261
  export { name, version } from './version.js'
261
262
  export { cachePromise } from './websocket/cachePromise.js'
263
+ export type { DecodeChainMode } from './websocket/decodeChain.js'
264
+ export { DecodeChain } from './websocket/decodeChain.js'
262
265
  export type {
263
266
  PerpsWsClientOptions,
264
267
  WsProviderFactory,
@@ -1,4 +1,4 @@
1
- import type { Asset, AssetsResponse } from '@lifi/perps-types'
1
+ import type { Asset, AssetDisplay, AssetsResponse } from '@lifi/perps-types'
2
2
  import { PerpsErrorCode } from '@lifi/perps-types'
3
3
  import { PerpsError } from '../errors/PerpsError.js'
4
4
  import { buildUrl, request } from '../transport/request.js'
@@ -59,6 +59,21 @@ export class AssetRegistry extends ReferenceDataRegistry<Asset> {
59
59
  }
60
60
  }
61
61
 
62
+ /**
63
+ * Project an {@link Asset} to the identity and labels a display needs.
64
+ *
65
+ * @public
66
+ */
67
+ export const toAssetDisplay = (asset: Asset): AssetDisplay => ({
68
+ providerId: asset.providerId,
69
+ id: asset.id,
70
+ displaySymbol: asset.displaySymbol,
71
+ logoURI: asset.logoURI,
72
+ ...(asset.displayName === undefined
73
+ ? {}
74
+ : { displayName: asset.displayName }),
75
+ })
76
+
62
77
  const registries = new WeakMap<PerpsSDKClient, Map<string, AssetRegistry>>()
63
78
 
64
79
  /**
@@ -8,6 +8,7 @@ import type {
8
8
  ActionType,
9
9
  ActivitiesResponse,
10
10
  ActivityType,
11
+ AvailableToTrade,
11
12
  FillsResponse,
12
13
  Market,
13
14
  MarketRef,
@@ -216,6 +217,17 @@ export interface ProviderGetMarketSettingsParams {
216
217
  market: MarketRef
217
218
  }
218
219
 
220
+ /**
221
+ * Read params for {@link PerpsProviderPlugin.getAvailableToTrade}.
222
+ *
223
+ * @public
224
+ */
225
+ export interface ProviderGetAvailableToTradeParams {
226
+ address: Address
227
+ /** The market's opaque `Market.id` (not a display symbol). */
228
+ marketId: string
229
+ }
230
+
219
231
  /**
220
232
  * Read params for {@link PerpsProvider.getOrders}.
221
233
  *
@@ -442,6 +454,19 @@ export interface PerpsProviderPlugin {
442
454
  options?: SDKRequestOptions
443
455
  ): Promise<MarketSettings | undefined>
444
456
 
457
+ /**
458
+ * The amounts the account can still buy and sell on one market, in that
459
+ * market's margin asset. Optional because venues expose this unevenly:
460
+ * Hyperliquid reads it directly (`activeAssetData`), other venues publish
461
+ * only an account-scoped figure. `undefined` means the venue has nothing
462
+ * to read for this market, and the caller falls back to the account
463
+ * summary.
464
+ */
465
+ getAvailableToTrade?(
466
+ params: ProviderGetAvailableToTradeParams,
467
+ options?: SDKRequestOptions
468
+ ): Promise<AvailableToTrade | undefined>
469
+
445
470
  getOrders(
446
471
  params: ProviderGetOrdersParams,
447
472
  options?: SDKRequestOptions
@@ -15,7 +15,10 @@ export const ExplorerChainId = {
15
15
  ETHEREUM: 1,
16
16
  ARBITRUM_ONE: 42161,
17
17
  LIGHTER: 304,
18
+ ROBINHOOD: 4663,
18
19
  HYPERLIQUID: 999,
20
+ AVALANCHE_C_CHAIN: 43114,
21
+ BNB_SMART_CHAIN: 56,
19
22
  } as const
20
23
 
21
24
  /**
@@ -30,7 +33,10 @@ const TX_BASE_URL_BY_CHAIN: Record<ExplorerChainId, string> = {
30
33
  [ExplorerChainId.ETHEREUM]: 'https://etherscan.io/tx/',
31
34
  [ExplorerChainId.ARBITRUM_ONE]: 'https://arbiscan.io/tx/',
32
35
  [ExplorerChainId.LIGHTER]: 'https://app.lighter.xyz/explorer/logs/',
36
+ [ExplorerChainId.ROBINHOOD]: 'https://robin.etherscan.io/tx/',
33
37
  [ExplorerChainId.HYPERLIQUID]: 'https://app.hyperliquid.xyz/explorer/tx/',
38
+ [ExplorerChainId.AVALANCHE_C_CHAIN]: 'https://snowtrace.io/tx/',
39
+ [ExplorerChainId.BNB_SMART_CHAIN]: 'https://bscscan.com/tx/',
34
40
  }
35
41
 
36
42
  /**
@@ -84,6 +84,7 @@ export function triggerConditionFor(
84
84
  export const ACTIVE_ORDER_STATUSES: ReadonlySet<OrderStatus> = new Set([
85
85
  OrderStatus.OPEN,
86
86
  OrderStatus.PENDING,
87
+ OrderStatus.ACCEPTED,
87
88
  OrderStatus.PARTIALLY_FILLED,
88
89
  OrderStatus.TRIGGERED,
89
90
  ])
package/src/version.ts CHANGED
@@ -10,4 +10,4 @@ export const name = '@lifi/perps-sdk'
10
10
  *
11
11
  * @public
12
12
  */
13
- export const version = '14.0.0'
13
+ export const version = '16.0.0'
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Frame semantics of a {@link DecodeChain}:
3
+ * - `'every'`: every frame decodes and applies in arrival order — required for
4
+ * delta-carrying channels, where dropping a frame corrupts downstream state.
5
+ * - `'latest'`: frames queued behind an in-flight decode coalesce to the
6
+ * newest — for snapshot-like channels, where a newer frame supersedes
7
+ * anything still waiting.
8
+ */
9
+ export type DecodeChainMode = 'every' | 'latest'
10
+
11
+ /**
12
+ * Serializes async WS frame decodes so results apply in arrival order.
13
+ * In `'latest'` mode a backlog (e.g. queued behind a main-thread stall)
14
+ * collapses to its newest frame instead of replaying every stale decode.
15
+ * Decode failures are reported to `onError` and never break the chain.
16
+ */
17
+ export class DecodeChain {
18
+ private tail: Promise<void> = Promise.resolve()
19
+ private queued: (() => Promise<void>) | undefined
20
+
21
+ constructor(
22
+ private readonly mode: DecodeChainMode,
23
+ private readonly onError: (error: unknown) => void
24
+ ) {}
25
+
26
+ push(decode: () => Promise<void>): void {
27
+ if (this.mode === 'every') {
28
+ this.tail = this.tail.then(decode).catch(this.onError)
29
+ return
30
+ }
31
+ const drainScheduled = this.queued !== undefined
32
+ this.queued = decode
33
+ if (drainScheduled) {
34
+ return
35
+ }
36
+ this.tail = this.tail
37
+ .then(() => {
38
+ const next = this.queued
39
+ this.queued = undefined
40
+ return next?.()
41
+ })
42
+ .catch(this.onError)
43
+ }
44
+
45
+ /**
46
+ * Detach on connection teardown: drops the queued latest-mode decode and
47
+ * starts subsequent pushes on a fresh chain. A decode already in flight
48
+ * still settles.
49
+ */
50
+ reset(): void {
51
+ this.tail = Promise.resolve()
52
+ this.queued = undefined
53
+ }
54
+ }
@@ -1,4 +0,0 @@
1
- import type { AccountResponse, AccountSummary, Position } from '@lifi/perps-types';
2
- export type CollateralSemantics = 'free' | 'net' | 'gross' | 'equity';
3
- export declare function summarizeAccount(account: AccountResponse, positions: Position[], semantics: CollateralSemantics): AccountSummary;
4
- //# sourceMappingURL=accountSummary.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"accountSummary.d.ts","sourceRoot":"","sources":["../../../src/utils/accountSummary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,QAAQ,EACT,MAAM,mBAAmB,CAAA;AAgC1B,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;AAUrE,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,QAAQ,EAAE,EACrB,SAAS,EAAE,mBAAmB,GAC7B,cAAc,CAuChB"}
@@ -1,35 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.summarizeAccount = summarizeAccount;
4
- const parse_js_1 = require("./parse.js");
5
- const sumValueUsd = (balances) => balances.reduce((sum, b) => sum + (0, parse_js_1.stringToFloat)(b.valueUsd), 0);
6
- const sumMarginUsd = (balances) => balances.reduce((sum, b) => sum + (0, parse_js_1.stringToFloat)(b.valueUsd) * (b.collateralWeight ?? 1), 0);
7
- function summarizeAccount(account, positions, semantics) {
8
- let marginUsed = 0;
9
- let unrealizedPnl = 0;
10
- for (const p of positions) {
11
- marginUsed += (0, parse_js_1.stringToFloat)(p.marginUsed);
12
- unrealizedPnl += (0, parse_js_1.stringToFloat)(p.unrealizedPnl);
13
- }
14
- const collateral = sumValueUsd(account.collateralBalances);
15
- const marginCollateral = sumMarginUsd(account.collateralBalances);
16
- const balances = sumValueUsd(account.balances);
17
- const withLocked = semantics === 'free' || semantics === 'net';
18
- const grossCollateral = withLocked ? collateral + marginUsed : collateral;
19
- const grossMarginCollateral = withLocked
20
- ? marginCollateral + marginUsed
21
- : marginCollateral;
22
- const equity = semantics === 'equity' || semantics === 'net'
23
- ? grossCollateral
24
- : grossCollateral + unrealizedPnl;
25
- const availableMargin = grossMarginCollateral +
26
- (semantics === 'gross' ? unrealizedPnl : 0) -
27
- marginUsed;
28
- return {
29
- portfolioValue: (balances + equity).toString(),
30
- availableMargin: availableMargin.toString(),
31
- marginUsed: marginUsed.toString(),
32
- unrealizedPnl: unrealizedPnl.toString(),
33
- };
34
- }
35
- //# sourceMappingURL=accountSummary.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"accountSummary.js","sourceRoot":"","sources":["../../../src/utils/accountSummary.ts"],"names":[],"mappings":";;AA8CA,4CA2CC;AApFD,yCAA0C;AAE1C,MAAM,WAAW,GAAG,CAAC,QAAqC,EAAU,EAAE,CACpE,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAA,wBAAa,EAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;AAEjE,MAAM,YAAY,GAAG,CAAC,QAAqC,EAAU,EAAE,CACrE,QAAQ,CAAC,MAAM,CACb,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAA,wBAAa,EAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB,IAAI,CAAC,CAAC,EACvE,CAAC,CACF,CAAA;AAgCH,SAAgB,gBAAgB,CAC9B,OAAwB,EACxB,SAAqB,EACrB,SAA8B;IAE9B,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,IAAI,aAAa,GAAG,CAAC,CAAA;IACrB,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,UAAU,IAAI,IAAA,wBAAa,EAAC,CAAC,CAAC,UAAU,CAAC,CAAA;QACzC,aAAa,IAAI,IAAA,wBAAa,EAAC,CAAC,CAAC,aAAa,CAAC,CAAA;IACjD,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAI1D,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACjE,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAE9C,MAAM,UAAU,GAAG,SAAS,KAAK,MAAM,IAAI,SAAS,KAAK,KAAK,CAAA;IAC9D,MAAM,eAAe,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAA;IACzE,MAAM,qBAAqB,GAAG,UAAU;QACtC,CAAC,CAAC,gBAAgB,GAAG,UAAU;QAC/B,CAAC,CAAC,gBAAgB,CAAA;IACpB,MAAM,MAAM,GACV,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,KAAK;QAC3C,CAAC,CAAC,eAAe;QACjB,CAAC,CAAC,eAAe,GAAG,aAAa,CAAA;IAKrC,MAAM,eAAe,GACnB,qBAAqB;QACrB,CAAC,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,UAAU,CAAA;IAEZ,OAAO;QACL,cAAc,EAAE,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC,QAAQ,EAAE;QAC9C,eAAe,EAAE,eAAe,CAAC,QAAQ,EAAE;QAC3C,UAAU,EAAE,UAAU,CAAC,QAAQ,EAAE;QACjC,aAAa,EAAE,aAAa,CAAC,QAAQ,EAAE;KACxC,CAAA;AACH,CAAC"}
@@ -1,32 +0,0 @@
1
- import type { AccountResponse, AccountSummary, Position } from '@lifi/perps-types';
2
- /**
3
- * What the summed `collateralBalances` rows already contain, relative to the
4
- * positions' locked margin and unrealized PnL:
5
- *
6
- * - `'free'` — free collateral only; locked margin and unrealized PnL are
7
- * both carried by the positions. Available margin is the free collateral
8
- * as-is; unrealized PnL is not counted toward it.
9
- * - `'net'` — free collateral with unrealized PnL already marked in; only
10
- * the locked margin is carried by the positions (e.g. a venue-reported
11
- * available balance).
12
- * - `'gross'` — locked margin included, unrealized PnL carried by the
13
- * positions (e.g. spot holdings backing a unified account). The venue
14
- * counts unrealized PnL toward buying power, so it is added to available
15
- * margin on top of the gross collateral.
16
- * - `'equity'` — total equity: locked margin AND unrealized PnL included
17
- * (e.g. a venue-reported account value). Unrealized PnL is thus already
18
- * counted toward available margin.
19
- *
20
- * @public
21
- */
22
- export type CollateralSemantics = 'free' | 'net' | 'gross' | 'equity';
23
- /**
24
- * Roll an {@link AccountResponse} and its open positions up into an
25
- * {@link AccountSummary}.
26
- *
27
- * @param semantics - How the collateral rows relate to the positions'
28
- * locked margin and unrealized PnL; see {@link CollateralSemantics}.
29
- * @public
30
- */
31
- export declare function summarizeAccount(account: AccountResponse, positions: Position[], semantics: CollateralSemantics): AccountSummary;
32
- //# sourceMappingURL=accountSummary.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"accountSummary.d.ts","sourceRoot":"","sources":["../../../src/utils/accountSummary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,QAAQ,EACT,MAAM,mBAAmB,CAAA;AAY1B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;AAErE;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,QAAQ,EAAE,EACrB,SAAS,EAAE,mBAAmB,GAC7B,cAAc,CAuChB"}
@@ -1,46 +0,0 @@
1
- import { stringToFloat } from './parse.js';
2
- const sumValueUsd = (balances) => balances.reduce((sum, b) => sum + stringToFloat(b.valueUsd), 0);
3
- const sumMarginUsd = (balances) => balances.reduce((sum, b) => sum + stringToFloat(b.valueUsd) * (b.collateralWeight ?? 1), 0);
4
- /**
5
- * Roll an {@link AccountResponse} and its open positions up into an
6
- * {@link AccountSummary}.
7
- *
8
- * @param semantics - How the collateral rows relate to the positions'
9
- * locked margin and unrealized PnL; see {@link CollateralSemantics}.
10
- * @public
11
- */
12
- export function summarizeAccount(account, positions, semantics) {
13
- let marginUsed = 0;
14
- let unrealizedPnl = 0;
15
- for (const p of positions) {
16
- marginUsed += stringToFloat(p.marginUsed);
17
- unrealizedPnl += stringToFloat(p.unrealizedPnl);
18
- }
19
- const collateral = sumValueUsd(account.collateralBalances);
20
- // Margin-eligible collateral: each row's value scaled by its loan-to-value
21
- // weight (1 when unset), so a haircut asset backs buying power at less than
22
- // face while `portfolioValue` still reflects its full value.
23
- const marginCollateral = sumMarginUsd(account.collateralBalances);
24
- const balances = sumValueUsd(account.balances);
25
- const withLocked = semantics === 'free' || semantics === 'net';
26
- const grossCollateral = withLocked ? collateral + marginUsed : collateral;
27
- const grossMarginCollateral = withLocked
28
- ? marginCollateral + marginUsed
29
- : marginCollateral;
30
- const equity = semantics === 'equity' || semantics === 'net'
31
- ? grossCollateral
32
- : grossCollateral + unrealizedPnl;
33
- // Buying power is equity net of locked margin, so unrealized PnL counts
34
- // toward it. The `'free'` rows exclude uPnL from available margin, holding
35
- // it purely as free collateral.
36
- const availableMargin = grossMarginCollateral +
37
- (semantics === 'gross' ? unrealizedPnl : 0) -
38
- marginUsed;
39
- return {
40
- portfolioValue: (balances + equity).toString(),
41
- availableMargin: availableMargin.toString(),
42
- marginUsed: marginUsed.toString(),
43
- unrealizedPnl: unrealizedPnl.toString(),
44
- };
45
- }
46
- //# sourceMappingURL=accountSummary.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"accountSummary.js","sourceRoot":"","sources":["../../../src/utils/accountSummary.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,MAAM,WAAW,GAAG,CAAC,QAAqC,EAAU,EAAE,CACpE,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;AAEjE,MAAM,YAAY,GAAG,CAAC,QAAqC,EAAU,EAAE,CACrE,QAAQ,CAAC,MAAM,CACb,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB,IAAI,CAAC,CAAC,EACvE,CAAC,CACF,CAAA;AAwBH;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAwB,EACxB,SAAqB,EACrB,SAA8B;IAE9B,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,IAAI,aAAa,GAAG,CAAC,CAAA;IACrB,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,UAAU,IAAI,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;QACzC,aAAa,IAAI,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,CAAA;IACjD,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC1D,2EAA2E;IAC3E,4EAA4E;IAC5E,6DAA6D;IAC7D,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACjE,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAE9C,MAAM,UAAU,GAAG,SAAS,KAAK,MAAM,IAAI,SAAS,KAAK,KAAK,CAAA;IAC9D,MAAM,eAAe,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAA;IACzE,MAAM,qBAAqB,GAAG,UAAU;QACtC,CAAC,CAAC,gBAAgB,GAAG,UAAU;QAC/B,CAAC,CAAC,gBAAgB,CAAA;IACpB,MAAM,MAAM,GACV,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,KAAK;QAC3C,CAAC,CAAC,eAAe;QACjB,CAAC,CAAC,eAAe,GAAG,aAAa,CAAA;IAErC,wEAAwE;IACxE,2EAA2E;IAC3E,gCAAgC;IAChC,MAAM,eAAe,GACnB,qBAAqB;QACrB,CAAC,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,UAAU,CAAA;IAEZ,OAAO;QACL,cAAc,EAAE,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC,QAAQ,EAAE;QAC9C,eAAe,EAAE,eAAe,CAAC,QAAQ,EAAE;QAC3C,UAAU,EAAE,UAAU,CAAC,QAAQ,EAAE;QACjC,aAAa,EAAE,aAAa,CAAC,QAAQ,EAAE;KACxC,CAAA;AACH,CAAC"}
@@ -1,32 +0,0 @@
1
- import type { AccountResponse, AccountSummary, Position } from '@lifi/perps-types';
2
- /**
3
- * What the summed `collateralBalances` rows already contain, relative to the
4
- * positions' locked margin and unrealized PnL:
5
- *
6
- * - `'free'` — free collateral only; locked margin and unrealized PnL are
7
- * both carried by the positions. Available margin is the free collateral
8
- * as-is; unrealized PnL is not counted toward it.
9
- * - `'net'` — free collateral with unrealized PnL already marked in; only
10
- * the locked margin is carried by the positions (e.g. a venue-reported
11
- * available balance).
12
- * - `'gross'` — locked margin included, unrealized PnL carried by the
13
- * positions (e.g. spot holdings backing a unified account). The venue
14
- * counts unrealized PnL toward buying power, so it is added to available
15
- * margin on top of the gross collateral.
16
- * - `'equity'` — total equity: locked margin AND unrealized PnL included
17
- * (e.g. a venue-reported account value). Unrealized PnL is thus already
18
- * counted toward available margin.
19
- *
20
- * @public
21
- */
22
- export type CollateralSemantics = 'free' | 'net' | 'gross' | 'equity';
23
- /**
24
- * Roll an {@link AccountResponse} and its open positions up into an
25
- * {@link AccountSummary}.
26
- *
27
- * @param semantics - How the collateral rows relate to the positions'
28
- * locked margin and unrealized PnL; see {@link CollateralSemantics}.
29
- * @public
30
- */
31
- export declare function summarizeAccount(account: AccountResponse, positions: Position[], semantics: CollateralSemantics): AccountSummary;
32
- //# sourceMappingURL=accountSummary.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"accountSummary.d.ts","sourceRoot":"","sources":["../../../src/utils/accountSummary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,QAAQ,EACT,MAAM,mBAAmB,CAAA;AAY1B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;AAErE;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,QAAQ,EAAE,EACrB,SAAS,EAAE,mBAAmB,GAC7B,cAAc,CAuChB"}
@@ -1,90 +0,0 @@
1
- import type {
2
- AccountResponse,
3
- AccountSummary,
4
- Position,
5
- } from '@lifi/perps-types'
6
- import { stringToFloat } from './parse.js'
7
-
8
- const sumValueUsd = (balances: AccountResponse['balances']): number =>
9
- balances.reduce((sum, b) => sum + stringToFloat(b.valueUsd), 0)
10
-
11
- const sumMarginUsd = (balances: AccountResponse['balances']): number =>
12
- balances.reduce(
13
- (sum, b) => sum + stringToFloat(b.valueUsd) * (b.collateralWeight ?? 1),
14
- 0
15
- )
16
-
17
- /**
18
- * What the summed `collateralBalances` rows already contain, relative to the
19
- * positions' locked margin and unrealized PnL:
20
- *
21
- * - `'free'` — free collateral only; locked margin and unrealized PnL are
22
- * both carried by the positions. Available margin is the free collateral
23
- * as-is; unrealized PnL is not counted toward it.
24
- * - `'net'` — free collateral with unrealized PnL already marked in; only
25
- * the locked margin is carried by the positions (e.g. a venue-reported
26
- * available balance).
27
- * - `'gross'` — locked margin included, unrealized PnL carried by the
28
- * positions (e.g. spot holdings backing a unified account). The venue
29
- * counts unrealized PnL toward buying power, so it is added to available
30
- * margin on top of the gross collateral.
31
- * - `'equity'` — total equity: locked margin AND unrealized PnL included
32
- * (e.g. a venue-reported account value). Unrealized PnL is thus already
33
- * counted toward available margin.
34
- *
35
- * @public
36
- */
37
- export type CollateralSemantics = 'free' | 'net' | 'gross' | 'equity'
38
-
39
- /**
40
- * Roll an {@link AccountResponse} and its open positions up into an
41
- * {@link AccountSummary}.
42
- *
43
- * @param semantics - How the collateral rows relate to the positions'
44
- * locked margin and unrealized PnL; see {@link CollateralSemantics}.
45
- * @public
46
- */
47
- export function summarizeAccount(
48
- account: AccountResponse,
49
- positions: Position[],
50
- semantics: CollateralSemantics
51
- ): AccountSummary {
52
- let marginUsed = 0
53
- let unrealizedPnl = 0
54
- for (const p of positions) {
55
- marginUsed += stringToFloat(p.marginUsed)
56
- unrealizedPnl += stringToFloat(p.unrealizedPnl)
57
- }
58
-
59
- const collateral = sumValueUsd(account.collateralBalances)
60
- // Margin-eligible collateral: each row's value scaled by its loan-to-value
61
- // weight (1 when unset), so a haircut asset backs buying power at less than
62
- // face while `portfolioValue` still reflects its full value.
63
- const marginCollateral = sumMarginUsd(account.collateralBalances)
64
- const balances = sumValueUsd(account.balances)
65
-
66
- const withLocked = semantics === 'free' || semantics === 'net'
67
- const grossCollateral = withLocked ? collateral + marginUsed : collateral
68
- const grossMarginCollateral = withLocked
69
- ? marginCollateral + marginUsed
70
- : marginCollateral
71
- const equity =
72
- semantics === 'equity' || semantics === 'net'
73
- ? grossCollateral
74
- : grossCollateral + unrealizedPnl
75
-
76
- // Buying power is equity net of locked margin, so unrealized PnL counts
77
- // toward it. The `'free'` rows exclude uPnL from available margin, holding
78
- // it purely as free collateral.
79
- const availableMargin =
80
- grossMarginCollateral +
81
- (semantics === 'gross' ? unrealizedPnl : 0) -
82
- marginUsed
83
-
84
- return {
85
- portfolioValue: (balances + equity).toString(),
86
- availableMargin: availableMargin.toString(),
87
- marginUsed: marginUsed.toString(),
88
- unrealizedPnl: unrealizedPnl.toString(),
89
- }
90
- }