@buildonspark/issuer-sdk 0.0.39 → 0.0.40

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/index.cjs CHANGED
@@ -35,7 +35,7 @@ if (typeof global === "undefined") {
35
35
 
36
36
  // src/issuer-spark-wallet.ts
37
37
  var import_lrc20_sdk2 = require("@buildonspark/lrc20-sdk");
38
- var import_spark_sdk = require("@buildonspark/spark-sdk");
38
+ var import_spark_sdk3 = require("@buildonspark/spark-sdk");
39
39
  var import_address = require("@buildonspark/spark-sdk/address");
40
40
  var import_utils5 = require("@noble/curves/abstract/utils");
41
41
 
@@ -44,9 +44,14 @@ var import_utils2 = require("@buildonspark/spark-sdk/utils");
44
44
 
45
45
  // src/utils/token-hashing.ts
46
46
  var import_utils = require("@scure/btc-signer/utils");
47
+ var import_spark_sdk = require("@buildonspark/spark-sdk");
47
48
  function hashFreezeTokensPayload(payload) {
48
49
  if (!payload) {
49
- throw new Error("freeze tokens payload cannot be nil");
50
+ throw new import_spark_sdk.ValidationError("Freeze tokens payload cannot be nil", {
51
+ field: "payload",
52
+ value: payload,
53
+ expected: "valid freeze tokens payload"
54
+ });
50
55
  }
51
56
  let allHashes = [];
52
57
  const ownerPubKeyHash = import_utils.sha256.create();
@@ -87,6 +92,7 @@ function hashFreezeTokensPayload(payload) {
87
92
  }
88
93
 
89
94
  // src/services/freeze.ts
95
+ var import_spark_sdk2 = require("@buildonspark/spark-sdk");
90
96
  var TokenFreezeService = class {
91
97
  config;
92
98
  connectionManager;
@@ -115,14 +121,26 @@ var TokenFreezeService = class {
115
121
  };
116
122
  const hashedPayload = hashFreezeTokensPayload(freezeTokensPayload);
117
123
  const issuerSignature = await this.config.signer.signMessageWithIdentityKey(hashedPayload);
118
- const response = await internalSparkClient.freeze_tokens({
119
- freezeTokensPayload,
120
- issuerSignature
121
- });
122
- return {
123
- identifier,
124
- response
125
- };
124
+ try {
125
+ const response = await internalSparkClient.freeze_tokens({
126
+ freezeTokensPayload,
127
+ issuerSignature
128
+ });
129
+ return {
130
+ identifier,
131
+ response
132
+ };
133
+ } catch (error) {
134
+ throw new import_spark_sdk2.NetworkError(
135
+ "Failed to send a freeze/unfreeze operation",
136
+ {
137
+ operation: "freeze_tokens",
138
+ errorCount: 1,
139
+ errors: error instanceof Error ? error.message : String(error)
140
+ },
141
+ error instanceof Error ? error : void 0
142
+ );
143
+ }
126
144
  })
127
145
  );
128
146
  const successfulResponses = (0, import_utils2.collectResponses)(freezeResponses);
@@ -321,8 +339,9 @@ function convertTokenActivityToHexEncoded(rawTransactions) {
321
339
  }
322
340
 
323
341
  // src/issuer-spark-wallet.ts
342
+ var import_spark_sdk4 = require("@buildonspark/spark-sdk");
324
343
  var BURN_ADDRESS = "02".repeat(33);
