@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.
- package/README.md +29 -19
- package/dist/cjs/src/account/config.d.ts +3 -2
- package/dist/cjs/src/generated/waterx_rule/waterx_rule.d.ts +7 -134
- package/dist/cjs/src/generated/waterx_rule/waterx_rule.js +8 -154
- package/dist/cjs/src/oracle/aggregate.d.ts +21 -21
- package/dist/cjs/src/oracle/aggregate.js +121 -71
- package/dist/cjs/src/oracle/config.d.ts +38 -72
- package/dist/cjs/src/oracle/config.js +0 -56
- package/dist/cjs/src/oracle/host.d.ts +24 -20
- package/dist/cjs/src/oracle/index.d.ts +7 -3
- package/dist/cjs/src/oracle/index.js +41 -8
- package/dist/cjs/src/oracle/price-update-rule.d.ts +32 -11
- package/dist/cjs/src/oracle/price-update-rule.js +23 -3
- package/dist/cjs/src/oracle/pyth.d.ts +44 -0
- package/dist/cjs/src/oracle/pyth.js +48 -6
- package/dist/cjs/src/oracle/read-plane.d.ts +70 -0
- package/dist/cjs/src/oracle/read-plane.js +85 -0
- package/dist/cjs/src/oracle/rule-registry.d.ts +6 -4
- package/dist/cjs/src/oracle/rule-registry.js +6 -4
- package/dist/cjs/src/oracle/rules/pyth-core-rule.js +6 -5
- package/dist/cjs/src/oracle/rules/pyth-lazer-rule.d.ts +21 -0
- package/dist/cjs/src/oracle/rules/pyth-lazer-rule.js +34 -6
- package/dist/cjs/src/oracle/rules/pyth-rule.js +5 -4
- package/dist/cjs/src/oracle/rules/waterx-rule.d.ts +42 -5
- package/dist/cjs/src/oracle/rules/waterx-rule.js +61 -18
- package/dist/cjs/src/oracle/source-list.d.ts +36 -0
- package/dist/cjs/src/oracle/source-list.js +57 -0
- package/dist/cjs/src/perp/client.d.ts +41 -28
- package/dist/cjs/src/perp/client.js +32 -20
- package/dist/cjs/src/perp/config-view.js +7 -6
- package/dist/cjs/src/perp/config.d.ts +6 -6
- package/dist/cjs/src/perp/config.js +12 -12
- package/dist/cjs/src/perp/index.d.ts +3 -3
- package/dist/cjs/src/perp/index.js +11 -4
- package/dist/cjs/src/perp/user/staking.js +2 -1
- package/dist/cjs/src/unified-client.d.ts +14 -9
- package/dist/cjs/src/unified-client.js +2 -2
- package/dist/cjs/src/utils/config.js +2 -1
- package/dist/cjs/src/utils/record.d.ts +12 -0
- package/dist/cjs/src/utils/record.js +22 -0
- package/dist/src/account/config.d.ts +3 -2
- package/dist/src/generated/waterx_rule/waterx_rule.d.ts +7 -134
- package/dist/src/generated/waterx_rule/waterx_rule.js +7 -143
- package/dist/src/oracle/aggregate.d.ts +21 -21
- package/dist/src/oracle/aggregate.js +121 -71
- package/dist/src/oracle/config.d.ts +38 -72
- package/dist/src/oracle/config.js +1 -55
- package/dist/src/oracle/host.d.ts +24 -20
- package/dist/src/oracle/index.d.ts +7 -3
- package/dist/src/oracle/index.js +32 -10
- package/dist/src/oracle/price-update-rule.d.ts +32 -11
- package/dist/src/oracle/price-update-rule.js +22 -3
- package/dist/src/oracle/pyth.d.ts +44 -0
- package/dist/src/oracle/pyth.js +45 -5
- package/dist/src/oracle/read-plane.d.ts +70 -0
- package/dist/src/oracle/read-plane.js +81 -0
- package/dist/src/oracle/rule-registry.d.ts +6 -4
- package/dist/src/oracle/rule-registry.js +6 -4
- package/dist/src/oracle/rules/pyth-core-rule.js +7 -6
- package/dist/src/oracle/rules/pyth-lazer-rule.d.ts +21 -0
- package/dist/src/oracle/rules/pyth-lazer-rule.js +33 -5
- package/dist/src/oracle/rules/pyth-rule.js +5 -4
- package/dist/src/oracle/rules/waterx-rule.d.ts +42 -5
- package/dist/src/oracle/rules/waterx-rule.js +57 -15
- package/dist/src/oracle/source-list.d.ts +36 -0
- package/dist/src/oracle/source-list.js +53 -0
- package/dist/src/perp/client.d.ts +41 -28
- package/dist/src/perp/client.js +33 -21
- package/dist/src/perp/config-view.js +7 -6
- package/dist/src/perp/config.d.ts +6 -6
- package/dist/src/perp/config.js +11 -9
- package/dist/src/perp/index.d.ts +3 -3
- package/dist/src/perp/index.js +2 -2
- package/dist/src/perp/user/staking.js +2 -1
- package/dist/src/unified-client.d.ts +14 -9
- package/dist/src/unified-client.js +2 -2
- package/dist/src/utils/config.js +2 -1
- package/dist/src/utils/record.d.ts +12 -0
- package/dist/src/utils/record.js +19 -0
- package/package.json +1 -1
|
@@ -9,12 +9,38 @@
|
|
|
9
9
|
* `Update` PTB value back through a `RuleUpdateHandle` for the feed calls.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.PythLazerRule = exports.LazerApiKeyMissingError = void 0;
|
|
12
|
+
exports.PythLazerRule = exports.LazerApiKeyMissingError = exports.LAZER_INFRA = void 0;
|
|
13
13
|
exports.feedLazerRule = feedLazerRule;
|
|
14
14
|
const bcs_1 = require("@mysten/bcs");
|
|
15
|
-
const
|
|
15
|
+
const record_ts_1 = require("../../utils/record.js");
|
|
16
16
|
const price_update_rule_ts_1 = require("../price-update-rule.js");
|
|
17
17
|
const update_fetch_ts_1 = require("../update-fetch.js");
|
|
18
|
+
/**
|
|
19
|
+
* Pyth Lazer (Pyth Pro) external infra — owned by THIS source, by network.
|
|
20
|
+
* Per-network constants for infrastructure Pyth operates (not part of the
|
|
21
|
+
* `waterx-config` JSON), co-located with the only rule that reads them —
|
|
22
|
+
* no other oracle source ever touches a Lazer endpoint or verifier.
|
|
23
|
+
*
|
|
24
|
+
* - `endpoint` — Lazer HTTP API base; signed updates come from
|
|
25
|
+
* `POST /v1/latest_price` (Bearer-authenticated). The service is
|
|
26
|
+
* network-agnostic (one signed payload verifies on any chain that trusts the
|
|
27
|
+
* Lazer signers), so both networks share the production host.
|
|
28
|
+
* - `verifier_package` — the Sui package carrying
|
|
29
|
+
* `pyth_lazer::parse_and_verify_le_ecdsa_update`. Per-network: testnet is
|
|
30
|
+
* still the original v1 publish; mainnet is the v2-upgraded package (which
|
|
31
|
+
* still exposes the v1 entry `pyth_lazer_rule` binds). Values mirror the
|
|
32
|
+
* contract repo's `pyth_lazer_rule/Move.toml` published-at pins.
|
|
33
|
+
*/
|
|
34
|
+
exports.LAZER_INFRA = {
|
|
35
|
+
MAINNET: {
|
|
36
|
+
endpoint: "https://pyth-lazer.dourolabs.app",
|
|
37
|
+
verifier_package: "0xefbfd064480777699fd9c557a5804d72ace7bc82661fdc8d1f1a44ea6d92ee10",
|
|
38
|
+
},
|
|
39
|
+
TESTNET: {
|
|
40
|
+
endpoint: "https://pyth-lazer.dourolabs.app",
|
|
41
|
+
verifier_package: "0xf5bd2141967507050a91b58de3d95e77c432cd90d1799ee46effc27430a68c21",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
18
44
|
/**
|
|
19
45
|
* Signed-update request pins, mirroring what the on-chain rule consumes:
|
|
20
46
|
* - `properties` — `price` + `exponent` are REQUIRED by
|
|
@@ -142,7 +168,9 @@ exports.PythLazerRule = {
|
|
|
142
168
|
// not fail per ticker as if only that feed were missing.
|
|
143
169
|
const { feeds } = requireLazerPackage(host);
|
|
144
170
|
const feedIds = tickers.map((ticker) => {
|
|
145
|
-
|
|
171
|
+
// ownEntry: a prototype-key ticker ("toString") must throw here as
|
|
172
|
+
// unlisted, not turn an inherited Function into a "feed id".
|
|
173
|
+
const feedId = (0, record_ts_1.ownEntry)(feeds, ticker);
|
|
146
174
|
if (feedId === undefined) {
|
|
147
175
|
throw new Error(`No pyth_lazer_rule feed listed for ticker: ${ticker}`);
|
|
148
176
|
}
|
|
@@ -152,7 +180,7 @@ exports.PythLazerRule = {
|
|
|
152
180
|
if (!apiKey) {
|
|
153
181
|
throw new LazerApiKeyMissingError();
|
|
154
182
|
}
|
|
155
|
-
const update = await fetchLazerSignedUpdate(
|
|
183
|
+
const update = await fetchLazerSignedUpdate(exports.LAZER_INFRA[host.network].endpoint, apiKey, feedIds, host.pyth.fetch);
|
|
156
184
|
return { kind: "pyth_lazer_rule", payload: { update, feedIds } };
|
|
157
185
|
},
|
|
158
186
|
/**
|
|
@@ -170,7 +198,7 @@ exports.PythLazerRule = {
|
|
|
170
198
|
return null;
|
|
171
199
|
const packedFeedIds = new Set(payload.feedIds);
|
|
172
200
|
for (const ticker of tickers) {
|
|
173
|
-
const feedId = host.config.packages.pyth_lazer_rule?.feeds
|
|
201
|
+
const feedId = (0, record_ts_1.ownEntry)(host.config.packages.pyth_lazer_rule?.feeds, ticker);
|
|
174
202
|
if (feedId === undefined || !packedFeedIds.has(feedId))
|
|
175
203
|
return null;
|
|
176
204
|
}
|
|
@@ -189,7 +217,7 @@ exports.PythLazerRule = {
|
|
|
189
217
|
return undefined;
|
|
190
218
|
const lazer = requireLazerPackage(host);
|
|
191
219
|
const [update] = tx.moveCall({
|
|
192
|
-
target: `${
|
|
220
|
+
target: `${exports.LAZER_INFRA[host.network].verifier_package}::pyth_lazer::parse_and_verify_le_ecdsa_update`,
|
|
193
221
|
arguments: [tx.object(lazer.state), tx.object.clock(), tx.pure.vector("u8", payload.update)],
|
|
194
222
|
});
|
|
195
223
|
return { kind: "pyth_lazer_rule", update };
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.feedPythRule = feedPythRule;
|
|
12
12
|
const pyth_rule_ts_1 = require("../../generated/waterx_pyth_rule/pyth_rule.js");
|
|
13
|
+
const pyth_ts_1 = require("../pyth.js");
|
|
13
14
|
function feedPythRule(tx, host, collector, priceInfoObjectId) {
|
|
14
15
|
(0, pyth_rule_ts_1.feed)({
|
|
15
16
|
package: host.config.packages.pyth_rule.published_at,
|
|
@@ -18,10 +19,10 @@ function feedPythRule(tx, host, collector, priceInfoObjectId) {
|
|
|
18
19
|
config: tx.object(host.config.packages.pyth_rule.config),
|
|
19
20
|
// The deployed pyth_rule package is compiled against the Core pyth
|
|
20
21
|
// dependency, so its `&PythState` parameter is the Core-package-qualified
|
|
21
|
-
// type and
|
|
22
|
-
// right state to pass. The config's price_info_object
|
|
23
|
-
// objects to match.
|
|
24
|
-
pythState: tx.object(host.
|
|
22
|
+
// type and the Core source's own per-network state (`PYTH_CORE_INFRA`)
|
|
23
|
+
// is always the right state to pass. The config's price_info_object
|
|
24
|
+
// entries are Core objects to match.
|
|
25
|
+
pythState: tx.object(pyth_ts_1.PYTH_CORE_INFRA[host.network].state_id),
|
|
25
26
|
pythPriceInfo: tx.object(priceInfoObjectId),
|
|
26
27
|
},
|
|
27
28
|
})(tx);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `aggregateTicker` appends per waterx-routed ticker. Pulls one enclave-signed
|
|
5
5
|
* batch envelope covering every requested ticker from the quote-center
|
|
6
6
|
* (`GET /v1/quotes/update?symbols=…`, endpoint from `host.waterx` — the
|
|
7
|
-
* `waterxEndpoint`/`waterxFetch` create options — else `
|
|
7
|
+
* `waterxEndpoint`/`waterxFetch` create options — else its own `WATERX_INFRA`), then —
|
|
8
8
|
* unlike Pyth Lazer, whose verify is a single shared PTB step — verifies AND
|
|
9
9
|
* feeds in ONE `waterx_rule::collect_batch_latest` call per collector (the Move
|
|
10
10
|
* API bundles the two). So `buildUpdateCalls` emits nothing and the signed
|
|
@@ -13,12 +13,47 @@
|
|
|
13
13
|
* `collect_batch_latest` is the dual-rule path: it feeds the item matching
|
|
14
14
|
* `collector.symbol()` WITHOUT aggregating, so a waterx-routed ticker composes
|
|
15
15
|
* onto the same collector as Pyth/Supra (compose-then-aggregate). On-chain a
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* FRESHNESS miss ABSTAINS (the other weighted rules cover); a config/
|
|
17
|
+
* integrity mismatch, bad signature, future timestamp — or a REPLAYED signed
|
|
18
|
+
* timestamp — ABORTS (`EReplayedSignature`, audit F-014: a signed tuple is
|
|
19
|
+
* single-use per symbol, enforced by a per-symbol high-water mark BEFORE any
|
|
20
|
+
* weight arbitration). Consequence for concurrent builds: two PTBs carrying
|
|
21
|
+
* the same envelope for the same symbol cannot both land — the second aborts
|
|
22
|
+
* even if the rule is unweighted for that ticker. Never share one fetched
|
|
23
|
+
* envelope across builds that may execute concurrently for the same symbol.
|
|
18
24
|
*/
|
|
19
25
|
import type { Transaction, TransactionArgument } from "@mysten/sui/transactions";
|
|
26
|
+
import type { Network } from "../../constants.ts";
|
|
20
27
|
import type { OracleHost } from "../host.ts";
|
|
21
28
|
import { type PriceUpdateRule, type RuleUpdateData } from "../price-update-rule.ts";
|
|
29
|
+
/** The single signing intent the quote-center emits — exported so read-plane
|
|
30
|
+
* consumers can mirror the rule's own envelope intent check (a mispointed
|
|
31
|
+
* endpoint must be rejected by reads exactly as tx-builds reject it). */
|
|
32
|
+
export declare const BATCH_PRICE_INTENT = 1;
|
|
33
|
+
/**
|
|
34
|
+
* WaterX quote-center external infra — owned by THIS source, by network.
|
|
35
|
+
* Mirrors `PYTH_CORE_INFRA` (oracle/pyth.ts) and `LAZER_INFRA`
|
|
36
|
+
* (rules/pyth-lazer-rule.ts): per-network constants for infrastructure the
|
|
37
|
+
* source's operator runs, co-located with the only rule that reads them — no
|
|
38
|
+
* other oracle source ever touches a quote-center endpoint. Public read (no
|
|
39
|
+
* auth), so there is no api_key. `endpoint` has no trailing slash — the rule
|
|
40
|
+
* appends the path.
|
|
41
|
+
*
|
|
42
|
+
* These are the DEFAULTS behind the caller's `client.waterx` access slice
|
|
43
|
+
* (`waterxEndpoint` / `waterxFetch` create options) — the browser-CORS proxy
|
|
44
|
+
* hook, since this is the one source fetched from the page.
|
|
45
|
+
*/
|
|
46
|
+
export declare const WATERX_INFRA: Record<Network, {
|
|
47
|
+
endpoint: string;
|
|
48
|
+
}>;
|
|
49
|
+
/**
|
|
50
|
+
* The waterx source's quote-center base for `network` — the ONE accessor
|
|
51
|
+
* consumers (BE/FE read planes) use when, and only when, their own
|
|
52
|
+
* `ORACLE_SOURCE` resolves to `'waterx_rule'`. Mirrors
|
|
53
|
+
* `pythCoreHermesEndpoint`. Under any other source the read endpoint is that
|
|
54
|
+
* source's own configuration — never this one.
|
|
55
|
+
*/
|
|
56
|
+
export declare function waterxQuoteCenterEndpoint(network: Network): string;
|
|
22
57
|
/**
|
|
23
58
|
* One item inside a signed batch payload, mirroring the quote-center
|
|
24
59
|
* `/v1/quotes/update` JSON 1:1 (snake_case). The u64 integer fields are the
|
|
@@ -82,8 +117,10 @@ export declare function waterxEnvelopeOf(data: RuleUpdateData): WaterxSignedEnve
|
|
|
82
117
|
* per item, the exact shape the enclave signed) and contribute the price for
|
|
83
118
|
* `collector.symbol()` to the collector. One collect call re-verifies the batch
|
|
84
119
|
* signature and picks this collector's symbol out of the batch; on-chain it
|
|
85
|
-
* abstains (records `none`)
|
|
86
|
-
*
|
|
120
|
+
* abstains (records `none`) when the symbol is stale or absent from the batch,
|
|
121
|
+
* but ABORTS `EReplayedSignature` when the symbol's signed timestamp was
|
|
122
|
+
* already accepted (per-symbol high-water mark, audit F-014) — see the module
|
|
123
|
+
* header for the concurrent-build consequence.
|
|
87
124
|
*/
|
|
88
125
|
export declare function feedWaterxRule(tx: Transaction, host: OracleHost, collector: TransactionArgument, envelope: WaterxSignedEnvelope): void;
|
|
89
126
|
export declare const WaterxRule: PriceUpdateRule;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `aggregateTicker` appends per waterx-routed ticker. Pulls one enclave-signed
|
|
6
6
|
* batch envelope covering every requested ticker from the quote-center
|
|
7
7
|
* (`GET /v1/quotes/update?symbols=…`, endpoint from `host.waterx` — the
|
|
8
|
-
* `waterxEndpoint`/`waterxFetch` create options — else `
|
|
8
|
+
* `waterxEndpoint`/`waterxFetch` create options — else its own `WATERX_INFRA`), then —
|
|
9
9
|
* unlike Pyth Lazer, whose verify is a single shared PTB step — verifies AND
|
|
10
10
|
* feeds in ONE `waterx_rule::collect_batch_latest` call per collector (the Move
|
|
11
11
|
* API bundles the two). So `buildUpdateCalls` emits nothing and the signed
|
|
@@ -14,21 +14,57 @@
|
|
|
14
14
|
* `collect_batch_latest` is the dual-rule path: it feeds the item matching
|
|
15
15
|
* `collector.symbol()` WITHOUT aggregating, so a waterx-routed ticker composes
|
|
16
16
|
* onto the same collector as Pyth/Supra (compose-then-aggregate). On-chain a
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* FRESHNESS miss ABSTAINS (the other weighted rules cover); a config/
|
|
18
|
+
* integrity mismatch, bad signature, future timestamp — or a REPLAYED signed
|
|
19
|
+
* timestamp — ABORTS (`EReplayedSignature`, audit F-014: a signed tuple is
|
|
20
|
+
* single-use per symbol, enforced by a per-symbol high-water mark BEFORE any
|
|
21
|
+
* weight arbitration). Consequence for concurrent builds: two PTBs carrying
|
|
22
|
+
* the same envelope for the same symbol cannot both land — the second aborts
|
|
23
|
+
* even if the rule is unweighted for that ticker. Never share one fetched
|
|
24
|
+
* envelope across builds that may execute concurrently for the same symbol.
|
|
19
25
|
*/
|
|
20
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.WaterxRule = void 0;
|
|
27
|
+
exports.WaterxRule = exports.WATERX_INFRA = exports.BATCH_PRICE_INTENT = void 0;
|
|
28
|
+
exports.waterxQuoteCenterEndpoint = waterxQuoteCenterEndpoint;
|
|
22
29
|
exports.parseSignedEnvelope = parseSignedEnvelope;
|
|
23
30
|
exports.waterxEnvelopeOf = waterxEnvelopeOf;
|
|
24
31
|
exports.feedWaterxRule = feedWaterxRule;
|
|
25
32
|
const bcs_1 = require("@mysten/bcs");
|
|
26
33
|
const waterx_rule_ts_1 = require("../../generated/waterx_rule/waterx_rule.js");
|
|
27
|
-
const
|
|
34
|
+
const record_ts_1 = require("../../utils/record.js");
|
|
28
35
|
const price_update_rule_ts_1 = require("../price-update-rule.js");
|
|
29
36
|
const update_fetch_ts_1 = require("../update-fetch.js");
|
|
30
|
-
/** The single signing intent
|
|
31
|
-
|
|
37
|
+
/** The single signing intent the quote-center emits — exported so read-plane
|
|
38
|
+
* consumers can mirror the rule's own envelope intent check (a mispointed
|
|
39
|
+
* endpoint must be rejected by reads exactly as tx-builds reject it). */
|
|
40
|
+
exports.BATCH_PRICE_INTENT = 1;
|
|
41
|
+
/**
|
|
42
|
+
* WaterX quote-center external infra — owned by THIS source, by network.
|
|
43
|
+
* Mirrors `PYTH_CORE_INFRA` (oracle/pyth.ts) and `LAZER_INFRA`
|
|
44
|
+
* (rules/pyth-lazer-rule.ts): per-network constants for infrastructure the
|
|
45
|
+
* source's operator runs, co-located with the only rule that reads them — no
|
|
46
|
+
* other oracle source ever touches a quote-center endpoint. Public read (no
|
|
47
|
+
* auth), so there is no api_key. `endpoint` has no trailing slash — the rule
|
|
48
|
+
* appends the path.
|
|
49
|
+
*
|
|
50
|
+
* These are the DEFAULTS behind the caller's `client.waterx` access slice
|
|
51
|
+
* (`waterxEndpoint` / `waterxFetch` create options) — the browser-CORS proxy
|
|
52
|
+
* hook, since this is the one source fetched from the page.
|
|
53
|
+
*/
|
|
54
|
+
exports.WATERX_INFRA = {
|
|
55
|
+
MAINNET: { endpoint: "https://quote-center.waterx.app" },
|
|
56
|
+
TESTNET: { endpoint: "https://quote-center-staging.waterx.app" },
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* The waterx source's quote-center base for `network` — the ONE accessor
|
|
60
|
+
* consumers (BE/FE read planes) use when, and only when, their own
|
|
61
|
+
* `ORACLE_SOURCE` resolves to `'waterx_rule'`. Mirrors
|
|
62
|
+
* `pythCoreHermesEndpoint`. Under any other source the read endpoint is that
|
|
63
|
+
* source's own configuration — never this one.
|
|
64
|
+
*/
|
|
65
|
+
function waterxQuoteCenterEndpoint(network) {
|
|
66
|
+
return exports.WATERX_INFRA[network].endpoint;
|
|
67
|
+
}
|
|
32
68
|
/**
|
|
33
69
|
* Shape check ONLY — the `kind` discriminant is checked separately by the
|
|
34
70
|
* caller before this runs (mirrors the other rules' guard split), so a
|
|
@@ -90,18 +126,21 @@ function requireWaterxPackage(host) {
|
|
|
90
126
|
return entry;
|
|
91
127
|
}
|
|
92
128
|
/**
|
|
93
|
-
* Resolve the quote-center infra for this host:
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
129
|
+
* Resolve the quote-center infra for this host: each field independently from
|
|
130
|
+
* the caller's `client.waterx` access slice (`waterxEndpoint` / `waterxFetch`
|
|
131
|
+
* create options) when set, else this source's own `WATERX_INFRA` default /
|
|
132
|
+
* `fetchWithPolicy`'s built-ins. Deliberately NO fallback onto `pyth.fetch`
|
|
133
|
+
* or any other source's policy — sources stay fully independent.
|
|
97
134
|
*
|
|
98
135
|
* This is the seam a browser consumer needs: the envelope is fetched FROM THE
|
|
99
136
|
* PAGE, so a front end whose origin the quote-center does not allow (CORS)
|
|
100
137
|
* points `endpoint` at a same-origin proxy, or supplies its own `fetchImpl`.
|
|
101
138
|
*/
|
|
102
139
|
function resolveWaterxInfra(host) {
|
|
103
|
-
|
|
104
|
-
|
|
140
|
+
return {
|
|
141
|
+
endpoint: host.waterx?.endpoint ?? exports.WATERX_INFRA[host.network].endpoint,
|
|
142
|
+
fetch: host.waterx?.fetch,
|
|
143
|
+
};
|
|
105
144
|
}
|
|
106
145
|
/**
|
|
107
146
|
* Pull one enclave-signed batch envelope covering `symbols` from the
|
|
@@ -136,8 +175,8 @@ async function fetchWaterxSignedUpdate(endpoint, symbols, fetchOpts) {
|
|
|
136
175
|
// Parse from raw text (not res.json()) so the u64 fields are decoded exact as
|
|
137
176
|
// bigint — see parseSignedEnvelope. Malformed-shape check lives there.
|
|
138
177
|
const envelope = parseSignedEnvelope(await res.text());
|
|
139
|
-
if (envelope.intent !== BATCH_PRICE_INTENT) {
|
|
140
|
-
throw new Error(`WaterX quote-center returned intent ${envelope.intent}, expected BATCH_PRICE_INTENT ${BATCH_PRICE_INTENT}`);
|
|
178
|
+
if (envelope.intent !== exports.BATCH_PRICE_INTENT) {
|
|
179
|
+
throw new Error(`WaterX quote-center returned intent ${envelope.intent}, expected BATCH_PRICE_INTENT ${exports.BATCH_PRICE_INTENT}`);
|
|
141
180
|
}
|
|
142
181
|
return envelope;
|
|
143
182
|
}
|
|
@@ -157,8 +196,10 @@ function decodeSig(hex) {
|
|
|
157
196
|
* per item, the exact shape the enclave signed) and contribute the price for
|
|
158
197
|
* `collector.symbol()` to the collector. One collect call re-verifies the batch
|
|
159
198
|
* signature and picks this collector's symbol out of the batch; on-chain it
|
|
160
|
-
* abstains (records `none`)
|
|
161
|
-
*
|
|
199
|
+
* abstains (records `none`) when the symbol is stale or absent from the batch,
|
|
200
|
+
* but ABORTS `EReplayedSignature` when the symbol's signed timestamp was
|
|
201
|
+
* already accepted (per-symbol high-water mark, audit F-014) — see the module
|
|
202
|
+
* header for the concurrent-build consequence.
|
|
162
203
|
*/
|
|
163
204
|
function feedWaterxRule(tx, host, collector, envelope) {
|
|
164
205
|
const wr = requireWaterxPackage(host);
|
|
@@ -227,7 +268,9 @@ exports.WaterxRule = {
|
|
|
227
268
|
// not fail per ticker as if only that feed were missing.
|
|
228
269
|
const { feeds } = requireWaterxPackage(host);
|
|
229
270
|
for (const ticker of tickers) {
|
|
230
|
-
|
|
271
|
+
// ownEntry: a prototype-key ticker ("toString") must throw here as
|
|
272
|
+
// unlisted, not pass as an inherited Function and reach the network.
|
|
273
|
+
if ((0, record_ts_1.ownEntry)(feeds, ticker) === undefined) {
|
|
231
274
|
throw new Error(`No waterx_rule feed listed for ticker: ${ticker}`);
|
|
232
275
|
}
|
|
233
276
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `source-list.ts` — THE parser for a consumer's `ORACLE_SOURCE` env string
|
|
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:
|
|
7
|
+
*
|
|
8
|
+
* - split on `,`, trim entries, DROP empties (trailing/doubled commas are
|
|
9
|
+
* the most common env typo, never a boot failure)
|
|
10
|
+
* - validate every entry against {@link ORACLE_SOURCES}
|
|
11
|
+
* - dedupe, order-preserving (list order is consumer read-plane policy —
|
|
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
|
|
15
|
+
*
|
|
16
|
+
* The SDK still never reads `process.env` — callers pass the raw string.
|
|
17
|
+
*
|
|
18
|
+
* STRICTER than the consumers' previous `in`-operator checks: a value named
|
|
19
|
+
* like an `Object.prototype` key (`toString`, `constructor`, …) passed those
|
|
20
|
+
* and died deep in the stack; `Set.has` rejects it here at parse.
|
|
21
|
+
*
|
|
22
|
+
* Zod adopters: this THROWS a plain Error. Inside a zod `.transform()` a
|
|
23
|
+
* throw escapes `schema.parse()` un-aggregated and masks sibling issues —
|
|
24
|
+
* wrap it: `try { return parseOracleSourceList(raw); } catch (e) {
|
|
25
|
+
* ctx.addIssue({ code: "custom", message: (e as Error).message }); return
|
|
26
|
+
* z.NEVER; }`.
|
|
27
|
+
*/
|
|
28
|
+
import { type OracleSource } from "./price-update-rule.ts";
|
|
29
|
+
/**
|
|
30
|
+
* THE runtime membership check for {@link ORACLE_SOURCES} — the parser below
|
|
31
|
+
* and `PerpClient`'s ctor validation both use this one predicate, so the env
|
|
32
|
+
* parser and the create-option front door can never disagree. `Set.has`,
|
|
33
|
+
* never `in`/bracket reads (prototype-chain safe by construction).
|
|
34
|
+
*/
|
|
35
|
+
export declare function isOracleSource(value: string): value is OracleSource;
|
|
36
|
+
export declare function parseOracleSourceList(raw: string | null | undefined): OracleSource[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `source-list.ts` — THE parser for a consumer's `ORACLE_SOURCE` env string
|
|
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:
|
|
8
|
+
*
|
|
9
|
+
* - split on `,`, trim entries, DROP empties (trailing/doubled commas are
|
|
10
|
+
* the most common env typo, never a boot failure)
|
|
11
|
+
* - validate every entry against {@link ORACLE_SOURCES}
|
|
12
|
+
* - dedupe, order-preserving (list order is consumer read-plane policy —
|
|
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
|
|
16
|
+
*
|
|
17
|
+
* The SDK still never reads `process.env` — callers pass the raw string.
|
|
18
|
+
*
|
|
19
|
+
* STRICTER than the consumers' previous `in`-operator checks: a value named
|
|
20
|
+
* like an `Object.prototype` key (`toString`, `constructor`, …) passed those
|
|
21
|
+
* and died deep in the stack; `Set.has` rejects it here at parse.
|
|
22
|
+
*
|
|
23
|
+
* Zod adopters: this THROWS a plain Error. Inside a zod `.transform()` a
|
|
24
|
+
* throw escapes `schema.parse()` un-aggregated and masks sibling issues —
|
|
25
|
+
* wrap it: `try { return parseOracleSourceList(raw); } catch (e) {
|
|
26
|
+
* ctx.addIssue({ code: "custom", message: (e as Error).message }); return
|
|
27
|
+
* z.NEVER; }`.
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.isOracleSource = isOracleSource;
|
|
31
|
+
exports.parseOracleSourceList = parseOracleSourceList;
|
|
32
|
+
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
|
+
/**
|
|
37
|
+
* THE runtime membership check for {@link ORACLE_SOURCES} — the parser below
|
|
38
|
+
* and `PerpClient`'s ctor validation both use this one predicate, so the env
|
|
39
|
+
* parser and the create-option front door can never disagree. `Set.has`,
|
|
40
|
+
* never `in`/bracket reads (prototype-chain safe by construction).
|
|
41
|
+
*/
|
|
42
|
+
function isOracleSource(value) {
|
|
43
|
+
return ORACLE_SOURCE_SET.has(value);
|
|
44
|
+
}
|
|
45
|
+
function parseOracleSourceList(raw) {
|
|
46
|
+
const parts = (raw ?? "")
|
|
47
|
+
.split(",")
|
|
48
|
+
.map((part) => part.trim())
|
|
49
|
+
.filter((part) => part !== "");
|
|
50
|
+
const sources = parts.filter(isOracleSource);
|
|
51
|
+
if (parts.length === 0 || sources.length !== parts.length) {
|
|
52
|
+
const got = raw == null || raw.trim() === "" ? "unset" : `'${raw}'`;
|
|
53
|
+
throw new Error(`ORACLE_SOURCE must be a comma-separated list of ${price_update_rule_ts_1.ORACLE_SOURCES.join(" | ")} ` +
|
|
54
|
+
`(got ${got}) — there is NO default oracle source; set it in the deployment's env.`);
|
|
55
|
+
}
|
|
56
|
+
return [...new Set(sources)];
|
|
57
|
+
}
|
|
@@ -11,37 +11,50 @@
|
|
|
11
11
|
* {@link PerpConfigView}. This class is just the wiring + factory between them.
|
|
12
12
|
*/
|
|
13
13
|
import { BaseLineClient } from "../base-client.ts";
|
|
14
|
-
import type
|
|
14
|
+
import { type OracleSource } from "../oracle/price-update-rule.ts";
|
|
15
15
|
import type { FetchPolicy } from "../oracle/update-fetch.ts";
|
|
16
|
-
import { type LoadConfigOptions, type
|
|
16
|
+
import { type LoadConfigOptions, type PythAccessConfig, type PythFetchPolicy, type WaterxAccessConfig, type WaterXConfig, type WormholeInfraConfig } from "./config.ts";
|
|
17
17
|
import type { Network } from "./constants.ts";
|
|
18
18
|
export interface CreateClientOptions extends LoadConfigOptions {
|
|
19
19
|
grpcUrl?: string;
|
|
20
20
|
/**
|
|
21
|
-
* Which oracle price-update source drives `refreshOraclePrices`.
|
|
22
|
-
* is
|
|
21
|
+
* Which oracle price-update source drives `refreshOraclePrices`. REQUIRED —
|
|
22
|
+
* there is NO default source: every deployment names its source explicitly
|
|
23
|
+
* (wire it from your own env var, e.g. `ORACLE_SOURCE`). Each source is
|
|
24
|
+
* self-contained (own infra, own endpoints, own config) with NO cross-source
|
|
25
|
+
* fallback:
|
|
23
26
|
*
|
|
24
|
-
* - `'pyth_rule'`
|
|
25
|
-
*
|
|
27
|
+
* - `'pyth_rule'` — Pyth Core `pyth_rule` updates (Hermes VAA + per-feed
|
|
28
|
+
* update fees); Core state + Hermes endpoint live in the source's own
|
|
29
|
+
* `PYTH_CORE_INFRA` table.
|
|
26
30
|
* - `'pyth_lazer_rule'` — Pyth Lazer signed updates (ONE `leEcdsa` verify
|
|
27
31
|
* per PTB, no per-feed fees); needs `packages.pyth_lazer_rule` with feeds
|
|
28
|
-
* and a `pythApiKey` (Lazer is auth-first)
|
|
32
|
+
* and a `pythApiKey` (Lazer is auth-first); Lazer infra lives in the
|
|
33
|
+
* source's own `LAZER_INFRA` table.
|
|
29
34
|
* - `'waterx_rule'` — the first-party WaterX quote-center (Nautilus-TEE,
|
|
30
35
|
* ed25519-signed batches): ONE envelope covering the build's tickers,
|
|
31
36
|
* verified AND fed by a single `collect_batch_latest` per collector. No
|
|
32
37
|
* credential and no per-update fee; needs `packages.waterx_rule` with
|
|
33
|
-
* feeds.
|
|
34
|
-
* /
|
|
38
|
+
* feeds. Quote-center infra lives in the source's own `WATERX_INFRA`
|
|
39
|
+
* table; endpoint/transport overridable via
|
|
40
|
+
* {@link CreateClientOptions.waterxEndpoint} /
|
|
41
|
+
* {@link CreateClientOptions.waterxFetch} — the browser-CORS proxy hook,
|
|
35
42
|
* since this is the one source fetched from the page.
|
|
36
43
|
*
|
|
37
44
|
* The name is source-neutral on purpose — a source need not be Pyth (as
|
|
38
45
|
* `'waterx_rule'` shows). Selecting a source whose feed for a requested
|
|
39
46
|
* ticker is absent is NOT an error at client creation: it fails at tx-build
|
|
40
|
-
* time for exactly those tickers (see `refreshOraclePrices`).
|
|
41
|
-
*
|
|
42
|
-
*
|
|
47
|
+
* time for exactly those tickers (see `refreshOraclePrices`).
|
|
48
|
+
*
|
|
49
|
+
* Accepts a SINGLE source or a LIST. A list means every listed source's
|
|
50
|
+
* data is fetched and fed in one build — required whenever the on-chain
|
|
51
|
+
* weight tables have more than one rule weighted (e.g. a Core→Pro or
|
|
52
|
+
* Pro+Waterx coexistence window): the chain drops unweighted contributions
|
|
53
|
+
* (harmless) but aborts on a starved weighted rule, so the list must stay a
|
|
54
|
+
* SUPERSET of every ticker's weighted set. Order is meaningful to consumers
|
|
55
|
+
* (read-plane priority), not to the on-chain build.
|
|
43
56
|
*/
|
|
44
|
-
oracleSource
|
|
57
|
+
oracleSource: OracleSource | OracleSource[];
|
|
45
58
|
/**
|
|
46
59
|
* Pyth Lazer access token (`Authorization: Bearer …`). Required under
|
|
47
60
|
* `oracleSource: 'pyth_lazer_rule'` (Lazer is auth-first) and unused by
|
|
@@ -57,7 +70,7 @@ export interface CreateClientOptions extends LoadConfigOptions {
|
|
|
57
70
|
pythFetch?: PythFetchPolicy;
|
|
58
71
|
/**
|
|
59
72
|
* Quote-center base URL for `oracleSource: 'waterx_rule'` — overrides the
|
|
60
|
-
* per-network
|
|
73
|
+
* source's own per-network `WATERX_INFRA` default.
|
|
61
74
|
*
|
|
62
75
|
* This is the one source a BROWSER fetches itself (the signed envelope is
|
|
63
76
|
* pulled from the page), so it is bound by the quote-center deployment's CORS
|
|
@@ -73,28 +86,28 @@ export interface CreateClientOptions extends LoadConfigOptions {
|
|
|
73
86
|
waterxEndpoint?: string;
|
|
74
87
|
/**
|
|
75
88
|
* Retry/timeout policy — and `fetchImpl` — for the quote-center fetch (see
|
|
76
|
-
* `fetchWithPolicy`). Optional: falls back to
|
|
77
|
-
*
|
|
89
|
+
* `fetchWithPolicy`). Optional: falls back to the built-in defaults — never
|
|
90
|
+
* to `pythFetch` (sources stay independent). Supply `fetchImpl` to route the request through your own
|
|
78
91
|
* transport (a proxying `fetch` wrapper, a non-global `fetch`, a test double).
|
|
79
92
|
*/
|
|
80
93
|
waterxFetch?: FetchPolicy;
|
|
81
94
|
}
|
|
82
95
|
export declare class PerpClient extends BaseLineClient<WaterXConfig> {
|
|
83
|
-
/** Pyth
|
|
84
|
-
pyth:
|
|
96
|
+
/** Caller-supplied Pyth credential + fetch policy — NO infra; each source owns its own tables. */
|
|
97
|
+
pyth: PythAccessConfig;
|
|
85
98
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
99
|
+
* Caller-supplied quote-center overrides for `oracleSource: 'waterx_rule'`
|
|
100
|
+
* (`waterxEndpoint` / `waterxFetch` create options) — access-only, mirroring
|
|
101
|
+
* `pyth` above; unset fields resolve against the rule's own `WATERX_INFRA`.
|
|
89
102
|
*/
|
|
90
|
-
waterx:
|
|
103
|
+
waterx: WaterxAccessConfig;
|
|
91
104
|
/** Wormhole infra for the credit bridge (network defaults unless overridden). */
|
|
92
105
|
wormhole: WormholeInfraConfig;
|
|
93
|
-
/**
|
|
94
|
-
readonly
|
|
106
|
+
/** The fed set: `oracleSource` create option normalized to a non-empty, deduped list. */
|
|
107
|
+
readonly oracleSources: readonly OracleSource[];
|
|
95
108
|
/** Canonical-schema lookups (delegated to below); no transport. */
|
|
96
109
|
private readonly view;
|
|
97
|
-
constructor(network: Network, config: WaterXConfig, opts
|
|
110
|
+
constructor(network: Network, config: WaterXConfig, opts: CreateClientOptions);
|
|
98
111
|
/**
|
|
99
112
|
* Async factory: fetches the deployment config for `network` and returns
|
|
100
113
|
* a ready-to-use client. Pass `opts.cache=true` to memoize the JSON.
|
|
@@ -103,9 +116,9 @@ export declare class PerpClient extends BaseLineClient<WaterXConfig> {
|
|
|
103
116
|
* not an error at init — it surfaces at tx-build time for the specific
|
|
104
117
|
* tickers that source can't serve (see `refreshOraclePrices`).
|
|
105
118
|
*/
|
|
106
|
-
static create(network: Network, opts
|
|
107
|
-
static mainnet(opts
|
|
108
|
-
static testnet(opts
|
|
119
|
+
static create(network: Network, opts: CreateClientOptions): Promise<PerpClient>;
|
|
120
|
+
static mainnet(opts: CreateClientOptions): Promise<PerpClient>;
|
|
121
|
+
static testnet(opts: CreateClientOptions): Promise<PerpClient>;
|
|
109
122
|
/** @see PerpConfigView.getMarket */
|
|
110
123
|
getMarket(ticker: string): import("./config.ts").WaterxPerpMarketEntry;
|
|
111
124
|
/** @see PerpConfigView.getAggregator */
|
|
@@ -14,44 +14,56 @@
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.PerpClient = void 0;
|
|
16
16
|
const base_client_ts_1 = require("../base-client.js");
|
|
17
|
+
const price_update_rule_ts_1 = require("../oracle/price-update-rule.js");
|
|
18
|
+
const source_list_ts_1 = require("../oracle/source-list.js");
|
|
17
19
|
const config_view_ts_1 = require("./config-view.js");
|
|
18
20
|
const config_ts_1 = require("./config.js");
|
|
19
21
|
class PerpClient extends base_client_ts_1.BaseLineClient {
|
|
20
|
-
/** Pyth
|
|
22
|
+
/** Caller-supplied Pyth credential + fetch policy — NO infra; each source owns its own tables. */
|
|
21
23
|
pyth;
|
|
22
24
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
25
|
+
* Caller-supplied quote-center overrides for `oracleSource: 'waterx_rule'`
|
|
26
|
+
* (`waterxEndpoint` / `waterxFetch` create options) — access-only, mirroring
|
|
27
|
+
* `pyth` above; unset fields resolve against the rule's own `WATERX_INFRA`.
|
|
26
28
|
*/
|
|
27
29
|
waterx;
|
|
28
30
|
/** Wormhole infra for the credit bridge (network defaults unless overridden). */
|
|
29
31
|
wormhole;
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
+
/** The fed set: `oracleSource` create option normalized to a non-empty, deduped list. */
|
|
33
|
+
oracleSources;
|
|
32
34
|
/** Canonical-schema lookups (delegated to below); no transport. */
|
|
33
35
|
view;
|
|
34
|
-
constructor(network, config, opts
|
|
36
|
+
constructor(network, config, opts) {
|
|
35
37
|
super(network, config, opts);
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
38
|
+
// Access-only slice: the api_key + fetch policy are caller-supplied at
|
|
39
|
+
// init (a secret has no place in the canonical waterx-config JSON). All
|
|
40
|
+
// endpoint/object-id infra is per-source, owned by the rule modules —
|
|
41
|
+
// nothing infra-shaped lives on the client.
|
|
40
42
|
this.pyth = {
|
|
41
|
-
...config_ts_1.PYTH_DEFAULTS[network],
|
|
42
43
|
...(opts.pythApiKey !== undefined ? { api_key: opts.pythApiKey } : {}),
|
|
43
44
|
...(opts.pythFetch !== undefined ? { fetch: opts.pythFetch } : {}),
|
|
44
45
|
};
|
|
45
46
|
this.wormhole = config.wormhole ?? config_ts_1.WORMHOLE_DEFAULTS[network];
|
|
46
|
-
// Quote-center
|
|
47
|
-
//
|
|
48
|
-
//
|
|
47
|
+
// Quote-center access slice: overrides only — a browser blocked by the
|
|
48
|
+
// quote-center's CORS allowlist swaps `endpoint` for a same-origin proxy;
|
|
49
|
+
// unset fields resolve inside the rule against WATERX_INFRA[network].
|
|
49
50
|
this.waterx = {
|
|
50
|
-
...config_ts_1.WATERX_DEFAULTS[network],
|
|
51
51
|
...(opts.waterxEndpoint !== undefined ? { endpoint: opts.waterxEndpoint } : {}),
|
|
52
52
|
...(opts.waterxFetch !== undefined ? { fetch: opts.waterxFetch } : {}),
|
|
53
53
|
};
|
|
54
|
-
|
|
54
|
+
// Normalize single-or-list to a deduped, order-preserving list, gated by
|
|
55
|
+
// `isOracleSource` — the SAME predicate `parseOracleSourceList` uses. An
|
|
56
|
+
// empty list, a nullish entry (an untyped caller omitting the REQUIRED
|
|
57
|
+
// option), or an unregistered value (`'core'`, `'pyth'`, …) fails
|
|
58
|
+
// construction loudly instead of booting green and surfacing as
|
|
59
|
+
// `OracleSourceNotImplemented` at the first tx-build.
|
|
60
|
+
const sources = Array.isArray(opts.oracleSource) ? opts.oracleSource : [opts.oracleSource];
|
|
61
|
+
this.oracleSources = [...new Set(sources)];
|
|
62
|
+
if (this.oracleSources.length === 0 ||
|
|
63
|
+
this.oracleSources.some((source) => !(0, source_list_ts_1.isOracleSource)(source))) {
|
|
64
|
+
throw new Error(`oracleSource is REQUIRED and must name at least one of ${price_update_rule_ts_1.ORACLE_SOURCES.join(" | ")} ` +
|
|
65
|
+
`(got ${JSON.stringify(sources)})`);
|
|
66
|
+
}
|
|
55
67
|
this.view = new config_view_ts_1.PerpConfigView(() => this.config, () => this.wormhole);
|
|
56
68
|
}
|
|
57
69
|
/**
|
|
@@ -62,14 +74,14 @@ class PerpClient extends base_client_ts_1.BaseLineClient {
|
|
|
62
74
|
* not an error at init — it surfaces at tx-build time for the specific
|
|
63
75
|
* tickers that source can't serve (see `refreshOraclePrices`).
|
|
64
76
|
*/
|
|
65
|
-
static async create(network, opts
|
|
77
|
+
static async create(network, opts) {
|
|
66
78
|
const config = await (0, config_ts_1.loadConfig)(network, opts);
|
|
67
79
|
return new PerpClient(network, config, opts);
|
|
68
80
|
}
|
|
69
|
-
static mainnet(opts
|
|
81
|
+
static mainnet(opts) {
|
|
70
82
|
return PerpClient.create("MAINNET", opts);
|
|
71
83
|
}
|
|
72
|
-
static testnet(opts
|
|
84
|
+
static testnet(opts) {
|
|
73
85
|
return PerpClient.create("TESTNET", opts);
|
|
74
86
|
}
|
|
75
87
|
// ========================================================
|