@waterx/sdk 4.0.0 → 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 (128) hide show
  1. package/README.md +52 -37
  2. package/dist/cjs/src/account/account.js +2 -1
  3. package/dist/cjs/src/account/config.d.ts +0 -16
  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 +940 -0
  14. package/dist/cjs/src/generated/waterx_rule/waterx_rule.js +1092 -0
  15. package/dist/cjs/src/oracle/aggregate.d.ts +21 -21
  16. package/dist/cjs/src/oracle/aggregate.js +84 -71
  17. package/dist/cjs/src/oracle/config.d.ts +107 -52
  18. package/dist/cjs/src/oracle/config.js +15 -35
  19. package/dist/cjs/src/oracle/host.d.ts +13 -2
  20. package/dist/cjs/src/oracle/index.d.ts +4 -2
  21. package/dist/cjs/src/oracle/index.js +25 -6
  22. package/dist/cjs/src/oracle/price-update-rule.d.ts +3 -4
  23. package/dist/cjs/src/oracle/pyth.d.ts +68 -6
  24. package/dist/cjs/src/oracle/pyth.js +338 -22
  25. package/dist/cjs/src/oracle/rule-registry.d.ts +11 -6
  26. package/dist/cjs/src/oracle/rule-registry.js +13 -6
  27. package/dist/cjs/src/oracle/rules/pyth-core-rule.js +17 -2
  28. package/dist/cjs/src/oracle/rules/pyth-lazer-rule.d.ts +6 -6
  29. package/dist/cjs/src/oracle/rules/pyth-lazer-rule.js +25 -22
  30. package/dist/cjs/src/oracle/rules/pyth-rule.js +5 -0
  31. package/dist/cjs/src/oracle/rules/waterx-rule.d.ts +89 -0
  32. package/dist/cjs/src/oracle/rules/waterx-rule.js +272 -0
  33. package/dist/cjs/src/oracle/update-fetch.d.ts +32 -2
  34. package/dist/cjs/src/oracle/update-fetch.js +60 -3
  35. package/dist/cjs/src/perp/client.d.ts +71 -19
  36. package/dist/cjs/src/perp/client.js +30 -10
  37. package/dist/cjs/src/perp/config.d.ts +4 -7
  38. package/dist/cjs/src/perp/config.js +9 -12
  39. package/dist/cjs/src/perp/constants.d.ts +0 -6
  40. package/dist/cjs/src/perp/constants.js +11 -9
  41. package/dist/cjs/src/perp/fetch/account.js +3 -0
  42. package/dist/cjs/src/perp/fetch/bridge.js +2 -1
  43. package/dist/cjs/src/perp/fetch/market.js +2 -1
  44. package/dist/cjs/src/perp/fetch/positions.d.ts +16 -10
  45. package/dist/cjs/src/perp/fetch/positions.js +23 -20
  46. package/dist/cjs/src/perp/index.d.ts +8 -4
  47. package/dist/cjs/src/perp/index.js +12 -7
  48. package/dist/cjs/src/perp/liq-view.d.ts +64 -0
  49. package/dist/cjs/src/perp/liq-view.js +74 -0
  50. package/dist/cjs/src/perp/user/order.d.ts +13 -0
  51. package/dist/cjs/src/perp/user/order.js +30 -16
  52. package/dist/cjs/src/perp/user/staking.js +3 -2
  53. package/dist/cjs/src/perp/user/trading.js +25 -24
  54. package/dist/cjs/src/perp/user/wlp.js +6 -5
  55. package/dist/cjs/src/prediction/utils.d.ts +11 -2
  56. package/dist/cjs/src/prediction/utils.js +22 -22
  57. package/dist/cjs/src/unified-client.d.ts +49 -20
  58. package/dist/cjs/src/unified-client.js +4 -1
  59. package/dist/cjs/src/utils/format.d.ts +14 -0
  60. package/dist/cjs/src/utils/format.js +24 -0
  61. package/dist/cjs/src/utils/math.d.ts +304 -12
  62. package/dist/cjs/src/utils/math.js +397 -17
  63. package/dist/cjs/src/utils/validate.d.ts +69 -0
  64. package/dist/cjs/src/utils/validate.js +183 -0
  65. package/dist/src/account/account.js +2 -1
  66. package/dist/src/account/config.d.ts +0 -16
  67. package/dist/src/account/funding/balance.d.ts +25 -2
  68. package/dist/src/account/funding/balance.js +36 -24
  69. package/dist/src/account/funding/credit.js +6 -10
  70. package/dist/src/constants.d.ts +15 -1
  71. package/dist/src/constants.js +17 -3
  72. package/dist/src/generated/waterx_rule/deps/bucket_v2_framework/float.d.ts +8 -0
  73. package/dist/src/generated/waterx_rule/deps/bucket_v2_framework/float.js +10 -0
  74. package/dist/src/generated/waterx_rule/deps/sui/vec_map.d.ts +36 -0
  75. package/dist/src/generated/waterx_rule/deps/sui/vec_map.js +27 -0
  76. package/dist/src/generated/waterx_rule/waterx_rule.d.ts +940 -0
  77. package/dist/src/generated/waterx_rule/waterx_rule.js +991 -0
  78. package/dist/src/oracle/aggregate.d.ts +21 -21
  79. package/dist/src/oracle/aggregate.js +84 -71
  80. package/dist/src/oracle/config.d.ts +107 -52
  81. package/dist/src/oracle/config.js +14 -34
  82. package/dist/src/oracle/host.d.ts +13 -2
  83. package/dist/src/oracle/index.d.ts +4 -2
  84. package/dist/src/oracle/index.js +18 -7
  85. package/dist/src/oracle/price-update-rule.d.ts +3 -4
  86. package/dist/src/oracle/pyth.d.ts +68 -6
  87. package/dist/src/oracle/pyth.js +334 -22
  88. package/dist/src/oracle/rule-registry.d.ts +11 -6
  89. package/dist/src/oracle/rule-registry.js +13 -6
  90. package/dist/src/oracle/rules/pyth-core-rule.js +18 -3
  91. package/dist/src/oracle/rules/pyth-lazer-rule.d.ts +6 -6
  92. package/dist/src/oracle/rules/pyth-lazer-rule.js +26 -23
  93. package/dist/src/oracle/rules/pyth-rule.js +5 -0
  94. package/dist/src/oracle/rules/waterx-rule.d.ts +89 -0
  95. package/dist/src/oracle/rules/waterx-rule.js +266 -0
  96. package/dist/src/oracle/update-fetch.d.ts +32 -2
  97. package/dist/src/oracle/update-fetch.js +57 -3
  98. package/dist/src/perp/client.d.ts +71 -19
  99. package/dist/src/perp/client.js +31 -11
  100. package/dist/src/perp/config.d.ts +4 -7
  101. package/dist/src/perp/config.js +9 -12
  102. package/dist/src/perp/constants.d.ts +0 -6
  103. package/dist/src/perp/constants.js +10 -8
  104. package/dist/src/perp/fetch/account.js +3 -0
  105. package/dist/src/perp/fetch/bridge.js +2 -1
  106. package/dist/src/perp/fetch/market.js +2 -1
  107. package/dist/src/perp/fetch/positions.d.ts +16 -10
  108. package/dist/src/perp/fetch/positions.js +28 -20
  109. package/dist/src/perp/index.d.ts +8 -4
  110. package/dist/src/perp/index.js +5 -3
  111. package/dist/src/perp/liq-view.d.ts +64 -0
  112. package/dist/src/perp/liq-view.js +71 -0
  113. package/dist/src/perp/user/order.d.ts +13 -0
  114. package/dist/src/perp/user/order.js +30 -16
  115. package/dist/src/perp/user/staking.js +3 -2
  116. package/dist/src/perp/user/trading.js +25 -24
  117. package/dist/src/perp/user/wlp.js +6 -5
  118. package/dist/src/prediction/utils.d.ts +11 -2
  119. package/dist/src/prediction/utils.js +22 -22
  120. package/dist/src/unified-client.d.ts +49 -20
  121. package/dist/src/unified-client.js +4 -1
  122. package/dist/src/utils/format.d.ts +14 -0
  123. package/dist/src/utils/format.js +21 -0
  124. package/dist/src/utils/math.d.ts +304 -12
  125. package/dist/src/utils/math.js +394 -17
  126. package/dist/src/utils/validate.d.ts +69 -0
  127. package/dist/src/utils/validate.js +167 -0
  128. package/package.json +4 -1
