@lifi/perps-sdk-provider-hyperliquid 13.0.1 → 14.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 (245) hide show
  1. package/README.md +115 -0
  2. package/dist/cjs/HyperliquidProvider.d.ts.map +1 -1
  3. package/dist/cjs/HyperliquidProvider.js +1 -2
  4. package/dist/cjs/HyperliquidProvider.js.map +1 -1
  5. package/dist/cjs/constants.d.ts +1 -0
  6. package/dist/cjs/constants.d.ts.map +1 -1
  7. package/dist/cjs/constants.js +2 -1
  8. package/dist/cjs/constants.js.map +1 -1
  9. package/dist/cjs/index.d.ts +1 -3
  10. package/dist/cjs/index.d.ts.map +1 -1
  11. package/dist/cjs/index.js +2 -3
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/cjs/services/getAccount.d.ts.map +1 -1
  14. package/dist/cjs/services/getAccount.js +5 -2
  15. package/dist/cjs/services/getAccount.js.map +1 -1
  16. package/dist/cjs/services/getActivity.d.ts.map +1 -1
  17. package/dist/cjs/services/getActivity.js +40 -7
  18. package/dist/cjs/services/getActivity.js.map +1 -1
  19. package/dist/cjs/services/getFills.d.ts.map +1 -1
  20. package/dist/cjs/services/getFills.js +3 -0
  21. package/dist/cjs/services/getFills.js.map +1 -1
  22. package/dist/cjs/services/getOrder.d.ts.map +1 -1
  23. package/dist/cjs/services/getOrder.js +14 -3
  24. package/dist/cjs/services/getOrder.js.map +1 -1
  25. package/dist/cjs/services/getOrders.d.ts +2 -7
  26. package/dist/cjs/services/getOrders.d.ts.map +1 -1
  27. package/dist/cjs/services/getOrders.js +94 -31
  28. package/dist/cjs/services/getOrders.js.map +1 -1
  29. package/dist/cjs/services/getPositions.d.ts.map +1 -1
  30. package/dist/cjs/services/getPositions.js +1 -1
  31. package/dist/cjs/services/getPositions.js.map +1 -1
  32. package/dist/cjs/types/explorer.d.ts +10 -0
  33. package/dist/cjs/types/explorer.d.ts.map +1 -0
  34. package/dist/cjs/types/explorer.js +17 -0
  35. package/dist/cjs/types/explorer.js.map +1 -0
  36. package/dist/cjs/types/fill.d.ts +6 -0
  37. package/dist/cjs/types/fill.d.ts.map +1 -1
  38. package/dist/cjs/types/index.d.ts +1 -0
  39. package/dist/cjs/types/index.d.ts.map +1 -1
  40. package/dist/cjs/types/index.js +1 -0
  41. package/dist/cjs/types/index.js.map +1 -1
  42. package/dist/cjs/types/ledger.d.ts +24 -1
  43. package/dist/cjs/types/ledger.d.ts.map +1 -1
  44. package/dist/cjs/types/ledger.js +5 -1
  45. package/dist/cjs/types/ledger.js.map +1 -1
  46. package/dist/cjs/types/order.d.ts +24 -0
  47. package/dist/cjs/types/order.d.ts.map +1 -1
  48. package/dist/cjs/types/ws.d.ts +14 -0
  49. package/dist/cjs/types/ws.d.ts.map +1 -1
  50. package/dist/cjs/utils/assetId.d.ts +1 -0
  51. package/dist/cjs/utils/assetId.d.ts.map +1 -1
  52. package/dist/cjs/utils/assetId.js +6 -1
  53. package/dist/cjs/utils/assetId.js.map +1 -1
  54. package/dist/cjs/utils/explorer.d.ts +7 -0
  55. package/dist/cjs/utils/explorer.d.ts.map +1 -0
  56. package/dist/cjs/utils/explorer.js +49 -0
  57. package/dist/cjs/utils/explorer.js.map +1 -0
  58. package/dist/cjs/utils/index.d.ts +4 -3
  59. package/dist/cjs/utils/index.d.ts.map +1 -1
  60. package/dist/cjs/utils/index.js +7 -6
  61. package/dist/cjs/utils/index.js.map +1 -1
  62. package/dist/cjs/utils/infoClient.d.ts +1 -0
  63. package/dist/cjs/utils/infoClient.d.ts.map +1 -1
  64. package/dist/cjs/utils/infoClient.js +16 -10
  65. package/dist/cjs/utils/infoClient.js.map +1 -1
  66. package/dist/cjs/utils/mapActivity.d.ts +5 -3
  67. package/dist/cjs/utils/mapActivity.d.ts.map +1 -1
  68. package/dist/cjs/utils/mapActivity.js +116 -48
  69. package/dist/cjs/utils/mapActivity.js.map +1 -1
  70. package/dist/cjs/utils/mapFill.d.ts.map +1 -1
  71. package/dist/cjs/utils/mapFill.js +0 -1
  72. package/dist/cjs/utils/mapFill.js.map +1 -1
  73. package/dist/cjs/utils/mapOrder.d.ts +4 -9
  74. package/dist/cjs/utils/mapOrder.d.ts.map +1 -1
  75. package/dist/cjs/utils/mapOrder.js +149 -135
  76. package/dist/cjs/utils/mapOrder.js.map +1 -1
  77. package/dist/cjs/utils/spotBalance.d.ts.map +1 -1
  78. package/dist/cjs/utils/spotBalance.js +10 -6
  79. package/dist/cjs/utils/spotBalance.js.map +1 -1
  80. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts +3 -0
  81. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  82. package/dist/cjs/websocket/HyperliquidWsProvider.js +100 -29
  83. package/dist/cjs/websocket/HyperliquidWsProvider.js.map +1 -1
  84. package/dist/esm/HyperliquidProvider.d.ts.map +1 -1
  85. package/dist/esm/HyperliquidProvider.js +1 -2
  86. package/dist/esm/HyperliquidProvider.js.map +1 -1
  87. package/dist/esm/constants.d.ts +8 -0
  88. package/dist/esm/constants.d.ts.map +1 -1
  89. package/dist/esm/constants.js +8 -0
  90. package/dist/esm/constants.js.map +1 -1
  91. package/dist/esm/index.d.ts +1 -3
  92. package/dist/esm/index.d.ts.map +1 -1
  93. package/dist/esm/index.js +1 -2
  94. package/dist/esm/index.js.map +1 -1
  95. package/dist/esm/services/getAccount.d.ts.map +1 -1
  96. package/dist/esm/services/getAccount.js +6 -3
  97. package/dist/esm/services/getAccount.js.map +1 -1
  98. package/dist/esm/services/getActivity.d.ts +2 -2
  99. package/dist/esm/services/getActivity.d.ts.map +1 -1
  100. package/dist/esm/services/getActivity.js +44 -9
  101. package/dist/esm/services/getActivity.js.map +1 -1
  102. package/dist/esm/services/getFills.d.ts.map +1 -1
  103. package/dist/esm/services/getFills.js +4 -1
  104. package/dist/esm/services/getFills.js.map +1 -1
  105. package/dist/esm/services/getOrder.d.ts.map +1 -1
  106. package/dist/esm/services/getOrder.js +15 -4
  107. package/dist/esm/services/getOrder.js.map +1 -1
  108. package/dist/esm/services/getOrders.d.ts +4 -24
  109. package/dist/esm/services/getOrders.d.ts.map +1 -1
  110. package/dist/esm/services/getOrders.js +115 -42
  111. package/dist/esm/services/getOrders.js.map +1 -1
  112. package/dist/esm/services/getPositions.d.ts.map +1 -1
  113. package/dist/esm/services/getPositions.js +2 -2
  114. package/dist/esm/services/getPositions.js.map +1 -1
  115. package/dist/esm/types/explorer.d.ts +27 -0
  116. package/dist/esm/types/explorer.d.ts.map +1 -0
  117. package/dist/esm/types/explorer.js +20 -0
  118. package/dist/esm/types/explorer.js.map +1 -0
  119. package/dist/esm/types/fill.d.ts +11 -0
  120. package/dist/esm/types/fill.d.ts.map +1 -1
  121. package/dist/esm/types/index.d.ts +1 -0
  122. package/dist/esm/types/index.d.ts.map +1 -1
  123. package/dist/esm/types/index.js +1 -0
  124. package/dist/esm/types/index.js.map +1 -1
  125. package/dist/esm/types/ledger.d.ts +28 -1
  126. package/dist/esm/types/ledger.d.ts.map +1 -1
  127. package/dist/esm/types/ledger.js +4 -0
  128. package/dist/esm/types/ledger.js.map +1 -1
  129. package/dist/esm/types/order.d.ts +26 -0
  130. package/dist/esm/types/order.d.ts.map +1 -1
  131. package/dist/esm/types/ws.d.ts +15 -0
  132. package/dist/esm/types/ws.d.ts.map +1 -1
  133. package/dist/esm/utils/assetId.d.ts +9 -0
  134. package/dist/esm/utils/assetId.d.ts.map +1 -1
  135. package/dist/esm/utils/assetId.js +13 -0
  136. package/dist/esm/utils/assetId.js.map +1 -1
  137. package/dist/esm/utils/explorer.d.ts +31 -0
  138. package/dist/esm/utils/explorer.d.ts.map +1 -0
  139. package/dist/esm/utils/explorer.js +68 -0
  140. package/dist/esm/utils/explorer.js.map +1 -0
  141. package/dist/esm/utils/index.d.ts +4 -3
  142. package/dist/esm/utils/index.d.ts.map +1 -1
  143. package/dist/esm/utils/index.js +4 -3
  144. package/dist/esm/utils/index.js.map +1 -1
  145. package/dist/esm/utils/infoClient.d.ts +14 -3
  146. package/dist/esm/utils/infoClient.d.ts.map +1 -1
  147. package/dist/esm/utils/infoClient.js +29 -14
  148. package/dist/esm/utils/infoClient.js.map +1 -1
  149. package/dist/esm/utils/mapActivity.d.ts +16 -13
  150. package/dist/esm/utils/mapActivity.d.ts.map +1 -1
  151. package/dist/esm/utils/mapActivity.js +128 -72
  152. package/dist/esm/utils/mapActivity.js.map +1 -1
  153. package/dist/esm/utils/mapFill.d.ts.map +1 -1
  154. package/dist/esm/utils/mapFill.js +1 -2
  155. package/dist/esm/utils/mapFill.js.map +1 -1
  156. package/dist/esm/utils/mapOrder.d.ts +8 -73
  157. package/dist/esm/utils/mapOrder.d.ts.map +1 -1
  158. package/dist/esm/utils/mapOrder.js +148 -187
  159. package/dist/esm/utils/mapOrder.js.map +1 -1
  160. package/dist/esm/utils/spotBalance.d.ts +1 -1
  161. package/dist/esm/utils/spotBalance.d.ts.map +1 -1
  162. package/dist/esm/utils/spotBalance.js +12 -7
  163. package/dist/esm/utils/spotBalance.js.map +1 -1
  164. package/dist/esm/websocket/HyperliquidWsProvider.d.ts +5 -1
  165. package/dist/esm/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  166. package/dist/esm/websocket/HyperliquidWsProvider.js +105 -34
  167. package/dist/esm/websocket/HyperliquidWsProvider.js.map +1 -1
  168. package/dist/types/HyperliquidProvider.d.ts.map +1 -1
  169. package/dist/types/constants.d.ts +8 -0
  170. package/dist/types/constants.d.ts.map +1 -1
  171. package/dist/types/index.d.ts +1 -3
  172. package/dist/types/index.d.ts.map +1 -1
  173. package/dist/types/services/getAccount.d.ts.map +1 -1
  174. package/dist/types/services/getActivity.d.ts +2 -2
  175. package/dist/types/services/getActivity.d.ts.map +1 -1
  176. package/dist/types/services/getFills.d.ts.map +1 -1
  177. package/dist/types/services/getOrder.d.ts.map +1 -1
  178. package/dist/types/services/getOrders.d.ts +4 -24
  179. package/dist/types/services/getOrders.d.ts.map +1 -1
  180. package/dist/types/services/getPositions.d.ts.map +1 -1
  181. package/dist/types/types/explorer.d.ts +27 -0
  182. package/dist/types/types/explorer.d.ts.map +1 -0
  183. package/dist/types/types/fill.d.ts +11 -0
  184. package/dist/types/types/fill.d.ts.map +1 -1
  185. package/dist/types/types/index.d.ts +1 -0
  186. package/dist/types/types/index.d.ts.map +1 -1
  187. package/dist/types/types/ledger.d.ts +28 -1
  188. package/dist/types/types/ledger.d.ts.map +1 -1
  189. package/dist/types/types/order.d.ts +26 -0
  190. package/dist/types/types/order.d.ts.map +1 -1
  191. package/dist/types/types/ws.d.ts +15 -0
  192. package/dist/types/types/ws.d.ts.map +1 -1
  193. package/dist/types/utils/assetId.d.ts +9 -0
  194. package/dist/types/utils/assetId.d.ts.map +1 -1
  195. package/dist/types/utils/explorer.d.ts +31 -0
  196. package/dist/types/utils/explorer.d.ts.map +1 -0
  197. package/dist/types/utils/index.d.ts +4 -3
  198. package/dist/types/utils/index.d.ts.map +1 -1
  199. package/dist/types/utils/infoClient.d.ts +14 -3
  200. package/dist/types/utils/infoClient.d.ts.map +1 -1
  201. package/dist/types/utils/mapActivity.d.ts +16 -13
  202. package/dist/types/utils/mapActivity.d.ts.map +1 -1
  203. package/dist/types/utils/mapFill.d.ts.map +1 -1
  204. package/dist/types/utils/mapOrder.d.ts +8 -73
  205. package/dist/types/utils/mapOrder.d.ts.map +1 -1
  206. package/dist/types/utils/spotBalance.d.ts +1 -1
  207. package/dist/types/utils/spotBalance.d.ts.map +1 -1
  208. package/dist/types/websocket/HyperliquidWsProvider.d.ts +5 -1
  209. package/dist/types/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  210. package/package.json +3 -3
  211. package/src/HyperliquidProvider.ts +1 -13
  212. package/src/constants.ts +10 -0
  213. package/src/index.ts +1 -2
  214. package/src/services/getAccount.ts +8 -4
  215. package/src/services/getActivity.ts +81 -6
  216. package/src/services/getFills.ts +4 -1
  217. package/src/services/getOrder.ts +29 -5
  218. package/src/services/getOrders.ts +185 -70
  219. package/src/services/getPositions.ts +4 -1
  220. package/src/types/explorer.ts +39 -0
  221. package/src/types/fill.ts +12 -0
  222. package/src/types/index.ts +1 -0
  223. package/src/types/ledger.ts +37 -0
  224. package/src/types/order.ts +25 -0
  225. package/src/types/ws.ts +16 -0
  226. package/src/utils/assetId.ts +16 -0
  227. package/src/utils/explorer.ts +93 -0
  228. package/src/utils/index.ts +16 -7
  229. package/src/utils/infoClient.ts +46 -24
  230. package/src/utils/mapActivity.ts +163 -75
  231. package/src/utils/mapFill.ts +0 -2
  232. package/src/utils/mapOrder.ts +163 -212
  233. package/src/utils/spotBalance.ts +12 -7
  234. package/src/websocket/HyperliquidWsProvider.ts +127 -40
  235. package/dist/cjs/services/getRunningTwaps.d.ts +0 -10
  236. package/dist/cjs/services/getRunningTwaps.d.ts.map +0 -1
  237. package/dist/cjs/services/getRunningTwaps.js +0 -46
  238. package/dist/cjs/services/getRunningTwaps.js.map +0 -1
  239. package/dist/esm/services/getRunningTwaps.d.ts +0 -12
  240. package/dist/esm/services/getRunningTwaps.d.ts.map +0 -1
  241. package/dist/esm/services/getRunningTwaps.js +0 -40
  242. package/dist/esm/services/getRunningTwaps.js.map +0 -1
  243. package/dist/types/services/getRunningTwaps.d.ts +0 -12
  244. package/dist/types/services/getRunningTwaps.d.ts.map +0 -1
  245. package/src/services/getRunningTwaps.ts +0 -90
