pctestupgrade2 1.1.8

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 (66) hide show
  1. package/.github/workflows/npm-publish.yml +55 -0
  2. package/README.md +31 -0
  3. package/__tests__/e2e.test.ts +56 -0
  4. package/__tests__/e2espl.test.ts +73 -0
  5. package/__tests__/encryption.test.ts +1635 -0
  6. package/circuit2/transaction2.wasm +0 -0
  7. package/circuit2/transaction2.zkey +0 -0
  8. package/dist/config.d.ts +9 -0
  9. package/dist/config.js +12 -0
  10. package/dist/deposit.d.ts +19 -0
  11. package/dist/deposit.js +396 -0
  12. package/dist/depositSPL.d.ts +21 -0
  13. package/dist/depositSPL.js +452 -0
  14. package/dist/exportUtils.d.ts +10 -0
  15. package/dist/exportUtils.js +10 -0
  16. package/dist/getUtxos.d.ts +29 -0
  17. package/dist/getUtxos.js +294 -0
  18. package/dist/getUtxosSPL.d.ts +33 -0
  19. package/dist/getUtxosSPL.js +395 -0
  20. package/dist/index.d.ts +128 -0
  21. package/dist/index.js +305 -0
  22. package/dist/models/keypair.d.ts +26 -0
  23. package/dist/models/keypair.js +43 -0
  24. package/dist/models/utxo.d.ts +49 -0
  25. package/dist/models/utxo.js +85 -0
  26. package/dist/utils/address_lookup_table.d.ts +9 -0
  27. package/dist/utils/address_lookup_table.js +45 -0
  28. package/dist/utils/constants.d.ts +27 -0
  29. package/dist/utils/constants.js +56 -0
  30. package/dist/utils/encryption.d.ts +107 -0
  31. package/dist/utils/encryption.js +376 -0
  32. package/dist/utils/logger.d.ts +9 -0
  33. package/dist/utils/logger.js +35 -0
  34. package/dist/utils/merkle_tree.d.ts +92 -0
  35. package/dist/utils/merkle_tree.js +186 -0
  36. package/dist/utils/node-shim.d.ts +5 -0
  37. package/dist/utils/node-shim.js +5 -0
  38. package/dist/utils/prover.d.ts +36 -0
  39. package/dist/utils/prover.js +147 -0
  40. package/dist/utils/utils.d.ts +64 -0
  41. package/dist/utils/utils.js +165 -0
  42. package/dist/withdraw.d.ts +22 -0
  43. package/dist/withdraw.js +272 -0
  44. package/dist/withdrawSPL.d.ts +24 -0
  45. package/dist/withdrawSPL.js +308 -0
  46. package/package.json +51 -0
  47. package/src/config.ts +22 -0
  48. package/src/deposit.ts +493 -0
  49. package/src/depositSPL.ts +575 -0
  50. package/src/exportUtils.ts +12 -0
  51. package/src/getUtxos.ts +396 -0
  52. package/src/getUtxosSPL.ts +528 -0
  53. package/src/index.ts +356 -0
  54. package/src/models/keypair.ts +52 -0
  55. package/src/models/utxo.ts +106 -0
  56. package/src/utils/address_lookup_table.ts +78 -0
  57. package/src/utils/constants.ts +77 -0
  58. package/src/utils/encryption.ts +464 -0
  59. package/src/utils/logger.ts +42 -0
  60. package/src/utils/merkle_tree.ts +207 -0
  61. package/src/utils/node-shim.ts +6 -0
  62. package/src/utils/prover.ts +222 -0
  63. package/src/utils/utils.ts +222 -0
  64. package/src/withdraw.ts +335 -0
  65. package/src/withdrawSPL.ts +399 -0
  66. package/tsconfig.json +28 -0
