@buildonspark/spark-sdk 0.1.44 → 0.1.46

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 +17 -0
  2. package/dist/{RequestLightningSendInput-BxbCtwpV.d.cts → RequestLightningSendInput-2cSh_In4.d.cts} +1 -1
  3. package/dist/{RequestLightningSendInput-RGel43ks.d.ts → RequestLightningSendInput-CN6BNg_g.d.ts} +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-EKFD62HN.js → chunk-4EMV7HHW.js} +2 -1
  9. package/dist/{chunk-4Q2ZDYYU.js → chunk-BGGEVUJK.js} +1157 -208
  10. package/dist/{chunk-CIZNCBKE.js → chunk-C2S227QR.js} +648 -45
  11. package/dist/{chunk-WPTRVD2V.js → chunk-DXR2PXJU.js} +15 -15
  12. package/dist/{chunk-NBCNYDWJ.js → chunk-HHNQ3ZHC.js} +2 -2
  13. package/dist/{chunk-DAXGVPVM.js → chunk-HSCLBJEL.js} +2 -2
  14. package/dist/{chunk-6AFUC5M2.js → chunk-HWJWKEIU.js} +8 -2
  15. package/dist/{chunk-A2ZLMH6I.js → chunk-JB64OQES.js} +259 -327
  16. package/dist/{chunk-KEKGSH7B.js → chunk-KMUMFYFX.js} +3 -3
  17. package/dist/chunk-LHRD2WT6.js +2374 -0
  18. package/dist/{chunk-HTMXTJRK.js → chunk-N5VZVCGJ.js} +4 -4
  19. package/dist/{chunk-SQKXGAIR.js → chunk-NTFKFRQ2.js} +1 -1
  20. package/dist/{chunk-K4BJARWM.js → chunk-OBFKIEMP.js} +1 -1
  21. package/dist/{chunk-UBT6EDVJ.js → chunk-OFCJFZ4I.js} +1 -1
  22. package/dist/{chunk-XX4RRWOX.js → chunk-UXDODSDT.js} +8 -10
  23. package/dist/graphql/objects/index.d.cts +5 -4
  24. package/dist/graphql/objects/index.d.ts +5 -4
  25. package/dist/index-CKL5DodV.d.cts +214 -0
  26. package/dist/index-COm59SPw.d.ts +214 -0
  27. package/dist/index.cjs +4026 -1315
  28. package/dist/index.d.cts +764 -19
  29. package/dist/index.d.ts +764 -19
  30. package/dist/index.js +23 -27
  31. package/dist/index.node.cjs +4026 -1319
  32. package/dist/index.node.d.cts +10 -8
  33. package/dist/index.node.d.ts +10 -8
  34. package/dist/index.node.js +23 -31
  35. package/dist/native/index.cjs +4027 -1316
  36. package/dist/native/index.d.cts +281 -85
  37. package/dist/native/index.d.ts +281 -85
  38. package/dist/native/index.js +4018 -1307
  39. package/dist/{network-CfxLnaot.d.cts → network-Css46DAz.d.cts} +1 -1
  40. package/dist/{network-CroCOQ0B.d.ts → network-hynb7iTZ.d.ts} +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-CTbTdDbE.d.ts → sdk-types-CKBsylfW.d.ts} +1 -1
  54. package/dist/{sdk-types-BeCBoozO.d.cts → sdk-types-Ct8xmN7l.d.cts} +1 -1
  55. package/dist/services/config.cjs +2 -2
  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 +5937 -3154
  64. package/dist/services/index.d.cts +7 -6
  65. package/dist/services/index.d.ts +7 -6
  66. package/dist/services/index.js +17 -15
  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 +3 -1
  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-BP6F__oR.d.cts} +2 -6
  84. package/dist/{signer-DaY8c60s.d.cts → signer-BVZJXcq7.d.ts} +2 -6
  85. package/dist/{spark-C4ZrsgjC.d.cts → spark-DbzGfse6.d.cts} +93 -15
  86. package/dist/{spark-C4ZrsgjC.d.ts → spark-DbzGfse6.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 +65 -13
  102. package/dist/utils/index.d.cts +14 -134
  103. package/dist/utils/index.d.ts +14 -134
  104. package/dist/utils/index.js +13 -13
  105. package/package.json +22 -2
  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 +2 -2
  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
@@ -142,6 +142,215 @@ var InternalValidationError = class extends SparkSDKError {
142
142
  }
143
143
  };
144
144
 
