privacycash 1.0.19 → 1.1.0

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',
@@ -63,10 +63,11 @@ async function relayDepositToIndexer({ signedTransaction, publicKey, referrer, m
63
63
  }
64
64
 
65
65
  type DepositParams = {
66
- mintAddress: PublicKey,
66
+ mintAddress: PublicKey | string,
67
67
  publicKey: PublicKey,
68
68
  connection: Connection,
69
- base_units: number,
69
+ base_units?: number,
70
+ amount?: number,
70
71
  storage: Storage,
71
72
  encryptionService: EncryptionService,
72
73
  keyBasePath: string,
@@ -74,61 +75,70 @@ type DepositParams = {
74
75
  referrer?: string,
75
76
  transactionSigner: (tx: VersionedTransaction) => Promise<VersionedTransaction>
76
77
  }
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
78
+ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, connection, base_units, amount, encryptionService, transactionSigner, referrer, mintAddress }: DepositParams) {
79
+ if (typeof mintAddress == 'string') {
80
+ mintAddress = new PublicKey(mintAddress)
81
+ }
82
+ let token = tokens.find(t => t.pubkey.toString() == mintAddress.toString())
83
+ if (!token) {
84
+ throw new Error('token not found: ' + mintAddress.toString())
85
+ }
86
+
87
+ if (amount) {
88
+ base_units = amount * token.units_per_token
89
+ }
90
+
91
+ if (!base_units) {
92
+ throw new Error('You must input at least one of "base_units" or "amount"')
93
+ }
94
+
95
+
96
+ // let mintInfo = await getMint(connection, token.pubkey)
97
+ // let units_per_token = 10 ** mintInfo.decimals
80
98
 
81
99
  let recipient = new PublicKey('AWexibGxNFKTa1b5R5MN4PJr9HWnWRwf8EW9g8cLx3dM')
82
100
  let recipient_ata = getAssociatedTokenAddressSync(
83
- mintAddress,
101
+ token.pubkey,
84
102
  recipient,
85
103
  true
86
104
  );
87
105
  let feeRecipientTokenAccount = getAssociatedTokenAddressSync(
88
- mintAddress,
106
+ token.pubkey,
89
107
  FEE_RECIPIENT,
90
108
  true
91
109
  );
92
110
  let signerTokenAccount = getAssociatedTokenAddressSync(
93
- mintAddress,
111
+ token.pubkey,
94
112
  publicKey
95
113
  );
96
114
 
97
- // Derive tree account PDA with mint address for SPL (different from SOL version)
115
+ // Derive tree account PDA with mint address for SPL
98
116
  const [treeAccount] = PublicKey.findProgramAddressSync(
99
- [Buffer.from('merkle_tree'), mintAddress.toBuffer()],
117
+ [Buffer.from('merkle_tree'), token.pubkey.toBuffer()],
100
118
  PROGRAM_ID
101
119
  );
102
120
 
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`)
121
+ let limitAmount = await checkDepositLimit(connection, treeAccount, token)
122
+ if (limitAmount && base_units > limitAmount * token.units_per_token) {
123
+ throw new Error(`Don't deposit more than ${limitAmount} ${token.name.toUpperCase()}`)
106
124
  }
107
125
 
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
126
  // const base_units = amount_in_sol * units_per_token
117
127
  const fee_base_units = 0
118
128
  logger.debug('Encryption key generated from user keypair');
119
129
  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)`);
130
+ logger.debug(`Deposit amount: ${base_units} base_units (${base_units / token.units_per_token} ${token.name.toUpperCase()})`);
131
+ logger.debug(`Calculated fee: ${fee_base_units} base_units (${fee_base_units / token.units_per_token} ${token.name.toUpperCase()})`);
122
132
 
123
133
  // Check SPL balance
124
134
  const accountInfo = await getAccount(connection, signerTokenAccount)
125
135
  let balance = Number(accountInfo.amount)
126
- logger.debug(`USDC wallet balance: ${balance / units_per_token} USDC`);
136
+ logger.debug(`wallet balance: ${balance / token.units_per_token} ${token.name.toUpperCase()}`);
127
137
  console.log('balance', balance)
128
138
  console.log('base_units + fee_base_units', base_units + fee_base_units)
129
139
 
130
140
  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.`);
141
+ throw new Error(`Insufficient balance. Need at least ${(base_units + fee_base_units) / token.units_per_token} ${token.name.toUpperCase()}.`);
132
142
  }
