@lifi/perps-sdk-provider-hyperliquid 19.0.0 → 20.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 (63) hide show
  1. package/README.md +11 -0
  2. package/dist/cjs/HyperliquidProvider.js.map +1 -1
  3. package/dist/cjs/services/getAccount.d.ts.map +1 -1
  4. package/dist/cjs/services/getAccount.js +12 -8
  5. package/dist/cjs/services/getAccount.js.map +1 -1
  6. package/dist/cjs/services/getActivity.d.ts.map +1 -1
  7. package/dist/cjs/services/getActivity.js +3 -1
  8. package/dist/cjs/services/getActivity.js.map +1 -1
  9. package/dist/cjs/services/getMarketSettings.d.ts +2 -2
  10. package/dist/cjs/services/getMarketSettings.d.ts.map +1 -1
  11. package/dist/cjs/services/getMarketSettings.js +8 -3
  12. package/dist/cjs/services/getMarketSettings.js.map +1 -1
  13. package/dist/cjs/types/ledger.d.ts +17 -1
  14. package/dist/cjs/types/ledger.d.ts.map +1 -1
  15. package/dist/cjs/types/ledger.js +10 -1
  16. package/dist/cjs/types/ledger.js.map +1 -1
  17. package/dist/cjs/utils/mapActivity.d.ts.map +1 -1
  18. package/dist/cjs/utils/mapActivity.js +41 -0
  19. package/dist/cjs/utils/mapActivity.js.map +1 -1
  20. package/dist/cjs/utils/withdrawableBalances.d.ts.map +1 -1
  21. package/dist/cjs/utils/withdrawableBalances.js +3 -1
  22. package/dist/cjs/utils/withdrawableBalances.js.map +1 -1
  23. package/dist/esm/HyperliquidProvider.js.map +1 -1
  24. package/dist/esm/services/getAccount.d.ts.map +1 -1
  25. package/dist/esm/services/getAccount.js +10 -9
  26. package/dist/esm/services/getAccount.js.map +1 -1
  27. package/dist/esm/services/getActivity.d.ts.map +1 -1
  28. package/dist/esm/services/getActivity.js +4 -2
  29. package/dist/esm/services/getActivity.js.map +1 -1
  30. package/dist/esm/services/getMarketSettings.d.ts +5 -3
  31. package/dist/esm/services/getMarketSettings.d.ts.map +1 -1
  32. package/dist/esm/services/getMarketSettings.js +13 -7
  33. package/dist/esm/services/getMarketSettings.js.map +1 -1
  34. package/dist/esm/types/ledger.d.ts +42 -1
  35. package/dist/esm/types/ledger.d.ts.map +1 -1
  36. package/dist/esm/types/ledger.js +17 -0
  37. package/dist/esm/types/ledger.js.map +1 -1
  38. package/dist/esm/utils/mapActivity.d.ts +1 -0
  39. package/dist/esm/utils/mapActivity.d.ts.map +1 -1
  40. package/dist/esm/utils/mapActivity.js +49 -1
  41. package/dist/esm/utils/mapActivity.js.map +1 -1
  42. package/dist/esm/utils/withdrawableBalances.d.ts +2 -1
  43. package/dist/esm/utils/withdrawableBalances.d.ts.map +1 -1
  44. package/dist/esm/utils/withdrawableBalances.js +5 -2
  45. package/dist/esm/utils/withdrawableBalances.js.map +1 -1
  46. package/dist/types/services/getAccount.d.ts.map +1 -1
  47. package/dist/types/services/getActivity.d.ts.map +1 -1
  48. package/dist/types/services/getMarketSettings.d.ts +5 -3
  49. package/dist/types/services/getMarketSettings.d.ts.map +1 -1
  50. package/dist/types/types/ledger.d.ts +42 -1
  51. package/dist/types/types/ledger.d.ts.map +1 -1
  52. package/dist/types/utils/mapActivity.d.ts +1 -0
  53. package/dist/types/utils/mapActivity.d.ts.map +1 -1
  54. package/dist/types/utils/withdrawableBalances.d.ts +2 -1
  55. package/dist/types/utils/withdrawableBalances.d.ts.map +1 -1
  56. package/package.json +3 -3
  57. package/src/HyperliquidProvider.ts +1 -1
  58. package/src/services/getAccount.ts +22 -12
  59. package/src/services/getActivity.ts +5 -1
  60. package/src/services/getMarketSettings.ts +20 -8
  61. package/src/types/ledger.ts +61 -0
  62. package/src/utils/mapActivity.ts +56 -0
  63. package/src/utils/withdrawableBalances.ts +8 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/perps-sdk-provider-hyperliquid",
