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/README.md CHANGED
@@ -9,11 +9,11 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
9
9
  ### Usage
10
10
  This SDK provides APIs for developers to interact with Privacy Cash relayers easily. Developers can easily deposit/withdraw/query balances in Privacy Cash solana program.
11
11
 
12
- Main APIs for this SDK are:
13
- a. for SOL:
14
- deposit(), withdraw(), getPrivateBalance()
15
- b. for USDC:
16
- depositUSDC(), withdrawUSDC(), getPrivateBalanceUSDC()
12
+ Main APIs for this SDK are:
13
+ a. for SOL:
14
+ deposit(), withdraw(), getPrivateBalance()
15
+ b. for SPL (currently supporting USDC and USDT):
16
+ depositSPL(), withdrawSPL(), getPrivateBalanceSpl()
17
17
 
18
18
  Check the example project under /example folder. The code should be fairly self-explanatory.
19
19
 
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,10 +360,10 @@ 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
- console.log(`Account data size: ${accountInfo.data.length} bytes`);
366
+ logger.debug(`Account data size: ${accountInfo.data.length} bytes`);
367
367
  const authority = new PublicKey(accountInfo.data.slice(8, 40));
368
368
  const nextIndex = new BN(accountInfo.data.slice(40, 48), 'le');
369
369
  const rootIndex = new BN(accountInfo.data.slice(4112, 4120), 'le');
@@ -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: {
@@ -31,53 +31,68 @@ async function relayDepositToIndexer({ signedTransaction, publicKey, referrer, m
31
31
  body: JSON.stringify(params)
32
32
  });
33
33
  if (!response.ok) {
34
- console.log('res text:', await response.json());
34
+ logger.debug('res text:', await response.text());
35
35
  throw new Error('response not ok');
36
36
  // const errorData = await response.json() as { error?: string };
37
37
  // throw new Error(`Deposit relay failed: ${response.status} ${response.statusText} - ${errorData.error || 'Unknown error'}`);
38
38
  }
39
- const result = await response.json();
40
- logger.debug('Pre-signed deposit transaction relayed successfully!');
41
- logger.debug('Response:', result);
39
+ let result;
40
+ try {
41
+ result = await response.json();
42
+ logger.debug('Pre-signed deposit transaction relayed successfully!');
43
+ logger.debug('Response:', result);
44
+ }
45
+ catch (e) {
46
+ console.log('response.text', await response.text());
47
+ throw new Error('failed to parse json');
48
+ }
42
49
  return result.signature;
43
50
  }
44
51
  catch (error) {
45
- console.error('Failed to relay deposit transaction to indexer:', error);
52
+ console.error('Failed to relay deposit transaction to indexer:', error.message);
46
53
  throw error;
47
54
  }
48
55
  }
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;
56
+ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, connection, base_units, amount, encryptionService, transactionSigner, referrer, mintAddress }) {
57
+ if (typeof mintAddress == 'string') {
58
+ mintAddress = new PublicKey(mintAddress);
59
+ }
60
+ let token = tokens.find(t => t.pubkey.toString() == mintAddress.toString());
61
+ if (!token) {
62
+ throw new Error('token not found: ' + mintAddress.toString());
63
+ }
64
+ if (amount) {
65
+ base_units = amount * token.units_per_token;
66
+ }
67
+ if (!base_units) {
68
+ throw new Error('You must input at least one of "base_units" or "amount"');
69
+ }
70
+ // let mintInfo = await getMint(connection, token.pubkey)
71
+ // let units_per_token = 10 ** mintInfo.decimals
52
72
  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`);
73
+ let recipient_ata = getAssociatedTokenAddressSync(token.pubkey, recipient, true);
74
+ let feeRecipientTokenAccount = getAssociatedTokenAddressSync(token.pubkey, FEE_RECIPIENT, true);
75
+ let signerTokenAccount = getAssociatedTokenAddressSync(token.pubkey, publicKey);
76
+ // Derive tree account PDA with mint address for SPL
77
+ const [treeAccount] = PublicKey.findProgramAddressSync([Buffer.from('merkle_tree'), token.pubkey.toBuffer()], PROGRAM_ID);
78
+ let limitAmount = await checkDepositLimit(connection, treeAccount, token);
79
+ if (limitAmount && base_units > limitAmount * token.units_per_token) {
80
+ throw new Error(`Don't deposit more than ${limitAmount} ${token.name.toUpperCase()}`);
61
81
  }
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
82
  // const base_units = amount_in_sol * units_per_token
