@lifi/perps-sdk-provider-hyperliquid 15.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 (181) hide show
  1. package/dist/cjs/HyperliquidProvider.d.ts.map +1 -1
  2. package/dist/cjs/HyperliquidProvider.js +4 -0
  3. package/dist/cjs/HyperliquidProvider.js.map +1 -1
  4. package/dist/cjs/accountSummary.d.ts.map +1 -1
  5. package/dist/cjs/accountSummary.js +34 -7
  6. package/dist/cjs/accountSummary.js.map +1 -1
  7. package/dist/cjs/index.d.ts +2 -0
  8. package/dist/cjs/index.d.ts.map +1 -1
  9. package/dist/cjs/index.js +3 -1
  10. package/dist/cjs/index.js.map +1 -1
  11. package/dist/cjs/services/activeAssetData.d.ts +6 -0
  12. package/dist/cjs/services/activeAssetData.d.ts.map +1 -0
  13. package/dist/cjs/services/activeAssetData.js +7 -0
  14. package/dist/cjs/services/activeAssetData.js.map +1 -0
  15. package/dist/cjs/services/getAccount.d.ts.map +1 -1
  16. package/dist/cjs/services/getAccount.js +23 -35
  17. package/dist/cjs/services/getAccount.js.map +1 -1
  18. package/dist/cjs/services/getAvailableToTrade.d.ts +10 -0
  19. package/dist/cjs/services/getAvailableToTrade.d.ts.map +1 -0
  20. package/dist/cjs/services/getAvailableToTrade.js +32 -0
  21. package/dist/cjs/services/getAvailableToTrade.js.map +1 -0
  22. package/dist/cjs/services/getMarketSettings.d.ts +1 -1
  23. package/dist/cjs/services/getMarketSettings.d.ts.map +1 -1
  24. package/dist/cjs/services/getMarketSettings.js +3 -7
  25. package/dist/cjs/services/getMarketSettings.js.map +1 -1
  26. package/dist/cjs/services/getWithdrawableBalances.d.ts +5 -0
  27. package/dist/cjs/services/getWithdrawableBalances.d.ts.map +1 -0
  28. package/dist/cjs/services/getWithdrawableBalances.js +27 -0
  29. package/dist/cjs/services/getWithdrawableBalances.js.map +1 -0
  30. package/dist/cjs/types/account.d.ts +11 -8
  31. package/dist/cjs/types/account.d.ts.map +1 -1
  32. package/dist/cjs/types/account.js.map +1 -1
  33. package/dist/cjs/types/ws.d.ts +3 -4
  34. package/dist/cjs/types/ws.d.ts.map +1 -1
  35. package/dist/cjs/utils/abstractionMode.d.ts +1 -1
  36. package/dist/cjs/utils/abstractionMode.d.ts.map +1 -1
  37. package/dist/cjs/utils/abstractionMode.js.map +1 -1
  38. package/dist/cjs/utils/index.d.ts +2 -0
  39. package/dist/cjs/utils/index.d.ts.map +1 -1
  40. package/dist/cjs/utils/index.js +6 -1
  41. package/dist/cjs/utils/index.js.map +1 -1
  42. package/dist/cjs/utils/mapPosition.d.ts.map +1 -1
  43. package/dist/cjs/utils/mapPosition.js +1 -4
  44. package/dist/cjs/utils/mapPosition.js.map +1 -1
  45. package/dist/cjs/utils/spotCollateral.d.ts +1 -1
  46. package/dist/cjs/utils/spotCollateral.d.ts.map +1 -1
  47. package/dist/cjs/utils/spotCollateral.js +1 -12
  48. package/dist/cjs/utils/spotCollateral.js.map +1 -1
  49. package/dist/cjs/utils/venueTotals.d.ts +11 -0
  50. package/dist/cjs/utils/venueTotals.d.ts.map +1 -0
  51. package/dist/cjs/utils/venueTotals.js +24 -0
  52. package/dist/cjs/utils/venueTotals.js.map +1 -0
  53. package/dist/cjs/utils/withdrawableBalances.d.ts +4 -0
  54. package/dist/cjs/utils/withdrawableBalances.d.ts.map +1 -0
  55. package/dist/cjs/utils/withdrawableBalances.js +35 -0
  56. package/dist/cjs/utils/withdrawableBalances.js.map +1 -0
  57. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts +3 -1
  58. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  59. package/dist/cjs/websocket/HyperliquidWsProvider.js +102 -36
  60. package/dist/cjs/websocket/HyperliquidWsProvider.js.map +1 -1
  61. package/dist/esm/HyperliquidProvider.d.ts.map +1 -1
  62. package/dist/esm/HyperliquidProvider.js +4 -0
  63. package/dist/esm/HyperliquidProvider.js.map +1 -1
  64. package/dist/esm/accountSummary.d.ts +4 -3
  65. package/dist/esm/accountSummary.d.ts.map +1 -1
  66. package/dist/esm/accountSummary.js +36 -18
  67. package/dist/esm/accountSummary.js.map +1 -1
  68. package/dist/esm/index.d.ts +2 -0
  69. package/dist/esm/index.d.ts.map +1 -1
  70. package/dist/esm/index.js +1 -0
  71. package/dist/esm/index.js.map +1 -1
  72. package/dist/esm/services/activeAssetData.d.ts +13 -0
  73. package/dist/esm/services/activeAssetData.d.ts.map +1 -0
  74. package/dist/esm/services/activeAssetData.js +10 -0
  75. package/dist/esm/services/activeAssetData.js.map +1 -0
  76. package/dist/esm/services/getAccount.d.ts.map +1 -1
  77. package/dist/esm/services/getAccount.js +19 -29
  78. package/dist/esm/services/getAccount.js.map +1 -1
  79. package/dist/esm/services/getAvailableToTrade.d.ts +26 -0
  80. package/dist/esm/services/getAvailableToTrade.d.ts.map +1 -0
  81. package/dist/esm/services/getAvailableToTrade.js +42 -0
  82. package/dist/esm/services/getAvailableToTrade.js.map +1 -0
  83. package/dist/esm/services/getMarketSettings.d.ts +1 -1
  84. package/dist/esm/services/getMarketSettings.d.ts.map +1 -1
  85. package/dist/esm/services/getMarketSettings.js +3 -7
  86. package/dist/esm/services/getMarketSettings.js.map +1 -1
  87. package/dist/esm/services/getWithdrawableBalances.d.ts +22 -0
  88. package/dist/esm/services/getWithdrawableBalances.d.ts.map +1 -0
  89. package/dist/esm/services/getWithdrawableBalances.js +35 -0
  90. package/dist/esm/services/getWithdrawableBalances.js.map +1 -0
  91. package/dist/esm/types/account.d.ts +21 -8
  92. package/dist/esm/types/account.d.ts.map +1 -1
  93. package/dist/esm/types/account.js.map +1 -1
  94. package/dist/esm/types/ws.d.ts +10 -4
  95. package/dist/esm/types/ws.d.ts.map +1 -1
  96. package/dist/esm/utils/abstractionMode.d.ts +1 -1
  97. package/dist/esm/utils/abstractionMode.d.ts.map +1 -1
  98. package/dist/esm/utils/abstractionMode.js.map +1 -1
  99. package/dist/esm/utils/index.d.ts +2 -0
  100. package/dist/esm/utils/index.d.ts.map +1 -1
  101. package/dist/esm/utils/index.js +2 -0
  102. package/dist/esm/utils/index.js.map +1 -1
  103. package/dist/esm/utils/mapPosition.d.ts.map +1 -1
  104. package/dist/esm/utils/mapPosition.js +1 -4
  105. package/dist/esm/utils/mapPosition.js.map +1 -1
  106. package/dist/esm/utils/spotCollateral.d.ts +4 -4
  107. package/dist/esm/utils/spotCollateral.d.ts.map +1 -1
  108. package/dist/esm/utils/spotCollateral.js +4 -23
  109. package/dist/esm/utils/spotCollateral.js.map +1 -1
  110. package/dist/esm/utils/venueTotals.d.ts +26 -0
  111. package/dist/esm/utils/venueTotals.d.ts.map +1 -0
  112. package/dist/esm/utils/venueTotals.js +26 -0
  113. package/dist/esm/utils/venueTotals.js.map +1 -0
  114. package/dist/esm/utils/withdrawableBalances.d.ts +14 -0
  115. package/dist/esm/utils/withdrawableBalances.d.ts.map +1 -0
  116. package/dist/esm/utils/withdrawableBalances.js +41 -0
  117. package/dist/esm/utils/withdrawableBalances.js.map +1 -0
  118. package/dist/esm/websocket/HyperliquidWsProvider.d.ts +4 -1
  119. package/dist/esm/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  120. package/dist/esm/websocket/HyperliquidWsProvider.js +101 -35
  121. package/dist/esm/websocket/HyperliquidWsProvider.js.map +1 -1
  122. package/dist/types/HyperliquidProvider.d.ts.map +1 -1
  123. package/dist/types/accountSummary.d.ts +4 -3
  124. package/dist/types/accountSummary.d.ts.map +1 -1
  125. package/dist/types/index.d.ts +2 -0
  126. package/dist/types/index.d.ts.map +1 -1
  127. package/dist/types/services/activeAssetData.d.ts +13 -0
  128. package/dist/types/services/activeAssetData.d.ts.map +1 -0
  129. package/dist/types/services/getAccount.d.ts.map +1 -1
  130. package/dist/types/services/getAvailableToTrade.d.ts +26 -0
  131. package/dist/types/services/getAvailableToTrade.d.ts.map +1 -0
  132. package/dist/types/services/getMarketSettings.d.ts +1 -1
  133. package/dist/types/services/getMarketSettings.d.ts.map +1 -1
  134. package/dist/types/services/getWithdrawableBalances.d.ts +22 -0
  135. package/dist/types/services/getWithdrawableBalances.d.ts.map +1 -0
  136. package/dist/types/types/account.d.ts +21 -8
  137. package/dist/types/types/account.d.ts.map +1 -1
  138. package/dist/types/types/ws.d.ts +10 -4
  139. package/dist/types/types/ws.d.ts.map +1 -1
  140. package/dist/types/utils/abstractionMode.d.ts +1 -1
  141. package/dist/types/utils/abstractionMode.d.ts.map +1 -1
  142. package/dist/types/utils/index.d.ts +2 -0
  143. package/dist/types/utils/index.d.ts.map +1 -1
  144. package/dist/types/utils/mapPosition.d.ts.map +1 -1
  145. package/dist/types/utils/spotCollateral.d.ts +4 -4
  146. package/dist/types/utils/spotCollateral.d.ts.map +1 -1
  147. package/dist/types/utils/venueTotals.d.ts +26 -0
  148. package/dist/types/utils/venueTotals.d.ts.map +1 -0
  149. package/dist/types/utils/withdrawableBalances.d.ts +14 -0
  150. package/dist/types/utils/withdrawableBalances.d.ts.map +1 -0
  151. package/dist/types/websocket/HyperliquidWsProvider.d.ts +4 -1
  152. package/dist/types/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  153. package/package.json +3 -3
  154. package/src/HyperliquidProvider.ts +26 -0
  155. package/src/accountSummary.ts +64 -19
  156. package/src/index.ts +2 -0
  157. package/src/services/activeAssetData.ts +24 -0
  158. package/src/services/getAccount.ts +37 -52
  159. package/src/services/getAvailableToTrade.ts +72 -0
  160. package/src/services/getMarketSettings.ts +7 -11
  161. package/src/services/getWithdrawableBalances.ts +84 -0
  162. package/src/types/account.ts +22 -8
  163. package/src/types/ws.ts +18 -5
  164. package/src/utils/abstractionMode.ts +1 -1
  165. package/src/utils/index.ts +6 -0
  166. package/src/utils/mapPosition.ts +1 -5
  167. package/src/utils/spotCollateral.ts +4 -25
  168. package/src/utils/venueTotals.ts +48 -0
  169. package/src/utils/withdrawableBalances.ts +57 -0
  170. package/src/websocket/HyperliquidWsProvider.ts +149 -42
  171. package/dist/cjs/websocket/decodeChain.d.ts +0 -11
  172. package/dist/cjs/websocket/decodeChain.d.ts.map +0 -1
  173. package/dist/cjs/websocket/decodeChain.js +0 -37
  174. package/dist/cjs/websocket/decodeChain.js.map +0 -1
  175. package/dist/esm/websocket/decodeChain.d.ts +0 -30
  176. package/dist/esm/websocket/decodeChain.d.ts.map +0 -1
  177. package/dist/esm/websocket/decodeChain.js +0 -44
  178. package/dist/esm/websocket/decodeChain.js.map +0 -1
  179. package/dist/types/websocket/decodeChain.d.ts +0 -30
  180. package/dist/types/websocket/decodeChain.d.ts.map +0 -1
  181. package/src/websocket/decodeChain.ts +0 -54
