@waterx/sdk 4.0.1 → 4.3.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 (130) hide show
  1. package/README.md +37 -17
  2. package/dist/cjs/src/account/account.js +2 -1
  3. package/dist/cjs/src/account/config.d.ts +3 -2
  4. package/dist/cjs/src/account/funding/balance.d.ts +25 -2
  5. package/dist/cjs/src/account/funding/balance.js +36 -24
  6. package/dist/cjs/src/account/funding/credit.js +6 -10
  7. package/dist/cjs/src/constants.d.ts +15 -1
  8. package/dist/cjs/src/constants.js +18 -4
  9. package/dist/cjs/src/generated/waterx_rule/deps/bucket_v2_framework/float.d.ts +8 -0
  10. package/dist/cjs/src/generated/waterx_rule/deps/bucket_v2_framework/float.js +13 -0
  11. package/dist/cjs/src/generated/waterx_rule/deps/sui/vec_map.d.ts +36 -0
  12. package/dist/cjs/src/generated/waterx_rule/deps/sui/vec_map.js +31 -0
  13. package/dist/cjs/src/generated/waterx_rule/waterx_rule.d.ts +813 -0
  14. package/dist/cjs/src/generated/waterx_rule/waterx_rule.js +946 -0
  15. package/dist/cjs/src/oracle/aggregate.d.ts +23 -21
  16. package/dist/cjs/src/oracle/aggregate.js +133 -59
  17. package/dist/cjs/src/oracle/config.d.ts +82 -41
  18. package/dist/cjs/src/oracle/config.js +0 -42
  19. package/dist/cjs/src/oracle/host.d.ts +26 -11
  20. package/dist/cjs/src/oracle/index.d.ts +6 -2
  21. package/dist/cjs/src/oracle/index.js +36 -8
  22. package/dist/cjs/src/oracle/price-update-rule.d.ts +16 -10
  23. package/dist/cjs/src/oracle/price-update-rule.js +3 -3
  24. package/dist/cjs/src/oracle/pyth.d.ts +44 -0
  25. package/dist/cjs/src/oracle/pyth.js +48 -6
  26. package/dist/cjs/src/oracle/read-plane.d.ts +70 -0
  27. package/dist/cjs/src/oracle/read-plane.js +78 -0
  28. package/dist/cjs/src/oracle/rule-registry.d.ts +14 -11
  29. package/dist/cjs/src/oracle/rule-registry.js +16 -11
  30. package/dist/cjs/src/oracle/rules/pyth-core-rule.js +4 -4
  31. package/dist/cjs/src/oracle/rules/pyth-lazer-rule.d.ts +21 -0
  32. package/dist/cjs/src/oracle/rules/pyth-lazer-rule.js +29 -4
  33. package/dist/cjs/src/oracle/rules/pyth-rule.js +5 -4
  34. package/dist/cjs/src/oracle/rules/waterx-rule.d.ts +126 -0
  35. package/dist/cjs/src/oracle/rules/waterx-rule.js +312 -0
  36. package/dist/cjs/src/perp/client.d.ts +71 -20
  37. package/dist/cjs/src/perp/client.js +35 -13
  38. package/dist/cjs/src/perp/config.d.ts +6 -7
  39. package/dist/cjs/src/perp/config.js +12 -11
  40. package/dist/cjs/src/perp/constants.d.ts +0 -6
  41. package/dist/cjs/src/perp/constants.js +11 -9
  42. package/dist/cjs/src/perp/fetch/account.js +3 -0
  43. package/dist/cjs/src/perp/fetch/bridge.js +2 -1
  44. package/dist/cjs/src/perp/fetch/market.js +2 -1
  45. package/dist/cjs/src/perp/fetch/positions.d.ts +16 -10
  46. package/dist/cjs/src/perp/fetch/positions.js +23 -20
  47. package/dist/cjs/src/perp/index.d.ts +9 -5
  48. package/dist/cjs/src/perp/index.js +16 -7
  49. package/dist/cjs/src/perp/liq-view.d.ts +64 -0
  50. package/dist/cjs/src/perp/liq-view.js +74 -0
  51. package/dist/cjs/src/perp/user/order.d.ts +13 -0
  52. package/dist/cjs/src/perp/user/order.js +30 -16
  53. package/dist/cjs/src/perp/user/staking.js +3 -2
  54. package/dist/cjs/src/perp/user/trading.js +25 -24
  55. package/dist/cjs/src/perp/user/wlp.js +6 -5
  56. package/dist/cjs/src/prediction/utils.d.ts +11 -2
  57. package/dist/cjs/src/prediction/utils.js +22 -22
  58. package/dist/cjs/src/unified-client.d.ts +41 -15
  59. package/dist/cjs/src/unified-client.js +4 -2
  60. package/dist/cjs/src/utils/format.d.ts +14 -0
  61. package/dist/cjs/src/utils/format.js +24 -0
  62. package/dist/cjs/src/utils/math.d.ts +304 -12
  63. package/dist/cjs/src/utils/math.js +397 -17
  64. package/dist/cjs/src/utils/validate.d.ts +69 -0
  65. package/dist/cjs/src/utils/validate.js +183 -0
  66. package/dist/src/account/account.js +2 -1
  67. package/dist/src/account/config.d.ts +3 -2
  68. package/dist/src/account/funding/balance.d.ts +25 -2
  69. package/dist/src/account/funding/balance.js +36 -24
  70. package/dist/src/account/funding/credit.js +6 -10
  71. package/dist/src/constants.d.ts +15 -1
  72. package/dist/src/constants.js +17 -3
  73. package/dist/src/generated/waterx_rule/deps/bucket_v2_framework/float.d.ts +8 -0
  74. package/dist/src/generated/waterx_rule/deps/bucket_v2_framework/float.js +10 -0
  75. package/dist/src/generated/waterx_rule/deps/sui/vec_map.d.ts +36 -0
  76. package/dist/src/generated/waterx_rule/deps/sui/vec_map.js +27 -0
  77. package/dist/src/generated/waterx_rule/waterx_rule.d.ts +813 -0
  78. package/dist/src/generated/waterx_rule/waterx_rule.js +855 -0
  79. package/dist/src/oracle/aggregate.d.ts +23 -21
  80. package/dist/src/oracle/aggregate.js +133 -59
  81. package/dist/src/oracle/config.d.ts +82 -41
  82. package/dist/src/oracle/config.js +1 -41
  83. package/dist/src/oracle/host.d.ts +26 -11
  84. package/dist/src/oracle/index.d.ts +6 -2
  85. package/dist/src/oracle/index.js +27 -9
  86. package/dist/src/oracle/price-update-rule.d.ts +16 -10
  87. package/dist/src/oracle/price-update-rule.js +3 -3
  88. package/dist/src/oracle/pyth.d.ts +44 -0
  89. package/dist/src/oracle/pyth.js +45 -5
  90. package/dist/src/oracle/read-plane.d.ts +70 -0
  91. package/dist/src/oracle/read-plane.js +74 -0
  92. package/dist/src/oracle/rule-registry.d.ts +14 -11
  93. package/dist/src/oracle/rule-registry.js +16 -11
  94. package/dist/src/oracle/rules/pyth-core-rule.js +5 -5
  95. package/dist/src/oracle/rules/pyth-lazer-rule.d.ts +21 -0
  96. package/dist/src/oracle/rules/pyth-lazer-rule.js +28 -3
  97. package/dist/src/oracle/rules/pyth-rule.js +5 -4
  98. package/dist/src/oracle/rules/waterx-rule.d.ts +126 -0
  99. package/dist/src/oracle/rules/waterx-rule.js +305 -0
  100. package/dist/src/perp/client.d.ts +71 -20
  101. package/dist/src/perp/client.js +36 -14
  102. package/dist/src/perp/config.d.ts +6 -7
  103. package/dist/src/perp/config.js +11 -9
  104. package/dist/src/perp/constants.d.ts +0 -6
  105. package/dist/src/perp/constants.js +10 -8
  106. package/dist/src/perp/fetch/account.js +3 -0
  107. package/dist/src/perp/fetch/bridge.js +2 -1
  108. package/dist/src/perp/fetch/market.js +2 -1
  109. package/dist/src/perp/fetch/positions.d.ts +16 -10
  110. package/dist/src/perp/fetch/positions.js +28 -20
  111. package/dist/src/perp/index.d.ts +9 -5
  112. package/dist/src/perp/index.js +6 -4
  113. package/dist/src/perp/liq-view.d.ts +64 -0
  114. package/dist/src/perp/liq-view.js +71 -0
  115. package/dist/src/perp/user/order.d.ts +13 -0
  116. package/dist/src/perp/user/order.js +30 -16
  117. package/dist/src/perp/user/staking.js +3 -2
  118. package/dist/src/perp/user/trading.js +25 -24
  119. package/dist/src/perp/user/wlp.js +6 -5
  120. package/dist/src/prediction/utils.d.ts +11 -2
  121. package/dist/src/prediction/utils.js +22 -22
  122. package/dist/src/unified-client.d.ts +41 -15
  123. package/dist/src/unified-client.js +4 -2
  124. package/dist/src/utils/format.d.ts +14 -0
  125. package/dist/src/utils/format.js +21 -0
  126. package/dist/src/utils/math.d.ts +304 -12
  127. package/dist/src/utils/math.js +394 -17
  128. package/dist/src/utils/validate.d.ts +69 -0
  129. package/dist/src/utils/validate.js +167 -0
  130. package/package.json +4 -1
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Exposes three namespaces over the two product-line sub-clients:
5
5
  *
