@cetusprotocol/xcetus-sdk 1.0.0 → 1.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.
@@ -1,1067 +0,0 @@
1
- import { Transaction } from '@mysten/sui/transactions'
2
- import type { SuiAddressType, SuiObjectIdType, SuiResource } from '@cetusprotocol/common-sdk'
3
- import {
4
- buildNFT,
5
- CACHE_TIME_24H,
6
- CACHE_TIME_5MIN,
7
- CachedContent,
8
- CLOCK_ADDRESS,
9
- CoinAsset,
10
- CoinAssist,
11
- d,
12
- DETAILS_KEYS,
13
- extractStructTagFromType,
14
- getFutureTime,
15
- getMoveObjectType,
16
- getObjectFields,
17
- getObjectId,
18
- getPackagerConfigs,
19
- IModule,
20
- } from '@cetusprotocol/common-sdk'
21
- import Decimal from 'decimal.js'
22
- import { handleError, XCetusErrorCode } from '../errors/errors'
23
- import type { CetusXcetusSDK } from '../sdk'
24
- import type {
25
- CancelRedeemParams,
26
- ConvertParams,
27
- DividendConfig,
28
- DividendManager,
29
- DividendReward,
30
- LockCetus,
31
- LockUpManager,
32
- PhaseDividendInfo,
33
- RedeemLockParams,
34
- RedeemXcetusParams,
35
- VeNFT,
36
- VeNFTDividendInfo,
37
- XcetusConfig,
38
- XcetusManager,
39
- } from '../types/xcetus_type'
40
- import {
41
- defaultLockUpConfig,
42
- DividendsRouterModule,
43
- EXCHANGE_RATE_MULTIPLIER,
44
- REDEEM_NUM_MULTIPLIER,
45
- XcetusRouterModule,
46
- } from '../types/xcetus_type'
47
- import { XCetusUtil } from '../utils/xcetus'
48
-
49
- /**
50
- * Helper class to help interact with xcetus with a router interface.
51
- */
52
- export class XCetusModule implements IModule<CetusXcetusSDK> {
53
- protected _sdk: CetusXcetusSDK
54
-
55
- private readonly _cache: Record<string, CachedContent> = {}
56
-
57
- constructor(sdk: CetusXcetusSDK) {
58
- this._sdk = sdk
59
- }
60
-
61
- get sdk() {
62
- return this._sdk
63
- }
64
-
65
- /**
66
- * Gets the VeNFT object for the specified account address.
67
- *
68
- * @param account_address The address of the account that owns the VeNFT object.
69
- * @param force_refresh Indicates whether to refresh the cache of the VeNFT object.
70
- * @returns A Promise that resolves to the VeNFT object or `undefined` if the object is not found.
71
- */
72
- async getOwnerVeNFT(account_address: SuiAddressType, force_refresh = true): Promise<VeNFT | void> {
73
- const { xcetus } = this.sdk.sdkOptions
74
-
75
- const cacheKey = `${account_address}_getLockUpManagerEvent`
76
- const cacheData = this.getCache<VeNFT>(cacheKey, force_refresh)
77
-
78
- if (cacheData !== undefined) {
79
- return cacheData
80
- }
81
- let veNFT: VeNFT | undefined
82
- const filterType = `${xcetus.package_id}::xcetus::VeNFT`
83
- try {
84
- const ownerRes: any = await this._sdk.FullClient.getOwnedObjectsByPage(account_address, {
85
- options: { showType: true, showContent: true, showDisplay: true },
86
- filter: { StructType: filterType },
87
- })
88
- ownerRes.data.forEach((item: any) => {
89
- const type = extractStructTagFromType(getMoveObjectType(item) as string).source_address
90
- if (type === filterType) {
91
- if (item.data && item.data.content) {
92
- const { fields } = item.data.content
93
- veNFT = {
94
- ...buildNFT(item),
95
- id: fields.id.id,
96
- index: fields.index,
97
- type,
98
- xcetus_balance: fields.xcetus_balance,
99
- }
100
- this.updateCache(cacheKey, veNFT, CACHE_TIME_24H)
101
- }
102
- }
103
- })
104
- return veNFT
105
- } catch (error) {
106
- return handleError(XCetusErrorCode.InvalidAccountAddress, error as Error, {
107
- [DETAILS_KEYS.METHOD_NAME]: 'getOwnerVeNFT',
108
- [DETAILS_KEYS.REQUEST_PARAMS]: { account_address },
109
- })
110
- }
111
- }
112
-
113
- /**
114
- * Gets the list of LockCetus objects owned by the specified account address.
115
- *
116
- * @param account_address The address of the account that owns the LockCetus objects.
117
- * @returns A Promise that resolves to a list of LockCetus objects.
118
- */
119
- async getOwnerRedeemLockList(account_address: SuiAddressType): Promise<LockCetus[]> {
120
- const { xcetus } = this.sdk.sdkOptions
121
- const lockCetusList: LockCetus[] = []
122
- const xcetusType = extractStructTagFromType(
123
- this.buildCetusCoinType()
124
- ).full_address;
125
- const filterType = `${xcetus.package_id}::lock_coin::LockedCoin<${xcetusType}>`
126
-
127
- try {
128
-
129
- const ownerRes: any = await this._sdk.FullClient.getOwnedObjectsByPage(account_address, {
130
- options: { showType: true, showContent: true },
131
- filter: { StructType: filterType },
132
- })
133
-
134
- for (const item of ownerRes.data) {
135
- const type = extractStructTagFromType(
136
- getMoveObjectType(item) as string
137
- ).source_address;
138
-
139
- if (type === filterType) {
140
- if (item.data) {
141
- const lockCetus = XCetusUtil.buildLockCetus(item.data.content)
142
- lockCetus.xcetus_amount = await this.getXCetusAmount(lockCetus.id)
143
- lockCetusList.push(lockCetus)
144
- }
145
- }
146
- }
147
-
148
- return lockCetusList
149
- } catch (error) {
150
- return handleError(XCetusErrorCode.InvalidAccountAddress, error as Error, {
151
- [DETAILS_KEYS.METHOD_NAME]: 'getOwnerRedeemLockList',
152
- [DETAILS_KEYS.REQUEST_PARAMS]: { account_address },
153
- })
154
- }
155
- }
156
-
157
- /**
158
- * Gets the LockCetus object with the specified ID.
159
- *
160
- * @param lock_id The ID of the LockCetus object.
161
- * @returns A Promise that resolves to the LockCetus object or `undefined` if the object is not found.
162
- */
163
- async getLockCetus(lock_id: SuiObjectIdType): Promise<LockCetus | undefined> {
164
- try {
165
- const result = await this._sdk.FullClient.getObject({
166
- id: lock_id,
167
- options: { showType: true, showContent: true },
168
- })
169
-
170
- if (result.data?.content) {
171
- const lockCetus = XCetusUtil.buildLockCetus(result.data.content)
172
- lockCetus.xcetus_amount = await this.getXCetusAmount(lockCetus.id)
173
- return lockCetus
174
- }
175
- return undefined
176
- } catch (error) {
177
- return handleError(XCetusErrorCode.InvalidLockId, error as Error, {
178
- [DETAILS_KEYS.METHOD_NAME]: 'getLockCetus',
179
- [DETAILS_KEYS.REQUEST_PARAMS]: { lock_id },
180
- })
181
- }
182
- }
183
-
184
- /**
185
- * Gets the list of Cetus coins owned by the specified account address.
186
- *
187
- * @param account_address The address of the account that owns the Cetus coins.
188
- * @returns A Promise that resolves to a list of CoinAsset objects.
189
- */
190
- async getOwnerCetusCoins(account_address: SuiAddressType): Promise<CoinAsset[]> {
191
- try {
192
- const coins = await this._sdk.FullClient.getOwnerCoinAssets(account_address, this.buildCetusCoinType())
193
- return coins
194
- } catch (error) {
195
- return handleError(XCetusErrorCode.InvalidAccountAddress, error as Error, {
196
- [DETAILS_KEYS.METHOD_NAME]: 'getOwnerCetusCoins',
197
- [DETAILS_KEYS.REQUEST_PARAMS]: { account_address },
198
- })
199
- }
200
- }
201
-
202
- /**
203
- * mint venft
204
- * @returns
205
- */
206
- mintVeNFTPayload(): Transaction {
207
- const { xcetus } = this.sdk.sdkOptions
208
-
209
- const tx = new Transaction()
210
-
211
- tx.moveCall({
212
- target: `${xcetus.published_at}::${XcetusRouterModule}::mint_venft`,
213
- typeArguments: [],
214
- arguments: [tx.object(getPackagerConfigs(xcetus)?.xcetus_manager_id)],
215
- })
216
-
217
- return tx
218
- }
219
-
220
- /**
221
- * Convert Cetus to Xcetus.
222
- * @param params
223
- * @returns
224
- */
225
- convertPayload(params: ConvertParams, tx?: Transaction): Transaction {
226
- const { xcetus } = this.sdk.sdkOptions
227
- tx = tx || new Transaction()
228
- const coin_type = this.buildCetusCoinType()
229
-
230
- tx.setSender(this._sdk.getSenderAddress())
231
-
232
- const primaryCoinInputs = CoinAssist.buildCoinWithBalance(BigInt(params.amount), coin_type, tx)
233
-
234
- if (params.venft_id === undefined) {
235
- tx.moveCall({
236
- target: `${xcetus.published_at}::${XcetusRouterModule}::mint_and_convert`,
237
- typeArguments: [],
238
- arguments: [
239
- tx.object(getPackagerConfigs(xcetus)?.lock_manager_id),
240
- tx.object(getPackagerConfigs(xcetus)?.xcetus_manager_id),
241
- tx.makeMoveVec({ elements: [primaryCoinInputs] }),
242
- tx.pure.u64(params.amount),
243
- ],
244
- })
245
- } else {
246
- tx.moveCall({
247
- target: `${xcetus.published_at}::${XcetusRouterModule}::convert`,
248
- typeArguments: [],
249
- arguments: [
250
- tx.object(getPackagerConfigs(xcetus)?.lock_manager_id),
251
- tx.object(getPackagerConfigs(xcetus)?.xcetus_manager_id),
252
- tx.makeMoveVec({ elements: [primaryCoinInputs] }),
253
- tx.pure.u64(params.amount),
254
- tx.object(params.venft_id),
255
- ],
256
- })
257
- }
258
-
259
- return tx
260
- }
261
-
262
- /**
263
- * Convert Xcetus to Cetus, first step is to lock the Cetus for a period.
264
- * When the time is reach, cetus can be redeem and xcetus will be burned.
265
- * @param params
266
- * @returns
267
- */
268
- redeemLockPayload(params: RedeemLockParams): Transaction {
269
- const { xcetus } = this.sdk.sdkOptions
270
-
271
- const tx = new Transaction()
272
-
273
- tx.moveCall({
274
- target: `${xcetus.published_at}::${XcetusRouterModule}::redeem_lock`,
275
- typeArguments: [],
276
- arguments: [
277
- tx.object(getPackagerConfigs(xcetus)?.lock_manager_id),
278
- tx.object(getPackagerConfigs(xcetus)?.xcetus_manager_id),
279
- tx.object(params.venft_id),
280
- tx.pure.u64(params.amount),
281
- tx.pure.u64(params.lock_day),
282
- tx.object(CLOCK_ADDRESS),
283
- ],
284
- })
285
-
286
- return tx
287
- }
288
-
289
- /**
290
- * lock time is reach and the cetus can be redeemed, the xcetus will be burned.
291
- * @param params
292
- * @returns
293
- */
294
- redeemPayload(params: RedeemXcetusParams): Transaction {
295
- const { xcetus } = this.sdk.sdkOptions
296
-
297
- const tx = new Transaction()
298
-
299
- tx.moveCall({
300
- target: `${xcetus.published_at}::${XcetusRouterModule}::redeem`,
301
- typeArguments: [],
302
- arguments: [
303
- tx.object(getPackagerConfigs(xcetus)?.lock_manager_id),
304
- tx.object(getPackagerConfigs(xcetus)?.xcetus_manager_id),
305
- tx.object(params.venft_id),
306
- tx.object(params.lock_id),
307
- tx.object(CLOCK_ADDRESS),
308
- ],
309
- })
310
-
311
- return tx
312
- }
313
-
314
- redeemDividendPayload(venft_id: SuiObjectIdType, bonus_types: SuiAddressType[]): Transaction {
315
- const { xcetus, xcetus_dividends } = this.sdk.sdkOptions
316
-
317
- const tx = new Transaction()
318
-
319
- bonus_types.forEach((coin) => {
320
- tx.moveCall({
321
- target: `${xcetus.published_at}::${DividendsRouterModule}::redeem`,
322
- typeArguments: [coin],
323
- arguments: [tx.object(getPackagerConfigs(xcetus_dividends)?.dividend_manager_id), tx.object(venft_id)],
324
- })
325
- })
326
- return tx
327
- }
328
-
329
- redeemDividendV2Payload(venft_id: SuiObjectIdType, bonus_types: SuiAddressType[], x_token_type: SuiAddressType[]): Transaction {
330
- const { xcetus_dividends } = this.sdk.sdkOptions
331
-
332
- let tx = new Transaction()
333
-
334
- const xTokenTypeList = bonus_types.filter((coin) => {
335
- return x_token_type.includes(coin)
336
- })
337
-
338
- if (xTokenTypeList.length > 0) {
339
- tx = this.redeemDividendXTokenPayload(venft_id, tx)
340
- }
341
-
342
- bonus_types.forEach((coin) => {
343
- if (!x_token_type.includes(coin)) {
344
- tx.moveCall({
345
- target: `${xcetus_dividends.published_at}::${DividendsRouterModule}::redeem_v2`,
346
- typeArguments: [coin],
347
- arguments: [tx.object(getPackagerConfigs(xcetus_dividends)?.dividend_manager_id), tx.object(venft_id), tx.object(CLOCK_ADDRESS)],
348
- })
349
- }
350
- })
351
- return tx
352
- }
353
-
354
- redeemDividendV3Payload(venft_id: string, reward_list: DividendReward[]): Transaction {
355
- const { xcetus_dividends } = this.sdk.sdkOptions
356
- const tx = new Transaction()
357
- const bonus_types = XCetusUtil.buildDividendRewardTypeList(reward_list)
358
- const bonus_types_v2 = XCetusUtil.buildDividendRewardTypeListV2(reward_list)
359
-
360
- const xcetusType = extractStructTagFromType(this.buildXTokenCoinType()).full_address
361
- const hasXcetus = bonus_types.find((type) => extractStructTagFromType(type).full_address === xcetusType) !== undefined
362
-
363
- if (hasXcetus) {
364
- this.redeemDividendXTokenPayload(venft_id, tx)
365
- }
366
-
367
- bonus_types.forEach((coin: SuiAddressType) => {
368
- tx.moveCall({
369
- target: `${xcetus_dividends.published_at}::${DividendsRouterModule}::redeem_v3`,
370
- typeArguments: [coin],
371
- arguments: [
372
- tx.object(getPackagerConfigs(xcetus_dividends)?.dividend_manager_id),
373
- tx.object(getPackagerConfigs(xcetus_dividends)?.venft_dividends_id),
374
- tx.makeMoveVec({
375
- elements: bonus_types_v2[coin].map((index) => tx.pure.u64(index)),
376
- type: 'u64',
377
- }),
378
- tx.object(venft_id),
379
- tx.object(CLOCK_ADDRESS),
380
- ],
381
- })
382
- })
383
- return tx
384
- }
385
-
386
- redeemDividendXTokenPayload(venft_id: SuiObjectIdType, tx?: Transaction): Transaction {
387
- const { xcetus_dividends, xcetus } = this.sdk.sdkOptions
388
- const { xcetus_manager_id, lock_manager_id } = getPackagerConfigs(xcetus)
389
- const { dividend_manager_id } = getPackagerConfigs(xcetus_dividends)
390
-
391
- tx = tx === undefined ? new Transaction() : tx
392
-
393
- tx.moveCall({
394
- target: `${xcetus_dividends.published_at}::${DividendsRouterModule}::redeem_xtoken`,
395
- typeArguments: [],
396
- arguments: [
397
- tx.object(lock_manager_id),
398
- tx.object(xcetus_manager_id),
399
- tx.object(dividend_manager_id),
400
- tx.object(venft_id),
401
- tx.object(CLOCK_ADDRESS),
402
- ],
403
- })
404
- return tx
405
- }
406
-
407
- buildCetusCoinType(): SuiAddressType {
408
- return `${this.sdk.sdkOptions.cetus_faucet.package_id}::cetus::CETUS`
409
- }
410
-
411
- buildXTokenCoinType(package_id = this._sdk.sdkOptions.xcetus.package_id, module = 'xcetus', name = 'XCETUS'): SuiAddressType {
412
- return `${package_id}::${module}::${name}`
413
- }
414
-
415
- /**
416
- * Cancel the redeem lock, the cetus locked will be return back to the manager and the xcetus will be available again.
417
- * @param params
418
- * @returns
419
- */
420
- cancelRedeemPayload(params: CancelRedeemParams): Transaction {
421
- const { xcetus } = this.sdk.sdkOptions
422
-
423
- const tx = new Transaction()
424
-
425
- tx.moveCall({
426
- target: `${xcetus.published_at}::${XcetusRouterModule}::cancel_redeem_lock`,
427
- typeArguments: [],
428
- arguments: [
429
- tx.object(getPackagerConfigs(xcetus).lock_manager_id),
430
- tx.object(getPackagerConfigs(xcetus).xcetus_manager_id),
431
- tx.object(params.venft_id),
432
- tx.object(params.lock_id),
433
- tx.object(CLOCK_ADDRESS),
434
- ],
435
- })
436
-
437
- return tx
438
- }
439
-
440
- /**
441
- * Gets the init factory event.
442
- *
443
- * @returns A Promise that resolves to the init factory event.
444
- */
445
- async getInitConfigs(): Promise<XcetusConfig> {
446
- const { package_id } = this.sdk.sdkOptions.xcetus
447
-
448
- const cacheKey = `${package_id}_getInitFactoryEvent`
449
- const cacheData = this.getCache<XcetusConfig>(cacheKey)
450
-
451
- if (cacheData !== undefined) {
452
- return cacheData
453
- }
454
-
455
- const initEventObjects = (
456
- await this._sdk.FullClient.queryEventsByPage({
457
- MoveEventType: `${package_id}::xcetus::InitEvent`,
458
- })
459
- )?.data
460
-
461
- const initEvent: XcetusConfig = {
462
- xcetus_manager_id: '',
463
- lock_manager_id: '',
464
- lock_handle_id: '',
465
- }
466
-
467
- if (initEventObjects.length > 0) {
468
- initEventObjects.forEach((item: any) => {
469
- const fields = item.parsedJson
470
- if (fields) {
471
- initEvent.xcetus_manager_id = fields.xcetus_manager
472
- }
473
- })
474
- }
475
-
476
- const lockEventObjects = (
477
- await this._sdk.FullClient.queryEventsByPage({
478
- MoveEventType: `${package_id}::locking::InitializeEvent`,
479
- })
480
- )?.data
481
- if (lockEventObjects.length > 0) {
482
- lockEventObjects.forEach((item: any) => {
483
- const fields = item.parsedJson
484
- if (fields) {
485
- initEvent.lock_manager_id = fields.lock_manager
486
- }
487
- })
488
- }
489
- try {
490
- if (initEvent.lock_manager_id.length > 0) {
491
- const res = await this.getLockUpManager(initEvent.lock_manager_id)
492
- if (res && res?.lock_infos.lock_handle_id) {
493
- initEvent.lock_handle_id = res?.lock_infos.lock_handle_id
494
- }
495
- }
496
- this.updateCache(cacheKey, initEvent, CACHE_TIME_24H)
497
- return initEvent
498
- } catch (error) {
499
- return handleError(XCetusErrorCode.FetchError, error as Error, {
500
- [DETAILS_KEYS.METHOD_NAME]: 'getInitConfigs',
501
- })
502
- }
503
- }
504
-
505
- /**
506
- * Gets the lock up manager event.
507
- *
508
- * @returns A Promise that resolves to the lock up manager event.
509
- */
510
- async getLockUpManager(
511
- lock_manager_id = getPackagerConfigs(this.sdk.sdkOptions.xcetus).lock_manager_id,
512
- force_refresh = false
513
- ): Promise<LockUpManager> {
514
- const cacheKey = `${lock_manager_id}_getLockUpManager`
515
- const cacheData = this.getCache<LockUpManager>(cacheKey, force_refresh)
516
-
517
- if (cacheData !== undefined) {
518
- return cacheData
519
- }
520
- try {
521
- const lockObject = await this.sdk.FullClient.getObject({
522
- id: lock_manager_id,
523
- options: { showContent: true },
524
- })
525
- const info = XCetusUtil.buildLockUpManager(getObjectFields(lockObject))
526
-
527
- this.updateCache(cacheKey, info, CACHE_TIME_24H)
528
- return info
529
- } catch (error) {
530
- return handleError(XCetusErrorCode.InvalidLockManagerId, error as Error, {
531
- [DETAILS_KEYS.METHOD_NAME]: 'getLockUpManager',
532
- [DETAILS_KEYS.REQUEST_PARAMS]: { lock_manager_id },
533
- })
534
- }
535
- }
536
-
537
- /**
538
- * Gets the dividend manager event.
539
- *
540
- * @returns A Promise that resolves to the dividend manager event.
541
- */
542
- async getDividendConfigs(): Promise<DividendConfig> {
543
- const { package_id } = this.sdk.sdkOptions.xcetus_dividends
544
- const { dividend_manager_id, venft_dividends_id } = getPackagerConfigs(this._sdk.sdkOptions.xcetus_dividends)
545
-
546
- const cacheKey = `${package_id}_getDividendManagerEvent`
547
- const cacheData = this.getCache<DividendConfig>(cacheKey)
548
-
549
- if (cacheData !== undefined) {
550
- return cacheData
551
- }
552
- try {
553
- const lockEventObjects = (
554
- await this._sdk.FullClient.queryEventsByPage({
555
- MoveEventType: `${package_id}::dividend::InitEvent`,
556
- })
557
- )?.data
558
-
559
- const veNftDividendsObjects: any = await this._sdk.FullClient.getDynamicFieldObject({
560
- parentId: dividend_manager_id,
561
- name: {
562
- type: '0x1::string::String',
563
- value: 'VeNFTDividends',
564
- },
565
- })
566
-
567
- const initEvent: DividendConfig = {
568
- dividend_manager_id: '',
569
- dividend_admin_id: '',
570
- dividend_settle_id: '',
571
- venft_dividends_id: '',
572
- venft_dividends_id_v2: '',
573
- }
574
-
575
- if (lockEventObjects.length > 0) {
576
- lockEventObjects.forEach((item: any) => {
577
- const fields = item.parsedJson
578
-
579
- if (fields) {
580
- initEvent.dividend_manager_id = fields.manager_id
581
- initEvent.dividend_admin_id = fields.admin_id
582
- initEvent.dividend_settle_id = fields.settle_id
583
- this.updateCache(cacheKey, initEvent, CACHE_TIME_24H)
584
- }
585
- })
586
- }
587
- if (veNftDividendsObjects && veNftDividendsObjects.data && veNftDividendsObjects.data.content) {
588
- initEvent.venft_dividends_id = veNftDividendsObjects.data.content?.fields?.value
589
- this.updateCache(cacheKey, initEvent, CACHE_TIME_24H)
590
- }
591
-
592
- const objects: any = await this._sdk.FullClient.getObject({
593
- id: venft_dividends_id,
594
- options: { showContent: true },
595
- })
596
- initEvent.venft_dividends_id_v2 = objects.data.content.fields.venft_dividends.fields.id.id
597
-
598
- return initEvent
599
- } catch (error) {
600
- return handleError(XCetusErrorCode.FetchError, error as Error, {
601
- [DETAILS_KEYS.METHOD_NAME]: 'getDividendConfigs',
602
- })
603
- }
604
- }
605
-
606
- /**
607
- * Gets the dividend manager object.
608
- *
609
- * @param force_refresh Whether to force a refresh of the cache.
610
- * @returns A Promise that resolves to the dividend manager object.
611
- */
612
- async getDividendManager(force_refresh = false): Promise<DividendManager> {
613
- const { dividend_manager_id } = getPackagerConfigs(this._sdk.sdkOptions.xcetus_dividends)
614
-
615
- const cacheKey = `${dividend_manager_id}_getDividendManager`
616
- const cacheData = this.getCache<DividendManager>(cacheKey, force_refresh)
617
-
618
- if (cacheData !== undefined) {
619
- return cacheData
620
- }
621
- try {
622
- const objects = await this._sdk.FullClient.getObject({
623
- id: dividend_manager_id,
624
- options: { showContent: true },
625
- })
626
- const fields = getObjectFields(objects)
627
- const dividendManager: DividendManager = XCetusUtil.buildDividendManager(fields)
628
- this.updateCache(cacheKey, dividendManager, CACHE_TIME_24H)
629
- return dividendManager
630
- } catch (error) {
631
- return handleError(XCetusErrorCode.FetchError, error as Error, {
632
- [DETAILS_KEYS.METHOD_NAME]: 'getDividendManager',
633
- })
634
- }
635
- }
636
-
637
- /**
638
- * Gets the Xcetus manager object.
639
- *
640
- * @returns A Promise that resolves to the Xcetus manager object.
641
- */
642
- async getXcetusManager(force_refresh = true): Promise<XcetusManager> {
643
- const { xcetus_manager_id } = getPackagerConfigs(this._sdk.sdkOptions.xcetus)
644
- const cacheKey = `${xcetus_manager_id}_getXcetusManager`
645
- const cacheData = this.getCache<XcetusManager>(cacheKey, force_refresh)
646
-
647
- if (cacheData) {
648
- return cacheData
649
- }
650
- try {
651
- const result = await this._sdk.FullClient.getObject({
652
- id: xcetus_manager_id,
653
- options: { showContent: true },
654
- })
655
- const fields = getObjectFields(result)
656
- const xcetusManager: XcetusManager = {
657
- id: fields.id.id,
658
- index: Number(fields.index),
659
- has_venft: {
660
- handle: fields.has_venft.fields.id.id,
661
- size: fields.has_venft.fields.size,
662
- },
663
- nfts: {
664
- handle: fields.nfts.fields.id.id,
665
- size: fields.nfts.fields.size,
666
- },
667
- total_locked: fields.total_locked,
668
- treasury: fields.treasury.fields.total_supply.fields.value,
669
- }
670
- this.updateCache(cacheKey, xcetusManager)
671
- return xcetusManager
672
- } catch (error) {
673
- return handleError(XCetusErrorCode.FetchError, error as Error, {
674
- [DETAILS_KEYS.METHOD_NAME]: 'getXcetusManager',
675
- })
676
- }
677
- }
678
-
679
- private async fetchDividendInfo(venft_id: string) {
680
- const { xcetus_dividends } = this._sdk.sdkOptions
681
- const { dividend_manager_id, venft_dividends_id } = getPackagerConfigs(xcetus_dividends)
682
-
683
- const tx = new Transaction()
684
- tx.moveCall({
685
- target: `${xcetus_dividends.published_at}::dividend::fetch_dividend_info_v2`,
686
- typeArguments: [],
687
- arguments: [tx.object(dividend_manager_id), tx.object(venft_dividends_id), tx.object(venft_id)],
688
- })
689
- try {
690
- const res: any = await this._sdk.FullClient.devInspectTransactionBlock({
691
- transactionBlock: tx,
692
- sender: this._sdk.getSenderAddress(),
693
- })
694
-
695
- const { contents } = res.events[0].parsedJson.info
696
-
697
- const veNFTDividendInfo: VeNFTDividendInfo = {
698
- id: '',
699
- venft_id: venft_id,
700
- rewards: [],
701
- }
702
-
703
- contents.forEach((item: any) => {
704
- const periodRewards: any[] = []
705
- const period = item.key
706
- const { contents } = item.value
707
-
708
- contents.forEach((reward: any) => {
709
- if (d(reward.value).gt(0)) {
710
- periodRewards.push({
711
- coin_type: extractStructTagFromType(reward.key.name).source_address,
712
- amount: reward.value,
713
- })
714
- }
715
- })
716
- if (periodRewards.length > 0) {
717
- veNFTDividendInfo.rewards.push({
718
- period: Number(period),
719
- rewards: periodRewards,
720
- version: Number(period) > 66 ? 'v2' : 'v1',
721
- })
722
- }
723
- })
724
-
725
- return veNFTDividendInfo
726
- } catch (error) {
727
- handleError(XCetusErrorCode.InvalidVeNftId, error as Error, {
728
- [DETAILS_KEYS.METHOD_NAME]: 'fetchDividendInfo',
729
- [DETAILS_KEYS.REQUEST_PARAMS]: { venft_id },
730
- })
731
- }
732
- }
733
-
734
- /**
735
- * Gets the VeNFT dividend information for the specified VeNFT dividend handle and VeNFT ID.
736
- *
737
- * @param venft_id The VeNFT ID.
738
- * @returns A Promise that resolves to the VeNFT dividend information or undefined if an error occurs.
739
- */
740
- async getVeNFTDividendInfo(venft_id: string): Promise<VeNFTDividendInfo | void> {
741
- try {
742
- return await this.fetchDividendInfo(venft_id)
743
- } catch (error) {
744
- try {
745
- return await this.getVeNFTDividendInfoV2(venft_id)
746
- } catch (error) {
747
- console.log('getVeNFTDividendInfo', error)
748
- return handleError(XCetusErrorCode.InvalidVeNftId, error as Error, {
749
- [DETAILS_KEYS.METHOD_NAME]: 'getVeNFTDividendInfo',
750
- [DETAILS_KEYS.REQUEST_PARAMS]: { venft_id },
751
- })
752
- }
753
- }
754
- }
755
-
756
- private async getVeNFTDividendInfoV2(venft_id: SuiObjectIdType): Promise<VeNFTDividendInfo> {
757
- const { xcetus_dividends } = this._sdk.sdkOptions
758
- const { venft_dividends_id_v2 } = getPackagerConfigs(xcetus_dividends)
759
- const veNFTDividendInfo: VeNFTDividendInfo = {
760
- id: '',
761
- venft_id: venft_id,
762
- rewards: [],
763
- }
764
-
765
- const rewards: any = []
766
- try {
767
- const venft_dividends_v2 = await this._sdk.FullClient.getDynamicFieldObject({
768
- parentId: venft_dividends_id_v2,
769
- name: {
770
- type: '0x2::object::ID',
771
- value: venft_id,
772
- },
773
- })
774
- const venft_table_id = getObjectFields(venft_dividends_v2).value.fields.value.fields.dividends.fields.id.id
775
- let nextCursor: string | null = null
776
- const limit = 50
777
- const tableIdList: any = []
778
- while (true) {
779
- const tableRes: any = await this._sdk.FullClient.getDynamicFields({
780
- parentId: venft_table_id,
781
- cursor: nextCursor,
782
- limit,
783
- })
784
- tableRes.data.forEach((item: any) => {
785
- tableIdList.push(item.objectId)
786
- })
787
- nextCursor = tableRes.nextCursor
788
- if (nextCursor === null || tableRes.data.length < limit) {
789
- break
790
- }
791
- }
792
- const objects: any = await this._sdk.FullClient.batchGetObjects(tableIdList, { showType: true, showContent: true })
793
-
794
- objects.forEach((item: any) => {
795
- rewards.push({
796
- period: Number(item.data.content.fields.name),
797
- version: 'v2',
798
- rewards: item.data.content.fields.value.fields.contents.map((ele: any) => {
799
- return {
800
- coin_type: extractStructTagFromType(ele.fields.key.fields.name).source_address,
801
- amount: ele.fields.value,
802
- }
803
- }),
804
- })
805
- })
806
- } catch (error) {
807
- console.log('getVeNFTDividendInfoV2', error)
808
- return handleError(XCetusErrorCode.InvalidVeNftId, error as Error, {
809
- [DETAILS_KEYS.METHOD_NAME]: 'getVeNFTDividendInfoV2',
810
- [DETAILS_KEYS.REQUEST_PARAMS]: { venft_id },
811
- })
812
- }
813
-
814
- return {
815
- ...veNFTDividendInfo,
816
- rewards: [...rewards],
817
- }
818
- }
819
-
820
- private async getVeNFTDividendInfoV1(venft_id: SuiObjectIdType): Promise<VeNFTDividendInfo> {
821
- const { xcetus_dividends } = this._sdk.sdkOptions
822
- const { venft_dividends_id } = getPackagerConfigs(xcetus_dividends)
823
-
824
- let veNFTDividendInfo: VeNFTDividendInfo = {
825
- id: '',
826
- venft_id: venft_id,
827
- rewards: [],
828
- }
829
- try {
830
- const venft_dividends = await this._sdk.FullClient.getDynamicFieldObject({
831
- parentId: venft_dividends_id,
832
- name: {
833
- type: '0x2::object::ID',
834
- value: venft_id,
835
- },
836
- })
837
- const fields = getObjectFields(venft_dividends)
838
- veNFTDividendInfo = XCetusUtil.buildVeNFTDividendInfo(fields)
839
- } catch (error) {
840
- console.log('getVeNFTDividendInfoV1 ~ error:', error)
841
- return handleError(XCetusErrorCode.InvalidVeNftId, error as Error, {
842
- [DETAILS_KEYS.METHOD_NAME]: 'getVeNFTDividendInfoV1',
843
- [DETAILS_KEYS.REQUEST_PARAMS]: { venft_id },
844
- })
845
- }
846
-
847
- return veNFTDividendInfo
848
- }
849
-
850
- /**
851
- * Calculates the redeem number for the specified amount and lock day.
852
- *
853
- * @param redeem_amount The amount to redeem.
854
- * @param lock_day The number of days to lock the amount for.
855
- * @returns A Promise that resolves to an object with the amount out and percent.
856
- */
857
- redeemNum(redeem_amount: string | number, lock_day: number): { amount_out: string; percent: string } {
858
- if (BigInt(redeem_amount) === BigInt(0)) {
859
- return { amount_out: '0', percent: '0' }
860
- }
861
-
862
- const mid = d(REDEEM_NUM_MULTIPLIER)
863
- .mul(d(defaultLockUpConfig.max_lock_day).sub(d(lock_day)))
864
- .mul(d(defaultLockUpConfig.max_percent_numerator).sub(d(defaultLockUpConfig.min_percent_numerator)))
865
- .div(d(defaultLockUpConfig.max_lock_day).sub(d(defaultLockUpConfig.min_lock_day)))
866
-
867
- const percent = d(REDEEM_NUM_MULTIPLIER)
868
- .mul(d(defaultLockUpConfig.max_percent_numerator))
869
- .sub(mid)
870
- .div(d(EXCHANGE_RATE_MULTIPLIER))
871
- .div(REDEEM_NUM_MULTIPLIER)
872
-
873
- return {
874
- amount_out: d(percent).mul(d(redeem_amount)).round().toString(),
875
- percent: percent.toString(),
876
- }
877
- }
878
-
879
- /**
880
- * Reverses the redeem number for the specified amount and lock day.
881
- *
882
- * @param amount The amount to redeem.
883
- * @param lock_day The number of days to lock the amount for.
884
- * @returns A Promise that resolves to an object with the reversed amount and percent.
885
- */
886
- reverseRedeemNum(amount: string | number, lock_day: number): { amount_out: string; percent: string } {
887
- if (BigInt(amount) === BigInt(0)) {
888
- return { amount_out: '0', percent: '0' }
889
- }
890
-
891
- const mid = d(REDEEM_NUM_MULTIPLIER)
892
- .mul(d(defaultLockUpConfig.max_lock_day).sub(d(lock_day)))
893
- .mul(d(defaultLockUpConfig.max_percent_numerator).sub(d(defaultLockUpConfig.min_percent_numerator)))
894
- .div(d(defaultLockUpConfig.max_lock_day).sub(d(defaultLockUpConfig.min_lock_day)))
895
-
896
- const percent = d(REDEEM_NUM_MULTIPLIER)
897
- .mul(d(defaultLockUpConfig.max_percent_numerator))
898
- .sub(mid)
899
- .div(d(EXCHANGE_RATE_MULTIPLIER))
900
- .div(REDEEM_NUM_MULTIPLIER)
901
- return {
902
- amount_out: d(amount).div(percent).toFixed(0, Decimal.ROUND_UP),
903
- percent: percent.toString(),
904
- }
905
- }
906
-
907
- /**
908
- * Gets the XCetus amount for the specified lock ID.
909
- *
910
- * @param lock_id The ID of the lock.
911
- * @returns A Promise that resolves to the XCetus amount.
912
- */
913
- async getXCetusAmount(lock_id: string): Promise<string> {
914
- const { lock_handle_id } = getPackagerConfigs(this._sdk.sdkOptions.xcetus)
915
-
916
- const cacheKey = `${lock_id}_getXCetusAmount`
917
- const cacheData = this.getCache<string>(cacheKey)
918
-
919
- if (cacheData !== undefined) {
920
- return cacheData
921
- }
922
-
923
- try {
924
- const response = await this.sdk.FullClient.getDynamicFieldObject({
925
- parentId: lock_handle_id,
926
- name: {
927
- type: '0x2::object::ID',
928
- value: lock_id,
929
- },
930
- })
931
- const fields = getObjectFields(response)
932
- if (fields) {
933
- const { xcetus_amount } = fields.value.fields.value.fields
934
- this.updateCache(cacheKey, xcetus_amount, CACHE_TIME_24H)
935
- return xcetus_amount
936
- }
937
- } catch (error) {
938
- console.log('getXCetusAmount', error)
939
- handleError(XCetusErrorCode.InvalidLockId, error as Error, {
940
- [DETAILS_KEYS.METHOD_NAME]: 'getXCetusAmount',
941
- [DETAILS_KEYS.REQUEST_PARAMS]: { lock_id },
942
- })
943
- }
944
- return '0'
945
- }
946
-
947
- /**
948
- * Gets the amount of XCetus and lock for the specified VENFT.
949
- *
950
- * @param nft_handle_id The ID of the NFT handle.
951
- * @param venft_id The ID of the VENFT.
952
- * @returns A Promise that resolves to an object with the XCetus amount and lock amount.
953
- */
954
- async getVeNftAmount(nft_handle_id: string, venft_id: string): Promise<{ xcetus_amount: string; lock_amount: string }> {
955
- try {
956
- const response = await this.sdk.FullClient.getDynamicFieldObject({
957
- parentId: nft_handle_id,
958
- name: {
959
- type: '0x2::object::ID',
960
- value: venft_id,
961
- },
962
- })
963
- const fields = getObjectFields(response)
964
- if (fields) {
965
- const { lock_amount, xcetus_amount } = fields.value.fields.value.fields
966
- return { lock_amount, xcetus_amount }
967
- }
968
- } catch (error) {
969
- console.log('getVeNftAmount', error)
970
- handleError(XCetusErrorCode.FetchError, error as Error, {
971
- [DETAILS_KEYS.METHOD_NAME]: 'getVeNftAmount',
972
- [DETAILS_KEYS.REQUEST_PARAMS]: { nft_handle_id, venft_id },
973
- })
974
- }
975
- return { lock_amount: '0', xcetus_amount: '0' }
976
- }
977
-
978
- /**
979
- * @param phase_handle
980
- * @param phase
981
- * @param force_refresh
982
- * @returns
983
- */
984
- async getPhaseDividendInfo(phase: string, force_refresh = false): Promise<PhaseDividendInfo | undefined> {
985
- try {
986
- const dividendManager = await this.getDividendManager()
987
- if (dividendManager) {
988
- const phase_handle = dividendManager.dividends.id
989
- const cacheKey = `${phase_handle}_${phase}_getPhaseDividendInfo`
990
- const cacheData = this._sdk.getCache<PhaseDividendInfo>(cacheKey, force_refresh)
991
-
992
- if (cacheData) {
993
- return cacheData
994
- }
995
- const res = await this._sdk.FullClient.getDynamicFieldObject({
996
- parentId: phase_handle,
997
- name: {
998
- type: 'u64',
999
- value: phase,
1000
- },
1001
- })
1002
- const fields = getObjectFields(res)
1003
- const valueFields = fields.value.fields.value.fields
1004
-
1005
- const redeemed_num = valueFields.redeemed_num.fields.contents.map((item: any) => {
1006
- return {
1007
- name: item.fields.key.fields.name,
1008
- value: item.fields.value,
1009
- }
1010
- })
1011
-
1012
- const bonus_types = valueFields.bonus_types.map((item: any) => {
1013
- return item.fields.name
1014
- })
1015
-
1016
- const bonus = valueFields.bonus.fields.contents.map((item: any) => {
1017
- return {
1018
- name: item.fields.key.fields.name,
1019
- value: item.fields.value,
1020
- }
1021
- })
1022
-
1023
- const info: PhaseDividendInfo = {
1024
- id: getObjectId(res),
1025
- phase: fields.name,
1026
- settled_num: valueFields.settled_num,
1027
- register_time: valueFields.register_time,
1028
- redeemed_num,
1029
- is_settled: valueFields.is_settled,
1030
- bonus_types,
1031
- bonus,
1032
- phase_end_time: '',
1033
- }
1034
- this.updateCache(cacheKey, info)
1035
- return info
1036
- }
1037
- } catch (error) {
1038
- console.log('getPhaseDividendInfo', error)
1039
- handleError(XCetusErrorCode.InvalidPhase, error as Error, {
1040
- [DETAILS_KEYS.METHOD_NAME]: 'getPhaseDividendInfo',
1041
- [DETAILS_KEYS.REQUEST_PARAMS]: { phase },
1042
- })
1043
- }
1044
-
1045
- return undefined
1046
- }
1047
-
1048
- private updateCache(key: string, data: SuiResource, time = CACHE_TIME_5MIN) {
1049
- let cacheData = this._cache[key]
1050
- if (cacheData) {
1051
- cacheData.overdue_time = getFutureTime(time)
1052
- cacheData.value = data
1053
- } else {
1054
- cacheData = new CachedContent(data, getFutureTime(time))
1055
- }
1056
- this._cache[key] = cacheData
1057
- }
1058
-
1059
- private getCache<T>(key: string, force_refresh = false): T | undefined {
1060
- const cacheData = this._cache[key]
1061
- if (!force_refresh && cacheData?.isValid()) {
1062
- return cacheData.value as T
1063
- }
1064
- delete this._cache[key]
1065
- return undefined
1066
- }
1067
- }