pvc-fork 1.0.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.
Files changed (65) hide show
  1. package/.github/workflows/npm-publish.yml +55 -0
  2. package/__tests__/e2e.test.ts +56 -0
  3. package/__tests__/e2espl.test.ts +73 -0
  4. package/__tests__/encryption.test.ts +1635 -0
  5. package/circuit2/transaction2.wasm +0 -0
  6. package/circuit2/transaction2.zkey +0 -0
  7. package/dist/config.d.ts +9 -0
  8. package/dist/config.js +12 -0
  9. package/dist/deposit.d.ts +22 -0
  10. package/dist/deposit.js +405 -0
  11. package/dist/depositSPL.d.ts +24 -0
  12. package/dist/depositSPL.js +457 -0
  13. package/dist/exportUtils.d.ts +10 -0
  14. package/dist/exportUtils.js +10 -0
  15. package/dist/getUtxos.d.ts +29 -0
  16. package/dist/getUtxos.js +294 -0
  17. package/dist/getUtxosSPL.d.ts +33 -0
  18. package/dist/getUtxosSPL.js +401 -0
  19. package/dist/index.d.ts +138 -0
  20. package/dist/index.js +288 -0
  21. package/dist/models/keypair.d.ts +26 -0
  22. package/dist/models/keypair.js +43 -0
  23. package/dist/models/utxo.d.ts +49 -0
  24. package/dist/models/utxo.js +85 -0
  25. package/dist/utils/address_lookup_table.d.ts +9 -0
  26. package/dist/utils/address_lookup_table.js +44 -0
  27. package/dist/utils/constants.d.ts +27 -0
  28. package/dist/utils/constants.js +50 -0
  29. package/dist/utils/encryption.d.ts +107 -0
  30. package/dist/utils/encryption.js +376 -0
  31. package/dist/utils/logger.d.ts +9 -0
  32. package/dist/utils/logger.js +35 -0
  33. package/dist/utils/merkle_tree.d.ts +92 -0
  34. package/dist/utils/merkle_tree.js +186 -0
  35. package/dist/utils/node-shim.d.ts +5 -0
  36. package/dist/utils/node-shim.js +5 -0
  37. package/dist/utils/prover.d.ts +36 -0
  38. package/dist/utils/prover.js +147 -0
  39. package/dist/utils/utils.d.ts +64 -0
  40. package/dist/utils/utils.js +165 -0
  41. package/dist/withdraw.d.ts +21 -0
  42. package/dist/withdraw.js +270 -0
  43. package/dist/withdrawSPL.d.ts +23 -0
  44. package/dist/withdrawSPL.js +306 -0
  45. package/package.json +51 -0
  46. package/src/config.ts +22 -0
  47. package/src/deposit.ts +508 -0
  48. package/src/depositSPL.ts +591 -0
  49. package/src/exportUtils.ts +12 -0
  50. package/src/getUtxos.ts +396 -0
  51. package/src/getUtxosSPL.ts +533 -0
  52. package/src/index.ts +338 -0
  53. package/src/models/keypair.ts +52 -0
  54. package/src/models/utxo.ts +106 -0
  55. package/src/utils/address_lookup_table.ts +77 -0
  56. package/src/utils/constants.ts +72 -0
  57. package/src/utils/encryption.ts +464 -0
  58. package/src/utils/logger.ts +42 -0
  59. package/src/utils/merkle_tree.ts +207 -0
  60. package/src/utils/node-shim.ts +6 -0
  61. package/src/utils/prover.ts +222 -0
  62. package/src/utils/utils.ts +222 -0
  63. package/src/withdraw.ts +332 -0
  64. package/src/withdrawSPL.ts +397 -0
  65. package/tsconfig.json +28 -0