6
- * const client = await WaterXClient.create({ network: "TESTNET" });
6
+ * const client = await WaterXClient.create({ network: "TESTNET", oracleSource: "pyth_rule" });
7
7
  * client.account.createAccount(tx, { alias }); // -> shared waterx_account + funding
8
8
  * client.perp.placeOrderRequest(tx, params); // -> perp builder
9
9
  * client.predict.placeOrder(tx, params); // -> prediction builder
@@ -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,17 +370,25 @@ 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
+ * REQUIRED there is NO default source: every deployment names its source
374
+ * explicitly (wire it from your own env var, e.g. `ORACLE_SOURCE`).
375
+ * Source-neutral by design: a source need not be Pyth (see `'waterx_rule'`).
372
376
  *
373
- * - `'pyth_rule'` (default) — Pyth Core updates on Core infra.
377
+ * - `'pyth_rule'` — Pyth Core updates; infra in the source's own
378
+ * `PYTH_CORE_INFRA` table.
374
379
  * - `'pyth_lazer_rule'` — Pyth Lazer signed updates (pair with `pythApiKey`
375
- * and a config carrying `packages.pyth_lazer_rule`).
380
+ * and a config carrying `packages.pyth_lazer_rule`); infra in the
381
+ * source's own `LAZER_INFRA` table.
382
+ * - `'waterx_rule'` — the first-party WaterX quote-center (Nautilus-TEE,
383
+ * ed25519 signed batches; no credential, no per-update fee); infra in the
384
+ * source's own `WATERX_INFRA` table. Pair with `waterxEndpoint` /
385
+ * `waterxFetch` when the browser needs a proxy.
376
386
  *
