@buildonspark/issuer-sdk 0.0.78 β†’ 0.0.80

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.
@@ -139,9 +139,14 @@ describe.each(TEST_CONFIGS)(
139
139
  const tokenAmount: bigint = 1000n;
140
140
 
141
141
  await sharedIssuerWallet.mintTokens(tokenAmount);
142
+
143
+ const sharedIssuerBalance =
144
+ await sharedIssuerWallet.getIssuerTokenBalance();
145
+ expect(sharedIssuerBalance).toBeDefined();
146
+ expect(sharedIssuerBalance.tokenIdentifier).toBeDefined();
142
147
  await sharedIssuerWallet.transferTokens({
143
148
  tokenAmount,
144
- tokenPublicKey: sharedTokenPublicKey,
149
+ tokenIdentifier: sharedIssuerBalance.tokenIdentifier!,
145
150
  receiverSparkAddress: await sharedUserWallet.getSparkAddress(),
146
151
  });
147
152
 
@@ -308,24 +313,28 @@ describe.each(TEST_CONFIGS)(
308
313
  });
309
314
 
310
315
  await sharedIssuerWallet.mintTokens(tokenAmount);
311
- const sourceBalanceBefore = (
312
- await sharedIssuerWallet.getIssuerTokenBalance()
313
- ).balance;
316
+ const sharedIssuerBalance =
317
+ await sharedIssuerWallet.getIssuerTokenBalance();
318
+ expect(sharedIssuerBalance).toBeDefined();
319
+ expect(sharedIssuerBalance.tokenIdentifier).toBeDefined();
320
+
321
+ const tokenIdentifier = sharedIssuerBalance.tokenIdentifier!;
322
+ const sourceBalanceBefore = sharedIssuerBalance.balance;
314
323
 
315
324
  await sharedIssuerWallet.batchTransferTokens([
316
325
  {
317
326
  tokenAmount: tokenAmount / 3n,
318
- tokenPublicKey: sharedTokenPublicKey,
327
+ tokenIdentifier,
319
328
  receiverSparkAddress: await destinationWallet.getSparkAddress(),
320
329
  },
321
330
  {
322
331
  tokenAmount: tokenAmount / 3n,
323
- tokenPublicKey: sharedTokenPublicKey,
332
+ tokenIdentifier,
324
333
  receiverSparkAddress: await destinationWallet2.getSparkAddress(),
325
334
  },
326
335
  {
327
336
  tokenAmount: tokenAmount / 3n,
328
- tokenPublicKey: sharedTokenPublicKey,
337
+ tokenIdentifier,
329
338
  receiverSparkAddress: await destinationWallet3.getSparkAddress(),
330
339
  },
331
340
  ]);