@@ -17,7 +17,11 @@
17
17
  * `Authorization` header at all). This is the Phase-0 invariant of the
18
18
  * Pyth Pro migration: existing keyless deployments see no behavior change.
19
19
  * - Retries on network errors, HTTP 429, and HTTP 5xx, with exponential
20
- * backoff (`retryDelayMs * 2^attempt`, capped at `MAX_BACKOFF_MS`). Other
20
+ * backoff (`retryDelayMs * 2^attempt`, capped at `MAX_BACKOFF_MS`). A 429
21
+ * carrying a numeric `Retry-After` header uses the SERVER'S delay instead,
22
+ * when it fits under the same cap — a longer ask degrades to normal
23
+ * backoff rather than stalling a money-path build for tens of seconds.
24
+ * Other
21
25
  * 4xx statuses (401/400/403/404/…) are NOT retried — auth/bad-request
22
26
  * failures are deterministic, so that `Response` (`ok: false`) is handed
23
27
  * back on the first attempt for the caller to format its own
@@ -47,10 +51,13 @@
47
51
  * - Retry worst case: with the defaults (15s timeout × 3 attempts + ~0.75s of
48
52
  * backoff between them) a FULL outage takes up to ~46s to surface as a
49
53
  * `FetchPolicyError`, vs ~15s pre-3.2.0's single bare-`fetch` attempt.
50
- * Tunable per client via `config.pyth.fetch.{timeoutMs,retries}`.
54
+ * Tunable per client via the `pythFetch` create option (`{timeoutMs,retries}`).
51
55
  */
52
56
  Object.defineProperty(exports, "__esModule", { value: true });
53
57
  exports.FetchPolicyError = void 0;
58
+ exports.rethrowExhaustedFetch = rethrowExhaustedFetch;
59
+ exports.trimTrailingSlashes = trimTrailingSlashes;
60
+ exports.joinEndpointPath = joinEndpointPath;
54
61
  exports.fetchWithPolicy = fetchWithPolicy;
55
62
  const DEFAULT_TIMEOUT_MS = 15_000;
56
63
  const DEFAULT_RETRIES = 2;
@@ -75,12 +82,60 @@ class FetchPolicyError extends Error {
75
82
  }
76
83
  }
77
84
  exports.FetchPolicyError = FetchPolicyError;
