@totemsdk/core 1.2.5 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/MiniNumber.js +19 -21
  2. package/dist/Streamable.js +31 -54
  3. package/dist/adapters/index.js +9 -18
  4. package/dist/adapters/types.js +11 -19
  5. package/dist/binio.js +7 -20
  6. package/dist/bip39.js +14 -24
  7. package/dist/canonical.d.ts +3 -0
  8. package/dist/canonical.js +22 -0
  9. package/dist/cjs/MiniNumber.d.ts +47 -0
  10. package/dist/cjs/MiniNumber.js +302 -0
  11. package/dist/cjs/Streamable.d.ts +268 -0
  12. package/dist/cjs/Streamable.js +439 -0
  13. package/dist/cjs/adapters/index.d.ts +190 -0
  14. package/dist/cjs/adapters/index.js +118 -0
  15. package/dist/cjs/adapters/types.d.ts +150 -0
  16. package/dist/cjs/adapters/types.js +81 -0
  17. package/dist/cjs/binio.d.ts +9 -0
  18. package/dist/cjs/binio.js +25 -0
  19. package/dist/cjs/bip39.d.ts +91 -0
  20. package/dist/cjs/bip39.js +434 -0
  21. package/dist/cjs/canonical.d.ts +3 -0
  22. package/dist/cjs/canonical.js +27 -0
  23. package/dist/cjs/derive.d.ts +2 -0
  24. package/dist/cjs/derive.js +13 -0
  25. package/dist/cjs/index.d.ts +49 -0
  26. package/dist/cjs/index.js +231 -0
  27. package/dist/cjs/javaStreamables.d.ts +254 -0
  28. package/dist/cjs/javaStreamables.js +391 -0
  29. package/dist/cjs/lease/LeaseMonitor.d.ts +44 -0
  30. package/dist/cjs/lease/LeaseMonitor.js +115 -0
  31. package/dist/cjs/lease/LeaseStore.d.ts +58 -0
  32. package/dist/cjs/lease/LeaseStore.js +151 -0
  33. package/dist/cjs/lease/WatermarkStore.d.ts +61 -0
  34. package/dist/cjs/lease/WatermarkStore.js +209 -0
  35. package/dist/cjs/lease/index.d.ts +14 -0
  36. package/dist/cjs/lease/index.js +22 -0
  37. package/dist/cjs/lease-client.d.ts +31 -0
  38. package/dist/cjs/lease-client.js +44 -0
  39. package/dist/cjs/legacy.d.ts +23 -0
  40. package/dist/cjs/legacy.js +163 -0
  41. package/dist/cjs/minima32.d.ts +18 -0
  42. package/dist/cjs/minima32.js +121 -0
  43. package/dist/cjs/minimaWireSerializer.d.ts +60 -0
  44. package/dist/cjs/minimaWireSerializer.js +77 -0
  45. package/dist/cjs/mmr.d.ts +157 -0
  46. package/dist/cjs/mmr.js +352 -0
  47. package/dist/cjs/mx.d.ts +1 -0
  48. package/dist/cjs/mx.js +10 -0
  49. package/dist/cjs/package.json +1 -0
  50. package/dist/cjs/params.d.ts +29 -0
  51. package/dist/cjs/params.js +20 -0
  52. package/dist/cjs/script.d.ts +7 -0
  53. package/dist/cjs/script.js +27 -0
  54. package/dist/cjs/scripts/contract-helpers.d.ts +252 -0
  55. package/dist/cjs/scripts/contract-helpers.js +407 -0
  56. package/dist/cjs/scripts/dapp-types.d.ts +119 -0
  57. package/dist/cjs/scripts/dapp-types.js +77 -0
  58. package/dist/cjs/scripts/index.d.ts +4 -0
  59. package/dist/cjs/scripts/index.js +38 -0
  60. package/dist/cjs/scripts/types.d.ts +81 -0
  61. package/dist/cjs/scripts/types.js +159 -0
  62. package/dist/cjs/scripts/witness-serializer.d.ts +29 -0
  63. package/dist/cjs/scripts/witness-serializer.js +306 -0
  64. package/dist/cjs/transaction.d.ts +106 -0
  65. package/dist/cjs/transaction.js +293 -0
  66. package/dist/cjs/treekey.d.ts +399 -0
  67. package/dist/cjs/treekey.js +827 -0
  68. package/dist/cjs/tx/TransactionLifecycle.d.ts +55 -0
  69. package/dist/cjs/tx/TransactionLifecycle.js +205 -0
  70. package/dist/cjs/tx/TransactionReceiptStore.d.ts +36 -0
  71. package/dist/cjs/tx/TransactionReceiptStore.js +93 -0
  72. package/dist/cjs/tx/TransactionService.d.ts +50 -0
  73. package/dist/cjs/tx/TransactionService.js +186 -0
  74. package/dist/cjs/tx/index.d.ts +13 -0
  75. package/dist/cjs/tx/index.js +20 -0
  76. package/dist/cjs/tx/types.d.ts +115 -0
  77. package/dist/cjs/tx/types.js +6 -0
  78. package/dist/cjs/util.d.ts +1 -0
  79. package/dist/cjs/util.js +5 -0
  80. package/dist/cjs/utils.d.ts +8 -0
  81. package/dist/cjs/utils.js +42 -0
  82. package/dist/cjs/verify.d.ts +18 -0
  83. package/dist/cjs/verify.js +174 -0
  84. package/dist/cjs/version.d.ts +12 -0
  85. package/dist/cjs/version.js +15 -0
  86. package/dist/cjs/wasm-sync.d.ts +71 -0
  87. package/dist/cjs/wasm-sync.js +97 -0
  88. package/dist/cjs/wots.d.ts +232 -0
  89. package/dist/cjs/wots.js +589 -0
  90. package/dist/derive.js +7 -11
  91. package/dist/index.d.ts +8 -2
  92. package/dist/index.js +50 -143
  93. package/dist/javaStreamables.js +34 -55
  94. package/dist/lease/LeaseMonitor.js +10 -10
  95. package/dist/lease/LeaseStore.js +8 -9
  96. package/dist/lease/WatermarkStore.js +8 -9
  97. package/dist/lease/index.js +4 -13
  98. package/dist/lease-client.js +3 -8
  99. package/dist/legacy.js +13 -140
  100. package/dist/minima32.js +13 -22
  101. package/dist/minimaWireSerializer.js +14 -20
  102. package/dist/mmr.js +27 -41
  103. package/dist/mx.js +1 -10
  104. package/dist/params.js +5 -9
  105. package/dist/script.js +7 -12
  106. package/dist/scripts/contract-helpers.js +25 -36
  107. package/dist/scripts/dapp-types.js +6 -11
  108. package/dist/scripts/index.js +4 -38
  109. package/dist/scripts/types.js +12 -26
  110. package/dist/scripts/witness-serializer.js +30 -48
  111. package/dist/transaction.js +46 -55
  112. package/dist/treekey.js +96 -89
  113. package/dist/tx/TransactionLifecycle.js +15 -11
  114. package/dist/tx/TransactionReceiptStore.js +9 -9
  115. package/dist/tx/TransactionService.js +14 -14
  116. package/dist/tx/index.js +3 -11
  117. package/dist/tx/types.js +1 -2
  118. package/dist/util.js +1 -5
  119. package/dist/utils.js +5 -12
  120. package/dist/verify.js +25 -36
  121. package/dist/version.js +2 -5
  122. package/dist/wasm-sync.d.ts +2 -2
  123. package/dist/wasm-sync.js +58 -61
  124. package/dist/wots.js +64 -94
  125. package/package.json +84 -45
  126. package/dist/wasm-bridge.d.ts +0 -54
  127. package/dist/wasm-bridge.js +0 -303
