@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.
Files changed (150) hide show
  1. package/.claude/skills/waterx-sdk-integration/SKILL.md +225 -0
  2. package/README.md +216 -45
  3. package/SKILLS.md +34 -0
  4. package/dist/cjs/src/account/config.d.ts +3 -5
  5. package/dist/cjs/src/account/funding/wormhole.d.ts +1 -2
  6. package/dist/cjs/src/account/funding/wormhole.js +1 -2
  7. package/dist/cjs/src/generated/waterx_rule/waterx_rule.d.ts +140 -7
  8. package/dist/cjs/src/generated/waterx_rule/waterx_rule.js +163 -8
  9. package/dist/cjs/src/oracle/aggregate.d.ts +49 -62
  10. package/dist/cjs/src/oracle/aggregate.js +208 -164
  11. package/dist/cjs/src/oracle/config.d.ts +38 -51
  12. package/dist/cjs/src/oracle/config.js +1 -1
  13. package/dist/cjs/src/oracle/host.d.ts +14 -19
  14. package/dist/cjs/src/oracle/host.js +3 -3
  15. package/dist/cjs/src/oracle/index.d.ts +25 -16
  16. package/dist/cjs/src/oracle/index.js +87 -69
  17. package/dist/cjs/src/oracle/price-update-rule.d.ts +104 -66
  18. package/dist/cjs/src/oracle/price-update-rule.js +24 -16
  19. package/dist/cjs/src/oracle/pyth-pro-history.d.ts +36 -0
  20. package/dist/cjs/src/oracle/pyth-pro-history.js +41 -0
  21. package/dist/cjs/src/oracle/read-plane.d.ts +35 -53
  22. package/dist/cjs/src/oracle/read-plane.js +26 -48
  23. package/dist/cjs/src/oracle/read-prices.d.ts +105 -0
  24. package/dist/cjs/src/oracle/read-prices.js +162 -0
  25. package/dist/cjs/src/oracle/rule-registry.d.ts +12 -12
  26. package/dist/cjs/src/oracle/rule-registry.js +12 -14
  27. package/dist/cjs/src/oracle/rules/pyth-lazer-rule.d.ts +24 -3
  28. package/dist/cjs/src/oracle/rules/pyth-lazer-rule.js +40 -20
  29. package/dist/cjs/src/oracle/rules/waterx-rule.d.ts +271 -57
  30. package/dist/cjs/src/oracle/rules/waterx-rule.js +743 -144
  31. package/dist/cjs/src/oracle/schedule.d.ts +109 -0
  32. package/dist/cjs/src/oracle/schedule.js +991 -0
  33. package/dist/cjs/src/oracle/source-list.d.ts +31 -27
  34. package/dist/cjs/src/oracle/source-list.js +43 -45
  35. package/dist/cjs/src/oracle/symbol-catalog.d.ts +54 -0
  36. package/dist/cjs/src/oracle/symbol-catalog.js +69 -0
  37. package/dist/cjs/src/oracle/update-fetch.d.ts +11 -15
  38. package/dist/cjs/src/oracle/update-fetch.js +12 -19
  39. package/dist/cjs/src/oracle/validate.d.ts +105 -0
  40. package/dist/cjs/src/oracle/validate.js +185 -0
  41. package/dist/cjs/src/oracle/weight-coverage.d.ts +79 -0
  42. package/dist/cjs/src/oracle/weight-coverage.js +173 -0
  43. package/dist/cjs/src/perp/client.d.ts +40 -55
  44. package/dist/cjs/src/perp/client.js +44 -17
  45. package/dist/cjs/src/perp/config-view.d.ts +5 -14
  46. package/dist/cjs/src/perp/config-view.js +5 -16
  47. package/dist/cjs/src/perp/config.d.ts +3 -3
  48. package/dist/cjs/src/perp/config.js +11 -13
  49. package/dist/cjs/src/perp/index.d.ts +4 -6
  50. package/dist/cjs/src/perp/index.js +43 -18
  51. package/dist/cjs/src/perp/tx-builders/common.d.ts +86 -48
  52. package/dist/cjs/src/perp/tx-builders/common.js +115 -47
  53. package/dist/cjs/src/perp/tx-builders/wlp.d.ts +3 -14
  54. package/dist/cjs/src/perp/tx-builders/wlp.js +6 -35
  55. package/dist/cjs/src/perp/tx-builders.d.ts +6 -5
  56. package/dist/cjs/src/perp/tx-builders.js +16 -10
  57. package/dist/cjs/src/unified-client.d.ts +9 -35
  58. package/dist/cjs/src/unified-client.js +3 -4
  59. package/dist/cjs/src/utils/config.d.ts +0 -8
  60. package/dist/cjs/src/utils/config.js +5 -13
  61. package/dist/src/account/config.d.ts +3 -5
  62. package/dist/src/account/funding/wormhole.d.ts +1 -2
  63. package/dist/src/account/funding/wormhole.js +1 -2
  64. package/dist/src/generated/waterx_rule/waterx_rule.d.ts +140 -7
  65. package/dist/src/generated/waterx_rule/waterx_rule.js +151 -7
  66. package/dist/src/oracle/aggregate.d.ts +49 -62
  67. package/dist/src/oracle/aggregate.js +209 -164
  68. package/dist/src/oracle/config.d.ts +38 -51
  69. package/dist/src/oracle/config.js +1 -1
  70. package/dist/src/oracle/host.d.ts +14 -19
  71. package/dist/src/oracle/host.js +3 -3
  72. package/dist/src/oracle/index.d.ts +25 -16
  73. package/dist/src/oracle/index.js +61 -54
  74. package/dist/src/oracle/price-update-rule.d.ts +104 -66
  75. package/dist/src/oracle/price-update-rule.js +23 -16
  76. package/dist/src/oracle/pyth-pro-history.d.ts +36 -0
  77. package/dist/src/oracle/pyth-pro-history.js +38 -0
  78. package/dist/src/oracle/read-plane.d.ts +35 -53
  79. package/dist/src/oracle/read-plane.js +25 -47
  80. package/dist/src/oracle/read-prices.d.ts +105 -0
  81. package/dist/src/oracle/read-prices.js +156 -0
  82. package/dist/src/oracle/rule-registry.d.ts +12 -12
  83. package/dist/src/oracle/rule-registry.js +12 -14
  84. package/dist/src/oracle/rules/pyth-lazer-rule.d.ts +24 -3
  85. package/dist/src/oracle/rules/pyth-lazer-rule.js +39 -20
  86. package/dist/src/oracle/rules/waterx-rule.d.ts +271 -57
  87. package/dist/src/oracle/rules/waterx-rule.js +735 -144
  88. package/dist/src/oracle/schedule.d.ts +109 -0
  89. package/dist/src/oracle/schedule.js +985 -0
  90. package/dist/src/oracle/source-list.d.ts +31 -27
  91. package/dist/src/oracle/source-list.js +42 -43
  92. package/dist/src/oracle/symbol-catalog.d.ts +54 -0
  93. package/dist/src/oracle/symbol-catalog.js +65 -0
  94. package/dist/src/oracle/update-fetch.d.ts +11 -15
  95. package/dist/src/oracle/update-fetch.js +12 -18
  96. package/dist/src/oracle/validate.d.ts +105 -0
  97. package/dist/src/oracle/validate.js +177 -0
  98. package/dist/src/oracle/weight-coverage.d.ts +79 -0
  99. package/dist/src/oracle/weight-coverage.js +166 -0
  100. package/dist/src/perp/client.d.ts +40 -55
  101. package/dist/src/perp/client.js +45 -18
  102. package/dist/src/perp/config-view.d.ts +5 -14
  103. package/dist/src/perp/config-view.js +5 -16
  104. package/dist/src/perp/config.d.ts +3 -3
  105. package/dist/src/perp/config.js +11 -13
  106. package/dist/src/perp/index.d.ts +4 -6
  107. package/dist/src/perp/index.js +17 -5
  108. package/dist/src/perp/tx-builders/common.d.ts +86 -48
  109. package/dist/src/perp/tx-builders/common.js +114 -48
  110. package/dist/src/perp/tx-builders/wlp.d.ts +3 -14
  111. package/dist/src/perp/tx-builders/wlp.js +6 -35
  112. package/dist/src/perp/tx-builders.d.ts +6 -5
  113. package/dist/src/perp/tx-builders.js +15 -6
  114. package/dist/src/unified-client.d.ts +9 -35
  115. package/dist/src/unified-client.js +3 -4
  116. package/dist/src/utils/config.d.ts +0 -8
  117. package/dist/src/utils/config.js +5 -12
  118. package/package.json +8 -2
  119. package/dist/cjs/src/generated/pyth_sponsor_rule/deps/sui/balance.d.ts +0 -12
  120. package/dist/cjs/src/generated/pyth_sponsor_rule/deps/sui/balance.js +0 -17
  121. package/dist/cjs/src/generated/pyth_sponsor_rule/pyth_sponsor_rule.d.ts +0 -68
  122. package/dist/cjs/src/generated/pyth_sponsor_rule/pyth_sponsor_rule.js +0 -113
  123. package/dist/cjs/src/generated/waterx_pyth_rule/deps/sui/vec_map.d.ts +0 -36
  124. package/dist/cjs/src/generated/waterx_pyth_rule/deps/sui/vec_map.js +0 -31
  125. package/dist/cjs/src/generated/waterx_pyth_rule/pyth_rule.d.ts +0 -192
  126. package/dist/cjs/src/generated/waterx_pyth_rule/pyth_rule.js +0 -214
  127. package/dist/cjs/src/oracle/pyth.d.ts +0 -227
  128. package/dist/cjs/src/oracle/pyth.js +0 -652
  129. package/dist/cjs/src/oracle/rules/pyth-core-rule.d.ts +0 -15
  130. package/dist/cjs/src/oracle/rules/pyth-core-rule.js +0 -100
  131. package/dist/cjs/src/oracle/rules/pyth-rule.d.ts +0 -11
  132. package/dist/cjs/src/oracle/rules/pyth-rule.js +0 -29
  133. package/dist/cjs/src/oracle/rules/sponsor.d.ts +0 -32
  134. package/dist/cjs/src/oracle/rules/sponsor.js +0 -56
  135. package/dist/src/generated/pyth_sponsor_rule/deps/sui/balance.d.ts +0 -12
  136. package/dist/src/generated/pyth_sponsor_rule/deps/sui/balance.js +0 -14
  137. package/dist/src/generated/pyth_sponsor_rule/pyth_sponsor_rule.d.ts +0 -68
  138. package/dist/src/generated/pyth_sponsor_rule/pyth_sponsor_rule.js +0 -73
  139. package/dist/src/generated/waterx_pyth_rule/deps/sui/vec_map.d.ts +0 -36
  140. package/dist/src/generated/waterx_pyth_rule/deps/sui/vec_map.js +0 -27
  141. package/dist/src/generated/waterx_pyth_rule/pyth_rule.d.ts +0 -192
  142. package/dist/src/generated/waterx_pyth_rule/pyth_rule.js +0 -170
  143. package/dist/src/oracle/pyth.d.ts +0 -227
  144. package/dist/src/oracle/pyth.js +0 -638
  145. package/dist/src/oracle/rules/pyth-core-rule.d.ts +0 -15
  146. package/dist/src/oracle/rules/pyth-core-rule.js +0 -97
  147. package/dist/src/oracle/rules/pyth-rule.d.ts +0 -11
  148. package/dist/src/oracle/rules/pyth-rule.js +0 -26
  149. package/dist/src/oracle/rules/sponsor.d.ts +0 -32
  150. package/dist/src/oracle/rules/sponsor.js +0 -52
