@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,1066 @@
1
+ import { SuiClient, type DynamicFieldPage, type SuiObjectResponse } from '@mysten/sui/client'
2
+ import type { TransactionObjectArgument } from '@mysten/sui/transactions'
3
+ import { Transaction } from '@mysten/sui/transactions'
4
+ import { normalizeSuiAddress } from '@mysten/sui/utils'
5
+ import BN from 'bn.js'
6
+ import { ConfigErrorCode, handleError, handleMessageError, PartnerErrorCode, PoolErrorCode, PositionErrorCode } from '../errors/errors'
7
+ import type { CetusClmmSDK } from '../sdk'
8
+ import type {
9
+ CalculateCreatePoolResult,
10
+ CalculateCreatePoolWithPriceParams,
11
+ ClmmConfig,
12
+ CreatePoolAddLiquidityParams,
13
+ CreatePoolAddLiquidityWithPriceParams,
14
+ FetchParams,
15
+ Pool,
16
+ PoolImmutables,
17
+ PoolTransactionInfo,
18
+ Position,
19
+ PositionReward,
20
+ TickData,
21
+ } from '../types'
22
+ import { ClmmFetcherModule, ClmmPartnerModule } from '../types/sui'
23
+ import {
24
+ buildPool,
25
+ buildPoolTransactionInfo,
26
+ buildPositionReward,
27
+ buildTickData,
28
+ buildTickDataByEvent,
29
+ buildTransferCoinToSender,
30
+ } from '../utils/common'
31
+
32
+ import type { PageQuery, PaginationArgs, SuiObjectIdType } from '@cetusprotocol/common-sdk'
33
+ import {
34
+ asUintN,
35
+ CACHE_TIME_24H,
36
+ ClmmPoolUtil,
37
+ CLOCK_ADDRESS,
38
+ CoinAsset,
39
+ CoinAssist,
40
+ createFullClient,
41
+ d,
42
+ DataPage,
43
+ DETAILS_KEYS,
44
+ extractStructTagFromType,
45
+ getObjectPreviousTransactionDigest,
46
+ getPackagerConfigs,
47
+ IModule,
48
+ isSortedSymbols,
49
+ TickMath,
50
+ tickScore,
51
+ TickUtil,
52
+ } from '@cetusprotocol/common-sdk'
53
+
54
+ type GetTickParams = {
55
+ start: number[]
56
+ limit: number
57
+ } & FetchParams
58
+
59
+ export type CreatePoolAndAddLiquidityRowResult = {
60
+ pos_id: TransactionObjectArgument
61
+ remain_coin_a: TransactionObjectArgument
62
+ remain_coin_b: TransactionObjectArgument
63
+ tx: Transaction
64
+ remain_coin_type_a: string
65
+ remain_coin_type_b: string
66
+ }
67
+
68
+ /**
69
+ * Helper class to help interact with clmm pools with a pool router interface.
70
+ */
71
+ export class PoolModule implements IModule<CetusClmmSDK> {
72
+ protected _sdk: CetusClmmSDK
73
+
74
+ constructor(sdk: CetusClmmSDK) {
75
+ this._sdk = sdk
76
+ }
77
+
78
+ get sdk() {
79
+ return this._sdk
80
+ }
81
+
82
+ /**
83
+ * Gets a list of positions for the given positionHandle.
84
+ * @param {string} position_handle The handle for the position.
85
+ * @returns {DataPage<Position>} A promise that resolves to an array of Position objects.
86
+ */
87
+ async getPositionList(position_handle: string, pagination_args: PaginationArgs = 'all'): Promise<DataPage<Position>> {
88
+ const dataPage: DataPage<Position> = {
89
+ data: [],
90
+ has_next_page: true,
91
+ }
92
+ const objects = await this._sdk.FullClient.getDynamicFieldsByPage(position_handle, pagination_args)
93
+
94
+ dataPage.has_next_page = objects.has_next_page
95
+ dataPage.next_cursor = objects.next_cursor
96
+
97
+ const positionObjectIDs = objects.data.map((item: any) => {
98
+ if (item.error != null) {
99
+ handleMessageError(
100
+ ConfigErrorCode.InvalidConfig,
101
+ `when getPositionList get position objects error: ${item.error}, please check the rpc, contracts address config and position id.`,
102
+ {
103
+ [DETAILS_KEYS.METHOD_NAME]: 'getPositionList',
104
+ }
105
+ )
106
+ }
107
+
108
+ return item.name.value
109
+ })
110
+
111
+ const allPosition: Position[] = await this._sdk.Position.getSimplePositionList(positionObjectIDs)
112
+ dataPage.data = allPosition
113
+ return dataPage
114
+ }
115
+
116
+ /**
117
+ * Gets a list of pool immutables.
118
+ * @param {PaginationArgs} paginationArgs The cursor and limit to start at.
119
+ * @returns {Promise<DataPage<PoolImmutables>>} Array of PoolImmutable objects.
120
+ */
121
+ async getPoolImmutablesWithPage(pagination_args: PaginationArgs = 'all', force_refresh = false): Promise<DataPage<PoolImmutables>> {
122
+ const { package_id } = this._sdk.sdkOptions.clmm_pool
123
+ const allPools: PoolImmutables[] = []
124
+ const dataPage: DataPage<PoolImmutables> = {
125
+ data: [],
126
+ has_next_page: false,
127
+ }
128
+
129
+ const queryAll = pagination_args === 'all'
130
+ const cacheAllKey = `${package_id}_getPoolImmutables`
131
+ if (queryAll) {
132
+ const cacheDate = this._sdk.getCache<PoolImmutables[]>(cacheAllKey, force_refresh)
133
+ if (cacheDate) {
134
+ allPools.push(...cacheDate)
135
+ }
136
+ }
137
+ if (allPools.length === 0) {
138
+ try {
139
+ const moveEventType = `${package_id}::factory::CreatePoolEvent`
140
+ const objects = await this._sdk.FullClient.queryEventsByPage({ MoveEventType: moveEventType }, pagination_args)
141
+ dataPage.has_next_page = objects.has_next_page
142
+ dataPage.next_cursor = objects.next_cursor
143
+ objects.data.forEach((object: any) => {
144
+ const fields = object.parsedJson
145
+ if (fields) {
146
+ allPools.push({
147
+ id: fields.pool_id,
148
+ tick_spacing: fields.tick_spacing,
149
+ coin_type_a: extractStructTagFromType(fields.coin_type_a).full_address,
150
+ coin_type_b: extractStructTagFromType(fields.coin_type_b).full_address,
151
+ })
152
+ }
153
+ })
154
+ } catch (error) {
155
+ console.log('getPoolImmutables', error)
156
+ }
157
+ }
158
+ dataPage.data = allPools
159
+ if (queryAll) {
160
+ this._sdk.updateCache(`${package_id}_getPoolImmutables`, allPools, CACHE_TIME_24H)
161
+ }
162
+ return dataPage
163
+ }
164
+
165
+ /**
166
+ * Gets a list of pools.
167
+ * @param {PaginationArgs} pagination_args The cursor and limit to start at.
168
+ * @param {boolean} force_refresh Whether to force a refresh of the cache.
169
+ * @returns {Promise<Pool[]>} An array of Pool objects.
170
+ */
171
+ async getPoolsWithPage(pagination_args: PaginationArgs = 'all', force_refresh = false): Promise<DataPage<Pool>> {
172
+ const dataPage: DataPage<Pool> = {
173
+ data: [],
174
+ has_next_page: false,
175
+ }
176
+
177
+ const poolImmutables = await this.getPoolImmutablesWithPage(pagination_args, force_refresh)
178
+
179
+ const objectDataResponses: any[] = await this._sdk.FullClient.batchGetObjects(
180
+ poolImmutables.data.map((item) => item.id),
181
+ {
182
+ showContent: true,
183
+ showType: true,
184
+ }
185
+ )
186
+
187
+ for (const suiObj of objectDataResponses) {
188
+ if (suiObj.error != null || suiObj.data?.content?.dataType !== 'moveObject') {
189
+ handleMessageError(
190
+ PoolErrorCode.InvalidPoolObject,
191
+ `getPoolWithPages error code: ${suiObj.error?.code ?? 'unknown error'}, please check config and object ids`,
192
+ {
193
+ [DETAILS_KEYS.METHOD_NAME]: 'getPoolsWithPage',
194
+ }
195
+ )
196
+ }
197
+ const pool = buildPool(suiObj)
198
+ dataPage.data.push(pool)
199
+ const cacheKey = `${pool.id}_getPoolObject`
200
+ this._sdk.updateCache(cacheKey, pool, CACHE_TIME_24H)
201
+ }
202
+ dataPage.has_next_page = poolImmutables.has_next_page
203
+ dataPage.next_cursor = poolImmutables.next_cursor
204
+ return dataPage
205
+ }
206
+
207
+ /**
208
+ * Gets a list of pools.
209
+ * @param {string[]} assign_pools An array of pool ID to get.
210
+ * @returns {Promise<Pool[]>} array of Pool objects.
211
+ */
212
+ async getAssignPools(assign_pools: string[]): Promise<Pool[]> {
213
+ if (assign_pools.length === 0) {
214
+ return []
215
+ }
216
+ const allPool: Pool[] = []
217
+
218
+ const objectDataResponses = await this._sdk.FullClient.batchGetObjects(assign_pools, {
219
+ showContent: true,
220
+ showType: true,
221
+ })
222
+
223
+ for (const suiObj of objectDataResponses) {
224
+ if (suiObj.error != null || suiObj.data?.content?.dataType !== 'moveObject') {
225
+ handleMessageError(
226
+ PoolErrorCode.InvalidPoolObject,
227
+ `getPools error code: ${suiObj.error?.code ?? 'unknown error'}, please check config and object ids`,
228
+ {
229
+ [DETAILS_KEYS.METHOD_NAME]: 'getAssignPools',
230
+ }
231
+ )
232
+ }
233
+
234
+ const pool = buildPool(suiObj)
235
+ allPool.push(pool)
236
+ const cacheKey = `${pool.id}_getPoolObject`
237
+ this._sdk.updateCache(cacheKey, pool, CACHE_TIME_24H)
238
+ }
239
+ return allPool
240
+ }
241
+
242
+ /**
243
+ * Gets a pool by its object ID.
244
+ * @param {string} pool_id The object ID of the pool to get.
245
+ * @param {true} force_refresh Whether to force a refresh of the cache.
246
+ * @returns {Promise<Pool>} A promise that resolves to a Pool object.
247
+ */
248
+ async getPool(pool_id: string, force_refresh = true): Promise<Pool> {
249
+ const cacheKey = `${pool_id}_getPoolObject`
250
+ const cacheData = this._sdk.getCache<Pool>(cacheKey, force_refresh)
251
+ if (cacheData !== undefined) {
252
+ return cacheData
253
+ }
254
+ const object = (await this._sdk.FullClient.getObject({
255
+ id: pool_id,
256
+ options: {
257
+ showType: true,
258
+ showContent: true,
259
+ },
260
+ })) as SuiObjectResponse
261
+
262
+ if (object.error != null || object.data?.content?.dataType !== 'moveObject') {
263
+ handleMessageError(
264
+ PoolErrorCode.InvalidPoolObject,
265
+ `getPool error code: ${object.error?.code ?? 'unknown error'}, please check config and object id`,
266
+ {
267
+ [DETAILS_KEYS.METHOD_NAME]: 'getPool',
268
+ }
269
+ )
270
+ }
271
+ const pool = buildPool(object)
272
+ this._sdk.updateCache(cacheKey, pool)
273
+ return pool
274
+ }
275
+
276
+ async getPoolByCoins(coins: string[], fee_rate?: number): Promise<Pool[]> {
277
+ if (coins.length === 0) {
278
+ return []
279
+ }
280
+
281
+ // 0x2::sui::SUI -> 0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI
282
+ for (let i = 0; i < coins.length; i++) {
283
+ if (coins[i] === '0x2::sui::SUI') {
284
+ coins[i] = '0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI'
285
+ }
286
+ }
287
+
288
+ let url = this._sdk.sdkOptions.stats_pools_url!
289
+ if (!url) {
290
+ handleMessageError(PoolErrorCode.StatsPoolsUrlNotSet, `statsPoolsUrl is not set in the sdk options.`, {
291
+ [DETAILS_KEYS.METHOD_NAME]: 'getPoolByCoins',
292
+ })
293
+ }
294
+ url += `?order_by=-fees&limit=100&has_mining=true&has_farming=true&no_incentives=true&display_all_pools=true&coin_type=${coins.join(
295
+ ','
296
+ )}`
297
+
298
+ const response = await fetch(url)
299
+ let json
300
+ try {
301
+ json = await response.json()
302
+ } catch (e) {
303
+ handleError(PoolErrorCode.FetchError, e as Error, {
304
+ [DETAILS_KEYS.METHOD_NAME]: 'getPoolByCoins',
305
+ [DETAILS_KEYS.REQUEST_PARAMS]: url,
306
+ })
307
+ }
308
+ const pools = json.data.lp_list
309
+
310
+ const poolAddresses = []
311
+ for (const pool of pools) {
312
+ if (coins.includes(pool.coin_a_address) && coins.includes(pool.coin_b_address)) {
313
+ if (fee_rate != null) {
314
+ if (pool.object.feeRate === fee_rate) {
315
+ poolAddresses.push(pool.address)
316
+ }
317
+ } else {
318
+ poolAddresses.push(pool.address)
319
+ }
320
+ }
321
+ }
322
+
323
+ if (poolAddresses.length > 0) {
324
+ const poolObjects = await this.getAssignPools(poolAddresses)
325
+ return poolObjects
326
+ }
327
+
328
+ return []
329
+ }
330
+
331
+ /**
332
+ * Create a pool of clmmpool protocol. The pool is identified by (CoinTypeA, CoinTypeB, tick_spacing).
333
+ * @param {CreatePoolParams | CreatePoolAddLiquidityParams} params
334
+ * @returns {Promise<Transaction>}
335
+ */
336
+ async createPoolPayload(params: CreatePoolAddLiquidityParams, tx?: Transaction): Promise<Transaction> {
337
+ if (isSortedSymbols(normalizeSuiAddress(params.coin_type_a), normalizeSuiAddress(params.coin_type_b))) {
338
+ const swap_coin_type_b = params.coin_type_b
339
+ params.coin_type_b = params.coin_type_a
340
+ params.coin_type_a = swap_coin_type_b
341
+ const metadata_b = params.metadata_b
342
+ params.metadata_b = params.metadata_a
343
+ params.metadata_a = metadata_b
344
+ }
345
+ return await this.createPoolAndAddLiquidity(params, tx)
346
+ }
347
+
348
+ /**
349
+ * Create pool and add liquidity row. It will call `pool_creator_v2::create_pool_v2` function.
350
+ * This method will return the position, coin_a, coin_b. User can use these to build own transaction.
351
+ * @param {CreatePoolAddLiquidityParams}params The parameters for the create and liquidity.
352
+ * @returns {Promise<CreatePoolAndAddLiquidityRowResult>} A promise that resolves to the transaction payload.
353
+ */
354
+ async createPoolRowPayload(params: CreatePoolAddLiquidityParams, tx?: Transaction): Promise<CreatePoolAndAddLiquidityRowResult> {
355
+ // If the coin types are not sorted, swap them and swap the metadata.
356
+ // You can refer to the documentation for the specific sorting rules. ## How to determine coin_type_a and coin_type_b ?
357
+ // https://cetus-1.gitbook.io/cetus-developer-docs/developer/via-sdk/features-available/create-clmm-pool
358
+ if (isSortedSymbols(normalizeSuiAddress(params.coin_type_a), normalizeSuiAddress(params.coin_type_b))) {
359
+ const swap_coin_type_b = params.coin_type_b
360
+ params.coin_type_b = params.coin_type_a
361
+ params.coin_type_a = swap_coin_type_b
362
+ const metadata_b = params.metadata_b
363
+ params.metadata_b = params.metadata_a
364
+ params.metadata_a = metadata_b
365
+ }
366
+ return await this.createPoolAndAddLiquidityRow(params, tx)
367
+ }
368
+
369
+ /**
370
+ * Gets the ClmmConfig object for the given package object ID.
371
+ * @param {boolean} force_refresh Whether to force a refresh of the cache.
372
+ * @returns the ClmmConfig object.
373
+ */
374
+ async getClmmConfigs(force_refresh = false): Promise<ClmmConfig> {
375
+ const { package_id } = this._sdk.sdkOptions.clmm_pool
376
+ const cacheKey = `${package_id}_getInitEvent`
377
+ const cacheData = this._sdk.getCache<ClmmConfig>(cacheKey, force_refresh)
378
+ if (cacheData !== undefined) {
379
+ return cacheData
380
+ }
381
+ const packageObject = await this._sdk.FullClient.getObject({
382
+ id: package_id,
383
+ options: { showPreviousTransaction: true },
384
+ })
385
+
386
+ const previousTx = getObjectPreviousTransactionDigest(packageObject) as string
387
+
388
+ const objects = (await this._sdk.FullClient.queryEventsByPage({ Transaction: previousTx })).data
389
+
390
+ const clmmConfig: ClmmConfig = {
391
+ pools_id: '',
392
+ global_config_id: '',
393
+ global_vault_id: '',
394
+ admin_cap_id: '',
395
+ }
396
+
397
+ if (objects.length > 0) {
398
+ objects.forEach((item: any) => {
399
+ const fields = item.parsedJson as any
400
+
401
+ if (item.type) {
402
+ switch (extractStructTagFromType(item.type).full_address) {
403
+ case `${package_id}::config::InitConfigEvent`:
404
+ clmmConfig.global_config_id = fields.global_config_id
405
+ clmmConfig.admin_cap_id = fields.admin_cap_id
406
+ break
407
+ case `${package_id}::factory::InitFactoryEvent`:
408
+ clmmConfig.pools_id = fields.pools_id
409
+ break
410
+ case `${package_id}::rewarder::RewarderInitEvent`:
411
+ clmmConfig.global_vault_id = fields.global_vault_id
412
+ break
413
+ case `${package_id}::partner::InitPartnerEvent`:
414
+ clmmConfig.partners_id = fields.partners_id
415
+ break
416
+ default:
417
+ break
418
+ }
419
+ }
420
+ })
421
+ this._sdk.updateCache(cacheKey, clmmConfig, CACHE_TIME_24H)
422
+ return clmmConfig
423
+ }
424
+
425
+ return clmmConfig
426
+ }
427
+
428
+ async getPoolTransactionList({
429
+ pool_id,
430
+ pagination_args,
431
+ order = 'descending',
432
+ full_rpc_url,
433
+ }: {
434
+ pool_id: string
435
+ full_rpc_url?: string
436
+ pagination_args: PageQuery
437
+ order?: 'ascending' | 'descending' | null | undefined
438
+ }): Promise<DataPage<PoolTransactionInfo>> {
439
+ const { FullClient: fullClient, sdkOptions } = this._sdk
440
+ let client
441
+ if (full_rpc_url) {
442
+ client = createFullClient(new SuiClient({ url: full_rpc_url }))
443
+ } else {
444
+ client = fullClient
445
+ }
446
+ const data: DataPage<PoolTransactionInfo> = {
447
+ data: [],
448
+ has_next_page: false,
449
+ }
450
+
451
+ const limit = 50
452
+ const query = pagination_args
453
+ const user_limit = pagination_args.limit || 10
454
+ do {
455
+ const res = await client.queryTransactionBlocksByPage({ ChangedObject: pool_id }, { ...query, limit: 50 }, order)
456
+ res.data.forEach((item, index) => {
457
+ data.next_cursor = res.next_cursor
458
+ const dataList = buildPoolTransactionInfo(item, index, sdkOptions.clmm_pool.package_id, pool_id)
459
+ data.data = [...data.data, ...dataList]
460
+ })
461
+ data.has_next_page = res.has_next_page
462
+ data.next_cursor = res.next_cursor
463
+ query.cursor = res.next_cursor
464
+ } while (data.data.length < user_limit && data.has_next_page)
465
+
466
+ if (data.data.length > user_limit) {
467
+ data.data = data.data.slice(0, user_limit)
468
+ data.next_cursor = data.data[data.data.length - 1].tx
469
+ }
470
+
471
+ return data
472
+ }
473
+
474
+ async calculateCreatePoolWithPrice(params: CalculateCreatePoolWithPriceParams): Promise<CalculateCreatePoolResult> {
475
+ const { current_price, slippage, tick_spacing, add_mode_params, price_base_coin, coin_decimals_a, coin_decimals_b } = params
476
+
477
+ const current_sqrt_price = TickMath.priceToSqrtPriceX64(
478
+ price_base_coin === 'coin_a' ? d(current_price) : d(1).div(current_price),
479
+ coin_decimals_a,
480
+ coin_decimals_b
481
+ )
482
+
483
+ let tick_lower = 0
484
+ let tick_upper = 0
485
+
486
+ if (add_mode_params.is_full_range) {
487
+ tick_lower = TickUtil.getMinIndex(tick_spacing)
488
+ tick_upper = TickUtil.getMaxIndex(tick_spacing)
489
+ } else {
490
+ const { min_price, max_price } = add_mode_params
491
+ tick_lower = TickMath.priceToInitializeTickIndex(
492
+ price_base_coin === 'coin_a' ? d(min_price) : d(1).div(max_price),
493
+ coin_decimals_a,
494
+ coin_decimals_b,
495
+ tick_spacing
496
+ )
497
+ tick_upper = TickMath.priceToInitializeTickIndex(
498
+ price_base_coin === 'coin_a' ? d(max_price) : d(1).div(min_price),
499
+ coin_decimals_a,
500
+ coin_decimals_b,
501
+ tick_spacing
502
+ )
503
+ }
504
+
505
+ const { coin_amount, fix_amount_a } = params
506
+ const { coin_amount_limit_a, coin_amount_limit_b, liquidity_amount, coin_amount_a, coin_amount_b } =
507
+ ClmmPoolUtil.estLiquidityAndCoinAmountFromOneAmounts(
508
+ tick_lower,
509
+ tick_upper,
510
+ new BN(coin_amount),
511
+ fix_amount_a,
512
+ true,
513
+ slippage,
514
+ current_sqrt_price
515
+ )
516
+
517
+ return {
518
+ coin_amount_a,
519
+ coin_amount_b,
520
+ coin_amount_limit_a,
521
+ coin_amount_limit_b,
522
+ liquidity: liquidity_amount,
523
+ initialize_sqrt_price: current_sqrt_price.toString(),
524
+ tick_lower,
525
+ tick_upper,
526
+ fix_amount_a,
527
+ }
528
+ }
529
+
530
+ /**
531
+ * Create pool and add liquidity internal. It will call `pool_creator_v2::create_pool_v2` function in cetus integrate contract.
532
+ * It encapsulates the original create_pool_v2 method from the Cetus CLMM and processes the additional outputs for position and coin within a single move call.
533
+ * @param {CreatePoolAddLiquidityParams}params The parameters for the create and liquidity.
534
+ * @returns {Promise<Transaction>} A promise that resolves to the transaction payload.
535
+ */
536
+ private async createPoolAndAddLiquidity(params: CreatePoolAddLiquidityParams, tx?: Transaction): Promise<Transaction> {
537
+ tx = tx || new Transaction()
538
+ tx.setSender(this.sdk.getSenderAddress())
539
+ const { integrate, clmm_pool } = this.sdk.sdkOptions
540
+ const eventConfig = getPackagerConfigs(clmm_pool)
541
+ const globalPauseStatusObjectId = eventConfig.global_config_id
542
+ const poolsId = eventConfig.pools_id
543
+
544
+ const allCoinAsset = await this._sdk.FullClient.getOwnerCoinAssets(this.sdk.getSenderAddress())
545
+ const primaryCoinAInputsR = CoinAssist.buildCoinForAmount(tx, allCoinAsset, BigInt(params.amount_a), params.coin_type_a, false, true)
546
+ const primaryCoinBInputsR = CoinAssist.buildCoinForAmount(tx, allCoinAsset, BigInt(params.amount_b), params.coin_type_b, false, true)
547
+
548
+ const args = [
549
+ tx.object(globalPauseStatusObjectId),
550
+ tx.object(poolsId),
551
+ tx.pure.u32(params.tick_spacing),
552
+ tx.pure.u128(params.initialize_sqrt_price),
553
+ tx.pure.string(params.uri),
554
+ tx.pure.u32(Number(asUintN(BigInt(params.tick_lower)).toString())),
555
+ tx.pure.u32(Number(asUintN(BigInt(params.tick_upper)).toString())),
556
+ primaryCoinAInputsR.target_coin,
557
+ primaryCoinBInputsR.target_coin,
558
+ tx.object(params.metadata_a),
559
+ tx.object(params.metadata_b),
560
+ tx.pure.bool(params.fix_amount_a),
561
+ tx.object(CLOCK_ADDRESS),
562
+ ]
563
+ tx.moveCall({
564
+ target: `${integrate.published_at}::pool_creator_v2::create_pool_v2`,
565
+ typeArguments: [params.coin_type_a, params.coin_type_b],
566
+ arguments: args,
567
+ })
568
+ buildTransferCoinToSender(this._sdk, tx, primaryCoinAInputsR.target_coin, params.coin_type_a)
569
+ buildTransferCoinToSender(this._sdk, tx, primaryCoinBInputsR.target_coin, params.coin_type_b)
570
+
571
+ return tx
572
+ }
573
+
574
+ /**
575
+ * Create pool and add liquidity row. It will call `pool_creator_v2::create_pool_v2` function.
576
+ * This method will return the position, coin_a, coin_b. User can use these to build own transaction.
577
+ * @param {CreatePoolAddLiquidityParams}params The parameters for the create and liquidity.
578
+ * @returns {Promise<Transaction>} A promise that resolves to the transaction payload.
579
+ */
580
+ private async createPoolAndAddLiquidityRow(
581
+ params: CreatePoolAddLiquidityParams,
582
+ tx?: Transaction
583
+ ): Promise<CreatePoolAndAddLiquidityRowResult> {
584
+ tx = tx || new Transaction()
585
+ const { clmm_pool } = this.sdk.sdkOptions
586
+ const eventConfig = getPackagerConfigs(clmm_pool)
587
+ const globalPauseStatusObjectId = eventConfig.global_config_id
588
+ const poolsId = eventConfig.pools_id
589
+ const allCoinAsset = await this._sdk.FullClient.getOwnerCoinAssets(this.sdk.getSenderAddress())
590
+ const primaryCoinAInputsR = CoinAssist.buildCoinForAmount(tx, allCoinAsset, BigInt(params.amount_a), params.coin_type_a, false, true)
591
+ const primaryCoinBInputsR = CoinAssist.buildCoinForAmount(tx, allCoinAsset, BigInt(params.amount_b), params.coin_type_b, false, true)
592
+
593
+ const args = [
594
+ tx.object(globalPauseStatusObjectId),
595
+ tx.object(poolsId),
596
+ tx.pure.u32(params.tick_spacing),
597
+ tx.pure.u128(params.initialize_sqrt_price),
598
+ tx.pure.string(params.uri),
599
+ tx.pure.u32(Number(asUintN(BigInt(params.tick_lower)).toString())),
600
+ tx.pure.u32(Number(asUintN(BigInt(params.tick_upper)).toString())),
601
+ primaryCoinAInputsR.target_coin,
602
+ primaryCoinBInputsR.target_coin,
603
+ tx.object(params.metadata_a),
604
+ tx.object(params.metadata_b),
605
+ tx.pure.bool(params.fix_amount_a),
606
+ tx.object(CLOCK_ADDRESS),
607
+ ]
608
+ const res: TransactionObjectArgument[] = tx.moveCall({
609
+ target: `${clmm_pool.published_at}::pool_creator::create_pool_v2`,
610
+ typeArguments: [params.coin_type_a, params.coin_type_b],
611
+ arguments: args,
612
+ })
613
+
614
+ return {
615
+ tx,
616
+ pos_id: res[0],
617
+ remain_coin_a: res[1],
618
+ remain_coin_b: res[2],
619
+ remain_coin_type_a: params.coin_type_a,
620
+ remain_coin_type_b: params.coin_type_b,
621
+ }
622
+ }
623
+
624
+ public async createPoolWithPriceReturnPositionPayload(
625
+ params: CreatePoolAddLiquidityWithPriceParams,
626
+ tx?: Transaction
627
+ ): Promise<CreatePoolAndAddLiquidityRowResult> {
628
+ const { coin_type_a, coin_type_b, tick_spacing, uri, calculate_result } = params
629
+ const {
630
+ initialize_sqrt_price,
631
+ tick_lower,
632
+ tick_upper,
633
+ liquidity,
634
+ coin_amount_a,
635
+ coin_amount_b,
636
+ fix_amount_a,
637
+ coin_amount_limit_a,
638
+ coin_amount_limit_b,
639
+ } = calculate_result
640
+
641
+ const coinMetadataA = await this._sdk.FullClient.fetchCoinMetadata(coin_type_a)
642
+ const coinMetadataB = await this._sdk.FullClient.fetchCoinMetadata(coin_type_b)
643
+
644
+ if (coinMetadataA === null) {
645
+ return handleMessageError(PoolErrorCode.FetchError, `fetch coin ${coin_type_a} metadata failed`, {
646
+ [DETAILS_KEYS.METHOD_NAME]: 'createPoolAndAddLiquidityWithPrice',
647
+ [DETAILS_KEYS.REQUEST_PARAMS]: params,
648
+ })
649
+ }
650
+
651
+ if (coinMetadataB === null) {
652
+ return handleMessageError(PoolErrorCode.FetchError, `fetch coin ${coin_type_b} metadata failed`, {
653
+ [DETAILS_KEYS.METHOD_NAME]: 'createPoolAndAddLiquidityWithPrice',
654
+ [DETAILS_KEYS.REQUEST_PARAMS]: params,
655
+ })
656
+ }
657
+
658
+ let metadata_a = coinMetadataA.id!
659
+ let metadata_b = coinMetadataB.id!
660
+
661
+ return this.createPoolRowPayload(
662
+ {
663
+ tick_spacing,
664
+ initialize_sqrt_price,
665
+ uri: uri || '',
666
+ coin_type_a: coin_type_a,
667
+ coin_type_b: coin_type_b,
668
+ amount_a: fix_amount_a ? coin_amount_a : coin_amount_limit_a,
669
+ amount_b: fix_amount_a ? coin_amount_limit_b : coin_amount_b,
670
+ fix_amount_a: fix_amount_a,
671
+ tick_lower: tick_lower,
672
+ tick_upper: tick_upper,
673
+ metadata_a: metadata_a,
674
+ metadata_b: metadata_b,
675
+ },
676
+ tx
677
+ )
678
+ }
679
+
680
+ public async createPoolWithPricePayload(params: CreatePoolAddLiquidityWithPriceParams): Promise<Transaction> {
681
+ const { coin_type_a, coin_type_b, tick_spacing, uri, calculate_result } = params
682
+ const {
683
+ initialize_sqrt_price,
684
+ tick_lower,
685
+ tick_upper,
686
+ liquidity,
687
+ coin_amount_a,
688
+ coin_amount_b,
689
+ fix_amount_a,
690
+ coin_amount_limit_a,
691
+ coin_amount_limit_b,
692
+ } = calculate_result
693
+
694
+ const coinMetadataA = await this._sdk.FullClient.fetchCoinMetadata(coin_type_a)
695
+ const coinMetadataB = await this._sdk.FullClient.fetchCoinMetadata(coin_type_b)
696
+
697
+ if (coinMetadataA === null) {
698
+ return handleMessageError(PoolErrorCode.FetchError, `fetch coin ${coin_type_a} metadata failed`, {
699
+ [DETAILS_KEYS.METHOD_NAME]: 'createPoolAndAddLiquidityWithPrice',
700
+ [DETAILS_KEYS.REQUEST_PARAMS]: params,
701
+ })
702
+ }
703
+
704
+ if (coinMetadataB === null) {
705
+ return handleMessageError(PoolErrorCode.FetchError, `fetch coin ${coin_type_b} metadata failed`, {
706
+ [DETAILS_KEYS.METHOD_NAME]: 'createPoolAndAddLiquidityWithPrice',
707
+ [DETAILS_KEYS.REQUEST_PARAMS]: params,
708
+ })
709
+ }
710
+
711
+ let metadata_a = coinMetadataA.id!
712
+ let metadata_b = coinMetadataB.id!
713
+
714
+ return this.createPoolPayload({
715
+ tick_spacing,
716
+ initialize_sqrt_price,
717
+ uri: uri || '',
718
+ coin_type_a: coin_type_a,
719
+ coin_type_b: coin_type_b,
720
+ amount_a: fix_amount_a ? coin_amount_a : coin_amount_limit_a,
721
+ amount_b: fix_amount_a ? coin_amount_limit_b : coin_amount_b,
722
+ fix_amount_a: fix_amount_a,
723
+ tick_lower: tick_lower,
724
+ tick_upper: tick_upper,
725
+ metadata_a: metadata_a,
726
+ metadata_b: metadata_b,
727
+ })
728
+ }
729
+
730
+ /**
731
+ * Fetches ticks from the exchange.
732
+ * @param {FetchParams} params The parameters for the fetch.
733
+ * @returns {Promise<TickData[]>} A promise that resolves to an array of tick data.
734
+ */
735
+ async fetchTicks(params: FetchParams): Promise<TickData[]> {
736
+ let ticks: TickData[] = []
737
+ let start: number[] = []
738
+ const limit = 512
739
+
740
+ while (true) {
741
+ const data = await this.getTicks({
742
+ pool_id: params.pool_id,
743
+ coin_type_a: params.coin_type_a,
744
+ coin_type_b: params.coin_type_b,
745
+ start,
746
+ limit,
747
+ })
748
+ ticks = [...ticks, ...data]
749
+ if (data.length < limit) {
750
+ break
751
+ }
752
+ start = [Number(asUintN(BigInt(data[data.length - 1].index)))]
753
+ }
754
+ return ticks
755
+ }
756
+
757
+ /**
758
+ * Fetches ticks from the exchange using the simulation exec tx.
759
+ * @param {GetTickParams} params The parameters for the fetch.
760
+ * @returns {Promise<TickData[]>} A promise that resolves to an array of tick data.
761
+ */
762
+ private async getTicks(params: GetTickParams): Promise<TickData[]> {
763
+ const { integrate } = this.sdk.sdkOptions
764
+ const ticks: TickData[] = []
765
+ const typeArguments = [params.coin_type_a, params.coin_type_b]
766
+
767
+ const tx = new Transaction()
768
+
769
+ const start = tx.makeMoveVec({
770
+ elements: params.start.map((index) => tx.pure.u32(index)),
771
+ type: 'u32',
772
+ })
773
+
774
+ const args = [tx.object(params.pool_id), start, tx.pure.u64(params.limit.toString())]
775
+
776
+ tx.moveCall({
777
+ target: `${integrate.published_at}::${ClmmFetcherModule}::fetch_ticks`,
778
+ arguments: args,
779
+ typeArguments,
780
+ })
781
+
782
+ const simulateRes = await this.sdk.FullClient.devInspectTransactionBlock({
783
+ transactionBlock: tx,
784
+ sender: normalizeSuiAddress('0x0'),
785
+ })
786
+
787
+ if (simulateRes.error != null) {
788
+ handleMessageError(
789
+ PoolErrorCode.InvalidTickObjectId,
790
+ `getTicks error code: ${simulateRes.error ?? 'unknown error'}, please check config and tick object ids`,
791
+ {
792
+ [DETAILS_KEYS.METHOD_NAME]: 'getTicks',
793
+ [DETAILS_KEYS.REQUEST_PARAMS]: params,
794
+ }
795
+ )
796
+ }
797
+
798
+ simulateRes.events?.forEach((item: any) => {
799
+ if (extractStructTagFromType(item.type).name === `FetchTicksResultEvent`) {
800
+ item.parsedJson.ticks.forEach((tick: any) => {
801
+ ticks.push(buildTickDataByEvent(tick))
802
+ })
803
+ }
804
+ })
805
+ return ticks
806
+ }
807
+
808
+ /**
809
+ * Fetches a list of position rewards from the exchange.
810
+ * @param {FetchParams} params The parameters for the fetch.
811
+ * @returns {Promise<PositionReward[]>} A promise that resolves to an array of position rewards.
812
+ */
813
+ async fetchPositionRewardList(params: FetchParams): Promise<PositionReward[]> {
814
+ const { integrate } = this.sdk.sdkOptions
815
+ const allPosition: PositionReward[] = []
816
+ let start: SuiObjectIdType[] = []
817
+ const limit = 512
818
+
819
+ while (true) {
820
+ const typeArguments = [params.coin_type_a, params.coin_type_b]
821
+
822
+ const tx = new Transaction()
823
+
824
+ const vecStart = tx.pure.vector(
825
+ 'id',
826
+ start.map((id) => id)
827
+ )
828
+
829
+ const args = [tx.object(params.pool_id), vecStart, tx.pure.u64(limit)]
830
+
831
+ tx.moveCall({
832
+ target: `${integrate.published_at}::${ClmmFetcherModule}::fetch_positions`,
833
+ arguments: args,
834
+ typeArguments,
835
+ })
836
+
837
+ const simulateRes = await this.sdk.FullClient.devInspectTransactionBlock({
838
+ transactionBlock: tx,
839
+ sender: normalizeSuiAddress('0x0'),
840
+ })
841
+
842
+ if (simulateRes.error != null) {
843
+ handleMessageError(
844
+ PositionErrorCode.InvalidPositionRewardObject,
845
+ `fetch position reward error code: ${simulateRes.error ?? 'unknown error'}, please check config and tick object ids`,
846
+ {
847
+ [DETAILS_KEYS.METHOD_NAME]: 'fetchPositionRewardList',
848
+ [DETAILS_KEYS.REQUEST_PARAMS]: params,
849
+ }
850
+ )
851
+ }
852
+
853
+ const positionRewards: PositionReward[] = []
854
+ simulateRes?.events?.forEach((item: any) => {
855
+ if (extractStructTagFromType(item.type).name === `FetchPositionsEvent`) {
856
+ item.parsedJson.positions.forEach((item: any) => {
857
+ const positionReward = buildPositionReward(item)
858
+ positionRewards.push(positionReward)
859
+ })
860
+ }
861
+ })
862
+
863
+ allPosition.push(...positionRewards)
864
+
865
+ if (positionRewards.length < limit) {
866
+ break
867
+ } else {
868
+ start = [positionRewards[positionRewards.length - 1].pos_object_id]
869
+ }
870
+ }
871
+
872
+ return allPosition
873
+ }
874
+
875
+ /**
876
+ * Fetches ticks from the fullnode using the RPC API.
877
+ * @param {string} tick_handle The handle for the tick. Get tick handle from `sdk.Pool.getPool()`
878
+ * @returns {Promise<TickData[]>} A promise that resolves to an array of tick data.
879
+ */
880
+ async fetchTicksByRpc(tick_handle: string): Promise<TickData[]> {
881
+ let allTickData: TickData[] = []
882
+ let nextCursor: string | null = null
883
+ const limit = 50
884
+ while (true) {
885
+ const allTickId: SuiObjectIdType[] = []
886
+ const idRes: DynamicFieldPage = await this.sdk.FullClient.getDynamicFields({
887
+ parentId: tick_handle,
888
+ cursor: nextCursor,
889
+ limit,
890
+ })
891
+ nextCursor = idRes.nextCursor
892
+ idRes.data.forEach((item) => {
893
+ if (extractStructTagFromType(item.objectType).module === 'skip_list') {
894
+ allTickId.push(item.objectId)
895
+ }
896
+ })
897
+
898
+ allTickData = [...allTickData, ...(await this.getTicksByRpc(allTickId))]
899
+
900
+ if (!idRes.hasNextPage) {
901
+ break
902
+ }
903
+ }
904
+
905
+ return allTickData
906
+ }
907
+
908
+ /**
909
+ * Get ticks by tick object ids.
910
+ * @param {string} tick_object_id The object ids of the ticks.
911
+ * @returns {Promise<TickData[]>} A promise that resolves to an array of tick data.
912
+ */
913
+ private async getTicksByRpc(tick_object_id: string[]): Promise<TickData[]> {
914
+ const ticks: TickData[] = []
915
+ const objectDataResponses = await this.sdk.FullClient.batchGetObjects(tick_object_id, { showContent: true, showType: true })
916
+ for (const suiObj of objectDataResponses) {
917
+ if (suiObj.error != null || suiObj.data?.content?.dataType !== 'moveObject') {
918
+ handleMessageError(
919
+ PoolErrorCode.InvalidTickObjectId,
920
+ `getTicksByRpc error code: ${suiObj.error?.code ?? 'unknown error'}, please check config and tick object ids`,
921
+ {
922
+ [DETAILS_KEYS.METHOD_NAME]: 'getTicksByRpc',
923
+ }
924
+ )
925
+ }
926
+
927
+ const tick = buildTickData(suiObj)
928
+ if (tick != null) {
929
+ ticks.push(tick)
930
+ }
931
+ }
932
+ return ticks
933
+ }
934
+
935
+ /**
936
+ * Gets the tick data for the given tick index.
937
+ * @param {string} tick_handle The handle for the tick.
938
+ * @param {number} tick_index The index of the tick.
939
+ * @returns {Promise<TickData | null>} A promise that resolves to the tick data.
940
+ */
941
+ async getTickDataByIndex(tick_handle: string, tick_index: number): Promise<TickData> {
942
+ const name = { type: 'u64', value: asUintN(BigInt(tickScore(tick_index).toString())).toString() }
943
+ const res = await this.sdk.FullClient.getDynamicFieldObject({
944
+ parentId: tick_handle,
945
+ name,
946
+ })
947
+
948
+ if (res.error != null || res.data?.content?.dataType !== 'moveObject') {
949
+ handleMessageError(PoolErrorCode.InvalidTickIndex, `get tick by index: ${tick_index} error: ${res.error}`, {
950
+ [DETAILS_KEYS.METHOD_NAME]: 'getTickDataByIndex',
951
+ })
952
+ }
953
+
954
+ return buildTickData(res)
955
+ }
956
+
957
+ /**
958
+ * Gets the tick data for the given object ID.
959
+ * @param {string} tick_id The object ID of the tick.
960
+ * @returns {Promise<TickData | null>} A promise that resolves to the tick data.
961
+ */
962
+ async getTickDataByObjectId(tick_id: string): Promise<TickData | null> {
963
+ const res = await this.sdk.FullClient.getObject({
964
+ id: tick_id,
965
+ options: { showContent: true },
966
+ })
967
+
968
+ if (res.error != null || res.data?.content?.dataType !== 'moveObject') {
969
+ handleMessageError(
970
+ PoolErrorCode.InvalidTickObjectId,
971
+ `getTicksByRpc error code: ${res.error?.code ?? 'unknown error'}, please check config and tick object ids`,
972
+ {
973
+ [DETAILS_KEYS.METHOD_NAME]: 'getTickDataByObjectId',
974
+ }
975
+ )
976
+ }
977
+ return buildTickData(res)
978
+ }
979
+
980
+ /**
981
+ * Get partner ref fee amount
982
+ * @param {string}partner Partner object id
983
+ * @returns {Promise<CoinAsset[]>} A promise that resolves to an array of coin asset.
984
+ */
985
+ async getPartnerRefFeeAmount(partner: string, show_display = true): Promise<CoinAsset[]> {
986
+ const objectDataResponses: any = await this._sdk.FullClient.batchGetObjects([partner], {
987
+ showOwner: true,
988
+ showContent: true,
989
+ showDisplay: show_display,
990
+ showType: true,
991
+ })
992
+
993
+ if (objectDataResponses[0].data?.content?.dataType !== 'moveObject') {
994
+ handleMessageError(
995
+ PartnerErrorCode.NotFoundPartnerObject,
996
+ `get partner by object id: ${partner} error: ${objectDataResponses[0].error}`,
997
+ {
998
+ [DETAILS_KEYS.METHOD_NAME]: 'getPartnerRefFeeAmount',
999
+ }
1000
+ )
1001
+ }
1002
+
1003
+ const balance = (objectDataResponses[0].data.content.fields as any).balances
1004
+
1005
+ const objects = await this._sdk.FullClient.getDynamicFieldsByPage(balance.fields.id.id)
1006
+
1007
+ const coins: string[] = []
1008
+ objects.data.forEach((object) => {
1009
+ if (object.objectId != null) {
1010
+ coins.push(object.objectId)
1011
+ }
1012
+ })
1013
+
1014
+ const refFee: CoinAsset[] = []
1015
+ const object = await this._sdk.FullClient.batchGetObjects(coins, {
1016
+ showOwner: true,
1017
+ showContent: true,
1018
+ showDisplay: show_display,
1019
+ showType: true,
1020
+ })
1021
+ object.forEach((info: any) => {
1022
+ if (info.error != null || info.data?.content?.dataType !== 'moveObject') {
1023
+ handleMessageError(
1024
+ PartnerErrorCode.InvalidPartnerRefFeeFields,
1025
+ `get coin by object id: ${info.data.objectId} error: ${info.error}`,
1026
+ {
1027
+ [DETAILS_KEYS.METHOD_NAME]: 'getPartnerRefFeeAmount',
1028
+ }
1029
+ )
1030
+ }
1031
+
1032
+ const coinAsset: CoinAsset = {
1033
+ coin_type: info.data.content.fields.name,
1034
+ coin_object_id: info.data.objectId,
1035
+ balance: BigInt(info.data.content.fields.value),
1036
+ }
1037
+ refFee.push(coinAsset)
1038
+ })
1039
+
1040
+ return refFee
1041
+ }
1042
+
1043
+ /**
1044
+ * Claim partner ref fee.
1045
+ * @param {string} partner_cap partner cap id.
1046
+ * @param {string} partner partner id.
1047
+ * @param {string} coin_type coin type.
1048
+ * @returns {Promise<Transaction>} A promise that resolves to the transaction payload.
1049
+ */
1050
+ async claimPartnerRefFeePayload(partner_cap: string, partner: string, coin_type: string): Promise<Transaction> {
1051
+ const tx = new Transaction()
1052
+ const { clmm_pool } = this.sdk.sdkOptions
1053
+ const { global_config_id } = getPackagerConfigs(clmm_pool)
1054
+ const typeArguments = [coin_type]
1055
+
1056
+ const args = [tx.object(global_config_id), tx.object(partner_cap), tx.object(partner)]
1057
+
1058
+ tx.moveCall({
1059
+ target: `${clmm_pool.published_at}::${ClmmPartnerModule}::claim_ref_fee`,
1060
+ arguments: args,
1061
+ typeArguments,
1062
+ })
1063
+
1064
+ return tx
1065
+ }
1066
+ }