@waterx/sdk 3.1.1 → 4.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/README.md +36 -0
- package/dist/cjs/src/oracle/aggregate.d.ts +98 -17
- package/dist/cjs/src/oracle/aggregate.js +191 -21
- package/dist/cjs/src/oracle/config.d.ts +103 -0
- package/dist/cjs/src/oracle/config.js +64 -1
- package/dist/cjs/src/oracle/host.d.ts +13 -0
- package/dist/cjs/src/oracle/index.d.ts +19 -5
- package/dist/cjs/src/oracle/index.js +40 -6
- package/dist/cjs/src/oracle/price-update-rule.d.ts +180 -0
- package/dist/cjs/src/oracle/price-update-rule.js +56 -0
- package/dist/cjs/src/oracle/pyth.d.ts +80 -11
- package/dist/cjs/src/oracle/pyth.js +84 -17
- package/dist/cjs/src/oracle/rule-registry.d.ts +37 -0
- package/dist/cjs/src/oracle/rule-registry.js +61 -0
- package/dist/cjs/src/oracle/rules/pyth-core-rule.d.ts +15 -0
- package/dist/cjs/src/oracle/rules/pyth-core-rule.js +84 -0
- package/dist/cjs/src/oracle/rules/pyth-lazer-rule.d.ts +41 -0
- package/dist/cjs/src/oracle/rules/pyth-lazer-rule.js +194 -0
- package/dist/cjs/src/oracle/rules/sponsor.d.ts +11 -7
- package/dist/cjs/src/oracle/rules/sponsor.js +11 -7
- package/dist/cjs/src/oracle/update-fetch.d.ts +85 -0
- package/dist/cjs/src/oracle/update-fetch.js +228 -0
- package/dist/cjs/src/perp/client.d.ts +22 -1
- package/dist/cjs/src/perp/client.js +11 -2
- package/dist/cjs/src/perp/config.d.ts +9 -4
- package/dist/cjs/src/perp/config.js +82 -18
- package/dist/cjs/src/perp/index.d.ts +4 -3
- package/dist/cjs/src/perp/index.js +8 -3
- package/dist/cjs/src/perp/tx-builders/common.d.ts +52 -15
- package/dist/cjs/src/perp/tx-builders/common.js +39 -6
- package/dist/cjs/src/perp/tx-builders/wlp.d.ts +11 -3
- package/dist/cjs/src/perp/tx-builders/wlp.js +29 -3
- package/dist/cjs/src/perp/tx-builders.d.ts +3 -3
- package/dist/cjs/src/perp/tx-builders.js +3 -3
- package/dist/cjs/src/unified-client.d.ts +17 -0
- package/dist/cjs/src/unified-client.js +2 -0
- package/dist/src/oracle/aggregate.d.ts +98 -17
- package/dist/src/oracle/aggregate.js +192 -22
- package/dist/src/oracle/config.d.ts +103 -0
- package/dist/src/oracle/config.js +63 -0
- package/dist/src/oracle/host.d.ts +13 -0
- package/dist/src/oracle/index.d.ts +19 -5
- package/dist/src/oracle/index.js +34 -6
- package/dist/src/oracle/price-update-rule.d.ts +180 -0
- package/dist/src/oracle/price-update-rule.js +53 -0
- package/dist/src/oracle/pyth.d.ts +80 -11
- package/dist/src/oracle/pyth.js +82 -16
- package/dist/src/oracle/rule-registry.d.ts +37 -0
- package/dist/src/oracle/rule-registry.js +56 -0
- package/dist/src/oracle/rules/pyth-core-rule.d.ts +15 -0
- package/dist/src/oracle/rules/pyth-core-rule.js +81 -0
- package/dist/src/oracle/rules/pyth-lazer-rule.d.ts +41 -0
- package/dist/src/oracle/rules/pyth-lazer-rule.js +189 -0
- package/dist/src/oracle/rules/sponsor.d.ts +11 -7
- package/dist/src/oracle/rules/sponsor.js +11 -7
- package/dist/src/oracle/update-fetch.d.ts +85 -0
- package/dist/src/oracle/update-fetch.js +223 -0
- package/dist/src/perp/client.d.ts +22 -1
- package/dist/src/perp/client.js +12 -3
- package/dist/src/perp/config.d.ts +9 -4
- package/dist/src/perp/config.js +81 -18
- package/dist/src/perp/index.d.ts +4 -3
- package/dist/src/perp/index.js +2 -2
- package/dist/src/perp/tx-builders/common.d.ts +52 -15
- package/dist/src/perp/tx-builders/common.js +39 -6
- package/dist/src/perp/tx-builders/wlp.d.ts +11 -3
- package/dist/src/perp/tx-builders/wlp.js +29 -3
- package/dist/src/perp/tx-builders.d.ts +3 -3
- package/dist/src/perp/tx-builders.js +3 -3
- package/dist/src/unified-client.d.ts +17 -0
- package/dist/src/unified-client.js +2 -0
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* and the request+execute envelope (optional Pyth-sponsor flow + pre-sweep).
|
|
5
5
|
*/
|
|
6
6
|
import { Transaction, type TransactionArgument } from "@mysten/sui/transactions";
|
|
7
|
-
import { PythCache } from "../../oracle/index.ts";
|
|
7
|
+
import { PythCache, type OracleFeeSource, 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. */
|
|
@@ -14,16 +14,31 @@ export interface CommonBuildOpts {
|
|
|
14
14
|
/** Share a `PythCache` across builders to avoid redundant pyth_state reads. */
|
|
15
15
|
pythCache?: PythCache;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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.
|
|
25
27
|
*/
|
|
26
28
|
useSponsor?: boolean;
|
|
29
|
+
/**
|
|
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`.
|
|
40
|
+
*/
|
|
41
|
+
allowGasFee?: boolean;
|
|
27
42
|
/**
|
|
28
43
|
* Pre-sweep parked backing assets (USDC, USDsui, …) at the wxa account's
|
|
29
44
|
* address into USD credit, plus any CREDIT coins/funds at the address into
|
|
@@ -42,6 +57,15 @@ export interface CommonBuildOpts {
|
|
|
42
57
|
* {@link buildConsolidateToUsdTx} separately.
|
|
43
58
|
*/
|
|
44
59
|
consolidateToUsd?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* BE prefetch-cache seam for the oracle update-data fetch — forwarded
|
|
62
|
+
* verbatim into `refreshOraclePrices`'s `updateDataProvider` opt (see
|
|
63
|
+
* `UpdateDataProvider` in `oracle/price-update-rule.ts`). Default: none
|
|
64
|
+
* (always a live fetch). A caller-supplied provider that misses or throws
|
|
65
|
+
* still falls back to a live fetch — this option can only make a refresh
|
|
66
|
+
* faster, never break it.
|
|
67
|
+
*/
|
|
68
|
+
updateDataProvider?: UpdateDataProvider;
|
|
45
69
|
}
|
|
46
70
|
interface RequestParams {
|
|
47
71
|
ticker: string;
|
|
@@ -57,20 +81,33 @@ export declare function newTx(opts?: CommonBuildOpts): Transaction;
|
|
|
57
81
|
*/
|
|
58
82
|
export declare function refreshWlpPoolOracles(tx: Transaction, client: PerpClient, extraTickers: string[], opts: {
|
|
59
83
|
cache?: PythCache;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
packageId: string;
|
|
63
|
-
};
|
|
84
|
+
/** Forwarded to `refreshOraclePrices` — already resolved by the caller (see `OracleFeeSource`). */
|
|
85
|
+
feeSource?: OracleFeeSource;
|
|
64
86
|
lpType?: string;
|
|
87
|
+
updateDataProvider?: UpdateDataProvider;
|
|
65
88
|
}): Promise<void>;
|
|
66
89
|
/**
|
|
67
|
-
* Build the *Request + execute envelope with
|
|
90
|
+
* Build the *Request + execute envelope with the config-driven Pyth sponsor flow:
|
|
68
91
|
*
|
|
92
|
+
* [maybeConsolidate(tx)]
|
|
69
93
|
* [fund = sponsor.request()]
|
|
70
|
-
* refreshOraclePrices(...,
|
|
94
|
+
* refreshOraclePrices(..., feeSource?)
|
|
71
95
|
* req = buildRequest()
|
|
72
96
|
* [sponsor.reimburse(fund, req)]
|
|
73
97
|
* trading::execute(req)
|
|
98
|
+
*
|
|
99
|
+
* 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
|
|
106
|
+
* mid-build on-chain-read failures — not a new hole. It matters only for a
|
|
107
|
+
* caller that passed in their OWN `opts.tx` (reusing one `Transaction`
|
|
108
|
+
* across builder calls, e.g. to compose several actions in one PTB); such a
|
|
109
|
+
* caller must discard the whole `tx` on any throw from this function, not
|
|
110
|
+
* just retry the failed step.
|
|
74
111
|
*/
|
|
75
112
|
export declare function wrapRequestAndExecute(client: PerpClient, tx: Transaction, req: RequestParams & {
|
|
76
113
|
accountId: string;
|
|
@@ -29,32 +29,65 @@ async function refreshWlpPoolOracles(tx, client, extraTickers, opts) {
|
|
|
29
29
|
const oracleTickers = Array.from(new Set([...extraTickers, ...poolTickers]));
|
|
30
30
|
await (0, index_ts_1.refreshOraclePrices)(tx, client, oracleTickers, {
|
|
31
31
|
cache: opts.cache,
|
|
32
|
-
|
|
32
|
+
feeSource: opts.feeSource,
|
|
33
|
+
updateDataProvider: opts.updateDataProvider,
|
|
33
34
|
});
|
|
34
35
|
for (const tokenType of Object.values(client.config.packages.wlp.pool_tokens)) {
|
|
35
36
|
(0, wlp_ts_1.updateTokenValue)(client, tx, { tokenType, lpType: opts.lpType });
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
39
|
-
* Build the *Request + execute envelope with
|
|
40
|
+
* Build the *Request + execute envelope with the config-driven Pyth sponsor flow:
|
|
40
41
|
*
|
|
42
|
+
* [maybeConsolidate(tx)]
|
|
41
43
|
* [fund = sponsor.request()]
|
|
42
|
-
* refreshOraclePrices(...,
|
|
44
|
+
* refreshOraclePrices(..., feeSource?)
|
|
43
45
|
* req = buildRequest()
|
|
44
46
|
* [sponsor.reimburse(fund, req)]
|
|
45
47
|
* trading::execute(req)
|
|
48
|
+
*
|
|
49
|
+
* Accepted ordering caveat: `maybeConsolidate` runs FIRST and can itself
|
|
50
|
+
* append PTB commands (the consolidation sweep) before the fee-source check
|
|
51
|
+
* inside `refreshOraclePrices` ever runs — so an `OracleFeeSourceUnavailable`
|
|
52
|
+
* throw here is NOT the "zero commands appended" guarantee
|
|
53
|
+
* `refreshOraclePrices` gives its own callers (see its docblock in
|
|
54
|
+
* `aggregate.ts`); `tx` can already carry the sweep. This is the same
|
|
55
|
+
* discard-tx-on-throw contract every `build*Tx` composer already has for
|
|
56
|
+
* mid-build on-chain-read failures — not a new hole. It matters only for a
|
|
57
|
+
* caller that passed in their OWN `opts.tx` (reusing one `Transaction`
|
|
58
|
+
* across builder calls, e.g. to compose several actions in one PTB); such a
|
|
59
|
+
* caller must discard the whole `tx` on any throw from this function, not
|
|
60
|
+
* just retry the failed step.
|
|
46
61
|
*/
|
|
47
62
|
async function wrapRequestAndExecute(client, tx, req, collateralTicker, opts, buildRequest) {
|
|
48
63
|
await maybeConsolidate(client, tx, req.accountId, opts);
|
|
49
|
-
|
|
64
|
+
// Fee source + witness attachment is config-driven, not a caller flag: the
|
|
65
|
+
// sponsor fund is opened (and later reimbursed) whenever this client's
|
|
66
|
+
// config has `pyth_sponsor_rule` deployed — regardless of the deprecated
|
|
67
|
+
// `useSponsor` flag (see its JSDoc). `allowGasFee` is the only caller lever
|
|
68
|
+
// left, and it only matters when config has NO sponsor rule to open (see
|
|
69
|
+
// `OracleFeeSourceUnavailable` in `oracle/pyth.ts`).
|
|
70
|
+
//
|
|
71
|
+
// `feeSource` is resolved HERE, once, from that same decision — sponsor
|
|
72
|
+
// beats gas structurally because this is the only branch that ever sees
|
|
73
|
+
// both candidates; everything downstream (`refreshWlpPoolOracles` →
|
|
74
|
+
// `refreshOraclePrices` → `BuildUpdateOpts` → `PythCoreRule` →
|
|
75
|
+
// `buildPythPriceUpdateCalls`) just carries the single resolved value.
|
|
50
76
|
let sponsorFund;
|
|
51
|
-
if (
|
|
77
|
+
if (client.config.packages.pyth_sponsor_rule) {
|
|
52
78
|
sponsorFund = (0, index_ts_1.openPythSponsorFund)(tx, client);
|
|
79
|
+
}
|
|
80
|
+
const feeSource = sponsorFund
|
|
81
|
+
? { kind: "sponsor", ...sponsorFund }
|
|
82
|
+
: opts?.allowGasFee
|
|
83
|
+
? { kind: "gas" }
|
|
84
|
+
: undefined;
|
|
53
85
|
if (!opts?.skipOraclePriceRefresh) {
|
|
54
86
|
await refreshWlpPoolOracles(tx, client, [req.ticker, collateralTicker], {
|
|
55
87
|
cache: opts?.pythCache,
|
|
56
|
-
|
|
88
|
+
feeSource,
|
|
57
89
|
lpType: req.lpType,
|
|
90
|
+
updateDataProvider: opts?.updateDataProvider,
|
|
58
91
|
});
|
|
59
92
|
}
|
|
60
93
|
const tradingReq = buildRequest(sponsorFund);
|
|
@@ -17,9 +17,15 @@ export interface BuildMintWlpParams extends MintWlpParams, CommonBuildOpts {
|
|
|
17
17
|
* `last_price_refresh_timestamp` so `assert_prices_fresh` inside
|
|
18
18
|
* `mint_wlp` passes.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
20
|
+
* Never uses the pyth_sponsor flow — `mint_wlp` produces no
|
|
21
21
|
* `TradingRequest`, so there's nothing for the sponsor to attach its
|
|
22
|
-
* witness to
|
|
22
|
+
* witness to, and `pyth_sponsor_rule::reimburse` cannot consume a Fund
|
|
23
|
+
* without one. So when `skipOraclePriceRefresh` is `false` (the refresh
|
|
24
|
+
* actually runs), the caller MUST pass `allowGasFee: true` — the Pyth
|
|
25
|
+
* update fee is drawn from `tx.gas`, which Enoki-sponsored transactions
|
|
26
|
+
* reject; a sponsored caller should keep `skipOraclePriceRefresh: true`
|
|
27
|
+
* instead and rely on freshness from other trade traffic (see
|
|
28
|
+
* `OracleFeeSourceUnavailable` in `oracle/pyth.ts`).
|
|
23
29
|
*/
|
|
24
30
|
export declare function buildMintWlpTx(client: PerpClient, params: BuildMintWlpParams): Promise<Transaction>;
|
|
25
31
|
export interface BuildMintAndStakeWlpParams extends BuildMintWlpParams {
|
|
@@ -62,7 +68,9 @@ export interface BuildUnstakeAndRequestRedeemWlpParams extends Omit<RequestRedee
|
|
|
62
68
|
* Refreshes every WLP pool-token oracle by default — `request_redeem` runs
|
|
63
69
|
* `assert_prices_fresh` internally, so a stale oracle would abort the PTB.
|
|
64
70
|
* Pass `skipOraclePriceRefresh: true` only when the caller is composing this
|
|
65
|
-
* into a larger PTB that already pre-pumps prices.
|
|
71
|
+
* into a larger PTB that already pre-pumps prices. Like `buildMintWlpTx`,
|
|
72
|
+
* `request_redeem` produces no `TradingRequest`, so a non-skipped refresh
|
|
73
|
+
* requires `allowGasFee: true` (see `buildMintWlpTx`'s doc comment).
|
|
66
74
|
*/
|
|
67
75
|
export declare function buildUnstakeAndRequestRedeemWlpTx(client: PerpClient, params: BuildUnstakeAndRequestRedeemWlpParams): Promise<Transaction>;
|
|
68
76
|
export interface BuildCancelRedeemAndStakeWlpParams extends CancelRedeemWlpParams, CommonBuildOpts {
|
|
@@ -18,17 +18,29 @@ const common_ts_1 = require("./common.js");
|
|
|
18
18
|
* `last_price_refresh_timestamp` so `assert_prices_fresh` inside
|
|
19
19
|
* `mint_wlp` passes.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
21
|
+
* Never uses the pyth_sponsor flow — `mint_wlp` produces no
|
|
22
22
|
* `TradingRequest`, so there's nothing for the sponsor to attach its
|
|
23
|
-
* witness to
|
|
23
|
+
* witness to, and `pyth_sponsor_rule::reimburse` cannot consume a Fund
|
|
24
|
+
* without one. So when `skipOraclePriceRefresh` is `false` (the refresh
|
|
25
|
+
* actually runs), the caller MUST pass `allowGasFee: true` — the Pyth
|
|
26
|
+
* update fee is drawn from `tx.gas`, which Enoki-sponsored transactions
|
|
27
|
+
* reject; a sponsored caller should keep `skipOraclePriceRefresh: true`
|
|
28
|
+
* instead and rely on freshness from other trade traffic (see
|
|
29
|
+
* `OracleFeeSourceUnavailable` in `oracle/pyth.ts`).
|
|
24
30
|
*/
|
|
25
31
|
async function buildMintWlpTx(client, params) {
|
|
26
32
|
const tx = (0, common_ts_1.newTx)(params);
|
|
27
33
|
await (0, common_ts_1.maybeConsolidate)(client, tx, params.accountId, params);
|
|
34
|
+
// `mint_wlp` has no sponsor flow to resolve against (see the doc comment
|
|
35
|
+
// above) — the only candidate source at this edge is the caller's
|
|
36
|
+
// ergonomic `allowGasFee` opt-in.
|
|
37
|
+
const feeSource = params.allowGasFee ? { kind: "gas" } : undefined;
|
|
28
38
|
if (!params.skipOraclePriceRefresh) {
|
|
29
39
|
await (0, common_ts_1.refreshWlpPoolOracles)(tx, client, [params.depositTicker], {
|
|
30
40
|
cache: params.pythCache,
|
|
31
41
|
lpType: params.lpType,
|
|
42
|
+
feeSource,
|
|
43
|
+
updateDataProvider: params.updateDataProvider,
|
|
32
44
|
});
|
|
33
45
|
}
|
|
34
46
|
(0, wlp_ts_1.mintWlp)(client, tx, params);
|
|
@@ -46,10 +58,16 @@ async function buildMintWlpTx(client, params) {
|
|
|
46
58
|
async function buildMintAndStakeWlpTx(client, params) {
|
|
47
59
|
const tx = (0, common_ts_1.newTx)(params);
|
|
48
60
|
await (0, common_ts_1.maybeConsolidate)(client, tx, params.accountId, params);
|
|
61
|
+
// `mint_wlp` has no sponsor flow to resolve against (see `buildMintWlpTx`'s
|
|
62
|
+
// doc comment) — the only candidate source at this edge is the caller's
|
|
63
|
+
// ergonomic `allowGasFee` opt-in.
|
|
64
|
+
const feeSource = params.allowGasFee ? { kind: "gas" } : undefined;
|
|
49
65
|
if (!params.skipOraclePriceRefresh) {
|
|
50
66
|
await (0, common_ts_1.refreshWlpPoolOracles)(tx, client, [params.depositTicker], {
|
|
51
67
|
cache: params.pythCache,
|
|
52
68
|
lpType: params.lpType,
|
|
69
|
+
feeSource,
|
|
70
|
+
updateDataProvider: params.updateDataProvider,
|
|
53
71
|
});
|
|
54
72
|
}
|
|
55
73
|
const stakeAlias = params.stakeAlias ?? "WLP";
|
|
@@ -71,16 +89,24 @@ async function buildMintAndStakeWlpTx(client, params) {
|
|
|
71
89
|
* Refreshes every WLP pool-token oracle by default — `request_redeem` runs
|
|
72
90
|
* `assert_prices_fresh` internally, so a stale oracle would abort the PTB.
|
|
73
91
|
* Pass `skipOraclePriceRefresh: true` only when the caller is composing this
|
|
74
|
-
* into a larger PTB that already pre-pumps prices.
|
|
92
|
+
* into a larger PTB that already pre-pumps prices. Like `buildMintWlpTx`,
|
|
93
|
+
* `request_redeem` produces no `TradingRequest`, so a non-skipped refresh
|
|
94
|
+
* requires `allowGasFee: true` (see `buildMintWlpTx`'s doc comment).
|
|
75
95
|
*/
|
|
76
96
|
async function buildUnstakeAndRequestRedeemWlpTx(client, params) {
|
|
77
97
|
const tx = (0, common_ts_1.newTx)(params);
|
|
78
98
|
const stakeAlias = params.stakeAlias ?? "WLP";
|
|
79
99
|
await (0, common_ts_1.maybeConsolidate)(client, tx, params.accountId, params);
|
|
100
|
+
// `request_redeem` has no sponsor flow to resolve against (see
|
|
101
|
+
// `buildMintWlpTx`'s doc comment) — the only candidate source at this edge
|
|
102
|
+
// is the caller's ergonomic `allowGasFee` opt-in.
|
|
103
|
+
const feeSource = params.allowGasFee ? { kind: "gas" } : undefined;
|
|
80
104
|
if (!params.skipOraclePriceRefresh) {
|
|
81
105
|
await (0, common_ts_1.refreshWlpPoolOracles)(tx, client, [], {
|
|
82
106
|
cache: params.pythCache,
|
|
83
107
|
lpType: params.lpType,
|
|
108
|
+
feeSource,
|
|
109
|
+
updateDataProvider: params.updateDataProvider,
|
|
84
110
|
});
|
|
85
111
|
}
|
|
86
112
|
(0, staking_ts_1.unstake)(client, tx, {
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Each `build*Tx` composer creates (or appends to) a `Transaction`, refreshes
|
|
5
5
|
* the on-chain `Oracle` via Pyth, optionally pre-sweeps parked balances
|
|
6
|
-
* (`consolidateToUsd`), wires the `pyth_sponsor_rule` flow when
|
|
7
|
-
* calls the matching `*_request` + `execute`. Implementations are
|
|
8
|
-
* domain under `tx-builders/`:
|
|
6
|
+
* (`consolidateToUsd`), wires the `pyth_sponsor_rule` flow when deployed in
|
|
7
|
+
* config, and calls the matching `*_request` + `execute`. Implementations are
|
|
8
|
+
* split by domain under `tx-builders/`:
|
|
9
9
|
*
|
|
10
10
|
* common.ts CommonBuildOpts + request/execute envelope + oracle refresh
|
|
11
11
|
* consolidate.ts parked-balance → wxUSD pre-sweep (appendConsolidate*)
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Each `build*Tx` composer creates (or appends to) a `Transaction`, refreshes
|
|
6
6
|
* the on-chain `Oracle` via Pyth, optionally pre-sweeps parked balances
|
|
7
|
-
* (`consolidateToUsd`), wires the `pyth_sponsor_rule` flow when
|
|
8
|
-
* calls the matching `*_request` + `execute`. Implementations are
|
|
9
|
-
* domain under `tx-builders/`:
|
|
7
|
+
* (`consolidateToUsd`), wires the `pyth_sponsor_rule` flow when deployed in
|
|
8
|
+
* config, and calls the matching `*_request` + `execute`. Implementations are
|
|
9
|
+
* split by domain under `tx-builders/`:
|
|
10
10
|
*
|
|
11
11
|
* common.ts CommonBuildOpts + request/execute envelope + oracle refresh
|
|
12
12
|
* consolidate.ts parked-balance → wxUSD pre-sweep (appendConsolidate*)
|
|
@@ -32,6 +32,8 @@ import { Transaction } from "@mysten/sui/transactions";
|
|
|
32
32
|
import * as accountOps from "./account/index.ts";
|
|
33
33
|
import * as perpReferral from "./account/referral.ts";
|
|
34
34
|
import type { Network } from "./constants.ts";
|
|
35
|
+
import type { PythGeneration } from "./oracle/config.ts";
|
|
36
|
+
import type { OracleSource } from "./oracle/price-update-rule.ts";
|
|
35
37
|
import { PerpClient, type CreateClientOptions as PerpCreateOptions } from "./perp/client.ts";
|
|
36
38
|
import * as perpFetch from "./perp/fetch.ts";
|
|
37
39
|
import * as perpTx from "./perp/tx-builders.ts";
|
|
@@ -363,6 +365,21 @@ export interface ClientCreateOptions {
|
|
|
363
365
|
waterxConfigUrl?: string;
|
|
364
366
|
/** Memoize the fetched config JSON. */
|
|
365
367
|
cache?: boolean;
|
|
368
|
+
/**
|
|
369
|
+
* Selects which `PriceUpdateRule` the perp line's `refreshOraclePrices` uses
|
|
370
|
+
* for the on-chain price-update leg (see `OracleHost.oracleSource`).
|
|
371
|
+
* Default: `'pyth_rule'`. Perp-line only. The SDK never reads `process.env`
|
|
372
|
+
* — pass this from your own env var (e.g. `ORACLE_SOURCE`).
|
|
373
|
+
*/
|
|
374
|
+
oracleSource?: OracleSource;
|
|
375
|
+
/**
|
|
376
|
+
* Selects which Pyth Core contract generation feeds the perp line's
|
|
377
|
+
* `client.perp.pyth` when the config JSON has no explicit `pyth` override:
|
|
378
|
+
* `'core'` (default) or `'pro'` (post-2026-08-18 Pro-compatible contracts +
|
|
379
|
+
* Hermes-compatible endpoint; pair with `pyth.api_key`). Perp-line only.
|
|
380
|
+
* See `PythGeneration` / `PYTH_PRO_DEFAULTS`.
|
|
381
|
+
*/
|
|
382
|
+
pythGeneration?: PythGeneration;
|
|
366
383
|
/** Perp-line overrides (network, grpcUrl, waterxConfigUrl, cache, …). */
|
|
367
384
|
perp?: PerpLineOptions;
|
|
368
385
|
/** Prediction-line overrides (network, grpcUrl, waterxConfigUrl, cache, settlement, …). */
|
|
@@ -204,6 +204,8 @@ class WaterXClient {
|
|
|
204
204
|
grpcUrl: opts.grpcUrl,
|
|
205
205
|
waterxConfigUrl: opts.waterxConfigUrl,
|
|
206
206
|
cache: opts.cache,
|
|
207
|
+
oracleSource: opts.oracleSource,
|
|
208
|
+
pythGeneration: opts.pythGeneration,
|
|
207
209
|
...perpRest,
|
|
208
210
|
});
|
|
209
211
|
const predictClient = await client_ts_2.PredictClient.create(resolvedPredictNetwork, {
|
|
@@ -2,40 +2,58 @@
|
|
|
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
|
-
* (Pyth / Supra / Constant), then `aggregate`s.
|
|
5
|
+
* (Pyth / Lazer / Supra / Constant), then `aggregate`s.
|
|
6
6
|
*
|
|
7
7
|
* Per ticker:
|
|
8
8
|
* collector = oracle::new_collector(ticker)
|
|
9
9
|
* [pyth_rule::feed] when the ticker has a pyth_rule.feeds entry
|
|
10
|
+
* [pyth_lazer_rule::feed] when the update leg produced a verified lazer Update
|
|
10
11
|
* [supra_rule::feed] when supra is enabled + wired
|
|
11
12
|
* [constant_rule::feed] when the ticker is a constant ticker
|
|
12
13
|
* oracle::aggregate(oracle, collector)
|
|
13
14
|
*
|
|
14
|
-
* The fed rule set must
|
|
15
|
+
* The fed rule set must cover the on-chain weighted set for the ticker —
|
|
15
16
|
* `aggregator::remove_outliers` aborts `EMissingPriceSource` if a weighted rule
|
|
16
|
-
* is missing from the collector
|
|
17
|
+
* is missing from the collector (an abstaining feed call counts as present;
|
|
18
|
+
* a fed-but-unweighted rule is silently dropped).
|
|
19
|
+
*
|
|
20
|
+
* `refreshOraclePrices` additionally routes the on-chain price *update* leg
|
|
21
|
+
* (the fetch + verify/push step, before any of the above feeding) through the
|
|
22
|
+
* `PriceUpdateRule` selected by `host.oracleSource` — see `rule-registry.ts`.
|
|
17
23
|
*/
|
|
18
24
|
import type { Transaction, TransactionArgument } from "@mysten/sui/transactions";
|
|
19
25
|
import type { OracleHost } from "./host.ts";
|
|
20
|
-
import {
|
|
26
|
+
import type { OracleSource, PriceUpdateRule, UpdateDataProvider } from "./price-update-rule.ts";
|
|
27
|
+
import { type OracleFeeSource, type PythCache } from "./pyth.ts";
|
|
21
28
|
/**
|
|
22
29
|
* Aggregate one ticker's price into the shared `Oracle`: build a collector, feed
|
|
23
30
|
* every rule the ticker is configured for, then `aggregate`.
|
|
24
31
|
*
|
|
25
32
|
* - **Pyth** — fed when `priceInfoObjectId` is supplied (i.e. the ticker has a
|
|
26
|
-
* `pyth_rule.feeds` entry).
|
|
27
|
-
* `PriceInfoObject`
|
|
28
|
-
*
|
|
29
|
-
*
|
|
33
|
+
* `pyth_rule.feeds` entry). When this PTB's update leg refreshed the
|
|
34
|
+
* `PriceInfoObject` it contributes a fresh price; when it did not (a
|
|
35
|
+
* lazer-routed ticker), the on-chain rule only READS the object and abstains
|
|
36
|
+
* if it is stale — it never aborts — so the call stays mandatory while
|
|
37
|
+
* `pyth_rule` remains in the ticker's on-chain weighted set
|
|
38
|
+
* (`EMissingPriceSource` requires every weighted rule to appear).
|
|
39
|
+
* - **Lazer** — fed when `lazerUpdate` is supplied: the verified
|
|
40
|
+
* `pyth_lazer::update::Update` produced by this PTB's lazer update leg
|
|
41
|
+
* (see `PythLazerRule.buildUpdateCalls`). If the ticker's aggregator does
|
|
42
|
+
* not (yet) weight `PythLazerRule`, the contribution is silently dropped
|
|
43
|
+
* on-chain — feeding ahead of the weight migration is harmless.
|
|
44
|
+
* - **Supra** — fed alongside Pyth/Lazer when supra is enabled + wired
|
|
45
|
+
* (abstains on-chain for symbols it has no pair for).
|
|
30
46
|
* - **Constant** — fed when the ticker is a constant ticker
|
|
31
47
|
* ({@link OracleHost.isConstantTicker}).
|
|
32
48
|
*
|
|
33
|
-
* "Dual-feed" (Pyth + Constant) and "constant-only" are not
|
|
34
|
-
* fall out of which rules the ticker is in. Throws if no
|
|
49
|
+
* "Dual-feed" (Pyth + Constant, or Pyth + Lazer) and "constant-only" are not
|
|
50
|
+
* special cases — they fall out of which rules the ticker is in. Throws if no
|
|
51
|
+
* rule applies.
|
|
35
52
|
*/
|
|
36
53
|
export declare function aggregateTicker(tx: Transaction, host: OracleHost, args: {
|
|
37
54
|
ticker: string;
|
|
38
55
|
priceInfoObjectId?: string;
|
|
56
|
+
lazerUpdate?: TransactionArgument;
|
|
39
57
|
}): void;
|
|
40
58
|
/**
|
|
41
59
|
* Thin wrapper over {@link aggregateTicker} for a Pyth-fed ticker. Kept for
|
|
@@ -61,14 +79,77 @@ export declare function aggregateTickerWithConstant(tx: Transaction, host: Oracl
|
|
|
61
79
|
/**
|
|
62
80
|
* Refresh multiple tickers in one PTB. For each ticker {@link aggregateTicker}
|
|
63
81
|
* feeds whichever rules it is configured for (Pyth if it has a `pyth_rule.feeds`
|
|
64
|
-
* entry,
|
|
65
|
-
*
|
|
66
|
-
*
|
|
82
|
+
* entry, Lazer if the lazer update leg served it — see below — Supra when
|
|
83
|
+
* enabled, Constant when it's a constant ticker).
|
|
84
|
+
*
|
|
85
|
+
* Before that, the on-chain price *update* leg is routed by `host.oracleSource`
|
|
86
|
+
* (see `rule-registry.ts`): the selected rule serves every ticker in its
|
|
87
|
+
* `supportedTickers(host)`; tickers it doesn't cover fall back to `pyth_rule`
|
|
88
|
+
* (`PythCoreRule`) when THEY support it — so when `oracleSource` IS `'pyth_rule'`
|
|
89
|
+
* there is exactly one group, identical to the pre-routing behavior. A ticker
|
|
90
|
+
* supported by neither is simply skipped from this leg (no fetch/build call for
|
|
91
|
+
* it) — the same way today's non-pyth tickers (e.g. constant-only) always were;
|
|
92
|
+
* it still gets aggregated below via whichever rule {@link aggregateTicker} finds.
|
|
93
|
+
*
|
|
94
|
+
* Each group's fetch + build runs against its own rule, which guarantees
|
|
95
|
+
* per-rule PTB atomicity (no mixed-generation payload within one rule's calls).
|
|
96
|
+
* When `oracleSource` isn't `'pyth_rule'`, one PTB may legitimately carry BOTH a
|
|
97
|
+
* non-Pyth-Core block (selected group) and a Pyth Core block (fallback group) —
|
|
98
|
+
* each verifies against its own contract objects, so that's fine. A fee-source
|
|
99
|
+
* pre-check runs first, across every group's `requiresFeeSource` — BEFORE any
|
|
100
|
+
* off-chain fetch or PTB mutation — so a fee-charging group with no
|
|
101
|
+
* `opts.feeSource` throws `OracleFeeSourceUnavailable` with zero wasted
|
|
102
|
+
* network calls and zero stray moveCalls, even in a mixed shape (e.g. a
|
|
103
|
+
* fee-free Lazer group ordered ahead of a Pyth Core fallback group). Only once
|
|
104
|
+
* that check passes do all groups' off-chain fetches run concurrently
|
|
105
|
+
* (`Promise.all`) and complete before any PTB mutation; on-chain reads inside
|
|
106
|
+
* `buildUpdateCalls` can still fail mid-append for other reasons — callers
|
|
107
|
+
* discard the tx on any throw.
|
|
108
|
+
*
|
|
109
|
+
* **Collector-feed leg is rule-aware:** a lazer-served group's
|
|
110
|
+
* `buildUpdateCalls` returns the verified `Update` PTB value
|
|
111
|
+
* ({@link RuleUpdateHandle}), and every ticker in that group is aggregated
|
|
112
|
+
* with `lazerUpdate` set so {@link aggregateTicker} appends
|
|
113
|
+
* `pyth_lazer_rule::feed` against it. A lazer-routed ticker that still has a
|
|
114
|
+
* `pyth_rule.feeds` entry ALSO keeps its `pyth_rule::feed` leg — required
|
|
115
|
+
* on-chain while `pyth_rule` stays in the ticker's weighted set
|
|
116
|
+
* (`aggregator::remove_outliers` aborts `EMissingPriceSource` unless every
|
|
117
|
+
* weighted rule appears in the collector; an abstention counts as
|
|
118
|
+
* appearing), and safe: `pyth_rule::feed` only READS the `PriceInfoObject`
|
|
119
|
+
* this PTB never refreshed and abstains when it is stale rather than
|
|
120
|
+
* aborting. Conversely, a lazer feed call on an aggregator that does not
|
|
121
|
+
* (yet) weight `PythLazerRule` is silently dropped on-chain — so
|
|
122
|
+
* lazer-routing a ticker ahead of its on-chain weight migration prices it
|
|
123
|
+
* from the remaining weighted rules instead of failing.
|
|
67
124
|
*/
|
|
68
125
|
export declare function refreshOraclePrices(tx: Transaction, host: OracleHost, tickers: string[], opts?: {
|
|
69
126
|
cache?: PythCache;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
127
|
+
/**
|
|
128
|
+
* The single resolved fee source for the Pyth update fee, forwarded
|
|
129
|
+
* verbatim to each group's `PriceUpdateRule.buildUpdateCalls` as
|
|
130
|
+
* `BuildUpdateOpts.feeSource`. Already-resolved by the caller (see
|
|
131
|
+
* {@link OracleFeeSource}'s own doc for where/how) — this function makes
|
|
132
|
+
* no sponsor-vs-gas decision itself, it only checks whether a source was
|
|
133
|
+
* resolved at all. Ignored by rules with no update fee (e.g.
|
|
134
|
+
* `pyth_lazer_rule`). Building with `feeSource` unset throws
|
|
135
|
+
* `OracleFeeSourceUnavailable` (see `oracle/pyth.ts`) instead of
|
|
136
|
+
* silently drawing from `tx.gas`.
|
|
137
|
+
*/
|
|
138
|
+
feeSource?: OracleFeeSource;
|
|
139
|
+
/**
|
|
140
|
+
* @internal Test-only: layer fake `PriceUpdateRule`s on top of the
|
|
141
|
+
* production registry (see `rule-registry.ts`'s `resolveOracleRule`).
|
|
142
|
+
* Production callers never set this — routing is by `host.oracleSource`
|
|
143
|
+
* alone.
|
|
144
|
+
*/
|
|
145
|
+
ruleOverrides?: Partial<Record<OracleSource, PriceUpdateRule>>;
|
|
146
|
+
/**
|
|
147
|
+
* BE prefetch-cache seam: checked per group BEFORE that group's live
|
|
148
|
+
* `rule.fetchUpdateData`. See {@link UpdateDataProvider}. A cache miss
|
|
149
|
+
* (`null`) or a throw from the provider falls back to the live fetch —
|
|
150
|
+
* a degraded/broken cache must never break the money path; a
|
|
151
|
+
* kind-mismatched hit (the provider handed back the wrong rule's
|
|
152
|
+
* payload) throws instead, since that is a caller bug, not a cache miss.
|
|
153
|
+
*/
|
|
154
|
+
updateDataProvider?: UpdateDataProvider;
|
|
74
155
|
}): Promise<void>;
|