3
- "version": "19.0.0",
3
+ "version": "20.0.0",
4
4
  "description": "Hyperliquid provider plugin for @lifi/perps-sdk",
5
5
  "homepage": "https://github.com/lifinance/perps-sdk",
6
6
  "bugs": {
@@ -49,10 +49,10 @@
49
49
  "dependencies": {
50
50
  "big.js": "^7.0.1",
51
51
  "viem": "^2.55.18",
52
- "@lifi/perps-types": "^17.0.0"
52
+ "@lifi/perps-types": "^18.0.0"
53
53
  },
54
54
  "peerDependencies": {
55
- "@lifi/perps-sdk": "^19.0.0"
55
+ "@lifi/perps-sdk": "^20.0.0"
56
56
  },
57
57
  "publishConfig": {
58
58
  "access": "public"
@@ -363,7 +363,7 @@ export function hyperliquidProvider(
363
363
  getMarketSettings: (
364
364
  params: ProviderGetMarketSettingsParams,
365
365
  opts?: SDKRequestOptions
366
- ): Promise<MarketSettings | undefined> =>
366
+ ): Promise<MarketSettings> =>
367
367
  getMarketSettings(
368
368
  contextRef.require(),
369
369
  { address: params.address, market: params.market },
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  getMarketRegistry,
3
3
  getMarketsContext,
4
+ maxOf,
5
+ minOf,
4
6
  type ProviderGetAccountParams,
5
7
  type SDKRequestOptions,
6
8
  stringToFloat,
@@ -14,7 +16,7 @@ import type {
14
16
  HyperliquidDexAccountState,
15
17
  Position,
16
18
  } from '@lifi/perps-types'
17
- import type Big from 'big.js'
19
+ import Big from 'big.js'
18
20
  import { PROVIDER_KEY } from '../constants.js'
19
21
  import type { HyperliquidContext } from '../context.js'
20
22
  import type {
@@ -66,13 +68,6 @@ interface BalancePartition {
66
68
  collateralBalances: Balance[]
67
69
  }
68
70
 
69
- const transferableWithin = (venueFigure: Big, units: Big): string => {
70
- if (venueFigure.lt(0)) {
71
- return '0'
72
- }
73
- return (venueFigure.gt(units) ? units : venueFigure).toFixed()
74
- }
75
-
76
71
  const buildBalances = (
77
72
  abstraction: HlAbstractionMode | null,
78
73
  spotState: HlSpotClearinghouseState,
@@ -84,7 +79,19 @@ const buildBalances = (
84
79
  const { balances, collateralBalances } = partitionSpotBalances(
85
80
  spotState.balances
86
81
  .filter((b) => !assetIsOutcome(b.coin))
87
- .map((b) => spotBalance(spotAssetFromToken(b), b.total, priceById)),
82
+ .map((b) => {
83
+ const total = toWireBig(b.total, 'spotClearinghouseState.total')
84
+ return {
85
+ ...spotBalance(spotAssetFromToken(b), b.total, priceById),
86
+ transferable: minOf(
87
+ maxOf(
88
+ total.minus(toWireBig(b.hold, 'spotClearinghouseState.hold')),
89
+ new Big(0)
90
+ ),
91
+ total
92
+ ).toFixed(),
93
+ }
94
+ }),
88
95
  quoteAssetIds
89
96
  )
90
97
 
@@ -107,10 +114,13 @@ const buildBalances = (
107
114
  units: value.toFixed(),
108
115
  valueUsd: value.toFixed(),
109
116
  price: '1',
110
- transferable: transferableWithin(
111
- toWireBig(state.withdrawable, 'clearinghouseState.withdrawable'),
117
+ transferable: minOf(
118
+ maxOf(
119
+ toWireBig(state.withdrawable, 'clearinghouseState.withdrawable'),
120
+ new Big(0)
121
+ ),
112
122
  value
113
- ),
123
+ ).toFixed(),
114
124
  })
115
125
  }
116
126
  }
@@ -24,7 +24,9 @@ import type {
24
24
  HlUserNonFundingLedgerUpdates,
25
25
  } from '../types/index.js'
26
26
  import {
27
+ isBorrowLendDelta,
27
28
  isCollateralTransferDelta,
29
+ isCStakingTransferDelta,
28
30
  isDepositDelta,
29
31
  isLiquidationDelta,
30
32
  isSendAssetDelta,
@@ -130,7 +132,9 @@ const fetchActivityData = async (
130
132
  (isSpotTransferDelta(entry.delta) ||
131
133
  isSendAssetDelta(entry.delta) ||
132
134
  isCollateralTransferDelta(entry.delta) ||
133
- isVaultTransferDelta(entry.delta))))
135
+ isVaultTransferDelta(entry.delta) ||
136
+ isCStakingTransferDelta(entry.delta) ||
137
+ isBorrowLendDelta(entry.delta))))
134
138
  ) {
135
139
  return []
136
140
  }
