@lifi/perps-sdk-provider-hyperliquid 1.4.0 → 1.5.2

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 (47) hide show
  1. package/dist/cjs/accountSummary.d.ts.map +1 -1
  2. package/dist/cjs/accountSummary.js +5 -3
  3. package/dist/cjs/accountSummary.js.map +1 -1
  4. package/dist/cjs/services/getAccount.d.ts.map +1 -1
  5. package/dist/cjs/services/getAccount.js +4 -4
  6. package/dist/cjs/services/getAccount.js.map +1 -1
  7. package/dist/cjs/types/ws.d.ts +8 -0
  8. package/dist/cjs/types/ws.d.ts.map +1 -1
  9. package/dist/cjs/utils/mapOrder.d.ts.map +1 -1
  10. package/dist/cjs/utils/mapOrder.js +12 -5
  11. package/dist/cjs/utils/mapOrder.js.map +1 -1
  12. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts +2 -0
  13. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  14. package/dist/cjs/websocket/HyperliquidWsProvider.js +85 -13
  15. package/dist/cjs/websocket/HyperliquidWsProvider.js.map +1 -1
  16. package/dist/esm/accountSummary.d.ts +2 -2
  17. package/dist/esm/accountSummary.d.ts.map +1 -1
  18. package/dist/esm/accountSummary.js +12 -9
  19. package/dist/esm/accountSummary.js.map +1 -1
  20. package/dist/esm/services/getAccount.d.ts.map +1 -1
  21. package/dist/esm/services/getAccount.js +10 -7
  22. package/dist/esm/services/getAccount.js.map +1 -1
  23. package/dist/esm/types/ws.d.ts +10 -0
  24. package/dist/esm/types/ws.d.ts.map +1 -1
  25. package/dist/esm/utils/mapOrder.d.ts +11 -1
  26. package/dist/esm/utils/mapOrder.d.ts.map +1 -1
  27. package/dist/esm/utils/mapOrder.js +23 -6
  28. package/dist/esm/utils/mapOrder.js.map +1 -1
  29. package/dist/esm/websocket/HyperliquidWsProvider.d.ts +2 -0
  30. package/dist/esm/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  31. package/dist/esm/websocket/HyperliquidWsProvider.js +85 -13
  32. package/dist/esm/websocket/HyperliquidWsProvider.js.map +1 -1
  33. package/dist/types/accountSummary.d.ts +2 -2
  34. package/dist/types/accountSummary.d.ts.map +1 -1
  35. package/dist/types/services/getAccount.d.ts.map +1 -1
  36. package/dist/types/types/ws.d.ts +10 -0
  37. package/dist/types/types/ws.d.ts.map +1 -1
  38. package/dist/types/utils/mapOrder.d.ts +11 -1
  39. package/dist/types/utils/mapOrder.d.ts.map +1 -1
  40. package/dist/types/websocket/HyperliquidWsProvider.d.ts +2 -0
  41. package/dist/types/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  42. package/package.json +3 -3
  43. package/src/accountSummary.ts +12 -9
  44. package/src/services/getAccount.ts +10 -11
  45. package/src/types/ws.ts +12 -0
  46. package/src/utils/mapOrder.ts +23 -6
  47. package/src/websocket/HyperliquidWsProvider.ts +108 -13
