@waterx/sdk 4.3.2 → 5.0.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/.claude/skills/waterx-sdk-integration/SKILL.md +225 -0
- package/README.md +216 -45
- package/SKILLS.md +34 -0
- package/dist/cjs/src/account/config.d.ts +3 -5
- package/dist/cjs/src/account/funding/wormhole.d.ts +1 -2
- package/dist/cjs/src/account/funding/wormhole.js +1 -2
- package/dist/cjs/src/generated/waterx_rule/waterx_rule.d.ts +140 -7
- package/dist/cjs/src/generated/waterx_rule/waterx_rule.js +163 -8
- package/dist/cjs/src/oracle/aggregate.d.ts +49 -62
- package/dist/cjs/src/oracle/aggregate.js +208 -164
- package/dist/cjs/src/oracle/config.d.ts +38 -51
- package/dist/cjs/src/oracle/config.js +1 -1
- package/dist/cjs/src/oracle/host.d.ts +14 -19
- package/dist/cjs/src/oracle/host.js +3 -3
- package/dist/cjs/src/oracle/index.d.ts +25 -16
- package/dist/cjs/src/oracle/index.js +87 -69
- package/dist/cjs/src/oracle/price-update-rule.d.ts +104 -66
- package/dist/cjs/src/oracle/price-update-rule.js +24 -16
- package/dist/cjs/src/oracle/pyth-pro-history.d.ts +36 -0
- package/dist/cjs/src/oracle/pyth-pro-history.js +41 -0
- package/dist/cjs/src/oracle/read-plane.d.ts +35 -53
- package/dist/cjs/src/oracle/read-plane.js +26 -48
- package/dist/cjs/src/oracle/read-prices.d.ts +105 -0
- package/dist/cjs/src/oracle/read-prices.js +162 -0
- package/dist/cjs/src/oracle/rule-registry.d.ts +12 -12
- package/dist/cjs/src/oracle/rule-registry.js +12 -14
- package/dist/cjs/src/oracle/rules/pyth-lazer-rule.d.ts +24 -3
- package/dist/cjs/src/oracle/rules/pyth-lazer-rule.js +40 -20
- package/dist/cjs/src/oracle/rules/waterx-rule.d.ts +271 -57
- package/dist/cjs/src/oracle/rules/waterx-rule.js +743 -144
- package/dist/cjs/src/oracle/schedule.d.ts +109 -0
- package/dist/cjs/src/oracle/schedule.js +991 -0
- package/dist/cjs/src/oracle/source-list.d.ts +31 -27
- package/dist/cjs/src/oracle/source-list.js +43 -45
- package/dist/cjs/src/oracle/symbol-catalog.d.ts +54 -0
- package/dist/cjs/src/oracle/symbol-catalog.js +69 -0
- package/dist/cjs/src/oracle/update-fetch.d.ts +11 -15
- package/dist/cjs/src/oracle/update-fetch.js +12 -19
- package/dist/cjs/src/oracle/validate.d.ts +105 -0
- package/dist/cjs/src/oracle/validate.js +185 -0
- package/dist/cjs/src/oracle/weight-coverage.d.ts +79 -0
- package/dist/cjs/src/oracle/weight-coverage.js +173 -0
- package/dist/cjs/src/perp/client.d.ts +40 -55
- package/dist/cjs/src/perp/client.js +44 -17
- package/dist/cjs/src/perp/config-view.d.ts +5 -14
- package/dist/cjs/src/perp/config-view.js +5 -16
- package/dist/cjs/src/perp/config.d.ts +3 -3
- package/dist/cjs/src/perp/config.js +11 -13
- package/dist/cjs/src/perp/index.d.ts +4 -6
- package/dist/cjs/src/perp/index.js +43 -18
- package/dist/cjs/src/perp/tx-builders/common.d.ts +86 -48
- package/dist/cjs/src/perp/tx-builders/common.js +115 -47
- package/dist/cjs/src/perp/tx-builders/wlp.d.ts +3 -14
- package/dist/cjs/src/perp/tx-builders/wlp.js +6 -35
- package/dist/cjs/src/perp/tx-builders.d.ts +6 -5
- package/dist/cjs/src/perp/tx-builders.js +16 -10
- package/dist/cjs/src/unified-client.d.ts +9 -35
- package/dist/cjs/src/unified-client.js +3 -4
- package/dist/cjs/src/utils/config.d.ts +0 -8
- package/dist/cjs/src/utils/config.js +5 -13
- package/dist/src/account/config.d.ts +3 -5
- package/dist/src/account/funding/wormhole.d.ts +1 -2
- package/dist/src/account/funding/wormhole.js +1 -2
- package/dist/src/generated/waterx_rule/waterx_rule.d.ts +140 -7
- package/dist/src/generated/waterx_rule/waterx_rule.js +151 -7
- package/dist/src/oracle/aggregate.d.ts +49 -62
- package/dist/src/oracle/aggregate.js +209 -164
- package/dist/src/oracle/config.d.ts +38 -51
- package/dist/src/oracle/config.js +1 -1
- package/dist/src/oracle/host.d.ts +14 -19
- package/dist/src/oracle/host.js +3 -3
- package/dist/src/oracle/index.d.ts +25 -16
- package/dist/src/oracle/index.js +61 -54
- package/dist/src/oracle/price-update-rule.d.ts +104 -66
- package/dist/src/oracle/price-update-rule.js +23 -16
- package/dist/src/oracle/pyth-pro-history.d.ts +36 -0
- package/dist/src/oracle/pyth-pro-history.js +38 -0
- package/dist/src/oracle/read-plane.d.ts +35 -53
- package/dist/src/oracle/read-plane.js +25 -47
- package/dist/src/oracle/read-prices.d.ts +105 -0
- package/dist/src/oracle/read-prices.js +156 -0
- package/dist/src/oracle/rule-registry.d.ts +12 -12
- package/dist/src/oracle/rule-registry.js +12 -14
- package/dist/src/oracle/rules/pyth-lazer-rule.d.ts +24 -3
- package/dist/src/oracle/rules/pyth-lazer-rule.js +39 -20
- package/dist/src/oracle/rules/waterx-rule.d.ts +271 -57
- package/dist/src/oracle/rules/waterx-rule.js +735 -144
- package/dist/src/oracle/schedule.d.ts +109 -0
- package/dist/src/oracle/schedule.js +985 -0
- package/dist/src/oracle/source-list.d.ts +31 -27
- package/dist/src/oracle/source-list.js +42 -43
- package/dist/src/oracle/symbol-catalog.d.ts +54 -0
- package/dist/src/oracle/symbol-catalog.js +65 -0
- package/dist/src/oracle/update-fetch.d.ts +11 -15
- package/dist/src/oracle/update-fetch.js +12 -18
- package/dist/src/oracle/validate.d.ts +105 -0
- package/dist/src/oracle/validate.js +177 -0
- package/dist/src/oracle/weight-coverage.d.ts +79 -0
- package/dist/src/oracle/weight-coverage.js +166 -0
- package/dist/src/perp/client.d.ts +40 -55
- package/dist/src/perp/client.js +45 -18
- package/dist/src/perp/config-view.d.ts +5 -14
- package/dist/src/perp/config-view.js +5 -16
- package/dist/src/perp/config.d.ts +3 -3
- package/dist/src/perp/config.js +11 -13
- package/dist/src/perp/index.d.ts +4 -6
- package/dist/src/perp/index.js +17 -5
- package/dist/src/perp/tx-builders/common.d.ts +86 -48
- package/dist/src/perp/tx-builders/common.js +114 -48
- package/dist/src/perp/tx-builders/wlp.d.ts +3 -14
- package/dist/src/perp/tx-builders/wlp.js +6 -35
- package/dist/src/perp/tx-builders.d.ts +6 -5
- package/dist/src/perp/tx-builders.js +15 -6
- package/dist/src/unified-client.d.ts +9 -35
- package/dist/src/unified-client.js +3 -4
- package/dist/src/utils/config.d.ts +0 -8
- package/dist/src/utils/config.js +5 -12
- package/package.json +8 -2
- package/dist/cjs/src/generated/pyth_sponsor_rule/deps/sui/balance.d.ts +0 -12
- package/dist/cjs/src/generated/pyth_sponsor_rule/deps/sui/balance.js +0 -17
- package/dist/cjs/src/generated/pyth_sponsor_rule/pyth_sponsor_rule.d.ts +0 -68
- package/dist/cjs/src/generated/pyth_sponsor_rule/pyth_sponsor_rule.js +0 -113
- package/dist/cjs/src/generated/waterx_pyth_rule/deps/sui/vec_map.d.ts +0 -36
- package/dist/cjs/src/generated/waterx_pyth_rule/deps/sui/vec_map.js +0 -31
- package/dist/cjs/src/generated/waterx_pyth_rule/pyth_rule.d.ts +0 -192
- package/dist/cjs/src/generated/waterx_pyth_rule/pyth_rule.js +0 -214
- package/dist/cjs/src/oracle/pyth.d.ts +0 -227
- package/dist/cjs/src/oracle/pyth.js +0 -652
- package/dist/cjs/src/oracle/rules/pyth-core-rule.d.ts +0 -15
- package/dist/cjs/src/oracle/rules/pyth-core-rule.js +0 -100
- package/dist/cjs/src/oracle/rules/pyth-rule.d.ts +0 -11
- package/dist/cjs/src/oracle/rules/pyth-rule.js +0 -29
- package/dist/cjs/src/oracle/rules/sponsor.d.ts +0 -32
- package/dist/cjs/src/oracle/rules/sponsor.js +0 -56
- package/dist/src/generated/pyth_sponsor_rule/deps/sui/balance.d.ts +0 -12
- package/dist/src/generated/pyth_sponsor_rule/deps/sui/balance.js +0 -14
- package/dist/src/generated/pyth_sponsor_rule/pyth_sponsor_rule.d.ts +0 -68
- package/dist/src/generated/pyth_sponsor_rule/pyth_sponsor_rule.js +0 -73
- package/dist/src/generated/waterx_pyth_rule/deps/sui/vec_map.d.ts +0 -36
- package/dist/src/generated/waterx_pyth_rule/deps/sui/vec_map.js +0 -27
- package/dist/src/generated/waterx_pyth_rule/pyth_rule.d.ts +0 -192
- package/dist/src/generated/waterx_pyth_rule/pyth_rule.js +0 -170
- package/dist/src/oracle/pyth.d.ts +0 -227
- package/dist/src/oracle/pyth.js +0 -638
- package/dist/src/oracle/rules/pyth-core-rule.d.ts +0 -15
- package/dist/src/oracle/rules/pyth-core-rule.js +0 -97
- package/dist/src/oracle/rules/pyth-rule.d.ts +0 -11
- package/dist/src/oracle/rules/pyth-rule.js +0 -26
- package/dist/src/oracle/rules/sponsor.d.ts +0 -32
- package/dist/src/oracle/rules/sponsor.js +0 -52
|
@@ -1,36 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `source-list.ts` — THE
|
|
3
|
-
* (comma list of `OracleSource` values → the fed set). The FE and BE
|
|
4
|
-
* previously carried twin hand-written parsers whose semantics drifted once
|
|
5
|
-
* in review (a trailing comma booted one deployment green and 500'd the
|
|
6
|
-
* other); this canonical behavior is what both fold onto:
|
|
2
|
+
* `source-list.ts` — THE fed set, derived from the deployment config.
|
|
7
3
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* the SDK's own fed-set build treats the list as a set)
|
|
13
|
-
* - throw an operator-actionable error on empty/unset/invalid input —
|
|
14
|
-
* there is NO default oracle source
|
|
4
|
+
* There is no `oracleSource` option and no `ORACLE_SOURCE` env var. Which
|
|
5
|
+
* sources a build feeds is a property of the DEPLOYMENT, so it is read from
|
|
6
|
+
* the same canonical JSON that carries their packages and feeds: a source is
|
|
7
|
+
* in the fed set when its block is published AND carries at least one feed.
|
|
15
8
|
*
|
|
16
|
-
*
|
|
9
|
+
* Why derived rather than declared. The chain arbitrates — per-ticker weights
|
|
10
|
+
* decide which contributions count, feeding an UNWEIGHTED rule is dropped
|
|
11
|
+
* on-chain, and starving a WEIGHTED one aborts `EMissingPriceSource`. The
|
|
12
|
+
* failure is therefore one-sided: over-feeding is free, under-feeding is fatal.
|
|
13
|
+
* A hand-typed list errs in the fatal direction (the classic being one copied
|
|
14
|
+
* between networks, naming a source that deployment does not carry); the
|
|
15
|
+
* config cannot, because it IS what wires the rules. Mainnet derives
|
|
16
|
+
* `[pyth_lazer_rule, waterx_rule]` and testnet `[waterx_rule]` with no
|
|
17
|
+
* per-deployment configuration at all.
|
|
17
18
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Retired rules are inert here by construction: `pyth_rule` and
|
|
20
|
+
* `pyth_sponsor_rule` still sit in the live configs, but neither is an
|
|
21
|
+
* {@link ORACLE_SOURCES} member — there is no rule module that could feed one
|
|
22
|
+
* — so their blocks are never consulted.
|
|
21
23
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* Deliberately NOT filtered by which credentials the caller holds. A keyless
|
|
25
|
+
* client whose config wires Lazer fails loudly at build
|
|
26
|
+
* (`LazerApiKeyMissing`); silently dropping the source instead would starve a
|
|
27
|
+
* rule the chain may weight and turn a clear build error into an opaque
|
|
28
|
+
* on-chain abort.
|
|
27
29
|
*/
|
|
30
|
+
import type { OracleConfig } from "./config.ts";
|
|
28
31
|
import { type OracleSource } from "./price-update-rule.ts";
|
|
29
32
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
33
|
+
* The fed set this deployment wires: every implementable source with a
|
|
34
|
+
* published package and a non-empty feeds map, in {@link ORACLE_SOURCES}
|
|
35
|
+
* order.
|
|
36
|
+
*
|
|
37
|
+
* Pure and config-only, so consumers can call it before a client exists (e.g.
|
|
38
|
+
* to pair with {@link missingOracleCredentials} in a boot assert).
|
|
34
39
|
*/
|
|
35
|
-
export declare function
|
|
36
|
-
export declare function parseOracleSourceList(raw: string | null | undefined): OracleSource[];
|
|
40
|
+
export declare function deriveOracleSources(config: OracleConfig): OracleSource[];
|
|
@@ -1,57 +1,55 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* `source-list.ts` — THE
|
|
4
|
-
* (comma list of `OracleSource` values → the fed set). The FE and BE
|
|
5
|
-
* previously carried twin hand-written parsers whose semantics drifted once
|
|
6
|
-
* in review (a trailing comma booted one deployment green and 500'd the
|
|
7
|
-
* other); this canonical behavior is what both fold onto:
|
|
3
|
+
* `source-list.ts` — THE fed set, derived from the deployment config.
|
|
8
4
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* the SDK's own fed-set build treats the list as a set)
|
|
14
|
-
* - throw an operator-actionable error on empty/unset/invalid input —
|
|
15
|
-
* there is NO default oracle source
|
|
5
|
+
* There is no `oracleSource` option and no `ORACLE_SOURCE` env var. Which
|
|
6
|
+
* sources a build feeds is a property of the DEPLOYMENT, so it is read from
|
|
7
|
+
* the same canonical JSON that carries their packages and feeds: a source is
|
|
8
|
+
* in the fed set when its block is published AND carries at least one feed.
|
|
16
9
|
*
|
|
17
|
-
*
|
|
10
|
+
* Why derived rather than declared. The chain arbitrates — per-ticker weights
|
|
11
|
+
* decide which contributions count, feeding an UNWEIGHTED rule is dropped
|
|
12
|
+
* on-chain, and starving a WEIGHTED one aborts `EMissingPriceSource`. The
|
|
13
|
+
* failure is therefore one-sided: over-feeding is free, under-feeding is fatal.
|
|
14
|
+
* A hand-typed list errs in the fatal direction (the classic being one copied
|
|
15
|
+
* between networks, naming a source that deployment does not carry); the
|
|
16
|
+
* config cannot, because it IS what wires the rules. Mainnet derives
|
|
17
|
+
* `[pyth_lazer_rule, waterx_rule]` and testnet `[waterx_rule]` with no
|
|
18
|
+
* per-deployment configuration at all.
|
|
18
19
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* Retired rules are inert here by construction: `pyth_rule` and
|
|
21
|
+
* `pyth_sponsor_rule` still sit in the live configs, but neither is an
|
|
22
|
+
* {@link ORACLE_SOURCES} member — there is no rule module that could feed one
|
|
23
|
+
* — so their blocks are never consulted.
|
|
22
24
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* Deliberately NOT filtered by which credentials the caller holds. A keyless
|
|
26
|
+
* client whose config wires Lazer fails loudly at build
|
|
27
|
+
* (`LazerApiKeyMissing`); silently dropping the source instead would starve a
|
|
28
|
+
* rule the chain may weight and turn a clear build error into an opaque
|
|
29
|
+
* on-chain abort.
|
|
28
30
|
*/
|
|
29
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.
|
|
31
|
-
exports.parseOracleSourceList = parseOracleSourceList;
|
|
32
|
+
exports.deriveOracleSources = deriveOracleSources;
|
|
32
33
|
const price_update_rule_ts_1 = require("./price-update-rule.js");
|
|
33
|
-
// Widened-annotation Set (not an assertion) so the type predicate below
|
|
34
|
-
// narrows by CONSTRUCTION rather than by cast.
|
|
35
|
-
const ORACLE_SOURCE_SET = new Set(price_update_rule_ts_1.ORACLE_SOURCES);
|
|
36
34
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
35
|
+
* The fed set this deployment wires: every implementable source with a
|
|
36
|
+
* published package and a non-empty feeds map, in {@link ORACLE_SOURCES}
|
|
37
|
+
* order.
|
|
38
|
+
*
|
|
39
|
+
* Pure and config-only, so consumers can call it before a client exists (e.g.
|
|
40
|
+
* to pair with {@link missingOracleCredentials} in a boot assert).
|
|
41
41
|
*/
|
|
42
|
-
function
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
return [...new Set(sources)];
|
|
42
|
+
function deriveOracleSources(config) {
|
|
43
|
+
return price_update_rule_ts_1.ORACLE_SOURCES.filter((source) => {
|
|
44
|
+
const block = config.packages[source];
|
|
45
|
+
if (!block?.published_at || Object.keys(block.feeds ?? {}).length === 0)
|
|
46
|
+
return false;
|
|
47
|
+
// `enabled` is the one lever the schema offers for switching a source off,
|
|
48
|
+
// and with routing derived from config it is the ONLY lever left — so it
|
|
49
|
+
// is honoured here. Absent means ON: every live config omits it, and a
|
|
50
|
+
// published block with feeds is a wired source. (`supra_rule` is
|
|
51
|
+
// default-OFF and requires an explicit `true` — it is an auxiliary leg,
|
|
52
|
+
// not a source, so the asymmetry is deliberate.)
|
|
53
|
+
return block.enabled !== false;
|
|
54
|
+
});
|
|
57
55
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `symbol-catalog.ts` — the Pyth Pro symbol catalog (`GET /v1/symbols`):
|
|
3
|
+
* every Lazer feed's integer id, its legacy Hermes hex id, its
|
|
4
|
+
* fully-qualified reference symbol (`Crypto.BTC/USD`, `Equity.US.AAPL/USD`),
|
|
5
|
+
* its `schedule` string (same grammar `parsePythSchedule` implements), asset
|
|
6
|
+
* type, state, and fastest channel. This replaces the retired Hermes
|
|
7
|
+
* `/v2/price_feeds` catalog for BOTH of its jobs: the schedule catalog for
|
|
8
|
+
* market hours AND the hex↔integer feed-id map.
|
|
9
|
+
*
|
|
10
|
+
* KEYLESS — the catalog read requires no Bearer (probed 2026-08-19), so
|
|
11
|
+
* schedule consumers need no credential. It is also BIG (~4.6MB, ~3.6k
|
|
12
|
+
* records), hence the generous default timeout; consumers cache the result
|
|
13
|
+
* (BE service interval-refreshes; FE route caches) rather than fetch per
|
|
14
|
+
* request.
|
|
15
|
+
*/
|
|
16
|
+
import { type FetchPolicy } from "./update-fetch.ts";
|
|
17
|
+
/**
|
|
18
|
+
* The Pyth Pro API base every Pro read surface hangs off — the catalog here
|
|
19
|
+
* and the chart history in `pyth-pro-history.ts`, which imports this. ONE
|
|
20
|
+
* declaration, mirroring the rule-owned `LAZER_INFRA` / `WATERX_INFRA`
|
|
21
|
+
* pattern: a Pro host change is a single edit.
|
|
22
|
+
*/
|
|
23
|
+
export declare const PYTH_PRO_API_ENDPOINT = "https://pyth.dourolabs.app";
|
|
24
|
+
/**
|
|
25
|
+
* One `/v1/symbols` record (the fields consumers key off; extra wire fields
|
|
26
|
+
* are dropped, not preserved).
|
|
27
|
+
*/
|
|
28
|
+
export type PythSymbolRecord = {
|
|
29
|
+
/** Integer Lazer feed id — the `pyth_lazer_rule.feeds` id scheme. */
|
|
30
|
+
pyth_lazer_id: number;
|
|
31
|
+
/** Legacy Hermes hex feed id, or null for Lazer-only feeds. */
|
|
32
|
+
hermes_id: string | null;
|
|
33
|
+
/** Fully-qualified reference symbol, e.g. `Crypto.BTC/USD` — the join key consumers map tickers onto. */
|
|
34
|
+
symbol: string;
|
|
35
|
+
/** Pyth market-hours grammar string (absent for some 24/7 feeds). */
|
|
36
|
+
schedule?: string;
|
|
37
|
+
/** e.g. `"crypto"`, `"equity"`, `"fx"`, `"metal"`. */
|
|
38
|
+
asset_type: string;
|
|
39
|
+
/** Feed lifecycle state (e.g. `"stable"`). */
|
|
40
|
+
state: string;
|
|
41
|
+
/** Fastest channel the feed publishes (e.g. `"real_time"`, `"fixed_rate@200ms"`). */
|
|
42
|
+
min_channel?: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Fetch the full symbol catalog. Returns records with a numeric
|
|
46
|
+
* `pyth_lazer_id` and a string `symbol`; other fields pass through as-is
|
|
47
|
+
* (missing → undefined/null per the type). Throws on non-2xx or a non-array
|
|
48
|
+
* body. `opts.fetch` overrides the retry/timeout policy (and `fetchImpl` for
|
|
49
|
+
* tests); the default budget is deliberately generous — see the module
|
|
50
|
+
* header.
|
|
51
|
+
*/
|
|
52
|
+
export declare function fetchPythSymbolCatalog(opts?: {
|
|
53
|
+
fetch?: FetchPolicy;
|
|
54
|
+
}): Promise<PythSymbolRecord[]>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `symbol-catalog.ts` — the Pyth Pro symbol catalog (`GET /v1/symbols`):
|
|
4
|
+
* every Lazer feed's integer id, its legacy Hermes hex id, its
|
|
5
|
+
* fully-qualified reference symbol (`Crypto.BTC/USD`, `Equity.US.AAPL/USD`),
|
|
6
|
+
* its `schedule` string (same grammar `parsePythSchedule` implements), asset
|
|
7
|
+
* type, state, and fastest channel. This replaces the retired Hermes
|
|
8
|
+
* `/v2/price_feeds` catalog for BOTH of its jobs: the schedule catalog for
|
|
9
|
+
* market hours AND the hex↔integer feed-id map.
|
|
10
|
+
*
|
|
11
|
+
* KEYLESS — the catalog read requires no Bearer (probed 2026-08-19), so
|
|
12
|
+
* schedule consumers need no credential. It is also BIG (~4.6MB, ~3.6k
|
|
13
|
+
* records), hence the generous default timeout; consumers cache the result
|
|
14
|
+
* (BE service interval-refreshes; FE route caches) rather than fetch per
|
|
15
|
+
* request.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.PYTH_PRO_API_ENDPOINT = void 0;
|
|
19
|
+
exports.fetchPythSymbolCatalog = fetchPythSymbolCatalog;
|
|
20
|
+
const update_fetch_ts_1 = require("./update-fetch.js");
|
|
21
|
+
/**
|
|
22
|
+
* The Pyth Pro API base every Pro read surface hangs off — the catalog here
|
|
23
|
+
* and the chart history in `pyth-pro-history.ts`, which imports this. ONE
|
|
24
|
+
* declaration, mirroring the rule-owned `LAZER_INFRA` / `WATERX_INFRA`
|
|
25
|
+
* pattern: a Pro host change is a single edit.
|
|
26
|
+
*/
|
|
27
|
+
exports.PYTH_PRO_API_ENDPOINT = "https://pyth.dourolabs.app";
|
|
28
|
+
/** ~4.6MB body — a money-path 15s budget is too tight on slow links. */
|
|
29
|
+
const CATALOG_TIMEOUT_MS = 60_000;
|
|
30
|
+
/**
|
|
31
|
+
* Fetch the full symbol catalog. Returns records with a numeric
|
|
32
|
+
* `pyth_lazer_id` and a string `symbol`; other fields pass through as-is
|
|
33
|
+
* (missing → undefined/null per the type). Throws on non-2xx or a non-array
|
|
34
|
+
* body. `opts.fetch` overrides the retry/timeout policy (and `fetchImpl` for
|
|
35
|
+
* tests); the default budget is deliberately generous — see the module
|
|
36
|
+
* header.
|
|
37
|
+
*/
|
|
38
|
+
async function fetchPythSymbolCatalog(opts) {
|
|
39
|
+
const url = (0, update_fetch_ts_1.joinEndpointPath)(exports.PYTH_PRO_API_ENDPOINT, "v1/symbols");
|
|
40
|
+
// Same ordering hazard as the history reader: spreading the caller's policy
|
|
41
|
+
// over the default let `{ timeoutMs: undefined }` revert the deliberately
|
|
42
|
+
// generous catalog budget to the 15s money-path default, which this ~4.6MB
|
|
43
|
+
// body does not fit. An explicit value still wins; absence does not.
|
|
44
|
+
const res = await (0, update_fetch_ts_1.fetchWithPolicy)(url.toString(), {}, { ...opts?.fetch, timeoutMs: opts?.fetch?.timeoutMs ?? CATALOG_TIMEOUT_MS });
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
throw new Error(`Pyth symbol catalog fetch failed: ${res.status} ${await res.text()}`);
|
|
47
|
+
}
|
|
48
|
+
const json = (await res.json());
|
|
49
|
+
if (!Array.isArray(json)) {
|
|
50
|
+
throw new Error("Pyth symbol catalog returned a non-array body");
|
|
51
|
+
}
|
|
52
|
+
const records = [];
|
|
53
|
+
for (const raw of json) {
|
|
54
|
+
// Tolerant per-record gate: one malformed row must not sink the ~3.6k
|
|
55
|
+
// others (mirrors the schedule parser's skip-a-bad-feed posture).
|
|
56
|
+
if (typeof raw?.pyth_lazer_id !== "number" || typeof raw.symbol !== "string")
|
|
57
|
+
continue;
|
|
58
|
+
records.push({
|
|
59
|
+
pyth_lazer_id: raw.pyth_lazer_id,
|
|
60
|
+
hermes_id: typeof raw.hermes_id === "string" ? raw.hermes_id : null,
|
|
61
|
+
symbol: raw.symbol,
|
|
62
|
+
...(typeof raw.schedule === "string" ? { schedule: raw.schedule } : {}),
|
|
63
|
+
asset_type: typeof raw.asset_type === "string" ? raw.asset_type : "",
|
|
64
|
+
state: typeof raw.state === "string" ? raw.state : "",
|
|
65
|
+
...(typeof raw.min_channel === "string" ? { min_channel: raw.min_channel } : {}),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return records;
|
|
69
|
+
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `fetchWithPolicy` — resilience wrapper around `fetch` for the oracle money
|
|
3
3
|
* path's off-chain update-data fetches. Every order/position/collateral
|
|
4
|
-
* tx-build depends on one of these REST calls landing (
|
|
5
|
-
* `
|
|
6
|
-
* with a single attempt and no retry means one
|
|
7
|
-
* fails every trade.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* implemented for these fetches.
|
|
4
|
+
* tx-build depends on one of these REST calls landing (the Lazer signed
|
|
5
|
+
* update for `pyth_lazer_rule`, the quote-center pull for `waterx_rule`); a
|
|
6
|
+
* bare `fetch` with a single attempt and no retry means one upstream 429/5xx
|
|
7
|
+
* or timeout fails every trade. Those two rule fetches, the read executors
|
|
8
|
+
* (`./read-prices.ts`), the Pyth Pro catalog/history readers, and `loadConfig`
|
|
9
|
+
* (`../perp/config.ts`) all delegate here instead of calling `fetch` directly
|
|
10
|
+
* — this is the ONE place a retry/timeout/auth policy is implemented.
|
|
12
11
|
*
|
|
13
12
|
* Policy semantics:
|
|
14
13
|
* - Bearer auth is attached iff `policy.apiKey` is a non-empty string —
|
|
@@ -96,14 +95,11 @@ export declare function rethrowExhaustedFetch(err: unknown, describe: (err: Fetc
|
|
|
96
95
|
* Join an API `path` onto an `endpoint` PRESERVING the endpoint's own base
|
|
97
96
|
* path. `new URL(path, endpoint)` is the footgun this replaces: a
|
|
98
97
|
* leading-slash path is *absolute* and silently discards the endpoint's path
|
|
99
|
-
* — harmless for a bare-origin endpoint
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* targets `<endpoint><fixed path>` must build its URL here.
|
|
98
|
+
* — harmless for a bare-origin endpoint, but it is exactly what a consumer's
|
|
99
|
+
* `waterxEndpoint` proxy route (`https://app.example/api/quote-center`) is
|
|
100
|
+
* made of, and dropping it silently bypasses the proxy. Every oracle fetch
|
|
101
|
+
* that targets `<endpoint><fixed path>` must build its URL here.
|
|
103
102
|
*/
|
|
104
|
-
/** One canonical trailing-slash trim — `joinEndpointPath` (URL building) and
|
|
105
|
-
* `pyth.ts`'s `memoKey` (endpoint identity) must never drift apart on it. */
|
|
106
|
-
export declare function trimTrailingSlashes(endpoint: string): string;
|
|
107
103
|
export declare function joinEndpointPath(endpoint: string, path: string): URL;
|
|
108
104
|
/**
|
|
109
105
|
* `fetch` with per-attempt timeout, bounded retry + backoff, and optional
|
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* `fetchWithPolicy` — resilience wrapper around `fetch` for the oracle money
|
|
4
4
|
* path's off-chain update-data fetches. Every order/position/collateral
|
|
5
|
-
* tx-build depends on one of these REST calls landing (
|
|
6
|
-
* `
|
|
7
|
-
* with a single attempt and no retry means one
|
|
8
|
-
* fails every trade.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* implemented for these fetches.
|
|
5
|
+
* tx-build depends on one of these REST calls landing (the Lazer signed
|
|
6
|
+
* update for `pyth_lazer_rule`, the quote-center pull for `waterx_rule`); a
|
|
7
|
+
* bare `fetch` with a single attempt and no retry means one upstream 429/5xx
|
|
8
|
+
* or timeout fails every trade. Those two rule fetches, the read executors
|
|
9
|
+
* (`./read-prices.ts`), the Pyth Pro catalog/history readers, and `loadConfig`
|
|
10
|
+
* (`../perp/config.ts`) all delegate here instead of calling `fetch` directly
|
|
11
|
+
* — this is the ONE place a retry/timeout/auth policy is implemented.
|
|
13
12
|
*
|
|
14
13
|
* Policy semantics:
|
|
15
14
|
* - Bearer auth is attached iff `policy.apiKey` is a non-empty string —
|
|
@@ -56,7 +55,6 @@
|
|
|
56
55
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
56
|
exports.FetchPolicyError = void 0;
|
|
58
57
|
exports.rethrowExhaustedFetch = rethrowExhaustedFetch;
|
|
59
|
-
exports.trimTrailingSlashes = trimTrailingSlashes;
|
|
60
58
|
exports.joinEndpointPath = joinEndpointPath;
|
|
61
59
|
exports.fetchWithPolicy = fetchWithPolicy;
|
|
62
60
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
@@ -106,18 +104,13 @@ function rethrowExhaustedFetch(err, describe) {
|
|
|
106
104
|
* Join an API `path` onto an `endpoint` PRESERVING the endpoint's own base
|
|
107
105
|
* path. `new URL(path, endpoint)` is the footgun this replaces: a
|
|
108
106
|
* leading-slash path is *absolute* and silently discards the endpoint's path
|
|
109
|
-
* — harmless for a bare-origin endpoint
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* targets `<endpoint><fixed path>` must build its URL here.
|
|
107
|
+
* — harmless for a bare-origin endpoint, but it is exactly what a consumer's
|
|
108
|
+
* `waterxEndpoint` proxy route (`https://app.example/api/quote-center`) is
|
|
109
|
+
* made of, and dropping it silently bypasses the proxy. Every oracle fetch
|
|
110
|
+
* that targets `<endpoint><fixed path>` must build its URL here.
|
|
113
111
|
*/
|
|
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
112
|
function joinEndpointPath(endpoint, path) {
|
|
120
|
-
return new URL(`${
|
|
113
|
+
return new URL(`${endpoint.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`);
|
|
121
114
|
}
|
|
122
115
|
function isRetryableStatus(status) {
|
|
123
116
|
return status === 429 || status >= 500;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `validate.ts` — boot-time oracle-deployment asserts consumers (FE/BE) fold
|
|
3
|
+
* onto instead of each hand-rolling them:
|
|
4
|
+
*
|
|
5
|
+
* - {@link assertOracleWriteCoverage} — the "this fed set can actually price
|
|
6
|
+
* the tickers I care about" guard. Because every source reads through its
|
|
7
|
+
* own feeds namespace, write set == read set — passing this ALSO validates
|
|
8
|
+
* the read plane (there is deliberately no separate read-coverage assert).
|
|
9
|
+
* - {@link missingOracleCredentials} — the env-shaped credential audit: which
|
|
10
|
+
* listed sources cannot run with the credentials this deployment supplied.
|
|
11
|
+
* Boot-time mirror of `refreshOraclePrices`'s own per-build credential
|
|
12
|
+
* pre-check (`aggregate.ts`), keyed off the same rule-owned
|
|
13
|
+
* `credential` declaration so the two can never disagree.
|
|
14
|
+
*
|
|
15
|
+
* Deliberately NOT called by `PerpClient` itself: client creation stays
|
|
16
|
+
* guard-free (a source with absent feeds surfaces at tx-build for exactly the
|
|
17
|
+
* tickers it can't serve). These are for consumers whose deployment policy is
|
|
18
|
+
* "fail the BOOT, not the first trade".
|
|
19
|
+
*/
|
|
20
|
+
import type { OracleHost } from "./host.ts";
|
|
21
|
+
import { type OracleCredentialKind, type OracleSource } from "./price-update-rule.ts";
|
|
22
|
+
export type { OracleCredentialKind };
|
|
23
|
+
/**
|
|
24
|
+
* A build depends on a ticker this client's fed set cannot price.
|
|
25
|
+
*
|
|
26
|
+
* Raised from two places, deliberately the same type: the per-BUILD composers
|
|
27
|
+
* (`refreshOraclePrices` skipped the ticker by design — see
|
|
28
|
+
* `OracleRefreshSummary` — and the composer decided it was load-bearing for
|
|
29
|
+
* the action) and the boot-time {@link assertOracleWriteCoverage}. One error
|
|
30
|
+
* for one question: "this fed set cannot price these tickers".
|
|
31
|
+
*/
|
|
32
|
+
export declare class OracleTickerUnservedError extends Error {
|
|
33
|
+
readonly tickers: string[];
|
|
34
|
+
readonly sources: readonly string[];
|
|
35
|
+
constructor(tickers: string[], sources: readonly string[], why?: string);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Assert this deployment's fed set can price every ticker in `tickers`.
|
|
39
|
+
*
|
|
40
|
+
* Throws {@link OracleTickerUnservedError} naming ALL the unservable ones (not
|
|
41
|
+
* just the first — an operator fixing a config wants the whole list).
|
|
42
|
+
*
|
|
43
|
+
* This is the boot-time twin of the per-build behaviour, and it guards the gap
|
|
44
|
+
* that per-build handling deliberately leaves open: `refreshOraclePrices`
|
|
45
|
+
* SKIPS a ticker no source serves, and only a composer that happens to depend
|
|
46
|
+
* on that ticker turns the skip into an error. A market nobody trades today
|
|
47
|
+
* would therefore stay silently unpriceable until someone did. Pass the ticker
|
|
48
|
+
* set your deployment cares about (typically every market) and find out at
|
|
49
|
+
* boot instead.
|
|
50
|
+
*
|
|
51
|
+
* It does NOT check "every listed source has feeds" any more: the fed set is
|
|
52
|
+
* derived from exactly that condition (see `deriveOracleSources`), so that
|
|
53
|
+
* assert became unreachable for any real client — it could only fire for a
|
|
54
|
+
* config mutated after construction, which is a test fixture, not a
|
|
55
|
+
* deployment. Write set == read set by construction (each source reads its own
|
|
56
|
+
* feeds), so this single assert still covers both planes.
|
|
57
|
+
*/
|
|
58
|
+
export declare function assertOracleWriteCoverage(host: OracleHost, tickers: readonly string[]): void;
|
|
59
|
+
/**
|
|
60
|
+
* THE acceptance predicate, in partition form — the single definition of
|
|
61
|
+
* "will `refreshOraclePrices` put a price on chain for this ticker".
|
|
62
|
+
*
|
|
63
|
+
* A ticker is servable when some LISTED source's feeds carry it, or when it is
|
|
64
|
+
* CONSTANT-ONLY: `constant_rule` pins it and no other rule in the config feeds
|
|
65
|
+
* it. The stricter constant test matters — a constant-pinned ticker that some
|
|
66
|
+
* other rule also feeds gets aggregated from a constant-only collector, and if
|
|
67
|
+
* the chain weights that other rule the aggregate aborts `EMissingPriceSource`.
|
|
68
|
+
* (`aggregateTicker` only appends a supra leg when a price-update source
|
|
69
|
+
* already fed the collector, so a constant-only collector can never carry one.)
|
|
70
|
+
*
|
|
71
|
+
* `covered` lets a caller that has ALREADY resolved which of its tickers its
|
|
72
|
+
* fed set serves — `refreshOraclePrices`, off its rule groups — pass that in
|
|
73
|
+
* rather than have it recomputed. Both callers therefore share one rule, which
|
|
74
|
+
* is the point: a consumer pre-filtering with {@link servableTickers} cannot
|
|
75
|
+
* hand the build a ticker it will silently skip.
|
|
76
|
+
*
|
|
77
|
+
* Order-preserving. The `anyFeed` set is built at most once per call, and only
|
|
78
|
+
* when a constant-pinned ticker actually needs it.
|
|
79
|
+
*/
|
|
80
|
+
export declare function partitionServableTickers(host: OracleHost, tickers: readonly string[], covered?: ReadonlySet<string>): {
|
|
81
|
+
servable: string[];
|
|
82
|
+
unservable: string[];
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* The subset of `tickers` this deployment's fed set can actually price — the
|
|
86
|
+
* servable half of {@link partitionServableTickers}, which is literally the
|
|
87
|
+
* rule `refreshOraclePrices` applies.
|
|
88
|
+
*/
|
|
89
|
+
export declare function servableTickers(host: OracleHost, tickers: readonly string[]): string[];
|
|
90
|
+
/**
|
|
91
|
+
* Which of `sources` cannot run with the supplied credentials — one row per
|
|
92
|
+
* (source, missing credential). Empty array ⇒ the fed set is fully
|
|
93
|
+
* credentialed. Pure and env-shaped on purpose: consumers call it from their
|
|
94
|
+
* boot-time env asserts (zod superRefine, config validators) BEFORE any
|
|
95
|
+
* client exists, passing the raw values their env resolved. The per-build
|
|
96
|
+
* enforcement twin — `refreshOraclePrices`'s credential pre-check — reads the
|
|
97
|
+
* same rule-owned `credential` declaration, so a deployment this function passes
|
|
98
|
+
* cannot later trip that check for a listed source.
|
|
99
|
+
*/
|
|
100
|
+
export declare function missingOracleCredentials(sources: readonly OracleSource[], creds: {
|
|
101
|
+
pythApiKey?: string;
|
|
102
|
+
}): {
|
|
103
|
+
source: OracleSource;
|
|
104
|
+
credential: OracleCredentialKind;
|
|
105
|
+
}[];
|