85
+ /**
86
+ * Rethrow a `catch`-ed {@link fetchWithPolicy} failure. When it is a
87
+ * status-carrying `FetchPolicyError` — a retryable status (429/5xx) that never
88
+ * recovered — throw a new Error `${describe(err)} (retries exhausted after N
89
+ * attempts)` with the original as `cause`; otherwise (a network-level
90
+ * exhaustion with no status, or any non-`FetchPolicyError`) rethrow it verbatim,
91
+ * since there is no domain reframing to add. `describe` builds the
92
+ * status-bearing prefix so each caller keeps its own message shape (the e2e
93
+ * transient detector keys off those prefixes) while the guard, the `retries
94
+ * exhausted` suffix, and the `cause` wrapping live in one place. `: never` so a
95
+ * caller's `catch` block is understood not to fall through.
96
+ */
97
+ function rethrowExhaustedFetch(err, describe) {
98
+ if (err instanceof FetchPolicyError && err.status !== undefined) {
99
+ throw new Error(`${describe(err)} (retries exhausted after ${err.attempts} attempts)`, {
100
+ cause: err,
101
+ });
102
+ }
103
+ throw err;
104
+ }
105
+ /**
106
+ * Join an API `path` onto an `endpoint` PRESERVING the endpoint's own base
107
+ * path. `new URL(path, endpoint)` is the footgun this replaces: a
108
+ * leading-slash path is *absolute* and silently discards the endpoint's path
109
+ * — harmless for a bare-origin endpoint (`https://hermes.pyth.network`) but
110
+ * it dropped the `/hermes` prefix of the Pyth Pro compat endpoint and 404'd
111
+ * every feed (see `fetchPriceFeedsUpdateData`). Every oracle fetch that
112
+ * targets `<endpoint><fixed path>` must build its URL here.
113
+ */
114
+ /** One canonical trailing-slash trim — `joinEndpointPath` (URL building) and
115
+ * `pyth.ts`'s `memoKey` (endpoint identity) must never drift apart on it. */
116
+ function trimTrailingSlashes(endpoint) {
117
+ return endpoint.replace(/\/+$/, "");
118
+ }
119
+ function joinEndpointPath(endpoint, path) {
120
+ return new URL(`${trimTrailingSlashes(endpoint)}/${path.replace(/^\/+/, "")}`);
121
+ }
78
122
  function isRetryableStatus(status) {
79
123
  return status === 429 || status >= 500;
80
124
  }
81
125
  function backoffMs(retryDelayMs, attempt) {
82
126
  return Math.min(retryDelayMs * 2 ** attempt, MAX_BACKOFF_MS);
83
127
  }
128
+ /**
129
+ * The server's own `Retry-After` (numeric-seconds form only), when present
130
+ * and within `MAX_BACKOFF_MS` — `undefined` otherwise (absent, HTTP-date
131
+ * form, zero/garbage, or an ask too long to honor inside a build path).
132
+ */
133
+ function retryAfterMs(response) {
134
+ // Optional-chained: minimal test doubles (and some fetch shims) carry no
135
+ // `headers` — a missing header must read as "no hint", never throw.
136
+ const ms = Number(response.headers?.get?.("retry-after")) * 1_000;
137
+ return ms > 0 && ms <= MAX_BACKOFF_MS ? ms : undefined;
138
+ }
84
139
  /** `host + pathname` only — never the query string (feed ids are noise, not diagnostic). */
