privacycash 1.0.21 → 1.1.1

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/src/depositSPL.ts CHANGED
@@ -8,7 +8,7 @@ import { MerkleTree } from './utils/merkle_tree.js';
8
8
  import { EncryptionService, serializeProofAndExtData } from './utils/encryption.js';
9
9
  import { Keypair as UtxoKeypair } from './models/keypair.js';
10
10
  import { getUtxosSPL, isUtxoSpent } from './getUtxosSPL.js';
11
- import { FIELD_SIZE, FEE_RECIPIENT, MERKLE_TREE_DEPTH, RELAYER_API_URL, PROGRAM_ID, ALT_ADDRESS } from './utils/constants.js';
11
+ import { FIELD_SIZE, FEE_RECIPIENT, MERKLE_TREE_DEPTH, RELAYER_API_URL, PROGRAM_ID, ALT_ADDRESS, tokens, SplList, Token } from './utils/constants.js';
12
12
  import { getProtocolAddressesWithMint, useExistingALT } from './utils/address_lookup_table.js';
13
13
  import { logger } from './utils/logger.js';
14
14
  import { getAssociatedTokenAddress, ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, getMint, getAccount } from '@solana/spl-token';
@@ -34,7 +34,7 @@ async function relayDepositToIndexer({ signedTransaction, publicKey, referrer, m
34
34
  if (referrer) {
35
35
  params.referralWalletAddress = referrer
36
36
  }
37
- params.mintAddress = mintAddress.toString()
37
+ params.mintAddress = mintAddress
38
38
 
39
39
  const response = await fetch(`${RELAYER_API_URL}/deposit/spl`, {
40
40
  method: 'POST',
@@ -45,28 +45,33 @@ async function relayDepositToIndexer({ signedTransaction, publicKey, referrer, m
45
45
  });
46
46
 
47
47
  if (!response.ok) {
48
- console.log('res text:', await response.json())
48
+ logger.debug('res text:', await response.text())
49
49
  throw new Error('response not ok')
50
50
  // const errorData = await response.json() as { error?: string };
51
51
  // throw new Error(`Deposit relay failed: ${response.status} ${response.statusText} - ${errorData.error || 'Unknown error'}`);
52
52
  }
53
-
54
- const result = await response.json() as { signature: string, success: boolean };
55
- logger.debug('Pre-signed deposit transaction relayed successfully!');
56
- logger.debug('Response:', result);
57
-
53
+ let result: { signature: string, success: boolean }
54
+ try {
55
+ result = await response.json()
56
+ logger.debug('Pre-signed deposit transaction relayed successfully!');
57
+ logger.debug('Response:', result);
58
+ } catch (e) {
59
+ console.log('response.text', await response.text())
60
+ throw new Error('failed to parse json')
61
+ }
58
62
  return result.signature;
59
- } catch (error) {
60
- console.error('Failed to relay deposit transaction to indexer:', error);
63
+ } catch (error: any) {
64
+ console.error('Failed to relay deposit transaction to indexer:', error.message);
61
65
  throw error;
62
66
  }
63
67
  }
64
68
 
65
69
  type DepositParams = {
66
- mintAddress: PublicKey,
70
+ mintAddress: PublicKey | string,
67
71
  publicKey: PublicKey,
68
72
  connection: Connection,
69
- base_units: number,
73
+ base_units?: number,
74
+ amount?: number,
70
75
  storage: Storage,
71
76
  encryptionService: EncryptionService,
72
77
  keyBasePath: string,
@@ -74,61 +79,70 @@ type DepositParams = {
74
79
  referrer?: string,
75
80
  transactionSigner: (tx: VersionedTransaction) => Promise<VersionedTransaction>
76
81
  }
77
- export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, connection, base_units, encryptionService, transactionSigner, referrer, mintAddress }: DepositParams) {
78
- let mintInfo = await getMint(connection, mintAddress)
79
- let units_per_token = 10 ** mintInfo.decimals
82
+ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, connection, base_units, amount, encryptionService, transactionSigner, referrer, mintAddress }: DepositParams) {
83
+ if (typeof mintAddress == 'string') {
84
+ mintAddress = new PublicKey(mintAddress)
85
+ }
86
+ let token = tokens.find(t => t.pubkey.toString() == mintAddress.toString())
87
+ if (!token) {
88
+ throw new Error('token not found: ' + mintAddress.toString())
89
+ }
90
+
91
+ if (amount) {
92
+ base_units = amount * token.units_per_token
93
+ }
94
+
95
+ if (!base_units) {
96
+ throw new Error('You must input at least one of "base_units" or "amount"')
97
+ }
98
+
99
+
100
+ // let mintInfo = await getMint(connection, token.pubkey)
101
+ // let units_per_token = 10 ** mintInfo.decimals
80
102
 
81
103
  let recipient = new PublicKey('AWexibGxNFKTa1b5R5MN4PJr9HWnWRwf8EW9g8cLx3dM')
82
104
  let recipient_ata = getAssociatedTokenAddressSync(
83
- mintAddress,
105
+ token.pubkey,
84
106
  recipient,
85
107
  true
86
108
  );
87
109
  let feeRecipientTokenAccount = getAssociatedTokenAddressSync(
88
- mintAddress,
110
+ token.pubkey,
89
111
  FEE_RECIPIENT,
90
112
  true
91
113
  );
92
114
  let signerTokenAccount = getAssociatedTokenAddressSync(
93
- mintAddress,
115
+ token.pubkey,
94
116
  publicKey
95
117
  );
96
118
 
97
- // Derive tree account PDA with mint address for SPL (different from SOL version)
119
+ // Derive tree account PDA with mint address for SPL
98
120
  const [treeAccount] = PublicKey.findProgramAddressSync(
99
- [Buffer.from('merkle_tree'), mintAddress.toBuffer()],
121
+ [Buffer.from('merkle_tree'), token.pubkey.toBuffer()],
100
122
  PROGRAM_ID
101
123
  );
102
124
 
103
- let limitAmount = await checkDepositLimit(connection, treeAccount)
104
- if (limitAmount && base_units > limitAmount * 1e6) {
105
- throw new Error(`Don't deposit more than ${limitAmount} USDC`)
125
+ let limitAmount = await checkDepositLimit(connection, treeAccount, token)
126
+ if (limitAmount && base_units > limitAmount * token.units_per_token) {
127
+ throw new Error(`Don't deposit more than ${limitAmount} ${token.name.toUpperCase()}`)
106
128
  }
107
129
 
108
-
109
-
110
- // check limit
111
- // let limitAmount = await checkDepositLimit(connection)
112
- // if (limitAmount && base_units > limitAmount * units_per_token) {
113
- // throw new Error(`Don't deposit more than ${limitAmount} SOL`)
114
- // }
115
-
116
130
  // const base_units = amount_in_sol * units_per_token
117
131
  const fee_base_units = 0
118
132
  logger.debug('Encryption key generated from user keypair');
119
133
  logger.debug(`User wallet: ${publicKey.toString()}`);
120
- logger.debug(`Deposit amount: ${base_units} base_units (${base_units / units_per_token} USDC)`);
121
- logger.debug(`Calculated fee: ${fee_base_units} base_units (${fee_base_units / units_per_token} USDC)`);
134
+ logger.debug(`Deposit amount: ${base_units} base_units (${base_units / token.units_per_token} ${token.name.toUpperCase()})`);
135
+ logger.debug(`Calculated fee: ${fee_base_units} base_units (${fee_base_units / token.units_per_token} ${token.name.toUpperCase()})`);
122
136
 
123
137
  // Check SPL balance
124
138
  const accountInfo = await getAccount(connection, signerTokenAccount)
125
139
  let balance = Number(accountInfo.amount)
126
- logger.debug(`USDC wallet balance: ${balance / units_per_token} USDC`);
127
- console.log('balance', balance)
128
- console.log('base_units + fee_base_units', base_units + fee_base_units)
140
+ logger.debug(`wallet balance: ${balance / token.units_per_token} ${token.name.toUpperCase()}`);
141
+ logger.debug('balance', balance)
142
+ logger.debug('base_units + fee_base_units', base_units + fee_base_units)
129
143
 
130
144
  if (balance < (base_units + fee_base_units)) {
131
- throw new Error(`Insufficient balance. Need at least ${(base_units + fee_base_units) / units_per_token} USDC.`);
145
+ throw new Error(`Insufficient balance. Need at least ${(base_units + fee_base_units) / token.units_per_token} ${token.name.toUpperCase()}.`);
132
146
  }
133
147
 
134
148
  // Check SOL balance
@@ -145,7 +159,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
145
159
  const tree = new MerkleTree(MERKLE_TREE_DEPTH, lightWasm);
146
160
 
147
161
  // Initialize root and nextIndex variables
148
- const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState('usdc');
162
+ const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState(token.name);
149
163
 
150
164
  logger.debug(`Using tree root: ${root}`);
151
165
  logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
@@ -161,6 +175,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
161
175
  // Fetch existing UTXOs for this user
162
176
  logger.debug('\nFetching existing UTXOs...');
163
177
  const mintUtxos = await getUtxosSPL({ connection, publicKey, encryptionService, storage, mintAddress });
178
+
164
179
  // Calculate output amounts and external amount based on scenario
165
180
  let extAmount: number;
166
181
  let outputAmount: string;
@@ -185,12 +200,12 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
185
200
  new Utxo({
186
201
  lightWasm,
187
202
  keypair: utxoKeypair,
188
- mintAddress: mintAddress.toString()
203
+ mintAddress: token.pubkey.toString()
189
204
  }),
190
205
  new Utxo({
191
206
  lightWasm,
192
207
  keypair: utxoKeypair,
193
- mintAddress: mintAddress.toString()
208
+ mintAddress: token.pubkey.toString()
194
209
  })
195
210
  ];
196
211
 
@@ -220,14 +235,13 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
220
235
  logger.debug(`External amount (deposit): ${extAmount}`);
221
236
 
222
237
  logger.debug('\nFirst UTXO to be consolidated:');
223
- await firstUtxo.log();
224
238
 
225
239
  // Use first existing UTXO as first input, and either second UTXO or dummy UTXO as second input
226
240
  const secondUtxo = mintUtxos.length > 1 ? mintUtxos[1] : new Utxo({
227
241
  lightWasm,
228
242
  keypair: utxoKeypair,
229
243
  amount: '0', // This UTXO will be inserted at currentNextIndex
230
- mintAddress: mintAddress.toString()
244
+ mintAddress: token.pubkey.toString()
231
245
  });
232
246
 
233
247
  inputs = [
@@ -237,13 +251,13 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
237
251
 
238
252
  // Fetch Merkle proofs for real UTXOs
239
253
  const firstUtxoCommitment = await firstUtxo.getCommitment();
240
- const firstUtxoMerkleProof = await fetchMerkleProof(firstUtxoCommitment, 'usdc');
254
+ const firstUtxoMerkleProof = await fetchMerkleProof(firstUtxoCommitment, token.name);
241
255
 
242
256
  let secondUtxoMerkleProof;
243
257
  if (secondUtxo.amount.gt(new BN(0))) {
244
258
  // Second UTXO is real, fetch its proof
245
259
  const secondUtxoCommitment = await secondUtxo.getCommitment();
246
- secondUtxoMerkleProof = await fetchMerkleProof(secondUtxoCommitment, 'usdc');
260
+ secondUtxoMerkleProof = await fetchMerkleProof(secondUtxoCommitment, token.name);
247
261
  logger.debug('\nSecond UTXO to be consolidated:');
248
262
  await secondUtxo.log();
249
263
  }
@@ -278,14 +292,14 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
278
292
  amount: outputAmount,
279
293
  keypair: utxoKeypair,
280
294
  index: currentNextIndex, // This UTXO will be inserted at currentNextIndex
281
- mintAddress: mintAddress.toString()
295
+ mintAddress: token.pubkey.toString()
282
296
  }), // Output with value (either deposit amount minus fee, or input amount minus fee)
