@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,499 @@
1
+ import { Transaction, TransactionObjectArgument } from '@mysten/sui/transactions'
2
+ import BN from 'bn.js'
3
+ import {
4
+ adjustForSlippage,
5
+ BuildCoinResult,
6
+ CLOCK_ADDRESS,
7
+ CoinAsset,
8
+ CoinAssist,
9
+ getPackagerConfigs,
10
+ MathUtil,
11
+ MAX_SQRT_PRICE,
12
+ MIN_SQRT_PRICE,
13
+ U64_MAX,
14
+ ZERO,
15
+ } from '@cetusprotocol/common-sdk'
16
+ import { ConfigErrorCode, handleError, handleMessageError, UtilsErrorCode } from '../errors/errors'
17
+ import { CetusClmmSDK, SdkOptions } from '../sdk'
18
+ import { SwapGasEstimateArg, SwapParams } from '../types/clmm_type'
19
+ import { ClmmIntegratePoolV2Module, ClmmIntegrateRouterModule, ClmmIntegrateRouterWithPartnerModule } from '../types/sui'
20
+ import { PositionUtils } from './positionUtils'
21
+
22
+ export class SwapUtils {
23
+ /**
24
+ * Get the default sqrt price limit for a swap.
25
+ *
26
+ * @param a2b - true if the swap is A to B, false if the swap is B to A.
27
+ * @returns The default sqrt price limit for the swap.
28
+ */
29
+ static getDefaultSqrtPriceLimit(a2b: boolean): BN {
30
+ return new BN(a2b ? MIN_SQRT_PRICE : MAX_SQRT_PRICE)
31
+ }
32
+
33
+ /**
34
+ * Get the default values for the otherAmountThreshold in a swap.
35
+ *
36
+ * @param amount_specified_is_input - The direction of a swap
37
+ * @returns The default values for the otherAmountThreshold parameter in a swap.
38
+ */
39
+ static getDefaultOtherAmountThreshold(amount_specified_is_input: boolean): BN {
40
+ return amount_specified_is_input ? ZERO : U64_MAX
41
+ }
42
+
43
+ /**
44
+ * build add liquidity transaction
45
+ * @param params
46
+ * @param slippage
47
+ * @param curSqrtPrice
48
+ * @returns
49
+ */
50
+ static async buildSwapTransactionForGas(
51
+ sdk: CetusClmmSDK,
52
+ params: SwapParams,
53
+ all_coin_asset: CoinAsset[],
54
+ gas_estimate_arg: SwapGasEstimateArg
55
+ ): Promise<Transaction> {
56
+ let tx = this.buildSwapTransaction(sdk, params, all_coin_asset)
57
+ tx.setSender(sdk.getSenderAddress())
58
+ const newResult = await this.adjustTransactionForGas(
59
+ sdk,
60
+ CoinAssist.getCoinAssets(params.a2b ? params.coin_type_a : params.coin_type_b, all_coin_asset),
61
+ BigInt(params.by_amount_in ? params.amount : params.amount_limit),
62
+ tx
63
+ )
64
+
65
+ const { fixAmount, newTx } = newResult
66
+
67
+ if (newTx !== undefined) {
68
+ newTx.setSender(sdk.getSenderAddress())
69
+ if (params.by_amount_in) {
70
+ params.amount = fixAmount.toString()
71
+ } else {
72
+ params.amount_limit = fixAmount.toString()
73
+ }
74
+ params = await this.fixSwapParams(sdk, params, gas_estimate_arg)
75
+
76
+ const primaryCoinInputA = CoinAssist.buildCoinForAmount(
77
+ tx,
78
+ all_coin_asset,
79
+ params.a2b ? BigInt(params.by_amount_in ? params.amount : params.amount_limit) : BigInt(0),
80
+ params.coin_type_a
81
+ )
82
+
83
+ const primaryCoinInputB = CoinAssist.buildCoinForAmount(
84
+ tx,
85
+ all_coin_asset,
86
+ params.a2b ? BigInt(0) : BigInt(params.by_amount_in ? params.amount : params.amount_limit),
87
+ params.coin_type_b
88
+ )
89
+
90
+ tx = this.buildSwapTransactionArgs(newTx, params, sdk.sdkOptions, primaryCoinInputA, primaryCoinInputB)
91
+ }
92
+
93
+ return tx
94
+ }
95
+
96
+ /**
97
+ * build swap transaction
98
+ * @param params
99
+ * @param packageId
100
+ * @returns
101
+ */
102
+ static buildSwapTransactionArgs(
103
+ tx: Transaction,
104
+ params: SwapParams,
105
+ sdk_options: SdkOptions,
106
+ primary_coin_input_a: BuildCoinResult,
107
+ primary_coin_input_b: BuildCoinResult
108
+ ): Transaction {
109
+ const { clmm_pool, integrate } = sdk_options
110
+
111
+ const sqrtPriceLimit = SwapUtils.getDefaultSqrtPriceLimit(params.a2b)
112
+ const typeArguments = [params.coin_type_a, params.coin_type_b]
113
+ const { global_config_id } = getPackagerConfigs(clmm_pool)
114
+
115
+ if (global_config_id === undefined) {
116
+ handleMessageError(ConfigErrorCode.InvalidConfig, 'clmm.config.global_config_id is undefined')
117
+ }
118
+
119
+ const hasSwapPartner = params.swap_partner !== undefined
120
+
121
+ const functionName = hasSwapPartner
122
+ ? params.a2b
123
+ ? 'swap_a2b_with_partner'
124
+ : 'swap_b2a_with_partner'
125
+ : params.a2b
126
+ ? 'swap_a2b'
127
+ : 'swap_b2a'
128
+
129
+ const args = hasSwapPartner
130
+ ? [
131
+ tx.object(global_config_id),
132
+ tx.object(params.pool_id),
133
+ tx.object(params.swap_partner!),
134
+ primary_coin_input_a.target_coin,
135
+ primary_coin_input_b.target_coin,
136
+ tx.pure.bool(params.by_amount_in),
137
+ tx.pure.u64(params.amount),
138
+ tx.pure.u64(params.amount_limit),
139
+ tx.pure.u128(sqrtPriceLimit.toString()),
140
+ tx.object(CLOCK_ADDRESS),
141
+ ]
142
+ : [
143
+ tx.object(global_config_id),
144
+ tx.object(params.pool_id),
145
+ primary_coin_input_a.target_coin,
146
+ primary_coin_input_b.target_coin,
147
+ tx.pure.bool(params.by_amount_in),
148
+ tx.pure.u64(params.amount),
149
+ tx.pure.u64(params.amount_limit),
150
+ tx.pure.u128(sqrtPriceLimit.toString()),
151
+ tx.object(CLOCK_ADDRESS),
152
+ ]
153
+
154
+ tx.moveCall({
155
+ target: `${integrate.published_at}::${ClmmIntegratePoolV2Module}::${functionName}`,
156
+ typeArguments,
157
+ arguments: args,
158
+ })
159
+ return tx
160
+ }
161
+
162
+ /**
163
+ * adjust transaction for gas
164
+ * @param sdk
165
+ * @param amount
166
+ * @param tx
167
+ * @returns
168
+ */
169
+ static async adjustTransactionForGas(
170
+ sdk: CetusClmmSDK,
171
+ all_coins: CoinAsset[],
172
+ amount: bigint,
173
+ tx: Transaction
174
+ ): Promise<{ fixAmount: bigint; newTx?: Transaction }> {
175
+ tx.setSender(sdk.getSenderAddress())
176
+ // amount coins
177
+ const amount_coins = CoinAssist.selectCoinAssetGreaterThanOrEqual(all_coins, amount).selected_coins
178
+ const total_amount = CoinAssist.calculateTotalBalance(all_coins)
179
+
180
+ if (amount_coins.length === 0) {
181
+ handleMessageError(UtilsErrorCode.InsufficientBalance, `Insufficient balance exceed amount ${amount} real amount ${total_amount}`)
182
+ }
183
+
184
+ // If the remaining coin balance is greater than 1000000000, no gas fee correction will be done
185
+ if (total_amount - amount > 1000000000) {
186
+ return { fixAmount: amount }
187
+ }
188
+
189
+ // payload Estimated gas consumption
190
+ const estimate_gas = await sdk.FullClient.calculationTxGas(tx)
191
+
192
+ // Find estimateGas objectIds
193
+ const gas_coins = CoinAssist.selectCoinAssetGreaterThanOrEqual(
194
+ all_coins,
195
+ BigInt(estimate_gas),
196
+ amount_coins.map((item) => item.coin_object_id)
197
+ ).selected_coins
198
+
199
+ // There is not enough gas and the amount needs to be adjusted
200
+ if (gas_coins.length === 0) {
201
+ // Readjust the amount , Reserve 500 gas for the spit
202
+ const new_gas = BigInt(estimate_gas) + BigInt(500)
203
+ if (total_amount - amount < new_gas) {
204
+ amount -= new_gas
205
+ if (amount < 0) {
206
+ handleMessageError(UtilsErrorCode.InsufficientBalance, `gas Insufficient balance`)
207
+ }
208
+
209
+ const newTx = new Transaction()
210
+ return { fixAmount: amount, newTx }
211
+ }
212
+ }
213
+ return { fixAmount: amount }
214
+ }
215
+
216
+ /**
217
+ * build swap transaction
218
+ * @param params
219
+ * @param packageId
220
+ * @returns
221
+ */
222
+ static buildSwapTransaction(sdk: CetusClmmSDK, params: SwapParams, all_coin_asset: CoinAsset[]): Transaction {
223
+ let tx = new Transaction()
224
+ tx.setSender(sdk.getSenderAddress())
225
+
226
+ const primaryCoinInputA = CoinAssist.buildCoinForAmount(
227
+ tx,
228
+ all_coin_asset,
229
+ params.a2b ? BigInt(params.by_amount_in ? params.amount : params.amount_limit) : BigInt(0),
230
+ params.coin_type_a,
231
+ false
232
+ )
233
+
234
+ const primaryCoinInputB = CoinAssist.buildCoinForAmount(
235
+ tx,
236
+ all_coin_asset,
237
+ params.a2b ? BigInt(0) : BigInt(params.by_amount_in ? params.amount : params.amount_limit),
238
+ params.coin_type_b,
239
+ false
240
+ )
241
+
242
+ tx = this.buildSwapTransactionArgs(tx, params, sdk.sdkOptions, primaryCoinInputA, primaryCoinInputB)
243
+ return tx
244
+ }
245
+
246
+ static async fixSwapParams(sdk: CetusClmmSDK, params: SwapParams, gasEstimateArg: SwapGasEstimateArg): Promise<SwapParams> {
247
+ const { current_pool } = gasEstimateArg
248
+ try {
249
+ const res: any = await sdk.Swap.preSwap({
250
+ decimals_a: gasEstimateArg.decimals_a,
251
+ decimals_b: gasEstimateArg.decimals_b,
252
+ a2b: params.a2b,
253
+ by_amount_in: params.by_amount_in,
254
+ amount: params.amount,
255
+ pool: current_pool,
256
+ current_sqrt_price: current_pool.current_sqrt_price,
257
+ coin_type_a: current_pool.coin_type_a,
258
+ coin_type_b: current_pool.coin_type_b,
259
+ })
260
+
261
+ const to_amount = gasEstimateArg.by_amount_in ? res.estimated_amount_out : res.estimated_amount_in
262
+
263
+ const amount_limit = adjustForSlippage(to_amount, gasEstimateArg.slippage, !gasEstimateArg.by_amount_in)
264
+ params.amount_limit = amount_limit.toString()
265
+ } catch (error) {
266
+ handleError(ConfigErrorCode.InvalidConfig, error as Error)
267
+ }
268
+
269
+ return params
270
+ }
271
+
272
+ static async buildSwapTransactionWithoutTransferCoinsForGas(
273
+ sdk: CetusClmmSDK,
274
+ params: SwapParams,
275
+ all_coin_asset: CoinAsset[],
276
+ gas_estimate_arg: SwapGasEstimateArg
277
+ ): Promise<{ tx: Transaction; coin_ab_s: TransactionObjectArgument[] }> {
278
+ let { tx, coin_ab_s } = SwapUtils.buildSwapTransactionWithoutTransferCoins(sdk, params, all_coin_asset)
279
+ tx.setSender(sdk.getSenderAddress())
280
+ const newResult = await SwapUtils.adjustTransactionForGas(
281
+ sdk,
282
+ CoinAssist.getCoinAssets(params.a2b ? params.coin_type_a : params.coin_type_b, all_coin_asset),
283
+ BigInt(params.by_amount_in ? params.amount : params.amount_limit),
284
+ tx
285
+ )
286
+
287
+ const { fixAmount, newTx } = newResult
288
+
289
+ if (newTx !== undefined) {
290
+ newTx.setSender(sdk.getSenderAddress())
291
+ if (params.by_amount_in) {
292
+ params.amount = fixAmount.toString()
293
+ } else {
294
+ params.amount_limit = fixAmount.toString()
295
+ }
296
+ params = await SwapUtils.fixSwapParams(sdk, params, gas_estimate_arg)
297
+
298
+ const primaryCoinInputA = CoinAssist.buildCoinForAmount(
299
+ tx,
300
+ all_coin_asset,
301
+ params.a2b ? BigInt(params.by_amount_in ? params.amount : params.amount_limit) : BigInt(0),
302
+ params.coin_type_a,
303
+ false,
304
+ true
305
+ )
306
+
307
+ const primaryCoinInputB = CoinAssist.buildCoinForAmount(
308
+ tx,
309
+ all_coin_asset,
310
+ params.a2b ? BigInt(0) : BigInt(params.by_amount_in ? params.amount : params.amount_limit),
311
+ params.coin_type_b,
312
+ false,
313
+ true
314
+ )
315
+
316
+ const res = SwapUtils.buildSwapTransactionWithoutTransferCoinArgs(
317
+ sdk,
318
+ newTx,
319
+ params,
320
+ sdk.sdkOptions,
321
+ primaryCoinInputA,
322
+ primaryCoinInputB
323
+ )
324
+ tx = res.tx
325
+ coin_ab_s = res.txRes
326
+ }
327
+
328
+ return { tx, coin_ab_s }
329
+ }
330
+
331
+ /**
332
+ * build swap transaction and return swapped coin
333
+ * @param params
334
+ * @param packageId
335
+ * @returns
336
+ */
337
+ static buildSwapTransactionWithoutTransferCoins(
338
+ sdk: CetusClmmSDK,
339
+ params: SwapParams,
340
+ all_coin_asset: CoinAsset[]
341
+ ): { tx: Transaction; coin_ab_s: TransactionObjectArgument[] } {
342
+ const tx = new Transaction()
343
+ tx.setSender(sdk.getSenderAddress())
344
+
345
+ // Fix amount must set true, to support amount limit.
346
+ const primaryCoinInputA = CoinAssist.buildCoinForAmount(
347
+ tx,
348
+ all_coin_asset,
349
+ params.a2b ? BigInt(params.by_amount_in ? params.amount : params.amount_limit) : BigInt(0),
350
+ params.coin_type_a,
351
+ false,
352
+ true
353
+ )
354
+
355
+ const primaryCoinInputB = CoinAssist.buildCoinForAmount(
356
+ tx,
357
+ all_coin_asset,
358
+ params.a2b ? BigInt(0) : BigInt(params.by_amount_in ? params.amount : params.amount_limit),
359
+ params.coin_type_b,
360
+ false,
361
+ true
362
+ )
363
+
364
+ const res = SwapUtils.buildSwapTransactionWithoutTransferCoinArgs(sdk, tx, params, sdk.sdkOptions, primaryCoinInputA, primaryCoinInputB)
365
+ return { tx: res.tx, coin_ab_s: res.txRes }
366
+ }
367
+
368
+ /**
369
+ * build swap transaction
370
+ * @param params
371
+ * @param packageId
372
+ * @returns
373
+ */
374
+ static buildSwapTransactionWithoutTransferCoinArgs(
375
+ sdk: CetusClmmSDK,
376
+ tx: Transaction,
377
+ params: SwapParams,
378
+ sdk_options: SdkOptions,
379
+ primary_coin_input_a: BuildCoinResult,
380
+ primary_coin_input_b: BuildCoinResult
381
+ ): { tx: Transaction; txRes: TransactionObjectArgument[] } {
382
+ const { clmm_pool, integrate } = sdk_options
383
+
384
+ const sqrtPriceLimit = SwapUtils.getDefaultSqrtPriceLimit(params.a2b)
385
+
386
+ const { global_config_id } = getPackagerConfigs(clmm_pool)
387
+
388
+ if (global_config_id === undefined) {
389
+ handleMessageError(ConfigErrorCode.InvalidConfig, 'clmm.config.global_config_id is undefined')
390
+ }
391
+
392
+ const hasSwapPartner = params.swap_partner !== undefined
393
+
394
+ const functionName = hasSwapPartner ? 'swap_with_partner' : 'swap'
395
+
396
+ const moduleName = hasSwapPartner ? ClmmIntegrateRouterWithPartnerModule : ClmmIntegrateRouterModule
397
+
398
+ const args = hasSwapPartner
399
+ ? [
400
+ tx.object(global_config_id),
401
+ tx.object(params.pool_id),
402
+ tx.object(params.swap_partner!),
403
+ primary_coin_input_a.target_coin,
404
+ primary_coin_input_b.target_coin,
405
+ tx.pure.bool(params.a2b),
406
+ tx.pure.bool(params.by_amount_in),
407
+ tx.pure.u64(params.amount),
408
+ tx.pure.u128(sqrtPriceLimit.toString()),
409
+ tx.pure.bool(false), // use coin value always set false.
410
+ tx.object(CLOCK_ADDRESS),
411
+ ]
412
+ : [
413
+ tx.object(global_config_id),
414
+ tx.object(params.pool_id),
415
+ primary_coin_input_a.target_coin,
416
+ primary_coin_input_b.target_coin,
417
+ tx.pure.bool(params.a2b),
418
+ tx.pure.bool(params.by_amount_in),
419
+ tx.pure.u64(params.amount),
420
+ tx.pure.u128(sqrtPriceLimit.toString()),
421
+ tx.pure.bool(false), // use coin value always set false.
422
+ tx.object(CLOCK_ADDRESS),
423
+ ]
424
+
425
+ const typeArguments = [params.coin_type_a, params.coin_type_b]
426
+ const coinABs: TransactionObjectArgument[] = tx.moveCall({
427
+ target: `${integrate.published_at}::${moduleName}::${functionName}`,
428
+ typeArguments,
429
+ arguments: args,
430
+ })
431
+
432
+ if (params.by_amount_in) {
433
+ const toCoinType = params.a2b ? params.coin_type_b : params.coin_type_a
434
+ const toCoin = params.a2b ? coinABs[1] : coinABs[0]
435
+ const totalAmount = Number(params.amount_limit)
436
+ PositionUtils.checkCoinThreshold(sdk, params.by_amount_in, tx, toCoin, totalAmount, toCoinType)
437
+ }
438
+
439
+ return { tx, txRes: coinABs }
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Get lower sqrt price from token A.
445
+ *
446
+ * @param amount - The amount of tokens the user wanted to swap from.
447
+ * @param liquidity - The liquidity of the pool.
448
+ * @param sqrt_price_x64 - The sqrt price of the pool.
449
+ * @returns LowerSqrtPriceX64
450
+ */
451
+ export function getLowerSqrtPriceFromCoinA(amount: BN, liquidity: BN, sqrt_price_x64: BN): BN {
452
+ const numerator = liquidity.mul(sqrt_price_x64).shln(64)
453
+ const denominator = liquidity.shln(64).add(amount.mul(sqrt_price_x64))
454
+
455
+ // always round up
456
+ return MathUtil.divRoundUp(numerator, denominator)
457
+ }
458
+
459
+ /**
460
+ * Get upper sqrt price from token A.
461
+ *
462
+ * @param amount - The amount of tokens the user wanted to swap from.
463
+ * @param liquidity - The liquidity of the pool.
464
+ * @param sqrt_price_x64 - The sqrt price of the pool.
465
+ * @returns LowerSqrtPriceX64
466
+ */
467
+ export function getUpperSqrtPriceFromCoinA(amount: BN, liquidity: BN, sqrt_price_x64: BN): BN {
468
+ const numerator = liquidity.mul(sqrt_price_x64).shln(64)
469
+ const denominator = liquidity.shln(64).sub(amount.mul(sqrt_price_x64))
470
+
471
+ // always round up
472
+ return MathUtil.divRoundUp(numerator, denominator)
473
+ }
474
+
475
+ /**
476
+ * Get lower sqrt price from coin B.
477
+ *
478
+ * @param amount - The amount of coins the user wanted to swap from.
479
+ * @param liquidity - The liquidity of the pool.
480
+ * @param sqrt_price_x64 - The sqrt price of the pool.
481
+ * @returns LowerSqrtPriceX64
482
+ */
483
+ export function getLowerSqrtPriceFromCoinB(amount: BN, liquidity: BN, sqrt_price_x64: BN): BN {
484
+ // always round down(rounding up a negative number)
485
+ return sqrt_price_x64.sub(MathUtil.divRoundUp(amount.shln(64), liquidity))
486
+ }
487
+
488
+ /**
489
+ * Get upper sqrt price from coin B.
490
+ *
491
+ * @param amount - The amount of coins the user wanted to swap from.
492
+ * @param liquidity - The liquidity of the pool.
493
+ * @param sqrtPriceX64 - The sqrt price of the pool.
494
+ * @returns LowerSqrtPriceX64
495
+ */
496
+ export function getUpperSqrtPriceFromCoinB(amount: BN, liquidity: BN, sqrt_price_x64: BN): BN {
497
+ // always round down (rounding up a negative number)
498
+ return sqrt_price_x64.add(amount.shln(64).div(liquidity))
499
+ }
@@ -0,0 +1,121 @@
1
+ import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'
2
+ import BN from 'bn.js'
3
+ import { adjustForCoinSlippage, ClmmPoolUtil, Percentage, printTransaction, TickMath } from '@cetusprotocol/common-sdk'
4
+ import { buildTestAccount } from '@cetusprotocol/test-utils'
5
+ import 'isomorphic-fetch'
6
+ import CetusClmmSDK, { AddLiquidityParams, CustomRangeParams, FullRangeParams } from '../src'
7
+
8
+ let send_key_pair: Ed25519Keypair
9
+
10
+ const poolId = '0xb8d7d9e66a60c239e7a60110efcf8de6c705580ed924d0dde141f4a0e2c90105'
11
+ const position_nft_id = '0xcf995f40b0f9c40a8b03e0b9d9554fea2bc12a18fe63db3a04c59c46be5c10be'
12
+ describe('add Liquidity Module', () => {
13
+ const sdk = CetusClmmSDK.createSDK({ env: 'mainnet' })
14
+
15
+ beforeEach(async () => {
16
+ send_key_pair = buildTestAccount()
17
+ sdk.setSenderAddress(send_key_pair.getPublicKey().toSuiAddress())
18
+ })
19
+
20
+ test('custom tick range add liquidity for input liquidity', async () => {
21
+ const pool = await sdk.Pool.getPool(poolId)
22
+ const position = await sdk.Position.getPositionById(position_nft_id)
23
+ const cur_sqrt_price = new BN(pool.current_sqrt_price)
24
+ const lower_tick = Number(position.tick_lower_index)
25
+ const upper_tick = Number(position.tick_upper_index)
26
+
27
+ const lower_sqrt_price = TickMath.tickIndexToSqrtPriceX64(lower_tick)
28
+ const upper_sqrt_price = TickMath.tickIndexToSqrtPriceX64(upper_tick)
29
+
30
+ const slippage_tolerance = new Percentage(new BN(5), new BN(100))
31
+ const liquidity = 10000
32
+
33
+ const coin_amounts = ClmmPoolUtil.getCoinAmountFromLiquidity(
34
+ new BN(liquidity),
35
+ cur_sqrt_price,
36
+ lower_sqrt_price,
37
+ upper_sqrt_price,
38
+ false
39
+ )
40
+
41
+ const { coin_amount_limit_a, coin_amount_limit_b } = adjustForCoinSlippage(coin_amounts, slippage_tolerance, true)
42
+
43
+ const add_liquidity_payload_params: AddLiquidityParams = {
44
+ coin_type_a: pool.coin_type_a,
45
+ coin_type_b: pool.coin_type_b,
46
+ pool_id: pool.id,
47
+ tick_lower: lower_tick.toString(),
48
+ tick_upper: upper_tick.toString(),
49
+ delta_liquidity: liquidity.toString(),
50
+ max_amount_a: coin_amount_limit_a.toString(),
51
+ max_amount_b: coin_amount_limit_b.toString(),
52
+ pos_id: position.pos_object_id,
53
+ rewarder_coin_types: [],
54
+ collect_fee: false,
55
+ }
56
+
57
+ const payload = await sdk.Position.createAddLiquidityPayload(add_liquidity_payload_params)
58
+
59
+ printTransaction(payload)
60
+
61
+ const transfer_txn = await sdk.FullClient.executeTx(send_key_pair, payload, true)
62
+ console.log('createAddLiquidityPayload: ', transfer_txn)
63
+ })
64
+
65
+ test('custom price range add liquidity for input liquidity ', async () => {
66
+ const params: CustomRangeParams = {
67
+ is_full_range: false,
68
+ min_price: '0.2',
69
+ max_price: '0.7',
70
+ coin_decimals_a: 6,
71
+ coin_decimals_b: 9,
72
+ price_base_coin: 'coin_a',
73
+ }
74
+
75
+ const result = await sdk.Position.calculateAddLiquidityResultWithPrice({
76
+ add_mode_params: params,
77
+ pool_id: poolId,
78
+ liquidity: '10000',
79
+ slippage: 0.01,
80
+ })
81
+
82
+ console.log('result: ', result)
83
+
84
+ const payload = await sdk.Position.addLiquidityWithPricePayload({
85
+ pool_id: poolId,
86
+ calculate_result: result,
87
+ add_mode_params: params,
88
+ })
89
+
90
+ printTransaction(payload)
91
+
92
+ const transfer_txn = await sdk.FullClient.executeTx(send_key_pair, payload, true)
93
+ console.log('createAddLiquidityPayload: ', transfer_txn)
94
+ })
95
+
96
+ test('full range price range add liquidity for input liquidity ', async () => {
97
+ const params: FullRangeParams = {
98
+ is_full_range: true,
99
+ }
100
+
101
+ const result = await sdk.Position.calculateAddLiquidityResultWithPrice({
102
+ add_mode_params: params,
103
+ pool_id: poolId,
104
+ liquidity: '1000000',
105
+ slippage: 0.01,
106
+ })
107
+
108
+ console.log('result: ', result)
109
+
110
+ const payload = await sdk.Position.addLiquidityWithPricePayload({
111
+ pool_id: poolId,
112
+ calculate_result: result,
113
+ add_mode_params: params,
114
+ })
115
+
116
+ printTransaction(payload)
117
+
118
+ const transfer_txn = await sdk.FullClient.executeTx(send_key_pair, payload, false)
119
+ console.log('createAddLiquidityPayload: ', transfer_txn)
120
+ })
121
+ })