145
+ // src/proto/spark_token.ts
146
+ var import_wire5 = require("@bufbuild/protobuf/wire");
147
+
148
+ // src/proto/google/protobuf/timestamp.ts
149
+ var import_wire = require("@bufbuild/protobuf/wire");
150
+
151
+ // src/proto/spark.ts
152
+ var import_wire4 = require("@bufbuild/protobuf/wire");
153
+
154
+ // src/proto/common.ts
155
+ var import_wire2 = require("@bufbuild/protobuf/wire");
156
+
157
+ // src/proto/google/protobuf/empty.ts
158
+ var import_wire3 = require("@bufbuild/protobuf/wire");
159
+
160
+ // src/proto/spark.ts
161
+ function createBaseSparkAddress() {
162
+ return { identityPublicKey: new Uint8Array(0), paymentIntentFields: void 0 };
163
+ }
164
+ var SparkAddress = {
165
+ encode(message, writer = new import_wire4.BinaryWriter()) {
166
+ if (message.identityPublicKey.length !== 0) {
167
+ writer.uint32(10).bytes(message.identityPublicKey);
168
+ }
169
+ if (message.paymentIntentFields !== void 0) {
170
+ PaymentIntentFields.encode(message.paymentIntentFields, writer.uint32(18).fork()).join();
171
+ }
172
+ return writer;
173
+ },
174
+ decode(input, length) {
175
+ const reader = input instanceof import_wire4.BinaryReader ? input : new import_wire4.BinaryReader(input);
176
+ const end = length === void 0 ? reader.len : reader.pos + length;
177
+ const message = createBaseSparkAddress();
178
+ while (reader.pos < end) {
179
+ const tag = reader.uint32();
180
+ switch (tag >>> 3) {
181
+ case 1: {
182
+ if (tag !== 10) {
183
+ break;
184
+ }
185
+ message.identityPublicKey = reader.bytes();
186
+ continue;
187
+ }
188
+ case 2: {
189
+ if (tag !== 18) {
190
+ break;
191
+ }
192
+ message.paymentIntentFields = PaymentIntentFields.decode(reader, reader.uint32());
193
+ continue;
194
+ }
195
+ }
196
+ if ((tag & 7) === 4 || tag === 0) {
197
+ break;
198
+ }
199
+ reader.skip(tag & 7);
200
+ }
201
+ return message;
202
+ },
203
+ fromJSON(object) {
204
+ return {
205
+ identityPublicKey: isSet(object.identityPublicKey) ? bytesFromBase64(object.identityPublicKey) : new Uint8Array(0),
206
+ paymentIntentFields: isSet(object.paymentIntentFields) ? PaymentIntentFields.fromJSON(object.paymentIntentFields) : void 0
207
+ };
208
+ },
209
+ toJSON(message) {
210
+ const obj = {};
211
+ if (message.identityPublicKey.length !== 0) {
212
+ obj.identityPublicKey = base64FromBytes(message.identityPublicKey);
213
+ }
214
+ if (message.paymentIntentFields !== void 0) {
215
+ obj.paymentIntentFields = PaymentIntentFields.toJSON(message.paymentIntentFields);
216
+ }
217
+ return obj;
218
+ },
219
+ create(base) {
220
+ return SparkAddress.fromPartial(base ?? {});
221
+ },
222
+ fromPartial(object) {
223
+ const message = createBaseSparkAddress();
224
+ message.identityPublicKey = object.identityPublicKey ?? new Uint8Array(0);
225
+ message.paymentIntentFields = object.paymentIntentFields !== void 0 && object.paymentIntentFields !== null ? PaymentIntentFields.fromPartial(object.paymentIntentFields) : void 0;
226
+ return message;
227
+ }
228
+ };
229
+ function createBasePaymentIntentFields() {
230
+ return { id: new Uint8Array(0), assetIdentifier: void 0, assetAmount: new Uint8Array(0), memo: void 0 };
231
+ }
232
+ var PaymentIntentFields = {
233
+ encode(message, writer = new import_wire4.BinaryWriter()) {
234
+ if (message.id.length !== 0) {
235
+ writer.uint32(10).bytes(message.id);
236
+ }
237
+ if (message.assetIdentifier !== void 0) {
238
+ writer.uint32(18).bytes(message.assetIdentifier);
239
+ }
240
+ if (message.assetAmount.length !== 0) {
241
+ writer.uint32(26).bytes(message.assetAmount);
242
+ }
243
+ if (message.memo !== void 0) {
244
+ writer.uint32(34).string(message.memo);
245
+ }
246
+ return writer;
247
+ },
248
+ decode(input, length) {
249
+ const reader = input instanceof import_wire4.BinaryReader ? input : new import_wire4.BinaryReader(input);
250
+ const end = length === void 0 ? reader.len : reader.pos + length;
251
+ const message = createBasePaymentIntentFields();
252
+ while (reader.pos < end) {
253
+ const tag = reader.uint32();
254
+ switch (tag >>> 3) {
255
+ case 1: {
256
+ if (tag !== 10) {
257
+ break;
258
+ }
259
+ message.id = reader.bytes();
260
+ continue;
261
+ }
262
+ case 2: {
263
+ if (tag !== 18) {
264
+ break;
265
+ }
266
+ message.assetIdentifier = reader.bytes();
267
+ continue;
268
+ }
269
+ case 3: {
270
+ if (tag !== 26) {
271
+ break;
272
+ }
273
+ message.assetAmount = reader.bytes();
274
+ continue;
275
+ }
276
+ case 4: {
277
+ if (tag !== 34) {
278
+ break;
279
+ }
280
+ message.memo = reader.string();
281
+ continue;
282
+ }
283
+ }
284
+ if ((tag & 7) === 4 || tag === 0) {
285
+ break;
286
+ }
287
+ reader.skip(tag & 7);
288
+ }
289
+ return message;
290
+ },
291
+ fromJSON(object) {
292
+ return {
293
+ id: isSet(object.id) ? bytesFromBase64(object.id) : new Uint8Array(0),
294
+ assetIdentifier: isSet(object.assetIdentifier) ? bytesFromBase64(object.assetIdentifier) : void 0,
295
+ assetAmount: isSet(object.assetAmount) ? bytesFromBase64(object.assetAmount) : new Uint8Array(0),
296
+ memo: isSet(object.memo) ? globalThis.String(object.memo) : void 0
297
+ };
298
+ },
299
+ toJSON(message) {
300
+ const obj = {};
301
+ if (message.id.length !== 0) {
302
+ obj.id = base64FromBytes(message.id);
303
+ }
304
+ if (message.assetIdentifier !== void 0) {
305
+ obj.assetIdentifier = base64FromBytes(message.assetIdentifier);
306
+ }
307
+ if (message.assetAmount.length !== 0) {
308
+ obj.assetAmount = base64FromBytes(message.assetAmount);
309
+ }
310
+ if (message.memo !== void 0) {
311
+ obj.memo = message.memo;
312
+ }
313
+ return obj;
314
+ },
315
+ create(base) {
316
+ return PaymentIntentFields.fromPartial(base ?? {});
317
+ },
318
+ fromPartial(object) {
319
+ const message = createBasePaymentIntentFields();
320
+ message.id = object.id ?? new Uint8Array(0);
321
+ message.assetIdentifier = object.assetIdentifier ?? void 0;
322
+ message.assetAmount = object.assetAmount ?? new Uint8Array(0);
323
+ message.memo = object.memo ?? void 0;
324
+ return message;
325
+ }
326
+ };
327
+ function bytesFromBase64(b64) {
328
+ if (globalThis.Buffer) {
329
+ return Uint8Array.from(globalThis.Buffer.from(b64, "base64"));
330
+ } else {
331
+ const bin = globalThis.atob(b64);
332
+ const arr = new Uint8Array(bin.length);
333
+ for (let i = 0; i < bin.length; ++i) {
334
+ arr[i] = bin.charCodeAt(i);
335
+ }
336
+ return arr;
337
+ }
338
+ }
339
+ function base64FromBytes(arr) {
340
+ if (globalThis.Buffer) {
341
+ return globalThis.Buffer.from(arr).toString("base64");
342
+ } else {
343
+ const bin = [];
344
+ arr.forEach((byte) => {
345
+ bin.push(globalThis.String.fromCharCode(byte));
346
+ });
347
+ return globalThis.btoa(bin.join(""));
348
+ }
349
+ }
350
+ function isSet(value) {
351
+ return value !== null && value !== void 0;
352
+ }
353
+
145
354
  // src/utils/token-hashing.ts
146
355
  function hashTokenTransaction(tokenTransaction, partialHash = false) {
147
356
  switch (tokenTransaction.version) {
@@ -224,13 +433,21 @@ function hashTokenTransactionV0(tokenTransaction, partialHash = false) {
224
433
  });
225
434
  }
226
435
  hashObj2.update(issuerPubKey);
