@lifi/perps-sdk-provider-hyperliquid 1.5.3 → 1.5.5

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/services/getFills.d.ts.map +1 -1
  2. package/dist/cjs/services/getFills.js +13 -6
  3. package/dist/cjs/services/getFills.js.map +1 -1
  4. package/dist/cjs/services/getOrders.js +1 -1
  5. package/dist/cjs/services/getOrders.js.map +1 -1
  6. package/dist/cjs/signers/HyperliquidAgentStore.js +1 -1
  7. package/dist/cjs/signers/HyperliquidAgentStore.js.map +1 -1
  8. package/dist/cjs/types/order.d.ts +1 -1
  9. package/dist/cjs/types/order.d.ts.map +1 -1
  10. package/dist/cjs/utils/mapOrder.d.ts.map +1 -1
  11. package/dist/cjs/utils/mapOrder.js +26 -0
  12. package/dist/cjs/utils/mapOrder.js.map +1 -1
  13. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts +2 -0
  14. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  15. package/dist/cjs/websocket/HyperliquidWsProvider.js +59 -38
  16. package/dist/cjs/websocket/HyperliquidWsProvider.js.map +1 -1
  17. package/dist/esm/services/getFills.d.ts +4 -4
  18. package/dist/esm/services/getFills.d.ts.map +1 -1
  19. package/dist/esm/services/getFills.js +19 -9
  20. package/dist/esm/services/getFills.js.map +1 -1
  21. package/dist/esm/services/getOrders.js +1 -1
  22. package/dist/esm/services/getOrders.js.map +1 -1
  23. package/dist/esm/signers/HyperliquidAgentStore.js +1 -1
  24. package/dist/esm/signers/HyperliquidAgentStore.js.map +1 -1
  25. package/dist/esm/types/order.d.ts +1 -1
  26. package/dist/esm/types/order.d.ts.map +1 -1
  27. package/dist/esm/utils/mapOrder.d.ts.map +1 -1
  28. package/dist/esm/utils/mapOrder.js +26 -0
  29. package/dist/esm/utils/mapOrder.js.map +1 -1
  30. package/dist/esm/websocket/HyperliquidWsProvider.d.ts +13 -2
  31. package/dist/esm/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  32. package/dist/esm/websocket/HyperliquidWsProvider.js +75 -44
  33. package/dist/esm/websocket/HyperliquidWsProvider.js.map +1 -1
  34. package/dist/types/services/getFills.d.ts +4 -4
  35. package/dist/types/services/getFills.d.ts.map +1 -1
  36. package/dist/types/types/order.d.ts +1 -1
  37. package/dist/types/types/order.d.ts.map +1 -1
  38. package/dist/types/utils/mapOrder.d.ts.map +1 -1
  39. package/dist/types/websocket/HyperliquidWsProvider.d.ts +13 -2
  40. package/dist/types/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  41. package/package.json +2 -2
  42. package/src/services/getFills.ts +37 -11
  43. package/src/services/getOrders.ts +1 -1
  44. package/src/signers/HyperliquidAgentStore.ts +1 -1
  45. package/src/types/order.ts +1 -1
  46. package/src/utils/mapOrder.ts +26 -0
  47. package/src/websocket/HyperliquidWsProvider.ts +79 -47