@@ -1 +1 @@
1
- {"version":3,"file":"mapOrder.d.ts","sourceRoot":"","sources":["../../../src/utils/mapOrder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,KAAK,EACL,YAAY,EACb,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAEL,WAAW,EACX,SAAS,EAEV,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAE3E;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,KAAG,SAehD,CAAA;AAED,cAAc;AACd,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,KAAG,OAIjB,CAAA;AAE/B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,cAAc,GACzB,GAAG,mBAAmB,GAAG,aAAa,CAAC,OAAO,CAAC,KAC9C,OAkBF,CAAA;AAED,cAAc;AACd,eAAO,MAAM,YAAY,GACvB,GAAG,mBAAmB,EACtB,QAAQ,aAAa,KACpB,SAaD,CAAA;AAEF,cAAc;AACd,eAAO,MAAM,eAAe,GAC1B,GAAG,mBAAmB,EACtB,QAAQ,aAAa,KACpB,YAcF,CAAA;AAED,cAAc;AACd,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,KAAG,WAmB/C,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAAM,GAAG,SAuBzD,CAAA;AAeD,cAAc;AACd,eAAO,MAAM,QAAQ,GACnB,QAAQ,aAAa,EACrB,QAAQ,aAAa,KACpB,KAsBF,CAAA"}
1
+ {"version":3,"file":"mapOrder.d.ts","sourceRoot":"","sources":["../../../src/utils/mapOrder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,KAAK,EACL,YAAY,EACb,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAEL,WAAW,EACX,SAAS,EAEV,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAE3E;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,KAAG,SAehD,CAAA;AAED,cAAc;AACd,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,KAAG,OAIjB,CAAA;AAE/B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,cAAc,GACzB,GAAG,mBAAmB,GAAG,aAAa,CAAC,OAAO,CAAC,KAC9C,OAkBF,CAAA;AAED,cAAc;AACd,eAAO,MAAM,YAAY,GACvB,GAAG,mBAAmB,EACtB,QAAQ,aAAa,KACpB,SAaD,CAAA;AAEF,cAAc;AACd,eAAO,MAAM,eAAe,GAC1B,GAAG,mBAAmB,EACtB,QAAQ,aAAa,KACpB,YAcF,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,KAAG,WAwB/C,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAAM,GAAG,SAyBzD,CAAA;AAeD,cAAc;AACd,eAAO,MAAM,QAAQ,GACnB,QAAQ,aAAa,EACrB,QAAQ,aAAa,KACpB,KAsBF,CAAA"}
@@ -80,6 +80,8 @@ export declare class HyperliquidWsProvider extends WsProviderBase<object> {
80
80
  * + mark overlaid from `fastAssetCtxs`.
81
81
  */
82
82
  private emitMarketsContext;
83
+ private handleActiveAssetCtx;
84
+ private handleActiveSpotAssetCtx;
83
85
  /** Latest mid per `Market.id` across asset-context and fast feeds. */
84
86
  private mergedMids;
85
87
  private orderbookReferencePrice;
@@ -1 +1 @@
1
- {"version":3,"file":"HyperliquidWsProvider.d.ts","sourceRoot":"","sources":["../../../src/websocket/HyperliquidWsProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAGlB,cAAc,EACd,KAAK,iBAAiB,EAEvB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAOL,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAA;AA2C1B;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,QAC5B,iBAEgD,CAAA;AAEtD;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,SAAQ,cAAc,CAAC,MAAM,CAAC;IAC/D,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IACrD,OAAO,CAAC,eAAe,CAAsD;IAC7E,OAAO,CAAC,iBAAiB,CAAuC;IAChE,OAAO,CAAC,uBAAuB,CAAiC;IAChE,OAAO,CAAC,yBAAyB,CAAuC;IAKxE,OAAO,CAAC,iBAAiB,CAAuC;IAGhE,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,oBAAoB,CAAmC;gBAEnD,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc;IASjE,cAAc,CAClB,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,IAAI,CAAC;cAkBN,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IA+DnE;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,iBAAiB;IAmBzB;;;;OAIG;IACH,OAAO,CAAC,2BAA2B;IAWnC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,gBAAgB;cAcL,OAAO,IAAI,IAAI;IAalC,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM9C,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM;IAwB1C,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,WAAW;IAwCnB,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM;IAwEnC,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,wBAAwB;IAsBhC,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,oBAAoB;IAe5B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAgB3B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA6C1B,sEAAsE;IACtE,OAAO,CAAC,UAAU;IAuBlB,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,QAAQ;IAoBhB,OAAO,CAAC,oBAAoB;IA2B5B,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,kBAAkB;IA0D1B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,+BAA+B;IAkBvC,OAAO,CAAC,eAAe;CAaxB"}
1
+ {"version":3,"file":"HyperliquidWsProvider.d.ts","sourceRoot":"","sources":["../../../src/websocket/HyperliquidWsProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAGlB,cAAc,EACd,KAAK,iBAAiB,EAEvB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAOL,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAA;AA6C1B;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,QAC5B,iBAEgD,CAAA;AAEtD;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,SAAQ,cAAc,CAAC,MAAM,CAAC;IAC/D,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IACrD,OAAO,CAAC,eAAe,CAAsD;IAC7E,OAAO,CAAC,iBAAiB,CAAuC;IAChE,OAAO,CAAC,uBAAuB,CAAiC;IAChE,OAAO,CAAC,yBAAyB,CAAuC;IAKxE,OAAO,CAAC,iBAAiB,CAAuC;IAGhE,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,oBAAoB,CAAmC;gBAEnD,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc;IASjE,cAAc,CAClB,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,IAAI,CAAC;cAkBN,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IA+DnE;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,iBAAiB;IAmBzB;;;;OAIG;IACH,OAAO,CAAC,2BAA2B;IAWnC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,gBAAgB;cAcL,OAAO,IAAI,IAAI;IAalC,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM9C,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM;IA0B1C,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,WAAW;IA6CnB,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM;IA8EnC,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,wBAAwB;IAsBhC,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,oBAAoB;IAe5B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAgB3B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAuC1B,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,wBAAwB;IAWhC,sEAAsE;IACtE,OAAO,CAAC,UAAU;IAuBlB,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,QAAQ;IAoBhB,OAAO,CAAC,oBAAoB;IA2B5B,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,kBAAkB;IA0D1B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,+BAA+B;IAkBvC,OAAO,CAAC,eAAe;CAaxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/perps-sdk-provider-hyperliquid",
3
- "version": "1.4.0",
3
+ "version": "1.5.2",
4
4
  "description": "Hyperliquid provider plugin for @lifi/perps-sdk",
5
5
  "homepage": "https://github.com/lifinance/perps-sdk",
6
6
  "bugs": {
@@ -48,10 +48,10 @@
48
48
  "dependencies": {
49
49
  "big.js": "^7.0.1",
50
50
  "viem": "^2.48.8",
51
- "@lifi/perps-types": "^1.4.0"
51
+ "@lifi/perps-types": "^1.5.1"
52
52
  },
53
53
  "peerDependencies": {
54
- "@lifi/perps-sdk": "^1.4.0"
54
+ "@lifi/perps-sdk": "^1.5.2"
55
55
  },
56
56
  "publishConfig": {
57
57
  "access": "public"
@@ -1,4 +1,4 @@
1
- import { summarizeAccount } from '@lifi/perps-sdk'
1
+ import { type CollateralSemantics, summarizeAccount } from '@lifi/perps-sdk'
2
2
  import type {
3
3
  AccountResponse,
4
4
  AccountSummary,
@@ -7,20 +7,23 @@ import type {
7
7
  import { HlAbstractionMode } from './types/index.js'
8
8
 
9
9
  /**
10
- * In `unifiedAccount`/`portfolioMargin` the whole account lives in spot, so the
11
- * collateral rows are already gross holdings (locked margin included). In
12
- * `disabled`/`dexAbstraction` (and the unset/standard mode) the venue rows hold
13
- * `accountValue`, which Hyperliquid reports net of locked margin — i.e. free.
10
+ * In `unifiedAccount`/`portfolioMargin` the whole account lives in spot, so
11
+ * the collateral rows are gross holdings: locked margin included, unrealized
12
+ * PnL carried by the positions. In `disabled`/`dexAbstraction` (and the
13
+ * unset/standard mode) the venue rows hold `accountValue`, Hyperliquid's
14
+ * total equity — locked margin AND unrealized PnL already included.
14
15
  */
15
- const isUnifiedMode = (account: AccountResponse): boolean =>
16
+ const collateralSemantics = (account: AccountResponse): CollateralSemantics =>
16
17
  account.config.provider === 'hyperliquid' &&
17
18
  (account.config.abstractionMode === HlAbstractionMode.UNIFIED_ACCOUNT ||
18
19
  account.config.abstractionMode === HlAbstractionMode.PORTFOLIO_MARGIN)
20
+ ? 'gross'
21
+ : 'equity'
19
22
 
20
23
  /**
21
24
  * Roll a Hyperliquid {@link AccountResponse} up into an {@link AccountSummary},
22
- * branching on the abstraction mode so the gross/free meaning of the collateral
23
- * rows is interpreted correctly.
25
+ * branching on the abstraction mode so the margin/PnL content of the
26
+ * collateral rows is interpreted correctly.
24
27
  *
25
28
  * @public
26
29
  */
@@ -28,5 +31,5 @@ export function getAccountSummary(
28
31
  account: AccountResponse,
29
32
  positions: Position[]
30
33
  ): AccountSummary {
31
- return summarizeAccount(account, positions, isUnifiedMode(account))
34
+ return summarizeAccount(account, positions, collateralSemantics(account))
32
35
  }
@@ -28,7 +28,7 @@ import {
28
28
  spotPriceById,
29
29
  } from '../utils/index.js'
30
30
  import { hlInfoOptions, infoRequest } from '../utils/infoClient.js'
31
- import { mapPosition } from '../utils/mapPosition.js'
31
+ import { isOpenAssetPosition, mapPosition } from '../utils/mapPosition.js'
32
32
 
33
33
  /**
34
34
  * Parameters for {@link getAccount}.
@@ -37,16 +37,14 @@ import { mapPosition } from '../utils/mapPosition.js'
37
37
  */
38
38
  export type GetAccountParams = ProviderGetAccountParams
39
39
 
40
+ // `marginSummary` covers the whole account (cross AND isolated positions);
41
+ // `crossMarginSummary` is the cross-only subset and would drop isolated
42
+ // equity/margin.
40
43
  const getAccountValue = (state: HlClearinghouseState): number =>
41
- Number.parseFloat(
42
- state.crossMarginSummary.accountValue || state.marginSummary.accountValue
43
- )
44
+ Number.parseFloat(state.marginSummary.accountValue)
44
45
 
45
46
  const getTotalMarginUsed = (state: HlClearinghouseState): number =>
46
- Number.parseFloat(
47
- state.crossMarginSummary.totalMarginUsed ||
48
- state.marginSummary.totalMarginUsed
49
- )
47
+ Number.parseFloat(state.marginSummary.totalMarginUsed)
50
48
 
51
49
  const getMarginUsed = (
52
50
  abstraction: HlAbstractionMode | null,
@@ -113,8 +111,8 @@ const buildBalances = (
113
111
 
114
112
  // Unified/portfolio modes hold everything in spot — per-dex equity would
115
113
  // double-count. Only disabled/dexAbstraction carry separate venue collateral.
116
- // `accountValue` is reported net of locked margin, i.e. the free collateral;
117
- // the locked portion is carried by the positions' `marginUsed`.
114
+ // `accountValue` is the dex's TOTAL equity: locked margin and unrealized
115
+ // PnL are already included, so summaries must not add them on top.
118
116
  if (!isUnifiedMode(abstraction)) {
119
117
  for (const [dex, state] of stateByDex) {
120
118
  const categoryId = dex || PROVIDER_KEY
@@ -218,7 +216,7 @@ export const getAccount = async (
218
216
 
219
217
  const positions: Position[] = stateResults.flatMap((state) =>
220
218
  state.assetPositions
221
- .filter((ap) => Number.parseFloat(ap.position.szi) !== 0)
219
+ .filter(isOpenAssetPosition)
222
220
  .map((ap) => mapPosition(ap, registry.require(ap.position.coin)))
223
221
  )
224
222
 
@@ -252,6 +250,7 @@ export const getAccount = async (
252
250
  address: params.address,
253
251
  balances,
254
252
  collateralBalances,
253
+ positions,
255
254
  marginUsed: getMarginUsed(abstractionResult, positions, stateByDex),
256
255
  unrealizedPnl: totalUnrealizedPnl.toString(),
257
256
  feeTier: {
package/src/types/ws.ts CHANGED
@@ -71,6 +71,18 @@ export type HlWsSpotAssetCtx = {
71
71
  circulatingSupply?: HlWsNumberString
72
72
  }
73
73
 
74
+ /** @public */
75
+ export type HlWsActiveAssetCtxData = {
76
+ coin: string
77
+ ctx: Partial<Record<keyof HlWsPerpAssetCtx, HlWsNumberString | null>>
78
+ }
79
+
80
+ /** @public */
81
+ export type HlWsActiveSpotAssetCtxData = {
82
+ coin: string
83
+ ctx: HlWsSpotAssetCtx
84
+ }
85
+
74
86
  /**
75
87
  * Compressed spot asset-context feed. The first frame is a broad snapshot; later
76
88
  * frames only carry changed fields.
@@ -122,7 +122,17 @@ export const mapTriggerOrder = (
122
122
  }
123
123
  }
124
124
 
125
- /** @public */
125
+ /**
126
+ * Map a raw Hyperliquid order status string to the SDK's OrderStatus enum.
127
+ *
128
+ * HL's terminal statuses are mostly compound strings ending in `Canceled`
129
+ * or `Rejected` (e.g. `siblingFilledCanceled`, `tickRejected`); classify
130
+ * those by suffix so a status HL adds later still lands in the correct
131
+ * terminal bucket instead of silently falling through to PENDING, which
132
+ * {@link isActiveOrderStatus} treats as active. `scheduledCancel` is the
133
+ * one documented cancel status without the `Canceled` suffix.
134
+ * @public
135
+ */
126
136
  export const mapOrderStatus = (status: string): OrderStatus => {
127
137
  switch (status) {
128
138
  case 'open':
@@ -132,14 +142,19 @@ export const mapOrderStatus = (status: string): OrderStatus => {
132
142
  return OrderStatus.FILLED
133
143
  case 'canceled':
134
144
  case 'cancelled':
145
+ case 'scheduledCancel':
135
146
  return OrderStatus.CANCELLED
136
147
  case 'rejected':
137
148
  return OrderStatus.REJECTED
138
149
  case 'triggered':
139
150
  return OrderStatus.TRIGGERED
140
- case 'marginCanceled':
141
- return OrderStatus.CANCELLED
142
151
  default:
152
+ if (status.endsWith('Canceled') || status.endsWith('Cancelled')) {
153
+ return OrderStatus.CANCELLED
154
+ }
155
+ if (status.endsWith('Rejected')) {
156
+ return OrderStatus.REJECTED
157
+ }
143
158
  return OrderStatus.PENDING
144
159
  }
145
160
  }
@@ -153,7 +168,7 @@ export const mapOrderStatus = (status: string): OrderStatus => {
153
168
  */
154
169
  export const mapStatusReason = (status: string): string | undefined => {
155
170
  switch (status) {
156
- case 'iocCanceled':
171
+ case 'iocCancelRejected':
157
172
  return 'Order cancelled: not enough liquidity to fill immediately.'
158
173
  case 'reduceOnlyCanceled':
159
174
  return 'Order cancelled: would not reduce your position.'
@@ -165,12 +180,14 @@ export const mapStatusReason = (status: string): string | undefined => {
165
180
  return 'Order cancelled: sibling OCO order filled first.'
166
181
  case 'selfTradeCanceled':
167
182
  return 'Order cancelled: would self-trade against your own resting order.'
183
+ case 'scheduledCancel':
184
+ return "Order cancelled: dead man's switch triggered."
168
185
  case 'tickRejected':
169
186
  return 'Order rejected: price did not match the tick size.'
170
187
  case 'minTradeNtlRejected':
171
188
  return 'Order rejected: notional value below the minimum trade size.'
172
- case 'delistedRejected':
173
- return 'Order rejected: market has been delisted.'
189
+ case 'delistedCanceled':
190
+ return 'Order cancelled: market has been delisted.'
174
191
  default:
175
192
  return undefined
176
193
  }
@@ -27,6 +27,8 @@ import type {
27
27
  HlAssetPosition,
28
28
  HlOrderDetail,
29
29
  HlUserFill,
30
+ HlWsActiveAssetCtxData,
31
+ HlWsActiveSpotAssetCtxData,
30
32
  HlWsAllDexsAssetCtxsData,
31
33
  HlWsAllDexsClearinghouseStateData,
32
34
  HlWsCandleData,
@@ -309,6 +311,8 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
309
311
  switch (sub.channel) {
310
312
  case 'marketsContext':
311
313
  return 'marketsContext'
314
+ case 'marketContext':
315
+ return `marketContext:${sub.marketId}`
312
316
  case 'orderbook':
313
317
  return `l2:${sub.marketId}:${this.orderbookAggregationKey(
314
318
  sub.marketId,
@@ -361,6 +365,11 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
361
365
  // Handled via getMarketsContextSubEntries in openChannel; never reaches
362
366
  // toHlPayload, but TS requires an exhaustive switch.
363
367
  return { type: 'allDexsAssetCtxs' }
368
+ case 'marketContext':
369
+ return {
370
+ type: 'activeAssetCtx',
371
+ coin: sub.marketId,
372
+ }
364
373
  case 'orderbook': {
365
374
  const aggregation = this.orderbookAggregation(
366
375
  sub.marketId,
@@ -435,6 +444,12 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
435
444
  case 'fastAssetCtxs':
436
445
  this.handleFastAssetCtxs(msg.data as string)
437
446
  break
447
+ case 'activeAssetCtx':
448
+ this.handleActiveAssetCtx(msg.data as HlWsActiveAssetCtxData, raw)
449
+ break
450
+ case 'activeSpotAssetCtx':
451
+ this.handleActiveSpotAssetCtx(msg.data as HlWsActiveSpotAssetCtxData)
452
+ break
438
453
  case 'l2':
439
454
  this.handleL2(msg.data as HlWsL2Data)
440
455
  break
@@ -580,21 +595,15 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
580
595
  }
581
596
  }
582
597
  for (const [marketId, ctx] of Object.entries(this.spotCtxByMarketId)) {
583
- const midPrice = toMarketContextString(ctx.midPx ?? ctx.markPx)
584
- const markPrice = toMarketContextString(ctx.markPx)
585
- if (midPrice === undefined || markPrice === undefined) {
586
- continue
587
- }
588
- const fast = this.fastCtxByMarketId[marketId]
589
- const emittedMarkPrice = fast?.markPx != null ? fast.markPx : markPrice
590
- data[marketId] = {
598
+ const context = mapSpotMarketContext(
591
599
  marketId,
592
- midPrice: fast?.midPx != null ? fast.midPx : midPrice,
593
- markPrice: emittedMarkPrice,
594
- prevDayPrice: toMarketContextString(ctx.prevDayPx),
595
- volume24h: toMarketContextString(ctx.dayNtlVlm),
596
- marketCap: toMarketCapString(emittedMarkPrice, ctx.circulatingSupply),
600
+ ctx,
601
+ this.fastCtxByMarketId[marketId]
602
+ )
603
+ if (context === undefined) {
604
+ continue
597
605
  }
606
+ data[marketId] = context
598
607
  }
599
608
  for (const [marketId, fast] of Object.entries(this.fastCtxByMarketId)) {
600
609
  if (data[marketId] !== undefined) {
@@ -611,6 +620,29 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
611
620
  this.emit('marketsContext', { channel: 'marketsContext', data })
612
621
  }
613
622
 
623
+ private handleActiveAssetCtx(data: HlWsActiveAssetCtxData, raw: string) {
624
+ const ctx = activePerpAssetCtx(data.coin, data.ctx)
625
+ if (ctx === undefined) {
626
+ wsLog.parseFailure(this.providerKey, raw)
627
+ return
628
+ }
629
+ this.emit(`marketContext:${data.coin}`, {
630
+ channel: 'marketContext',
631
+ data: mapMarketContext(data.coin, ctx),
632
+ })
633
+ }
634
+
635
+ private handleActiveSpotAssetCtx(data: HlWsActiveSpotAssetCtxData) {
636
+ const context = mapSpotMarketContext(data.coin, data.ctx)
637
+ if (context === undefined) {
638
+ return
639
+ }
640
+ this.emit(`marketContext:${data.coin}`, {
641
+ channel: 'marketContext',
642
+ data: context,
643
+ })
644
+ }
645
+
614
646
  /** Latest mid per `Market.id` across asset-context and fast feeds. */
615
647
  private mergedMids(): Map<string, number> {
616
648
  const map = new Map<string, number>()
@@ -932,6 +964,66 @@ function allDexsAssetCtxEntries(
932
964
  return data.assetCtxs ?? data.ctxs ?? []
933
965
  }
934
966
 
967
+ function activePerpAssetCtx(
968
+ marketId: string,
969
+ ctx: HlWsActiveAssetCtxData['ctx']
970
+ ): HlWsPerpAssetCtx | undefined {
971
+ const funding = toMarketContextString(ctx.funding)
972
+ const openInterest = toMarketContextString(ctx.openInterest)
973
+ const dayNtlVlm = toMarketContextString(ctx.dayNtlVlm)
974
+ const prevDayPx = toMarketContextString(ctx.prevDayPx)
975
+ const markPx = toMarketContextString(ctx.markPx)
976
+ const midPx =
977
+ ctx.midPx === undefined || ctx.midPx === null
978
+ ? null
979
+ : (toMarketContextString(ctx.midPx) ?? null)
980
+ const oraclePx = toMarketContextString(ctx.oraclePx)
981
+
982
+ if (
983
+ funding === undefined ||
984
+ openInterest === undefined ||
985
+ dayNtlVlm === undefined ||
986
+ prevDayPx === undefined ||
987
+ markPx === undefined ||
988
+ oraclePx === undefined
989
+ ) {
990
+ return undefined
991
+ }
992
+
993
+ return {
994
+ coin: marketId,
995
+ funding,
996
+ openInterest,
997
+ dayNtlVlm,
998
+ prevDayPx,
999
+ markPx,
1000
+ midPx,
1001
+ oraclePx,
1002
+ }
1003
+ }
1004
+
1005
+ function mapSpotMarketContext(
1006
+ marketId: string,
1007
+ ctx: HlWsSpotAssetCtx,
1008
+ fast?: HlWsFastAssetCtx
1009
+ ): MarketContext | undefined {
1010
+ const midPrice = toMarketContextString(ctx.midPx ?? ctx.markPx)
1011
+ const markPrice = toMarketContextString(ctx.markPx)
1012
+ if (midPrice === undefined || markPrice === undefined) {
1013
+ return undefined
1014
+ }
1015
+
1016
+ const emittedMarkPrice = fast?.markPx != null ? fast.markPx : markPrice
1017
+ return {
1018
+ marketId,
1019
+ midPrice: fast?.midPx != null ? fast.midPx : midPrice,
1020
+ markPrice: emittedMarkPrice,
1021
+ prevDayPrice: toMarketContextString(ctx.prevDayPx),
1022
+ volume24h: toMarketContextString(ctx.dayNtlVlm),
1023
+ marketCap: toMarketCapString(emittedMarkPrice, ctx.circulatingSupply),
1024
+ }
1025
+ }
1026
+
935
1027
  function compactRemovalPrice(
936
1028
  levels: OrderbookLevel[],
937
1029
  value: number | string | { p: string }
@@ -991,6 +1083,9 @@ function isValidHlFrame(channel: string, data: unknown): boolean {
991
1083
  switch (channel) {
992
1084
  case 'allDexsAssetCtxs':
993
1085
  return Array.isArray(data.assetCtxs) || Array.isArray(data.ctxs)
1086
+ case 'activeAssetCtx':
1087
+ case 'activeSpotAssetCtx':
1088
+ return typeof data.coin === 'string' && isObject(data.ctx)
994
1089
  case 'l2Book':
995
1090
  return (
996
1091
  typeof data.coin === 'string' &&