@waterx/sdk 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +25 -10
  2. package/dist/cjs/src/account/account.js +2 -1
  3. package/dist/cjs/src/account/funding/balance.d.ts +25 -2
  4. package/dist/cjs/src/account/funding/balance.js +36 -24
  5. package/dist/cjs/src/account/funding/credit.js +6 -10
  6. package/dist/cjs/src/constants.d.ts +15 -1
  7. package/dist/cjs/src/constants.js +18 -4
  8. package/dist/cjs/src/generated/waterx_rule/deps/bucket_v2_framework/float.d.ts +8 -0
  9. package/dist/cjs/src/generated/waterx_rule/deps/bucket_v2_framework/float.js +13 -0
  10. package/dist/cjs/src/generated/waterx_rule/deps/sui/vec_map.d.ts +36 -0
  11. package/dist/cjs/src/generated/waterx_rule/deps/sui/vec_map.js +31 -0
  12. package/dist/cjs/src/generated/waterx_rule/waterx_rule.d.ts +940 -0
  13. package/dist/cjs/src/generated/waterx_rule/waterx_rule.js +1092 -0
  14. package/dist/cjs/src/oracle/aggregate.d.ts +2 -0
  15. package/dist/cjs/src/oracle/aggregate.js +27 -2
  16. package/dist/cjs/src/oracle/config.d.ts +75 -0
  17. package/dist/cjs/src/oracle/config.js +15 -1
  18. package/dist/cjs/src/oracle/host.d.ts +12 -1
  19. package/dist/cjs/src/oracle/index.d.ts +2 -0
  20. package/dist/cjs/src/oracle/index.js +6 -1
  21. package/dist/cjs/src/oracle/price-update-rule.d.ts +3 -4
  22. package/dist/cjs/src/oracle/rule-registry.d.ts +8 -7
  23. package/dist/cjs/src/oracle/rule-registry.js +10 -7
  24. package/dist/cjs/src/oracle/rules/waterx-rule.d.ts +89 -0
  25. package/dist/cjs/src/oracle/rules/waterx-rule.js +272 -0
  26. package/dist/cjs/src/perp/client.d.ts +44 -6
  27. package/dist/cjs/src/perp/client.js +14 -0
  28. package/dist/cjs/src/perp/config.d.ts +2 -3
  29. package/dist/cjs/src/perp/config.js +2 -1
  30. package/dist/cjs/src/perp/constants.d.ts +0 -6
  31. package/dist/cjs/src/perp/constants.js +11 -9
  32. package/dist/cjs/src/perp/fetch/account.js +3 -0
  33. package/dist/cjs/src/perp/fetch/bridge.js +2 -1
  34. package/dist/cjs/src/perp/fetch/market.js +2 -1
  35. package/dist/cjs/src/perp/fetch/positions.d.ts +16 -10
  36. package/dist/cjs/src/perp/fetch/positions.js +23 -20
  37. package/dist/cjs/src/perp/index.d.ts +6 -2
  38. package/dist/cjs/src/perp/index.js +12 -6
  39. package/dist/cjs/src/perp/liq-view.d.ts +64 -0
  40. package/dist/cjs/src/perp/liq-view.js +74 -0
  41. package/dist/cjs/src/perp/user/order.d.ts +13 -0
  42. package/dist/cjs/src/perp/user/order.js +30 -16
  43. package/dist/cjs/src/perp/user/staking.js +3 -2
  44. package/dist/cjs/src/perp/user/trading.js +25 -24
  45. package/dist/cjs/src/perp/user/wlp.js +6 -5
  46. package/dist/cjs/src/prediction/utils.d.ts +11 -2
  47. package/dist/cjs/src/prediction/utils.js +22 -22
  48. package/dist/cjs/src/unified-client.d.ts +31 -10
  49. package/dist/cjs/src/unified-client.js +2 -0
  50. package/dist/cjs/src/utils/format.d.ts +14 -0
  51. package/dist/cjs/src/utils/format.js +24 -0
  52. package/dist/cjs/src/utils/math.d.ts +304 -12
  53. package/dist/cjs/src/utils/math.js +397 -17
  54. package/dist/cjs/src/utils/validate.d.ts +69 -0
  55. package/dist/cjs/src/utils/validate.js +183 -0
  56. package/dist/src/account/account.js +2 -1
  57. package/dist/src/account/funding/balance.d.ts +25 -2
  58. package/dist/src/account/funding/balance.js +36 -24
  59. package/dist/src/account/funding/credit.js +6 -10
  60. package/dist/src/constants.d.ts +15 -1
  61. package/dist/src/constants.js +17 -3
  62. package/dist/src/generated/waterx_rule/deps/bucket_v2_framework/float.d.ts +8 -0
  63. package/dist/src/generated/waterx_rule/deps/bucket_v2_framework/float.js +10 -0
  64. package/dist/src/generated/waterx_rule/deps/sui/vec_map.d.ts +36 -0
  65. package/dist/src/generated/waterx_rule/deps/sui/vec_map.js +27 -0
  66. package/dist/src/generated/waterx_rule/waterx_rule.d.ts +940 -0
  67. package/dist/src/generated/waterx_rule/waterx_rule.js +991 -0
  68. package/dist/src/oracle/aggregate.d.ts +2 -0
  69. package/dist/src/oracle/aggregate.js +27 -2
  70. package/dist/src/oracle/config.d.ts +75 -0
  71. package/dist/src/oracle/config.js +14 -0
  72. package/dist/src/oracle/host.d.ts +12 -1
  73. package/dist/src/oracle/index.d.ts +2 -0
  74. package/dist/src/oracle/index.js +3 -0
  75. package/dist/src/oracle/price-update-rule.d.ts +3 -4
  76. package/dist/src/oracle/rule-registry.d.ts +8 -7
  77. package/dist/src/oracle/rule-registry.js +10 -7
  78. package/dist/src/oracle/rules/waterx-rule.d.ts +89 -0
  79. package/dist/src/oracle/rules/waterx-rule.js +266 -0
  80. package/dist/src/perp/client.d.ts +44 -6
  81. package/dist/src/perp/client.js +15 -1
  82. package/dist/src/perp/config.d.ts +2 -3
  83. package/dist/src/perp/config.js +1 -1
  84. package/dist/src/perp/constants.d.ts +0 -6
  85. package/dist/src/perp/constants.js +10 -8
  86. package/dist/src/perp/fetch/account.js +3 -0
  87. package/dist/src/perp/fetch/bridge.js +2 -1
  88. package/dist/src/perp/fetch/market.js +2 -1
  89. package/dist/src/perp/fetch/positions.d.ts +16 -10
  90. package/dist/src/perp/fetch/positions.js +28 -20
  91. package/dist/src/perp/index.d.ts +6 -2
  92. package/dist/src/perp/index.js +4 -2
  93. package/dist/src/perp/liq-view.d.ts +64 -0
  94. package/dist/src/perp/liq-view.js +71 -0
  95. package/dist/src/perp/user/order.d.ts +13 -0
  96. package/dist/src/perp/user/order.js +30 -16
  97. package/dist/src/perp/user/staking.js +3 -2
  98. package/dist/src/perp/user/trading.js +25 -24
  99. package/dist/src/perp/user/wlp.js +6 -5
  100. package/dist/src/prediction/utils.d.ts +11 -2
  101. package/dist/src/prediction/utils.js +22 -22
  102. package/dist/src/unified-client.d.ts +31 -10
  103. package/dist/src/unified-client.js +2 -0
  104. package/dist/src/utils/format.d.ts +14 -0
  105. package/dist/src/utils/format.js +21 -0
  106. package/dist/src/utils/math.d.ts +304 -12
  107. package/dist/src/utils/math.js +394 -17
  108. package/dist/src/utils/validate.d.ts +69 -0
  109. package/dist/src/utils/validate.js +167 -0
  110. package/package.json +4 -1
