@cetusprotocol/aggregator-sdk 1.6.0 → 1.6.2

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 CHANGED
@@ -27,6 +27,8 @@ Easy Integration: The aggregator is designed to be simple and easy to integrate.
27
27
 
28
28
  Multi-Platform Support: Currently, we have integrated multiple mainstream DEXs on the Sui chain, including cetus, deepbook, kriya, flowx, aftermath, afsui, haedal, volo, turbos etc, allowing users to enjoy a diversified trading experience on a single platform.
29
29
 
30
+ Sponsored Transactions: The SDK can build swap PTBs that do not use the user's gas coin, allowing a separate sponsor account to pay transaction gas.
31
+
30
32
  By using our aggregator, you can trade more efficiently and securely on the Sui blockchain, fully leveraging the various opportunities brought by decentralized finance (DeFi).
31
33
 
32
34
  # Aggregator SDK
@@ -55,7 +57,7 @@ const from = "0x2::sui::SUI"
55
57
  const target =
56
58
  "0x06864a6f921804860930db6ddbe2e16acdf8504495ea7481637a1c8b9a8fe54b::cetus::CETUS"
57
59
 
58
- const routers = await client.findRouters({
60
+ const routerRes = await client.findRouters({
59
61
  from,
60
62
  target,
61
63
  amount,
@@ -70,7 +72,7 @@ const txb = new Transaction()
70
72
 
71
73
  if (routerRes != null) {
72
74
  await client.fastRouterSwap({
73
- routers,
75
+ router: routerRes,
74
76
  txb,
75
77
  slippage: 0.01,
76
78
  })
@@ -85,7 +87,83 @@ if (routerRes != null) {
85
87
  }
86
88
  ```
87
89
 
88
- ### 4. Build PTB and return target coin
90
+ ### 4. Sponsored transaction: sponsor pays gas
91
+
92
+ Set `sponsored: true` when building the swap. In this mode the swap does not use `txb.gas` as input or output, because the gas coin belongs to the sponsor.
93
+
94
+ ```typescript
95
+ import { Transaction } from "@mysten/sui/transactions"
96
+ import { SUI_TYPE_ARG } from "@mysten/sui/utils"
97
+
98
+ const sender = userSigner.toSuiAddress()
99
+ const sponsor = sponsorSigner.toSuiAddress()
100
+ const txb = new Transaction()
101
+
102
+ await client.fastRouterSwap({
103
+ router: routerRes,
104
+ txb,
105
+ slippage: 0.01,
106
+ sponsored: true,
107
+ })
108
+
109
+ // Build command-only bytes. Pass sender so CoinWithBalance can resolve
110
+ // the user's input coins before the sponsor adds gas data.
111
+ const txKindBytes = await client.buildTransactionKind(txb, sender)
112
+
113
+ const { objects: gasCoins } = await client.client.listCoins({
114
+ owner: sponsor,
115
+ coinType: SUI_TYPE_ARG,
116
+ limit: 1,
117
+ })
118
+
119
+ const sponsorCoins = gasCoins.map((coin) => ({
120
+ objectId: coin.objectId,
121
+ version: coin.version,
122
+ digest: coin.digest,
123
+ }))
124
+
125
+ const sponsoredTx = client.buildSponsoredTransaction({
126
+ txKindBytes,
127
+ sender,
128
+ sponsor,
129
+ sponsorCoins,
130
+ gasBudget: "100000000",
131
+ })
132
+
133
+ const result = await client.signAndExecuteSponsoredTransaction(
134
+ sponsoredTx,
135
+ userSigner,
136
+ sponsorSigner
137
+ )
138
+ ```
139
+
140
+ For a gas-station integration, the same flow is split across two parties: the user builds `txKindBytes`, the sponsor sets `gasOwner` and `gasPayment`, then both parties sign the same full transaction bytes.
141
+
142
+ #### Sponsor secret for the real transaction test
143
+
144
+ The real sponsored transaction test reuses the existing wallet secret as the user/sender and adds one sponsor secret:
145
+
146
+ ```bash
147
+ SUI_WALLET_SECRET="user_base64_secret" \
148
+ SUI_SPONSOR_SECRET="sponsor_base64_secret" \
149
+ npx vitest run tests/unit/sponsored.test.ts
150
+ ```
151
+
152
+ `SUI_WALLET_SECRET` owns the swap input coins and signs as the transaction sender. `SUI_SPONSOR_SECRET` owns the gas coin and signs as the gas sponsor. Use two different accounts; the sponsor account only needs enough SUI to cover `SPONSORED_GAS_BUDGET` (`100000000` MIST by default).
153
+
154
+ Optional test variables:
155
+
156
+ ```bash
157
+ SPONSORED_SWAP_AMOUNT=1000000
158
+ SPONSORED_GAS_BUDGET=100000000
159
+ SUI_RPC="https://fullnode.mainnet.sui.io:443"
160
+ ```
161
+
162
+ If either secret is missing, `tests/unit/sponsored.test.ts` is skipped and no transaction is submitted.
163
+
164
+ Keep both secrets in your local shell or `.env` file, and do not commit them.
165
+
166
+ ### 5. Build PTB and return target coin
89
167
 
90
168
  ```typescript
91
169
  const txb = new Transaction()
@@ -93,7 +171,7 @@ const byAmountIn = true
93
171
 
94
172
  if (routerRes != null) {
95
173
  const targetCoin = await client.routerSwap({
96
- routers,
174
+ router: routerRes,
97
175
  txb,
98
176
  inputCoin,
99
177
  slippage: 0.01,
@@ -118,18 +196,18 @@ if (routerRes != null) {
118
196
 
119
197
  | Contract | Tag of Repo | Latest published at address |
120
198
  | ------------------------- | ----------- | ------------------------------------------------------------------ |
121
- | CetusAggregatorV2 | mainnet | 0x40e457bc65a398d2db7026881358fcb7cfa2f1bb052bca41f46c55a1103f2d6f |
199
+ | CetusAggregatorV2 | mainnet | 0x3a7fa58adcd7ff474ca0330c93068b139f5263c0cf9c64e702f5c4b17996ff10 |
122
200
  | CetusAggregatorV2ExtendV1 | mainnet | 0x2edc22bf96c85482b2208624fa9339255d5055113c92fd6c33add48ce971b774 |
123
201
  | CetusAggregatorV2ExtendV2 | mainnet | 0x2e227a3cbc6715518b18ed339d2f967153674b7b257da114ca62c72b2011258a |
124
202
 
125
203
  ## Example
126
204
 
127
205
  ```
128
- CetusAggregatorV2 = { git = "https://github.com/CetusProtocol/aggregator.git", subdir = "packages/cetus-aggregator-v2/mainnet", rev = "mainnet-v1.56.0", override = true }
206
+ CetusAggregatorV2 = { git = "https://github.com/CetusProtocol/aggregator.git", subdir = "packages/cetus-aggregator-v2/mainnet", rev = "mainnet-v1.63.0", override = true }
129
207
 
130
- CetusAggregatorV2ExtendV1 = { git = "https://github.com/CetusProtocol/aggregator.git", subdir = "packages/cetus-aggregator-v2-extend-v1", rev = "mainnet-v1.56.0", override = true }
208
+ CetusAggregatorV2ExtendV1 = { git = "https://github.com/CetusProtocol/aggregator.git", subdir = "packages/cetus-aggregator-v2-extend-v1", rev = "mainnet-v1.63.0", override = true }
131
209
 
132
- CetusAggregatorV2ExtendV2 = { git = "https://github.com/CetusProtocol/aggregator.git", subdir = "packages/cetus-aggregator-v2-extend-v2", rev = "mainnet-v1.56.0", override = true }
210
+ CetusAggregatorV2ExtendV2 = { git = "https://github.com/CetusProtocol/aggregator.git", subdir = "packages/cetus-aggregator-v2-extend-v2", rev = "mainnet-v1.63.0", override = true }
133
211
  ```
134
212
 
135
213
  # Simple Aggregator Contract Interface
@@ -140,12 +218,12 @@ CetusAggregatorV2ExtendV2 = { git = "https://github.com/CetusProtocol/aggregator
140
218
 
141
219
  | Contract | Tag of Repo | Latest published at address |
142
220
  | --------------------- | ----------- | ------------------------------------------------------------------ |
143
- | CetusAggregatorSimple | mainnet | 0x54394cb5bceb8073c60df0964b904a607c9974ef771d9e8b3f9c45b8ce075499 |
221
+ | CetusAggregatorSimple | mainnet | 0xf44ab76524a6b22a175968ae193a13f4905aea9b805afb6db51655b6b59db69e |
144
222
 
145
223
  ## Example
146
224
 
147
225
  ```
148
- CetusAggregatorSimple = { git = "https://github.com/CetusProtocol/aggregator.git", subdir = "packages/cetus-aggregator-v2/simple-mainnet", rev = "mainnet-v1.58.0", override = true }
226
+ CetusAggregatorSimple = { git = "https://github.com/CetusProtocol/aggregator.git", subdir = "packages/cetus-aggregator-v2/simple-mainnet", rev = "mainnet-v1.63.0", override = true }
149
227
  ```
150
228
 
151
229
  ## Usage
package/dist/index.cjs CHANGED
@@ -3416,7 +3416,7 @@ var AGGREGATOR_V3_CONFIG = {
3416
3416
  };
3417
3417
 
3418
3418
  // src/api.ts
3419
- var SDK_VERSION = 1010600;
3419
+ var SDK_VERSION = 1010602;
3420
3420
  function parseRouterResponse(data, byAmountIn) {
3421
3421
  let packages = /* @__PURE__ */ new Map();
3422
3422
  if (data.packages) {
@@ -7822,20 +7822,56 @@ function transferOrDestroyCoin(params, txb) {
7822
7822
  });
7823
7823
  }
7824
7824
  var MAX_ARGUMENT_SIZE = 16 * 1024;
7825
+ var DEFAULT_HERMES_URL = "https://hermes.pyth.network";
7826
+ var DEFAULT_AVAILABILITY_TIMEOUT = 3e3;
7827
+ var DEFAULT_IS_AVAILABLE_STATUS = (status) => status >= 200 && status < 400;
7825
7828
  var PythAdapter = class {
7826
7829
  constructor(client, pythStateId, wormholeStateId, hermesUrls) {
7827
7830
  this.priceFeedObjectIdCache = /* @__PURE__ */ new Map();
7828
7831
  this.client = client;
7829
7832
  this.pythStateId = pythStateId;
7830
7833
  this.wormholeStateId = wormholeStateId;
7831
- const urls = [...hermesUrls];
7832
- if (!urls.includes("https://hermes.pyth.network")) {
7833
- urls.push("https://hermes.pyth.network");
7834
+ const urls = Array.from(
7835
+ new Set(hermesUrls.filter(Boolean).map(normalizeServiceUrl))
7836
+ );
7837
+ if (!urls.includes(DEFAULT_HERMES_URL)) {
7838
+ urls.push(DEFAULT_HERMES_URL);
7834
7839
  }
7840
+ this.hermesUrls = urls;
7835
7841
  this.hermesClients = urls.map(
7836
7842
  (url) => new hermesClient.HermesClient(url, { timeout: 3e3 })
7837
7843
  );
7838
7844
  }
7845
+ getHermesUrls() {
7846
+ return [...this.hermesUrls];
7847
+ }
7848
+ async checkHermesAvailability({
7849
+ timeout = DEFAULT_AVAILABILITY_TIMEOUT,
7850
+ isAvailableStatus = DEFAULT_IS_AVAILABLE_STATUS
7851
+ } = {}) {
7852
+ if (this.hermesUrls.length === 0) {
7853
+ return {
7854
+ checked: false,
7855
+ availableUrls: [],
7856
+ unavailableUrls: []
7857
+ };
7858
+ }
7859
+ const results = await Promise.all(
7860
+ this.hermesUrls.map(async (url) => ({
7861
+ url,
7862
+ available: await checkServiceUrl(url, timeout, isAvailableStatus)
7863
+ }))
7864
+ );
7865
+ return {
7866
+ checked: true,
7867
+ availableUrls: results.filter((result) => result.available).map((result) => result.url),
7868
+ unavailableUrls: results.filter((result) => !result.available).map((result) => result.url)
7869
+ };
7870
+ }
7871
+ async isHermesAvailable(options) {
7872
+ const snapshot = await this.checkHermesAvailability(options);
7873
+ return snapshot.availableUrls.length > 0;
7874
+ }
7839
7875
  async getPriceFeedsUpdateData(priceIDs) {
7840
7876
  let lastError = null;
7841
7877
  for (const hermes of this.hermesClients) {
@@ -8044,6 +8080,25 @@ var PythAdapter = class {
8044
8080
  return priceInfoObjects;
8045
8081
  }
8046
8082
  };
8083
+ async function checkServiceUrl(url, timeout, isAvailableStatus) {
8084
+ const controller = new AbortController();
8085
+ const timer = setTimeout(() => controller.abort(), timeout);
8086
+ try {
8087
+ const response = await fetch(url, {
8088
+ method: "GET",
8089
+ cache: "no-store",
8090
+ signal: controller.signal
8091
+ });
8092
+ return isAvailableStatus(response.status);
8093
+ } catch {
8094
+ return false;
8095
+ } finally {
8096
+ clearTimeout(timer);
8097
+ }
8098
+ }
8099
+ function normalizeServiceUrl(url) {
8100
+ return url.replace(/\/+$/, "");
8101
+ }
8047
8102
 
8048
8103
  // src/utils/uuid.ts
8049
8104
  function generateDowngradeUuid6() {
@@ -8948,6 +9003,12 @@ var ALL_DEXES = [
8948
9003
  MAGMAPROPAMM,
8949
9004
  HAEDALPROPAMM
8950
9005
  ];
9006
+ var PYTH_ORACLE_BASED_DEXES = [
9007
+ HAEDALPMM,
9008
+ METASTABLE,
9009
+ STEAMM_OMM_V2,
9010
+ HAEDALHMMV2
9011
+ ];
8951
9012
  function getAllProviders() {
8952
9013
  return ALL_DEXES;
8953
9014
  }
@@ -9027,6 +9088,9 @@ var _AggregatorClient = class _AggregatorClient {
9027
9088
  this.apiKey = params.apiKey || "";
9028
9089
  this.partner = params.partner;
9029
9090
  this.cetusDlmmPartner = params.cetusDlmmPartner;
9091
+ this.pythAvailabilityCheckInterval = validatePythAvailabilityCheckInterval(
9092
+ params.pythAvailabilityCheckInterval
9093
+ );
9030
9094
  if (params.overlayFeeRate) {
9031
9095
  if (params.overlayFeeRate > 0 && params.overlayFeeRate <= CLIENT_CONFIG.MAX_OVERLAY_FEE_RATE) {
9032
9096
  this.overlayFeeRate = params.overlayFeeRate * AGGREGATOR_V3_CONFIG.FEE_DENOMINATOR;
@@ -9069,23 +9133,74 @@ var _AggregatorClient = class _AggregatorClient {
9069
9133
  }
9070
9134
  }
9071
9135
  async findRouters(params) {
9136
+ const {
9137
+ params: routerParams,
9138
+ providersExhausted
9139
+ } = await this.filterProvidersByPythAvailability(params);
9140
+ if (providersExhausted) {
9141
+ return emptyRouterResult(routerParams.byAmountIn);
9142
+ }
9072
9143
  return getRouterResult(
9073
9144
  this.endpoint,
9074
9145
  this.apiKey,
9075
- params,
9146
+ routerParams,
9076
9147
  this.overlayFeeRate,
9077
9148
  this.overlayFeeReceiver
9078
9149
  );
9079
9150
  }
9080
9151
  async findMergeSwapRouters(params) {
9152
+ const {
9153
+ params: routerParams,
9154
+ providersExhausted
9155
+ } = await this.filterProvidersByPythAvailability(params);
9156
+ if (providersExhausted) {
9157
+ return emptyMergeSwapResult();
9158
+ }
9081
9159
  return getMergeSwapResult(
9082
9160
  this.endpoint,
9083
9161
  this.apiKey,
9084
- params,
9162
+ routerParams,
9085
9163
  this.overlayFeeRate,
9086
9164
  this.overlayFeeReceiver
9087
9165
  );
9088
9166
  }
9167
+ async filterProvidersByPythAvailability(params) {
9168
+ const interval = validatePythAvailabilityCheckInterval(
9169
+ params.pythAvailabilityCheckInterval ?? this.pythAvailabilityCheckInterval
9170
+ );
9171
+ if (interval === void 0) {
9172
+ return { params, providersExhausted: false };
9173
+ }
9174
+ const pythAvailable = await this.getPythAvailability(interval);
9175
+ if (pythAvailable) {
9176
+ return { params, providersExhausted: false };
9177
+ }
9178
+ const sourceProviders = params.providers && params.providers.length > 0 ? params.providers : ALL_DEXES;
9179
+ const providers = sourceProviders.filter(
9180
+ (provider) => !PYTH_ORACLE_BASED_DEXES.includes(provider)
9181
+ );
9182
+ return {
9183
+ params: { ...params, providers },
9184
+ providersExhausted: providers.length === 0
9185
+ };
9186
+ }
9187
+ async getPythAvailability(interval) {
9188
+ const now = Date.now();
9189
+ if (this.pythAvailable !== void 0 && this.pythAvailabilityCheckedAt !== void 0 && now - this.pythAvailabilityCheckedAt < interval) {
9190
+ return this.pythAvailable;
9191
+ }
9192
+ if (this.pythAvailabilityPromise) {
9193
+ return this.pythAvailabilityPromise;
9194
+ }
9195
+ this.pythAvailabilityPromise = this.pythAdapter.isHermesAvailable().catch(() => false).then((available) => {
9196
+ this.pythAvailable = available;
9197
+ this.pythAvailabilityCheckedAt = Date.now();
9198
+ return available;
9199
+ }).finally(() => {
9200
+ this.pythAvailabilityPromise = void 0;
9201
+ });
9202
+ return this.pythAvailabilityPromise;
9203
+ }
9089
9204
  async executeFlexibleInputSwap(txb, inputCoin, routerData, expectedAmountOut, amountLimit, pythPriceIDs, partner, deepbookv3DeepFee, packages) {
9090
9205
  }
9091
9206
  newDexRouterV3(provider, pythPriceIDs, partner, cetusDlmmPartner) {
@@ -9589,7 +9704,15 @@ var _AggregatorClient = class _AggregatorClient {
9589
9704
  // auto build input coin
9590
9705
  // auto merge, transfer or destory target coin.
9591
9706
  async fastRouterSwap(params) {
9592
- const { router, slippage, txb, partner, cetusDlmmPartner, payDeepFeeAmount } = params;
9707
+ const {
9708
+ router,
9709
+ slippage,
9710
+ txb,
9711
+ partner,
9712
+ cetusDlmmPartner,
9713
+ payDeepFeeAmount,
9714
+ sponsored = false
9715
+ } = params;
9593
9716
  const fromCoinType = router.paths[0].from;
9594
9717
  const targetCoinType = router.paths[router.paths.length - 1].target;
9595
9718
  const byAmountIn = router.byAmountIn;
@@ -9614,7 +9737,7 @@ var _AggregatorClient = class _AggregatorClient {
9614
9737
  const amount = byAmountIn ? expectedAmountIn : amountLimit;
9615
9738
  let inputCoin = transactions.coinWithBalance({
9616
9739
  balance: BigInt(amount.toString()),
9617
- useGasCoin: true,
9740
+ useGasCoin: !sponsored,
9618
9741
  type: fromCoinType
9619
9742
  });
9620
9743
  let deepCoin;
@@ -9634,22 +9757,46 @@ var _AggregatorClient = class _AggregatorClient {
9634
9757
  deepbookv3DeepFee: deepCoin
9635
9758
  };
9636
9759
  const targetCoin = await this.routerSwap(routerSwapParams);
9760
+ await this.deliverTargetCoin(
9761
+ txb,
9762
+ targetCoin,
9763
+ targetCoinType,
9764
+ router.packages,
9765
+ sponsored
9766
+ );
9767
+ }
9768
+ // Delivers a swap's output coin. In sponsored mode the SDK must not query or touch
9769
+ // coins through `this.signer`, because the transaction sender may be different from
9770
+ // the SDK instance signer. Let the router transfer helper deliver the output to the
9771
+ // transaction sender instead.
9772
+ async deliverTargetCoin(txb, targetCoin, targetCoinType, packages, sponsored) {
9773
+ if (sponsored) {
9774
+ transferOrDestroyCoin(
9775
+ {
9776
+ coin: targetCoin,
9777
+ coinType: targetCoinType,
9778
+ packages
9779
+ },
9780
+ txb
9781
+ );
9782
+ return;
9783
+ }
9637
9784
  if (CoinUtils.isSuiCoin(targetCoinType)) {
9638
9785
  txb.mergeCoins(txb.gas, [targetCoin]);
9786
+ return;
9787
+ }
9788
+ const targetCoinObjID = await this.getOneCoinUsedToMerge(targetCoinType);
9789
+ if (targetCoinObjID != null) {
9790
+ txb.mergeCoins(txb.object(targetCoinObjID), [targetCoin]);
9639
9791
  } else {
9640
- const targetCoinObjID = await this.getOneCoinUsedToMerge(targetCoinType);
9641
- if (targetCoinObjID != null) {
9642
- txb.mergeCoins(txb.object(targetCoinObjID), [targetCoin]);
9643
- } else {
9644
- transferOrDestroyCoin(
9645
- {
9646
- coin: targetCoin,
9647
- coinType: targetCoinType,
9648
- packages: router.packages
9649
- },
9650
- txb
9651
- );
9652
- }
9792
+ transferOrDestroyCoin(
9793
+ {
9794
+ coin: targetCoin,
9795
+ coinType: targetCoinType,
9796
+ packages
9797
+ },
9798
+ txb
9799
+ );
9653
9800
  }
9654
9801
  }
9655
9802
  async mergeSwap(params) {
@@ -9698,7 +9845,7 @@ var _AggregatorClient = class _AggregatorClient {
9698
9845
  return finalOutputCoin;
9699
9846
  }
9700
9847
  async fastMergeSwap(params) {
9701
- const { router, slippage, txb, partner } = params;
9848
+ const { router, slippage, txb, partner, sponsored = false } = params;
9702
9849
  if (!router || !router.allRoutes || router.allRoutes.length === 0) {
9703
9850
  throw new Error("Invalid router: no routes found");
9704
9851
  }
@@ -9714,7 +9861,7 @@ var _AggregatorClient = class _AggregatorClient {
9714
9861
  coinTypeSet.add(firstCoinType);
9715
9862
  const coin = transactions.coinWithBalance({
9716
9863
  balance: BigInt(route.amountIn.toString()),
9717
- useGasCoin: CoinUtils.isSuiCoin(firstCoinType),
9864
+ useGasCoin: !sponsored && CoinUtils.isSuiCoin(firstCoinType),
9718
9865
  type: firstCoinType
9719
9866
  });
9720
9867
  inputCoins.push({ coinType: firstCoinType, coin });
@@ -9727,23 +9874,13 @@ var _AggregatorClient = class _AggregatorClient {
9727
9874
  partner: partner ?? this.partner
9728
9875
  };
9729
9876
  const targetCoin = await this.mergeSwap(mergeSwapParams);
9730
- if (CoinUtils.isSuiCoin(targetCoinType)) {
9731
- txb.mergeCoins(txb.gas, [targetCoin]);
9732
- } else {
9733
- const targetCoinObjID = await this.getOneCoinUsedToMerge(targetCoinType);
9734
- if (targetCoinObjID != null) {
9735
- txb.mergeCoins(txb.object(targetCoinObjID), [targetCoin]);
9736
- } else {
9737
- transferOrDestroyCoin(
9738
- {
9739
- coin: targetCoin,
9740
- coinType: targetCoinType,
9741
- packages: router.packages
9742
- },
9743
- txb
9744
- );
9745
- }
9746
- }
9877
+ await this.deliverTargetCoin(
9878
+ txb,
9879
+ targetCoin,
9880
+ targetCoinType,
9881
+ router.packages,
9882
+ sponsored
9883
+ );
9747
9884
  }
9748
9885
  async fixableRouterSwapV3(params) {
9749
9886
  const { router, inputCoin, slippage, txb, partner } = params;
@@ -9942,6 +10079,70 @@ var _AggregatorClient = class _AggregatorClient {
9942
10079
  });
9943
10080
  return res;
9944
10081
  }
10082
+ // ---------------------------------------------------------------------------
10083
+ // Sponsored transactions
10084
+ //
10085
+ // A sponsored transaction lets a sponsor (gas station) pay gas for a transaction
10086
+ // whose commands are authored by a different sender. The sender and the sponsor both
10087
+ // sign the same transaction bytes, and the transaction is submitted with both
10088
+ // signatures. See: https://docs.sui.io/concepts/transactions/sponsored-transactions
10089
+ //
10090
+ // Build the swap with `sponsored: true` (e.g. `fastRouterSwap`/`fastMergeSwap`) so the
10091
+ // gas coin is never used, then:
10092
+ // - Remote gas station: `buildTransactionKind(txb, sender)` -> station assembles gas
10093
+ // data with `buildSponsoredTransaction` -> station builds/signs full bytes -> user
10094
+ // signs the same bytes -> `executeWithSignatures`.
10095
+ // - Local sponsor (both keypairs available): `signAndExecuteSponsoredTransaction`.
10096
+ // ---------------------------------------------------------------------------
10097
+ // Builds the TransactionKind bytes (commands only, no gas data) to hand to a sponsor /
10098
+ // gas station. Pass `sender` when the transaction contains CoinWithBalance intents,
10099
+ // because those are resolved while building the kind.
10100
+ async buildTransactionKind(txb, sender) {
10101
+ if (sender) {
10102
+ txb.setSender(sender);
10103
+ }
10104
+ return txb.build({ client: this.client, onlyTransactionKind: true });
10105
+ }
10106
+ // Assembles a sponsored transaction from TransactionKind bytes plus the gas data
10107
+ // provided by the sponsor. The returned transaction is ready to be built and signed by
10108
+ // both the sender and the sponsor.
10109
+ buildSponsoredTransaction(params) {
10110
+ const { txKindBytes, sender, sponsor, sponsorCoins, gasBudget, gasPrice } = params;
10111
+ const tx = transactions.Transaction.fromKind(txKindBytes);
10112
+ tx.setSender(sender);
10113
+ tx.setGasOwner(sponsor);
10114
+ tx.setGasPayment(sponsorCoins);
10115
+ if (gasBudget != null) {
10116
+ tx.setGasBudget(gasBudget);
10117
+ }
10118
+ if (gasPrice != null) {
10119
+ tx.setGasPrice(gasPrice);
10120
+ }
10121
+ return tx;
10122
+ }
10123
+ // Builds the full transaction bytes (including gas data) that both parties must sign.
10124
+ // Returns the exact bytes the signatures must be produced over.
10125
+ async buildTransactionBytes(txb) {
10126
+ return txb.build({ client: this.client });
10127
+ }
10128
+ // Executes a transaction from pre-built bytes plus the collected signatures. The
10129
+ // signatures MUST be produced over exactly these `bytes` (see `buildTransactionBytes`).
10130
+ async executeWithSignatures(bytes, signatures) {
10131
+ return this.client.executeTransaction({
10132
+ transaction: bytes,
10133
+ signatures,
10134
+ include: { effects: true, events: true, balanceChanges: true }
10135
+ });
10136
+ }
10137
+ // Convenience for the local dual-key case: signs the transaction with both the user's
10138
+ // and the sponsor's signer and executes it. `txb` must already have its sender, gas
10139
+ // owner and gas payment set (e.g. via `buildSponsoredTransaction`).
10140
+ async signAndExecuteSponsoredTransaction(txb, userSigner, sponsorSigner) {
10141
+ const bytes = await txb.build({ client: this.client });
10142
+ const { signature: sponsorSig } = await sponsorSigner.signTransaction(bytes);
10143
+ const { signature: userSig } = await userSigner.signTransaction(bytes);
10144
+ return this.executeWithSignatures(bytes, [userSig, sponsorSig]);
10145
+ }
9945
10146
  };
9946
10147
  _AggregatorClient.CONFIG = {
9947
10148
  [1 /* Testnet */]: {
@@ -9961,6 +10162,35 @@ function generateUUID() {
9961
10162
  return v.toString(16);
9962
10163
  });
9963
10164
  }
10165
+ function validatePythAvailabilityCheckInterval(interval) {
10166
+ if (interval === void 0) {
10167
+ return void 0;
10168
+ }
10169
+ if (!Number.isFinite(interval) || interval < 0) {
10170
+ throw new Error(
10171
+ "pythAvailabilityCheckInterval must be a non-negative number"
10172
+ );
10173
+ }
10174
+ return interval;
10175
+ }
10176
+ function emptyRouterResult(byAmountIn) {
10177
+ return {
10178
+ quoteID: "",
10179
+ amountIn: ZERO,
10180
+ amountOut: ZERO,
10181
+ paths: [],
10182
+ byAmountIn,
10183
+ insufficientLiquidity: true,
10184
+ deviationRatio: 0
10185
+ };
10186
+ }
10187
+ function emptyMergeSwapResult() {
10188
+ return {
10189
+ quoteID: "",
10190
+ totalAmountOut: ZERO,
10191
+ allRoutes: []
10192
+ };
10193
+ }
9964
10194
  function recordFirstCoinIndex(paths) {
9965
10195
  let newCoinRecord = /* @__PURE__ */ new Map();
9966
10196
  for (let i = 0; i < paths.length; i++) {
@@ -10100,6 +10330,7 @@ exports.PAY_MODULE = PAY_MODULE;
10100
10330
  exports.POOL_MODULT = POOL_MODULT;
10101
10331
  exports.PUBLISHED_ADDRESSES = PUBLISHED_ADDRESSES;
10102
10332
  exports.PYTH_CONFIG = PYTH_CONFIG;
10333
+ exports.PYTH_ORACLE_BASED_DEXES = PYTH_ORACLE_BASED_DEXES;
10103
10334
  exports.PYTH_PRELUDE_CALLS = PYTH_PRELUDE_CALLS;
10104
10335
  exports.REPAY_FLASH_SWAP_A2B_FUNC = REPAY_FLASH_SWAP_A2B_FUNC;
10105
10336
  exports.REPAY_FLASH_SWAP_B2A_FUNC = REPAY_FLASH_SWAP_B2A_FUNC;