@haneullabs/haneulns 0.1.0

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 (70) hide show
  1. package/CHANGELOG.md +505 -0
  2. package/README.md +9 -0
  3. package/dist/cjs/constants.d.ts +11 -0
  4. package/dist/cjs/constants.js +120 -0
  5. package/dist/cjs/constants.js.map +7 -0
  6. package/dist/cjs/haneulns-client.d.ts +40 -0
  7. package/dist/cjs/haneulns-client.js +261 -0
  8. package/dist/cjs/haneulns-client.js.map +7 -0
  9. package/dist/cjs/haneulns-transaction.d.ts +105 -0
  10. package/dist/cjs/haneulns-transaction.js +465 -0
  11. package/dist/cjs/haneulns-transaction.js.map +7 -0
  12. package/dist/cjs/helpers.d.ts +18 -0
  13. package/dist/cjs/helpers.js +63 -0
  14. package/dist/cjs/helpers.js.map +7 -0
  15. package/dist/cjs/index.d.ts +5 -0
  16. package/dist/cjs/index.js +39 -0
  17. package/dist/cjs/index.js.map +7 -0
  18. package/dist/cjs/package.json +5 -0
  19. package/dist/cjs/pyth/PriceServiceConnection.d.ts +26 -0
  20. package/dist/cjs/pyth/PriceServiceConnection.js +68 -0
  21. package/dist/cjs/pyth/PriceServiceConnection.js.map +7 -0
  22. package/dist/cjs/pyth/pyth-helpers.d.ts +7 -0
  23. package/dist/cjs/pyth/pyth-helpers.js +36 -0
  24. package/dist/cjs/pyth/pyth-helpers.js.map +7 -0
  25. package/dist/cjs/pyth/pyth.d.ts +66 -0
  26. package/dist/cjs/pyth/pyth.js +270 -0
  27. package/dist/cjs/pyth/pyth.js.map +7 -0
  28. package/dist/cjs/types.d.ts +89 -0
  29. package/dist/cjs/types.js +17 -0
  30. package/dist/cjs/types.js.map +7 -0
  31. package/dist/esm/constants.d.ts +11 -0
  32. package/dist/esm/constants.js +100 -0
  33. package/dist/esm/constants.js.map +7 -0
  34. package/dist/esm/haneulns-client.d.ts +40 -0
  35. package/dist/esm/haneulns-client.js +249 -0
  36. package/dist/esm/haneulns-client.js.map +7 -0
  37. package/dist/esm/haneulns-transaction.d.ts +105 -0
  38. package/dist/esm/haneulns-transaction.js +445 -0
  39. package/dist/esm/haneulns-transaction.js.map +7 -0
  40. package/dist/esm/helpers.d.ts +18 -0
  41. package/dist/esm/helpers.js +43 -0
  42. package/dist/esm/helpers.js.map +7 -0
  43. package/dist/esm/index.d.ts +5 -0
  44. package/dist/esm/index.js +28 -0
  45. package/dist/esm/index.js.map +7 -0
  46. package/dist/esm/package.json +5 -0
  47. package/dist/esm/pyth/PriceServiceConnection.d.ts +26 -0
  48. package/dist/esm/pyth/PriceServiceConnection.js +38 -0
  49. package/dist/esm/pyth/PriceServiceConnection.js.map +7 -0
  50. package/dist/esm/pyth/pyth-helpers.d.ts +7 -0
  51. package/dist/esm/pyth/pyth-helpers.js +16 -0
  52. package/dist/esm/pyth/pyth-helpers.js.map +7 -0
  53. package/dist/esm/pyth/pyth.d.ts +66 -0
  54. package/dist/esm/pyth/pyth.js +250 -0
  55. package/dist/esm/pyth/pyth.js.map +7 -0
  56. package/dist/esm/types.d.ts +89 -0
  57. package/dist/esm/types.js +1 -0
  58. package/dist/esm/types.js.map +7 -0
  59. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  60. package/dist/tsconfig.tsbuildinfo +1 -0
  61. package/package.json +63 -0
  62. package/src/constants.ts +106 -0
  63. package/src/haneulns-client.ts +368 -0
  64. package/src/haneulns-transaction.ts +555 -0
  65. package/src/helpers.ts +52 -0
  66. package/src/index.ts +16 -0
  67. package/src/pyth/PriceServiceConnection.ts +48 -0
  68. package/src/pyth/pyth-helpers.ts +23 -0
  69. package/src/pyth/pyth.ts +312 -0
  70. package/src/types.ts +111 -0