68
83
  const fee_base_units = 0;
69
84
  logger.debug('Encryption key generated from user keypair');
70
85
  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)`);
86
+ logger.debug(`Deposit amount: ${base_units} base_units (${base_units / token.units_per_token} ${token.name.toUpperCase()})`);
87
+ logger.debug(`Calculated fee: ${fee_base_units} base_units (${fee_base_units / token.units_per_token} ${token.name.toUpperCase()})`);
73
88
  // Check SPL balance
74
89
  const accountInfo = await getAccount(connection, signerTokenAccount);
75
90
  let balance = Number(accountInfo.amount);
76
- logger.debug(`USDC wallet balance: ${balance / units_per_token} USDC`);
77
- console.log('balance', balance);
78
- console.log('base_units + fee_base_units', base_units + fee_base_units);
91
+ logger.debug(`wallet balance: ${balance / token.units_per_token} ${token.name.toUpperCase()}`);
92
+ logger.debug('balance', balance);
93
+ logger.debug('base_units + fee_base_units', base_units + fee_base_units);
79
94
  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.`);
95
+ throw new Error(`Insufficient balance. Need at least ${(base_units + fee_base_units) / token.units_per_token} ${token.name.toUpperCase()}.`);
81
96
  }
82
97
  // Check SOL balance
83
98
  const solBalance = await connection.getBalance(publicKey);
@@ -89,7 +104,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
89
104
  // Create the merkle tree with the pre-initialized poseidon hash
90
105
  const tree = new MerkleTree(MERKLE_TREE_DEPTH, lightWasm);
91
106
  // Initialize root and nextIndex variables
92
- const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState('usdc');
107
+ const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState(token.name);
93
108
  logger.debug(`Using tree root: ${root}`);
94
109
  logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
95
110
  // Generate a deterministic private key derived from the wallet keypair
@@ -121,12 +136,12 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
121
136
  new Utxo({
122
137
  lightWasm,
123
138
  keypair: utxoKeypair,
124
- mintAddress: mintAddress.toString()
139
+ mintAddress: token.pubkey.toString()
125
140
  }),
126
141
  new Utxo({
127
142
  lightWasm,
128
143
  keypair: utxoKeypair,
129
- mintAddress: mintAddress.toString()
144
+ mintAddress: token.pubkey.toString()
130
145
  })
131
146
  ];
132
147
  // Both inputs are dummy, so use mock indices and zero-filled Merkle paths
@@ -153,13 +168,12 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
153
168
  logger.debug(`Output amount (existing UTXOs + deposit - fee): ${outputAmount}`);
154
169
  logger.debug(`External amount (deposit): ${extAmount}`);
155
170
  logger.debug('\nFirst UTXO to be consolidated:');
156
- await firstUtxo.log();
157
171
  // Use first existing UTXO as first input, and either second UTXO or dummy UTXO as second input
158
172
  const secondUtxo = mintUtxos.length > 1 ? mintUtxos[1] : new Utxo({
159
173
  lightWasm,
160
174
  keypair: utxoKeypair,
161
175
  amount: '0', // This UTXO will be inserted at currentNextIndex
162
- mintAddress: mintAddress.toString()
176
+ mintAddress: token.pubkey.toString()
163
177
  });
164
178
  inputs = [
165
179
  firstUtxo, // Use the first existing UTXO
@@ -167,12 +181,12 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
167
181
  ];
168
182
  // Fetch Merkle proofs for real UTXOs
169
183
  const firstUtxoCommitment = await firstUtxo.getCommitment();
