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