@lifi/perps-sdk-provider-hyperliquid 7.0.1 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/cjs/services/getActivity.d.ts.map +1 -1
  2. package/dist/cjs/services/getActivity.js +8 -1
  3. package/dist/cjs/services/getActivity.js.map +1 -1
  4. package/dist/cjs/types/account.d.ts +5 -0
  5. package/dist/cjs/types/account.d.ts.map +1 -1
  6. package/dist/cjs/types/account.js.map +1 -1
  7. package/dist/cjs/types/fill.d.ts +1 -0
  8. package/dist/cjs/types/fill.d.ts.map +1 -1
  9. package/dist/cjs/types/ledger.d.ts +24 -1
  10. package/dist/cjs/types/ledger.d.ts.map +1 -1
  11. package/dist/cjs/types/ledger.js +8 -1
  12. package/dist/cjs/types/ledger.js.map +1 -1
  13. package/dist/cjs/utils/index.d.ts +1 -1
  14. package/dist/cjs/utils/index.d.ts.map +1 -1
  15. package/dist/cjs/utils/index.js.map +1 -1
  16. package/dist/cjs/utils/mapActivity.d.ts.map +1 -1
  17. package/dist/cjs/utils/mapActivity.js +70 -49
  18. package/dist/cjs/utils/mapActivity.js.map +1 -1
  19. package/dist/cjs/utils/mapFill.d.ts.map +1 -1
  20. package/dist/cjs/utils/mapFill.js +4 -1
  21. package/dist/cjs/utils/mapFill.js.map +1 -1
  22. package/dist/cjs/utils/mapOrder.d.ts +4 -3
  23. package/dist/cjs/utils/mapOrder.d.ts.map +1 -1
  24. package/dist/cjs/utils/mapOrder.js +4 -3
  25. package/dist/cjs/utils/mapOrder.js.map +1 -1
  26. package/dist/cjs/utils/mapPosition.d.ts.map +1 -1
  27. package/dist/cjs/utils/mapPosition.js +1 -0
  28. package/dist/cjs/utils/mapPosition.js.map +1 -1
  29. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts +1 -1
  30. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  31. package/dist/cjs/websocket/HyperliquidWsProvider.js +2 -26
  32. package/dist/cjs/websocket/HyperliquidWsProvider.js.map +1 -1
  33. package/dist/esm/services/getActivity.d.ts.map +1 -1
  34. package/dist/esm/services/getActivity.js +10 -1
  35. package/dist/esm/services/getActivity.js.map +1 -1
  36. package/dist/esm/types/account.d.ts +11 -0
  37. package/dist/esm/types/account.d.ts.map +1 -1
  38. package/dist/esm/types/account.js.map +1 -1
  39. package/dist/esm/types/fill.d.ts +5 -0
  40. package/dist/esm/types/fill.d.ts.map +1 -1
  41. package/dist/esm/types/ledger.d.ts +65 -1
  42. package/dist/esm/types/ledger.d.ts.map +1 -1
  43. package/dist/esm/types/ledger.js +22 -0
  44. package/dist/esm/types/ledger.js.map +1 -1
  45. package/dist/esm/utils/index.d.ts +1 -1
  46. package/dist/esm/utils/index.d.ts.map +1 -1
  47. package/dist/esm/utils/index.js.map +1 -1
  48. package/dist/esm/utils/mapActivity.d.ts +2 -2
  49. package/dist/esm/utils/mapActivity.d.ts.map +1 -1
  50. package/dist/esm/utils/mapActivity.js +92 -55
  51. package/dist/esm/utils/mapActivity.js.map +1 -1
  52. package/dist/esm/utils/mapFill.d.ts.map +1 -1
  53. package/dist/esm/utils/mapFill.js +4 -1
  54. package/dist/esm/utils/mapFill.js.map +1 -1
  55. package/dist/esm/utils/mapOrder.d.ts +10 -3
  56. package/dist/esm/utils/mapOrder.d.ts.map +1 -1
  57. package/dist/esm/utils/mapOrder.js +4 -3
  58. package/dist/esm/utils/mapOrder.js.map +1 -1
  59. package/dist/esm/utils/mapPosition.d.ts +3 -1
  60. package/dist/esm/utils/mapPosition.d.ts.map +1 -1
  61. package/dist/esm/utils/mapPosition.js +4 -1
  62. package/dist/esm/utils/mapPosition.js.map +1 -1
  63. package/dist/esm/websocket/HyperliquidWsProvider.d.ts +1 -1
  64. package/dist/esm/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  65. package/dist/esm/websocket/HyperliquidWsProvider.js +3 -27
  66. package/dist/esm/websocket/HyperliquidWsProvider.js.map +1 -1
  67. package/dist/types/services/getActivity.d.ts.map +1 -1
  68. package/dist/types/types/account.d.ts +11 -0
  69. package/dist/types/types/account.d.ts.map +1 -1
  70. package/dist/types/types/fill.d.ts +5 -0
  71. package/dist/types/types/fill.d.ts.map +1 -1
  72. package/dist/types/types/ledger.d.ts +65 -1
  73. package/dist/types/types/ledger.d.ts.map +1 -1
  74. package/dist/types/utils/index.d.ts +1 -1
  75. package/dist/types/utils/index.d.ts.map +1 -1
  76. package/dist/types/utils/mapActivity.d.ts +2 -2
  77. package/dist/types/utils/mapActivity.d.ts.map +1 -1
  78. package/dist/types/utils/mapFill.d.ts.map +1 -1
  79. package/dist/types/utils/mapOrder.d.ts +10 -3
  80. package/dist/types/utils/mapOrder.d.ts.map +1 -1
  81. package/dist/types/utils/mapPosition.d.ts +3 -1
  82. package/dist/types/utils/mapPosition.d.ts.map +1 -1
  83. package/dist/types/websocket/HyperliquidWsProvider.d.ts +1 -1
  84. package/dist/types/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  85. package/package.json +3 -3
  86. package/src/services/getActivity.ts +13 -1
  87. package/src/types/account.ts +11 -0
  88. package/src/types/fill.ts +5 -0
  89. package/src/types/ledger.ts +84 -0
  90. package/src/utils/index.ts +1 -0
  91. package/src/utils/mapActivity.ts +105 -62
  92. package/src/utils/mapFill.ts +4 -1
  93. package/src/utils/mapOrder.ts +15 -8
  94. package/src/utils/mapPosition.ts +4 -1
  95. package/src/websocket/HyperliquidWsProvider.ts +14 -39