@@ -0,0 +1,555 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { bcs } from '@haneullabs/haneul/bcs';
5
+ import type {
6
+ Transaction,
7
+ TransactionObjectArgument,
8
+ TransactionObjectInput,
9
+ } from '@haneullabs/haneul/transactions';
10
+ import { isValidHaneulNSName, normalizeHaneulNSName, HANEUL_CLOCK_OBJECT_ID } from '@haneullabs/haneul/utils';
11
+
12
+ import { ALLOWED_METADATA, MAX_U64 } from './constants.js';
13
+ import { isNestedSubName, isSubName, zeroCoin } from './helpers.js';
14
+ import type { HaneulnsClient } from './haneulns-client.js';
15
+ import type { DiscountInfo, ReceiptParams, RegistrationParams, RenewalParams } from './types.js';
16
+
17
+ export class HaneulnsTransaction {
18
+ haneulnsClient: HaneulnsClient;
19
+ transaction: Transaction;
20
+
21
+ constructor(client: HaneulnsClient, transaction: Transaction) {
22
+ this.haneulnsClient = client;
23
+ this.transaction = transaction;
24
+ }
25
+
26
+ /**
27
+ * Registers a domain for a number of years.
28
+ */
29
+ register(params: RegistrationParams): TransactionObjectArgument {
30
+ if (params.couponCode && params.discountInfo) {
31
+ throw new Error('Cannot apply both coupon and discount NFT');
32
+ }
33
+
34
+ const paymentIntent = this.initRegistration(params.domain);
35
+ if (params.couponCode) {
36
+ this.applyCoupon(paymentIntent, params.couponCode);
37
+ }
38
+ if (params.discountInfo) {
39
+ this.applyDiscount(paymentIntent, params.discountInfo);
40
+ }
41
+ const priceAfterDiscount = this.calculatePriceAfterDiscount(
42
+ paymentIntent,
43
+ params.coinConfig.type,
44
+ );
45
+ const receipt = this.generateReceipt({
46
+ paymentIntent,
47
+ priceAfterDiscount,
48
+ coinConfig: params.coinConfig,
49
+ coin: params.coin,
50
+ maxAmount: params.maxAmount,
51
+ priceInfoObjectId: params.priceInfoObjectId,
52
+ });
53
+ const nft = this.finalizeRegister(receipt);
54
+
55
+ if (params.years > 1) {
56
+ this.renew({
57
+ nft,
58
+ years: params.years - 1,
59
+ coinConfig: params.coinConfig,
60
+ coin: params.coin,
61
+ couponCode: params.couponCode,
62
+ discountInfo: params.discountInfo,
63
+ maxAmount: params.maxAmount,
64
+ priceInfoObjectId: params.priceInfoObjectId,
65
+ });
66
+ }
67
+
68
+ return nft as TransactionObjectArgument;
69
+ }
70
+
71
+ /**
72
+ * Renews an NFT for a number of years.
73
+ */
74
+ renew(params: RenewalParams): void {
75
+ if (params.couponCode && params.discountInfo) {
76
+ throw new Error('Cannot apply both coupon and discount NFT');
77
+ }
78
+
79
+ const paymentIntent = this.initRenewal(params.nft, params.years);
80
+ if (params.couponCode) {
81
+ this.applyCoupon(paymentIntent, params.couponCode);
82
+ }
83
+ if (params.discountInfo) {
84
+ this.applyDiscount(paymentIntent, params.discountInfo);
85
+ }
86
+ const priceAfterDiscount = this.calculatePriceAfterDiscount(
87
+ paymentIntent,
88
+ params.coinConfig.type,
89
+ );
90
+ const receipt = this.generateReceipt({
91
+ paymentIntent,
92
+ priceAfterDiscount,
93
+ coinConfig: params.coinConfig,
94
+ coin: params.coin,
95
+ maxAmount: params.maxAmount,
96
+ priceInfoObjectId: params.priceInfoObjectId,
97
+ });
98
+ this.finalizeRenew(receipt, params.nft);
99
+ }
100
+
101
+ initRegistration(domain: string): TransactionObjectArgument {
102
+ const config = this.haneulnsClient.config;
103
+ return this.transaction.moveCall({
104
+ target: `${config.packageId}::payment::init_registration`,
105
+ arguments: [this.transaction.object(config.haneulns), this.transaction.pure.string(domain)],
106
+ });
107
+ }
108
+
109
+ initRenewal(nft: TransactionObjectInput, years: number): TransactionObjectArgument {
110
+ const config = this.haneulnsClient.config;
111
+ return this.transaction.moveCall({
112
+ target: `${config.packageId}::payment::init_renewal`,
113
+ arguments: [
114
+ this.transaction.object(config.haneulns),
115
+ this.transaction.object(nft),
116
+ this.transaction.pure.u8(years),
117
+ ],
118
+ });
119
+ }
120
+
121
+ calculatePrice(
122
+ baseAmount: TransactionObjectArgument,
123
+ paymentType: string,
124
+ priceInfoObjectId: string,
125
+ ): TransactionObjectArgument {
126
+ const config = this.haneulnsClient.config;
127
+ return this.transaction.moveCall({
128
+ target: `${config.payments.packageId}::payments::calculate_price`,
129
+ arguments: [
130
+ this.transaction.object(config.haneulns),
131
+ baseAmount,
132
+ this.transaction.object.clock(),
133
+ this.transaction.object(priceInfoObjectId),
134
+ ],
135
+ typeArguments: [paymentType],
136
+ });
137
+ }
138
+
139
+ handleBasePayment(
140
+ paymentIntent: TransactionObjectArgument,
141
+ payment: TransactionObjectArgument,
142
+ paymentType: string,
143
+ ): TransactionObjectArgument {
144
+ const config = this.haneulnsClient.config;
145
+ return this.transaction.moveCall({
146
+ target: `${config.payments.packageId}::payments::handle_base_payment`,
147
+ arguments: [
148
+ this.transaction.object(config.haneulns),
149
+ this.transaction.object(config.bbb.vault),
150
+ paymentIntent,
151
+ payment,
152
+ ],
153
+ typeArguments: [paymentType],
154
+ });
155
+ }
156
+
157
+ handlePayment(
158
+ paymentIntent: TransactionObjectArgument,
159
+ payment: TransactionObjectArgument,
160
+ paymentType: string,
161
+ priceInfoObjectId: string,
162
+ maxAmount: bigint = MAX_U64,
163
+ ): TransactionObjectArgument {
164
+ const config = this.haneulnsClient.config;
165
+ return this.transaction.moveCall({
166
+ target: `${config.payments.packageId}::payments::handle_payment`,
167
+ arguments: [
168
+ this.transaction.object(config.haneulns),
169
+ this.transaction.object(config.bbb.vault),
170
+ paymentIntent,
171
+ payment,
172
+ this.transaction.object.clock(),
173
+ this.transaction.object(priceInfoObjectId),
174
+ this.transaction.pure.u64(maxAmount),
175
+ ],
176
+ typeArguments: [paymentType],
177
+ });
178
+ }
179
+
180
+ finalizeRegister(receipt: TransactionObjectArgument): TransactionObjectArgument {
181
+ const config = this.haneulnsClient.config;
182
+ return this.transaction.moveCall({
183
+ target: `${config.packageId}::payment::register`,
184
+ arguments: [receipt, this.transaction.object(config.haneulns), this.transaction.object.clock()],
185
+ });
186
+ }
187
+
188
+ finalizeRenew(
189
+ receipt: TransactionObjectArgument,
190
+ nft: TransactionObjectInput,
191
+ ): TransactionObjectArgument {
192
+ const config = this.haneulnsClient.config;
193
+ return this.transaction.moveCall({
194
+ target: `${config.packageId}::payment::renew`,
195
+ arguments: [
196
+ receipt,
197
+ this.transaction.object(config.haneulns),
198
+ this.transaction.object(nft),
199
+ this.transaction.object.clock(),
200
+ ],
201
+ });
202
+ }
203
+
204
+ calculatePriceAfterDiscount(
205
+ paymentIntent: TransactionObjectArgument,
206
+ paymentType: string,
207
+ ): TransactionObjectArgument {
208
+ const config = this.haneulnsClient.config;
209
+ return this.transaction.moveCall({
210
+ target: `${config.payments.packageId}::payments::calculate_price_after_discount`,
211
+ arguments: [this.transaction.object(config.haneulns), paymentIntent],
212
+ typeArguments: [paymentType],
213
+ });
214
+ }
215
+
216
+ generateReceipt(params: ReceiptParams): TransactionObjectArgument {
217
+ const baseAssetPurchase = params.coinConfig.feed === '';
218
+ if (baseAssetPurchase) {
219
+ const payment = params.coin
220
+ ? this.transaction.splitCoins(this.transaction.object(params.coin), [
221
+ params.priceAfterDiscount,
222
+ ])
223
+ : zeroCoin(this.transaction, params.coinConfig.type);
224
+ const receipt = this.handleBasePayment(params.paymentIntent, payment, params.coinConfig.type);
225
+ return receipt;
226
+ } else {
227
+ const priceInfoObjectId = params.priceInfoObjectId;
228
+ if (!priceInfoObjectId)
229
+ throw new Error('Price info object ID is required for non-base asset purchases');
230
+ const price = this.calculatePrice(
231
+ params.priceAfterDiscount,
232
+ params.coinConfig.type,
233
+ priceInfoObjectId,
234
+ );
235
+ if (!params.coin) throw new Error('coin input is required');
236
+ const payment = this.transaction.splitCoins(this.transaction.object(params.coin!), [price]);
237
+ const receipt = this.handlePayment(
238
+ params.paymentIntent,
239
+ payment,
240
+ params.coinConfig.type,
241
+ priceInfoObjectId,
242
+ params.maxAmount,
243
+ );
244
+ return receipt;
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Applies a coupon to the payment intent.
250
+ */
251
+ applyCoupon(intent: TransactionObjectArgument, couponCode: string): TransactionObjectArgument {
252
+ const config = this.haneulnsClient.config;
253
+ return this.transaction.moveCall({
254
+ target: `${config.coupons.packageId}::coupon_house::apply_coupon`,
255
+ arguments: [
256
+ this.transaction.object(config.haneulns),
257
+ intent,
258
+ this.transaction.pure.string(couponCode),
259
+ this.transaction.object.clock(),
260
+ ],
261
+ });
262
+ }
263
+
264
+ /**
265
+ * Applies a discount to the payment intent.
266
+ */
267
+ applyDiscount(intent: TransactionObjectArgument, discountInfo: DiscountInfo): void {
268
+ const config = this.haneulnsClient.config;
269
+
270
+ if (discountInfo.isFreeClaim) {
271
+ this.transaction.moveCall({
272
+ target: `${config.discountsPackage.packageId}::free_claims::free_claim`,
273
+ arguments: [
274
+ this.transaction.object(config.discountsPackage.discountHouseId),
275
+ this.transaction.object(config.haneulns),
276
+ intent,
277
+ this.transaction.object(discountInfo.discountNft),
278
+ ],
279
+ typeArguments: [discountInfo.type],
280
+ });
281
+ } else {
282
+ this.transaction.moveCall({
283
+ target: `${config.discountsPackage.packageId}::discounts::apply_percentage_discount`,
284
+ arguments: [
285
+ this.transaction.object(config.discountsPackage.discountHouseId),
286
+ intent,
287
+ this.transaction.object(config.haneulns),
288
+ this.transaction.object(discountInfo.discountNft),
289
+ ],
290
+ typeArguments: [discountInfo.type],
291
+ });
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Creates a subdomain.
297
+ */
298
+ createSubName({
299
+ parentNft,
300
+ name,
301
+ expirationTimestampMs,
302
+ allowChildCreation,
303
+ allowTimeExtension,
304
+ }: {
305
+ parentNft: TransactionObjectInput;
306
+ name: string;
307
+ expirationTimestampMs: number;
308
+ allowChildCreation: boolean;
309
+ allowTimeExtension: boolean;
310
+ }) {
311
+ if (!isValidHaneulNSName(name)) throw new Error('Invalid HaneulNS name');
312
+ const isParentSubdomain = isNestedSubName(name);
313
+ if (!this.haneulnsClient.config.haneulns) throw new Error('HaneulNS Object ID not found');
314
+ if (!this.haneulnsClient.config.subNamesPackageId)
315
+ throw new Error('Subnames package ID not found');
316
+ if (isParentSubdomain && !this.haneulnsClient.config.tempSubdomainsProxyPackageId)
317
+ throw new Error('Subnames proxy package ID not found');
318
+
319
+ const subNft = this.transaction.moveCall({
320
+ target: isParentSubdomain
321
+ ? `${this.haneulnsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::new`
322
+ : `${this.haneulnsClient.config.subNamesPackageId}::subdomains::new`,
323
+ arguments: [
324
+ this.transaction.object(this.haneulnsClient.config.haneulns),
325
+ this.transaction.object(parentNft),
326
+ this.transaction.object(HANEUL_CLOCK_OBJECT_ID),
327
+ this.transaction.pure.string(normalizeHaneulNSName(name, 'dot')),
328
+ this.transaction.pure.u64(expirationTimestampMs),
329
+ this.transaction.pure.bool(!!allowChildCreation),
330
+ this.transaction.pure.bool(!!allowTimeExtension),
331
+ ],
332
+ });
333
+
334
+ return subNft;
335
+ }
336
+
337
+ /**
338
+ * Builds the PTB to create a leaf subdomain.
339
+ * Parent can be a `HaneulnsRegistration` or a `SubDomainRegistration` object.
340
+ * Can be passed in as an ID or a TransactionArgument.
341
+ */
342
+ createLeafSubName({
343
+ parentNft,
344
+ name,
345
+ targetAddress,
346
+ }: {
347
+ parentNft: TransactionObjectInput;
348
+ name: string;
349
+ targetAddress: string;
350
+ }) {
351
+ if (!isValidHaneulNSName(name)) throw new Error('Invalid HaneulNS name');
352
+ const isParentSubdomain = isNestedSubName(name);
353
+ if (!this.haneulnsClient.config.haneulns) throw new Error('HaneulNS Object ID not found');
354
+ if (!this.haneulnsClient.config.subNamesPackageId)
355
+ throw new Error('Subnames package ID not found');
356
+ if (isParentSubdomain && !this.haneulnsClient.config.tempSubdomainsProxyPackageId)
357
+ throw new Error('Subnames proxy package ID not found');
358
+
359
+ this.transaction.moveCall({
360
+ target: isParentSubdomain
361
+ ? `${this.haneulnsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::new_leaf`
362
+ : `${this.haneulnsClient.config.subNamesPackageId}::subdomains::new_leaf`,
363
+ arguments: [
364
+ this.transaction.object(this.haneulnsClient.config.haneulns),
365
+ this.transaction.object(parentNft),
366
+ this.transaction.object(HANEUL_CLOCK_OBJECT_ID),
367
+ this.transaction.pure.string(normalizeHaneulNSName(name, 'dot')),
368
+ this.transaction.pure.address(targetAddress),
369
+ ],
370
+ });
371
+ }
372
+
373
+ /**
374
+ * Removes a leaf subname.
375
+ */
376
+ removeLeafSubName({ parentNft, name }: { parentNft: TransactionObjectInput; name: string }) {
377
+ if (!isValidHaneulNSName(name)) throw new Error('Invalid HaneulNS name');
378
+ const isParentSubdomain = isNestedSubName(name);
379
+ if (!isSubName(name)) throw new Error('This can only be invoked for subnames');
380
+ if (!this.haneulnsClient.config.haneulns) throw new Error('HaneulNS Object ID not found');
381
+ if (!this.haneulnsClient.config.subNamesPackageId)
382
+ throw new Error('Subnames package ID not found');
383
+ if (isParentSubdomain && !this.haneulnsClient.config.tempSubdomainsProxyPackageId)
384
+ throw new Error('Subnames proxy package ID not found');
385
+
386
+ this.transaction.moveCall({
387
+ target: isParentSubdomain
388
+ ? `${this.haneulnsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::remove_leaf`
389
+ : `${this.haneulnsClient.config.subNamesPackageId}::subdomains::remove_leaf`,
390
+ arguments: [
391
+ this.transaction.object(this.haneulnsClient.config.haneulns),
392
+ this.transaction.object(parentNft),
393
+ this.transaction.object(HANEUL_CLOCK_OBJECT_ID),
394
+ this.transaction.pure.string(normalizeHaneulNSName(name, 'dot')),
395
+ ],
396
+ });
397
+ }
398
+
399
+ /**
400
+ * Sets the target address of an NFT.
401
+ */
402
+ setTargetAddress({
403
+ nft, // Can be string or argument
404
+ address,
405
+ isSubname,
406
+ }: {
407
+ nft: TransactionObjectInput;
408
+ address?: string;
409
+ isSubname?: boolean;
410
+ }) {
411
+ if (isSubname && !this.haneulnsClient.config.tempSubdomainsProxyPackageId)
412
+ throw new Error('Subnames proxy package ID not found');
413
+
414
+ this.transaction.moveCall({
415
+ target: isSubname
416
+ ? `${this.haneulnsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::set_target_address`
417
+ : `${this.haneulnsClient.config.packageId}::controller::set_target_address`,
418
+ arguments: [
419
+ this.transaction.object(this.haneulnsClient.config.haneulns),
420
+ this.transaction.object(nft),
421
+ this.transaction.pure(bcs.option(bcs.Address).serialize(address).toBytes()),
422
+ this.transaction.object(HANEUL_CLOCK_OBJECT_ID),
423
+ ],
424
+ });
425
+ }
426
+
427
+ /**
428
+ * Sets a default name for the user.
429
+ */
430
+ setDefault(name: string) {
431
+ if (!isValidHaneulNSName(name)) throw new Error('Invalid HaneulNS name');
432
+ if (!this.haneulnsClient.config.haneulns) throw new Error('HaneulNS Object ID not found');
433
+
434
+ this.transaction.moveCall({
435
+ target: `${this.haneulnsClient.config.packageId}::controller::set_reverse_lookup`,
436
+ arguments: [
437
+ this.transaction.object(this.haneulnsClient.config.haneulns),
438
+ this.transaction.pure.string(normalizeHaneulNSName(name, 'dot')),
439
+ ],
440
+ });
441
+ }
442
+
443
+ /**
444
+ * Edits the setup of a subname.
445
+ */
446
+ editSetup({
447
+ parentNft,
448
+ name,
449
+ allowChildCreation,
450
+ allowTimeExtension,
451
+ }: {
452
+ parentNft: TransactionObjectInput;
453
+ name: string;
454
+ allowChildCreation: boolean;
455
+ allowTimeExtension: boolean;
456
+ }) {
457
+ if (!isValidHaneulNSName(name)) throw new Error('Invalid HaneulNS name');
458
+ const isParentSubdomain = isNestedSubName(name);
459
+ if (!this.haneulnsClient.config.haneulns) throw new Error('HaneulNS Object ID not found');
460
+ if (!isParentSubdomain && !this.haneulnsClient.config.subNamesPackageId)
461
+ throw new Error('Subnames package ID not found');
462
+ if (isParentSubdomain && !this.haneulnsClient.config.tempSubdomainsProxyPackageId)
463
+ throw new Error('Subnames proxy package ID not found');
464
+
465
+ this.transaction.moveCall({
466
+ target: isParentSubdomain
467
+ ? `${this.haneulnsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::edit_setup`
468
+ : `${this.haneulnsClient.config.subNamesPackageId}::subdomains::edit_setup`,
469
+ arguments: [
470
+ this.transaction.object(this.haneulnsClient.config.haneulns),
471
+ this.transaction.object(parentNft),
472
+ this.transaction.object(HANEUL_CLOCK_OBJECT_ID),
473
+ this.transaction.pure.string(normalizeHaneulNSName(name, 'dot')),
474
+ this.transaction.pure.bool(!!allowChildCreation),
475
+ this.transaction.pure.bool(!!allowTimeExtension),
476
+ ],
477
+ });
478
+ }
479
+
480
+ /**
481
+ * Extends the expiration of a subname.
482
+ */
483
+ extendExpiration({
484
+ nft,
485
+ expirationTimestampMs,
486
+ }: {
487
+ nft: TransactionObjectInput;
488
+ expirationTimestampMs: number;
489
+ }) {
490
+ if (!this.haneulnsClient.config.haneulns) throw new Error('HaneulNS Object ID not found');
491
+ if (!this.haneulnsClient.config.subNamesPackageId)
492
+ throw new Error('Subnames package ID not found');
493
+
494
+ this.transaction.moveCall({
495
+ target: `${this.haneulnsClient.config.subNamesPackageId}::subdomains::extend_expiration`,
496
+ arguments: [
497
+ this.transaction.object(this.haneulnsClient.config.haneulns),
498
+ this.transaction.object(nft),
499
+ this.transaction.pure.u64(expirationTimestampMs),
500
+ ],
501
+ });
502
+ }
503
+
504
+ /**
505
+ * Sets the user data of an NFT.
506
+ */
507
+ setUserData({
508
+ nft,
509
+ value,
510
+ key,
511
+ isSubname,
512
+ }: {
513
+ nft: TransactionObjectInput;
514
+ value: string;
515
+ key: string;
516
+ isSubname?: boolean;
517
+ }) {
518
+ if (!this.haneulnsClient.config.haneulns) throw new Error('HaneulNS Object ID not found');
519
+ if (isSubname && !this.haneulnsClient.config.tempSubdomainsProxyPackageId)
520
+ throw new Error('Subnames proxy package ID not found');
521
+
522
+ if (!Object.values(ALLOWED_METADATA).some((x) => x === key)) throw new Error('Invalid key');
523
+
524
+ this.transaction.moveCall({
525
+ target: isSubname
526
+ ? `${this.haneulnsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::set_user_data`
527
+ : `${this.haneulnsClient.config.packageId}::controller::set_user_data`,
528
+ arguments: [
529
+ this.transaction.object(this.haneulnsClient.config.haneulns),
530
+ this.transaction.object(nft),
531
+ this.transaction.pure.string(key),
532
+ this.transaction.pure.string(value),
533
+ this.transaction.object(HANEUL_CLOCK_OBJECT_ID),
534
+ ],
535
+ });
536
+ }
537
+
538
+ /**
539
+ * Burns an expired NFT to collect storage rebates.
540
+ */
541
+ burnExpired({ nft, isSubname }: { nft: TransactionObjectInput; isSubname?: boolean }) {
542
+ if (!this.haneulnsClient.config.haneulns) throw new Error('HaneulNS Object ID not found');
543
+
544
+ this.transaction.moveCall({
545
+ target: `${this.haneulnsClient.config.packageId}::controller::${
546
+ isSubname ? 'burn_expired_subname' : 'burn_expired'
547
+ }`, // Update this
548
+ arguments: [
549
+ this.transaction.object(this.haneulnsClient.config.haneulns),
550
+ this.transaction.object(nft),
551
+ this.transaction.object(HANEUL_CLOCK_OBJECT_ID),
552
+ ],
553
+ });
554
+ }
555
+ }
package/src/helpers.ts ADDED
@@ -0,0 +1,52 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { Transaction } from '@haneullabs/haneul/transactions';
5
+ import { normalizeHaneulNSName } from '@haneullabs/haneul/utils';
6
+
7
+ export function isSubName(name: string): boolean {
8
+ return normalizeHaneulNSName(name, 'dot').split('.').length > 2;
9
+ }
10
+
11
+ /**
12
+ * Checks if a name is a nested subname.
13
+ * A nested subdomain is a subdomain that is a subdomain of another subdomain.
14
+ * @param name The name to check (e.g test.example.sub.haneul)
15
+ */
16
+ export function isNestedSubName(name: string): boolean {
17
+ return normalizeHaneulNSName(name, 'dot').split('.').length > 3;
18
+ }
19
+
20
+ /**
21
+ * The years must be between 1 and 5.
22
+ */
23
+ export function validateYears(years: number) {
24
+ if (!(years > 0 && years < 6)) throw new Error('Years must be between 1 and 5');
25
+ }
26
+
27
+ export function zeroCoin(tx: Transaction, type: string) {
28
+ return tx.moveCall({
29
+ target: '0x2::coin::zero',
30
+ typeArguments: [type],
31
+ });
32
+ }
33
+
34
+ export function getConfigType(haneulnsPackageV1: string, innerType: string): string {
35
+ return `${haneulnsPackageV1}::haneulns::ConfigKey<${innerType}>`;
36
+ }
37
+
38
+ export function getDomainType(haneulnsPackageV1: string): string {
39
+ return `${haneulnsPackageV1}::domain::Domain`;
40
+ }
41
+
42
+ export function getPricelistConfigType(haneulnsPackageId: string): string {
43
+ return `${haneulnsPackageId}::pricing_config::PricingConfig`;
44
+ }
45
+
46
+ export function getRenewalPricelistConfigType(haneulnsPackageId: string): string {
47
+ return `${haneulnsPackageId}::pricing_config::RenewalConfig`;
48
+ }
49
+
50
+ export function getCoinDiscountConfigType(paymentPackageId: string): string {
51
+ return `${paymentPackageId}::payments::PaymentsConfig`;
52
+ }
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export { HaneulnsClient } from './haneulns-client.js';
4
+ export { HaneulnsTransaction } from './haneulns-transaction.js';
5
+ export type { Network, HaneulnsClientConfig, Config } from './types.js';
6
+ export { ALLOWED_METADATA, mainPackage } from './constants.js';
7
+ export {
8
+ isSubName,
9
+ isNestedSubName,
10
+ validateYears,
11
+ getConfigType,
12
+ getDomainType,
13
+ getPricelistConfigType,
14
+ getRenewalPricelistConfigType,
15
+ getCoinDiscountConfigType,
16
+ } from './helpers.js';
@@ -0,0 +1,48 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import axios from 'axios';
4
+ import type { AxiosInstance } from 'axios';
5
+ import axiosRetry from 'axios-retry';
6
+
7
+ export type HexString = string;
8
+ export type PriceFeedRequestConfig = {
9
+ verbose?: boolean;
10
+ binary?: boolean;
11
+ };
12
+ export type PriceServiceConnectionConfig = {
13
+ timeout?: number;
14
+ httpRetries?: number;
15
+ };
16
+ export class PriceServiceConnection {
17
+ private httpClient: AxiosInstance;
18
+ /**
19
+ * Constructs a new Connection.
20
+ *
21
+ * @param endpoint endpoint URL to the price service.
22
+ * @param config Optional configuration for custom setups.
23
+ */
24
+ constructor(endpoint: string, config?: PriceServiceConnectionConfig) {
25
+ this.httpClient = axios.create({
26
+ baseURL: endpoint,
27
+ timeout: config?.timeout || 5000,
28
+ });
29
+ axiosRetry(this.httpClient, {
30
+ retries: config?.httpRetries || 3,
31
+ retryDelay: axiosRetry.exponentialDelay,
32
+ });
33
+ }
34
+ /**
35
+ * Fetch latest VAAs of given price IDs.
36
+ *
37
+ * @param priceIds Array of hex-encoded price IDs.
38
+ * @returns Array of base64 encoded VAAs.
39
+ */
40
+ async getLatestVaas(priceIds: HexString[]): Promise<string[]> {
41
+ const response = await this.httpClient.get('/api/latest_vaas', {
42
+ params: {
43
+ ids: priceIds,
44
+ },
45
+ });
46
+ return response.data;
47
+ }
48
+ }
@@ -0,0 +1,23 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * Extracts the VAA bytes embedded in an accumulator message.
6
+ *
7
+ * @param accumulatorMessage The accumulator price update message as a Uint8Array.
8
+ * @returns VAA bytes as a Uint8Array.
9
+ */
10
+ export function extractVaaBytesFromAccumulatorMessage(accumulatorMessage: Uint8Array): Uint8Array {
11
+ const dataView = new DataView(
12
+ accumulatorMessage.buffer,
13
+ accumulatorMessage.byteOffset,
14
+ accumulatorMessage.byteLength,
15
+ );
16
+
17
+ const trailingPayloadSize = dataView.getUint8(6);
18
+ const vaaSizeOffset = 7 + trailingPayloadSize + 1; // Header (7 bytes), trailing payload size, proof type
19
+ const vaaSize = dataView.getUint16(vaaSizeOffset, false); // Read 2 bytes for VAA size (big-endian)
20
+ const vaaOffset = vaaSizeOffset + 2; // VAA size is 2 bytes
21
+
22
+ return accumulatorMessage.subarray(vaaOffset, vaaOffset + vaaSize);
23
+ }