@buildonspark/spark-sdk 0.1.45 → 0.1.47

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 (146) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/{chunk-I54FARY2.js → chunk-EAP3U3CW.js} +14 -14
  3. package/dist/chunk-GWFQ7EBA.js +3773 -0
  4. package/dist/{chunk-J2IE4Z7Y.js → chunk-NNX4OK44.js} +3487 -934
  5. package/dist/{RequestLightningSendInput-Du0z7Om7.d.cts → client-CvpTRpcw.d.cts} +422 -212
  6. package/dist/{RequestLightningSendInput-DEPd_fPO.d.ts → client-D7KgLN44.d.ts} +422 -212
  7. package/dist/graphql/objects/index.d.cts +5 -9
  8. package/dist/graphql/objects/index.d.ts +5 -9
  9. package/dist/graphql/objects/index.js +1 -1
  10. package/dist/index.cjs +20461 -23377
  11. package/dist/index.d.cts +15 -769
  12. package/dist/index.d.ts +15 -769
  13. package/dist/index.js +81 -71
  14. package/dist/index.node.cjs +21994 -25018
  15. package/dist/index.node.d.cts +312 -34
  16. package/dist/index.node.d.ts +312 -34
  17. package/dist/index.node.js +82 -176
  18. package/dist/native/index.cjs +22847 -25841
  19. package/dist/native/index.d.cts +974 -1138
  20. package/dist/native/index.d.ts +974 -1138
  21. package/dist/native/index.js +10604 -13592
  22. package/dist/proto/lrc20.d.cts +2 -2
  23. package/dist/proto/lrc20.d.ts +2 -2
  24. package/dist/proto/lrc20.js +3098 -46
  25. package/dist/proto/spark.d.cts +1 -1
  26. package/dist/proto/spark.d.ts +1 -1
  27. package/dist/proto/spark_token.d.cts +1 -1
  28. package/dist/proto/spark_token.d.ts +1 -1
  29. package/dist/{sdk-types-Cc4l4kb1.d.ts → sdk-types-BGCeea0G.d.ts} +1 -1
  30. package/dist/{sdk-types-B0SwjolI.d.cts → sdk-types-XUeQMLFP.d.cts} +1 -1
  31. package/dist/{spark-dM7EYXYQ.d.cts → spark-BbUrbvZz.d.cts} +1 -1
  32. package/dist/{spark-dM7EYXYQ.d.ts → spark-BbUrbvZz.d.ts} +1 -1
  33. package/dist/spark-wallet-BAFPpPtY.d.cts +923 -0
  34. package/dist/spark-wallet-CJkQW8pK.d.ts +923 -0
  35. package/dist/spark_bindings/native/index.d.cts +1 -1
  36. package/dist/spark_bindings/native/index.d.ts +1 -1
  37. package/dist/spark_bindings/wasm/index.d.cts +1 -1
  38. package/dist/spark_bindings/wasm/index.d.ts +1 -1
  39. package/dist/{services/index.cjs → tests/test-utils.cjs} +2512 -4380
  40. package/dist/tests/test-utils.d.cts +79 -0
  41. package/dist/tests/test-utils.d.ts +79 -0
  42. package/dist/tests/test-utils.js +85 -0
  43. package/dist/types/index.d.cts +5 -9
  44. package/dist/types/index.d.ts +5 -9
  45. package/dist/types/index.js +5 -5
  46. package/dist/{types-C-Rp0Oo7.d.cts → types-BADxR3bm.d.cts} +1 -1
  47. package/dist/{types-C-Rp0Oo7.d.ts → types-BADxR3bm.d.ts} +1 -1
  48. package/package.json +7 -35
  49. package/src/graphql/client.ts +59 -20
  50. package/src/index.node.ts +28 -2
  51. package/src/index.ts +31 -1
  52. package/src/native/index.ts +16 -2
  53. package/src/services/config.ts +4 -6
  54. package/src/services/connection.ts +131 -64
  55. package/src/services/lightning.ts +1 -2
  56. package/src/services/token-transactions.ts +7 -7
  57. package/src/services/transfer.ts +1 -1
  58. package/src/services/tree-creation.ts +1 -1
  59. package/src/services/wallet-config.ts +18 -10
  60. package/src/signer/signer.react-native.ts +2 -5
  61. package/src/signer/signer.ts +138 -64
  62. package/src/signer/types.ts +52 -0
  63. package/src/spark-wallet/spark-wallet.ts +79 -36
  64. package/src/spark-wallet/types.ts +4 -4
  65. package/src/tests/integration/coop-exit.test.ts +2 -1
  66. package/src/tests/integration/lightning.test.ts +2 -2
  67. package/src/tests/integration/swap.test.ts +1 -1
  68. package/src/tests/integration/transfer.test.ts +5 -5
  69. package/src/tests/integration/tree-creation.test.ts +1 -1
  70. package/src/tests/integration/wallet.test.ts +1 -0
  71. package/src/tests/isHermeticTest.ts +3 -24
  72. package/src/tests/{test-util.ts → test-utils.ts} +3 -7
  73. package/src/tests/wrapWithOtelSpan.test.ts +1 -1
  74. package/src/{address → utils}/address.ts +1 -1
  75. package/src/utils/crypto.ts +19 -9
  76. package/src/utils/index.ts +2 -0
  77. package/src/utils/network.ts +17 -0
  78. package/src/utils/secret-sharing.ts +1 -2
  79. package/src/utils/signing.ts +1 -1
  80. package/src/utils/token-transactions.ts +3 -3
  81. package/src/utils/unilateral-exit.ts +32 -0
  82. package/src/utils/xchain-address.ts +1 -1
  83. package/dist/BitcoinNetwork-TnABML0T.d.cts +0 -18
  84. package/dist/BitcoinNetwork-TnABML0T.d.ts +0 -18
  85. package/dist/LightningSendFeeEstimateInput-BgOhEAI-.d.cts +0 -10
  86. package/dist/LightningSendFeeEstimateInput-BgOhEAI-.d.ts +0 -10
  87. package/dist/address/index.cjs +0 -458
  88. package/dist/address/index.d.cts +0 -32
  89. package/dist/address/index.d.ts +0 -32
  90. package/dist/address/index.js +0 -17
  91. package/dist/chunk-5FUB65LX.js +0 -838
  92. package/dist/chunk-6264CGDM.js +0 -113
  93. package/dist/chunk-7V6N75CC.js +0 -24
  94. package/dist/chunk-C2S227QR.js +0 -2336
  95. package/dist/chunk-GSI4OLXZ.js +0 -117
  96. package/dist/chunk-GZ5IPPJ2.js +0 -170
  97. package/dist/chunk-HWJWKEIU.js +0 -75
  98. package/dist/chunk-KMUMFYFX.js +0 -137
  99. package/dist/chunk-L3EHBOUX.js +0 -0
  100. package/dist/chunk-NSJF5F5O.js +0 -325
  101. package/dist/chunk-NTFKFRQ2.js +0 -3146
  102. package/dist/chunk-PQN3C2MF.js +0 -1122
  103. package/dist/chunk-QNNSEJ4P.js +0 -232
  104. package/dist/chunk-R5PXJZQS.js +0 -277
  105. package/dist/chunk-VTUGIIWI.js +0 -0
  106. package/dist/chunk-YUPMXTCJ.js +0 -622
  107. package/dist/chunk-Z5HIAYFT.js +0 -84
  108. package/dist/index-B2AwKW5J.d.cts +0 -214
  109. package/dist/index-CJDi1HWc.d.ts +0 -214
  110. package/dist/network-BTJl-Sul.d.ts +0 -46
  111. package/dist/network-CqgsdUF2.d.cts +0 -46
  112. package/dist/services/config.cjs +0 -2354
  113. package/dist/services/config.d.cts +0 -42
  114. package/dist/services/config.d.ts +0 -42
  115. package/dist/services/config.js +0 -17
  116. package/dist/services/connection.cjs +0 -17691
  117. package/dist/services/connection.d.cts +0 -95
  118. package/dist/services/connection.d.ts +0 -95
  119. package/dist/services/connection.js +0 -11
  120. package/dist/services/index.d.cts +0 -21
  121. package/dist/services/index.d.ts +0 -21
  122. package/dist/services/index.js +0 -58
  123. package/dist/services/lrc-connection.cjs +0 -4713
  124. package/dist/services/lrc-connection.d.cts +0 -34
  125. package/dist/services/lrc-connection.d.ts +0 -34
  126. package/dist/services/lrc-connection.js +0 -11
  127. package/dist/services/token-transactions.cjs +0 -2877
  128. package/dist/services/token-transactions.d.cts +0 -75
  129. package/dist/services/token-transactions.d.ts +0 -75
  130. package/dist/services/token-transactions.js +0 -15
  131. package/dist/services/wallet-config.cjs +0 -340
  132. package/dist/services/wallet-config.d.cts +0 -56
  133. package/dist/services/wallet-config.d.ts +0 -56
  134. package/dist/services/wallet-config.js +0 -33
  135. package/dist/signer/signer.cjs +0 -2004
  136. package/dist/signer/signer.d.cts +0 -10
  137. package/dist/signer/signer.d.ts +0 -10
  138. package/dist/signer/signer.js +0 -24
  139. package/dist/signer-BocS_J6B.d.ts +0 -187
  140. package/dist/signer-DKS0AJkw.d.cts +0 -187
  141. package/dist/utils/index.cjs +0 -2947
  142. package/dist/utils/index.d.cts +0 -18
  143. package/dist/utils/index.d.ts +0 -18
  144. package/dist/utils/index.js +0 -157
  145. package/src/address/index.ts +0 -1
  146. package/src/services/lrc-connection.ts +0 -215
