@lightconexyz/lightcone-sdk 0.7.1 → 0.8.1-rc.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 (109) hide show
  1. package/README.md +20 -0
  2. package/dist/auth/client.d.ts +3 -3
  3. package/dist/auth/client.d.ts.map +1 -1
  4. package/dist/auth/client.js +19 -38
  5. package/dist/auth/client.js.map +1 -1
  6. package/dist/auth/index.d.ts +95 -54
  7. package/dist/auth/index.d.ts.map +1 -1
  8. package/dist/auth/index.js +82 -7
  9. package/dist/auth/index.js.map +1 -1
  10. package/dist/client.d.ts +0 -2
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +0 -4
  13. package/dist/client.js.map +1 -1
  14. package/dist/domain/index.d.ts +0 -1
  15. package/dist/domain/index.d.ts.map +1 -1
  16. package/dist/domain/index.js +1 -2
  17. package/dist/domain/index.js.map +1 -1
  18. package/dist/domain/market/tokens.d.ts +2 -0
  19. package/dist/domain/market/tokens.d.ts.map +1 -1
  20. package/dist/domain/market/tokens.js +8 -0
  21. package/dist/domain/market/tokens.js.map +1 -1
  22. package/dist/domain/market/wire.d.ts +1 -0
  23. package/dist/domain/market/wire.d.ts.map +1 -1
  24. package/dist/domain/market/wire.js.map +1 -1
  25. package/dist/domain/metrics/client.d.ts +8 -2
  26. package/dist/domain/metrics/client.d.ts.map +1 -1
  27. package/dist/domain/metrics/client.js +48 -1
  28. package/dist/domain/metrics/client.js.map +1 -1
  29. package/dist/domain/metrics/wire.d.ts +107 -4
  30. package/dist/domain/metrics/wire.d.ts.map +1 -1
  31. package/dist/domain/metrics/wire.js.map +1 -1
  32. package/dist/domain/order/client.d.ts +2 -0
  33. package/dist/domain/order/client.d.ts.map +1 -1
  34. package/dist/domain/order/client.js.map +1 -1
  35. package/dist/domain/orderbook/aggregation.d.ts +50 -0
  36. package/dist/domain/orderbook/aggregation.d.ts.map +1 -0
  37. package/dist/domain/orderbook/aggregation.js +85 -0
  38. package/dist/domain/orderbook/aggregation.js.map +1 -0
  39. package/dist/domain/orderbook/client.d.ts +11 -1
  40. package/dist/domain/orderbook/client.d.ts.map +1 -1
  41. package/dist/domain/orderbook/client.js +24 -3
  42. package/dist/domain/orderbook/client.js.map +1 -1
  43. package/dist/domain/orderbook/index.d.ts +1 -0
  44. package/dist/domain/orderbook/index.d.ts.map +1 -1
  45. package/dist/domain/orderbook/index.js +1 -0
  46. package/dist/domain/orderbook/index.js.map +1 -1
  47. package/dist/domain/orderbook/state.d.ts +29 -31
  48. package/dist/domain/orderbook/state.d.ts.map +1 -1
  49. package/dist/domain/orderbook/state.js +20 -67
  50. package/dist/domain/orderbook/state.js.map +1 -1
  51. package/dist/domain/orderbook/wire.d.ts +32 -0
  52. package/dist/domain/orderbook/wire.d.ts.map +1 -1
  53. package/dist/domain/orderbook/wire.js.map +1 -1
  54. package/dist/env.d.ts +18 -3
  55. package/dist/env.d.ts.map +1 -1
  56. package/dist/env.js +28 -4
  57. package/dist/env.js.map +1 -1
  58. package/dist/http/client.d.ts +1 -4
  59. package/dist/http/client.d.ts.map +1 -1
  60. package/dist/http/client.js +56 -22
  61. package/dist/http/client.js.map +1 -1
  62. package/dist/index.d.ts +3 -2
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +8 -1
  65. package/dist/index.js.map +1 -1
  66. package/dist/prelude.d.ts +9 -8
  67. package/dist/prelude.d.ts.map +1 -1
  68. package/dist/prelude.js +20 -4
  69. package/dist/prelude.js.map +1 -1
  70. package/dist/shared/fmt/index.d.ts +1 -0
  71. package/dist/shared/fmt/index.d.ts.map +1 -1
  72. package/dist/shared/fmt/index.js +2 -1
  73. package/dist/shared/fmt/index.js.map +1 -1
  74. package/dist/shared/fmt/str.d.ts +8 -0
  75. package/dist/shared/fmt/str.d.ts.map +1 -0
  76. package/dist/shared/fmt/str.js +17 -0
  77. package/dist/shared/fmt/str.js.map +1 -0
  78. package/dist/shared/index.d.ts +1 -1
  79. package/dist/shared/index.d.ts.map +1 -1
  80. package/dist/shared/index.js +11 -1
  81. package/dist/shared/index.js.map +1 -1
  82. package/dist/shared/rejection.d.ts.map +1 -1
  83. package/dist/shared/rejection.js +11 -0
  84. package/dist/shared/rejection.js.map +1 -1
  85. package/dist/shared/types.d.ts +41 -0
  86. package/dist/shared/types.d.ts.map +1 -1
  87. package/dist/shared/types.js +85 -1
  88. package/dist/shared/types.js.map +1 -1
  89. package/dist/ws/index.d.ts +25 -2
  90. package/dist/ws/index.d.ts.map +1 -1
  91. package/dist/ws/index.js +23 -2
  92. package/dist/ws/index.js.map +1 -1
  93. package/dist/ws/subscriptions.d.ts +12 -0
  94. package/dist/ws/subscriptions.d.ts.map +1 -1
  95. package/dist/ws/subscriptions.js +14 -4
  96. package/dist/ws/subscriptions.js.map +1 -1
  97. package/package.json +1 -1
  98. package/dist/domain/admin/client.d.ts +0 -63
  99. package/dist/domain/admin/client.d.ts.map +0 -1
  100. package/dist/domain/admin/client.js +0 -234
  101. package/dist/domain/admin/client.js.map +0 -1
  102. package/dist/domain/admin/index.d.ts +0 -16
  103. package/dist/domain/admin/index.d.ts.map +0 -1
  104. package/dist/domain/admin/index.js +0 -19
  105. package/dist/domain/admin/index.js.map +0 -1
  106. package/dist/domain/admin/wire.d.ts +0 -450
  107. package/dist/domain/admin/wire.d.ts.map +0 -1
  108. package/dist/domain/admin/wire.js +0 -3
  109. package/dist/domain/admin/wire.js.map +0 -1
@@ -1,12 +1,22 @@
1
1
  import { Transaction, type PublicKey, type TransactionInstruction } from "@solana/web3.js";
2
2
  import type { ClientContext } from "../../context";
3
3
  import type { CloseOrderbookAltParams, CloseOrderbookParams, Orderbook as ProgramOrderbook } from "../../program/types";
4
+ import { type BookAggregation } from "./aggregation";
4
5
  import type { OrderbookDepthResponse } from "./wire";
5
6
  export declare class Orderbooks {
6
7
  private readonly client;
7
8
  constructor(client: ClientContext);
8
9
  pda(mintA: PublicKey, mintB: PublicKey): PublicKey;
9
- get(orderbookId: string, depth?: number): Promise<OrderbookDepthResponse>;
10
+ /**
11
+ * Get live orderbook depth, optionally aggregated (Hyperliquid-style).
12
+ *
13
+ * `depth` is capped server-side at 20 levels per side (omitted, `0`, or
14
+ * `>20` all serve 20). Invalid aggregation combinations throw client-side
15
+ * before any request is made (the server would 400 with
16
+ * `INVALID_ORDERBOOK_QUERY`), and unknown query params are rejected
17
+ * server-side — only `depth`, `nSigFigs`, and `mantissa` are ever sent.
18
+ */
19
+ get(orderbookId: string, depth?: number, aggregation?: BookAggregation): Promise<OrderbookDepthResponse>;
10
20
  closeOrderbookAltIx(params: CloseOrderbookAltParams): TransactionInstruction;
11
21
  closeOrderbookIx(params: CloseOrderbookParams): TransactionInstruction;
12
22
  closeOrderbookAltTx(params: CloseOrderbookAltParams): Transaction;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/domain/orderbook/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAUnD,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACpB,SAAS,IAAI,gBAAgB,EAC9B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAC;AAErD,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAIlD,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS;IAM5C,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAQ/E,mBAAmB,CAAC,MAAM,EAAE,uBAAuB,GAAG,sBAAsB;IAI5E,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,GAAG,sBAAsB;IAItE,mBAAmB,CAAC,MAAM,EAAE,uBAAuB,GAAG,WAAW;IAKjE,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,GAAG,WAAW;IAOrD,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAWhF"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/domain/orderbook/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAUnD,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACpB,SAAS,IAAI,gBAAgB,EAC9B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAC;AAErD,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAIlD,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS;IAMlD;;;;;;;;OAQG;IACG,GAAG,CACP,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,MAAM,EACd,WAAW,GAAE,eAAgC,GAC5C,OAAO,CAAC,sBAAsB,CAAC;IAmBlC,mBAAmB,CAAC,MAAM,EAAE,uBAAuB,GAAG,sBAAsB;IAI5E,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,GAAG,sBAAsB;IAItE,mBAAmB,CAAC,MAAM,EAAE,uBAAuB,GAAG,WAAW;IAKjE,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,GAAG,WAAW;IAOrD,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAWhF"}
