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/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
|
-
|
|
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
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]
|
|
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
|
-
|
|
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');
|
package/dist/depositSPL.d.ts
CHANGED
|
@@ -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
|
|
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 {};
|
package/dist/depositSPL.js
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
51
|
-
|
|
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(
|
|
54
|
-
let feeRecipientTokenAccount = getAssociatedTokenAddressSync(
|
|
55
|
-
let signerTokenAccount = getAssociatedTokenAddressSync(
|
|
56
|
-
// Derive tree account PDA with mint address for SPL
|
|
57
|
-
const [treeAccount] = PublicKey.findProgramAddressSync([Buffer.from('merkle_tree'),
|
|
58
|
-
let limitAmount = await checkDepositLimit(connection, treeAccount);
|
|
59
|
-
if (limitAmount && base_units > limitAmount *
|
|
60
|
-
throw new Error(`Don't deposit more than ${limitAmount}
|
|
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}
|
|
72
|
-
logger.debug(`Calculated fee: ${fee_base_units} base_units (${fee_base_units / units_per_token}
|
|
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(`
|
|
77
|
-
|
|
78
|
-
|
|
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}
|
|
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(
|
|
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:
|
|
139
|
+
mintAddress: token.pubkey.toString()
|
|
125
140
|
}),
|
|
126
141
|
new Utxo({
|
|
127
142
|
lightWasm,
|
|
128
143
|
keypair: utxoKeypair,
|
|
129
|
-
mintAddress:
|
|
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:
|
|
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,
|
|
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,
|
|
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:
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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:
|
|
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:
|
|
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=
|
|
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
|
|
415
|
-
const
|
|
416
|
-
const
|
|
417
|
-
const remainder = maxDepositAmount.mod(
|
|
418
|
-
// Format the
|
|
419
|
-
let
|
|
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
|
-
|
|
435
|
+
amountFormatted = maxDepositSpl.toString();
|
|
422
436
|
}
|
|
423
437
|
else {
|
|
424
|
-
// Handle fractional
|
|
425
|
-
const fractional = remainder.toNumber() /
|
|
426
|
-
|
|
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(
|
|
442
|
+
return Number(amountFormatted);
|
|
429
443
|
}
|
|
430
444
|
catch (error) {
|
|
431
445
|
console.log('❌ Error reading deposit limit:', error);
|
package/dist/exportUtils.d.ts
CHANGED
|
@@ -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';
|
package/dist/exportUtils.js
CHANGED
|
@@ -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';
|
package/dist/getUtxosSPL.d.ts
CHANGED
|
@@ -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,
|
|
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
|
};
|
package/dist/getUtxosSPL.js
CHANGED
|
@@ -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,
|
|
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(
|
|
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
|
|
61
|
-
let fetched = await fetchUserUtxos({
|
|
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
|
-
|
|
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({
|
|
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 {
|
|
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:
|
|
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
|
|
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
|
}
|