283
297
  new Utxo({
284
298
  lightWasm,
285
299
  amount: '0',
286
300
  keypair: utxoKeypair,
287
301
  index: currentNextIndex + 1, // This UTXO will be inserted at currentNextIndex
288
- mintAddress: mintAddress.toString()
302
+ mintAddress: token.pubkey.toString()
289
303
  }) // Empty UTXO
290
304
  ];
291
305
 
@@ -314,10 +328,10 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
314
328
  const encryptedOutput1 = encryptionService.encryptUtxo(outputs[0]);
315
329
  const encryptedOutput2 = encryptionService.encryptUtxo(outputs[1]);
316
330
 
317
- logger.debug(`\nOutput[0] (with value):`);
318
- await outputs[0].log();
319
- logger.debug(`\nOutput[1] (empty):`);
320
- await outputs[1].log();
331
+ // logger.debug(`\nOutput[0] (with value):`);
332
+ // await outputs[0].log();
333
+ // logger.debug(`\nOutput[1] (empty):`);
334
+ // await outputs[1].log();
321
335
 
322
336
  logger.debug(`\nEncrypted output 1 size: ${encryptedOutput1.length} bytes`);
323
337
  logger.debug(`Encrypted output 2 size: ${encryptedOutput2.length} bytes`);
@@ -341,7 +355,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
341
355
  encryptedOutput2: encryptedOutput2,
