@waterx/sdk 4.1.0 → 4.3.1

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 (80) hide show
  1. package/README.md +29 -19
  2. package/dist/cjs/src/account/config.d.ts +3 -2
  3. package/dist/cjs/src/generated/waterx_rule/waterx_rule.d.ts +7 -134
  4. package/dist/cjs/src/generated/waterx_rule/waterx_rule.js +8 -154
  5. package/dist/cjs/src/oracle/aggregate.d.ts +21 -21
  6. package/dist/cjs/src/oracle/aggregate.js +121 -71
  7. package/dist/cjs/src/oracle/config.d.ts +38 -72
  8. package/dist/cjs/src/oracle/config.js +0 -56
  9. package/dist/cjs/src/oracle/host.d.ts +24 -20
  10. package/dist/cjs/src/oracle/index.d.ts +7 -3
  11. package/dist/cjs/src/oracle/index.js +41 -8
  12. package/dist/cjs/src/oracle/price-update-rule.d.ts +32 -11
  13. package/dist/cjs/src/oracle/price-update-rule.js +23 -3
  14. package/dist/cjs/src/oracle/pyth.d.ts +44 -0
  15. package/dist/cjs/src/oracle/pyth.js +48 -6
  16. package/dist/cjs/src/oracle/read-plane.d.ts +70 -0
  17. package/dist/cjs/src/oracle/read-plane.js +85 -0
  18. package/dist/cjs/src/oracle/rule-registry.d.ts +6 -4
  19. package/dist/cjs/src/oracle/rule-registry.js +6 -4
  20. package/dist/cjs/src/oracle/rules/pyth-core-rule.js +6 -5
  21. package/dist/cjs/src/oracle/rules/pyth-lazer-rule.d.ts +21 -0
  22. package/dist/cjs/src/oracle/rules/pyth-lazer-rule.js +34 -6
  23. package/dist/cjs/src/oracle/rules/pyth-rule.js +5 -4
  24. package/dist/cjs/src/oracle/rules/waterx-rule.d.ts +42 -5
  25. package/dist/cjs/src/oracle/rules/waterx-rule.js +61 -18
  26. package/dist/cjs/src/oracle/source-list.d.ts +36 -0
  27. package/dist/cjs/src/oracle/source-list.js +57 -0
  28. package/dist/cjs/src/perp/client.d.ts +41 -28
  29. package/dist/cjs/src/perp/client.js +32 -20
  30. package/dist/cjs/src/perp/config-view.js +7 -6
  31. package/dist/cjs/src/perp/config.d.ts +6 -6
  32. package/dist/cjs/src/perp/config.js +12 -12
  33. package/dist/cjs/src/perp/index.d.ts +3 -3
  34. package/dist/cjs/src/perp/index.js +11 -4
  35. package/dist/cjs/src/perp/user/staking.js +2 -1
  36. package/dist/cjs/src/unified-client.d.ts +14 -9
  37. package/dist/cjs/src/unified-client.js +2 -2
  38. package/dist/cjs/src/utils/config.js +2 -1
  39. package/dist/cjs/src/utils/record.d.ts +12 -0
  40. package/dist/cjs/src/utils/record.js +22 -0
  41. package/dist/src/account/config.d.ts +3 -2
  42. package/dist/src/generated/waterx_rule/waterx_rule.d.ts +7 -134
  43. package/dist/src/generated/waterx_rule/waterx_rule.js +7 -143
  44. package/dist/src/oracle/aggregate.d.ts +21 -21
  45. package/dist/src/oracle/aggregate.js +121 -71
  46. package/dist/src/oracle/config.d.ts +38 -72
  47. package/dist/src/oracle/config.js +1 -55
  48. package/dist/src/oracle/host.d.ts +24 -20
  49. package/dist/src/oracle/index.d.ts +7 -3
  50. package/dist/src/oracle/index.js +32 -10
  51. package/dist/src/oracle/price-update-rule.d.ts +32 -11
  52. package/dist/src/oracle/price-update-rule.js +22 -3
  53. package/dist/src/oracle/pyth.d.ts +44 -0
  54. package/dist/src/oracle/pyth.js +45 -5
  55. package/dist/src/oracle/read-plane.d.ts +70 -0
  56. package/dist/src/oracle/read-plane.js +81 -0
  57. package/dist/src/oracle/rule-registry.d.ts +6 -4
  58. package/dist/src/oracle/rule-registry.js +6 -4
  59. package/dist/src/oracle/rules/pyth-core-rule.js +7 -6
  60. package/dist/src/oracle/rules/pyth-lazer-rule.d.ts +21 -0
  61. package/dist/src/oracle/rules/pyth-lazer-rule.js +33 -5
  62. package/dist/src/oracle/rules/pyth-rule.js +5 -4
  63. package/dist/src/oracle/rules/waterx-rule.d.ts +42 -5
  64. package/dist/src/oracle/rules/waterx-rule.js +57 -15
  65. package/dist/src/oracle/source-list.d.ts +36 -0
  66. package/dist/src/oracle/source-list.js +53 -0
  67. package/dist/src/perp/client.d.ts +41 -28
  68. package/dist/src/perp/client.js +33 -21
  69. package/dist/src/perp/config-view.js +7 -6
  70. package/dist/src/perp/config.d.ts +6 -6
  71. package/dist/src/perp/config.js +11 -9
  72. package/dist/src/perp/index.d.ts +3 -3
  73. package/dist/src/perp/index.js +2 -2
  74. package/dist/src/perp/user/staking.js +2 -1
  75. package/dist/src/unified-client.d.ts +14 -9
  76. package/dist/src/unified-client.js +2 -2
  77. package/dist/src/utils/config.js +2 -1
  78. package/dist/src/utils/record.d.ts +12 -0
  79. package/dist/src/utils/record.js +19 -0
  80. package/package.json +1 -1
