@cetusprotocol/sui-clmm-sdk 1.0.0

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 (64) hide show
  1. package/.turbo/turbo-build.log +11100 -0
  2. package/README.md +108 -0
  3. package/dist/index.d.mts +2251 -0
  4. package/dist/index.d.ts +2251 -0
  5. package/dist/index.js +13 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +13 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/docs/add_liquidity.md +145 -0
  10. package/docs/close_position.md +57 -0
  11. package/docs/collect_fees.md +37 -0
  12. package/docs/create_clmm_pool.md +228 -0
  13. package/docs/error_code.md +69 -0
  14. package/docs/get_clmm_pools.md +92 -0
  15. package/docs/get_positions.md +70 -0
  16. package/docs/get_reward.md +53 -0
  17. package/docs/get_ticks.md +39 -0
  18. package/docs/migrate_to_version_6.0.md +143 -0
  19. package/docs/open_position.md +224 -0
  20. package/docs/partner_swap.md +60 -0
  21. package/docs/pre_swap.md +136 -0
  22. package/docs/remove_liquidity.md +124 -0
  23. package/docs/swap.md +153 -0
  24. package/docs/utils.md +85 -0
  25. package/package.json +37 -0
  26. package/src/config/index.ts +2 -0
  27. package/src/config/mainnet.ts +41 -0
  28. package/src/config/testnet.ts +40 -0
  29. package/src/errors/errors.ts +93 -0
  30. package/src/errors/index.ts +1 -0
  31. package/src/index.ts +10 -0
  32. package/src/math/apr.ts +167 -0
  33. package/src/math/index.ts +1 -0
  34. package/src/modules/configModule.ts +540 -0
  35. package/src/modules/index.ts +5 -0
  36. package/src/modules/poolModule.ts +1066 -0
  37. package/src/modules/positionModule.ts +932 -0
  38. package/src/modules/rewarderModule.ts +430 -0
  39. package/src/modules/swapModule.ts +389 -0
  40. package/src/sdk.ts +131 -0
  41. package/src/types/clmm_type.ts +1002 -0
  42. package/src/types/clmmpool.ts +366 -0
  43. package/src/types/config_type.ts +241 -0
  44. package/src/types/index.ts +8 -0
  45. package/src/types/sui.ts +124 -0
  46. package/src/types/token_type.ts +189 -0
  47. package/src/utils/common.ts +426 -0
  48. package/src/utils/index.ts +3 -0
  49. package/src/utils/positionUtils.ts +434 -0
  50. package/src/utils/swapUtils.ts +499 -0
  51. package/tests/add_liquidity.test.ts +121 -0
  52. package/tests/add_liquidity_fix_token.test.ts +182 -0
  53. package/tests/apr.test.ts +71 -0
  54. package/tests/cetus_config.test.ts +26 -0
  55. package/tests/collect_fees.test.ts +11 -0
  56. package/tests/pool.test.ts +267 -0
  57. package/tests/position.test.ts +145 -0
  58. package/tests/remove_liquidity.test.ts +119 -0
  59. package/tests/rewarder.test.ts +60 -0
  60. package/tests/sdk_config.test.ts +49 -0
  61. package/tests/swap.test.ts +254 -0
  62. package/tests/tsconfig.json +26 -0
  63. package/tsconfig.json +5 -0
  64. package/tsup.config.ts +10 -0