@@ -50,6 +50,51 @@ export type HlSendAssetDelta = {
50
50
  feeToken: string
51
51
  }
52
52
 
53
+ /**
54
+ * Hyperliquid `deposit` ledger delta. Perp-collateral deposits are always
55
+ * USDC-denominated, which is why the amount arrives in a field named `usdc`.
56
+ * @public
57
+ */
58
+ export type HlDepositDelta = {
59
+ type: 'deposit'
60
+ usdc: string
61
+ }
62
+
63
+ /**
64
+ * Hyperliquid `withdraw` ledger delta. `fee` is denominated in the withdrawn
65
+ * asset (USDC) and is absent when the venue charges none.
66
+ * @public
67
+ */
68
+ export type HlWithdrawDelta = {
69
+ type: 'withdraw'
70
+ usdc: string
71
+ fee?: string
72
+ }
73
+
74
+ /**
75
+ * One position closed by a Hyperliquid liquidation. `coin` is the venue market
76
+ * key and `szi` the signed position size.
77
+ * @public
78
+ */
79
+ export type HlLiquidatedPosition = {
80
+ coin: string
81
+ szi: string
82
+ }
83
+
84
+ /**
85
+ * Hyperliquid `liquidation` ledger delta. A cross-margin cascade reports every
86
+ * closed position in `liquidatedPositions`, so the venue itself carries the
87
+ * grouping and no timestamp heuristic is needed.
88
+ * @public
89
+ */
90
+ export type HlLiquidationDelta = {
91
+ type: 'liquidation'
92
+ liquidatedNtlPos?: string
93
+ accountValue?: string
94
+ leverageType: string
95
+ liquidatedPositions?: HlLiquidatedPosition[]
96
+ }
97
+
53
98
  /**
54
99
  * Union of known Hyperliquid non-funding ledger deltas plus an open fallback
55
100
  * for endpoint variants the provider does not map.
@@ -58,6 +103,9 @@ export type HlSendAssetDelta = {
58
103
  export type HlLedgerDelta =
59
104
  | HlSpotTransferDelta
60
105
  | HlSendAssetDelta
106
+ | HlDepositDelta
107
+ | HlWithdrawDelta
108
+ | HlLiquidationDelta
61
109
  | {
62
110
  type: string
63
111
  usdc?: string
@@ -85,6 +133,38 @@ export const isSendAssetDelta = (
85
133
  delta: HlLedgerDelta
86
134
  ): delta is HlSendAssetDelta => delta.type === 'send'
87
135
 
136
+ // `usdc` is declared only on the union's catch-all arm, so reading it off the
137
+ // union needs a widening read before the discriminant has narrowed the type.
138
+ const hasUsdcAmount = (delta: HlLedgerDelta): boolean =>
139
+ typeof (delta as { usdc?: unknown }).usdc === 'string'
140
+
141
+ /**
142
+ * Type guard for `HlDepositDelta`. The amount is part of the guard: a deposit
143
+ * row without one identifies no movement, and reporting it as a zero amount
144
+ * would read as a real zero-value deposit.
145
+ * @public
146
+ */
147
+ export const isDepositDelta = (delta: HlLedgerDelta): delta is HlDepositDelta =>
148
+ delta.type === 'deposit' && hasUsdcAmount(delta)
149
+
150
+ /**
151
+ * Type guard for `HlWithdrawDelta`. Same amount-bearing requirement as
152
+ * `isDepositDelta`.
153
+ * @public
154
+ */
155
+ export const isWithdrawDelta = (
156
+ delta: HlLedgerDelta
157
+ ): delta is HlWithdrawDelta => delta.type === 'withdraw' && hasUsdcAmount(delta)
158
+
159
+ /**
160
+ * Type guard for `HlLiquidationDelta`. Same catch-all-arm caveat as
161
+ * `isSpotTransferDelta`.
162
+ * @public
163
+ */
164
+ export const isLiquidationDelta = (
165
+ delta: HlLedgerDelta
166
+ ): delta is HlLiquidationDelta => delta.type === 'liquidation'
167
+
88
168
  /**
89
169
  * One timestamped non-funding ledger update. `time` is milliseconds since
90
170
  * epoch and `hash` is the upstream transaction identifier.
@@ -108,6 +188,10 @@ export type HlUserNonFundingLedgerUpdates = HlLedgerUpdate[]
108
188
  export type HlFundingDelta = {
109
189
  type: 'funding'
110
190
  coin: string
191
+ /**
192
+ * Signed, in USDC. Positive means the account received funding; negative
193
+ * means the account paid it. Mapped straight onto `FundingActivity.amount`.
194
+ */
111
195
  usdc: string