@@ -51,6 +51,7 @@ exports.settleRedeemWlp = settleRedeemWlp;
51
51
  exports.updateTokenValue = updateTokenValue;
52
52
  const account_request_ts_1 = require("../../account/account-request.js");
53
53
  const lp = __importStar(require("../../generated/waterx_perp/lp_pool.js"));
54
+ const validate_ts_1 = require("../../utils/validate.js");
54
55
  /** Returns the minted `lp_amount` so it can be chained into e.g. `stake`. */
55
56
  function mintWlp(client, tx, params) {
56
57
  const req = (0, account_request_ts_1.makeSenderRequest)(client, tx, params.bucketAccount);
@@ -63,8 +64,8 @@ function mintWlp(client, tx, params) {
63
64
  aum: tx.object(requireWlpAum(client)),
64
65
  senderRequest: req,
65
66
  accountId: params.accountId,
66
- depositAmount: params.depositAmount,
67
- minLpAmount: params.minLpAmount,
67
+ depositAmount: (0, validate_ts_1.toU64)(params.depositAmount, "depositAmount"),
68
+ minLpAmount: (0, validate_ts_1.toU64)(params.minLpAmount, "minLpAmount"),
68
69
  oracle: tx.object(client.config.packages.waterx_oracle.oracle),
69
70
  },
70
71
  typeArguments: [params.lpType ?? client.wlpType(), params.depositTokenType],
@@ -88,7 +89,7 @@ function requestRedeemWlp(client, tx, params) {
88
89
  wxaRegistry: tx.object(client.config.packages.waterx_account.account_registry),
89
90
  senderRequest: req,
90
91
  accountId: params.accountId,
91
- lpAmount: params.lpAmount,
92
+ lpAmount: (0, validate_ts_1.toU64)(params.lpAmount, "lpAmount"),
92
93
  },
93
94
  typeArguments: [params.lpType ?? client.wlpType(), params.redeemTokenType],
94
95
  })(tx);
