@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,932 @@
|
|
|
1
|
+
import { DevInspectResults, SuiClient } from '@mysten/sui/client'
|
|
2
|
+
import type { TransactionObjectArgument } from '@mysten/sui/transactions'
|
|
3
|
+
import { Transaction } from '@mysten/sui/transactions'
|
|
4
|
+
import { isValidSuiObjectId, normalizeSuiAddress } from '@mysten/sui/utils'
|
|
5
|
+
import BN from 'bn.js'
|
|
6
|
+
import type { BuildCoinResult, DataPage, PaginationArgs, SuiObjectIdType } from '@cetusprotocol/common-sdk'
|
|
7
|
+
import {
|
|
8
|
+
asUintN,
|
|
9
|
+
ClmmPoolUtil,
|
|
10
|
+
CLOCK_ADDRESS,
|
|
11
|
+
CoinAssist,
|
|
12
|
+
d,
|
|
13
|
+
DETAILS_KEYS,
|
|
14
|
+
extractStructTagFromType,
|
|
15
|
+
getObjectFields,
|
|
16
|
+
getPackagerConfigs,
|
|
17
|
+
IModule,
|
|
18
|
+
FullClient,
|
|
19
|
+
TickMath,
|
|
20
|
+
TickUtil,
|
|
21
|
+
createFullClient,
|
|
22
|
+
} from '@cetusprotocol/common-sdk'
|
|
23
|
+
import Decimal from 'decimal.js'
|
|
24
|
+
import { handleError, handleMessageError, PoolErrorCode, PositionErrorCode } from '../errors/errors'
|
|
25
|
+
import type { CetusClmmSDK } from '../sdk'
|
|
26
|
+
import type {
|
|
27
|
+
AddLiquidityFixTokenParams,
|
|
28
|
+
AddLiquidityParams,
|
|
29
|
+
AddLiquidityWithPriceRangeParams,
|
|
30
|
+
CalculateAddLiquidityFixCoinWithPriceParams,
|
|
31
|
+
CalculateAddLiquidityResult,
|
|
32
|
+
CalculateAddLiquidityWithPriceParams,
|
|
33
|
+
ClosePositionParams,
|
|
34
|
+
CollectFeeParams,
|
|
35
|
+
CollectFeesQuote,
|
|
36
|
+
FetchPosFeeParams,
|
|
37
|
+
OpenPositionParams,
|
|
38
|
+
OpenPositionWithPriceParams,
|
|
39
|
+
Position,
|
|
40
|
+
PositionReward,
|
|
41
|
+
PositionTransactionInfo,
|
|
42
|
+
RemoveLiquidityParams,
|
|
43
|
+
} from '../types'
|
|
44
|
+
import { ClmmFetcherModule, ClmmIntegratePoolModule, ClmmIntegratePoolV2Module, ClmmIntegratePoolV3Module } from '../types/sui'
|
|
45
|
+
import { buildPosition, buildPositionReward, buildPositionTransactionInfo } from '../utils'
|
|
46
|
+
import { findAdjustCoin, PositionUtils } from '../utils/positionUtils'
|
|
47
|
+
/**
|
|
48
|
+
* Helper class to help interact with clmm position with a position router interface.
|
|
49
|
+
*/
|
|
50
|
+
export class PositionModule implements IModule<CetusClmmSDK> {
|
|
51
|
+
protected _sdk: CetusClmmSDK
|
|
52
|
+
|
|
53
|
+
constructor(sdk: CetusClmmSDK) {
|
|
54
|
+
this._sdk = sdk
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get sdk() {
|
|
58
|
+
return this._sdk
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Builds the full address of the Position type.
|
|
63
|
+
* @returns The full address of the Position type.
|
|
64
|
+
*/
|
|
65
|
+
buildPositionType() {
|
|
66
|
+
const cetusClmm = this._sdk.sdkOptions.clmm_pool.package_id
|
|
67
|
+
return `${cetusClmm}::position::Position`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Gets a list of position transaction information for the given position ID.
|
|
72
|
+
* @param {Object} params - The parameters for the position transaction list.
|
|
73
|
+
* @param {string} params.pos_id - The ID of the position to get transactions for.
|
|
74
|
+
* @param {PaginationArgs} [params.pagination_args] - The pagination arguments for the transaction list.
|
|
75
|
+
* @param {string} [params.order] - The order of the transaction list.
|
|
76
|
+
* @param {string} [params.full_rpc_url] - The full RPC URL for the transaction list.
|
|
77
|
+
* @param {string} [params.origin_pos_id] - The origin position ID for the transaction list.
|
|
78
|
+
* @returns {Promise<DataPage<PositionTransactionInfo>>} A promise that resolves to a DataPage object containing the position transaction information.
|
|
79
|
+
*/
|
|
80
|
+
async getPositionTransactionList({
|
|
81
|
+
pos_id,
|
|
82
|
+
origin_pos_id,
|
|
83
|
+
full_rpc_url,
|
|
84
|
+
pagination_args = 'all',
|
|
85
|
+
order = 'ascending',
|
|
86
|
+
}: {
|
|
87
|
+
pos_id: string
|
|
88
|
+
origin_pos_id?: string
|
|
89
|
+
full_rpc_url?: string
|
|
90
|
+
pagination_args?: PaginationArgs
|
|
91
|
+
order?: 'ascending' | 'descending' | null | undefined
|
|
92
|
+
}): Promise<DataPage<PositionTransactionInfo>> {
|
|
93
|
+
const { FullClient: fullClient } = this._sdk
|
|
94
|
+
const filterIds: string[] = [pos_id]
|
|
95
|
+
if (origin_pos_id) {
|
|
96
|
+
filterIds.push(origin_pos_id)
|
|
97
|
+
}
|
|
98
|
+
let client
|
|
99
|
+
if (full_rpc_url) {
|
|
100
|
+
client = createFullClient(new SuiClient({ url: full_rpc_url }))
|
|
101
|
+
} else {
|
|
102
|
+
client = fullClient
|
|
103
|
+
}
|
|
104
|
+
const data: DataPage<PositionTransactionInfo> = {
|
|
105
|
+
data: [],
|
|
106
|
+
has_next_page: false,
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const res = await client.queryTransactionBlocksByPage({ ChangedObject: pos_id }, pagination_args, order)
|
|
110
|
+
|
|
111
|
+
res.data.forEach((item, index) => {
|
|
112
|
+
const dataList = buildPositionTransactionInfo(item, index, filterIds)
|
|
113
|
+
data.data = [...data.data, ...dataList]
|
|
114
|
+
})
|
|
115
|
+
data.has_next_page = res.has_next_page
|
|
116
|
+
data.next_cursor = res.next_cursor
|
|
117
|
+
return data
|
|
118
|
+
} catch (error) {
|
|
119
|
+
handleError(PoolErrorCode.FetchError, error as Error, {
|
|
120
|
+
[DETAILS_KEYS.METHOD_NAME]: 'getPositionTransactionList',
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return data
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Gets a list of positions for the given account address.
|
|
129
|
+
* @param account_address The account address to get positions for.
|
|
130
|
+
* @param assign_pool_ids An array of pool ID to filter the positions by.
|
|
131
|
+
* @returns array of Position objects.
|
|
132
|
+
*/
|
|
133
|
+
async getPositionList(account_address: string, assign_pool_ids: string[] = [], show_display = true): Promise<Position[]> {
|
|
134
|
+
const all_position: Position[] = []
|
|
135
|
+
|
|
136
|
+
const owner_res: any = await this._sdk.FullClient.getOwnedObjectsByPage(account_address, {
|
|
137
|
+
options: { showType: true, showContent: true, showDisplay: show_display, showOwner: true },
|
|
138
|
+
filter: { Package: this._sdk.sdkOptions.clmm_pool.package_id },
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
const has_assign_pool_ids = assign_pool_ids.length > 0
|
|
142
|
+
for (const item of owner_res.data as any[]) {
|
|
143
|
+
const type = extractStructTagFromType(item.data.type)
|
|
144
|
+
|
|
145
|
+
if (type.full_address === this.buildPositionType()) {
|
|
146
|
+
const position = buildPosition(item)
|
|
147
|
+
const cache_key = `${position.pos_object_id}_getPositionList`
|
|
148
|
+
this._sdk.updateCache(cache_key, position)
|
|
149
|
+
if (has_assign_pool_ids) {
|
|
150
|
+
if (assign_pool_ids.includes(position.pool)) {
|
|
151
|
+
all_position.push(position)
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
all_position.push(position)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return all_position
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Gets a position by its handle and ID. But it needs pool info, so it is not recommended to use this method.
|
|
164
|
+
* if you want to get a position, you can use getPositionById method directly.
|
|
165
|
+
* @param {string} position_handle The handle of the position to get.
|
|
166
|
+
* @param {string} position_id The ID of the position to get.
|
|
167
|
+
* @param {boolean} calculate_rewarder Whether to calculate the rewarder of the position.
|
|
168
|
+
* @returns {Promise<Position>} Position object.
|
|
169
|
+
*/
|
|
170
|
+
async getPosition(position_handle: string, position_id: string, calculate_rewarder = true, show_display = true): Promise<Position> {
|
|
171
|
+
let position = await this.getSimplePosition(position_id, show_display)
|
|
172
|
+
if (calculate_rewarder) {
|
|
173
|
+
position = await this.updatePositionRewarders(position_handle, position)
|
|
174
|
+
}
|
|
175
|
+
return position
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Gets a position by its ID.
|
|
180
|
+
* @param {string} position_id The ID of the position to get.
|
|
181
|
+
* @param {boolean} calculate_rewarder Whether to calculate the rewarder of the position.
|
|
182
|
+
* @param {boolean} show_display When some testnet rpc nodes can't return object's display data, you can set this option to false to avoid returning errors. Default is true.
|
|
183
|
+
* @returns {Promise<Position>} Position object.
|
|
184
|
+
*/
|
|
185
|
+
async getPositionById(position_id: string, calculate_rewarder = true, show_display = true): Promise<Position> {
|
|
186
|
+
const position = await this.getSimplePosition(position_id, show_display)
|
|
187
|
+
if (calculate_rewarder) {
|
|
188
|
+
const pool = await this._sdk.Pool.getPool(position.pool, false)
|
|
189
|
+
const result = await this.updatePositionRewarders(pool.position_manager.positions_handle, position)
|
|
190
|
+
return result
|
|
191
|
+
}
|
|
192
|
+
return position
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Gets a simple position for the given position ID.
|
|
197
|
+
* @param {string} position_id The ID of the position to get.
|
|
198
|
+
* @returns {Promise<Position>} Position object.
|
|
199
|
+
*/
|
|
200
|
+
async getSimplePosition(position_id: string, show_display = true): Promise<Position> {
|
|
201
|
+
const cache_key = `${position_id}_getPositionList`
|
|
202
|
+
|
|
203
|
+
let position = this.getSimplePositionByCache(position_id)
|
|
204
|
+
|
|
205
|
+
if (position === undefined) {
|
|
206
|
+
const object_data_responses = await this.sdk.FullClient.getObject({
|
|
207
|
+
id: position_id,
|
|
208
|
+
options: { showContent: true, showType: true, showDisplay: show_display, showOwner: true },
|
|
209
|
+
})
|
|
210
|
+
position = buildPosition(object_data_responses)
|
|
211
|
+
|
|
212
|
+
this._sdk.updateCache(cache_key, position)
|
|
213
|
+
}
|
|
214
|
+
return position
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Gets a simple position for the given position ID.
|
|
219
|
+
* @param {string} position_id Position object id
|
|
220
|
+
* @returns {Position | undefined} Position object
|
|
221
|
+
*/
|
|
222
|
+
private getSimplePositionByCache(position_id: string): Position | undefined {
|
|
223
|
+
const cache_key = `${position_id}_getPositionList`
|
|
224
|
+
return this._sdk.getCache<Position>(cache_key)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Gets a list of simple positions for the given position ID.
|
|
229
|
+
* @param {SuiObjectIdType[]} position_ids The IDs of the positions to get.
|
|
230
|
+
* @returns {Promise<Position[]>} A promise that resolves to an array of Position objects.
|
|
231
|
+
*/
|
|
232
|
+
async getSimplePositionList(position_ids: SuiObjectIdType[], show_display = true): Promise<Position[]> {
|
|
233
|
+
const position_list: Position[] = []
|
|
234
|
+
const not_found_ids: SuiObjectIdType[] = []
|
|
235
|
+
|
|
236
|
+
position_ids.forEach((id) => {
|
|
237
|
+
const position = this.getSimplePositionByCache(id)
|
|
238
|
+
if (position) {
|
|
239
|
+
position_list.push(position)
|
|
240
|
+
} else {
|
|
241
|
+
not_found_ids.push(id)
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
if (not_found_ids.length > 0) {
|
|
246
|
+
const object_data_responses = await this._sdk.FullClient.batchGetObjects(not_found_ids, {
|
|
247
|
+
showOwner: true,
|
|
248
|
+
showContent: true,
|
|
249
|
+
showDisplay: show_display,
|
|
250
|
+
showType: true,
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
object_data_responses.forEach((info) => {
|
|
254
|
+
if (info.error == null) {
|
|
255
|
+
const position = buildPosition(info)
|
|
256
|
+
position_list.push(position)
|
|
257
|
+
const cache_key = `${position.pos_object_id}_getPositionList`
|
|
258
|
+
this._sdk.updateCache(cache_key, position)
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return position_list
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Updates the rewarders of position
|
|
268
|
+
* @param {string} position_handle Position handle
|
|
269
|
+
* @param {Position} position Position object
|
|
270
|
+
* @returns {Promise<Position>} A promise that resolves to an array of Position objects.
|
|
271
|
+
*/
|
|
272
|
+
private async updatePositionRewarders(position_handle: string, position: Position): Promise<Position> {
|
|
273
|
+
const position_reward = await this.getPositionRewarders(position_handle, position.pos_object_id)
|
|
274
|
+
return {
|
|
275
|
+
...position,
|
|
276
|
+
...position_reward,
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Gets the position rewarders for the given position handle and position object ID.
|
|
282
|
+
* @param {string} position_handle The handle of the position.
|
|
283
|
+
* @param {string} position_id The ID of the position object.
|
|
284
|
+
* @returns {Promise<PositionReward | undefined>} PositionReward object.
|
|
285
|
+
*/
|
|
286
|
+
async getPositionRewarders(position_handle: string, position_id: string): Promise<PositionReward | undefined> {
|
|
287
|
+
try {
|
|
288
|
+
const dynamic_field_object = await this._sdk.FullClient.getDynamicFieldObject({
|
|
289
|
+
parentId: position_handle,
|
|
290
|
+
name: {
|
|
291
|
+
type: '0x2::object::ID',
|
|
292
|
+
value: position_id,
|
|
293
|
+
},
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
const object_fields = getObjectFields(dynamic_field_object.data as any) as any
|
|
297
|
+
const fields = object_fields.value.fields.value
|
|
298
|
+
const position_reward = buildPositionReward(fields)
|
|
299
|
+
return position_reward
|
|
300
|
+
} catch (error) {
|
|
301
|
+
console.log(error)
|
|
302
|
+
return undefined
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
public buildFetchPosFee(params: FetchPosFeeParams, tx: Transaction) {
|
|
307
|
+
const { clmm_pool, integrate } = this.sdk.sdkOptions
|
|
308
|
+
const typeArguments = [params.coin_type_a, params.coin_type_b]
|
|
309
|
+
const args = [tx.object(getPackagerConfigs(clmm_pool).global_config_id), tx.object(params.pool_id), tx.pure.address(params.position_id)]
|
|
310
|
+
tx.moveCall({
|
|
311
|
+
target: `${integrate.published_at}::${ClmmFetcherModule}::fetch_position_fees`,
|
|
312
|
+
arguments: args,
|
|
313
|
+
typeArguments,
|
|
314
|
+
})
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
parsedPosFeeData(simulate_res: DevInspectResults) {
|
|
318
|
+
const feeData: Record<string, { position_id: string; fee_owned_a: string; fee_owned_b: string }> = {}
|
|
319
|
+
const feeValueData: any[] = simulate_res.events?.filter((item: any) => {
|
|
320
|
+
return item.type.includes('fetcher_script::FetchPositionFeesEvent')
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
for (let i = 0; i < feeValueData.length; i += 1) {
|
|
324
|
+
const { parsedJson } = feeValueData[i]
|
|
325
|
+
const posObj = {
|
|
326
|
+
position_id: parsedJson.position_id,
|
|
327
|
+
fee_owned_a: parsedJson.fee_owned_a,
|
|
328
|
+
fee_owned_b: parsedJson.fee_owned_b,
|
|
329
|
+
}
|
|
330
|
+
feeData[parsedJson.position_id] = posObj
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return feeData
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Fetches the Position fee amount for a given list of addresses.
|
|
338
|
+
* @param {FetchPosFeeParams[]} params An array of FetchPosFeeParams objects containing the target addresses and their corresponding amounts.
|
|
339
|
+
* @returns {Promise<CollectFeesQuote[]>} A Promise that resolves with the fetched position fee amount for the specified addresses.
|
|
340
|
+
*/
|
|
341
|
+
public async fetchPosFeeAmount(params: FetchPosFeeParams[]): Promise<CollectFeesQuote[]> {
|
|
342
|
+
const { clmm_pool, integrate } = this.sdk.sdkOptions
|
|
343
|
+
const tx = new Transaction()
|
|
344
|
+
|
|
345
|
+
for (const paramItem of params) {
|
|
346
|
+
this.buildFetchPosFee(paramItem, tx)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const simulateRes = await this.sdk.FullClient.devInspectTransactionBlock({
|
|
350
|
+
transactionBlock: tx,
|
|
351
|
+
sender: normalizeSuiAddress('0x0'),
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
if (simulateRes.error != null) {
|
|
355
|
+
handleMessageError(
|
|
356
|
+
PoolErrorCode.InvalidPoolObject,
|
|
357
|
+
`fetch position fee error code: ${simulateRes.error ?? 'unknown error'}, please check config and position and pool object ids`,
|
|
358
|
+
{
|
|
359
|
+
[DETAILS_KEYS.METHOD_NAME]: 'fetchPosFeeAmount',
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const result: CollectFeesQuote[] = []
|
|
365
|
+
const parsedPosFeeData = this.parsedPosFeeData(simulateRes)
|
|
366
|
+
for (let i = 0; i < params.length; i += 1) {
|
|
367
|
+
const posFeeData = parsedPosFeeData[params[i].position_id]
|
|
368
|
+
if (posFeeData) {
|
|
369
|
+
const posFeeResult: CollectFeesQuote = {
|
|
370
|
+
fee_owned_a: posFeeData.fee_owned_a,
|
|
371
|
+
fee_owned_b: posFeeData.fee_owned_b,
|
|
372
|
+
position_id: params[i].position_id,
|
|
373
|
+
}
|
|
374
|
+
result.push(posFeeResult)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return result
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Fetches the Position fee amount for a given list of addresses.
|
|
383
|
+
* @param position_ids An array of position object id.
|
|
384
|
+
* @returns {Promise<Record<string, CollectFeesQuote>>} A Promise that resolves with the fetched position fee amount for the specified position object ids.
|
|
385
|
+
*/
|
|
386
|
+
async batchFetchPositionFees(position_ids: string[]): Promise<Record<string, CollectFeesQuote>> {
|
|
387
|
+
const pos_fee_params_list: FetchPosFeeParams[] = []
|
|
388
|
+
for (const id of position_ids) {
|
|
389
|
+
const position = await this._sdk.Position.getPositionById(id, false)
|
|
390
|
+
const pool = await this._sdk.Pool.getPool(position.pool, false)
|
|
391
|
+
pos_fee_params_list.push({
|
|
392
|
+
pool_id: pool.id,
|
|
393
|
+
position_id: position.pos_object_id,
|
|
394
|
+
coin_type_a: pool.coin_type_a,
|
|
395
|
+
coin_type_b: pool.coin_type_b,
|
|
396
|
+
})
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const positionMap: Record<string, CollectFeesQuote> = {}
|
|
400
|
+
|
|
401
|
+
if (pos_fee_params_list.length > 0) {
|
|
402
|
+
const result: CollectFeesQuote[] = await this.fetchPosFeeAmount(pos_fee_params_list)
|
|
403
|
+
for (const pos_rewarder_info of result) {
|
|
404
|
+
positionMap[pos_rewarder_info.position_id] = pos_rewarder_info
|
|
405
|
+
}
|
|
406
|
+
return positionMap
|
|
407
|
+
}
|
|
408
|
+
return positionMap
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* create add liquidity transaction payload with fix token
|
|
413
|
+
* @param {AddLiquidityFixTokenParams} params
|
|
414
|
+
* @param gas_estimate_arg : When the fix input amount is SUI, gasEstimateArg can control whether to recalculate the number of SUI to prevent insufficient gas.
|
|
415
|
+
* If this parameter is not passed, gas estimation is not performed
|
|
416
|
+
* @returns {Promise<TransactionBlock>}
|
|
417
|
+
*/
|
|
418
|
+
async createAddLiquidityFixTokenPayload(
|
|
419
|
+
params: AddLiquidityFixTokenParams,
|
|
420
|
+
gas_estimate_arg?: {
|
|
421
|
+
slippage: number
|
|
422
|
+
cur_sqrt_price: BN
|
|
423
|
+
},
|
|
424
|
+
tx?: Transaction,
|
|
425
|
+
input_coin_a?: TransactionObjectArgument,
|
|
426
|
+
input_coin_b?: TransactionObjectArgument
|
|
427
|
+
): Promise<Transaction> {
|
|
428
|
+
const all_coin_asset = await this._sdk.FullClient.getOwnerCoinAssets(this.sdk.getSenderAddress())
|
|
429
|
+
|
|
430
|
+
if (gas_estimate_arg) {
|
|
431
|
+
const { is_adjust_coin_a, is_adjust_coin_b } = findAdjustCoin(params)
|
|
432
|
+
params = params as AddLiquidityFixTokenParams
|
|
433
|
+
if ((params.fix_amount_a && is_adjust_coin_a) || (!params.fix_amount_a && is_adjust_coin_b)) {
|
|
434
|
+
tx = await PositionUtils.buildAddLiquidityFixTokenForGas(
|
|
435
|
+
this._sdk,
|
|
436
|
+
all_coin_asset,
|
|
437
|
+
params,
|
|
438
|
+
gas_estimate_arg,
|
|
439
|
+
tx,
|
|
440
|
+
input_coin_a,
|
|
441
|
+
input_coin_b
|
|
442
|
+
)
|
|
443
|
+
return tx
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return PositionUtils.buildAddLiquidityFixToken(this._sdk, all_coin_asset, params, tx, input_coin_a, input_coin_b)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* create add liquidity transaction payload
|
|
452
|
+
* @param {AddLiquidityParams} params
|
|
453
|
+
* @returns {Promise<TransactionBlock>}
|
|
454
|
+
*/
|
|
455
|
+
async createAddLiquidityPayload(
|
|
456
|
+
params: AddLiquidityParams,
|
|
457
|
+
tx?: Transaction,
|
|
458
|
+
input_coin_a?: TransactionObjectArgument,
|
|
459
|
+
input_coin_b?: TransactionObjectArgument
|
|
460
|
+
): Promise<Transaction> {
|
|
461
|
+
const { integrate, clmm_pool } = this._sdk.sdkOptions
|
|
462
|
+
|
|
463
|
+
const tick_lower = asUintN(BigInt(params.tick_lower)).toString()
|
|
464
|
+
const tick_upper = asUintN(BigInt(params.tick_upper)).toString()
|
|
465
|
+
|
|
466
|
+
const typeArguments = [params.coin_type_a, params.coin_type_b]
|
|
467
|
+
|
|
468
|
+
tx = tx || new Transaction()
|
|
469
|
+
|
|
470
|
+
const needOpenPosition = !isValidSuiObjectId(params.pos_id)
|
|
471
|
+
const max_amount_a = BigInt(params.max_amount_a)
|
|
472
|
+
const max_amount_b = BigInt(params.max_amount_b)
|
|
473
|
+
|
|
474
|
+
let primary_coin_a_inputs: BuildCoinResult
|
|
475
|
+
let primary_coin_b_inputs: BuildCoinResult
|
|
476
|
+
if (input_coin_a == null || input_coin_b == null) {
|
|
477
|
+
const all_coin_asset = await this._sdk.FullClient.getOwnerCoinAssets(this.sdk.getSenderAddress())
|
|
478
|
+
primary_coin_a_inputs = CoinAssist.buildCoinForAmount(tx, all_coin_asset, max_amount_a, params.coin_type_a, false, true)
|
|
479
|
+
primary_coin_b_inputs = CoinAssist.buildCoinForAmount(tx, all_coin_asset, max_amount_b, params.coin_type_b, false, true)
|
|
480
|
+
} else {
|
|
481
|
+
primary_coin_a_inputs = {
|
|
482
|
+
target_coin: input_coin_a,
|
|
483
|
+
remain_coins: [],
|
|
484
|
+
is_mint_zero_coin: false,
|
|
485
|
+
target_coin_amount: '0',
|
|
486
|
+
selected_coins: [],
|
|
487
|
+
}
|
|
488
|
+
primary_coin_b_inputs = {
|
|
489
|
+
target_coin: input_coin_b,
|
|
490
|
+
remain_coins: [],
|
|
491
|
+
is_mint_zero_coin: false,
|
|
492
|
+
target_coin_amount: '0',
|
|
493
|
+
selected_coins: [],
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (needOpenPosition) {
|
|
498
|
+
tx.moveCall({
|
|
499
|
+
target: `${integrate.published_at}::${ClmmIntegratePoolV2Module}::open_position_with_liquidity`,
|
|
500
|
+
typeArguments,
|
|
501
|
+
arguments: [
|
|
502
|
+
tx.object(getPackagerConfigs(clmm_pool).global_config_id),
|
|
503
|
+
tx.object(params.pool_id),
|
|
504
|
+
tx.pure.u32(Number(tick_lower)),
|
|
505
|
+
tx.pure.u32(Number(tick_upper)),
|
|
506
|
+
primary_coin_a_inputs.target_coin,
|
|
507
|
+
primary_coin_b_inputs.target_coin,
|
|
508
|
+
tx.pure.u64(params.max_amount_a),
|
|
509
|
+
tx.pure.u64(params.max_amount_b),
|
|
510
|
+
tx.pure.u128(params.delta_liquidity),
|
|
511
|
+
tx.object(CLOCK_ADDRESS),
|
|
512
|
+
],
|
|
513
|
+
})
|
|
514
|
+
} else {
|
|
515
|
+
const all_coin_asset = await this._sdk.FullClient.getOwnerCoinAssets(this._sdk.getSenderAddress())
|
|
516
|
+
tx = PositionUtils.createCollectRewarderAndFeeParams(
|
|
517
|
+
this._sdk,
|
|
518
|
+
tx,
|
|
519
|
+
params,
|
|
520
|
+
all_coin_asset,
|
|
521
|
+
primary_coin_a_inputs.remain_coins,
|
|
522
|
+
primary_coin_b_inputs.remain_coins
|
|
523
|
+
)
|
|
524
|
+
tx.moveCall({
|
|
525
|
+
target: `${integrate.published_at}::${ClmmIntegratePoolV2Module}::add_liquidity`,
|
|
526
|
+
typeArguments,
|
|
527
|
+
arguments: [
|
|
528
|
+
tx.object(getPackagerConfigs(clmm_pool).global_config_id),
|
|
529
|
+
tx.object(params.pool_id),
|
|
530
|
+
tx.object(params.pos_id),
|
|
531
|
+
primary_coin_a_inputs.target_coin,
|
|
532
|
+
primary_coin_b_inputs.target_coin,
|
|
533
|
+
tx.pure.u64(params.max_amount_a),
|
|
534
|
+
tx.pure.u64(params.max_amount_b),
|
|
535
|
+
tx.pure.u128(params.delta_liquidity),
|
|
536
|
+
tx.object(CLOCK_ADDRESS),
|
|
537
|
+
],
|
|
538
|
+
})
|
|
539
|
+
}
|
|
540
|
+
return tx
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Remove liquidity from a position.
|
|
545
|
+
* @param {RemoveLiquidityParams} params
|
|
546
|
+
* @returns {TransactionBlock}
|
|
547
|
+
*/
|
|
548
|
+
async removeLiquidityPayload(params: RemoveLiquidityParams, tx?: Transaction): Promise<Transaction> {
|
|
549
|
+
const { clmm_pool, integrate } = this.sdk.sdkOptions
|
|
550
|
+
|
|
551
|
+
const functionName = 'remove_liquidity'
|
|
552
|
+
|
|
553
|
+
tx = tx || new Transaction()
|
|
554
|
+
|
|
555
|
+
const typeArguments = [params.coin_type_a, params.coin_type_b]
|
|
556
|
+
|
|
557
|
+
const allCoinAsset = await this._sdk.FullClient.getOwnerCoinAssets(this.sdk.getSenderAddress())
|
|
558
|
+
|
|
559
|
+
tx = PositionUtils.createCollectRewarderAndFeeParams(this._sdk, tx, params, allCoinAsset)
|
|
560
|
+
|
|
561
|
+
const args = [
|
|
562
|
+
tx.object(getPackagerConfigs(clmm_pool).global_config_id),
|
|
563
|
+
tx.object(params.pool_id),
|
|
564
|
+
tx.object(params.pos_id),
|
|
565
|
+
tx.pure.u128(params.delta_liquidity),
|
|
566
|
+
tx.pure.u64(params.min_amount_a),
|
|
567
|
+
tx.pure.u64(params.min_amount_b),
|
|
568
|
+
tx.object(CLOCK_ADDRESS),
|
|
569
|
+
]
|
|
570
|
+
|
|
571
|
+
tx.moveCall({
|
|
572
|
+
target: `${integrate.published_at}::${ClmmIntegratePoolModule}::${functionName}`,
|
|
573
|
+
typeArguments,
|
|
574
|
+
arguments: args,
|
|
575
|
+
})
|
|
576
|
+
|
|
577
|
+
return tx
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Close position and remove all liquidity and collect_reward
|
|
582
|
+
* @param {ClosePositionParams} params
|
|
583
|
+
* @returns {TransactionBlock}
|
|
584
|
+
*/
|
|
585
|
+
async closePositionPayload(params: ClosePositionParams, tx?: Transaction): Promise<Transaction> {
|
|
586
|
+
const { clmm_pool, integrate } = this.sdk.sdkOptions
|
|
587
|
+
|
|
588
|
+
tx = tx || new Transaction()
|
|
589
|
+
|
|
590
|
+
const typeArguments = [params.coin_type_a, params.coin_type_b]
|
|
591
|
+
|
|
592
|
+
const allCoinAsset = await this._sdk.FullClient.getOwnerCoinAssets(this.sdk.getSenderAddress())
|
|
593
|
+
|
|
594
|
+
tx = PositionUtils.createCollectRewarderAndFeeParams(this._sdk, tx, params, allCoinAsset)
|
|
595
|
+
|
|
596
|
+
tx.moveCall({
|
|
597
|
+
target: `${integrate.published_at}::${ClmmIntegratePoolModule}::close_position`,
|
|
598
|
+
typeArguments,
|
|
599
|
+
arguments: [
|
|
600
|
+
tx.object(getPackagerConfigs(clmm_pool).global_config_id),
|
|
601
|
+
tx.object(params.pool_id),
|
|
602
|
+
tx.object(params.pos_id),
|
|
603
|
+
tx.pure.u64(params.min_amount_a),
|
|
604
|
+
tx.pure.u64(params.min_amount_b),
|
|
605
|
+
tx.object(CLOCK_ADDRESS),
|
|
606
|
+
],
|
|
607
|
+
})
|
|
608
|
+
|
|
609
|
+
return tx
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Open position in clmmpool.
|
|
614
|
+
* @param {OpenPositionParams} params
|
|
615
|
+
* @returns {TransactionBlock}
|
|
616
|
+
*/
|
|
617
|
+
openPositionPayload(params: OpenPositionParams, tx?: Transaction): Transaction {
|
|
618
|
+
const { clmm_pool, integrate } = this.sdk.sdkOptions
|
|
619
|
+
tx = tx || new Transaction()
|
|
620
|
+
|
|
621
|
+
const typeArguments = [params.coin_type_a, params.coin_type_b]
|
|
622
|
+
const tick_lower = asUintN(BigInt(params.tick_lower)).toString()
|
|
623
|
+
const tick_upper = asUintN(BigInt(params.tick_upper)).toString()
|
|
624
|
+
const args = [
|
|
625
|
+
tx.object(getPackagerConfigs(clmm_pool).global_config_id),
|
|
626
|
+
tx.object(params.pool_id),
|
|
627
|
+
tx.pure.u32(Number(tick_lower)),
|
|
628
|
+
tx.pure.u32(Number(tick_upper)),
|
|
629
|
+
]
|
|
630
|
+
|
|
631
|
+
tx.moveCall({
|
|
632
|
+
target: `${integrate.published_at}::${ClmmIntegratePoolModule}::open_position`,
|
|
633
|
+
typeArguments,
|
|
634
|
+
arguments: args,
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
return tx
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Open position with price range in clmmpool.
|
|
642
|
+
* @param {OpenPositionWithPriceParams} params
|
|
643
|
+
* @returns {TransactionBlock}
|
|
644
|
+
*/
|
|
645
|
+
async openPositionWithPricePayload(params: OpenPositionWithPriceParams, tx?: Transaction): Promise<Transaction> {
|
|
646
|
+
const { pool_id } = params
|
|
647
|
+
const { clmm_pool, integrate } = this.sdk.sdkOptions
|
|
648
|
+
tx = tx || new Transaction()
|
|
649
|
+
|
|
650
|
+
const pool = await this.sdk.Pool.getPool(pool_id, false)
|
|
651
|
+
const tick_spacing = Number(pool.tick_spacing)
|
|
652
|
+
|
|
653
|
+
let tick_lower = 0
|
|
654
|
+
let tick_upper = 0
|
|
655
|
+
|
|
656
|
+
if (params.is_full_range) {
|
|
657
|
+
tick_lower = TickUtil.getMinIndex(tick_spacing)
|
|
658
|
+
tick_upper = TickUtil.getMaxIndex(tick_spacing)
|
|
659
|
+
} else {
|
|
660
|
+
const { price_base_coin, min_price, max_price } = params
|
|
661
|
+
tick_lower = TickMath.priceToInitializeTickIndex(
|
|
662
|
+
price_base_coin === 'coin_a' ? d(min_price) : d(1).div(max_price),
|
|
663
|
+
params.coin_decimals_a,
|
|
664
|
+
params.coin_decimals_b,
|
|
665
|
+
tick_spacing
|
|
666
|
+
)
|
|
667
|
+
tick_upper = TickMath.priceToInitializeTickIndex(
|
|
668
|
+
price_base_coin === 'coin_a' ? d(max_price) : d(1).div(min_price),
|
|
669
|
+
params.coin_decimals_a,
|
|
670
|
+
params.coin_decimals_b,
|
|
671
|
+
tick_spacing
|
|
672
|
+
)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const typeArguments = [pool.coin_type_a, pool.coin_type_b]
|
|
676
|
+
const args = [
|
|
677
|
+
tx.object(getPackagerConfigs(clmm_pool).global_config_id),
|
|
678
|
+
tx.object(params.pool_id),
|
|
679
|
+
tx.pure.u32(Number(asUintN(BigInt(tick_lower)))),
|
|
680
|
+
tx.pure.u32(Number(asUintN(BigInt(tick_upper)))),
|
|
681
|
+
]
|
|
682
|
+
|
|
683
|
+
tx.moveCall({
|
|
684
|
+
target: `${integrate.published_at}::${ClmmIntegratePoolModule}::open_position`,
|
|
685
|
+
typeArguments,
|
|
686
|
+
arguments: args,
|
|
687
|
+
})
|
|
688
|
+
|
|
689
|
+
return tx
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Collect LP fee from Position.
|
|
694
|
+
* @param {CollectFeeParams} params
|
|
695
|
+
* @param {TransactionBlock} tx
|
|
696
|
+
* @returns {TransactionBlock}
|
|
697
|
+
*/
|
|
698
|
+
async collectFeePayload(
|
|
699
|
+
params: CollectFeeParams,
|
|
700
|
+
tx?: Transaction,
|
|
701
|
+
input_coin_a?: TransactionObjectArgument,
|
|
702
|
+
input_coin_b?: TransactionObjectArgument
|
|
703
|
+
): Promise<Transaction> {
|
|
704
|
+
tx = tx || new Transaction()
|
|
705
|
+
|
|
706
|
+
const coin_a = input_coin_a || CoinAssist.buildCoinWithBalance(BigInt(0), params.coin_type_a, tx)
|
|
707
|
+
const coin_b = input_coin_b || CoinAssist.buildCoinWithBalance(BigInt(0), params.coin_type_b, tx)
|
|
708
|
+
|
|
709
|
+
this.createCollectFeePayload(params, tx, coin_a, coin_b)
|
|
710
|
+
return tx
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
createCollectFeePayload(
|
|
714
|
+
params: CollectFeeParams,
|
|
715
|
+
tx: Transaction,
|
|
716
|
+
primary_coin_a_input: TransactionObjectArgument,
|
|
717
|
+
primary_coin_b_input: TransactionObjectArgument
|
|
718
|
+
) {
|
|
719
|
+
const { clmm_pool, integrate } = this.sdk.sdkOptions
|
|
720
|
+
const typeArguments = [params.coin_type_a, params.coin_type_b]
|
|
721
|
+
const args = [
|
|
722
|
+
tx.object(getPackagerConfigs(clmm_pool).global_config_id),
|
|
723
|
+
tx.object(params.pool_id),
|
|
724
|
+
tx.object(params.pos_id),
|
|
725
|
+
primary_coin_a_input,
|
|
726
|
+
primary_coin_b_input,
|
|
727
|
+
]
|
|
728
|
+
|
|
729
|
+
tx.moveCall({
|
|
730
|
+
target: `${integrate.published_at}::${ClmmIntegratePoolV2Module}::collect_fee`,
|
|
731
|
+
typeArguments,
|
|
732
|
+
arguments: args,
|
|
733
|
+
})
|
|
734
|
+
return tx
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Calculate the result of add liquidity with price.
|
|
739
|
+
* @param {CalculateAddLiquidityWithPriceParams} params
|
|
740
|
+
* @returns {Promise<CalculateAddLiquidityResult>}
|
|
741
|
+
*/
|
|
742
|
+
async calculateAddLiquidityResultWithPrice(
|
|
743
|
+
params: CalculateAddLiquidityWithPriceParams | CalculateAddLiquidityFixCoinWithPriceParams
|
|
744
|
+
): Promise<CalculateAddLiquidityResult> {
|
|
745
|
+
const { pool_id, slippage, refresh_pool_price, add_mode_params } = params
|
|
746
|
+
|
|
747
|
+
const pool = await this.sdk.Pool.getPool(pool_id, refresh_pool_price)
|
|
748
|
+
const tick_spacing = Number(pool.tick_spacing)
|
|
749
|
+
|
|
750
|
+
let tick_lower = 0
|
|
751
|
+
let tick_upper = 0
|
|
752
|
+
|
|
753
|
+
if (add_mode_params.is_full_range) {
|
|
754
|
+
tick_lower = TickUtil.getMinIndex(tick_spacing)
|
|
755
|
+
tick_upper = TickUtil.getMaxIndex(tick_spacing)
|
|
756
|
+
} else {
|
|
757
|
+
const { price_base_coin, min_price, max_price } = add_mode_params
|
|
758
|
+
tick_lower = TickMath.priceToInitializeTickIndex(
|
|
759
|
+
price_base_coin === 'coin_a' ? d(min_price) : d(1).div(max_price),
|
|
760
|
+
add_mode_params.coin_decimals_a,
|
|
761
|
+
add_mode_params.coin_decimals_b,
|
|
762
|
+
tick_spacing
|
|
763
|
+
)
|
|
764
|
+
tick_upper = TickMath.priceToInitializeTickIndex(
|
|
765
|
+
price_base_coin === 'coin_a' ? d(max_price) : d(1).div(min_price),
|
|
766
|
+
add_mode_params.coin_decimals_a,
|
|
767
|
+
add_mode_params.coin_decimals_b,
|
|
768
|
+
tick_spacing
|
|
769
|
+
)
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if ('liquidity' in params) {
|
|
773
|
+
const { liquidity } = params
|
|
774
|
+
|
|
775
|
+
const lower_sqrt_price = TickMath.tickIndexToSqrtPriceX64(tick_lower)
|
|
776
|
+
const upper_sqrt_price = TickMath.tickIndexToSqrtPriceX64(tick_upper)
|
|
777
|
+
|
|
778
|
+
const { coin_amount_a, coin_amount_b } = ClmmPoolUtil.getCoinAmountFromLiquidity(
|
|
779
|
+
new BN(liquidity),
|
|
780
|
+
new BN(pool.current_sqrt_price),
|
|
781
|
+
lower_sqrt_price,
|
|
782
|
+
upper_sqrt_price,
|
|
783
|
+
false
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
const coin_amount_limit_a = d(coin_amount_a)
|
|
787
|
+
.mul(1 + slippage)
|
|
788
|
+
.toFixed(0, Decimal.ROUND_UP)
|
|
789
|
+
|
|
790
|
+
const coin_amount_limit_b = d(coin_amount_b)
|
|
791
|
+
.mul(1 + slippage)
|
|
792
|
+
.toFixed(0, Decimal.ROUND_UP)
|
|
793
|
+
|
|
794
|
+
return { coin_amount_a, coin_amount_b, coin_amount_limit_a, coin_amount_limit_b, liquidity, tick_lower, tick_upper }
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const { coin_amount, fix_amount_a } = params
|
|
798
|
+
const { coin_amount_limit_a, coin_amount_limit_b, liquidity_amount, coin_amount_a, coin_amount_b } =
|
|
799
|
+
ClmmPoolUtil.estLiquidityAndCoinAmountFromOneAmounts(
|
|
800
|
+
tick_lower,
|
|
801
|
+
tick_upper,
|
|
802
|
+
new BN(coin_amount),
|
|
803
|
+
fix_amount_a,
|
|
804
|
+
true,
|
|
805
|
+
slippage,
|
|
806
|
+
new BN(pool.current_sqrt_price)
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
return {
|
|
810
|
+
coin_amount_a,
|
|
811
|
+
coin_amount_b,
|
|
812
|
+
coin_amount_limit_a,
|
|
813
|
+
coin_amount_limit_b,
|
|
814
|
+
liquidity: liquidity_amount,
|
|
815
|
+
tick_lower,
|
|
816
|
+
tick_upper,
|
|
817
|
+
fix_amount_a,
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Add liquidity with price range.
|
|
823
|
+
* @param {AddLiquidityWithPriceRangeParams} params
|
|
824
|
+
* @param {TransactionBlock} tx
|
|
825
|
+
* @returns {TransactionBlock}
|
|
826
|
+
*/
|
|
827
|
+
async addLiquidityWithPricePayload(
|
|
828
|
+
params: AddLiquidityWithPriceRangeParams,
|
|
829
|
+
tx?: Transaction,
|
|
830
|
+
input_coin_a?: TransactionObjectArgument,
|
|
831
|
+
input_coin_b?: TransactionObjectArgument
|
|
832
|
+
): Promise<Transaction> {
|
|
833
|
+
const { pool_id, calculate_result } = params
|
|
834
|
+
const { coin_amount_limit_a, coin_amount_limit_b, liquidity, tick_lower, tick_upper } = calculate_result
|
|
835
|
+
tx = tx || new Transaction()
|
|
836
|
+
const pool = await this.sdk.Pool.getPool(pool_id, false)
|
|
837
|
+
|
|
838
|
+
await this.createAddLiquidityPayload(
|
|
839
|
+
{
|
|
840
|
+
delta_liquidity: liquidity,
|
|
841
|
+
max_amount_a: coin_amount_limit_a,
|
|
842
|
+
max_amount_b: coin_amount_limit_b,
|
|
843
|
+
tick_lower: tick_lower,
|
|
844
|
+
tick_upper: tick_upper,
|
|
845
|
+
collect_fee: false,
|
|
846
|
+
rewarder_coin_types: [],
|
|
847
|
+
coin_type_a: pool.coin_type_a,
|
|
848
|
+
coin_type_b: pool.coin_type_b,
|
|
849
|
+
pool_id: pool_id,
|
|
850
|
+
pos_id: '',
|
|
851
|
+
},
|
|
852
|
+
tx,
|
|
853
|
+
input_coin_a,
|
|
854
|
+
input_coin_b
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
return tx
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Add liquidity with price range.
|
|
862
|
+
* @param {AddLiquidityWithPriceRangeParams} params
|
|
863
|
+
* @param {TransactionBlock} tx
|
|
864
|
+
* @returns {TransactionBlock}
|
|
865
|
+
*/
|
|
866
|
+
async createAddLiquidityFixCoinWithPricePayload(
|
|
867
|
+
params: AddLiquidityWithPriceRangeParams,
|
|
868
|
+
tx?: Transaction,
|
|
869
|
+
input_coin_a?: TransactionObjectArgument,
|
|
870
|
+
input_coin_b?: TransactionObjectArgument
|
|
871
|
+
): Promise<Transaction> {
|
|
872
|
+
const { pool_id, calculate_result } = params
|
|
873
|
+
const { coin_amount_limit_a, coin_amount_limit_b, liquidity, tick_lower, tick_upper, fix_amount_a, coin_amount_a, coin_amount_b } =
|
|
874
|
+
calculate_result
|
|
875
|
+
if (fix_amount_a === undefined) {
|
|
876
|
+
throw handleMessageError(PositionErrorCode.InvalidParams, 'fix_amount_a is undefined', {
|
|
877
|
+
[DETAILS_KEYS.METHOD_NAME]: 'addLiquidityFixCoinCoinWithPricePayload',
|
|
878
|
+
[DETAILS_KEYS.REQUEST_PARAMS]: params,
|
|
879
|
+
})
|
|
880
|
+
}
|
|
881
|
+
tx = tx || new Transaction()
|
|
882
|
+
const pool = await this.sdk.Pool.getPool(pool_id, false)
|
|
883
|
+
|
|
884
|
+
await this.createAddLiquidityFixTokenPayload(
|
|
885
|
+
{
|
|
886
|
+
amount_a: fix_amount_a ? coin_amount_a : coin_amount_limit_a,
|
|
887
|
+
amount_b: fix_amount_a ? coin_amount_limit_b : coin_amount_b,
|
|
888
|
+
slippage: 0,
|
|
889
|
+
fix_amount_a,
|
|
890
|
+
is_open: true,
|
|
891
|
+
tick_lower: tick_lower,
|
|
892
|
+
tick_upper: tick_upper,
|
|
893
|
+
collect_fee: false,
|
|
894
|
+
rewarder_coin_types: [],
|
|
895
|
+
coin_type_a: pool.coin_type_a,
|
|
896
|
+
coin_type_b: pool.coin_type_b,
|
|
897
|
+
pool_id: pool_id,
|
|
898
|
+
pos_id: '',
|
|
899
|
+
},
|
|
900
|
+
undefined,
|
|
901
|
+
tx,
|
|
902
|
+
input_coin_a,
|
|
903
|
+
input_coin_b
|
|
904
|
+
)
|
|
905
|
+
|
|
906
|
+
return tx
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
createCollectFeeNoSendPayload(
|
|
910
|
+
params: CollectFeeParams,
|
|
911
|
+
tx: Transaction,
|
|
912
|
+
primary_coin_a_input: TransactionObjectArgument,
|
|
913
|
+
primary_coin_b_input: TransactionObjectArgument
|
|
914
|
+
) {
|
|
915
|
+
const { clmm_pool, integrate } = this.sdk.sdkOptions
|
|
916
|
+
const typeArguments = [params.coin_type_a, params.coin_type_b]
|
|
917
|
+
const args = [
|
|
918
|
+
tx.object(getPackagerConfigs(clmm_pool).global_config_id),
|
|
919
|
+
tx.object(params.pool_id),
|
|
920
|
+
tx.object(params.pos_id),
|
|
921
|
+
primary_coin_a_input,
|
|
922
|
+
primary_coin_b_input,
|
|
923
|
+
]
|
|
924
|
+
|
|
925
|
+
tx.moveCall({
|
|
926
|
+
target: `${integrate.published_at}::${ClmmIntegratePoolV3Module}::collect_fee`,
|
|
927
|
+
typeArguments,
|
|
928
|
+
arguments: args,
|
|
929
|
+
})
|
|
930
|
+
return tx
|
|
931
|
+
}
|
|
932
|
+
}
|