112
196
  szi: string
113
197
  fundingRate: string
@@ -23,6 +23,7 @@ export { classifyFillFromPosition, mapFill } from './mapFill.js'
23
23
  export { mapMarket } from './mapMarket.js'
24
24
  export { mapMarketContext } from './mapMarketContext.js'
25
25
  export {
26
+ type HlOrderLike,
26
27
  isTriggerOrder,
27
28
  isTriggerType,
28
29
  mapOpenOrder,
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  ActivityItem,
3
3
  DepositActivity,
4
+ Fee,
4
5
  FundingActivity,
5
6
  LiquidationActivity,
6
7
  MarketDisplay,
@@ -9,15 +10,44 @@ import type {
9
10
  } from '@lifi/perps-types'
10
11
  import { ActivityType } from '@lifi/perps-types'
11
12
  import type { HlFundingUpdate, HlLedgerUpdate } from '../types/index.js'
12
- import { isSendAssetDelta, isSpotTransferDelta } from '../types/index.js'
13
+ import {
14
+ isDepositDelta,
15
+ isLiquidationDelta,
16
+ isSendAssetDelta,
17
+ isSpotTransferDelta,
18
+ isWithdrawDelta,
19
+ } from '../types/index.js'
20
+
21
+ /**
22
+ * Hyperliquid settles every perp deposit and withdrawal in USDC, and charges
23
+ * the `spotTransfer` fee in USDC too — that delta carries no `feeToken`.
24
+ */
25
+ const HL_COLLATERAL_SYMBOL = 'USDC'
26
+
27
+ /**
28
+ * Hyperliquid's native token. A `nativeTokenFee` names no token on the wire,
29
+ * so the symbol comes from the venue rather than from the delta.
30
+ */
31
+ const HL_NATIVE_TOKEN_SYMBOL = 'HYPE'
32
+
33
+ /**
34
+ * Display symbol for a ledger delta's wire token (`"USDC"`, `"PURR:0x..."`).
35
+ * The colon separates the name from the token id, so the symbol is the part
36
+ * BEFORE it — the opposite half to a market coin's `dex:COIN`, which is why
37
+ * `coinAsset` cannot be reused here.
38
+ */
39
+ const ledgerTokenSymbol = (token: string): string => {
40
+ const separator = token.indexOf(':')
41
+ return separator === -1 ? token : token.slice(0, separator)
42
+ }
13
43
 
14
44
  /**
15
45
  * Map a Hyperliquid non-funding ledger entry to an ActivityItem.
16
46
  *
17
47
  * Direction for `spotTransfer` and `sendAsset` is derived from
18
48
  * `queriedAddress` matching the delta's `user` (OUT) or `destination` (IN).
19
- * Returns null for unsupported delta types and for same-user `sendAsset`
20
- * dex moves (where `user === destination === queriedAddress`).
49
+ * Returns null for unsupported delta types and for same-account moves, where
50
+ * `user === destination === queriedAddress`.
21
51
  * @public
22
52
  */
23
53
  export const mapLedgerEntry = (
@@ -40,6 +70,13 @@ export const mapLedgerEntry = (
40
70
  const queried = queriedAddress.toLowerCase()
41
71
  const sender = delta.user.toLowerCase()
42
72
  const recipient = delta.destination.toLowerCase()
73
+
74
+ // A transfer reports a movement between two accounts. A row whose sender
75
+ // and recipient are the queried account moves nothing between accounts.
76
+ if (sender === recipient && sender === queried) {
77
+ return null
78
+ }
79
+
43
80
  const direction: 'IN' | 'OUT' = queried === sender ? 'OUT' : 'IN'
44
81
  const counterpartyAddress = direction === 'OUT' ? recipient : sender
45
82
  const meta: Record<string, unknown> = {
@@ -48,22 +85,27 @@ export const mapLedgerEntry = (
48
85
  if (delta.usdcValue !== undefined) {
49
86
  meta.usdcValue = delta.usdcValue
50
87
  }
88
+ if (delta.nonce !== undefined) {
89
+ meta.nonce = delta.nonce
90
+ }
91
+ const fees: Fee[] = []
51
92
  if (delta.fee !== undefined) {
52
- meta.fee = delta.fee
93
+ fees.push({ amount: delta.fee, asset: HL_COLLATERAL_SYMBOL })
53
94
  }
54
95
  if (delta.nativeTokenFee !== undefined) {
55
- meta.nativeTokenFee = delta.nativeTokenFee
56
- }
57
- if (delta.nonce !== undefined) {
58
- meta.nonce = delta.nonce
96
+ fees.push({
97
+ amount: delta.nativeTokenFee,
98
+ asset: HL_NATIVE_TOKEN_SYMBOL,
99
+ })
59
100
  }
60
101
  return {
61
102
  ...base,
62
103
  type: ActivityType.TRANSFER,
63
104
  direction,
64
105
  counterpartyAddress,
65
- asset: delta.token,
106
+ asset: ledgerTokenSymbol(delta.token),
66
107
  amount: delta.amount,
108
+ ...(fees.length === 0 ? {} : { fees }),
67
109
  meta,
68
110
  explorerLink: entry.hash
69
111
  ? `https://app.hyperliquid.xyz/explorer/tx/${entry.hash}`
@@ -91,9 +133,6 @@ export const mapLedgerEntry = (
91
133
  sourceDex: delta.sourceDex,
92
134
  destinationDex: delta.destinationDex,
93
135
  usdcValue: delta.usdcValue,
94
- fee: delta.fee,
95
- nativeTokenFee: delta.nativeTokenFee,
96
- feeToken: delta.feeToken,
97
136
  nonce: delta.nonce,
98
137
  }
99
138
  return {
@@ -101,8 +140,12 @@ export const mapLedgerEntry = (
101
140
  type: ActivityType.TRANSFER,
102
141
  direction,
103
142
  counterpartyAddress,
104
- asset: delta.token,
143
+ asset: ledgerTokenSymbol(delta.token),
105
144
  amount: delta.amount,
145
+ fees: [
146
+ { amount: delta.fee, asset: ledgerTokenSymbol(delta.feeToken) },
147
+ { amount: delta.nativeTokenFee, asset: HL_NATIVE_TOKEN_SYMBOL },
148
+ ],
106
149
  meta,
107
150
  explorerLink: entry.hash
108
151
  ? `https://app.hyperliquid.xyz/explorer/tx/${entry.hash}`
@@ -110,59 +153,59 @@ export const mapLedgerEntry = (
110
153
  } satisfies TransferActivity
111
154
  }
112
155
 
113
- switch (delta.type) {
114
- case 'deposit':
115
- return {
116
- ...base,
117
- type: ActivityType.DEPOSIT,
118
- amount: delta.usdc ?? '0',
119
- explorerLink: entry.hash
120
- ? `https://scan.li.fi/tx/${entry.hash}`
121
- : undefined,
122
- } satisfies DepositActivity
123
-
124
- case 'withdraw':
125
- return {
126
- ...base,
127
- type: ActivityType.WITHDRAWAL,
128
- amount: delta.usdc ?? '0',
129
- fee: (delta as { fee?: string }).fee ?? '0',
130
- explorerLink: entry.hash
131
- ? `https://scan.li.fi/tx/${entry.hash}`
132
- : undefined,
133
- } satisfies WithdrawalActivity
156
+ if (isDepositDelta(delta)) {
157
+ return {
158
+ ...base,
159
+ type: ActivityType.DEPOSIT,
160
+ asset: HL_COLLATERAL_SYMBOL,
161
+ amount: delta.usdc,
162
+ explorerLink: entry.hash
163
+ ? `https://scan.li.fi/tx/${entry.hash}`
164
+ : undefined,
165
+ } satisfies DepositActivity
166
+ }
134
167
 
135
- case 'liquidation': {
136
- const d = delta as unknown as {
137
- type: string
138
- liquidatedNtlPos: string
139
- accountValue: string
140
- leverageType: string
141
- liquidatedPositions?: { coin: string; szi: string }[]
142
- }
143
- const liquidatedPositions = (d.liquidatedPositions ?? []).map((p) => ({
144
- market: resolveMarket(p.coin),
145
- size: p.szi,
146
- }))
147
- // Liquidation rows must point to at least one market. Drop entries with
148
- // missing/empty positions so downstream consumers can rely on that
149
- // invariant and avoid rendering a market-less liquidation card.
150
- if (liquidatedPositions.length === 0) {
151
- return null
152
- }
153
- return {
154
- ...base,
155
- type: ActivityType.LIQUIDATION,
156
- liquidatedNotionalPosition: d.liquidatedNtlPos,
157
- accountValue: d.accountValue,
158
- leverageType: d.leverageType,
159
- liquidatedPositions,
160
- } satisfies LiquidationActivity
161
- }
168
+ if (isWithdrawDelta(delta)) {
169
+ return {
170
+ ...base,
171
+ type: ActivityType.WITHDRAWAL,
172
+ asset: HL_COLLATERAL_SYMBOL,
173
+ amount: delta.usdc,
174
+ ...(delta.fee === undefined
175
+ ? {}
176
+ : { fee: { amount: delta.fee, asset: HL_COLLATERAL_SYMBOL } }),
177
+ explorerLink: entry.hash
178
+ ? `https://scan.li.fi/tx/${entry.hash}`
179
+ : undefined,
180
+ } satisfies WithdrawalActivity
181
+ }
162
182
 
163
- default:
183
+ if (isLiquidationDelta(delta)) {
184
+ const liquidatedPositions = (delta.liquidatedPositions ?? []).map((p) => ({
185
+ market: resolveMarket(p.coin),
186
+ size: p.szi,
187
+ }))
188
+ // Liquidation rows must point to at least one market. Drop entries with
189
+ // missing/empty positions so downstream consumers can rely on that
190
+ // invariant and avoid rendering a market-less liquidation card.
191
+ if (liquidatedPositions.length === 0) {
164
192
  return null
193
+ }
194
+ return {
195
+ ...base,
196
+ type: ActivityType.LIQUIDATION,
197
+ ...(delta.liquidatedNtlPos === undefined
198
+ ? {}
199
+ : { liquidatedNotionalPosition: delta.liquidatedNtlPos }),
200
+ ...(delta.accountValue === undefined
201
+ ? {}
202
+ : { accountValue: delta.accountValue }),
203
+ leverageType: delta.leverageType,
204
+ liquidatedPositions,
205
+ } satisfies LiquidationActivity
165
206
  }
207
+
208
+ return null
166
209
  }
167
210
 
168
211
  /**
@@ -40,7 +40,10 @@ export const mapFill = (fill: HlUserFill, market: MarketDisplay): Fill => ({
40
40
  status: FillStatus.FILLED,
41
41
  liquidity: fill.crossed ? LiquidityRole.TAKER : LiquidityRole.MAKER,
42
42
  filledSize: fill.sz,
43
- fee: fill.fee,
43
+ fee: {
44
+ amount: fill.fee,
45
+ asset: fill.feeToken ?? market.quoteAsset.displaySymbol,
46
+ },
44
47
  startPosition: fill.startPosition,
45
48
  explorerLink: explorerTxUrl(ExplorerChainId.HYPERLIQUID, fill.hash),
46
49
  classification:
@@ -12,6 +12,14 @@ import {
12
12
  } from '@lifi/perps-types'
13
13
  import type { HlFrontendOpenOrder, HlOrderDetail } from '../types/index.js'
14
14
 
15
+ /**
16
+ * An order as seen by the REST `frontendOpenOrders` payload, or the smaller
17
+ * shape nested in the `orderUpdates` WebSocket frame. `mapOpenOrder` and
18
+ * `mapTriggerOrder` accept either so both code paths share one mapper.
19
+ * @public
20
+ */
21
+ export type HlOrderLike = HlFrontendOpenOrder | HlOrderDetail['order']
22
+
15
23
  /**
16
24
  * Map a Hyperliquid orderType string to the OrderType enum.
17
25
  *
@@ -61,9 +69,7 @@ export const isTriggerType = (type: OrderType): boolean =>
61
69
  * 5. `orderType` — final fall-back via `isTriggerType`.
62
70
  * @public
63
71
  */
64
- export const isTriggerOrder = (
65
- o: HlFrontendOpenOrder | HlOrderDetail['order']
66
- ): boolean => {
72
+ export const isTriggerOrder = (o: HlOrderLike): boolean => {
67
73
  if ('isTrigger' in o && o.isTrigger === true) {
68
74
  return true
69
75
  }
@@ -89,20 +95,21 @@ export const isTriggerOrder = (
89
95
  * @public
90
96
  */
91
97
  export const mapOpenOrder = (
92
- o: HlFrontendOpenOrder,
98
+ o: HlOrderLike,
93
99
  market: MarketDisplay
94
100
  ): OpenOrder => ({
95
101
  orderId: String(o.oid),
96
102
  market,
97
103
  side: o.side === 'B' ? OrderSide.BUY : OrderSide.SELL,
98
104
  type: mapOrderType(o.orderType),
99
- size: o.sz,
105
+ originalSize: o.origSz,
106
+ remainingSize: o.sz,
100
107
  price: o.limitPx,
101
108
  filledSize: o.origSz
102
109
  ? (parseFloat(o.origSz) - parseFloat(o.sz)).toString()
103
110
  : '0',
104
111
  reduceOnly: o.reduceOnly ?? false,
105
- label: o.isTrigger ? o.triggerCondition : undefined,
112
+ label: 'isTrigger' in o && o.isTrigger ? o.triggerCondition : undefined,
106
113
  createdAt: new Date(o.timestamp).toISOString(),
107
114
  })
108
115
 
@@ -111,7 +118,7 @@ export const mapOpenOrder = (
111
118
  * @public
112
119
  */
113
120
  export const mapTriggerOrder = (
114
- o: HlFrontendOpenOrder,
121
+ o: HlOrderLike,
115
122
  market: MarketDisplay
116
123
  ): TriggerOrder => {
117
124
  const type = mapOrderType(o.orderType)
@@ -122,7 +129,7 @@ export const mapTriggerOrder = (
122
129
  market,
123
130
  type,
124
131
  size: o.sz,
125
- triggerPrice: o.triggerPx,
132
+ triggerPrice: o.triggerPx ?? '0',
126
133
  ...(isLimit ? { limitPrice: o.limitPx } : {}),
127
134
  label: o.triggerCondition,
128
135
  createdAt: new Date(o.timestamp).toISOString(),
@@ -15,7 +15,9 @@ export const isOpenAssetPosition = (ap: HlAssetPosition): boolean =>
15
15
  /**
16
16
  * Map a non-zero Hyperliquid position payload to the SDK's normalized
17
17
  * position. Signed wire size determines side; decimal strings remain strings
18
- * in the normalized response.
18
+ * in the normalized response. `cumFunding.sinceOpen` is negated because
19
+ * Hyperliquid signs funding paid as positive and `accruedFunding` signs it as
20
+ * negative.
19
21
  * @public
20
22
  */
21
23
  export const mapPosition = (
@@ -41,6 +43,7 @@ export const mapPosition = (
41
43
  markPrice: szi.eq(0) ? '0' : positionValue.div(szi.abs()).toFixed(),
42
44
  liquidationPrice: pos.liquidationPx ?? '0',
43
45
  unrealizedPnl: pos.unrealizedPnl,
46
+ accruedFunding: new Big(pos.cumFunding.sinceOpen).neg().toFixed(),
44
47
  leverage: ap.position.leverage.value,
45
48
  marginUsed,
46
49
  initialMarginRequirement: positionValue.div(leverage).toFixed(),
@@ -16,18 +16,16 @@ import {
16
16
  type WsStatusListener,
17
17
  wsLog,
18
18
  } from '@lifi/perps-sdk'
19
- import {
20
- type AccountResponse,
21
- type Balance,
22
- type MarketContext,
23
- type OpenOrder,
24
- type OrderbookLevel,
25
- type OrderbookResponse,
26
- OrderSide,
27
- OrderType,
28
- type Position,
29
- type Subscription,
30
- type TriggerOrder,
19
+ import type {
20
+ AccountResponse,
21
+ Balance,
22
+ MarketContext,
23
+ OpenOrder,
24
+ OrderbookLevel,
25
+ OrderbookResponse,
26
+ Position,
27
+ Subscription,
28
+ TriggerOrder,
31
29
  } from '@lifi/perps-types'
32
30
  import Big from 'big.js'
33
31
  import { isAddress } from 'viem'
@@ -69,9 +67,10 @@ import {
69
67
  isTriggerOrder,
70
68
  mapFill,
71
69
  mapMarketContext,
70
+ mapOpenOrder,
72
71
  mapOrderStatus,
73
- mapOrderType,
74
72
  mapPosition,
73
+ mapTriggerOrder,
75
74
  partitionSpotBalances,
76
75
  priceStepToAggregation,
77
76
  spotAssetFromToken,
@@ -993,39 +992,15 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
993
992
  terminated.push(orderId)
994
993
  continue
995
994
  }
996
- const type = mapOrderType(o.orderType)
997
995
  // Unknown market id (absent from the synced snapshot); skip just this item.
998
996
  const market = this.registry?.get(o.coin)
999
997
  if (!market) {
1000
998
  continue
1001
999
  }
1002
- const createdAt = new Date(o.timestamp).toISOString()
1003
1000
  if (isTriggerOrder(o)) {
1004
- const isLimit =
1005
- type === OrderType.TAKE_PROFIT_LIMIT || type === OrderType.STOP_LIMIT
1006
- triggerOrders.push({
1007
- orderId,
1008
- market,
1009
- type,
1010
- size: o.sz,
1011
- triggerPrice: o.triggerPx ?? '0',
1012
- ...(isLimit ? { limitPrice: o.limitPx } : {}),
1013
- label: o.triggerCondition,
1014
- createdAt,
1015
- })
1001
+ triggerOrders.push(mapTriggerOrder(o, market))
1016
1002
  } else {
1017
- const filled = parseFloat(o.origSz) - parseFloat(o.sz)
1018
- openOrders.push({
1019
- orderId,
1020
- market,
1021
- side: o.side === 'B' ? OrderSide.BUY : OrderSide.SELL,
1022
- type,
1023
- size: o.sz,
1024
- price: o.limitPx,
1025
- filledSize: filled.toString(),
1026
- reduceOnly: o.reduceOnly ?? false,
1027
- createdAt,
1028
- })
1003
+ openOrders.push(mapOpenOrder(o, market))
1029
1004
  }
1030
1005
  }
1031
1006
  this.emit(key, {