@cetusprotocol/aggregator-sdk 1.4.0 → 1.4.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 CHANGED
@@ -79,11 +79,11 @@ The new Aggregator Client V3 offers significant improvements over the previous v
79
79
 
80
80
  - **Increased Reliability**: More robust transaction execution with better error handling
81
81
 
82
- ## Migration from Aggregator Client to V3
82
+ ## Migration from V2 to V3 API
83
83
 
84
84
  ### Installation
85
85
 
86
- Both versions use the same package:
86
+ Both V2 and V3 APIs use the same package:
87
87
 
88
88
  ```bash
89
89
  npm install @cetusprotocol/aggregator-sdk
@@ -91,61 +91,67 @@ npm install @cetusprotocol/aggregator-sdk
91
91
 
92
92
  ### Key Changes
93
93
 
94
- 1. **Client Initialization**
94
+ **Important:** Both V2 and V3 use the same `AggregatorClient` class. The main differences are:
95
+ - **Return types**: V3 uses `RouterDataV3` (with flattened `paths`) instead of V2's `RouterData` (with nested `routes`)
96
+ - **Swap methods**: V3 introduces `fastRouterSwap()` for automatic coin handling
97
+ - **Transaction structure**: V3 optimizes gas through transaction merging
98
+
99
+ 1. **Client Initialization (Same for both)**
95
100
 
96
101
  ```typescript
97
- // V2 (Legacy)
98
- const client = new AggregatorClient({})
102
+ import { AggregatorClient } from "@cetusprotocol/aggregator-sdk"
99
103
 
100
- // V3 (New)
101
- const client = new AggregatorClient({})
104
+ const client = new AggregatorClient({
105
+ // Optional: Add custom configuration
106
+ })
102
107
  ```
103
108
 
104
- 2. **Router Finding**
109
+ 2. **Router Finding (API unchanged, but return type differs)**
105
110
 
106
111
  ```typescript
107
- // Both versions use the same API
112
+ import BN from "bn.js"
113
+
114
+ // Works with both V2 and V3
108
115
  const routers = await client.findRouters({
109
116
  from: "0x2::sui::SUI",
110
- target:
111
- "0x06864a6f921804860930db6ddbe2e16acdf8504495ea7481637a1c8b9a8fe54b::cetus::CETUS",
117
+ target: "0x06864a6f921804860930db6ddbe2e16acdf8504495ea7481637a1c8b9a8fe54b::cetus::CETUS",
112
118
  amount: new BN(1000000),
113
119
  byAmountIn: true,
114
120
  })
121
+
122
+ // V2 returns: RouterData with routes[]
123
+ // V3 returns: RouterDataV3 with paths[]
115
124
  ```
116
125
 
117
126
  3. **Transaction Building**
118
127
 
119
128
  ```typescript
120
- // V2 Method
121
- await client.buildRouterSwap({
122
- routers,
123
- txb,
124
- inputCoin,
125
- slippage: 0.01,
126
- })
129
+ import { Transaction } from "@mysten/sui/transactions"
130
+
131
+ const txb = new Transaction()
127
132
 
128
- // V3 Method (Recommended)
133
+ // V3 Method (Recommended) - Automatic coin handling
129
134
  await client.fastRouterSwap({
130
- routers,
135
+ router: routers,
131
136
  txb,
132
137
  slippage: 0.01,
133
138
  })
134
139
 
135
- // V3 Alternative (For PTB building)
140
+ // V3 Alternative - Manual coin handling for PTB building
136
141
  const targetCoin = await client.routerSwap({
137
- routers,
142
+ router: routers,
138
143
  txb,
139
- inputCoin,
144
+ inputCoin, // TransactionObjectArgument
140
145
  slippage: 0.01,
141
146
  })
142
147
  ```
143
148
 
144
- 4. **Benefits of Migration**
149
+ 4. **Benefits of V3 Migration**
145
150
  - Reduced gas costs through transaction merging
146
- - Better transaction traceability
151
+ - Better transaction traceability on-chain
147
152
  - Improved error handling and reliability
148
153
  - Access to latest DEX integrations
154
+ - Automatic coin management with `fastRouterSwap()`
149
155
 
150
156
  ## Install
151
157
 
@@ -157,74 +163,208 @@ npm install @cetusprotocol/aggregator-sdk
157
163
 
158
164
  ## Usage
159
165
 
160
- ### 1. Init client with rpc and package config
166
+ ### Complete Example with All Imports
161
167
 
162
168
  ```typescript
163
- const client = new AggregatorClient({})
169
+ import { AggregatorClient } from "@cetusprotocol/aggregator-sdk"
170
+ import { Transaction } from "@mysten/sui/transactions"
171
+ import BN from "bn.js"
172
+ import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"
173
+
174
+ // Initialize your keypair (example using Ed25519)
175
+ const keypair = Ed25519Keypair.deriveKeypair("your-mnemonic-phrase")
164
176
  ```
165
177
 
166
- ### 2. Get best router swap result from aggregator service
178
+ ### 1. Initialize Client
167
179
 
168
180
  ```typescript
169
- const amount = new BN(1000000)
181
+ import { Env } from "@cetusprotocol/aggregator-sdk"
182
+ import { getFullnodeUrl, SuiClient } from "@mysten/sui/client"
183
+
184
+ const client = new AggregatorClient({
185
+ // Optional configuration parameters:
186
+
187
+ // Network environment (default: Mainnet)
188
+ env: Env.Mainnet, // or Env.Testnet
189
+
190
+ // Custom API endpoint for aggregator service
191
+ // endpoint: "https://api-sui.cetus.zone",
192
+
193
+ // Custom Sui client (default: mainnet RPC)
194
+ // client: new SuiClient({ url: getFullnodeUrl("mainnet") }),
195
+
196
+ // Partner ID for revenue sharing
197
+ // partner: "your-partner-id",
198
+
199
+ // CETUS DLMM specific partner ID
200
+ // cetusDlmmPartner: "your-dlmm-partner-id",
201
+
202
+ // Overlay fee rate (0 to 0.01 for 0-1%)
203
+ // overlayFeeRate: 0.001, // 0.1%
204
+
205
+ // Overlay fee receiver address
206
+ // overlayFeeReceiver: "0x...",
207
+
208
+ // Custom Pyth oracle URLs
209
+ // pythUrls: ["https://hermes.pyth.network"],
210
+
211
+ // API key for rate limiting
212
+ // apiKey: "your-api-key",
213
+ })
214
+ ```
215
+
216
+ ### 2. Find Best Router
217
+
218
+ Get the optimal swap route from the aggregator service:
219
+
220
+ ```typescript
221
+ const amount = new BN(1000000) // 1 SUI (with 6 decimals)
170
222
  const from = "0x2::sui::SUI"