@@ -10,7 +10,7 @@ import type { HyperliquidContext } from '../context.js';
10
10
  export interface GetFillsParams {
11
11
  address: Address;
12
12
  limit?: number;
13
- /** `tid` of the oldest fill to return (returned items have `tid < cursor`). */
13
+ /** Opaque pagination cursor from a previous page's `pagination.cursor`. */
14
14
  cursor?: string;
15
15
  /** Inclusive lower bound in ms-since-epoch — switches to the `userFillsByTime` endpoint. */
16
16
  startTime?: number;
@@ -24,9 +24,9 @@ export interface GetFillsParams {
24
24
  * list supplies the display fields; only the fills endpoint is read direct
25
25
  * from HL.
26
26
  *
27
- * Pagination is cursor-based on the fill's `tid`: results with
28
- * `tid < cursor` are kept and the last-page's tail `id` is returned as the
29
- * next cursor.
27
+ * The response is sorted (time desc, `tid` as tiebreaker) before pagination —
28
+ * see {@link compareFillsDesc}. The cursor encodes the last returned fill's
29
+ * `(time, tid)`; the next page keeps fills that sort strictly after it.
30
30
  * @throws {PerpsError} On Hyperliquid REST error, network, or parsing failures.
31
31
  * @public
32
32
  */
@@ -1 +1 @@
1
- {"version":3,"file":"getFills.d.ts","sourceRoot":"","sources":["../../../src/services/getFills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAMnC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAKvD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ,GACnB,oBAAoB,kBAAkB,EACtC,QAAQ,cAAc,EACtB,UAAU,iBAAiB,KAC1B,OAAO,CAAC,aAAa,CAiDvB,CAAA"}
1
+ {"version":3,"file":"getFills.d.ts","sourceRoot":"","sources":["../../../src/services/getFills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAMnC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AASvD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAoBD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ,GACnB,oBAAoB,kBAAkB,EACtC,QAAQ,cAAc,EACtB,UAAU,iBAAiB,KAC1B,OAAO,CAAC,aAAa,CAqDvB,CAAA"}
@@ -13,7 +13,7 @@ export type HlFrontendOpenOrder = {
13
13
  isPositionTpsl: boolean;
14
14
  triggerCondition: string;
15
15
  triggerPx: string;
16
- children: HlFrontendOpenOrder[];
16
+ children?: HlFrontendOpenOrder[];
17
17
  tif: string | null;
18
18
  cloid: string | null;
19
19
  };
@@ -1 +1 @@
1
- {"version":3,"file":"order.d.ts","sourceRoot":"","sources":["../../../src/types/order.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,cAAc,EAAE,OAAO,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,mBAAmB,EAAE,CAAA;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,EAAE,CAAA;AAExD,cAAc;AACd,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAA;QACX,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,EAAE,EAAE,MAAM,CAAA;QACV,OAAO,EAAE,MAAM,CAAA;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,EAAE,OAAO,CAAA;QACnB,SAAS,EAAE,MAAM,CAAA;QACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;QAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;QACpB,gBAAgB,EAAE,MAAM,CAAA;QACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;KACzB,CAAA;IACD,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;CACrB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,qBAAqB,GAC7B,kBAAkB,GAClB;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,CAAA"}
1
+ {"version":3,"file":"order.d.ts","sourceRoot":"","sources":["../../../src/types/order.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,cAAc,EAAE,OAAO,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAChC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,EAAE,CAAA;AAExD,cAAc;AACd,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAA;QACX,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,EAAE,EAAE,MAAM,CAAA;QACV,OAAO,EAAE,MAAM,CAAA;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,EAAE,OAAO,CAAA;QACnB,SAAS,EAAE,MAAM,CAAA;QACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;QAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;QACpB,gBAAgB,EAAE,MAAM,CAAA;QACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;KACzB,CAAA;IACD,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;CACrB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,qBAAqB,GAC7B,kBAAkB,GAClB;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,CAAA"}
@@ -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;;;;;;;;;;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"}
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,SAmDzD,CAAA;AAeD,cAAc;AACd,eAAO,MAAM,QAAQ,GACnB,QAAQ,aAAa,EACrB,QAAQ,aAAa,KACpB,KAsBF,CAAA"}
@@ -30,6 +30,7 @@ export declare class HyperliquidWsProvider extends WsProviderBase<object> {
30
30
  private readonly registry;
31
31
  private perpCtxBySubDex;
32
32
  private spotCtxByMarketId;
33
+ private marketsContextByMarketId;
33
34
  private orderbookKeysByMarketId;
34
35
  private latestOrderbookByMarketId;
35
36
  private fastCtxByMarketId;
@@ -65,6 +66,7 @@ export declare class HyperliquidWsProvider extends WsProviderBase<object> {
65
66
  protected handleMessage(raw: string): void;
66
67
  private handleAllDexsAssetCtxs;
67
68
  private handlePac;
69
+ /** Merge perp asset contexts in place; returns the marketIds this frame touched. */
68
70
  private mergePerpAssetCtxEntries;
69
71
  private handleSac;
70
72
  private spotMarketIdBySacKey;
@@ -76,10 +78,19 @@ export declare class HyperliquidWsProvider extends WsProviderBase<object> {
76
78
  */
77
79
  private handleFastAssetCtxs;
78
80
  /**
79
- * Build the all-markets context map from perp + spot asset contexts, with mid
80
- * + mark overlaid from `fastAssetCtxs`.
81
+ * Emit the all-markets context map, re-mapping only the markets the incoming
82
+ * frame touched. Each emission is a fresh snapshot object that carries
83
+ * untouched entries over by reference, so a listener holding an earlier
84
+ * snapshot never observes later-frame mutation.
81
85
  */
82
86
  private emitMarketsContext;
87
+ /**
88
+ * Context for one market from the cached feeds: spot asset context when it
89
+ * maps, else perp asset context with mid + mark overlaid from
90
+ * `fastAssetCtxs`, else the fast feed alone — each price standing in for the
91
+ * other where it carries only one of mid/mark.
92
+ */
93
+ private computeMarketContext;
83
94
  private handleActiveAssetCtx;
84
95
  private handleActiveSpotAssetCtx;
85
96
  /** Latest mid per `Market.id` across asset-context and fast feeds. */
@@ -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;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;IAkBvB,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,wBAAwB,CAAoC;IACpE,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;IAgEnE;;;;;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;IAclC,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;IAM9B,OAAO,CAAC,SAAS;IAYjB,oFAAoF;IACpF,OAAO,CAAC,wBAAwB;IAyBhC,OAAO,CAAC,SAAS;IAsBjB,OAAO,CAAC,oBAAoB;IAe5B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAgB3B;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IA6B5B,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;IAmC5B,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,kBAAkB;IAyD1B,OAAO,CAAC,eAAe;IAkBvB,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.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "Hyperliquid provider plugin for @lifi/perps-sdk",
5
5
  "homepage": "https://github.com/lifinance/perps-sdk",
6
6
  "bugs": {
@@ -51,7 +51,7 @@
51
51
  "@lifi/perps-types": "^1.6.0"
52
52
  },
53
53
  "peerDependencies": {
54
- "@lifi/perps-sdk": "^1.5.3"
54
+ "@lifi/perps-sdk": "^1.5.6"
55
55
  },
56
56
  "publishConfig": {
57
57
  "access": "public"
@@ -7,7 +7,11 @@ import {
7
7
  PROVIDER_KEY,
8
8
  } from '../constants.js'
9
9
  import type { HyperliquidContext } from '../context.js'
10
- import type { HlUserFills, HlUserFillsByTime } from '../types/index.js'
10
+ import type {
11
+ HlUserFill,
12
+ HlUserFills,
13
+ HlUserFillsByTime,
14
+ } from '../types/index.js'
11
15
  import { mapFill } from '../utils/index.js'
12
16
  import { hlInfoOptions, infoRequest } from '../utils/infoClient.js'
13
17
 
@@ -19,7 +23,7 @@ import { hlInfoOptions, infoRequest } from '../utils/infoClient.js'
19
23
  export interface GetFillsParams {
20
24
  address: Address
21
25
  limit?: number
22
- /** `tid` of the oldest fill to return (returned items have `tid < cursor`). */
26
+ /** Opaque pagination cursor from a previous page's `pagination.cursor`. */
23
27
  cursor?: string
24
28
  /** Inclusive lower bound in ms-since-epoch — switches to the `userFillsByTime` endpoint. */
25
29
  startTime?: number
@@ -27,6 +31,24 @@ export interface GetFillsParams {
27
31
  endTime?: number
28
32
  }
29
33
 
34
+ interface FillCursor {
35
+ time: number
36
+ tid: number
37
+ }
38
+
39
+ // HL's docs specify neither newest-first ordering nor monotonic `tid` for
40
+ // `userFills`/`userFillsByTime`, so pagination can't rely on upstream order —
41
+ // it must impose its own (time desc, tid desc as tiebreaker for same-time fills).
42
+ const compareFillsDesc = (a: FillCursor, b: FillCursor): number =>
43
+ b.time - a.time || b.tid - a.tid
44
+
45
+ const encodeCursor = (fill: FillCursor): string => `${fill.time}:${fill.tid}`
46
+
47
+ const decodeCursor = (cursor: string): FillCursor => {
48
+ const [time, tid] = cursor.split(':').map(Number)
49
+ return { time, tid }
50
+ }
51
+
30
52
  /**
31
53
  * Fetch the user's fills history. When `startTime` or `endTime` is provided,
32
54
  * Hyperliquid's `userFillsByTime` endpoint is used; otherwise the unbounded
@@ -34,9 +56,9 @@ export interface GetFillsParams {
34
56
  * list supplies the display fields; only the fills endpoint is read direct
35
57
  * from HL.
36
58
  *
37
- * Pagination is cursor-based on the fill's `tid`: results with
38
- * `tid < cursor` are kept and the last-page's tail `id` is returned as the
39
- * next cursor.
59
+ * The response is sorted (time desc, `tid` as tiebreaker) before pagination —
60
+ * see {@link compareFillsDesc}. The cursor encodes the last returned fill's
61
+ * `(time, tid)`; the next page keeps fills that sort strictly after it.
40
62
  * @throws {PerpsError} On Hyperliquid REST error, network, or parsing failures.
41
63
  * @public
42
64
  */
@@ -74,15 +96,19 @@ export const getFills = async (
74
96
  infoOpts
75
97
  )
76
98
 
99
+ const sorted = [...allFills].sort(compareFillsDesc)
100
+
77
101
  const filtered =
78
102
  params.cursor === undefined
79
- ? allFills
80
- : allFills.filter((f) => f.tid < Number.parseInt(params.cursor!, 10))
103
+ ? sorted
104
+ : sorted.filter(
105
+ (f) => compareFillsDesc(f, decodeCursor(params.cursor!)) > 0
106
+ )
81
107
 
82
108
  const hasMore = filtered.length > limit
83
- const items = filtered
84
- .slice(0, limit)
85
- .map((f) => mapFill(f, registry.require(f.coin)))
109
+ const page = filtered.slice(0, limit)
110
+ const items = page.map((f) => mapFill(f, registry.require(f.coin)))
111
+ const lastPageFill: HlUserFill | undefined = page[page.length - 1]
86
112
 
87
113
  return {
88
114
  provider: PROVIDER_KEY,
@@ -90,7 +116,7 @@ export const getFills = async (
90
116
  pagination: {
91
117
  limit,
92
118
  hasMore,
93
- cursor: items.length > 0 ? items[items.length - 1].id : undefined,
119
+ cursor: lastPageFill ? encodeCursor(lastPageFill) : undefined,
94
120
  },
95
121
  }
96
122
  }
@@ -64,7 +64,7 @@ export const getOrders = async (
64
64
 
65
65
  const childOids = new Set<number>()
66
66
  for (const o of raw) {
67
- for (const child of o.children) {
67
+ for (const child of o.children ?? []) {
68
68
  childOids.add(child.oid)
69
69
  }
70
70
  }
@@ -115,7 +115,7 @@ export class HyperliquidAgentStore {
115
115
  return await this.get(address)
116
116
  } catch (error) {
117
117
  if (
118
- error instanceof PerpsError &&
118
+ !(error instanceof PerpsError) ||
119
119
  error.message !== PerpsErrorMessage.AgentNotFound
120
120
  ) {
121
121
  throw error
@@ -15,7 +15,7 @@ export type HlFrontendOpenOrder = {
15
15
  isPositionTpsl: boolean
16
16
  triggerCondition: string
17
17
  triggerPx: string
18
- children: HlFrontendOpenOrder[]
18
+ children?: HlFrontendOpenOrder[]
19
19
  tif: string | null
20
20
  cloid: string | null
21
21
  }
@@ -188,6 +188,32 @@ export const mapStatusReason = (status: string): string | undefined => {
188
188
  return 'Order rejected: notional value below the minimum trade size.'
189
189
  case 'delistedCanceled':
190
190
  return 'Order cancelled: market has been delisted.'
191
+ case 'perpMarginRejected':
192
+ return 'Order rejected: insufficient margin.'
193
+ case 'reduceOnlyRejected':
194
+ return 'Order rejected: would not reduce your position.'
195
+ case 'badAloPxRejected':
196
+ return 'Order rejected: post-only order would have matched immediately.'
197
+ case 'badTriggerPxRejected':
198
+ return 'Order rejected: invalid take-profit/stop-loss trigger price.'
199
+ case 'marketOrderNoLiquidityRejected':
200
+ return 'Order rejected: not enough liquidity for the market order.'
201
+ case 'oracleRejected':
202
+ return 'Order rejected: price too far from the oracle price.'
203
+ case 'vaultWithdrawalCanceled':
204
+ return 'Order cancelled: a vault withdrawal occurred.'
205
+ case 'openInterestCapCanceled':
206
+ return 'Order cancelled: too aggressive while open interest was at its cap.'
207
+ case 'positionIncreaseAtOpenInterestCapRejected':
208
+ case 'positionFlipAtOpenInterestCapRejected':
209
+ case 'openInterestIncreaseRejected':
210
+ return 'Order rejected: open interest is at its cap.'
211
+ case 'tooAggressiveAtOpenInterestCapRejected':
212
+ return 'Order rejected: price too aggressive while open interest was at its cap.'
213
+ case 'insufficientSpotBalanceRejected':
214
+ return 'Order rejected: insufficient spot balance.'
215
+ case 'perpMaxPositionRejected':
216
+ return 'Order rejected: exceeds the maximum position size for the current leverage tier.'
191
217
  default:
192
218
  return undefined
193
219
  }
@@ -99,6 +99,7 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
99
99
  private readonly registry: MarketRegistry | undefined
100
100
  private perpCtxBySubDex = new Map<string, Record<string, HlWsPerpAssetCtx>>()
101
101
  private spotCtxByMarketId: Record<string, HlWsSpotAssetCtx> = {}
102
+ private marketsContextByMarketId: Record<string, MarketContext> = {}
102
103
  private orderbookKeysByMarketId = new Map<string, Set<string>>()
103
104
  private latestOrderbookByMarketId = new Map<string, OrderbookResponse>()
104
105
  // Latest mid/mark per `Market.id` from the high-frequency `fastAssetCtxs`
@@ -173,6 +174,7 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
173
174
  this.perpCtxBySubDex.clear()
174
175
  this.spotCtxByMarketId = {}
175
176
  this.fastCtxByMarketId = {}
177
+ this.marketsContextByMarketId = {}
176
178
  }
177
179
  }
178
180
 
@@ -292,6 +294,7 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
292
294
  this.perpCtxBySubDex.clear()
293
295
  this.spotCtxByMarketId = {}
294
296
  this.fastCtxByMarketId = {}
297
+ this.marketsContextByMarketId = {}
295
298
  this.orderbookKeysByMarketId.clear()
296
299
  this.latestOrderbookByMarketId.clear()
297
300
  this.fastDecodeChain = Promise.resolve()
@@ -483,41 +486,47 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
483
486
  }
484
487
 
485
488
  private handleAllDexsAssetCtxs(data: HlWsAllDexsAssetCtxsData) {
486
- this.mergePerpAssetCtxEntries(allDexsAssetCtxEntries(data))
487
- this.emitMarketsContext()
489
+ this.emitMarketsContext(
490
+ this.mergePerpAssetCtxEntries(allDexsAssetCtxEntries(data))
491
+ )
488
492
  }
489
493
 
490
494
  private handlePac(base64: string) {
491
495
  this.pacDecodeChain = this.pacDecodeChain
492
496
  .then(async () => {
493
- this.mergePerpAssetCtxEntries(
494
- await decodeCompressedJson<HlWsPacData>(base64)
497
+ this.emitMarketsContext(
498
+ this.mergePerpAssetCtxEntries(
499
+ await decodeCompressedJson<HlWsPacData>(base64)
500
+ )
495
501
  )
496
- this.emitMarketsContext()
497
502
  })
498
503
  .catch((error) => wsLog.handlerFailure(this.providerKey, error))
499
504
  }
500
505
 
506
+ /** Merge perp asset contexts in place; returns the marketIds this frame touched. */
501
507
  private mergePerpAssetCtxEntries(
502
508
  entries: [string, HlWsPerpAssetCtxPayload[]][]
503
- ) {
509
+ ): Set<string> {
510
+ const touched = new Set<string>()
504
511
  for (const [dex, ctxs] of entries) {
505
- const previous = this.perpCtxBySubDex.get(dex || 'default')
506
- const byMarketId: Record<string, HlWsPerpAssetCtx> = {
507
- ...(previous ?? {}),
512
+ let byMarketId = this.perpCtxBySubDex.get(dex || 'default')
513
+ if (byMarketId === undefined) {
514
+ byMarketId = {}
515
+ this.perpCtxBySubDex.set(dex || 'default', byMarketId)
508
516
  }
509
517
  for (const ctx of ctxs) {
510
518
  const marketId = ctx.coin
511
519
  if (marketId === undefined) {
512
520
  continue
513
521
  }
514
- const merged = mergePerpAssetCtx(previous?.[marketId], marketId, ctx)
522
+ const merged = mergePerpAssetCtx(byMarketId[marketId], marketId, ctx)
515
523
  if (merged !== undefined) {
516
524
  byMarketId[marketId] = merged
525
+ touched.add(marketId)
517
526
  }
518
527
  }
519
- this.perpCtxBySubDex.set(dex || 'default', byMarketId)
520
528
  }
529
+ return touched
521
530
  }
522
531
 
523
532
  private handleSac(base64: string) {
@@ -525,6 +534,7 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
525
534
  .then(async () => {
526
535
  const spotMarketIdBySacKey = this.spotMarketIdBySacKey()
527
536
  const ctxs = await decodeCompressedJson<HlWsSacData>(base64)
537
+ const touched = new Set<string>()
528
538
  for (const [sacKey, ctx] of Object.entries(ctxs)) {
529
539
  const marketId = spotMarketIdBySacKey.get(sacKey)
530
540
  if (marketId === undefined) {
@@ -534,8 +544,9 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
534
544
  ...this.spotCtxByMarketId[marketId],
535
545
  ...ctx,
536
546
  }
547
+ touched.add(marketId)
537
548
  }
538
- this.emitMarketsContext()
549
+ this.emitMarketsContext(touched)
539
550
  })
540
551
  .catch((error) => wsLog.handlerFailure(this.providerKey, error))
541
552
  }
@@ -572,52 +583,66 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
572
583
  midPx: 'midPx' in ctx ? ctx.midPx : prev?.midPx,
573
584
  }
574
585
  }
575
- this.emitMarketsContext()
586
+ this.emitMarketsContext(Object.keys(ctxs))
576
587
  })
577
588
  .catch((error) => wsLog.handlerFailure(this.providerKey, error))
578
589
  }
579
590
 
580
591
  /**
581
- * Build the all-markets context map from perp + spot asset contexts, with mid
582
- * + mark overlaid from `fastAssetCtxs`.
592
+ * Emit the all-markets context map, re-mapping only the markets the incoming
593
+ * frame touched. Each emission is a fresh snapshot object that carries
594
+ * untouched entries over by reference, so a listener holding an earlier
595
+ * snapshot never observes later-frame mutation.
583
596
  */
584
- private emitMarketsContext() {
585
- const data: Record<string, MarketContext> = {}
586
- for (const byMarketId of this.perpCtxBySubDex.values()) {
587
- for (const [marketId, ctx] of Object.entries(byMarketId)) {
588
- const base = mapMarketContext(marketId, ctx)
589
- const fast = this.fastCtxByMarketId[marketId]
590
- data[marketId] = {
591
- ...base,
592
- midPrice: fast?.midPx != null ? fast.midPx : base.midPrice,
593
- markPrice: fast?.markPx != null ? fast.markPx : base.markPrice,
594
- }
595
- }
597
+ private emitMarketsContext(touchedMarketIds: Iterable<string>) {
598
+ const data: Record<string, MarketContext> = {
599
+ ...this.marketsContextByMarketId,
596
600
  }
597
- for (const [marketId, ctx] of Object.entries(this.spotCtxByMarketId)) {
598
- const context = mapSpotMarketContext(
599
- marketId,
600
- ctx,
601
- this.fastCtxByMarketId[marketId]
602
- )
601
+ for (const marketId of touchedMarketIds) {
602
+ const context = this.computeMarketContext(marketId)
603
603
  if (context === undefined) {
604
- continue
604
+ delete data[marketId]
605
+ } else {
606
+ data[marketId] = context
605
607
  }
606
- data[marketId] = context
607
608
  }
608
- for (const [marketId, fast] of Object.entries(this.fastCtxByMarketId)) {
609
- if (data[marketId] !== undefined) {
610
- continue
609
+ this.marketsContextByMarketId = data
610
+ this.emit('marketsContext', { channel: 'marketsContext', data })
611
+ }
612
+
613
+ /**
614
+ * Context for one market from the cached feeds: spot asset context when it
615
+ * maps, else perp asset context with mid + mark overlaid from
616
+ * `fastAssetCtxs`, else the fast feed alone — each price standing in for the
617
+ * other where it carries only one of mid/mark.
618
+ */
619
+ private computeMarketContext(marketId: string): MarketContext | undefined {
620
+ const fast = this.fastCtxByMarketId[marketId]
621
+ const spotCtx = this.spotCtxByMarketId[marketId]
622
+ if (spotCtx !== undefined) {
623
+ const context = mapSpotMarketContext(marketId, spotCtx, fast)
624
+ if (context !== undefined) {
625
+ return context
611
626
  }
612
- // No asset-context frame for this market yet — each price stands in for
613
- // the other where the fast feed carries only one of mid/mark.
614
- const midPrice = fast.midPx ?? fast.markPx
615
- const markPrice = fast.markPx ?? fast.midPx
616
- if (midPrice != null && markPrice != null) {
617
- data[marketId] = { marketId, midPrice, markPrice }
627
+ }
628
+ let perpCtx: HlWsPerpAssetCtx | undefined
629
+ for (const byMarketId of this.perpCtxBySubDex.values()) {
630
+ perpCtx = byMarketId[marketId] ?? perpCtx
631
+ }
632
+ if (perpCtx !== undefined) {
633
+ const base = mapMarketContext(marketId, perpCtx)
634
+ return {
635
+ ...base,
636
+ midPrice: fast?.midPx != null ? fast.midPx : base.midPrice,
637
+ markPrice: fast?.markPx != null ? fast.markPx : base.markPrice,
618
638
  }
619
639
  }
620
- this.emit('marketsContext', { channel: 'marketsContext', data })
640
+ const midPrice = fast?.midPx ?? fast?.markPx
641
+ const markPrice = fast?.markPx ?? fast?.midPx
642
+ if (midPrice != null && markPrice != null) {
643
+ return { marketId, midPrice, markPrice }
644
+ }
645
+ return undefined
621
646
  }
622
647
 
623
648
  private handleActiveAssetCtx(data: HlWsActiveAssetCtxData, raw: string) {
@@ -725,6 +750,14 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
725
750
  return
726
751
  }
727
752
 
753
+ // Compact deltas decode asynchronously via `orderbookDecodeChain`, so one
754
+ // can resolve after a newer frame (sync snapshot/update, or a later delta)
755
+ // has already been applied. A delta no newer than the maintained book is
756
+ // stale; applying it would overwrite fresher state with older levels.
757
+ if (delta.t <= previous.timestamp) {
758
+ return
759
+ }
760
+
728
761
  const book: OrderbookResponse = {
729
762
  provider: this.providerKey,
730
763
  marketId: delta.c,
@@ -806,8 +839,7 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
806
839
  continue
807
840
  }
808
841
  const type = mapOrderType(o.orderType)
809
- // A miss warns once and schedules a cooldown-gated registry refetch
810
- // (the id may have listed after the snapshot); skip just this item.
842
+ // Unknown market id (absent from the synced snapshot); skip just this item.
811
843
  const market = this.registry?.get(o.coin)
812
844
  if (!market) {
813
845
  continue