@cetusprotocol/aggregator-sdk 0.4.1 → 0.4.3

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.
Files changed (103) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/index.mjs +1 -1
  3. package/package.json +1 -1
  4. package/.env.example +0 -4
  5. package/bun.lockb +0 -0
  6. package/dist/src/api.d.ts +0 -104
  7. package/dist/src/client.d.ts +0 -118
  8. package/dist/src/const.d.ts +0 -8
  9. package/dist/src/errors.d.ts +0 -40
  10. package/dist/src/index.d.ts +0 -9
  11. package/dist/src/math.d.ts +0 -6
  12. package/dist/src/transaction/afsui.d.ts +0 -10
  13. package/dist/src/transaction/aftermath.d.ts +0 -13
  14. package/dist/src/transaction/alphafi.d.ts +0 -7
  15. package/dist/src/transaction/bluefin.d.ts +0 -7
  16. package/dist/src/transaction/bluemove.d.ts +0 -7
  17. package/dist/src/transaction/cetus.d.ts +0 -15
  18. package/dist/src/transaction/deepbook_v2.d.ts +0 -14
  19. package/dist/src/transaction/deepbook_v3.d.ts +0 -12
  20. package/dist/src/transaction/flowx_v2.d.ts +0 -7
  21. package/dist/src/transaction/flowx_v3.d.ts +0 -8
  22. package/dist/src/transaction/haedal.d.ts +0 -6
  23. package/dist/src/transaction/haedal_pmm.d.ts +0 -7
  24. package/dist/src/transaction/index.d.ts +0 -10
  25. package/dist/src/transaction/kriya_v2.d.ts +0 -6
  26. package/dist/src/transaction/kriya_v3.d.ts +0 -7
  27. package/dist/src/transaction/metastable.d.ts +0 -8
  28. package/dist/src/transaction/obric.d.ts +0 -8
  29. package/dist/src/transaction/scallop.d.ts +0 -8
  30. package/dist/src/transaction/steamm.d.ts +0 -6
  31. package/dist/src/transaction/suilend.d.ts +0 -7
  32. package/dist/src/transaction/swap.d.ts +0 -4
  33. package/dist/src/transaction/turbos.d.ts +0 -7
  34. package/dist/src/transaction/volo.d.ts +0 -8
  35. package/dist/src/types/CoinAssist.d.ts +0 -122
  36. package/dist/src/types/sui.d.ts +0 -112
  37. package/dist/src/utils/api.d.ts +0 -1
  38. package/dist/src/utils/coin.d.ts +0 -11
  39. package/dist/src/utils/contracts.d.ts +0 -16
  40. package/dist/src/utils/index.d.ts +0 -5
  41. package/dist/src/utils/msafe.d.ts +0 -2
  42. package/dist/src/utils/transaction.d.ts +0 -3
  43. package/dist/tests/math.test.d.ts +0 -1
  44. package/dist/tests/router/alphafi.test.d.ts +0 -2
  45. package/dist/tests/router/metastable.test.d.ts +0 -2
  46. package/dist/tests/router/obric.test.d.ts +0 -2
  47. package/dist/tests/router/scallop.test.d.ts +0 -2
  48. package/dist/tests/router/steamm.test.d.ts +0 -2
  49. package/dist/tests/router.test.d.ts +0 -2
  50. package/dist/tests/test_data.test.d.ts +0 -18
  51. package/dist/tests/wallet.test.d.ts +0 -1
  52. package/example/package.json +0 -17
  53. package/example/swap.ts +0 -21
  54. package/example/tsconfig.json +0 -12
  55. package/jest.config.mjs +0 -13
  56. package/src/api.ts +0 -325
  57. package/src/client.ts +0 -1081
  58. package/src/const.ts +0 -15
  59. package/src/errors.ts +0 -77
  60. package/src/index.ts +0 -10
  61. package/src/math.ts +0 -66
  62. package/src/transaction/afsui.ts +0 -62
  63. package/src/transaction/aftermath.ts +0 -88
  64. package/src/transaction/alphafi.ts +0 -50
  65. package/src/transaction/bluefin.ts +0 -46
  66. package/src/transaction/bluemove.ts +0 -42
  67. package/src/transaction/cetus.ts +0 -118
  68. package/src/transaction/deepbook_v2.ts +0 -122
  69. package/src/transaction/deepbook_v3.ts +0 -60
  70. package/src/transaction/flowx_v2.ts +0 -42
  71. package/src/transaction/flowx_v3.ts +0 -52
  72. package/src/transaction/haedal.ts +0 -33
  73. package/src/transaction/haedal_pmm.ts +0 -80
  74. package/src/transaction/index.ts +0 -42
  75. package/src/transaction/kriya_v2.ts +0 -37
  76. package/src/transaction/kriya_v3.ts +0 -47
  77. package/src/transaction/metastable.ts +0 -141
  78. package/src/transaction/obric.ts +0 -90
  79. package/src/transaction/scallop.ts +0 -64
  80. package/src/transaction/steamm.ts +0 -77
  81. package/src/transaction/suilend.ts +0 -48
  82. package/src/transaction/swap.ts +0 -162
  83. package/src/transaction/turbos.ts +0 -56
  84. package/src/transaction/volo.ts +0 -53
  85. package/src/types/CoinAssist.ts +0 -217
  86. package/src/types/sui.ts +0 -148
  87. package/src/utils/api.ts +0 -6
  88. package/src/utils/coin.ts +0 -136
  89. package/src/utils/contracts.ts +0 -136
  90. package/src/utils/index.ts +0 -5
  91. package/src/utils/msafe.ts +0 -40
  92. package/src/utils/transaction.ts +0 -20
  93. package/tests/math.test.ts +0 -21
  94. package/tests/router/alphafi.test.ts +0 -132
  95. package/tests/router/metastable.test.ts +0 -383
  96. package/tests/router/obric.test.ts +0 -203
  97. package/tests/router/scallop.test.ts +0 -133
  98. package/tests/router/steamm.test.ts +0 -130
  99. package/tests/router.test.ts +0 -379
  100. package/tests/test_data.test.ts +0 -28
  101. package/tests/wallet.test.ts +0 -21
  102. package/tsup.config.ts +0 -23
  103. package/version.mjs +0 -28