@@ -461,9 +470,14 @@ describe.each(TEST_CONFIGS)(
461
470
  await issuerWallet.mintTokens(tokenAmount);
462
471
 
463
472
  // Check issuer balance after minting
464
- const issuerBalanceAfterMint = (
465
- await issuerWallet.getIssuerTokenBalance()
466
- ).balance;
473
+ const issuerBalanceObjAfterMint =
474
+ await issuerWallet.getIssuerTokenBalance();
475
+ expect(issuerBalanceObjAfterMint).toBeDefined();
476
+ expect(issuerBalanceObjAfterMint.tokenIdentifier).toBeDefined();
477
+
478
+ const issuerBalanceAfterMint = issuerBalanceObjAfterMint.balance;
479
+ const tokenIdentifier = issuerBalanceObjAfterMint.tokenIdentifier!;
480
+
467
481
  expect(issuerBalanceAfterMint).toEqual(tokenAmount);
468
482
 
469
483
  const { wallet: userWallet } = await SparkWalletTesting.initialize({
@@ -473,7 +487,7 @@ describe.each(TEST_CONFIGS)(
473
487
 
474
488
  await issuerWallet.transferTokens({
475
489
  tokenAmount,
476
- tokenPublicKey: await issuerWallet.getIdentityPublicKey(),
490
+ tokenIdentifier,
477
491
  receiverSparkAddress: userWalletPublicKey,
478
492
  });
479
493
  const issuerBalanceAfterTransfer = (
@@ -533,17 +547,18 @@ describe.each(TEST_CONFIGS)(
533
547
  .balance;
534
548
 
535
549
  await sharedIssuerWallet.mintTokens(tokenAmount);
536
-
537
- const issuerBalanceAfterMint = (
538
- await sharedIssuerWallet.getIssuerTokenBalance()
539
- ).balance;
550
+ const issuerBalanceObjAfterMint =
551
+ await sharedIssuerWallet.getIssuerTokenBalance();
552
+ expect(issuerBalanceObjAfterMint).toBeDefined();
553
+ const issuerBalanceAfterMint = issuerBalanceObjAfterMint.balance;
540
554
  expect(issuerBalanceAfterMint).toEqual(initialBalance + tokenAmount);
541
-
555
+ expect(issuerBalanceObjAfterMint.tokenIdentifier).toBeDefined();
556
+ const tokenIdentifier = issuerBalanceObjAfterMint.tokenIdentifier!;
542
557
  const userWalletPublicKey = await userWallet.getSparkAddress();
543
558
 
544
559
  await sharedIssuerWallet.transferTokens({
545
560
  tokenAmount,
546
- tokenPublicKey: sharedTokenPublicKey,
561
+ tokenIdentifier,
547
562
  receiverSparkAddress: userWalletPublicKey,
548
563
  });
549
564
 
@@ -560,7 +575,7 @@ describe.each(TEST_CONFIGS)(
560
575
  expect(userBalanceAfterTransfer.balance).toEqual(tokenAmount);
561
576
 
562
577
  await userWallet.transferTokens({
563
- tokenPublicKey: sharedTokenPublicKey,
578
+ tokenIdentifier,
564
579
  tokenAmount,
565
580
  receiverSparkAddress: await sharedIssuerWallet.getSparkAddress(),
566
581
  });
@@ -660,6 +675,37 @@ describe.each(TEST_CONFIGS)(
660
675
  // expect(transferBackTransaction).toBeDefined();
661
676
  // expect(userBurnTransaction).toBeDefined();
662
677
  // });
678
+
679
+ (config.tokenTransactionVersion === "V0" ? it.skip : it)(
680
+ "should create a token using createToken API",
681
+ async () => {
682
+ const { wallet: issuerWallet } =
683
+ await IssuerSparkWalletTesting.initialize({
684
+ options: config,
685
+ });
686
+
687
+ const tokenName = `${name}Creatable`;
688
+ const tokenTicker = "CRT";
689
+ const maxSupply = 5000n;
690
+ const decimals = 0;
691
+ const txId = await issuerWallet.createToken({
692
+ tokenName,
693
+ tokenTicker,
694
+ decimals,
695
+ isFreezable: false,
696
+ maxSupply,
697
+ });
698
+
699
+ expect(typeof txId).toBe("string");
700
+ expect(txId.length).toBeGreaterThan(0);
701
+
702
+ const metadata = await issuerWallet.getIssuerTokenMetadata();
703
+ expect(metadata.tokenName).toEqual(tokenName);
704
+ expect(metadata.tokenTicker).toEqual(tokenTicker);
705
+ expect(metadata.maxSupply).toEqual(maxSupply);
706
+ expect(metadata.decimals).toEqual(decimals);
707
+ },
708
+ );
663
709
  },
664
710
  );
665
711
 
@@ -45,6 +45,10 @@ describe("Stress test for token transfers", () => {
45
45
  const tokenPublicKey = await issuerWallet.getIdentityPublicKey();
46
46
  const userWalletSparkAddress = await userWallet.getSparkAddress();
47
47
  const issuerWalletSparkAddress = await issuerWallet.getSparkAddress();
48
+ const issuerBalanceObj = await issuerWallet.getIssuerTokenBalance();
49
+ expect(issuerBalanceObj).toBeDefined();
50
+ expect(issuerBalanceObj.tokenIdentifier).toBeDefined();
51
+ const tokenIdentifier = issuerBalanceObj.tokenIdentifier!;
48
52
 
49
53
  for (let i = 0; i < maxTransactionCycles; i++) {
50
54
  if (timeoutReached) {
@@ -59,7 +63,7 @@ describe("Stress test for token transfers", () => {
59
63
  try {
60
64
  // Transfer tokens from issuer to user
61
65
  await issuerWallet.transferTokens({
62
- tokenPublicKey,
66
+ tokenIdentifier,
63
67
  tokenAmount: TOKEN_AMOUNT,
64
68
  receiverSparkAddress: userWalletSparkAddress,
65
69
  });
@@ -75,7 +79,7 @@ describe("Stress test for token transfers", () => {
75
79
 
76
80
  // Transfer tokens from user to issuer
77
81
  await userWallet.transferTokens({
78
- tokenPublicKey: tokenPublicKey,
82
+ tokenIdentifier,
79
83
  tokenAmount: TOKEN_AMOUNT,
80
84
  receiverSparkAddress: issuerWalletSparkAddress,
81
85
  });
@@ -126,20 +130,23 @@ describe("Stress test for token transfers", () => {
126
130
  });
127
131
 
128
132
  await issuer.wallet.mintTokens(TOKEN_AMOUNT);
129
- const tokenPublicKey = await issuer.wallet.getIdentityPublicKey();
130
133
  const userAddress = await user.wallet.getSparkAddress();
134
+ const issuerBalanceObj = await issuer.wallet.getIssuerTokenBalance();
135
+ expect(issuerBalanceObj).toBeDefined();
136
+ expect(issuerBalanceObj.tokenIdentifier).toBeDefined();
137
+ const tokenIdentifier = issuerBalanceObj.tokenIdentifier!;
131
138
 
132
- return { issuer, tokenPublicKey, userAddress };
139
+ return { issuer, tokenIdentifier, userAddress };
133
140
  }),
134
141
  );
135
142
 
136
143
  const transactions = walletPairs.map(
137
- ({ issuer, tokenPublicKey, userAddress }) =>
144
+ ({ issuer, tokenIdentifier, userAddress }) =>
138
145
  async () => {
139
146
  const start_time = Date.now();
140
147
  try {
141
148
  await issuer.wallet.transferTokens({
142
- tokenPublicKey,
149
+ tokenIdentifier,
143
150
  tokenAmount: TOKEN_AMOUNT,
144
151
  receiverSparkAddress: userAddress,
145
152
  });
@@ -0,0 +1,85 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { validateTokenParameters } from "../utils/create-validation.js";
3
+
4
+ const MAX_SUPPLY_128 = (1n << 128n) - 1n;
5
+
6
+ describe("validateTokenParameters (V1)", () => {
7
+ describe("valid inputs", () => {
8
+ it("accepts minimum & maximum byte length for name", () => {
9
+ expect(() => validateTokenParameters("abc", "AAA", 0, 1n)).not.toThrow();
10
+ expect(() =>
11
+ validateTokenParameters("12345678901234567890", "AAA", 0, 1n),
12
+ ).not.toThrow();
13
+ });
14
+
15
+ it("accepts minimum & maximum byte length for symbol", () => {
16
+ expect(() =>
17
+ validateTokenParameters("Token", "ABC", 0, 1n),
18
+ ).not.toThrow();
19
+ expect(() =>
20
+ validateTokenParameters("Token", "ABCDEF", 0, 1n),
21
+ ).not.toThrow();
22
+ });
23
+
24
+ it("accepts combined length exactly at upper bound", () => {
25
+ // name 17 bytes + symbol 3 bytes = 20 bytes
26
+ expect(() =>
27
+ validateTokenParameters("ABCDEFGHIJKLMNOPQ", "AAA", 0, 1n),
28
+ ).not.toThrow();
29
+ });
30
+
31
+ it("accepts decimals within 0-255 and maxSupply within u128", () => {
32
+ expect(() =>
33
+ validateTokenParameters("Token", "TOK", 255, MAX_SUPPLY_128),
34
+ ).not.toThrow();
35
+ });
36
+
37
+ it("handles multi-byte UTF-8 characters correctly", () => {
38
+ // "πŸš€" is 4 bytes in UTF-8
39
+ expect(() =>
40
+ validateTokenParameters("TokπŸš€n", "TOK", 8, 1000n),
41
+ ).not.toThrow();
42
+ });
43
+ });
44
+
45
+ describe("invalid inputs", () => {
46
+ it("rejects name too short or too long", () => {
47
+ expect(() => validateTokenParameters("ab", "AAA", 0, 1n)).toThrow();
48
+ expect(() =>
49
+ validateTokenParameters("123456789012345678901", "AAA", 0, 1n),
50
+ ).toThrow();
51
+ });
52
+
53
+ it("rejects symbol too short or too long", () => {
54
+ expect(() => validateTokenParameters("Token", "AB", 0, 1n)).toThrow();
55
+ expect(() =>
56
+ validateTokenParameters("Token", "ABCDEFG", 0, 1n),
57
+ ).toThrow();
58
+ });
59
+
60
+ it("rejects decimals outside 0-255 or non-integer", () => {
61
+ // >255
62
+ expect(() => validateTokenParameters("Token", "TOK", 256, 1n)).toThrow();
63
+ // negative
64
+ expect(() => validateTokenParameters("Token", "TOK", -1, 1n)).toThrow();
65
+ // non-integer (should be rejected by safe-integer check)
66
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
67
+ // @ts-ignore intentional wrong type for test
68
+ expect(() => validateTokenParameters("Token", "TOK", 1.5, 1n)).toThrow();
69
+ });
70
+
71
+ it("rejects decimals >= 2^53", () => {
72
+ const hugeDecimal = 2 ** 53;
73
+ expect(() =>
74
+ validateTokenParameters("Token", "TOK", hugeDecimal, 1n),
75
+ ).toThrow();
76
+ });
77
+
78
+ it("rejects maxSupply outside u128 range", () => {
79
+ expect(() => validateTokenParameters("Token", "TOK", 0, -1n)).toThrow();
80
+ expect(() =>
81
+ validateTokenParameters("Token", "TOK", 0, MAX_SUPPLY_128 + 1n),
82
+ ).toThrow();
83
+ });
84
+ });
85
+ });
@@ -0,0 +1,92 @@
1
+ import { ValidationError } from "@buildonspark/spark-sdk";
2
+
3
+ /**
4
+ * Returns true when the input is already in NFC normalisation form.
5
+ * JavaScript strings are UTF-16 encoded, so any JavaScript string is
6
+ * already valid Unicode. However, we still need to ensure canonical
7
+ * equivalence so that, for example, \u00E9 (é) and \u0065\u0301 (é)
8
+ * are treated identically. We do this by comparing the original
9
+ * string to its NFC-normalised representation.
10
+ */
11
+ function isNfcNormalized(value: string): boolean {
12
+ return value.normalize("NFC") === value;
13
+ }
14
+
15
+ const MIN_NAME_SIZE = 3; // bytes
16
+ const MAX_NAME_SIZE = 20; // bytes
17
+ const MIN_SYMBOL_SIZE = 3; // bytes
18
+ const MAX_SYMBOL_SIZE = 6; // bytes
19
+ const MAX_DECIMALS = 255; // fits into single byte
20
+ const MAXIMUM_MAX_SUPPLY = (1n << 128n) - 1n; // fits into 16 bytes (u128)
21
+
22
+ export function validateTokenParameters(
23
+ tokenName: string,
24
+ tokenTicker: string,
25
+ decimals: number,
26
+ maxSupply: bigint,
27
+ ) {
28
+ if (!isNfcNormalized(tokenName)) {
29
+ throw new ValidationError("Token name must be NFC-normalised UTF-8", {
30
+ field: "tokenName",
31
+ value: tokenName,
32
+ expected: "NFC normalised string",
33
+ });
34
+ }
35
+
36
+ if (!isNfcNormalized(tokenTicker)) {
37
+ throw new ValidationError("Token ticker must be NFC-normalised UTF-8", {
38
+ field: "tokenTicker",
39
+ value: tokenTicker,
40
+ expected: "NFC normalised string",
41
+ });
42
+ }
43
+
44
+ const nameBytes = Buffer.from(tokenName, "utf-8").length;
45
+ if (nameBytes < MIN_NAME_SIZE || nameBytes > MAX_NAME_SIZE) {
46
+ throw new ValidationError(
47
+ `Token name must be between ${MIN_NAME_SIZE} and ${MAX_NAME_SIZE} bytes`,
48
+ {
49
+ field: "tokenName",
50
+ value: tokenName,
51
+ actualLength: nameBytes,
52
+ expected: `>=${MIN_NAME_SIZE} and <=${MAX_NAME_SIZE}`,
53
+ },
54
+ );
55
+ }
56
+
57
+ const tickerBytes = Buffer.from(tokenTicker, "utf-8").length;
58
+ if (tickerBytes < MIN_SYMBOL_SIZE || tickerBytes > MAX_SYMBOL_SIZE) {
59
+ throw new ValidationError(
60
+ `Token ticker must be between ${MIN_SYMBOL_SIZE} and ${MAX_SYMBOL_SIZE} bytes`,
61
+ {
62
+ field: "tokenTicker",
63
+ value: tokenTicker,
64
+ actualLength: tickerBytes,
65
+ expected: `>=${MIN_SYMBOL_SIZE} and <=${MAX_SYMBOL_SIZE}`,
66
+ },
67
+ );
68
+ }
69
+
70
+ if (
71
+ !Number.isSafeInteger(decimals) ||
72
+ decimals < 0 ||
73
+ decimals > MAX_DECIMALS
74
+ ) {
75
+ throw new ValidationError(
76
+ `Decimals must be an integer between 0 and ${MAX_DECIMALS}`,
77
+ {
78
+ field: "decimals",
79
+ value: decimals,
80
+ expected: `>=0 and <=${MAX_DECIMALS}`,
81
+ },
82
+ );
83
+ }
84
+
85
+ if (maxSupply < 0n || maxSupply > MAXIMUM_MAX_SUPPLY) {
86
+ throw new ValidationError(`maxSupply must be between 0 and 2^128-1`, {
87
+ field: "maxSupply",
88
+ value: maxSupply.toString(),
89
+ expected: `>=0 and <=${MAXIMUM_MAX_SUPPLY.toString()}`,
90
+ });
91
+ }
92
+ }