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