@@ -0,0 +1,72 @@
1
+ import {
2
+ getMarketRegistry,
3
+ PerpsError,
4
+ type SDKRequestOptions,
5
+ toAssetDisplay,
6
+ } from '@lifi/perps-sdk'
7
+ import type { AvailableToTrade } from '@lifi/perps-types'
8
+ import { PerpsErrorCode } from '@lifi/perps-types'
9
+ import type { Address } from 'viem'
10
+ import { PROVIDER_KEY, SPOT_MARKET_ID } from '../constants.js'
11
+ import type { HyperliquidContext } from '../context.js'
12
+ import { fetchActiveAssetData } from './activeAssetData.js'
13
+
14
+ /**
15
+ * Parameters for {@link getAvailableToTrade}.
16
+ *
17
+ * @public
18
+ */
19
+ export interface GetAvailableToTradeParams {
20
+ address: Address
21
+ marketId: string
22
+ }
23
+
24
+ /**
25
+ * The amounts the account can still buy and sell on one perps market, read
26
+ * from `activeAssetData`. The amounts are denominated in the market's margin
27
+ * asset: USDC on the main dex, and the dex margin asset on a builder-deployed
28
+ * dex. Resolves `undefined` for a spot market, which carries no such state.
29
+ *
30
+ * @throws {PerpsError} On an unknown market, an `availableToTrade` payload
31
+ * that is not a buy/sell pair, Hyperliquid REST error, network, or parsing
32
+ * failures.
33
+ * @public
34
+ */
35
+ export const getAvailableToTrade = async (
36
+ context: HyperliquidContext,
37
+ params: GetAvailableToTradeParams,
38
+ options?: SDKRequestOptions
39
+ ): Promise<AvailableToTrade | undefined> => {
40
+ const registry = getMarketRegistry(context.client, PROVIDER_KEY)
41
+ await registry.sync()
42
+ const market = registry.require(params.marketId)
43
+ if (market.categoryId === SPOT_MARKET_ID) {
44
+ return undefined
45
+ }
46
+ const data = await fetchActiveAssetData(
47
+ context,
48
+ params.address,
49
+ params.marketId,
50
+ options
51
+ )
52
+ // The wire type states the pair; nothing verifies the response, so read it
53
+ // as a plain list to keep the length check in reach of the type checker.
54
+ const amounts: readonly string[] = data.availableToTrade
55
+ if (amounts.length !== 2) {
56
+ const err = new PerpsError(
57
+ PerpsErrorCode.ValidationError,
58
+ `Hyperliquid returned ${amounts.length} availableToTrade amounts for market '${params.marketId}'. Expected a buy amount and a sell amount.`
59
+ )
60
+ err.tool = PROVIDER_KEY
61
+ throw err
62
+ }
63
+ // Hyperliquid orders the pair buy-side first, sell-side second.
64
+ const [buy, sell] = amounts
65
+ return {
66
+ providerId: market.providerId,
67
+ marketId: market.id,
68
+ asset: toAssetDisplay(market.quoteAsset),
69
+ buy,
70
+ sell,
71
+ }
72
+ }
@@ -7,8 +7,7 @@ import {
7
7
  import type { Address } from 'viem'
8
8
  import { SPOT_MARKET_ID } from '../constants.js'
9
9
  import type { HyperliquidContext } from '../context.js'
10
- import type { HlActiveAssetData } from '../types/index.js'
11
- import { hlInfoOptions, infoRequest } from '../utils/infoClient.js'
10
+ import { fetchActiveAssetData } from './activeAssetData.js'
12
11
 
13
12
  /**
14
13
  * Parameters for {@link getMarketSettings}.
@@ -27,7 +26,7 @@ export interface GetMarketSettingsParams {
27
26
  * @public
28
27
  */
29
28
  export const getMarketSettings = async (
30
- { client, apiUrl }: HyperliquidContext,
29
+ context: HyperliquidContext,
31
30
  params: GetMarketSettingsParams,
32
31
  options?: SDKRequestOptions
33
32
  ): Promise<MarketSettings | undefined> => {
@@ -35,14 +34,11 @@ export const getMarketSettings = async (
35
34
  if (params.market.categoryId === SPOT_MARKET_ID) {
36
35
  return undefined
37
36
  }
38
- const data = await infoRequest<HlActiveAssetData>(
39
- apiUrl,
40
- {
41
- type: 'activeAssetData',
42
- user: params.address,
43
- coin: params.market.marketId,
44
- },
45
- hlInfoOptions(client, options)
37
+ const data = await fetchActiveAssetData(
38
+ context,
39
+ params.address,
40
+ params.market.marketId,
41
+ options
46
42
  )
47
43
  const leverage = data.leverage
48
44
  if (!leverage) {
@@ -0,0 +1,84 @@
1
+ import {
2
+ getMarketRegistry,
3
+ PerpsError,
4
+ type ProviderGetWithdrawableBalancesParams,
5
+ type ProviderWithdrawableBalance,
6
+ type SDKRequestOptions,
7
+ } from '@lifi/perps-sdk'
8
+ import { PerpsErrorCode } from '@lifi/perps-types'
9
+ import { MAIN_MARKET_ID, PROVIDER_KEY } from '../constants.js'
10
+ import type { HyperliquidContext } from '../context.js'
11
+ import type {
12
+ HlAbstractionMode,
13
+ HlClearinghouseState,
14
+ HlSpotClearinghouseState,
15
+ } from '../types/index.js'
16
+ import { hlInfoOptions, infoRequest } from '../utils/infoClient.js'
17
+ import { hyperliquidWithdrawableBalances } from '../utils/withdrawableBalances.js'
18
+
19
+ /**
20
+ * Parameters for {@link getWithdrawableBalances}.
21
+ *
22
+ * @public
23
+ */
24
+ export type GetWithdrawableBalancesParams =
25
+ ProviderGetWithdrawableBalancesParams
26
+
27
+ /**
28
+ * The `(asset, route)` pairs `params.address` can withdraw from Hyperliquid,
29
+ * read from the venue's own `withdrawable` and spot `total`/`hold` figures.
30
+ *
31
+ * Only the main perps dex is read: a withdrawal to L1 draws on that account,
32
+ * and a sub-dex balance reaches it through a transfer first.
33
+ *
34
+ * @throws {PerpsError} On Hyperliquid REST error, network, or parsing
35
+ * failures, and `SDKError` when the market registry carries no main perps
36
+ * market to name the quote asset.
37
+ * @public
38
+ */
39
+ export const getWithdrawableBalances = async (
40
+ { client, apiUrl }: HyperliquidContext,
41
+ params: GetWithdrawableBalancesParams,
42
+ options?: SDKRequestOptions
43
+ ): Promise<ProviderWithdrawableBalance[]> => {
44
+ const registry = getMarketRegistry(client, PROVIDER_KEY)
45
+ const infoOpts = hlInfoOptions(client, options)
46
+
47
+ const [markets, abstraction, state, spotState] = await Promise.all([
48
+ registry.sync(),
49
+ infoRequest<HlAbstractionMode | null>(
50
+ apiUrl,
51
+ { type: 'userAbstraction', user: params.address },
52
+ infoOpts
53
+ ),
54
+ infoRequest<HlClearinghouseState>(
55
+ apiUrl,
56
+ { type: 'clearinghouseState', user: params.address },
57
+ infoOpts
58
+ ),
59
+ infoRequest<HlSpotClearinghouseState>(
60
+ apiUrl,
61
+ { type: 'spotClearinghouseState', user: params.address },
62
+ infoOpts
63
+ ),
64
+ ])
65
+
66
+ const quoteAsset = markets.find(
67
+ (market) => market.categoryId === MAIN_MARKET_ID
68
+ )?.quoteAsset
69
+ if (quoteAsset === undefined) {
70
+ const error = new PerpsError(
71
+ PerpsErrorCode.SDKError,
72
+ `Hyperliquid market registry carries no '${MAIN_MARKET_ID}' market to name the withdrawal quote asset`
73
+ )
74
+ error.tool = PROVIDER_KEY
75
+ throw error
76
+ }
77
+
78
+ return hyperliquidWithdrawableBalances(
79
+ abstraction,
80
+ state,
81
+ spotState,
82
+ quoteAsset.id
83
+ )
84
+ }
@@ -51,6 +51,19 @@ export type HlAssetPosition = {
51
51
  position: HlPosition
52
52
  }
53
53
 
54
+ /**
55
+ * Margin totals block of a Hyperliquid clearinghouse state. Monetary fields
56
+ * are decimal strings in the account's quote asset.
57
+ * @public
58
+ */
59
+ export type HlMarginSummary = {
60
+ /** Total equity: locked margin and unrealized PnL included. */
61
+ accountValue: string
62
+ totalNtlPos: string
63
+ totalRawUsd: string
64
+ totalMarginUsed: string
65
+ }
66
+
54
67
  /**
55
68
  * Perpetual clearinghouse account state returned by Hyperliquid `/info`.
56
69
  * `assetPositions` includes zero-size rows that callers may discard. Monetary
@@ -60,14 +73,10 @@ export type HlAssetPosition = {
60
73
  */
61
74
  export type HlClearinghouseState = {
62
75
  assetPositions: HlAssetPosition[]
63
- marginSummary: {
64
- accountValue: string
65
- totalMarginUsed: string
66
- }
67
- crossMarginSummary: {
68
- accountValue: string
69
- totalMarginUsed: string
70
- }
76
+ marginSummary: HlMarginSummary
77
+ crossMarginSummary: HlMarginSummary
78
+ crossMaintenanceMarginUsed: string
79
+ withdrawable: string
71
80
  }
72
81
 
73
82
  /**
@@ -90,6 +99,11 @@ export type HlSpotBalance = {
90
99
  */
91
100
  export type HlSpotClearinghouseState = {
92
101
  balances: HlSpotBalance[]
102
+ /**
103
+ * Venue buying power per spot token, as `[token index, amount]` pairs.
104
+ * The venue reports the amount after maintenance margin.
105
+ */
106
+ tokenToAvailableAfterMaintenance: [number, string][]
93
107
  }
94
108
 
95
109
  /**
package/src/types/ws.ts CHANGED
@@ -184,6 +184,18 @@ export type HlWsUserFillsData = {
184
184
  fills: HlUserFill[]
185
185
  }
186
186
 
187
+ /**
188
+ * One sub-dex slice of an `allDexsClearinghouseState` frame. `marginSummary`
189
+ * is absent on a frame that reports positions only, so a reader must treat it
190
+ * as optional.
191
+ * @public
192
+ */
193
+ export type HlWsDexClearinghouseState = Pick<
194
+ HlClearinghouseState,
195
+ 'assetPositions'
196
+ > &
197
+ Partial<Pick<HlClearinghouseState, 'marginSummary'>>
198
+
187
199
  /**
188
200
  * Per-dex clearinghouse event for one user. Each tuple contains a wire DEX name
189
201
  * and the perp positions/equity fields supplied by that DEX.
@@ -191,14 +203,15 @@ export type HlWsUserFillsData = {
191
203
  */
192
204
  export type HlWsAllDexsClearinghouseStateData = {
193
205
  user: string
194
- clearinghouseStates: [
195
- string,
196
- Pick<HlClearinghouseState, 'assetPositions' | 'marginSummary'>,
197
- ][]
206
+ clearinghouseStates: [string, HlWsDexClearinghouseState][]
198
207
  }
199
208
 
200
209
  /** User spot clearinghouse event containing the current spot balances. @public */
201
210
  export type HlWsSpotStateData = {
202
211
  user: string
203
- spotState: { balances: HlSpotBalance[] }
212
+ spotState: {
213
+ balances: HlSpotBalance[]
214
+ /** Absent on some frames; the REST `spotClearinghouseState` read covers it. */
215
+ tokenToAvailableAfterMaintenance?: [number, string][]
216
+ }
204
217
  }
@@ -6,7 +6,7 @@ import { HlAbstractionMode } from '../types/account.js'
6
6
  * per-dex, apart from the spot balances.
7
7
  */
8
8
  export const isUnifiedAbstraction = (
9
- abstraction: HlAbstractionMode | null
9
+ abstraction: HlAbstractionMode | string | null
10
10
  ): boolean =>
11
11
  abstraction === HlAbstractionMode.UNIFIED_ACCOUNT ||
12
12
  abstraction === HlAbstractionMode.PORTFOLIO_MARGIN
@@ -57,3 +57,9 @@ export {
57
57
  } from './spotBalance.js'
58
58
  export { partitionSpotBalances, type SpotPartition } from './spotCollateral.js'
59
59
  export { positionMarginConstraints } from './transferMargin.js'
60
+ export {
61
+ type DexMarginSummary,
62
+ perpsTotals,
63
+ sumUnrealizedPnl,
64
+ } from './venueTotals.js'
65
+ export { hyperliquidWithdrawableBalances } from './withdrawableBalances.js'
@@ -30,10 +30,6 @@ export const mapPosition = (
30
30
  const leverage = new Big(pos.leverage.value)
31
31
  const marginMode =
32
32
  pos.leverage.type === 'cross' ? MarginMode.CROSS : MarginMode.ISOLATED
33
- const marginUsed =
34
- marginMode === MarginMode.ISOLATED
35
- ? new Big(pos.marginUsed).minus(pos.unrealizedPnl).toFixed()
36
- : pos.marginUsed
37
33
 
38
34
  return {
39
35
  market,
@@ -45,7 +41,7 @@ export const mapPosition = (
45
41
  unrealizedPnl: pos.unrealizedPnl,
46
42
  accruedFunding: new Big(pos.cumFunding.sinceOpen).neg().toFixed(),
47
43
  leverage: ap.position.leverage.value,
48
- marginUsed,
44
+ marginUsed: pos.marginUsed,
49
45
  initialMarginRequirement: positionValue.div(leverage).toFixed(),
50
46
  marginMode,
51
47
  }
@@ -1,19 +1,6 @@
1
1
  import type { Balance } from '@lifi/perps-types'
2
2
  import { toWireBig } from './decimal.js'
3
3
 
4
- /**
5
- * Portfolio-margin collateral beyond the category quote assets, keyed by spot
6
- * display symbol with its loan-to-value weight. Hyperliquid credits these
7
- * toward buying power at the given fraction (0.5 for both today). Quote assets
8
- * (USDC/USDT) are already full-value collateral via the category model, so
9
- * they are not listed here.
10
- * https://hyperliquid.gitbook.io/hyperliquid-docs/trading/portfolio-margin
11
- */
12
- const PORTFOLIO_MARGIN_LTV: Readonly<Record<string, number>> = {
13
- HYPE: 0.5,
14
- UBTC: 0.5,
15
- }
16
-
17
4
  /**
18
5
  * Result of {@link partitionSpotBalances}: balances that count toward
19
6
  * collateral and balances treated as ordinary holdings.
@@ -25,15 +12,14 @@ export interface SpotPartition {
25
12
  }
26
13
 
27
14
  /**
28
- * Partition spot balances into collateral and ordinary holdings. Quote assets
29
- * are full-value collateral; portfolio-margin-supported assets retain their
30
- * LTV as `collateralWeight`.
15
+ * Partition spot balances into collateral and ordinary holdings. Category
16
+ * quote assets are collateral; every other token is an ordinary holding.
17
+ * Zero-unit rows are dropped from both lists.
31
18
  * @public
32
19
  */
33
20
  export const partitionSpotBalances = (
34
21
  spotBalances: readonly Balance[],
35
- quoteAssetIds: ReadonlySet<string>,
36
- portfolioMargin: boolean
22
+ quoteAssetIds: ReadonlySet<string>
37
23
  ): SpotPartition => {
38
24
  const collateralBalances: Balance[] = []
39
25
  const balances: Balance[] = []
@@ -43,13 +29,6 @@ export const partitionSpotBalances = (
43
29
  }
44
30
  if (quoteAssetIds.has(balance.asset.id)) {
45
31
  collateralBalances.push(balance)
46
- continue
47
- }
48
- const ltv = portfolioMargin
49
- ? PORTFOLIO_MARGIN_LTV[balance.asset.displaySymbol]
50
- : undefined
51
- if (ltv !== undefined) {
52
- collateralBalances.push({ ...balance, collateralWeight: ltv })
53
32
  } else {
54
33
  balances.push(balance)
55
34
  }
@@ -0,0 +1,48 @@
1
+ import type { HyperliquidMarginSummary, Position } from '@lifi/perps-types'
2
+ import Big from 'big.js'
3
+ import { toWireBig } from './decimal.js'
4
+
5
+ /**
6
+ * One perps sub-dex entry {@link perpsTotals} reads. The REST response always
7
+ * carries `marginSummary`; a WebSocket frame may omit it.
8
+ * @public
9
+ */
10
+ export interface DexMarginSummary {
11
+ marginSummary?: HyperliquidMarginSummary
12
+ }
13
+
14
+ /**
15
+ * Sum the venue `marginSummary` totals over every perps sub-dex of an
16
+ * account. `accountValue` is total equity, so it already carries locked
17
+ * margin and unrealized PnL. An entry without a `marginSummary` adds nothing.
18
+ * @public
19
+ */
20
+ export const perpsTotals = (
21
+ dexStates: readonly DexMarginSummary[]
22
+ ): { accountValue: Big; marginUsed: Big } => {
23
+ let accountValue = new Big(0)
24
+ let marginUsed = new Big(0)
25
+ for (const { marginSummary } of dexStates) {
26
+ if (marginSummary === undefined) {
27
+ continue
28
+ }
29
+ accountValue = accountValue.plus(
30
+ toWireBig(marginSummary.accountValue, 'marginSummary.accountValue')
31
+ )
32
+ marginUsed = marginUsed.plus(
33
+ toWireBig(marginSummary.totalMarginUsed, 'marginSummary.totalMarginUsed')
34
+ )
35
+ }
36
+ return { accountValue, marginUsed }
37
+ }
38
+
39
+ /**
40
+ * Sum the unrealized PnL of every position, in quote-asset units.
41
+ * @public
42
+ */
43
+ export const sumUnrealizedPnl = (positions: readonly Position[]): Big =>
44
+ positions.reduce(
45
+ (sum, position) =>
46
+ sum.plus(toWireBig(position.unrealizedPnl, 'position.unrealizedPnl')),
47
+ new Big(0)
48
+ )
@@ -0,0 +1,57 @@
1
+ import type { ProviderWithdrawableBalance } from '@lifi/perps-sdk'
2
+ import type {
3
+ HlAbstractionMode,
4
+ HlClearinghouseState,
5
+ HlSpotClearinghouseState,
6
+ } from '../types/index.js'
7
+ import { isUnifiedAbstraction } from './abstractionMode.js'
8
+ import { assetIsOutcome } from './assetId.js'
9
+ import { toWireBig } from './decimal.js'
10
+
11
+ /**
12
+ * Split a Hyperliquid account's venue figures into the routes a withdrawal
13
+ * draws on. A unified or portfolio-margin account holds its collateral in spot,
14
+ * so each spot token adds the part of `total` that no order or margin holds. A
15
+ * route with nothing left to draw carries no row, and the caller applies the
16
+ * per-asset venue minimum.
17
+ *
18
+ * @param quoteAssetId - `Asset.id` the perps-route row is keyed by.
19
+ * @public
20
+ */
21
+ export const hyperliquidWithdrawableBalances = (
22
+ abstraction: HlAbstractionMode | null,
23
+ state: HlClearinghouseState,
24
+ spotState: HlSpotClearinghouseState,
25
+ quoteAssetId: string
26
+ ): ProviderWithdrawableBalance[] => {
27
+ const rows: ProviderWithdrawableBalance[] = []
28
+
29
+ if (isUnifiedAbstraction(abstraction)) {
30
+ for (const balance of spotState.balances) {
31
+ if (assetIsOutcome(balance.coin)) {
32
+ continue
33
+ }
34
+ const spot = toWireBig(balance.total, 'spotBalance.total').minus(
35
+ toWireBig(balance.hold, 'spotBalance.hold')
36
+ )
37
+ if (spot.gt(0)) {
38
+ rows.push({
39
+ assetId: String(balance.token),
40
+ route: 'spot',
41
+ available: spot.toFixed(),
42
+ })
43
+ }
44
+ }
45
+ }
46
+
47
+ const perps = toWireBig(state.withdrawable, 'clearinghouseState.withdrawable')
48
+ if (perps.gt(0)) {
49
+ rows.push({
50
+ assetId: quoteAssetId,
51
+ route: 'perps',
52
+ available: perps.toFixed(),
53
+ })
54
+ }
55
+
56
+ return rows
57
+ }