@@ -8,6 +8,7 @@ const http_1 = require("../../http");
8
8
  const instructions_1 = require("../../program/instructions");
9
9
  const pda_1 = require("../../program/pda");
10
10
  const accounts_1 = require("../../program/accounts");
11
+ const aggregation_1 = require("./aggregation");
11
12
  class Orderbooks {
12
13
  client;
13
14
  constructor(client) {
@@ -18,9 +19,29 @@ class Orderbooks {
18
19
  return (0, pda_1.getOrderbookPda)(mintA, mintB, this.client.programId)[0];
19
20
  }
20
21
  // ── HTTP methods ─────────────────────────────────────────────────────
21
- async get(orderbookId, depth) {
22
- const query = depth !== undefined ? `?depth=${depth}` : "";
23
- const url = `${this.client.http.baseUrl()}/api/orderbook/${encodeURIComponent(orderbookId)}${query}`;
22
+ /**
23
+ * Get live orderbook depth, optionally aggregated (Hyperliquid-style).
24
+ *
25
+ * `depth` is capped server-side at 20 levels per side (omitted, `0`, or
26
+ * `>20` all serve 20). Invalid aggregation combinations throw client-side
27
+ * before any request is made (the server would 400 with
28
+ * `INVALID_ORDERBOOK_QUERY`), and unknown query params are rejected
29
+ * server-side — only `depth`, `nSigFigs`, and `mantissa` are ever sent.
30
+ */
31
+ async get(orderbookId, depth, aggregation = aggregation_1.FULL_PRECISION) {
32
+ const validated = (0, aggregation_1.validateAggregation)(aggregation);
33
+ const query = new URLSearchParams();
34
+ if (depth !== undefined) {
35
+ query.set("depth", String(depth));
36
+ }
37
+ if (validated.nSigFigs !== undefined) {
38
+ query.set("nSigFigs", String(validated.nSigFigs));
39
+ }
40
+ if (validated.mantissa !== undefined) {
41
+ query.set("mantissa", String(validated.mantissa));
42
+ }
43
+ const queryString = query.toString();
44
+ const url = `${this.client.http.baseUrl()}/api/orderbook/${encodeURIComponent(orderbookId)}${queryString ? `?${queryString}` : ""}`;
24
45
  return this.client.http.get(url, http_1.RetryPolicy.Idempotent);
25
46
  }
26
47
  // ── On-chain transaction builders ────────────────────────────────────
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/domain/orderbook/client.ts"],"names":[],"mappings":";;;AAAA,6CAA2F;AAE3F,2CAAkD;AAClD,+CAAsD;AACtD,qCAAyC;AACzC,6DAGoC;AACpC,2CAAoD;AACpD,qDAA6F;AAQ7F,MAAa,UAAU;IACQ;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,wEAAwE;IAExE,GAAG,CAAC,KAAgB,EAAE,KAAgB;QACpC,OAAO,IAAA,qBAAe,EAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,wEAAwE;IAExE,KAAK,CAAC,GAAG,CAAC,WAAmB,EAAE,KAAc;QAC3C,MAAM,KAAK,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,kBAAkB,CAAC,WAAW,CAAC,GAAG,KAAK,EAAE,CAAC;QACrG,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAyB,GAAG,EAAE,kBAAW,CAAC,UAAU,CAAC,CAAC;IACnF,CAAC;IAED,wEAAwE;IAExE,mBAAmB,CAAC,MAA+B;QACjD,OAAO,IAAA,uCAAwB,EAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC;IAED,gBAAgB,CAAC,MAA4B;QAC3C,OAAO,IAAA,oCAAqB,EAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC9D,CAAC;IAED,mBAAmB,CAAC,MAA+B;QACjD,MAAM,EAAE,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC5C,OAAO,IAAI,qBAAW,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,gBAAgB,CAAC,MAA4B;QAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACzC,OAAO,IAAI,qBAAW,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,uEAAuE;IAEvE,KAAK,CAAC,UAAU,CAAC,KAAgB,EAAE,KAAgB;QACjD,MAAM,UAAU,GAAG,IAAA,2BAAiB,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClD,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC5C,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;QAClE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,uBAAe,CAAC,eAAe,CACnC,iBAAiB,KAAK,CAAC,QAAQ,EAAE,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,CAC1D,CAAC;QACJ,CAAC;QACD,OAAO,IAAA,+BAA2B,EAAC,WAAW,CAAC,IAAc,CAAC,CAAC;IACjE,CAAC;CACF;AAlDD,gCAkDC","sourcesContent":["import { Transaction, type PublicKey, type TransactionInstruction } from \"@solana/web3.js\";\nimport type { ClientContext } from \"../../context\";\nimport { requireConnection } from \"../../context\";\nimport { ProgramSdkError } from \"../../program/error\";\nimport { RetryPolicy } from \"../../http\";\nimport {\n buildCloseOrderbookAltIx,\n buildCloseOrderbookIx,\n} from \"../../program/instructions\";\nimport { getOrderbookPda } from \"../../program/pda\";\nimport { deserializeOrderbook as deserializeProgramOrderbook } from \"../../program/accounts\";\nimport type {\n CloseOrderbookAltParams,\n CloseOrderbookParams,\n Orderbook as ProgramOrderbook,\n} from \"../../program/types\";\nimport type { OrderbookDepthResponse } from \"./wire\";\n\nexport class Orderbooks {\n constructor(private readonly client: ClientContext) {}\n\n // ── PDA helpers ──────────────────────────────────────────────────────\n\n pda(mintA: PublicKey, mintB: PublicKey): PublicKey {\n return getOrderbookPda(mintA, mintB, this.client.programId)[0];\n }\n\n // ── HTTP methods ─────────────────────────────────────────────────────\n\n async get(orderbookId: string, depth?: number): Promise<OrderbookDepthResponse> {\n const query = depth !== undefined ? `?depth=${depth}` : \"\";\n const url = `${this.client.http.baseUrl()}/api/orderbook/${encodeURIComponent(orderbookId)}${query}`;\n return this.client.http.get<OrderbookDepthResponse>(url, RetryPolicy.Idempotent);\n }\n\n // ── On-chain transaction builders ────────────────────────────────────\n\n closeOrderbookAltIx(params: CloseOrderbookAltParams): TransactionInstruction {\n return buildCloseOrderbookAltIx(params, this.client.programId);\n }\n\n closeOrderbookIx(params: CloseOrderbookParams): TransactionInstruction {\n return buildCloseOrderbookIx(params, this.client.programId);\n }\n\n closeOrderbookAltTx(params: CloseOrderbookAltParams): Transaction {\n const ix = this.closeOrderbookAltIx(params);\n return new Transaction({ feePayer: params.operator }).add(ix);\n }\n\n closeOrderbookTx(params: CloseOrderbookParams): Transaction {\n const ix = this.closeOrderbookIx(params);\n return new Transaction({ feePayer: params.operator }).add(ix);\n }\n\n // ── On-chain account fetchers (require Connection) ──────────────────\n\n async getOnchain(mintA: PublicKey, mintB: PublicKey): Promise<ProgramOrderbook> {\n const connection = requireConnection(this.client);\n const orderbookPda = this.pda(mintA, mintB);\n const accountInfo = await connection.getAccountInfo(orderbookPda);\n if (!accountInfo) {\n throw ProgramSdkError.accountNotFound(\n `Orderbook for ${mintA.toBase58()} / ${mintB.toBase58()}`\n );\n }\n return deserializeProgramOrderbook(accountInfo.data as Buffer);\n }\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/domain/orderbook/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { Transaction, type PublicKey, type TransactionInstruction } from \"@solana/web3.js\";\nimport type { ClientContext } from \"../../context\";\nimport { requireConnection } from \"../../context\";\nimport { ProgramSdkError } from \"../../program/error\";\nimport { RetryPolicy } from \"../../http\";\nimport {\n buildCloseOrderbookAltIx,\n buildCloseOrderbookIx,\n} from \"../../program/instructions\";\nimport { getOrderbookPda } from \"../../program/pda\";\nimport { deserializeOrderbook as deserializeProgramOrderbook } from \"../../program/accounts\";\nimport type {\n CloseOrderbookAltParams,\n CloseOrderbookParams,\n Orderbook as ProgramOrderbook,\n} from \"../../program/types\";\nimport {\n FULL_PRECISION,\n validateAggregation,\n type BookAggregation,\n} from \"./aggregation\";\nimport type { OrderbookDepthResponse } from \"./wire\";\n\nexport class Orderbooks {\n constructor(private readonly client: ClientContext) {}\n\n // ── PDA helpers ──────────────────────────────────────────────────────\n\n pda(mintA: PublicKey, mintB: PublicKey): PublicKey {\n return getOrderbookPda(mintA, mintB, this.client.programId)[0];\n }\n\n // ── HTTP methods ─────────────────────────────────────────────────────\n\n /**\n * Get live orderbook depth, optionally aggregated (Hyperliquid-style).\n *\n * `depth` is capped server-side at 20 levels per side (omitted, `0`, or\n * `>20` all serve 20). Invalid aggregation combinations throw client-side\n * before any request is made (the server would 400 with\n * `INVALID_ORDERBOOK_QUERY`), and unknown query params are rejected\n * server-side — only `depth`, `nSigFigs`, and `mantissa` are ever sent.\n */\n async get(\n orderbookId: string,\n depth?: number,\n aggregation: BookAggregation = FULL_PRECISION\n ): Promise<OrderbookDepthResponse> {\n const validated = validateAggregation(aggregation);\n const query = new URLSearchParams();\n if (depth !== undefined) {\n query.set(\"depth\", String(depth));\n }\n if (validated.nSigFigs !== undefined) {\n query.set(\"nSigFigs\", String(validated.nSigFigs));\n }\n if (validated.mantissa !== undefined) {\n query.set(\"mantissa\", String(validated.mantissa));\n }\n const queryString = query.toString();\n const url = `${this.client.http.baseUrl()}/api/orderbook/${encodeURIComponent(orderbookId)}${queryString ? `?${queryString}` : \"\"}`;\n return this.client.http.get<OrderbookDepthResponse>(url, RetryPolicy.Idempotent);\n }\n\n // ── On-chain transaction builders ────────────────────────────────────\n\n closeOrderbookAltIx(params: CloseOrderbookAltParams): TransactionInstruction {\n return buildCloseOrderbookAltIx(params, this.client.programId);\n }\n\n closeOrderbookIx(params: CloseOrderbookParams): TransactionInstruction {\n return buildCloseOrderbookIx(params, this.client.programId);\n }\n\n closeOrderbookAltTx(params: CloseOrderbookAltParams): Transaction {\n const ix = this.closeOrderbookAltIx(params);\n return new Transaction({ feePayer: params.operator }).add(ix);\n }\n\n closeOrderbookTx(params: CloseOrderbookParams): Transaction {\n const ix = this.closeOrderbookIx(params);\n return new Transaction({ feePayer: params.operator }).add(ix);\n }\n\n // ── On-chain account fetchers (require Connection) ──────────────────\n\n async getOnchain(mintA: PublicKey, mintB: PublicKey): Promise<ProgramOrderbook> {\n const connection = requireConnection(this.client);\n const orderbookPda = this.pda(mintA, mintB);\n const accountInfo = await connection.getAccountInfo(orderbookPda);\n if (!accountInfo) {\n throw ProgramSdkError.accountNotFound(\n `Orderbook for ${mintA.toBase58()} / ${mintB.toBase58()}`\n );\n }\n return deserializeProgramOrderbook(accountInfo.data as Buffer);\n }\n}\n"]}
@@ -3,6 +3,7 @@ import { PublicKey } from "@solana/web3.js";
3
3
  import type { OrderBookId, PubkeyStr } from "../../shared";
4
4
  import type { OrderbookDecimals } from "../../shared/scaling";
5
5
  import type { ConditionalToken } from "../market";
6
+ export * from "./aggregation";
6
7
  export * from "./client";
7
8
  export * from "./wire";
8
9
  export * from "./state";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/domain/orderbook/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,SAAS,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,gBAAgB,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,SAAS,CAAC,YAAY,EAAE,OAAO,EAAE,gBAAgB,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAO5F;AAED,wBAAgB,MAAM,CACpB,iBAAiB,EAAE,OAAO,EAC1B,gBAAgB,EAAE,OAAO,GACxB,aAAa,CAef;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,iBAAiB,CAUxE;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,CAE9D;AAED,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,CAEhE;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,CAEjE;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;gBAEf,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;CAMnD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/domain/orderbook/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,SAAS,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,gBAAgB,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,SAAS,CAAC,YAAY,EAAE,OAAO,EAAE,gBAAgB,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAO5F;AAED,wBAAgB,MAAM,CACpB,iBAAiB,EAAE,OAAO,EAC1B,gBAAgB,EAAE,OAAO,GACxB,aAAa,CAef;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,iBAAiB,CAUxE;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,CAE9D;AAED,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,CAEhE;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,CAEjE;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;gBAEf,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;CAMnD"}
@@ -22,6 +22,7 @@ exports.orderBookMarket = orderBookMarket;
22
22
  exports.orderBookBaseMint = orderBookBaseMint;
23
23
  exports.orderBookQuoteMint = orderBookQuoteMint;
24
24
  const web3_js_1 = require("@solana/web3.js");
25
+ __exportStar(require("./aggregation"), exports);
25
26
  __exportStar(require("./client"), exports);
26
27
  __exportStar(require("./wire"), exports);
27
28
  __exportStar(require("./state"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/domain/orderbook/index.ts"],"names":[],"mappings":"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","sourcesContent":["import Decimal from \"decimal.js\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport type { OrderBookId, PubkeyStr } from \"../../shared\";\nimport type { OrderbookDecimals } from \"../../shared/scaling\";\nimport type { ConditionalToken } from \"../market\";\n\nexport * from \"./client\";\nexport * from \"./wire\";\nexport * from \"./state\";\nexport * from \"./ticker\";\nexport { orderBookPairFromWire } from \"./convert\";\n\nexport interface OrderBookPair {\n id: number;\n marketPubkey: PubkeyStr;\n orderbookId: OrderBookId;\n base: ConditionalToken;\n quote: ConditionalToken;\n outcomeIndex: number;\n tickSize: number;\n totalBids: number;\n totalAsks: number;\n lastTradePrice?: string;\n lastTradeTime?: Date;\n active: boolean;\n}\n\nexport interface OutcomeImpact {\n sign: string;\n pct: number;\n dollar: string;\n isPositive: boolean;\n}\n\nexport function impactPct(depositPrice: Decimal, conditionalPrice: Decimal): [number, string] {\n if (depositPrice.isZero() || conditionalPrice.isZero()) {\n return [0, \"\"];\n }\n\n const value = conditionalPrice.minus(depositPrice).div(depositPrice).mul(100);\n return [value.toNumber(), value.greaterThan(0) ? \"+\" : \"\"];\n}\n\nexport function impact(\n depositAssetPrice: Decimal,\n conditionalPrice: Decimal\n): OutcomeImpact {\n if (depositAssetPrice.isZero()) {\n return { sign: \"\", pct: 0, dollar: \"0\", isPositive: false };\n }\n\n const pctDecimal = conditionalPrice.minus(depositAssetPrice).div(depositAssetPrice).mul(100);\n const pct = pctDecimal.toNumber();\n const dollar = conditionalPrice.minus(depositAssetPrice).abs().toString();\n\n return {\n sign: pct > 0 ? \"+\" : \"-\",\n pct: Math.abs(pct),\n dollar,\n isPositive: pct > 0,\n };\n}\n\n/**\n * Derive scaling decimals from an orderbook pair's token metadata.\n *\n * No REST call needed — decimals are computed from the base/quote token objects.\n */\nexport function orderbookDecimals(pair: OrderBookPair): OrderbookDecimals {\n const baseDecimals = pair.base.decimals;\n const quoteDecimals = pair.quote.decimals;\n return {\n orderbookId: pair.orderbookId,\n baseDecimals,\n quoteDecimals,\n priceDecimals: Math.max(0, 6 + quoteDecimals - baseDecimals),\n tickSize: BigInt(Math.max(pair.tickSize, 0)),\n };\n}\n\n/** Return the market as a `PublicKey`. */\nexport function orderBookMarket(pair: OrderBookPair): PublicKey {\n return new PublicKey(pair.marketPubkey);\n}\n\n/** Return the base conditional-token mint as a `PublicKey`. */\nexport function orderBookBaseMint(pair: OrderBookPair): PublicKey {\n return new PublicKey(pair.base.pubkey);\n}\n\n/** Return the quote conditional-token mint as a `PublicKey`. */\nexport function orderBookQuoteMint(pair: OrderBookPair): PublicKey {\n return new PublicKey(pair.quote.pubkey);\n}\n\nexport class OrderBookValidationError extends Error {\n readonly orderbookId: string;\n readonly details: string[];\n\n constructor(orderbookId: string, details: string[]) {\n super(`OrderBook validation errors (${orderbookId}): ${details.join(\"; \")}`);\n this.name = \"OrderBookValidationError\";\n this.orderbookId = orderbookId;\n this.details = details;\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/domain/orderbook/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAmCA,8BAOC;AAED,wBAkBC;AAOD,8CAUC;AAGD,0CAEC;AAGD,8CAEC;AAGD,gDAEC;AA7FD,6CAA4C;AAK5C,gDAA8B;AAC9B,2CAAyB;AACzB,yCAAuB;AACvB,0CAAwB;AACxB,2CAAyB;AACzB,qCAAkD;AAAzC,gHAAA,qBAAqB,OAAA;AAwB9B,SAAgB,SAAS,CAAC,YAAqB,EAAE,gBAAyB;IACxE,IAAI,YAAY,CAAC,MAAM,EAAE,IAAI,gBAAgB,CAAC,MAAM,EAAE,EAAE,CAAC;QACvD,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9E,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,SAAgB,MAAM,CACpB,iBAA0B,EAC1B,gBAAyB;IAEzB,IAAI,iBAAiB,CAAC,MAAM,EAAE,EAAE,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC9D,CAAC;IAED,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7F,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAE1E,OAAO;QACL,IAAI,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;QACzB,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QAClB,MAAM;QACN,UAAU,EAAE,GAAG,GAAG,CAAC;KACpB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,iBAAiB,CAAC,IAAmB;IACnD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;IACxC,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC1C,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,YAAY;QACZ,aAAa;QACb,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,aAAa,GAAG,YAAY,CAAC;QAC5D,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED,0CAA0C;AAC1C,SAAgB,eAAe,CAAC,IAAmB;IACjD,OAAO,IAAI,mBAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AAC1C,CAAC;AAED,+DAA+D;AAC/D,SAAgB,iBAAiB,CAAC,IAAmB;IACnD,OAAO,IAAI,mBAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACzC,CAAC;AAED,gEAAgE;AAChE,SAAgB,kBAAkB,CAAC,IAAmB;IACpD,OAAO,IAAI,mBAAS,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED,MAAa,wBAAyB,SAAQ,KAAK;IACxC,WAAW,CAAS;IACpB,OAAO,CAAW;IAE3B,YAAY,WAAmB,EAAE,OAAiB;QAChD,KAAK,CAAC,gCAAgC,WAAW,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAVD,4DAUC","sourcesContent":["import Decimal from \"decimal.js\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport type { OrderBookId, PubkeyStr } from \"../../shared\";\nimport type { OrderbookDecimals } from \"../../shared/scaling\";\nimport type { ConditionalToken } from \"../market\";\n\nexport * from \"./aggregation\";\nexport * from \"./client\";\nexport * from \"./wire\";\nexport * from \"./state\";\nexport * from \"./ticker\";\nexport { orderBookPairFromWire } from \"./convert\";\n\nexport interface OrderBookPair {\n id: number;\n marketPubkey: PubkeyStr;\n orderbookId: OrderBookId;\n base: ConditionalToken;\n quote: ConditionalToken;\n outcomeIndex: number;\n tickSize: number;\n totalBids: number;\n totalAsks: number;\n lastTradePrice?: string;\n lastTradeTime?: Date;\n active: boolean;\n}\n\nexport interface OutcomeImpact {\n sign: string;\n pct: number;\n dollar: string;\n isPositive: boolean;\n}\n\nexport function impactPct(depositPrice: Decimal, conditionalPrice: Decimal): [number, string] {\n if (depositPrice.isZero() || conditionalPrice.isZero()) {\n return [0, \"\"];\n }\n\n const value = conditionalPrice.minus(depositPrice).div(depositPrice).mul(100);\n return [value.toNumber(), value.greaterThan(0) ? \"+\" : \"\"];\n}\n\nexport function impact(\n depositAssetPrice: Decimal,\n conditionalPrice: Decimal\n): OutcomeImpact {\n if (depositAssetPrice.isZero()) {\n return { sign: \"\", pct: 0, dollar: \"0\", isPositive: false };\n }\n\n const pctDecimal = conditionalPrice.minus(depositAssetPrice).div(depositAssetPrice).mul(100);\n const pct = pctDecimal.toNumber();\n const dollar = conditionalPrice.minus(depositAssetPrice).abs().toString();\n\n return {\n sign: pct > 0 ? \"+\" : \"-\",\n pct: Math.abs(pct),\n dollar,\n isPositive: pct > 0,\n };\n}\n\n/**\n * Derive scaling decimals from an orderbook pair's token metadata.\n *\n * No REST call needed — decimals are computed from the base/quote token objects.\n */\nexport function orderbookDecimals(pair: OrderBookPair): OrderbookDecimals {\n const baseDecimals = pair.base.decimals;\n const quoteDecimals = pair.quote.decimals;\n return {\n orderbookId: pair.orderbookId,\n baseDecimals,\n quoteDecimals,\n priceDecimals: Math.max(0, 6 + quoteDecimals - baseDecimals),\n tickSize: BigInt(Math.max(pair.tickSize, 0)),\n };\n}\n\n/** Return the market as a `PublicKey`. */\nexport function orderBookMarket(pair: OrderBookPair): PublicKey {\n return new PublicKey(pair.marketPubkey);\n}\n\n/** Return the base conditional-token mint as a `PublicKey`. */\nexport function orderBookBaseMint(pair: OrderBookPair): PublicKey {\n return new PublicKey(pair.base.pubkey);\n}\n\n/** Return the quote conditional-token mint as a `PublicKey`. */\nexport function orderBookQuoteMint(pair: OrderBookPair): PublicKey {\n return new PublicKey(pair.quote.pubkey);\n}\n\nexport class OrderBookValidationError extends Error {\n readonly orderbookId: string;\n readonly details: string[];\n\n constructor(orderbookId: string, details: string[]) {\n super(`OrderBook validation errors (${orderbookId}): ${details.join(\"; \")}`);\n this.name = \"OrderBookValidationError\";\n this.orderbookId = orderbookId;\n this.details = details;\n }\n}\n"]}
@@ -1,54 +1,52 @@
1
1
  import type { OrderBookId } from "../../shared";
2
2
  import type { OrderBook } from "./wire";
3
+ /**
4
+ * The `book_update` stream is snapshot-only: every data frame carries the
5
+ * full top-20 levels per side and replaces the previous book wholesale
6
+ * (last-write-wins). Consumers holding multiple aggregation views of one
7
+ * orderbook on the same connection key their `OrderbookState` instances by
8
+ * `(orderbook_id, aggregation)` using
9
+ * `aggregationFromFrame(book.n_sig_figs, book.mantissa)`.
10
+ */
3
11
  export type OrderbookApplyResult = {
4
12
  kind: "applied";
5
- } | {
6
- kind: "ignored";
7
- reason: OrderbookIgnoreReason;
8
13
  } | {
9
14
  kind: "refresh_required";
10
15
  reason: OrderbookRefreshReason;
11
16
  };
12
- export type OrderbookIgnoreReason = {
13
- kind: "invalid_delta_sequence";
14
- got: number;
15
- } | {
16
- kind: "stale_delta";
17
- current: number;
18
- got: number;
19
- } | {
20
- kind: "already_awaiting_snapshot";
21
- got: number;
22
- };
23
- export type OrderbookRefreshReason = {
24
- kind: "missing_snapshot";
25
- got: number;
26
- } | {
27
- kind: "sequence_gap";
28
- expected: number;
29
- got: number;
30
- } | {
17
+ export type OrderbookRefreshReason =
18
+ /**
19
+ * The backend explicitly requested a resync: unsubscribe and re-subscribe
20
+ * with the same parameters (including aggregation) to receive a fresh
21
+ * snapshot.
22
+ */
23
+ {
31
24
  kind: "server_resync";
32
- got: number;
33
25
  };
34
26
  export declare class OrderbookState {
35
27
  readonly orderbookId: OrderBookId;
28
+ /**
29
+ * Projection version of the last applied frame. Strictly increasing but
30
+ * non-contiguous server-side (conflation skips versions), and the initial
31
+ * snapshot after every (re)subscribe is `seq: 0` — informational only,
32
+ * never used to gate frames.
33
+ */
36
34
  seq: number;
37
35
  private readonly bidsMap;
38
36
  private readonly asksMap;
39
37
  private cachedBestBid;
40
38
  private cachedBestAsk;
41
- private hasSnapshot;
42
- private awaitingSnapshot;
43
39
  constructor(orderbookId: OrderBookId);
44
40
  /**
45
- * Apply a WS orderbook message (snapshot replaces, delta merges).
41
+ * Apply a WS orderbook frame (snapshot-only stream, last-write-wins).
46
42
  *
47
- * Server resync messages take precedence and return `refresh_required`.
48
- * Otherwise, snapshots are applied and deltas with a `seq` at or below the
49
- * current value are ignored to prevent stale updates. Deltas that skip one
50
- * or more expected sequence values are rejected so callers can refresh from
51
- * a fresh snapshot instead of mutating a corrupted book.
43
+ * `resync` frames take precedence and leave the book untouched — the
44
+ * caller must re-subscribe with the same parameters. Every other data
45
+ * frame is a full snapshot by contract and replaces the book wholesale
46
+ * (the `is_snapshot` flag is not consulted), including the `seq: 0`
47
+ * initial snapshot delivered after every (re)subscribe: gating on `seq`
48
+ * would freeze the book after a resync or aggregation change, so `seq` is
49
+ * stored as informational only.
52
50
  */
53
51
  apply(book: OrderBook): OrderbookApplyResult;
54
52
  bids(): ReadonlyMap<string, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/domain/orderbook/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAExC,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,CAAC;AAEjE,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,2BAA2B,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3C,qBAAa,cAAc;IACzB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,gBAAgB,CAAU;gBAEtB,WAAW,EAAE,WAAW;IAWpC;;;;;;;;OAQG;IACH,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,oBAAoB;IAkF5C,IAAI,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;IAInC,IAAI,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;IAInC,OAAO,IAAI,MAAM,GAAG,SAAS;IAe7B,OAAO,IAAI,MAAM,GAAG,SAAS;IAc7B,QAAQ,IAAI,MAAM,GAAG,SAAS;IAU9B,MAAM,IAAI,MAAM,GAAG,SAAS;IAU5B,OAAO,IAAI,OAAO;IAIlB,KAAK,IAAI,IAAI;CASd"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/domain/orderbook/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAExC;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,CAAC;AAEjE,MAAM,MAAM,sBAAsB;AAChC;;;;GAIG;AACH;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAE5B,qBAAa,cAAc;IACzB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,aAAa,CAA4B;gBAErC,WAAW,EAAE,WAAW;IASpC;;;;;;;;;;OAUG;IACH,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,oBAAoB;IA2B5C,IAAI,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;IAInC,IAAI,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;IAInC,OAAO,IAAI,MAAM,GAAG,SAAS;IAe7B,OAAO,IAAI,MAAM,GAAG,SAAS;IAc7B,QAAQ,IAAI,MAAM,GAAG,SAAS;IAU9B,MAAM,IAAI,MAAM,GAAG,SAAS;IAU5B,OAAO,IAAI,OAAO;IAIlB,KAAK,IAAI,IAAI;CAOd"}
@@ -7,13 +7,17 @@ exports.OrderbookState = void 0;
7
7
  const decimal_js_1 = __importDefault(require("decimal.js"));
8
8
  class OrderbookState {
9
9
  orderbookId;
10
+ /**
11
+ * Projection version of the last applied frame. Strictly increasing but
12
+ * non-contiguous server-side (conflation skips versions), and the initial
13
+ * snapshot after every (re)subscribe is `seq: 0` — informational only,
14
+ * never used to gate frames.
15
+ */
10
16
  seq;
11
17
  bidsMap;
12
18
  asksMap;
13
19
  cachedBestBid;
14
20
  cachedBestAsk;
15
- hasSnapshot;
16
- awaitingSnapshot;
17
21
  constructor(orderbookId) {
18
22
  this.orderbookId = orderbookId;
19
23
  this.seq = 0;
@@ -21,87 +25,38 @@ class OrderbookState {
21
25
  this.asksMap = new Map();
22
26
  this.cachedBestBid = null;
23
27
  this.cachedBestAsk = null;
24
- this.hasSnapshot = false;
25
- this.awaitingSnapshot = false;
26
28
  }
27
29
  /**
28
- * Apply a WS orderbook message (snapshot replaces, delta merges).
30
+ * Apply a WS orderbook frame (snapshot-only stream, last-write-wins).
29
31
  *
30
- * Server resync messages take precedence and return `refresh_required`.
31
- * Otherwise, snapshots are applied and deltas with a `seq` at or below the
32
- * current value are ignored to prevent stale updates. Deltas that skip one
33
- * or more expected sequence values are rejected so callers can refresh from
34
- * a fresh snapshot instead of mutating a corrupted book.
32
+ * `resync` frames take precedence and leave the book untouched — the
33
+ * caller must re-subscribe with the same parameters. Every other data
34
+ * frame is a full snapshot by contract and replaces the book wholesale
35
+ * (the `is_snapshot` flag is not consulted), including the `seq: 0`
36
+ * initial snapshot delivered after every (re)subscribe: gating on `seq`
37
+ * would freeze the book after a resync or aggregation change, so `seq` is
38
+ * stored as informational only.
35
39
  */
36
40
  apply(book) {
37
- const seq = book.seq ?? 0;
38
41
  if (book.resync) {
39
- this.awaitingSnapshot = true;
40
42
  return {
41
43
  kind: "refresh_required",
42
- reason: { kind: "server_resync", got: seq },
44
+ reason: { kind: "server_resync" },
43
45
  };
44
46
  }
45
- if (book.is_snapshot) {
46
- this.bidsMap.clear();
47
- this.asksMap.clear();
48
- this.hasSnapshot = true;
49
- this.awaitingSnapshot = false;
50
- }
51
- else {
52
- if (this.awaitingSnapshot) {
53
- return {
54
- kind: "ignored",
55
- reason: { kind: "already_awaiting_snapshot", got: seq },
56
- };
57
- }
58
- // The backend sends snapshots with seq=0 and starts delta seq at 1.
59
- // A delta with seq=0 means it has no valid sequence, so drop it.
60
- if (seq <= 0) {
61
- return {
62
- kind: "ignored",
63
- reason: { kind: "invalid_delta_sequence", got: seq },
64
- };
65
- }
66
- if (!this.hasSnapshot) {
67
- this.awaitingSnapshot = true;
68
- return {
69
- kind: "refresh_required",
70
- reason: { kind: "missing_snapshot", got: seq },
71
- };
72
- }
73
- if (seq <= this.seq) {
74
- return {
75
- kind: "ignored",
76
- reason: { kind: "stale_delta", current: this.seq, got: seq },
77
- };
78
- }
79
- const expected = this.seq + 1;
80
- if (seq !== expected) {
81
- this.awaitingSnapshot = true;
82
- return {
83
- kind: "refresh_required",
84
- reason: { kind: "sequence_gap", expected, got: seq },
85
- };
86
- }
87
- }
88
- this.seq = seq;
47
+ this.bidsMap.clear();
48
+ this.asksMap.clear();
89
49
  for (const level of book.bids) {
90
- if (new decimal_js_1.default(level.size).isZero()) {
91
- this.bidsMap.delete(level.price);
92
- }
93
- else {
50
+ if (!new decimal_js_1.default(level.size).isZero()) {
94
51
  this.bidsMap.set(level.price, level.size);
95
52
  }
96
53
  }
97
54
  for (const level of book.asks) {
98
- if (new decimal_js_1.default(level.size).isZero()) {
99
- this.asksMap.delete(level.price);
100
- }
101
- else {
55
+ if (!new decimal_js_1.default(level.size).isZero()) {
102
56
  this.asksMap.set(level.price, level.size);
103
57
  }
104
58
  }
59
+ this.seq = book.seq ?? 0;
105
60
  this.cachedBestBid = null;
106
61
  this.cachedBestAsk = null;
107
62
  return { kind: "applied" };
@@ -164,8 +119,6 @@ class OrderbookState {
164
119
  this.seq = 0;
165
120
  this.cachedBestBid = null;
166
121
  this.cachedBestAsk = null;
167
- this.hasSnapshot = false;
168
- this.awaitingSnapshot = false;
169
122
  }
170
123
  }
171
124
  exports.OrderbookState = OrderbookState;
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/domain/orderbook/state.ts"],"names":[],"mappings":";;;;;;AAAA,4DAAiC;AAmBjC,MAAa,cAAc;IAChB,WAAW,CAAc;IAClC,GAAG,CAAS;IACK,OAAO,CAAsB;IAC7B,OAAO,CAAsB;IACtC,aAAa,CAA4B;IACzC,aAAa,CAA4B;IACzC,WAAW,CAAU;IACrB,gBAAgB,CAAU;IAElC,YAAY,WAAwB;QAClC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;QACb,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAChC,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,IAAe;QACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAE1B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;YAC7B,OAAO;gBACL,IAAI,EAAE,kBAAkB;gBACxB,MAAM,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,GAAG,EAAE;aAC5C,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1B,OAAO;oBACL,IAAI,EAAE,SAAS;oBACf,MAAM,EAAE,EAAE,IAAI,EAAE,2BAA2B,EAAE,GAAG,EAAE,GAAG,EAAE;iBACxD,CAAC;YACJ,CAAC;YAED,oEAAoE;YACpE,iEAAiE;YACjE,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;gBACb,OAAO;oBACL,IAAI,EAAE,SAAS;oBACf,MAAM,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,GAAG,EAAE,GAAG,EAAE;iBACrD,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;gBAC7B,OAAO;oBACL,IAAI,EAAE,kBAAkB;oBACxB,MAAM,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,GAAG,EAAE;iBAC/C,CAAC;YACJ,CAAC;YAED,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;gBACpB,OAAO;oBACL,IAAI,EAAE,SAAS;oBACf,MAAM,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;iBAC7D,CAAC;YACJ,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;YAC9B,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;gBACrB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;gBAC7B,OAAO;oBACL,IAAI,EAAE,kBAAkB;oBACxB,MAAM,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE;iBACrD,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QAEf,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,IAAI,oBAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;gBACrC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,IAAI,oBAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;gBACrC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAE1B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC7B,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC,aAAa,CAAC;QAC5B,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;aAC3C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,oBAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,oBAAO,CAAC,CAAC,CAAC,CAAC,CAAC;aAClD,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACV,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC;QAC5B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC,aAAa,CAAC;QAC5B,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;aAC3C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,oBAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,oBAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC;QAC5B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,QAAQ;QACN,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACjB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,oBAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACtD,CAAC;IAED,MAAM;QACJ,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACjB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,oBAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;IAChD,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;QACb,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAChC,CAAC;CACF;AAtLD,wCAsLC","sourcesContent":["import Decimal from \"decimal.js\";\nimport type { OrderBookId } from \"../../shared\";\nimport type { OrderBook } from \"./wire\";\n\nexport type OrderbookApplyResult =\n | { kind: \"applied\" }\n | { kind: \"ignored\"; reason: OrderbookIgnoreReason }\n | { kind: \"refresh_required\"; reason: OrderbookRefreshReason };\n\nexport type OrderbookIgnoreReason =\n | { kind: \"invalid_delta_sequence\"; got: number }\n | { kind: \"stale_delta\"; current: number; got: number }\n | { kind: \"already_awaiting_snapshot\"; got: number };\n\nexport type OrderbookRefreshReason =\n | { kind: \"missing_snapshot\"; got: number }\n | { kind: \"sequence_gap\"; expected: number; got: number }\n | { kind: \"server_resync\"; got: number };\n\nexport class OrderbookState {\n readonly orderbookId: OrderBookId;\n seq: number;\n private readonly bidsMap: Map<string, string>;\n private readonly asksMap: Map<string, string>;\n private cachedBestBid: string | undefined | null;\n private cachedBestAsk: string | undefined | null;\n private hasSnapshot: boolean;\n private awaitingSnapshot: boolean;\n\n constructor(orderbookId: OrderBookId) {\n this.orderbookId = orderbookId;\n this.seq = 0;\n this.bidsMap = new Map();\n this.asksMap = new Map();\n this.cachedBestBid = null;\n this.cachedBestAsk = null;\n this.hasSnapshot = false;\n this.awaitingSnapshot = false;\n }\n\n /**\n * Apply a WS orderbook message (snapshot replaces, delta merges).\n *\n * Server resync messages take precedence and return `refresh_required`.\n * Otherwise, snapshots are applied and deltas with a `seq` at or below the\n * current value are ignored to prevent stale updates. Deltas that skip one\n * or more expected sequence values are rejected so callers can refresh from\n * a fresh snapshot instead of mutating a corrupted book.\n */\n apply(book: OrderBook): OrderbookApplyResult {\n const seq = book.seq ?? 0;\n\n if (book.resync) {\n this.awaitingSnapshot = true;\n return {\n kind: \"refresh_required\",\n reason: { kind: \"server_resync\", got: seq },\n };\n }\n\n if (book.is_snapshot) {\n this.bidsMap.clear();\n this.asksMap.clear();\n this.hasSnapshot = true;\n this.awaitingSnapshot = false;\n } else {\n if (this.awaitingSnapshot) {\n return {\n kind: \"ignored\",\n reason: { kind: \"already_awaiting_snapshot\", got: seq },\n };\n }\n\n // The backend sends snapshots with seq=0 and starts delta seq at 1.\n // A delta with seq=0 means it has no valid sequence, so drop it.\n if (seq <= 0) {\n return {\n kind: \"ignored\",\n reason: { kind: \"invalid_delta_sequence\", got: seq },\n };\n }\n\n if (!this.hasSnapshot) {\n this.awaitingSnapshot = true;\n return {\n kind: \"refresh_required\",\n reason: { kind: \"missing_snapshot\", got: seq },\n };\n }\n\n if (seq <= this.seq) {\n return {\n kind: \"ignored\",\n reason: { kind: \"stale_delta\", current: this.seq, got: seq },\n };\n }\n\n const expected = this.seq + 1;\n if (seq !== expected) {\n this.awaitingSnapshot = true;\n return {\n kind: \"refresh_required\",\n reason: { kind: \"sequence_gap\", expected, got: seq },\n };\n }\n }\n\n this.seq = seq;\n\n for (const level of book.bids) {\n if (new Decimal(level.size).isZero()) {\n this.bidsMap.delete(level.price);\n } else {\n this.bidsMap.set(level.price, level.size);\n }\n }\n\n for (const level of book.asks) {\n if (new Decimal(level.size).isZero()) {\n this.asksMap.delete(level.price);\n } else {\n this.asksMap.set(level.price, level.size);\n }\n }\n\n this.cachedBestBid = null;\n this.cachedBestAsk = null;\n\n return { kind: \"applied\" };\n }\n\n bids(): ReadonlyMap<string, string> {\n return this.bidsMap;\n }\n\n asks(): ReadonlyMap<string, string> {\n return this.asksMap;\n }\n\n bestBid(): string | undefined {\n if (this.cachedBestBid !== null) {\n return this.cachedBestBid;\n }\n if (this.bidsMap.size === 0) {\n this.cachedBestBid = undefined;\n return undefined;\n }\n const result = Array.from(this.bidsMap.keys())\n .sort((a, b) => new Decimal(a).cmp(new Decimal(b)))\n .at(-1);\n this.cachedBestBid = result;\n return result;\n }\n\n bestAsk(): string | undefined {\n if (this.cachedBestAsk !== null) {\n return this.cachedBestAsk;\n }\n if (this.asksMap.size === 0) {\n this.cachedBestAsk = undefined;\n return undefined;\n }\n const result = Array.from(this.asksMap.keys())\n .sort((a, b) => new Decimal(a).cmp(new Decimal(b)))[0];\n this.cachedBestAsk = result;\n return result;\n }\n\n midPrice(): string | undefined {\n const bid = this.bestBid();\n const ask = this.bestAsk();\n if (!bid || !ask) {\n return undefined;\n }\n\n return new Decimal(bid).plus(ask).div(2).toString();\n }\n\n spread(): string | undefined {\n const bid = this.bestBid();\n const ask = this.bestAsk();\n if (!bid || !ask) {\n return undefined;\n }\n\n return new Decimal(ask).minus(bid).toString();\n }\n\n isEmpty(): boolean {\n return this.bidsMap.size === 0 && this.asksMap.size === 0;\n }\n\n clear(): void {\n this.bidsMap.clear();\n this.asksMap.clear();\n this.seq = 0;\n this.cachedBestBid = null;\n this.cachedBestAsk = null;\n this.hasSnapshot = false;\n this.awaitingSnapshot = false;\n }\n}\n"]}
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/domain/orderbook/state.ts"],"names":[],"mappings":"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","sourcesContent":["import Decimal from \"decimal.js\";\nimport type { OrderBookId } from \"../../shared\";\nimport type { OrderBook } from \"./wire\";\n\n/**\n * The `book_update` stream is snapshot-only: every data frame carries the\n * full top-20 levels per side and replaces the previous book wholesale\n * (last-write-wins). Consumers holding multiple aggregation views of one\n * orderbook on the same connection key their `OrderbookState` instances by\n * `(orderbook_id, aggregation)` using\n * `aggregationFromFrame(book.n_sig_figs, book.mantissa)`.\n */\nexport type OrderbookApplyResult =\n | { kind: \"applied\" }\n | { kind: \"refresh_required\"; reason: OrderbookRefreshReason };\n\nexport type OrderbookRefreshReason =\n /**\n * The backend explicitly requested a resync: unsubscribe and re-subscribe\n * with the same parameters (including aggregation) to receive a fresh\n * snapshot.\n */\n { kind: \"server_resync\" };\n\nexport class OrderbookState {\n readonly orderbookId: OrderBookId;\n /**\n * Projection version of the last applied frame. Strictly increasing but\n * non-contiguous server-side (conflation skips versions), and the initial\n * snapshot after every (re)subscribe is `seq: 0` — informational only,\n * never used to gate frames.\n */\n seq: number;\n private readonly bidsMap: Map<string, string>;\n private readonly asksMap: Map<string, string>;\n private cachedBestBid: string | undefined | null;\n private cachedBestAsk: string | undefined | null;\n\n constructor(orderbookId: OrderBookId) {\n this.orderbookId = orderbookId;\n this.seq = 0;\n this.bidsMap = new Map();\n this.asksMap = new Map();\n this.cachedBestBid = null;\n this.cachedBestAsk = null;\n }\n\n /**\n * Apply a WS orderbook frame (snapshot-only stream, last-write-wins).\n *\n * `resync` frames take precedence and leave the book untouched — the\n * caller must re-subscribe with the same parameters. Every other data\n * frame is a full snapshot by contract and replaces the book wholesale\n * (the `is_snapshot` flag is not consulted), including the `seq: 0`\n * initial snapshot delivered after every (re)subscribe: gating on `seq`\n * would freeze the book after a resync or aggregation change, so `seq` is\n * stored as informational only.\n */\n apply(book: OrderBook): OrderbookApplyResult {\n if (book.resync) {\n return {\n kind: \"refresh_required\",\n reason: { kind: \"server_resync\" },\n };\n }\n\n this.bidsMap.clear();\n this.asksMap.clear();\n for (const level of book.bids) {\n if (!new Decimal(level.size).isZero()) {\n this.bidsMap.set(level.price, level.size);\n }\n }\n for (const level of book.asks) {\n if (!new Decimal(level.size).isZero()) {\n this.asksMap.set(level.price, level.size);\n }\n }\n this.seq = book.seq ?? 0;\n this.cachedBestBid = null;\n this.cachedBestAsk = null;\n\n return { kind: \"applied\" };\n }\n\n bids(): ReadonlyMap<string, string> {\n return this.bidsMap;\n }\n\n asks(): ReadonlyMap<string, string> {\n return this.asksMap;\n }\n\n bestBid(): string | undefined {\n if (this.cachedBestBid !== null) {\n return this.cachedBestBid;\n }\n if (this.bidsMap.size === 0) {\n this.cachedBestBid = undefined;\n return undefined;\n }\n const result = Array.from(this.bidsMap.keys())\n .sort((a, b) => new Decimal(a).cmp(new Decimal(b)))\n .at(-1);\n this.cachedBestBid = result;\n return result;\n }\n\n bestAsk(): string | undefined {\n if (this.cachedBestAsk !== null) {\n return this.cachedBestAsk;\n }\n if (this.asksMap.size === 0) {\n this.cachedBestAsk = undefined;\n return undefined;\n }\n const result = Array.from(this.asksMap.keys())\n .sort((a, b) => new Decimal(a).cmp(new Decimal(b)))[0];\n this.cachedBestAsk = result;\n return result;\n }\n\n midPrice(): string | undefined {\n const bid = this.bestBid();\n const ask = this.bestAsk();\n if (!bid || !ask) {\n return undefined;\n }\n\n return new Decimal(bid).plus(ask).div(2).toString();\n }\n\n spread(): string | undefined {\n const bid = this.bestBid();\n const ask = this.bestAsk();\n if (!bid || !ask) {\n return undefined;\n }\n\n return new Decimal(ask).minus(bid).toString();\n }\n\n isEmpty(): boolean {\n return this.bidsMap.size === 0 && this.asksMap.size === 0;\n }\n\n clear(): void {\n this.bidsMap.clear();\n this.asksMap.clear();\n this.seq = 0;\n this.cachedBestBid = null;\n this.cachedBestAsk = null;\n }\n}\n"]}
@@ -24,6 +24,9 @@ export interface RestBookLevel {
24
24
  size: string;
25
25
  orders?: number;
26
26
  }
27
+ /**
28
+ * REST depth response. Depth is capped server-side at 20 levels per side.
29
+ */
27
30
  export interface OrderbookDepthResponse {
28
31
  orderbook_id: OrderBookId;
29
32
  market_pubkey?: string;
@@ -33,6 +36,19 @@ export interface OrderbookDepthResponse {
33
36
  tick_size?: string;
34
37
  bids: RestBookLevel[];
35
38
  asks: RestBookLevel[];
39
+ /**
40
+ * Display decimals for prices and sizes. Always sent by current backends;
41
+ * optional for tolerance of older payloads.
42
+ */
43
+ decimals?: OrderbookDepthDecimals;
44
+ }
45
+ /**
46
+ * Price/size display decimals from the depth endpoint. Distinct from
47
+ * `DecimalsResponse` (the `/decimals` endpoint).
48
+ */
49
+ export interface OrderbookDepthDecimals {
50
+ price: number;
51
+ size: number;
36
52
  }
37
53
  export interface DecimalsResponse {
38
54
  orderbook_id: string;
@@ -45,6 +61,14 @@ export interface WsBookLevel {
45
61
  price: string;
46
62
  size: string;
47
63
  }
64
+ /**
65
+ * WS orderbook snapshot frame.
66
+ *
67
+ * The stream is snapshot-only: every data frame carries the full top-20
68
+ * levels per side and replaces the previous book wholesale (last-write-wins).
69
+ * `seq` is strictly increasing but non-contiguous, and the initial snapshot
70
+ * after every (re)subscribe is `seq: 0` — informational only, never a gate.
71
+ */
48
72
  export interface OrderBook {
49
73
  orderbook_id: OrderBookId;
50
74
  is_snapshot?: boolean;
@@ -53,6 +77,14 @@ export interface OrderBook {
53
77
  timestamp?: string;
54
78
  bids: WsBookLevel[];
55
79
  asks: WsBookLevel[];
80
+ /**
81
+ * Aggregation tags echoed by the backend (omitted = full precision).
82
+ * Always normalized server-side ((5, none) arrives as (5, 1)). Use
83
+ * `aggregationFromFrame(book.n_sig_figs, book.mantissa)` to key
84
+ * per-`(orderbook, aggregation)` book state.
85
+ */
86
+ n_sig_figs?: number;
87
+ mantissa?: number;
56
88
  }
57
89
  export interface WsTickerData {
58
90
  orderbook_id: OrderBookId;
@@ -1 +1 @@
1
- {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../../src/domain/orderbook/wire.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,WAAW,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,IAAI,EAAE,aAAa,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,IAAI,EAAE,WAAW,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../../src/domain/orderbook/wire.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,WAAW,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"wire.js","sourceRoot":"","sources":["../../../src/domain/orderbook/wire.ts"],"names":[],"mappings":"","sourcesContent":["import type { OrderBookId, Side } from \"../../shared\";\n\nexport interface OrderbookResponse {\n id: number;\n market_pubkey: string;\n orderbook_id: string;\n base_token: string;\n quote_token: string;\n outcome_index?: number;\n tick_size: number;\n total_bids: number;\n total_asks: number;\n last_trade_price?: string;\n last_trade_time?: string;\n active: boolean;\n created_at: string;\n updated_at: string;\n}\n\nexport interface OrderbooksResponse {\n orderbooks: OrderbookResponse[];\n total: number;\n}\n\nexport interface RestBookLevel {\n price: string;\n size: string;\n orders?: number;\n}\n\nexport interface OrderbookDepthResponse {\n orderbook_id: OrderBookId;\n market_pubkey?: string;\n best_bid?: string;\n best_ask?: string;\n spread?: string;\n tick_size?: string;\n bids: RestBookLevel[];\n asks: RestBookLevel[];\n}\n\nexport interface DecimalsResponse {\n orderbook_id: string;\n base_decimals: number;\n quote_decimals: number;\n price_decimals: number;\n}\n\nexport interface WsBookLevel {\n side: Side;\n price: string;\n size: string;\n}\n\nexport interface OrderBook {\n orderbook_id: OrderBookId;\n is_snapshot?: boolean;\n seq?: number;\n resync?: boolean;\n timestamp?: string;\n bids: WsBookLevel[];\n asks: WsBookLevel[];\n}\n\nexport interface WsTickerData {\n orderbook_id: OrderBookId;\n best_bid?: string;\n best_ask?: string;\n mid?: string;\n timestamp?: string;\n}\n"]}
1
+ {"version":3,"file":"wire.js","sourceRoot":"","sources":["../../../src/domain/orderbook/wire.ts"],"names":[],"mappings":"","sourcesContent":["import type { OrderBookId, Side } from \"../../shared\";\n\nexport interface OrderbookResponse {\n id: number;\n market_pubkey: string;\n orderbook_id: string;\n base_token: string;\n quote_token: string;\n outcome_index?: number;\n tick_size: number;\n total_bids: number;\n total_asks: number;\n last_trade_price?: string;\n last_trade_time?: string;\n active: boolean;\n created_at: string;\n updated_at: string;\n}\n\nexport interface OrderbooksResponse {\n orderbooks: OrderbookResponse[];\n total: number;\n}\n\nexport interface RestBookLevel {\n price: string;\n size: string;\n orders?: number;\n}\n\n/**\n * REST depth response. Depth is capped server-side at 20 levels per side.\n */\nexport interface OrderbookDepthResponse {\n orderbook_id: OrderBookId;\n market_pubkey?: string;\n best_bid?: string;\n best_ask?: string;\n spread?: string;\n tick_size?: string;\n bids: RestBookLevel[];\n asks: RestBookLevel[];\n /**\n * Display decimals for prices and sizes. Always sent by current backends;\n * optional for tolerance of older payloads.\n */\n decimals?: OrderbookDepthDecimals;\n}\n\n/**\n * Price/size display decimals from the depth endpoint. Distinct from\n * `DecimalsResponse` (the `/decimals` endpoint).\n */\nexport interface OrderbookDepthDecimals {\n price: number;\n size: number;\n}\n\nexport interface DecimalsResponse {\n orderbook_id: string;\n base_decimals: number;\n quote_decimals: number;\n price_decimals: number;\n}\n\nexport interface WsBookLevel {\n side: Side;\n price: string;\n size: string;\n}\n\n/**\n * WS orderbook snapshot frame.\n *\n * The stream is snapshot-only: every data frame carries the full top-20\n * levels per side and replaces the previous book wholesale (last-write-wins).\n * `seq` is strictly increasing but non-contiguous, and the initial snapshot\n * after every (re)subscribe is `seq: 0` — informational only, never a gate.\n */\nexport interface OrderBook {\n orderbook_id: OrderBookId;\n is_snapshot?: boolean;\n seq?: number;\n resync?: boolean;\n timestamp?: string;\n bids: WsBookLevel[];\n asks: WsBookLevel[];\n /**\n * Aggregation tags echoed by the backend (omitted = full precision).\n * Always normalized server-side ((5, none) arrives as (5, 1)). Use\n * `aggregationFromFrame(book.n_sig_figs, book.mantissa)` to key\n * per-`(orderbook, aggregation)` book state.\n */\n n_sig_figs?: number;\n mantissa?: number;\n}\n\nexport interface WsTickerData {\n orderbook_id: OrderBookId;\n best_bid?: string;\n best_ask?: string;\n mid?: string;\n timestamp?: string;\n}\n"]}
package/dist/env.d.ts CHANGED
@@ -17,11 +17,26 @@ export declare enum LightconeEnv {
17
17
  Staging = "staging",
18
18
  Prod = "prod"
19
19
  }
20
- /** REST API base URL for the given environment. */
20
+ /**
21
+ * REST API base URL for the given environment.
22
+ *
23
+ * If the `SDK_API_URL` environment variable is set, its value is used
24
+ * regardless of the selected environment.
25
+ */
21
26
  export declare function apiUrl(environment: LightconeEnv): string;
22
- /** WebSocket URL for the given environment. */
27
+ /**
28
+ * WebSocket URL for the given environment.
29
+ *
30
+ * If the `SDK_WS_URL` environment variable is set, its value is used
31
+ * regardless of the selected environment.
32
+ */
23
33
  export declare function wsUrl(environment: LightconeEnv): string;
24
- /** Solana RPC URL for the given environment. */
34
+ /**
35
+ * Solana RPC URL for the given environment.
36
+ *
37
+ * If the `SDK_RPC_URL` environment variable is set, its value is used
38
+ * regardless of the selected environment.
39
+ */
25
40
  export declare function rpcUrl(environment: LightconeEnv): string;
26
41
  /**
27
42
  * On-chain Lightcone program ID for the given environment.
package/dist/env.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,oBAAY,YAAY;IACtB,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,IAAI,SAAS;CACd;AAED,mDAAmD;AACnD,wBAAgB,MAAM,CAAC,WAAW,EAAE,YAAY,GAAG,MAAM,CASxD;AAED,+CAA+C;AAC/C,wBAAgB,KAAK,CAAC,WAAW,EAAE,YAAY,GAAG,MAAM,CASvD;AAED,gDAAgD;AAChD,wBAAgB,MAAM,CAAC,WAAW,EAAE,YAAY,GAAG,MAAM,CASxD;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,YAAY,GAAG,SAAS,CAa9D;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,WAA+B,CAAC"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,oBAAY,YAAY;IACtB,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,IAAI,SAAS;CACd;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,WAAW,EAAE,YAAY,GAAG,MAAM,CAWxD;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,WAAW,EAAE,YAAY,GAAG,MAAM,CAWvD;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,WAAW,EAAE,YAAY,GAAG,MAAM,CAWxD;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,YAAY,GAAG,SAAS,CAa9D;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,WAA+B,CAAC"}
package/dist/env.js CHANGED
@@ -25,8 +25,16 @@ var LightconeEnv;
25
25
  LightconeEnv["Staging"] = "staging";
26
26
  LightconeEnv["Prod"] = "prod";
27
27
  })(LightconeEnv || (exports.LightconeEnv = LightconeEnv = {}));
28
- /** REST API base URL for the given environment. */
28
+ /**
29
+ * REST API base URL for the given environment.
30
+ *
31
+ * If the `SDK_API_URL` environment variable is set, its value is used
32
+ * regardless of the selected environment.
33
+ */
29
34
  function apiUrl(environment) {
35
+ const overrideUrl = typeof process !== "undefined" ? process.env.SDK_API_URL : undefined;
36
+ if (overrideUrl)
37
+ return overrideUrl;
30
38
  switch (environment) {
31
39
  case LightconeEnv.Local:
32
40
  return "https://api.local.lightcone.xyz";
@@ -36,8 +44,16 @@ function apiUrl(environment) {
36
44
  return "https://api.lightcone.xyz";
37
45
  }
38
46
  }
39
- /** WebSocket URL for the given environment. */
47
+ /**
48
+ * WebSocket URL for the given environment.
49
+ *
50
+ * If the `SDK_WS_URL` environment variable is set, its value is used
51
+ * regardless of the selected environment.
52
+ */
40
53
  function wsUrl(environment) {
54
+ const overrideUrl = typeof process !== "undefined" ? process.env.SDK_WS_URL : undefined;
55
+ if (overrideUrl)
56
+ return overrideUrl;
41
57
  switch (environment) {
42
58
  case LightconeEnv.Local:
43
59
  return "wss://ws.local.lightcone.xyz/ws";
@@ -47,15 +63,23 @@ function wsUrl(environment) {
47
63
  return "wss://ws.lightcone.xyz/ws";
48
64
  }
49
65
  }
50
- /** Solana RPC URL for the given environment. */
66
+ /**
67
+ * Solana RPC URL for the given environment.
68
+ *
69
+ * If the `SDK_RPC_URL` environment variable is set, its value is used
70
+ * regardless of the selected environment.
71
+ */
51
72
  function rpcUrl(environment) {
73
+ const overrideUrl = typeof process !== "undefined" ? process.env.SDK_RPC_URL : undefined;
74
+ if (overrideUrl)
75
+ return overrideUrl;
52
76
  switch (environment) {
53
77
  case LightconeEnv.Local:
54
78
  return "https://api.devnet.solana.com";
55
79
  case LightconeEnv.Staging:
56
80
  return "https://api.devnet.solana.com";
57
81
  case LightconeEnv.Prod:
58
- return "https://api.devnet.solana.com";
82
+ return "https://api.mainnet-beta.solana.com";
59
83
  }
60
84
  }
61
85
  /**