@scallop-io/sui-scallop-sdk 0.46.36 → 0.46.38
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/dist/builders/loyaltyProgramBuilder.d.ts +12 -0
- package/dist/builders/referralBuilder.d.ts +1 -1
- package/dist/constants/testAddress.d.ts +2 -0
- package/dist/index.js +684 -172
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +674 -162
- package/dist/index.mjs.map +1 -1
- package/dist/models/scallopQuery.d.ts +10 -3
- package/dist/models/scallopUtils.d.ts +1 -1
- package/dist/queries/index.d.ts +1 -0
- package/dist/queries/loyaltyProgramQuery.d.ts +10 -0
- package/dist/queries/vescaQuery.d.ts +8 -6
- package/dist/types/address.d.ts +6 -0
- package/dist/types/builder/index.d.ts +3 -1
- package/dist/types/builder/loyaltyProgram.d.ts +23 -0
- package/dist/types/builder/vesca.d.ts +16 -0
- package/dist/types/query/index.d.ts +1 -0
- package/dist/types/query/loyaltyProgram.d.ts +5 -0
- package/dist/types/query/vesca.d.ts +18 -0
- package/dist/utils/builder.d.ts +6 -5
- package/package.json +7 -6
- package/src/builders/index.ts +6 -1
- package/src/builders/loyaltyProgramBuilder.ts +115 -0
- package/src/builders/referralBuilder.ts +1 -1
- package/src/builders/vescaBuilder.ts +5 -1
- package/src/constants/testAddress.ts +383 -0
- package/src/models/scallopAddress.ts +12 -2
- package/src/models/scallopCache.ts +0 -1
- package/src/models/scallopQuery.ts +28 -16
- package/src/models/scallopUtils.ts +3 -3
- package/src/queries/borrowIncentiveQuery.ts +9 -8
- package/src/queries/coreQuery.ts +70 -66
- package/src/queries/index.ts +1 -0
- package/src/queries/loyaltyProgramQuery.ts +77 -0
- package/src/queries/portfolioQuery.ts +36 -28
- package/src/queries/vescaQuery.ts +70 -15
- package/src/types/address.ts +6 -0
- package/src/types/builder/index.ts +3 -0
- package/src/types/builder/loyaltyProgram.ts +35 -0
- package/src/types/builder/vesca.ts +16 -0
- package/src/types/query/index.ts +1 -0
- package/src/types/query/loyaltyProgram.ts +5 -0
- package/src/types/query/vesca.ts +21 -0
- package/src/utils/builder.ts +69 -53
- package/src/utils/query.ts +6 -5
package/src/queries/coreQuery.ts
CHANGED
|
@@ -50,18 +50,6 @@ export const queryMarket = async (
|
|
|
50
50
|
query: ScallopQuery,
|
|
51
51
|
indexer: boolean = false
|
|
52
52
|
) => {
|
|
53
|
-
const packageId = query.address.get('core.packages.query.id');
|
|
54
|
-
const marketId = query.address.get('core.market');
|
|
55
|
-
const queryTarget = `${packageId}::market_query::market_data`;
|
|
56
|
-
const args = [marketId];
|
|
57
|
-
|
|
58
|
-
// const txBlock = new SuiKitTxBlock();
|
|
59
|
-
// txBlock.moveCall(queryTarget, args);
|
|
60
|
-
const queryResult = await query.cache.queryInspectTxn(
|
|
61
|
-
{ queryTarget, args }
|
|
62
|
-
// txBlock
|
|
63
|
-
);
|
|
64
|
-
const marketData = queryResult.events[0].parsedJson as MarketQueryInterface;
|
|
65
53
|
const coinPrices = await query.utils.getCoinPrices();
|
|
66
54
|
|
|
67
55
|
const pools: MarketPools = {};
|
|
@@ -86,6 +74,14 @@ export const queryMarket = async (
|
|
|
86
74
|
};
|
|
87
75
|
}
|
|
88
76
|
|
|
77
|
+
const packageId = query.address.get('core.packages.query.id');
|
|
78
|
+
const marketId = query.address.get('core.market');
|
|
79
|
+
const queryTarget = `${packageId}::market_query::market_data`;
|
|
80
|
+
const args = [marketId];
|
|
81
|
+
|
|
82
|
+
const queryResult = await query.cache.queryInspectTxn({ queryTarget, args });
|
|
83
|
+
const marketData = queryResult.events[0].parsedJson as MarketQueryInterface;
|
|
84
|
+
|
|
89
85
|
for (const pool of marketData.pools) {
|
|
90
86
|
const coinType = normalizeStructTag(pool.type.name);
|
|
91
87
|
const poolCoinName =
|
|
@@ -236,19 +232,21 @@ export const getMarketPools = async (
|
|
|
236
232
|
return marketPools;
|
|
237
233
|
}
|
|
238
234
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
235
|
+
Promise.allSettled(
|
|
236
|
+
poolCoinNames.map(async (poolCoinName) => {
|
|
237
|
+
const marketPool = await getMarketPool(
|
|
238
|
+
query,
|
|
239
|
+
poolCoinName,
|
|
240
|
+
indexer,
|
|
241
|
+
marketObjectResponse.data,
|
|
242
|
+
coinPrices?.[poolCoinName]
|
|
243
|
+
);
|
|
247
244
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
245
|
+
if (marketPool) {
|
|
246
|
+
marketPools[poolCoinName] = marketPool;
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
);
|
|
252
250
|
|
|
253
251
|
return marketPools;
|
|
254
252
|
};
|
|
@@ -475,11 +473,12 @@ export const getMarketCollaterals = async (
|
|
|
475
473
|
) => {
|
|
476
474
|
collateralCoinNames = collateralCoinNames || [...SUPPORT_COLLATERALS];
|
|
477
475
|
const marketId = query.address.get('core.market');
|
|
478
|
-
const marketObjectResponse = await
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
476
|
+
const [marketObjectResponse, coinPrices] = await Promise.all([
|
|
477
|
+
query.cache.queryGetObject(marketId, {
|
|
478
|
+
showContent: true,
|
|
479
|
+
}),
|
|
480
|
+
query.utils.getCoinPrices(collateralCoinNames ?? []),
|
|
481
|
+
]);
|
|
483
482
|
const marketCollaterals: MarketCollaterals = {};
|
|
484
483
|
|
|
485
484
|
if (indexer) {
|
|
@@ -496,19 +495,21 @@ export const getMarketCollaterals = async (
|
|
|
496
495
|
return marketCollaterals;
|
|
497
496
|
}
|
|
498
497
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
498
|
+
await Promise.allSettled(
|
|
499
|
+
collateralCoinNames.map(async (collateralCoinName) => {
|
|
500
|
+
const marketCollateral = await getMarketCollateral(
|
|
501
|
+
query,
|
|
502
|
+
collateralCoinName,
|
|
503
|
+
indexer,
|
|
504
|
+
marketObjectResponse.data,
|
|
505
|
+
coinPrices?.[collateralCoinName]
|
|
506
|
+
);
|
|
507
507
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
508
|
+
if (marketCollateral) {
|
|
509
|
+
marketCollaterals[collateralCoinName] = marketCollateral;
|
|
510
|
+
}
|
|
511
|
+
})
|
|
512
|
+
);
|
|
512
513
|
|
|
513
514
|
return marketCollaterals;
|
|
514
515
|
};
|
|
@@ -530,6 +531,22 @@ export const getMarketCollateral = async (
|
|
|
530
531
|
marketObject?: SuiObjectData | null,
|
|
531
532
|
coinPrice?: number
|
|
532
533
|
) => {
|
|
534
|
+
if (indexer) {
|
|
535
|
+
const marketCollateralIndexer =
|
|
536
|
+
await query.indexer.getMarketCollateral(collateralCoinName);
|
|
537
|
+
marketCollateralIndexer.coinPrice =
|
|
538
|
+
coinPrice || marketCollateralIndexer.coinPrice;
|
|
539
|
+
marketCollateralIndexer.coinWrappedType = query.utils.getCoinWrappedType(
|
|
540
|
+
marketCollateralIndexer.coinName
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
return marketCollateralIndexer;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
let marketCollateral: MarketCollateral | undefined;
|
|
547
|
+
let riskModel: RiskModel | undefined;
|
|
548
|
+
let collateralStat: CollateralStat | undefined;
|
|
549
|
+
|
|
533
550
|
const marketId = query.address.get('core.market');
|
|
534
551
|
marketObject =
|
|
535
552
|
marketObject ||
|
|
@@ -545,22 +562,6 @@ export const getMarketCollateral = async (
|
|
|
545
562
|
collateralCoinName
|
|
546
563
|
];
|
|
547
564
|
|
|
548
|
-
let marketCollateral: MarketCollateral | undefined;
|
|
549
|
-
let riskModel: RiskModel | undefined;
|
|
550
|
-
let collateralStat: CollateralStat | undefined;
|
|
551
|
-
|
|
552
|
-
if (indexer) {
|
|
553
|
-
const marketCollateralIndexer =
|
|
554
|
-
await query.indexer.getMarketCollateral(collateralCoinName);
|
|
555
|
-
marketCollateralIndexer.coinPrice =
|
|
556
|
-
coinPrice || marketCollateralIndexer.coinPrice;
|
|
557
|
-
marketCollateralIndexer.coinWrappedType = query.utils.getCoinWrappedType(
|
|
558
|
-
marketCollateralIndexer.coinName
|
|
559
|
-
);
|
|
560
|
-
|
|
561
|
-
return marketCollateralIndexer;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
565
|
if (marketObject) {
|
|
565
566
|
if (marketObject.content && 'fields' in marketObject.content) {
|
|
566
567
|
const fields = marketObject.content.fields as any;
|
|
@@ -698,15 +699,18 @@ export const getObligations = async (
|
|
|
698
699
|
const keyObjects = await query.cache.queryGetObjects(keyObjectIds);
|
|
699
700
|
|
|
700
701
|
const obligations: Obligation[] = [];
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
702
|
+
await Promise.allSettled(
|
|
703
|
+
keyObjects.map(async (keyObject) => {
|
|
704
|
+
const keyId = keyObject.objectId;
|
|
705
|
+
if (keyObject.content && 'fields' in keyObject.content) {
|
|
706
|
+
const fields = keyObject.content.fields as any;
|
|
707
|
+
const obligationId = String(fields.ownership.fields.of);
|
|
708
|
+
const locked = await getObligationLocked(query, obligationId);
|
|
709
|
+
obligations.push({ id: obligationId, keyId, locked });
|
|
710
|
+
}
|
|
711
|
+
})
|
|
712
|
+
);
|
|
713
|
+
|
|
710
714
|
return obligations;
|
|
711
715
|
};
|
|
712
716
|
|
package/src/queries/index.ts
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SuiObjectData } from '@mysten/sui.js/src/client';
|
|
2
|
+
import BigNumber from 'bignumber.js';
|
|
3
|
+
import { ScallopQuery } from 'src/models';
|
|
4
|
+
import { LoyaltyProgramInfo } from 'src/types';
|
|
5
|
+
import { z as zod } from 'zod';
|
|
6
|
+
|
|
7
|
+
const rewardPoolFieldsZod = zod
|
|
8
|
+
.object({
|
|
9
|
+
balance: zod.string(),
|
|
10
|
+
enable_claim: zod.boolean(),
|
|
11
|
+
})
|
|
12
|
+
.transform((value) => ({
|
|
13
|
+
totalPoolReward: BigNumber(value.balance).shiftedBy(-9).toNumber(),
|
|
14
|
+
isClaimEnabled: value.enable_claim,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const userRewardFieldsZod = zod
|
|
18
|
+
.object({
|
|
19
|
+
value: zod.string(),
|
|
20
|
+
})
|
|
21
|
+
.transform((value) => BigNumber(value.value).shiftedBy(-9).toNumber());
|
|
22
|
+
|
|
23
|
+
type RewardPoolFields = zod.infer<typeof rewardPoolFieldsZod>;
|
|
24
|
+
type UserRewardFields = zod.infer<typeof userRewardFieldsZod>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get user's loyalty program information and pending rewards if exists
|
|
28
|
+
* @param query
|
|
29
|
+
* @param veScaKey
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
32
|
+
export const getLoyaltyProgramInformations = async (
|
|
33
|
+
query: ScallopQuery,
|
|
34
|
+
veScaKey?: string | SuiObjectData
|
|
35
|
+
): Promise<LoyaltyProgramInfo | null> => {
|
|
36
|
+
const rewardPool = query.address.get('loyaltyProgram.rewardPool');
|
|
37
|
+
|
|
38
|
+
// get fields from rewardPool object
|
|
39
|
+
const rewardPoolObject = await query.cache.queryGetObject(rewardPool, {
|
|
40
|
+
showContent: true,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (rewardPoolObject.data?.content?.dataType !== 'moveObject') return null;
|
|
44
|
+
const rewardPoolFields = rewardPoolObject.data.content.fields;
|
|
45
|
+
const { isClaimEnabled, totalPoolReward } = rewardPoolFieldsZod.parse(
|
|
46
|
+
rewardPoolFields
|
|
47
|
+
) as RewardPoolFields;
|
|
48
|
+
|
|
49
|
+
const result: LoyaltyProgramInfo = {
|
|
50
|
+
pendingReward: 0,
|
|
51
|
+
totalPoolReward,
|
|
52
|
+
isClaimEnabled,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// query the user pending reward if exist
|
|
56
|
+
veScaKey = veScaKey || (await query.getVeScas())[0]?.keyObject;
|
|
57
|
+
if (!veScaKey) return result;
|
|
58
|
+
|
|
59
|
+
const userRewardTableId = query.address.get(
|
|
60
|
+
'loyaltyProgram.userRewardTableId'
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const userRewardObject = await query.cache.queryGetDynamicFieldObject({
|
|
64
|
+
parentId: userRewardTableId,
|
|
65
|
+
name: {
|
|
66
|
+
type: '0x2::object::ID',
|
|
67
|
+
value: typeof veScaKey === 'string' ? veScaKey : veScaKey.objectId,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (userRewardObject.data?.content?.dataType !== 'moveObject') return result;
|
|
72
|
+
const userRewardFields = userRewardObject.data.content.fields;
|
|
73
|
+
result.pendingReward = userRewardFieldsZod.parse(
|
|
74
|
+
userRewardFields
|
|
75
|
+
) as UserRewardFields;
|
|
76
|
+
return result;
|
|
77
|
+
};
|
|
@@ -49,36 +49,44 @@ export const getLendings = async (
|
|
|
49
49
|
(SUPPORT_SPOOLS as readonly SupportMarketCoins[]).includes(marketCoinName)
|
|
50
50
|
) as SupportStakeMarketCoins[];
|
|
51
51
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
const [
|
|
53
|
+
marketPools,
|
|
54
|
+
spools,
|
|
55
|
+
coinAmounts,
|
|
56
|
+
marketCoinAmounts,
|
|
57
|
+
allStakeAccounts,
|
|
58
|
+
coinPrices,
|
|
59
|
+
] = await Promise.all([
|
|
60
|
+
query.getMarketPools(poolCoinNames, indexer),
|
|
61
|
+
query.getSpools(stakeMarketCoinNames, indexer),
|
|
62
|
+
query.getCoinAmounts(poolCoinNames, ownerAddress),
|
|
63
|
+
query.getMarketCoinAmounts(marketCoinNames, ownerAddress),
|
|
64
|
+
query.getAllStakeAccounts(ownerAddress),
|
|
65
|
+
query.utils.getCoinPrices(poolCoinNames),
|
|
66
|
+
]);
|
|
61
67
|
|
|
62
68
|
const lendings: Lendings = {};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
(
|
|
66
|
-
marketCoinName
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
await Promise.allSettled(
|
|
70
|
+
poolCoinNames.map(async (poolCoinName) => {
|
|
71
|
+
const stakeMarketCoinName = stakeMarketCoinNames.find(
|
|
72
|
+
(marketCoinName) =>
|
|
73
|
+
marketCoinName === query.utils.parseMarketCoinName(poolCoinName)
|
|
74
|
+
);
|
|
75
|
+
const marketCoinName = query.utils.parseMarketCoinName(poolCoinName);
|
|
76
|
+
lendings[poolCoinName] = await getLending(
|
|
77
|
+
query,
|
|
78
|
+
poolCoinName,
|
|
79
|
+
ownerAddress,
|
|
80
|
+
indexer,
|
|
81
|
+
marketPools?.[poolCoinName],
|
|
82
|
+
stakeMarketCoinName ? spools[stakeMarketCoinName] : undefined,
|
|
83
|
+
stakeMarketCoinName ? allStakeAccounts[stakeMarketCoinName] : undefined,
|
|
84
|
+
coinAmounts?.[poolCoinName],
|
|
85
|
+
marketCoinAmounts?.[marketCoinName],
|
|
86
|
+
coinPrices?.[poolCoinName] ?? 0
|
|
87
|
+
);
|
|
88
|
+
})
|
|
89
|
+
);
|
|
82
90
|
|
|
83
91
|
return lendings;
|
|
84
92
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import BigNumber from 'bignumber.js';
|
|
2
|
-
import { Vesca } from '../types';
|
|
2
|
+
import { VeScaTreasuryFields, VeScaTreasuryInfo, Vesca } from '../types';
|
|
3
3
|
import {
|
|
4
4
|
type SuiObjectResponse,
|
|
5
5
|
type SuiObjectData,
|
|
@@ -9,6 +9,7 @@ import type { ScallopQuery } from '../models';
|
|
|
9
9
|
import { MAX_LOCK_DURATION } from 'src/constants';
|
|
10
10
|
import { SUI_CLOCK_OBJECT_ID, SuiTxBlock } from '@scallop-io/sui-kit';
|
|
11
11
|
import { bcs } from '@mysten/sui.js/bcs';
|
|
12
|
+
import { z as zod } from 'zod';
|
|
12
13
|
/**
|
|
13
14
|
* Query all owned veSca key.
|
|
14
15
|
*
|
|
@@ -63,11 +64,10 @@ export const getVescaKeys = async (
|
|
|
63
64
|
*/
|
|
64
65
|
export const getVeScas = async (query: ScallopQuery, ownerAddress?: string) => {
|
|
65
66
|
const keyObjectDatas = await getVescaKeys(query, ownerAddress);
|
|
66
|
-
const keyObjectId: string[] = keyObjectDatas.map((data) => data.objectId);
|
|
67
67
|
|
|
68
|
-
const veScas:
|
|
69
|
-
const tasks =
|
|
70
|
-
const veSca = await getVeSca(query,
|
|
68
|
+
const veScas: Vesca[] = Array(keyObjectDatas.length).fill(null);
|
|
69
|
+
const tasks = keyObjectDatas.map(async (veScaKey, idx) => {
|
|
70
|
+
const veSca = await getVeSca(query, veScaKey);
|
|
71
71
|
if (veSca) {
|
|
72
72
|
veScas[idx] = veSca;
|
|
73
73
|
}
|
|
@@ -79,22 +79,33 @@ export const getVeScas = async (query: ScallopQuery, ownerAddress?: string) => {
|
|
|
79
79
|
.sort((a, b) => b!.currentVeScaBalance - a!.currentVeScaBalance);
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
+
const SuiObjectRefZod = zod.object({
|
|
83
|
+
objectId: zod.string(),
|
|
84
|
+
digest: zod.string(),
|
|
85
|
+
version: zod.string(),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
type SuiObjectRefType = zod.infer<typeof SuiObjectRefZod>;
|
|
82
89
|
/**
|
|
83
90
|
* Get veSca data.
|
|
84
91
|
*
|
|
85
92
|
* @param query - The Scallop query instance.
|
|
86
|
-
* @param
|
|
93
|
+
* @param veScaKey - The vesca key id.
|
|
87
94
|
* @param ownerAddress - The owner address.
|
|
88
95
|
* @returns Vesca data.
|
|
89
96
|
*/
|
|
90
97
|
export const getVeSca = async (
|
|
91
98
|
query: ScallopQuery,
|
|
92
|
-
|
|
99
|
+
veScaKey?: string | SuiObjectData,
|
|
93
100
|
ownerAddress?: string
|
|
94
101
|
) => {
|
|
95
102
|
const tableId = query.address.get(`vesca.tableId`);
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
veScaKey = veScaKey || (await getVescaKeys(query, ownerAddress))[0];
|
|
104
|
+
|
|
105
|
+
if (!veScaKey) return undefined;
|
|
106
|
+
if (typeof veScaKey === 'object') {
|
|
107
|
+
veScaKey = SuiObjectRefZod.parse(veScaKey) as SuiObjectRefType;
|
|
108
|
+
}
|
|
98
109
|
|
|
99
110
|
let vesca: Vesca | undefined = undefined;
|
|
100
111
|
|
|
@@ -103,7 +114,7 @@ export const getVeSca = async (
|
|
|
103
114
|
parentId: tableId,
|
|
104
115
|
name: {
|
|
105
116
|
type: '0x2::object::ID',
|
|
106
|
-
value:
|
|
117
|
+
value: typeof veScaKey === 'string' ? veScaKey : veScaKey.objectId,
|
|
107
118
|
},
|
|
108
119
|
});
|
|
109
120
|
const veScaDynamicFieldObject = veScaDynamicFieldObjectResponse.data;
|
|
@@ -131,7 +142,9 @@ export const getVeSca = async (
|
|
|
131
142
|
|
|
132
143
|
vesca = {
|
|
133
144
|
id: veScaDynamicFieldObject.objectId,
|
|
134
|
-
keyId:
|
|
145
|
+
keyId: typeof veScaKey === 'string' ? veScaKey : veScaKey.objectId,
|
|
146
|
+
keyObject: typeof veScaKey === 'string' ? undefined : veScaKey,
|
|
147
|
+
object: SuiObjectRefZod.parse(veScaDynamicFieldObjectResponse.data),
|
|
135
148
|
lockedScaAmount,
|
|
136
149
|
lockedScaCoin,
|
|
137
150
|
currentVeScaBalance,
|
|
@@ -145,12 +158,13 @@ export const getVeSca = async (
|
|
|
145
158
|
/**
|
|
146
159
|
* Get current total veSca treasury amount.
|
|
147
160
|
*/
|
|
148
|
-
|
|
149
|
-
query: ScallopQuery
|
|
161
|
+
const getTotalVeScaTreasuryAmount = async (
|
|
162
|
+
query: ScallopQuery,
|
|
163
|
+
veScaTreasury: SuiObjectData
|
|
150
164
|
): Promise<string> => {
|
|
151
165
|
const veScaPkgId = query.address.get('vesca.id');
|
|
152
166
|
const veScaConfig = query.address.get('vesca.config');
|
|
153
|
-
|
|
167
|
+
veScaTreasury = veScaTreasury ?? query.address.get('vesca.treasury');
|
|
154
168
|
|
|
155
169
|
// refresh query
|
|
156
170
|
const refreshQueryTarget = `${veScaPkgId}::treasury::refresh`;
|
|
@@ -201,7 +215,6 @@ export const getTotalVeScaTreasuryAmount = async (
|
|
|
201
215
|
queryFn: async () => {
|
|
202
216
|
return await query.suiKit.inspectTxn(txBytes);
|
|
203
217
|
},
|
|
204
|
-
staleTime: 8000,
|
|
205
218
|
});
|
|
206
219
|
|
|
207
220
|
const results = res.results;
|
|
@@ -213,3 +226,45 @@ export const getTotalVeScaTreasuryAmount = async (
|
|
|
213
226
|
|
|
214
227
|
return '0';
|
|
215
228
|
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get veSCA treasury informations
|
|
232
|
+
* @param query
|
|
233
|
+
* @returns VeScaTreasuryInfo
|
|
234
|
+
*/
|
|
235
|
+
export const getVeScaTreasuryInfo = async (
|
|
236
|
+
query: ScallopQuery
|
|
237
|
+
): Promise<VeScaTreasuryInfo | null> => {
|
|
238
|
+
const veScaTreasuryId = query.address.get('vesca.treasury');
|
|
239
|
+
const veScaTreasury = await query.cache.queryGetObject(veScaTreasuryId, {
|
|
240
|
+
showContent: true,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
if (!veScaTreasury || veScaTreasury.data?.content?.dataType !== 'moveObject')
|
|
244
|
+
return null;
|
|
245
|
+
|
|
246
|
+
const treasuryFields = veScaTreasury.data.content
|
|
247
|
+
.fields as VeScaTreasuryFields;
|
|
248
|
+
|
|
249
|
+
console.log(treasuryFields);
|
|
250
|
+
const totalLockedSca = BigNumber(
|
|
251
|
+
treasuryFields.unlock_schedule.fields.locked_sca_amount
|
|
252
|
+
)
|
|
253
|
+
.shiftedBy(-9)
|
|
254
|
+
.toNumber();
|
|
255
|
+
const totalVeSca = BigNumber(
|
|
256
|
+
(await getTotalVeScaTreasuryAmount(query, veScaTreasury.data)) ?? 0
|
|
257
|
+
)
|
|
258
|
+
.shiftedBy(-9)
|
|
259
|
+
.toNumber();
|
|
260
|
+
const averageLockingPeriod =
|
|
261
|
+
totalLockedSca > 0 ? (totalVeSca / totalLockedSca) * 4 : 0; // in years
|
|
262
|
+
|
|
263
|
+
const averageLockingPeriodUnit = 'year';
|
|
264
|
+
return {
|
|
265
|
+
totalLockedSca,
|
|
266
|
+
totalVeSca,
|
|
267
|
+
averageLockingPeriod,
|
|
268
|
+
averageLockingPeriodUnit,
|
|
269
|
+
};
|
|
270
|
+
};
|
package/src/types/address.ts
CHANGED
|
@@ -115,6 +115,12 @@ export interface AddressesInterface {
|
|
|
115
115
|
tiersTableId: string;
|
|
116
116
|
authorizedWitnessList: string;
|
|
117
117
|
};
|
|
118
|
+
loyaltyProgram: {
|
|
119
|
+
id: string;
|
|
120
|
+
object: string;
|
|
121
|
+
rewardPool: string;
|
|
122
|
+
userRewardTableId: string;
|
|
123
|
+
};
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
type AddressPathsProps<T> = T extends string
|
|
@@ -3,14 +3,17 @@ import type { SpoolTxBlock } from './spool';
|
|
|
3
3
|
import type { BorrowIncentiveTxBlock } from './borrowIncentive';
|
|
4
4
|
import type { VeScaTxBlock } from './vesca';
|
|
5
5
|
import type { ReferralTxBlock } from './referral';
|
|
6
|
+
import { LoyaltyProgramTxBlock } from './loyaltyProgram';
|
|
6
7
|
|
|
7
8
|
export type * from './core';
|
|
8
9
|
export type * from './spool';
|
|
9
10
|
export type * from './borrowIncentive';
|
|
10
11
|
export type * from './vesca';
|
|
12
|
+
export type * from './loyaltyProgram';
|
|
11
13
|
|
|
12
14
|
export type ScallopTxBlock = CoreTxBlock &
|
|
13
15
|
SpoolTxBlock &
|
|
14
16
|
ReferralTxBlock &
|
|
17
|
+
LoyaltyProgramTxBlock &
|
|
15
18
|
BorrowIncentiveTxBlock &
|
|
16
19
|
VeScaTxBlock;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SuiObjectArg,
|
|
3
|
+
SuiTxBlock as SuiKitTxBlock,
|
|
4
|
+
TransactionResult,
|
|
5
|
+
} from '@scallop-io/sui-kit';
|
|
6
|
+
import { type ScallopBuilder } from 'src/models';
|
|
7
|
+
|
|
8
|
+
export type LoyaltyProgramIds = {
|
|
9
|
+
loyaltyProgramPkgId: string;
|
|
10
|
+
rewardPool: string;
|
|
11
|
+
userRewardTableId: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type LoyaltyProgramNormalMethods = {
|
|
15
|
+
claimLoyaltyRevenue: (veScaKey: SuiObjectArg) => TransactionResult;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type LoyaltyProgramQuickMethods = {
|
|
19
|
+
claimLoyaltyRevenueQuick: (veScaKey?: SuiObjectArg) => Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type SuiTxBlockWithLoyaltyProgramNormalMethods = SuiKitTxBlock &
|
|
23
|
+
LoyaltyProgramNormalMethods;
|
|
24
|
+
export type LoyaltyProgramTxBlock = SuiTxBlockWithLoyaltyProgramNormalMethods &
|
|
25
|
+
LoyaltyProgramQuickMethods;
|
|
26
|
+
|
|
27
|
+
export type GenerateLoyaltyProgramNormalMethod = (params: {
|
|
28
|
+
builder: ScallopBuilder;
|
|
29
|
+
txBlock: SuiKitTxBlock;
|
|
30
|
+
}) => LoyaltyProgramNormalMethods;
|
|
31
|
+
|
|
32
|
+
export type GenerateLoyaltyProgramQuickMethod = (params: {
|
|
33
|
+
builder: ScallopBuilder;
|
|
34
|
+
txBlock: SuiTxBlockWithLoyaltyProgramNormalMethods;
|
|
35
|
+
}) => LoyaltyProgramQuickMethods;
|
|
@@ -33,6 +33,22 @@ export type VeScaNormalMethods = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
export type VeScaQuickMethods = {
|
|
36
|
+
/**
|
|
37
|
+
* Quick methods to automate
|
|
38
|
+
* lock initial SCA, extend lock period, lock more SCA, renew expired VeSCA, and redeem SCA
|
|
39
|
+
*
|
|
40
|
+
* **Flow:**
|
|
41
|
+
* - If only `amountOrCoin` is provided, it will lock the amount of existing not expired veSCA
|
|
42
|
+
* - If only `lockPeriodInDays` is provided, it will extend the lock period of existing not expired veSCA
|
|
43
|
+
*
|
|
44
|
+
* **Note:**
|
|
45
|
+
* - If one or both flow above is used on a expired veSCA, it will claim the unlocked SCA
|
|
46
|
+
* and renew the veSCA first, and then flow continues
|
|
47
|
+
* - If users has no veSCA yet, they need to provide both `amountOrCoin` and `lockPeriodInDays` for initial lock
|
|
48
|
+
* @param amountOrCoin
|
|
49
|
+
* @param lockPeriodInDays
|
|
50
|
+
* @param autoCheck
|
|
51
|
+
*/
|
|
36
52
|
lockScaQuick(
|
|
37
53
|
amountOrCoin?: SuiObjectArg | number,
|
|
38
54
|
lockPeriodInDays?: number,
|
package/src/types/query/index.ts
CHANGED
package/src/types/query/vesca.ts
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
|
+
import type { SuiObjectRef } from '@mysten/sui.js/client';
|
|
2
|
+
|
|
1
3
|
export type Vesca = {
|
|
2
4
|
id: string;
|
|
3
5
|
keyId: string;
|
|
6
|
+
keyObject?: SuiObjectRef;
|
|
7
|
+
object?: SuiObjectRef;
|
|
4
8
|
lockedScaAmount: string;
|
|
5
9
|
lockedScaCoin: number;
|
|
6
10
|
currentVeScaBalance: number;
|
|
7
11
|
unlockAt: number;
|
|
8
12
|
};
|
|
13
|
+
|
|
14
|
+
export type VeScaTreasuryFields = {
|
|
15
|
+
total_ve_sca_amount: string;
|
|
16
|
+
sca_balance: string;
|
|
17
|
+
unlock_schedule: {
|
|
18
|
+
fields: {
|
|
19
|
+
locked_sca_amount: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type VeScaTreasuryInfo = {
|
|
25
|
+
totalLockedSca: number;
|
|
26
|
+
totalVeSca: number;
|
|
27
|
+
averageLockingPeriod: number;
|
|
28
|
+
averageLockingPeriodUnit: string;
|
|
29
|
+
};
|