@buildonspark/spark-sdk 0.1.44 → 0.1.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/{RequestLightningSendInput-RGel43ks.d.ts → RequestLightningSendInput-DEPd_fPO.d.ts} +1 -1
  3. package/dist/{RequestLightningSendInput-BxbCtwpV.d.cts → RequestLightningSendInput-Du0z7Om7.d.cts} +1 -1
  4. package/dist/address/index.cjs +2 -2
  5. package/dist/address/index.d.cts +2 -2
  6. package/dist/address/index.d.ts +2 -2
  7. package/dist/address/index.js +2 -2
  8. package/dist/{chunk-XX4RRWOX.js → chunk-5FUB65LX.js} +5 -7
  9. package/dist/{chunk-DAXGVPVM.js → chunk-6264CGDM.js} +4 -4
  10. package/dist/{chunk-UBT6EDVJ.js → chunk-7V6N75CC.js} +1 -1
  11. package/dist/{chunk-4Q2ZDYYU.js → chunk-BGGEVUJK.js} +1157 -208
  12. package/dist/{chunk-CIZNCBKE.js → chunk-C2S227QR.js} +648 -45
  13. package/dist/{chunk-NBCNYDWJ.js → chunk-GZ5IPPJ2.js} +2 -2
  14. package/dist/{chunk-6AFUC5M2.js → chunk-HWJWKEIU.js} +8 -2
  15. package/dist/{chunk-A2ZLMH6I.js → chunk-J2IE4Z7Y.js} +231 -299
  16. package/dist/{chunk-KEKGSH7B.js → chunk-KMUMFYFX.js} +3 -3
  17. package/dist/chunk-LHRD2WT6.js +2374 -0
  18. package/dist/{chunk-SQKXGAIR.js → chunk-NTFKFRQ2.js} +1 -1
  19. package/dist/{chunk-K4BJARWM.js → chunk-OBFKIEMP.js} +1 -1
  20. package/dist/{chunk-WPTRVD2V.js → chunk-PQN3C2MF.js} +15 -15
  21. package/dist/{chunk-EKFD62HN.js → chunk-R5PXJZQS.js} +1 -0
  22. package/dist/{chunk-HTMXTJRK.js → chunk-YUPMXTCJ.js} +4 -4
  23. package/dist/graphql/objects/index.d.cts +5 -4
  24. package/dist/graphql/objects/index.d.ts +5 -4
  25. package/dist/index-B2AwKW5J.d.cts +214 -0
  26. package/dist/index-CJDi1HWc.d.ts +214 -0
  27. package/dist/index.cjs +4025 -1314
  28. package/dist/index.d.cts +764 -19
  29. package/dist/index.d.ts +764 -19
  30. package/dist/index.js +16 -20
  31. package/dist/index.node.cjs +4025 -1318
  32. package/dist/index.node.d.cts +10 -8
  33. package/dist/index.node.d.ts +10 -8
  34. package/dist/index.node.js +16 -24
  35. package/dist/native/index.cjs +4026 -1315
  36. package/dist/native/index.d.cts +281 -85
  37. package/dist/native/index.d.ts +281 -85
  38. package/dist/native/index.js +4017 -1306
  39. package/dist/{network-CroCOQ0B.d.ts → network-BTJl-Sul.d.ts} +1 -1
  40. package/dist/{network-CfxLnaot.d.cts → network-CqgsdUF2.d.cts} +1 -1
  41. package/dist/proto/lrc20.cjs +222 -19
  42. package/dist/proto/lrc20.d.cts +1 -1
  43. package/dist/proto/lrc20.d.ts +1 -1
  44. package/dist/proto/lrc20.js +2 -2
  45. package/dist/proto/spark.cjs +1154 -205
  46. package/dist/proto/spark.d.cts +1 -1
  47. package/dist/proto/spark.d.ts +1 -1
  48. package/dist/proto/spark.js +3 -1
  49. package/dist/proto/spark_token.cjs +1377 -58
  50. package/dist/proto/spark_token.d.cts +153 -15
  51. package/dist/proto/spark_token.d.ts +153 -15
  52. package/dist/proto/spark_token.js +40 -4
  53. package/dist/{sdk-types-BeCBoozO.d.cts → sdk-types-B0SwjolI.d.cts} +1 -1
  54. package/dist/{sdk-types-CTbTdDbE.d.ts → sdk-types-Cc4l4kb1.d.ts} +1 -1
  55. package/dist/services/config.cjs +1 -1
  56. package/dist/services/config.d.cts +5 -4
  57. package/dist/services/config.d.ts +5 -4
  58. package/dist/services/config.js +6 -6
  59. package/dist/services/connection.cjs +2438 -262
  60. package/dist/services/connection.d.cts +5 -4
  61. package/dist/services/connection.d.ts +5 -4
  62. package/dist/services/connection.js +4 -4
  63. package/dist/services/index.cjs +5936 -3153
  64. package/dist/services/index.d.cts +7 -6
  65. package/dist/services/index.d.ts +7 -6
  66. package/dist/services/index.js +15 -13
  67. package/dist/services/lrc-connection.cjs +223 -20
  68. package/dist/services/lrc-connection.d.cts +5 -4
  69. package/dist/services/lrc-connection.d.ts +5 -4
  70. package/dist/services/lrc-connection.js +4 -4
  71. package/dist/services/token-transactions.cjs +840 -236
  72. package/dist/services/token-transactions.d.cts +25 -7
  73. package/dist/services/token-transactions.d.ts +25 -7
  74. package/dist/services/token-transactions.js +5 -4
  75. package/dist/services/wallet-config.cjs +2 -0
  76. package/dist/services/wallet-config.d.cts +7 -5
  77. package/dist/services/wallet-config.d.ts +7 -5
  78. package/dist/services/wallet-config.js +3 -1
  79. package/dist/signer/signer.cjs +1 -1
  80. package/dist/signer/signer.d.cts +3 -2
  81. package/dist/signer/signer.d.ts +3 -2
  82. package/dist/signer/signer.js +2 -2
  83. package/dist/{signer-D7vfYik9.d.ts → signer-BocS_J6B.d.ts} +2 -6
  84. package/dist/{signer-DaY8c60s.d.cts → signer-DKS0AJkw.d.cts} +2 -6
  85. package/dist/{spark-C4ZrsgjC.d.cts → spark-dM7EYXYQ.d.cts} +93 -15
  86. package/dist/{spark-C4ZrsgjC.d.ts → spark-dM7EYXYQ.d.ts} +93 -15
  87. package/dist/spark_bindings/native/index.cjs +183 -0
  88. package/dist/spark_bindings/native/index.d.cts +14 -0
  89. package/dist/spark_bindings/native/index.d.ts +14 -0
  90. package/dist/spark_bindings/native/index.js +141 -0
  91. package/dist/spark_bindings/wasm/index.cjs +1093 -0
  92. package/dist/spark_bindings/wasm/index.d.cts +47 -0
  93. package/dist/spark_bindings/wasm/index.d.ts +47 -0
  94. package/dist/{chunk-K4C4W5FC.js → spark_bindings/wasm/index.js} +7 -6
  95. package/dist/types/index.cjs +1156 -208
  96. package/dist/types/index.d.cts +5 -4
  97. package/dist/types/index.d.ts +5 -4
  98. package/dist/types/index.js +2 -2
  99. package/dist/types-C-Rp0Oo7.d.cts +46 -0
  100. package/dist/types-C-Rp0Oo7.d.ts +46 -0
  101. package/dist/utils/index.cjs +64 -12
  102. package/dist/utils/index.d.cts +14 -134
  103. package/dist/utils/index.d.ts +14 -134
  104. package/dist/utils/index.js +7 -7
  105. package/package.json +21 -1
  106. package/src/index.node.ts +0 -1
  107. package/src/index.ts +0 -1
  108. package/src/native/index.ts +1 -2
  109. package/src/proto/common.ts +5 -5
  110. package/src/proto/google/protobuf/descriptor.ts +34 -34
  111. package/src/proto/google/protobuf/duration.ts +2 -2
  112. package/src/proto/google/protobuf/empty.ts +2 -2
  113. package/src/proto/google/protobuf/timestamp.ts +2 -2
  114. package/src/proto/mock.ts +4 -4
  115. package/src/proto/spark.ts +1452 -185
  116. package/src/proto/spark_authn.ts +7 -7
  117. package/src/proto/spark_token.ts +1668 -105
  118. package/src/proto/validate/validate.ts +24 -24
  119. package/src/services/bolt11-spark.ts +62 -187
  120. package/src/services/coop-exit.ts +3 -0
  121. package/src/services/lrc20.ts +1 -1
  122. package/src/services/token-transactions.ts +197 -9
  123. package/src/services/transfer.ts +22 -0
  124. package/src/services/tree-creation.ts +13 -0
  125. package/src/services/wallet-config.ts +1 -1
  126. package/src/spark-wallet/spark-wallet.node.ts +0 -4
  127. package/src/spark-wallet/spark-wallet.ts +76 -108
  128. package/src/spark-wallet/types.ts +39 -3
  129. package/src/tests/bolt11-spark.test.ts +7 -15
  130. package/src/tests/integration/ssp/coop-exit.test.ts +7 -7
  131. package/src/tests/integration/swap.test.ts +453 -433
  132. package/src/tests/integration/transfer.test.ts +261 -248
  133. package/src/tests/token-identifier.test.ts +54 -0
  134. package/src/tests/tokens.test.ts +218 -23
  135. package/src/utils/token-hashing.ts +320 -44
  136. package/src/utils/token-identifier.ts +88 -0
  137. package/src/utils/token-transaction-validation.ts +350 -5
  138. package/src/utils/token-transactions.ts +12 -8
  139. package/src/utils/transaction.ts +0 -6
  140. package/dist/chunk-B3AMIGJG.js +0 -1073
  141. package/dist/index-CZmDdSts.d.cts +0 -829
  142. package/dist/index-ClIRO_3y.d.ts +0 -829
  143. package/dist/wasm-7OWFHDMS.js +0 -21