170
- const firstUtxoMerkleProof = await fetchMerkleProof(firstUtxoCommitment, 'usdc');
184
+ const firstUtxoMerkleProof = await fetchMerkleProof(firstUtxoCommitment, token.name);
171
185
  let secondUtxoMerkleProof;
172
186
  if (secondUtxo.amount.gt(new BN(0))) {
173
187
  // Second UTXO is real, fetch its proof
174
188
  const secondUtxoCommitment = await secondUtxo.getCommitment();
175
- secondUtxoMerkleProof = await fetchMerkleProof(secondUtxoCommitment, 'usdc');
189
+ secondUtxoMerkleProof = await fetchMerkleProof(secondUtxoCommitment, token.name);
176
190
  logger.debug('\nSecond UTXO to be consolidated:');
177
191
  await secondUtxo.log();
178
192
  }
@@ -202,14 +216,14 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
202
216
  amount: outputAmount,
203
217
  keypair: utxoKeypair,
204
218
  index: currentNextIndex, // This UTXO will be inserted at currentNextIndex
205
- mintAddress: mintAddress.toString()
219
+ mintAddress: token.pubkey.toString()
206
220
  }), // Output with value (either deposit amount minus fee, or input amount minus fee)
207
221
  new Utxo({
208
222
  lightWasm,
209
223
  amount: '0',
210
224
  keypair: utxoKeypair,
211
225
  index: currentNextIndex + 1, // This UTXO will be inserted at currentNextIndex
212
- mintAddress: mintAddress.toString()
226
+ mintAddress: token.pubkey.toString()
213
227
  }) // Empty UTXO
214
228
  ];
215
229
  // Verify this matches the circuit balance equation: sumIns + publicAmount = sumOuts
@@ -232,10 +246,10 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
232
246
  logger.debug('\nEncrypting UTXOs with keypair data...');
233
247
  const encryptedOutput1 = encryptionService.encryptUtxo(outputs[0]);
234
248
  const encryptedOutput2 = encryptionService.encryptUtxo(outputs[1]);
235
- logger.debug(`\nOutput[0] (with value):`);
236
- await outputs[0].log();
237
- logger.debug(`\nOutput[1] (empty):`);
238
- await outputs[1].log();
249
+ // logger.debug(`\nOutput[0] (with value):`);
250
+ // await outputs[0].log();
251
+ // logger.debug(`\nOutput[1] (empty):`);
252
+ // await outputs[1].log();
239
253
  logger.debug(`\nEncrypted output 1 size: ${encryptedOutput1.length} bytes`);
240
254
  logger.debug(`Encrypted output 2 size: ${encryptedOutput2.length} bytes`);
241
255
  logger.debug(`Total encrypted outputs size: ${encryptedOutput1.length + encryptedOutput2.length} bytes`);
@@ -256,7 +270,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
256
270
  encryptedOutput2: encryptedOutput2,
257
271
  fee: new BN(fee_base_units),
258
272
  feeRecipient: feeRecipientTokenAccount,
259
- mintAddress: mintAddress.toString()
273
+ mintAddress: token.pubkey.toString()
260
274
  };
261
275
  // Calculate the extDataHash with the encrypted outputs (now includes mintAddress for security)
262
276
  const calculatedExtDataHash = getExtDataHash(extData);