171
- const target =
172
- "0x06864a6f921804860930db6ddbe2e16acdf8504495ea7481637a1c8b9a8fe54b::cetus::CETUS"
223
+ const target = "0x06864a6f921804860930db6ddbe2e16acdf8504495ea7481637a1c8b9a8fe54b::cetus::CETUS"
173
224
 
174
225
  const routers = await client.findRouters({
175
226
  from,
176
227
  target,
177
228
  amount,
178
- byAmountIn: true, // true means fix input amount, false means fix output amount
229
+ byAmountIn: true, // true = fixed input amount, false = fixed output amount
230
+ // Optional parameters:
231
+ // depth: 3, // Maximum number of hops
232
+ // providers: ["CETUS", "TURBOS"], // Limit to specific DEXs
179
233
  })
234
+
235
+ // Check if a valid route was found
236
+ if (!routers || routers.insufficientLiquidity) {
237
+ console.error("No valid route found or insufficient liquidity")
238
+ process.exit(1)
239
+ }
240
+
241
+ console.log(`Expected output: ${routers.amountOut.toString()}`)
180
242
  ```
181
243
 
182
- ### 3. Confirm and do fast swap
244
+ ### 3. Fast Swap (Recommended - Automatic Coin Handling)
245
+
246
+ The easiest way to execute a swap with automatic coin management:
183
247
 
184
248
  ```typescript
185
249
  const txb = new Transaction()
186
250
 