@@ -1,11 +1,12 @@
1
- import type { SDKRequestOptions } from '@lifi/perps-sdk'
1
+ import { PerpsError, type SDKRequestOptions } from '@lifi/perps-sdk'
2
2
  import {
3
3
  MarginMode,
4
4
  type MarketRef,
5
5
  type MarketSettings,
6
+ PerpsErrorCode,
6
7
  } from '@lifi/perps-types'
7
8
  import type { Address } from 'viem'
8
- import { SPOT_MARKET_ID } from '../constants.js'
9
+ import { PROVIDER_KEY, SPOT_MARKET_ID } from '../constants.js'
9
10
  import type { HyperliquidContext } from '../context.js'
10
11
  import { fetchActiveAssetData } from './activeAssetData.js'
11
12
 
@@ -22,17 +23,23 @@ export interface GetMarketSettingsParams {
22
23
  /**
23
24
  * The user's venue-stored margin mode and leverage for one perps market,
24
25
  * read from `activeAssetData` — present whether or not a position is open.
25
- * @throws {PerpsError} On Hyperliquid REST error, network, or parsing failures.
26
+ * @throws {PerpsError} `ValidationError` for a spot market; `SDKError` when
27
+ * the response carries no positive leverage; any other `PerpsError` on
28
+ * Hyperliquid REST error, network, or parsing failures.
26
29
  * @public
27
30
  */
28
31
  export const getMarketSettings = async (
29
32
  context: HyperliquidContext,
30
33
  params: GetMarketSettingsParams,
31
34
  options?: SDKRequestOptions
32
- ): Promise<MarketSettings | undefined> => {
33
- // Spot markets carry no leverage state on the venue.
35
+ ): Promise<MarketSettings> => {
34
36
  if (params.market.categoryId === SPOT_MARKET_ID) {
35
- return undefined
37
+ const error = new PerpsError(
38
+ PerpsErrorCode.ValidationError,
39
+ `Hyperliquid market '${params.market.marketId}' is a spot market and carries no leverage setting`
40
+ )
41
+ error.tool = PROVIDER_KEY
42
+ throw error
36
43
  }
37
44
  const data = await fetchActiveAssetData(
38
45
  context,
@@ -41,8 +48,13 @@ export const getMarketSettings = async (
41
48
  options
42
49
  )
43
50
  const leverage = data.leverage
44
- if (!leverage) {
45
- return undefined
51
+ if (!leverage || !(leverage.value > 0)) {
52
+ const error = new PerpsError(
53
+ PerpsErrorCode.SDKError,
54
+ `Hyperliquid returned no leverage for market '${params.market.marketId}'`
55
+ )
56
+ error.tool = PROVIDER_KEY
57
+ throw error
46
58
  }
47
59
  return {
48
60
  marginMode:
@@ -130,6 +130,38 @@ export type HlLiquidationDelta = {
130
130
  liquidatedPositions?: HlLiquidatedPosition[]
131
131
  }
132
132
 
133
+ /**
134
+ * Hyperliquid `cStakingTransfer` ledger delta. Moves `token` between the
135
+ * account's spot balance and its staking balance; `isDeposit` is `true` for a
136
+ * move into staking. `amount` is unsigned. A completed unstake row carries the
137
+ * zero hash.
138
+ * @public
139
+ */
140
+ export type HlCStakingTransferDelta = {
141
+ type: 'cStakingTransfer'
142
+ token: string
143
+ amount: string
144
+ isDeposit: boolean
145
+ }
146
+
147
+ /** Borrow-lend market action named by a `borrowLend` ledger delta. @public */
148
+ export type HlBorrowLendOperation = 'supply' | 'withdraw' | 'borrow' | 'repay'
149
+
150
+ /**
151
+ * Hyperliquid `borrowLend` ledger delta. Moves `token` between the account's
152
+ * spot balance and the borrow-lend market; `operation` gives the direction and
153
+ * `amount` is unsigned. `interestAmount` is the accrued interest that
154
+ * `amount` includes on a `withdraw` or `repay`, and `"0.0"` otherwise.
155
+ * @public
156
+ */
157
+ export type HlBorrowLendDelta = {
158
+ type: 'borrowLend'
159
+ token: string
160
+ operation: HlBorrowLendOperation
161
+ amount: string
162
+ interestAmount: string
163
+ }
164
+
133
165
  /**
134
166
  * Union of known Hyperliquid non-funding ledger deltas plus an open fallback
135
167
  * for endpoint variants the provider does not map.
@@ -143,6 +175,8 @@ export type HlLedgerDelta =
143
175
  | HlDepositDelta
144
176
  | HlWithdrawDelta
145
177
  | HlLiquidationDelta
178
+ | HlCStakingTransferDelta
179
+ | HlBorrowLendDelta
146
180
  | {
147
181
  type: string
148
182
  usdc?: string
@@ -202,6 +236,33 @@ export const isLiquidationDelta = (
202
236
  delta: HlLedgerDelta
203
237
  ): delta is HlLiquidationDelta => delta.type === 'liquidation'
204
238
 
239
+ /**
240
+ * Type guard for `HlCStakingTransferDelta`. `isDeposit` is part of the guard:
241
+ * a row without a boolean `isDeposit` names no direction for the movement.
242
+ * @public
243
+ */
244
+ export const isCStakingTransferDelta = (
245
+ delta: HlLedgerDelta
246
+ ): delta is HlCStakingTransferDelta =>
247
+ delta.type === 'cStakingTransfer' &&
248
+ 'isDeposit' in delta &&
249
+ typeof delta.isDeposit === 'boolean'
250
+
251
+ const HL_BORROW_LEND_OPERATIONS: ReadonlySet<unknown> =
252
+ new Set<HlBorrowLendOperation>(['supply', 'withdraw', 'borrow', 'repay'])
253
+
254
+ /**
255
+ * Type guard for `HlBorrowLendDelta`. The operation is part of the guard: an
256
+ * unknown operation names no direction for the movement.
257
+ * @public
258
+ */
259
+ export const isBorrowLendDelta = (
260
+ delta: HlLedgerDelta
261
+ ): delta is HlBorrowLendDelta =>
262
+ delta.type === 'borrowLend' &&
263
+ 'operation' in delta &&
264
+ HL_BORROW_LEND_OPERATIONS.has(delta.operation)
265
+
205
266
  /**
206
267
  * One timestamped non-funding ledger update. `time` is milliseconds since
207
268
  * epoch and `hash` is the upstream transaction identifier.
@@ -17,13 +17,17 @@ import type {
17
17
  } from '@lifi/perps-types'
18
18
  import { ActivityType, PerpsErrorCode } from '@lifi/perps-types'
19
19
  import Big from 'big.js'
20
+ import { zeroHash } from 'viem'
20
21
  import type {
22
+ HlBorrowLendOperation,
21
23
  HlFundingUpdate,
22
24
  HlLedgerUpdate,
23
25
  HlUserFill,
24
26
  } from '../types/index.js'
25
27
  import {
28
+ isBorrowLendDelta,
26
29
  isCollateralTransferDelta,
30
+ isCStakingTransferDelta,
27
31
  isDepositDelta,
28
32
  isLiquidationDelta,
29
33
  isSendAssetDelta,
@@ -47,6 +51,17 @@ const HL_NATIVE_TOKEN_SYMBOL = 'HYPE'
47
51
  // Hyperliquid reserves spot token index 0 for USDC.
48
52
  const HL_COLLATERAL_ASSET_ID = '0'
49
53
 
54
+ /**
55
+ * Direction relative to the account's spot balance, the same frame as an
56
+ * outbound `vaultDeposit`.
57
+ */
58
+ const BORROW_LEND_DIRECTION: Record<HlBorrowLendOperation, 'IN' | 'OUT'> = {
59
+ supply: 'OUT',
60
+ withdraw: 'IN',
61
+ borrow: 'IN',
62
+ repay: 'OUT',
63
+ }
64
+
50
65
  /**
51
66
  * A ledger row moves a spot asset — a perp contract cannot be sent, received
52
67
  * or withdrawn — so the delta's token symbol resolves inside the spot asset
@@ -69,6 +84,7 @@ const resolveLedgerAsset = (symbol: string, registry: AssetRegistry): Asset => {
69
84
 
70
85
  /**
71
86
  * Map supported ledger entries; exclude same-account moves and unsupported types.
87
+ * Staking and borrow-lend moves map with the queried account as counterparty.
72
88
  * Missing assets throw; unresolved liquidation markets omit only those positions.
73
89
  * @public
74
90
  */
@@ -143,6 +159,46 @@ export const mapLedgerEntry = (
143
159
  } satisfies TransferActivity
144
160
  }
145
161
 
162
+ if (isCStakingTransferDelta(delta) || isBorrowLendDelta(delta)) {
163
+ // A completed unstake is a system event with the zero hash and no venue
164
+ // transaction, so the id is synthesized and no explorer link applies.
165
+ const hasTransaction = entry.hash !== zeroHash
166
+ const direction = isCStakingTransferDelta(delta)
167
+ ? delta.isDeposit
168
+ ? 'OUT'
169
+ : 'IN'
170
+ : BORROW_LEND_DIRECTION[delta.operation]
171
+ const action = isCStakingTransferDelta(delta) ? direction : delta.operation
172
+ return {
173
+ ...base,
174
+ ...(hasTransaction
175
+ ? {}
176
+ : {
177
+ id: `${delta.type}:${delta.token}:${action}:${delta.amount}:${base.timestamp}`,
178
+ }),
179
+ type: ActivityType.TRANSFER,
180
+ direction,
181
+ counterpartyAddress: queriedAddress.toLowerCase(),
182
+ asset: resolveLedgerAsset(delta.token, assetRegistry),
183
+ amount: delta.amount,
184
+ meta: isCStakingTransferDelta(delta)
185
+ ? { transferType: delta.type }
186
+ : {
187
+ transferType: delta.type,
188
+ operation: delta.operation,
189
+ interestAmount: delta.interestAmount,
190
+ },
191
+ ...(hasTransaction
192
+ ? {
193
+ explorerLink: explorerTxUrl(
194
+ ExplorerChainId.HYPERLIQUID,
195
+ entry.hash
196
+ ),
197
+ }
198
+ : {}),
199
+ } satisfies TransferActivity
200
+ }
201
+
146
202
  if (isVaultTransferDelta(delta)) {
147
203
  const queried = queriedAddress.toLowerCase()
148
204
  const vault = delta.vault.toLowerCase()
@@ -17,7 +17,8 @@ import { toWireBig } from './decimal.js'
17
17
  *
18
18
  * @param quoteAssetId - `Asset.id` the perps-route row is keyed by.
19
19
  * @param withdrawalFee - Flat venue fee in quote-asset units, set on every
20
- * quote-asset row. Absent leaves every row without a fee.
20
+ * quote-asset row. Hyperliquid deducts it from the requested amount. Absent
21
+ * leaves every row without a fee.
21
22
  * @public
22
23
  */
23
24
  export const hyperliquidWithdrawableBalances = (
@@ -32,8 +33,12 @@ export const hyperliquidWithdrawableBalances = (
32
33
  withdrawalFee === undefined
33
34
  ? undefined
34
35
  : toWireBig(withdrawalFee, 'providers.withdrawalFeeUsd').toFixed()
35
- const feeFor = (assetId: string) =>
36
- fee === undefined || assetId !== quoteAssetId ? {} : { withdrawalFee: fee }
36
+ const feeFor = (
37
+ assetId: string
38
+ ): Pick<ProviderWithdrawableBalance, 'withdrawalFee' | 'isFeeDeducted'> =>
39
+ fee === undefined || assetId !== quoteAssetId
40
+ ? {}
41
+ : { withdrawalFee: fee, isFeeDeducted: true }
37
42
 
38
43
  if (isUnifiedAbstraction(abstraction)) {
39
44
  for (const balance of spotState.balances) {