325
- var IssuerSparkWallet = class _IssuerSparkWallet extends import_spark_sdk.SparkWallet {
344
+ var IssuerSparkWallet = class _IssuerSparkWallet extends import_spark_sdk3.SparkWallet {
326
345
  issuerTokenTransactionService;
327
346
  tokenFreezeService;
328
347
  static async initialize(options) {
@@ -358,18 +377,26 @@ var IssuerSparkWallet = class _IssuerSparkWallet extends import_spark_sdk.SparkW
358
377
  }
359
378
  async getIssuerTokenInfo() {
360
379
  const lrc20Client = await this.lrc20ConnectionManager.createLrc20Client();
361
- const tokenInfo = await lrc20Client.getTokenPubkeyInfo({
362
- publicKeys: [(0, import_utils5.hexToBytes)(await super.getIdentityPublicKey())]
363
- });
364
- const info = tokenInfo.tokenPubkeyInfos[0];
365
- return {
366
- tokenPublicKey: (0, import_utils5.bytesToHex)(info.announcement.publicKey.publicKey),
367
- tokenName: info.announcement.name,
368
- tokenSymbol: info.announcement.symbol,
369
- tokenDecimals: Number((0, import_utils5.bytesToNumberBE)(info.announcement.decimal)),
370
- isFreezable: info.announcement.isFreezable,
371
- maxSupply: (0, import_utils5.bytesToNumberBE)(info.announcement.maxSupply)
372
- };
380
+ try {
381
+ const tokenInfo = await lrc20Client.getTokenPubkeyInfo({
382
+ publicKeys: [(0, import_utils5.hexToBytes)(await super.getIdentityPublicKey())]
383
+ });
384
+ const info = tokenInfo.tokenPubkeyInfos[0];
385
+ return {
386
+ tokenPublicKey: (0, import_utils5.bytesToHex)(info.announcement.publicKey.publicKey),
387
+ tokenName: info.announcement.name,
388
+ tokenSymbol: info.announcement.symbol,
389
+ tokenDecimals: Number((0, import_utils5.bytesToNumberBE)(info.announcement.decimal)),
390
+ isFreezable: info.announcement.isFreezable,
391
+ maxSupply: (0, import_utils5.bytesToNumberBE)(info.announcement.maxSupply)
392
+ };
393
+ } catch (error) {
394
+ throw new import_spark_sdk3.NetworkError("Failed to get token info", {
395
+ operation: "getIssuerTokenInfo",
396
+ errorCount: 1,
397
+ errors: error instanceof Error ? error.message : String(error)
398
+ });
399
+ }
373
400
  }
374
401
  async mintTokens(tokenAmount) {
375
402
  var tokenPublicKey = await super.getIdentityPublicKey();
@@ -429,27 +456,28 @@ var IssuerSparkWallet = class _IssuerSparkWallet extends import_spark_sdk.SparkW
429
456
  }
430
457
  async getIssuerTokenActivity(pageSize = 100, cursor, operationTypes, beforeTimestamp, afterTimestamp) {
431
458
  const lrc20Client = await this.lrc20ConnectionManager.createLrc20Client();
432
- const transactions = await lrc20Client.listTransactions({
433
- tokenPublicKey: (0, import_utils5.hexToBytes)(await super.getIdentityPublicKey()),
434
- cursor,
435
- pageSize,
436
- beforeTimestamp,
437
- afterTimestamp,
438
- operationTypes
439
- });
440
- return convertTokenActivityToHexEncoded(transactions);
459
+ try {
460
+ const transactions = await lrc20Client.listTransactions({
461
+ tokenPublicKey: (0, import_utils5.hexToBytes)(await super.getIdentityPublicKey()),
462
+ cursor,
463
+ pageSize,
464
+ beforeTimestamp,
465
+ afterTimestamp,
466
+ operationTypes
467
+ });
468
+ return convertTokenActivityToHexEncoded(transactions);
469
+ } catch (error) {
470
+ throw new import_spark_sdk3.NetworkError("Failed to get token activity", {
471
+ operation: "listTransactions",
472
+ errorCount: 1,
473
+ errors: error instanceof Error ? error.message : String(error)
474
+ });
475
+ }
441
476
  }
442
477
  async getIssuerTokenDistribution() {
443
- throw new Error("Not implemented");
478
+ throw new import_spark_sdk4.NotImplementedError("Token distribution is not yet supported");
444
479
  }
445
- async announceTokenL1({
446
- tokenName,
447
- tokenTicker,
448
- decimals,
449
- maxSupply,
450
- isFreezable,
451
- feeRateSatsPerVb = 2
452
- }) {
480
+ async announceTokenL1(tokenName, tokenTicker, decimals, maxSupply, isFreezable, feeRateSatsPerVb = 2) {
453
481
  await this.lrc20Wallet.syncWallet();
454
482
  const tokenPublicKey = new import_lrc20_sdk2.TokenPubkey(this.lrc20Wallet.pubkey);
455
483
  const announcement = new import_lrc20_sdk2.TokenPubkeyAnnouncement(
@@ -460,13 +488,24 @@ var IssuerSparkWallet = class _IssuerSparkWallet extends import_spark_sdk.SparkW
460
488
  maxSupply,
461
489
  isFreezable
462
490
  );
463
- const tx = await this.lrc20Wallet.prepareAnnouncement(
464
- announcement,
465
- feeRateSatsPerVb
466
- );
467
- return await this.lrc20Wallet.broadcastRawBtcTransaction(
468
- tx.bitcoin_tx.toHex()
469
- );
491
+ try {
492
+ const tx = await this.lrc20Wallet.prepareAnnouncement(
493
+ announcement,
494
+ feeRateSatsPerVb
495
+ );
496
+ return await this.lrc20Wallet.broadcastRawBtcTransaction(
497
+ tx.bitcoin_tx.toHex()
498
+ );
499
+ } catch (error) {
500
+ throw new import_spark_sdk3.NetworkError(
501
+ "Failed to broadcast announcement transaction on L1",
502
+ {
503
+ operation: "broadcastRawBtcTransaction",
504
+ errorCount: 1,
505
+ errors: error instanceof Error ? error.message : String(error)
506
+ }
507
+ );
508
+ }
470
509
  }
471
510
  };
472
511
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.d.cts CHANGED
@@ -36,14 +36,7 @@ declare class IssuerSparkWallet extends SparkWallet {
36
36
  }>;
37
37
  getIssuerTokenActivity(pageSize?: number, cursor?: ListAllTokenTransactionsCursor, operationTypes?: OperationType[], beforeTimestamp?: Date, afterTimestamp?: Date): Promise<GetTokenActivityResponse>;
38
38
  getIssuerTokenDistribution(): Promise<TokenDistribution>;
39
- announceTokenL1({ tokenName, tokenTicker, decimals, maxSupply, isFreezable, feeRateSatsPerVb, }: {
40
- tokenName: any;
41
- tokenTicker: any;
42
- decimals: any;
43
- maxSupply: any;
44
- isFreezable: any;
45
- feeRateSatsPerVb?: number | undefined;
46
- }): Promise<string>;
39
+ announceTokenL1(tokenName: string, tokenTicker: string, decimals: number, maxSupply: bigint, isFreezable: boolean, feeRateSatsPerVb?: number): Promise<string>;
47
40
  }
48
41
 
49
42
  export { IssuerSparkWallet, type IssuerTokenInfo };
package/dist/index.d.ts CHANGED
@@ -36,14 +36,7 @@ declare class IssuerSparkWallet extends SparkWallet {
36
36
  }>;
37
37
  getIssuerTokenActivity(pageSize?: number, cursor?: ListAllTokenTransactionsCursor, operationTypes?: OperationType[], beforeTimestamp?: Date, afterTimestamp?: Date): Promise<GetTokenActivityResponse>;
38
38
  getIssuerTokenDistribution(): Promise<TokenDistribution>;
39
- announceTokenL1({ tokenName, tokenTicker, decimals, maxSupply, isFreezable, feeRateSatsPerVb, }: {
40
- tokenName: any;
41
- tokenTicker: any;
42
- decimals: any;
43
- maxSupply: any;
44
- isFreezable: any;
45
- feeRateSatsPerVb?: number | undefined;
46
- }): Promise<string>;
39
+ announceTokenL1(tokenName: string, tokenTicker: string, decimals: number, maxSupply: bigint, isFreezable: boolean, feeRateSatsPerVb?: number): Promise<string>;
47
40
  }
48
41
 
49
42
  export { IssuerSparkWallet, type IssuerTokenInfo };
package/dist/index.js CHANGED
@@ -2,7 +2,10 @@ import "./chunk-GB7N6I5O.js";
2
2
 
3
3
  // src/issuer-spark-wallet.ts
4
4
  import { TokenPubkey, TokenPubkeyAnnouncement } from "@buildonspark/lrc20-sdk";
5
- import { SparkWallet } from "@buildonspark/spark-sdk";
5
+ import {
6
+ NetworkError as NetworkError2,
7
+ SparkWallet
8
+ } from "@buildonspark/spark-sdk";
6
9
  import {
7
10
  decodeSparkAddress,
8
11
  encodeSparkAddress
@@ -18,9 +21,14 @@ import { collectResponses } from "@buildonspark/spark-sdk/utils";
18
21
 
19
22
  // src/utils/token-hashing.ts
20
23
  import { sha256 } from "@scure/btc-signer/utils";
24
+ import { ValidationError } from "@buildonspark/spark-sdk";
21
25
  function hashFreezeTokensPayload(payload) {
22
26
  if (!payload) {
23
- throw new Error("freeze tokens payload cannot be nil");
27
+ throw new ValidationError("Freeze tokens payload cannot be nil", {
28
+ field: "payload",
29
+ value: payload,
30
+ expected: "valid freeze tokens payload"
31
+ });
24
32
  }
25
33
  let allHashes = [];
26
34
  const ownerPubKeyHash = sha256.create();
@@ -61,6 +69,7 @@ function hashFreezeTokensPayload(payload) {
61
69
  }
62
70
 
63
71
  // src/services/freeze.ts
72
+ import { NetworkError } from "@buildonspark/spark-sdk";
64
73
  var TokenFreezeService = class {
65
74
  config;
66
75
  connectionManager;
@@ -89,14 +98,26 @@ var TokenFreezeService = class {
89
98
  };
90
99
  const hashedPayload = hashFreezeTokensPayload(freezeTokensPayload);
91
100
  const issuerSignature = await this.config.signer.signMessageWithIdentityKey(hashedPayload);
92
- const response = await internalSparkClient.freeze_tokens({
93
- freezeTokensPayload,
94
- issuerSignature
95
- });
96
- return {
97
- identifier,
98
- response
99
- };
101
+ try {
102
+ const response = await internalSparkClient.freeze_tokens({
103
+ freezeTokensPayload,
104
+ issuerSignature
105
+ });
106
+ return {
107
+ identifier,
108
+ response
109
+ };
110
+ } catch (error) {
111
+ throw new NetworkError(
112
+ "Failed to send a freeze/unfreeze operation",
113
+ {
114
+ operation: "freeze_tokens",
115
+ errorCount: 1,
116
+ errors: error instanceof Error ? error.message : String(error)
117
+ },
118
+ error instanceof Error ? error : void 0
119
+ );
120
+ }
100
121
  })
101
122
  );