133
143
 
134
144
  // Check SOL balance
@@ -145,7 +155,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
145
155
  const tree = new MerkleTree(MERKLE_TREE_DEPTH, lightWasm);
146
156
 
147
157
  // Initialize root and nextIndex variables
148
- const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState('usdc');
158
+ const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState(token.name);
149
159
 
150
160
  logger.debug(`Using tree root: ${root}`);
151
161
  logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
@@ -185,12 +195,12 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
185
195
  new Utxo({
186
196
  lightWasm,
187
197
  keypair: utxoKeypair,
188
- mintAddress: mintAddress.toString()
198
+ mintAddress: token.pubkey.toString()
189
199
  }),
190
200
  new Utxo({
191
201
  lightWasm,
192
202
  keypair: utxoKeypair,
193
- mintAddress: mintAddress.toString()
203
+ mintAddress: token.pubkey.toString()
194
204
  })
195
205
  ];
196
206
 
@@ -227,7 +237,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
227
237
  lightWasm,
228
238
  keypair: utxoKeypair,
229
239
  amount: '0', // This UTXO will be inserted at currentNextIndex
230
- mintAddress: mintAddress.toString()
240
+ mintAddress: token.pubkey.toString()
231
241
  });
232
242
 
233
243
  inputs = [
@@ -237,13 +247,13 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
237
247
 
238
248
  // Fetch Merkle proofs for real UTXOs
239
249
  const firstUtxoCommitment = await firstUtxo.getCommitment();
240
- const firstUtxoMerkleProof = await fetchMerkleProof(firstUtxoCommitment, 'usdc');
250
+ const firstUtxoMerkleProof = await fetchMerkleProof(firstUtxoCommitment, token.name);
241
251
 
242
252
  let secondUtxoMerkleProof;
243
253
  if (secondUtxo.amount.gt(new BN(0))) {
244
254
  // Second UTXO is real, fetch its proof
245
255
  const secondUtxoCommitment = await secondUtxo.getCommitment();
246
- secondUtxoMerkleProof = await fetchMerkleProof(secondUtxoCommitment, 'usdc');
256
+ secondUtxoMerkleProof = await fetchMerkleProof(secondUtxoCommitment, token.name);
247
257
  logger.debug('\nSecond UTXO to be consolidated:');
248
258
  await secondUtxo.log();
249
259
  }
@@ -278,14 +288,14 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
278
288
  amount: outputAmount,
279
289
  keypair: utxoKeypair,
280
290
  index: currentNextIndex, // This UTXO will be inserted at currentNextIndex
281
- mintAddress: mintAddress.toString()
291
+ mintAddress: token.pubkey.toString()
282
292
  }), // Output with value (either deposit amount minus fee, or input amount minus fee)
283
293
  new Utxo({
284
294
  lightWasm,
285
295
  amount: '0',
286
296
  keypair: utxoKeypair,
287
297
  index: currentNextIndex + 1, // This UTXO will be inserted at currentNextIndex
288
- mintAddress: mintAddress.toString()
298
+ mintAddress: token.pubkey.toString()
289
299
  }) // Empty UTXO
290
300
  ];
291
301
 
@@ -341,7 +351,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
341
351
  encryptedOutput2: encryptedOutput2,
342
352
  fee: new BN(fee_base_units),
343
353
  feeRecipient: feeRecipientTokenAccount,
344
- mintAddress: mintAddress.toString()
354
+ mintAddress: token.pubkey.toString()
345
355
  };
346
356
  // Calculate the extDataHash with the encrypted outputs (now includes mintAddress for security)
347
357
  const calculatedExtDataHash = getExtDataHash(extData);
@@ -350,7 +360,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
350
360
  const input = {
351
361
  // Common transaction data
352
362
  root: root,
353
- mintAddress: getMintAddressField(mintAddress),// new mint address
363
+ mintAddress: getMintAddressField(token.pubkey),// new mint address
354
364
  publicAmount: publicAmountForCircuit.toString(), // Use proper field arithmetic result
355
365
  extDataHash: calculatedExtDataHash,
356
366
 
@@ -403,7 +413,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
403
413
  [Buffer.from("global_config")],
404
414
  PROGRAM_ID
405
415
  );
406
- const treeAta = getAssociatedTokenAddressSync(mintAddress, globalConfigPda, true);
416
+ const treeAta = getAssociatedTokenAddressSync(token.pubkey, globalConfigPda, true);
407
417
 
