@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.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +10423 -0
  2. package/README.md +646 -0
  3. package/dist/index.d.mts +1015 -0
  4. package/dist/index.d.ts +1015 -0
  5. package/dist/index.js +13 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +13 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +35 -0
  10. package/src/config/index.ts +2 -0
  11. package/src/config/mainnet.ts +25 -0
  12. package/src/config/testnet.ts +30 -0
  13. package/src/errors/errors.ts +40 -0
  14. package/src/index.ts +8 -0
  15. package/src/modules/configModule.ts +184 -0
  16. package/src/modules/index.ts +1 -0
  17. package/src/modules/partnerModule.ts +302 -0
  18. package/src/modules/poolModule.ts +578 -0
  19. package/src/modules/positionModule.ts +888 -0
  20. package/src/modules/rewardModule.ts +175 -0
  21. package/src/modules/swapModule.ts +129 -0
  22. package/src/sdk.ts +88 -0
  23. package/src/types/constants.ts +23 -0
  24. package/src/types/dlmm.ts +445 -0
  25. package/src/types/index.ts +2 -0
  26. package/src/utils/binUtils.ts +552 -0
  27. package/src/utils/feeUtils.ts +92 -0
  28. package/src/utils/index.ts +5 -0
  29. package/src/utils/parseData.ts +519 -0
  30. package/src/utils/strategyUtils.ts +121 -0
  31. package/src/utils/weightUtils.ts +510 -0
  32. package/tests/add_liquidity_bidask.test.ts +180 -0
  33. package/tests/add_liquidity_curve.test.ts +244 -0
  34. package/tests/add_liquidity_spot.test.ts +262 -0
  35. package/tests/bin.test.ts +80 -0
  36. package/tests/config.test.ts +51 -0
  37. package/tests/partner.test.ts +74 -0
  38. package/tests/pool.test.ts +174 -0
  39. package/tests/position.test.ts +76 -0
  40. package/tests/remove_liquidity.test.ts +137 -0
  41. package/tests/swap.test.ts +96 -0
  42. package/tests/tsconfig.json +26 -0
  43. package/tsconfig.json +5 -0
  44. package/tsup.config.ts +9 -0