102
123
  const successfulResponses = collectResponses(freezeResponses);
@@ -295,6 +316,7 @@ function convertTokenActivityToHexEncoded(rawTransactions) {
295
316
  }
296
317
 
297
318
  // src/issuer-spark-wallet.ts
319
+ import { NotImplementedError } from "@buildonspark/spark-sdk";
298
320
  var BURN_ADDRESS = "02".repeat(33);
299
321
  var IssuerSparkWallet = class _IssuerSparkWallet extends SparkWallet {
300
322
  issuerTokenTransactionService;
@@ -332,18 +354,26 @@ var IssuerSparkWallet = class _IssuerSparkWallet extends SparkWallet {
332
354
  }
333
355
  async getIssuerTokenInfo() {
334
356
  const lrc20Client = await this.lrc20ConnectionManager.createLrc20Client();
335
- const tokenInfo = await lrc20Client.getTokenPubkeyInfo({
336
- publicKeys: [hexToBytes(await super.getIdentityPublicKey())]
337
- });
338
- const info = tokenInfo.tokenPubkeyInfos[0];
339
- return {
340
- tokenPublicKey: bytesToHex2(info.announcement.publicKey.publicKey),
341
- tokenName: info.announcement.name,
342
- tokenSymbol: info.announcement.symbol,
343
- tokenDecimals: Number(bytesToNumberBE2(info.announcement.decimal)),
344
- isFreezable: info.announcement.isFreezable,
345
- maxSupply: bytesToNumberBE2(info.announcement.maxSupply)
346
- };
357
+ try {
358
+ const tokenInfo = await lrc20Client.getTokenPubkeyInfo({
359
+ publicKeys: [hexToBytes(await super.getIdentityPublicKey())]
360
+ });
361
+ const info = tokenInfo.tokenPubkeyInfos[0];
362
+ return {
363
+ tokenPublicKey: bytesToHex2(info.announcement.publicKey.publicKey),
364
+ tokenName: info.announcement.name,
365
+ tokenSymbol: info.announcement.symbol,
366
+ tokenDecimals: Number(bytesToNumberBE2(info.announcement.decimal)),
367
+ isFreezable: info.announcement.isFreezable,
368
+ maxSupply: bytesToNumberBE2(info.announcement.maxSupply)
369
+ };
370
+ } catch (error) {
371
+ throw new NetworkError2("Failed to get token info", {
372
+ operation: "getIssuerTokenInfo",
373
+ errorCount: 1,
374
+ errors: error instanceof Error ? error.message : String(error)
375
+ });
376
+ }
347
377
  }
348
378
  async mintTokens(tokenAmount) {
349
379
  var tokenPublicKey = await super.getIdentityPublicKey();
@@ -403,27 +433,28 @@ var IssuerSparkWallet = class _IssuerSparkWallet extends SparkWallet {
403
433
  }
404
434
  async getIssuerTokenActivity(pageSize = 100, cursor, operationTypes, beforeTimestamp, afterTimestamp) {
405
435
  const lrc20Client = await this.lrc20ConnectionManager.createLrc20Client();
406
- const transactions = await lrc20Client.listTransactions({
407
- tokenPublicKey: hexToBytes(await super.getIdentityPublicKey()),
408
- cursor,
409
- pageSize,
410
- beforeTimestamp,
411
- afterTimestamp,
412
- operationTypes
413
- });
414
- return convertTokenActivityToHexEncoded(transactions);
436
+ try {
437
+ const transactions = await lrc20Client.listTransactions({
438
+ tokenPublicKey: hexToBytes(await super.getIdentityPublicKey()),
439
+ cursor,
440
+ pageSize,
441
+ beforeTimestamp,
442
+ afterTimestamp,
443
+ operationTypes
444
+ });
445
+ return convertTokenActivityToHexEncoded(transactions);
446
+ } catch (error) {
447
+ throw new NetworkError2("Failed to get token activity", {
448
+ operation: "listTransactions",
449
+ errorCount: 1,
450
+ errors: error instanceof Error ? error.message : String(error)
451
+ });
452
+ }
415
453
  }
416
454
  async getIssuerTokenDistribution() {
417
- throw new Error("Not implemented");
455
+ throw new NotImplementedError("Token distribution is not yet supported");
418
456
  }
419
- async announceTokenL1({
420
- tokenName,
421
- tokenTicker,
422
- decimals,
423
- maxSupply,
424
- isFreezable,
425
- feeRateSatsPerVb = 2
426
- }) {
457
+ async announceTokenL1(tokenName, tokenTicker, decimals, maxSupply, isFreezable, feeRateSatsPerVb = 2) {
427
458
  await this.lrc20Wallet.syncWallet();
428
459
  const tokenPublicKey = new TokenPubkey(this.lrc20Wallet.pubkey);
429
460
  const announcement = new TokenPubkeyAnnouncement(
@@ -434,13 +465,24 @@ var IssuerSparkWallet = class _IssuerSparkWallet extends SparkWallet {
434
465
  maxSupply,
435
466
  isFreezable
436
467
  );
437
- const tx = await this.lrc20Wallet.prepareAnnouncement(
438
- announcement,
439
- feeRateSatsPerVb
440
- );
441
- return await this.lrc20Wallet.broadcastRawBtcTransaction(
442
- tx.bitcoin_tx.toHex()
443
- );
468
+ try {
469
+ const tx = await this.lrc20Wallet.prepareAnnouncement(
470
+ announcement,
471
+ feeRateSatsPerVb
472
+ );
473
+ return await this.lrc20Wallet.broadcastRawBtcTransaction(
474
+ tx.bitcoin_tx.toHex()
475
+ );
476
+ } catch (error) {
477
+ throw new NetworkError2(
478
+ "Failed to broadcast announcement transaction on L1",
479
+ {
480
+ operation: "broadcastRawBtcTransaction",
481
+ errorCount: 1,
482
+ errors: error instanceof Error ? error.message : String(error)
483
+ }
484
+ );
485
+ }
444
486
  }
445
487
  };
446
488
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buildonspark/issuer-sdk",
3
- "version": "0.0.39",
3
+ "version": "0.0.40",
4
4
  "description": "Spark Issuer SDK for token issuance",
5
5
  "license": "Apache-2.0",
6
6
  "module": "./dist/index.js",
@@ -54,8 +54,8 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@bufbuild/protobuf": "^2.2.5",
57
- "@buildonspark/lrc20-sdk": "0.0.36",
58
- "@buildonspark/spark-sdk": "0.1.8",
57
+ "@buildonspark/lrc20-sdk": "0.0.37",
58
+ "@buildonspark/spark-sdk": "0.1.9",
59
59
  "@noble/curves": "^1.8.0",
60
60
  "@scure/btc-signer": "^1.5.0",
61
61
  "bitcoinjs-lib": "^6.1.5",
@@ -3,7 +3,11 @@ import {
3
3
  ListAllTokenTransactionsCursor,
4
4
  OperationType,
5
5
  } from "@buildonspark/lrc20-sdk/proto/rpc/v1/types";
6
- import { SparkWallet, SparkWalletProps } from "@buildonspark/spark-sdk";
6
+ import {
7
+ NetworkError,
8
+ SparkWallet,
9
+ SparkWalletProps,
10
+ } from "@buildonspark/spark-sdk";
7
11
  import {
8
12
  decodeSparkAddress,
9
13
  encodeSparkAddress,
@@ -19,6 +23,7 @@ import { TokenFreezeService } from "./services/freeze.js";
19
23
  import { IssuerTokenTransactionService } from "./services/token-transactions.js";
20
24
  import { GetTokenActivityResponse, TokenDistribution } from "./types.js";
21
25
  import { convertTokenActivityToHexEncoded } from "./utils/type-mappers.js";
26
+ import { NotImplementedError } from "@buildonspark/spark-sdk";
22
27
 
23
28
  const BURN_ADDRESS = "02".repeat(33);
24
29
 
@@ -76,19 +81,27 @@ export class IssuerSparkWallet extends SparkWallet {
76
81
  public async getIssuerTokenInfo(): Promise<IssuerTokenInfo | null> {
77
82
  const lrc20Client = await this.lrc20ConnectionManager.createLrc20Client();
78
83
 
79
- const tokenInfo = await lrc20Client.getTokenPubkeyInfo({
80
- publicKeys: [hexToBytes(await super.getIdentityPublicKey())],
81
- });
84
+ try {
85
+ const tokenInfo = await lrc20Client.getTokenPubkeyInfo({
86
+ publicKeys: [hexToBytes(await super.getIdentityPublicKey())],
87
+ });
82
88
 
83
- const info = tokenInfo.tokenPubkeyInfos[0];
84
- return {
85
- tokenPublicKey: bytesToHex(info.announcement!.publicKey!.publicKey),
86
- tokenName: info.announcement!.name,
87
- tokenSymbol: info.announcement!.symbol,
88
- tokenDecimals: Number(bytesToNumberBE(info.announcement!.decimal)),
89
- isFreezable: info.announcement!.isFreezable,
90
- maxSupply: bytesToNumberBE(info.announcement!.maxSupply),
91
- };
89
+ const info = tokenInfo.tokenPubkeyInfos[0];
90
+ return {
91
+ tokenPublicKey: bytesToHex(info.announcement!.publicKey!.publicKey),
92
+ tokenName: info.announcement!.name,
93
+ tokenSymbol: info.announcement!.symbol,
94
+ tokenDecimals: Number(bytesToNumberBE(info.announcement!.decimal)),
95
+ isFreezable: info.announcement!.isFreezable,
96
+ maxSupply: bytesToNumberBE(info.announcement!.maxSupply),
97
+ };
98
+ } catch (error) {
99
+ throw new NetworkError("Failed to get token info", {
100
+ operation: "getIssuerTokenInfo",
101
+ errorCount: 1,
102
+ errors: error instanceof Error ? error.message : String(error),
103
+ });
104
+ }
92
105
  }
93
106
 
94
107
  public async mintTokens(tokenAmount: bigint): Promise<string> {
@@ -174,30 +187,38 @@ export class IssuerSparkWallet extends SparkWallet {
174
187
  ): Promise<GetTokenActivityResponse> {
175
188
  const lrc20Client = await this.lrc20ConnectionManager.createLrc20Client();
176
189
 
177
- const transactions = await lrc20Client.listTransactions({
178
- tokenPublicKey: hexToBytes(await super.getIdentityPublicKey()),
179
- cursor,
180
- pageSize,
181
- beforeTimestamp,
182
- afterTimestamp,
183
- operationTypes,
184
- });
185
-
186
- return convertTokenActivityToHexEncoded(transactions);
190
+ try {
191
+ const transactions = await lrc20Client.listTransactions({
192
+ tokenPublicKey: hexToBytes(await super.getIdentityPublicKey()),
193
+ cursor,
194
+ pageSize,
195
+ beforeTimestamp,
196
+ afterTimestamp,
197
+ operationTypes,
198
+ });
199
+
200
+ return convertTokenActivityToHexEncoded(transactions);
201
+ } catch (error) {
202
+ throw new NetworkError("Failed to get token activity", {
203
+ operation: "listTransactions",
204
+ errorCount: 1,
205
+ errors: error instanceof Error ? error.message : String(error),
206
+ });
207
+ }
187
208
  }
188
209
 
189
210
  public async getIssuerTokenDistribution(): Promise<TokenDistribution> {
190
- throw new Error("Not implemented");
211
+ throw new NotImplementedError("Token distribution is not yet supported");
191
212
  }
192
213
 
193
- public async announceTokenL1({
194
- tokenName,
195
- tokenTicker,
196
- decimals,
197
- maxSupply,
198
- isFreezable,
199
- feeRateSatsPerVb = 2.0,
200
- }): Promise<string> {
214
+ public async announceTokenL1(
215
+ tokenName: string,
216
+ tokenTicker: string,
217
+ decimals: number,
218
+ maxSupply: bigint,
219
+ isFreezable: boolean,
220
+ feeRateSatsPerVb: number = 2.0,
221
+ ): Promise<string> {
201
222
  await this.lrc20Wallet!.syncWallet();
202
223
 
203
224
  const tokenPublicKey = new TokenPubkey(this.lrc20Wallet!.pubkey);
@@ -211,13 +232,24 @@ export class IssuerSparkWallet extends SparkWallet {
211
232
  isFreezable,
212
233
  );
213
234
 
214
- const tx = await this.lrc20Wallet!.prepareAnnouncement(
215
- announcement,
216
- feeRateSatsPerVb,
217
- );
235
+ try {
236
+ const tx = await this.lrc20Wallet!.prepareAnnouncement(
237
+ announcement,
238
+ feeRateSatsPerVb,
239
+ );
218
240
 
219
- return await this.lrc20Wallet!.broadcastRawBtcTransaction(
220
- tx.bitcoin_tx.toHex(),
221
- );
241
+ return await this.lrc20Wallet!.broadcastRawBtcTransaction(
242
+ tx.bitcoin_tx.toHex(),
243
+ );
244
+ } catch (error) {
245
+ throw new NetworkError(
246
+ "Failed to broadcast announcement transaction on L1",
247
+ {
248
+ operation: "broadcastRawBtcTransaction",
249
+ errorCount: 1,
250
+ errors: error instanceof Error ? error.message : String(error),
251
+ },
252
+ );
253
+ }
222
254
  }
223
255
  }
@@ -6,6 +6,7 @@ import {
6
6
  } from "@buildonspark/spark-sdk/proto/spark";
7
7
  import { collectResponses } from "@buildonspark/spark-sdk/utils";
8
8
  import { hashFreezeTokensPayload } from "../utils/token-hashing.js";
9
+ import { NetworkError } from "@buildonspark/spark-sdk";
9
10
 
10
11
  export class TokenFreezeService {
11
12
  private readonly config: WalletConfigService;
@@ -61,14 +62,27 @@ export class TokenFreezeService {
61
62
  const issuerSignature =
62
63
  await this.config.signer.signMessageWithIdentityKey(hashedPayload);
63
64
 
64
- const response = await internalSparkClient.freeze_tokens({
65
- freezeTokensPayload,
66
- issuerSignature,
67
- });
68
- return {
69
- identifier,
70
- response,
71
- };
65
+ try {
66
+ const response = await internalSparkClient.freeze_tokens({
67
+ freezeTokensPayload,
68
+ issuerSignature,
69
+ });
70
+
71
+ return {
72
+ identifier,
73
+ response,
74
+ };
75
+ } catch (error) {
76
+ throw new NetworkError(
77
+ "Failed to send a freeze/unfreeze operation",
78
+ {
79
+ operation: "freeze_tokens",
80
+ errorCount: 1,
81
+ errors: error instanceof Error ? error.message : String(error),
82
+ },
83
+ error instanceof Error ? error : undefined,
84
+ );
85
+ }
72
86
  }),
73
87
  );
74
88
 
@@ -8,96 +8,79 @@ import {
8
8
  import { BitcoinFaucet } from "../../../../spark-sdk/src/tests/utils/test-faucet.js";
9
9
  import { IssuerSparkWalletTesting } from "../utils/issuer-test-wallet.js";
10
10
  import { SparkWalletTesting } from "../utils/spark-testing-wallet.js";
11
+ import { SparkWallet } from "@buildonspark/spark-sdk";
12
+ import { IssuerSparkWallet } from "../../index.js";
11
13
 
12
14
  const brokenTestFn = process.env.GITHUB_ACTIONS ? it.skip : it;
13
- describe("token integration test", () => {
15
+ describe("token integration tests", () => {
14
16
  jest.setTimeout(80000);
15
17
 
16
- brokenTestFn("should issue a single token with ECDSA", async () => {
17
- const tokenAmount: bigint = 1000n;
18
- const { wallet } = await IssuerSparkWalletTesting.initialize({
19
- options: LOCAL_WALLET_CONFIG_ECDSA,
20
- });
18
+ brokenTestFn(
19
+ "should fail when minting tokens without announcement",
20
+ async () => {
21
+ const tokenAmount: bigint = 1000n;
22
+ const { wallet } = await IssuerSparkWalletTesting.initialize({
23
+ options: LOCAL_WALLET_CONFIG_ECDSA,
24
+ });
21
25
 
22
- await wallet.mintTokens(tokenAmount);
26
+ await wallet.mintTokens(tokenAmount);
23
27
 
24
- const tokenBalance = (await wallet.getIssuerTokenBalance()).balance;
25
- expect(tokenBalance).toEqual(tokenAmount);
26
- });
28
+ const tokenBalance = (await wallet.getIssuerTokenBalance()).balance;
29
+ expect(tokenBalance).toEqual(tokenAmount);
30
+ },
31
+ );
27
32
 
28
- brokenTestFn("should issue a single token with Schnorr", async () => {
33
+ brokenTestFn("should fail when minting more than max supply", async () => {
29
34
  const tokenAmount: bigint = 1000n;
30
35
  const { wallet } = await IssuerSparkWalletTesting.initialize({
31
36
  options: LOCAL_WALLET_CONFIG_SCHNORR,
32
37
  });
33
38
 
34
- await wallet.mintTokens(tokenAmount);
35
-
36
- const tokenBalance = (await wallet.getIssuerTokenBalance()).balance;
37
- expect(tokenBalance).toEqual(tokenAmount);
39
+ await fundAndAnnounce(wallet, 2n, "MaxSupplyToken", "MST");
40
+ await expect(wallet.mintTokens(tokenAmount)).rejects.toThrow();
38
41
  });
39
42
 
40
- brokenTestFn("should announce and issue a single token", async () => {
41
- const tokenAmount: bigint = 1000n;
42
- const { wallet } = await IssuerSparkWalletTesting.initialize({
43
- options: LOCAL_WALLET_CONFIG_SCHNORR,
44
- });
45
-
46
- // Faucet funds to the Issuer wallet because announcing a token
47
- // requires ownership of an L1 UTXO.
48
- const faucet = BitcoinFaucet.getInstance();
49
- const l1WalletPubKey = await wallet.getTokenL1Address();
50
- await faucet.sendToAddress(l1WalletPubKey, 100_000n);
51
- await faucet.mineBlocks(6);
52
-
53
- await new Promise((resolve) => setTimeout(resolve, 5000));
54
-
55
- try {
56
- const response = await wallet.announceTokenL1({
57
- tokenName: "TestToken1",
58
- tokenTicker: "TT1",
59
- decimals: 0,
60
- maxSupply: 0n,
61
- isFreezable: false,
43
+ brokenTestFn(
44
+ "should announce token and issue tokens successfully",
45
+ async () => {
46
+ const tokenAmount: bigint = 1000n;
47
+ const tokenName = "AnnounceIssueToken";
48
+ const tokenSymbol = "AIT";
49
+ const { wallet } = await IssuerSparkWalletTesting.initialize({
50
+ options: LOCAL_WALLET_CONFIG_SCHNORR,
62
51
  });
63
- console.log("Announce token response:", response);
64
- } catch (error: any) {
65
- console.error("Error when announcing token on L1:", error);
66
- expect(error).toBeUndefined();
67
- }
68
- await faucet.mineBlocks(6);
69
52
 
70
- // Wait for LRC20 processing.
71
- await new Promise((resolve) => setTimeout(resolve, 50000));
53
+ await fundAndAnnounce(wallet, 100000n, tokenName, tokenSymbol);
72
54
 
73
- const publicKeyInfo = await wallet.getIssuerTokenInfo();
55
+ const publicKeyInfo = await wallet.getIssuerTokenInfo();
74
56
 
75
- // Assert token public key info values
76
- const identityPublicKey = await wallet.getIdentityPublicKey();
77
- expect(publicKeyInfo?.tokenName).toEqual("TestToken1");
78
- expect(publicKeyInfo?.tokenSymbol).toEqual("TT1");
79
- expect(publicKeyInfo?.tokenDecimals).toEqual(0);
80
- expect(publicKeyInfo?.maxSupply).toEqual(0n);
81
- expect(publicKeyInfo?.isFreezable).toEqual(false);
57
+ // Assert token public key info values
58
+ const identityPublicKey = await wallet.getIdentityPublicKey();
59
+ expect(publicKeyInfo?.tokenName).toEqual(tokenName);
60
+ expect(publicKeyInfo?.tokenSymbol).toEqual(tokenSymbol);
61
+ expect(publicKeyInfo?.tokenDecimals).toEqual(0);
62
+ expect(publicKeyInfo?.maxSupply).toEqual(0n);
63
+ expect(publicKeyInfo?.isFreezable).toEqual(false);
82
64
 
83
- // Compare the public key using bytesToHex
84
- const pubKeyHex = publicKeyInfo?.tokenPublicKey;
85
- expect(pubKeyHex).toEqual(identityPublicKey);
65
+ // Compare the public key using bytesToHex
66
+ const pubKeyHex = publicKeyInfo?.tokenPublicKey;
67
+ expect(pubKeyHex).toEqual(identityPublicKey);
86
68
 
87
- await wallet.mintTokens(tokenAmount);
69
+ await wallet.mintTokens(tokenAmount);
88
70
 
89
- const sourceBalance = (await wallet.getIssuerTokenBalance()).balance;
90
- expect(sourceBalance).toEqual(tokenAmount);
71
+ const sourceBalance = (await wallet.getIssuerTokenBalance()).balance;
72
+ expect(sourceBalance).toEqual(tokenAmount);
91
73
 
92
- const tokenInfo = await wallet.getTokenInfo();
93
- expect(tokenInfo[0].tokenName).toEqual("TestToken1");
94
- expect(tokenInfo[0].tokenSymbol).toEqual("TT1");
95
- expect(tokenInfo[0].tokenDecimals).toEqual(0);
96
- expect(tokenInfo[0].maxSupply).toEqual(tokenAmount);
97
- });
74
+ const tokenInfo = await wallet.getTokenInfo();
75
+ expect(tokenInfo[0].tokenName).toEqual(tokenName);
76
+ expect(tokenInfo[0].tokenSymbol).toEqual(tokenSymbol);
77
+ expect(tokenInfo[0].tokenDecimals).toEqual(0);
78
+ expect(tokenInfo[0].maxSupply).toEqual(tokenAmount);
79
+ },
80
+ );
98
81
 
99
82
  brokenTestFn(
100
- "should issue a single token and transfer it with ECDSA",
83
+ "should announce, mint, and transfer tokens with ECDSA",
101
84
  async () => {
102
85
  const tokenAmount: bigint = 1000n;
103
86
 
@@ -112,6 +95,8 @@ describe("token integration test", () => {
112
95
  },
113
96
  );
114
97
 
98
+ await fundAndAnnounce(issuerWallet, 100000n, "ECDSATransferToken", "ETT");
99
+
115
100
  await issuerWallet.mintTokens(tokenAmount);
116
101
  await issuerWallet.transferTokens({
117
102
  tokenAmount,
@@ -132,7 +117,7 @@ describe("token integration test", () => {
132
117
  },
133
118
  );
134
119
 
135
- brokenTestFn("monitoring operations", async () => {
120
+ brokenTestFn("should track token operations in monitoring", async () => {
136
121
  const tokenAmount: bigint = 1000n;
137
122
 
138
123
  const { wallet: issuerWallet } = await IssuerSparkWalletTesting.initialize({
@@ -143,6 +128,8 @@ describe("token integration test", () => {
143
128
  options: LOCAL_WALLET_CONFIG_ECDSA,
144
129
  });
145
130
 
131
+ await fundAndAnnounce(issuerWallet, 100000n, "MonitoringToken", "MOT");
132
+
146
133
  await issuerWallet.mintTokens(tokenAmount);
147
134
  await issuerWallet.transferTokens({
148
135
  tokenAmount,
@@ -170,7 +157,7 @@ describe("token integration test", () => {
170
157
  });
171
158
 
172
159
  brokenTestFn(
173
- "should issue a single token and transfer it with Schnorr",
160
+ "should announce, mint, and transfer tokens with Schnorr",
174
161
  async () => {
175
162
  const tokenAmount: bigint = 1000n;
176
163
 
@@ -185,6 +172,13 @@ describe("token integration test", () => {
185
172
  },
186
173
  );
187
174
 
175
+ await fundAndAnnounce(
176
+ issuerWallet,
177
+ 100000n,
178
+ "SchnorrTransferToken",
179
+ "STT",
180
+ );
181
+
188
182
  await issuerWallet.mintTokens(tokenAmount);
189
183
  await issuerWallet.transferTokens({
190
184
  tokenPublicKey: await issuerWallet.getIdentityPublicKey(),
@@ -204,140 +198,166 @@ describe("token integration test", () => {
204
198
  },
205
199
  );
206
200
 
207
- brokenTestFn("should freeze tokens with ECDSA", async () => {
208
- const tokenAmount: bigint = 1000n;
209
- const { wallet: issuerWallet } = await IssuerSparkWalletTesting.initialize({
210
- options: LOCAL_WALLET_CONFIG_ECDSA,
211
- });
201
+ brokenTestFn(
202
+ "should announce, mint, freeze and unfreeze tokens with ECDSA",
203
+ async () => {
204
+ const tokenAmount: bigint = 1000n;
205
+ const { wallet: issuerWallet } =
206
+ await IssuerSparkWalletTesting.initialize({
207
+ options: LOCAL_WALLET_CONFIG_ECDSA,
208
+ });
212
209
 
213
- await issuerWallet.mintTokens(tokenAmount);
210
+ await fundAndAnnounce(issuerWallet, 100000n, "ECDSAFreezeToken", "EFT");
211
+ await issuerWallet.mintTokens(tokenAmount);
214
212
 
215
- // Check issuer balance after minting
216
- const issuerBalanceAfterMint = (await issuerWallet.getIssuerTokenBalance())
217
- .balance;
218
- expect(issuerBalanceAfterMint).toEqual(tokenAmount);
213
+ // Check issuer balance after minting
214
+ const issuerBalanceAfterMint = (
215
+ await issuerWallet.getIssuerTokenBalance()
216
+ ).balance;
217
+ expect(issuerBalanceAfterMint).toEqual(tokenAmount);
219
218
 
220
- const { wallet: userWallet } = await SparkWalletTesting.initialize({
221
- options: LOCAL_WALLET_CONFIG_ECDSA,
222
- });
223
- const userWalletPublicKey = await userWallet.getSparkAddress();
219
+ const { wallet: userWallet } = await SparkWalletTesting.initialize({
220
+ options: LOCAL_WALLET_CONFIG_ECDSA,
221
+ });
222
+ const userWalletPublicKey = await userWallet.getSparkAddress();
224
223
 
225
- await issuerWallet.transferTokens({
226
- tokenAmount,
227
- tokenPublicKey: await issuerWallet.getIdentityPublicKey(),
228
- receiverSparkAddress: userWalletPublicKey,
229
- });
230
- const issuerBalanceAfterTransfer = (
231
- await issuerWallet.getIssuerTokenBalance()
232
- ).balance;
233
- expect(issuerBalanceAfterTransfer).toEqual(0n);
224
+ await issuerWallet.transferTokens({
225
+ tokenAmount,
226
+ tokenPublicKey: await issuerWallet.getIdentityPublicKey(),
227
+ receiverSparkAddress: userWalletPublicKey,
228
+ });
229
+ const issuerBalanceAfterTransfer = (
230
+ await issuerWallet.getIssuerTokenBalance()
231
+ ).balance;
232
+ expect(issuerBalanceAfterTransfer).toEqual(0n);
234
233
 
235
- const tokenPublicKey = await issuerWallet.getIdentityPublicKey();
236
- const userBalanceObj = await userWallet.getBalance();
237
- const userBalanceAfterTransfer = filterTokenBalanceForTokenPublicKey(
238
- userBalanceObj?.tokenBalances,
239
- tokenPublicKey,
240
- );
241
- expect(userBalanceAfterTransfer.balance).toEqual(tokenAmount);
242
- // Freeze tokens
243
- const freezeResponse = await issuerWallet.freezeTokens(userWalletPublicKey);
244
- expect(freezeResponse.impactedOutputIds.length).toBeGreaterThan(0);
245
- expect(freezeResponse.impactedTokenAmount).toEqual(tokenAmount);
246
-
247
- // Unfreeze tokens
248
- const unfreezeResponse =
249
- await issuerWallet.unfreezeTokens(userWalletPublicKey);
250
- expect(unfreezeResponse.impactedOutputIds.length).toBeGreaterThan(0);
251
- expect(unfreezeResponse.impactedTokenAmount).toEqual(tokenAmount);
252
- });
234
+ const tokenPublicKey = await issuerWallet.getIdentityPublicKey();
235
+ const userBalanceObj = await userWallet.getBalance();
236
+ const userBalanceAfterTransfer = filterTokenBalanceForTokenPublicKey(
237
+ userBalanceObj?.tokenBalances,
238
+ tokenPublicKey,
239
+ );
240
+ expect(userBalanceAfterTransfer.balance).toEqual(tokenAmount);
241
+ // Freeze tokens
242
+ const freezeResponse =
243
+ await issuerWallet.freezeTokens(userWalletPublicKey);
244
+ expect(freezeResponse.impactedOutputIds.length).toBeGreaterThan(0);
245
+ expect(freezeResponse.impactedTokenAmount).toEqual(tokenAmount);
246
+
247
+ // Unfreeze tokens
248
+ const unfreezeResponse =
249
+ await issuerWallet.unfreezeTokens(userWalletPublicKey);
250
+ expect(unfreezeResponse.impactedOutputIds.length).toBeGreaterThan(0);
251
+ expect(unfreezeResponse.impactedTokenAmount).toEqual(tokenAmount);
252
+ },
253
+ );
253
254
 
254
- brokenTestFn("should freeze tokens with Schnorr", async () => {
255
- const tokenAmount: bigint = 1000n;
256
- const { wallet: issuerWallet } = await IssuerSparkWalletTesting.initialize({
257
- options: LOCAL_WALLET_CONFIG_SCHNORR,
258
- });
255
+ brokenTestFn(
256
+ "should announce, mint, freeze and unfreeze tokens with Schnorr",
257
+ async () => {
258
+ const tokenAmount: bigint = 1000n;
259
+ const { wallet: issuerWallet } =
260
+ await IssuerSparkWalletTesting.initialize({
261
+ options: LOCAL_WALLET_CONFIG_SCHNORR,
262
+ });
259
263
 
260
- await issuerWallet.mintTokens(tokenAmount);
264
+ await fundAndAnnounce(issuerWallet, 100000n, "SchnorrFreezeToken", "SFT");
261
265
 
262
- // Check issuer balance after minting
263
- const issuerBalanceAfterMint = (await issuerWallet.getIssuerTokenBalance())
264
- .balance;
265
- expect(issuerBalanceAfterMint).toEqual(tokenAmount);
266
+ await issuerWallet.mintTokens(tokenAmount);
266
267
 
267
- const { wallet: userWallet } = await SparkWalletTesting.initialize({
268
- options: LOCAL_WALLET_CONFIG_SCHNORR,
269
- });
270
- const userWalletPublicKey = await userWallet.getSparkAddress();
268
+ // Check issuer balance after minting
269
+ const issuerBalanceAfterMint = (
270
+ await issuerWallet.getIssuerTokenBalance()
271
+ ).balance;
272
+ expect(issuerBalanceAfterMint).toEqual(tokenAmount);
271
273
 
272
- await issuerWallet.transferTokens({
273
- tokenAmount,
274
- tokenPublicKey: await issuerWallet.getIdentityPublicKey(),
275
- receiverSparkAddress: userWalletPublicKey,
276
- });
274
+ const { wallet: userWallet } = await SparkWalletTesting.initialize({
275
+ options: LOCAL_WALLET_CONFIG_SCHNORR,
276
+ });
277
+ const userWalletPublicKey = await userWallet.getSparkAddress();
277
278
 
278
- const issuerBalanceAfterTransfer = (
279
- await issuerWallet.getIssuerTokenBalance()
280
- ).balance;
281
- expect(issuerBalanceAfterTransfer).toEqual(0n);
279
+ await issuerWallet.transferTokens({
280
+ tokenAmount,
281
+ tokenPublicKey: await issuerWallet.getIdentityPublicKey(),
282
+ receiverSparkAddress: userWalletPublicKey,
283
+ });
282
284
 
283
- const tokenPublicKey = await issuerWallet.getIdentityPublicKey();
284
- const userBalanceObj = await userWallet.getBalance();
285
- const userBalanceAfterTransfer = filterTokenBalanceForTokenPublicKey(
286
- userBalanceObj?.tokenBalances,
287
- tokenPublicKey,
288
- );
289
- expect(userBalanceAfterTransfer.balance).toEqual(tokenAmount);
285
+ const issuerBalanceAfterTransfer = (
286
+ await issuerWallet.getIssuerTokenBalance()
287
+ ).balance;
288
+ expect(issuerBalanceAfterTransfer).toEqual(0n);
290
289
 
291
- const freezeResult = await issuerWallet.freezeTokens(userWalletPublicKey);
292
- expect(freezeResult.impactedOutputIds.length).toBe(1);
293
- expect(freezeResult.impactedTokenAmount).toBe(1000n);
290
+ const tokenPublicKey = await issuerWallet.getIdentityPublicKey();
291
+ const userBalanceObj = await userWallet.getBalance();
292
+ const userBalanceAfterTransfer = filterTokenBalanceForTokenPublicKey(
293
+ userBalanceObj?.tokenBalances,
294
+ tokenPublicKey,
295
+ );
296
+ expect(userBalanceAfterTransfer.balance).toEqual(tokenAmount);
294
297
 
295
- const unfreezeResult =
296
- await issuerWallet.unfreezeTokens(userWalletPublicKey);
297
- expect(unfreezeResult.impactedOutputIds.length).toBe(1);
298
- expect(unfreezeResult.impactedTokenAmount).toBe(1000n);
299
- });
298
+ const freezeResult = await issuerWallet.freezeTokens(userWalletPublicKey);
299
+ expect(freezeResult.impactedOutputIds.length).toBe(1);
300
+ expect(freezeResult.impactedTokenAmount).toBe(1000n);
300
301
 
301
- brokenTestFn("should burn tokens with ECDSA", async () => {
302
- const tokenAmount: bigint = 200n;
303
- const { wallet: issuerWallet } = await IssuerSparkWalletTesting.initialize({
304
- options: LOCAL_WALLET_CONFIG_ECDSA,
305
- });
306
- await issuerWallet.mintTokens(tokenAmount);
302
+ const unfreezeResult =
303
+ await issuerWallet.unfreezeTokens(userWalletPublicKey);
304
+ expect(unfreezeResult.impactedOutputIds.length).toBe(1);
305
+ expect(unfreezeResult.impactedTokenAmount).toBe(1000n);
306
+ },
307
+ );
307
308
 
308
- const issuerTokenBalance = (await issuerWallet.getIssuerTokenBalance())
309
- .balance;
310
- expect(issuerTokenBalance).toEqual(tokenAmount);
309
+ brokenTestFn(
310
+ "should announce, mint, and burn tokens with ECDSA",
311
+ async () => {
312
+ const tokenAmount: bigint = 200n;
313
+ const { wallet: issuerWallet } =
314
+ await IssuerSparkWalletTesting.initialize({
315
+ options: LOCAL_WALLET_CONFIG_ECDSA,
316
+ });
311
317
 
312
- await issuerWallet.burnTokens(tokenAmount);
318
+ await fundAndAnnounce(issuerWallet, 100000n, "ECDSABurnToken", "EBT");
319
+ await issuerWallet.mintTokens(tokenAmount);
313
320
 
314
- const issuerTokenBalanceAfterBurn = (
315
- await issuerWallet.getIssuerTokenBalance()
316
- ).balance;
317
- expect(issuerTokenBalanceAfterBurn).toEqual(0n);
318
- });
321
+ const issuerTokenBalance = (await issuerWallet.getIssuerTokenBalance())
322
+ .balance;
323
+ expect(issuerTokenBalance).toEqual(tokenAmount);
319
324
 
320
- brokenTestFn("should burn tokens with Schnorr", async () => {
321
- const tokenAmount: bigint = 200n;
322
- const { wallet: issuerWallet } = await IssuerSparkWalletTesting.initialize({
323
- options: LOCAL_WALLET_CONFIG_SCHNORR,
324
- });
325
- await issuerWallet.mintTokens(tokenAmount);
325
+ await issuerWallet.burnTokens(tokenAmount);
326
326
 
327
- const issuerTokenBalance = (await issuerWallet.getIssuerTokenBalance())
328
- .balance;
329
- expect(issuerTokenBalance).toEqual(tokenAmount);
327
+ const issuerTokenBalanceAfterBurn = (
328
+ await issuerWallet.getIssuerTokenBalance()
329
+ ).balance;
330
+ expect(issuerTokenBalanceAfterBurn).toEqual(0n);
331
+ },
332
+ );
330
333
 
331
- await issuerWallet.burnTokens(tokenAmount);
334
+ brokenTestFn(
335
+ "should announce, mint, and burn tokens with Schnorr",
336
+ async () => {
337
+ const tokenAmount: bigint = 200n;
338
+ const { wallet: issuerWallet } =
339
+ await IssuerSparkWalletTesting.initialize({
340
+ options: LOCAL_WALLET_CONFIG_SCHNORR,
341
+ });
332
342
 
333
- const issuerTokenBalanceAfterBurn = (
334
- await issuerWallet.getIssuerTokenBalance()
335
- ).balance;
336
- expect(issuerTokenBalanceAfterBurn).toEqual(0n);
337
- });
343
+ await fundAndAnnounce(issuerWallet, 100000n, "SchnorrBurnToken", "SBT");
344
+ await issuerWallet.mintTokens(tokenAmount);
345
+
346
+ const issuerTokenBalance = (await issuerWallet.getIssuerTokenBalance())
347
+ .balance;
348
+ expect(issuerTokenBalance).toEqual(tokenAmount);
349
+
350
+ await issuerWallet.burnTokens(tokenAmount);
351
+
352
+ const issuerTokenBalanceAfterBurn = (
353
+ await issuerWallet.getIssuerTokenBalance()
354
+ ).balance;
355
+ expect(issuerTokenBalanceAfterBurn).toEqual(0n);
356
+ },
357
+ );
338
358
 
339
359
  brokenTestFn(
340
- "mint, transfer to user, user transfer to issuer, burn with ECDSA",
360
+ "should complete full token lifecycle with ECDSA: announce, mint, transfer, return, burn",
341
361
  async () => {
342
362
  const tokenAmount: bigint = 1000n;
343
363
 
@@ -350,6 +370,12 @@ describe("token integration test", () => {
350
370
  options: LOCAL_WALLET_CONFIG_ECDSA,
351
371
  });
352
372
 
373
+ await fundAndAnnounce(
374
+ issuerWallet,
375
+ 100000n,
376
+ "ECDSAFullCycleToken",
377
+ "EFCT",
378
+ );
353
379
  await issuerWallet.mintTokens(tokenAmount);
354
380
 
355
381
  const issuerBalanceAfterMint = (
@@ -403,7 +429,7 @@ describe("token integration test", () => {
403
429
  );
404
430
 
405
431
  brokenTestFn(
406
- "mint, transfer to user, user transfer to issuer, burn with Schnorr",
432
+ "should complete full token lifecycle with Schnorr: announce, mint, transfer, return, burn",
407
433
  async () => {
408
434
  const tokenAmount: bigint = 1000n;
409
435
 
@@ -416,7 +442,12 @@ describe("token integration test", () => {
416
442
  options: LOCAL_WALLET_CONFIG_SCHNORR,
417
443
  });
418
444
 
419
- const tokenPublicKey = await issuerWallet.getIdentityPublicKey();
445
+ await fundAndAnnounce(
446
+ issuerWallet,
447
+ 100000n,
448
+ "SchnorrFullCycleToken",
449
+ "SFCT",
450
+ );
420
451
  await issuerWallet.mintTokens(tokenAmount);
421
452
 
422
453
  const issuerBalanceAfterMint = (
@@ -428,7 +459,7 @@ describe("token integration test", () => {
428
459
 
429
460
  await issuerWallet.transferTokens({
430
461
  tokenAmount,
431
- tokenPublicKey,
462
+ tokenPublicKey: await issuerWallet.getIdentityPublicKey(),
432
463
  receiverSparkAddress: userWalletPublicKey,
433
464
  });
434
465
 
@@ -471,3 +502,37 @@ describe("token integration test", () => {
471
502
  },
472
503
  );
473
504
  });
505
+
506
+ async function fundAndAnnounce(
507
+ wallet: IssuerSparkWallet,
508
+ maxSupply: bigint = 100000n,
509
+ tokenName: string = "TestToken1",
510
+ tokenSymbol: string = "TT1",
511
+ ) {
512
+ // Faucet funds to the Issuer wallet because announcing a token
513
+ // requires ownership of an L1 UTXO.
514
+ const faucet = BitcoinFaucet.getInstance();
515
+ const l1WalletPubKey = await wallet.getTokenL1Address();
516
+ await faucet.sendToAddress(l1WalletPubKey, 100_000n);
517
+ await faucet.mineBlocks(6);
518
+
519
+ await new Promise((resolve) => setTimeout(resolve, 3000));
520
+
521
+ try {
522
+ const response = await wallet.announceTokenL1(
523
+ tokenName,
524
+ tokenSymbol,
525
+ 0,
526
+ maxSupply,
527
+ false,
528
+ );
529
+ console.log("Announce token response:", response);
530
+ } catch (error: any) {
531
+ console.error("Error when announcing token on L1:", error);
532
+ expect(error).toBeUndefined();
533
+ }
534
+ await faucet.mineBlocks(1);
535
+
536
+ // Wait for LRC20 processing.
537
+ await new Promise((resolve) => setTimeout(resolve, 30000));
538
+ }
package/src/types.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { Lrc20Protos } from "@buildonspark/lrc20-sdk";
2
-
3
1
  // String enums to replace numeric enums
4
2
  export enum LayerType {
5
3
  L1 = "L1",
@@ -1,11 +1,16 @@
1
1
  import { sha256 } from "@scure/btc-signer/utils";
2
2
  import { FreezeTokensPayload } from "@buildonspark/spark-sdk/proto/spark";
3
+ import { ValidationError } from "@buildonspark/spark-sdk";
3
4
 
4
5
  export function hashFreezeTokensPayload(
5
6
  payload: FreezeTokensPayload,
6
7
  ): Uint8Array {
7
8
  if (!payload) {
8
- throw new Error("freeze tokens payload cannot be nil");
9
+ throw new ValidationError("Freeze tokens payload cannot be nil", {
10
+ field: "payload",
11
+ value: payload,
12
+ expected: "valid freeze tokens payload",
13
+ });
9
14
  }
10
15
 
11
16
  let allHashes: Uint8Array[] = [];
@@ -1,16 +1,8 @@
1
1
  import {
2
2
  ListAllTokenTransactionsResponse,
3
3
  TokenPubkeyInfo,
4
- Lrc20Protos,
5
4
  } from "@buildonspark/lrc20-sdk";
6
- import {
7
- GetTokenActivityResponse,
8
- TokenPubKeyInfoResponse,
9
- OperationType,
10
- OnChainTransactionStatus,
11
- SparkTransactionStatus,
12
- LayerType,
13
- } from "../types.js";
5
+ import { GetTokenActivityResponse, TokenPubKeyInfoResponse } from "../types.js";
14
6
  import { bytesToHex, bytesToNumberBE } from "@noble/curves/abstract/utils";
15
7
  import {
16
8
  mapOperationType,