package/README.md CHANGED
@@ -11,19 +11,21 @@ The perp and prediction lines expose builder functions with **colliding names**
11
11
  ```ts
12
12
  import { WaterXClient } from "@waterx/sdk";
13
13
 
14
- // waterxConfigUrl is REQUIRED — the SDK has no built-in default and never reads env.
14
+ // waterxConfigUrl and oracleSource are REQUIRED — the SDK has no built-in
15
+ // defaults and never reads env. oracleSource: see "Oracle sources" below.
15
16
  const client = await WaterXClient.create({
16
17
  network: "TESTNET",
17
18
  waterxConfigUrl:
18
19
  "https://raw.githubusercontent.com/WaterXProtocol/waterx-config/main/testnet.json",
20
+ oracleSource: "pyth_rule",
19
21
  });
20
22
  client.account.createAccount(tx, { alias }); // shared waterx_account + funding (credit/custody)
21
23
  client.perp.buildPlaceOrderTx(params); // perpetuals
22
24
  client.predict.placeOrder(tx, params); // prediction markets
23
25
  // client.perp / client.predict ARE the line clients — sign/execute on them directly:
24
26
  // await client.perp.signAndExecuteTransaction({ transaction: tx, signer })
25
- // each line can target a different network + URL:
26
- // WaterXClient.create({ perp: { network: "MAINNET", waterxConfigUrl: mainnetUrl }, predict: { network: "TESTNET", waterxConfigUrl: testnetUrl } })
27
+ // each line can target a different network + URL (oracleSource stays top-level):
28
+ // WaterXClient.create({ oracleSource: "pyth_rule", perp: { network: "MAINNET", waterxConfigUrl: mainnetUrl }, predict: { network: "TESTNET", waterxConfigUrl: testnetUrl } })
27
29
  ```
28
30
 
29
31
  > `WaterXClient` is the umbrella entry point. `Client` is kept as a **deprecated alias** for one major cycle.
@@ -56,6 +58,7 @@ import { Transaction } from "@mysten/sui/transactions";
56
58
  const client = await WaterXClient.create({
57
59
  network: "TESTNET",
58
60
  waterxConfigUrl: "https://raw.githubusercontent.com/WaterXProtocol/waterx-config/main/testnet.json",
61
+ oracleSource: "pyth_rule", // REQUIRED — single source or a list (the fed set); see "Oracle sources"
59
62
  });
60
63
  const signer = /* your Ed25519Keypair or wallet Signer */;
61
64
 
@@ -94,46 +97,53 @@ import { PredictClient } from "@waterx/sdk/prediction";
94
97
 
95
98
  const waterxConfigUrl =
96
99
  "https://raw.githubusercontent.com/WaterXProtocol/waterx-config/main/testnet.json";
97
- const perp = await PerpClient.create("TESTNET", { waterxConfigUrl }); // or PerpClient.testnet({ waterxConfigUrl })
98
- const predict = await PredictClient.create("TESTNET", { waterxConfigUrl }); // or PredictClient.testnet({ waterxConfigUrl })
100
+ const perp = await PerpClient.create("TESTNET", { waterxConfigUrl, oracleSource: "pyth_rule" }); // or PerpClient.testnet({ ... })
101
+ const predict = await PredictClient.create("TESTNET", { waterxConfigUrl }); // predict line needs no oracle source
99
102
  ```
100
103
 
101
104
  Read-only queries use gRPC `simulateTransaction` (no signer) — the `getX` view helpers, e.g. `await perp.simulate(tx)` or `getMarketData(perp, …)`.
102
105
 
103
106
  ## Oracle sources
104
107
 
105
- ONE client create option, `oracleSource`, selects the price-update source. Each source is **self-contained** — it owns its own infra + config and does **not** back-stop any other source. The name is source-neutral on purpose: a future source need not be Pyth. The SDK **never reads `process.env`** — each consumer wires it from its own env var, so every environment runs the **same SDK version** and differs only by env:
108
+ ONE **required** client create option, `oracleSource`, names the price-update source(s) — a single value or a **list (the fed set)**. Each source is **self-contained** — it owns its own infra + config and does **not** back-stop any other source. There is **no default source**: a client that has not named its sources fails at creation. The name is source-neutral on purpose: a future source need not be Pyth. The SDK **never reads `process.env`** — each consumer wires it from its own env var, so every environment runs the **same SDK version** and differs only by env:
106
109
 
107
- | Option | Values | What it selects |
108
- | -------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
109
- | `oracleSource` | `'pyth_rule'` (default) \| `'pyth_lazer_rule'` \| `'waterx_rule'` | The price-update source. `'pyth_rule'` = Pyth Core updates (Hermes VAA + per-feed update fees, keyless `hermes.pyth.network`). `'pyth_lazer_rule'` = Pyth Lazer signed updates (ONE `leEcdsa` verify per PTB, no per-feed fees); needs `packages.pyth_lazer_rule` feeds + a `pythApiKey`. `'waterx_rule'` = the first-party WaterX quote-center (Nautilus-TEE, ed25519-signed CEX prices): one signed batch envelope per build, no API key and no per-update fee; needs `packages.waterx_rule` feeds. |
110
+ | Option | Values | What it selects |
111
+ | -------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
112
+ | `oracleSource` | `OracleSource \| OracleSource[]` of `'pyth_rule'` \| `'pyth_lazer_rule'` \| `'waterx_rule'` — REQUIRED, no default | The price-update source(s). `'pyth_rule'` = Pyth Core updates (Hermes VAA + per-feed update fees, keyless `hermes.pyth.network`). `'pyth_lazer_rule'` = Pyth Lazer signed updates (ONE `leEcdsa` verify per PTB, no per-feed fees); needs `packages.pyth_lazer_rule` feeds + a `pythApiKey`. `'waterx_rule'` = the first-party WaterX quote-center (Nautilus-TEE, ed25519-signed CEX prices): one signed batch envelope per build, no API key and no per-update fee; needs `packages.waterx_rule` feeds. |
110
113
 
111
- **No cross-source fallback, no init guard.** Selecting a source whose feed for a requested ticker is absent is **not** an error at client creation it fails at **tx-build** for exactly those tickers (constant-only tickers, which need no price update, are exempt). A present-but-wrong feed id is not validated by the SDK; it aborts on-chain at dry-run.
114
+ **Multi-source fed sets.** With a list, ONE build fetches and feeds EVERY listed source's data in the same PTB; the chain's per-ticker aggregator **weight tables** decide which contributions count — feeding an unweighted rule's price is dropped on-chain, while starving a weighted rule aborts. That asymmetry is what makes weight migrations (Core→Pro, Pyth↔waterx coexistence) safe: keep the list a **superset** of every ticker's weighted rule set and flip weights per ticker at any time — an env edit, never an SDK release. (One caveat: waterx's feed call burns a per-symbol signed-timestamp high-water mark regardless of weights see the replay note below.)
112
115
 
113
- The Pyth Core infra (`client.pyth`: state ids + Hermes endpoint) is **fixed per network** by `PYTH_DEFAULTS` and is **not** deployment-overridable the canonical `waterx-config` JSON carries no `pyth` block. The `pyth_lazer_rule` source reads only the credential/policy (`api_key`/`fetch`) from `client.pyth`; its on-chain infra comes from `LAZER_DEFAULTS` + config. The credential and fetch policy are passed at client init (`pythApiKey` / `pythFetch`), never through the JSON. `waterx_rule` touches no Pyth infra at all: its quote-center endpoint **defaults** to `WATERX_DEFAULTS[network]` (testnet `quote-center-staging.waterx.app` / mainnet `quote-center.waterx.app`) and is overridable with `waterxEndpoint`; its fetch policy/transport resolves **`waterxFetch` → `pythFetch` → built-in defaults** (15s timeout, 2 retries), so the dedicated override wins and the shared Pyth policy is only the fallback. Both are client-init options — see the browser/CORS note below.
116
+ **No cross-source fallback, no feeds guard at init.** Construction rejects an empty/nullish `oracleSource` **and any value outside `ORACLE_SOURCES`** (a legacy `'core'` / `'pyth'` string fails at `create`), but a listed source whose feed for a requested ticker is absent is **not** an error at client creation the build fails at **tx-build** only when **no** listed source serves the ticker (constant-only tickers, which need no price update, are exempt). A present-but-wrong feed id is not validated by the SDK; it aborts on-chain at dry-run.
117
+
118
+ Every source's external infra is a **rule-owned per-network table**, never deployment-overridable and never in the config JSON: `PYTH_CORE_INFRA` (`src/oracle/pyth.ts` — Pyth state ids + the keyless Core Hermes endpoint, read-plane accessor `pythCoreHermesEndpoint(network)`), `LAZER_INFRA` (`src/oracle/rules/pyth-lazer-rule.ts`), `WATERX_INFRA` (`src/oracle/rules/waterx-rule.ts` — testnet `quote-center-staging.waterx.app` / mainnet `quote-center.waterx.app`, accessor `waterxQuoteCenterEndpoint(network)`). For **price READS** under a fed set without `pyth_rule`, the documented Pyth Pro base (`pythProHermesEndpoint()` — identical for every subscriber, auth via the Bearer key) applies: resolve the read endpoint with `resolveHermesReadEndpoint(network, sources, override?)` instead of branching by hand, and pair it with `resolveOracleReadPlan` for the per-source served-sets/ids. `client.pyth` is the access-only `PythAccessConfig` — just the caller-supplied `pythApiKey` / `pythFetch` create options (a secret has no place in a public CDN JSON); `client.waterx` is likewise `WaterxAccessConfig` (`waterxEndpoint` / `waterxFetch` overrides only; fetch policy resolves **`waterxFetch` → built-in defaults** — deliberately no `pythFetch` fallback, sources never share config). See the browser/CORS note below.
114
119
 
115
120
  ```ts
