@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 +87 -48
- package/dist/index.d.cts +1 -8
- package/dist/index.d.ts +1 -8
- package/dist/index.js +89 -47
- package/package.json +3 -3
- package/src/issuer-spark-wallet.ts +71 -39
- package/src/services/freeze.ts +22 -8
- package/src/tests/integration/spark.test.ts +246 -181
- package/src/types.ts +0 -2
- package/src/utils/token-hashing.ts +6 -1
- package/src/utils/type-mappers.ts +1 -9
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
|
|
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
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
|
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
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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(
|
|
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(
|
|
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 {
|
|
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
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
|
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
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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.
|
|
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.
|
|
58
|
-
"@buildonspark/spark-sdk": "0.1.
|
|
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 {
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
try {
|
|
85
|
+
const tokenInfo = await lrc20Client.getTokenPubkeyInfo({
|
|
86
|
+
publicKeys: [hexToBytes(await super.getIdentityPublicKey())],
|
|
87
|
+
});
|
|
82
88
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
235
|
+
try {
|
|
236
|
+
const tx = await this.lrc20Wallet!.prepareAnnouncement(
|
|
237
|
+
announcement,
|
|
238
|
+
feeRateSatsPerVb,
|
|
239
|
+
);
|
|
218
240
|
|
|
219
|
-
|
|
220
|
-
|
|
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
|
}
|
package/src/services/freeze.ts
CHANGED
|
@@ -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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
15
|
+
describe("token integration tests", () => {
|
|
14
16
|
jest.setTimeout(80000);
|
|
15
17
|
|
|
16
|
-
brokenTestFn(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
26
|
+
await wallet.mintTokens(tokenAmount);
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
const tokenBalance = (await wallet.getIssuerTokenBalance()).balance;
|
|
29
|
+
expect(tokenBalance).toEqual(tokenAmount);
|
|
30
|
+
},
|
|
31
|
+
);
|
|
27
32
|
|
|
28
|
-
brokenTestFn("should
|
|
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
|
|
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(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
71
|
-
await new Promise((resolve) => setTimeout(resolve, 50000));
|
|
53
|
+
await fundAndAnnounce(wallet, 100000n, tokenName, tokenSymbol);
|
|
72
54
|
|
|
73
|
-
|
|
55
|
+
const publicKeyInfo = await wallet.getIssuerTokenInfo();
|
|
74
56
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
65
|
+
// Compare the public key using bytesToHex
|
|
66
|
+
const pubKeyHex = publicKeyInfo?.tokenPublicKey;
|
|
67
|
+
expect(pubKeyHex).toEqual(identityPublicKey);
|
|
86
68
|
|
|
87
|
-
|
|
69
|
+
await wallet.mintTokens(tokenAmount);
|
|
88
70
|
|
|
89
|
-
|
|
90
|
-
|
|
71
|
+
const sourceBalance = (await wallet.getIssuerTokenBalance()).balance;
|
|
72
|
+
expect(sourceBalance).toEqual(tokenAmount);
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
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("
|
|
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
|
|
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(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
210
|
+
await fundAndAnnounce(issuerWallet, 100000n, "ECDSAFreezeToken", "EFT");
|
|
211
|
+
await issuerWallet.mintTokens(tokenAmount);
|
|
214
212
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
213
|
+
// Check issuer balance after minting
|
|
214
|
+
const issuerBalanceAfterMint = (
|
|
215
|
+
await issuerWallet.getIssuerTokenBalance()
|
|
216
|
+
).balance;
|
|
217
|
+
expect(issuerBalanceAfterMint).toEqual(tokenAmount);
|
|
219
218
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
219
|
+
const { wallet: userWallet } = await SparkWalletTesting.initialize({
|
|
220
|
+
options: LOCAL_WALLET_CONFIG_ECDSA,
|
|
221
|
+
});
|
|
222
|
+
const userWalletPublicKey = await userWallet.getSparkAddress();
|
|
224
223
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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(
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
264
|
+
await fundAndAnnounce(issuerWallet, 100000n, "SchnorrFreezeToken", "SFT");
|
|
261
265
|
|
|
262
|
-
|
|
263
|
-
const issuerBalanceAfterMint = (await issuerWallet.getIssuerTokenBalance())
|
|
264
|
-
.balance;
|
|
265
|
-
expect(issuerBalanceAfterMint).toEqual(tokenAmount);
|
|
266
|
+
await issuerWallet.mintTokens(tokenAmount);
|
|
266
267
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
268
|
+
// Check issuer balance after minting
|
|
269
|
+
const issuerBalanceAfterMint = (
|
|
270
|
+
await issuerWallet.getIssuerTokenBalance()
|
|
271
|
+
).balance;
|
|
272
|
+
expect(issuerBalanceAfterMint).toEqual(tokenAmount);
|
|
271
273
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
});
|
|
274
|
+
const { wallet: userWallet } = await SparkWalletTesting.initialize({
|
|
275
|
+
options: LOCAL_WALLET_CONFIG_SCHNORR,
|
|
276
|
+
});
|
|
277
|
+
const userWalletPublicKey = await userWallet.getSparkAddress();
|
|
277
278
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
279
|
+
await issuerWallet.transferTokens({
|
|
280
|
+
tokenAmount,
|
|
281
|
+
tokenPublicKey: await issuerWallet.getIdentityPublicKey(),
|
|
282
|
+
receiverSparkAddress: userWalletPublicKey,
|
|
283
|
+
});
|
|
282
284
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
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
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
318
|
+
await fundAndAnnounce(issuerWallet, 100000n, "ECDSABurnToken", "EBT");
|
|
319
|
+
await issuerWallet.mintTokens(tokenAmount);
|
|
313
320
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
expect(issuerTokenBalanceAfterBurn).toEqual(0n);
|
|
318
|
-
});
|
|
321
|
+
const issuerTokenBalance = (await issuerWallet.getIssuerTokenBalance())
|
|
322
|
+
.balance;
|
|
323
|
+
expect(issuerTokenBalance).toEqual(tokenAmount);
|
|
319
324
|
|
|
320
|
-
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
327
|
+
const issuerTokenBalanceAfterBurn = (
|
|
328
|
+
await issuerWallet.getIssuerTokenBalance()
|
|
329
|
+
).balance;
|
|
330
|
+
expect(issuerTokenBalanceAfterBurn).toEqual(0n);
|
|
331
|
+
},
|
|
332
|
+
);
|
|
330
333
|
|
|
331
|
-
|
|
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
|
-
|
|
334
|
-
await issuerWallet.
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
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,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
|
|
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,
|