@lifi/perps-sdk-provider-hyperliquid 1.0.0 → 1.1.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 (79) hide show
  1. package/dist/cjs/services/getAccount.js +1 -1
  2. package/dist/cjs/services/getAccount.js.map +1 -1
  3. package/dist/cjs/types/asset.d.ts +0 -1
  4. package/dist/cjs/types/asset.d.ts.map +1 -1
  5. package/dist/cjs/types/ws.d.ts +17 -4
  6. package/dist/cjs/types/ws.d.ts.map +1 -1
  7. package/dist/cjs/utils/decodeFastAssetCtxs.d.ts +3 -0
  8. package/dist/cjs/utils/decodeFastAssetCtxs.d.ts.map +1 -0
  9. package/dist/cjs/utils/decodeFastAssetCtxs.js +12 -0
  10. package/dist/cjs/utils/decodeFastAssetCtxs.js.map +1 -0
  11. package/dist/cjs/utils/index.d.ts +2 -1
  12. package/dist/cjs/utils/index.d.ts.map +1 -1
  13. package/dist/cjs/utils/index.js +5 -3
  14. package/dist/cjs/utils/index.js.map +1 -1
  15. package/dist/cjs/utils/mapMarketContext.d.ts +4 -0
  16. package/dist/cjs/utils/mapMarketContext.d.ts.map +1 -0
  17. package/dist/cjs/utils/mapMarketContext.js +23 -0
  18. package/dist/cjs/utils/mapMarketContext.js.map +1 -0
  19. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts +8 -5
  20. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  21. package/dist/cjs/websocket/HyperliquidWsProvider.js +85 -38
  22. package/dist/cjs/websocket/HyperliquidWsProvider.js.map +1 -1
  23. package/dist/esm/services/getAccount.js +2 -2
  24. package/dist/esm/services/getAccount.js.map +1 -1
  25. package/dist/esm/types/asset.d.ts +0 -2
  26. package/dist/esm/types/asset.d.ts.map +1 -1
  27. package/dist/esm/types/ws.d.ts +34 -5
  28. package/dist/esm/types/ws.d.ts.map +1 -1
  29. package/dist/esm/utils/decodeFastAssetCtxs.d.ts +11 -0
  30. package/dist/esm/utils/decodeFastAssetCtxs.d.ts.map +1 -0
  31. package/dist/esm/utils/decodeFastAssetCtxs.js +17 -0
  32. package/dist/esm/utils/decodeFastAssetCtxs.js.map +1 -0
  33. package/dist/esm/utils/index.d.ts +2 -1
  34. package/dist/esm/utils/index.d.ts.map +1 -1
  35. package/dist/esm/utils/index.js +2 -1
  36. package/dist/esm/utils/index.js.map +1 -1
  37. package/dist/esm/utils/mapMarket.d.ts +2 -2
  38. package/dist/esm/utils/mapMarket.js +2 -2
  39. package/dist/esm/utils/mapMarketContext.d.ts +10 -0
  40. package/dist/esm/utils/mapMarketContext.d.ts.map +1 -0
  41. package/dist/esm/utils/mapMarketContext.js +25 -0
  42. package/dist/esm/utils/mapMarketContext.js.map +1 -0
  43. package/dist/esm/websocket/HyperliquidWsProvider.d.ts +32 -13
  44. package/dist/esm/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  45. package/dist/esm/websocket/HyperliquidWsProvider.js +124 -50
  46. package/dist/esm/websocket/HyperliquidWsProvider.js.map +1 -1
  47. package/dist/types/types/asset.d.ts +0 -2
  48. package/dist/types/types/asset.d.ts.map +1 -1
  49. package/dist/types/types/ws.d.ts +34 -5
  50. package/dist/types/types/ws.d.ts.map +1 -1
  51. package/dist/types/utils/decodeFastAssetCtxs.d.ts +11 -0
  52. package/dist/types/utils/decodeFastAssetCtxs.d.ts.map +1 -0
  53. package/dist/types/utils/index.d.ts +2 -1
  54. package/dist/types/utils/index.d.ts.map +1 -1
  55. package/dist/types/utils/mapMarket.d.ts +2 -2
  56. package/dist/types/utils/mapMarketContext.d.ts +10 -0
  57. package/dist/types/utils/mapMarketContext.d.ts.map +1 -0
  58. package/dist/types/websocket/HyperliquidWsProvider.d.ts +32 -13
  59. package/dist/types/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  60. package/package.json +5 -5
  61. package/src/services/getAccount.ts +2 -2
  62. package/src/types/asset.ts +0 -3
  63. package/src/types/ws.ts +36 -5
  64. package/src/utils/decodeFastAssetCtxs.ts +20 -0
  65. package/src/utils/index.ts +2 -1
  66. package/src/utils/mapMarket.ts +2 -2
  67. package/src/utils/mapMarketContext.ts +33 -0
  68. package/src/websocket/HyperliquidWsProvider.ts +135 -57
  69. package/dist/cjs/utils/mapMarketPrice.d.ts +0 -4
  70. package/dist/cjs/utils/mapMarketPrice.d.ts.map +0 -1
  71. package/dist/cjs/utils/mapMarketPrice.js +0 -22
  72. package/dist/cjs/utils/mapMarketPrice.js.map +0 -1
  73. package/dist/esm/utils/mapMarketPrice.d.ts +0 -10
  74. package/dist/esm/utils/mapMarketPrice.d.ts.map +0 -1
  75. package/dist/esm/utils/mapMarketPrice.js +0 -24
  76. package/dist/esm/utils/mapMarketPrice.js.map +0 -1
  77. package/dist/types/utils/mapMarketPrice.d.ts +0 -10
  78. package/dist/types/utils/mapMarketPrice.d.ts.map +0 -1
  79. package/src/utils/mapMarketPrice.ts +0 -33