377
387
  * Each source is self-contained with no cross-source fallback; selecting a
378
388
  * source whose feed for a ticker is absent fails at tx-build (not at init).
379
389
  * See perp `CreateClientOptions.oracleSource` for the full note.
380
390
  */
381
- oracleSource?: OracleSource;
391
+ oracleSource: OracleSource | OracleSource[];
382
392
  /**
383
393
  * Pyth Lazer access token, forwarded to the perp line. Required under
384
394
  * `oracleSource: 'pyth_lazer_rule'`, unused by `'pyth_rule'`. A SECRET —
@@ -388,6 +398,22 @@ export interface ClientCreateOptions {
388
398
  pythApiKey?: string;
389
399
  /** Retry/timeout policy for the perp line's off-chain oracle fetches. */
390
400
  pythFetch?: PythFetchPolicy;
401
+ /**
402
+ * Quote-center base URL for `oracleSource: 'waterx_rule'`, forwarded to the
403
+ * perp line — overrides the source's own per-network `WATERX_INFRA` default.
404
+ *
405
+ * `waterx_rule` is the one source a BROWSER fetches itself, so it is bound by
406
+ * the quote-center deployment's CORS allowlist: a front end whose origin is
407
+ * not allowed points this at its own same-origin proxy. An absolute URL whose
408
+ * base PATH is preserved — `https://app.example/api/quote-center` fetches
409
+ * `…/api/quote-center/v1/quotes/update`. Unused by the Pyth sources.
410
+ */
411
+ waterxEndpoint?: string;
412
+ /**
413
+ * Retry/timeout policy — and `fetchImpl` — for the perp line's quote-center
414
+ * fetch. Falls back to the built-in defaults — never to `pythFetch`.
415
+ */
416
+ waterxFetch?: FetchPolicy;
391
417
  /** Perp-line overrides (network, grpcUrl, waterxConfigUrl, cache, …). */
392
418
  perp?: PerpLineOptions;
393
419
  /** Prediction-line overrides (network, grpcUrl, waterxConfigUrl, cache, settlement, …). */
@@ -427,6 +453,6 @@ export declare class WaterXClient {
427
453
  * `waterx-config` JSON) and returns a ready client. Each line can target a
428
454
  * different network via `opts.perp.network` / `opts.predict.network`.
429
455
  */
430
- static create(opts?: ClientCreateOptions): Promise<WaterXClient>;
456
+ static create(opts: ClientCreateOptions): Promise<WaterXClient>;
431
457
  }
432
458
  export {};
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Exposes three namespaces over the two product-line sub-clients:
6
6
  *
7
- * const client = await WaterXClient.create({ network: "TESTNET" });
7
+ * const client = await WaterXClient.create({ network: "TESTNET", oracleSource: "pyth_rule" });
8
8
  * client.account.createAccount(tx, { alias }); // -> shared waterx_account + funding
9
9
  * client.perp.placeOrderRequest(tx, params); // -> perp builder
10
10
  * client.predict.placeOrder(tx, params); // -> prediction builder
@@ -186,7 +186,7 @@ class WaterXClient {
186
186
  * `waterx-config` JSON) and returns a ready client. Each line can target a
187
187
  * different network via `opts.perp.network` / `opts.predict.network`.
188
188
  */
