@lifi/perps-sdk-provider-hyperliquid 13.0.1 → 15.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 +120 -60
  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 +132 -84
  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 +172 -87
  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,5 +1,12 @@
1
+ import {
2
+ type AssetRegistry,
3
+ ExplorerChainId,
4
+ explorerTxUrl,
5
+ PerpsError,
6
+ } from '@lifi/perps-sdk'
1
7
  import type {
2
8
  ActivityItem,
9
+ Asset,
3
10
  DepositActivity,
4
11
  Fee,
5
12
  FundingActivity,
@@ -8,13 +15,20 @@ import type {
8
15
  TransferActivity,
9
16
  WithdrawalActivity,
10
17
  } from '@lifi/perps-types'
11
- import { ActivityType } from '@lifi/perps-types'
12
- import type { HlFundingUpdate, HlLedgerUpdate } from '../types/index.js'
18
+ import { ActivityType, PerpsErrorCode } from '@lifi/perps-types'
19
+ import Big from 'big.js'
20
+ import type {
21
+ HlFundingUpdate,
22
+ HlLedgerUpdate,
23
+ HlUserFill,
24
+ } from '../types/index.js'
13
25
  import {
26
+ isCollateralTransferDelta,
14
27
  isDepositDelta,
15
28
  isLiquidationDelta,
16
29
  isSendAssetDelta,
17
30
  isSpotTransferDelta,
31
+ isVaultTransferDelta,
18
32
  isWithdrawDelta,
19
33
  } from '../types/index.js'
20
34
 
@@ -30,34 +44,39 @@ const HL_COLLATERAL_SYMBOL = 'USDC'
30
44
  */
31
45
  const HL_NATIVE_TOKEN_SYMBOL = 'HYPE'
32
46
 
47
+ // Hyperliquid reserves spot token index 0 for USDC.
48
+ const HL_COLLATERAL_ASSET_ID = '0'
49
+
33
50
  /**
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.
51
+ * A ledger row moves a spot asset — a perp contract cannot be sent, received
52
+ * or withdrawn — so the delta's token symbol resolves inside the spot asset
53
+ * registry. The HIP-1 ticker auction keeps those symbols unique.
38
54
  */
39
- const ledgerTokenSymbol = (token: string): string => {
40
- const separator = token.indexOf(':')
41
- return separator === -1 ? token : token.slice(0, separator)
55
+ const resolveLedgerAsset = (symbol: string, registry: AssetRegistry): Asset => {
56
+ const asset = registry.assets.find(
57
+ (candidate) => candidate.displaySymbol === symbol
58
+ )
59
+ if (asset === undefined) {
60
+ const error = new PerpsError(
61
+ PerpsErrorCode.ValidationError,
62
+ `[hyperliquid] stale or mis-keyed asset registry: unknown spot symbol '${symbol}'`
63
+ )
64
+ error.tool = 'hyperliquid'
65
+ throw error
66
+ }
67
+ return asset
42
68
  }
43
69
 
44
70
  /**
45
- * Map a Hyperliquid non-funding ledger entry to an ActivityItem.
46
- *
47
- * Direction for `spotTransfer` and `sendAsset` is derived from
48
- * `queriedAddress` matching the delta's `user` (OUT) or `destination` (IN).
49
- * Returns null for unsupported delta types and for same-account moves, where
50
- * `user === destination === queriedAddress`.
51
- *
52
- * @param resolveMarket - Market identity for a venue coin, or `undefined` when
53
- * the backend market list does not hold it. A liquidated position the resolver
54
- * cannot identify is dropped.
71
+ * Map supported ledger entries; exclude same-account moves and unsupported types.
72
+ * Missing assets throw; unresolved liquidation markets omit only those positions.
55
73
  * @public
56
74
  */
57
75
  export const mapLedgerEntry = (
58
76
  entry: HlLedgerUpdate,
59
77
  providerKey: string,
60
78
  queriedAddress: string,
79
+ assetRegistry: AssetRegistry,
61
80
  resolveMarket: (coin: string) => MarketDisplay | undefined
62
81
  ): ActivityItem | null => {
63
82
  const { delta } = entry
@@ -67,93 +86,102 @@ export const mapLedgerEntry = (
67
86
  timestamp: new Date(entry.time).toISOString(),
68
87
  }
69
88
 
70
- // Handled before the switch: the catch-all arm of `HlLedgerDelta` is a
71
- // structural supertype of the concrete delta, so a `switch (delta.type)`
72
- // cannot narrow off the discriminant. The user-defined type guard does.
73
- if (isSpotTransferDelta(delta)) {
89
+ if (
90
+ isSpotTransferDelta(delta) ||
91
+ isSendAssetDelta(delta) ||
92
+ isCollateralTransferDelta(delta)
93
+ ) {
74
94
  const queried = queriedAddress.toLowerCase()
75
95
  const sender = delta.user.toLowerCase()
76
96
  const recipient = delta.destination.toLowerCase()
77
-
78
- // A transfer reports a movement between two accounts. A row whose sender
79
- // and recipient are the queried account moves nothing between accounts.
80
97
  if (sender === recipient && sender === queried) {
81
98
  return null
82
99
  }
83
-
84
100
  const direction: 'IN' | 'OUT' = queried === sender ? 'OUT' : 'IN'
85
- const counterpartyAddress = direction === 'OUT' ? recipient : sender
86
- const meta: Record<string, unknown> = {
87
- transferType: 'spotTransfer',
88
- }
89
- if (delta.usdcValue !== undefined) {
90
- meta.usdcValue = delta.usdcValue
91
- }
92
- if (delta.nonce !== undefined) {
93
- meta.nonce = delta.nonce
94
- }
95
101
  const fees: Fee[] = []
96
102
  if (delta.fee !== undefined) {
97
- fees.push({ amount: delta.fee, asset: HL_COLLATERAL_SYMBOL })
98
- }
99
- if (delta.nativeTokenFee !== undefined) {
100
103
  fees.push({
101
- amount: delta.nativeTokenFee,
102
- asset: HL_NATIVE_TOKEN_SYMBOL,
104
+ amount: delta.fee,
105
+ asset: isSendAssetDelta(delta)
106
+ ? delta.feeToken.split(':')[0]
107
+ : HL_COLLATERAL_SYMBOL,
103
108
  })
104
109
  }
110
+ if (
111
+ !isCollateralTransferDelta(delta) &&
112
+ delta.nativeTokenFee !== undefined
113
+ ) {
114
+ fees.push({ amount: delta.nativeTokenFee, asset: HL_NATIVE_TOKEN_SYMBOL })
115
+ }
116
+ const meta: Record<string, unknown> = {
117
+ transferType: isSendAssetDelta(delta) ? 'sendAsset' : delta.type,
118
+ }
119
+ if (!isCollateralTransferDelta(delta)) {
120
+ if (delta.usdcValue !== undefined) {
121
+ meta.usdcValue = delta.usdcValue
122
+ }
123
+ if (delta.nonce !== undefined) {
124
+ meta.nonce = delta.nonce
125
+ }
126
+ }
127
+ if (isSendAssetDelta(delta)) {
128
+ meta.sourceDex = delta.sourceDex
129
+ meta.destinationDex = delta.destinationDex
130
+ }
105
131
  return {
106
132
  ...base,
107
133
  type: ActivityType.TRANSFER,
108
134
  direction,
109
- counterpartyAddress,
110
- asset: ledgerTokenSymbol(delta.token),
111
- amount: delta.amount,
135
+ counterpartyAddress: direction === 'OUT' ? recipient : sender,
136
+ asset: isCollateralTransferDelta(delta)
137
+ ? assetRegistry.require(HL_COLLATERAL_ASSET_ID)
138
+ : resolveLedgerAsset(delta.token, assetRegistry),
139
+ amount: isCollateralTransferDelta(delta) ? delta.usdc : delta.amount,
112
140
  ...(fees.length === 0 ? {} : { fees }),
113
141
  meta,
114
- explorerLink: entry.hash
115
- ? `https://app.hyperliquid.xyz/explorer/tx/${entry.hash}`
116
- : undefined,
142
+ explorerLink: explorerTxUrl(ExplorerChainId.HYPERLIQUID, entry.hash),
117
143
  } satisfies TransferActivity
118
144
  }
119
145
 
120
- // `sendAsset` (wire `type === 'send'`) covers cross-user transfers
121
- // (modelled as TRANSFER) and same-user dex moves (returned as null —
122
- // overloading TRANSFER for those would lie about direction). Pre-switch
123
- // narrowing for the same reason as spotTransfer.
124
- if (isSendAssetDelta(delta)) {
146
+ if (isVaultTransferDelta(delta)) {
125
147
  const queried = queriedAddress.toLowerCase()
126
- const sender = delta.user.toLowerCase()
127
- const recipient = delta.destination.toLowerCase()
128
-
129
- if (sender === recipient && sender === queried) {
130
- return null
148
+ const vault = delta.vault.toLowerCase()
149
+ if (delta.type === 'vaultDeposit' && queried === vault) {
150
+ throw new PerpsError(
151
+ PerpsErrorCode.ValidationError,
152
+ 'Hyperliquid vaultDeposit identifies no depositor for the vault account'
153
+ )
131
154
  }
132
-
133
- const direction: 'IN' | 'OUT' = queried === sender ? 'OUT' : 'IN'
134
- const counterpartyAddress = direction === 'OUT' ? recipient : sender
135
- const meta: Record<string, unknown> = {
136
- transferType: 'sendAsset',
137
- sourceDex: delta.sourceDex,
138
- destinationDex: delta.destinationDex,
139
- usdcValue: delta.usdcValue,
140
- nonce: delta.nonce,
155
+ if (
156
+ delta.type === 'vaultWithdraw' &&
157
+ delta.user.toLowerCase() === vault &&
158
+ queried === vault
159
+ ) {
160
+ return null
141
161
  }
142
162
  return {
143
163
  ...base,
144
164
  type: ActivityType.TRANSFER,
145
- direction,
146
- counterpartyAddress,
147
- asset: ledgerTokenSymbol(delta.token),
148
- amount: delta.amount,
149
- fees: [
150
- { amount: delta.fee, asset: ledgerTokenSymbol(delta.feeToken) },
151
- { amount: delta.nativeTokenFee, asset: HL_NATIVE_TOKEN_SYMBOL },
152
- ],
153
- meta,
154
- explorerLink: entry.hash
155
- ? `https://app.hyperliquid.xyz/explorer/tx/${entry.hash}`
156
- : undefined,
165
+ direction:
166
+ delta.type === 'vaultDeposit' || queried === vault ? 'OUT' : 'IN',
167
+ counterpartyAddress:
168
+ delta.type === 'vaultWithdraw' && queried === vault
169
+ ? delta.user.toLowerCase()
170
+ : vault,
171
+ asset: assetRegistry.require(HL_COLLATERAL_ASSET_ID),
172
+ amount:
173
+ delta.type === 'vaultDeposit' ? delta.usdc : delta.netWithdrawnUsd,
174
+ meta:
175
+ delta.type === 'vaultDeposit'
176
+ ? { transferType: delta.type }
177
+ : {
178
+ transferType: delta.type,
179
+ requestedUsd: delta.requestedUsd,
180
+ commission: delta.commission,
181
+ closingCost: delta.closingCost,
182
+ basis: delta.basis,
183
+ },
184
+ explorerLink: explorerTxUrl(ExplorerChainId.HYPERLIQUID, entry.hash),
157
185
  } satisfies TransferActivity
158
186
  }
159
187
 
@@ -163,11 +191,9 @@ export const mapLedgerEntry = (
163
191
  return {
164
192
  ...base,
165
193
  type: ActivityType.DEPOSIT,
166
- asset: HL_COLLATERAL_SYMBOL,
194
+ asset: assetRegistry.require(HL_COLLATERAL_ASSET_ID),
167
195
  amount: delta.usdc,
168
- explorerLink: entry.hash
169
- ? `https://scan.li.fi/tx/${entry.hash}`
170
- : undefined,
196
+ explorerLink: explorerTxUrl(ExplorerChainId.HYPERLIQUID, entry.hash),
171
197
  } satisfies DepositActivity
172
198
  }
173
199
 
@@ -175,14 +201,12 @@ export const mapLedgerEntry = (
175
201
  return {
176
202
  ...base,
177
203
  type: ActivityType.WITHDRAWAL,
178
- asset: HL_COLLATERAL_SYMBOL,
204
+ asset: assetRegistry.require(HL_COLLATERAL_ASSET_ID),
179
205
  amount: delta.usdc,
180
206
  ...(delta.fee === undefined
181
207
  ? {}
182
208
  : { fee: { amount: delta.fee, asset: HL_COLLATERAL_SYMBOL } }),
183
- explorerLink: entry.hash
184
- ? `https://scan.li.fi/tx/${entry.hash}`
185
- : undefined,
209
+ explorerLink: explorerTxUrl(ExplorerChainId.HYPERLIQUID, entry.hash),
186
210
  } satisfies WithdrawalActivity
187
211
  }
188
212
 
@@ -247,3 +271,64 @@ export const mapFundingActivity = (
247
271
  fundingRate: entry.delta.fundingRate,
248
272
  }
249
273
  }
274
+
275
+ /**
276
+ * Build one liquidation activity per liquidation order from the fills of the
277
+ * liquidated account. Hyperliquid attaches `liquidation` to the fills of both
278
+ * parties, so only fills whose `liquidatedUser` is the queried address count.
279
+ * A fill whose hash a ledger liquidation row already carries is the same
280
+ * event and is skipped. The size sign follows the closed position: a sell
281
+ * fill closes a long. Groups whose market does not resolve are dropped.
282
+ * @public
283
+ */
284
+ export const mapLiquidationFills = (
285
+ fills: HlUserFill[],
286
+ providerKey: string,
287
+ queriedAddress: string,
288
+ resolveMarket: (coin: string) => MarketDisplay | undefined,
289
+ ledgerHashes: ReadonlySet<string>
290
+ ): LiquidationActivity[] => {
291
+ const queried = queriedAddress.toLowerCase()
292
+ const byOrder = new Map<number, HlUserFill[]>()
293
+ for (const fill of fills) {
294
+ if (
295
+ fill.liquidation?.liquidatedUser.toLowerCase() !== queried ||
296
+ (fill.hash !== undefined && ledgerHashes.has(fill.hash))
297
+ ) {
298
+ continue
299
+ }
300
+ const group = byOrder.get(fill.oid) ?? []
301
+ group.push(fill)
302
+ byOrder.set(fill.oid, group)
303
+ }
304
+ return [...byOrder.entries()].flatMap(([oid, group]) => {
305
+ const [first] = group
306
+ const market = resolveMarket(first.coin)
307
+ if (market === undefined) {
308
+ return []
309
+ }
310
+ let size = new Big(0)
311
+ let notional = new Big(0)
312
+ let time = first.time
313
+ for (const fill of group) {
314
+ size = size.plus(fill.sz)
315
+ notional = notional.plus(new Big(fill.px).times(fill.sz))
316
+ time = Math.max(time, fill.time)
317
+ }
318
+ return [
319
+ {
320
+ id: `liquidation:${oid}`,
321
+ provider: providerKey,
322
+ timestamp: new Date(time).toISOString(),
323
+ type: ActivityType.LIQUIDATION,
324
+ liquidatedNotionalPosition: notional.toFixed(),
325
+ liquidatedPositions: [
326
+ {
327
+ market,
328
+ size: (first.side === 'A' ? size : size.neg()).toFixed(),
329
+ },
330
+ ],
331
+ } satisfies LiquidationActivity,
332
+ ]
333
+ })
334
+ }
@@ -6,7 +6,6 @@ import {
6
6
  import type { Fill, MarketDisplay } from '@lifi/perps-types'
7
7
  import {
8
8
  FillClassification,
9
- FillStatus,
10
9
  LiquidityRole,
11
10
  OrderSide,
12
11
  OrderType,
@@ -42,7 +41,6 @@ export const mapFill = (fill: HlUserFill, market: MarketDisplay): Fill => {
42
41
  type: fill.crossed ? undefined : OrderType.LIMIT,
43
42
  size: fill.sz,
44
43
  price: fill.px,
45
- status: FillStatus.FILLED,
46
44
  liquidity: fill.crossed ? LiquidityRole.TAKER : LiquidityRole.MAKER,
47
45
  filledSize: fill.sz,
48
46
  fee: {