@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.
- package/CHANGELOG.md +22 -0
- package/dist/{chunk-I54FARY2.js → chunk-EAP3U3CW.js} +14 -14
- package/dist/chunk-GWFQ7EBA.js +3773 -0
- package/dist/{chunk-J2IE4Z7Y.js → chunk-NNX4OK44.js} +3487 -934
- package/dist/{RequestLightningSendInput-Du0z7Om7.d.cts → client-CvpTRpcw.d.cts} +422 -212
- package/dist/{RequestLightningSendInput-DEPd_fPO.d.ts → client-D7KgLN44.d.ts} +422 -212
- package/dist/graphql/objects/index.d.cts +5 -9
- package/dist/graphql/objects/index.d.ts +5 -9
- package/dist/graphql/objects/index.js +1 -1
- package/dist/index.cjs +20461 -23377
- package/dist/index.d.cts +15 -769
- package/dist/index.d.ts +15 -769
- package/dist/index.js +81 -71
- package/dist/index.node.cjs +21994 -25018
- package/dist/index.node.d.cts +312 -34
- package/dist/index.node.d.ts +312 -34
- package/dist/index.node.js +82 -176
- package/dist/native/index.cjs +22847 -25841
- package/dist/native/index.d.cts +974 -1138
- package/dist/native/index.d.ts +974 -1138
- package/dist/native/index.js +10604 -13592
- package/dist/proto/lrc20.d.cts +2 -2
- package/dist/proto/lrc20.d.ts +2 -2
- package/dist/proto/lrc20.js +3098 -46
- package/dist/proto/spark.d.cts +1 -1
- package/dist/proto/spark.d.ts +1 -1
- package/dist/proto/spark_token.d.cts +1 -1
- package/dist/proto/spark_token.d.ts +1 -1
- package/dist/{sdk-types-Cc4l4kb1.d.ts → sdk-types-BGCeea0G.d.ts} +1 -1
- package/dist/{sdk-types-B0SwjolI.d.cts → sdk-types-XUeQMLFP.d.cts} +1 -1
- package/dist/{spark-dM7EYXYQ.d.cts → spark-BbUrbvZz.d.cts} +1 -1
- package/dist/{spark-dM7EYXYQ.d.ts → spark-BbUrbvZz.d.ts} +1 -1
- package/dist/spark-wallet-BAFPpPtY.d.cts +923 -0
- package/dist/spark-wallet-CJkQW8pK.d.ts +923 -0
- package/dist/spark_bindings/native/index.d.cts +1 -1
- package/dist/spark_bindings/native/index.d.ts +1 -1
- package/dist/spark_bindings/wasm/index.d.cts +1 -1
- package/dist/spark_bindings/wasm/index.d.ts +1 -1
- package/dist/{services/index.cjs → tests/test-utils.cjs} +2512 -4380
- package/dist/tests/test-utils.d.cts +79 -0
- package/dist/tests/test-utils.d.ts +79 -0
- package/dist/tests/test-utils.js +85 -0
- package/dist/types/index.d.cts +5 -9
- package/dist/types/index.d.ts +5 -9
- package/dist/types/index.js +5 -5
- package/dist/{types-C-Rp0Oo7.d.cts → types-BADxR3bm.d.cts} +1 -1
- package/dist/{types-C-Rp0Oo7.d.ts → types-BADxR3bm.d.ts} +1 -1
- package/package.json +7 -35
- package/src/graphql/client.ts +59 -20
- package/src/index.node.ts +28 -2
- package/src/index.ts +31 -1
- package/src/native/index.ts +16 -2
- package/src/services/config.ts +4 -6
- package/src/services/connection.ts +131 -64
- package/src/services/lightning.ts +1 -2
- package/src/services/token-transactions.ts +7 -7
- package/src/services/transfer.ts +1 -1
- package/src/services/tree-creation.ts +1 -1
- package/src/services/wallet-config.ts +18 -10
- package/src/signer/signer.react-native.ts +2 -5
- package/src/signer/signer.ts +138 -64
- package/src/signer/types.ts +52 -0
- package/src/spark-wallet/spark-wallet.ts +79 -36
- package/src/spark-wallet/types.ts +4 -4
- package/src/tests/integration/coop-exit.test.ts +2 -1
- package/src/tests/integration/lightning.test.ts +2 -2
- package/src/tests/integration/swap.test.ts +1 -1
- package/src/tests/integration/transfer.test.ts +5 -5
- package/src/tests/integration/tree-creation.test.ts +1 -1
- package/src/tests/integration/wallet.test.ts +1 -0
- package/src/tests/isHermeticTest.ts +3 -24
- package/src/tests/{test-util.ts → test-utils.ts} +3 -7
- package/src/tests/wrapWithOtelSpan.test.ts +1 -1
- package/src/{address → utils}/address.ts +1 -1
- package/src/utils/crypto.ts +19 -9
- package/src/utils/index.ts +2 -0
- package/src/utils/network.ts +17 -0
- package/src/utils/secret-sharing.ts +1 -2
- package/src/utils/signing.ts +1 -1
- package/src/utils/token-transactions.ts +3 -3
- package/src/utils/unilateral-exit.ts +32 -0
- package/src/utils/xchain-address.ts +1 -1
- package/dist/BitcoinNetwork-TnABML0T.d.cts +0 -18
- package/dist/BitcoinNetwork-TnABML0T.d.ts +0 -18
- package/dist/LightningSendFeeEstimateInput-BgOhEAI-.d.cts +0 -10
- package/dist/LightningSendFeeEstimateInput-BgOhEAI-.d.ts +0 -10
- package/dist/address/index.cjs +0 -458
- package/dist/address/index.d.cts +0 -32
- package/dist/address/index.d.ts +0 -32
- package/dist/address/index.js +0 -17
- package/dist/chunk-5FUB65LX.js +0 -838
- package/dist/chunk-6264CGDM.js +0 -113
- package/dist/chunk-7V6N75CC.js +0 -24
- package/dist/chunk-C2S227QR.js +0 -2336
- package/dist/chunk-GSI4OLXZ.js +0 -117
- package/dist/chunk-GZ5IPPJ2.js +0 -170
- package/dist/chunk-HWJWKEIU.js +0 -75
- package/dist/chunk-KMUMFYFX.js +0 -137
- package/dist/chunk-L3EHBOUX.js +0 -0
- package/dist/chunk-NSJF5F5O.js +0 -325
- package/dist/chunk-NTFKFRQ2.js +0 -3146
- package/dist/chunk-PQN3C2MF.js +0 -1122
- package/dist/chunk-QNNSEJ4P.js +0 -232
- package/dist/chunk-R5PXJZQS.js +0 -277
- package/dist/chunk-VTUGIIWI.js +0 -0
- package/dist/chunk-YUPMXTCJ.js +0 -622
- package/dist/chunk-Z5HIAYFT.js +0 -84
- package/dist/index-B2AwKW5J.d.cts +0 -214
- package/dist/index-CJDi1HWc.d.ts +0 -214
- package/dist/network-BTJl-Sul.d.ts +0 -46
- package/dist/network-CqgsdUF2.d.cts +0 -46
- package/dist/services/config.cjs +0 -2354
- package/dist/services/config.d.cts +0 -42
- package/dist/services/config.d.ts +0 -42
- package/dist/services/config.js +0 -17
- package/dist/services/connection.cjs +0 -17691
- package/dist/services/connection.d.cts +0 -95
- package/dist/services/connection.d.ts +0 -95
- package/dist/services/connection.js +0 -11
- package/dist/services/index.d.cts +0 -21
- package/dist/services/index.d.ts +0 -21
- package/dist/services/index.js +0 -58
- package/dist/services/lrc-connection.cjs +0 -4713
- package/dist/services/lrc-connection.d.cts +0 -34
- package/dist/services/lrc-connection.d.ts +0 -34
- package/dist/services/lrc-connection.js +0 -11
- package/dist/services/token-transactions.cjs +0 -2877
- package/dist/services/token-transactions.d.cts +0 -75
- package/dist/services/token-transactions.d.ts +0 -75
- package/dist/services/token-transactions.js +0 -15
- package/dist/services/wallet-config.cjs +0 -340
- package/dist/services/wallet-config.d.cts +0 -56
- package/dist/services/wallet-config.d.ts +0 -56
- package/dist/services/wallet-config.js +0 -33
- package/dist/signer/signer.cjs +0 -2004
- package/dist/signer/signer.d.cts +0 -10
- package/dist/signer/signer.d.ts +0 -10
- package/dist/signer/signer.js +0 -24
- package/dist/signer-BocS_J6B.d.ts +0 -187
- package/dist/signer-DKS0AJkw.d.cts +0 -187
- package/dist/utils/index.cjs +0 -2947
- package/dist/utils/index.d.cts +0 -18
- package/dist/utils/index.d.ts +0 -18
- package/dist/utils/index.js +0 -157
- package/src/address/index.ts +0 -1
- 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
|
-
});
|