408
418
  const lookupTableAccount = await useExistingALT(connection, ALT_ADDRESS);
409
419
 
@@ -428,7 +438,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
428
438
  // signer
429
439
  { pubkey: publicKey, isSigner: true, isWritable: true },
430
440
  // SPL token mint
431
- { pubkey: mintAddress, isSigner: false, isWritable: false },
441
+ { pubkey: token.pubkey, isSigner: false, isWritable: false },
432
442
  // signer's token account
433
443
  { pubkey: signerTokenAccount, isSigner: false, isWritable: true },
434
444
  // recipient (placeholder)
@@ -482,7 +492,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
482
492
  // Relay the pre-signed transaction to indexer backend
483
493
  logger.info('submitting transaction to relayer...')
484
494
  const signature = await relayDepositToIndexer({
485
- mintAddress: mintAddress.toString(),
495
+ mintAddress: token.pubkey.toString(),
486
496
  publicKey,
487
497
  signedTransaction: serializedTransaction,
488
498
  referrer
@@ -500,7 +510,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
500
510
  logger.debug(`retryTimes: ${retryTimes}`)
501
511
  await new Promise(resolve => setTimeout(resolve, itv * 1000));
502
512
  logger.debug('Fetching updated tree state...');
503
- let url = RELAYER_API_URL + '/utxos/check/' + encryptedOutputStr + '?token=usdc'
513
+ let url = RELAYER_API_URL + '/utxos/check/' + encryptedOutputStr + '?token=' + token.name
504
514
  let res = await fetch(url)
505
515
  let resJson = await res.json()
506
516
  if (resJson.exists) {
@@ -516,7 +526,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
516
526
  }
517
527
 
518
528
 
519
- async function checkDepositLimit(connection: Connection, treeAccount: PublicKey) {
529
+ async function checkDepositLimit(connection: Connection, treeAccount: PublicKey, token: Token) {
520
530
  try {
521
531
 
522
532
  // Fetch the account data
@@ -533,21 +543,21 @@ async function checkDepositLimit(connection: Connection, treeAccount: PublicKey)
533
543
  const maxDepositAmount = new BN(accountInfo.data.slice(4120, 4128), 'le');
534
544
  const bump = accountInfo.data[4128];
535
545
 
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);
546
+ // Convert to SPL using BN division to handle large numbers
547
+ const unitesPerToken = new BN(token.units_per_token);
548
+ const maxDepositSpl = maxDepositAmount.div(unitesPerToken);
549
+ const remainder = maxDepositAmount.mod(unitesPerToken);
540
550
 
541
- // Format the SOL amount with decimals
542
- let solFormatted = '1';
551
+ // Format the SPL amount with decimals
552
+ let amountFormatted = '1';
543
553
  if (remainder.eq(new BN(0))) {
544
- solFormatted = maxDepositSol.toString();
554
+ amountFormatted = maxDepositSpl.toString();
545
555
  } 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)}`;
556
+ // Handle fractional SPL by converting remainder to decimal
557
+ const fractional = remainder.toNumber() / token.units_per_token;
558
+ amountFormatted = `${maxDepositSpl.toString()}${fractional.toFixed(Math.log10(token.units_per_token)).substring(1)}`;
549
559
  }
550
- return Number(solFormatted)
560
+ return Number(amountFormatted)
551
561
 
552
562
  } catch (error) {
553
563
  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'
package/src/getUtxos.ts CHANGED
@@ -51,12 +51,13 @@ let decryptionTaskFinished = 0;
51
51
  * @returns Array of decrypted UTXOs that belong to the user
52
52
  */
53
53
 
54
- export async function getUtxos({ publicKey, connection, encryptionService, storage, abortSignal }: {
54
+ export async function getUtxos({ publicKey, connection, encryptionService, storage, abortSignal, offset }: {
55
55
  publicKey: PublicKey,
56
56
  connection: Connection,
57
57
  encryptionService: EncryptionService,
58
58
  storage: Storage,
59
59
  abortSignal?: AbortSignal
60
+ offset?: number
60
61
  }): Promise<Utxo[]> {
61
62
 
62
63
  let valid_utxos: Utxo[] = []
@@ -69,15 +70,22 @@ export async function getUtxos({ publicKey, connection, encryptionService, stora
69
70
  roundStartIndex = 0
70
71
  }
71
72
  decryptionTaskFinished = 0
73
+ if (!offset) {
74
+ offset = 0
75
+ }
76
+ roundStartIndex = Math.max(offset, roundStartIndex)
72
77
  while (true) {
73
78
  if (abortSignal?.aborted) {
74
79
  throw new Error('aborted')
75
80
  }
76
81
  let offsetStr = storage.getItem(LSK_FETCH_OFFSET + localstorageKey(publicKey))
77
82
  let fetch_utxo_offset = offsetStr ? Number(offsetStr) : 0
83
+ if (offset) {
84
+ fetch_utxo_offset = Math.max(offset, fetch_utxo_offset)
85
+ }
78
86
  let fetch_utxo_end = fetch_utxo_offset + FETCH_UTXOS_GROUP_SIZE
79
87
  let fetch_utxo_url = `${RELAYER_API_URL}/utxos/range?start=${fetch_utxo_offset}&end=${fetch_utxo_end}`
80
- let fetched = await fetchUserUtxos({ publicKey, connection, url: fetch_utxo_url, encryptionService, storage })
88
+ let fetched = await fetchUserUtxos({ publicKey, connection, url: fetch_utxo_url, encryptionService, storage, initOffset: offset })
81
89
  let am = 0
82
90
 
83
91
  const nonZeroUtxos: Utxo[] = [];
@@ -131,12 +139,13 @@ export async function getUtxos({ publicKey, connection, encryptionService, stora
131
139
 
132
140
  }
133
141
 
134
- async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionService }: {
142
+ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionService, initOffset }: {
135
143
  publicKey: PublicKey,
136
144
  connection: Connection,
137
145
  url: string,
138
146
  encryptionService: EncryptionService,
139
147
  storage: Storage
148
+ initOffset: number
140
149
  }): Promise<{
141
150
  encryptedOutputs: string[],
142
151
  utxos: Utxo[],
@@ -188,6 +197,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
188
197
 
189
198
 
190
199
  let decryptionTaskTotal = data.total + cachedStringNum - roundStartIndex;
200
+
191
201
  let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm)
192
202
  decryptionTaskFinished += encryptedOutputs.length
193
203
  logger.debug('batchReslen', batchRes.length)
@@ -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,21 +55,34 @@ 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 }: {
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
+ offset?: number
65
66
  }): Promise<Utxo[]> {
66
67
  let valid_utxos: Utxo[] = []
67
68
  let valid_strings: string[] = []
68
69
  let history_indexes: number[] = []
69
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
+
70
83
  try {
71
84
  publicKey_ata = await getAssociatedTokenAddress(
72
- mintAddress,
85
+ token.pubkey,
73
86
  publicKey
74
87
  );
75
88
  let offsetStr = storage.getItem(LSK_FETCH_OFFSET + localstorageKey(publicKey_ata))
@@ -79,15 +92,23 @@ export async function getUtxosSPL({ publicKey, connection, encryptionService, st
79
92
  roundStartIndex = 0
80
93
  }
81
94
  decryptionTaskFinished = 0
95
+ if (!offset) {
96
+ offset = 0
97
+ }
98
+ roundStartIndex = Math.max(offset, roundStartIndex)
82
99
  while (true) {
83
100
  if (abortSignal?.aborted) {
84
101
  throw new Error('aborted')
85
102
  }
86
103
  let offsetStr = storage.getItem(LSK_FETCH_OFFSET + localstorageKey(publicKey_ata))
87
104
  let fetch_utxo_offset = offsetStr ? Number(offsetStr) : 0
105
+ if (offset) {
106
+ fetch_utxo_offset = Math.max(offset, fetch_utxo_offset)
107
+ }
108
+ console.log(' ####fetch_utxo_offset', fetch_utxo_offset)
88
109
  let fetch_utxo_end = fetch_utxo_offset + FETCH_UTXOS_GROUP_SIZE
89
- let fetch_utxo_url = `${RELAYER_API_URL}/utxos/range?token=usdc&start=${fetch_utxo_offset}&end=${fetch_utxo_end}`
90
- let fetched = await fetchUserUtxos({ publicKey, connection, url: fetch_utxo_url, encryptionService, storage, publicKey_ata })
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 })
91
112
  let am = 0
92
113
 
93
114
  const nonZeroUtxos: Utxo[] = [];
@@ -142,16 +163,20 @@ export async function getUtxosSPL({ publicKey, connection, encryptionService, st
142
163
  logger.debug(`valid_strings len after set: ${valid_strings.length}`)
143
164
  storage.setItem(LSK_ENCRYPTED_OUTPUTS + localstorageKey(publicKey_ata), JSON.stringify(valid_strings))
144
165
  // reorgnize
145
- return valid_utxos.filter(u => u.mintAddress == mintAddress.toString())
166
+ if (valid_utxos.length) {
167
+ console.log('filter mint', valid_utxos[0].mintAddress, token.pubkey.toString())
168
+ }
169
+ let filtered_utxos = valid_utxos.filter(u => u.mintAddress == token.pubkey.toString())
170
+ console.log('filtered_utxos.len', filtered_utxos.length)
171
+ return filtered_utxos
146
172
  }
147
173
 
148
- async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionService, publicKey_ata }: {
149
- publicKey: PublicKey,
150
- connection: Connection,
174
+ async function fetchUserUtxos({ url, storage, encryptionService, publicKey_ata, tokenName }: {
151
175
  url: string,
152
176
  encryptionService: EncryptionService,
153
177
  storage: Storage,
154
178
  publicKey_ata: PublicKey
179
+ tokenName: string
155
180
  }): Promise<{
156
181
  encryptedOutputs: string[],
157
182
  utxos: Utxo[],
@@ -203,7 +228,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
203
228
 
204
229
 
205
230
  let decryptionTaskTotal = data.total + cachedStringNum - roundStartIndex;
206
- let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm)
231
+ let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName)
207
232
  decryptionTaskFinished += encryptedOutputs.length
208
233
  logger.debug('batchReslen', batchRes.length)
209
234
  for (let i = 0; i < batchRes.length; i++) {
@@ -221,7 +246,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
221
246
  if (decryptionTaskFinished % 100 == 0) {
222
247
  logger.info(`(decrypting cached utxo: ${decryptionTaskFinished + 1}/${decryptionTaskTotal}...)`)
223
248
  }
224
- let batchRes = await decrypt_outputs(cachedEncryptedOutputs, encryptionService, utxoKeypair, lightWasm)
249
+ let batchRes = await decrypt_outputs(cachedEncryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName)
225
250
  decryptionTaskFinished += cachedEncryptedOutputs.length
226
251
  logger.debug('cachedbatchReslen', batchRes.length, ' source', cachedEncryptedOutputs.length)
227
252
  for (let i = 0; i < batchRes.length; i++) {
@@ -337,11 +362,23 @@ async function areUtxosSpent(
337
362
  // Calculate total balance
338
363
  export function getBalanceFromUtxosSPL(utxos: Utxo[]): {
339
364
  base_units: number
365
+ amount: number
340
366
  /** @deprecated use base_units instead */
341
367
  lamports: number
342
368
  } {
369
+ if (!utxos.length) {
370
+ return { base_units: 0, amount: 0, lamports: 0 }
371
+ }
372
+ let token = tokens.find(t => t.pubkey.toString() == utxos[0].mintAddress.toString())
373
+ if (!token) {
374
+ throw new Error('token not found for ' + utxos[0].mintAddress.toString())
375
+ }
343
376
  const totalBalance = utxos.reduce((sum, utxo) => sum.add(utxo.amount), new BN(0));
344
- return { base_units: totalBalance.toNumber(), lamports: totalBalance.toNumber() }
377
+ return {
378
+ base_units: totalBalance.toNumber(),
379
+ lamports: totalBalance.toNumber(),
380
+ amount: totalBalance.toNumber() / token.units_per_token
381
+ }
345
382
  }
346
383
 
347
384
  // Decrypt single output to Utxo
@@ -445,6 +482,7 @@ async function decrypt_outputs(
445
482
  encryptionService: EncryptionService,
446
483
  utxoKeypair: UtxoKeypair,
447
484
  lightWasm: any,
485
+ tokenName: string
448
486
  ): Promise<DecryptRes[]> {
449
487
  let results: DecryptRes[] = [];
450
488
 
@@ -476,7 +514,7 @@ async function decrypt_outputs(
476
514
  let url = RELAYER_API_URL + `/utxos/indices`
477
515
  let res = await fetch(url, {
478
516
  method: 'POST', headers: { "Content-Type": "application/json" },
479
- body: JSON.stringify({ encrypted_outputs, token: 'usdc' })
517
+ body: JSON.stringify({ encrypted_outputs, token: tokenName })
480
518
  })
481
519
  let j = await res.json()
482
520
  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))
@@ -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
+ console.log(`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(