@@ -264,7 +278,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
264
278
  const input = {
265
279
  // Common transaction data
266
280
  root: root,
267
- mintAddress: getMintAddressField(mintAddress), // new mint address
281
+ mintAddress: getMintAddressField(token.pubkey), // new mint address
268
282
  publicAmount: publicAmountForCircuit.toString(), // Use proper field arithmetic result
269
283
  extDataHash: calculatedExtDataHash,
270
284
  // Input UTXO data (UTXOs being spent) - ensure all values are in decimal format
@@ -307,7 +321,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
307
321
  const { nullifier0PDA, nullifier1PDA } = findNullifierPDAs(proofToSubmit);
308
322
  const { nullifier2PDA, nullifier3PDA } = findCrossCheckNullifierPDAs(proofToSubmit);
309
323
  const [globalConfigPda, globalConfigPdaBump] = await PublicKey.findProgramAddressSync([Buffer.from("global_config")], PROGRAM_ID);
310
- const treeAta = getAssociatedTokenAddressSync(mintAddress, globalConfigPda, true);
324
+ const treeAta = getAssociatedTokenAddressSync(token.pubkey, globalConfigPda, true);
311
325
  const lookupTableAccount = await useExistingALT(connection, ALT_ADDRESS);
312
326
  if (!lookupTableAccount?.value) {
313
327
  throw new Error(`ALT not found at address ${ALT_ADDRESS.toString()} `);
@@ -327,7 +341,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
327
341
  // signer
328
342
  { pubkey: publicKey, isSigner: true, isWritable: true },
329
343
  // SPL token mint
330
- { pubkey: mintAddress, isSigner: false, isWritable: false },
344
+ { pubkey: token.pubkey, isSigner: false, isWritable: false },
331
345
  // signer's token account
332
346
  { pubkey: signerTokenAccount, isSigner: false, isWritable: true },
333
347
  // recipient (placeholder)
@@ -369,7 +383,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
369
383
  // Relay the pre-signed transaction to indexer backend
370
384
  logger.info('submitting transaction to relayer...');
371
385
  const signature = await relayDepositToIndexer({
372
- mintAddress: mintAddress.toString(),
386
+ mintAddress: token.pubkey.toString(),
373
387
  publicKey,
374
388
  signedTransaction: serializedTransaction,
375
389
  referrer
@@ -385,7 +399,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
385
399
  logger.debug(`retryTimes: ${retryTimes}`);
386
400
  await new Promise(resolve => setTimeout(resolve, itv * 1000));
387
401
  logger.debug('Fetching updated tree state...');
388
- let url = RELAYER_API_URL + '/utxos/check/' + encryptedOutputStr + '?token=usdc';
402
+ let url = RELAYER_API_URL + '/utxos/check/' + encryptedOutputStr + '?token=' + token.name;
389
403
  let res = await fetch(url);
390
404
  let resJson = await res.json();
391
405
  if (resJson.exists) {
@@ -398,7 +412,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
398
412
  retryTimes++;
399
413
  }
400
414
  }
401
- async function checkDepositLimit(connection, treeAccount) {
415
+ async function checkDepositLimit(connection, treeAccount, token) {
402
416
  try {
403
417
  // Fetch the account data
404
418
  const accountInfo = await connection.getAccountInfo(treeAccount);
@@ -411,21 +425,21 @@ async function checkDepositLimit(connection, treeAccount) {
411
425
  const rootIndex = new BN(accountInfo.data.slice(4112, 4120), 'le');
412
426
  const maxDepositAmount = new BN(accountInfo.data.slice(4120, 4128), 'le');
413
427
  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';
428
+ // Convert to SPL using BN division to handle large numbers
429
+ const unitesPerToken = new BN(token.units_per_token);
430
+ const maxDepositSpl = maxDepositAmount.div(unitesPerToken);
431
+ const remainder = maxDepositAmount.mod(unitesPerToken);
432
+ // Format the SPL amount with decimals
433
+ let amountFormatted = '1';
420
434
  if (remainder.eq(new BN(0))) {
421
- solFormatted = maxDepositSol.toString();
435
+ amountFormatted = maxDepositSpl.toString();
422
436
  }
423
437
  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)}`;
438
+ // Handle fractional SPL by converting remainder to decimal
439
+ const fractional = remainder.toNumber() / token.units_per_token;
440
+ amountFormatted = `${maxDepositSpl.toString()}${fractional.toFixed(Math.log10(token.units_per_token)).substring(1)}`;
427
441
  }
428
- return Number(solFormatted);
442
+ return Number(amountFormatted);
429
443
  }
430
444
  catch (error) {
431
445
  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
+ logger.debug(' ####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 = [];
@@ -113,10 +122,9 @@ export async function getUtxosSPL({ publicKey, connection, encryptionService, st
113
122
  valid_strings = [...new Set(valid_strings)];
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
- // reorgnize
117
- return valid_utxos.filter(u => u.mintAddress == mintAddress.toString());
125
+ return valid_utxos.filter(u => u.mintAddress == token.pubkey.toString());
118
126
  }
119
- async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionService, publicKey_ata, initOffset }) {
127
+ async function fetchUserUtxos({ url, storage, encryptionService, publicKey_ata, tokenName }) {
120
128
  const lightWasm = await WasmFactory.getInstance();
121
129
  // Derive the UTXO keypair from the wallet keypair
122
130
  const utxoPrivateKey = encryptionService.deriveUtxoPrivateKey();
@@ -159,7 +167,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
159
167
  cachedStringNum = JSON.parse(cachedString).length;
160
168
  }
161
169
  let decryptionTaskTotal = data.total + cachedStringNum - roundStartIndex;
162
- let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm);
170
+ let batchRes = await decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName);
163
171
  decryptionTaskFinished += encryptedOutputs.length;
164
172
  logger.debug('batchReslen', batchRes.length);
165
173
  for (let i = 0; i < batchRes.length; i++) {
@@ -177,7 +185,7 @@ async function fetchUserUtxos({ publicKey, connection, url, storage, encryptionS
177
185
  if (decryptionTaskFinished % 100 == 0) {
178
186
  logger.info(`(decrypting cached utxo: ${decryptionTaskFinished + 1}/${decryptionTaskTotal}...)`);
179
187
  }
180
- let batchRes = await decrypt_outputs(cachedEncryptedOutputs, encryptionService, utxoKeypair, lightWasm);
188
+ let batchRes = await decrypt_outputs(cachedEncryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName);
181
189
  decryptionTaskFinished += cachedEncryptedOutputs.length;
182
190
  logger.debug('cachedbatchReslen', batchRes.length, ' source', cachedEncryptedOutputs.length);
183
191
  for (let i = 0; i < batchRes.length; i++) {
@@ -257,8 +265,19 @@ async function areUtxosSpent(connection, utxos) {
257
265
  }
258
266
  // Calculate total balance
259
267
  export function getBalanceFromUtxosSPL(utxos) {
268
+ if (!utxos.length) {
269
+ return { base_units: 0, amount: 0, lamports: 0 };
270
+ }
271
+ let token = tokens.find(t => t.pubkey.toString() == utxos[0].mintAddress.toString());
272
+ if (!token) {
273
+ throw new Error('token not found for ' + utxos[0].mintAddress.toString());
274
+ }
260
275
  const totalBalance = utxos.reduce((sum, utxo) => sum.add(utxo.amount), new BN(0));
261
- return { base_units: totalBalance.toNumber(), lamports: totalBalance.toNumber() };
276
+ return {
277
+ base_units: totalBalance.toNumber(),
278
+ lamports: totalBalance.toNumber(),
279
+ amount: totalBalance.toNumber() / token.units_per_token
280
+ };
262
281
  }
263
282
  async function decrypt_output(encryptedOutput, encryptionService, utxoKeypair, lightWasm, connection) {
264
283
  let res = { status: 'unDecrypted' };
@@ -332,7 +351,7 @@ async function decrypt_output(encryptedOutput, encryptionService, utxoKeypair, l
332
351
  }
333
352
  return res;
334
353
  }
335
- async function decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm) {
354
+ async function decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair, lightWasm, tokenName) {
336
355
  let results = [];
337
356
  // decript all UTXO
338
357
  for (const encryptedOutput of encryptedOutputs) {
@@ -358,7 +377,7 @@ async function decrypt_outputs(encryptedOutputs, encryptionService, utxoKeypair,
358
377
  let url = RELAYER_API_URL + `/utxos/indices`;
359
378
  let res = await fetch(url, {
360
379
  method: 'POST', headers: { "Content-Type": "application/json" },
361
- body: JSON.stringify({ encrypted_outputs, token: 'usdc' })
380
+ body: JSON.stringify({ encrypted_outputs, token: tokenName })
362
381
  });
363
382
  let j = await res.json();
364
383
  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
  }