@@ -1,15 +1,44 @@
1
1
  import type { HlClearinghouseState, HlSpotBalance } from './account.js';
2
- import type { HlAllMids, HlCandle, HlL2Book } from './asset.js';
2
+ import type { HlCandle, HlL2Book } from './asset.js';
3
3
  import type { HlUserFill } from './fill.js';
4
+ /**
5
+ * Perp asset context as carried on the `allDexsAssetCtxs` WS feed. Extends the
6
+ * REST `/info` `HlAssetCtx` shape with the wire-only `coin` tag, the order-book
7
+ * `midPx` (null when the book is empty), and `oraclePx`.
8
+ * @public
9
+ */
10
+ export type HlWsPerpAssetCtx = {
11
+ coin: string;
12
+ funding: string;
13
+ openInterest: string;
14
+ dayNtlVlm: string;
15
+ prevDayPx: string;
16
+ markPx: string;
17
+ midPx: string | null;
18
+ oraclePx: string;
19
+ };
20
+ /**
21
+ * `allDexsAssetCtxs` carries one `[dex, ctxs]` pair per perp sub-DEX, mirroring
22
+ * `allDexsClearinghouseState`. Spot contexts arrive separately.
23
+ * @public
24
+ */
25
+ export type HlWsAllDexsAssetCtxsData = {
26
+ assetCtxs: [string, HlWsPerpAssetCtx[]][];
27
+ };
4
28
  /** @public */
5
29
  export type HlWsMessage = {
6
30
  channel: string;
7
31
  data: unknown;
8
32
  };