189
- static async create(opts = {}) {
189
+ static async create(opts) {
190
190
  const baseNetwork = opts.network ?? "TESTNET";
191
191
  const { network: perpNetwork, ...perpRest } = opts.perp ?? {};
192
192
  const { network: predictNetwork, ...predictRest } = opts.predict ?? {};
@@ -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
+ }
@@ -1,8 +1,96 @@
1
+ /**
2
+ * Perp / WLP number math: Move-mirroring formulas, the `Float` fixed-point
3
+ * mirrors the canonical raw estimates need, and the on-chain price encoding.
4
+ *
5
+ * ## Validation policy — two tiers, deliberate
6
+ *
7
+ * The per-function `@throws` blocks below are INSTANCES of this rule, not a
8
+ * dozen independent decisions — every tier-1 function carries one. Guards come
9
+ * from `utils/validate.ts` (`assertFinite` / `assertFiniteNonNegative` /
10
+ * `assertUnitFraction` / `assertTokenDecimal` / `assertUnsignedBigInt`) and
11
+ * every message names the offending parameter.
12
+ *
13
+ * 1. **MONEY PATH — throws `RangeError`.** The fee bundles
14
+ * (`calcRealLiqNetCostUsd`, `calcViewEstLiqFeesUsd`), the liquidation
15
+ * estimates (`calcEstLiqPrice`, `calcEstLiqPriceRaw`), collateral and
16
+ * withdrawable (`calcEffectiveCollateralUsd`,
17
+ * `calcMaxReducibleCollateralUsd`), and the WLP quote / APY surface
18
+ * (`calcWlpMintOut`, `calcWlpRedeemOut`, `calcDynamicFeeBps`,
19
+ * `annualizedApyFromRatio`, `calcWlpIncentiveApy`). These map garbage onto a
20
+ * PLAUSIBLE number — an `Infinity` fee returns 0, indistinguishable from
21
+ * "already liquidatable"; a negative fee ADDS to equity — so the input dies
22
+ * at the boundary instead of becoming a wrong price on a screen.
23
+ * 2. **BARE ARITHMETIC — no guards.** Mostly one-line helpers (`calcNotional`,
24
+ * `calcFee`, `calcUnrealizedPnl`, `calcLeverage`, `calcTotalTradingFeeRate`,
25
+ * `calcFundingRate`, `calcFundingFeeUsd`, `decodeFundingIndexDelta`,
26
+ * `calcBorrowRate`, `calcBorrowRateAccrual`, `calcPositionBorrowFee`,
27
+ * `calcTokenUtilizationBps`, `annualizeFundingRate`, `calcWlpPrice`), plus
28
+ * `calcImpactFeeRate` — NOT a one-liner (a branchy curve with eight early
29
+ * returns) but the same category: each of its early returns tests an exact
30
+ * zero or an ordering that a NaN never satisfies, so garbage falls straight
31
+ * through the branches instead of being absorbed by one. A NaN / Infinity
32
+ * input propagates VISIBLY to the output in all of them — there is no
33
+ * plausible-looking value for it to hide behind — so a guard would add noise
34
+ * and cost without buying safety.
35
+ *
36
+ * `rawPrice` sits in neither tier: it is a PARSE, and throws plain `Error` on
37
+ * malformed input.
38
+ *
39
+ * ### Documented domain zeros
40
+ * Distinct from garbage: an input that is legitimately zero and has exactly one
41
+ * honest answer returns it rather than throwing. `sizeInAsset === 0` /
42
+ * `entryNotional === 0` → `0` (nothing to liquidate); `totalSupply === 0` → `0`
43
+ * (no shares to price — `calcWlpMintOut` is the deliberate exception, where it
44
+ * means the bootstrap mint); `intervalMs === 0` / `days <= 0` → `0` (no window
45
+ * to annualize over); `liquidityAmount === 0` → `0` utilization;
46
+ * `collateralUsd === 0` → `Infinity` leverage. Each is noted at its function.
47
+ */
48
+ /**
49
+ * A USD price as a plain decimal STRING (`"95000"` / `"95000.5"`) — the EXACT
50
+ * mode of `rawPrice`.
51
+ *
52
+ * The digits are parsed straight onto the 1e9 grid with no f64 round-trip, so
53
+ * the conversion is digit-exact at any magnitude (up to 9 decimal places;
54
+ * `rawPrice` throws on malformed input, a leading `-`, scientific notation, or
55
+ * >9 decimals, which the grid cannot represent).
56
+ *
57
+ * Prefer this mode for any price that is an EXACT KEY rather than a bound —
58
+ * above all `triggerPrice`, where a raw value off by a single 1e-9 unit
59
+ * silently fails the order-book lookup.
60
+ */
61
+ export type ExactDecimalUsd = string;
62
+ /**
63
+ * Accepted input to `rawPrice`, in two DELIBERATELY different modes:
64
+ *
65
+ * - `number` — LOSSY above the f64 cliff. Exact only while `usd × 1e9` stays
66
+ * within 2^53 (i.e. below ≈ $9,007,199), and even below that a non-binary
67
+ * fraction rounds to the nearest representable double before scaling.
68
+ * Fine for slippage bounds (`acceptablePrice`), where ±1 raw unit is noise.
69
+ * - {@link ExactDecimalUsd} (`string`) — digit-exact at any magnitude. Use for
70
+ * exact order-book keys (`triggerPrice`).
71
+ *
72
+ * The union is NOT "either works": picking `number` for an exact key is the
73
+ * documented footgun this alias exists to surface at the call site.
74
+ */
75
+ export type RawPriceInput = number | ExactDecimalUsd;
1
76
  /**
2
77
  * Convert a human-readable USD price to the raw 1e9-scaled `u128` value
3
78
  * that on-chain `Float`-typed parameters expect.
79
+ *
80
+ * Use for **tx-build** price args only (`acceptablePrice` / `triggerPrice` /
81
+ * size args). Do NOT use for the view-read params `basePriceUsd` /
82
+ * `collateralPriceUsd` on `perp/fetch` (`getPosition`, `getMarketPositions`,
83
+ * `getOrder`, …) — those take WHOLE-DOLLAR integer USD (the Move view applies
84
+ * `float::from` internally; a 1e9-scaled value inflates pnl/notional-derived
85
+ * fields by 1e9).
86
+ *
87
+ * Precision: see {@link RawPriceInput}. The `number` path rounds through f64
88
+ * and is exact only below ≈ $9,007,199; an {@link ExactDecimalUsd} string
89
+ * parses digits directly onto the 1e9 grid without touching f64. This bites
90
+ * hardest on `triggerPrice`, which is an EXACT order-book key: a raw value off
91
+ * by one unit silently fails the order lookup.
4
92
  */
5
- export declare function rawPrice(usd: number | string): bigint;
93
+ export declare function rawPrice(usd: RawPriceInput): bigint;
6
94
  /** Notional value in USD: sizeInAsset × price. */
7
95
  export declare function calcNotional(sizeInAsset: number, price: number): number;
8
96
  /** Trading fee in USD: notional × feeRate. */
@@ -12,9 +100,89 @@ export declare function calcUnrealizedPnl(isLong: boolean, entryPrice: number, s
12
100
  /** Position leverage = notional / collateralUsd. Returns Infinity when collateral is zero. */
13
101
  export declare function calcLeverage(sizeUsd: number, collateralUsd: number): number;
14
102
  /**
15
- * Estimated liquidation price.
103
+ * Accrued-fee inputs for the REAL-model liquidation estimate — see
104
+ * `calcRealLiqNetCostUsd` for the rule and the Move-semantics note.
105
+ *
106
+ * `fundingFeeUsd` is SIGNED, cost-positive: > 0 the position owes funding,
107
+ * < 0 is funding income (a genuine equity credit, applied in full).
108
+ */
109
+ export type LiqFeeBundle = {
110
+ borrowFeeUsd: number;
111
+ openFeeUsd: number;
112
+ closingFeeUsd: number;
113
+ /** SIGNED, cost-positive: > 0 owed, < 0 income (credits equity in full). */
114
+ fundingFeeUsd: number;
115
+ };
116
+ /**
117
+ * Net fee cost (USD) of the REAL liquidation check — a plain SIGNED sum:
118
+ *
119
+ * netCostUsd = borrowFeeUsd + openFeeUsd + closingFeeUsd + fundingFeeUsd
120
+ *
121
+ * `fundingFeeUsd` is SIGNED cost-positive, and the result MAY BE NEGATIVE
122
+ * when funding income exceeds the other fees. That is deliberate:
123
+ * `position.move::is_liquidatable` credits funding income IN FULL — income
124
+ * first pays down any accumulated deficit, and the remainder is added back to
125
+ * remaining equity — so the linearized equity model is a plain signed sum
126
+ * with no floor. Flooring at 0 here would understate an income-rich
127
+ * position's equity and show a liq price closer to spot than the real check.
128
+ *
129
+ * Three-way Move-semantics relationship (verified against the Move sources
130
+ * 2026-07-30):
131
+ * - the REAL liquidation check, `position.move::is_liquidatable`
132
+ * (waterx_perp): deducts borrow + open + CLOSING fee and credits funding
133
+ * income in full (deficit first, remainder back to equity) — exactly this
134
+ * function's signed sum;
135
+ * - the view estimate, `view.move::calculate_est_liq_price`
136
+ * (waterx_perp_view): OMITS the closing fee AND floors its fee bundle at 0
137
+ * (`Float.saturating_sub` — Float is unsigned) — see `calcViewEstLiqFeesUsd`
138
+ * and the op-exact `calcEstLiqPriceRaw`;
139
+ * - SDK `calcEffectiveCollateralUsd` does NOT credit funding income at all:
140
+ * it mirrors the withdrawable-collateral checks
141
+ * (`calculate_effective_collateral_amount` in `trading.move`), not the
142
+ * liquidation inequality.
143
+ * Do not "fix" any of the three to match another.
144
+ *
145
+ * @throws RangeError when borrow/open/closing fees are not finite `>= 0`
146
+ * numbers, or `fundingFeeUsd` is not finite.
147
+ */
148
+ export declare function calcRealLiqNetCostUsd(fees: LiqFeeBundle): number;
149
+ /**
150
+ * Fee bundle (USD) of the VIEW estimate `view.move::calculate_est_liq_price`:
151
+ *
152
+ * viewFeesUsd = max(0, borrowFeeUsd + openFeeUsd + fundingFeeUsd)
153
+ *
154
+ * Mirrors the view's unsigned Float arithmetic: funding income is credited
155
+ * via `Float.saturating_sub`, so the bundle FLOORS AT 0 — income beyond the
156
+ * other fees is discarded by the view. There is deliberately NO
157
+ * `closingFeeUsd` field: the view omits the close-fee term, and this shape
158
+ * makes it structurally impossible to include one. For the REAL liquidation
159
+ * check's semantics use `calcRealLiqNetCostUsd`; for chain-bit-identical
160
+ * output use `calcEstLiqPriceRaw`.
161
+ *
162
+ * Exported on purpose even though `calcEstLiqPriceRaw` covers the chain-exact
163
+ * path: this is the VIEW-model counterpart of `calcRealLiqNetCostUsd`, for
164
+ * consumers that need the view's fee bundle as a Number (mirroring what the
165
+ * view displays) rather than the full raw price. The two-API split is what makes
166
+ * the REAL and VIEW models impossible to confuse — do not fold it away.
167
+ *
168
+ * @throws RangeError when borrow/open fees are not finite `>= 0` numbers, or
169
+ * `fundingFeeUsd` is not finite.
170
+ */
171
+ export declare function calcViewEstLiqFeesUsd(fees: {
172
+ borrowFeeUsd: number;
173
+ openFeeUsd: number;
174
+ /** SIGNED, cost-positive: > 0 owed, < 0 income (bundle floors at 0). */
175
+ fundingFeeUsd: number;
176
+ }): number;
177
+ /**
178
+ * Estimated liquidation price — Number (f64) UI convenience.
179
+ *
180
+ * An approximation for display. The CANONICAL implementation is
181
+ * `calcEstLiqPriceRaw` (BigInt fixed-point, op-for-op mirror of
182
+ * `calculate_est_liq_price` in `waterx_perp_view/sources/view.move`); use that
183
+ * wherever exact parity with the on-chain `est_liq_price` matters.
16
184
  *
17
- * Matches `calculate_est_liq_price` in `waterx_perp_view/sources/view.move`:
185
+ * Same linear model as the view:
18
186
  * maintenance = maintenanceMarginRate × (size × spotPrice) ← uses current notional
19
187
  * ratio = (collateralUsd − totalFeesUsd − maintenance) / (size × avgPrice)
20
188
  * long: liq = avgPrice × (1 − ratio)
@@ -22,7 +190,24 @@ export declare function calcLeverage(sizeUsd: number, collateralUsd: number): nu
22
190
  *
23
191
  * Returns 0 when the position is already liquidatable or has no size.
24
192
  *
25
- * @param totalFeesUsd Sum of all accrued fees in USD: borrow + trading + net funding.
193
+ * Fees pass EITHER:
194
+ * - `fees`: the structured bundle, matching the REAL liquidation check
195
+ * (`position.move::is_liquidatable`). `totalFeesUsd` is derived via
196
+ * `calcRealLiqNetCostUsd` — a SIGNED sum including the closing fee, with
197
+ * funding income credited IN FULL, so a caller structurally cannot omit a
198
+ * term or mis-handle income. A negative net cost ADDS to the remaining
199
+ * margin, pushing the estimate FARTHER from spot. Takes precedence when
200
+ * both are given; or
201
+ * - `totalFeesUsd`: a pre-computed number (back-compat path) — the caller
202
+ * owns the fee model.
203
+ * NOTE the deliberate model difference vs the on-chain VIEW: the view both
204
+ * floors its fee bundle at 0 and omits the close fee (`calcViewEstLiqFeesUsd`
205
+ * / `calcEstLiqPriceRaw`); the `fees` path here matches the REAL check.
206
+ *
207
+ * @throws RangeError when sizeInAsset / avgPrice / spotPrice / collateralUsd
208
+ * are not finite `>= 0` numbers, when `maintenanceMarginRate` is not finite
209
+ * inside `[0, 1]`, or when `totalFeesUsd` is not finite.
210
+ * (`sizeInAsset === 0` stays a documented domain case returning 0.)
26
211
  */
27
212
  export declare function calcEstLiqPrice(params: {
28
213
  isLong: boolean;
@@ -31,8 +216,74 @@ export declare function calcEstLiqPrice(params: {
31
216
  collateralUsd: number;
32
217
  maintenanceMarginRate: number;
33
218
  spotPrice: number;
219
+ } & ({
34
220
  totalFeesUsd: number;
35
- }): number;
221
+ fees?: LiqFeeBundle;
222
+ } | {
223
+ totalFeesUsd?: number;
224
+ fees: LiqFeeBundle;
225
+ })): number;
226
+ /**
227
+ * Estimated liquidation price — CANONICAL raw fixed-point implementation.
228
+ *
229
+ * Op-for-op mirror of `calculate_est_liq_price` in
230
+ * `waterx_perp_view/sources/view.move` under `bucket_v2_framework::float`
231
+ * semantics: unsigned 1e9 fixed-point, every `mul` is `(a×b)/1e9` and every
232
+ * `div` is `(a×1e9)/b` with truncating BigInt division at EACH step,
233
+ * including `math::amount_to_usd`'s exact composition, the u64 addition of
234
+ * borrow + open fee BEFORE the USD conversion, and the `saturating_sub`
235
+ * funding credit (the VIEW model: fee bundle floors at 0, close fee omitted).
236
+ * Given the same raw inputs the view receives, the result is bit-identical to
237
+ * the chain's `PositionData.est_liq_price`.
238
+ *
239
+ * The Number `calcEstLiqPrice` is a UI convenience approximation of this
240
+ * canonical form (and its `fees` path models the REAL liquidation check
241
+ * instead of the view — see `calcRealLiqNetCostUsd`).
242
+ *
243
+ * Inputs are the raw on-chain values exactly as the view takes them.
244
+ * Returns the raw 1e9-scaled u128 price; `0n` = already liquidatable /
245
+ * zero size (the view's N/A signal).
246
+ *
247
+ * Holding a fetched `PositionDataView` row? Use `calcEstLiqPriceRawFromView`
248
+ * (`perp/liq-view.ts`) instead of hand-mapping its nine raw fields — the adapter
249
+ * owns that mapping and carries the invariant below on its own signature.
250
+ *
251
+ * INVARIANT the signature cannot enforce: `basePriceUsd` / `collateralPriceUsd`
252
+ * MUST be the same whole-dollar values passed to the `perp/fetch` read that
253
+ * produced the row whose fields you are feeding in. `PositionDataView` does not
254
+ * carry the probe prices, so nothing here can check it — feed different prices
255
+ * and the fee/notional-derived fields were computed against one price while the
256
+ * estimate is computed against another, and parity with
257
+ * `PositionData.est_liq_price` silently breaks.
258
+ *
259
+ * @throws RangeError when any bigint input is negative or
260
+ * `collateralDecimal` is not an integer in `[0, 19]` (u64 `10^decimal`).
261
+ */
262
+ export declare function calcEstLiqPriceRaw(params: {
263
+ isLong: boolean;
264
+ /** `PositionData.size` — raw 1e9-scaled Float value. */
265
+ sizeRaw: bigint;
266
+ /** `PositionData.average_price` — raw 1e9-scaled Float value. */
267
+ avgPriceRaw: bigint;
268
+ /** `PositionData.collateral_amount` — raw collateral token units. */
269
+ collateralAmountRaw: bigint;
270
+ /** `PositionData.collateral_decimal`. */
271
+ collateralDecimal: number;
272
+ /** Whole-dollar u64 base price — the exact value passed to the view (pre `float::from`). */
273
+ basePriceUsd: bigint;
274
+ /** Whole-dollar u64 collateral price — the exact value passed to the view (pre `float::from`). */
275
+ collateralPriceUsd: bigint;
276
+ /** `MarketData.maintenance_margin` — raw 1e9-scaled Float value. */
277
+ maintenanceMarginRaw: bigint;
278
+ /** `PositionData.borrow_fee` (accrued + unrealized, pre-combined by the view) — raw collateral units. */
279
+ borrowFeeRaw: bigint;
280
+ /** `PositionData.funding_fee_positive` — true when the position owes funding. */
281
+ fundingSign: boolean;
282
+ /** `PositionData.funding_fee` magnitude — raw collateral units. */
283
+ fundingFeeRaw: bigint;
284
+ /** `PositionData.unrealized_trading_fee` (open fee) — raw collateral units. */
285
+ tradingFeeRaw: bigint;
286
+ }): bigint;
36
287
  /**
37
288
  * Effective (fee-adjusted) collateral in USD.
38
289
  *
@@ -53,9 +304,11 @@ export declare function calcEstLiqPrice(params: {
53
304
  * @param grossCollateralUsd Position collateral in USD (`collateral_amount` → USD).
54
305
  * @param borrowFeeUsd `unrealized_borrow_fee` in USD.
55
306
  * @param fundingSign `unrealized_funding_sign` — true when the position owes funding.
56
- * @param fundingFeeUsd `unrealized_funding_fee` magnitude in USD.
307
+ * @param fundingFeeUsd `unrealized_funding_fee` magnitude in USD (unsigned; sign travels separately).
57
308
  * @param tradingFeeUsd `unrealized_trading_fee` in USD.
58
309
  * @param projectedTradingFeeUsd Closing fee to reserve (0 for a bare collateral withdrawal).
310
+ * @throws RangeError when any USD input is not a finite `>= 0` number — a
311
+ * negative fee would silently ADD to effective collateral.
59
312
  */
60
313
  export declare function calcEffectiveCollateralUsd(params: {
61
314
  grossCollateralUsd: number;
@@ -101,6 +354,16 @@ export declare function calcEffectiveCollateralUsd(params: {
101
354
  * @param closingFeeUsd Full closing fee in USD (`close_fee` → USD).
102
355
  * @param collateralPriceUsd Oracle price of the collateral token (USD per token).
103
356
  * @param collateralDecimal Collateral token decimals — sets the smallest withdraw step.
357
+ * @throws RangeError when any USD / size / price input is not a finite `>= 0`
358
+ * number, when `maintenanceMarginRate` is not finite inside `[0, 1]`, or when
359
+ * `collateralDecimal` is not an integer in `[0, 19]` — the same domains
360
+ * `calcEstLiqPrice` / `calcEstLiqPriceRaw` enforce. Two of these are the
361
+ * reason the whole set is here: a negative fee silently INFLATES the
362
+ * withdrawable amount, and a negative `collateralPriceUsd` drops the
363
+ * one-raw-unit liquidation back-off to 0 — removing the abort-safety margin
364
+ * while still returning a plausible-looking dollar figure.
365
+ * (`maxLeverage === 0` → no leverage cap and `collateralPriceUsd === 0` → no
366
+ * back-off stay documented domain zeros, not garbage.)
104
367
  */
105
368
  export declare function calcMaxReducibleCollateralUsd(params: {
106
369
  grossCollateralUsd: number;
@@ -192,8 +455,11 @@ export declare function calcBorrowRate(utilizationBps: number, rate0: number, ra
192
455
  /**
193
456
  * Time-weighted borrow rate accrual for a given elapsed period.
194
457
  *
195
- * Matches `calculate_borrow_rate_accrual` in `lp_pool.move`.
196
- * `elapsedMs / intervalMs` gives the number of completed intervals.
458
+ * Continuous proration: `rate × elapsedMs / intervalMs`, matching the formula
459
+ * of `lp_pool.move::calculate_borrow_rate_accrual`
460
+ * (`borrow_rate.mul_u64(elapsed_ms).div_u64(interval_ms)`) — the contract does
461
+ * NOT floor to completed intervals; a partial interval accrues pro rata
462
+ * (verified against the Move source 2026-07-29).
197
463
  */
198
464
  export declare function calcBorrowRateAccrual(borrowRate: number, elapsedMs: number, intervalMs: number): number;
199
465
  /**
@@ -220,19 +486,22 @@ export declare function annualizeFundingRate(rate: number, intervalMs: number):
220
486
  * Annualized APY from a NAV ratio over a given number of days.
221
487
  *
222
488
  * Compounds `ratio` (WLP price now / WLP price past) to a 365-day return.
223
- * Returns 0 when the result is not finite (e.g. ratio 0 or days = 0).
489
+ * Documented domain cases returning 0: `ratio <= 0` or `days <= 0` (no valid
490
+ * sample window), and an overflowing compound result.
224
491
  *
225
492
  * @param ratio Current NAV divided by past NAV (e.g. 1.05 for 5% growth).
226
493
  * @param days Number of days elapsed between the two NAV samples.
494
+ * @throws RangeError when `ratio` or `days` is NaN / ±Infinity.
227
495
  */
228
496
  export declare function annualizedApyFromRatio(ratio: number, days: number): number;
229
497
  /**
230
498
  * Convert a continuously-compounded incentive APR to APY.
231
499
  *
232
500
  * Rewards stream via `flow_rate` (continuous compounding), so APY = e^APR − 1.
233
- * Returns 0 when the result is not finite.
501
+ * Returns 0 when the compound result overflows to Infinity.
234
502
  *
235
503
  * @param apr Time-weighted incentive APR as a decimal fraction (e.g. 0.12 for 12%).
504
+ * @throws RangeError when `apr` is NaN / ±Infinity.
236
505
  */
237
506
  export declare function calcWlpIncentiveApy(apr: number): number;
238
507
  /**
@@ -248,12 +517,21 @@ export declare function calcWlpPrice(tvlUsd: number, totalSupply: number, lpDeci
248
517
  *
249
518
  * Matches the LP-amount formula in `mint_wlp_with_pricing_tvl` in `lp_pool.move`.
250
519
  * Pass `netDepositUsd` (after the dynamic mint fee is deducted).
251
- * Bootstrap path (totalSupply === 0): lpAmount = netDepositUsd × 10^lpDecimals.
520
+ *
521
+ * Bootstrap ($1/share par, `lpAmount = netDepositUsd × 10^lpDecimals`) applies
522
+ * ONLY to the genuine first mint (`totalSupply === 0`) — exactly like the
523
+ * chain. When supply is outstanding but the priced TVL has floored to 0 (e.g.
524
+ * trader unrealized profit ≥ TVL drove the equity `saturating_sub` to 0) the
525
+ * chain ABORTS `EInvalidBootstrap` rather than par-minting cheap shares that
526
+ * dilute existing LPs (re-audit F-023); this helper throws `RangeError` in
527
+ * that state instead of silently par-quoting.
252
528
  *
253
529
  * @param netDepositUsd Deposit value in USD after dynamic mint fee.
254
530
  * @param tvlUsd Pool TVL in USD at pricing time.
255
531
  * @param totalSupply Current total WLP supply in raw units.
256
532
  * @param lpDecimals WLP token decimals (6).
533
+ * @throws RangeError when inputs are not finite `>= 0` numbers, or on the
534
+ * `totalSupply > 0 && tvlUsd === 0` state (chain aborts `EInvalidBootstrap`).
257
535
  */
258
536
  export declare function calcWlpMintOut(netDepositUsd: number, tvlUsd: number, totalSupply: number, lpDecimals: number): number;
259
537
  /**
@@ -262,11 +540,21 @@ export declare function calcWlpMintOut(netDepositUsd: number, tvlUsd: number, to
262
540
  * Matches the settlement formula in `settle_redeem_with_pricing_tvl` in `lp_pool.move`.
263
541
  * Apply `calcDynamicFeeBps` separately to get the net output.
264
542
  *
543
+ * Chain-divergence notes (display-convenience helper, not settlement-exact):
544
+ * - `tokenPriceUsd === 0` returns 0 here, but the chain ABORTS (`EZeroPrice`,
545
+ * lp_pool.move) — a zero from this helper on that input is a display
546
+ * placeholder, not a real quote.
547
+ * - The burn fee is applied on-chain as
548
+ * `float::from_bps(fee_bps).mul_u64(raw_amount).ceil()` — composing this
549
+ * helper with `calcDynamicFeeBps` in f64 (which cannot `.ceil()` on the raw
550
+ * grid) can drift ±1 raw unit from the settled output.
551
+ *
265
552
  * @param lpAmount LP tokens being redeemed (raw units).
266
553
  * @param tvlUsd Pool TVL in USD at pricing time.
267
554
  * @param totalSupply Current total WLP supply in raw units.
268
555
  * @param tokenPriceUsd Oracle price of the output token.
269
556
  * @param tokenDecimals Output token decimals.
557
+ * @throws RangeError when any numeric input is not a finite `>= 0` number.
270
558
  */
271
559
  export declare function calcWlpRedeemOut(lpAmount: number, tvlUsd: number, totalSupply: number, tokenPriceUsd: number, tokenDecimals: number): number;
272
560
  /**
@@ -275,7 +563,8 @@ export declare function calcWlpRedeemOut(lpAmount: number, tvlUsd: number, total
275
563
  * Matches `calculate_dynamic_fee` in `lp_pool.move`. Returns `baseFeeBps` when
276
564
  * the operation moves the token closer to (or does not worsen) its target weight.
277
565
  * Adds an additional fee proportional to the average deviation when it moves
278
- * further away.
566
+ * further away. Clamped to 100% (`bp_scale`) on both the additional term and
567
+ * the total, mirroring the on-chain F-039 clamp.
279
568
  *
280
569
  * @param tokenValueUsd Current USD value of this token in the pool.
281
570
  * @param tvlUsd Total pool TVL in USD.
@@ -283,5 +572,8 @@ export declare function calcWlpRedeemOut(lpAmount: number, tvlUsd: number, total
283
572
  * @param targetWeightBps Target allocation weight for this token (bps).
284
573
  * @param baseFeeBps Base mint/burn fee (bps).
285
574
  * @param isDeposit True for mint, false for redeem.
575
+ * @throws RangeError when any USD / bps input is not a finite `>= 0` number.
576
+ * (`tvlUsd` / `operationValueUsd` / `targetWeightBps` of exactly 0 stay
577
+ * documented domain cases returning `baseFeeBps` — nothing to deviate from.)
286
578
  */
287
579
  export declare function calcDynamicFeeBps(tokenValueUsd: number, tvlUsd: number, operationValueUsd: number, targetWeightBps: number, baseFeeBps: number, isDeposit: boolean): number;