@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.
- package/.turbo/turbo-build.log +11100 -0
- package/README.md +108 -0
- package/dist/index.d.mts +2251 -0
- package/dist/index.d.ts +2251 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +13 -0
- package/dist/index.mjs.map +1 -0
- package/docs/add_liquidity.md +145 -0
- package/docs/close_position.md +57 -0
- package/docs/collect_fees.md +37 -0
- package/docs/create_clmm_pool.md +228 -0
- package/docs/error_code.md +69 -0
- package/docs/get_clmm_pools.md +92 -0
- package/docs/get_positions.md +70 -0
- package/docs/get_reward.md +53 -0
- package/docs/get_ticks.md +39 -0
- package/docs/migrate_to_version_6.0.md +143 -0
- package/docs/open_position.md +224 -0
- package/docs/partner_swap.md +60 -0
- package/docs/pre_swap.md +136 -0
- package/docs/remove_liquidity.md +124 -0
- package/docs/swap.md +153 -0
- package/docs/utils.md +85 -0
- package/package.json +37 -0
- package/src/config/index.ts +2 -0
- package/src/config/mainnet.ts +41 -0
- package/src/config/testnet.ts +40 -0
- package/src/errors/errors.ts +93 -0
- package/src/errors/index.ts +1 -0
- package/src/index.ts +10 -0
- package/src/math/apr.ts +167 -0
- package/src/math/index.ts +1 -0
- package/src/modules/configModule.ts +540 -0
- package/src/modules/index.ts +5 -0
- package/src/modules/poolModule.ts +1066 -0
- package/src/modules/positionModule.ts +932 -0
- package/src/modules/rewarderModule.ts +430 -0
- package/src/modules/swapModule.ts +389 -0
- package/src/sdk.ts +131 -0
- package/src/types/clmm_type.ts +1002 -0
- package/src/types/clmmpool.ts +366 -0
- package/src/types/config_type.ts +241 -0
- package/src/types/index.ts +8 -0
- package/src/types/sui.ts +124 -0
- package/src/types/token_type.ts +189 -0
- package/src/utils/common.ts +426 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/positionUtils.ts +434 -0
- package/src/utils/swapUtils.ts +499 -0
- package/tests/add_liquidity.test.ts +121 -0
- package/tests/add_liquidity_fix_token.test.ts +182 -0
- package/tests/apr.test.ts +71 -0
- package/tests/cetus_config.test.ts +26 -0
- package/tests/collect_fees.test.ts +11 -0
- package/tests/pool.test.ts +267 -0
- package/tests/position.test.ts +145 -0
- package/tests/remove_liquidity.test.ts +119 -0
- package/tests/rewarder.test.ts +60 -0
- package/tests/sdk_config.test.ts +49 -0
- package/tests/swap.test.ts +254 -0
- package/tests/tsconfig.json +26 -0
- package/tsconfig.json +5 -0
- 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
|
+
}
|