@waterx/sdk 3.1.1 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -18
- package/dist/cjs/src/account/config.d.ts +0 -16
- package/dist/cjs/src/oracle/aggregate.d.ts +96 -17
- package/dist/cjs/src/oracle/aggregate.js +179 -21
- package/dist/cjs/src/oracle/config.d.ts +85 -2
- package/dist/cjs/src/oracle/config.js +30 -1
- package/dist/cjs/src/oracle/host.d.ts +14 -1
- package/dist/cjs/src/oracle/index.d.ts +19 -5
- package/dist/cjs/src/oracle/index.js +54 -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 +142 -11
- package/dist/cjs/src/oracle/pyth.js +408 -25
- package/dist/cjs/src/oracle/rule-registry.d.ts +41 -0
- package/dist/cjs/src/oracle/rule-registry.js +65 -0
- package/dist/cjs/src/oracle/rules/pyth-core-rule.d.ts +15 -0
- package/dist/cjs/src/oracle/rules/pyth-core-rule.js +99 -0
- package/dist/cjs/src/oracle/rules/pyth-lazer-rule.d.ts +41 -0
- package/dist/cjs/src/oracle/rules/pyth-lazer-rule.js +197 -0
- package/dist/cjs/src/oracle/rules/pyth-rule.js +5 -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 +115 -0
- package/dist/cjs/src/oracle/update-fetch.js +285 -0
- package/dist/cjs/src/perp/client.d.ts +40 -5
- package/dist/cjs/src/perp/client.js +18 -3
- package/dist/cjs/src/perp/config.d.ts +10 -7
- package/dist/cjs/src/perp/config.js +77 -17
- package/dist/cjs/src/perp/index.d.ts +3 -2
- package/dist/cjs/src/perp/index.js +6 -2
- 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 +25 -0
- package/dist/cjs/src/unified-client.js +3 -0
- package/dist/src/account/config.d.ts +0 -16
- package/dist/src/oracle/aggregate.d.ts +96 -17
- package/dist/src/oracle/aggregate.js +180 -22
- package/dist/src/oracle/config.d.ts +85 -2
- package/dist/src/oracle/config.js +29 -0
- package/dist/src/oracle/host.d.ts +14 -1
- package/dist/src/oracle/index.d.ts +19 -5
- package/dist/src/oracle/index.js +42 -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 +142 -11
- package/dist/src/oracle/pyth.js +402 -24
- package/dist/src/oracle/rule-registry.d.ts +41 -0
- package/dist/src/oracle/rule-registry.js +60 -0
- package/dist/src/oracle/rules/pyth-core-rule.d.ts +15 -0
- package/dist/src/oracle/rules/pyth-core-rule.js +96 -0
- package/dist/src/oracle/rules/pyth-lazer-rule.d.ts +41 -0
- package/dist/src/oracle/rules/pyth-lazer-rule.js +192 -0
- package/dist/src/oracle/rules/pyth-rule.js +5 -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 +115 -0
- package/dist/src/oracle/update-fetch.js +277 -0
- package/dist/src/perp/client.d.ts +40 -5
- package/dist/src/perp/client.js +18 -3
- package/dist/src/perp/config.d.ts +10 -7
- package/dist/src/perp/config.js +77 -17
- package/dist/src/perp/index.d.ts +3 -2
- package/dist/src/perp/index.js +1 -1
- 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 +25 -0
- package/dist/src/unified-client.js +3 -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 { PythFetchPolicy } 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,29 @@ export interface ClientCreateOptions {
|
|
|
363
365
|
waterxConfigUrl?: string;
|
|
364
366
|
/** Memoize the fetched config JSON. */
|
|
365
367
|
cache?: boolean;
|
|
368
|
+
/**
|
|
369
|
+
* The perp line's oracle price-update source (perp-line only — the
|
|
370
|
+
* prediction line has no oracle leg), forwarded to `PerpClient.create`.
|
|
371
|
+
* Source-neutral by design: a future source need not be Pyth.
|
|
372
|
+
*
|
|
373
|
+
* - `'pyth_rule'` (default) — Pyth Core updates on Core infra.
|
|
374
|
+
* - `'pyth_lazer_rule'` — Pyth Lazer signed updates (pair with `pythApiKey`
|
|
375
|
+
* and a config carrying `packages.pyth_lazer_rule`).
|
|
376
|
+
*
|
|
377
|
+
* Each source is self-contained with no cross-source fallback; selecting a
|
|
378
|
+
* source whose feed for a ticker is absent fails at tx-build (not at init).
|
|
379
|
+
* See perp `CreateClientOptions.oracleSource` for the full note.
|
|
380
|
+
*/
|
|
381
|
+
oracleSource?: OracleSource;
|
|
382
|
+
/**
|
|
383
|
+
* Pyth Lazer access token, forwarded to the perp line. Required under
|
|
384
|
+
* `oracleSource: 'pyth_lazer_rule'`, unused by `'pyth_rule'`. A SECRET —
|
|
385
|
+
* pass it at init from your own env var; it is never read from the config
|
|
386
|
+
* JSON or `process.env`.
|
|
387
|
+
*/
|
|
388
|
+
pythApiKey?: string;
|
|
389
|
+
/** Retry/timeout policy for the perp line's off-chain oracle fetches. */
|
|
390
|
+
pythFetch?: PythFetchPolicy;
|
|
366
391
|
/** Perp-line overrides (network, grpcUrl, waterxConfigUrl, cache, …). */
|
|
367
392
|
perp?: PerpLineOptions;
|
|
368
393
|
/** Prediction-line overrides (network, grpcUrl, waterxConfigUrl, cache, settlement, …). */
|
|
@@ -204,6 +204,9 @@ class WaterXClient {
|
|
|
204
204
|
grpcUrl: opts.grpcUrl,
|
|
205
205
|
waterxConfigUrl: opts.waterxConfigUrl,
|
|
206
206
|
cache: opts.cache,
|
|
207
|
+
oracleSource: opts.oracleSource,
|
|
208
|
+
pythApiKey: opts.pythApiKey,
|
|
209
|
+
pythFetch: opts.pythFetch,
|
|
207
210
|
...perpRest,
|
|
208
211
|
});
|
|
209
212
|
const predictClient = await client_ts_2.PredictClient.create(resolvedPredictNetwork, {
|
|
@@ -48,28 +48,12 @@ export interface NativeCustodyPackage {
|
|
|
48
48
|
/** Backing assets registered via `add_asset` (array, identified by `type`). */
|
|
49
49
|
assets: NativeCustodyAsset[];
|
|
50
50
|
}
|
|
51
|
-
export interface TrustedEmitterRow {
|
|
52
|
-
/** Source EVM chain's Wormhole chain id (e.g. 10002 = Sepolia). */
|
|
53
|
-
chain_id: number;
|
|
54
|
-
/** 32-byte left-padded EVM bridge address (0x form). */
|
|
55
|
-
evm_bridge_address_32b: string;
|
|
56
|
-
/** Whitelisted 20-byte EVM token addresses (0x form). */
|
|
57
|
-
evm_tokens_20b: string[];
|
|
58
|
-
}
|
|
59
51
|
export interface WormholeBridgePackage {
|
|
60
52
|
published_at: string;
|
|
61
53
|
/** Shared Sui Wormhole `State` object id for this deployment. */
|
|
62
54
|
wormhole_state: string;
|
|
63
|
-
hourly_mint_limit?: string;
|
|
64
55
|
max_mint_per_tx?: string;
|
|
65
|
-
hourly_burn_limit?: string;
|
|
66
56
|
max_burn_per_tx?: string;
|
|
67
|
-
/**
|
|
68
|
-
* @deprecated EVM emitter↔token config now lives solely under `evm.bridge.chains`
|
|
69
|
-
* (deposit_vault = emitter, wormhole_chain_id = chain key). The runtime allowlist is
|
|
70
|
-
* read from the on-chain `Bridge` object, not from config. Kept optional for back-compat.
|
|
71
|
-
*/
|
|
72
|
-
trusted_emitters?: TrustedEmitterRow[];
|
|
73
57
|
/** Shared `Bridge` (phase-5 output). */
|
|
74
58
|
bridge?: string;
|
|
75
59
|
/**
|
|
@@ -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,75 @@ 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 ONE selected source serves every ticker in its
|
|
87
|
+
* `supportedTickers(host)`. There is **no cross-source fallback** — a requested
|
|
88
|
+
* ticker the selected source does not serve, and that is not a constant-only
|
|
89
|
+
* ticker (which needs no price-update leg), fails the build immediately with a
|
|
90
|
+
* clear error naming the ticker and source. That is the deliberate "fail the
|
|
91
|
+
* tx-build, don't silently reroute" contract: a wrong-but-present feed id is
|
|
92
|
+
* NOT validated here (it surfaces on-chain at dry-run); a MISSING feed for the
|
|
93
|
+
* selected source is caught here. When the selected source's feed exists but is
|
|
94
|
+
* wrong, this function does nothing special — the on-chain aggregate aborts at
|
|
95
|
+
* dry-run, which is correct.
|
|
96
|
+
*
|
|
97
|
+
* The selected source's fetch + build runs against its own infra, guaranteeing
|
|
98
|
+
* per-rule PTB atomicity. A fee-source pre-check runs first (the source's
|
|
99
|
+
* `requiresFeeSource`) BEFORE any off-chain fetch or PTB mutation — so a
|
|
100
|
+
* fee-charging source with no `opts.feeSource` throws
|
|
101
|
+
* `OracleFeeSourceUnavailable` with zero wasted network calls and zero stray
|
|
102
|
+
* moveCalls. Only once that check passes does the off-chain fetch run and
|
|
103
|
+
* complete before any PTB mutation; on-chain reads inside `buildUpdateCalls`
|
|
104
|
+
* can still fail mid-append for other reasons — callers discard the tx on any
|
|
105
|
+
* throw.
|
|
106
|
+
*
|
|
107
|
+
* **Collector-feed leg is rule-aware:** a lazer-served group's
|
|
108
|
+
* `buildUpdateCalls` returns the verified `Update` PTB value
|
|
109
|
+
* ({@link RuleUpdateHandle}), and every ticker in that group is aggregated
|
|
110
|
+
* with `lazerUpdate` set so {@link aggregateTicker} appends
|
|
111
|
+
* `pyth_lazer_rule::feed` against it. A lazer-routed ticker that still has a
|
|
112
|
+
* `pyth_rule.feeds` entry ALSO keeps its `pyth_rule::feed` leg — required
|
|
113
|
+
* on-chain while `pyth_rule` stays in the ticker's weighted set
|
|
114
|
+
* (`aggregator::remove_outliers` aborts `EMissingPriceSource` unless every
|
|
115
|
+
* weighted rule appears in the collector; an abstention counts as
|
|
116
|
+
* appearing), and safe: `pyth_rule::feed` only READS the `PriceInfoObject`
|
|
117
|
+
* this PTB never refreshed and abstains when it is stale rather than
|
|
118
|
+
* aborting. Conversely, a lazer feed call on an aggregator that does not
|
|
119
|
+
* (yet) weight `PythLazerRule` is silently dropped on-chain — so
|
|
120
|
+
* lazer-routing a ticker ahead of its on-chain weight migration prices it
|
|
121
|
+
* from the remaining weighted rules instead of failing.
|
|
67
122
|
*/
|
|
68
123
|
export declare function refreshOraclePrices(tx: Transaction, host: OracleHost, tickers: string[], opts?: {
|
|
69
124
|
cache?: PythCache;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
125
|
+
/**
|
|
126
|
+
* The single resolved fee source for the Pyth update fee, forwarded
|
|
127
|
+
* verbatim to each group's `PriceUpdateRule.buildUpdateCalls` as
|
|
128
|
+
* `BuildUpdateOpts.feeSource`. Already-resolved by the caller (see
|
|
129
|
+
* {@link OracleFeeSource}'s own doc for where/how) — this function makes
|
|
130
|
+
* no sponsor-vs-gas decision itself, it only checks whether a source was
|
|
131
|
+
* resolved at all. Ignored by rules with no update fee (e.g.
|
|
132
|
+
* `pyth_lazer_rule`). Building with `feeSource` unset throws
|
|
133
|
+
* `OracleFeeSourceUnavailable` (see `oracle/pyth.ts`) instead of
|
|
134
|
+
* silently drawing from `tx.gas`.
|
|
135
|
+
*/
|
|
136
|
+
feeSource?: OracleFeeSource;
|
|
137
|
+
/**
|
|
138
|
+
* @internal Test-only: layer fake `PriceUpdateRule`s on top of the
|
|
139
|
+
* production registry (see `rule-registry.ts`'s `resolveOracleRule`).
|
|
140
|
+
* Production callers never set this — routing is by `host.oracleSource`
|
|
141
|
+
* alone.
|
|
142
|
+
*/
|
|
143
|
+
ruleOverrides?: Partial<Record<OracleSource, PriceUpdateRule>>;
|
|
144
|
+
/**
|
|
145
|
+
* BE prefetch-cache seam: checked per group BEFORE that group's live
|
|
146
|
+
* `rule.fetchUpdateData`. See {@link UpdateDataProvider}. A cache miss
|
|
147
|
+
* (`null`) or a throw from the provider falls back to the live fetch —
|
|
148
|
+
* a degraded/broken cache must never break the money path; a
|
|
149
|
+
* kind-mismatched hit (the provider handed back the wrong rule's
|
|
150
|
+
* payload) throws instead, since that is a caller bug, not a cache miss.
|
|
151
|
+
*/
|
|
152
|
+
updateDataProvider?: UpdateDataProvider;
|
|
74
153
|
}): Promise<void>;
|