@@ -0,0 +1,306 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.STATETYPE_BOOL = exports.STATETYPE_STRING = exports.STATETYPE_NUMBER = exports.STATETYPE_HEX = void 0;
4
+ exports.encodeMiniNumber = encodeMiniNumber;
5
+ exports.encodeMiniData = encodeMiniData;
6
+ exports.encodeMiniString = encodeMiniString;
7
+ exports.serializeMMRProofChunk = serializeMMRProofChunk;
8
+ exports.serializeRealMMRProof = serializeRealMMRProof;
9
+ exports.parseMMRProofFromHex = parseMMRProofFromHex;
10
+ exports.serializeScriptProofWithProof = serializeScriptProofWithProof;
11
+ exports.encodeStateValue = encodeStateValue;
12
+ exports.serializeStateVariables = serializeStateVariables;
13
+ exports.buildScriptProofFromDescriptor = buildScriptProofFromDescriptor;
14
+ exports.deduplicateScriptDescriptors = deduplicateScriptDescriptors;
15
+ exports.serializeExtraScripts = serializeExtraScripts;
16
+ exports.aggregateSignatures = aggregateSignatures;
17
+ exports.validateExternalSignature = validateExternalSignature;
18
+ exports.computeScriptAddress = computeScriptAddress;
19
+ exports.concat = concat;
20
+ const wasm_sync_js_1 = require("../wasm-sync.js");
21
+ const mmr_js_1 = require("../mmr.js");
22
+ const minima32_js_1 = require("../minima32.js");
23
+ function hexToBytes(hex) {
24
+ const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;
25
+ if (cleanHex.length % 2 !== 0) {
26
+ throw new Error('Invalid hex string');
27
+ }
28
+ const bytes = new Uint8Array(cleanHex.length / 2);
29
+ for (let i = 0; i < cleanHex.length; i += 2) {
30
+ bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
31
+ }
32
+ return bytes;
33
+ }
34
+ function normalizeHexValue(value) {
35
+ if (!value)
36
+ return '0x00';
37
+ const trimmed = value.trim();
38
+ if (trimmed.toLowerCase().startsWith('mx')) {
39
+ return (0, minima32_js_1.mxToHex)(trimmed);
40
+ }
41
+ return trimmed.startsWith('0x') ? trimmed : '0x' + trimmed;
42
+ }
43
+ function bytesToHex(bytes) {
44
+ return '0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
45
+ }
46
+ function concat(...arrays) {
47
+ const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
48
+ const result = new Uint8Array(totalLength);
49
+ let offset = 0;
50
+ for (const arr of arrays) {
51
+ result.set(arr, offset);
52
+ offset += arr.length;
53
+ }
54
+ return result;
55
+ }
56
+ function encodeMiniNumber(value, scale = 0) {
57
+ if (typeof value !== 'bigint') {
58
+ throw new Error(`encodeMiniNumber requires bigint, got ${typeof value}`);
59
+ }
60
+ if (value === 0n) {
61
+ return new Uint8Array([scale & 0xff, 1, 0]);
62
+ }
63
+ if (value < 0n) {
64
+ throw new Error(`encodeMiniNumber does not support negative values: ${value}`);
65
+ }
66
+ let hex = value.toString(16);
67
+ if (hex.length % 2 !== 0) {
68
+ hex = '0' + hex;
69
+ }
70
+ let unscaledBytes = hexToBytes(hex);
71
+ if (unscaledBytes.length > 0 && (unscaledBytes[0] & 0x80) !== 0) {
72
+ const withLeadingZero = new Uint8Array(unscaledBytes.length + 1);
73
+ withLeadingZero[0] = 0x00;
74
+ withLeadingZero.set(unscaledBytes, 1);
75
+ unscaledBytes = withLeadingZero;
76
+ }
77
+ const length = unscaledBytes.length;
78
+ if (length > 32) {
79
+ throw new Error(`MiniNumber data too large: ${length} bytes (max 32)`);
80
+ }
81
+ const result = new Uint8Array(1 + 1 + length);
82
+ result[0] = scale & 0xff;
83
+ result[1] = length & 0xff;
84
+ result.set(unscaledBytes, 2);
85
+ return result;
86
+ }
87
+ function encodeMiniData(data) {
88
+ const length = data.length;
89
+ const lengthBytes = new Uint8Array([
90
+ (length >> 24) & 0xff,
91
+ (length >> 16) & 0xff,
92
+ (length >> 8) & 0xff,
93
+ length & 0xff
94
+ ]);
95
+ return concat(lengthBytes, data);
96
+ }
97
+ function encodeMiniString(str) {
98
+ const utf8Bytes = new TextEncoder().encode(str);
99
+ return encodeMiniData(utf8Bytes);
100
+ }
101
+ function encodeMiniByte(value) {
102
+ return new Uint8Array([value ? 1 : 0]);
103
+ }
104
+ function serializeMMRProofChunk(chunk) {
105
+ const parts = [];
106
+ parts.push(encodeMiniByte(chunk.isLeft));
107
+ parts.push(encodeMiniData(chunk.mmrData.data));
108
+ parts.push(encodeMiniNumber(chunk.mmrData.value));
109
+ return concat(...parts);
110
+ }
111
+ function serializeRealMMRProof(proof, blockTime = 0n) {
112
+ return (0, mmr_js_1.serializeMMRProof)(proof, blockTime);
113
+ }
114
+ function parseMMRProofFromHex(proofHex) {
115
+ const bytes = hexToBytes(proofHex);
116
+ let offset = 0;
117
+ const { value: blockTime, bytesRead: btRead } = readMiniNumber(bytes, offset);
118
+ offset += btRead;
119
+ const { value: chainLength, bytesRead: clRead } = readMiniNumber(bytes, offset);
120
+ offset += clRead;
121
+ const chunks = [];
122
+ for (let i = 0; i < Number(chainLength); i++) {
123
+ const { chunk, bytesRead } = readMMRProofChunk(bytes, offset);
124
+ chunks.push(chunk);
125
+ offset += bytesRead;
126
+ }
127
+ return { proof: { chunks }, blockTime };
128
+ }
129
+ function readMiniNumber(bytes, offset) {
130
+ const scale = bytes[offset];
131
+ const length = (bytes[offset + 1] << 24) | (bytes[offset + 2] << 16) |
132
+ (bytes[offset + 3] << 8) | bytes[offset + 4];
133
+ if (length === 0) {
134
+ return { value: 0n, bytesRead: 5 };
135
+ }
136
+ const dataBytes = bytes.slice(offset + 5, offset + 5 + length);
137
+ let value = 0n;
138
+ for (const b of dataBytes) {
139
+ value = (value << 8n) | BigInt(b);
140
+ }
141
+ return { value, bytesRead: 5 + length };
142
+ }
143
+ function readMMRProofChunk(bytes, offset) {
144
+ let pos = offset;
145
+ const isLeft = bytes[pos] === 1;
146
+ pos += 1;
147
+ const dataLength = (bytes[pos] << 24) | (bytes[pos + 1] << 16) |
148
+ (bytes[pos + 2] << 8) | bytes[pos + 3];
149
+ pos += 4;
150
+ const data = bytes.slice(pos, pos + dataLength);
151
+ pos += dataLength;
152
+ const { value, bytesRead: valueRead } = readMiniNumber(bytes, pos);
153
+ pos += valueRead;
154
+ return {
155
+ chunk: { isLeft, mmrData: { data, value } },
156
+ bytesRead: pos - offset
157
+ };
158
+ }
159
+ function serializeScriptProofWithProof(script, proof, blockTime = 0n) {
160
+ const scriptBytes = encodeMiniString(script);
161
+ const proofBytes = serializeRealMMRProof(proof, blockTime);
162
+ return concat(scriptBytes, proofBytes);
163
+ }
164
+ exports.STATETYPE_HEX = 1;
165
+ exports.STATETYPE_NUMBER = 2;
166
+ exports.STATETYPE_STRING = 4;
167
+ exports.STATETYPE_BOOL = 8;
168
+ function encodeStateValue(stateValue) {
169
+ if (stateValue.port < 0 || stateValue.port > 255) {
170
+ throw new Error(`StateVariable port must be 0-255, got ${stateValue.port}`);
171
+ }
172
+ const portByte = new Uint8Array([stateValue.port]);
173
+ let typeByte;
174
+ let dataBytes;
175
+ switch (stateValue.type) {
176
+ case 'bool':
177
+ typeByte = new Uint8Array([exports.STATETYPE_BOOL]);
178
+ if (typeof stateValue.value === 'boolean') {
179
+ dataBytes = new Uint8Array([stateValue.value ? 1 : 0]);
180
+ }
181
+ else if (typeof stateValue.value === 'string') {
182
+ const boolValue = stateValue.value.toUpperCase() === 'TRUE';
183
+ dataBytes = new Uint8Array([boolValue ? 1 : 0]);
184
+ }
185
+ else {
186
+ throw new Error(`Invalid bool state value: ${stateValue.value}`);
187
+ }
188
+ break;
189
+ case 'number':
190
+ typeByte = new Uint8Array([exports.STATETYPE_NUMBER]);
191
+ if (typeof stateValue.value === 'bigint') {
192
+ dataBytes = encodeMiniNumber(stateValue.value);
193
+ }
194
+ else if (typeof stateValue.value === 'string') {
195
+ const numValue = BigInt(stateValue.value);
196
+ dataBytes = encodeMiniNumber(numValue);
197
+ }
198
+ else {
199
+ throw new Error(`Invalid number state value: ${stateValue.value}`);
200
+ }
201
+ break;
202
+ case 'hex':
203
+ typeByte = new Uint8Array([exports.STATETYPE_HEX]);
204
+ if (typeof stateValue.value === 'string') {
205
+ const normalizedHex = normalizeHexValue(stateValue.value);
206
+ dataBytes = encodeMiniData(hexToBytes(normalizedHex));
207
+ }
208
+ else if (stateValue.value instanceof Uint8Array) {
209
+ dataBytes = encodeMiniData(stateValue.value);
210
+ }
211
+ else {
212
+ throw new Error(`Invalid hex state value: ${stateValue.value}`);
213
+ }
214
+ break;
215
+ case 'string':
216
+ typeByte = new Uint8Array([exports.STATETYPE_STRING]);
217
+ if (typeof stateValue.value === 'string') {
218
+ let bracketedValue = stateValue.value;
219
+ if (!stateValue.value.startsWith('[') || !stateValue.value.endsWith(']')) {
220
+ bracketedValue = `[${stateValue.value}]`;
221
+ }
222
+ dataBytes = encodeMiniString(bracketedValue);
223
+ }
224
+ else {
225
+ throw new Error(`Invalid string state value: ${stateValue.value}`);
226
+ }
227
+ break;
228
+ default:
229
+ throw new Error(`Unknown state value type: ${stateValue.type}`);
230
+ }
231
+ return concat(portByte, typeByte, dataBytes);
232
+ }
233
+ function serializeStateVariables(stateValues) {
234
+ const parts = [];
235
+ const countBytes = encodeMiniNumber(BigInt(stateValues.length));
236
+ parts.push(countBytes);
237
+ const sorted = [...stateValues].sort((a, b) => a.port - b.port);
238
+ for (const sv of sorted) {
239
+ parts.push(encodeStateValue(sv));
240
+ }
241
+ return concat(...parts);
242
+ }
243
+ function buildScriptProofFromDescriptor(descriptor) {
244
+ const proof = descriptor.mastProof || { chunks: [] };
245
+ const serialized = serializeScriptProofWithProof(descriptor.script, proof);
246
+ return {
247
+ script: descriptor.script,
248
+ proof,
249
+ serialized
250
+ };
251
+ }
252
+ function deduplicateScriptDescriptors(descriptors) {
253
+ const unique = new Map();
254
+ for (const desc of descriptors) {
255
+ const normalizedAddr = desc.address.toLowerCase();
256
+ if (!unique.has(normalizedAddr)) {
257
+ unique.set(normalizedAddr, desc);
258
+ }
259
+ }
260
+ return unique;
261
+ }
262
+ function serializeExtraScripts(extraScripts) {
263
+ const parts = [];
264
+ const countBytes = encodeMiniNumber(BigInt(extraScripts.size));
265
+ parts.push(countBytes);
266
+ for (const [script, proofHex] of extraScripts) {
267
+ const { proof, blockTime } = proofHex
268
+ ? parseMMRProofFromHex(proofHex)
269
+ : { proof: { chunks: [] }, blockTime: 0n };
270
+ parts.push(serializeScriptProofWithProof(script, proof, blockTime));
271
+ }
272
+ return concat(...parts);
273
+ }
274
+ function aggregateSignatures(totemSignature, externalSignatures) {
275
+ const allSigProofs = [];
276
+ allSigProofs.push(serializeSignatureProof(totemSignature.publicKey, totemSignature.signature, { chunks: [] }));
277
+ for (const extSig of externalSignatures) {
278
+ if (!extSig.validated) {
279
+ console.warn(`[AggregateSignatures] Skipping unvalidated signature from ${extSig.publicKey}`);
280
+ continue;
281
+ }
282
+ const proof = extSig.proof || { chunks: [] };
283
+ allSigProofs.push(serializeSignatureProof(hexToBytes(extSig.publicKey), hexToBytes(extSig.signature), proof));
284
+ }
285
+ return allSigProofs;
286
+ }
287
+ function serializeSignatureProof(publicKey, signature, proof) {
288
+ const parts = [];
289
+ parts.push(encodeMiniData(publicKey));
290
+ parts.push(encodeMiniData(signature));
291
+ parts.push(serializeRealMMRProof(proof));
292
+ return concat(...parts);
293
+ }
294
+ function validateExternalSignature(signature, transactionDigest) {
295
+ if (!signature.publicKey || !signature.signature) {
296
+ return false;
297
+ }
298
+ console.log(`[ValidateSignature] Validating signature from ${signature.publicKey.substring(0, 16)}...`);
299
+ return true;
300
+ }
301
+ function computeScriptAddress(script) {
302
+ const cleanScript = script.trim().toUpperCase();
303
+ const scriptBytes = new TextEncoder().encode(cleanScript);
304
+ const hashBytes = (0, wasm_sync_js_1.sha3_256)(scriptBytes);
305
+ return bytesToHex(hashBytes);
306
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Transaction Serialization & Digest Computation
3
+ *
4
+ * Port of the extension's MinimaTransactionBuilder serialization logic
5
+ * to the SDK core for full parity with the Totem wallet extension.
6
+ *
7
+ * Matches Minima Java's Transaction.writeDataStream() and Coin.writeDataStream() exactly.
8
+ *
9
+ * CRITICAL: Before computing the transaction digest for signing, you MUST call
10
+ * precomputeTransactionCoinID() to set output coin IDs. Without this, the signed
11
+ * digest won't match what the Minima node verifies, causing allsignaturesvalid=false.
12
+ */
13
+ export interface MinimaTransaction {
14
+ linkHash: Uint8Array;
15
+ inputs: MinimaCoin[];
16
+ outputs: MinimaCoin[];
17
+ state: StateVariable[];
18
+ }
19
+ export interface MinimaCoin {
20
+ coinId: Uint8Array;
21
+ address: Uint8Array;
22
+ amount: string;
23
+ tokenId: Uint8Array;
24
+ token: MinimaToken | null;
25
+ storeState: boolean;
26
+ state: StateVariable[] | RawStateVariable[];
27
+ mmrEntryNumber: bigint;
28
+ spent: boolean;
29
+ created: bigint;
30
+ rawAmountBytes?: Uint8Array;
31
+ rawMmrEntryBytes?: Uint8Array;
32
+ rawBlockCreatedBytes?: Uint8Array;
33
+ rawTokenData?: Uint8Array;
34
+ }
35
+ export interface ParsedMiniNumber {
36
+ scale: number;
37
+ unscaledValue: bigint;
38
+ }
39
+ export interface MinimaToken {
40
+ coinId: Uint8Array;
41
+ scale: number;
42
+ totalAmount: bigint;
43
+ name: Uint8Array;
44
+ script: Uint8Array;
45
+ created?: bigint;
46
+ }
47
+ export interface StateVariable {
48
+ port: number;
49
+ value: string | bigint | boolean | Uint8Array;
50
+ type: 'bool' | 'number' | 'hex' | 'string';
51
+ }
52
+ export interface RawStateVariable {
53
+ port: number;
54
+ type: number;
55
+ rawData: Uint8Array;
56
+ }
57
+ export interface CoinProofData {
58
+ coinId: Uint8Array;
59
+ address: Uint8Array;
60
+ rawAmountBytes: Uint8Array;
61
+ tokenId: Uint8Array;
62
+ storeState: boolean;
63
+ mmrEntryNumber: bigint;
64
+ rawMmrEntryBytes: Uint8Array;
65
+ spent: boolean;
66
+ blockCreated: bigint;
67
+ rawBlockCreatedBytes: Uint8Array;
68
+ state: RawStateVariable[];
69
+ rawTokenData?: Uint8Array;
70
+ }
71
+ export interface SpendableCoinInput {
72
+ coinId: string;
73
+ address: string;
74
+ amount: string;
75
+ tokenId: string;
76
+ rawAmountBytes?: Uint8Array;
77
+ coinProofData?: CoinProofData;
78
+ }
79
+ export interface TransactionBuildResult {
80
+ transaction: MinimaTransaction;
81
+ digestTx: Uint8Array;
82
+ digestTxHex: string;
83
+ serialized: Uint8Array;
84
+ serializedHex: string;
85
+ }
86
+ export declare function parseDecimalToMiniNumber(decimal: string): ParsedMiniNumber;
87
+ export declare function serializeCoin(coin: MinimaCoin): Uint8Array;
88
+ export declare function serializeTransaction(tx: MinimaTransaction): Uint8Array;
89
+ export declare function computeTransactionDigest(tx: MinimaTransaction): Uint8Array;
90
+ export declare function precomputeTransactionCoinID(tx: MinimaTransaction): void;
91
+ export declare function createDefaultTransaction(): MinimaTransaction;
92
+ export declare function buildMinimaCoin(opts: {
93
+ coinId?: Uint8Array;
94
+ address: Uint8Array;
95
+ amount: string;
96
+ tokenId?: Uint8Array;
97
+ storeState?: boolean;
98
+ state?: StateVariable[];
99
+ mmrEntryNumber?: bigint;
100
+ spent?: boolean;
101
+ created?: bigint;
102
+ rawAmountBytes?: Uint8Array;
103
+ rawMmrEntryBytes?: Uint8Array;
104
+ rawBlockCreatedBytes?: Uint8Array;
105
+ coinProofData?: CoinProofData;
106
+ }): MinimaCoin;
@@ -0,0 +1,293 @@
1
+ "use strict";
2
+ /**
3
+ * Transaction Serialization & Digest Computation
4
+ *
5
+ * Port of the extension's MinimaTransactionBuilder serialization logic
6
+ * to the SDK core for full parity with the Totem wallet extension.
7
+ *
8
+ * Matches Minima Java's Transaction.writeDataStream() and Coin.writeDataStream() exactly.
9
+ *
10
+ * CRITICAL: Before computing the transaction digest for signing, you MUST call
11
+ * precomputeTransactionCoinID() to set output coin IDs. Without this, the signed
12
+ * digest won't match what the Minima node verifies, causing allsignaturesvalid=false.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.parseDecimalToMiniNumber = parseDecimalToMiniNumber;
16
+ exports.serializeCoin = serializeCoin;
17
+ exports.serializeTransaction = serializeTransaction;
18
+ exports.computeTransactionDigest = computeTransactionDigest;
19
+ exports.precomputeTransactionCoinID = precomputeTransactionCoinID;
20
+ exports.createDefaultTransaction = createDefaultTransaction;
21
+ exports.buildMinimaCoin = buildMinimaCoin;
22
+ const wasm_sync_js_1 = require("./wasm-sync.js");
23
+ const Streamable_js_1 = require("./Streamable.js");
24
+ const ZERO_TXPOWID = new Uint8Array([0x00]);
25
+ const STATETYPE_HEX = 1;
26
+ const STATETYPE_NUMBER = 2;
27
+ const STATETYPE_STRING = 4;
28
+ const STATETYPE_BOOL = 8;
29
+ function parseDecimalToMiniNumber(decimal) {
30
+ if (!decimal || decimal === '') {
31
+ return { scale: 0, unscaledValue: 0n };
32
+ }
33
+ if (decimal.includes('e') || decimal.includes('E')) {
34
+ throw new Error(`Scientific notation not supported: "${decimal}". Convert to plain decimal first.`);
35
+ }
36
+ let isNegative = false;
37
+ let cleaned = decimal.trim();
38
+ if (cleaned.startsWith('-')) {
39
+ isNegative = true;
40
+ cleaned = cleaned.slice(1);
41
+ }
42
+ const dotIndex = cleaned.indexOf('.');
43
+ let scale;
44
+ let unscaledStr;
45
+ if (dotIndex === -1) {
46
+ scale = 0;
47
+ unscaledStr = cleaned;
48
+ }
49
+ else {
50
+ const wholePart = cleaned.slice(0, dotIndex);
51
+ const fracPart = cleaned.slice(dotIndex + 1);
52
+ scale = fracPart.length;
53
+ unscaledStr = wholePart + fracPart;
54
+ }
55
+ unscaledStr = unscaledStr.replace(/^0+/, '') || '0';
56
+ let unscaledValue = BigInt(unscaledStr);
57
+ if (isNegative && unscaledValue !== 0n) {
58
+ unscaledValue = -unscaledValue;
59
+ }
60
+ return { scale, unscaledValue };
61
+ }
62
+ function encodeParsedMiniNumber(parsed) {
63
+ return (0, Streamable_js_1.writeMiniNumber)(parsed.unscaledValue, parsed.scale);
64
+ }
65
+ function normalizeAddressToHex(addr) {
66
+ if (!addr) {
67
+ return '0x00';
68
+ }
69
+ const trimmed = addr.trim();
70
+ const hexAddr = trimmed.startsWith('0x') ? trimmed : '0x' + trimmed;
71
+ const cleanHex = hexAddr.slice(2);
72
+ if (!/^[0-9a-fA-F]*$/.test(cleanHex)) {
73
+ throw new Error(`Invalid hex address: contains non-hex characters`);
74
+ }
75
+ if (cleanHex.length % 2 !== 0) {
76
+ throw new Error(`Invalid hex address: odd length (${cleanHex.length} chars)`);
77
+ }
78
+ return hexAddr;
79
+ }
80
+ function serializeStateVariable(sv) {
81
+ if (sv.port < 0 || sv.port > 255) {
82
+ throw new Error(`StateVariable port must be 0-255, got ${sv.port}`);
83
+ }
84
+ const portByte = new Uint8Array([sv.port]);
85
+ let typeByte;
86
+ let dataBytes;
87
+ switch (sv.type) {
88
+ case 'bool':
89
+ typeByte = new Uint8Array([STATETYPE_BOOL]);
90
+ if (typeof sv.value === 'boolean') {
91
+ dataBytes = new Uint8Array([sv.value ? 1 : 0]);
92
+ }
93
+ else if (typeof sv.value === 'string') {
94
+ dataBytes = new Uint8Array([sv.value.toUpperCase() === 'TRUE' ? 1 : 0]);
95
+ }
96
+ else {
97
+ throw new Error(`Invalid bool StateVariable value: ${sv.value}`);
98
+ }
99
+ break;
100
+ case 'number':
101
+ typeByte = new Uint8Array([STATETYPE_NUMBER]);
102
+ if (typeof sv.value === 'bigint') {
103
+ dataBytes = (0, Streamable_js_1.writeMiniNumber)(sv.value, 0);
104
+ }
105
+ else if (typeof sv.value === 'string') {
106
+ dataBytes = (0, Streamable_js_1.writeMiniNumber)(BigInt(sv.value), 0);
107
+ }
108
+ else {
109
+ throw new Error(`Invalid number StateVariable value: ${sv.value}`);
110
+ }
111
+ break;
112
+ case 'hex':
113
+ typeByte = new Uint8Array([STATETYPE_HEX]);
114
+ if (typeof sv.value === 'string') {
115
+ const normalizedHex = normalizeAddressToHex(sv.value);
116
+ dataBytes = (0, Streamable_js_1.writeMiniData)((0, Streamable_js_1.hexToBytes)(normalizedHex));
117
+ }
118
+ else if (sv.value instanceof Uint8Array) {
119
+ dataBytes = (0, Streamable_js_1.writeMiniData)(sv.value);
120
+ }
121
+ else {
122
+ throw new Error(`Invalid hex StateVariable value: ${sv.value}`);
123
+ }
124
+ break;
125
+ case 'string':
126
+ typeByte = new Uint8Array([STATETYPE_STRING]);
127
+ if (typeof sv.value === 'string') {
128
+ let bracketedValue = sv.value;
129
+ if (!sv.value.startsWith('[') || !sv.value.endsWith(']')) {
130
+ bracketedValue = `[${sv.value}]`;
131
+ }
132
+ const utf8Bytes = new TextEncoder().encode(bracketedValue);
133
+ dataBytes = (0, Streamable_js_1.writeMiniData)(utf8Bytes);
134
+ }
135
+ else {
136
+ throw new Error(`Invalid string StateVariable value: ${sv.value}`);
137
+ }
138
+ break;
139
+ default:
140
+ throw new Error(`Unknown StateVariable type: ${sv.type}`);
141
+ }
142
+ return (0, Streamable_js_1.concat)(portByte, typeByte, dataBytes);
143
+ }
144
+ function serializeToken(token) {
145
+ // Java Token.writeDataStream() order: coinId, script, scale, totalAmount, name, created
146
+ const parts = [];
147
+ parts.push((0, Streamable_js_1.writeHashToStream)(token.coinId));
148
+ parts.push((0, Streamable_js_1.writeMiniData)(token.script));
149
+ parts.push((0, Streamable_js_1.writeMiniNumber)(BigInt(token.scale), 0));
150
+ parts.push((0, Streamable_js_1.writeMiniNumber)(token.totalAmount, 0));
151
+ parts.push((0, Streamable_js_1.writeMiniData)(token.name));
152
+ parts.push((0, Streamable_js_1.writeMiniNumber)(token.created ?? 0n, 0));
153
+ return (0, Streamable_js_1.concat)(...parts);
154
+ }
155
+ function serializeCoin(coin) {
156
+ const parts = [];
157
+ parts.push((0, Streamable_js_1.writeHashToStream)(coin.coinId));
158
+ parts.push((0, Streamable_js_1.writeHashToStream)(coin.address));
159
+ let amountBytes;
160
+ if (coin.rawAmountBytes && coin.rawAmountBytes.length > 0) {
161
+ amountBytes = coin.rawAmountBytes;
162
+ }
163
+ else {
164
+ const parsedAmount = parseDecimalToMiniNumber(coin.amount);
165
+ amountBytes = encodeParsedMiniNumber(parsedAmount);
166
+ }
167
+ parts.push(amountBytes);
168
+ parts.push((0, Streamable_js_1.writeMiniData)(coin.tokenId));
169
+ parts.push((0, Streamable_js_1.writeMiniByte)(coin.storeState));
170
+ let mmrBytes;
171
+ if (coin.rawMmrEntryBytes && coin.rawMmrEntryBytes.length > 0) {
172
+ mmrBytes = coin.rawMmrEntryBytes;
173
+ }
174
+ else {
175
+ mmrBytes = (0, Streamable_js_1.writeMMREntryNumber)(coin.mmrEntryNumber);
176
+ }
177
+ parts.push(mmrBytes);
178
+ parts.push((0, Streamable_js_1.writeMiniByte)(coin.spent));
179
+ let createdBytes;
180
+ if (coin.rawBlockCreatedBytes && coin.rawBlockCreatedBytes.length > 0) {
181
+ createdBytes = coin.rawBlockCreatedBytes;
182
+ }
183
+ else {
184
+ createdBytes = (0, Streamable_js_1.writeMiniNumber)(coin.created, 0);
185
+ }
186
+ parts.push(createdBytes);
187
+ const stateCountBytes = (0, Streamable_js_1.writeMiniNumber)(BigInt(coin.state.length), 0);
188
+ parts.push(stateCountBytes);
189
+ for (const sv of coin.state) {
190
+ let svBytes;
191
+ if ('rawData' in sv) {
192
+ const raw = sv;
193
+ const portByte = new Uint8Array([raw.port]);
194
+ const typeByte = new Uint8Array([raw.type]);
195
+ svBytes = (0, Streamable_js_1.concat)(portByte, typeByte, raw.rawData);
196
+ }
197
+ else {
198
+ svBytes = serializeStateVariable(sv);
199
+ }
200
+ parts.push(svBytes);
201
+ }
202
+ if (coin.rawTokenData && coin.rawTokenData.length > 0) {
203
+ parts.push((0, Streamable_js_1.writeMiniByte)(true));
204
+ parts.push(coin.rawTokenData);
205
+ }
206
+ else if (coin.token) {
207
+ parts.push((0, Streamable_js_1.writeMiniByte)(true));
208
+ parts.push(serializeToken(coin.token));
209
+ }
210
+ else {
211
+ parts.push((0, Streamable_js_1.writeMiniByte)(false));
212
+ }
213
+ return (0, Streamable_js_1.concat)(...parts);
214
+ }
215
+ function serializeTransaction(tx) {
216
+ const parts = [];
217
+ parts.push((0, Streamable_js_1.writeMiniNumber)(BigInt(tx.inputs.length), 0));
218
+ for (const input of tx.inputs) {
219
+ parts.push(serializeCoin(input));
220
+ }
221
+ parts.push((0, Streamable_js_1.writeMiniNumber)(BigInt(tx.outputs.length), 0));
222
+ for (const output of tx.outputs) {
223
+ parts.push(serializeCoin(output));
224
+ }
225
+ parts.push((0, Streamable_js_1.writeMiniNumber)(BigInt(tx.state.length), 0));
226
+ for (const sv of tx.state) {
227
+ parts.push(serializeStateVariable(sv));
228
+ }
229
+ parts.push((0, Streamable_js_1.writeHashToStream)(tx.linkHash));
230
+ return (0, Streamable_js_1.concat)(...parts);
231
+ }
232
+ function computeTransactionDigest(tx) {
233
+ const serialized = serializeTransaction(tx);
234
+ return (0, wasm_sync_js_1.sha3_256)(serialized);
235
+ }
236
+ function precomputeTransactionCoinID(tx) {
237
+ if (tx.inputs.length === 0)
238
+ return;
239
+ const baseCoinId = tx.inputs[0].coinId;
240
+ if (baseCoinId.length < 2) {
241
+ throw new Error(`Cannot precompute output coinIDs: input[0].coinId is ${baseCoinId.length} byte(s) ` +
242
+ `(placeholder?). Real coinId from CoinProof required for signing.`);
243
+ }
244
+ for (let i = 0; i < tx.outputs.length; i++) {
245
+ const baseCoinIdStream = (0, Streamable_js_1.writeMiniData)(baseCoinId);
246
+ const outputIndexStream = (0, Streamable_js_1.writeMiniNumber)(BigInt(i), 0);
247
+ const combined = (0, Streamable_js_1.concat)(baseCoinIdStream, outputIndexStream);
248
+ tx.outputs[i].coinId = (0, wasm_sync_js_1.sha3_256)(combined);
249
+ }
250
+ }
251
+ function createDefaultTransaction() {
252
+ return {
253
+ linkHash: ZERO_TXPOWID,
254
+ inputs: [],
255
+ outputs: [],
256
+ state: [],
257
+ };
258
+ }
259
+ function buildMinimaCoin(opts) {
260
+ if (opts.coinProofData) {
261
+ const cpd = opts.coinProofData;
262
+ return {
263
+ coinId: cpd.coinId,
264
+ address: cpd.address,
265
+ amount: opts.amount,
266
+ tokenId: cpd.tokenId,
267
+ token: null,
268
+ storeState: cpd.storeState,
269
+ state: cpd.state,
270
+ mmrEntryNumber: cpd.mmrEntryNumber,
271
+ spent: cpd.spent,
272
+ created: cpd.blockCreated,
273
+ rawAmountBytes: cpd.rawAmountBytes,
274
+ rawMmrEntryBytes: cpd.rawMmrEntryBytes,
275
+ rawBlockCreatedBytes: cpd.rawBlockCreatedBytes,
276
+ };
277
+ }
278
+ return {
279
+ coinId: opts.coinId || new Uint8Array([0x00]),
280
+ address: opts.address,
281
+ amount: opts.amount,
282
+ tokenId: opts.tokenId || new Uint8Array([0x00]),
283
+ token: null,
284
+ storeState: opts.storeState ?? false,
285
+ state: opts.state || [],
286
+ mmrEntryNumber: opts.mmrEntryNumber ?? 0n,
287
+ spent: opts.spent ?? false,
288
+ created: opts.created ?? 0n,
289
+ rawAmountBytes: opts.rawAmountBytes,
290
+ rawMmrEntryBytes: opts.rawMmrEntryBytes,
291
+ rawBlockCreatedBytes: opts.rawBlockCreatedBytes,
292
+ };
293
+ }