privacycash 1.0.21 → 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/dist/config.d.ts CHANGED
@@ -3,6 +3,7 @@ type Config = {
3
3
  withdraw_rent_fee: number;
4
4
  deposit_fee_rate: number;
5
5
  usdc_withdraw_rent_fee: number;
6
+ rent_fees: any;
6
7
  };
7
8
  export declare function getConfig<K extends keyof Config>(key: K): Promise<Config[K]>;
8
9
  export {};
package/dist/config.js CHANGED
@@ -5,7 +5,7 @@ export async function getConfig(key) {
5
5
  const res = await fetch(RELAYER_API_URL + '/config');
6
6
  config = await res.json();
7
7
  }
8
- if (typeof config[key] != 'number') {
8
+ if (typeof config[key] == 'undefined') {
9
9
  throw new Error(`can not get ${key} from ${RELAYER_API_URL}/config`);
10
10
  }
11
11
  return config[key];
package/dist/deposit.js CHANGED
@@ -360,7 +360,7 @@ async function checkDepositLimit(connection) {
360
360
  // Fetch the account data
361
361
  const accountInfo = await connection.getAccountInfo(treeAccount);
362
362
  if (!accountInfo) {
363
- console.error('❌ Tree account not found. Make sure the program is initialized.');
363
+ console.error('❌ Tree account not found. Make sure the program is initialized.' + PROGRAM_ID);
364
364
  return;
365
365
  }
366
366
  console.log(`Account data size: ${accountInfo.data.length} bytes`);
@@ -2,10 +2,11 @@ import { Connection, PublicKey, VersionedTransaction } from '@solana/web3.js';
2
2
  import * as hasher from '@lightprotocol/hasher.rs';
3
3
  import { EncryptionService } from './utils/encryption.js';
4
4
  type DepositParams = {
5
- mintAddress: PublicKey;
5
+ mintAddress: PublicKey | string;
6
6
  publicKey: PublicKey;
7
7
  connection: Connection;
8
- base_units: number;
8
+ base_units?: number;
9
+ amount?: number;
9
10
  storage: Storage;
10
11
  encryptionService: EncryptionService;
11
12
  keyBasePath: string;
@@ -13,7 +14,7 @@ type DepositParams = {
13
14
  referrer?: string;
14
15
  transactionSigner: (tx: VersionedTransaction) => Promise<VersionedTransaction>;
15
16
  };
16
- export declare function depositSPL({ lightWasm, storage, keyBasePath, publicKey, connection, base_units, encryptionService, transactionSigner, referrer, mintAddress }: DepositParams): Promise<{
17
+ export declare function depositSPL({ lightWasm, storage, keyBasePath, publicKey, connection, base_units, amount, encryptionService, transactionSigner, referrer, mintAddress }: DepositParams): Promise<{
17
18
  tx: string;
18
19
  }>;
19
20
  export {};
@@ -7,10 +7,10 @@ import { MerkleTree } from './utils/merkle_tree.js';
7
7
  import { serializeProofAndExtData } from './utils/encryption.js';
8
8
  import { Keypair as UtxoKeypair } from './models/keypair.js';
9
9
  import { getUtxosSPL } from './getUtxosSPL.js';
10
- import { FIELD_SIZE, FEE_RECIPIENT, MERKLE_TREE_DEPTH, RELAYER_API_URL, PROGRAM_ID, ALT_ADDRESS } from './utils/constants.js';
10
+ import { FIELD_SIZE, FEE_RECIPIENT, MERKLE_TREE_DEPTH, RELAYER_API_URL, PROGRAM_ID, ALT_ADDRESS, tokens } from './utils/constants.js';
11
11
  import { useExistingALT } from './utils/address_lookup_table.js';
12
12
  import { logger } from './utils/logger.js';
13
- import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, getMint, getAccount } from '@solana/spl-token';
13
+ import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, getAccount } from '@solana/spl-token';
14
14
  // Function to relay pre-signed deposit transaction to indexer backend
15
15
  async function relayDepositToIndexer({ signedTransaction, publicKey, referrer, mintAddress }) {
16
16
  try {
@@ -22,7 +22,7 @@ async function relayDepositToIndexer({ signedTransaction, publicKey, referrer, m
22
22
  if (referrer) {
23
23
  params.referralWalletAddress = referrer;
24
24
  }
25
- params.mintAddress = mintAddress.toString();
25
+ params.mintAddress = mintAddress;
26
26
  const response = await fetch(`${RELAYER_API_URL}/deposit/spl`, {
27
27
  method: 'POST',
28
28
  headers: {
@@ -46,38 +46,46 @@ async function relayDepositToIndexer({ signedTransaction, publicKey, referrer, m
46
46
  throw error;
47
47
  }
48
48
  }
49
- export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, connection, base_units, encryptionService, transactionSigner, referrer, mintAddress }) {
50
- let mintInfo = await getMint(connection, mintAddress);
51
- let units_per_token = 10 ** mintInfo.decimals;
49
+ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, connection, base_units, amount, encryptionService, transactionSigner, referrer, mintAddress }) {
50
+ if (typeof mintAddress == 'string') {
51
+ mintAddress = new PublicKey(mintAddress);
52
+ }
53
+ let token = tokens.find(t => t.pubkey.toString() == mintAddress.toString());
54
+ if (!token) {
55
+ throw new Error('token not found: ' + mintAddress.toString());
56
+ }
57
+ if (amount) {
58
+ base_units = amount * token.units_per_token;
59
+ }
60
+ if (!base_units) {
61
+ throw new Error('You must input at least one of "base_units" or "amount"');
62
+ }
63
+ // let mintInfo = await getMint(connection, token.pubkey)
64
+ // let units_per_token = 10 ** mintInfo.decimals
52
65
  let recipient = new PublicKey('AWexibGxNFKTa1b5R5MN4PJr9HWnWRwf8EW9g8cLx3dM');
53
- let recipient_ata = getAssociatedTokenAddressSync(mintAddress, recipient, true);
54
- let feeRecipientTokenAccount = getAssociatedTokenAddressSync(mintAddress, FEE_RECIPIENT, true);
55
- let signerTokenAccount = getAssociatedTokenAddressSync(mintAddress, publicKey);
56
- // Derive tree account PDA with mint address for SPL (different from SOL version)
57
- const [treeAccount] = PublicKey.findProgramAddressSync([Buffer.from('merkle_tree'), mintAddress.toBuffer()], PROGRAM_ID);
58
- let limitAmount = await checkDepositLimit(connection, treeAccount);
59
- if (limitAmount && base_units > limitAmount * 1e6) {
60
- throw new Error(`Don't deposit more than ${limitAmount} USDC`);
66
+ let recipient_ata = getAssociatedTokenAddressSync(token.pubkey, recipient, true);
67
+ let feeRecipientTokenAccount = getAssociatedTokenAddressSync(token.pubkey, FEE_RECIPIENT, true);
68
+ let signerTokenAccount = getAssociatedTokenAddressSync(token.pubkey, publicKey);
69
+ // Derive tree account PDA with mint address for SPL
70
+ const [treeAccount] = PublicKey.findProgramAddressSync([Buffer.from('merkle_tree'), token.pubkey.toBuffer()], PROGRAM_ID);
71
+ let limitAmount = await checkDepositLimit(connection, treeAccount, token);
72
+ if (limitAmount && base_units > limitAmount * token.units_per_token) {
73
+ throw new Error(`Don't deposit more than ${limitAmount} ${token.name.toUpperCase()}`);
61
74
  }
62
- // check limit
63
- // let limitAmount = await checkDepositLimit(connection)
64
- // if (limitAmount && base_units > limitAmount * units_per_token) {
65
- // throw new Error(`Don't deposit more than ${limitAmount} SOL`)
66
- // }
67
75
  // const base_units = amount_in_sol * units_per_token
68
76
  const fee_base_units = 0;
69
77
  logger.debug('Encryption key generated from user keypair');
70
78
  logger.debug(`User wallet: ${publicKey.toString()}`);
71
- logger.debug(`Deposit amount: ${base_units} base_units (${base_units / units_per_token} USDC)`);
72
- logger.debug(`Calculated fee: ${fee_base_units} base_units (${fee_base_units / units_per_token} USDC)`);
79
+ logger.debug(`Deposit amount: ${base_units} base_units (${base_units / token.units_per_token} ${token.name.toUpperCase()})`);
80
+ logger.debug(`Calculated fee: ${fee_base_units} base_units (${fee_base_units / token.units_per_token} ${token.name.toUpperCase()})`);
73
81
  // Check SPL balance
74
82
  const accountInfo = await getAccount(connection, signerTokenAccount);
75
83
  let balance = Number(accountInfo.amount);
76
- logger.debug(`USDC wallet balance: ${balance / units_per_token} USDC`);
84
+ logger.debug(`wallet balance: ${balance / token.units_per_token} ${token.name.toUpperCase()}`);
77
85
  console.log('balance', balance);
78
86
  console.log('base_units + fee_base_units', base_units + fee_base_units);
79
87
  if (balance < (base_units + fee_base_units)) {
80
- throw new Error(`Insufficient balance. Need at least ${(base_units + fee_base_units) / units_per_token} USDC.`);
88
+ throw new Error(`Insufficient balance. Need at least ${(base_units + fee_base_units) / token.units_per_token} ${token.name.toUpperCase()}.`);
81
89
  }
82
90
  // Check SOL balance
83
91
  const solBalance = await connection.getBalance(publicKey);
@@ -89,7 +97,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
89
97
  // Create the merkle tree with the pre-initialized poseidon hash
90
98
  const tree = new MerkleTree(MERKLE_TREE_DEPTH, lightWasm);
91
99
  // Initialize root and nextIndex variables
92
- const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState('usdc');
100
+ const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState(token.name);
93
101
  logger.debug(`Using tree root: ${root}`);
94
102
  logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
95
103
  // Generate a deterministic private key derived from the wallet keypair
@@ -121,12 +129,12 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
121
129
  new Utxo({
122
130
  lightWasm,
123
131
  keypair: utxoKeypair,
124
- mintAddress: mintAddress.toString()
132
+ mintAddress: token.pubkey.toString()
125
133
  }),
126
134
  new Utxo({
127
135
  lightWasm,
128
136
  keypair: utxoKeypair,
129
- mintAddress: mintAddress.toString()
137
+ mintAddress: token.pubkey.toString()
130
138
  })
131
139
  ];
132
140
  // Both inputs are dummy, so use mock indices and zero-filled Merkle paths
@@ -159,7 +167,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
159
167
  lightWasm,
160
168
  keypair: utxoKeypair,
161
169
  amount: '0', // This UTXO will be inserted at currentNextIndex
162
- mintAddress: mintAddress.toString()
170
+ mintAddress: token.pubkey.toString()
163
171
  });
164
172
  inputs = [
165
173
  firstUtxo, // Use the first existing UTXO
@@ -167,12 +175,12 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
167
175
  ];
168
176
  // Fetch Merkle proofs for real UTXOs
169
177
  const firstUtxoCommitment = await firstUtxo.getCommitment();
170
- const firstUtxoMerkleProof = await fetchMerkleProof(firstUtxoCommitment, 'usdc');
178
+ const firstUtxoMerkleProof = await fetchMerkleProof(firstUtxoCommitment, token.name);
171
179
  let secondUtxoMerkleProof;
172
180
  if (secondUtxo.amount.gt(new BN(0))) {
173
181
  // Second UTXO is real, fetch its proof
174
182
  const secondUtxoCommitment = await secondUtxo.getCommitment();
175
- secondUtxoMerkleProof = await fetchMerkleProof(secondUtxoCommitment, 'usdc');
183
+ secondUtxoMerkleProof = await fetchMerkleProof(secondUtxoCommitment, token.name);
176
184
  logger.debug('\nSecond UTXO to be consolidated:');
177
185
  await secondUtxo.log();
178
186
  }
@@ -202,14 +210,14 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
202
210
  amount: outputAmount,
203
211
  keypair: utxoKeypair,
204
212
  index: currentNextIndex, // This UTXO will be inserted at currentNextIndex
205
- mintAddress: mintAddress.toString()
213
+ mintAddress: token.pubkey.toString()
206
214
  }), // Output with value (either deposit amount minus fee, or input amount minus fee)
207
215
  new Utxo({
208
216
  lightWasm,
209
217
  amount: '0',
210
218
  keypair: utxoKeypair,
211
219
  index: currentNextIndex + 1, // This UTXO will be inserted at currentNextIndex
212
- mintAddress: mintAddress.toString()
220
+ mintAddress: token.pubkey.toString()
213
221
  }) // Empty UTXO
214
222
  ];
215
223
  // Verify this matches the circuit balance equation: sumIns + publicAmount = sumOuts
@@ -256,7 +264,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
256
264
  encryptedOutput2: encryptedOutput2,
257
265
  fee: new BN(fee_base_units),
258
266
  feeRecipient: feeRecipientTokenAccount,
259
- mintAddress: mintAddress.toString()
267
+ mintAddress: token.pubkey.toString()
260
268
  };
261
269
  // Calculate the extDataHash with the encrypted outputs (now includes mintAddress for security)
262
270
  const calculatedExtDataHash = getExtDataHash(extData);
@@ -264,7 +272,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
264
272
  const input = {
265
273
  // Common transaction data
266
274
  root: root,
267
- mintAddress: getMintAddressField(mintAddress), // new mint address
275
+ mintAddress: getMintAddressField(token.pubkey), // new mint address
268
276
  publicAmount: publicAmountForCircuit.toString(), // Use proper field arithmetic result
269
277
  extDataHash: calculatedExtDataHash,
270
278
  // Input UTXO data (UTXOs being spent) - ensure all values are in decimal format
@@ -307,7 +315,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
307
315
  const { nullifier0PDA, nullifier1PDA } = findNullifierPDAs(proofToSubmit);
308
316
  const { nullifier2PDA, nullifier3PDA } = findCrossCheckNullifierPDAs(proofToSubmit);
309
317
  const [globalConfigPda, globalConfigPdaBump] = await PublicKey.findProgramAddressSync([Buffer.from("global_config")], PROGRAM_ID);
310
- const treeAta = getAssociatedTokenAddressSync(mintAddress, globalConfigPda, true);
318
+ const treeAta = getAssociatedTokenAddressSync(token.pubkey, globalConfigPda, true);
311
319
  const lookupTableAccount = await useExistingALT(connection, ALT_ADDRESS);
312
320
  if (!lookupTableAccount?.value) {
313
321
  throw new Error(`ALT not found at address ${ALT_ADDRESS.toString()} `);
@@ -327,7 +335,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
327
335
  // signer
328
336
  { pubkey: publicKey, isSigner: true, isWritable: true },
329
337
  // SPL token mint
330
- { pubkey: mintAddress, isSigner: false, isWritable: false },
338
+ { pubkey: token.pubkey, isSigner: false, isWritable: false },
331
339
  // signer's token account
332
340
  { pubkey: signerTokenAccount, isSigner: false, isWritable: true },
333
341
  // recipient (placeholder)
@@ -369,7 +377,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
369
377
  // Relay the pre-signed transaction to indexer backend
370
378
  logger.info('submitting transaction to relayer...');
371
379
  const signature = await relayDepositToIndexer({
372
- mintAddress: mintAddress.toString(),
380
+ mintAddress: token.pubkey.toString(),
373
381
  publicKey,
374
382
  signedTransaction: serializedTransaction,
375
383
  referrer
@@ -385,7 +393,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
385
393
  logger.debug(`retryTimes: ${retryTimes}`);
386
394
  await new Promise(resolve => setTimeout(resolve, itv * 1000));
387
395
  logger.debug('Fetching updated tree state...');
388
- let url = RELAYER_API_URL + '/utxos/check/' + encryptedOutputStr + '?token=usdc';
396
+ let url = RELAYER_API_URL + '/utxos/check/' + encryptedOutputStr + '?token=' + token.name;
389
397
  let res = await fetch(url);
390
398
  let resJson = await res.json();
391
399
  if (resJson.exists) {
@@ -398,7 +406,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
398
406
  retryTimes++;
399
407
  }
400
408
  }
401
- async function checkDepositLimit(connection, treeAccount) {
409
+ async function checkDepositLimit(connection, treeAccount, token) {
402
410
  try {
403
411
  // Fetch the account data
404
412
  const accountInfo = await connection.getAccountInfo(treeAccount);
@@ -411,21 +419,21 @@ async function checkDepositLimit(connection, treeAccount) {
411
419
  const rootIndex = new BN(accountInfo.data.slice(4112, 4120), 'le');
412
420
  const maxDepositAmount = new BN(accountInfo.data.slice(4120, 4128), 'le');
413
421
  const bump = accountInfo.data[4128];
414
- // Convert to SOL using BN division to handle large numbers
415
- const lamportsPerSol = new BN(1e6);
416
- const maxDepositSol = maxDepositAmount.div(lamportsPerSol);
417
- const remainder = maxDepositAmount.mod(lamportsPerSol);
418
- // Format the SOL amount with decimals
419
- let solFormatted = '1';
422
+ // Convert to SPL using BN division to handle large numbers
423
+ const unitesPerToken = new BN(token.units_per_token);
424
+ const maxDepositSpl = maxDepositAmount.div(unitesPerToken);
425
+ const remainder = maxDepositAmount.mod(unitesPerToken);
426
+ // Format the SPL amount with decimals
427
+ let amountFormatted = '1';
420
428
  if (remainder.eq(new BN(0))) {
421
- solFormatted = maxDepositSol.toString();
429
+ amountFormatted = maxDepositSpl.toString();
422
430
  }
423
431
  else {
424
- // Handle fractional SOL by converting remainder to decimal
425
- const fractional = remainder.toNumber() / 1e6;
426
- solFormatted = `${maxDepositSol.toString()}${fractional.toFixed(9).substring(1)}`;
432
+ // Handle fractional SPL by converting remainder to decimal
433
+ const fractional = remainder.toNumber() / token.units_per_token;
434
+ amountFormatted = `${maxDepositSpl.toString()}${fractional.toFixed(Math.log10(token.units_per_token)).substring(1)}`;
427
435
  }
428
- return Number(solFormatted);
436
+ return Number(amountFormatted);
429
437
  }
430
438
  catch (error) {
431
439
  console.log('❌ Error reading deposit limit:', error);
@@ -7,3 +7,4 @@ export { getBalanceFromUtxos, getUtxos, localstorageKey } from './getUtxos.js';
7
7
  export { depositSPL } from './depositSPL.js';
8
8
  export { withdrawSPL } from './withdrawSPL.js';
9
9
  export { getBalanceFromUtxosSPL, getUtxosSPL } from './getUtxosSPL.js';
10
+ export { type TokenList, type SplList, tokens } from './utils/constants.js';
@@ -7,3 +7,4 @@ export { getBalanceFromUtxos, getUtxos, localstorageKey } from './getUtxos.js';
7
7
  export { depositSPL } from './depositSPL.js';
8
8
  export { withdrawSPL } from './withdrawSPL.js';
9
9
  export { getBalanceFromUtxosSPL, getUtxosSPL } from './getUtxosSPL.js';
10
+ export { tokens } from './utils/constants.js';
@@ -9,12 +9,12 @@ export declare function localstorageKey(key: PublicKey): string;
9
9
  * @param setStatus A global state updator. Set live status message showing on webpage
10
10
  * @returns Array of decrypted UTXOs that belong to the user
11
11
  */
12
- export declare function getUtxosSPL({ publicKey, connection, encryptionService, storage, mintAddress, abortSignal, offset }: {
12
+ export declare function getUtxosSPL({ publicKey, connection, encryptionService, storage, abortSignal, offset, mintAddress }: {
13
13
  publicKey: PublicKey;
14
14
  connection: Connection;
15
15
  encryptionService: EncryptionService;
16
16
  storage: Storage;
17
- mintAddress: PublicKey;
17
+ mintAddress: PublicKey | string;
18
18
  abortSignal?: AbortSignal;
19
19
  offset?: number;
20
20
  }): Promise<Utxo[]>;
@@ -27,6 +27,7 @@ export declare function getUtxosSPL({ publicKey, connection, encryptionService,
27
27
  export declare function isUtxoSpent(connection: Connection, utxo: Utxo): Promise<boolean>;
28
28
  export declare function getBalanceFromUtxosSPL(utxos: Utxo[]): {
29
29
  base_units: number;
30
+ amount: number;
30
31
  /** @deprecated use base_units instead */
31
32
  lamports: number;
32
33
  };
@@ -4,7 +4,7 @@ import { Keypair as UtxoKeypair } from './models/keypair.js';
4
4
  import { WasmFactory } from '@lightprotocol/hasher.rs';
5
5
  //@ts-ignore
6
6
  import * as ffjavascript from 'ffjavascript';
7
- import { FETCH_UTXOS_GROUP_SIZE, RELAYER_API_URL, LSK_ENCRYPTED_OUTPUTS, LSK_FETCH_OFFSET, PROGRAM_ID } from './utils/constants.js';
7
+ import { FETCH_UTXOS_GROUP_SIZE, RELAYER_API_URL, LSK_ENCRYPTED_OUTPUTS, LSK_FETCH_OFFSET, PROGRAM_ID, tokens } from './utils/constants.js';
8
8
  import { logger } from './utils/logger.js';
9
9
  import { getAssociatedTokenAddress } from '@solana/spl-token';
10
10
  // Use type assertion for the utility functions (same pattern as in get_verification_keys.ts)
@@ -28,13 +28,21 @@ let decryptionTaskFinished = 0;
28
28
  * @param setStatus A global state updator. Set live status message showing on webpage
29
29
  * @returns Array of decrypted UTXOs that belong to the user
30
30
  */
31
- export async function getUtxosSPL({ publicKey, connection, encryptionService, storage, mintAddress, abortSignal, offset }) {
31
+ export async function getUtxosSPL({ publicKey, connection, encryptionService, storage, abortSignal, offset, mintAddress }) {
32
32
  let valid_utxos = [];
33
33
  let valid_strings = [];
34
34
  let history_indexes = [];
35
35
  let publicKey_ata;
36
+ if (typeof mintAddress == 'string') {
37
+ mintAddress = new PublicKey(mintAddress);
38
+ }
39
+ let token = tokens.find(t => t.pubkey.toString() == mintAddress.toString());
40
+ if (!token) {
41
+ throw new Error('token not found: ' + mintAddress.toString());
42
+ }
43
+ logger.debug('token name: ' + token.name + ', token address' + token.pubkey.toString());
36
44
  try {
37
- publicKey_ata = await getAssociatedTokenAddress(mintAddress, publicKey);
45
+ publicKey_ata = await getAssociatedTokenAddress(token.pubkey, publicKey);
38
46
  let offsetStr = storage.getItem(LSK_FETCH_OFFSET + localstorageKey(publicKey_ata));
39
47
  if (offsetStr) {
40
48
  roundStartIndex = Number(offsetStr);
@@ -56,9 +64,10 @@ export async function getUtxosSPL({ publicKey, connection, encryptionService, st
56
64
  if (offset) {
57
65
  fetch_utxo_offset = Math.max(offset, fetch_utxo_offset);
58
66
  }
67
+ console.log(' ####fetch_utxo_offset', fetch_utxo_offset);
59
68
  let fetch_utxo_end = fetch_utxo_offset + FETCH_UTXOS_GROUP_SIZE;
60
- let fetch_utxo_url = `${RELAYER_API_URL}/utxos/range?token=usdc&start=${fetch_utxo_offset}&end=${fetch_utxo_end}`;
61
- let fetched = await fetchUserUtxos({ publicKey, connection, url: fetch_utxo_url, encryptionService, storage, publicKey_ata, initOffset: offset });
69
+ let fetch_utxo_url = `${RELAYER_API_URL}/utxos/range?token=${token.name}&start=${fetch_utxo_offset}&end=${fetch_utxo_end}`;
70
+ let fetched = await fetchUserUtxos({ url: fetch_utxo_url, encryptionService, storage, publicKey_ata, tokenName: token.name });
62
71
  let am = 0;
63
72
  const nonZeroUtxos = [];
64
73
  const nonZeroEncrypted = [];
@@ -114,9 +123,14 @@ export async function getUtxosSPL({ publicKey, connection, encryptionService, st
114
123
  logger.debug(`valid_strings len after set: ${valid_strings.length}`);
115
124
  storage.setItem(LSK_ENCRYPTED_OUTPUTS + localstorageKey(publicKey_ata), JSON.stringify(valid_strings));
116
125
  // reorgnize
117
- return valid_utxos.filter(u => u.mintAddress == mintAddress.toString());
126
+ if (valid_utxos.length) {
127
+ console.log('filter mint', valid_utxos[0].mintAddress, token.pubkey.toString());
128
+ }
129
+ let filtered_utxos = valid_utxos.filter(u => u.mintAddress == token.pubkey.toString());
130
+ console.log('filtered_utxos.len', filtered_utxos.length);
131
+ return filtered_utxos;
118
132
  }
119
- async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionService, publicKey_ata, initOffset }) {
133
+ async function fetchUserUtxos({ url, storage, encryptionService, publicKey_ata, tokenName }) {
120
134
  const lightWasm = await WasmFactory.getInstance();
121
135
  // Derive the UTXO keypair from the wallet keypair
122
136
  const utxoPrivateKey = encryptionService.deriveUtxoPrivateKey();
@@ -159,7 +173,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
159
173
  cachedStringNum = JSON.parse(cachedString).length;
160
174
  }
161
175
  let decryptionTaskTotal = data.total + cachedStringNum - roundStartIndex;
162
- let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm);
176
+ let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName);
163
177
  decryptionTaskFinished += encryptedOutputs.length;
164
178
  logger.debug('batchReslen', batchRes.length);
165
179
  for (let i = 0; i < batchRes.length; i++) {
@@ -177,7 +191,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
177
191
  if (decryptionTaskFinished % 100 == 0) {
178
192
  logger.info(`(decrypting cached utxo: ${decryptionTaskFinished + 1}/${decryptionTaskTotal}...)`);
179
193
  }
180
- let batchRes = await decrypt_outputs(cachedEncryptedOutputs, encryptionService, utxoKeypair, lightWasm);
194
+ let batchRes = await decrypt_outputs(cachedEncryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName);
181
195
  decryptionTaskFinished += cachedEncryptedOutputs.length;
182
196
  logger.debug('cachedbatchReslen', batchRes.length, ' source', cachedEncryptedOutputs.length);
183
197
  for (let i = 0; i < batchRes.length; i++) {
@@ -257,8 +271,19 @@ async function areUtxosSpent(connection, utxos) {
257
271
  }
258
272
  // Calculate total balance
259
273
  export function getBalanceFromUtxosSPL(utxos) {
274
+ if (!utxos.length) {
275
+ return { base_units: 0, amount: 0, lamports: 0 };
276
+ }
277
+ let token = tokens.find(t => t.pubkey.toString() == utxos[0].mintAddress.toString());
278
+ if (!token) {
279
+ throw new Error('token not found for ' + utxos[0].mintAddress.toString());
280
+ }
260
281
  const totalBalance = utxos.reduce((sum, utxo) => sum.add(utxo.amount), new BN(0));
261
- return { base_units: totalBalance.toNumber(), lamports: totalBalance.toNumber() };
282
+ return {
283
+ base_units: totalBalance.toNumber(),
284
+ lamports: totalBalance.toNumber(),
285
+ amount: totalBalance.toNumber() / token.units_per_token
286
+ };
262
287
  }
263
288
  async function decrypt_output(encryptedOutput, encryptionService, utxoKeypair, lightWasm, connection) {
264
289
  let res = { status: 'unDecrypted' };
@@ -332,7 +357,7 @@ async function decrypt_output(encryptedOutput, encryptionService, utxoKeypair, l
332
357
  }
333
358
  return res;
334
359
  }
335
- async function decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm) {
360
+ async function decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName) {
336
361
  let results = [];
337
362
  // decript all UTXO
338
363
  for (const encryptedOutput of encryptedOutputs) {
@@ -358,7 +383,7 @@ async function decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair,
358
383
  let url = RELAYER_API_URL + `/utxos/indices`;
359
384
  let res = await fetch(url, {
360
385
  method: 'POST', headers: { "Content-Type": "application/json" },
361
- body: JSON.stringify({ encrypted_outputs, token: 'usdc' })
386
+ body: JSON.stringify({ encrypted_outputs, token: tokenName })
362
387
  });
363
388
  let j = await res.json();
364
389
  if (!j.indices || !Array.isArray(j.indices) || j.indices.length != encrypted_outputs.length) {
package/dist/index.d.ts CHANGED
@@ -77,10 +77,19 @@ export declare class PrivacyCash {
77
77
  lamports: number;
78
78
  }>;
79
79
  /**
80
- * Returns the amount of lamports current wallet has in Privacy Cash.
80
+ * Returns the amount of base unites current wallet has in Privacy Cash.
81
81
  */
82
82
  getPrivateBalanceUSDC(): Promise<{
83
83
  base_units: number;
84
+ amount: number;
85
+ lamports: number;
86
+ }>;
87
+ /**
88
+ * Returns the amount of base unites current wallet has in Privacy Cash.
89
+ */
90
+ getPrivateBalanceSpl(mintAddress: PublicKey | string): Promise<{
91
+ base_units: number;
92
+ amount: number;
84
93
  lamports: number;
85
94
  }>;
86
95
  /**
@@ -88,4 +97,29 @@ export declare class PrivacyCash {
88
97
  */
89
98
  isBrowser(): boolean;
90
99
  startStatusRender(): Promise<void>;
100
+ /**
101
+ * Deposit SPL to the Privacy Cash.
102
+ */
103
+ depositSPL({ base_units, mintAddress, amount }: {
104
+ base_units?: number;
105
+ amount?: number;
106
+ mintAddress: PublicKey | string;
107
+ }): Promise<{
108
+ tx: string;
109
+ }>;
110
+ /**
111
+ * Withdraw SPL from the Privacy Cash.
112
+ */
113
+ withdrawSPL({ base_units, mintAddress, recipientAddress, amount }: {
114
+ base_units?: number;
115
+ amount?: number;
116
+ mintAddress: PublicKey | string;
117
+ recipientAddress?: string;
118
+ }): Promise<{
119
+ isPartial: boolean;
120
+ tx: string;
121
+ recipient: string;
122
+ base_units: number;
123
+ fee_base_units: number;
124
+ }>;
91
125
  }
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.j
2
2
  import { deposit } from './deposit.js';
3
3
  import { getBalanceFromUtxos, getUtxos, localstorageKey } from './getUtxos.js';
4
4
  import { getBalanceFromUtxosSPL, getUtxosSPL } from './getUtxosSPL.js';
5
- import { LSK_ENCRYPTED_OUTPUTS, LSK_FETCH_OFFSET, USDC_MINT } from './utils/constants.js';
5
+ import { LSK_ENCRYPTED_OUTPUTS, LSK_FETCH_OFFSET, tokens, USDC_MINT } from './utils/constants.js';
6
6
  import { logger, setLogger } from './utils/logger.js';
7
7
  import { EncryptionService } from './utils/encryption.js';
8
8
  import { WasmFactory } from '@lightprotocol/hasher.rs';
@@ -62,9 +62,8 @@ export class PrivacyCash {
62
62
  storage.removeItem(LSK_FETCH_OFFSET + localstorageKey(this.publicKey));
63
63
  storage.removeItem(LSK_ENCRYPTED_OUTPUTS + localstorageKey(this.publicKey));
64
64
  // spl
65
- let mintAddresses = [USDC_MINT];
66
- for (let mintAddress of mintAddresses) {
67
- let ata = await getAssociatedTokenAddress(mintAddress, this.publicKey);
65
+ for (let token of tokens) {
66
+ let ata = await getAssociatedTokenAddress(token.pubkey, this.publicKey);
68
67
  storage.removeItem(LSK_FETCH_OFFSET + localstorageKey(ata));
69
68
  storage.removeItem(LSK_ENCRYPTED_OUTPUTS + localstorageKey(ata));
70
69
  }
@@ -179,7 +178,7 @@ export class PrivacyCash {
179
178
  return getBalanceFromUtxos(utxos);
180
179
  }
181
180
  /**
182
- * Returns the amount of lamports current wallet has in Privacy Cash.
181
+ * Returns the amount of base unites current wallet has in Privacy Cash.
183
182
  */
184
183
  async getPrivateBalanceUSDC() {
185
184
  logger.info('getting private balance');
@@ -188,6 +187,21 @@ export class PrivacyCash {
188
187
  this.isRuning = false;
189
188
  return getBalanceFromUtxosSPL(utxos);
190
189
  }
190
+ /**
191
+ * Returns the amount of base unites current wallet has in Privacy Cash.
192
+ */
193
+ async getPrivateBalanceSpl(mintAddress) {
194
+ this.isRuning = true;
195
+ let utxos = await getUtxosSPL({
196
+ publicKey: this.publicKey,
197
+ connection: this.connection,
198
+ encryptionService: this.encryptionService,
199
+ storage,
200
+ mintAddress
201
+ });
202
+ this.isRuning = false;
203
+ return getBalanceFromUtxosSPL(utxos);
204
+ }
191
205
  /**
192
206
  * Returns true if the code is running in a browser.
193
207
  */
@@ -206,6 +220,55 @@ export class PrivacyCash {
206
220
  await new Promise(r => setTimeout(r, 250));
207
221
  }
208
222
  }
223
+ /**
224
+ * Deposit SPL to the Privacy Cash.
225
+ */
226
+ async depositSPL({ base_units, mintAddress, amount }) {
227
+ this.isRuning = true;
228
+ logger.info('start depositting');
229
+ let lightWasm = await WasmFactory.getInstance();
230
+ let res = await depositSPL({
231
+ lightWasm,
232
+ base_units,
233
+ amount,
234
+ connection: this.connection,
235
+ encryptionService: this.encryptionService,
236
+ publicKey: this.publicKey,
237
+ transactionSigner: async (tx) => {
238
+ tx.sign([this.keypair]);
239
+ return tx;
240
+ },
241
+ keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
242
+ storage,
243
+ mintAddress
244
+ });
245
+ this.isRuning = false;
246
+ return res;
247
+ }
248
+ /**
249
+ * Withdraw SPL from the Privacy Cash.
250
+ */
251
+ async withdrawSPL({ base_units, mintAddress, recipientAddress, amount }) {
252
+ this.isRuning = true;
253
+ logger.info('start withdrawing');
254
+ let lightWasm = await WasmFactory.getInstance();
255
+ let recipient = recipientAddress ? new PublicKey(recipientAddress) : this.publicKey;
256
+ let res = await withdrawSPL({
257
+ lightWasm,
258
+ base_units,
259
+ amount,
260
+ connection: this.connection,
261
+ encryptionService: this.encryptionService,
262
+ publicKey: this.publicKey,
263
+ recipient,
264
+ keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
265
+ storage,
266
+ mintAddress
267
+ });
268
+ console.log(`Withdraw successful. Recipient ${recipient} received ${base_units} USDC units`);
269
+ this.isRuning = false;
270
+ return res;
271
+ }
209
272
  }
210
273
  function getSolanaKeypair(secret) {
211
274
  try {
@@ -13,3 +13,15 @@ export declare const SIGN_MESSAGE = "Privacy Money account sign in";
13
13
  export declare const LSK_FETCH_OFFSET = "fetch_offset";
14
14
  export declare const LSK_ENCRYPTED_OUTPUTS = "encrypted_outputs";
15
15
  export declare const USDC_MINT: PublicKey;
16
+ declare const tokenList: readonly ["sol", "usdc", "usdt", "zec", "ore"];
17
+ export type TokenList = typeof tokenList[number];
18
+ declare const splList: readonly ["usdc", "usdt", "ore"];
19
+ export type SplList = typeof splList[number];
20
+ export type Token = {
21
+ name: 'sol' | 'usdc' | 'usdt' | 'zec' | 'ore';
22
+ prefix: string;
23
+ units_per_token: number;
24
+ pubkey: PublicKey;
25
+ };
26
+ export declare const tokens: Token[];
27
+ export {};