@waterx/sdk 4.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -10
- package/dist/cjs/src/account/account.js +2 -1
- package/dist/cjs/src/account/funding/balance.d.ts +25 -2
- package/dist/cjs/src/account/funding/balance.js +36 -24
- package/dist/cjs/src/account/funding/credit.js +6 -10
- package/dist/cjs/src/constants.d.ts +15 -1
- package/dist/cjs/src/constants.js +18 -4
- package/dist/cjs/src/generated/waterx_rule/deps/bucket_v2_framework/float.d.ts +8 -0
- package/dist/cjs/src/generated/waterx_rule/deps/bucket_v2_framework/float.js +13 -0
- package/dist/cjs/src/generated/waterx_rule/deps/sui/vec_map.d.ts +36 -0
- package/dist/cjs/src/generated/waterx_rule/deps/sui/vec_map.js +31 -0
- package/dist/cjs/src/generated/waterx_rule/waterx_rule.d.ts +940 -0
- package/dist/cjs/src/generated/waterx_rule/waterx_rule.js +1092 -0
- package/dist/cjs/src/oracle/aggregate.d.ts +2 -0
- package/dist/cjs/src/oracle/aggregate.js +27 -2
- package/dist/cjs/src/oracle/config.d.ts +75 -0
- package/dist/cjs/src/oracle/config.js +15 -1
- package/dist/cjs/src/oracle/host.d.ts +12 -1
- package/dist/cjs/src/oracle/index.d.ts +2 -0
- package/dist/cjs/src/oracle/index.js +6 -1
- package/dist/cjs/src/oracle/price-update-rule.d.ts +3 -4
- package/dist/cjs/src/oracle/rule-registry.d.ts +8 -7
- package/dist/cjs/src/oracle/rule-registry.js +10 -7
- package/dist/cjs/src/oracle/rules/waterx-rule.d.ts +89 -0
- package/dist/cjs/src/oracle/rules/waterx-rule.js +272 -0
- package/dist/cjs/src/perp/client.d.ts +44 -6
- package/dist/cjs/src/perp/client.js +14 -0
- package/dist/cjs/src/perp/config.d.ts +2 -3
- package/dist/cjs/src/perp/config.js +2 -1
- package/dist/cjs/src/perp/constants.d.ts +0 -6
- package/dist/cjs/src/perp/constants.js +11 -9
- package/dist/cjs/src/perp/fetch/account.js +3 -0
- package/dist/cjs/src/perp/fetch/bridge.js +2 -1
- package/dist/cjs/src/perp/fetch/market.js +2 -1
- package/dist/cjs/src/perp/fetch/positions.d.ts +16 -10
- package/dist/cjs/src/perp/fetch/positions.js +23 -20
- package/dist/cjs/src/perp/index.d.ts +6 -2
- package/dist/cjs/src/perp/index.js +12 -6
- package/dist/cjs/src/perp/liq-view.d.ts +64 -0
- package/dist/cjs/src/perp/liq-view.js +74 -0
- package/dist/cjs/src/perp/user/order.d.ts +13 -0
- package/dist/cjs/src/perp/user/order.js +30 -16
- package/dist/cjs/src/perp/user/staking.js +3 -2
- package/dist/cjs/src/perp/user/trading.js +25 -24
- package/dist/cjs/src/perp/user/wlp.js +6 -5
- package/dist/cjs/src/prediction/utils.d.ts +11 -2
- package/dist/cjs/src/prediction/utils.js +22 -22
- package/dist/cjs/src/unified-client.d.ts +31 -10
- package/dist/cjs/src/unified-client.js +2 -0
- package/dist/cjs/src/utils/format.d.ts +14 -0
- package/dist/cjs/src/utils/format.js +24 -0
- package/dist/cjs/src/utils/math.d.ts +304 -12
- package/dist/cjs/src/utils/math.js +397 -17
- package/dist/cjs/src/utils/validate.d.ts +69 -0
- package/dist/cjs/src/utils/validate.js +183 -0
- package/dist/src/account/account.js +2 -1
- package/dist/src/account/funding/balance.d.ts +25 -2
- package/dist/src/account/funding/balance.js +36 -24
- package/dist/src/account/funding/credit.js +6 -10
- package/dist/src/constants.d.ts +15 -1
- package/dist/src/constants.js +17 -3
- package/dist/src/generated/waterx_rule/deps/bucket_v2_framework/float.d.ts +8 -0
- package/dist/src/generated/waterx_rule/deps/bucket_v2_framework/float.js +10 -0
- package/dist/src/generated/waterx_rule/deps/sui/vec_map.d.ts +36 -0
- package/dist/src/generated/waterx_rule/deps/sui/vec_map.js +27 -0
- package/dist/src/generated/waterx_rule/waterx_rule.d.ts +940 -0
- package/dist/src/generated/waterx_rule/waterx_rule.js +991 -0
- package/dist/src/oracle/aggregate.d.ts +2 -0
- package/dist/src/oracle/aggregate.js +27 -2
- package/dist/src/oracle/config.d.ts +75 -0
- package/dist/src/oracle/config.js +14 -0
- package/dist/src/oracle/host.d.ts +12 -1
- package/dist/src/oracle/index.d.ts +2 -0
- package/dist/src/oracle/index.js +3 -0
- package/dist/src/oracle/price-update-rule.d.ts +3 -4
- package/dist/src/oracle/rule-registry.d.ts +8 -7
- package/dist/src/oracle/rule-registry.js +10 -7
- package/dist/src/oracle/rules/waterx-rule.d.ts +89 -0
- package/dist/src/oracle/rules/waterx-rule.js +266 -0
- package/dist/src/perp/client.d.ts +44 -6
- package/dist/src/perp/client.js +15 -1
- package/dist/src/perp/config.d.ts +2 -3
- package/dist/src/perp/config.js +1 -1
- package/dist/src/perp/constants.d.ts +0 -6
- package/dist/src/perp/constants.js +10 -8
- package/dist/src/perp/fetch/account.js +3 -0
- package/dist/src/perp/fetch/bridge.js +2 -1
- package/dist/src/perp/fetch/market.js +2 -1
- package/dist/src/perp/fetch/positions.d.ts +16 -10
- package/dist/src/perp/fetch/positions.js +28 -20
- package/dist/src/perp/index.d.ts +6 -2
- package/dist/src/perp/index.js +4 -2
- package/dist/src/perp/liq-view.d.ts +64 -0
- package/dist/src/perp/liq-view.js +71 -0
- package/dist/src/perp/user/order.d.ts +13 -0
- package/dist/src/perp/user/order.js +30 -16
- package/dist/src/perp/user/staking.js +3 -2
- package/dist/src/perp/user/trading.js +25 -24
- package/dist/src/perp/user/wlp.js +6 -5
- package/dist/src/prediction/utils.d.ts +11 -2
- package/dist/src/prediction/utils.js +22 -22
- package/dist/src/unified-client.d.ts +31 -10
- package/dist/src/unified-client.js +2 -0
- package/dist/src/utils/format.d.ts +14 -0
- package/dist/src/utils/format.js +21 -0
- package/dist/src/utils/math.d.ts +304 -12
- package/dist/src/utils/math.js +394 -17
- package/dist/src/utils/validate.d.ts +69 -0
- package/dist/src/utils/validate.js +167 -0
- package/package.json +4 -1
|
@@ -25,6 +25,7 @@ import type { Transaction, TransactionArgument } from "@mysten/sui/transactions"
|
|
|
25
25
|
import type { OracleHost } from "./host.ts";
|
|
26
26
|
import type { OracleSource, PriceUpdateRule, UpdateDataProvider } from "./price-update-rule.ts";
|
|
27
27
|
import { type OracleFeeSource, type PythCache } from "./pyth.ts";
|
|
28
|
+
import { type WaterxSignedEnvelope } from "./rules/waterx-rule.ts";
|
|
28
29
|
/**
|
|
29
30
|
* Aggregate one ticker's price into the shared `Oracle`: build a collector, feed
|
|
30
31
|
* every rule the ticker is configured for, then `aggregate`.
|
|
@@ -54,6 +55,7 @@ export declare function aggregateTicker(tx: Transaction, host: OracleHost, args:
|
|
|
54
55
|
ticker: string;
|
|
55
56
|
priceInfoObjectId?: string;
|
|
56
57
|
lazerUpdate?: TransactionArgument;
|
|
58
|
+
waterxEnvelope?: WaterxSignedEnvelope;
|
|
57
59
|
}): void;
|
|
58
60
|
/**
|
|
59
61
|
* Thin wrapper over {@link aggregateTicker} for a Pyth-fed ticker. Kept for
|
|
@@ -34,6 +34,7 @@ const constant_rule_ts_1 = require("./rules/constant-rule.js");
|
|
|
34
34
|
const pyth_lazer_rule_ts_1 = require("./rules/pyth-lazer-rule.js");
|
|
35
35
|
const pyth_rule_ts_1 = require("./rules/pyth-rule.js");
|
|
36
36
|
const supra_rule_ts_1 = require("./rules/supra-rule.js");
|
|
37
|
+
const waterx_rule_ts_1 = require("./rules/waterx-rule.js");
|
|
37
38
|
/**
|
|
38
39
|
* Resolve one group's off-chain update payload for {@link refreshOraclePrices}:
|
|
39
40
|
* try `provider.get(source, tickers)` first (when a provider is configured),
|
|
@@ -116,6 +117,14 @@ function aggregateTicker(tx, host, args) {
|
|
|
116
117
|
(0, pyth_lazer_rule_ts_1.feedLazerRule)(tx, host, collector, args.lazerUpdate);
|
|
117
118
|
fed = true;
|
|
118
119
|
}
|
|
120
|
+
if (args.waterxEnvelope !== undefined) {
|
|
121
|
+
// waterx_rule::collect_batch_latest verifies the batch signature and feeds
|
|
122
|
+
// this collector's symbol from the batch. If the ticker's aggregator does
|
|
123
|
+
// not (yet) weight `WaterxRule`, the contribution is silently dropped
|
|
124
|
+
// on-chain — feeding ahead of the weight migration is harmless.
|
|
125
|
+
(0, waterx_rule_ts_1.feedWaterxRule)(tx, host, collector, args.waterxEnvelope);
|
|
126
|
+
fed = true;
|
|
127
|
+
}
|
|
119
128
|
if (fed) {
|
|
120
129
|
// Supra rides on the same collector when enabled (abstains on-chain otherwise).
|
|
121
130
|
(0, supra_rule_ts_1.maybeFeedSupra)(tx, host, collector);
|
|
@@ -250,6 +259,11 @@ async function refreshOraclePrices(tx, host, tickers, opts = {}) {
|
|
|
250
259
|
// moveCalls in a caller-owned tx. Map each lazer-served ticker to the one
|
|
251
260
|
// verified `Update` PTB value for the collector-feed leg below.
|
|
252
261
|
const lazerUpdateByTicker = new Map();
|
|
262
|
+
// Signed batch envelope per waterx-served ticker. Unlike Lazer's shared PTB
|
|
263
|
+
// handle, waterx's verify+feed is bundled into `collect_batch_latest` in the
|
|
264
|
+
// per-ticker feed leg, so its `buildUpdateCalls` emits nothing and the
|
|
265
|
+
// envelope is carried straight from the group's fetched data.
|
|
266
|
+
const waterxEnvelopeByTicker = new Map();
|
|
253
267
|
if (selectedGroup.length > 0) {
|
|
254
268
|
const group = { source: host.oracleSource, rule: selectedRule, tickers: selectedGroup };
|
|
255
269
|
const data = await resolveGroupUpdateData(host, group, opts.updateDataProvider);
|
|
@@ -258,12 +272,22 @@ async function refreshOraclePrices(tx, host, tickers, opts = {}) {
|
|
|
258
272
|
feeSource: opts.feeSource,
|
|
259
273
|
})) ?? undefined;
|
|
260
274
|
// Route by the handle's kind discriminant — the one site the tag exists to
|
|
261
|
-
// protect: a future non-lazer handle
|
|
262
|
-
//
|
|
275
|
+
// protect: a future non-lazer handle must never be silently fed into
|
|
276
|
+
// pyth_lazer_rule::feed.
|
|
263
277
|
if (handle?.kind === "pyth_lazer_rule") {
|
|
264
278
|
for (const ticker of selectedGroup)
|
|
265
279
|
lazerUpdateByTicker.set(ticker, handle.update);
|
|
266
280
|
}
|
|
281
|
+
// waterx_rule emits no shared handle (verify+feed is bundled into the
|
|
282
|
+
// per-ticker `collect_batch_latest`), so the envelope is carried straight
|
|
283
|
+
// from this group's fetched data to the feed leg below.
|
|
284
|
+
if (selectedRule.kind === "waterx_rule") {
|
|
285
|
+
const envelope = (0, waterx_rule_ts_1.waterxEnvelopeOf)(data);
|
|
286
|
+
if (envelope) {
|
|
287
|
+
for (const ticker of selectedGroup)
|
|
288
|
+
waterxEnvelopeByTicker.set(ticker, envelope);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
267
291
|
}
|
|
268
292
|
// Aggregate each ticker, feeding whichever rules it is configured for.
|
|
269
293
|
for (const ticker of tickers) {
|
|
@@ -271,6 +295,7 @@ async function refreshOraclePrices(tx, host, tickers, opts = {}) {
|
|
|
271
295
|
ticker,
|
|
272
296
|
priceInfoObjectId: priceInfoByTicker.get(ticker),
|
|
273
297
|
lazerUpdate: lazerUpdateByTicker.get(ticker),
|
|
298
|
+
waterxEnvelope: waterxEnvelopeByTicker.get(ticker),
|
|
274
299
|
});
|
|
275
300
|
}
|
|
276
301
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import type { BasePackageEntry } from "../account/config.ts";
|
|
13
13
|
import type { BaseLineConfig } from "../base-client.ts";
|
|
14
14
|
import type { Network } from "../constants.ts";
|
|
15
|
+
import type { FetchPolicy } from "./update-fetch.ts";
|
|
15
16
|
export interface PythRulePackage extends BasePackageEntry {
|
|
16
17
|
config: string;
|
|
17
18
|
feeds: Record<string, {
|
|
@@ -82,6 +83,39 @@ export interface SupraRulePackage extends BasePackageEntry {
|
|
|
82
83
|
*/
|
|
83
84
|
enabled?: boolean;
|
|
84
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Per-ticker `waterx_rule` feed entry. Keyed in `feeds` by the oracle **ticker**
|
|
88
|
+
* (e.g. `"SUIUSD"`, the same key `pyth_rule.feeds` uses), so
|
|
89
|
+
* `Object.keys(feeds)` is the SDK's supported-ticker set. The fields are
|
|
90
|
+
* informational off-chain — the SDK keys routing/support off the entry's
|
|
91
|
+
* presence and pushes the enclave-signed price verbatim, never re-deriving it.
|
|
92
|
+
*/
|
|
93
|
+
export interface WaterxRuleFeedEntry {
|
|
94
|
+
/** Exchange ticker the quote-center aggregates from (e.g. `"SUIUSDT"`). */
|
|
95
|
+
ticker?: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* `waterx_rule` deployment entry — the first-party Nautilus-TEE oracle rule.
|
|
99
|
+
* Read by `WaterxRule` (`rules/waterx-rule.ts`): `feeds` for ticker support,
|
|
100
|
+
* `config`/`enclave_config`/`enclave` for the `collect_batch_latest` call,
|
|
101
|
+
* `published_at` for the package address. The off-chain signed price is pulled
|
|
102
|
+
* from the quote-center (endpoint from {@link WATERX_DEFAULTS}), not this JSON.
|
|
103
|
+
*
|
|
104
|
+
* `enabled` mirrors the JSON field verbatim but MUST NOT be read for routing —
|
|
105
|
+
* which rule prices a ticker is decided solely by the client's `oracleSource`
|
|
106
|
+
* create option (see `OracleHost.oracleSource`), mirroring `pyth_lazer_rule`.
|
|
107
|
+
*/
|
|
108
|
+
export interface WaterxRulePackage extends BasePackageEntry {
|
|
109
|
+
/** Shared `waterx_rule::Config` (per-symbol on-chain feed_config). */
|
|
110
|
+
config: string;
|
|
111
|
+
/** Shared `EnclaveConfig<WATERX_RULE>` the on-chain signature verify runs against. */
|
|
112
|
+
enclave_config: string;
|
|
113
|
+
/** Shared `Enclave<WATERX_RULE>` holding the registered TEE signing pubkey. */
|
|
114
|
+
enclave: string;
|
|
115
|
+
enabled?: boolean;
|
|
116
|
+
/** Oracle ticker → feed entry; presence marks the ticker as waterx-served. */
|
|
117
|
+
feeds: Record<string, WaterxRuleFeedEntry>;
|
|
118
|
+
}
|
|
85
119
|
export interface WaterxOraclePackage extends BasePackageEntry {
|
|
86
120
|
listing_cap: string;
|
|
87
121
|
oracle: string;
|
|
@@ -99,6 +133,8 @@ export interface OraclePackages {
|
|
|
99
133
|
pyth_lazer_rule?: PythLazerRulePackage;
|
|
100
134
|
constant_rule?: WaterxConstantRulePackage;
|
|
101
135
|
supra_rule?: SupraRulePackage;
|
|
136
|
+
/** See {@link WaterxRulePackage} — read by `WaterxRule` when `oracleSource` selects it. */
|
|
137
|
+
waterx_rule?: WaterxRulePackage;
|
|
102
138
|
waterx_oracle: WaterxOraclePackage;
|
|
103
139
|
}
|
|
104
140
|
/**
|
|
@@ -172,6 +208,45 @@ export declare const LAZER_DEFAULTS: Record<Network, {
|
|
|
172
208
|
endpoint: string;
|
|
173
209
|
verifier_package: string;
|
|
174
210
|
}>;
|
|
211
|
+
/**
|
|
212
|
+
* Resolved WaterX quote-center infra for `WaterxRule` — the network default
|
|
213
|
+
* from {@link WATERX_DEFAULTS}, with both fields overridable at client init
|
|
214
|
+
* (`waterxEndpoint` / `waterxFetch`).
|
|
215
|
+
*
|
|
216
|
+
* The override exists because this is the one oracle source a BROWSER fetches
|
|
217
|
+
* itself: the rule pulls the signed envelope from the page, so it is subject to
|
|
218
|
+
* the quote-center deployment's CORS allowlist. A front end whose origin is not
|
|
219
|
+
* on that list — or one that must route egress through its own backend — points
|
|
220
|
+
* `endpoint` at a same-origin proxy (or supplies `fetch.fetchImpl`) instead of
|
|
221
|
+
* being locked to the hardcoded host.
|
|
222
|
+
*/
|
|
223
|
+
export interface WaterxInfraConfig {
|
|
224
|
+
/**
|
|
225
|
+
* Quote-center base URL. A base PATH is preserved — the rule appends via
|
|
226
|
+
* `joinEndpointPath`, so `https://app.example/api/quote-center` resolves to
|
|
227
|
+
* `…/api/quote-center/v1/quotes/update` and a proxy route is not rewritten
|
|
228
|
+
* away. A trailing slash is trimmed.
|
|
229
|
+
*/
|
|
230
|
+
endpoint: string;
|
|
231
|
+
/**
|
|
232
|
+
* Retry/timeout policy (and `fetchImpl`) for the quote-center fetch — see
|
|
233
|
+
* `fetchWithPolicy` (`./update-fetch.ts`). Supplied via the `waterxFetch`
|
|
234
|
+
* create option. When unset the rule falls back to the shared `pyth.fetch`
|
|
235
|
+
* policy, then to `fetchWithPolicy`'s defaults (15s timeout, 2 retries).
|
|
236
|
+
*/
|
|
237
|
+
fetch?: FetchPolicy;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* WaterX quote-center base URL by network — the first-party TEE-signed price
|
|
241
|
+
* hub `WaterxRule` pulls from (`GET /v1/quotes/update?symbols=…`). Mirrors
|
|
242
|
+
* {@link LAZER_DEFAULTS}: infra WaterX operates, not part of the `waterx-config`
|
|
243
|
+
* JSON. Public read (no auth), so there is no api_key. `endpoint` has no
|
|
244
|
+
* trailing slash — the rule appends the path.
|
|
245
|
+
*
|
|
246
|
+
* These are DEFAULTS, not a hard pin: a consumer overrides them per client via
|
|
247
|
+
* `waterxEndpoint` / `waterxFetch` (resolved onto `client.waterx`).
|
|
248
|
+
*/
|
|
249
|
+
export declare const WATERX_DEFAULTS: Record<Network, WaterxInfraConfig>;
|
|
175
250
|
/**
|
|
176
251
|
* The narrow config shape the oracle/refresh code needs. `WaterXConfig`
|
|
177
252
|
* (the perp line's full config) is assignable to this, so `PerpClient` satisfies
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* mirrors the earlier account-config hoist.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.LAZER_DEFAULTS = exports.PYTH_DEFAULTS = void 0;
|
|
14
|
+
exports.WATERX_DEFAULTS = exports.LAZER_DEFAULTS = exports.PYTH_DEFAULTS = void 0;
|
|
15
15
|
exports.PYTH_DEFAULTS = {
|
|
16
16
|
MAINNET: {
|
|
17
17
|
state_id: "0x1f9310238ee9298fb703c3419030b35b22bb1cc37113e3bb5007c99aec79e5b8",
|
|
@@ -53,3 +53,17 @@ exports.LAZER_DEFAULTS = {
|
|
|
53
53
|
verifier_package: "0xf5bd2141967507050a91b58de3d95e77c432cd90d1799ee46effc27430a68c21",
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* WaterX quote-center base URL by network — the first-party TEE-signed price
|
|
58
|
+
* hub `WaterxRule` pulls from (`GET /v1/quotes/update?symbols=…`). Mirrors
|
|
59
|
+
* {@link LAZER_DEFAULTS}: infra WaterX operates, not part of the `waterx-config`
|
|
60
|
+
* JSON. Public read (no auth), so there is no api_key. `endpoint` has no
|
|
61
|
+
* trailing slash — the rule appends the path.
|
|
62
|
+
*
|
|
63
|
+
* These are DEFAULTS, not a hard pin: a consumer overrides them per client via
|
|
64
|
+
* `waterxEndpoint` / `waterxFetch` (resolved onto `client.waterx`).
|
|
65
|
+
*/
|
|
66
|
+
exports.WATERX_DEFAULTS = {
|
|
67
|
+
MAINNET: { endpoint: "https://quote-center.waterx.app" },
|
|
68
|
+
TESTNET: { endpoint: "https://quote-center-staging.waterx.app" },
|
|
69
|
+
};
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { SuiGrpcClient } from "@mysten/sui/grpc";
|
|
11
11
|
import type { Network } from "../constants.ts";
|
|
12
|
-
import type { OracleConfig, PythInfraConfig } from "./config.ts";
|
|
12
|
+
import type { OracleConfig, PythInfraConfig, WaterxInfraConfig } from "./config.ts";
|
|
13
13
|
import type { OracleSource } from "./price-update-rule.ts";
|
|
14
14
|
export interface OracleHost {
|
|
15
15
|
/** Sui network this client targets — selects per-network external-infra defaults (e.g. `LAZER_DEFAULTS`). */
|
|
@@ -18,6 +18,17 @@ export interface OracleHost {
|
|
|
18
18
|
readonly config: OracleConfig;
|
|
19
19
|
/** External Pyth/Wormhole/Hermes infra — fixed per `(network, generation)`; api_key/fetch layered from create options. */
|
|
20
20
|
readonly pyth: PythInfraConfig;
|
|
21
|
+
/**
|
|
22
|
+
* WaterX quote-center infra for `WaterxRule` — endpoint + fetch policy,
|
|
23
|
+
* resolved from the `waterxEndpoint` / `waterxFetch` create options.
|
|
24
|
+
*
|
|
25
|
+
* OPTIONAL so an existing host stays a valid `OracleHost`: when absent the
|
|
26
|
+
* rule falls back to `WATERX_DEFAULTS[network]`. This is the hook a browser
|
|
27
|
+
* consumer uses to route the quote-center fetch through a same-origin proxy
|
|
28
|
+
* (`endpoint`) or its own transport (`fetch.fetchImpl`) — that request is
|
|
29
|
+
* made from the page, so it is bound by the quote-center's CORS allowlist.
|
|
30
|
+
*/
|
|
31
|
+
readonly waterx?: WaterxInfraConfig;
|
|
21
32
|
/** gRPC client for the on-chain reads the Pyth update path needs. */
|
|
22
33
|
readonly grpcClient: SuiGrpcClient;
|
|
23
34
|
/**
|
|
@@ -25,6 +25,8 @@ export { PythCoreRule } from "./rules/pyth-core-rule.ts";
|
|
|
25
25
|
export type { PythCoreUpdatePayload } from "./rules/pyth-core-rule.ts";
|
|
26
26
|
export { PythLazerRule, LazerApiKeyMissingError } from "./rules/pyth-lazer-rule.ts";
|
|
27
27
|
export type { PythLazerUpdatePayload } from "./rules/pyth-lazer-rule.ts";
|
|
28
|
+
export { WaterxRule, parseSignedEnvelope } from "./rules/waterx-rule.ts";
|
|
29
|
+
export type { WaterxUpdatePayload, WaterxSignedEnvelope, WaterxBatchItem, } from "./rules/waterx-rule.ts";
|
|
28
30
|
export { OracleSourceNotImplementedError } from "./rule-registry.ts";
|
|
29
31
|
export { aggregateTicker, aggregateTickerWithPyth, aggregateTickerWithConstant, refreshOraclePrices, } from "./aggregate.ts";
|
|
30
32
|
export { openPythSponsorFund, reimbursePythSponsor } from "./rules/sponsor.ts";
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* rules that consume it are separate concerns.
|
|
18
18
|
*/
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.reimbursePythSponsor = exports.openPythSponsorFund = exports.refreshOraclePrices = exports.aggregateTickerWithConstant = exports.aggregateTickerWithPyth = exports.aggregateTicker = exports.OracleSourceNotImplementedError = exports.LazerApiKeyMissingError = exports.PythLazerRule = exports.PythCoreRule = exports.OracleFeeSourceUnavailableError = exports.MISSING_FEED_MEMO_TTL_MS = exports.HermesEndpointRejectedAllFeedsError = exports.updatePythPrices = exports.buildPythPriceUpdateCalls = exports.probeMissingFeeds = exports.endpointSupportedFeedIds = exports.fetchPriceFeedsUpdateData = exports.PythCache = exports.joinEndpointPath = exports.fetchWithPolicy = exports.FetchPolicyError = void 0;
|
|
20
|
+
exports.reimbursePythSponsor = exports.openPythSponsorFund = exports.refreshOraclePrices = exports.aggregateTickerWithConstant = exports.aggregateTickerWithPyth = exports.aggregateTicker = exports.OracleSourceNotImplementedError = exports.parseSignedEnvelope = exports.WaterxRule = exports.LazerApiKeyMissingError = exports.PythLazerRule = exports.PythCoreRule = exports.OracleFeeSourceUnavailableError = exports.MISSING_FEED_MEMO_TTL_MS = exports.HermesEndpointRejectedAllFeedsError = exports.updatePythPrices = exports.buildPythPriceUpdateCalls = exports.probeMissingFeeds = exports.endpointSupportedFeedIds = exports.fetchPriceFeedsUpdateData = exports.PythCache = exports.joinEndpointPath = exports.fetchWithPolicy = exports.FetchPolicyError = void 0;
|
|
21
21
|
// Shared fetch resilience wrapper — `FetchPolicyError` is re-exported (not
|
|
22
22
|
// just the type) so a consumer (e.g. a BE prefetch cache) can `instanceof`
|
|
23
23
|
// it off the failure `fetchPriceFeedsUpdateData` / `PythLazerRule` /
|
|
@@ -58,6 +58,11 @@ Object.defineProperty(exports, "PythCoreRule", { enumerable: true, get: function
|
|
|
58
58
|
var pyth_lazer_rule_ts_1 = require("./rules/pyth-lazer-rule.js");
|
|
59
59
|
Object.defineProperty(exports, "PythLazerRule", { enumerable: true, get: function () { return pyth_lazer_rule_ts_1.PythLazerRule; } });
|
|
60
60
|
Object.defineProperty(exports, "LazerApiKeyMissingError", { enumerable: true, get: function () { return pyth_lazer_rule_ts_1.LazerApiKeyMissingError; } });
|
|
61
|
+
// WaterX quote-center rule (first-party ed25519 signed batches; `feedWaterxRule`
|
|
62
|
+
// stays internal to `aggregate.ts`).
|
|
63
|
+
var waterx_rule_ts_1 = require("./rules/waterx-rule.js");
|
|
64
|
+
Object.defineProperty(exports, "WaterxRule", { enumerable: true, get: function () { return waterx_rule_ts_1.WaterxRule; } });
|
|
65
|
+
Object.defineProperty(exports, "parseSignedEnvelope", { enumerable: true, get: function () { return waterx_rule_ts_1.parseSignedEnvelope; } });
|
|
61
66
|
// `resolveOracleRule` (rule-registry.ts) is NOT re-exported here — it has one
|
|
62
67
|
// production consumer (`refreshOraclePrices` below) and no external caller;
|
|
63
68
|
// per repo convention ("no unused exports") it stays module-internal. Tests
|
|
@@ -24,11 +24,10 @@ export type PriceUpdateRuleKind = "pyth_rule" | "pyth_lazer_rule" | "supra_rule"
|
|
|
24
24
|
* create option (see `OracleHost.oracleSource`) — i.e. rules that can serve as
|
|
25
25
|
* the on-chain price *update* leg `refreshOraclePrices` runs before aggregating.
|
|
26
26
|
* `supra_rule` and `constant_rule` are auxiliary rules fed alongside whichever
|
|
27
|
-
* source is selected (see `aggregateTicker`), not sources themselves
|
|
28
|
-
* `
|
|
29
|
-
* reads `process.env` — consumers resolve their own env var to this type.
|
|
27
|
+
* source is selected (see `aggregateTicker`), not sources themselves. The SDK
|
|
28
|
+
* never reads `process.env` — consumers resolve their own env var to this type.
|
|
30
29
|
*/
|
|
31
|
-
export type OracleSource = "pyth_rule" | "pyth_lazer_rule";
|
|
30
|
+
export type OracleSource = "pyth_rule" | "pyth_lazer_rule" | "waterx_rule";
|
|
32
31
|
/**
|
|
33
32
|
* Off-chain payload fetched by a rule, tagged by `kind` so a caller holding
|
|
34
33
|
* several rules' results can tell them apart. `payload` is `unknown` here —
|
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
* option) — never by a config JSON `enabled` flag and never by `process.env`.
|
|
8
8
|
*
|
|
9
9
|
* Each source is self-contained: it owns its own infra + config and does NOT
|
|
10
|
-
* back-stop any other source.
|
|
11
|
-
* Hermes VAA)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
* back-stop any other source. Three are registered: `pyth_rule`
|
|
11
|
+
* (`PythCoreRule`, Hermes VAA), `pyth_lazer_rule` (`PythLazerRule`, Lazer
|
|
12
|
+
* signed updates), and `waterx_rule` (`WaterxRule`, first-party quote-center
|
|
13
|
+
* ed25519 signed batches). Resolving a source with no registered rule throws a
|
|
14
|
+
* clear `OracleSourceNotImplemented` error. There is deliberately no
|
|
15
|
+
* cross-source fallback and no client-creation config guard: selecting a source
|
|
16
|
+
* whose feeds are absent is not an error at init — it surfaces at tx-build time
|
|
17
|
+
* for the specific tickers that source can't serve (see `refreshOraclePrices`).
|
|
17
18
|
*/
|
|
18
19
|
import type { OracleSource, PriceUpdateRule } from "./price-update-rule.ts";
|
|
19
20
|
/**
|
|
@@ -8,19 +8,21 @@
|
|
|
8
8
|
* option) — never by a config JSON `enabled` flag and never by `process.env`.
|
|
9
9
|
*
|
|
10
10
|
* Each source is self-contained: it owns its own infra + config and does NOT
|
|
11
|
-
* back-stop any other source.
|
|
12
|
-
* Hermes VAA)
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
11
|
+
* back-stop any other source. Three are registered: `pyth_rule`
|
|
12
|
+
* (`PythCoreRule`, Hermes VAA), `pyth_lazer_rule` (`PythLazerRule`, Lazer
|
|
13
|
+
* signed updates), and `waterx_rule` (`WaterxRule`, first-party quote-center
|
|
14
|
+
* ed25519 signed batches). Resolving a source with no registered rule throws a
|
|
15
|
+
* clear `OracleSourceNotImplemented` error. There is deliberately no
|
|
16
|
+
* cross-source fallback and no client-creation config guard: selecting a source
|
|
17
|
+
* whose feeds are absent is not an error at init — it surfaces at tx-build time
|
|
18
|
+
* for the specific tickers that source can't serve (see `refreshOraclePrices`).
|
|
18
19
|
*/
|
|
19
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
21
|
exports.OracleSourceNotImplementedError = void 0;
|
|
21
22
|
exports.resolveOracleRule = resolveOracleRule;
|
|
22
23
|
const pyth_core_rule_ts_1 = require("./rules/pyth-core-rule.js");
|
|
23
24
|
const pyth_lazer_rule_ts_1 = require("./rules/pyth-lazer-rule.js");
|
|
25
|
+
const waterx_rule_ts_1 = require("./rules/waterx-rule.js");
|
|
24
26
|
/**
|
|
25
27
|
* Production registry. Frozen — tests inject a fake rule via
|
|
26
28
|
* `resolveOracleRule`'s `overrides` param instead of mutating this.
|
|
@@ -28,6 +30,7 @@ const pyth_lazer_rule_ts_1 = require("./rules/pyth-lazer-rule.js");
|
|
|
28
30
|
const DEFAULT_RULES = Object.freeze({
|
|
29
31
|
pyth_rule: pyth_core_rule_ts_1.PythCoreRule,
|
|
30
32
|
pyth_lazer_rule: pyth_lazer_rule_ts_1.PythLazerRule,
|
|
33
|
+
waterx_rule: waterx_rule_ts_1.WaterxRule,
|
|
31
34
|
});
|
|
32
35
|
/**
|
|
33
36
|
* Thrown by {@link resolveOracleRule} when `source` has no `PriceUpdateRule`
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `WaterxRule` — `PriceUpdateRule` for the first-party WaterX quote-center
|
|
3
|
+
* (Nautilus-TEE, ed25519), plus `feedWaterxRule`, the collector-feed leg
|
|
4
|
+
* `aggregateTicker` appends per waterx-routed ticker. Pulls one enclave-signed
|
|
5
|
+
* batch envelope covering every requested ticker from the quote-center
|
|
6
|
+
* (`GET /v1/quotes/update?symbols=…`, endpoint from `host.waterx` — the
|
|
7
|
+
* `waterxEndpoint`/`waterxFetch` create options — else `WATERX_DEFAULTS`), then —
|
|
8
|
+
* unlike Pyth Lazer, whose verify is a single shared PTB step — verifies AND
|
|
9
|
+
* feeds in ONE `waterx_rule::collect_batch_latest` call per collector (the Move
|
|
10
|
+
* API bundles the two). So `buildUpdateCalls` emits nothing and the signed
|
|
11
|
+
* envelope is handed straight to the per-ticker feed leg.
|
|
12
|
+
*
|
|
13
|
+
* `collect_batch_latest` is the dual-rule path: it feeds the item matching
|
|
14
|
+
* `collector.symbol()` WITHOUT aggregating, so a waterx-routed ticker composes
|
|
15
|
+
* onto the same collector as Pyth/Supra (compose-then-aggregate). On-chain a
|
|
16
|
+
* freshness miss / replayed timestamp ABSTAINS (the other weighted rules
|
|
17
|
+
* cover); a config/integrity mismatch or bad signature aborts.
|
|
18
|
+
*/
|
|
19
|
+
import type { Transaction, TransactionArgument } from "@mysten/sui/transactions";
|
|
20
|
+
import type { OracleHost } from "../host.ts";
|
|
21
|
+
import { type PriceUpdateRule, type RuleUpdateData } from "../price-update-rule.ts";
|
|
22
|
+
/**
|
|
23
|
+
* One item inside a signed batch payload, mirroring the quote-center
|
|
24
|
+
* `/v1/quotes/update` JSON 1:1 (snake_case). The u64 integer fields are the
|
|
25
|
+
* EXACT values the enclave signed over BCS — `collect_batch_latest` rebuilds
|
|
26
|
+
* the payload on-chain and re-verifies, so they must round-trip byte-for-byte.
|
|
27
|
+
* They are `bigint` (not `number`): {@link parseSignedEnvelope} decodes them
|
|
28
|
+
* from the raw JSON integer literals so a value above `Number.MAX_SAFE_INTEGER`
|
|
29
|
+
* (2^53) can never lose precision and silently abort the on-chain signature
|
|
30
|
+
* check. `num_sources` is a `u8` (≤ 255) and stays a `number`.
|
|
31
|
+
*/
|
|
32
|
+
export interface WaterxBatchItem {
|
|
33
|
+
symbol: string;
|
|
34
|
+
ticker: string;
|
|
35
|
+
sources: bigint[];
|
|
36
|
+
method: string;
|
|
37
|
+
price_timestamp_ms: bigint;
|
|
38
|
+
price_n: bigint;
|
|
39
|
+
price_scale: bigint;
|
|
40
|
+
confidence_n: bigint;
|
|
41
|
+
confidence_scale: bigint;
|
|
42
|
+
max_source_deviation_bps: bigint;
|
|
43
|
+
num_sources: number;
|
|
44
|
+
}
|
|
45
|
+
/** The enclave-signed batch envelope from `GET /v1/quotes/update`. */
|
|
46
|
+
export interface WaterxSignedEnvelope {
|
|
47
|
+
intent: number;
|
|
48
|
+
/** Enclave signing timestamp (ms) — the on-chain `timestamp_ms` argument. */
|
|
49
|
+
timestamp_ms: bigint;
|
|
50
|
+
payload: {
|
|
51
|
+
items: WaterxBatchItem[];
|
|
52
|
+
};
|
|
53
|
+
/** ed25519 signature over `BCS(IntentMessage<BatchPricePayload>)`, hex (± `0x`). */
|
|
54
|
+
signature: string;
|
|
55
|
+
}
|
|
56
|
+
/** `waterx_rule`'s narrowed `RuleUpdateData.payload` shape. */
|
|
57
|
+
export interface WaterxUpdatePayload {
|
|
58
|
+
readonly envelope: WaterxSignedEnvelope;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Parse a quote-center `/v1/quotes/update` response body into a
|
|
62
|
+
* {@link WaterxSignedEnvelope} with the u64 fields decoded as `bigint`, exact.
|
|
63
|
+
*
|
|
64
|
+
* The signature is over `BCS(IntentMessage<BatchPricePayload>)`, so every u64
|
|
65
|
+
* the SDK rebuilds in-PTB must equal the enclave's byte-for-byte or
|
|
66
|
+
* `collect_batch_latest` aborts the whole trade PTB (bad signature — not an
|
|
67
|
+
* abstain). A plain `JSON.parse` yields IEEE-754 doubles that lose precision
|
|
68
|
+
* above 2^53, so instead we recover each integer's exact source literal via the
|
|
69
|
+
* ES2023 reviver `context.source` (Node 21+ / modern browsers) and `BigInt()`
|
|
70
|
+
* it. On an older runtime that passes no `context`, a value within 2^53 is
|
|
71
|
+
* still exact (`BigInt(number)`); a value ABOVE it throws loudly here rather
|
|
72
|
+
* than silently corrupting the payload into an on-chain abort. `num_sources`
|
|
73
|
+
* (u8) and `intent` are coerced back to `number` — both are tiny.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseSignedEnvelope(text: string): WaterxSignedEnvelope;
|
|
76
|
+
/** Narrow a `RuleUpdateData` to its `WaterxSignedEnvelope`, or `null`. */
|
|
77
|
+
export declare function waterxEnvelopeOf(data: RuleUpdateData): WaterxSignedEnvelope | null;
|
|
78
|
+
/**
|
|
79
|
+
* `waterx_rule::collect_batch_latest(collector, config, clock, enclave_config,
|
|
80
|
+
* enclave, timestamp_ms, payload, sig)` — rebuild the enclave-signed batch
|
|
81
|
+
* payload in-PTB (`new_batch_payload` + one `new_batch_item`/`push_batch_item`
|
|
82
|
+
* per item, the exact shape the enclave signed) and contribute the price for
|
|
83
|
+
* `collector.symbol()` to the collector. One collect call re-verifies the batch
|
|
84
|
+
* signature and picks this collector's symbol out of the batch; on-chain it
|
|
85
|
+
* abstains (records `none`) instead of aborting when the symbol is stale,
|
|
86
|
+
* absent from the batch, or its timestamp was already accepted (replay).
|
|
87
|
+
*/
|
|
88
|
+
export declare function feedWaterxRule(tx: Transaction, host: OracleHost, collector: TransactionArgument, envelope: WaterxSignedEnvelope): void;
|
|
89
|
+
export declare const WaterxRule: PriceUpdateRule;
|