@@ -2,10 +2,10 @@ import {
2
2
  calculateAvailableTokenAmount,
3
3
  checkIfSelectedOutputsAreAvailable,
4
4
  collectResponses
5
- } from "./chunk-6AFUC5M2.js";
5
+ } from "./chunk-HWJWKEIU.js";
6
6
  import {
7
7
  decodeSparkAddress
8
- } from "./chunk-KEKGSH7B.js";
8
+ } from "./chunk-KMUMFYFX.js";
9
9
  import {
10
10
  bigIntToPrivateKey,
11
11
  recoverSecret
@@ -110,13 +110,21 @@ function hashTokenTransactionV0(tokenTransaction, partialHash = false) {
110
110
  });
111
111
  }
112
112
  hashObj2.update(issuerPubKey);
113
- if (tokenTransaction.tokenInputs.mintInput.issuerProvidedTimestamp != 0) {
113
+ let timestampValue = 0;
114
+ const mintInput = tokenTransaction.tokenInputs.mintInput;
115
+ if ("issuerProvidedTimestamp" in mintInput) {
116
+ const v0MintInput = mintInput;
117
+ if (v0MintInput.issuerProvidedTimestamp != 0) {
118
+ timestampValue = v0MintInput.issuerProvidedTimestamp;
119
+ }
120
+ } else if ("clientCreatedTimestamp" in tokenTransaction && tokenTransaction.clientCreatedTimestamp) {
121
+ timestampValue = tokenTransaction.clientCreatedTimestamp.getTime();
122
+ }
123
+ if (timestampValue != 0) {
114
124
  const timestampBytes = new Uint8Array(8);
115
125
  new DataView(timestampBytes.buffer).setBigUint64(
116
126
  0,
117
- BigInt(
118
- tokenTransaction.tokenInputs.mintInput.issuerProvidedTimestamp
119
- ),
127
+ BigInt(timestampValue),
120
128
  true
121
129
  // true for little-endian to match Go implementation
122
130
  );
@@ -125,12 +133,95 @@ function hashTokenTransactionV0(tokenTransaction, partialHash = false) {
125
133
  allHashes.push(hashObj2.digest());
126
134
  }
127
135
  }
136
+ if (tokenTransaction.tokenInputs?.$case === "createInput") {
137
+ const issuerPubKeyHashObj = sha256.create();
138
+ const createInput = tokenTransaction.tokenInputs.createInput;
139
+ if (!createInput.issuerPublicKey || createInput.issuerPublicKey.length === 0) {
140
+ throw new ValidationError("issuer public key cannot be nil or empty", {
141
+ field: "tokenInputs.createInput.issuerPublicKey"
142
+ });
143
+ }
144
+ issuerPubKeyHashObj.update(createInput.issuerPublicKey);
145
+ allHashes.push(issuerPubKeyHashObj.digest());
146
+ const tokenNameHashObj = sha256.create();
147
+ if (!createInput.tokenName || createInput.tokenName.length === 0) {
148
+ throw new ValidationError("token name cannot be empty", {
149
+ field: "tokenInputs.createInput.tokenName"
150
+ });
151
+ }
152
+ if (createInput.tokenName.length > 20) {
153
+ throw new ValidationError("token name cannot be longer than 20 bytes", {
154
+ field: "tokenInputs.createInput.tokenName",
155
+ value: createInput.tokenName,
156
+ expectedLength: 20,
157
+ actualLength: createInput.tokenName.length
158
+ });
159
+ }
160
+ const tokenNameBytes = new Uint8Array(20);
161
+ const tokenNameEncoder = new TextEncoder();
162
+ tokenNameBytes.set(tokenNameEncoder.encode(createInput.tokenName));
163
+ tokenNameHashObj.update(tokenNameBytes);
164
+ allHashes.push(tokenNameHashObj.digest());
165
+ const tokenTickerHashObj = sha256.create();
166
+ if (!createInput.tokenTicker || createInput.tokenTicker.length === 0) {
167
+ throw new ValidationError("token ticker cannot be empty", {
168
+ field: "tokenInputs.createInput.tokenTicker"
169
+ });
170
+ }
171
+ if (createInput.tokenTicker.length > 6) {
172
+ throw new ValidationError("token ticker cannot be longer than 6 bytes", {
173
+ field: "tokenInputs.createInput.tokenTicker",
174
+ value: createInput.tokenTicker,
175
+ expectedLength: 6,
176
+ actualLength: createInput.tokenTicker.length
177
+ });
178
+ }
179
+ const tokenTickerBytes = new Uint8Array(6);
180
+ const tokenTickerEncoder = new TextEncoder();
181
+ tokenTickerBytes.set(tokenTickerEncoder.encode(createInput.tokenTicker));
182
+ tokenTickerHashObj.update(tokenTickerBytes);
183
+ allHashes.push(tokenTickerHashObj.digest());
184
+ const decimalsHashObj = sha256.create();
185
+ const decimalsBytes = new Uint8Array(4);
186
+ new DataView(decimalsBytes.buffer).setUint32(
187
+ 0,
188
+ createInput.decimals,
189
+ false
190
+ );
191
+ decimalsHashObj.update(decimalsBytes);
192
+ allHashes.push(decimalsHashObj.digest());
193
+ const maxSupplyHashObj = sha256.create();
194
+ if (!createInput.maxSupply) {
195
+ throw new ValidationError("max supply cannot be nil", {
196
+ field: "tokenInputs.createInput.maxSupply"
197
+ });
198
+ }
199
+ if (createInput.maxSupply.length !== 16) {
200
+ throw new ValidationError("max supply must be exactly 16 bytes", {
201
+ field: "tokenInputs.createInput.maxSupply",
202
+ value: createInput.maxSupply,
203
+ expectedLength: 16,
204
+ actualLength: createInput.maxSupply.length
205
+ });
206
+ }
207
+ maxSupplyHashObj.update(createInput.maxSupply);
208
+ allHashes.push(maxSupplyHashObj.digest());
209
+ const isFreezableHashObj = sha256.create();
210
+ const isFreezableByte = new Uint8Array([createInput.isFreezable ? 1 : 0]);
211
+ isFreezableHashObj.update(isFreezableByte);
212
+ allHashes.push(isFreezableHashObj.digest());
213
+ const creationEntityHashObj = sha256.create();
214
+ if (!partialHash && createInput.creationEntityPublicKey) {
215
+ creationEntityHashObj.update(createInput.creationEntityPublicKey);
216
+ }
217
+ allHashes.push(creationEntityHashObj.digest());
218
+ }
128
219
  if (!tokenTransaction.tokenOutputs) {
129
220
  throw new ValidationError("token outputs cannot be null", {
130
221
  field: "tokenOutputs"
131
222
  });
132
223
  }
133
- if (tokenTransaction.tokenOutputs.length === 0) {
224
+ if (tokenTransaction.tokenOutputs.length === 0 && tokenTransaction.tokenInputs?.$case !== "createInput") {
134
225
  throw new ValidationError("token outputs cannot be empty", {
135
226
  field: "tokenOutputs"
136
227
  });
@@ -307,6 +398,26 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
307
398
  );
308
399
  versionHashObj.update(versionBytes);
309
400
  allHashes.push(versionHashObj.digest());
401
+ const typeHashObj = sha256.create();
402
+ const typeBytes = new Uint8Array(4);
403
+ let transactionType = 0;
404
+ if (tokenTransaction.tokenInputs?.$case === "mintInput") {
405
+ transactionType = 2 /* TOKEN_TRANSACTION_TYPE_MINT */;
406
+ } else if (tokenTransaction.tokenInputs?.$case === "transferInput") {
407
+ transactionType = 3 /* TOKEN_TRANSACTION_TYPE_TRANSFER */;
408
+ } else if (tokenTransaction.tokenInputs?.$case === "createInput") {
409
+ transactionType = 1 /* TOKEN_TRANSACTION_TYPE_CREATE */;
410
+ } else {
411
+ throw new ValidationError(
412
+ "token transaction must have exactly one input type",
413
+ {
414
+ field: "tokenInputs"
415
+ }
416
+ );
417
+ }
418
+ new DataView(typeBytes.buffer).setUint32(0, transactionType, false);
419
+ typeHashObj.update(typeBytes);
420
+ allHashes.push(typeHashObj.digest());
310
421
  if (tokenTransaction.tokenInputs?.$case === "transferInput") {
311
422
  if (!tokenTransaction.tokenInputs.transferInput.outputsToSpend) {
312
423
  throw new ValidationError("outputs to spend cannot be null", {
@@ -318,6 +429,15 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
318
429
  field: "tokenInputs.transferInput.outputsToSpend"
319
430
  });
320
431
  }
432
+ const outputsLenHashObj2 = sha256.create();
433
+ const outputsLenBytes2 = new Uint8Array(4);
434
+ new DataView(outputsLenBytes2.buffer).setUint32(
435
+ 0,
436
+ tokenTransaction.tokenInputs.transferInput.outputsToSpend.length,
437
+ false
438
+ );
439
+ outputsLenHashObj2.update(outputsLenBytes2);
440
+ allHashes.push(outputsLenHashObj2.digest());
321
441
  for (const [
322
442
  i,
323
443
  output
@@ -354,8 +474,7 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
354
474
  hashObj2.update(voutBytes);
355
475
  allHashes.push(hashObj2.digest());
356
476
  }
357
- }
358
- if (tokenTransaction.tokenInputs?.$case === "mintInput") {
477
+ } else if (tokenTransaction.tokenInputs?.$case === "mintInput") {
359
478
  const hashObj2 = sha256.create();
360
479
  if (tokenTransaction.tokenInputs.mintInput.issuerPublicKey) {
361
480
  const issuerPubKey = tokenTransaction.tokenInputs.mintInput.issuerPublicKey;
@@ -368,31 +487,113 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
368
487
  });
369
488
  }
370
489
  hashObj2.update(issuerPubKey);
371
- if (tokenTransaction.tokenInputs.mintInput.issuerProvidedTimestamp != 0) {
372
- const timestampBytes = new Uint8Array(8);
373
- new DataView(timestampBytes.buffer).setBigUint64(
374
- 0,
375
- BigInt(
376
- tokenTransaction.tokenInputs.mintInput.issuerProvidedTimestamp
377
- ),
378
- true
379
- // true for little-endian to match Go implementation
490
+ allHashes.push(hashObj2.digest());
491
+ const tokenIdentifierHashObj = sha256.create();
492
+ if (tokenTransaction.tokenInputs.mintInput.tokenIdentifier) {
493
+ tokenIdentifierHashObj.update(
494
+ tokenTransaction.tokenInputs.mintInput.tokenIdentifier
380
495
  );
381
- hashObj2.update(timestampBytes);
496
+ } else {
497
+ tokenIdentifierHashObj.update(new Uint8Array(32));
382
498
  }
383
- allHashes.push(hashObj2.digest());
499
+ allHashes.push(tokenIdentifierHashObj.digest());
500
+ }
501
+ } else if (tokenTransaction.tokenInputs?.$case === "createInput") {
502
+ const createInput = tokenTransaction.tokenInputs.createInput;
503
+ const issuerPubKeyHashObj = sha256.create();
504
+ if (!createInput.issuerPublicKey || createInput.issuerPublicKey.length === 0) {
505
+ throw new ValidationError("issuer public key cannot be nil or empty", {
506
+ field: "tokenInputs.createInput.issuerPublicKey"
507
+ });
508
+ }
509
+ issuerPubKeyHashObj.update(createInput.issuerPublicKey);
510
+ allHashes.push(issuerPubKeyHashObj.digest());
511
+ const tokenNameHashObj = sha256.create();
512
+ if (!createInput.tokenName || createInput.tokenName.length === 0) {
513
+ throw new ValidationError("token name cannot be empty", {
514
+ field: "tokenInputs.createInput.tokenName"
515
+ });
516
+ }
517
+ if (createInput.tokenName.length > 20) {
518
+ throw new ValidationError("token name cannot be longer than 20 bytes", {
519
+ field: "tokenInputs.createInput.tokenName",
520
+ value: createInput.tokenName,
521
+ expectedLength: 20,
522
+ actualLength: createInput.tokenName.length
523
+ });
384
524
  }
525
+ const tokenNameEncoder = new TextEncoder();
526
+ tokenNameHashObj.update(tokenNameEncoder.encode(createInput.tokenName));
527
+ allHashes.push(tokenNameHashObj.digest());
528
+ const tokenTickerHashObj = sha256.create();
529
+ if (!createInput.tokenTicker || createInput.tokenTicker.length === 0) {
530
+ throw new ValidationError("token ticker cannot be empty", {
531
+ field: "tokenInputs.createInput.tokenTicker"
532
+ });
533
+ }
534
+ if (createInput.tokenTicker.length > 6) {
535
+ throw new ValidationError("token ticker cannot be longer than 6 bytes", {
536
+ field: "tokenInputs.createInput.tokenTicker",
537
+ value: createInput.tokenTicker,
538
+ expectedLength: 6,
539
+ actualLength: createInput.tokenTicker.length
540
+ });
541
+ }
542
+ const tokenTickerEncoder = new TextEncoder();
543
+ tokenTickerHashObj.update(
544
+ tokenTickerEncoder.encode(createInput.tokenTicker)
545
+ );
546
+ allHashes.push(tokenTickerHashObj.digest());
547
+ const decimalsHashObj = sha256.create();
548
+ const decimalsBytes = new Uint8Array(4);
549
+ new DataView(decimalsBytes.buffer).setUint32(
550
+ 0,
551
+ createInput.decimals,
552
+ false
553
+ );
554
+ decimalsHashObj.update(decimalsBytes);
555
+ allHashes.push(decimalsHashObj.digest());
556
+ const maxSupplyHashObj = sha256.create();
557
+ if (!createInput.maxSupply) {
558
+ throw new ValidationError("max supply cannot be nil", {
559
+ field: "tokenInputs.createInput.maxSupply"
560
+ });
561
+ }
562
+ if (createInput.maxSupply.length !== 16) {
563
+ throw new ValidationError("max supply must be exactly 16 bytes", {
564
+ field: "tokenInputs.createInput.maxSupply",
565
+ value: createInput.maxSupply,
566
+ expectedLength: 16,
567
+ actualLength: createInput.maxSupply.length
568
+ });
569
+ }
570
+ maxSupplyHashObj.update(createInput.maxSupply);
571
+ allHashes.push(maxSupplyHashObj.digest());
572
+ const isFreezableHashObj = sha256.create();
573
+ isFreezableHashObj.update(
574
+ new Uint8Array([createInput.isFreezable ? 1 : 0])
575
+ );
576
+ allHashes.push(isFreezableHashObj.digest());
577
+ const creationEntityHashObj = sha256.create();
578
+ if (!partialHash && createInput.creationEntityPublicKey) {
579
+ creationEntityHashObj.update(createInput.creationEntityPublicKey);
580
+ }
581
+ allHashes.push(creationEntityHashObj.digest());
385
582
  }
386
583
  if (!tokenTransaction.tokenOutputs) {
387
584
  throw new ValidationError("token outputs cannot be null", {
388
585
  field: "tokenOutputs"
389
586
  });
390
587
  }
391
- if (tokenTransaction.tokenOutputs.length === 0) {
392
- throw new ValidationError("token outputs cannot be empty", {
393
- field: "tokenOutputs"
394
- });
395
- }
588
+ const outputsLenHashObj = sha256.create();
589
+ const outputsLenBytes = new Uint8Array(4);
590
+ new DataView(outputsLenBytes.buffer).setUint32(
591
+ 0,
592
+ tokenTransaction.tokenOutputs.length,
593
+ false
594
+ );
595
+ outputsLenHashObj.update(outputsLenBytes);
596
+ allHashes.push(outputsLenHashObj.digest());
396
597
  for (const [i, output] of tokenTransaction.tokenOutputs.entries()) {
397
598
  if (!output) {
398
599
  throw new ValidationError(`output cannot be null at index ${i}`, {
@@ -451,18 +652,16 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
451
652
  );
452
653
  hashObj2.update(locktimeBytes);
453
654
  }
454
- if (output.tokenPublicKey) {
455
- if (output.tokenPublicKey.length === 0) {
456
- throw new ValidationError(
457
- `token public key at index ${i} cannot be empty`,
458
- {
459
- field: `tokenOutputs[${i}].tokenPublicKey`,
460
- index: i
461
- }
462
- );
463
- }
655
+ if (!output.tokenPublicKey || output.tokenPublicKey.length === 0) {
656
+ hashObj2.update(new Uint8Array(33));
657
+ } else {
464
658
  hashObj2.update(output.tokenPublicKey);
465
659
  }
660
+ if (!output.tokenIdentifier || output.tokenIdentifier.length === 0) {
661
+ hashObj2.update(new Uint8Array(32));
662
+ } else {
663
+ hashObj2.update(output.tokenIdentifier);
664
+ }
466
665
  if (output.tokenAmount) {
467
666
  if (output.tokenAmount.length === 0) {
468
667
  throw new ValidationError(
@@ -503,6 +702,15 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
503
702
  }
504
703
  return a.length - b.length;
505
704
  });
705
+ const operatorLenHashObj = sha256.create();
706
+ const operatorLenBytes = new Uint8Array(4);
707
+ new DataView(operatorLenBytes.buffer).setUint32(
708
+ 0,
709
+ sortedPubKeys.length,
710
+ false
711
+ );
712
+ operatorLenHashObj.update(operatorLenBytes);
713
+ allHashes.push(operatorLenHashObj.digest());
506
714
  for (const [i, pubKey] of sortedPubKeys.entries()) {
507
715
  if (!pubKey) {
508
716
  throw new ValidationError(
@@ -538,7 +746,15 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
538
746
  allHashes.push(hashObj.digest());
539
747
  const clientTimestampHashObj = sha256.create();
540
748
  const clientCreatedTs = tokenTransaction.clientCreatedTimestamp;
541
- const clientUnixTime = clientCreatedTs ? Math.floor(clientCreatedTs.getTime() / 1e3) : 0;
749
+ if (!clientCreatedTs) {
750
+ throw new ValidationError(
751
+ "client created timestamp cannot be null for V1 token transactions",
752
+ {
753
+ field: "clientCreatedTimestamp"
754
+ }
755
+ );
756
+ }
757
+ const clientUnixTime = clientCreatedTs.getTime();
542
758
  const clientTimestampBytes = new Uint8Array(8);
543
759
  new DataView(clientTimestampBytes.buffer).setBigUint64(
544
760
  0,
@@ -631,7 +847,7 @@ function areByteArraysEqual(a, b) {
631
847
  function hasDuplicates(array) {
632
848
  return new Set(array).size !== array.length;
633
849
  }
634
- function validateTokenTransaction(finalTokenTransaction, partialTokenTransaction, signingOperators, keyshareInfo, expectedWithdrawBondSats, expectedWithdrawRelativeBlockLocktime, expectedThreshold) {
850
+ function validateTokenTransactionV0(finalTokenTransaction, partialTokenTransaction, signingOperators, keyshareInfo, expectedWithdrawBondSats, expectedWithdrawRelativeBlockLocktime, expectedThreshold) {
635
851
  if (finalTokenTransaction.network !== partialTokenTransaction.network) {
636
852
  throw new InternalValidationError(
637
853
  "Network mismatch in response token transaction",
@@ -788,7 +1004,251 @@ function validateTokenTransaction(finalTokenTransaction, partialTokenTransaction
788
1004
  }
789
1005
  );
790
1006
  }
1007
+ if (finalOutput.tokenPublicKey !== void 0 && partialOutput.tokenPublicKey !== void 0 && !areByteArraysEqual(
1008
+ finalOutput.tokenPublicKey,
1009
+ partialOutput.tokenPublicKey
1010
+ )) {
1011
+ throw new InternalValidationError(
1012
+ "Token public key mismatch in token output",
1013
+ {
1014
+ outputIndex: i,
1015
+ value: finalOutput.tokenPublicKey?.toString(),
1016
+ expected: partialOutput.tokenPublicKey?.toString()
1017
+ }
1018
+ );
1019
+ }
1020
+ if (!areByteArraysEqual(finalOutput.tokenAmount, partialOutput.tokenAmount)) {
1021
+ throw new InternalValidationError(
1022
+ "Token amount mismatch in token output",
1023
+ {
1024
+ outputIndex: i,
1025
+ value: finalOutput.tokenAmount.toString(),
1026
+ expected: partialOutput.tokenAmount.toString()
1027
+ }
1028
+ );
1029
+ }
1030
+ if (finalOutput.withdrawBondSats !== void 0) {
1031
+ if (finalOutput.withdrawBondSats !== expectedWithdrawBondSats) {
1032
+ throw new InternalValidationError(
1033
+ "Withdraw bond sats mismatch in token output",
1034
+ {
1035
+ outputIndex: i,
1036
+ value: finalOutput.withdrawBondSats,
1037
+ expected: expectedWithdrawBondSats
1038
+ }
1039
+ );
1040
+ }
1041
+ }
1042
+ if (finalOutput.withdrawRelativeBlockLocktime !== void 0) {
1043
+ if (finalOutput.withdrawRelativeBlockLocktime !== expectedWithdrawRelativeBlockLocktime) {
1044
+ throw new InternalValidationError(
1045
+ "Withdraw relative block locktime mismatch in token output",
1046
+ {
1047
+ outputIndex: i,
1048
+ value: finalOutput.withdrawRelativeBlockLocktime,
1049
+ expected: expectedWithdrawRelativeBlockLocktime
1050
+ }
1051
+ );
1052
+ }
1053
+ }
1054
+ if (keyshareInfo.threshold !== expectedThreshold) {
1055
+ throw new InternalValidationError(
1056
+ "Threshold mismatch: expected " + expectedThreshold + " but got " + keyshareInfo.threshold,
1057
+ {
1058
+ field: "threshold",
1059
+ value: keyshareInfo.threshold,
1060
+ expected: expectedThreshold
1061
+ }
1062
+ );
1063
+ }
1064
+ }
1065
+ if (keyshareInfo.ownerIdentifiers.length !== Object.keys(signingOperators).length) {
1066
+ throw new InternalValidationError(
1067
+ `Keyshare operator count (${keyshareInfo.ownerIdentifiers.length}) does not match signing operator count (${Object.keys(signingOperators).length})`,
1068
+ {
1069
+ keyshareInfo: keyshareInfo.ownerIdentifiers.length,
1070
+ signingOperators: Object.keys(signingOperators).length
1071
+ }
1072
+ );
1073
+ }
1074
+ if (hasDuplicates(keyshareInfo.ownerIdentifiers)) {
1075
+ throw new InternalValidationError(
1076
+ "Duplicate ownerIdentifiers found in keyshareInfo",
1077
+ {
1078
+ keyshareInfo: keyshareInfo.ownerIdentifiers
1079
+ }
1080
+ );
1081
+ }
1082
+ for (const identifier of keyshareInfo.ownerIdentifiers) {
1083
+ if (!signingOperators[identifier]) {
1084
+ throw new InternalValidationError(
1085
+ `Keyshare operator ${identifier} not found in signing operator list`,
1086
+ {
1087
+ keyshareInfo: identifier,
1088
+ signingOperators: Object.keys(signingOperators)
1089
+ }
1090
+ );
1091
+ }
1092
+ }
1093
+ }
1094
+ function validateTokenTransaction(finalTokenTransaction, partialTokenTransaction, signingOperators, keyshareInfo, expectedWithdrawBondSats, expectedWithdrawRelativeBlockLocktime, expectedThreshold) {
1095
+ if (finalTokenTransaction.network !== partialTokenTransaction.network) {
1096
+ throw new InternalValidationError(
1097
+ "Network mismatch in response token transaction",
1098
+ {
1099
+ value: finalTokenTransaction.network,
1100
+ expected: partialTokenTransaction.network
1101
+ }
1102
+ );
1103
+ }
1104
+ if (!finalTokenTransaction.tokenInputs) {
1105
+ throw new InternalValidationError(
1106
+ "Token inputs missing in final transaction",
1107
+ {
1108
+ value: finalTokenTransaction
1109
+ }
1110
+ );
1111
+ }
1112
+ if (!partialTokenTransaction.tokenInputs) {
1113
+ throw new InternalValidationError(
1114
+ "Token inputs missing in partial transaction",
1115
+ {
1116
+ value: partialTokenTransaction
1117
+ }
1118
+ );
1119
+ }
1120
+ if (finalTokenTransaction.tokenInputs.$case !== partialTokenTransaction.tokenInputs.$case) {
1121
+ throw new InternalValidationError(
1122
+ `Transaction type mismatch: final transaction has ${finalTokenTransaction.tokenInputs.$case}, partial transaction has ${partialTokenTransaction.tokenInputs.$case}`,
1123
+ {
1124
+ value: finalTokenTransaction.tokenInputs.$case,
1125
+ expected: partialTokenTransaction.tokenInputs.$case
1126
+ }
1127
+ );
1128
+ }
1129
+ if (finalTokenTransaction.sparkOperatorIdentityPublicKeys.length !== partialTokenTransaction.sparkOperatorIdentityPublicKeys.length) {
1130
+ throw new InternalValidationError(
1131
+ "Spark operator identity public keys count mismatch",
1132
+ {
1133
+ value: finalTokenTransaction.sparkOperatorIdentityPublicKeys.length,
1134
+ expected: partialTokenTransaction.sparkOperatorIdentityPublicKeys.length
1135
+ }
1136
+ );
1137
+ }
1138
+ if (partialTokenTransaction.tokenInputs.$case === "mintInput" && finalTokenTransaction.tokenInputs.$case === "mintInput") {
1139
+ const finalMintInput = finalTokenTransaction.tokenInputs.mintInput;
1140
+ const partialMintInput = partialTokenTransaction.tokenInputs.mintInput;
1141
+ if (!areByteArraysEqual(
1142
+ finalMintInput.issuerPublicKey,
1143
+ partialMintInput.issuerPublicKey
1144
+ )) {
1145
+ throw new InternalValidationError(
1146
+ "Issuer public key mismatch in mint input",
1147
+ {
1148
+ value: finalMintInput.issuerPublicKey.toString(),
1149
+ expected: partialMintInput.issuerPublicKey.toString()
1150
+ }
1151
+ );
1152
+ }
1153
+ } else if (partialTokenTransaction.tokenInputs.$case === "transferInput" && finalTokenTransaction.tokenInputs.$case === "transferInput") {
1154
+ const finalTransferInput = finalTokenTransaction.tokenInputs.transferInput;
1155
+ const partialTransferInput = partialTokenTransaction.tokenInputs.transferInput;
1156
+ if (finalTransferInput.outputsToSpend.length !== partialTransferInput.outputsToSpend.length) {
1157
+ throw new InternalValidationError(
1158
+ "Outputs to spend count mismatch in transfer input",
1159
+ {
1160
+ value: finalTransferInput.outputsToSpend.length,
1161
+ expected: partialTransferInput.outputsToSpend.length
1162
+ }
1163
+ );
1164
+ }
1165
+ for (let i = 0; i < finalTransferInput.outputsToSpend.length; i++) {
1166
+ const finalOutput = finalTransferInput.outputsToSpend[i];
1167
+ const partialOutput = partialTransferInput.outputsToSpend[i];
1168
+ if (!finalOutput) {
1169
+ throw new InternalValidationError(
1170
+ "Token output to spend missing in final transaction",
1171
+ {
1172
+ outputIndex: i,
1173
+ value: finalOutput
1174
+ }
1175
+ );
1176
+ }
1177
+ if (!partialOutput) {
1178
+ throw new InternalValidationError(
1179
+ "Token output to spend missing in partial transaction",
1180
+ {
1181
+ outputIndex: i,
1182
+ value: partialOutput
1183
+ }
1184
+ );
1185
+ }
1186
+ if (!areByteArraysEqual(
1187
+ finalOutput.prevTokenTransactionHash,
1188
+ partialOutput.prevTokenTransactionHash
1189
+ )) {
1190
+ throw new InternalValidationError(
1191
+ "Previous token transaction hash mismatch in transfer input",
1192
+ {
1193
+ outputIndex: i,
1194
+ value: finalOutput.prevTokenTransactionHash.toString(),
1195
+ expected: partialOutput.prevTokenTransactionHash.toString()
1196
+ }
1197
+ );
1198
+ }
1199
+ if (finalOutput.prevTokenTransactionVout !== partialOutput.prevTokenTransactionVout) {
1200
+ throw new InternalValidationError(
1201
+ "Previous token transaction vout mismatch in transfer input",
1202
+ {
1203
+ outputIndex: i,
1204
+ value: finalOutput.prevTokenTransactionVout,
1205
+ expected: partialOutput.prevTokenTransactionVout
1206
+ }
1207
+ );
1208
+ }
1209
+ }
1210
+ }
1211
+ if (finalTokenTransaction.tokenOutputs.length !== partialTokenTransaction.tokenOutputs.length) {
1212
+ throw new InternalValidationError("Token outputs count mismatch", {
1213
+ value: finalTokenTransaction.tokenOutputs.length,
1214
+ expected: partialTokenTransaction.tokenOutputs.length
1215
+ });
1216
+ }
1217
+ for (let i = 0; i < finalTokenTransaction.tokenOutputs.length; i++) {
1218
+ const finalOutput = finalTokenTransaction.tokenOutputs[i];
1219
+ const partialOutput = partialTokenTransaction.tokenOutputs[i];
1220
+ if (!finalOutput) {
1221
+ throw new InternalValidationError(
1222
+ "Token output missing in final transaction",
1223
+ {
1224
+ outputIndex: i,
1225
+ value: finalOutput
1226
+ }
1227
+ );
1228
+ }
1229
+ if (!partialOutput) {
1230
+ throw new InternalValidationError(
1231
+ "Token output missing in partial transaction",
1232
+ {
1233
+ outputIndex: i,
1234
+ value: partialOutput
1235
+ }
1236
+ );
1237
+ }
791
1238
  if (!areByteArraysEqual(
1239
+ finalOutput.ownerPublicKey,
1240
+ partialOutput.ownerPublicKey
1241
+ )) {
1242
+ throw new InternalValidationError(
1243
+ "Owner public key mismatch in token output",
1244
+ {
1245
+ outputIndex: i,
1246
+ value: finalOutput.ownerPublicKey.toString(),
1247
+ expected: partialOutput.ownerPublicKey.toString()
1248
+ }
1249
+ );
1250
+ }
1251
+ if (finalOutput.tokenPublicKey !== void 0 && partialOutput.tokenPublicKey !== void 0 && !areByteArraysEqual(
792
1252
  finalOutput.tokenPublicKey,
793
1253
  partialOutput.tokenPublicKey
794
1254
  )) {
@@ -874,6 +1334,12 @@ function validateTokenTransaction(finalTokenTransaction, partialTokenTransaction
874
1334
  );
875
1335
  }
876
1336
  }
1337
+ if (finalTokenTransaction.clientCreatedTimestamp.getTime() !== partialTokenTransaction.clientCreatedTimestamp.getTime()) {
1338
+ throw new InternalValidationError("Client created timestamp mismatch", {
1339
+ value: finalTokenTransaction.clientCreatedTimestamp,
1340
+ expected: partialTokenTransaction.clientCreatedTimestamp
1341
+ });
1342
+ }
877
1343
  }
878
1344
 
879
1345
  // src/services/token-transactions.ts
@@ -1063,7 +1529,8 @@ var TokenTransactionService = class {
1063
1529
  },
1064
1530
  tokenOutputs,
1065
1531
  sparkOperatorIdentityPublicKeys: this.collectOperatorIdentityPublicKeys(),
1066
- expiryTime: void 0
1532
+ expiryTime: void 0,
1533
+ clientCreatedTimestamp: /* @__PURE__ */ new Date()
1067
1534
  };
1068
1535
  }
1069
1536
  collectOperatorIdentityPublicKeys() {
@@ -1272,7 +1739,7 @@ var TokenTransactionService = class {
1272
1739
  if (!startResponse.keyshareInfo) {
1273
1740
  throw new Error("Keyshare info missing in start response");
1274
1741
  }
1275
- validateTokenTransaction(
1742
+ validateTokenTransactionV0(
1276
1743
  startResponse.finalTokenTransaction,
1277
1744
  tokenTransaction,
1278
1745
  signingOperators,
@@ -1523,7 +1990,26 @@ var TokenTransactionService = class {
1523
1990
  );
1524
1991
  }
1525
1992
  }
1526
- async fetchOwnedTokenOutputs(ownerPublicKeys, tokenPublicKeys) {
1993
+ async fetchOwnedTokenOutputs(params) {
1994
+ if (this.config.getTokenTransactionVersion() === "V0") {
1995
+ return this.fetchOwnedTokenOutputsV0(params);
1996
+ } else {
1997
+ return this.fetchOwnedTokenOutputsV1(params);
1998
+ }
1999
+ }
2000
+ async queryTokenTransactions(params) {
2001
+ if (this.config.getTokenTransactionVersion() === "V0") {
2002
+ return this.queryTokenTransactionsV0(params);
2003
+ } else {
2004
+ return this.queryTokenTransactionsV1(params);
2005
+ }
2006
+ }
2007
+ async fetchOwnedTokenOutputsV0(params) {
2008
+ const {
2009
+ ownerPublicKeys,
2010
+ issuerPublicKeys: tokenPublicKeys = [],
2011
+ tokenIdentifiers = []
2012
+ } = params;
1527
2013
  const sparkClient = await this.connectionManager.createSparkClient(
1528
2014
  this.config.getCoordinatorAddress()
1529
2015
  );
@@ -1531,6 +2017,7 @@ var TokenTransactionService = class {
1531
2017
  const result = await sparkClient.query_token_outputs({
1532
2018
  ownerPublicKeys,
1533
2019
  tokenPublicKeys,
2020
+ tokenIdentifiers,
1534
2021
  network: this.config.getNetworkProto()
1535
2022
  });
1536
2023
  return result.outputsWithPreviousTransactionData;
@@ -1538,7 +2025,7 @@ var TokenTransactionService = class {
1538
2025
  throw new NetworkError(
1539
2026
  "Failed to fetch owned token outputs",
1540
2027
  {
1541
- operation: "query_token_outputs",
2028
+ operation: "spark.query_token_outputs",
1542
2029
  errorCount: 1,
1543
2030
  errors: error instanceof Error ? error.message : String(error)
1544
2031
  },
@@ -1546,11 +2033,127 @@ var TokenTransactionService = class {
1546
2033
  );
1547
2034
  }
1548
2035
  }
1549
- async syncTokenOutputs(tokenOutputs) {
1550
- const unsortedTokenOutputs = await this.fetchOwnedTokenOutputs(
1551
- await this.config.signer.getTrackedPublicKeys(),
1552
- []
2036
+ async fetchOwnedTokenOutputsV1(params) {
2037
+ const {
2038
+ ownerPublicKeys,
2039
+ issuerPublicKeys = [],
2040
+ tokenIdentifiers = []
2041
+ } = params;
2042
+ const tokenClient = await this.connectionManager.createSparkTokenClient(
2043
+ this.config.getCoordinatorAddress()
2044
+ );
2045
+ try {
2046
+ const result = await tokenClient.query_token_outputs({
2047
+ ownerPublicKeys,
2048
+ issuerPublicKeys,
2049
+ tokenIdentifiers,
2050
+ network: this.config.getNetworkProto()
2051
+ });
2052
+ return result.outputsWithPreviousTransactionData;
2053
+ } catch (error) {
2054
+ throw new NetworkError(
2055
+ "Failed to fetch owned token outputs",
2056
+ {
2057
+ operation: "spark_token.query_token_outputs",
2058
+ errorCount: 1,
2059
+ errors: error instanceof Error ? error.message : String(error)
2060
+ },
2061
+ error
2062
+ );
2063
+ }
2064
+ }
2065
+ async queryTokenTransactionsV0(params) {
2066
+ const {
2067
+ ownerPublicKeys,
2068
+ issuerPublicKeys,
2069
+ tokenTransactionHashes,
2070
+ tokenIdentifiers,
2071
+ outputIds
2072
+ } = params;
2073
+ const sparkClient = await this.connectionManager.createSparkClient(
2074
+ this.config.getCoordinatorAddress()
1553
2075
  );
2076
+ let queryParams = {
2077
+ tokenPublicKeys: issuerPublicKeys?.map(hexToBytes),
2078
+ ownerPublicKeys: ownerPublicKeys?.map(hexToBytes),
2079
+ tokenIdentifiers: tokenIdentifiers?.map(hexToBytes),
2080
+ tokenTransactionHashes: tokenTransactionHashes?.map(hexToBytes),
2081
+ outputIds: outputIds || [],
2082
+ limit: 100,
2083
+ offset: 0
2084
+ };
2085
+ try {
2086
+ const response = await sparkClient.query_token_transactions(queryParams);
2087
+ return response.tokenTransactionsWithStatus.map((tx) => {
2088
+ const v1TokenTransaction = {
2089
+ version: 1,
2090
+ network: tx.tokenTransaction.network,
2091
+ tokenInputs: tx.tokenTransaction.tokenInputs,
2092
+ tokenOutputs: tx.tokenTransaction.tokenOutputs,
2093
+ sparkOperatorIdentityPublicKeys: tx.tokenTransaction.sparkOperatorIdentityPublicKeys,
2094
+ expiryTime: void 0,
2095
+ // V0 doesn't have expiry time
2096
+ clientCreatedTimestamp: tx.tokenTransaction?.tokenInputs?.$case === "mintInput" ? new Date(
2097
+ tx.tokenTransaction.tokenInputs.mintInput.issuerProvidedTimestamp * 1e3
2098
+ ) : /* @__PURE__ */ new Date()
2099
+ };
2100
+ return {
2101
+ tokenTransaction: v1TokenTransaction,
2102
+ status: tx.status,
2103
+ confirmationMetadata: tx.confirmationMetadata
2104
+ };
2105
+ });
2106
+ } catch (error) {
2107
+ throw new NetworkError(
2108
+ "Failed to query token transactions",
2109
+ {
2110
+ operation: "spark.query_token_transactions",
2111
+ errorCount: 1,
2112
+ errors: error instanceof Error ? error.message : String(error)
2113
+ },
2114
+ error
2115
+ );
2116
+ }
2117
+ }
2118
+ async queryTokenTransactionsV1(params) {
2119
+ const {
2120
+ ownerPublicKeys,
2121
+ issuerPublicKeys,
2122
+ tokenTransactionHashes,
2123
+ tokenIdentifiers,
2124
+ outputIds
2125
+ } = params;
2126
+ const tokenClient = await this.connectionManager.createSparkTokenClient(
2127
+ this.config.getCoordinatorAddress()
2128
+ );
2129
+ let queryParams = {
2130
+ issuerPublicKeys: issuerPublicKeys?.map(hexToBytes),
2131
+ ownerPublicKeys: ownerPublicKeys?.map(hexToBytes),
2132
+ tokenIdentifiers: tokenIdentifiers?.map(hexToBytes),
2133
+ tokenTransactionHashes: tokenTransactionHashes?.map(hexToBytes),
2134
+ outputIds: outputIds || [],
2135
+ limit: 100,
2136
+ offset: 0
2137
+ };
2138
+ try {
2139
+ const response = await tokenClient.query_token_transactions(queryParams);
2140
+ return response.tokenTransactionsWithStatus;
2141
+ } catch (error) {
2142
+ throw new NetworkError(
2143
+ "Failed to query token transactions",
2144
+ {
2145
+ operation: "spark_token.query_token_transactions",
2146
+ errorCount: 1,
2147
+ errors: error instanceof Error ? error.message : String(error)
2148
+ },
2149
+ error
2150
+ );
2151
+ }
2152
+ }
2153
+ async syncTokenOutputs(tokenOutputs) {
2154
+ const unsortedTokenOutputs = await this.fetchOwnedTokenOutputs({
2155
+ ownerPublicKeys: await this.config.signer.getTrackedPublicKeys()
2156
+ });
1554
2157
  unsortedTokenOutputs.forEach((output) => {
1555
2158
  const tokenKey = bytesToHex(output.output.tokenPublicKey);
1556
2159
  const index = output.previousTransactionVout;