@cetusprotocol/dlmm-sdk 0.0.1
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 +10423 -0
- package/README.md +646 -0
- package/dist/index.d.mts +1015 -0
- package/dist/index.d.ts +1015 -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/package.json +35 -0
- package/src/config/index.ts +2 -0
- package/src/config/mainnet.ts +25 -0
- package/src/config/testnet.ts +30 -0
- package/src/errors/errors.ts +40 -0
- package/src/index.ts +8 -0
- package/src/modules/configModule.ts +184 -0
- package/src/modules/index.ts +1 -0
- package/src/modules/partnerModule.ts +302 -0
- package/src/modules/poolModule.ts +578 -0
- package/src/modules/positionModule.ts +888 -0
- package/src/modules/rewardModule.ts +175 -0
- package/src/modules/swapModule.ts +129 -0
- package/src/sdk.ts +88 -0
- package/src/types/constants.ts +23 -0
- package/src/types/dlmm.ts +445 -0
- package/src/types/index.ts +2 -0
- package/src/utils/binUtils.ts +552 -0
- package/src/utils/feeUtils.ts +92 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/parseData.ts +519 -0
- package/src/utils/strategyUtils.ts +121 -0
- package/src/utils/weightUtils.ts +510 -0
- package/tests/add_liquidity_bidask.test.ts +180 -0
- package/tests/add_liquidity_curve.test.ts +244 -0
- package/tests/add_liquidity_spot.test.ts +262 -0
- package/tests/bin.test.ts +80 -0
- package/tests/config.test.ts +51 -0
- package/tests/partner.test.ts +74 -0
- package/tests/pool.test.ts +174 -0
- package/tests/position.test.ts +76 -0
- package/tests/remove_liquidity.test.ts +137 -0
- package/tests/swap.test.ts +96 -0
- package/tests/tsconfig.json +26 -0
- package/tsconfig.json +5 -0
- package/tsup.config.ts +9 -0
|
@@ -0,0 +1,888 @@
|
|
|
1
|
+
import { asUintN, CLOCK_ADDRESS, CoinAssist, d, DETAILS_KEYS, getPackagerConfigs, IModule } from '@cetusprotocol/common-sdk'
|
|
2
|
+
import { Transaction, TransactionObjectArgument, TransactionResult } from '@mysten/sui/transactions'
|
|
3
|
+
import { normalizeSuiAddress } from '@mysten/sui/utils'
|
|
4
|
+
import { DlmmErrorCode, handleError } from '../errors/errors'
|
|
5
|
+
import { CetusDlmmSDK } from '../sdk'
|
|
6
|
+
import { BASIS_POINT, MAX_BIN_PER_POSITION } from '../types/constants'
|
|
7
|
+
import {
|
|
8
|
+
AddLiquidityOption,
|
|
9
|
+
BinAmount,
|
|
10
|
+
BinLiquidityInfo,
|
|
11
|
+
CalculateAddLiquidityAutoFillOption,
|
|
12
|
+
CalculateAddLiquidityOption,
|
|
13
|
+
CalculateRemoveLiquidityBothOption,
|
|
14
|
+
CalculateRemoveLiquidityOnlyOption,
|
|
15
|
+
ClosePositionOption,
|
|
16
|
+
CollectFeeOption,
|
|
17
|
+
CollectRewardAndFeeOption,
|
|
18
|
+
CollectRewardOption,
|
|
19
|
+
DlmmPosition,
|
|
20
|
+
OpenAndAddLiquidityOption,
|
|
21
|
+
OpenAndAddLiquidityWithPriceOption,
|
|
22
|
+
PositionFee,
|
|
23
|
+
PositionReward,
|
|
24
|
+
RemoveLiquidityOption,
|
|
25
|
+
StrategyType,
|
|
26
|
+
UpdatePositionFeeAndRewardsOption,
|
|
27
|
+
ValidateActiveIdSlippageOption,
|
|
28
|
+
} from '../types/dlmm'
|
|
29
|
+
import {
|
|
30
|
+
BinUtils,
|
|
31
|
+
getRouterModule,
|
|
32
|
+
parsedDlmmPosFeeData,
|
|
33
|
+
parsedDlmmPosRewardData,
|
|
34
|
+
parseDlmmPosition,
|
|
35
|
+
parseStrategyType,
|
|
36
|
+
StrategyUtils,
|
|
37
|
+
} from '../utils'
|
|
38
|
+
import Decimal from 'decimal.js'
|
|
39
|
+
|
|
40
|
+
export class PositionModule implements IModule<CetusDlmmSDK> {
|
|
41
|
+
protected _sdk: CetusDlmmSDK
|
|
42
|
+
|
|
43
|
+
constructor(sdk: CetusDlmmSDK) {
|
|
44
|
+
this._sdk = sdk
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get sdk() {
|
|
48
|
+
return this._sdk
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
buildPositionType(): string {
|
|
52
|
+
const package_id = this._sdk.sdkOptions.dlmm_pool.package_id
|
|
53
|
+
return `${package_id}::position::Position`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getOwnerPositionList(owner: string): Promise<DlmmPosition[]> {
|
|
57
|
+
const list: DlmmPosition[] = []
|
|
58
|
+
try {
|
|
59
|
+
const res = await this._sdk.FullClient.getOwnedObjectsByPage(owner, {
|
|
60
|
+
options: { showType: true, showContent: true, showOwner: true },
|
|
61
|
+
filter: {
|
|
62
|
+
StructType: this.buildPositionType(),
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
res.data.forEach((obj) => {
|
|
67
|
+
list.push(parseDlmmPosition(obj))
|
|
68
|
+
})
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.log('🚀 ~ PositionModule ~ getOwnerPositionList ~ error:', error)
|
|
71
|
+
handleError(DlmmErrorCode.GetObjectError, error as Error, {
|
|
72
|
+
[DETAILS_KEYS.METHOD_NAME]: 'getOwnerPositionList',
|
|
73
|
+
[DETAILS_KEYS.REQUEST_PARAMS]: owner,
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return list
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async getPosition(position_id: string): Promise<DlmmPosition> {
|
|
81
|
+
try {
|
|
82
|
+
const res = await this._sdk.FullClient.getObject({ id: position_id, options: { showType: true, showContent: true, showOwner: true } })
|
|
83
|
+
return parseDlmmPosition(res)
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.log('🚀 ~ PositionModule ~ getPosition ~ error:', error)
|
|
86
|
+
return handleError(DlmmErrorCode.GetObjectError, error as Error, {
|
|
87
|
+
[DETAILS_KEYS.METHOD_NAME]: 'getPosition',
|
|
88
|
+
[DETAILS_KEYS.REQUEST_PARAMS]: position_id,
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Collect fee
|
|
95
|
+
* @param option - The option for collecting fee
|
|
96
|
+
* @param tx - The transaction object
|
|
97
|
+
* @returns The transaction object
|
|
98
|
+
*/
|
|
99
|
+
collectFeePayload(option: CollectFeeOption, tx?: Transaction): Transaction {
|
|
100
|
+
const { pool_id, position_id, coin_type_a, coin_type_b } = option
|
|
101
|
+
const { dlmm_pool } = this.sdk.sdkOptions
|
|
102
|
+
const { versioned_id, global_config_id } = getPackagerConfigs(dlmm_pool)
|
|
103
|
+
|
|
104
|
+
tx = tx || new Transaction()
|
|
105
|
+
|
|
106
|
+
const [fee_a_balance, fee_b_balance] = tx.moveCall({
|
|
107
|
+
target: `${dlmm_pool.published_at}::pool::collect_position_fee`,
|
|
108
|
+
arguments: [tx.object(pool_id), tx.object(position_id), tx.object(global_config_id), tx.object(versioned_id)],
|
|
109
|
+
typeArguments: [coin_type_a, coin_type_b],
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
const fee_a_obj = CoinAssist.fromBalance(fee_a_balance, coin_type_a, tx)
|
|
113
|
+
const fee_b_obj = CoinAssist.fromBalance(fee_b_balance, coin_type_b, tx)
|
|
114
|
+
tx.transferObjects([fee_a_obj, fee_b_obj], this.sdk.getSenderAddress())
|
|
115
|
+
|
|
116
|
+
return tx
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Update the fee and rewards of the position
|
|
121
|
+
* @param option - The option for updating the fee and rewards of the position
|
|
122
|
+
* @param tx - The transaction object
|
|
123
|
+
* @returns The transaction object
|
|
124
|
+
*/
|
|
125
|
+
updatePositionFeeAndRewards(option: UpdatePositionFeeAndRewardsOption, tx: Transaction) {
|
|
126
|
+
const { dlmm_pool } = this.sdk.sdkOptions
|
|
127
|
+
const { versioned_id, global_config_id } = getPackagerConfigs(dlmm_pool)
|
|
128
|
+
const { pool_id, position_id, coin_type_a, coin_type_b } = option
|
|
129
|
+
tx.moveCall({
|
|
130
|
+
target: `${dlmm_pool.published_at}::pool::update_position_fee_and_rewards`,
|
|
131
|
+
arguments: [
|
|
132
|
+
tx.object(pool_id),
|
|
133
|
+
tx.pure.id(position_id),
|
|
134
|
+
tx.object(global_config_id),
|
|
135
|
+
tx.object(versioned_id),
|
|
136
|
+
tx.object(CLOCK_ADDRESS),
|
|
137
|
+
],
|
|
138
|
+
typeArguments: [coin_type_a, coin_type_b],
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
return tx
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Collect reward
|
|
146
|
+
* @param options - The option for collecting reward
|
|
147
|
+
* @param tx - The transaction object
|
|
148
|
+
* @returns The transaction object
|
|
149
|
+
*/
|
|
150
|
+
collectRewardPayload(options: CollectRewardOption[], tx?: Transaction): Transaction {
|
|
151
|
+
const { dlmm_pool } = this.sdk.sdkOptions
|
|
152
|
+
const { versioned_id, global_config_id } = getPackagerConfigs(dlmm_pool)
|
|
153
|
+
|
|
154
|
+
tx = tx || new Transaction()
|
|
155
|
+
|
|
156
|
+
options.forEach((option) => {
|
|
157
|
+
const { pool_id, position_id, reward_coins, coin_type_a, coin_type_b } = option
|
|
158
|
+
|
|
159
|
+
reward_coins.forEach((reward_coin) => {
|
|
160
|
+
const reward_coin_balance = tx.moveCall({
|
|
161
|
+
target: `${dlmm_pool.published_at}::pool::collect_position_reward`,
|
|
162
|
+
arguments: [tx.object(pool_id), tx.object(position_id), tx.object(global_config_id), tx.object(versioned_id)],
|
|
163
|
+
typeArguments: [coin_type_a, coin_type_b, reward_coin],
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
const reward_coin_obj = CoinAssist.fromBalance(reward_coin_balance, reward_coin, tx)
|
|
167
|
+
tx.transferObjects([reward_coin_obj], this.sdk.getSenderAddress())
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
return tx
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
collectRewardAndFeePayload(options: CollectRewardAndFeeOption[], tx?: Transaction): Transaction {
|
|
175
|
+
tx = tx || new Transaction()
|
|
176
|
+
|
|
177
|
+
options.forEach((option) => {
|
|
178
|
+
const { pool_id, position_id, reward_coins, coin_type_a, coin_type_b } = option
|
|
179
|
+
|
|
180
|
+
this.updatePositionFeeAndRewards({ pool_id, position_id, coin_type_a, coin_type_b }, tx)
|
|
181
|
+
|
|
182
|
+
this.collectFeePayload({ pool_id, position_id, coin_type_a, coin_type_b }, tx)
|
|
183
|
+
this.collectRewardPayload([{ pool_id, position_id, reward_coins, coin_type_a, coin_type_b }], tx)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
return tx
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Validate the active id slippage
|
|
191
|
+
* @param options - The option for validating the active id slippage
|
|
192
|
+
* @param tx - The transaction object
|
|
193
|
+
* @returns The transaction object
|
|
194
|
+
*/
|
|
195
|
+
validateActiveIdSlippage(options: ValidateActiveIdSlippageOption, tx: Transaction): Transaction {
|
|
196
|
+
const { pool_id, active_id, max_price_slippage, bin_step, coin_type_a, coin_type_b } = options
|
|
197
|
+
const { dlmm_router } = this.sdk.sdkOptions
|
|
198
|
+
|
|
199
|
+
const bin_shift = BinUtils.getBinShift(active_id, bin_step, max_price_slippage)
|
|
200
|
+
const active_id_u32 = Number(asUintN(BigInt(active_id)))
|
|
201
|
+
|
|
202
|
+
tx.moveCall({
|
|
203
|
+
target: `${dlmm_router.published_at}::utils::validate_active_id_slippage`,
|
|
204
|
+
arguments: [
|
|
205
|
+
typeof pool_id === 'string' ? tx.object(pool_id) : pool_id,
|
|
206
|
+
tx.pure.u32(Number(active_id_u32)),
|
|
207
|
+
tx.pure.u32(Number(bin_shift)),
|
|
208
|
+
],
|
|
209
|
+
typeArguments: [coin_type_a, coin_type_b],
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
return tx
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Close a position
|
|
217
|
+
* @param option - The option for closing a position
|
|
218
|
+
* @returns The transaction object
|
|
219
|
+
*/
|
|
220
|
+
closePositionPayload(option: ClosePositionOption, tx?: Transaction): Transaction {
|
|
221
|
+
const { pool_id, position_id, reward_coins, coin_type_a, coin_type_b } = option
|
|
222
|
+
const { dlmm_pool } = this.sdk.sdkOptions
|
|
223
|
+
const { versioned_id, global_config_id } = getPackagerConfigs(dlmm_pool)
|
|
224
|
+
|
|
225
|
+
tx = tx || new Transaction()
|
|
226
|
+
|
|
227
|
+
this.updatePositionFeeAndRewards({ pool_id, position_id, coin_type_a, coin_type_b }, tx)
|
|
228
|
+
|
|
229
|
+
this.collectRewardPayload([{ pool_id, position_id, reward_coins, coin_type_a, coin_type_b }], tx)
|
|
230
|
+
|
|
231
|
+
const [close_position_cert, coin_a_balance, coin_b_balance] = tx.moveCall({
|
|
232
|
+
target: `${dlmm_pool.published_at}::pool::close_position`,
|
|
233
|
+
arguments: [
|
|
234
|
+
tx.object(pool_id),
|
|
235
|
+
tx.object(position_id),
|
|
236
|
+
tx.object(global_config_id),
|
|
237
|
+
tx.object(versioned_id),
|
|
238
|
+
tx.object(CLOCK_ADDRESS),
|
|
239
|
+
],
|
|
240
|
+
typeArguments: [coin_type_a, coin_type_b],
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
const coin_a_obj = CoinAssist.fromBalance(coin_a_balance, coin_type_a, tx)
|
|
244
|
+
const coin_b_obj = CoinAssist.fromBalance(coin_b_balance, coin_type_b, tx)
|
|
245
|
+
|
|
246
|
+
tx.moveCall({
|
|
247
|
+
target: `${dlmm_pool.published_at}::pool::destroy_close_position_cert`,
|
|
248
|
+
arguments: [close_position_cert, tx.object(versioned_id)],
|
|
249
|
+
typeArguments: [],
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
tx.transferObjects([coin_a_obj, coin_b_obj], this.sdk.getSenderAddress())
|
|
253
|
+
|
|
254
|
+
return tx
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Get the amounts in the active bin if in range
|
|
259
|
+
* @param bin_manager_handle - The bin manager handle
|
|
260
|
+
* @param lower_bin_id - The lower bin id
|
|
261
|
+
* @param upper_bin_id - The upper bin id
|
|
262
|
+
* @param active_id - The active id
|
|
263
|
+
* @returns The amounts in the active bin if in range
|
|
264
|
+
*/
|
|
265
|
+
async getActiveBinIfInRange(
|
|
266
|
+
bin_manager_handle: string,
|
|
267
|
+
lower_bin_id: number,
|
|
268
|
+
upper_bin_id: number,
|
|
269
|
+
active_id: number,
|
|
270
|
+
bin_step: number,
|
|
271
|
+
force_refresh = false
|
|
272
|
+
): Promise<BinAmount | undefined> {
|
|
273
|
+
if (active_id <= upper_bin_id && active_id >= lower_bin_id) {
|
|
274
|
+
const bin_info = await this._sdk.Pool.getBinInfo(bin_manager_handle, active_id, bin_step, force_refresh)
|
|
275
|
+
return bin_info
|
|
276
|
+
}
|
|
277
|
+
return undefined
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Calculate the result of removing liquidity
|
|
282
|
+
* @param option - The option for calculating the result of removing liquidity
|
|
283
|
+
* @returns The result of removing liquidity
|
|
284
|
+
*/
|
|
285
|
+
calculateRemoveLiquidityInfo(option: CalculateRemoveLiquidityBothOption | CalculateRemoveLiquidityOnlyOption): BinLiquidityInfo {
|
|
286
|
+
const { bins, active_id, coin_amount } = option
|
|
287
|
+
const isBothSide = 'fix_amount_a' in option
|
|
288
|
+
|
|
289
|
+
const bins_b = bins.filter((bin) => bin.bin_id < active_id)
|
|
290
|
+
const bins_a = bins.filter((bin) => bin.bin_id > active_id)
|
|
291
|
+
|
|
292
|
+
let total_amount = d(0)
|
|
293
|
+
let amount_rate = d(0)
|
|
294
|
+
let used_bins: BinAmount[] = []
|
|
295
|
+
|
|
296
|
+
if (isBothSide) {
|
|
297
|
+
used_bins = [...bins]
|
|
298
|
+
const { fix_amount_a } = option
|
|
299
|
+
const active_bin = bins.find((bin) => bin.bin_id === active_id)
|
|
300
|
+
const total_amount_a = bins_a.reduce((acc, bin) => d(acc).plus(bin.amount_a), d(0)).add(active_bin?.amount_a || '0')
|
|
301
|
+
const total_amount_b = bins_b.reduce((acc, bin) => d(acc).plus(bin.amount_b), d(0)).add(active_bin?.amount_b || '0')
|
|
302
|
+
total_amount = fix_amount_a ? total_amount_a : total_amount_b
|
|
303
|
+
|
|
304
|
+
amount_rate = d(coin_amount).gte(total_amount) ? d(1) : d(coin_amount).div(total_amount)
|
|
305
|
+
} else {
|
|
306
|
+
const { is_only_a } = option
|
|
307
|
+
used_bins = is_only_a ? bins_a : bins_b
|
|
308
|
+
total_amount = used_bins.reduce((acc, bin) => d(acc).plus(is_only_a ? bin.amount_a : bin.amount_b), d(0))
|
|
309
|
+
|
|
310
|
+
amount_rate = d(coin_amount).gte(total_amount) ? d(1) : d(coin_amount).div(total_amount)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (d(total_amount).isZero()) {
|
|
314
|
+
return handleError(DlmmErrorCode.InsufficientLiquidity, new Error('Insufficient liquidity'), {
|
|
315
|
+
[DETAILS_KEYS.METHOD_NAME]: 'calculateRemoveLiquidityResult',
|
|
316
|
+
[DETAILS_KEYS.REQUEST_PARAMS]: option,
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const info = BinUtils.processBinsByRate([...used_bins], amount_rate.toFixed())
|
|
321
|
+
// If the proportional allocation results in an invalid amount, iterate through the bins and remove them one by one
|
|
322
|
+
if (info.has_invalid_amount) {
|
|
323
|
+
let remaining_amount = d(coin_amount)
|
|
324
|
+
const processed_bins: BinAmount[] = []
|
|
325
|
+
const is_amount_a = isBothSide ? option.fix_amount_a : option.is_only_a
|
|
326
|
+
for (const bin of used_bins) {
|
|
327
|
+
if (remaining_amount.lte(0)) break
|
|
328
|
+
|
|
329
|
+
const bin_amount = is_amount_a ? bin.amount_a : bin.amount_b
|
|
330
|
+
const bin_amount_other = is_amount_a ? bin.amount_b : bin.amount_a
|
|
331
|
+
|
|
332
|
+
let rate = d(d(remaining_amount).div(bin_amount).toFixed(9, Decimal.ROUND_UP))
|
|
333
|
+
rate = rate.gt(1) ? d(1) : rate
|
|
334
|
+
let amount_to_remove = d(bin_amount).mul(rate)
|
|
335
|
+
remaining_amount = remaining_amount.minus(amount_to_remove)
|
|
336
|
+
|
|
337
|
+
const amount_to_remove_other = d(bin_amount_other).mul(rate)
|
|
338
|
+
const liquidity_to_remove = d(bin.liquidity!).mul(rate)
|
|
339
|
+
|
|
340
|
+
processed_bins.push({
|
|
341
|
+
...bin,
|
|
342
|
+
amount_a: is_amount_a ? amount_to_remove.toFixed(0) : amount_to_remove_other.toFixed(0),
|
|
343
|
+
amount_b: is_amount_a ? amount_to_remove_other.toFixed(0) : amount_to_remove.toFixed(0),
|
|
344
|
+
liquidity: liquidity_to_remove.toFixed(0),
|
|
345
|
+
})
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
bins: processed_bins,
|
|
350
|
+
amount_a: processed_bins.reduce((acc, bin) => d(acc).plus(bin.amount_a), d(0)).toFixed(0),
|
|
351
|
+
amount_b: processed_bins.reduce((acc, bin) => d(acc).plus(bin.amount_b), d(0)).toFixed(0),
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return info.bins
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Calculate the result of adding liquidity
|
|
360
|
+
* @param option - The option for calculating the result of adding liquidity
|
|
361
|
+
* @returns The result of adding liquidity
|
|
362
|
+
*/
|
|
363
|
+
calculateAddLiquidityInfo(option: CalculateAddLiquidityOption | CalculateAddLiquidityAutoFillOption): BinLiquidityInfo {
|
|
364
|
+
const isAutoFill = 'fix_amount_a' in option
|
|
365
|
+
const { active_id, bin_step, lower_bin_id, upper_bin_id, amount_a_in_active_bin, amount_b_in_active_bin, strategy_type } = option
|
|
366
|
+
|
|
367
|
+
if (isAutoFill) {
|
|
368
|
+
const { coin_amount, fix_amount_a } = option
|
|
369
|
+
return StrategyUtils.autoFillCoinByStrategy(
|
|
370
|
+
active_id,
|
|
371
|
+
bin_step,
|
|
372
|
+
coin_amount,
|
|
373
|
+
fix_amount_a,
|
|
374
|
+
amount_a_in_active_bin,
|
|
375
|
+
amount_b_in_active_bin,
|
|
376
|
+
lower_bin_id,
|
|
377
|
+
upper_bin_id,
|
|
378
|
+
strategy_type
|
|
379
|
+
)
|
|
380
|
+
} else {
|
|
381
|
+
return StrategyUtils.toAmountsBothSideByStrategy(
|
|
382
|
+
active_id,
|
|
383
|
+
bin_step,
|
|
384
|
+
lower_bin_id,
|
|
385
|
+
upper_bin_id,
|
|
386
|
+
option.amount_a,
|
|
387
|
+
option.amount_b,
|
|
388
|
+
amount_a_in_active_bin,
|
|
389
|
+
amount_b_in_active_bin,
|
|
390
|
+
strategy_type
|
|
391
|
+
)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Remove liquidity
|
|
397
|
+
* @param option - The option for removing liquidity
|
|
398
|
+
* @returns The transaction
|
|
399
|
+
*/
|
|
400
|
+
removeLiquidityPayload(option: RemoveLiquidityOption): Transaction {
|
|
401
|
+
const {
|
|
402
|
+
pool_id,
|
|
403
|
+
position_id,
|
|
404
|
+
bin_infos,
|
|
405
|
+
reward_coins,
|
|
406
|
+
slippage,
|
|
407
|
+
coin_type_a,
|
|
408
|
+
coin_type_b,
|
|
409
|
+
active_id,
|
|
410
|
+
collect_fee,
|
|
411
|
+
bin_step,
|
|
412
|
+
remove_percent,
|
|
413
|
+
} = option
|
|
414
|
+
const { dlmm_pool } = this.sdk.sdkOptions
|
|
415
|
+
const { bins } = bin_infos
|
|
416
|
+
const tx = new Transaction()
|
|
417
|
+
|
|
418
|
+
if (collect_fee || reward_coins.length > 0) {
|
|
419
|
+
this.updatePositionFeeAndRewards({ pool_id, position_id, coin_type_a, coin_type_b }, tx)
|
|
420
|
+
}
|
|
421
|
+
if (collect_fee) {
|
|
422
|
+
this.collectFeePayload({ pool_id, position_id, coin_type_a, coin_type_b }, tx)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
this.collectRewardPayload([{ pool_id, position_id, reward_coins, coin_type_a, coin_type_b }], tx)
|
|
426
|
+
|
|
427
|
+
const { versioned_id, global_config_id } = getPackagerConfigs(dlmm_pool)
|
|
428
|
+
|
|
429
|
+
if (remove_percent) {
|
|
430
|
+
const min_bin_id_u32 = asUintN(BigInt(bins[0].bin_id))
|
|
431
|
+
const max_bin_id_u32 = asUintN(BigInt(bins[bins.length - 1].bin_id))
|
|
432
|
+
const remove_percent_fixed = Number(d(remove_percent).mul(BASIS_POINT).toFixed(0))
|
|
433
|
+
const [coin_a_balance, coin_b_balance] = tx.moveCall({
|
|
434
|
+
target: `${dlmm_pool.published_at}::pool::remove_liquidity_by_percent`,
|
|
435
|
+
arguments: [
|
|
436
|
+
tx.object(pool_id),
|
|
437
|
+
tx.object(position_id),
|
|
438
|
+
tx.pure.u32(Number(min_bin_id_u32)),
|
|
439
|
+
tx.pure.u32(Number(max_bin_id_u32)),
|
|
440
|
+
tx.pure.u16(remove_percent_fixed),
|
|
441
|
+
tx.object(global_config_id),
|
|
442
|
+
tx.object(versioned_id),
|
|
443
|
+
tx.object(CLOCK_ADDRESS),
|
|
444
|
+
],
|
|
445
|
+
typeArguments: [coin_type_a, coin_type_b],
|
|
446
|
+
})
|
|
447
|
+
const coin_a_obj = CoinAssist.fromBalance(coin_a_balance, coin_type_a, tx)
|
|
448
|
+
const coin_b_obj = CoinAssist.fromBalance(coin_b_balance, coin_type_b, tx)
|
|
449
|
+
tx.transferObjects([coin_a_obj, coin_b_obj], this.sdk.getSenderAddress())
|
|
450
|
+
} else {
|
|
451
|
+
const bin_amounts = tx.pure.vector(
|
|
452
|
+
'u32',
|
|
453
|
+
bins.map((bin) => Number(asUintN(BigInt(bin.bin_id))))
|
|
454
|
+
)
|
|
455
|
+
const remove_liquiditys = tx.pure.vector(
|
|
456
|
+
'u128',
|
|
457
|
+
bins.map((bin) => bin.liquidity!)
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
const [coin_a_balance, coin_b_balance] = tx.moveCall({
|
|
461
|
+
target: `${dlmm_pool.published_at}::pool::remove_liquidity`,
|
|
462
|
+
arguments: [
|
|
463
|
+
tx.object(pool_id),
|
|
464
|
+
tx.object(position_id),
|
|
465
|
+
bin_amounts,
|
|
466
|
+
remove_liquiditys,
|
|
467
|
+
tx.object(global_config_id),
|
|
468
|
+
tx.object(versioned_id),
|
|
469
|
+
tx.object(CLOCK_ADDRESS),
|
|
470
|
+
],
|
|
471
|
+
typeArguments: [coin_type_a, coin_type_b],
|
|
472
|
+
})
|
|
473
|
+
const coin_a_obj = CoinAssist.fromBalance(coin_a_balance, coin_type_a, tx)
|
|
474
|
+
const coin_b_obj = CoinAssist.fromBalance(coin_b_balance, coin_type_b, tx)
|
|
475
|
+
tx.transferObjects([coin_a_obj, coin_b_obj], this.sdk.getSenderAddress())
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// validate the active id slippage
|
|
479
|
+
this.validateActiveIdSlippage({ pool_id, active_id, max_price_slippage: slippage, bin_step, coin_type_a, coin_type_b }, tx)
|
|
480
|
+
|
|
481
|
+
return tx
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Add liquidity with price
|
|
486
|
+
* @param option - The option for adding liquidity with price
|
|
487
|
+
* @returns The transaction
|
|
488
|
+
*/
|
|
489
|
+
addLiquidityWithPricePayload(option: OpenAndAddLiquidityWithPriceOption): Transaction {
|
|
490
|
+
const {
|
|
491
|
+
pool_id,
|
|
492
|
+
bin_infos,
|
|
493
|
+
coin_type_a,
|
|
494
|
+
coin_type_b,
|
|
495
|
+
price_base_coin,
|
|
496
|
+
price,
|
|
497
|
+
lower_price,
|
|
498
|
+
upper_price,
|
|
499
|
+
bin_step,
|
|
500
|
+
strategy_type,
|
|
501
|
+
amount_a_in_active_bin,
|
|
502
|
+
amount_b_in_active_bin,
|
|
503
|
+
decimals_a,
|
|
504
|
+
decimals_b,
|
|
505
|
+
use_bin_infos,
|
|
506
|
+
max_price_slippage,
|
|
507
|
+
} = option
|
|
508
|
+
let lower_bin_id
|
|
509
|
+
let upper_bin_id
|
|
510
|
+
let active_id
|
|
511
|
+
let new_bin_infos: BinLiquidityInfo = bin_infos
|
|
512
|
+
|
|
513
|
+
const is_coin_a_base = price_base_coin === 'coin_a'
|
|
514
|
+
|
|
515
|
+
if (is_coin_a_base) {
|
|
516
|
+
lower_bin_id = BinUtils.getBinIdFromPrice(lower_price, bin_step, false, decimals_a, decimals_b)
|
|
517
|
+
upper_bin_id = BinUtils.getBinIdFromPrice(upper_price, bin_step, true, decimals_a, decimals_b)
|
|
518
|
+
active_id = BinUtils.getBinIdFromPrice(price, bin_step, true, decimals_a, decimals_b)
|
|
519
|
+
} else {
|
|
520
|
+
lower_bin_id = BinUtils.getBinIdFromPrice(d(1).div(upper_price).toString(), bin_step, false, decimals_a, decimals_b)
|
|
521
|
+
upper_bin_id = BinUtils.getBinIdFromPrice(d(1).div(lower_price).toString(), bin_step, true, decimals_a, decimals_b)
|
|
522
|
+
active_id = BinUtils.getBinIdFromPrice(d(1).div(price).toString(), bin_step, false, decimals_a, decimals_b)
|
|
523
|
+
|
|
524
|
+
const calculateOption: CalculateAddLiquidityOption = {
|
|
525
|
+
amount_a: bin_infos.amount_b,
|
|
526
|
+
amount_b: bin_infos.amount_a,
|
|
527
|
+
active_id,
|
|
528
|
+
bin_step,
|
|
529
|
+
lower_bin_id,
|
|
530
|
+
upper_bin_id,
|
|
531
|
+
amount_a_in_active_bin,
|
|
532
|
+
amount_b_in_active_bin,
|
|
533
|
+
strategy_type: strategy_type,
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
new_bin_infos = this.sdk.Position.calculateAddLiquidityInfo(calculateOption)
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const openAndAddLiquidityOption: OpenAndAddLiquidityOption = {
|
|
540
|
+
pool_id,
|
|
541
|
+
active_id,
|
|
542
|
+
bin_infos: new_bin_infos,
|
|
543
|
+
coin_type_a,
|
|
544
|
+
coin_type_b,
|
|
545
|
+
lower_bin_id,
|
|
546
|
+
upper_bin_id,
|
|
547
|
+
strategy_type,
|
|
548
|
+
use_bin_infos,
|
|
549
|
+
max_price_slippage,
|
|
550
|
+
bin_step,
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return this.addLiquidityPayload(openAndAddLiquidityOption)
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Add liquidity
|
|
558
|
+
* @param option - The option for adding liquidity
|
|
559
|
+
* @returns The transaction object
|
|
560
|
+
*/
|
|
561
|
+
addLiquidityPayload(option: AddLiquidityOption | OpenAndAddLiquidityOption, tx?: Transaction): Transaction {
|
|
562
|
+
const {
|
|
563
|
+
pool_id,
|
|
564
|
+
bin_infos,
|
|
565
|
+
coin_type_a,
|
|
566
|
+
coin_type_b,
|
|
567
|
+
active_id,
|
|
568
|
+
strategy_type,
|
|
569
|
+
max_price_slippage,
|
|
570
|
+
bin_step,
|
|
571
|
+
use_bin_infos = false,
|
|
572
|
+
} = option
|
|
573
|
+
tx = tx || new Transaction()
|
|
574
|
+
|
|
575
|
+
const isOpenPosition = 'lower_bin_id' in option
|
|
576
|
+
|
|
577
|
+
const liquidity_bins: BinLiquidityInfo[] = []
|
|
578
|
+
|
|
579
|
+
if (isOpenPosition) {
|
|
580
|
+
const position_bins = BinUtils.splitBinLiquidityInfo(bin_infos, option.lower_bin_id, option.upper_bin_id)
|
|
581
|
+
liquidity_bins.push(...position_bins)
|
|
582
|
+
} else {
|
|
583
|
+
const position_id = option.position_id
|
|
584
|
+
liquidity_bins.push(bin_infos)
|
|
585
|
+
|
|
586
|
+
if (option.collect_fee || option.reward_coins.length > 0) {
|
|
587
|
+
this.updatePositionFeeAndRewards({ pool_id: pool_id as string, position_id, coin_type_a, coin_type_b }, tx)
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (option.collect_fee) {
|
|
591
|
+
this.collectFeePayload({ pool_id: pool_id as string, position_id, coin_type_a, coin_type_b }, tx)
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (option.reward_coins.length > 0) {
|
|
595
|
+
this.collectRewardPayload(
|
|
596
|
+
[{ pool_id: pool_id as string, position_id, reward_coins: option.reward_coins, coin_type_a, coin_type_b }],
|
|
597
|
+
tx
|
|
598
|
+
)
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
liquidity_bins.forEach((liquidity_bin, index) => {
|
|
603
|
+
console.log('🚀 ~ PositionModule ~ addLiquidityPayload ~ liquidity_bin:', index, liquidity_bin)
|
|
604
|
+
const { amount_a, amount_b, bins } = liquidity_bin
|
|
605
|
+
|
|
606
|
+
const coin_a_obj_id = CoinAssist.buildCoinWithBalance(BigInt(amount_a), coin_type_a, tx)
|
|
607
|
+
const coin_b_obj_id = CoinAssist.buildCoinWithBalance(BigInt(amount_b), coin_type_b, tx)
|
|
608
|
+
|
|
609
|
+
if (use_bin_infos) {
|
|
610
|
+
this.addLiquidityInternal({
|
|
611
|
+
pool_id,
|
|
612
|
+
coin_type_a,
|
|
613
|
+
coin_type_b,
|
|
614
|
+
active_id,
|
|
615
|
+
liquidity_bin,
|
|
616
|
+
tx,
|
|
617
|
+
coin_a_obj_id,
|
|
618
|
+
coin_b_obj_id,
|
|
619
|
+
position_id: isOpenPosition ? undefined : option.position_id,
|
|
620
|
+
max_price_slippage,
|
|
621
|
+
bin_step,
|
|
622
|
+
})
|
|
623
|
+
} else {
|
|
624
|
+
this.addLiquidityStrategyInternal({
|
|
625
|
+
pool_id,
|
|
626
|
+
coin_type_a,
|
|
627
|
+
coin_type_b,
|
|
628
|
+
active_id,
|
|
629
|
+
liquidity_bin,
|
|
630
|
+
tx,
|
|
631
|
+
max_price_slippage,
|
|
632
|
+
bin_step,
|
|
633
|
+
coin_a_obj_id,
|
|
634
|
+
coin_b_obj_id,
|
|
635
|
+
strategy_type,
|
|
636
|
+
position_id: isOpenPosition ? undefined : option.position_id,
|
|
637
|
+
})
|
|
638
|
+
}
|
|
639
|
+
})
|
|
640
|
+
|
|
641
|
+
return tx
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
private addLiquidityStrategyInternal(option: {
|
|
645
|
+
pool_id: string | TransactionObjectArgument
|
|
646
|
+
coin_type_a: string
|
|
647
|
+
coin_type_b: string
|
|
648
|
+
active_id: number
|
|
649
|
+
liquidity_bin: BinLiquidityInfo
|
|
650
|
+
tx: Transaction
|
|
651
|
+
coin_a_obj_id: TransactionObjectArgument
|
|
652
|
+
coin_b_obj_id: TransactionObjectArgument
|
|
653
|
+
position_id?: string
|
|
654
|
+
strategy_type: StrategyType
|
|
655
|
+
max_price_slippage: number
|
|
656
|
+
bin_step: number
|
|
657
|
+
}): Transaction {
|
|
658
|
+
const {
|
|
659
|
+
max_price_slippage,
|
|
660
|
+
bin_step,
|
|
661
|
+
position_id,
|
|
662
|
+
pool_id,
|
|
663
|
+
coin_type_a,
|
|
664
|
+
coin_type_b,
|
|
665
|
+
active_id,
|
|
666
|
+
liquidity_bin,
|
|
667
|
+
tx,
|
|
668
|
+
coin_a_obj_id,
|
|
669
|
+
coin_b_obj_id,
|
|
670
|
+
strategy_type,
|
|
671
|
+
} = option
|
|
672
|
+
|
|
673
|
+
const { dlmm_pool, dlmm_router } = this.sdk.sdkOptions
|
|
674
|
+
const { versioned_id, global_config_id } = getPackagerConfigs(dlmm_pool)
|
|
675
|
+
|
|
676
|
+
const { bins, amount_a, amount_b } = liquidity_bin
|
|
677
|
+
|
|
678
|
+
let position: string | undefined | TransactionObjectArgument = position_id
|
|
679
|
+
|
|
680
|
+
const lower_bin_id = bins[0].bin_id
|
|
681
|
+
const upper_bin_id = bins[bins.length - 1].bin_id
|
|
682
|
+
const lower_bin_id_u32 = asUintN(BigInt(lower_bin_id))
|
|
683
|
+
|
|
684
|
+
const active_id_u32 = Number(asUintN(BigInt(active_id)))
|
|
685
|
+
const bin_shift = BinUtils.getBinShift(active_id, bin_step, max_price_slippage)
|
|
686
|
+
const routerModule = getRouterModule(strategy_type)
|
|
687
|
+
|
|
688
|
+
if (position_id === undefined) {
|
|
689
|
+
const width = upper_bin_id - lower_bin_id + 1
|
|
690
|
+
if (width > MAX_BIN_PER_POSITION) {
|
|
691
|
+
handleError(DlmmErrorCode.InvalidBinWidth, new Error('Width is too large'), {
|
|
692
|
+
[DETAILS_KEYS.METHOD_NAME]: 'openPosition',
|
|
693
|
+
})
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
position = tx.moveCall({
|
|
697
|
+
target: `${dlmm_router.published_at}::${routerModule}::open_position`,
|
|
698
|
+
arguments: [
|
|
699
|
+
typeof pool_id === 'string' ? tx.object(pool_id) : pool_id,
|
|
700
|
+
coin_a_obj_id,
|
|
701
|
+
coin_b_obj_id,
|
|
702
|
+
tx.pure.u64(amount_a),
|
|
703
|
+
tx.pure.u64(amount_b),
|
|
704
|
+
tx.pure.u32(Number(lower_bin_id_u32)),
|
|
705
|
+
tx.pure.u16(Number(width)),
|
|
706
|
+
tx.pure.u32(Number(active_id_u32)),
|
|
707
|
+
tx.pure.u32(Number(bin_shift)),
|
|
708
|
+
tx.object(global_config_id),
|
|
709
|
+
tx.object(versioned_id),
|
|
710
|
+
tx.object(CLOCK_ADDRESS),
|
|
711
|
+
],
|
|
712
|
+
typeArguments: [coin_type_a, coin_type_b],
|
|
713
|
+
})
|
|
714
|
+
} else {
|
|
715
|
+
const valid_bins = bins.filter((bin) => bin.amount_a !== '0' || bin.amount_b !== '0')
|
|
716
|
+
if (valid_bins.length === 0) {
|
|
717
|
+
return handleError(DlmmErrorCode.InvalidParams, new Error('No bins to add liquidity'), {
|
|
718
|
+
[DETAILS_KEYS.METHOD_NAME]: 'addLiquidityStrategyInternal',
|
|
719
|
+
[DETAILS_KEYS.REQUEST_PARAMS]: option,
|
|
720
|
+
})
|
|
721
|
+
}
|
|
722
|
+
const valid_lower_bin_id_u32 = asUintN(BigInt(valid_bins[0].bin_id))
|
|
723
|
+
const valid_upper_bin_id_u32 = asUintN(BigInt(valid_bins[valid_bins.length - 1].bin_id))
|
|
724
|
+
|
|
725
|
+
tx.moveCall({
|
|
726
|
+
target: `${dlmm_router.published_at}::${routerModule}::add_liquidity`,
|
|
727
|
+
arguments: [
|
|
728
|
+
typeof pool_id === 'string' ? tx.object(pool_id) : pool_id,
|
|
729
|
+
tx.object(position_id!),
|
|
730
|
+
coin_a_obj_id,
|
|
731
|
+
coin_b_obj_id,
|
|
732
|
+
tx.pure.u64(amount_a),
|
|
733
|
+
tx.pure.u64(amount_b),
|
|
734
|
+
tx.pure.u32(Number(valid_lower_bin_id_u32)),
|
|
735
|
+
tx.pure.u32(Number(valid_upper_bin_id_u32)),
|
|
736
|
+
tx.pure.u32(Number(active_id_u32)),
|
|
737
|
+
tx.pure.u32(Number(bin_shift)),
|
|
738
|
+
tx.object(global_config_id),
|
|
739
|
+
tx.object(versioned_id),
|
|
740
|
+
tx.object(CLOCK_ADDRESS),
|
|
741
|
+
],
|
|
742
|
+
typeArguments: [coin_type_a, coin_type_b],
|
|
743
|
+
})
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
if (position) {
|
|
747
|
+
tx.transferObjects([position, coin_a_obj_id, coin_b_obj_id], this.sdk.getSenderAddress())
|
|
748
|
+
} else {
|
|
749
|
+
tx.transferObjects([coin_a_obj_id, coin_b_obj_id], this.sdk.getSenderAddress())
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return tx
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
private addLiquidityInternal(option: {
|
|
756
|
+
pool_id: string | TransactionObjectArgument
|
|
757
|
+
coin_type_a: string
|
|
758
|
+
coin_type_b: string
|
|
759
|
+
active_id: number
|
|
760
|
+
liquidity_bin: BinLiquidityInfo
|
|
761
|
+
tx: Transaction
|
|
762
|
+
coin_a_obj_id: TransactionObjectArgument
|
|
763
|
+
coin_b_obj_id: TransactionObjectArgument
|
|
764
|
+
position_id?: string
|
|
765
|
+
max_price_slippage: number
|
|
766
|
+
bin_step: number
|
|
767
|
+
}): Transaction {
|
|
768
|
+
const {
|
|
769
|
+
position_id,
|
|
770
|
+
pool_id,
|
|
771
|
+
coin_type_a,
|
|
772
|
+
coin_type_b,
|
|
773
|
+
active_id,
|
|
774
|
+
liquidity_bin,
|
|
775
|
+
tx,
|
|
776
|
+
coin_a_obj_id,
|
|
777
|
+
coin_b_obj_id,
|
|
778
|
+
max_price_slippage,
|
|
779
|
+
bin_step,
|
|
780
|
+
} = option
|
|
781
|
+
const { bins } = liquidity_bin
|
|
782
|
+
|
|
783
|
+
const { dlmm_pool, dlmm_router } = this.sdk.sdkOptions
|
|
784
|
+
const { versioned_id, global_config_id } = getPackagerConfigs(dlmm_pool)
|
|
785
|
+
const amounts_a = tx.pure.vector(
|
|
786
|
+
'u64',
|
|
787
|
+
bins.map((bin) => bin.amount_a)
|
|
788
|
+
)
|
|
789
|
+
const amounts_b = tx.pure.vector(
|
|
790
|
+
'u64',
|
|
791
|
+
bins.map((bin) => bin.amount_b)
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
const bin_ids = tx.makeMoveVec({
|
|
795
|
+
elements: bins.map((bin) => tx.pure.u32(Number(asUintN(BigInt(bin.bin_id))))),
|
|
796
|
+
type: 'u32',
|
|
797
|
+
})
|
|
798
|
+
const lower_bin_id = liquidity_bin.bins[0].bin_id
|
|
799
|
+
const upper_bin_id = liquidity_bin.bins[liquidity_bin.bins.length - 1].bin_id
|
|
800
|
+
if (position_id === undefined) {
|
|
801
|
+
const width = upper_bin_id - lower_bin_id + 1
|
|
802
|
+
if (width > MAX_BIN_PER_POSITION) {
|
|
803
|
+
handleError(DlmmErrorCode.InvalidBinWidth, new Error('Width is too large'), {
|
|
804
|
+
[DETAILS_KEYS.METHOD_NAME]: 'openPosition',
|
|
805
|
+
})
|
|
806
|
+
}
|
|
807
|
+
const open_position_id = tx.moveCall({
|
|
808
|
+
target: `${dlmm_router.published_at}::add_liquidity::open_position`,
|
|
809
|
+
arguments: [
|
|
810
|
+
typeof pool_id === 'string' ? tx.object(pool_id) : pool_id,
|
|
811
|
+
coin_a_obj_id,
|
|
812
|
+
coin_b_obj_id,
|
|
813
|
+
bin_ids,
|
|
814
|
+
amounts_a,
|
|
815
|
+
amounts_b,
|
|
816
|
+
tx.object(global_config_id),
|
|
817
|
+
tx.object(versioned_id),
|
|
818
|
+
tx.object(CLOCK_ADDRESS),
|
|
819
|
+
],
|
|
820
|
+
typeArguments: [coin_type_a, coin_type_b],
|
|
821
|
+
})
|
|
822
|
+
// validate the active id slippage
|
|
823
|
+
if (active_id >= lower_bin_id && active_id <= upper_bin_id) {
|
|
824
|
+
this.validateActiveIdSlippage({ pool_id, active_id, max_price_slippage, bin_step, coin_type_a, coin_type_b }, tx)
|
|
825
|
+
}
|
|
826
|
+
tx.transferObjects([coin_a_obj_id, coin_b_obj_id, open_position_id], this.sdk.getSenderAddress())
|
|
827
|
+
} else {
|
|
828
|
+
tx.moveCall({
|
|
829
|
+
target: `${dlmm_router.published_at}::add_liquidity::add_liquidity`,
|
|
830
|
+
arguments: [
|
|
831
|
+
typeof pool_id === 'string' ? tx.object(pool_id) : pool_id,
|
|
832
|
+
tx.object(position_id!),
|
|
833
|
+
coin_a_obj_id,
|
|
834
|
+
coin_b_obj_id,
|
|
835
|
+
bin_ids,
|
|
836
|
+
amounts_a,
|
|
837
|
+
amounts_b,
|
|
838
|
+
tx.object(global_config_id),
|
|
839
|
+
tx.object(versioned_id),
|
|
840
|
+
tx.object(CLOCK_ADDRESS),
|
|
841
|
+
],
|
|
842
|
+
typeArguments: [coin_type_a, coin_type_b],
|
|
843
|
+
})
|
|
844
|
+
// validate the active id slippage
|
|
845
|
+
if (active_id >= lower_bin_id && active_id <= upper_bin_id) {
|
|
846
|
+
this.validateActiveIdSlippage({ pool_id, active_id, max_price_slippage, bin_step, coin_type_a, coin_type_b }, tx)
|
|
847
|
+
}
|
|
848
|
+
tx.transferObjects([coin_a_obj_id, coin_b_obj_id], this.sdk.getSenderAddress())
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return tx
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Fetch the fee and reward of the position
|
|
856
|
+
* @param options - The option for fetching the fee and reward of the position
|
|
857
|
+
* @returns The fee and reward of the position
|
|
858
|
+
*/
|
|
859
|
+
async fetchPositionFeeAndReward(
|
|
860
|
+
options: CollectRewardAndFeeOption[]
|
|
861
|
+
): Promise<{ feeData: Record<string, PositionFee>; rewardData: Record<string, PositionReward> }> {
|
|
862
|
+
const tx = new Transaction()
|
|
863
|
+
this.collectRewardAndFeePayload(options, tx)
|
|
864
|
+
|
|
865
|
+
const simulateRes = await this.sdk.FullClient.devInspectTransactionBlock({
|
|
866
|
+
transactionBlock: tx,
|
|
867
|
+
sender: normalizeSuiAddress('0x0'),
|
|
868
|
+
})
|
|
869
|
+
|
|
870
|
+
if (simulateRes.error != null) {
|
|
871
|
+
return handleError(DlmmErrorCode.FetchError, new Error(simulateRes.error), {
|
|
872
|
+
[DETAILS_KEYS.METHOD_NAME]: 'fetchPositionFeeAndReward',
|
|
873
|
+
[DETAILS_KEYS.REQUEST_PARAMS]: {
|
|
874
|
+
options,
|
|
875
|
+
totalOptions: options.length,
|
|
876
|
+
},
|
|
877
|
+
})
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
const feeData = parsedDlmmPosFeeData(simulateRes)
|
|
881
|
+
const rewardData = parsedDlmmPosRewardData(simulateRes)
|
|
882
|
+
|
|
883
|
+
return {
|
|
884
|
+
feeData,
|
|
885
|
+
rewardData,
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|