@waterx/sdk 4.3.3 → 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 +41 -35
- package/README.md +81 -77
- 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/oracle/aggregate.d.ts +45 -61
- package/dist/cjs/src/oracle/aggregate.js +138 -138
- package/dist/cjs/src/oracle/config.d.ts +35 -49
- 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 +79 -66
- 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 +137 -12
- package/dist/cjs/src/oracle/rules/waterx-rule.js +318 -55
- 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 +35 -52
- 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 +35 -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 +8 -34
- 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/oracle/aggregate.d.ts +45 -61
- package/dist/src/oracle/aggregate.js +138 -137
- package/dist/src/oracle/config.d.ts +35 -49
- 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 +55 -50
- 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 +137 -12
- package/dist/src/oracle/rules/waterx-rule.js +314 -56
- 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 +35 -52
- 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 +11 -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 +8 -34
- 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 +1 -1
- 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
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Oracle aggregation — the orchestrator that composes rules into the shared
|
|
3
3
|
* `Oracle`. This is the ONE file that knows about every rule: it builds a
|
|
4
4
|
* `PriceCollector`, feeds whichever rules a ticker is configured for
|
|
5
|
-
* (
|
|
5
|
+
* (Lazer / Waterx / Supra / Constant), then `aggregate`s.
|
|
6
6
|
*
|
|
7
7
|
* Per ticker:
|
|
8
8
|
* collector = oracle::new_collector(ticker)
|
|
9
|
-
* [pyth_rule::feed] when the ticker has a pyth_rule.feeds entry
|
|
10
9
|
* [pyth_lazer_rule::feed] when the update leg produced a verified lazer Update
|
|
10
|
+
* [waterx_rule::collect_*] when the update leg fetched signed waterx data
|
|
11
11
|
* [supra_rule::feed] when supra is enabled + wired
|
|
12
12
|
* [constant_rule::feed] when the ticker is a constant ticker
|
|
13
13
|
* oracle::aggregate(oracle, collector)
|
|
@@ -24,38 +24,32 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import type { Transaction, TransactionArgument } from "@mysten/sui/transactions";
|
|
26
26
|
import type { OracleHost } from "./host.ts";
|
|
27
|
-
import type
|
|
28
|
-
import { type OracleFeeSource, type PythCache } from "./pyth.ts";
|
|
27
|
+
import { type OracleSource, type PriceUpdateRule, type UpdateDataProvider } from "./price-update-rule.ts";
|
|
29
28
|
import { type WaterxSignedEnvelope, type WaterxSignedLeaf } from "./rules/waterx-rule.ts";
|
|
30
29
|
/**
|
|
31
30
|
* Aggregate one ticker's price into the shared `Oracle`: build a collector, feed
|
|
32
31
|
* every rule the ticker is configured for, then `aggregate`.
|
|
33
32
|
*
|
|
34
|
-
* - **Pyth** — fed when `priceInfoObjectId` is supplied (i.e. the ticker has a
|
|
35
|
-
* `pyth_rule.feeds` entry). When this PTB's update leg refreshed the
|
|
36
|
-
* `PriceInfoObject` it contributes a fresh price; when it did not (a
|
|
37
|
-
* lazer-routed ticker), the on-chain rule only READS the object and abstains
|
|
38
|
-
* if it is stale — it never aborts — so the call stays mandatory while
|
|
39
|
-
* `pyth_rule` remains in the ticker's on-chain weighted set
|
|
40
|
-
* (`EMissingPriceSource` requires every weighted rule to appear).
|
|
41
33
|
* - **Lazer** — fed when `lazerUpdate` is supplied: the verified update this
|
|
42
34
|
* PTB's lazer update leg produced with the network's verify entry
|
|
43
35
|
* (`update_v2::Update` on mainnet, `update::Update` on testnet — see
|
|
44
36
|
* `PythLazerRule.buildUpdateCalls`). If the ticker's aggregator does
|
|
45
37
|
* not (yet) weight `PythLazerRule`, the contribution is silently dropped
|
|
46
38
|
* on-chain — feeding ahead of the weight migration is harmless.
|
|
47
|
-
* - **
|
|
39
|
+
* - **Waterx** — fed when `waterxLeaf` (default shape) or `waterxEnvelope`
|
|
40
|
+
* (fallback shape) is supplied; verify AND feed are bundled into the one
|
|
41
|
+
* collect call per collector.
|
|
42
|
+
* - **Supra** — fed alongside the sources when supra is enabled + wired
|
|
48
43
|
* (abstains on-chain for symbols it has no pair for).
|
|
49
44
|
* - **Constant** — fed when the ticker is a constant ticker
|
|
50
45
|
* ({@link OracleHost.isConstantTicker}).
|
|
51
46
|
*
|
|
52
|
-
* "Dual-feed" (
|
|
47
|
+
* "Dual-feed" (Lazer + Constant, or Lazer + Waterx) and "constant-only" are not
|
|
53
48
|
* special cases — they fall out of which rules the ticker is in. Throws if no
|
|
54
49
|
* rule applies.
|
|
55
50
|
*/
|
|
56
51
|
export declare function aggregateTicker(tx: Transaction, host: OracleHost, args: {
|
|
57
52
|
ticker: string;
|
|
58
|
-
priceInfoObjectId?: string;
|
|
59
53
|
lazerUpdate?: TransactionArgument;
|
|
60
54
|
/** This ticker's signed Merkle leaf — the default waterx shape. */
|
|
61
55
|
waterxLeaf?: WaterxSignedLeaf;
|
|
@@ -63,31 +57,35 @@ export declare function aggregateTicker(tx: Transaction, host: OracleHost, args:
|
|
|
63
57
|
waterxEnvelope?: WaterxSignedEnvelope;
|
|
64
58
|
}): void;
|
|
65
59
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
60
|
+
* {@link aggregateTicker} for a **constant-only** ticker (no source update
|
|
61
|
+
* needed — the price comes from the on-chain `constant_rule::Config`). Kept as
|
|
62
|
+
* a named entry so constant-only call sites (e.g. WLP builders refreshing the
|
|
63
|
+
* USDCUSD pool token) read as what they are; it adds nothing over
|
|
64
|
+
* `aggregateTicker(tx, host, { ticker })`.
|
|
68
65
|
*/
|
|
69
|
-
export declare function
|
|
66
|
+
export declare function aggregateTickerWithConstant(tx: Transaction, host: OracleHost, args: {
|
|
70
67
|
ticker: string;
|
|
71
|
-
priceInfoObjectId: string;
|
|
72
68
|
}): void;
|
|
73
69
|
/**
|
|
74
|
-
* {@link
|
|
75
|
-
* the price comes from the on-chain `constant_rule::Config`).
|
|
70
|
+
* What {@link refreshOraclePrices} actually put on chain.
|
|
76
71
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
72
|
+
* `skipped` holds the requested tickers NO listed source can price (and that
|
|
73
|
+
* `constant_rule` does not pin). They got no collector and no aggregate, so
|
|
74
|
+
* their on-chain price is whatever a previous transaction left — which is why
|
|
75
|
+
* every caller whose ACTION depends on a ticker must check this rather than
|
|
76
|
+
* assume the refresh covered its whole request.
|
|
82
77
|
*/
|
|
83
|
-
export
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
export interface OracleRefreshSummary {
|
|
79
|
+
/** Tickers aggregated in this PTB — includes constant-pinned ones. */
|
|
80
|
+
refreshed: string[];
|
|
81
|
+
/** Requested tickers no listed source serves. */
|
|
82
|
+
skipped: string[];
|
|
83
|
+
}
|
|
86
84
|
/**
|
|
87
85
|
* Refresh multiple tickers in one PTB. For each ticker {@link aggregateTicker}
|
|
88
|
-
* feeds whichever rules it is configured for (
|
|
89
|
-
*
|
|
90
|
-
* enabled, Constant when it's a constant ticker).
|
|
86
|
+
* feeds whichever rules it is configured for (Lazer if the lazer update leg
|
|
87
|
+
* served it, Waterx if the waterx leg fetched signed data for it — see below —
|
|
88
|
+
* Supra when enabled, Constant when it's a constant ticker).
|
|
91
89
|
*
|
|
92
90
|
* Before that, the on-chain price *update* leg is routed by the
|
|
93
91
|
* `host.oracleSources` fed set (see `rule-registry.ts`): EVERY listed source
|
|
@@ -101,11 +99,15 @@ export declare function aggregateTickerWithConstant(tx: Transaction, host: Oracl
|
|
|
101
99
|
* source's feeds is caught here.
|
|
102
100
|
*
|
|
103
101
|
* Each source's fetch + build runs against its own infra, guaranteeing
|
|
104
|
-
* per-rule PTB atomicity. A
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* moveCalls.
|
|
102
|
+
* per-rule PTB atomicity. A credential pre-check runs early: any group that
|
|
103
|
+
* still needs to fetch, whose rule declares a `credential` the host does not
|
|
104
|
+
* carry while `host.pyth.api_key` is unset, throws `LazerApiKeyMissing`
|
|
105
|
+
* BEFORE any ORACLE fetch or PTB mutation — zero wasted oracle calls, zero
|
|
106
|
+
* stray moveCalls. (With an `updateDataProvider` configured, its per-source
|
|
107
|
+
* lookups run first so the check can be scoped to the groups the cache did
|
|
108
|
+
* NOT serve; a consumer-implemented provider may do I/O of its own, so the
|
|
109
|
+
* guarantee is about oracle fetches, not about every possible round trip.)
|
|
110
|
+
* Only once that check passes do the off-chain fetches run — in
|
|
109
111
|
* parallel across sources — and ALL settle before the first PTB mutation;
|
|
110
112
|
* on-chain reads inside `buildUpdateCalls` can still fail mid-append for
|
|
111
113
|
* other reasons — callers discard the tx on any throw.
|
|
@@ -114,32 +116,14 @@ export declare function aggregateTickerWithConstant(tx: Transaction, host: Oracl
|
|
|
114
116
|
* `buildUpdateCalls` returns the verified `Update` PTB value
|
|
115
117
|
* ({@link RuleUpdateHandle}), and every ticker in that group is aggregated
|
|
116
118
|
* with `lazerUpdate` set so {@link aggregateTicker} appends
|
|
117
|
-
* `pyth_lazer_rule::feed` against it. A
|
|
118
|
-
*
|
|
119
|
-
* on
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* this PTB never refreshed and abstains when it is stale rather than
|
|
124
|
-
* aborting. Conversely, a lazer feed call on an aggregator that does not
|
|
125
|
-
* (yet) weight `PythLazerRule` is silently dropped on-chain — so
|
|
126
|
-
* lazer-routing a ticker ahead of its on-chain weight migration prices it
|
|
127
|
-
* from the remaining weighted rules instead of failing.
|
|
119
|
+
* `pyth_lazer_rule::feed` against it. A waterx-served group's signed data is
|
|
120
|
+
* carried straight from its fetched payload to the per-ticker collect call.
|
|
121
|
+
* A feed call on an aggregator that does not (yet) weight that rule is
|
|
122
|
+
* silently dropped on-chain — so routing a ticker ahead of its on-chain
|
|
123
|
+
* weight migration prices it from the remaining weighted rules instead of
|
|
124
|
+
* failing.
|
|
128
125
|
*/
|
|
129
126
|
export declare function refreshOraclePrices(tx: Transaction, host: OracleHost, tickers: string[], opts?: {
|
|
130
|
-
cache?: PythCache;
|
|
131
|
-
/**
|
|
132
|
-
* The single resolved fee source for the Pyth update fee, forwarded
|
|
133
|
-
* verbatim to each group's `PriceUpdateRule.buildUpdateCalls` as
|
|
134
|
-
* `BuildUpdateOpts.feeSource`. Already-resolved by the caller (see
|
|
135
|
-
* {@link OracleFeeSource}'s own doc for where/how) — this function makes
|
|
136
|
-
* no sponsor-vs-gas decision itself, it only checks whether a source was
|
|
137
|
-
* resolved at all. Ignored by rules with no update fee (e.g.
|
|
138
|
-
* `pyth_lazer_rule`). Building with `feeSource` unset throws
|
|
139
|
-
* `OracleFeeSourceUnavailable` (see `oracle/pyth.ts`) instead of
|
|
140
|
-
* silently drawing from `tx.gas`.
|
|
141
|
-
*/
|
|
142
|
-
feeSource?: OracleFeeSource;
|
|
143
127
|
/**
|
|
144
128
|
* @internal Test-only: layer fake `PriceUpdateRule`s on top of the
|
|
145
129
|
* production registry (see `rule-registry.ts`'s `resolveOracleRule`).
|
|
@@ -156,4 +140,4 @@ export declare function refreshOraclePrices(tx: Transaction, host: OracleHost, t
|
|
|
156
140
|
* payload) throws instead, since that is a caller bug, not a cache miss.
|
|
157
141
|
*/
|
|
158
142
|
updateDataProvider?: UpdateDataProvider;
|
|
159
|
-
}): Promise<
|
|
143
|
+
}): Promise<OracleRefreshSummary>;
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* Oracle aggregation — the orchestrator that composes rules into the shared
|
|
4
4
|
* `Oracle`. This is the ONE file that knows about every rule: it builds a
|
|
5
5
|
* `PriceCollector`, feeds whichever rules a ticker is configured for
|
|
6
|
-
* (
|
|
6
|
+
* (Lazer / Waterx / Supra / Constant), then `aggregate`s.
|
|
7
7
|
*
|
|
8
8
|
* Per ticker:
|
|
9
9
|
* collector = oracle::new_collector(ticker)
|
|
10
|
-
* [pyth_rule::feed] when the ticker has a pyth_rule.feeds entry
|
|
11
10
|
* [pyth_lazer_rule::feed] when the update leg produced a verified lazer Update
|
|
11
|
+
* [waterx_rule::collect_*] when the update leg fetched signed waterx data
|
|
12
12
|
* [supra_rule::feed] when supra is enabled + wired
|
|
13
13
|
* [constant_rule::feed] when the ticker is a constant ticker
|
|
14
14
|
* oracle::aggregate(oracle, collector)
|
|
@@ -25,18 +25,16 @@
|
|
|
25
25
|
*/
|
|
26
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
27
|
exports.aggregateTicker = aggregateTicker;
|
|
28
|
-
exports.aggregateTickerWithPyth = aggregateTickerWithPyth;
|
|
29
28
|
exports.aggregateTickerWithConstant = aggregateTickerWithConstant;
|
|
30
29
|
exports.refreshOraclePrices = refreshOraclePrices;
|
|
31
30
|
const oracle_ts_1 = require("../generated/waterx_oracle/oracle.js");
|
|
32
|
-
const
|
|
33
|
-
const pyth_ts_1 = require("./pyth.js");
|
|
31
|
+
const price_update_rule_ts_1 = require("./price-update-rule.js");
|
|
34
32
|
const rule_registry_ts_1 = require("./rule-registry.js");
|
|
35
33
|
const constant_rule_ts_1 = require("./rules/constant-rule.js");
|
|
36
34
|
const pyth_lazer_rule_ts_1 = require("./rules/pyth-lazer-rule.js");
|
|
37
|
-
const pyth_rule_ts_1 = require("./rules/pyth-rule.js");
|
|
38
35
|
const supra_rule_ts_1 = require("./rules/supra-rule.js");
|
|
39
36
|
const waterx_rule_ts_1 = require("./rules/waterx-rule.js");
|
|
37
|
+
const validate_ts_1 = require("./validate.js");
|
|
40
38
|
/**
|
|
41
39
|
* Resolve one group's off-chain update payload for {@link refreshOraclePrices}:
|
|
42
40
|
* try `provider.get(source, tickers)` first (when a provider is configured),
|
|
@@ -48,60 +46,53 @@ const waterx_rule_ts_1 = require("./rules/waterx-rule.js");
|
|
|
48
46
|
* provider commonly caches one whole-universe payload per source — see
|
|
49
47
|
* {@link UpdateDataProvider}), so it is narrowed to exactly `group.tickers`
|
|
50
48
|
* via `rule.narrowUpdateData` before use. This is load-bearing, not
|
|
51
|
-
* defensive:
|
|
52
|
-
* `
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* payload), so that throws — via `narrowUpdateData`'s own
|
|
49
|
+
* defensive: a payload that cannot cover the group (`narrowUpdateData` →
|
|
50
|
+
* `null`) would never reach the live-fetch fallback without it, and a
|
|
51
|
+
* divisible payload (waterx leaves) is subset instead of fanned out whole.
|
|
52
|
+
* Each rule owns its own subsetting (waterx subsets per-symbol leaves;
|
|
53
|
+
* Lazer's indivisible payload passes whole iff fully covered), so the
|
|
54
|
+
* orchestrator never branches on `kind` here. A hit whose `kind` doesn't
|
|
55
|
+
* match the group's rule is a caller bug (the provider handed back a
|
|
56
|
+
* different rule's payload), so that throws — via `narrowUpdateData`'s own
|
|
60
57
|
* `assertRuleUpdateData` guard — instead of silently falling back.
|
|
61
58
|
*/
|
|
62
|
-
async function
|
|
63
|
-
if (provider)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Wrong-kind hit throws inside narrowUpdateData (assertRuleUpdateData);
|
|
74
|
-
// a hit that can't cover the group narrows to null → live-fetch below.
|
|
75
|
-
const narrowed = group.rule.narrowUpdateData(host, cached, group.tickers);
|
|
76
|
-
if (narrowed !== null)
|
|
77
|
-
return narrowed;
|
|
78
|
-
}
|
|
59
|
+
async function resolveCachedUpdateData(host, group, provider) {
|
|
60
|
+
if (!provider)
|
|
61
|
+
return null;
|
|
62
|
+
let cached;
|
|
63
|
+
try {
|
|
64
|
+
cached = await provider.get(group.source, group.tickers);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Provider errors must never break the money path — report a miss and let
|
|
68
|
+
// the caller live-fetch, exactly as a `null` return would.
|
|
69
|
+
return null;
|
|
79
70
|
}
|
|
80
|
-
|
|
71
|
+
if (cached === null)
|
|
72
|
+
return null;
|
|
73
|
+
// Wrong-kind hit throws inside narrowUpdateData (assertRuleUpdateData); a
|
|
74
|
+
// hit that can't cover the group narrows to null, i.e. a miss.
|
|
75
|
+
return group.rule.narrowUpdateData(host, cached, group.tickers);
|
|
81
76
|
}
|
|
82
77
|
/**
|
|
83
78
|
* Aggregate one ticker's price into the shared `Oracle`: build a collector, feed
|
|
84
79
|
* every rule the ticker is configured for, then `aggregate`.
|
|
85
80
|
*
|
|
86
|
-
* - **Pyth** — fed when `priceInfoObjectId` is supplied (i.e. the ticker has a
|
|
87
|
-
* `pyth_rule.feeds` entry). When this PTB's update leg refreshed the
|
|
88
|
-
* `PriceInfoObject` it contributes a fresh price; when it did not (a
|
|
89
|
-
* lazer-routed ticker), the on-chain rule only READS the object and abstains
|
|
90
|
-
* if it is stale — it never aborts — so the call stays mandatory while
|
|
91
|
-
* `pyth_rule` remains in the ticker's on-chain weighted set
|
|
92
|
-
* (`EMissingPriceSource` requires every weighted rule to appear).
|
|
93
81
|
* - **Lazer** — fed when `lazerUpdate` is supplied: the verified update this
|
|
94
82
|
* PTB's lazer update leg produced with the network's verify entry
|
|
95
83
|
* (`update_v2::Update` on mainnet, `update::Update` on testnet — see
|
|
96
84
|
* `PythLazerRule.buildUpdateCalls`). If the ticker's aggregator does
|
|
97
85
|
* not (yet) weight `PythLazerRule`, the contribution is silently dropped
|
|
98
86
|
* on-chain — feeding ahead of the weight migration is harmless.
|
|
99
|
-
* - **
|
|
87
|
+
* - **Waterx** — fed when `waterxLeaf` (default shape) or `waterxEnvelope`
|
|
88
|
+
* (fallback shape) is supplied; verify AND feed are bundled into the one
|
|
89
|
+
* collect call per collector.
|
|
90
|
+
* - **Supra** — fed alongside the sources when supra is enabled + wired
|
|
100
91
|
* (abstains on-chain for symbols it has no pair for).
|
|
101
92
|
* - **Constant** — fed when the ticker is a constant ticker
|
|
102
93
|
* ({@link OracleHost.isConstantTicker}).
|
|
103
94
|
*
|
|
104
|
-
* "Dual-feed" (
|
|
95
|
+
* "Dual-feed" (Lazer + Constant, or Lazer + Waterx) and "constant-only" are not
|
|
105
96
|
* special cases — they fall out of which rules the ticker is in. Throws if no
|
|
106
97
|
* rule applies.
|
|
107
98
|
*/
|
|
@@ -112,10 +103,6 @@ function aggregateTicker(tx, host, args) {
|
|
|
112
103
|
arguments: { symbol: args.ticker },
|
|
113
104
|
})(tx);
|
|
114
105
|
let fed = false;
|
|
115
|
-
if (args.priceInfoObjectId) {
|
|
116
|
-
(0, pyth_rule_ts_1.feedPythRule)(tx, host, collector, args.priceInfoObjectId);
|
|
117
|
-
fed = true;
|
|
118
|
-
}
|
|
119
106
|
if (args.lazerUpdate !== undefined) {
|
|
120
107
|
(0, pyth_lazer_rule_ts_1.feedLazerRule)(tx, host, collector, args.lazerUpdate);
|
|
121
108
|
fed = true;
|
|
@@ -151,7 +138,7 @@ function aggregateTicker(tx, host, args) {
|
|
|
151
138
|
fed = true;
|
|
152
139
|
}
|
|
153
140
|
if (!fed) {
|
|
154
|
-
throw new Error(`no oracle rule configured for ticker '${args.ticker}' (no
|
|
141
|
+
throw new Error(`no oracle rule configured for ticker '${args.ticker}' (no lazer update, no waterx data, not a constant ticker)`);
|
|
155
142
|
}
|
|
156
143
|
(0, oracle_ts_1.aggregate)({
|
|
157
144
|
package: oraclePkg,
|
|
@@ -162,33 +149,20 @@ function aggregateTicker(tx, host, args) {
|
|
|
162
149
|
})(tx);
|
|
163
150
|
}
|
|
164
151
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* {@link aggregateTicker} for a **constant-only** ticker (no Pyth update needed —
|
|
173
|
-
* the price comes from the on-chain `constant_rule::Config`).
|
|
174
|
-
*
|
|
175
|
-
* Throws if the ticker ALSO has a `pyth_rule.feeds` entry (a dual-feed transition
|
|
176
|
-
* ticker): feeding only the constant leg would leave the still-weighted Pyth rule
|
|
177
|
-
* absent from the collector and abort `aggregate` with `EMissingPriceSource`. Such
|
|
178
|
-
* tickers must go through {@link aggregateTicker} with a `priceInfoObjectId` (or
|
|
179
|
-
* {@link refreshOraclePrices}), which feeds both.
|
|
152
|
+
* {@link aggregateTicker} for a **constant-only** ticker (no source update
|
|
153
|
+
* needed — the price comes from the on-chain `constant_rule::Config`). Kept as
|
|
154
|
+
* a named entry so constant-only call sites (e.g. WLP builders refreshing the
|
|
155
|
+
* USDCUSD pool token) read as what they are; it adds nothing over
|
|
156
|
+
* `aggregateTicker(tx, host, { ticker })`.
|
|
180
157
|
*/
|
|
181
158
|
function aggregateTickerWithConstant(tx, host, args) {
|
|
182
|
-
if ((0, record_ts_1.ownEntry)(host.config.packages.pyth_rule?.feeds, args.ticker) !== undefined) {
|
|
183
|
-
throw new Error(`'${args.ticker}' is in pyth_rule.feeds (dual-feed) — feed both via aggregateTicker({ priceInfoObjectId }) / refreshOraclePrices, not aggregateTickerWithConstant`);
|
|
184
|
-
}
|
|
185
159
|
aggregateTicker(tx, host, { ticker: args.ticker });
|
|
186
160
|
}
|
|
187
161
|
/**
|
|
188
162
|
* Refresh multiple tickers in one PTB. For each ticker {@link aggregateTicker}
|
|
189
|
-
* feeds whichever rules it is configured for (
|
|
190
|
-
*
|
|
191
|
-
* enabled, Constant when it's a constant ticker).
|
|
163
|
+
* feeds whichever rules it is configured for (Lazer if the lazer update leg
|
|
164
|
+
* served it, Waterx if the waterx leg fetched signed data for it — see below —
|
|
165
|
+
* Supra when enabled, Constant when it's a constant ticker).
|
|
192
166
|
*
|
|
193
167
|
* Before that, the on-chain price *update* leg is routed by the
|
|
194
168
|
* `host.oracleSources` fed set (see `rule-registry.ts`): EVERY listed source
|
|
@@ -202,11 +176,15 @@ function aggregateTickerWithConstant(tx, host, args) {
|
|
|
202
176
|
* source's feeds is caught here.
|
|
203
177
|
*
|
|
204
178
|
* Each source's fetch + build runs against its own infra, guaranteeing
|
|
205
|
-
* per-rule PTB atomicity. A
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
* moveCalls.
|
|
179
|
+
* per-rule PTB atomicity. A credential pre-check runs early: any group that
|
|
180
|
+
* still needs to fetch, whose rule declares a `credential` the host does not
|
|
181
|
+
* carry while `host.pyth.api_key` is unset, throws `LazerApiKeyMissing`
|
|
182
|
+
* BEFORE any ORACLE fetch or PTB mutation — zero wasted oracle calls, zero
|
|
183
|
+
* stray moveCalls. (With an `updateDataProvider` configured, its per-source
|
|
184
|
+
* lookups run first so the check can be scoped to the groups the cache did
|
|
185
|
+
* NOT serve; a consumer-implemented provider may do I/O of its own, so the
|
|
186
|
+
* guarantee is about oracle fetches, not about every possible round trip.)
|
|
187
|
+
* Only once that check passes do the off-chain fetches run — in
|
|
210
188
|
* parallel across sources — and ALL settle before the first PTB mutation;
|
|
211
189
|
* on-chain reads inside `buildUpdateCalls` can still fail mid-append for
|
|
212
190
|
* other reasons — callers discard the tx on any throw.
|
|
@@ -215,21 +193,16 @@ function aggregateTickerWithConstant(tx, host, args) {
|
|
|
215
193
|
* `buildUpdateCalls` returns the verified `Update` PTB value
|
|
216
194
|
* ({@link RuleUpdateHandle}), and every ticker in that group is aggregated
|
|
217
195
|
* with `lazerUpdate` set so {@link aggregateTicker} appends
|
|
218
|
-
* `pyth_lazer_rule::feed` against it. A
|
|
219
|
-
*
|
|
220
|
-
* on
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* this PTB never refreshed and abstains when it is stale rather than
|
|
225
|
-
* aborting. Conversely, a lazer feed call on an aggregator that does not
|
|
226
|
-
* (yet) weight `PythLazerRule` is silently dropped on-chain — so
|
|
227
|
-
* lazer-routing a ticker ahead of its on-chain weight migration prices it
|
|
228
|
-
* from the remaining weighted rules instead of failing.
|
|
196
|
+
* `pyth_lazer_rule::feed` against it. A waterx-served group's signed data is
|
|
197
|
+
* carried straight from its fetched payload to the per-ticker collect call.
|
|
198
|
+
* A feed call on an aggregator that does not (yet) weight that rule is
|
|
199
|
+
* silently dropped on-chain — so routing a ticker ahead of its on-chain
|
|
200
|
+
* weight migration prices it from the remaining weighted rules instead of
|
|
201
|
+
* failing.
|
|
229
202
|
*/
|
|
230
203
|
async function refreshOraclePrices(tx, host, tickers, opts = {}) {
|
|
231
204
|
if (tickers.length === 0)
|
|
232
|
-
return;
|
|
205
|
+
return { refreshed: [], skipped: [] };
|
|
233
206
|
// Dedupe the caller's list (order-preserving): a repeated ticker would
|
|
234
207
|
// otherwise aggregate TWICE in this one PTB — wasted gas for every rule, and
|
|
235
208
|
// under waterx the second collect would be dead weight on top of that: the
|
|
@@ -237,13 +210,7 @@ async function refreshOraclePrices(tx, host, tickers, opts = {}) {
|
|
|
237
210
|
// high-water mark from earlier in this same transaction and abstains, so the
|
|
238
211
|
// repeat pays full verification cost to contribute nothing.
|
|
239
212
|
tickers = [...new Set(tickers)];
|
|
240
|
-
//
|
|
241
|
-
// needed by aggregateTicker's (unchanged) Pyth feed step below regardless of
|
|
242
|
-
// which rule performed the on-chain update for that ticker.
|
|
243
|
-
const pythTickers = tickers.filter((t) => (0, record_ts_1.ownEntry)(host.config.packages.pyth_rule?.feeds, t) !== undefined);
|
|
244
|
-
const priceInfoByTicker = new Map();
|
|
245
|
-
pythTickers.forEach((t) => priceInfoByTicker.set(t, host.getPythFeed(t).price_info_object));
|
|
246
|
-
// The fed set is a LIST (`host.oracleSources`, normalized + deduped at
|
|
213
|
+
// The fed set is a LIST (`host.oracleSources`, derived from the config at
|
|
247
214
|
// client creation): ONE build carries every listed source's data, and the
|
|
248
215
|
// chain's per-ticker weight tables decide which contributions count —
|
|
249
216
|
// feeding an unweighted rule's PRICE is dropped on-chain, while starving a
|
|
@@ -253,8 +220,8 @@ async function refreshOraclePrices(tx, host, tickers, opts = {}) {
|
|
|
253
220
|
// burns a per-symbol signed-timestamp high-water mark regardless of
|
|
254
221
|
// weights — see aggregateTicker's waterx branch.) Still NO fallback
|
|
255
222
|
// BETWEEN sources: each group serves only the tickers its own feeds list.
|
|
256
|
-
// Zero-ticker groups are dropped here so everything downstream (
|
|
257
|
-
// fetch fan-out, update-leg build) can assume every group has work.
|
|
223
|
+
// Zero-ticker groups are dropped here so everything downstream (credential
|
|
224
|
+
// check, fetch fan-out, update-leg build) can assume every group has work.
|
|
258
225
|
const groups = host.oracleSources
|
|
259
226
|
.map((source) => {
|
|
260
227
|
const rule = (0, rule_registry_ts_1.resolveOracleRule)(source, opts.ruleOverrides);
|
|
@@ -262,44 +229,82 @@ async function refreshOraclePrices(tx, host, tickers, opts = {}) {
|
|
|
262
229
|
return { source, rule, tickers: tickers.filter((t) => supported.has(t)) };
|
|
263
230
|
})
|
|
264
231
|
.filter((group) => group.tickers.length > 0);
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
//
|
|
232
|
+
// A ticker no listed source can price is SKIPPED, not thrown on, and named
|
|
233
|
+
// in the returned summary.
|
|
234
|
+
//
|
|
235
|
+
// This is a broad primitive: callers sweep whole market lists through it, and
|
|
236
|
+
// losing 29 tickers because the 30th is unconfigured is the wrong trade — the
|
|
237
|
+
// 29 still need their prices on chain. Safety lives one level up, where the
|
|
238
|
+
// ACTION is known: the `build*Tx` composers fail closed on the tickers their
|
|
239
|
+
// specific call actually depends on (`assertTickersRefreshed` /
|
|
240
|
+
// `assertWlpPoolRefreshed` in `perp/tx-builders/common.ts`). A bare
|
|
241
|
+
// `refreshOraclePrices` caller composing its own PTB reads `skipped` and
|
|
242
|
+
// decides for itself.
|
|
243
|
+
//
|
|
244
|
+
// Exemption: a CONSTANT-ONLY ticker needs no update leg from any source, so
|
|
245
|
+
// it counts as refreshed with just its constant feed.
|
|
246
|
+
//
|
|
247
|
+
// "Constant-only" is deliberately stricter than "constant-pinned". A ticker
|
|
248
|
+
// that constant_rule pins AND some other rule also has a feed for is NOT
|
|
249
|
+
// exempt, even when that other rule is outside the fed set: the on-chain
|
|
250
|
+
// aggregator very likely weights the rule whose feed the config carries, and
|
|
251
|
+
// aggregating a constant-only collector for it aborts `EMissingPriceSource`.
|
|
252
|
+
// Exempting on `isConstantTicker` alone would be correct only while the fed
|
|
253
|
+
// set covers every ticker's weighted set — an operator-maintained property
|
|
254
|
+
// this function cannot verify without reading chain state. So the strict
|
|
255
|
+
// reading fails SAFE: the ticker is skipped, named, and the composers turn
|
|
256
|
+
// that into a build error instead of an opaque on-chain abort.
|
|
257
|
+
//
|
|
258
|
+
// Catches a MISSING feed only; a present-but-WRONG feed id is deliberately
|
|
259
|
+
// left to abort on-chain at dry-run.
|
|
276
260
|
const covered = new Set(groups.flatMap((group) => group.tickers));
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
261
|
+
const { servable: refreshed, unservable: skipped } = (0, validate_ts_1.partitionServableTickers)(host, tickers, covered);
|
|
262
|
+
// Credential pre-check, hoisted ABOVE the oracle fetches and PTB build below
|
|
263
|
+
// (the position the retired fee-source pre-check held). It consults only
|
|
264
|
+
// `rule.credential` — known without fetching anything — so a keyless build
|
|
265
|
+
// against an auth-first source (Lazer) in the fed set throws with ZERO
|
|
266
|
+
// wasted ORACLE calls and zero PTB commands, rather than waiting for that
|
|
267
|
+
// group's own fetch guard to fire after sibling groups' fetches already ran.
|
|
268
|
+
// Fully generic: the kind→value mapping is the port's
|
|
269
|
+
// (`oracleCredentialsFromHost`) and the ERROR is the rule's own, so this
|
|
270
|
+
// loop names neither a credential kind nor a rule.
|
|
271
|
+
//
|
|
272
|
+
// It does NOT run before absolutely everything, and cannot: scoping it to
|
|
273
|
+
// the groups that still need to fetch means knowing which ones the cache
|
|
274
|
+
// served, and `UpdateDataProvider` is consumer-implemented, so its lookup
|
|
275
|
+
// may do I/O. That is the trade — a provider round trip may precede the
|
|
276
|
+
// throw, an oracle fetch never does — and it buys the scoping below.
|
|
277
|
+
//
|
|
278
|
+
// The check is scoped to the groups that will actually FETCH. An
|
|
279
|
+
// `updateDataProvider` is a per-SOURCE cache (`get(source, tickers)`), so a
|
|
280
|
+
// consumer holding the credential out-of-band for one source must not
|
|
281
|
+
// exempt the whole fed set — with a waterx-only cache and a keyless Lazer
|
|
282
|
+
// group, a blanket skip would let the quote-center GET fire before the
|
|
283
|
+
// Lazer group failed. So cache lookups (no network) run first, and only the
|
|
284
|
+
// groups that missed are credential-checked.
|
|
285
|
+
//
|
|
286
|
+
// Phase 0 — resolve cache hits. Cheap and network-free by contract.
|
|
287
|
+
const cachedByGroup = await Promise.all(groups.map((group) => resolveCachedUpdateData(host, group, opts.updateDataProvider)));
|
|
288
|
+
const needsFetch = groups.filter((_, i) => cachedByGroup[i] === null);
|
|
289
|
+
const credentials = (0, price_update_rule_ts_1.oracleCredentialsFromHost)(host);
|
|
290
|
+
for (const { rule } of needsFetch) {
|
|
291
|
+
if (rule.credential && !credentials[rule.credential.kind]) {
|
|
292
|
+
throw rule.credential.missing();
|
|
293
|
+
}
|
|
293
294
|
}
|
|
294
|
-
// Phase 1 —
|
|
295
|
-
// fetches (
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
//
|
|
295
|
+
// Phase 1 — live-fetch whatever the cache did not serve, IN PARALLEL: the
|
|
296
|
+
// per-source fetches (Lazer POST / quote-center GET) are independent network
|
|
297
|
+
// calls on the tx-build money path, so a multi-source fed set must not pay
|
|
298
|
+
// one RTT per source sequentially. ALL of them settle before the first PTB
|
|
299
|
+
// mutation below, so a fetch failure never strands moveCalls in a
|
|
299
300
|
// caller-owned tx — and a failure in ANY group fails the whole build (a
|
|
300
301
|
// listed source is load-bearing; silently building without it would starve
|
|
301
302
|
// its weighted tickers on-chain).
|
|
302
|
-
const
|
|
303
|
+
const fetched = new Map();
|
|
304
|
+
await Promise.all(needsFetch.map(async (group) => {
|
|
305
|
+
fetched.set(group.source, await group.rule.fetchUpdateData(host, group.tickers));
|
|
306
|
+
}));
|
|
307
|
+
const dataByGroup = groups.map((group, i) => cachedByGroup[i] ?? fetched.get(group.source) ?? null);
|
|
303
308
|
// Phase 2 — build each group's update leg sequentially, in list order, so
|
|
304
309
|
// PTB command order stays deterministic. The carry step below is an
|
|
305
310
|
// exhaustive switch over the group's rule kind: a future source whose feed
|
|
@@ -315,15 +320,8 @@ async function refreshOraclePrices(tx, host, tickers, opts = {}) {
|
|
|
315
320
|
const waterxEnvelopeByTicker = new Map();
|
|
316
321
|
for (const [i, group] of groups.entries()) {
|
|
317
322
|
const data = dataByGroup[i] ?? null;
|
|
318
|
-
const handle = (await group.rule.buildUpdateCalls(tx, host, data
|
|
319
|
-
cache: opts.cache,
|
|
320
|
-
feeSource: opts.feeSource,
|
|
321
|
-
})) ?? undefined;
|
|
323
|
+
const handle = (await group.rule.buildUpdateCalls(tx, host, data)) ?? undefined;
|
|
322
324
|
switch (group.rule.kind) {
|
|
323
|
-
case "pyth_rule":
|
|
324
|
-
// Core's update leg wrote the PriceInfoObjects in place — the feed
|
|
325
|
-
// leg reads them by id (`priceInfoByTicker`), nothing to carry.
|
|
326
|
-
break;
|
|
327
325
|
case "pyth_lazer_rule":
|
|
328
326
|
// Route by the handle's kind discriminant — the tag exists so a
|
|
329
327
|
// non-lazer handle can never be silently fed into
|
|
@@ -383,14 +381,16 @@ async function refreshOraclePrices(tx, host, tickers, opts = {}) {
|
|
|
383
381
|
}
|
|
384
382
|
}
|
|
385
383
|
}
|
|
386
|
-
// Aggregate each ticker, feeding whichever rules it is configured
|
|
387
|
-
for
|
|
384
|
+
// Aggregate each REFRESHED ticker, feeding whichever rules it is configured
|
|
385
|
+
// for. A skipped ticker gets no collector at all — aggregating one with no
|
|
386
|
+
// feed leg would write an empty collector and abort `EMissingPriceSource`.
|
|
387
|
+
for (const ticker of refreshed) {
|
|
388
388
|
aggregateTicker(tx, host, {
|
|
389
389
|
ticker,
|
|
390
|
-
priceInfoObjectId: priceInfoByTicker.get(ticker),
|
|
391
390
|
lazerUpdate: lazerUpdateByTicker.get(ticker),
|
|
392
391
|
waterxLeaf: waterxLeafByTicker.get(ticker),
|
|
393
392
|
waterxEnvelope: waterxEnvelopeByTicker.get(ticker),
|
|
394
393
|
});
|
|
395
394
|
}
|
|
395
|
+
return { refreshed, skipped };
|
|
396
396
|
}
|