@@ -102,7 +103,7 @@ function cancelRedeemWlp(client, tx, params) {
102
103
  globalConfig: tx.object(client.config.packages.waterx_perp.global_config),
103
104
  wxaRegistry: tx.object(client.config.packages.waterx_account.account_registry),
104
105
  senderRequest: req,
105
- requestId: params.requestId,
106
+ requestId: (0, validate_ts_1.toU64)(params.requestId, "requestId"),
106
107
  },
107
108
  typeArguments: [params.lpType ?? client.wlpType()],
108
109
  })(tx);
@@ -117,7 +118,7 @@ function settleRedeemWlp(client, tx, params) {
117
118
  wxaRegistry: tx.object(client.config.packages.waterx_account.account_registry),
118
119
  operatorRequest: req,
119
120
  aum: tx.object(requireWlpAum(client)),
120
- requestId: params.requestId,
121
+ requestId: (0, validate_ts_1.toU64)(params.requestId, "requestId"),
121
122
  oracle: tx.object(client.config.packages.waterx_oracle.oracle),
122
123
  },
123
124
  typeArguments: [params.lpType ?? client.wlpType(), params.redeemTokenType],
@@ -10,8 +10,17 @@ export declare function resolveSettlementCoinType(client: PredictClient, coinTyp
10
10
  export declare function resolveMarketRegistry(client: PredictClient, marketRegistry?: string): string;
11
11
  export declare function resolveAccountRegistry(client: PredictClient, accountRegistry?: string): string;
12
12
  export declare function resolveGlobalConfig(client: PredictClient, globalConfig?: string): string;
13
- /** Ensures `value` is a valid unsigned 64-bit integer. */
14
- export declare function assertU64(value: bigint, name?: string): bigint;
13
+ /**
14
+ * Ensures `value` is a valid unsigned 64-bit integer.
15
+ *
16
+ * Thin alias for the shared `utils/validate.toU64` — the u64 domain rule
17
+ * (bigint range check; a `number` must be a NON-NEGATIVE SAFE integer, since
18
+ * `2^53 + 2` passes `isInteger` yet has already lost f64 precision and would
19
+ * BCS-encode a silently-wrong value) is authored ONCE there for both product
20
+ * lines. Throws `RangeError` (a subclass of `Error`) naming the parameter.
21
+ */
22
+ export declare function assertU64(value: bigint | number, name?: string): bigint;
23
+ /** Normalize a u64-ish input (bigint / number / decimal string) to a checked `bigint`. */
15
24
  export declare function toBigInt(value: bigint | number | string): bigint;
16
25
  export declare function objectArg(tx: Transaction, value: ObjectArgument): TransactionArgument;
17
26
  export declare function idArg(tx: Transaction, value: IdArgument): TransactionArgument;
@@ -21,10 +21,11 @@ exports.marketIdArg = marketIdArg;
21
21
  exports.bytesToHex = bytesToHex;
22
22
  exports.optionU64 = optionU64;
23
23
  exports.receivingCoinArg = receivingCoinArg;
24
+ const validate_ts_1 = require("../utils/validate.js");
24
25
  const constants_ts_1 = require("./constants.js");
25
26
  const SELECTIONS = ["YES", "NO"];
26
27
  const OUTCOMES = ["YES", "NO", "INVALID"];
27
- const U64_MAX = (1n << 64n) - 1n;
28
+ const DECIMAL_INT_RE = /^\d+$/;
28
29
  function assertSelection(value) {
29
30
  if (SELECTIONS.includes(value))
30
31
  return value;
@@ -68,32 +69,31 @@ function resolveAccountRegistry(client, accountRegistry) {
68
69
  function resolveGlobalConfig(client, globalConfig) {
69
70
  return globalConfig === undefined || globalConfig === "" ? client.globalConfigId() : globalConfig;
70
71
  }
71
- /** Ensures `value` is a valid unsigned 64-bit integer. */
72
+ /**
73
+ * Ensures `value` is a valid unsigned 64-bit integer.
74
+ *
75
+ * Thin alias for the shared `utils/validate.toU64` — the u64 domain rule
76
+ * (bigint range check; a `number` must be a NON-NEGATIVE SAFE integer, since
77
+ * `2^53 + 2` passes `isInteger` yet has already lost f64 precision and would
78
+ * BCS-encode a silently-wrong value) is authored ONCE there for both product
79
+ * lines. Throws `RangeError` (a subclass of `Error`) naming the parameter.
80
+ */
72
81
  function assertU64(value, name = "value") {
73
- if (value < 0n) {
74
- throw new Error(`${name} must be non-negative, got ${value}`);
75
- }
76
- if (value > U64_MAX) {
77
- throw new Error(`${name} exceeds u64 max (${U64_MAX}), got ${value}`);
78
- }
79
- return value;
82
+ return (0, validate_ts_1.toU64)(value, name);
80
83
  }
84
+ /** Normalize a u64-ish input (bigint / number / decimal string) to a checked `bigint`. */
81
85
  function toBigInt(value) {
82
- if (typeof value === "bigint") {
86
+ if (typeof value !== "string")
83
87
  return assertU64(value);
84
- }
85
- if (typeof value === "number") {
86
- if (!Number.isInteger(value)) {
87
- throw new Error(`Invalid integer: ${value}`);
88
- }
89
- return assertU64(BigInt(value));
90
- }
88
+ // String is the one form the shared guard does not cover: digits parse exactly
89
+ // past the 2^53 f64 cliff, so the shape check lives here and the numeric
90
+ // domain still funnels through assertU64.
91
91
  const trimmed = value.trim();
92
92
  if (trimmed === "") {
93
- throw new Error("Invalid integer: empty string");
93
+ throw new RangeError("Invalid integer: empty string");
94
94
  }
95
- if (!/^\d+$/.test(trimmed)) {
96
- throw new Error(`Invalid integer: ${JSON.stringify(value)}`);
95
+ if (!DECIMAL_INT_RE.test(trimmed)) {
96
+ throw new RangeError(`Invalid integer: ${JSON.stringify(value)}`);
97
97
  }
98
98
  return assertU64(BigInt(trimmed));
99
99
  }
@@ -184,8 +184,8 @@ function optionU64(value) {
184
184
  function formatObjectVersion(version) {
185
185
  if (typeof version === "string") {
186
186
  const trimmed = version.trim();
187
- if (trimmed === "" || !/^\d+$/.test(trimmed)) {
188
- throw new Error(`Invalid object version: ${JSON.stringify(version)}`);
187
+ if (trimmed === "" || !DECIMAL_INT_RE.test(trimmed)) {
188
+ throw new RangeError(`Invalid object version: ${JSON.stringify(version)}`);
189
189
  }
190
190
  return trimmed;
191
191
  }
@@ -34,6 +34,7 @@ import * as perpReferral from "./account/referral.ts";
34
34
  import type { Network } from "./constants.ts";
35
35
  import type { PythFetchPolicy } from "./oracle/config.ts";
36
36
  import type { OracleSource } from "./oracle/price-update-rule.ts";
37
+ import type { FetchPolicy } from "./oracle/update-fetch.ts";
37
38
  import { PerpClient, type CreateClientOptions as PerpCreateOptions } from "./perp/client.ts";
38
39
  import * as perpFetch from "./perp/fetch.ts";
39
40
  import * as perpTx from "./perp/tx-builders.ts";
@@ -90,8 +91,8 @@ declare const perpOps: {
90
91
  getPosition(client: PerpClient, args: {
91
92
  ticker: string;
92
93
  positionId: bigint | number;
93
- basePriceUsd: bigint | number;
94
- collateralPriceUsd: bigint | number;
94
+ basePriceUsd: perpFetch.WholeDollarUsdPrice;
95
+ collateralPriceUsd: perpFetch.WholeDollarUsdPrice;
95
96
  lpType?: string;
96
97
  }): Promise<perpFetch.PositionDataView>;
97
98
  getOrder(client: PerpClient, args: {
@@ -99,12 +100,12 @@ declare const perpOps: {
99
100
  orderId: bigint | number;
100
101
  orderTypeTag: number;
101
102
  triggerPrice: bigint | number;
102
- basePriceUsd: bigint | number;
103
+ basePriceUsd: perpFetch.WholeDollarUsdPrice;
103
104
  lpType?: string;
104
105
  }): Promise<perpFetch.OrderDataView>;
105
106
  getMarketOrders(client: PerpClient, args: {
106
107
  ticker: string;
107
- basePriceUsd?: bigint | number;
108
+ basePriceUsd?: perpFetch.WholeDollarUsdPrice;
108
109
  lpType?: string;
109
110
  } & perpFetch.PageOpts): Promise<{
110
111
  orders: perpFetch.OrderDataView[];
@@ -112,8 +113,8 @@ declare const perpOps: {
112
113
  }>;
113
114
  getMarketPositions(client: PerpClient, args: {
114
115
  ticker: string;
115
- basePriceUsd: bigint | number;
116
- collateralPriceUsd?: bigint | number;
116
+ basePriceUsd: perpFetch.WholeDollarUsdPrice;
117
+ collateralPriceUsd?: perpFetch.WholeDollarUsdPrice;
117
118
  lpType?: string;
118
119
  } & perpFetch.PageOpts): Promise<{
119
120
  positions: perpFetch.PositionDataView[];
@@ -122,14 +123,14 @@ declare const perpOps: {
122
123
  getAccountPositions(client: PerpClient, args: {
123
124
  ticker: string;
124
125
  accountObjectAddress: string;
125
- basePriceUsd: bigint | number;
126
- collateralPriceUsd?: bigint | number;
126
+ basePriceUsd: perpFetch.WholeDollarUsdPrice;
127
+ collateralPriceUsd?: perpFetch.WholeDollarUsdPrice;
127
128
  lpType?: string;
128
129
  }): Promise<perpFetch.PositionDataView[]>;
129
130
  getAccountOrders(client: PerpClient, args: {
130
131
  ticker: string;
131
132
  accountObjectAddress: string;
132
- basePriceUsd?: bigint | number;
133
+ basePriceUsd?: perpFetch.WholeDollarUsdPrice;
133
134
  lpType?: string;
134
135
  }): Promise<perpFetch.OrderDataView[]>;
135
136
  getRedeemRequests(client: PerpClient, args?: {
@@ -138,6 +139,7 @@ declare const perpOps: {
138
139
  requests: perpFetch.RedeemRequestDataView[];
139
140
  nextCursor?: bigint;
140
141
  }>;
142
+ parseWholeDollarU64: typeof perpFetch.parseWholeDollarU64;
141
143
  getAccountsByOwner(client: PerpClient, owner: string): Promise<string[]>;
142
144
  getAccountBalance(client: PerpClient, accountId: string, coinType?: string): Promise<bigint>;
143
145
  getSpendableCreditBalance(client: PerpClient, accountId: string): Promise<perpFetch.SpendableCreditBalance>;
@@ -368,11 +370,14 @@ export interface ClientCreateOptions {
368
370
  /**
369
371
  * The perp line's oracle price-update source (perp-line only — the
370
372
  * prediction line has no oracle leg), forwarded to `PerpClient.create`.
371
- * Source-neutral by design: a future source need not be Pyth.
373
+ * Source-neutral by design: a source need not be Pyth (see `'waterx_rule'`).
372
374
  *
373
375
  * - `'pyth_rule'` (default) — Pyth Core updates on Core infra.
374
376
  * - `'pyth_lazer_rule'` — Pyth Lazer signed updates (pair with `pythApiKey`
375
377
  * and a config carrying `packages.pyth_lazer_rule`).
378
+ * - `'waterx_rule'` — the first-party WaterX quote-center (Nautilus-TEE,
379
+ * ed25519 signed batches; no credential, no per-update fee). Pair with
380
+ * `waterxEndpoint` / `waterxFetch` when the browser needs a proxy.
376
381
  *
377
382
  * Each source is self-contained with no cross-source fallback; selecting a
378
383
  * source whose feed for a ticker is absent fails at tx-build (not at init).
@@ -388,6 +393,22 @@ export interface ClientCreateOptions {
388
393
  pythApiKey?: string;
389
394
  /** Retry/timeout policy for the perp line's off-chain oracle fetches. */
390
395
  pythFetch?: PythFetchPolicy;
396
+ /**
397
+ * Quote-center base URL for `oracleSource: 'waterx_rule'`, forwarded to the
398
+ * perp line — overrides the per-network `WATERX_DEFAULTS` host.
399
+ *
400
+ * `waterx_rule` is the one source a BROWSER fetches itself, so it is bound by
401
+ * the quote-center deployment's CORS allowlist: a front end whose origin is
402
+ * not allowed points this at its own same-origin proxy. An absolute URL whose
403
+ * base PATH is preserved — `https://app.example/api/quote-center` fetches
404
+ * `…/api/quote-center/v1/quotes/update`. Unused by the Pyth sources.
405
+ */
406
+ waterxEndpoint?: string;
407
+ /**
408
+ * Retry/timeout policy — and `fetchImpl` — for the perp line's quote-center
409
+ * fetch. Falls back to `pythFetch`, then to the built-in defaults.
410
+ */
411
+ waterxFetch?: FetchPolicy;
391
412
  /** Perp-line overrides (network, grpcUrl, waterxConfigUrl, cache, …). */
392
413
  perp?: PerpLineOptions;
393
414
  /** Prediction-line overrides (network, grpcUrl, waterxConfigUrl, cache, settlement, …). */
@@ -207,6 +207,8 @@ class WaterXClient {
207
207
  oracleSource: opts.oracleSource,
208
208
  pythApiKey: opts.pythApiKey,
209
209
  pythFetch: opts.pythFetch,
210
+ waterxEndpoint: opts.waterxEndpoint,
211
+ waterxFetch: opts.waterxFetch,
210
212
  ...perpRest,
211
213
  });
212
214
  const predictClient = await client_ts_2.PredictClient.create(resolvedPredictNetwork, {
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Funding-interval label from milliseconds: hour-based at or above one hour,
3
+ * minute-based below. Non-integer hours are kept as-is:
4
+ *
5
+ * 3_600_000 → "1H"
6
+ * 28_800_000 → "8H"
7
+ * 5_400_000 → "1.5H"
8
+ * 1_800_000 → "30M"
9
+ *
10
+ * Single source for the label the BE tickers payload emits and the FE reader
11
+ * must reproduce byte-identically (output-identical port of bucket-backend-mono
12
+ * `apps/waterx/src/core/utils/funding.ts::formatFundingInterval`).
13
+ */
14
+ export declare function formatFundingInterval(intervalMs: number): string;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ // Display-label formatters that must be byte-identical wherever the same wire
3
+ // field is rendered. Hosted in the SDK so FE and BE share one implementation
4
+ // instead of hand-mirroring each other.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.formatFundingInterval = formatFundingInterval;
7
+ const constants_ts_1 = require("../constants.js");
8
+ /**
9
+ * Funding-interval label from milliseconds: hour-based at or above one hour,
10
+ * minute-based below. Non-integer hours are kept as-is:
11
+ *
12
+ * 3_600_000 → "1H"
13
+ * 28_800_000 → "8H"
14
+ * 5_400_000 → "1.5H"
15
+ * 1_800_000 → "30M"
16
+ *
17
+ * Single source for the label the BE tickers payload emits and the FE reader
18
+ * must reproduce byte-identically (output-identical port of bucket-backend-mono
19
+ * `apps/waterx/src/core/utils/funding.ts::formatFundingInterval`).
20
+ */
21
+ function formatFundingInterval(intervalMs) {
22
+ const hours = intervalMs / constants_ts_1.MS_PER_HOUR;
23
+ return hours >= 1 ? `${hours}H` : `${intervalMs / constants_ts_1.MS_PER_MINUTE}M`;
24
+ }