@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 +88 -10
- package/dist/index.cjs +271 -40
- package/dist/index.d.cts +52 -30
- package/dist/index.d.ts +52 -30
- package/dist/index.js +271 -41
- package/package.json +1 -1
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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 |
|
|
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.
|
|
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.
|
|
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.
|
|
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 |
|
|
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.
|
|
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 =
|
|
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 =
|
|
7832
|
-
|
|
7833
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
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
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
|
|
9644
|
-
|
|
9645
|
-
|
|
9646
|
-
|
|
9647
|
-
|
|
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
|
-
|
|
9731
|
-
txb
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
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;
|