@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
@@ -17,7 +17,6 @@ import {
17
17
  type ProviderGetPortfolioHistoryParams,
18
18
  type ProviderGetPositionsParams,
19
19
  type ProviderGetQuoteParams,
20
- type ProviderGetRunningTwapsParams,
21
20
  resolveQuote,
22
21
  type SDKRequestOptions,
23
22
  type SignActionsContext,
@@ -46,7 +45,6 @@ import {
46
45
  type Quote,
47
46
  type SignedActionStep,
48
47
  type SigningMethod,
49
- type TwapOrder,
50
48
  } from '@lifi/perps-types'
51
49
  import { type Address, type Hex, isAddress } from 'viem'
52
50
  import { projectHyperliquidConfigSettings } from './accountConfig.js'
@@ -67,7 +65,6 @@ import { getOrder } from './services/getOrder.js'
67
65
  import { getOrders } from './services/getOrders.js'
68
66
  import { getPortfolioHistory } from './services/getPortfolioHistory.js'
69
67
  import { getPositions } from './services/getPositions.js'
70
- import { getRunningTwaps } from './services/getRunningTwaps.js'
71
68
  import {
72
69
  type HyperliquidAgent,
73
70
  HyperliquidAgentStore,
@@ -331,22 +328,13 @@ export function hyperliquidProvider(
331
328
  contextRef.require(),
332
329
  {
333
330
  address: params.address,
331
+ statuses: params.statuses,
334
332
  marketId: params.marketId,
335
333
  limit: params.limit,
336
334
  },
337
335
  opts
338
336
  ),
339
337
 
340
- getRunningTwaps: (
341
- params: ProviderGetRunningTwapsParams,
342
- opts?: SDKRequestOptions
343
- ): Promise<TwapOrder[]> =>
344
- getRunningTwaps(
345
- contextRef.require(),
346
- { address: params.address, marketId: params.marketId },
347
- opts
348
- ),
349
-
350
338
  getOrder: (
351
339
  params: ProviderGetOrderParams,
352
340
  opts?: SDKRequestOptions
package/src/constants.ts CHANGED
@@ -15,6 +15,16 @@ export const PROVIDER_KEY = 'hyperliquid'
15
15
  */
16
16
  export const DEFAULT_HYPERLIQUID_API_URL = 'https://api.hyperliquid.xyz'
17
17
 
18
+ /**
19
+ * Hyperliquid explorer RPC endpoint. It serves the `userDetails` transaction
20
+ * window from a host separate from the REST API, and it answers a browser
21
+ * cross-origin POST.
22
+ *
23
+ * @public
24
+ */
25
+ export const HYPERLIQUID_EXPLORER_RPC_URL =
26
+ 'https://rpc.hyperliquid.xyz/explorer'
27
+
18
28
  /**
19
29
  * Approximate published baseline maker/taker fee tier for Hyperliquid, used to
20
30
  * seed fee math before the live account `feeTier` resolves. Rates are fractions
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export { projectHyperliquidConfigSettings } from './accountConfig.js'
15
15
  export { getAccountSummary } from './accountSummary.js'
16
16
  export {
17
17
  DEFAULT_HYPERLIQUID_API_URL,
18
+ HYPERLIQUID_EXPLORER_RPC_URL,
18
19
  HYPERLIQUID_FEE_TIER_FALLBACK,
19
20
  PROVIDER_KEY as HYPERLIQUID_PROVIDER_KEY,
20
21
  } from './constants.js'
@@ -39,8 +40,6 @@ export type { GetPortfolioHistoryParams } from './services/getPortfolioHistory.j
39
40
  export { getPortfolioHistory } from './services/getPortfolioHistory.js'
40
41
  export type { GetPositionsParams } from './services/getPositions.js'
41
42
  export { getPositions } from './services/getPositions.js'
42
- export type { GetRunningTwapsParams } from './services/getRunningTwaps.js'
43
- export { getRunningTwaps } from './services/getRunningTwaps.js'
44
43
  export type { HyperliquidAgent } from './signers/HyperliquidAgentStore.js'
45
44
  export { HyperliquidAgentStore } from './signers/HyperliquidAgentStore.js'
46
45
  export * from './types/index.js'
@@ -26,6 +26,7 @@ import {
26
26
  import { isUnifiedAbstraction } from '../utils/abstractionMode.js'
27
27
  import { toWireBig } from '../utils/decimal.js'
28
28
  import {
29
+ assetIsOutcome,
29
30
  partitionSpotBalances,
30
31
  perpsDexNames,
31
32
  spotAssetFromToken,
@@ -102,9 +103,9 @@ const buildBalances = (
102
103
  quoteAssetByCategory: Map<string, Asset>
103
104
  ): BalancePartition => {
104
105
  const { balances, collateralBalances } = partitionSpotBalances(
105
- spotState.balances.map((b) =>
106
- spotBalance(spotAssetFromToken(b), b.total, priceById)
107
- ),
106
+ spotState.balances
107
+ .filter((b) => !assetIsOutcome(b.coin))
108
+ .map((b) => spotBalance(spotAssetFromToken(b), b.total, priceById)),
108
109
  quoteAssetIds,
109
110
  abstraction === HlAbstractionMode.PORTFOLIO_MARGIN
110
111
  )
@@ -127,6 +128,7 @@ const buildBalances = (
127
128
  asset: quoteAssetByCategory.get(categoryId)!,
128
129
  units: value.toFixed(),
129
130
  valueUsd: value.toFixed(),
131
+ price: '1',
130
132
  })
131
133
  }
132
134
  }
@@ -220,7 +222,9 @@ export const getAccount = async (
220
222
 
221
223
  const positions: Position[] = stateResults.flatMap((state) =>
222
224
  state.assetPositions
223
- .filter(isOpenAssetPosition)
225
+ .filter(
226
+ (ap) => !assetIsOutcome(ap.position.coin) && isOpenAssetPosition(ap)
227
+ )
224
228
  .map((ap) =>
225
229
  mapPosition(
226
230
  ap,
@@ -1,4 +1,9 @@
1
- import { getMarketRegistry, type SDKRequestOptions } from '@lifi/perps-sdk'
1
+ import {
2
+ type AssetRegistry,
3
+ getAssetRegistry,
4
+ getMarketRegistry,
5
+ type SDKRequestOptions,
6
+ } from '@lifi/perps-sdk'
2
7
  import type {
3
8
  ActivitiesResponse,
4
9
  ActivityItem,
@@ -14,10 +19,24 @@ import {
14
19
  } from '../constants.js'
15
20
  import type { HyperliquidContext } from '../context.js'
16
21
  import type {
22
+ HlUserFills,
17
23
  HlUserFunding,
18
24
  HlUserNonFundingLedgerUpdates,
19
25
  } from '../types/index.js'
20
- import { mapFundingActivity, mapLedgerEntry } from '../utils/index.js'
26
+ import {
27
+ isCollateralTransferDelta,
28
+ isDepositDelta,
29
+ isLiquidationDelta,
30
+ isSendAssetDelta,
31
+ isSpotTransferDelta,
32
+ isVaultTransferDelta,
33
+ isWithdrawDelta,
34
+ } from '../types/index.js'
35
+ import {
36
+ mapFundingActivity,
37
+ mapLedgerEntry,
38
+ mapLiquidationFills,
39
+ } from '../utils/index.js'
21
40
  import {
22
41
  hlInfoOptions,
23
42
  type InfoRequestOptions,
@@ -48,6 +67,12 @@ const MARKET_BEARING_TYPES: ReadonlySet<ActivityType> = new Set([
48
67
  ActivityType.LIQUIDATION,
49
68
  ])
50
69
 
70
+ const ASSET_BEARING_TYPES: ReadonlySet<ActivityType> = new Set([
71
+ ActivityType.DEPOSIT,
72
+ ActivityType.WITHDRAWAL,
73
+ ActivityType.TRANSFER,
74
+ ])
75
+
51
76
  const needsMarkets = (typeFilter: ActivityType[] | undefined): boolean =>
52
77
  !typeFilter || typeFilter.some((t) => MARKET_BEARING_TYPES.has(t))
53
78
 
@@ -55,14 +80,19 @@ const fetchActivityData = async (
55
80
  apiUrl: string,
56
81
  typeFilter: ActivityType[] | undefined,
57
82
  timeParams: { user: Address; startTime?: number; endTime?: number },
83
+ assetRegistry: AssetRegistry,
58
84
  resolveMarket: (coin: string) => MarketDisplay | undefined,
59
85
  options?: InfoRequestOptions
60
86
  ): Promise<ActivityItem[]> => {
61
87
  const needLedger =
62
88
  !typeFilter || typeFilter.some((t) => t !== ActivityType.FUNDING)
63
89
  const needFunding = !typeFilter || typeFilter.includes(ActivityType.FUNDING)
90
+ // A liquidation executed as a market order reaches the account as fills
91
+ // with a `liquidation` field and never as a ledger `liquidation` delta.
92
+ const needLiquidationFills =
93
+ !typeFilter || typeFilter.includes(ActivityType.LIQUIDATION)
64
94
 
65
- const [ledgerUpdates, fundingUpdates] = await Promise.all([
95
+ const [ledgerUpdates, fundingUpdates, fills] = await Promise.all([
66
96
  needLedger
67
97
  ? infoRequest<HlUserNonFundingLedgerUpdates>(
68
98
  apiUrl,
@@ -77,14 +107,38 @@ const fetchActivityData = async (
77
107
  options
78
108
  )
79
109
  : Promise.resolve([] as HlUserFunding),
110
+ needLiquidationFills
111
+ ? infoRequest<HlUserFills>(
112
+ apiUrl,
113
+ timeParams.startTime === undefined
114
+ ? { type: 'userFills', user: timeParams.user }
115
+ : { type: 'userFillsByTime', ...timeParams },
116
+ options
117
+ )
118
+ : Promise.resolve([] as HlUserFills),
80
119
  ])
81
120
 
82
121
  const ledgerItems: ActivityItem[] = ledgerUpdates.flatMap(
83
122
  (entry): ActivityItem[] => {
123
+ if (
124
+ typeFilter !== undefined &&
125
+ ((isDepositDelta(entry.delta) &&
126
+ !typeFilter.includes(ActivityType.DEPOSIT)) ||
127
+ (isWithdrawDelta(entry.delta) &&
128
+ !typeFilter.includes(ActivityType.WITHDRAWAL)) ||
129
+ (!typeFilter.includes(ActivityType.TRANSFER) &&
130
+ (isSpotTransferDelta(entry.delta) ||
131
+ isSendAssetDelta(entry.delta) ||
132
+ isCollateralTransferDelta(entry.delta) ||
133
+ isVaultTransferDelta(entry.delta))))
134
+ ) {
135
+ return []
136
+ }
84
137
  const item = mapLedgerEntry(
85
138
  entry,
86
139
  PROVIDER_KEY,
87
140
  timeParams.user,
141
+ assetRegistry,
88
142
  resolveMarket
89
143
  )
90
144
  return item === null ? [] : [item]
@@ -98,7 +152,20 @@ const fetchActivityData = async (
98
152
  }
99
153
  )
100
154
 
101
- const merged = [...ledgerItems, ...fundingItems].sort(
155
+ const ledgerLiquidationHashes = new Set(
156
+ ledgerUpdates
157
+ .filter((entry) => isLiquidationDelta(entry.delta))
158
+ .map((entry) => entry.hash)
159
+ )
160
+ const liquidationItems = mapLiquidationFills(
161
+ fills,
162
+ PROVIDER_KEY,
163
+ timeParams.user,
164
+ resolveMarket,
165
+ ledgerLiquidationHashes
166
+ )
167
+
168
+ const merged = [...ledgerItems, ...fundingItems, ...liquidationItems].sort(
102
169
  (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
103
170
  )
104
171
 
@@ -112,8 +179,8 @@ const fetchActivityData = async (
112
179
  /**
113
180
  * Fetch a chronological activity feed (deposits, withdrawals, transfers,
114
181
  * liquidations, funding) for `address`. Combines results from Hyperliquid's
115
- * `userNonFundingLedgerUpdates` and `userFunding` endpoints, sorted
116
- * newest-first.
182
+ * `userNonFundingLedgerUpdates`, `userFunding`, and `userFills` endpoints,
183
+ * sorted newest-first.
117
184
  *
118
185
  * Cursor-based pagination uses the ms-since-epoch timestamp of the last item
119
186
  * on the current page.
@@ -131,6 +198,13 @@ export const getActivity = async (
131
198
  if (needsMarkets(params.type)) {
132
199
  await registry.sync()
133
200
  }
201
+ const assetRegistry = getAssetRegistry(client, PROVIDER_KEY)
202
+ if (
203
+ params.type === undefined ||
204
+ params.type.some((type) => ASSET_BEARING_TYPES.has(type))
205
+ ) {
206
+ await assetRegistry.sync()
207
+ }
134
208
  const infoOpts = hlInfoOptions(client, options)
135
209
 
136
210
  const limit = Math.min(
@@ -152,6 +226,7 @@ export const getActivity = async (
152
226
  apiUrl,
153
227
  params.type,
154
228
  timeParams,
229
+ assetRegistry,
155
230
  (coin) => registry.get(coin),
156
231
  infoOpts
157
232
  )
@@ -12,7 +12,7 @@ import type {
12
12
  HlUserFills,
13
13
  HlUserFillsByTime,
14
14
  } from '../types/index.js'
15
- import { mapFill } from '../utils/index.js'
15
+ import { assetIsOutcome, mapFill } from '../utils/index.js'
16
16
  import { hlInfoOptions, infoRequest } from '../utils/infoClient.js'
17
17
 
18
18
  /**
@@ -112,6 +112,9 @@ export const getFills = async (
112
112
  // only its own row instead of rejecting the whole page. The registry warns
113
113
  // once per unresolved id. A delisted market still resolves, so its rows stay.
114
114
  const items = page.flatMap((f): Fill[] => {
115
+ if (assetIsOutcome(f.coin)) {
116
+ return []
117
+ }
115
118
  const market = registry.get(f.coin)
116
119
  return market === undefined ? [] : [mapFill(f, market)]
117
120
  })
@@ -8,8 +8,11 @@ import type { Order } from '@lifi/perps-types'
8
8
  import { PerpsErrorCode } from '@lifi/perps-types'
9
9
  import { PROVIDER_KEY } from '../constants.js'
10
10
  import type { HyperliquidContext } from '../context.js'
11
- import type { HlOrderStatusResponse } from '../types/index.js'
12
- import { mapOrder } from '../utils/index.js'
11
+ import type {
12
+ HlOrderStatusResponse,
13
+ HlTwapHistoryEntry,
14
+ } from '../types/index.js'
15
+ import { assetIsOutcome, mapOrder } from '../utils/index.js'
13
16
  import { hlInfoOptions, infoRequest } from '../utils/infoClient.js'
14
17
 
15
18
  /**
@@ -30,11 +33,15 @@ export const getOrder = async (
30
33
  params: GetOrderParams,
31
34
  options?: SDKRequestOptions
32
35
  ): Promise<Order> => {
33
- const oid = Number.parseInt(params.id, 10)
34
- if (Number.isNaN(oid)) {
36
+ const isClientId = /^0x[0-9a-fA-F]{32}$/.test(params.id)
37
+ const oid = isClientId ? params.id : Number(params.id)
38
+ if (
39
+ !isClientId &&
40
+ (!/^[0-9]+$/.test(params.id) || !Number.isSafeInteger(oid))
41
+ ) {
35
42
  const err = new PerpsError(
36
43
  PerpsErrorCode.ValidationError,
37
- `Invalid order ID: ${params.id}. Must be a numeric oid.`
44
+ `Invalid order ID: ${params.id}. Expected a numeric oid or a 128-bit cloid.`
38
45
  )
39
46
  err.tool = PROVIDER_KEY
40
47
  throw err
@@ -47,6 +54,23 @@ export const getOrder = async (
47
54
  )
48
55
 
49
56
  if (status.status !== 'order') {
57
+ const history = await infoRequest<HlTwapHistoryEntry[]>(
58
+ apiUrl,
59
+ { type: 'twapHistory', user: params.address },
60
+ hlInfoOptions(client, options)
61
+ )
62
+ const twap = history.find(
63
+ (entry) =>
64
+ String(entry.twapId) === params.id && !assetIsOutcome(entry.state.coin)
65
+ )
66
+ if (twap !== undefined) {
67
+ const registry = getMarketRegistry(client, PROVIDER_KEY)
68
+ await registry.sync()
69
+ return mapOrder(twap, registry.require(twap.state.coin))
70
+ }
71
+ }
72
+
73
+ if (status.status !== 'order' || assetIsOutcome(status.order.order.coin)) {
50
74
  const err = new PerpsError(
51
75
  PerpsErrorCode.OrderNotFound,
52
76
  `Order not found: ${params.id}`
@@ -1,102 +1,217 @@
1
- import { getMarketRegistry, type SDKRequestOptions } from '@lifi/perps-sdk'
2
- import type { OpenOrder, OrdersResponse, TriggerOrder } from '@lifi/perps-types'
3
- import type { Address } from 'viem'
1
+ import {
2
+ ACTIVE_ORDER_STATUSES,
3
+ createWarnOnce,
4
+ ExplorerChainId,
5
+ explorerTxUrl,
6
+ getMarketRegistry,
7
+ isActiveOrderStatus,
8
+ type MarketRegistry,
9
+ PerpsError,
10
+ type ProviderGetOrdersParams,
11
+ type SDKRequestOptions,
12
+ } from '@lifi/perps-sdk'
13
+ import type { MarketDisplay, Order, OrdersResponse } from '@lifi/perps-types'
4
14
  import { PROVIDER_KEY } from '../constants.js'
5
15
  import type { HyperliquidContext } from '../context.js'
6
16
  import type {
7
- HlFrontendOpenOrder,
17
+ HlExplorerTx,
8
18
  HlFrontendOpenOrders,
19
+ HlHistoricalOrders,
20
+ HlTwapHistoryEntry,
9
21
  } from '../types/index.js'
10
22
  import {
11
- isTriggerOrder,
12
- mapOpenOrder,
13
- mapTriggerOrder,
23
+ assetIsOutcome,
24
+ fetchUserTransactions,
25
+ mapOrder,
26
+ matchOrderActionHash,
14
27
  perpsDexNames,
15
28
  } from '../utils/index.js'
16
- import { hlInfoOptions, infoRequest } from '../utils/infoClient.js'
29
+ import {
30
+ hlInfoOptions,
31
+ type InfoRequestOptions,
32
+ infoRequest,
33
+ } from '../utils/infoClient.js'
34
+
35
+ /** Parameters for a lifecycle-filtered Hyperliquid order read. */
36
+ export type GetOrdersParams = ProviderGetOrdersParams
37
+
38
+ const warn = createWarnOnce()
39
+
40
+ const warnOnce = (key: string, detail?: string): void => {
41
+ warn(
42
+ key,
43
+ `[${PROVIDER_KEY}] ${detail === undefined ? key : `${key}: ${detail}`}`
44
+ )
45
+ }
17
46
 
18
47
  /**
19
- * Parameters for {@link getOrders}.
20
- *
21
- * @public
48
+ * Map one venue row, or drop it. An outcome market identity, a coin the backend
49
+ * market list does not hold, and a row the mapper rejects each drop only their
50
+ * own row instead of rejecting the whole page; each distinct mapper message
51
+ * warns once.
22
52
  */
23
- export interface GetOrdersParams {
24
- address: Address
25
- /** Optional filter using the normalized opaque `Market.id`. */
26
- marketId?: string
27
- /** Page-size hint; Hyperliquid returns all open orders in one response. */
28
- limit?: number
53
+ const mapRow = (
54
+ coin: string,
55
+ registry: MarketRegistry,
56
+ map: (market: MarketDisplay) => Order
57
+ ): Order | undefined => {
58
+ if (assetIsOutcome(coin)) {
59
+ return undefined
60
+ }
61
+ const market = registry.get(coin)
62
+ if (market === undefined) {
63
+ return undefined
64
+ }
65
+ try {
66
+ return map(market)
67
+ } catch (error) {
68
+ if (!(error instanceof PerpsError)) {
69
+ throw error
70
+ }
71
+ warnOnce(`dropped order row: ${error.message}`)
72
+ return undefined
73
+ }
29
74
  }
30
75
 
31
76
  /**
32
- * Fetch open + trigger orders across every supported perps sub-dex for
33
- * `address`, normalised into `OrdersResponse`. Trigger orders that appear as
34
- * children of a parent limit order (Hyperliquid's `normalTpsl` flow) are
35
- * extracted and surfaced alongside top-level trigger orders. The backend's
36
- * enriched asset list supplies the sub-dex fan-out and display fields; only
37
- * `frontendOpenOrders` is read direct.
38
- * @throws {PerpsError} On Hyperliquid REST error, network, or parsing failures.
39
- * @public
77
+ * Set `explorerLink` on each row whose placement transaction the explorer
78
+ * window still holds. One explorer read serves the whole page, and it is
79
+ * skipped when no row carries a client order id to match on. The link is
80
+ * supplementary, so an explorer failure warns and leaves the rows unlinked
81
+ * instead of failing the order read.
40
82
  */
83
+ const withExplorerLinks = async (
84
+ orders: Order[],
85
+ address: string,
86
+ options: InfoRequestOptions
87
+ ): Promise<Order[]> => {
88
+ if (!orders.some((order) => order.clientOrderId !== undefined)) {
89
+ return orders
90
+ }
91
+ let txs: HlExplorerTx[]
92
+ try {
93
+ txs = await fetchUserTransactions(address, options)
94
+ } catch (error) {
95
+ if (!(error instanceof PerpsError)) {
96
+ throw error
97
+ }
98
+ // The message stays out of the key: a transport failure names hosts and
99
+ // ports, so keying on it would let the dedupe set grow without bound.
100
+ warnOnce('explorer link lookup failed', error.message)
101
+ return orders
102
+ }
103
+ return orders.map((order) => {
104
+ const explorerLink = explorerTxUrl(
105
+ ExplorerChainId.HYPERLIQUID,
106
+ matchOrderActionHash(txs, order)
107
+ )
108
+ return explorerLink === undefined ? order : { ...order, explorerLink }
109
+ })
110
+ }
111
+
112
+ /** Read regular, trigger, and TWAP orders from the requested lifecycle feeds. */
41
113
  export const getOrders = async (
42
114
  { client, apiUrl }: HyperliquidContext,
43
115
  params: GetOrdersParams,
44
116
  options?: SDKRequestOptions
45
117
  ): Promise<OrdersResponse> => {
118
+ const statuses = params.statuses ?? [...ACTIVE_ORDER_STATUSES]
119
+ if (statuses.length === 0) {
120
+ return {
121
+ provider: PROVIDER_KEY,
122
+ orders: [],
123
+ pagination: { limit: params.limit ?? 0, hasMore: false },
124
+ }
125
+ }
46
126
  const registry = getMarketRegistry(client, PROVIDER_KEY)
47
127
  const markets = await registry.sync()
48
128
  const infoOpts = hlInfoOptions(client, options)
49
-
50
- const ordersResults = await Promise.all(
51
- perpsDexNames(markets).map((name) =>
52
- infoRequest<HlFrontendOpenOrders>(
53
- apiUrl,
54
- {
55
- type: 'frontendOpenOrders',
56
- user: params.address,
57
- ...(name ? { dex: name } : {}),
58
- },
59
- infoOpts
60
- )
129
+ const active = statuses.some(isActiveOrderStatus)
130
+ const terminal = statuses.some((status) => !isActiveOrderStatus(status))
131
+ const [open, historical, twaps] = await Promise.all([
132
+ active
133
+ ? Promise.all(
134
+ perpsDexNames(markets).map((name) =>
135
+ infoRequest<HlFrontendOpenOrders>(
136
+ apiUrl,
137
+ {
138
+ type: 'frontendOpenOrders',
139
+ user: params.address,
140
+ ...(name ? { dex: name } : {}),
141
+ },
142
+ infoOpts
143
+ )
144
+ )
145
+ )
146
+ : [],
147
+ terminal
148
+ ? infoRequest<HlHistoricalOrders>(
149
+ apiUrl,
150
+ { type: 'historicalOrders', user: params.address },
151
+ infoOpts
152
+ )
153
+ : [],
154
+ infoRequest<HlTwapHistoryEntry[]>(
155
+ apiUrl,
156
+ { type: 'twapHistory', user: params.address },
157
+ infoOpts
158
+ ),
159
+ ])
160
+ const rows = new Map<string, Order>()
161
+ const keep = (key: string, order: Order | undefined): void => {
162
+ if (order !== undefined) {
163
+ rows.set(key, order)
164
+ }
165
+ }
166
+ for (const detail of historical) {
167
+ const key = String(detail.order.oid)
168
+ // The feed carries one row per lifecycle transition, newest first, and a
169
+ // terminal row shares its timestamp with the `open` row beneath it, so
170
+ // only the first row of an id states that order's current status.
171
+ if (rows.has(key)) {
172
+ continue
173
+ }
174
+ keep(
175
+ key,
176
+ mapRow(detail.order.coin, registry, (market) => mapOrder(detail, market))
61
177
  )
178
+ }
179
+ const raw = open.flat()
180
+ const childIds = new Set(
181
+ raw.flatMap((order) => (order.children ?? []).map((child) => child.oid))
62
182
  )
63
-
64
- const raw: HlFrontendOpenOrder[] = ordersResults.flat()
65
-
66
- const childOids = new Set<number>()
67
- for (const o of raw) {
68
- for (const child of o.children ?? []) {
69
- childOids.add(child.oid)
183
+ for (const order of raw) {
184
+ if (!childIds.has(order.oid)) {
185
+ keep(
186
+ String(order.oid),
187
+ mapRow(order.coin, registry, (market) => mapOrder(order, market))
188
+ )
189
+ }
190
+ for (const child of order.children ?? []) {
191
+ keep(
192
+ String(child.oid),
193
+ mapRow(child.coin, registry, (market) =>
194
+ mapOrder(child, market, String(order.oid))
195
+ )
196
+ )
70
197
  }
71
198
  }
72
-
73
- const nonChild = raw.filter((o) => !childOids.has(o.oid))
74
-
75
- let openOrders: OpenOrder[] = nonChild
76
- .filter((o) => !isTriggerOrder(o))
77
- .map((o) => mapOpenOrder(o, registry.require(o.coin)))
78
-
79
- let triggerOrders: TriggerOrder[] = [
80
- ...nonChild
81
- .filter((o) => isTriggerOrder(o))
82
- .map((o) => mapTriggerOrder(o, registry.require(o.coin))),
83
- ...raw
84
- .filter((o) => childOids.has(o.oid))
85
- .map((o) => mapTriggerOrder(o, registry.require(o.coin))),
86
- ]
87
-
88
- if (params.marketId !== undefined) {
89
- openOrders = openOrders.filter((o) => o.market.id === params.marketId)
90
- triggerOrders = triggerOrders.filter((o) => o.market.id === params.marketId)
199
+ // TWAP ids and regular order ids occupy separate venue namespaces.
200
+ for (const twap of twaps) {
201
+ keep(
202
+ `twap:${twap.twapId}`,
203
+ mapRow(twap.state.coin, registry, (market) => mapOrder(twap, market))
204
+ )
91
205
  }
92
-
206
+ const matching = [...rows.values()].filter(
207
+ (order) =>
208
+ statuses.includes(order.status) &&
209
+ (params.marketId === undefined || order.market.id === params.marketId)
210
+ )
211
+ const orders = await withExplorerLinks(matching, params.address, infoOpts)
93
212
  return {
94
213
  provider: PROVIDER_KEY,
95
- openOrders,
96
- triggerOrders,
97
- pagination: {
98
- limit: params.limit ?? openOrders.length + triggerOrders.length,
99
- hasMore: false,
100
- },
214
+ orders,
215
+ pagination: { limit: params.limit ?? orders.length, hasMore: false },
101
216
  }
102
217
  }
@@ -9,6 +9,7 @@ import { PROVIDER_KEY } from '../constants.js'
9
9
  import type { HyperliquidContext } from '../context.js'
10
10
  import type { HlClearinghouseState } from '../types/index.js'
11
11
  import {
12
+ assetIsOutcome,
12
13
  isOpenAssetPosition,
13
14
  mapPosition,
14
15
  perpsDexNames,
@@ -61,7 +62,9 @@ export const getPositions = async (
61
62
 
62
63
  let positions = stateResults.flatMap((state) =>
63
64
  state.assetPositions
64
- .filter(isOpenAssetPosition)
65
+ .filter(
66
+ (ap) => !assetIsOutcome(ap.position.coin) && isOpenAssetPosition(ap)
67
+ )
65
68
  .map((ap) =>
66
69
  mapPosition(
67
70
  ap,