85
140
  function describeTarget(url) {
86
141
  const parsed = new URL(url);
@@ -187,11 +242,13 @@ async function fetchWithPolicy(url, init = {}, policy = {}, externalSignal) {
187
242
  let status;
188
243
  let bodySnippet;
189
244
  let cause;
245
+ let serverRetryDelay;
190
246
  try {
191
247
  const response = await doFetch(url, { ...init, headers, signal });
192
248
  if (response.ok || !isRetryableStatus(response.status))
193
249
  return response;
194
250
  status = response.status;
251
+ serverRetryDelay = retryAfterMs(response);
195
252
  if (attempt === retries) {
196
253
  bodySnippet = await readBodySnippet(response);
197
254
  }
@@ -218,7 +275,7 @@ async function fetchWithPolicy(url, init = {}, policy = {}, externalSignal) {
218
275
  const detail = statusDetail ?? causeMessage(cause);
219
276
  throw new FetchPolicyError(`fetchWithPolicy: ${describeTarget(url)} failed after ${attempt + 1} attempt(s) — ${detail}`, { status, bodySnippet, cause, attempts: attempt + 1 });
220
277
  }
221
- await sleep(backoffMs(retryDelayMs, attempt), combinedExternalSignal);
278
+ await sleep(serverRetryDelay ?? backoffMs(retryDelayMs, attempt), combinedExternalSignal);
222
279
  }
223
280
  // Unreachable: the loop above always returns or throws on its final
224
281
  // (attempt === retries) iteration — this satisfies the compiler only.
@@ -12,44 +12,96 @@
12
12
  */
13
13
  import { BaseLineClient } from "../base-client.ts";
14
14
  import type { OracleSource } from "../oracle/price-update-rule.ts";
15
- import { type LoadConfigOptions, type PythGeneration, type PythInfraConfig, type WaterXConfig, type WormholeInfraConfig } from "./config.ts";
15
+ import type { FetchPolicy } from "../oracle/update-fetch.ts";
16
+ import { type LoadConfigOptions, type PythFetchPolicy, type PythInfraConfig, type WaterXConfig, type WaterxInfraConfig, type WormholeInfraConfig } from "./config.ts";
16
17
  import type { Network } from "./constants.ts";
17
18
  export interface CreateClientOptions extends LoadConfigOptions {
18
19
  grpcUrl?: string;
19
20
  /**
20
- * Selects which `PriceUpdateRule` `refreshOraclePrices` uses for the
21
- * on-chain price-update leg (see `OracleHost.oracleSource`). Default:
22
- * `'pyth_rule'`. The SDK never reads `process.env` — pass this from your
23
- * own env var (e.g. `ORACLE_SOURCE`).
21
+ * Which oracle price-update source drives `refreshOraclePrices`. Each source
22
+ * is self-contained (own infra + config) with NO cross-source fallback:
23
+ *
24
+ * - `'pyth_rule'` (default) — Pyth Core `pyth_rule` updates (Hermes VAA +
25
+ * per-feed update fees), Core state + keyless Core Hermes.
26
+ * - `'pyth_lazer_rule'` — Pyth Lazer signed updates (ONE `leEcdsa` verify
27
+ * per PTB, no per-feed fees); needs `packages.pyth_lazer_rule` with feeds
28
+ * and a `pythApiKey` (Lazer is auth-first).
29
+ * - `'waterx_rule'` — the first-party WaterX quote-center (Nautilus-TEE,
30
+ * ed25519-signed batches): ONE envelope covering the build's tickers,
31
+ * verified AND fed by a single `collect_batch_latest` per collector. No
32
+ * credential and no per-update fee; needs `packages.waterx_rule` with
33
+ * feeds. Endpoint/transport via {@link CreateClientOptions.waterxEndpoint}
34
+ * / {@link CreateClientOptions.waterxFetch} — the browser-CORS proxy hook,
35
+ * since this is the one source fetched from the page.
36
+ *
37
+ * The name is source-neutral on purpose — a source need not be Pyth (as
38
+ * `'waterx_rule'` shows). Selecting a source whose feed for a requested
39
+ * ticker is absent is NOT an error at client creation: it fails at tx-build
40
+ * time for exactly those tickers (see `refreshOraclePrices`). The Pyth Core
41
+ * infra is fixed per network by `PYTH_DEFAULTS` and is not
42
+ * deployment-overridable.
24
43
  */
25
44
  oracleSource?: OracleSource;
26
45
  /**
27
- * Selects which Pyth Core contract generation feeds `client.pyth` when the
28
- * config JSON has no explicit `pyth` override: `'core'` (default,
29
- * `PYTH_DEFAULTS`) or `'pro'` (`PYTH_PRO_DEFAULTS` the post-2026-08-18
30
- * Pro-compatible contracts + Hermes-compatible endpoint; pair with
31
- * `pyth.api_key`). Orthogonal to `oracleSource`. An explicit `config.pyth`
32
- * always wins wholesale (see `PythGeneration`).
46
+ * Pyth Lazer access token (`Authorization: Bearer …`). Required under
47
+ * `oracleSource: 'pyth_lazer_rule'` (Lazer is auth-first) and unused by
48
+ * `'pyth_rule'` (keyless Core Hermes). This is a SECRET and never belongs in
49
+ * the canonical `waterx-config` JSON pass it at client init from your own
50
+ * env var (e.g. `PYTH_API_KEY`); the SDK never reads `process.env`.
33
51
  */
34
- pythGeneration?: PythGeneration;
52
+ pythApiKey?: string;
53
+ /**
54
+ * Retry/timeout policy for the off-chain Hermes / Lazer update fetches (see
55
+ * `fetchWithPolicy`). Optional — defaults to 15s timeout, 2 retries.
56
+ */
57
+ pythFetch?: PythFetchPolicy;
58
+ /**
59
+ * Quote-center base URL for `oracleSource: 'waterx_rule'` — overrides the
60
+ * per-network {@link WATERX_DEFAULTS} host.
61
+ *
62
+ * This is the one source a BROWSER fetches itself (the signed envelope is
63
+ * pulled from the page), so it is bound by the quote-center deployment's CORS
64
+ * allowlist. A front end whose origin is not allowed — or one that must route
65
+ * egress through its own backend — points this at a same-origin proxy that
66
+ * forwards `GET /v1/quotes/update`. Unused by the Pyth sources.
67
+ *
68
+ * An absolute URL. Any base PATH is preserved (`joinEndpointPath`), so
69
+ * `https://app.example/api/quote-center` fetches
70
+ * `https://app.example/api/quote-center/v1/quotes/update` — a proxy route
71
+ * survives instead of being rewritten to the origin root.
72
+ */
73
+ waterxEndpoint?: string;
74
+ /**
75
+ * Retry/timeout policy — and `fetchImpl` — for the quote-center fetch (see
76
+ * `fetchWithPolicy`). Optional: falls back to `pythFetch`, then to the
77
+ * built-in defaults. Supply `fetchImpl` to route the request through your own
78
+ * transport (a proxying `fetch` wrapper, a non-global `fetch`, a test double).
79
+ */
80
+ waterxFetch?: FetchPolicy;
35
81
  }
36
82
  export declare class PerpClient extends BaseLineClient<WaterXConfig> {
37
- /** Pyth infra (network defaults unless overridden in JSON). */
83
+ /** Pyth Core infra (fixed per network) plus the caller-supplied credential/policy. */
38
84
  pyth: PythInfraConfig;
85
+ /**
86
+ * WaterX quote-center infra for `oracleSource: 'waterx_rule'` — the network
87
+ * default, overridden by the `waterxEndpoint` / `waterxFetch` create options
88
+ * (a same-origin proxy or a custom `fetchImpl` for browser consumers).
89
+ */
90
+ waterx: WaterxInfraConfig;
39
91
  /** Wormhole infra for the credit bridge (network defaults unless overridden). */
40
92
  wormhole: WormholeInfraConfig;
41
- /** Selected oracle rule source (client option, resolved at creation; default `'pyth_rule'`). See `OracleHost.oracleSource`. */
93
+ /** Selected oracle price-update source (`oracleSource` create option; default `'pyth_rule'`). */
42
94
  readonly oracleSource: OracleSource;
43
95
  /** Canonical-schema lookups (delegated to below); no transport. */
44
96
  private readonly view;
45
- constructor(network: Network, config: WaterXConfig, opts?: {
46
- grpcUrl?: string;
47
- oracleSource?: OracleSource;
48
- pythGeneration?: PythGeneration;
49
- });
97
+ constructor(network: Network, config: WaterXConfig, opts?: CreateClientOptions);
50
98
  /**
51
99
  * Async factory: fetches the deployment config for `network` and returns
52
100
  * a ready-to-use client. Pass `opts.cache=true` to memoize the JSON.
101
+ *
102
+ * No oracle-config guard here: selecting a source whose feeds are absent is
103
+ * not an error at init — it surfaces at tx-build time for the specific
104
+ * tickers that source can't serve (see `refreshOraclePrices`).
53
105
  */
54
106
  static create(network: Network, opts?: CreateClientOptions): Promise<PerpClient>;
55
107
  static mainnet(opts?: CreateClientOptions): Promise<PerpClient>;
@@ -17,34 +17,54 @@ const base_client_ts_1 = require("../base-client.js");
17
17
  const config_view_ts_1 = require("./config-view.js");
18
18
  const config_ts_1 = require("./config.js");
19
19
  class PerpClient extends base_client_ts_1.BaseLineClient {
20
- /** Pyth infra (network defaults unless overridden in JSON). */
20
+ /** Pyth Core infra (fixed per network) plus the caller-supplied credential/policy. */
21
21
  pyth;
22
+ /**
23
+ * WaterX quote-center infra for `oracleSource: 'waterx_rule'` — the network
24
+ * default, overridden by the `waterxEndpoint` / `waterxFetch` create options
25
+ * (a same-origin proxy or a custom `fetchImpl` for browser consumers).
26
+ */
27
+ waterx;
22
28
  /** Wormhole infra for the credit bridge (network defaults unless overridden). */
23
29
  wormhole;
24
- /** Selected oracle rule source (client option, resolved at creation; default `'pyth_rule'`). See `OracleHost.oracleSource`. */
30
+ /** Selected oracle price-update source (`oracleSource` create option; default `'pyth_rule'`). */
25
31
  oracleSource;
26
32
  /** Canonical-schema lookups (delegated to below); no transport. */
27
33
  view;
28
34
  constructor(network, config, opts = {}) {
29
35
  super(network, config, opts);
30
- // Precedence: explicit config.pyth override > generation constants.
31
- this.pyth =
32
- config.pyth ?? (opts.pythGeneration === "pro" ? config_ts_1.PYTH_PRO_DEFAULTS : config_ts_1.PYTH_DEFAULTS)[network];
36
+ // Pyth Core infra is fixed per network — NOT deployment-overridable and
37
+ // NOT source-dependent (the pyth_lazer_rule source reads only api_key/fetch
38
+ // from here). The api_key + fetch policy are caller-supplied at init: a
39
+ // secret has no place in the canonical waterx-config JSON.
40
+ this.pyth = {
41
+ ...config_ts_1.PYTH_DEFAULTS[network],
42
+ ...(opts.pythApiKey !== undefined ? { api_key: opts.pythApiKey } : {}),
43
+ ...(opts.pythFetch !== undefined ? { fetch: opts.pythFetch } : {}),
44
+ };
33
45
  this.wormhole = config.wormhole ?? config_ts_1.WORMHOLE_DEFAULTS[network];
46
+ // Quote-center infra: network default, each field independently overridable
47
+ // — a browser blocked by the quote-center's CORS allowlist swaps `endpoint`
48
+ // for a same-origin proxy without touching anything else.
49
+ this.waterx = {
50
+ ...config_ts_1.WATERX_DEFAULTS[network],
51
+ ...(opts.waterxEndpoint !== undefined ? { endpoint: opts.waterxEndpoint } : {}),
52
+ ...(opts.waterxFetch !== undefined ? { fetch: opts.waterxFetch } : {}),
53
+ };
34
54
  this.oracleSource = opts.oracleSource ?? "pyth_rule";
35
55
  this.view = new config_view_ts_1.PerpConfigView(() => this.config, () => this.wormhole);
36
56
  }
37
57
  /**
38
58
  * Async factory: fetches the deployment config for `network` and returns
39
59
  * a ready-to-use client. Pass `opts.cache=true` to memoize the JSON.
60
+ *
61
+ * No oracle-config guard here: selecting a source whose feeds are absent is
62
+ * not an error at init — it surfaces at tx-build time for the specific
63
+ * tickers that source can't serve (see `refreshOraclePrices`).
40
64
  */
41
65
  static async create(network, opts = {}) {
42
66
  const config = await (0, config_ts_1.loadConfig)(network, opts);
43
- return new PerpClient(network, config, {
44
- grpcUrl: opts.grpcUrl,
45
- oracleSource: opts.oracleSource,
46
- pythGeneration: opts.pythGeneration,
47
- });
67
+ return new PerpClient(network, config, opts);
48
68
  }
49
69
  static mainnet(opts = {}) {
50
70
  return PerpClient.create("MAINNET", opts);
@@ -9,11 +9,11 @@
9
9
  * below, keyed by network.
10
10
  */
11
11
  import type { AccountPackages, BasePackageEntry, WormholeInfraConfig } from "../account/config.ts";
12
- import type { OraclePackages, PythInfraConfig } from "../oracle/config.ts";
12
+ import type { OraclePackages } from "../oracle/config.ts";
13
13
  import type { Network } from "./constants.ts";
14
- export type { AccountConfig, AccountPackages, BasePackageEntry, NativeCustodyAsset, NativeCustodyPackage, TrustedEmitterRow, WaterxCreditPackage, WaterxReferralPackage, WithdrawalQueuePackage, WormholeBridgePackage, WormholeInfraConfig, WxaAccountPackage, } from "../account/config.ts";
15
- export type { ConstantFeedEntry, OracleConfig, OraclePackages, PythGeneration, PythInfraConfig, PythLazerRulePackage, PythRulePackage, PythSponsorRulePackage, SupraFeedEntry, SupraRulePackage, WaterxConstantRulePackage, WaterxOraclePackage, } from "../oracle/config.ts";
16
- export { PYTH_DEFAULTS, PYTH_PRO_DEFAULTS } from "../oracle/config.ts";
14
+ export type { AccountConfig, AccountPackages, BasePackageEntry, NativeCustodyAsset, NativeCustodyPackage, WaterxCreditPackage, WaterxReferralPackage, WithdrawalQueuePackage, WormholeBridgePackage, WormholeInfraConfig, WxaAccountPackage, } from "../account/config.ts";
15
+ export type { ConstantFeedEntry, OracleConfig, OraclePackages, PythFetchPolicy, PythInfraConfig, PythLazerRulePackage, PythRulePackage, PythSponsorRulePackage, SupraFeedEntry, SupraRulePackage, WaterxConstantRulePackage, WaterxInfraConfig, WaterxOraclePackage, WaterxRulePackage, } from "../oracle/config.ts";
16
+ export { PYTH_DEFAULTS, WATERX_DEFAULTS } from "../oracle/config.ts";
17
17
  export interface WaterxPerpMarketEntry {
18
18
  market: string;
19
19
  config: string;
@@ -71,7 +71,6 @@ export interface WaterXPackages extends AccountPackages, OraclePackages {
71
71
  mock_usdc?: MockCoinPackage;
72
72
  mock_usdsui?: MockCoinPackage;
73
73
  mock_sui?: MockCoinPackage;
74
- waterx_rule?: BasePackageEntry;
75
74
  waterx_rule_nautilus_enclave?: BasePackageEntry;
76
75
  }
77
76
  export declare const WORMHOLE_DEFAULTS: Record<Network, WormholeInfraConfig>;
@@ -82,8 +81,6 @@ export interface WaterXConfig {
82
81
  /** Sui gRPC base URL (default: public Mysten fullnode for the network). */
83
82
  grpcUrl?: string;
84
83
  packages: WaterXPackages;
85
- /** Pyth infra override (defaults from `PYTH_DEFAULTS[network]`). */
86
- pyth?: PythInfraConfig;
87
84
  /** Wormhole infra override (defaults from `WORMHOLE_DEFAULTS[network]`). */
88
85
  wormhole?: WormholeInfraConfig;
89
86
  /** Sui `CoinRegistry` shared object (credit deployments). */
@@ -10,13 +10,13 @@
10
10
  * below, keyed by network.
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.WORMHOLE_DEFAULTS = exports.PYTH_PRO_DEFAULTS = exports.PYTH_DEFAULTS = void 0;
13
+ exports.WORMHOLE_DEFAULTS = exports.WATERX_DEFAULTS = exports.PYTH_DEFAULTS = void 0;
14
14
  exports.clearConfigCache = clearConfigCache;
15
15
  exports.loadConfig = loadConfig;
16
16
  const update_fetch_ts_1 = require("../oracle/update-fetch.js");
17
17
  var config_ts_1 = require("../oracle/config.js");
18
18
  Object.defineProperty(exports, "PYTH_DEFAULTS", { enumerable: true, get: function () { return config_ts_1.PYTH_DEFAULTS; } });
19
- Object.defineProperty(exports, "PYTH_PRO_DEFAULTS", { enumerable: true, get: function () { return config_ts_1.PYTH_PRO_DEFAULTS; } });
19
+ Object.defineProperty(exports, "WATERX_DEFAULTS", { enumerable: true, get: function () { return config_ts_1.WATERX_DEFAULTS; } });
20
20
  // ============================================================================
21
21
  // Wormhole / Hermes — external chain infra, defaults by network
22
22
  // ============================================================================
@@ -120,16 +120,13 @@ async function loadConfig(network, opts = {}) {
120
120
  const stale = configCache.get(cacheKey);
121
121
  if (stale)
122
122
  return stale;
123
- // Reformat a status-carrying FetchPolicyError (retries exhausted on a
124
- // retryable status) into this function's own message shape, mirroring
125
- // the non-retried `!response.ok` throw above. A network-level
126
- // exhaustion (no status), a `!response.ok` throw, or a JSON parse /
127
- // `validateConfig` failure has no domain-specific reframing to add —
128
- // propagate that error's own message as-is.
129
- if (err instanceof update_fetch_ts_1.FetchPolicyError && err.status !== undefined) {
130
- throw new Error(`loadConfig: HTTP ${err.status} fetching ${url} (retries exhausted after ${err.attempts} attempts)`, { cause: err });
131
- }
132
- throw err;
123
+ // Reframe a status-carrying FetchPolicyError into this function's own
124
+ // message shape, mirroring the non-retried `!response.ok` throw above and
125
+ // carrying the URL (the key datum for a config-fetch failure). A
126
+ // network-level exhaustion (no status), a `!response.ok` throw, or a JSON
127
+ // parse / `validateConfig` failure has no reframing to add — the helper
128
+ // propagates those verbatim.
129
+ (0, update_fetch_ts_1.rethrowExhaustedFetch)(err, (e) => `loadConfig: HTTP ${e.status} fetching ${url}`);
133
130
  }
134
131
  configCache.set(cacheKey, raw);
135
132
  return raw;
@@ -1,10 +1,4 @@
1
1
  export * from "../constants.ts";
2
- /** Default crypto market trading fee rate (3 bps). Per-market value lives in MarketConfig. */
3
- export declare const CRYPTO_FEE_RATE = 0.0003;
4
- /** Default stock / commodity market trading fee rate (5 bps). Per-market value lives in MarketConfig. */
5
- export declare const STOCK_FEE_RATE = 0.0005;
6
- /** Default maintenance margin rate (150 bps = 1.5%). Per-market value lives in MarketConfig. */
7
- export declare const MAINTENANCE_MARGIN_RATE = 0.015;
8
2
  export declare const PERM_OPEN_POSITION = 1;
9
3
  export declare const PERM_CLOSE_POSITION = 2;
10
4
  export declare const PERM_INCREASE_POSITION = 4;
@@ -1,8 +1,17 @@
1
1
  "use strict";
2
2
  // Perp-line domain constants (trading permissions / order tags / action codes /
3
- // fee rates / well-known addresses). Re-exports the shared primitives from
3
+ // well-known addresses). Re-exports the shared primitives from
4
4
  // `../constants.ts` so perp code and the `./perp` barrel get the full set from
5
5
  // a single import.
6
+ //
7
+ // NOTE: there are deliberately NO fee-rate / maintenance-margin constants here.
8
+ // `CRYPTO_FEE_RATE` / `STOCK_FEE_RATE` / `MAINTENANCE_MARGIN_RATE` were removed
9
+ // — they were defaults masquerading as truth; per-market `MarketConfig` on
10
+ // chain is the only source for fee and margin parameters (real MMRs span
11
+ // 0.5%–5%; the flat 1.5% understated AAPLX-class risk >3x — mainnet incident
12
+ // 2026-07-28: shorts displayed liq ~$364 but were liquidated at ~$343). When
13
+ // the market rate is unavailable, treat the value as NOT estimable and fail
14
+ // safe — never substitute a flat default.
6
15
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
16
  if (k2 === undefined) k2 = k;
8
17
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -18,15 +27,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
27
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
28
  };
20
29
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.ACCUMULATOR_ROOT = exports.ACTION_ADD_PRE_ORDER = exports.ACTION_CANCEL_PRE_ORDER = exports.ACTION_UPDATE_ORDER = exports.ACTION_DECREASE_POSITION = exports.ACTION_INCREASE_POSITION = exports.ACTION_LIQUIDATE = exports.ACTION_WITHDRAW_COLLATERAL = exports.ACTION_DEPOSIT_COLLATERAL = exports.ACTION_CANCEL_ORDER = exports.ACTION_PLACE_ORDER = exports.ACTION_CLOSE_POSITION = exports.ACTION_OPEN_POSITION = exports.ORDER_TAG_WILDCARD = exports.ORDER_STOP_SELL = exports.ORDER_STOP_BUY = exports.ORDER_LIMIT_SELL = exports.ORDER_LIMIT_BUY = exports.STAKING_PERM_ALL = exports.STAKING_PERM_CLAIM_REWARD = exports.STAKING_PERM_REDEEM_STAKE = exports.STAKING_PERM_DEPOSIT_STAKE = exports.PERM_ALL = exports.PERM_ALL_TRADING = exports.PERM_REDEEM_WLP = exports.PERM_MINT_WLP = exports.PERM_WITHDRAW_COLLATERAL = exports.PERM_DEPOSIT_COLLATERAL = exports.PERM_CANCEL_ORDER = exports.PERM_PLACE_ORDER = exports.PERM_DECREASE_POSITION = exports.PERM_INCREASE_POSITION = exports.PERM_CLOSE_POSITION = exports.PERM_OPEN_POSITION = exports.MAINTENANCE_MARGIN_RATE = exports.STOCK_FEE_RATE = exports.CRYPTO_FEE_RATE = void 0;
30
+ exports.ACCUMULATOR_ROOT = exports.ACTION_ADD_PRE_ORDER = exports.ACTION_CANCEL_PRE_ORDER = exports.ACTION_UPDATE_ORDER = exports.ACTION_DECREASE_POSITION = exports.ACTION_INCREASE_POSITION = exports.ACTION_LIQUIDATE = exports.ACTION_WITHDRAW_COLLATERAL = exports.ACTION_DEPOSIT_COLLATERAL = exports.ACTION_CANCEL_ORDER = exports.ACTION_PLACE_ORDER = exports.ACTION_CLOSE_POSITION = exports.ACTION_OPEN_POSITION = exports.ORDER_TAG_WILDCARD = exports.ORDER_STOP_SELL = exports.ORDER_STOP_BUY = exports.ORDER_LIMIT_SELL = exports.ORDER_LIMIT_BUY = exports.STAKING_PERM_ALL = exports.STAKING_PERM_CLAIM_REWARD = exports.STAKING_PERM_REDEEM_STAKE = exports.STAKING_PERM_DEPOSIT_STAKE = exports.PERM_ALL = exports.PERM_ALL_TRADING = exports.PERM_REDEEM_WLP = exports.PERM_MINT_WLP = exports.PERM_WITHDRAW_COLLATERAL = exports.PERM_DEPOSIT_COLLATERAL = exports.PERM_CANCEL_ORDER = exports.PERM_PLACE_ORDER = exports.PERM_DECREASE_POSITION = exports.PERM_INCREASE_POSITION = exports.PERM_CLOSE_POSITION = exports.PERM_OPEN_POSITION = void 0;
22
31
  __exportStar(require("../constants.js"), exports);
23
- // ======== Fee rates & risk parameters ========
24
- /** Default crypto market trading fee rate (3 bps). Per-market value lives in MarketConfig. */
25
- exports.CRYPTO_FEE_RATE = 0.0003;
26
- /** Default stock / commodity market trading fee rate (5 bps). Per-market value lives in MarketConfig. */
27
- exports.STOCK_FEE_RATE = 0.0005;
28
- /** Default maintenance margin rate (150 bps = 1.5%). Per-market value lives in MarketConfig. */
29
- exports.MAINTENANCE_MARGIN_RATE = 0.015;
30
32
  // ======== Permission Bitmasks (matches account_data.move) ========
31
33
  exports.PERM_OPEN_POSITION = 1;
32
34
  exports.PERM_CLOSE_POSITION = 2;
@@ -56,6 +56,9 @@ async function getSpendableCreditBalance(client, accountId) {
56
56
  (0, balance_ts_1.probeParkedBackingAssets)(client, accountId),
57
57
  (0, balance_ts_1.probeAddressCreditBalance)(client, accountId),
58
58
  ]);
59
+ // COLLATERAL_DECIMALS here is the CREDIT/wxUSD *target* scale (collateral-typed;
60
+ // 6 on all current deployments — config carries no credit decimal). The
61
+ // per-asset backing decimals come from config via each row's `decimals`.
59
62
  const pendingBackingRaw = (0, balance_ts_1.sumParkedBackingAsCreditRaw)(parkedBacking, constants_ts_1.COLLATERAL_DECIMALS);
60
63
  const pendingCreditAtAddressRaw = addressCredit.fundsRaw + addressCredit.coinsRaw;
61
64
  const totalRaw = internalRaw + pendingBackingRaw + pendingCreditAtAddressRaw;
@@ -10,6 +10,7 @@ const bcs_1 = require("@mysten/sui/bcs");
10
10
  const transactions_1 = require("@mysten/sui/transactions");
11
11
  const withdrawal_queue_ts_1 = require("../../generated/withdrawal_queue/withdrawal_queue.js");
12
12
  const wormhole_bridge_ts_1 = require("../../generated/wormhole_bridge/wormhole_bridge.js");
13
+ const validate_ts_1 = require("../../utils/validate.js");
13
14
  const simulate_ts_1 = require("./simulate.js");
14
15
  /**
15
16
  * Batched read of the bridge's rate-limit / cap state in a single simulate.
@@ -100,7 +101,7 @@ function requireWithdrawalQueue(client) {
100
101
  */
101
102
  async function getBridgeFee(client, args) {
102
103
  const { pkg, queue } = requireWithdrawalQueue(client);
103
- const amount = BigInt(args.amount);
104
+ const amount = (0, validate_ts_1.toU64)(args.amount, "amount");
104
105
  const common = {
105
106
  package: pkg,
106
107
  typeArguments: [args.creditType ?? client.creditType()],
@@ -12,6 +12,7 @@ exports.getGlobalConfigData = getGlobalConfigData;
12
12
  const bcs_1 = require("@mysten/sui/bcs");
13
13
  const transactions_1 = require("@mysten/sui/transactions");
14
14
  const view_ts_1 = require("../../generated/waterx_perp_view/view.js");
15
+ const validate_ts_1 = require("../../utils/validate.js");
15
16
  const simulate_ts_1 = require("./simulate.js");
16
17
  /**
17
18
  * Look up the registered AccountData for a given wxa account ID.
@@ -59,7 +60,7 @@ async function getTokenPoolData(client, args) {
59
60
  package: client.config.packages.waterx_perp_view.published_at,
60
61
  arguments: {
61
62
  pool: tx.object(client.config.packages.wlp.wlp_pool),
62
- tokenIndex: args.tokenIndex,
63
+ tokenIndex: (0, validate_ts_1.toU64)(args.tokenIndex, "tokenIndex"),
63
64
  },
64
65
  typeArguments: [(0, simulate_ts_1.withLp)(client, args.lpType)],
65
66
  })(tx);
@@ -3,7 +3,9 @@
3
3
  * list (`waterx_perp_view`).
4
4
  */
5
5
  import { OrderData, PositionData, RedeemRequestData } from "../../generated/waterx_perp_view/view.ts";
6
+ import { parseWholeDollarU64, type WholeDollarUsdPrice } from "../../utils/validate.ts";
6
7
  import type { PerpClient } from "../client.ts";
8
+ export { parseWholeDollarU64, type WholeDollarUsdPrice };
7
9
  export type PositionDataView = ReturnType<typeof PositionData.parse>;
8
10
  export declare function positionExists(client: PerpClient, args: {
9
11
  ticker: string;
@@ -13,9 +15,8 @@ export declare function positionExists(client: PerpClient, args: {
13
15
  export declare function getPosition(client: PerpClient, args: {
14
16
  ticker: string;
15
17
  positionId: bigint | number;
16
- /** Human-readable USD prices for Pnl / liq price calc; pass 0n if unsure. */
17
- basePriceUsd: bigint | number;
18
- collateralPriceUsd: bigint | number;
18
+ basePriceUsd: WholeDollarUsdPrice;
19
+ collateralPriceUsd: WholeDollarUsdPrice;
19
20
  lpType?: string;
20
21
  }): Promise<PositionDataView>;
21
22
  export type OrderDataView = ReturnType<typeof OrderData.parse>;
@@ -23,8 +24,9 @@ export declare function getOrder(client: PerpClient, args: {
23
24
  ticker: string;
24
25
  orderId: bigint | number;
25
26
  orderTypeTag: number;
27
+ /** Raw 1e9-scaled u128 order-book key — same scale as tx-build `rawPrice()`. */
26
28
  triggerPrice: bigint | number;
27
- basePriceUsd: bigint | number;
29
+ basePriceUsd: WholeDollarUsdPrice;
28
30
  lpType?: string;
29
31
  }): Promise<OrderDataView>;
30
32
  export interface PageOpts {
@@ -33,7 +35,8 @@ export interface PageOpts {
33
35
  }
34
36
  export declare function getMarketOrders(client: PerpClient, args: {
35
37
  ticker: string;
36
- basePriceUsd?: bigint | number;
38
+ /** Defaults to `0n`. */
39
+ basePriceUsd?: WholeDollarUsdPrice;
37
40
  lpType?: string;
38
41
  } & PageOpts): Promise<{
39
42
  orders: OrderDataView[];
@@ -41,8 +44,9 @@ export declare function getMarketOrders(client: PerpClient, args: {
41
44
  }>;
42
45
  export declare function getMarketPositions(client: PerpClient, args: {
43
46
  ticker: string;
44
- basePriceUsd: bigint | number;
45
- collateralPriceUsd?: bigint | number;
47
+ basePriceUsd: WholeDollarUsdPrice;
48
+ /** Defaults to `0n`. */
49
+ collateralPriceUsd?: WholeDollarUsdPrice;
46
50
  lpType?: string;
47
51
  } & PageOpts): Promise<{
48
52
  positions: PositionDataView[];
@@ -51,14 +55,16 @@ export declare function getMarketPositions(client: PerpClient, args: {
51
55
  export declare function getAccountPositions(client: PerpClient, args: {
52
56
  ticker: string;
53
57
  accountObjectAddress: string;
54
- basePriceUsd: bigint | number;
55
- collateralPriceUsd?: bigint | number;
58
+ basePriceUsd: WholeDollarUsdPrice;
59
+ /** Defaults to `0n`. */
60
+ collateralPriceUsd?: WholeDollarUsdPrice;
56
61
  lpType?: string;
57
62
  }): Promise<PositionDataView[]>;
58
63
  export declare function getAccountOrders(client: PerpClient, args: {
59
64
  ticker: string;
60
65
  accountObjectAddress: string;
61
- basePriceUsd?: bigint | number;
66
+ /** Defaults to `0n`. */
67
+ basePriceUsd?: WholeDollarUsdPrice;
62
68
  lpType?: string;
63
69
  }): Promise<OrderDataView[]>;
64
70
  export type RedeemRequestDataView = ReturnType<typeof RedeemRequestData.parse>;