@@ -0,0 +1,510 @@
1
+ import { d, DETAILS_KEYS } from '@cetusprotocol/common-sdk'
2
+ import { BinAmount, BinLiquidityInfo, BinWeight, StrategyType } from '../types/dlmm'
3
+ import Decimal from 'decimal.js'
4
+ import { DlmmErrorCode, handleError } from '../errors/errors'
5
+ import { DEFAULT_MAX_WEIGHT, DEFAULT_MIN_WEIGHT } from '../types/constants'
6
+ import { BinUtils } from './binUtils'
7
+ import { safeMulAmount } from './parseData'
8
+
9
+ export class WeightUtils {
10
+ static toWeightSpotBalanced(min_bin_id: number, max_bin_id: number): BinWeight[] {
11
+ let distributions = []
12
+ for (let i = min_bin_id; i <= max_bin_id; i++) {
13
+ distributions.push({
14
+ bin_id: i,
15
+ weight: 1,
16
+ })
17
+ }
18
+ return distributions
19
+ }
20
+
21
+ static toWeightDescendingOrder(min_bin_id: number, max_bin_id: number): BinWeight[] {
22
+ let distributions = []
23
+ for (let i = min_bin_id; i <= max_bin_id; i++) {
24
+ distributions.push({
25
+ bin_id: i,
26
+ weight: max_bin_id - i + 1,
27
+ })
28
+ }
29
+ return distributions
30
+ }
31
+
32
+ static toWeightAscendingOrder(min_bin_id: number, max_bin_id: number): BinWeight[] {
33
+ let distributions = []
34
+ for (let i = min_bin_id; i <= max_bin_id; i++) {
35
+ distributions.push({
36
+ bin_id: i,
37
+ weight: i - min_bin_id + 1,
38
+ })
39
+ }
40
+ return distributions
41
+ }
42
+
43
+ static toWeightCurve(min_bin_id: number, max_bin_id: number, active_id: number): BinWeight[] {
44
+ if (active_id < min_bin_id) {
45
+ return WeightUtils.toWeightDescendingOrder(min_bin_id, max_bin_id)
46
+ } else if (active_id > max_bin_id) {
47
+ return WeightUtils.toWeightAscendingOrder(min_bin_id, max_bin_id)
48
+ }
49
+
50
+ let maxWeight = DEFAULT_MAX_WEIGHT
51
+ let minWeight = DEFAULT_MIN_WEIGHT
52
+
53
+ let diffWeight = maxWeight - minWeight
54
+ let diffMinWeight = active_id > min_bin_id ? Math.floor(diffWeight / (active_id - min_bin_id)) : 0
55
+ let diffMaxWeight = max_bin_id > active_id ? Math.floor(diffWeight / (max_bin_id - active_id)) : 0
56
+
57
+ let distributions: BinWeight[] = []
58
+ for (let i = min_bin_id; i <= max_bin_id; i++) {
59
+ if (i < active_id) {
60
+ distributions.push({
61
+ bin_id: i,
62
+ weight: maxWeight - (active_id - i) * diffMinWeight,
63
+ })
64
+ } else if (i > active_id) {
65
+ distributions.push({
66
+ bin_id: i,
67
+ weight: maxWeight - (i - active_id) * diffMaxWeight,
68
+ })
69
+ } else {
70
+ distributions.push({
71
+ bin_id: i,
72
+ weight: maxWeight,
73
+ })
74
+ }
75
+ }
76
+ return distributions
77
+ }
78
+
79
+ static toWeightBidAsk(min_bin_id: number, max_bin_id: number, active_id: number): BinWeight[] {
80
+ if (active_id > max_bin_id) {
81
+ return WeightUtils.toWeightDescendingOrder(min_bin_id, max_bin_id)
82
+ } else if (active_id < min_bin_id) {
83
+ return WeightUtils.toWeightAscendingOrder(min_bin_id, max_bin_id)
84
+ }
85
+
86
+ let maxWeight = DEFAULT_MAX_WEIGHT
87
+ let minWeight = DEFAULT_MIN_WEIGHT
88
+
89
+ let diffWeight = maxWeight - minWeight
90
+ let diffMinWeight = active_id > min_bin_id ? Math.floor(diffWeight / (active_id - min_bin_id)) : 0
91
+ let diffMaxWeight = max_bin_id > active_id ? Math.floor(diffWeight / (max_bin_id - active_id)) : 0
92
+
93
+ let distributions: BinWeight[] = []
94
+ for (let i = min_bin_id; i <= max_bin_id; i++) {
95
+ if (i < active_id) {
96
+ distributions.push({
97
+ bin_id: i,
98
+ weight: minWeight + (active_id - i) * diffMinWeight,
99
+ })
100
+ } else if (i > active_id) {
101
+ distributions.push({
102
+ bin_id: i,
103
+ weight: minWeight + (i - active_id) * diffMaxWeight,
104
+ })
105
+ } else {
106
+ distributions.push({
107
+ bin_id: i,
108
+ weight: minWeight,
109
+ })
110
+ }
111
+ }
112
+ return distributions
113
+ }
114
+
115
+ /**
116
+ * Distribute totalAmount to all bid side bins according to given distributions.
117
+ * @param active_id - active bin id
118
+ * @param amount_b - total amount of coin b to be distributed
119
+ * @param distributions - weight distribution of each bin
120
+ * @returns array of {binId, amount} where amount is the amount of coin b in each bin
121
+ */
122
+ static toAmountBidSide(
123
+ active_id: number,
124
+ amount_b: string,
125
+ bin_step: number,
126
+ distributions: BinWeight[],
127
+ contain_active_bin = false
128
+ ): BinLiquidityInfo {
129
+ // get sum of weight
130
+ const totalWeight = distributions
131
+ .filter((bin) => bin.bin_id <= active_id)
132
+ .reduce(function (sum, el) {
133
+ if (contain_active_bin) {
134
+ return el.bin_id > active_id ? sum : sum.add(el.weight) // skip all ask side
135
+ } else {
136
+ return el.bin_id >= active_id ? sum : sum.add(el.weight) // skip all ask side
137
+ }
138
+ }, d(0))
139
+
140
+ if (totalWeight.cmp(d(0)) != 1) {
141
+ throw Error('Invalid parameters')
142
+ }
143
+
144
+ const bin_amounts = distributions.map((bin) => {
145
+ let price_per_lamport = BinUtils.getPricePerLamportFromBinId(bin.bin_id, bin_step)
146
+
147
+ const isValidBin = bin.bin_id <= active_id
148
+
149
+ if (!isValidBin || (bin.bin_id >= active_id && !contain_active_bin)) {
150
+ return {
151
+ bin_id: bin.bin_id,
152
+ amount_a: '0',
153
+ amount_b: '0',
154
+ price_per_lamport,
155
+ liquidity: '0',
156
+ }
157
+ } else {
158
+ const rate = d(bin.weight).div(totalWeight)
159
+ const amount_b_in_bin = safeMulAmount(d(amount_b), rate).toString()
160
+ const amount_a = '0'
161
+ const qPrice = BinUtils.getQPriceFromId(bin.bin_id, bin_step)
162
+ const liquidity = BinUtils.getLiquidity(amount_a, amount_b_in_bin, qPrice)
163
+ return {
164
+ bin_id: bin.bin_id,
165
+ amount_b: amount_b_in_bin,
166
+ amount_a,
167
+ price_per_lamport,
168
+ liquidity,
169
+ }
170
+ }
171
+ })
172
+
173
+ return {
174
+ bins: bin_amounts,
175
+ amount_a: '0',
176
+ amount_b,
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Distribute totalAmount to all ask side bins according to given distributions.
182
+ * @param active_id active bin id
183
+ * @param amount_a total amount of coin a to be distributed
184
+ * @param distributions weight distribution of each bin
185
+ * @returns array of {binId, amount} where amount is the amount of coin a in each bin
186
+ */
187
+ static toAmountAskSide(
188
+ active_id: number,
189
+ bin_step: number,
190
+ amount_a: string,
191
+ distributions: BinWeight[],
192
+ contain_active_bin = false
193
+ ): BinLiquidityInfo {
194
+ // get sum of weight
195
+ const totalWeight: Decimal = distributions
196
+ .filter((bin) => bin.bin_id >= active_id)
197
+ .reduce(function (sum, el) {
198
+ if (el.bin_id <= active_id && !contain_active_bin) {
199
+ return sum
200
+ } else {
201
+ const price_per_lamport = BinUtils.getPricePerLamportFromBinId(el.bin_id, bin_step)
202
+ const weightPerPrice = new Decimal(el.weight).div(price_per_lamport)
203
+ return sum.add(weightPerPrice)
204
+ }
205
+ }, new Decimal(0))
206
+
207
+ if (totalWeight.cmp(new Decimal(0)) != 1) {
208
+ throw Error('Invalid parameters')
209
+ }
210
+
211
+ const bin_amounts = distributions.map((bin) => {
212
+ let price_per_lamport = BinUtils.getPricePerLamportFromBinId(bin.bin_id, bin_step)
213
+ const isValidBin = bin.bin_id >= active_id
214
+
215
+ if (!isValidBin || (bin.bin_id <= active_id && !contain_active_bin)) {
216
+ return {
217
+ bin_id: bin.bin_id,
218
+ amount_a: '0',
219
+ amount_b: '0',
220
+ price_per_lamport,
221
+ liquidity: '0',
222
+ }
223
+ } else {
224
+ const weightPerPrice = new Decimal(bin.weight).div(price_per_lamport)
225
+ const rate = weightPerPrice.div(totalWeight)
226
+ const amount_a_in_bin = safeMulAmount(d(amount_a), rate).toString()
227
+ const amount_b = '0'
228
+ const qPrice = BinUtils.getQPriceFromId(bin.bin_id, bin_step)
229
+ const liquidity = BinUtils.getLiquidity(amount_a_in_bin, amount_b, qPrice)
230
+ return {
231
+ bin_id: bin.bin_id,
232
+ amount_a: amount_a_in_bin,
233
+ amount_b,
234
+ price_per_lamport,
235
+ liquidity,
236
+ }
237
+ }
238
+ })
239
+
240
+ return {
241
+ bins: bin_amounts,
242
+ amount_a,
243
+ amount_b: '0',
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Distributes the given amounts of tokens X and Y to both bid and ask side bins
249
+ * based on the provided weight distributions.
250
+ *
251
+ * @param activeId - The id of the active bin.
252
+ * @param binStep - The step interval between bin ids.
253
+ * @param amountX - Total amount of token X to distribute.
254
+ * @param amountY - Total amount of token Y to distribute.
255
+ * @param amountXInActiveBin - Amount of token X already in the active bin.
256
+ * @param amountYInActiveBin - Amount of token Y already in the active bin.
257
+ * @param distributions - Array of bins with their respective weight distributions.
258
+ * @param mintX - Mint information for token X. Get from DLMM instance.
259
+ * @param mintY - Mint information for token Y. Get from DLMM instance.
260
+ * @param clock - Clock instance. Get from DLMM instance.
261
+ * @returns An array of objects containing binId, amountX, and amountY for each bin.
262
+ */
263
+ static toAmountBothSide(
264
+ active_id: number,
265
+ bin_step: number,
266
+ amount_a: string,
267
+ amount_b: string,
268
+ amount_a_in_active_bin: string,
269
+ amount_b_in_active_bin: string,
270
+ distributions: BinWeight[]
271
+ ): BinLiquidityInfo {
272
+ const isOnlyAmountA = !d(amount_a).isZero() && d(amount_b).isZero()
273
+ const isOnlyAmountB = d(amount_a).isZero() && !d(amount_b).isZero()
274
+
275
+ // only bid side
276
+ if (active_id > distributions[distributions.length - 1].bin_id) {
277
+ return WeightUtils.toAmountBidSide(active_id, amount_b, bin_step, distributions)
278
+ }
279
+
280
+ if (isOnlyAmountB && active_id !== distributions[distributions.length - 1].bin_id) {
281
+ return WeightUtils.toAmountBidSide(active_id, amount_b, bin_step, distributions, true)
282
+ }
283
+
284
+ // only ask side
285
+ if (active_id < distributions[0].bin_id) {
286
+ return WeightUtils.toAmountAskSide(active_id, bin_step, amount_a, distributions)
287
+ }
288
+
289
+ if (isOnlyAmountA && active_id !== distributions[0].bin_id) {
290
+ return WeightUtils.toAmountAskSide(active_id, bin_step, amount_a, distributions, true)
291
+ }
292
+
293
+ const activeBins = distributions.filter((element) => {
294
+ return element.bin_id === active_id
295
+ })
296
+
297
+ if (activeBins.length === 1) {
298
+ const { totalWeightA, totalWeightB, activeWeightA, activeWeightB } = WeightUtils.calculateTotalWeights(
299
+ bin_step,
300
+ distributions,
301
+ active_id,
302
+ activeBins[0],
303
+ amount_a_in_active_bin,
304
+ amount_b_in_active_bin,
305
+ isOnlyAmountA ? 'a' : isOnlyAmountB ? 'b' : undefined
306
+ )
307
+ const kA = new Decimal(amount_a.toString()).div(totalWeightA)
308
+ const kB = new Decimal(amount_b.toString()).div(totalWeightB)
309
+ const bin_amounts = distributions.map((bin) => {
310
+ let price_per_lamport = BinUtils.getPricePerLamportFromBinId(bin.bin_id, bin_step)
311
+ if (bin.bin_id < active_id || (bin.bin_id === active_id && isOnlyAmountB)) {
312
+ const amount_b = safeMulAmount(kB, new Decimal(bin.weight))
313
+ const amount_a = '0'
314
+ const qPrice = BinUtils.getQPriceFromId(bin.bin_id, bin_step)
315
+ const liquidity = BinUtils.getLiquidity(amount_a, amount_b.toString(), qPrice)
316
+ return {
317
+ bin_id: bin.bin_id,
318
+ amount_a: '0',
319
+ amount_b: amount_b.toString(),
320
+ price_per_lamport,
321
+ liquidity,
322
+ }
323
+ }
324
+ if (bin.bin_id > active_id || (bin.bin_id === active_id && isOnlyAmountA)) {
325
+ const weighPerPrice = new Decimal(bin.weight).div(price_per_lamport)
326
+ const amount_a = safeMulAmount(kA, new Decimal(weighPerPrice))
327
+ const amount_b = '0'
328
+ const qPrice = BinUtils.getQPriceFromId(bin.bin_id, bin_step)
329
+ const liquidity = BinUtils.getLiquidity(amount_a.toString(), amount_b, qPrice)
330
+
331
+ return {
332
+ bin_id: bin.bin_id,
333
+ amount_a: amount_a.toString(),
334
+ amount_b: '0',
335
+ price_per_lamport,
336
+ liquidity,
337
+ }
338
+ }
339
+
340
+ const amountAActiveBin = safeMulAmount(kA, activeWeightA)
341
+ const amountBActiveBin = safeMulAmount(kB, activeWeightB)
342
+ let amount_a = amountAActiveBin.toString()
343
+ let amount_b = amountBActiveBin.toString()
344
+
345
+ const qPrice = BinUtils.getQPriceFromId(bin.bin_id, bin_step)
346
+ const liquidity = BinUtils.getLiquidity(amount_a, amount_b, qPrice)
347
+ return {
348
+ bin_id: bin.bin_id,
349
+ amount_a,
350
+ amount_b,
351
+ price_per_lamport,
352
+ liquidity,
353
+ }
354
+ })
355
+
356
+ const total_amount_a = bin_amounts.reduce((sum, bin) => d(sum).add(d(bin.amount_a)), d(0)).toString()
357
+ const total_amount_b = bin_amounts.reduce((sum, bin) => d(sum).add(d(bin.amount_b)), d(0)).toString()
358
+
359
+ return {
360
+ bins: bin_amounts,
361
+ amount_a: total_amount_a,
362
+ amount_b: total_amount_b,
363
+ }
364
+ } else {
365
+ const { totalWeightA, totalWeightB } = WeightUtils.calculateTotalWeights(bin_step, distributions, active_id)
366
+ let kA = new Decimal(amount_a.toString()).div(totalWeightA)
367
+ let kB = new Decimal(amount_b.toString()).div(totalWeightB)
368
+ // let k = kA.lessThan(kB) ? kA : kB
369
+
370
+ const bin_amounts = distributions.map((bin) => {
371
+ let price_per_lamport = BinUtils.getPricePerLamportFromBinId(bin.bin_id, bin_step)
372
+ if (bin.bin_id < active_id) {
373
+ const amount = safeMulAmount(kB, new Decimal(bin.weight))
374
+ return {
375
+ bin_id: bin.bin_id,
376
+ amount_a: '0',
377
+ amount_b: amount.toString(),
378
+ price_per_lamport,
379
+ }
380
+ } else {
381
+ let weighPerPrice = new Decimal(bin.weight).div(price_per_lamport)
382
+ const amount = safeMulAmount(kA, weighPerPrice)
383
+ return {
384
+ bin_id: bin.bin_id,
385
+ amount_a: amount.toString(),
386
+ amount_b: '0',
387
+ price_per_lamport,
388
+ }
389
+ }
390
+ })
391
+
392
+ const total_amount_a = bin_amounts.reduce((sum, bin) => d(sum).add(d(bin.amount_a)), d(0)).toString()
393
+ const total_amount_b = bin_amounts.reduce((sum, bin) => d(sum).add(d(bin.amount_b)), d(0)).toString()
394
+
395
+ return {
396
+ bins: bin_amounts,
397
+ amount_a: total_amount_a,
398
+ amount_b: total_amount_b,
399
+ }
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Distributes the given amount of coin B to both bid and ask side bins
405
+ * based on the provided weight distributions.
406
+ *
407
+ * @param active_id - The id of the active bin.
408
+ * @param bin_step - The step interval between bin ids.
409
+ * @param amount_a - Total amount of coin A to distribute.
410
+ * @param amount_a_in_active_bin - Amount of coin A already in the active bin.
411
+ * @param amount_b_in_active_bin - Amount of coin B already in the active bin.
412
+ * @param distributions - Array of bins with their respective weight distributions.
413
+ * @returns An array of objects containing binId, amountA, and amountB for each bin.
414
+ */
415
+ static autoFillCoinByWeight(
416
+ active_id: number,
417
+ bin_step: number,
418
+ amount: string,
419
+ fix_amount_a: boolean,
420
+ amount_a_in_active_bin: string,
421
+ amount_b_in_active_bin: string,
422
+ distributions: BinWeight[]
423
+ ): BinLiquidityInfo {
424
+ // only bid side
425
+ if (active_id > distributions[distributions.length - 1].bin_id) {
426
+ return WeightUtils.toAmountBidSide(active_id, amount, bin_step, distributions)
427
+ }
428
+ // only ask side
429
+ if (active_id < distributions[0].bin_id) {
430
+ return WeightUtils.toAmountAskSide(active_id, bin_step, amount, distributions)
431
+ }
432
+
433
+ const activeBins = distributions.filter((element) => {
434
+ return element.bin_id === active_id
435
+ })
436
+
437
+ const { totalWeightA, totalWeightB } = WeightUtils.calculateTotalWeights(
438
+ bin_step,
439
+ distributions,
440
+ active_id,
441
+ activeBins.length === 1 ? activeBins[0] : undefined,
442
+ activeBins.length === 1 ? amount_a_in_active_bin : undefined,
443
+ activeBins.length === 1 ? amount_b_in_active_bin : undefined
444
+ )
445
+
446
+ let k = d(0)
447
+
448
+ if (fix_amount_a) {
449
+ k = totalWeightA.isZero() ? new Decimal(1) : new Decimal(amount).div(totalWeightA)
450
+ } else {
451
+ k = totalWeightB.isZero() ? new Decimal(1) : new Decimal(amount).div(totalWeightB)
452
+ }
453
+ const other_amount = safeMulAmount(k, fix_amount_a ? totalWeightB : totalWeightA).toString()
454
+
455
+ return WeightUtils.toAmountBothSide(
456
+ active_id,
457
+ bin_step,
458
+ fix_amount_a ? amount : other_amount,
459
+ fix_amount_a ? other_amount : amount,
460
+ amount_a_in_active_bin,
461
+ amount_b_in_active_bin,
462
+ distributions
463
+ )
464
+ }
465
+
466
+ static calculateTotalWeights(
467
+ bin_step: number,
468
+ distributions: BinWeight[],
469
+ active_id: number,
470
+ activeBin?: BinWeight,
471
+ amount_a_in_active_bin?: string,
472
+ amount_b_in_active_bin?: string,
473
+ is_only_amount?: 'a' | 'b'
474
+ ): { totalWeightA: Decimal; totalWeightB: Decimal; activeWeightA: Decimal; activeWeightB: Decimal } {
475
+ const p0 = d(BinUtils.getPricePerLamportFromBinId(active_id, bin_step))
476
+ let activeWeightA = d(0)
477
+ let activeWeightB = d(0)
478
+
479
+ if (amount_a_in_active_bin && amount_b_in_active_bin && activeBin && !is_only_amount) {
480
+ if (d(amount_a_in_active_bin).isZero() && d(amount_b_in_active_bin).isZero()) {
481
+ activeWeightA = new Decimal(activeBin.weight).div(p0.mul(new Decimal(2)))
482
+ activeWeightB = new Decimal(activeBin.weight).div(new Decimal(2))
483
+ } else {
484
+ let amountAInActiveBinDec = new Decimal(amount_a_in_active_bin.toString())
485
+ let amountBInActiveBinDec = new Decimal(amount_b_in_active_bin.toString())
486
+
487
+ if (!d(amount_a_in_active_bin).isZero()) {
488
+ activeWeightA = new Decimal(activeBin.weight).div(p0.add(amountBInActiveBinDec.div(amountAInActiveBinDec)))
489
+ }
490
+ if (!d(amount_b_in_active_bin).isZero()) {
491
+ activeWeightB = new Decimal(activeBin.weight).div(new Decimal(1).add(p0.mul(amountAInActiveBinDec).div(amountBInActiveBinDec)))
492
+ }
493
+ }
494
+ }
495
+
496
+ let totalWeightA = activeWeightA
497
+ let totalWeightB = activeWeightB
498
+ distributions.forEach((element) => {
499
+ if (element.bin_id < active_id || is_only_amount === 'b') {
500
+ totalWeightB = totalWeightB.add(new Decimal(element.weight))
501
+ }
502
+ if (element.bin_id > active_id || is_only_amount === 'a') {
503
+ let price_per_lamport = BinUtils.getPricePerLamportFromBinId(element.bin_id, bin_step)
504
+ let weighPerPrice = new Decimal(element.weight).div(price_per_lamport)
505
+ totalWeightA = totalWeightA.add(weighPerPrice)
506
+ }
507
+ })
508
+ return { totalWeightA, totalWeightB, activeWeightA, activeWeightB }
509
+ }
510
+ }
@@ -0,0 +1,180 @@
1
+ // buildTestAccount
2
+ import type { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'
3
+ import { buildTestAccount } from '@cetusprotocol/test-utils'
4
+ import { CetusDlmmSDK } from '../src/sdk'
5
+ import {
6
+ AddLiquidityOption,
7
+ CalculateAddLiquidityAutoFillOption,
8
+ CalculateAddLiquidityOption,
9
+ DlmmPool,
10
+ OpenAndAddLiquidityOption,
11
+ StrategyType,
12
+ } from '../src/types/dlmm'
13
+ import { printTransaction } from '@cetusprotocol/common-sdk'
14
+
15
+ const pool_id = '0x56d2a3270238f1347fa6fb94836da8afd2415c49ab7bb843996fe5c6a807dc5a'
16
+ const position_id = '0x081a3fda4c7df0fc86ff7f69809434ac096911eb7b5e81865cdd1bf8858214fa'
17
+
18
+ describe('dlmm add liquidity bid ask', () => {
19
+ const sdk = CetusDlmmSDK.createSDK({ env: 'testnet', full_rpc_url: 'https://rpc-testnet.suiscan.xyz' })
20
+ let send_key_pair: Ed25519Keypair
21
+ let account: string
22
+ let pool: DlmmPool
23
+
24
+ beforeEach(async () => {
25
+ send_key_pair = buildTestAccount()
26
+ account = send_key_pair.getPublicKey().toSuiAddress()
27
+ sdk.setSenderAddress(account)
28
+ })
29
+
30
+ test('1 bid ask both amounts open and add liquidity', async () => {
31
+ pool = await sdk.Pool.getPool(pool_id)
32
+ console.log('🚀 ~ beforeEach ~ pool:', pool)
33
+ const { active_id, bin_step } = pool
34
+ const amount_a = '1000000'
35
+ const amount_b = '1200000'
36
+ const lower_bin_id = -10
37
+ const upper_bin_id = 10
38
+
39
+ const amounts_in_active_bin = await sdk.Position.getActiveBinIfInRange(
40
+ pool.bin_manager.bin_manager_handle,
41
+ lower_bin_id,
42
+ upper_bin_id,
43
+ active_id,
44
+ bin_step
45
+ )
46
+
47
+ const calculateOption: CalculateAddLiquidityOption = {
48
+ amount_a,
49
+ amount_b,
50
+ active_id,
51
+ bin_step,
52
+ lower_bin_id,
53
+ upper_bin_id,
54
+ amount_a_in_active_bin: amounts_in_active_bin?.amount_a || '0',
55
+ amount_b_in_active_bin: amounts_in_active_bin?.amount_b || '0',
56
+ strategy_type: StrategyType.BidAsk,
57
+ }
58
+ const bin_infos = sdk.Position.calculateAddLiquidityInfo(calculateOption)
59
+ console.log('🚀 ~ test ~ bin_infos:', bin_infos)
60
+
61
+ const addOption: OpenAndAddLiquidityOption = {
62
+ pool_id,
63
+ bin_infos: bin_infos,
64
+ coin_type_a: pool.coin_type_a,
65
+ coin_type_b: pool.coin_type_b,
66
+ lower_bin_id,
67
+ upper_bin_id,
68
+ active_id,
69
+ }
70
+ const tx = sdk.Position.addLiquidityPayload(addOption)
71
+
72
+ printTransaction(tx)
73
+
74
+ const res = await sdk.FullClient.executeTx(send_key_pair, tx, false)
75
+ console.log('🚀 ~ test ~ res:', res)
76
+ })
77
+
78
+ test('2 bid ask strategy both amounts add liquidity', async () => {
79
+ pool = await sdk.Pool.getPool(pool_id)
80
+ const { active_id, bin_step, bin_manager, coin_type_a, coin_type_b } = pool
81
+ console.log('🚀 ~ pool:', pool)
82
+
83
+ const position = await sdk.Position.getPosition(position_id)
84
+ const { lower_bin_id, upper_bin_id } = position
85
+ console.log('🚀 ~ position:', position)
86
+
87
+ const amounts_in_active_bin = await sdk.Position.getActiveBinIfInRange(
88
+ bin_manager.bin_manager_handle,
89
+ lower_bin_id,
90
+ upper_bin_id,
91
+ active_id,
92
+ bin_step
93
+ )
94
+
95
+ const amount_a = '1000000'
96
+ const amount_b = '1200000'
97
+
98
+ const calculateOption: CalculateAddLiquidityOption = {
99
+ amount_a,
100
+ amount_b,
101
+ active_id,
102
+ bin_step,
103
+ lower_bin_id,
104
+ upper_bin_id,
105
+ amount_a_in_active_bin: amounts_in_active_bin?.amount_a || '0',
106
+ amount_b_in_active_bin: amounts_in_active_bin?.amount_b || '0',
107
+ strategy_type: StrategyType.BidAsk,
108
+ }
109
+ const bin_infos = sdk.Position.calculateAddLiquidityInfo(calculateOption)
110
+ console.log('🚀 ~ test ~ bin_infos:', bin_infos)
111
+
112
+ const addOption: AddLiquidityOption = {
113
+ pool_id,
114
+ bin_infos: bin_infos,
115
+ coin_type_a,
116
+ coin_type_b,
117
+ active_id,
118
+ position_id,
119
+ collect_fee: true,
120
+ reward_coins: [],
121
+ }
122
+ const tx = sdk.Position.addLiquidityPayload(addOption)
123
+
124
+ printTransaction(tx)
125
+
126
+ const res = await sdk.FullClient.executeTx(send_key_pair, tx, false)
127
+ console.log('🚀 ~ test ~ res:', res)
128
+ })
129
+
130
+ test('3 bid ask strategy fix coin a add liquidity', async () => {
131
+ pool = await sdk.Pool.getPool(pool_id)
132
+ const { active_id, bin_step, bin_manager, coin_type_a, coin_type_b } = pool
133
+ console.log('🚀 ~ pool:', pool)
134
+
135
+ const position = await sdk.Position.getPosition(position_id)
136
+ const { lower_bin_id, upper_bin_id } = position
137
+ console.log('🚀 ~ position:', position)
138
+
139
+ const amounts_in_active_bin = await sdk.Position.getActiveBinIfInRange(
140
+ bin_manager.bin_manager_handle,
141
+ lower_bin_id,
142
+ upper_bin_id,
143
+ active_id,
144
+ bin_step
145
+ )
146
+
147
+ const coin_amount = '2000000'
148
+
149
+ const calculateOption: CalculateAddLiquidityAutoFillOption = {
150
+ coin_amount,
151
+ fix_amount_a: true,
152
+ active_id,
153
+ bin_step,
154
+ lower_bin_id,
155
+ upper_bin_id,
156
+ amount_a_in_active_bin: amounts_in_active_bin?.amount_a || '0',
157
+ amount_b_in_active_bin: amounts_in_active_bin?.amount_b || '0',
158
+ strategy_type: StrategyType.BidAsk,
159
+ }
160
+ const bin_infos = sdk.Position.calculateAddLiquidityInfo(calculateOption)
161
+ console.log('🚀 ~ test ~ bin_infos:', bin_infos)
162
+
163
+ const addOption: AddLiquidityOption = {
164
+ pool_id,
165
+ bin_infos: bin_infos,
166
+ coin_type_a,
167
+ coin_type_b,
168
+ active_id,
169
+ position_id,
170
+ collect_fee: true,
171
+ reward_coins: [],
172
+ }
173
+ const tx = sdk.Position.addLiquidityPayload(addOption)
174
+
175
+ printTransaction(tx)
176
+
177
+ const res = await sdk.FullClient.executeTx(send_key_pair, tx, false)
178
+ console.log('🚀 ~ test ~ res:', res)
179
+ })
180
+ })