116
- // Per-environment wiring — the consumer owns the env var, not the SDK:
121
+ // Per-environment wiring — the consumer owns the env var, not the SDK.
122
+ // parseOracleSourceList is THE canonical parser (trim, drop empty entries,
123
+ // validate every value, dedupe, throw operator-actionably) — never a bare
124
+ // split-and-cast, which would hand the strict constructor untrimmed junk.
125
+ import { parseOracleSourceList } from "@waterx/sdk/oracle";
126
+
117
127
  const perp = await PerpClient.create(network, {
118
128
  waterxConfigUrl,
119
- oracleSource: process.env.ORACLE_SOURCE as OracleSource | undefined, // e.g. staging: 'pyth_lazer_rule' | 'waterx_rule'
120
- pythApiKey: process.env.PYTH_API_KEY, // Lazer is auth-first; unused by 'pyth_rule' / 'waterx_rule'
129
+ oracleSource: parseOracleSourceList(process.env.ORACLE_SOURCE), // REQUIRED; comma list = the fed set
130
+ pythApiKey: process.env.PYTH_API_KEY, // required iff 'pyth_lazer_rule' is listed (Lazer is auth-first)
121
131
  });
122
132
  ```
123
133
 
124
- This is the staging-Lazer / prod-Core rollout pattern: staging sets `ORACLE_SOURCE=pyth_lazer_rule` (+ `PYTH_API_KEY`) or `ORACLE_SOURCE=waterx_rule`, which needs no credential while production leaves it unset — flipping an environment is an env-var change, never an SDK release.
134
+ This is the coexistence rollout pattern: staging lists every source under migration (`ORACLE_SOURCE=pyth_rule,pyth_lazer_rule,waterx_rule` + `PYTH_API_KEY`) while production stays single-value (`ORACLE_SOURCE=pyth_rule`) until its weight tables move — flipping an environment is an env-var change, never an SDK release.
125
135
 
126
136
  ### Adding an oracle source (runbook)
127
137
 
128
138
  Every source plugs in the same way — routing is driven **only** by the client's `oracleSource` option (never a config `enabled` flag, never `process.env`):
129
139
 
130
140
  1. **Implement `PriceUpdateRule`** in `src/oracle/rules/<name>-rule.ts` — all port fields (`src/oracle/price-update-rule.ts`): `kind`, `requiresFeeSource` (`true` iff the on-chain verify draws a per-update fee — gates the fail-fast fee-source check), `supportedTickers`, `fetchUpdateData`, `narrowUpdateData` (subset a cached whole-universe payload to one build's tickers — a divisible payload returns a per-feed subset, an indivisible one returns itself whole iff fully covered; uncovered ticker → `null` miss), `buildUpdateCalls`.
131
- 2. **Register it** in `src/oracle/rule-registry.ts` (`DEFAULT_RULES`) under a new `OracleSource` value (added to the union in `price-update-rule.ts`).
141
+ 2. **Register it** in `src/oracle/rule-registry.ts` (`DEFAULT_RULES`) under a new `OracleSource` value added to `ORACLE_SOURCES` in `price-update-rule.ts` (the union derives from that list; a registry test pins every listed value to a registered rule).
132
142
  3. **Publish the on-chain rule package** — its config entry (package ids, per-ticker `feeds`) arrives via the normal `waterx-config` deploy pipeline; type it in `OraclePackages` (`src/oracle/config.ts`).
133
- 4. **Add SDK infra constants** if the source needs external infra that is not part of the config JSON (API endpoints, verifier packages, state objects) — a per-network map in `src/oracle/config.ts`, mirroring `LAZER_DEFAULTS` / `WATERX_DEFAULTS`.
143
+ 4. **Add SDK infra constants** if the source needs external infra that is not part of the config JSON (API endpoints, verifier packages, state objects) — a **rule-owned** per-network table inside the rule's own file, mirroring `LAZER_INFRA` / `WATERX_INFRA` (never on the shared client, never in `oracle/config.ts`). Wire its read-plane served-set/ids into `resolveOracleReadPlan` (`src/oracle/read-plane.ts`).
134
144
  5. **Consumers flip `oracleSource`** per environment — no consumer code change, no SDK re-release.
135
145
 
136
- The in-house `waterx_rule` (ed25519 enclave-signed CEX prices, `src/oracle/rules/waterx-rule.ts`) took exactly this path: it pulls one enclave-signed batch envelope covering the requested tickers from the quote-center (`GET /v1/quotes/update?symbols=…`, public read — no auth), then verifies **and** feeds in a single `waterx_rule::collect_batch_latest` call per collector, so it emits no shared verify step. On-chain a freshness miss / replayed timestamp abstains (the other weighted rules cover); a config mismatch or bad signature aborts.
146
+ The in-house `waterx_rule` (ed25519 enclave-signed CEX prices, `src/oracle/rules/waterx-rule.ts`) took exactly this path: it pulls one enclave-signed batch envelope covering the requested tickers from the quote-center (`GET /v1/quotes/update?symbols=…`, public read — no auth), then verifies **and** feeds in a single `waterx_rule::collect_batch_latest` call per collector, so it emits no shared verify step. On-chain a **freshness** miss abstains (the other weighted rules cover); a config mismatch or bad signature aborts — and so does a **replayed** signed timestamp (`EReplayedSignature`, audit F-014: a signed tuple is single-use per symbol, weight-independent). Consequence: two PTBs carrying the same envelope for the same symbol cannot both land — never share one fetched envelope across concurrent builds for the same symbol.
137
147
 
138
148
  > **Browser consumers:** this source fetches the quote-center directly from the page, so the quote-center deployment must return `Access-Control-Allow-Origin` for the app's origin. For an origin that is not on that allowlist, point the SDK at your own proxy instead of the default host — the endpoint and the transport are both overridable at client init:
139
149
  >
@@ -148,7 +158,7 @@ The in-house `waterx_rule` (ed25519 enclave-signed CEX prices, `src/oracle/rules
148
158
  > });