187
- if (routerRes != null) {
188
- await client.fastRouterSwap({
189
- routers,
190
- txb,
191
- slippage: 0.01,
192
- })
251
+ await client.fastRouterSwap({
252
+ router: routers,
253
+ txb,
254
+ slippage: 0.01, // 1% slippage tolerance
255
+ // Optional:
256
+ // partner: "your-partner-id",
257
+ // payDeepFeeAmount: 1000000, // For DeepBook V3 pools
258
+ })
193
259
 
194
- const result = await client.devInspectTransactionBlock(txb, keypair)
260
+ // Simulate the transaction first
261
+ const dryRunResult = await client.devInspectTransactionBlock(txb, keypair)
195
262
 
196
- if (result.effects.status.status === "success") {
197
- console.log("Sim exec transaction success")
198
- const result = await client.signAndExecuteTransaction(txb, keypair)
199
- }
200
- console.log("result", result)
263
+ if (dryRunResult.effects.status.status === "success") {
264
+ console.log("Simulation successful, executing transaction...")
265
+
266
+ // Execute the actual transaction
267
+ const result = await client.signAndExecuteTransaction(txb, keypair)
268
+ console.log("Transaction result:", result)
269
+ } else {
270
+ console.error("Simulation failed:", dryRunResult.effects.status)
271
+ }
272
+ ```
273
+
274
+ ### 4. Manual Swap (Advanced - For Custom PTB Building)
275
+
276
+ Use this when you need to build complex programmable transaction blocks:
277
+
278
+ ```typescript
279
+ import { coinWithBalance } from "@mysten/sui/transactions"
280
+
281
+ const txb = new Transaction()
282
+
283
+ // Create input coin object
284
+ // Option 1: From existing coin object
285
+ const inputCoin = txb.object("0x...") // Your coin object ID
286
+
287
+ // Option 2: Create coin with specific balance
288
+ // const inputCoin = coinWithBalance({
289
+ // type: from,
290
+ // balance: amount.toString()
291
+ // })
292
+
293
+ // Execute swap and get output coin
294
+ const targetCoin = await client.routerSwap({
295
+ router: routers,
296
+ txb,
297
+ inputCoin,
298
+ slippage: 0.01, // 1% slippage tolerance
299
+ // Optional:
300
+ // partner: "your-partner-id",
301
+ // deepbookv3DeepFee: deepCoinObject, // For DeepBook V3
302
+ })
303
+
304
+ // Use the target coin in your custom PTB logic
305
+ // For example, transfer to recipient or use in another DeFi protocol
306
+ txb.transferObjects([targetCoin], keypair.toSuiAddress())
307
+
308
+ // Or destroy if sending to current address
309
+ // client.transferOrDestroyCoin(txb, targetCoin, target)
310
+
311
+ // Simulate and execute
312
+ const dryRunResult = await client.devInspectTransactionBlock(txb, keypair)
313
+
314
+ if (dryRunResult.effects.status.status === "success") {
315
+ console.log("Simulation successful, executing transaction...")
316
+ const result = await client.signAndExecuteTransaction(txb, keypair)
317
+ console.log("Transaction result:", result)
318
+ } else {
319
+ console.error("Simulation failed:", dryRunResult.effects.status)
201
320
  }
202
321
  ```
203
322
 
204
- ### 4. Build PTB and return target coin
323
+ ### 5. Advanced: Swap with Maximum Input Amount
324
+
325
+ Protect against excessive input amounts:
205
326
 
206
327
  ```typescript
207
328
  const txb = new Transaction()
208
- const byAmountIn = true
209
-
210
- if (routerRes != null) {
211
- const targetCoin = await client.routerSwap({
212
- routers,
213
- txb,
214
- inputCoin,
215
- slippage: 0.01,
329
+ const inputCoin = txb.object("0x...") // Your coin object
330
+
331
+ const targetCoin = await client.routerSwapWithMaxAmountIn({
332
+ router: routers,
333
+ txb,
334
+ inputCoin,
335
+ slippage: 0.01,
336
+ maxAmountIn: new BN(2000000), // Maximum 2 SUI allowed
337
+ })
338
+
339
+ // Transaction will abort if input amount exceeds maxAmountIn
340
+ ```
341
+
342
+ ### Error Handling
343
+
344
+ ```typescript
345
+ try {
346
+ const routers = await client.findRouters({
347
+ from,
348
+ target,
349
+ amount,
350
+ byAmountIn: true,
216
351
  })
217
352
 
218
- // you can use this target coin object argument to build your ptb.
219
- client.transferOrDestoryCoin(txb, targetCoin, targetCoinType)
353
+ if (!routers) {
354
+ throw new Error("No route found")
355
+ }
220
356
 
221
- const result = await client.devInspectTransactionBlock(txb, keypair)
357
+ if (routers.insufficientLiquidity) {
358
+ throw new Error("Insufficient liquidity for this swap")
359
+ }
222
360
 
223
- if (result.effects.status.status === "success") {
224
- console.log("Sim exec transaction success")
225
- const result = await client.signAndExecuteTransaction(txb, keypair)
361
+ if (routers.error) {
362
+ throw new Error(`Router error: ${routers.error.msg}`)
226
363
  }
227
- console.log("result", result)
364
+
365
+ // Proceed with swap...
366
+ } catch (error) {
367
+ console.error("Swap failed:", error)
228
368
  }
229
369
  ```
230
370
 
package/dist/index.d.mts CHANGED
@@ -9,7 +9,7 @@ import { Signer } from '@mysten/sui/cryptography';
9
9
  interface FindRouterParams {
10
10
  from: string;
11
11
  target: string;
12
- amount: BN;
12
+ amount: BN | string | number | bigint;
13
13
  byAmountIn: boolean;
14
14
  depth?: number;
15
15
  splitAlgorithm?: string;
@@ -20,11 +20,11 @@ interface FindRouterParams {
20
20
  }
21
21
  interface MergeSwapFromCoin {
22
22
  coinType: string;
23
- amount: BN | string | number;
23
+ amount: BN | string | number | bigint;
24
24
  }
25
25
  interface MergeSwapFromCoin {
26
26
  coinType: string;
27
- amount: BN | string | number;
27
+ amount: BN | string | number | bigint;
28
28
  }
29
29
  interface MergeSwapParams {
30
30
  target: string;
@@ -394,9 +394,24 @@ declare class AggregatorClient {
394
394
  executeFlexibleInputSwap(txb: Transaction, inputCoin: TransactionObjectArgument, routerData: RouterDataV3, expectedAmountOut: string, amountLimit: string, pythPriceIDs: Map<string, string>, partner?: string, deepbookv3DeepFee?: TransactionObjectArgument, packages?: Map<string, string>): Promise<void>;
395
395
  newDexRouterV3(provider: string, pythPriceIDs: Map<string, string>, partner?: string, cetusDlmmPartner?: string): DexRouter;
396
396
  expectInputSwapV3(txb: Transaction, inputCoin: TransactionObjectArgument, routerData: RouterDataV3, expectAmountOut: string, amountOutLimit: string, pythPriceIDs: Map<string, string>, partner?: string, cetusDlmmPartner?: string): TransactionObjectArgument;
397
+ expectInputSwapV3WithMaxAmountIn(txb: Transaction, inputCoin: TransactionObjectArgument, routerData: RouterDataV3, maxAmountIn: BN, expectAmountOut: string, amountOutLimit: string, pythPriceIDs: Map<string, string>, partner?: string, cetusDlmmPartner?: string): TransactionObjectArgument;
397
398
  expectOutputSwapV3(txb: Transaction, inputCoin: TransactionObjectArgument, routerData: RouterDataV3, amountOut: string, _amountLimit: string, // it will set when build inputcoin
398
399
  partner?: string): TransactionObjectArgument;
400
+ expectOutputSwapV3WithMaxAmountIn(txb: Transaction, inputCoin: TransactionObjectArgument, routerData: RouterDataV3, maxAmountIn: BN, amountOut: string, _amountLimit: string, // it will set when build inputcoin
401
+ partner?: string): TransactionObjectArgument;
399
402
  routerSwap(params: BuildRouterSwapParamsV3): Promise<TransactionObjectArgument>;
403
+ /**
404
+ * Router swap with max amount in validation.
405
+ * This method validates that the input coin amount does not exceed maxAmountIn.
406
+ * If the validation fails, the transaction will abort with an error.
407
+ *
408
+ * @param params - Router swap parameters with maxAmountIn
409
+ * @returns TransactionObjectArgument - The output coin from the swap
410
+ * @throws Error if input coin amount exceeds maxAmountIn
411
+ */
412
+ routerSwapWithMaxAmountIn(params: BuildRouterSwapParamsV3 & {
413
+ maxAmountIn: BN;
414
+ }): Promise<TransactionObjectArgument>;
400
415
  fastRouterSwap(params: BuildFastRouterSwapParamsV3): Promise<void>;
401
416
  mergeSwap(params: BuildMergeSwapParams): Promise<TransactionObjectArgument>;
402
417
  fastMergeSwap(params: BuildFastMergeSwapParams): Promise<void>;
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ import { Signer } from '@mysten/sui/cryptography';
9
9
  interface FindRouterParams {
10
10
  from: string;
11
11
  target: string;
12
- amount: BN;
12
+ amount: BN | string | number | bigint;
13
13
  byAmountIn: boolean;
14
14
  depth?: number;
15
15
  splitAlgorithm?: string;
@@ -20,11 +20,11 @@ interface FindRouterParams {
20
20
  }
21
21
  interface MergeSwapFromCoin {
22
22
  coinType: string;
23
- amount: BN | string | number;
23
+ amount: BN | string | number | bigint;
24
24
  }
25
25
  interface MergeSwapFromCoin {
26
26
  coinType: string;
27
- amount: BN | string | number;
27
+ amount: BN | string | number | bigint;
28
28
  }
29
29
  interface MergeSwapParams {
30
30
  target: string;
@@ -394,9 +394,24 @@ declare class AggregatorClient {
394
394
  executeFlexibleInputSwap(txb: Transaction, inputCoin: TransactionObjectArgument, routerData: RouterDataV3, expectedAmountOut: string, amountLimit: string, pythPriceIDs: Map<string, string>, partner?: string, deepbookv3DeepFee?: TransactionObjectArgument, packages?: Map<string, string>): Promise<void>;
395
395
  newDexRouterV3(provider: string, pythPriceIDs: Map<string, string>, partner?: string, cetusDlmmPartner?: string): DexRouter;
396
396
  expectInputSwapV3(txb: Transaction, inputCoin: TransactionObjectArgument, routerData: RouterDataV3, expectAmountOut: string, amountOutLimit: string, pythPriceIDs: Map<string, string>, partner?: string, cetusDlmmPartner?: string): TransactionObjectArgument;
397
+ expectInputSwapV3WithMaxAmountIn(txb: Transaction, inputCoin: TransactionObjectArgument, routerData: RouterDataV3, maxAmountIn: BN, expectAmountOut: string, amountOutLimit: string, pythPriceIDs: Map<string, string>, partner?: string, cetusDlmmPartner?: string): TransactionObjectArgument;
397
398
  expectOutputSwapV3(txb: Transaction, inputCoin: TransactionObjectArgument, routerData: RouterDataV3, amountOut: string, _amountLimit: string, // it will set when build inputcoin
398
399
  partner?: string): TransactionObjectArgument;
400
+ expectOutputSwapV3WithMaxAmountIn(txb: Transaction, inputCoin: TransactionObjectArgument, routerData: RouterDataV3, maxAmountIn: BN, amountOut: string, _amountLimit: string, // it will set when build inputcoin
401
+ partner?: string): TransactionObjectArgument;
399
402
  routerSwap(params: BuildRouterSwapParamsV3): Promise<TransactionObjectArgument>;
403
+ /**
404
+ * Router swap with max amount in validation.
405
+ * This method validates that the input coin amount does not exceed maxAmountIn.
406
+ * If the validation fails, the transaction will abort with an error.
407
+ *
408
+ * @param params - Router swap parameters with maxAmountIn
409
+ * @returns TransactionObjectArgument - The output coin from the swap
410
+ * @throws Error if input coin amount exceeds maxAmountIn
411
+ */
412
+ routerSwapWithMaxAmountIn(params: BuildRouterSwapParamsV3 & {
413
+ maxAmountIn: BN;
414
+ }): Promise<TransactionObjectArgument>;
400
415
  fastRouterSwap(params: BuildFastRouterSwapParamsV3): Promise<void>;
401
416
  mergeSwap(params: BuildMergeSwapParams): Promise<TransactionObjectArgument>;
402
417
  fastMergeSwap(params: BuildFastMergeSwapParams): Promise<void>;
package/dist/index.js CHANGED
@@ -3453,7 +3453,7 @@ var AGGREGATOR_V3_CONFIG = {
3453
3453
  };
3454
3454
 
3455
3455
  // src/api.ts
3456
- var SDK_VERSION = 1010400;
3456
+ var SDK_VERSION = 1010401;
3457
3457
  function parseRouterResponse(data, byAmountIn) {
3458
3458
  let packages = /* @__PURE__ */ new Map();
3459
3459
  if (data.packages) {
@@ -7759,6 +7759,37 @@ function newSwapContext(params, txb) {
7759
7759
  });
7760
7760
  return swap_context;
7761
7761
  }
7762
+ function newSwapContextV2(params, txb) {
7763
+ const {
7764
+ quoteID,
7765
+ fromCoinType,
7766
+ targetCoinType,
7767
+ maxAmountIn,
7768
+ expectAmountOut,
7769
+ amountOutLimit,
7770
+ inputCoin,
7771
+ feeRate,
7772
+ feeRecipient,
7773
+ aggregatorPublishedAt,
7774
+ packages
7775
+ } = params;
7776
+ const publishedAt = getAggregatorPublishedAt(packages, aggregatorPublishedAt);
7777
+ const args = [
7778
+ txb.pure.string(quoteID),
7779
+ txb.pure.u64(maxAmountIn.toString()),
7780
+ txb.pure.u64(expectAmountOut.toString()),
7781
+ txb.pure.u64(amountOutLimit.toString()),
7782
+ inputCoin,
7783
+ txb.pure.u32(Number(feeRate.toString())),
7784
+ txb.pure.address(feeRecipient)
7785
+ ];
7786
+ const swap_context = txb.moveCall({
7787
+ target: `${publishedAt}::router::new_swap_context_v2`,
7788
+ typeArguments: [fromCoinType, targetCoinType],
7789
+ arguments: args
7790
+ });
7791
+ return swap_context;
7792
+ }
7762
7793
  function confirmSwap(params, txb) {
7763
7794
  const { swapContext, targetCoinType, aggregatorPublishedAt, packages } = params;
7764
7795
  const publishedAt = getAggregatorPublishedAt(packages, aggregatorPublishedAt);
@@ -8760,6 +8791,53 @@ var _AggregatorClient = class _AggregatorClient {
8760
8791
  );
8761
8792
  return outputCoin;
8762
8793
  }
8794
+ expectInputSwapV3WithMaxAmountIn(txb, inputCoin, routerData, maxAmountIn, expectAmountOut, amountOutLimit, pythPriceIDs, partner, cetusDlmmPartner) {
8795
+ if (routerData.quoteID == null) {
8796
+ throw new Error(CLIENT_CONFIG.ERRORS.QUOTE_ID_REQUIRED);
8797
+ }
8798
+ const processedData = processFlattenRoutes(routerData);
8799
+ const swapCtx = newSwapContextV2(
8800
+ {
8801
+ quoteID: processedData.quoteID,
8802
+ fromCoinType: processedData.fromCoinType,
8803
+ targetCoinType: processedData.targetCoinType,
8804
+ maxAmountIn,
8805
+ expectAmountOut,
8806
+ amountOutLimit,
8807
+ inputCoin,
8808
+ feeRate: this.overlayFeeRate,
8809
+ feeRecipient: this.overlayFeeReceiver,
8810
+ packages: processedData.packages
8811
+ },
8812
+ txb
8813
+ );
8814
+ let dexRouters = /* @__PURE__ */ new Map();
8815
+ for (const flattenedPath of processedData.flattenedPaths) {
8816
+ const path = flattenedPath.path;
8817
+ if (!dexRouters.has(path.provider)) {
8818
+ dexRouters.set(
8819
+ path.provider,
8820
+ this.newDexRouterV3(
8821
+ path.provider,
8822
+ pythPriceIDs,
8823
+ partner,
8824
+ cetusDlmmPartner
8825
+ )
8826
+ );
8827
+ }
8828
+ const dex = dexRouters.get(path.provider);
8829
+ dex.swap(txb, flattenedPath, swapCtx, { pythPriceIDs });
8830
+ }
8831
+ const outputCoin = confirmSwap(
8832
+ {
8833
+ swapContext: swapCtx,
8834
+ targetCoinType: processedData.targetCoinType,
8835
+ packages: processedData.packages
8836
+ },
8837
+ txb
8838
+ );
8839
+ return outputCoin;
8840
+ }
8763
8841
  expectOutputSwapV3(txb, inputCoin, routerData, amountOut, _amountLimit, partner) {
8764
8842
  const receipts = [];
8765
8843
  const dex = new CetusRouter(this.env, partner);
@@ -8869,6 +8947,116 @@ var _AggregatorClient = class _AggregatorClient {
8869
8947
  );
8870
8948
  return outputCoin;
8871
8949
  }
8950
+ expectOutputSwapV3WithMaxAmountIn(txb, inputCoin, routerData, maxAmountIn, amountOut, _amountLimit, partner) {
8951
+ const receipts = [];
8952
+ const dex = new CetusRouter(this.env, partner);
8953
+ const processedData = processFlattenRoutes(routerData);
8954
+ const swapCtx = newSwapContextV2(
8955
+ {
8956
+ quoteID: processedData.quoteID,
8957
+ fromCoinType: processedData.fromCoinType,
8958
+ targetCoinType: processedData.targetCoinType,
8959
+ maxAmountIn,
8960
+ expectAmountOut: amountOut,
8961
+ amountOutLimit: amountOut,
8962
+ // amountOutLimit equals expectAmountOut when fix amout out
8963
+ inputCoin,
8964
+ feeRate: this.overlayFeeRate,
8965
+ feeRecipient: this.overlayFeeReceiver,
8966
+ packages: processedData.packages
8967
+ },
8968
+ txb
8969
+ );
8970
+ const firstCoinRecord = recordFirstCoinIndex(routerData.paths);
8971
+ let needRepayRecord = /* @__PURE__ */ new Map();
8972
+ let payRecord = /* @__PURE__ */ new Map();
8973
+ for (let j = routerData.paths.length - 1; j >= 0; j--) {
8974
+ const path = routerData.paths[j];
8975
+ const firstFromTokenIndex = firstCoinRecord.get(path.from);
8976
+ let amountArg;
8977
+ if (j !== firstFromTokenIndex || path.target === processedData.targetCoinType) {
8978
+ if (path.target !== processedData.targetCoinType) {
8979
+ let payAmount = BigInt(path.amountOut);
8980
+ if (payRecord.has(path.target)) {
8981
+ const oldPayAmount = payRecord.get(path.target);
8982
+ payAmount = oldPayAmount + payAmount;
8983
+ }
8984
+ payRecord.set(path.target, payAmount);
8985
+ }
8986
+ amountArg = txb.pure.u64(
8987
+ path.amountOut.toString()
8988
+ );
8989
+ } else {
8990
+ if (!needRepayRecord.has(path.target)) {
8991
+ throw Error("no need repay record");
8992
+ }
8993
+ if (payRecord.has(path.target)) {
8994
+ const oldPayAmount = payRecord.get(path.target);
8995
+ const oldNeedRepay = needRepayRecord.get(path.target);
8996
+ amountArg = dex.sub(
8997
+ txb,
8998
+ oldNeedRepay,
8999
+ txb.pure.u64(oldPayAmount),
9000
+ path.publishedAt
9001
+ );
9002
+ } else {
9003
+ amountArg = needRepayRecord.get(path.target);
9004
+ }
9005
+ }
9006
+ const flashSwapResult = dex.flashSwapFixedOutput(
9007
+ txb,
9008
+ path,
9009
+ amountArg,
9010
+ swapCtx
9011
+ );
9012
+ receipts.unshift(flashSwapResult.flashReceipt);
9013
+ if (needRepayRecord.has(path.from)) {
9014
+ const oldNeedRepay = needRepayRecord.get(path.from);
9015
+ needRepayRecord.set(
9016
+ path.from,
9017
+ dex.add(
9018
+ txb,
9019
+ oldNeedRepay,
9020
+ flashSwapResult.repayAmount,
9021
+ path.publishedAt
9022
+ )
9023
+ );
9024
+ } else {
9025
+ needRepayRecord.set(path.from, flashSwapResult.repayAmount);
9026
+ }
9027
+ }
9028
+ for (let j = 0; j < routerData.paths.length; j++) {
9029
+ const path = routerData.paths[j];
9030
+ dex.repayFlashSwapFixedOutput(txb, path, swapCtx, receipts[j]);
9031
+ }
9032
+ const remainInputBalance = takeBalance(
9033
+ {
9034
+ coinType: processedData.fromCoinType,
9035
+ amount: U64_MAX,
9036
+ swapCtx,
9037
+ packages: processedData.packages
9038
+ },
9039
+ txb
9040
+ );
9041
+ transferBalance(
9042
+ {
9043
+ balance: remainInputBalance,
9044
+ coinType: processedData.fromCoinType,
9045
+ recipient: this.signer,
9046
+ packages: processedData.packages
9047
+ },
9048
+ txb
9049
+ );
9050
+ const outputCoin = confirmSwap(
9051
+ {
9052
+ swapContext: swapCtx,
9053
+ targetCoinType: processedData.targetCoinType,
9054
+ packages: processedData.packages
9055
+ },
9056
+ txb
9057
+ );
9058
+ return outputCoin;
9059
+ }
8872
9060
  routerSwap(params) {
8873
9061
  return __async(this, null, function* () {
8874
9062
  const { router, inputCoin, slippage, txb, partner } = params;
@@ -8919,6 +9107,67 @@ var _AggregatorClient = class _AggregatorClient {
8919
9107
  }
8920
9108
  });
8921
9109
  }
9110
+ /**
9111
+ * Router swap with max amount in validation.
9112
+ * This method validates that the input coin amount does not exceed maxAmountIn.
9113
+ * If the validation fails, the transaction will abort with an error.
9114
+ *
9115
+ * @param params - Router swap parameters with maxAmountIn
9116
+ * @returns TransactionObjectArgument - The output coin from the swap
9117
+ * @throws Error if input coin amount exceeds maxAmountIn
9118
+ */
9119
+ routerSwapWithMaxAmountIn(params) {
9120
+ return __async(this, null, function* () {
9121
+ const { router, inputCoin, slippage, txb, partner, maxAmountIn } = params;
9122
+ if (slippage > 1 || slippage < 0) {
9123
+ throw new Error(CLIENT_CONFIG.ERRORS.INVALID_SLIPPAGE);
9124
+ }
9125
+ if (!params.router.packages || !params.router.packages.get(PACKAGE_NAMES.AGGREGATOR_V3)) {
9126
+ throw new Error(CLIENT_CONFIG.ERRORS.PACKAGES_REQUIRED);
9127
+ }
9128
+ const byAmountIn = params.router.byAmountIn;
9129
+ const amountIn = router.amountIn;
9130
+ const amountOut = router.amountOut;
9131
+ checkOverlayFeeConfig(this.overlayFeeRate, this.overlayFeeReceiver);
9132
+ let overlayFee = new import_bn6.default(0);
9133
+ if (byAmountIn) {
9134
+ overlayFee = amountOut.mul(new import_bn6.default(this.overlayFeeRate)).div(new import_bn6.default(1e6));
9135
+ } else {
9136
+ overlayFee = amountIn.mul(new import_bn6.default(this.overlayFeeRate)).div(new import_bn6.default(1e6));
9137
+ }
9138
+ const expectedAmountOut = byAmountIn ? amountOut.sub(overlayFee) : amountOut;
9139
+ const expectedAmountIn = byAmountIn ? amountIn : amountIn.add(overlayFee);
9140
+ const amountLimit = CalculateAmountLimitBN(
9141
+ byAmountIn ? expectedAmountOut : expectedAmountIn,
9142
+ byAmountIn,
9143
+ slippage
9144
+ );
9145
+ const priceIDs = findPythPriceIDs(router.paths);
9146
+ const priceInfoObjectIds = priceIDs.length > 0 ? yield this.updatePythPriceIDs(priceIDs, txb) : /* @__PURE__ */ new Map();
9147
+ if (byAmountIn) {
9148
+ return this.expectInputSwapV3WithMaxAmountIn(
9149
+ txb,
9150
+ inputCoin,
9151
+ router,
9152
+ maxAmountIn,
9153
+ amountOut.toString(),
9154
+ amountLimit.toString(),
9155
+ priceInfoObjectIds,
9156
+ partner != null ? partner : this.partner
9157
+ );
9158
+ } else {
9159
+ return this.expectOutputSwapV3WithMaxAmountIn(
9160
+ txb,
9161
+ inputCoin,
9162
+ router,
9163
+ maxAmountIn,
9164
+ amountOut.toString(),
9165
+ amountLimit.toString(),
9166
+ partner != null ? partner : this.partner
9167
+ );
9168
+ }
9169
+ });
9170
+ }
8922
9171
  // auto build input coin
8923
9172
  // auto merge, transfer or destory target coin.
8924
9173
  fastRouterSwap(params) {
package/dist/index.mjs CHANGED
@@ -3447,7 +3447,7 @@ var AGGREGATOR_V3_CONFIG = {
3447
3447
  };
3448
3448
 
3449
3449
  // src/api.ts
3450
- var SDK_VERSION = 1010400;
3450
+ var SDK_VERSION = 1010401;
3451
3451
  function parseRouterResponse(data, byAmountIn) {
3452
3452
  let packages = /* @__PURE__ */ new Map();
3453
3453
  if (data.packages) {
@@ -7753,6 +7753,37 @@ function newSwapContext(params, txb) {
7753
7753
  });
7754
7754
  return swap_context;
7755
7755
  }
7756
+ function newSwapContextV2(params, txb) {
7757
+ const {
7758
+ quoteID,
7759
+ fromCoinType,
7760
+ targetCoinType,
7761
+ maxAmountIn,
7762
+ expectAmountOut,
7763
+ amountOutLimit,
7764
+ inputCoin,
7765
+ feeRate,
7766
+ feeRecipient,
7767
+ aggregatorPublishedAt,
7768
+ packages
7769
+ } = params;
7770
+ const publishedAt = getAggregatorPublishedAt(packages, aggregatorPublishedAt);
7771
+ const args = [
7772
+ txb.pure.string(quoteID),
7773
+ txb.pure.u64(maxAmountIn.toString()),
7774
+ txb.pure.u64(expectAmountOut.toString()),
7775
+ txb.pure.u64(amountOutLimit.toString()),
7776
+ inputCoin,
7777
+ txb.pure.u32(Number(feeRate.toString())),
7778
+ txb.pure.address(feeRecipient)
7779
+ ];
7780
+ const swap_context = txb.moveCall({
7781
+ target: `${publishedAt}::router::new_swap_context_v2`,
7782
+ typeArguments: [fromCoinType, targetCoinType],
7783
+ arguments: args
7784
+ });
7785
+ return swap_context;
7786
+ }
7756
7787
  function confirmSwap(params, txb) {
7757
7788
  const { swapContext, targetCoinType, aggregatorPublishedAt, packages } = params;
7758
7789
  const publishedAt = getAggregatorPublishedAt(packages, aggregatorPublishedAt);
@@ -8754,6 +8785,53 @@ var _AggregatorClient = class _AggregatorClient {
8754
8785
  );
8755
8786
  return outputCoin;
8756
8787
  }
8788
+ expectInputSwapV3WithMaxAmountIn(txb, inputCoin, routerData, maxAmountIn, expectAmountOut, amountOutLimit, pythPriceIDs, partner, cetusDlmmPartner) {
8789
+ if (routerData.quoteID == null) {
8790
+ throw new Error(CLIENT_CONFIG.ERRORS.QUOTE_ID_REQUIRED);
8791
+ }
8792
+ const processedData = processFlattenRoutes(routerData);
8793
+ const swapCtx = newSwapContextV2(
8794
+ {
8795
+ quoteID: processedData.quoteID,
8796
+ fromCoinType: processedData.fromCoinType,
8797
+ targetCoinType: processedData.targetCoinType,
8798
+ maxAmountIn,
8799
+ expectAmountOut,
8800
+ amountOutLimit,
8801
+ inputCoin,
8802
+ feeRate: this.overlayFeeRate,
8803
+ feeRecipient: this.overlayFeeReceiver,
8804
+ packages: processedData.packages
8805
+ },
8806
+ txb
8807
+ );
8808
+ let dexRouters = /* @__PURE__ */ new Map();
8809
+ for (const flattenedPath of processedData.flattenedPaths) {
8810
+ const path = flattenedPath.path;
8811
+ if (!dexRouters.has(path.provider)) {
8812
+ dexRouters.set(
8813
+ path.provider,
8814
+ this.newDexRouterV3(
8815
+ path.provider,
8816
+ pythPriceIDs,
8817
+ partner,
8818
+ cetusDlmmPartner
8819
+ )
8820
+ );
8821
+ }
8822
+ const dex = dexRouters.get(path.provider);
8823
+ dex.swap(txb, flattenedPath, swapCtx, { pythPriceIDs });
8824
+ }
8825
+ const outputCoin = confirmSwap(
8826
+ {
8827
+ swapContext: swapCtx,
8828
+ targetCoinType: processedData.targetCoinType,
8829
+ packages: processedData.packages
8830
+ },
8831
+ txb
8832
+ );
8833
+ return outputCoin;
8834
+ }
8757
8835
  expectOutputSwapV3(txb, inputCoin, routerData, amountOut, _amountLimit, partner) {
8758
8836
  const receipts = [];
8759
8837
  const dex = new CetusRouter(this.env, partner);
@@ -8863,6 +8941,116 @@ var _AggregatorClient = class _AggregatorClient {
8863
8941
  );
8864
8942
  return outputCoin;
8865
8943
  }
8944
+ expectOutputSwapV3WithMaxAmountIn(txb, inputCoin, routerData, maxAmountIn, amountOut, _amountLimit, partner) {
8945
+ const receipts = [];
8946
+ const dex = new CetusRouter(this.env, partner);
8947
+ const processedData = processFlattenRoutes(routerData);
8948
+ const swapCtx = newSwapContextV2(
8949
+ {
8950
+ quoteID: processedData.quoteID,
8951
+ fromCoinType: processedData.fromCoinType,
8952
+ targetCoinType: processedData.targetCoinType,
8953
+ maxAmountIn,
8954
+ expectAmountOut: amountOut,
8955
+ amountOutLimit: amountOut,
8956
+ // amountOutLimit equals expectAmountOut when fix amout out
8957
+ inputCoin,
8958
+ feeRate: this.overlayFeeRate,
8959
+ feeRecipient: this.overlayFeeReceiver,
8960
+ packages: processedData.packages
8961
+ },
8962
+ txb
8963
+ );
8964
+ const firstCoinRecord = recordFirstCoinIndex(routerData.paths);
8965
+ let needRepayRecord = /* @__PURE__ */ new Map();
8966
+ let payRecord = /* @__PURE__ */ new Map();
8967
+ for (let j = routerData.paths.length - 1; j >= 0; j--) {
8968
+ const path = routerData.paths[j];
8969
+ const firstFromTokenIndex = firstCoinRecord.get(path.from);
8970
+ let amountArg;
8971
+ if (j !== firstFromTokenIndex || path.target === processedData.targetCoinType) {
8972
+ if (path.target !== processedData.targetCoinType) {
8973
+ let payAmount = BigInt(path.amountOut);
8974
+ if (payRecord.has(path.target)) {
8975
+ const oldPayAmount = payRecord.get(path.target);
8976
+ payAmount = oldPayAmount + payAmount;
8977
+ }
8978
+ payRecord.set(path.target, payAmount);
8979
+ }
8980
+ amountArg = txb.pure.u64(
8981
+ path.amountOut.toString()
8982
+ );
8983
+ } else {
8984
+ if (!needRepayRecord.has(path.target)) {
8985
+ throw Error("no need repay record");
8986
+ }
8987
+ if (payRecord.has(path.target)) {
8988
+ const oldPayAmount = payRecord.get(path.target);
8989
+ const oldNeedRepay = needRepayRecord.get(path.target);
8990
+ amountArg = dex.sub(
8991
+ txb,
8992
+ oldNeedRepay,
8993
+ txb.pure.u64(oldPayAmount),
8994
+ path.publishedAt
8995
+ );
8996
+ } else {
8997
+ amountArg = needRepayRecord.get(path.target);
8998
+ }
8999
+ }
9000
+ const flashSwapResult = dex.flashSwapFixedOutput(
9001
+ txb,
9002
+ path,
9003
+ amountArg,
9004
+ swapCtx
9005
+ );
9006
+ receipts.unshift(flashSwapResult.flashReceipt);
9007
+ if (needRepayRecord.has(path.from)) {
9008
+ const oldNeedRepay = needRepayRecord.get(path.from);
9009
+ needRepayRecord.set(
9010
+ path.from,
9011
+ dex.add(
9012
+ txb,
9013
+ oldNeedRepay,
9014
+ flashSwapResult.repayAmount,
9015
+ path.publishedAt
9016
+ )
9017
+ );
9018
+ } else {
9019
+ needRepayRecord.set(path.from, flashSwapResult.repayAmount);
9020
+ }
9021
+ }
9022
+ for (let j = 0; j < routerData.paths.length; j++) {
9023
+ const path = routerData.paths[j];
9024
+ dex.repayFlashSwapFixedOutput(txb, path, swapCtx, receipts[j]);
9025
+ }
9026
+ const remainInputBalance = takeBalance(
9027
+ {
9028
+ coinType: processedData.fromCoinType,
9029
+ amount: U64_MAX,
9030
+ swapCtx,
9031
+ packages: processedData.packages
9032
+ },
9033
+ txb
9034
+ );
9035
+ transferBalance(
9036
+ {
9037
+ balance: remainInputBalance,
9038
+ coinType: processedData.fromCoinType,
9039
+ recipient: this.signer,
9040
+ packages: processedData.packages
9041
+ },
9042
+ txb
9043
+ );
9044
+ const outputCoin = confirmSwap(
9045
+ {
9046
+ swapContext: swapCtx,
9047
+ targetCoinType: processedData.targetCoinType,
9048
+ packages: processedData.packages
9049
+ },
9050
+ txb
9051
+ );
9052
+ return outputCoin;
9053
+ }
8866
9054
  routerSwap(params) {
8867
9055
  return __async(this, null, function* () {
8868
9056
  const { router, inputCoin, slippage, txb, partner } = params;
@@ -8913,6 +9101,67 @@ var _AggregatorClient = class _AggregatorClient {
8913
9101
  }
8914
9102
  });
8915
9103
  }
9104
+ /**
9105
+ * Router swap with max amount in validation.
9106
+ * This method validates that the input coin amount does not exceed maxAmountIn.
9107
+ * If the validation fails, the transaction will abort with an error.
9108
+ *
9109
+ * @param params - Router swap parameters with maxAmountIn
9110
+ * @returns TransactionObjectArgument - The output coin from the swap
9111
+ * @throws Error if input coin amount exceeds maxAmountIn
9112
+ */
9113
+ routerSwapWithMaxAmountIn(params) {
9114
+ return __async(this, null, function* () {
9115
+ const { router, inputCoin, slippage, txb, partner, maxAmountIn } = params;
9116
+ if (slippage > 1 || slippage < 0) {
9117
+ throw new Error(CLIENT_CONFIG.ERRORS.INVALID_SLIPPAGE);
9118
+ }
9119
+ if (!params.router.packages || !params.router.packages.get(PACKAGE_NAMES.AGGREGATOR_V3)) {
9120
+ throw new Error(CLIENT_CONFIG.ERRORS.PACKAGES_REQUIRED);
9121
+ }
9122
+ const byAmountIn = params.router.byAmountIn;
9123
+ const amountIn = router.amountIn;
9124
+ const amountOut = router.amountOut;
9125
+ checkOverlayFeeConfig(this.overlayFeeRate, this.overlayFeeReceiver);
9126
+ let overlayFee = new import_bn6.default(0);
9127
+ if (byAmountIn) {
9128
+ overlayFee = amountOut.mul(new import_bn6.default(this.overlayFeeRate)).div(new import_bn6.default(1e6));
9129
+ } else {
9130
+ overlayFee = amountIn.mul(new import_bn6.default(this.overlayFeeRate)).div(new import_bn6.default(1e6));
9131
+ }
9132
+ const expectedAmountOut = byAmountIn ? amountOut.sub(overlayFee) : amountOut;
9133
+ const expectedAmountIn = byAmountIn ? amountIn : amountIn.add(overlayFee);
9134
+ const amountLimit = CalculateAmountLimitBN(
9135
+ byAmountIn ? expectedAmountOut : expectedAmountIn,
9136
+ byAmountIn,
9137
+ slippage
9138
+ );
9139
+ const priceIDs = findPythPriceIDs(router.paths);
9140
+ const priceInfoObjectIds = priceIDs.length > 0 ? yield this.updatePythPriceIDs(priceIDs, txb) : /* @__PURE__ */ new Map();
9141
+ if (byAmountIn) {
9142
+ return this.expectInputSwapV3WithMaxAmountIn(
9143
+ txb,
9144
+ inputCoin,
9145
+ router,
9146
+ maxAmountIn,
9147
+ amountOut.toString(),
9148
+ amountLimit.toString(),
9149
+ priceInfoObjectIds,
9150
+ partner != null ? partner : this.partner
9151
+ );
9152
+ } else {
9153
+ return this.expectOutputSwapV3WithMaxAmountIn(
9154
+ txb,
9155
+ inputCoin,
9156
+ router,
9157
+ maxAmountIn,
9158
+ amountOut.toString(),
9159
+ amountLimit.toString(),
9160
+ partner != null ? partner : this.partner
9161
+ );
9162
+ }
9163
+ });
9164
+ }
8916
9165
  // auto build input coin
8917
9166
  // auto merge, transfer or destory target coin.
8918
9167
  fastRouterSwap(params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cetusprotocol/aggregator-sdk",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "sideEffects": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",