@@ -19,27 +19,18 @@ export declare class PerpConfigView {
19
19
  getMarket(ticker: string): import("./config.ts").WaterxPerpMarketEntry;
20
20
  /** `waterx_oracle.aggregators[ticker]`, throws if unknown. */
21
21
  getAggregator(ticker: string): string;
22
- /** `pyth_rule.feeds[ticker]`, throws if unknown. */
23
- getPythFeed(ticker: string): {
24
- feed_id: string;
25
- price_info_object: string;
26
- };
27
22
  /**
28
23
  * True when `ticker` is priced by `constant_rule` (a constant pin,
29
- * e.g. `USDCUSD → $1`) rather than Pyth. Such tickers are fed via
30
- * `constant_rule::feed` and need no Pyth update; see {@link refreshOraclePrices}.
24
+ * e.g. `USDCUSD → $1`) rather than a live source. Such tickers are fed via
25
+ * `constant_rule::feed` and need no source update leg; see
26
+ * {@link refreshOraclePrices}.
31
27
  *
32
28
  * All-or-nothing, mirroring {@link getSupraRule} and the keeper: only routes a
33
29
  * ticker to the constant rule when the rule is FULLY wired (`published_at` +
34
30
  * `config` present). A half-populated block — a `feeds` entry listed before the
35
31
  * rule is deployed, a realistic mid-rollout state — would otherwise make this
36
- * true while {@link aggregateTicker} throws, aborting the whole price-refresh PTB
37
- * instead of safely falling back to Pyth.
38
- *
39
- * Whether a constant ticker is *also* Pyth-fed (the dual-feed transition state)
40
- * or constant-only is not a separate flag — it falls out of whether the ticker
41
- * still has a `pyth_rule.feeds` entry. {@link aggregateTicker} feeds each rule the
42
- * ticker is configured for, so no `isDualFeed` / `isConstantOnly` predicate is needed.
32
+ * true while {@link aggregateTicker} throws, aborting the whole price-refresh
33
+ * PTB instead of leaving the ticker to its live sources.
43
34
  */
44
35
  isConstantTicker(ticker: string): boolean;
45
36
  /**
@@ -39,29 +39,18 @@ export class PerpConfigView {
39
39
  throw new Error(`No aggregator listed for ticker: ${ticker}`);
40
40
  return a;
41
41
  }
42
- /** `pyth_rule.feeds[ticker]`, throws if unknown. */
43
- getPythFeed(ticker) {
44
- const f = ownEntry(this.config.packages.pyth_rule?.feeds, ticker);
45
- if (!f)
46
- throw new Error(`No pyth feed listed for ticker: ${ticker}`);
47
- return f;
48
- }
49
42
  /**
50
43
  * True when `ticker` is priced by `constant_rule` (a constant pin,
51
- * e.g. `USDCUSD → $1`) rather than Pyth. Such tickers are fed via
52
- * `constant_rule::feed` and need no Pyth update; see {@link refreshOraclePrices}.
44
+ * e.g. `USDCUSD → $1`) rather than a live source. Such tickers are fed via
45
+ * `constant_rule::feed` and need no source update leg; see
46
+ * {@link refreshOraclePrices}.
53
47
  *
54
48
  * All-or-nothing, mirroring {@link getSupraRule} and the keeper: only routes a
55
49
  * ticker to the constant rule when the rule is FULLY wired (`published_at` +
56
50
  * `config` present). A half-populated block — a `feeds` entry listed before the
57
51
  * rule is deployed, a realistic mid-rollout state — would otherwise make this
58
- * true while {@link aggregateTicker} throws, aborting the whole price-refresh PTB
59
- * instead of safely falling back to Pyth.
60
- *
61
- * Whether a constant ticker is *also* Pyth-fed (the dual-feed transition state)
62
- * or constant-only is not a separate flag — it falls out of whether the ticker
63
- * still has a `pyth_rule.feeds` entry. {@link aggregateTicker} feeds each rule the
64
- * ticker is configured for, so no `isDualFeed` / `isConstantOnly` predicate is needed.
52
+ * true while {@link aggregateTicker} throws, aborting the whole price-refresh
53
+ * PTB instead of leaving the ticker to its live sources.
65
54
  */
66
55
  isConstantTicker(ticker) {
67
56
  const c = this.config.packages.constant_rule;
@@ -5,15 +5,15 @@
5
5
  * The schema mirrors the canonical JSON layout one-to-one (each package
6
6
  * groups its own object IDs + per-ticker maps). External chain infra is
7
7
  * **not** in the JSON — each oracle source owns its own per-network table
8
- * (`PYTH_CORE_INFRA` in `oracle/pyth.ts`, `LAZER_INFRA` in
9
- * `oracle/rules/pyth-lazer-rule.ts`); Wormhole bridge infra lives in
8
+ * (`LAZER_INFRA` in `oracle/rules/pyth-lazer-rule.ts`, `WATERX_INFRA` in
9
+ * `oracle/rules/waterx-rule.ts`); Wormhole bridge infra lives in
10
10
  * `WORMHOLE_DEFAULTS` below.
11
11
  */
12
12
  import type { AccountPackages, BasePackageEntry, WormholeInfraConfig } from "../account/config.ts";
13
13
  import type { OraclePackages } from "../oracle/config.ts";
14
14
  import type { Network } from "./constants.ts";
15
15
  export type { AccountConfig, AccountPackages, BasePackageEntry, NativeCustodyAsset, NativeCustodyPackage, WaterxCreditPackage, WaterxReferralPackage, WithdrawalQueuePackage, WormholeBridgePackage, WormholeInfraConfig, WxaAccountPackage, } from "../account/config.ts";
16
- export type { ConstantFeedEntry, OracleConfig, OraclePackages, PythAccessConfig, PythFetchPolicy, PythLazerRulePackage, PythRulePackage, PythSponsorRulePackage, SupraFeedEntry, SupraRulePackage, WaterxConstantRulePackage, WaterxAccessConfig, WaterxOraclePackage, WaterxRulePackage, } from "../oracle/config.ts";
16
+ export type { ConstantFeedEntry, OracleConfig, OraclePackages, PythAccessConfig, PythFetchPolicy, PythLazerRulePackage, SupraFeedEntry, SupraRulePackage, WaterxConstantRulePackage, WaterxAccessConfig, WaterxOraclePackage, WaterxRulePackage, } from "../oracle/config.ts";
17
17
  export interface WaterxPerpMarketEntry {
18
18
  market: string;
19
19
  config: string;
@@ -5,8 +5,8 @@
5
5
  * The schema mirrors the canonical JSON layout one-to-one (each package
6
6
  * groups its own object IDs + per-ticker maps). External chain infra is
7
7
  * **not** in the JSON — each oracle source owns its own per-network table
8
- * (`PYTH_CORE_INFRA` in `oracle/pyth.ts`, `LAZER_INFRA` in
9
- * `oracle/rules/pyth-lazer-rule.ts`); Wormhole bridge infra lives in
8
+ * (`LAZER_INFRA` in `oracle/rules/pyth-lazer-rule.ts`, `WATERX_INFRA` in
9
+ * `oracle/rules/waterx-rule.ts`); Wormhole bridge infra lives in
10
10
  * `WORMHOLE_DEFAULTS` below.
11
11
  */
12
12
  import { fetchWithPolicy, rethrowExhaustedFetch } from "../oracle/update-fetch.js";
@@ -14,14 +14,12 @@ import { fetchWithPolicy, rethrowExhaustedFetch } from "../oracle/update-fetch.j
14
14
  // Wormhole — external chain infra, defaults by network
15
15
  // ============================================================================
16
16
  //
17
- // Oracle-source infra lives with each source (`PYTH_CORE_INFRA` in
18
- // `oracle/pyth.ts`, `LAZER_INFRA` in `oracle/rules/pyth-lazer-rule.ts`);
19
- // `client.pyth` is the caller-supplied access slice only (`PythAccessConfig`,
20
- // re-exported at the top of this file).
17
+ // Oracle-source infra lives with each source (`LAZER_INFRA` in
18
+ // `oracle/rules/pyth-lazer-rule.ts`, `WATERX_INFRA` in
19
+ // `oracle/rules/waterx-rule.ts`); `client.pyth` is the caller-supplied access
20
+ // slice only (`PythAccessConfig`, re-exported at the top of this file).
21
21
  // `WormholeInfraConfig` is defined in `account/config.ts` (funding base) and
22
- // re-exported at the top of this file. `state_id` is the same shared Sui
23
- // Wormhole `State` object Pyth uses (kept in sync with
24
- // `PYTH_CORE_INFRA[*].wormhole_state_id`).
22
+ // re-exported at the top of this file.
25
23
  export const WORMHOLE_DEFAULTS = {
26
24
  MAINNET: {
27
25
  state_id: "0xaeab97f96cf9877fee2883315d459552b2b921edc16d7ceac6eab944dd88919c",
@@ -141,15 +139,15 @@ function validateConfig(cfg, expected, url) {
141
139
  };
142
140
  // Validate by deployment kind. A perp config must carry the perp set; a
143
141
  // credit config the credit set. `waterx_account` is common to both.
144
- // `pyth_lazer_rule` is intentionally NOT required (and never will be by
145
- // presence alone) it's an optional/experimental package selected only via
146
- // a client's `oracleSource` option, never by its presence in the config.
142
+ // NO oracle-rule package is required by presence which rules run is the
143
+ // client's derived fed set, never a hand-declared list (and
144
+ // `pyth_rule`, once required here, was retired in 5.0.0 a config may
145
+ // carry or omit its block freely).
147
146
  const isPerp = "waterx_perp" in cfg.packages;
148
147
  const isCredit = "waterx_credit" in cfg.packages || "wormhole_bridge" in cfg.packages;
149
148
  const required = isPerp
150
149
  ? [
151
150
  "bucket_framework",
152
- "pyth_rule",
153
151
  "waterx_account",
154
152
  "waterx_oracle",
155
153
  "waterx_perp",
@@ -1,10 +1,10 @@
1
1
  export { PerpClient } from "./client.ts";
2
2
  export type { CreateClientOptions } from "./client.ts";
3
3
  export { WORMHOLE_DEFAULTS, clearConfigCache, loadConfig } from "./config.ts";
4
- export type { BasePackageEntry, ConstantFeedEntry, WaterxReferralPackage, LoadConfigOptions, NativeCustodyAsset, NativeCustodyPackage, PythAccessConfig, PythFetchPolicy, PythLazerRulePackage, PythRulePackage, PythSponsorRulePackage, SupraFeedEntry, SupraRulePackage, TestnetFaucetPackage, WaterXConfig, WaterXPackages, WaterxCreditPackage, WaterxOraclePackage, WaterxPerpMarketEntry, WaterxPerpPackage, WaterxStakingPackage, WithdrawalQueuePackage, WlpPackage, WormholeBridgePackage, WormholeInfraConfig, WxaAccountPackage, } from "./config.ts";
4
+ export type { BasePackageEntry, ConstantFeedEntry, WaterxReferralPackage, LoadConfigOptions, NativeCustodyAsset, NativeCustodyPackage, PythAccessConfig, PythFetchPolicy, PythLazerRulePackage, SupraFeedEntry, SupraRulePackage, TestnetFaucetPackage, WaterXConfig, WaterXPackages, WaterxCreditPackage, WaterxOraclePackage, WaterxPerpMarketEntry, WaterxPerpPackage, WaterxStakingPackage, WithdrawalQueuePackage, WlpPackage, WormholeBridgePackage, WormholeInfraConfig, WxaAccountPackage, } from "./config.ts";
5
5
  export { ACTION_ADD_PRE_ORDER, ACTION_CANCEL_ORDER, ACTION_CANCEL_PRE_ORDER, ACTION_CLOSE_POSITION, ACTION_DECREASE_POSITION, ACTION_DEPOSIT_COLLATERAL, ACTION_INCREASE_POSITION, ACTION_LIQUIDATE, ACTION_OPEN_POSITION, ACTION_PLACE_ORDER, ACTION_UPDATE_ORDER, ACTION_WITHDRAW_COLLATERAL, BPS_SCALE, DOUBLE_SCALE, DRY_RUN_SENDER, FLOAT_SCALE, ORDER_LIMIT_BUY, ORDER_LIMIT_SELL, ORDER_STOP_BUY, ORDER_STOP_SELL, ORDER_TAG_WILDCARD, PERM_ALL, PERM_ALL_TRADING, PERM_CANCEL_ORDER, PERM_CLOSE_POSITION, PERM_DECREASE_POSITION, PERM_DEPOSIT_COLLATERAL, PERM_INCREASE_POSITION, PERM_MINT_WLP, PERM_OPEN_POSITION, PERM_PLACE_ORDER, PERM_REDEEM_WLP, PERM_WITHDRAW_COLLATERAL, STAKING_PERM_DEPOSIT_STAKE, STAKING_PERM_REDEEM_STAKE, STAKING_PERM_CLAIM_REWARD, STAKING_PERM_ALL, MS_PER_MINUTE, MS_PER_HOUR, MS_PER_YEAR, SUI_DECIMALS, WLP_DECIMALS, COLLATERAL_DECIMALS, TOKEN_DECIMALS, } from "./constants.ts";
6
6
  export type { Network } from "./constants.ts";
7
- export { getMarketTickers, getCollateralAssets } from "../utils/config.ts";
7
+ export { getMarketTickers } from "../utils/config.ts";
8
8
  export { annualizedApyFromRatio, annualizeFundingRate, calcBorrowRate, calcBorrowRateAccrual, calcDynamicFeeBps, calcEffectiveCollateralUsd, calcEstLiqPrice, calcEstLiqPriceRaw, calcFee, calcFundingFeeUsd, calcFundingRate, calcImpactFeeRate, calcLeverage, calcMaxReducibleCollateralUsd, calcNotional, calcPositionBorrowFee, calcRealLiqNetCostUsd, calcTokenUtilizationBps, calcTotalTradingFeeRate, calcUnrealizedPnl, calcViewEstLiqFeesUsd, calcWlpIncentiveApy, calcWlpMintOut, calcWlpPrice, calcWlpRedeemOut, decodeFundingIndexDelta, rawPrice, } from "../utils/math.ts";
9
9
  export type { ExactDecimalUsd, LiqFeeBundle, RawPriceInput } from "../utils/math.ts";
10
10
  export { formatFundingInterval } from "../utils/format.ts";
@@ -13,8 +13,8 @@ export type { EstLiqPriceViewOpts } from "./liq-view.ts";
13
13
  export * from "./user/index.ts";
14
14
  export * from "./tx-builders.ts";
15
15
  export * from "./fetch.ts";
16
- export { FetchPolicyError, LazerApiKeyMissingError, OracleFeeSourceUnavailableError, OracleSourceNotImplementedError, PythCache, aggregateTicker, aggregateTickerWithConstant, aggregateTickerWithPyth, ORACLE_SOURCES, buildPythPriceUpdateCalls, fetchPriceFeedsUpdateData, isOracleSource, parseOracleSourceList, pythCoreHermesEndpoint, pythProHermesEndpoint, refreshOraclePrices, resolveHermesReadEndpoint, updatePythPrices, waterxEnvelopeOf, waterxQuoteCenterEndpoint, } from "../oracle/index.ts";
17
- export type { FetchPolicy, OracleFeeSource, OracleSource, UpdateDataProvider, } from "../oracle/index.ts";
16
+ export { FetchPolicyError, LazerApiKeyMissingError, LazerNotEntitledError, OracleTickerUnservedError, OracleWeightCoverageError, OracleWeightUnreadableError, OracleSourceNotImplementedError, aggregateTicker, aggregateTickerWithConstant, ORACLE_SOURCES, assertOracleWeightCoverage, assertOracleWriteCoverage, fetchPythProHistory, fetchPythSymbolCatalog, fetchWaterxSignedLeaves, fetchWaterxSignedUpdate, fetchWaterxUpdateData, getMarketStatus, isFreshWaterxEntry, deriveOracleSources, missingOracleCredentials, parsePythSchedule, PythScheduleParseError, pullWaterxQuotes, readLazerPrices, readPlanTickers, readQuoteCenterPrices, refreshOraclePrices, resolveOracleReadPlan, resolveOracleRule, servableTickers, WATERX_MAX_PRICE_AGE_MS, waterxEnvelopeOf, waterxLeavesOf, parseSignedLeaves, MERKLE_ROOT_INTENT, waterxQuoteCenterEndpoint, } from "../oracle/index.ts";
17
+ export type { FetchPolicy, HolidayDate, MarketStatusResult, OracleCredentialKind, OraclePriceEntry, OracleRefreshSummary, OracleReadPlan, OracleSource, ParsedPythSchedule, PythSymbolRecord, TradingHours, TradingSession, UpdateDataProvider, WaterxBatchItem, WaterxEnvelopePayload, WaterxLeafPayload, WaterxSignedEnvelope, WaterxSignedLeaf, WaterxUpdatePayload, } from "../oracle/index.ts";
18
18
  export { fetchDepositVaa, fetchVaa, listBridgeWithdrawalVaas, listVaasByEmitter, padEvmEmitter, toWormholescanEmitter, vaaBase64ToBytes, vaaBase64ToHex, vaaBytesToBase64, waitForVaa, } from "../account/funding/wormhole.ts";
19
19
  export type { VaaListItem, VaaResponse, WormholescanOptions } from "../account/funding/wormhole.ts";
20
20
  export { AccountData as AccountDataBcs, GlobalConfigData as GlobalConfigDataBcs, MarketData as MarketDataBcs, OrderData as OrderDataBcs, PoolData as PoolDataBcs, PositionData as PositionDataBcs, RedeemRequestData as RedeemRequestDataBcs, TokenPoolData as TokenPoolDataBcs, } from "../generated/waterx_perp_view/view.ts";
@@ -27,8 +27,6 @@ export * as viewCalls from "../generated/waterx_perp_view/view.ts";
27
27
  export * as wxaAccountCalls from "../generated/waterx_account/account.ts";
28
28
  export * as stakingCalls from "../generated/waterx_staking/waterx_staking.ts";
29
29
  export * as oracleCalls from "../generated/waterx_oracle/oracle.ts";
30
- export * as pythRuleCalls from "../generated/waterx_pyth_rule/pyth_rule.ts";
31
- export * as pythSponsorRuleCalls from "../generated/pyth_sponsor_rule/pyth_sponsor_rule.ts";
32
30
  export * as referralCalls from "../generated/waterx_referral/referral_table.ts";
33
31
  export * as nativeCustodyCalls from "../generated/native_custody/custody_vault.ts";
34
32
  export * as withdrawalQueueCalls from "../generated/withdrawal_queue/withdrawal_queue.ts";
@@ -4,7 +4,7 @@ export { WORMHOLE_DEFAULTS, clearConfigCache, loadConfig } from "./config.js";
4
4
  // ======== Constants & enums ========
5
5
  export { ACTION_ADD_PRE_ORDER, ACTION_CANCEL_ORDER, ACTION_CANCEL_PRE_ORDER, ACTION_CLOSE_POSITION, ACTION_DECREASE_POSITION, ACTION_DEPOSIT_COLLATERAL, ACTION_INCREASE_POSITION, ACTION_LIQUIDATE, ACTION_OPEN_POSITION, ACTION_PLACE_ORDER, ACTION_UPDATE_ORDER, ACTION_WITHDRAW_COLLATERAL, BPS_SCALE, DOUBLE_SCALE, DRY_RUN_SENDER, FLOAT_SCALE, ORDER_LIMIT_BUY, ORDER_LIMIT_SELL, ORDER_STOP_BUY, ORDER_STOP_SELL, ORDER_TAG_WILDCARD, PERM_ALL, PERM_ALL_TRADING, PERM_CANCEL_ORDER, PERM_CLOSE_POSITION, PERM_DECREASE_POSITION, PERM_DEPOSIT_COLLATERAL, PERM_INCREASE_POSITION, PERM_MINT_WLP, PERM_OPEN_POSITION, PERM_PLACE_ORDER, PERM_REDEEM_WLP, PERM_WITHDRAW_COLLATERAL, STAKING_PERM_DEPOSIT_STAKE, STAKING_PERM_REDEEM_STAKE, STAKING_PERM_CLAIM_REWARD, STAKING_PERM_ALL, MS_PER_MINUTE, MS_PER_HOUR, MS_PER_YEAR, SUI_DECIMALS, WLP_DECIMALS, COLLATERAL_DECIMALS, TOKEN_DECIMALS, } from "./constants.js";
6
6
  // ======== Utilities ========
7
- export { getMarketTickers, getCollateralAssets } from "../utils/config.js";
7
+ export { getMarketTickers } from "../utils/config.js";
8
8
  export { annualizedApyFromRatio, annualizeFundingRate, calcBorrowRate, calcBorrowRateAccrual, calcDynamicFeeBps, calcEffectiveCollateralUsd, calcEstLiqPrice, calcEstLiqPriceRaw, calcFee, calcFundingFeeUsd, calcFundingRate, calcImpactFeeRate, calcLeverage, calcMaxReducibleCollateralUsd, calcNotional, calcPositionBorrowFee, calcRealLiqNetCostUsd, calcTokenUtilizationBps, calcTotalTradingFeeRate, calcUnrealizedPnl, calcViewEstLiqFeesUsd, calcWlpIncentiveApy, calcWlpMintOut, calcWlpPrice, calcWlpRedeemOut, decodeFundingIndexDelta, rawPrice, } from "../utils/math.js";
9
9
  export { formatFundingInterval } from "../utils/format.js";
10
10
  export { calcEstLiqPriceRawFromView } from "./liq-view.js";
@@ -14,8 +14,22 @@ export * from "./user/index.js";
14
14
  export * from "./tx-builders.js";
15
15
  // ======== Read-only queries ========
16
16
  export * from "./fetch.js";
17
- // ======== Oracle utilities (Pyth source + rule aggregation) ========
18
- export { FetchPolicyError, LazerApiKeyMissingError, OracleFeeSourceUnavailableError, OracleSourceNotImplementedError, PythCache, aggregateTicker, aggregateTickerWithConstant, aggregateTickerWithPyth, ORACLE_SOURCES, buildPythPriceUpdateCalls, fetchPriceFeedsUpdateData, isOracleSource, parseOracleSourceList, pythCoreHermesEndpoint, pythProHermesEndpoint, refreshOraclePrices, resolveHermesReadEndpoint, updatePythPrices, waterxEnvelopeOf, waterxQuoteCenterEndpoint, } from "../oracle/index.js";
17
+ // ======== Oracle utilities (sources + rule aggregation + read planes) ========
18
+ export { FetchPolicyError, LazerApiKeyMissingError, LazerNotEntitledError, OracleTickerUnservedError, OracleWeightCoverageError, OracleWeightUnreadableError, OracleSourceNotImplementedError, aggregateTicker, aggregateTickerWithConstant, ORACLE_SOURCES, assertOracleWeightCoverage, assertOracleWriteCoverage, fetchPythProHistory, fetchPythSymbolCatalog, fetchWaterxSignedLeaves, fetchWaterxSignedUpdate, fetchWaterxUpdateData, getMarketStatus, isFreshWaterxEntry, deriveOracleSources, missingOracleCredentials, parsePythSchedule, PythScheduleParseError,
19
+ // THE quote-center route ladder (leaves-first, envelope only where there is
20
+ // no leaf route). Both SDK planes go through it, so a consumer running its
21
+ // own prefetch reaches for the same one rather than re-deriving the ladder.
22
+ pullWaterxQuotes, readLazerPrices, readPlanTickers, readQuoteCenterPrices, refreshOraclePrices, resolveOracleReadPlan, resolveOracleRule,
23
+ // `refreshOraclePrices`' OWN acceptance predicate. Consumers filtering a
24
+ // ticker list before a build must use this rather than re-deriving "some
25
+ // listed source carries it", or they drift from what the build accepts.
26
+ servableTickers, WATERX_MAX_PRICE_AGE_MS, waterxEnvelopeOf,
27
+ // Leaves are the DEFAULT waterx wire shape, so their accessor + parser belong
28
+ // on the same surface as `waterxEnvelopeOf` (and on the root, which re-exports
29
+ // this file). A prefetch cache written against the envelope-only surface would
30
+ // otherwise get `null` from its only accessor on the normal path and have to
31
+ // discover `@waterx/sdk/oracle` to fix it.
32
+ waterxLeavesOf, parseSignedLeaves, MERKLE_ROOT_INTENT, waterxQuoteCenterEndpoint, } from "../oracle/index.js";
19
33
  // ======== Wormhole / Wormholescan utilities (credit bridge) ========
20
34
  export { fetchDepositVaa, fetchVaa, listBridgeWithdrawalVaas, listVaasByEmitter, padEvmEmitter, toWormholescanEmitter, vaaBase64ToBytes, vaaBase64ToHex, vaaBytesToBase64, waitForVaa, } from "../account/funding/wormhole.js";
21
35
  // ======== Generated BCS types (sui-ts-codegen) ========
@@ -30,8 +44,6 @@ export * as viewCalls from "../generated/waterx_perp_view/view.js";
30
44
  export * as wxaAccountCalls from "../generated/waterx_account/account.js";
31
45
  export * as stakingCalls from "../generated/waterx_staking/waterx_staking.js";
32
46
  export * as oracleCalls from "../generated/waterx_oracle/oracle.js";
33
- export * as pythRuleCalls from "../generated/waterx_pyth_rule/pyth_rule.js";
34
- export * as pythSponsorRuleCalls from "../generated/pyth_sponsor_rule/pyth_sponsor_rule.js";
35
47
  export * as referralCalls from "../generated/waterx_referral/referral_table.js";
36
48
  export * as nativeCustodyCalls from "../generated/native_custody/custody_vault.js";
37
49
  export * as withdrawalQueueCalls from "../generated/withdrawal_queue/withdrawal_queue.js";
@@ -1,44 +1,58 @@
1
1
  /**
2
2
  * Shared scaffolding for the high-level perp `build*Tx` composers:
3
3
  * `CommonBuildOpts`, the new-or-reuse PTB helper, the WLP pool-oracle refresh,
4
- * and the request+execute envelope (optional Pyth-sponsor flow + pre-sweep).
4
+ * and the request+execute envelope (optional pre-sweep).
5
5
  */
6
6
  import { Transaction, type TransactionArgument } from "@mysten/sui/transactions";
7
- import { PythCache, type OracleFeeSource, type UpdateDataProvider } from "../../oracle/index.ts";
7
+ import { type OracleRefreshSummary, type UpdateDataProvider } from "../../oracle/index.ts";
8
8
  import type { PerpClient } from "../client.ts";
9
9
  export interface CommonBuildOpts {
10
10
  /** Append to an existing PTB instead of creating a new one. */
11
11
  tx?: Transaction;
12
- /** Skip oracle refresh entirely (caller manages freshness). Default: false. */
13
- skipOraclePriceRefresh?: boolean;
14
- /** Share a `PythCache` across builders to avoid redundant pyth_state reads. */
15
- pythCache?: PythCache;
16
12
  /**
17
- * @deprecated No longer a fee-source decision. `wrapRequestAndExecute` now
18
- * opens (and reimburses) the `pyth_sponsor_rule` Fund purely from config
19
- * presence whenever `client.config.packages.pyth_sponsor_rule` is
20
- * deployed, the fund is ALWAYS opened, regardless of this flag (see
21
- * `OracleFeeSourceUnavailable` in `oracle/pyth.ts`). This closes the gap
22
- * where a market whose checklist required `PythSponsorRule`, or a caller
23
- * that mis-set this flag, silently drew from `tx.gas` and failed
24
- * ON-CHAIN instead of at build time. Use `allowGasFee` for the
25
- * non-sponsored case instead. Kept accepted (as a no-op) only so existing
26
- * callers keep compiling; will be removed in a future major version.
13
+ * Skip oracle refresh entirely (caller manages freshness). Default: false.
14
+ *
15
+ * This is the SHARED-REFRESH composition lever: a caller batching several
16
+ * builders into one PTB refreshes once, up front, then passes the same `tx`
17
+ * to each builder with this flag set —
18
+ *
19
+ * const tx = new Transaction();
20
+ * await refreshOraclePrices(tx, client.perp, allTickers);
21
+ * await buildIncreasePositionTx(client.perp, { ...a, tx, skipOraclePriceRefresh: true });
22
+ * await buildPlaceOrderTx(client.perp, { ...b, tx, skipOraclePriceRefresh: true });
23
+ *
24
+ * — one refresh covering `allTickers` (every builder's market + collateral
25
+ * tickers + the WLP pool tokens) instead of N. This is not just a gas
26
+ * saving: under `waterx_rule` a repeated per-builder refresh would submit
27
+ * the same signed timestamp twice per symbol in one PTB, paying full
28
+ * verification for an on-chain abstain (the F-014 per-symbol replay
29
+ * high-water mark).
30
+ *
31
+ * Sharing one fetched waterx snapshot across CONCURRENT builds is safe on
32
+ * these paths: every builder here feeds through `collect_*`, where a
33
+ * replayed per-symbol timestamp ABSTAINS rather than aborting — the chain
34
+ * already holds a price at least that fresh. `EReplayedSignature` is raised
35
+ * only by the single-rule `feed_*` entries, which no `build*Tx` uses. The
36
+ * cost of sharing is a contribution that does not count, not a failed
37
+ * transaction.
27
38
  */
28
- useSponsor?: boolean;
39
+ skipOraclePriceRefresh?: boolean;
29
40
  /**
30
- * Explicit opt-in to draw the Pyth update fee from `tx.gas` when this
31
- * client's config has no `pyth_sponsor_rule` deployed. Ignored whenever a
32
- * sponsor fund IS available the sponsor pool always wins over `tx.gas`
33
- * when one can be opened (see `useSponsor`'s deprecation note above).
34
- * Required for flows with no `TradingRequest` to reimburse a sponsor fund
35
- * against (e.g. `buildMintWlpTx` see its doc comment). Building an
36
- * oracle refresh with neither a sponsor fund nor this flag throws
37
- * `OracleFeeSourceUnavailable` instead of silently drawing from `tx.gas`
38
- * (Enoki-sponsored transactions reject any `tx.gas` draw). Default:
39
- * `false`.
41
+ * Build even when a ticker this action depends on could not be refreshed.
42
+ *
43
+ * `refreshOraclePrices` SKIPS tickers no listed source serves (it is a broad
44
+ * primitive losing 29 tickers because the 30th is unconfigured is the wrong
45
+ * trade). The composers then fail closed on the subset their specific action
46
+ * actually depends on. This opts out of that check, building against whatever
47
+ * price the chain already holds.
48
+ *
49
+ * Rarely correct. The on-chain guards do NOT cover for it: `mint_wlp` sizes
50
+ * the payout off `pool.tvl_usd`, and `assert_prices_fresh` only compares each
51
+ * token's `last_price_refresh_timestamp` against
52
+ * `price_refresh_threshold_ms` — so a pool asset last refreshed by SOMEONE
53
+ * ELSE minutes ago passes, and the mint is valued off that stale price.
40
54
  */
41
- allowGasFee?: boolean;
55
+ allowUnrefreshedPrices?: boolean;
42
56
  /**
43
57
  * Pre-sweep parked backing assets (USDC, USDsui, …) at the wxa account's
44
58
  * address into USD credit, plus any CREDIT coins/funds at the address into
@@ -73,36 +87,63 @@ interface RequestParams {
73
87
  lpType?: string;
74
88
  }
75
89
  export declare function newTx(opts?: CommonBuildOpts): Transaction;
90
+ /**
91
+ * Fail the build when a ticker THIS action depends on went unrefreshed.
92
+ *
93
+ * The split of responsibility: `refreshOraclePrices` skips (it cannot know
94
+ * which of its tickers were load-bearing), the composer asserts (it does).
95
+ */
96
+ export declare function assertTickersRefreshed(client: PerpClient, summary: OracleRefreshSummary, required: string[], opts: CommonBuildOpts | undefined,
97
+ /** Appended to the error, to say why THESE tickers were load-bearing. */
98
+ why?: string): void;
99
+ /**
100
+ * Fail the build when ANY WLP pool asset went unpriced — stricter than
101
+ * {@link assertTickersRefreshed}, and deliberately so.
102
+ *
103
+ * `mint_wlp` / `settle_redeem` take `&WlpAum` and size the payout against the
104
+ * pool's WHOLE `tvl_usd`, so a stale price on any pool asset mis-values the
105
+ * trade — not just a stale price on the deposit ticker.
106
+ *
107
+ * Nothing on chain catches the gap. Two paths, neither of which fails:
108
+ * - the asset IS bumped: `update_token_value` calls `oracle::get_price`,
109
+ * which aborts `EStalePrice` unless this PTB aggregated it — loud, safe;
110
+ * - the asset is NOT bumped (it was skipped, so no leg was emitted): its
111
+ * `last_price_refresh_timestamp` keeps whatever a previous transaction
112
+ * left, and `assert_prices_fresh` only asks that it be within
113
+ * `price_refresh_threshold_ms`. A recent-enough stale price PASSES.
114
+ *
115
+ * So the silent case is exactly the skipped one, and the build is the only
116
+ * place it can be caught.
117
+ */
118
+ export declare function assertWlpPoolRefreshed(client: PerpClient, summary: OracleRefreshSummary, opts: CommonBuildOpts | undefined): void;
76
119
  /**
77
120
  * Refresh every WLP pool-token oracle (+ caller-supplied extra tickers) and
78
121
  * bump each pool token's `last_price_refresh_timestamp` so the pool's
79
122
  * `assert_prices_fresh` passes when the next `mint_wlp` / `request_redeem` /
80
123
  * trading `execute` runs in the same PTB.
124
+ *
125
+ * Fails closed via {@link assertWlpPoolRefreshed} — see there for why a gap is
126
+ * otherwise silent rather than an on-chain abort.
81
127
  */
82
- export declare function refreshWlpPoolOracles(tx: Transaction, client: PerpClient, extraTickers: string[], opts: {
83
- cache?: PythCache;
84
- /** Forwarded to `refreshOraclePrices` — already resolved by the caller (see `OracleFeeSource`). */
85
- feeSource?: OracleFeeSource;
128
+ export declare function refreshWlpPoolOracles(tx: Transaction, client: PerpClient, extraTickers: string[],
129
+ /** The build's options — `lpType`, plus `allowUnrefreshedPrices` / `updateDataProvider`. */
130
+ opts: CommonBuildOpts & {
86
131
  lpType?: string;
87
- updateDataProvider?: UpdateDataProvider;
88
- }): Promise<void>;
132
+ }): Promise<OracleRefreshSummary>;
89
133
  /**
90
- * Build the *Request + execute envelope with the config-driven Pyth sponsor flow:
134
+ * Build the *Request + execute envelope:
91
135
  *
92
136
  * [maybeConsolidate(tx)]
93
- * [fund = sponsor.request()]
94
- * refreshOraclePrices(..., feeSource?)
137
+ * refreshOraclePrices(...)
95
138
  * req = buildRequest()
96
- * [sponsor.reimburse(fund, req)]
97
139
  * trading::execute(req)
98
140
  *
99
141
  * Accepted ordering caveat: `maybeConsolidate` runs FIRST and can itself
100
- * append PTB commands (the consolidation sweep) before the fee-source check
101
- * inside `refreshOraclePrices` ever runs — so an `OracleFeeSourceUnavailable`
102
- * throw here is NOT the "zero commands appended" guarantee
103
- * `refreshOraclePrices` gives its own callers (see its docblock in
104
- * `aggregate.ts`); `tx` can already carry the sweep. This is the same
105
- * discard-tx-on-throw contract every `build*Tx` composer already has for
142
+ * append PTB commands (the consolidation sweep) before the refresh's own
143
+ * pre-checks run — so a throw out of `refreshOraclePrices` here is NOT the
144
+ * "zero commands appended" guarantee it gives its own callers (see its
145
+ * docblock in `aggregate.ts`); `tx` can already carry the sweep. This is the
146
+ * same discard-tx-on-throw contract every `build*Tx` composer already has for
106
147
  * mid-build on-chain-read failures — not a new hole. It matters only for a
107
148
  * caller that passed in their OWN `opts.tx` (reusing one `Transaction`
108
149
  * across builder calls, e.g. to compose several actions in one PTB); such a
@@ -111,10 +152,7 @@ export declare function refreshWlpPoolOracles(tx: Transaction, client: PerpClien
111
152
  */
112
153
  export declare function wrapRequestAndExecute(client: PerpClient, tx: Transaction, req: RequestParams & {
113
154
  accountId: string;
114
- }, collateralTicker: string, opts: CommonBuildOpts | undefined, buildRequest: (sponsorFund?: {
115
- fund: TransactionArgument;
116
- packageId: string;
117
- }) => TransactionArgument): Promise<void>;
155
+ }, collateralTicker: string, opts: CommonBuildOpts | undefined, buildRequest: () => TransactionArgument): Promise<void>;
118
156
  /** Run the async sweep iff `consolidateToUsd !== false`. */
119
157
  export declare function maybeConsolidate(client: PerpClient, tx: Transaction, accountId: string, opts: CommonBuildOpts | undefined): Promise<void>;
120
158
  export {};
@@ -1,52 +1,141 @@
1
1
  /**
2
2
  * Shared scaffolding for the high-level perp `build*Tx` composers:
3
3
  * `CommonBuildOpts`, the new-or-reuse PTB helper, the WLP pool-oracle refresh,
4
- * and the request+execute envelope (optional Pyth-sponsor flow + pre-sweep).
4
+ * and the request+execute envelope (optional pre-sweep).
5
5
  */
6
6
  import { Transaction } from "@mysten/sui/transactions";
7
7
  import { appendConsolidateForSpend } from "../../account/funding/consolidate.js";
8
- import { openPythSponsorFund, refreshOraclePrices, reimbursePythSponsor, } from "../../oracle/index.js";
9
- import { getCollateralAssets } from "../../utils/config.js";
8
+ import { OracleTickerUnservedError, partitionServableTickers, refreshOraclePrices, } from "../../oracle/index.js";
10
9
  import { executeTrading } from "../user/trading.js";
11
10
  import { updateTokenValue } from "../user/wlp.js";
12
11
  export function newTx(opts) {
13
12
  return opts?.tx ?? new Transaction();
14
13
  }
14
+ /**
15
+ * Fail the build when a ticker THIS action depends on went unrefreshed.
16
+ *
17
+ * The split of responsibility: `refreshOraclePrices` skips (it cannot know
18
+ * which of its tickers were load-bearing), the composer asserts (it does).
19
+ */
20
+ export function assertTickersRefreshed(client, summary, required, opts,
21
+ /** Appended to the error, to say why THESE tickers were load-bearing. */
22
+ why) {
23
+ if (opts?.allowUnrefreshedPrices)
24
+ return;
25
+ const skipped = new Set(summary.skipped);
26
+ const unserved = [...new Set(required)].filter((ticker) => skipped.has(ticker));
27
+ if (unserved.length > 0) {
28
+ throw new OracleTickerUnservedError(unserved, client.oracleSources, why);
29
+ }
30
+ }
31
+ /**
32
+ * Fail the build when ANY WLP pool asset went unpriced — stricter than
33
+ * {@link assertTickersRefreshed}, and deliberately so.
34
+ *
35
+ * `mint_wlp` / `settle_redeem` take `&WlpAum` and size the payout against the
36
+ * pool's WHOLE `tvl_usd`, so a stale price on any pool asset mis-values the
37
+ * trade — not just a stale price on the deposit ticker.
38
+ *
39
+ * Nothing on chain catches the gap. Two paths, neither of which fails:
40
+ * - the asset IS bumped: `update_token_value` calls `oracle::get_price`,
41
+ * which aborts `EStalePrice` unless this PTB aggregated it — loud, safe;
42
+ * - the asset is NOT bumped (it was skipped, so no leg was emitted): its
43
+ * `last_price_refresh_timestamp` keeps whatever a previous transaction
44
+ * left, and `assert_prices_fresh` only asks that it be within
45
+ * `price_refresh_threshold_ms`. A recent-enough stale price PASSES.
46
+ *
47
+ * So the silent case is exactly the skipped one, and the build is the only
48
+ * place it can be caught.
49
+ */
50
+ export function assertWlpPoolRefreshed(client, summary, opts) {
51
+ // Every pool asset is required — that IS the WLP-specific part. The general
52
+ // assert does the rest; there is no second class of fault to detect, because
53
+ // `refreshWlpPoolOracles` requests the whole pool, so anything this client
54
+ // cannot price is necessarily in `summary.skipped` already.
55
+ assertTickersRefreshed(client, summary, Object.keys(client.config.packages.wlp?.pool_tokens ?? {}), opts, "They are WLP pool assets, and mint/redeem values the WHOLE pool. ");
56
+ }
15
57
  /**
16
58
  * Refresh every WLP pool-token oracle (+ caller-supplied extra tickers) and
17
59
  * bump each pool token's `last_price_refresh_timestamp` so the pool's
18
60
  * `assert_prices_fresh` passes when the next `mint_wlp` / `request_redeem` /
19
61
  * trading `execute` runs in the same PTB.
62
+ *
63
+ * Fails closed via {@link assertWlpPoolRefreshed} — see there for why a gap is
64
+ * otherwise silent rather than an on-chain abort.
20
65
  */
21
- export async function refreshWlpPoolOracles(tx, client, extraTickers, opts) {
22
- const poolTickers = getCollateralAssets(client.config);
23
- const oracleTickers = Array.from(new Set([...extraTickers, ...poolTickers]));
24
- await refreshOraclePrices(tx, client, oracleTickers, {
25
- cache: opts.cache,
26
- feeSource: opts.feeSource,
66
+ export async function refreshWlpPoolOracles(tx, client, extraTickers,
67
+ /** The build's options — `lpType`, plus `allowUnrefreshedPrices` / `updateDataProvider`. */
68
+ opts) {
69
+ // EVERY pool asset is requested, not a pre-filtered subset. Filtering here
70
+ // was the bug: a token the fed set cannot price was dropped from BOTH the
71
+ // refresh and the bump, so nothing on chain objected (see
72
+ // `assertWlpPoolRefreshed` for why `assert_prices_fresh` lets that through)
73
+ // and the mint was valued off a stale price. Ask for all of them, fail the
74
+ // build on any gap, then bump.
75
+ const poolTokens = client.config.packages.wlp?.pool_tokens ?? {};
76
+ // `refreshOraclePrices` dedupes its own input, so a plain concat is enough.
77
+ const oracleTickers = [...extraTickers, ...Object.keys(poolTokens)];
78
+ // Fail closed BEFORE the fetch, on EVERYTHING this build depends on.
79
+ //
80
+ // `refreshOraclePrices` no longer throws on an unservable ticker (it skips
81
+ // and reports), so nothing is gained by waiting for it — and waiting costs
82
+ // the full off-chain fetch plus every collector/aggregate moveCall appended
83
+ // to a caller-supplied `tx`, all discarded on the throw. The predicate is
84
+ // pure config, so the pre-flight answer is identical to the summary's, free.
85
+ //
86
+ // Two required sets, each with its own explanation: `extraTickers` are the
87
+ // caller's action-critical ones (the traded market + its collateral, or a
88
+ // WLP deposit ticker), and the pool is required wholesale because mint and
89
+ // redeem value the WHOLE pool.
90
+ const planned = plannedSummary(client, oracleTickers);
91
+ assertTickersRefreshed(client, planned, extraTickers, opts);
92
+ assertWlpPoolRefreshed(client, planned, opts);
93
+ const summary = await refreshOraclePrices(tx, client, oracleTickers, {
27
94
  updateDataProvider: opts.updateDataProvider,
28
95
  });
29
- for (const tokenType of Object.values(client.config.packages.wlp.pool_tokens)) {
96
+ // Bump only what was actually AGGREGATED, not every pool entry.
97
+ //
98
+ // `update_token_value` calls `oracle::get_price`, which aborts `EStalePrice`
99
+ // unless this PTB aggregated that ticker. Normally the asserts above
100
+ // guarantee it — but `allowUnrefreshedPrices` skips them by design, and
101
+ // bumping a skipped asset then produced a transaction guaranteed to abort,
102
+ // which made the escape hatch unusable on exactly the deployments that need
103
+ // it. Driving the loop off the summary makes the opt-out mean what it says:
104
+ // build with whatever prices the chain already holds, and touch nothing else.
105
+ const aggregated = new Set(summary.refreshed);
106
+ for (const [ticker, tokenType] of Object.entries(poolTokens)) {
107
+ if (!aggregated.has(ticker))
108
+ continue;
30
109
  updateTokenValue(client, tx, { tokenType, lpType: opts.lpType });
31
110
  }
111
+ return summary;
32
112
  }
33
113
  /**
34
- * Build the *Request + execute envelope with the config-driven Pyth sponsor flow:
114
+ * What {@link refreshOraclePrices} WOULD report for `tickers`, computed from
115
+ * config alone.
116
+ *
117
+ * Same predicate, same partition — so a pre-flight assert cannot disagree with
118
+ * the summary the refresh later returns, and a build destined to fail can fail
119
+ * before spending a network round trip or mutating the caller's transaction.
120
+ */
121
+ function plannedSummary(client, tickers) {
122
+ const { servable, unservable } = partitionServableTickers(client, tickers);
123
+ return { refreshed: servable, skipped: unservable };
124
+ }
125
+ /**
126
+ * Build the *Request + execute envelope:
35
127
  *
36
128
  * [maybeConsolidate(tx)]
37
- * [fund = sponsor.request()]
38
- * refreshOraclePrices(..., feeSource?)
129
+ * refreshOraclePrices(...)
39
130
  * req = buildRequest()
40
- * [sponsor.reimburse(fund, req)]
41
131
  * trading::execute(req)
42
132
  *
43
133
  * Accepted ordering caveat: `maybeConsolidate` runs FIRST and can itself
44
- * append PTB commands (the consolidation sweep) before the fee-source check
45
- * inside `refreshOraclePrices` ever runs — so an `OracleFeeSourceUnavailable`
46
- * throw here is NOT the "zero commands appended" guarantee
47
- * `refreshOraclePrices` gives its own callers (see its docblock in
48
- * `aggregate.ts`); `tx` can already carry the sweep. This is the same
49
- * discard-tx-on-throw contract every `build*Tx` composer already has for
134
+ * append PTB commands (the consolidation sweep) before the refresh's own
135
+ * pre-checks run — so a throw out of `refreshOraclePrices` here is NOT the
136
+ * "zero commands appended" guarantee it gives its own callers (see its
137
+ * docblock in `aggregate.ts`); `tx` can already carry the sweep. This is the
138
+ * same discard-tx-on-throw contract every `build*Tx` composer already has for
50
139
  * mid-build on-chain-read failures — not a new hole. It matters only for a
51
140
  * caller that passed in their OWN `opts.tx` (reusing one `Transaction`
52
141
  * across builder calls, e.g. to compose several actions in one PTB); such a
@@ -55,39 +144,16 @@ export async function refreshWlpPoolOracles(tx, client, extraTickers, opts) {
55
144
  */
56
145
  export async function wrapRequestAndExecute(client, tx, req, collateralTicker, opts, buildRequest) {
57
146
  await maybeConsolidate(client, tx, req.accountId, opts);
58
- // Fee source + witness attachment is config-driven, not a caller flag: the
59
- // sponsor fund is opened (and later reimbursed) whenever this client's
60
- // config has `pyth_sponsor_rule` deployed — regardless of the deprecated
61
- // `useSponsor` flag (see its JSDoc). `allowGasFee` is the only caller lever
62
- // left, and it only matters when config has NO sponsor rule to open (see
63
- // `OracleFeeSourceUnavailable` in `oracle/pyth.ts`).
64
- //
65
- // `feeSource` is resolved HERE, once, from that same decision — sponsor
66
- // beats gas structurally because this is the only branch that ever sees
67
- // both candidates; everything downstream (`refreshWlpPoolOracles` →
68
- // `refreshOraclePrices` → `BuildUpdateOpts` → `PythCoreRule` →
69
- // `buildPythPriceUpdateCalls`) just carries the single resolved value.
70
- let sponsorFund;
71
- if (client.config.packages.pyth_sponsor_rule) {
72
- sponsorFund = openPythSponsorFund(tx, client);
73
- }
74
- const feeSource = sponsorFund
75
- ? { kind: "sponsor", ...sponsorFund }
76
- : opts?.allowGasFee
77
- ? { kind: "gas" }
78
- : undefined;
79
147
  if (!opts?.skipOraclePriceRefresh) {
148
+ // The action-critical pair for a trade — the market being traded and the
149
+ // collateral it is margined in — is passed as the extras, which
150
+ // `refreshWlpPoolOracles` fails closed on before it fetches anything.
80
151
  await refreshWlpPoolOracles(tx, client, [req.ticker, collateralTicker], {
81
- cache: opts?.pythCache,
82
- feeSource,
152
+ ...opts,
83
153
  lpType: req.lpType,
84
- updateDataProvider: opts?.updateDataProvider,
85
154
  });
86
155
  }
87
- const tradingReq = buildRequest(sponsorFund);
88
- if (sponsorFund) {
89
- reimbursePythSponsor(tx, client, sponsorFund.fund, tradingReq, req.collateralType);
90
- }
156
+ const tradingReq = buildRequest();
91
157
  executeTrading(client, tx, {
92
158
  ticker: req.ticker,
93
159
  collateralType: req.collateralType,