149
159
  > ```
150
160
  >
151
- > Unset, `waterxEndpoint` falls back to `WATERX_DEFAULTS[network]` and `waterxFetch` to `pythFetch`, then to the built-in policy (15s timeout, 2 retries). Both are inert under the Pyth sources. They are also top-level options on the umbrella `WaterXClient.create({ oracleSource, waterxEndpoint, waterxFetch, … })`, which forwards them to the perp line. Node/keeper consumers are unaffected by CORS either way.
161
+ > Unset, `waterxEndpoint` falls back to the rule-owned `WATERX_INFRA[network]` and `waterxFetch` to the built-in policy (15s timeout, 2 retries) — there is deliberately no `pythFetch` fallback. Both are inert under the Pyth sources. They are also top-level options on the umbrella `WaterXClient.create({ oracleSource, waterxEndpoint, waterxFetch, … })`, which forwards them to the perp line. Node/keeper consumers are unaffected by CORS either way.
152
162
 
153
163
  ## Recipes & full surface
154
164
 
@@ -73,8 +73,9 @@ export interface WithdrawalQueuePackage {
73
73
  /**
74
74
  * Wormhole infra for the cross-chain credit bridge. `state_id` is the same
75
75
  * shared Sui Wormhole `State` object Pyth uses (kept in sync with
76
- * `PYTH_DEFAULTS[*].wormhole_state_id`). Override per-deployment via
77
- * `WaterXConfig.wormhole` if a deployment ever points elsewhere.
76
+ * `PYTH_CORE_INFRA[*].wormhole_state_id` in `oracle/pyth.ts`). Override
77
+ * per-deployment via `WaterXConfig.wormhole` if a deployment ever points
78
+ * elsewhere.
78
79
  */
79
80
  export interface WormholeInfraConfig {
80
81
  /** Shared Sui Wormhole `State` object. */
@@ -70,11 +70,6 @@ export declare const BatchPricePayload: MoveStruct<{
70
70
  length: number;
71
71
  }, string>;
72
72
  }, "@waterx/rule::waterx_rule::BatchPricePayload">;
73
- export declare const MerkleRoot: MoveStruct<{
74
- root: import("@mysten/sui/bcs").BcsType<number[], Iterable<number> & {
75
- length: number;
76
- }, string>;
77
- }, "@waterx/rule::waterx_rule::MerkleRoot">;
78
73
  export declare const FeedConfig: MoveStruct<{
79
74
  ticker: import("@mysten/sui/bcs").BcsType<string, string, "string">;
80
75
  sources: import("@mysten/sui/bcs").BcsType<string[], Iterable<string | number | bigint> & {
@@ -219,82 +214,17 @@ export interface CollectBatchLatestOptions {
219
214
  * and so aborts on any stale item, this path runs alongside Pyth/Supra. To let
220
215
  * those cover a lagging TEE — the blast-radius bound in the GCP ADR — a
221
216
  * _freshness_ miss ABSTAINS (records `none`, which `aggregator::remove_outliers`
222
- * drops before the `weight_threshold` fail-closed check), and so does a _replayed_
223
- * signed timestamp (the chain already accepted that snapshot the on-chain price
224
- * is at least as fresh, and two concurrent keeper PTBs sharing one envelope must
225
- * not kill each other). A _config/integrity_ mismatch (source / ticker / method /
226
- * confidence / deviation) still ABORTS: an enclave whose signed payload disagrees
227
- * with the on-chain feed config is a red flag, not a liveness blip. Bad signature,
228
- * malformed batch, and a future timestamp always abort.
217
+ * drops before the `weight_threshold` fail-closed check). A _config/integrity_
218
+ * mismatch (source / ticker / method / confidence / deviation) still ABORTS: an
219
+ * enclave whose signed payload disagrees with the on-chain feed config is a red
220
+ * flag, not a liveness blip. Bad signature, malformed batch, a future timestamp,
221
+ * and replay always abort.
229
222
  *
230
223
  * Every gate is identical to `feed_batch_item_latest`; only the abort-vs-abstain
231
- * disposition of the freshness + replay gates differs, and the sink is the
232
- * caller's collector rather than a fresh single-rule one.
224
+ * disposition of the two freshness gates differs, and the sink is the caller's
225
+ * collector rather than a fresh single-rule one.
233
226
  */
234
227
  export declare function collectBatchLatest(options: CollectBatchLatestOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
235
- export interface FeedSingleWithProofArguments {
236
- oracleObj: RawTransactionArgument<string>;
237
- config: RawTransactionArgument<string>;
238
- enclaveConfig: RawTransactionArgument<string>;
239
- enclave: RawTransactionArgument<string>;
240
- timestampMs: RawTransactionArgument<number | bigint>;
241
- item: TransactionArgument;
242
- proof: RawTransactionArgument<Array<Array<number>>>;
243
- sig: RawTransactionArgument<Array<number>>;
244
- }
245
- export interface FeedSingleWithProofOptions {
246
- package?: string;
247
- arguments: FeedSingleWithProofArguments | [
248
- oracleObj: RawTransactionArgument<string>,
249
- config: RawTransactionArgument<string>,
250
- enclaveConfig: RawTransactionArgument<string>,
251
- enclave: RawTransactionArgument<string>,
252
- timestampMs: RawTransactionArgument<number | bigint>,
253
- item: TransactionArgument,
254
- proof: RawTransactionArgument<Array<Array<number>>>,
255
- sig: RawTransactionArgument<Array<number>>
256
- ];
257
- }
258
- /**
259
- * Streaming per-symbol sibling of `feed_batch_latest`. Verifies ONE enclave
260
- * signature over a Merkle `root`, checks `item` is a leaf of that root via
261
- * `proof`, then runs the identical per-item gates + `oracle::aggregate`. Use for a
262
- * WaterxRule-only aggregator; for a Pyth-coexisting aggregator use
263
- * `collect_single_with_proof`.
264
- */
265
- export declare function feedSingleWithProof(options: FeedSingleWithProofOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
266
- export interface CollectSingleWithProofArguments {
267
- collector: TransactionArgument;
268
- config: RawTransactionArgument<string>;
269
- enclaveConfig: RawTransactionArgument<string>;
270
- enclave: RawTransactionArgument<string>;
271
- timestampMs: RawTransactionArgument<number | bigint>;
272
- item: TransactionArgument;
273
- proof: RawTransactionArgument<Array<Array<number>>>;
274
- sig: RawTransactionArgument<Array<number>>;
275
- }
276
- export interface CollectSingleWithProofOptions {
277
- package?: string;
278
- arguments: CollectSingleWithProofArguments | [
279
- collector: TransactionArgument,
280
- config: RawTransactionArgument<string>,
281
- enclaveConfig: RawTransactionArgument<string>,
282
- enclave: RawTransactionArgument<string>,
283
- timestampMs: RawTransactionArgument<number | bigint>,
284
- item: TransactionArgument,
285
- proof: RawTransactionArgument<Array<Array<number>>>,
286
- sig: RawTransactionArgument<Array<number>>
287
- ];
288
- }
289
- /**
290
- * Collect-only sibling of `feed_single_with_proof` for the dual-rule latest path
291
- * (WaterX alongside Pyth on one aggregator), mirroring `collect_batch_latest`:
292
- * verifies root + proof, feeds the leaf into the caller's `PriceCollector` WITHOUT
293
- * calling `oracle::aggregate`. Abort vs abstain matches `collect_batch_latest`
294
- * exactly — a config/integrity mismatch aborts, a freshness miss abstains so Pyth
295
- * can cover a lagging TEE.
296
- */
297
- export declare function collectSingleWithProof(options: CollectSingleWithProofOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
298
228
  export interface QuoteForTimestampArguments {
299
229
  config: RawTransactionArgument<string>;
300
230
  enclaveConfig: RawTransactionArgument<string>;
@@ -491,27 +421,6 @@ export interface BatchPriceIntentOptions {
491
421
  ];
492
422
  }
493
423
  export declare function batchPriceIntent(options?: BatchPriceIntentOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
494
- export interface MerkleRootIntentOptions {
495
- package?: string;
496
- arguments?: [
497
- ];
498
- }
499
- export declare function merkleRootIntent(options?: MerkleRootIntentOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
500
- export interface LeafHashOfArguments {
501
- item: TransactionArgument;
502
- }
503
- export interface LeafHashOfOptions {
504
- package?: string;
505
- arguments: LeafHashOfArguments | [
506
- item: TransactionArgument
507
- ];
508
- }
509
- /**
510
- * The keccak256 leaf hash for `item` — `keccak256(0x00 || BCS(item))`. Exposed so
511
- * the off-chain builder / SDK can be pinned against the on-chain encoding in a
512
- * cross-implementation golden test.
513
- */
514
- export declare function leafHashOf(options: LeafHashOfOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
515
424
  export interface MaxBatchSizeOptions {
516
425
  package?: string;
517
426
  arguments?: [
@@ -808,42 +717,6 @@ export interface SourceGataUsdtPerpWsOptions {
808
717
  ];
809
718
  }
810
719
  export declare function sourceGataUsdtPerpWs(options?: SourceGataUsdtPerpWsOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
811
- export interface SourceBybitSpotWsOptions {
812
- package?: string;
813
- arguments?: [
814
- ];
815
- }
816
- export declare function sourceBybitSpotWs(options?: SourceBybitSpotWsOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
817
- export interface SourceXstockEquityRestOptions {
818
- package?: string;
819
- arguments?: [
820
- ];
821
- }
822
- export declare function sourceXstockEquityRest(options?: SourceXstockEquityRestOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
823
- export interface SourceOkxSpotWsOptions {
824
- package?: string;
825
- arguments?: [
826
- ];
827
- }
828
- export declare function sourceOkxSpotWs(options?: SourceOkxSpotWsOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
829
- export interface SourceHyperliquidPerpWsOptions {
830
- package?: string;
831
- arguments?: [
832
- ];
833
- }
834
- export declare function sourceHyperliquidPerpWs(options?: SourceHyperliquidPerpWsOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
835
- export interface SourceGateioSpotWsOptions {
836
- package?: string;
837
- arguments?: [
838
- ];
839
- }
840
- export declare function sourceGateioSpotWs(options?: SourceGateioSpotWsOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
841
- export interface SourceKrakenSpotWsOptions {
842
- package?: string;
843
- arguments?: [
844
- ];
845
- }
846
- export declare function sourceKrakenSpotWs(options?: SourceKrakenSpotWsOptions): (tx: Transaction) => import("@mysten/sui/transactions").TransactionResult;
847
720
  export interface MethodDirectOptions {
848
721
  package?: string;
849
722
  arguments?: [
@@ -36,11 +36,9 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.BatchLatestUpdated = exports.HistoricalPriceVerified = exports.SignedTsHwmKey = exports.Config = exports.VerifiedPrice = exports.FeedConfig = exports.MerkleRoot = exports.BatchPricePayload = exports.BatchPriceItem = exports.PricePayload = exports.WaterxRule = exports.WATERX_RULE = void 0;
39
+ exports.BatchLatestUpdated = exports.HistoricalPriceVerified = exports.SignedTsHwmKey = exports.Config = exports.VerifiedPrice = exports.FeedConfig = exports.BatchPricePayload = exports.BatchPriceItem = exports.PricePayload = exports.WaterxRule = exports.WATERX_RULE = void 0;
40
40
  exports.feedBatchLatest = feedBatchLatest;
41
41
  exports.collectBatchLatest = collectBatchLatest;
42
- exports.feedSingleWithProof = feedSingleWithProof;
43
- exports.collectSingleWithProof = collectSingleWithProof;
44
42
  exports.quoteForTimestamp = quoteForTimestamp;
45
43
  exports.destroyVerifiedPrice = destroyVerifiedPrice;
46
44
  exports.collectVerifiedPrice = collectVerifiedPrice;
@@ -52,8 +50,6 @@ exports.newBatchItem = newBatchItem;
52
50
  exports.pushBatchItem = pushBatchItem;
53
51
  exports.feedConfig = feedConfig;
54
52
  exports.batchPriceIntent = batchPriceIntent;
55
- exports.merkleRootIntent = merkleRootIntent;
56
- exports.leafHashOf = leafHashOf;
57
53
  exports.maxBatchSize = maxBatchSize;
58
54
  exports.batchItems = batchItems;
59
55
  exports.batchItemSymbol = batchItemSymbol;
@@ -86,12 +82,6 @@ exports.sourceBinanceUsdmPerpWs = sourceBinanceUsdmPerpWs;
86
82
  exports.sourceBybitLinearPerpWs = sourceBybitLinearPerpWs;
87
83
  exports.sourceGateioUsdtPerpWs = sourceGateioUsdtPerpWs;
88
84
  exports.sourceGataUsdtPerpWs = sourceGataUsdtPerpWs;
89
- exports.sourceBybitSpotWs = sourceBybitSpotWs;
90
- exports.sourceXstockEquityRest = sourceXstockEquityRest;
91
- exports.sourceOkxSpotWs = sourceOkxSpotWs;
92
- exports.sourceHyperliquidPerpWs = sourceHyperliquidPerpWs;
93
- exports.sourceGateioSpotWs = sourceGateioSpotWs;
94
- exports.sourceKrakenSpotWs = sourceKrakenSpotWs;
95
85
  exports.methodDirect = methodDirect;
96
86
  exports.methodMedian = methodMedian;
97
87
  exports.methodConfidence = methodConfidence;
@@ -142,9 +132,6 @@ exports.BatchPriceItem = new index_ts_1.MoveStruct({ name: `${$moduleName}::Batc
142
132
  exports.BatchPricePayload = new index_ts_1.MoveStruct({ name: `${$moduleName}::BatchPricePayload`, fields: {
143
133
  items: bcs_1.bcs.vector(exports.BatchPriceItem)
144
134
  } });