@@ -0,0 +1,434 @@
1
+ import type { TransactionObjectArgument } from '@mysten/sui/transactions'
2
+ import { Transaction } from '@mysten/sui/transactions'
3
+ import BN from 'bn.js'
4
+ import {
5
+ asUintN,
6
+ BuildCoinResult,
7
+ ClmmPoolUtil,
8
+ CLOCK_ADDRESS,
9
+ CoinAsset,
10
+ CoinAssist,
11
+ CoinPairType,
12
+ getPackagerConfigs,
13
+ normalizeCoinType,
14
+ } from '@cetusprotocol/common-sdk'
15
+ import Decimal from 'decimal.js'
16
+ import { handleMessageError, UtilsErrorCode } from '../errors/errors'
17
+ import SDK, { AddLiquidityFixTokenParams, CollectRewarderParams } from '../index'
18
+ import { ClmmIntegratePoolV2Module, ClmmIntegrateRouterModule } from '../types/sui'
19
+
20
+ export type AdjustResult = {
21
+ is_adjust_coin_a: boolean
22
+ is_adjust_coin_b: boolean
23
+ }
24
+
25
+ /**
26
+ * Adjust coinpair is sui
27
+ * @param {CoinPairType} coinPair
28
+ * @returns
29
+ */
30
+ export function findAdjustCoin(coinPair: CoinPairType): AdjustResult {
31
+ const is_adjust_coin_a = CoinAssist.isSuiCoin(coinPair.coin_type_a)
32
+ const is_adjust_coin_b = CoinAssist.isSuiCoin(coinPair.coin_type_b)
33
+ return { is_adjust_coin_a, is_adjust_coin_b }
34
+ }
35
+
36
+ /**
37
+ *
38
+ * @param {number} slippageAmount
39
+ * @param slippage
40
+ * @returns
41
+ */
42
+ function raverSlippageAmount(slippageAmount: number | string, slippage: number): string {
43
+ return Decimal.ceil(Decimal(slippageAmount).div(1 + slippage)).toString()
44
+ }
45
+
46
+ export class PositionUtils {
47
+ static createCollectRewarderAndFeeParams(
48
+ sdk: SDK,
49
+ tx: Transaction,
50
+ params: CollectRewarderParams,
51
+ all_coin_asset: CoinAsset[],
52
+ all_coin_asset_a?: CoinAsset[],
53
+ all_coin_asset_b?: CoinAsset[]
54
+ ) {
55
+ if (all_coin_asset_a === undefined) {
56
+ all_coin_asset_a = [...all_coin_asset]
57
+ }
58
+ if (all_coin_asset_b === undefined) {
59
+ all_coin_asset_b = [...all_coin_asset]
60
+ }
61
+ const coin_type_a = normalizeCoinType(params.coin_type_a)
62
+ const coin_type_b = normalizeCoinType(params.coin_type_b)
63
+ if (params.collect_fee) {
64
+ const primary_coin_a_input = CoinAssist.buildCoinForAmount(tx, all_coin_asset_a, BigInt(0), coin_type_a, false)
65
+ all_coin_asset_a = primary_coin_a_input.remain_coins
66
+
67
+ const primary_coin_b_input = CoinAssist.buildCoinForAmount(tx, all_coin_asset_b, BigInt(0), coin_type_b, false)
68
+ all_coin_asset_b = primary_coin_b_input.remain_coins
69
+
70
+ tx = sdk.Position.createCollectFeePayload(
71
+ {
72
+ pool_id: params.pool_id,
73
+ pos_id: params.pos_id,
74
+ coin_type_a: params.coin_type_a,
75
+ coin_type_b: params.coin_type_b,
76
+ },
77
+ tx,
78
+ primary_coin_a_input.target_coin,
79
+ primary_coin_b_input.target_coin
80
+ )
81
+ }
82
+ const primary_coin_inputs: TransactionObjectArgument[] = []
83
+ params.rewarder_coin_types.forEach((type) => {
84
+ switch (normalizeCoinType(type)) {
85
+ case coin_type_a:
86
+ primary_coin_inputs.push(CoinAssist.buildCoinForAmount(tx, all_coin_asset_a!, BigInt(0), type, false).target_coin)
87
+ break
88
+ case coin_type_b:
89
+ primary_coin_inputs.push(CoinAssist.buildCoinForAmount(tx, all_coin_asset_b!, BigInt(0), type, false).target_coin)
90
+ break
91
+ default:
92
+ primary_coin_inputs.push(CoinAssist.buildCoinForAmount(tx, all_coin_asset, BigInt(0), type, false).target_coin)
93
+ break
94
+ }
95
+ })
96
+ tx = sdk.Rewarder.createCollectRewarderPayload(params, tx, primary_coin_inputs)
97
+ return tx
98
+ }
99
+
100
+ /**
101
+ * adjust transaction for gas
102
+ * @param sdk
103
+ * @param amount
104
+ * @param tx
105
+ * @returns
106
+ */
107
+ static async adjustTransactionForGas(
108
+ sdk: SDK,
109
+ all_coins: CoinAsset[],
110
+ amount: bigint,
111
+ tx: Transaction
112
+ ): Promise<{ fixAmount: bigint; newTx?: Transaction }> {
113
+ tx.setSender(sdk.getSenderAddress())
114
+ // amount coins
115
+ const amount_coins = CoinAssist.selectCoinAssetGreaterThanOrEqual(all_coins, amount).selected_coins
116
+
117
+ const total_amount = CoinAssist.calculateTotalBalance(all_coins)
118
+
119
+ if (amount_coins.length === 0) {
120
+ handleMessageError(UtilsErrorCode.InsufficientBalance, `Insufficient balance exceed amount ${amount} real amount ${total_amount}`)
121
+ }
122
+ // If the remaining coin balance is greater than 1000000000, no gas fee correction will be done
123
+ if (total_amount - amount > 1000000000) {
124
+ return { fixAmount: amount }
125
+ }
126
+
127
+ // payload Estimated gas consumption
128
+ const estimate_gas = await sdk.FullClient.calculationTxGas(tx)
129
+
130
+ // Find estimateGas objectIds
131
+ const gas_coins = CoinAssist.selectCoinAssetGreaterThanOrEqual(
132
+ all_coins,
133
+ BigInt(estimate_gas),
134
+ amount_coins.map((item) => item.coin_object_id)
135
+ ).selected_coins
136
+
137
+ // There is not enough gas and the amount needs to be adjusted
138
+ if (gas_coins.length === 0) {
139
+ // Readjust the amount , Reserve 500 gas for the spit
140
+ const new_gas = BigInt(estimate_gas) + BigInt(500)
141
+ if (total_amount - amount < new_gas) {
142
+ amount -= new_gas
143
+ if (amount < 0) {
144
+ handleMessageError(UtilsErrorCode.InsufficientBalance, `gas Insufficient balance`)
145
+ }
146
+
147
+ const newTx = new Transaction()
148
+ return { fixAmount: amount, newTx }
149
+ }
150
+ }
151
+ return { fixAmount: amount }
152
+ }
153
+
154
+ // -----------------------------------------liquidity-----------------------------------------------//
155
+ /**
156
+ * build add liquidity transaction
157
+ * @param params
158
+ * @param slippage
159
+ * @param curSqrtPrice
160
+ * @returns
161
+ */
162
+ static async buildAddLiquidityFixTokenForGas(
163
+ sdk: SDK,
164
+ all_coins: CoinAsset[],
165
+ params: AddLiquidityFixTokenParams,
166
+ gas_estimate_arg: {
167
+ slippage: number
168
+ cur_sqrt_price: BN
169
+ },
170
+ tx?: Transaction,
171
+ input_coin_a?: TransactionObjectArgument,
172
+ input_coin_b?: TransactionObjectArgument
173
+ ): Promise<Transaction> {
174
+ tx = await this.buildAddLiquidityFixToken(sdk, all_coins, params, tx, input_coin_a, input_coin_b)
175
+
176
+ const { is_adjust_coin_a } = findAdjustCoin(params)
177
+
178
+ const sui_amount = is_adjust_coin_a ? params.amount_a : params.amount_b
179
+
180
+ const newResult = await this.adjustTransactionForGas(
181
+ sdk,
182
+ CoinAssist.getCoinAssets(is_adjust_coin_a ? params.coin_type_a : params.coin_type_b, all_coins),
183
+ BigInt(sui_amount),
184
+ tx
185
+ )
186
+
187
+ const { fixAmount } = newResult
188
+ const { newTx } = newResult
189
+
190
+ if (newTx != null) {
191
+ let primaryCoinAInputs: BuildCoinResult
192
+ let primaryCoinBInputs: BuildCoinResult
193
+
194
+ if (is_adjust_coin_a) {
195
+ params.amount_a = Number(fixAmount)
196
+ primaryCoinAInputs = this.buildAddLiquidityFixTokenCoinInput(
197
+ newTx,
198
+ !params.fix_amount_a,
199
+ fixAmount.toString(),
200
+ params.slippage,
201
+ params.coin_type_a,
202
+ all_coins,
203
+ false,
204
+ true
205
+ )
206
+ primaryCoinBInputs = this.buildAddLiquidityFixTokenCoinInput(
207
+ newTx,
208
+ params.fix_amount_a,
209
+ params.amount_b,
210
+ params.slippage,
211
+ params.coin_type_b,
212
+ all_coins,
213
+ false,
214
+ true
215
+ )
216
+ } else {
217
+ params.amount_b = Number(fixAmount)
218
+ primaryCoinAInputs = this.buildAddLiquidityFixTokenCoinInput(
219
+ newTx,
220
+ !params.fix_amount_a,
221
+ params.amount_a,
222
+ params.slippage,
223
+ params.coin_type_a,
224
+ all_coins,
225
+ false,
226
+ true
227
+ )
228
+ primaryCoinBInputs = this.buildAddLiquidityFixTokenCoinInput(
229
+ newTx,
230
+ params.fix_amount_a,
231
+ fixAmount.toString(),
232
+ params.slippage,
233
+ params.coin_type_b,
234
+ all_coins,
235
+ false,
236
+ true
237
+ )
238
+ params = this.fixAddLiquidityFixTokenParams(params, gas_estimate_arg.slippage, gas_estimate_arg.cur_sqrt_price)
239
+
240
+ tx = await this.buildAddLiquidityFixTokenArgs(newTx, sdk, all_coins, params, primaryCoinAInputs, primaryCoinBInputs)
241
+ return tx
242
+ }
243
+ }
244
+ return tx
245
+ }
246
+
247
+ /**
248
+ * build add liquidity transaction
249
+ * @param params
250
+ * @param packageId
251
+ * @returns
252
+ */
253
+ static async buildAddLiquidityFixToken(
254
+ sdk: SDK,
255
+ all_coins: CoinAsset[],
256
+ params: AddLiquidityFixTokenParams,
257
+ tx?: Transaction,
258
+ input_coin_a?: TransactionObjectArgument,
259
+ input_coin_b?: TransactionObjectArgument
260
+ ): Promise<Transaction> {
261
+ tx = tx || new Transaction()
262
+
263
+ let primaryCoinAInputs: BuildCoinResult
264
+ let primaryCoinBInputs: BuildCoinResult
265
+ if (input_coin_a == null || input_coin_b == null) {
266
+ primaryCoinAInputs = this.buildAddLiquidityFixTokenCoinInput(
267
+ tx,
268
+ !params.fix_amount_a,
269
+ params.amount_a,
270
+ params.slippage,
271
+ params.coin_type_a,
272
+ all_coins,
273
+ false,
274
+ true
275
+ )
276
+ primaryCoinBInputs = this.buildAddLiquidityFixTokenCoinInput(
277
+ tx,
278
+ params.fix_amount_a,
279
+ params.amount_b,
280
+ params.slippage,
281
+ params.coin_type_b,
282
+ all_coins,
283
+ false,
284
+ true
285
+ )
286
+ } else {
287
+ primaryCoinAInputs = {
288
+ target_coin: input_coin_a,
289
+ remain_coins: [],
290
+ is_mint_zero_coin: false,
291
+ target_coin_amount: '0',
292
+ selected_coins: [],
293
+ }
294
+ primaryCoinBInputs = {
295
+ target_coin: input_coin_b,
296
+ remain_coins: [],
297
+ is_mint_zero_coin: false,
298
+ target_coin_amount: '0',
299
+ selected_coins: [],
300
+ }
301
+ }
302
+
303
+ tx = this.buildAddLiquidityFixTokenArgs(
304
+ tx,
305
+ sdk,
306
+ all_coins,
307
+ params as AddLiquidityFixTokenParams,
308
+ primaryCoinAInputs,
309
+ primaryCoinBInputs
310
+ )
311
+ return tx
312
+ }
313
+
314
+ public static buildAddLiquidityFixTokenCoinInput(
315
+ tx: Transaction,
316
+ need_interval_amount: boolean,
317
+ amount: number | string,
318
+ slippage: number,
319
+ coin_type: string,
320
+ all_coins: CoinAsset[],
321
+ build_vector = true,
322
+ fix_amount = true
323
+ ): BuildCoinResult {
324
+ return need_interval_amount
325
+ ? CoinAssist.buildCoinForAmountInterval(
326
+ tx,
327
+ all_coins,
328
+ { amount_second: BigInt(raverSlippageAmount(amount, slippage)), amount_first: BigInt(amount) },
329
+ coin_type,
330
+ build_vector,
331
+ fix_amount
332
+ )
333
+ : CoinAssist.buildCoinForAmount(tx, all_coins, BigInt(amount), coin_type, build_vector, fix_amount)
334
+ }
335
+
336
+ /**
337
+ * fix add liquidity fix token for coin amount
338
+ * @param params
339
+ * @param slippage
340
+ * @param curSqrtPrice
341
+ * @returns
342
+ */
343
+ static fixAddLiquidityFixTokenParams(params: AddLiquidityFixTokenParams, slippage: number, curSqrtPrice: BN): AddLiquidityFixTokenParams {
344
+ const coinAmount = params.fix_amount_a ? params.amount_a : params.amount_b
345
+ const liquidityInput = ClmmPoolUtil.estLiquidityAndCoinAmountFromOneAmounts(
346
+ Number(params.tick_lower),
347
+ Number(params.tick_upper),
348
+ new BN(coinAmount),
349
+ params.fix_amount_a,
350
+ true,
351
+ slippage,
352
+ curSqrtPrice
353
+ )
354
+
355
+ params.amount_a = params.fix_amount_a ? params.amount_a : liquidityInput.coin_amount_limit_a
356
+ params.amount_b = params.fix_amount_a ? liquidityInput.coin_amount_limit_b : params.amount_b
357
+
358
+ return params
359
+ }
360
+
361
+ private static buildAddLiquidityFixTokenArgs(
362
+ tx: Transaction,
363
+ sdk: SDK,
364
+ all_coins: CoinAsset[],
365
+ params: AddLiquidityFixTokenParams,
366
+ primary_coina_inputs: BuildCoinResult,
367
+ primary_coinb_inputs: BuildCoinResult
368
+ ) {
369
+ const typeArguments = [params.coin_type_a, params.coin_type_b]
370
+ const functionName = params.is_open ? 'open_position_with_liquidity_by_fix_coin' : 'add_liquidity_by_fix_coin'
371
+ const { clmm_pool, integrate } = sdk.sdkOptions
372
+
373
+ if (!params.is_open) {
374
+ tx = this.createCollectRewarderAndFeeParams(
375
+ sdk,
376
+ tx,
377
+ params,
378
+ all_coins,
379
+ primary_coina_inputs.remain_coins,
380
+ primary_coinb_inputs.remain_coins
381
+ )
382
+ }
383
+
384
+ const clmmConfig = getPackagerConfigs(clmm_pool)
385
+ const args = params.is_open
386
+ ? [
387
+ tx.object(clmmConfig.global_config_id),
388
+ tx.object(params.pool_id),
389
+ tx.pure.u32(Number(asUintN(BigInt(params.tick_lower)).toString())),
390
+ tx.pure.u32(Number(asUintN(BigInt(params.tick_upper)).toString())),
391
+ primary_coina_inputs.target_coin,
392
+ primary_coinb_inputs.target_coin,
393
+ tx.pure.u64(params.amount_a),
394
+ tx.pure.u64(params.amount_b),
395
+ tx.pure.bool(params.fix_amount_a),
396
+ tx.object(CLOCK_ADDRESS),
397
+ ]
398
+ : [
399
+ tx.object(clmmConfig.global_config_id),
400
+ tx.object(params.pool_id),
401
+ tx.object(params.pos_id),
402
+ primary_coina_inputs.target_coin,
403
+ primary_coinb_inputs.target_coin,
404
+ tx.pure.u64(params.amount_a),
405
+ tx.pure.u64(params.amount_b),
406
+ tx.pure.bool(params.fix_amount_a),
407
+ tx.object(CLOCK_ADDRESS),
408
+ ]
409
+
410
+ tx.moveCall({
411
+ target: `${integrate.published_at}::${ClmmIntegratePoolV2Module}::${functionName}`,
412
+ typeArguments,
413
+ arguments: args,
414
+ })
415
+ return tx
416
+ }
417
+
418
+ static checkCoinThreshold(
419
+ sdk: SDK,
420
+ by_amount_in: boolean,
421
+ tx: Transaction,
422
+ coin: TransactionObjectArgument,
423
+ amount_limit: number,
424
+ coin_type: string
425
+ ) {
426
+ if (by_amount_in) {
427
+ tx.moveCall({
428
+ target: `${sdk.sdkOptions.integrate.published_at}::${ClmmIntegrateRouterModule}::check_coin_threshold`,
429
+ typeArguments: [coin_type],
430
+ arguments: [coin, tx.pure.u64(amount_limit)],
431
+ })
432
+ }
433
+ }
434
+ }