342
356
  fee: new BN(fee_base_units),
343
357
  feeRecipient: feeRecipientTokenAccount,
344
- mintAddress: mintAddress.toString()
358
+ mintAddress: token.pubkey.toString()
345
359
  };
346
360
  // Calculate the extDataHash with the encrypted outputs (now includes mintAddress for security)
347
361
  const calculatedExtDataHash = getExtDataHash(extData);
@@ -350,7 +364,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
350
364
  const input = {
351
365
  // Common transaction data
352
366
  root: root,
353
- mintAddress: getMintAddressField(mintAddress),// new mint address
367
+ mintAddress: getMintAddressField(token.pubkey),// new mint address
354
368
  publicAmount: publicAmountForCircuit.toString(), // Use proper field arithmetic result
355
369
  extDataHash: calculatedExtDataHash,
356
370
 
@@ -403,7 +417,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
403
417
  [Buffer.from("global_config")],
404
418
  PROGRAM_ID
405
419
  );
406
- const treeAta = getAssociatedTokenAddressSync(mintAddress, globalConfigPda, true);
420
+ const treeAta = getAssociatedTokenAddressSync(token.pubkey, globalConfigPda, true);
407
421
 
408
422
  const lookupTableAccount = await useExistingALT(connection, ALT_ADDRESS);
