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