9
- /** @public */
10
- export type HlWsAllMidsData = {
11
- mids: HlAllMids;
12
- dex?: string;
33
+ /**
34
+ * Per-coin entry from the compressed `fastAssetCtxs` feed (mark + mid only,
35
+ * keyed by coin across all dexes). `midPx` is null when the book is empty;
36
+ * fields are omitted from incremental frames when unchanged.
37
+ * @public
38
+ */
39
+ export type HlWsFastAssetCtx = {
40
+ markPx?: string;
41
+ midPx?: string | null;
13
42
  };
14
43
  /** @public */
15
44
  export type HlWsL2BookData = HlL2Book & {
@@ -1 +1 @@
1
- {"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../../src/types/ws.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C,cAAc;AACd,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,OAAO,CAAA;CACd,CAAA;AAED,cAAc;AACd,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,SAAS,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,cAAc;AACd,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAExD,cAAc;AACd,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG;IACtC,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV,CAAA;AAED,cAAc;AACd,MAAM,MAAM,iBAAiB,GAAG;IAC9B,UAAU,EAAE,OAAO,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,UAAU,EAAE,CAAA;CACpB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,mBAAmB,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAA;CAC9E,CAAA;AAED,cAAc;AACd,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE;QAAE,QAAQ,EAAE,aAAa,EAAE,CAAA;KAAE,CAAA;CACzC,CAAA"}
1
+ {"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../../src/types/ws.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EAAE,CAAA;CAC1C,CAAA;AAED,cAAc;AACd,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,OAAO,CAAA;CACd,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAExD,cAAc;AACd,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG;IACtC,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV,CAAA;AAED,cAAc;AACd,MAAM,MAAM,iBAAiB,GAAG;IAC9B,UAAU,EAAE,OAAO,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,UAAU,EAAE,CAAA;CACpB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,mBAAmB,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAA;CAC9E,CAAA;AAED,cAAc;AACd,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE;QAAE,QAAQ,EAAE,aAAa,EAAE,CAAA;KAAE,CAAA;CACzC,CAAA"}
@@ -0,0 +1,11 @@
1
+ import type { HlWsFastAssetCtx } from '../types/index.js';
2
+ /**
3
+ * Decode a Hyperliquid `fastAssetCtxs` frame. HL sends the payload as a
4
+ * base64-encoded, raw-DEFLATE-compressed (RFC 1951 — no zlib/gzip wrapper) JSON
5
+ * object keyed by coin. Decoded via the web-standard `DecompressionStream`
6
+ * (Node 18+, modern browsers), so no dependency is needed.
7
+ *
8
+ * @throws If the input is not valid base64 / raw-DEFLATE / JSON.
9
+ */
10
+ export declare function decodeFastAssetCtxs(base64: string): Promise<Record<string, HlWsFastAssetCtx>>;
11
+ //# sourceMappingURL=decodeFastAssetCtxs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decodeFastAssetCtxs.d.ts","sourceRoot":"","sources":["../../../src/utils/decodeFastAssetCtxs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEzD;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAO3C"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Decode a Hyperliquid `fastAssetCtxs` frame. HL sends the payload as a
3
+ * base64-encoded, raw-DEFLATE-compressed (RFC 1951 — no zlib/gzip wrapper) JSON
4
+ * object keyed by coin. Decoded via the web-standard `DecompressionStream`
5
+ * (Node 18+, modern browsers), so no dependency is needed.
6
+ *
7
+ * @throws If the input is not valid base64 / raw-DEFLATE / JSON.
8
+ */
9
+ export async function decodeFastAssetCtxs(base64) {
10
+ const bytes = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
11
+ const stream = new Blob([bytes])
12
+ .stream()
13
+ .pipeThrough(new DecompressionStream('deflate-raw'));
14
+ const text = await new Response(stream).text();
15
+ return JSON.parse(text);
16
+ }
17
+ //# sourceMappingURL=decodeFastAssetCtxs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decodeFastAssetCtxs.js","sourceRoot":"","sources":["../../../src/utils/decodeFastAssetCtxs.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAc;IAEd,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;IACnE,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;SAC7B,MAAM,EAAE;SACR,WAAW,CAAC,IAAI,mBAAmB,CAAC,aAAa,CAAC,CAAC,CAAA;IACtD,MAAM,IAAI,GAAG,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
@@ -1,11 +1,12 @@
1
1
  export { assetIsSpot, calculateAssetId, getProviderIndex } from './assetId.js';
2
+ export { decodeFastAssetCtxs } from './decodeFastAssetCtxs.js';
2
3
  export type { InfoRequestOptions } from './infoClient.js';
3
4
  export { hlInfoOptions, infoRequest } from './infoClient.js';
4
5
  export { calculateLiquidationPrice, calculateMaintenanceMarginRate, } from './liquidation.js';
5
6
  export { mapFundingActivity, mapLedgerEntry } from './mapActivity.js';
6
7
  export { classifyFillFromPosition, mapFill } from './mapFill.js';
7
8
  export { mapMarket } from './mapMarket.js';
8
- export { mapMarketPrice } from './mapMarketPrice.js';
9
+ export { mapMarketContext } from './mapMarketContext.js';
9
10
  export { isTriggerOrder, isTriggerType, mapOpenOrder, mapOrder, mapOrderStatus, mapOrderType, mapStatusReason, mapTriggerOrder, } from './mapOrder.js';
10
11
  export { isOpenAssetPosition, mapPosition } from './mapPosition.js';
11
12
  export { coinAsset, perpsDexNames } from './marketDisplay.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC9E,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EACL,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,wBAAwB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EACL,cAAc,EACd,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GACpB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,aAAa,GACd,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EACL,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,wBAAwB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EACL,cAAc,EACd,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GACpB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,aAAa,GACd,MAAM,kBAAkB,CAAA"}
@@ -2,12 +2,13 @@
2
2
  // for the package's utility surface. Internal modules import per-file paths
3
3
  // (e.g. `./assetId.js`, `./mapFill.js`) directly.
4
4
  export { assetIsSpot, calculateAssetId, getProviderIndex } from './assetId.js';
5
+ export { decodeFastAssetCtxs } from './decodeFastAssetCtxs.js';
5
6
  export { hlInfoOptions, infoRequest } from './infoClient.js';
6
7
  export { calculateLiquidationPrice, calculateMaintenanceMarginRate, } from './liquidation.js';
7
8
  export { mapFundingActivity, mapLedgerEntry } from './mapActivity.js';
8
9
  export { classifyFillFromPosition, mapFill } from './mapFill.js';
9
10
  export { mapMarket } from './mapMarket.js';
10
- export { mapMarketPrice } from './mapMarketPrice.js';
11
+ export { mapMarketContext } from './mapMarketContext.js';
11
12
  export { isTriggerOrder, isTriggerType, mapOpenOrder, mapOrder, mapOrderStatus, mapOrderType, mapStatusReason, mapTriggerOrder, } from './mapOrder.js';
12
13
  export { isOpenAssetPosition, mapPosition } from './mapPosition.js';
13
14
  export { coinAsset, perpsDexNames } from './marketDisplay.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,4EAA4E;AAC5E,kDAAkD;AAElD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE9E,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EACL,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,wBAAwB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EACL,cAAc,EACd,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GACpB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,aAAa,GACd,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,4EAA4E;AAC5E,kDAAkD;AAElD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EACL,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,wBAAwB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EACL,cAAc,EACd,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GACpB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,aAAa,GACd,MAAM,kBAAkB,CAAA"}
@@ -2,8 +2,8 @@ import type { PerpsMarket } from '@lifi/perps-types';
2
2
  import type { HlUniverseItem } from '../types/index.js';
3
3
  /**
4
4
  * Map a Hyperliquid universe entry to a {@link PerpsMarket}'s static instrument
5
- * metadata. Live mark/stats fields live on the `/prices` item — see
6
- * {@link mapMarketPrice}. `categoryId` is the `Provider.categories[].id` of the
5
+ * metadata. Live mark/stats fields live on the {@link MarketContext} — see
6
+ * {@link mapMarketContext}. `categoryId` is the `Provider.categories[].id` of the
7
7
  * dex the entry was fetched from — known to the caller at fetch time, never
8
8
  * re-derived from the coin string.
9
9
  * @public
@@ -6,8 +6,8 @@ import { getMaxPriceDecimals } from './orderFormatting.js';
6
6
  const QUOTE_SYMBOL = 'USDC';
7
7
  /**
8
8
  * Map a Hyperliquid universe entry to a {@link PerpsMarket}'s static instrument
9
- * metadata. Live mark/stats fields live on the `/prices` item — see
10
- * {@link mapMarketPrice}. `categoryId` is the `Provider.categories[].id` of the
9
+ * metadata. Live mark/stats fields live on the {@link MarketContext} — see
10
+ * {@link mapMarketContext}. `categoryId` is the `Provider.categories[].id` of the
11
11
  * dex the entry was fetched from — known to the caller at fetch time, never
12
12
  * re-derived from the coin string.
13
13
  * @public
@@ -0,0 +1,10 @@
1
+ import type { MarketContext } from '@lifi/perps-types';
2
+ import type { HlWsPerpAssetCtx } from '../types/index.js';
3
+ /**
4
+ * Map a Hyperliquid perp asset context from the `allDexsAssetCtxs` feed to a
5
+ * {@link MarketContext}. `midPx` is null when the book is empty; mid then falls
6
+ * back to the mark so the context always carries a usable mid.
7
+ * @public
8
+ */
9
+ export declare const mapMarketContext: (marketId: string, ctx: HlWsPerpAssetCtx) => MarketContext;
10
+ //# sourceMappingURL=mapMarketContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapMarketContext.d.ts","sourceRoot":"","sources":["../../../src/utils/mapMarketContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAIzD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAChB,KAAK,gBAAgB,KACpB,aAkBF,CAAA"}
@@ -0,0 +1,25 @@
1
+ const NEXT_FUNDING_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
2
+ /**
3
+ * Map a Hyperliquid perp asset context from the `allDexsAssetCtxs` feed to a
4
+ * {@link MarketContext}. `midPx` is null when the book is empty; mid then falls
5
+ * back to the mark so the context always carries a usable mid.
6
+ * @public
7
+ */
8
+ export const mapMarketContext = (marketId, ctx) => {
9
+ const now = Date.now();
10
+ const nextFundingTime = Math.ceil(now / NEXT_FUNDING_INTERVAL_MS) * NEXT_FUNDING_INTERVAL_MS;
11
+ return {
12
+ marketId,
13
+ midPrice: ctx.midPx ?? ctx.markPx,
14
+ markPrice: ctx.markPx,
15
+ oraclePrice: ctx.oraclePx,
16
+ prevDayPrice: ctx.prevDayPx,
17
+ volume24h: ctx.dayNtlVlm,
18
+ openInterest: ctx.openInterest,
19
+ funding: {
20
+ rate: ctx.funding,
21
+ nextFundingTime,
22
+ },
23
+ };
24
+ };
25
+ //# sourceMappingURL=mapMarketContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapMarketContext.js","sourceRoot":"","sources":["../../../src/utils/mapMarketContext.ts"],"names":[],"mappings":"AAGA,MAAM,wBAAwB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA,CAAC,SAAS;AAEzD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,QAAgB,EAChB,GAAqB,EACN,EAAE;IACjB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,MAAM,eAAe,GACnB,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,wBAAwB,CAAC,GAAG,wBAAwB,CAAA;IAEtE,OAAO;QACL,QAAQ;QACR,QAAQ,EAAE,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM;QACjC,SAAS,EAAE,GAAG,CAAC,MAAM;QACrB,WAAW,EAAE,GAAG,CAAC,QAAQ;QACzB,YAAY,EAAE,GAAG,CAAC,SAAS;QAC3B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,OAAO,EAAE;YACP,IAAI,EAAE,GAAG,CAAC,OAAO;YACjB,eAAe;SAChB;KACF,CAAA;AACH,CAAC,CAAA"}
@@ -4,17 +4,16 @@ import { type Subscription } from '@lifi/perps-types';
4
4
  * `WsProviderFactory` constructor for Hyperliquid — pass to
5
5
  * `new PerpsWsClient(client, { wsProviders: { hyperliquid: hyperliquidWsProvider() } })`.
6
6
  *
7
- * Derives the active sub-DEX set from the `markets` list `PerpsWsClient`
8
- * passes in (the provider's own key and `'spot'` are excluded — they
9
- * aren't sub-DEX subscriptions on the HL wire). Higher-order shape mirrors
10
- * `lighterWsProvider(options)` so the two factories register identically.
7
+ * Higher-order shape mirrors `lighterWsProvider(options)` so the two factories
8
+ * register identically.
11
9
  *
12
10
  * @public
13
11
  */
14
12
  export declare const hyperliquidWsProvider: () => WsProviderFactory;
15
13
  /**
16
- * Hyperliquid realtime {@link WsProvider}: multiplexes prices, positions,
17
- * orders, fills and spot balances over a single {@link ReconnectingWebSocket}.
14
+ * Hyperliquid realtime {@link WsProvider}: multiplexes markets context,
15
+ * positions, orders, fills and spot balances over a single
16
+ * {@link ReconnectingWebSocket}.
18
17
  * Construct via {@link hyperliquidWsProvider}.
19
18
  *
20
19
  * `orderUpdates` supports a single address per provider instance: HL delivers
@@ -27,11 +26,12 @@ export declare const hyperliquidWsProvider: () => WsProviderFactory;
27
26
  */
28
27
  export declare class HyperliquidWsProvider extends WsProviderBase<object> {
29
28
  private orderUpdatesKey;
30
- private readonly subDexes;
31
29
  private readonly client;
32
30
  private readonly registry;
33
- private midsBySubDex;
34
- constructor(wsUrl: string, providerKey: string, subDexes: string[], client?: PerpsSDKClient);
31
+ private perpCtxBySubDex;
32
+ private fastCtxByMarketId;
33
+ private fastDecodeChain;
34
+ constructor(wsUrl: string, providerKey: string, client?: PerpsSDKClient);
35
35
  subscribeQuote(params: ProviderGetQuoteParams, onQuote: QuoteListener): Promise<() => void>;
36
36
  protected openChannel(sub: Subscription): Promise<() => void>;
37
37
  /**
@@ -41,15 +41,34 @@ export declare class HyperliquidWsProvider extends WsProviderBase<object> {
41
41
  * so a wallet switch needn't wait out the teardown linger; a live one throws.
42
42
  */
43
43
  private claimOrderUpdatesKey;
44
- /** Build sub-key + payload pairs for each sub-dex allMids subscription. */
45
- private getPriceSubEntries;
44
+ /**
45
+ * Sub-key + payload pairs for the markets-context aggregation: the all-dexs
46
+ * perp asset-context feed (oracle/funding/OI/metadata) plus the compressed
47
+ * `fastAssetCtxs` feed (high-frequency mid + mark across every dex).
48
+ */
49
+ private getMarketsContextSubEntries;
46
50
  protected onClose(): void;
47
51
  protected sendSubscribe(payload: object): void;
48
52
  protected toKey(sub: Subscription): string;
49
53
  private toHlPayload;
50
54
  protected handleMessage(raw: string): void;
51
- private handleAllMids;
52
- /** Latest mid per `Market.id`, merged across sub-dex allMids frames. */
55
+ private handleAllDexsAssetCtxs;
56
+ /**
57
+ * Decode a `fastAssetCtxs` frame (base64 + raw-DEFLATE) and merge its changed
58
+ * coins into the per-market fast-context store. Frames are incremental, so a
59
+ * field absent from this frame keeps its prior value. Decodes are chained to
60
+ * preserve arrival order across the async boundary.
61
+ */
62
+ private handleFastAssetCtxs;
63
+ /**
64
+ * Build the all-markets context map: every perp asset context across sub-dexs
65
+ * (oracle/funding/OI/metadata), with mid + mark overlaid from the
66
+ * high-frequency `fastAssetCtxs` feed. A market present in `fastAssetCtxs` but
67
+ * without an asset-context frame yet (the aggregate feed is slow/idle) still
68
+ * gets a mid+mark entry, so price ticks without waiting on the aggregate.
69
+ */
70
+ private emitMarketsContext;
71
+ /** Latest mid per `Market.id` across the perp ctx feed and `fastAssetCtxs`. */
53
72
  private mergedMids;
54
73
  private handleL2Book;
55
74
  private handleCandle;
@@ -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,EAIL,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAA;AA8B1B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,QAC5B,iBAIH,CAAA;AAEH;;;;;;;;;;;;GAYG;AACH,qBAAa,qBAAsB,SAAQ,cAAc,CAAC,MAAM,CAAC;IAC/D,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IACrD,OAAO,CAAC,YAAY,CAA4C;gBAG9D,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,CAAC,EAAE,cAAc;IAWnB,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;IAgDnE;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAiB5B,2EAA2E;IAC3E,OAAO,CAAC,kBAAkB;cAaP,OAAO,IAAI,IAAI;IAKlC,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM9C,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM;IAmB1C,OAAO,CAAC,WAAW;IAqCnB,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM;IAyDnC,OAAO,CAAC,aAAa;IAYrB,wEAAwE;IACxE,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,YAAY;IAcpB,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,EAKL,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAA;AAkC1B;;;;;;;;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;IAK7E,OAAO,CAAC,iBAAiB,CAAuC;IAGhE,OAAO,CAAC,eAAe,CAAmC;gBAE9C,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;IAmDnE;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAiB5B;;;;OAIG;IACH,OAAO,CAAC,2BAA2B;cAUhB,OAAO,IAAI,IAAI;IAMlC,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM9C,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM;IAmB1C,OAAO,CAAC,WAAW;IAqCnB,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM;IA4DnC,OAAO,CAAC,sBAAsB;IAW9B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAgB3B;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;IA4B1B,+EAA+E;IAC/E,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,kBAAkB;IA0D1B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,+BAA+B;IAkBvC,OAAO,CAAC,eAAe;CAaxB"}
@@ -1,27 +1,23 @@
1
1
  import { getMarketRegistry, isActiveOrderStatus, ReconnectingWebSocket, resolveSubscribeQuote, WsProviderBase, wsLog, } from '@lifi/perps-sdk';
2
2
  import { OrderSide, OrderType, } from '@lifi/perps-types';
3
3
  import { HYPERLIQUID_FEE_TIER_FALLBACK } from '../constants.js';
4
- import { isOpenAssetPosition, isTriggerOrder, mapFill, mapOrderStatus, mapOrderType, mapPosition, priceStepToAggregation, spotAssetFromToken, spotBalance, spotPriceById, } from '../utils/index.js';
4
+ import { decodeFastAssetCtxs, isOpenAssetPosition, isTriggerOrder, mapFill, mapMarketContext, mapOrderStatus, mapOrderType, mapPosition, priceStepToAggregation, spotAssetFromToken, spotBalance, spotPriceById, } from '../utils/index.js';
5
5
  /** HL's `l2Book` returns at most this many levels per side (slow/default mode). */
6
6
  const HL_L2_BOOK_MAX_LEVELS_PER_SIDE = 20;
7
7
  /**
8
8
  * `WsProviderFactory` constructor for Hyperliquid — pass to
9
9
  * `new PerpsWsClient(client, { wsProviders: { hyperliquid: hyperliquidWsProvider() } })`.
10
10
  *
11
- * Derives the active sub-DEX set from the `markets` list `PerpsWsClient`
12
- * passes in (the provider's own key and `'spot'` are excluded — they
13
- * aren't sub-DEX subscriptions on the HL wire). Higher-order shape mirrors
14
- * `lighterWsProvider(options)` so the two factories register identically.
11
+ * Higher-order shape mirrors `lighterWsProvider(options)` so the two factories
12
+ * register identically.
15
13
  *
16
14
  * @public
17
15
  */
18
- export const hyperliquidWsProvider = () => ({ provider, wsUrl, markets, client }) => {
19
- const subDexes = markets.filter((m) => m !== provider && m !== 'spot');
20
- return new HyperliquidWsProvider(wsUrl, provider, subDexes, client);
21
- };
16
+ export const hyperliquidWsProvider = () => ({ provider, wsUrl, client }) => new HyperliquidWsProvider(wsUrl, provider, client);
22
17
  /**
23
- * Hyperliquid realtime {@link WsProvider}: multiplexes prices, positions,
24
- * orders, fills and spot balances over a single {@link ReconnectingWebSocket}.
18
+ * Hyperliquid realtime {@link WsProvider}: multiplexes markets context,
19
+ * positions, orders, fills and spot balances over a single
20
+ * {@link ReconnectingWebSocket}.
25
21
  * Construct via {@link hyperliquidWsProvider}.
26
22
  *
27
23
  * `orderUpdates` supports a single address per provider instance: HL delivers
@@ -34,13 +30,19 @@ export const hyperliquidWsProvider = () => ({ provider, wsUrl, markets, client }
34
30
  */
35
31
  export class HyperliquidWsProvider extends WsProviderBase {
36
32
  orderUpdatesKey;
37
- subDexes;
38
33
  client;
39
34
  registry;
40
- midsBySubDex = new Map();
41
- constructor(wsUrl, providerKey, subDexes, client) {
35
+ perpCtxBySubDex = new Map();
36
+ // Latest mid/mark per `Market.id` from the high-frequency `fastAssetCtxs`
37
+ // feed (all dexes, incl. builder/sub-dex coins). Merged incrementally —
38
+ // frames carry only changed coins — and overlaid onto the slower
39
+ // `allDexsAssetCtxs` context (which supplies oracle/funding/OI/metadata).
40
+ fastCtxByMarketId = {};
41
+ // `fastAssetCtxs` payloads are base64 + raw-DEFLATE; decode is async, so
42
+ // chain decodes to apply incremental frames in arrival order.
43
+ fastDecodeChain = Promise.resolve();
44
+ constructor(wsUrl, providerKey, client) {
42
45
  super(new ReconnectingWebSocket(wsUrl, { pingPayload: '{"method":"ping"}' }), providerKey);
43
- this.subDexes = subDexes;
44
46
  this.client = client;
45
47
  this.registry = client && getMarketRegistry(client, providerKey);
46
48
  }
@@ -59,9 +61,11 @@ export class HyperliquidWsProvider extends WsProviderBase {
59
61
  this.claimOrderUpdatesKey(this.toKey(sub));
60
62
  }
61
63
  await this.registry?.sync();
62
- // Prices require multi-sub-dex allMids subscriptions under one logical key.
63
- if (sub.channel === 'prices') {
64
- const entries = this.getPriceSubEntries();
64
+ // Markets context aggregates the all-dexs perp asset-context feed
65
+ // (oracle, funding, OI, metadata) with the high-frequency `fastAssetCtxs`
66
+ // feed, which supplies fast mid + mark for every market across all dexes.
67
+ if (sub.channel === 'marketsContext') {
68
+ const entries = this.getMarketsContextSubEntries();
65
69
  for (const { subKey, payload } of entries) {
66
70
  await this.registerSub(subKey, payload);
67
71
  }
@@ -71,7 +75,8 @@ export class HyperliquidWsProvider extends WsProviderBase {
71
75
  this.unregisterSub(subKey);
72
76
  this.rws.send(JSON.stringify({ method: 'unsubscribe', subscription: payload }));
73
77
  }
74
- this.midsBySubDex.clear();
78
+ this.perpCtxBySubDex.clear();
79
+ this.fastCtxByMarketId = {};
75
80
  };
76
81
  }
77
82
  // All other channels: single WS subscription per key
@@ -105,21 +110,20 @@ export class HyperliquidWsProvider extends WsProviderBase {
105
110
  }
106
111
  this.orderUpdatesKey = key;
107
112
  }
108
- /** Build sub-key + payload pairs for each sub-dex allMids subscription. */
109
- getPriceSubEntries() {
113
+ /**
114
+ * Sub-key + payload pairs for the markets-context aggregation: the all-dexs
115
+ * perp asset-context feed (oracle/funding/OI/metadata) plus the compressed
116
+ * `fastAssetCtxs` feed (high-frequency mid + mark across every dex).
117
+ */
118
+ getMarketsContextSubEntries() {
110
119
  return [
111
- {
112
- subKey: 'allMids:default',
113
- payload: { type: 'allMids' },
114
- },
115
- ...this.subDexes.map((dex) => ({
116
- subKey: `allMids:${dex}`,
117
- payload: { type: 'allMids', dex },
118
- })),
120
+ { subKey: 'allDexsAssetCtxs', payload: { type: 'allDexsAssetCtxs' } },
121
+ { subKey: 'fastAssetCtxs', payload: { type: 'fastAssetCtxs' } },
119
122
  ];
120
123
  }
121
124
  onClose() {
122
- this.midsBySubDex.clear();
125
+ this.perpCtxBySubDex.clear();
126
+ this.fastCtxByMarketId = {};
123
127
  this.orderUpdatesKey = undefined;
124
128
  }
125
129
  sendSubscribe(payload) {
@@ -127,8 +131,8 @@ export class HyperliquidWsProvider extends WsProviderBase {
127
131
  }
128
132
  toKey(sub) {
129
133
  switch (sub.channel) {
130
- case 'prices':
131
- return 'allMids';
134
+ case 'marketsContext':
135
+ return 'marketsContext';
132
136
  case 'orderbook':
133
137
  return `l2Book:${sub.marketId}`;
134
138
  case 'candle':
@@ -145,10 +149,10 @@ export class HyperliquidWsProvider extends WsProviderBase {
145
149
  }
146
150
  toHlPayload(sub) {
147
151
  switch (sub.channel) {
148
- case 'prices':
149
- // Prices are handled via getPriceSubEntries in subscribe()
150
- // and never reach toHlPayload, but TS requires exhaustive cases.
151
- return { type: 'allMids' };
152
+ case 'marketsContext':
153
+ // Handled via getMarketsContextSubEntries in openChannel; never reaches
154
+ // toHlPayload, but TS requires an exhaustive switch.
155
+ return { type: 'allDexsAssetCtxs' };
152
156
  case 'orderbook':
153
157
  return {
154
158
  type: 'l2Book',
@@ -200,8 +204,11 @@ export class HyperliquidWsProvider extends WsProviderBase {
200
204
  }
201
205
  try {
202
206
  switch (msg.channel) {
203
- case 'allMids':
204
- this.handleAllMids(msg.data);
207
+ case 'allDexsAssetCtxs':
208
+ this.handleAllDexsAssetCtxs(msg.data);
209
+ break;
210
+ case 'fastAssetCtxs':
211
+ this.handleFastAssetCtxs(msg.data);
205
212
  break;
206
213
  case 'l2Book':
207
214
  this.handleL2Book(msg.data);
@@ -227,20 +234,83 @@ export class HyperliquidWsProvider extends WsProviderBase {
227
234
  wsLog.handlerFailure(this.providerKey, error);
228
235
  }
229
236
  }
230
- handleAllMids(data) {
231
- const subDexKey = data.dex || 'default';
232
- this.midsBySubDex.set(subDexKey, data.mids);
233
- const merged = {};
234
- for (const mids of this.midsBySubDex.values()) {
235
- Object.assign(merged, mids);
237
+ handleAllDexsAssetCtxs(data) {
238
+ for (const [dex, ctxs] of data.assetCtxs) {
239
+ const byMarketId = {};
240
+ for (const ctx of ctxs) {
241
+ byMarketId[ctx.coin] = ctx;
242
+ }
243
+ this.perpCtxBySubDex.set(dex || 'default', byMarketId);
236
244
  }
237
- this.emit('allMids', { channel: 'prices', data: merged });
245
+ this.emitMarketsContext();
238
246
  }
239
- /** Latest mid per `Market.id`, merged across sub-dex allMids frames. */
247
+ /**
248
+ * Decode a `fastAssetCtxs` frame (base64 + raw-DEFLATE) and merge its changed
249
+ * coins into the per-market fast-context store. Frames are incremental, so a
250
+ * field absent from this frame keeps its prior value. Decodes are chained to
251
+ * preserve arrival order across the async boundary.
252
+ */
253
+ handleFastAssetCtxs(base64) {
254
+ this.fastDecodeChain = this.fastDecodeChain
255
+ .then(async () => {
256
+ const ctxs = await decodeFastAssetCtxs(base64);
257
+ for (const [marketId, ctx] of Object.entries(ctxs)) {
258
+ const prev = this.fastCtxByMarketId[marketId];
259
+ this.fastCtxByMarketId[marketId] = {
260
+ markPx: 'markPx' in ctx ? ctx.markPx : prev?.markPx,
261
+ midPx: 'midPx' in ctx ? ctx.midPx : prev?.midPx,
262
+ };
263
+ }
264
+ this.emitMarketsContext();
265
+ })
266
+ .catch((error) => wsLog.handlerFailure(this.providerKey, error));
267
+ }
268
+ /**
269
+ * Build the all-markets context map: every perp asset context across sub-dexs
270
+ * (oracle/funding/OI/metadata), with mid + mark overlaid from the
271
+ * high-frequency `fastAssetCtxs` feed. A market present in `fastAssetCtxs` but
272
+ * without an asset-context frame yet (the aggregate feed is slow/idle) still
273
+ * gets a mid+mark entry, so price ticks without waiting on the aggregate.
274
+ */
275
+ emitMarketsContext() {
276
+ const data = {};
277
+ for (const byMarketId of this.perpCtxBySubDex.values()) {
278
+ for (const [marketId, ctx] of Object.entries(byMarketId)) {
279
+ const base = mapMarketContext(marketId, ctx);
280
+ const fast = this.fastCtxByMarketId[marketId];
281
+ data[marketId] = {
282
+ ...base,
283
+ midPrice: fast?.midPx != null ? fast.midPx : base.midPrice,
284
+ markPrice: fast?.markPx != null ? fast.markPx : base.markPrice,
285
+ };
286
+ }
287
+ }
288
+ for (const [marketId, fast] of Object.entries(this.fastCtxByMarketId)) {
289
+ if (data[marketId] !== undefined) {
290
+ continue;
291
+ }
292
+ // No asset-context frame for this market yet — each price stands in for
293
+ // the other where the fast feed carries only one of mid/mark.
294
+ const midPrice = fast.midPx ?? fast.markPx;
295
+ const markPrice = fast.markPx ?? fast.midPx;
296
+ if (midPrice != null && markPrice != null) {
297
+ data[marketId] = { marketId, midPrice, markPrice };
298
+ }
299
+ }
300
+ this.emit('marketsContext', { channel: 'marketsContext', data });
301
+ }
302
+ /** Latest mid per `Market.id` across the perp ctx feed and `fastAssetCtxs`. */
240
303
  mergedMids() {
241
304
  const map = new Map();
242
- for (const mids of this.midsBySubDex.values()) {
243
- for (const [id, mid] of Object.entries(mids)) {
305
+ for (const byMarketId of this.perpCtxBySubDex.values()) {
306
+ for (const [id, ctx] of Object.entries(byMarketId)) {
307
+ const mid = ctx.midPx ?? ctx.markPx;
308
+ map.set(id, Number(mid));
309
+ }
310
+ }
311
+ for (const [id, fast] of Object.entries(this.fastCtxByMarketId)) {
312
+ const mid = fast.midPx ?? fast.markPx;
313
+ if (mid != null) {
244
314
  map.set(id, Number(mid));
245
315
  }
246
316
  }
@@ -375,12 +445,16 @@ const isObject = (v) => typeof v === 'object' && v !== null;
375
445
  * to the dispatch switch, which silently ignores them.
376
446
  */
377
447
  function isValidHlFrame(channel, data) {
448
+ // `fastAssetCtxs` carries a base64 string payload, not an object.
449
+ if (channel === 'fastAssetCtxs') {
450
+ return typeof data === 'string';
451
+ }
378
452
  if (!isObject(data)) {
379
453
  return false;
380
454
  }
381
455
  switch (channel) {
382
- case 'allMids':
383
- return isObject(data.mids);
456
+ case 'allDexsAssetCtxs':
457
+ return Array.isArray(data.assetCtxs);
384
458
  case 'l2Book':
385
459
  return (typeof data.coin === 'string' &&
386
460
  Array.isArray(data.levels) &&