package/src/index.ts ADDED
@@ -0,0 +1,338 @@
1
+ import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, VersionedTransaction } from '@solana/web3.js';
2
+ import { deposit } from './deposit.js';
3
+ import { getBalanceFromUtxos, getUtxos, localstorageKey } from './getUtxos.js';
4
+ import { getBalanceFromUtxosSPL, getUtxosSPL } from './getUtxosSPL.js';
5
+
6
+ import { LSK_ENCRYPTED_OUTPUTS, LSK_FETCH_OFFSET, SplList, TokenList, tokens, USDC_MINT } from './utils/constants.js';
7
+ import { logger, type LoggerFn, setLogger } from './utils/logger.js';
8
+ import { EncryptionService } from './utils/encryption.js';
9
+ import { WasmFactory } from '@lightprotocol/hasher.rs';
10
+ import bs58 from 'bs58'
11
+ import { withdraw } from './withdraw.js';
12
+ import { LocalStorage } from "node-localstorage";
13
+ import path from 'node:path'
14
+ import { depositSPL } from './depositSPL.js';
15
+ import { withdrawSPL } from './withdrawSPL.js';
16
+ import { getAssociatedTokenAddress } from '@solana/spl-token';
17
+
18
+ let storage = new LocalStorage(path.join(process.cwd(), "cache"));
19
+
20
+ export class PrivacyCash {
21
+ private connection: Connection
22
+ public publicKey: PublicKey
23
+ private encryptionService: EncryptionService
24
+ private isRuning?: boolean = false
25
+ private status: string = ''
26
+ constructor({ RPC_url, publicKey, signature, enableDebug }: {
27
+ RPC_url: string,
28
+ publicKey: PublicKey,
29
+ signature: Uint8Array,
30
+ enableDebug?: boolean
31
+ }) {
32
+ this.connection = new Connection(RPC_url, 'confirmed')
33
+ this.publicKey = publicKey;
34
+ this.encryptionService = new EncryptionService();
35
+ this.encryptionService.deriveEncryptionKeyFromSignature(signature);
36
+ if (!enableDebug) {
37
+ this.startStatusRender()
38
+ this.setLogger((level, message) => {
39
+ if (level == 'info') {
40
+ this.status = message
41
+ } else if (level == 'error') {
42
+ console.log('error message: ', message)
43
+ }
44
+ })
45
+ }
46
+ }
47
+
48
+ setLogger(loger: LoggerFn) {
49
+ setLogger(loger)
50
+ return this
51
+ }
52
+
53
+ /**
54
+ * Clears the cache of utxos.
55
+ *
56
+ * By default, downloaded utxos will be cached in the local storage. Thus the next time when you makes another
57
+ * deposit or withdraw or getPrivateBalance, the SDK only fetches the utxos that are not in the cache.
58
+ *
59
+ * This method clears the cache of utxos.
60
+ */
61
+ async clearCache() {
62
+ if (!this.publicKey) {
63
+ return this
64
+ }
65
+ storage.removeItem(LSK_FETCH_OFFSET + localstorageKey(this.publicKey))
66
+ storage.removeItem(LSK_ENCRYPTED_OUTPUTS + localstorageKey(this.publicKey))
67
+ // spl
68
+ for (let token of tokens) {
69
+ let ata = await getAssociatedTokenAddress(
70
+ token.pubkey,
71
+ this.publicKey
72
+ );
73
+ storage.removeItem(LSK_FETCH_OFFSET + localstorageKey(ata))
74
+ storage.removeItem(LSK_ENCRYPTED_OUTPUTS + localstorageKey(ata))
75
+ }
76
+ return this
77
+ }
78
+
79
+ async deriveEncryptionKeyFromWallet(keypair: Keypair) {
80
+ this.encryptionService.deriveEncryptionKeyFromWallet(keypair)
81
+ return this
82
+ }
83
+
84
+ /**
85
+ * Deposit SOL to the Privacy Cash.
86
+ *
87
+ * Lamports is the amount of SOL in lamports. e.g. if you want to deposit 0.01 SOL (10000000 lamports), call deposit({ lamports: 10000000 })
88
+ */
89
+ async deposit({ lamports }: {
90
+ lamports: number
91
+ }) {
92
+ this.isRuning = true
93
+ logger.info('start depositting')
94
+ let lightWasm = await WasmFactory.getInstance()
95
+ let res = await deposit({
96
+ lightWasm,
97
+ amount_in_lamports: lamports,
98
+ connection: this.connection,
99
+ encryptionService: this.encryptionService,
100
+ publicKey: this.publicKey,
101
+ keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
102
+ storage
103
+ })
104
+ this.isRuning = false
105
+ return res
106
+ }
107
+
108
+ /**
109
+ * Deposit USDC to the Privacy Cash.
110
+ */
111
+ async depositUSDC({ base_units }: {
112
+ base_units: number
113
+ }) {
114
+ this.isRuning = true
115
+ logger.info('start depositting')
116
+ let lightWasm = await WasmFactory.getInstance()
117
+ let res = await depositSPL({
118
+ mintAddress: USDC_MINT,
119
+ lightWasm,
120
+ base_units: base_units,
121
+ connection: this.connection,
122
+ encryptionService: this.encryptionService,
123
+ publicKey: this.publicKey,
124
+ keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
125
+ storage
126
+ })
127
+ this.isRuning = false
128
+ return res
129
+ }
130
+
131
+ /**
132
+ * Withdraw SOL from the Privacy Cash.
133
+ *
134
+ * Lamports is the amount of SOL in lamports. e.g. if you want to withdraw 0.01 SOL (10000000 lamports), call withdraw({ lamports: 10000000 })
135
+ */
136
+ async withdraw({ lamports, recipientAddress }: {
137
+ lamports: number,
138
+ recipientAddress?: string
139
+ }) {
140
+ this.isRuning = true
141
+ logger.info('start withdrawing')
142
+ let lightWasm = await WasmFactory.getInstance()
143
+ let recipient = recipientAddress ? new PublicKey(recipientAddress) : this.publicKey
144
+ let res = await withdraw({
145
+ lightWasm,
146
+ amount_in_lamports: lamports,
147
+ connection: this.connection,
148
+ encryptionService: this.encryptionService,
149
+ publicKey: this.publicKey,
150
+ recipient,
151
+ keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
152
+ storage
153
+ })
154
+ console.log(`Withdraw successful. Recipient ${recipient} received ${res.amount_in_lamports / LAMPORTS_PER_SOL} SOL, with ${res.fee_in_lamports / LAMPORTS_PER_SOL} SOL relayers fees`)
155
+ this.isRuning = false
156
+ return res
157
+ }
158
+
159
+ /**
160
+ * Withdraw USDC from the Privacy Cash.
161
+ *
162
+ * base_units is the amount of USDC in base unit. e.g. if you want to withdraw 1 USDC (1,000,000 base unit), call withdraw({ base_units: 1000000, recipientAddress: 'some_address' })
163
+ */
164
+ async withdrawUSDC({ base_units, recipientAddress }: {
165
+ base_units: number,
166
+ recipientAddress?: string
167
+ }) {
168
+ this.isRuning = true
169
+ logger.info('start withdrawing')
170
+ let lightWasm = await WasmFactory.getInstance()
171
+ let recipient = recipientAddress ? new PublicKey(recipientAddress) : this.publicKey
172
+ let res = await withdrawSPL({
173
+ mintAddress: USDC_MINT,
174
+ lightWasm,
175
+ base_units,
176
+ connection: this.connection,
177
+ encryptionService: this.encryptionService,
178
+ publicKey: this.publicKey,
179
+ recipient,
180
+ keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
181
+ storage
182
+ })
183
+ console.log(`Withdraw successful. Recipient ${recipient} received ${base_units} USDC units`)
184
+ this.isRuning = false
185
+ return res
186
+ }
187
+
188
+ /**
189
+ * Returns the amount of lamports current wallet has in Privacy Cash.
190
+ */
191
+ async getPrivateBalance(abortSignal?: AbortSignal) {
192
+ logger.info('getting private balance')
193
+ this.isRuning = true
194
+ let utxos = await getUtxos({ publicKey: this.publicKey, connection: this.connection, encryptionService: this.encryptionService, storage, abortSignal })
195
+ this.isRuning = false
196
+ return getBalanceFromUtxos(utxos)
197
+ }
198
+
199
+ /**
200
+ * Returns the amount of base unites current wallet has in Privacy Cash.
201
+ */
202
+ async getPrivateBalanceUSDC() {
203
+ logger.info('getting private balance')
204
+ this.isRuning = true
205
+ let utxos = await getUtxosSPL({ publicKey: this.publicKey, connection: this.connection, encryptionService: this.encryptionService, storage, mintAddress: USDC_MINT })
206
+ this.isRuning = false
207
+ return getBalanceFromUtxosSPL(utxos)
208
+ }
209
+
210
+ /**
211
+ * Returns the amount of base unites current wallet has in Privacy Cash.
212
+ */
213
+ async getPrivateBalanceSpl(mintAddress: PublicKey | string) {
214
+ this.isRuning = true
215
+ let utxos = await getUtxosSPL({
216
+ publicKey: this.publicKey,
217
+ connection: this.connection,
218
+ encryptionService: this.encryptionService,
219
+ storage,
220
+ mintAddress
221
+ })
222
+ this.isRuning = false
223
+ return getBalanceFromUtxosSPL(utxos)
224
+ }
225
+
226
+ /**
227
+ * Returns true if the code is running in a browser.
228
+ */
229
+ isBrowser() {
230
+ return typeof window !== "undefined"
231
+ }
232
+
233
+ async startStatusRender() {
234
+ let frames = ['-', '\\', '|', '/'];
235
+ let i = 0
236
+ while (true) {
237
+ if (this.isRuning) {
238
+ let k = i % frames.length
239
+ i++
240
+ stdWrite(this.status, frames[k])
241
+ }
242
+ await new Promise(r => setTimeout(r, 250));
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Deposit SPL to the Privacy Cash.
248
+ */
249
+ async depositSPL({ base_units, mintAddress, amount }: {
250
+ base_units?: number,
251
+ amount?: number,
252
+ mintAddress: PublicKey | string
253
+ }) {
254
+ this.isRuning = true
255
+ logger.info('start depositting')
256
+ let lightWasm = await WasmFactory.getInstance()
257
+ let res = await depositSPL({
258
+ lightWasm,
259
+ base_units,
260
+ amount,
261
+ connection: this.connection,
262
+ encryptionService: this.encryptionService,
263
+ publicKey: this.publicKey,
264
+ keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
265
+ storage,
266
+ mintAddress
267
+ })
268
+ this.isRuning = false
269
+ return res
270
+ }
271
+
272
+ /**
273
+ * Withdraw SPL from the Privacy Cash.
274
+ */
275
+ async withdrawSPL({ base_units, mintAddress, recipientAddress, amount }: {
276
+ base_units?: number,
277
+ amount?: number,
278
+ mintAddress: PublicKey | string,
279
+ recipientAddress?: string
280
+ }) {
281
+ this.isRuning = true
282
+ logger.info('start withdrawing')
283
+ let lightWasm = await WasmFactory.getInstance()
284
+ let recipient = recipientAddress ? new PublicKey(recipientAddress) : this.publicKey
285
+
286
+ let res = await withdrawSPL({
287
+ lightWasm,
288
+ base_units,
289
+ amount,
290
+ connection: this.connection,
291
+ encryptionService: this.encryptionService,
292
+ publicKey: this.publicKey,
293
+ recipient,
294
+ keyBasePath: path.join(import.meta.dirname, '..', 'circuit2', 'transaction2'),
295
+ storage,
296
+ mintAddress
297
+ })
298
+ console.log(`Withdraw successful. Recipient ${recipient} received ${base_units} USDC units`)
299
+ this.isRuning = false
300
+ return res
301
+ }
302
+
303
+
304
+ }
305
+
306
+ function getSolanaKeypair(
307
+ secret: string | number[] | Uint8Array | Keypair
308
+ ): Keypair | null {
309
+ try {
310
+ if (secret instanceof Keypair) {
311
+ return secret;
312
+ }
313
+
314
+ let keyArray: Uint8Array;
315
+
316
+ if (typeof secret === "string") {
317
+ keyArray = bs58.decode(secret);
318
+ } else if (secret instanceof Uint8Array) {
319
+ keyArray = secret;
320
+ } else {
321
+ // number[]
322
+ keyArray = Uint8Array.from(secret);
323
+ }
324
+
325
+ if (keyArray.length !== 32 && keyArray.length !== 64) {
326
+ return null;
327
+ }
328
+ return Keypair.fromSecretKey(keyArray);
329
+ } catch {
330
+ return null;
331
+ }
332
+ }
333
+
334
+ function stdWrite(status: string, frame: string) {
335
+ let blue = "\x1b[34m";
336
+ let reset = "\x1b[0m";
337
+ process.stdout.write(`${frame}status: ${blue}${status}${reset}\r`);
338
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Keypair module for ZK Cash
3
+ *
4
+ * Provides cryptographic keypair functionality for the ZK Cash system
5
+ * Based on: https://github.com/tornadocash/tornado-nova
6
+ */
7
+
8
+ import BN from 'bn.js';
9
+ import { ethers } from 'ethers';
10
+ import * as hasher from '@lightprotocol/hasher.rs';
11
+
12
+ // Field size constant
13
+ const FIELD_SIZE = new BN(
14
+ '21888242871839275222246405745257275088548364400416034343698204186575808495617'
15
+ );
16
+
17
+ /**
18
+ * Simplified version of Keypair
19
+ */
20
+ export class Keypair {
21
+ public privkey: BN;
22
+ public pubkey: BN;
23
+ private lightWasm: hasher.LightWasm;
24
+
25
+ constructor(privkeyHex: string, lightWasm: hasher.LightWasm) {
26
+ const rawDecimal = BigInt(privkeyHex);
27
+ this.privkey = new BN((rawDecimal % BigInt(FIELD_SIZE.toString())).toString());
28
+ this.lightWasm = lightWasm;
29
+ // TODO: lazily compute pubkey
30
+ this.pubkey = new BN(this.lightWasm.poseidonHashString([this.privkey.toString()]));
31
+ }
32
+
33
+ /**
34
+ * Sign a message using keypair private key
35
+ *
36
+ * @param {string|number|BigNumber} commitment a hex string with commitment
37
+ * @param {string|number|BigNumber} merklePath a hex string with merkle path
38
+ * @returns {BigNumber} a hex string with signature
39
+ */
40
+ sign(commitment: string, merklePath: string): string {
41
+ return this.lightWasm.poseidonHashString([this.privkey.toString(), commitment, merklePath]);
42
+ }
43
+
44
+ static async generateNew(lightWasm: hasher.LightWasm): Promise<Keypair> {
45
+ // Tornado Cash Nova uses ethers.js to generate a random private key
46
+ // We can't generate Solana keypairs because it won't fit in the field size
47
+ // It's OK to use ethereum secret keys, because the secret key is only used for the proof generation.
48
+ // Namely, it's used to guarantee the uniqueness of the nullifier.
49
+ const wallet = ethers.Wallet.createRandom();
50
+ return new Keypair(wallet.privateKey, lightWasm);
51
+ }
52
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * UTXO (Unspent Transaction Output) module for ZK Cash
3
+ *
4
+ * Provides UTXO functionality for the ZK Cash system
5
+ * Based on: https://github.com/tornadocash/tornado-nova
6
+ */
7
+
8
+ import BN from 'bn.js';
9
+ import { Keypair } from './keypair.js';
10
+ import * as hasher from '@lightprotocol/hasher.rs';
11
+ import { ethers } from 'ethers';
12
+ import { getMintAddressField } from '../utils/utils.js';
13
+ import { PublicKey } from '@solana/web3.js';
14
+ /**
15
+ * Simplified Utxo class inspired by Tornado Cash Nova
16
+ * Based on: https://github.com/tornadocash/tornado-nova/blob/f9264eeffe48bf5e04e19d8086ee6ec58cdf0d9e/src/utxo.js
17
+ */
18
+ export class Utxo {
19
+ amount: BN;
20
+ blinding: BN;
21
+ keypair: Keypair;
22
+ index: number;
23
+ mintAddress: string;
24
+ version: 'v1' | 'v2';
25
+ private lightWasm: hasher.LightWasm;
26
+
27
+ constructor({
28
+ lightWasm,
29
+ amount = new BN(0),
30
+ /**
31
+ * Tornado nova doesn't use solana eddsa with curve 25519 but their own "keypair"
32
+ * which is:
33
+ * - private key: random [31;u8]
34
+ * - public key: PoseidonHash(privateKey)
35
+ *
36
+ * Generate a new keypair for each UTXO
37
+ */
38
+ keypair,
39
+ blinding = new BN(Math.floor(Math.random() * 1000000000)), // Use fixed value for consistency instead of randomBN()
40
+ index = 0,
41
+ mintAddress = '11111111111111111111111111111112', // Default to Solana native SOL mint address,
42
+ version = 'v2'
43
+ }: {
44
+ lightWasm: hasher.LightWasm,
45
+ amount?: BN | number | string,
46
+ keypair?: Keypair,
47
+ blinding?: BN | number | string,
48
+ index?: number,
49
+ mintAddress?: string,
50
+ version?: 'v1' | 'v2'
51
+ }) {
52
+ this.amount = new BN(amount.toString());
53
+ this.blinding = new BN(blinding.toString());
54
+ this.lightWasm = lightWasm;
55
+ this.keypair = keypair || new Keypair(ethers.Wallet.createRandom().privateKey, lightWasm);
56
+ this.index = index;
57
+ this.mintAddress = mintAddress;
58
+ this.version = version;
59
+ }
60
+
61
+ async getCommitment(): Promise<string> {
62
+ // return this.lightWasm.poseidonHashString([this.amount.toString(), this.keypair.pubkey.toString(), this.blinding.toString(), this.mintAddress]);
63
+ const mintAddressField = getMintAddressField(new PublicKey(this.mintAddress));
64
+ return this.lightWasm.poseidonHashString([
65
+ this.amount.toString(),
66
+ this.keypair.pubkey.toString(),
67
+ this.blinding.toString(),
68
+ mintAddressField
69
+ ]);
70
+ }
71
+
72
+ async getNullifier(): Promise<string> {
73
+ const commitmentValue = await this.getCommitment();
74
+ const signature = this.keypair.sign(commitmentValue, new BN(this.index).toString());
75
+
76
+ return this.lightWasm.poseidonHashString([commitmentValue, new BN(this.index).toString(), signature]);
77
+ }
78
+
79
+ /**
80
+ * Log all the UTXO's public properties and derived values in JSON format
81
+ * @returns Promise that resolves once all logging is complete
82
+ */
83
+ async log(): Promise<void> {
84
+ // Prepare the UTXO data object
85
+ const utxoData: any = {
86
+ amount: this.amount.toString(),
87
+ blinding: this.blinding.toString(),
88
+ index: this.index,
89
+ mintAddress: this.mintAddress,
90
+ keypair: {
91
+ pubkey: this.keypair.pubkey.toString()
92
+ }
93
+ };
94
+
95
+ // Add derived values
96
+ try {
97
+ utxoData.commitment = await this.getCommitment();
98
+ utxoData.nullifier = await this.getNullifier();
99
+ } catch (error: any) {
100
+ utxoData.error = error.message;
101
+ }
102
+
103
+ // Output as formatted JSON
104
+ console.log(JSON.stringify(utxoData, null, 2));
105
+ }
106
+ }
@@ -0,0 +1,77 @@
1
+ import {
2
+ Connection,
3
+ Keypair,
4
+ PublicKey,
5
+ SystemProgram,
6
+ AddressLookupTableProgram,
7
+ Transaction,
8
+ sendAndConfirmTransaction,
9
+ ComputeBudgetProgram,
10
+ VersionedTransaction,
11
+ TransactionMessage
12
+ } from '@solana/web3.js';
13
+ import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token';
14
+
15
+
16
+ /**
17
+ * Helper function to use an existing ALT (recommended for production)
18
+ * Use create_alt.ts to create the ALT once, then hardcode the address and use this function
19
+ */
20
+ export async function useExistingALT(
21
+ connection: Connection,
22
+ altAddress: PublicKey
23
+ ): Promise<{ value: any } | null> {
24
+ try {
25
+ console.log(`Using existing ALT: ${altAddress.toString()}`);
26
+ const altAccount = await connection.getAddressLookupTable(altAddress);
27
+
28
+ if (altAccount.value) {
29
+ console.log(`✅ ALT found with ${altAccount.value.state.addresses.length} addresses`);
30
+ } else {
31
+ console.log('❌ ALT not found');
32
+ }
33
+
34
+ return altAccount;
35
+ } catch (error) {
36
+ console.error('Error getting existing ALT:', error);
37
+ return null;
38
+ }
39
+ }
40
+
41
+
42
+ export function getProtocolAddressesWithMint(
43
+ programId: PublicKey,
44
+ authority: PublicKey,
45
+ treeAta: PublicKey,
46
+ feeRecipient: PublicKey,
47
+ feeRecipientAta: PublicKey
48
+ ): PublicKey[] {
49
+ // Derive global config PDA
50
+ const [globalConfigAccount] = PublicKey.findProgramAddressSync(
51
+ [Buffer.from('global_config')],
52
+ programId
53
+ );
54
+
55
+ // Derive tree accounts
56
+ const [treeAccount] = PublicKey.findProgramAddressSync(
57
+ [Buffer.from('merkle_tree')],
58
+ programId
59
+ );
60
+
61
+ return [
62
+ // Core program accounts (constant)
63
+ programId,
64
+ treeAccount,
65
+ treeAta,
66
+ globalConfigAccount,
67
+ authority,
68
+ feeRecipient,
69
+ feeRecipientAta,
70
+
71
+ // System programs (constant)
72
+ SystemProgram.programId,
73
+ ComputeBudgetProgram.programId,
74
+ ASSOCIATED_TOKEN_PROGRAM_ID,
75
+ TOKEN_PROGRAM_ID,
76
+ ];
77
+ }
@@ -0,0 +1,72 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+ import BN from 'bn.js';
3
+
4
+ export const FIELD_SIZE = new BN('21888242871839275222246405745257275088548364400416034343698204186575808495617')
5
+
6
+ export const PROGRAM_ID = process.env.NEXT_PUBLIC_PROGRAM_ID ? new PublicKey(process.env.NEXT_PUBLIC_PROGRAM_ID) : new PublicKey('9fhQBbumKEFuXtMBDw8AaQyAjCorLGJQiS3skWZdQyQD');
7
+
8
+ export const FEE_RECIPIENT = new PublicKey('AWexibGxNFKTa1b5R5MN4PJr9HWnWRwf8EW9g8cLx3dM')
9
+
10
+ export const FETCH_UTXOS_GROUP_SIZE = 20_000
11
+
12
+ export const TRANSACT_IX_DISCRIMINATOR = Buffer.from([217, 149, 130, 143, 221, 52, 252, 119]);
13
+
14
+ export const TRANSACT_SPL_IX_DISCRIMINATOR = Buffer.from([154, 66, 244, 204, 78, 225, 163, 151]);
15
+
16
+ export const MERKLE_TREE_DEPTH = 26;
17
+
18
+ export const ALT_ADDRESS = process.env.NEXT_PUBLIC_ALT_ADDRESS ? new PublicKey(process.env.NEXT_PUBLIC_ALT_ADDRESS) : new PublicKey('HEN49U2ySJ85Vc78qprSW9y6mFDhs1NczRxyppNHjofe');
19
+
20
+ export const RELAYER_API_URL = process.env.NEXT_PUBLIC_RELAYER_API_URL ?? 'https://api3.privacycash.org';
21
+
22
+ export const SIGN_MESSAGE = `Privacy Money account sign in`
23
+
24
+ // localStorage cache keys
25
+ export const LSK_FETCH_OFFSET = 'fetch_offset'
26
+ export const LSK_ENCRYPTED_OUTPUTS = 'encrypted_outputs'
27
+
28
+ export const USDC_MINT = process.env.NEXT_PUBLIC_USDC_MINT ? new PublicKey(process.env.NEXT_PUBLIC_USDC_MINT) : new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v')
29
+
30
+ const tokenList = ['sol', 'usdc', 'usdt', 'zec', 'ore'] as const;
31
+ export type TokenList = typeof tokenList[number];
32
+ const splList = ['usdc', 'usdt', 'ore'] as const;
33
+ export type SplList = typeof splList[number];
34
+ export type Token = {
35
+ name: 'sol' | 'usdc' | 'usdt' | 'zec' | 'ore'
36
+ prefix: string
37
+ units_per_token: number
38
+ pubkey: PublicKey
39
+ }
40
+ export const tokens: Token[] = [
41
+ {
42
+ name: 'sol',
43
+ pubkey: new PublicKey('So11111111111111111111111111111111111111112'),
44
+ prefix: '',
45
+ units_per_token: 1e9
46
+ },
47
+ {
48
+ name: 'usdc',
49
+ pubkey: process.env.NEXT_PUBLIC_USDC_MINT ? new PublicKey(process.env.NEXT_PUBLIC_USDC_MINT) : new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
50
+ prefix: 'usdc_',
51
+ units_per_token: 1e6
52
+ },
53
+ {
54
+ name: 'usdt',
55
+ pubkey: process.env.NEXT_PUBLIC_USDT_MINT ? new PublicKey(process.env.NEXT_PUBLIC_USDT_MINT) : new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'),
56
+ prefix: 'usdt_',
57
+ units_per_token: 1e6
58
+ },
59
+ {
60
+ name: 'zec',
61
+ pubkey: process.env.NEXT_PUBLIC_ZEC_MINT ? new PublicKey(process.env.NEXT_PUBLIC_ZEC_MINT) : new PublicKey('A7bdiYdS5GjqGFtxf17ppRHtDKPkkRqbKtR27dxvQXaS'),
62
+ prefix: 'zec_',
63
+ units_per_token: 1e8
64
+ }
65
+ ,
66
+ {
67
+ name: 'ore',
68
+ pubkey: process.env.NEXT_PUBLIC_ORE_MINT ? new PublicKey(process.env.NEXT_PUBLIC_ORE_MINT) : new PublicKey('oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp'),
69
+ prefix: 'ore_',
70
+ units_per_token: 1e11
71
+ }
72
+ ]