@scallop-io/sui-scallop-sdk 0.44.18 → 0.44.19
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/borrowIncentiveBuilder.d.ts +7 -0
- package/dist/builders/vescaBuilder.d.ts +24 -0
- package/dist/constants/common.d.ts +7 -4
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/vesca.d.ts +5 -0
- package/dist/index.js +874 -254
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +863 -247
- package/dist/index.mjs.map +1 -1
- package/dist/models/scallopClient.d.ts +7 -7
- package/dist/models/scallopUtils.d.ts +16 -14
- package/dist/queries/index.d.ts +1 -0
- package/dist/queries/vescaQuery.d.ts +28 -0
- package/dist/types/address.d.ts +9 -0
- package/dist/types/builder/borrowIncentive.d.ts +9 -6
- package/dist/types/builder/index.d.ts +3 -1
- package/dist/types/builder/vesca.d.ts +33 -0
- package/dist/types/constant/enum.d.ts +1 -1
- package/dist/types/query/borrowIncentive.d.ts +65 -60
- package/dist/types/query/index.d.ts +1 -0
- package/dist/types/query/portfolio.d.ts +12 -6
- package/dist/types/query/vesca.d.ts +7 -0
- package/dist/types/utils.d.ts +1 -2
- package/dist/utils/builder.d.ts +6 -0
- package/dist/utils/query.d.ts +4 -10
- package/dist/utils/util.d.ts +7 -0
- package/package.json +1 -1
- package/src/builders/borrowIncentiveBuilder.ts +174 -25
- package/src/builders/index.ts +6 -2
- package/src/builders/vescaBuilder.ts +392 -0
- package/src/constants/common.ts +19 -6
- package/src/constants/enum.ts +9 -3
- package/src/constants/index.ts +1 -0
- package/src/constants/vesca.ts +7 -0
- package/src/models/scallopAddress.ts +9 -1
- package/src/models/scallopClient.ts +29 -20
- package/src/models/scallopUtils.ts +45 -0
- package/src/queries/borrowIncentiveQuery.ts +93 -83
- package/src/queries/coreQuery.ts +19 -20
- package/src/queries/index.ts +1 -0
- package/src/queries/portfolioQuery.ts +79 -41
- package/src/queries/spoolQuery.ts +1 -1
- package/src/queries/vescaQuery.ts +124 -0
- package/src/types/address.ts +9 -0
- package/src/types/builder/borrowIncentive.ts +22 -5
- package/src/types/builder/index.ts +4 -1
- package/src/types/builder/vesca.ts +73 -0
- package/src/types/constant/enum.ts +1 -1
- package/src/types/query/borrowIncentive.ts +195 -74
- package/src/types/query/index.ts +1 -0
- package/src/types/query/portfolio.ts +17 -6
- package/src/types/query/vesca.ts +7 -0
- package/src/types/utils.ts +1 -1
- package/src/utils/builder.ts +141 -0
- package/src/utils/query.ts +221 -130
- package/src/utils/util.ts +28 -0
|
@@ -13,14 +13,20 @@ import type {
|
|
|
13
13
|
SuiTxBlockWithBorrowIncentiveNormalMethods,
|
|
14
14
|
BorrowIncentiveTxBlock,
|
|
15
15
|
ScallopTxBlock,
|
|
16
|
+
VescaIds,
|
|
16
17
|
} from '../types';
|
|
18
|
+
import { requireVeSca } from './vescaBuilder';
|
|
19
|
+
import {
|
|
20
|
+
IS_VE_SCA_TEST,
|
|
21
|
+
OLD_BORROW_INCENTIVE_PROTOCOL_ID,
|
|
22
|
+
} from 'src/constants';
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
25
|
* Check and get Obligation information from transaction block.
|
|
20
26
|
*
|
|
21
27
|
* @description
|
|
22
|
-
* If the obligation id is provided,
|
|
23
|
-
* If both obligation id and key is provided,
|
|
28
|
+
* If the obligation id is provided, directly return it.
|
|
29
|
+
* If both obligation id and key is provided, directly return them.
|
|
24
30
|
* Otherwise, automatically get obligation id and key from the sender.
|
|
25
31
|
*
|
|
26
32
|
* @param builder - Scallop builder instance.
|
|
@@ -62,6 +68,59 @@ const requireObligationInfo = async (
|
|
|
62
68
|
};
|
|
63
69
|
};
|
|
64
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Check veSca bind status
|
|
73
|
+
* @param query
|
|
74
|
+
* @param veScaKey
|
|
75
|
+
* @returns
|
|
76
|
+
*/
|
|
77
|
+
export const getBindedObligationId = async (
|
|
78
|
+
builder: ScallopBuilder,
|
|
79
|
+
veScaKey: string
|
|
80
|
+
) => {
|
|
81
|
+
const borrowIncentivePkgId = builder.address.get('borrowIncentive.id');
|
|
82
|
+
const incentivePoolsId = builder.address.get(
|
|
83
|
+
'borrowIncentive.incentivePools'
|
|
84
|
+
);
|
|
85
|
+
const veScaPkgId = IS_VE_SCA_TEST
|
|
86
|
+
? '0xb220d034bdf335d77ae5bfbf6daf059c2cc7a1f719b12bfed75d1736fac038c8'
|
|
87
|
+
: builder.address.get('vesca.id');
|
|
88
|
+
|
|
89
|
+
const client = builder.suiKit.client();
|
|
90
|
+
|
|
91
|
+
// get incentive pools
|
|
92
|
+
const incentivePoolsResponse = await client.getObject({
|
|
93
|
+
id: incentivePoolsId,
|
|
94
|
+
options: {
|
|
95
|
+
showContent: true,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (incentivePoolsResponse.data?.content?.dataType !== 'moveObject')
|
|
100
|
+
return false;
|
|
101
|
+
const incentivePoolFields = incentivePoolsResponse.data.content.fields as any;
|
|
102
|
+
const veScaBindTableId = incentivePoolFields.ve_sca_bind.fields.id
|
|
103
|
+
.id as string;
|
|
104
|
+
|
|
105
|
+
// check if veSca is inside the bind table
|
|
106
|
+
const keyType = `${borrowIncentivePkgId}::typed_id::TypedID<${veScaPkgId}::ve_sca::VeScaKey>`;
|
|
107
|
+
const veScaBindTableResponse = await client.getDynamicFieldObject({
|
|
108
|
+
parentId: veScaBindTableId,
|
|
109
|
+
name: {
|
|
110
|
+
type: keyType,
|
|
111
|
+
value: veScaKey,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (veScaBindTableResponse.data?.content?.dataType !== 'moveObject')
|
|
116
|
+
return false;
|
|
117
|
+
const veScaBindTableFields = veScaBindTableResponse.data.content
|
|
118
|
+
.fields as any;
|
|
119
|
+
// get obligationId pair
|
|
120
|
+
const obligationId = veScaBindTableFields.value.fields.id as string;
|
|
121
|
+
|
|
122
|
+
return obligationId;
|
|
123
|
+
};
|
|
65
124
|
/**
|
|
66
125
|
* Generate borrow incentive normal methods.
|
|
67
126
|
*
|
|
@@ -72,59 +131,89 @@ const requireObligationInfo = async (
|
|
|
72
131
|
const generateBorrowIncentiveNormalMethod: GenerateBorrowIncentiveNormalMethod =
|
|
73
132
|
({ builder, txBlock }) => {
|
|
74
133
|
const borrowIncentiveIds: BorrowIncentiveIds = {
|
|
75
|
-
borrowIncentivePkg:
|
|
134
|
+
borrowIncentivePkg: IS_VE_SCA_TEST
|
|
135
|
+
? '0x4d5a7cefa4147b4ace0ca845b20437d6ac0d32e5f2f855171f745472c2576246'
|
|
136
|
+
: builder.address.get('borrowIncentive.id'),
|
|
76
137
|
query: builder.address.get('borrowIncentive.query'),
|
|
138
|
+
config: builder.address.get('borrowIncentive.config'),
|
|
77
139
|
incentivePools: builder.address.get('borrowIncentive.incentivePools'),
|
|
78
140
|
incentiveAccounts: builder.address.get(
|
|
79
141
|
'borrowIncentive.incentiveAccounts'
|
|
80
142
|
),
|
|
81
143
|
obligationAccessStore: builder.address.get('core.obligationAccessStore'),
|
|
82
144
|
};
|
|
145
|
+
|
|
146
|
+
const veScaIds: Omit<VescaIds, 'pkgId'> = {
|
|
147
|
+
table: builder.address.get('vesca.table'),
|
|
148
|
+
treasury: builder.address.get('vesca.treasury'),
|
|
149
|
+
config: builder.address.get('vesca.config'),
|
|
150
|
+
};
|
|
151
|
+
|
|
83
152
|
return {
|
|
84
|
-
stakeObligation: (obligationId,
|
|
85
|
-
// NOTE: Pools without incentives also need to stake after change obligation,
|
|
86
|
-
// the default here use sui as reward coin.
|
|
87
|
-
const rewardCoinName = 'sui';
|
|
88
|
-
const rewardType = builder.utils.parseCoinType(rewardCoinName);
|
|
153
|
+
stakeObligation: (obligationId, obligationKey) => {
|
|
89
154
|
txBlock.moveCall(
|
|
90
155
|
`${borrowIncentiveIds.borrowIncentivePkg}::user::stake`,
|
|
91
156
|
[
|
|
157
|
+
borrowIncentiveIds.config,
|
|
158
|
+
borrowIncentiveIds.incentivePools,
|
|
159
|
+
borrowIncentiveIds.incentiveAccounts,
|
|
160
|
+
obligationKey,
|
|
161
|
+
obligationId,
|
|
162
|
+
borrowIncentiveIds.obligationAccessStore,
|
|
163
|
+
SUI_CLOCK_OBJECT_ID,
|
|
164
|
+
]
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
stakeObligationWithVesca: (obligationId, obligationKey, veScaKey) => {
|
|
168
|
+
txBlock.moveCall(
|
|
169
|
+
`${borrowIncentiveIds.borrowIncentivePkg}::user::stake_with_ve_sca`,
|
|
170
|
+
[
|
|
171
|
+
borrowIncentiveIds.config,
|
|
92
172
|
borrowIncentiveIds.incentivePools,
|
|
93
173
|
borrowIncentiveIds.incentiveAccounts,
|
|
94
|
-
|
|
174
|
+
obligationKey,
|
|
95
175
|
obligationId,
|
|
96
176
|
borrowIncentiveIds.obligationAccessStore,
|
|
177
|
+
veScaIds.config,
|
|
178
|
+
veScaIds.treasury,
|
|
179
|
+
veScaIds.table,
|
|
180
|
+
veScaKey,
|
|
97
181
|
SUI_CLOCK_OBJECT_ID,
|
|
98
182
|
],
|
|
99
|
-
[
|
|
183
|
+
[]
|
|
100
184
|
);
|
|
101
185
|
},
|
|
102
|
-
unstakeObligation: (obligationId,
|
|
103
|
-
// NOTE: Pools without incentives also need to unstake to change obligation,
|
|
104
|
-
// the default here use sui as reward coin.
|
|
105
|
-
const rewardCoinName = 'sui';
|
|
106
|
-
const rewardType = builder.utils.parseCoinType(rewardCoinName);
|
|
186
|
+
unstakeObligation: (obligationId, obligationKey) => {
|
|
107
187
|
txBlock.moveCall(
|
|
108
188
|
`${borrowIncentiveIds.borrowIncentivePkg}::user::unstake`,
|
|
109
189
|
[
|
|
190
|
+
borrowIncentiveIds.config,
|
|
110
191
|
borrowIncentiveIds.incentivePools,
|
|
111
192
|
borrowIncentiveIds.incentiveAccounts,
|
|
112
|
-
|
|
193
|
+
obligationKey,
|
|
113
194
|
obligationId,
|
|
114
195
|
SUI_CLOCK_OBJECT_ID,
|
|
115
|
-
]
|
|
116
|
-
[rewardType]
|
|
196
|
+
]
|
|
117
197
|
);
|
|
118
198
|
},
|
|
119
|
-
claimBorrowIncentive: (
|
|
120
|
-
|
|
199
|
+
claimBorrowIncentive: (
|
|
200
|
+
obligationId,
|
|
201
|
+
obligationKey,
|
|
202
|
+
coinName,
|
|
203
|
+
rewardCoinName
|
|
204
|
+
) => {
|
|
205
|
+
const rewardCoinNames = borrowIncentiveRewardCoins[coinName];
|
|
206
|
+
if (rewardCoinNames.includes(rewardCoinName) === false) {
|
|
207
|
+
throw new Error(`Invalid reward coin name ${rewardCoinName}`);
|
|
208
|
+
}
|
|
121
209
|
const rewardType = builder.utils.parseCoinType(rewardCoinName);
|
|
122
210
|
return txBlock.moveCall(
|
|
123
211
|
`${borrowIncentiveIds.borrowIncentivePkg}::user::redeem_rewards`,
|
|
124
212
|
[
|
|
213
|
+
borrowIncentiveIds.config,
|
|
125
214
|
borrowIncentiveIds.incentivePools,
|
|
126
215
|
borrowIncentiveIds.incentiveAccounts,
|
|
127
|
-
|
|
216
|
+
obligationKey,
|
|
128
217
|
obligationId,
|
|
129
218
|
SUI_CLOCK_OBJECT_ID,
|
|
130
219
|
],
|
|
@@ -144,7 +233,7 @@ const generateBorrowIncentiveNormalMethod: GenerateBorrowIncentiveNormalMethod =
|
|
|
144
233
|
*
|
|
145
234
|
* @param builder - Scallop builder instance.
|
|
146
235
|
* @param txBlock - TxBlock created by SuiKit .
|
|
147
|
-
* @return
|
|
236
|
+
* @return Borrow Incentive quick methods.
|
|
148
237
|
*/
|
|
149
238
|
const generateBorrowIncentiveQuickMethod: GenerateBorrowIncentiveQuickMethod =
|
|
150
239
|
({ builder, txBlock }) => {
|
|
@@ -165,14 +254,72 @@ const generateBorrowIncentiveQuickMethod: GenerateBorrowIncentiveQuickMethod =
|
|
|
165
254
|
!!txBlock.txBlock.blockData.transactions.find(
|
|
166
255
|
(txn) =>
|
|
167
256
|
txn.kind === 'MoveCall' &&
|
|
168
|
-
txn.target ===
|
|
169
|
-
`${
|
|
257
|
+
(txn.target ===
|
|
258
|
+
`${OLD_BORROW_INCENTIVE_PROTOCOL_ID}::user::unstake` ||
|
|
259
|
+
txn.target ===
|
|
260
|
+
(IS_VE_SCA_TEST
|
|
261
|
+
? `${'0x4d5a7cefa4147b4ace0ca845b20437d6ac0d32e5f2f855171f745472c2576246'}::user::unstake`
|
|
262
|
+
: `${builder.address.get(
|
|
263
|
+
'borrowIncentive.id'
|
|
264
|
+
)}::user::unstake`))
|
|
170
265
|
);
|
|
171
266
|
|
|
172
267
|
if (!obligationLocked || unstakeObligationBeforeStake) {
|
|
173
268
|
txBlock.stakeObligation(obligationArg, obligationtKeyArg);
|
|
174
269
|
}
|
|
175
270
|
},
|
|
271
|
+
stakeObligationWithVeScaQuick: async (
|
|
272
|
+
obligation,
|
|
273
|
+
obligationKey,
|
|
274
|
+
veScaKey
|
|
275
|
+
) => {
|
|
276
|
+
const {
|
|
277
|
+
obligationId: obligationArg,
|
|
278
|
+
obligationKey: obligationtKeyArg,
|
|
279
|
+
obligationLocked: obligationLocked,
|
|
280
|
+
} = await requireObligationInfo(
|
|
281
|
+
builder,
|
|
282
|
+
txBlock,
|
|
283
|
+
obligation,
|
|
284
|
+
obligationKey
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
const unstakeObligationBeforeStake =
|
|
288
|
+
!!txBlock.txBlock.blockData.transactions.find(
|
|
289
|
+
(txn) =>
|
|
290
|
+
txn.kind === 'MoveCall' &&
|
|
291
|
+
(txn.target ===
|
|
292
|
+
`${OLD_BORROW_INCENTIVE_PROTOCOL_ID}::user::unstake` ||
|
|
293
|
+
txn.target ===
|
|
294
|
+
(IS_VE_SCA_TEST
|
|
295
|
+
? `${'0x4d5a7cefa4147b4ace0ca845b20437d6ac0d32e5f2f855171f745472c2576246'}::user::unstake`
|
|
296
|
+
: `${builder.address.get(
|
|
297
|
+
'borrowIncentive.id'
|
|
298
|
+
)}::user::unstake`))
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
if (!obligationLocked || unstakeObligationBeforeStake) {
|
|
302
|
+
const veSca = await requireVeSca(builder, txBlock, veScaKey);
|
|
303
|
+
if (veSca) {
|
|
304
|
+
const bindedObligationId = await getBindedObligationId(
|
|
305
|
+
builder,
|
|
306
|
+
veSca.keyId
|
|
307
|
+
);
|
|
308
|
+
// if bindedObligationId is equal to obligationId, then use it again
|
|
309
|
+
if (!bindedObligationId || bindedObligationId === obligationArg) {
|
|
310
|
+
txBlock.stakeObligationWithVesca(
|
|
311
|
+
obligationArg,
|
|
312
|
+
obligationtKeyArg,
|
|
313
|
+
veSca.keyId
|
|
314
|
+
);
|
|
315
|
+
} else {
|
|
316
|
+
txBlock.stakeObligation(obligationArg, obligationtKeyArg);
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
txBlock.stakeObligation(obligationArg, obligationtKeyArg);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
},
|
|
176
323
|
unstakeObligationQuick: async (obligation, obligationKey) => {
|
|
177
324
|
const {
|
|
178
325
|
obligationId: obligationArg,
|
|
@@ -191,6 +338,7 @@ const generateBorrowIncentiveQuickMethod: GenerateBorrowIncentiveQuickMethod =
|
|
|
191
338
|
},
|
|
192
339
|
claimBorrowIncentiveQuick: async (
|
|
193
340
|
coinName,
|
|
341
|
+
rewardCoinName,
|
|
194
342
|
obligation,
|
|
195
343
|
obligationKey
|
|
196
344
|
) => {
|
|
@@ -207,7 +355,8 @@ const generateBorrowIncentiveQuickMethod: GenerateBorrowIncentiveQuickMethod =
|
|
|
207
355
|
return txBlock.claimBorrowIncentive(
|
|
208
356
|
obligationArg,
|
|
209
357
|
obligationtKeyArg,
|
|
210
|
-
coinName
|
|
358
|
+
coinName,
|
|
359
|
+
rewardCoinName
|
|
211
360
|
);
|
|
212
361
|
},
|
|
213
362
|
};
|
package/src/builders/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { SuiTxBlock as SuiKitTxBlock } from '@scallop-io/sui-kit';
|
|
|
3
3
|
import { newCoreTxBlock } from './coreBuilder';
|
|
4
4
|
import { newSpoolTxBlock } from './spoolBuilder';
|
|
5
5
|
import { newBorrowIncentiveTxBlock } from './borrowIncentiveBuilder';
|
|
6
|
+
import { newVeScaTxBlock } from './vescaBuilder';
|
|
6
7
|
import type { ScallopBuilder } from '../models';
|
|
7
8
|
import type { ScallopTxBlock } from '../types';
|
|
8
9
|
|
|
@@ -17,16 +18,19 @@ export const newScallopTxBlock = (
|
|
|
17
18
|
builder: ScallopBuilder,
|
|
18
19
|
initTxBlock?: ScallopTxBlock | SuiKitTxBlock | TransactionBlock
|
|
19
20
|
): ScallopTxBlock => {
|
|
21
|
+
const vescaTxBlock = newVeScaTxBlock(builder, initTxBlock);
|
|
20
22
|
const borrowIncentiveTxBlock = newBorrowIncentiveTxBlock(
|
|
21
23
|
builder,
|
|
22
|
-
|
|
24
|
+
vescaTxBlock
|
|
23
25
|
);
|
|
24
26
|
const spoolTxBlock = newSpoolTxBlock(builder, borrowIncentiveTxBlock);
|
|
25
27
|
const coreTxBlock = newCoreTxBlock(builder, spoolTxBlock);
|
|
26
28
|
|
|
27
29
|
return new Proxy(coreTxBlock, {
|
|
28
30
|
get: (target, prop) => {
|
|
29
|
-
if (prop in
|
|
31
|
+
if (prop in vescaTxBlock) {
|
|
32
|
+
return Reflect.get(vescaTxBlock, prop);
|
|
33
|
+
} else if (prop in borrowIncentiveTxBlock) {
|
|
30
34
|
return Reflect.get(borrowIncentiveTxBlock, prop);
|
|
31
35
|
} else if (prop in spoolTxBlock) {
|
|
32
36
|
return Reflect.get(spoolTxBlock, prop);
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SUI_CLOCK_OBJECT_ID,
|
|
3
|
+
SuiAddressArg,
|
|
4
|
+
SuiTxBlock,
|
|
5
|
+
TransactionBlock,
|
|
6
|
+
SuiTxBlock as SuiKitTxBlock,
|
|
7
|
+
} from '@scallop-io/sui-kit';
|
|
8
|
+
import { SCA_COIN_TYPE } from 'src/constants';
|
|
9
|
+
import { ScallopBuilder } from '../models';
|
|
10
|
+
import { getVeSca, getVeScas } from '../queries';
|
|
11
|
+
import {
|
|
12
|
+
requireSender,
|
|
13
|
+
checkLockSca,
|
|
14
|
+
checkExtendLockPeriod,
|
|
15
|
+
checkExtendLockAmount,
|
|
16
|
+
checkRenewExpiredVeSca,
|
|
17
|
+
checkVesca,
|
|
18
|
+
} from '../utils';
|
|
19
|
+
import type {
|
|
20
|
+
TransactionObjectArgument,
|
|
21
|
+
SuiObjectArg,
|
|
22
|
+
} from '@scallop-io/sui-kit';
|
|
23
|
+
import type {
|
|
24
|
+
GenerateVeScaNormalMethod,
|
|
25
|
+
GenerateVeScaQuickMethod,
|
|
26
|
+
ScallopTxBlock,
|
|
27
|
+
SuiTxBlockWithVeScaNormalMethods,
|
|
28
|
+
VeScaTxBlock,
|
|
29
|
+
VescaIds,
|
|
30
|
+
} from 'src/types';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Check and get veSCA data from transaction block.
|
|
34
|
+
*
|
|
35
|
+
* @description
|
|
36
|
+
* If the veScaKey id is provided, directly return it.
|
|
37
|
+
* Otherwise, automatically get veScaKey from the sender.
|
|
38
|
+
*
|
|
39
|
+
* @param builder - Scallop builder instance.
|
|
40
|
+
* @param txBlock - TxBlock created by SuiKit.
|
|
41
|
+
* @param veScaKey - veSCA key.
|
|
42
|
+
* @return veSCA key, ID, locked amount and unlock at timestamp.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
export const requireVeSca = async (
|
|
46
|
+
...params: [
|
|
47
|
+
builder: ScallopBuilder,
|
|
48
|
+
SuiTxBlock: SuiTxBlock,
|
|
49
|
+
veScaKey?: SuiAddressArg,
|
|
50
|
+
]
|
|
51
|
+
) => {
|
|
52
|
+
const [builder, txBlock, veScaKey] = params;
|
|
53
|
+
if (params.length === 3 && veScaKey && typeof veScaKey === 'string') {
|
|
54
|
+
const veSca = await getVeSca(builder.query, veScaKey);
|
|
55
|
+
|
|
56
|
+
if (!veSca) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return veSca;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const sender = requireSender(txBlock);
|
|
64
|
+
const veScas = await getVeScas(builder.query, sender);
|
|
65
|
+
if (veScas.length === 0) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return veScas[0];
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate veSCA normal methods.
|
|
74
|
+
*
|
|
75
|
+
* @param builder - Scallop builder instance.
|
|
76
|
+
* @param txBlock - TxBlock created by SuiKit .
|
|
77
|
+
* @return veSCA normal methods.
|
|
78
|
+
*/
|
|
79
|
+
const generateNormalVeScaMethod: GenerateVeScaNormalMethod = ({
|
|
80
|
+
builder,
|
|
81
|
+
txBlock,
|
|
82
|
+
}) => {
|
|
83
|
+
const veScaIds: VescaIds = {
|
|
84
|
+
pkgId: builder.address.get('vesca.id'),
|
|
85
|
+
table: builder.address.get('vesca.table'),
|
|
86
|
+
treasury: builder.address.get('vesca.treasury'),
|
|
87
|
+
config: builder.address.get('vesca.config'),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
lockSca: (scaCoin, unlockAtInSecondTimestamp) => {
|
|
92
|
+
return txBlock.moveCall(
|
|
93
|
+
`${veScaIds.pkgId}::ve_sca::mint_ve_sca_key`,
|
|
94
|
+
[
|
|
95
|
+
veScaIds.config,
|
|
96
|
+
veScaIds.table,
|
|
97
|
+
veScaIds.treasury,
|
|
98
|
+
scaCoin,
|
|
99
|
+
unlockAtInSecondTimestamp,
|
|
100
|
+
SUI_CLOCK_OBJECT_ID,
|
|
101
|
+
],
|
|
102
|
+
[]
|
|
103
|
+
);
|
|
104
|
+
},
|
|
105
|
+
extendLockPeriod: (veScaKey, newUnlockAtInSecondTimestamp) => {
|
|
106
|
+
txBlock.moveCall(
|
|
107
|
+
`${veScaIds.pkgId}::ve_sca::extend_lock_period`,
|
|
108
|
+
[
|
|
109
|
+
veScaIds.config,
|
|
110
|
+
veScaKey,
|
|
111
|
+
veScaIds.table,
|
|
112
|
+
veScaIds.treasury,
|
|
113
|
+
newUnlockAtInSecondTimestamp,
|
|
114
|
+
SUI_CLOCK_OBJECT_ID,
|
|
115
|
+
],
|
|
116
|
+
[]
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
extendLockAmount: (veScaKey, scaCoin) => {
|
|
120
|
+
txBlock.moveCall(
|
|
121
|
+
`${veScaIds.pkgId}::ve_sca::lock_more_sca`,
|
|
122
|
+
[
|
|
123
|
+
veScaIds.config,
|
|
124
|
+
veScaKey,
|
|
125
|
+
veScaIds.table,
|
|
126
|
+
veScaIds.treasury,
|
|
127
|
+
scaCoin,
|
|
128
|
+
SUI_CLOCK_OBJECT_ID,
|
|
129
|
+
],
|
|
130
|
+
[]
|
|
131
|
+
);
|
|
132
|
+
},
|
|
133
|
+
renewExpiredVeSca: (veScaKey, scaCoin, newUnlockAtInSecondTimestamp) => {
|
|
134
|
+
txBlock.moveCall(
|
|
135
|
+
`${veScaIds.pkgId}::ve_sca::renew_expired_ve_sca`,
|
|
136
|
+
[
|
|
137
|
+
veScaIds.config,
|
|
138
|
+
veScaKey,
|
|
139
|
+
veScaIds.table,
|
|
140
|
+
veScaIds.treasury,
|
|
141
|
+
scaCoin,
|
|
142
|
+
newUnlockAtInSecondTimestamp,
|
|
143
|
+
SUI_CLOCK_OBJECT_ID,
|
|
144
|
+
],
|
|
145
|
+
[]
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
redeemSca: (veScaKey) => {
|
|
149
|
+
return txBlock.moveCall(
|
|
150
|
+
`${veScaIds.pkgId}::ve_sca::redeem`,
|
|
151
|
+
[
|
|
152
|
+
veScaIds.config,
|
|
153
|
+
veScaKey,
|
|
154
|
+
veScaIds.table,
|
|
155
|
+
veScaIds.treasury,
|
|
156
|
+
SUI_CLOCK_OBJECT_ID,
|
|
157
|
+
],
|
|
158
|
+
[]
|
|
159
|
+
);
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Generate veSCA quick methods.
|
|
166
|
+
*
|
|
167
|
+
* @description
|
|
168
|
+
* The quick methods are the same as the normal methods, but they will automatically
|
|
169
|
+
* help users organize transaction blocks, include get veSca info, and transfer
|
|
170
|
+
* coins to the sender. So, they are all asynchronous methods.
|
|
171
|
+
*
|
|
172
|
+
* @param builder - Scallop builder instance.
|
|
173
|
+
* @param txBlock - TxBlock created by SuiKit .
|
|
174
|
+
* @return veSCA quick methods.
|
|
175
|
+
*/
|
|
176
|
+
const generateQuickVeScaMethod: GenerateVeScaQuickMethod = ({
|
|
177
|
+
builder,
|
|
178
|
+
txBlock,
|
|
179
|
+
}) => {
|
|
180
|
+
return {
|
|
181
|
+
lockScaQuick: async (amountOrCoin, lockPeriodInDays, autoCheck = true) => {
|
|
182
|
+
const sender = requireSender(txBlock);
|
|
183
|
+
const veSca = await requireVeSca(builder, txBlock);
|
|
184
|
+
|
|
185
|
+
let scaCoin: TransactionObjectArgument | SuiObjectArg | undefined =
|
|
186
|
+
undefined;
|
|
187
|
+
const transferObjects = [];
|
|
188
|
+
if (amountOrCoin !== undefined && typeof amountOrCoin === 'number') {
|
|
189
|
+
const coins = await builder.utils.selectCoinIds(
|
|
190
|
+
amountOrCoin,
|
|
191
|
+
SCA_COIN_TYPE,
|
|
192
|
+
sender
|
|
193
|
+
);
|
|
194
|
+
const [takeCoin, leftCoin] = txBlock.takeAmountFromCoins(
|
|
195
|
+
coins,
|
|
196
|
+
amountOrCoin
|
|
197
|
+
);
|
|
198
|
+
scaCoin = takeCoin;
|
|
199
|
+
transferObjects.push(leftCoin);
|
|
200
|
+
} else {
|
|
201
|
+
// With amountOrCoin is SuiObjectArg, we cannot validate the minimum sca amount for locking and topup
|
|
202
|
+
scaCoin = amountOrCoin;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const newUnlockAt = builder.utils.getUnlockAt(
|
|
206
|
+
lockPeriodInDays,
|
|
207
|
+
veSca?.unlockAt
|
|
208
|
+
);
|
|
209
|
+
if (autoCheck)
|
|
210
|
+
checkLockSca(
|
|
211
|
+
amountOrCoin,
|
|
212
|
+
lockPeriodInDays,
|
|
213
|
+
newUnlockAt,
|
|
214
|
+
veSca?.unlockAt
|
|
215
|
+
);
|
|
216
|
+
console.log(
|
|
217
|
+
new Date(newUnlockAt * 1000).toLocaleString('en-CA', {
|
|
218
|
+
hour12: true,
|
|
219
|
+
})
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const isInitialLock = !veSca?.unlockAt;
|
|
223
|
+
const isLockExpired =
|
|
224
|
+
!isInitialLock && veSca.unlockAt * 1000 <= new Date().getTime();
|
|
225
|
+
if (isInitialLock || isLockExpired) {
|
|
226
|
+
if (scaCoin) {
|
|
227
|
+
if (isInitialLock) {
|
|
228
|
+
const veScaKey = txBlock.lockSca(scaCoin, newUnlockAt);
|
|
229
|
+
transferObjects.push(veScaKey);
|
|
230
|
+
} else {
|
|
231
|
+
// user must withdraw current unlocked SCA first if any
|
|
232
|
+
if (veSca.lockedScaAmount !== 0) {
|
|
233
|
+
const unlockedSca = txBlock.redeemSca(veSca.keyId);
|
|
234
|
+
transferObjects.push(unlockedSca);
|
|
235
|
+
}
|
|
236
|
+
// enforce renew on expired
|
|
237
|
+
txBlock.renewExpiredVeSca(veSca.keyId, scaCoin, newUnlockAt);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
if (!!scaCoin && !!lockPeriodInDays) {
|
|
242
|
+
txBlock.extendLockPeriod(veSca.keyId, newUnlockAt);
|
|
243
|
+
txBlock.extendLockAmount(veSca.keyId, scaCoin);
|
|
244
|
+
} else if (lockPeriodInDays) {
|
|
245
|
+
txBlock.extendLockPeriod(veSca.keyId, newUnlockAt);
|
|
246
|
+
} else if (scaCoin) {
|
|
247
|
+
txBlock.extendLockAmount(veSca.keyId, scaCoin);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (transferObjects.length > 0) {
|
|
252
|
+
txBlock.transferObjects(transferObjects, sender);
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
extendLockPeriodQuick: async (
|
|
256
|
+
lockPeriodInDays: number,
|
|
257
|
+
veScaKey?: SuiAddressArg,
|
|
258
|
+
autoCheck = true
|
|
259
|
+
) => {
|
|
260
|
+
const veSca = await requireVeSca(builder, txBlock, veScaKey);
|
|
261
|
+
|
|
262
|
+
const newUnlockAt = builder.utils.getUnlockAt(lockPeriodInDays);
|
|
263
|
+
if (autoCheck)
|
|
264
|
+
checkExtendLockPeriod(lockPeriodInDays, newUnlockAt, veSca?.unlockAt);
|
|
265
|
+
|
|
266
|
+
if (veSca) {
|
|
267
|
+
txBlock.extendLockPeriod(veSca.keyId, newUnlockAt);
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
extendLockAmountQuick: async (
|
|
271
|
+
scaAmount: number,
|
|
272
|
+
veScaKey?: SuiAddressArg,
|
|
273
|
+
autoCheck = true
|
|
274
|
+
) => {
|
|
275
|
+
const sender = requireSender(txBlock);
|
|
276
|
+
const veSca = await requireVeSca(builder, txBlock, veScaKey);
|
|
277
|
+
|
|
278
|
+
if (autoCheck) checkExtendLockAmount(scaAmount, veSca?.unlockAt);
|
|
279
|
+
|
|
280
|
+
if (veSca) {
|
|
281
|
+
const scaCoins = await builder.utils.selectCoinIds(
|
|
282
|
+
scaAmount,
|
|
283
|
+
SCA_COIN_TYPE,
|
|
284
|
+
sender
|
|
285
|
+
);
|
|
286
|
+
const [takeCoin, leftCoin] = txBlock.takeAmountFromCoins(
|
|
287
|
+
scaCoins,
|
|
288
|
+
scaAmount
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
txBlock.extendLockAmount(veSca.keyId, takeCoin);
|
|
292
|
+
txBlock.transferObjects([leftCoin], sender);
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
renewExpiredVeScaQuick: async (
|
|
296
|
+
scaAmount: number,
|
|
297
|
+
lockPeriodInDays: number,
|
|
298
|
+
veScaKey?: SuiAddressArg,
|
|
299
|
+
autoCheck = true
|
|
300
|
+
) => {
|
|
301
|
+
const sender = requireSender(txBlock);
|
|
302
|
+
const veSca = await requireVeSca(builder, txBlock, veScaKey);
|
|
303
|
+
|
|
304
|
+
const newUnlockAt = builder.utils.getUnlockAt(
|
|
305
|
+
lockPeriodInDays,
|
|
306
|
+
veSca?.unlockAt
|
|
307
|
+
);
|
|
308
|
+
if (autoCheck)
|
|
309
|
+
checkRenewExpiredVeSca(scaAmount, lockPeriodInDays, veSca?.unlockAt);
|
|
310
|
+
|
|
311
|
+
if (veSca) {
|
|
312
|
+
const transferObjects = [];
|
|
313
|
+
if (veSca.lockedScaAmount !== 0) {
|
|
314
|
+
const unlockedSca = txBlock.redeemSca(veSca.keyId);
|
|
315
|
+
transferObjects.push(unlockedSca);
|
|
316
|
+
}
|
|
317
|
+
const scaCoins = await builder.utils.selectCoinIds(
|
|
318
|
+
scaAmount,
|
|
319
|
+
SCA_COIN_TYPE,
|
|
320
|
+
sender
|
|
321
|
+
);
|
|
322
|
+
const [takeCoin, leftCoin] = txBlock.takeAmountFromCoins(
|
|
323
|
+
scaCoins,
|
|
324
|
+
scaAmount
|
|
325
|
+
);
|
|
326
|
+
transferObjects.push(leftCoin);
|
|
327
|
+
|
|
328
|
+
txBlock.renewExpiredVeSca(veSca.keyId, takeCoin, newUnlockAt);
|
|
329
|
+
txBlock.transferObjects(transferObjects, sender);
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
redeemScaQuick: async (veScaKey?: SuiAddressArg) => {
|
|
333
|
+
const sender = requireSender(txBlock);
|
|
334
|
+
const veSca = await requireVeSca(builder, txBlock, veScaKey);
|
|
335
|
+
|
|
336
|
+
checkVesca(veSca?.unlockAt);
|
|
337
|
+
|
|
338
|
+
if (veSca) {
|
|
339
|
+
const sca = txBlock.redeemSca(veSca.keyId);
|
|
340
|
+
txBlock.transferObjects([sca], sender);
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Create an enhanced transaction block instance for interaction with veSCA modules of the Scallop contract.
|
|
348
|
+
*
|
|
349
|
+
* @param builder - Scallop builder instance.
|
|
350
|
+
* @param initTxBlock - Scallop txBlock, txBlock created by SuiKit, or original transaction block.
|
|
351
|
+
* @return Scallop borrow incentive txBlock.
|
|
352
|
+
*/
|
|
353
|
+
export const newVeScaTxBlock = (
|
|
354
|
+
builder: ScallopBuilder,
|
|
355
|
+
initTxBlock?: ScallopTxBlock | SuiKitTxBlock | TransactionBlock
|
|
356
|
+
) => {
|
|
357
|
+
const txBlock =
|
|
358
|
+
initTxBlock instanceof TransactionBlock
|
|
359
|
+
? new SuiKitTxBlock(initTxBlock)
|
|
360
|
+
: initTxBlock
|
|
361
|
+
? initTxBlock
|
|
362
|
+
: new SuiKitTxBlock();
|
|
363
|
+
|
|
364
|
+
const normalMethod = generateNormalVeScaMethod({
|
|
365
|
+
builder,
|
|
366
|
+
txBlock,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const normalTxBlock = new Proxy(txBlock, {
|
|
370
|
+
get: (target, prop) => {
|
|
371
|
+
if (prop in normalMethod) {
|
|
372
|
+
return Reflect.get(normalMethod, prop);
|
|
373
|
+
}
|
|
374
|
+
return Reflect.get(target, prop);
|
|
375
|
+
},
|
|
376
|
+
}) as SuiTxBlockWithVeScaNormalMethods;
|
|
377
|
+
|
|
378
|
+
// TODO: Add quickMethod for veSCA
|
|
379
|
+
const quickMethod = generateQuickVeScaMethod({
|
|
380
|
+
builder,
|
|
381
|
+
txBlock: normalTxBlock,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
return new Proxy(normalTxBlock, {
|
|
385
|
+
get: (target, prop) => {
|
|
386
|
+
if (prop in quickMethod) {
|
|
387
|
+
return Reflect.get(quickMethod, prop);
|
|
388
|
+
}
|
|
389
|
+
return Reflect.get(target, prop);
|
|
390
|
+
},
|
|
391
|
+
}) as VeScaTxBlock;
|
|
392
|
+
};
|