package/src/client.ts DELETED
@@ -1,1081 +0,0 @@
1
- import Decimal from "decimal.js"
2
- import { getFullnodeUrl, SuiClient } from "@mysten/sui/client"
3
- import {
4
- Transaction,
5
- TransactionObjectArgument,
6
- } from "@mysten/sui/transactions"
7
- import { Signer } from "@mysten/sui/cryptography"
8
- import BN from "bn.js"
9
- import { SUI_FRAMEWORK_ADDRESS } from "@mysten/sui/utils"
10
- import {
11
- Dex,
12
- Env,
13
- extractStructTagFromType,
14
- FindRouterParams,
15
- getRouterResult,
16
- Router,
17
- RouterData,
18
- getDeepbookV3Config,
19
- processEndpoint,
20
- DeepbookV3Config,
21
- getAggregatorV2PublishedAt,
22
- } from "."
23
- import { Aftermath } from "./transaction/aftermath"
24
- import { DeepbookV2 } from "./transaction/deepbook_v2"
25
- import { KriyaV2 } from "./transaction/kriya_v2"
26
- import { KriyaV3 } from "./transaction/kriya_v3"
27
- import { FlowxV2 } from "./transaction/flowx_v2"
28
- import { FlowxV3 } from "./transaction/flowx_v3"
29
- import { Turbos } from "./transaction/turbos"
30
- import { Cetus } from "./transaction/cetus"
31
- import { swapInPools } from "./transaction/swap"
32
- import { CalculateAmountLimit, CalculateAmountLimitBN } from "./math"
33
- import { Haedal } from "./transaction/haedal"
34
- import { Afsui } from "./transaction/afsui"
35
- import { Volo } from "./transaction/volo"
36
- import { Bluemove } from "./transaction/bluemove"
37
- import { CoinAsset } from "./types/sui"
38
- import { buildInputCoin } from "./utils/coin"
39
- import { DeepbookV3 } from "./transaction/deepbook_v3"
40
- import { Scallop } from "./transaction/scallop"
41
- import { Suilend } from "./transaction/suilend"
42
- import { Bluefin } from "./transaction/bluefin"
43
- import { HaedalPmm } from "./transaction/haedal_pmm"
44
- import { Alphafi } from "./transaction/alphafi"
45
- import { CoinUtils } from "./types/CoinAssist"
46
- import {
47
- SuiPriceServiceConnection,
48
- SuiPythClient,
49
- } from "@pythnetwork/pyth-sui-js"
50
- import { Steamm } from "./transaction/steamm"
51
- import { Metastable } from "./transaction/metastable"
52
- import { Obric } from "./transaction/obric"
53
-
54
- export const CETUS = "CETUS"
55
- export const DEEPBOOKV2 = "DEEPBOOK"
56
- export const KRIYA = "KRIYA"
57
- export const FLOWXV2 = "FLOWX"
58
- export const FLOWXV3 = "FLOWXV3"
59
- export const KRIYAV3 = "KRIYAV3"
60
- export const TURBOS = "TURBOS"
61
- export const AFTERMATH = "AFTERMATH"
62
- export const HAEDAL = "HAEDAL"
63
- export const VOLO = "VOLO"
64
- export const AFSUI = "AFSUI"
65
- export const BLUEMOVE = "BLUEMOVE"
66
- export const DEEPBOOKV3 = "DEEPBOOKV3"
67
- export const SCALLOP = "SCALLOP"
68
- export const SUILEND = "SUILEND"
69
- export const BLUEFIN = "BLUEFIN"
70
- export const HAEDALPMM = "HAEDALPMM"
71
- export const ALPHAFI = "ALPHAFI"
72
- export const SPRINGSUI = "SPRINGSUI"
73
- export const STEAMM = "STEAMM"
74
- export const METASTABLE = "METASTABLE"
75
- export const OBRIC = "OBRIC"
76
- export const DEFAULT_ENDPOINT = "https://api-sui.cetus.zone/router_v2"
77
-
78
- export type BuildRouterSwapParams = {
79
- routers: Router[]
80
- byAmountIn: boolean
81
- inputCoin: TransactionObjectArgument
82
- slippage: number
83
- txb: Transaction
84
- partner?: string
85
- // This parameter is used to pass the Deep token object. When using the DeepBook V3 provider,
86
- // users must pay fees with Deep tokens in non-whitelisted pools.
87
- deepbookv3DeepFee?: TransactionObjectArgument
88
- }
89
-
90
- export type BuildFastRouterSwapParams = {
91
- routers: Router[]
92
- byAmountIn: boolean
93
- slippage: number
94
- txb: Transaction
95
- partner?: string
96
- refreshAllCoins?: boolean
97
- payDeepFeeAmount?: number
98
- }
99
-
100
- export type BuildRouterSwapParamsV2 = {
101
- routers: RouterData
102
- inputCoin: TransactionObjectArgument
103
- slippage: number
104
- txb: Transaction
105
- partner?: string
106
- // This parameter is used to pass the Deep token object. When using the DeepBook V3 provider,
107
- // users must pay fees with Deep tokens in non-whitelisted pools.
108
- deepbookv3DeepFee?: TransactionObjectArgument
109
- }
110
-
111
- export type BuildFastRouterSwapParamsV2 = {
112
- routers: RouterData
113
- slippage: number
114
- txb: Transaction
115
- partner?: string
116
- refreshAllCoins?: boolean
117
- payDeepFeeAmount?: number
118
- }
119
-
120
- export interface SwapInPoolsParams {
121
- from: string
122
- target: string
123
- amount: BN
124
- byAmountIn: boolean
125
- pools: string[]
126
- }
127
-
128
- interface PythConfig {
129
- wormholeStateId: string
130
- pythStateId: string
131
- }
132
-
133
- export interface SwapInPoolsResult {
134
- isExceed: boolean
135
- routeData?: RouterData
136
- }
137
-
138
- function isBuilderRouterSwapParams(
139
- params: BuildRouterSwapParams | BuildRouterSwapParamsV2
140
- ): params is BuildRouterSwapParams {
141
- return Array.isArray((params as BuildRouterSwapParams).routers)
142
- }
143
-
144
- function isBuilderFastRouterSwapParams(
145
- params: BuildFastRouterSwapParams | BuildFastRouterSwapParamsV2
146
- ): params is BuildFastRouterSwapParams {
147
- return Array.isArray((params as BuildFastRouterSwapParams).routers)
148
- }
149
-
150
- export type AggregatorClientParams = {
151
- endpoint?: string
152
- signer?: string
153
- client?: SuiClient
154
- env?: Env
155
- pythUrls?: string[]
156
- apiKey?: string
157
- }
158
-
159
- export class AggregatorClient {
160
- public endpoint: string
161
- public signer: string
162
- public client: SuiClient
163
- public env: Env
164
- public apiKey: string
165
- private allCoins: Map<string, CoinAsset[]>
166
-
167
- private pythConnections: SuiPriceServiceConnection[]
168
- private pythClient: SuiPythClient
169
-
170
- private static readonly CONFIG: Record<Env, PythConfig> = {
171
- [Env.Testnet]: {
172
- wormholeStateId:
173
- "0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790",
174
- pythStateId:
175
- "0x243759059f4c3111179da5878c12f68d612c21a8d54d85edc86164bb18be1c7c",
176
- },
177
- [Env.Mainnet]: {
178
- wormholeStateId:
179
- "0xaeab97f96cf9877fee2883315d459552b2b921edc16d7ceac6eab944dd88919c",
180
- pythStateId:
181
- "0x1f9310238ee9298fb703c3419030b35b22bb1cc37113e3bb5007c99aec79e5b8",
182
- },
183
- }
184
-
185
- constructor(params: AggregatorClientParams) {
186
- this.endpoint = params.endpoint ? processEndpoint(params.endpoint) : DEFAULT_ENDPOINT
187
- this.client = params.client || new SuiClient({ url: getFullnodeUrl("mainnet") })
188
- this.signer = params.signer || ""
189
- this.env = params.env || Env.Mainnet
190
- this.allCoins = new Map<string, CoinAsset[]>()
191
-
192
- const config = AggregatorClient.CONFIG[this.env]
193
- this.pythConnections = this.newPythClients(params.pythUrls ?? [])
194
- this.pythClient = new SuiPythClient(
195
- this.client,
196
- config.pythStateId,
197
- config.wormholeStateId
198
- )
199
- this.apiKey = params.apiKey || ""
200
- }
201
-
202
- newPythClients(pythUrls: string[]) {
203
- if (!pythUrls.includes("https://hermes.pyth.network")) {
204
- pythUrls.push("https://hermes.pyth.network")
205
- }
206
-
207
- const connections = pythUrls.map(url => new SuiPriceServiceConnection(url, { timeout: 3000 }))
208
- return connections
209
- }
210
-
211
- async getCoins(
212
- coinType: string,
213
- refresh: boolean = true
214
- ): Promise<CoinAsset[]> {
215
- if (this.signer === "") {
216
- throw new Error("Signer is required, but not provided.")
217
- }
218
-
219
- let cursor = null
220
- let limit = 50
221
-
222
- if (!refresh) {
223
- const gotFromCoins = this.allCoins.get(coinType)
224
- if (gotFromCoins) {
225
- return gotFromCoins
226
- }
227
- }
228
-
229
- const allCoins: CoinAsset[] = []
230
- while (true) {
231
- try {
232
- const gotCoins = await this.client.getCoins({
233
- owner: this.signer,
234
- coinType,
235
- cursor,
236
- limit,
237
- })
238
- for (const coin of gotCoins.data) {
239
- allCoins.push({
240
- coinAddress: extractStructTagFromType(coin.coinType).source_address,
241
- coinObjectId: coin.coinObjectId,
242
- balance: BigInt(coin.balance),
243
- })
244
- }
245
- if (gotCoins.data.length < limit) {
246
- break
247
- }
248
- cursor = gotCoins.data[limit - 1].coinObjectId
249
- } catch (e) {
250
- console.error("getCoins error:", e)
251
- break
252
- }
253
- }
254
-
255
- this.allCoins.set(coinType, allCoins)
256
- return allCoins
257
- }
258
-
259
- async findRouters(params: FindRouterParams): Promise<RouterData | null> {
260
- return getRouterResult(this.endpoint, this.apiKey, params)
261
- }
262
-
263
- async executeFlexibleInputSwap(
264
- txb: Transaction,
265
- inputCoin: TransactionObjectArgument,
266
- routers: Router[],
267
- amountOutLimit: BN,
268
- pythPriceIDs: Map<string, string>,
269
- partner?: string,
270
- deepbookv3DeepFee?: TransactionObjectArgument,
271
- packages?: Map<string, string>
272
- ) {
273
- if (routers.length === 0) {
274
- throw new Error("No router found")
275
- }
276
-
277
- const outputCoinType = routers[0].path[routers[0].path.length - 1].target
278
- const outputCoins = []
279
-
280
- for (let i = 0; i < routers.length - 1; i++) {
281
- if (routers[i].path.length === 0) {
282
- throw new Error("Empty path")
283
- }
284
- // 为每条路径分割所需的代币
285
- const splitCoin = txb.splitCoins(inputCoin, [routers[i].amountIn.toString()])
286
- let nextCoin = splitCoin[0] as TransactionObjectArgument
287
-
288
- for (const path of routers[i].path) {
289
- const dex = this.newDex(path.provider, pythPriceIDs, partner)
290
- nextCoin = await dex.swap(
291
- this,
292
- txb,
293
- path,
294
- nextCoin,
295
- packages,
296
- deepbookv3DeepFee
297
- )
298
- }
299
- outputCoins.push(nextCoin)
300
- }
301
-
302
- // 处理最后一条路径,使用剩余的所有代币
303
- if (routers[routers.length - 1].path.length === 0) {
304
- throw new Error("Empty path")
305
- }
306
- let lastCoin = inputCoin
307
- for (const path of routers[routers.length - 1].path) {
308
- const dex = this.newDex(path.provider, pythPriceIDs, partner)
309
- lastCoin = await dex.swap(
310
- this,
311
- txb,
312
- path,
313
- lastCoin,
314
- packages,
315
- deepbookv3DeepFee
316
- )
317
- }
318
- outputCoins.push(lastCoin)
319
-
320
- const aggregatorV2PublishedAt = getAggregatorV2PublishedAt(
321
- this.publishedAtV2(),
322
- packages
323
- )
324
-
325
- const mergedTargetCoin = this.checkCoinThresholdAndMergeCoin(
326
- txb,
327
- outputCoins,
328
- outputCoinType,
329
- amountOutLimit,
330
- aggregatorV2PublishedAt
331
- )
332
- return mergedTargetCoin
333
- }
334
-
335
- async expectInputSwap(
336
- txb: Transaction,
337
- inputCoin: TransactionObjectArgument,
338
- routers: Router[],
339
- amountOutLimit: BN,
340
- pythPriceIDs: Map<string, string>,
341
- partner?: string,
342
- deepbookv3DeepFee?: TransactionObjectArgument,
343
- packages?: Map<string, string>
344
- ) {
345
- if (routers.length === 0) {
346
- throw new Error("No router found")
347
- }
348
- const splitAmounts = routers.map((router) => router.amountIn.toString())
349
- const inputCoinType = routers[0].path[0].from
350
- const outputCoinType = routers[0].path[routers[0].path.length - 1].target
351
- const inputCoins = txb.splitCoins(inputCoin, splitAmounts)
352
- const outputCoins = []
353
- for (let i = 0; i < routers.length; i++) {
354
- if (routers[i].path.length === 0) {
355
- throw new Error("Empty path")
356
- }
357
- let nextCoin = inputCoins[i] as TransactionObjectArgument
358
- for (const path of routers[i].path) {
359
- const dex = this.newDex(path.provider, pythPriceIDs, partner)
360
- nextCoin = await dex.swap(
361
- this,
362
- txb,
363
- path,
364
- nextCoin,
365
- packages,
366
- deepbookv3DeepFee
367
- )
368
- }
369
-
370
- outputCoins.push(nextCoin)
371
- }
372
-
373
- const aggregatorV2PublishedAt = getAggregatorV2PublishedAt(
374
- this.publishedAtV2(),
375
- packages
376
- )
377
-
378
- this.transferOrDestoryCoin(
379
- txb,
380
- inputCoin,
381
- inputCoinType,
382
- this.publishedAtV2()
383
- )
384
- const mergedTargetCointhis = this.checkCoinThresholdAndMergeCoin(
385
- txb,
386
- outputCoins,
387
- outputCoinType,
388
- amountOutLimit,
389
- aggregatorV2PublishedAt
390
- )
391
- return mergedTargetCointhis
392
- }
393
-
394
- async expectOutputSwap(
395
- txb: Transaction,
396
- inputCoin: TransactionObjectArgument,
397
- routers: Router[],
398
- partner?: string,
399
- packages?: Map<string, string>
400
- ): Promise<TransactionObjectArgument> {
401
- const returnCoins: TransactionObjectArgument[] = []
402
- const receipts: TransactionObjectArgument[] = []
403
- const targetCoins = []
404
- const dex = new Cetus(this.env, partner)
405
-
406
- const aggregatorV2PublishedAt = getAggregatorV2PublishedAt(
407
- this.publishedAtV2(),
408
- packages
409
- )
410
-
411
- for (let i = 0; i < routers.length; i++) {
412
- const router = routers[i]
413
- for (let j = router.path.length - 1; j >= 0; j--) {
414
- const path = router.path[j]
415
- const flashSwapResult = dex.flash_swap(this, txb, path, false)
416
- returnCoins.unshift(flashSwapResult.targetCoin)
417
- receipts.unshift(flashSwapResult.flashReceipt)
418
- }
419
-
420
- let nextRepayCoin = inputCoin
421
- for (let j = 0; j < router.path.length; j++) {
422
- const path = router.path[j]
423
- const repayResult = dex.repay_flash_swap(
424
- this,
425
- txb,
426
- path,
427
- nextRepayCoin,
428
- receipts[j]
429
- )
430
- nextRepayCoin = returnCoins[j]
431
- if (j === 0) {
432
- inputCoin = repayResult
433
- } else {
434
- this.transferOrDestoryCoin(
435
- txb,
436
- repayResult,
437
- path.from,
438
- aggregatorV2PublishedAt
439
- )
440
- }
441
- if (j === router.path.length - 1) {
442
- targetCoins.push(nextRepayCoin)
443
- }
444
- }
445
- }
446
- const inputCoinType = routers[0].path[0].from
447
- this.transferOrDestoryCoin(
448
- txb,
449
- inputCoin,
450
- inputCoinType,
451
- aggregatorV2PublishedAt
452
- )
453
- if (targetCoins.length > 1) {
454
- const vec = txb.makeMoveVec({ elements: targetCoins.slice(1) })
455
- txb.moveCall({
456
- target: `${SUI_FRAMEWORK_ADDRESS}::pay::join_vec`,
457
- typeArguments: [routers[0].path[routers[0].path.length - 1].target],
458
- arguments: [targetCoins[0], vec],
459
- })
460
- }
461
-
462
- return targetCoins[0]
463
- }
464
-
465
- async swapInPools(
466
- params: SwapInPoolsParams
467
- ): Promise<SwapInPoolsResult | null> {
468
- let result
469
- try {
470
- result = await swapInPools(this.client, params, this.signer, this.env)
471
- } catch (e) {
472
- console.error("swapInPools error:", e)
473
- return null
474
- }
475
- return result
476
- }
477
-
478
- async routerSwap(
479
- params: BuildRouterSwapParams | BuildRouterSwapParamsV2
480
- ): Promise<TransactionObjectArgument> {
481
- const { routers, inputCoin, slippage, txb, partner, deepbookv3DeepFee } =
482
- params
483
-
484
- const routerData = Array.isArray(routers) ? routers : routers.routes
485
- const byAmountIn = isBuilderRouterSwapParams(params)
486
- ? params.byAmountIn
487
- : params.routers.byAmountIn
488
-
489
- const amountIn = routerData.reduce(
490
- (acc, router) => acc.add(router.amountIn),
491
- new BN(0)
492
- )
493
- const amountOut = routerData.reduce(
494
- (acc, router) => acc.add(router.amountOut),
495
- new BN(0)
496
- )
497
-
498
- const amountLimit = CalculateAmountLimitBN(
499
- byAmountIn ? amountOut : amountIn,
500
- byAmountIn,
501
- slippage
502
- )
503
-
504
- const packages = isBuilderRouterSwapParams(params)
505
- ? undefined
506
- : params.routers.packages
507
-
508
- const aggregatorV2PublishedAt = getAggregatorV2PublishedAt(
509
- this.publishedAtV2(),
510
- packages
511
- )
512
-
513
- const priceIDs = findPythPriceIDs(routerData)
514
-
515
- const priceInfoObjectIds =
516
- priceIDs.length > 0
517
- ? await this.updatePythPriceIDs(priceIDs, txb)
518
- : new Map<string, string>()
519
-
520
- if (byAmountIn) {
521
- const targetCoin = await this.expectInputSwap(
522
- txb,
523
- inputCoin,
524
- routerData,
525
- amountLimit,
526
- priceInfoObjectIds,
527
- partner,
528
- deepbookv3DeepFee,
529
- packages
530
- )
531
- return targetCoin
532
- }
533
-
534
- // When exact output, we will set slippage limit in split coin.
535
- const splitedInputCoins = txb.splitCoins(inputCoin, [
536
- amountLimit.toString(),
537
- ])
538
- this.transferOrDestoryCoin(
539
- txb,
540
- inputCoin,
541
- routerData[0].path[0].from,
542
- aggregatorV2PublishedAt
543
- )
544
- const targetCoin = await this.expectOutputSwap(
545
- txb,
546
- splitedInputCoins[0],
547
- routerData,
548
- partner
549
- )
550
- return targetCoin
551
- }
552
-
553
- async fixableRouterSwap(
554
- params: BuildRouterSwapParamsV2
555
- ): Promise<TransactionObjectArgument> {
556
- const { routers, inputCoin, slippage, txb, partner, deepbookv3DeepFee } =
557
- params
558
-
559
- const routerData = Array.isArray(routers) ? routers : routers.routes
560
- const byAmountIn = params.routers.byAmountIn
561
-
562
- const amountIn = routerData.reduce(
563
- (acc, router) => acc.add(router.amountIn),
564
- new BN(0)
565
- )
566
- const amountOut = routerData.reduce(
567
- (acc, router) => acc.add(router.amountOut),
568
- new BN(0)
569
- )
570
-
571
- const amountLimit = CalculateAmountLimitBN(
572
- byAmountIn ? amountOut : amountIn,
573
- byAmountIn,
574
- slippage
575
- )
576
-
577
- const packages = isBuilderRouterSwapParams(params)
578
- ? undefined
579
- : params.routers.packages
580
-
581
- const aggregatorV2PublishedAt = getAggregatorV2PublishedAt(
582
- this.publishedAtV2(),
583
- packages
584
- )
585
-
586
- const priceIDs = findPythPriceIDs(routerData)
587
-
588
- const priceInfoObjectIds =
589
- priceIDs.length > 0
590
- ? await this.updatePythPriceIDs(priceIDs, txb)
591
- : new Map<string, string>()
592
-
593
- if (byAmountIn) {
594
- const targetCoin = await this.executeFlexibleInputSwap(
595
- txb,
596
- inputCoin,
597
- routerData,
598
- amountLimit,
599
- priceInfoObjectIds,
600
- partner,
601
- deepbookv3DeepFee,
602
- packages
603
- )
604
- return targetCoin
605
- }
606
-
607
- const targetCoin = await this.expectOutputSwap(
608
- txb,
609
- inputCoin,
610
- routerData,
611
- partner
612
- )
613
- return targetCoin
614
- }
615
-
616
- // auto build input coin
617
- // auto merge, transfer or destory target coin.
618
- async fastRouterSwap(
619
- params: BuildFastRouterSwapParams | BuildFastRouterSwapParamsV2
620
- ) {
621
- const {
622
- routers,
623
- slippage,
624
- txb,
625
- partner,
626
- refreshAllCoins,
627
- payDeepFeeAmount,
628
- } = params
629
-
630
- const routerData = Array.isArray(routers) ? routers : routers.routes
631
- const fromCoinType = routerData[0].path[0].from
632
- let fromCoins = await this.getCoins(fromCoinType, refreshAllCoins)
633
-
634
- const targetCoinType =
635
- routerData[0].path[routerData[0].path.length - 1].target
636
- const amountIn = routerData.reduce(
637
- (acc, router) => acc.add(router.amountIn),
638
- new BN(0)
639
- )
640
- const amountOut = routerData.reduce(
641
- (acc, router) => acc.add(router.amountOut),
642
- new BN(0)
643
- )
644
-
645
- const byAmountIn = isBuilderFastRouterSwapParams(params)
646
- ? params.byAmountIn
647
- : params.routers.byAmountIn
648
- const amountLimit = CalculateAmountLimit(
649
- byAmountIn ? amountOut : amountIn,
650
- byAmountIn,
651
- slippage
652
- )
653
- const amount = byAmountIn ? amountIn : amountLimit
654
- const buildFromCoinRes = buildInputCoin(
655
- txb,
656
- fromCoins,
657
- BigInt(amount.toString()),
658
- fromCoinType
659
- )
660
-
661
- let deepCoin
662
- if (payDeepFeeAmount && payDeepFeeAmount > 0) {
663
- let deepCoins = await this.getCoins(this.deepbookv3DeepFeeType())
664
- deepCoin = buildInputCoin(
665
- txb,
666
- deepCoins,
667
- BigInt(payDeepFeeAmount),
668
- this.deepbookv3DeepFeeType()
669
- ).targetCoin
670
- }
671
-
672
- const routerSwapParams = isBuilderFastRouterSwapParams(params)
673
- ? {
674
- routers: routerData,
675
- inputCoin: buildFromCoinRes.targetCoin,
676
- slippage,
677
- byAmountIn,
678
- txb,
679
- partner,
680
- deepbookv3DeepFee: deepCoin,
681
- }
682
- : {
683
- routers: params.routers,
684
- inputCoin: buildFromCoinRes.targetCoin,
685
- slippage,
686
- byAmountIn,
687
- txb,
688
- partner,
689
- deepbookv3DeepFee: deepCoin,
690
- }
691
-
692
- const targetCoin = await this.routerSwap(routerSwapParams)
693
-
694
- if (CoinUtils.isSuiCoin(targetCoinType)) {
695
- txb.mergeCoins(txb.gas, [targetCoin])
696
- } else {
697
- let targetCoins = await this.getCoins(targetCoinType, refreshAllCoins)
698
- const targetCoinRes = buildInputCoin(
699
- txb,
700
- targetCoins,
701
- BigInt(0),
702
- targetCoinType
703
- )
704
-
705
- const packages = isBuilderFastRouterSwapParams(params)
706
- ? undefined
707
- : params.routers.packages
708
- const aggregatorV2PublishedAt = getAggregatorV2PublishedAt(
709
- this.publishedAtV2(),
710
- packages
711
- )
712
-
713
- txb.mergeCoins(targetCoinRes.targetCoin, [targetCoin])
714
- if (targetCoinRes.isMintZeroCoin) {
715
- this.transferOrDestoryCoin(
716
- txb,
717
- targetCoinRes.targetCoin,
718
- targetCoinType,
719
- aggregatorV2PublishedAt
720
- )
721
- }
722
- }
723
- }
724
-
725
- // Include cetus、deepbookv2、flowxv2 & v3、kriyav2 & v3、turbos、aftermath、haedal、afsui、volo、bluemove
726
- publishedAtV2(): string {
727
- if (this.env === Env.Mainnet) {
728
- // return "0x3fb42ddf908af45f9fc3c59eab227888ff24ba2e137b3b55bf80920fd47e11af" // version 6
729
- // return "0xf9c6f78322ed667909e05f6b42b2f5a67af6297503c905335e02a15148e9440d" // version 7
730
- // return "0x2485feb9d42c7c3bcb8ecde555ad40f1b073d9fb4faf354fa2d30a0b183a23ce" // version 8
731
- return "0x3864c7c59a4889fec05d1aae4bc9dba5a0e0940594b424fbed44cb3f6ac4c032" // version 9
732
- // return "0x803db8dfcc86fc1afbc7d2212bd14ec9690978ddebea0d590e01147d6b17aa06" // pre
733
- } else {
734
- // return "0x0ed287d6c3fe4962d0994ffddc1d19a15fba6a81533f3f0dcc2bbcedebce0637" // version 2
735
- return "0x52eae33adeb44de55cfb3f281d4cc9e02d976181c0952f5323648b5717b33934"
736
- }
737
- }
738
-
739
- // Include deepbookv3, scallop, bluefin
740
- publishedAtV2Extend(): string {
741
- if (this.env === Env.Mainnet) {
742
- // return "0x43811be4677f5a5de7bf2dac740c10abddfaa524aee6b18e910eeadda8a2f6ae" // version 1, deepbookv3
743
- // return "0x6d70ffa7aa3f924c3f0b573d27d29895a0ee666aaff821073f75cb14af7fd01a" // version 3, deepbookv3 & scallop
744
- // return "0x16d9418726c26d8cb4ce8c9dd75917fa9b1c7bf47d38d7a1a22603135f0f2a56" // version 4, add suilend
745
- // return "0x3b6d71bdeb8ce5b06febfd3cfc29ecd60d50da729477c8b8038ecdae34541b91" // version 5, add bluefin
746
- // return "0x81ade554cb24a7564ca43a4bfb7381b08d9e5c5f375162c95215b696ab903502" // version 6, force upgrade scallop
747
- // return "0x347dd58bbd11cd82c8b386b344729717c04a998da73386e82a239cc196d5706b" // version 7
748
- // return "0xf2fcea41dc217385019828375764fa06d9bd25e8e4726ba1962680849fb8d613" // version 8
749
- // return "0xa2d8a4279d69d8fec04b2fea8852d0d467d3cc0d39c5890180d439ae7a9953ed" // version 9
750
- // return "0x34ef25b60b51f9d07cd9b7dc5b08dfdf26c7b0ff00c57bb17454c161fa6b6b83" // version 10
751
- // return "0xf57be4b9f9036034b1c5484d299d8fb68d5f43862d6afe8886d67db293dfc4bc" // version 11
752
- return "0x200e762fa2c49f3dc150813038fbf22fd4f894ac6f23ebe1085c62f2ef97f1ca" // version 12
753
- } else {
754
- return "0xabb6a81c8a216828e317719e06125de5bb2cb0fe8f9916ff8c023ca5be224c78"
755
- }
756
- }
757
-
758
- deepbookv3DeepFeeType(): string {
759
- if (this.env === Env.Mainnet) {
760
- return "0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP"
761
- } else {
762
- return "0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP"
763
- }
764
- }
765
-
766
- transferOrDestoryCoin(
767
- txb: Transaction,
768
- coin: TransactionObjectArgument,
769
- coinType: string,
770
- aggregatorV2PublishedAt: string
771
- ) {
772
- txb.moveCall({
773
- target: `${aggregatorV2PublishedAt}::utils::transfer_or_destroy_coin`,
774
- typeArguments: [coinType],
775
- arguments: [coin],
776
- })
777
- }
778
-
779
- checkCoinThresholdAndMergeCoin(
780
- txb: Transaction,
781
- coins: TransactionObjectArgument[],
782
- coinType: string,
783
- amountLimit: BN,
784
- aggregatorV2PublishedAt: string
785
- ) {
786
- let targetCoin = coins[0]
787
- if (coins.length > 1) {
788
- let vec = txb.makeMoveVec({ elements: coins.slice(1) })
789
- txb.moveCall({
790
- target: `${SUI_FRAMEWORK_ADDRESS}::pay::join_vec`,
791
- typeArguments: [coinType],
792
- arguments: [coins[0], vec],
793
- })
794
- targetCoin = coins[0]
795
- }
796
-
797
- txb.moveCall({
798
- target: `${aggregatorV2PublishedAt}::utils::check_coin_threshold`,
799
- typeArguments: [coinType],
800
- arguments: [targetCoin, txb.pure.u64(amountLimit.toString())],
801
- })
802
- return targetCoin
803
- }
804
-
805
- newDex(
806
- provider: string,
807
- pythPriceIDs: Map<string, string>,
808
- partner?: string
809
- ): Dex {
810
- switch (provider) {
811
- case CETUS:
812
- return new Cetus(this.env, partner)
813
- case DEEPBOOKV2:
814
- return new DeepbookV2(this.env)
815
- case DEEPBOOKV3:
816
- return new DeepbookV3(this.env)
817
- case KRIYA:
818
- return new KriyaV2(this.env)
819
- case KRIYAV3:
820
- return new KriyaV3(this.env)
821
- case FLOWXV2:
822
- return new FlowxV2(this.env)
823
- case FLOWXV3:
824
- return new FlowxV3(this.env)
825
- case TURBOS:
826
- return new Turbos(this.env)
827
- case AFTERMATH:
828
- return new Aftermath(this.env)
829
- case HAEDAL:
830
- return new Haedal(this.env)
831
- case AFSUI:
832
- return new Afsui(this.env)
833
- case VOLO:
834
- return new Volo(this.env)
835
- case BLUEMOVE:
836
- return new Bluemove(this.env)
837
- case SCALLOP:
838
- return new Scallop(this.env)
839
- case SUILEND:
840
- return new Suilend(this.env)
841
- case SPRINGSUI:
842
- return new Suilend(this.env)
843
- case BLUEFIN:
844
- return new Bluefin(this.env)
845
- case HAEDALPMM:
846
- return new HaedalPmm(this.env, pythPriceIDs)
847
- case ALPHAFI:
848
- return new Alphafi(this.env)
849
- case STEAMM:
850
- return new Steamm(this.env)
851
- case METASTABLE:
852
- return new Metastable(this.env, pythPriceIDs)
853
- case OBRIC:
854
- return new Obric(this.env, pythPriceIDs)
855
- default:
856
- throw new Error(`Unsupported dex ${provider}`)
857
- }
858
- }
859
-
860
- async signAndExecuteTransaction(txb: Transaction, signer: Signer) {
861
- const res = await this.client.signAndExecuteTransaction({
862
- transaction: txb,
863
- signer,
864
- options: {
865
- showEffects: true,
866
- showEvents: true,
867
- showInput: true,
868
- showBalanceChanges: true,
869
- },
870
- })
871
- return res
872
- }
873
-
874
- async devInspectTransactionBlock(txb: Transaction) {
875
- const res = await this.client.devInspectTransactionBlock({
876
- transactionBlock: txb,
877
- sender: this.signer,
878
- })
879
-
880
- return res
881
- }
882
-
883
- async sendTransaction(txb: Transaction, signer: Signer) {
884
- const res = await this.client.signAndExecuteTransaction({
885
- transaction: txb,
886
- signer,
887
- })
888
- return res
889
- }
890
-
891
- async getDeepbookV3Config(): Promise<DeepbookV3Config | null> {
892
- const res = await getDeepbookV3Config(this.endpoint)
893
- if (res) {
894
- return res.data
895
- }
896
- return null
897
- }
898
-
899
- async updatePythPriceIDs(
900
- priceIDs: string[],
901
- txb: Transaction
902
- ): Promise<Map<string, string>> {
903
- let priceUpdateData: Buffer[] | null = null
904
- let lastError: Error | null = null
905
-
906
- for (const connection of this.pythConnections) {
907
- try {
908
- priceUpdateData = await connection.getPriceFeedsUpdateData(priceIDs)
909
- break
910
- } catch (e) {
911
- lastError = e as Error
912
- console.log("Error: ", e)
913
- continue
914
- }
915
- }
916
-
917
- if (priceUpdateData == null) {
918
- throw new Error(
919
- `All Pyth price nodes are unavailable. Cannot fetch price data. Please switch to or add new available Pyth nodes. Detailed error: ${lastError?.message}`
920
- )
921
- }
922
-
923
- let priceInfoObjectIds = []
924
- try {
925
- priceInfoObjectIds = await this.pythClient.updatePriceFeeds(
926
- txb,
927
- priceUpdateData,
928
- priceIDs
929
- )
930
- } catch (e) {
931
- throw new Error(`All Pyth price nodes are unavailable. Cannot fetch price data. Please switch to or add new available Pyth nodes in the pythUrls parameter when initializing AggregatorClient, for example: new AggregatorClient({ pythUrls: ["https://your-pyth-node-url"] }). Detailed error: ${e}`)
932
- }
933
-
934
- let priceInfoObjectIdsMap = new Map<string, string>()
935
- for (let i = 0; i < priceIDs.length; i++) {
936
- priceInfoObjectIdsMap.set(priceIDs[i], priceInfoObjectIds[i])
937
- }
938
- return priceInfoObjectIdsMap
939
- }
940
- }
941
-
942
- export function findPythPriceIDs(routes: Router[]): string[] {
943
- const priceIDs = new Set<string>()
944
-
945
- for (const route of routes) {
946
- for (const path of route.path) {
947
- if (path.provider === HAEDALPMM) {
948
- if (
949
- path.extendedDetails &&
950
- path.extendedDetails.haedalPmmBasePriceSeed &&
951
- path.extendedDetails.haedalPmmQuotePriceSeed
952
- ) {
953
- priceIDs.add(path.extendedDetails.haedalPmmBasePriceSeed)
954
- priceIDs.add(path.extendedDetails.haedalPmmQuotePriceSeed)
955
- }
956
- }
957
- if (path.provider === METASTABLE) {
958
- if (path.extendedDetails && path.extendedDetails.metastablePriceSeed) {
959
- priceIDs.add(path.extendedDetails.metastablePriceSeed)
960
- }
961
- if (path.extendedDetails && path.extendedDetails.metastableETHPriceSeed) {
962
- priceIDs.add(path.extendedDetails.metastableETHPriceSeed)
963
- }
964
- }
965
- if (path.provider === OBRIC) {
966
- if (path.extendedDetails && path.extendedDetails.obricCoinAPriceSeed) {
967
- priceIDs.add(path.extendedDetails.obricCoinAPriceSeed)
968
- }
969
- if (path.extendedDetails && path.extendedDetails.obricCoinBPriceSeed) {
970
- priceIDs.add(path.extendedDetails.obricCoinBPriceSeed)
971
- }
972
- }
973
- }
974
- }
975
- return Array.from(priceIDs)
976
- }
977
-
978
- export function parseRouterResponse(
979
- data: any,
980
- byAmountIn: boolean
981
- ): RouterData {
982
- let totalDeepFee = 0
983
- for (const route of data.routes) {
984
- for (const path of route.path) {
985
- if (path.extended_details && path.extended_details.deepbookv3_deep_fee) {
986
- totalDeepFee += Number(path.extended_details.deepbookv3_deep_fee)
987
- }
988
- }
989
- }
990
-
991
- let packages = undefined
992
- if (data.packages != null) {
993
- packages = new Map<string, string>()
994
- for (const [key, value] of Object.entries(data.packages)) {
995
- packages.set(key, value as string)
996
- }
997
- }
998
-
999
- let routerData: RouterData = {
1000
- amountIn: new BN(data.amount_in.toString()),
1001
- amountOut: new BN(data.amount_out.toString()),
1002
- byAmountIn,
1003
- insufficientLiquidity: false,
1004
- routes: data.routes.map((route: any) => {
1005
- return {
1006
- path: route.path.map((path: any) => {
1007
- let version
1008
- if (path.provider === AFTERMATH) {
1009
- version =
1010
- path.extended_details.aftermath_pool_flatness === 0 ? "v2" : "v3"
1011
- }
1012
-
1013
- let extendedDetails
1014
- if (
1015
- path.provider === TURBOS ||
1016
- path.provider === AFTERMATH ||
1017
- path.provider === CETUS ||
1018
- path.provider === DEEPBOOKV3 ||
1019
- path.provider === SCALLOP ||
1020
- path.provider === HAEDALPMM ||
1021
- path.provider === STEAMM ||
1022
- path.provider === METASTABLE ||
1023
- path.provider === OBRIC
1024
- ) {
1025
- extendedDetails = {
1026
- aftermathLpSupplyType:
1027
- path.extended_details?.aftermath_lp_supply_type,
1028
- turbosFeeType: path.extended_details?.turbos_fee_type,
1029
- afterSqrtPrice: path.extended_details?.after_sqrt_price,
1030
- deepbookv3DeepFee: path.extended_details?.deepbookv3_deep_fee,
1031
- scallopScoinTreasury:
1032
- path.extended_details?.scallop_scoin_treasury,
1033
- haedalPmmBasePriceSeed:
1034
- path.extended_details?.haedal_pmm_base_price_seed,
1035
- haedalPmmQuotePriceSeed:
1036
- path.extended_details?.haedal_pmm_quote_price_seed,
1037
- steammBankA: path.extended_details?.steamm_bank_a,
1038
- steammBankB: path.extended_details?.steamm_bank_b,
1039
- steammLendingMarket: path.extended_details?.steamm_lending_market,
1040
- steammLendingMarketType: path.extended_details?.steamm_lending_market_type,
1041
- steammBCoinAType: path.extended_details?.steamm_btoken_a_type,
1042
- steammBCoinBType: path.extended_details?.steamm_btoken_b_type,
1043
- steammLPToken: path.extended_details?.steamm_lp_token_type,
1044
- metastablePriceSeed: path.extended_details?.metastable_price_seed,
1045
- metastableETHPriceSeed: path.extended_details?.metastable_eth_price_seed,
1046
- metastableWhitelistedAppId: path.extended_details?.metastable_whitelisted_app_id,
1047
- metastableCreateCapPkgId: path.extended_details?.metastable_create_cap_pkg_id,
1048
- metastableCreateCapModule: path.extended_details?.metastable_create_cap_module,
1049
- metastableCreateCapAllTypeParams: path.extended_details?.metastable_create_cap_all_type_params,
1050
- metastableRegistryId: path.extended_details?.metastable_registry_id,
1051
- obricCoinAPriceSeed: path.extended_details?.obric_coin_a_price_seed,
1052
- obricCoinBPriceSeed: path.extended_details?.obric_coin_b_price_seed,
1053
- obricCoinAPriceId: path.extended_details?.obric_coin_a_price_id,
1054
- obricCoinBPriceId: path.extended_details?.obric_coin_b_price_id,
1055
- }
1056
- }
1057
-
1058
- return {
1059
- id: path.id,
1060
- direction: path.direction,
1061
- provider: path.provider,
1062
- from: path.from,
1063
- target: path.target,
1064
- feeRate: path.fee_rate,
1065
- amountIn: path.amount_in,
1066
- amountOut: path.amount_out,
1067
- extendedDetails,
1068
- version,
1069
- }
1070
- }),
1071
- amountIn: new BN(route.amount_in.toString()),
1072
- amountOut: new BN(route.amount_out.toString()),
1073
- initialPrice: new Decimal(route.initial_price.toString()),
1074
- }
1075
- }),
1076
- totalDeepFee,
1077
- packages,
1078
- }
1079
-
1080
- return routerData
1081
- }