@@ -1,31 +1,32 @@
1
+ import { PerpsError, triggerConditionFor } from '@lifi/perps-sdk'
1
2
  import type {
2
3
  MarketDisplay,
3
- OpenOrder,
4
4
  Order,
5
+ OrderBase,
5
6
  TriggerOrder,
6
7
  } from '@lifi/perps-types'
7
8
  import {
8
9
  OrderSide,
9
10
  OrderStatus,
10
11
  OrderType,
12
+ PerpsErrorCode,
11
13
  TimeInForce,
12
14
  } from '@lifi/perps-types'
13
- import type { HlFrontendOpenOrder, HlOrderDetail } from '../types/index.js'
15
+ import Big from 'big.js'
16
+ import { PROVIDER_KEY } from '../constants.js'
17
+ import type {
18
+ HlFrontendOpenOrder,
19
+ HlOrderDetail,
20
+ HlTwapHistoryEntry,
21
+ } from '../types/index.js'
14
22
 
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
- */
23
+ /** Order payload shared by frontend, historical, and single-order reads. */
21
24
  export type HlOrderLike = HlFrontendOpenOrder | HlOrderDetail['order']
22
25
 
23
- /**
24
- * Map a Hyperliquid orderType string to the OrderType enum.
25
- *
26
- * @public
27
- */
28
- export const mapOrderType = (orderType: string): OrderType => {
26
+ /** Translate documented venue execution types without reading trigger prose. */
27
+ export const mapOrderType = (
28
+ orderType: string
29
+ ): Exclude<OrderType, OrderType.TWAP> => {
29
30
  switch (orderType) {
30
31
  case 'Take Profit Market':
31
32
  return OrderType.TAKE_PROFIT_MARKET
@@ -37,243 +38,193 @@ export const mapOrderType = (orderType: string): OrderType => {
37
38
  return OrderType.STOP_LIMIT
38
39
  case 'Market':
39
40
  return OrderType.MARKET
40
- default:
41
+ case 'Limit':
41
42
  return OrderType.LIMIT
43
+ default:
44
+ throw venueError(`Unknown Hyperliquid order type: ${orderType}`)
42
45
  }
43
46
  }
44
47
 
45
- /** Return whether an SDK order type represents a take-profit or stop trigger. @public */
46
- export const isTriggerType = (type: OrderType): boolean =>
47
- type === OrderType.TAKE_PROFIT_MARKET ||
48
- type === OrderType.TAKE_PROFIT_LIMIT ||
49
- type === OrderType.STOP_MARKET ||
50
- type === OrderType.STOP_LIMIT
51
-
52
- /**
53
- * Decide whether a raw Hyperliquid order belongs in the cross-provider
54
- * `triggerOrders` bucket. Single source of truth for both the REST
55
- * `getOrders` path (`HlFrontendOpenOrder` payload) and the `orderUpdates`
56
- * WS handler (`HlOrderDetail.order`, a strict subset).
57
- *
58
- * Authoritative signals, in order:
59
- * 1. `isTrigger` — REST-only boolean Hyperliquid sets on
60
- * `frontendOpenOrders`. Definitive when present.
61
- * 2. `isPositionTpsl` — REST-only flag for TP/SL legs attached to a
62
- * position.
63
- * 3. `triggerCondition !== 'N/A'` — present on both REST and WS payloads.
64
- * Non-trigger orders carry the literal `'N/A'`.
65
- * 4. `triggerPx` non-null and non-zero — present on both. The reliable
66
- * trigger signal on the WS wire when `orderType` lies (HL pushes
67
- * `'Limit'` for a freshly-placed TP/SL until REST refetch lands the
68
- * corrected type).
69
- * 5. `orderType` — final fall-back via `isTriggerType`.
70
- * @public
71
- */
72
- export const isTriggerOrder = (o: HlOrderLike): boolean => {
73
- if ('isTrigger' in o && o.isTrigger === true) {
74
- return true
75
- }
76
- if ('isPositionTpsl' in o && o.isPositionTpsl === true) {
77
- return true
78
- }
79
- if (o.triggerCondition && o.triggerCondition !== 'N/A') {
80
- return true
81
- }
82
- if (
83
- o.triggerPx != null &&
84
- o.triggerPx !== '' &&
85
- parseFloat(o.triggerPx) > 0
86
- ) {
87
- return true
88
- }
89
- return isTriggerType(mapOrderType(o.orderType))
90
- }
91
-
92
- /**
93
- * Map an open Hyperliquid order to the SDK's normalized open-order shape.
94
- * Numeric IDs become strings and wire millisecond timestamps become ISO dates.
95
- * @public
96
- */
97
- export const mapOpenOrder = (
98
- o: HlOrderLike,
99
- market: MarketDisplay
100
- ): OpenOrder => ({
101
- orderId: String(o.oid),
102
- market,
103
- side: o.side === 'B' ? OrderSide.BUY : OrderSide.SELL,
104
- type: mapOrderType(o.orderType),
105
- originalSize: o.origSz,
106
- remainingSize: o.sz,
107
- price: o.limitPx,
108
- filledSize: o.origSz
109
- ? (parseFloat(o.origSz) - parseFloat(o.sz)).toString()
110
- : '0',
111
- reduceOnly: o.reduceOnly ?? false,
112
- label: 'isTrigger' in o && o.isTrigger ? o.triggerCondition : undefined,
113
- createdAt: new Date(o.timestamp).toISOString(),
114
- })
115
-
116
- /**
117
- * Map a Hyperliquid trigger/TP-SL order to the SDK's trigger-order shape.
118
- * @public
119
- */
120
- export const mapTriggerOrder = (
121
- o: HlOrderLike,
122
- market: MarketDisplay
123
- ): TriggerOrder => {
124
- const type = mapOrderType(o.orderType)
125
- const isLimit =
126
- type === OrderType.TAKE_PROFIT_LIMIT || type === OrderType.STOP_LIMIT
127
- return {
128
- orderId: String(o.oid),
129
- market,
130
- type,
131
- size: o.sz,
132
- triggerPrice: o.triggerPx ?? '0',
133
- ...(isLimit ? { limitPrice: o.limitPx } : {}),
134
- label: o.triggerCondition,
135
- createdAt: new Date(o.timestamp).toISOString(),
136
- }
137
- }
138
-
139
- /**
140
- * Map a raw Hyperliquid order status string to the SDK's OrderStatus enum.
141
- *
142
- * HL's terminal statuses are mostly compound strings ending in `Canceled`
143
- * or `Rejected` (e.g. `siblingFilledCanceled`, `tickRejected`); classify
144
- * those by suffix so a status HL adds later still lands in the correct
145
- * terminal bucket instead of silently falling through to PENDING, which
146
- * {@link isActiveOrderStatus} treats as active. `scheduledCancel` is the
147
- * one documented cancel status without the `Canceled` suffix.
148
- * @public
149
- */
48
+ /** Translate venue lifecycle statuses; unknown statuses are errors. */
150
49
  export const mapOrderStatus = (status: string): OrderStatus => {
151
50
  switch (status) {
152
51
  case 'open':
153
- case 'resting':
154
52
  return OrderStatus.OPEN
155
53
  case 'filled':
156
54
  return OrderStatus.FILLED
157
55
  case 'canceled':
158
- case 'cancelled':
159
56
  case 'scheduledCancel':
160
- return OrderStatus.CANCELLED
161
- case 'rejected':
162
- return OrderStatus.REJECTED
163
- case 'triggered':
164
- return OrderStatus.TRIGGERED
165
- default:
166
- if (status.endsWith('Canceled') || status.endsWith('Cancelled')) {
167
- return OrderStatus.CANCELLED
168
- }
169
- if (status.endsWith('Rejected')) {
170
- return OrderStatus.REJECTED
171
- }
172
- return OrderStatus.PENDING
173
- }
174
- }
175
-
176
- /**
177
- * Map a raw Hyperliquid order status to a short English sentence
178
- * describing *why* the order ended in a terminal non-FILLED state. Bare
179
- * `canceled`/`cancelled`/`rejected` carry no actionable detail and
180
- * return `undefined`; so do non-terminal and unknown values.
181
- * @public
182
- */
183
- export const mapStatusReason = (status: string): string | undefined => {
184
- switch (status) {
185
- case 'iocCancelRejected':
186
- return 'Order cancelled: not enough liquidity to fill immediately.'
187
- case 'reduceOnlyCanceled':
188
- return 'Order cancelled: would not reduce your position.'
189
57
  case 'marginCanceled':
190
- return 'Order cancelled: insufficient margin.'
191
- case 'liquidatedCanceled':
192
- return 'Order cancelled: account was liquidated.'
193
- case 'siblingFilledCanceled':
194
- return 'Order cancelled: sibling OCO order filled first.'
58
+ case 'vaultWithdrawalCanceled':
59
+ case 'openInterestCapCanceled':
195
60
  case 'selfTradeCanceled':
196
- return 'Order cancelled: would self-trade against your own resting order.'
197
- case 'scheduledCancel':
198
- return "Order cancelled: dead man's switch triggered."
61
+ case 'reduceOnlyCanceled':
62
+ case 'siblingFilledCanceled':
63
+ case 'delistedCanceled':
64
+ case 'liquidatedCanceled':
65
+ return OrderStatus.CANCELLED
66
+ case 'rejected':
199
67
  case 'tickRejected':
200
- return 'Order rejected: price did not match the tick size.'
201
68
  case 'minTradeNtlRejected':
202
- return 'Order rejected: notional value below the minimum trade size.'
203
- case 'delistedCanceled':
204
- return 'Order cancelled: market has been delisted.'
205
69
  case 'perpMarginRejected':
206
- return 'Order rejected: insufficient margin.'
207
70
  case 'reduceOnlyRejected':
208
- return 'Order rejected: would not reduce your position.'
209
71
  case 'badAloPxRejected':
210
- return 'Order rejected: post-only order would have matched immediately.'
72
+ case 'iocCancelRejected':
211
73
  case 'badTriggerPxRejected':
212
- return 'Order rejected: invalid take-profit/stop-loss trigger price.'
213
74
  case 'marketOrderNoLiquidityRejected':
214
- return 'Order rejected: not enough liquidity for the market order.'
215
- case 'oracleRejected':
216
- return 'Order rejected: price too far from the oracle price.'
217
- case 'vaultWithdrawalCanceled':
218
- return 'Order cancelled: a vault withdrawal occurred.'
219
- case 'openInterestCapCanceled':
220
- return 'Order cancelled: too aggressive while open interest was at its cap.'
221
75
  case 'positionIncreaseAtOpenInterestCapRejected':
222
76
  case 'positionFlipAtOpenInterestCapRejected':
223
- case 'openInterestIncreaseRejected':
224
- return 'Order rejected: open interest is at its cap.'
225
77
  case 'tooAggressiveAtOpenInterestCapRejected':
226
- return 'Order rejected: price too aggressive while open interest was at its cap.'
78
+ case 'openInterestIncreaseRejected':
227
79
  case 'insufficientSpotBalanceRejected':
228
- return 'Order rejected: insufficient spot balance.'
80
+ case 'oracleRejected':
229
81
  case 'perpMaxPositionRejected':
230
- return 'Order rejected: exceeds the maximum position size for the current leverage tier.'
82
+ return OrderStatus.REJECTED
83
+ case 'triggered':
84
+ return OrderStatus.TRIGGERED
231
85
  default:
232
- return undefined
86
+ throw venueError(`Unknown Hyperliquid order status: ${status}`)
233
87
  }
234
88
  }
235
89
 
236
- const mapTimeInForce = (tif: string | undefined): TimeInForce | undefined => {
237
- switch (tif) {
238
- case 'Gtc':
239
- return TimeInForce.GTC
240
- case 'Ioc':
241
- return TimeInForce.IOC
242
- case 'Alo':
243
- return TimeInForce.POST_ONLY
244
- default:
245
- return undefined
246
- }
90
+ function venueError(message: string): PerpsError {
91
+ const error = new PerpsError(PerpsErrorCode.ThirdPartyError, message)
92
+ error.tool = PROVIDER_KEY
93
+ return error
247
94
  }
248
95
 
249
- /**
250
- * Map an `orderStatus` detail to the SDK's normalized order shape, preserving
251
- * decimal size/price strings and translating status, side, and time-in-force.
252
- * @public
253
- */
96
+ /** Normalize regular, trigger, and TWAP rows through one lifecycle model. */
254
97
  export const mapOrder = (
255
- detail: HlOrderDetail,
256
- market: MarketDisplay
98
+ raw: HlOrderLike | HlOrderDetail | HlTwapHistoryEntry,
99
+ market: MarketDisplay,
100
+ parentOrderId?: string
257
101
  ): Order => {
258
- const o = detail.order
259
- const filled = parseFloat(o.origSz) - parseFloat(o.sz)
260
-
261
- return {
102
+ if ('state' in raw) {
103
+ if (raw.twapId === undefined) {
104
+ throw venueError('Hyperliquid returned a TWAP without a twapId.')
105
+ }
106
+ const { state } = raw
107
+ const filled = new Big(state.executedSz)
108
+ let status: OrderStatus
109
+ switch (raw.status.status) {
110
+ case 'activated':
111
+ status = filled.gt(0) ? OrderStatus.PARTIALLY_FILLED : OrderStatus.OPEN
112
+ break
113
+ case 'waitingForTrigger':
114
+ status = OrderStatus.OPEN
115
+ break
116
+ case 'finished':
117
+ status = OrderStatus.FILLED
118
+ break
119
+ case 'terminated':
120
+ case 'stopped':
121
+ status = OrderStatus.CANCELLED
122
+ break
123
+ case 'error':
124
+ status = OrderStatus.REJECTED
125
+ break
126
+ default:
127
+ throw venueError(
128
+ `Unknown Hyperliquid TWAP status: ${raw.status.status}`
129
+ )
130
+ }
131
+ return {
132
+ orderId: String(raw.twapId),
133
+ market,
134
+ type: OrderType.TWAP,
135
+ side: state.side === 'B' ? OrderSide.BUY : OrderSide.SELL,
136
+ originalSize: state.sz,
137
+ remainingSize: new Big(state.sz).minus(filled).toFixed(),
138
+ filledSize: state.executedSz,
139
+ ...(filled.eq(0)
140
+ ? {}
141
+ : { averagePrice: new Big(state.executedNtl).div(filled).toFixed() }),
142
+ reduceOnly: state.reduceOnly,
143
+ status,
144
+ ...(status === OrderStatus.CANCELLED || status === OrderStatus.REJECTED
145
+ ? { statusReason: raw.status.description ?? raw.status.status }
146
+ : {}),
147
+ createdAt: new Date(state.timestamp).toISOString(),
148
+ updatedAt: new Date(raw.time * 1000).toISOString(),
149
+ startedAt: new Date(state.timestamp).toISOString(),
150
+ durationSeconds: state.minutes * 60,
151
+ }
152
+ }
153
+ const o = 'order' in raw ? raw.order : raw
154
+ const venueStatus = 'order' in raw ? raw.status : 'open'
155
+ const filled = new Big(o.origSz).minus(o.sz)
156
+ let status = mapOrderStatus(venueStatus)
157
+ if (parentOrderId !== undefined && !('order' in raw)) {
158
+ status = OrderStatus.PENDING
159
+ } else if (status === OrderStatus.OPEN && filled.gt(0)) {
160
+ status = OrderStatus.PARTIALLY_FILLED
161
+ }
162
+ const base: OrderBase = {
262
163
  orderId: String(o.oid),
263
164
  market,
165
+ ...(typeof o.cloid === 'string' ? { clientOrderId: o.cloid } : {}),
264
166
  side: o.side === 'B' ? OrderSide.BUY : OrderSide.SELL,
265
- type: mapOrderType(o.orderType),
266
- price: o.limitPx,
167
+ status,
168
+ ...(status === OrderStatus.CANCELLED || status === OrderStatus.REJECTED
169
+ ? { statusReason: venueStatus }
170
+ : {}),
267
171
  originalSize: o.origSz,
268
172
  remainingSize: o.sz,
269
- filledSize: filled.toString(),
270
- timeInForce: mapTimeInForce(o.tif ?? undefined),
271
- reduceOnly: o.reduceOnly ?? undefined,
272
- isTrigger: o.triggerCondition !== undefined && o.triggerCondition !== 'N/A',
273
- triggerPrice: o.triggerPx ?? undefined,
274
- status: mapOrderStatus(detail.status),
275
- statusReason: mapStatusReason(detail.status),
173
+ filledSize: filled.toFixed(),
174
+ reduceOnly: o.reduceOnly,
175
+ ...(parentOrderId === undefined ? {} : { parentOrderId }),
276
176
  createdAt: new Date(o.timestamp).toISOString(),
277
- updatedAt: new Date(detail.statusTimestamp).toISOString(),
177
+ updatedAt: new Date(
178
+ 'order' in raw ? raw.statusTimestamp : o.timestamp
179
+ ).toISOString(),
180
+ }
181
+ let type = mapOrderType(o.orderType)
182
+ if (o.isTrigger && (type === OrderType.MARKET || type === OrderType.LIMIT)) {
183
+ if (o.tpsl === undefined) {
184
+ throw venueError('Hyperliquid trigger order has no tpsl discriminator.')
185
+ }
186
+ type =
187
+ o.tpsl === 'tp'
188
+ ? type === OrderType.MARKET
189
+ ? OrderType.TAKE_PROFIT_MARKET
190
+ : OrderType.TAKE_PROFIT_LIMIT
191
+ : type === OrderType.MARKET
192
+ ? OrderType.STOP_MARKET
193
+ : OrderType.STOP_LIMIT
194
+ }
195
+ if (type !== OrderType.MARKET && type !== OrderType.LIMIT) {
196
+ const triggerType: TriggerOrder['type'] = type
197
+ if (o.triggerPx === null) {
198
+ throw venueError('Hyperliquid trigger order has no trigger price.')
199
+ }
200
+ return {
201
+ ...base,
202
+ type: triggerType,
203
+ triggerPrice: o.triggerPx,
204
+ triggerCondition: triggerConditionFor(triggerType, base.side),
205
+ ...(type === OrderType.STOP_LIMIT || type === OrderType.TAKE_PROFIT_LIMIT
206
+ ? { limitPrice: o.limitPx }
207
+ : {}),
208
+ }
209
+ }
210
+ let timeInForce: TimeInForce
211
+ switch (o.tif) {
212
+ case 'Ioc':
213
+ case 'FrontendMarket':
214
+ case 'LiquidationMarket':
215
+ timeInForce = TimeInForce.IOC
216
+ break
217
+ case 'Alo':
218
+ timeInForce = TimeInForce.POST_ONLY
219
+ break
220
+ case 'Gtc':
221
+ case null:
222
+ case undefined:
223
+ timeInForce =
224
+ type === OrderType.MARKET ? TimeInForce.IOC : TimeInForce.GTC
225
+ break
226
+ default:
227
+ throw venueError(`Unknown Hyperliquid time in force: ${o.tif}`)
278
228
  }
229
+ return { ...base, type, price: o.limitPx, timeInForce }
279
230
  }
@@ -41,14 +41,19 @@ export const spotAssetFromToken = (b: HlSpotBalance): Asset => ({
41
41
  logoURI: spotLogoURI(b.coin),
42
42
  })
43
43
 
44
- /** Assemble a typed spot {@link Balance}; `total` is native token units and its USD value uses `priceById`. @public */
44
+ /** Assemble a typed spot {@link Balance}; `total` is native token units and its unit price and USD value use `priceById`. @public */
45
45
  export const spotBalance = (
46
46
  asset: Asset,
47
47
  total: string,
48
48
  priceById: Map<string, number>
49
- ): Balance => ({
50
- categoryId: SPOT_MARKET_ID,
51
- asset,
52
- units: total,
53
- valueUsd: (stringToFloat(total) * (priceById.get(asset.id) ?? 0)).toString(),
54
- })
49
+ ): Balance => {
50
+ const price = priceById.get(asset.id) ?? 0
51
+ return {
52
+ categoryId: SPOT_MARKET_ID,
53
+ asset,
54
+ units: total,
55
+ valueUsd: (stringToFloat(total) * price).toString(),
56
+ // A zero entry means the map holds no mark for the asset, not a free asset.
57
+ ...(price > 0 ? { price: price.toString() } : {}),
58
+ }
59
+ }