@@ -1,2877 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/services/token-transactions.ts
31
- var token_transactions_exports = {};
32
- __export(token_transactions_exports, {
33
- TokenTransactionService: () => TokenTransactionService
34
- });
35
- module.exports = __toCommonJS(token_transactions_exports);
36
-
37
- // buffer.js
38
- var import_buffer = require("buffer");
39
- if (typeof globalThis.Buffer === "undefined") {
40
- globalThis.Buffer = import_buffer.Buffer;
41
- }
42
- if (typeof window !== "undefined") {
43
- if (typeof window.global === "undefined") {
44
- window.global = window;
45
- }
46
- if (typeof window.globalThis === "undefined") {
47
- window.globalThis = window;
48
- }
49
- }
50
-
51
- // src/services/token-transactions.ts
52
- var import_utils6 = require("@noble/curves/abstract/utils");
53
- var import_secp256k14 = require("@noble/curves/secp256k1");
54
-
55
- // src/utils/token-hashing.ts
56
- var import_sha2 = require("@noble/hashes/sha2");
57
-
58
- // src/errors/base.ts
59
- var import_utils = require("@noble/hashes/utils");
60
- var SparkSDKError = class extends Error {
61
- context;
62
- originalError;
63
- constructor(message, context = {}, originalError) {
64
- const msg = getMessage(message, context, originalError);
65
- super(msg);
66
- this.name = this.constructor.name;
67
- this.context = context;
68
- this.originalError = originalError;
69
- if (Error.captureStackTrace) {
70
- Error.captureStackTrace(this, this.constructor);
71
- }
72
- }
73
- toString() {
74
- return this.message;
75
- }
76
- toJSON() {
77
- return {
78
- name: this.name,
79
- message: this.message,
80
- context: this.context,
81
- originalError: this.originalError ? {
82
- name: this.originalError.name,
83
- message: this.originalError.message,
84
- stack: this.originalError.stack
85
- } : void 0,
86
- stack: this.stack
87
- };
88
- }
89
- };
90
- function getMessage(message, context = {}, originalError) {
91
- const contextStr = Object.entries(context).map(([key, value]) => `${key}: ${safeStringify(value)}`).join(", ");
92
- const originalErrorStr = originalError ? `
93
- Original Error: ${originalError.message}` : "";
94
- return `SparkSDKError: ${message}${contextStr ? `
95
- Context: ${contextStr}` : ""}${originalErrorStr}`;
96
- }
97
- function safeStringify(value) {
98
- const replacer = (_, v) => {
99
- if (typeof v === "bigint") {
100
- return v.toString();
101
- }
102
- if (v instanceof Uint8Array) {
103
- return formatUint8Array(v);
104
- }
105
- return v;
106
- };
107
- if (typeof value === "bigint") {
108
- return `"${value.toString()}"`;
109
- }
110
- if (value instanceof Uint8Array) {
111
- return `"${formatUint8Array(value)}"`;
112
- }
113
- try {
114
- const result = JSON.stringify(value, replacer);
115
- return result === void 0 ? String(value) : result;
116
- } catch {
117
- try {
118
- return String(value);
119
- } catch {
120
- return "[Unserializable]";
121
- }
122
- }
123
- }
124
- function formatUint8Array(arr) {
125
- return `Uint8Array(0x${(0, import_utils.bytesToHex)(arr)})`;
126
- }
127
-
128
- // src/errors/types.ts
129
- var NetworkError = class extends SparkSDKError {
130
- constructor(message, context = {}, originalError) {
131
- super(message, context, originalError);
132
- }
133
- };
134
- var ValidationError = class extends SparkSDKError {
135
- constructor(message, context = {}, originalError) {
136
- super(message, context, originalError);
137
- }
138
- };
139
- var InternalValidationError = class extends SparkSDKError {
140
- constructor(message, context = {}, originalError) {
141
- super(message, context, originalError);
142
- }
143
- };
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
-
354
- // src/utils/token-hashing.ts
355
- function hashTokenTransaction(tokenTransaction, partialHash = false) {
356
- switch (tokenTransaction.version) {
357
- case 0:
358
- return hashTokenTransactionV0(tokenTransaction, partialHash);
359
- case 1:
360
- return hashTokenTransactionV1(tokenTransaction, partialHash);
361
- default:
362
- throw new ValidationError("invalid token transaction version", {
363
- field: "tokenTransaction.version",
364
- value: tokenTransaction.version
365
- });
366
- }
367
- }
368
- function hashTokenTransactionV0(tokenTransaction, partialHash = false) {
369
- if (!tokenTransaction) {
370
- throw new ValidationError("token transaction cannot be nil", {
371
- field: "tokenTransaction"
372
- });
373
- }
374
- let allHashes = [];
375
- if (tokenTransaction.tokenInputs?.$case === "transferInput") {
376
- if (!tokenTransaction.tokenInputs.transferInput.outputsToSpend) {
377
- throw new ValidationError("outputs to spend cannot be null", {
378
- field: "tokenInputs.transferInput.outputsToSpend"
379
- });
380
- }
381
- if (tokenTransaction.tokenInputs.transferInput.outputsToSpend.length === 0) {
382
- throw new ValidationError("outputs to spend cannot be empty", {
383
- field: "tokenInputs.transferInput.outputsToSpend"
384
- });
385
- }
386
- for (const [
387
- i,
388
- output
389
- ] of tokenTransaction.tokenInputs.transferInput.outputsToSpend.entries()) {
390
- if (!output) {
391
- throw new ValidationError(`output cannot be null at index ${i}`, {
392
- field: `tokenInputs.transferInput.outputsToSpend[${i}]`,
393
- index: i
394
- });
395
- }
396
- const hashObj2 = import_sha2.sha256.create();
397
- if (output.prevTokenTransactionHash) {
398
- const prevHash = output.prevTokenTransactionHash;
399
- if (output.prevTokenTransactionHash.length !== 32) {
400
- throw new ValidationError(
401
- `invalid previous transaction hash length at index ${i}`,
402
- {
403
- field: `tokenInputs.transferInput.outputsToSpend[${i}].prevTokenTransactionHash`,
404
- value: prevHash,
405
- expectedLength: 32,
406
- actualLength: prevHash.length,
407
- index: i
408
- }
409
- );
410
- }
411
- hashObj2.update(output.prevTokenTransactionHash);
412
- }
413
- const voutBytes = new Uint8Array(4);
414
- new DataView(voutBytes.buffer).setUint32(
415
- 0,
416
- output.prevTokenTransactionVout,
417
- false
418
- );
419
- hashObj2.update(voutBytes);
420
- allHashes.push(hashObj2.digest());
421
- }
422
- }
423
- if (tokenTransaction.tokenInputs?.$case === "mintInput") {
424
- const hashObj2 = import_sha2.sha256.create();
425
- if (tokenTransaction.tokenInputs.mintInput.issuerPublicKey) {
426
- const issuerPubKey = tokenTransaction.tokenInputs.mintInput.issuerPublicKey;
427
- if (issuerPubKey.length === 0) {
428
- throw new ValidationError("issuer public key cannot be empty", {
429
- field: "tokenInputs.mintInput.issuerPublicKey",
430
- value: issuerPubKey,
431
- expectedLength: 1,
432
- actualLength: 0
433
- });
434
- }
435
- hashObj2.update(issuerPubKey);
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) {
447
- const timestampBytes = new Uint8Array(8);
448
- new DataView(timestampBytes.buffer).setBigUint64(
449
- 0,
450
- BigInt(timestampValue),
451
- true
452
- // true for little-endian to match Go implementation
453
- );
454
- hashObj2.update(timestampBytes);
455
- }
456
- allHashes.push(hashObj2.digest());
457
- }
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
- }
542
- if (!tokenTransaction.tokenOutputs) {
543
- throw new ValidationError("token outputs cannot be null", {
544
- field: "tokenOutputs"
545
- });
546
- }
547
- if (tokenTransaction.tokenOutputs.length === 0 && tokenTransaction.tokenInputs?.$case !== "createInput") {
548
- throw new ValidationError("token outputs cannot be empty", {
549
- field: "tokenOutputs"
550
- });
551
- }
552
- for (const [i, output] of tokenTransaction.tokenOutputs.entries()) {
553
- if (!output) {
554
- throw new ValidationError(`output cannot be null at index ${i}`, {
555
- field: `tokenOutputs[${i}]`,
556
- index: i
557
- });
558
- }
559
- const hashObj2 = import_sha2.sha256.create();
560
- if (output.id && !partialHash) {
561
- if (output.id.length === 0) {
562
- throw new ValidationError(`output ID at index ${i} cannot be empty`, {
563
- field: `tokenOutputs[${i}].id`,
564
- index: i
565
- });
566
- }
567
- hashObj2.update(new TextEncoder().encode(output.id));
568
- }
569
- if (output.ownerPublicKey) {
570
- if (output.ownerPublicKey.length === 0) {
571
- throw new ValidationError(
572
- `owner public key at index ${i} cannot be empty`,
573
- {
574
- field: `tokenOutputs[${i}].ownerPublicKey`,
575
- index: i
576
- }
577
- );
578
- }
579
- hashObj2.update(output.ownerPublicKey);
580
- }
581
- if (!partialHash) {
582
- const revPubKey = output.revocationCommitment;
583
- if (revPubKey) {
584
- if (revPubKey.length === 0) {
585
- throw new ValidationError(
586
- `revocation commitment at index ${i} cannot be empty`,
587
- {
588
- field: `tokenOutputs[${i}].revocationCommitment`,
589
- index: i
590
- }
591
- );
592
- }
593
- hashObj2.update(revPubKey);
594
- }
595
- const bondBytes = new Uint8Array(8);
596
- new DataView(bondBytes.buffer).setBigUint64(
597
- 0,
598
- BigInt(output.withdrawBondSats),
599
- false
600
- );
601
- hashObj2.update(bondBytes);
602
- const locktimeBytes = new Uint8Array(8);
603
- new DataView(locktimeBytes.buffer).setBigUint64(
604
- 0,
605
- BigInt(output.withdrawRelativeBlockLocktime),
606
- false
607
- );
608
- hashObj2.update(locktimeBytes);
609
- }
610
- if (output.tokenPublicKey) {
611
- if (output.tokenPublicKey.length === 0) {
612
- throw new ValidationError(
613
- `token public key at index ${i} cannot be empty`,
614
- {
615
- field: `tokenOutputs[${i}].tokenPublicKey`,
616
- index: i
617
- }
618
- );
619
- }
620
- hashObj2.update(output.tokenPublicKey);
621
- }
622
- if (output.tokenAmount) {
623
- if (output.tokenAmount.length === 0) {
624
- throw new ValidationError(
625
- `token amount at index ${i} cannot be empty`,
626
- {
627
- field: `tokenOutputs[${i}].tokenAmount`,
628
- index: i
629
- }
630
- );
631
- }
632
- if (output.tokenAmount.length > 16) {
633
- throw new ValidationError(
634
- `token amount at index ${i} exceeds maximum length`,
635
- {
636
- field: `tokenOutputs[${i}].tokenAmount`,
637
- value: output.tokenAmount,
638
- expectedLength: 16,
639
- actualLength: output.tokenAmount.length,
640
- index: i
641
- }
642
- );
643
- }
644
- hashObj2.update(output.tokenAmount);
645
- }
646
- allHashes.push(hashObj2.digest());
647
- }
648
- if (!tokenTransaction.sparkOperatorIdentityPublicKeys) {
649
- throw new ValidationError(
650
- "spark operator identity public keys cannot be null",
651
- {}
652
- );
653
- }
654
- const sortedPubKeys = [
655
- ...tokenTransaction.sparkOperatorIdentityPublicKeys || []
656
- ].sort((a, b) => {
657
- for (let i = 0; i < a.length && i < b.length; i++) {
658
- if (a[i] !== b[i]) return a[i] - b[i];
659
- }
660
- return a.length - b.length;
661
- });
662
- for (const [i, pubKey] of sortedPubKeys.entries()) {
663
- if (!pubKey) {
664
- throw new ValidationError(
665
- `operator public key at index ${i} cannot be null`,
666
- {
667
- field: `sparkOperatorIdentityPublicKeys[${i}]`,
668
- index: i
669
- }
670
- );
671
- }
672
- if (pubKey.length === 0) {
673
- throw new ValidationError(
674
- `operator public key at index ${i} cannot be empty`,
675
- {
676
- field: `sparkOperatorIdentityPublicKeys[${i}]`,
677
- index: i
678
- }
679
- );
680
- }
681
- const hashObj2 = import_sha2.sha256.create();
682
- hashObj2.update(pubKey);
683
- allHashes.push(hashObj2.digest());
684
- }
685
- const hashObj = import_sha2.sha256.create();
686
- let networkBytes = new Uint8Array(4);
687
- new DataView(networkBytes.buffer).setUint32(
688
- 0,
689
- tokenTransaction.network.valueOf(),
690
- false
691
- // false for big-endian
692
- );
693
- hashObj.update(networkBytes);
694
- allHashes.push(hashObj.digest());
695
- const finalHashObj = import_sha2.sha256.create();
696
- const concatenatedHashes = new Uint8Array(
697
- allHashes.reduce((sum, hash) => sum + hash.length, 0)
698
- );
699
- let offset = 0;
700
- for (const hash of allHashes) {
701
- concatenatedHashes.set(hash, offset);
702
- offset += hash.length;
703
- }
704
- finalHashObj.update(concatenatedHashes);
705
- return finalHashObj.digest();
706
- }
707
- function hashTokenTransactionV1(tokenTransaction, partialHash = false) {
708
- if (!tokenTransaction) {
709
- throw new ValidationError("token transaction cannot be nil", {
710
- field: "tokenTransaction"
711
- });
712
- }
713
- let allHashes = [];
714
- const versionHashObj = import_sha2.sha256.create();
715
- const versionBytes = new Uint8Array(4);
716
- new DataView(versionBytes.buffer).setUint32(
717
- 0,
718
- tokenTransaction.version,
719
- false
720
- // false for big-endian
721
- );
722
- versionHashObj.update(versionBytes);
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());
744
- if (tokenTransaction.tokenInputs?.$case === "transferInput") {
745
- if (!tokenTransaction.tokenInputs.transferInput.outputsToSpend) {
746
- throw new ValidationError("outputs to spend cannot be null", {
747
- field: "tokenInputs.transferInput.outputsToSpend"
748
- });
749
- }
750
- if (tokenTransaction.tokenInputs.transferInput.outputsToSpend.length === 0) {
751
- throw new ValidationError("outputs to spend cannot be empty", {
752
- field: "tokenInputs.transferInput.outputsToSpend"
753
- });
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());
764
- for (const [
765
- i,
766
- output
767
- ] of tokenTransaction.tokenInputs.transferInput.outputsToSpend.entries()) {
768
- if (!output) {
769
- throw new ValidationError(`output cannot be null at index ${i}`, {
770
- field: `tokenInputs.transferInput.outputsToSpend[${i}]`,
771
- index: i
772
- });
773
- }
774
- const hashObj2 = import_sha2.sha256.create();
775
- if (output.prevTokenTransactionHash) {
776
- const prevHash = output.prevTokenTransactionHash;
777
- if (output.prevTokenTransactionHash.length !== 32) {
778
- throw new ValidationError(
779
- `invalid previous transaction hash length at index ${i}`,
780
- {
781
- field: `tokenInputs.transferInput.outputsToSpend[${i}].prevTokenTransactionHash`,
782
- value: prevHash,
783
- expectedLength: 32,
784
- actualLength: prevHash.length,
785
- index: i
786
- }
787
- );
788
- }
789
- hashObj2.update(output.prevTokenTransactionHash);
790
- }
791
- const voutBytes = new Uint8Array(4);
792
- new DataView(voutBytes.buffer).setUint32(
793
- 0,
794
- output.prevTokenTransactionVout,
795
- false
796
- );
797
- hashObj2.update(voutBytes);
798
- allHashes.push(hashObj2.digest());
799
- }
800
- } else if (tokenTransaction.tokenInputs?.$case === "mintInput") {
801
- const hashObj2 = import_sha2.sha256.create();
802
- if (tokenTransaction.tokenInputs.mintInput.issuerPublicKey) {
803
- const issuerPubKey = tokenTransaction.tokenInputs.mintInput.issuerPublicKey;
804
- if (issuerPubKey.length === 0) {
805
- throw new ValidationError("issuer public key cannot be empty", {
806
- field: "tokenInputs.mintInput.issuerPublicKey",
807
- value: issuerPubKey,
808
- expectedLength: 1,
809
- actualLength: 0
810
- });
811
- }
812
- hashObj2.update(issuerPubKey);
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
818
- );
819
- } else {
820
- tokenIdentifierHashObj.update(new Uint8Array(32));
821
- }
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
- });
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());
905
- }
906
- if (!tokenTransaction.tokenOutputs) {
907
- throw new ValidationError("token outputs cannot be null", {
908
- field: "tokenOutputs"
909
- });
910
- }
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());
920
- for (const [i, output] of tokenTransaction.tokenOutputs.entries()) {
921
- if (!output) {
922
- throw new ValidationError(`output cannot be null at index ${i}`, {
923
- field: `tokenOutputs[${i}]`,
924
- index: i
925
- });
926
- }
927
- const hashObj2 = import_sha2.sha256.create();
928
- if (output.id && !partialHash) {
929
- if (output.id.length === 0) {
930
- throw new ValidationError(`output ID at index ${i} cannot be empty`, {
931
- field: `tokenOutputs[${i}].id`,
932
- index: i
933
- });
934
- }
935
- hashObj2.update(new TextEncoder().encode(output.id));
936
- }
937
- if (output.ownerPublicKey) {
938
- if (output.ownerPublicKey.length === 0) {
939
- throw new ValidationError(
940
- `owner public key at index ${i} cannot be empty`,
941
- {
942
- field: `tokenOutputs[${i}].ownerPublicKey`,
943
- index: i
944
- }
945
- );
946
- }
947
- hashObj2.update(output.ownerPublicKey);
948
- }
949
- if (!partialHash) {
950
- const revPubKey = output.revocationCommitment;
951
- if (revPubKey) {
952
- if (revPubKey.length === 0) {
953
- throw new ValidationError(
954
- `revocation commitment at index ${i} cannot be empty`,
955
- {
956
- field: `tokenOutputs[${i}].revocationCommitment`,
957
- index: i
958
- }
959
- );
960
- }
961
- hashObj2.update(revPubKey);
962
- }
963
- const bondBytes = new Uint8Array(8);
964
- new DataView(bondBytes.buffer).setBigUint64(
965
- 0,
966
- BigInt(output.withdrawBondSats),
967
- false
968
- );
969
- hashObj2.update(bondBytes);
970
- const locktimeBytes = new Uint8Array(8);
971
- new DataView(locktimeBytes.buffer).setBigUint64(
972
- 0,
973
- BigInt(output.withdrawRelativeBlockLocktime),
974
- false
975
- );
976
- hashObj2.update(locktimeBytes);
977
- }
978
- if (!output.tokenPublicKey || output.tokenPublicKey.length === 0) {
979
- hashObj2.update(new Uint8Array(33));
980
- } else {
981
- hashObj2.update(output.tokenPublicKey);
982
- }
983
- if (!output.tokenIdentifier || output.tokenIdentifier.length === 0) {
984
- hashObj2.update(new Uint8Array(32));
985
- } else {
986
- hashObj2.update(output.tokenIdentifier);
987
- }
988
- if (output.tokenAmount) {
989
- if (output.tokenAmount.length === 0) {
990
- throw new ValidationError(
991
- `token amount at index ${i} cannot be empty`,
992
- {
993
- field: `tokenOutputs[${i}].tokenAmount`,
994
- index: i
995
- }
996
- );
997
- }
998
- if (output.tokenAmount.length > 16) {
999
- throw new ValidationError(
1000
- `token amount at index ${i} exceeds maximum length`,
1001
- {
1002
- field: `tokenOutputs[${i}].tokenAmount`,
1003
- value: output.tokenAmount,
1004
- expectedLength: 16,
1005
- actualLength: output.tokenAmount.length,
1006
- index: i
1007
- }
1008
- );
1009
- }
1010
- hashObj2.update(output.tokenAmount);
1011
- }
1012
- allHashes.push(hashObj2.digest());
1013
- }
1014
- if (!tokenTransaction.sparkOperatorIdentityPublicKeys) {
1015
- throw new ValidationError(
1016
- "spark operator identity public keys cannot be null",
1017
- {}
1018
- );
1019
- }
1020
- const sortedPubKeys = [
1021
- ...tokenTransaction.sparkOperatorIdentityPublicKeys || []
1022
- ].sort((a, b) => {
1023
- for (let i = 0; i < a.length && i < b.length; i++) {
1024
- if (a[i] !== b[i]) return a[i] - b[i];
1025
- }
1026
- return a.length - b.length;
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());
1037
- for (const [i, pubKey] of sortedPubKeys.entries()) {
1038
- if (!pubKey) {
1039
- throw new ValidationError(
1040
- `operator public key at index ${i} cannot be null`,
1041
- {
1042
- field: `sparkOperatorIdentityPublicKeys[${i}]`,
1043
- index: i
1044
- }
1045
- );
1046
- }
1047
- if (pubKey.length === 0) {
1048
- throw new ValidationError(
1049
- `operator public key at index ${i} cannot be empty`,
1050
- {
1051
- field: `sparkOperatorIdentityPublicKeys[${i}]`,
1052
- index: i
1053
- }
1054
- );
1055
- }
1056
- const hashObj2 = import_sha2.sha256.create();
1057
- hashObj2.update(pubKey);
1058
- allHashes.push(hashObj2.digest());
1059
- }
1060
- const hashObj = import_sha2.sha256.create();
1061
- let networkBytes = new Uint8Array(4);
1062
- new DataView(networkBytes.buffer).setUint32(
1063
- 0,
1064
- tokenTransaction.network.valueOf(),
1065
- false
1066
- // false for big-endian
1067
- );
1068
- hashObj.update(networkBytes);
1069
- allHashes.push(hashObj.digest());
1070
- const clientTimestampHashObj = import_sha2.sha256.create();
1071
- const clientCreatedTs = tokenTransaction.clientCreatedTimestamp;
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();
1081
- const clientTimestampBytes = new Uint8Array(8);
1082
- new DataView(clientTimestampBytes.buffer).setBigUint64(
1083
- 0,
1084
- BigInt(clientUnixTime),
1085
- false
1086
- );
1087
- clientTimestampHashObj.update(clientTimestampBytes);
1088
- allHashes.push(clientTimestampHashObj.digest());
1089
- if (!partialHash) {
1090
- const expiryHashObj = import_sha2.sha256.create();
1091
- const expiryTimeBytes = new Uint8Array(8);
1092
- const expiryUnixTime = tokenTransaction.expiryTime ? Math.floor(tokenTransaction.expiryTime.getTime() / 1e3) : 0;
1093
- new DataView(expiryTimeBytes.buffer).setBigUint64(
1094
- 0,
1095
- BigInt(expiryUnixTime),
1096
- false
1097
- // false for big-endian
1098
- );
1099
- expiryHashObj.update(expiryTimeBytes);
1100
- allHashes.push(expiryHashObj.digest());
1101
- }
1102
- const finalHashObj = import_sha2.sha256.create();
1103
- const concatenatedHashes = new Uint8Array(
1104
- allHashes.reduce((sum, hash) => sum + hash.length, 0)
1105
- );
1106
- let offset = 0;
1107
- for (const hash of allHashes) {
1108
- concatenatedHashes.set(hash, offset);
1109
- offset += hash.length;
1110
- }
1111
- finalHashObj.update(concatenatedHashes);
1112
- return finalHashObj.digest();
1113
- }
1114
- function hashOperatorSpecificTokenTransactionSignablePayload(payload) {
1115
- if (!payload) {
1116
- throw new ValidationError(
1117
- "operator specific token transaction signable payload cannot be null",
1118
- {
1119
- field: "payload"
1120
- }
1121
- );
1122
- }
1123
- let allHashes = [];
1124
- if (payload.finalTokenTransactionHash) {
1125
- const hashObj2 = import_sha2.sha256.create();
1126
- if (payload.finalTokenTransactionHash.length !== 32) {
1127
- throw new ValidationError(`invalid final token transaction hash length`, {
1128
- field: "finalTokenTransactionHash",
1129
- value: payload.finalTokenTransactionHash,
1130
- expectedLength: 32,
1131
- actualLength: payload.finalTokenTransactionHash.length
1132
- });
1133
- }
1134
- hashObj2.update(payload.finalTokenTransactionHash);
1135
- allHashes.push(hashObj2.digest());
1136
- }
1137
- if (!payload.operatorIdentityPublicKey) {
1138
- throw new ValidationError("operator identity public key cannot be null", {
1139
- field: "operatorIdentityPublicKey"
1140
- });
1141
- }
1142
- if (payload.operatorIdentityPublicKey.length === 0) {
1143
- throw new ValidationError("operator identity public key cannot be empty", {
1144
- field: "operatorIdentityPublicKey"
1145
- });
1146
- }
1147
- const hashObj = import_sha2.sha256.create();
1148
- hashObj.update(payload.operatorIdentityPublicKey);
1149
- allHashes.push(hashObj.digest());
1150
- const finalHashObj = import_sha2.sha256.create();
1151
- const concatenatedHashes = new Uint8Array(
1152
- allHashes.reduce((sum, hash) => sum + hash.length, 0)
1153
- );
1154
- let offset = 0;
1155
- for (const hash of allHashes) {
1156
- concatenatedHashes.set(hash, offset);
1157
- offset += hash.length;
1158
- }
1159
- finalHashObj.update(concatenatedHashes);
1160
- return finalHashObj.digest();
1161
- }
1162
-
1163
- // src/utils/token-transactions.ts
1164
- var import_utils2 = require("@noble/curves/abstract/utils");
1165
- function calculateAvailableTokenAmount(outputLeaves) {
1166
- return outputLeaves.reduce(
1167
- (sum, output) => sum + BigInt((0, import_utils2.bytesToNumberBE)(output.output.tokenAmount)),
1168
- BigInt(0)
1169
- );
1170
- }
1171
- function checkIfSelectedOutputsAreAvailable(selectedOutputs, tokenOutputs, tokenPublicKey) {
1172
- const tokenPubKeyHex = (0, import_utils2.bytesToHex)(tokenPublicKey);
1173
- const tokenOutputsAvailable = tokenOutputs.get(tokenPubKeyHex);
1174
- if (!tokenOutputsAvailable) {
1175
- return false;
1176
- }
1177
- if (selectedOutputs.length === 0 || tokenOutputsAvailable.length < selectedOutputs.length) {
1178
- return false;
1179
- }
1180
- const availableOutputIds = new Set(
1181
- tokenOutputsAvailable.map((output) => output.output.id)
1182
- );
1183
- for (const selectedOutput of selectedOutputs) {
1184
- if (!selectedOutput.output?.id || !availableOutputIds.has(selectedOutput.output.id)) {
1185
- return false;
1186
- }
1187
- }
1188
- return true;
1189
- }
1190
-
1191
- // src/utils/token-transaction-validation.ts
1192
- function areByteArraysEqual(a, b) {
1193
- if (a.length !== b.length) {
1194
- return false;
1195
- }
1196
- return a.every((byte, index) => byte === b[index]);
1197
- }
1198
- function hasDuplicates(array) {
1199
- return new Set(array).size !== array.length;
1200
- }
1201
- function validateTokenTransactionV0(finalTokenTransaction, partialTokenTransaction, signingOperators, keyshareInfo, expectedWithdrawBondSats, expectedWithdrawRelativeBlockLocktime, expectedThreshold) {
1202
- if (finalTokenTransaction.network !== partialTokenTransaction.network) {
1203
- throw new InternalValidationError(
1204
- "Network mismatch in response token transaction",
1205
- {
1206
- value: finalTokenTransaction.network,
1207
- expected: partialTokenTransaction.network
1208
- }
1209
- );
1210
- }
1211
- if (!finalTokenTransaction.tokenInputs) {
1212
- throw new InternalValidationError(
1213
- "Token inputs missing in final transaction",
1214
- {
1215
- value: finalTokenTransaction
1216
- }
1217
- );
1218
- }
1219
- if (!partialTokenTransaction.tokenInputs) {
1220
- throw new InternalValidationError(
1221
- "Token inputs missing in partial transaction",
1222
- {
1223
- value: partialTokenTransaction
1224
- }
1225
- );
1226
- }
1227
- if (finalTokenTransaction.tokenInputs.$case !== partialTokenTransaction.tokenInputs.$case) {
1228
- throw new InternalValidationError(
1229
- `Transaction type mismatch: final transaction has ${finalTokenTransaction.tokenInputs.$case}, partial transaction has ${partialTokenTransaction.tokenInputs.$case}`,
1230
- {
1231
- value: finalTokenTransaction.tokenInputs.$case,
1232
- expected: partialTokenTransaction.tokenInputs.$case
1233
- }
1234
- );
1235
- }
1236
- if (finalTokenTransaction.sparkOperatorIdentityPublicKeys.length !== partialTokenTransaction.sparkOperatorIdentityPublicKeys.length) {
1237
- throw new InternalValidationError(
1238
- "Spark operator identity public keys count mismatch",
1239
- {
1240
- value: finalTokenTransaction.sparkOperatorIdentityPublicKeys.length,
1241
- expected: partialTokenTransaction.sparkOperatorIdentityPublicKeys.length
1242
- }
1243
- );
1244
- }
1245
- if (partialTokenTransaction.tokenInputs.$case === "mintInput" && finalTokenTransaction.tokenInputs.$case === "mintInput") {
1246
- const finalMintInput = finalTokenTransaction.tokenInputs.mintInput;
1247
- const partialMintInput = partialTokenTransaction.tokenInputs.mintInput;
1248
- if (!areByteArraysEqual(
1249
- finalMintInput.issuerPublicKey,
1250
- partialMintInput.issuerPublicKey
1251
- )) {
1252
- throw new InternalValidationError(
1253
- "Issuer public key mismatch in mint input",
1254
- {
1255
- value: finalMintInput.issuerPublicKey.toString(),
1256
- expected: partialMintInput.issuerPublicKey.toString()
1257
- }
1258
- );
1259
- }
1260
- } else if (partialTokenTransaction.tokenInputs.$case === "transferInput" && finalTokenTransaction.tokenInputs.$case === "transferInput") {
1261
- const finalTransferInput = finalTokenTransaction.tokenInputs.transferInput;
1262
- const partialTransferInput = partialTokenTransaction.tokenInputs.transferInput;
1263
- if (finalTransferInput.outputsToSpend.length !== partialTransferInput.outputsToSpend.length) {
1264
- throw new InternalValidationError(
1265
- "Outputs to spend count mismatch in transfer input",
1266
- {
1267
- value: finalTransferInput.outputsToSpend.length,
1268
- expected: partialTransferInput.outputsToSpend.length
1269
- }
1270
- );
1271
- }
1272
- for (let i = 0; i < finalTransferInput.outputsToSpend.length; i++) {
1273
- const finalOutput = finalTransferInput.outputsToSpend[i];
1274
- const partialOutput = partialTransferInput.outputsToSpend[i];
1275
- if (!finalOutput) {
1276
- throw new InternalValidationError(
1277
- "Token output to spend missing in final transaction",
1278
- {
1279
- outputIndex: i,
1280
- value: finalOutput
1281
- }
1282
- );
1283
- }
1284
- if (!partialOutput) {
1285
- throw new InternalValidationError(
1286
- "Token output to spend missing in partial transaction",
1287
- {
1288
- outputIndex: i,
1289
- value: partialOutput
1290
- }
1291
- );
1292
- }
1293
- if (!areByteArraysEqual(
1294
- finalOutput.prevTokenTransactionHash,
1295
- partialOutput.prevTokenTransactionHash
1296
- )) {
1297
- throw new InternalValidationError(
1298
- "Previous token transaction hash mismatch in transfer input",
1299
- {
1300
- outputIndex: i,
1301
- value: finalOutput.prevTokenTransactionHash.toString(),
1302
- expected: partialOutput.prevTokenTransactionHash.toString()
1303
- }
1304
- );
1305
- }
1306
- if (finalOutput.prevTokenTransactionVout !== partialOutput.prevTokenTransactionVout) {
1307
- throw new InternalValidationError(
1308
- "Previous token transaction vout mismatch in transfer input",
1309
- {
1310
- outputIndex: i,
1311
- value: finalOutput.prevTokenTransactionVout,
1312
- expected: partialOutput.prevTokenTransactionVout
1313
- }
1314
- );
1315
- }
1316
- }
1317
- }
1318
- if (finalTokenTransaction.tokenOutputs.length !== partialTokenTransaction.tokenOutputs.length) {
1319
- throw new InternalValidationError("Token outputs count mismatch", {
1320
- value: finalTokenTransaction.tokenOutputs.length,
1321
- expected: partialTokenTransaction.tokenOutputs.length
1322
- });
1323
- }
1324
- for (let i = 0; i < finalTokenTransaction.tokenOutputs.length; i++) {
1325
- const finalOutput = finalTokenTransaction.tokenOutputs[i];
1326
- const partialOutput = partialTokenTransaction.tokenOutputs[i];
1327
- if (!finalOutput) {
1328
- throw new InternalValidationError(
1329
- "Token output missing in final transaction",
1330
- {
1331
- outputIndex: i,
1332
- value: finalOutput
1333
- }
1334
- );
1335
- }
1336
- if (!partialOutput) {
1337
- throw new InternalValidationError(
1338
- "Token output missing in partial transaction",
1339
- {
1340
- outputIndex: i,
1341
- value: partialOutput
1342
- }
1343
- );
1344
- }
1345
- if (!areByteArraysEqual(
1346
- finalOutput.ownerPublicKey,
1347
- partialOutput.ownerPublicKey
1348
- )) {
1349
- throw new InternalValidationError(
1350
- "Owner public key mismatch in token output",
1351
- {
1352
- outputIndex: i,
1353
- value: finalOutput.ownerPublicKey.toString(),
1354
- expected: partialOutput.ownerPublicKey.toString()
1355
- }
1356
- );
1357
- }
1358
- if (finalOutput.tokenPublicKey !== void 0 && partialOutput.tokenPublicKey !== void 0 && !areByteArraysEqual(
1359
- finalOutput.tokenPublicKey,
1360
- partialOutput.tokenPublicKey
1361
- )) {
1362
- throw new InternalValidationError(
1363
- "Token public key mismatch in token output",
1364
- {
1365
- outputIndex: i,
1366
- value: finalOutput.tokenPublicKey?.toString(),
1367
- expected: partialOutput.tokenPublicKey?.toString()
1368
- }
1369
- );
1370
- }
1371
- if (!areByteArraysEqual(finalOutput.tokenAmount, partialOutput.tokenAmount)) {
1372
- throw new InternalValidationError(
1373
- "Token amount mismatch in token output",
1374
- {
1375
- outputIndex: i,
1376
- value: finalOutput.tokenAmount.toString(),
1377
- expected: partialOutput.tokenAmount.toString()
1378
- }
1379
- );
1380
- }
1381
- if (finalOutput.withdrawBondSats !== void 0) {
1382
- if (finalOutput.withdrawBondSats !== expectedWithdrawBondSats) {
1383
- throw new InternalValidationError(
1384
- "Withdraw bond sats mismatch in token output",
1385
- {
1386
- outputIndex: i,
1387
- value: finalOutput.withdrawBondSats,
1388
- expected: expectedWithdrawBondSats
1389
- }
1390
- );
1391
- }
1392
- }
1393
- if (finalOutput.withdrawRelativeBlockLocktime !== void 0) {
1394
- if (finalOutput.withdrawRelativeBlockLocktime !== expectedWithdrawRelativeBlockLocktime) {
1395
- throw new InternalValidationError(
1396
- "Withdraw relative block locktime mismatch in token output",
1397
- {
1398
- outputIndex: i,
1399
- value: finalOutput.withdrawRelativeBlockLocktime,
1400
- expected: expectedWithdrawRelativeBlockLocktime
1401
- }
1402
- );
1403
- }
1404
- }
1405
- if (keyshareInfo.threshold !== expectedThreshold) {
1406
- throw new InternalValidationError(
1407
- "Threshold mismatch: expected " + expectedThreshold + " but got " + keyshareInfo.threshold,
1408
- {
1409
- field: "threshold",
1410
- value: keyshareInfo.threshold,
1411
- expected: expectedThreshold
1412
- }
1413
- );
1414
- }
1415
- }
1416
- if (keyshareInfo.ownerIdentifiers.length !== Object.keys(signingOperators).length) {
1417
- throw new InternalValidationError(
1418
- `Keyshare operator count (${keyshareInfo.ownerIdentifiers.length}) does not match signing operator count (${Object.keys(signingOperators).length})`,
1419
- {
1420
- keyshareInfo: keyshareInfo.ownerIdentifiers.length,
1421
- signingOperators: Object.keys(signingOperators).length
1422
- }
1423
- );
1424
- }
1425
- if (hasDuplicates(keyshareInfo.ownerIdentifiers)) {
1426
- throw new InternalValidationError(
1427
- "Duplicate ownerIdentifiers found in keyshareInfo",
1428
- {
1429
- keyshareInfo: keyshareInfo.ownerIdentifiers
1430
- }
1431
- );
1432
- }
1433
- for (const identifier of keyshareInfo.ownerIdentifiers) {
1434
- if (!signingOperators[identifier]) {
1435
- throw new InternalValidationError(
1436
- `Keyshare operator ${identifier} not found in signing operator list`,
1437
- {
1438
- keyshareInfo: identifier,
1439
- signingOperators: Object.keys(signingOperators)
1440
- }
1441
- );
1442
- }
1443
- }
1444
- }
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
- );
1503
- }
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
- );
1515
- }
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
1525
- }
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
1534
- }
1535
- );
1536
- }
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
- );
1559
- }
1560
- }
1561
- }
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
- );
1579
- }
1580
- if (!partialOutput) {
1581
- throw new InternalValidationError(
1582
- "Token output missing in partial transaction",
1583
- {
1584
- outputIndex: i,
1585
- value: partialOutput
1586
- }
1587
- );
1588
- }
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()
1599
- }
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()
1612
- }
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()
1622
- }
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
1633
- }
1634
- );
1635
- }
1636
- }
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
- }
1648
- }
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
- );
1658
- }
1659
- }
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
- );
1686
- }
1687
- }
1688
- if (finalTokenTransaction.clientCreatedTimestamp.getTime() !== partialTokenTransaction.clientCreatedTimestamp.getTime()) {
1689
- throw new InternalValidationError("Client created timestamp mismatch", {
1690
- value: finalTokenTransaction.clientCreatedTimestamp,
1691
- expected: partialTokenTransaction.clientCreatedTimestamp
1692
- });
1693
- }
1694
- }
1695
-
1696
- // src/services/token-transactions.ts
1697
- var import_utils7 = require("@noble/hashes/utils");
1698
-
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");
1703
- var import_uuidv7 = require("uuidv7");
1704
- var import_utils4 = require("@noble/curves/abstract/utils");
1705
- var AddressNetwork = {
1706
- MAINNET: "sp",
1707
- TESTNET: "spt",
1708
- REGTEST: "sprt",
1709
- SIGNET: "sps",
1710
- LOCAL: "spl"
1711
- };
1712
- function decodeSparkAddress(address, network) {
1713
- try {
1714
- const decoded = import_base2.bech32m.decode(address, 500);
1715
- if (decoded.prefix !== AddressNetwork[network]) {
1716
- throw new ValidationError("Invalid Spark address prefix", {
1717
- field: "address",
1718
- value: address,
1719
- expected: `prefix='${AddressNetwork[network]}'`
1720
- });
1721
- }
1722
- const payload = SparkAddress.decode(import_base2.bech32m.fromWords(decoded.words));
1723
- const publicKey = (0, import_utils3.bytesToHex)(payload.identityPublicKey);
1724
- isValidPublicKey(publicKey);
1725
- const paymentIntentFields = payload.paymentIntentFields;
1726
- return {
1727
- identityPublicKey: publicKey,
1728
- network,
1729
- paymentIntentFields: paymentIntentFields && {
1730
- id: import_uuidv7.UUID.ofInner(paymentIntentFields.id).toString(),
1731
- assetIdentifier: paymentIntentFields.assetIdentifier ? (0, import_utils3.bytesToHex)(paymentIntentFields.assetIdentifier) : void 0,
1732
- assetAmount: (0, import_utils4.bytesToNumberBE)(paymentIntentFields.assetAmount),
1733
- memo: paymentIntentFields.memo
1734
- }
1735
- };
1736
- } catch (error) {
1737
- if (error instanceof ValidationError) {
1738
- throw error;
1739
- }
1740
- throw new ValidationError(
1741
- "Failed to decode Spark address",
1742
- {
1743
- field: "address",
1744
- value: address
1745
- },
1746
- error
1747
- );
1748
- }
1749
- }
1750
- function isValidPublicKey(publicKey) {
1751
- try {
1752
- const point = import_secp256k1.secp256k1.ProjectivePoint.fromHex(publicKey);
1753
- point.assertValidity();
1754
- } catch (error) {
1755
- throw new ValidationError(
1756
- "Invalid public key",
1757
- {
1758
- field: "publicKey",
1759
- value: publicKey
1760
- },
1761
- error
1762
- );
1763
- }
1764
- }
1765
-
1766
- // src/utils/response-validation.ts
1767
- function collectResponses(responses) {
1768
- const successfulResponses = responses.filter(
1769
- (result) => result.status === "fulfilled"
1770
- ).map((result) => result.value);
1771
- const failedResponses = responses.filter(
1772
- (result) => result.status === "rejected"
1773
- );
1774
- if (failedResponses.length > 0) {
1775
- const errors = failedResponses.map((result) => result.reason).join("\n");
1776
- throw new NetworkError(
1777
- `${failedResponses.length} out of ${responses.length} requests failed, please try again`,
1778
- {
1779
- errorCount: failedResponses.length,
1780
- errors
1781
- }
1782
- );
1783
- }
1784
- return successfulResponses;
1785
- }
1786
-
1787
- // src/utils/token-keyshares.ts
1788
- var import_secp256k13 = require("@noble/curves/secp256k1");
1789
-
1790
- // src/utils/secret-sharing.ts
1791
- var import_utils5 = require("@noble/curves/abstract/utils");
1792
- var import_secp256k12 = require("@noble/curves/secp256k1");
1793
-
1794
- // src/utils/crypto.ts
1795
- var import_crypto = __toESM(require("crypto"), 1);
1796
- var getCrypto = () => {
1797
- let cryptoImpl = typeof window !== "undefined" ? window.crypto : typeof global !== "undefined" && global.crypto ? global.crypto : import_crypto.default;
1798
- return cryptoImpl;
1799
- };
1800
-
1801
- // src/utils/secret-sharing.ts
1802
- var crypto = getCrypto();
1803
- function modInverse(a, m) {
1804
- a = (a % m + m) % m;
1805
- let [old_r, r] = [a, m];
1806
- let [old_s, s] = [1n, 0n];
1807
- let [old_t, t] = [0n, 1n];
1808
- while (r !== 0n) {
1809
- const quotient = old_r / r;
1810
- [old_r, r] = [r, old_r - quotient * r];
1811
- [old_s, s] = [s, old_s - quotient * s];
1812
- [old_t, t] = [t, old_t - quotient * t];
1813
- }
1814
- if (old_r !== 1n) {
1815
- throw new ValidationError("Modular inverse does not exist", {
1816
- field: "modInverse",
1817
- value: `a: ${a}, m: ${m}`,
1818
- expected: "a and m must be coprime"
1819
- });
1820
- }
1821
- return (old_s % m + m) % m;
1822
- }
1823
- function fieldDiv(numerator, denominator, fieldModulus) {
1824
- if (denominator === 0n) {
1825
- throw new ValidationError("Division by zero", {
1826
- field: "denominator",
1827
- value: "0",
1828
- expected: "Non-zero value"
1829
- });
1830
- }
1831
- const inverse = modInverse(denominator, fieldModulus);
1832
- return numerator * inverse % fieldModulus;
1833
- }
1834
- function computerLagrangeCoefficients(index, points) {
1835
- let numerator = 1n;
1836
- let denominator = 1n;
1837
- let fieldModulus = points[0]?.fieldModulus;
1838
- if (!fieldModulus) {
1839
- throw new ValidationError("Field modulus is undefined", {
1840
- field: "fieldModulus",
1841
- value: "undefined",
1842
- expected: "A valid field modulus"
1843
- });
1844
- }
1845
- for (const point of points) {
1846
- if (point.index === index) {
1847
- continue;
1848
- }
1849
- numerator = numerator * point.index;
1850
- const value = point.index - index;
1851
- denominator = denominator * value;
1852
- }
1853
- return fieldDiv(numerator, denominator, fieldModulus);
1854
- }
1855
- function recoverSecret(shares) {
1856
- if (shares.length === 0) return 0n;
1857
- const threshold = shares[0]?.threshold;
1858
- const fieldModulus = shares[0]?.fieldModulus;
1859
- if (!threshold || !fieldModulus) {
1860
- throw new ValidationError("Shares are not valid", {
1861
- field: "shares",
1862
- value: "Missing threshold or fieldModulus",
1863
- expected: "Valid shares with threshold and fieldModulus"
1864
- });
1865
- }
1866
- if (shares.length < threshold) {
1867
- throw new ValidationError("Not enough shares to recover secret", {
1868
- field: "shares",
1869
- value: shares.length,
1870
- expected: `At least ${threshold} shares`
1871
- });
1872
- }
1873
- let result = 0n;
1874
- for (const share of shares) {
1875
- const coeff = computerLagrangeCoefficients(share.index, shares);
1876
- const item = share.share * coeff % fieldModulus;
1877
- result = (result + item) % fieldModulus;
1878
- }
1879
- return result;
1880
- }
1881
- function bigIntToPrivateKey(value) {
1882
- const hex = value.toString(16).padStart(64, "0");
1883
- const bytes = new Uint8Array(32);
1884
- for (let i = 0; i < 32; i++) {
1885
- bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
1886
- }
1887
- return bytes;
1888
- }
1889
-
1890
- // src/utils/token-keyshares.ts
1891
- function recoverRevocationSecretFromKeyshares(keyshares, threshold) {
1892
- const shares = keyshares.map((keyshare) => ({
1893
- fieldModulus: BigInt("0x" + import_secp256k13.secp256k1.CURVE.n.toString(16)),
1894
- // secp256k1 curve order
1895
- threshold,
1896
- index: BigInt(keyshare.operatorIndex),
1897
- share: BigInt(
1898
- "0x" + import_buffer.Buffer.from(keyshare.keyshare.keyshare).toString("hex")
1899
- ),
1900
- proofs: []
1901
- }));
1902
- const recoveredSecret = recoverSecret(shares);
1903
- return bigIntToPrivateKey(recoveredSecret);
1904
- }
1905
-
1906
- // src/services/token-transactions.ts
1907
- var MAX_TOKEN_OUTPUTS = 500;
1908
- var TokenTransactionService = class {
1909
- config;
1910
- connectionManager;
1911
- constructor(config, connectionManager) {
1912
- this.config = config;
1913
- this.connectionManager = connectionManager;
1914
- }
1915
- async tokenTransfer(tokenOutputs, receiverOutputs, outputSelectionStrategy = "SMALL_FIRST", selectedOutputs) {
1916
- if (receiverOutputs.length === 0) {
1917
- throw new ValidationError("No receiver outputs provided", {
1918
- field: "receiverOutputs",
1919
- value: receiverOutputs,
1920
- expected: "Non-empty array"
1921
- });
1922
- }
1923
- const totalTokenAmount = receiverOutputs.reduce(
1924
- (sum, transfer) => sum + transfer.tokenAmount,
1925
- 0n
1926
- );
1927
- let outputsToUse;
1928
- if (selectedOutputs) {
1929
- outputsToUse = selectedOutputs;
1930
- if (!checkIfSelectedOutputsAreAvailable(
1931
- outputsToUse,
1932
- tokenOutputs,
1933
- (0, import_utils7.hexToBytes)(receiverOutputs[0].tokenPublicKey)
1934
- )) {
1935
- throw new ValidationError(
1936
- "One or more selected TTXOs are not available",
1937
- {
1938
- field: "selectedOutputs",
1939
- value: selectedOutputs,
1940
- expected: "Available TTXOs"
1941
- }
1942
- );
1943
- }
1944
- } else {
1945
- outputsToUse = this.selectTokenOutputs(
1946
- tokenOutputs.get(receiverOutputs[0].tokenPublicKey),
1947
- totalTokenAmount,
1948
- outputSelectionStrategy
1949
- );
1950
- }
1951
- if (outputsToUse.length > MAX_TOKEN_OUTPUTS) {
1952
- const availableOutputs = tokenOutputs.get(
1953
- receiverOutputs[0].tokenPublicKey
1954
- );
1955
- const sortedOutputs = [...availableOutputs];
1956
- this.sortTokenOutputsByStrategy(sortedOutputs, outputSelectionStrategy);
1957
- const maxOutputsToUse = sortedOutputs.slice(0, MAX_TOKEN_OUTPUTS);
1958
- const maxAmount = calculateAvailableTokenAmount(maxOutputsToUse);
1959
- throw new ValidationError(
1960
- `Cannot transfer more than ${MAX_TOKEN_OUTPUTS} TTXOs in a single transaction (${outputsToUse.length} selected). Maximum transferable amount is: ${maxAmount}`,
1961
- {
1962
- field: "outputsToUse",
1963
- value: outputsToUse.length,
1964
- expected: `Less than or equal to ${MAX_TOKEN_OUTPUTS}, with maximum transferable amount of ${maxAmount}`
1965
- }
1966
- );
1967
- }
1968
- const tokenOutputData = receiverOutputs.map((transfer) => {
1969
- const receiverAddress = decodeSparkAddress(
1970
- transfer.receiverSparkAddress,
1971
- this.config.getNetworkType()
1972
- );
1973
- return {
1974
- receiverSparkAddress: (0, import_utils7.hexToBytes)(receiverAddress.identityPublicKey),
1975
- tokenPublicKey: (0, import_utils7.hexToBytes)(transfer.tokenPublicKey),
1976
- tokenAmount: transfer.tokenAmount
1977
- };
1978
- });
1979
- let tokenTransaction;
1980
- if (this.config.getTokenTransactionVersion() === "V0") {
1981
- tokenTransaction = await this.constructTransferTokenTransactionV0(
1982
- outputsToUse,
1983
- tokenOutputData
1984
- );
1985
- } else {
1986
- tokenTransaction = await this.constructTransferTokenTransaction(
1987
- outputsToUse,
1988
- tokenOutputData
1989
- );
1990
- }
1991
- const txId = await this.broadcastTokenTransaction(
1992
- tokenTransaction,
1993
- outputsToUse.map((output) => output.output.ownerPublicKey),
1994
- outputsToUse.map((output) => output.output.revocationCommitment)
1995
- );
1996
- return txId;
1997
- }
1998
- async constructTransferTokenTransactionV0(selectedOutputs, tokenOutputData) {
1999
- selectedOutputs.sort(
2000
- (a, b) => a.previousTransactionVout - b.previousTransactionVout
2001
- );
2002
- const availableTokenAmount = calculateAvailableTokenAmount(selectedOutputs);
2003
- const totalRequestedAmount = tokenOutputData.reduce(
2004
- (sum, output) => sum + output.tokenAmount,
2005
- 0n
2006
- );
2007
- const tokenOutputs = tokenOutputData.map((output) => ({
2008
- ownerPublicKey: output.receiverSparkAddress,
2009
- tokenPublicKey: output.tokenPublicKey,
2010
- tokenAmount: (0, import_utils6.numberToBytesBE)(output.tokenAmount, 16)
2011
- }));
2012
- if (availableTokenAmount > totalRequestedAmount) {
2013
- const changeAmount = availableTokenAmount - totalRequestedAmount;
2014
- const firstTokenPublicKey = tokenOutputData[0].tokenPublicKey;
2015
- tokenOutputs.push({
2016
- ownerPublicKey: await this.config.signer.getIdentityPublicKey(),
2017
- tokenPublicKey: firstTokenPublicKey,
2018
- tokenAmount: (0, import_utils6.numberToBytesBE)(changeAmount, 16)
2019
- });
2020
- }
2021
- return {
2022
- network: this.config.getNetworkProto(),
2023
- tokenInputs: {
2024
- $case: "transferInput",
2025
- transferInput: {
2026
- outputsToSpend: selectedOutputs.map((output) => ({
2027
- prevTokenTransactionHash: output.previousTransactionHash,
2028
- prevTokenTransactionVout: output.previousTransactionVout
2029
- }))
2030
- }
2031
- },
2032
- tokenOutputs,
2033
- sparkOperatorIdentityPublicKeys: this.collectOperatorIdentityPublicKeys()
2034
- };
2035
- }
2036
- async constructTransferTokenTransaction(selectedOutputs, tokenOutputData) {
2037
- selectedOutputs.sort(
2038
- (a, b) => a.previousTransactionVout - b.previousTransactionVout
2039
- );
2040
- const availableTokenAmount = calculateAvailableTokenAmount(selectedOutputs);
2041
- const totalRequestedAmount = tokenOutputData.reduce(
2042
- (sum, output) => sum + output.tokenAmount,
2043
- 0n
2044
- );
2045
- const tokenOutputs = tokenOutputData.map((output) => ({
2046
- ownerPublicKey: output.receiverSparkAddress,
2047
- tokenPublicKey: output.tokenPublicKey,
2048
- tokenAmount: (0, import_utils6.numberToBytesBE)(output.tokenAmount, 16)
2049
- }));
2050
- if (availableTokenAmount > totalRequestedAmount) {
2051
- const changeAmount = availableTokenAmount - totalRequestedAmount;
2052
- const firstTokenPublicKey = tokenOutputData[0].tokenPublicKey;
2053
- tokenOutputs.push({
2054
- ownerPublicKey: await this.config.signer.getIdentityPublicKey(),
2055
- tokenPublicKey: firstTokenPublicKey,
2056
- tokenAmount: (0, import_utils6.numberToBytesBE)(changeAmount, 16)
2057
- });
2058
- }
2059
- return {
2060
- version: 1,
2061
- network: this.config.getNetworkProto(),
2062
- tokenInputs: {
2063
- $case: "transferInput",
2064
- transferInput: {
2065
- outputsToSpend: selectedOutputs.map((output) => ({
2066
- prevTokenTransactionHash: output.previousTransactionHash,
2067
- prevTokenTransactionVout: output.previousTransactionVout
2068
- }))
2069
- }
2070
- },
2071
- tokenOutputs,
2072
- sparkOperatorIdentityPublicKeys: this.collectOperatorIdentityPublicKeys(),
2073
- expiryTime: void 0,
2074
- clientCreatedTimestamp: /* @__PURE__ */ new Date()
2075
- };
2076
- }
2077
- collectOperatorIdentityPublicKeys() {
2078
- const operatorKeys = [];
2079
- for (const [_, operator] of Object.entries(
2080
- this.config.getSigningOperators()
2081
- )) {
2082
- operatorKeys.push((0, import_utils7.hexToBytes)(operator.identityPublicKey));
2083
- }
2084
- return operatorKeys;
2085
- }
2086
- async broadcastTokenTransaction(tokenTransaction, outputsToSpendSigningPublicKeys, outputsToSpendCommitments) {
2087
- const signingOperators = this.config.getSigningOperators();
2088
- if (!isTokenTransaction(tokenTransaction)) {
2089
- return this.broadcastTokenTransactionV0(
2090
- tokenTransaction,
2091
- signingOperators,
2092
- outputsToSpendSigningPublicKeys,
2093
- outputsToSpendCommitments
2094
- );
2095
- } else {
2096
- return this.broadcastTokenTransactionV1(
2097
- tokenTransaction,
2098
- signingOperators,
2099
- outputsToSpendSigningPublicKeys,
2100
- outputsToSpendCommitments
2101
- );
2102
- }
2103
- }
2104
- async broadcastTokenTransactionV0(tokenTransaction, signingOperators, outputsToSpendSigningPublicKeys, outputsToSpendCommitments) {
2105
- const { finalTokenTransaction, finalTokenTransactionHash, threshold } = await this.startTokenTransactionV0(
2106
- tokenTransaction,
2107
- signingOperators,
2108
- outputsToSpendSigningPublicKeys,
2109
- outputsToSpendCommitments
2110
- );
2111
- const { successfulSignatures } = await this.signTokenTransactionV0(
2112
- finalTokenTransaction,
2113
- finalTokenTransactionHash,
2114
- signingOperators
2115
- );
2116
- if (finalTokenTransaction.tokenInputs.$case === "transferInput") {
2117
- const outputsToSpend = finalTokenTransaction.tokenInputs.transferInput.outputsToSpend;
2118
- const errors = [];
2119
- const revocationSecrets = [];
2120
- for (let outputIndex = 0; outputIndex < outputsToSpend.length; outputIndex++) {
2121
- const outputKeyshares = successfulSignatures.map(({ identifier, response }) => ({
2122
- operatorIndex: parseInt(identifier, 16),
2123
- keyshare: response.revocationKeyshares[outputIndex]
2124
- }));
2125
- if (outputKeyshares.length < threshold) {
2126
- errors.push(
2127
- new ValidationError("Insufficient keyshares", {
2128
- field: "outputKeyshares",
2129
- value: outputKeyshares.length,
2130
- expected: threshold,
2131
- index: outputIndex
2132
- })
2133
- );
2134
- }
2135
- const seenIndices = /* @__PURE__ */ new Set();
2136
- for (const { operatorIndex } of outputKeyshares) {
2137
- if (seenIndices.has(operatorIndex)) {
2138
- errors.push(
2139
- new ValidationError("Duplicate operator index", {
2140
- field: "outputKeyshares",
2141
- value: operatorIndex,
2142
- expected: "Unique operator index",
2143
- index: outputIndex
2144
- })
2145
- );
2146
- }
2147
- seenIndices.add(operatorIndex);
2148
- }
2149
- const revocationSecret = recoverRevocationSecretFromKeyshares(
2150
- outputKeyshares,
2151
- threshold
2152
- );
2153
- const derivedRevocationCommitment = import_secp256k14.secp256k1.getPublicKey(
2154
- revocationSecret,
2155
- true
2156
- );
2157
- if (!outputsToSpendCommitments || !outputsToSpendCommitments[outputIndex] || !derivedRevocationCommitment.every(
2158
- (byte, i) => byte === outputsToSpendCommitments[outputIndex][i]
2159
- )) {
2160
- errors.push(
2161
- new InternalValidationError(
2162
- "Revocation commitment verification failed",
2163
- {
2164
- field: "revocationCommitment",
2165
- value: derivedRevocationCommitment,
2166
- expected: (0, import_utils6.bytesToHex)(outputsToSpendCommitments[outputIndex]),
2167
- outputIndex
2168
- }
2169
- )
2170
- );
2171
- }
2172
- revocationSecrets.push({
2173
- inputIndex: outputIndex,
2174
- revocationSecret
2175
- });
2176
- }
2177
- if (errors.length > 0) {
2178
- throw new ValidationError(
2179
- "Multiple validation errors occurred across outputs",
2180
- {
2181
- field: "outputValidation",
2182
- value: errors
2183
- }
2184
- );
2185
- }
2186
- await this.finalizeTokenTransaction(
2187
- finalTokenTransaction,
2188
- revocationSecrets,
2189
- threshold
2190
- );
2191
- }
2192
- return (0, import_utils6.bytesToHex)(finalTokenTransactionHash);
2193
- }
2194
- async broadcastTokenTransactionV1(tokenTransaction, signingOperators, outputsToSpendSigningPublicKeys, outputsToSpendCommitments) {
2195
- const { finalTokenTransaction, finalTokenTransactionHash, threshold } = await this.startTokenTransaction(
2196
- tokenTransaction,
2197
- signingOperators,
2198
- outputsToSpendSigningPublicKeys,
2199
- outputsToSpendCommitments
2200
- );
2201
- await this.signTokenTransaction(
2202
- finalTokenTransaction,
2203
- finalTokenTransactionHash,
2204
- signingOperators
2205
- );
2206
- return (0, import_utils6.bytesToHex)(finalTokenTransactionHash);
2207
- }
2208
- async startTokenTransactionV0(tokenTransaction, signingOperators, outputsToSpendSigningPublicKeys, outputsToSpendCommitments) {
2209
- const sparkClient = await this.connectionManager.createSparkClient(
2210
- this.config.getCoordinatorAddress()
2211
- );
2212
- const partialTokenTransactionHash = hashTokenTransactionV0(
2213
- tokenTransaction,
2214
- true
2215
- );
2216
- const ownerSignaturesWithIndex = [];
2217
- if (tokenTransaction.tokenInputs.$case === "mintInput") {
2218
- const issuerPublicKey = tokenTransaction.tokenInputs.mintInput.issuerPublicKey;
2219
- if (!issuerPublicKey) {
2220
- throw new ValidationError("Invalid mint input", {
2221
- field: "issuerPublicKey",
2222
- value: null,
2223
- expected: "Non-null issuer public key"
2224
- });
2225
- }
2226
- const ownerSignature = await this.signMessageWithKey(
2227
- partialTokenTransactionHash,
2228
- issuerPublicKey
2229
- );
2230
- ownerSignaturesWithIndex.push({
2231
- signature: ownerSignature,
2232
- inputIndex: 0
2233
- });
2234
- } else if (tokenTransaction.tokenInputs.$case === "transferInput") {
2235
- if (!outputsToSpendSigningPublicKeys || !outputsToSpendCommitments) {
2236
- throw new ValidationError("Invalid transfer input", {
2237
- field: "outputsToSpend",
2238
- value: {
2239
- signingPublicKeys: outputsToSpendSigningPublicKeys,
2240
- revocationPublicKeys: outputsToSpendCommitments
2241
- },
2242
- expected: "Non-null signing and revocation public keys"
2243
- });
2244
- }
2245
- for (const [i, key] of outputsToSpendSigningPublicKeys.entries()) {
2246
- if (!key) {
2247
- throw new ValidationError("Invalid signing key", {
2248
- field: "outputsToSpendSigningPublicKeys",
2249
- value: i,
2250
- expected: "Non-null signing key"
2251
- });
2252
- }
2253
- const ownerSignature = await this.signMessageWithKey(
2254
- partialTokenTransactionHash,
2255
- key
2256
- );
2257
- ownerSignaturesWithIndex.push({
2258
- signature: ownerSignature,
2259
- inputIndex: i
2260
- });
2261
- }
2262
- }
2263
- const startResponse = await sparkClient.start_token_transaction(
2264
- {
2265
- identityPublicKey: await this.config.signer.getIdentityPublicKey(),
2266
- partialTokenTransaction: tokenTransaction,
2267
- tokenTransactionSignatures: {
2268
- ownerSignatures: ownerSignaturesWithIndex
2269
- }
2270
- },
2271
- {
2272
- retry: true,
2273
- retryableStatuses: ["UNKNOWN", "UNAVAILABLE", "CANCELLED", "INTERNAL"],
2274
- retryMaxAttempts: 3
2275
- }
2276
- );
2277
- if (!startResponse.finalTokenTransaction) {
2278
- throw new Error("Final token transaction missing in start response");
2279
- }
2280
- if (!startResponse.keyshareInfo) {
2281
- throw new Error("Keyshare info missing in start response");
2282
- }
2283
- validateTokenTransactionV0(
2284
- startResponse.finalTokenTransaction,
2285
- tokenTransaction,
2286
- signingOperators,
2287
- startResponse.keyshareInfo,
2288
- this.config.getExpectedWithdrawBondSats(),
2289
- this.config.getExpectedWithdrawRelativeBlockLocktime(),
2290
- this.config.getThreshold()
2291
- );
2292
- const finalTokenTransaction = startResponse.finalTokenTransaction;
2293
- const finalTokenTransactionHash = hashTokenTransactionV0(
2294
- finalTokenTransaction,
2295
- false
2296
- );
2297
- return {
2298
- finalTokenTransaction,
2299
- finalTokenTransactionHash,
2300
- threshold: startResponse.keyshareInfo.threshold
2301
- };
2302
- }
2303
- async startTokenTransaction(tokenTransaction, signingOperators, outputsToSpendSigningPublicKeys, outputsToSpendCommitments) {
2304
- const sparkClient = await this.connectionManager.createSparkTokenClient(
2305
- this.config.getCoordinatorAddress()
2306
- );
2307
- const partialTokenTransactionHash = hashTokenTransaction(
2308
- tokenTransaction,
2309
- true
2310
- );
2311
- const ownerSignaturesWithIndex = [];
2312
- if (tokenTransaction.tokenInputs.$case === "mintInput") {
2313
- const issuerPublicKey = tokenTransaction.tokenInputs.mintInput.issuerPublicKey;
2314
- if (!issuerPublicKey) {
2315
- throw new ValidationError("Invalid mint input", {
2316
- field: "issuerPublicKey",
2317
- value: null,
2318
- expected: "Non-null issuer public key"
2319
- });
2320
- }
2321
- const ownerSignature = await this.signMessageWithKey(
2322
- partialTokenTransactionHash,
2323
- issuerPublicKey
2324
- );
2325
- ownerSignaturesWithIndex.push({
2326
- signature: ownerSignature,
2327
- inputIndex: 0
2328
- });
2329
- } else if (tokenTransaction.tokenInputs.$case === "transferInput") {
2330
- if (!outputsToSpendSigningPublicKeys || !outputsToSpendCommitments) {
2331
- throw new ValidationError("Invalid transfer input", {
2332
- field: "outputsToSpend",
2333
- value: {
2334
- signingPublicKeys: outputsToSpendSigningPublicKeys,
2335
- revocationPublicKeys: outputsToSpendCommitments
2336
- },
2337
- expected: "Non-null signing and revocation public keys"
2338
- });
2339
- }
2340
- for (const [i, key] of outputsToSpendSigningPublicKeys.entries()) {
2341
- if (!key) {
2342
- throw new ValidationError("Invalid signing key", {
2343
- field: "outputsToSpendSigningPublicKeys",
2344
- value: i,
2345
- expected: "Non-null signing key"
2346
- });
2347
- }
2348
- const ownerSignature = await this.signMessageWithKey(
2349
- partialTokenTransactionHash,
2350
- key
2351
- );
2352
- ownerSignaturesWithIndex.push({
2353
- signature: ownerSignature,
2354
- inputIndex: i
2355
- });
2356
- }
2357
- }
2358
- const startResponse = await sparkClient.start_transaction(
2359
- {
2360
- identityPublicKey: await this.config.signer.getIdentityPublicKey(),
2361
- partialTokenTransaction: tokenTransaction,
2362
- validityDurationSeconds: await this.config.getTokenValidityDurationSeconds(),
2363
- partialTokenTransactionOwnerSignatures: ownerSignaturesWithIndex
2364
- },
2365
- {
2366
- retry: true,
2367
- retryableStatuses: ["UNKNOWN", "UNAVAILABLE", "CANCELLED", "INTERNAL"],
2368
- retryMaxAttempts: 3
2369
- }
2370
- );
2371
- if (!startResponse.finalTokenTransaction) {
2372
- throw new Error("Final token transaction missing in start response");
2373
- }
2374
- if (!startResponse.keyshareInfo) {
2375
- throw new Error("Keyshare info missing in start response");
2376
- }
2377
- validateTokenTransaction(
2378
- startResponse.finalTokenTransaction,
2379
- tokenTransaction,
2380
- signingOperators,
2381
- startResponse.keyshareInfo,
2382
- this.config.getExpectedWithdrawBondSats(),
2383
- this.config.getExpectedWithdrawRelativeBlockLocktime(),
2384
- this.config.getThreshold()
2385
- );
2386
- const finalTokenTransaction = startResponse.finalTokenTransaction;
2387
- const finalTokenTransactionHash = hashTokenTransaction(
2388
- finalTokenTransaction,
2389
- false
2390
- );
2391
- return {
2392
- finalTokenTransaction,
2393
- finalTokenTransactionHash,
2394
- threshold: startResponse.keyshareInfo.threshold
2395
- };
2396
- }
2397
- async signTokenTransactionV0(finalTokenTransaction, finalTokenTransactionHash, signingOperators) {
2398
- const soSignatures = await Promise.allSettled(
2399
- Object.entries(signingOperators).map(
2400
- async ([identifier, operator], index) => {
2401
- const internalSparkClient = await this.connectionManager.createSparkClient(operator.address);
2402
- const identityPublicKey = await this.config.signer.getIdentityPublicKey();
2403
- const payload = {
2404
- finalTokenTransactionHash,
2405
- operatorIdentityPublicKey: (0, import_utils7.hexToBytes)(operator.identityPublicKey)
2406
- };
2407
- const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
2408
- let operatorSpecificSignatures = [];
2409
- if (finalTokenTransaction.tokenInputs.$case === "mintInput") {
2410
- const issuerPublicKey = finalTokenTransaction.tokenInputs.mintInput.issuerPublicKey;
2411
- if (!issuerPublicKey) {
2412
- throw new ValidationError("Invalid mint input", {
2413
- field: "issuerPublicKey",
2414
- value: null,
2415
- expected: "Non-null issuer public key"
2416
- });
2417
- }
2418
- const ownerSignature = await this.signMessageWithKey(
2419
- payloadHash,
2420
- issuerPublicKey
2421
- );
2422
- operatorSpecificSignatures.push({
2423
- ownerSignature: {
2424
- signature: ownerSignature,
2425
- inputIndex: 0
2426
- },
2427
- payload
2428
- });
2429
- }
2430
- if (finalTokenTransaction.tokenInputs.$case === "transferInput") {
2431
- const transferInput = finalTokenTransaction.tokenInputs.transferInput;
2432
- for (let i = 0; i < transferInput.outputsToSpend.length; i++) {
2433
- let ownerSignature;
2434
- if (this.config.getTokenSignatures() === "SCHNORR") {
2435
- ownerSignature = await this.config.signer.signSchnorrWithIdentityKey(
2436
- payloadHash
2437
- );
2438
- } else {
2439
- ownerSignature = await this.config.signer.signMessageWithIdentityKey(
2440
- payloadHash
2441
- );
2442
- }
2443
- operatorSpecificSignatures.push({
2444
- ownerSignature: {
2445
- signature: ownerSignature,
2446
- inputIndex: i
2447
- },
2448
- payload
2449
- });
2450
- }
2451
- }
2452
- try {
2453
- const response = await internalSparkClient.sign_token_transaction(
2454
- {
2455
- finalTokenTransaction,
2456
- operatorSpecificSignatures,
2457
- identityPublicKey
2458
- },
2459
- {
2460
- retry: true,
2461
- retryableStatuses: [
2462
- "UNKNOWN",
2463
- "UNAVAILABLE",
2464
- "CANCELLED",
2465
- "INTERNAL"
2466
- ],
2467
- retryMaxAttempts: 3
2468
- }
2469
- );
2470
- return {
2471
- index,
2472
- identifier,
2473
- response
2474
- };
2475
- } catch (error) {
2476
- throw new NetworkError(
2477
- "Failed to sign token transaction",
2478
- {
2479
- operation: "sign_token_transaction",
2480
- errorCount: 1,
2481
- errors: error instanceof Error ? error.message : String(error)
2482
- },
2483
- error
2484
- );
2485
- }
2486
- }
2487
- )
2488
- );
2489
- const successfulSignatures = collectResponses(soSignatures);
2490
- return {
2491
- successfulSignatures
2492
- };
2493
- }
2494
- async signTokenTransaction(finalTokenTransaction, finalTokenTransactionHash, signingOperators) {
2495
- const coordinatorClient = await this.connectionManager.createSparkTokenClient(
2496
- this.config.getCoordinatorAddress()
2497
- );
2498
- const inputTtxoSignaturesPerOperator = await this.createSignaturesForOperators(
2499
- finalTokenTransaction,
2500
- finalTokenTransactionHash,
2501
- signingOperators
2502
- );
2503
- try {
2504
- await coordinatorClient.commit_transaction(
2505
- {
2506
- finalTokenTransaction,
2507
- finalTokenTransactionHash,
2508
- inputTtxoSignaturesPerOperator,
2509
- ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey()
2510
- },
2511
- {
2512
- retry: true,
2513
- retryableStatuses: [
2514
- "UNKNOWN",
2515
- "UNAVAILABLE",
2516
- "CANCELLED",
2517
- "INTERNAL"
2518
- ],
2519
- retryMaxAttempts: 3
2520
- }
2521
- );
2522
- } catch (error) {
2523
- throw new NetworkError(
2524
- "Failed to sign token transaction",
2525
- {
2526
- operation: "sign_token_transaction",
2527
- errorCount: 1,
2528
- errors: error instanceof Error ? error.message : String(error)
2529
- },
2530
- error
2531
- );
2532
- }
2533
- }
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;
2554
- const sparkClient = await this.connectionManager.createSparkClient(
2555
- this.config.getCoordinatorAddress()
2556
- );
2557
- try {
2558
- const result = await sparkClient.query_token_outputs({
2559
- ownerPublicKeys,
2560
- tokenPublicKeys,
2561
- tokenIdentifiers,
2562
- network: this.config.getNetworkProto()
2563
- });
2564
- return result.outputsWithPreviousTransactionData;
2565
- } catch (error) {
2566
- throw new NetworkError(
2567
- "Failed to fetch owned token outputs",
2568
- {
2569
- operation: "spark.query_token_outputs",
2570
- errorCount: 1,
2571
- errors: error instanceof Error ? error.message : String(error)
2572
- },
2573
- error
2574
- );
2575
- }
2576
- }
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()
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
- });
2698
- unsortedTokenOutputs.forEach((output) => {
2699
- const tokenKey = (0, import_utils6.bytesToHex)(output.output.tokenPublicKey);
2700
- const index = output.previousTransactionVout;
2701
- tokenOutputs.set(tokenKey, [
2702
- { ...output, previousTransactionVout: index }
2703
- ]);
2704
- });
2705
- }
2706
- selectTokenOutputs(tokenOutputs, tokenAmount, strategy) {
2707
- if (calculateAvailableTokenAmount(tokenOutputs) < tokenAmount) {
2708
- throw new ValidationError("Insufficient token amount", {
2709
- field: "tokenAmount",
2710
- value: calculateAvailableTokenAmount(tokenOutputs),
2711
- expected: tokenAmount
2712
- });
2713
- }
2714
- const exactMatch = tokenOutputs.find(
2715
- (item) => (0, import_utils6.bytesToNumberBE)(item.output.tokenAmount) === tokenAmount
2716
- );
2717
- if (exactMatch) {
2718
- return [exactMatch];
2719
- }
2720
- this.sortTokenOutputsByStrategy(tokenOutputs, strategy);
2721
- let remainingAmount = tokenAmount;
2722
- const selectedOutputs = [];
2723
- for (const outputWithPreviousTransactionData of tokenOutputs) {
2724
- if (remainingAmount <= 0n) break;
2725
- selectedOutputs.push(outputWithPreviousTransactionData);
2726
- remainingAmount -= (0, import_utils6.bytesToNumberBE)(
2727
- outputWithPreviousTransactionData.output.tokenAmount
2728
- );
2729
- }
2730
- if (remainingAmount > 0n) {
2731
- throw new ValidationError("Insufficient funds", {
2732
- field: "remainingAmount",
2733
- value: remainingAmount
2734
- });
2735
- }
2736
- return selectedOutputs;
2737
- }
2738
- sortTokenOutputsByStrategy(tokenOutputs, strategy) {
2739
- if (strategy === "SMALL_FIRST") {
2740
- tokenOutputs.sort((a, b) => {
2741
- return Number(
2742
- (0, import_utils6.bytesToNumberBE)(a.output.tokenAmount) - (0, import_utils6.bytesToNumberBE)(b.output.tokenAmount)
2743
- );
2744
- });
2745
- } else {
2746
- tokenOutputs.sort((a, b) => {
2747
- return Number(
2748
- (0, import_utils6.bytesToNumberBE)(b.output.tokenAmount) - (0, import_utils6.bytesToNumberBE)(a.output.tokenAmount)
2749
- );
2750
- });
2751
- }
2752
- }
2753
- // Helper function for deciding if the signer public key is the identity public key
2754
- async signMessageWithKey(message, publicKey) {
2755
- const tokenSignatures = this.config.getTokenSignatures();
2756
- if ((0, import_utils6.bytesToHex)(publicKey) === (0, import_utils6.bytesToHex)(await this.config.signer.getIdentityPublicKey())) {
2757
- if (tokenSignatures === "SCHNORR") {
2758
- return await this.config.signer.signSchnorrWithIdentityKey(message);
2759
- } else {
2760
- return await this.config.signer.signMessageWithIdentityKey(message);
2761
- }
2762
- } else {
2763
- if (tokenSignatures === "SCHNORR") {
2764
- return await this.config.signer.signSchnorr(message, publicKey);
2765
- } else {
2766
- return await this.config.signer.signMessageWithPublicKey(
2767
- message,
2768
- publicKey
2769
- );
2770
- }
2771
- }
2772
- }
2773
- async finalizeTokenTransaction(finalTokenTransaction, revocationSecrets, threshold) {
2774
- const signingOperators = this.config.getSigningOperators();
2775
- const soResponses = await Promise.allSettled(
2776
- Object.entries(signingOperators).map(async ([identifier, operator]) => {
2777
- const internalSparkClient = await this.connectionManager.createSparkClient(operator.address);
2778
- const identityPublicKey = await this.config.signer.getIdentityPublicKey();
2779
- try {
2780
- const response = await internalSparkClient.finalize_token_transaction(
2781
- {
2782
- finalTokenTransaction,
2783
- revocationSecrets,
2784
- identityPublicKey
2785
- },
2786
- {
2787
- retry: true,
2788
- retryableStatuses: [
2789
- "UNKNOWN",
2790
- "UNAVAILABLE",
2791
- "CANCELLED",
2792
- "INTERNAL"
2793
- ],
2794
- retryMaxAttempts: 3
2795
- }
2796
- );
2797
- return {
2798
- identifier,
2799
- response
2800
- };
2801
- } catch (error) {
2802
- throw new NetworkError(
2803
- "Failed to finalize token transaction",
2804
- {
2805
- operation: "finalize_token_transaction",
2806
- errorCount: 1,
2807
- errors: error instanceof Error ? error.message : String(error)
2808
- },
2809
- error
2810
- );
2811
- }
2812
- })
2813
- );
2814
- collectResponses(soResponses);
2815
- return finalTokenTransaction;
2816
- }
2817
- async createSignaturesForOperators(finalTokenTransaction, finalTokenTransactionHash, signingOperators) {
2818
- const inputTtxoSignaturesPerOperator = [];
2819
- for (const [_, operator] of Object.entries(signingOperators)) {
2820
- let ttxoSignatures = [];
2821
- if (finalTokenTransaction.tokenInputs.$case === "mintInput") {
2822
- const issuerPublicKey = finalTokenTransaction.tokenInputs.mintInput.issuerPublicKey;
2823
- if (!issuerPublicKey) {
2824
- throw new ValidationError("Invalid mint input", {
2825
- field: "issuerPublicKey",
2826
- value: null,
2827
- expected: "Non-null issuer public key"
2828
- });
2829
- }
2830
- const payload = {
2831
- finalTokenTransactionHash,
2832
- operatorIdentityPublicKey: (0, import_utils7.hexToBytes)(operator.identityPublicKey)
2833
- };
2834
- const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
2835
- const ownerSignature = await this.signMessageWithKey(
2836
- payloadHash,
2837
- issuerPublicKey
2838
- );
2839
- ttxoSignatures.push({
2840
- signature: ownerSignature,
2841
- inputIndex: 0
2842
- });
2843
- } else if (finalTokenTransaction.tokenInputs.$case === "transferInput") {
2844
- const transferInput = finalTokenTransaction.tokenInputs.transferInput;
2845
- for (let i = 0; i < transferInput.outputsToSpend.length; i++) {
2846
- const payload = {
2847
- finalTokenTransactionHash,
2848
- operatorIdentityPublicKey: (0, import_utils7.hexToBytes)(operator.identityPublicKey)
2849
- };
2850
- const payloadHash = await hashOperatorSpecificTokenTransactionSignablePayload(payload);
2851
- let ownerSignature;
2852
- if (this.config.getTokenSignatures() === "SCHNORR") {
2853
- ownerSignature = await this.config.signer.signSchnorrWithIdentityKey(payloadHash);
2854
- } else {
2855
- ownerSignature = await this.config.signer.signMessageWithIdentityKey(payloadHash);
2856
- }
2857
- ttxoSignatures.push({
2858
- signature: ownerSignature,
2859
- inputIndex: i
2860
- });
2861
- }
2862
- }
2863
- inputTtxoSignaturesPerOperator.push({
2864
- ttxoSignatures,
2865
- operatorIdentityPublicKey: (0, import_utils7.hexToBytes)(operator.identityPublicKey)
2866
- });
2867
- }
2868
- return inputTtxoSignaturesPerOperator;
2869
- }
2870
- };
2871
- function isTokenTransaction(tokenTransaction) {
2872
- return "version" in tokenTransaction && "expiryTime" in tokenTransaction;
2873
- }
2874
- // Annotate the CommonJS export names for ESM import in node:
2875
- 0 && (module.exports = {
2876
- TokenTransactionService
2877
- });