@@ -0,0 +1,107 @@
1
+ import { Keypair } from '@solana/web3.js';
2
+ import { Utxo } from '../models/utxo.js';
3
+ /**
4
+ * Represents a UTXO with minimal required fields
5
+ */
6
+ export interface UtxoData {
7
+ amount: string;
8
+ blinding: string;
9
+ index: number | string;
10
+ [key: string]: any;
11
+ }
12
+ export interface EncryptionKey {
13
+ v1: Uint8Array;
14
+ v2: Uint8Array;
15
+ }
16
+ /**
17
+ * Service for handling encryption and decryption of UTXO data
18
+ */
19
+ export declare class EncryptionService {
20
+ static readonly ENCRYPTION_VERSION_V2: Buffer<ArrayBuffer>;
21
+ private encryptionKeyV1;
22
+ private encryptionKeyV2;
23
+ private utxoPrivateKeyV1;
24
+ private utxoPrivateKeyV2;
25
+ /**
26
+ * Generate an encryption key from a signature
27
+ * @param signature The user's signature
28
+ * @returns The generated encryption key
29
+ */
30
+ deriveEncryptionKeyFromSignature(signature: Uint8Array): EncryptionKey;
31
+ /**
32
+ * Generate an encryption key from a wallet keypair (V2 format)
33
+ * @param keypair The Solana keypair to derive the encryption key from
34
+ * @returns The generated encryption key
35
+ */
36
+ deriveEncryptionKeyFromWallet(keypair: Keypair): EncryptionKey;
37
+ /**
38
+ * Encrypt data with the stored encryption key
39
+ * @param data The data to encrypt
40
+ * @returns The encrypted data as a Buffer
41
+ * @throws Error if the encryption key has not been generated
42
+ */
43
+ encrypt(data: Buffer | string): Buffer;
44
+ encryptDecryptedDoNotUse(data: Buffer | string): Buffer;
45
+ /**
46
+ * Decrypt data with the stored encryption key
47
+ * @param encryptedData The encrypted data to decrypt
48
+ * @returns The decrypted data as a Buffer
49
+ * @throws Error if the encryption key has not been generated or if the wrong key is used
50
+ */
51
+ decrypt(encryptedData: Buffer): Buffer;
52
+ /**
53
+ * Decrypt data using the old V1 format (120-bit HMAC with SHA256)
54
+ * @param encryptedData The encrypted data to decrypt
55
+ * @param keypair Optional keypair to derive V1 key for backward compatibility
56
+ * @returns The decrypted data as a Buffer
57
+ */
58
+ private decryptV1;
59
+ private timingSafeEqual;
60
+ /**
61
+ * Decrypt data using the new V2 format (256-bit Keccak HMAC)
62
+ * @param encryptedData The encrypted data to decrypt
63
+ * @returns The decrypted data as a Buffer
64
+ */
65
+ private decryptV2;
66
+ /**
67
+ * Reset the encryption keys (mainly for testing purposes)
68
+ */
69
+ resetEncryptionKey(): void;
70
+ /**
71
+ * Encrypt a UTXO using a compact pipe-delimited format
72
+ * Always uses V2 encryption format. The UTXO's version property is used only for key derivation.
73
+ * @param utxo The UTXO to encrypt (includes version property)
74
+ * @returns The encrypted UTXO data as a Buffer
75
+ * @throws Error if the V2 encryption key has not been set
76
+ */
77
+ encryptUtxo(utxo: Utxo): Buffer;
78
+ encryptUtxoDecryptedDoNotUse(utxo: Utxo): Buffer;
79
+ getEncryptionKeyVersion(encryptedData: Buffer | string): 'v1' | 'v2';
80
+ /**
81
+ * Decrypt an encrypted UTXO and parse it to a Utxo instance
82
+ * Automatically detects the UTXO version based on the encryption format
83
+ * @param encryptedData The encrypted UTXO data
84
+ * @param keypair The UTXO keypair to use for the decrypted UTXO
85
+ * @param lightWasm Optional LightWasm instance. If not provided, a new one will be created
86
+ * @param walletKeypair Optional wallet keypair for V1 backward compatibility
87
+ * @returns Promise resolving to the decrypted Utxo instance
88
+ * @throws Error if the encryption key has not been set or if decryption fails
89
+ */
90
+ decryptUtxo(encryptedData: Buffer | string, lightWasm?: any): Promise<Utxo>;
91
+ getUtxoPrivateKeyWithVersion(version: 'v1' | 'v2'): string;
92
+ deriveUtxoPrivateKey(encryptedData?: Buffer | string): string;
93
+ hasUtxoPrivateKeyWithVersion(version: 'v1' | 'v2'): boolean;
94
+ /**
95
+ * Get the cached V1 UTXO private key
96
+ * @returns A private key in hex format that can be used to create a UTXO keypair
97
+ * @throws Error if V1 encryption key has not been set
98
+ */
99
+ getUtxoPrivateKeyV1(): string;
100
+ /**
101
+ * Get the cached V2 UTXO private key
102
+ * @returns A private key in hex format that can be used to create a UTXO keypair
103
+ * @throws Error if V2 encryption key has not been set
104
+ */
105
+ getUtxoPrivateKeyV2(): string;
106
+ }
107
+ export declare function serializeProofAndExtData(proof: any, extData: any, isSpl?: boolean): Buffer<ArrayBuffer>;
@@ -0,0 +1,376 @@
1
+ import nacl from 'tweetnacl';
2
+ import * as crypto from 'crypto';
3
+ import { Utxo } from '../models/utxo.js';
4
+ import { WasmFactory } from '@lightprotocol/hasher.rs';
5
+ import { Keypair as UtxoKeypair } from '../models/keypair.js';
6
+ import { keccak256 } from '@ethersproject/keccak256';
7
+ import { TRANSACT_IX_DISCRIMINATOR, TRANSACT_SPL_IX_DISCRIMINATOR } from './constants.js';
8
+ import BN from 'bn.js';
9
+ /**
10
+ * Service for handling encryption and decryption of UTXO data
11
+ */
12
+ export class EncryptionService {
13
+ static ENCRYPTION_VERSION_V2 = Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]); // Version 2
14
+ encryptionKeyV1 = null;
15
+ encryptionKeyV2 = null;
16
+ utxoPrivateKeyV1 = null;
17
+ utxoPrivateKeyV2 = null;
18
+ /**
19
+ * Generate an encryption key from a signature
20
+ * @param signature The user's signature
21
+ * @returns The generated encryption key
22
+ */
23
+ deriveEncryptionKeyFromSignature(signature) {
24
+ // Extract the first 31 bytes of the signature to create a deterministic key (legacy method)
25
+ const encryptionKeyV1 = signature.slice(0, 31);
26
+ // Store the V1 key in the service
27
+ this.encryptionKeyV1 = encryptionKeyV1;
28
+ // Precompute and cache the UTXO private key
29
+ const hashedSeedV1 = crypto.createHash('sha256').update(encryptionKeyV1).digest();
30
+ this.utxoPrivateKeyV1 = '0x' + hashedSeedV1.toString('hex');
31
+ // Use Keccak256 to derive a full 32-byte encryption key from the signature
32
+ const encryptionKeyV2 = Buffer.from(keccak256(signature).slice(2), 'hex');
33
+ // Store the V2 key in the service
34
+ this.encryptionKeyV2 = encryptionKeyV2;
35
+ // Precompute and cache the UTXO private key
36
+ const hashedSeedV2 = Buffer.from(keccak256(encryptionKeyV2).slice(2), 'hex');
37
+ this.utxoPrivateKeyV2 = '0x' + hashedSeedV2.toString('hex');
38
+ return {
39
+ v1: this.encryptionKeyV1,
40
+ v2: this.encryptionKeyV2
41
+ };
42
+ }
43
+ /**
44
+ * Generate an encryption key from a wallet keypair (V2 format)
45
+ * @param keypair The Solana keypair to derive the encryption key from
46
+ * @returns The generated encryption key
47
+ */
48
+ deriveEncryptionKeyFromWallet(keypair) {
49
+ // Sign a constant message with the keypair
50
+ const message = Buffer.from('Privacy Money account sign in');
51
+ const signature = nacl.sign.detached(message, keypair.secretKey);
52
+ return this.deriveEncryptionKeyFromSignature(signature);
53
+ }
54
+ /**
55
+ * Encrypt data with the stored encryption key
56
+ * @param data The data to encrypt
57
+ * @returns The encrypted data as a Buffer
58
+ * @throws Error if the encryption key has not been generated
59
+ */
60
+ encrypt(data) {
61
+ if (!this.encryptionKeyV2) {
62
+ throw new Error('Encryption key not set. Call setEncryptionKey or deriveEncryptionKeyFromWallet first.');
63
+ }
64
+ // Convert string to Buffer if needed
65
+ const dataBuffer = typeof data === 'string' ? Buffer.from(data) : data;
66
+ // Generate a standard initialization vector (12 bytes for GCM)
67
+ const iv = crypto.randomBytes(12);
68
+ // Use the full 32-byte V2 encryption key for AES-256
69
+ const key = Buffer.from(this.encryptionKeyV2);
70
+ // Use AES-256-GCM for authenticated encryption
71
+ const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
72
+ const encryptedData = Buffer.concat([
73
+ cipher.update(dataBuffer),
74
+ cipher.final()
75
+ ]);
76
+ // Get the authentication tag from GCM (16 bytes)
77
+ const authTag = cipher.getAuthTag();
78
+ // Version 2 format: [version(8)] + [IV(12)] + [authTag(16)] + [encryptedData]
79
+ return Buffer.concat([
80
+ EncryptionService.ENCRYPTION_VERSION_V2,
81
+ iv,
82
+ authTag,
83
+ encryptedData
84
+ ]);
85
+ }
86
+ // v1 encryption, only used for testing now
87
+ encryptDecryptedDoNotUse(data) {
88
+ if (!this.encryptionKeyV1) {
89
+ throw new Error('Encryption key not set. Call setEncryptionKey or deriveEncryptionKeyFromWallet first.');
90
+ }
91
+ // Convert string to Buffer if needed
92
+ const dataBuffer = typeof data === 'string' ? Buffer.from(data) : data;
93
+ // Generate a standard initialization vector (16 bytes)
94
+ const iv = crypto.randomBytes(16);
95
+ // Create a key from our encryption key (using only first 16 bytes for AES-128)
96
+ const key = Buffer.from(this.encryptionKeyV1).slice(0, 16);
97
+ // Use a more compact encryption algorithm (aes-128-ctr)
98
+ const cipher = crypto.createCipheriv('aes-128-ctr', key, iv);
99
+ const encryptedData = Buffer.concat([
100
+ cipher.update(dataBuffer),
101
+ cipher.final()
102
+ ]);
103
+ // Create an authentication tag (HMAC) to verify decryption with correct key
104
+ const hmacKey = Buffer.from(this.encryptionKeyV1).slice(16, 31);
105
+ const hmac = crypto.createHmac('sha256', hmacKey);
106
+ hmac.update(iv);
107
+ hmac.update(encryptedData);
108
+ const authTag = hmac.digest().slice(0, 16); // Use first 16 bytes of HMAC as auth tag
109
+ // Combine IV, auth tag and encrypted data
110
+ return Buffer.concat([iv, authTag, encryptedData]);
111
+ }
112
+ /**
113
+ * Decrypt data with the stored encryption key
114
+ * @param encryptedData The encrypted data to decrypt
115
+ * @returns The decrypted data as a Buffer
116
+ * @throws Error if the encryption key has not been generated or if the wrong key is used
117
+ */
118
+ decrypt(encryptedData) {
119
+ // Check if this is the new version format (starts with 8-byte version identifier)
120
+ if (encryptedData.length >= 8 && encryptedData.subarray(0, 8).equals(EncryptionService.ENCRYPTION_VERSION_V2)) {
121
+ if (!this.encryptionKeyV2) {
122
+ throw new Error('Encryption key not set. Call setEncryptionKey or deriveEncryptionKeyFromWallet first.');
123
+ }
124
+ return this.decryptV2(encryptedData);
125
+ }
126
+ else {
127
+ // V1 format - need V1 key or keypair to derive it
128
+ if (!this.encryptionKeyV1) {
129
+ throw new Error('Encryption key not set. Call setEncryptionKey or deriveEncryptionKeyFromWallet first.');
130
+ }
131
+ return this.decryptV1(encryptedData);
132
+ }
133
+ }
134
+ /**
135
+ * Decrypt data using the old V1 format (120-bit HMAC with SHA256)
136
+ * @param encryptedData The encrypted data to decrypt
137
+ * @param keypair Optional keypair to derive V1 key for backward compatibility
138
+ * @returns The decrypted data as a Buffer
139
+ */
140
+ decryptV1(encryptedData) {
141
+ if (!this.encryptionKeyV1) {
142
+ throw new Error('Encryption key not set. Call setEncryptionKey or deriveEncryptionKeyFromWallet first.');
143
+ }
144
+ // Extract the IV from the first 16 bytes
145
+ const iv = encryptedData.slice(0, 16);
146
+ // Extract the auth tag from the next 16 bytes
147
+ const authTag = encryptedData.slice(16, 32);
148
+ // The rest is the actual encrypted data
149
+ const data = encryptedData.slice(32);
150
+ // Verify the authentication tag
151
+ const hmacKey = Buffer.from(this.encryptionKeyV1).slice(16, 31);
152
+ const hmac = crypto.createHmac('sha256', hmacKey);
153
+ hmac.update(iv);
154
+ hmac.update(data);
155
+ const calculatedTag = hmac.digest().slice(0, 16);
156
+ // Compare tags - if they don't match, the key is wrong
157
+ if (!this.timingSafeEqual(authTag, calculatedTag)) {
158
+ throw new Error('Failed to decrypt data. Invalid encryption key or corrupted data.');
159
+ }
160
+ // Create a key from our encryption key (using only first 16 bytes for AES-128)
161
+ const key = Buffer.from(this.encryptionKeyV1).slice(0, 16);
162
+ // Use the same algorithm as in encrypt
163
+ const decipher = crypto.createDecipheriv('aes-128-ctr', key, iv);
164
+ try {
165
+ return Buffer.concat([
166
+ decipher.update(data),
167
+ decipher.final()
168
+ ]);
169
+ }
170
+ catch (error) {
171
+ throw new Error('Failed to decrypt data. Invalid encryption key or corrupted data.');
172
+ }
173
+ }
174
+ // Custom timingSafeEqual for browser compatibility
175
+ timingSafeEqual(a, b) {
176
+ if (a.length !== b.length) {
177
+ return false;
178
+ }
179
+ let diff = 0;
180
+ for (let i = 0; i < a.length; i++) {
181
+ diff |= a[i] ^ b[i];
182
+ }
183
+ return diff === 0;
184
+ }
185
+ /**
186
+ * Decrypt data using the new V2 format (256-bit Keccak HMAC)
187
+ * @param encryptedData The encrypted data to decrypt
188
+ * @returns The decrypted data as a Buffer
189
+ */
190
+ decryptV2(encryptedData) {
191
+ if (!this.encryptionKeyV2) {
192
+ throw new Error('encryptionKeyV2 not set. Call setEncryptionKey or deriveEncryptionKeyFromWallet first.');
193
+ }
194
+ // Skip 8-byte version identifier and extract components for GCM format
195
+ const iv = encryptedData.slice(8, 20); // bytes 8-19 (12 bytes for GCM)
196
+ const authTag = encryptedData.slice(20, 36); // bytes 20-35 (16 bytes for GCM)
197
+ const data = encryptedData.slice(36); // remaining bytes
198
+ // Use the full 32-byte V2 encryption key for AES-256
199
+ const key = Buffer.from(this.encryptionKeyV2);
200
+ // Use AES-256-GCM for authenticated decryption
201
+ const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
202
+ decipher.setAuthTag(authTag);
203
+ try {
204
+ return Buffer.concat([
205
+ decipher.update(data),
206
+ decipher.final()
207
+ ]);
208
+ }
209
+ catch (error) {
210
+ throw new Error('Failed to decrypt data. Invalid encryption key or corrupted data.');
211
+ }
212
+ }
213
+ /**
214
+ * Reset the encryption keys (mainly for testing purposes)
215
+ */
216
+ resetEncryptionKey() {
217
+ this.encryptionKeyV1 = null;
218
+ this.encryptionKeyV2 = null;
219
+ this.utxoPrivateKeyV1 = null;
220
+ this.utxoPrivateKeyV2 = null;
221
+ }
222
+ /**
223
+ * Encrypt a UTXO using a compact pipe-delimited format
224
+ * Always uses V2 encryption format. The UTXO's version property is used only for key derivation.
225
+ * @param utxo The UTXO to encrypt (includes version property)
226
+ * @returns The encrypted UTXO data as a Buffer
227
+ * @throws Error if the V2 encryption key has not been set
228
+ */
229
+ encryptUtxo(utxo) {
230
+ if (!this.encryptionKeyV2) {
231
+ throw new Error('Encryption key not set. Call setEncryptionKey or deriveEncryptionKeyFromWallet first.');
232
+ }
233
+ // Create a compact string representation using pipe delimiter
234
+ // Version is stored in the UTXO model, not in the encrypted content
235
+ const utxoString = `${utxo.amount.toString()}|${utxo.blinding.toString()}|${utxo.index}|${utxo.mintAddress}`;
236
+ // Always use V2 encryption format (which adds version byte 0x02 at the beginning)
237
+ return this.encrypt(utxoString);
238
+ }
239
+ // Deprecated, only used for testing now
240
+ encryptUtxoDecryptedDoNotUse(utxo) {
241
+ if (!this.encryptionKeyV2) {
242
+ throw new Error('Encryption key not set. Call setEncryptionKey or deriveEncryptionKeyFromWallet first.');
243
+ }
244
+ const utxoString = `${utxo.amount.toString()}|${utxo.blinding.toString()}|${utxo.index}|${utxo.mintAddress}`;
245
+ return this.encryptDecryptedDoNotUse(utxoString);
246
+ }
247
+ getEncryptionKeyVersion(encryptedData) {
248
+ const buffer = typeof encryptedData === 'string' ? Buffer.from(encryptedData, 'hex') : encryptedData;
249
+ if (buffer.length >= 8 && buffer.subarray(0, 8).equals(EncryptionService.ENCRYPTION_VERSION_V2)) {
250
+ // V2 encryption format → V2 UTXO
251
+ return 'v2';
252
+ }
253
+ else {
254
+ // V1 encryption format → UTXO
255
+ return 'v1';
256
+ }
257
+ }
258
+ /**
259
+ * Decrypt an encrypted UTXO and parse it to a Utxo instance
260
+ * Automatically detects the UTXO version based on the encryption format
261
+ * @param encryptedData The encrypted UTXO data
262
+ * @param keypair The UTXO keypair to use for the decrypted UTXO
263
+ * @param lightWasm Optional LightWasm instance. If not provided, a new one will be created
264
+ * @param walletKeypair Optional wallet keypair for V1 backward compatibility
265
+ * @returns Promise resolving to the decrypted Utxo instance
266
+ * @throws Error if the encryption key has not been set or if decryption fails
267
+ */
268
+ async decryptUtxo(encryptedData, lightWasm) {
269
+ // Convert hex string to Buffer if needed
270
+ const encryptedBuffer = typeof encryptedData === 'string'
271
+ ? Buffer.from(encryptedData, 'hex')
272
+ : encryptedData;
273
+ // Detect UTXO version based on encryption format
274
+ let utxoVersion = this.getEncryptionKeyVersion(encryptedBuffer);
275
+ // The decrypt() method already handles encryption format version detection (V1 vs V2)
276
+ // It checks the first byte to determine whether to use decryptV1() or decryptV2()
277
+ const decrypted = this.decrypt(encryptedBuffer);
278
+ // Parse the pipe-delimited format: amount|blinding|index|mintAddress
279
+ const decryptedStr = decrypted.toString();
280
+ const parts = decryptedStr.split('|');
281
+ if (parts.length !== 4) {
282
+ throw new Error('Invalid UTXO format after decryption');
283
+ }
284
+ const [amount, blinding, index, mintAddress] = parts;
285
+ if (!amount || !blinding || index === undefined || mintAddress === undefined) {
286
+ throw new Error('Invalid UTXO format after decryption');
287
+ }
288
+ // Get or create a LightWasm instance
289
+ const wasmInstance = lightWasm || await WasmFactory.getInstance();
290
+ const privateKey = this.getUtxoPrivateKeyWithVersion(utxoVersion);
291
+ // Create a Utxo instance with the detected version
292
+ const utxo = new Utxo({
293
+ lightWasm: wasmInstance,
294
+ amount: amount,
295
+ blinding: blinding,
296
+ keypair: new UtxoKeypair(privateKey, wasmInstance),
297
+ index: Number(index),
298
+ mintAddress: mintAddress,
299
+ version: utxoVersion
300
+ });
301
+ return utxo;
302
+ }
303
+ getUtxoPrivateKeyWithVersion(version) {
304
+ if (version === 'v1') {
305
+ return this.getUtxoPrivateKeyV1();
306
+ }
307
+ return this.getUtxoPrivateKeyV2();
308
+ }
309
+ deriveUtxoPrivateKey(encryptedData) {
310
+ if (encryptedData && this.getEncryptionKeyVersion(encryptedData) === 'v2') {
311
+ return this.getUtxoPrivateKeyWithVersion('v2');
312
+ }
313
+ return this.getUtxoPrivateKeyWithVersion('v1');
314
+ }
315
+ hasUtxoPrivateKeyWithVersion(version) {
316
+ if (version === 'v1') {
317
+ return !!this.utxoPrivateKeyV1;
318
+ }
319
+ return !!this.utxoPrivateKeyV2;
320
+ }
321
+ /**
322
+ * Get the cached V1 UTXO private key
323
+ * @returns A private key in hex format that can be used to create a UTXO keypair
324
+ * @throws Error if V1 encryption key has not been set
325
+ */
326
+ getUtxoPrivateKeyV1() {
327
+ if (!this.utxoPrivateKeyV1) {
328
+ throw new Error('Encryption key not set. Call setEncryptionKey or deriveEncryptionKeyFromWallet first.');
329
+ }
330
+ return this.utxoPrivateKeyV1;
331
+ }
332
+ /**
333
+ * Get the cached V2 UTXO private key
334
+ * @returns A private key in hex format that can be used to create a UTXO keypair
335
+ * @throws Error if V2 encryption key has not been set
336
+ */
337
+ getUtxoPrivateKeyV2() {
338
+ if (!this.utxoPrivateKeyV2) {
339
+ throw new Error('Encryption key not set. Call setEncryptionKey or deriveEncryptionKeyFromWallet first.');
340
+ }
341
+ return this.utxoPrivateKeyV2;
342
+ }
343
+ }
344
+ export function serializeProofAndExtData(proof, extData, isSpl = false) {
345
+ // Create the ExtDataMinified object for the program call (only extAmount and fee)
346
+ const extDataMinified = {
347
+ extAmount: extData.extAmount,
348
+ fee: extData.fee
349
+ };
350
+ // Use the appropriate discriminator based on whether this is SPL or native SOL
351
+ const discriminator = isSpl ? TRANSACT_SPL_IX_DISCRIMINATOR : TRANSACT_IX_DISCRIMINATOR;
352
+ // Use the same serialization approach as deposit script
353
+ const instructionData = Buffer.concat([
354
+ discriminator,
355
+ // Serialize proof
356
+ Buffer.from(proof.proofA),
357
+ Buffer.from(proof.proofB),
358
+ Buffer.from(proof.proofC),
359
+ Buffer.from(proof.root),
360
+ Buffer.from(proof.publicAmount),
361
+ Buffer.from(proof.extDataHash),
362
+ Buffer.from(proof.inputNullifiers[0]),
363
+ Buffer.from(proof.inputNullifiers[1]),
364
+ Buffer.from(proof.outputCommitments[0]),
365
+ Buffer.from(proof.outputCommitments[1]),
366
+ // Serialize ExtDataMinified (only extAmount and fee)
367
+ Buffer.from(new BN(extDataMinified.extAmount).toTwos(64).toArray('le', 8)),
368
+ Buffer.from(new BN(extDataMinified.fee).toArray('le', 8)),
369
+ // Serialize encrypted outputs as separate parameters
370
+ Buffer.from(new BN(extData.encryptedOutput1.length).toArray('le', 4)),
371
+ extData.encryptedOutput1,
372
+ Buffer.from(new BN(extData.encryptedOutput2.length).toArray('le', 4)),
373
+ extData.encryptedOutput2,
374
+ ]);
375
+ return instructionData;
376
+ }
@@ -0,0 +1,9 @@
1
+ export type LogLevel = "debug" | "info" | "warn" | "error";
2
+ export type LoggerFn = (level: LogLevel, message: string) => void;
3
+ export declare function setLogger(logger: LoggerFn): void;
4
+ export declare const logger: {
5
+ debug: (...args: unknown[]) => void;
6
+ info: (...args: unknown[]) => void;
7
+ warn: (...args: unknown[]) => void;
8
+ error: (...args: unknown[]) => void;
9
+ };
@@ -0,0 +1,35 @@
1
+ const defaultLogger = (level, message) => {
2
+ const prefix = `[${level.toUpperCase()}]`;
3
+ console.log(prefix, message);
4
+ };
5
+ let userLogger = defaultLogger;
6
+ export function setLogger(logger) {
7
+ userLogger = logger;
8
+ }
9
+ function argToStr(args) {
10
+ return args.map(arg => {
11
+ if (typeof arg === "object" && arg !== null) {
12
+ try {
13
+ return JSON.stringify(arg);
14
+ }
15
+ catch {
16
+ return String(arg);
17
+ }
18
+ }
19
+ return String(arg);
20
+ }).join(" ");
21
+ }
22
+ export const logger = {
23
+ debug: (...args) => {
24
+ userLogger('debug', argToStr(args));
25
+ },
26
+ info: (...args) => {
27
+ userLogger('info', argToStr(args));
28
+ },
29
+ warn: (...args) => {
30
+ userLogger('warn', argToStr(args));
31
+ },
32
+ error: (...args) => {
33
+ userLogger('error', argToStr(args));
34
+ },
35
+ };
@@ -0,0 +1,92 @@
1
+ import * as hasher from '@lightprotocol/hasher.rs';
2
+ export declare const DEFAULT_ZERO = 0;
3
+ /**
4
+ * @callback hashFunction
5
+ * @param left Left leaf
6
+ * @param right Right leaf
7
+ */
8
+ /**
9
+ * Merkle tree
10
+ */
11
+ export declare class MerkleTree {
12
+ /**
13
+ * Constructor
14
+ * @param {number} levels Number of levels in the tree
15
+ * @param {Array} [elements] Initial elements
16
+ * @param {Object} options
17
+ * @param {hashFunction} [options.hashFunction] Function used to hash 2 leaves
18
+ * @param [options.zeroElement] Value for non-existent leaves
19
+ */
20
+ levels: number;
21
+ capacity: number;
22
+ zeroElement: string;
23
+ _zeros: string[];
24
+ _layers: string[][];
25
+ _lightWasm: hasher.LightWasm;
26
+ constructor(levels: number, lightWasm: hasher.LightWasm, elements?: string[], { zeroElement }?: {
27
+ zeroElement?: number | undefined;
28
+ });
29
+ _rebuild(): void;
30
+ /**
31
+ * Get tree root
32
+ * @returns {*}
33
+ */
34
+ root(): string;
35
+ /**
36
+ * Insert new element into the tree
37
+ * @param element Element to insert
38
+ */
39
+ insert(element: string): void;
40
+ /**
41
+ * Insert multiple elements into the tree. Tree will be fully rebuilt during this operation.
42
+ * @param {Array} elements Elements to insert
43
+ */
44
+ bulkInsert(elements: string[]): void;
45
+ /**
46
+ * Change an element in the tree
47
+ * @param {number} index Index of element to change
48
+ * @param element Updated element value
49
+ */
50
+ update(index: number, element: string): void;
51
+ /**
52
+ * Get merkle path to a leaf
53
+ * @param {number} index Leaf index to generate path for
54
+ * @returns {{pathElements: number[], pathIndex: number[]}} An object containing adjacent elements and left-right index
55
+ */
56
+ path(index: number): {
57
+ pathElements: string[];
58
+ pathIndices: number[];
59
+ };
60
+ /**
61
+ * Find an element in the tree
62
+ * @param element An element to find
63
+ * @param comparator A function that checks leaf value equality
64
+ * @returns {number} Index if element is found, otherwise -1
65
+ */
66
+ indexOf(element: string, comparator?: Function | null): number;
67
+ /**
68
+ * Returns a copy of non-zero tree elements
69
+ * @returns {Object[]}
70
+ */
71
+ elements(): string[];
72
+ /**
73
+ * Serialize entire tree state including intermediate layers into a plain object
74
+ * Deserializing it back will not require to recompute any hashes
75
+ * Elements are not converted to a plain type, this is responsibility of the caller
76
+ */
77
+ serialize(): {
78
+ levels: number;
79
+ _zeros: string[];
80
+ _layers: string[][];
81
+ };
82
+ /**
83
+ * Deserialize data into a MerkleTree instance
84
+ * Make sure to provide the same hashFunction as was used in the source tree,
85
+ * otherwise the tree state will be invalid
86
+ *
87
+ * @param data
88
+ * @param hashFunction
89
+ * @returns {MerkleTree}
90
+ */
91
+ static deserialize(data: any, hashFunction: Function): any;
92
+ }