227
- if (tokenTransaction.tokenInputs.mintInput.issuerProvidedTimestamp != 0) {
436
+ let timestampValue = 0;
437
+ const mintInput = tokenTransaction.tokenInputs.mintInput;
438
+ if ("issuerProvidedTimestamp" in mintInput) {
439
+ const v0MintInput = mintInput;
440
+ if (v0MintInput.issuerProvidedTimestamp != 0) {
441
+ timestampValue = v0MintInput.issuerProvidedTimestamp;
442
+ }
443
+ } else if ("clientCreatedTimestamp" in tokenTransaction && tokenTransaction.clientCreatedTimestamp) {
444
+ timestampValue = tokenTransaction.clientCreatedTimestamp.getTime();
445
+ }
446
+ if (timestampValue != 0) {
228
447
  const timestampBytes = new Uint8Array(8);
229
448
  new DataView(timestampBytes.buffer).setBigUint64(
230
449
  0,
231
- BigInt(
232
- tokenTransaction.tokenInputs.mintInput.issuerProvidedTimestamp
233
- ),
450
+ BigInt(timestampValue),
234
451
  true
235
452
  // true for little-endian to match Go implementation
236
453
  );
@@ -239,12 +456,95 @@ function hashTokenTransactionV0(tokenTransaction, partialHash = false) {
239
456
  allHashes.push(hashObj2.digest());
240
457
  }
241
458
  }
459
+ if (tokenTransaction.tokenInputs?.$case === "createInput") {
460
+ const issuerPubKeyHashObj = import_sha2.sha256.create();
461
+ const createInput = tokenTransaction.tokenInputs.createInput;
462
+ if (!createInput.issuerPublicKey || createInput.issuerPublicKey.length === 0) {
463
+ throw new ValidationError("issuer public key cannot be nil or empty", {
464
+ field: "tokenInputs.createInput.issuerPublicKey"
465
+ });
466
+ }
467
+ issuerPubKeyHashObj.update(createInput.issuerPublicKey);
468
+ allHashes.push(issuerPubKeyHashObj.digest());
469
+ const tokenNameHashObj = import_sha2.sha256.create();
470
+ if (!createInput.tokenName || createInput.tokenName.length === 0) {
471
+ throw new ValidationError("token name cannot be empty", {
472
+ field: "tokenInputs.createInput.tokenName"
473
+ });
474
+ }
475
+ if (createInput.tokenName.length > 20) {
476
+ throw new ValidationError("token name cannot be longer than 20 bytes", {
477
+ field: "tokenInputs.createInput.tokenName",
478
+ value: createInput.tokenName,
479
+ expectedLength: 20,
480
+ actualLength: createInput.tokenName.length
481
+ });
482
+ }
483
+ const tokenNameBytes = new Uint8Array(20);
484
+ const tokenNameEncoder = new TextEncoder();
485
+ tokenNameBytes.set(tokenNameEncoder.encode(createInput.tokenName));
486
+ tokenNameHashObj.update(tokenNameBytes);
487
+ allHashes.push(tokenNameHashObj.digest());
488
+ const tokenTickerHashObj = import_sha2.sha256.create();
489
+ if (!createInput.tokenTicker || createInput.tokenTicker.length === 0) {
490
+ throw new ValidationError("token ticker cannot be empty", {
491
+ field: "tokenInputs.createInput.tokenTicker"
492
+ });
493
+ }
494
+ if (createInput.tokenTicker.length > 6) {
495
+ throw new ValidationError("token ticker cannot be longer than 6 bytes", {
496
+ field: "tokenInputs.createInput.tokenTicker",
497
+ value: createInput.tokenTicker,
498
+ expectedLength: 6,
499
+ actualLength: createInput.tokenTicker.length
500
+ });
501
+ }
502
+ const tokenTickerBytes = new Uint8Array(6);
503
+ const tokenTickerEncoder = new TextEncoder();
504
+ tokenTickerBytes.set(tokenTickerEncoder.encode(createInput.tokenTicker));
505
+ tokenTickerHashObj.update(tokenTickerBytes);
506
+ allHashes.push(tokenTickerHashObj.digest());
507
+ const decimalsHashObj = import_sha2.sha256.create();
508
+ const decimalsBytes = new Uint8Array(4);
509
+ new DataView(decimalsBytes.buffer).setUint32(
510
+ 0,
511
+ createInput.decimals,
512
+ false
513
+ );
514
+ decimalsHashObj.update(decimalsBytes);
515
+ allHashes.push(decimalsHashObj.digest());
516
+ const maxSupplyHashObj = import_sha2.sha256.create();
517
+ if (!createInput.maxSupply) {
518
+ throw new ValidationError("max supply cannot be nil", {
519
+ field: "tokenInputs.createInput.maxSupply"
520
+ });
521
+ }
522
+ if (createInput.maxSupply.length !== 16) {
523
+ throw new ValidationError("max supply must be exactly 16 bytes", {
524
+ field: "tokenInputs.createInput.maxSupply",
525
+ value: createInput.maxSupply,
526
+ expectedLength: 16,
527
+ actualLength: createInput.maxSupply.length
528
+ });
529
+ }
530
+ maxSupplyHashObj.update(createInput.maxSupply);
531
+ allHashes.push(maxSupplyHashObj.digest());
532
+ const isFreezableHashObj = import_sha2.sha256.create();
533
+ const isFreezableByte = new Uint8Array([createInput.isFreezable ? 1 : 0]);
534
+ isFreezableHashObj.update(isFreezableByte);
535
+ allHashes.push(isFreezableHashObj.digest());
536
+ const creationEntityHashObj = import_sha2.sha256.create();
537
+ if (!partialHash && createInput.creationEntityPublicKey) {
538
+ creationEntityHashObj.update(createInput.creationEntityPublicKey);
539
+ }
540
+ allHashes.push(creationEntityHashObj.digest());
541
+ }
242
542
  if (!tokenTransaction.tokenOutputs) {
243
543
  throw new ValidationError("token outputs cannot be null", {
244
544
  field: "tokenOutputs"
245
545
  });
246
546
  }
247
- if (tokenTransaction.tokenOutputs.length === 0) {
547
+ if (tokenTransaction.tokenOutputs.length === 0 && tokenTransaction.tokenInputs?.$case !== "createInput") {
248
548
  throw new ValidationError("token outputs cannot be empty", {
249
549
  field: "tokenOutputs"
250
550
  });
@@ -421,6 +721,26 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
421
721
  );
422
722
  versionHashObj.update(versionBytes);
423
723
  allHashes.push(versionHashObj.digest());
724
+ const typeHashObj = import_sha2.sha256.create();
725
+ const typeBytes = new Uint8Array(4);
726
+ let transactionType = 0;
727
+ if (tokenTransaction.tokenInputs?.$case === "mintInput") {
728
+ transactionType = 2 /* TOKEN_TRANSACTION_TYPE_MINT */;
729
+ } else if (tokenTransaction.tokenInputs?.$case === "transferInput") {
730
+ transactionType = 3 /* TOKEN_TRANSACTION_TYPE_TRANSFER */;
731
+ } else if (tokenTransaction.tokenInputs?.$case === "createInput") {
732
+ transactionType = 1 /* TOKEN_TRANSACTION_TYPE_CREATE */;
733
+ } else {
734
+ throw new ValidationError(
735
+ "token transaction must have exactly one input type",
736
+ {
737
+ field: "tokenInputs"
738
+ }
739
+ );
740
+ }
741
+ new DataView(typeBytes.buffer).setUint32(0, transactionType, false);
742
+ typeHashObj.update(typeBytes);
743
+ allHashes.push(typeHashObj.digest());
424
744
  if (tokenTransaction.tokenInputs?.$case === "transferInput") {
425
745
  if (!tokenTransaction.tokenInputs.transferInput.outputsToSpend) {
426
746
  throw new ValidationError("outputs to spend cannot be null", {
@@ -432,6 +752,15 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
432
752
  field: "tokenInputs.transferInput.outputsToSpend"
433
753
  });
434
754
  }
755
+ const outputsLenHashObj2 = import_sha2.sha256.create();
756
+ const outputsLenBytes2 = new Uint8Array(4);
757
+ new DataView(outputsLenBytes2.buffer).setUint32(
758
+ 0,
759
+ tokenTransaction.tokenInputs.transferInput.outputsToSpend.length,
760
+ false
761
+ );
762
+ outputsLenHashObj2.update(outputsLenBytes2);
763
+ allHashes.push(outputsLenHashObj2.digest());
435
764
  for (const [
436
765
  i,
437
766
  output
@@ -468,8 +797,7 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
468
797
  hashObj2.update(voutBytes);
469
798
  allHashes.push(hashObj2.digest());
470
799
  }
471
- }
472
- if (tokenTransaction.tokenInputs?.$case === "mintInput") {
800
+ } else if (tokenTransaction.tokenInputs?.$case === "mintInput") {
473
801
  const hashObj2 = import_sha2.sha256.create();
474
802
  if (tokenTransaction.tokenInputs.mintInput.issuerPublicKey) {
475
803
  const issuerPubKey = tokenTransaction.tokenInputs.mintInput.issuerPublicKey;
@@ -482,31 +810,113 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
482
810
  });
483
811
  }
484
812
  hashObj2.update(issuerPubKey);
485
- if (tokenTransaction.tokenInputs.mintInput.issuerProvidedTimestamp != 0) {
486
- const timestampBytes = new Uint8Array(8);
487
- new DataView(timestampBytes.buffer).setBigUint64(
488
- 0,
489
- BigInt(
490
- tokenTransaction.tokenInputs.mintInput.issuerProvidedTimestamp
491
- ),
492
- true
493
- // true for little-endian to match Go implementation
813
+ allHashes.push(hashObj2.digest());
814
+ const tokenIdentifierHashObj = import_sha2.sha256.create();
815
+ if (tokenTransaction.tokenInputs.mintInput.tokenIdentifier) {
816
+ tokenIdentifierHashObj.update(
817
+ tokenTransaction.tokenInputs.mintInput.tokenIdentifier
494
818
  );
495
- hashObj2.update(timestampBytes);
819
+ } else {
820
+ tokenIdentifierHashObj.update(new Uint8Array(32));
496
821
  }
497
- allHashes.push(hashObj2.digest());
822
+ allHashes.push(tokenIdentifierHashObj.digest());
823
+ }
824
+ } else if (tokenTransaction.tokenInputs?.$case === "createInput") {
825
+ const createInput = tokenTransaction.tokenInputs.createInput;
826
+ const issuerPubKeyHashObj = import_sha2.sha256.create();
827
+ if (!createInput.issuerPublicKey || createInput.issuerPublicKey.length === 0) {
828
+ throw new ValidationError("issuer public key cannot be nil or empty", {
829
+ field: "tokenInputs.createInput.issuerPublicKey"
830
+ });
831
+ }
832
+ issuerPubKeyHashObj.update(createInput.issuerPublicKey);
833
+ allHashes.push(issuerPubKeyHashObj.digest());
834
+ const tokenNameHashObj = import_sha2.sha256.create();
835
+ if (!createInput.tokenName || createInput.tokenName.length === 0) {
836
+ throw new ValidationError("token name cannot be empty", {
837
+ field: "tokenInputs.createInput.tokenName"
838
+ });
839
+ }
840
+ if (createInput.tokenName.length > 20) {
841
+ throw new ValidationError("token name cannot be longer than 20 bytes", {
842
+ field: "tokenInputs.createInput.tokenName",
843
+ value: createInput.tokenName,
844
+ expectedLength: 20,
845
+ actualLength: createInput.tokenName.length
846
+ });
847
+ }
848
+ const tokenNameEncoder = new TextEncoder();
849
+ tokenNameHashObj.update(tokenNameEncoder.encode(createInput.tokenName));
850
+ allHashes.push(tokenNameHashObj.digest());
851
+ const tokenTickerHashObj = import_sha2.sha256.create();
852
+ if (!createInput.tokenTicker || createInput.tokenTicker.length === 0) {
853
+ throw new ValidationError("token ticker cannot be empty", {
854
+ field: "tokenInputs.createInput.tokenTicker"
855
+ });
856
+ }
857
+ if (createInput.tokenTicker.length > 6) {
858
+ throw new ValidationError("token ticker cannot be longer than 6 bytes", {
859
+ field: "tokenInputs.createInput.tokenTicker",
860
+ value: createInput.tokenTicker,
861
+ expectedLength: 6,
862
+ actualLength: createInput.tokenTicker.length
863
+ });
864
+ }
865
+ const tokenTickerEncoder = new TextEncoder();
866
+ tokenTickerHashObj.update(
867
+ tokenTickerEncoder.encode(createInput.tokenTicker)
868
+ );
869
+ allHashes.push(tokenTickerHashObj.digest());
870
+ const decimalsHashObj = import_sha2.sha256.create();
871
+ const decimalsBytes = new Uint8Array(4);
872
+ new DataView(decimalsBytes.buffer).setUint32(
873
+ 0,
874
+ createInput.decimals,
875
+ false
876
+ );
877
+ decimalsHashObj.update(decimalsBytes);
878
+ allHashes.push(decimalsHashObj.digest());
879
+ const maxSupplyHashObj = import_sha2.sha256.create();
880
+ if (!createInput.maxSupply) {
881
+ throw new ValidationError("max supply cannot be nil", {
882
+ field: "tokenInputs.createInput.maxSupply"
883
+ });
498
884
  }
885
+ if (createInput.maxSupply.length !== 16) {
886
+ throw new ValidationError("max supply must be exactly 16 bytes", {
887
+ field: "tokenInputs.createInput.maxSupply",
888
+ value: createInput.maxSupply,
889
+ expectedLength: 16,
890
+ actualLength: createInput.maxSupply.length
891
+ });
892
+ }
893
+ maxSupplyHashObj.update(createInput.maxSupply);
894
+ allHashes.push(maxSupplyHashObj.digest());
895
+ const isFreezableHashObj = import_sha2.sha256.create();
896
+ isFreezableHashObj.update(
897
+ new Uint8Array([createInput.isFreezable ? 1 : 0])
898
+ );
899
+ allHashes.push(isFreezableHashObj.digest());
900
+ const creationEntityHashObj = import_sha2.sha256.create();
901
+ if (!partialHash && createInput.creationEntityPublicKey) {
902
+ creationEntityHashObj.update(createInput.creationEntityPublicKey);
903
+ }
904
+ allHashes.push(creationEntityHashObj.digest());
499
905
  }
500
906
  if (!tokenTransaction.tokenOutputs) {
501
907
  throw new ValidationError("token outputs cannot be null", {
502
908
  field: "tokenOutputs"
503
909
  });
504
910
  }
505
- if (tokenTransaction.tokenOutputs.length === 0) {
506
- throw new ValidationError("token outputs cannot be empty", {
507
- field: "tokenOutputs"
508
- });
509
- }
911
+ const outputsLenHashObj = import_sha2.sha256.create();
912
+ const outputsLenBytes = new Uint8Array(4);
913
+ new DataView(outputsLenBytes.buffer).setUint32(
914
+ 0,
915
+ tokenTransaction.tokenOutputs.length,
916
+ false
917
+ );
918
+ outputsLenHashObj.update(outputsLenBytes);
919
+ allHashes.push(outputsLenHashObj.digest());
510
920
  for (const [i, output] of tokenTransaction.tokenOutputs.entries()) {
511
921
  if (!output) {
512
922
  throw new ValidationError(`output cannot be null at index ${i}`, {
@@ -565,18 +975,16 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
565
975
  );
566
976
  hashObj2.update(locktimeBytes);
567
977
  }
568
- if (output.tokenPublicKey) {
569
- if (output.tokenPublicKey.length === 0) {
570
- throw new ValidationError(
571
- `token public key at index ${i} cannot be empty`,
572
- {
573
- field: `tokenOutputs[${i}].tokenPublicKey`,
574
- index: i
575
- }
576
- );
577
- }
978
+ if (!output.tokenPublicKey || output.tokenPublicKey.length === 0) {
979
+ hashObj2.update(new Uint8Array(33));
980
+ } else {
578
981
  hashObj2.update(output.tokenPublicKey);
579
982
  }
983
+ if (!output.tokenIdentifier || output.tokenIdentifier.length === 0) {
984
+ hashObj2.update(new Uint8Array(32));
985
+ } else {
986
+ hashObj2.update(output.tokenIdentifier);
987
+ }
580
988
  if (output.tokenAmount) {
581
989
  if (output.tokenAmount.length === 0) {
582
990
  throw new ValidationError(
@@ -617,6 +1025,15 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
617
1025
  }
618
1026
  return a.length - b.length;
619
1027
  });
1028
+ const operatorLenHashObj = import_sha2.sha256.create();
1029
+ const operatorLenBytes = new Uint8Array(4);
1030
+ new DataView(operatorLenBytes.buffer).setUint32(
1031
+ 0,
1032
+ sortedPubKeys.length,
1033
+ false
1034
+ );
1035
+ operatorLenHashObj.update(operatorLenBytes);
1036
+ allHashes.push(operatorLenHashObj.digest());
620
1037
  for (const [i, pubKey] of sortedPubKeys.entries()) {
621
1038
  if (!pubKey) {
622
1039
  throw new ValidationError(
@@ -652,7 +1069,15 @@ function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
652
1069
  allHashes.push(hashObj.digest());
653
1070
  const clientTimestampHashObj = import_sha2.sha256.create();
654
1071
  const clientCreatedTs = tokenTransaction.clientCreatedTimestamp;
655
- const clientUnixTime = clientCreatedTs ? Math.floor(clientCreatedTs.getTime() / 1e3) : 0;
1072
+ if (!clientCreatedTs) {
1073
+ throw new ValidationError(
1074
+ "client created timestamp cannot be null for V1 token transactions",
1075
+ {
1076
+ field: "clientCreatedTimestamp"
1077
+ }
1078
+ );
1079
+ }
1080
+ const clientUnixTime = clientCreatedTs.getTime();
656
1081
  const clientTimestampBytes = new Uint8Array(8);
657
1082
  new DataView(clientTimestampBytes.buffer).setBigUint64(
658
1083
  0,
@@ -773,7 +1198,7 @@ function areByteArraysEqual(a, b) {
773
1198
  function hasDuplicates(array) {
774
1199
  return new Set(array).size !== array.length;
775
1200
  }
776
- function validateTokenTransaction(finalTokenTransaction, partialTokenTransaction, signingOperators, keyshareInfo, expectedWithdrawBondSats, expectedWithdrawRelativeBlockLocktime, expectedThreshold) {
1201
+ function validateTokenTransactionV0(finalTokenTransaction, partialTokenTransaction, signingOperators, keyshareInfo, expectedWithdrawBondSats, expectedWithdrawRelativeBlockLocktime, expectedThreshold) {
777
1202
  if (finalTokenTransaction.network !== partialTokenTransaction.network) {
778
1203
  throw new InternalValidationError(
779
1204
  "Network mismatch in response token transaction",
@@ -930,7 +1355,7 @@ function validateTokenTransaction(finalTokenTransaction, partialTokenTransaction
930
1355
  }
931
1356
  );
932
1357
  }
933
- if (!areByteArraysEqual(
1358
+ if (finalOutput.tokenPublicKey !== void 0 && partialOutput.tokenPublicKey !== void 0 && !areByteArraysEqual(
934
1359
  finalOutput.tokenPublicKey,
935
1360
  partialOutput.tokenPublicKey
936
1361
  )) {
@@ -938,8 +1363,8 @@ function validateTokenTransaction(finalTokenTransaction, partialTokenTransaction
938
1363
  "Token public key mismatch in token output",
939
1364
  {
940
1365
  outputIndex: i,
941
- value: finalOutput.tokenPublicKey.toString(),
942
- expected: partialOutput.tokenPublicKey.toString()
1366
+ value: finalOutput.tokenPublicKey?.toString(),
1367
+ expected: partialOutput.tokenPublicKey?.toString()
943
1368
  }
944
1369
  );
945
1370
  }
@@ -1017,222 +1442,264 @@ function validateTokenTransaction(finalTokenTransaction, partialTokenTransaction
1017
1442
  }
1018
1443
  }
1019
1444
  }
1020
-
1021
- // src/services/token-transactions.ts
1022
- var import_utils7 = require("@noble/hashes/utils");
1023
-
1024
- // src/address/address.ts
1025
- var import_secp256k1 = require("@noble/curves/secp256k1");
1026
- var import_utils3 = require("@noble/hashes/utils");
1027
- var import_base2 = require("@scure/base");
1028
-
1029
- // src/proto/spark.ts
1030
- var import_wire4 = require("@bufbuild/protobuf/wire");
1031
-
1032
- // src/proto/common.ts
1033
- var import_wire = require("@bufbuild/protobuf/wire");
1034
-
1035
- // src/proto/google/protobuf/empty.ts
1036
- var import_wire2 = require("@bufbuild/protobuf/wire");
1037
-
1038
- // src/proto/google/protobuf/timestamp.ts
1039
- var import_wire3 = require("@bufbuild/protobuf/wire");
1040
-
1041
- // src/proto/spark.ts
1042
- function createBaseSparkAddress() {
1043
- return { identityPublicKey: new Uint8Array(0), paymentIntentFields: void 0 };
1044
- }
1045
- var SparkAddress = {
1046
- encode(message, writer = new import_wire4.BinaryWriter()) {
1047
- if (message.identityPublicKey.length !== 0) {
1048
- writer.uint32(10).bytes(message.identityPublicKey);
1445
+ function validateTokenTransaction(finalTokenTransaction, partialTokenTransaction, signingOperators, keyshareInfo, expectedWithdrawBondSats, expectedWithdrawRelativeBlockLocktime, expectedThreshold) {
1446
+ if (finalTokenTransaction.network !== partialTokenTransaction.network) {
1447
+ throw new InternalValidationError(
1448
+ "Network mismatch in response token transaction",
1449
+ {
1450
+ value: finalTokenTransaction.network,
1451
+ expected: partialTokenTransaction.network
1452
+ }
1453
+ );
1454
+ }
1455
+ if (!finalTokenTransaction.tokenInputs) {
1456
+ throw new InternalValidationError(
1457
+ "Token inputs missing in final transaction",
1458
+ {
1459
+ value: finalTokenTransaction
1460
+ }
1461
+ );
1462
+ }
1463
+ if (!partialTokenTransaction.tokenInputs) {
1464
+ throw new InternalValidationError(
1465
+ "Token inputs missing in partial transaction",
1466
+ {
1467
+ value: partialTokenTransaction
1468
+ }
1469
+ );
1470
+ }
1471
+ if (finalTokenTransaction.tokenInputs.$case !== partialTokenTransaction.tokenInputs.$case) {
1472
+ throw new InternalValidationError(
1473
+ `Transaction type mismatch: final transaction has ${finalTokenTransaction.tokenInputs.$case}, partial transaction has ${partialTokenTransaction.tokenInputs.$case}`,
1474
+ {
1475
+ value: finalTokenTransaction.tokenInputs.$case,
1476
+ expected: partialTokenTransaction.tokenInputs.$case
1477
+ }
1478
+ );
1479
+ }
1480
+ if (finalTokenTransaction.sparkOperatorIdentityPublicKeys.length !== partialTokenTransaction.sparkOperatorIdentityPublicKeys.length) {
1481
+ throw new InternalValidationError(
1482
+ "Spark operator identity public keys count mismatch",
1483
+ {
1484
+ value: finalTokenTransaction.sparkOperatorIdentityPublicKeys.length,
1485
+ expected: partialTokenTransaction.sparkOperatorIdentityPublicKeys.length
1486
+ }
1487
+ );
1488
+ }
1489
+ if (partialTokenTransaction.tokenInputs.$case === "mintInput" && finalTokenTransaction.tokenInputs.$case === "mintInput") {
1490
+ const finalMintInput = finalTokenTransaction.tokenInputs.mintInput;
1491
+ const partialMintInput = partialTokenTransaction.tokenInputs.mintInput;
1492
+ if (!areByteArraysEqual(
1493
+ finalMintInput.issuerPublicKey,
1494
+ partialMintInput.issuerPublicKey
1495
+ )) {
1496
+ throw new InternalValidationError(
1497
+ "Issuer public key mismatch in mint input",
1498
+ {
1499
+ value: finalMintInput.issuerPublicKey.toString(),
1500
+ expected: partialMintInput.issuerPublicKey.toString()
1501
+ }
1502
+ );
1049
1503
  }
1050
- if (message.paymentIntentFields !== void 0) {
1051
- PaymentIntentFields.encode(message.paymentIntentFields, writer.uint32(18).fork()).join();
1504
+ } else if (partialTokenTransaction.tokenInputs.$case === "transferInput" && finalTokenTransaction.tokenInputs.$case === "transferInput") {
1505
+ const finalTransferInput = finalTokenTransaction.tokenInputs.transferInput;
1506
+ const partialTransferInput = partialTokenTransaction.tokenInputs.transferInput;
1507
+ if (finalTransferInput.outputsToSpend.length !== partialTransferInput.outputsToSpend.length) {
1508
+ throw new InternalValidationError(
1509
+ "Outputs to spend count mismatch in transfer input",
1510
+ {
1511
+ value: finalTransferInput.outputsToSpend.length,
1512
+ expected: partialTransferInput.outputsToSpend.length
1513
+ }
1514
+ );
1052
1515
  }
1053
- return writer;
1054
- },
1055
- decode(input, length) {
1056
- const reader = input instanceof import_wire4.BinaryReader ? input : new import_wire4.BinaryReader(input);
1057
- let end = length === void 0 ? reader.len : reader.pos + length;
1058
- const message = createBaseSparkAddress();
1059
- while (reader.pos < end) {
1060
- const tag = reader.uint32();
1061
- switch (tag >>> 3) {
1062
- case 1: {
1063
- if (tag !== 10) {
1064
- break;
1516
+ for (let i = 0; i < finalTransferInput.outputsToSpend.length; i++) {
1517
+ const finalOutput = finalTransferInput.outputsToSpend[i];
1518
+ const partialOutput = partialTransferInput.outputsToSpend[i];
1519
+ if (!finalOutput) {
1520
+ throw new InternalValidationError(
1521
+ "Token output to spend missing in final transaction",
1522
+ {
1523
+ outputIndex: i,
1524
+ value: finalOutput
1065
1525
  }
1066
- message.identityPublicKey = reader.bytes();
1067
- continue;
1068
- }
1069
- case 2: {
1070
- if (tag !== 18) {
1071
- break;
1526
+ );
1527
+ }
1528
+ if (!partialOutput) {
1529
+ throw new InternalValidationError(
1530
+ "Token output to spend missing in partial transaction",
1531
+ {
1532
+ outputIndex: i,
1533
+ value: partialOutput
1072
1534
  }
1073
- message.paymentIntentFields = PaymentIntentFields.decode(reader, reader.uint32());
1074
- continue;
1075
- }
1535
+ );
1076
1536
  }
1077
- if ((tag & 7) === 4 || tag === 0) {
1078
- break;
1537
+ if (!areByteArraysEqual(
1538
+ finalOutput.prevTokenTransactionHash,
1539
+ partialOutput.prevTokenTransactionHash
1540
+ )) {
1541
+ throw new InternalValidationError(
1542
+ "Previous token transaction hash mismatch in transfer input",
1543
+ {
1544
+ outputIndex: i,
1545
+ value: finalOutput.prevTokenTransactionHash.toString(),
1546
+ expected: partialOutput.prevTokenTransactionHash.toString()
1547
+ }
1548
+ );
1549
+ }
1550
+ if (finalOutput.prevTokenTransactionVout !== partialOutput.prevTokenTransactionVout) {
1551
+ throw new InternalValidationError(
1552
+ "Previous token transaction vout mismatch in transfer input",
1553
+ {
1554
+ outputIndex: i,
1555
+ value: finalOutput.prevTokenTransactionVout,
1556
+ expected: partialOutput.prevTokenTransactionVout
1557
+ }
1558
+ );
1079
1559
  }
1080
- reader.skip(tag & 7);
1081
- }
1082
- return message;
1083
- },
1084
- fromJSON(object) {
1085
- return {
1086
- identityPublicKey: isSet(object.identityPublicKey) ? bytesFromBase64(object.identityPublicKey) : new Uint8Array(0),
1087
- paymentIntentFields: isSet(object.paymentIntentFields) ? PaymentIntentFields.fromJSON(object.paymentIntentFields) : void 0
1088
- };
1089
- },
1090
- toJSON(message) {
1091
- const obj = {};
1092
- if (message.identityPublicKey.length !== 0) {
1093
- obj.identityPublicKey = base64FromBytes(message.identityPublicKey);
1094
- }
1095
- if (message.paymentIntentFields !== void 0) {
1096
- obj.paymentIntentFields = PaymentIntentFields.toJSON(message.paymentIntentFields);
1097
1560
  }
1098
- return obj;
1099
- },
1100
- create(base) {
1101
- return SparkAddress.fromPartial(base ?? {});
1102
- },
1103
- fromPartial(object) {
1104
- const message = createBaseSparkAddress();
1105
- message.identityPublicKey = object.identityPublicKey ?? new Uint8Array(0);
1106
- message.paymentIntentFields = object.paymentIntentFields !== void 0 && object.paymentIntentFields !== null ? PaymentIntentFields.fromPartial(object.paymentIntentFields) : void 0;
1107
- return message;
1108
1561
  }
1109
- };
1110
- function createBasePaymentIntentFields() {
1111
- return { id: new Uint8Array(0), assetIdentifier: void 0, assetAmount: new Uint8Array(0), memo: void 0 };
1112
- }
1113
- var PaymentIntentFields = {
1114
- encode(message, writer = new import_wire4.BinaryWriter()) {
1115
- if (message.id.length !== 0) {
1116
- writer.uint32(10).bytes(message.id);
1117
- }
1118
- if (message.assetIdentifier !== void 0) {
1119
- writer.uint32(18).bytes(message.assetIdentifier);
1120
- }
1121
- if (message.assetAmount.length !== 0) {
1122
- writer.uint32(26).bytes(message.assetAmount);
1562
+ if (finalTokenTransaction.tokenOutputs.length !== partialTokenTransaction.tokenOutputs.length) {
1563
+ throw new InternalValidationError("Token outputs count mismatch", {
1564
+ value: finalTokenTransaction.tokenOutputs.length,
1565
+ expected: partialTokenTransaction.tokenOutputs.length
1566
+ });
1567
+ }
1568
+ for (let i = 0; i < finalTokenTransaction.tokenOutputs.length; i++) {
1569
+ const finalOutput = finalTokenTransaction.tokenOutputs[i];
1570
+ const partialOutput = partialTokenTransaction.tokenOutputs[i];
1571
+ if (!finalOutput) {
1572
+ throw new InternalValidationError(
1573
+ "Token output missing in final transaction",
1574
+ {
1575
+ outputIndex: i,
1576
+ value: finalOutput
1577
+ }
1578
+ );
1123
1579
  }
1124
- if (message.memo !== void 0) {
1125
- writer.uint32(34).string(message.memo);
1580
+ if (!partialOutput) {
1581
+ throw new InternalValidationError(
1582
+ "Token output missing in partial transaction",
1583
+ {
1584
+ outputIndex: i,
1585
+ value: partialOutput
1586
+ }
1587
+ );
1126
1588
  }
1127
- return writer;
1128
- },
1129
- decode(input, length) {
1130
- const reader = input instanceof import_wire4.BinaryReader ? input : new import_wire4.BinaryReader(input);
1131
- let end = length === void 0 ? reader.len : reader.pos + length;
1132
- const message = createBasePaymentIntentFields();
1133
- while (reader.pos < end) {
1134
- const tag = reader.uint32();
1135
- switch (tag >>> 3) {
1136
- case 1: {
1137
- if (tag !== 10) {
1138
- break;
1139
- }
1140
- message.id = reader.bytes();
1141
- continue;
1589
+ if (!areByteArraysEqual(
1590
+ finalOutput.ownerPublicKey,
1591
+ partialOutput.ownerPublicKey
1592
+ )) {
1593
+ throw new InternalValidationError(
1594
+ "Owner public key mismatch in token output",
1595
+ {
1596
+ outputIndex: i,
1597
+ value: finalOutput.ownerPublicKey.toString(),
1598
+ expected: partialOutput.ownerPublicKey.toString()
1142
1599
  }
1143
- case 2: {
1144
- if (tag !== 18) {
1145
- break;
1146
- }
1147
- message.assetIdentifier = reader.bytes();
1148
- continue;
1600
+ );
1601
+ }
1602
+ if (finalOutput.tokenPublicKey !== void 0 && partialOutput.tokenPublicKey !== void 0 && !areByteArraysEqual(
1603
+ finalOutput.tokenPublicKey,
1604
+ partialOutput.tokenPublicKey
1605
+ )) {
1606
+ throw new InternalValidationError(
1607
+ "Token public key mismatch in token output",
1608
+ {
1609
+ outputIndex: i,
1610
+ value: finalOutput.tokenPublicKey.toString(),
1611
+ expected: partialOutput.tokenPublicKey.toString()
1149
1612
  }
1150
- case 3: {
1151
- if (tag !== 26) {
1152
- break;
1153
- }
1154
- message.assetAmount = reader.bytes();
1155
- continue;
1613
+ );
1614
+ }
1615
+ if (!areByteArraysEqual(finalOutput.tokenAmount, partialOutput.tokenAmount)) {
1616
+ throw new InternalValidationError(
1617
+ "Token amount mismatch in token output",
1618
+ {
1619
+ outputIndex: i,
1620
+ value: finalOutput.tokenAmount.toString(),
1621
+ expected: partialOutput.tokenAmount.toString()
1156
1622
  }
1157
- case 4: {
1158
- if (tag !== 34) {
1159
- break;
1623
+ );
1624
+ }
1625
+ if (finalOutput.withdrawBondSats !== void 0) {
1626
+ if (finalOutput.withdrawBondSats !== expectedWithdrawBondSats) {
1627
+ throw new InternalValidationError(
1628
+ "Withdraw bond sats mismatch in token output",
1629
+ {
1630
+ outputIndex: i,
1631
+ value: finalOutput.withdrawBondSats,
1632
+ expected: expectedWithdrawBondSats
1160
1633
  }
1161
- message.memo = reader.string();
1162
- continue;
1163
- }
1164
- }
1165
- if ((tag & 7) === 4 || tag === 0) {
1166
- break;
1634
+ );
1167
1635
  }
1168
- reader.skip(tag & 7);
1169
- }
1170
- return message;
1171
- },
1172
- fromJSON(object) {
1173
- return {
1174
- id: isSet(object.id) ? bytesFromBase64(object.id) : new Uint8Array(0),
1175
- assetIdentifier: isSet(object.assetIdentifier) ? bytesFromBase64(object.assetIdentifier) : void 0,
1176
- assetAmount: isSet(object.assetAmount) ? bytesFromBase64(object.assetAmount) : new Uint8Array(0),
1177
- memo: isSet(object.memo) ? globalThis.String(object.memo) : void 0
1178
- };
1179
- },
1180
- toJSON(message) {
1181
- const obj = {};
1182
- if (message.id.length !== 0) {
1183
- obj.id = base64FromBytes(message.id);
1184
- }
1185
- if (message.assetIdentifier !== void 0) {
1186
- obj.assetIdentifier = base64FromBytes(message.assetIdentifier);
1187
1636
  }
1188
- if (message.assetAmount.length !== 0) {
1189
- obj.assetAmount = base64FromBytes(message.assetAmount);
1637
+ if (finalOutput.withdrawRelativeBlockLocktime !== void 0) {
1638
+ if (finalOutput.withdrawRelativeBlockLocktime !== expectedWithdrawRelativeBlockLocktime) {
1639
+ throw new InternalValidationError(
1640
+ "Withdraw relative block locktime mismatch in token output",
1641
+ {
1642
+ outputIndex: i,
1643
+ value: finalOutput.withdrawRelativeBlockLocktime,
1644
+ expected: expectedWithdrawRelativeBlockLocktime
1645
+ }
1646
+ );
1647
+ }
1190
1648
  }
1191
- if (message.memo !== void 0) {
1192
- obj.memo = message.memo;
1649
+ if (keyshareInfo.threshold !== expectedThreshold) {
1650
+ throw new InternalValidationError(
1651
+ "Threshold mismatch: expected " + expectedThreshold + " but got " + keyshareInfo.threshold,
1652
+ {
1653
+ field: "threshold",
1654
+ value: keyshareInfo.threshold,
1655
+ expected: expectedThreshold
1656
+ }
1657
+ );
1193
1658
  }
1194
- return obj;
1195
- },
1196
- create(base) {
1197
- return PaymentIntentFields.fromPartial(base ?? {});
1198
- },
1199
- fromPartial(object) {
1200
- const message = createBasePaymentIntentFields();
1201
- message.id = object.id ?? new Uint8Array(0);
1202
- message.assetIdentifier = object.assetIdentifier ?? void 0;
1203
- message.assetAmount = object.assetAmount ?? new Uint8Array(0);
1204
- message.memo = object.memo ?? void 0;
1205
- return message;
1206
1659
  }
1207
- };
1208
- function bytesFromBase64(b64) {
1209
- if (globalThis.Buffer) {
1210
- return Uint8Array.from(globalThis.Buffer.from(b64, "base64"));
1211
- } else {
1212
- const bin = globalThis.atob(b64);
1213
- const arr = new Uint8Array(bin.length);
1214
- for (let i = 0; i < bin.length; ++i) {
1215
- arr[i] = bin.charCodeAt(i);
1660
+ if (keyshareInfo.ownerIdentifiers.length !== Object.keys(signingOperators).length) {
1661
+ throw new InternalValidationError(
1662
+ `Keyshare operator count (${keyshareInfo.ownerIdentifiers.length}) does not match signing operator count (${Object.keys(signingOperators).length})`,
1663
+ {
1664
+ keyshareInfo: keyshareInfo.ownerIdentifiers.length,
1665
+ signingOperators: Object.keys(signingOperators).length
1666
+ }
1667
+ );
1668
+ }
1669
+ if (hasDuplicates(keyshareInfo.ownerIdentifiers)) {
1670
+ throw new InternalValidationError(
1671
+ "Duplicate ownerIdentifiers found in keyshareInfo",
1672
+ {
1673
+ keyshareInfo: keyshareInfo.ownerIdentifiers
1674
+ }
1675
+ );
1676
+ }
1677
+ for (const identifier of keyshareInfo.ownerIdentifiers) {
1678
+ if (!signingOperators[identifier]) {
1679
+ throw new InternalValidationError(
1680
+ `Keyshare operator ${identifier} not found in signing operator list`,
1681
+ {
1682
+ keyshareInfo: identifier,
1683
+ signingOperators: Object.keys(signingOperators)
1684
+ }
1685
+ );
1216
1686
  }
1217
- return arr;
1218
1687
  }
1219
- }
1220
- function base64FromBytes(arr) {
1221
- if (globalThis.Buffer) {
1222
- return globalThis.Buffer.from(arr).toString("base64");
1223
- } else {
1224
- const bin = [];
1225
- arr.forEach((byte) => {
1226
- bin.push(globalThis.String.fromCharCode(byte));
1688
+ if (finalTokenTransaction.clientCreatedTimestamp.getTime() !== partialTokenTransaction.clientCreatedTimestamp.getTime()) {
1689
+ throw new InternalValidationError("Client created timestamp mismatch", {
1690
+ value: finalTokenTransaction.clientCreatedTimestamp,
1691
+ expected: partialTokenTransaction.clientCreatedTimestamp
1227
1692
  });
1228
- return globalThis.btoa(bin.join(""));
1229
1693
  }
1230
1694
  }
1231
- function isSet(value) {
1232
- return value !== null && value !== void 0;
1233
- }
1695
+
1696
+ // src/services/token-transactions.ts
1697
+ var import_utils7 = require("@noble/hashes/utils");
1234
1698
 
1235
1699
  // src/address/address.ts
1700
+ var import_secp256k1 = require("@noble/curves/secp256k1");
1701
+ var import_utils3 = require("@noble/hashes/utils");
1702
+ var import_base2 = require("@scure/base");
1236
1703
  var import_uuidv7 = require("uuidv7");
1237
1704
  var import_utils4 = require("@noble/curves/abstract/utils");
1238
1705
  var AddressNetwork = {
@@ -1603,7 +2070,8 @@ var TokenTransactionService = class {
1603
2070
  },
1604
2071
  tokenOutputs,
1605
2072
  sparkOperatorIdentityPublicKeys: this.collectOperatorIdentityPublicKeys(),
1606
- expiryTime: void 0
2073
+ expiryTime: void 0,
2074
+ clientCreatedTimestamp: /* @__PURE__ */ new Date()
1607
2075
  };
1608
2076
  }
1609
2077
  collectOperatorIdentityPublicKeys() {
@@ -1812,7 +2280,7 @@ var TokenTransactionService = class {
1812
2280
  if (!startResponse.keyshareInfo) {
1813
2281
  throw new Error("Keyshare info missing in start response");
1814
2282
  }
1815
- validateTokenTransaction(
2283
+ validateTokenTransactionV0(
1816
2284
  startResponse.finalTokenTransaction,
1817
2285
  tokenTransaction,
1818
2286
  signingOperators,
@@ -2063,7 +2531,26 @@ var TokenTransactionService = class {
2063
2531
  );
2064
2532
  }
2065
2533
  }
2066
- async fetchOwnedTokenOutputs(ownerPublicKeys, tokenPublicKeys) {
2534
+ async fetchOwnedTokenOutputs(params) {
2535
+ if (this.config.getTokenTransactionVersion() === "V0") {
2536
+ return this.fetchOwnedTokenOutputsV0(params);
2537
+ } else {
2538
+ return this.fetchOwnedTokenOutputsV1(params);
2539
+ }
2540
+ }
2541
+ async queryTokenTransactions(params) {
2542
+ if (this.config.getTokenTransactionVersion() === "V0") {
2543
+ return this.queryTokenTransactionsV0(params);
2544
+ } else {
2545
+ return this.queryTokenTransactionsV1(params);
2546
+ }
2547
+ }
2548
+ async fetchOwnedTokenOutputsV0(params) {
2549
+ const {
2550
+ ownerPublicKeys,
2551
+ issuerPublicKeys: tokenPublicKeys = [],
2552
+ tokenIdentifiers = []
2553
+ } = params;
2067
2554
  const sparkClient = await this.connectionManager.createSparkClient(
2068
2555
  this.config.getCoordinatorAddress()
2069
2556
  );
@@ -2071,6 +2558,7 @@ var TokenTransactionService = class {
2071
2558
  const result = await sparkClient.query_token_outputs({
2072
2559
  ownerPublicKeys,
2073
2560
  tokenPublicKeys,
2561
+ tokenIdentifiers,
2074
2562
  network: this.config.getNetworkProto()
2075
2563
  });
2076
2564
  return result.outputsWithPreviousTransactionData;
@@ -2078,7 +2566,7 @@ var TokenTransactionService = class {
2078
2566
  throw new NetworkError(
2079
2567
  "Failed to fetch owned token outputs",
2080
2568
  {
2081
- operation: "query_token_outputs",
2569
+ operation: "spark.query_token_outputs",
2082
2570
  errorCount: 1,
2083
2571
  errors: error instanceof Error ? error.message : String(error)
2084
2572
  },
@@ -2086,11 +2574,127 @@ var TokenTransactionService = class {
2086
2574
  );
2087
2575
  }
2088
2576
  }
2089
- async syncTokenOutputs(tokenOutputs) {
2090
- const unsortedTokenOutputs = await this.fetchOwnedTokenOutputs(
2091
- await this.config.signer.getTrackedPublicKeys(),
2092
- []
2577
+ async fetchOwnedTokenOutputsV1(params) {
2578
+ const {
2579
+ ownerPublicKeys,
2580
+ issuerPublicKeys = [],
2581
+ tokenIdentifiers = []
2582
+ } = params;
2583
+ const tokenClient = await this.connectionManager.createSparkTokenClient(
2584
+ this.config.getCoordinatorAddress()
2585
+ );
2586
+ try {
2587
+ const result = await tokenClient.query_token_outputs({
2588
+ ownerPublicKeys,
2589
+ issuerPublicKeys,
2590
+ tokenIdentifiers,
2591
+ network: this.config.getNetworkProto()
2592
+ });
2593
+ return result.outputsWithPreviousTransactionData;
2594
+ } catch (error) {
2595
+ throw new NetworkError(
2596
+ "Failed to fetch owned token outputs",
2597
+ {
2598
+ operation: "spark_token.query_token_outputs",
2599
+ errorCount: 1,
2600
+ errors: error instanceof Error ? error.message : String(error)
2601
+ },
2602
+ error
2603
+ );
2604
+ }
2605
+ }
2606
+ async queryTokenTransactionsV0(params) {
2607
+ const {
2608
+ ownerPublicKeys,
2609
+ issuerPublicKeys,
2610
+ tokenTransactionHashes,
2611
+ tokenIdentifiers,
2612
+ outputIds
2613
+ } = params;
2614
+ const sparkClient = await this.connectionManager.createSparkClient(
2615
+ this.config.getCoordinatorAddress()
2616
+ );
2617
+ let queryParams = {
2618
+ tokenPublicKeys: issuerPublicKeys?.map(import_utils7.hexToBytes),
2619
+ ownerPublicKeys: ownerPublicKeys?.map(import_utils7.hexToBytes),
2620
+ tokenIdentifiers: tokenIdentifiers?.map(import_utils7.hexToBytes),
2621
+ tokenTransactionHashes: tokenTransactionHashes?.map(import_utils7.hexToBytes),
2622
+ outputIds: outputIds || [],
2623
+ limit: 100,
2624
+ offset: 0
2625
+ };
2626
+ try {
2627
+ const response = await sparkClient.query_token_transactions(queryParams);
2628
+ return response.tokenTransactionsWithStatus.map((tx) => {
2629
+ const v1TokenTransaction = {
2630
+ version: 1,
2631
+ network: tx.tokenTransaction.network,
2632
+ tokenInputs: tx.tokenTransaction.tokenInputs,
2633
+ tokenOutputs: tx.tokenTransaction.tokenOutputs,
2634
+ sparkOperatorIdentityPublicKeys: tx.tokenTransaction.sparkOperatorIdentityPublicKeys,
2635
+ expiryTime: void 0,
2636
+ // V0 doesn't have expiry time
2637
+ clientCreatedTimestamp: tx.tokenTransaction?.tokenInputs?.$case === "mintInput" ? new Date(
2638
+ tx.tokenTransaction.tokenInputs.mintInput.issuerProvidedTimestamp * 1e3
2639
+ ) : /* @__PURE__ */ new Date()
2640
+ };
2641
+ return {
2642
+ tokenTransaction: v1TokenTransaction,
2643
+ status: tx.status,
2644
+ confirmationMetadata: tx.confirmationMetadata
2645
+ };
2646
+ });
2647
+ } catch (error) {
2648
+ throw new NetworkError(
2649
+ "Failed to query token transactions",
2650
+ {
2651
+ operation: "spark.query_token_transactions",
2652
+ errorCount: 1,
2653
+ errors: error instanceof Error ? error.message : String(error)
2654
+ },
2655
+ error
2656
+ );
2657
+ }
2658
+ }
2659
+ async queryTokenTransactionsV1(params) {
2660
+ const {
2661
+ ownerPublicKeys,
2662
+ issuerPublicKeys,
2663
+ tokenTransactionHashes,
2664
+ tokenIdentifiers,
2665
+ outputIds
2666
+ } = params;
2667
+ const tokenClient = await this.connectionManager.createSparkTokenClient(
2668
+ this.config.getCoordinatorAddress()
2093
2669
  );
2670
+ let queryParams = {
2671
+ issuerPublicKeys: issuerPublicKeys?.map(import_utils7.hexToBytes),
2672
+ ownerPublicKeys: ownerPublicKeys?.map(import_utils7.hexToBytes),
2673
+ tokenIdentifiers: tokenIdentifiers?.map(import_utils7.hexToBytes),
2674
+ tokenTransactionHashes: tokenTransactionHashes?.map(import_utils7.hexToBytes),
2675
+ outputIds: outputIds || [],
2676
+ limit: 100,
2677
+ offset: 0
2678
+ };
2679
+ try {
2680
+ const response = await tokenClient.query_token_transactions(queryParams);
2681
+ return response.tokenTransactionsWithStatus;
2682
+ } catch (error) {
2683
+ throw new NetworkError(
2684
+ "Failed to query token transactions",
2685
+ {
2686
+ operation: "spark_token.query_token_transactions",
2687
+ errorCount: 1,
2688
+ errors: error instanceof Error ? error.message : String(error)
2689
+ },
2690
+ error
2691
+ );
2692
+ }
2693
+ }
2694
+ async syncTokenOutputs(tokenOutputs) {
2695
+ const unsortedTokenOutputs = await this.fetchOwnedTokenOutputs({
2696
+ ownerPublicKeys: await this.config.signer.getTrackedPublicKeys()
2697
+ });
2094
2698
  unsortedTokenOutputs.forEach((output) => {
2095
2699
  const tokenKey = (0, import_utils6.bytesToHex)(output.output.tokenPublicKey);
2096
2700
  const index = output.previousTransactionVout;