145
- exports.MerkleRoot = new index_ts_1.MoveStruct({ name: `${$moduleName}::MerkleRoot`, fields: {
146
- root: bcs_1.bcs.vector(bcs_1.bcs.u8())
147
- } });
148
135
  exports.FeedConfig = new index_ts_1.MoveStruct({ name: `${$moduleName}::FeedConfig`, fields: {
149
136
  ticker: bcs_1.bcs.string(),
150
137
  sources: bcs_1.bcs.vector(bcs_1.bcs.u64()),
@@ -230,17 +217,15 @@ function feedBatchLatest(options) {
230
217
  * and so aborts on any stale item, this path runs alongside Pyth/Supra. To let
231
218
  * those cover a lagging TEE — the blast-radius bound in the GCP ADR — a
232
219
  * _freshness_ miss ABSTAINS (records `none`, which `aggregator::remove_outliers`
233
- * drops before the `weight_threshold` fail-closed check), and so does a _replayed_
234
- * signed timestamp (the chain already accepted that snapshot the on-chain price
235
- * is at least as fresh, and two concurrent keeper PTBs sharing one envelope must
236
- * not kill each other). A _config/integrity_ mismatch (source / ticker / method /
237
- * confidence / deviation) still ABORTS: an enclave whose signed payload disagrees
238
- * with the on-chain feed config is a red flag, not a liveness blip. Bad signature,
239
- * malformed batch, and a future timestamp always abort.
220
+ * drops before the `weight_threshold` fail-closed check). A _config/integrity_
221
+ * mismatch (source / ticker / method / confidence / deviation) still ABORTS: an
222
+ * enclave whose signed payload disagrees with the on-chain feed config is a red
223
+ * flag, not a liveness blip. Bad signature, malformed batch, a future timestamp,
224
+ * and replay always abort.
240
225
  *
241
226
  * Every gate is identical to `feed_batch_item_latest`; only the abort-vs-abstain
242
- * disposition of the freshness + replay gates differs, and the sink is the
243
- * caller's collector rather than a fresh single-rule one.
227
+ * disposition of the two freshness gates differs, and the sink is the caller's
228
+ * collector rather than a fresh single-rule one.
244
229
  */
245
230
  function collectBatchLatest(options) {
246
231
  const packageAddress = options.package ?? '@waterx/rule';
@@ -262,63 +247,6 @@ function collectBatchLatest(options) {
262
247
  arguments: (0, index_ts_1.normalizeMoveArguments)(options.arguments, argumentsTypes, parameterNames),
263
248
  });
264
249
  }
265
- /**
266
- * Streaming per-symbol sibling of `feed_batch_latest`. Verifies ONE enclave
267
- * signature over a Merkle `root`, checks `item` is a leaf of that root via
268
- * `proof`, then runs the identical per-item gates + `oracle::aggregate`. Use for a
269
- * WaterxRule-only aggregator; for a Pyth-coexisting aggregator use
270
- * `collect_single_with_proof`.
271
- */
272
- function feedSingleWithProof(options) {
273
- const packageAddress = options.package ?? '@waterx/rule';
274
- const argumentsTypes = [
275
- null,
276
- null,
277
- '0x2::clock::Clock',
278
- null,
279
- null,
280
- 'u64',
281
- null,
282
- 'vector<vector<u8>>',
283
- 'vector<u8>'
284
- ];
285
- const parameterNames = ["oracleObj", "config", "enclaveConfig", "enclave", "timestampMs", "item", "proof", "sig"];
286
- return (tx) => tx.moveCall({
287
- package: packageAddress,
288
- module: 'waterx_rule',
289
- function: 'feed_single_with_proof',
290
- arguments: (0, index_ts_1.normalizeMoveArguments)(options.arguments, argumentsTypes, parameterNames),
291
- });
292
- }
293
- /**
294
- * Collect-only sibling of `feed_single_with_proof` for the dual-rule latest path
295
- * (WaterX alongside Pyth on one aggregator), mirroring `collect_batch_latest`:
296
- * verifies root + proof, feeds the leaf into the caller's `PriceCollector` WITHOUT
297
- * calling `oracle::aggregate`. Abort vs abstain matches `collect_batch_latest`
298
- * exactly — a config/integrity mismatch aborts, a freshness miss abstains so Pyth
299
- * can cover a lagging TEE.
300
- */
301
- function collectSingleWithProof(options) {
302
- const packageAddress = options.package ?? '@waterx/rule';
303
- const argumentsTypes = [
304
- null,
305
- null,
306
- '0x2::clock::Clock',
307
- null,
308
- null,
309
- 'u64',
310
- null,
311
- 'vector<vector<u8>>',
312
- 'vector<u8>'
313
- ];
314
- const parameterNames = ["collector", "config", "enclaveConfig", "enclave", "timestampMs", "item", "proof", "sig"];
315
- return (tx) => tx.moveCall({
316
- package: packageAddress,
317
- module: 'waterx_rule',
318
- function: 'collect_single_with_proof',
319
- arguments: (0, index_ts_1.normalizeMoveArguments)(options.arguments, argumentsTypes, parameterNames),
320
- });
321
- }
322
250
  /**
323
251
  * Verifies a historical quote without touching `waterx_oracle`.
324
252
  *
@@ -510,32 +438,6 @@ function batchPriceIntent(options = {}) {
510
438
  function: 'batch_price_intent',
511
439
  });
512
440
  }
513
- function merkleRootIntent(options = {}) {
514
- const packageAddress = options.package ?? '@waterx/rule';
515
- return (tx) => tx.moveCall({
516
- package: packageAddress,
517
- module: 'waterx_rule',
518
- function: 'merkle_root_intent',
519
- });
520
- }
521
- /**
522
- * The keccak256 leaf hash for `item` — `keccak256(0x00 || BCS(item))`. Exposed so
523
- * the off-chain builder / SDK can be pinned against the on-chain encoding in a
524
- * cross-implementation golden test.
525
- */
526
- function leafHashOf(options) {
527
- const packageAddress = options.package ?? '@waterx/rule';
528
- const argumentsTypes = [
529
- null
530
- ];
531
- const parameterNames = ["item"];
532
- return (tx) => tx.moveCall({
533
- package: packageAddress,
534
- module: 'waterx_rule',
535
- function: 'leaf_hash_of',
536
- arguments: (0, index_ts_1.normalizeMoveArguments)(options.arguments, argumentsTypes, parameterNames),
537
- });
538
- }
539
441
  function maxBatchSize(options = {}) {
540
442
  const packageAddress = options.package ?? '@waterx/rule';
541
443
  return (tx) => tx.moveCall({
@@ -922,54 +824,6 @@ function sourceGataUsdtPerpWs(options = {}) {
922
824
  function: 'source_gata_usdt_perp_ws',
923
825
  });
924
826
  }
925
- function sourceBybitSpotWs(options = {}) {
926
- const packageAddress = options.package ?? '@waterx/rule';
927
- return (tx) => tx.moveCall({
928
- package: packageAddress,
929
- module: 'waterx_rule',
930
- function: 'source_bybit_spot_ws',
931
- });
932
- }
933
- function sourceXstockEquityRest(options = {}) {
934
- const packageAddress = options.package ?? '@waterx/rule';
935
- return (tx) => tx.moveCall({
936
- package: packageAddress,
937
- module: 'waterx_rule',
938
- function: 'source_xstock_equity_rest',
939
- });
940
- }
941
- function sourceOkxSpotWs(options = {}) {
942
- const packageAddress = options.package ?? '@waterx/rule';
943
- return (tx) => tx.moveCall({
944
- package: packageAddress,
945
- module: 'waterx_rule',
946
- function: 'source_okx_spot_ws',
947
- });
948
- }
949
- function sourceHyperliquidPerpWs(options = {}) {
950
- const packageAddress = options.package ?? '@waterx/rule';
951
- return (tx) => tx.moveCall({
952
- package: packageAddress,
953
- module: 'waterx_rule',
954
- function: 'source_hyperliquid_perp_ws',
955
- });
956
- }
957
- function sourceGateioSpotWs(options = {}) {
958
- const packageAddress = options.package ?? '@waterx/rule';
959
- return (tx) => tx.moveCall({
960
- package: packageAddress,
961
- module: 'waterx_rule',
962
- function: 'source_gateio_spot_ws',
963
- });
964
- }
965
- function sourceKrakenSpotWs(options = {}) {
966
- const packageAddress = options.package ?? '@waterx/rule';
967
- return (tx) => tx.moveCall({
968
- package: packageAddress,
969
- module: 'waterx_rule',
970
- function: 'source_kraken_spot_ws',
971
- });
972
- }
973
827
  function methodDirect(options = {}) {
974
828
  const packageAddress = options.package ?? '@waterx/rule';
975
829
  return (tx) => tx.moveCall({
@@ -19,7 +19,8 @@
19
19
  *
20
20
  * `refreshOraclePrices` additionally routes the on-chain price *update* leg
21
21
  * (the fetch + verify/push step, before any of the above feeding) through the
22
- * `PriceUpdateRule` selected by `host.oracleSource` — see `rule-registry.ts`.
22
+ * `PriceUpdateRule` of EVERY source in the `host.oracleSources` fed set — see
23
+ * `rule-registry.ts`.
23
24
  */
24
25
  import type { Transaction, TransactionArgument } from "@mysten/sui/transactions";
25
26
  import type { OracleHost } from "./host.ts";
@@ -84,27 +85,26 @@ export declare function aggregateTickerWithConstant(tx: Transaction, host: Oracl
84
85
  * entry, Lazer if the lazer update leg served it — see below — Supra when
85
86
  * enabled, Constant when it's a constant ticker).
86
87
  *
87
- * Before that, the on-chain price *update* leg is routed by `host.oracleSource`
88
- * (see `rule-registry.ts`): the ONE selected source serves every ticker in its
89
- * `supportedTickers(host)`. There is **no cross-source fallback** — a requested
90
- * ticker the selected source does not serve, and that is not a constant-only
91
- * ticker (which needs no price-update leg), fails the build immediately with a
92
- * clear error naming the ticker and source. That is the deliberate "fail the
93
- * tx-build, don't silently reroute" contract: a wrong-but-present feed id is
94
- * NOT validated here (it surfaces on-chain at dry-run); a MISSING feed for the
95
- * selected source is caught here. When the selected source's feed exists but is
96
- * wrong, this function does nothing special — the on-chain aggregate aborts at
97
- * dry-run, which is correct.
88
+ * Before that, the on-chain price *update* leg is routed by the
89
+ * `host.oracleSources` fed set (see `rule-registry.ts`): EVERY listed source
90
+ * updates the tickers its own `supportedTickers(host)` serves, all in this one
91
+ * PTB. There is **no cross-source fallback** a requested ticker NO listed
92
+ * source serves, and that is not a constant-only ticker (which needs no
93
+ * price-update leg), fails the build immediately with a clear error naming
94
+ * the ticker and the list. That is the deliberate "fail the tx-build, don't
95
+ * silently reroute" contract: a wrong-but-present feed id is NOT validated
96
+ * here (it surfaces on-chain at dry-run); a ticker MISSING from every listed
97
+ * source's feeds is caught here.
98
98
  *
99
- * The selected source's fetch + build runs against its own infra, guaranteeing
100
- * per-rule PTB atomicity. A fee-source pre-check runs first (the source's
101
- * `requiresFeeSource`) BEFORE any off-chain fetch or PTB mutation — so a
102
- * fee-charging source with no `opts.feeSource` throws
99
+ * Each source's fetch + build runs against its own infra, guaranteeing
100
+ * per-rule PTB atomicity. A fee-source pre-check runs first (any listed
101
+ * source's `requiresFeeSource`) BEFORE any off-chain fetch or PTB mutation —
102
+ * so a fee-charging source with no `opts.feeSource` throws
103
103
  * `OracleFeeSourceUnavailable` with zero wasted network calls and zero stray
104
- * moveCalls. Only once that check passes does the off-chain fetch run and
105
- * complete before any PTB mutation; on-chain reads inside `buildUpdateCalls`
106
- * can still fail mid-append for other reasons — callers discard the tx on any
107
- * throw.
104
+ * moveCalls. Only once that check passes do the off-chain fetches run — in
105
+ * parallel across sources — and ALL settle before the first PTB mutation;
106
+ * on-chain reads inside `buildUpdateCalls` can still fail mid-append for
107
+ * other reasons — callers discard the tx on any throw.
108
108
  *
109
109
  * **Collector-feed leg is rule-aware:** a lazer-served group's
110
110
  * `buildUpdateCalls` returns the verified `Update` PTB value
@@ -139,7 +139,7 @@ export declare function refreshOraclePrices(tx: Transaction, host: OracleHost, t
139
139
  /**
140
140
  * @internal Test-only: layer fake `PriceUpdateRule`s on top of the
141
141
  * production registry (see `rule-registry.ts`'s `resolveOracleRule`).
142
- * Production callers never set this — routing is by `host.oracleSource`
142
+ * Production callers never set this — routing is by `host.oracleSources`
143
143
  * alone.
144
144
  */
145
145
  ruleOverrides?: Partial<Record<OracleSource, PriceUpdateRule>>;