409
423
 
@@ -428,7 +442,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
428
442
  // signer
429
443
  { pubkey: publicKey, isSigner: true, isWritable: true },
430
444
  // SPL token mint
431
- { pubkey: mintAddress, isSigner: false, isWritable: false },
445
+ { pubkey: token.pubkey, isSigner: false, isWritable: false },
432
446
  // signer's token account
433
447
  { pubkey: signerTokenAccount, isSigner: false, isWritable: true },
434
448
  // recipient (placeholder)
@@ -482,7 +496,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
482
496
  // Relay the pre-signed transaction to indexer backend
483
497
  logger.info('submitting transaction to relayer...')
484
498
  const signature = await relayDepositToIndexer({
485
- mintAddress: mintAddress.toString(),
499
+ mintAddress: token.pubkey.toString(),
486
500
  publicKey,
487
501
  signedTransaction: serializedTransaction,
488
502
  referrer
@@ -500,7 +514,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
500
514
  logger.debug(`retryTimes: ${retryTimes}`)
501
515
  await new Promise(resolve => setTimeout(resolve, itv * 1000));
502
516
  logger.debug('Fetching updated tree state...');
503
- let url = RELAYER_API_URL + '/utxos/check/' + encryptedOutputStr + '?token=usdc'
517
+ let url = RELAYER_API_URL + '/utxos/check/' + encryptedOutputStr + '?token=' + token.name
504
518
  let res = await fetch(url)
505
519
  let resJson = await res.json()
506
520
  if (resJson.exists) {
@@ -516,7 +530,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
516
530
  }
517
531
 
518
532
 
519
- async function checkDepositLimit(connection: Connection, treeAccount: PublicKey) {
533
+ async function checkDepositLimit(connection: Connection, treeAccount: PublicKey, token: Token) {
520
534
  try {
521
535
 
522
536
  // Fetch the account data
@@ -533,21 +547,21 @@ async function checkDepositLimit(connection: Connection, treeAccount: PublicKey)
533
547
  const maxDepositAmount = new BN(accountInfo.data.slice(4120, 4128), 'le');
534
548
  const bump = accountInfo.data[4128];
535
549
 
536
- // Convert to SOL using BN division to handle large numbers
537
- const lamportsPerSol = new BN(1e6);
538
- const maxDepositSol = maxDepositAmount.div(lamportsPerSol);
539
- const remainder = maxDepositAmount.mod(lamportsPerSol);
550
+ // Convert to SPL using BN division to handle large numbers
551
+ const unitesPerToken = new BN(token.units_per_token);
552
+ const maxDepositSpl = maxDepositAmount.div(unitesPerToken);
553
+ const remainder = maxDepositAmount.mod(unitesPerToken);
540
554
 
541
- // Format the SOL amount with decimals
542
- let solFormatted = '1';
555
+ // Format the SPL amount with decimals
556
+ let amountFormatted = '1';
543
557
  if (remainder.eq(new BN(0))) {
544
- solFormatted = maxDepositSol.toString();
558
+ amountFormatted = maxDepositSpl.toString();
545
559
  } else {
546
- // Handle fractional SOL by converting remainder to decimal
547
- const fractional = remainder.toNumber() / 1e6;
548
- solFormatted = `${maxDepositSol.toString()}${fractional.toFixed(9).substring(1)}`;
560
+ // Handle fractional SPL by converting remainder to decimal
561
+ const fractional = remainder.toNumber() / token.units_per_token;
562
+ amountFormatted = `${maxDepositSpl.toString()}${fractional.toFixed(Math.log10(token.units_per_token)).substring(1)}`;
549
563
  }
550
- return Number(solFormatted)
564
+ return Number(amountFormatted)
551
565
 
552
566
  } catch (error) {
553
567
  console.log('❌ Error reading deposit limit:', error);
@@ -7,4 +7,6 @@ export { getBalanceFromUtxos, getUtxos, localstorageKey } from './getUtxos.js'
7
7
 
8
8
  export { depositSPL } from './depositSPL.js'
9
9
  export { withdrawSPL } from './withdrawSPL.js'
10
- export { getBalanceFromUtxosSPL, getUtxosSPL } from './getUtxosSPL.js'
10
+ export { getBalanceFromUtxosSPL, getUtxosSPL } from './getUtxosSPL.js'
11
+
12
+ export { type TokenList, type SplList, tokens } from './utils/constants.js'
@@ -6,7 +6,7 @@ import { EncryptionService } from './utils/encryption.js';
6
6
  import { WasmFactory } from '@lightprotocol/hasher.rs';
7
7
  //@ts-ignore
8
8
  import * as ffjavascript from 'ffjavascript';
9
- import { FETCH_UTXOS_GROUP_SIZE, RELAYER_API_URL, LSK_ENCRYPTED_OUTPUTS, LSK_FETCH_OFFSET, PROGRAM_ID } from './utils/constants.js';
9
+ import { FETCH_UTXOS_GROUP_SIZE, RELAYER_API_URL, LSK_ENCRYPTED_OUTPUTS, LSK_FETCH_OFFSET, PROGRAM_ID, SplList, tokens } from './utils/constants.js';
10
10
  import { logger } from './utils/logger.js';
11
11
  import { getAssociatedTokenAddress } from '@solana/spl-token';
12
12
 
@@ -55,12 +55,12 @@ let decryptionTaskFinished = 0;
55
55
  * @returns Array of decrypted UTXOs that belong to the user
56
56
  */
57
57
 
58
- export async function getUtxosSPL({ publicKey, connection, encryptionService, storage, mintAddress, abortSignal, offset }: {
58
+ export async function getUtxosSPL({ publicKey, connection, encryptionService, storage, abortSignal, offset, mintAddress }: {
59
59
  publicKey: PublicKey,
60
60
  connection: Connection,
61
61
  encryptionService: EncryptionService,
62
62
  storage: Storage,
63
- mintAddress: PublicKey,
63
+ mintAddress: PublicKey | string,
64
64
  abortSignal?: AbortSignal
65
65
  offset?: number
66
66
  }): Promise<Utxo[]> {
@@ -68,9 +68,21 @@ export async function getUtxosSPL({ publicKey, connection, encryptionService, st
68
68
  let valid_strings: string[] = []
69
69
  let history_indexes: number[] = []
70
70
  let publicKey_ata: PublicKey
71
+
72
+ if (typeof mintAddress == 'string') {
73
+ mintAddress = new PublicKey(mintAddress)
74
+ }
75
+
76
+ let token = tokens.find(t => t.pubkey.toString() == mintAddress.toString())
77
+ if (!token) {
78
+ throw new Error('token not found: ' + mintAddress.toString())
79
+ }
80
+
81
+ logger.debug('token name: ' + token.name + ', token address' + token.pubkey.toString())
82
+
71
83
  try {
72
84
  publicKey_ata = await getAssociatedTokenAddress(
73
- mintAddress,
85
+ token.pubkey,
74
86
  publicKey
75
87
  );
76
88
  let offsetStr = storage.getItem(LSK_FETCH_OFFSET + localstorageKey(publicKey_ata))
@@ -93,9 +105,10 @@ export async function getUtxosSPL({ publicKey, connection, encryptionService, st
93
105
  if (offset) {
94
106
  fetch_utxo_offset = Math.max(offset, fetch_utxo_offset)
95
107
  }
108
+ logger.debug(' ####fetch_utxo_offset', fetch_utxo_offset)
96
109
  let fetch_utxo_end = fetch_utxo_offset + FETCH_UTXOS_GROUP_SIZE
97
- let fetch_utxo_url = `${RELAYER_API_URL}/utxos/range?token=usdc&start=${fetch_utxo_offset}&end=${fetch_utxo_end}`
98
- let fetched = await fetchUserUtxos({ publicKey, connection, url: fetch_utxo_url, encryptionService, storage, publicKey_ata, initOffset: offset })
110
+ let fetch_utxo_url = `${RELAYER_API_URL}/utxos/range?token=${token.name}&start=${fetch_utxo_offset}&end=${fetch_utxo_end}`
111
+ let fetched = await fetchUserUtxos({ url: fetch_utxo_url, encryptionService, storage, publicKey_ata, tokenName: token.name })
99
112
  let am = 0
100
113
 
101
114
  const nonZeroUtxos: Utxo[] = [];
@@ -149,18 +162,16 @@ export async function getUtxosSPL({ publicKey, connection, encryptionService, st
149
162
  valid_strings = [...new Set(valid_strings)];
150
163
  logger.debug(`valid_strings len after set: ${valid_strings.length}`)
151
164
  storage.setItem(LSK_ENCRYPTED_OUTPUTS + localstorageKey(publicKey_ata), JSON.stringify(valid_strings))
152
- // reorgnize
153
- return valid_utxos.filter(u => u.mintAddress == mintAddress.toString())
165
+ return valid_utxos.filter(u => u.mintAddress == token.pubkey.toString())
166
+
154
167
  }
155
168
 
156
- async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionService, publicKey_ata, initOffset }: {
157
- publicKey: PublicKey,
158
- connection: Connection,
169
+ async function fetchUserUtxos({ url, storage, encryptionService, publicKey_ata, tokenName }: {
159
170
  url: string,
160
171
  encryptionService: EncryptionService,
161
172
  storage: Storage,
162
173
  publicKey_ata: PublicKey
163
- initOffset: number
174
+ tokenName: string
164
175
  }): Promise<{
165
176
  encryptedOutputs: string[],
166
177
  utxos: Utxo[],
@@ -212,7 +223,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
212
223
 
213
224
 
214
225
  let decryptionTaskTotal = data.total + cachedStringNum - roundStartIndex;
215
- let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm)
226
+ let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName)
216
227
  decryptionTaskFinished += encryptedOutputs.length
217
228
  logger.debug('batchReslen', batchRes.length)
218
229
  for (let i = 0; i < batchRes.length; i++) {
@@ -230,7 +241,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
230
241
  if (decryptionTaskFinished % 100 == 0) {
231
242
  logger.info(`(decrypting cached utxo: ${decryptionTaskFinished + 1}/${decryptionTaskTotal}...)`)
232
243
  }
233
- let batchRes = await decrypt_outputs(cachedEncryptedOutputs, encryptionService, utxoKeypair, lightWasm)
244
+ let batchRes = await decrypt_outputs(cachedEncryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName)
234
245
  decryptionTaskFinished += cachedEncryptedOutputs.length
235
246
  logger.debug('cachedbatchReslen', batchRes.length, ' source', cachedEncryptedOutputs.length)
236
247
  for (let i = 0; i < batchRes.length; i++) {
@@ -346,11 +357,23 @@ async function areUtxosSpent(
346
357
  // Calculate total balance
347
358
  export function getBalanceFromUtxosSPL(utxos: Utxo[]): {
348
359
  base_units: number
360
+ amount: number
349
361
  /** @deprecated use base_units instead */
350
362
  lamports: number
351
363
  } {
364
+ if (!utxos.length) {
365
+ return { base_units: 0, amount: 0, lamports: 0 }
366
+ }
367
+ let token = tokens.find(t => t.pubkey.toString() == utxos[0].mintAddress.toString())
368
+ if (!token) {
369
+ throw new Error('token not found for ' + utxos[0].mintAddress.toString())
370
+ }
352
371
  const totalBalance = utxos.reduce((sum, utxo) => sum.add(utxo.amount), new BN(0));
353
- return { base_units: totalBalance.toNumber(), lamports: totalBalance.toNumber() }
372
+ return {
373
+ base_units: totalBalance.toNumber(),
374
+ lamports: totalBalance.toNumber(),
375
+ amount: totalBalance.toNumber() / token.units_per_token
376
+ }
354
377
  }
355
378
 
356
379
  // Decrypt single output to Utxo
@@ -454,6 +477,7 @@ async function decrypt_outputs(
454
477
  encryptionService: EncryptionService,
455
478
  utxoKeypair: UtxoKeypair,
456
479
  lightWasm: any,
480
+ tokenName: string
457
481
  ): Promise<DecryptRes[]> {
458
482
  let results: DecryptRes[] = [];
459
483
 
@@ -485,7 +509,7 @@ async function decrypt_outputs(
485
509
  let url = RELAYER_API_URL + `/utxos/indices`
486
510
  let res = await fetch(url, {
487
511
  method: 'POST', headers: { "Content-Type": "application/json" },
488
- body: JSON.stringify({ encrypted_outputs, token: 'usdc' })
512
+ body: JSON.stringify({ encrypted_outputs, token: tokenName })
489
513
  })
490
514
  let j = await res.json()
491
515
  if (!j.indices || !Array.isArray(j.indices) || j.indices.length != encrypted_outputs.length) {
package/src/index.ts CHANGED
@@ -3,7 +3,7 @@ import { deposit } from './deposit.js';
3
3
  import { getBalanceFromUtxos, getUtxos, localstorageKey } from './getUtxos.js';
4
4
  import { getBalanceFromUtxosSPL, getUtxosSPL } from './getUtxosSPL.js';
5
5
 
6
- import { LSK_ENCRYPTED_OUTPUTS, LSK_FETCH_OFFSET, USDC_MINT } from './utils/constants.js';
6
+ import { LSK_ENCRYPTED_OUTPUTS, LSK_FETCH_OFFSET, SplList, TokenList, tokens, USDC_MINT } from './utils/constants.js';
7
7
  import { logger, type LoggerFn, setLogger } from './utils/logger.js';
8
8
  import { EncryptionService } from './utils/encryption.js';
9
9
  import { WasmFactory } from '@lightprotocol/hasher.rs';
@@ -70,10 +70,9 @@ export class PrivacyCash {
70
70
  storage.removeItem(LSK_FETCH_OFFSET + localstorageKey(this.publicKey))
71
71
  storage.removeItem(LSK_ENCRYPTED_OUTPUTS + localstorageKey(this.publicKey))
72
72
  // spl
73
- let mintAddresses = [USDC_MINT]
74
- for (let mintAddress of mintAddresses) {
73
+ for (let token of tokens) {
75
74
  let ata = await getAssociatedTokenAddress(
76
- mintAddress,
75
+ token.pubkey,
77
76
  this.publicKey
78
77
  );
79
78
  storage.removeItem(LSK_FETCH_OFFSET + localstorageKey(ata))
@@ -160,7 +159,7 @@ export class PrivacyCash {
160
159
  keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
161
160
  storage
162
161
  })
163
- console.log(`Withdraw successful. Recipient ${recipient} received ${res.amount_in_lamports / LAMPORTS_PER_SOL} SOL, with ${res.fee_in_lamports / LAMPORTS_PER_SOL} SOL relayers fees`)
162
+ logger.debug(`Withdraw successful. Recipient ${recipient} received ${res.amount_in_lamports / LAMPORTS_PER_SOL} SOL, with ${res.fee_in_lamports / LAMPORTS_PER_SOL} SOL relayers fees`)
164
163
  this.isRuning = false
165
164
  return res
166
165
  }
@@ -189,7 +188,7 @@ export class PrivacyCash {
189
188
  keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
190
189
  storage
191
190
  })
192
- console.log(`Withdraw successful. Recipient ${recipient} received ${base_units} USDC units`)
191
+ logger.debug(`Withdraw successful. Recipient ${recipient} received ${base_units} USDC units`)
193
192
  this.isRuning = false
194
193
  return res
195
194
  }
@@ -206,7 +205,7 @@ export class PrivacyCash {
206
205
  }
207
206
 
208
207
  /**
209
- * Returns the amount of lamports current wallet has in Privacy Cash.
208
+ * Returns the amount of base unites current wallet has in Privacy Cash.
210
209
  */
211
210
  async getPrivateBalanceUSDC() {
212
211
  logger.info('getting private balance')
@@ -216,6 +215,22 @@ export class PrivacyCash {
216
215
  return getBalanceFromUtxosSPL(utxos)
217
216
  }
218
217
 
218
+ /**
219
+ * Returns the amount of base unites current wallet has in Privacy Cash.
220
+ */
221
+ async getPrivateBalanceSpl(mintAddress: PublicKey | string) {
222
+ this.isRuning = true
223
+ let utxos = await getUtxosSPL({
224
+ publicKey: this.publicKey,
225
+ connection: this.connection,
226
+ encryptionService: this.encryptionService,
227
+ storage,
228
+ mintAddress
229
+ })
230
+ this.isRuning = false
231
+ return getBalanceFromUtxosSPL(utxos)
232
+ }
233
+
219
234
  /**
220
235
  * Returns true if the code is running in a browser.
221
236
  */
@@ -235,6 +250,69 @@ export class PrivacyCash {
235
250
  await new Promise(r => setTimeout(r, 250));
236
251
  }
237
252
  }
253
+
254
+ /**
255
+ * Deposit SPL to the Privacy Cash.
256
+ */
257
+ async depositSPL({ base_units, mintAddress, amount }: {
258
+ base_units?: number,
259
+ amount?: number,
260
+ mintAddress: PublicKey | string
261
+ }) {
262
+ this.isRuning = true
263
+ logger.info('start depositting')
264
+ let lightWasm = await WasmFactory.getInstance()
265
+ let res = await depositSPL({
266
+ lightWasm,
267
+ base_units,
268
+ amount,
269
+ connection: this.connection,
270
+ encryptionService: this.encryptionService,
271
+ publicKey: this.publicKey,
272
+ transactionSigner: async (tx: VersionedTransaction) => {
273
+ tx.sign([this.keypair])
274
+ return tx
275
+ },
276
+ keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
277
+ storage,
278
+ mintAddress
279
+ })
280
+ this.isRuning = false
281
+ return res
282
+ }
283
+
284
+ /**
285
+ * Withdraw SPL from the Privacy Cash.
286
+ */
287
+ async withdrawSPL({ base_units, mintAddress, recipientAddress, amount }: {
288
+ base_units?: number,
289
+ amount?: number,
290
+ mintAddress: PublicKey | string,
291
+ recipientAddress?: string
292
+ }) {
293
+ this.isRuning = true
294
+ logger.info('start withdrawing')
295
+ let lightWasm = await WasmFactory.getInstance()
296
+ let recipient = recipientAddress ? new PublicKey(recipientAddress) : this.publicKey
297
+
298
+ let res = await withdrawSPL({
299
+ lightWasm,
300
+ base_units,
301
+ amount,
302
+ connection: this.connection,
303
+ encryptionService: this.encryptionService,
304
+ publicKey: this.publicKey,
305
+ recipient,
306
+ keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
307
+ storage,
308
+ mintAddress
309
+ })
310
+ logger.debug(`Withdraw successful. Recipient ${recipient} received ${base_units} USDC units`)
311
+ this.isRuning = false
312
+ return res
313
+ }
314
+
315
+
238
316
  }
239
317
 
240
318
  function getSolanaKeypair(
@@ -11,6 +11,7 @@ import {
11
11
  TransactionMessage
12
12
  } from '@solana/web3.js';
13
13
  import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token';
14
+ import { logger } from './logger';
14
15
 
15
16
 
16
17
  /**
@@ -22,13 +23,13 @@ export async function useExistingALT(
22
23
  altAddress: PublicKey
23
24
  ): Promise<{ value: any } | null> {
24
25
  try {
25
- console.log(`Using existing ALT: ${altAddress.toString()}`);
26
+ logger.debug(`Using existing ALT: ${altAddress.toString()}`);
26
27
  const altAccount = await connection.getAddressLookupTable(altAddress);
27
28
 
28
29
  if (altAccount.value) {
29
- console.log(`✅ ALT found with ${altAccount.value.state.addresses.length} addresses`);
30
+ logger.debug(`✅ ALT found with ${altAccount.value.state.addresses.length} addresses`);
30
31
  } else {
31
- console.log('❌ ALT not found');
32
+ logger.error('❌ ALT not found');
32
33
  }
33
34
 
34
35
  return altAccount;